Be a little more explicit in our description of tvb_get_ptr.
[metze/wireshark/wip.git] / gtk / packet_list_store.c
1 /* packet_list_store.c
2  * Routines to implement a custom GTK+ list model for Wireshark's packet list
3  * Copyright 2008-2009, Stephen Fisher (see AUTHORS file)
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
24  * USA.
25  */
26
27 /* This code is based on the GTK+ Tree View tutorial at http://scentric.net */
28
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #ifdef NEW_PACKET_LIST
35
36 #include <string.h>
37
38 #include <gtk/gtk.h>
39 #include <glib.h>
40
41 #include "epan/column_info.h"
42 #include "epan/column.h"
43
44 #include "packet_list_store.h"
45 #include "globals.h"
46
47 static void packet_list_init(PacketList *pkg_tree);
48 static void packet_list_class_init(PacketListClass *klass);
49 static void packet_list_tree_model_init(GtkTreeModelIface *iface);
50 static void packet_list_finalize(GObject *object);
51 static GtkTreeModelFlags packet_list_get_flags(GtkTreeModel *tree_model);
52 static gint packet_list_get_n_columns(GtkTreeModel *tree_model);
53 static GType packet_list_get_column_type(GtkTreeModel *tree_model, gint index);
54 static gboolean packet_list_get_iter(GtkTreeModel *tree_model,
55                                      GtkTreeIter *iter, GtkTreePath *path);
56 static GtkTreePath *packet_list_get_path(GtkTreeModel *tree_model,
57                                          GtkTreeIter *iter);
58 static void packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter,
59                                   gint column, GValue *value);
60 static gboolean packet_list_iter_next(GtkTreeModel *tree_model,
61                                       GtkTreeIter *iter);
62 static gboolean packet_list_iter_children(GtkTreeModel *tree_model,
63                                           GtkTreeIter *iter,
64                                           GtkTreeIter *parent);
65 static gboolean packet_list_iter_has_child(GtkTreeModel *tree_model _U_,
66                                            GtkTreeIter *iter _U_);
67 static gint packet_list_iter_n_children(GtkTreeModel *tree_model,
68                                         GtkTreeIter *iter);
69 static gboolean packet_list_iter_nth_child(GtkTreeModel *tree_model,
70                                            GtkTreeIter *iter,
71                                            GtkTreeIter *parent,
72                                            gint n);
73 static gboolean packet_list_iter_parent(GtkTreeModel *tree_model _U_,
74                                         GtkTreeIter *iter _U_,
75                                         GtkTreeIter *child _U_);
76
77 static gboolean packet_list_sortable_get_sort_column_id(GtkTreeSortable
78                                                         *sortable,
79                                                         gint *sort_col_id,
80                                                         GtkSortType *order);
81 static void packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
82                                                     gint sort_col_id,
83                                                     GtkSortType order);
84 static void packet_list_sortable_set_sort_func(GtkTreeSortable *sortable,
85                                                gint sort_col_id,
86                                                GtkTreeIterCompareFunc sort_func,
87                                                gpointer user_data,
88                                                GtkDestroyNotify destroy_func);
89 static void packet_list_sortable_set_default_sort_func(GtkTreeSortable
90                                                        *sortable,
91                                                        GtkTreeIterCompareFunc
92                                                        sort_func,
93                                                        gpointer user_data,
94                                                        GtkDestroyNotify
95                                                        destroy_func);
96 static gboolean packet_list_sortable_has_default_sort_func(GtkTreeSortable
97                                                            *sortable);
98 static void packet_list_sortable_init(GtkTreeSortableIface *iface);
99 static gint packet_list_compare_records(gint sort_id _U_, PacketListRecord *a,
100                                         PacketListRecord *b);
101 static gint packet_list_qsort_compare_func(PacketListRecord **a,
102                                            PacketListRecord **b,
103                                            PacketList *packet_list);
104 static void packet_list_resort(PacketList *packet_list);
105
106 static GObjectClass *parent_class = NULL;
107
108
109 GType
110 packet_list_get_type(void)
111 {
112         static GType packet_list_type = 0;
113
114         if(packet_list_type == 0) {
115                 static const GTypeInfo packet_list_info = {
116                         sizeof(PacketListClass),
117                         NULL, /* base_init */
118                         NULL, /* base_finalize */
119                         (GClassInitFunc) packet_list_class_init,
120                         NULL, /* class finalize */
121                         NULL, /* class_data */
122                         sizeof(PacketList),
123                         0, /* n_preallocs */
124                         (GInstanceInitFunc) packet_list_init,
125                         NULL /* value_table */
126                 };
127
128                 static const GInterfaceInfo tree_model_info = {
129                         (GInterfaceInitFunc) packet_list_tree_model_init,
130                         NULL,
131                         NULL
132                 };
133
134                 static const GInterfaceInfo tree_sortable_info = {
135                                 (GInterfaceInitFunc) packet_list_sortable_init,
136                                 NULL,
137                                 NULL
138                 };
139
140                 /* Register the new derived type with the GObject type system */
141                 packet_list_type = g_type_register_static(G_TYPE_OBJECT,
142                                                           "PacketList",
143                                                           &packet_list_info,
144                                                           (GTypeFlags)0);
145
146                 g_type_add_interface_static(packet_list_type,
147                                             GTK_TYPE_TREE_MODEL,
148                                             &tree_model_info);
149                                                           
150
151                 /* Register our GtkTreeModel interface with the type system */
152                 g_type_add_interface_static(packet_list_type,
153                                             GTK_TYPE_TREE_SORTABLE,
154                                             &tree_sortable_info);
155         }
156
157         return packet_list_type;
158 }
159
160 static void
161 packet_list_sortable_init(GtkTreeSortableIface *iface)
162 {
163         iface->get_sort_column_id = packet_list_sortable_get_sort_column_id;
164         iface->set_sort_column_id = packet_list_sortable_set_sort_column_id;
165         /* The following three functions are not implemented */
166         iface->set_sort_func = packet_list_sortable_set_sort_func;
167         iface->set_default_sort_func =
168                 packet_list_sortable_set_default_sort_func;
169         iface->has_default_sort_func =
170                 packet_list_sortable_has_default_sort_func;
171 }
172
173 static void
174 packet_list_class_init(PacketListClass *klass)
175 {
176         GObjectClass *object_class;
177
178         parent_class = (GObjectClass*) g_type_class_peek_parent(klass);
179         object_class = (GObjectClass*) klass;
180
181         object_class->finalize = packet_list_finalize;
182
183         /* XXX this seems to affect TreeView Application wide
184          * Move to main.c ??? as it's not a bad thing(tm)
185          */
186         gtk_rc_parse_string (
187                 "style \"PacketList-style\"\n"
188                 "{\n"
189                 "  GtkTreeView::horizontal-separator = 0\n"
190                 "} widget_class \"*TreeView*\""
191                 " style \"PacketList-style\"");
192
193 }
194
195 static void
196 packet_list_tree_model_init(GtkTreeModelIface *iface)
197 {
198         iface->get_flags = packet_list_get_flags;
199         iface->get_n_columns = packet_list_get_n_columns;
200         iface->get_column_type = packet_list_get_column_type;
201         iface->get_iter = packet_list_get_iter;
202         iface->get_path = packet_list_get_path;
203         iface->get_value = packet_list_get_value;
204         iface->iter_next = packet_list_iter_next;
205         iface->iter_children = packet_list_iter_children;
206         iface->iter_has_child = packet_list_iter_has_child;
207         iface->iter_n_children = packet_list_iter_n_children;
208         iface->iter_nth_child = packet_list_iter_nth_child;
209         iface->iter_parent = packet_list_iter_parent;
210 }
211
212 /* This is called every time a new packet list object instance is created in
213  * packet_list_new.  Initialize the list structure's fields here. */
214 static void
215 packet_list_init(PacketList *packet_list)
216 {
217         guint i;
218         gint fmt;
219
220         for(i = 0; i < (guint)cfile.cinfo.num_cols; i++) {
221                 /* Get the format of the column, see column_info.h */
222                 fmt = get_column_format(i);
223                 switch(fmt){
224                         /* if we wish to store data rater than strings for some
225                          * colum types add case statements to the switch.
226                          */
227                         case COL_NUMBER:
228                         default:
229                                 packet_list->column_types[i] = G_TYPE_STRING;
230                                 break;
231                 }
232         }
233         
234         packet_list->n_columns = (guint)cfile.cinfo.num_cols;
235         packet_list->num_rows = 0;
236         packet_list->rows = NULL;
237
238         packet_list->sort_id = 0; /* defaults to first column for now */
239         packet_list->sort_order = GTK_SORT_ASCENDING;
240
241         packet_list->stamp = g_random_int(); /* To check whether an iter belongs
242                                               * to our model. */
243 }
244
245 /* This function is called just before a packet list is destroyed.  Free
246  * dynamically allocated memory here. */
247 static void
248 packet_list_finalize(GObject *object)
249 {
250         /* PacketList *packet_list = PACKET_LIST(object); */
251
252         /* XXX - Free all records and free all memory used by the list */
253
254         /* must chain up - finalize parent */
255         (* parent_class->finalize) (object);
256 }
257
258 static GtkTreeModelFlags
259 packet_list_get_flags(GtkTreeModel *tree_model)
260 {
261         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
262                              (GtkTreeModelFlags)0);
263
264         return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
265 }
266
267 static gint
268 packet_list_get_n_columns(GtkTreeModel *tree_model)
269 {
270         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
271
272         return PACKET_LIST(tree_model)->n_columns;
273 }
274
275 static GType
276 packet_list_get_column_type(GtkTreeModel *tree_model, gint index)
277 {
278         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
279         g_return_val_if_fail(index < PACKET_LIST(tree_model)->n_columns &&
280                              index >= 0, G_TYPE_INVALID);
281
282         return PACKET_LIST(tree_model)->column_types[index];
283 }
284
285 static gboolean
286 packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
287                      GtkTreePath *path)
288 {
289         PacketList *packet_list;
290         PacketListRecord *record;
291         gint *indices, depth;
292         guint n;
293
294         g_assert(PACKETLIST_IS_LIST(tree_model));
295         g_assert(path != NULL);
296
297         packet_list = PACKET_LIST(tree_model);
298
299         indices = gtk_tree_path_get_indices(path);
300         depth = gtk_tree_path_get_depth(path);
301
302         /* we do not allow children since it's just a list */
303         g_assert(depth == 1);
304
305         n = indices[0]; /* the n-th top level row */
306
307         if(n >= packet_list->num_rows)
308                 return FALSE;
309
310         record = packet_list->rows[n];
311
312         g_assert(record != NULL);
313         g_assert(record->pos == n);
314
315         /* We simply store a pointer to our custom record in the iter */
316         iter->stamp = packet_list->stamp;
317         iter->user_data = record;
318         iter->user_data2 = NULL;
319         iter->user_data3 = NULL;
320
321         return TRUE;
322 }
323
324 static GtkTreePath *
325 packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
326 {
327         GtkTreePath *path;
328         PacketListRecord *record;
329         PacketList *packet_list;
330
331         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
332         g_return_val_if_fail(iter != NULL, NULL);
333         g_return_val_if_fail(iter->user_data != NULL, NULL);
334
335         packet_list = PACKET_LIST(tree_model);
336
337         record = (PacketListRecord*) iter->user_data;
338
339         path = gtk_tree_path_new();
340         gtk_tree_path_append_index(path, record->pos);
341
342         return path;
343 }
344
345 static void
346 packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
347                       GValue *value)
348 {
349         PacketListRecord *record;
350         PacketList *packet_list;
351         GType type;
352
353         g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
354         g_return_if_fail(iter != NULL);
355         g_return_if_fail(column < PACKET_LIST(tree_model)->n_columns);
356
357         type = PACKET_LIST(tree_model)->column_types[column];
358         g_value_init(value, type);
359
360         packet_list = PACKET_LIST(tree_model);
361
362         record = (PacketListRecord*) iter->user_data;
363
364         if(record->pos >= packet_list->num_rows)
365                 g_return_if_reached();
366
367         /* XXX Probably the switch should be on column or 
368          * should we allways return the pointer and read the data as required??
369          * If we use FOREGROUND_COLOR_COL etc we'll need a couple of "internal" columns
370          */ 
371         switch(type){
372                 case G_TYPE_POINTER:
373                         g_value_set_pointer(value, record);
374                         break;
375                 case G_TYPE_STRING:
376                         g_value_set_string(value, record->col_text[column]);
377                         break;
378                 default:
379                         g_warning ("%s: Unsupported type (%s) retrieved.", G_STRLOC, g_type_name (value->g_type));
380                         break;
381         }
382 }
383
384 /* Takes an iter structure and sets it to point to the next row. */
385 static gboolean
386 packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
387 {
388         PacketListRecord *record, *nextrecord;
389         PacketList *packet_list;
390
391         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
392
393         if(iter == NULL || iter->user_data == NULL)
394                 return FALSE;
395
396         packet_list = PACKET_LIST(tree_model);
397
398         record = (PacketListRecord*) iter->user_data;
399
400         /* Is this the last record in the list? */
401         if((record->pos + 1) >= packet_list->num_rows)
402                 return FALSE;
403
404         nextrecord = packet_list->rows[(record->pos + 1)];
405
406         g_assert(nextrecord != NULL);
407         g_assert(nextrecord->pos == (record->pos + 1));
408
409         iter->stamp = packet_list->stamp;
410         iter->user_data = nextrecord;
411
412         return TRUE;
413 }
414
415 static gboolean
416 packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
417                           GtkTreeIter *parent)
418 {
419         PacketList *packet_list;
420
421         g_return_val_if_fail(parent == NULL || parent->user_data != NULL,
422                              FALSE);
423
424         /* This is a list, nodes have no children. */
425         if(parent)
426                 return FALSE;
427
428         /* parent == NULL is a special case; we need to return the first top-
429          * level row */
430
431         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
432
433         packet_list = PACKET_LIST(tree_model);
434
435         /* No rows => no first row */
436         if(packet_list->num_rows == 0)
437                 return FALSE;
438
439         /* Set iter to first item in list */
440         iter->stamp = packet_list->stamp;
441         iter->user_data = packet_list->rows[0];
442
443         return TRUE;
444 }
445
446 static gboolean
447 packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
448 {
449         return FALSE; /* Lists have no children */
450 }
451
452 static gint
453 packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
454 {
455         PacketList *packet_list;
456
457         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), -1);
458         g_return_val_if_fail(iter == NULL || iter->user_data != NULL, FALSE);
459
460         packet_list = PACKET_LIST(tree_model);
461
462         /* special case: if iter == NULL, return number of top-level rows */
463         if(!iter)
464                 return packet_list->num_rows;
465
466         return 0; /* Lists have zero children */
467 }
468
469 static gboolean
470 packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
471                            GtkTreeIter *parent, gint n)
472 {
473         PacketListRecord *record;
474         PacketList *packet_list;
475
476         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
477
478         packet_list = PACKET_LIST(tree_model);
479
480         /* A list only has top-level rows */
481         if(parent)
482                 return FALSE;
483
484         /* Special case: if parent == NULL, set iter to n-th
485          * top-level row. */
486         if((guint)n >= packet_list->num_rows)
487                 return FALSE;
488
489         record = packet_list->rows[n];
490
491         g_assert(record != NULL);
492         g_assert(record->pos == (guint)n);
493
494         iter->stamp = packet_list->stamp;
495         iter->user_data = record;
496
497         return TRUE;
498 }
499
500 static gboolean
501 packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
502                         GtkTreeIter *child _U_)
503 {
504         return FALSE; /* No parents since no children in a list */
505 }
506
507 PacketList *
508 new_packet_list_new(void)
509 {
510         PacketList *newpacketlist;
511
512         newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
513
514         g_assert(newpacketlist != NULL);
515
516         return newpacketlist;
517 }
518
519 #if 0
520 static void
521 packet_list_row_deleted(PacketList *packet_list, guint pos)
522 {
523         GtkTreePath *path;
524
525         /* Inform the tree view and other interested objects (such as tree row
526          * references) that we have deleted a row */
527         path = gtk_tree_path_new();
528         gtk_tree_path_append_index(path, pos);
529
530         gtk_tree_model_row_deleted(GTK_TREE_MODEL(packet_list), path);
531
532         gtk_tree_path_free(path);
533 }
534 #endif
535
536 void
537 new_packet_list_store_clear(PacketList *packet_list)
538 {
539         g_return_if_fail(packet_list != NULL);
540         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
541
542         if(packet_list->num_rows == 0)
543                 return;
544
545         /* Don't issue a row_deleted signal. We rely on our caller to have disconnected
546          * the model from the view.
547         for( ; packet_list->num_rows > 0; --packet_list->num_rows)
548                 packet_list_row_deleted(packet_list, packet_list->num_rows-1);
549         */
550
551         /* XXX - hold on to these rows and reuse them instead */
552         g_free(packet_list->rows);
553         packet_list->rows = NULL;
554         packet_list->num_rows = 0;
555 }
556
557 #if 0
558 static void
559 packet_list_row_inserted(PacketList *packet_list, guint pos)
560 {
561         GtkTreeIter iter;
562         GtkTreePath *path;
563
564         /* Inform the tree view and other interested objects (such as tree row
565          * references) that we have inserted a new row and where it was
566          * inserted. */
567         path = gtk_tree_path_new();
568         gtk_tree_path_append_index(path, pos);
569
570         packet_list_get_iter(GTK_TREE_MODEL(packet_list), &iter, path);
571
572         gtk_tree_model_row_inserted(GTK_TREE_MODEL(packet_list), path, &iter);
573
574         gtk_tree_path_free(path);
575 }
576 #endif
577
578 void
579 packet_list_append_record(PacketList *packet_list, row_data_t *row_data)
580 {
581         PacketListRecord *newrecord;
582         guint pos;
583
584         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
585
586         pos = packet_list->num_rows;
587
588         packet_list->num_rows++;
589
590         packet_list->rows = g_renew(PacketListRecord*, packet_list->rows,
591                                     packet_list->num_rows);
592
593         newrecord = se_alloc(sizeof(PacketListRecord));
594         newrecord->dissected = FALSE;
595         newrecord->col_text = row_data->col_text;
596         newrecord->fdata = row_data->fdata;
597         newrecord->pos = pos;
598
599         packet_list->rows[pos] = newrecord;
600
601         /* Don't issue a row_inserted signal. We rely on our caller to have disconnected
602          * the model from the view.
603          * packet_list_row_inserted(packet_list, newrecord->pos);
604          */
605
606         /* Don't resort the list for every row, the list will be in packet order any way.
607          * packet_list_resort(packet_list);
608          */
609 }
610
611 void
612 packet_list_change_record(PacketList *packet_list, guint row, gint col, column_info *cinfo)
613 {
614         PacketListRecord *record;
615
616         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
617
618         g_assert(row < packet_list->num_rows);
619         record = packet_list->rows[row];
620         g_assert(record->pos == row);
621         g_assert(!record->col_text || (record->col_text[col] == NULL));
622         if (!record->col_text)
623                 record->col_text = se_alloc0(sizeof(record->col_text)*packet_list->n_columns);
624
625         record->col_text[col] = se_strdup(cinfo->col_data[col]);
626 }
627
628 static gboolean
629 packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
630                                         gint *sort_col_id,
631                                         GtkSortType *order)
632 {
633         PacketList *packet_list;
634
635         g_return_val_if_fail(sortable != NULL, FALSE);
636         g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
637
638         packet_list = PACKET_LIST(sortable);
639
640         if(sort_col_id)
641                 *sort_col_id = packet_list->sort_id;
642
643         if(order)
644                 *order = packet_list->sort_order;
645
646         return TRUE;
647 }
648
649 static void
650 packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
651                                         gint sort_col_id,
652                                         GtkSortType order)
653 {
654         PacketList *packet_list;
655
656         g_return_if_fail(sortable != NULL);
657         g_return_if_fail(PACKETLIST_IS_LIST(sortable));
658
659         packet_list = PACKET_LIST(sortable);
660
661         if(packet_list->sort_id == sort_col_id &&
662            packet_list->sort_order == order)
663                 return;
664
665         packet_list->sort_id = sort_col_id;
666         packet_list->sort_order = order;
667
668         packet_list_resort(packet_list);
669
670         /* emit "sort-column-changed" signal to tell any tree views
671          * that the sort column has changed (so the little arrow
672          * in the column header of the sort column is drawn
673          * in the right column) */
674
675         gtk_tree_sortable_sort_column_changed(sortable);
676 }
677
678 static void
679 packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
680                                    gint sort_col_id _U_,
681                                    GtkTreeIterCompareFunc sort_func _U_,
682                                    gpointer user_data _U_,
683                                    GtkDestroyNotify destroy_func _U_)
684 {
685         g_warning("%s is not supported by the PacketList model.\n",
686                   __FUNCTION__);
687 }
688
689 static void
690 packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
691                                            GtkTreeIterCompareFunc sort_func _U_,
692                                            gpointer user_data _U_,
693                                            GtkDestroyNotify destroy_func _U_)
694 {
695         g_warning("%s is not supported by the PacketList model.\n",
696                   __FUNCTION__);
697 }
698
699 static gboolean
700 packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
701 {
702         return FALSE; /* Since packet_list_sortable_set_sort_func and
703                          set_default_sort_func are not implemented. */
704 }
705
706 static gint
707 packet_list_compare_records(gint sort_id, PacketListRecord *a,
708                             PacketListRecord *b)
709 {
710
711         /* XXX If we want to store other things than text, we need other sort functions */ 
712
713         if (col_based_on_frame_data(&cfile.cinfo, sort_id))
714                 return frame_data_compare(a->fdata, b->fdata, cfile.cinfo.col_fmt[sort_id]);
715
716         if((a->col_text[sort_id]) && (b->col_text[sort_id]))
717                 return strcmp(a->col_text[sort_id], b->col_text[sort_id]);
718
719         if(a->col_text[sort_id] == b->col_text[sort_id])
720                 return 0; /* both are NULL */
721         else
722                 return (a->col_text[sort_id] == NULL) ? -1 : 1;
723
724         g_return_val_if_reached(0);
725 }               
726 static gint
727 packet_list_qsort_compare_func(PacketListRecord **a, PacketListRecord **b,
728                                PacketList *packet_list)
729 {
730         gint ret;
731
732         g_assert((a) && (b) && (packet_list));
733
734         ret = packet_list_compare_records(packet_list->sort_id, *a, *b);
735
736         /* Swap -1 and 1 if sort order is reverse */
737         if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
738                 ret = (ret < 0) ? 1 : -1;
739
740         return ret;
741 }
742
743 static void
744 packet_list_resort(PacketList *packet_list)
745 {
746         GtkTreePath *path;
747         gint *neworder;
748         guint i;
749
750         g_return_if_fail(packet_list != NULL);
751         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
752
753         if(packet_list->num_rows == 0)
754                 return;
755
756         /* resort */
757         g_qsort_with_data(packet_list->rows, packet_list->num_rows,
758                           sizeof(PacketListRecord*),
759                           (GCompareDataFunc) packet_list_qsort_compare_func,
760                           packet_list);
761
762         /* let other objects know about the new order */
763         neworder = g_new0(gint, packet_list->num_rows);
764
765         for(i = 0; i < packet_list->num_rows; ++i) {
766                 neworder[i] = (packet_list->rows[i])->pos;
767                 (packet_list->rows[i])->pos = i;
768         }
769
770         path = gtk_tree_path_new();
771
772         gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
773                                       neworder);
774
775         gtk_tree_path_free(path);
776         g_free(neworder);
777 }
778
779 #endif /* NEW_PACKET_LIST */