Be a little more explicit in our description of tvb_get_ptr.
[metze/wireshark/wip.git] / gtk / new_packet_list.c
1 /* new_packet_list.c
2  * Routines to implement a new GTK2 packet list using our custom model
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 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #ifdef NEW_PACKET_LIST
32
33 #ifdef HAVE_STRING_H
34 #include "string.h"
35 #endif
36
37 #include <gtk/gtk.h>
38 #include <glib.h>
39
40 #include "gui_utils.h"
41 #include "packet_list_store.h"
42 #include "epan/column_info.h"
43 #include "epan/prefs.h"
44 #include <epan/packet.h>
45 #include "../ui_util.h"
46 #include "../simple_dialog.h"
47 #include "epan/emem.h"
48 #include "globals.h"
49 #include "gtk/gtkglobals.h"
50 #include "gtk/font_utils.h"
51 #include "gtk/packet_history.h"
52 #include "epan/column.h"
53 #include "gtk/recent.h"
54 #include "gtk/keys.h"
55 #include "gtk/menus.h"
56 #include "color.h"
57 #include "color_filters.h"
58 #include "gtk/color_utils.h"
59 #include "gtk/capture_file_dlg.h"
60 #include "gtk/main_statusbar.h"
61
62 static PacketList *packetlist;
63
64 static gboolean enable_color;
65
66 static GtkWidget *create_view_and_model(void);
67 static guint row_from_iter(GtkTreeIter *iter);
68 static gboolean iter_from_row(GtkTreeIter *iter, guint row);
69 static void scroll_to_and_select_iter(GtkTreeIter *iter);
70 static void new_packet_list_select_cb(GtkTreeView *tree_view, gpointer data _U_);
71 static void show_cell_data_func(GtkTreeViewColumn *col,
72                                 GtkCellRenderer *renderer,
73                                 GtkTreeModel *model,
74                                 GtkTreeIter *iter,
75                                 gpointer data);
76
77 GtkWidget *
78 new_packet_list_create(void)
79 {
80         GtkWidget *view, *scrollwin;
81
82         scrollwin = scrolled_window_new(NULL, NULL);
83
84         view = create_view_and_model();
85
86         gtk_container_add(GTK_CONTAINER(scrollwin), view);
87
88         /* XXX - Implement me
89         g_signal_connect(view, "row-activated",
90                          G_CALLBACK(popup_menu_handler),
91                          g_object_get_data(G_OBJECT(popup_menu_object),
92                                            PM_PACKET_LIST_KEY));
93         g_signal_connect(view, "button_press_event",
94                          G_CALLBACK(new_packet_list_button_pressed_cb), NULL);
95         */
96
97         g_object_set_data(G_OBJECT(popup_menu_object), E_MPACKET_LIST_KEY, view);
98
99         return scrollwin;
100 }
101
102 guint
103 new_packet_list_append(column_info *cinfo, frame_data *fdata, packet_info *pinfo _U_)
104 {
105         gint i;
106         row_data_t row_data;
107
108         if (cinfo) {
109                 /* Allocate the array holding column data, the size is the current number of columns */
110                 row_data.col_text = se_alloc(sizeof(row_data.col_text)*packetlist->n_columns);
111                 g_assert(packetlist->n_columns == cinfo->num_cols);
112                 for(i = 0; i < cinfo->num_cols; i++) {
113                         if (col_based_on_frame_data(cinfo, i) ||
114                                 /* We handle custom columns lazily */
115                                 cinfo->col_fmt[i] == COL_CUSTOM)
116                                 /* We already store the value in frame_data, so don't duplicate this. */
117                                 row_data.col_text[i] = NULL;
118                         else
119                                 row_data.col_text[i] = se_strdup(cinfo->col_data[i]);
120                 }
121         }
122         else
123                 row_data.col_text = NULL;
124
125         row_data.fdata = fdata;
126
127         packet_list_append_record(packetlist, &row_data);
128
129         return packetlist->num_rows; /* XXX - Check that this is the right # */
130 }
131
132 static GtkWidget *
133 create_view_and_model(void)
134 {
135         GtkTreeViewColumn *col;
136         GtkCellRenderer *renderer;
137         PangoLayout *layout;
138         gint i, col_width;
139
140         packetlist = new_packet_list_new();
141
142         packetlist->view = tree_view_new(GTK_TREE_MODEL(packetlist));
143
144 #if GTK_CHECK_VERSION(2,6,0)
145         gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(packetlist->view),
146                                             TRUE);
147 #endif
148         g_signal_connect(packetlist->view, "cursor-changed",
149                          G_CALLBACK(new_packet_list_select_cb), NULL);
150         g_signal_connect(packetlist->view, "button_press_event", G_CALLBACK(popup_menu_handler),
151                                    g_object_get_data(G_OBJECT(popup_menu_object), PM_PACKET_LIST_KEY));
152         g_object_set_data(G_OBJECT(popup_menu_object), E_MPACKET_LIST_KEY, packetlist);
153
154         /*      g_object_unref(packetlist); */ /* Destroy automatically with view for now */ /* XXX - Messes up freezing & thawing */
155
156         renderer = gtk_cell_renderer_text_new();
157         g_object_set(renderer,
158                      "ypad", 0,
159                      NULL);                
160         gtk_widget_modify_font(packetlist->view, user_font_get_regular());
161
162         for(i = 0; i < cfile.cinfo.num_cols; i++) {
163                 col = gtk_tree_view_column_new();
164                 gtk_tree_view_column_pack_start(col, renderer, TRUE);
165                 gtk_tree_view_column_set_cell_data_func(col, renderer,
166                                                         show_cell_data_func,
167                                                         GINT_TO_POINTER(i),
168                                                         NULL);
169                 gtk_tree_view_column_set_title(col, cfile.cinfo.col_title[i]);
170                 gtk_tree_view_column_set_sort_column_id(col, i);
171                 gtk_tree_view_column_set_resizable(col, TRUE);
172                 gtk_tree_view_column_set_sizing(col,GTK_TREE_VIEW_COLUMN_FIXED);
173                 gtk_tree_view_column_set_reorderable(col, TRUE); /* XXX - Should this be saved in the prefs? */
174
175                 col_width = recent_get_column_width(i);
176                 if(col_width == -1) {
177                         layout = gtk_widget_create_pango_layout(packetlist->view, get_column_width_string(get_column_format(i), i));
178                         pango_layout_get_pixel_size(layout, &col_width, NULL);
179                         gtk_tree_view_column_set_min_width(col, col_width);
180                         g_object_unref(G_OBJECT(layout));
181                 }
182
183                 gtk_tree_view_append_column(GTK_TREE_VIEW(packetlist->view), col);
184         }
185
186         return packetlist->view;
187 }
188
189 void
190 new_packet_list_clear(void)
191 {
192         packet_history_clear();
193
194         new_packet_list_store_clear(packetlist);
195         gtk_widget_queue_draw(packetlist->view);
196 }
197
198 void
199 new_packet_list_freeze(void)
200 {
201         /* So we don't lose the model by the time we want to thaw it */
202         g_object_ref(packetlist);
203
204         /* Detach view from model */
205         gtk_tree_view_set_model(GTK_TREE_VIEW(packetlist->view), NULL);
206 }
207
208 void
209 new_packet_list_thaw(void)
210 {
211         /* Remove extra reference added by new_packet_list_freeze() */
212         g_object_unref(packetlist);
213
214         /* Re-attach view to the model */
215         gtk_tree_view_set_model(GTK_TREE_VIEW(packetlist->view),
216                                 GTK_TREE_MODEL(packetlist));
217 }
218
219 void
220 new_packet_list_resize_columns_cb(GtkWidget *widget _U_, gpointer data _U_)
221 {
222         g_warning("*** new_packet_list_resize_columns_cb() not yet implemented.");
223 }
224
225 void
226 new_packet_list_next(void)
227 {
228         GtkTreeSelection *selection;
229         GtkTreeIter iter;
230         guint row;
231
232         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
233         if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
234                 return;
235
236         row = row_from_iter(&iter);
237         if (!iter_from_row(&iter, row+1))
238                 return;
239
240         scroll_to_and_select_iter(&iter);
241 }
242
243 void
244 new_packet_list_prev(void)
245 {
246         GtkTreeSelection *selection;
247         GtkTreeIter iter;
248         guint row;
249
250         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
251         if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
252                 return;
253
254         row = row_from_iter(&iter);
255         if (!iter_from_row(&iter, row-1))
256                 return;
257
258         scroll_to_and_select_iter(&iter);
259 }
260
261 static void
262 scroll_to_and_select_iter(GtkTreeIter *iter)
263 {
264         GtkTreeModel *model = GTK_TREE_MODEL(packetlist);
265         GtkTreeSelection *selection;
266         GtkTreePath *path;
267
268         /* Select the row */
269         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
270         gtk_tree_selection_select_iter (selection, iter);
271         path = gtk_tree_model_get_path(model, iter);
272         gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(packetlist->view),
273                         path,
274                         NULL,
275                         TRUE,   /* use_align */
276                         0.5,    /* row_align determines where the row is placed, 0.5 means center */
277                         0);             /* The horizontal alignment of the column */
278         gtk_tree_view_set_cursor(GTK_TREE_VIEW(packetlist->view),
279                         path,
280                         NULL,
281                         FALSE); /* start_editing */
282
283         /* Needed to get the middle and bottom panes updated */
284         new_packet_list_select_cb(GTK_TREE_VIEW(packetlist->view), NULL);
285 }
286
287 void
288 new_packet_list_select_first_row(void)
289 {
290         GtkTreeModel *model = GTK_TREE_MODEL(packetlist);
291         GtkTreeIter iter;
292
293         if(!gtk_tree_model_get_iter_first(model, &iter))
294                 return;
295
296         scroll_to_and_select_iter(&iter);
297 }
298
299 void
300 new_packet_list_select_last_row(void)
301 {
302         GtkTreeModel *model = GTK_TREE_MODEL(packetlist);
303         GtkTreeIter iter;
304         gint children;
305
306         if((children = gtk_tree_model_iter_n_children(model, NULL)) == 0)
307                 return;
308
309         if(!iter_from_row(&iter, children-1))
310                 return;
311
312         scroll_to_and_select_iter(&iter);
313 }
314
315 gint
316 new_packet_list_find_row_from_data(gpointer data, gboolean select)
317 {
318         GtkTreeModel *model = GTK_TREE_MODEL(packetlist);
319         GtkTreeIter iter;
320         frame_data *fdata;
321         gint row;
322
323         /* Initializes iter with the first iterator in the tree (the one at the path "0") 
324          * and returns TRUE. Returns FALSE if the tree is empty
325          */
326         if(!gtk_tree_model_get_iter_first(model, &iter))
327                 return -1;
328
329         do {
330                 row = row_from_iter(&iter);
331                 fdata = new_packet_list_get_row_data(row);
332
333                 if(fdata == (frame_data*)data){
334                         if(select)
335                                 scroll_to_and_select_iter(&iter);
336
337                         return row;
338                 }
339         } while (gtk_tree_model_iter_next (model,&iter));
340
341     return -1;
342 }
343
344 void
345 new_packet_list_set_selected_row(gint row)
346 {
347         GtkTreeIter iter;
348         GtkTreeModel *model = GTK_TREE_MODEL(packetlist);
349         GtkTreeSelection *selection;
350         GtkTreePath *path;
351
352         if (!iter_from_row(&iter, row))
353                 return;
354
355         /* Select the row */
356         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
357         gtk_tree_selection_select_iter (selection, &iter);
358         path = gtk_tree_model_get_path(model, &iter);
359         gtk_tree_view_set_cursor(GTK_TREE_VIEW(packetlist->view),
360                         path,
361                         NULL,
362                         FALSE); /* start_editing */
363
364         /* Needed to get the middle and bottom panes updated */
365         new_packet_list_select_cb(GTK_TREE_VIEW(packetlist->view), NULL);
366 }
367
368 static void
369 new_packet_list_select_cb(GtkTreeView *tree_view, gpointer data _U_)
370 {
371         GtkTreeSelection *selection;
372         GtkTreeIter iter;
373         guint row;
374         frame_data *fdata;
375
376         selection = gtk_tree_view_get_selection(tree_view);
377         gtk_tree_selection_get_selected(selection, NULL, &iter);
378
379         /* Remove the hex display tab pages */
380         while(gtk_notebook_get_nth_page(GTK_NOTEBOOK(byte_nb_ptr), 0))
381                 gtk_notebook_remove_page(GTK_NOTEBOOK(byte_nb_ptr), 0);
382
383         row = row_from_iter(&iter);
384
385         cf_select_packet(&cfile, row);
386
387         /* Add newly selected frame to packet history (breadcrumbs) */
388         fdata = new_packet_list_get_row_data(row);
389         if (fdata != NULL)
390                 packet_history_add(fdata->num);
391 }
392
393 gboolean
394 new_packet_list_get_event_row_column(GtkWidget *w _U_, GdkEventButton *event_button,
395                                  gint *row, gint *column)
396 {
397     GtkTreePath *path;
398     GtkTreeViewColumn *view_column;
399
400     if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(packetlist->view),
401                                       (gint) event_button->x,
402                                       (gint) event_button->y,
403                                       &path, &view_column, NULL, NULL)) {
404         GtkTreeIter iter;
405         GList *cols;
406
407         /* Fetch row */
408         gtk_tree_model_get_iter (GTK_TREE_MODEL(packetlist), &iter, path);
409         *row = row_from_iter(&iter);
410         gtk_tree_path_free(path);
411
412         /* Fetch column */
413         cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(packetlist->view));
414         *column = g_list_index(cols, (gpointer) view_column);
415         g_list_free(cols);
416
417         return TRUE;
418     }
419     else
420         return FALSE;
421 }
422
423 frame_data *
424 new_packet_list_get_row_data(gint row)
425 {
426         PacketListRecord *record;
427
428         record = packetlist->rows[row];
429
430         return record->fdata;
431 }
432
433 static guint
434 row_from_iter(GtkTreeIter *iter)
435 {
436         PacketListRecord *record;
437
438         record = iter->user_data;
439
440         return record->pos;
441 }
442
443 static gboolean
444 get_dissected_flag_from_iter(GtkTreeIter *iter)
445 {
446         PacketListRecord *record;
447
448         record = iter->user_data;
449
450         return record->dissected;
451 }
452
453 static gboolean
454 col_text_present_from_iter(GtkTreeIter *iter)
455 {
456         PacketListRecord *record;
457
458         record = iter->user_data;
459
460         return record->col_text != NULL;
461 }
462
463 static void
464 set_dissected_flag_from_iter(GtkTreeIter *iter, gboolean dissected)
465 {
466         PacketListRecord *record;
467
468         record = iter->user_data;
469
470         record->dissected = dissected;
471 }
472
473 /* XXX: will this work with display filters? */
474 static gboolean
475 iter_from_row(GtkTreeIter *iter, guint row)
476 {
477         GtkTreeModel *model = GTK_TREE_MODEL(packetlist);
478
479         return gtk_tree_model_iter_nth_child(model, iter, NULL, row);
480 }
481
482 static void
483 new_packet_list_dissect(frame_data *fdata, gboolean col_text_present)
484 {
485         epan_dissect_t *edt;
486         int err;
487         gchar *err_info;
488         column_info *cinfo;
489
490         /* We need to construct the columns if we skipped the columns entirely
491          * when reading the file or if we have custom columns enabled */
492         if (have_custom_cols(&cfile.cinfo) || !col_text_present)
493                 cinfo = &cfile.cinfo;
494         else
495                 cinfo = NULL;
496
497         if (!wtap_seek_read(cfile.wth, fdata->file_off, &cfile.pseudo_header,
498                 cfile.pd, fdata->cap_len, &err, &err_info)) {
499                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
500                         cf_read_error_message(err, err_info), cfile.filename);
501                         return;
502         }
503
504         edt = epan_dissect_new(TRUE /* create_proto_tree */, FALSE /* proto_tree_visible */);
505         color_filters_prime_edt(edt);
506         col_custom_prime_edt(edt, &cfile.cinfo);
507         epan_dissect_run(edt, &cfile.pseudo_header, cfile.pd, fdata, cinfo);
508         fdata->color_filter = color_filters_colorize_packet(0 /* row - unused */, edt);
509
510         /* "Stringify" non frame_data vals */
511         if (!col_text_present)
512                 epan_dissect_fill_in_columns(edt, FALSE /* fill_fd_colums */);
513
514         epan_dissect_free(edt);
515 }
516
517 static void
518 cache_columns(frame_data *fdata, guint row, gboolean col_text_present)
519 {
520         int col;
521
522         /* None of the columns are present. Fill them out in the record */
523         if (!col_text_present) {
524                 for(col = 0; col < cfile.cinfo.num_cols; ++col) {
525                         /* Skip columns based om frame_data because we  already store those. */
526                         if (!col_based_on_frame_data(&cfile.cinfo, col))
527                                 packet_list_change_record(packetlist, row, col, &cfile.cinfo);
528                 }
529                 return;
530         }
531
532         /* Custom columns are present. Fill them out in the record */
533         if (have_custom_cols(&cfile.cinfo))
534                 for (col = cfile.cinfo.col_first[COL_CUSTOM];
535                          col <= cfile.cinfo.col_last[COL_CUSTOM];
536                          ++col)
537                         if (cfile.cinfo.col_fmt[col] == COL_CUSTOM)
538                                 packet_list_change_record(packetlist, row, col, &cfile.cinfo);
539 }
540
541 static void
542 show_cell_data_func(GtkTreeViewColumn *col _U_, GtkCellRenderer *renderer,
543                     GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
544 {
545         guint row = row_from_iter(iter);
546         guint col_num = GPOINTER_TO_INT(data);
547         frame_data *fdata = new_packet_list_get_row_data(row);
548         color_filter_t *color_filter;
549         color_t fg_color_t;
550         color_t bg_color_t;
551         GdkColor fg_gdk;
552         GdkColor bg_gdk;
553         gchar *cell_text;
554
555         if (get_dissected_flag_from_iter(iter))
556                 color_filter = fdata->color_filter;
557         else {
558                 gboolean col_text_present = col_text_present_from_iter(iter);
559                 new_packet_list_dissect(fdata, col_text_present);
560                 set_dissected_flag_from_iter(iter, TRUE);
561                 cache_columns(fdata, row, col_text_present);
562                 color_filter = fdata->color_filter;
563         }
564
565         if (col_based_on_frame_data(&cfile.cinfo, col_num)) {
566                 col_fill_in_frame_data(fdata, &cfile.cinfo, col_num);
567                 cell_text = g_strdup(cfile.cinfo.col_data[col_num]);
568         }else{
569                 gtk_tree_model_get(model, iter,
570                                    col_num, &cell_text,
571                                    -1);
572         }
573
574         if((fdata->color_filter)||(fdata->flags.marked)){
575                 gboolean color_on = enable_color;
576                 if(fdata->flags.marked){
577                         color_t_to_gdkcolor(&fg_gdk, &prefs.gui_marked_fg);
578                         color_t_to_gdkcolor(&bg_gdk, &prefs.gui_marked_bg);
579                         color_on = TRUE;
580                 }else{
581                         color_filter = fdata->color_filter;
582                         fg_color_t = color_filter->fg_color;
583                         bg_color_t = color_filter->bg_color;
584                         color_t_to_gdkcolor(&fg_gdk, &fg_color_t);
585                         color_t_to_gdkcolor(&bg_gdk, &bg_color_t);
586                 }
587                 g_object_set(renderer,
588                      "text", cell_text,
589                      "foreground-gdk", &fg_gdk,
590                      "foreground-set", color_on,
591                      "background-gdk", &bg_gdk,
592                      "background-set", color_on,
593                      NULL);
594         }else{
595                 g_object_set(renderer,
596                      "text", cell_text,
597                      NULL);
598         }
599
600         g_free(cell_text);
601 }
602
603 void
604 new_packet_list_enable_color(gboolean enable)
605 {
606         enable_color = enable;
607         gtk_widget_queue_draw (packetlist->view);
608 }
609
610 void
611 new_packet_list_queue_draw(void)
612 {
613         gtk_widget_queue_draw (packetlist->view);
614 }
615
616 /* call this after last set_frame_mark is done */
617 static void mark_frames_ready(void) 
618 {
619   file_save_update_dynamics();
620   packets_bar_update();
621 }
622
623 static void
624 set_frame_mark(gboolean set, frame_data *frame)
625 {
626   if (set) {
627     cf_mark_frame(&cfile, frame);
628   } else {
629     cf_unmark_frame(&cfile, frame);
630   }
631 }
632
633 void
634 new_packet_list_set_font(PangoFontDescription *font)
635 {
636         gtk_widget_modify_font(packetlist->view, font);
637 }
638
639 void new_packet_list_mark_frame_cb(GtkWidget *w _U_, gpointer data _U_) 
640 {
641         GtkTreeSelection *selection;
642         GtkTreeIter iter;
643         guint row;
644         frame_data *fdata;
645
646         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(packetlist->view));
647         gtk_tree_selection_get_selected(selection, NULL, &iter);
648         row = row_from_iter(&iter);
649         
650         fdata = new_packet_list_get_row_data(row);
651         if (fdata != NULL){
652                 set_frame_mark(!fdata->flags.marked, fdata);
653         }
654     mark_frames_ready();
655 }
656
657 #endif /* NEW_PACKET_LIST */