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