33f45c5eb64a06e55bd42a5f023a8b017249ff43
[obnox/wireshark/wip.git] / gtk / funnel_stat.c
1 /*
2  * funnel_stat.c
3  *
4  * EPAN's funneled GUI mini-API
5  *
6  * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org>
7  *
8  * $Id$
9  *
10  * Wireshark - Network traffic analyzer
11  * By Gerald Combs <gerald@wireshark.org>
12  * Copyright 1998 Gerald Combs
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27  */
28
29 /*
30  * Most of the code here has been harvested from other Wireshark gtk modules.
31  * most from prefs_dlg.c and about_dlg.c
32  */
33
34 #ifdef HAVE_CONFIG_H
35 # include "config.h"
36 #endif
37
38 #ifdef HAVE_SYS_TYPES_H
39 # include <sys/types.h>
40 #endif
41
42 #include <stdio.h>
43 #include <string.h>
44
45 #include <gtk/gtk.h>
46
47 #include <epan/prefs.h>
48 #include <epan/funnel.h>
49
50 #include "../timestats.h"
51 #include "../simple_dialog.h"
52 #include "../file.h"
53 #include "../stat_menu.h"
54 #include "../file.h"
55 #include "../progress_dlg.h"
56
57 #include "gtk/gui_utils.h"
58 #include "gtk/dlg_utils.h"
59 #include "gtk/tap_dfilter_dlg.h"
60 #include "gtk/font_utils.h"
61 #include "gtk/gui_stat_menu.h"
62 #include "gtk/prefs_dlg.h"
63 #include "gtk/main.h"
64 #include "gtk/webbrowser.h"
65 #include "gtk/gtkglobals.h"
66
67
68 struct _funnel_text_window_t {
69         GtkWidget* win;
70     GtkWidget* txt;
71     GtkWidget* button_hbox;
72     GtkWidget* bt_close;
73     text_win_close_cb_t close_cb;
74     void* close_data;
75         GPtrArray* buttons;
76 };
77
78 struct _funnel_tree_window_t {
79         GtkWidget *win;
80
81 };
82
83 struct _funnel_node_t {
84     void* dummy;
85 };
86
87 static void text_window_cancel_button_cb(GtkWidget *bt _U_, gpointer data) {
88     funnel_text_window_t* tw = data;
89
90     window_destroy(GTK_WIDGET(tw->win));
91     tw->win = NULL;
92
93     if (tw->close_cb)
94         tw->close_cb(tw->close_data);
95 }
96
97 static void unref_text_win_cancel_bt_cb(GtkWidget *bt _U_, gpointer data) {
98     funnel_text_window_t* tw = data;
99     unsigned i;
100
101     window_destroy(GTK_WIDGET(tw->win));
102     tw->win = NULL;
103
104     if (tw->close_cb)
105         tw->close_cb(tw->close_data);
106
107         for (i = 0; i < tw->buttons->len; i++) {
108                 funnel_bt_t* cbd = g_ptr_array_index(tw->buttons,i);
109                 /* XXX a free cb should be passed somehow */
110                 if (cbd->data && cbd->free_data_fcn) cbd->free_data_fcn(cbd->data);
111                 if (cbd->free_fcn) cbd->free_fcn(cbd);
112         }
113         g_ptr_array_free(tw->buttons,TRUE);
114     g_free(tw);
115 }
116
117
118 static gboolean text_window_unref_del_event_cb(GtkWidget *win _U_, GdkEvent *event _U_, gpointer user_data) {
119     funnel_text_window_t* tw = user_data;
120     unsigned i;
121
122     window_destroy(GTK_WIDGET(tw->win));
123     tw->win = NULL;
124
125     if (tw->close_cb)
126         tw->close_cb(tw->close_data);
127
128         for (i = 0; i < tw->buttons->len; i++) {
129                 funnel_bt_t* cbd = g_ptr_array_index(tw->buttons,i);
130                 /* XXX a free cb should be passed somehow */
131                 if (cbd->data && cbd->free_data_fcn) cbd->free_data_fcn(cbd->data);
132                 if (cbd->free_fcn) cbd->free_fcn(cbd);
133         }
134         g_ptr_array_free(tw->buttons,TRUE);
135     g_free(tw);
136
137     return TRUE;
138 }
139
140 static gboolean text_window_delete_event_cb(GtkWidget *win _U_, GdkEvent *event _U_, gpointer user_data)
141 {
142     funnel_text_window_t* tw = user_data;
143
144     window_destroy(GTK_WIDGET(tw->win));
145     tw->win = NULL;
146
147     if (tw->close_cb)
148         tw->close_cb(tw->close_data);
149
150     return TRUE;
151 }
152
153 static funnel_text_window_t* new_text_window(const gchar* title) {
154     funnel_text_window_t* tw = g_malloc(sizeof(funnel_text_window_t));
155         GtkWidget *txt_scrollw, *main_vb, *hbox;
156
157     tw->close_cb = NULL;
158     tw->close_data = NULL;
159     tw->buttons = g_ptr_array_new();
160
161         tw->win = dlg_window_new(title);  /* transient_for top_level */
162         gtk_window_set_destroy_with_parent (GTK_WINDOW(tw->win), TRUE);
163
164     g_signal_connect(tw->win, "delete-event", G_CALLBACK(text_window_delete_event_cb), tw);
165
166     txt_scrollw = scrolled_window_new(NULL, NULL);
167     main_vb = gtk_vbox_new(FALSE, 3);
168         gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
169         gtk_container_add(GTK_CONTAINER(tw->win), main_vb);
170
171     gtk_container_add(GTK_CONTAINER(main_vb), txt_scrollw);
172
173     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw),
174                                         GTK_SHADOW_IN);
175
176     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scrollw),
177                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
178     tw->txt = gtk_text_view_new();
179     gtk_text_view_set_editable(GTK_TEXT_VIEW(tw->txt), FALSE);
180     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(tw->txt), GTK_WRAP_WORD);
181     gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(tw->txt), FALSE);
182
183     gtk_text_view_set_left_margin(GTK_TEXT_VIEW(tw->txt), 4);
184     gtk_text_view_set_right_margin(GTK_TEXT_VIEW(tw->txt), 4);
185
186         hbox = gtk_hbox_new(FALSE, 0);
187     gtk_widget_show(hbox);
188
189     tw->button_hbox = gtk_hbutton_box_new();
190         gtk_button_box_set_layout(GTK_BUTTON_BOX(tw->button_hbox), GTK_BUTTONBOX_START);
191
192     gtk_box_pack_start(GTK_BOX(hbox), tw->button_hbox, TRUE, TRUE, 0);
193     gtk_widget_show(tw->button_hbox);
194
195         gtk_box_pack_start(GTK_BOX(main_vb), hbox, FALSE, FALSE, 0);
196
197         tw->bt_close = gtk_button_new_with_label("Close");
198         GTK_WIDGET_SET_FLAGS(tw->bt_close, GTK_CAN_DEFAULT);
199         g_object_set_data(G_OBJECT(hbox), "Close", tw->bt_close);
200
201         gtk_box_pack_end(GTK_BOX(hbox), tw->bt_close, FALSE, FALSE, 0);
202         gtk_widget_show(tw->bt_close);
203
204         g_signal_connect(tw->bt_close, "clicked", G_CALLBACK(text_window_cancel_button_cb), tw);
205     gtk_widget_grab_default(tw->bt_close);
206
207         gtk_container_add(GTK_CONTAINER(txt_scrollw), tw->txt);
208     gtk_window_resize(GTK_WINDOW(tw->win),400,300);
209     gtk_widget_show_all(tw->win);
210
211     return tw;
212 }
213
214
215 static void text_window_clear(funnel_text_window_t*  tw)
216 {
217     GtkTextBuffer *buf;
218
219     if (! tw->win) return;
220
221     buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tw->txt));
222
223     gtk_text_buffer_set_text(buf, "", 0);
224 }
225
226
227 static void text_window_append(funnel_text_window_t*  tw, const char *str)
228 {
229     GtkWidget *txt;
230     int nchars;
231     GtkTextBuffer *buf;
232     GtkTextIter    iter;
233
234     if (! tw->win) return;
235
236     txt = tw->txt;
237     nchars = (int) strlen(str);
238
239
240     buf= gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
241
242     gtk_text_buffer_get_end_iter(buf, &iter);
243     gtk_widget_modify_font(GTK_WIDGET(txt), user_font_get_regular());
244
245     if (!g_utf8_validate(str, -1, NULL))
246         printf("Invalid utf8 encoding: %s\n", str);
247
248     gtk_text_buffer_insert(buf, &iter, str, nchars);
249 }
250
251
252 static void text_window_set_text(funnel_text_window_t*  tw, const gchar* text)
253 {
254     if (! tw->win) return;
255
256     text_window_clear(tw);
257     text_window_append(tw, text);
258 }
259
260
261 static void text_window_prepend(funnel_text_window_t*  tw, const char *str _U_) {
262     GtkWidget *txt;
263     int nchars;
264     GtkTextBuffer *buf;
265     GtkTextIter    iter;
266
267     if (! tw->win) return;
268
269     txt = tw->txt;
270     nchars = (int) strlen(str);
271
272
273     buf= gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
274
275     gtk_text_buffer_get_start_iter(buf, &iter);
276     gtk_widget_modify_font(GTK_WIDGET(txt), user_font_get_regular());
277
278     if (!g_utf8_validate(str, -1, NULL))
279         printf("Invalid utf8 encoding: %s\n", str);
280
281     gtk_text_buffer_insert(buf, &iter, str, nchars);
282 }
283
284 static const gchar* text_window_get_text(funnel_text_window_t*  tw) {
285     GtkWidget *txt;
286     GtkTextBuffer *buf;
287     GtkTextIter    start;
288     GtkTextIter    end;
289
290     if (! tw->win) return "";
291
292         txt = tw->txt;
293
294     buf= gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt));
295         gtk_text_buffer_get_start_iter(buf, &start);
296         gtk_text_buffer_get_end_iter(buf, &end);
297
298         return gtk_text_buffer_get_text(buf, &start, &end, FALSE);
299 }
300
301
302
303 static void text_window_set_close_cb(funnel_text_window_t*  tw, text_win_close_cb_t cb, void* data) {
304     tw->close_cb = cb;
305     tw->close_data = data;
306 }
307
308 static void text_window_destroy(funnel_text_window_t*  tw) {
309     if (tw->win) {
310         /*
311          * the window is still there and its callbacks refer to this data structure
312          * we need to change the callback so that they free tw.
313          */
314         g_signal_connect(tw->bt_close, "clicked", G_CALLBACK(unref_text_win_cancel_bt_cb), tw);
315         g_signal_connect(tw->win, "delete-event", G_CALLBACK(text_window_unref_del_event_cb), tw);
316     } else {
317                 unsigned i;
318         /*
319          * we have no window anymore a human user closed
320          * the window already just free the container
321          */
322                 for (i = 0; i < tw->buttons->len; i++) {
323                         funnel_bt_t* cbd = g_ptr_array_index(tw->buttons,i);
324                         /* XXX a free cb should be passed somehow */
325                         if (cbd->data && cbd->free_data_fcn) cbd->free_data_fcn(cbd->data);
326                         if (cbd->free_fcn) cbd->free_fcn(cbd);
327                 }
328                 g_ptr_array_free(tw->buttons,TRUE);
329         g_free(tw);
330     }
331 }
332
333 static void text_window_set_editable(funnel_text_window_t*  tw, gboolean editable){
334         gtk_text_view_set_editable(GTK_TEXT_VIEW(tw->txt), editable);
335         gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(tw->txt), editable);
336 }
337
338 static gboolean text_window_button_cb(GtkWidget *bt _U_, gpointer user_data)
339 {
340         funnel_bt_t* cbd = user_data;
341
342         if (cbd->func) {
343                 return cbd->func(cbd->tw,cbd->data);
344         } else {
345                 return TRUE;
346         }
347 }
348
349 static void text_window_add_button(funnel_text_window_t*  tw, funnel_bt_t* cbd, const gchar* label) {
350         GtkWidget *button;
351
352         cbd->tw = tw;
353         g_ptr_array_add(tw->buttons,cbd);
354
355         button = gtk_button_new_with_label(label);
356         GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
357
358         gtk_box_pack_start(GTK_BOX(tw->button_hbox), button, FALSE, FALSE, 0);
359
360         gtk_widget_show(button);
361         g_signal_connect(button, "clicked", G_CALLBACK(text_window_button_cb), cbd);
362
363 }
364
365
366 struct _funnel_dlg_data {
367     GtkWidget* win;
368     GPtrArray* entries;
369     funnel_dlg_cb_t dlg_cb;
370     void* data;
371 };
372
373 static gboolean funnel_dlg_cb(GtkWidget *win _U_, gpointer user_data)
374 {
375     struct _funnel_dlg_data* dd = user_data;
376     guint i;
377     guint len = dd->entries->len;
378     GPtrArray* returns = g_ptr_array_new();
379
380     for(i=0; i<len; i++) {
381         GtkEntry* entry = g_ptr_array_index(dd->entries,i);
382         g_ptr_array_add(returns,g_strdup(gtk_entry_get_text(entry)));
383     }
384
385     g_ptr_array_add(returns,NULL);
386
387     if (dd->dlg_cb)
388         dd->dlg_cb((gchar**)returns->pdata,dd->data);
389
390     window_destroy(GTK_WIDGET(dd->win));
391
392     g_ptr_array_free(returns,FALSE);
393
394     return TRUE;
395 }
396
397 static void funnel_cancel_btn_cb(GtkWidget *bt _U_, gpointer data) {
398     GtkWidget* win = data;
399
400     window_destroy(GTK_WIDGET(win));
401 }
402
403 static void funnel_new_dialog(const gchar* title,
404                                           const gchar** fieldnames,
405                                           funnel_dlg_cb_t dlg_cb,
406                                           void* data) {
407     GtkWidget *win, *main_tb, *main_vb, *bbox, *bt_cancel, *bt_ok;
408     guint i;
409     const gchar* fieldname;
410     struct _funnel_dlg_data* dd = g_malloc(sizeof(struct _funnel_dlg_data));
411
412     dd->entries = g_ptr_array_new();
413     dd->dlg_cb = dlg_cb;
414     dd->data = data;
415
416     for (i=0;fieldnames[i];i++);
417
418     win = dlg_window_new(title);
419
420     dd->win = win;
421
422     gtk_window_resize(GTK_WINDOW(win),400,10*(i+2));
423
424     main_vb = gtk_vbox_new(TRUE,5);
425     gtk_container_add(GTK_CONTAINER(win), main_vb);
426         gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
427
428     main_tb = gtk_table_new(i+1, 2, FALSE);
429     gtk_box_pack_start(GTK_BOX(main_vb), main_tb, FALSE, FALSE, 0);
430     gtk_table_set_row_spacings(GTK_TABLE(main_tb), 10);
431     gtk_table_set_col_spacings(GTK_TABLE(main_tb), 15);
432
433     for (i = 0; (fieldname = fieldnames[i]) ; i++) {
434         GtkWidget *entry, *label;
435
436         label = gtk_label_new(fieldname);
437         gtk_misc_set_alignment(GTK_MISC(label), 1.0f, 0.5f);
438         gtk_table_attach_defaults(GTK_TABLE(main_tb), label, 0, 1, i+1, i + 2);
439         gtk_widget_show(label);
440
441         entry = gtk_entry_new();
442         g_ptr_array_add(dd->entries,entry);
443         gtk_table_attach_defaults(GTK_TABLE(main_tb), entry, 1, 2, i+1, i + 2);
444         gtk_widget_show(entry);
445     }
446
447     bbox = dlg_button_row_new(GTK_STOCK_CANCEL,GTK_STOCK_OK, NULL);
448     gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
449
450     bt_ok = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
451     g_signal_connect(bt_ok, "clicked", G_CALLBACK(funnel_dlg_cb), dd);
452     gtk_widget_grab_default(bt_ok);
453
454     bt_cancel = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
455     g_signal_connect(bt_cancel, "clicked", G_CALLBACK(funnel_cancel_btn_cb), win);
456     gtk_widget_grab_default(bt_cancel);
457
458     gtk_widget_show(main_tb);
459     gtk_widget_show(main_vb);
460     gtk_widget_show(win);
461 }
462
463 static void funnel_set_filter(const char* filter_string) {
464         gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), filter_string);
465 }
466
467 static void funnel_apply_filter(void) {
468         const char* filter_string = gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget));
469         main_filter_packets(&cfile, filter_string, FALSE);
470 }
471
472 /* XXX: finish this */
473 static void funnel_logger(const gchar *log_domain _U_,
474                           GLogLevelFlags log_level _U_,
475                           const gchar *message,
476                           gpointer user_data _U_) {
477     fputs(message,stderr);
478 }
479
480 static void funnel_retap_packets(void) {
481         cf_retap_packets(&cfile);
482 }
483
484 static gboolean funnel_open_file(const char* fname, const char* filter, const char** err_str) {
485         int err = 0;
486         dfilter_t   *rfcode = NULL;
487
488         *err_str = "no error";
489
490         switch (cfile.state) {
491                 case FILE_CLOSED:
492                 case FILE_READ_DONE:
493                 case FILE_READ_ABORTED:
494                         break;
495                 case FILE_READ_IN_PROGRESS:
496                         *err_str = "file read in progress";
497                         return FALSE;
498         }
499
500         if (filter) {
501                 if (!dfilter_compile(filter, &rfcode)) {
502                         *err_str = dfilter_error_msg ? dfilter_error_msg : "cannot compile filter";
503                         return FALSE;
504                 }
505         }
506
507
508         if (cf_open(&cfile, fname, FALSE, &err) != CF_OK) {
509                 *err_str = strerror(err);
510                 if (rfcode != NULL) dfilter_free(rfcode);
511                 return FALSE;
512         }
513
514         cfile.rfcode = rfcode;
515
516         switch (cf_read(&cfile, FALSE)) {
517                 case CF_READ_OK:
518                 case CF_READ_ERROR:
519                         break;
520                 default:
521                         *err_str = "problem while reading file";
522                         return FALSE;
523         }
524
525         return TRUE;
526 }
527
528 typedef struct progdlg _funnel_progress_window_t;
529
530 static funnel_progress_window_t* funnel_new_progress_window(const gchar* label, const gchar* task, gboolean terminate_is_stop, gboolean *stop_flag) {
531     return (funnel_progress_window_t*)create_progress_dlg(label, task, terminate_is_stop, stop_flag);
532 }
533
534 static void funnel_update_progress(funnel_progress_window_t* win, float pr, const gchar* task) {
535     update_progress_dlg((progdlg_t*)win, pr, task);
536 }
537
538 static void funnel_destroy_progress_window(funnel_progress_window_t* win) {
539     destroy_progress_dlg((progdlg_t*)win);
540 }
541
542 static void funnel_reload(void) {
543         if (cfile.state == FILE_READ_DONE) cf_reload(&cfile);
544 }
545
546 static const funnel_ops_t funnel_ops = {
547     new_text_window,
548     text_window_set_text,
549     text_window_append,
550     text_window_prepend,
551     text_window_clear,
552     text_window_get_text,
553     text_window_set_close_cb,
554         text_window_set_editable,
555     text_window_destroy,
556         text_window_add_button,
557     /*...,*/
558     funnel_new_dialog,
559     funnel_logger,
560         funnel_retap_packets,
561         copy_to_clipboard,
562         funnel_set_filter,
563         funnel_open_file,
564         funnel_reload,
565         funnel_apply_filter,
566         browser_open_url,
567         browser_open_data_file,
568     funnel_new_progress_window,
569     funnel_update_progress,
570     funnel_destroy_progress_window
571 };
572
573
574 typedef struct _menu_cb_t {
575     void (*callback)(gpointer);
576     void* callback_data;
577     gboolean retap;
578 } menu_cb_t;
579
580 static void our_menu_callback(void* unused _U_, gpointer data) {
581     menu_cb_t* mcb = data;
582     mcb->callback(mcb->callback_data);
583     if (mcb->retap) cf_retap_packets(&cfile);
584 }
585
586 static void register_menu_cb(const char *name,
587                              register_stat_group_t group,
588                              void (*callback)(gpointer),
589                              gpointer callback_data,
590                              gboolean retap) {
591     menu_cb_t* mcb = g_malloc(sizeof(menu_cb_t));
592
593     mcb->callback = callback;
594     mcb->callback_data = callback_data;
595     mcb->retap = retap;
596
597     register_stat_menu_item(name, group, our_menu_callback, NULL, NULL, mcb);
598
599 }
600
601 void initialize_funnel_ops(void) {
602     funnel_set_funnel_ops(&funnel_ops);
603 }
604
605 void
606 register_tap_listener_gtkfunnel(void)
607 {
608     funnel_register_all_menus(register_menu_cb);
609 }