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