4 * EPAN's funneled GUI mini-API
6 * (c) 2006, Luis E. Garcia Ontanon <luis@ontanon.org>
10 * Wireshark - Network traffic analyzer
11 * By Gerald Combs <gerald@wireshark.org>
12 * Copyright 1998 Gerald Combs
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 * Most of the code here has been harvested from other Wireshark gtk modules.
31 * most from prefs_dlg.c and about_dlg.c
33 * (From original checkin message:
34 * The funneled GUI mini API.
35 * A very reduced set of gui ops (by now just a text window)
36 * that can be funneled to dissectors (even plugins) via epan.
46 #include <epan/prefs.h>
47 #include <epan/funnel.h>
50 #include "../stat_menu.h"
51 #include "ui/progress_dlg.h"
52 #include "../color_filters.h"
54 #include "ui/gtk/gui_utils.h"
55 #include "ui/gtk/dlg_utils.h"
56 #include "ui/gtk/tap_param_dlg.h"
57 #include "ui/gtk/font_utils.h"
58 #include "ui/gtk/gui_stat_menu.h"
59 #include "ui/gtk/prefs_dlg.h"
60 #include "ui/gtk/main.h"
61 #include "ui/gtk/webbrowser.h"
62 #include "ui/gtk/gtkglobals.h"
63 #include "ui/gtk/old-gtk-compat.h"
66 struct _funnel_text_window_t
{
69 GtkWidget
* button_hbox
;
71 text_win_close_cb_t close_cb
;
76 struct _funnel_tree_window_t
{
81 struct _funnel_node_t
{
85 static void text_window_cancel_button_cb(GtkWidget
*bt _U_
, gpointer data
) {
86 funnel_text_window_t
* tw
= (funnel_text_window_t
*)data
;
88 window_destroy(GTK_WIDGET(tw
->win
));
92 tw
->close_cb(tw
->close_data
);
95 static void unref_text_win_cancel_bt_cb(GtkWidget
*bt _U_
, gpointer data
) {
96 funnel_text_window_t
* tw
= (funnel_text_window_t
*)data
;
99 window_destroy(GTK_WIDGET(tw
->win
));
103 tw
->close_cb(tw
->close_data
);
105 for (i
= 0; i
< tw
->buttons
->len
; i
++) {
106 funnel_bt_t
* cbd
= (funnel_bt_t
*)g_ptr_array_index(tw
->buttons
,i
);
107 /* XXX a free cb should be passed somehow */
108 if (cbd
->data
&& cbd
->free_data_fcn
) cbd
->free_data_fcn(cbd
->data
);
109 if (cbd
->free_fcn
) cbd
->free_fcn(cbd
);
111 g_ptr_array_free(tw
->buttons
,TRUE
);
116 static gboolean
text_window_unref_del_event_cb(GtkWidget
*win _U_
, GdkEvent
*event _U_
, gpointer user_data
) {
117 funnel_text_window_t
* tw
= (funnel_text_window_t
*)user_data
;
120 window_destroy(GTK_WIDGET(tw
->win
));
124 tw
->close_cb(tw
->close_data
);
126 for (i
= 0; i
< tw
->buttons
->len
; i
++) {
127 funnel_bt_t
* cbd
= (funnel_bt_t
*)g_ptr_array_index(tw
->buttons
,i
);
128 /* XXX a free cb should be passed somehow */
129 if (cbd
->data
&& cbd
->free_data_fcn
) cbd
->free_data_fcn(cbd
->data
);
130 if (cbd
->free_fcn
) cbd
->free_fcn(cbd
);
132 g_ptr_array_free(tw
->buttons
,TRUE
);
138 static gboolean
text_window_delete_event_cb(GtkWidget
*win _U_
, GdkEvent
*event _U_
, gpointer user_data
)
140 funnel_text_window_t
* tw
= (funnel_text_window_t
*)user_data
;
142 window_destroy(GTK_WIDGET(tw
->win
));
146 tw
->close_cb(tw
->close_data
);
151 static funnel_text_window_t
* new_text_window(const gchar
* title
) {
152 funnel_text_window_t
* tw
= (funnel_text_window_t
*)g_malloc(sizeof(funnel_text_window_t
));
153 GtkWidget
*txt_scrollw
, *main_vb
, *hbox
;
156 tw
->close_data
= NULL
;
157 tw
->buttons
= g_ptr_array_new();
159 tw
->win
= dlg_window_new(title
); /* transient_for top_level */
160 gtk_window_set_destroy_with_parent (GTK_WINDOW(tw
->win
), TRUE
);
162 g_signal_connect(tw
->win
, "delete-event", G_CALLBACK(text_window_delete_event_cb
), tw
);
164 txt_scrollw
= scrolled_window_new(NULL
, NULL
);
165 main_vb
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 3, FALSE
);
166 gtk_container_set_border_width(GTK_CONTAINER(main_vb
), 6);
167 gtk_container_add(GTK_CONTAINER(tw
->win
), main_vb
);
169 gtk_box_pack_start(GTK_BOX (main_vb
), txt_scrollw
, TRUE
, TRUE
, 0);
171 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scrollw
),
174 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scrollw
),
175 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
176 tw
->txt
= gtk_text_view_new();
177 gtk_text_view_set_editable(GTK_TEXT_VIEW(tw
->txt
), FALSE
);
178 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(tw
->txt
), GTK_WRAP_WORD
);
179 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(tw
->txt
), FALSE
);
181 gtk_text_view_set_left_margin(GTK_TEXT_VIEW(tw
->txt
), 4);
182 gtk_text_view_set_right_margin(GTK_TEXT_VIEW(tw
->txt
), 4);
184 hbox
= ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0, FALSE
);
185 gtk_widget_show(hbox
);
187 tw
->button_hbox
= gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL
);
188 gtk_button_box_set_layout(GTK_BUTTON_BOX(tw
->button_hbox
), GTK_BUTTONBOX_START
);
190 gtk_box_pack_start(GTK_BOX(hbox
), tw
->button_hbox
, TRUE
, TRUE
, 0);
191 gtk_widget_show(tw
->button_hbox
);
193 gtk_box_pack_start(GTK_BOX(main_vb
), hbox
, FALSE
, FALSE
, 0);
195 tw
->bt_close
= gtk_button_new_with_label("Close");
196 gtk_widget_set_can_default(tw
->bt_close
, TRUE
);
197 g_object_set_data(G_OBJECT(hbox
), "Close", tw
->bt_close
);
199 gtk_box_pack_end(GTK_BOX(hbox
), tw
->bt_close
, FALSE
, FALSE
, 0);
200 gtk_widget_show(tw
->bt_close
);
202 g_signal_connect(tw
->bt_close
, "clicked", G_CALLBACK(text_window_cancel_button_cb
), tw
);
203 gtk_widget_grab_default(tw
->bt_close
);
205 gtk_container_add(GTK_CONTAINER(txt_scrollw
), tw
->txt
);
206 gtk_window_resize(GTK_WINDOW(tw
->win
),400,300);
207 gtk_widget_show_all(tw
->win
);
213 static void text_window_clear(funnel_text_window_t
* tw
)
217 if (! tw
->win
) return;
219 buf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(tw
->txt
));
221 gtk_text_buffer_set_text(buf
, "", 0);
225 static void text_window_append(funnel_text_window_t
* tw
, const char *str
)
232 if (! tw
->win
) return;
235 nchars
= (int) strlen(str
);
238 buf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt
));
240 gtk_text_buffer_get_end_iter(buf
, &iter
);
241 #if GTK_CHECK_VERSION(3,0,0)
242 gtk_widget_override_font(GTK_WIDGET(txt
), user_font_get_regular());
244 gtk_widget_modify_font(GTK_WIDGET(txt
), user_font_get_regular());
246 if (!g_utf8_validate(str
, -1, NULL
))
247 printf("Invalid utf8 encoding: %s\n", str
);
249 gtk_text_buffer_insert(buf
, &iter
, str
, nchars
);
253 static void text_window_set_text(funnel_text_window_t
* tw
, const gchar
* text
)
255 if (! tw
->win
) return;
257 text_window_clear(tw
);
258 text_window_append(tw
, text
);
262 static void text_window_prepend(funnel_text_window_t
* tw
, const char *str _U_
) {
268 if (! tw
->win
) return;
271 nchars
= (int) strlen(str
);
274 buf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt
));
276 gtk_text_buffer_get_start_iter(buf
, &iter
);
277 #if GTK_CHECK_VERSION(3,0,0)
278 gtk_widget_override_font(GTK_WIDGET(txt
), user_font_get_regular());
280 gtk_widget_modify_font(GTK_WIDGET(txt
), user_font_get_regular());
282 if (!g_utf8_validate(str
, -1, NULL
))
283 printf("Invalid utf8 encoding: %s\n", str
);
285 gtk_text_buffer_insert(buf
, &iter
, str
, nchars
);
288 static const gchar
* text_window_get_text(funnel_text_window_t
* tw
) {
294 if (! tw
->win
) return "";
298 buf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt
));
299 gtk_text_buffer_get_start_iter(buf
, &start
);
300 gtk_text_buffer_get_end_iter(buf
, &end
);
302 return gtk_text_buffer_get_text(buf
, &start
, &end
, FALSE
);
307 static void text_window_set_close_cb(funnel_text_window_t
* tw
, text_win_close_cb_t cb
, void* data
) {
309 tw
->close_data
= data
;
312 static void text_window_destroy(funnel_text_window_t
* tw
) {
315 * the window is still there and its callbacks refer to this data structure
316 * we need to change the callback so that they free tw.
318 g_signal_connect(tw
->bt_close
, "clicked", G_CALLBACK(unref_text_win_cancel_bt_cb
), tw
);
319 g_signal_connect(tw
->win
, "delete-event", G_CALLBACK(text_window_unref_del_event_cb
), tw
);
323 * we have no window anymore a human user closed
324 * the window already just free the container
326 for (i
= 0; i
< tw
->buttons
->len
; i
++) {
327 funnel_bt_t
* cbd
= (funnel_bt_t
*)g_ptr_array_index(tw
->buttons
,i
);
328 /* XXX a free cb should be passed somehow */
329 if (cbd
->data
&& cbd
->free_data_fcn
) cbd
->free_data_fcn(cbd
->data
);
330 if (cbd
->free_fcn
) cbd
->free_fcn(cbd
);
332 g_ptr_array_free(tw
->buttons
,TRUE
);
337 static void text_window_set_editable(funnel_text_window_t
* tw
, gboolean editable
){
338 gtk_text_view_set_editable(GTK_TEXT_VIEW(tw
->txt
), editable
);
339 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(tw
->txt
), editable
);
342 static gboolean
text_window_button_cb(GtkWidget
*bt _U_
, gpointer user_data
)
344 funnel_bt_t
* cbd
= (funnel_bt_t
*)user_data
;
347 return cbd
->func(cbd
->tw
,cbd
->data
);
353 static void text_window_add_button(funnel_text_window_t
* tw
, funnel_bt_t
* cbd
, const gchar
* label
) {
357 g_ptr_array_add(tw
->buttons
,cbd
);
359 button
= gtk_button_new_with_label(label
);
360 gtk_widget_set_can_default(button
, TRUE
);
362 gtk_box_pack_start(GTK_BOX(tw
->button_hbox
), button
, FALSE
, FALSE
, 0);
364 gtk_widget_show(button
);
365 g_signal_connect(button
, "clicked", G_CALLBACK(text_window_button_cb
), cbd
);
370 struct _funnel_dlg_data
{
373 funnel_dlg_cb_t dlg_cb
;
377 static gboolean
funnel_dlg_cb(GtkWidget
*win _U_
, gpointer user_data
)
379 struct _funnel_dlg_data
* dd
= (struct _funnel_dlg_data
*)user_data
;
381 guint len
= dd
->entries
->len
;
382 GPtrArray
* returns
= g_ptr_array_new();
384 for(i
=0; i
<len
; i
++) {
385 GtkEntry
* entry
= (GtkEntry
*)g_ptr_array_index(dd
->entries
,i
);
386 g_ptr_array_add(returns
,g_strdup(gtk_entry_get_text(entry
)));
389 g_ptr_array_add(returns
,NULL
);
392 dd
->dlg_cb((gchar
**)returns
->pdata
,dd
->data
);
394 window_destroy(GTK_WIDGET(dd
->win
));
396 g_ptr_array_free(returns
,FALSE
);
401 static void funnel_cancel_btn_cb(GtkWidget
*bt _U_
, gpointer data
) {
402 GtkWidget
* win
= (GtkWidget
*)data
;
404 window_destroy(GTK_WIDGET(win
));
407 static void funnel_new_dialog(const gchar
* title
,
408 const gchar
** fieldnames
,
409 funnel_dlg_cb_t dlg_cb
,
411 GtkWidget
*win
, *main_grid
, *main_vb
, *bbox
, *bt_cancel
, *bt_ok
;
413 const gchar
* fieldname
;
414 struct _funnel_dlg_data
* dd
= (struct _funnel_dlg_data
*)g_malloc(sizeof(struct _funnel_dlg_data
));
416 dd
->entries
= g_ptr_array_new();
420 for (i
=0; fieldnames
[i
]; i
++);
422 win
= dlg_window_new(title
);
426 gtk_window_resize(GTK_WINDOW(win
),400,10*(i
+2));
428 main_vb
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 5, FALSE
);
429 gtk_container_add(GTK_CONTAINER(win
), main_vb
);
430 gtk_container_set_border_width(GTK_CONTAINER(main_vb
), 6);
432 main_grid
= ws_gtk_grid_new();
433 gtk_box_pack_start(GTK_BOX(main_vb
), main_grid
, FALSE
, FALSE
, 0);
434 ws_gtk_grid_set_row_spacing(GTK_GRID(main_grid
), 10);
435 ws_gtk_grid_set_column_spacing(GTK_GRID(main_grid
), 15);
437 for (i
= 0; (fieldname
= fieldnames
[i
]) ; i
++) {
438 GtkWidget
*entry
, *label
;
440 label
= gtk_label_new(fieldname
);
441 gtk_misc_set_alignment(GTK_MISC(label
), 1.0f
, 0.5f
);
442 ws_gtk_grid_attach_defaults(GTK_GRID(main_grid
), label
, 0, i
+1, 1, 1);
443 gtk_widget_show(label
);
445 entry
= gtk_entry_new();
446 g_ptr_array_add(dd
->entries
,entry
);
447 ws_gtk_grid_attach_defaults(GTK_GRID(main_grid
), entry
, 1, i
+1, 1, 1);
448 gtk_widget_show(entry
);
451 bbox
= dlg_button_row_new(GTK_STOCK_CANCEL
,GTK_STOCK_OK
, NULL
);
452 gtk_box_pack_start(GTK_BOX(main_vb
), bbox
, FALSE
, FALSE
, 0);
454 bt_ok
= (GtkWidget
*)g_object_get_data(G_OBJECT(bbox
), GTK_STOCK_OK
);
455 g_signal_connect(bt_ok
, "clicked", G_CALLBACK(funnel_dlg_cb
), dd
);
456 gtk_widget_grab_default(bt_ok
);
458 bt_cancel
= (GtkWidget
*)g_object_get_data(G_OBJECT(bbox
), GTK_STOCK_CANCEL
);
459 g_signal_connect(bt_cancel
, "clicked", G_CALLBACK(funnel_cancel_btn_cb
), win
);
460 gtk_widget_grab_default(bt_cancel
);
462 gtk_widget_show(main_grid
);
463 gtk_widget_show(main_vb
);
464 gtk_widget_show(win
);
467 static gchar
* funnel_get_filter(void) {
468 return (gchar
*)gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget
));
471 static void funnel_set_filter(const char* filter_string
) {
472 gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget
), filter_string
);
475 static void funnel_set_color_filter_slot(guint8 filt_nr
, const gchar
* filter_string
) {
476 color_filters_set_tmp(filt_nr
, (gchar
*)filter_string
, FALSE
);
479 static void funnel_apply_filter(void) {
480 const char* filter_string
= gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget
));
481 main_filter_packets(&cfile
, filter_string
, FALSE
);
484 /* XXX: finish this */
485 static void funnel_logger(const gchar
*log_domain _U_
,
486 GLogLevelFlags log_level _U_
,
487 const gchar
*message
,
488 gpointer user_data _U_
) {
489 fputs(message
,stderr
);
492 static void funnel_retap_packets(void) {
493 cf_retap_packets(&cfile
);
496 static gboolean
funnel_open_file(const char* fname
, const char* filter
, const char** err_str
) {
498 dfilter_t
*rfcode
= NULL
;
500 *err_str
= "no error";
502 switch (cfile
.state
) {
505 case FILE_READ_ABORTED
:
507 case FILE_READ_IN_PROGRESS
:
508 *err_str
= "file read in progress";
513 if (!dfilter_compile(filter
, &rfcode
)) {
514 *err_str
= dfilter_error_msg
? dfilter_error_msg
: "cannot compile filter";
520 if (cf_open(&cfile
, fname
, FALSE
, &err
) != CF_OK
) {
521 *err_str
= g_strerror(err
);
522 if (rfcode
!= NULL
) dfilter_free(rfcode
);
526 cfile
.rfcode
= rfcode
;
528 switch (cf_read(&cfile
, FALSE
)) {
533 *err_str
= "problem while reading file";
540 static funnel_progress_window_t
* funnel_new_progress_window(const gchar
* label
, const gchar
* task
, gboolean terminate_is_stop
, gboolean
*stop_flag
) {
541 return (funnel_progress_window_t
*)create_progress_dlg(top_level
, label
, task
, terminate_is_stop
, stop_flag
);
544 static void funnel_update_progress(funnel_progress_window_t
* win
, float pr
, const gchar
* task
) {
545 update_progress_dlg((progdlg_t
*)win
, pr
, task
);
548 static void funnel_destroy_progress_window(funnel_progress_window_t
* win
) {
549 destroy_progress_dlg((progdlg_t
*)win
);
552 static void funnel_reload(void) {
553 if (cfile
.state
== FILE_READ_DONE
) cf_reload(&cfile
);
556 static const funnel_ops_t funnel_ops
= {
558 text_window_set_text
,
562 text_window_get_text
,
563 text_window_set_close_cb
,
564 text_window_set_editable
,
566 text_window_add_button
,
570 funnel_retap_packets
,
574 funnel_set_color_filter_slot
,
579 browser_open_data_file
,
580 funnel_new_progress_window
,
581 funnel_update_progress
,
582 funnel_destroy_progress_window
586 typedef struct _menu_cb_t
{
587 void (*callback
)(gpointer
);
592 static void our_menu_callback(void* unused _U_
, gpointer data
) {
593 menu_cb_t
* mcb
= (menu_cb_t
*)data
;
594 mcb
->callback(mcb
->callback_data
);
595 if (mcb
->retap
) cf_retap_packets(&cfile
);
598 static void register_menu_cb(const char *name
,
599 register_stat_group_t group
,
600 void (*callback
)(gpointer
),
601 gpointer callback_data
,
604 menu_cb_t
* mcb
= (menu_cb_t
*)g_malloc(sizeof(menu_cb_t
));
605 const char *label
= NULL
, *str
= NULL
;
607 mcb
->callback
= callback
;
608 mcb
->callback_data
= callback_data
;
611 str
= strrchr(name
,'/');
618 register_menu_bar_menu_items(
619 stat_group_name(group
), /* GUI path to the place holder in the menu */
620 name
, /* Action name */
623 NULL
, /* Accelerator */
625 our_menu_callback
, /* Callback */
626 mcb
, /* callback data */
632 void initialize_funnel_ops(void) {
633 funnel_set_funnel_ops(&funnel_ops
);
637 register_tap_listener_gtkfunnel(void)
639 funnel_register_all_menus(register_menu_cb
);