r343: Updated the docs.
[rox-filer/ma.git] / ROX-Filer / src / gui_support.c
blobd85c82479fbce64cec327a1291620b5c2a496b90
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@users.sourceforge.net>.
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 <X11/Xlib.h>
34 #include <X11/Xatom.h>
35 #include <gdk/gdkx.h>
36 #include <gdk/gdk.h>
38 #include "global.h"
40 #include "main.h"
41 #include "gui_support.h"
42 #include "pixmaps.h"
44 GdkFont *item_font = NULL;
45 GdkFont *fixed_font = NULL;
46 GtkStyle *fixed_style = NULL;
47 gint fixed_width;
48 GdkColor red = {0, 0xffff, 0, 0};
49 GdkGC *red_gc = NULL; /* Not automatically initialised */
50 gint screen_width, screen_height;
52 static GdkAtom xa_cardinal;
54 void gui_support_init()
56 fixed_font = gdk_font_load("fixed");
57 item_font = gtk_widget_get_default_style()->font;
59 fixed_style = gtk_style_copy(gtk_widget_get_default_style());
60 fixed_style->font = fixed_font;
62 fixed_width = gdk_string_width(fixed_font, "m");
64 xa_cardinal = gdk_atom_intern("CARDINAL", FALSE);
66 gdk_color_alloc(gtk_widget_get_default_colormap(), &red);
68 /* This call starts returning strange values after a while, so get
69 * the result here during init.
71 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width, &screen_height);
74 static void choice_clicked(GtkWidget *widget, gpointer number)
76 int *choice_return;
78 choice_return = gtk_object_get_data(GTK_OBJECT(widget),
79 "choice_return");
81 if (choice_return)
82 *choice_return = (int) number;
85 /* Open a modal dialog box showing a message.
86 * The user can choose from a selection of buttons at the bottom.
87 * Returns -1 if the window is destroyed, or the number of the button
88 * if one is clicked (starting from zero).
90 int get_choice(char *title,
91 char *message,
92 int number_of_buttons, ...)
94 GtkWidget *dialog;
95 GtkWidget *vbox, *action_area, *separator;
96 GtkWidget *text, *text_container;
97 GtkWidget *button = NULL;
98 int i, retval;
99 va_list ap;
100 int choice_return;
102 dialog = gtk_window_new(GTK_WINDOW_DIALOG);
103 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
104 gtk_window_set_title(GTK_WINDOW(dialog), title);
105 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
107 vbox = gtk_vbox_new(FALSE, 0);
108 gtk_container_add(GTK_CONTAINER(dialog), vbox);
110 action_area = gtk_hbox_new(TRUE, 5);
111 gtk_container_set_border_width(GTK_CONTAINER(action_area), 10);
112 gtk_box_pack_end(GTK_BOX(vbox), action_area, FALSE, TRUE, 0);
114 separator = gtk_hseparator_new ();
115 gtk_box_pack_end(GTK_BOX(vbox), separator, FALSE, TRUE, 0);
117 text = gtk_label_new(message);
118 gtk_label_set_line_wrap(GTK_LABEL(text), TRUE);
119 text_container = gtk_event_box_new();
120 gtk_container_set_border_width(GTK_CONTAINER(text_container), 32);
121 gtk_container_add(GTK_CONTAINER(text_container), text);
123 gtk_box_pack_start(GTK_BOX(vbox),
124 text_container,
125 TRUE, TRUE, 0);
127 va_start(ap, number_of_buttons);
129 for (i = 0; i < number_of_buttons; i++)
131 button = gtk_button_new_with_label(va_arg(ap, char *));
132 gtk_object_set_data(GTK_OBJECT(button), "choice_return",
133 &choice_return);
134 gtk_box_pack_start(GTK_BOX(action_area),
135 button,
136 TRUE, TRUE, 16);
137 gtk_signal_connect(GTK_OBJECT(button), "clicked",
138 choice_clicked, (gpointer) i);
139 if (i == 0)
140 gtk_window_set_focus(GTK_WINDOW(dialog), button);
142 if (!i)
143 gtk_widget_grab_focus(button);
145 va_end(ap);
147 gtk_object_set_data(GTK_OBJECT(dialog), "choice_return",
148 &choice_return);
149 gtk_signal_connect(GTK_OBJECT(dialog), "destroy", choice_clicked,
150 (gpointer) -1);
152 choice_return = -2;
154 gtk_widget_show_all(dialog);
156 while (choice_return == -2)
157 g_main_iteration(TRUE);
159 retval = choice_return;
161 if (retval != -1)
162 gtk_widget_destroy(dialog);
164 return retval;
167 /* Display a message in a window */
168 void report_error(char *title, char *message)
170 g_return_if_fail(message != NULL);
172 if (!title)
173 title = _("Error");
175 get_choice(title, message, 1, "OK");
178 void set_cardinal_property(GdkWindow *window, GdkAtom prop, guint32 value)
180 gdk_property_change(window, prop, xa_cardinal, 32,
181 GDK_PROP_MODE_REPLACE, (gchar *) &value, 1);
184 /* NB: Also used for pinned icons.
185 * TODO: Set the level here too.
187 void make_panel_window(GdkWindow *window)
189 static gboolean need_init = TRUE;
190 static GdkAtom xa_state;
192 if (need_init)
194 xa_state = gdk_atom_intern("_WIN_STATE", FALSE);
195 need_init = FALSE;
198 gdk_window_set_decorations(window, 0);
199 gdk_window_set_functions(window, 0);
201 set_cardinal_property(window, xa_state,
202 WIN_STATE_STICKY | WIN_STATE_HIDDEN |
203 WIN_STATE_FIXED_POSITION | WIN_STATE_ARRANGE_IGNORE);
206 gint hide_dialog_event(GtkWidget *widget, GdkEvent *event, gpointer window)
208 gtk_widget_hide((GtkWidget *) window);
210 return TRUE;
213 static gboolean error_idle_cb(gpointer data)
215 char **error = (char **) data;
217 report_error(error[0], error[1]);
218 g_free(error[0]);
219 g_free(error[1]);
220 error[0] = error[1] = NULL;
222 if (--number_of_windows == 0)
223 gtk_main_quit();
225 return FALSE;
228 /* Display an error next time we are idle */
229 void delayed_error(char *title, char *error)
231 static char *delayed_error_data[2] = {NULL, NULL};
232 gboolean already_open;
234 g_return_if_fail(error != NULL);
236 already_open = delayed_error_data[1] != NULL;
238 g_free(delayed_error_data[0]);
239 g_free(delayed_error_data[1]);
241 delayed_error_data[0] = g_strdup(title);
242 delayed_error_data[1] = g_strdup(error);
244 if (already_open)
245 return;
247 gtk_idle_add(error_idle_cb, delayed_error_data);
249 number_of_windows++;
252 /* Load the file into memory. Return TRUE on success.
253 * Block is zero terminated (but this is not included in the length).
255 gboolean load_file(char *pathname, char **data_out, long *length_out)
257 FILE *file;
258 long length;
259 char *buffer;
260 gboolean retval = FALSE;
262 file = fopen(pathname, "r");
264 if (!file)
266 guchar *message;
268 message = g_strdup_printf("open(%s): %s",
269 pathname, g_strerror(errno));
270 delayed_error(PROJECT, message);
271 g_free(message);
272 return FALSE;
275 fseek(file, 0, SEEK_END);
276 length = ftell(file);
278 buffer = malloc(length + 1);
279 if (buffer)
281 fseek(file, 0, SEEK_SET);
282 fread(buffer, 1, length, file);
284 if (ferror(file))
286 guchar *tmp;
288 tmp = g_strdup_printf("%s: %s\n",
289 pathname, g_strerror(errno));
290 delayed_error(_("Error reading file"), tmp);
291 g_free(tmp);
292 g_free(buffer);
294 else
296 *data_out = buffer;
297 *length_out = length;
298 buffer[length] = '\0';
299 retval = TRUE;
302 else
303 delayed_error(PROJECT,
304 _("Can't allocate memory for buffer to "
305 "load this file"));
307 fclose(file);
309 return retval;
312 GtkWidget *new_help_button(HelpFunc show_help, gpointer data)
314 GtkWidget *b, *icon;
316 b = gtk_button_new();
317 gtk_button_set_relief(GTK_BUTTON(b), GTK_RELIEF_NONE);
318 icon = gtk_pixmap_new(im_help->pixmap, im_help->mask);
319 gtk_container_add(GTK_CONTAINER(b), icon);
320 gtk_signal_connect_object(GTK_OBJECT(b), "clicked", show_help, data);
322 GTK_WIDGET_UNSET_FLAGS(b, GTK_CAN_FOCUS);
324 return b;
327 /* Read file into memory. Call parse_line(guchar *line) for each line
328 * in the file. Callback returns NULL on success, or an error message
329 * if something went wrong. Only the first error is displayed to the user.
331 void parse_file(char *path, ParseFunc *parse_line)
333 char *data;
334 long length;
335 gboolean seen_error = FALSE;
337 if (load_file(path, &data, &length))
339 char *eol, *error;
340 char *line = data;
341 int line_number = 1;
343 while (line && *line)
345 eol = strchr(line, '\n');
346 if (eol)
347 *eol = '\0';
349 error = parse_line(line);
351 if (error && !seen_error)
353 GString *message;
355 message = g_string_new(NULL);
356 g_string_sprintf(message,
357 _("Error in '%s' file at line %d: "
358 "\n\"%s\"\n"
359 "This may be due to upgrading from a previous version of "
360 "ROX-Filer. Open the Options window and click on Save.\n"
361 "Further errors will be ignored."),
362 path,
363 line_number,
364 error);
365 delayed_error(PROJECT, message->str);
366 g_string_free(message, TRUE);
367 seen_error = TRUE;
370 if (!eol)
371 break;
372 line = eol + 1;
373 line_number++;
375 g_free(data);
379 /* Sets up a proxy window for DnD on the specified X window.
380 * Courtesy of Owen Taylor (taken from gmc).
382 gboolean setup_xdnd_proxy(guint32 xid, GdkWindow *proxy_window)
384 GdkAtom xdnd_proxy_atom;
385 Window proxy_xid;
386 Atom type;
387 int format;
388 unsigned long nitems, after;
389 Window *proxy_data;
390 Window proxy;
391 guint32 old_warnings;
393 XGrabServer(GDK_DISPLAY());
395 xdnd_proxy_atom = gdk_atom_intern("XdndProxy", FALSE);
396 proxy_xid = GDK_WINDOW_XWINDOW(proxy_window);
397 type = None;
398 proxy = None;
400 old_warnings = gdk_error_warnings;
402 gdk_error_code = 0;
403 gdk_error_warnings = 0;
405 /* Check if somebody else already owns drops on the root window */
407 XGetWindowProperty(GDK_DISPLAY(), xid,
408 xdnd_proxy_atom, 0,
409 1, False, AnyPropertyType,
410 &type, &format, &nitems, &after,
411 (guchar **) &proxy_data);
413 if (type != None)
415 if (format == 32 && nitems == 1)
416 proxy = *proxy_data;
418 XFree(proxy_data);
421 /* The property was set, now check if the window it points to exists
422 * and has a XdndProxy property pointing to itself.
424 if (proxy)
426 XGetWindowProperty(GDK_DISPLAY(), proxy,
427 xdnd_proxy_atom, 0,
428 1, False, AnyPropertyType,
429 &type, &format, &nitems, &after,
430 (guchar **) &proxy_data);
432 if (!gdk_error_code && type != None)
434 if (format == 32 && nitems == 1)
435 if (*proxy_data != proxy)
436 proxy = GDK_NONE;
438 XFree(proxy_data);
440 else
441 proxy = GDK_NONE;
444 if (!proxy)
446 /* OK, we can set the property to point to us */
448 XChangeProperty(GDK_DISPLAY(), xid,
449 xdnd_proxy_atom,
450 gdk_atom_intern("WINDOW", FALSE),
451 32, PropModeReplace,
452 (guchar *) &proxy_xid, 1);
455 gdk_error_code = 0;
456 gdk_error_warnings = old_warnings;
458 XUngrabServer(GDK_DISPLAY());
459 gdk_flush();
461 if (!proxy)
463 /* Mark our window as a valid proxy window with a XdndProxy
464 * property pointing recursively;
466 XChangeProperty(GDK_DISPLAY(), proxy_xid,
467 xdnd_proxy_atom,
468 gdk_atom_intern("WINDOW", FALSE),
469 32, PropModeReplace,
470 (guchar *) &proxy_xid, 1);
473 return !proxy;
476 /* xid is the window (usually the root) which points to the proxy */
477 void release_xdnd_proxy(guint32 xid)
479 GdkAtom xdnd_proxy_atom;
481 xdnd_proxy_atom = gdk_atom_intern("XdndProxy", FALSE);
483 XDeleteProperty(GDK_DISPLAY(), xid, xdnd_proxy_atom);
486 /* Looks for the proxy window to get root window clicks from the window
487 * manager. Taken from gmc. NULL if there is no proxy window.
489 GdkWindow *find_click_proxy_window(void)
491 GdkAtom click_proxy_atom;
492 Atom type;
493 int format;
494 unsigned long nitems, after;
495 Window *proxy_data;
496 Window proxy;
497 guint32 old_warnings;
498 GdkWindow *proxy_gdk_window;
500 XGrabServer(GDK_DISPLAY());
502 click_proxy_atom = gdk_atom_intern("_WIN_DESKTOP_BUTTON_PROXY", FALSE);
503 type = None;
504 proxy = None;
506 old_warnings = gdk_error_warnings;
508 gdk_error_code = 0;
509 gdk_error_warnings = 0;
511 /* Check if the proxy window exists */
513 XGetWindowProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
514 click_proxy_atom, 0,
515 1, False, AnyPropertyType,
516 &type, &format, &nitems, &after,
517 (guchar **) &proxy_data);
519 if (type != None)
521 if (format == 32 && nitems == 1)
522 proxy = *proxy_data;
524 XFree(proxy_data);
527 /* If the property was set, check if the window it points to exists
528 * and has a _WIN_DESKTOP_BUTTON_PROXY property pointing to itself.
531 if (proxy)
533 XGetWindowProperty(GDK_DISPLAY(), proxy,
534 click_proxy_atom, 0,
535 1, False, AnyPropertyType,
536 &type, &format, &nitems, &after,
537 (guchar **) &proxy_data);
539 if (!gdk_error_code && type != None)
541 if (format == 32 && nitems == 1)
542 if (*proxy_data != proxy)
543 proxy = GDK_NONE;
545 XFree(proxy_data);
547 else
548 proxy = GDK_NONE;
551 gdk_error_code = 0;
552 gdk_error_warnings = old_warnings;
554 XUngrabServer(GDK_DISPLAY());
555 gdk_flush();
557 if (proxy)
558 proxy_gdk_window = gdk_window_foreign_new(proxy);
559 else
560 proxy_gdk_window = NULL;
562 return proxy_gdk_window;
565 /* Returns the position of the pointer.
566 * TRUE if any modifier keys or mouse buttons are pressed.
568 gboolean get_pointer_xy(int *x, int *y)
570 Window root, child;
571 int win_x, win_y;
572 unsigned int mask;
574 XQueryPointer(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
575 &root, &child, x, y, &win_x, &win_y, &mask);
577 return mask != 0;
580 #define DECOR_BORDER 32
582 /* Centre the window at these coords */
583 void centre_window(GdkWindow *window, int x, int y)
585 int w, h;
587 g_return_if_fail(window != NULL);
589 gdk_window_get_size(window, &w, &h);
591 x -= w / 2;
592 y -= h / 2;
594 gdk_window_move(window,
595 CLAMP(x, DECOR_BORDER, screen_width - w - DECOR_BORDER),
596 CLAMP(y, DECOR_BORDER, screen_height - h - DECOR_BORDER));