r328: Changed the install script so that the CVS directories don't get installed.
[rox-filer.git] / ROX-Filer / src / gui_support.c
blob83fa1f57af3f59c5426a8c2f06330692bf37ae94
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@ecs.soton.ac.uk>.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* gui_support.c - general (GUI) support routines */
24 #include "config.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/param.h>
30 #include <stdarg.h>
31 #include <errno.h>
33 #include <glib.h>
34 #include <X11/Xlib.h>
35 #include <X11/Xatom.h>
36 #include <gdk/gdkx.h>
37 #include <gdk/gdk.h>
39 #include "main.h"
40 #include "gui_support.h"
41 #include "pixmaps.h"
43 GdkFont *item_font = NULL;
44 GdkFont *fixed_font = NULL;
45 GtkStyle *fixed_style = NULL;
46 gint fixed_width;
47 GdkColor red = {0, 0xffff, 0, 0};
48 GdkGC *red_gc = NULL; /* Not automatically initialised */
49 gint screen_width, screen_height;
51 static GdkAtom xa_cardinal;
53 void gui_support_init()
55 fixed_font = gdk_font_load("fixed");
56 item_font = gtk_widget_get_default_style()->font;
58 fixed_style = gtk_style_copy(gtk_widget_get_default_style());
59 fixed_style->font = fixed_font;
61 fixed_width = gdk_string_width(fixed_font, "m");
63 xa_cardinal = gdk_atom_intern("CARDINAL", FALSE);
65 gdk_color_alloc(gtk_widget_get_default_colormap(), &red);
67 /* This call starts returning strange values after a while, so get
68 * the result here during init.
70 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width, &screen_height);
73 static void choice_clicked(GtkWidget *widget, gpointer number)
75 int *choice_return;
77 choice_return = gtk_object_get_data(GTK_OBJECT(widget),
78 "choice_return");
80 if (choice_return)
81 *choice_return = (int) number;
84 /* Open a modal dialog box showing a message.
85 * The user can choose from a selection of buttons at the bottom.
86 * Returns -1 if the window is destroyed, or the number of the button
87 * if one is clicked (starting from zero).
89 int get_choice(char *title,
90 char *message,
91 int number_of_buttons, ...)
93 GtkWidget *dialog;
94 GtkWidget *vbox, *action_area, *separator;
95 GtkWidget *text, *text_container;
96 GtkWidget *button = NULL;
97 int i, retval;
98 va_list ap;
99 int choice_return;
101 dialog = gtk_window_new(GTK_WINDOW_DIALOG);
102 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
103 gtk_window_set_title(GTK_WINDOW(dialog), title);
104 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
106 vbox = gtk_vbox_new(FALSE, 0);
107 gtk_container_add(GTK_CONTAINER(dialog), vbox);
109 action_area = gtk_hbox_new(TRUE, 5);
110 gtk_container_set_border_width(GTK_CONTAINER(action_area), 10);
111 gtk_box_pack_end(GTK_BOX(vbox), action_area, FALSE, TRUE, 0);
113 separator = gtk_hseparator_new ();
114 gtk_box_pack_end(GTK_BOX(vbox), separator, FALSE, TRUE, 0);
116 text = gtk_label_new(message);
117 gtk_label_set_line_wrap(GTK_LABEL(text), TRUE);
118 text_container = gtk_event_box_new();
119 gtk_container_set_border_width(GTK_CONTAINER(text_container), 32);
120 gtk_container_add(GTK_CONTAINER(text_container), text);
122 gtk_box_pack_start(GTK_BOX(vbox),
123 text_container,
124 TRUE, TRUE, 0);
126 va_start(ap, number_of_buttons);
128 for (i = 0; i < number_of_buttons; i++)
130 button = gtk_button_new_with_label(va_arg(ap, char *));
131 gtk_object_set_data(GTK_OBJECT(button), "choice_return",
132 &choice_return);
133 gtk_box_pack_start(GTK_BOX(action_area),
134 button,
135 TRUE, TRUE, 16);
136 gtk_signal_connect(GTK_OBJECT(button), "clicked",
137 choice_clicked, (gpointer) i);
138 if (i == 0)
139 gtk_window_set_focus(GTK_WINDOW(dialog), button);
141 if (!i)
142 gtk_widget_grab_focus(button);
144 va_end(ap);
146 gtk_object_set_data(GTK_OBJECT(dialog), "choice_return",
147 &choice_return);
148 gtk_signal_connect(GTK_OBJECT(dialog), "destroy", choice_clicked,
149 (gpointer) -1);
151 choice_return = -2;
153 gtk_widget_show_all(dialog);
155 while (choice_return == -2)
156 g_main_iteration(TRUE);
158 retval = choice_return;
160 if (retval != -1)
161 gtk_widget_destroy(dialog);
163 return retval;
166 /* Display a message in a window */
167 void report_error(char *title, char *message)
169 g_return_if_fail(message != NULL);
171 if (!title)
172 title = _("Error");
174 get_choice(title, message, 1, "OK");
177 void set_cardinal_property(GdkWindow *window, GdkAtom prop, guint32 value)
179 gdk_property_change(window, prop, xa_cardinal, 32,
180 GDK_PROP_MODE_REPLACE, (gchar *) &value, 1);
183 /* NB: Also used for pinned icons.
184 * TODO: Set the level here too.
186 void make_panel_window(GdkWindow *window)
188 static gboolean need_init = TRUE;
189 static GdkAtom xa_state;
191 if (need_init)
193 xa_state = gdk_atom_intern("_WIN_STATE", FALSE);
194 need_init = FALSE;
197 gdk_window_set_decorations(window, 0);
198 gdk_window_set_functions(window, 0);
200 set_cardinal_property(window, xa_state,
201 WIN_STATE_STICKY | WIN_STATE_HIDDEN |
202 WIN_STATE_FIXED_POSITION | WIN_STATE_ARRANGE_IGNORE);
205 gint hide_dialog_event(GtkWidget *widget, GdkEvent *event, gpointer window)
207 gtk_widget_hide((GtkWidget *) window);
209 return TRUE;
212 static gboolean error_idle_cb(gpointer data)
214 char **error = (char **) data;
216 report_error(error[0], error[1]);
217 g_free(error[0]);
218 g_free(error[1]);
219 error[0] = error[1] = NULL;
221 if (--number_of_windows == 0)
222 gtk_main_quit();
224 return FALSE;
227 /* Display an error next time we are idle */
228 void delayed_error(char *title, char *error)
230 static char *delayed_error_data[2] = {NULL, NULL};
231 gboolean already_open;
233 g_return_if_fail(error != NULL);
235 already_open = delayed_error_data[1] != NULL;
237 g_free(delayed_error_data[0]);
238 g_free(delayed_error_data[1]);
240 delayed_error_data[0] = g_strdup(title);
241 delayed_error_data[1] = g_strdup(error);
243 if (already_open)
244 return;
246 gtk_idle_add(error_idle_cb, delayed_error_data);
248 number_of_windows++;
251 /* Load the file into memory. Return TRUE on success.
252 * Block is zero terminated (but this is not included in the length).
254 gboolean load_file(char *pathname, char **data_out, long *length_out)
256 FILE *file;
257 long length;
258 char *buffer;
259 gboolean retval = FALSE;
261 file = fopen(pathname, "r");
263 if (!file)
265 guchar *message;
267 message = g_strdup_printf("open(%s): %s",
268 pathname, g_strerror(errno));
269 delayed_error(PROJECT, message);
270 g_free(message);
271 return FALSE;
274 fseek(file, 0, SEEK_END);
275 length = ftell(file);
277 buffer = malloc(length + 1);
278 if (buffer)
280 fseek(file, 0, SEEK_SET);
281 fread(buffer, 1, length, file);
283 if (ferror(file))
285 guchar *tmp;
287 tmp = g_strdup_printf("%s: %s\n",
288 pathname, g_strerror(errno));
289 delayed_error(_("Error reading file"), tmp);
290 g_free(tmp);
291 g_free(buffer);
293 else
295 *data_out = buffer;
296 *length_out = length;
297 buffer[length] = '\0';
298 retval = TRUE;
301 else
302 delayed_error(PROJECT,
303 _("Can't allocate memory for buffer to "
304 "load this file"));
306 fclose(file);
308 return retval;
311 GtkWidget *new_help_button(HelpFunc show_help, gpointer data)
313 GtkWidget *b, *icon;
315 b = gtk_button_new();
316 gtk_button_set_relief(GTK_BUTTON(b), GTK_RELIEF_NONE);
317 icon = gtk_pixmap_new(im_help->pixmap, im_help->mask);
318 gtk_container_add(GTK_CONTAINER(b), icon);
319 gtk_signal_connect_object(GTK_OBJECT(b), "clicked", show_help, data);
321 GTK_WIDGET_UNSET_FLAGS(b, GTK_CAN_FOCUS);
323 return b;
326 /* Read file into memory. Call parse_line(guchar *line) for each line
327 * in the file. Callback returns NULL on success, or an error message
328 * if something went wrong. Only the first error is displayed to the user.
330 void parse_file(char *path, ParseFunc *parse_line)
332 char *data;
333 long length;
334 gboolean seen_error = FALSE;
336 if (load_file(path, &data, &length))
338 char *eol, *error;
339 char *line = data;
340 int line_number = 1;
342 while (line && *line)
344 eol = strchr(line, '\n');
345 if (eol)
346 *eol = '\0';
348 error = parse_line(line);
350 if (error && !seen_error)
352 GString *message;
354 message = g_string_new(NULL);
355 g_string_sprintf(message,
356 _("Error in '%s' file at line %d: "
357 "\n\"%s\"\n"
358 "This may be due to upgrading from a previous version of "
359 "ROX-Filer. Open the Options window and click on Save.\n"
360 "Further errors will be ignored."),
361 path,
362 line_number,
363 error);
364 delayed_error(PROJECT, message->str);
365 g_string_free(message, TRUE);
366 seen_error = TRUE;
369 if (!eol)
370 break;
371 line = eol + 1;
372 line_number++;
374 g_free(data);
378 /* Sets up a proxy window for DnD on the specified X window.
379 * Courtesy of Owen Taylor (taken from gmc).
381 gboolean setup_xdnd_proxy(guint32 xid, GdkWindow *proxy_window)
383 GdkAtom xdnd_proxy_atom;
384 Window proxy_xid;
385 Atom type;
386 int format;
387 unsigned long nitems, after;
388 Window *proxy_data;
389 Window proxy;
390 guint32 old_warnings;
392 XGrabServer(GDK_DISPLAY());
394 xdnd_proxy_atom = gdk_atom_intern("XdndProxy", FALSE);
395 proxy_xid = GDK_WINDOW_XWINDOW(proxy_window);
396 type = None;
397 proxy = None;
399 old_warnings = gdk_error_warnings;
401 gdk_error_code = 0;
402 gdk_error_warnings = 0;
404 /* Check if somebody else already owns drops on the root window */
406 XGetWindowProperty(GDK_DISPLAY(), xid,
407 xdnd_proxy_atom, 0,
408 1, False, AnyPropertyType,
409 &type, &format, &nitems, &after,
410 (guchar **) &proxy_data);
412 if (type != None)
414 if (format == 32 && nitems == 1)
415 proxy = *proxy_data;
417 XFree(proxy_data);
420 /* The property was set, now check if the window it points to exists
421 * and has a XdndProxy property pointing to itself.
423 if (proxy)
425 XGetWindowProperty(GDK_DISPLAY(), proxy,
426 xdnd_proxy_atom, 0,
427 1, False, AnyPropertyType,
428 &type, &format, &nitems, &after,
429 (guchar **) &proxy_data);
431 if (!gdk_error_code && type != None)
433 if (format == 32 && nitems == 1)
434 if (*proxy_data != proxy)
435 proxy = GDK_NONE;
437 XFree(proxy_data);
439 else
440 proxy = GDK_NONE;
443 if (!proxy)
445 /* OK, we can set the property to point to us */
447 XChangeProperty(GDK_DISPLAY(), xid,
448 xdnd_proxy_atom,
449 gdk_atom_intern("WINDOW", FALSE),
450 32, PropModeReplace,
451 (guchar *) &proxy_xid, 1);
454 gdk_error_code = 0;
455 gdk_error_warnings = old_warnings;
457 XUngrabServer(GDK_DISPLAY());
458 gdk_flush();
460 if (!proxy)
462 /* Mark our window as a valid proxy window with a XdndProxy
463 * property pointing recursively;
465 XChangeProperty(GDK_DISPLAY(), proxy_xid,
466 xdnd_proxy_atom,
467 gdk_atom_intern("WINDOW", FALSE),
468 32, PropModeReplace,
469 (guchar *) &proxy_xid, 1);
472 return !proxy;
475 /* xid is the window (usually the root) which points to the proxy */
476 void release_xdnd_proxy(guint32 xid)
478 GdkAtom xdnd_proxy_atom;
480 xdnd_proxy_atom = gdk_atom_intern("XdndProxy", FALSE);
482 XDeleteProperty(GDK_DISPLAY(), xid, xdnd_proxy_atom);
485 /* Looks for the proxy window to get root window clicks from the window
486 * manager. Taken from gmc. NULL if there is no proxy window.
488 GdkWindow *find_click_proxy_window(void)
490 GdkAtom click_proxy_atom;
491 Atom type;
492 int format;
493 unsigned long nitems, after;
494 Window *proxy_data;
495 Window proxy;
496 guint32 old_warnings;
497 GdkWindow *proxy_gdk_window;
499 XGrabServer(GDK_DISPLAY());
501 click_proxy_atom = gdk_atom_intern("_WIN_DESKTOP_BUTTON_PROXY", FALSE);
502 type = None;
503 proxy = None;
505 old_warnings = gdk_error_warnings;
507 gdk_error_code = 0;
508 gdk_error_warnings = 0;
510 /* Check if the proxy window exists */
512 XGetWindowProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
513 click_proxy_atom, 0,
514 1, False, AnyPropertyType,
515 &type, &format, &nitems, &after,
516 (guchar **) &proxy_data);
518 if (type != None)
520 if (format == 32 && nitems == 1)
521 proxy = *proxy_data;
523 XFree(proxy_data);
526 /* If the property was set, check if the window it points to exists
527 * and has a _WIN_DESKTOP_BUTTON_PROXY property pointing to itself.
530 if (proxy)
532 XGetWindowProperty(GDK_DISPLAY(), proxy,
533 click_proxy_atom, 0,
534 1, False, AnyPropertyType,
535 &type, &format, &nitems, &after,
536 (guchar **) &proxy_data);
538 if (!gdk_error_code && type != None)
540 if (format == 32 && nitems == 1)
541 if (*proxy_data != proxy)
542 proxy = GDK_NONE;
544 XFree(proxy_data);
546 else
547 proxy = GDK_NONE;
550 gdk_error_code = 0;
551 gdk_error_warnings = old_warnings;
553 XUngrabServer(GDK_DISPLAY());
554 gdk_flush();
556 if (proxy)
557 proxy_gdk_window = gdk_window_foreign_new(proxy);
558 else
559 proxy_gdk_window = NULL;
561 return proxy_gdk_window;