4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2003, 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 */
34 #include <libxml/parser.h>
46 #include "gui_support.h"
56 #include "panel.h" /* For panel_mark_used() */
59 static gboolean tmp_icon_selected
= FALSE
; /* When dragging */
62 guchar
*name
; /* Leaf name */
66 gchar
*backdrop
; /* Pathname */
67 BackdropStyle backdrop_style
;
68 gint to_backdrop_app
; /* pipe FD, or -1 */
69 gint from_backdrop_app
; /* pipe FD, or -1 */
71 GString
*input_buffer
;
73 GtkWidget
*window
; /* Screen-sized window */
78 #define IS_PIN_ICON(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), pin_icon_get_type())
80 typedef struct _PinIconClass PinIconClass
;
81 typedef struct _PinIcon PinIcon
;
83 struct _PinIconClass
{
92 GtkWidget
*widget
; /* The drawing area for the icon */
96 /* The number of pixels between the bottom of the image and the top
101 /* The size of the border around the icon which is used when winking */
105 #define GRID_STEP_FINE 2
106 #define GRID_STEP_MED 16
107 #define GRID_STEP_COARSE 32
109 /* Used in options */
110 #define CORNER_TOP_LEFT 0
111 #define CORNER_TOP_RIGHT 1
112 #define CORNER_BOTTOM_LEFT 2
113 #define CORNER_BOTTOM_RIGHT 3
118 static PinIcon
*current_wink_icon
= NULL
;
119 static gint wink_timeout
;
121 /* Used for the text colours (only) in the icons */
122 static GdkColor pin_text_fg_col
, pin_text_bg_col
;
123 PangoFontDescription
*pinboard_font
= NULL
; /* NULL => Gtk default */
125 static GdkColor pin_text_shadow_col
;
127 Pinboard
*current_pinboard
= NULL
;
128 static gint loading_pinboard
= 0; /* Non-zero => loading */
130 /* The Icon that was used to start the current drag, if any */
131 Icon
*pinboard_drag_in_progress
= NULL
;
133 /* For selecting groups of icons */
134 static gboolean lasso_in_progress
= FALSE
;
135 static int lasso_rect_x1
, lasso_rect_x2
;
136 static int lasso_rect_y1
, lasso_rect_y2
;
138 static Option o_pinboard_clamp_icons
, o_pinboard_grid_step
;
139 static Option o_pinboard_fg_colour
, o_pinboard_bg_colour
;
140 static Option o_pinboard_tasklist
, o_forward_buttons_13
;
141 static Option o_iconify_start
, o_iconify_dir
;
142 static Option o_label_font
, o_pinboard_shadow_colour
;
143 static Option o_pinboard_shadow_labels
;
145 /* Static prototypes */
146 static GType
pin_icon_get_type(void);
147 static void set_size_and_style(PinIcon
*pi
);
148 static gint
draw_icon(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
);
149 static void draw_label_shadow(WrappedLabel
*label
);
150 static gint
end_wink(gpointer data
);
151 static gboolean
button_release_event(GtkWidget
*widget
,
152 GdkEventButton
*event
,
154 static gboolean
enter_notify(GtkWidget
*widget
,
155 GdkEventCrossing
*event
,
157 static gboolean
button_press_event(GtkWidget
*widget
,
158 GdkEventButton
*event
,
160 static gint
icon_motion_notify(GtkWidget
*widget
,
161 GdkEventMotion
*event
,
163 static const char *pin_from_file(gchar
*line
);
164 static void snap_to_grid(int *x
, int *y
);
165 static void offset_from_centre(PinIcon
*pi
, int *x
, int *y
);
166 static gboolean
drag_motion(GtkWidget
*widget
,
167 GdkDragContext
*context
,
172 static void drag_set_pinicon_dest(PinIcon
*pi
);
173 static void drag_leave(GtkWidget
*widget
,
174 GdkDragContext
*context
,
177 static gboolean
bg_drag_motion(GtkWidget
*widget
,
178 GdkDragContext
*context
,
183 static gboolean
bg_drag_leave(GtkWidget
*widget
,
184 GdkDragContext
*context
,
187 static gboolean
bg_expose(GtkWidget
*window
,
188 GdkEventExpose
*event
, gpointer data
);
189 static void drag_end(GtkWidget
*widget
,
190 GdkDragContext
*context
,
192 static void reshape_all(void);
193 static void pinboard_check_options(void);
194 static void pinboard_load_from_xml(xmlDocPtr doc
);
195 static void pinboard_clear(void);
196 static void pinboard_save(void);
197 static PinIcon
*pin_icon_new(const char *pathname
, const char *name
);
198 static void pin_icon_destroyed(PinIcon
*pi
);
199 static void pin_icon_set_tip(PinIcon
*pi
);
200 static void pinboard_show_menu(GdkEventButton
*event
, PinIcon
*pi
);
201 static void create_pinboard_window(Pinboard
*pinboard
);
202 static void reload_backdrop(Pinboard
*pinboard
,
203 const gchar
*backdrop
,
204 BackdropStyle backdrop_style
);
205 static void set_backdrop(const gchar
*path
, BackdropStyle style
);
206 static void pinboard_reshape_icon(Icon
*icon
);
207 static gint
draw_wink(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
);
208 static void abandon_backdrop_app(Pinboard
*pinboard
);
209 static void drag_backdrop_dropped(GtkWidget
*frame
,
210 GdkDragContext
*context
,
213 GtkSelectionData
*selection_data
,
217 static void backdrop_response(GtkWidget
*dialog
, gint response
, gpointer data
);
218 static void find_free_rect(Pinboard
*pinboard
, GdkRectangle
*rect
);
219 static void update_pinboard_font(void);
220 static void draw_lasso(void);
221 static gint
lasso_motion(GtkWidget
*widget
, GdkEventMotion
*event
, gpointer d
);
224 /****************************************************************
225 * EXTERNAL INTERFACE *
226 ****************************************************************/
228 void pinboard_init(void)
230 option_add_string(&o_pinboard_fg_colour
, "pinboard_fg_colour", "#fff");
231 option_add_string(&o_pinboard_bg_colour
, "pinboard_bg_colour", "#888");
232 option_add_string(&o_pinboard_shadow_colour
, "pinboard_shadow_colour",
234 option_add_string(&o_label_font
, "label_font", "");
235 option_add_int(&o_pinboard_shadow_labels
, "pinboard_shadow_labels", 1);
237 option_add_int(&o_pinboard_clamp_icons
, "pinboard_clamp_icons", 1);
238 option_add_int(&o_pinboard_grid_step
, "pinboard_grid_step",
240 option_add_int(&o_pinboard_tasklist
, "pinboard_tasklist", TRUE
);
241 option_add_int(&o_forward_buttons_13
, "pinboard_forward_buttons_13",
244 option_add_int(&o_iconify_start
, "iconify_start", CORNER_TOP_RIGHT
);
245 option_add_int(&o_iconify_dir
, "iconify_dir", DIR_VERT
);
247 option_add_notify(pinboard_check_options
);
249 gdk_color_parse(o_pinboard_fg_colour
.value
, &pin_text_fg_col
);
250 gdk_color_parse(o_pinboard_bg_colour
.value
, &pin_text_bg_col
);
251 gdk_color_parse(o_pinboard_shadow_colour
.value
, &pin_text_shadow_col
);
252 update_pinboard_font();
255 /* Load 'pb_<pinboard>' config file from Choices (if it exists)
256 * and make it the current pinboard.
257 * Any existing pinned items are removed. You must call this
258 * at least once before using the pinboard. NULL disables the
261 void pinboard_activate(const gchar
*name
)
263 Pinboard
*old_board
= current_pinboard
;
264 guchar
*path
, *slash
;
266 /* Treat an empty name the same as NULL */
275 if (number_of_windows
< 1 && gtk_main_level() > 0)
278 gdk_property_delete(gdk_get_default_root_window(),
279 gdk_atom_intern("_XROOTPMAP_ID", FALSE
));
285 slash
= strchr(name
, '/');
288 if (access(name
, F_OK
))
289 path
= NULL
; /* File does not (yet) exist */
291 path
= g_strdup(name
);
297 leaf
= g_strconcat("pb_", name
, NULL
);
298 path
= choices_find_path_load(leaf
, PROJECT
);
302 current_pinboard
= g_new(Pinboard
, 1);
303 current_pinboard
->name
= g_strdup(name
);
304 current_pinboard
->icons
= NULL
;
305 current_pinboard
->window
= NULL
;
306 current_pinboard
->backdrop
= NULL
;
307 current_pinboard
->backdrop_style
= BACKDROP_NONE
;
308 current_pinboard
->to_backdrop_app
= -1;
309 current_pinboard
->from_backdrop_app
= -1;
310 current_pinboard
->input_tag
= -1;
311 current_pinboard
->input_buffer
= NULL
;
313 create_pinboard_window(current_pinboard
);
319 doc
= xmlParseFile(path
);
322 pinboard_load_from_xml(doc
);
324 reload_backdrop(current_pinboard
,
325 current_pinboard
->backdrop
,
326 current_pinboard
->backdrop_style
);
330 parse_file(path
, pin_from_file
);
331 info_message(_("Your old pinboard file has been "
332 "converted to the new XML format."));
338 pinboard_pin(home_dir
, "Home",
344 if (o_pinboard_tasklist
.int_value
)
345 tasklist_set_active(TRUE
);
348 /* Return the window of the current pinboard, or NULL.
349 * Used to make sure lowering the panels doesn't lose them...
351 GdkWindow
*pinboard_get_window(void)
353 if (current_pinboard
)
354 return current_pinboard
->window
->window
;
358 const char *pinboard_get_name(void)
360 g_return_val_if_fail(current_pinboard
!= NULL
, NULL
);
362 return current_pinboard
->name
;
365 /* Add widget to the pinboard. Caller is responsible for coping with pinboard
368 void pinboard_add_widget(GtkWidget
*widget
)
373 g_return_if_fail(current_pinboard
!= NULL
);
375 gtk_fixed_put(GTK_FIXED(current_pinboard
->fixed
), widget
, 0, 0);
377 gtk_widget_size_request(widget
, &req
);
379 rect
.width
= req
.width
;
380 rect
.height
= req
.height
;
381 find_free_rect(current_pinboard
, &rect
);
383 gtk_fixed_move(GTK_FIXED(current_pinboard
->fixed
),
384 widget
, rect
.x
, rect
.y
);
387 /* Add a new icon to the background.
388 * 'path' should be an absolute pathname.
389 * 'x' and 'y' are the coordinates of the point in the middle of the text.
390 * 'name' is the name to use. If NULL then the leafname of path is used.
392 * name and path are in UTF-8 for Gtk+-2.0 only.
394 void pinboard_pin(const gchar
*path
, const gchar
*name
, int x
, int y
,
395 const gchar
*shortcut
)
397 GtkWidget
*align
, *vbox
;
402 g_return_if_fail(path
!= NULL
);
403 g_return_if_fail(current_pinboard
!= NULL
);
405 pi
= pin_icon_new(path
, name
);
410 /* This is a bit complicated...
412 * An icon needs to be a NO_WINDOW widget so that the image can
413 * blend with the background (A ParentRelative window also works, but
414 * is slow, causes the xfree86's memory consumption to grow without
415 * bound, and doesn't even get freed when the filer quits!).
417 * However, the icon also needs to have a window, so we get events
418 * delivered correctly. The solution is to float an InputOnly window
419 * over the icon. Since GtkButton works the same way, we just use
423 /* Button takes the initial ref of Icon */
424 pi
->win
= gtk_button_new();
425 gtk_container_set_border_width(GTK_CONTAINER(pi
->win
), WINK_FRAME
);
426 g_signal_connect(pi
->win
, "expose-event", G_CALLBACK(draw_wink
), pi
);
427 gtk_button_set_relief(GTK_BUTTON(pi
->win
), GTK_RELIEF_NONE
);
429 vbox
= gtk_vbox_new(FALSE
, 0);
430 gtk_container_add(GTK_CONTAINER(pi
->win
), vbox
);
432 align
= gtk_alignment_new(0.5, 0.5, 0, 0);
433 pi
->widget
= gtk_hbox_new(FALSE
, 0); /* Placeholder */
434 gtk_container_add(GTK_CONTAINER(align
), pi
->widget
);
436 gtk_box_pack_start(GTK_BOX(vbox
), align
, FALSE
, TRUE
, 0);
437 drag_set_pinicon_dest(pi
);
438 g_signal_connect(pi
->win
, "drag_data_get",
439 G_CALLBACK(drag_data_get
), NULL
);
441 pi
->label
= wrapped_label_new(icon
->item
->leafname
, 180);
442 gtk_box_pack_start(GTK_BOX(vbox
), pi
->label
, TRUE
, TRUE
, 0);
444 gtk_fixed_put(GTK_FIXED(current_pinboard
->fixed
), pi
->win
, 0, 0);
446 snap_to_grid(&x
, &y
);
449 gtk_widget_show_all(pi
->win
);
450 pinboard_reshape_icon((Icon
*) pi
);
452 gtk_widget_realize(pi
->win
);
453 events
= GTK_BUTTON(pi
->win
)->event_window
;
454 gdk_window_set_events(events
,
456 GDK_ENTER_NOTIFY_MASK
| GDK_LEAVE_NOTIFY_MASK
|
457 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
458 GDK_BUTTON1_MOTION_MASK
| GDK_ENTER_NOTIFY_MASK
|
459 GDK_BUTTON2_MOTION_MASK
| GDK_BUTTON3_MOTION_MASK
);
460 g_signal_connect(pi
->win
, "enter-notify-event",
461 G_CALLBACK(enter_notify
), pi
);
462 g_signal_connect(pi
->win
, "button-press-event",
463 G_CALLBACK(button_press_event
), pi
);
464 g_signal_connect(pi
->win
, "button-release-event",
465 G_CALLBACK(button_release_event
), pi
);
466 g_signal_connect(pi
->win
, "motion-notify-event",
467 G_CALLBACK(icon_motion_notify
), pi
);
468 g_signal_connect(pi
->win
, "expose-event",
469 G_CALLBACK(draw_icon
), pi
);
470 g_signal_connect_swapped(pi
->win
, "style-set",
471 G_CALLBACK(pinboard_reshape_icon
), pi
);
472 g_signal_connect_swapped(pi
->win
, "destroy",
473 G_CALLBACK(pin_icon_destroyed
), pi
);
475 current_pinboard
->icons
= g_list_prepend(current_pinboard
->icons
, pi
);
476 pin_icon_set_tip(pi
);
478 icon_set_shortcut(icon
, shortcut
);
480 if (!loading_pinboard
)
484 /* Put a border around the icon, briefly.
485 * If icon is NULL then cancel any existing wink.
486 * The icon will automatically unhighlight unless timeout is FALSE,
487 * in which case you must call this function again (with NULL or another
488 * icon) to remove the highlight.
490 static void pinboard_wink_item(PinIcon
*pi
, gboolean timeout
)
492 PinIcon
*old
= current_wink_icon
;
497 current_wink_icon
= pi
;
501 gtk_widget_queue_draw(old
->win
);
502 gdk_window_process_updates(old
->widget
->window
, TRUE
);
504 if (wink_timeout
!= -1)
505 gtk_timeout_remove(wink_timeout
);
510 gtk_widget_queue_draw(pi
->win
);
511 gdk_window_process_updates(pi
->widget
->window
, TRUE
);
514 wink_timeout
= gtk_timeout_add(300, end_wink
, NULL
);
520 /* 'app' is saved as the new application to set the backdrop. It will then be
521 * run, and should communicate with the filer as described in the manual.
523 void pinboard_set_backdrop_app(const gchar
*app
)
529 item
= diritem_new("");
530 diritem_restat(app
, item
, NULL
);
531 if (!(item
->flags
& ITEM_FLAG_APPDIR
))
533 delayed_error(_("The backdrop handler must be an application "
534 "directory. Drag an application directory "
535 "into the Set Backdrop dialog box, or (for "
536 "programmers) pass it to the SOAP "
537 "SetBackdropApp method."));
542 ai
= appinfo_get(app
, item
);
545 can_set
= ai
&& xml_get_section(ai
, ROX_NS
, "CanSetBackdrop") != NULL
;
550 set_backdrop(app
, BACKDROP_PROGRAM
);
552 delayed_error(_("You can only set the backdrop to an image "
553 "or to a program which knows how to "
554 "manage ROX-Filer's backdrop.\n\n"
555 "Programmers: the application's AppInfo.xml "
556 "must contain the CanSetBackdrop element, as "
557 "described in ROX-Filer's manual."));
560 /* Open a dialog box allowing the user to set the backdrop */
561 void pinboard_set_backdrop(void)
563 GtkWidget
*dialog
, *frame
, *label
, *radio
, *hbox
;
565 GtkTargetEntry targets
[] = {
566 {"text/uri-list", 0, TARGET_URI_LIST
},
569 dialog
= gtk_dialog_new_with_buttons(_("Set backdrop"), NULL
,
570 GTK_DIALOG_NO_SEPARATOR
,
571 GTK_STOCK_CLEAR
, GTK_RESPONSE_NO
,
572 GTK_STOCK_OK
, GTK_RESPONSE_OK
,
574 vbox
= GTK_BOX(GTK_DIALOG(dialog
)->vbox
);
576 gtk_window_set_position(GTK_WINDOW(dialog
), GTK_WIN_POS_MOUSE
);
578 label
= gtk_label_new(_("Display backdrop image:"));
579 gtk_misc_set_padding(GTK_MISC(label
), 4, 0);
580 gtk_misc_set_alignment(GTK_MISC(label
), 0, 0);
581 gtk_box_pack_start(vbox
, label
, TRUE
, TRUE
, 4);
583 /* The Centred, Scaled, Tiled radios... */
584 hbox
= gtk_hbox_new(TRUE
, 2);
585 gtk_box_pack_start(vbox
, hbox
, TRUE
, TRUE
, 4);
587 radio
= gtk_radio_button_new_with_label(NULL
, _("Centred"));
588 g_object_set_data(G_OBJECT(dialog
), "radio_centred", radio
);
589 gtk_box_pack_start(GTK_BOX(hbox
), radio
, FALSE
, TRUE
, 0);
591 radio
= gtk_radio_button_new_with_label(
592 gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio
)),
594 g_object_set_data(G_OBJECT(dialog
), "radio_scaled", radio
);
595 gtk_box_pack_start(GTK_BOX(hbox
), radio
, FALSE
, TRUE
, 0);
597 radio
= gtk_radio_button_new_with_label(
598 gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio
)),
600 gtk_box_pack_start(GTK_BOX(hbox
), radio
, FALSE
, TRUE
, 0);
602 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio
), TRUE
);
604 /* The drop area... */
605 frame
= gtk_frame_new(NULL
);
606 gtk_box_pack_start(vbox
, frame
, TRUE
, TRUE
, 4);
607 gtk_frame_set_shadow_type(GTK_FRAME(frame
), GTK_SHADOW_IN
);
608 gtk_container_set_border_width(GTK_CONTAINER(frame
), 4);
610 gtk_drag_dest_set(frame
, GTK_DEST_DEFAULT_ALL
,
611 targets
, sizeof(targets
) / sizeof(*targets
),
613 g_signal_connect(frame
, "drag_data_received",
614 G_CALLBACK(drag_backdrop_dropped
), dialog
);
616 label
= gtk_label_new(_("Drop an image here"));
617 gtk_misc_set_padding(GTK_MISC(label
), 10, 20);
618 gtk_container_add(GTK_CONTAINER(frame
), label
);
620 g_signal_connect(dialog
, "response",
621 G_CALLBACK(backdrop_response
), NULL
);
622 gtk_dialog_set_default_response(GTK_DIALOG(dialog
), GTK_RESPONSE_OK
);
624 gtk_widget_show_all(dialog
);
627 /****************************************************************
628 * INTERNAL FUNCTIONS *
629 ****************************************************************/
631 static void backdrop_response(GtkWidget
*dialog
, gint response
, gpointer data
)
633 if (response
== GTK_RESPONSE_NO
)
634 set_backdrop(NULL
, BACKDROP_NONE
);
636 gtk_widget_destroy(dialog
);
639 static void drag_backdrop_dropped(GtkWidget
*frame
,
640 GdkDragContext
*context
,
643 GtkSelectionData
*selection_data
,
649 const gchar
*path
= NULL
;
652 if (!selection_data
->data
)
653 return; /* Timeout? */
655 uris
= uri_list_to_glist(selection_data
->data
);
657 if (g_list_length(uris
) == 1)
658 path
= get_local_path((guchar
*) uris
->data
);
664 _("You should drop a single (local) image file "
665 "onto the drop box - that image will be "
666 "used for the desktop background. You can also "
667 "drag certain applications onto this box."));
671 if (mc_stat(path
, &info
))
674 _("Can't access '%s':\n%s"), path
,
679 if (S_ISDIR(info
.st_mode
))
681 /* Use this program to set the backdrop */
682 pinboard_set_backdrop_app(path
);
684 else if (S_ISREG(info
.st_mode
))
688 radio
= g_object_get_data(G_OBJECT(dialog
), "radio_scaled");
689 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio
)))
691 set_backdrop(path
, BACKDROP_SCALE
);
695 radio
= g_object_get_data(G_OBJECT(dialog
), "radio_centred");
696 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio
)))
698 set_backdrop(path
, BACKDROP_CENTRE
);
702 set_backdrop(path
, BACKDROP_TILE
);
705 delayed_error(_("Only files (and certain applications) can be "
706 "used to set the background image."));
709 /* Do this in the idle loop so that we don't try to put an unmanaged
710 * pinboard behind a managed panel (crashes some WMs).
712 static gboolean
recreate_pinboard(gchar
*name
)
714 pinboard_activate(name
);
720 static void pinboard_check_options(void)
722 GdkColor n_fg
, n_bg
, n_shadow
;
724 gdk_color_parse(o_pinboard_fg_colour
.value
, &n_fg
);
725 gdk_color_parse(o_pinboard_bg_colour
.value
, &n_bg
);
726 gdk_color_parse(o_pinboard_shadow_colour
.value
, &n_shadow
);
728 if (o_override_redirect
.has_changed
&& current_pinboard
)
731 name
= g_strdup(current_pinboard
->name
);
732 pinboard_activate(NULL
);
733 gtk_idle_add((GtkFunction
) recreate_pinboard
, name
);
736 tasklist_set_active(o_pinboard_tasklist
.int_value
&& current_pinboard
);
738 if (gdk_color_equal(&n_fg
, &pin_text_fg_col
) == 0 ||
739 gdk_color_equal(&n_bg
, &pin_text_bg_col
) == 0 ||
740 gdk_color_equal(&n_shadow
, &pin_text_shadow_col
) == 0 ||
741 o_pinboard_shadow_labels
.has_changed
||
742 o_label_font
.has_changed
)
744 pin_text_fg_col
= n_fg
;
745 pin_text_bg_col
= n_bg
;
746 pin_text_shadow_col
= n_shadow
;
747 update_pinboard_font();
749 if (current_pinboard
)
751 GtkWidget
*w
= current_pinboard
->window
;
754 cm
= gtk_widget_get_colormap(w
);
756 gdk_colormap_alloc_color(cm
, &n_bg
, FALSE
, TRUE
);
757 gtk_widget_modify_bg(w
, GTK_STATE_NORMAL
, &n_bg
);
759 gdk_gc_set_rgb_fg_color(current_pinboard
->shadow_gc
,
762 /* Only redraw the background if there is no image */
763 if (!current_pinboard
->backdrop
)
764 reload_backdrop(current_pinboard
,
765 NULL
, BACKDROP_NONE
);
770 tasklist_style_changed();
774 static gint
end_wink(gpointer data
)
776 pinboard_wink_item(NULL
, FALSE
);
780 /* Sets the appearance from the options and updates the size request of
783 static void set_size_and_style(PinIcon
*pi
)
785 Icon
*icon
= (Icon
*) pi
;
786 MaskedPixmap
*image
= icon
->item
->image
;
787 int iwidth
= image
->width
;
788 int iheight
= image
->height
;
790 gtk_widget_modify_fg(pi
->label
, GTK_STATE_PRELIGHT
, &pin_text_fg_col
);
791 gtk_widget_modify_bg(pi
->label
, GTK_STATE_PRELIGHT
, &pin_text_bg_col
);
792 gtk_widget_modify_fg(pi
->label
, GTK_STATE_NORMAL
, &pin_text_fg_col
);
793 gtk_widget_modify_bg(pi
->label
, GTK_STATE_NORMAL
, &pin_text_bg_col
);
794 widget_modify_font(pi
->label
, pinboard_font
);
796 wrapped_label_set_text(WRAPPED_LABEL(pi
->label
), icon
->item
->leafname
);
798 gtk_widget_set_size_request(pi
->widget
, iwidth
, iheight
);
801 static gint
draw_icon(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
)
803 static GtkWidgetClass
*parent_class
= NULL
;
804 Icon
*icon
= (Icon
*) pi
;
805 DirItem
*item
= icon
->item
;
806 MaskedPixmap
*image
= item
->image
;
807 int iwidth
= image
->width
;
808 int iheight
= image
->height
;
813 gpointer c
= ((GTypeInstance
*) widget
)->g_class
;
814 parent_class
= (GtkWidgetClass
*) g_type_class_peek_parent(c
);
817 x
= pi
->widget
->allocation
.x
;
818 y
= pi
->widget
->allocation
.y
;
820 gdk_pixbuf_render_to_drawable_alpha(
821 icon
->selected
? image
->pixbuf_lit
: image
->pixbuf
,
826 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
827 GDK_RGB_DITHER_NORMAL
, 0, 0);
829 if (item
->flags
& ITEM_FLAG_SYMLINK
)
831 gdk_pixbuf_render_to_drawable_alpha(im_symlink
->pixbuf
,
836 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
837 GDK_RGB_DITHER_NORMAL
, 0, 0);
839 else if (item
->flags
& ITEM_FLAG_MOUNT_POINT
)
841 MaskedPixmap
*mp
= item
->flags
& ITEM_FLAG_MOUNTED
845 gdk_pixbuf_render_to_drawable_alpha(mp
->pixbuf
,
850 GDK_PIXBUF_ALPHA_FULL
, 128, /* (unused) */
851 GDK_RGB_DITHER_NORMAL
, 0, 0);
856 GtkStyle
*style
= pi
->label
->style
;
857 GdkGC
*gc
= style
->bg_gc
[GTK_STATE_SELECTED
];
859 gdk_gc_set_clip_region(gc
, event
->region
);
860 gdk_draw_rectangle(pi
->label
->window
, gc
, TRUE
,
861 pi
->label
->allocation
.x
,
862 pi
->label
->allocation
.y
,
863 pi
->label
->allocation
.width
,
864 pi
->label
->allocation
.height
);
865 gdk_gc_set_clip_region(gc
, NULL
);
867 else if (o_pinboard_shadow_labels
.int_value
)
869 gdk_gc_set_clip_region(current_pinboard
->shadow_gc
,
871 draw_label_shadow((WrappedLabel
*) pi
->label
);
872 gdk_gc_set_clip_region(current_pinboard
->shadow_gc
, NULL
);
876 gdk_gc_set_clip_region(pi
->label
->style
->fg_gc
[GTK_STATE_NORMAL
],
878 (parent_class
->expose_event
)(widget
, event
);
879 gdk_gc_set_clip_region(pi
->label
->style
->fg_gc
[GTK_STATE_NORMAL
], NULL
);
881 /* Stop the button effect */
885 static void draw_label_shadow(WrappedLabel
*wl
)
890 widget
= GTK_WIDGET(wl
);
892 y
= widget
->allocation
.y
- wl
->y_off
;
893 x
= widget
->allocation
.x
- wl
->x_off
-
894 ((wl
->text_width
- widget
->allocation
.width
) >> 1);
896 gdk_draw_layout(widget
->window
, current_pinboard
->shadow_gc
,
897 x
+ 1, y
+ 1, wl
->layout
);
899 if (o_pinboard_shadow_labels
.int_value
> 1)
900 gdk_draw_layout(widget
->window
, current_pinboard
->shadow_gc
,
901 x
+ 2, y
+ 2, wl
->layout
);
904 static gint
draw_wink(GtkWidget
*widget
, GdkEventExpose
*event
, PinIcon
*pi
)
906 gint x
, y
, width
, height
;
908 if (current_wink_icon
!= pi
)
911 x
= widget
->allocation
.x
;
912 y
= widget
->allocation
.y
;
913 width
= widget
->allocation
.width
;
914 height
= widget
->allocation
.height
;
916 gdk_draw_rectangle(widget
->window
,
917 pi
->widget
->style
->white_gc
,
919 x
, y
, width
- 1, height
- 1);
920 gdk_draw_rectangle(widget
->window
,
921 pi
->widget
->style
->black_gc
,
923 x
+ 1, y
+ 1, width
- 3, height
- 3);
928 static gboolean
enter_notify(GtkWidget
*widget
,
929 GdkEventCrossing
*event
,
932 icon_may_update((Icon
*) pi
);
937 static void select_lasso(void)
940 int minx
, miny
, maxx
, maxy
;
942 g_return_if_fail(lasso_in_progress
== TRUE
);
944 minx
= MIN(lasso_rect_x1
, lasso_rect_x2
);
945 miny
= MIN(lasso_rect_y1
, lasso_rect_y2
);
946 maxx
= MAX(lasso_rect_x1
, lasso_rect_x2
);
947 maxy
= MAX(lasso_rect_y1
, lasso_rect_y2
);
949 for (next
= current_pinboard
->icons
; next
; next
= next
->next
)
951 PinIcon
*pi
= (PinIcon
*) next
->data
;
952 GtkAllocation
*alloc
= &pi
->win
->allocation
;
953 int cx
= alloc
->x
+ alloc
->width
/ 2;
954 int cy
= alloc
->y
+ alloc
->height
/ 2;
956 if (cx
> minx
&& cx
< maxx
&& cy
> miny
&& cy
< maxy
)
957 icon_set_selected((Icon
*) pi
, TRUE
);
961 static void cancel_lasso(void)
964 lasso_in_progress
= FALSE
;
967 static void pinboard_lasso_box(int start_x
, int start_y
)
969 if (lasso_in_progress
)
971 lasso_in_progress
= TRUE
;
972 lasso_rect_x1
= lasso_rect_x2
= start_x
;
973 lasso_rect_y1
= lasso_rect_y2
= start_y
;
978 static gint
lasso_motion(GtkWidget
*widget
, GdkEventMotion
*event
, gpointer d
)
980 if (!lasso_in_progress
)
983 if (lasso_rect_x2
!= event
->x
|| lasso_rect_y2
!= event
->y
)
986 lasso_rect_x2
= event
->x
;
987 lasso_rect_y2
= event
->y
;
994 /* Mark the area of the screen covered by the lasso box for redraw */
995 static void draw_lasso(void)
997 GdkRectangle area
, edge
;
999 if (!lasso_in_progress
)
1002 area
.x
= MIN(lasso_rect_x1
, lasso_rect_x2
);
1003 area
.y
= MIN(lasso_rect_y1
, lasso_rect_y2
);
1004 area
.width
= ABS(lasso_rect_x1
- lasso_rect_x2
);
1005 area
.height
= ABS(lasso_rect_y1
- lasso_rect_y2
);
1009 edge
.width
= area
.width
;
1011 edge
.height
= 2; /* Top */
1012 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1015 edge
.y
+= area
.height
- 2; /* Bottom */
1016 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1020 edge
.height
= area
.height
;
1021 edge
.width
= 2; /* Left */
1022 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1025 edge
.x
+= area
.width
- 2; /* Right */
1026 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1030 static void perform_action(PinIcon
*pi
, GdkEventButton
*event
)
1033 Icon
*icon
= (Icon
*) pi
;
1035 action
= bind_lookup_bev(pi
? BIND_PINBOARD_ICON
: BIND_PINBOARD
,
1038 /* Actions that can happen with or without an icon */
1041 case ACT_LASSO_CLEAR
:
1042 icon_select_only(NULL
);
1044 case ACT_LASSO_MODIFY
:
1045 pinboard_lasso_box(event
->x
, event
->y
);
1047 case ACT_CLEAR_SELECTION
:
1048 icon_select_only(NULL
);
1050 case ACT_POPUP_MENU
:
1051 dnd_motion_ungrab();
1052 pinboard_show_menu(event
, pi
);
1060 g_return_if_fail(pi
!= NULL
);
1065 dnd_motion_ungrab();
1066 pinboard_wink_item(pi
, TRUE
);
1067 if (event
->type
== GDK_2BUTTON_PRESS
)
1068 icon_set_selected(icon
, FALSE
);
1069 run_diritem(icon
->path
, icon
->item
, NULL
, NULL
, FALSE
);
1072 dnd_motion_ungrab();
1073 pinboard_wink_item(pi
, TRUE
);
1074 if (event
->type
== GDK_2BUTTON_PRESS
)
1075 icon_set_selected(icon
, FALSE
);
1076 run_diritem(icon
->path
, icon
->item
, NULL
, NULL
, TRUE
);
1078 case ACT_PRIME_AND_SELECT
:
1079 if (!icon
->selected
)
1080 icon_select_only(icon
);
1081 dnd_motion_start(MOTION_READY_FOR_DND
);
1083 case ACT_PRIME_AND_TOGGLE
:
1084 icon_set_selected(icon
, !icon
->selected
);
1085 dnd_motion_start(MOTION_READY_FOR_DND
);
1087 case ACT_PRIME_FOR_DND
:
1088 dnd_motion_start(MOTION_READY_FOR_DND
);
1090 case ACT_TOGGLE_SELECTED
:
1091 icon_set_selected(icon
, !icon
->selected
);
1093 case ACT_SELECT_EXCL
:
1094 icon_select_only(icon
);
1097 g_warning("Unsupported action : %d\n", action
);
1102 static void forward_to_root(GdkEventButton
*event
)
1106 if (event
->type
== GDK_BUTTON_PRESS
)
1108 xev
.type
= ButtonPress
;
1109 XUngrabPointer(gdk_display
, event
->time
);
1112 xev
.type
= ButtonRelease
;
1114 xev
.window
= gdk_x11_get_default_root_xwindow();
1115 xev
.root
= xev
.window
;
1116 xev
.subwindow
= None
;
1117 xev
.time
= event
->time
;
1118 xev
.x
= event
->x_root
; /* Needed for icewm */
1119 xev
.y
= event
->y_root
;
1120 xev
.x_root
= event
->x_root
;
1121 xev
.y_root
= event
->y_root
;
1122 xev
.state
= event
->state
;
1123 xev
.button
= event
->button
;
1124 xev
.same_screen
= True
;
1126 XSendEvent(gdk_display
, xev
.window
, False
,
1127 ButtonPressMask
| ButtonReleaseMask
, (XEvent
*) &xev
);
1130 #define FORWARDED_BUTTON(pi, b) ((b) == 2 || \
1131 (((b) == 3 || (b) == 1) && o_forward_buttons_13.int_value && !pi))
1133 /* pi is NULL if this is a root event */
1134 static gboolean
button_release_event(GtkWidget
*widget
,
1135 GdkEventButton
*event
,
1138 if (FORWARDED_BUTTON(pi
, event
->button
))
1139 forward_to_root(event
);
1140 else if (dnd_motion_release(event
))
1142 if (motion_buttons_pressed
== 0 && lasso_in_progress
)
1150 perform_action(pi
, event
);
1155 /* pi is NULL if this is a root event */
1156 static gboolean
button_press_event(GtkWidget
*widget
,
1157 GdkEventButton
*event
,
1160 /* Just in case we've jumped in front of everything... */
1161 gdk_window_lower(current_pinboard
->window
->window
);
1163 if (FORWARDED_BUTTON(pi
, event
->button
))
1164 forward_to_root(event
);
1165 else if (dnd_motion_press(widget
, event
))
1166 perform_action(pi
, event
);
1171 static void start_drag(PinIcon
*pi
, GdkEventMotion
*event
)
1173 GtkWidget
*widget
= pi
->win
;
1174 Icon
*icon
= (Icon
*) pi
;
1176 if (!icon
->selected
)
1178 tmp_icon_selected
= TRUE
;
1179 icon_select_only(icon
);
1182 g_return_if_fail(icon_selection
!= NULL
);
1184 pinboard_drag_in_progress
= icon
;
1186 if (icon_selection
->next
== NULL
)
1187 drag_one_item(widget
, event
, icon
->path
, icon
->item
, NULL
);
1192 uri_list
= icon_create_uri_list();
1193 drag_selection(widget
, event
, uri_list
);
1198 /* An icon is being dragged around... */
1199 static gint
icon_motion_notify(GtkWidget
*widget
,
1200 GdkEventMotion
*event
,
1203 if (motion_state
== MOTION_READY_FOR_DND
)
1205 if (dnd_motion_moved(event
))
1206 start_drag(pi
, event
);
1213 static void backdrop_from_xml(xmlNode
*node
)
1217 g_free(current_pinboard
->backdrop
);
1218 current_pinboard
->backdrop
= xmlNodeGetContent(node
);
1220 style
= xmlGetProp(node
, "style");
1224 current_pinboard
->backdrop_style
=
1225 g_strcasecmp(style
, "Tiled") == 0 ? BACKDROP_TILE
:
1226 g_strcasecmp(style
, "Scaled") == 0 ? BACKDROP_SCALE
:
1227 g_strcasecmp(style
, "Centred") == 0 ? BACKDROP_CENTRE
:
1228 g_strcasecmp(style
, "Program") == 0 ? BACKDROP_PROGRAM
:
1233 current_pinboard
->backdrop_style
= BACKDROP_TILE
;
1236 /* Create one pinboard icon for each icon in the doc */
1237 static void pinboard_load_from_xml(xmlDocPtr doc
)
1239 xmlNodePtr node
, root
;
1240 char *tmp
, *label
, *path
, *shortcut
;
1243 root
= xmlDocGetRootElement(doc
);
1245 for (node
= root
->xmlChildrenNode
; node
; node
= node
->next
)
1247 if (node
->type
!= XML_ELEMENT_NODE
)
1249 if (strcmp(node
->name
, "backdrop") == 0)
1251 backdrop_from_xml(node
);
1254 if (strcmp(node
->name
, "icon") != 0)
1257 tmp
= xmlGetProp(node
, "x");
1263 tmp
= xmlGetProp(node
, "y");
1269 label
= xmlGetProp(node
, "label");
1271 label
= g_strdup("<missing label>");
1272 path
= xmlNodeGetContent(node
);
1274 path
= g_strdup("<missing path>");
1275 shortcut
= xmlGetProp(node
, "shortcut");
1277 pinboard_pin(path
, label
, x
, y
, shortcut
);
1285 /* Called for each line in the pinboard file while loading a new board.
1286 * Only used for old-format files when converting to XML.
1288 static const char *pin_from_file(gchar
*line
)
1297 end
= strchr(line
+ 1, '>');
1299 return _("Missing '>' in icon label");
1301 leaf
= g_strndup(line
+ 1, end
- line
- 1);
1305 while (isspace(*line
))
1308 return _("Missing ',' after icon label");
1312 if (sscanf(line
, " %d , %d , %n", &x
, &y
, &n
) < 2)
1313 return NULL
; /* Ignore format errors */
1315 pinboard_pin(line
+ n
, leaf
, x
, y
, NULL
);
1322 /* Write the current state of the pinboard to the current pinboard file */
1323 static void pinboard_save(void)
1325 guchar
*save
= NULL
;
1326 guchar
*save_new
= NULL
;
1328 xmlDocPtr doc
= NULL
;
1331 g_return_if_fail(current_pinboard
!= NULL
);
1333 if (strchr(current_pinboard
->name
, '/'))
1334 save
= g_strdup(current_pinboard
->name
);
1339 leaf
= g_strconcat("pb_", current_pinboard
->name
, NULL
);
1340 save
= choices_find_path_save(leaf
, PROJECT
, TRUE
);
1347 doc
= xmlNewDoc("1.0");
1348 xmlDocSetRootElement(doc
, xmlNewDocNode(doc
, NULL
, "pinboard", NULL
));
1350 root
= xmlDocGetRootElement(doc
);
1352 if (current_pinboard
->backdrop
)
1354 BackdropStyle style
= current_pinboard
->backdrop_style
;
1357 tree
= xmlNewTextChild(root
, NULL
, "backdrop",
1358 current_pinboard
->backdrop
);
1359 xmlSetProp(tree
, "style",
1360 style
== BACKDROP_TILE
? "Tiled" :
1361 style
== BACKDROP_CENTRE
? "Centred" :
1362 style
== BACKDROP_SCALE
? "Scaled" :
1366 for (next
= current_pinboard
->icons
; next
; next
= next
->next
)
1369 PinIcon
*pi
= (PinIcon
*) next
->data
;
1370 Icon
*icon
= (Icon
*) pi
;
1373 tree
= xmlNewTextChild(root
, NULL
, "icon", icon
->src_path
);
1375 tmp
= g_strdup_printf("%d", pi
->x
);
1376 xmlSetProp(tree
, "x", tmp
);
1379 tmp
= g_strdup_printf("%d", pi
->y
);
1380 xmlSetProp(tree
, "y", tmp
);
1383 xmlSetProp(tree
, "label", icon
->item
->leafname
);
1385 xmlSetProp(tree
, "shortcut", icon
->shortcut
);
1388 save_new
= g_strconcat(save
, ".new", NULL
);
1389 if (save_xml_file(doc
, save_new
) || rename(save_new
, save
))
1390 delayed_error(_("Error saving pinboard %s: %s"),
1391 save
, g_strerror(errno
));
1399 static void snap_to_grid(int *x
, int *y
)
1401 int step
= o_pinboard_grid_step
.int_value
;
1403 *x
= ((*x
+ step
/ 2) / step
) * step
;
1404 *y
= ((*y
+ step
/ 2) / step
) * step
;
1407 /* Convert (x,y) from a centre point to a window position */
1408 static void offset_from_centre(PinIcon
*pi
, int *x
, int *y
)
1410 gboolean clamp
= o_pinboard_clamp_icons
.int_value
;
1413 gtk_widget_size_request(pi
->win
, &req
);
1415 *x
-= req
.width
>> 1;
1416 *y
-= req
.height
>> 1;
1417 *x
= CLAMP(*x
, 0, screen_width
- (clamp
? req
.width
: 0));
1418 *y
= CLAMP(*y
, 0, screen_height
- (clamp
? req
.height
: 0));
1421 /* Same as drag_set_dest(), but for pinboard icons */
1422 static void drag_set_pinicon_dest(PinIcon
*pi
)
1424 GtkObject
*obj
= GTK_OBJECT(pi
->win
);
1426 make_drop_target(pi
->win
, 0);
1428 g_signal_connect(obj
, "drag_motion", G_CALLBACK(drag_motion
), pi
);
1429 g_signal_connect(obj
, "drag_leave", G_CALLBACK(drag_leave
), pi
);
1430 g_signal_connect(obj
, "drag_end", G_CALLBACK(drag_end
), pi
);
1433 /* Called during the drag when the mouse is in a widget registered
1434 * as a drop target. Returns TRUE if we can accept the drop.
1436 static gboolean
drag_motion(GtkWidget
*widget
,
1437 GdkDragContext
*context
,
1443 GdkDragAction action
= context
->suggested_action
;
1444 const char *type
= NULL
;
1445 Icon
*icon
= (Icon
*) pi
;
1446 DirItem
*item
= icon
->item
;
1448 if (gtk_drag_get_source_widget(context
) == widget
)
1450 g_dataset_set_data(context
, "drop_dest_type",
1451 (gpointer
) drop_dest_pass_through
);
1452 return FALSE
; /* Can't drag something to itself! */
1456 goto out
; /* Can't drag a selection to itself */
1458 type
= dnd_motion_item(context
, &item
);
1463 /* We actually must pretend to accept the drop, even if the
1464 * directory isn't writeable, so that the spring-opening
1468 /* Don't allow drops to non-writeable directories */
1469 if (o_dnd_spring_open
.int_value
== FALSE
&&
1470 type
== drop_dest_dir
&&
1471 access(icon
->path
, W_OK
) != 0)
1476 g_dataset_set_data(context
, "drop_dest_type", (gpointer
) type
);
1479 gdk_drag_status(context
, action
, time
);
1480 g_dataset_set_data_full(context
, "drop_dest_path",
1481 g_strdup(icon
->path
), g_free
);
1482 if (type
== drop_dest_dir
)
1483 dnd_spring_load(context
, NULL
);
1485 pinboard_wink_item(pi
, FALSE
);
1488 gdk_drag_status(context
, 0, time
);
1490 /* Always return TRUE to stop the pinboard getting the events */
1494 static gboolean pinboard_shadow
= FALSE
;
1495 static gint shadow_x
, shadow_y
;
1496 #define SHADOW_SIZE (ICON_WIDTH)
1498 static gboolean
bg_expose(GtkWidget
*widget
,
1499 GdkEventExpose
*event
, gpointer data
)
1501 GdkRectangle clipbox
;
1502 gpointer gclass
= ((GTypeInstance
*) widget
)->g_class
;
1504 gdk_gc_set_clip_region(widget
->style
->white_gc
, event
->region
);
1505 gdk_gc_set_clip_region(widget
->style
->black_gc
, event
->region
);
1507 gdk_region_get_clipbox(event
->region
, &clipbox
);
1509 /* Clear the area to the background image */
1511 GtkStyle
*style
= current_pinboard
->window
->style
;
1512 GdkGC
*gc
= style
->bg_gc
[GTK_STATE_NORMAL
];
1514 gdk_gc_set_clip_region(gc
, event
->region
);
1515 if (style
->bg_pixmap
[GTK_STATE_NORMAL
])
1517 gdk_gc_set_fill(gc
, GDK_TILED
);
1518 gdk_gc_set_tile(gc
, style
->bg_pixmap
[GTK_STATE_NORMAL
]);
1521 gdk_draw_rectangle(current_pinboard
->window
->window
, gc
, TRUE
,
1522 clipbox
.x
, clipbox
.y
,
1523 clipbox
.width
, clipbox
.height
);
1524 if (style
->bg_pixmap
[GTK_STATE_NORMAL
])
1525 gdk_gc_set_fill(gc
, GDK_SOLID
);
1527 gdk_gc_set_clip_region(gc
, NULL
);
1530 if (pinboard_shadow
)
1532 gdk_draw_rectangle(widget
->window
,
1533 widget
->style
->white_gc
, FALSE
,
1535 SHADOW_SIZE
, SHADOW_SIZE
);
1536 gdk_draw_rectangle(widget
->window
,
1537 widget
->style
->black_gc
, FALSE
,
1538 shadow_x
+ 1, shadow_y
+ 1,
1539 SHADOW_SIZE
- 2, SHADOW_SIZE
- 2);
1542 if (lasso_in_progress
)
1546 area
.x
= MIN(lasso_rect_x1
, lasso_rect_x2
);
1547 area
.y
= MIN(lasso_rect_y1
, lasso_rect_y2
);
1548 area
.width
= ABS(lasso_rect_x1
- lasso_rect_x2
);
1549 area
.height
= ABS(lasso_rect_y1
- lasso_rect_y2
);
1551 if (area
.width
> 4 && area
.height
> 4)
1553 gdk_draw_rectangle(widget
->window
,
1554 widget
->style
->white_gc
, FALSE
,
1556 area
.width
- 1, area
.height
- 1);
1557 gdk_draw_rectangle(widget
->window
,
1558 widget
->style
->black_gc
, FALSE
,
1559 area
.x
+ 1, area
.y
+ 1,
1560 area
.width
- 3, area
.height
- 3);
1564 gdk_gc_set_clip_region(widget
->style
->white_gc
, NULL
);
1565 gdk_gc_set_clip_region(widget
->style
->black_gc
, NULL
);
1567 ((GtkWidgetClass
*) gclass
)->expose_event(widget
, event
);
1572 /* Draw a 'shadow' under an icon being dragged, showing where
1575 static void pinboard_set_shadow(gboolean on
)
1579 if (pinboard_shadow
)
1583 area
.width
= SHADOW_SIZE
+ 1;
1584 area
.height
= SHADOW_SIZE
+ 1;
1586 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1592 int old_x
= shadow_x
, old_y
= shadow_y
;
1594 gdk_window_get_pointer(current_pinboard
->fixed
->window
,
1595 &shadow_x
, &shadow_y
, NULL
);
1596 snap_to_grid(&shadow_x
, &shadow_y
);
1597 shadow_x
-= SHADOW_SIZE
/ 2;
1598 shadow_y
-= SHADOW_SIZE
/ 2;
1601 if (pinboard_shadow
&& shadow_x
== old_x
&& shadow_y
== old_y
)
1606 area
.width
= SHADOW_SIZE
+ 1;
1607 area
.height
= SHADOW_SIZE
+ 1;
1609 gdk_window_invalidate_rect(current_pinboard
->window
->window
,
1613 pinboard_shadow
= on
;
1616 /* Called when dragging some pinboard icons finishes */
1617 void pinboard_move_icons(void)
1619 int x
= shadow_x
, y
= shadow_y
;
1620 PinIcon
*pi
= (PinIcon
*) pinboard_drag_in_progress
;
1625 g_return_if_fail(pi
!= NULL
);
1627 x
+= SHADOW_SIZE
/ 2;
1628 y
+= SHADOW_SIZE
/ 2;
1629 snap_to_grid(&x
, &y
);
1631 if (pi
->x
== x
&& pi
->y
== y
)
1634 /* Find out how much the dragged icon moved (after snapping).
1635 * Move all selected icons by the same amount.
1640 /* Move the other selected icons to keep the same relative
1643 for (next
= icon_selection
; next
; next
= next
->next
)
1645 PinIcon
*pi
= (PinIcon
*) next
->data
;
1648 g_return_if_fail(IS_PIN_ICON(pi
));
1655 gdk_drawable_get_size(pi
->win
->window
, &width
, &height
);
1656 offset_from_centre(pi
, &nx
, &ny
);
1658 fixed_move_fast(GTK_FIXED(current_pinboard
->fixed
),
1665 static void drag_leave(GtkWidget
*widget
,
1666 GdkDragContext
*context
,
1670 pinboard_wink_item(NULL
, FALSE
);
1674 static gboolean
bg_drag_leave(GtkWidget
*widget
,
1675 GdkDragContext
*context
,
1679 pinboard_set_shadow(FALSE
);
1683 static gboolean
bg_drag_motion(GtkWidget
*widget
,
1684 GdkDragContext
*context
,
1690 /* Dragging from the pinboard to the pinboard is not allowed */
1692 if (!provides(context
, text_uri_list
))
1695 pinboard_set_shadow(TRUE
);
1697 gdk_drag_status(context
,
1698 context
->suggested_action
== GDK_ACTION_ASK
1699 ? GDK_ACTION_LINK
: context
->suggested_action
,
1704 static void drag_end(GtkWidget
*widget
,
1705 GdkDragContext
*context
,
1708 pinboard_drag_in_progress
= NULL
;
1709 if (tmp_icon_selected
)
1711 icon_select_only(NULL
);
1712 tmp_icon_selected
= FALSE
;
1716 /* Something which affects all the icons has changed - reshape
1717 * and redraw all of them.
1719 static void reshape_all(void)
1723 g_return_if_fail(current_pinboard
!= NULL
);
1725 for (next
= current_pinboard
->icons
; next
; next
= next
->next
)
1727 Icon
*icon
= (Icon
*) next
->data
;
1728 pinboard_reshape_icon(icon
);
1732 /* Turns off the pinboard. Does not call gtk_main_quit. */
1733 static void pinboard_clear(void)
1737 g_return_if_fail(current_pinboard
!= NULL
);
1739 tasklist_set_active(FALSE
);
1741 next
= current_pinboard
->icons
;
1744 PinIcon
*pi
= (PinIcon
*) next
->data
;
1748 gtk_widget_destroy(pi
->win
);
1751 gtk_widget_destroy(current_pinboard
->window
);
1753 abandon_backdrop_app(current_pinboard
);
1755 g_object_unref(current_pinboard
->shadow_gc
);
1756 current_pinboard
->shadow_gc
= NULL
;
1758 g_free(current_pinboard
->name
);
1759 null_g_free(¤t_pinboard
);
1761 number_of_windows
--;
1764 static gpointer parent_class
;
1766 static void pin_icon_destroy(Icon
*icon
)
1768 PinIcon
*pi
= (PinIcon
*) icon
;
1770 g_return_if_fail(pi
->win
!= NULL
);
1772 gtk_widget_destroy(pi
->win
);
1775 static void pinboard_remove_items(void)
1777 g_return_if_fail(icon_selection
!= NULL
);
1779 while (icon_selection
)
1780 icon_destroy((Icon
*) icon_selection
->data
);
1785 static void pin_icon_update(Icon
*icon
)
1787 pinboard_reshape_icon(icon
);
1791 static gboolean
pin_icon_same_group(Icon
*icon
, Icon
*other
)
1793 return IS_PIN_ICON(other
);
1796 static void pin_wink_icon(Icon
*icon
)
1798 pinboard_wink_item((PinIcon
*) icon
, TRUE
);
1801 static void pin_icon_class_init(gpointer gclass
, gpointer data
)
1803 IconClass
*icon
= (IconClass
*) gclass
;
1805 parent_class
= g_type_class_peek_parent(gclass
);
1807 icon
->destroy
= pin_icon_destroy
;
1808 icon
->redraw
= pinboard_reshape_icon
;
1809 icon
->update
= pin_icon_update
;
1810 icon
->wink
= pin_wink_icon
;
1811 icon
->remove_items
= pinboard_remove_items
;
1812 icon
->same_group
= pin_icon_same_group
;
1815 static void pin_icon_init(GTypeInstance
*object
, gpointer gclass
)
1819 static GType
pin_icon_get_type(void)
1821 static GType type
= 0;
1825 static const GTypeInfo info
=
1827 sizeof (PinIconClass
),
1828 NULL
, /* base_init */
1829 NULL
, /* base_finalise */
1830 pin_icon_class_init
,
1831 NULL
, /* class_finalise */
1832 NULL
, /* class_data */
1834 0, /* n_preallocs */
1838 type
= g_type_register_static(icon_get_type(),
1839 "PinIcon", &info
, 0);
1845 static PinIcon
*pin_icon_new(const char *pathname
, const char *name
)
1850 pi
= g_object_new(pin_icon_get_type(), NULL
);
1853 icon_set_path(icon
, pathname
, name
);
1858 /* Called when the window widget is somehow destroyed */
1859 static void pin_icon_destroyed(PinIcon
*pi
)
1861 g_return_if_fail(pi
->win
!= NULL
);
1865 pinboard_wink_item(NULL
, FALSE
);
1867 if (pinboard_drag_in_progress
== (Icon
*) pi
)
1868 pinboard_drag_in_progress
= NULL
;
1870 if (current_pinboard
)
1871 current_pinboard
->icons
=
1872 g_list_remove(current_pinboard
->icons
, pi
);
1877 /* Set the tooltip */
1878 static void pin_icon_set_tip(PinIcon
*pi
)
1882 Icon
*icon
= (Icon
*) pi
;
1884 g_return_if_fail(pi
!= NULL
);
1886 ai
= appinfo_get(icon
->path
, icon
->item
);
1888 if (ai
&& ((node
= xml_get_section(ai
, NULL
, "Summary"))))
1891 str
= xmlNodeListGetString(node
->doc
,
1892 node
->xmlChildrenNode
, 1);
1895 gtk_tooltips_set_tip(tooltips
, pi
->win
, str
, NULL
);
1900 gtk_tooltips_set_tip(tooltips
, pi
->widget
, NULL
, NULL
);
1906 static void pinboard_show_menu(GdkEventButton
*event
, PinIcon
*pi
)
1910 pos
[0] = event
->x_root
;
1911 pos
[1] = event
->y_root
;
1914 icon_prepare_menu((Icon
*) pi
, TRUE
);
1916 gtk_menu_popup(GTK_MENU(icon_menu
), NULL
, NULL
,
1918 (gpointer
) pos
, event
->button
, event
->time
);
1921 static void create_pinboard_window(Pinboard
*pinboard
)
1925 g_return_if_fail(pinboard
->window
== NULL
);
1927 win
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
1928 gtk_widget_set_style(win
, gtk_widget_get_default_style());
1930 gtk_widget_set_double_buffered(win
, FALSE
);
1931 gtk_widget_set_app_paintable(win
, TRUE
);
1932 gtk_widget_set_name(win
, "rox-pinboard");
1933 pinboard
->window
= win
;
1934 pinboard
->fixed
= gtk_fixed_new();
1935 gtk_container_add(GTK_CONTAINER(win
), pinboard
->fixed
);
1937 gtk_window_set_wmclass(GTK_WINDOW(win
), "ROX-Pinboard", PROJECT
);
1939 gtk_widget_set_size_request(win
, screen_width
, screen_height
);
1940 gtk_widget_realize(win
);
1941 gtk_window_move(GTK_WINDOW(win
), 0, 0);
1942 make_panel_window(win
);
1944 /* TODO: Use gdk function when it supports this type */
1946 GdkAtom desktop_type
;
1948 desktop_type
= gdk_atom_intern("_NET_WM_WINDOW_TYPE_DESKTOP",
1950 gdk_property_change(win
->window
,
1951 gdk_atom_intern("_NET_WM_WINDOW_TYPE", FALSE
),
1952 gdk_atom_intern("ATOM", FALSE
), 32,
1953 GDK_PROP_MODE_REPLACE
, (guchar
*) &desktop_type
, 1);
1956 gtk_widget_add_events(win
,
1957 GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
|
1959 GDK_BUTTON1_MOTION_MASK
|
1960 GDK_BUTTON2_MOTION_MASK
| GDK_BUTTON3_MOTION_MASK
);
1961 g_signal_connect(win
, "button-press-event",
1962 G_CALLBACK(button_press_event
), NULL
);
1963 g_signal_connect(win
, "button-release-event",
1964 G_CALLBACK(button_release_event
), NULL
);
1965 g_signal_connect(win
, "motion-notify-event",
1966 G_CALLBACK(lasso_motion
), NULL
);
1967 g_signal_connect(pinboard
->fixed
, "expose_event",
1968 G_CALLBACK(bg_expose
), NULL
);
1970 /* Drag and drop handlers */
1971 drag_set_pinboard_dest(win
);
1972 g_signal_connect(win
, "drag_motion", G_CALLBACK(bg_drag_motion
), NULL
);
1973 g_signal_connect(win
, "drag_leave", G_CALLBACK(bg_drag_leave
), NULL
);
1975 pinboard
->shadow_gc
= gdk_gc_new(win
->window
);
1976 gdk_gc_set_rgb_fg_color(pinboard
->shadow_gc
, &pin_text_shadow_col
);
1978 reload_backdrop(current_pinboard
, NULL
, BACKDROP_NONE
);
1980 gtk_widget_show_all(win
);
1981 gdk_window_lower(win
->window
);
1984 /* Load image 'path' and scale according to 'style' */
1985 static GdkPixmap
*load_backdrop(const gchar
*path
, BackdropStyle style
)
1989 GError
*error
= NULL
;
1991 pixbuf
= gdk_pixbuf_new_from_file(path
, &error
);
1994 delayed_error(_("Error loading backdrop image:\n%s\n"
1995 "Backdrop removed."),
1997 g_error_free(error
);
1998 set_backdrop(NULL
, BACKDROP_NONE
);
2002 if (style
== BACKDROP_SCALE
)
2004 GdkPixbuf
*old
= pixbuf
;
2006 pixbuf
= gdk_pixbuf_scale_simple(old
,
2007 screen_width
, screen_height
,
2010 g_object_unref(old
);
2012 else if (style
== BACKDROP_CENTRE
)
2014 GdkPixbuf
*old
= pixbuf
;
2015 int x
, y
, width
, height
;
2017 width
= gdk_pixbuf_get_width(pixbuf
);
2018 height
= gdk_pixbuf_get_height(pixbuf
);
2020 pixbuf
= gdk_pixbuf_new(
2021 gdk_pixbuf_get_colorspace(pixbuf
), 0,
2022 8, screen_width
, screen_height
);
2023 gdk_pixbuf_fill(pixbuf
, 0);
2025 x
= (screen_width
- width
) / 2;
2026 y
= (screen_height
- height
) / 2;
2030 gdk_pixbuf_composite(old
, pixbuf
,
2032 MIN(screen_width
, width
),
2033 MIN(screen_height
, height
),
2035 GDK_INTERP_NEAREST
, 255);
2036 g_object_unref(old
);
2039 gdk_pixbuf_render_pixmap_and_mask(pixbuf
,
2041 g_object_unref(pixbuf
);
2046 static void abandon_backdrop_app(Pinboard
*pinboard
)
2048 g_return_if_fail(pinboard
!= NULL
);
2050 if (pinboard
->to_backdrop_app
!= -1)
2052 close(pinboard
->to_backdrop_app
);
2053 close(pinboard
->from_backdrop_app
);
2054 gtk_input_remove(pinboard
->input_tag
);
2055 g_string_free(pinboard
->input_buffer
, TRUE
);
2056 pinboard
->to_backdrop_app
= -1;
2057 pinboard
->from_backdrop_app
= -1;
2058 pinboard
->input_tag
= -1;
2059 pinboard
->input_buffer
= NULL
;
2062 g_return_if_fail(pinboard
->to_backdrop_app
== -1);
2063 g_return_if_fail(pinboard
->from_backdrop_app
== -1);
2064 g_return_if_fail(pinboard
->input_tag
== -1);
2065 g_return_if_fail(pinboard
->input_buffer
== NULL
);
2068 /* A single line has been read from the child.
2069 * Processes the command, and replies 'ok' (or abandons the child on error).
2071 static void command_from_backdrop_app(Pinboard
*pinboard
, const gchar
*command
)
2073 BackdropStyle style
;
2074 const char *ok
= "ok\n";
2076 if (strncmp(command
, "tile ", 5) == 0)
2078 style
= BACKDROP_TILE
;
2081 else if (strncmp(command
, "scale ", 6) == 0)
2083 style
= BACKDROP_SCALE
;
2086 else if (strncmp(command
, "centre ", 7) == 0)
2088 style
= BACKDROP_CENTRE
;
2093 g_warning("Invalid command '%s' from backdrop app\n",
2095 abandon_backdrop_app(pinboard
);
2099 /* Load the backdrop. May abandon the program if loading fails. */
2100 reload_backdrop(pinboard
, command
, style
);
2102 if (pinboard
->to_backdrop_app
== -1)
2109 sent
= write(pinboard
->to_backdrop_app
, ok
, strlen(ok
));
2112 /* Remote app quit? Not an error. */
2113 abandon_backdrop_app(pinboard
);
2120 static void backdrop_from_child(Pinboard
*pinboard
,
2121 int src
, GdkInputCondition cond
)
2126 got
= read(src
, buf
, sizeof(buf
));
2131 g_warning("backdrop_from_child: %s\n",
2133 abandon_backdrop_app(pinboard
);
2137 g_string_append_len(pinboard
->input_buffer
, buf
, got
);
2139 while (pinboard
->from_backdrop_app
!= -1)
2144 nl
= strchr(pinboard
->input_buffer
->str
, '\n');
2146 return; /* Haven't got a whole line yet */
2148 len
= nl
- pinboard
->input_buffer
->str
;
2149 command
= g_strndup(pinboard
->input_buffer
->str
, len
);
2150 g_string_erase(pinboard
->input_buffer
, 0, len
+ 1);
2152 command_from_backdrop_app(pinboard
, command
);
2158 static void reload_backdrop(Pinboard
*pinboard
,
2159 const gchar
*backdrop
,
2160 BackdropStyle backdrop_style
)
2164 if (backdrop
&& backdrop_style
== BACKDROP_PROGRAM
)
2166 const char *argv
[] = {NULL
, "--backdrop", NULL
};
2167 GError
*error
= NULL
;
2169 g_return_if_fail(pinboard
->to_backdrop_app
== -1);
2170 g_return_if_fail(pinboard
->from_backdrop_app
== -1);
2171 g_return_if_fail(pinboard
->input_tag
== -1);
2172 g_return_if_fail(pinboard
->input_buffer
== NULL
);
2174 argv
[0] = make_path(backdrop
, "AppRun");
2176 /* Run the program. It'll send us a SOAP message and we'll
2177 * get back here with a different style and image.
2180 if (g_spawn_async_with_pipes(NULL
, (gchar
**) argv
, NULL
,
2181 G_SPAWN_DO_NOT_REAP_CHILD
|
2182 G_SPAWN_SEARCH_PATH
,
2183 NULL
, NULL
, /* Child setup fn */
2184 NULL
, /* Child PID */
2185 &pinboard
->to_backdrop_app
,
2186 &pinboard
->from_backdrop_app
,
2187 NULL
, /* Standard error */
2190 pinboard
->input_buffer
= g_string_new(NULL
);
2191 pinboard
->input_tag
= gtk_input_add_full(
2192 pinboard
->from_backdrop_app
,
2194 (GdkInputFunction
) backdrop_from_child
,
2195 NULL
, pinboard
, NULL
);
2199 delayed_error("%s", error
? error
->message
: "(null)");
2200 g_error_free(error
);
2205 /* Note: Copying a style does not ref the pixmaps! */
2207 style
= gtk_style_copy(gtk_widget_get_style(pinboard
->window
));
2208 style
->bg_pixmap
[GTK_STATE_NORMAL
] = NULL
;
2211 style
->bg_pixmap
[GTK_STATE_NORMAL
] =
2212 load_backdrop(backdrop
, backdrop_style
);
2214 gdk_color_parse(o_pinboard_bg_colour
.value
,
2215 &style
->bg
[GTK_STATE_NORMAL
]);
2217 gtk_widget_set_style(pinboard
->window
, style
);
2219 g_object_unref(style
);
2221 gtk_widget_queue_draw(pinboard
->window
);
2223 /* Also update root window property (for transparent xterms, etc) */
2224 if (style
->bg_pixmap
[GTK_STATE_NORMAL
])
2226 XID id
= GDK_DRAWABLE_XID(style
->bg_pixmap
[GTK_STATE_NORMAL
]);
2227 gdk_property_change(gdk_get_default_root_window(),
2228 gdk_atom_intern("_XROOTPMAP_ID", FALSE
),
2229 gdk_atom_intern("PIXMAP", FALSE
),
2230 32, GDK_PROP_MODE_REPLACE
,
2235 gdk_property_delete(gdk_get_default_root_window(),
2236 gdk_atom_intern("_XROOTPMAP_ID", FALSE
));
2240 /* Set and save (path, style) as the new backdrop.
2241 * If style is BACKDROP_PROGRAM, the program is run to get the backdrop.
2242 * Otherwise, the image is displayed now.
2244 static void set_backdrop(const gchar
*path
, BackdropStyle style
)
2246 g_return_if_fail((path
== NULL
&& style
== BACKDROP_NONE
) ||
2247 (path
!= NULL
&& style
!= BACKDROP_NONE
));
2249 if (!current_pinboard
)
2253 pinboard_activate("Default");
2254 delayed_error(_("No pinboard was in use... "
2255 "the 'Default' pinboard has been selected. "
2256 "Use 'rox -p=Default' to turn it on in "
2258 g_return_if_fail(current_pinboard
!= NULL
);
2261 /* We might have just run the old backdrop program and now
2262 * we're going to set a new one! Seems a bit mean...
2265 abandon_backdrop_app(current_pinboard
);
2267 g_free(current_pinboard
->backdrop
);
2268 current_pinboard
->backdrop
= g_strdup(path
);
2269 current_pinboard
->backdrop_style
= style
;
2270 reload_backdrop(current_pinboard
,
2271 current_pinboard
->backdrop
,
2272 current_pinboard
->backdrop_style
);
2277 #define SEARCH_STEP 32
2279 static void search_free(GdkRectangle
*rect
, GdkRegion
*used
,
2280 int *outer
, int od
, int omax
,
2281 int *inner
, int id
, int imax
)
2283 *outer
= od
> 0 ? 0 : omax
;
2284 while (*outer
>= 0 && *outer
<= omax
)
2286 *inner
= id
> 0 ? 0 : imax
;
2287 while (*inner
>= 0 && *inner
<= imax
)
2289 if (gdk_region_rect_in(used
, rect
) ==
2290 GDK_OVERLAP_RECTANGLE_OUT
)
2302 /* Finds a free area on the pinboard large enough for the width and height
2303 * of the given rectangle, by filling in the x and y fields of 'rect'.
2304 * The search order respects user preferences.
2305 * If no area is free, returns any old area.
2307 static void find_free_rect(Pinboard
*pinboard
, GdkRectangle
*rect
)
2311 GdkRectangle used_rect
;
2312 int dx
= SEARCH_STEP
, dy
= SEARCH_STEP
;
2314 used
= gdk_region_new();
2316 panel_mark_used(used
);
2318 /* Subtract the used areas... */
2320 next
= GTK_FIXED(pinboard
->fixed
)->children
;
2321 for (; next
; next
= next
->next
)
2323 GtkFixedChild
*fix
= (GtkFixedChild
*) next
->data
;
2325 if (!GTK_WIDGET_VISIBLE(fix
->widget
))
2328 used_rect
.x
= fix
->x
;
2329 used_rect
.y
= fix
->y
;
2330 used_rect
.width
= fix
->widget
->requisition
.width
;
2331 used_rect
.height
= fix
->widget
->requisition
.height
;
2333 gdk_region_union_with_rect(used
, &used_rect
);
2336 /* Find the first free area (yes, this isn't exactly pretty, but
2337 * it works). If you know a better (fast!) algorithm, let me know!
2341 if (o_iconify_start
.int_value
== CORNER_TOP_RIGHT
||
2342 o_iconify_start
.int_value
== CORNER_BOTTOM_RIGHT
)
2345 if (o_iconify_start
.int_value
== CORNER_BOTTOM_LEFT
||
2346 o_iconify_start
.int_value
== CORNER_BOTTOM_RIGHT
)
2349 if (o_iconify_dir
.int_value
== DIR_VERT
)
2351 search_free(rect
, used
,
2352 &rect
->x
, dx
, screen_width
- rect
->width
,
2353 &rect
->y
, dy
, screen_height
- rect
->height
);
2357 search_free(rect
, used
,
2358 &rect
->y
, dy
, screen_height
- rect
->height
,
2359 &rect
->x
, dx
, screen_width
- rect
->width
);
2362 gdk_region_destroy(used
);
2371 /* Icon's size, shape or appearance has changed - update the display */
2372 static void pinboard_reshape_icon(Icon
*icon
)
2374 PinIcon
*pi
= (PinIcon
*) icon
;
2375 int x
= pi
->x
, y
= pi
->y
;
2377 set_size_and_style(pi
);
2378 offset_from_centre(pi
, &x
, &y
);
2380 if (pi
->win
->allocation
.x
!= x
|| pi
->win
->allocation
.y
!= y
)
2382 fixed_move_fast(GTK_FIXED(current_pinboard
->fixed
),
2387 /* Sets the pinboard_font global from the option. Doesn't do anything else. */
2388 static void update_pinboard_font(void)
2391 pango_font_description_free(pinboard_font
);
2392 pinboard_font
= o_label_font
.value
[0] != '\0'
2393 ? pango_font_description_from_string(o_label_font
.value
)