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