Qt: fix assertion failure when redissecting with a debug build of Qt
[metze/wireshark/wip.git] / ui / qt / models / packet_list_model.cpp
1 /* packet_list_model.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9
10 #include <algorithm>
11
12 #include "packet_list_model.h"
13
14 #include "file.h"
15
16 #include <wsutil/nstime.h>
17 #include <epan/column.h>
18 #include <epan/prefs.h>
19
20 #include "ui/packet_list_utils.h"
21 #include "ui/recent.h"
22
23 #include <epan/color_filters.h>
24 #include "frame_tvbuff.h"
25
26 #include <ui/qt/utils/color_utils.h>
27 #include "wireshark_application.h"
28
29 #include <QColor>
30 #include <QElapsedTimer>
31 #include <QFontMetrics>
32 #include <QModelIndex>
33 #include <QElapsedTimer>
34
35 // Print timing information
36 //#define DEBUG_PACKET_LIST_MODEL 1
37
38 #ifdef DEBUG_PACKET_LIST_MODEL
39 #include <wsutil/time_util.h>
40 #endif
41
42 static const int reserved_packets_ = 100000;
43
44 PacketListModel::PacketListModel(QObject *parent, capture_file *cf) :
45     QAbstractItemModel(parent),
46     number_to_row_(QVector<int>()),
47     max_row_height_(0),
48     max_line_count_(1),
49     idle_dissection_row_(0)
50 {
51     setCaptureFile(cf);
52     PacketListRecord::clearStringPool();
53
54     physical_rows_.reserve(reserved_packets_);
55     visible_rows_.reserve(reserved_packets_);
56     new_visible_rows_.reserve(1000);
57     number_to_row_.reserve(reserved_packets_);
58
59     connect(this, SIGNAL(maxLineCountChanged(QModelIndex)),
60             this, SLOT(emitItemHeightChanged(QModelIndex)),
61             Qt::QueuedConnection);
62     idle_dissection_timer_ = new QElapsedTimer();
63 }
64
65 PacketListModel::~PacketListModel()
66 {
67     delete idle_dissection_timer_;
68 }
69
70 void PacketListModel::setCaptureFile(capture_file *cf)
71 {
72     cap_file_ = cf;
73     resetColumns();
74 }
75
76 // Packet list records have no children (for now, at least).
77 QModelIndex PacketListModel::index(int row, int column, const QModelIndex &) const
78 {
79     if (row >= visible_rows_.count() || row < 0 || !cap_file_ || column >= prefs.num_cols)
80         return QModelIndex();
81
82     PacketListRecord *record = visible_rows_[row];
83
84     return createIndex(row, column, record);
85 }
86
87 // Everything is under the root.
88 QModelIndex PacketListModel::parent(const QModelIndex &) const
89 {
90     return QModelIndex();
91 }
92
93 int PacketListModel::packetNumberToRow(int packet_num) const
94 {
95     // map 1-based values to 0-based row numbers. Invisible rows are stored as
96     // the default value (0) and should map to -1.
97     return number_to_row_.value(packet_num) - 1;
98 }
99
100 guint PacketListModel::recreateVisibleRows()
101 {
102     beginResetModel();
103     visible_rows_.resize(0);
104     number_to_row_.fill(0);
105     endResetModel();
106
107     foreach (PacketListRecord *record, physical_rows_) {
108         frame_data *fdata = record->frameData();
109
110         if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
111             visible_rows_ << record;
112             if (number_to_row_.size() <= (int)fdata->num) {
113                 number_to_row_.resize(fdata->num + 10000);
114             }
115             number_to_row_[fdata->num] = visible_rows_.count();
116         }
117     }
118     if (!visible_rows_.isEmpty()) {
119         beginInsertRows(QModelIndex(), 0, visible_rows_.count() - 1);
120         endInsertRows();
121     }
122     idle_dissection_row_ = 0;
123     return visible_rows_.count();
124 }
125
126 void PacketListModel::clear() {
127     beginResetModel();
128     qDeleteAll(physical_rows_);
129     physical_rows_.resize(0);
130     visible_rows_.resize(0);
131     new_visible_rows_.resize(0);
132     number_to_row_.resize(0);
133     PacketListRecord::clearStringPool();
134     endResetModel();
135     max_row_height_ = 0;
136     max_line_count_ = 1;
137     idle_dissection_row_ = 0;
138 }
139
140 void PacketListModel::invalidateAllColumnStrings()
141 {
142     PacketListRecord::invalidateAllRecords();
143     dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
144     headerDataChanged(Qt::Horizontal, 0, columnCount() - 1);
145 }
146
147 void PacketListModel::resetColumns()
148 {
149     if (cap_file_) {
150         PacketListRecord::resetColumns(&cap_file_->cinfo);
151     }
152     dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
153     headerDataChanged(Qt::Horizontal, 0, columnCount() - 1);
154 }
155
156 void PacketListModel::resetColorized()
157 {
158     foreach (PacketListRecord *record, physical_rows_) {
159         record->resetColorized();
160     }
161     dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
162 }
163
164 void PacketListModel::toggleFrameMark(const QModelIndex &fm_index)
165 {
166     if (!cap_file_ || !fm_index.isValid()) return;
167
168     PacketListRecord *record = static_cast<PacketListRecord*>(fm_index.internalPointer());
169     if (!record) return;
170
171     frame_data *fdata = record->frameData();
172     if (!fdata) return;
173
174     if (fdata->flags.marked)
175         cf_unmark_frame(cap_file_, fdata);
176     else
177         cf_mark_frame(cap_file_, fdata);
178
179     dataChanged(fm_index, fm_index);
180 }
181
182 void PacketListModel::setDisplayedFrameMark(gboolean set)
183 {
184     foreach (PacketListRecord *record, visible_rows_) {
185         if (set) {
186             cf_mark_frame(cap_file_, record->frameData());
187         } else {
188             cf_unmark_frame(cap_file_, record->frameData());
189         }
190     }
191     dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
192 }
193
194 void PacketListModel::toggleFrameIgnore(const QModelIndex &i_index)
195 {
196     if (!cap_file_ || !i_index.isValid()) return;
197
198     PacketListRecord *record = static_cast<PacketListRecord*>(i_index.internalPointer());
199     if (!record) return;
200
201     frame_data *fdata = record->frameData();
202     if (!fdata) return;
203
204     if (fdata->flags.ignored)
205         cf_unignore_frame(cap_file_, fdata);
206     else
207         cf_ignore_frame(cap_file_, fdata);
208 }
209
210 void PacketListModel::setDisplayedFrameIgnore(gboolean set)
211 {
212     foreach (PacketListRecord *record, visible_rows_) {
213         if (set) {
214             cf_ignore_frame(cap_file_, record->frameData());
215         } else {
216             cf_unignore_frame(cap_file_, record->frameData());
217         }
218     }
219     dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
220 }
221
222 void PacketListModel::toggleFrameRefTime(const QModelIndex &rt_index)
223 {
224     if (!cap_file_ || !rt_index.isValid()) return;
225
226     PacketListRecord *record = static_cast<PacketListRecord*>(rt_index.internalPointer());
227     if (!record) return;
228
229     frame_data *fdata = record->frameData();
230     if (!fdata) return;
231
232     if (fdata->flags.ref_time) {
233         fdata->flags.ref_time=0;
234         cap_file_->ref_time_count--;
235     } else {
236         fdata->flags.ref_time=1;
237         cap_file_->ref_time_count++;
238     }
239     cf_reftime_packets(cap_file_);
240     if (!fdata->flags.ref_time && !fdata->flags.passed_dfilter) {
241         cap_file_->displayed_count--;
242     }
243     record->resetColumns(&cap_file_->cinfo);
244     dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
245 }
246
247 void PacketListModel::unsetAllFrameRefTime()
248 {
249     if (!cap_file_) return;
250
251     /* XXX: we might need a progressbar here */
252
253     foreach (PacketListRecord *record, physical_rows_) {
254         frame_data *fdata = record->frameData();
255         if (fdata->flags.ref_time) {
256             fdata->flags.ref_time = 0;
257         }
258     }
259     cap_file_->ref_time_count = 0;
260     cf_reftime_packets(cap_file_);
261     PacketListRecord::resetColumns(&cap_file_->cinfo);
262     dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
263 }
264
265 void PacketListModel::applyTimeShift()
266 {
267     resetColumns();
268     dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
269 }
270
271 void PacketListModel::setMaximiumRowHeight(int height)
272 {
273     max_row_height_ = height;
274     // As the QTreeView uniformRowHeights documentation says,
275     // "The height is obtained from the first item in the view. It is
276     //  updated when the data changes on that item."
277     dataChanged(index(0, 0), index(0, columnCount() - 1));
278 }
279
280 //void PacketListModel::setMonospaceFont(const QFont &mono_font, int row_height)
281 //{
282 //    QFontMetrics fm(mono_font_);
283 //    mono_font_ = mono_font;
284 //    row_height_ = row_height;
285 //    line_spacing_ = fm.lineSpacing();
286 //}
287
288 // The Qt MVC documentation suggests using QSortFilterProxyModel for sorting
289 // and filtering. That seems like overkill but it might be something we want
290 // to do in the future.
291
292 int PacketListModel::sort_column_;
293 int PacketListModel::sort_column_is_numeric_;
294 int PacketListModel::text_sort_column_;
295 Qt::SortOrder PacketListModel::sort_order_;
296 capture_file *PacketListModel::sort_cap_file_;
297
298 QElapsedTimer busy_timer_;
299 const int busy_timeout_ = 65; // ms, approximately 15 fps
300 void PacketListModel::sort(int column, Qt::SortOrder order)
301 {
302     // packet_list_store.c:packet_list_dissect_and_cache_all
303     if (!cap_file_ || visible_rows_.count() < 1) return;
304     if (column < 0) return;
305
306     sort_column_ = column;
307     text_sort_column_ = PacketListRecord::textColumn(column);
308     sort_order_ = order;
309     sort_cap_file_ = cap_file_;
310
311     gboolean stop_flag = FALSE;
312     QString col_title = get_column_title(column);
313
314     busy_timer_.start();
315     emit pushProgressStatus(tr("Dissecting"), true, true, &stop_flag);
316     int row_num = 0;
317     foreach (PacketListRecord *row, physical_rows_) {
318         row->columnString(sort_cap_file_, column);
319         row_num++;
320         if (busy_timer_.elapsed() > busy_timeout_) {
321             if (stop_flag) {
322                 emit popProgressStatus();
323                 return;
324             }
325             emit updateProgressStatus(row_num * 100 / physical_rows_.count());
326             // What's the least amount of processing that we can do which will draw
327             // the progress indicator?
328             wsApp->processEvents(QEventLoop::AllEvents, 1);
329             busy_timer_.restart();
330         }
331     }
332     emit popProgressStatus();
333
334     // XXX Use updateProgress instead. We'd have to switch from std::sort to
335     // something we can interrupt.
336     if (!col_title.isEmpty()) {
337         QString busy_msg = tr("Sorting \"%1\"").arg(col_title);
338         emit pushBusyStatus(busy_msg);
339     }
340
341     busy_timer_.restart();
342     sort_column_is_numeric_ = isNumericColumn(sort_column_);
343     std::sort(physical_rows_.begin(), physical_rows_.end(), recordLessThan);
344
345     beginResetModel();
346     visible_rows_.resize(0);
347     number_to_row_.fill(0);
348     foreach (PacketListRecord *record, physical_rows_) {
349         frame_data *fdata = record->frameData();
350
351         if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
352             visible_rows_ << record;
353             if (number_to_row_.size() <= (int)fdata->num) {
354                 number_to_row_.resize(fdata->num + 10000);
355             }
356             number_to_row_[fdata->num] = visible_rows_.count();
357         }
358     }
359     endResetModel();
360
361     if (!col_title.isEmpty()) {
362         emit popBusyStatus();
363     }
364
365     if (cap_file_->current_frame) {
366         emit goToPacket(cap_file_->current_frame->num);
367     }
368 }
369
370 bool PacketListModel::isNumericColumn(int column)
371 {
372     if (column < 0) {
373         return false;
374     }
375     switch (sort_cap_file_->cinfo.columns[column].col_fmt) {
376     case COL_8021Q_VLAN_ID:  /**< 0) 802.1Q vlan ID */
377     case COL_CUMULATIVE_BYTES: /**< 5) Cumulative number of bytes */
378     case COL_DELTA_TIME:     /**< 8) Delta time */
379     case COL_DELTA_TIME_DIS: /**< 9) Delta time displayed*/
380     case COL_UNRES_DST_PORT: /**< 13) Unresolved dest port */
381     case COL_FREQ_CHAN:      /**< 18) IEEE 802.11 (and WiMax?) - Channel */
382     case COL_RSSI:           /**< 25) IEEE 802.11 - received signal strength */
383     case COL_TX_RATE:        /**< 26) IEEE 802.11 - TX rate in Mbps */
384     case COL_NUMBER:         /**< 35) Packet list item number */
385     case COL_PACKET_LENGTH:  /**< 36) Packet length in bytes */
386     case COL_UNRES_SRC_PORT: /**< 44) Unresolved source port */
387     case COL_TEI:            /**< 45) Q.921 TEI */
388         return true;
389
390     /*
391      * Try to sort port numbers as number, if the numeric comparison fails (due
392      * to name resolution), it will fallback to string comparison.
393      * */
394     case COL_RES_DST_PORT:   /**< 12) Resolved dest port */
395     case COL_DEF_DST_PORT:   /**< 15) Destination port */
396     case COL_DEF_SRC_PORT:   /**< 40) Source port */
397     case COL_RES_SRC_PORT:   /**< 43) Resolved source port */
398         return true;
399
400     case COL_CUSTOM:
401         /* handle custom columns below. */
402         break;
403
404     default:
405         return false;
406     }
407
408     guint num_fields = g_slist_length(sort_cap_file_->cinfo.columns[column].col_custom_fields_ids);
409     for (guint i = 0; i < num_fields; i++) {
410         guint *field_idx = (guint *) g_slist_nth_data(sort_cap_file_->cinfo.columns[column].col_custom_fields_ids, i);
411         header_field_info *hfi = proto_registrar_get_nth(*field_idx);
412
413         /*
414          * Reject a field when there is no numeric field type or when:
415          * - there are (value_string) "strings"
416          *   (but do accept fields which have a unit suffix).
417          * - BASE_HEX or BASE_HEX_DEC (these have a constant width, string
418          *   comparison is faster than conversion to double).
419          * - BASE_CUSTOM (these can be formatted in any way).
420          */
421         if (!hfi ||
422               (hfi->strings != NULL && !(hfi->display & BASE_UNIT_STRING)) ||
423               !(((IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)) &&
424                  ((FIELD_DISPLAY(hfi->display) == BASE_DEC) ||
425                   (FIELD_DISPLAY(hfi->display) == BASE_OCT) ||
426                   (FIELD_DISPLAY(hfi->display) == BASE_DEC_HEX))) ||
427                 (hfi->type == FT_DOUBLE) || (hfi->type == FT_FLOAT) ||
428                 (hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
429                 (hfi->type == FT_RELATIVE_TIME))) {
430             return false;
431         }
432     }
433
434     return true;
435 }
436
437 bool PacketListModel::recordLessThan(PacketListRecord *r1, PacketListRecord *r2)
438 {
439     int cmp_val = 0;
440
441     // Wherein we try to cram the logic of packet_list_compare_records,
442     // _packet_list_compare_records, and packet_list_compare_custom from
443     // gtk/packet_list_store.c into one function
444
445     if (busy_timer_.elapsed() > busy_timeout_) {
446         // What's the least amount of processing that we can do which will draw
447         // the busy indicator?
448         wsApp->processEvents(QEventLoop::ExcludeUserInputEvents | QEventLoop::ExcludeSocketNotifiers, 1);
449         busy_timer_.restart();
450     }
451     if (sort_column_ < 0) {
452         // No column.
453         cmp_val = frame_data_compare(sort_cap_file_->epan, r1->frameData(), r2->frameData(), COL_NUMBER);
454     } else if (text_sort_column_ < 0) {
455         // Column comes directly from frame data
456         cmp_val = frame_data_compare(sort_cap_file_->epan, r1->frameData(), r2->frameData(), sort_cap_file_->cinfo.columns[sort_column_].col_fmt);
457     } else  {
458         if (r1->columnString(sort_cap_file_, sort_column_).constData() == r2->columnString(sort_cap_file_, sort_column_).constData()) {
459             cmp_val = 0;
460         } else if (sort_column_is_numeric_) {
461             // Custom column with numeric data (or something like a port number).
462             // Attempt to convert to numbers.
463             // XXX This is slow. Can we avoid doing this?
464             bool ok_r1, ok_r2;
465             double num_r1 = parseNumericColumn(r1->columnString(sort_cap_file_, sort_column_), &ok_r1);
466             double num_r2 = parseNumericColumn(r2->columnString(sort_cap_file_, sort_column_), &ok_r2);
467
468             if (!ok_r1 && !ok_r2) {
469                 cmp_val = 0;
470             } else if (!ok_r1 || (ok_r2 && num_r1 < num_r2)) {
471                 // either r1 is invalid (and sort it before others) or both
472                 // r1 and r2 are valid (sort normally)
473                 cmp_val = -1;
474             } else if (!ok_r2 || (ok_r1 && num_r1 > num_r2)) {
475                 cmp_val = 1;
476             }
477         } else {
478             cmp_val = strcmp(r1->columnString(sort_cap_file_, sort_column_).constData(), r2->columnString(sort_cap_file_, sort_column_).constData());
479         }
480
481         if (cmp_val == 0) {
482             // All else being equal, compare column numbers.
483             cmp_val = frame_data_compare(sort_cap_file_->epan, r1->frameData(), r2->frameData(), COL_NUMBER);
484         }
485     }
486
487     if (sort_order_ == Qt::AscendingOrder) {
488         return cmp_val < 0;
489     } else {
490         return cmp_val > 0;
491     }
492 }
493
494 // Parses a field as a double. Handle values with suffixes ("12ms"), negative
495 // values ("-1.23") and fields with multiple occurrences ("1,2"). Marks values
496 // that do not contain any numeric value ("Unknown") as invalid.
497 double PacketListModel::parseNumericColumn(const QString &val, bool *ok)
498 {
499     QByteArray ba = val.toUtf8();
500     const char *strval = ba.constData();
501     gchar *end = NULL;
502     double num = g_ascii_strtod(strval, &end);
503     *ok = strval != end;
504     return num;
505 }
506
507 // ::data is const so we have to make changes here.
508 void PacketListModel::emitItemHeightChanged(const QModelIndex &ih_index)
509 {
510     if (!ih_index.isValid()) return;
511
512     PacketListRecord *record = static_cast<PacketListRecord*>(ih_index.internalPointer());
513     if (!record) return;
514
515     if (record->lineCount() > max_line_count_) {
516         max_line_count_ = record->lineCount();
517         emit itemHeightChanged(ih_index);
518     }
519 }
520
521 int PacketListModel::rowCount(const QModelIndex &parent) const
522 {
523     if (parent.column() >= prefs.num_cols)
524         return 0;
525
526     return visible_rows_.count();
527 }
528
529 int PacketListModel::columnCount(const QModelIndex &) const
530 {
531     return prefs.num_cols;
532 }
533
534 QVariant PacketListModel::data(const QModelIndex &d_index, int role) const
535 {
536     if (!d_index.isValid())
537         return QVariant();
538
539     PacketListRecord *record = static_cast<PacketListRecord*>(d_index.internalPointer());
540     if (!record)
541         return QVariant();
542     const frame_data *fdata = record->frameData();
543     if (!fdata)
544         return QVariant();
545
546     switch (role) {
547     case Qt::TextAlignmentRole:
548         switch(recent_get_column_xalign(d_index.column())) {
549         case COLUMN_XALIGN_RIGHT:
550             return Qt::AlignRight;
551             break;
552         case COLUMN_XALIGN_CENTER:
553             return Qt::AlignCenter;
554             break;
555         case COLUMN_XALIGN_LEFT:
556             return Qt::AlignLeft;
557             break;
558         case COLUMN_XALIGN_DEFAULT:
559         default:
560             if (right_justify_column(d_index.column(), cap_file_)) {
561                 return Qt::AlignRight;
562             }
563             break;
564         }
565         return Qt::AlignLeft;
566
567     case Qt::BackgroundRole:
568         const color_t *color;
569         if (fdata->flags.ignored) {
570             color = &prefs.gui_ignored_bg;
571         } else if (fdata->flags.marked) {
572             color = &prefs.gui_marked_bg;
573         } else if (fdata->color_filter && recent.packet_list_colorize) {
574             const color_filter_t *color_filter = (const color_filter_t *) fdata->color_filter;
575             color = &color_filter->bg_color;
576         } else {
577             return QVariant();
578         }
579         return ColorUtils::fromColorT(color);
580     case Qt::ForegroundRole:
581         if (fdata->flags.ignored) {
582             color = &prefs.gui_ignored_fg;
583         } else if (fdata->flags.marked) {
584             color = &prefs.gui_marked_fg;
585         } else if (fdata->color_filter && recent.packet_list_colorize) {
586             const color_filter_t *color_filter = (const color_filter_t *) fdata->color_filter;
587             color = &color_filter->fg_color;
588         } else {
589             return QVariant();
590         }
591         return ColorUtils::fromColorT(color);
592     case Qt::DisplayRole:
593     {
594         int column = d_index.column();
595         QByteArray column_string = record->columnString(cap_file_, column, true);
596         // We don't know an item's sizeHint until we fetch its text here.
597         // Assume each line count is 1. If the line count changes, emit
598         // itemHeightChanged which triggers another redraw (including a
599         // fetch of SizeHintRole and DisplayRole) in the next event loop.
600         if (column == 0 && record->lineCountChanged() && record->lineCount() > max_line_count_) {
601             emit maxLineCountChanged(d_index);
602         }
603         return column_string;
604     }
605     case Qt::SizeHintRole:
606     {
607         // If this is the first row and column, return the maximum row height...
608         if (d_index.row() < 1 && d_index.column() < 1 && max_row_height_ > 0) {
609             QSize size = QSize(-1, max_row_height_);
610             return size;
611         }
612         // ...otherwise punt so that the item delegate can correctly calculate the item width.
613         return QVariant();
614     }
615     default:
616         return QVariant();
617     }
618 }
619
620 QVariant PacketListModel::headerData(int section, Qt::Orientation orientation,
621                                      int role) const
622 {
623     if (!cap_file_) return QVariant();
624
625     if (orientation == Qt::Horizontal && section < prefs.num_cols) {
626         switch (role) {
627         case Qt::DisplayRole:
628             return get_column_title(section);
629         case Qt::ToolTipRole:
630         {
631             gchar *tooltip = get_column_tooltip(section);
632             QVariant data(tooltip);
633             g_free (tooltip);
634             return data;
635         }
636         default:
637             break;
638         }
639     }
640
641     return QVariant();
642 }
643
644 void PacketListModel::flushVisibleRows()
645 {
646     gint pos = visible_rows_.count();
647
648     if (new_visible_rows_.count() > 0) {
649         beginInsertRows(QModelIndex(), pos, pos + new_visible_rows_.count());
650         foreach (PacketListRecord *record, new_visible_rows_) {
651             frame_data *fdata = record->frameData();
652
653             visible_rows_ << record;
654             if (number_to_row_.size() <= (int)fdata->num) {
655                 number_to_row_.resize(fdata->num + 10000);
656             }
657             number_to_row_[fdata->num] = visible_rows_.count();
658         }
659         endInsertRows();
660         new_visible_rows_.resize(0);
661     }
662 }
663
664 // Fill our column string and colorization cache while the application is
665 // idle. Try to be as conservative with the CPU and disk as possible.
666 static const int idle_dissection_interval_ = 5; // ms
667 void PacketListModel::dissectIdle(bool reset)
668 {
669     if (reset) {
670 //        qDebug() << "=di reset" << idle_dissection_row_;
671         idle_dissection_row_ = 0;
672     } else if (!idle_dissection_timer_->isValid()) {
673         return;
674     }
675
676     idle_dissection_timer_->restart();
677
678     int first = idle_dissection_row_;
679     while (idle_dissection_timer_->elapsed() < idle_dissection_interval_
680            && idle_dissection_row_ < physical_rows_.count()) {
681         ensureRowColorized(idle_dissection_row_);
682         idle_dissection_row_++;
683 //        if (idle_dissection_row_ % 1000 == 0) qDebug() << "=di row" << idle_dissection_row_;
684     }
685
686     if (idle_dissection_row_ < physical_rows_.count()) {
687         QTimer::singleShot(idle_dissection_interval_, this, SLOT(dissectIdle()));
688     } else {
689         idle_dissection_timer_->invalidate();
690     }
691
692     // report colorization progress
693     bgColorizationProgress(first+1, idle_dissection_row_+1);
694 }
695
696 // XXX Pass in cinfo from packet_list_append so that we can fill in
697 // line counts?
698 gint PacketListModel::appendPacket(frame_data *fdata)
699 {
700     PacketListRecord *record = new PacketListRecord(fdata);
701     gint pos = -1;
702
703 #ifdef DEBUG_PACKET_LIST_MODEL
704     if (fdata->num % 10000 == 1) {
705         log_resource_usage(fdata->num == 1, "%u packets", fdata->num);
706     }
707 #endif
708
709     physical_rows_ << record;
710
711     if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
712         new_visible_rows_ << record;
713         if (new_visible_rows_.count() < 2) {
714             // This is the first queued packet. Schedule an insertion for
715             // the next UI update.
716             QTimer::singleShot(0, this, SLOT(flushVisibleRows()));
717         }
718         pos = visible_rows_.count() + new_visible_rows_.count() - 1;
719     }
720
721     return pos;
722 }
723
724 frame_data *PacketListModel::getRowFdata(int row) {
725     if (row < 0 || row >= visible_rows_.count())
726         return NULL;
727     PacketListRecord *record = visible_rows_[row];
728     if (!record)
729         return NULL;
730     return record->frameData();
731 }
732
733 void PacketListModel::ensureRowColorized(int row)
734 {
735     if (row < 0 || row >= visible_rows_.count())
736         return;
737     PacketListRecord *record = visible_rows_[row];
738     if (!record)
739         return;
740     if (!record->colorized()) {
741         record->columnString(cap_file_, 1, true);
742     }
743 }
744
745 int PacketListModel::visibleIndexOf(frame_data *fdata) const
746 {
747     int row = 0;
748     foreach (PacketListRecord *record, visible_rows_) {
749         if (record->frameData() == fdata) {
750             return row;
751         }
752         row++;
753     }
754
755     return -1;
756 }
757
758 /*
759  * Editor modelines
760  *
761  * Local Variables:
762  * c-basic-offset: 4
763  * tab-width: 8
764  * indent-tabs-mode: nil
765  * End:
766  *
767  * ex: set shiftwidth=4 tabstop=8 expandtab:
768  * :indentSize=4:tabSize=8:noTabs=true:
769  */