r2402: Changing the icon for a MIME type updates all filer windows.
[rox-filer.git] / ROX-Filer / src / pinboard.c
bloba322ecd0d9281b312ff614c2a764b01f16bf608e
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"
59 static gboolean tmp_icon_selected = FALSE; /* When dragging */
61 struct _Pinboard {
62 guchar *name; /* Leaf name */
63 GList *icons;
64 GtkStyle *style;
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 */
70 gint input_tag;
71 GString *input_buffer;
73 GtkWidget *window; /* Screen-sized window */
74 GtkWidget *fixed;
75 GdkGC *shadow_gc;
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 {
84 IconClass parent;
87 struct _PinIcon {
88 Icon icon;
90 int x, y;
91 GtkWidget *win;
92 GtkWidget *widget; /* The drawing area for the icon */
93 GtkWidget *label;
96 /* The number of pixels between the bottom of the image and the top
97 * of the text.
99 #define GAP 4
101 /* The size of the border around the icon which is used when winking */
102 #define WINK_FRAME 2
104 /* Grid sizes */
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
115 #define DIR_HORZ 0
116 #define DIR_VERT 1
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,
153 PinIcon *pi);
154 static gboolean enter_notify(GtkWidget *widget,
155 GdkEventCrossing *event,
156 PinIcon *pi);
157 static gboolean button_press_event(GtkWidget *widget,
158 GdkEventButton *event,
159 PinIcon *pi);
160 static gint icon_motion_notify(GtkWidget *widget,
161 GdkEventMotion *event,
162 PinIcon *pi);
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,
168 gint x,
169 gint y,
170 guint time,
171 PinIcon *pi);
172 static void drag_set_pinicon_dest(PinIcon *pi);
173 static void drag_leave(GtkWidget *widget,
174 GdkDragContext *context,
175 guint32 time,
176 PinIcon *pi);
177 static gboolean bg_drag_motion(GtkWidget *widget,
178 GdkDragContext *context,
179 gint x,
180 gint y,
181 guint time,
182 gpointer data);
183 static gboolean bg_drag_leave(GtkWidget *widget,
184 GdkDragContext *context,
185 guint32 time,
186 gpointer data);
187 static gboolean bg_expose(GtkWidget *window,
188 GdkEventExpose *event, gpointer data);
189 static void drag_end(GtkWidget *widget,
190 GdkDragContext *context,
191 PinIcon *pi);
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,
211 gint x,
212 gint y,
213 GtkSelectionData *selection_data,
214 guint drag_info,
215 guint32 time,
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);
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",
233 "#000");
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",
239 GRID_STEP_COARSE);
240 option_add_int(&o_pinboard_tasklist, "pinboard_tasklist", TRUE);
241 option_add_int(&o_forward_buttons_13, "pinboard_forward_buttons_13",
242 FALSE);
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
259 * pinboard.
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 */
267 if (name && !*name)
268 name = NULL;
270 if (old_board)
271 pinboard_clear();
273 if (!name)
275 if (number_of_windows < 1 && gtk_main_level() > 0)
276 gtk_main_quit();
278 gdk_property_delete(gdk_get_default_root_window(),
279 gdk_atom_intern("_XROOTPMAP_ID", FALSE));
280 return;
283 number_of_windows++;
285 slash = strchr(name, '/');
286 if (slash)
288 if (access(name, F_OK))
289 path = NULL; /* File does not (yet) exist */
290 else
291 path = g_strdup(name);
293 else
295 guchar *leaf;
297 leaf = g_strconcat("pb_", name, NULL);
298 path = choices_find_path_load(leaf, PROJECT);
299 g_free(leaf);
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);
315 loading_pinboard++;
316 if (path)
318 xmlDocPtr doc;
319 doc = xmlParseFile(path);
320 if (doc)
322 pinboard_load_from_xml(doc);
323 xmlFreeDoc(doc);
324 reload_backdrop(current_pinboard,
325 current_pinboard->backdrop,
326 current_pinboard->backdrop_style);
328 else
330 parse_file(path, pin_from_file);
331 info_message(_("Your old pinboard file has been "
332 "converted to the new XML format."));
333 pinboard_save();
335 g_free(path);
337 else
338 pinboard_pin(home_dir, "Home",
339 4 + ICON_WIDTH / 2,
340 4 + ICON_HEIGHT / 2,
341 NULL);
342 loading_pinboard--;
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;
355 return NULL;
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
366 * being cleared.
368 void pinboard_add_widget(GtkWidget *widget)
370 GtkRequisition req;
371 GdkRectangle rect;
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;
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_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)
481 pinboard_save();
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;
494 if (old == pi)
495 return;
497 current_wink_icon = pi;
499 if (old)
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);
508 if (pi)
510 gtk_widget_queue_draw(pi->win);
511 gdk_window_process_updates(pi->widget->window, TRUE);
513 if (timeout)
514 wink_timeout = gtk_timeout_add(300, end_wink, NULL);
515 else
516 wink_timeout = -1;
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)
525 XMLwrapper *ai;
526 DirItem *item;
527 gboolean can_set;
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."));
538 diritem_free(item);
539 return;
542 ai = appinfo_get(app, item);
543 diritem_free(item);
545 can_set = ai && xml_get_section(ai, ROX_NS, "CanSetBackdrop") != NULL;
546 if (ai)
547 g_object_unref(ai);
549 if (can_set)
550 set_backdrop(app, BACKDROP_PROGRAM);
551 else
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;
564 GtkBox *vbox;
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,
573 NULL);
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)),
593 _("Scaled"));
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)),
599 _("Tiled"));
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),
612 GDK_ACTION_COPY);
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,
641 gint x,
642 gint y,
643 GtkSelectionData *selection_data,
644 guint drag_info,
645 guint32 time,
646 GtkWidget *dialog)
648 struct stat info;
649 const gchar *path = NULL;
650 GList *uris;
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);
659 g_list_free(uris);
661 if (!path)
663 delayed_error(
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."));
668 return;
671 if (mc_stat(path, &info))
673 delayed_error(
674 _("Can't access '%s':\n%s"), path,
675 g_strerror(errno));
676 return;
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))
686 GtkWidget *radio;
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);
692 return;
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);
699 return;
702 set_backdrop(path, BACKDROP_TILE);
704 else
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);
715 g_free(name);
717 return FALSE;
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)
730 gchar *name;
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;
752 GdkColormap *cm;
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,
760 &n_shadow);
762 /* Only redraw the background if there is no image */
763 if (!current_pinboard->backdrop)
764 reload_backdrop(current_pinboard,
765 NULL, BACKDROP_NONE);
767 reshape_all();
770 tasklist_style_changed();
774 static gint end_wink(gpointer data)
776 pinboard_wink_item(NULL, FALSE);
777 return FALSE;
780 /* Sets the appearance from the options and updates the size request of
781 * the image.
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;
809 int x, y;
811 if (!parent_class)
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,
822 pi->widget->window,
823 0, 0, /* src */
824 x, y, /* dest */
825 iwidth, iheight,
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,
832 pi->widget->window,
833 0, 0, /* src */
834 x, y,
835 -1, -1,
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
842 ? im_mounted
843 : im_unmounted;
845 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
846 pi->widget->window,
847 0, 0, /* src */
848 x, y,
849 -1, -1,
850 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
851 GDK_RGB_DITHER_NORMAL, 0, 0);
854 if (icon->selected)
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,
870 event->region);
871 draw_label_shadow((WrappedLabel *) pi->label);
872 gdk_gc_set_clip_region(current_pinboard->shadow_gc, NULL);
875 /* Draw children */
876 gdk_gc_set_clip_region(pi->label->style->fg_gc[GTK_STATE_NORMAL],
877 event->region);
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 */
882 return TRUE;
885 static void draw_label_shadow(WrappedLabel *wl)
887 GtkWidget *widget;
888 gint x, y;
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)
909 return FALSE;
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,
918 FALSE,
919 x, y, width - 1, height - 1);
920 gdk_draw_rectangle(widget->window,
921 pi->widget->style->black_gc,
922 FALSE,
923 x + 1, y + 1, width - 3, height - 3);
925 return FALSE;
928 static gboolean enter_notify(GtkWidget *widget,
929 GdkEventCrossing *event,
930 PinIcon *pi)
932 icon_may_update((Icon *) pi);
934 return FALSE;
937 static void select_lasso(void)
939 GList *next;
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)
963 draw_lasso();
964 lasso_in_progress = FALSE;
967 static void pinboard_lasso_box(int start_x, int start_y)
969 if (lasso_in_progress)
970 cancel_lasso();
971 lasso_in_progress = TRUE;
972 lasso_rect_x1 = lasso_rect_x2 = start_x;
973 lasso_rect_y1 = lasso_rect_y2 = start_y;
975 draw_lasso();
978 static gint lasso_motion(GtkWidget *widget, GdkEventMotion *event, gpointer d)
980 if (!lasso_in_progress)
981 return FALSE;
983 if (lasso_rect_x2 != event->x || lasso_rect_y2 != event->y)
985 draw_lasso();
986 lasso_rect_x2 = event->x;
987 lasso_rect_y2 = event->y;
988 draw_lasso();
991 return FALSE;
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)
1000 return;
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);
1007 edge.x = area.x;
1008 edge.y = area.y;
1009 edge.width = area.width;
1011 edge.height = 2; /* Top */
1012 gdk_window_invalidate_rect(current_pinboard->window->window,
1013 &edge, TRUE);
1015 edge.y += area.height - 2; /* Bottom */
1016 gdk_window_invalidate_rect(current_pinboard->window->window,
1017 &edge, TRUE);
1019 edge.y = area.y;
1020 edge.height = area.height;
1021 edge.width = 2; /* Left */
1022 gdk_window_invalidate_rect(current_pinboard->window->window,
1023 &edge, TRUE);
1025 edge.x += area.width - 2; /* Right */
1026 gdk_window_invalidate_rect(current_pinboard->window->window,
1027 &edge, TRUE);
1030 static void perform_action(PinIcon *pi, GdkEventButton *event)
1032 BindAction action;
1033 Icon *icon = (Icon *) pi;
1035 action = bind_lookup_bev(pi ? BIND_PINBOARD_ICON : BIND_PINBOARD,
1036 event);
1038 /* Actions that can happen with or without an icon */
1039 switch (action)
1041 case ACT_LASSO_CLEAR:
1042 icon_select_only(NULL);
1043 /* (no break) */
1044 case ACT_LASSO_MODIFY:
1045 pinboard_lasso_box(event->x, event->y);
1046 return;
1047 case ACT_CLEAR_SELECTION:
1048 icon_select_only(NULL);
1049 return;
1050 case ACT_POPUP_MENU:
1051 dnd_motion_ungrab();
1052 pinboard_show_menu(event, pi);
1053 return;
1054 case ACT_IGNORE:
1055 return;
1056 default:
1057 break;
1060 g_return_if_fail(pi != NULL);
1062 switch (action)
1064 case ACT_OPEN_ITEM:
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);
1070 break;
1071 case ACT_EDIT_ITEM:
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);
1077 break;
1078 case ACT_PRIME_AND_SELECT:
1079 if (!icon->selected)
1080 icon_select_only(icon);
1081 dnd_motion_start(MOTION_READY_FOR_DND);
1082 break;
1083 case ACT_PRIME_AND_TOGGLE:
1084 icon_set_selected(icon, !icon->selected);
1085 dnd_motion_start(MOTION_READY_FOR_DND);
1086 break;
1087 case ACT_PRIME_FOR_DND:
1088 dnd_motion_start(MOTION_READY_FOR_DND);
1089 break;
1090 case ACT_TOGGLE_SELECTED:
1091 icon_set_selected(icon, !icon->selected);
1092 break;
1093 case ACT_SELECT_EXCL:
1094 icon_select_only(icon);
1095 break;
1096 default:
1097 g_warning("Unsupported action : %d\n", action);
1098 break;
1102 static void forward_to_root(GdkEventButton *event)
1104 XButtonEvent xev;
1106 if (event->type == GDK_BUTTON_PRESS)
1108 xev.type = ButtonPress;
1109 XUngrabPointer(gdk_display, event->time);
1111 else
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,
1136 PinIcon *pi)
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)
1144 select_lasso();
1145 cancel_lasso();
1147 return FALSE;
1150 perform_action(pi, event);
1152 return TRUE;
1155 /* pi is NULL if this is a root event */
1156 static gboolean button_press_event(GtkWidget *widget,
1157 GdkEventButton *event,
1158 PinIcon *pi)
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);
1168 return TRUE;
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);
1188 else
1190 guchar *uri_list;
1192 uri_list = icon_create_uri_list();
1193 drag_selection(widget, event, uri_list);
1194 g_free(uri_list);
1198 /* An icon is being dragged around... */
1199 static gint icon_motion_notify(GtkWidget *widget,
1200 GdkEventMotion *event,
1201 PinIcon *pi)
1203 if (motion_state == MOTION_READY_FOR_DND)
1205 if (dnd_motion_moved(event))
1206 start_drag(pi, event);
1207 return TRUE;
1210 return FALSE;
1213 static void backdrop_from_xml(xmlNode *node)
1215 gchar *style;
1217 g_free(current_pinboard->backdrop);
1218 current_pinboard->backdrop = xmlNodeGetContent(node);
1220 style = xmlGetProp(node, "style");
1222 if (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 :
1229 BACKDROP_NONE;
1230 g_free(style);
1232 else
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;
1241 int x, y;
1243 root = xmlDocGetRootElement(doc);
1245 for (node = root->xmlChildrenNode; node; node = node->next)
1247 if (node->type != XML_ELEMENT_NODE)
1248 continue;
1249 if (strcmp(node->name, "backdrop") == 0)
1251 backdrop_from_xml(node);
1252 continue;
1254 if (strcmp(node->name, "icon") != 0)
1255 continue;
1257 tmp = xmlGetProp(node, "x");
1258 if (!tmp)
1259 continue;
1260 x = atoi(tmp);
1261 g_free(tmp);
1263 tmp = xmlGetProp(node, "y");
1264 if (!tmp)
1265 continue;
1266 y = atoi(tmp);
1267 g_free(tmp);
1269 label = xmlGetProp(node, "label");
1270 if (!label)
1271 label = g_strdup("<missing label>");
1272 path = xmlNodeGetContent(node);
1273 if (!path)
1274 path = g_strdup("<missing path>");
1275 shortcut = xmlGetProp(node, "shortcut");
1277 pinboard_pin(path, label, x, y, shortcut);
1279 g_free(path);
1280 g_free(label);
1281 g_free(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)
1290 gchar *leaf = NULL;
1291 int x, y, n;
1293 if (*line == '<')
1295 gchar *end;
1297 end = strchr(line + 1, '>');
1298 if (!end)
1299 return _("Missing '>' in icon label");
1301 leaf = g_strndup(line + 1, end - line - 1);
1303 line = end + 1;
1305 while (isspace(*line))
1306 line++;
1307 if (*line != ',')
1308 return _("Missing ',' after icon label");
1309 line++;
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);
1317 g_free(leaf);
1319 return 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;
1327 GList *next;
1328 xmlDocPtr doc = NULL;
1329 xmlNodePtr root;
1331 g_return_if_fail(current_pinboard != NULL);
1333 if (strchr(current_pinboard->name, '/'))
1334 save = g_strdup(current_pinboard->name);
1335 else
1337 guchar *leaf;
1339 leaf = g_strconcat("pb_", current_pinboard->name, NULL);
1340 save = choices_find_path_save(leaf, PROJECT, TRUE);
1341 g_free(leaf);
1344 if (!save)
1345 return;
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;
1355 xmlNodePtr tree;
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" :
1363 "Program");
1366 for (next = current_pinboard->icons; next; next = next->next)
1368 xmlNodePtr tree;
1369 PinIcon *pi = (PinIcon *) next->data;
1370 Icon *icon = (Icon *) pi;
1371 char *tmp;
1373 tree = xmlNewTextChild(root, NULL, "icon", icon->src_path);
1375 tmp = g_strdup_printf("%d", pi->x);
1376 xmlSetProp(tree, "x", tmp);
1377 g_free(tmp);
1379 tmp = g_strdup_printf("%d", pi->y);
1380 xmlSetProp(tree, "y", tmp);
1381 g_free(tmp);
1383 xmlSetProp(tree, "label", icon->item->leafname);
1384 if (icon->shortcut)
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));
1392 g_free(save_new);
1394 g_free(save);
1395 if (doc)
1396 xmlFreeDoc(doc);
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;
1411 GtkRequisition req;
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,
1438 gint x,
1439 gint y,
1440 guint time,
1441 PinIcon *pi)
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! */
1455 if (icon->selected)
1456 goto out; /* Can't drag a selection to itself */
1458 type = dnd_motion_item(context, &item);
1460 if (!item)
1461 type = NULL;
1462 out:
1463 /* We actually must pretend to accept the drop, even if the
1464 * directory isn't writeable, so that the spring-opening
1465 * thing works.
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)
1473 type = NULL;
1476 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1477 if (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);
1487 else
1488 gdk_drag_status(context, 0, time);
1490 /* Always return TRUE to stop the pinboard getting the events */
1491 return TRUE;
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,
1534 shadow_x, shadow_y,
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)
1544 GdkRectangle area;
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,
1555 area.x, area.y,
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);
1569 return TRUE;
1572 /* Draw a 'shadow' under an icon being dragged, showing where
1573 * it will land.
1575 static void pinboard_set_shadow(gboolean on)
1577 GdkRectangle area;
1579 if (pinboard_shadow)
1581 area.x = shadow_x;
1582 area.y = shadow_y;
1583 area.width = SHADOW_SIZE + 1;
1584 area.height = SHADOW_SIZE + 1;
1586 gdk_window_invalidate_rect(current_pinboard->window->window,
1587 &area, TRUE);
1590 if (on)
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)
1602 return;
1604 area.x = shadow_x;
1605 area.y = shadow_y;
1606 area.width = SHADOW_SIZE + 1;
1607 area.height = SHADOW_SIZE + 1;
1609 gdk_window_invalidate_rect(current_pinboard->window->window,
1610 &area, TRUE);
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;
1621 int width, height;
1622 int dx, dy;
1623 GList *next;
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)
1632 return;
1634 /* Find out how much the dragged icon moved (after snapping).
1635 * Move all selected icons by the same amount.
1637 dx = x - pi->x;
1638 dy = y - pi->y;
1640 /* Move the other selected icons to keep the same relative
1641 * position.
1643 for (next = icon_selection; next; next = next->next)
1645 PinIcon *pi = (PinIcon *) next->data;
1646 int nx, ny;
1648 g_return_if_fail(IS_PIN_ICON(pi));
1650 pi->x += dx;
1651 pi->y += dy;
1652 nx = pi->x;
1653 ny = pi->y;
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),
1659 pi->win, nx, ny);
1662 pinboard_save();
1665 static void drag_leave(GtkWidget *widget,
1666 GdkDragContext *context,
1667 guint32 time,
1668 PinIcon *pi)
1670 pinboard_wink_item(NULL, FALSE);
1671 dnd_spring_abort();
1674 static gboolean bg_drag_leave(GtkWidget *widget,
1675 GdkDragContext *context,
1676 guint32 time,
1677 gpointer data)
1679 pinboard_set_shadow(FALSE);
1680 return TRUE;
1683 static gboolean bg_drag_motion(GtkWidget *widget,
1684 GdkDragContext *context,
1685 gint x,
1686 gint y,
1687 guint time,
1688 gpointer data)
1690 /* Dragging from the pinboard to the pinboard is not allowed */
1692 if (!provides(context, text_uri_list))
1693 return FALSE;
1695 pinboard_set_shadow(TRUE);
1697 gdk_drag_status(context,
1698 context->suggested_action == GDK_ACTION_ASK
1699 ? GDK_ACTION_LINK : context->suggested_action,
1700 time);
1701 return TRUE;
1704 static void drag_end(GtkWidget *widget,
1705 GdkDragContext *context,
1706 PinIcon *pi)
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)
1721 GList *next;
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)
1735 GList *next;
1737 g_return_if_fail(current_pinboard != NULL);
1739 tasklist_set_active(FALSE);
1741 next = current_pinboard->icons;
1742 while (next)
1744 PinIcon *pi = (PinIcon *) next->data;
1746 next = next->next;
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(&current_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);
1782 pinboard_save();
1785 static void pin_icon_update(Icon *icon)
1787 pinboard_reshape_icon(icon);
1788 pinboard_save();
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;
1823 if (!type)
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 */
1833 sizeof(PinIcon),
1834 0, /* n_preallocs */
1835 pin_icon_init
1838 type = g_type_register_static(icon_get_type(),
1839 "PinIcon", &info, 0);
1842 return type;
1845 static PinIcon *pin_icon_new(const char *pathname, const char *name)
1847 PinIcon *pi;
1848 Icon *icon;
1850 pi = g_object_new(pin_icon_get_type(), NULL);
1851 icon = (Icon *) pi;
1853 icon_set_path(icon, pathname, name);
1855 return pi;
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);
1863 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);
1874 g_object_unref(pi);
1877 /* Set the tooltip */
1878 static void pin_icon_set_tip(PinIcon *pi)
1880 XMLwrapper *ai;
1881 xmlNode *node;
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"))))
1890 guchar *str;
1891 str = xmlNodeListGetString(node->doc,
1892 node->xmlChildrenNode, 1);
1893 if (str)
1895 gtk_tooltips_set_tip(tooltips, pi->win, str, NULL);
1896 g_free(str);
1899 else
1900 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
1902 if (ai)
1903 g_object_unref(ai);
1906 static void pinboard_show_menu(GdkEventButton *event, PinIcon *pi)
1908 int pos[3];
1910 pos[0] = event->x_root;
1911 pos[1] = event->y_root;
1912 pos[2] = 1;
1914 icon_prepare_menu((Icon *) pi, TRUE);
1916 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
1917 position_menu,
1918 (gpointer) pos, event->button, event->time);
1921 static void create_pinboard_window(Pinboard *pinboard)
1923 GtkWidget *win;
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",
1949 FALSE);
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 |
1958 GDK_EXPOSURE_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)
1987 GdkPixmap *pixmap;
1988 GdkPixbuf *pixbuf;
1989 GError *error = NULL;
1991 pixbuf = gdk_pixbuf_new_from_file(path, &error);
1992 if (error)
1994 delayed_error(_("Error loading backdrop image:\n%s\n"
1995 "Backdrop removed."),
1996 error->message);
1997 g_error_free(error);
1998 set_backdrop(NULL, BACKDROP_NONE);
1999 return NULL;
2002 if (style == BACKDROP_SCALE)
2004 GdkPixbuf *old = pixbuf;
2006 pixbuf = gdk_pixbuf_scale_simple(old,
2007 screen_width, screen_height,
2008 GDK_INTERP_HYPER);
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;
2027 x = MAX(x, 0);
2028 y = MAX(y, 0);
2030 gdk_pixbuf_composite(old, pixbuf,
2031 x, y,
2032 MIN(screen_width, width),
2033 MIN(screen_height, height),
2034 x, y, 1, 1,
2035 GDK_INTERP_NEAREST, 255);
2036 g_object_unref(old);
2039 gdk_pixbuf_render_pixmap_and_mask(pixbuf,
2040 &pixmap, NULL, 0);
2041 g_object_unref(pixbuf);
2043 return pixmap;
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;
2079 command += 5;
2081 else if (strncmp(command, "scale ", 6) == 0)
2083 style = BACKDROP_SCALE;
2084 command += 6;
2086 else if (strncmp(command, "centre ", 7) == 0)
2088 style = BACKDROP_CENTRE;
2089 command += 7;
2091 else
2093 g_warning("Invalid command '%s' from backdrop app\n",
2094 command);
2095 abandon_backdrop_app(pinboard);
2096 return;
2099 /* Load the backdrop. May abandon the program if loading fails. */
2100 reload_backdrop(pinboard, command, style);
2102 if (pinboard->to_backdrop_app == -1)
2103 return;
2105 while (*ok)
2107 int sent;
2109 sent = write(pinboard->to_backdrop_app, ok, strlen(ok));
2110 if (sent <= 0)
2112 /* Remote app quit? Not an error. */
2113 abandon_backdrop_app(pinboard);
2114 break;
2116 ok += sent;
2120 static void backdrop_from_child(Pinboard *pinboard,
2121 int src, GdkInputCondition cond)
2123 char buf[256];
2124 int got;
2126 got = read(src, buf, sizeof(buf));
2128 if (got <= 0)
2130 if (got < 0)
2131 g_warning("backdrop_from_child: %s\n",
2132 g_strerror(errno));
2133 abandon_backdrop_app(pinboard);
2134 return;
2137 g_string_append_len(pinboard->input_buffer, buf, got);
2139 while (pinboard->from_backdrop_app != -1)
2141 int len;
2142 char *nl, *command;
2144 nl = strchr(pinboard->input_buffer->str, '\n');
2145 if (!nl)
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);
2154 g_free(command);
2158 static void reload_backdrop(Pinboard *pinboard,
2159 const gchar *backdrop,
2160 BackdropStyle backdrop_style)
2162 GtkStyle *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 */
2188 &error))
2190 pinboard->input_buffer = g_string_new(NULL);
2191 pinboard->input_tag = gtk_input_add_full(
2192 pinboard->from_backdrop_app,
2193 GDK_INPUT_READ,
2194 (GdkInputFunction) backdrop_from_child,
2195 NULL, pinboard, NULL);
2197 else
2199 delayed_error("%s", error ? error->message : "(null)");
2200 g_error_free(error);
2202 return;
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;
2210 if (backdrop)
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,
2231 (guchar *) &id, 1);
2233 else
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)
2251 if (!path)
2252 return;
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 "
2257 "future."));
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);
2274 pinboard_save();
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)
2291 return;
2292 *inner += id;
2295 *outer += od;
2298 rect->x = -1;
2299 rect->y = -1;
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)
2309 GdkRegion *used;
2310 GList *next;
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))
2326 continue;
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)
2343 dx = -SEARCH_STEP;
2345 if (o_iconify_start.int_value == CORNER_BOTTOM_LEFT ||
2346 o_iconify_start.int_value == CORNER_BOTTOM_RIGHT)
2347 dy = -SEARCH_STEP;
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);
2355 else
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);
2364 if (rect->x == -1)
2366 rect->x = 0;
2367 rect->y = 0;
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),
2383 pi->win, x, y);
2387 /* Sets the pinboard_font global from the option. Doesn't do anything else. */
2388 static void update_pinboard_font(void)
2390 if (pinboard_font)
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)
2394 : NULL;