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