r2596: Updated for new version.
[rox-filer.git] / ROX-Filer / src / pinboard.c
blob96254986d84b513323c9ce34964445b67216070a
1 /*
2 * $Id$
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)
10 * any later version.
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
15 * more details.
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 */
24 #include "config.h"
26 #include <ctype.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <gtk/gtk.h>
31 #include <gdk/gdkx.h>
32 #include <stdlib.h>
33 #include <math.h>
34 #include <libxml/parser.h>
35 #include <signal.h>
37 #include "global.h"
39 #include "pinboard.h"
40 #include "main.h"
41 #include "dnd.h"
42 #include "pixmaps.h"
43 #include "type.h"
44 #include "choices.h"
45 #include "support.h"
46 #include "gui_support.h"
47 #include "options.h"
48 #include "diritem.h"
49 #include "bind.h"
50 #include "icon.h"
51 #include "run.h"
52 #include "appinfo.h"
53 #include "menu.h"
54 #include "xml.h"
55 #include "tasklist.h"
56 #include "panel.h" /* For panel_mark_used() */
57 #include "wrapped.h"
58 #include "dropbox.h"
60 static gboolean tmp_icon_selected = FALSE; /* When dragging */
62 static GtkWidget *set_backdrop_dialog = NULL;
64 struct _Pinboard {
65 guchar *name; /* Leaf name */
66 GList *icons;
67 GtkStyle *style;
69 gchar *backdrop; /* Pathname */
70 BackdropStyle backdrop_style;
71 gint to_backdrop_app; /* pipe FD, or -1 */
72 gint from_backdrop_app; /* pipe FD, or -1 */
73 gint input_tag;
74 GString *input_buffer;
76 GtkWidget *window; /* Screen-sized window */
77 GtkWidget *fixed;
78 GdkGC *shadow_gc;
81 #define IS_PIN_ICON(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), pin_icon_get_type())
83 typedef struct _PinIconClass PinIconClass;
84 typedef struct _PinIcon PinIcon;
86 struct _PinIconClass {
87 IconClass parent;
90 struct _PinIcon {
91 Icon icon;
93 int x, y;
94 GtkWidget *win;
95 GtkWidget *widget; /* The drawing area for the icon */
96 GtkWidget *label;
99 /* The number of pixels between the bottom of the image and the top
100 * of the text.
102 #define GAP 4
104 /* The size of the border around the icon which is used when winking */
105 #define WINK_FRAME 2
107 /* Grid sizes */
108 #define GRID_STEP_FINE 2
109 #define GRID_STEP_MED 16
110 #define GRID_STEP_COARSE 32
112 /* Used in options */
113 #define CORNER_TOP_LEFT 0
114 #define CORNER_TOP_RIGHT 1
115 #define CORNER_BOTTOM_LEFT 2
116 #define CORNER_BOTTOM_RIGHT 3
118 #define DIR_HORZ 0
119 #define DIR_VERT 1
121 static PinIcon *current_wink_icon = NULL;
122 static gint wink_timeout;
124 /* Used for the text colours (only) in the icons */
125 GdkColor pin_text_fg_col, pin_text_bg_col;
126 PangoFontDescription *pinboard_font = NULL; /* NULL => Gtk default */
128 static GdkColor pin_text_shadow_col;
130 Pinboard *current_pinboard = NULL;
131 static gint loading_pinboard = 0; /* Non-zero => loading */
133 /* The Icon that was used to start the current drag, if any */
134 Icon *pinboard_drag_in_progress = NULL;
136 /* For selecting groups of icons */
137 static gboolean lasso_in_progress = FALSE;
138 static int lasso_rect_x1, lasso_rect_x2;
139 static int lasso_rect_y1, lasso_rect_y2;
141 static Option o_pinboard_clamp_icons, o_pinboard_grid_step;
142 static Option o_pinboard_fg_colour, o_pinboard_bg_colour;
143 static Option o_pinboard_tasklist, o_forward_buttons_13;
144 static Option o_iconify_start, o_iconify_dir;
145 static Option o_label_font, o_pinboard_shadow_colour;
146 static Option o_pinboard_shadow_labels;
148 /* Static prototypes */
149 static GType pin_icon_get_type(void);
150 static void set_size_and_style(PinIcon *pi);
151 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi);
152 static gint end_wink(gpointer data);
153 static gboolean button_release_event(GtkWidget *widget,
154 GdkEventButton *event,
155 PinIcon *pi);
156 static gboolean enter_notify(GtkWidget *widget,
157 GdkEventCrossing *event,
158 PinIcon *pi);
159 static gint leave_notify(GtkWidget *widget,
160 GdkEventCrossing *event,
161 PinIcon *pi);
162 static gboolean button_press_event(GtkWidget *widget,
163 GdkEventButton *event,
164 PinIcon *pi);
165 static gint icon_motion_notify(GtkWidget *widget,
166 GdkEventMotion *event,
167 PinIcon *pi);
168 static const char *pin_from_file(gchar *line);
169 static void snap_to_grid(int *x, int *y);
170 static void offset_from_centre(PinIcon *pi, int *x, int *y);
171 static gboolean drag_motion(GtkWidget *widget,
172 GdkDragContext *context,
173 gint x,
174 gint y,
175 guint time,
176 PinIcon *pi);
177 static void drag_set_pinicon_dest(PinIcon *pi);
178 static void drag_leave(GtkWidget *widget,
179 GdkDragContext *context,
180 guint32 time,
181 PinIcon *pi);
182 static gboolean bg_drag_motion(GtkWidget *widget,
183 GdkDragContext *context,
184 gint x,
185 gint y,
186 guint time,
187 gpointer data);
188 static gboolean bg_drag_leave(GtkWidget *widget,
189 GdkDragContext *context,
190 guint32 time,
191 gpointer data);
192 static gboolean bg_expose(GtkWidget *window,
193 GdkEventExpose *event, gpointer data);
194 static void drag_end(GtkWidget *widget,
195 GdkDragContext *context,
196 PinIcon *pi);
197 static void reshape_all(void);
198 static void pinboard_check_options(void);
199 static void pinboard_load_from_xml(xmlDocPtr doc);
200 static void pinboard_clear(void);
201 static void pinboard_save(void);
202 static PinIcon *pin_icon_new(const char *pathname, const char *name);
203 static void pin_icon_destroyed(PinIcon *pi);
204 static void pin_icon_set_tip(PinIcon *pi);
205 static void pinboard_show_menu(GdkEventButton *event, PinIcon *pi);
206 static void create_pinboard_window(Pinboard *pinboard);
207 static void reload_backdrop(Pinboard *pinboard,
208 const gchar *backdrop,
209 BackdropStyle backdrop_style);
210 static void set_backdrop(const gchar *path, BackdropStyle style);
211 static void pinboard_reshape_icon(Icon *icon);
212 static gint draw_wink(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi);
213 static void abandon_backdrop_app(Pinboard *pinboard);
214 static void drag_backdrop_dropped(GtkWidget *drop_box,
215 const guchar *path,
216 GtkWidget *dialog);
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);
222 static void clear_backdrop(GtkWidget *drop_box, gpointer data);
223 static void radios_changed(gpointer data);
224 static void update_radios(GtkWidget *dialog);
226 /****************************************************************
227 * EXTERNAL INTERFACE *
228 ****************************************************************/
230 void pinboard_init(void)
232 option_add_string(&o_pinboard_fg_colour, "pinboard_fg_colour", "#fff");
233 option_add_string(&o_pinboard_bg_colour, "pinboard_bg_colour", "#888");
234 option_add_string(&o_pinboard_shadow_colour, "pinboard_shadow_colour",
235 "#000");
236 option_add_string(&o_label_font, "label_font", "");
237 option_add_int(&o_pinboard_shadow_labels, "pinboard_shadow_labels", 1);
239 option_add_int(&o_pinboard_clamp_icons, "pinboard_clamp_icons", 1);
240 option_add_int(&o_pinboard_grid_step, "pinboard_grid_step",
241 GRID_STEP_COARSE);
242 option_add_int(&o_pinboard_tasklist, "pinboard_tasklist", TRUE);
243 option_add_int(&o_forward_buttons_13, "pinboard_forward_buttons_13",
244 FALSE);
246 option_add_int(&o_iconify_start, "iconify_start", CORNER_TOP_RIGHT);
247 option_add_int(&o_iconify_dir, "iconify_dir", DIR_VERT);
249 option_add_notify(pinboard_check_options);
251 gdk_color_parse(o_pinboard_fg_colour.value, &pin_text_fg_col);
252 gdk_color_parse(o_pinboard_bg_colour.value, &pin_text_bg_col);
253 gdk_color_parse(o_pinboard_shadow_colour.value, &pin_text_shadow_col);
254 update_pinboard_font();
257 /* Load 'pb_<pinboard>' config file from Choices (if it exists)
258 * and make it the current pinboard.
259 * Any existing pinned items are removed. You must call this
260 * at least once before using the pinboard. NULL disables the
261 * pinboard.
263 void pinboard_activate(const gchar *name)
265 Pinboard *old_board = current_pinboard;
266 guchar *path, *slash;
268 /* Treat an empty name the same as NULL */
269 if (name && !*name)
270 name = NULL;
272 if (old_board)
273 pinboard_clear();
275 if (!name)
277 if (number_of_windows < 1 && gtk_main_level() > 0)
278 gtk_main_quit();
280 gdk_property_delete(gdk_get_default_root_window(),
281 gdk_atom_intern("_XROOTPMAP_ID", FALSE));
282 return;
285 number_of_windows++;
287 slash = strchr(name, '/');
288 if (slash)
290 if (access(name, F_OK))
291 path = NULL; /* File does not (yet) exist */
292 else
293 path = g_strdup(name);
295 else
297 guchar *leaf;
299 leaf = g_strconcat("pb_", name, NULL);
300 path = choices_find_path_load(leaf, PROJECT);
301 g_free(leaf);
304 current_pinboard = g_new(Pinboard, 1);
305 current_pinboard->name = g_strdup(name);
306 current_pinboard->icons = NULL;
307 current_pinboard->window = NULL;
308 current_pinboard->backdrop = NULL;
309 current_pinboard->backdrop_style = BACKDROP_NONE;
310 current_pinboard->to_backdrop_app = -1;
311 current_pinboard->from_backdrop_app = -1;
312 current_pinboard->input_tag = -1;
313 current_pinboard->input_buffer = NULL;
315 create_pinboard_window(current_pinboard);
317 loading_pinboard++;
318 if (path)
320 xmlDocPtr doc;
321 doc = xmlParseFile(path);
322 if (doc)
324 pinboard_load_from_xml(doc);
325 xmlFreeDoc(doc);
326 reload_backdrop(current_pinboard,
327 current_pinboard->backdrop,
328 current_pinboard->backdrop_style);
330 else
332 parse_file(path, pin_from_file);
333 info_message(_("Your old pinboard file has been "
334 "converted to the new XML format."));
335 pinboard_save();
337 g_free(path);
339 else
340 pinboard_pin(home_dir, "Home",
341 4 + ICON_WIDTH / 2,
342 4 + ICON_HEIGHT / 2,
343 NULL);
344 loading_pinboard--;
346 if (o_pinboard_tasklist.int_value)
347 tasklist_set_active(TRUE);
350 /* Return the window of the current pinboard, or NULL.
351 * Used to make sure lowering the panels doesn't lose them...
353 GdkWindow *pinboard_get_window(void)
355 if (current_pinboard)
356 return current_pinboard->window->window;
357 return NULL;
360 const char *pinboard_get_name(void)
362 g_return_val_if_fail(current_pinboard != NULL, NULL);
364 return current_pinboard->name;
367 /* Add widget to the pinboard. Caller is responsible for coping with pinboard
368 * being cleared.
370 void pinboard_add_widget(GtkWidget *widget)
372 GtkRequisition req;
373 GdkRectangle rect;
375 g_return_if_fail(current_pinboard != NULL);
377 gtk_fixed_put(GTK_FIXED(current_pinboard->fixed), widget, 0, 0);
379 gtk_widget_size_request(widget, &req);
381 rect.width = req.width;
382 rect.height = req.height;
383 find_free_rect(current_pinboard, &rect);
385 gtk_fixed_move(GTK_FIXED(current_pinboard->fixed),
386 widget, rect.x, rect.y);
389 /* Add a new icon to the background.
390 * 'path' should be an absolute pathname.
391 * 'x' and 'y' are the coordinates of the point in the middle of the text.
392 * 'name' is the name to use. If NULL then the leafname of path is used.
394 void pinboard_pin(const gchar *path, const gchar *name, int x, int y,
395 const gchar *shortcut)
397 GtkWidget *align, *vbox;
398 GdkWindow *events;
399 PinIcon *pi;
400 Icon *icon;
402 g_return_if_fail(path != NULL);
403 g_return_if_fail(current_pinboard != NULL);
405 pi = pin_icon_new(path, name);
406 icon = (Icon *) pi;
407 pi->x = x;
408 pi->y = y;
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
420 * that :-)
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);
447 pi->x = x;
448 pi->y = 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,
455 GDK_EXPOSURE_MASK |
456 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
457 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
458 GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
459 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, "leave-notify-event",
463 G_CALLBACK(leave_notify), pi);
464 g_signal_connect(pi->win, "button-press-event",
465 G_CALLBACK(button_press_event), pi);
466 g_signal_connect(pi->win, "button-release-event",
467 G_CALLBACK(button_release_event), pi);
468 g_signal_connect(pi->win, "motion-notify-event",
469 G_CALLBACK(icon_motion_notify), pi);
470 g_signal_connect(pi->win, "expose-event",
471 G_CALLBACK(draw_icon), pi);
472 g_signal_connect_swapped(pi->win, "style-set",
473 G_CALLBACK(pinboard_reshape_icon), pi);
474 g_signal_connect_swapped(pi->win, "destroy",
475 G_CALLBACK(pin_icon_destroyed), pi);
477 current_pinboard->icons = g_list_prepend(current_pinboard->icons, pi);
478 pin_icon_set_tip(pi);
480 icon_set_shortcut(icon, shortcut);
482 if (!loading_pinboard)
483 pinboard_save();
486 /* Put a border around the icon, briefly.
487 * If icon is NULL then cancel any existing wink.
488 * The icon will automatically unhighlight unless timeout is FALSE,
489 * in which case you must call this function again (with NULL or another
490 * icon) to remove the highlight.
492 static void pinboard_wink_item(PinIcon *pi, gboolean timeout)
494 PinIcon *old = current_wink_icon;
496 if (old == pi)
497 return;
499 current_wink_icon = pi;
501 if (old)
503 gtk_widget_queue_draw(old->win);
504 gdk_window_process_updates(old->widget->window, TRUE);
506 if (wink_timeout != -1)
507 gtk_timeout_remove(wink_timeout);
510 if (pi)
512 gtk_widget_queue_draw(pi->win);
513 gdk_window_process_updates(pi->widget->window, TRUE);
515 if (timeout)
516 wink_timeout = gtk_timeout_add(300, end_wink, NULL);
517 else
518 wink_timeout = -1;
522 /* 'app' is saved as the new application to set the backdrop. It will then be
523 * run, and should communicate with the filer as described in the manual.
525 void pinboard_set_backdrop_app(const gchar *app)
527 XMLwrapper *ai;
528 DirItem *item;
529 gboolean can_set;
531 item = diritem_new("");
532 diritem_restat(app, item, NULL);
533 if (!(item->flags & ITEM_FLAG_APPDIR))
535 delayed_error(_("The backdrop handler must be an application "
536 "directory. Drag an application directory "
537 "into the Set Backdrop dialog box, or (for "
538 "programmers) pass it to the SOAP "
539 "SetBackdropApp method."));
540 diritem_free(item);
541 return;
544 ai = appinfo_get(app, item);
545 diritem_free(item);
547 can_set = ai && xml_get_section(ai, ROX_NS, "CanSetBackdrop") != NULL;
548 if (ai)
549 g_object_unref(ai);
551 if (can_set)
552 set_backdrop(app, BACKDROP_PROGRAM);
553 else
554 delayed_error(_("You can only set the backdrop to an image "
555 "or to a program which knows how to "
556 "manage ROX-Filer's backdrop.\n\n"
557 "Programmers: the application's AppInfo.xml "
558 "must contain the CanSetBackdrop element, as "
559 "described in ROX-Filer's manual."));
562 /* Open a dialog box allowing the user to set the backdrop */
563 void pinboard_set_backdrop(void)
565 GtkWidget *dialog, *frame, *label, *hbox;
566 GtkBox *vbox;
567 Radios *radios;
569 g_return_if_fail(current_pinboard != NULL);
571 if (set_backdrop_dialog)
572 gtk_widget_destroy(set_backdrop_dialog);
574 dialog = gtk_dialog_new_with_buttons(_("Set backdrop"), NULL,
575 GTK_DIALOG_NO_SEPARATOR,
576 GTK_STOCK_CLOSE, GTK_RESPONSE_OK,
577 NULL);
578 set_backdrop_dialog = dialog;
579 g_signal_connect(dialog, "destroy",
580 G_CALLBACK(gtk_widget_destroyed), &set_backdrop_dialog);
581 vbox = GTK_BOX(GTK_DIALOG(dialog)->vbox);
583 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
585 label = gtk_label_new(_("Choose a style and drag an image in:"));
586 gtk_misc_set_padding(GTK_MISC(label), 4, 0);
587 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
588 gtk_box_pack_start(vbox, label, TRUE, TRUE, 4);
590 /* The Centred, Scaled, Tiled radios... */
591 hbox = gtk_hbox_new(TRUE, 2);
592 gtk_box_pack_start(vbox, hbox, TRUE, TRUE, 4);
594 radios = radios_new(radios_changed, dialog);
595 g_object_set_data(G_OBJECT(dialog), "rox-radios", radios);
596 g_object_set_data(G_OBJECT(dialog), "rox-radios-hbox", hbox);
598 radios_add(radios, _("Centre the image without scaling it"),
599 BACKDROP_CENTRE, _("Centre"));
600 radios_add(radios, _("Scale the image to fit the backdrop area, "
601 "without distorting it"),
602 BACKDROP_SCALE, _("Scale"));
603 radios_add(radios, _("Stretch the image to fill the backdrop area"),
604 BACKDROP_STRETCH, _("Stretch"));
605 radios_add(radios, _("Tile the image over the backdrop area"),
606 BACKDROP_TILE, _("Tile"));
608 update_radios(dialog);
610 /* The drop area... */
611 frame = drop_box_new(_("Drop an image here"));
612 g_object_set_data(G_OBJECT(dialog), "rox-dropbox", frame);
614 radios_pack(radios, GTK_BOX(hbox));
615 gtk_box_pack_start(vbox, frame, TRUE, TRUE, 4);
617 drop_box_set_path(DROP_BOX(frame), current_pinboard->backdrop);
619 g_signal_connect(frame, "path_dropped",
620 G_CALLBACK(drag_backdrop_dropped), dialog);
621 g_signal_connect(frame, "clear",
622 G_CALLBACK(clear_backdrop), dialog);
624 g_signal_connect(dialog, "response",
625 G_CALLBACK(backdrop_response), NULL);
626 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
628 gtk_widget_show_all(dialog);
631 /* Also used by tasklist.c */
632 void draw_label_shadow(WrappedLabel *wl, GdkRegion *region)
634 GtkWidget *widget;
635 gint x, y;
637 if (!o_pinboard_shadow_labels.int_value)
638 return;
640 gdk_gc_set_clip_region(current_pinboard->shadow_gc, region);
642 widget = GTK_WIDGET(wl);
644 y = widget->allocation.y - wl->y_off;
645 x = widget->allocation.x - wl->x_off -
646 ((wl->text_width - widget->allocation.width) >> 1);
648 gdk_draw_layout(widget->window, current_pinboard->shadow_gc,
649 x + 1, y + 1, wl->layout);
651 if (o_pinboard_shadow_labels.int_value > 1)
652 gdk_draw_layout(widget->window, current_pinboard->shadow_gc,
653 x + 2, y + 2, wl->layout);
655 gdk_gc_set_clip_region(current_pinboard->shadow_gc, NULL);
658 /****************************************************************
659 * INTERNAL FUNCTIONS *
660 ****************************************************************/
662 static void backdrop_response(GtkWidget *dialog, gint response, gpointer data)
664 gtk_widget_destroy(dialog);
667 static void clear_backdrop(GtkWidget *drop_box, gpointer data)
669 set_backdrop(NULL, BACKDROP_NONE);
672 static void drag_backdrop_dropped(GtkWidget *drop_box,
673 const guchar *path,
674 GtkWidget *dialog)
676 struct stat info;
677 Radios *radios;
679 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
680 g_return_if_fail(radios != NULL);
682 if (mc_stat(path, &info))
684 delayed_error(
685 _("Can't access '%s':\n%s"), path,
686 g_strerror(errno));
687 return;
690 if (S_ISDIR(info.st_mode))
692 /* Use this program to set the backdrop */
693 pinboard_set_backdrop_app(path);
695 else if (S_ISREG(info.st_mode))
696 set_backdrop(path, radios_get_value(radios));
697 else
698 delayed_error(_("Only files (and certain applications) can be "
699 "used to set the background image."));
702 /* Do this in the idle loop so that we don't try to put an unmanaged
703 * pinboard behind a managed panel (crashes some WMs).
705 static gboolean recreate_pinboard(gchar *name)
707 pinboard_activate(name);
708 g_free(name);
710 return FALSE;
713 static void pinboard_check_options(void)
715 GdkColor n_fg, n_bg, n_shadow;
717 gdk_color_parse(o_pinboard_fg_colour.value, &n_fg);
718 gdk_color_parse(o_pinboard_bg_colour.value, &n_bg);
719 gdk_color_parse(o_pinboard_shadow_colour.value, &n_shadow);
721 if (o_override_redirect.has_changed && current_pinboard)
723 gchar *name;
724 name = g_strdup(current_pinboard->name);
725 pinboard_activate(NULL);
726 gtk_idle_add((GtkFunction) recreate_pinboard, name);
729 tasklist_set_active(o_pinboard_tasklist.int_value && current_pinboard);
731 if (gdk_color_equal(&n_fg, &pin_text_fg_col) == 0 ||
732 gdk_color_equal(&n_bg, &pin_text_bg_col) == 0 ||
733 gdk_color_equal(&n_shadow, &pin_text_shadow_col) == 0 ||
734 o_pinboard_shadow_labels.has_changed ||
735 o_label_font.has_changed)
737 pin_text_fg_col = n_fg;
738 pin_text_bg_col = n_bg;
739 pin_text_shadow_col = n_shadow;
740 update_pinboard_font();
742 if (current_pinboard)
744 GtkWidget *w = current_pinboard->window;
745 GdkColormap *cm;
747 cm = gtk_widget_get_colormap(w);
749 gdk_colormap_alloc_color(cm, &n_bg, FALSE, TRUE);
750 gtk_widget_modify_bg(w, GTK_STATE_NORMAL, &n_bg);
752 gdk_gc_set_rgb_fg_color(current_pinboard->shadow_gc,
753 &n_shadow);
755 abandon_backdrop_app(current_pinboard);
756 reload_backdrop(current_pinboard,
757 current_pinboard->backdrop,
758 current_pinboard->backdrop_style);
760 reshape_all();
763 tasklist_style_changed();
767 static gint end_wink(gpointer data)
769 pinboard_wink_item(NULL, FALSE);
770 return FALSE;
773 /* Sets the appearance from the options and updates the size request of
774 * the image.
776 static void set_size_and_style(PinIcon *pi)
778 Icon *icon = (Icon *) pi;
779 MaskedPixmap *image = icon->item->image;
780 int iwidth = image->width;
781 int iheight = image->height;
783 gtk_widget_modify_fg(pi->label, GTK_STATE_PRELIGHT, &pin_text_fg_col);
784 gtk_widget_modify_bg(pi->label, GTK_STATE_PRELIGHT, &pin_text_bg_col);
785 gtk_widget_modify_fg(pi->label, GTK_STATE_NORMAL, &pin_text_fg_col);
786 gtk_widget_modify_bg(pi->label, GTK_STATE_NORMAL, &pin_text_bg_col);
787 widget_modify_font(pi->label, pinboard_font);
789 wrapped_label_set_text(WRAPPED_LABEL(pi->label), icon->item->leafname);
791 gtk_widget_set_size_request(pi->widget, iwidth, iheight);
794 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi)
796 static GtkWidgetClass *parent_class = NULL;
797 Icon *icon = (Icon *) pi;
798 DirItem *item = icon->item;
799 MaskedPixmap *image = item->image;
800 int iwidth = image->width;
801 int iheight = image->height;
802 int x, y;
804 if (!parent_class)
806 gpointer c = ((GTypeInstance *) widget)->g_class;
807 parent_class = (GtkWidgetClass *) g_type_class_peek_parent(c);
810 x = pi->widget->allocation.x;
811 y = pi->widget->allocation.y;
813 gdk_gc_set_clip_region(pi->widget->style->black_gc, event->region);
815 render_pixbuf(icon->selected ? image->pixbuf_lit : image->pixbuf,
816 pi->widget->window,
817 pi->widget->style->black_gc,
818 x, y, iwidth, iheight);
820 if (item->flags & ITEM_FLAG_SYMLINK)
822 render_pixbuf(im_symlink->pixbuf, pi->widget->window,
823 pi->widget->style->black_gc,
824 x, y, -1, -1);
826 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
828 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
829 ? im_mounted
830 : im_unmounted;
832 render_pixbuf(mp->pixbuf, pi->widget->window,
833 pi->widget->style->black_gc,
834 x, y, -1, -1);
837 gdk_gc_set_clip_region(pi->widget->style->black_gc, NULL);
839 if (icon->selected)
841 GtkStyle *style = pi->label->style;
842 GdkGC *gc = style->bg_gc[GTK_STATE_SELECTED];
844 gdk_gc_set_clip_region(gc, event->region);
845 gdk_draw_rectangle(pi->label->window, gc, TRUE,
846 pi->label->allocation.x,
847 pi->label->allocation.y,
848 pi->label->allocation.width,
849 pi->label->allocation.height);
850 gdk_gc_set_clip_region(gc, NULL);
852 else if (o_pinboard_shadow_labels.int_value)
853 draw_label_shadow((WrappedLabel *) pi->label, event->region);
855 /* Draw children */
856 gdk_gc_set_clip_region(pi->label->style->fg_gc[GTK_STATE_NORMAL],
857 event->region);
858 (parent_class->expose_event)(widget, event);
859 gdk_gc_set_clip_region(pi->label->style->fg_gc[GTK_STATE_NORMAL], NULL);
861 /* Stop the button effect */
862 return TRUE;
865 static gint draw_wink(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi)
867 gint x, y, width, height;
869 if (current_wink_icon != pi)
870 return FALSE;
872 x = widget->allocation.x;
873 y = widget->allocation.y;
874 width = widget->allocation.width;
875 height = widget->allocation.height;
877 gdk_draw_rectangle(widget->window,
878 pi->widget->style->white_gc,
879 FALSE,
880 x, y, width - 1, height - 1);
881 gdk_draw_rectangle(widget->window,
882 pi->widget->style->black_gc,
883 FALSE,
884 x + 1, y + 1, width - 3, height - 3);
886 return FALSE;
889 static gboolean enter_notify(GtkWidget *widget,
890 GdkEventCrossing *event,
891 PinIcon *pi)
893 icon_may_update((Icon *) pi);
894 pin_icon_set_tip(pi);
895 return TRUE;
898 static gint leave_notify(GtkWidget *widget,
899 GdkEventCrossing *event,
900 PinIcon *pi)
902 return TRUE;
905 static void select_lasso(void)
907 GList *next;
908 int minx, miny, maxx, maxy;
910 g_return_if_fail(lasso_in_progress == TRUE);
912 minx = MIN(lasso_rect_x1, lasso_rect_x2);
913 miny = MIN(lasso_rect_y1, lasso_rect_y2);
914 maxx = MAX(lasso_rect_x1, lasso_rect_x2);
915 maxy = MAX(lasso_rect_y1, lasso_rect_y2);
917 for (next = current_pinboard->icons; next; next = next->next)
919 PinIcon *pi = (PinIcon *) next->data;
920 GtkAllocation *alloc = &pi->win->allocation;
921 int cx = alloc->x + alloc->width / 2;
922 int cy = alloc->y + alloc->height / 2;
924 if (cx > minx && cx < maxx && cy > miny && cy < maxy)
925 icon_set_selected((Icon *) pi, TRUE);
929 static void cancel_lasso(void)
931 draw_lasso();
932 lasso_in_progress = FALSE;
935 static void pinboard_lasso_box(int start_x, int start_y)
937 if (lasso_in_progress)
938 cancel_lasso();
939 lasso_in_progress = TRUE;
940 lasso_rect_x1 = lasso_rect_x2 = start_x;
941 lasso_rect_y1 = lasso_rect_y2 = start_y;
943 draw_lasso();
946 static gint lasso_motion(GtkWidget *widget, GdkEventMotion *event, gpointer d)
948 if (!lasso_in_progress)
949 return FALSE;
951 if (lasso_rect_x2 != event->x || lasso_rect_y2 != event->y)
953 draw_lasso();
954 lasso_rect_x2 = event->x;
955 lasso_rect_y2 = event->y;
956 draw_lasso();
959 return FALSE;
962 /* Mark the area of the screen covered by the lasso box for redraw */
963 static void draw_lasso(void)
965 GdkRectangle area, edge;
967 if (!lasso_in_progress)
968 return;
970 area.x = MIN(lasso_rect_x1, lasso_rect_x2);
971 area.y = MIN(lasso_rect_y1, lasso_rect_y2);
972 area.width = ABS(lasso_rect_x1 - lasso_rect_x2);
973 area.height = ABS(lasso_rect_y1 - lasso_rect_y2);
975 edge.x = area.x;
976 edge.y = area.y;
977 edge.width = area.width;
979 edge.height = 2; /* Top */
980 gdk_window_invalidate_rect(current_pinboard->window->window,
981 &edge, TRUE);
983 edge.y += area.height - 2; /* Bottom */
984 gdk_window_invalidate_rect(current_pinboard->window->window,
985 &edge, TRUE);
987 edge.y = area.y;
988 edge.height = area.height;
989 edge.width = 2; /* Left */
990 gdk_window_invalidate_rect(current_pinboard->window->window,
991 &edge, TRUE);
993 edge.x += area.width - 2; /* Right */
994 gdk_window_invalidate_rect(current_pinboard->window->window,
995 &edge, TRUE);
998 static void perform_action(PinIcon *pi, GdkEventButton *event)
1000 BindAction action;
1001 Icon *icon = (Icon *) pi;
1003 action = bind_lookup_bev(pi ? BIND_PINBOARD_ICON : BIND_PINBOARD,
1004 event);
1006 /* Actions that can happen with or without an icon */
1007 switch (action)
1009 case ACT_LASSO_CLEAR:
1010 icon_select_only(NULL);
1011 /* (no break) */
1012 case ACT_LASSO_MODIFY:
1013 pinboard_lasso_box(event->x, event->y);
1014 return;
1015 case ACT_CLEAR_SELECTION:
1016 icon_select_only(NULL);
1017 return;
1018 case ACT_POPUP_MENU:
1019 dnd_motion_ungrab();
1020 pinboard_show_menu(event, pi);
1021 return;
1022 case ACT_IGNORE:
1023 return;
1024 default:
1025 break;
1028 g_return_if_fail(pi != NULL);
1030 switch (action)
1032 case ACT_OPEN_ITEM:
1033 dnd_motion_ungrab();
1034 pinboard_wink_item(pi, TRUE);
1035 if (event->type == GDK_2BUTTON_PRESS)
1036 icon_set_selected(icon, FALSE);
1037 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
1038 break;
1039 case ACT_EDIT_ITEM:
1040 dnd_motion_ungrab();
1041 pinboard_wink_item(pi, TRUE);
1042 if (event->type == GDK_2BUTTON_PRESS)
1043 icon_set_selected(icon, FALSE);
1044 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
1045 break;
1046 case ACT_PRIME_AND_SELECT:
1047 if (!icon->selected)
1048 icon_select_only(icon);
1049 dnd_motion_start(MOTION_READY_FOR_DND);
1050 break;
1051 case ACT_PRIME_AND_TOGGLE:
1052 icon_set_selected(icon, !icon->selected);
1053 dnd_motion_start(MOTION_READY_FOR_DND);
1054 break;
1055 case ACT_PRIME_FOR_DND:
1056 dnd_motion_start(MOTION_READY_FOR_DND);
1057 break;
1058 case ACT_TOGGLE_SELECTED:
1059 icon_set_selected(icon, !icon->selected);
1060 break;
1061 case ACT_SELECT_EXCL:
1062 icon_select_only(icon);
1063 break;
1064 default:
1065 g_warning("Unsupported action : %d\n", action);
1066 break;
1070 static void forward_to_root(GdkEventButton *event)
1072 XButtonEvent xev;
1074 if (event->type == GDK_BUTTON_PRESS)
1076 xev.type = ButtonPress;
1077 XUngrabPointer(gdk_display, event->time);
1079 else
1080 xev.type = ButtonRelease;
1082 xev.window = gdk_x11_get_default_root_xwindow();
1083 xev.root = xev.window;
1084 xev.subwindow = None;
1085 xev.time = event->time;
1086 xev.x = event->x_root; /* Needed for icewm */
1087 xev.y = event->y_root;
1088 xev.x_root = event->x_root;
1089 xev.y_root = event->y_root;
1090 xev.state = event->state;
1091 xev.button = event->button;
1092 xev.same_screen = True;
1094 XSendEvent(gdk_display, xev.window, False,
1095 ButtonPressMask | ButtonReleaseMask, (XEvent *) &xev);
1098 #define FORWARDED_BUTTON(pi, b) ((b) == 2 || \
1099 (((b) == 3 || (b) == 1) && o_forward_buttons_13.int_value && !pi))
1101 /* pi is NULL if this is a root event */
1102 static gboolean button_release_event(GtkWidget *widget,
1103 GdkEventButton *event,
1104 PinIcon *pi)
1106 if (FORWARDED_BUTTON(pi, event->button))
1107 forward_to_root(event);
1108 else if (dnd_motion_release(event))
1110 if (motion_buttons_pressed == 0 && lasso_in_progress)
1112 select_lasso();
1113 cancel_lasso();
1115 return FALSE;
1118 perform_action(pi, event);
1120 return TRUE;
1123 /* pi is NULL if this is a root event */
1124 static gboolean button_press_event(GtkWidget *widget,
1125 GdkEventButton *event,
1126 PinIcon *pi)
1128 /* Just in case we've jumped in front of everything... */
1129 gdk_window_lower(current_pinboard->window->window);
1131 if (FORWARDED_BUTTON(pi, event->button))
1132 forward_to_root(event);
1133 else if (dnd_motion_press(widget, event))
1134 perform_action(pi, event);
1136 return TRUE;
1139 static void start_drag(PinIcon *pi, GdkEventMotion *event)
1141 GtkWidget *widget = pi->win;
1142 Icon *icon = (Icon *) pi;
1144 if (!icon->selected)
1146 tmp_icon_selected = TRUE;
1147 icon_select_only(icon);
1150 g_return_if_fail(icon_selection != NULL);
1152 pinboard_drag_in_progress = icon;
1154 if (icon_selection->next == NULL)
1155 drag_one_item(widget, event, icon->path, icon->item, NULL);
1156 else
1158 guchar *uri_list;
1160 uri_list = icon_create_uri_list();
1161 drag_selection(widget, event, uri_list);
1162 g_free(uri_list);
1166 /* An icon is being dragged around... */
1167 static gint icon_motion_notify(GtkWidget *widget,
1168 GdkEventMotion *event,
1169 PinIcon *pi)
1171 if (motion_state == MOTION_READY_FOR_DND)
1173 if (dnd_motion_moved(event))
1174 start_drag(pi, event);
1175 return TRUE;
1178 return FALSE;
1181 static void backdrop_from_xml(xmlNode *node)
1183 gchar *style;
1185 g_free(current_pinboard->backdrop);
1186 current_pinboard->backdrop = xmlNodeGetContent(node);
1188 style = xmlGetProp(node, "style");
1190 if (style)
1192 current_pinboard->backdrop_style =
1193 g_strcasecmp(style, "Tiled") == 0 ? BACKDROP_TILE :
1194 g_strcasecmp(style, "Scaled") == 0 ? BACKDROP_SCALE :
1195 g_strcasecmp(style, "Stretched") == 0 ? BACKDROP_STRETCH :
1196 g_strcasecmp(style, "Centred") == 0 ? BACKDROP_CENTRE :
1197 g_strcasecmp(style, "Program") == 0 ? BACKDROP_PROGRAM :
1198 BACKDROP_NONE;
1199 g_free(style);
1201 else
1202 current_pinboard->backdrop_style = BACKDROP_TILE;
1205 /* Create one pinboard icon for each icon in the doc */
1206 static void pinboard_load_from_xml(xmlDocPtr doc)
1208 xmlNodePtr node, root;
1209 char *tmp, *label, *path, *shortcut;
1210 int x, y;
1212 root = xmlDocGetRootElement(doc);
1214 for (node = root->xmlChildrenNode; node; node = node->next)
1216 if (node->type != XML_ELEMENT_NODE)
1217 continue;
1218 if (strcmp(node->name, "backdrop") == 0)
1220 backdrop_from_xml(node);
1221 continue;
1223 if (strcmp(node->name, "icon") != 0)
1224 continue;
1226 tmp = xmlGetProp(node, "x");
1227 if (!tmp)
1228 continue;
1229 x = atoi(tmp);
1230 g_free(tmp);
1232 tmp = xmlGetProp(node, "y");
1233 if (!tmp)
1234 continue;
1235 y = atoi(tmp);
1236 g_free(tmp);
1238 label = xmlGetProp(node, "label");
1239 if (!label)
1240 label = g_strdup("<missing label>");
1241 path = xmlNodeGetContent(node);
1242 if (!path)
1243 path = g_strdup("<missing path>");
1244 shortcut = xmlGetProp(node, "shortcut");
1246 pinboard_pin(path, label, x, y, shortcut);
1248 g_free(path);
1249 g_free(label);
1250 g_free(shortcut);
1254 /* Called for each line in the pinboard file while loading a new board.
1255 * Only used for old-format files when converting to XML.
1257 static const char *pin_from_file(gchar *line)
1259 gchar *leaf = NULL;
1260 int x, y, n;
1262 if (*line == '<')
1264 gchar *end;
1266 end = strchr(line + 1, '>');
1267 if (!end)
1268 return _("Missing '>' in icon label");
1270 leaf = g_strndup(line + 1, end - line - 1);
1272 line = end + 1;
1274 while (isspace(*line))
1275 line++;
1276 if (*line != ',')
1277 return _("Missing ',' after icon label");
1278 line++;
1281 if (sscanf(line, " %d , %d , %n", &x, &y, &n) < 2)
1282 return NULL; /* Ignore format errors */
1284 pinboard_pin(line + n, leaf, x, y, NULL);
1286 g_free(leaf);
1288 return NULL;
1291 /* Write the current state of the pinboard to the current pinboard file */
1292 static void pinboard_save(void)
1294 guchar *save = NULL;
1295 guchar *save_new = NULL;
1296 GList *next;
1297 xmlDocPtr doc = NULL;
1298 xmlNodePtr root;
1300 g_return_if_fail(current_pinboard != NULL);
1302 if (strchr(current_pinboard->name, '/'))
1303 save = g_strdup(current_pinboard->name);
1304 else
1306 guchar *leaf;
1308 leaf = g_strconcat("pb_", current_pinboard->name, NULL);
1309 save = choices_find_path_save(leaf, PROJECT, TRUE);
1310 g_free(leaf);
1313 if (!save)
1314 return;
1316 doc = xmlNewDoc("1.0");
1317 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "pinboard", NULL));
1319 root = xmlDocGetRootElement(doc);
1321 if (current_pinboard->backdrop)
1323 BackdropStyle style = current_pinboard->backdrop_style;
1324 xmlNodePtr tree;
1326 tree = xmlNewTextChild(root, NULL, "backdrop",
1327 current_pinboard->backdrop);
1328 xmlSetProp(tree, "style",
1329 style == BACKDROP_TILE ? "Tiled" :
1330 style == BACKDROP_CENTRE ? "Centred" :
1331 style == BACKDROP_SCALE ? "Scaled" :
1332 style == BACKDROP_STRETCH ? "Stretched" :
1333 "Program");
1336 for (next = current_pinboard->icons; next; next = next->next)
1338 xmlNodePtr tree;
1339 PinIcon *pi = (PinIcon *) next->data;
1340 Icon *icon = (Icon *) pi;
1341 char *tmp;
1343 tree = xmlNewTextChild(root, NULL, "icon", icon->src_path);
1345 tmp = g_strdup_printf("%d", pi->x);
1346 xmlSetProp(tree, "x", tmp);
1347 g_free(tmp);
1349 tmp = g_strdup_printf("%d", pi->y);
1350 xmlSetProp(tree, "y", tmp);
1351 g_free(tmp);
1353 xmlSetProp(tree, "label", icon->item->leafname);
1354 if (icon->shortcut)
1355 xmlSetProp(tree, "shortcut", icon->shortcut);
1358 save_new = g_strconcat(save, ".new", NULL);
1359 if (save_xml_file(doc, save_new) || rename(save_new, save))
1360 delayed_error(_("Error saving pinboard %s: %s"),
1361 save, g_strerror(errno));
1362 g_free(save_new);
1364 g_free(save);
1365 if (doc)
1366 xmlFreeDoc(doc);
1369 static void snap_to_grid(int *x, int *y)
1371 int step = o_pinboard_grid_step.int_value;
1373 *x = ((*x + step / 2) / step) * step;
1374 *y = ((*y + step / 2) / step) * step;
1377 /* Convert (x,y) from a centre point to a window position */
1378 static void offset_from_centre(PinIcon *pi, int *x, int *y)
1380 gboolean clamp = o_pinboard_clamp_icons.int_value;
1381 GtkRequisition req;
1383 gtk_widget_size_request(pi->win, &req);
1385 *x -= req.width >> 1;
1386 *y -= req.height >> 1;
1387 *x = CLAMP(*x, 0, screen_width - (clamp ? req.width : 0));
1388 *y = CLAMP(*y, 0, screen_height - (clamp ? req.height : 0));
1391 /* Same as drag_set_dest(), but for pinboard icons */
1392 static void drag_set_pinicon_dest(PinIcon *pi)
1394 GtkObject *obj = GTK_OBJECT(pi->win);
1396 make_drop_target(pi->win, 0);
1398 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1399 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1400 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1403 /* Called during the drag when the mouse is in a widget registered
1404 * as a drop target. Returns TRUE if we can accept the drop.
1406 static gboolean drag_motion(GtkWidget *widget,
1407 GdkDragContext *context,
1408 gint x,
1409 gint y,
1410 guint time,
1411 PinIcon *pi)
1413 GdkDragAction action = context->suggested_action;
1414 const char *type = NULL;
1415 Icon *icon = (Icon *) pi;
1416 DirItem *item = icon->item;
1418 if (gtk_drag_get_source_widget(context) == widget)
1420 g_dataset_set_data(context, "drop_dest_type",
1421 (gpointer) drop_dest_pass_through);
1422 return FALSE; /* Can't drag something to itself! */
1425 if (icon->selected)
1426 goto out; /* Can't drag a selection to itself */
1428 type = dnd_motion_item(context, &item);
1430 if (!item)
1431 type = NULL;
1432 out:
1433 /* We actually must pretend to accept the drop, even if the
1434 * directory isn't writeable, so that the spring-opening
1435 * thing works.
1438 /* Don't allow drops to non-writeable directories */
1439 if (o_dnd_spring_open.int_value == FALSE &&
1440 type == drop_dest_dir &&
1441 access(icon->path, W_OK) != 0)
1443 type = NULL;
1446 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1447 if (type)
1449 gdk_drag_status(context, action, time);
1450 g_dataset_set_data_full(context, "drop_dest_path",
1451 g_strdup(icon->path), g_free);
1452 if (type == drop_dest_dir)
1453 dnd_spring_load(context, NULL);
1455 pinboard_wink_item(pi, FALSE);
1457 else
1458 gdk_drag_status(context, 0, time);
1460 /* Always return TRUE to stop the pinboard getting the events */
1461 return TRUE;
1464 static gboolean pinboard_shadow = FALSE;
1465 static gint shadow_x, shadow_y;
1466 #define SHADOW_SIZE (ICON_WIDTH)
1468 static gboolean bg_expose(GtkWidget *widget,
1469 GdkEventExpose *event, gpointer data)
1471 GdkRectangle clipbox;
1472 gpointer gclass = ((GTypeInstance *) widget)->g_class;
1473 gboolean double_buffer;
1475 gdk_gc_set_clip_region(widget->style->white_gc, event->region);
1476 gdk_gc_set_clip_region(widget->style->black_gc, event->region);
1478 gdk_region_get_clipbox(event->region, &clipbox);
1480 double_buffer = (clipbox.width * clipbox.height) < 20000;
1481 if (double_buffer)
1482 gdk_window_begin_paint_region(widget->window, event->region);
1484 /* Clear the area to the background image */
1486 GtkStyle *style = current_pinboard->window->style;
1487 GdkGC *gc = style->bg_gc[GTK_STATE_NORMAL];
1489 gdk_gc_set_clip_region(gc, event->region);
1490 if (style->bg_pixmap[GTK_STATE_NORMAL])
1492 gdk_gc_set_ts_origin(gc, 0, 0);
1493 gdk_gc_set_fill(gc, GDK_TILED);
1494 gdk_gc_set_tile(gc, style->bg_pixmap[GTK_STATE_NORMAL]);
1497 gdk_draw_rectangle(current_pinboard->window->window, gc, TRUE,
1498 clipbox.x, clipbox.y,
1499 clipbox.width, clipbox.height);
1500 if (style->bg_pixmap[GTK_STATE_NORMAL])
1501 gdk_gc_set_fill(gc, GDK_SOLID);
1503 gdk_gc_set_clip_region(gc, NULL);
1506 if (pinboard_shadow)
1508 gdk_draw_rectangle(widget->window,
1509 widget->style->white_gc, FALSE,
1510 shadow_x, shadow_y,
1511 SHADOW_SIZE, SHADOW_SIZE);
1512 gdk_draw_rectangle(widget->window,
1513 widget->style->black_gc, FALSE,
1514 shadow_x + 1, shadow_y + 1,
1515 SHADOW_SIZE - 2, SHADOW_SIZE - 2);
1518 if (lasso_in_progress)
1520 GdkRectangle area;
1522 area.x = MIN(lasso_rect_x1, lasso_rect_x2);
1523 area.y = MIN(lasso_rect_y1, lasso_rect_y2);
1524 area.width = ABS(lasso_rect_x1 - lasso_rect_x2);
1525 area.height = ABS(lasso_rect_y1 - lasso_rect_y2);
1527 if (area.width > 4 && area.height > 4)
1529 gdk_draw_rectangle(widget->window,
1530 widget->style->white_gc, FALSE,
1531 area.x, area.y,
1532 area.width - 1, area.height - 1);
1533 gdk_draw_rectangle(widget->window,
1534 widget->style->black_gc, FALSE,
1535 area.x + 1, area.y + 1,
1536 area.width - 3, area.height - 3);
1540 gdk_gc_set_clip_region(widget->style->white_gc, NULL);
1541 gdk_gc_set_clip_region(widget->style->black_gc, NULL);
1543 ((GtkWidgetClass *) gclass)->expose_event(widget, event);
1545 if (double_buffer)
1546 gdk_window_end_paint(widget->window);
1548 return TRUE;
1551 /* Draw a 'shadow' under an icon being dragged, showing where
1552 * it will land.
1554 static void pinboard_set_shadow(gboolean on)
1556 GdkRectangle area;
1558 if (pinboard_shadow)
1560 area.x = shadow_x;
1561 area.y = shadow_y;
1562 area.width = SHADOW_SIZE + 1;
1563 area.height = SHADOW_SIZE + 1;
1565 gdk_window_invalidate_rect(current_pinboard->window->window,
1566 &area, TRUE);
1569 if (on)
1571 int old_x = shadow_x, old_y = shadow_y;
1573 gdk_window_get_pointer(current_pinboard->fixed->window,
1574 &shadow_x, &shadow_y, NULL);
1575 snap_to_grid(&shadow_x, &shadow_y);
1576 shadow_x -= SHADOW_SIZE / 2;
1577 shadow_y -= SHADOW_SIZE / 2;
1580 if (pinboard_shadow && shadow_x == old_x && shadow_y == old_y)
1581 return;
1583 area.x = shadow_x;
1584 area.y = shadow_y;
1585 area.width = SHADOW_SIZE + 1;
1586 area.height = SHADOW_SIZE + 1;
1588 gdk_window_invalidate_rect(current_pinboard->window->window,
1589 &area, TRUE);
1592 pinboard_shadow = on;
1595 /* Called when dragging some pinboard icons finishes */
1596 void pinboard_move_icons(void)
1598 int x = shadow_x, y = shadow_y;
1599 PinIcon *pi = (PinIcon *) pinboard_drag_in_progress;
1600 int width, height;
1601 int dx, dy;
1602 GList *next;
1604 g_return_if_fail(pi != NULL);
1606 x += SHADOW_SIZE / 2;
1607 y += SHADOW_SIZE / 2;
1608 snap_to_grid(&x, &y);
1610 if (pi->x == x && pi->y == y)
1611 return;
1613 /* Find out how much the dragged icon moved (after snapping).
1614 * Move all selected icons by the same amount.
1616 dx = x - pi->x;
1617 dy = y - pi->y;
1619 /* Move the other selected icons to keep the same relative
1620 * position.
1622 for (next = icon_selection; next; next = next->next)
1624 PinIcon *pi = (PinIcon *) next->data;
1625 int nx, ny;
1627 g_return_if_fail(IS_PIN_ICON(pi));
1629 pi->x += dx;
1630 pi->y += dy;
1631 nx = pi->x;
1632 ny = pi->y;
1634 gdk_drawable_get_size(pi->win->window, &width, &height);
1635 offset_from_centre(pi, &nx, &ny);
1637 fixed_move_fast(GTK_FIXED(current_pinboard->fixed),
1638 pi->win, nx, ny);
1641 pinboard_save();
1644 static void drag_leave(GtkWidget *widget,
1645 GdkDragContext *context,
1646 guint32 time,
1647 PinIcon *pi)
1649 pinboard_wink_item(NULL, FALSE);
1650 dnd_spring_abort();
1653 static gboolean bg_drag_leave(GtkWidget *widget,
1654 GdkDragContext *context,
1655 guint32 time,
1656 gpointer data)
1658 pinboard_set_shadow(FALSE);
1659 return TRUE;
1662 static gboolean bg_drag_motion(GtkWidget *widget,
1663 GdkDragContext *context,
1664 gint x,
1665 gint y,
1666 guint time,
1667 gpointer data)
1669 /* Dragging from the pinboard to the pinboard is not allowed */
1671 if (!provides(context, text_uri_list))
1672 return FALSE;
1674 pinboard_set_shadow(TRUE);
1676 gdk_drag_status(context,
1677 context->suggested_action == GDK_ACTION_ASK
1678 ? GDK_ACTION_LINK : context->suggested_action,
1679 time);
1680 return TRUE;
1683 static void drag_end(GtkWidget *widget,
1684 GdkDragContext *context,
1685 PinIcon *pi)
1687 pinboard_drag_in_progress = NULL;
1688 if (tmp_icon_selected)
1690 icon_select_only(NULL);
1691 tmp_icon_selected = FALSE;
1695 /* Something which affects all the icons has changed - reshape
1696 * and redraw all of them.
1698 static void reshape_all(void)
1700 GList *next;
1702 g_return_if_fail(current_pinboard != NULL);
1704 for (next = current_pinboard->icons; next; next = next->next)
1706 Icon *icon = (Icon *) next->data;
1707 pinboard_reshape_icon(icon);
1711 /* Turns off the pinboard. Does not call gtk_main_quit. */
1712 static void pinboard_clear(void)
1714 GList *next;
1716 g_return_if_fail(current_pinboard != NULL);
1718 tasklist_set_active(FALSE);
1720 next = current_pinboard->icons;
1721 while (next)
1723 PinIcon *pi = (PinIcon *) next->data;
1725 next = next->next;
1727 gtk_widget_destroy(pi->win);
1730 gtk_widget_destroy(current_pinboard->window);
1732 abandon_backdrop_app(current_pinboard);
1734 g_object_unref(current_pinboard->shadow_gc);
1735 current_pinboard->shadow_gc = NULL;
1737 g_free(current_pinboard->name);
1738 null_g_free(&current_pinboard);
1740 number_of_windows--;
1743 static gpointer parent_class;
1745 static void pin_icon_destroy(Icon *icon)
1747 PinIcon *pi = (PinIcon *) icon;
1749 g_return_if_fail(pi->win != NULL);
1751 gtk_widget_hide(pi->win); /* Stops flicker - stupid GtkFixed! */
1752 gtk_widget_destroy(pi->win);
1755 static void pinboard_remove_items(void)
1757 g_return_if_fail(icon_selection != NULL);
1759 while (icon_selection)
1760 icon_destroy((Icon *) icon_selection->data);
1762 pinboard_save();
1765 static void pin_icon_update(Icon *icon)
1767 pinboard_reshape_icon(icon);
1768 pinboard_save();
1771 static gboolean pin_icon_same_group(Icon *icon, Icon *other)
1773 return IS_PIN_ICON(other);
1776 static void pin_wink_icon(Icon *icon)
1778 pinboard_wink_item((PinIcon *) icon, TRUE);
1781 static void pin_icon_class_init(gpointer gclass, gpointer data)
1783 IconClass *icon = (IconClass *) gclass;
1785 parent_class = g_type_class_peek_parent(gclass);
1787 icon->destroy = pin_icon_destroy;
1788 icon->redraw = pinboard_reshape_icon;
1789 icon->update = pin_icon_update;
1790 icon->wink = pin_wink_icon;
1791 icon->remove_items = pinboard_remove_items;
1792 icon->same_group = pin_icon_same_group;
1795 static void pin_icon_init(GTypeInstance *object, gpointer gclass)
1799 static GType pin_icon_get_type(void)
1801 static GType type = 0;
1803 if (!type)
1805 static const GTypeInfo info =
1807 sizeof (PinIconClass),
1808 NULL, /* base_init */
1809 NULL, /* base_finalise */
1810 pin_icon_class_init,
1811 NULL, /* class_finalise */
1812 NULL, /* class_data */
1813 sizeof(PinIcon),
1814 0, /* n_preallocs */
1815 pin_icon_init
1818 type = g_type_register_static(icon_get_type(),
1819 "PinIcon", &info, 0);
1822 return type;
1825 static PinIcon *pin_icon_new(const char *pathname, const char *name)
1827 PinIcon *pi;
1828 Icon *icon;
1830 pi = g_object_new(pin_icon_get_type(), NULL);
1831 icon = (Icon *) pi;
1833 icon_set_path(icon, pathname, name);
1835 return pi;
1838 /* Called when the window widget is somehow destroyed */
1839 static void pin_icon_destroyed(PinIcon *pi)
1841 g_return_if_fail(pi->win != NULL);
1843 pi->win = NULL;
1845 pinboard_wink_item(NULL, FALSE);
1847 if (pinboard_drag_in_progress == (Icon *) pi)
1848 pinboard_drag_in_progress = NULL;
1850 if (current_pinboard)
1851 current_pinboard->icons =
1852 g_list_remove(current_pinboard->icons, pi);
1854 g_object_unref(pi);
1857 /* Set the tooltip */
1858 static void pin_icon_set_tip(PinIcon *pi)
1860 XMLwrapper *ai;
1861 xmlNode *node;
1862 Icon *icon = (Icon *) pi;
1864 g_return_if_fail(pi != NULL);
1866 ai = appinfo_get(icon->path, icon->item);
1868 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
1870 guchar *str;
1871 str = xmlNodeListGetString(node->doc,
1872 node->xmlChildrenNode, 1);
1873 if (str)
1875 gtk_tooltips_set_tip(tooltips, pi->win, str, NULL);
1876 g_free(str);
1879 else
1880 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
1882 if (ai)
1883 g_object_unref(ai);
1886 static void pinboard_show_menu(GdkEventButton *event, PinIcon *pi)
1888 int pos[3];
1890 pos[0] = event->x_root;
1891 pos[1] = event->y_root;
1892 pos[2] = 1;
1894 icon_prepare_menu((Icon *) pi, TRUE);
1896 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
1897 position_menu,
1898 (gpointer) pos, event->button, event->time);
1901 static void create_pinboard_window(Pinboard *pinboard)
1903 GtkWidget *win;
1905 g_return_if_fail(pinboard->window == NULL);
1907 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1908 gtk_widget_set_style(win, gtk_widget_get_default_style());
1910 gtk_widget_set_double_buffered(win, FALSE);
1911 gtk_widget_set_app_paintable(win, TRUE);
1912 gtk_widget_set_name(win, "rox-pinboard");
1913 pinboard->window = win;
1914 pinboard->fixed = gtk_fixed_new();
1915 gtk_container_add(GTK_CONTAINER(win), pinboard->fixed);
1917 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Pinboard", PROJECT);
1919 gtk_widget_set_size_request(win, screen_width, screen_height);
1920 gtk_widget_realize(win);
1921 gtk_window_move(GTK_WINDOW(win), 0, 0);
1922 make_panel_window(win);
1924 /* TODO: Use gdk function when it supports this type */
1926 GdkAtom desktop_type;
1928 desktop_type = gdk_atom_intern("_NET_WM_WINDOW_TYPE_DESKTOP",
1929 FALSE);
1930 gdk_property_change(win->window,
1931 gdk_atom_intern("_NET_WM_WINDOW_TYPE", FALSE),
1932 gdk_atom_intern("ATOM", FALSE), 32,
1933 GDK_PROP_MODE_REPLACE, (guchar *) &desktop_type, 1);
1936 gtk_widget_add_events(win,
1937 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1938 GDK_EXPOSURE_MASK |
1939 GDK_BUTTON1_MOTION_MASK |
1940 GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK);
1941 g_signal_connect(win, "button-press-event",
1942 G_CALLBACK(button_press_event), NULL);
1943 g_signal_connect(win, "button-release-event",
1944 G_CALLBACK(button_release_event), NULL);
1945 g_signal_connect(win, "motion-notify-event",
1946 G_CALLBACK(lasso_motion), NULL);
1947 g_signal_connect(pinboard->fixed, "expose_event",
1948 G_CALLBACK(bg_expose), NULL);
1950 /* Drag and drop handlers */
1951 drag_set_pinboard_dest(win);
1952 g_signal_connect(win, "drag_motion", G_CALLBACK(bg_drag_motion), NULL);
1953 g_signal_connect(win, "drag_leave", G_CALLBACK(bg_drag_leave), NULL);
1955 pinboard->shadow_gc = gdk_gc_new(win->window);
1956 gdk_gc_set_rgb_fg_color(pinboard->shadow_gc, &pin_text_shadow_col);
1958 reload_backdrop(current_pinboard, NULL, BACKDROP_NONE);
1960 gtk_widget_show_all(win);
1961 gdk_window_lower(win->window);
1964 /* Load image 'path' and scale according to 'style' */
1965 static GdkPixmap *load_backdrop(const gchar *path, BackdropStyle style)
1967 GdkPixmap *pixmap;
1968 GdkPixbuf *pixbuf;
1969 GError *error = NULL;
1971 pixbuf = gdk_pixbuf_new_from_file(path, &error);
1972 if (error)
1974 delayed_error(_("Error loading backdrop image:\n%s\n"
1975 "Backdrop removed."),
1976 error->message);
1977 g_error_free(error);
1978 set_backdrop(NULL, BACKDROP_NONE);
1979 return NULL;
1982 if (style == BACKDROP_STRETCH)
1984 GdkPixbuf *old = pixbuf;
1986 pixbuf = gdk_pixbuf_scale_simple(old,
1987 screen_width, screen_height,
1988 GDK_INTERP_HYPER);
1990 g_object_unref(old);
1992 else if (style == BACKDROP_CENTRE || style == BACKDROP_SCALE)
1994 GdkPixbuf *old = pixbuf;
1995 int x, y, width, height;
1996 float scale;
1998 width = gdk_pixbuf_get_width(pixbuf);
1999 height = gdk_pixbuf_get_height(pixbuf);
2001 if (style == BACKDROP_SCALE)
2003 float scale_x, scale_y;
2004 scale_x = screen_width / ((float) width);
2005 scale_y = screen_height / ((float) height);
2006 scale = MIN(scale_x, scale_y);
2008 else
2009 scale = 1;
2011 pixbuf = gdk_pixbuf_new(
2012 gdk_pixbuf_get_colorspace(pixbuf), FALSE,
2013 8, screen_width, screen_height);
2014 gdk_pixbuf_fill(pixbuf, ((pin_text_bg_col.red & 0xff00) << 16) |
2015 ((pin_text_bg_col.green & 0xff00) << 8) |
2016 ((pin_text_bg_col.blue & 0xff00)));
2018 x = (screen_width - width * scale) / 2;
2019 y = (screen_height - height * scale) / 2;
2020 x = MAX(x, 0);
2021 y = MAX(y, 0);
2023 gdk_pixbuf_composite(old, pixbuf,
2024 x, y,
2025 MIN(screen_width, width * scale),
2026 MIN(screen_height, height * scale),
2027 x, y, scale, scale,
2028 GDK_INTERP_NEAREST, 255);
2029 g_object_unref(old);
2032 gdk_pixbuf_render_pixmap_and_mask(pixbuf,
2033 &pixmap, NULL, 0);
2034 g_object_unref(pixbuf);
2036 return pixmap;
2039 static void abandon_backdrop_app(Pinboard *pinboard)
2041 g_return_if_fail(pinboard != NULL);
2043 if (pinboard->to_backdrop_app != -1)
2045 close(pinboard->to_backdrop_app);
2046 close(pinboard->from_backdrop_app);
2047 gtk_input_remove(pinboard->input_tag);
2048 g_string_free(pinboard->input_buffer, TRUE);
2049 pinboard->to_backdrop_app = -1;
2050 pinboard->from_backdrop_app = -1;
2051 pinboard->input_tag = -1;
2052 pinboard->input_buffer = NULL;
2055 g_return_if_fail(pinboard->to_backdrop_app == -1);
2056 g_return_if_fail(pinboard->from_backdrop_app == -1);
2057 g_return_if_fail(pinboard->input_tag == -1);
2058 g_return_if_fail(pinboard->input_buffer == NULL);
2061 /* A single line has been read from the child.
2062 * Processes the command, and replies 'ok' (or abandons the child on error).
2064 static void command_from_backdrop_app(Pinboard *pinboard, const gchar *command)
2066 BackdropStyle style;
2067 const char *ok = "ok\n";
2069 if (strncmp(command, "tile ", 5) == 0)
2071 style = BACKDROP_TILE;
2072 command += 5;
2074 else if (strncmp(command, "scale ", 6) == 0)
2076 style = BACKDROP_SCALE;
2077 command += 6;
2079 else if (strncmp(command, "stretch ", 8) == 0)
2081 style = BACKDROP_STRETCH;
2082 command += 8;
2084 else if (strncmp(command, "centre ", 7) == 0)
2086 style = BACKDROP_CENTRE;
2087 command += 7;
2089 else
2091 g_warning("Invalid command '%s' from backdrop app\n",
2092 command);
2093 abandon_backdrop_app(pinboard);
2094 return;
2097 /* Load the backdrop. May abandon the program if loading fails. */
2098 reload_backdrop(pinboard, command, style);
2100 if (pinboard->to_backdrop_app == -1)
2101 return;
2103 while (*ok)
2105 int sent;
2107 sent = write(pinboard->to_backdrop_app, ok, strlen(ok));
2108 if (sent <= 0)
2110 /* Remote app quit? Not an error. */
2111 abandon_backdrop_app(pinboard);
2112 break;
2114 ok += sent;
2118 static void backdrop_from_child(Pinboard *pinboard,
2119 int src, GdkInputCondition cond)
2121 char buf[256];
2122 int got;
2124 got = read(src, buf, sizeof(buf));
2126 if (got <= 0)
2128 if (got < 0)
2129 g_warning("backdrop_from_child: %s\n",
2130 g_strerror(errno));
2131 abandon_backdrop_app(pinboard);
2132 return;
2135 g_string_append_len(pinboard->input_buffer, buf, got);
2137 while (pinboard->from_backdrop_app != -1)
2139 int len;
2140 char *nl, *command;
2142 nl = strchr(pinboard->input_buffer->str, '\n');
2143 if (!nl)
2144 return; /* Haven't got a whole line yet */
2146 len = nl - pinboard->input_buffer->str;
2147 command = g_strndup(pinboard->input_buffer->str, len);
2148 g_string_erase(pinboard->input_buffer, 0, len + 1);
2150 command_from_backdrop_app(pinboard, command);
2152 g_free(command);
2156 static void reload_backdrop(Pinboard *pinboard,
2157 const gchar *backdrop,
2158 BackdropStyle backdrop_style)
2160 GtkStyle *style;
2162 if (backdrop && backdrop_style == BACKDROP_PROGRAM)
2164 const char *argv[] = {NULL, "--backdrop", NULL};
2165 GError *error = NULL;
2167 g_return_if_fail(pinboard->to_backdrop_app == -1);
2168 g_return_if_fail(pinboard->from_backdrop_app == -1);
2169 g_return_if_fail(pinboard->input_tag == -1);
2170 g_return_if_fail(pinboard->input_buffer == NULL);
2172 argv[0] = make_path(backdrop, "AppRun");
2174 /* Run the program. It'll send us a SOAP message and we'll
2175 * get back here with a different style and image.
2178 if (g_spawn_async_with_pipes(NULL, (gchar **) argv, NULL,
2179 G_SPAWN_DO_NOT_REAP_CHILD |
2180 G_SPAWN_SEARCH_PATH,
2181 NULL, NULL, /* Child setup fn */
2182 NULL, /* Child PID */
2183 &pinboard->to_backdrop_app,
2184 &pinboard->from_backdrop_app,
2185 NULL, /* Standard error */
2186 &error))
2188 pinboard->input_buffer = g_string_new(NULL);
2189 pinboard->input_tag = gtk_input_add_full(
2190 pinboard->from_backdrop_app,
2191 GDK_INPUT_READ,
2192 (GdkInputFunction) backdrop_from_child,
2193 NULL, pinboard, NULL);
2195 else
2197 delayed_error("%s", error ? error->message : "(null)");
2198 g_error_free(error);
2200 return;
2203 /* Note: Copying a style does not ref the pixmaps! */
2205 style = gtk_style_copy(gtk_widget_get_style(pinboard->window));
2206 style->bg_pixmap[GTK_STATE_NORMAL] = NULL;
2208 if (backdrop)
2209 style->bg_pixmap[GTK_STATE_NORMAL] =
2210 load_backdrop(backdrop, backdrop_style);
2212 gdk_color_parse(o_pinboard_bg_colour.value,
2213 &style->bg[GTK_STATE_NORMAL]);
2215 gtk_widget_set_style(pinboard->window, style);
2217 g_object_unref(style);
2219 gtk_widget_queue_draw(pinboard->window);
2221 /* Also update root window property (for transparent xterms, etc) */
2222 if (style->bg_pixmap[GTK_STATE_NORMAL])
2224 XID id = GDK_DRAWABLE_XID(style->bg_pixmap[GTK_STATE_NORMAL]);
2225 gdk_property_change(gdk_get_default_root_window(),
2226 gdk_atom_intern("_XROOTPMAP_ID", FALSE),
2227 gdk_atom_intern("PIXMAP", FALSE),
2228 32, GDK_PROP_MODE_REPLACE,
2229 (guchar *) &id, 1);
2231 else
2233 gdk_property_delete(gdk_get_default_root_window(),
2234 gdk_atom_intern("_XROOTPMAP_ID", FALSE));
2238 /* Set and save (path, style) as the new backdrop.
2239 * If style is BACKDROP_PROGRAM, the program is run to get the backdrop.
2240 * Otherwise, the image is displayed now.
2242 static void set_backdrop(const gchar *path, BackdropStyle style)
2244 g_return_if_fail((path == NULL && style == BACKDROP_NONE) ||
2245 (path != NULL && style != BACKDROP_NONE));
2247 if (!current_pinboard)
2249 if (!path)
2250 return;
2251 pinboard_activate("Default");
2252 delayed_error(_("No pinboard was in use... "
2253 "the 'Default' pinboard has been selected. "
2254 "Use 'rox -p=Default' to turn it on in "
2255 "future."));
2256 g_return_if_fail(current_pinboard != NULL);
2259 /* We might have just run the old backdrop program and now
2260 * we're going to set a new one! Seems a bit mean...
2263 abandon_backdrop_app(current_pinboard);
2265 g_free(current_pinboard->backdrop);
2266 current_pinboard->backdrop = g_strdup(path);
2267 current_pinboard->backdrop_style = style;
2268 reload_backdrop(current_pinboard,
2269 current_pinboard->backdrop,
2270 current_pinboard->backdrop_style);
2272 pinboard_save();
2274 if (set_backdrop_dialog)
2276 DropBox *box = g_object_get_data(G_OBJECT(set_backdrop_dialog),
2277 "rox-dropbox");
2278 g_return_if_fail(box != NULL);
2279 drop_box_set_path(box, current_pinboard->backdrop);
2280 update_radios(set_backdrop_dialog);
2284 #define SEARCH_STEP 32
2286 static void search_free(GdkRectangle *rect, GdkRegion *used,
2287 int *outer, int od, int omax,
2288 int *inner, int id, int imax)
2290 *outer = od > 0 ? 0 : omax;
2291 while (*outer >= 0 && *outer <= omax)
2293 *inner = id > 0 ? 0 : imax;
2294 while (*inner >= 0 && *inner <= imax)
2296 if (gdk_region_rect_in(used, rect) ==
2297 GDK_OVERLAP_RECTANGLE_OUT)
2298 return;
2299 *inner += id;
2302 *outer += od;
2305 rect->x = -1;
2306 rect->y = -1;
2309 /* Finds a free area on the pinboard large enough for the width and height
2310 * of the given rectangle, by filling in the x and y fields of 'rect'.
2311 * The search order respects user preferences.
2312 * If no area is free, returns any old area.
2314 static void find_free_rect(Pinboard *pinboard, GdkRectangle *rect)
2316 GdkRegion *used;
2317 GList *next;
2318 GdkRectangle used_rect;
2319 int dx = SEARCH_STEP, dy = SEARCH_STEP;
2321 used = gdk_region_new();
2323 panel_mark_used(used);
2325 /* Subtract the used areas... */
2327 next = GTK_FIXED(pinboard->fixed)->children;
2328 for (; next; next = next->next)
2330 GtkFixedChild *fix = (GtkFixedChild *) next->data;
2332 if (!GTK_WIDGET_VISIBLE(fix->widget))
2333 continue;
2335 used_rect.x = fix->x;
2336 used_rect.y = fix->y;
2337 used_rect.width = fix->widget->requisition.width;
2338 used_rect.height = fix->widget->requisition.height;
2340 gdk_region_union_with_rect(used, &used_rect);
2343 /* Find the first free area (yes, this isn't exactly pretty, but
2344 * it works). If you know a better (fast!) algorithm, let me know!
2348 if (o_iconify_start.int_value == CORNER_TOP_RIGHT ||
2349 o_iconify_start.int_value == CORNER_BOTTOM_RIGHT)
2350 dx = -SEARCH_STEP;
2352 if (o_iconify_start.int_value == CORNER_BOTTOM_LEFT ||
2353 o_iconify_start.int_value == CORNER_BOTTOM_RIGHT)
2354 dy = -SEARCH_STEP;
2356 if (o_iconify_dir.int_value == DIR_VERT)
2358 search_free(rect, used,
2359 &rect->x, dx, screen_width - rect->width,
2360 &rect->y, dy, screen_height - rect->height);
2362 else
2364 search_free(rect, used,
2365 &rect->y, dy, screen_height - rect->height,
2366 &rect->x, dx, screen_width - rect->width);
2369 gdk_region_destroy(used);
2371 if (rect->x == -1)
2373 rect->x = 0;
2374 rect->y = 0;
2378 /* Icon's size, shape or appearance has changed - update the display */
2379 static void pinboard_reshape_icon(Icon *icon)
2381 PinIcon *pi = (PinIcon *) icon;
2382 int x = pi->x, y = pi->y;
2384 set_size_and_style(pi);
2385 offset_from_centre(pi, &x, &y);
2387 if (pi->win->allocation.x != x || pi->win->allocation.y != y)
2389 fixed_move_fast(GTK_FIXED(current_pinboard->fixed),
2390 pi->win, x, y);
2394 /* Sets the pinboard_font global from the option. Doesn't do anything else. */
2395 static void update_pinboard_font(void)
2397 if (pinboard_font)
2398 pango_font_description_free(pinboard_font);
2399 pinboard_font = o_label_font.value[0] != '\0'
2400 ? pango_font_description_from_string(o_label_font.value)
2401 : NULL;
2404 static void radios_changed(gpointer data)
2406 GObject *dialog = G_OBJECT(data);
2407 DropBox *drop_box;
2408 const guchar *path;
2409 Radios *radios;
2411 g_return_if_fail(dialog != NULL);
2413 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
2414 drop_box = g_object_get_data(G_OBJECT(dialog), "rox-dropbox");
2416 g_return_if_fail(radios != NULL);
2417 g_return_if_fail(drop_box != NULL);
2418 g_return_if_fail(current_pinboard != NULL);
2420 if (current_pinboard->backdrop_style != BACKDROP_PROGRAM)
2422 path = drop_box_get_path(drop_box);
2423 if (path)
2424 set_backdrop(path, radios_get_value(radios));
2428 static void update_radios(GtkWidget *dialog)
2430 Radios *radios;
2431 GtkWidget *hbox;
2433 g_return_if_fail(dialog != NULL);
2435 radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
2436 hbox = g_object_get_data(G_OBJECT(dialog), "rox-radios-hbox");
2438 g_return_if_fail(current_pinboard != NULL);
2439 g_return_if_fail(radios != NULL);
2440 g_return_if_fail(hbox != NULL);
2442 switch (current_pinboard->backdrop_style)
2444 case BACKDROP_TILE:
2445 case BACKDROP_STRETCH:
2446 case BACKDROP_SCALE:
2447 case BACKDROP_CENTRE:
2448 radios_set_value(radios,
2449 current_pinboard->backdrop_style);
2450 gtk_widget_set_sensitive(hbox, TRUE);
2451 break;
2452 default:
2453 gtk_widget_set_sensitive(hbox, FALSE);
2454 radios_set_value(radios, BACKDROP_TILE);
2455 break;