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)
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
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.
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.
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.
34 #include <gdk/gdkkeysyms.h>
36 #include <epan/prefs.h>
37 #include "epan/epan.h"
39 #include "../ui_util.h"
40 #include <wsutil/file_util.h>
42 #include "gtk/gtkglobals.h"
43 #include "gtk/gui_utils.h"
44 #include "gtk/recent.h"
46 #include "image/wsicon16.xpm"
49 #define WINDOW_GEOM_KEY "window_geom"
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);
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.
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.
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.
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
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.
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.
101 window_icon_realize_cb (GtkWidget *win, gpointer data _U_)
104 static GdkPixmap *icon_pmap = NULL;
105 static GdkBitmap *icon_mask = NULL;
108 style = gtk_widget_get_style (win);
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);
116 gdk_window_set_icon (win->window, NULL, icon_pmap, icon_mask);
121 /* Create a new window, of the specified type, with the specified title
122 (if any) and the Wireshark icon. */
124 window_new(GtkWindowType type, const gchar *title)
128 win = gtk_window_new(type);
130 gtk_window_set_title(GTK_WINDOW(win), title);
131 g_signal_connect(win, "realize", G_CALLBACK(window_icon_realize_cb), NULL);
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 */
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);
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! */
148 window_new_with_geom(GtkWindowType type, const gchar *title, const gchar *geom_name)
150 window_geometry_t geom;
151 GtkWidget *win = window_new(type, title);
153 g_object_set_data(G_OBJECT(win), WINDOW_GEOM_KEY, (gpointer)g_strdup(geom_name));
155 /* do we have a previously saved size and position of this window? */
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? */
162 geom.set_size = TRUE;
163 geom.set_maximized = FALSE; /* don't maximize until window is shown */
164 window_set_geometry(win, &geom);
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. */
175 splash_window_new(void)
179 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
180 gtk_window_set_decorated(GTK_WINDOW(win), FALSE);
182 /* set the initial position (must be done, before show is called!) */
183 gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER);
189 /* Present the created window on the screen. */
191 window_present(GtkWidget *win)
193 window_geometry_t geom;
196 /* present this window */
197 gtk_window_present(GTK_WINDOW(win));
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);
202 if(window_geom_load(name, &geom)) {
203 /* XXX - use prefs to select which values to set? */
205 geom.set_size = TRUE;
206 geom.set_maximized = TRUE;
207 window_set_geometry(win, &geom);
214 window_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer cancel_button)
216 g_return_val_if_fail (widget != NULL, FALSE);
217 g_return_val_if_fail (event != NULL, FALSE);
219 if (event->keyval == GDK_Escape) {
220 gtk_widget_activate(GTK_WIDGET(cancel_button));
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.
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!). */
240 window_set_cancel(GtkWidget *widget, GtkWidget *cancel_button)
242 g_signal_connect(widget, "key_press_event", G_CALLBACK(window_key_press_cb), cancel_button);
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)
250 g_signal_connect(bt, "clicked", G_CALLBACK(cb), win);
252 gtk_widget_grab_default(bt);
254 window_set_cancel(win, bt);
258 /* default callback handler for cancel button "clicked" signal */
259 void window_cancel_button_cb(GtkWidget *w _U_, gpointer data)
261 window_destroy(GTK_WIDGET(data));
265 /* default callback handler: the window managers X of the window was clicked (delete_event) */
267 window_delete_event_cb(GtkWidget *win, GdkEvent *event _U_, gpointer user_data _U_)
271 /* event handled, don't do anything else */
276 /* get the geometry of a window from window_new() */
278 window_get_geometry(GtkWidget *widget, window_geometry_t *geom)
281 GdkWindowState state;
283 /* Try to grab our geometry.
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.
290 The code below tries both routines, and picks the one that returns
291 the upper-left-most coordinates.
295 http://mail.gnome.org/archives/gtk-devel-list/2001-March/msg00289.html
296 http://www.gtk.org/faq/#AEN606
299 gdk_window_get_root_origin(widget->window,
302 if (gdk_window_get_deskrelative_origin(widget->window,
304 if (desk_x <= geom->x &&
312 /* XXX - Is this the "approved" method? */
313 gdk_window_get_size(widget->window,
317 state = gdk_window_get_state(widget->window);
318 geom->maximized = (state == GDK_WINDOW_STATE_MAXIMIZED);
322 /* set the geometry of a window from window_new() */
324 window_set_geometry(GtkWidget *widget, window_geometry_t *geom)
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),
334 if (geom->set_size) {
335 gtk_window_resize(GTK_WINDOW(widget),
336 /*gtk_widget_set_size_request(widget,*/
341 if(geom->set_maximized) {
342 if (geom->maximized) {
343 gdk_window_maximize(widget->window);
345 gdk_window_unmaximize(widget->window);
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;
356 /* save the window and it's current geometry into the geometry hashtable */
358 window_geom_save(const gchar *name, window_geometry_t *geom)
361 window_geometry_t *work;
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);
367 /* if we have an old one, remove and free it first */
368 work = g_hash_table_lookup(window_geom_hash, name);
370 g_hash_table_remove(window_geom_hash, name);
375 /* g_malloc and insert the new one */
376 work = g_malloc(sizeof(*geom));
378 key = g_strdup(name);
380 g_hash_table_insert(window_geom_hash, key, work);
384 /* load the desired geometry for this window from the geometry hashtable */
386 window_geom_load(const gchar *name, window_geometry_t *geom)
388 window_geometry_t *p;
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);
395 p = g_hash_table_lookup(window_geom_hash, name);
405 /* read in a single key value pair from the recent file into the geometry hashtable */
407 window_geom_recent_read_pair(const char *name, const char *key, const char *value)
409 window_geometry_t geom;
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;
419 geom.set_size = FALSE;
423 geom.set_maximized = FALSE;/* this is valid in GTK2 only */
424 geom.maximized = FALSE; /* this is valid in GTK2 only */
427 if (strcmp(key, "x") == 0) {
428 geom.x = strtol(value, NULL, 10);
430 } else if (strcmp(key, "y") == 0) {
431 geom.y = strtol(value, NULL, 10);
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;
444 geom.maximized = FALSE;
446 geom.set_maximized = TRUE;
449 * Silently ignore the bogus key. We shouldn't abort here,
450 * as this could be due to a corrupt recent file.
452 * XXX - should we print a message about this?
457 /* save / replace geometry in hashtable */
458 window_geom_save(name, &geom);
462 /* write all geometry values of all windows from the hashtable to the recent file */
464 window_geom_recent_write_all(gpointer rf)
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);
471 g_hash_table_foreach(window_geom_hash, write_recent_geom, rf);
476 window_destroy(GtkWidget *win)
478 window_geometry_t geom;
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);
488 name = g_object_get_data(G_OBJECT(win), WINDOW_GEOM_KEY);
490 window_geom_save(name, &geom);
491 g_free((gpointer)name);
495 gtk_widget_destroy(win);
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) {
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);
510 return gtk_image_new_from_pixmap (pixmap, bitmap);
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);
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
525 #define MAIN_WINDOW_NAME_KEY "main_window_name"
527 /* Set the name of the top-level window and its icon to the specified
530 set_main_window_name(const gchar *window_name)
532 gchar *old_window_name;
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));
540 update_main_window_name();
543 /* Update the name of the main window if the user-specified decoration
546 update_main_window_name(void)
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);
561 /* update the main window */
562 void main_window_update(void)
564 while (gtk_events_pending()) gtk_main_iteration();
567 /* exit the main window */
568 void main_window_exit(void)
575 /* quit a nested main window */
576 void main_window_nested_quit(void)
578 if (gtk_main_level() > 0)
582 /* quit the main window */
583 void main_window_quit(void)
590 typedef struct pipe_input_tag {
594 pipe_input_cb_t input_cb;
600 /* The timer has expired, see if there's stuff to read from the pipe,
601 if so, do the callback */
603 pipe_timer_cb(gpointer data)
607 gboolean result, result1;
609 pipe_input_t *pipe_input = data;
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");*/
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);
622 /* Get the child process exit status */
623 result1 = GetExitCodeProcess((HANDLE)*(pipe_input->child_process),
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
629 if (!result || avail > 0 || childstatus != STILL_ACTIVE) {
631 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: data avail");*/
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;
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 */
648 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: no data avail");*/
649 /* No data, stop now */
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);*/
661 /* Return false so that the old timer is not run again */
664 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: finished with iterations: %u, old timer", iterations);*/
666 /* we didn't stopped the old timer, so let it run */
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). */
677 pipe_input_cb(gpointer data, gint source _U_,
678 GdkInputCondition condition _U_)
680 pipe_input_t *pipe_input = data;
683 /* avoid reentrancy problems and stack overflow */
684 gtk_input_remove(pipe_input->pipe_input_id);
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,
698 void pipe_input_set_handler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb)
700 static pipe_input_t pipe_input;
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;
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
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);
716 pipe_input.pipe_input_id = gtk_input_add_full(source,
717 GDK_INPUT_READ|GDK_INPUT_EXCEPTION,
726 #endif /* HAVE_LIBPCAP */
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.
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.
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.
746 XXX - will this do the right thing on window systems other than X? */
748 reactivate_window(GtkWidget *win)
750 gdk_window_show(win->window);
751 gdk_window_raise(win->window);
754 /* List of all GtkScrolledWindows, so we can globally set the scrollbar
755 placement of all of them. */
756 static GList *scrolled_windows;
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);
762 /* Create a GtkScrolledWindow, set its scrollbar placement appropriately,
765 scrolled_window_new(GtkAdjustment *hadjustment, GtkAdjustment *vadjustment)
769 scrollw = gtk_scrolled_window_new(hadjustment, vadjustment);
770 setup_scrolled_window(scrollw);
774 /* Set a GtkScrolledWindow's scrollbar placement and add it to the list
775 of GtkScrolledWindows. */
777 setup_scrolled_window(GtkWidget *scrollw)
779 set_scrollbar_placement_scrollw(scrollw);
781 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollw),
782 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
784 scrolled_windows = g_list_append(scrolled_windows, scrollw);
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);
791 /* Remove a GtkScrolledWindow from the list of GtkScrolledWindows. */
793 forget_scrolled_window(GtkWidget *scrollw, gpointer data _U_)
795 scrolled_windows = g_list_remove(scrolled_windows, scrollw);
798 /* Set the scrollbar placement of a GtkScrolledWindow based upon user
801 set_scrollbar_placement_scrollw(GtkWidget *scrollw)
803 if (prefs.gui_scrollbar_on_right) {
804 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
805 GTK_CORNER_TOP_LEFT);
807 gtk_scrolled_window_set_placement(GTK_SCROLLED_WINDOW(scrollw),
808 GTK_CORNER_TOP_RIGHT);
813 set_scrollbar_placement_cb(gpointer data, gpointer user_data _U_)
815 set_scrollbar_placement_scrollw((GtkWidget *)data);
818 /* Set the scrollbar placement of all GtkScrolledWindows based on
821 set_scrollbar_placement_all(void)
823 g_list_foreach(scrolled_windows, set_scrollbar_placement_cb, NULL);
826 /* List of all CTrees/TreeViews, so we can globally set the line and
827 * expander style of all of them. */
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_);
835 /* Create a Tree, give it the right styles, and remember it. */
837 tree_view_new(GtkTreeModel *model)
841 tree = gtk_tree_view_new_with_model(model);
846 /* Set a Tree's styles and add it to the list of Trees. */
848 setup_tree(GtkWidget *tree)
850 set_tree_styles(tree);
852 trees = g_list_append(trees, tree);
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 );
860 /* Remove a Tree from the list of Trees. */
862 forget_tree(GtkWidget *tree, gpointer data _U_)
864 trees = g_list_remove(trees, tree);
867 /* Set the styles of a Tree based upon user preferences. */
869 set_tree_styles(GtkWidget *tree)
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);
877 set_tree_styles_cb(gpointer data, gpointer user_data _U_)
879 set_tree_styles((GtkWidget *)data);
882 /* Set the styles of all Trees based upon style values. */
884 set_tree_styles_all(void)
886 g_list_foreach(trees, set_tree_styles_cb, NULL);
891 /* append a row to the simple list */
892 /* use it like: simple_list_append(list, 0, "first", 1, "second", -1) */
894 simple_list_append(GtkWidget *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);
908 /* create a simple list widget */
910 simple_list_new(gint cols, const gchar **titles) {
911 GtkWidget *plugins_list;
914 GtkCellRenderer *renderer;
915 GtkTreeViewColumn *column;
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,
929 gtk_tree_view_column_set_sort_column_id(column, i);
930 gtk_tree_view_append_column(GTK_TREE_VIEW(plugins_list), column);
936 void render_as_url(GtkCellRenderer *cell)
938 g_object_set(cell, "foreground", "blue", NULL);
939 g_object_set(cell, "foreground-set", TRUE, NULL);
941 g_object_set(cell, "underline", PANGO_UNDERLINE_SINGLE, NULL);
942 g_object_set(cell, "underline-set", TRUE, NULL);
945 void simple_list_url_col(GtkWidget *list, gint col)
947 GtkTreeViewColumn *ul_column;
948 GList *renderers_list;
949 GtkCellRenderer *ul_renderer;
951 /* make the column look like a link ... */
952 ul_column = gtk_tree_view_get_column(GTK_TREE_VIEW(list), col);
954 renderers_list = gtk_tree_view_column_get_cell_renderers(ul_column);
956 if(renderers_list != NULL) {
957 /* it is simple list - there should be only one renderer */
958 ul_renderer = (GtkCellRenderer*)renderers_list->data;
960 render_as_url(ul_renderer);
962 g_list_free(renderers_list);
968 copy_to_clipboard(GString *str)
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 */
977 typedef struct _copy_binary_t {
983 copy_binary_t* create_copy_binary_t(const guint8* data, int len)
985 copy_binary_t* copy_data;
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));
995 static void destroy_copy_binary_t(copy_binary_t* copy_data) {
996 g_free(copy_data->data);
1001 void copy_binary_free_cb(GtkClipboard *clipboard _U_, gpointer user_data_or_owner)
1003 copy_binary_t* copy_data;
1004 copy_data = user_data_or_owner;
1005 destroy_copy_binary_t(copy_data);
1009 void copy_binary_get_cb(GtkClipboard *clipboard _U_, GtkSelectionData *selection_data, guint info _U_, gpointer user_data_or_owner)
1011 copy_binary_t* copy_data;
1013 copy_data = user_data_or_owner;
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);
1019 void copy_binary_to_clipboard(const guint8* data_p, int len)
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?
1028 copy_binary_t* copy_data;
1032 return; /* XXX would it be better to clear the clipboard? */
1034 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
1035 copy_data = create_copy_binary_t(data_p,len);
1037 ret = gtk_clipboard_set_with_data(cb,target_entry,1,
1038 copy_binary_get_cb, copy_binary_free_cb,copy_data);
1041 destroy_copy_binary_t(copy_data);
1046 * Create a new window title string with user-defined title preference.
1047 * (Or ignore it if unspecified).
1050 create_user_window_title(const gchar *caption)
1053 if (caption == NULL)
1054 return g_strdup("");
1056 /* no user-defined title specified */
1057 if ((prefs.gui_window_title == NULL) || (*prefs.gui_window_title == '\0'))
1058 return g_strdup(caption);
1060 return g_strdup_printf("%s %s", prefs.gui_window_title, caption);
1063 /* XXX move toggle_tree over from proto_draw.c to handle GTK+ 1 */
1065 tree_view_key_pressed_cb(GtkWidget *tree, GdkEventKey *event, gpointer user_data _U_)
1067 GtkTreeSelection* selection;
1070 GtkTreeModel* model;
1074 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1079 if(!gtk_tree_selection_get_selected (selection, &model, &iter)) {
1083 path = gtk_tree_model_get_path(model, &iter);
1087 expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree), path);
1089 switch (event->keyval) {
1092 /* subtree is expanded, collapse it */
1093 gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), path);
1096 /* No break - fall through to jumping to the parent */
1099 /* subtree is already collapsed, jump to parent node */
1100 if(! gtk_tree_model_iter_parent(model, &parent, &iter)) {
1103 path = gtk_tree_model_get_path(model, &parent);
1107 gtk_tree_view_set_cursor(GTK_TREE_VIEW(tree), path,
1108 NULL /* focus_column */,
1109 FALSE /* !start_editing */);
1114 /* try to expand the subtree */
1115 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, FALSE /* !open_all */);
1119 /* Reverse the current state. */
1121 gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), path);
1123 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, FALSE /* !open_all */);