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