merge_all_tap_menus() has been moved to menus.c.
[obnox/wireshark/wip.git] / gtk / packet_list_store.c
1 /* packet_list_store.c
2  * Routines to implement a custom GTK+ list model for Wireshark's packet list
3  * Copyright 2008-2009, Stephen Fisher (see AUTHORS file)
4  * * Co-authors Anders Broman and Kovarththanan Rajaratnam.
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
25  * USA.
26  */
27
28 /* This code was originally based on the GTK+ Tree View tutorial at
29  * http://scentric.net/tutorial */
30
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #include <string.h>
36
37 #include <gtk/gtk.h>
38 #include <glib.h>
39
40 #include "packet_list_store.h"
41 #include "ui_util.h"
42
43 #include <epan/epan_dissect.h>
44 #include <epan/column_info.h>
45 #include <epan/column.h>
46 #include <epan/nstime.h>
47
48 #include "color.h"
49 #include "color_filters.h"
50
51 #include "globals.h"
52
53 #include "../progress_dlg.h"
54
55 static void packet_list_init(PacketList *pkg_tree);
56 static void packet_list_class_init(PacketListClass *klass);
57 static void packet_list_tree_model_init(GtkTreeModelIface *iface);
58 static void packet_list_finalize(GObject *object);
59 static GtkTreeModelFlags packet_list_get_flags(GtkTreeModel *tree_model);
60 static gint packet_list_get_n_columns(GtkTreeModel *tree_model);
61 static GType packet_list_get_column_type(GtkTreeModel *tree_model, gint idx);
62 static gboolean packet_list_get_iter(GtkTreeModel *tree_model,
63                                          GtkTreeIter *iter, GtkTreePath *path);
64 static GtkTreePath *packet_list_get_path(GtkTreeModel *tree_model,
65                                          GtkTreeIter *iter);
66 static void packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
67                                   gint column, GValue *value);
68 static gboolean packet_list_iter_next(GtkTreeModel *tree_model,
69                                           GtkTreeIter *iter);
70 static gboolean packet_list_iter_children(GtkTreeModel *tree_model,
71                                           GtkTreeIter *iter,
72                                           GtkTreeIter *parent);
73 static gboolean packet_list_iter_has_child(GtkTreeModel *tree_model _U_,
74                                            GtkTreeIter *iter _U_);
75 static gint packet_list_iter_n_children(GtkTreeModel *tree_model,
76                                         GtkTreeIter *iter);
77 static gboolean packet_list_iter_nth_child(GtkTreeModel *tree_model,
78                                            GtkTreeIter *iter,
79                                            GtkTreeIter *parent,
80                                            gint n);
81 static gboolean packet_list_iter_parent(GtkTreeModel *tree_model _U_,
82                                         GtkTreeIter *iter _U_,
83                                         GtkTreeIter *child _U_);
84
85 static gboolean packet_list_sortable_get_sort_column_id(GtkTreeSortable
86                                                         *sortable,
87                                                         gint *sort_col_id,
88                                                         GtkSortType *order);
89 static void packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
90                                                         gint sort_col_id,
91                                                         GtkSortType order);
92 static void packet_list_sortable_set_sort_func(GtkTreeSortable *sortable,
93                                                    gint sort_col_id,
94                                                    GtkTreeIterCompareFunc sort_func,
95                                                    gpointer user_data,
96                                                    GDestroyNotify destroy_func);
97 static void packet_list_sortable_set_default_sort_func(GtkTreeSortable
98                                                            *sortable,
99                                                            GtkTreeIterCompareFunc
100                                                            sort_func,
101                                                            gpointer user_data,
102                                                            GDestroyNotify
103                                                            destroy_func);
104 static gboolean packet_list_sortable_has_default_sort_func(GtkTreeSortable
105                                                            *sortable);
106 static void packet_list_sortable_init(GtkTreeSortableIface *iface);
107 static gint packet_list_compare_records(gint sort_id _U_, PacketListRecord *a,
108                                         PacketListRecord *b);
109 static void packet_list_resort(PacketList *packet_list);
110 static void packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_columns, gboolean dissect_color );
111
112 static GObjectClass *parent_class = NULL;
113
114
115 GType
116 packet_list_get_type(void)
117 {
118         static GType packet_list_type = 0;
119
120         if(packet_list_type == 0) {
121                 static const GTypeInfo packet_list_info = {
122                         sizeof(PacketListClass),
123                         NULL, /* base_init */
124                         NULL, /* base_finalize */
125                         (GClassInitFunc) packet_list_class_init,
126                         NULL, /* class finalize */
127                         NULL, /* class_data */
128                         sizeof(PacketList),
129                         0, /* n_preallocs */
130                         (GInstanceInitFunc) packet_list_init,
131                         NULL /* value_table */
132                 };
133
134                 static const GInterfaceInfo tree_model_info = {
135                         (GInterfaceInitFunc) packet_list_tree_model_init,
136                         NULL,
137                         NULL
138                 };
139
140                 static const GInterfaceInfo tree_sortable_info = {
141                                 (GInterfaceInitFunc) packet_list_sortable_init,
142                                 NULL,
143                                 NULL
144                 };
145
146                 /* Register the new derived type with the GObject type system */
147                 packet_list_type = g_type_register_static(G_TYPE_OBJECT,
148                                                           "PacketList",
149                                                           &packet_list_info,
150                                                           (GTypeFlags)0);
151
152                 g_type_add_interface_static(packet_list_type,
153                                                 GTK_TYPE_TREE_MODEL,
154                                                 &tree_model_info);
155
156
157                 /* Register our GtkTreeModel interface with the type system */
158                 g_type_add_interface_static(packet_list_type,
159                                                 GTK_TYPE_TREE_SORTABLE,
160                                                 &tree_sortable_info);
161         }
162
163         return packet_list_type;
164 }
165
166 static void
167 packet_list_sortable_init(GtkTreeSortableIface *iface)
168 {
169         iface->get_sort_column_id = packet_list_sortable_get_sort_column_id;
170         iface->set_sort_column_id = packet_list_sortable_set_sort_column_id;
171         /* The following three functions are not implemented */
172         iface->set_sort_func = packet_list_sortable_set_sort_func;
173         iface->set_default_sort_func =
174                 packet_list_sortable_set_default_sort_func;
175         iface->has_default_sort_func =
176                 packet_list_sortable_has_default_sort_func;
177 }
178
179 static void
180 packet_list_class_init(PacketListClass *klass)
181 {
182         GObjectClass *object_class;
183
184         parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
185         object_class = (GObjectClass*) klass;
186
187         object_class->finalize = packet_list_finalize;
188
189 #if GTK_CHECK_VERSION(3,0,0)
190 #else
191         /* XXX this seems to affect TreeView Application wide
192          * Move to main.c ??? as it's not a bad thing(tm)
193          */
194         gtk_rc_parse_string (
195                 "style \"PacketList-style\"\n"
196                 "{\n"
197                 "  GtkTreeView::horizontal-separator = 0\n"
198                 "  GtkTreeView::vertical-separator = 1\n"
199                 "} widget_class \"*TreeView*\""
200                 " style \"PacketList-style\"");
201 #endif
202
203 }
204
205 static void
206 packet_list_tree_model_init(GtkTreeModelIface *iface)
207 {
208         iface->get_flags = packet_list_get_flags;
209         iface->get_n_columns = packet_list_get_n_columns;
210         iface->get_column_type = packet_list_get_column_type;
211         iface->get_iter = packet_list_get_iter;
212         iface->get_path = packet_list_get_path;
213         iface->get_value = packet_list_get_value;
214         iface->iter_next = packet_list_iter_next;
215         iface->iter_children = packet_list_iter_children;
216         iface->iter_has_child = packet_list_iter_has_child;
217         iface->iter_n_children = packet_list_iter_n_children;
218         iface->iter_nth_child = packet_list_iter_nth_child;
219         iface->iter_parent = packet_list_iter_parent;
220 }
221
222 /* This is called every time a new packet list object instance is created in
223  * packet_list_new.  Initialize the list structure's fields here. */
224 static void
225 packet_list_init(PacketList *packet_list)
226 {
227         guint i;
228
229         for(i = 0; i < (guint)cfile.cinfo.num_cols; i++) {
230                 /* We get the packetlist record for all columns */
231                 packet_list->column_types[i] = G_TYPE_STRING;
232         }
233
234         /* To check whether an iter belongs to our model. */
235         packet_list->stamp = g_random_int();
236
237         /* Note: We need one extra column to store the entire PacketListRecord */
238         packet_list->column_types[i] = G_TYPE_POINTER;
239         packet_list->n_columns = (guint)cfile.cinfo.num_cols+1;
240         packet_list->physical_rows = g_ptr_array_new();
241         packet_list->visible_rows = g_ptr_array_new();
242
243         packet_list->columnized = FALSE;
244         packet_list->sort_id = 0; /* defaults to first column for now */
245         packet_list->sort_order = GTK_SORT_ASCENDING;
246
247 #ifdef NEW_PACKET_LIST_STATISTICS
248         packet_list->const_strings = 0;
249 #endif
250 }
251
252 /* This function is called just before a packet list is destroyed.      Free
253  * dynamically allocated memory here. */
254 static void
255 packet_list_finalize(GObject *object)
256 {
257         /* PacketList *packet_list = PACKET_LIST(object); */
258
259         /* XXX - Free all records and free all memory used by the list */
260
261         /* must chain up - finalize parent */
262         (* parent_class->finalize) (object);
263 }
264
265 static GtkTreeModelFlags
266 packet_list_get_flags(GtkTreeModel *tree_model)
267 {
268         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
269                                  (GtkTreeModelFlags)0);
270
271         return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
272 }
273
274 static gint
275 packet_list_get_n_columns(GtkTreeModel *tree_model)
276 {
277         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
278
279         return PACKET_LIST(tree_model)->n_columns;
280 }
281
282 static GType
283 packet_list_get_column_type(GtkTreeModel *tree_model, gint idx)
284 {
285         PacketList *packet_list;
286         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
287         packet_list = PACKET_LIST(tree_model);
288         /* Note: We use one extra column to store the entire PacketListRecord */
289         g_return_val_if_fail(idx < packet_list->n_columns &&
290                                  idx >= 0, G_TYPE_INVALID);
291
292         return packet_list->column_types[idx];
293 }
294
295 static gboolean
296 packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
297                          GtkTreePath *path)
298 {
299         PacketList *packet_list;
300         PacketListRecord *record;
301         gint *indices, depth;
302         gint n;
303
304         g_assert(PACKETLIST_IS_LIST(tree_model));
305         g_assert(path != NULL);
306
307         indices = gtk_tree_path_get_indices(path);
308         depth = gtk_tree_path_get_depth(path);
309
310         /* we do not allow children since it's just a list */
311         g_assert(depth == 1);
312
313         n = indices[0]; /* the n-th top level row */
314
315         packet_list = PACKET_LIST(tree_model);
316         if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
317                 return FALSE;
318
319         if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, n))
320                 return FALSE;
321
322         record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, n);
323
324         g_assert(record->visible_pos == n);
325
326         /* We simply store a pointer to our custom record in the iter */
327         iter->stamp = packet_list->stamp;
328         iter->user_data = record;
329         iter->user_data2 = NULL;
330         iter->user_data3 = NULL;
331
332         return TRUE;
333 }
334
335 static GtkTreePath *
336 packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
337 {
338         GtkTreePath *path;
339         PacketListRecord *record;
340
341         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
342         g_return_val_if_fail(iter != NULL, NULL);
343         g_return_val_if_fail(iter->user_data != NULL, NULL);
344
345         record = (PacketListRecord*) iter->user_data;
346
347         path = gtk_tree_path_new();
348         gtk_tree_path_append_index(path, record->visible_pos);
349
350         return path;
351 }
352
353 static void
354 packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
355                           GValue *value)
356 {
357         PacketListRecord *record;
358         PacketList *packet_list;
359         GType type;
360
361         g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
362         g_return_if_fail(iter != NULL);
363
364         packet_list = PACKET_LIST(tree_model);
365         /* Note: We use one extra column to store the entire PacketListRecord */
366         g_return_if_fail(column < packet_list->n_columns);
367
368         record = (PacketListRecord*) iter->user_data;
369
370         g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->physical_rows, record->physical_pos));
371         g_return_if_fail(PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, record->visible_pos));
372
373         type = packet_list->column_types[column];
374         g_value_init(value, type);
375
376         /* XXX Probably the switch should be on column or
377          * should we allways return the pointer and read the data as required??
378          * If we use FOREGROUND_COLOR_COL etc we'll need a couple of "internal" columns
379          */
380         switch(type){
381                 case G_TYPE_POINTER:
382                         g_value_set_pointer(value, record);
383                         break;
384                 case G_TYPE_STRING:
385                         g_return_if_fail(record->col_text);
386                         g_value_set_string(value, record->col_text[column]);
387                         break;
388                 default:
389                         g_warning (G_STRLOC ": Unsupported type (%s) retrieved.", g_type_name (value->g_type));
390                         break;
391         }
392 }
393
394 static PacketListRecord *
395 packet_list_iter_next_visible(PacketList *packet_list, PacketListRecord *record)
396 {
397         PacketListRecord *nextrecord;
398         gint next_visible_pos;
399
400         g_assert(record->visible_pos >= 0);
401         next_visible_pos = record->visible_pos + 1;
402
403         /* Is this the last record in the list? */
404         if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, next_visible_pos))
405                 return NULL;
406
407         nextrecord = PACKET_LIST_RECORD_GET(packet_list->visible_rows, next_visible_pos);
408
409         g_assert(nextrecord->visible_pos == (record->visible_pos + 1));
410         g_assert(nextrecord->physical_pos >= (record->physical_pos + 1));
411
412         return nextrecord;
413 }
414
415 /* Takes an iter structure and sets it to point to the next row. */
416 static gboolean
417 packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
418 {
419         PacketListRecord *record, *nextrecord;
420         PacketList *packet_list;
421
422         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
423
424         if(iter == NULL)
425                 return FALSE;
426
427         g_return_val_if_fail(iter->user_data, FALSE);
428
429         packet_list = PACKET_LIST(tree_model);
430
431         record = (PacketListRecord*) iter->user_data;
432         nextrecord = packet_list_iter_next_visible(packet_list, record);
433
434         if (!nextrecord)
435                 return FALSE;
436
437         iter->stamp = packet_list->stamp;
438         iter->user_data = nextrecord;
439
440         return TRUE;
441 }
442
443 static gboolean
444 packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
445                           GtkTreeIter *parent)
446 {
447         PacketList *packet_list;
448
449         g_return_val_if_fail(parent == NULL || parent->user_data != NULL, FALSE);
450
451         /* This is a list, nodes have no children. */
452         if(parent)
453                 return FALSE;
454
455         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
456
457         packet_list = PACKET_LIST(tree_model);
458
459         /* No rows => no first row */
460         if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
461                 return FALSE;
462
463         /* Set iter to first item in list */
464         iter->stamp = packet_list->stamp;
465         iter->user_data = PACKET_LIST_RECORD_GET(packet_list->visible_rows, 0);
466
467         return TRUE;
468 }
469
470 static gboolean
471 packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
472 {
473         return FALSE; /* Lists have no children */
474 }
475
476 static gint
477 packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
478 {
479         PacketList *packet_list;
480
481         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
482         g_return_val_if_fail(iter == NULL || iter->user_data != NULL, 0);
483
484         packet_list = PACKET_LIST(tree_model);
485
486         if(!iter) {
487                 /* special case: if iter == NULL, return number of top-level rows */
488                 return PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
489         }
490         else {
491                 /* Lists have zero children */
492                 return 0;
493         }
494 }
495
496 static gboolean
497 packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
498                            GtkTreeIter *parent, gint n)
499 {
500         PacketListRecord *record;
501         PacketList *packet_list;
502
503         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
504
505         packet_list = PACKET_LIST(tree_model);
506
507         /* A list only has top-level rows */
508         if(parent)
509                 return FALSE;
510
511         /* Special case: if parent == NULL, set iter to n-th top-level row. */
512         if(!PACKET_LIST_RECORD_INDEX_VALID(packet_list->visible_rows, n))
513                 return FALSE;
514
515         record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, n);
516
517         g_assert(record->visible_pos == n);
518
519         iter->stamp = packet_list->stamp;
520         iter->user_data = record;
521
522         return TRUE;
523 }
524
525 static gboolean
526 packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
527                         GtkTreeIter *child _U_)
528 {
529         return FALSE; /* No parents since no children in a list */
530 }
531
532 PacketList *
533 new_packet_list_new(void)
534 {
535         PacketList *newpacketlist;
536
537         newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
538
539         g_assert(newpacketlist != NULL);
540
541         return newpacketlist;
542 }
543
544 #if 0
545 static void
546 packet_list_row_deleted(PacketList *packet_list, guint pos)
547 {
548         GtkTreePath *path;
549
550         /* Inform the tree view and other interested objects (such as tree row
551          * references) that we have deleted a row */
552         path = gtk_tree_path_new();
553         gtk_tree_path_append_index(path, pos);
554
555         gtk_tree_model_row_deleted(GTK_TREE_MODEL(packet_list), path);
556
557         gtk_tree_path_free(path);
558 }
559 #endif
560
561 void
562 new_packet_list_store_clear(PacketList *packet_list)
563 {
564         g_return_if_fail(packet_list != NULL);
565         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
566
567         /* Don't issue a row_deleted signal. We rely on our caller to have disconnected
568          * the model from the view.
569         for( ; packet_list->num_rows > 0; --packet_list->num_rows)
570                 packet_list_row_deleted(packet_list, packet_list->num_rows-1);
571         */
572
573         /* XXX - hold on to these rows and reuse them instead */
574         if(packet_list->physical_rows)
575                 g_ptr_array_free(packet_list->physical_rows, TRUE);
576         if(packet_list->visible_rows)
577                 g_ptr_array_free(packet_list->visible_rows, TRUE);
578         packet_list->physical_rows = g_ptr_array_new();
579         packet_list->visible_rows = g_ptr_array_new();
580
581         packet_list->columnized = FALSE;
582
583 #ifdef NEW_PACKET_LIST_STATISTICS
584         g_warning("Const strings: %u", packet_list->const_strings);
585         packet_list->const_strings = 0;
586 #endif
587 }
588
589 static void
590 packet_list_row_inserted(PacketList *packet_list, guint pos)
591 {
592         GtkTreeIter iter;
593         GtkTreePath *path;
594
595         /* Inform the tree view and other interested objects (such as tree row
596          * references) that we have inserted a new row and where it was
597          * inserted. */
598         path = gtk_tree_path_new();
599         gtk_tree_path_append_index(path, pos);
600
601         packet_list_get_iter(GTK_TREE_MODEL(packet_list), &iter, path);
602
603         gtk_tree_model_row_inserted(GTK_TREE_MODEL(packet_list), path, &iter);
604
605         gtk_tree_path_free(path);
606 }
607
608 gboolean
609 packet_list_visible_record(PacketList *packet_list, GtkTreeIter *iter)
610 {
611         PacketListRecord *record;
612
613         g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), FALSE);
614
615         if(iter == NULL || iter->user_data == NULL)
616                 return FALSE;
617
618         record = (PacketListRecord*) iter->user_data;
619
620         g_return_val_if_fail(record, FALSE);
621         g_return_val_if_fail(record->fdata, FALSE);
622
623         return (record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time);
624 }
625
626 gint
627 packet_list_append_record(PacketList *packet_list, frame_data *fdata)
628 {
629         PacketListRecord *newrecord;
630         GtkTreeModel *model = GTK_TREE_MODEL(packet_list);
631
632         g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), -1);
633
634         newrecord = se_alloc(sizeof(PacketListRecord));
635         newrecord->columnized   = FALSE;
636         newrecord->colorized    = FALSE;
637         newrecord->col_text_len = se_alloc0(sizeof(*newrecord->col_text_len) * cfile.cinfo.num_cols);
638         newrecord->col_text     = se_alloc0(sizeof(*newrecord->col_text) * cfile.cinfo.num_cols);
639         newrecord->fdata        = fdata;
640         newrecord->physical_pos = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
641
642         if (fdata->flags.passed_dfilter || fdata->flags.ref_time) {
643                 newrecord->visible_pos = PACKET_LIST_RECORD_COUNT(packet_list->visible_rows);
644                 PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, newrecord);
645         }
646         else
647                 newrecord->visible_pos = -1;
648
649         PACKET_LIST_RECORD_APPEND(packet_list->physical_rows, newrecord);
650
651         packet_list->columnized = FALSE;   /* XXX, dissect? */
652
653         /*
654          * Issue a row_inserted signal if the model is connected
655          * and the row is visible.
656          */
657         if((model)&&(newrecord->visible_pos!=-1))
658                 packet_list_row_inserted(packet_list, newrecord->visible_pos);
659
660         /* XXXX If the model is connected and sort column != frame_num we should
661          * probably resort.
662          * Don't resort the list for every row, the list will be in packet order any way.
663          * packet_list_resort(packet_list);
664          */
665
666         return newrecord->visible_pos;
667 }
668
669 void
670 packet_list_change_record(PacketList *packet_list, guint row, gint col, column_info *cinfo)
671 {
672         PacketListRecord *record;
673         gchar *str;
674
675         g_return_if_fail(packet_list);
676         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
677
678         g_assert(row < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows));
679
680         record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, row);
681
682         g_assert(record->physical_pos == row);
683
684         g_assert((record->col_text != NULL)&&(record->col_text_len != NULL));
685
686         if (record->col_text[col] != NULL)
687                 /* TODO: Column already contains a value. Bail out */
688                 return;
689
690         switch (cfile.cinfo.col_fmt[col]) {
691                 case COL_DEF_SRC:
692                 case COL_RES_SRC:       /* COL_DEF_SRC is currently just like COL_RES_SRC */
693                 case COL_UNRES_SRC:
694                 case COL_DEF_DL_SRC:
695                 case COL_RES_DL_SRC:
696                 case COL_UNRES_DL_SRC:
697                 case COL_DEF_NET_SRC:
698                 case COL_RES_NET_SRC:
699                 case COL_UNRES_NET_SRC:
700                 case COL_DEF_DST:
701                 case COL_RES_DST:       /* COL_DEF_DST is currently just like COL_RES_DST */
702                 case COL_UNRES_DST:
703                 case COL_DEF_DL_DST:
704                 case COL_RES_DL_DST:
705                 case COL_UNRES_DL_DST:
706                 case COL_DEF_NET_DST:
707                 case COL_RES_NET_DST:
708                 case COL_UNRES_NET_DST:
709                 case COL_PROTOCOL:
710                 case COL_INFO:
711                 case COL_IF_DIR:
712                 case COL_DCE_CALL:
713                 case COL_8021Q_VLAN_ID:
714                 case COL_EXPERT:
715                 case COL_FREQ_CHAN:
716                         if (cinfo->col_data[col] && cinfo->col_data[col] != cinfo->col_buf[col]) {
717                                 /* This is a constant string, so we don't have to copy it */
718                                 record->col_text[col] = (gchar *) cinfo->col_data[col];
719                                 record->col_text_len[col] = (guint) strlen(record->col_text[col]);
720 #ifdef NEW_PACKET_LIST_STATISTICS
721                                 ++packet_list->const_strings;
722 #endif
723                                 break;
724                         }
725                 /* !! FALL-THROUGH!! */
726
727                 default:
728                         record->col_text_len[col] = (guint) strlen(cinfo->col_data[col]);
729
730                         if (!record->col_text_len[col]) {
731                                 record->col_text[col] = "";
732 #ifdef NEW_PACKET_LIST_STATISTICS
733                                 ++packet_list->const_strings;
734 #endif
735                                 break;
736                         }
737
738                         if(!packet_list->string_pool)
739                                 packet_list->string_pool = g_string_chunk_new(32);
740                         if (!get_column_resolved (col) && cinfo->col_expr.col_expr_val[col]) {
741                                 /* Use the unresolved value in col_expr_val */
742                                 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_expr.col_expr_val[col]);
743                         } else {
744                                 str = g_string_chunk_insert_const (packet_list->string_pool, (const gchar *)cinfo->col_data[col]);
745                         }
746                         record->col_text[col] = str;
747                         break;
748         }
749 }
750
751 static gboolean
752 packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
753                                         gint *sort_col_id,
754                                         GtkSortType *order)
755 {
756         PacketList *packet_list;
757
758         g_return_val_if_fail(sortable != NULL, FALSE);
759         g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
760
761         packet_list = PACKET_LIST(sortable);
762
763         if(sort_col_id)
764                 *sort_col_id = packet_list->sort_id;
765
766         if(order)
767                 *order = packet_list->sort_order;
768
769         return TRUE;
770 }
771
772 static gboolean
773 packet_list_column_contains_values(PacketList *packet_list, gint sort_col_id)
774 {
775         if (packet_list->columnized || col_based_on_frame_data(&cfile.cinfo, sort_col_id))
776                 return TRUE;
777         else
778                 return FALSE;
779 }
780
781 /* packet_list_dissect_and_cache_all()
782  *  returns:
783  *   TRUE   if columnization completed;
784  *            packet_list->columnized set to TRUE;
785  *   FALSE: columnization did not complete (i.e., was stopped by the user);
786  *            packet_list->columnized unchanged (i.e., FALSE).
787  */
788
789 static gboolean
790 packet_list_dissect_and_cache_all(PacketList *packet_list)
791 {
792         PacketListRecord *record;
793
794         int             progbar_nextstep;
795         int             progbar_quantum;
796         gboolean        progbar_stop_flag;
797         GTimeVal        progbar_start_time;
798         float           progbar_val;
799         progdlg_t  *progbar = NULL;
800         gchar           progbar_status_str[100];
801         gint            progbar_loop_max;
802         gint            progbar_loop_var;
803         gint            progbar_updates = 100 /* 100% */;
804
805         g_assert(packet_list->columnized == FALSE);
806
807         progbar_loop_max = PACKET_LIST_RECORD_COUNT(packet_list->physical_rows);
808         /* Update the progress bar when it gets to this value. */
809         progbar_nextstep = 0;
810         /* When we reach the value that triggers a progress bar update,
811            bump that value by this amount. */
812         progbar_quantum = progbar_loop_max/progbar_updates;
813         /* Progress so far. */
814         progbar_val = 0.0f;
815
816         progbar_stop_flag = FALSE;
817         g_get_current_time(&progbar_start_time);
818
819         main_window_update();
820
821         for (progbar_loop_var = 0; progbar_loop_var < progbar_loop_max; ++progbar_loop_var) {
822                 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, progbar_loop_var);
823                 packet_list_dissect_and_cache_record(packet_list, record, TRUE, FALSE);
824
825                 /* Create the progress bar if necessary.
826                    We check on every iteration of the loop, so that it takes no
827                    longer than the standard time to create it (otherwise, for a
828                    large file, we might take considerably longer than that standard
829                    time in order to get to the next progress bar step). */
830                 if (progbar == NULL)
831                         /* Note: The following may call gtk_main_iteration() which will */
832                         /*       allow certain "interupts" to happen during this code.  */
833                         /*       (Note that the progress_dlg window is set to "modal"   */
834                         /*        so that clicking on other windows is disabled).       */
835                         progbar = delayed_create_progress_dlg("Construct", "Columns",
836                                                               TRUE, &progbar_stop_flag,
837                                                               &progbar_start_time, progbar_val);
838
839                 if (progbar_loop_var >= progbar_nextstep) {
840                         /* let's not divide by zero. We should never be started
841                          * with count == 0, so let's assert that */
842                         g_assert(progbar_loop_max > 0);
843
844                         progbar_val = (gfloat) progbar_loop_var / progbar_loop_max;
845
846                         if (progbar != NULL) {
847                                 g_snprintf(progbar_status_str, sizeof(progbar_status_str),
848                                            "%u of %u frames", progbar_loop_var+1, progbar_loop_max);
849                                 /* Note: See comment above re use of gtk_main_iteration() */
850                                 update_progress_dlg(progbar, progbar_val, progbar_status_str);
851                         }
852
853                         progbar_nextstep += progbar_quantum;
854                 }
855
856                 if (progbar_stop_flag) {
857                         /* Well, the user decided to abort ... */
858                         break;
859                 }
860         }
861
862         /* We're done; destroy the progress bar if it was created. */
863         if (progbar != NULL)
864                 destroy_progress_dlg(progbar);
865
866         if (progbar_stop_flag) {
867                 return FALSE; /* user aborted before columnization completed */
868         }
869
870         packet_list->columnized = TRUE;
871         return TRUE;
872 }
873
874 /* packet_list_do_packet_list_dissect_and_cache_all()
875  *  returns:
876  *    TRUE:  if columnization not needed or columnization completed;
877  *    FALSE: columnization did not complete (i.e., stopped by the user)
878  */
879 gboolean
880 packet_list_do_packet_list_dissect_and_cache_all(PacketList *packet_list, gint sort_col_id)
881 {
882         if (!packet_list_column_contains_values(packet_list, sort_col_id)) {
883                 return packet_list_dissect_and_cache_all(packet_list);
884         }
885         return TRUE;
886 }
887
888 static void
889 packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
890                                         gint sort_col_id,
891                                         GtkSortType order)
892 {
893         PacketList *packet_list;
894
895         g_return_if_fail(sortable != NULL);
896         g_return_if_fail(PACKETLIST_IS_LIST(sortable));
897
898         packet_list = PACKET_LIST(sortable);
899
900         if(packet_list->sort_id == sort_col_id &&
901            packet_list->sort_order == order)
902                 return;
903
904         packet_list->sort_id = sort_col_id;
905         packet_list->sort_order = order;
906
907         if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
908                 return;
909
910         packet_list_resort(packet_list);
911
912         /* emit "sort-column-changed" signal to tell any tree views
913          * that the sort column has changed (so the little arrow
914          * in the column header of the sort column is drawn
915          * in the right column) */
916
917         gtk_tree_sortable_sort_column_changed(sortable);
918 }
919
920 static void
921 packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
922                                    gint sort_col_id _U_,
923                                    GtkTreeIterCompareFunc sort_func _U_,
924                                    gpointer user_data _U_,
925                                    GDestroyNotify destroy_func _U_)
926 {
927         g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
928 }
929
930 static void
931 packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
932                                            GtkTreeIterCompareFunc sort_func _U_,
933                                            gpointer user_data _U_,
934                                            GDestroyNotify destroy_func _U_)
935 {
936         g_warning(G_STRLOC ": is not supported by the PacketList model.\n");
937 }
938
939 static gboolean
940 packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
941 {
942         return FALSE; /* Since packet_list_sortable_set_sort_func and
943                          set_default_sort_func are not implemented. */
944 }
945
946 static gint
947 packet_list_compare_custom(gint sort_id, PacketListRecord *a, PacketListRecord *b)
948 {
949         header_field_info *hfi;
950
951         hfi = proto_registrar_get_byname(cfile.cinfo.col_custom_field[sort_id]);
952
953         if (hfi == NULL) {
954                 return frame_data_compare(a->fdata, b->fdata, COL_NUMBER);
955         } else if ((hfi->strings == NULL) &&
956                    (((IS_FT_INT(hfi->type) || IS_FT_UINT(hfi->type)) &&
957                      ((hfi->display == BASE_DEC) || (hfi->display == BASE_DEC_HEX) ||
958                       (hfi->display == BASE_OCT))) ||
959                     (hfi->type == FT_DOUBLE) || (hfi->type == FT_FLOAT) ||
960                     (hfi->type == FT_BOOLEAN) || (hfi->type == FT_FRAMENUM) ||
961                     (hfi->type == FT_RELATIVE_TIME)))
962           {
963                 /* Attempt to convert to numbers */
964                 double num_a = atof(a->col_text[sort_id]);
965                 double num_b = atof(b->col_text[sort_id]);
966
967                 if (num_a < num_b)
968                         return -1;
969                 else if (num_a > num_b)
970                         return 1;
971                 else
972                         return frame_data_compare(a->fdata, b->fdata, COL_NUMBER);
973           }
974
975         return strcmp(a->col_text[sort_id], b->col_text[sort_id]);
976 }
977
978 static gint
979 _packet_list_compare_records(gint sort_id, PacketListRecord *a,
980                                 PacketListRecord *b)
981 {
982         g_assert(a->col_text);
983         g_assert(b->col_text);
984         g_assert(a->col_text[sort_id]);
985         g_assert(b->col_text[sort_id]);
986
987         if(a->col_text[sort_id] == b->col_text[sort_id])
988                 return 0; /* no need to call strcmp() */
989
990         if (cfile.cinfo.col_fmt[sort_id] == COL_CUSTOM) {
991                 return packet_list_compare_custom (sort_id, a, b);
992         }
993         return strcmp(a->col_text[sort_id], b->col_text[sort_id]);
994 }
995
996 static gint
997 packet_list_compare_records(gint sort_id, PacketListRecord *a,
998                                 PacketListRecord *b)
999 {
1000         gint ret;
1001
1002         if (col_based_on_frame_data(&cfile.cinfo, sort_id))
1003                 return frame_data_compare(a->fdata, b->fdata, cfile.cinfo.col_fmt[sort_id]);
1004
1005         ret = _packet_list_compare_records(sort_id, a, b);
1006         if (ret == 0)
1007                 ret = a->fdata->num - b->fdata->num;
1008         return ret;
1009 }
1010
1011 static gint
1012 packet_list_qsort_physical_compare_func(PacketListRecord **a, PacketListRecord **b,
1013                                    PacketList *packet_list)
1014 {
1015         gint ret;
1016
1017         g_assert((a) && (b) && (packet_list));
1018
1019         ret = packet_list_compare_records(packet_list->sort_id, *a, *b);
1020
1021         /* Swap -1 and 1 if sort order is reverse */
1022         if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
1023                 ret = (ret < 0) ? 1 : -1;
1024
1025         return ret;
1026 }
1027
1028 static void
1029 packet_list_resort(PacketList *packet_list)
1030 {
1031         PacketListRecord *record;
1032         GtkTreePath *path;
1033         gint *neworder;
1034         guint phy_idx;
1035         guint vis_idx;
1036
1037         g_return_if_fail(packet_list != NULL);
1038         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1039
1040         if(PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1041                 return;
1042
1043         /* resort physical rows according to sorting column */
1044         g_ptr_array_sort_with_data(packet_list->physical_rows,
1045                           (GCompareDataFunc) packet_list_qsort_physical_compare_func,
1046                           packet_list);
1047
1048         /* let other objects know about the new order */
1049         neworder = g_new0(gint, PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1050
1051         for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1052                 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1053                 record->physical_pos = phy_idx;
1054                 g_assert(record->visible_pos >= -1);
1055                 if (record->visible_pos >= 0) {
1056                         g_assert(record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time);
1057                         neworder[vis_idx] = record->visible_pos;
1058                         PACKET_LIST_RECORD_SET(packet_list->visible_rows, vis_idx, record);
1059                         record->visible_pos = vis_idx;
1060                         ++vis_idx;
1061                 }
1062         }
1063
1064         g_assert(vis_idx == PACKET_LIST_RECORD_COUNT(packet_list->visible_rows));
1065
1066         path = gtk_tree_path_new();
1067
1068         gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
1069                                           neworder);
1070
1071         gtk_tree_path_free(path);
1072         g_free(neworder);
1073 }
1074
1075 guint
1076 packet_list_recreate_visible_rows(PacketList *packet_list)
1077 {
1078         guint phy_idx;
1079         guint vis_idx;
1080         PacketListRecord *record;
1081
1082         g_return_val_if_fail(packet_list != NULL, 0);
1083         g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), 0);
1084
1085         if(PACKET_LIST_RECORD_COUNT(packet_list->physical_rows) == 0)
1086                 return 0;
1087
1088         if(packet_list->visible_rows)
1089                 g_ptr_array_free(packet_list->visible_rows, TRUE);
1090
1091         packet_list->visible_rows = g_ptr_array_new();
1092
1093         for(phy_idx = 0, vis_idx = 0; phy_idx < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++phy_idx) {
1094                 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, phy_idx);
1095                 if (record->fdata->flags.passed_dfilter || record->fdata->flags.ref_time) {
1096                         record->visible_pos = vis_idx++;
1097                         PACKET_LIST_RECORD_APPEND(packet_list->visible_rows, record);
1098                 }
1099                 else
1100                         record->visible_pos = -1;
1101         }
1102
1103         return vis_idx;
1104 }
1105
1106 void
1107 packet_list_dissect_and_cache_iter(PacketList *packet_list, GtkTreeIter *iter, gboolean dissect_columns, gboolean dissect_color)
1108 {
1109         PacketListRecord *record;
1110
1111         g_return_if_fail(packet_list != NULL);
1112         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
1113         g_return_if_fail(iter != NULL);
1114         g_return_if_fail(iter->user_data != NULL);
1115
1116         record = iter->user_data;
1117
1118         packet_list_dissect_and_cache_record(packet_list, record, dissect_columns, dissect_color);
1119 }
1120
1121 static void
1122 packet_list_dissect_and_cache_record(PacketList *packet_list, PacketListRecord *record, gboolean dissect_columns, gboolean dissect_color)
1123 {
1124         epan_dissect_t edt;
1125         frame_data *fdata;
1126         column_info *cinfo;
1127         gint col;
1128         gboolean create_proto_tree;
1129         union wtap_pseudo_header pseudo_header; /* Packet pseudo_header */
1130         guint8 pd[WTAP_MAX_PACKET_SIZE];  /* Packet data */
1131
1132         /* XXX: Does it work to check if the record is already columnized/colorized ?
1133          *      i.e.: test record->columnized and record->colorized and just return
1134          *            if they're both TRUE.
1135          *      Note: Part of the patch submitted with Bug #4273 had code to do this but it
1136          *            was commented out in the patch and was not included in SVN #33011.
1137          */
1138         fdata = record->fdata;
1139
1140         if (dissect_columns)
1141                 cinfo = &cfile.cinfo;
1142         else
1143                 cinfo = NULL;
1144
1145         if (!cf_read_frame_r(&cfile, fdata, &pseudo_header, pd)) {
1146                 /*
1147                  * Error reading the frame.
1148                  *
1149                  * Don't set the color filter for now (we might want
1150                  * to colorize it in some fashion to warn that the
1151                  * row couldn't be filled in or colorized), and
1152                  * set the columns to placeholder values, except
1153                  * for the Info column, where we'll put in an
1154                  * error message.
1155                  */
1156                 if (dissect_columns) {
1157                         col_fill_in_error(cinfo, fdata, FALSE, FALSE /* fill_fd_columns */);
1158
1159                         for(col = 0; col < cinfo->num_cols; ++col) {
1160                                 /* Skip columns based on frame_data because we already store those. */
1161                                 if (!col_based_on_frame_data(cinfo, col))
1162                                         packet_list_change_record(packet_list, record->physical_pos, col, cinfo);
1163                         }
1164                         record->columnized = TRUE;
1165                 }
1166                 if (dissect_color) {
1167                         fdata->color_filter = NULL;
1168                         record->colorized = TRUE;
1169                 }
1170                 return; /* error reading the frame */
1171         }
1172
1173         create_proto_tree = (color_filters_used() && dissect_color) ||
1174                                                 (have_custom_cols(cinfo) && dissect_columns);
1175
1176         epan_dissect_init(&edt,
1177                                           create_proto_tree,
1178                                           FALSE /* proto_tree_visible */);
1179
1180         if (dissect_color)
1181                 color_filters_prime_edt(&edt);
1182         if (dissect_columns)
1183                 col_custom_prime_edt(&edt, cinfo);
1184
1185         epan_dissect_run(&edt, &pseudo_header, pd, fdata, cinfo);
1186
1187         if (dissect_color)
1188                 fdata->color_filter = color_filters_colorize_packet(&edt);
1189
1190         if (dissect_columns) {
1191                 /* "Stringify" non frame_data vals */
1192                 epan_dissect_fill_in_columns(&edt, FALSE, FALSE /* fill_fd_columns */);
1193
1194                 for(col = 0; col < cinfo->num_cols; ++col) {
1195                         /* Skip columns based on frame_data because we already store those. */
1196                         if (!col_based_on_frame_data(cinfo, col))
1197                                 packet_list_change_record(packet_list, record->physical_pos, col, cinfo);
1198                 }
1199         }
1200
1201         if (dissect_columns)
1202                 record->columnized = TRUE;
1203         if (dissect_color)
1204                 record->colorized = TRUE;
1205
1206         epan_dissect_cleanup(&edt);
1207 }
1208
1209 void
1210 packet_list_reset_colorized(PacketList *packet_list)
1211 {
1212         PacketListRecord *record;
1213         guint i;
1214
1215         for(i = 0; i < PACKET_LIST_RECORD_COUNT(packet_list->physical_rows); ++i) {
1216                 record = PACKET_LIST_RECORD_GET(packet_list->physical_rows, i);
1217                 record->colorized = FALSE;
1218         }
1219 }
1220
1221 const char*
1222 packet_list_get_widest_column_string(PacketList *packet_list, gint col)
1223 {
1224         g_return_val_if_fail(packet_list != NULL, NULL);
1225         g_return_val_if_fail(PACKETLIST_IS_LIST(packet_list), NULL);
1226         g_return_val_if_fail(col < packet_list->n_columns && col >= 0, NULL);
1227
1228         if (PACKET_LIST_RECORD_COUNT(packet_list->visible_rows) == 0)
1229                 return "";
1230
1231         if (col_based_on_frame_data(&cfile.cinfo, col)) {
1232                 PacketListRecord *record;
1233                 guint vis_idx;
1234
1235                 frame_data fdata;
1236                 memset (&fdata, 0, sizeof fdata);
1237
1238                 nstime_set_zero(&fdata.abs_ts);
1239                 nstime_set_zero(&fdata.rel_ts);
1240                 nstime_set_zero(&fdata.del_cap_ts);
1241                 nstime_set_zero(&fdata.del_dis_ts);
1242
1243                 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1244                         record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1245                         switch (cfile.cinfo.col_fmt[col]) {
1246
1247                         case COL_NUMBER:
1248                                 if (record->fdata->num > fdata.num)
1249                                         fdata.num = record->fdata->num;
1250                                 break;
1251                         case COL_PACKET_LENGTH:
1252                                 if (record->fdata->pkt_len > fdata.pkt_len)
1253                                         fdata.pkt_len = record->fdata->pkt_len;
1254                                 break;
1255                         case COL_CUMULATIVE_BYTES:
1256                                 if (record->fdata->cum_bytes > fdata.cum_bytes)
1257                                         fdata.cum_bytes = record->fdata->cum_bytes;
1258                                 break;
1259                         case COL_ABS_TIME:
1260                         case COL_ABS_DATE_TIME:
1261                         case COL_UTC_TIME:
1262                         case COL_UTC_DATE_TIME:
1263                                 if (nstime_cmp(&record->fdata->abs_ts, &fdata.abs_ts) > 0)
1264                                         fdata.abs_ts = record->fdata->abs_ts;
1265                                 break;
1266                         case COL_REL_TIME:
1267                                 if (nstime_cmp(&record->fdata->rel_ts, &fdata.rel_ts) > 0)
1268                                         fdata.rel_ts = record->fdata->rel_ts;
1269                                 break;
1270                         case COL_DELTA_TIME:
1271                                 if (nstime_cmp(&record->fdata->del_cap_ts, &fdata.del_cap_ts) > 0)
1272                                         fdata.del_cap_ts = record->fdata->del_cap_ts;
1273                                 break;
1274                         case COL_DELTA_TIME_DIS:
1275                                 if (nstime_cmp(&record->fdata->del_dis_ts, &fdata.del_dis_ts) > 0)
1276                                         fdata.del_dis_ts = record->fdata->del_dis_ts;
1277                                 break;
1278                         case COL_CLS_TIME:
1279                                 switch (timestamp_get_type()) {
1280                                 case TS_ABSOLUTE:
1281                                 case TS_ABSOLUTE_WITH_DATE:
1282                                 case TS_UTC:
1283                                 case TS_UTC_WITH_DATE:
1284                                   if (nstime_cmp(&record->fdata->abs_ts, &fdata.abs_ts) > 0)
1285                                           fdata.abs_ts = record->fdata->abs_ts;
1286                                   break;
1287
1288                                 case TS_RELATIVE:
1289                                   if (nstime_cmp(&record->fdata->rel_ts, &fdata.rel_ts) > 0)
1290                                           fdata.rel_ts = record->fdata->rel_ts;
1291                                   break;
1292
1293                                 case TS_DELTA:
1294                                   if (nstime_cmp(&record->fdata->del_cap_ts, &fdata.del_cap_ts) > 0)
1295                                           fdata.del_cap_ts = record->fdata->del_cap_ts;
1296                                   break;
1297
1298                                 case TS_DELTA_DIS:
1299                                   if (nstime_cmp(&record->fdata->del_dis_ts, &fdata.del_dis_ts) > 0)
1300                                           fdata.del_dis_ts = record->fdata->del_dis_ts;
1301                                   break;
1302
1303                                 case TS_EPOCH:
1304                                   if (nstime_cmp(&record->fdata->abs_ts, &fdata.abs_ts) > 0)
1305                                           fdata.abs_ts = record->fdata->abs_ts;
1306                                   break;
1307
1308                                 case TS_NOT_SET:
1309                                   /* code is missing for this case, but I don't know which [jmayer20051219] */
1310                                   g_assert_not_reached();
1311                                   break;
1312                                 }
1313                                 break;
1314
1315                         default:
1316                                 g_assert_not_reached();
1317                         }
1318                 }
1319
1320                 col_fill_in_frame_data(&fdata, &cfile.cinfo, col, FALSE);
1321
1322                 return cfile.cinfo.col_buf[col];
1323         }
1324         else {
1325                 PacketListRecord *record;
1326                 guint vis_idx;
1327
1328                 gchar *widest_column_str = NULL;
1329                 guint widest_column_len = 0;
1330
1331                 if (!packet_list->columnized)
1332                         packet_list_dissect_and_cache_all(packet_list); /* XXX: need to handle case of "incomplete" ? */
1333
1334                 for(vis_idx = 0; vis_idx < PACKET_LIST_RECORD_COUNT(packet_list->visible_rows); ++vis_idx) {
1335                         record = PACKET_LIST_RECORD_GET(packet_list->visible_rows, vis_idx);
1336                         if (record->col_text_len[col] > widest_column_len) {
1337                                 widest_column_str = record->col_text[col];
1338                                 widest_column_len = record->col_text_len[col];
1339                         }
1340                 }
1341
1342                 return widest_column_str;
1343         }
1344 }