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