Introduce utility function to ease GTK3 depcrecated funktion removal.
[metze/wireshark/wip.git] / ui / gtk / gui_utils.c
1 /* gui_utils.c
2  * UI utility routines, some GTK+-specific (declared in gtk/gui_utils.h)
3  * and some with GUI-independent APIs, with this file containing the GTK+
4  * implementations of them (declared in ui_util.h)
5  *
6  * $Id$
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <string.h>
32 #include <locale.h>
33
34 #include <gtk/gtk.h>
35 #include <gdk/gdkkeysyms.h>
36 #if GTK_CHECK_VERSION(3,0,0)
37 # include <gdk/gdkkeysyms-compat.h>
38 #endif
39
40 #include <epan/prefs.h>
41 #include "epan/epan.h"
42
43 #include <epan/packet_info.h>
44
45 #include "ui/recent.h"
46 #include "ui/ui_util.h"
47
48 #include <wsutil/file_util.h>
49
50 #include "ui/gtk/gtkglobals.h"
51 #include "ui/gtk/gui_utils.h"
52 #include "ui/gtk/font_utils.h"
53
54 #include "ui/gtk/old-gtk-compat.h"
55
56 #include "image/wsicon16.xpm"
57 #include "image/wsicon32.xpm"
58 #include "image/wsicon48.xpm"
59 #include "image/wsicon64.xpm"
60
61 #include "../version_info.h"
62
63 #ifdef _WIN32
64 #include <windows.h>
65 #endif
66
67 #define WINDOW_GEOM_KEY "window_geom"
68
69
70 /* load the geometry values for a window from previously saved values */
71 static gboolean window_geom_load(const gchar *name, window_geometry_t *geom);
72
73
74
75 /* Set our window icon.  The GDK documentation doesn't provide any
76    actual documentation for gdk_window_set_icon(), so we'll steal
77    libgimp/gimpdialog.c:gimp_dialog_realize_callback() from the Gimp
78    sources and assume it's safe.
79
80    XXX - The current icon size is fixed at 16x16 pixels, which looks fine
81    with kwm (KDE 1.x's window manager), Sawfish (the "default" window
82    manager for GNOME?), and under Windows with Exceed putting X windows
83    on the Windows desktop, using Exceed as the window manager, as those
84    window managers put a 16x16 icon on the title bar.
85
86    The window managers in some windowing environments (e.g. dtwm in CDE)
87    and some stand-alone window managers have larger icon sizes (many window
88    managers put the window icon on the desktop, in the Windows 3.x style,
89    rather than in the titlebar, in the Windows 4.x style), so we need to
90    find a way to size our icon appropriately.
91
92    The X11 Inter-Client Communications Conventions Manual, Version 1.1,
93    in X11R5, specifies that "a window manager that wishes to place
94    constraints on the sizes of icon pixmaps and/or windows should
95    place a property called WM_ICON_SIZE on the root"; that property
96    contains minimum width and height, maximum width and height, and
97    width and height increment values.  "XGetIconSizes()" retrieves
98    that property; unfortunately, I've yet to find a window manager
99    that sets it on the root window (kwm, AfterStep, and Exceed don't
100    appear to set it).
101
102    The X Desktop Group's Window Manager Standard specifies, in the section
103    on Application Window Properties, an _NET_WM_ICON property, presumably
104    set by the window manager, which is an array of possible icon sizes
105    for the client.  There's no API in GTK+ 1.2[.x] for this; there may
106    eventually be one either in GTK+ 2.0 or GNOME 2.0.
107
108    Some window managers can be configured to take the window name
109    specified by the WM_NAME property of a window or the resource
110    or class name specified by the WM_CLASS property and base the
111    choice of icon for the window on one of those; WM_CLASS for
112    Wireshark's windows has a resource name of "wireshark" and a class
113    name of "Wireshark".  However, the way that's done is window-manager-
114    specific, and there's no way to determine what size a particular
115    window manager would want, so there's no way to automate this as
116    part of the installation of Wireshark.
117    */
118 static void
119 window_icon_realize_cb (GtkWidget *win, gpointer data _U_)
120 {
121 #ifndef _WIN32
122     GList            *ws_icon_list=NULL;
123     GdkPixbuf        *icon;
124
125
126         icon = gdk_pixbuf_new_from_xpm_data ((const char **) wsicon16_xpm);
127         ws_icon_list = g_list_append (ws_icon_list, icon);
128         icon = gdk_pixbuf_new_from_xpm_data ((const char **) wsicon32_xpm);
129         ws_icon_list = g_list_append (ws_icon_list, icon);
130         icon = gdk_pixbuf_new_from_xpm_data ((const char **) wsicon48_xpm);
131         ws_icon_list = g_list_append (ws_icon_list, icon);
132         icon = gdk_pixbuf_new_from_xpm_data ((const char **) wsicon64_xpm);
133         ws_icon_list = g_list_append (ws_icon_list, icon);
134         gtk_window_set_icon_list(GTK_WINDOW(win), ws_icon_list);
135         /* set icon by name, this allows us to use even SVG icon if it is present */
136         gtk_window_set_icon_name(GTK_WINDOW(win), "wireshark");
137
138
139 #endif
140 }
141
142
143 /* Create a new window, of the specified type, with the specified title
144    (if any) and the Wireshark icon. */
145 GtkWidget *
146 window_new(GtkWindowType type, const gchar *title)
147 {
148     GtkWidget *win;
149
150     win = gtk_window_new(type);
151     if (title != NULL)
152         gtk_window_set_title(GTK_WINDOW(win), title);
153     g_signal_connect(win, "realize", G_CALLBACK(window_icon_realize_cb), NULL);
154
155     /* XXX - which one is the correct default policy? or use a preference for this? */
156     /* GTK_WIN_POS_NONE, GTK_WIN_POS_CENTER or GTK_WIN_POS_MOUSE */
157     /* a lot of people dislike GTK_WIN_POS_MOUSE */
158
159     /* set the initial position (must be done, before show is called!) */
160 /*  gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER_ON_PARENT);*/
161     gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_NONE);
162
163     return win;
164 }
165
166
167 /* Same as window_new(), but will keep its geometry values (size, position, ...).
168  * Be sure to use window_present() and window_destroy() appropriately! */
169 GtkWidget *
170 window_new_with_geom(GtkWindowType type, const gchar *title, const gchar *geom_name)
171 {
172     window_geometry_t geom;
173     GtkWidget *win = window_new(type, title);
174
175     g_object_set_data(G_OBJECT(win), WINDOW_GEOM_KEY, (gpointer)g_strdup(geom_name));
176
177     /* do we have a previously saved size and position of this window? */
178     if(geom_name) {
179         /* It's a good idea to set the position and size of the window already here,
180          * as it's still invisible and won't "flicker the screen" while initially resizing. */
181         if(window_geom_load(geom_name, &geom)) {
182             /* XXX - use prefs to select which values to set? */
183             geom.set_pos        = TRUE;
184             geom.set_size       = TRUE;
185             geom.set_maximized  = FALSE;  /* don't maximize until window is shown */
186             window_set_geometry(win, &geom);
187         }
188     }
189
190     return win;
191 }
192
193
194 /* Create a new window for a splash screen; it's a main window, without decoration,
195    positioned in the center of the screen. */
196 GtkWidget *
197 splash_window_new(void)
198 {
199     GtkWidget *win;
200
201     win = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark");
202     gtk_window_set_decorated(GTK_WINDOW(win), FALSE);
203
204     /* set the initial position (must be done, before show is called!) */
205     gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER);
206
207     return win;
208 }
209
210
211 /* Present the created window on the screen. */
212 void
213 window_present(GtkWidget *win)
214 {
215     window_geometry_t geom;
216     const gchar *name;
217
218     /* present this window */
219     gtk_window_present(GTK_WINDOW(win));
220
221     /* do we have a previously saved size and position of this window? */
222     name = g_object_get_data(G_OBJECT(win), WINDOW_GEOM_KEY);
223     if(name) {
224         if(window_geom_load(name, &geom)) {
225             /* XXX - use prefs to select which values to set? */
226             geom.set_pos        = TRUE;
227             geom.set_size       = TRUE;
228             geom.set_maximized  = TRUE;
229             window_set_geometry(win, &geom);
230         }
231     }
232 }
233
234
235 static gboolean
236 window_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer cancel_button)
237 {
238     g_return_val_if_fail (widget != NULL, FALSE);
239     g_return_val_if_fail (event != NULL, FALSE);
240
241     if (event->keyval == GDK_Escape) {
242         gtk_widget_activate(GTK_WIDGET(cancel_button));
243         return TRUE;
244     }
245
246     return FALSE;
247 }
248
249
250 /* Set the "key_press_event" signal for a top-level dialog window to
251    call a routine to activate the "Cancel" button for a dialog box if
252    the key being pressed is the <Esc> key.
253
254    XXX - there should be a GTK+ widget that'll do that for you, and
255    let you specify a "Cancel" button.  It should also not impose
256    a requirement that there be a separator in the dialog box, as
257    the GtkDialog widget does; the visual convention that there's
258    such a separator between the rest of the dialog boxes and buttons
259    such as "OK" and "Cancel" is, for better or worse, not universal
260    (not even in GTK+ - look at the GtkFileSelection dialog!). */
261 static void
262 window_set_cancel(GtkWidget *widget, GtkWidget *cancel_button)
263 {
264     g_signal_connect(widget, "key_press_event", G_CALLBACK(window_key_press_cb), cancel_button);
265 }
266
267
268 /* set the actions needed for the cancel "Close"/"Ok"/"Cancel" button that closes the window */
269 void window_set_cancel_button(GtkWidget *win, GtkWidget *bt, window_cancel_button_fct cb)
270 {
271     if(cb)
272         g_signal_connect(bt, "clicked", G_CALLBACK(cb), win);
273
274     gtk_widget_grab_default(bt);
275
276     window_set_cancel(win, bt);
277 }
278
279
280 /* default callback handler for cancel button "clicked" signal */
281 void window_cancel_button_cb(GtkWidget *w _U_, gpointer data)
282 {
283     window_destroy(GTK_WIDGET(data));
284 }
285
286
287 /* default callback handler: the window managers X of the window was clicked (delete_event) */
288 gboolean
289 window_delete_event_cb(GtkWidget *win, GdkEvent *event _U_, gpointer user_data _U_)
290 {
291     window_destroy(win);
292
293     /* event handled, don't do anything else */
294     return TRUE;
295 }
296
297
298 /* get the geometry of a window from window_new() */
299 void
300 window_get_geometry(GtkWidget *widget, window_geometry_t *geom)
301 {
302     GdkWindowState state;
303     GdkWindow *widget_window;
304
305     /* Try to grab our geometry.
306
307        GTK+ provides two routines to get a window's position relative
308        to the X root window.  If I understand the documentation correctly,
309        gdk_window_get_deskrelative_origin applies mainly to Enlightenment
310        and gdk_window_get_root_origin applies for all other WMs.
311
312        The code below tries both routines, and picks the one that returns
313        the upper-left-most coordinates.
314
315        More info at:
316
317         http://mail.gnome.org/archives/gtk-devel-list/2001-March/msg00289.html
318         http://www.gtk.org/faq/#AEN606
319
320                 As gdk_window_get_deskrelative_origin() is deprecated it has been removed 2011-07-24.
321      */
322
323     memset (geom, 0, sizeof (window_geometry_t));
324
325     widget_window = gtk_widget_get_window(widget);
326
327     gdk_window_get_root_origin(widget_window,
328         &geom->x,
329         &geom->y);
330
331     /* XXX - Is this the "approved" method? */
332 #if GTK_CHECK_VERSION(2,24,0)
333         geom->width = gdk_window_get_width(widget_window);
334         geom->height = gdk_window_get_height (widget_window);
335 #else
336     gdk_drawable_get_size(widget_window,
337         &geom->width,
338         &geom->height);
339 #endif
340     state = gdk_window_get_state(widget_window);
341     geom->maximized = (state == GDK_WINDOW_STATE_MAXIMIZED);
342 }
343
344
345 /* set the geometry of a window from window_new() */
346 void
347 window_set_geometry(GtkWidget *widget, window_geometry_t *geom)
348 {
349         GdkScreen *default_screen;
350         GdkRectangle viewable_area;
351         gint monitor_num;
352
353     /* as we now have the geometry from the recent file, set it */
354     /* if the window was minimized, x and y are -32000 (at least on Win32) */
355     if (geom->set_pos && geom->x != -32000 && geom->y != -32000) {
356                 /* Per Wireshark bug #553, GTK has a problem on MS Windows
357                  * where the upper-left corner of the window may appear off
358                  * screen when when a single desktop spans multiple monitors
359                  * of different resolutions and positions relative to each
360                  * other.
361                  *
362                  * If the requested (x,y) position isn't within the monitor's
363                  * viewable area, change it to the viewable area's (0,0). */
364
365                 default_screen = gdk_screen_get_default();
366                 monitor_num = gdk_screen_get_monitor_at_point(default_screen,
367                                                               geom->x, geom->y);
368                 gdk_screen_get_monitor_geometry(default_screen, monitor_num,
369                                                 &viewable_area);
370
371                 if(geom->x < viewable_area.x || geom->x > viewable_area.width)
372                         geom->x = viewable_area.x;
373
374                 if(geom->y < viewable_area.y || geom->y > viewable_area.height)
375                         geom->y = viewable_area.y;
376
377         gtk_window_move(GTK_WINDOW(widget),
378                         geom->x,
379                         geom->y);
380     }
381
382     if (geom->set_size) {
383         gtk_window_resize(GTK_WINDOW(widget),
384         /*gtk_widget_set_size_request(widget,*/
385                                 geom->width,
386                                 geom->height);
387     }
388
389     if(geom->set_maximized) {
390         if (geom->maximized) {
391             gdk_window_maximize(gtk_widget_get_window(widget));
392         } else {
393             gdk_window_unmaximize(gtk_widget_get_window(widget));
394         }
395     }
396 }
397
398
399 /* the geometry hashtable for all known window classes,
400  * the window name is the key, and the geometry struct is the value */
401 static GHashTable *window_geom_hash = NULL;
402
403
404 /* save the window and it's current geometry into the geometry hashtable */
405 static void
406 window_geom_save(const gchar *name, window_geometry_t *geom)
407 {
408     gchar *key;
409     window_geometry_t *work;
410
411     /* init hashtable, if not already done */
412     if(!window_geom_hash) {
413         window_geom_hash = g_hash_table_new (g_str_hash, g_str_equal);
414     }
415     /* if we have an old one, remove and free it first */
416     work = g_hash_table_lookup(window_geom_hash, name);
417     if(work) {
418         g_hash_table_remove(window_geom_hash, name);
419         g_free(work->key);
420         g_free(work);
421     }
422
423     /* g_malloc and insert the new one */
424     work = g_malloc(sizeof(*geom));
425     *work = *geom;
426     key = g_strdup(name);
427     work->key = key;
428     g_hash_table_insert(window_geom_hash, key, work);
429 }
430
431
432 /* load the desired geometry for this window from the geometry hashtable */
433 static gboolean
434 window_geom_load(const gchar *name, window_geometry_t *geom)
435 {
436     window_geometry_t *p;
437
438     /* init hashtable, if not already done */
439     if(!window_geom_hash) {
440         window_geom_hash = g_hash_table_new (g_str_hash, g_str_equal);
441     }
442
443     p = g_hash_table_lookup(window_geom_hash, name);
444     if(p) {
445         *geom = *p;
446         return TRUE;
447     } else {
448         return FALSE;
449     }
450 }
451
452
453 /* read in a single key value pair from the recent file into the geometry hashtable */
454 void
455 window_geom_recent_read_pair(const char *name, const char *key, const char *value)
456 {
457     window_geometry_t geom;
458
459
460     /* find window geometry maybe already in hashtable */
461     if(!window_geom_load(name, &geom)) {
462         /* not in table, init geom with "basic" values */
463         geom.key        = NULL;    /* Will be set in window_geom_save () */
464         geom.set_pos    = FALSE;
465         geom.x          = -1;
466         geom.y          = -1;
467         geom.set_size   = FALSE;
468         geom.width      = -1;
469         geom.height     = -1;
470
471         geom.set_maximized = FALSE;/* this is valid in GTK2 only */
472         geom.maximized  = FALSE;   /* this is valid in GTK2 only */
473     }
474
475     if (strcmp(key, "x") == 0) {
476         geom.x = strtol(value, NULL, 10);
477         geom.set_pos = TRUE;
478     } else if (strcmp(key, "y") == 0) {
479         geom.y = strtol(value, NULL, 10);
480         geom.set_pos = TRUE;
481     } else if (strcmp(key, "width") == 0) {
482         geom.width = strtol(value, NULL, 10);
483         geom.set_size = TRUE;
484     } else if (strcmp(key, "height") == 0) {
485         geom.height = strtol(value, NULL, 10);
486         geom.set_size = TRUE;
487     } else if (strcmp(key, "maximized") == 0) {
488         if (g_ascii_strcasecmp(value, "true") == 0) {
489             geom.maximized = TRUE;
490         }
491         else {
492             geom.maximized = FALSE;
493         }
494         geom.set_maximized = TRUE;
495     } else {
496         /*
497          * Silently ignore the bogus key.  We shouldn't abort here,
498          * as this could be due to a corrupt recent file.
499          *
500          * XXX - should we print a message about this?
501          */
502         return;
503     }
504
505     /* save / replace geometry in hashtable */
506     window_geom_save(name, &geom);
507 }
508
509
510 /* write all geometry values of all windows from the hashtable to the recent file */
511 void
512 window_geom_recent_write_all(gpointer rf)
513 {
514     /* init hashtable, if not already done */
515     if(!window_geom_hash) {
516         window_geom_hash = g_hash_table_new (g_str_hash, g_str_equal);
517     }
518
519     g_hash_table_foreach(window_geom_hash, write_recent_geom, rf);
520 }
521
522
523 void
524 window_destroy(GtkWidget *win)
525 {
526     window_geometry_t geom;
527     const gchar *name;
528
529     if (!win)
530         return;
531
532     /* get_geometry must be done *before* destroy is running, as the window geometry
533      * cannot be retrieved at destroy time (so don't use event "destroy" for this) */
534     /* ...and don't do this at all, if we currently have no GdkWindow (e.g. if the
535      * GtkWidget is hidden) */
536     if(gtk_widget_get_has_window (win) && gtk_widget_get_visible(win)) {
537         window_get_geometry(win, &geom);
538
539         name = g_object_get_data(G_OBJECT(win), WINDOW_GEOM_KEY);
540         if(name) {
541             window_geom_save(name, &geom);
542             g_free((gpointer)name);
543         }
544     }
545
546     gtk_widget_destroy(win);
547 }
548
549 #if 0
550 /* Do we need this one ? */
551 /* convert an xpm to a GtkWidget, using the window settings from it's parent */
552 /* (be sure that the parent window is already being displayed) */
553 GtkWidget *xpm_to_widget_from_parent(GtkWidget *parent, const char ** xpm) {
554     GdkPixbuf * pixbuf;
555     GdkPixmap * pixmap;
556     GdkBitmap * bitmap;
557
558
559     pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
560     gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf, gtk_widget_get_colormap(parent), &pixmap, &bitmap, 128);
561
562     return gtk_image_new_from_pixmap (pixmap, bitmap);
563 }
564 #endif
565
566 /* convert an xpm to a GtkWidget */
567 GtkWidget *xpm_to_widget(const char ** xpm) {
568     GdkPixbuf *pixbuf;
569
570     pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
571     return gtk_image_new_from_pixbuf(pixbuf);
572 }
573
574 /* Convert an pixbuf data to a GtkWidget */
575 /* Data should be created with "gdk-pixbuf-csource --raw" */
576 GtkWidget *pixbuf_to_widget(const char * pb_data) {
577     GdkPixbuf *pixbuf;
578
579     pixbuf = gdk_pixbuf_new_from_inline (-1, pb_data, FALSE, NULL);
580     return gtk_image_new_from_pixbuf(pixbuf);
581 }
582
583 /*
584  * Key to attach the "un-decorated" title to the window, so that if the
585  * user-specified decoration changes, we can correctly update the
586  * window title.
587  */
588 #define MAIN_WINDOW_NAME_KEY  "main_window_name"
589
590 /* Set the name of the top level main_window_name with the specified string and call
591    update_main_window_title() to construct the full title and display it in the main window
592    and its icon title. */
593 void
594 set_main_window_name(const gchar *window_name)
595 {
596     gchar *old_window_name;
597
598     /* Attach the new un-decorated window name to the window. */
599     old_window_name = g_object_get_data(G_OBJECT(top_level), MAIN_WINDOW_NAME_KEY);
600     g_free(old_window_name);
601     g_object_set_data(G_OBJECT(top_level), MAIN_WINDOW_NAME_KEY, g_strdup(window_name));
602
603     update_main_window_title();
604 }
605
606 /* Construct the main window's title with the current main_window_name, optionally appended
607    with the user-specified title and/or wireshark version. Display the result in the main
608    window title bar and in its icon title.
609    NOTE: The name was changed from '_name' to '_title' because main_window_name is actually
610          set in set_main_window_name() and is only one of the components of the title. */
611 void
612 update_main_window_title(void)
613 {
614     gchar *window_name;
615     gchar *title;
616
617     /* Get the current filename or other title set in set_main_window_name */
618     window_name = g_object_get_data(G_OBJECT(top_level), MAIN_WINDOW_NAME_KEY);
619     if (window_name != NULL) {
620         /* Optionally append the user-defined window title */
621         title = create_user_window_title(window_name);
622
623         /* Optionally append the version */
624         if (prefs.gui_version_in_start_page) {
625             gchar *old_title = title;
626             title = g_strdup_printf("%s   [Wireshark %s %s]", title, VERSION, wireshark_svnversion);
627             g_free(old_title);
628         }
629         gtk_window_set_title(GTK_WINDOW(top_level), title);
630         g_free(title);
631     }
632 }
633
634 /* update the main window */
635 void main_window_update(void)
636 {
637     while (gtk_events_pending()) gtk_main_iteration();
638 }
639
640 /* exit the main window */
641 void main_window_exit(void)
642 {
643     exit(0);
644 }
645
646 #ifdef HAVE_LIBPCAP
647
648 /* quit a nested main window */
649 void main_window_nested_quit(void)
650 {
651     if (gtk_main_level() > 0)
652         gtk_main_quit();
653 }
654
655 /* quit the main window */
656 void main_window_quit(void)
657 {
658     gtk_main_quit();
659 }
660
661
662
663 typedef struct pipe_input_tag {
664     gint                source;
665     gpointer            user_data;
666     int                 *child_process;
667     pipe_input_cb_t     input_cb;
668     guint               pipe_input_id;
669 #ifdef _WIN32
670 #else
671     GIOChannel          *channel;
672 #endif
673 } pipe_input_t;
674
675
676 #ifdef _WIN32
677 /* The timer has expired, see if there's stuff to read from the pipe,
678    if so, do the callback */
679 static gboolean
680 pipe_timer_cb(gpointer data)
681 {
682     HANDLE handle;
683     DWORD avail = 0;
684     gboolean result, result1;
685     DWORD childstatus;
686     pipe_input_t *pipe_input = data;
687     gint iterations = 0;
688
689
690     /* try to read data from the pipe only 5 times, to avoid blocking */
691     while(iterations < 5) {
692         /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: new iteration");*/
693
694         /* Oddly enough although Named pipes don't work on win9x,
695            PeekNamedPipe does !!! */
696         handle = (HANDLE) _get_osfhandle (pipe_input->source);
697         result = PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL);
698
699         /* Get the child process exit status */
700         result1 = GetExitCodeProcess((HANDLE)*(pipe_input->child_process),
701                                      &childstatus);
702
703         /* If the Peek returned an error, or there are bytes to be read
704            or the childwatcher thread has terminated then call the normal
705            callback */
706         if (!result || avail > 0 || childstatus != STILL_ACTIVE) {
707
708             /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: data avail");*/
709
710             if(pipe_input->pipe_input_id != 0) {
711                 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: stop timer");*/
712                 /* avoid reentrancy problems and stack overflow */
713                 g_source_remove(pipe_input->pipe_input_id);
714                 pipe_input->pipe_input_id = 0;
715             }
716
717             /* And call the real handler */
718             if (!pipe_input->input_cb(pipe_input->source, pipe_input->user_data)) {
719                 g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: input pipe closed, iterations: %u", iterations);
720                 /* pipe closed, return false so that the old timer is not run again */
721                 return FALSE;
722             }
723         }
724         else {
725             /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: no data avail");*/
726             /* No data, stop now */
727             break;
728         }
729
730         iterations++;
731     }
732
733     if(pipe_input->pipe_input_id == 0) {
734         /* restore pipe handler */
735         pipe_input->pipe_input_id = g_timeout_add(200, pipe_timer_cb, data);
736         /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: finished with iterations: %u, new timer", iterations);*/
737
738         /* Return false so that the old timer is not run again */
739         return FALSE;
740     } else {
741         /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: finished with iterations: %u, old timer", iterations);*/
742
743         /* we didn't stopped the old timer, so let it run */
744         return TRUE;
745     }
746 }
747
748 #else /* _WIN32 */
749
750 /* There's stuff to read from the sync pipe, meaning the child has sent
751    us a message, or the sync pipe has closed, meaning the child has
752    closed it (perhaps because it exited). */
753 static gboolean
754 pipe_input_cb(GIOChannel *source _U_, GIOCondition condition _U_,
755                gpointer data)
756 {
757     pipe_input_t *pipe_input = data;
758
759
760     /* avoid reentrancy problems and stack overflow */
761     g_source_remove(pipe_input->pipe_input_id);
762
763     if (pipe_input->input_cb(pipe_input->source, pipe_input->user_data)) {
764         /* restore pipe handler */
765         pipe_input->pipe_input_id = g_io_add_watch_full (pipe_input->channel,
766                                                          G_PRIORITY_HIGH,
767                                                          G_IO_IN|G_IO_ERR|G_IO_HUP,
768                                                          pipe_input_cb,
769                                                          pipe_input,
770                                                          NULL);
771     }
772     return TRUE;
773 }
774 #endif
775
776 void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
777 {
778     static pipe_input_t pipe_input;
779
780     pipe_input.source        = source;
781     pipe_input.child_process = child_process;
782     pipe_input.user_data     = user_data;
783     pipe_input.input_cb      = input_cb;
784
785 #ifdef _WIN32
786     /* Tricky to use pipes in win9x, as no concept of wait.  NT can
787        do this but that doesn't cover all win32 platforms.  GTK can do
788        this but doesn't seem to work over processes.  Attempt to do
789        something similar here, start a timer and check for data on every
790        timeout. */
791        /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
792     pipe_input.pipe_input_id = g_timeout_add(200, pipe_timer_cb, &pipe_input);
793 #else
794     pipe_input.channel = g_io_channel_unix_new(source);
795     g_io_channel_set_encoding(pipe_input.channel, NULL, NULL);
796     pipe_input.pipe_input_id = g_io_add_watch_full(pipe_input.channel,
797                                                    G_PRIORITY_HIGH,
798                                                    G_IO_IN|G_IO_ERR|G_IO_HUP,
799                                                    pipe_input_cb,
800                                                    &pipe_input,
801                                                    NULL);
802 #endif
803 }
804
805
806 #endif /* HAVE_LIBPCAP */
807
808 /* Given a pointer to a GtkWidget for a top-level window, raise it and
809    de-iconify it.  This routine is used if the user has done something to
810    ask that a window of a certain type be popped up when there can be only
811    one such window and such a window has already been popped up - we
812    pop up the existing one rather than creating a new one.
813
814    XXX - we should request that it be given the input focus, too.  Alas,
815    GDK has nothing to do that, e.g. by calling "XSetInputFocus()" in a
816    window in X.  Besides, using "XSetInputFocus()" doesn't work anyway,
817    apparently due to the way GTK+/GDK manages the input focus.
818
819    The X Desktop Group's Window Manager Standard specifies, in the section
820    on Root Window Properties, an _NET_ACTIVE_WINDOW client message that
821    can be sent to the root window, containing the window ID of the
822    window to activate; I infer that this might be the way to give the
823    window the input focus - I assume that means it's also de-iconified,
824    but I wouldn't assume it'd raise it.
825
826    XXX - will this do the right thing on window systems other than X? */
827 void
828 reactivate_window(GtkWidget *win)
829 {
830     GdkWindow *win_window;
831
832     win_window = gtk_widget_get_window(win);
833
834     gdk_window_show(win_window);
835     gdk_window_raise(win_window);
836 }
837
838 /* List of all GtkScrolledWindows, so we can globally set the scrollbar
839    placement of all of them. */
840 static GList *scrolled_windows;
841
842 static void setup_scrolled_window(GtkWidget *scrollw);
843 static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
844 static void set_scrollbar_placement_scrollw(GtkWidget *scrollw);
845
846 /* Create a GtkScrolledWindow, set its scrollbar placement appropriately,
847    and remember it. */
848 GtkWidget *
849 scrolled_window_new(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment)
850 {
851     GtkWidget *scrollw;
852
853     scrollw = gtk_scrolled_window_new(hadjustment, vadjustment);
854     setup_scrolled_window(scrollw);
855     return scrollw;
856 }
857
858 /* Set a GtkScrolledWindow's scrollbar placement and add it to the list
859    of GtkScrolledWindows. */
860 static void
861 setup_scrolled_window(GtkWidget *scrollw)
862 {
863     set_scrollbar_placement_scrollw(scrollw);
864
865     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollw),
866                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
867
868     scrolled_windows = g_list_append(scrolled_windows, scrollw);
869
870     /* Catch the "destroy" event on the widget, so that we remove it from
871        the list when it's destroyed. */
872     g_signal_connect(scrollw, "destroy", G_CALLBACK(forget_scrolled_window), NULL);
873 }
874
875 /* Remove a GtkScrolledWindow from the list of GtkScrolledWindows. */
876 static void
877 forget_scrolled_window(GtkWidget *scrollw, gpointer data _U_)
878 {
879     scrolled_windows = g_list_remove(scrolled_windows, scrollw);
880 }
881
882 /* Set the scrollbar placement of a GtkScrolledWindow based upon user
883    preference. */
884 static void
885 set_scrollbar_placement_scrollw(GtkWidget *scrollw)
886 {
887     if (prefs.gui_scrollbar_on_right) {
888         gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
889                                           GTK_CORNER_TOP_LEFT);
890     } else {
891         gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
892                                           GTK_CORNER_TOP_RIGHT);
893     }
894 }
895
896 static void
897 set_scrollbar_placement_cb(gpointer data, gpointer user_data _U_)
898 {
899     set_scrollbar_placement_scrollw((GtkWidget *)data);
900 }
901
902 /* Set the scrollbar placement of all GtkScrolledWindows based on
903    user preference. */
904 void
905 set_scrollbar_placement_all(void)
906 {
907     g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, NULL);
908 }
909
910 /* List of all CTrees/TreeViews, so we can globally set the line and
911  * expander style of all of them. */
912 static GList *trees;
913
914 static void setup_tree(GtkWidget *tree);
915 static void forget_tree(GtkWidget *tree, gpointer data);
916 static void set_tree_styles(GtkWidget *tree);
917 static gboolean tree_view_key_pressed_cb(GtkWidget *tree, GdkEventKey *event, gpointer user_data _U_);
918
919 /* Create a Tree, give it the right styles, and remember it. */
920 GtkWidget *
921 tree_view_new(GtkTreeModel *model)
922 {
923     GtkWidget *tree;
924
925     tree = gtk_tree_view_new_with_model(model);
926     setup_tree(tree);
927     return tree;
928 }
929
930 /* Set a Tree's styles and add it to the list of Trees. */
931 static void
932 setup_tree(GtkWidget *tree)
933 {
934     set_tree_styles(tree);
935
936     trees = g_list_append(trees, tree);
937
938     /* Catch the "destroy" event on the widget, so that we remove it from
939        the list when it's destroyed. */
940     g_signal_connect(tree, "destroy", G_CALLBACK(forget_tree), NULL);
941     g_signal_connect(tree, "key-press-event", G_CALLBACK(tree_view_key_pressed_cb), NULL );
942 }
943
944 /* Remove a Tree from the list of Trees. */
945 static void
946 forget_tree(GtkWidget *tree, gpointer data _U_)
947 {
948     trees = g_list_remove(trees, tree);
949 }
950
951 /* Set the styles of a Tree based upon user preferences. */
952 static void
953 set_tree_styles(GtkWidget *tree)
954 {
955     g_assert(prefs.gui_altern_colors >= 0 && prefs.gui_altern_colors <= 1);
956     gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree),
957                                  prefs.gui_altern_colors);
958 }
959
960 static void
961 set_tree_styles_cb(gpointer data, gpointer user_data _U_)
962 {
963     set_tree_styles((GtkWidget *)data);
964 }
965
966 /* Set the styles of all Trees based upon style values. */
967 void
968 set_tree_styles_all(void)
969 {
970     g_list_foreach(trees, set_tree_styles_cb, NULL);
971 }
972
973 /* Move the currently-selected item in a list store up or down one position. */
974 gboolean
975 tree_view_list_store_move_selection(GtkTreeView *tree, gboolean move_up)
976 {
977     GtkTreeIter       from, to;
978     GtkTreeModel     *model;
979     GtkTreeSelection *sel;
980     GtkTreePath      *path_from, *path_to;
981
982     sel = gtk_tree_view_get_selection(tree);
983     if (! gtk_tree_selection_get_selected(sel, &model, &from)) {
984         return FALSE;
985     }
986
987     path_from = gtk_tree_model_get_path(model, &from);
988     if (!path_from) {
989         return FALSE;
990     }
991
992     path_to = gtk_tree_path_copy(path_from);
993     /* XXX - Why does one return void and the other return a gboolean? */
994     if (move_up) {
995         gtk_tree_path_prev(path_to);
996     } else {
997         gtk_tree_path_next(path_to);
998     }
999
1000     if (gtk_tree_path_compare(path_from, path_to) == 0) {
1001         gtk_tree_path_free(path_from);
1002         gtk_tree_path_free(path_to);
1003         return FALSE;
1004     }
1005
1006     gtk_tree_model_get_iter(model, &to, path_to);
1007     gtk_list_store_swap(GTK_LIST_STORE(model), &from, &to);
1008     gtk_tree_path_free(path_from);
1009     gtk_tree_path_free(path_to);
1010     return TRUE;
1011 }
1012
1013 /* Find the selected row number in a list store. */
1014 gint
1015 tree_view_list_store_get_selected_row(GtkTreeView *tree) {
1016     GtkTreeIter       iter;
1017     GtkTreeModel     *model;
1018     GtkTreeSelection *sel;
1019     GtkTreePath      *path;
1020     gchar            *path_str;
1021     gint              row;
1022
1023     sel = gtk_tree_view_get_selection(tree);
1024     if (! gtk_tree_selection_get_selected(sel, &model, &iter)) {
1025         return -1;
1026     }
1027
1028     path = gtk_tree_model_get_path(model, &iter);
1029     if (!path) {
1030         return FALSE;
1031     }
1032
1033     path_str = gtk_tree_path_to_string(path);
1034     gtk_tree_path_free(path);
1035
1036     row = (gint) strtol(path_str, NULL, 10);
1037     g_free(path_str);
1038
1039     return row;
1040 }
1041
1042 /* append a row to the simple list */
1043 /* use it like: simple_list_append(list, 0, "first", 1, "second", -1) */
1044 void
1045 simple_list_append(GtkWidget *list, ...)
1046 {
1047     va_list ap;
1048
1049     GtkTreeIter iter;
1050     GtkListStore *store;
1051
1052     va_start(ap, list);
1053     store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
1054     gtk_list_store_append(store, &iter);
1055     gtk_list_store_set_valist(store, &iter, ap);
1056     va_end(ap);
1057 }
1058
1059 /* create a simple list widget */
1060 GtkWidget *
1061 simple_list_new(gint cols, const gchar **titles) {
1062     GtkWidget *plugins_list;
1063     int i;
1064     GtkListStore *store;
1065     GtkCellRenderer *renderer;
1066     GtkTreeViewColumn *column;
1067
1068
1069     g_assert(cols <= 10);
1070     store = gtk_list_store_new(cols,
1071         G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
1072         G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
1073     plugins_list = tree_view_new(GTK_TREE_MODEL(store));
1074     g_object_unref(G_OBJECT(store));
1075     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(plugins_list), (titles != NULL));
1076     for(i=0; i<cols; i++) {
1077         renderer = gtk_cell_renderer_text_new();
1078         column = gtk_tree_view_column_new_with_attributes(titles ? titles[i] : "", renderer,
1079                                                           "text", i, NULL);
1080         gtk_tree_view_column_set_sort_column_id(column, i);
1081         gtk_tree_view_append_column(GTK_TREE_VIEW(plugins_list), column);
1082     }
1083
1084     return plugins_list;
1085 }
1086
1087 void render_as_url(GtkCellRenderer *cell)
1088 {
1089     g_object_set(cell, "foreground", "blue", NULL);
1090     g_object_set(cell, "foreground-set", TRUE, NULL);
1091
1092     g_object_set(cell, "underline", PANGO_UNDERLINE_SINGLE, NULL);
1093     g_object_set(cell, "underline-set", TRUE, NULL);
1094 }
1095
1096 void simple_list_url_col(GtkWidget *list, gint col)
1097 {
1098     GtkTreeViewColumn *ul_column;
1099     GList             *renderers_list;
1100     GtkCellRenderer   *ul_renderer;
1101
1102     /* make the column look like a link ... */
1103     ul_column = gtk_tree_view_get_column(GTK_TREE_VIEW(list), col);
1104
1105     renderers_list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(ul_column));
1106
1107     if(renderers_list != NULL) {
1108         /* it is simple list - there should be only one renderer */
1109         ul_renderer = (GtkCellRenderer*)renderers_list->data;
1110
1111         render_as_url(ul_renderer);
1112
1113         g_list_free(renderers_list);
1114     }
1115
1116 }
1117
1118 void
1119 copy_to_clipboard(GString *str)
1120 {
1121     GtkClipboard    *cb;
1122
1123     cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);     /* Get the default clipboard */
1124     gtk_clipboard_set_text(cb, str->str, -1);            /* Copy the byte data into the clipboard */
1125 }
1126
1127
1128 typedef struct _copy_binary_t {
1129     guint8* data;
1130     int len;
1131 } copy_binary_t;
1132
1133 static
1134 copy_binary_t* create_copy_binary_t(const guint8* data, int len)
1135 {
1136     copy_binary_t* copy_data;
1137
1138     g_assert(len > 0);
1139     copy_data = g_new(copy_binary_t,1);
1140     copy_data->data = g_new(guint8,len);
1141     copy_data->len = len;
1142     memcpy(copy_data->data,data,len * sizeof(guint8));
1143     return copy_data;
1144 }
1145
1146 static void destroy_copy_binary_t(copy_binary_t* copy_data) {
1147     g_free(copy_data->data);
1148     g_free(copy_data);
1149 }
1150
1151 static
1152 void copy_binary_free_cb(GtkClipboard *clipboard _U_, gpointer user_data_or_owner)
1153 {
1154     copy_binary_t* copy_data;
1155     copy_data = user_data_or_owner;
1156     destroy_copy_binary_t(copy_data);
1157 }
1158
1159 static
1160 void copy_binary_get_cb(GtkClipboard *clipboard _U_, GtkSelectionData *selection_data, guint info _U_, gpointer user_data_or_owner)
1161 {
1162     copy_binary_t* copy_data;
1163
1164     copy_data = user_data_or_owner;
1165
1166     /* Just do a dumb set as binary data */
1167     gtk_selection_data_set(selection_data, GDK_NONE, 8, copy_data->data, copy_data->len);
1168 }
1169
1170 void copy_binary_to_clipboard(const guint8* data_p, int len)
1171 {
1172     static GtkTargetEntry target_entry[] = {
1173          {"application/octet_stream", 0, 0}}; /* XXX - this not understood by most applications,
1174                                              * but can be pasted into the better hex editors - is
1175                                              * there something better that we can do?
1176                                              */
1177
1178     GtkClipboard    *cb;
1179     copy_binary_t* copy_data;
1180     gboolean ret;
1181
1182     if(len <= 0) {
1183         return; /* XXX would it be better to clear the clipboard? */
1184     }
1185     cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);     /* Get the default clipboard */
1186     copy_data = create_copy_binary_t(data_p,len);
1187
1188     ret = gtk_clipboard_set_with_data(cb,target_entry,1,
1189         copy_binary_get_cb, copy_binary_free_cb,copy_data);
1190
1191     if(!ret) {
1192         destroy_copy_binary_t(copy_data);
1193     }
1194 }
1195
1196 /*
1197  * Create a new window title string with user-defined title preference.
1198  * (Or ignore it if unspecified).
1199  */
1200 gchar *
1201 create_user_window_title(const gchar *caption)
1202 {
1203     /* fail-safe */
1204     if (caption == NULL)
1205         return g_strdup("");
1206
1207     /* no user-defined title specified */
1208     if ((prefs.gui_window_title == NULL) || (*prefs.gui_window_title == '\0'))
1209         return g_strdup(caption);
1210
1211     return g_strdup_printf("%s   [%s]", caption, prefs.gui_window_title);
1212 }
1213
1214 /* XXX move toggle_tree over from proto_draw.c to handle GTK+ 1 */
1215 /*
1216  * This callback is invoked when keyboard focus is within either
1217  * the packetlist view or the detail view.  The keystrokes processed
1218  * within this callback are attempting to modify the detail view.
1219  * Within the detail view we special case the Left Arrow, Backspace
1220  * and Enter keys depending on the state of the expander (if any)
1221  * for the item in focus.
1222  *
1223  * Returning FALSE allows processing of the original key_press_event
1224  * by other callbacks.  Left/Right scrolling of the packetlist
1225  * view and expanding/collapsing of the detail view lists is
1226  * handled by the default GtkTreeView key-press-event call back.
1227  *
1228  * XXX - Would an improved version of this callback test to see which
1229  * of the two GtkTreeView lists has focus?  Left/Right scrolling of
1230  * the packetlist is currently not optimal.  It will take several
1231  * right or left keypress events before the packetlist responds.
1232  * The problem appears to be that the focus is on a particular cell
1233  * within the highlighted row cell (like a spreadsheet).  Scrolling
1234  * of the view  right or left will not occur until the focus is
1235  * moved to a cell off the left or right edge of the packet list
1236  * view.  Also TAB/SHIFT-TAB events can move keyboard focus to
1237  * the packetlist header where there is currently visual hint
1238  * a header cell has focus.
1239  */
1240 static gboolean
1241 tree_view_key_pressed_cb(GtkWidget *tree, GdkEventKey *event, gpointer user_data _U_)
1242 {
1243     GtkTreeSelection* selection;
1244     GtkTreeIter iter;
1245     GtkTreeIter parent;
1246     GtkTreeModel* model;
1247     GtkTreePath* path;
1248     gboolean    expanded, expandable;
1249     int rc = FALSE;
1250
1251     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1252     if(!selection) {
1253         return FALSE;
1254     }
1255
1256     if(!gtk_tree_selection_get_selected (selection, &model, &iter)) {
1257         return FALSE;
1258     }
1259
1260     path = gtk_tree_model_get_path(model, &iter);
1261     if(!path) {
1262         return FALSE;
1263     }
1264
1265     /* Always FALSE when we're in the packet list (at least until we add sub-packets) */
1266     expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree), path);
1267     expandable = gtk_tree_model_iter_has_child(model, &iter);
1268
1269     switch (event->keyval) {
1270         case GDK_Left:
1271             if(expanded) {
1272                 /* Subtree is expanded. Collapse it. */
1273                 gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), path);
1274                 rc = TRUE;
1275                 break;
1276             }
1277             /* No break - fall through to jumping to the parent */
1278         case GDK_BackSpace:
1279             if (!expanded) {
1280                 /* subtree is already collapsed, jump to parent node */
1281                 if(! gtk_tree_model_iter_parent(model, &parent, &iter)) {
1282                     rc = FALSE;
1283                     break;
1284                 }
1285                 gtk_tree_path_free(path);
1286                 path = gtk_tree_model_get_path(model, &parent);
1287                 if(!path) {
1288                     return FALSE;
1289                 }
1290                 gtk_tree_view_set_cursor(GTK_TREE_VIEW(tree), path,
1291                                                  NULL /* focus_column */,
1292                                                  FALSE /* !start_editing */);
1293                 rc = TRUE;
1294                 break;
1295             }
1296             break;
1297         case GDK_Right:
1298             if (expandable) {
1299                 /* We have a subtree. Try to expand it. */
1300                 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, FALSE /* !open_all */);
1301                 rc = TRUE;
1302                 break;
1303             } else {
1304                 rc = FALSE;
1305                 break;
1306             }
1307         case GDK_Return:
1308         case GDK_KP_Enter:
1309             /* Reverse the current state. */
1310             if (expanded)
1311                 gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), path);
1312             else
1313                 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, FALSE /* !open_all */);
1314             rc = TRUE;
1315             break;
1316     }
1317
1318     if(path) {
1319         gtk_tree_path_free(path);
1320     }
1321     return rc;
1322 }
1323
1324 void
1325 switch_to_fixed_col(GtkTreeView *view)
1326 {
1327     gint size;
1328     GtkTreeViewColumn *column;
1329     GList             *columns, *list;
1330
1331     columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
1332     list = columns;
1333     while(columns) {
1334         column = columns->data;
1335         size = gtk_tree_view_column_get_width (column);
1336         gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_FIXED);
1337         if (size > gtk_tree_view_column_get_fixed_width(column))
1338             gtk_tree_view_column_set_fixed_width(column, size);
1339         columns = g_list_next(columns);
1340     }
1341     g_list_free(list);
1342
1343     gtk_tree_view_set_fixed_height_mode(view, TRUE);
1344 }
1345
1346 gint
1347 get_default_col_size(GtkWidget *view, const gchar *str)
1348 {
1349     PangoLayout *layout;
1350     gint col_width;
1351
1352     layout = gtk_widget_create_pango_layout(view, str);
1353     pango_layout_get_pixel_size(layout,
1354                                 &col_width, /* width */
1355                                 NULL); /* height */
1356     g_object_unref(G_OBJECT(layout));
1357     /* Add a single character's width to get some spacing between columns */
1358     return col_width + (pango_font_description_get_size(user_font_get_regular()) / PANGO_SCALE);
1359 }
1360
1361
1362 /*
1363  * This function can be called from gtk_tree_view_column_set_cell_data_func()
1364  * the user data must be the colum number.
1365  * Present floats with two decimals
1366  */
1367 void
1368 float_data_func (GtkTreeViewColumn *column _U_,
1369                  GtkCellRenderer   *renderer,
1370                  GtkTreeModel      *model,
1371                  GtkTreeIter       *iter,
1372                  gpointer           user_data)
1373 {
1374     gfloat  float_val;
1375     gchar   buf[20];
1376     char *savelocale;
1377
1378     /* the col to get data from is in userdata */
1379     gint float_col = GPOINTER_TO_INT(user_data);
1380
1381     gtk_tree_model_get(model, iter, float_col, &float_val, -1);
1382
1383     /* save the current locale */
1384     savelocale = setlocale(LC_NUMERIC, NULL);
1385     /* switch to "C" locale to avoid problems with localized decimal separators
1386      * in g_snprintf("%f") functions
1387      */
1388     setlocale(LC_NUMERIC, "C");
1389
1390     g_snprintf(buf, sizeof(buf), "%.2f", float_val);
1391     /* restore previous locale setting */
1392     setlocale(LC_NUMERIC, savelocale);
1393
1394     g_object_set(renderer, "text", buf, NULL);
1395 }
1396
1397 /*
1398  * This function can be called from gtk_tree_view_column_set_cell_data_func()
1399  * the user data must be the colum number.
1400  * Present value as hexadecimal.
1401  */
1402 void
1403 present_as_hex_func (GtkTreeViewColumn *column _U_,
1404                      GtkCellRenderer   *renderer,
1405                      GtkTreeModel      *model,
1406                      GtkTreeIter       *iter,
1407                      gpointer           user_data)
1408 {
1409     guint  val;
1410     gchar   buf[35];
1411
1412     /* the col to get data from is in userdata */
1413     gint col = GPOINTER_TO_INT(user_data);
1414
1415     gtk_tree_model_get(model, iter, col, &val, -1);
1416
1417     g_snprintf(buf, sizeof(buf), "0x%02x", val);
1418
1419     g_object_set(renderer, "text", buf, NULL);
1420 }
1421
1422 void
1423 u64_data_func (GtkTreeViewColumn *column _U_,
1424                GtkCellRenderer   *renderer,
1425                GtkTreeModel      *model,
1426                GtkTreeIter       *iter,
1427                gpointer           user_data)
1428 {
1429     guint64 val;
1430     int i = 0;
1431     gchar *bp;
1432     gchar   buf[35];
1433
1434     /* the col to get data from is in userdata */
1435     gint col = GPOINTER_TO_INT(user_data);
1436
1437     gtk_tree_model_get(model, iter, col, &val, -1);
1438
1439     bp = &buf[34];
1440     *bp = 0;
1441     do {
1442         *--bp = (gchar)(val % 10) +'0';
1443         if (!(++i % 3)) {
1444             *--bp = ' ';
1445         }
1446     } while ((val /= 10) != 0 && bp > buf);
1447     g_object_set(renderer, "text", bp, NULL);
1448 }
1449
1450 /*
1451  * This function can be called from gtk_tree_view_column_set_cell_data_func()
1452  * The user data must be the column number.
1453  * Renders the const static string whose pointer is stored.
1454  */
1455 void
1456 str_ptr_data_func (GtkTreeViewColumn *column _U_,
1457                    GtkCellRenderer   *renderer,
1458                    GtkTreeModel      *model,
1459                    GtkTreeIter       *iter,
1460                    gpointer           user_data)
1461 {
1462     const gchar *str = NULL;
1463
1464     /* The col to get data from is in userdata */
1465     gint data_column = GPOINTER_TO_INT(user_data);
1466
1467     gtk_tree_model_get(model, iter, data_column, &str, -1);
1468     /* XXX should we check that str is non NULL and print a warning or do assert? */
1469
1470     g_object_set(renderer, "text", str, NULL);
1471 }
1472
1473 gint
1474 str_ptr_sort_func(GtkTreeModel *model,
1475                   GtkTreeIter *a,
1476                   GtkTreeIter *b,
1477                   gpointer user_data)
1478 {
1479     const gchar *str_a = NULL;
1480     const gchar *str_b = NULL;
1481     gint ret = 0;
1482
1483     /* The col to get data from is in userdata */
1484     gint data_column = GPOINTER_TO_INT(user_data);
1485
1486     gtk_tree_model_get(model, a, data_column, &str_a, -1);
1487     gtk_tree_model_get(model, b, data_column, &str_b, -1);
1488
1489     if (str_a == str_b) {
1490         /* it's worth testing because a lot of rows point to the same data */
1491         return 0;
1492     }
1493     else if (str_a == NULL || str_b == NULL) {
1494         ret = (str_a == NULL) ? -1 : 1;
1495     }
1496     else {
1497         ret = g_ascii_strcasecmp(str_a,str_b);
1498     }
1499     return ret;
1500 }
1501
1502 /** --------------------------------------------------
1503  * ws_combo_box_text_and_pointer convenience functions
1504  *  (Code adapted from GtkComboBox.c)
1505  */
1506
1507 /**
1508  * ws_combo_box_new_text_and_pointer_full:
1509  *
1510  * Convenience function which constructs a new "text and pointer" combo box, which
1511  * is a #GtkComboBox just displaying strings and storing a pointer associated with
1512  * each combo_box entry; The pointer can be retrieved when an entry is selected.
1513  * Also: optionally returns the cell renderer for the combo box.
1514  * If you use this function to create a text_and_pointer combo_box,
1515  * you should only manipulate its data source with the
1516  * following convenience functions:
1517  *   ws_combo_box_append_text_and_pointer()
1518  *   ws_combo_box_append_text_and_pointer_full()
1519  *
1520  * @param cell_p  pointer to return the 'GtkCellRenderer *' for the combo box (or NULL).
1521  * @return A pointer to a new text_and_pointer combo_box.
1522  */
1523
1524 /* Note:
1525  * GtkComboBox style property: "appears-as-list":
1526  *   Default: 0: ie: displays as menus
1527  *   Wireshark Windows gtkrc: 1: ie: displays as lists (treeview)
1528  */
1529 GtkWidget *
1530 ws_combo_box_new_text_and_pointer_full(GtkCellRenderer **cell_p) {
1531     GtkWidget       *combo_box;
1532     GtkCellRenderer *cell;
1533     GtkTreeStore    *store;
1534
1535     /* The Tree store for the GtkComboBox has 3 columns:
1536        0: text string for display in GtkComboBox list;
1537        1: pointer (data) associated with the entry;
1538        2: True/False depending upon whether this entry is selectable ("sensitive" attribute).
1539     */
1540
1541     store = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
1542     combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL (store));
1543     g_object_unref(store);
1544     cell = gtk_cell_renderer_text_new();
1545     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), cell, TRUE);
1546     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell,
1547                                    "text", 0, "sensitive", 2,
1548                                    NULL);
1549     if (cell_p != NULL) {
1550         *cell_p = cell;
1551     }
1552     return combo_box;
1553 }
1554
1555 /**
1556  * ws_combo_box_new_text_and_pointer:
1557  *
1558  * Convenience function which constructs a new "text and pointer" combo box, which
1559  * is a #GtkComboBox just displaying strings and storing a pointer associated with
1560  * each combo_box entry; The pointer can be retrieved when an entry is selected.
1561  * If you use this function to create a text_and_pointer combo_box,
1562  * you should only manipulate its data source with the
1563  * following convenience functions:
1564  *   ws_combo_box_append_text_and_pointer()
1565  *   ws_combo_box_append_text_and_pointer_full()
1566  *
1567  * @return A pointer to a new text_and_pointer combo_box.
1568  */
1569
1570 GtkWidget *
1571 ws_combo_box_new_text_and_pointer(void) {
1572     return ws_combo_box_new_text_and_pointer_full(NULL);
1573 }
1574
1575
1576 /**
1577  * ws_combo_box_clear_text_and_pointer:
1578  * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1579  *
1580  * Clears all the text_and_pointer entries in the text_and_pointer combo_box.
1581  * Note: A "changed" signal will be emitted after the clear if there was
1582  * an active (selected) entry before the clear.
1583  * You should use this function only with combo boxes constructed with
1584  * ws_combo_box_new_text_and_pointer().
1585  */
1586 void
1587 ws_combo_box_clear_text_and_pointer(GtkComboBox *combo_box)
1588 {
1589     gtk_tree_store_clear(GTK_TREE_STORE(gtk_combo_box_get_model(combo_box)));
1590 }
1591
1592 /**
1593  * ws_combo_box_append_text_and_pointer_full:
1594  * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1595  * @param parent_iter Parent row for apending; NULL if appending to tree top-level;
1596  * @param text A string to be displayed as an entry in the dropdown list of the combo_box
1597  * @param ptr  A pointer to be associated with this entry of the combo_box
1598  * @param sensitive TRUE/FALSE to set sensitivity of the entry
1599  * @return A GtkTreeIter pointing to the appended GtkVomboBox entry.
1600  *
1601  * Appends text and ptr to the list of strings and pointers stored in combo_box.
1602  * The text and ptr can be appended to any existing level of the tree_store.
1603  * The sensitivity of the row will be set as requested.
1604  * Note that you can only use this function with combo boxes constructed with
1605  * ws_combo_box_new_text_and_pointer().
1606  */
1607 GtkTreeIter
1608 ws_combo_box_append_text_and_pointer_full(GtkComboBox    *combo_box,
1609                                           GtkTreeIter    *parent_iter,
1610                                           const gchar    *text,
1611                                           const gpointer  ptr,
1612                                           const gboolean  sensitive)
1613 {
1614     GtkTreeIter   iter;
1615     GtkTreeStore *store;
1616
1617     store = GTK_TREE_STORE(gtk_combo_box_get_model(combo_box));
1618
1619     gtk_tree_store_append(store, &iter, parent_iter);
1620     gtk_tree_store_set(store, &iter, 0, text, 1, ptr, 2, sensitive, -1);
1621
1622     return iter;
1623 }
1624
1625 /**
1626  * ws_combo_box_append_text_and_pointer:
1627  * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1628  * @param text A string to be displayed as an entry in the dropdown list of the combo_box
1629  * @param ptr  A pointer to be associated with this entry of the combo_box
1630  * @return A GtkTreeIter pointing to the appended GtkComboBox entry.
1631  *
1632  * Appends text and ptr to the list of strings and pointers stored in combo_box. Note that
1633  * you can only use this function with combo boxes constructed with
1634  * ws_combo_box_new_text_and_pointer().
1635  */
1636 GtkTreeIter
1637 ws_combo_box_append_text_and_pointer(GtkComboBox    *combo_box,
1638                                      const gchar    *text,
1639                                      const gpointer  ptr)
1640 {
1641     return ws_combo_box_append_text_and_pointer_full(combo_box, NULL, text, ptr, TRUE);
1642 }
1643
1644
1645 /**
1646  * ws_combo_box_get_active_pointer:
1647  * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1648  * @param ptr  A pointer to a location in which to store the pointer associated with the active entry
1649  * @return TRUE if an entry is selected (i.e: an active entry exists); FALSE otherwise
1650  *
1651  * You can only use this function with combo boxes constructed with
1652  * ws_combo_box_new_text_and_pointer().
1653  */
1654 gboolean
1655 ws_combo_box_get_active_pointer(GtkComboBox *combo_box, gpointer *ptr)
1656 {
1657     GtkTreeStore *store;
1658     GtkTreeIter iter;
1659
1660     *ptr = NULL;
1661
1662     if (gtk_combo_box_get_active_iter(combo_box, &iter)) {
1663         store = GTK_TREE_STORE(gtk_combo_box_get_model(combo_box));
1664         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1665                            1, ptr, -1);
1666         return TRUE;
1667     }
1668     return FALSE;
1669 }
1670
1671 /**
1672  * ws_combo_box_get_active:
1673  * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1674  * @return Index of the active entry; -1 if no entry is selected;
1675  *         Note: If the active item is not an immediate child of root of the tree then
1676  *          the index returned is that of the top-level for the acftive entry.
1677  */
1678 gint
1679 ws_combo_box_get_active(GtkComboBox *combo_box)
1680 {
1681     return gtk_combo_box_get_active(combo_box);
1682 }
1683
1684 /**
1685  * ws_combo_box_set_active:
1686  * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1687  * @param idx of the entry which is to be set as active (ie: selected).
1688  *        Index refers to the immediate children of the tree.
1689  */
1690 void
1691 ws_combo_box_set_active(GtkComboBox *combo_box, gint idx)
1692 {
1693     gtk_combo_box_set_active(combo_box, idx);
1694 }
1695
1696 /**
1697  * ws_combo_box_set_active_iter:
1698  * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1699  * @param iter of the entry which is to be set as active (ie: selected).
1700  */
1701 void
1702 ws_combo_box_set_active_iter(GtkComboBox *combo_box, GtkTreeIter *iter)
1703 {
1704     gtk_combo_box_set_active_iter(combo_box, iter);
1705 }
1706
1707
1708 /* Copy functions from GTK 3.0 to be used if GTK version is 2.22 or 2.24 to be able save Graphs to file */
1709 #if GTK_CHECK_VERSION(2,22,0)
1710 #if !GTK_CHECK_VERSION(3,0,0)
1711 static cairo_format_t
1712 gdk_cairo_format_for_content (cairo_content_t content)
1713 {
1714   switch (content)
1715     {
1716     case CAIRO_CONTENT_COLOR:
1717       return CAIRO_FORMAT_RGB24;
1718     case CAIRO_CONTENT_ALPHA:
1719       return CAIRO_FORMAT_A8;
1720     case CAIRO_CONTENT_COLOR_ALPHA:
1721     default:
1722       return CAIRO_FORMAT_ARGB32;
1723     }
1724 }
1725
1726 static cairo_surface_t *
1727 gdk_cairo_surface_coerce_to_image (cairo_surface_t *surface,
1728                                    cairo_content_t  content,
1729                                    int              src_x,
1730                                    int              src_y,
1731                                    int              width,
1732                                    int              height)
1733 {
1734   cairo_surface_t *copy;
1735   cairo_t *cr;
1736
1737   copy = cairo_image_surface_create (gdk_cairo_format_for_content (content),
1738                                      width,
1739                                      height);
1740
1741   cr = cairo_create (copy);
1742   cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
1743   cairo_set_source_surface (cr, surface, -src_x, -src_y);
1744   cairo_paint (cr);
1745   cairo_destroy (cr);
1746
1747   return copy;
1748 }
1749
1750 static void
1751 convert_alpha (guchar *dest_data,
1752                int     dest_stride,
1753                guchar *src_data,
1754                int     src_stride,
1755                int     src_x,
1756                int     src_y,
1757                int     width,
1758                int     height)
1759 {
1760   int x, y;
1761
1762   src_data += src_stride * src_y + src_x * 4;
1763
1764   for (y = 0; y < height; y++) {
1765     guint32 *src = (guint32 *) src_data;
1766
1767     for (x = 0; x < width; x++) {
1768       guint alpha = src[x] >> 24;
1769
1770       if (alpha == 0)
1771         {
1772           dest_data[x * 4 + 0] = 0;
1773           dest_data[x * 4 + 1] = 0;
1774           dest_data[x * 4 + 2] = 0;
1775         }
1776       else
1777         {
1778           dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
1779           dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >>  8) * 255 + alpha / 2) / alpha;
1780           dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >>  0) * 255 + alpha / 2) / alpha;
1781         }
1782       dest_data[x * 4 + 3] = alpha;
1783     }
1784
1785     src_data += src_stride;
1786     dest_data += dest_stride;
1787   }
1788 }
1789
1790 static void
1791 convert_no_alpha (guchar *dest_data,
1792                   int     dest_stride,
1793                   guchar *src_data,
1794                   int     src_stride,
1795                   int     src_x,
1796                   int     src_y,
1797                   int     width,
1798                   int     height)
1799 {
1800   int x, y;
1801
1802   src_data += src_stride * src_y + src_x * 4;
1803
1804   for (y = 0; y < height; y++) {
1805     guint32 *src = (guint32 *) src_data;
1806
1807     for (x = 0; x < width; x++) {
1808       dest_data[x * 3 + 0] = src[x] >> 16;
1809       dest_data[x * 3 + 1] = src[x] >>  8;
1810       dest_data[x * 3 + 2] = src[x];
1811     }
1812
1813     src_data += src_stride;
1814     dest_data += dest_stride;
1815   }
1816 }
1817
1818 /**
1819  * gdk_pixbuf_get_from_surface:
1820  * @surface: surface to copy from
1821  * @src_x: Source X coordinate within @surface
1822  * @src_y: Source Y coordinate within @surface
1823  * @width: Width in pixels of region to get
1824  * @height: Height in pixels of region to get
1825  *
1826  * Transfers image data from a #cairo_surface_t and converts it to an RGB(A)
1827  * representation inside a #GdkPixbuf. This allows you to efficiently read
1828  * individual pixels from cairo surfaces. For #GdkWindows, use
1829  * gdk_pixbuf_get_from_window() instead.
1830  *
1831  * This function will create an RGB pixbuf with 8 bits per channel.
1832  * The pixbuf will contain an alpha channel if the @surface contains one.
1833  *
1834  * Return value: (transfer full): A newly-created pixbuf with a reference
1835  *     count of 1, or %NULL on error
1836  */
1837 GdkPixbuf *
1838 gdk_pixbuf_get_from_surface  (cairo_surface_t *surface,
1839                               gint             src_x,
1840                               gint             src_y,
1841                               gint             width,
1842                               gint             height)
1843 {
1844   cairo_content_t content;
1845   GdkPixbuf *dest;
1846
1847   /* General sanity checks */
1848   g_return_val_if_fail (surface != NULL, NULL);
1849   g_return_val_if_fail (width > 0 && height > 0, NULL);
1850
1851   content = cairo_surface_get_content (surface) | CAIRO_CONTENT_COLOR;
1852   dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
1853                          !!(content & CAIRO_CONTENT_ALPHA),
1854                          8,
1855                          width, height);
1856
1857   surface = gdk_cairo_surface_coerce_to_image (surface, content,
1858                                                src_x, src_y,
1859                                                width, height);
1860   cairo_surface_flush (surface);
1861   if (cairo_surface_status (surface) || dest == NULL)
1862     {
1863       cairo_surface_destroy (surface);
1864       return NULL;
1865     }
1866
1867   if (gdk_pixbuf_get_has_alpha (dest))
1868     convert_alpha (gdk_pixbuf_get_pixels (dest),
1869                    gdk_pixbuf_get_rowstride (dest),
1870                    cairo_image_surface_get_data (surface),
1871                    cairo_image_surface_get_stride (surface),
1872                    0, 0,
1873                    width, height);
1874   else
1875     convert_no_alpha (gdk_pixbuf_get_pixels (dest),
1876                       gdk_pixbuf_get_rowstride (dest),
1877                       cairo_image_surface_get_data (surface),
1878                       cairo_image_surface_get_stride (surface),
1879                       0, 0,
1880                       width, height);
1881
1882   cairo_surface_destroy (surface);
1883   return dest;
1884 }
1885 #endif /* !GTK_CHECK_VERSION(3,0,0) */
1886 #endif /* GTK_CHECK_VERSION(2,22,0) */
1887
1888
1889 GtkWidget * ws_gtk_box_new(GtkOrientation orientation, gint spacing, gboolean homogeneous)
1890 {
1891 #if !GTK_CHECK_VERSION(3,0,0)
1892         if (orientation == GTK_ORIENTATION_HORIZONTAL){
1893                 if(homogeneous == TRUE){
1894                         return gtk_hbox_new(TRUE, spacing);
1895                 }else{
1896                         return gtk_hbox_new(FALSE, spacing);
1897                 }
1898         }else{
1899                 if(homogeneous == TRUE){
1900                         return gtk_vbox_new(TRUE, spacing);
1901                 }else{
1902                         return gtk_vbox_new(FALSE, spacing);
1903                 }
1904         }
1905 #else
1906         GtkWidget widget;
1907
1908         widget = gtk_box_new(rientation, spacing);
1909         gtk_box_set_homogeneous(GTK_BOX(widget), homogeneous);
1910 #endif /* GTK_CHECK_VERSION(3,0,0) */
1911 }