add parameter dcerpc_info to PIDL_dissect_ipv?address()
[wireshark-wip.git] / ui / gtk / gui_utils.c
blob8fc19d0e76eca7be247b75136a6b3d14fb82f5f8
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)
6 * $Id$
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "config.h"
29 #include <string.h>
30 #include <locale.h>
32 #include <gtk/gtk.h>
33 #include <gdk/gdkkeysyms.h>
34 #if GTK_CHECK_VERSION(3,0,0)
35 # include <gdk/gdkkeysyms-compat.h>
36 #endif
38 #include <epan/prefs.h>
39 #include "epan/epan.h"
41 #include <epan/packet_info.h>
43 #include "../../globals.h"
45 #include "ui/recent.h"
46 #include "ui/ui_util.h"
48 #include <wsutil/file_util.h>
50 #include "ui/gtk/gtkglobals.h"
51 #include "ui/gtk/gui_utils.h"
52 #include "ui/gtk/font_utils.h"
53 #include "ui/gtk/color_utils.h"
55 #include "ui/gtk/old-gtk-compat.h"
57 #include "ui/gtk/wsicon.h"
59 #ifdef _WIN32
60 #include <windows.h>
61 #endif
63 #define WINDOW_GEOM_KEY "window_geom"
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.
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.
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.
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).
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.
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 Wireshark's windows has a resource name of "wireshark" and a class
103 name of "Wireshark". 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 Wireshark.
108 static void
109 window_icon_realize_cb(GtkWidget *win,
110 gpointer data _U_)
112 #ifndef _WIN32
113 GList *ws_icon_list = NULL;
114 GdkPixbuf *icon;
116 icon = gdk_pixbuf_new_from_inline(-1, wsicon_16_pb_data, FALSE, NULL);
117 ws_icon_list = g_list_append(ws_icon_list, icon);
118 icon = gdk_pixbuf_new_from_inline(-1, wsicon_32_pb_data, FALSE, NULL);
119 ws_icon_list = g_list_append(ws_icon_list, icon);
120 icon = gdk_pixbuf_new_from_inline(-1, wsicon_48_pb_data, FALSE, NULL);
121 ws_icon_list = g_list_append(ws_icon_list, icon);
122 icon = gdk_pixbuf_new_from_inline(-1, wsicon_64_pb_data, FALSE, NULL);
123 ws_icon_list = g_list_append(ws_icon_list, icon);
125 gtk_window_set_icon_list(GTK_WINDOW(win), ws_icon_list);
127 g_list_foreach(ws_icon_list, (GFunc)g_object_unref, NULL);
128 g_list_free(ws_icon_list);
130 /* set icon by name, this allows us to use even SVG icon if it is present */
131 gtk_window_set_icon_name(GTK_WINDOW(win), "wireshark");
132 #endif
136 /* Create a new window, of the specified type, with the specified title
137 (if any) and the Wireshark icon. */
138 GtkWidget *
139 window_new(GtkWindowType type,
140 const gchar *title)
142 GtkWidget *win;
144 win = gtk_window_new(type);
145 if (title != NULL)
146 gtk_window_set_title(GTK_WINDOW(win), title);
147 g_signal_connect(win, "realize", G_CALLBACK(window_icon_realize_cb), NULL);
149 /* XXX - which one is the correct default policy? or use a preference for this? */
150 /* GTK_WIN_POS_NONE, GTK_WIN_POS_CENTER or GTK_WIN_POS_MOUSE */
151 /* a lot of people dislike GTK_WIN_POS_MOUSE */
153 /* set the initial position (must be done, before show is called!) */
154 gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_NONE);
156 if (top_level) {
157 GdkScreen *default_screen;
158 gint x, y, n;
160 /* Ideally, new windows would open on the same monitor where the main
161 * window is located, but this doesn't happen when the main window is
162 * not located on the primary monitor. So, if there's more than 1
163 * monitor and Wireshark's main window isn't located on the primary
164 * one, attempt to improve the situation by at least displaying the new
165 * window somewhere on the same monitor, even if it won't be positioned
166 * the same way as it would be when it's on the primary monitor. Don't
167 * attempt to influence the placement on the primary monitor though,
168 * because that's probably the preferred placement strategy. But how
169 * to make window placement behave the same way on any monitor?
171 default_screen = gdk_screen_get_default();
172 n = gdk_screen_get_n_monitors(default_screen);
173 if (n > 1) {
174 gtk_window_get_position(GTK_WINDOW(top_level), &x, &y);
175 n = gdk_screen_get_monitor_at_point(default_screen, x, y);
176 if (n > 0)
177 gtk_window_move(GTK_WINDOW(win), x + 40, y + 30);
181 return win;
185 /* Same as window_new(), but will keep its geometry values (size, position, ...).
186 * Be sure to use window_present() and window_destroy() appropriately! */
187 GtkWidget *
188 window_new_with_geom(GtkWindowType type,
189 const gchar *title,
190 const gchar *geom_name)
192 window_geometry_t geom;
193 GtkWidget *win = window_new(type, title);
195 g_object_set_data(G_OBJECT(win), WINDOW_GEOM_KEY, (gpointer)g_strdup(geom_name));
197 /* do we have a previously saved size and position of this window? */
198 if(geom_name) {
199 /* It's a good idea to set the position and size of the window already here,
200 * as it's still invisible and won't "flicker the screen" while initially resizing. */
201 if(window_geom_load(geom_name, &geom)) {
202 /* XXX - use prefs to select which values to set? */
203 geom.set_pos = TRUE;
204 geom.set_size = TRUE;
205 geom.set_maximized = FALSE; /* don't maximize until window is shown */
206 window_set_geometry(win, &geom);
210 return win;
214 /* Create a new window for a splash screen; it's a main window, without decoration,
215 positioned in the center of the screen. */
216 GtkWidget *
217 splash_window_new(void)
219 GtkWidget *win;
221 win = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark");
222 gtk_window_set_decorated(GTK_WINDOW(win), FALSE);
224 /* set the initial position (must be done, before show is called!) */
225 gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_CENTER);
227 return win;
231 /* Present the created window on the screen. */
232 void
233 window_present(GtkWidget *win)
235 window_geometry_t geom;
236 const gchar *name;
238 /* present this window */
239 gtk_window_present(GTK_WINDOW(win));
241 /* do we have a previously saved size and position of this window? */
242 name = (const gchar *)g_object_get_data(G_OBJECT(win), WINDOW_GEOM_KEY);
243 if(name) {
244 if(window_geom_load(name, &geom)) {
245 /* XXX - use prefs to select which values to set? */
246 geom.set_pos = TRUE;
247 geom.set_size = TRUE;
248 geom.set_maximized = TRUE;
249 window_set_geometry(win, &geom);
255 static gboolean
256 window_key_press_cb(GtkWidget *widget,
257 GdkEventKey *event,
258 gpointer cancel_button)
260 g_return_val_if_fail(widget != NULL, FALSE);
261 g_return_val_if_fail(event != NULL, FALSE);
263 if (event->keyval == GDK_Escape) {
264 gtk_widget_activate(GTK_WIDGET(cancel_button));
265 return TRUE;
268 return FALSE;
272 /* Set the "key_press_event" signal for a top-level dialog window to
273 call a routine to activate the "Cancel" button for a dialog box if
274 the key being pressed is the <Esc> key.
276 XXX - there should be a GTK+ widget that'll do that for you, and
277 let you specify a "Cancel" button. It should also not impose
278 a requirement that there be a separator in the dialog box, as
279 the GtkDialog widget does; the visual convention that there's
280 such a separator between the rest of the dialog boxes and buttons
281 such as "OK" and "Cancel" is, for better or worse, not universal
282 (not even in GTK+ - look at the GtkFileSelection dialog!). */
283 static void
284 window_set_cancel(GtkWidget *widget,
285 GtkWidget *cancel_button)
287 g_signal_connect(widget, "key_press_event", G_CALLBACK(window_key_press_cb), cancel_button);
291 /* set the actions needed for the cancel "Close"/"Ok"/"Cancel" button that closes the window */
292 void
293 window_set_cancel_button(GtkWidget *win,
294 GtkWidget *bt,
295 window_cancel_button_fct cb)
297 if(cb)
298 g_signal_connect(bt, "clicked", G_CALLBACK(cb), win);
300 gtk_widget_grab_default(bt);
302 window_set_cancel(win, bt);
306 /* default callback handler for cancel button "clicked" signal */
307 void
308 window_cancel_button_cb(GtkWidget *w _U_,
309 gpointer data)
311 window_destroy(GTK_WIDGET(data));
315 /* default callback handler: the window managers X of the window was clicked (delete_event) */
316 gboolean
317 window_delete_event_cb(GtkWidget *win,
318 GdkEvent *event _U_,
319 gpointer user_data _U_)
321 window_destroy(win);
323 /* event handled, don't do anything else */
324 return TRUE;
328 /* get the geometry of a window from window_new() */
329 void
330 window_get_geometry(GtkWidget *widget,
331 window_geometry_t *geom)
333 GdkWindowState state;
334 GdkWindow *widget_window;
336 /* Try to grab our geometry.
338 GTK+ provides two routines to get a window's position relative
339 to the X root window. If I understand the documentation correctly,
340 gdk_window_get_deskrelative_origin applies mainly to Enlightenment
341 and gdk_window_get_root_origin applies for all other WMs.
343 The code below tries both routines, and picks the one that returns
344 the upper-left-most coordinates.
346 More info at:
348 http://mail.gnome.org/archives/gtk-devel-list/2001-March/msg00289.html
349 http://www.gtk.org/faq/#AEN606
351 As gdk_window_get_deskrelative_origin() is deprecated it has been removed 2011-07-24.
354 memset(geom, 0, sizeof(window_geometry_t));
356 widget_window = gtk_widget_get_window(widget);
358 gdk_window_get_root_origin(widget_window,
359 &geom->x,
360 &geom->y);
362 /* XXX - Is this the "approved" method? */
363 #if GTK_CHECK_VERSION(2,24,0)
364 geom->width = gdk_window_get_width(widget_window);
365 geom->height = gdk_window_get_height(widget_window);
366 #else
367 gdk_drawable_get_size(widget_window,
368 &geom->width,
369 &geom->height);
370 #endif
371 state = gdk_window_get_state(widget_window);
372 geom->maximized = ((state & GDK_WINDOW_STATE_MAXIMIZED) != 0);
376 #ifdef _WIN32
377 /* Ensure Wireshark isn't obscured by the system taskbar (or other desktop toolbars).
378 * Resolves https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=3034 */
379 static void
380 window_adjust_if_obscured(window_geometry_t *geom)
382 MONITORINFO MonitorInfo;
383 HMONITOR hMonitor;
384 POINT pt, vs;
385 DWORD dwFlags = MONITOR_DEFAULTTONEAREST; /* MONITOR_DEFAULTTOPRIMARY? */
388 * Get the virtual screen's top-left coordinates so we can reliably
389 * determine which monitor we're dealing with. See also:
390 * http://msdn.microsoft.com/en-us/library/windows/desktop/dd145136%28v=vs.85%29.aspx
392 vs.x = GetSystemMetrics(SM_XVIRTUALSCREEN);
393 vs.y = GetSystemMetrics(SM_YVIRTUALSCREEN);
394 pt.x = geom->x + vs.x;
395 pt.y = geom->y + vs.y;
396 MonitorInfo.cbSize = sizeof(MONITORINFO);
397 hMonitor = MonitorFromPoint(pt, dwFlags);
398 if (GetMonitorInfo(hMonitor, &MonitorInfo)) {
399 if (pt.x < MonitorInfo.rcWork.left)
400 geom->x += MonitorInfo.rcWork.left - pt.x;
401 if (pt.y < MonitorInfo.rcWork.top)
402 geom->y += MonitorInfo.rcWork.top - pt.y;
405 #endif
407 /* set the geometry of a window from window_new() */
408 void
409 window_set_geometry(GtkWidget *widget,
410 window_geometry_t *geom)
412 GdkScreen *default_screen;
413 GdkRectangle viewable_area;
414 gint monitor_num;
416 /* as we now have the geometry from the recent file, set it */
417 /* if the window was minimized, x and y are -32000 (at least on Win32) */
418 if (geom->set_pos && geom->x != -32000 && geom->y != -32000) {
419 /* Per Wireshark bug #553, GTK has a problem on MS Windows
420 * where the upper-left corner of the window may appear off
421 * screen when when a single desktop spans multiple monitors
422 * of different resolutions and positions relative to each
423 * other.
425 * If the requested (x,y) position isn't within the monitor's
426 * viewable area, change it to the viewable area's (x,y). */
427 default_screen = gdk_screen_get_default();
428 monitor_num = gdk_screen_get_monitor_at_point(default_screen,
429 geom->x, geom->y);
430 gdk_screen_get_monitor_geometry(default_screen, monitor_num,
431 &viewable_area);
432 if(geom->x < viewable_area.x || geom->x > (viewable_area.x + viewable_area.width))
433 geom->x = viewable_area.x;
435 if(geom->y < viewable_area.y || geom->y > (viewable_area.y + viewable_area.height))
436 geom->y = viewable_area.y;
438 #ifdef _WIN32
439 window_adjust_if_obscured(geom);
440 #endif
442 gtk_window_move(GTK_WINDOW(widget),
443 geom->x,
444 geom->y);
447 if (geom->set_size) {
448 gtk_window_resize(GTK_WINDOW(widget),
449 /*gtk_widget_set_size_request(widget,*/
450 geom->width,
451 geom->height);
454 if(geom->set_maximized) {
455 if (geom->maximized) {
456 gdk_window_maximize(gtk_widget_get_window(widget));
457 } else {
458 gdk_window_unmaximize(gtk_widget_get_window(widget));
463 void
464 window_destroy(GtkWidget *win)
466 window_geometry_t geom;
467 const gchar *name;
469 if (!win)
470 return;
472 /* get_geometry must be done *before* destroy is running, as the window geometry
473 * cannot be retrieved at destroy time (so don't use event "destroy" for this) */
474 /* ...and don't do this at all, if we currently have no GdkWindow (e.g. if the
475 * GtkWidget is hidden) */
476 if(gtk_widget_get_has_window(win) && gtk_widget_get_visible(win)) {
477 window_get_geometry(win, &geom);
479 name = (const gchar *)g_object_get_data(G_OBJECT(win), WINDOW_GEOM_KEY);
480 if(name) {
481 window_geom_save(name, &geom);
482 g_free((gpointer)name);
486 gtk_widget_destroy(win);
489 static GtkWidget *
490 _gtk_image_new_from_pixbuf_unref(GdkPixbuf *pixbuf) {
491 GtkWidget *widget;
493 widget = gtk_image_new_from_pixbuf(pixbuf);
494 g_object_unref(pixbuf);
495 return widget;
498 /* convert an xpm to a GtkWidget */
499 GtkWidget *
500 xpm_to_widget(const char **xpm) {
501 GdkPixbuf *pixbuf;
503 pixbuf = gdk_pixbuf_new_from_xpm_data(xpm);
504 return _gtk_image_new_from_pixbuf_unref(pixbuf);
507 /* Convert an pixbuf data to a GtkWidget */
508 /* Data should be created with "gdk-pixbuf-csource --raw" */
509 GtkWidget *
510 pixbuf_to_widget(const guint8 *pb_data) {
511 GdkPixbuf *pixbuf;
513 pixbuf = gdk_pixbuf_new_from_inline(-1, pb_data, FALSE, NULL);
514 return _gtk_image_new_from_pixbuf_unref(pixbuf);
518 * Alert box for an invalid display filter expression.
519 * Assumes "dfilter_error_msg" has been set by "dfilter_compile()" to the
520 * error message for the filter.
522 * XXX - should this have a "Help" button that pops up the display filter
523 * help?
525 void
526 bad_dfilter_alert_box(GtkWidget *parent,
527 const char *dftext)
529 GtkWidget *msg_dialog;
531 msg_dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
532 GTK_DIALOG_DESTROY_WITH_PARENT,
533 GTK_MESSAGE_ERROR,
534 GTK_BUTTONS_OK,
535 "The filter expression \"%s\" isn't a valid display filter. (%s)",
536 dftext, dfilter_error_msg);
537 gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
538 "See the help for a description of the display filter syntax.");
539 gtk_dialog_run(GTK_DIALOG(msg_dialog));
540 gtk_widget_destroy(msg_dialog);
543 /* update the main window */
544 void
545 main_window_update(void)
547 while (gtk_events_pending())
548 gtk_main_iteration();
551 #ifdef HAVE_LIBPCAP
553 /* quit a nested main window */
554 void
555 main_window_nested_quit(void)
557 if (gtk_main_level() > 0)
558 gtk_main_quit();
561 /* quit the main window */
562 void
563 main_window_quit(void)
565 gtk_main_quit();
568 typedef struct pipe_input_tag {
569 gint source;
570 gpointer user_data;
571 int *child_process;
572 pipe_input_cb_t input_cb;
573 guint pipe_input_id;
574 #ifdef _WIN32
575 #else
576 GIOChannel *channel;
577 #endif
578 } pipe_input_t;
581 #ifdef _WIN32
582 /* The timer has expired, see if there's stuff to read from the pipe,
583 if so, do the callback */
584 static gboolean
585 pipe_timer_cb(gpointer data)
587 HANDLE handle;
588 DWORD avail = 0;
589 gboolean result;
590 DWORD childstatus;
591 pipe_input_t *pipe_input = data;
592 gint iterations = 0;
595 /* try to read data from the pipe only 5 times, to avoid blocking */
596 while(iterations < 5) {
597 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: new iteration");*/
599 /* Oddly enough although Named pipes don't work on win9x,
600 PeekNamedPipe does !!! */
601 handle = (HANDLE)_get_osfhandle(pipe_input->source);
602 result = PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL);
604 /* Get the child process exit status */
605 GetExitCodeProcess((HANDLE)*(pipe_input->child_process), &childstatus);
607 /* If the Peek returned an error, or there are bytes to be read
608 or the childwatcher thread has terminated then call the normal
609 callback */
610 if (!result || avail > 0 || childstatus != STILL_ACTIVE) {
612 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: data avail");*/
614 if(pipe_input->pipe_input_id != 0) {
615 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: stop timer");*/
616 /* avoid reentrancy problems and stack overflow */
617 g_source_remove(pipe_input->pipe_input_id);
618 pipe_input->pipe_input_id = 0;
621 /* And call the real handler */
622 if (!pipe_input->input_cb(pipe_input->source, pipe_input->user_data)) {
623 g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: input pipe closed, iterations: %u", iterations);
624 /* pipe closed, return false so that the old timer is not run again */
625 return FALSE;
628 else {
629 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: no data avail");*/
630 /* No data, stop now */
631 break;
634 iterations++;
637 if(pipe_input->pipe_input_id == 0) {
638 /* restore pipe handler */
639 pipe_input->pipe_input_id = g_timeout_add(200, pipe_timer_cb, data);
640 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: finished with iterations: %u, new timer", iterations);*/
642 /* Return false so that the old timer is not run again */
643 return FALSE;
644 } else {
645 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_timer_cb: finished with iterations: %u, old timer", iterations);*/
647 /* we didn't stopped the old timer, so let it run */
648 return TRUE;
652 #else /* _WIN32 */
654 /* There's stuff to read from the sync pipe, meaning the child has sent
655 us a message, or the sync pipe has closed, meaning the child has
656 closed it (perhaps because it exited). */
657 static gboolean
658 pipe_input_cb(GIOChannel *source _U_,
659 GIOCondition condition _U_,
660 gpointer data)
662 pipe_input_t *pipe_input = (pipe_input_t *)data;
665 /* avoid reentrancy problems and stack overflow */
666 g_source_remove(pipe_input->pipe_input_id);
668 if (pipe_input->input_cb(pipe_input->source, pipe_input->user_data)) {
669 /* restore pipe handler */
670 pipe_input->pipe_input_id = g_io_add_watch_full(pipe_input->channel,
671 G_PRIORITY_HIGH,
672 (GIOCondition)(G_IO_IN|G_IO_ERR|G_IO_HUP),
673 pipe_input_cb,
674 pipe_input,
675 NULL);
677 return TRUE;
679 #endif
681 void
682 pipe_input_set_handler(gint source,
683 gpointer user_data,
684 int *child_process,
685 pipe_input_cb_t input_cb)
687 static pipe_input_t pipe_input;
689 pipe_input.source = source;
690 pipe_input.child_process = child_process;
691 pipe_input.user_data = user_data;
692 pipe_input.input_cb = input_cb;
694 #ifdef _WIN32
695 /* Tricky to use pipes in win9x, as no concept of wait. NT can
696 do this but that doesn't cover all win32 platforms. GTK can do
697 this but doesn't seem to work over processes. Attempt to do
698 something similar here, start a timer and check for data on every
699 timeout. */
700 /*g_log(NULL, G_LOG_LEVEL_DEBUG, "pipe_input_set_handler: new");*/
701 pipe_input.pipe_input_id = g_timeout_add(200, pipe_timer_cb, &pipe_input);
702 #else
703 pipe_input.channel = g_io_channel_unix_new(source);
704 g_io_channel_set_encoding(pipe_input.channel, NULL, NULL);
705 pipe_input.pipe_input_id = g_io_add_watch_full(pipe_input.channel,
706 G_PRIORITY_HIGH,
707 (GIOCondition)(G_IO_IN|G_IO_ERR|G_IO_HUP),
708 pipe_input_cb,
709 &pipe_input,
710 NULL);
711 #endif
715 #endif /* HAVE_LIBPCAP */
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.
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.
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.
735 XXX - will this do the right thing on window systems other than X? */
736 void
737 reactivate_window(GtkWidget *win)
739 GdkWindow *win_window;
741 win_window = gtk_widget_get_window(win);
743 gdk_window_show(win_window);
744 gdk_window_raise(win_window);
747 /* List of all GtkScrolledWindows, so we can globally set the scrollbar
748 placement of all of them. */
749 static GList *scrolled_windows;
751 static void setup_scrolled_window(GtkWidget *scrollw);
752 static void forget_scrolled_window(GtkWidget *scrollw, gpointer data);
754 /* Create a GtkScrolledWindow, set its scrollbar placement appropriately,
755 and remember it. */
756 GtkWidget *
757 scrolled_window_new(GtkAdjustment *hadjustment,
758 GtkAdjustment *vadjustment)
760 GtkWidget *scrollw;
762 scrollw = gtk_scrolled_window_new(hadjustment, vadjustment);
763 setup_scrolled_window(scrollw);
764 return scrollw;
767 /* Set a GtkScrolledWindow's scrollbar placement and add it to the list
768 of GtkScrolledWindows. */
769 static void
770 setup_scrolled_window(GtkWidget *scrollw)
772 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollw),
773 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
775 scrolled_windows = g_list_append(scrolled_windows, scrollw);
777 /* Catch the "destroy" event on the widget, so that we remove it from
778 the list when it's destroyed. */
779 g_signal_connect(scrollw, "destroy", G_CALLBACK(forget_scrolled_window), NULL);
782 /* Remove a GtkScrolledWindow from the list of GtkScrolledWindows. */
783 static void
784 forget_scrolled_window(GtkWidget *scrollw,
785 gpointer data _U_)
787 scrolled_windows = g_list_remove(scrolled_windows, scrollw);
790 /* List of all CTrees/TreeViews, so we can globally set the line and
791 * expander style of all of them. */
792 static GList *trees;
794 static void setup_tree(GtkWidget *tree);
795 static void forget_tree(GtkWidget *tree, gpointer data);
796 static void set_tree_styles(GtkWidget *tree);
797 static gboolean tree_view_key_pressed_cb(GtkWidget *tree, GdkEventKey *event, gpointer user_data _U_);
799 /* Create a Tree, give it the right styles, and remember it. */
800 GtkWidget *
801 tree_view_new(GtkTreeModel *model)
803 GtkWidget *tree;
805 tree = gtk_tree_view_new_with_model(model);
806 setup_tree(tree);
807 return tree;
810 /* Set a Tree's styles and add it to the list of Trees. */
811 static void
812 setup_tree(GtkWidget *tree)
814 set_tree_styles(tree);
816 trees = g_list_append(trees, tree);
818 /* Catch the "destroy" event on the widget, so that we remove it from
819 the list when it's destroyed. */
820 g_signal_connect(tree, "destroy", G_CALLBACK(forget_tree), NULL);
821 g_signal_connect(tree, "key-press-event", G_CALLBACK(tree_view_key_pressed_cb), NULL );
824 /* Remove a Tree from the list of Trees. */
825 static void
826 forget_tree(GtkWidget *tree,
827 gpointer data _U_)
829 trees = g_list_remove(trees, tree);
832 /* Set the styles of a Tree based upon user preferences. */
833 static void
834 set_tree_styles(GtkWidget *tree)
836 g_assert(prefs.gui_altern_colors >= 0 && prefs.gui_altern_colors <= 1);
837 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree),
838 prefs.gui_altern_colors);
841 static void
842 set_tree_styles_cb(gpointer data,
843 gpointer user_data _U_)
845 set_tree_styles((GtkWidget *)data);
848 /* Set the styles of all Trees based upon style values. */
849 void
850 set_tree_styles_all(void)
852 g_list_foreach(trees, set_tree_styles_cb, NULL);
855 /* Move the currently-selected item in a list store up or down one position. */
856 gboolean
857 tree_view_list_store_move_selection(GtkTreeView *tree,
858 gboolean move_up)
860 GtkTreeIter from, to;
861 GtkTreeModel *model;
862 GtkTreeSelection *sel;
863 GtkTreePath *path_from, *path_to;
865 sel = gtk_tree_view_get_selection(tree);
866 if (!gtk_tree_selection_get_selected(sel, &model, &from)) {
867 return FALSE;
870 path_from = gtk_tree_model_get_path(model, &from);
871 if (!path_from) {
872 return FALSE;
875 path_to = gtk_tree_path_copy(path_from);
876 /* XXX - Why does one return void and the other return a gboolean? */
877 if (move_up) {
878 gtk_tree_path_prev(path_to);
879 } else {
880 gtk_tree_path_next(path_to);
883 if (gtk_tree_path_compare(path_from, path_to) == 0) {
884 gtk_tree_path_free(path_from);
885 gtk_tree_path_free(path_to);
886 return FALSE;
889 gtk_tree_model_get_iter(model, &to, path_to);
890 gtk_list_store_swap(GTK_LIST_STORE(model), &from, &to);
891 gtk_tree_path_free(path_from);
892 gtk_tree_path_free(path_to);
893 return TRUE;
896 /* Find the selected row number in a list store. */
897 gint
898 tree_view_list_store_get_selected_row(GtkTreeView *tree) {
899 GtkTreeIter iter;
900 GtkTreeModel *model;
901 GtkTreeSelection *sel;
902 GtkTreePath *path;
903 gchar *path_str;
904 gint row;
906 sel = gtk_tree_view_get_selection(tree);
907 if (!gtk_tree_selection_get_selected(sel, &model, &iter)) {
908 return -1;
911 path = gtk_tree_model_get_path(model, &iter);
912 if (!path) {
913 return FALSE;
916 path_str = gtk_tree_path_to_string(path);
917 gtk_tree_path_free(path);
919 row = (gint)strtol(path_str, NULL, 10);
920 g_free(path_str);
922 return row;
925 /* append a row to the simple list */
926 /* use it like: simple_list_append(list, 0, "first", 1, "second", -1) */
927 void
928 simple_list_append(GtkWidget *list,
929 ...)
931 va_list ap;
933 GtkTreeIter iter;
934 GtkListStore *store;
936 va_start(ap, list);
937 store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list)));
938 gtk_list_store_append(store, &iter);
939 gtk_list_store_set_valist(store, &iter, ap);
940 va_end(ap);
943 /* create a simple list widget */
944 GtkWidget *
945 simple_list_new(gint cols,
946 const gchar **titles) {
947 GtkWidget *plugins_list;
948 int i;
949 GtkListStore *store;
950 GtkCellRenderer *renderer;
951 GtkTreeViewColumn *column;
954 g_assert(cols <= 10);
955 store = gtk_list_store_new(cols,
956 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
957 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
958 plugins_list = tree_view_new(GTK_TREE_MODEL(store));
959 g_object_unref(G_OBJECT(store));
960 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(plugins_list), (titles != NULL));
961 for(i=0; i<cols; i++) {
962 renderer = gtk_cell_renderer_text_new();
963 column = gtk_tree_view_column_new_with_attributes(titles ? titles[i] : "", renderer,
964 "text", i, NULL);
965 gtk_tree_view_column_set_sort_column_id(column, i);
966 gtk_tree_view_append_column(GTK_TREE_VIEW(plugins_list), column);
969 return plugins_list;
972 void
973 render_as_url(GtkCellRenderer *cell)
975 g_object_set(cell, "foreground", "blue", NULL);
976 g_object_set(cell, "foreground-set", TRUE, NULL);
978 g_object_set(cell, "underline", PANGO_UNDERLINE_SINGLE, NULL);
979 g_object_set(cell, "underline-set", TRUE, NULL);
982 void
983 simple_list_url_col(GtkWidget *list,
984 gint col)
986 GtkTreeViewColumn *ul_column;
987 GList *renderers_list;
988 GtkCellRenderer *ul_renderer;
990 /* make the column look like a link ... */
991 ul_column = gtk_tree_view_get_column(GTK_TREE_VIEW(list), col);
993 renderers_list = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(ul_column));
995 if(renderers_list != NULL) {
996 /* it is simple list - there should be only one renderer */
997 ul_renderer = (GtkCellRenderer*)renderers_list->data;
999 render_as_url(ul_renderer);
1001 g_list_free(renderers_list);
1006 void
1007 copy_to_clipboard(GString *str)
1009 GtkClipboard *cb;
1011 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
1012 gtk_clipboard_set_text(cb, str->str, -1); /* Copy the byte data into the clipboard */
1016 typedef struct _copy_binary_t {
1017 guint8 *data;
1018 int len;
1019 } copy_binary_t;
1021 static copy_binary_t *
1022 create_copy_binary_t(const guint8 *data,
1023 int len)
1025 copy_binary_t* copy_data;
1027 g_assert(len > 0);
1028 copy_data = g_new(copy_binary_t,1);
1029 copy_data->data = g_new(guint8,len);
1030 copy_data->len = len;
1031 memcpy(copy_data->data,data,len * sizeof(guint8));
1032 return copy_data;
1035 static void
1036 destroy_copy_binary_t(copy_binary_t *copy_data) {
1037 g_free(copy_data->data);
1038 g_free(copy_data);
1041 static void
1042 copy_binary_free_cb(GtkClipboard *clipboard _U_,
1043 gpointer user_data_or_owner)
1045 destroy_copy_binary_t((copy_binary_t*)user_data_or_owner);
1048 static void
1049 copy_binary_get_cb(GtkClipboard *clipboard _U_,
1050 GtkSelectionData *selection_data,
1051 guint info _U_,
1052 gpointer user_data_or_owner)
1054 copy_binary_t* copy_data;
1056 copy_data = (copy_binary_t*)user_data_or_owner;
1058 /* Just do a dumb set as binary data */
1059 gtk_selection_data_set(selection_data, GDK_NONE, 8, copy_data->data, copy_data->len);
1062 void
1063 copy_binary_to_clipboard(const guint8 *data_p,
1064 int len)
1066 static GtkTargetEntry target_entry[] = {
1067 {(char *)"application/octet-stream", 0, 0}};
1068 /* XXX - this is not understood by most applications,
1069 * but can be pasted into the better hex editors - is
1070 * there something better that we can do?
1073 GtkClipboard *cb;
1074 copy_binary_t *copy_data;
1075 gboolean ret;
1077 if(len <= 0) {
1078 return; /* XXX would it be better to clear the clipboard? */
1080 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */
1081 copy_data = create_copy_binary_t(data_p,len);
1083 ret = gtk_clipboard_set_with_data(cb,target_entry,1,
1084 copy_binary_get_cb, copy_binary_free_cb,copy_data);
1086 if(!ret) {
1087 destroy_copy_binary_t(copy_data);
1092 * Create a new window title string with user-defined title preference.
1093 * (Or ignore it if unspecified).
1095 gchar *
1096 create_user_window_title(const gchar *caption)
1098 /* fail-safe */
1099 if (caption == NULL)
1100 return g_strdup("");
1102 /* no user-defined title specified */
1103 if ((prefs.gui_window_title == NULL) || (*prefs.gui_window_title == '\0'))
1104 return g_strdup(caption);
1106 return g_strdup_printf("%s [%s]", caption, prefs.gui_window_title);
1110 * Set the title of a window based on a supplied caption and the
1111 * display name for the capture file.
1113 * XXX - should this include the user preference as well?
1115 void
1116 set_window_title(GtkWidget *win,
1117 const gchar *caption)
1119 char *title;
1120 char *display_name;
1122 display_name = cf_get_display_name(&cfile);
1123 title = g_strdup_printf("%s: %s", caption, display_name);
1124 g_free(display_name);
1125 gtk_window_set_title(GTK_WINDOW(win), title);
1126 g_free(title);
1130 * Collapse row and his children
1132 static void
1133 tree_collapse_row_with_children(GtkTreeView *tree_view, GtkTreeModel *model, GtkTreePath *path,
1134 GtkTreeIter *iter)
1136 GtkTreeIter child;
1138 if (gtk_tree_view_row_expanded(tree_view, path)) {
1139 if (gtk_tree_model_iter_children(model, &child, iter)) {
1140 gtk_tree_path_down(path);
1142 do {
1144 if (gtk_tree_view_row_expanded(tree_view, path)) {
1145 tree_collapse_row_with_children(tree_view, model, path, &child);
1148 gtk_tree_path_next(path);
1149 } while (gtk_tree_model_iter_next(model, &child));
1151 gtk_tree_path_up(path);
1153 gtk_tree_view_collapse_row(tree_view, path);
1158 void
1159 tree_collapse_path_all(GtkTreeView *tree_view, GtkTreePath *path)
1161 GtkTreeIter iter;
1162 GtkTreeModel *model;
1164 model = gtk_tree_view_get_model(tree_view);
1165 gtk_tree_model_get_iter(model, &iter, path);
1167 tree_collapse_row_with_children(tree_view, model, path, &iter);
1171 * This callback is invoked when keyboard focus is within either
1172 * the packetlist view or the detail view. The keystrokes processed
1173 * within this callback are attempting to modify the detail view.
1174 * Within the detail view we special case the Left Arrow, Backspace
1175 * and Enter keys depending on the state of the expander (if any)
1176 * for the item in focus.
1178 * Returning FALSE allows processing of the original key_press_event
1179 * by other callbacks. Left/Right scrolling of the packetlist
1180 * view and expanding/collapsing of the detail view lists is
1181 * handled by the default GtkTreeView key-press-event call back.
1183 * XXX - Would an improved version of this callback test to see which
1184 * of the two GtkTreeView lists has focus? Left/Right scrolling of
1185 * the packetlist is currently not optimal. It will take several
1186 * right or left keypress events before the packetlist responds.
1187 * The problem appears to be that the focus is on a particular cell
1188 * within the highlighted row cell (like a spreadsheet). Scrolling
1189 * of the view right or left will not occur until the focus is
1190 * moved to a cell off the left or right edge of the packet list
1191 * view. Also TAB/SHIFT-TAB events can move keyboard focus to
1192 * the packetlist header where there is currently visual hint
1193 * a header cell has focus.
1195 static gboolean
1196 tree_view_key_pressed_cb(GtkWidget *tree,
1197 GdkEventKey *event,
1198 gpointer user_data _U_)
1200 GtkTreeSelection *selection;
1201 GtkTreeIter iter;
1202 GtkTreeIter parent;
1203 GtkTreeModel *model;
1204 GtkTreePath *path;
1205 gboolean expanded, expandable;
1206 int rc = FALSE;
1208 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree));
1209 if(!selection) {
1210 return FALSE;
1213 if(!gtk_tree_selection_get_selected(selection, &model, &iter)) {
1214 return FALSE;
1217 path = gtk_tree_model_get_path(model, &iter);
1218 if(!path) {
1219 return FALSE;
1222 /* Always FALSE when we're in the packet list (at least until we add sub-packets) */
1223 expanded = gtk_tree_view_row_expanded(GTK_TREE_VIEW(tree), path);
1224 expandable = gtk_tree_model_iter_has_child(model, &iter);
1226 switch (event->keyval) {
1227 case GDK_Left:
1228 if(expanded) {
1229 /* Subtree is expanded. Collapse it. */
1230 if (event->state & GDK_SHIFT_MASK)
1232 tree_collapse_row_with_children(GTK_TREE_VIEW(tree), model, path, &iter);
1234 else
1235 gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), path);
1236 rc = TRUE;
1237 break;
1239 /* No break - fall through to jumping to the parent */
1240 case GDK_BackSpace:
1241 if (!expanded) {
1242 /* subtree is already collapsed, jump to parent node */
1243 if(! gtk_tree_model_iter_parent(model, &parent, &iter)) {
1244 rc = FALSE;
1245 break;
1247 gtk_tree_path_free(path);
1248 path = gtk_tree_model_get_path(model, &parent);
1249 if(!path) {
1250 return FALSE;
1252 gtk_tree_view_set_cursor(GTK_TREE_VIEW(tree), path,
1253 NULL /* focus_column */,
1254 FALSE /* !start_editing */);
1255 rc = TRUE;
1256 break;
1258 break;
1259 case GDK_Right:
1260 if (expandable) {
1261 /* We have a subtree. Try to expand it. */
1262 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, FALSE /* !open_all */);
1263 rc = TRUE;
1264 break;
1265 } else {
1266 rc = FALSE;
1267 break;
1269 case GDK_Return:
1270 case GDK_KP_Enter:
1271 /* Reverse the current state. */
1272 if (expanded)
1273 gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), path);
1274 else
1275 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, FALSE /* !open_all */);
1276 rc = TRUE;
1277 break;
1280 if(path) {
1281 gtk_tree_path_free(path);
1283 return rc;
1286 void
1287 switch_to_fixed_col(GtkTreeView *view)
1289 gint size;
1290 GtkTreeViewColumn *column;
1291 GList *columns, *list;
1293 columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(view));
1294 list = columns;
1295 while(columns) {
1296 column = (GtkTreeViewColumn *)columns->data;
1297 size = gtk_tree_view_column_get_width(column);
1298 gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_FIXED);
1299 if (size > gtk_tree_view_column_get_fixed_width(column))
1300 gtk_tree_view_column_set_fixed_width(column, size);
1301 columns = g_list_next(columns);
1303 g_list_free(list);
1305 gtk_tree_view_set_fixed_height_mode(view, TRUE);
1308 gint
1309 get_default_col_size(GtkWidget *view,
1310 const gchar *str)
1312 PangoLayout *layout;
1313 gint col_width;
1315 layout = gtk_widget_create_pango_layout(view, str);
1316 pango_layout_get_pixel_size(layout,
1317 &col_width, /* width */
1318 NULL); /* height */
1319 g_object_unref(G_OBJECT(layout));
1320 /* Add a single character's width to get some spacing between columns */
1321 return col_width + (pango_font_description_get_size(user_font_get_regular()) / PANGO_SCALE);
1326 * This function can be called from gtk_tree_view_column_set_cell_data_func()
1327 * the user data must be the column number.
1328 * Present floats with two decimals
1330 void
1331 float_data_func(GtkTreeViewColumn *column _U_,
1332 GtkCellRenderer *renderer,
1333 GtkTreeModel *model,
1334 GtkTreeIter *iter,
1335 gpointer user_data)
1337 gfloat float_val;
1338 gchar buf[20];
1339 char *savelocale;
1341 /* the col to get data from is in userdata */
1342 gint float_col = GPOINTER_TO_INT(user_data);
1344 gtk_tree_model_get(model, iter, float_col, &float_val, -1);
1346 /* save the current locale */
1347 savelocale = setlocale(LC_NUMERIC, NULL);
1348 /* switch to "C" locale to avoid problems with localized decimal separators
1349 * in g_snprintf("%f") functions
1351 setlocale(LC_NUMERIC, "C");
1353 g_snprintf(buf, sizeof(buf), "%.2f", float_val);
1354 /* restore previous locale setting */
1355 setlocale(LC_NUMERIC, savelocale);
1357 g_object_set(renderer, "text", buf, NULL);
1361 * This function can be called from gtk_tree_view_column_set_cell_data_func()
1362 * the user data must be the column number.
1363 * Present value as hexadecimal.
1365 void
1366 present_as_hex_func(GtkTreeViewColumn *column _U_,
1367 GtkCellRenderer *renderer,
1368 GtkTreeModel *model,
1369 GtkTreeIter *iter,
1370 gpointer user_data)
1372 guint val;
1373 gchar buf[35];
1375 /* the col to get data from is in userdata */
1376 gint col = GPOINTER_TO_INT(user_data);
1378 gtk_tree_model_get(model, iter, col, &val, -1);
1380 g_snprintf(buf, sizeof(buf), "0x%02x", val);
1382 g_object_set(renderer, "text", buf, NULL);
1385 void
1386 u64_data_func(GtkTreeViewColumn *column _U_,
1387 GtkCellRenderer *renderer,
1388 GtkTreeModel *model,
1389 GtkTreeIter *iter,
1390 gpointer user_data)
1392 guint64 val;
1393 int i = 0;
1394 gchar *bp;
1395 gchar buf[35];
1397 /* the col to get data from is in userdata */
1398 gint col = GPOINTER_TO_INT(user_data);
1400 gtk_tree_model_get(model, iter, col, &val, -1);
1402 bp = &buf[34];
1403 *bp = 0;
1404 do {
1405 *--bp = (gchar)(val % 10) +'0';
1406 if (!(++i % 3)) {
1407 *--bp = ' ';
1409 } while ((val /= 10) != 0 && bp > buf);
1410 g_object_set(renderer, "text", bp, NULL);
1414 * This function can be called from gtk_tree_view_column_set_cell_data_func()
1415 * The user data must be the column number.
1416 * Renders the const static string whose pointer is stored.
1418 void
1419 str_ptr_data_func(GtkTreeViewColumn *column _U_,
1420 GtkCellRenderer *renderer,
1421 GtkTreeModel *model,
1422 GtkTreeIter *iter,
1423 gpointer user_data)
1425 const gchar *str = NULL;
1427 /* The col to get data from is in userdata */
1428 gint data_column = GPOINTER_TO_INT(user_data);
1430 gtk_tree_model_get(model, iter, data_column, &str, -1);
1431 /* XXX should we check that str is non NULL and print a warning or do assert? */
1433 g_object_set(renderer, "text", str, NULL);
1436 gint
1437 str_ptr_sort_func(GtkTreeModel *model,
1438 GtkTreeIter *a,
1439 GtkTreeIter *b,
1440 gpointer user_data)
1442 const gchar *str_a = NULL;
1443 const gchar *str_b = NULL;
1444 gint ret = 0;
1446 /* The col to get data from is in userdata */
1447 gint data_column = GPOINTER_TO_INT(user_data);
1449 gtk_tree_model_get(model, a, data_column, &str_a, -1);
1450 gtk_tree_model_get(model, b, data_column, &str_b, -1);
1452 if (str_a == str_b) {
1453 /* it's worth testing because a lot of rows point to the same data */
1454 return 0;
1456 else if (str_a == NULL || str_b == NULL) {
1457 ret = (str_a == NULL) ? -1 : 1;
1459 else {
1460 ret = g_ascii_strcasecmp(str_a,str_b);
1462 return ret;
1465 /** --------------------------------------------------
1466 * ws_combo_box_text_and_pointer convenience functions
1467 * (Code adapted from GtkComboBox.c)
1471 * ws_combo_box_new_text_and_pointer_full:
1473 * Convenience function which constructs a new "text and pointer" combo box, which
1474 * is a #GtkComboBox just displaying strings and storing a pointer associated with
1475 * each combo_box entry; The pointer can be retrieved when an entry is selected.
1476 * Also: optionally returns the cell renderer for the combo box.
1477 * If you use this function to create a text_and_pointer combo_box,
1478 * you should only manipulate its data source with the
1479 * following convenience functions:
1480 * ws_combo_box_append_text_and_pointer()
1481 * ws_combo_box_append_text_and_pointer_full()
1483 * @param cell_p pointer to return the 'GtkCellRenderer *' for the combo box (or NULL).
1484 * @return A pointer to a new text_and_pointer combo_box.
1487 /* Note:
1488 * GtkComboBox style property: "appears-as-list":
1489 * Default: 0: ie: displays as menus
1490 * Wireshark Windows gtkrc: 1: ie: displays as lists (treeview)
1492 GtkWidget *
1493 ws_combo_box_new_text_and_pointer_full(GtkCellRenderer **cell_p) {
1494 GtkWidget *combo_box;
1495 GtkCellRenderer *cell;
1496 GtkTreeStore *store;
1498 /* The Tree store for the GtkComboBox has 3 columns:
1499 0: text string for display in GtkComboBox list;
1500 1: pointer (data) associated with the entry;
1501 2: True/False depending upon whether this entry is selectable ("sensitive" attribute).
1504 store = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
1505 combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
1506 g_object_unref(store);
1507 cell = gtk_cell_renderer_text_new();
1508 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), cell, TRUE);
1509 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell,
1510 "text", 0, "sensitive", 2,
1511 NULL);
1512 if (cell_p != NULL) {
1513 *cell_p = cell;
1515 return combo_box;
1519 * ws_combo_box_new_text_and_pointer:
1521 * Convenience function which constructs a new "text and pointer" combo box, which
1522 * is a #GtkComboBox just displaying strings and storing a pointer associated with
1523 * each combo_box entry; The pointer can be retrieved when an entry is selected.
1524 * If you use this function to create a text_and_pointer combo_box,
1525 * you should only manipulate its data source with the
1526 * following convenience functions:
1527 * ws_combo_box_append_text_and_pointer()
1528 * ws_combo_box_append_text_and_pointer_full()
1530 * @return A pointer to a new text_and_pointer combo_box.
1533 GtkWidget *
1534 ws_combo_box_new_text_and_pointer(void) {
1535 return ws_combo_box_new_text_and_pointer_full(NULL);
1540 * ws_combo_box_clear_text_and_pointer:
1541 * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1543 * Clears all the text_and_pointer entries in the text_and_pointer combo_box.
1544 * Note: A "changed" signal will be emitted after the clear if there was
1545 * an active (selected) entry before the clear.
1546 * You should use this function only with combo boxes constructed with
1547 * ws_combo_box_new_text_and_pointer().
1549 void
1550 ws_combo_box_clear_text_and_pointer(GtkComboBox *combo_box)
1552 gtk_tree_store_clear(GTK_TREE_STORE(gtk_combo_box_get_model(combo_box)));
1556 * ws_combo_box_append_text_and_pointer_full:
1557 * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1558 * @param parent_iter Parent row for apending; NULL if appending to tree top-level;
1559 * @param text A string to be displayed as an entry in the dropdown list of the combo_box
1560 * @param ptr A pointer to be associated with this entry of the combo_box
1561 * @param sensitive TRUE/FALSE to set sensitivity of the entry
1562 * @return A GtkTreeIter pointing to the appended GtkVomboBox entry.
1564 * Appends text and ptr to the list of strings and pointers stored in combo_box.
1565 * The text and ptr can be appended to any existing level of the tree_store.
1566 * The sensitivity of the row will be set as requested.
1567 * Note that you can only use this function with combo boxes constructed with
1568 * ws_combo_box_new_text_and_pointer().
1570 GtkTreeIter
1571 ws_combo_box_append_text_and_pointer_full(GtkComboBox *combo_box,
1572 GtkTreeIter *parent_iter,
1573 const gchar *text,
1574 const gpointer ptr,
1575 const gboolean sensitive)
1577 GtkTreeIter iter;
1578 GtkTreeStore *store;
1580 store = GTK_TREE_STORE(gtk_combo_box_get_model(combo_box));
1582 gtk_tree_store_append(store, &iter, parent_iter);
1583 gtk_tree_store_set(store, &iter, 0, text, 1, ptr, 2, sensitive, -1);
1585 return iter;
1589 * ws_combo_box_append_text_and_pointer:
1590 * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1591 * @param text A string to be displayed as an entry in the dropdown list of the combo_box
1592 * @param ptr A pointer to be associated with this entry of the combo_box
1593 * @return A GtkTreeIter pointing to the appended GtkComboBox entry.
1595 * Appends text and ptr to the list of strings and pointers stored in combo_box. Note that
1596 * you can only use this function with combo boxes constructed with
1597 * ws_combo_box_new_text_and_pointer().
1599 GtkTreeIter
1600 ws_combo_box_append_text_and_pointer(GtkComboBox *combo_box,
1601 const gchar *text,
1602 const gpointer ptr)
1604 return ws_combo_box_append_text_and_pointer_full(combo_box, NULL, text, ptr, TRUE);
1609 * ws_combo_box_get_active_pointer:
1610 * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1611 * @param ptr A pointer to a location in which to store the pointer associated with the active entry
1612 * @return TRUE if an entry is selected (i.e: an active entry exists); FALSE otherwise
1614 * You can only use this function with combo boxes constructed with
1615 * ws_combo_box_new_text_and_pointer().
1617 gboolean
1618 ws_combo_box_get_active_pointer(GtkComboBox *combo_box,
1619 gpointer *ptr)
1621 GtkTreeStore *store;
1622 GtkTreeIter iter;
1624 *ptr = NULL;
1626 if (gtk_combo_box_get_active_iter(combo_box, &iter)) {
1627 store = GTK_TREE_STORE(gtk_combo_box_get_model(combo_box));
1628 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1629 1, ptr, -1);
1630 return TRUE;
1632 return FALSE;
1636 * ws_combo_box_get_active:
1637 * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1638 * @return Index of the active entry; -1 if no entry is selected;
1639 * Note: If the active item is not an immediate child of root of the tree then
1640 * the index returned is that of the top-level for the acftive entry.
1642 gint
1643 ws_combo_box_get_active(GtkComboBox *combo_box)
1645 return gtk_combo_box_get_active(combo_box);
1649 * ws_combo_box_set_active:
1650 * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1651 * @param idx of the entry which is to be set as active (ie: selected).
1652 * Index refers to the immediate children of the tree.
1654 void
1655 ws_combo_box_set_active(GtkComboBox *combo_box,
1656 gint idx)
1658 gtk_combo_box_set_active(combo_box, idx);
1662 * ws_combo_box_set_active_iter:
1663 * @param combo_box A #GtkComboBox constructed using ws_combo_box_new_text_and_pointer()
1664 * @param iter of the entry which is to be set as active (ie: selected).
1666 void
1667 ws_combo_box_set_active_iter(GtkComboBox *combo_box, GtkTreeIter *iter)
1669 gtk_combo_box_set_active_iter(combo_box, iter);
1673 /* Copy functions from GTK 3.0 to be used if GTK version is 2.22 or 2.24 to be able save Graphs to file */
1674 #if GTK_CHECK_VERSION(2,22,0)
1675 #if !GTK_CHECK_VERSION(3,0,0)
1676 static cairo_format_t
1677 gdk_cairo_format_for_content(cairo_content_t content)
1679 switch (content)
1681 case CAIRO_CONTENT_COLOR:
1682 return CAIRO_FORMAT_RGB24;
1683 case CAIRO_CONTENT_ALPHA:
1684 return CAIRO_FORMAT_A8;
1685 case CAIRO_CONTENT_COLOR_ALPHA:
1686 default:
1687 return CAIRO_FORMAT_ARGB32;
1691 static cairo_surface_t *
1692 gdk_cairo_surface_coerce_to_image(cairo_surface_t *surface,
1693 cairo_content_t content,
1694 int src_x,
1695 int src_y,
1696 int width,
1697 int height)
1699 cairo_surface_t *copy;
1700 cairo_t *cr;
1702 copy = cairo_image_surface_create(gdk_cairo_format_for_content(content),
1703 width,
1704 height);
1706 cr = cairo_create(copy);
1707 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1708 cairo_set_source_surface(cr, surface, -src_x, -src_y);
1709 cairo_paint(cr);
1710 cairo_destroy(cr);
1712 return copy;
1715 static void
1716 convert_alpha(guchar *dest_data,
1717 int dest_stride,
1718 guchar *src_data,
1719 int src_stride,
1720 int src_x,
1721 int src_y,
1722 int width,
1723 int height)
1725 int x, y;
1727 src_data += src_stride * src_y + src_x * 4;
1729 for (y = 0; y < height; y++) {
1730 guint32 *src = (guint32 *)src_data;
1732 for (x = 0; x < width; x++) {
1733 guint alpha = src[x] >> 24;
1735 if (alpha == 0)
1737 dest_data[x * 4 + 0] = 0;
1738 dest_data[x * 4 + 1] = 0;
1739 dest_data[x * 4 + 2] = 0;
1741 else
1743 dest_data[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha;
1744 dest_data[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha;
1745 dest_data[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha;
1747 dest_data[x * 4 + 3] = alpha;
1750 src_data += src_stride;
1751 dest_data += dest_stride;
1755 static void
1756 convert_no_alpha(guchar *dest_data,
1757 int dest_stride,
1758 guchar *src_data,
1759 int src_stride,
1760 int src_x,
1761 int src_y,
1762 int width,
1763 int height)
1765 int x, y;
1767 src_data += src_stride * src_y + src_x * 4;
1769 for (y = 0; y < height; y++) {
1770 guint32 *src = (guint32 *)src_data;
1772 for (x = 0; x < width; x++) {
1773 dest_data[x * 3 + 0] = src[x] >> 16;
1774 dest_data[x * 3 + 1] = src[x] >> 8;
1775 dest_data[x * 3 + 2] = src[x];
1778 src_data += src_stride;
1779 dest_data += dest_stride;
1784 * gdk_pixbuf_get_from_surface:
1785 * @surface: surface to copy from
1786 * @src_x: Source X coordinate within @surface
1787 * @src_y: Source Y coordinate within @surface
1788 * @width: Width in pixels of region to get
1789 * @height: Height in pixels of region to get
1791 * Transfers image data from a #cairo_surface_t and converts it to an RGB(A)
1792 * representation inside a #GdkPixbuf. This allows you to efficiently read
1793 * individual pixels from cairo surfaces. For #GdkWindows, use
1794 * gdk_pixbuf_get_from_window() instead.
1796 * This function will create an RGB pixbuf with 8 bits per channel.
1797 * The pixbuf will contain an alpha channel if the @surface contains one.
1799 * Return value: (transfer full): A newly-created pixbuf with a reference
1800 * count of 1, or %NULL on error
1802 GdkPixbuf *
1803 gdk_pixbuf_get_from_surface(cairo_surface_t *surface,
1804 gint src_x,
1805 gint src_y,
1806 gint width,
1807 gint height)
1809 cairo_content_t content;
1810 GdkPixbuf *dest;
1812 /* General sanity checks */
1813 g_return_val_if_fail(surface != NULL, NULL);
1814 g_return_val_if_fail(width > 0 && height > 0, NULL);
1816 content = (cairo_content_t)(cairo_surface_get_content(surface) | CAIRO_CONTENT_COLOR);
1817 dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
1818 !!(content & CAIRO_CONTENT_ALPHA),
1820 width, height);
1822 surface = gdk_cairo_surface_coerce_to_image(surface, content,
1823 src_x, src_y,
1824 width, height);
1825 cairo_surface_flush(surface);
1826 if (cairo_surface_status(surface) || dest == NULL)
1828 cairo_surface_destroy(surface);
1829 return NULL;
1832 if (gdk_pixbuf_get_has_alpha(dest))
1833 convert_alpha(gdk_pixbuf_get_pixels(dest),
1834 gdk_pixbuf_get_rowstride(dest),
1835 cairo_image_surface_get_data(surface),
1836 cairo_image_surface_get_stride(surface),
1837 0, 0,
1838 width, height);
1839 else
1840 convert_no_alpha(gdk_pixbuf_get_pixels(dest),
1841 gdk_pixbuf_get_rowstride(dest),
1842 cairo_image_surface_get_data(surface),
1843 cairo_image_surface_get_stride(surface),
1844 0, 0,
1845 width, height);
1847 cairo_surface_destroy(surface);
1848 return dest;
1850 #endif /* !GTK_CHECK_VERSION(3,0,0) */
1851 #endif /* GTK_CHECK_VERSION(2,22,0) */
1854 GtkWidget *
1855 ws_gtk_box_new(GtkOrientation orientation,
1856 gint spacing,
1857 gboolean homogeneous)
1859 #if !GTK_CHECK_VERSION(3,0,0)
1860 if (orientation == GTK_ORIENTATION_HORIZONTAL)
1861 return gtk_hbox_new(homogeneous, spacing);
1862 else
1863 return gtk_vbox_new(homogeneous, spacing);
1864 #else
1865 GtkWidget *widget;
1867 widget = gtk_box_new(orientation, spacing);
1868 gtk_box_set_homogeneous(GTK_BOX(widget), homogeneous);
1870 return widget;
1871 #endif /* GTK_CHECK_VERSION(3,0,0) */
1874 #if !GTK_CHECK_VERSION(3,0,0)
1875 GtkWidget *
1876 gtk_button_box_new(GtkOrientation orientation)
1878 if (orientation == GTK_ORIENTATION_HORIZONTAL) {
1879 return gtk_hbutton_box_new();
1880 } else {
1881 return gtk_vbutton_box_new();
1885 GtkWidget *
1886 gtk_scrollbar_new(GtkOrientation orientation,
1887 GtkAdjustment *adjustment)
1889 if (orientation == GTK_ORIENTATION_HORIZONTAL) {
1890 return gtk_hscrollbar_new(adjustment);
1891 } else {
1892 return gtk_vscrollbar_new(adjustment);
1896 GtkWidget *
1897 gtk_paned_new(GtkOrientation orientation)
1899 if(orientation == GTK_ORIENTATION_HORIZONTAL) {
1900 return gtk_hpaned_new();
1901 } else {
1902 return gtk_vpaned_new();
1906 GtkWidget *
1907 gtk_separator_new(GtkOrientation orientation)
1909 if (orientation == GTK_ORIENTATION_HORIZONTAL) {
1910 return gtk_hseparator_new();
1911 } else {
1912 return gtk_vseparator_new();
1915 #endif /* GTK_CHECK_VERSION(3,0,0) */
1917 GtkWidget *
1918 frame_new(const gchar *title) {
1919 GtkWidget *frame, *frame_lb;
1920 GString *mu_title = g_string_new("");
1922 frame = gtk_frame_new(NULL);
1923 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1924 if (title) {
1925 #if defined(_WIN32) || defined(__APPLE__)
1926 g_string_printf(mu_title, "%s", title);
1927 #else
1928 g_string_printf(mu_title, "<b>%s</b>", title);
1929 #endif
1930 frame_lb = gtk_label_new(NULL);
1931 gtk_label_set_markup(GTK_LABEL(frame_lb), mu_title->str);
1932 gtk_frame_set_label_widget(GTK_FRAME(frame), frame_lb);
1934 g_string_free(mu_title, TRUE);
1936 return frame;
1940 /* ---------------------------------
1941 * ws_gtk_grid...() wrappers
1942 * See gui_utils.h
1945 #if !GTK_CHECK_VERSION(3,0,0)
1946 #else /* GTK3 */
1948 void
1949 ws_gtk_grid_attach_defaults(GtkGrid *grid, GtkWidget *child, gint left, gint top, gint width, gint height)
1951 /* Use defaults for [x|y]options and [x|y]padding which match those for gtk_table_attach_defaults() */
1952 ws_gtk_grid_attach_extended(grid, child, left, top, width, height,
1953 (GtkAttachOptions)(GTK_EXPAND|GTK_FILL), (GtkAttachOptions)(GTK_EXPAND|GTK_FILL), 0, 0);
1956 void
1957 ws_gtk_grid_attach_extended(GtkGrid *grid, GtkWidget *child,
1958 gint left, gint top, gint width, gint height,
1959 GtkAttachOptions xoptions, GtkAttachOptions yoptions,
1960 guint xpadding, guint ypadding)
1962 gtk_grid_attach(grid, child, left, top, width, height);
1964 /* XXX: On Gtk3, there's Some trickyness about EXPAND which I probably don't
1965 * really understand.
1966 * It seems that:
1967 * Default for EXPAND is "not set".
1968 * In this case "computed expand" based on any child(ren) of this widget will
1969 * affect this widget.
1970 * If EXPAND is set (either TRUE or FALSE) then the value overrides any effect
1971 * from children.
1974 /* Note: widget defaults are FALSE */
1975 if (xoptions & GTK_EXPAND)
1976 gtk_widget_set_hexpand(child, TRUE);
1977 if (yoptions & GTK_EXPAND)
1978 gtk_widget_set_vexpand(child, TRUE);
1980 /* Note: widget default is GTK_FILL */
1981 /* XXX: Is an 'align' ignored if the corresponding 'fill; is FALSE ? */
1982 /* XXX: don't set FILL(since is dedault) but just clear if not set ?? */
1983 /* ToDo: review effect of explicit set/clear vs explict clear only */
1984 gtk_widget_set_halign(child, (xoptions & GTK_FILL) ? GTK_ALIGN_FILL : GTK_ALIGN_CENTER);
1985 gtk_widget_set_valign(child, (yoptions & GTK_FILL) ? GTK_ALIGN_FILL : GTK_ALIGN_CENTER);
1987 if (xpadding != 0) {
1988 gtk_widget_set_margin_left(child, xpadding);
1989 gtk_widget_set_margin_right(child, xpadding);
1991 if (ypadding != 0) {
1992 gtk_widget_set_margin_top(child, ypadding);
1993 gtk_widget_set_margin_bottom(child, ypadding);
1997 void
1998 ws_gtk_grid_set_homogeneous(GtkGrid *grid, gboolean homogeneous)
2000 gtk_grid_set_row_homogeneous(grid, homogeneous);
2001 gtk_grid_set_column_homogeneous(grid, homogeneous);
2003 #endif /* !GTK_CHECK_VERSION(3,0,0) */
2006 * Wrap gdk_cairo_set_source_color() with the GTK 3 equivalent
2007 * to be used in GTK2
2009 #if !GTK_CHECK_VERSION(3,0,0)
2010 void
2011 gdk_cairo_set_source_rgba(cairo_t *cr, const GdkRGBA *rgba)
2013 GdkColor color;
2015 gdkRGBAcolor_to_GdkColor(&color, rgba);
2017 gdk_cairo_set_source_color(cr, &color);
2020 #endif /* GTK_CHECK_VERSION(3,0,0) */