r2180: Code tidying (Bernard Jungen).
[rox-filer.git] / ROX-Filer / src / pinboard.c
blob85a3adbd6dac6dc66960bbf31e55ae87b20796b7
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
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() */
58 static gboolean tmp_icon_selected = FALSE; /* When dragging */
60 struct _Pinboard {
61 guchar *name; /* Leaf name */
62 GList *icons;
63 GtkStyle *style;
65 gchar *backdrop; /* Pathname */
66 BackdropStyle backdrop_style;
67 gint to_backdrop_app; /* pipe FD, or -1 */
68 gint from_backdrop_app; /* pipe FD, or -1 */
69 gint input_tag;
70 GString *input_buffer;
72 GtkWidget *window; /* Screen-sized window */
73 GtkWidget *fixed;
74 GdkGC *shadow_gc;
77 #define IS_PIN_ICON(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), pin_icon_get_type())
79 typedef struct _PinIconClass PinIconClass;
80 typedef struct _PinIcon PinIcon;
82 struct _PinIconClass {
83 IconClass parent;
86 struct _PinIcon {
87 Icon icon;
89 int x, y;
90 GtkWidget *win;
91 GtkWidget *widget; /* The drawing area for the icon */
92 GtkWidget *label;
95 /* The number of pixels between the bottom of the image and the top
96 * of the text.
98 #define GAP 4
100 /* The size of the border around the icon which is used when winking */
101 #define WINK_FRAME 2
103 /* Grid sizes */
104 #define GRID_STEP_FINE 2
105 #define GRID_STEP_MED 16
106 #define GRID_STEP_COARSE 32
108 /* Used in options */
109 #define CORNER_TOP_LEFT 0
110 #define CORNER_TOP_RIGHT 1
111 #define CORNER_BOTTOM_LEFT 2
112 #define CORNER_BOTTOM_RIGHT 3
114 #define DIR_HORZ 0
115 #define DIR_VERT 1
117 static PinIcon *current_wink_icon = NULL;
118 static gint wink_timeout;
120 /* Used for the text colours (only) in the icons (and tasklist windows) */
121 GdkColor pin_text_fg_col, pin_text_bg_col;
122 PangoFontDescription *pinboard_font = NULL; /* NULL => Gtk default */
124 static GdkColor pin_text_shadow_col;
126 Pinboard *current_pinboard = NULL;
127 static gint loading_pinboard = 0; /* Non-zero => loading */
129 /* The Icon that was used to start the current drag, if any */
130 Icon *pinboard_drag_in_progress = NULL;
132 /* For selecting groups of icons */
133 static gboolean lasso_in_progress = FALSE;
134 static int lasso_rect_x1, lasso_rect_x2;
135 static int lasso_rect_y1, lasso_rect_y2;
137 static Option o_pinboard_clamp_icons, o_pinboard_grid_step;
138 static Option o_pinboard_fg_colour, o_pinboard_bg_colour;
139 static Option o_pinboard_tasklist, o_forward_buttons_13;
140 static Option o_iconify_start, o_iconify_dir;
141 static Option o_label_font, o_pinboard_shadow_colour;
143 /* tasklist.c needs it */
144 Option o_pinboard_shadow_labels;
146 /* Static prototypes */
147 static GType pin_icon_get_type(void);
148 static void set_size_and_style(PinIcon *pi);
149 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi);
150 static void draw_label_shadow(GtkLabel *label);
151 static gint end_wink(gpointer data);
152 static gboolean button_release_event(GtkWidget *widget,
153 GdkEventButton *event,
154 PinIcon *pi);
155 static gboolean enter_notify(GtkWidget *widget,
156 GdkEventCrossing *event,
157 PinIcon *pi);
158 static gboolean button_press_event(GtkWidget *widget,
159 GdkEventButton *event,
160 PinIcon *pi);
161 static gint icon_motion_notify(GtkWidget *widget,
162 GdkEventMotion *event,
163 PinIcon *pi);
164 static const char *pin_from_file(gchar *line);
165 static void snap_to_grid(int *x, int *y);
166 static void offset_from_centre(PinIcon *pi, int *x, int *y);
167 static gboolean drag_motion(GtkWidget *widget,
168 GdkDragContext *context,
169 gint x,
170 gint y,
171 guint time,
172 PinIcon *pi);
173 static void drag_set_pinicon_dest(PinIcon *pi);
174 static void drag_leave(GtkWidget *widget,
175 GdkDragContext *context,
176 guint32 time,
177 PinIcon *pi);
178 static gboolean bg_drag_motion(GtkWidget *widget,
179 GdkDragContext *context,
180 gint x,
181 gint y,
182 guint time,
183 gpointer data);
184 static gboolean bg_drag_leave(GtkWidget *widget,
185 GdkDragContext *context,
186 guint32 time,
187 gpointer data);
188 static gboolean bg_expose(GtkWidget *window,
189 GdkEventExpose *event, gpointer data);
190 static void drag_end(GtkWidget *widget,
191 GdkDragContext *context,
192 PinIcon *pi);
193 static void reshape_all(void);
194 static void pinboard_check_options(void);
195 static void pinboard_load_from_xml(xmlDocPtr doc);
196 static void pinboard_clear(void);
197 static void pinboard_save(void);
198 static PinIcon *pin_icon_new(const char *pathname, const char *name);
199 static void pin_icon_destroyed(PinIcon *pi);
200 static void pin_icon_set_tip(PinIcon *pi);
201 static void pinboard_show_menu(GdkEventButton *event, PinIcon *pi);
202 static void create_pinboard_window(Pinboard *pinboard);
203 static void reload_backdrop(Pinboard *pinboard,
204 const gchar *backdrop,
205 BackdropStyle backdrop_style);
206 static void set_backdrop(const gchar *path, BackdropStyle style);
207 static void pinboard_reshape_icon(Icon *icon);
208 static gint draw_wink(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi);
209 static void abandon_backdrop_app(Pinboard *pinboard);
210 static void drag_backdrop_dropped(GtkWidget *frame,
211 GdkDragContext *context,
212 gint x,
213 gint y,
214 GtkSelectionData *selection_data,
215 guint drag_info,
216 guint32 time,
217 GtkWidget *dialog);
218 static void backdrop_response(GtkWidget *dialog, gint response, gpointer data);
219 static void find_free_rect(Pinboard *pinboard, GdkRectangle *rect);
220 static void update_pinboard_font(void);
221 static void draw_lasso(void);
222 static gint lasso_motion(GtkWidget *widget, GdkEventMotion *event, gpointer d);
225 /****************************************************************
226 * EXTERNAL INTERFACE *
227 ****************************************************************/
229 void pinboard_init(void)
231 option_add_string(&o_pinboard_fg_colour, "pinboard_fg_colour", "#fff");
232 option_add_string(&o_pinboard_bg_colour, "pinboard_bg_colour", "#888");
233 option_add_string(&o_pinboard_shadow_colour, "pinboard_shadow_colour",
234 "#000");
235 option_add_string(&o_label_font, "label_font", "");
236 option_add_int(&o_pinboard_shadow_labels, "pinboard_shadow_labels", 1);
238 option_add_int(&o_pinboard_clamp_icons, "pinboard_clamp_icons", 1);
239 option_add_int(&o_pinboard_grid_step, "pinboard_grid_step",
240 GRID_STEP_COARSE);
241 option_add_int(&o_pinboard_tasklist, "pinboard_tasklist", TRUE);
242 option_add_int(&o_forward_buttons_13, "pinboard_forward_buttons_13",
243 FALSE);
245 option_add_int(&o_iconify_start, "iconify_start", CORNER_TOP_RIGHT);
246 option_add_int(&o_iconify_dir, "iconify_dir", DIR_VERT);
248 option_add_notify(pinboard_check_options);
250 gdk_color_parse(o_pinboard_fg_colour.value, &pin_text_fg_col);
251 gdk_color_parse(o_pinboard_bg_colour.value, &pin_text_bg_col);
252 gdk_color_parse(o_pinboard_shadow_colour.value, &pin_text_shadow_col);
253 update_pinboard_font();
256 /* Load 'pb_<pinboard>' config file from Choices (if it exists)
257 * and make it the current pinboard.
258 * Any existing pinned items are removed. You must call this
259 * at least once before using the pinboard. NULL disables the
260 * pinboard.
262 void pinboard_activate(const gchar *name)
264 Pinboard *old_board = current_pinboard;
265 guchar *path, *slash;
267 /* Treat an empty name the same as NULL */
268 if (name && !*name)
269 name = NULL;
271 if (old_board)
272 pinboard_clear();
274 if (!name)
276 if (number_of_windows < 1 && gtk_main_level() > 0)
277 gtk_main_quit();
279 gdk_property_delete(gdk_get_default_root_window(),
280 gdk_atom_intern("_XROOTPMAP_ID", FALSE));
281 return;
284 number_of_windows++;
286 slash = strchr(name, '/');
287 if (slash)
289 if (access(name, F_OK))
290 path = NULL; /* File does not (yet) exist */
291 else
292 path = g_strdup(name);
294 else
296 guchar *leaf;
298 leaf = g_strconcat("pb_", name, NULL);
299 path = choices_find_path_load(leaf, PROJECT);
300 g_free(leaf);
303 current_pinboard = g_new(Pinboard, 1);
304 current_pinboard->name = g_strdup(name);
305 current_pinboard->icons = NULL;
306 current_pinboard->window = NULL;
307 current_pinboard->backdrop = NULL;
308 current_pinboard->backdrop_style = BACKDROP_NONE;
309 current_pinboard->to_backdrop_app = -1;
310 current_pinboard->from_backdrop_app = -1;
311 current_pinboard->input_tag = -1;
312 current_pinboard->input_buffer = NULL;
314 create_pinboard_window(current_pinboard);
316 loading_pinboard++;
317 if (path)
319 xmlDocPtr doc;
320 doc = xmlParseFile(path);
321 if (doc)
323 pinboard_load_from_xml(doc);
324 xmlFreeDoc(doc);
325 reload_backdrop(current_pinboard,
326 current_pinboard->backdrop,
327 current_pinboard->backdrop_style);
329 else
331 parse_file(path, pin_from_file);
332 info_message(_("Your old pinboard file has been "
333 "converted to the new XML format."));
334 pinboard_save();
336 g_free(path);
338 else
339 pinboard_pin(home_dir, "Home",
340 4 + ICON_WIDTH / 2,
341 4 + ICON_HEIGHT / 2,
342 NULL);
343 loading_pinboard--;
345 if (o_pinboard_tasklist.int_value)
346 tasklist_set_active(TRUE);
349 /* Return the window of the current pinboard, or NULL.
350 * Used to make sure lowering the panels doesn't lose them...
352 GdkWindow *pinboard_get_window(void)
354 if (current_pinboard)
355 return current_pinboard->window->window;
356 return NULL;
359 GdkGC *pinboard_get_shadow_gc(void)
361 g_return_val_if_fail(current_pinboard != NULL, NULL);
363 return current_pinboard->shadow_gc;
366 const char *pinboard_get_name(void)
368 g_return_val_if_fail(current_pinboard != NULL, NULL);
370 return current_pinboard->name;
373 /* Add widget to the pinboard. Caller is responsible for coping with pinboard
374 * being cleared.
376 void pinboard_add_widget(GtkWidget *widget)
378 GtkRequisition req;
379 GdkRectangle rect;
381 g_return_if_fail(current_pinboard != NULL);
383 gtk_fixed_put(GTK_FIXED(current_pinboard->fixed), widget, 0, 0);
385 gtk_widget_size_request(widget, &req);
387 rect.width = req.width;
388 rect.height = req.height;
389 find_free_rect(current_pinboard, &rect);
391 gtk_fixed_move(GTK_FIXED(current_pinboard->fixed),
392 widget, rect.x, rect.y);
395 /* Add a new icon to the background.
396 * 'path' should be an absolute pathname.
397 * 'x' and 'y' are the coordinates of the point in the middle of the text.
398 * 'name' is the name to use. If NULL then the leafname of path is used.
400 * name and path are in UTF-8 for Gtk+-2.0 only.
402 void pinboard_pin(const gchar *path, const gchar *name, int x, int y,
403 const gchar *shortcut)
405 GtkWidget *align, *vbox;
406 GdkWindow *events;
407 PinIcon *pi;
408 Icon *icon;
410 g_return_if_fail(path != NULL);
411 g_return_if_fail(current_pinboard != NULL);
413 pi = pin_icon_new(path, name);
414 icon = (Icon *) pi;
415 pi->x = x;
416 pi->y = y;
418 /* This is a bit complicated...
420 * An icon needs to be a NO_WINDOW widget so that the image can
421 * blend with the background (A ParentRelative window also works, but
422 * is slow, causes the xfree86's memory consumption to grow without
423 * bound, and doesn't even get freed when the filer quits!).
425 * However, the icon also needs to have a window, so we get events
426 * delivered correctly. The solution is to float an InputOnly window
427 * over the icon. Since GtkButton works the same way, we just use
428 * that :-)
431 /* Button takes the initial ref of Icon */
432 pi->win = gtk_button_new();
433 gtk_container_set_border_width(GTK_CONTAINER(pi->win), WINK_FRAME);
434 g_signal_connect(pi->win, "expose-event", G_CALLBACK(draw_wink), pi);
435 gtk_button_set_relief(GTK_BUTTON(pi->win), GTK_RELIEF_NONE);
437 vbox = gtk_vbox_new(FALSE, 0);
438 gtk_container_add(GTK_CONTAINER(pi->win), vbox);
440 align = gtk_alignment_new(0.5, 0.5, 0, 0);
441 pi->widget = gtk_hbox_new(FALSE, 0); /* Placeholder */
442 gtk_container_add(GTK_CONTAINER(align), pi->widget);
444 gtk_box_pack_start(GTK_BOX(vbox), align, FALSE, TRUE, 0);
445 drag_set_pinicon_dest(pi);
446 g_signal_connect(pi->win, "drag_data_get",
447 G_CALLBACK(drag_data_get), NULL);
449 pi->label = gtk_label_new(icon->item->leafname);
450 gtk_label_set_line_wrap(GTK_LABEL(pi->label), TRUE);
451 gtk_box_pack_start(GTK_BOX(vbox), pi->label, TRUE, TRUE, 0);
453 gtk_fixed_put(GTK_FIXED(current_pinboard->fixed), pi->win, 0, 0);
455 snap_to_grid(&x, &y);
456 pi->x = x;
457 pi->y = y;
458 gtk_widget_show_all(pi->win);
459 pinboard_reshape_icon((Icon *) pi);
461 gtk_widget_realize(pi->win);
462 events = GTK_BUTTON(pi->win)->event_window;
463 gdk_window_set_events(events,
464 GDK_EXPOSURE_MASK |
465 GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
466 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
467 GDK_BUTTON1_MOTION_MASK | GDK_ENTER_NOTIFY_MASK |
468 GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK);
469 g_signal_connect(pi->win, "enter-notify-event",
470 G_CALLBACK(enter_notify), pi);
471 g_signal_connect(pi->win, "button-press-event",
472 G_CALLBACK(button_press_event), pi);
473 g_signal_connect(pi->win, "button-release-event",
474 G_CALLBACK(button_release_event), pi);
475 g_signal_connect(pi->win, "motion-notify-event",
476 G_CALLBACK(icon_motion_notify), pi);
477 g_signal_connect(pi->win, "expose-event",
478 G_CALLBACK(draw_icon), pi);
479 g_signal_connect_swapped(pi->win, "style-set",
480 G_CALLBACK(pinboard_reshape_icon), pi);
481 g_signal_connect_swapped(pi->win, "destroy",
482 G_CALLBACK(pin_icon_destroyed), pi);
484 current_pinboard->icons = g_list_prepend(current_pinboard->icons, pi);
485 pin_icon_set_tip(pi);
487 icon_set_shortcut(icon, shortcut);
489 if (!loading_pinboard)
490 pinboard_save();
493 /* Put a border around the icon, briefly.
494 * If icon is NULL then cancel any existing wink.
495 * The icon will automatically unhighlight unless timeout is FALSE,
496 * in which case you must call this function again (with NULL or another
497 * icon) to remove the highlight.
499 static void pinboard_wink_item(PinIcon *pi, gboolean timeout)
501 PinIcon *old = current_wink_icon;
503 if (old == pi)
504 return;
506 current_wink_icon = pi;
508 if (old)
510 gtk_widget_queue_draw(old->win);
511 gdk_window_process_updates(old->widget->window, TRUE);
513 if (wink_timeout != -1)
514 gtk_timeout_remove(wink_timeout);
517 if (pi)
519 gtk_widget_queue_draw(pi->win);
520 gdk_window_process_updates(pi->widget->window, TRUE);
522 if (timeout)
523 wink_timeout = gtk_timeout_add(300, end_wink, NULL);
524 else
525 wink_timeout = -1;
529 /* 'app' is saved as the new application to set the backdrop. It will then be
530 * run, and should communicate with the filer as described in the manual.
532 void pinboard_set_backdrop_app(const gchar *app)
534 XMLwrapper *ai;
535 DirItem *item;
536 gboolean can_set;
538 item = diritem_new("");
539 diritem_restat(app, item, NULL);
540 if (!(item->flags & ITEM_FLAG_APPDIR))
542 delayed_error(_("The backdrop handler must be an application "
543 "directory. Drag an application directory "
544 "into the Set Backdrop dialog box, or (for "
545 "programmers) pass it to the SOAP "
546 "SetBackdropApp method."));
547 diritem_free(item);
548 return;
551 ai = appinfo_get(app, item);
552 diritem_free(item);
554 can_set = ai && xml_get_section(ai, ROX_NS, "CanSetBackdrop") != NULL;
555 if (ai)
556 g_object_unref(ai);
558 if (can_set)
559 set_backdrop(app, BACKDROP_PROGRAM);
560 else
561 delayed_error(_("You can only set the backdrop to an image "
562 "or to a program which knows how to "
563 "manage ROX-Filer's backdrop.\n\n"
564 "Programmers: the application's AppInfo.xml "
565 "must contain the CanSetBackdrop element, as "
566 "described in ROX-Filer's manual."));
569 /* Open a dialog box allowing the user to set the backdrop */
570 void pinboard_set_backdrop(void)
572 GtkWidget *dialog, *frame, *label, *radio, *hbox;
573 GtkBox *vbox;
574 GtkTargetEntry targets[] = {
575 {"text/uri-list", 0, TARGET_URI_LIST},
578 dialog = gtk_dialog_new_with_buttons(_("Set backdrop"), NULL,
579 GTK_DIALOG_NO_SEPARATOR,
580 GTK_STOCK_CLEAR, GTK_RESPONSE_NO,
581 GTK_STOCK_OK, GTK_RESPONSE_OK,
582 NULL);
583 vbox = GTK_BOX(GTK_DIALOG(dialog)->vbox);
585 gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
587 label = gtk_label_new(_("Display backdrop image:"));
588 gtk_misc_set_padding(GTK_MISC(label), 4, 0);
589 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
590 gtk_box_pack_start(vbox, label, TRUE, TRUE, 4);
592 /* The Centred, Scaled, Tiled radios... */
593 hbox = gtk_hbox_new(TRUE, 2);
594 gtk_box_pack_start(vbox, hbox, TRUE, TRUE, 4);
596 radio = gtk_radio_button_new_with_label(NULL, _("Centred"));
597 g_object_set_data(G_OBJECT(dialog), "radio_centred", radio);
598 gtk_box_pack_start(GTK_BOX(hbox), radio, FALSE, TRUE, 0);
600 radio = gtk_radio_button_new_with_label(
601 gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)),
602 _("Scaled"));
603 g_object_set_data(G_OBJECT(dialog), "radio_scaled", radio);
604 gtk_box_pack_start(GTK_BOX(hbox), radio, FALSE, TRUE, 0);
606 radio = gtk_radio_button_new_with_label(
607 gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio)),
608 _("Tiled"));
609 gtk_box_pack_start(GTK_BOX(hbox), radio, FALSE, TRUE, 0);
611 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE);
613 /* The drop area... */
614 frame = gtk_frame_new(NULL);
615 gtk_box_pack_start(vbox, frame, TRUE, TRUE, 4);
616 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
617 gtk_container_set_border_width(GTK_CONTAINER(frame), 4);
619 gtk_drag_dest_set(frame, GTK_DEST_DEFAULT_ALL,
620 targets, sizeof(targets) / sizeof(*targets),
621 GDK_ACTION_COPY);
622 g_signal_connect(frame, "drag_data_received",
623 G_CALLBACK(drag_backdrop_dropped), dialog);
625 label = gtk_label_new(_("Drop an image here"));
626 gtk_misc_set_padding(GTK_MISC(label), 10, 20);
627 gtk_container_add(GTK_CONTAINER(frame), label);
629 g_signal_connect(dialog, "response",
630 G_CALLBACK(backdrop_response), NULL);
631 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
633 gtk_widget_show_all(dialog);
636 /****************************************************************
637 * INTERNAL FUNCTIONS *
638 ****************************************************************/
640 static void backdrop_response(GtkWidget *dialog, gint response, gpointer data)
642 if (response == GTK_RESPONSE_NO)
643 set_backdrop(NULL, BACKDROP_NONE);
645 gtk_widget_destroy(dialog);
648 static void drag_backdrop_dropped(GtkWidget *frame,
649 GdkDragContext *context,
650 gint x,
651 gint y,
652 GtkSelectionData *selection_data,
653 guint drag_info,
654 guint32 time,
655 GtkWidget *dialog)
657 struct stat info;
658 const gchar *path = NULL;
659 GList *uris;
661 if (!selection_data->data)
662 return; /* Timeout? */
664 uris = uri_list_to_glist(selection_data->data);
666 if (g_list_length(uris) == 1)
667 path = get_local_path((guchar *) uris->data);
668 g_list_free(uris);
670 if (!path)
672 delayed_error(
673 _("You should drop a single (local) image file "
674 "onto the drop box - that image will be "
675 "used for the desktop background. You can also "
676 "drag certain applications onto this box."));
677 return;
680 if (mc_stat(path, &info))
682 delayed_error(
683 _("Can't access '%s':\n%s"), path,
684 g_strerror(errno));
685 return;
688 if (S_ISDIR(info.st_mode))
690 /* Use this program to set the backdrop */
691 pinboard_set_backdrop_app(path);
693 else if (S_ISREG(info.st_mode))
695 GtkWidget *radio;
697 radio = g_object_get_data(G_OBJECT(dialog), "radio_scaled");
698 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio)))
700 set_backdrop(path, BACKDROP_SCALE);
701 return;
704 radio = g_object_get_data(G_OBJECT(dialog), "radio_centred");
705 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio)))
707 set_backdrop(path, BACKDROP_CENTRE);
708 return;
711 set_backdrop(path, BACKDROP_TILE);
713 else
714 delayed_error(_("Only files (and certain applications) can be "
715 "used to set the background image."));
718 /* Do this in the idle loop so that we don't try to put an unmanaged
719 * pinboard behind a managed panel (crashes some WMs).
721 static gboolean recreate_pinboard(gchar *name)
723 pinboard_activate(name);
724 g_free(name);
726 return FALSE;
729 static void pinboard_check_options(void)
731 GdkColor n_fg, n_bg, n_shadow;
733 gdk_color_parse(o_pinboard_fg_colour.value, &n_fg);
734 gdk_color_parse(o_pinboard_bg_colour.value, &n_bg);
735 gdk_color_parse(o_pinboard_shadow_colour.value, &n_shadow);
737 if (o_override_redirect.has_changed && current_pinboard)
739 gchar *name;
740 name = g_strdup(current_pinboard->name);
741 pinboard_activate(NULL);
742 gtk_idle_add((GtkFunction) recreate_pinboard, name);
745 tasklist_set_active(o_pinboard_tasklist.int_value && current_pinboard);
747 if (gdk_color_equal(&n_fg, &pin_text_fg_col) == 0 ||
748 gdk_color_equal(&n_bg, &pin_text_bg_col) == 0 ||
749 gdk_color_equal(&n_shadow, &pin_text_shadow_col) == 0 ||
750 o_pinboard_shadow_labels.has_changed ||
751 o_label_font.has_changed)
753 pin_text_fg_col = n_fg;
754 pin_text_bg_col = n_bg;
755 pin_text_shadow_col = n_shadow;
756 update_pinboard_font();
758 if (current_pinboard)
760 GtkWidget *w = current_pinboard->window;
761 GdkColormap *cm;
763 cm = gtk_widget_get_colormap(w);
765 gdk_colormap_alloc_color(cm, &n_bg, FALSE, TRUE);
766 gtk_widget_modify_bg(w, GTK_STATE_NORMAL, &n_bg);
768 gdk_gc_set_rgb_fg_color(current_pinboard->shadow_gc,
769 &n_shadow);
771 /* Only redraw the background if there is no image */
772 if (!current_pinboard->backdrop)
773 reload_backdrop(current_pinboard,
774 NULL, BACKDROP_NONE);
776 reshape_all();
779 tasklist_style_changed();
783 static gint end_wink(gpointer data)
785 pinboard_wink_item(NULL, FALSE);
786 return FALSE;
789 /* Sets the appearance from the options and updates the size request of
790 * the image.
792 static void set_size_and_style(PinIcon *pi)
794 Icon *icon = (Icon *) pi;
795 MaskedPixmap *image = icon->item->image;
796 int iwidth = image->width;
797 int iheight = image->height;
799 gtk_widget_modify_fg(pi->label, GTK_STATE_PRELIGHT, &pin_text_fg_col);
800 gtk_widget_modify_bg(pi->label, GTK_STATE_PRELIGHT, &pin_text_bg_col);
801 gtk_widget_modify_fg(pi->label, GTK_STATE_NORMAL, &pin_text_fg_col);
802 gtk_widget_modify_bg(pi->label, GTK_STATE_NORMAL, &pin_text_bg_col);
803 widget_modify_font(pi->label, pinboard_font);
805 gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
807 gtk_widget_set_size_request(pi->widget, iwidth, iheight);
810 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi)
812 static GtkWidgetClass *parent_class = NULL;
813 Icon *icon = (Icon *) pi;
814 DirItem *item = icon->item;
815 MaskedPixmap *image = item->image;
816 int iwidth = image->width;
817 int iheight = image->height;
818 int x, y;
819 GtkStateType state = icon->selected ? GTK_STATE_SELECTED
820 : GTK_STATE_NORMAL;
822 if (!parent_class)
824 gpointer c = ((GTypeInstance *) widget)->g_class;
825 parent_class = (GtkWidgetClass *) g_type_class_peek_parent(c);
828 x = pi->widget->allocation.x;
829 y = pi->widget->allocation.y;
831 gdk_pixbuf_render_to_drawable_alpha(
832 icon->selected ? image->pixbuf_lit : image->pixbuf,
833 pi->widget->window,
834 0, 0, /* src */
835 x, y, /* dest */
836 iwidth, iheight,
837 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
838 GDK_RGB_DITHER_NORMAL, 0, 0);
840 if (item->flags & ITEM_FLAG_SYMLINK)
842 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
843 pi->widget->window,
844 0, 0, /* src */
845 x, y,
846 -1, -1,
847 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
848 GDK_RGB_DITHER_NORMAL, 0, 0);
850 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
852 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
853 ? im_mounted
854 : im_unmounted;
856 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
857 pi->widget->window,
858 0, 0, /* src */
859 x, y,
860 -1, -1,
861 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
862 GDK_RGB_DITHER_NORMAL, 0, 0);
865 if (icon->selected)
867 gtk_paint_flat_box(pi->label->style, pi->label->window,
868 state,
869 GTK_SHADOW_NONE,
870 NULL, pi->label, "text",
871 pi->label->allocation.x,
872 pi->label->allocation.y,
873 pi->label->allocation.width,
874 pi->label->allocation.height);
876 else if (o_pinboard_shadow_labels.int_value)
877 draw_label_shadow((GtkLabel *) pi->label);
879 /* Draw children */
880 (parent_class->expose_event)(widget, event);
882 /* Stop the button effect */
883 return TRUE;
886 static void draw_label_shadow(GtkLabel *label)
888 PangoLayout *layout;
889 GtkMisc *misc;
890 GtkWidget *widget;
891 gfloat xalign;
892 gint x, y;
894 misc = GTK_MISC(label);
895 widget = GTK_WIDGET(label);
896 layout = gtk_label_get_layout(label);
898 /* Taken from gtklabel.c ... */
899 if (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_LTR)
900 xalign = misc->xalign;
901 else
902 xalign = 1.0 - misc->xalign;
904 x = floor (widget->allocation.x + (gint)misc->xpad
905 + ((widget->allocation.width
906 - widget->requisition.width) * xalign)
907 + 0.5);
908 y = floor (widget->allocation.y + (gint)misc->ypad
909 + ((widget->allocation.height
910 - widget->requisition.height) * misc->yalign)
911 + 0.5);
913 gdk_gc_set_clip_origin(current_pinboard->shadow_gc,
914 widget->allocation.x, widget->allocation.y);
915 gdk_gc_set_clip_rectangle(current_pinboard->shadow_gc,
916 &widget->allocation);
918 gdk_draw_layout(widget->window, current_pinboard->shadow_gc,
919 x + 1, y + 1, layout);
921 if (o_pinboard_shadow_labels.int_value > 1)
922 gdk_draw_layout(widget->window, current_pinboard->shadow_gc,
923 x + 2, y + 2, layout);
926 static gint draw_wink(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi)
928 gint x, y, width, height;
930 if (current_wink_icon != pi)
931 return FALSE;
933 x = widget->allocation.x;
934 y = widget->allocation.y;
935 width = widget->allocation.width;
936 height = widget->allocation.height;
938 gdk_draw_rectangle(widget->window,
939 pi->widget->style->white_gc,
940 FALSE,
941 x, y, width - 1, height - 1);
942 gdk_draw_rectangle(widget->window,
943 pi->widget->style->black_gc,
944 FALSE,
945 x + 1, y + 1, width - 3, height - 3);
947 return FALSE;
950 static gboolean enter_notify(GtkWidget *widget,
951 GdkEventCrossing *event,
952 PinIcon *pi)
954 icon_may_update((Icon *) pi);
956 return FALSE;
959 static void select_lasso(void)
961 GList *next;
962 int minx, miny, maxx, maxy;
964 g_return_if_fail(lasso_in_progress == TRUE);
966 minx = MIN(lasso_rect_x1, lasso_rect_x2);
967 miny = MIN(lasso_rect_y1, lasso_rect_y2);
968 maxx = MAX(lasso_rect_x1, lasso_rect_x2);
969 maxy = MAX(lasso_rect_y1, lasso_rect_y2);
971 for (next = current_pinboard->icons; next; next = next->next)
973 PinIcon *pi = (PinIcon *) next->data;
974 GtkAllocation *alloc = &pi->win->allocation;
975 int cx = alloc->x + alloc->width / 2;
976 int cy = alloc->y + alloc->height / 2;
978 if (cx > minx && cx < maxx && cy > miny && cy < maxy)
979 icon_set_selected((Icon *) pi, TRUE);
983 static void cancel_lasso(void)
985 draw_lasso();
986 lasso_in_progress = FALSE;
989 static void pinboard_lasso_box(int start_x, int start_y)
991 if (lasso_in_progress)
992 cancel_lasso();
993 lasso_in_progress = TRUE;
994 lasso_rect_x1 = lasso_rect_x2 = start_x;
995 lasso_rect_y1 = lasso_rect_y2 = start_y;
997 draw_lasso();
1000 static gint lasso_motion(GtkWidget *widget, GdkEventMotion *event, gpointer d)
1002 if (!lasso_in_progress)
1003 return FALSE;
1005 if (lasso_rect_x2 != event->x || lasso_rect_y2 != event->y)
1007 draw_lasso();
1008 lasso_rect_x2 = event->x;
1009 lasso_rect_y2 = event->y;
1010 draw_lasso();
1013 return FALSE;
1016 /* Mark the area of the screen covered by the lasso box for redraw */
1017 static void draw_lasso(void)
1019 GdkRectangle area;
1021 if (!lasso_in_progress)
1022 return;
1024 area.x = MIN(lasso_rect_x1, lasso_rect_x2);
1025 area.y = MIN(lasso_rect_y1, lasso_rect_y2);
1026 area.width = ABS(lasso_rect_x1 - lasso_rect_x2);
1027 area.height = ABS(lasso_rect_y1 - lasso_rect_y2);
1029 gdk_window_invalidate_rect(current_pinboard->window->window,
1030 &area, TRUE);
1033 static void perform_action(PinIcon *pi, GdkEventButton *event)
1035 BindAction action;
1036 Icon *icon = (Icon *) pi;
1038 action = bind_lookup_bev(pi ? BIND_PINBOARD_ICON : BIND_PINBOARD,
1039 event);
1041 /* Actions that can happen with or without an icon */
1042 switch (action)
1044 case ACT_LASSO_CLEAR:
1045 icon_select_only(NULL);
1046 /* (no break) */
1047 case ACT_LASSO_MODIFY:
1048 pinboard_lasso_box(event->x, event->y);
1049 return;
1050 case ACT_CLEAR_SELECTION:
1051 icon_select_only(NULL);
1052 return;
1053 case ACT_POPUP_MENU:
1054 dnd_motion_ungrab();
1055 pinboard_show_menu(event, pi);
1056 return;
1057 case ACT_IGNORE:
1058 return;
1059 default:
1060 break;
1063 g_return_if_fail(pi != NULL);
1065 switch (action)
1067 case ACT_OPEN_ITEM:
1068 dnd_motion_ungrab();
1069 pinboard_wink_item(pi, TRUE);
1070 if (event->type == GDK_2BUTTON_PRESS)
1071 icon_set_selected(icon, FALSE);
1072 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
1073 break;
1074 case ACT_EDIT_ITEM:
1075 dnd_motion_ungrab();
1076 pinboard_wink_item(pi, TRUE);
1077 if (event->type == GDK_2BUTTON_PRESS)
1078 icon_set_selected(icon, FALSE);
1079 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
1080 break;
1081 case ACT_PRIME_AND_SELECT:
1082 if (!icon->selected)
1083 icon_select_only(icon);
1084 dnd_motion_start(MOTION_READY_FOR_DND);
1085 break;
1086 case ACT_PRIME_AND_TOGGLE:
1087 icon_set_selected(icon, !icon->selected);
1088 dnd_motion_start(MOTION_READY_FOR_DND);
1089 break;
1090 case ACT_PRIME_FOR_DND:
1091 dnd_motion_start(MOTION_READY_FOR_DND);
1092 break;
1093 case ACT_TOGGLE_SELECTED:
1094 icon_set_selected(icon, !icon->selected);
1095 break;
1096 case ACT_SELECT_EXCL:
1097 icon_select_only(icon);
1098 break;
1099 default:
1100 g_warning("Unsupported action : %d\n", action);
1101 break;
1105 static void forward_to_root(GdkEventButton *event)
1107 XButtonEvent xev;
1109 if (event->type == GDK_BUTTON_PRESS)
1111 xev.type = ButtonPress;
1112 XUngrabPointer(gdk_display, event->time);
1114 else
1115 xev.type = ButtonRelease;
1117 xev.window = gdk_x11_get_default_root_xwindow();
1118 xev.root = xev.window;
1119 xev.subwindow = None;
1120 xev.time = event->time;
1121 xev.x = event->x_root; /* Needed for icewm */
1122 xev.y = event->y_root;
1123 xev.x_root = event->x_root;
1124 xev.y_root = event->y_root;
1125 xev.state = event->state;
1126 xev.button = event->button;
1127 xev.same_screen = True;
1129 XSendEvent(gdk_display, xev.window, False,
1130 ButtonPressMask | ButtonReleaseMask, (XEvent *) &xev);
1133 #define FORWARDED_BUTTON(pi, b) ((b) == 2 || \
1134 (((b) == 3 || (b) == 1) && o_forward_buttons_13.int_value && !pi))
1136 /* pi is NULL if this is a root event */
1137 static gboolean button_release_event(GtkWidget *widget,
1138 GdkEventButton *event,
1139 PinIcon *pi)
1141 if (FORWARDED_BUTTON(pi, event->button))
1142 forward_to_root(event);
1143 else if (dnd_motion_release(event))
1145 if (motion_buttons_pressed == 0 && lasso_in_progress)
1147 select_lasso();
1148 cancel_lasso();
1150 return FALSE;
1153 perform_action(pi, event);
1155 return TRUE;
1158 /* pi is NULL if this is a root event */
1159 static gboolean button_press_event(GtkWidget *widget,
1160 GdkEventButton *event,
1161 PinIcon *pi)
1163 /* Just in case we've jumped in front of everything... */
1164 gdk_window_lower(current_pinboard->window->window);
1166 if (FORWARDED_BUTTON(pi, event->button))
1167 forward_to_root(event);
1168 else if (dnd_motion_press(widget, event))
1169 perform_action(pi, event);
1171 return TRUE;
1174 static void start_drag(PinIcon *pi, GdkEventMotion *event)
1176 GtkWidget *widget = pi->win;
1177 Icon *icon = (Icon *) pi;
1179 if (!icon->selected)
1181 tmp_icon_selected = TRUE;
1182 icon_select_only(icon);
1185 g_return_if_fail(icon_selection != NULL);
1187 pinboard_drag_in_progress = icon;
1189 if (icon_selection->next == NULL)
1190 drag_one_item(widget, event, icon->path, icon->item, NULL);
1191 else
1193 guchar *uri_list;
1195 uri_list = icon_create_uri_list();
1196 drag_selection(widget, event, uri_list);
1197 g_free(uri_list);
1201 /* An icon is being dragged around... */
1202 static gint icon_motion_notify(GtkWidget *widget,
1203 GdkEventMotion *event,
1204 PinIcon *pi)
1206 if (motion_state == MOTION_READY_FOR_DND)
1208 if (dnd_motion_moved(event))
1209 start_drag(pi, event);
1210 return TRUE;
1213 return FALSE;
1216 static void backdrop_from_xml(xmlNode *node)
1218 gchar *style;
1220 g_free(current_pinboard->backdrop);
1221 current_pinboard->backdrop = xmlNodeGetContent(node);
1223 style = xmlGetProp(node, "style");
1225 if (style)
1227 current_pinboard->backdrop_style =
1228 g_strcasecmp(style, "Tiled") == 0 ? BACKDROP_TILE :
1229 g_strcasecmp(style, "Scaled") == 0 ? BACKDROP_SCALE :
1230 g_strcasecmp(style, "Centred") == 0 ? BACKDROP_CENTRE :
1231 g_strcasecmp(style, "Program") == 0 ? BACKDROP_PROGRAM :
1232 BACKDROP_NONE;
1233 g_free(style);
1235 else
1236 current_pinboard->backdrop_style = BACKDROP_TILE;
1239 /* Create one pinboard icon for each icon in the doc */
1240 static void pinboard_load_from_xml(xmlDocPtr doc)
1242 xmlNodePtr node, root;
1243 char *tmp, *label, *path, *shortcut;
1244 int x, y;
1246 root = xmlDocGetRootElement(doc);
1248 for (node = root->xmlChildrenNode; node; node = node->next)
1250 if (node->type != XML_ELEMENT_NODE)
1251 continue;
1252 if (strcmp(node->name, "backdrop") == 0)
1254 backdrop_from_xml(node);
1255 continue;
1257 if (strcmp(node->name, "icon") != 0)
1258 continue;
1260 tmp = xmlGetProp(node, "x");
1261 if (!tmp)
1262 continue;
1263 x = atoi(tmp);
1264 g_free(tmp);
1266 tmp = xmlGetProp(node, "y");
1267 if (!tmp)
1268 continue;
1269 y = atoi(tmp);
1270 g_free(tmp);
1272 label = xmlGetProp(node, "label");
1273 if (!label)
1274 label = g_strdup("<missing label>");
1275 path = xmlNodeGetContent(node);
1276 if (!path)
1277 path = g_strdup("<missing path>");
1278 shortcut = xmlGetProp(node, "shortcut");
1280 pinboard_pin(path, label, x, y, shortcut);
1282 g_free(path);
1283 g_free(label);
1284 g_free(shortcut);
1288 /* Called for each line in the pinboard file while loading a new board.
1289 * Only used for old-format files when converting to XML.
1291 static const char *pin_from_file(gchar *line)
1293 gchar *leaf = NULL;
1294 int x, y, n;
1296 if (*line == '<')
1298 gchar *end;
1300 end = strchr(line + 1, '>');
1301 if (!end)
1302 return _("Missing '>' in icon label");
1304 leaf = g_strndup(line + 1, end - line - 1);
1306 line = end + 1;
1308 while (isspace(*line))
1309 line++;
1310 if (*line != ',')
1311 return _("Missing ',' after icon label");
1312 line++;
1315 if (sscanf(line, " %d , %d , %n", &x, &y, &n) < 2)
1316 return NULL; /* Ignore format errors */
1318 pinboard_pin(line + n, leaf, x, y, NULL);
1320 g_free(leaf);
1322 return NULL;
1325 /* Write the current state of the pinboard to the current pinboard file */
1326 static void pinboard_save(void)
1328 guchar *save = NULL;
1329 guchar *save_new = NULL;
1330 GList *next;
1331 xmlDocPtr doc = NULL;
1332 xmlNodePtr root;
1334 g_return_if_fail(current_pinboard != NULL);
1336 if (strchr(current_pinboard->name, '/'))
1337 save = g_strdup(current_pinboard->name);
1338 else
1340 guchar *leaf;
1342 leaf = g_strconcat("pb_", current_pinboard->name, NULL);
1343 save = choices_find_path_save(leaf, PROJECT, TRUE);
1344 g_free(leaf);
1347 if (!save)
1348 return;
1350 doc = xmlNewDoc("1.0");
1351 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "pinboard", NULL));
1353 root = xmlDocGetRootElement(doc);
1355 if (current_pinboard->backdrop)
1357 BackdropStyle style = current_pinboard->backdrop_style;
1358 xmlNodePtr tree;
1360 tree = xmlNewTextChild(root, NULL, "backdrop",
1361 current_pinboard->backdrop);
1362 xmlSetProp(tree, "style",
1363 style == BACKDROP_TILE ? "Tiled" :
1364 style == BACKDROP_CENTRE ? "Centred" :
1365 style == BACKDROP_SCALE ? "Scaled" :
1366 "Program");
1369 for (next = current_pinboard->icons; next; next = next->next)
1371 xmlNodePtr tree;
1372 PinIcon *pi = (PinIcon *) next->data;
1373 Icon *icon = (Icon *) pi;
1374 char *tmp;
1376 tree = xmlNewTextChild(root, NULL, "icon", icon->src_path);
1378 tmp = g_strdup_printf("%d", pi->x);
1379 xmlSetProp(tree, "x", tmp);
1380 g_free(tmp);
1382 tmp = g_strdup_printf("%d", pi->y);
1383 xmlSetProp(tree, "y", tmp);
1384 g_free(tmp);
1386 xmlSetProp(tree, "label", icon->item->leafname);
1387 if (icon->shortcut)
1388 xmlSetProp(tree, "shortcut", icon->shortcut);
1391 save_new = g_strconcat(save, ".new", NULL);
1392 if (save_xml_file(doc, save_new) || rename(save_new, save))
1393 delayed_error(_("Error saving pinboard %s: %s"),
1394 save, g_strerror(errno));
1395 g_free(save_new);
1397 g_free(save);
1398 if (doc)
1399 xmlFreeDoc(doc);
1402 static void snap_to_grid(int *x, int *y)
1404 int step = o_pinboard_grid_step.int_value;
1406 *x = ((*x + step / 2) / step) * step;
1407 *y = ((*y + step / 2) / step) * step;
1410 /* Convert (x,y) from a centre point to a window position */
1411 static void offset_from_centre(PinIcon *pi, int *x, int *y)
1413 gboolean clamp = o_pinboard_clamp_icons.int_value;
1414 GtkRequisition req;
1416 gtk_widget_size_request(pi->win, &req);
1418 *x -= req.width >> 1;
1419 *y -= req.height >> 1;
1420 *x = CLAMP(*x, 0, screen_width - (clamp ? req.width : 0));
1421 *y = CLAMP(*y, 0, screen_height - (clamp ? req.height : 0));
1424 /* Same as drag_set_dest(), but for pinboard icons */
1425 static void drag_set_pinicon_dest(PinIcon *pi)
1427 GtkObject *obj = GTK_OBJECT(pi->win);
1429 make_drop_target(pi->win, 0);
1431 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1432 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1433 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1436 /* Called during the drag when the mouse is in a widget registered
1437 * as a drop target. Returns TRUE if we can accept the drop.
1439 static gboolean drag_motion(GtkWidget *widget,
1440 GdkDragContext *context,
1441 gint x,
1442 gint y,
1443 guint time,
1444 PinIcon *pi)
1446 GdkDragAction action = context->suggested_action;
1447 const char *type = NULL;
1448 Icon *icon = (Icon *) pi;
1449 DirItem *item = icon->item;
1451 if (gtk_drag_get_source_widget(context) == widget)
1452 goto out; /* Can't drag something to itself! */
1454 if (icon->selected)
1455 goto out; /* Can't drag a selection to itself */
1457 type = dnd_motion_item(context, &item);
1459 if (!item)
1460 type = NULL;
1461 out:
1462 /* We actually must pretend to accept the drop, even if the
1463 * directory isn't writeable, so that the spring-opening
1464 * thing works.
1467 /* Don't allow drops to non-writeable directories */
1468 if (o_dnd_spring_open.int_value == FALSE &&
1469 type == drop_dest_dir &&
1470 access(icon->path, W_OK) != 0)
1472 type = NULL;
1475 g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1476 if (type)
1478 gdk_drag_status(context, action, time);
1479 g_dataset_set_data_full(context, "drop_dest_path",
1480 g_strdup(icon->path), g_free);
1481 if (type == drop_dest_dir)
1482 dnd_spring_load(context, NULL);
1484 pinboard_wink_item(pi, FALSE);
1486 else
1487 gdk_drag_status(context, 0, time);
1489 /* Always return TRUE to stop the pinboard getting the events */
1490 return TRUE;
1493 static gboolean pinboard_shadow = FALSE;
1494 static gint shadow_x, shadow_y;
1495 #define SHADOW_SIZE (ICON_WIDTH)
1497 static gboolean bg_expose(GtkWidget *widget,
1498 GdkEventExpose *event, gpointer data)
1500 gpointer gclass = ((GTypeInstance *) widget)->g_class;
1502 /* TODO: Split large regions into smaller chunks... */
1504 gdk_window_begin_paint_region(widget->window, event->region);
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 ((GtkWidgetClass *) gclass)->expose_event(widget, event);
1542 gdk_window_end_paint(widget->window);
1544 return TRUE;
1547 /* Draw a 'shadow' under an icon being dragged, showing where
1548 * it will land.
1550 static void pinboard_set_shadow(gboolean on)
1552 GdkRectangle area;
1554 if (pinboard_shadow)
1556 area.x = shadow_x;
1557 area.y = shadow_y;
1558 area.width = SHADOW_SIZE + 1;
1559 area.height = SHADOW_SIZE + 1;
1561 gdk_window_invalidate_rect(current_pinboard->window->window,
1562 &area, TRUE);
1565 if (on)
1567 int old_x = shadow_x, old_y = shadow_y;
1569 gdk_window_get_pointer(current_pinboard->fixed->window,
1570 &shadow_x, &shadow_y, NULL);
1571 snap_to_grid(&shadow_x, &shadow_y);
1572 shadow_x -= SHADOW_SIZE / 2;
1573 shadow_y -= SHADOW_SIZE / 2;
1576 if (pinboard_shadow && shadow_x == old_x && shadow_y == old_y)
1577 return;
1579 area.x = shadow_x;
1580 area.y = shadow_y;
1581 area.width = SHADOW_SIZE + 1;
1582 area.height = SHADOW_SIZE + 1;
1584 gdk_window_invalidate_rect(current_pinboard->window->window,
1585 &area, TRUE);
1588 pinboard_shadow = on;
1591 /* Called when dragging some pinboard icons finishes */
1592 void pinboard_move_icons(void)
1594 int x = shadow_x, y = shadow_y;
1595 PinIcon *pi = (PinIcon *) pinboard_drag_in_progress;
1596 int width, height;
1597 int dx, dy;
1598 GList *next;
1600 g_return_if_fail(pi != NULL);
1602 x += SHADOW_SIZE / 2;
1603 y += SHADOW_SIZE / 2;
1604 snap_to_grid(&x, &y);
1606 if (pi->x == x && pi->y == y)
1607 return;
1609 /* Find out how much the dragged icon moved (after snapping).
1610 * Move all selected icons by the same amount.
1612 dx = x - pi->x;
1613 dy = y - pi->y;
1615 /* Move the other selected icons to keep the same relative
1616 * position.
1618 for (next = icon_selection; next; next = next->next)
1620 PinIcon *pi = (PinIcon *) next->data;
1621 int nx, ny;
1623 g_return_if_fail(IS_PIN_ICON(pi));
1625 pi->x += dx;
1626 pi->y += dy;
1627 nx = pi->x;
1628 ny = pi->y;
1630 gdk_drawable_get_size(pi->win->window, &width, &height);
1631 offset_from_centre(pi, &nx, &ny);
1633 fixed_move_fast(GTK_FIXED(current_pinboard->fixed),
1634 pi->win, nx, ny);
1637 pinboard_save();
1640 static void drag_leave(GtkWidget *widget,
1641 GdkDragContext *context,
1642 guint32 time,
1643 PinIcon *pi)
1645 pinboard_wink_item(NULL, FALSE);
1646 dnd_spring_abort();
1649 static gboolean bg_drag_leave(GtkWidget *widget,
1650 GdkDragContext *context,
1651 guint32 time,
1652 gpointer data)
1654 pinboard_set_shadow(FALSE);
1655 return TRUE;
1658 static gboolean bg_drag_motion(GtkWidget *widget,
1659 GdkDragContext *context,
1660 gint x,
1661 gint y,
1662 guint time,
1663 gpointer data)
1665 /* Dragging from the pinboard to the pinboard is not allowed */
1667 if (!provides(context, text_uri_list))
1668 return FALSE;
1670 pinboard_set_shadow(TRUE);
1672 gdk_drag_status(context,
1673 context->suggested_action == GDK_ACTION_ASK
1674 ? GDK_ACTION_LINK : context->suggested_action,
1675 time);
1676 return TRUE;
1679 static void drag_end(GtkWidget *widget,
1680 GdkDragContext *context,
1681 PinIcon *pi)
1683 pinboard_drag_in_progress = NULL;
1684 if (tmp_icon_selected)
1686 icon_select_only(NULL);
1687 tmp_icon_selected = FALSE;
1691 /* Something which affects all the icons has changed - reshape
1692 * and redraw all of them.
1694 static void reshape_all(void)
1696 GList *next;
1698 g_return_if_fail(current_pinboard != NULL);
1700 for (next = current_pinboard->icons; next; next = next->next)
1702 Icon *icon = (Icon *) next->data;
1703 pinboard_reshape_icon(icon);
1707 /* Turns off the pinboard. Does not call gtk_main_quit. */
1708 static void pinboard_clear(void)
1710 GList *next;
1712 g_return_if_fail(current_pinboard != NULL);
1714 tasklist_set_active(FALSE);
1716 next = current_pinboard->icons;
1717 while (next)
1719 PinIcon *pi = (PinIcon *) next->data;
1721 next = next->next;
1723 gtk_widget_destroy(pi->win);
1726 gtk_widget_destroy(current_pinboard->window);
1728 abandon_backdrop_app(current_pinboard);
1730 g_object_unref(current_pinboard->shadow_gc);
1731 current_pinboard->shadow_gc = NULL;
1733 g_free(current_pinboard->name);
1734 null_g_free(&current_pinboard);
1736 number_of_windows--;
1739 static gpointer parent_class;
1741 static void pin_icon_destroy(Icon *icon)
1743 PinIcon *pi = (PinIcon *) icon;
1745 g_return_if_fail(pi->win != NULL);
1747 gtk_widget_destroy(pi->win);
1750 static void pinboard_remove_items(void)
1752 g_return_if_fail(icon_selection != NULL);
1754 while (icon_selection)
1755 icon_destroy((Icon *) icon_selection->data);
1757 pinboard_save();
1760 static void pin_icon_update(Icon *icon)
1762 pinboard_reshape_icon(icon);
1763 pinboard_save();
1766 static gboolean pin_icon_same_group(Icon *icon, Icon *other)
1768 return IS_PIN_ICON(other);
1771 static void pin_wink_icon(Icon *icon)
1773 pinboard_wink_item((PinIcon *) icon, TRUE);
1776 static void pin_icon_class_init(gpointer gclass, gpointer data)
1778 IconClass *icon = (IconClass *) gclass;
1780 parent_class = g_type_class_peek_parent(gclass);
1782 icon->destroy = pin_icon_destroy;
1783 icon->redraw = pinboard_reshape_icon;
1784 icon->update = pin_icon_update;
1785 icon->wink = pin_wink_icon;
1786 icon->remove_items = pinboard_remove_items;
1787 icon->same_group = pin_icon_same_group;
1790 static void pin_icon_init(GTypeInstance *object, gpointer gclass)
1794 static GType pin_icon_get_type(void)
1796 static GType type = 0;
1798 if (!type)
1800 static const GTypeInfo info =
1802 sizeof (PinIconClass),
1803 NULL, /* base_init */
1804 NULL, /* base_finalise */
1805 pin_icon_class_init,
1806 NULL, /* class_finalise */
1807 NULL, /* class_data */
1808 sizeof(PinIcon),
1809 0, /* n_preallocs */
1810 pin_icon_init
1813 type = g_type_register_static(icon_get_type(),
1814 "PinIcon", &info, 0);
1817 return type;
1820 static PinIcon *pin_icon_new(const char *pathname, const char *name)
1822 PinIcon *pi;
1823 Icon *icon;
1825 pi = g_object_new(pin_icon_get_type(), NULL);
1826 icon = (Icon *) pi;
1828 icon_set_path(icon, pathname, name);
1830 return pi;
1833 /* Called when the window widget is somehow destroyed */
1834 static void pin_icon_destroyed(PinIcon *pi)
1836 g_return_if_fail(pi->win != NULL);
1838 pi->win = NULL;
1840 pinboard_wink_item(NULL, FALSE);
1842 if (pinboard_drag_in_progress == (Icon *) pi)
1843 pinboard_drag_in_progress = NULL;
1845 if (current_pinboard)
1846 current_pinboard->icons =
1847 g_list_remove(current_pinboard->icons, pi);
1849 g_object_unref(pi);
1852 /* Set the tooltip */
1853 static void pin_icon_set_tip(PinIcon *pi)
1855 XMLwrapper *ai;
1856 xmlNode *node;
1857 Icon *icon = (Icon *) pi;
1859 g_return_if_fail(pi != NULL);
1861 ai = appinfo_get(icon->path, icon->item);
1863 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
1865 guchar *str;
1866 str = xmlNodeListGetString(node->doc,
1867 node->xmlChildrenNode, 1);
1868 if (str)
1870 gtk_tooltips_set_tip(tooltips, pi->win, str, NULL);
1871 g_free(str);
1874 else
1875 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
1877 if (ai)
1878 g_object_unref(ai);
1881 static void pinboard_show_menu(GdkEventButton *event, PinIcon *pi)
1883 int pos[3];
1885 pos[0] = event->x_root;
1886 pos[1] = event->y_root;
1887 pos[2] = 1;
1889 icon_prepare_menu((Icon *) pi, TRUE);
1891 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
1892 position_menu,
1893 (gpointer) pos, event->button, event->time);
1896 static void create_pinboard_window(Pinboard *pinboard)
1898 GtkWidget *win;
1900 g_return_if_fail(pinboard->window == NULL);
1902 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1903 gtk_widget_set_style(win, gtk_widget_get_default_style());
1905 gtk_widget_set_double_buffered(win, FALSE);
1906 gtk_widget_set_app_paintable(win, TRUE);
1907 gtk_widget_set_name(win, "rox-pinboard");
1908 pinboard->window = win;
1909 pinboard->fixed = gtk_fixed_new();
1910 gtk_container_add(GTK_CONTAINER(win), pinboard->fixed);
1912 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Pinboard", PROJECT);
1914 gtk_widget_set_size_request(win, screen_width, screen_height);
1915 gtk_widget_realize(win);
1916 gtk_window_move(GTK_WINDOW(win), 0, 0);
1917 make_panel_window(win);
1919 /* TODO: Use gdk function when it supports this type */
1921 GdkAtom desktop_type;
1923 desktop_type = gdk_atom_intern("_NET_WM_WINDOW_TYPE_DESKTOP",
1924 FALSE);
1925 gdk_property_change(win->window,
1926 gdk_atom_intern("_NET_WM_WINDOW_TYPE", FALSE),
1927 gdk_atom_intern("ATOM", FALSE), 32,
1928 GDK_PROP_MODE_REPLACE, (guchar *) &desktop_type, 1);
1931 gtk_widget_add_events(win,
1932 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1933 GDK_EXPOSURE_MASK |
1934 GDK_BUTTON1_MOTION_MASK |
1935 GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK);
1936 g_signal_connect(win, "button-press-event",
1937 G_CALLBACK(button_press_event), NULL);
1938 g_signal_connect(win, "button-release-event",
1939 G_CALLBACK(button_release_event), NULL);
1940 g_signal_connect(win, "motion-notify-event",
1941 G_CALLBACK(lasso_motion), NULL);
1942 g_signal_connect(pinboard->fixed, "expose_event",
1943 G_CALLBACK(bg_expose), NULL);
1945 /* Drag and drop handlers */
1946 drag_set_pinboard_dest(win);
1947 g_signal_connect(win, "drag_motion", G_CALLBACK(bg_drag_motion), NULL);
1948 g_signal_connect(win, "drag_leave", G_CALLBACK(bg_drag_leave), NULL);
1950 pinboard->shadow_gc = gdk_gc_new(win->window);
1951 gdk_gc_set_rgb_fg_color(pinboard->shadow_gc, &pin_text_shadow_col);
1953 reload_backdrop(current_pinboard, NULL, BACKDROP_NONE);
1955 gtk_widget_show_all(win);
1956 gdk_window_lower(win->window);
1959 /* Load image 'path' and scale according to 'style' */
1960 static GdkPixmap *load_backdrop(const gchar *path, BackdropStyle style)
1962 GdkPixmap *pixmap;
1963 GdkPixbuf *pixbuf;
1964 GError *error = NULL;
1966 pixbuf = gdk_pixbuf_new_from_file(path, &error);
1967 if (error)
1969 delayed_error(_("Error loading backdrop image:\n%s\n"
1970 "Backdrop removed."),
1971 error->message);
1972 g_error_free(error);
1973 set_backdrop(NULL, BACKDROP_NONE);
1974 return NULL;
1977 if (style == BACKDROP_SCALE)
1979 GdkPixbuf *old = pixbuf;
1981 pixbuf = gdk_pixbuf_scale_simple(old,
1982 screen_width, screen_height,
1983 GDK_INTERP_HYPER);
1985 g_object_unref(old);
1987 else if (style == BACKDROP_CENTRE)
1989 GdkPixbuf *old = pixbuf;
1990 int x, y, width, height;
1992 width = gdk_pixbuf_get_width(pixbuf);
1993 height = gdk_pixbuf_get_height(pixbuf);
1995 pixbuf = gdk_pixbuf_new(
1996 gdk_pixbuf_get_colorspace(pixbuf), 0,
1997 8, screen_width, screen_height);
1998 gdk_pixbuf_fill(pixbuf, 0);
2000 x = (screen_width - width) / 2;
2001 y = (screen_height - height) / 2;
2002 x = MAX(x, 0);
2003 y = MAX(y, 0);
2005 gdk_pixbuf_composite(old, pixbuf,
2006 x, y,
2007 MIN(screen_width, width),
2008 MIN(screen_height, height),
2009 x, y, 1, 1,
2010 GDK_INTERP_NEAREST, 255);
2011 g_object_unref(old);
2014 gdk_pixbuf_render_pixmap_and_mask(pixbuf,
2015 &pixmap, NULL, 0);
2016 g_object_unref(pixbuf);
2018 return pixmap;
2021 static void abandon_backdrop_app(Pinboard *pinboard)
2023 g_return_if_fail(pinboard != NULL);
2025 if (pinboard->to_backdrop_app != -1)
2027 close(pinboard->to_backdrop_app);
2028 close(pinboard->from_backdrop_app);
2029 gtk_input_remove(pinboard->input_tag);
2030 g_string_free(pinboard->input_buffer, TRUE);
2031 pinboard->to_backdrop_app = -1;
2032 pinboard->from_backdrop_app = -1;
2033 pinboard->input_tag = -1;
2034 pinboard->input_buffer = NULL;
2037 g_return_if_fail(pinboard->to_backdrop_app == -1);
2038 g_return_if_fail(pinboard->from_backdrop_app == -1);
2039 g_return_if_fail(pinboard->input_tag == -1);
2040 g_return_if_fail(pinboard->input_buffer == NULL);
2043 /* A single line has been read from the child.
2044 * Processes the command, and replies 'ok' (or abandons the child on error).
2046 static void command_from_backdrop_app(Pinboard *pinboard, const gchar *command)
2048 BackdropStyle style;
2049 const char *ok = "ok\n";
2051 if (strncmp(command, "tile ", 5) == 0)
2053 style = BACKDROP_TILE;
2054 command += 5;
2056 else if (strncmp(command, "scale ", 6) == 0)
2058 style = BACKDROP_SCALE;
2059 command += 6;
2061 else if (strncmp(command, "centre ", 7) == 0)
2063 style = BACKDROP_CENTRE;
2064 command += 7;
2066 else
2068 g_warning("Invalid command '%s' from backdrop app\n",
2069 command);
2070 abandon_backdrop_app(pinboard);
2071 return;
2074 /* Load the backdrop. May abandon the program if loading fails. */
2075 reload_backdrop(pinboard, command, style);
2077 if (pinboard->to_backdrop_app == -1)
2078 return;
2080 while (*ok)
2082 int sent;
2084 sent = write(pinboard->to_backdrop_app, ok, strlen(ok));
2085 if (sent <= 0)
2087 /* Remote app quit? Not an error. */
2088 abandon_backdrop_app(pinboard);
2089 break;
2091 ok += sent;
2095 static void backdrop_from_child(Pinboard *pinboard,
2096 int src, GdkInputCondition cond)
2098 char buf[256];
2099 int got;
2101 got = read(src, buf, sizeof(buf));
2103 if (got <= 0)
2105 if (got < 0)
2106 g_warning("backdrop_from_child: %s\n",
2107 g_strerror(errno));
2108 abandon_backdrop_app(pinboard);
2109 return;
2112 g_string_append_len(pinboard->input_buffer, buf, got);
2114 while (pinboard->from_backdrop_app != -1)
2116 int len;
2117 char *nl, *command;
2119 nl = strchr(pinboard->input_buffer->str, '\n');
2120 if (!nl)
2121 return; /* Haven't got a whole line yet */
2123 len = nl - pinboard->input_buffer->str;
2124 command = g_strndup(pinboard->input_buffer->str, len);
2125 g_string_erase(pinboard->input_buffer, 0, len + 1);
2127 command_from_backdrop_app(pinboard, command);
2129 g_free(command);
2133 static void reload_backdrop(Pinboard *pinboard,
2134 const gchar *backdrop,
2135 BackdropStyle backdrop_style)
2137 GtkStyle *style;
2139 if (backdrop && backdrop_style == BACKDROP_PROGRAM)
2141 const char *argv[] = {NULL, "--backdrop", NULL};
2142 GError *error = NULL;
2144 g_return_if_fail(pinboard->to_backdrop_app == -1);
2145 g_return_if_fail(pinboard->from_backdrop_app == -1);
2146 g_return_if_fail(pinboard->input_tag == -1);
2147 g_return_if_fail(pinboard->input_buffer == NULL);
2149 argv[0] = make_path(backdrop, "AppRun");
2151 /* Run the program. It'll send us a SOAP message and we'll
2152 * get back here with a different style and image.
2155 if (g_spawn_async_with_pipes(NULL, (gchar **) argv, NULL,
2156 G_SPAWN_DO_NOT_REAP_CHILD |
2157 G_SPAWN_SEARCH_PATH,
2158 NULL, NULL, /* Child setup fn */
2159 NULL, /* Child PID */
2160 &pinboard->to_backdrop_app,
2161 &pinboard->from_backdrop_app,
2162 NULL, /* Standard error */
2163 &error))
2165 pinboard->input_buffer = g_string_new(NULL);
2166 pinboard->input_tag = gtk_input_add_full(
2167 pinboard->from_backdrop_app,
2168 GDK_INPUT_READ,
2169 (GdkInputFunction) backdrop_from_child,
2170 NULL, pinboard, NULL);
2172 else
2174 delayed_error("%s", error ? error->message : "(null)");
2175 g_error_free(error);
2177 return;
2180 /* Note: Copying a style does not ref the pixmaps! */
2182 style = gtk_style_copy(gtk_widget_get_style(pinboard->window));
2183 style->bg_pixmap[GTK_STATE_NORMAL] = NULL;
2185 if (backdrop)
2186 style->bg_pixmap[GTK_STATE_NORMAL] =
2187 load_backdrop(backdrop, backdrop_style);
2189 gdk_color_parse(o_pinboard_bg_colour.value,
2190 &style->bg[GTK_STATE_NORMAL]);
2192 gtk_widget_set_style(pinboard->window, style);
2194 g_object_unref(style);
2196 gtk_widget_queue_draw(pinboard->window);
2198 /* Also update root window property (for transparent xterms, etc) */
2199 if (style->bg_pixmap[GTK_STATE_NORMAL])
2201 XID id = GDK_DRAWABLE_XID(style->bg_pixmap[GTK_STATE_NORMAL]);
2202 gdk_property_change(gdk_get_default_root_window(),
2203 gdk_atom_intern("_XROOTPMAP_ID", FALSE),
2204 gdk_atom_intern("PIXMAP", FALSE),
2205 32, GDK_PROP_MODE_REPLACE,
2206 (guchar *) &id, 1);
2208 else
2210 gdk_property_delete(gdk_get_default_root_window(),
2211 gdk_atom_intern("_XROOTPMAP_ID", FALSE));
2215 /* Set and save (path, style) as the new backdrop.
2216 * If style is BACKDROP_PROGRAM, the program is run to get the backdrop.
2217 * Otherwise, the image is displayed now.
2219 static void set_backdrop(const gchar *path, BackdropStyle style)
2221 g_return_if_fail((path == NULL && style == BACKDROP_NONE) ||
2222 (path != NULL && style != BACKDROP_NONE));
2224 if (!current_pinboard)
2226 if (!path)
2227 return;
2228 pinboard_activate("Default");
2229 delayed_error(_("No pinboard was in use... "
2230 "the 'Default' pinboard has been selected. "
2231 "Use 'rox -p=Default' to turn it on in "
2232 "future."));
2233 g_return_if_fail(current_pinboard != NULL);
2236 /* We might have just run the old backdrop program and now
2237 * we're going to set a new one! Seems a bit mean...
2240 abandon_backdrop_app(current_pinboard);
2242 g_free(current_pinboard->backdrop);
2243 current_pinboard->backdrop = g_strdup(path);
2244 current_pinboard->backdrop_style = style;
2245 reload_backdrop(current_pinboard,
2246 current_pinboard->backdrop,
2247 current_pinboard->backdrop_style);
2249 pinboard_save();
2252 #define SEARCH_STEP 32
2254 static void search_free(GdkRectangle *rect, GdkRegion *used,
2255 int *outer, int od, int omax,
2256 int *inner, int id, int imax)
2258 *outer = od > 0 ? 0 : omax;
2259 while (*outer >= 0 && *outer <= omax)
2261 *inner = id > 0 ? 0 : imax;
2262 while (*inner >= 0 && *inner <= imax)
2264 if (gdk_region_rect_in(used, rect) ==
2265 GDK_OVERLAP_RECTANGLE_OUT)
2266 return;
2267 *inner += id;
2270 *outer += od;
2273 rect->x = -1;
2274 rect->y = -1;
2277 /* Finds a free area on the pinboard large enough for the width and height
2278 * of the given rectangle, by filling in the x and y fields of 'rect'.
2279 * The search order respects user preferences.
2280 * If no area is free, returns any old area.
2282 static void find_free_rect(Pinboard *pinboard, GdkRectangle *rect)
2284 GdkRegion *used;
2285 GList *next;
2286 GdkRectangle used_rect;
2287 int dx = SEARCH_STEP, dy = SEARCH_STEP;
2289 used = gdk_region_new();
2291 panel_mark_used(used);
2293 /* Subtract the used areas... */
2295 next = GTK_FIXED(pinboard->fixed)->children;
2296 for (; next; next = next->next)
2298 GtkFixedChild *fix = (GtkFixedChild *) next->data;
2300 if (!GTK_WIDGET_VISIBLE(fix->widget))
2301 continue;
2303 used_rect.x = fix->x;
2304 used_rect.y = fix->y;
2305 used_rect.width = fix->widget->requisition.width;
2306 used_rect.height = fix->widget->requisition.height;
2308 gdk_region_union_with_rect(used, &used_rect);
2311 /* Find the first free area (yes, this isn't exactly pretty, but
2312 * it works). If you know a better (fast!) algorithm, let me know!
2316 if (o_iconify_start.int_value == CORNER_TOP_RIGHT ||
2317 o_iconify_start.int_value == CORNER_BOTTOM_RIGHT)
2318 dx = -SEARCH_STEP;
2320 if (o_iconify_start.int_value == CORNER_BOTTOM_LEFT ||
2321 o_iconify_start.int_value == CORNER_BOTTOM_RIGHT)
2322 dy = -SEARCH_STEP;
2324 if (o_iconify_dir.int_value == DIR_VERT)
2326 search_free(rect, used,
2327 &rect->x, dx, screen_width - rect->width,
2328 &rect->y, dy, screen_height - rect->height);
2330 else
2332 search_free(rect, used,
2333 &rect->y, dy, screen_height - rect->height,
2334 &rect->x, dx, screen_width - rect->width);
2337 gdk_region_destroy(used);
2339 if (rect->x == -1)
2341 rect->x = 0;
2342 rect->y = 0;
2346 /* Icon's size, shape or appearance has changed - update the display */
2347 static void pinboard_reshape_icon(Icon *icon)
2349 PinIcon *pi = (PinIcon *) icon;
2350 int x = pi->x, y = pi->y;
2352 set_size_and_style(pi);
2353 offset_from_centre(pi, &x, &y);
2355 if (pi->win->allocation.x != x || pi->win->allocation.y != y)
2357 fixed_move_fast(GTK_FIXED(current_pinboard->fixed),
2358 pi->win, x, y);
2362 /* Sets the pinboard_font global from the option. Doesn't do anything else. */
2363 static void update_pinboard_font(void)
2365 if (pinboard_font)
2366 pango_font_description_free(pinboard_font);
2367 pinboard_font = o_label_font.value[0] != '\0'
2368 ? pango_font_description_from_string(o_label_font.value)
2369 : NULL;