2 * ROX-Filer, filer for the ROX desktop project
3 * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place, Suite 330, Boston, MA 02111-1307 USA
20 /* pinboard.c - icons on the desktop background */
32 #include <libxml/parser.h>
44 #include "gui_support.h"
54 #include "panel.h" /* For panel_mark_used() */
58 static gboolean tmp_icon_selected
= FALSE
; /* When dragging */
60 static GtkWidget
*set_backdrop_dialog
= NULL
;
63 guchar
*name
; /* Leaf name */
67 gchar
*backdrop
; /* Pathname */
68 BackdropStyle backdrop_style
;
69 gint to_backdrop_app
; /* pipe FD, or -1 */
70 gint from_backdrop_app
; /* pipe FD, or -1 */
72 GString
*input_buffer
;
74 GtkWidget
*window
; /* Screen-sized window */
79 #define IS_PIN_ICON(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), pin_icon_get_type())
81 typedef struct _PinIconClass PinIconClass
;
82 typedef struct _PinIcon PinIcon
;
84 struct _PinIconClass
{
93 GtkWidget
*widget
; /* The drawing area for the icon */
97 /* The number of pixels between the bottom of the image and the top
102 /* The size of the border around the icon which is used when winking */
106 #define GRID_STEP_FINE 2
107 #define GRID_STEP_MED 16
108 #define GRID_STEP_COARSE 32
110 /* Used in options */
111 #define CORNER_TOP_LEFT 0
112 #define CORNER_TOP_RIGHT 1
113 #define CORNER_BOTTOM_LEFT 2
114 #define CORNER_BOTTOM_RIGHT 3
119 static PinIcon
*current_wink_icon
= NULL
;
120 static gint wink_timeout
;
122 /* Used for the text colours (only) in the icons */
123 GdkColor pin_text_fg_col
, pin_text_bg_col
;
124 PangoFontDescription
*pinboard_font
= NULL
; /* NULL => Gtk default */
126 static GdkColor pin_text_shadow_col
;
128 Pinboard
*current_pinboard
= NULL
;
129 static gint loading_pinboard
= 0; /* Non-zero => loading */
131 /* The Icon that was used to start the current drag, if any */
132 Icon
*pinboard_drag_in_progress
= NULL
;
134 /* For selecting groups of icons */
135 static gboolean lasso_in_progress
= FALSE
;
136 static int lasso_rect_x1
, lasso_rect_x2
;
137 static int lasso_rect_y1
, lasso_rect_y2
;
139 /* Tracking icon positions */
140 static GHashTable
*placed_icons
=NULL
;
142 Option o_pinboard_tasklist_per_workspace
;
143 static Option o_pinboard_clamp_icons
, o_pinboard_grid_step
;
144 static Option o_pinboard_fg_colour
, o_pinboard_bg_colour
;
145 static Option o_pinboard_tasklist
, o_forward_buttons_13
;
146 static Option o_iconify_start
, o_iconify_dir
;
147 static Option o_label_font
, o_pinboard_shadow_colour
;
148 static Option o_pinboard_shadow_labels
;
149 static Option o_blackbox_hack
;
151 static Option o_top_margin
, o_bottom_margin
, o_left_margin
, o_right_margin
;
152 static Option o_pinboard_image_scaling
;
154 /* Static prototypes */
155 static GType
pin_icon_get_type(void);
156 static void set_size_and_style(PinIcon
*pi
);
157 static gint
draw_icon(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
);
158 static gint
end_wink(gpointer data
);
159 static gboolean
button_release_event(GtkWidget
*widget
,
160 GdkEventButton
*event
,
162 static gboolean
enter_notify(GtkWidget
*widget
,
163 GdkEventCrossing
*event
,
165 static gint
leave_notify(GtkWidget
*widget
,
166 GdkEventCrossing
*event
,
168 static gboolean
button_press_event(GtkWidget
*widget
,
169 GdkEventButton
*event
,
171 static gboolean
scroll_event(GtkWidget
*widget
,
172 GdkEventScroll
*event
);
173 static gint
icon_motion_notify(GtkWidget
*widget
,
174 GdkEventMotion
*event
,
176 static const char *pin_from_file(gchar
*line
);
177 static void snap_to_grid(int *x
, int *y
);
178 static void offset_from_centre(PinIcon
*pi
, int *x
, int *y
);
179 static gboolean
drag_motion(GtkWidget
*widget
,
180 GdkDragContext
*context
,
185 static void drag_set_pinicon_dest(PinIcon
*pi
);
186 static void drag_leave(GtkWidget
*widget
,
187 GdkDragContext
*context
,
190 static gboolean
bg_drag_motion(GtkWidget
*widget
,
191 GdkDragContext
*context
,
196 static gboolean
bg_drag_leave(GtkWidget
*widget
,
197 GdkDragContext
*context
,
200 static gboolean
bg_expose(GtkWidget
*window
,
201 GdkEventExpose
*event
, gpointer data
);
202 static void drag_end(GtkWidget
*widget
,
203 GdkDragContext
*context
,
205 static void reshape_all(void);
206 static void pinboard_check_options(void);
207 static void pinboard_load_from_xml(xmlDocPtr doc
);
208 static void pinboard_clear(void);
209 static void pinboard_save(void);
210 static PinIcon
*pin_icon_new(const char *pathname
, const char *name
);
211 static void pin_icon_destroyed(PinIcon
*pi
);
212 static void pin_icon_set_tip(PinIcon
*pi
);
213 static void pinboard_show_menu(GdkEventButton
*event
, PinIcon
*pi
);
214 static void create_pinboard_window(Pinboard
*pinboard
);
215 static void reload_backdrop(Pinboard
*pinboard
,
216 const gchar
*backdrop
,
217 BackdropStyle backdrop_style
);
218 static void pinboard_reshape_icon(Icon
*icon
);
219 static gint
draw_wink(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
);
220 static void abandon_backdrop_app(Pinboard
*pinboard
);
221 static void drag_backdrop_dropped(GtkWidget
*drop_box
,
224 static void backdrop_response(GtkWidget
*dialog
, gint response
, gpointer data
);
225 static void find_free_rect(Pinboard
*pinboard
, GdkRectangle
*rect
,
226 gboolean old
, int start
, int direction
);
227 static void update_pinboard_font(void);
228 static void draw_lasso(void);
229 static gint
lasso_motion(GtkWidget
*widget
, GdkEventMotion
*event
, gpointer d
);
230 static void clear_backdrop(GtkWidget
*drop_box
, gpointer data
);
231 static void radios_changed(Radios
*radios
, gpointer data
);
232 static void update_radios(GtkWidget
*dialog
);
233 static void pinboard_set_backdrop_box(void);
235 /****************************************************************
236 * EXTERNAL INTERFACE *
237 ****************************************************************/
239 void pinboard_init(void)
241 option_add_string(&o_pinboard_fg_colour
, "pinboard_fg_colour", "#fff");
242 option_add_string(&o_pinboard_bg_colour
, "pinboard_bg_colour", "#888");
243 option_add_string(&o_pinboard_shadow_colour
, "pinboard_shadow_colour",
245 option_add_string(&o_label_font
, "label_font", "");
246 option_add_int(&o_pinboard_shadow_labels
, "pinboard_shadow_labels", 1);
248 option_add_int(&o_pinboard_clamp_icons
, "pinboard_clamp_icons", 1);
249 option_add_int(&o_pinboard_grid_step
, "pinboard_grid_step",
251 option_add_int(&o_pinboard_tasklist
, "pinboard_tasklist", TRUE
);
252 option_add_int(&o_pinboard_tasklist_per_workspace
, "pinboard_tasklist_per_workspace", FALSE
);
253 option_add_int(&o_forward_buttons_13
, "pinboard_forward_buttons_13",
256 option_add_int(&o_iconify_start
, "iconify_start", CORNER_TOP_RIGHT
);
257 option_add_int(&o_iconify_dir
, "iconify_dir", DIR_VERT
);
259 option_add_int(&o_blackbox_hack
, "blackbox_hack", FALSE
);
261 option_add_int(&o_top_margin
, "pinboard_top_margin", 0);
262 option_add_int(&o_bottom_margin
, "pinboard_bottom_margin", 0);
263 option_add_int(&o_left_margin
, "pinboard_left_margin", 0);
264 option_add_int(&o_right_margin
, "pinboard_right_margin", 0);
266 option_add_int(&o_pinboard_image_scaling
, "pinboard_image_scaling", 0);
268 option_add_notify(pinboard_check_options
);
270 gdk_color_parse(o_pinboard_fg_colour
.value
, &pin_text_fg_col
);
271 gdk_color_parse(o_pinboard_bg_colour
.value
, &pin_text_bg_col
);
272 gdk_color_parse(o_pinboard_shadow_colour
.value
, &pin_text_shadow_col
);
273 update_pinboard_font();
275 placed_icons
=g_hash_table_new(g_str_hash
, g_str_equal
);
278 /* Load 'pb_<pinboard>' config file from Choices (if it exists)
279 * and make it the current pinboard.
280 * Any existing pinned items are removed. You must call this
281 * at least once before using the pinboard. NULL disables the
284 void pinboard_activate(const gchar
*name
)
286 Pinboard
*old_board
= current_pinboard
;
287 guchar
*path
, *slash
;
289 /* Treat an empty name the same as NULL */
298 if (number_of_windows
< 1 && gtk_main_level() > 0)
301 gdk_property_delete(gdk_get_default_root_window(),
302 gdk_atom_intern("_XROOTPMAP_ID", FALSE
));
308 slash
= strchr(name
, '/');
311 if (access(name
, F_OK
))
312 path
= NULL
; /* File does not (yet) exist */
314 path
= g_strdup(name
);
320 leaf
= g_strconcat("pb_", name
, NULL
);
321 path
= choices_find_xdg_path_load(leaf
, PROJECT
, SITE
);
325 current_pinboard
= g_new(Pinboard
, 1);
326 current_pinboard
->name
= g_strdup(name
);
327 current_pinboard
->icons
= NULL
;
328 current_pinboard
->window
= NULL
;
329 current_pinboard
->backdrop
= NULL
;
330 current_pinboard
->backdrop_style
= BACKDROP_NONE
;
331 current_pinboard
->to_backdrop_app
= -1;
332 current_pinboard
->from_backdrop_app
= -1;
333 current_pinboard
->input_tag
= -1;
334 current_pinboard
->input_buffer
= NULL
;
336 create_pinboard_window(current_pinboard
);
342 doc
= xmlParseFile(path
);
345 pinboard_load_from_xml(doc
);
347 reload_backdrop(current_pinboard
,
348 current_pinboard
->backdrop
,
349 current_pinboard
->backdrop_style
);
353 parse_file(path
, pin_from_file
);
354 info_message(_("Your old pinboard file has been "
355 "converted to the new XML format."));
361 pinboard_pin(home_dir
, "Home",
367 if (o_pinboard_tasklist
.int_value
)
368 tasklist_set_active(TRUE
);
371 /* Return the window of the current pinboard, or NULL.
372 * Used to make sure lowering the panels doesn't lose them...
374 GdkWindow
*pinboard_get_window(void)
376 if (current_pinboard
)
377 return current_pinboard
->window
->window
;
381 const char *pinboard_get_name(void)
383 g_return_val_if_fail(current_pinboard
!= NULL
, NULL
);
385 return current_pinboard
->name
;
388 /* Add widget to the pinboard. Caller is responsible for coping with pinboard
391 void pinboard_add_widget(GtkWidget
*widget
, const gchar
*name
)
394 GdkRectangle
*rect
=NULL
;
395 gboolean found
=FALSE
;
397 g_return_if_fail(current_pinboard
!= NULL
);
399 gtk_fixed_put(GTK_FIXED(current_pinboard
->fixed
), widget
, 0, 0);
401 gtk_widget_size_request(widget
, &req
);
404 rect
=g_hash_table_lookup(placed_icons
, name
);
408 /*printf("%s at %p %d\n", name? name: "(nil)", rect, found);*/
411 if(rect
->width
<req
.width
|| rect
->height
<req
.height
) {
415 rect
=g_new(GdkRectangle
, 1);
416 rect
->width
= req
.width
;
417 rect
->height
= req
.height
;
419 /*printf("%s at %d,%d %d\n", name? name: "(nil)", rect->x,
421 find_free_rect(current_pinboard
, rect
, found
,
422 o_iconify_start
.int_value
, o_iconify_dir
.int_value
);
423 /*printf("%s at %d,%d %d\n", name? name: "(nil)", rect->x,
426 gtk_fixed_move(GTK_FIXED(current_pinboard
->fixed
),
427 widget
, rect
->x
, rect
->y
);
429 /* Store the new position (key and value are never freed) */
431 g_hash_table_insert(placed_icons
, g_strdup(name
),
435 void pinboard_moved_widget(GtkWidget
*widget
, const gchar
*name
,
442 rect
=g_hash_table_lookup(placed_icons
, name
);
450 /* Add a new icon to the background.
451 * 'path' should be an absolute pathname.
452 * 'x' and 'y' are the coordinates of the point in the middle of the text.
453 * If they are negative, the icon is placed automatically.
454 * The values then indicate where they should be added.
455 * x: -1 means left, -2 means right
456 * y: -1 means top, -2 means bottom
457 * 'name' is the name to use. If NULL then the leafname of path is used.
458 * If update is TRUE and there's already an icon for that path, it is updated.
460 void pinboard_pin_with_args(const gchar
*path
, const gchar
*name
,
461 int x
, int y
, const gchar
*shortcut
,
462 const gchar
*args
, gboolean locked
, gboolean update
)
464 GtkWidget
*align
, *vbox
;
469 g_return_if_fail(path
!= NULL
);
470 g_return_if_fail(current_pinboard
!= NULL
);
478 for (iter
= current_pinboard
->icons
; iter
; iter
= iter
->next
)
480 icon
= (Icon
*) iter
->data
;
481 if (strcmp(icon
->path
, path
) == 0)
483 pi
= (PinIcon
*) icon
;
493 icon_set_path(icon
, path
, name
);
494 goto out
; /* The icon is already on the pinboard. */
498 pi
= pin_icon_new(path
, name
);
501 /* This is a bit complicated...
503 * An icon needs to be a NO_WINDOW widget so that the image can
504 * blend with the background (A ParentRelative window also works, but
505 * is slow, causes the xfree86's memory consumption to grow without
506 * bound, and doesn't even get freed when the filer quits!).
508 * However, the icon also needs to have a window, so we get events
509 * delivered correctly. The solution is to float an InputOnly window
510 * over the icon. Since GtkButton works the same way, we just use
514 /* Button takes the initial ref of Icon */
515 pi
->win
= gtk_button_new();
516 gtk_container_set_border_width(GTK_CONTAINER(pi
->win
), WINK_FRAME
);
517 g_signal_connect(pi
->win
, "expose-event", G_CALLBACK(draw_wink
), pi
);
518 gtk_button_set_relief(GTK_BUTTON(pi
->win
), GTK_RELIEF_NONE
);
520 vbox
= gtk_vbox_new(FALSE
, 0);
521 gtk_container_add(GTK_CONTAINER(pi
->win
), vbox
);
523 align
= gtk_alignment_new(0.5, 0.5, 0, 0);
524 pi
->widget
= gtk_hbox_new(FALSE
, 0); /* Placeholder */
525 gtk_container_add(GTK_CONTAINER(align
), pi
->widget
);
527 gtk_box_pack_start(GTK_BOX(vbox
), align
, FALSE
, TRUE
, 0);
528 drag_set_pinicon_dest(pi
);
529 g_signal_connect(pi
->win
, "drag_data_get",
530 G_CALLBACK(drag_data_get
), NULL
);
532 pi
->label
= wrapped_label_new(icon
->item
->leafname
, 180);
533 gtk_box_pack_start(GTK_BOX(vbox
), pi
->label
, TRUE
, TRUE
, 0);
535 gtk_fixed_put(GTK_FIXED(current_pinboard
->fixed
), pi
->win
, 0, 0);
537 /* find free space for item if position is not given */
542 int placement
= CORNER_TOP_LEFT
;
548 placement
= CORNER_BOTTOM_LEFT
;
554 placement
= CORNER_TOP_RIGHT
;
557 placement
= CORNER_BOTTOM_RIGHT
;
563 pinboard_reshape_icon((Icon
*) pi
);
564 gtk_widget_size_request(pi
->widget
, &req
);
565 rect
.width
= req
.width
;
566 rect
.height
= req
.height
;
567 gtk_widget_size_request(pi
->label
, &req
);
568 rect
.width
= MAX(rect
.width
, req
.width
);
569 rect
.height
+= req
.height
;
570 find_free_rect(current_pinboard
, &rect
, FALSE
, placement
, DIR_VERT
);
571 x
= rect
.x
+ rect
.width
/2;
572 y
= rect
.y
+ rect
.height
/2;
575 gtk_widget_show_all(pi
->win
);
577 events
= GTK_BUTTON(pi
->win
)->event_window
;
578 gdk_window_set_events(events
,
580 GDK_ENTER_NOTIFY_MASK
| GDK_LEAVE_NOTIFY_MASK
|
581 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
582 GDK_BUTTON1_MOTION_MASK
| GDK_BUTTON2_MOTION_MASK
|
583 GDK_BUTTON3_MOTION_MASK
);
584 g_signal_connect(pi
->win
, "enter-notify-event",
585 G_CALLBACK(enter_notify
), pi
);
586 g_signal_connect(pi
->win
, "leave-notify-event",
587 G_CALLBACK(leave_notify
), pi
);
588 g_signal_connect(pi
->win
, "button-press-event",
589 G_CALLBACK(button_press_event
), pi
);
590 g_signal_connect(pi
->win
, "button-release-event",
591 G_CALLBACK(button_release_event
), pi
);
592 g_signal_connect(pi
->win
, "motion-notify-event",
593 G_CALLBACK(icon_motion_notify
), pi
);
594 g_signal_connect(pi
->win
, "expose-event",
595 G_CALLBACK(draw_icon
), pi
);
596 g_signal_connect_swapped(pi
->win
, "style-set",
597 G_CALLBACK(pinboard_reshape_icon
), pi
);
598 g_signal_connect_swapped(pi
->win
, "destroy",
599 G_CALLBACK(pin_icon_destroyed
), pi
);
601 current_pinboard
->icons
= g_list_prepend(current_pinboard
->icons
, pi
);
604 snap_to_grid(&x
, &y
);
609 pinboard_reshape_icon((Icon
*) pi
);
611 gtk_widget_realize(pi
->win
);
613 pin_icon_set_tip(pi
);
615 icon_set_shortcut(icon
, shortcut
);
616 icon_set_arguments(icon
, args
);
617 icon
->locked
= locked
;
619 if (!loading_pinboard
)
623 void pinboard_pin(const gchar
*path
, const gchar
*name
, int x
, int y
,
624 const gchar
*shortcut
)
626 pinboard_pin_with_args(path
, name
, x
, y
, shortcut
, NULL
, FALSE
, FALSE
);
630 * Remove an icon from the background. The first icon with a matching
631 * path is removed. If name is not NULL then that also must match
633 gboolean
pinboard_remove(const gchar
*path
, const gchar
*name
)
639 g_return_val_if_fail(current_pinboard
!= NULL
, FALSE
);
641 for(item
=current_pinboard
->icons
; item
; item
=g_list_next(item
)) {
642 pi
=(PinIcon
*) item
->data
;
645 if(strcmp(icon
->path
, path
)!=0)
648 if(name
&& strcmp(name
, icon
->item
->leafname
)!=0)
651 icon
->locked
= FALSE
;
662 /* Put a border around the icon, briefly.
663 * If icon is NULL then cancel any existing wink.
664 * The icon will automatically unhighlight unless timeout is FALSE,
665 * in which case you must call this function again (with NULL or another
666 * icon) to remove the highlight.
668 static void pinboard_wink_item(PinIcon
*pi
, gboolean timeout
)
670 PinIcon
*old
= current_wink_icon
;
675 current_wink_icon
= pi
;
679 gtk_widget_queue_draw(old
->win
);
680 gdk_window_process_updates(old
->widget
->window
, TRUE
);
682 if (wink_timeout
!= -1)
683 g_source_remove(wink_timeout
);
688 gtk_widget_queue_draw(pi
->win
);
689 gdk_window_process_updates(pi
->widget
->window
, TRUE
);
692 wink_timeout
= g_timeout_add(300, end_wink
, NULL
);
698 /* 'app' is saved as the new application to set the backdrop. It will then be
699 * run, and should communicate with the filer as described in the manual.
701 void pinboard_set_backdrop_app(const gchar
*app
)
707 item
= diritem_new("");
708 diritem_restat(app
, item
, NULL
);
709 if (!(item
->flags
& ITEM_FLAG_APPDIR
))
711 delayed_error(_("The backdrop handler must be an application "
712 "directory. Drag an application directory "
713 "into the Set Backdrop dialog box, or (for "
714 "programmers) pass it to the SOAP "
715 "SetBackdropApp method."));
720 ai
= appinfo_get(app
, item
);
723 can_set
= ai
&& xml_get_section(ai
, ROX_NS
, "CanSetBackdrop") != NULL
;
728 pinboard_set_backdrop(app
, BACKDROP_PROGRAM
);
730 delayed_error(_("You can only set the backdrop to an image "
731 "or to a program which knows how to "
732 "manage ROX-Filer's backdrop.\n\n"
733 "Programmers: the application's AppInfo.xml "
734 "must contain the CanSetBackdrop element, as "
735 "described in ROX-Filer's manual."));
738 /* Open a dialog box allowing the user to set the backdrop */
739 static void pinboard_set_backdrop_box(void)
741 GtkWidget
*dialog
, *frame
, *label
, *hbox
;
745 g_return_if_fail(current_pinboard
!= NULL
);
747 if (set_backdrop_dialog
)
748 gtk_widget_destroy(set_backdrop_dialog
);
750 dialog
= gtk_dialog_new_with_buttons(_("Set backdrop"), NULL
,
751 GTK_DIALOG_NO_SEPARATOR
,
752 GTK_STOCK_CLOSE
, GTK_RESPONSE_OK
,
754 set_backdrop_dialog
= dialog
;
755 g_signal_connect(dialog
, "destroy",
756 G_CALLBACK(gtk_widget_destroyed
), &set_backdrop_dialog
);
757 vbox
= GTK_BOX(GTK_DIALOG(dialog
)->vbox
);
759 gtk_window_set_position(GTK_WINDOW(dialog
), GTK_WIN_POS_MOUSE
);
761 label
= gtk_label_new(_("Choose a style and drag an image in:"));
762 gtk_misc_set_padding(GTK_MISC(label
), 4, 0);
763 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0);
764 gtk_box_pack_start(vbox
, label
, TRUE
, TRUE
, 4);
766 /* The Centred, Scaled, Fit, Tiled radios... */
767 hbox
= gtk_hbox_new(TRUE
, 2);
768 gtk_box_pack_start(vbox
, hbox
, TRUE
, TRUE
, 4);
770 radios
= radios_new(radios_changed
, dialog
);
771 g_object_set_data(G_OBJECT(dialog
), "rox-radios", radios
);
772 g_object_set_data(G_OBJECT(dialog
), "rox-radios-hbox", hbox
);
774 radios_add(radios
, _("Centre the image without scaling it"),
775 BACKDROP_CENTRE
, _("Centre"));
776 radios_add(radios
, _("Scale the image to fit the backdrop area, "
777 "without distorting it"),
778 BACKDROP_SCALE
, _("Scale"));
779 radios_add(radios
, _("Scale the image to fit the backdrop area, "
780 "regardless of image dimensions - overscale"),
781 BACKDROP_FIT
, _("Fit"));
782 radios_add(radios
, _("Stretch the image to fill the backdrop area"),
783 BACKDROP_STRETCH
, _("Stretch"));
784 radios_add(radios
, _("Tile the image over the backdrop area"),
785 BACKDROP_TILE
, _("Tile"));
787 update_radios(dialog
);
789 /* The drop area... */
790 frame
= drop_box_new(_("Drop an image here"));
791 g_object_set_data(G_OBJECT(dialog
), "rox-dropbox", frame
);
793 radios_pack(radios
, GTK_BOX(hbox
));
794 gtk_box_pack_start(vbox
, frame
, TRUE
, TRUE
, 4);
796 drop_box_set_path(DROP_BOX(frame
), current_pinboard
->backdrop
);
798 g_signal_connect(frame
, "path_dropped",
799 G_CALLBACK(drag_backdrop_dropped
), dialog
);
800 g_signal_connect(frame
, "clear",
801 G_CALLBACK(clear_backdrop
), dialog
);
803 g_signal_connect(dialog
, "response",
804 G_CALLBACK(backdrop_response
), NULL
);
805 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_OK
);
807 gtk_widget_show_all(dialog
);
810 /* Also used by tasklist.c */
811 void draw_label_shadow(WrappedLabel
*wl
, GdkRegion
*region
)
816 if (!o_pinboard_shadow_labels
.int_value
)
819 gdk_gc_set_clip_region(current_pinboard
->shadow_gc
, region
);
821 widget
= GTK_WIDGET(wl
);
823 y
= widget
->allocation
.y
- wl
->y_off
;
824 x
= widget
->allocation
.x
- wl
->x_off
-
825 ((wl
->text_width
- widget
->allocation
.width
) >> 1);
827 gdk_draw_layout(widget
->window
, current_pinboard
->shadow_gc
,
828 x
+ 1, y
+ 1, wl
->layout
);
830 if (o_pinboard_shadow_labels
.int_value
> 1)
831 gdk_draw_layout(widget
->window
, current_pinboard
->shadow_gc
,
832 x
+ 2, y
+ 2, wl
->layout
);
834 gdk_gc_set_clip_region(current_pinboard
->shadow_gc
, NULL
);
837 /* Set and save (path, style) as the new backdrop.
838 * If style is BACKDROP_PROGRAM, the program is run to get the backdrop.
839 * Otherwise, the image is displayed now.
841 void pinboard_set_backdrop(const gchar
*path
, BackdropStyle style
)
843 g_return_if_fail((path
== NULL
&& style
== BACKDROP_NONE
) ||
844 (path
!= NULL
&& style
!= BACKDROP_NONE
));
846 if (!current_pinboard
)
850 pinboard_activate("Default");
851 delayed_error(_("No pinboard was in use... "
852 "the 'Default' pinboard has been selected. "
853 "Use 'rox -p=Default' to turn it on in "
855 g_return_if_fail(current_pinboard
!= NULL
);
858 /* We might have just run the old backdrop program and now
859 * we're going to set a new one! Seems a bit mean...
862 abandon_backdrop_app(current_pinboard
);
864 g_free(current_pinboard
->backdrop
);
865 current_pinboard
->backdrop
= g_strdup(path
);
866 current_pinboard
->backdrop_style
= style
;
867 reload_backdrop(current_pinboard
,
868 current_pinboard
->backdrop
,
869 current_pinboard
->backdrop_style
);
873 if (set_backdrop_dialog
)
875 DropBox
*box
= g_object_get_data(G_OBJECT(set_backdrop_dialog
),
877 g_return_if_fail(box
!= NULL
);
878 drop_box_set_path(box
, current_pinboard
->backdrop
);
879 update_radios(set_backdrop_dialog
);
883 /* Called on xrandr screen resizes */
884 void pinboard_update_size(void)
888 if (!current_pinboard
)
891 gtk_window_get_size(GTK_WINDOW(current_pinboard
->window
),
894 /* Only update the pinboard's size if the screen gets bigger,
895 * not smaller. Not sure what to do about icons that end up
896 * offscreen if the screen shrinks, but perhaps a good policy
897 * is to leave them there until the screen size is increased
898 * again rather than mess them around. */
899 width
= MAX(width
, screen_width
);
900 height
= MAX(height
, screen_height
);
902 gtk_widget_set_size_request(current_pinboard
->window
, width
, height
);
905 /****************************************************************
906 * INTERNAL FUNCTIONS *
907 ****************************************************************/
909 static void backdrop_response(GtkWidget
*dialog
, gint response
, gpointer data
)
911 gtk_widget_destroy(dialog
);
914 static void clear_backdrop(GtkWidget
*drop_box
, gpointer data
)
916 pinboard_set_backdrop(NULL
, BACKDROP_NONE
);
919 static void drag_backdrop_dropped(GtkWidget
*drop_box
,
926 radios
= g_object_get_data(G_OBJECT(dialog
), "rox-radios");
927 g_return_if_fail(radios
!= NULL
);
929 if (mc_stat(path
, &info
))
932 _("Can't access '%s':\n%s"), path
,
937 if (S_ISDIR(info
.st_mode
))
939 /* Use this program to set the backdrop */
940 pinboard_set_backdrop_app(path
);
942 else if (S_ISREG(info
.st_mode
))
943 pinboard_set_backdrop(path
, radios_get_value(radios
));
945 delayed_error(_("Only files (and certain applications) can be "
946 "used to set the background image."));
949 /* Do this in the idle loop so that we don't try to put an unmanaged
950 * pinboard behind a managed panel (crashes some WMs).
952 static gboolean
recreate_pinboard(gchar
*name
)
954 pinboard_activate(name
);
960 static void pinboard_check_options(void)
962 GdkColor n_fg
, n_bg
, n_shadow
;
964 gdk_color_parse(o_pinboard_fg_colour
.value
, &n_fg
);
965 gdk_color_parse(o_pinboard_bg_colour
.value
, &n_bg
);
966 gdk_color_parse(o_pinboard_shadow_colour
.value
, &n_shadow
);
968 if (o_override_redirect
.has_changed
&& current_pinboard
)
971 name
= g_strdup(current_pinboard
->name
);
972 pinboard_activate(NULL
);
973 g_idle_add((GtkFunction
) recreate_pinboard
, name
);
976 tasklist_set_active(o_pinboard_tasklist
.int_value
&& current_pinboard
);
978 if (gdk_color_equal(&n_fg
, &pin_text_fg_col
) == 0 ||
979 gdk_color_equal(&n_bg
, &pin_text_bg_col
) == 0 ||
980 gdk_color_equal(&n_shadow
, &pin_text_shadow_col
) == 0 ||
981 o_pinboard_shadow_labels
.has_changed
||
982 o_label_font
.has_changed
)
984 pin_text_fg_col
= n_fg
;
985 pin_text_bg_col
= n_bg
;
986 pin_text_shadow_col
= n_shadow
;
987 update_pinboard_font();
989 if (current_pinboard
)
991 GtkWidget
*w
= current_pinboard
->window
;
994 cm
= gtk_widget_get_colormap(w
);
996 gdk_colormap_alloc_color(cm
, &n_bg
, FALSE
, TRUE
);
997 gtk_widget_modify_bg(w
, GTK_STATE_NORMAL
, &n_bg
);
999 gdk_gc_set_rgb_fg_color(current_pinboard
->shadow_gc
,
1002 abandon_backdrop_app(current_pinboard
);
1003 reload_backdrop(current_pinboard
,
1004 current_pinboard
->backdrop
,
1005 current_pinboard
->backdrop_style
);
1010 tasklist_style_changed();
1014 static gint
end_wink(gpointer data
)
1016 pinboard_wink_item(NULL
, FALSE
);
1020 /* Sets the appearance from the options and updates the size request of
1023 static void set_size_and_style(PinIcon
*pi
)
1025 Icon
*icon
= (Icon
*) pi
;
1026 MaskedPixmap
*image
= di_image(icon
->item
);
1027 int iwidth
= image
->width
;
1028 int iheight
= image
->height
;
1030 gtk_widget_modify_fg(pi
->label
, GTK_STATE_PRELIGHT
, &pin_text_fg_col
);
1031 gtk_widget_modify_bg(pi
->label
, GTK_STATE_PRELIGHT
, &pin_text_bg_col
);
1032 gtk_widget_modify_fg(pi
->label
, GTK_STATE_NORMAL
, &pin_text_fg_col
);
1033 gtk_widget_modify_bg(pi
->label
, GTK_STATE_NORMAL
, &pin_text_bg_col
);
1034 widget_modify_font(pi
->label
, pinboard_font
);
1036 wrapped_label_set_text(WRAPPED_LABEL(pi
->label
), icon
->item
->leafname
);
1038 gtk_widget_set_size_request(pi
->widget
, iwidth
, iheight
);
1041 static GdkPixbuf
*get_stock_icon(GtkWidget
*widget
,
1042 const char *stock_id
)
1044 GtkIconSet
*icon_set
;
1047 icon_set
= gtk_style_lookup_icon_set(widget
->style
,
1051 pixbuf
= gtk_icon_set_render_icon(icon_set
,
1061 pixbuf
=im_unknown
->pixbuf
;
1062 g_object_ref(pixbuf
);
1068 static gint
draw_icon(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
)
1070 static GtkWidgetClass
*parent_class
= NULL
;
1071 Icon
*icon
= (Icon
*) pi
;
1072 DirItem
*item
= icon
->item
;
1073 MaskedPixmap
*image
= di_image(item
);
1074 int iwidth
= image
->width
;
1075 int iheight
= image
->height
;
1081 gpointer c
= ((GTypeInstance
*) widget
)->g_class
;
1082 parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent(c
);
1085 x
= pi
->widget
->allocation
.x
;
1086 y
= pi
->widget
->allocation
.y
;
1088 gdk_gc_set_clip_region(pi
->widget
->style
->black_gc
, event
->region
);
1090 pixbuf
= icon
->selected
1091 ? create_spotlight_pixbuf(image
->pixbuf
,
1092 &pi
->widget
->style
->base
[GTK_STATE_SELECTED
])
1095 render_pixbuf(pixbuf
,
1097 pi
->widget
->style
->black_gc
,
1098 x
, y
, iwidth
, iheight
);
1101 g_object_unref(pixbuf
);
1103 if (item
->flags
& ITEM_FLAG_SYMLINK
)
1105 GdkPixbuf
*emblem
= get_stock_icon(pi
->widget
,
1107 render_pixbuf(emblem
, pi
->widget
->window
,
1108 pi
->widget
->style
->black_gc
,
1110 g_object_unref(emblem
);
1112 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
1114 GdkPixbuf
*emblem
= get_stock_icon(pi
->widget
,
1115 item
->flags
& ITEM_FLAG_MOUNTED
1119 render_pixbuf(emblem
, pi
->widget
->window
,
1120 pi
->widget
->style
->black_gc
,
1122 g_object_unref(emblem
);
1125 gdk_gc_set_clip_region(pi
->widget
->style
->black_gc
, NULL
);
1129 GtkStyle
*style
= pi
->label
->style
;
1130 GdkGC
*gc
= style
->bg_gc
[GTK_STATE_SELECTED
];
1132 gdk_gc_set_clip_region(gc
, event
->region
);
1133 gdk_draw_rectangle(pi
->label
->window
, gc
, TRUE
,
1134 pi
->label
->allocation
.x
,
1135 pi
->label
->allocation
.y
,
1136 pi
->label
->allocation
.width
,
1137 pi
->label
->allocation
.height
);
1138 gdk_gc_set_clip_region(gc
, NULL
);
1140 else if (o_pinboard_shadow_labels
.int_value
)
1141 draw_label_shadow((WrappedLabel
*) pi
->label
, event
->region
);
1144 gdk_gc_set_clip_region(pi
->label
->style
->fg_gc
[GTK_STATE_NORMAL
],
1146 (parent_class
->expose_event
)(widget
, event
);
1147 gdk_gc_set_clip_region(pi
->label
->style
->fg_gc
[GTK_STATE_NORMAL
], NULL
);
1149 /* Stop the button effect */
1153 static gint
draw_wink(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
)
1155 gint x
, y
, width
, height
;
1157 if (current_wink_icon
!= pi
)
1160 x
= widget
->allocation
.x
;
1161 y
= widget
->allocation
.y
;
1162 width
= widget
->allocation
.width
;
1163 height
= widget
->allocation
.height
;
1165 gdk_draw_rectangle(widget
->window
,
1166 pi
->widget
->style
->white_gc
,
1168 x
, y
, width
- 1, height
- 1);
1169 gdk_draw_rectangle(widget
->window
,
1170 pi
->widget
->style
->black_gc
,
1172 x
+ 1, y
+ 1, width
- 3, height
- 3);
1177 static gboolean
enter_notify(GtkWidget
*widget
,
1178 GdkEventCrossing
*event
,
1181 icon_may_update((Icon
*) pi
);
1182 pin_icon_set_tip(pi
);
1186 static gint
leave_notify(GtkWidget
*widget
,
1187 GdkEventCrossing
*event
,
1193 static void select_lasso(void)
1196 int minx
, miny
, maxx
, maxy
;
1198 g_return_if_fail(lasso_in_progress
== TRUE
);
1200 minx
= MIN(lasso_rect_x1
, lasso_rect_x2
);
1201 miny
= MIN(lasso_rect_y1
, lasso_rect_y2
);
1202 maxx
= MAX(lasso_rect_x1
, lasso_rect_x2
);
1203 maxy
= MAX(lasso_rect_y1
, lasso_rect_y2
);
1205 for (next
= current_pinboard
->icons
; next
; next
= next
->next
)
1207 PinIcon
*pi
= (PinIcon
*) next
->data
;
1208 GtkAllocation
*alloc
= &pi
->win
->allocation
;
1209 int cx
= alloc
->x
+ alloc
->width
/ 2;
1210 int cy
= alloc
->y
+ alloc
->height
/ 2;
1212 if (cx
> minx
&& cx
< maxx
&& cy
> miny
&& cy
< maxy
)
1213 icon_set_selected((Icon
*) pi
, TRUE
);
1217 static void cancel_lasso(void)
1220 lasso_in_progress
= FALSE
;
1223 static void pinboard_lasso_box(int start_x
, int start_y
)
1225 if (lasso_in_progress
)
1227 lasso_in_progress
= TRUE
;
1228 lasso_rect_x1
= lasso_rect_x2
= start_x
;
1229 lasso_rect_y1
= lasso_rect_y2
= start_y
;
1234 static gint
lasso_motion(GtkWidget
*widget
, GdkEventMotion
*event
, gpointer d
)
1236 if (!lasso_in_progress
)
1239 if (lasso_rect_x2
!= event
->x
|| lasso_rect_y2
!= event
->y
)
1242 lasso_rect_x2
= event
->x
;
1243 lasso_rect_y2
= event
->y
;
1250 /* Mark the area of the screen covered by the lasso box for redraw */
1251 static void draw_lasso(void)
1253 GdkRectangle area
, edge
;
1255 if (!lasso_in_progress
)
1258 area
.x
= MIN(lasso_rect_x1
, lasso_rect_x2
);
1259 area
.y
= MIN(lasso_rect_y1
, lasso_rect_y2
);
1260 area
.width
= ABS(lasso_rect_x1
- lasso_rect_x2
);
1261 area
.height
= ABS(lasso_rect_y1
- lasso_rect_y2
);
1265 edge
.width
= area
.width
;
1267 edge
.height
= 2; /* Top */
1268 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1271 edge
.y
+= area
.height
- 2; /* Bottom */
1272 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1276 edge
.height
= area
.height
;
1277 edge
.width
= 2; /* Left */
1278 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1281 edge
.x
+= area
.width
- 2; /* Right */
1282 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1286 static void perform_action(PinIcon
*pi
, GdkEventButton
*event
)
1289 Icon
*icon
= (Icon
*) pi
;
1291 action
= bind_lookup_bev(pi
? BIND_PINBOARD_ICON
: BIND_PINBOARD
,
1294 /* Actions that can happen with or without an icon */
1297 case ACT_LASSO_CLEAR
:
1298 icon_select_only(NULL
);
1300 case ACT_LASSO_MODIFY
:
1301 pinboard_lasso_box(event
->x
, event
->y
);
1303 case ACT_CLEAR_SELECTION
:
1304 icon_select_only(NULL
);
1306 case ACT_POPUP_MENU
:
1307 dnd_motion_ungrab();
1308 pinboard_show_menu(event
, pi
);
1316 g_return_if_fail(pi
!= NULL
);
1321 dnd_motion_ungrab();
1322 pinboard_wink_item(pi
, TRUE
);
1323 if (event
->type
== GDK_2BUTTON_PRESS
)
1324 icon_set_selected(icon
, FALSE
);
1328 dnd_motion_ungrab();
1329 pinboard_wink_item(pi
, TRUE
);
1330 if (event
->type
== GDK_2BUTTON_PRESS
)
1331 icon_set_selected(icon
, FALSE
);
1332 run_diritem(icon
->path
, icon
->item
, NULL
, NULL
, TRUE
);
1334 case ACT_PRIME_AND_SELECT
:
1335 if (!icon
->selected
)
1336 icon_select_only(icon
);
1337 dnd_motion_start(MOTION_READY_FOR_DND
);
1339 case ACT_PRIME_AND_TOGGLE
:
1340 icon_set_selected(icon
, !icon
->selected
);
1341 dnd_motion_start(MOTION_READY_FOR_DND
);
1343 case ACT_PRIME_FOR_DND
:
1344 dnd_motion_start(MOTION_READY_FOR_DND
);
1346 case ACT_TOGGLE_SELECTED
:
1347 icon_set_selected(icon
, !icon
->selected
);
1349 case ACT_SELECT_EXCL
:
1350 icon_select_only(icon
);
1353 g_warning("Unsupported action : %d\n", action
);
1358 static void forward_to_root(GdkEventButton
*event
)
1362 if (event
->type
== GDK_BUTTON_PRESS
)
1364 xev
.type
= ButtonPress
;
1365 if (!o_blackbox_hack
.int_value
)
1366 XUngrabPointer(gdk_display
, event
->time
);
1369 xev
.type
= ButtonRelease
;
1371 xev
.window
= gdk_x11_get_default_root_xwindow();
1372 xev
.root
= xev
.window
;
1373 xev
.subwindow
= None
;
1374 xev
.time
= event
->time
;
1375 xev
.x
= event
->x_root
; /* Needed for icewm */
1376 xev
.y
= event
->y_root
;
1377 xev
.x_root
= event
->x_root
;
1378 xev
.y_root
= event
->y_root
;
1379 xev
.state
= event
->state
;
1380 xev
.button
= event
->button
;
1381 xev
.same_screen
= True
;
1383 XSendEvent(gdk_display
, xev
.window
, False
,
1384 ButtonPressMask
| ButtonReleaseMask
, (XEvent
*) &xev
);
1387 #define FORWARDED_BUTTON(pi, b) ((b) == 2 || \
1388 (((b) == 3 || (b) == 1) && o_forward_buttons_13.int_value && !pi))
1390 /* pi is NULL if this is a root event */
1391 static gboolean
button_release_event(GtkWidget
*widget
,
1392 GdkEventButton
*event
,
1395 if (FORWARDED_BUTTON(pi
, event
->button
))
1396 forward_to_root(event
);
1397 else if (dnd_motion_release(event
))
1399 if (motion_buttons_pressed
== 0 && lasso_in_progress
)
1407 perform_action(pi
, event
);
1412 /* pi is NULL if this is a root event */
1413 static gboolean
button_press_event(GtkWidget
*widget
,
1414 GdkEventButton
*event
,
1417 /* Just in case we've jumped in front of everything... */
1418 gdk_window_lower(current_pinboard
->window
->window
);
1420 if (FORWARDED_BUTTON(pi
, event
->button
))
1421 forward_to_root(event
);
1422 else if (dnd_motion_press(widget
, event
))
1423 perform_action(pi
, event
);
1428 /* Forward mouse scroll events as buttons 4 and 5 to the window manager
1429 * (for old window managers that don't catch the buttons themselves)
1431 static gboolean
scroll_event(GtkWidget
*widget
, GdkEventScroll
*event
)
1435 xev
.type
= ButtonPress
;
1436 xev
.window
= gdk_x11_get_default_root_xwindow();
1437 xev
.root
= xev
.window
;
1438 xev
.subwindow
= None
;
1439 xev
.time
= event
->time
;
1440 xev
.x
= event
->x_root
; /* Needed for icewm */
1441 xev
.y
= event
->y_root
;
1442 xev
.x_root
= event
->x_root
;
1443 xev
.y_root
= event
->y_root
;
1444 xev
.state
= event
->state
;
1445 xev
.same_screen
= True
;
1447 if (event
->direction
== GDK_SCROLL_UP
)
1449 else if (event
->direction
== GDK_SCROLL_DOWN
)
1454 XSendEvent(gdk_display
, xev
.window
, False
,
1455 ButtonPressMask
, (XEvent
*) &xev
);
1460 static void start_drag(PinIcon
*pi
, GdkEventMotion
*event
)
1462 GtkWidget
*widget
= pi
->win
;
1463 Icon
*icon
= (Icon
*) pi
;
1465 if (!icon
->selected
)
1467 tmp_icon_selected
= TRUE
;
1468 icon_select_only(icon
);
1471 g_return_if_fail(icon_selection
!= NULL
);
1473 pinboard_drag_in_progress
= icon
;
1475 if (icon_selection
->next
== NULL
)
1476 drag_one_item(widget
, event
, icon
->path
, icon
->item
, NULL
);
1481 uri_list
= icon_create_uri_list();
1482 drag_selection(widget
, event
, uri_list
);
1487 /* An icon is being dragged around... */
1488 static gint
icon_motion_notify(GtkWidget
*widget
,
1489 GdkEventMotion
*event
,
1492 if (motion_state
== MOTION_READY_FOR_DND
)
1494 if (dnd_motion_moved(event
))
1495 start_drag(pi
, event
);
1502 static void backdrop_from_xml(xmlNode
*node
)
1506 g_free(current_pinboard
->backdrop
);
1507 current_pinboard
->backdrop
= xmlNodeGetContent(node
);
1509 style
= xmlGetProp(node
, "style");
1513 current_pinboard
->backdrop_style
=
1514 g_strcasecmp(style
, "Tiled") == 0 ? BACKDROP_TILE
:
1515 g_strcasecmp(style
, "Scaled") == 0 ? BACKDROP_SCALE
:
1516 g_strcasecmp(style
, "Fit") == 0 ? BACKDROP_FIT
:
1517 g_strcasecmp(style
, "Stretched") == 0 ? BACKDROP_STRETCH
:
1518 g_strcasecmp(style
, "Centred") == 0 ? BACKDROP_CENTRE
:
1519 g_strcasecmp(style
, "Program") == 0 ? BACKDROP_PROGRAM
:
1524 current_pinboard
->backdrop_style
= BACKDROP_TILE
;
1527 /* Create one pinboard icon for each icon in the doc */
1528 static void pinboard_load_from_xml(xmlDocPtr doc
)
1530 xmlNodePtr node
, root
;
1531 char *tmp
, *label
, *path
, *shortcut
, *args
;
1535 root
= xmlDocGetRootElement(doc
);
1537 for (node
= root
->xmlChildrenNode
; node
; node
= node
->next
)
1539 if (node
->type
!= XML_ELEMENT_NODE
)
1541 if (strcmp(node
->name
, "backdrop") == 0)
1543 backdrop_from_xml(node
);
1546 if (strcmp(node
->name
, "icon") != 0)
1549 tmp
= xmlGetProp(node
, "x");
1555 tmp
= xmlGetProp(node
, "y");
1561 label
= xmlGetProp(node
, "label");
1563 label
= g_strdup("<missing label>");
1564 path
= xmlNodeGetContent(node
);
1566 path
= g_strdup("<missing path>");
1567 shortcut
= xmlGetProp(node
, "shortcut");
1568 args
= xmlGetProp(node
, "args");
1570 tmp
= xmlGetProp(node
, "locked");
1573 locked
= text_to_boolean(tmp
, FALSE
);
1579 pinboard_pin_with_args(path
, label
, x
, y
, shortcut
, args
, locked
, FALSE
);
1588 /* Called for each line in the pinboard file while loading a new board.
1589 * Only used for old-format files when converting to XML.
1591 static const char *pin_from_file(gchar
*line
)
1600 end
= strchr(line
+ 1, '>');
1602 return _("Missing '>' in icon label");
1604 leaf
= g_strndup(line
+ 1, end
- line
- 1);
1608 while (g_ascii_isspace(*line
))
1611 return _("Missing ',' after icon label");
1615 if (sscanf(line
, " %d , %d , %n", &x
, &y
, &n
) < 2)
1616 return NULL
; /* Ignore format errors */
1618 pinboard_pin(line
+ n
, leaf
, x
, y
, NULL
);
1625 /* Write the current state of the pinboard to the current pinboard file */
1626 static void pinboard_save(void)
1628 guchar
*save
= NULL
;
1629 guchar
*save_new
= NULL
;
1631 xmlDocPtr doc
= NULL
;
1634 g_return_if_fail(current_pinboard
!= NULL
);
1636 if (strchr(current_pinboard
->name
, '/'))
1637 save
= g_strdup(current_pinboard
->name
);
1642 leaf
= g_strconcat("pb_", current_pinboard
->name
, NULL
);
1643 save
= choices_find_xdg_path_save(leaf
, PROJECT
, SITE
, TRUE
);
1650 doc
= xmlNewDoc("1.0");
1651 xmlDocSetRootElement(doc
, xmlNewDocNode(doc
, NULL
, "pinboard", NULL
));
1653 root
= xmlDocGetRootElement(doc
);
1655 if (current_pinboard
->backdrop
)
1657 BackdropStyle style
= current_pinboard
->backdrop_style
;
1660 tree
= xmlNewTextChild(root
, NULL
, "backdrop",
1661 current_pinboard
->backdrop
);
1662 xmlSetProp(tree
, "style",
1663 style
== BACKDROP_TILE
? "Tiled" :
1664 style
== BACKDROP_CENTRE
? "Centred" :
1665 style
== BACKDROP_SCALE
? "Scaled" :
1666 style
== BACKDROP_FIT
? "Fit" :
1667 style
== BACKDROP_STRETCH
? "Stretched" :
1671 for (next
= current_pinboard
->icons
; next
; next
= next
->next
)
1674 PinIcon
*pi
= (PinIcon
*) next
->data
;
1675 Icon
*icon
= (Icon
*) pi
;
1678 tree
= xmlNewTextChild(root
, NULL
, "icon", icon
->src_path
);
1680 tmp
= g_strdup_printf("%d", pi
->x
);
1681 xmlSetProp(tree
, "x", tmp
);
1684 tmp
= g_strdup_printf("%d", pi
->y
);
1685 xmlSetProp(tree
, "y", tmp
);
1688 xmlSetProp(tree
, "label", icon
->item
->leafname
);
1690 xmlSetProp(tree
, "shortcut", icon
->shortcut
);
1692 xmlSetProp(tree
, "args", icon
->args
);
1694 xmlSetProp(tree
, "locked", "true");
1697 save_new
= g_strconcat(save
, ".new", NULL
);
1698 if (save_xml_file(doc
, save_new
) || rename(save_new
, save
))
1699 delayed_error(_("Error saving pinboard %s: %s"),
1700 save
, g_strerror(errno
));
1708 static void snap_to_grid(int *x
, int *y
)
1710 int step
= o_pinboard_grid_step
.int_value
;
1712 *x
= ((*x
+ step
/ 2) / step
) * step
;
1713 *y
= ((*y
+ step
/ 2) / step
) * step
;
1716 /* Convert (x,y) from a centre point to a window position */
1717 static void offset_from_centre(PinIcon
*pi
, int *x
, int *y
)
1719 gboolean clamp
= o_pinboard_clamp_icons
.int_value
;
1722 gtk_widget_size_request(pi
->win
, &req
);
1724 *x
-= req
.width
>> 1;
1725 *y
-= req
.height
>> 1;
1726 *x
= CLAMP(*x
, 0, screen_width
- (clamp
? req
.width
: 0));
1727 *y
= CLAMP(*y
, 0, screen_height
- (clamp
? req
.height
: 0));
1730 /* Same as drag_set_dest(), but for pinboard icons */
1731 static void drag_set_pinicon_dest(PinIcon
*pi
)
1733 GtkObject
*obj
= GTK_OBJECT(pi
->win
);
1735 make_drop_target(pi
->win
, 0);
1737 g_signal_connect(obj
, "drag_motion", G_CALLBACK(drag_motion
), pi
);
1738 g_signal_connect(obj
, "drag_leave", G_CALLBACK(drag_leave
), pi
);
1739 g_signal_connect(obj
, "drag_end", G_CALLBACK(drag_end
), pi
);
1742 /* Called during the drag when the mouse is in a widget registered
1743 * as a drop target. Returns TRUE if we can accept the drop.
1745 static gboolean
drag_motion(GtkWidget
*widget
,
1746 GdkDragContext
*context
,
1752 GdkDragAction action
= context
->suggested_action
;
1753 const char *type
= NULL
;
1754 Icon
*icon
= (Icon
*) pi
;
1755 DirItem
*item
= icon
->item
;
1757 if (gtk_drag_get_source_widget(context
) == widget
)
1759 g_dataset_set_data(context
, "drop_dest_type",
1760 (gpointer
) drop_dest_pass_through
);
1761 return FALSE
; /* Can't drag something to itself! */
1765 goto out
; /* Can't drag a selection to itself */
1767 type
= dnd_motion_item(context
, &item
);
1769 if ((context
->actions
& GDK_ACTION_ASK
) && o_dnd_left_menu
.int_value
1770 && type
!= drop_dest_prog
)
1773 gdk_window_get_pointer(NULL
, NULL
, NULL
, &state
);
1774 if (state
& GDK_BUTTON1_MASK
)
1775 action
= GDK_ACTION_ASK
;
1781 /* We actually must pretend to accept the drop, even if the
1782 * directory isn't writeable, so that the spring-opening
1786 /* Don't allow drops to non-writeable directories */
1787 if (o_dnd_spring_open
.int_value
== FALSE
&&
1788 type
== drop_dest_dir
&&
1789 access(icon
->path
, W_OK
) != 0)
1794 g_dataset_set_data(context
, "drop_dest_type", (gpointer
) type
);
1797 gdk_drag_status(context
, action
, time
);
1798 g_dataset_set_data_full(context
, "drop_dest_path",
1799 g_strdup(icon
->path
), g_free
);
1800 if (type
== drop_dest_dir
)
1801 dnd_spring_load(context
, NULL
);
1803 pinboard_wink_item(pi
, FALSE
);
1806 gdk_drag_status(context
, 0, time
);
1808 /* Always return TRUE to stop the pinboard getting the events */
1812 static gboolean pinboard_shadow
= FALSE
;
1813 static gint shadow_x
, shadow_y
;
1814 #define SHADOW_SIZE (ICON_WIDTH)
1816 static gboolean
bg_expose(GtkWidget
*widget
,
1817 GdkEventExpose
*event
, gpointer data
)
1819 GdkRectangle clipbox
;
1820 gpointer gclass
= ((GTypeInstance
*) widget
)->g_class
;
1821 gboolean double_buffer
;
1823 gdk_gc_set_clip_region(widget
->style
->white_gc
, event
->region
);
1824 gdk_gc_set_clip_region(widget
->style
->black_gc
, event
->region
);
1826 gdk_region_get_clipbox(event
->region
, &clipbox
);
1828 double_buffer
= (clipbox
.width
* clipbox
.height
) < 20000;
1830 gdk_window_begin_paint_region(widget
->window
, event
->region
);
1832 /* Clear the area to the background image */
1834 GtkStyle
*style
= current_pinboard
->window
->style
;
1835 GdkGC
*gc
= style
->bg_gc
[GTK_STATE_NORMAL
];
1837 gdk_gc_set_clip_region(gc
, event
->region
);
1838 if (style
->bg_pixmap
[GTK_STATE_NORMAL
])
1840 gdk_gc_set_ts_origin(gc
, 0, 0);
1841 gdk_gc_set_fill(gc
, GDK_TILED
);
1842 gdk_gc_set_tile(gc
, style
->bg_pixmap
[GTK_STATE_NORMAL
]);
1845 gdk_draw_rectangle(current_pinboard
->window
->window
, gc
, TRUE
,
1846 clipbox
.x
, clipbox
.y
,
1847 clipbox
.width
, clipbox
.height
);
1848 if (style
->bg_pixmap
[GTK_STATE_NORMAL
])
1849 gdk_gc_set_fill(gc
, GDK_SOLID
);
1851 gdk_gc_set_clip_region(gc
, NULL
);
1854 if (pinboard_shadow
)
1856 gdk_draw_rectangle(widget
->window
,
1857 widget
->style
->white_gc
, FALSE
,
1859 SHADOW_SIZE
, SHADOW_SIZE
);
1860 gdk_draw_rectangle(widget
->window
,
1861 widget
->style
->black_gc
, FALSE
,
1862 shadow_x
+ 1, shadow_y
+ 1,
1863 SHADOW_SIZE
- 2, SHADOW_SIZE
- 2);
1866 if (lasso_in_progress
)
1870 area
.x
= MIN(lasso_rect_x1
, lasso_rect_x2
);
1871 area
.y
= MIN(lasso_rect_y1
, lasso_rect_y2
);
1872 area
.width
= ABS(lasso_rect_x1
- lasso_rect_x2
);
1873 area
.height
= ABS(lasso_rect_y1
- lasso_rect_y2
);
1875 if (area
.width
> 4 && area
.height
> 4)
1877 gdk_draw_rectangle(widget
->window
,
1878 widget
->style
->white_gc
, FALSE
,
1880 area
.width
- 1, area
.height
- 1);
1881 gdk_draw_rectangle(widget
->window
,
1882 widget
->style
->black_gc
, FALSE
,
1883 area
.x
+ 1, area
.y
+ 1,
1884 area
.width
- 3, area
.height
- 3);
1888 gdk_gc_set_clip_region(widget
->style
->white_gc
, NULL
);
1889 gdk_gc_set_clip_region(widget
->style
->black_gc
, NULL
);
1891 ((GtkWidgetClass
*) gclass
)->expose_event(widget
, event
);
1894 gdk_window_end_paint(widget
->window
);
1899 /* Draw a 'shadow' under an icon being dragged, showing where
1902 static void pinboard_set_shadow(gboolean on
)
1906 if (pinboard_shadow
)
1910 area
.width
= SHADOW_SIZE
+ 1;
1911 area
.height
= SHADOW_SIZE
+ 1;
1913 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1919 int old_x
= shadow_x
, old_y
= shadow_y
;
1921 gdk_window_get_pointer(current_pinboard
->fixed
->window
,
1922 &shadow_x
, &shadow_y
, NULL
);
1923 snap_to_grid(&shadow_x
, &shadow_y
);
1924 shadow_x
-= SHADOW_SIZE
/ 2;
1925 shadow_y
-= SHADOW_SIZE
/ 2;
1928 if (pinboard_shadow
&& shadow_x
== old_x
&& shadow_y
== old_y
)
1933 area
.width
= SHADOW_SIZE
+ 1;
1934 area
.height
= SHADOW_SIZE
+ 1;
1936 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1940 pinboard_shadow
= on
;
1943 /* Called when dragging some pinboard icons finishes */
1944 void pinboard_move_icons(void)
1946 int x
= shadow_x
, y
= shadow_y
;
1947 PinIcon
*pi
= (PinIcon
*) pinboard_drag_in_progress
;
1952 g_return_if_fail(pi
!= NULL
);
1954 x
+= SHADOW_SIZE
/ 2;
1955 y
+= SHADOW_SIZE
/ 2;
1956 snap_to_grid(&x
, &y
);
1958 if (pi
->x
== x
&& pi
->y
== y
)
1961 /* Find out how much the dragged icon moved (after snapping).
1962 * Move all selected icons by the same amount.
1967 /* Move the other selected icons to keep the same relative
1970 for (next
= icon_selection
; next
; next
= next
->next
)
1972 PinIcon
*pi
= (PinIcon
*) next
->data
;
1975 g_return_if_fail(IS_PIN_ICON(pi
));
1982 gdk_drawable_get_size(pi
->win
->window
, &width
, &height
);
1983 offset_from_centre(pi
, &nx
, &ny
);
1985 fixed_move_fast(GTK_FIXED(current_pinboard
->fixed
),
1992 static void drag_leave(GtkWidget
*widget
,
1993 GdkDragContext
*context
,
1997 pinboard_wink_item(NULL
, FALSE
);
2001 static gboolean
bg_drag_leave(GtkWidget
*widget
,
2002 GdkDragContext
*context
,
2006 pinboard_set_shadow(FALSE
);
2010 static gboolean
bg_drag_motion(GtkWidget
*widget
,
2011 GdkDragContext
*context
,
2017 /* Dragging from the pinboard to the pinboard is not allowed */
2019 if (!provides(context
, text_uri_list
))
2022 pinboard_set_shadow(TRUE
);
2024 gdk_drag_status(context
,
2025 context
->suggested_action
== GDK_ACTION_ASK
2026 ? GDK_ACTION_LINK
: context
->suggested_action
,
2031 static void drag_end(GtkWidget
*widget
,
2032 GdkDragContext
*context
,
2035 pinboard_drag_in_progress
= NULL
;
2036 if (tmp_icon_selected
)
2038 icon_select_only(NULL
);
2039 tmp_icon_selected
= FALSE
;
2043 /* Something which affects all the icons has changed - reshape
2044 * and redraw all of them.
2046 static void reshape_all(void)
2050 g_return_if_fail(current_pinboard
!= NULL
);
2052 for (next
= current_pinboard
->icons
; next
; next
= next
->next
)
2054 Icon
*icon
= (Icon
*) next
->data
;
2055 pinboard_reshape_icon(icon
);
2059 /* Turns off the pinboard. Does not call gtk_main_quit. */
2060 static void pinboard_clear(void)
2064 g_return_if_fail(current_pinboard
!= NULL
);
2066 tasklist_set_active(FALSE
);
2068 next
= current_pinboard
->icons
;
2071 PinIcon
*pi
= (PinIcon
*) next
->data
;
2075 gtk_widget_destroy(pi
->win
);
2078 gtk_widget_destroy(current_pinboard
->window
);
2080 abandon_backdrop_app(current_pinboard
);
2082 g_object_unref(current_pinboard
->shadow_gc
);
2083 current_pinboard
->shadow_gc
= NULL
;
2085 g_free(current_pinboard
->name
);
2086 null_g_free(¤t_pinboard
);
2088 number_of_windows
--;
2091 static gpointer parent_class
;
2093 static void pin_icon_destroy(Icon
*icon
)
2095 PinIcon
*pi
= (PinIcon
*) icon
;
2097 g_return_if_fail(pi
->win
!= NULL
);
2099 gtk_widget_hide(pi
->win
); /* Stops flicker - stupid GtkFixed! */
2100 gtk_widget_destroy(pi
->win
);
2103 static void pinboard_remove_items(void)
2105 g_return_if_fail(icon_selection
!= NULL
);
2107 while (icon_selection
)
2108 icon_destroy((Icon
*) icon_selection
->data
);
2113 static void pin_icon_update(Icon
*icon
)
2115 pinboard_reshape_icon(icon
);
2119 static gboolean
pin_icon_same_group(Icon
*icon
, Icon
*other
)
2121 return IS_PIN_ICON(other
);
2124 static void pin_wink_icon(Icon
*icon
)
2126 pinboard_wink_item((PinIcon
*) icon
, TRUE
);
2129 static void pin_icon_class_init(gpointer gclass
, gpointer data
)
2131 IconClass
*icon
= (IconClass
*) gclass
;
2133 parent_class
= g_type_class_peek_parent(gclass
);
2135 icon
->destroy
= pin_icon_destroy
;
2136 icon
->redraw
= pinboard_reshape_icon
;
2137 icon
->update
= pin_icon_update
;
2138 icon
->wink
= pin_wink_icon
;
2139 icon
->remove_items
= pinboard_remove_items
;
2140 icon
->same_group
= pin_icon_same_group
;
2143 static void pin_icon_init(GTypeInstance
*object
, gpointer gclass
)
2147 static GType
pin_icon_get_type(void)
2149 static GType type
= 0;
2153 static const GTypeInfo info
=
2155 sizeof (PinIconClass
),
2156 NULL
, /* base_init */
2157 NULL
, /* base_finalise */
2158 pin_icon_class_init
,
2159 NULL
, /* class_finalise */
2160 NULL
, /* class_data */
2162 0, /* n_preallocs */
2166 type
= g_type_register_static(icon_get_type(),
2167 "PinIcon", &info
, 0);
2173 static PinIcon
*pin_icon_new(const char *pathname
, const char *name
)
2178 pi
= g_object_new(pin_icon_get_type(), NULL
);
2181 icon_set_path(icon
, pathname
, name
);
2186 /* Called when the window widget is somehow destroyed */
2187 static void pin_icon_destroyed(PinIcon
*pi
)
2189 g_return_if_fail(pi
->win
!= NULL
);
2193 pinboard_wink_item(NULL
, FALSE
);
2195 if (pinboard_drag_in_progress
== (Icon
*) pi
)
2196 pinboard_drag_in_progress
= NULL
;
2198 if (current_pinboard
)
2199 current_pinboard
->icons
=
2200 g_list_remove(current_pinboard
->icons
, pi
);
2205 /* Set the tooltip */
2206 static void pin_icon_set_tip(PinIcon
*pi
)
2210 Icon
*icon
= (Icon
*) pi
;
2212 g_return_if_fail(pi
!= NULL
);
2214 ai
= appinfo_get(icon
->path
, icon
->item
);
2216 if (ai
&& ((node
= xml_get_section(ai
, NULL
, "Summary"))))
2219 str
= xmlNodeListGetString(node
->doc
,
2220 node
->xmlChildrenNode
, 1);
2223 gtk_tooltips_set_tip(tooltips
, pi
->win
, str
, NULL
);
2228 gtk_tooltips_set_tip(tooltips
, pi
->widget
, NULL
, NULL
);
2234 static void pinboard_show_menu(GdkEventButton
*event
, PinIcon
*pi
)
2236 GtkWidget
*option_item
;
2237 GtkWidget
*new_panel_item
;
2241 pos
[0] = event
->x_root
;
2242 pos
[1] = event
->y_root
;
2244 option_item
= gtk_image_menu_item_new_with_label(_("Backdrop..."));
2245 g_signal_connect(option_item
, "activate",
2246 G_CALLBACK(pinboard_set_backdrop_box
), NULL
);
2247 new_panel_item
= gtk_image_menu_item_new_with_label(_("Add Panel"));
2248 add_stock_to_menu_item(new_panel_item
, GTK_STOCK_ADD
);
2249 gtk_menu_item_set_submenu(GTK_MENU_ITEM(new_panel_item
),
2250 panel_new_panel_submenu());
2251 icon_prepare_menu((Icon
*) pi
, option_item
, new_panel_item
, NULL
);
2253 list
= gtk_container_get_children(GTK_CONTAINER(icon_menu
));
2254 pos
[2] = g_list_length(list
) - 6;
2257 gtk_menu_popup(GTK_MENU(icon_menu
), NULL
, NULL
,
2259 (gpointer
) pos
, event
->button
, event
->time
);
2262 static void create_pinboard_window(Pinboard
*pinboard
)
2266 g_return_if_fail(pinboard
->window
== NULL
);
2268 win
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
2269 gtk_widget_set_style(win
, gtk_widget_get_default_style());
2271 gtk_widget_set_double_buffered(win
, FALSE
);
2272 gtk_widget_set_app_paintable(win
, TRUE
);
2273 gtk_widget_set_name(win
, "rox-pinboard");
2274 pinboard
->window
= win
;
2275 pinboard
->fixed
= gtk_fixed_new();
2276 gtk_container_add(GTK_CONTAINER(win
), pinboard
->fixed
);
2278 gtk_window_set_wmclass(GTK_WINDOW(win
), "ROX-Pinboard", PROJECT
);
2280 gtk_widget_set_size_request(win
, screen_width
, screen_height
);
2281 gtk_widget_realize(win
);
2282 gtk_window_move(GTK_WINDOW(win
), 0, 0);
2283 make_panel_window(win
);
2285 /* TODO: Use gdk function when it supports this type */
2287 GdkAtom desktop_type
;
2289 desktop_type
= gdk_atom_intern("_NET_WM_WINDOW_TYPE_DESKTOP",
2291 gdk_property_change(win
->window
,
2292 gdk_atom_intern("_NET_WM_WINDOW_TYPE", FALSE
),
2293 gdk_atom_intern("ATOM", FALSE
), 32,
2294 GDK_PROP_MODE_REPLACE
, (guchar
*) &desktop_type
, 1);
2297 gtk_widget_add_events(win
,
2298 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
2300 GDK_BUTTON1_MOTION_MASK
|
2301 GDK_BUTTON2_MOTION_MASK
| GDK_BUTTON3_MOTION_MASK
);
2302 g_signal_connect(win
, "button-press-event",
2303 G_CALLBACK(button_press_event
), NULL
);
2304 g_signal_connect(win
, "button-release-event",
2305 G_CALLBACK(button_release_event
), NULL
);
2306 g_signal_connect(win
, "motion-notify-event",
2307 G_CALLBACK(lasso_motion
), NULL
);
2308 g_signal_connect(pinboard
->fixed
, "expose_event",
2309 G_CALLBACK(bg_expose
), NULL
);
2311 /* Some window managers use scroll events on the root to switch
2312 * desktops, but don't cope with our pinboard window, so we forward
2313 * them manually in that case.
2315 g_signal_connect(win
, "scroll-event", G_CALLBACK(scroll_event
), NULL
);
2317 /* Drag and drop handlers */
2318 drag_set_pinboard_dest(win
);
2319 g_signal_connect(win
, "drag_motion", G_CALLBACK(bg_drag_motion
), NULL
);
2320 g_signal_connect(win
, "drag_leave", G_CALLBACK(bg_drag_leave
), NULL
);
2322 pinboard
->shadow_gc
= gdk_gc_new(win
->window
);
2323 gdk_gc_set_rgb_fg_color(pinboard
->shadow_gc
, &pin_text_shadow_col
);
2325 reload_backdrop(current_pinboard
, NULL
, BACKDROP_NONE
);
2327 gtk_widget_show_all(win
);
2328 gdk_window_lower(win
->window
);
2331 /* Load image 'path' and scale according to 'style' */
2332 static GdkPixmap
*load_backdrop(const gchar
*path
, BackdropStyle style
)
2336 GError
*error
= NULL
;
2338 pixbuf
= gdk_pixbuf_new_from_file(path
, &error
);
2341 delayed_error(_("Error loading backdrop image:\n%s\n"
2342 "Backdrop removed."),
2344 g_error_free(error
);
2345 pinboard_set_backdrop(NULL
, BACKDROP_NONE
);
2349 if (style
== BACKDROP_STRETCH
)
2351 GdkPixbuf
*old
= pixbuf
;
2353 pixbuf
= gdk_pixbuf_scale_simple(old
,
2354 screen_width
, screen_height
,
2357 g_object_unref(old
);
2359 else if (style
== BACKDROP_CENTRE
|| style
== BACKDROP_SCALE
|| style
== BACKDROP_FIT
)
2361 GdkPixbuf
*old
= pixbuf
;
2362 int x
, y
, width
, height
;
2365 width
= gdk_pixbuf_get_width(pixbuf
);
2366 height
= gdk_pixbuf_get_height(pixbuf
);
2368 if (style
== BACKDROP_SCALE
)
2370 float scale_x
, scale_y
;
2371 scale_x
= screen_width
/ ((float) width
);
2372 scale_y
= screen_height
/ ((float) height
);
2373 scale
= MIN(scale_x
, scale_y
);
2375 else if (style
== BACKDROP_FIT
)
2377 float scale_x
, scale_y
;
2378 scale_x
= screen_width
/ ((float) width
);
2379 scale_y
= screen_height
/ ((float) height
);
2380 scale
= MAX(scale_x
, scale_y
);
2385 pixbuf
= gdk_pixbuf_new(
2386 gdk_pixbuf_get_colorspace(pixbuf
), FALSE
,
2387 8, screen_width
, screen_height
);
2388 gdk_pixbuf_fill(pixbuf
, ((pin_text_bg_col
.red
& 0xff00) << 16) |
2389 ((pin_text_bg_col
.green
& 0xff00) << 8) |
2390 ((pin_text_bg_col
.blue
& 0xff00)));
2392 x
= (screen_width
- width
* scale
) / 2;
2393 y
= (screen_height
- height
* scale
) / 2;
2397 gdk_pixbuf_composite(old
, pixbuf
,
2399 MIN(screen_width
, width
* scale
),
2400 MIN(screen_height
, height
* scale
),
2402 o_pinboard_image_scaling
.int_value
?
2403 GDK_INTERP_BILINEAR
: GDK_INTERP_HYPER
,
2405 g_object_unref(old
);
2408 gdk_pixbuf_render_pixmap_and_mask(pixbuf
,
2410 g_object_unref(pixbuf
);
2415 static void abandon_backdrop_app(Pinboard
*pinboard
)
2417 g_return_if_fail(pinboard
!= NULL
);
2419 if (pinboard
->to_backdrop_app
!= -1)
2421 close(pinboard
->to_backdrop_app
);
2422 close(pinboard
->from_backdrop_app
);
2423 g_source_remove(pinboard
->input_tag
);
2424 g_string_free(pinboard
->input_buffer
, TRUE
);
2425 pinboard
->to_backdrop_app
= -1;
2426 pinboard
->from_backdrop_app
= -1;
2427 pinboard
->input_tag
= -1;
2428 pinboard
->input_buffer
= NULL
;
2431 g_return_if_fail(pinboard
->to_backdrop_app
== -1);
2432 g_return_if_fail(pinboard
->from_backdrop_app
== -1);
2433 g_return_if_fail(pinboard
->input_tag
== -1);
2434 g_return_if_fail(pinboard
->input_buffer
== NULL
);
2437 /* A single line has been read from the child.
2438 * Processes the command, and replies 'ok' (or abandons the child on error).
2440 static void command_from_backdrop_app(Pinboard
*pinboard
, const gchar
*command
)
2442 BackdropStyle style
;
2443 const char *ok
= "ok\n";
2445 if (strncmp(command
, "tile ", 5) == 0)
2447 style
= BACKDROP_TILE
;
2450 else if (strncmp(command
, "scale ", 6) == 0)
2452 style
= BACKDROP_SCALE
;
2455 else if (strncmp(command
, "fit ", 4) == 0)
2457 style
= BACKDROP_FIT
;
2460 else if (strncmp(command
, "stretch ", 8) == 0)
2462 style
= BACKDROP_STRETCH
;
2465 else if (strncmp(command
, "centre ", 7) == 0)
2467 style
= BACKDROP_CENTRE
;
2472 g_warning("Invalid command '%s' from backdrop app\n",
2474 abandon_backdrop_app(pinboard
);
2478 /* Load the backdrop. May abandon the program if loading fails. */
2479 reload_backdrop(pinboard
, command
, style
);
2481 if (pinboard
->to_backdrop_app
== -1)
2488 sent
= write(pinboard
->to_backdrop_app
, ok
, strlen(ok
));
2491 /* Remote app quit? Not an error. */
2492 abandon_backdrop_app(pinboard
);
2499 static void backdrop_from_child(Pinboard
*pinboard
,
2500 int src
, GdkInputCondition cond
)
2505 got
= read(src
, buf
, sizeof(buf
));
2510 g_warning("backdrop_from_child: %s\n",
2512 abandon_backdrop_app(pinboard
);
2516 g_string_append_len(pinboard
->input_buffer
, buf
, got
);
2518 while (pinboard
->from_backdrop_app
!= -1)
2523 nl
= strchr(pinboard
->input_buffer
->str
, '\n');
2525 return; /* Haven't got a whole line yet */
2527 len
= nl
- pinboard
->input_buffer
->str
;
2528 command
= g_strndup(pinboard
->input_buffer
->str
, len
);
2529 g_string_erase(pinboard
->input_buffer
, 0, len
+ 1);
2531 command_from_backdrop_app(pinboard
, command
);
2537 static void reload_backdrop(Pinboard
*pinboard
,
2538 const gchar
*backdrop
,
2539 BackdropStyle backdrop_style
)
2543 if (backdrop
&& backdrop_style
== BACKDROP_PROGRAM
)
2545 const char *argv
[] = {NULL
, "--backdrop", NULL
};
2546 GError
*error
= NULL
;
2548 g_return_if_fail(pinboard
->to_backdrop_app
== -1);
2549 g_return_if_fail(pinboard
->from_backdrop_app
== -1);
2550 g_return_if_fail(pinboard
->input_tag
== -1);
2551 g_return_if_fail(pinboard
->input_buffer
== NULL
);
2553 argv
[0] = make_path(backdrop
, "AppRun");
2555 /* Run the program. It'll send us a SOAP message and we'll
2556 * get back here with a different style and image.
2559 if (g_spawn_async_with_pipes(NULL
, (gchar
**) argv
, NULL
,
2560 G_SPAWN_DO_NOT_REAP_CHILD
|
2561 G_SPAWN_SEARCH_PATH
,
2562 NULL
, NULL
, /* Child setup fn */
2563 NULL
, /* Child PID */
2564 &pinboard
->to_backdrop_app
,
2565 &pinboard
->from_backdrop_app
,
2566 NULL
, /* Standard error */
2569 pinboard
->input_buffer
= g_string_new(NULL
);
2570 pinboard
->input_tag
= gdk_input_add_full(
2571 pinboard
->from_backdrop_app
,
2573 (GdkInputFunction
) backdrop_from_child
,
2578 delayed_error("%s", error
? error
->message
: "(null)");
2579 g_error_free(error
);
2584 /* Note: Copying a style does not ref the pixmaps! */
2586 style
= gtk_style_copy(gtk_widget_get_style(pinboard
->window
));
2587 style
->bg_pixmap
[GTK_STATE_NORMAL
] = NULL
;
2590 style
->bg_pixmap
[GTK_STATE_NORMAL
] =
2591 load_backdrop(backdrop
, backdrop_style
);
2593 gdk_color_parse(o_pinboard_bg_colour
.value
,
2594 &style
->bg
[GTK_STATE_NORMAL
]);
2596 gtk_widget_set_style(pinboard
->window
, style
);
2598 g_object_unref(style
);
2600 gtk_widget_queue_draw(pinboard
->window
);
2602 /* Also update root window property (for transparent xterms, etc) */
2603 if (style
->bg_pixmap
[GTK_STATE_NORMAL
])
2605 XID id
= GDK_DRAWABLE_XID(style
->bg_pixmap
[GTK_STATE_NORMAL
]);
2606 gdk_property_change(gdk_get_default_root_window(),
2607 gdk_atom_intern("_XROOTPMAP_ID", FALSE
),
2608 gdk_atom_intern("PIXMAP", FALSE
),
2609 32, GDK_PROP_MODE_REPLACE
,
2614 gdk_property_delete(gdk_get_default_root_window(),
2615 gdk_atom_intern("_XROOTPMAP_ID", FALSE
));
2619 #define SEARCH_STEP 32
2621 /* Search the area (omin, imin) to (omax, imax) for a free region the size of
2622 * 'rect' that doesn't overlap 'used'. Which of inner and outer is the
2623 * vertical axis depends on the configuration.
2625 * id and od give the direction of the search (step size).
2627 * Returns the start of the found region in inner/outer, or -1 if there is no
2630 static void search_free(GdkRectangle
*rect
, GdkRegion
*used
,
2631 int *outer
, int od
, int omin
, int omax
,
2632 int *inner
, int id
, int imin
, int imax
)
2634 *outer
= od
> 0 ? omin
: omax
;
2635 while (*outer
>= omin
&& *outer
<= omax
)
2637 *inner
= id
> 0 ? imin
: imax
;
2638 while (*inner
>= imin
&& *inner
<= imax
)
2640 if (gdk_region_rect_in(used
, rect
) ==
2641 GDK_OVERLAP_RECTANGLE_OUT
)
2653 /* Search the width x height area from (x0, y0) for a free region of size
2654 * 'rect'. direction indicates whether to search rows or columns. dx, dy gives
2655 * the direction of the search.
2657 static void search_free_area(GdkRectangle
*rect
, GdkRegion
*used
,
2658 int direction
, int dx
, int dy
, int x0
, int y0
, int width
, int height
)
2660 if (direction
== DIR_VERT
)
2662 search_free(rect
, used
,
2663 &rect
->x
, dx
, x0
, width
,
2664 &rect
->y
, dy
, y0
, height
);
2668 search_free(rect
, used
,
2669 &rect
->y
, dy
, x0
, height
,
2670 &rect
->x
, dx
, y0
, width
);
2674 static gboolean
search_free_xinerama(GdkRectangle
*rect
, GdkRegion
*used
,
2675 int direction
, int dx
, int dy
, int rwidth
, int rheight
)
2677 GdkRectangle
*geom
= &monitor_geom
[get_monitor_under_pointer()];
2679 search_free_area(rect
, used
, direction
, dx
, dy
,
2680 geom
->x
, geom
->y
, geom
->width
- rwidth
, geom
->height
- rheight
);
2681 return rect
->x
!= -1;
2684 /* Finds a free area on the pinboard large enough for the width and height
2685 * of the given rectangle, by filling in the x and y fields of 'rect'.
2686 * If 'old' is true, 'rect' has a previous position and we first check
2688 * The search order respects user preferences.
2689 * If no area is free, returns any old area.
2691 static void find_free_rect(Pinboard
*pinboard
, GdkRectangle
*rect
,
2692 gboolean old
, int start
, int direction
)
2696 GdkRectangle used_rect
;
2697 int dx
= SEARCH_STEP
, dy
= SEARCH_STEP
;
2699 used
= gdk_region_new();
2701 panel_mark_used(used
);
2703 /* Subtract the no-go areas... */
2705 if (o_top_margin
.int_value
> 0)
2709 used_rect
.width
= gdk_screen_width();
2710 used_rect
.height
= o_top_margin
.int_value
;
2711 gdk_region_union_with_rect(used
, &used_rect
);
2714 if (o_bottom_margin
.int_value
> 0)
2717 used_rect
.y
= gdk_screen_height() - o_bottom_margin
.int_value
;
2718 used_rect
.width
= gdk_screen_width();
2719 used_rect
.height
= o_bottom_margin
.int_value
;
2720 gdk_region_union_with_rect(used
, &used_rect
);
2723 if (o_left_margin
.int_value
> 0)
2727 used_rect
.width
= o_left_margin
.int_value
;
2728 used_rect
.height
= gdk_screen_height();
2729 gdk_region_union_with_rect(used
, &used_rect
);
2732 if (o_right_margin
.int_value
> 0)
2734 used_rect
.x
= gdk_screen_width() - o_right_margin
.int_value
;
2736 used_rect
.width
= o_right_margin
.int_value
;
2737 used_rect
.height
= gdk_screen_height();
2738 gdk_region_union_with_rect(used
, &used_rect
);
2741 /* Subtract the used areas... */
2743 next
= GTK_FIXED(pinboard
->fixed
)->children
;
2744 for (; next
; next
= next
->next
)
2746 GtkFixedChild
*fix
= (GtkFixedChild
*) next
->data
;
2748 if (!GTK_WIDGET_VISIBLE(fix
->widget
))
2751 used_rect
.x
= fix
->x
;
2752 used_rect
.y
= fix
->y
;
2753 used_rect
.width
= fix
->widget
->requisition
.width
;
2754 used_rect
.height
= fix
->widget
->requisition
.height
;
2756 gdk_region_union_with_rect(used
, &used_rect
);
2759 /* Check the previous area */
2761 if(gdk_region_rect_in(used
, rect
)==GDK_OVERLAP_RECTANGLE_OUT
) {
2762 gdk_region_destroy(used
);
2767 /* Find the first free area (yes, this isn't exactly pretty, but
2768 * it works). If you know a better (fast!) algorithm, let me know!
2771 if (start
== CORNER_TOP_RIGHT
||
2772 start
== CORNER_BOTTOM_RIGHT
)
2775 if (start
== CORNER_BOTTOM_LEFT
||
2776 start
== CORNER_BOTTOM_RIGHT
)
2779 /* If pinboard covers more than one monitor, try to find free space on
2780 * monitor under pointer first, then whole screen if that fails */
2781 if (n_monitors
== 1 || !search_free_xinerama(rect
, used
,
2782 direction
, dx
, dy
, rect
->width
, rect
->height
))
2784 search_free_area(rect
, used
, direction
, dx
, dy
,
2785 0, 0, screen_width
- rect
->width
, screen_height
- rect
->height
);
2788 gdk_region_destroy(used
);
2797 /* Icon's size, shape or appearance has changed - update the display */
2798 static void pinboard_reshape_icon(Icon
*icon
)
2800 PinIcon
*pi
= (PinIcon
*) icon
;
2801 int x
= pi
->x
, y
= pi
->y
;
2803 set_size_and_style(pi
);
2804 offset_from_centre(pi
, &x
, &y
);
2806 if (pi
->win
->allocation
.x
!= x
|| pi
->win
->allocation
.y
!= y
)
2808 fixed_move_fast(GTK_FIXED(current_pinboard
->fixed
),
2812 /* Newer versions of GTK seem to need this, or the icon doesn't
2815 gtk_widget_queue_draw(pi
->win
);
2818 /* Sets the pinboard_font global from the option. Doesn't do anything else. */
2819 static void update_pinboard_font(void)
2822 pango_font_description_free(pinboard_font
);
2823 pinboard_font
= o_label_font
.value
[0] != '\0'
2824 ? pango_font_description_from_string(o_label_font
.value
)
2828 static void radios_changed(Radios
*radios
, gpointer data
)
2830 GObject
*dialog
= G_OBJECT(data
);
2834 g_return_if_fail(dialog
!= NULL
);
2836 drop_box
= g_object_get_data(G_OBJECT(dialog
), "rox-dropbox");
2838 g_return_if_fail(radios
!= NULL
);
2839 g_return_if_fail(drop_box
!= NULL
);
2840 g_return_if_fail(current_pinboard
!= NULL
);
2842 if (current_pinboard
->backdrop_style
!= BACKDROP_PROGRAM
)
2844 path
= drop_box_get_path(drop_box
);
2846 pinboard_set_backdrop(path
, radios_get_value(radios
));
2850 static void update_radios(GtkWidget
*dialog
)
2855 g_return_if_fail(dialog
!= NULL
);
2857 radios
= g_object_get_data(G_OBJECT(dialog
), "rox-radios");
2858 hbox
= g_object_get_data(G_OBJECT(dialog
), "rox-radios-hbox");
2860 g_return_if_fail(current_pinboard
!= NULL
);
2861 g_return_if_fail(radios
!= NULL
);
2862 g_return_if_fail(hbox
!= NULL
);
2864 switch (current_pinboard
->backdrop_style
)
2867 case BACKDROP_STRETCH
:
2868 case BACKDROP_SCALE
:
2869 case BACKDROP_CENTRE
:
2871 radios_set_value(radios
,
2872 current_pinboard
->backdrop_style
);
2873 gtk_widget_set_sensitive(hbox
, TRUE
);
2876 gtk_widget_set_sensitive(hbox
, FALSE
);
2877 radios_set_value(radios
, BACKDROP_TILE
);