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