4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, 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 /* pinboard.c - icons on the desktop background */
32 #include <gtk/gtkinvisible.h>
34 #include <libxml/parser.h>
46 #include "gui_support.h"
57 static gboolean tmp_icon_selected
= FALSE
; /* When dragging */
60 guchar
*name
; /* Leaf name */
64 gchar
*backdrop
; /* Pathname */
65 BackdropStyle backdrop_style
;
66 gint to_backdrop_app
; /* pipe FD, or -1 */
67 gint from_backdrop_app
; /* pipe FD, or -1 */
69 GString
*input_buffer
;
71 GtkWidget
*window
; /* Screen-sized window */
75 #define IS_PIN_ICON(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), pin_icon_get_type())
77 typedef struct _PinIconClass PinIconClass
;
78 typedef struct _PinIcon PinIcon
;
80 struct _PinIconClass
{
89 GtkWidget
*widget
; /* The drawing area for the icon */
93 /* The number of pixels between the bottom of the image and the top
98 /* The size of the border around the icon which is used when winking */
102 #define GRID_STEP_FINE 2
103 #define GRID_STEP_MED 16
104 #define GRID_STEP_COARSE 32
106 static PinIcon
*current_wink_icon
= NULL
;
107 static gint wink_timeout
;
109 /* Used for the text colours (only) in the icons (and tasklist windows) */
110 GdkColor pin_text_fg_col
, pin_text_bg_col
;
112 /* Style that all the icons should use. NULL => regenerate from text_fg/bg */
113 static GtkStyle
*pinicon_style
= NULL
;
115 Pinboard
*current_pinboard
= NULL
;
116 static gint loading_pinboard
= 0; /* Non-zero => loading */
118 /* The Icon that was used to start the current drag, if any */
119 Icon
*pinboard_drag_in_progress
= NULL
;
127 static Option o_pinboard_clamp_icons
, o_pinboard_grid_step
;
128 static Option o_pinboard_fg_colour
, o_pinboard_bg_colour
;
129 static Option o_pinboard_tasklist
;
131 /* Static prototypes */
132 static GType
pin_icon_get_type(void);
133 static void set_size_and_style(PinIcon
*pi
);
134 static gint
stop_expose(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
);
135 static gint
draw_icon(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
);
136 static gint
end_wink(gpointer data
);
137 static gboolean
button_release_event(GtkWidget
*widget
,
138 GdkEventButton
*event
,
140 static gboolean
enter_notify(GtkWidget
*widget
,
141 GdkEventCrossing
*event
,
143 static gboolean
button_press_event(GtkWidget
*widget
,
144 GdkEventButton
*event
,
146 static gint
icon_motion_notify(GtkWidget
*widget
,
147 GdkEventMotion
*event
,
149 static const char *pin_from_file(gchar
*line
);
150 static void snap_to_grid(int *x
, int *y
);
151 static void offset_from_centre(PinIcon
*pi
, int *x
, int *y
);
152 static gboolean
drag_motion(GtkWidget
*widget
,
153 GdkDragContext
*context
,
158 static void drag_set_pinicon_dest(PinIcon
*pi
);
159 static void drag_leave(GtkWidget
*widget
,
160 GdkDragContext
*context
,
163 static gboolean
bg_drag_motion(GtkWidget
*widget
,
164 GdkDragContext
*context
,
169 static gboolean
bg_drag_leave(GtkWidget
*widget
,
170 GdkDragContext
*context
,
173 static gboolean
bg_expose(GtkWidget
*window
,
174 GdkEventExpose
*event
, gpointer data
);
175 static void drag_end(GtkWidget
*widget
,
176 GdkDragContext
*context
,
178 static void reshape_all(void);
179 static void pinboard_check_options(void);
180 static void pinboard_load_from_xml(xmlDocPtr doc
);
181 static void pinboard_clear(void);
182 static void pinboard_save(void);
183 static PinIcon
*pin_icon_new(const char *pathname
, const char *name
);
184 static void pin_icon_destroyed(PinIcon
*pi
);
185 static void pin_icon_set_tip(PinIcon
*pi
);
186 static void pinboard_show_menu(GdkEventButton
*event
, PinIcon
*pi
);
187 static void create_pinboard_window(Pinboard
*pinboard
);
188 static void reload_backdrop(Pinboard
*pinboard
,
189 const gchar
*backdrop
,
190 BackdropStyle backdrop_style
);
191 static void set_backdrop(const gchar
*path
, BackdropStyle style
);
192 void pinboard_reshape_icon(Icon
*icon
);
193 static gint
draw_wink(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
);
194 static void abandon_backdrop_app(Pinboard
*pinboard
);
197 /****************************************************************
198 * EXTERNAL INTERFACE *
199 ****************************************************************/
201 void pinboard_init(void)
203 option_add_string(&o_pinboard_fg_colour
, "pinboard_fg_colour", "#fff");
204 option_add_string(&o_pinboard_bg_colour
, "pinboard_bg_colour", "#888");
206 option_add_int(&o_pinboard_clamp_icons
, "pinboard_clamp_icons", 1);
207 option_add_int(&o_pinboard_grid_step
, "pinboard_grid_step",
209 option_add_int(&o_pinboard_tasklist
, "pinboard_tasklist", TRUE
);
211 option_add_notify(pinboard_check_options
);
213 gdk_color_parse(o_pinboard_fg_colour
.value
, &pin_text_fg_col
);
214 gdk_color_parse(o_pinboard_bg_colour
.value
, &pin_text_bg_col
);
217 /* Load 'pb_<pinboard>' config file from Choices (if it exists)
218 * and make it the current pinboard.
219 * Any existing pinned items are removed. You must call this
220 * at least once before using the pinboard. NULL disables the
223 void pinboard_activate(const gchar
*name
)
225 Pinboard
*old_board
= current_pinboard
;
226 guchar
*path
, *slash
;
228 /* Treat an empty name the same as NULL */
237 if (number_of_windows
< 1 && gtk_main_level() > 0)
244 slash
= strchr(name
, '/');
247 if (access(name
, F_OK
))
248 path
= NULL
; /* File does not (yet) exist */
250 path
= g_strdup(name
);
256 leaf
= g_strconcat("pb_", name
, NULL
);
257 path
= choices_find_path_load(leaf
, PROJECT
);
261 current_pinboard
= g_new(Pinboard
, 1);
262 current_pinboard
->name
= g_strdup(name
);
263 current_pinboard
->icons
= NULL
;
264 current_pinboard
->window
= NULL
;
265 current_pinboard
->backdrop
= NULL
;
266 current_pinboard
->backdrop_style
= BACKDROP_NONE
;
267 current_pinboard
->to_backdrop_app
= -1;
268 current_pinboard
->from_backdrop_app
= -1;
269 current_pinboard
->input_tag
= -1;
270 current_pinboard
->input_buffer
= NULL
;
272 create_pinboard_window(current_pinboard
);
278 doc
= xmlParseFile(path
);
281 pinboard_load_from_xml(doc
);
283 reload_backdrop(current_pinboard
,
284 current_pinboard
->backdrop
,
285 current_pinboard
->backdrop_style
);
289 parse_file(path
, pin_from_file
);
290 info_message(_("Your old pinboard file has been "
291 "converted to the new XML format."));
297 pinboard_pin(home_dir
, "Home",
299 4 + ICON_HEIGHT
/ 2);
302 if (o_pinboard_tasklist
.int_value
)
303 tasklist_set_active(TRUE
);
306 /* Return the window of the current pinboard, or NULL.
307 * Used to make sure lowering the panels doesn't lose them...
309 GdkWindow
*pinboard_get_window(void)
311 if (current_pinboard
)
312 return current_pinboard
->window
->window
;
316 const char *pinboard_get_name(void)
318 g_return_val_if_fail(current_pinboard
!= NULL
, NULL
);
320 return current_pinboard
->name
;
323 /* Add widget to the pinboard. Caller is responsible for coping with pinboard
326 void pinboard_add_widget(GtkWidget
*widget
, int x
, int y
)
328 g_return_if_fail(current_pinboard
!= NULL
);
330 gtk_fixed_put(GTK_FIXED(current_pinboard
->fixed
), widget
, x
, y
);
333 /* Add a new icon to the background.
334 * 'path' should be an absolute pathname.
335 * 'x' and 'y' are the coordinates of the point in the middle of the text.
336 * 'name' is the name to use. If NULL then the leafname of path is used.
338 * name and path are in UTF-8 for Gtk+-2.0 only.
340 void pinboard_pin(const gchar
*path
, const gchar
*name
, int x
, int y
)
342 GtkWidget
*align
, *vbox
;
347 g_return_if_fail(path
!= NULL
);
348 g_return_if_fail(current_pinboard
!= NULL
);
350 pi
= pin_icon_new(path
, name
);
355 /* This is a bit complicated...
357 * An icon needs to be a NO_WINDOW widget so that the image can
358 * blend with the background (A ParentRelative window also works, but
359 * is slow, causes the xfree86's memory consumption to grow without
360 * bound, and doesn't even get freed when the filer quits!).
362 * However, the icon also needs to have a window, so we get events
363 * delivered correctly. The solution is to float an InputOnly window
364 * over the icon. Since GtkButton works the same way, we just use
368 /* Button takes the initial ref of Icon */
369 pi
->win
= gtk_button_new();
370 gtk_container_set_border_width(GTK_CONTAINER(pi
->win
), WINK_FRAME
);
371 g_signal_connect(pi
->win
, "expose-event", G_CALLBACK(draw_wink
), pi
);
372 gtk_button_set_relief(GTK_BUTTON(pi
->win
), GTK_RELIEF_NONE
);
374 vbox
= gtk_vbox_new(FALSE
, 0);
375 gtk_container_add(GTK_CONTAINER(pi
->win
), vbox
);
377 align
= gtk_alignment_new(0.5, 0.5, 0, 0);
378 pi
->widget
= gtk_hbox_new(FALSE
, 0); /* Placeholder */
379 gtk_container_add(GTK_CONTAINER(align
), pi
->widget
);
381 gtk_box_pack_start(GTK_BOX(vbox
), align
, FALSE
, TRUE
, 0);
382 drag_set_pinicon_dest(pi
);
383 g_signal_connect(pi
->win
, "drag_data_get",
384 G_CALLBACK(drag_data_get
), NULL
);
386 pi
->label
= gtk_label_new(icon
->item
->leafname
);
387 gtk_label_set_line_wrap(GTK_LABEL(pi
->label
), TRUE
);
388 gtk_box_pack_start(GTK_BOX(vbox
), pi
->label
, TRUE
, TRUE
, 0);
390 gtk_fixed_put(GTK_FIXED(current_pinboard
->fixed
), pi
->win
, 0, 0);
392 snap_to_grid(&x
, &y
);
395 gtk_widget_show_all(pi
->win
);
396 pinboard_reshape_icon((Icon
*) pi
);
398 gtk_widget_realize(pi
->win
);
399 events
= GTK_BUTTON(pi
->win
)->event_window
;
400 gdk_window_set_events(events
,
402 GDK_ENTER_NOTIFY_MASK
| GDK_LEAVE_NOTIFY_MASK
|
403 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
404 GDK_BUTTON1_MOTION_MASK
| GDK_ENTER_NOTIFY_MASK
|
405 GDK_BUTTON2_MOTION_MASK
| GDK_BUTTON3_MOTION_MASK
);
406 g_signal_connect(pi
->win
, "enter-notify-event",
407 G_CALLBACK(enter_notify
), pi
);
408 g_signal_connect(pi
->win
, "button-press-event",
409 G_CALLBACK(button_press_event
), pi
);
410 g_signal_connect(pi
->win
, "button-release-event",
411 G_CALLBACK(button_release_event
), pi
);
412 g_signal_connect(pi
->win
, "motion-notify-event",
413 G_CALLBACK(icon_motion_notify
), pi
);
414 g_signal_connect(pi
->win
, "expose-event",
415 G_CALLBACK(stop_expose
), pi
);
416 g_signal_connect(pi
->widget
, "expose-event",
417 G_CALLBACK(draw_icon
), pi
);
418 g_signal_connect_swapped(pi
->win
, "destroy",
419 G_CALLBACK(pin_icon_destroyed
), pi
);
421 current_pinboard
->icons
= g_list_prepend(current_pinboard
->icons
, pi
);
422 pin_icon_set_tip(pi
);
424 if (!loading_pinboard
)
428 /* Remove an icon from the pinboard */
429 /* XXX: use destroy */
430 void pinboard_unpin(PinIcon
*pi
)
432 g_return_if_fail(pi
!= NULL
);
434 gtk_widget_destroy(pi
->win
);
438 /* Put a border around the icon, briefly.
439 * If icon is NULL then cancel any existing wink.
440 * The icon will automatically unhighlight unless timeout is FALSE,
441 * in which case you must call this function again (with NULL or another
442 * icon) to remove the highlight.
444 static void pinboard_wink_item(PinIcon
*pi
, gboolean timeout
)
446 PinIcon
*old
= current_wink_icon
;
451 current_wink_icon
= pi
;
455 gtk_widget_queue_draw(old
->win
);
456 gdk_window_process_updates(old
->widget
->window
, TRUE
);
458 if (wink_timeout
!= -1)
459 gtk_timeout_remove(wink_timeout
);
464 gtk_widget_queue_draw(pi
->win
);
465 gdk_window_process_updates(pi
->widget
->window
, TRUE
);
468 wink_timeout
= gtk_timeout_add(300, end_wink
, NULL
);
474 /* Icon's size, shape or appearance has changed - update the display */
475 void pinboard_reshape_icon(Icon
*icon
)
477 PinIcon
*pi
= (PinIcon
*) icon
;
478 int x
= pi
->x
, y
= pi
->y
;
480 set_size_and_style(pi
);
481 offset_from_centre(pi
, &x
, &y
);
483 if (pi
->win
->allocation
.x
!= x
|| pi
->win
->allocation
.y
!= y
)
485 fixed_move_fast(GTK_FIXED(current_pinboard
->fixed
),
490 /* 'app' is saved as the new application to set the backdrop. It will then be
491 * run, and should communicate with the filer as described in the manual.
493 void pinboard_set_backdrop_app(const gchar
*app
)
499 item
= diritem_new("");
500 diritem_restat(app
, item
, NULL
);
501 ai
= appinfo_get(app
, item
);
504 can_set
= ai
&& xml_get_section(ai
, ROX_NS
, "CanSetBackdrop") != NULL
;
509 set_backdrop(app
, BACKDROP_PROGRAM
);
511 delayed_error(_("This program does not know how to "
512 "manage ROX-Filer's backdrop image."));
515 /* Use this icon / program as the backdrop for the current pinboard.
516 * NULL removes the backdrop. If type is BACKDROP_NONE then will try
517 * to work it out ourself.
518 * If no pinboard is in use, activates 'Default'.
520 void pinboard_set_backdrop(DirItem
*item
, const gchar
*path
)
522 if (path
== NULL
|| item
== NULL
)
524 /* Remove backdrop */
526 if (current_pinboard
&& current_pinboard
->backdrop
)
527 set_backdrop(NULL
, BACKDROP_NONE
);
529 delayed_error(_("No backdrop image is currently "
530 "set. Use the 'Use for Backdrop' menu "
531 "item to set an image (or program) for "
536 if (item
->flags
& ITEM_FLAG_APPDIR
)
538 /* Use this program to set the backdrop */
539 pinboard_set_backdrop_app(path
);
541 else if (item
->base_type
== TYPE_FILE
)
545 /* Use this file as the backdrop */
547 i
= get_choice(_("Set backdrop"),
548 _("How should this image be displayed?"),
550 _("Centred"), _("_Scaled"), _("Tiled"));
554 i
== 1 ? BACKDROP_CENTRE
:
555 i
== 2 ? BACKDROP_SCALE
:
559 delayed_error(_("Only files and certain applications can be "
560 "used to set the background image."));
563 /****************************************************************
564 * INTERNAL FUNCTIONS *
565 ****************************************************************/
567 static void pinboard_check_options(void)
571 gdk_color_parse(o_pinboard_fg_colour
.value
, &n_fg
);
572 gdk_color_parse(o_pinboard_bg_colour
.value
, &n_bg
);
574 tasklist_set_active(o_pinboard_tasklist
.int_value
&& current_pinboard
);
576 if (gdk_color_equal(&n_fg
, &pin_text_fg_col
) == 0 ||
577 gdk_color_equal(&n_bg
, &pin_text_bg_col
) == 0)
579 memcpy(&pin_text_fg_col
, &n_fg
, sizeof(GdkColor
));
580 memcpy(&pin_text_bg_col
, &n_bg
, sizeof(GdkColor
));
584 g_object_unref(G_OBJECT(pinicon_style
));
585 pinicon_style
= NULL
;
588 if (current_pinboard
)
591 tasklist_style_changed();
595 static gint
end_wink(gpointer data
)
597 pinboard_wink_item(NULL
, FALSE
);
601 /* Updates the width, height, name_width and label fields, and resizes the
602 * window. Also sets the style to pinicon_style, generating it if needed.
604 static void set_size_and_style(PinIcon
*pi
)
606 Icon
*icon
= (Icon
*) pi
;
607 MaskedPixmap
*image
= icon
->item
->image
;
608 int iwidth
= image
->width
;
609 int iheight
= image
->height
;
611 gtk_widget_modify_fg(pi
->label
, GTK_STATE_PRELIGHT
, &pin_text_fg_col
);
612 gtk_widget_modify_bg(pi
->label
, GTK_STATE_PRELIGHT
, &pin_text_bg_col
);
613 gtk_widget_modify_fg(pi
->label
, GTK_STATE_NORMAL
, &pin_text_fg_col
);
614 gtk_widget_modify_bg(pi
->label
, GTK_STATE_NORMAL
, &pin_text_bg_col
);
616 gtk_label_set_text(GTK_LABEL(pi
->label
), icon
->item
->leafname
);
618 gtk_widget_set_size_request(pi
->widget
, iwidth
, iheight
);
621 /* Don't draw the normal button effect */
622 static gint
stop_expose(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
)
624 static GtkWidgetClass
*parent_class
= NULL
;
628 gpointer c
= ((GTypeInstance
*) widget
)->g_class
;
629 parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent(c
);
632 (parent_class
->expose_event
)(widget
, event
);
636 static gint
draw_icon(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
)
638 Icon
*icon
= (Icon
*) pi
;
639 DirItem
*item
= icon
->item
;
640 MaskedPixmap
*image
= item
->image
;
641 int iwidth
= image
->width
;
642 int iheight
= image
->height
;
644 GtkStateType state
= icon
->selected
? GTK_STATE_SELECTED
648 x
= widget
->allocation
.x
;
649 y
= widget
->allocation
.y
;
651 gdk_pixbuf_render_to_drawable_alpha(
652 icon
->selected
? image
->pixbuf_lit
: image
->pixbuf
,
657 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
658 GDK_RGB_DITHER_NORMAL
, 0, 0);
660 if (item
->flags
& ITEM_FLAG_SYMLINK
)
662 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
667 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
668 GDK_RGB_DITHER_NORMAL
, 0, 0);
670 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
672 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
676 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
681 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
682 GDK_RGB_DITHER_NORMAL
, 0, 0);
687 gtk_paint_flat_box(pi
->label
->style
, pi
->label
->window
,
690 NULL
, pi
->label
, "text",
691 pi
->label
->allocation
.x
,
692 pi
->label
->allocation
.y
,
693 pi
->label
->allocation
.width
,
694 pi
->label
->allocation
.height
);
700 static gint
draw_wink(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
)
702 gint x
, y
, width
, height
;
704 if (current_wink_icon
!= pi
)
707 x
= widget
->allocation
.x
;
708 y
= widget
->allocation
.y
;
709 width
= widget
->allocation
.width
;
710 height
= widget
->allocation
.height
;
712 gdk_draw_rectangle(widget
->window
,
713 pi
->widget
->style
->white_gc
,
715 x
, y
, width
- 1, height
- 1);
716 gdk_draw_rectangle(widget
->window
,
717 pi
->widget
->style
->black_gc
,
719 x
+ 1, y
+ 1, width
- 3, height
- 3);
724 static gboolean
enter_notify(GtkWidget
*widget
,
725 GdkEventCrossing
*event
,
728 icon_may_update((Icon
*) pi
);
733 static void perform_action(PinIcon
*pi
, GdkEventButton
*event
)
736 Icon
*icon
= (Icon
*) pi
;
738 action
= bind_lookup_bev(pi
? BIND_PINBOARD_ICON
: BIND_PINBOARD
,
741 /* Actions that can happen with or without an icon */
744 case ACT_CLEAR_SELECTION
:
745 icon_select_only(NULL
);
749 pinboard_show_menu(event
, pi
);
757 g_return_if_fail(pi
!= NULL
);
763 pinboard_wink_item(pi
, TRUE
);
764 if (event
->type
== GDK_2BUTTON_PRESS
)
765 icon_set_selected(icon
, FALSE
);
766 run_diritem(icon
->path
, icon
->item
, NULL
, NULL
, FALSE
);
770 pinboard_wink_item(pi
, TRUE
);
771 if (event
->type
== GDK_2BUTTON_PRESS
)
772 icon_set_selected(icon
, FALSE
);
773 run_diritem(icon
->path
, icon
->item
, NULL
, NULL
, TRUE
);
775 case ACT_PRIME_AND_SELECT
:
777 icon_select_only(icon
);
778 dnd_motion_start(MOTION_READY_FOR_DND
);
780 case ACT_PRIME_AND_TOGGLE
:
781 icon_set_selected(icon
, !icon
->selected
);
782 dnd_motion_start(MOTION_READY_FOR_DND
);
784 case ACT_PRIME_FOR_DND
:
785 dnd_motion_start(MOTION_READY_FOR_DND
);
787 case ACT_TOGGLE_SELECTED
:
788 icon_set_selected(icon
, !icon
->selected
);
790 case ACT_SELECT_EXCL
:
791 icon_select_only(icon
);
794 g_warning("Unsupported action : %d\n", action
);
799 static void forward_to_root(GdkEventButton
*event
)
803 if (event
->type
== GDK_BUTTON_PRESS
)
805 xev
.type
= ButtonPress
;
806 XUngrabPointer(gdk_display
, event
->time
);
809 xev
.type
= ButtonRelease
;
811 xev
.window
= gdk_x11_get_default_root_xwindow();
812 xev
.root
= xev
.window
;
813 xev
.subwindow
= None
;
814 xev
.time
= event
->time
;
817 xev
.x_root
= event
->x_root
;
818 xev
.y_root
= event
->y_root
;
819 xev
.state
= event
->state
;
820 xev
.button
= event
->button
;
821 xev
.same_screen
= True
;
823 XSendEvent(gdk_display
, xev
.window
, False
,
824 ButtonPressMask
| ButtonReleaseMask
, (XEvent
*) &xev
);
827 /* pi is NULL if this is a root event */
828 static gboolean
button_release_event(GtkWidget
*widget
,
829 GdkEventButton
*event
,
832 if (event
->button
== 2)
833 forward_to_root(event
);
834 else if (dnd_motion_release(event
))
837 perform_action(pi
, event
);
842 /* pi is NULL if this is a root event */
843 static gboolean
button_press_event(GtkWidget
*widget
,
844 GdkEventButton
*event
,
847 /* Just in case we've jumped in front of everything... */
848 gdk_window_lower(current_pinboard
->window
->window
);
850 if (event
->button
== 2)
851 forward_to_root(event
);
852 else if (dnd_motion_press(widget
, event
))
853 perform_action(pi
, event
);
858 static void start_drag(PinIcon
*pi
, GdkEventMotion
*event
)
860 GtkWidget
*widget
= pi
->win
;
861 Icon
*icon
= (Icon
*) pi
;
865 tmp_icon_selected
= TRUE
;
866 icon_select_only(icon
);
869 g_return_if_fail(icon_selection
!= NULL
);
871 pinboard_drag_in_progress
= icon
;
873 if (icon_selection
->next
== NULL
)
874 drag_one_item(widget
, event
, icon
->path
, icon
->item
, NULL
);
879 uri_list
= icon_create_uri_list();
880 drag_selection(widget
, event
, uri_list
);
885 /* An icon is being dragged around... */
886 static gint
icon_motion_notify(GtkWidget
*widget
,
887 GdkEventMotion
*event
,
890 if (motion_state
== MOTION_READY_FOR_DND
)
892 if (dnd_motion_moved(event
))
893 start_drag(pi
, event
);
900 static void backdrop_from_xml(xmlNode
*node
)
904 g_free(current_pinboard
->backdrop
);
905 current_pinboard
->backdrop
= xmlNodeGetContent(node
);
907 style
= xmlGetProp(node
, "style");
911 current_pinboard
->backdrop_style
=
912 g_strcasecmp(style
, "Tiled") == 0 ? BACKDROP_TILE
:
913 g_strcasecmp(style
, "Scaled") == 0 ? BACKDROP_SCALE
:
914 g_strcasecmp(style
, "Centred") == 0 ? BACKDROP_CENTRE
:
915 g_strcasecmp(style
, "Program") == 0 ? BACKDROP_PROGRAM
:
920 current_pinboard
->backdrop_style
= BACKDROP_TILE
;
923 /* Create one pinboard icon for each icon in the doc */
924 static void pinboard_load_from_xml(xmlDocPtr doc
)
926 xmlNodePtr node
, root
;
927 char *tmp
, *label
, *path
;
930 root
= xmlDocGetRootElement(doc
);
932 for (node
= root
->xmlChildrenNode
; node
; node
= node
->next
)
934 if (node
->type
!= XML_ELEMENT_NODE
)
936 if (strcmp(node
->name
, "backdrop") == 0)
938 backdrop_from_xml(node
);
941 if (strcmp(node
->name
, "icon") != 0)
944 tmp
= xmlGetProp(node
, "x");
950 tmp
= xmlGetProp(node
, "y");
956 label
= xmlGetProp(node
, "label");
958 label
= g_strdup("<missing label>");
959 path
= xmlNodeGetContent(node
);
961 path
= g_strdup("<missing path>");
963 pinboard_pin(path
, label
, x
, y
);
970 /* Called for each line in the pinboard file while loading a new board.
971 * Only used for old-format files when converting to XML.
973 static const char *pin_from_file(gchar
*line
)
982 end
= strchr(line
+ 1, '>');
984 return _("Missing '>' in icon label");
986 leaf
= g_strndup(line
+ 1, end
- line
- 1);
990 while (isspace(*line
))
993 return _("Missing ',' after icon label");
997 if (sscanf(line
, " %d , %d , %n", &x
, &y
, &n
) < 2)
998 return NULL
; /* Ignore format errors */
1000 pinboard_pin(line
+ n
, leaf
, x
, y
);
1007 /* Write the current state of the pinboard to the current pinboard file */
1008 static void pinboard_save(void)
1010 guchar
*save
= NULL
;
1011 guchar
*save_new
= NULL
;
1013 xmlDocPtr doc
= NULL
;
1016 g_return_if_fail(current_pinboard
!= NULL
);
1018 if (strchr(current_pinboard
->name
, '/'))
1019 save
= g_strdup(current_pinboard
->name
);
1024 leaf
= g_strconcat("pb_", current_pinboard
->name
, NULL
);
1025 save
= choices_find_path_save(leaf
, PROJECT
, TRUE
);
1032 doc
= xmlNewDoc("1.0");
1033 xmlDocSetRootElement(doc
, xmlNewDocNode(doc
, NULL
, "pinboard", NULL
));
1035 root
= xmlDocGetRootElement(doc
);
1037 if (current_pinboard
->backdrop
)
1039 BackdropStyle style
= current_pinboard
->backdrop_style
;
1042 tree
= xmlNewTextChild(root
, NULL
, "backdrop",
1043 current_pinboard
->backdrop
);
1044 xmlSetProp(tree
, "style",
1045 style
== BACKDROP_TILE
? "Tiled" :
1046 style
== BACKDROP_CENTRE
? "Centred" :
1047 style
== BACKDROP_SCALE
? "Scaled" :
1051 for (next
= current_pinboard
->icons
; next
; next
= next
->next
)
1054 PinIcon
*pi
= (PinIcon
*) next
->data
;
1055 Icon
*icon
= (Icon
*) pi
;
1058 tree
= xmlNewTextChild(root
, NULL
, "icon", icon
->src_path
);
1060 tmp
= g_strdup_printf("%d", pi
->x
);
1061 xmlSetProp(tree
, "x", tmp
);
1064 tmp
= g_strdup_printf("%d", pi
->y
);
1065 xmlSetProp(tree
, "y", tmp
);
1068 xmlSetProp(tree
, "label", icon
->item
->leafname
);
1071 save_new
= g_strconcat(save
, ".new", NULL
);
1072 if (save_xml_file(doc
, save_new
) || rename(save_new
, save
))
1073 delayed_error(_("Error saving pinboard %s: %s"),
1074 save
, g_strerror(errno
));
1082 static void snap_to_grid(int *x
, int *y
)
1084 int step
= o_pinboard_grid_step
.int_value
;
1086 *x
= ((*x
+ step
/ 2) / step
) * step
;
1087 *y
= ((*y
+ step
/ 2) / step
) * step
;
1090 /* Convert (x,y) from a centre point to a window position */
1091 static void offset_from_centre(PinIcon
*pi
, int *x
, int *y
)
1093 gboolean clamp
= o_pinboard_clamp_icons
.int_value
;
1096 gtk_widget_size_request(pi
->win
, &req
);
1098 *x
-= req
.width
>> 1;
1099 *y
-= req
.height
>> 1;
1100 *x
= CLAMP(*x
, 0, screen_width
- (clamp
? req
.width
: 0));
1101 *y
= CLAMP(*y
, 0, screen_height
- (clamp
? req
.height
: 0));
1104 /* Same as drag_set_dest(), but for pinboard icons */
1105 static void drag_set_pinicon_dest(PinIcon
*pi
)
1107 GtkObject
*obj
= GTK_OBJECT(pi
->win
);
1109 make_drop_target(pi
->win
, 0);
1111 g_signal_connect(obj
, "drag_motion", G_CALLBACK(drag_motion
), pi
);
1112 g_signal_connect(obj
, "drag_leave", G_CALLBACK(drag_leave
), pi
);
1113 g_signal_connect(obj
, "drag_end", G_CALLBACK(drag_end
), pi
);
1116 /* Called during the drag when the mouse is in a widget registered
1117 * as a drop target. Returns TRUE if we can accept the drop.
1119 static gboolean
drag_motion(GtkWidget
*widget
,
1120 GdkDragContext
*context
,
1126 GdkDragAction action
= context
->suggested_action
;
1128 Icon
*icon
= (Icon
*) pi
;
1129 DirItem
*item
= icon
->item
;
1131 if (gtk_drag_get_source_widget(context
) == widget
)
1132 goto out
; /* Can't drag something to itself! */
1135 goto out
; /* Can't drag a selection to itself */
1137 type
= dnd_motion_item(context
, &item
);
1142 /* We actually must pretend to accept the drop, even if the
1143 * directory isn't writeable, so that the spring-opening
1147 /* Don't allow drops to non-writeable directories */
1148 if (o_dnd_spring_open
.int_value
== FALSE
&&
1149 type
== drop_dest_dir
&&
1150 access(icon
->path
, W_OK
) != 0)
1155 g_dataset_set_data(context
, "drop_dest_type", type
);
1158 gdk_drag_status(context
, action
, time
);
1159 g_dataset_set_data_full(context
, "drop_dest_path",
1160 g_strdup(icon
->path
), g_free
);
1161 if (type
== drop_dest_dir
)
1162 dnd_spring_load(context
, NULL
);
1164 pinboard_wink_item(pi
, FALSE
);
1167 gdk_drag_status(context
, 0, time
);
1169 /* Always return TRUE to stop the pinboard getting the events */
1173 static gboolean pinboard_shadow
= FALSE
;
1174 static gint shadow_x
, shadow_y
;
1175 #define SHADOW_SIZE (ICON_WIDTH)
1177 static gboolean
bg_expose(GtkWidget
*widget
,
1178 GdkEventExpose
*event
, gpointer data
)
1180 if (!pinboard_shadow
)
1183 gdk_draw_rectangle(widget
->window
,
1184 widget
->style
->white_gc
, FALSE
,
1186 SHADOW_SIZE
, SHADOW_SIZE
);
1187 gdk_draw_rectangle(widget
->window
,
1188 widget
->style
->black_gc
, FALSE
,
1189 shadow_x
+ 1, shadow_y
+ 1,
1190 SHADOW_SIZE
- 2, SHADOW_SIZE
- 2);
1195 /* Draw a 'shadow' under an icon being dragged, showing where
1198 static void pinboard_set_shadow(gboolean on
)
1202 if (pinboard_shadow
)
1206 area
.width
= SHADOW_SIZE
+ 1;
1207 area
.height
= SHADOW_SIZE
+ 1;
1209 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1215 int old_x
= shadow_x
, old_y
= shadow_y
;
1217 gdk_window_get_pointer(current_pinboard
->fixed
->window
,
1218 &shadow_x
, &shadow_y
, NULL
);
1219 snap_to_grid(&shadow_x
, &shadow_y
);
1220 shadow_x
-= SHADOW_SIZE
/ 2;
1221 shadow_y
-= SHADOW_SIZE
/ 2;
1224 if (pinboard_shadow
&& shadow_x
== old_x
&& shadow_y
== old_y
)
1229 area
.width
= SHADOW_SIZE
+ 1;
1230 area
.height
= SHADOW_SIZE
+ 1;
1232 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1236 pinboard_shadow
= on
;
1239 /* Called when dragging some pinboard icons finishes */
1240 void pinboard_move_icons(void)
1242 int x
= shadow_x
, y
= shadow_y
;
1243 PinIcon
*pi
= (PinIcon
*) pinboard_drag_in_progress
;
1246 g_return_if_fail(pi
!= NULL
);
1248 x
+= SHADOW_SIZE
/ 2;
1249 y
+= SHADOW_SIZE
/ 2;
1250 snap_to_grid(&x
, &y
);
1252 if (pi
->x
== x
&& pi
->y
== y
)
1257 gdk_drawable_get_size(pi
->win
->window
, &width
, &height
);
1258 offset_from_centre(pi
, &x
, &y
);
1260 fixed_move_fast(GTK_FIXED(current_pinboard
->fixed
), pi
->win
, x
, y
);
1265 static void drag_leave(GtkWidget
*widget
,
1266 GdkDragContext
*context
,
1270 pinboard_wink_item(NULL
, FALSE
);
1274 static gboolean
bg_drag_leave(GtkWidget
*widget
,
1275 GdkDragContext
*context
,
1279 pinboard_set_shadow(FALSE
);
1283 static gboolean
bg_drag_motion(GtkWidget
*widget
,
1284 GdkDragContext
*context
,
1290 /* Dragging from the pinboard to the pinboard is not allowed */
1292 if (!provides(context
, text_uri_list
))
1295 pinboard_set_shadow(TRUE
);
1297 gdk_drag_status(context
,
1298 context
->suggested_action
== GDK_ACTION_ASK
1299 ? GDK_ACTION_LINK
: context
->suggested_action
,
1304 static void drag_end(GtkWidget
*widget
,
1305 GdkDragContext
*context
,
1308 pinboard_drag_in_progress
= NULL
;
1309 if (tmp_icon_selected
)
1311 icon_select_only(NULL
);
1312 tmp_icon_selected
= FALSE
;
1316 /* Something which affects all the icons has changed - reshape
1317 * and redraw all of them.
1319 static void reshape_all(void)
1323 g_return_if_fail(current_pinboard
!= NULL
);
1325 for (next
= current_pinboard
->icons
; next
; next
= next
->next
)
1327 Icon
*icon
= (Icon
*) next
->data
;
1328 pinboard_reshape_icon(icon
);
1332 /* Turns off the pinboard. Does not call gtk_main_quit. */
1333 static void pinboard_clear(void)
1337 g_return_if_fail(current_pinboard
!= NULL
);
1339 tasklist_set_active(FALSE
);
1341 next
= current_pinboard
->icons
;
1344 PinIcon
*pi
= (PinIcon
*) next
->data
;
1348 gtk_widget_destroy(pi
->win
);
1351 gtk_widget_destroy(current_pinboard
->window
);
1353 abandon_backdrop_app(current_pinboard
);
1355 g_free(current_pinboard
->name
);
1356 g_free(current_pinboard
);
1357 current_pinboard
= NULL
;
1359 number_of_windows
--;
1362 static gpointer parent_class
;
1364 static void pin_icon_destroy(Icon
*icon
)
1366 PinIcon
*pi
= (PinIcon
*) icon
;
1368 g_return_if_fail(pi
->win
!= NULL
);
1370 gtk_widget_destroy(pi
->win
);
1373 static void pinboard_remove_items(void)
1375 g_return_if_fail(icon_selection
!= NULL
);
1377 while (icon_selection
)
1378 icon_destroy((Icon
*) icon_selection
->data
);
1383 static void pin_icon_update(Icon
*icon
)
1385 pinboard_reshape_icon(icon
);
1389 static gboolean
pin_icon_same_group(Icon
*icon
, Icon
*other
)
1391 return IS_PIN_ICON(other
);
1394 static void pin_icon_class_init(gpointer gclass
, gpointer data
)
1396 IconClass
*icon
= (IconClass
*) gclass
;
1398 parent_class
= g_type_class_peek_parent(gclass
);
1400 icon
->destroy
= pin_icon_destroy
;
1401 icon
->redraw
= pinboard_reshape_icon
;
1402 icon
->update
= pin_icon_update
;
1403 icon
->remove_items
= pinboard_remove_items
;
1404 icon
->same_group
= pin_icon_same_group
;
1407 static void pin_icon_init(GTypeInstance
*object
, gpointer gclass
)
1411 static GType
pin_icon_get_type(void)
1413 static GType type
= 0;
1417 static const GTypeInfo info
=
1419 sizeof (PinIconClass
),
1420 NULL
, /* base_init */
1421 NULL
, /* base_finalise */
1422 pin_icon_class_init
,
1423 NULL
, /* class_finalise */
1424 NULL
, /* class_data */
1426 0, /* n_preallocs */
1430 type
= g_type_register_static(icon_get_type(),
1431 "PinIcon", &info
, 0);
1437 static PinIcon
*pin_icon_new(const char *pathname
, const char *name
)
1442 pi
= g_object_new(pin_icon_get_type(), NULL
);
1445 icon_set_path(icon
, pathname
, name
);
1450 /* Called when the window widget is somehow destroyed */
1451 static void pin_icon_destroyed(PinIcon
*pi
)
1453 g_return_if_fail(pi
->win
!= NULL
);
1457 pinboard_wink_item(NULL
, FALSE
);
1459 if (pinboard_drag_in_progress
== (Icon
*) pi
)
1460 pinboard_drag_in_progress
= NULL
;
1462 if (current_pinboard
)
1463 current_pinboard
->icons
=
1464 g_list_remove(current_pinboard
->icons
, pi
);
1469 /* Set the tooltip */
1470 static void pin_icon_set_tip(PinIcon
*pi
)
1474 Icon
*icon
= (Icon
*) pi
;
1476 g_return_if_fail(pi
!= NULL
);
1478 ai
= appinfo_get(icon
->path
, icon
->item
);
1480 if (ai
&& ((node
= xml_get_section(ai
, NULL
, "Summary"))))
1483 str
= xmlNodeListGetString(node
->doc
,
1484 node
->xmlChildrenNode
, 1);
1487 gtk_tooltips_set_tip(tooltips
, pi
->win
, str
, NULL
);
1492 gtk_tooltips_set_tip(tooltips
, pi
->widget
, NULL
, NULL
);
1498 static void pinboard_show_menu(GdkEventButton
*event
, PinIcon
*pi
)
1502 pos
[0] = event
->x_root
;
1503 pos
[1] = event
->y_root
;
1506 icon_prepare_menu((Icon
*) pi
);
1507 gtk_widget_show(icon_menu_remove_backdrop
);
1508 gtk_widget_set_sensitive(GTK_BIN(icon_menu_remove_backdrop
)->child
,
1509 current_pinboard
->backdrop
!= NULL
);
1511 gtk_menu_popup(GTK_MENU(icon_menu
), NULL
, NULL
,
1513 (gpointer
) pos
, event
->button
, event
->time
);
1516 static void create_pinboard_window(Pinboard
*pinboard
)
1520 g_return_if_fail(pinboard
->window
== NULL
);
1522 win
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1523 gtk_widget_set_app_paintable(win
, TRUE
);
1524 gtk_widget_set_name(win
, "rox-pinboard");
1525 pinboard
->window
= win
;
1526 pinboard
->fixed
= gtk_fixed_new();
1527 gtk_container_add(GTK_CONTAINER(win
), pinboard
->fixed
);
1529 gtk_window_set_wmclass(GTK_WINDOW(win
), "ROX-Pinboard", PROJECT
);
1531 gtk_widget_set_size_request(win
, screen_width
, screen_height
);
1532 gtk_widget_realize(win
);
1533 gtk_window_move(GTK_WINDOW(win
), 0, 0);
1534 make_panel_window(win
);
1536 /* TODO: Use gdk function when it supports this type */
1538 GdkAtom desktop_type
;
1540 desktop_type
= gdk_atom_intern("_NET_WM_WINDOW_TYPE_DESKTOP",
1542 gdk_property_change(win
->window
,
1543 gdk_atom_intern("_NET_WM_WINDOW_TYPE", FALSE
),
1544 gdk_atom_intern("ATOM", FALSE
), 32,
1545 GDK_PROP_MODE_REPLACE
, (guchar
*) &desktop_type
, 1);
1548 gtk_widget_add_events(win
,
1549 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
1551 g_signal_connect(win
, "button-press-event",
1552 G_CALLBACK(button_press_event
), NULL
);
1553 g_signal_connect(win
, "button-release-event",
1554 G_CALLBACK(button_release_event
), NULL
);
1555 g_signal_connect(pinboard
->fixed
, "expose_event",
1556 G_CALLBACK(bg_expose
), NULL
);
1558 /* Drag and drop handlers */
1559 drag_set_pinboard_dest(win
);
1560 g_signal_connect(win
, "drag_motion", G_CALLBACK(bg_drag_motion
), NULL
);
1561 g_signal_connect(win
, "drag_leave", G_CALLBACK(bg_drag_leave
), NULL
);
1563 gtk_widget_show_all(win
);
1564 gdk_window_lower(win
->window
);
1567 /* Load image 'path' and scale according to 'style' */
1568 static GdkPixmap
*load_backdrop(const gchar
*path
, BackdropStyle style
)
1572 GError
*error
= NULL
;
1574 pixbuf
= gdk_pixbuf_new_from_file(path
, &error
);
1577 delayed_error(_("Error loading backdrop image:\n%s\n"
1578 "Backdrop removed."),
1580 g_error_free(error
);
1581 set_backdrop(NULL
, BACKDROP_NONE
);
1585 if (style
== BACKDROP_SCALE
)
1587 GdkPixbuf
*old
= pixbuf
;
1589 pixbuf
= gdk_pixbuf_scale_simple(old
,
1590 screen_width
, screen_height
,
1593 g_object_unref(old
);
1595 else if (style
== BACKDROP_CENTRE
)
1597 GdkPixbuf
*old
= pixbuf
;
1598 int x
, y
, width
, height
;
1600 width
= gdk_pixbuf_get_width(pixbuf
);
1601 height
= gdk_pixbuf_get_height(pixbuf
);
1603 pixbuf
= gdk_pixbuf_new(
1604 gdk_pixbuf_get_colorspace(pixbuf
), 0,
1605 8, screen_width
, screen_height
);
1606 gdk_pixbuf_fill(pixbuf
, 0);
1608 x
= (screen_width
- width
) / 2;
1609 y
= (screen_height
- height
) / 2;
1613 gdk_pixbuf_composite(old
, pixbuf
,
1615 MIN(screen_width
, width
),
1616 MIN(screen_height
, height
),
1618 GDK_INTERP_NEAREST
, 255);
1619 g_object_unref(old
);
1622 gdk_pixbuf_render_pixmap_and_mask(pixbuf
,
1624 g_object_unref(pixbuf
);
1629 static void abandon_backdrop_app(Pinboard
*pinboard
)
1631 g_return_if_fail(pinboard
!= NULL
);
1633 if (pinboard
->to_backdrop_app
!= -1)
1635 close(pinboard
->to_backdrop_app
);
1636 close(pinboard
->from_backdrop_app
);
1637 gtk_input_remove(pinboard
->input_tag
);
1638 g_string_free(pinboard
->input_buffer
, TRUE
);
1639 pinboard
->to_backdrop_app
= -1;
1640 pinboard
->from_backdrop_app
= -1;
1641 pinboard
->input_tag
= -1;
1642 pinboard
->input_buffer
= NULL
;
1645 g_return_if_fail(pinboard
->to_backdrop_app
== -1);
1646 g_return_if_fail(pinboard
->from_backdrop_app
== -1);
1647 g_return_if_fail(pinboard
->input_tag
== -1);
1648 g_return_if_fail(pinboard
->input_buffer
== NULL
);
1651 /* A single line has been read from the child.
1652 * Processes the command, and replies 'ok' (or abandons the child on error).
1654 static void command_from_backdrop_app(Pinboard
*pinboard
, const gchar
*command
)
1656 BackdropStyle style
;
1657 const char *ok
= "ok\n";
1659 if (strncmp(command
, "tile ", 5) == 0)
1661 style
= BACKDROP_TILE
;
1664 else if (strncmp(command
, "scale ", 6) == 0)
1666 style
= BACKDROP_SCALE
;
1669 else if (strncmp(command
, "centre ", 7) == 0)
1671 style
= BACKDROP_CENTRE
;
1676 g_warning("Invalid command '%s' from backdrop app\n",
1678 abandon_backdrop_app(pinboard
);
1682 reload_backdrop(pinboard
, command
, style
);
1688 sent
= write(pinboard
->to_backdrop_app
, ok
, strlen(ok
));
1691 g_warning("command_from_backdrop_app: %s\n",
1693 abandon_backdrop_app(pinboard
);
1700 static void backdrop_from_child(Pinboard
*pinboard
,
1701 int src
, GdkInputCondition cond
)
1706 got
= read(src
, buf
, sizeof(buf
));
1711 g_warning("backdrop_from_child: %s\n",
1713 abandon_backdrop_app(pinboard
);
1717 g_string_append_len(pinboard
->input_buffer
, buf
, got
);
1719 while (pinboard
->from_backdrop_app
!= -1)
1724 nl
= strchr(pinboard
->input_buffer
->str
, '\n');
1726 return; /* Haven't got a whole line yet */
1728 len
= nl
- pinboard
->input_buffer
->str
;
1729 command
= g_strndup(pinboard
->input_buffer
->str
, len
);
1730 g_string_erase(pinboard
->input_buffer
, 0, len
+ 1);
1732 command_from_backdrop_app(pinboard
, command
);
1738 static void reload_backdrop(Pinboard
*pinboard
,
1739 const gchar
*backdrop
,
1740 BackdropStyle backdrop_style
)
1744 if (backdrop
&& backdrop_style
== BACKDROP_PROGRAM
)
1746 const char *argv
[] = {NULL
, "--backdrop", NULL
};
1747 GError
*error
= NULL
;
1749 g_return_if_fail(pinboard
->to_backdrop_app
== -1);
1750 g_return_if_fail(pinboard
->from_backdrop_app
== -1);
1751 g_return_if_fail(pinboard
->input_tag
== -1);
1752 g_return_if_fail(pinboard
->input_buffer
== NULL
);
1754 argv
[0] = make_path(backdrop
, "AppRun")->str
;
1756 /* Run the program. It'll send us a SOAP message and we'll
1757 * get back here with a different style and image.
1760 if (g_spawn_async_with_pipes(NULL
, (gchar
**) argv
, NULL
,
1761 G_SPAWN_DO_NOT_REAP_CHILD
|
1762 G_SPAWN_SEARCH_PATH
,
1763 NULL
, NULL
, /* Child setup fn */
1764 NULL
, /* Child PID */
1765 &pinboard
->to_backdrop_app
,
1766 &pinboard
->from_backdrop_app
,
1767 NULL
, /* Standard error */
1770 pinboard
->input_buffer
= g_string_new(NULL
);
1771 pinboard
->input_tag
= gtk_input_add_full(
1772 pinboard
->from_backdrop_app
,
1774 (GdkInputFunction
) backdrop_from_child
,
1775 NULL
, pinboard
, NULL
);
1779 delayed_error("%s", error
? error
->message
: "(null)");
1780 g_error_free(error
);
1785 /* Note: Copying a style does not ref the pixmaps! */
1787 style
= gtk_style_copy(gtk_widget_get_style(pinboard
->window
));
1788 style
->bg_pixmap
[GTK_STATE_NORMAL
] = NULL
;
1791 style
->bg_pixmap
[GTK_STATE_NORMAL
] =
1792 load_backdrop(backdrop
, backdrop_style
);
1794 gtk_widget_set_style(pinboard
->window
, style
);
1796 g_object_unref(style
);
1798 gtk_widget_queue_draw(pinboard
->window
);
1801 /* Set and save (path, style) as the new backdrop.
1802 * If style is BACKDROP_PROGRAM, the program is run to get the backdrop.
1803 * Otherwise, the image is displayed now.
1805 static void set_backdrop(const gchar
*path
, BackdropStyle style
)
1807 g_return_if_fail((path
== NULL
&& style
== BACKDROP_NONE
) ||
1808 (path
!= NULL
&& style
!= BACKDROP_NONE
));
1810 if (!current_pinboard
)
1814 pinboard_activate("Default");
1815 delayed_error(_("No pinboard was in use... "
1816 "the 'Default' pinboard has been selected. "
1817 "Use 'rox -p=Default' to turn it on in "
1819 g_return_if_fail(current_pinboard
!= NULL
);
1822 abandon_backdrop_app(current_pinboard
);
1824 g_free(current_pinboard
->backdrop
);
1825 current_pinboard
->backdrop
= g_strdup(path
);
1826 current_pinboard
->backdrop_style
= style
;
1827 reload_backdrop(current_pinboard
,
1828 current_pinboard
->backdrop
,
1829 current_pinboard
->backdrop_style
);