"ssn_range" needs to be a copy of "global_ssn_range", so that it's not
[obnox/wireshark/wip.git] / gtk / ui_util.c
1 /* ui_util.c
2  * UI utility routines
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <string.h>
30
31 #ifdef _WIN32
32 #include <windows.h>
33 #endif
34
35 #ifdef HAVE_IO_H
36 # include <io.h>
37 #endif
38
39 #include <gtk/gtk.h>
40 #include <gdk/gdkkeysyms.h>
41
42 #include "gtkglobals.h"
43 #include "ui_util.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, 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     /* 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       }
531   }
532
533   gtk_widget_destroy(win);
534 }
535
536
537 /* convert an xpm to a GtkWidget, using the window settings from it's parent */
538 /* (be sure that the parent window is already being displayed) */
539 GtkWidget *xpm_to_widget_from_parent(GtkWidget *parent, const char ** xpm) {
540 #if GTK_MAJOR_VERSION < 2
541     GdkPixmap *icon;
542     GdkBitmap * mask;
543
544
545     icon = gdk_pixmap_create_from_xpm_d(parent->window, &mask, &parent->style->white, (char **) xpm);
546     return gtk_pixmap_new(icon, mask);
547 #else
548     GdkPixbuf * pixbuf;
549     GdkPixmap * pixmap;
550     GdkBitmap * bitmap;
551
552
553     pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
554     gdk_pixbuf_render_pixmap_and_mask_for_colormap (pixbuf, gtk_widget_get_colormap(parent), &pixmap, &bitmap, 128);
555
556     return gtk_image_new_from_pixmap (pixmap, bitmap);
557 #endif
558 }
559
560
561 /* convert an xpm to a GtkWidget, using the top_level window settings */
562 /* (be sure that the top_level window is already being displayed) */
563 GtkWidget *xpm_to_widget(const char ** xpm) {
564     return xpm_to_widget_from_parent(top_level, xpm);
565 }
566
567
568 /* Set the name of the top-level window and its icon to the specified
569    string. */
570 void
571 set_main_window_name(gchar *window_name)
572 {
573   gtk_window_set_title(GTK_WINDOW(top_level), window_name);
574   gdk_window_set_icon_name(top_level->window, window_name);
575 }
576
577
578 #ifdef HAVE_LIBPCAP
579
580 /* update the main window */
581 void main_window_update(void)
582 {
583   while (gtk_events_pending()) gtk_main_iteration();
584 }
585
586 /* exit the main window */
587 void main_window_exit(void)
588 {
589   gtk_exit(0);
590 }
591
592 /* quit a nested main window */
593 void main_window_nested_quit(void)
594 {
595   if (gtk_main_level() > 0)
596     gtk_main_quit();
597 }
598
599 /* quit the main window */
600 void main_window_quit(void)
601 {
602   gtk_main_quit();
603 }
604
605
606
607 typedef struct pipe_input_tag {
608     gint                source;
609     gpointer            user_data;
610     int                 *child_process;
611     pipe_input_cb_t     input_cb;
612     guint               pipe_input_id;
613 } pipe_input_t;
614
615
616 #ifdef _WIN32
617 /* The timer has expired, see if there's stuff to read from the pipe,
618    if so, do the callback */
619 static gint
620 pipe_timer_cb(gpointer data)
621 {
622   HANDLE handle;
623   DWORD avail = 0;
624   gboolean result, result1;
625   DWORD childstatus;
626   pipe_input_t *pipe_input = data;
627
628
629   /* Oddly enough although Named pipes don't work on win9x,
630      PeekNamedPipe does !!! */
631   handle = (HANDLE) _get_osfhandle (pipe_input->source);
632   result = PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL);
633
634   /* Get the child process exit status */
635   result1 = GetExitCodeProcess((HANDLE)*(pipe_input->child_process),
636                                &childstatus);
637
638   /* If the Peek returned an error, or there are bytes to be read
639      or the childwatcher thread has terminated then call the normal
640      callback */
641   if (!result || avail > 0 || childstatus != STILL_ACTIVE) {
642
643     /* avoid reentrancy problems and stack overflow */
644     gtk_timeout_remove(pipe_input->pipe_input_id);
645
646     /* And call the real handler */
647     if (pipe_input->input_cb(pipe_input->source, pipe_input->user_data)) {
648         /* restore pipe handler */
649         pipe_input->pipe_input_id = gtk_timeout_add(200, pipe_timer_cb, data);
650     }
651
652     /* Return false so that this timer is not run again */
653     return FALSE;
654   }
655   else {
656     /* No data so let timer run again */
657     return TRUE;
658   }
659 }
660
661 #else /* _WIN32 */
662
663 /* There's stuff to read from the sync pipe, meaning the child has sent
664    us a message, or the sync pipe has closed, meaning the child has
665    closed it (perhaps because it exited). */
666 static void
667 pipe_input_cb(gpointer data, gint source _U_,
668   GdkInputCondition condition _U_)
669 {
670   pipe_input_t *pipe_input = data;
671
672
673   /* avoid reentrancy problems and stack overflow */
674   gtk_input_remove(pipe_input->pipe_input_id);
675
676   if (pipe_input->input_cb(source, pipe_input->user_data)) {
677     /* restore pipe handler */
678     pipe_input->pipe_input_id = gtk_input_add_full (source,
679                                      GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
680                                      pipe_input_cb,
681                                      NULL,
682                                      data,
683                                      NULL);
684   }
685 }
686 #endif
687
688 void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
689 {
690     static pipe_input_t pipe_input;
691
692     pipe_input.source        = source;
693     pipe_input.child_process = child_process;
694     pipe_input.user_data     = user_data;
695     pipe_input.input_cb      = input_cb;
696
697 #ifdef _WIN32
698     /* Tricky to use pipes in win9x, as no concept of wait.  NT can
699        do this but that doesn't cover all win32 platforms.  GTK can do
700        this but doesn't seem to work over processes.  Attempt to do
701        something similar here, start a timer and check for data on every
702        timeout. */
703     pipe_input.pipe_input_id = gtk_timeout_add(200, pipe_timer_cb, &pipe_input);
704 #else
705     pipe_input.pipe_input_id = gtk_input_add_full(source,
706                                       GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
707                                       pipe_input_cb,
708                                       NULL,
709                                       &pipe_input,
710                                       NULL);
711 #endif
712 }
713
714
715 #endif /* HAVE_LIBPCAP */
716
717 /* Given a pointer to a GtkWidget for a top-level window, raise it and
718    de-iconify it.  This routine is used if the user has done something to
719    ask that a window of a certain type be popped up when there can be only
720    one such window and such a window has already been popped up - we
721    pop up the existing one rather than creating a new one.
722
723    XXX - we should request that it be given the input focus, too.  Alas,
724    GDK has nothing to do that, e.g. by calling "XSetInputFocus()" in a
725    window in X.  Besides, using "XSetInputFocus()" doesn't work anyway,
726    apparently due to the way GTK+/GDK manages the input focus.
727
728    The X Desktop Group's Window Manager Standard specifies, in the section
729    on Root Window Properties, an _NET_ACTIVE_WINDOW client message that
730    can be sent to the root window, containing the window ID of the
731    window to activate; I infer that this might be the way to give the
732    window the input focus - I assume that means it's also de-iconified,
733    but I wouldn't assume it'd raise it.
734
735    XXX - will this do the right thing on window systems other than X? */
736 void
737 reactivate_window(GtkWidget *win)
738 {
739   gdk_window_show(win->window);
740   gdk_window_raise(win->window);
741 }
742
743 /* List of all GtkScrolledWindows, so we can globally set the scrollbar
744    placement of all of them. */
745 static GList *scrolled_windows;
746
747 static void setup_scrolled_window(GtkWidget *scrollw);
748 static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
749 static void set_scrollbar_placement_scrollw(GtkWidget *scrollw);
750
751 /* Create a GtkScrolledWindow, set its scrollbar placement appropriately,
752    and remember it. */
753 GtkWidget *
754 scrolled_window_new(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment)
755 {
756   GtkWidget *scrollw;
757
758   scrollw = gtk_scrolled_window_new(hadjustment, vadjustment);
759   setup_scrolled_window(scrollw);
760   return scrollw;
761 }
762
763 /* Set a GtkScrolledWindow's scrollbar placement and add it to the list
764    of GtkScrolledWindows. */
765 static void
766 setup_scrolled_window(GtkWidget *scrollw)
767 {
768   set_scrollbar_placement_scrollw(scrollw);
769
770   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollw),
771                                    GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
772
773   scrolled_windows = g_list_append(scrolled_windows, scrollw);
774
775   /* Catch the "destroy" event on the widget, so that we remove it from
776      the list when it's destroyed. */
777   SIGNAL_CONNECT(scrollw, "destroy", forget_scrolled_window, NULL);
778 }
779
780 /* Remove a GtkScrolledWindow from the list of GtkScrolledWindows. */
781 static void
782 forget_scrolled_window(GtkWidget *scrollw, gpointer data _U_)
783 {
784   scrolled_windows = g_list_remove(scrolled_windows, scrollw);
785 }
786
787 /* Set the scrollbar placement of a GtkScrolledWindow based upon user
788    preference. */
789 static void
790 set_scrollbar_placement_scrollw(GtkWidget *scrollw)
791 {
792   if (prefs.gui_scrollbar_on_right) {
793     gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
794                                       GTK_CORNER_TOP_LEFT);
795   } else {
796     gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
797                                       GTK_CORNER_TOP_RIGHT);
798   }
799 }
800
801 static void
802 set_scrollbar_placement_cb(gpointer data, gpointer user_data _U_)
803 {
804   set_scrollbar_placement_scrollw((GtkWidget *)data);
805 }
806
807 /* Set the scrollbar placement of all GtkScrolledWindows based on
808    user preference. */
809 void
810 set_scrollbar_placement_all(void)
811 {
812   g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, NULL);
813 }
814
815 /* List of all CTrees/TreeViews, so we can globally set the line and
816  * expander style of all of them. */
817 static GList *trees;
818
819 static void setup_tree(GtkWidget *tree);
820 static void forget_tree(GtkWidget *tree, gpointer data);
821 static void set_tree_styles(GtkWidget *tree);
822
823 /* Create a Tree, give it the right styles, and remember it. */
824 #if GTK_MAJOR_VERSION < 2
825 GtkWidget *
826 ctree_new(gint columns, gint tree_column)
827 #else
828 GtkWidget *
829 tree_view_new(GtkTreeModel *model)
830 #endif
831 {
832   GtkWidget *tree;
833
834 #if GTK_MAJOR_VERSION < 2
835   tree = gtk_ctree_new(columns, tree_column);
836 #else
837   tree = gtk_tree_view_new_with_model(model);
838 #endif
839   setup_tree(tree);
840   return tree;
841 }
842
843 #if GTK_MAJOR_VERSION < 2
844 GtkWidget *
845 ctree_new_with_titles(gint columns, gint tree_column, gchar *titles[])
846 {
847   GtkWidget *tree;
848
849   tree = gtk_ctree_new_with_titles(columns, tree_column, titles);
850   setup_tree(tree);
851   return tree;
852 }
853 #endif
854
855 /* Set a Tree's styles and add it to the list of Trees. */
856 static void
857 setup_tree(GtkWidget *tree)
858 {
859   set_tree_styles(tree);
860
861   trees = g_list_append(trees, tree);
862
863   /* Catch the "destroy" event on the widget, so that we remove it from
864      the list when it's destroyed. */
865   SIGNAL_CONNECT(tree, "destroy", forget_tree, NULL);
866 }
867
868 /* Remove a Tree from the list of Trees. */
869 static void
870 forget_tree(GtkWidget *tree, gpointer data _U_)
871 {
872   trees = g_list_remove(trees, tree);
873 }
874
875 /* Set the styles of a Tree based upon user preferences. */
876 static void
877 set_tree_styles(GtkWidget *tree)
878 {
879 #if GTK_MAJOR_VERSION < 2
880   g_assert(prefs.gui_ptree_line_style >= GTK_CTREE_LINES_NONE &&
881            prefs.gui_ptree_line_style <= GTK_CTREE_LINES_TABBED);
882   gtk_ctree_set_line_style(GTK_CTREE(tree), prefs.gui_ptree_line_style);
883   g_assert(prefs.gui_ptree_expander_style >= GTK_CTREE_EXPANDER_NONE &&
884            prefs.gui_ptree_expander_style <= GTK_CTREE_EXPANDER_CIRCULAR);
885   gtk_ctree_set_expander_style(GTK_CTREE(tree),
886       prefs.gui_ptree_expander_style);
887 #else
888   g_assert(prefs.gui_altern_colors >= 0 && prefs.gui_altern_colors <= 1);
889   gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree),
890                                prefs.gui_altern_colors);
891 #endif
892 }
893
894 static void
895 set_tree_styles_cb(gpointer data, gpointer user_data _U_)
896 {
897   set_tree_styles((GtkWidget *)data);
898 }
899
900 /* Set the styles of all Trees based upon style values. */
901 void
902 set_tree_styles_all(void)
903 {
904   g_list_foreach(trees, set_tree_styles_cb, NULL);
905 }
906
907
908
909
910 #if GTK_MAJOR_VERSION < 2
911 /* convert variable argument list of values to array of strings (GTK2 -> GTK1) */
912 static void
913 simple_list_convert(gchar **ent, va_list ap)
914 {
915     int i;
916     char *s;
917
918     while( (i = va_arg(ap, int)) != -1 ) {
919         s = va_arg(ap, char *);
920         ent[i] = s;
921     }
922 }
923 #endif
924
925
926 /* append a row to the simple list */
927 /* use it like: simple_list_append(list, 0, "first", 1, "second", -1) */
928 void
929 simple_list_append(GtkWidget *list, ...)
930 {
931     va_list ap;
932
933 #if GTK_MAJOR_VERSION < 2
934     gchar      *ent[10];               /* new entry added in clist */
935 #else
936     GtkTreeIter iter;
937     GtkListStore *store;
938 #endif
939
940     va_start(ap, list);
941 #if GTK_MAJOR_VERSION < 2
942     simple_list_convert(ent, ap);
943     gtk_clist_append(GTK_CLIST(list), ent);
944 #else
945     store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
946     gtk_list_store_append(store, &iter);
947     gtk_list_store_set_valist(store, &iter, ap);
948 #endif
949     va_end(ap);
950 }
951
952 /* create a simple list widget */
953 GtkWidget *
954 simple_list_new(gint cols, gchar **titles) {
955     GtkWidget *plugins_list;
956 #if GTK_MAJOR_VERSION >= 2
957     int i;
958     GtkListStore *store;
959     GtkCellRenderer *renderer;
960     GtkTreeViewColumn *column;
961 #endif
962
963
964 #if GTK_MAJOR_VERSION < 2
965     plugins_list = gtk_clist_new_with_titles(cols, titles);
966     gtk_clist_set_selection_mode(GTK_CLIST(plugins_list), GTK_SELECTION_SINGLE);
967     gtk_clist_column_titles_passive(GTK_CLIST(plugins_list));
968     if(titles) {
969         gtk_clist_column_titles_show(GTK_CLIST(plugins_list));
970     } else {
971         gtk_clist_column_titles_hide(GTK_CLIST(plugins_list));
972     }
973     gtk_clist_set_column_auto_resize(GTK_CLIST(plugins_list), 0, TRUE);
974     gtk_clist_set_column_auto_resize(GTK_CLIST(plugins_list), 1, TRUE);
975 #else
976     g_assert(cols <= 10);
977     store = gtk_list_store_new(cols,
978         G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
979         G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
980     plugins_list = tree_view_new(GTK_TREE_MODEL(store));
981     g_object_unref(G_OBJECT(store));
982     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(plugins_list), (gboolean) titles);
983     for(i=0; i<cols; i++) {
984         renderer = gtk_cell_renderer_text_new();
985         column = gtk_tree_view_column_new_with_attributes(titles ? titles[i] : "", renderer,
986                                                           "text", i, NULL);
987         gtk_tree_view_column_set_sort_column_id(column, i);
988         gtk_tree_view_append_column(GTK_TREE_VIEW(plugins_list), column);
989     }
990 #endif
991
992     return plugins_list;
993 }
994
995 extern void
996 copy_to_clipboard(GString *str)  
997 {
998 #if (GTK_MAJOR_VERSION >= 2)
999         GtkClipboard    *cb;
1000
1001         cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);     /* Get the default clipboard */
1002         gtk_clipboard_set_text(cb, str->str, -1);            /* Copy the byte data into the clipboard */
1003 #else
1004         GtkWidget *window;
1005         GtkWidget *text;
1006
1007         window = window_new (GTK_WINDOW_TOPLEVEL,"");
1008         text = gtk_text_new (NULL, NULL);                 /* Create the GtkText widget */
1009         gtk_container_add (GTK_CONTAINER (window), text); /* Avoid a GTK assertion */
1010         gtk_widget_realize (text);   /* Realizing a widget creates a window for it, ready for us to insert some text */
1011         gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, str->str, -1);
1012         gtk_editable_select_region((GtkEditable *)text, 0, -1); /* Select ALL text */
1013         gtk_editable_copy_clipboard((GtkEditable *)text); /* Copy the byte data into the clipboard */
1014 #endif
1015 }