4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2001, the ROX-Filer team.
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)
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
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 */
29 #include <sys/param.h>
34 #include <X11/Xatom.h>
37 #include "collection.h"
42 #include "gui_support.h"
46 GdkFont
*item_font
= NULL
;
47 GdkFont
*fixed_font
= NULL
;
48 GtkStyle
*fixed_style
= NULL
;
50 GdkColor red
= {0, 0xffff, 0, 0};
51 GdkGC
*red_gc
= NULL
; /* Not automatically initialised */
52 gint screen_width
, screen_height
;
54 static GdkAtom xa_cardinal
;
56 static GtkWidget
*current_dialog
= NULL
;
58 void gui_support_init()
62 fixed_font
= gdk_font_load("fixed");
64 /* Create a window and get its font rather than using
65 * the default style (allows customisation via .gtkrc)
67 tmp
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
68 gtk_widget_realize(tmp
);
70 item_font
= gtk_style_get_font(gtk_widget_get_style(tmp
));
72 item_font
= gtk_widget_get_style(tmp
)->font
;
74 gdk_font_ref(item_font
);
75 gtk_widget_destroy(tmp
);
77 fixed_style
= gtk_style_copy(gtk_widget_get_default_style());
79 gtk_style_set_font(fixed_style
, fixed_font
);
81 fixed_style
->font
= fixed_font
;
84 fixed_width
= gdk_string_width(fixed_font
, "m");
86 xa_cardinal
= gdk_atom_intern("CARDINAL", FALSE
);
88 gdk_color_alloc(gtk_widget_get_default_colormap(), &red
);
90 /* This call starts returning strange values after a while, so get
91 * the result here during init.
93 gdk_window_get_size(GDK_ROOT_PARENT(), &screen_width
, &screen_height
);
96 static void choice_clicked(GtkWidget
*widget
, gpointer number
)
100 choice_return
= gtk_object_get_data(GTK_OBJECT(widget
),
104 *choice_return
= GPOINTER_TO_INT(number
);
107 /* Open a modal dialog box showing a message.
108 * The user can choose from a selection of buttons at the bottom.
109 * Returns -1 if the window is destroyed, or the number of the button
110 * if one is clicked (starting from zero).
112 * If a dialog is already open, returns -1 without waiting AND
113 * brings the current dialog to the front.
115 int get_choice(char *title
,
117 int number_of_buttons
, ...)
120 GtkWidget
*vbox
, *action_area
, *separator
;
121 GtkWidget
*text
, *text_container
;
122 GtkWidget
*button
= NULL
, *default_button
;
129 gtk_widget_hide(current_dialog
);
130 gtk_widget_show(current_dialog
);
134 current_dialog
= dialog
= gtk_window_new(GTK_WINDOW_DIALOG
);
135 GTK_WIDGET_UNSET_FLAGS(dialog
, GTK_CAN_FOCUS
);
136 gtk_window_set_modal(GTK_WINDOW(dialog
), TRUE
);
137 gtk_window_set_title(GTK_WINDOW(dialog
), title
);
138 gtk_window_set_position(GTK_WINDOW(dialog
), GTK_WIN_POS_CENTER
);
139 gtk_container_set_border_width(GTK_CONTAINER(dialog
), 2);
141 vbox
= gtk_vbox_new(FALSE
, 0);
142 gtk_container_add(GTK_CONTAINER(dialog
), vbox
);
144 action_area
= gtk_hbox_new(TRUE
, 5);
145 gtk_container_set_border_width(GTK_CONTAINER(action_area
), 2);
146 gtk_box_pack_end(GTK_BOX(vbox
), action_area
, FALSE
, TRUE
, 0);
148 separator
= gtk_hseparator_new ();
149 gtk_box_pack_end(GTK_BOX(vbox
), separator
, FALSE
, TRUE
, 2);
151 text
= gtk_label_new(message
);
152 gtk_label_set_line_wrap(GTK_LABEL(text
), TRUE
);
153 text_container
= gtk_event_box_new();
154 gtk_container_set_border_width(GTK_CONTAINER(text_container
), 40);
155 gtk_container_add(GTK_CONTAINER(text_container
), text
);
157 gtk_box_pack_start(GTK_BOX(vbox
),
161 va_start(ap
, number_of_buttons
);
163 default_button
= NULL
;
164 for (i
= 0; i
< number_of_buttons
; i
++)
166 button
= gtk_button_new_with_label(va_arg(ap
, char *));
167 GTK_WIDGET_SET_FLAGS(button
, GTK_CAN_DEFAULT
);
168 gtk_misc_set_padding(GTK_MISC(GTK_BIN(button
)->child
), 16, 2);
169 gtk_object_set_data(GTK_OBJECT(button
), "choice_return",
171 gtk_box_pack_start(GTK_BOX(action_area
), button
, TRUE
, TRUE
, 0);
172 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
173 GTK_SIGNAL_FUNC(choice_clicked
), GINT_TO_POINTER(i
));
175 default_button
= button
;
178 gtk_window_set_focus(GTK_WINDOW(dialog
), default_button
);
179 gtk_window_set_default(GTK_WINDOW(dialog
), default_button
);
180 gtk_container_set_focus_child(GTK_CONTAINER(action_area
),
185 gtk_object_set_data(GTK_OBJECT(dialog
), "choice_return",
187 gtk_signal_connect(GTK_OBJECT(dialog
), "destroy",
188 GTK_SIGNAL_FUNC(choice_clicked
), GINT_TO_POINTER(-1));
192 gtk_widget_show_all(dialog
);
194 while (choice_return
== -2)
195 g_main_iteration(TRUE
);
197 retval
= choice_return
;
200 gtk_widget_destroy(dialog
);
202 current_dialog
= NULL
;
207 /* Display a message in a window */
208 void report_error(char *title
, char *message
, ...)
213 g_return_if_fail(message
!= NULL
);
215 va_start(args
, message
);
216 s
= g_strdup_vprintf(message
, args
);
222 get_choice(title
, s
, 1, _("OK"));
226 /* Display a message in a window with "ROX-Filer" as title */
227 void report_rox_error(char *message
, ...)
232 g_return_if_fail(message
!= NULL
);
234 va_start(args
, message
);
235 s
= g_strdup_vprintf(message
, args
);
238 report_error(PROJECT
, "%s", s
);
242 void set_cardinal_property(GdkWindow
*window
, GdkAtom prop
, guint32 value
)
244 gdk_property_change(window
, prop
, xa_cardinal
, 32,
245 GDK_PROP_MODE_REPLACE
, (gchar
*) &value
, 1);
248 /* NB: Also used for pinned icons.
249 * TODO: Set the level here too.
251 void make_panel_window(GdkWindow
*window
)
253 static gboolean need_init
= TRUE
;
254 static GdkAtom xa_state
, xa_atom
, xa_net_state
;
255 static GdkAtom state_list
[3];
257 if (override_redirect
)
259 gdk_window_lower(window
);
260 gdk_window_set_override_redirect(window
, TRUE
);
266 xa_state
= gdk_atom_intern("_WIN_STATE", FALSE
);
267 xa_atom
= gdk_atom_intern("ATOM", FALSE
);
268 xa_net_state
= gdk_atom_intern("_NET_WM_STATE", FALSE
);
270 state_list
[0] = gdk_atom_intern("_NET_WM_STATE_STICKY", FALSE
);
271 state_list
[1] = gdk_atom_intern("_NET_WM_STATE_SKIP_PAGER",
273 state_list
[2] = gdk_atom_intern("_NET_WM_STATE_SKIP_TASKBAR",
279 gdk_window_set_decorations(window
, 0);
280 gdk_window_set_functions(window
, 0);
282 /* Don't hide panel/pinboard windows initially (WIN_STATE_HIDDEN).
283 * Needed for IceWM - Christopher Arndt <chris.arndt@web.de>
285 set_cardinal_property(window
, xa_state
,
287 WIN_STATE_FIXED_POSITION
| WIN_STATE_ARRANGE_IGNORE
);
289 gdk_property_change(window
, xa_net_state
, xa_atom
, 32,
290 GDK_PROP_MODE_APPEND
, (guchar
*) state_list
, 3);
293 gint
hide_dialog_event(GtkWidget
*widget
, GdkEvent
*event
, gpointer window
)
295 gtk_widget_hide((GtkWidget
*) window
);
300 static gboolean
error_idle_cb(gpointer data
)
302 char **error
= (char **) data
;
304 report_error(error
[0], error
[1]);
307 error
[0] = error
[1] = NULL
;
309 if (--number_of_windows
== 0)
315 /* Display an error next time we are idle */
316 void delayed_error(char *title
, char *error
, ...)
318 static char *delayed_error_data
[2] = {NULL
, NULL
};
319 gboolean already_open
;
322 g_return_if_fail(error
!= NULL
);
324 already_open
= delayed_error_data
[1] != NULL
;
326 g_free(delayed_error_data
[0]);
327 g_free(delayed_error_data
[1]);
329 delayed_error_data
[0] = g_strdup(title
);
330 va_start(args
, error
);
331 delayed_error_data
[1] = g_strdup_vprintf(error
, args
);
337 gtk_idle_add(error_idle_cb
, delayed_error_data
);
342 /* Display an error with "ROX-Filer" as title next time we are idle */
343 void delayed_rox_error(char *error
, ...)
348 g_return_if_fail(error
!= NULL
);
350 va_start(args
, error
);
351 s
= g_strdup_vprintf(error
, args
);
354 delayed_error(PROJECT
, "%s", s
);
358 /* Load the file into memory. Return TRUE on success.
359 * Block is zero terminated (but this is not included in the length).
361 gboolean
load_file(char *pathname
, char **data_out
, long *length_out
)
366 gboolean retval
= FALSE
;
368 file
= fopen(pathname
, "r");
372 delayed_rox_error("open(%s): %s", pathname
, g_strerror(errno
));
376 fseek(file
, 0, SEEK_END
);
377 length
= ftell(file
);
379 buffer
= malloc(length
+ 1);
382 fseek(file
, 0, SEEK_SET
);
383 fread(buffer
, 1, length
, file
);
387 delayed_rox_error(_("Error reading file %s: %s"),
388 pathname
, g_strerror(errno
));
394 *length_out
= length
;
395 buffer
[length
] = '\0';
400 delayed_rox_error(_("Can't allocate memory for buffer to "
408 GtkWidget
*new_help_button(HelpFunc show_help
, gpointer data
)
412 b
= gtk_button_new();
413 gtk_button_set_relief(GTK_BUTTON(b
), GTK_RELIEF_NONE
);
414 icon
= gtk_pixmap_new(im_help
->pixmap
, im_help
->mask
);
415 gtk_container_add(GTK_CONTAINER(b
), icon
);
416 gtk_signal_connect_object(GTK_OBJECT(b
), "clicked",
417 GTK_SIGNAL_FUNC(show_help
), data
);
419 GTK_WIDGET_UNSET_FLAGS(b
, GTK_CAN_FOCUS
);
424 /* Read file into memory. Call parse_line(guchar *line) for each line
425 * in the file. Callback returns NULL on success, or an error message
426 * if something went wrong. Only the first error is displayed to the user.
428 void parse_file(char *path
, ParseFunc
*parse_line
)
432 gboolean seen_error
= FALSE
;
434 if (load_file(path
, &data
, &length
))
440 while (line
&& *line
)
442 eol
= strchr(line
, '\n');
446 error
= parse_line(line
);
448 if (error
&& !seen_error
)
451 _("Error in '%s' file at line %d: "
453 "This may be due to upgrading from a previous version of "
454 "ROX-Filer. Open the Options window and click on Save.\n"
455 "Further errors will be ignored."),
471 /* Sets up a proxy window for DnD on the specified X window.
472 * Courtesy of Owen Taylor (taken from gmc).
474 gboolean
setup_xdnd_proxy(guint32 xid
, GdkWindow
*proxy_window
)
476 GdkAtom xdnd_proxy_atom
;
480 unsigned long nitems
, after
;
484 guint32 old_warnings
;
487 XGrabServer(GDK_DISPLAY());
489 xdnd_proxy_atom
= gdk_atom_intern("XdndProxy", FALSE
);
490 proxy_xid
= GDK_WINDOW_XWINDOW(proxy_window
);
495 gdk_error_trap_push();
497 old_warnings
= gdk_error_warnings
;
499 gdk_error_warnings
= 0;
502 /* Check if somebody else already owns drops on the root window */
504 XGetWindowProperty(GDK_DISPLAY(), xid
,
505 gdk_x11_atom_to_xatom(xdnd_proxy_atom
), 0,
506 1, False
, AnyPropertyType
,
507 &type
, &format
, &nitems
, &after
,
508 (guchar
**) &proxy_data
);
512 if (format
== 32 && nitems
== 1)
518 /* The property was set, now check if the window it points to exists
519 * and has a XdndProxy property pointing to itself.
527 XGetWindowProperty(GDK_DISPLAY(), proxy
,
528 gdk_x11_atom_to_xatom(xdnd_proxy_atom
),
529 0, 1, False
, AnyPropertyType
,
530 &type
, &format
, &nitems
, &after
,
531 (guchar
**) &proxy_data
);
534 gdk_error_code
= gdk_error_trap_pop();
535 gdk_error_trap_push();
538 if (!gdk_error_code
&& type
!= None
)
540 if (format
== 32 && nitems
== 1)
541 if (*proxy_data
!= proxy
)
547 proxy
= gdk_x11_atom_to_xatom(GDK_NONE
);
552 /* OK, we can set the property to point to us */
553 /* TODO: Use gdk call? */
555 XChangeProperty(GDK_DISPLAY(), xid
,
556 gdk_x11_atom_to_xatom(xdnd_proxy_atom
),
557 gdk_x11_atom_to_xatom(gdk_atom_intern("WINDOW",
560 (guchar
*) &proxy_xid
, 1);
564 gdk_error_trap_pop();
567 gdk_error_warnings
= old_warnings
;
570 XUngrabServer(GDK_DISPLAY());
575 /* Mark our window as a valid proxy window with a XdndProxy
576 * property pointing recursively;
578 XChangeProperty(GDK_DISPLAY(), proxy_xid
,
579 gdk_x11_atom_to_xatom(xdnd_proxy_atom
),
580 gdk_x11_atom_to_xatom(gdk_atom_intern("WINDOW",
583 (guchar
*) &proxy_xid
, 1);
589 /* xid is the window (usually the root) which points to the proxy */
590 void release_xdnd_proxy(guint32 xid
)
592 GdkAtom xdnd_proxy_atom
;
594 xdnd_proxy_atom
= gdk_atom_intern("XdndProxy", FALSE
);
596 XDeleteProperty(GDK_DISPLAY(), xid
,
597 gdk_x11_atom_to_xatom(xdnd_proxy_atom
));
600 /* Looks for the proxy window to get root window clicks from the window
601 * manager. Taken from gmc. NULL if there is no proxy window.
603 GdkWindow
*find_click_proxy_window(void)
605 GdkAtom click_proxy_atom
;
608 unsigned long nitems
, after
;
611 GdkWindow
*proxy_gdk_window
;
613 guint32 old_warnings
;
616 XGrabServer(GDK_DISPLAY());
618 click_proxy_atom
= gdk_atom_intern("_WIN_DESKTOP_BUTTON_PROXY", FALSE
);
623 gdk_error_trap_push();
625 old_warnings
= gdk_error_warnings
;
627 gdk_error_warnings
= 0;
630 /* Check if the proxy window exists */
632 XGetWindowProperty(GDK_DISPLAY(), GDK_ROOT_WINDOW(),
633 gdk_x11_atom_to_xatom(click_proxy_atom
), 0,
634 1, False
, AnyPropertyType
,
635 &type
, &format
, &nitems
, &after
,
636 (guchar
**) &proxy_data
);
640 if (format
== 32 && nitems
== 1)
646 /* If the property was set, check if the window it points to exists
647 * and has a _WIN_DESKTOP_BUTTON_PROXY property pointing to itself.
655 XGetWindowProperty(GDK_DISPLAY(), proxy
,
656 gdk_x11_atom_to_xatom(click_proxy_atom
), 0,
657 1, False
, AnyPropertyType
,
658 &type
, &format
, &nitems
, &after
,
659 (guchar
**) &proxy_data
);
662 gdk_error_code
= gdk_error_trap_pop();
663 gdk_error_trap_push();
666 if (!gdk_error_code
&& type
!= None
)
668 if (format
== 32 && nitems
== 1)
669 if (*proxy_data
!= proxy
)
670 proxy
= gdk_x11_atom_to_xatom(GDK_NONE
);
675 proxy
= gdk_x11_atom_to_xatom(GDK_NONE
);
679 gdk_error_trap_pop();
682 gdk_error_warnings
= old_warnings
;
685 XUngrabServer(GDK_DISPLAY());
689 proxy_gdk_window
= gdk_window_foreign_new(proxy
);
691 proxy_gdk_window
= NULL
;
693 return proxy_gdk_window
;
696 /* Returns the position of the pointer.
697 * TRUE if any modifier keys or mouse buttons are pressed.
699 gboolean
get_pointer_xy(int *x
, int *y
)
703 gdk_window_get_pointer(NULL
, x
, y
, &mask
);
708 #define DECOR_BORDER 32
710 /* Centre the window at these coords */
711 void centre_window(GdkWindow
*window
, int x
, int y
)
715 g_return_if_fail(window
!= NULL
);
717 gdk_window_get_size(window
, &w
, &h
);
722 gdk_window_move(window
,
723 CLAMP(x
, DECOR_BORDER
, screen_width
- w
- DECOR_BORDER
),
724 CLAMP(y
, DECOR_BORDER
, screen_height
- h
- DECOR_BORDER
));
727 /* Non-NULL => selector window is open */
728 static GtkColorSelectionDialog
*current_csel_box
= NULL
;
730 static void get_new_colour(GtkWidget
*ok
, GtkWidget
*button
)
736 g_return_if_fail(current_csel_box
!= NULL
);
738 csel
= current_csel_box
->colorsel
;
740 gtk_color_selection_get_color(GTK_COLOR_SELECTION(csel
), c
);
741 colour
.red
= c
[0] * 0xffff;
742 colour
.green
= c
[1] * 0xffff;
743 colour
.blue
= c
[2] * 0xffff;
745 button_patch_set_colour(button
, &colour
);
747 gtk_widget_destroy(GTK_WIDGET(current_csel_box
));
750 static void open_coloursel(GtkWidget
*ok
, GtkWidget
*button
)
752 GtkColorSelectionDialog
*csel
;
753 GtkWidget
*dialog
, *patch
;
756 if (current_csel_box
)
757 gtk_widget_destroy(GTK_WIDGET(current_csel_box
));
759 dialog
= gtk_color_selection_dialog_new(NULL
);
760 csel
= GTK_COLOR_SELECTION_DIALOG(dialog
);
761 current_csel_box
= csel
;
763 gtk_signal_connect_object(GTK_OBJECT(dialog
), "destroy",
764 GTK_SIGNAL_FUNC(set_to_null
),
765 (GtkObject
*) ¤t_csel_box
);
766 gtk_widget_hide(csel
->help_button
);
767 gtk_signal_connect_object(GTK_OBJECT(csel
->cancel_button
), "clicked",
768 GTK_SIGNAL_FUNC(gtk_widget_destroy
),
770 gtk_signal_connect(GTK_OBJECT(csel
->ok_button
), "clicked",
771 GTK_SIGNAL_FUNC(get_new_colour
), button
);
773 patch
= GTK_BIN(button
)->child
;
775 c
[0] = ((gdouble
) patch
->style
->bg
[GTK_STATE_NORMAL
].red
) / 0xffff;
776 c
[1] = ((gdouble
) patch
->style
->bg
[GTK_STATE_NORMAL
].green
) / 0xffff;
777 c
[2] = ((gdouble
) patch
->style
->bg
[GTK_STATE_NORMAL
].blue
) / 0xffff;
778 gtk_color_selection_set_color(GTK_COLOR_SELECTION(csel
->colorsel
), c
);
780 gtk_widget_show(dialog
);
783 /* Turns a button (with no child) into a colour patch button.
784 * When clicked, the button will open the colour selector window
785 * and let the user pick a colour. Afterwards, the buttons changes
786 * colour to the one chosen.
788 void make_colour_patch(GtkWidget
*button
)
792 da
= gtk_drawing_area_new();
793 gtk_drawing_area_size(GTK_DRAWING_AREA(da
), 64, 12);
794 gtk_container_add(GTK_CONTAINER(button
), da
);
795 gtk_signal_connect(GTK_OBJECT(button
), "clicked",
796 GTK_SIGNAL_FUNC(open_coloursel
), button
);
799 /* Returns a pointer to the style's background colour */
800 GdkColor
*button_patch_get_colour(GtkWidget
*button
)
802 return &(GTK_BIN(button
)->child
)->style
->bg
[GTK_STATE_NORMAL
];
805 void button_patch_set_colour(GtkWidget
*button
, GdkColor
*colour
)
810 patch
= GTK_BIN(button
)->child
;
812 style
= gtk_style_copy(GTK_WIDGET(patch
)->style
);
813 style
->bg
[GTK_STATE_NORMAL
].red
= colour
->red
;
814 style
->bg
[GTK_STATE_NORMAL
].green
= colour
->green
;
815 style
->bg
[GTK_STATE_NORMAL
].blue
= colour
->blue
;
816 gtk_widget_set_style(patch
, style
);
817 gtk_style_unref(style
);
819 if (GTK_WIDGET_REALIZED(patch
))
820 gdk_window_clear(patch
->window
);
823 static GtkWidget
*current_wink_widget
= NULL
;
824 static gint wink_timeout
= -1; /* Called when it's time to stop */
825 static gint wink_destroy
; /* Called if the widget dies first */
827 static gboolean
end_wink(gpointer data
)
829 gtk_drag_unhighlight(current_wink_widget
);
831 gtk_signal_disconnect(GTK_OBJECT(current_wink_widget
), wink_destroy
);
833 current_wink_widget
= NULL
;
838 static void cancel_wink(void)
840 gtk_timeout_remove(wink_timeout
);
844 static void wink_widget_died(gpointer data
)
846 current_wink_widget
= NULL
;
847 gtk_timeout_remove(wink_timeout
);
850 /* Draw a black box around this widget, briefly.
851 * Note: uses the drag highlighting code for now.
853 void wink_widget(GtkWidget
*widget
)
855 g_return_if_fail(widget
!= NULL
);
857 if (current_wink_widget
)
860 current_wink_widget
= widget
;
861 gtk_drag_highlight(current_wink_widget
);
863 wink_timeout
= gtk_timeout_add(300, (GtkFunction
) end_wink
, NULL
);
865 wink_destroy
= gtk_signal_connect_object(GTK_OBJECT(widget
), "destroy",
866 GTK_SIGNAL_FUNC(wink_widget_died
), NULL
);
869 static gboolean
idle_destroy_cb(GtkWidget
*widget
)
871 gtk_widget_unref(widget
);
872 gtk_widget_destroy(widget
);
876 /* Destroy the widget in an idle callback */
877 void destroy_on_idle(GtkWidget
*widget
)
879 gtk_widget_ref(widget
);
880 gtk_idle_add((GtkFunction
) idle_destroy_cb
, widget
);