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