Add support for AirPcap, an upcoming wireless product from CACE. Support
[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
33 #ifdef _WIN32
34 #include <windows.h>
35 #endif
36
37
38 #include <gtk/gtk.h>
39 #include <gdk/gdkkeysyms.h>
40
41 #include "file_util.h"
42 #include "gtkglobals.h"
43 #include "gui_utils.h"
44 #include <epan/prefs.h>
45 #include "epan/epan.h"
46 #include "../ui_util.h"
47 #include "compat_macros.h"
48 #include "recent.h"
49
50
51 #include "image/wsicon16.xpm"
52
53 /* XXX - remove this later again, when dlg_xx function cleanup done */
54 #include "dlg_utils.h"
55
56
57 #define WINDOW_GEOM_KEY "window_geom"
58
59
60 /* load the geometry values for a window from previously saved values */
61 static gboolean window_geom_load(const gchar *name, window_geometry_t *geom);
62
63
64
65 /* Set our window icon.  The GDK documentation doesn't provide any
66    actual documentation for gdk_window_set_icon(), so we'll steal
67    libgimp/gimpdialog.c:gimp_dialog_realize_callback() from the Gimp
68    sources and assume it's safe.
69
70    XXX - The current icon size is fixed at 16x16 pixels, which looks fine
71    with kwm (KDE 1.x's window manager), Sawfish (the "default" window
72    manager for GNOME?), and under Windows with Exceed putting X windows
73    on the Windows desktop, using Exceed as the window manager, as those
74    window managers put a 16x16 icon on the title bar.
75
76    The window managers in some windowing environments (e.g. dtwm in CDE)
77    and some stand-alone window managers have larger icon sizes (many window
78    managers put the window icon on the desktop, in the Windows 3.x style,
79    rather than in the titlebar, in the Windows 4.x style), so we need to
80    find a way to size our icon appropriately.
81
82    The X11 Inter-Client Communications Conventions Manual, Version 1.1,
83    in X11R5, specifies that "a window manager that wishes to place
84    constraints on the sizes of icon pixmaps and/or windows should
85    place a property called WM_ICON_SIZE on the root"; that property
86    contains minimum width and height, maximum width and height, and
87    width and height increment values.  "XGetIconSizes()" retrieves
88    that property; unfortunately, I've yet to find a window manager
89    that sets it on the root window (kwm, AfterStep, and Exceed don't
90    appear to set it).
91
92    The X Desktop Group's Window Manager Standard specifies, in the section
93    on Application Window Properties, an _NET_WM_ICON property, presumably
94    set by the window manager, which is an array of possible icon sizes
95    for the client.  There's no API in GTK+ 1.2[.x] for this; there may
96    eventually be one either in GTK+ 2.0 or GNOME 2.0.
97
98    Some window managers can be configured to take the window name
99    specified by the WM_NAME property of a window or the resource
100    or class name specified by the WM_CLASS property and base the
101    choice of icon for the window on one of those; WM_CLASS for
102    Wireshark's windows has a resource name of "wireshark" and a class
103    name of "Wireshark".  However, the way that's done is window-manager-
104    specific, and there's no way to determine what size a particular
105    window manager would want, so there's no way to automate this as
106    part of the installation of Wireshark.
107    */
108 static void
109 window_icon_realize_cb (GtkWidget *win, gpointer data _U_)
110 {
111 #ifndef _WIN32
112   static GdkPixmap *icon_pmap = NULL;
113   static GdkBitmap *icon_mask = NULL;
114   GtkStyle         *style;
115
116   style = gtk_widget_get_style (win);
117
118   if (icon_pmap == NULL) {
119     icon_pmap = gdk_pixmap_create_from_xpm_d (win->window,
120                 &icon_mask, &style->bg[GTK_STATE_NORMAL],
121                 (gchar **) wsicon16_xpm);
122   }
123
124   gdk_window_set_icon (win->window, NULL, icon_pmap, icon_mask);
125 #endif
126 }
127
128
129 /* Create a new window, of the specified type, with the specified title
130    (if any) and the Wireshark icon. */
131 GtkWidget *
132 window_new(GtkWindowType type, const gchar *title)
133 {
134   GtkWidget *win;
135
136   win = gtk_window_new(type);
137   if (title != NULL)
138     gtk_window_set_title(GTK_WINDOW(win), title);
139   SIGNAL_CONNECT(win, "realize", window_icon_realize_cb, NULL);
140
141   /* XXX - which one is the correct default policy? or use a preference for this? */
142   /* GTK_WIN_POS_NONE, GTK_WIN_POS_CENTER or GTK_WIN_POS_MOUSE */
143   /* a lot of people dislike GTK_WIN_POS_MOUSE */
144
145   /* set the initial position (must be done, before show is called!) */
146 #if GTK_MAJOR_VERSION >= 2       
147 /*  gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER_ON_PARENT);*/
148 #else    
149 /*  gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER);*/
150 #endif
151   gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_NONE);
152
153 #if GTK_MAJOR_VERSION < 2
154   /* allow window to be shrinked by user, as gtk_widget_set_usize() will set minimum size and */
155   /* the user never could shrink the window again */
156   gtk_window_set_policy(GTK_WINDOW(win), TRUE, TRUE, FALSE);
157 #endif
158
159   return win;
160 }
161
162
163 /* Same as window_new(), but will keep it's geometry values (size, position, ...).
164  * Be sure to use window_present() and window_destroy() appropriately! */
165 GtkWidget *
166 window_new_with_geom(GtkWindowType type, const gchar *title, const gchar *geom_name)
167 {
168   window_geometry_t geom;
169   GtkWidget *win = window_new(type, title);
170
171   OBJECT_SET_DATA(win, WINDOW_GEOM_KEY, (gpointer)g_strdup(geom_name));
172
173   /* do we have a previously saved size and position of this window? */
174   if(geom_name) {
175     /* It's a good idea to set the position and size of the window already here,
176      * as it's still invisible and won't "flicker the screen" while initially resizing. */
177     if(window_geom_load(geom_name, &geom)) {
178       /* XXX - use prefs to select which values to set? */
179       geom.set_pos        = TRUE;
180       geom.set_size       = TRUE;
181       geom.set_maximized  = FALSE;  /* don't maximize until window is shown */
182       window_set_geometry(win, &geom);
183     }
184   }
185
186   return win;
187 }
188
189
190 #if GTK_MAJOR_VERSION < 2
191 /* We can't set the decorations until the window is realized. */
192 static void
193 window_notitle_realize_cb (GtkWidget *win, gpointer data _U_)
194 {
195   gdk_window_set_decorations(win->window, 0);
196 }
197 #endif
198
199
200 /* Create a new window for a splash screen; it's a main window, with no title,
201    positioned in the center of the screen. */
202 GtkWidget *
203 splash_window_new(void)
204 {
205   GtkWidget *win;
206
207   win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
208 #if GTK_MAJOR_VERSION >= 2
209   gtk_window_set_decorated(GTK_WINDOW(win), FALSE);
210 #else
211   SIGNAL_CONNECT(win, "realize", window_notitle_realize_cb, NULL);
212 #endif
213
214   /* set the initial position (must be done, before show is called!) */
215   gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER);
216
217   return win;
218 }
219
220
221 /* Present the created window on the screen. */
222 void
223 window_present(GtkWidget *win)
224 {
225   window_geometry_t geom;
226   const gchar *name;
227
228 #if GTK_MAJOR_VERSION >= 2
229   /* present this window */
230   gtk_window_present(GTK_WINDOW(win));
231 #endif
232
233   /* do we have a previously saved size and position of this window? */
234   name = OBJECT_GET_DATA(win, WINDOW_GEOM_KEY);
235   if(name) {
236     if(window_geom_load(name, &geom)) {
237       /* XXX - use prefs to select which values to set? */
238       geom.set_pos        = TRUE;
239       geom.set_size       = TRUE;
240       geom.set_maximized  = TRUE;
241       window_set_geometry(win, &geom);
242     }
243   }
244 }
245
246
247 static gint
248 window_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer cancel_button)
249 {
250   g_return_val_if_fail (widget != NULL, FALSE);
251   g_return_val_if_fail (event != NULL, FALSE);
252
253   if (event->keyval == GDK_Escape) {
254     gtk_widget_activate(GTK_WIDGET(cancel_button));
255     return TRUE;
256   }
257
258   return FALSE;
259 }
260
261
262 /* Set the "key_press_event" signal for a top-level dialog window to
263    call a routine to activate the "Cancel" button for a dialog box if
264    the key being pressed is the <Esc> key.
265
266    XXX - there should be a GTK+ widget that'll do that for you, and
267    let you specify a "Cancel" button.  It should also not impose
268    a requirement that there be a separator in the dialog box, as
269    the GtkDialog widget does; the visual convention that there's
270    such a separator between the rest of the dialog boxes and buttons
271    such as "OK" and "Cancel" is, for better or worse, not universal
272    (not even in GTK+ - look at the GtkFileSelection dialog!). */
273 static void
274 window_set_cancel(GtkWidget *widget, GtkWidget *cancel_button)
275 {
276   SIGNAL_CONNECT(widget, "key_press_event", window_key_press_cb, cancel_button);
277 }
278
279
280 /* set the actions needed for the cancel "Close"/"Ok"/"Cancel" button that closes the window */
281 void window_set_cancel_button(GtkWidget *win, GtkWidget *bt, window_cancel_button_fct cb)
282 {
283   if(cb)
284     SIGNAL_CONNECT(bt, "clicked", cb, win);
285
286   gtk_widget_grab_default(bt);
287
288   window_set_cancel(win, bt);
289 }
290
291
292 /* default callback handler for cancel button "clicked" signal */
293 void window_cancel_button_cb(GtkWidget *w _U_, gpointer data)
294 {
295   window_destroy(GTK_WIDGET(data));
296 }
297
298
299 /* default callback handler: the window managers X of the window was clicked (delete_event) */
300 gboolean
301 window_delete_event_cb(GtkWidget *win, GdkEvent *event _U_, gpointer user_data _U_)
302 {
303     window_destroy(win);
304
305     /* event handled, don't do anything else */
306     return TRUE;
307 }
308
309
310 /* get the geometry of a window from window_new() */
311 void
312 window_get_geometry(GtkWidget *widget, window_geometry_t *geom)
313 {
314         gint desk_x, desk_y;
315 #if GTK_MAJOR_VERSION >= 2
316     GdkWindowState state;
317 #endif
318
319         /* Try to grab our geometry.
320
321            GTK+ provides two routines to get a window's position relative
322            to the X root window.  If I understand the documentation correctly,
323            gdk_window_get_deskrelative_origin applies mainly to Enlightenment
324            and gdk_window_get_root_origin applies for all other WMs.
325
326            The code below tries both routines, and picks the one that returns
327            the upper-left-most coordinates.
328
329            More info at:
330
331         http://mail.gnome.org/archives/gtk-devel-list/2001-March/msg00289.html
332         http://www.gtk.org/faq/#AEN606
333     */
334
335         gdk_window_get_root_origin(widget->window, 
336         &geom->x, 
337         &geom->y);
338         if (gdk_window_get_deskrelative_origin(widget->window,
339                                 &desk_x, &desk_y)) {
340                 if (desk_x <= geom->x && 
341             desk_y <= geom->y)
342         {
343                         geom->x = desk_x;
344                         geom->y = desk_y;
345                 }
346         }
347
348         /* XXX - Is this the "approved" method? */
349         gdk_window_get_size(widget->window, 
350         &geom->width, 
351         &geom->height);
352
353 #if GTK_MAJOR_VERSION >= 2
354     state = gdk_window_get_state(widget->window);
355     geom->maximized = (state == GDK_WINDOW_STATE_MAXIMIZED);
356 #endif
357 }
358
359
360 /* set the geometry of a window from window_new() */
361 void
362 window_set_geometry(GtkWidget *widget, window_geometry_t *geom)
363 {
364     /* as we now have the geometry from the recent file, set it */
365     /* if the window was minimized, x and y are -32000 (at least on Win32) */
366     if (geom->set_pos && geom->x != -32000 && geom->y != -32000) {
367 #if GTK_MAJOR_VERSION >= 2
368         gtk_window_move(GTK_WINDOW(widget),
369                         geom->x,
370                         geom->y);
371 #else
372         gtk_widget_set_uposition(widget,
373                                  geom->x,
374                                  geom->y);
375 #endif
376     }
377
378     if (geom->set_size) {
379 #if GTK_MAJOR_VERSION >= 2
380         gtk_window_resize(GTK_WINDOW(widget),
381 #else
382         gtk_window_set_default_size(GTK_WINDOW(widget), 
383                                 geom->width, 
384                                 geom->height);
385         gtk_widget_set_usize(widget,
386 #endif
387         /*WIDGET_SET_SIZE(widget,*/
388                                 geom->width,
389                                 geom->height);
390     }
391
392 #if GTK_MAJOR_VERSION >= 2
393     if(geom->set_maximized) {
394         if (geom->maximized) {
395             gdk_window_maximize(widget->window);
396         } else {
397             gdk_window_unmaximize(widget->window);
398         }
399     }
400 #endif
401 }
402
403
404 /* the geometry hashtable for all known window classes,
405  * the window name is the key, and the geometry struct is the value */
406 GHashTable *window_geom_hash = NULL;
407
408
409 /* save the window and it's current geometry into the geometry hashtable */
410 static void
411 window_geom_save(const gchar *name, window_geometry_t *geom)
412 {
413     gchar *key;
414     window_geometry_t *work;
415
416     /* init hashtable, if not already done */
417     if(!window_geom_hash) {
418         window_geom_hash = g_hash_table_new (g_str_hash, g_str_equal);
419     }
420     /* if we have an old one, remove and free it first */
421     work = g_hash_table_lookup(window_geom_hash, name);
422     if(work) {
423         g_hash_table_remove(window_geom_hash, name);
424         g_free(work->key);
425         g_free(work);
426     }
427
428     /* g_malloc and insert the new one */
429     work = g_malloc(sizeof(*geom));
430     *work = *geom;
431     key = g_strdup(name);
432     work->key = key;
433     g_hash_table_insert(window_geom_hash, key, work);
434 }
435
436
437 /* load the desired geometry for this window from the geometry hashtable */
438 static gboolean
439 window_geom_load(const gchar *name, window_geometry_t *geom)
440 {
441     window_geometry_t *p;
442
443     /* init hashtable, if not already done */
444     if(!window_geom_hash) {
445         window_geom_hash = g_hash_table_new (g_str_hash, g_str_equal);
446     }
447
448     p = g_hash_table_lookup(window_geom_hash, name);
449     if(p) {
450         *geom = *p;
451         return TRUE;
452     } else {
453         return FALSE;
454     }
455 }
456
457
458 /* read in a single key value pair from the recent file into the geometry hashtable */
459 void
460 window_geom_recent_read_pair(const char *name, const char *key, const char *value)
461 {
462     window_geometry_t geom;
463
464
465     /* find window geometry maybe already in hashtable */
466     if(!window_geom_load(name, &geom)) {
467         /* not in table, init geom with "basic" values */
468         geom.key        = g_strdup(name);
469         geom.set_pos    = FALSE;
470         geom.x          = -1;
471         geom.y          = -1;
472         geom.set_size   = FALSE;
473         geom.width      = -1;
474         geom.height     = -1;
475
476         geom.set_maximized = FALSE;/* this is valid in GTK2 only */
477         geom.maximized  = FALSE;   /* this is valid in GTK2 only */
478     }
479     
480     if (strcmp(key, "x") == 0) {
481         geom.x = strtol(value, NULL, 10);
482         geom.set_pos = TRUE;
483     } else if (strcmp(key, "y") == 0) {
484         geom.y = strtol(value, NULL, 10);
485         geom.set_pos = TRUE;
486     } else if (strcmp(key, "width") == 0) {
487         geom.width = strtol(value, NULL, 10);
488         geom.set_size = TRUE;
489     } else if (strcmp(key, "height") == 0) {
490         geom.height = strtol(value, NULL, 10);
491         geom.set_size = TRUE;
492     } else if (strcmp(key, "maximized") == 0) {
493         if (strcasecmp(value, "true") == 0) {
494             geom.maximized = TRUE;
495         }
496         else {
497             geom.maximized = FALSE;
498         }
499         geom.set_maximized = TRUE;
500     } else {
501         g_assert_not_reached();
502     }
503
504     /* save / replace geometry in hashtable */
505     window_geom_save(name, &geom);
506 }
507
508
509 /* write all geometry values of all windows from the hashtable to the recent file */
510 void
511 window_geom_recent_write_all(gpointer rf)
512 {
513     /* init hashtable, if not already done */
514     if(!window_geom_hash) {
515         window_geom_hash = g_hash_table_new (g_str_hash, g_str_equal);
516     }
517
518     g_hash_table_foreach(window_geom_hash, write_recent_geom, rf);
519 }
520
521
522 void
523 window_destroy(GtkWidget *win)
524 {
525   window_geometry_t geom;
526   const gchar *name;
527
528   /* get_geometry must be done *before* destroy is running, as the window geometry
529    * cannot be retrieved at destroy time (so don't use event "destroy" for this) */
530   /* ...and don't do this at all, if we currently have no GdkWindow (e.g. if the 
531    * GtkWidget is hidden) */
532   if(!GTK_WIDGET_NO_WINDOW(win) && GTK_WIDGET_VISIBLE(win)) {
533       window_get_geometry(win, &geom);
534
535       name = OBJECT_GET_DATA(win, WINDOW_GEOM_KEY);
536       if(name) {
537         window_geom_save(name, &geom);
538         g_free((gpointer)name);
539       }
540   }
541
542   gtk_widget_destroy(win);
543 }
544
545
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 #if GTK_MAJOR_VERSION < 2
550     GdkPixmap *icon;
551     GdkBitmap * mask;
552
553
554     icon = gdk_pixmap_create_from_xpm_d(parent->window, &mask, &parent->style->white, (char **) xpm);
555     return gtk_pixmap_new(icon, mask);
556 #else
557     GdkPixbuf * pixbuf;
558     GdkPixmap * pixmap;
559     GdkBitmap * bitmap;
560
561
562     pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
563     gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf, gtk_widget_get_colormap(parent), &pixmap, &bitmap, 128);
564
565     return gtk_image_new_from_pixmap (pixmap, bitmap);
566 #endif
567 }
568
569
570 /* convert an xpm to a GtkWidget, using the top_level window settings */
571 /* (be sure that the top_level window is already being displayed) */
572 GtkWidget *xpm_to_widget(const char ** xpm) {
573     return xpm_to_widget_from_parent(top_level, xpm);
574 }
575
576 /* Create a new hbox with an image packed into it
577  * and return the box. */
578 GtkWidget *xpm_box( gchar **xpm )
579 {
580     GtkWidget *box;
581     GtkWidget *image;
582
583     /* Create box for image */
584     box = gtk_hbox_new (FALSE, 0);
585     gtk_container_set_border_width (GTK_CONTAINER (box), 3);
586
587     /* Now on to the image stuff */
588     image = xpm_to_widget (xpm);
589
590     /* Pack the image into the box */
591     gtk_box_pack_start (GTK_BOX (box), image, FALSE, FALSE, 3);
592
593     return box;
594 }
595
596
597 /* Set the name of the top-level window and its icon to the specified
598    string. */
599 void
600 set_main_window_name(gchar *window_name)
601 {
602   gchar *title;
603
604   /* use user-defined window title if preference is set */
605   title = create_user_window_title(window_name);
606   gtk_window_set_title(GTK_WINDOW(top_level), title);
607   gdk_window_set_icon_name(top_level->window, title);
608   g_free(title);
609 }
610
611
612 /* update the main window */
613 void main_window_update(void)
614 {
615   while (gtk_events_pending()) gtk_main_iteration();
616 }
617
618 #ifdef HAVE_LIBPCAP
619
620 /* exit the main window */
621 void main_window_exit(void)
622 {
623   gtk_exit(0);
624 }
625
626 /* quit a nested main window */
627 void main_window_nested_quit(void)
628 {
629   if (gtk_main_level() > 0)
630     gtk_main_quit();
631 }
632
633 /* quit the main window */
634 void main_window_quit(void)
635 {
636   gtk_main_quit();
637 }
638
639
640
641 typedef struct pipe_input_tag {
642     gint                source;
643     gpointer            user_data;
644     int                 *child_process;
645     pipe_input_cb_t     input_cb;
646     guint               pipe_input_id;
647 } pipe_input_t;
648
649
650 #ifdef _WIN32
651 /* The timer has expired, see if there's stuff to read from the pipe,
652    if so, do the callback */
653 static gint
654 pipe_timer_cb(gpointer data)
655 {
656   HANDLE handle;
657   DWORD avail = 0;
658   gboolean result, result1;
659   DWORD childstatus;
660   pipe_input_t *pipe_input = data;
661   gint iterations = 0;
662
663
664   /* try to read data from the pipe only 5 times, to avoid blocking */
665   while(iterations < 5) {
666           /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: new iteration");*/
667
668           /* Oddly enough although Named pipes don't work on win9x,
669                  PeekNamedPipe does !!! */
670           handle = (HANDLE) _get_osfhandle (pipe_input->source);
671           result = PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL);
672
673           /* Get the child process exit status */
674           result1 = GetExitCodeProcess((HANDLE)*(pipe_input->child_process),
675                                                                    &childstatus);
676
677           /* If the Peek returned an error, or there are bytes to be read
678                  or the childwatcher thread has terminated then call the normal
679                  callback */
680           if (!result || avail > 0 || childstatus != STILL_ACTIVE) {
681
682                 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: data avail");*/
683
684                 if(pipe_input->pipe_input_id != 0) {
685                   /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: stop timer");*/
686                   /* avoid reentrancy problems and stack overflow */
687                   gtk_timeout_remove(pipe_input->pipe_input_id);
688                   pipe_input->pipe_input_id = 0;
689                 }
690
691                 /* And call the real handler */
692                 if (!pipe_input->input_cb(pipe_input->source, pipe_input->user_data)) {
693                         g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: input pipe closed, iterations: %u", iterations);
694                         /* pipe closed, return false so that the old timer is not run again */
695                         return FALSE;
696                 }
697           }
698           else {
699                 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: no data avail");*/
700                 /* No data, stop now */
701                 break;
702           }
703
704           iterations++;
705   }
706
707   if(pipe_input->pipe_input_id == 0) {
708         /* restore pipe handler */
709         pipe_input->pipe_input_id = gtk_timeout_add(200, pipe_timer_cb, data);
710         /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: finished with iterations: %u, new timer", iterations);*/
711
712         /* Return false so that the old timer is not run again */
713         return FALSE;
714   } else {
715         /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: finished with iterations: %u, old timer", iterations);*/
716
717         /* we didn't stopped the old timer, so let it run */
718         return TRUE;
719   }
720 }
721
722 #else /* _WIN32 */
723
724 /* There's stuff to read from the sync pipe, meaning the child has sent
725    us a message, or the sync pipe has closed, meaning the child has
726    closed it (perhaps because it exited). */
727 static void
728 pipe_input_cb(gpointer data, gint source _U_,
729   GdkInputCondition condition _U_)
730 {
731   pipe_input_t *pipe_input = data;
732
733
734   /* avoid reentrancy problems and stack overflow */
735   gtk_input_remove(pipe_input->pipe_input_id);
736
737   if (pipe_input->input_cb(source, pipe_input->user_data)) {
738     /* restore pipe handler */
739     pipe_input->pipe_input_id = gtk_input_add_full (source,
740                                      GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
741                                      pipe_input_cb,
742                                      NULL,
743                                      data,
744                                      NULL);
745   }
746 }
747 #endif
748
749 void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
750 {
751     static pipe_input_t pipe_input;
752
753     pipe_input.source        = source;
754     pipe_input.child_process = child_process;
755     pipe_input.user_data     = user_data;
756     pipe_input.input_cb      = input_cb;
757
758 #ifdef _WIN32
759     /* Tricky to use pipes in win9x, as no concept of wait.  NT can
760        do this but that doesn't cover all win32 platforms.  GTK can do
761        this but doesn't seem to work over processes.  Attempt to do
762        something similar here, start a timer and check for data on every
763        timeout. */
764         /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
765     pipe_input.pipe_input_id = gtk_timeout_add(200, pipe_timer_cb, &pipe_input);
766 #else
767     pipe_input.pipe_input_id = gtk_input_add_full(source,
768                                       GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
769                                       pipe_input_cb,
770                                       NULL,
771                                       &pipe_input,
772                                       NULL);
773 #endif
774 }
775
776
777 #endif /* HAVE_LIBPCAP */
778
779 /* Given a pointer to a GtkWidget for a top-level window, raise it and
780    de-iconify it.  This routine is used if the user has done something to
781    ask that a window of a certain type be popped up when there can be only
782    one such window and such a window has already been popped up - we
783    pop up the existing one rather than creating a new one.
784
785    XXX - we should request that it be given the input focus, too.  Alas,
786    GDK has nothing to do that, e.g. by calling "XSetInputFocus()" in a
787    window in X.  Besides, using "XSetInputFocus()" doesn't work anyway,
788    apparently due to the way GTK+/GDK manages the input focus.
789
790    The X Desktop Group's Window Manager Standard specifies, in the section
791    on Root Window Properties, an _NET_ACTIVE_WINDOW client message that
792    can be sent to the root window, containing the window ID of the
793    window to activate; I infer that this might be the way to give the
794    window the input focus - I assume that means it's also de-iconified,
795    but I wouldn't assume it'd raise it.
796
797    XXX - will this do the right thing on window systems other than X? */
798 void
799 reactivate_window(GtkWidget *win)
800 {
801   gdk_window_show(win->window);
802   gdk_window_raise(win->window);
803 }
804
805 /* List of all GtkScrolledWindows, so we can globally set the scrollbar
806    placement of all of them. */
807 static GList *scrolled_windows;
808
809 static void setup_scrolled_window(GtkWidget *scrollw);
810 static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
811 static void set_scrollbar_placement_scrollw(GtkWidget *scrollw);
812
813 /* Create a GtkScrolledWindow, set its scrollbar placement appropriately,
814    and remember it. */
815 GtkWidget *
816 scrolled_window_new(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment)
817 {
818   GtkWidget *scrollw;
819
820   scrollw = gtk_scrolled_window_new(hadjustment, vadjustment);
821   setup_scrolled_window(scrollw);
822   return scrollw;
823 }
824
825 /* Set a GtkScrolledWindow's scrollbar placement and add it to the list
826    of GtkScrolledWindows. */
827 static void
828 setup_scrolled_window(GtkWidget *scrollw)
829 {
830   set_scrollbar_placement_scrollw(scrollw);
831
832   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollw),
833                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
834
835   scrolled_windows = g_list_append(scrolled_windows, scrollw);
836
837   /* Catch the "destroy" event on the widget, so that we remove it from
838      the list when it's destroyed. */
839   SIGNAL_CONNECT(scrollw, "destroy", forget_scrolled_window, NULL);
840 }
841
842 /* Remove a GtkScrolledWindow from the list of GtkScrolledWindows. */
843 static void
844 forget_scrolled_window(GtkWidget *scrollw, gpointer data _U_)
845 {
846   scrolled_windows = g_list_remove(scrolled_windows, scrollw);
847 }
848
849 /* Set the scrollbar placement of a GtkScrolledWindow based upon user
850    preference. */
851 static void
852 set_scrollbar_placement_scrollw(GtkWidget *scrollw)
853 {
854   if (prefs.gui_scrollbar_on_right) {
855     gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
856                                       GTK_CORNER_TOP_LEFT);
857   } else {
858     gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
859                                       GTK_CORNER_TOP_RIGHT);
860   }
861 }
862
863 static void
864 set_scrollbar_placement_cb(gpointer data, gpointer user_data _U_)
865 {
866   set_scrollbar_placement_scrollw((GtkWidget *)data);
867 }
868
869 /* Set the scrollbar placement of all GtkScrolledWindows based on
870    user preference. */
871 void
872 set_scrollbar_placement_all(void)
873 {
874   g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, NULL);
875 }
876
877 /* List of all CTrees/TreeViews, so we can globally set the line and
878  * expander style of all of them. */
879 static GList *trees;
880
881 static void setup_tree(GtkWidget *tree);
882 static void forget_tree(GtkWidget *tree, gpointer data);
883 static void set_tree_styles(GtkWidget *tree);
884
885 /* Create a Tree, give it the right styles, and remember it. */
886 #if GTK_MAJOR_VERSION < 2
887 GtkWidget *
888 ctree_new(gint columns, gint tree_column)
889 #else
890 GtkWidget *
891 tree_view_new(GtkTreeModel *model)
892 #endif
893 {
894   GtkWidget *tree;
895
896 #if GTK_MAJOR_VERSION < 2
897   tree = gtk_ctree_new(columns, tree_column);
898 #else
899   tree = gtk_tree_view_new_with_model(model);
900 #endif
901   setup_tree(tree);
902   return tree;
903 }
904
905 #if GTK_MAJOR_VERSION < 2
906 GtkWidget *
907 ctree_new_with_titles(gint columns, gint tree_column, const gchar *titles[])
908 {
909   GtkWidget *tree;
910
911   tree = gtk_ctree_new_with_titles(columns, tree_column, (gchar **) titles);
912   setup_tree(tree);
913   return tree;
914 }
915 #endif
916
917 /* Set a Tree's styles and add it to the list of Trees. */
918 static void
919 setup_tree(GtkWidget *tree)
920 {
921   set_tree_styles(tree);
922
923   trees = g_list_append(trees, tree);
924
925   /* Catch the "destroy" event on the widget, so that we remove it from
926      the list when it's destroyed. */
927   SIGNAL_CONNECT(tree, "destroy", forget_tree, NULL);
928 }
929
930 /* Remove a Tree from the list of Trees. */
931 static void
932 forget_tree(GtkWidget *tree, gpointer data _U_)
933 {
934   trees = g_list_remove(trees, tree);
935 }
936
937 /* Set the styles of a Tree based upon user preferences. */
938 static void
939 set_tree_styles(GtkWidget *tree)
940 {
941 #if GTK_MAJOR_VERSION < 2
942   g_assert(prefs.gui_ptree_line_style >= GTK_CTREE_LINES_NONE &&
943            prefs.gui_ptree_line_style <= GTK_CTREE_LINES_TABBED);
944   gtk_ctree_set_line_style(GTK_CTREE(tree), prefs.gui_ptree_line_style);
945   g_assert(prefs.gui_ptree_expander_style >= GTK_CTREE_EXPANDER_NONE &&
946            prefs.gui_ptree_expander_style <= GTK_CTREE_EXPANDER_CIRCULAR);
947   gtk_ctree_set_expander_style(GTK_CTREE(tree),
948       prefs.gui_ptree_expander_style);
949 #else
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 #endif
954 }
955
956 static void
957 set_tree_styles_cb(gpointer data, gpointer user_data _U_)
958 {
959   set_tree_styles((GtkWidget *)data);
960 }
961
962 /* Set the styles of all Trees based upon style values. */
963 void
964 set_tree_styles_all(void)
965 {
966   g_list_foreach(trees, set_tree_styles_cb, NULL);
967 }
968
969
970
971
972 #if GTK_MAJOR_VERSION < 2
973 /* convert variable argument list of values to array of strings (GTK2 -> GTK1) */
974 static void
975 simple_list_convert(gchar **ent, va_list ap)
976 {
977     int i;
978     char *s;
979
980     while( (i = va_arg(ap, int)) != -1 ) {
981         s = va_arg(ap, char *);
982         ent[i] = s;
983     }
984 }
985 #endif
986
987
988 /* append a row to the simple list */
989 /* use it like: simple_list_append(list, 0, "first", 1, "second", -1) */
990 void
991 simple_list_append(GtkWidget *list, ...)
992 {
993     va_list ap;
994
995 #if GTK_MAJOR_VERSION < 2
996     gchar      *ent[10];               /* new entry added in clist */
997 #else
998     GtkTreeIter iter;
999     GtkListStore *store;
1000 #endif
1001
1002     va_start(ap, list);
1003 #if GTK_MAJOR_VERSION < 2
1004     simple_list_convert(ent, ap);
1005     gtk_clist_append(GTK_CLIST(list), ent);
1006 #else
1007     store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
1008     gtk_list_store_append(store, &iter);
1009     gtk_list_store_set_valist(store, &iter, ap);
1010 #endif
1011     va_end(ap);
1012 }
1013
1014 /* create a simple list widget */
1015 GtkWidget *
1016 simple_list_new(gint cols, const gchar **titles) {
1017     GtkWidget *plugins_list;
1018 #if GTK_MAJOR_VERSION >= 2
1019     int i;
1020     GtkListStore *store;
1021     GtkCellRenderer *renderer;
1022     GtkTreeViewColumn *column;
1023 #endif
1024
1025
1026 #if GTK_MAJOR_VERSION < 2
1027     plugins_list = gtk_clist_new_with_titles(cols, (gchar **) titles);
1028     gtk_clist_set_selection_mode(GTK_CLIST(plugins_list), GTK_SELECTION_SINGLE);
1029     gtk_clist_column_titles_passive(GTK_CLIST(plugins_list));
1030     if(titles) {
1031         gtk_clist_column_titles_show(GTK_CLIST(plugins_list));
1032     } else {
1033         gtk_clist_column_titles_hide(GTK_CLIST(plugins_list));
1034     }
1035     gtk_clist_set_column_auto_resize(GTK_CLIST(plugins_list), 0, TRUE);
1036     gtk_clist_set_column_auto_resize(GTK_CLIST(plugins_list), 1, TRUE);
1037 #else
1038     g_assert(cols <= 10);
1039     store = gtk_list_store_new(cols,
1040         G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
1041         G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
1042     plugins_list = tree_view_new(GTK_TREE_MODEL(store));
1043     g_object_unref(G_OBJECT(store));
1044     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(plugins_list), (titles != NULL));
1045     for(i=0; i<cols; i++) {
1046         renderer = gtk_cell_renderer_text_new();
1047         column = gtk_tree_view_column_new_with_attributes(titles ? titles[i] : "", renderer,
1048                                                           "text", i, NULL);
1049         gtk_tree_view_column_set_sort_column_id(column, i);
1050         gtk_tree_view_append_column(GTK_TREE_VIEW(plugins_list), column);
1051     }
1052 #endif
1053
1054     return plugins_list;
1055 }
1056
1057 void
1058 copy_to_clipboard(GString *str)  
1059 {
1060 #if (GTK_MAJOR_VERSION >= 2)
1061         GtkClipboard    *cb;
1062
1063         cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);     /* Get the default clipboard */
1064         gtk_clipboard_set_text(cb, str->str, -1);            /* Copy the byte data into the clipboard */
1065 #else
1066         GtkWidget *window;
1067         GtkWidget *text;
1068
1069         window = window_new (GTK_WINDOW_TOPLEVEL,"");
1070         text = gtk_text_new (NULL, NULL);                 /* Create the GtkText widget */
1071         gtk_container_add (GTK_CONTAINER (window), text); /* Avoid a GTK assertion */
1072         gtk_widget_realize (text);   /* Realizing a widget creates a window for it, ready for us to insert some text */
1073         gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, str->str, -1);
1074         gtk_editable_select_region((GtkEditable *)text, 0, -1); /* Select ALL text */
1075         gtk_editable_copy_clipboard((GtkEditable *)text); /* Copy the byte data into the clipboard */
1076 #endif
1077 }
1078
1079 /*
1080  * Create a new window title string with user-defined title preference.
1081  * (Or ignore it if unspecified).
1082  */
1083 gchar *
1084 create_user_window_title(const gchar *caption)
1085 {
1086         /* fail-safe */
1087         if (caption == NULL)
1088                 return g_strdup("");
1089
1090         /* no user-defined title specified */
1091         if ((prefs.gui_window_title == NULL) || (*prefs.gui_window_title == '\0'))
1092                 return g_strdup(caption);
1093
1094         return g_strdup_printf("%s %s", prefs.gui_window_title, caption);
1095 }