07a4d09cbdf7373a51f0f0858930df28d9b744dd
[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 <stephentfisher@yahoo.com>
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
184 static void
185 packet_list_tree_model_init(GtkTreeModelIface *iface)
186 {
187         iface->get_flags = packet_list_get_flags;
188         iface->get_n_columns = packet_list_get_n_columns;
189         iface->get_column_type = packet_list_get_column_type;
190         iface->get_iter = packet_list_get_iter;
191         iface->get_path = packet_list_get_path;
192         iface->get_value = packet_list_get_value;
193         iface->iter_next = packet_list_iter_next;
194         iface->iter_children = packet_list_iter_children;
195         iface->iter_has_child = packet_list_iter_has_child;
196         iface->iter_n_children = packet_list_iter_n_children;
197         iface->iter_nth_child = packet_list_iter_nth_child;
198         iface->iter_parent = packet_list_iter_parent;
199 }
200
201 /* This is called every time a new packet list object instance is created in
202  * packet_list_new.  Initialize the list structure's fields here. */
203 static void
204 packet_list_init(PacketList *packet_list)
205 {
206         guint i;
207         gint fmt;
208
209         for(i = 0; i < (guint)cfile.cinfo.num_cols; i++) {
210                 /* Get the format of the column, see column_info.h */
211                 fmt = get_column_format(i);
212                 switch(fmt){
213                         /* if we wish to store data rater than strings for some
214                          * colum types add case statements to the switch.
215                          */
216                         case COL_NUMBER:
217                         default:
218                                 packet_list->column_types[i] = G_TYPE_STRING;
219                                 break;
220                 }
221         }
222         
223         packet_list->n_columns = (guint)cfile.cinfo.num_cols;
224         packet_list->num_rows = 0;
225         packet_list->rows = NULL;
226
227         packet_list->sort_id = 0; /* defaults to first column for now */
228         packet_list->sort_order = GTK_SORT_ASCENDING;
229
230         packet_list->stamp = g_random_int(); /* To check whether an iter belongs
231                                               * to our model. */
232 }
233
234 /* This function is called just before a packet list is destroyed.  Free
235  * dynamically allocated memory here. */
236 static void
237 packet_list_finalize(GObject *object)
238 {
239         /* PacketList *packet_list = PACKET_LIST(object); */
240
241         /* XXX - Free all records and free all memory used by the list */
242
243         /* must chain up - finalize parent */
244         (* parent_class->finalize) (object);
245 }
246
247 static GtkTreeModelFlags
248 packet_list_get_flags(GtkTreeModel *tree_model)
249 {
250         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model),
251                              (GtkTreeModelFlags)0);
252
253         return (GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST);
254 }
255
256 static gint
257 packet_list_get_n_columns(GtkTreeModel *tree_model)
258 {
259         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), 0);
260
261         return PACKET_LIST(tree_model)->n_columns;
262 }
263
264 static GType
265 packet_list_get_column_type(GtkTreeModel *tree_model, gint index)
266 {
267         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), G_TYPE_INVALID);
268         g_return_val_if_fail(index < PACKET_LIST(tree_model)->n_columns &&
269                              index >= 0, G_TYPE_INVALID);
270
271         return PACKET_LIST(tree_model)->column_types[index];
272 }
273
274 static gboolean
275 packet_list_get_iter(GtkTreeModel *tree_model, GtkTreeIter *iter,
276                      GtkTreePath *path)
277 {
278         PacketList *packet_list;
279         PacketListRecord *record;
280         gint *indices, depth;
281         guint n;
282
283         g_assert(PACKETLIST_IS_LIST(tree_model));
284         g_assert(path != NULL);
285
286         packet_list = PACKET_LIST(tree_model);
287
288         indices = gtk_tree_path_get_indices(path);
289         depth = gtk_tree_path_get_depth(path);
290
291         /* we do not allow children since it's just a list */
292         g_assert(depth == 1);
293
294         n = indices[0]; /* the n-th top level row */
295
296         if(n >= packet_list->num_rows)
297                 return FALSE;
298
299         record = packet_list->rows[n];
300
301         g_assert(record != NULL);
302         g_assert(record->pos == n);
303
304         /* We simply store a pointer to our custom record in the iter */
305         iter->stamp = packet_list->stamp;
306         iter->user_data = record;
307         iter->user_data2 = NULL;
308         iter->user_data3 = NULL;
309
310         return TRUE;
311 }
312
313 static GtkTreePath *
314 packet_list_get_path(GtkTreeModel *tree_model, GtkTreeIter *iter)
315 {
316         GtkTreePath *path;
317         PacketListRecord *record;
318         PacketList *packet_list;
319
320         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), NULL);
321         g_return_val_if_fail(iter != NULL, NULL);
322         g_return_val_if_fail(iter->user_data != NULL, NULL);
323
324         packet_list = PACKET_LIST(tree_model);
325
326         record = (PacketListRecord*) iter->user_data;
327
328         path = gtk_tree_path_new();
329         gtk_tree_path_append_index(path, record->pos);
330
331         return path;
332 }
333
334 static void
335 packet_list_get_value(GtkTreeModel *tree_model, GtkTreeIter *iter, gint column,
336                       GValue *value)
337 {
338         PacketListRecord *record;
339         PacketList *packet_list;
340
341         g_return_if_fail(PACKETLIST_IS_LIST(tree_model));
342         g_return_if_fail(iter != NULL);
343         g_return_if_fail(column < PACKET_LIST(tree_model)->n_columns);
344
345         g_value_init(value, PACKET_LIST(tree_model)->column_types[column]);
346
347         packet_list = PACKET_LIST(tree_model);
348
349         record = (PacketListRecord*) iter->user_data;
350
351         if(record->pos >= packet_list->num_rows)
352                 g_return_if_reached();
353
354         g_value_set_string(value, record->col_text[column]);
355 }
356
357 /* Takes an iter structure and sets it to point to the next row. */
358 static gboolean
359 packet_list_iter_next(GtkTreeModel *tree_model, GtkTreeIter *iter)
360 {
361         PacketListRecord *record, *nextrecord;
362         PacketList *packet_list;
363
364         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
365
366         if(iter == NULL || iter->user_data == NULL)
367                 return FALSE;
368
369         packet_list = PACKET_LIST(tree_model);
370
371         record = (PacketListRecord*) iter->user_data;
372
373         /* Is this the last record in the list? */
374         if((record->pos + 1) >= packet_list->num_rows)
375                 return FALSE;
376
377         nextrecord = packet_list->rows[(record->pos + 1)];
378
379         g_assert(nextrecord != NULL);
380         g_assert(nextrecord->pos == (record->pos + 1));
381
382         iter->stamp = packet_list->stamp;
383         iter->user_data = nextrecord;
384
385         return TRUE;
386 }
387
388 static gboolean
389 packet_list_iter_children(GtkTreeModel *tree_model, GtkTreeIter *iter,
390                           GtkTreeIter *parent)
391 {
392         PacketList *packet_list;
393
394         g_return_val_if_fail(parent == NULL || parent->user_data != NULL,
395                              FALSE);
396
397         /* This is a list, nodes have no children. */
398         if(parent)
399                 return FALSE;
400
401         /* parent == NULL is a special case; we need to return the first top-
402          * level row */
403
404         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
405
406         packet_list = PACKET_LIST(tree_model);
407
408         /* No rows => no first row */
409         if(packet_list->num_rows == 0)
410                 return FALSE;
411
412         /* Set iter to first item in list */
413         iter->stamp = packet_list->stamp;
414         iter->user_data = packet_list->rows[0];
415
416         return TRUE;
417 }
418
419 static gboolean
420 packet_list_iter_has_child(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_)
421 {
422         return FALSE; /* Lists have no children */
423 }
424
425 static gint
426 packet_list_iter_n_children(GtkTreeModel *tree_model, GtkTreeIter *iter)
427 {
428         PacketList *packet_list;
429
430         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), -1);
431         g_return_val_if_fail(iter == NULL || iter->user_data != NULL, FALSE);
432
433         packet_list = PACKET_LIST(tree_model);
434
435         /* special case: if iter == NULL, return number of top-level rows */
436         if(!iter)
437                 return packet_list->num_rows;
438
439         return 0; /* Lists have zero children */
440 }
441
442 static gboolean
443 packet_list_iter_nth_child(GtkTreeModel *tree_model, GtkTreeIter *iter,
444                            GtkTreeIter *parent, gint n)
445 {
446         PacketListRecord *record;
447         PacketList *packet_list;
448
449         g_return_val_if_fail(PACKETLIST_IS_LIST(tree_model), FALSE);
450
451         packet_list = PACKET_LIST(tree_model);
452
453         /* A list only has top-level rows */
454         if(parent)
455                 return FALSE;
456
457         /* Special case: if parent == NULL, set iter to n-th
458          * top-level row. */
459         if((guint)n >= packet_list->num_rows)
460                 return FALSE;
461
462         record = packet_list->rows[n];
463
464         g_assert(record != NULL);
465         g_assert(record->pos == (guint)n);
466
467         iter->stamp = packet_list->stamp;
468         iter->user_data = record;
469
470         return TRUE;
471 }
472
473 static gboolean
474 packet_list_iter_parent(GtkTreeModel *tree_model _U_, GtkTreeIter *iter _U_,
475                         GtkTreeIter *child _U_)
476 {
477         return FALSE; /* No parents since no children in a list */
478 }
479
480 PacketList *
481 new_packet_list_new(void)
482 {
483         PacketList *newpacketlist;
484
485         newpacketlist = (PacketList*) g_object_new(PACKETLIST_TYPE_LIST, NULL);
486
487         g_assert(newpacketlist != NULL);
488
489         return newpacketlist;
490 }
491
492 void
493 packet_list_append_record(PacketList *packet_list, row_data_t *row_data)
494 {
495         GtkTreeIter iter;
496         GtkTreePath *path;
497         PacketListRecord *newrecord;
498         guint pos;
499         gint i;
500
501         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
502
503         pos = packet_list->num_rows;
504
505         packet_list->num_rows++;
506
507         packet_list->rows = g_renew(PacketListRecord*, packet_list->rows,
508                                     packet_list->num_rows);
509
510         newrecord = se_alloc0(sizeof(PacketListRecord));
511         newrecord->col_text = se_alloc0(sizeof(row_data->col_text)* cfile.cinfo.num_cols);
512
513
514         /* XXX newrecord->col_text still uses the fmt index */
515         for(i = 0; i < cfile.cinfo.num_cols; i++)
516                 newrecord->col_text[i] = row_data->col_text[i];
517
518         newrecord->fdata = row_data->fdata;
519
520         packet_list->rows[pos] = newrecord;
521         newrecord->pos = pos;
522
523         /* Inform the tree view and other interested objects (such as tree row
524          * references) that we have inserted a new row and where it was
525          * inserted. */
526         path = gtk_tree_path_new();
527         gtk_tree_path_append_index(path, newrecord->pos);
528
529         packet_list_get_iter(GTK_TREE_MODEL(packet_list), &iter, path);
530
531         gtk_tree_model_row_inserted(GTK_TREE_MODEL(packet_list), path, &iter);
532
533         gtk_tree_path_free(path);
534         
535         /* Don't resort the list for every row, the list will be in packet order any way.
536          * packet_list_resort(packet_list);
537          */
538 }
539
540 static gboolean
541 packet_list_sortable_get_sort_column_id(GtkTreeSortable *sortable,
542                                         gint *sort_col_id,
543                                         GtkSortType *order)
544 {
545         PacketList *packet_list;
546
547         g_return_val_if_fail(sortable != NULL, FALSE);
548         g_return_val_if_fail(PACKETLIST_IS_LIST(sortable), FALSE);
549
550         packet_list = PACKET_LIST(sortable);
551
552         if(sort_col_id)
553                 *sort_col_id = packet_list->sort_id;
554
555         if(order)
556                 *order = packet_list->sort_order;
557
558         return TRUE;
559 }
560
561 static void
562 packet_list_sortable_set_sort_column_id(GtkTreeSortable *sortable,
563                                         gint sort_col_id,
564                                         GtkSortType order)
565 {
566         PacketList *packet_list;
567
568         g_return_if_fail(sortable != NULL);
569         g_return_if_fail(PACKETLIST_IS_LIST(sortable));
570
571         packet_list = PACKET_LIST(sortable);
572
573         if(packet_list->sort_id == sort_col_id &&
574            packet_list->sort_order == order)
575                 return;
576
577         packet_list->sort_id = sort_col_id;
578         packet_list->sort_order = order;
579
580         packet_list_resort(packet_list);
581
582         /* emit "sort-column-changed" signal to tell any tree views
583          * that the sort column has changed (so the little arrow
584          * in the column header of the sort column is drawn
585          * in the right column) */
586
587         gtk_tree_sortable_sort_column_changed(sortable);
588 }
589
590 static void
591 packet_list_sortable_set_sort_func(GtkTreeSortable *sortable _U_,
592                                    gint sort_col_id _U_,
593                                    GtkTreeIterCompareFunc sort_func _U_,
594                                    gpointer user_data _U_,
595                                    GtkDestroyNotify destroy_func _U_)
596 {
597         g_warning("%s is not supported by the PacketList model.\n",
598                   __FUNCTION__);
599 }
600
601 static void
602 packet_list_sortable_set_default_sort_func(GtkTreeSortable *sortable _U_,
603                                            GtkTreeIterCompareFunc sort_func _U_,
604                                            gpointer user_data _U_,
605                                            GtkDestroyNotify destroy_func _U_)
606 {
607         g_warning("%s is not supported by the PacketList model.\n",
608                   __FUNCTION__);
609 }
610
611 static gboolean
612 packet_list_sortable_has_default_sort_func(GtkTreeSortable *sortable _U_)
613 {
614         return FALSE; /* Since packet_list_sortable_set_sort_func and
615                          set_default_sort_func are not implemented. */
616 }
617
618 static gint
619 packet_list_compare_records(gint sort_id, PacketListRecord *a,
620                             PacketListRecord *b)
621 {
622         /* The following is necessary because sort_id is a column number as
623          * seen by the user, whereas the col_text array from a and b is a
624          * column format number. This matches them to each other to get
625          * the right column text. */
626         gchar *a_col_text = a->col_text[cfile.cinfo.col_fmt[sort_id]];
627         gchar *b_col_text = b->col_text[cfile.cinfo.col_fmt[sort_id]];
628
629         if((a_col_text) && (b_col_text))
630                 return strcmp(a_col_text, b_col_text);
631
632         if(a_col_text == b_col_text)
633                 return 0; /* both are NULL */
634         else
635                 return (a_col_text == NULL) ? -1 : 1;
636
637         g_return_val_if_reached(0);
638 }
639                 
640 static gint
641 packet_list_qsort_compare_func(PacketListRecord **a, PacketListRecord **b,
642                                PacketList *packet_list)
643 {
644         gint ret;
645
646         g_assert((a) && (b) && (packet_list));
647
648         ret = packet_list_compare_records(packet_list->sort_id, *a, *b);
649
650         /* Swap -1 and 1 if sort order is reverse */
651         if(ret != 0 && packet_list->sort_order == GTK_SORT_DESCENDING)
652                 ret = (ret < 0) ? 1 : -1;
653
654         return ret;
655 }
656
657 static void
658 packet_list_resort(PacketList *packet_list)
659 {
660         GtkTreePath *path;
661         gint *neworder;
662         guint i;
663
664         g_return_if_fail(packet_list != NULL);
665         g_return_if_fail(PACKETLIST_IS_LIST(packet_list));
666
667         if(packet_list->num_rows == 0)
668                 return;
669
670         /* resort */
671         g_qsort_with_data(packet_list->rows, packet_list->num_rows,
672                           sizeof(PacketListRecord*),
673                           (GCompareDataFunc) packet_list_qsort_compare_func,
674                           packet_list);
675
676         /* let other objects know about the new order */
677         neworder = g_new0(gint, packet_list->num_rows);
678
679         for(i = 0; i < packet_list->num_rows; ++i) {
680                 neworder[i] = (packet_list->rows[i])->pos;
681                 (packet_list->rows[i])->pos = i;
682         }
683
684         path = gtk_tree_path_new();
685
686         gtk_tree_model_rows_reordered(GTK_TREE_MODEL(packet_list), path, NULL,
687                                       neworder);
688
689         gtk_tree_path_free(path);
690         g_free(neworder);
691 }
692
693 #endif /* NEW_PACKET_LIST */