name change
[metze/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/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 #if GTK_MAJOR_VERSION >= 2
366         gtk_window_move(GTK_WINDOW(widget),
367                         geom->x,
368                         geom->y);
369 #else
370         gtk_widget_set_uposition(widget,
371                                  geom->x,
372                                  geom->y);
373 #endif
374     }
375
376     if (geom->set_size) {
377 #if GTK_MAJOR_VERSION >= 2
378         gtk_window_resize(GTK_WINDOW(widget),
379 #else
380         gtk_window_set_default_size(GTK_WINDOW(widget), 
381                                 geom->width, 
382                                 geom->height);
383         gtk_widget_set_usize(widget,
384 #endif
385         /*WIDGET_SET_SIZE(widget,*/
386                                 geom->width,
387                                 geom->height);
388     }
389
390 #if GTK_MAJOR_VERSION >= 2
391     if(geom->set_maximized) {
392         if (geom->maximized) {
393             gdk_window_maximize(widget->window);
394         } else {
395             gdk_window_unmaximize(widget->window);
396         }
397     }
398 #endif
399 }
400
401
402 /* the geometry hashtable for all known window classes,
403  * the window name is the key, and the geometry struct is the value */
404 GHashTable *window_geom_hash = NULL;
405
406
407 /* save the window and it's current geometry into the geometry hashtable */
408 static void
409 window_geom_save(const gchar *name, window_geometry_t *geom)
410 {
411     gchar *key;
412     window_geometry_t *work;
413
414     /* init hashtable, if not already done */
415     if(!window_geom_hash) {
416         window_geom_hash = g_hash_table_new (g_str_hash, g_str_equal);
417     }
418     /* if we have an old one, remove and free it first */
419     work = g_hash_table_lookup(window_geom_hash, name);
420     if(work) {
421         g_hash_table_remove(window_geom_hash, name);
422         g_free(work->key);
423         g_free(work);
424     }
425
426     /* g_malloc and insert the new one */
427     work = g_malloc(sizeof(*geom));
428     *work = *geom;
429     key = g_strdup(name);
430     work->key = key;
431     g_hash_table_insert(window_geom_hash, key, work);
432 }
433
434
435 /* load the desired geometry for this window from the geometry hashtable */
436 static gboolean
437 window_geom_load(const gchar *name, window_geometry_t *geom)
438 {
439     window_geometry_t *p;
440
441     /* init hashtable, if not already done */
442     if(!window_geom_hash) {
443         window_geom_hash = g_hash_table_new (g_str_hash, g_str_equal);
444     }
445
446     p = g_hash_table_lookup(window_geom_hash, name);
447     if(p) {
448         *geom = *p;
449         return TRUE;
450     } else {
451         return FALSE;
452     }
453 }
454
455
456 /* read in a single key value pair from the recent file into the geometry hashtable */
457 void
458 window_geom_recent_read_pair(const char *name, const char *key, const char *value)
459 {
460     window_geometry_t geom;
461
462
463     /* find window geometry maybe already in hashtable */
464     if(!window_geom_load(name, &geom)) {
465         /* not in table, init geom with "basic" values */
466         geom.key        = g_strdup(name);
467         geom.set_pos    = FALSE;
468         geom.x          = -1;
469         geom.y          = -1;
470         geom.set_size   = FALSE;
471         geom.width      = -1;
472         geom.height     = -1;
473
474         geom.set_maximized = FALSE;/* this is valid in GTK2 only */
475         geom.maximized  = FALSE;   /* this is valid in GTK2 only */
476     }
477     
478     if (strcmp(key, "x") == 0) {
479         geom.x = strtol(value, NULL, 10);
480         geom.set_pos = TRUE;
481     } else if (strcmp(key, "y") == 0) {
482         geom.y = strtol(value, NULL, 10);
483         geom.set_pos = TRUE;
484     } else if (strcmp(key, "width") == 0) {
485         geom.width = strtol(value, NULL, 10);
486         geom.set_size = TRUE;
487     } else if (strcmp(key, "height") == 0) {
488         geom.height = strtol(value, NULL, 10);
489         geom.set_size = TRUE;
490     } else if (strcmp(key, "maximized") == 0) {
491         if (strcasecmp(value, "true") == 0) {
492             geom.maximized = TRUE;
493         }
494         else {
495             geom.maximized = FALSE;
496         }
497         geom.set_maximized = TRUE;
498     } else {
499         g_assert_not_reached();
500     }
501
502     /* save / replace geometry in hashtable */
503     window_geom_save(name, &geom);
504 }
505
506
507 /* write all geometry values of all windows from the hashtable to the recent file */
508 void
509 window_geom_recent_write_all(gpointer rf)
510 {
511     /* init hashtable, if not already done */
512     if(!window_geom_hash) {
513         window_geom_hash = g_hash_table_new (g_str_hash, g_str_equal);
514     }
515
516     g_hash_table_foreach(window_geom_hash, write_recent_geom, rf);
517 }
518
519
520 void
521 window_destroy(GtkWidget *win)
522 {
523   window_geometry_t geom;
524   const gchar *name;
525
526   /* get_geometry must be done *before* destroy is running, as the window geometry
527    * cannot be retrieved at destroy time (so don't use event "destroy" for this) */
528   /* ...and don't do this at all, if we currently have no GdkWindow (e.g. if the 
529    * GtkWidget is hidden) */
530   if(!GTK_WIDGET_NO_WINDOW(win) && GTK_WIDGET_VISIBLE(win)) {
531       window_get_geometry(win, &geom);
532
533       name = OBJECT_GET_DATA(win, WINDOW_GEOM_KEY);
534       if(name) {
535         window_geom_save(name, &geom);
536         g_free((gpointer)name);
537       }
538   }
539
540   gtk_widget_destroy(win);
541 }
542
543
544 /* convert an xpm to a GtkWidget, using the window settings from it's parent */
545 /* (be sure that the parent window is already being displayed) */
546 GtkWidget *xpm_to_widget_from_parent(GtkWidget *parent, const char ** xpm) {
547 #if GTK_MAJOR_VERSION < 2
548     GdkPixmap *icon;
549     GdkBitmap * mask;
550
551
552     icon = gdk_pixmap_create_from_xpm_d(parent->window, &mask, &parent->style->white, (char **) xpm);
553     return gtk_pixmap_new(icon, mask);
554 #else
555     GdkPixbuf * pixbuf;
556     GdkPixmap * pixmap;
557     GdkBitmap * bitmap;
558
559
560     pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
561     gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf, gtk_widget_get_colormap(parent), &pixmap, &bitmap, 128);
562
563     return gtk_image_new_from_pixmap (pixmap, bitmap);
564 #endif
565 }
566
567
568 /* convert an xpm to a GtkWidget, using the top_level window settings */
569 /* (be sure that the top_level window is already being displayed) */
570 GtkWidget *xpm_to_widget(const char ** xpm) {
571     return xpm_to_widget_from_parent(top_level, xpm);
572 }
573
574
575 /* Set the name of the top-level window and its icon to the specified
576    string. */
577 void
578 set_main_window_name(gchar *window_name)
579 {
580   gchar *title;
581
582   /* use user-defined window title if preference is set */
583   title = create_user_window_title(window_name);
584   gtk_window_set_title(GTK_WINDOW(top_level), title);
585   gdk_window_set_icon_name(top_level->window, title);
586   g_free(title);
587 }
588
589
590 /* update the main window */
591 void main_window_update(void)
592 {
593   while (gtk_events_pending()) gtk_main_iteration();
594 }
595
596 #ifdef HAVE_LIBPCAP
597
598 /* exit the main window */
599 void main_window_exit(void)
600 {
601   gtk_exit(0);
602 }
603
604 /* quit a nested main window */
605 void main_window_nested_quit(void)
606 {
607   if (gtk_main_level() > 0)
608     gtk_main_quit();
609 }
610
611 /* quit the main window */
612 void main_window_quit(void)
613 {
614   gtk_main_quit();
615 }
616
617
618
619 typedef struct pipe_input_tag {
620     gint                source;
621     gpointer            user_data;
622     int                 *child_process;
623     pipe_input_cb_t     input_cb;
624     guint               pipe_input_id;
625 } pipe_input_t;
626
627
628 #ifdef _WIN32
629 /* The timer has expired, see if there's stuff to read from the pipe,
630    if so, do the callback */
631 static gint
632 pipe_timer_cb(gpointer data)
633 {
634   HANDLE handle;
635   DWORD avail = 0;
636   gboolean result, result1;
637   DWORD childstatus;
638   pipe_input_t *pipe_input = data;
639   gint iterations = 0;
640
641
642   /* try to read data from the pipe only 5 times, to avoid blocking */
643   while(iterations < 5) {
644           /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: new iteration");*/
645
646           /* Oddly enough although Named pipes don't work on win9x,
647                  PeekNamedPipe does !!! */
648           handle = (HANDLE) _get_osfhandle (pipe_input->source);
649           result = PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL);
650
651           /* Get the child process exit status */
652           result1 = GetExitCodeProcess((HANDLE)*(pipe_input->child_process),
653                                                                    &childstatus);
654
655           /* If the Peek returned an error, or there are bytes to be read
656                  or the childwatcher thread has terminated then call the normal
657                  callback */
658           if (!result || avail > 0 || childstatus != STILL_ACTIVE) {
659
660                 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: data avail");*/
661
662                 if(pipe_input->pipe_input_id != 0) {
663                   /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: stop timer");*/
664                   /* avoid reentrancy problems and stack overflow */
665                   gtk_timeout_remove(pipe_input->pipe_input_id);
666                   pipe_input->pipe_input_id = 0;
667                 }
668
669                 /* And call the real handler */
670                 if (!pipe_input->input_cb(pipe_input->source, pipe_input->user_data)) {
671                         g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: input pipe closed, iterations: %u", iterations);
672                         /* pipe closed, return false so that the old timer is not run again */
673                         return FALSE;
674                 }
675           }
676           else {
677                 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: no data avail");*/
678                 /* No data, stop now */
679                 break;
680           }
681
682           iterations++;
683   }
684
685   if(pipe_input->pipe_input_id == 0) {
686         /* restore pipe handler */
687         pipe_input->pipe_input_id = gtk_timeout_add(200, pipe_timer_cb, data);
688         /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: finished with iterations: %u, new timer", iterations);*/
689
690         /* Return false so that the old timer is not run again */
691         return FALSE;
692   } else {
693         /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: finished with iterations: %u, old timer", iterations);*/
694
695         /* we didn't stopped the old timer, so let it run */
696         return TRUE;
697   }
698 }
699
700 #else /* _WIN32 */
701
702 /* There's stuff to read from the sync pipe, meaning the child has sent
703    us a message, or the sync pipe has closed, meaning the child has
704    closed it (perhaps because it exited). */
705 static void
706 pipe_input_cb(gpointer data, gint source _U_,
707   GdkInputCondition condition _U_)
708 {
709   pipe_input_t *pipe_input = data;
710
711
712   /* avoid reentrancy problems and stack overflow */
713   gtk_input_remove(pipe_input->pipe_input_id);
714
715   if (pipe_input->input_cb(source, pipe_input->user_data)) {
716     /* restore pipe handler */
717     pipe_input->pipe_input_id = gtk_input_add_full (source,
718                                      GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
719                                      pipe_input_cb,
720                                      NULL,
721                                      data,
722                                      NULL);
723   }
724 }
725 #endif
726
727 void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
728 {
729     static pipe_input_t pipe_input;
730
731     pipe_input.source        = source;
732     pipe_input.child_process = child_process;
733     pipe_input.user_data     = user_data;
734     pipe_input.input_cb      = input_cb;
735
736 #ifdef _WIN32
737     /* Tricky to use pipes in win9x, as no concept of wait.  NT can
738        do this but that doesn't cover all win32 platforms.  GTK can do
739        this but doesn't seem to work over processes.  Attempt to do
740        something similar here, start a timer and check for data on every
741        timeout. */
742         /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
743     pipe_input.pipe_input_id = gtk_timeout_add(200, pipe_timer_cb, &pipe_input);
744 #else
745     pipe_input.pipe_input_id = gtk_input_add_full(source,
746                                       GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
747                                       pipe_input_cb,
748                                       NULL,
749                                       &pipe_input,
750                                       NULL);
751 #endif
752 }
753
754
755 #endif /* HAVE_LIBPCAP */
756
757 /* Given a pointer to a GtkWidget for a top-level window, raise it and
758    de-iconify it.  This routine is used if the user has done something to
759    ask that a window of a certain type be popped up when there can be only
760    one such window and such a window has already been popped up - we
761    pop up the existing one rather than creating a new one.
762
763    XXX - we should request that it be given the input focus, too.  Alas,
764    GDK has nothing to do that, e.g. by calling "XSetInputFocus()" in a
765    window in X.  Besides, using "XSetInputFocus()" doesn't work anyway,
766    apparently due to the way GTK+/GDK manages the input focus.
767
768    The X Desktop Group's Window Manager Standard specifies, in the section
769    on Root Window Properties, an _NET_ACTIVE_WINDOW client message that
770    can be sent to the root window, containing the window ID of the
771    window to activate; I infer that this might be the way to give the
772    window the input focus - I assume that means it's also de-iconified,
773    but I wouldn't assume it'd raise it.
774
775    XXX - will this do the right thing on window systems other than X? */
776 void
777 reactivate_window(GtkWidget *win)
778 {
779   gdk_window_show(win->window);
780   gdk_window_raise(win->window);
781 }
782
783 /* List of all GtkScrolledWindows, so we can globally set the scrollbar
784    placement of all of them. */
785 static GList *scrolled_windows;
786
787 static void setup_scrolled_window(GtkWidget *scrollw);
788 static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
789 static void set_scrollbar_placement_scrollw(GtkWidget *scrollw);
790
791 /* Create a GtkScrolledWindow, set its scrollbar placement appropriately,
792    and remember it. */
793 GtkWidget *
794 scrolled_window_new(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment)
795 {
796   GtkWidget *scrollw;
797
798   scrollw = gtk_scrolled_window_new(hadjustment, vadjustment);
799   setup_scrolled_window(scrollw);
800   return scrollw;
801 }
802
803 /* Set a GtkScrolledWindow's scrollbar placement and add it to the list
804    of GtkScrolledWindows. */
805 static void
806 setup_scrolled_window(GtkWidget *scrollw)
807 {
808   set_scrollbar_placement_scrollw(scrollw);
809
810   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollw),
811                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
812
813   scrolled_windows = g_list_append(scrolled_windows, scrollw);
814
815   /* Catch the "destroy" event on the widget, so that we remove it from
816      the list when it's destroyed. */
817   SIGNAL_CONNECT(scrollw, "destroy", forget_scrolled_window, NULL);
818 }
819
820 /* Remove a GtkScrolledWindow from the list of GtkScrolledWindows. */
821 static void
822 forget_scrolled_window(GtkWidget *scrollw, gpointer data _U_)
823 {
824   scrolled_windows = g_list_remove(scrolled_windows, scrollw);
825 }
826
827 /* Set the scrollbar placement of a GtkScrolledWindow based upon user
828    preference. */
829 static void
830 set_scrollbar_placement_scrollw(GtkWidget *scrollw)
831 {
832   if (prefs.gui_scrollbar_on_right) {
833     gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
834                                       GTK_CORNER_TOP_LEFT);
835   } else {
836     gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
837                                       GTK_CORNER_TOP_RIGHT);
838   }
839 }
840
841 static void
842 set_scrollbar_placement_cb(gpointer data, gpointer user_data _U_)
843 {
844   set_scrollbar_placement_scrollw((GtkWidget *)data);
845 }
846
847 /* Set the scrollbar placement of all GtkScrolledWindows based on
848    user preference. */
849 void
850 set_scrollbar_placement_all(void)
851 {
852   g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, NULL);
853 }
854
855 /* List of all CTrees/TreeViews, so we can globally set the line and
856  * expander style of all of them. */
857 static GList *trees;
858
859 static void setup_tree(GtkWidget *tree);
860 static void forget_tree(GtkWidget *tree, gpointer data);
861 static void set_tree_styles(GtkWidget *tree);
862
863 /* Create a Tree, give it the right styles, and remember it. */
864 #if GTK_MAJOR_VERSION < 2
865 GtkWidget *
866 ctree_new(gint columns, gint tree_column)
867 #else
868 GtkWidget *
869 tree_view_new(GtkTreeModel *model)
870 #endif
871 {
872   GtkWidget *tree;
873
874 #if GTK_MAJOR_VERSION < 2
875   tree = gtk_ctree_new(columns, tree_column);
876 #else
877   tree = gtk_tree_view_new_with_model(model);
878 #endif
879   setup_tree(tree);
880   return tree;
881 }
882
883 #if GTK_MAJOR_VERSION < 2
884 GtkWidget *
885 ctree_new_with_titles(gint columns, gint tree_column, const gchar *titles[])
886 {
887   GtkWidget *tree;
888
889   tree = gtk_ctree_new_with_titles(columns, tree_column, (gchar **) titles);
890   setup_tree(tree);
891   return tree;
892 }
893 #endif
894
895 /* Set a Tree's styles and add it to the list of Trees. */
896 static void
897 setup_tree(GtkWidget *tree)
898 {
899   set_tree_styles(tree);
900
901   trees = g_list_append(trees, tree);
902
903   /* Catch the "destroy" event on the widget, so that we remove it from
904      the list when it's destroyed. */
905   SIGNAL_CONNECT(tree, "destroy", forget_tree, NULL);
906 }
907
908 /* Remove a Tree from the list of Trees. */
909 static void
910 forget_tree(GtkWidget *tree, gpointer data _U_)
911 {
912   trees = g_list_remove(trees, tree);
913 }
914
915 /* Set the styles of a Tree based upon user preferences. */
916 static void
917 set_tree_styles(GtkWidget *tree)
918 {
919 #if GTK_MAJOR_VERSION < 2
920   g_assert(prefs.gui_ptree_line_style >= GTK_CTREE_LINES_NONE &&
921            prefs.gui_ptree_line_style <= GTK_CTREE_LINES_TABBED);
922   gtk_ctree_set_line_style(GTK_CTREE(tree), prefs.gui_ptree_line_style);
923   g_assert(prefs.gui_ptree_expander_style >= GTK_CTREE_EXPANDER_NONE &&
924            prefs.gui_ptree_expander_style <= GTK_CTREE_EXPANDER_CIRCULAR);
925   gtk_ctree_set_expander_style(GTK_CTREE(tree),
926       prefs.gui_ptree_expander_style);
927 #else
928   g_assert(prefs.gui_altern_colors >= 0 && prefs.gui_altern_colors <= 1);
929   gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree),
930                                prefs.gui_altern_colors);
931 #endif
932 }
933
934 static void
935 set_tree_styles_cb(gpointer data, gpointer user_data _U_)
936 {
937   set_tree_styles((GtkWidget *)data);
938 }
939
940 /* Set the styles of all Trees based upon style values. */
941 void
942 set_tree_styles_all(void)
943 {
944   g_list_foreach(trees, set_tree_styles_cb, NULL);
945 }
946
947
948
949
950 #if GTK_MAJOR_VERSION < 2
951 /* convert variable argument list of values to array of strings (GTK2 -> GTK1) */
952 static void
953 simple_list_convert(gchar **ent, va_list ap)
954 {
955     int i;
956     char *s;
957
958     while( (i = va_arg(ap, int)) != -1 ) {
959         s = va_arg(ap, char *);
960         ent[i] = s;
961     }
962 }
963 #endif
964
965
966 /* append a row to the simple list */
967 /* use it like: simple_list_append(list, 0, "first", 1, "second", -1) */
968 void
969 simple_list_append(GtkWidget *list, ...)
970 {
971     va_list ap;
972
973 #if GTK_MAJOR_VERSION < 2
974     gchar      *ent[10];               /* new entry added in clist */
975 #else
976     GtkTreeIter iter;
977     GtkListStore *store;
978 #endif
979
980     va_start(ap, list);
981 #if GTK_MAJOR_VERSION < 2
982     simple_list_convert(ent, ap);
983     gtk_clist_append(GTK_CLIST(list), ent);
984 #else
985     store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
986     gtk_list_store_append(store, &iter);
987     gtk_list_store_set_valist(store, &iter, ap);
988 #endif
989     va_end(ap);
990 }
991
992 /* create a simple list widget */
993 GtkWidget *
994 simple_list_new(gint cols, const gchar **titles) {
995     GtkWidget *plugins_list;
996 #if GTK_MAJOR_VERSION >= 2
997     int i;
998     GtkListStore *store;
999     GtkCellRenderer *renderer;
1000     GtkTreeViewColumn *column;
1001 #endif
1002
1003
1004 #if GTK_MAJOR_VERSION < 2
1005     plugins_list = gtk_clist_new_with_titles(cols, (gchar **) titles);
1006     gtk_clist_set_selection_mode(GTK_CLIST(plugins_list), GTK_SELECTION_SINGLE);
1007     gtk_clist_column_titles_passive(GTK_CLIST(plugins_list));
1008     if(titles) {
1009         gtk_clist_column_titles_show(GTK_CLIST(plugins_list));
1010     } else {
1011         gtk_clist_column_titles_hide(GTK_CLIST(plugins_list));
1012     }
1013     gtk_clist_set_column_auto_resize(GTK_CLIST(plugins_list), 0, TRUE);
1014     gtk_clist_set_column_auto_resize(GTK_CLIST(plugins_list), 1, TRUE);
1015 #else
1016     g_assert(cols <= 10);
1017     store = gtk_list_store_new(cols,
1018         G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
1019         G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
1020     plugins_list = tree_view_new(GTK_TREE_MODEL(store));
1021     g_object_unref(G_OBJECT(store));
1022     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(plugins_list), (titles != NULL));
1023     for(i=0; i<cols; i++) {
1024         renderer = gtk_cell_renderer_text_new();
1025         column = gtk_tree_view_column_new_with_attributes(titles ? titles[i] : "", renderer,
1026                                                           "text", i, NULL);
1027         gtk_tree_view_column_set_sort_column_id(column, i);
1028         gtk_tree_view_append_column(GTK_TREE_VIEW(plugins_list), column);
1029     }
1030 #endif
1031
1032     return plugins_list;
1033 }
1034
1035 void
1036 copy_to_clipboard(GString *str)  
1037 {
1038 #if (GTK_MAJOR_VERSION >= 2)
1039         GtkClipboard    *cb;
1040
1041         cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);     /* Get the default clipboard */
1042         gtk_clipboard_set_text(cb, str->str, -1);            /* Copy the byte data into the clipboard */
1043 #else
1044         GtkWidget *window;
1045         GtkWidget *text;
1046
1047         window = window_new (GTK_WINDOW_TOPLEVEL,"");
1048         text = gtk_text_new (NULL, NULL);                 /* Create the GtkText widget */
1049         gtk_container_add (GTK_CONTAINER (window), text); /* Avoid a GTK assertion */
1050         gtk_widget_realize (text);   /* Realizing a widget creates a window for it, ready for us to insert some text */
1051         gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, str->str, -1);
1052         gtk_editable_select_region((GtkEditable *)text, 0, -1); /* Select ALL text */
1053         gtk_editable_copy_clipboard((GtkEditable *)text); /* Copy the byte data into the clipboard */
1054 #endif
1055 }
1056
1057 /*
1058  * Create a new window title string with user-defined title preference.
1059  * (Or ignore it if unspecified).
1060  */
1061 gchar *
1062 create_user_window_title(const gchar *caption)
1063 {
1064         /* fail-safe */
1065         if (caption == NULL)
1066                 return g_strdup("");
1067
1068         /* no user-defined title specified */
1069         if ((prefs.gui_window_title == NULL) || (*prefs.gui_window_title == '\0'))
1070                 return g_strdup(caption);
1071
1072         return g_strdup_printf("%s %s", prefs.gui_window_title, caption);
1073 }