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