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