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 */
33 #include <libxml/parser.h>
45 #include "gui_support.h"
55 #include "panel.h" /* For panel_mark_used() */
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 /* Used in options */
107 #define CORNER_TOP_LEFT 0
108 #define CORNER_TOP_RIGHT 1
109 #define CORNER_BOTTOM_LEFT 2
110 #define CORNER_BOTTOM_RIGHT 3
115 static PinIcon
*current_wink_icon
= NULL
;
116 static gint wink_timeout
;
118 /* Used for the text colours (only) in the icons (and tasklist windows) */
119 GdkColor pin_text_fg_col
, pin_text_bg_col
;
121 /* Style that all the icons should use. NULL => regenerate from text_fg/bg */
122 static GtkStyle
*pinicon_style
= NULL
;
124 Pinboard
*current_pinboard
= NULL
;
125 static gint loading_pinboard
= 0; /* Non-zero => loading */
127 /* The Icon that was used to start the current drag, if any */
128 Icon
*pinboard_drag_in_progress
= NULL
;
130 static Option o_pinboard_clamp_icons
, o_pinboard_grid_step
;
131 static Option o_pinboard_fg_colour
, o_pinboard_bg_colour
;
132 static Option o_pinboard_tasklist
, o_forward_button_3
;
133 static Option o_iconify_start
, o_iconify_dir
;
135 /* Static prototypes */
136 static GType
pin_icon_get_type(void);
137 static void set_size_and_style(PinIcon
*pi
);
138 static gint
draw_icon(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
);
139 static gint
end_wink(gpointer data
);
140 static gboolean
button_release_event(GtkWidget
*widget
,
141 GdkEventButton
*event
,
143 static gboolean
enter_notify(GtkWidget
*widget
,
144 GdkEventCrossing
*event
,
146 static gboolean
button_press_event(GtkWidget
*widget
,
147 GdkEventButton
*event
,
149 static gint
icon_motion_notify(GtkWidget
*widget
,
150 GdkEventMotion
*event
,
152 static const char *pin_from_file(gchar
*line
);
153 static void snap_to_grid(int *x
, int *y
);
154 static void offset_from_centre(PinIcon
*pi
, int *x
, int *y
);
155 static gboolean
drag_motion(GtkWidget
*widget
,
156 GdkDragContext
*context
,
161 static void drag_set_pinicon_dest(PinIcon
*pi
);
162 static void drag_leave(GtkWidget
*widget
,
163 GdkDragContext
*context
,
166 static gboolean
bg_drag_motion(GtkWidget
*widget
,
167 GdkDragContext
*context
,
172 static gboolean
bg_drag_leave(GtkWidget
*widget
,
173 GdkDragContext
*context
,
176 static gboolean
bg_expose(GtkWidget
*window
,
177 GdkEventExpose
*event
, gpointer data
);
178 static void drag_end(GtkWidget
*widget
,
179 GdkDragContext
*context
,
181 static void reshape_all(void);
182 static void pinboard_check_options(void);
183 static void pinboard_load_from_xml(xmlDocPtr doc
);
184 static void pinboard_clear(void);
185 static void pinboard_save(void);
186 static PinIcon
*pin_icon_new(const char *pathname
, const char *name
);
187 static void pin_icon_destroyed(PinIcon
*pi
);
188 static void pin_icon_set_tip(PinIcon
*pi
);
189 static void pinboard_show_menu(GdkEventButton
*event
, PinIcon
*pi
);
190 static void create_pinboard_window(Pinboard
*pinboard
);
191 static void reload_backdrop(Pinboard
*pinboard
,
192 const gchar
*backdrop
,
193 BackdropStyle backdrop_style
);
194 static void set_backdrop(const gchar
*path
, BackdropStyle style
);
195 static void pinboard_reshape_icon(Icon
*icon
);
196 static gint
draw_wink(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
);
197 static void abandon_backdrop_app(Pinboard
*pinboard
);
198 static void drag_backdrop_dropped(GtkWidget
*frame
,
199 GdkDragContext
*context
,
202 GtkSelectionData
*selection_data
,
206 static void backdrop_response(GtkWidget
*dialog
, gint response
, gpointer data
);
207 static void find_free_rect(Pinboard
*pinboard
, GdkRectangle
*rect
);
210 /****************************************************************
211 * EXTERNAL INTERFACE *
212 ****************************************************************/
214 void pinboard_init(void)
216 option_add_string(&o_pinboard_fg_colour
, "pinboard_fg_colour", "#fff");
217 option_add_string(&o_pinboard_bg_colour
, "pinboard_bg_colour", "#888");
219 option_add_int(&o_pinboard_clamp_icons
, "pinboard_clamp_icons", 1);
220 option_add_int(&o_pinboard_grid_step
, "pinboard_grid_step",
222 option_add_int(&o_pinboard_tasklist
, "pinboard_tasklist", TRUE
);
223 option_add_int(&o_forward_button_3
, "pinboard_forward_button_3", FALSE
);
225 option_add_int(&o_iconify_start
, "iconify_start", CORNER_TOP_RIGHT
);
226 option_add_int(&o_iconify_dir
, "iconify_dir", DIR_VERT
);
228 option_add_notify(pinboard_check_options
);
230 gdk_color_parse(o_pinboard_fg_colour
.value
, &pin_text_fg_col
);
231 gdk_color_parse(o_pinboard_bg_colour
.value
, &pin_text_bg_col
);
234 /* Load 'pb_<pinboard>' config file from Choices (if it exists)
235 * and make it the current pinboard.
236 * Any existing pinned items are removed. You must call this
237 * at least once before using the pinboard. NULL disables the
240 void pinboard_activate(const gchar
*name
)
242 Pinboard
*old_board
= current_pinboard
;
243 guchar
*path
, *slash
;
245 /* Treat an empty name the same as NULL */
254 if (number_of_windows
< 1 && gtk_main_level() > 0)
257 gdk_property_delete(gdk_get_default_root_window(),
258 gdk_atom_intern("_XROOTPMAP_ID", FALSE
));
264 slash
= strchr(name
, '/');
267 if (access(name
, F_OK
))
268 path
= NULL
; /* File does not (yet) exist */
270 path
= g_strdup(name
);
276 leaf
= g_strconcat("pb_", name
, NULL
);
277 path
= choices_find_path_load(leaf
, PROJECT
);
281 current_pinboard
= g_new(Pinboard
, 1);
282 current_pinboard
->name
= g_strdup(name
);
283 current_pinboard
->icons
= NULL
;
284 current_pinboard
->window
= NULL
;
285 current_pinboard
->backdrop
= NULL
;
286 current_pinboard
->backdrop_style
= BACKDROP_NONE
;
287 current_pinboard
->to_backdrop_app
= -1;
288 current_pinboard
->from_backdrop_app
= -1;
289 current_pinboard
->input_tag
= -1;
290 current_pinboard
->input_buffer
= NULL
;
292 create_pinboard_window(current_pinboard
);
298 doc
= xmlParseFile(path
);
301 pinboard_load_from_xml(doc
);
303 reload_backdrop(current_pinboard
,
304 current_pinboard
->backdrop
,
305 current_pinboard
->backdrop_style
);
309 parse_file(path
, pin_from_file
);
310 info_message(_("Your old pinboard file has been "
311 "converted to the new XML format."));
317 pinboard_pin(home_dir
, "Home",
319 4 + ICON_HEIGHT
/ 2);
322 if (o_pinboard_tasklist
.int_value
)
323 tasklist_set_active(TRUE
);
326 /* Return the window of the current pinboard, or NULL.
327 * Used to make sure lowering the panels doesn't lose them...
329 GdkWindow
*pinboard_get_window(void)
331 if (current_pinboard
)
332 return current_pinboard
->window
->window
;
336 const char *pinboard_get_name(void)
338 g_return_val_if_fail(current_pinboard
!= NULL
, NULL
);
340 return current_pinboard
->name
;
343 /* Add widget to the pinboard. Caller is responsible for coping with pinboard
346 void pinboard_add_widget(GtkWidget
*widget
)
351 g_return_if_fail(current_pinboard
!= NULL
);
353 gtk_fixed_put(GTK_FIXED(current_pinboard
->fixed
), widget
, 0, 0);
355 gtk_widget_size_request(widget
, &req
);
357 rect
.width
= req
.width
;
358 rect
.height
= req
.height
;
359 find_free_rect(current_pinboard
, &rect
);
361 gtk_fixed_move(GTK_FIXED(current_pinboard
->fixed
),
362 widget
, rect
.x
, rect
.y
);
365 /* Add a new icon to the background.
366 * 'path' should be an absolute pathname.
367 * 'x' and 'y' are the coordinates of the point in the middle of the text.
368 * 'name' is the name to use. If NULL then the leafname of path is used.
370 * name and path are in UTF-8 for Gtk+-2.0 only.
372 void pinboard_pin(const gchar
*path
, const gchar
*name
, int x
, int y
)
374 GtkWidget
*align
, *vbox
;
379 g_return_if_fail(path
!= NULL
);
380 g_return_if_fail(current_pinboard
!= NULL
);
382 pi
= pin_icon_new(path
, name
);
387 /* This is a bit complicated...
389 * An icon needs to be a NO_WINDOW widget so that the image can
390 * blend with the background (A ParentRelative window also works, but
391 * is slow, causes the xfree86's memory consumption to grow without
392 * bound, and doesn't even get freed when the filer quits!).
394 * However, the icon also needs to have a window, so we get events
395 * delivered correctly. The solution is to float an InputOnly window
396 * over the icon. Since GtkButton works the same way, we just use
400 /* Button takes the initial ref of Icon */
401 pi
->win
= gtk_button_new();
402 gtk_container_set_border_width(GTK_CONTAINER(pi
->win
), WINK_FRAME
);
403 g_signal_connect(pi
->win
, "expose-event", G_CALLBACK(draw_wink
), pi
);
404 gtk_button_set_relief(GTK_BUTTON(pi
->win
), GTK_RELIEF_NONE
);
406 vbox
= gtk_vbox_new(FALSE
, 0);
407 gtk_container_add(GTK_CONTAINER(pi
->win
), vbox
);
409 align
= gtk_alignment_new(0.5, 0.5, 0, 0);
410 pi
->widget
= gtk_hbox_new(FALSE
, 0); /* Placeholder */
411 gtk_container_add(GTK_CONTAINER(align
), pi
->widget
);
413 gtk_box_pack_start(GTK_BOX(vbox
), align
, FALSE
, TRUE
, 0);
414 drag_set_pinicon_dest(pi
);
415 g_signal_connect(pi
->win
, "drag_data_get",
416 G_CALLBACK(drag_data_get
), NULL
);
418 pi
->label
= gtk_label_new(icon
->item
->leafname
);
419 gtk_label_set_line_wrap(GTK_LABEL(pi
->label
), TRUE
);
420 gtk_box_pack_start(GTK_BOX(vbox
), pi
->label
, TRUE
, TRUE
, 0);
422 gtk_fixed_put(GTK_FIXED(current_pinboard
->fixed
), pi
->win
, 0, 0);
424 snap_to_grid(&x
, &y
);
427 gtk_widget_show_all(pi
->win
);
428 pinboard_reshape_icon((Icon
*) pi
);
430 gtk_widget_realize(pi
->win
);
431 events
= GTK_BUTTON(pi
->win
)->event_window
;
432 gdk_window_set_events(events
,
434 GDK_ENTER_NOTIFY_MASK
| GDK_LEAVE_NOTIFY_MASK
|
435 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
436 GDK_BUTTON1_MOTION_MASK
| GDK_ENTER_NOTIFY_MASK
|
437 GDK_BUTTON2_MOTION_MASK
| GDK_BUTTON3_MOTION_MASK
);
438 g_signal_connect(pi
->win
, "enter-notify-event",
439 G_CALLBACK(enter_notify
), pi
);
440 g_signal_connect(pi
->win
, "button-press-event",
441 G_CALLBACK(button_press_event
), pi
);
442 g_signal_connect(pi
->win
, "button-release-event",
443 G_CALLBACK(button_release_event
), pi
);
444 g_signal_connect(pi
->win
, "motion-notify-event",
445 G_CALLBACK(icon_motion_notify
), pi
);
446 g_signal_connect(pi
->win
, "expose-event",
447 G_CALLBACK(draw_icon
), pi
);
448 g_signal_connect_swapped(pi
->win
, "style-set",
449 G_CALLBACK(pinboard_reshape_icon
), pi
);
450 g_signal_connect_swapped(pi
->win
, "destroy",
451 G_CALLBACK(pin_icon_destroyed
), pi
);
453 current_pinboard
->icons
= g_list_prepend(current_pinboard
->icons
, pi
);
454 pin_icon_set_tip(pi
);
456 if (!loading_pinboard
)
460 /* Remove an icon from the pinboard */
461 /* XXX: use destroy */
462 void pinboard_unpin(PinIcon
*pi
)
464 g_return_if_fail(pi
!= NULL
);
466 gtk_widget_destroy(pi
->win
);
470 /* Put a border around the icon, briefly.
471 * If icon is NULL then cancel any existing wink.
472 * The icon will automatically unhighlight unless timeout is FALSE,
473 * in which case you must call this function again (with NULL or another
474 * icon) to remove the highlight.
476 static void pinboard_wink_item(PinIcon
*pi
, gboolean timeout
)
478 PinIcon
*old
= current_wink_icon
;
483 current_wink_icon
= pi
;
487 gtk_widget_queue_draw(old
->win
);
488 gdk_window_process_updates(old
->widget
->window
, TRUE
);
490 if (wink_timeout
!= -1)
491 gtk_timeout_remove(wink_timeout
);
496 gtk_widget_queue_draw(pi
->win
);
497 gdk_window_process_updates(pi
->widget
->window
, TRUE
);
500 wink_timeout
= gtk_timeout_add(300, end_wink
, NULL
);
506 /* 'app' is saved as the new application to set the backdrop. It will then be
507 * run, and should communicate with the filer as described in the manual.
509 void pinboard_set_backdrop_app(const gchar
*app
)
515 item
= diritem_new("");
516 diritem_restat(app
, item
, NULL
);
517 ai
= appinfo_get(app
, item
);
520 can_set
= ai
&& xml_get_section(ai
, ROX_NS
, "CanSetBackdrop") != NULL
;
525 set_backdrop(app
, BACKDROP_PROGRAM
);
527 delayed_error(_("You can only set the backdrop to an image "
528 "or to a program which knows how to "
529 "manage ROX-Filer's backdrop."));
532 /* Open a dialog box allowing the user to set the backdrop */
533 void pinboard_set_backdrop(void)
535 GtkWidget
*dialog
, *frame
, *label
, *radio
, *hbox
;
537 GtkTargetEntry targets
[] = {
538 {"text/uri-list", 0, TARGET_URI_LIST
},
541 dialog
= gtk_dialog_new_with_buttons(_("Set backdrop"), NULL
,
542 GTK_DIALOG_NO_SEPARATOR
,
543 GTK_STOCK_CLEAR
, GTK_RESPONSE_NO
,
544 GTK_STOCK_OK
, GTK_RESPONSE_OK
,
546 vbox
= GTK_BOX(GTK_DIALOG(dialog
)->vbox
);
548 gtk_window_set_position(GTK_WINDOW(dialog
), GTK_WIN_POS_MOUSE
);
550 label
= gtk_label_new(_("Display backdrop image:"));
551 gtk_misc_set_padding(GTK_MISC(label
), 4, 0);
552 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0);
553 gtk_box_pack_start(vbox
, label
, TRUE
, TRUE
, 4);
555 /* The Centred, Scaled, Tiled radios... */
556 hbox
= gtk_hbox_new(TRUE
, 2);
557 gtk_box_pack_start(vbox
, hbox
, TRUE
, TRUE
, 4);
559 radio
= gtk_radio_button_new_with_label(NULL
, _("Centred"));
560 g_object_set_data(G_OBJECT(dialog
), "radio_centred", radio
);
561 gtk_box_pack_start(GTK_BOX(hbox
), radio
, FALSE
, TRUE
, 0);
563 radio
= gtk_radio_button_new_with_label(
564 gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio
)),
566 g_object_set_data(G_OBJECT(dialog
), "radio_scaled", radio
);
567 gtk_box_pack_start(GTK_BOX(hbox
), radio
, FALSE
, TRUE
, 0);
569 radio
= gtk_radio_button_new_with_label(
570 gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio
)),
572 gtk_box_pack_start(GTK_BOX(hbox
), radio
, FALSE
, TRUE
, 0);
574 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio
), TRUE
);
576 /* The drop area... */
577 frame
= gtk_frame_new(NULL
);
578 gtk_box_pack_start(vbox
, frame
, TRUE
, TRUE
, 4);
579 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_IN
);
580 gtk_container_set_border_width(GTK_CONTAINER(frame
), 4);
582 gtk_drag_dest_set(frame
, GTK_DEST_DEFAULT_ALL
,
583 targets
, sizeof(targets
) / sizeof(*targets
),
585 g_signal_connect(frame
, "drag_data_received",
586 G_CALLBACK(drag_backdrop_dropped
), dialog
);
588 label
= gtk_label_new(_("Drop an image here"));
589 gtk_misc_set_padding(GTK_MISC(label
), 10, 20);
590 gtk_container_add(GTK_CONTAINER(frame
), label
);
592 g_signal_connect(dialog
, "response",
593 G_CALLBACK(backdrop_response
), NULL
);
594 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_OK
);
596 gtk_widget_show_all(dialog
);
599 /****************************************************************
600 * INTERNAL FUNCTIONS *
601 ****************************************************************/
603 static void backdrop_response(GtkWidget
*dialog
, gint response
, gpointer data
)
605 if (response
== GTK_RESPONSE_NO
)
606 set_backdrop(NULL
, BACKDROP_NONE
);
608 gtk_widget_destroy(dialog
);
611 static void drag_backdrop_dropped(GtkWidget
*frame
,
612 GdkDragContext
*context
,
615 GtkSelectionData
*selection_data
,
621 const gchar
*path
= NULL
;
624 if (!selection_data
->data
)
625 return; /* Timeout? */
627 uris
= uri_list_to_glist(selection_data
->data
);
629 if (g_list_length(uris
) == 1)
630 path
= get_local_path((guchar
*) uris
->data
);
636 _("You should drop a single (local) image file "
637 "onto the drop box - that image will be "
638 "used for the desktop background. You can also "
639 "drag certain applications onto this box."));
643 if (mc_stat(path
, &info
))
646 _("Can't access '%s':\n%s"), path
,
651 if (S_ISDIR(info
.st_mode
))
653 /* Use this program to set the backdrop */
654 pinboard_set_backdrop_app(path
);
656 else if (S_ISREG(info
.st_mode
))
660 radio
= g_object_get_data(G_OBJECT(dialog
), "radio_scaled");
661 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio
)))
663 set_backdrop(path
, BACKDROP_SCALE
);
667 radio
= g_object_get_data(G_OBJECT(dialog
), "radio_centred");
668 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio
)))
670 set_backdrop(path
, BACKDROP_CENTRE
);
674 set_backdrop(path
, BACKDROP_TILE
);
677 delayed_error(_("Only files (and certain applications) can be "
678 "used to set the background image."));
681 static void pinboard_check_options(void)
685 gdk_color_parse(o_pinboard_fg_colour
.value
, &n_fg
);
686 gdk_color_parse(o_pinboard_bg_colour
.value
, &n_bg
);
688 tasklist_set_active(o_pinboard_tasklist
.int_value
&& current_pinboard
);
690 if (gdk_color_equal(&n_fg
, &pin_text_fg_col
) == 0 ||
691 gdk_color_equal(&n_bg
, &pin_text_bg_col
) == 0)
693 pin_text_fg_col
= n_fg
;
694 pin_text_bg_col
= n_bg
;
698 g_object_unref(G_OBJECT(pinicon_style
));
699 pinicon_style
= NULL
;
702 if (current_pinboard
)
704 GtkWidget
*w
= current_pinboard
->window
;
707 cm
= gtk_widget_get_colormap(w
);
709 gdk_colormap_alloc_color(cm
, &n_bg
, FALSE
, TRUE
);
710 gtk_widget_modify_bg(w
, GTK_STATE_NORMAL
, &n_bg
);
712 /* Only redraw the background if there is no image */
713 if (!current_pinboard
->backdrop
)
714 reload_backdrop(current_pinboard
,
715 NULL
, BACKDROP_NONE
);
720 tasklist_style_changed();
724 static gint
end_wink(gpointer data
)
726 pinboard_wink_item(NULL
, FALSE
);
730 /* Updates the width, height, name_width and label fields, and resizes the
731 * window. Also sets the style to pinicon_style, generating it if needed.
733 static void set_size_and_style(PinIcon
*pi
)
735 Icon
*icon
= (Icon
*) pi
;
736 MaskedPixmap
*image
= icon
->item
->image
;
737 int iwidth
= image
->width
;
738 int iheight
= image
->height
;
740 gtk_widget_modify_fg(pi
->label
, GTK_STATE_PRELIGHT
, &pin_text_fg_col
);
741 gtk_widget_modify_bg(pi
->label
, GTK_STATE_PRELIGHT
, &pin_text_bg_col
);
742 gtk_widget_modify_fg(pi
->label
, GTK_STATE_NORMAL
, &pin_text_fg_col
);
743 gtk_widget_modify_bg(pi
->label
, GTK_STATE_NORMAL
, &pin_text_bg_col
);
745 gtk_label_set_text(GTK_LABEL(pi
->label
), icon
->item
->leafname
);
747 gtk_widget_set_size_request(pi
->widget
, iwidth
, iheight
);
750 static gint
draw_icon(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
)
752 static GtkWidgetClass
*parent_class
= NULL
;
753 Icon
*icon
= (Icon
*) pi
;
754 DirItem
*item
= icon
->item
;
755 MaskedPixmap
*image
= item
->image
;
756 int iwidth
= image
->width
;
757 int iheight
= image
->height
;
759 GtkStateType state
= icon
->selected
? GTK_STATE_SELECTED
764 gpointer c
= ((GTypeInstance
*) widget
)->g_class
;
765 parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent(c
);
768 x
= pi
->widget
->allocation
.x
;
769 y
= pi
->widget
->allocation
.y
;
771 gdk_pixbuf_render_to_drawable_alpha(
772 icon
->selected
? image
->pixbuf_lit
: image
->pixbuf
,
777 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
778 GDK_RGB_DITHER_NORMAL
, 0, 0);
780 if (item
->flags
& ITEM_FLAG_SYMLINK
)
782 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
787 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
788 GDK_RGB_DITHER_NORMAL
, 0, 0);
790 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
792 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
796 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
801 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
802 GDK_RGB_DITHER_NORMAL
, 0, 0);
807 gtk_paint_flat_box(pi
->label
->style
, pi
->label
->window
,
810 NULL
, pi
->label
, "text",
811 pi
->label
->allocation
.x
,
812 pi
->label
->allocation
.y
,
813 pi
->label
->allocation
.width
,
814 pi
->label
->allocation
.height
);
818 (parent_class
->expose_event
)(widget
, event
);
820 /* Stop the button effect */
824 static gint
draw_wink(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
)
826 gint x
, y
, width
, height
;
828 if (current_wink_icon
!= pi
)
831 x
= widget
->allocation
.x
;
832 y
= widget
->allocation
.y
;
833 width
= widget
->allocation
.width
;
834 height
= widget
->allocation
.height
;
836 gdk_draw_rectangle(widget
->window
,
837 pi
->widget
->style
->white_gc
,
839 x
, y
, width
- 1, height
- 1);
840 gdk_draw_rectangle(widget
->window
,
841 pi
->widget
->style
->black_gc
,
843 x
+ 1, y
+ 1, width
- 3, height
- 3);
848 static gboolean
enter_notify(GtkWidget
*widget
,
849 GdkEventCrossing
*event
,
852 icon_may_update((Icon
*) pi
);
857 static void perform_action(PinIcon
*pi
, GdkEventButton
*event
)
860 Icon
*icon
= (Icon
*) pi
;
862 action
= bind_lookup_bev(pi
? BIND_PINBOARD_ICON
: BIND_PINBOARD
,
865 /* Actions that can happen with or without an icon */
868 case ACT_CLEAR_SELECTION
:
869 icon_select_only(NULL
);
873 pinboard_show_menu(event
, pi
);
881 g_return_if_fail(pi
!= NULL
);
887 pinboard_wink_item(pi
, TRUE
);
888 if (event
->type
== GDK_2BUTTON_PRESS
)
889 icon_set_selected(icon
, FALSE
);
890 run_diritem(icon
->path
, icon
->item
, NULL
, NULL
, FALSE
);
894 pinboard_wink_item(pi
, TRUE
);
895 if (event
->type
== GDK_2BUTTON_PRESS
)
896 icon_set_selected(icon
, FALSE
);
897 run_diritem(icon
->path
, icon
->item
, NULL
, NULL
, TRUE
);
899 case ACT_PRIME_AND_SELECT
:
901 icon_select_only(icon
);
902 dnd_motion_start(MOTION_READY_FOR_DND
);
904 case ACT_PRIME_AND_TOGGLE
:
905 icon_set_selected(icon
, !icon
->selected
);
906 dnd_motion_start(MOTION_READY_FOR_DND
);
908 case ACT_PRIME_FOR_DND
:
909 dnd_motion_start(MOTION_READY_FOR_DND
);
911 case ACT_TOGGLE_SELECTED
:
912 icon_set_selected(icon
, !icon
->selected
);
914 case ACT_SELECT_EXCL
:
915 icon_select_only(icon
);
918 g_warning("Unsupported action : %d\n", action
);
923 static void forward_to_root(GdkEventButton
*event
)
927 if (event
->type
== GDK_BUTTON_PRESS
)
929 xev
.type
= ButtonPress
;
930 XUngrabPointer(gdk_display
, event
->time
);
933 xev
.type
= ButtonRelease
;
935 xev
.window
= gdk_x11_get_default_root_xwindow();
936 xev
.root
= xev
.window
;
937 xev
.subwindow
= None
;
938 xev
.time
= event
->time
;
941 xev
.x_root
= event
->x_root
;
942 xev
.y_root
= event
->y_root
;
943 xev
.state
= event
->state
;
944 xev
.button
= event
->button
;
945 xev
.same_screen
= True
;
947 XSendEvent(gdk_display
, xev
.window
, False
,
948 ButtonPressMask
| ButtonReleaseMask
, (XEvent
*) &xev
);
951 #define FORWARDED_BUTTON(pi, button) ((button) == 2 || \
952 ((button) == 3 && o_forward_button_3.int_value && !pi))
954 /* pi is NULL if this is a root event */
955 static gboolean
button_release_event(GtkWidget
*widget
,
956 GdkEventButton
*event
,
959 if (FORWARDED_BUTTON(pi
, event
->button
))
960 forward_to_root(event
);
961 else if (dnd_motion_release(event
))
964 perform_action(pi
, event
);
969 /* pi is NULL if this is a root event */
970 static gboolean
button_press_event(GtkWidget
*widget
,
971 GdkEventButton
*event
,
974 /* Just in case we've jumped in front of everything... */
975 gdk_window_lower(current_pinboard
->window
->window
);
977 if (FORWARDED_BUTTON(pi
, event
->button
))
978 forward_to_root(event
);
979 else if (dnd_motion_press(widget
, event
))
980 perform_action(pi
, event
);
985 static void start_drag(PinIcon
*pi
, GdkEventMotion
*event
)
987 GtkWidget
*widget
= pi
->win
;
988 Icon
*icon
= (Icon
*) pi
;
992 tmp_icon_selected
= TRUE
;
993 icon_select_only(icon
);
996 g_return_if_fail(icon_selection
!= NULL
);
998 pinboard_drag_in_progress
= icon
;
1000 if (icon_selection
->next
== NULL
)
1001 drag_one_item(widget
, event
, icon
->path
, icon
->item
, NULL
);
1006 uri_list
= icon_create_uri_list();
1007 drag_selection(widget
, event
, uri_list
);
1012 /* An icon is being dragged around... */
1013 static gint
icon_motion_notify(GtkWidget
*widget
,
1014 GdkEventMotion
*event
,
1017 if (motion_state
== MOTION_READY_FOR_DND
)
1019 if (dnd_motion_moved(event
))
1020 start_drag(pi
, event
);
1027 static void backdrop_from_xml(xmlNode
*node
)
1031 g_free(current_pinboard
->backdrop
);
1032 current_pinboard
->backdrop
= xmlNodeGetContent(node
);
1034 style
= xmlGetProp(node
, "style");
1038 current_pinboard
->backdrop_style
=
1039 g_strcasecmp(style
, "Tiled") == 0 ? BACKDROP_TILE
:
1040 g_strcasecmp(style
, "Scaled") == 0 ? BACKDROP_SCALE
:
1041 g_strcasecmp(style
, "Centred") == 0 ? BACKDROP_CENTRE
:
1042 g_strcasecmp(style
, "Program") == 0 ? BACKDROP_PROGRAM
:
1047 current_pinboard
->backdrop_style
= BACKDROP_TILE
;
1050 /* Create one pinboard icon for each icon in the doc */
1051 static void pinboard_load_from_xml(xmlDocPtr doc
)
1053 xmlNodePtr node
, root
;
1054 char *tmp
, *label
, *path
;
1057 root
= xmlDocGetRootElement(doc
);
1059 for (node
= root
->xmlChildrenNode
; node
; node
= node
->next
)
1061 if (node
->type
!= XML_ELEMENT_NODE
)
1063 if (strcmp(node
->name
, "backdrop") == 0)
1065 backdrop_from_xml(node
);
1068 if (strcmp(node
->name
, "icon") != 0)
1071 tmp
= xmlGetProp(node
, "x");
1077 tmp
= xmlGetProp(node
, "y");
1083 label
= xmlGetProp(node
, "label");
1085 label
= g_strdup("<missing label>");
1086 path
= xmlNodeGetContent(node
);
1088 path
= g_strdup("<missing path>");
1090 pinboard_pin(path
, label
, x
, y
);
1097 /* Called for each line in the pinboard file while loading a new board.
1098 * Only used for old-format files when converting to XML.
1100 static const char *pin_from_file(gchar
*line
)
1109 end
= strchr(line
+ 1, '>');
1111 return _("Missing '>' in icon label");
1113 leaf
= g_strndup(line
+ 1, end
- line
- 1);
1117 while (isspace(*line
))
1120 return _("Missing ',' after icon label");
1124 if (sscanf(line
, " %d , %d , %n", &x
, &y
, &n
) < 2)
1125 return NULL
; /* Ignore format errors */
1127 pinboard_pin(line
+ n
, leaf
, x
, y
);
1134 /* Write the current state of the pinboard to the current pinboard file */
1135 static void pinboard_save(void)
1137 guchar
*save
= NULL
;
1138 guchar
*save_new
= NULL
;
1140 xmlDocPtr doc
= NULL
;
1143 g_return_if_fail(current_pinboard
!= NULL
);
1145 if (strchr(current_pinboard
->name
, '/'))
1146 save
= g_strdup(current_pinboard
->name
);
1151 leaf
= g_strconcat("pb_", current_pinboard
->name
, NULL
);
1152 save
= choices_find_path_save(leaf
, PROJECT
, TRUE
);
1159 doc
= xmlNewDoc("1.0");
1160 xmlDocSetRootElement(doc
, xmlNewDocNode(doc
, NULL
, "pinboard", NULL
));
1162 root
= xmlDocGetRootElement(doc
);
1164 if (current_pinboard
->backdrop
)
1166 BackdropStyle style
= current_pinboard
->backdrop_style
;
1169 tree
= xmlNewTextChild(root
, NULL
, "backdrop",
1170 current_pinboard
->backdrop
);
1171 xmlSetProp(tree
, "style",
1172 style
== BACKDROP_TILE
? "Tiled" :
1173 style
== BACKDROP_CENTRE
? "Centred" :
1174 style
== BACKDROP_SCALE
? "Scaled" :
1178 for (next
= current_pinboard
->icons
; next
; next
= next
->next
)
1181 PinIcon
*pi
= (PinIcon
*) next
->data
;
1182 Icon
*icon
= (Icon
*) pi
;
1185 tree
= xmlNewTextChild(root
, NULL
, "icon", icon
->src_path
);
1187 tmp
= g_strdup_printf("%d", pi
->x
);
1188 xmlSetProp(tree
, "x", tmp
);
1191 tmp
= g_strdup_printf("%d", pi
->y
);
1192 xmlSetProp(tree
, "y", tmp
);
1195 xmlSetProp(tree
, "label", icon
->item
->leafname
);
1198 save_new
= g_strconcat(save
, ".new", NULL
);
1199 if (save_xml_file(doc
, save_new
) || rename(save_new
, save
))
1200 delayed_error(_("Error saving pinboard %s: %s"),
1201 save
, g_strerror(errno
));
1209 static void snap_to_grid(int *x
, int *y
)
1211 int step
= o_pinboard_grid_step
.int_value
;
1213 *x
= ((*x
+ step
/ 2) / step
) * step
;
1214 *y
= ((*y
+ step
/ 2) / step
) * step
;
1217 /* Convert (x,y) from a centre point to a window position */
1218 static void offset_from_centre(PinIcon
*pi
, int *x
, int *y
)
1220 gboolean clamp
= o_pinboard_clamp_icons
.int_value
;
1223 gtk_widget_size_request(pi
->win
, &req
);
1225 *x
-= req
.width
>> 1;
1226 *y
-= req
.height
>> 1;
1227 *x
= CLAMP(*x
, 0, screen_width
- (clamp
? req
.width
: 0));
1228 *y
= CLAMP(*y
, 0, screen_height
- (clamp
? req
.height
: 0));
1231 /* Same as drag_set_dest(), but for pinboard icons */
1232 static void drag_set_pinicon_dest(PinIcon
*pi
)
1234 GtkObject
*obj
= GTK_OBJECT(pi
->win
);
1236 make_drop_target(pi
->win
, 0);
1238 g_signal_connect(obj
, "drag_motion", G_CALLBACK(drag_motion
), pi
);
1239 g_signal_connect(obj
, "drag_leave", G_CALLBACK(drag_leave
), pi
);
1240 g_signal_connect(obj
, "drag_end", G_CALLBACK(drag_end
), pi
);
1243 /* Called during the drag when the mouse is in a widget registered
1244 * as a drop target. Returns TRUE if we can accept the drop.
1246 static gboolean
drag_motion(GtkWidget
*widget
,
1247 GdkDragContext
*context
,
1253 GdkDragAction action
= context
->suggested_action
;
1255 Icon
*icon
= (Icon
*) pi
;
1256 DirItem
*item
= icon
->item
;
1258 if (gtk_drag_get_source_widget(context
) == widget
)
1259 goto out
; /* Can't drag something to itself! */
1262 goto out
; /* Can't drag a selection to itself */
1264 type
= dnd_motion_item(context
, &item
);
1269 /* We actually must pretend to accept the drop, even if the
1270 * directory isn't writeable, so that the spring-opening
1274 /* Don't allow drops to non-writeable directories */
1275 if (o_dnd_spring_open
.int_value
== FALSE
&&
1276 type
== drop_dest_dir
&&
1277 access(icon
->path
, W_OK
) != 0)
1282 g_dataset_set_data(context
, "drop_dest_type", type
);
1285 gdk_drag_status(context
, action
, time
);
1286 g_dataset_set_data_full(context
, "drop_dest_path",
1287 g_strdup(icon
->path
), g_free
);
1288 if (type
== drop_dest_dir
)
1289 dnd_spring_load(context
, NULL
);
1291 pinboard_wink_item(pi
, FALSE
);
1294 gdk_drag_status(context
, 0, time
);
1296 /* Always return TRUE to stop the pinboard getting the events */
1300 static gboolean pinboard_shadow
= FALSE
;
1301 static gint shadow_x
, shadow_y
;
1302 #define SHADOW_SIZE (ICON_WIDTH)
1304 static gboolean
bg_expose(GtkWidget
*widget
,
1305 GdkEventExpose
*event
, gpointer data
)
1307 if (!pinboard_shadow
)
1310 gdk_draw_rectangle(widget
->window
,
1311 widget
->style
->white_gc
, FALSE
,
1313 SHADOW_SIZE
, SHADOW_SIZE
);
1314 gdk_draw_rectangle(widget
->window
,
1315 widget
->style
->black_gc
, FALSE
,
1316 shadow_x
+ 1, shadow_y
+ 1,
1317 SHADOW_SIZE
- 2, SHADOW_SIZE
- 2);
1322 /* Draw a 'shadow' under an icon being dragged, showing where
1325 static void pinboard_set_shadow(gboolean on
)
1329 if (pinboard_shadow
)
1333 area
.width
= SHADOW_SIZE
+ 1;
1334 area
.height
= SHADOW_SIZE
+ 1;
1336 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1342 int old_x
= shadow_x
, old_y
= shadow_y
;
1344 gdk_window_get_pointer(current_pinboard
->fixed
->window
,
1345 &shadow_x
, &shadow_y
, NULL
);
1346 snap_to_grid(&shadow_x
, &shadow_y
);
1347 shadow_x
-= SHADOW_SIZE
/ 2;
1348 shadow_y
-= SHADOW_SIZE
/ 2;
1351 if (pinboard_shadow
&& shadow_x
== old_x
&& shadow_y
== old_y
)
1356 area
.width
= SHADOW_SIZE
+ 1;
1357 area
.height
= SHADOW_SIZE
+ 1;
1359 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1363 pinboard_shadow
= on
;
1366 /* Called when dragging some pinboard icons finishes */
1367 void pinboard_move_icons(void)
1369 int x
= shadow_x
, y
= shadow_y
;
1370 PinIcon
*pi
= (PinIcon
*) pinboard_drag_in_progress
;
1373 g_return_if_fail(pi
!= NULL
);
1375 x
+= SHADOW_SIZE
/ 2;
1376 y
+= SHADOW_SIZE
/ 2;
1377 snap_to_grid(&x
, &y
);
1379 if (pi
->x
== x
&& pi
->y
== y
)
1384 gdk_drawable_get_size(pi
->win
->window
, &width
, &height
);
1385 offset_from_centre(pi
, &x
, &y
);
1387 fixed_move_fast(GTK_FIXED(current_pinboard
->fixed
), pi
->win
, x
, y
);
1392 static void drag_leave(GtkWidget
*widget
,
1393 GdkDragContext
*context
,
1397 pinboard_wink_item(NULL
, FALSE
);
1401 static gboolean
bg_drag_leave(GtkWidget
*widget
,
1402 GdkDragContext
*context
,
1406 pinboard_set_shadow(FALSE
);
1410 static gboolean
bg_drag_motion(GtkWidget
*widget
,
1411 GdkDragContext
*context
,
1417 /* Dragging from the pinboard to the pinboard is not allowed */
1419 if (!provides(context
, text_uri_list
))
1422 pinboard_set_shadow(TRUE
);
1424 gdk_drag_status(context
,
1425 context
->suggested_action
== GDK_ACTION_ASK
1426 ? GDK_ACTION_LINK
: context
->suggested_action
,
1431 static void drag_end(GtkWidget
*widget
,
1432 GdkDragContext
*context
,
1435 pinboard_drag_in_progress
= NULL
;
1436 if (tmp_icon_selected
)
1438 icon_select_only(NULL
);
1439 tmp_icon_selected
= FALSE
;
1443 /* Something which affects all the icons has changed - reshape
1444 * and redraw all of them.
1446 static void reshape_all(void)
1450 g_return_if_fail(current_pinboard
!= NULL
);
1452 for (next
= current_pinboard
->icons
; next
; next
= next
->next
)
1454 Icon
*icon
= (Icon
*) next
->data
;
1455 pinboard_reshape_icon(icon
);
1459 /* Turns off the pinboard. Does not call gtk_main_quit. */
1460 static void pinboard_clear(void)
1464 g_return_if_fail(current_pinboard
!= NULL
);
1466 tasklist_set_active(FALSE
);
1468 next
= current_pinboard
->icons
;
1471 PinIcon
*pi
= (PinIcon
*) next
->data
;
1475 gtk_widget_destroy(pi
->win
);
1478 gtk_widget_destroy(current_pinboard
->window
);
1480 abandon_backdrop_app(current_pinboard
);
1482 g_free(current_pinboard
->name
);
1483 g_free(current_pinboard
);
1484 current_pinboard
= NULL
;
1486 number_of_windows
--;
1489 static gpointer parent_class
;
1491 static void pin_icon_destroy(Icon
*icon
)
1493 PinIcon
*pi
= (PinIcon
*) icon
;
1495 g_return_if_fail(pi
->win
!= NULL
);
1497 gtk_widget_destroy(pi
->win
);
1500 static void pinboard_remove_items(void)
1502 g_return_if_fail(icon_selection
!= NULL
);
1504 while (icon_selection
)
1505 icon_destroy((Icon
*) icon_selection
->data
);
1510 static void pin_icon_update(Icon
*icon
)
1512 pinboard_reshape_icon(icon
);
1516 static gboolean
pin_icon_same_group(Icon
*icon
, Icon
*other
)
1518 return IS_PIN_ICON(other
);
1521 static void pin_icon_class_init(gpointer gclass
, gpointer data
)
1523 IconClass
*icon
= (IconClass
*) gclass
;
1525 parent_class
= g_type_class_peek_parent(gclass
);
1527 icon
->destroy
= pin_icon_destroy
;
1528 icon
->redraw
= pinboard_reshape_icon
;
1529 icon
->update
= pin_icon_update
;
1530 icon
->remove_items
= pinboard_remove_items
;
1531 icon
->same_group
= pin_icon_same_group
;
1534 static void pin_icon_init(GTypeInstance
*object
, gpointer gclass
)
1538 static GType
pin_icon_get_type(void)
1540 static GType type
= 0;
1544 static const GTypeInfo info
=
1546 sizeof (PinIconClass
),
1547 NULL
, /* base_init */
1548 NULL
, /* base_finalise */
1549 pin_icon_class_init
,
1550 NULL
, /* class_finalise */
1551 NULL
, /* class_data */
1553 0, /* n_preallocs */
1557 type
= g_type_register_static(icon_get_type(),
1558 "PinIcon", &info
, 0);
1564 static PinIcon
*pin_icon_new(const char *pathname
, const char *name
)
1569 pi
= g_object_new(pin_icon_get_type(), NULL
);
1572 icon_set_path(icon
, pathname
, name
);
1577 /* Called when the window widget is somehow destroyed */
1578 static void pin_icon_destroyed(PinIcon
*pi
)
1580 g_return_if_fail(pi
->win
!= NULL
);
1584 pinboard_wink_item(NULL
, FALSE
);
1586 if (pinboard_drag_in_progress
== (Icon
*) pi
)
1587 pinboard_drag_in_progress
= NULL
;
1589 if (current_pinboard
)
1590 current_pinboard
->icons
=
1591 g_list_remove(current_pinboard
->icons
, pi
);
1596 /* Set the tooltip */
1597 static void pin_icon_set_tip(PinIcon
*pi
)
1601 Icon
*icon
= (Icon
*) pi
;
1603 g_return_if_fail(pi
!= NULL
);
1605 ai
= appinfo_get(icon
->path
, icon
->item
);
1607 if (ai
&& ((node
= xml_get_section(ai
, NULL
, "Summary"))))
1610 str
= xmlNodeListGetString(node
->doc
,
1611 node
->xmlChildrenNode
, 1);
1614 gtk_tooltips_set_tip(tooltips
, pi
->win
, str
, NULL
);
1619 gtk_tooltips_set_tip(tooltips
, pi
->widget
, NULL
, NULL
);
1625 static void pinboard_show_menu(GdkEventButton
*event
, PinIcon
*pi
)
1629 pos
[0] = event
->x_root
;
1630 pos
[1] = event
->y_root
;
1633 icon_prepare_menu((Icon
*) pi
, TRUE
);
1635 gtk_menu_popup(GTK_MENU(icon_menu
), NULL
, NULL
,
1637 (gpointer
) pos
, event
->button
, event
->time
);
1640 static void create_pinboard_window(Pinboard
*pinboard
)
1644 g_return_if_fail(pinboard
->window
== NULL
);
1646 win
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1648 gtk_widget_modify_bg(win
, GTK_STATE_NORMAL
, &pin_text_bg_col
);
1650 gtk_widget_set_app_paintable(win
, TRUE
);
1651 gtk_widget_set_name(win
, "rox-pinboard");
1652 pinboard
->window
= win
;
1653 pinboard
->fixed
= gtk_fixed_new();
1654 gtk_container_add(GTK_CONTAINER(win
), pinboard
->fixed
);
1656 gtk_window_set_wmclass(GTK_WINDOW(win
), "ROX-Pinboard", PROJECT
);
1658 gtk_widget_set_size_request(win
, screen_width
, screen_height
);
1659 gtk_widget_realize(win
);
1660 gtk_window_move(GTK_WINDOW(win
), 0, 0);
1661 make_panel_window(win
);
1663 /* TODO: Use gdk function when it supports this type */
1665 GdkAtom desktop_type
;
1667 desktop_type
= gdk_atom_intern("_NET_WM_WINDOW_TYPE_DESKTOP",
1669 gdk_property_change(win
->window
,
1670 gdk_atom_intern("_NET_WM_WINDOW_TYPE", FALSE
),
1671 gdk_atom_intern("ATOM", FALSE
), 32,
1672 GDK_PROP_MODE_REPLACE
, (guchar
*) &desktop_type
, 1);
1675 gtk_widget_add_events(win
,
1676 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
1678 g_signal_connect(win
, "button-press-event",
1679 G_CALLBACK(button_press_event
), NULL
);
1680 g_signal_connect(win
, "button-release-event",
1681 G_CALLBACK(button_release_event
), NULL
);
1682 g_signal_connect(pinboard
->fixed
, "expose_event",
1683 G_CALLBACK(bg_expose
), NULL
);
1685 /* Drag and drop handlers */
1686 drag_set_pinboard_dest(win
);
1687 g_signal_connect(win
, "drag_motion", G_CALLBACK(bg_drag_motion
), NULL
);
1688 g_signal_connect(win
, "drag_leave", G_CALLBACK(bg_drag_leave
), NULL
);
1690 gtk_widget_show_all(win
);
1691 gdk_window_lower(win
->window
);
1694 /* Load image 'path' and scale according to 'style' */
1695 static GdkPixmap
*load_backdrop(const gchar
*path
, BackdropStyle style
)
1699 GError
*error
= NULL
;
1701 pixbuf
= gdk_pixbuf_new_from_file(path
, &error
);
1704 delayed_error(_("Error loading backdrop image:\n%s\n"
1705 "Backdrop removed."),
1707 g_error_free(error
);
1708 set_backdrop(NULL
, BACKDROP_NONE
);
1712 if (style
== BACKDROP_SCALE
)
1714 GdkPixbuf
*old
= pixbuf
;
1716 pixbuf
= gdk_pixbuf_scale_simple(old
,
1717 screen_width
, screen_height
,
1720 g_object_unref(old
);
1722 else if (style
== BACKDROP_CENTRE
)
1724 GdkPixbuf
*old
= pixbuf
;
1725 int x
, y
, width
, height
;
1727 width
= gdk_pixbuf_get_width(pixbuf
);
1728 height
= gdk_pixbuf_get_height(pixbuf
);
1730 pixbuf
= gdk_pixbuf_new(
1731 gdk_pixbuf_get_colorspace(pixbuf
), 0,
1732 8, screen_width
, screen_height
);
1733 gdk_pixbuf_fill(pixbuf
, 0);
1735 x
= (screen_width
- width
) / 2;
1736 y
= (screen_height
- height
) / 2;
1740 gdk_pixbuf_composite(old
, pixbuf
,
1742 MIN(screen_width
, width
),
1743 MIN(screen_height
, height
),
1745 GDK_INTERP_NEAREST
, 255);
1746 g_object_unref(old
);
1749 gdk_pixbuf_render_pixmap_and_mask(pixbuf
,
1751 g_object_unref(pixbuf
);
1756 static void abandon_backdrop_app(Pinboard
*pinboard
)
1758 g_return_if_fail(pinboard
!= NULL
);
1760 if (pinboard
->to_backdrop_app
!= -1)
1762 close(pinboard
->to_backdrop_app
);
1763 close(pinboard
->from_backdrop_app
);
1764 gtk_input_remove(pinboard
->input_tag
);
1765 g_string_free(pinboard
->input_buffer
, TRUE
);
1766 pinboard
->to_backdrop_app
= -1;
1767 pinboard
->from_backdrop_app
= -1;
1768 pinboard
->input_tag
= -1;
1769 pinboard
->input_buffer
= NULL
;
1772 g_return_if_fail(pinboard
->to_backdrop_app
== -1);
1773 g_return_if_fail(pinboard
->from_backdrop_app
== -1);
1774 g_return_if_fail(pinboard
->input_tag
== -1);
1775 g_return_if_fail(pinboard
->input_buffer
== NULL
);
1778 /* A single line has been read from the child.
1779 * Processes the command, and replies 'ok' (or abandons the child on error).
1781 static void command_from_backdrop_app(Pinboard
*pinboard
, const gchar
*command
)
1783 BackdropStyle style
;
1784 const char *ok
= "ok\n";
1786 if (strncmp(command
, "tile ", 5) == 0)
1788 style
= BACKDROP_TILE
;
1791 else if (strncmp(command
, "scale ", 6) == 0)
1793 style
= BACKDROP_SCALE
;
1796 else if (strncmp(command
, "centre ", 7) == 0)
1798 style
= BACKDROP_CENTRE
;
1803 g_warning("Invalid command '%s' from backdrop app\n",
1805 abandon_backdrop_app(pinboard
);
1813 sent
= write(pinboard
->to_backdrop_app
, ok
, strlen(ok
));
1816 /* Remote app quit? Not an error. */
1817 abandon_backdrop_app(pinboard
);
1823 reload_backdrop(pinboard
, command
, style
);
1826 static void backdrop_from_child(Pinboard
*pinboard
,
1827 int src
, GdkInputCondition cond
)
1832 got
= read(src
, buf
, sizeof(buf
));
1837 g_warning("backdrop_from_child: %s\n",
1839 abandon_backdrop_app(pinboard
);
1843 g_string_append_len(pinboard
->input_buffer
, buf
, got
);
1845 while (pinboard
->from_backdrop_app
!= -1)
1850 nl
= strchr(pinboard
->input_buffer
->str
, '\n');
1852 return; /* Haven't got a whole line yet */
1854 len
= nl
- pinboard
->input_buffer
->str
;
1855 command
= g_strndup(pinboard
->input_buffer
->str
, len
);
1856 g_string_erase(pinboard
->input_buffer
, 0, len
+ 1);
1858 command_from_backdrop_app(pinboard
, command
);
1864 static void reload_backdrop(Pinboard
*pinboard
,
1865 const gchar
*backdrop
,
1866 BackdropStyle backdrop_style
)
1870 if (backdrop
&& backdrop_style
== BACKDROP_PROGRAM
)
1872 const char *argv
[] = {NULL
, "--backdrop", NULL
};
1873 GError
*error
= NULL
;
1875 g_return_if_fail(pinboard
->to_backdrop_app
== -1);
1876 g_return_if_fail(pinboard
->from_backdrop_app
== -1);
1877 g_return_if_fail(pinboard
->input_tag
== -1);
1878 g_return_if_fail(pinboard
->input_buffer
== NULL
);
1880 argv
[0] = make_path(backdrop
, "AppRun")->str
;
1882 /* Run the program. It'll send us a SOAP message and we'll
1883 * get back here with a different style and image.
1886 if (g_spawn_async_with_pipes(NULL
, (gchar
**) argv
, NULL
,
1887 G_SPAWN_DO_NOT_REAP_CHILD
|
1888 G_SPAWN_SEARCH_PATH
,
1889 NULL
, NULL
, /* Child setup fn */
1890 NULL
, /* Child PID */
1891 &pinboard
->to_backdrop_app
,
1892 &pinboard
->from_backdrop_app
,
1893 NULL
, /* Standard error */
1896 pinboard
->input_buffer
= g_string_new(NULL
);
1897 pinboard
->input_tag
= gtk_input_add_full(
1898 pinboard
->from_backdrop_app
,
1900 (GdkInputFunction
) backdrop_from_child
,
1901 NULL
, pinboard
, NULL
);
1905 delayed_error("%s", error
? error
->message
: "(null)");
1906 g_error_free(error
);
1911 /* Note: Copying a style does not ref the pixmaps! */
1913 style
= gtk_style_copy(gtk_widget_get_style(pinboard
->window
));
1914 style
->bg_pixmap
[GTK_STATE_NORMAL
] = NULL
;
1917 style
->bg_pixmap
[GTK_STATE_NORMAL
] =
1918 load_backdrop(backdrop
, backdrop_style
);
1920 gdk_color_parse(o_pinboard_bg_colour
.value
,
1921 &style
->bg
[GTK_STATE_NORMAL
]);
1923 gtk_widget_set_style(pinboard
->window
, style
);
1925 g_object_unref(style
);
1927 gtk_widget_queue_draw(pinboard
->window
);
1929 /* Also update root window property (for transparent xterms, etc) */
1930 if (style
->bg_pixmap
[GTK_STATE_NORMAL
])
1932 XID id
= GDK_DRAWABLE_XID(style
->bg_pixmap
[GTK_STATE_NORMAL
]);
1933 gdk_property_change(gdk_get_default_root_window(),
1934 gdk_atom_intern("_XROOTPMAP_ID", FALSE
),
1935 gdk_atom_intern("PIXMAP", FALSE
),
1936 32, GDK_PROP_MODE_REPLACE
,
1941 gdk_property_delete(gdk_get_default_root_window(),
1942 gdk_atom_intern("_XROOTPMAP_ID", FALSE
));
1946 /* Set and save (path, style) as the new backdrop.
1947 * If style is BACKDROP_PROGRAM, the program is run to get the backdrop.
1948 * Otherwise, the image is displayed now.
1950 static void set_backdrop(const gchar
*path
, BackdropStyle style
)
1952 g_return_if_fail((path
== NULL
&& style
== BACKDROP_NONE
) ||
1953 (path
!= NULL
&& style
!= BACKDROP_NONE
));
1955 if (!current_pinboard
)
1959 pinboard_activate("Default");
1960 delayed_error(_("No pinboard was in use... "
1961 "the 'Default' pinboard has been selected. "
1962 "Use 'rox -p=Default' to turn it on in "
1964 g_return_if_fail(current_pinboard
!= NULL
);
1967 /* We might have just run the old backdrop program and now
1968 * we're going to set a new one! Seems a bit mean...
1971 abandon_backdrop_app(current_pinboard
);
1973 g_free(current_pinboard
->backdrop
);
1974 current_pinboard
->backdrop
= g_strdup(path
);
1975 current_pinboard
->backdrop_style
= style
;
1976 reload_backdrop(current_pinboard
,
1977 current_pinboard
->backdrop
,
1978 current_pinboard
->backdrop_style
);
1983 #define SEARCH_STEP 32
1985 static void search_free(GdkRectangle
*rect
, GdkRegion
*used
,
1986 int *outer
, int od
, int omax
,
1987 int *inner
, int id
, int imax
)
1989 *outer
= od
> 0 ? 0 : omax
;
1990 while (*outer
>= 0 && *outer
<= omax
)
1992 *inner
= id
> 0 ? 0 : imax
;
1993 while (*inner
>= 0 && *inner
<= imax
)
1995 if (gdk_region_rect_in(used
, rect
) ==
1996 GDK_OVERLAP_RECTANGLE_OUT
)
2008 /* Finds a free area on the pinboard large enough for the width and height
2009 * of the given rectangle, by filling in the x and y fields of 'rect'.
2010 * The search order respects user preferences.
2011 * If no area is free, returns any old area.
2013 static void find_free_rect(Pinboard
*pinboard
, GdkRectangle
*rect
)
2017 GdkRectangle used_rect
;
2018 int dx
= SEARCH_STEP
, dy
= SEARCH_STEP
;
2020 used
= gdk_region_new();
2022 panel_mark_used(used
);
2024 /* Subtract the used areas... */
2026 next
= GTK_FIXED(pinboard
->fixed
)->children
;
2027 for (; next
; next
= next
->next
)
2029 GtkFixedChild
*fix
= (GtkFixedChild
*) next
->data
;
2031 if (!GTK_WIDGET_VISIBLE(fix
->widget
))
2034 used_rect
.x
= fix
->x
;
2035 used_rect
.y
= fix
->y
;
2036 used_rect
.width
= fix
->widget
->requisition
.width
;
2037 used_rect
.height
= fix
->widget
->requisition
.height
;
2039 gdk_region_union_with_rect(used
, &used_rect
);
2042 /* Find the first free area (yes, this isn't exactly pretty, but
2043 * it works). If you know a better (fast!) algorithm, let me know!
2047 if (o_iconify_start
.int_value
== CORNER_TOP_RIGHT
||
2048 o_iconify_start
.int_value
== CORNER_BOTTOM_RIGHT
)
2051 if (o_iconify_start
.int_value
== CORNER_BOTTOM_LEFT
||
2052 o_iconify_start
.int_value
== CORNER_BOTTOM_RIGHT
)
2055 if (o_iconify_dir
.int_value
== DIR_VERT
)
2057 search_free(rect
, used
,
2058 &rect
->x
, dx
, screen_width
- rect
->width
,
2059 &rect
->y
, dy
, screen_height
- rect
->height
);
2063 search_free(rect
, used
,
2064 &rect
->y
, dy
, screen_height
- rect
->height
,
2065 &rect
->x
, dx
, screen_width
- rect
->width
);
2068 gdk_region_destroy(used
);
2077 /* Icon's size, shape or appearance has changed - update the display */
2078 static void pinboard_reshape_icon(Icon
*icon
)
2080 PinIcon
*pi
= (PinIcon
*) icon
;
2081 int x
= pi
->x
, y
= pi
->y
;
2083 set_size_and_style(pi
);
2084 offset_from_centre(pi
, &x
, &y
);
2086 if (pi
->win
->allocation
.x
!= x
|| pi
->win
->allocation
.y
!= y
)
2088 fixed_move_fast(GTK_FIXED(current_pinboard
->fixed
),