1 /*
2  * util.c
3  *
4  * Copyright (C) 2002 Sun Microsystems, Inc.
5  *           (C) 1999, 2000 Red Hat Inc.
6  *           (C) 1998 James Henstridge
7  *           (C) 1995-2002 Free Software Foundation
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  *
24  * Authors: Glynn Foster <glynn.foster@sun.com>
25  *          Havoc Pennington <hp@redhat.com>
26  *          James Henstridge <james@daa.com.au>
27  *          Tom Tromey <tromey@redhat.com>
28  */
29 
30 #include "config.h"
31 
32 #include "config.h"
33 #include "util.h"
34 #include "zenity.h"
35 #include <errno.h>
36 #include <locale.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 
42 #ifdef GDK_WINDOWING_X11
43 #include <gdk/gdkx.h>
44 #endif
45 
46 #define ZENITY_OK_DEFAULT 0
47 #define ZENITY_CANCEL_DEFAULT 1
48 #define ZENITY_ESC_DEFAULT 1
49 #define ZENITY_ERROR_DEFAULT -1
50 #define ZENITY_EXTRA_DEFAULT 127
51 
52 GtkBuilder *
zenity_util_load_ui_file(const gchar * root_widget,...)53 zenity_util_load_ui_file (const gchar *root_widget, ...) {
54 	va_list args;
55 	gchar *arg = NULL;
56 	GPtrArray *ptrarray;
57 	GtkBuilder *builder = gtk_builder_new ();
58 	GError *error = NULL;
59 	gchar **objects;
60 	guint result = 0;
61 
62 	gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE);
63 
64 	/* We have at least the root_widget and a NULL */
65 	ptrarray = g_ptr_array_sized_new (2);
66 
67 	g_ptr_array_add (ptrarray, g_strdup (root_widget));
68 
69 	va_start (args, root_widget);
70 
71 	arg = va_arg (args, gchar *);
72 
73 	while (arg) {
74 		g_ptr_array_add (ptrarray, g_strdup (arg));
75 		arg = va_arg (args, gchar *);
76 	}
77 	va_end (args);
78 
79 	/* Enforce terminating NULL */
80 	g_ptr_array_add (ptrarray, NULL);
81 	objects = (gchar **) g_ptr_array_free (ptrarray, FALSE);
82 
83 	if (g_file_test (ZENITY_UI_FILE_RELATIVEPATH, G_FILE_TEST_EXISTS)) {
84 		/* Try current dir, for debugging */
85 		result = gtk_builder_add_objects_from_file (
86 			builder, ZENITY_UI_FILE_RELATIVEPATH, objects, NULL);
87 	}
88 
89 	if (result == 0)
90 		result = gtk_builder_add_objects_from_file (
91 			builder, ZENITY_UI_FILE_FULLPATH, objects, &error);
92 
93 	g_strfreev (objects);
94 
95 	if (result == 0) {
96 		g_warning ("Could not load ui file %s: %s",
97 			ZENITY_UI_FILE_FULLPATH,
98 			error->message);
99 		g_error_free (error);
100 		g_object_unref (builder);
101 		return NULL;
102 	}
103 
104 	return builder;
105 }
106 gchar *
zenity_util_strip_newline(gchar * string)107 zenity_util_strip_newline (gchar *string) {
108 	gsize len;
109 
110 	g_return_val_if_fail (string != NULL, NULL);
111 
112 	len = strlen (string);
113 	while (len--) {
114 		if (string[len] == '\n')
115 			string[len] = '\0';
116 		else
117 			break;
118 	}
119 
120 	return string;
121 }
122 
123 gboolean
zenity_util_fill_file_buffer(GtkTextBuffer * buffer,const gchar * filename)124 zenity_util_fill_file_buffer (GtkTextBuffer *buffer, const gchar *filename) {
125 	GtkTextIter iter, end;
126 	FILE *f;
127 	gchar buf[2048];
128 	gint remaining = 0;
129 
130 	if (filename == NULL)
131 		return FALSE;
132 
133 	f = fopen (filename, "r");
134 
135 	if (f == NULL) {
136 		g_warning ("Cannot open file '%s': %s", filename, g_strerror (errno));
137 		return FALSE;
138 	}
139 
140 	gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
141 
142 	while (!feof (f)) {
143 		gint count;
144 		const char *leftover;
145 		int to_read = 2047 - remaining;
146 
147 		count = fread (buf + remaining, 1, to_read, f);
148 		buf[count + remaining] = '\0';
149 
150 		g_utf8_validate (buf, count + remaining, &leftover);
151 
152 		g_assert (g_utf8_validate (buf, leftover - buf, NULL));
153 		gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
154 
155 		remaining = (buf + remaining + count) - leftover;
156 		g_memmove (buf, leftover, remaining);
157 
158 		if (remaining > 6 || count < to_read)
159 			break;
160 	}
161 
162 	if (remaining) {
163 		g_warning (
164 			"Invalid UTF-8 data encountered reading file '%s'", filename);
165 		return FALSE;
166 	}
167 
168 	/* We had a newline in the buffer to begin with. (The buffer always contains
169 	 * a newline, so we delete to the end of the buffer to clean up.
170 	 */
171 
172 	gtk_text_buffer_get_end_iter (buffer, &end);
173 	gtk_text_buffer_delete (buffer, &iter, &end);
174 
175 	gtk_text_buffer_set_modified (buffer, FALSE);
176 
177 	return TRUE;
178 }
179 
180 const gchar *
zenity_util_icon_name_from_filename(const gchar * filename)181 zenity_util_icon_name_from_filename (const gchar *filename) {
182 	if (!filename || !filename[0])
183 		return "dialog-warning"; /* default */
184 
185 	if (!g_ascii_strcasecmp (filename, "warning"))
186 		return "dialog-warning";
187 	if (!g_ascii_strcasecmp (filename, "info"))
188 		return "dialog-information";
189 	if (!g_ascii_strcasecmp (filename, "question"))
190 		return "dialog-question";
191 	if (!g_ascii_strcasecmp (filename, "error"))
192 		return "dialog-error";
193 	return NULL;
194 }
195 
196 void
zenity_util_set_window_icon_from_file(GtkWidget * widget,const gchar * filename)197 zenity_util_set_window_icon_from_file (
198 	GtkWidget *widget, const gchar *filename) {
199 	GdkPixbuf *pixbuf;
200 	const gchar *icon_name;
201 
202 	icon_name = zenity_util_icon_name_from_filename (filename);
203 	if (icon_name) {
204 		gtk_window_set_icon_name (GTK_WINDOW (widget), icon_name);
205 	} else {
206 		pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
207 		gtk_window_set_icon (GTK_WINDOW (widget), pixbuf);
208 		g_object_unref (pixbuf);
209 	}
210 }
211 
212 void
zenity_util_set_window_icon(GtkWidget * widget,const gchar * filename,const gchar * default_file)213 zenity_util_set_window_icon (
214 	GtkWidget *widget, const gchar *filename, const gchar *default_file) {
215 	GdkPixbuf *pixbuf;
216 
217 	if (filename != NULL) {
218 		zenity_util_set_window_icon_from_file (widget, filename);
219 	} else {
220 		pixbuf = gdk_pixbuf_new_from_file (default_file, NULL);
221 		if (pixbuf != NULL) {
222 			gtk_window_set_icon (GTK_WINDOW (widget), pixbuf);
223 			g_object_unref (pixbuf);
224 		}
225 	}
226 }
227 
228 void
zenity_util_set_window_icon_from_icon_name(GtkWidget * widget,const gchar * filename,const gchar * default_icon_name)229 zenity_util_set_window_icon_from_icon_name (
230 	GtkWidget *widget, const gchar *filename, const gchar *default_icon_name) {
231 	if (filename != NULL)
232 		zenity_util_set_window_icon_from_file (widget, filename);
233 	else
234 		gtk_window_set_icon_name (GTK_WINDOW (widget), default_icon_name);
235 }
236 
237 void
zenity_util_show_help(GError ** error)238 zenity_util_show_help (GError **error) {
239 	gchar *tmp;
240 	tmp = g_find_program_in_path ("yelp");
241 
242 	if (tmp) {
243 		g_free (tmp);
244 		g_spawn_command_line_async ("yelp help:zenity", error);
245 	}
246 }
247 
248 gint
zenity_util_return_exit_code(ZenityExitCode value)249 zenity_util_return_exit_code (ZenityExitCode value) {
250 
251 	const gchar *env_var = NULL;
252 	gint retval;
253 
254 	switch (value) {
255 
256 		case ZENITY_OK:
257 			env_var = g_getenv ("ZENITY_OK");
258 			if (!env_var)
259 				env_var = g_getenv ("DIALOG_OK");
260 			if (!env_var)
261 				retval = ZENITY_OK_DEFAULT;
262 			break;
263 
264 		case ZENITY_CANCEL:
265 			env_var = g_getenv ("ZENITY_CANCEL");
266 			if (!env_var)
267 				env_var = g_getenv ("DIALOG_CANCEL");
268 			if (!env_var)
269 				retval = ZENITY_CANCEL_DEFAULT;
270 			break;
271 
272 		case ZENITY_ESC:
273 			env_var = g_getenv ("ZENITY_ESC");
274 			if (!env_var)
275 				env_var = g_getenv ("DIALOG_ESC");
276 			if (!env_var)
277 				retval = ZENITY_ESC_DEFAULT;
278 			break;
279 
280 		case ZENITY_EXTRA:
281 			env_var = g_getenv ("ZENITY_EXTRA");
282 			if (!env_var)
283 				env_var = g_getenv ("DIALOG_EXTRA");
284 			if (!env_var)
285 				retval = ZENITY_EXTRA_DEFAULT;
286 			break;
287 
288 		case ZENITY_ERROR:
289 			env_var = g_getenv ("ZENITY_ERROR");
290 			if (!env_var)
291 				env_var = g_getenv ("DIALOG_ERROR");
292 			if (!env_var)
293 				retval = ZENITY_ERROR_DEFAULT;
294 			break;
295 
296 		case ZENITY_TIMEOUT:
297 			env_var = g_getenv ("ZENITY_TIMEOUT");
298 			if (!env_var)
299 				env_var = g_getenv ("DIALOG_TIMEOUT");
300 			if (!env_var)
301 				retval = ZENITY_TIMEOUT;
302 			break;
303 
304 		default:
305 			retval = 1;
306 	}
307 
308 	if (env_var)
309 		retval = atoi (env_var);
310 	return retval;
311 }
312 
313 void
zenity_util_exit_code_with_data(ZenityExitCode value,ZenityData * zen_data)314 zenity_util_exit_code_with_data (ZenityExitCode value, ZenityData *zen_data) {
315 	zen_data->exit_code = zenity_util_return_exit_code (value);
316 }
317 
318 #ifdef GDK_WINDOWING_X11
319 
320 static Window
transient_get_xterm(void)321 transient_get_xterm (void) {
322 	const char *wid_str = g_getenv ("WINDOWID");
323 	if (wid_str) {
324 		char *wid_str_end;
325 		int ret;
326 		Window wid = strtoul (wid_str, &wid_str_end, 10);
327 		if (*wid_str != '\0' && *wid_str_end == '\0' && wid != 0) {
328 			XWindowAttributes attrs;
329 			gdk_error_trap_push ();
330 			ret = XGetWindowAttributes (
331 				GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), wid, &attrs);
332 			gdk_flush ();
333 			if (gdk_error_trap_pop () != 0 || ret == 0) {
334 				return None;
335 			}
336 			return wid;
337 		}
338 	}
339 	return None;
340 }
341 
342 static void
transient_x_free(void * ptr)343 transient_x_free (void *ptr) {
344 	if (ptr)
345 		XFree (ptr);
346 }
347 
348 static gboolean
transient_is_toplevel(Window wid)349 transient_is_toplevel (Window wid) {
350 	XTextProperty prop;
351 	Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
352 	if (!XGetWMName (dpy, wid, &prop))
353 		return FALSE;
354 	transient_x_free (prop.value);
355 	return !!prop.value;
356 }
357 
358 /*
359  * GNOME Terminal doesn't give us its toplevel window, but the WM needs a
360  * toplevel XID for proper stacking.  Other terminals work fine without this
361  * magic.  We can't use GDK here since "xterm" is a foreign window.
362  */
363 
364 static Window
transient_get_xterm_toplevel(void)365 transient_get_xterm_toplevel (void) {
366 	Window xterm = transient_get_xterm ();
367 	Display *dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
368 	while (xterm != None && !transient_is_toplevel (xterm)) {
369 		Window root, parent, *children;
370 		unsigned nchildren;
371 		XQueryTree (dpy, xterm, &root, &parent, &children, &nchildren);
372 		transient_x_free (children);
373 		if (parent == root)
374 			xterm = None;
375 		else
376 			xterm = parent;
377 	}
378 	return xterm;
379 }
380 
381 static void
zenity_util_make_transient(GdkWindow * window,Window parent)382 zenity_util_make_transient (GdkWindow *window, Window parent) {
383 	Window parent_window = parent;
384 	if (parent_window == 0)
385 		parent_window = transient_get_xterm_toplevel ();
386 	if (parent_window != None) {
387 		XSetTransientForHint (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
388 			GDK_WINDOW_XID (window),
389 			parent_window);
390 	}
391 }
392 
393 #endif /* GDK_WINDOWING_X11 */
394 
395 void
zenity_util_show_dialog(GtkWidget * dialog,guintptr parent)396 zenity_util_show_dialog (GtkWidget *dialog, guintptr parent) {
397 	gtk_widget_realize (dialog);
398 #ifdef GDK_WINDOWING_X11
399 	if (GDK_IS_X11_DISPLAY (gdk_display_get_default ())) {
400 		g_assert (gtk_widget_get_window (dialog));
401 		zenity_util_make_transient (gtk_widget_get_window (dialog), parent);
402 	}
403 #endif
404 	gtk_widget_show (dialog);
405 }
406 
407 gboolean
zenity_util_timeout_handle(gpointer data)408 zenity_util_timeout_handle (gpointer data) {
409 	GtkDialog *dialog = GTK_DIALOG (data);
410 	if (dialog != NULL)
411 		gtk_dialog_response (dialog, ZENITY_TIMEOUT);
412 	else {
413 		gtk_main_quit ();
414 		exit (ZENITY_TIMEOUT);
415 	}
416 	return FALSE;
417 }
418