r523: Converted AppMenu file format to XML.
[rox-filer.git] / ROX-Filer / src / pinboard.c
bloba932d680827e392ead0d52e9073ac01750c9805d
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2001, 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 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 <gtk/gtkinvisible.h>
34 #include "global.h"
36 #include "pinboard.h"
37 #include "main.h"
38 #include "dnd.h"
39 #include "pixmaps.h"
40 #include "type.h"
41 #include "choices.h"
42 #include "support.h"
43 #include "gui_support.h"
44 #include "run.h"
45 #include "menu.h"
46 #include "options.h"
47 #include "dir.h"
48 #include "mount.h"
49 #include "bind.h"
50 #include "icon.h"
51 #include "appmenu.h"
53 /* The number of pixels between the bottom of the image and the top
54 * of the text.
56 #define GAP 4
58 /* The size of the border around the icon which is used when winking */
59 #define WINK_FRAME 2
61 /* Grid sizes */
62 #define GRID_STEP_FINE 2
63 #define GRID_STEP_MED 16
64 #define GRID_STEP_COARSE 32
66 /* Used for the text colours (only) in the icons */
67 GdkColor text_fg_col, text_bg_col;
69 /* Style that all the icons should use. NULL => regenerate from text_fg/bg */
70 GtkStyle *pinicon_style = NULL;
72 struct _Pinboard {
73 guchar *name; /* Leaf name */
74 GList *icons;
77 static Pinboard *current_pinboard = NULL;
78 static gint loading_pinboard = 0; /* Non-zero => loading */
79 static gboolean tmp_icon_selected = FALSE;
82 static Icon *current_wink_icon = NULL;
83 static gint wink_timeout;
85 static gint number_selected = 0;
87 static GdkColor mask_solid = {1, 1, 1, 1};
88 static GdkColor mask_transp = {0, 0, 0, 0};
89 static GdkGC *mask_gc = NULL;
91 /* Proxy window for DnD and clicks on the desktop */
92 static GtkWidget *proxy_invisible;
94 /* The window (owned by the wm) which root clicks are forwarded to.
95 * NULL if wm does not support forwarding clicks.
97 static GdkWindow *click_proxy_gdk_window = NULL;
98 static GdkAtom win_button_proxy; /* _WIN_DESKTOP_BUTTON_PROXY */
100 static gboolean pinboard_drag_in_progress = FALSE;
102 /* Used when dragging icons around... */
103 static gboolean pinboard_modified = FALSE;
105 typedef enum {
106 TEXT_BG_NONE = 0,
107 TEXT_BG_OUTLINE = 1,
108 TEXT_BG_SOLID = 2,
109 } TextBgType;
111 TextBgType o_text_bg = TEXT_BG_SOLID;
112 gboolean o_clamp_icons = TRUE;
113 static int o_grid_step = GRID_STEP_COARSE;
114 static int old_x, old_y; /* For dragging (mouse start) */
115 static int icon_old_x, icon_old_y; /* For dragging (icon start) */
117 /* Static prototypes */
118 static void set_size_and_shape(Icon *icon, int *rwidth, int *rheight);
119 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, Icon *icon);
120 static void mask_wink_border(Icon *icon, GdkColor *alpha);
121 static gint end_wink(gpointer data);
122 static gboolean button_release_event(GtkWidget *widget,
123 GdkEventButton *event,
124 Icon *icon);
125 static gboolean root_property_event(GtkWidget *widget,
126 GdkEventProperty *event,
127 gpointer data);
128 static gboolean root_button_press(GtkWidget *widget,
129 GdkEventButton *event,
130 gpointer data);
131 static gboolean enter_notify(GtkWidget *widget,
132 GdkEventCrossing *event,
133 Icon *icon);
134 static gboolean button_press_event(GtkWidget *widget,
135 GdkEventButton *event,
136 Icon *icon);
137 static gint icon_motion_notify(GtkWidget *widget,
138 GdkEventMotion *event,
139 Icon *icon);
140 static char *pin_from_file(guchar *line);
141 static gboolean add_root_handlers(void);
142 static void pinboard_save(void);
143 static GdkFilterReturn proxy_filter(GdkXEvent *xevent,
144 GdkEvent *event,
145 gpointer data);
146 static void icon_destroyed(GtkWidget *widget, Icon *icon);
147 static void snap_to_grid(int *x, int *y);
148 static void offset_from_centre(Icon *icon,
149 int width, int height,
150 int *x, int *y);
151 static void offset_to_centre(Icon *icon,
152 int width, int height,
153 int *x, int *y);
154 static gboolean drag_motion(GtkWidget *widget,
155 GdkDragContext *context,
156 gint x,
157 gint y,
158 guint time,
159 Icon *icon);
160 static void drag_set_pinicon_dest(Icon *icon);
161 static void drag_leave(GtkWidget *widget,
162 GdkDragContext *context,
163 guint32 time,
164 Icon *icon);
165 static void forward_root_clicks(void);
166 static void change_number_selected(int delta);
167 static gint lose_selection(GtkWidget *widget, GdkEventSelection *event);
168 static void selection_get(GtkWidget *widget,
169 GtkSelectionData *selection_data,
170 guint info,
171 guint time,
172 gpointer data);
173 static gboolean bg_drag_motion(GtkWidget *widget,
174 GdkDragContext *context,
175 gint x,
176 gint y,
177 guint time,
178 gpointer data);
179 static void drag_end(GtkWidget *widget,
180 GdkDragContext *context,
181 Icon *icon);
182 static void reshape_icon(Icon *icon);
183 static void reshape_all(void);
184 static void menu_closed(GtkWidget *widget);
185 static void edit_icon(gpointer data, guint action, GtkWidget *widget);
186 static void show_location(gpointer data, guint action, GtkWidget *widget);
187 static void pin_help(gpointer data, guint action, GtkWidget *widget);
188 static void pin_remove(gpointer data, guint action, GtkWidget *widget);
189 static void show_pinboard_menu(GdkEventButton *event, Icon *icon);
191 static void pinboard_check_options(void);
193 static GtkItemFactoryEntry menu_def[] = {
194 {N_("ROX-Filer Help"), NULL, menu_rox_help, 0, NULL},
195 {N_("ROX-Filer Options..."), NULL, menu_show_options, 0, NULL},
196 {N_("Open Home Directory"), NULL, open_home, 0, NULL},
197 {"", NULL, NULL, 0, "<Separator>"},
198 {N_("Edit Icon"), NULL, edit_icon, 0, NULL},
199 {N_("Show Location"), NULL, show_location, 0, NULL},
200 {N_("Show Help"), NULL, pin_help, 0, NULL},
201 {N_("Remove Item(s)"), NULL, pin_remove, 0, NULL},
204 static GtkWidget *pinboard_menu; /* The popup pinboard menu */
206 /****************************************************************
207 * EXTERNAL INTERFACE *
208 ****************************************************************/
210 void pinboard_init(void)
212 GtkStyle *style;
214 option_add_string("pinboard_fg_colour", "#000", NULL);
215 option_add_string("pinboard_bg_colour", "#ddd", NULL);
217 option_add_int("pinboard_text_bg", TEXT_BG_SOLID, NULL);
218 option_add_int("pinboard_clamp_icons", 1, NULL);
219 option_add_int("pinboard_grid_step", GRID_STEP_COARSE, NULL);
220 option_add_notify(pinboard_check_options);
222 style = gtk_widget_get_default_style();
224 gdk_color_parse(option_get_static_string("pinboard_fg_colour"),
225 &text_fg_col);
226 gdk_color_parse(option_get_static_string("pinboard_bg_colour"),
227 &text_bg_col);
229 pinboard_menu = menu_create(menu_def,
230 sizeof(menu_def) / sizeof(*menu_def),
231 "<pinboard>");
232 gtk_signal_connect(GTK_OBJECT(pinboard_menu), "unmap_event",
233 GTK_SIGNAL_FUNC(menu_closed), NULL);
236 /* Load 'pb_<pinboard>' config file from Choices (if it exists)
237 * and make it the current pinboard.
238 * Any existing pinned items are removed. You must call this
239 * at least once before using the pinboard. NULL disables the
240 * pinboard.
242 void pinboard_activate(guchar *name)
244 Pinboard *old_board = current_pinboard;
245 guchar *path, *slash;
247 /* Treat an empty name the same as NULL */
248 if (!*name)
249 name = NULL;
251 if (old_board)
253 pinboard_clear();
254 number_of_windows--;
257 if (!name)
259 if (number_of_windows < 1 && gtk_main_level() > 0)
260 gtk_main_quit();
261 return;
264 if (!add_root_handlers())
266 delayed_error(PROJECT, _("Another application is already "
267 "managing the pinboard!"));
268 return;
271 number_of_windows++;
273 slash = strchr(name, '/');
274 if (slash)
276 if (access(name, F_OK))
277 path = NULL; /* File does not (yet) exist */
278 else
279 path = g_strdup(name);
281 else
283 guchar *leaf;
285 leaf = g_strconcat("pb_", name, NULL);
286 path = choices_find_path_load(leaf, "ROX-Filer");
287 g_free(leaf);
290 current_pinboard = g_new(Pinboard, 1);
291 current_pinboard->name = g_strdup(name);
292 current_pinboard->icons = NULL;
294 loading_pinboard++;
295 if (path)
297 parse_file(path, pin_from_file);
298 g_free(path);
300 else
301 pinboard_pin(home_dir, "Home", 4, 4, TRUE);
302 loading_pinboard--;
305 /* Add a new icon to the background.
306 * 'path' should be an absolute pathname.
307 * 'x' and 'y' are the coordinates of the point in the middle of the text
308 * if 'corner' is FALSE, and as the top-left corner of where the icon
309 * image should be if it is TRUE.
310 * 'name' is the name to use. If NULL then the leafname of path is used.
312 void pinboard_pin(guchar *path, guchar *name, int x, int y, gboolean corner)
314 Icon *icon;
315 int width, height;
317 g_return_if_fail(path != NULL);
318 g_return_if_fail(current_pinboard != NULL);
320 icon = g_new(Icon, 1);
321 icon->type = ICON_PINBOARD;
322 icon->selected = FALSE;
323 icon->src_path = g_strdup(path);
324 icon->path = icon_convert_path(path);
325 icon->mask = NULL;
326 icon->x = x;
327 icon->y = y;
329 icon_hash_path(icon);
331 dir_stat(icon->path, &icon->item, FALSE);
333 if (!name)
335 name = strrchr(icon->path, '/');
336 if (name && name[1])
337 name++;
338 else
339 name = icon->path;
342 icon->item.leafname = g_strdup(name);
344 icon->win = gtk_window_new(GTK_WINDOW_DIALOG);
345 gtk_window_set_wmclass(GTK_WINDOW(icon->win), "ROX-Pinboard", PROJECT);
347 icon->widget = gtk_drawing_area_new();
348 gtk_container_add(GTK_CONTAINER(icon->win), icon->widget);
349 drag_set_pinicon_dest(icon);
350 gtk_signal_connect(GTK_OBJECT(icon->widget), "drag_data_get",
351 drag_data_get, NULL);
353 gtk_widget_realize(icon->win);
354 gtk_widget_realize(icon->widget);
356 set_size_and_shape(icon, &width, &height);
357 if (corner)
359 /* Convert from icon-corner coordinates to center coordinates */
360 MaskedPixmap *image = icon->item.image;
361 x += (image->width >> 1);
362 y += height - (icon->widget->style->font->descent >> 1);
364 snap_to_grid(&x, &y);
365 offset_from_centre(icon, width, height, &x, &y);
366 gtk_widget_set_uposition(icon->win, x, y);
367 /* Set the correct position in the icon */
368 offset_to_centre(icon, width, height, &x, &y);
369 icon->x = x;
370 icon->y = y;
372 make_panel_window(icon->win->window);
374 gtk_widget_add_events(icon->widget,
375 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
376 GDK_BUTTON1_MOTION_MASK | GDK_ENTER_NOTIFY_MASK |
377 GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK);
378 gtk_signal_connect(GTK_OBJECT(icon->widget), "enter-notify-event",
379 GTK_SIGNAL_FUNC(enter_notify), icon);
380 gtk_signal_connect(GTK_OBJECT(icon->widget), "button-press-event",
381 GTK_SIGNAL_FUNC(button_press_event), icon);
382 gtk_signal_connect(GTK_OBJECT(icon->widget), "button-release-event",
383 GTK_SIGNAL_FUNC(button_release_event), icon);
384 gtk_signal_connect(GTK_OBJECT(icon->widget), "motion-notify-event",
385 GTK_SIGNAL_FUNC(icon_motion_notify), icon);
386 gtk_signal_connect(GTK_OBJECT(icon->widget), "expose-event",
387 GTK_SIGNAL_FUNC(draw_icon), icon);
388 gtk_signal_connect(GTK_OBJECT(icon->win), "destroy",
389 GTK_SIGNAL_FUNC(icon_destroyed), icon);
391 current_pinboard->icons = g_list_prepend(current_pinboard->icons,
392 icon);
393 gtk_widget_show_all(icon->win);
394 gdk_window_lower(icon->win->window);
396 if (!loading_pinboard)
397 pinboard_save();
400 /* Remove an icon from the pinboard */
401 void pinboard_unpin(Icon *icon)
403 g_return_if_fail(icon != NULL);
405 gtk_widget_destroy(icon->win);
406 pinboard_save();
409 /* Unpin all selected items */
410 void pinboard_unpin_selection(void)
412 GList *next;
414 g_return_if_fail(current_pinboard != NULL);
416 if (number_selected == 0)
418 delayed_error(PROJECT,
419 _("You should first select some pinned icons to "
420 "unpin. Hold down the Ctrl key to select some icons."));
421 return;
424 next = current_pinboard->icons;
425 while (next)
427 Icon *icon = (Icon *) next->data;
429 next = next->next;
431 if (icon->selected)
432 gtk_widget_destroy(icon->win);
435 pinboard_save();
438 /* Put a border around the icon, briefly.
439 * If icon is NULL then cancel any existing wink.
440 * The icon will automatically unhighlight unless timeout is FALSE,
441 * in which case you must call this function again (with NULL or another
442 * icon) to remove the highlight.
444 void pinboard_wink_item(Icon *icon, gboolean timeout)
446 if (current_wink_icon == icon)
447 return;
449 if (current_wink_icon)
451 mask_wink_border(current_wink_icon, &mask_transp);
452 if (wink_timeout != -1)
453 gtk_timeout_remove(wink_timeout);
456 current_wink_icon = icon;
458 if (current_wink_icon)
460 mask_wink_border(current_wink_icon, &mask_solid);
461 if (timeout)
462 wink_timeout = gtk_timeout_add(300, end_wink, NULL);
463 else
464 wink_timeout = -1;
468 /* Remove everything on the current pinboard and disables the pinboard.
469 * Does not change any files. Does not change number_of_windows.
471 void pinboard_clear(void)
473 GList *next;
475 g_return_if_fail(current_pinboard != NULL);
477 next = current_pinboard->icons;
478 while (next)
480 Icon *icon = (Icon *) next->data;
482 next = next->next;
484 gtk_widget_destroy(icon->win);
487 g_free(current_pinboard->name);
488 g_free(current_pinboard);
489 current_pinboard = NULL;
491 release_xdnd_proxy(GDK_ROOT_WINDOW());
492 gdk_window_remove_filter(GDK_ROOT_PARENT(), proxy_filter, NULL);
493 gdk_window_set_user_data(GDK_ROOT_PARENT(), NULL);
496 /* Return the single selected icon, or NULL */
497 Icon *pinboard_selected_icon(void)
499 GList *next;
500 Icon *found = NULL;
502 g_return_val_if_fail(current_pinboard != NULL, NULL);
504 for (next = current_pinboard->icons; next; next = next->next)
506 Icon *icon = (Icon *) next->data;
508 if (icon->selected)
510 if (found)
511 return NULL; /* >1 icon selected */
512 else
513 found = icon;
517 return found;
520 void pinboard_clear_selection(void)
522 pinboard_select_only(NULL);
525 /* Set whether an icon is selected or not */
526 void pinboard_set_selected(Icon *icon, gboolean selected)
528 g_return_if_fail(icon != NULL);
530 if (icon->selected == selected)
531 return;
533 if (selected)
534 change_number_selected(+1);
535 else
536 change_number_selected(-1);
538 icon->selected = selected;
539 gtk_widget_queue_draw(icon->win);
542 /* Return a list of all the selected icons.
543 * g_list_free() the result.
545 GList *pinboard_get_selected(void)
547 GList *next;
548 GList *selected = NULL;
550 for (next = current_pinboard->icons; next; next = next->next)
552 Icon *i = (Icon *) next->data;
554 if (i->selected)
555 selected = g_list_append(selected, i);
558 return selected;
561 /* Clear the selection and then select this icon.
562 * Doesn't release and claim the selection unnecessarily.
563 * If icon is NULL, then just clears the selection.
565 void pinboard_select_only(Icon *icon)
567 GList *next;
569 g_return_if_fail(current_pinboard != NULL);
571 if (icon)
572 pinboard_set_selected(icon, TRUE);
574 for (next = current_pinboard->icons; next; next = next->next)
576 Icon *i = (Icon *) next->data;
578 if (i->selected && i != icon)
579 pinboard_set_selected(i, FALSE);
584 /****************************************************************
585 * INTERNAL FUNCTIONS *
586 ****************************************************************/
588 static void pinboard_check_options(void)
590 int old_text_bg = o_text_bg;
591 GdkColor n_fg, n_bg;
593 o_text_bg = option_get_int("pinboard_text_bg");
594 o_grid_step = option_get_int("pinboard_grid_step");
595 o_clamp_icons = option_get_int("pinboard_clamp_icons");
597 gdk_color_parse(option_get_static_string("pinboard_fg_colour"), &n_fg);
598 gdk_color_parse(option_get_static_string("pinboard_bg_colour"), &n_bg);
600 if (o_text_bg != old_text_bg ||
601 gdk_color_equal(&n_fg, &text_fg_col) == 0 ||
602 gdk_color_equal(&n_bg, &text_bg_col) == 0)
604 memcpy(&text_fg_col, &n_fg, sizeof(GdkColor));
605 memcpy(&text_bg_col, &n_bg, sizeof(GdkColor));
607 if (pinicon_style)
609 gtk_style_unref(pinicon_style);
610 pinicon_style = NULL;
613 if (current_pinboard)
614 reshape_all();
618 /* Icon's size, shape or appearance has changed - update the display */
619 static void reshape_icon(Icon *icon)
621 int x = icon->x, y = icon->y;
622 int width, height;
624 set_size_and_shape(icon, &width, &height);
625 gdk_window_resize(icon->win->window, width, height);
626 offset_from_centre(icon, width, height, &x, &y);
627 gtk_widget_set_uposition(icon->win, x, y);
628 gtk_widget_queue_draw(icon->win);
631 /* See if the file the icon points to has changed. Update the icon
632 * if so.
634 void pinboard_icon_may_update(Icon *icon)
636 MaskedPixmap *image = icon->item.image;
637 int flags = icon->item.flags;
639 pixmap_ref(image);
640 mount_update(FALSE);
641 dir_restat(icon->path, &icon->item, FALSE);
643 if (icon->item.image != image || icon->item.flags != flags)
644 reshape_icon(icon);
646 pixmap_unref(image);
649 static gint end_wink(gpointer data)
651 pinboard_wink_item(NULL, FALSE);
652 return FALSE;
655 /* Make the wink border solid or transparent */
656 static void mask_wink_border(Icon *icon, GdkColor *alpha)
658 int width, height;
660 gdk_window_get_size(icon->widget->window, &width, &height);
662 gdk_gc_set_foreground(mask_gc, alpha);
663 gdk_draw_rectangle(icon->mask, mask_gc, FALSE,
664 0, 0, width - 1, height - 1);
665 gdk_draw_rectangle(icon->mask, mask_gc, FALSE,
666 1, 1, width - 3, height - 3);
668 gtk_widget_shape_combine_mask(icon->win, icon->mask, 0, 0);
670 gtk_widget_draw(icon->widget, NULL);
673 #define TEXT_AT(dx, dy) \
674 gdk_draw_string(icon->mask, font, mask_gc, \
675 text_x + dx, y + dy, \
676 item->leafname);
678 /* Updates the name_width field and resizes and masks the window.
679 * Also sets the style to pinicon_style, generating it if needed.
680 * Returns the new width and height.
682 static void set_size_and_shape(Icon *icon, int *rwidth, int *rheight)
684 int width, height;
685 GdkFont *font;
686 int font_height;
687 MaskedPixmap *image = icon->item.image;
688 DirItem *item = &icon->item;
689 int text_x, text_y;
691 if (!pinicon_style)
693 pinicon_style = gtk_style_copy(icon->widget->style);
694 memcpy(&pinicon_style->fg[GTK_STATE_NORMAL],
695 &text_fg_col, sizeof(GdkColor));
696 memcpy(&pinicon_style->bg[GTK_STATE_NORMAL],
697 &text_bg_col, sizeof(GdkColor));
699 gtk_widget_set_style(icon->widget, pinicon_style);
701 font = pinicon_style->font;
702 font_height = font->ascent + font->descent;
703 item->name_width = gdk_string_width(font, item->leafname);
705 width = MAX(image->width, item->name_width + 2) +
706 2 * WINK_FRAME;
707 height = image->height + GAP + (font_height + 2) + 2 * WINK_FRAME;
708 gtk_widget_set_usize(icon->win, width, height);
710 if (icon->mask)
711 gdk_pixmap_unref(icon->mask);
712 icon->mask = gdk_pixmap_new(icon->win->window, width, height, 1);
713 if (!mask_gc)
714 mask_gc = gdk_gc_new(icon->mask);
716 /* Clear the mask to transparent */
717 gdk_gc_set_foreground(mask_gc, &mask_transp);
718 gdk_draw_rectangle(icon->mask, mask_gc, TRUE, 0, 0, width, height);
720 gdk_gc_set_foreground(mask_gc, &mask_solid);
721 /* Make the icon area solid */
722 if (image->mask)
724 gdk_draw_pixmap(icon->mask, mask_gc, image->mask,
725 0, 0,
726 (width - image->width) >> 1,
727 WINK_FRAME,
728 image->width,
729 image->height);
731 else
733 gdk_draw_rectangle(icon->mask, mask_gc, TRUE,
734 (width - image->width) >> 1,
735 WINK_FRAME,
736 image->width,
737 image->height);
740 gdk_gc_set_function(mask_gc, GDK_OR);
741 if (item->flags & ITEM_FLAG_SYMLINK)
743 gdk_draw_pixmap(icon->mask, mask_gc, im_symlink->mask,
744 0, 0, /* Source x,y */
745 (width - image->width) >> 1, /* Dest x */
746 WINK_FRAME, /* Dest y */
747 -1, -1);
749 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
751 /* Note: Both mount state pixmaps must have the same mask */
752 gdk_draw_pixmap(icon->mask, mask_gc, im_mounted->mask,
753 0, 0, /* Source x,y */
754 (width - image->width) >> 1, /* Dest x */
755 WINK_FRAME, /* Dest y */
756 -1, -1);
758 gdk_gc_set_function(mask_gc, GDK_COPY);
760 /* Mask off an area for the text (from o_text_bg) */
762 text_x = (width - item->name_width) >> 1;
763 text_y = WINK_FRAME + image->height + GAP + 1;
765 if (o_text_bg == TEXT_BG_SOLID)
767 gdk_draw_rectangle(icon->mask, mask_gc, TRUE,
768 (width - (item->name_width + 2)) >> 1,
769 WINK_FRAME + image->height + GAP,
770 item->name_width + 2, font_height + 2);
772 else
774 int y = text_y + font->ascent;
776 TEXT_AT(0, 0);
778 if (o_text_bg == TEXT_BG_OUTLINE)
780 TEXT_AT(1, 0);
781 TEXT_AT(1, 1);
782 TEXT_AT(0, 1);
783 TEXT_AT(-1, 1);
784 TEXT_AT(-1, 0);
785 TEXT_AT(-1, -1);
786 TEXT_AT(0, -1);
787 TEXT_AT(1, -1);
791 gtk_widget_shape_combine_mask(icon->win, icon->mask, 0, 0);
793 *rwidth = width;
794 *rheight = height;
797 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, Icon *icon)
799 GdkFont *font = icon->widget->style->font;
800 int font_height;
801 int width, height;
802 int text_x, text_y;
803 DirItem *item = &icon->item;
804 MaskedPixmap *image = item->image;
805 int image_x;
806 GdkGC *gc = widget->style->black_gc;
807 GtkStateType state = icon->selected ? GTK_STATE_SELECTED
808 : GTK_STATE_NORMAL;
810 font_height = font->ascent + font->descent;
812 gdk_window_get_size(widget->window, &width, &height);
813 image_x = (width - image->width) >> 1;
815 /* TODO: If the shape extension is missing we might need to set
816 * the clip mask here...
818 gdk_draw_pixmap(widget->window, gc,
819 image->pixmap,
820 0, 0,
821 image_x,
822 WINK_FRAME,
823 image->width,
824 image->height);
826 if (item->flags & ITEM_FLAG_SYMLINK)
828 gdk_gc_set_clip_origin(gc, image_x, WINK_FRAME);
829 gdk_gc_set_clip_mask(gc, im_symlink->mask);
830 gdk_draw_pixmap(widget->window, gc,
831 im_symlink->pixmap,
832 0, 0, /* Source x,y */
833 image_x, WINK_FRAME, /* Dest x,y */
834 -1, -1);
835 gdk_gc_set_clip_mask(gc, NULL);
836 gdk_gc_set_clip_origin(gc, 0, 0);
838 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
840 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
841 ? im_mounted
842 : im_unmounted;
844 gdk_gc_set_clip_origin(gc, image_x, WINK_FRAME);
845 gdk_gc_set_clip_mask(gc, mp->mask);
846 gdk_draw_pixmap(widget->window, gc,
847 mp->pixmap,
848 0, 0, /* Source x,y */
849 image_x, WINK_FRAME, /* Dest x,y */
850 -1, -1);
851 gdk_gc_set_clip_mask(gc, NULL);
852 gdk_gc_set_clip_origin(gc, 0, 0);
855 text_x = (width - item->name_width) >> 1;
856 text_y = WINK_FRAME + image->height + GAP + 1;
858 if (o_text_bg != TEXT_BG_NONE)
860 gtk_paint_flat_box(widget->style, widget->window,
861 state,
862 GTK_SHADOW_NONE,
863 NULL, widget, "text",
864 text_x - 1,
865 text_y - 1,
866 item->name_width + 2,
867 font_height + 2);
870 gtk_paint_string(widget->style, widget->window,
871 state,
872 NULL, widget, "text",
873 text_x,
874 text_y + font->ascent,
875 item->leafname);
877 if (current_wink_icon == icon)
879 gdk_draw_rectangle(icon->widget->window,
880 icon->widget->style->white_gc,
881 FALSE,
882 0, 0, width - 1, height - 1);
883 gdk_draw_rectangle(icon->widget->window,
884 icon->widget->style->black_gc,
885 FALSE,
886 1, 1, width - 3, height - 3);
889 return FALSE;
892 static gboolean root_property_event(GtkWidget *widget,
893 GdkEventProperty *event,
894 gpointer data)
896 if (event->atom == win_button_proxy &&
897 event->state == GDK_PROPERTY_NEW_VALUE)
899 /* Setup forwarding on the new proxy window, if possible */
900 forward_root_clicks();
903 return FALSE;
906 static gboolean root_button_press(GtkWidget *widget,
907 GdkEventButton *event,
908 gpointer data)
910 BindAction action;
912 action = bind_lookup_bev(BIND_PINBOARD, event);
914 switch (action)
916 case ACT_CLEAR_SELECTION:
917 pinboard_clear_selection();
918 break;
919 case ACT_POPUP_MENU:
920 dnd_motion_ungrab();
921 show_pinboard_menu(event, NULL);
922 break;
923 case ACT_IGNORE:
924 break;
925 default:
926 g_warning("Unsupported action : %d\n", action);
927 break;
930 return TRUE;
933 static gboolean enter_notify(GtkWidget *widget,
934 GdkEventCrossing *event,
935 Icon *icon)
937 pinboard_icon_may_update(icon);
939 return FALSE;
942 static void perform_action(Icon *icon, GdkEventButton *event)
944 BindAction action;
946 action = bind_lookup_bev(BIND_PINBOARD_ICON, event);
948 switch (action)
950 case ACT_OPEN_ITEM:
951 dnd_motion_ungrab();
952 pinboard_wink_item(icon, TRUE);
953 run_diritem(icon->path, &icon->item, NULL, FALSE);
954 break;
955 case ACT_EDIT_ITEM:
956 dnd_motion_ungrab();
957 pinboard_wink_item(icon, TRUE);
958 run_diritem(icon->path, &icon->item, NULL, TRUE);
959 break;
960 case ACT_POPUP_MENU:
961 dnd_motion_ungrab();
962 show_pinboard_menu(event, icon);
963 break;
964 case ACT_MOVE_ICON:
965 old_x = event->x_root;
966 old_y = event->y_root;
967 icon_old_x = icon->x;
968 icon_old_y = icon->y;
969 dnd_motion_start(MOTION_REPOSITION);
970 break;
971 case ACT_PRIME_AND_SELECT:
972 if (!icon->selected)
973 pinboard_select_only(icon);
974 dnd_motion_start(MOTION_READY_FOR_DND);
975 break;
976 case ACT_PRIME_AND_TOGGLE:
977 pinboard_set_selected(icon, !icon->selected);
978 dnd_motion_start(MOTION_READY_FOR_DND);
979 break;
980 case ACT_PRIME_FOR_DND:
981 pinboard_wink_item(icon, TRUE);
982 dnd_motion_start(MOTION_READY_FOR_DND);
983 break;
984 case ACT_TOGGLE_SELECTED:
985 pinboard_set_selected(icon, !icon->selected);
986 break;
987 case ACT_SELECT_EXCL:
988 pinboard_select_only(icon);
989 break;
990 case ACT_IGNORE:
991 break;
992 default:
993 g_warning("Unsupported action : %d\n", action);
994 break;
998 static gboolean button_release_event(GtkWidget *widget,
999 GdkEventButton *event,
1000 Icon *icon)
1002 if (pinboard_modified)
1003 pinboard_save();
1005 if (dnd_motion_release(event))
1006 return TRUE;
1008 perform_action(icon, event);
1010 return TRUE;
1013 static gboolean button_press_event(GtkWidget *widget,
1014 GdkEventButton *event,
1015 Icon *icon)
1017 if (dnd_motion_press(widget, event))
1018 perform_action(icon, event);
1020 return TRUE;
1023 /* Return a text/uri-list of all the icons in the list */
1024 static guchar *create_uri_list(GList *list)
1026 GString *tmp;
1027 guchar *retval;
1028 guchar *leader;
1030 tmp = g_string_new(NULL);
1031 leader = g_strdup_printf("file://%s", our_host_name());
1033 for (; list; list = list->next)
1035 Icon *icon = (Icon *) list->data;
1037 g_string_append(tmp, leader);
1038 g_string_append(tmp, icon->path);
1039 g_string_append(tmp, "\r\n");
1042 g_free(leader);
1043 retval = tmp->str;
1044 g_string_free(tmp, FALSE);
1046 return retval;
1049 static void start_drag(Icon *icon, GdkEventMotion *event)
1051 GtkWidget *widget = icon->widget;
1052 GList *selected;
1054 if (!icon->selected)
1056 tmp_icon_selected = TRUE;
1057 pinboard_select_only(icon);
1060 selected = pinboard_get_selected();
1061 g_return_if_fail(selected != NULL);
1063 pinboard_drag_in_progress = TRUE;
1065 if (selected->next == NULL)
1066 drag_one_item(widget, event, icon->path, &icon->item);
1067 else
1069 guchar *uri_list;
1071 uri_list = create_uri_list(selected);
1072 drag_selection(widget, event, uri_list);
1073 g_free(uri_list);
1076 g_list_free(selected);
1079 /* An icon is being dragged around... */
1080 static gint icon_motion_notify(GtkWidget *widget,
1081 GdkEventMotion *event,
1082 Icon *icon)
1084 int x, y;
1085 int width, height;
1086 int dx,dy;
1088 if (motion_state == MOTION_READY_FOR_DND)
1090 if (dnd_motion_moved(event))
1091 start_drag(icon, event);
1092 return TRUE;
1094 else if (motion_state != MOTION_REPOSITION)
1095 return FALSE;
1097 /* How far the pointer has moved since the drag started */
1098 dx = event->x_root - old_x;
1099 dy = event->y_root - old_y;
1101 x = icon_old_x + dx;
1102 y = icon_old_y + dy;
1104 snap_to_grid(&x, &y);
1106 if (icon->x == x && icon->y == y)
1107 return TRUE;
1109 icon->x = x;
1110 icon->y = y;
1111 gdk_window_get_size(icon->win->window, &width, &height);
1112 offset_from_centre(icon, width, height, &x, &y);
1114 gdk_window_move(icon->win->window, x, y);
1116 /* Store the fixed position for the center of the icon */
1117 offset_to_centre(icon, width, height, &x, &y);
1118 icon->x = x;
1119 icon->y = y;
1121 pinboard_modified = TRUE;
1123 return TRUE;
1126 /* Called for each line in the pinboard file while loading a new board */
1127 static char *pin_from_file(guchar *line)
1129 guchar *leaf = NULL;
1130 int x, y, n;
1132 if (*line == '<')
1134 guchar *end;
1136 end = strchr(line + 1, '>');
1137 if (!end)
1138 return _("Missing '>' in icon label");
1140 leaf = g_strndup(line + 1, end - line - 1);
1142 line = end + 1;
1144 while (isspace(*line))
1145 line++;
1146 if (*line != ',')
1147 return _("Missing ',' after icon label");
1148 line++;
1151 if (sscanf(line, " %d , %d , %n", &x, &y, &n) < 2)
1152 return NULL; /* Ignore format errors */
1154 pinboard_pin(line + n, leaf, x, y, FALSE);
1156 g_free(leaf);
1158 return NULL;
1161 /* Make sure that clicks and drops on the root window come to us...
1162 * False if an error occurred (ie, someone else is using it).
1164 static gboolean add_root_handlers(void)
1166 GdkWindow *root;
1168 if (!proxy_invisible)
1170 GtkTargetEntry target_table[] =
1172 {"text/uri-list", 0, TARGET_URI_LIST},
1173 {"STRING", 0, TARGET_STRING},
1176 win_button_proxy = gdk_atom_intern("_WIN_DESKTOP_BUTTON_PROXY",
1177 FALSE);
1178 proxy_invisible = gtk_invisible_new();
1179 gtk_widget_show(proxy_invisible);
1181 gdk_window_add_filter(proxy_invisible->window,
1182 proxy_filter, NULL);
1185 gtk_signal_connect(GTK_OBJECT(proxy_invisible),
1186 "property_notify_event",
1187 GTK_SIGNAL_FUNC(root_property_event), NULL);
1188 gtk_signal_connect(GTK_OBJECT(proxy_invisible),
1189 "button_press_event",
1190 GTK_SIGNAL_FUNC(root_button_press), NULL);
1192 /* Drag and drop handlers */
1193 drag_set_pinboard_dest(proxy_invisible);
1194 gtk_signal_connect(GTK_OBJECT(proxy_invisible), "drag_motion",
1195 GTK_SIGNAL_FUNC(bg_drag_motion),
1196 NULL);
1198 /* The proxy window is also used to hold the selection... */
1199 gtk_signal_connect(GTK_OBJECT(proxy_invisible),
1200 "selection_clear_event",
1201 GTK_SIGNAL_FUNC(lose_selection),
1202 NULL);
1204 gtk_signal_connect(GTK_OBJECT(proxy_invisible),
1205 "selection_get",
1206 GTK_SIGNAL_FUNC(selection_get), NULL);
1208 gtk_selection_add_targets(proxy_invisible,
1209 GDK_SELECTION_PRIMARY,
1210 target_table,
1211 sizeof(target_table) / sizeof(*target_table));
1214 root = gdk_window_lookup(GDK_ROOT_WINDOW());
1215 if (!root)
1216 root = gdk_window_foreign_new(GDK_ROOT_WINDOW());
1218 if (!setup_xdnd_proxy(GDK_ROOT_WINDOW(), proxy_invisible->window))
1219 return FALSE;
1221 /* Forward events from the root window to our proxy window */
1222 gdk_window_add_filter(GDK_ROOT_PARENT(), proxy_filter, NULL);
1223 gdk_window_set_user_data(GDK_ROOT_PARENT(), proxy_invisible);
1224 gdk_window_set_events(GDK_ROOT_PARENT(),
1225 gdk_window_get_events(GDK_ROOT_PARENT()) |
1226 GDK_PROPERTY_CHANGE_MASK);
1228 forward_root_clicks();
1230 return TRUE;
1233 /* See if the window manager is offering to forward root window clicks.
1234 * If so, grab them. Otherwise, do nothing.
1235 * Call this whenever the _WIN_DESKTOP_BUTTON_PROXY property changes.
1237 static void forward_root_clicks(void)
1239 click_proxy_gdk_window = find_click_proxy_window();
1240 if (!click_proxy_gdk_window)
1241 return;
1243 /* Events on the wm's proxy are dealt with by our proxy widget */
1244 gdk_window_set_user_data(click_proxy_gdk_window, proxy_invisible);
1245 gdk_window_add_filter(click_proxy_gdk_window, proxy_filter, NULL);
1247 /* The proxy window for clicks sends us button press events with
1248 * SubstructureNotifyMask. We need StructureNotifyMask to receive
1249 * DestroyNotify events, too.
1251 XSelectInput(GDK_DISPLAY(),
1252 GDK_WINDOW_XWINDOW(click_proxy_gdk_window),
1253 SubstructureNotifyMask | StructureNotifyMask);
1256 /* Write the current state of the pinboard to the current pinboard file */
1257 static void pinboard_save(void)
1259 guchar *save = NULL;
1260 GString *tmp = NULL;
1261 FILE *file = NULL;
1262 GList *next;
1263 guchar *save_new = NULL;
1265 g_return_if_fail(current_pinboard != NULL);
1267 pinboard_modified = FALSE;
1269 if (strchr(current_pinboard->name, '/'))
1270 save = g_strdup(current_pinboard->name);
1271 else
1273 guchar *leaf;
1275 leaf = g_strconcat("pb_", current_pinboard->name, NULL);
1276 save = choices_find_path_save(leaf, "ROX-Filer", TRUE);
1277 g_free(leaf);
1280 if (!save)
1281 return;
1283 save_new = g_strconcat(save, ".new", NULL);
1284 file = fopen(save_new, "wb");
1285 if (!file)
1286 goto err;
1288 tmp = g_string_new(NULL);
1289 for (next = current_pinboard->icons; next; next = next->next)
1291 Icon *icon = (Icon *) next->data;
1293 g_string_sprintf(tmp, "<%s>, %d, %d, %s\n",
1294 icon->item.leafname, icon->x, icon->y, icon->src_path);
1295 if (fwrite(tmp->str, 1, tmp->len, file) < tmp->len)
1296 goto err;
1299 if (fclose(file))
1301 file = NULL;
1302 goto err;
1305 file = NULL;
1307 if (rename(save_new, save))
1308 goto err;
1310 goto out;
1311 err:
1312 delayed_error(_("Error saving pinboard"), g_strerror(errno));
1313 out:
1314 if (file)
1315 fclose(file);
1316 if (tmp)
1317 g_string_free(tmp, TRUE);
1319 g_free(save_new);
1320 g_free(save);
1324 * Filter that translates proxied events from virtual root windows into normal
1325 * Gdk events for the proxy_invisible widget. Stolen from gmc.
1327 * Also gets events from the root window.
1329 static GdkFilterReturn proxy_filter(GdkXEvent *xevent,
1330 GdkEvent *event,
1331 gpointer data)
1333 XEvent *xev;
1334 GdkWindow *proxy = proxy_invisible->window;
1336 xev = xevent;
1338 switch (xev->type) {
1339 case ButtonPress:
1340 case ButtonRelease:
1341 /* Translate button events into events that come from
1342 * the proxy window, so that we can catch them as a
1343 * signal from the invisible widget.
1345 if (xev->type == ButtonPress)
1346 event->button.type = GDK_BUTTON_PRESS;
1347 else
1348 event->button.type = GDK_BUTTON_RELEASE;
1350 gdk_window_ref(proxy);
1352 event->button.window = proxy;
1353 event->button.send_event = xev->xbutton.send_event;
1354 event->button.time = xev->xbutton.time;
1355 event->button.x_root = xev->xbutton.x_root;
1356 event->button.y_root = xev->xbutton.y_root;
1357 event->button.x = xev->xbutton.x;
1358 event->button.y = xev->xbutton.y;
1359 event->button.state = xev->xbutton.state;
1360 event->button.button = xev->xbutton.button;
1362 return GDK_FILTER_TRANSLATE;
1364 case DestroyNotify:
1365 /* XXX: I have no idea why this helps, but it does! */
1366 /* The proxy window was destroyed (i.e. the window
1367 * manager died), so we have to cope with it
1369 if (((GdkEventAny *) event)->window == proxy)
1370 gdk_window_destroy_notify(proxy);
1372 return GDK_FILTER_REMOVE;
1374 default:
1375 break;
1378 return GDK_FILTER_CONTINUE;
1381 /* Does not save the new state */
1382 static void icon_destroyed(GtkWidget *widget, Icon *icon)
1384 g_return_if_fail(icon != NULL);
1386 icon_unhash_path(icon);
1388 if (icon->selected)
1389 change_number_selected(-1);
1391 if (current_wink_icon == icon)
1392 current_wink_icon = NULL;
1394 gdk_pixmap_unref(icon->mask);
1395 dir_item_clear(&icon->item);
1396 g_free(icon->src_path);
1397 g_free(icon->path);
1398 g_free(icon);
1400 if (current_pinboard)
1401 current_pinboard->icons =
1402 g_list_remove(current_pinboard->icons, icon);
1405 static void snap_to_grid(int *x, int *y)
1407 *x = ((*x + o_grid_step / 2) / o_grid_step) * o_grid_step;
1408 *y = ((*y + o_grid_step / 2) / o_grid_step) * o_grid_step;
1411 /* Convert (x,y) from a centre point to a window position */
1412 static void offset_from_centre(Icon *icon,
1413 int width, int height,
1414 int *x, int *y)
1416 *x -= width >> 1;
1417 *y -= height - (icon->widget->style->font->descent >> 1);
1418 *x = CLAMP(*x, 0, screen_width - (o_clamp_icons ? width : 0));
1419 *y = CLAMP(*y, 0, screen_height - (o_clamp_icons ? height : 0));
1422 /* Convert (x,y) from a window position to a centre point */
1423 static void offset_to_centre(Icon *icon,
1424 int width, int height,
1425 int *x, int *y)
1427 *x += width >> 1;
1428 *y += height - (icon->widget->style->font->descent >> 1);
1431 /* Same as drag_set_dest(), but for pinboard icons */
1432 static void drag_set_pinicon_dest(Icon *icon)
1434 GtkObject *obj = GTK_OBJECT(icon->widget);
1436 make_drop_target(icon->widget, 0);
1438 gtk_signal_connect(obj, "drag_motion",
1439 GTK_SIGNAL_FUNC(drag_motion), icon);
1440 gtk_signal_connect(obj, "drag_leave",
1441 GTK_SIGNAL_FUNC(drag_leave), icon);
1442 gtk_signal_connect(obj, "drag_end",
1443 GTK_SIGNAL_FUNC(drag_end), icon);
1446 /* Called during the drag when the mouse is in a widget registered
1447 * as a drop target. Returns TRUE if we can accept the drop.
1449 static gboolean drag_motion(GtkWidget *widget,
1450 GdkDragContext *context,
1451 gint x,
1452 gint y,
1453 guint time,
1454 Icon *icon)
1456 GdkDragAction action = context->suggested_action;
1457 char *type = NULL;
1458 DirItem *item = &icon->item;
1460 if (gtk_drag_get_source_widget(context) == widget)
1461 goto out; /* Can't drag something to itself! */
1463 if (icon->selected)
1464 goto out; /* Can't drag a selection to itself */
1466 type = dnd_motion_item(context, &item);
1468 if (!item)
1469 type = NULL;
1470 out:
1471 /* We actually must pretend to accept the drop, even if the
1472 * directory isn't writeable, so that the spring-opening
1473 * thing works.
1476 /* Don't allow drops to non-writeable directories */
1477 if (option_get_int("dnd_spring_open") == FALSE &&
1478 type == drop_dest_dir &&
1479 access(icon->path, W_OK) != 0)
1481 type = NULL;
1484 g_dataset_set_data(context, "drop_dest_type", type);
1485 if (type)
1487 gdk_drag_status(context, action, time);
1488 g_dataset_set_data_full(context, "drop_dest_path",
1489 g_strdup(icon->path), g_free);
1490 if (type == drop_dest_dir)
1491 dnd_spring_load(context);
1493 pinboard_wink_item(icon, FALSE);
1496 return type != NULL;
1499 static void drag_leave(GtkWidget *widget,
1500 GdkDragContext *context,
1501 guint32 time,
1502 Icon *icon)
1504 pinboard_wink_item(NULL, FALSE);
1505 dnd_spring_abort();
1508 /* When changing the 'selected' attribute of an icon, call this
1509 * to update the global counter and claim or release the primary
1510 * selection as needed.
1512 static void change_number_selected(int delta)
1514 guint32 time;
1516 g_return_if_fail(delta != 0);
1517 g_return_if_fail(number_selected + delta >= 0);
1519 if (number_selected == 0)
1521 time = gdk_event_get_time(gtk_get_current_event());
1523 gtk_selection_owner_set(proxy_invisible,
1524 GDK_SELECTION_PRIMARY,
1525 time);
1528 number_selected += delta;
1530 if (number_selected == 0)
1532 time = gdk_event_get_time(gtk_get_current_event());
1534 gtk_selection_owner_set(NULL,
1535 GDK_SELECTION_PRIMARY,
1536 time);
1540 /* Called when another application wants the contents of our selection */
1541 static void selection_get(GtkWidget *widget,
1542 GtkSelectionData *selection_data,
1543 guint info,
1544 guint time,
1545 gpointer data)
1547 GString *str;
1548 GList *next;
1549 guchar *leader = NULL;
1551 str = g_string_new(NULL);
1553 if (info == TARGET_URI_LIST)
1554 leader = g_strdup_printf("file://%s", our_host_name());
1556 for (next = current_pinboard->icons; next; next = next->next)
1558 Icon *icon = (Icon *) next->data;
1560 if (!icon->selected)
1561 continue;
1563 if (leader)
1564 g_string_append(str, leader);
1565 g_string_append(str, icon->path);
1566 g_string_append_c(str, ' ');
1569 g_free(leader);
1571 gtk_selection_data_set(selection_data,
1572 gdk_atom_intern("STRING", FALSE),
1574 str->str,
1575 str->len ? str->len - 1 : 0);
1577 g_string_free(str, TRUE);
1580 /* Called when another application takes the selection away from us */
1581 static gint lose_selection(GtkWidget *widget, GdkEventSelection *event)
1583 /* 'lock' number_selected so that we don't send any events */
1584 number_selected++;
1585 pinboard_clear_selection();
1586 number_selected--;
1588 return TRUE;
1591 static gboolean bg_drag_motion(GtkWidget *widget,
1592 GdkDragContext *context,
1593 gint x,
1594 gint y,
1595 guint time,
1596 gpointer data)
1598 /* Dragging from the pinboard to the pinboard is not allowed */
1599 if (pinboard_drag_in_progress)
1600 return FALSE;
1602 gdk_drag_status(context, context->suggested_action, time);
1603 return TRUE;
1606 static void drag_end(GtkWidget *widget,
1607 GdkDragContext *context,
1608 Icon *icon)
1610 pinboard_drag_in_progress = FALSE;
1611 if (tmp_icon_selected)
1613 pinboard_clear_selection();
1614 tmp_icon_selected = FALSE;
1618 /* Something which affects all the icons has changed - reshape
1619 * and redraw all of them.
1621 static void reshape_all(void)
1623 GList *next;
1625 g_return_if_fail(current_pinboard != NULL);
1627 for (next = current_pinboard->icons; next; next = next->next)
1629 Icon *icon = (Icon *) next->data;
1630 reshape_icon(icon);
1634 /* Display the pinboard menu. Set icon to NULL if no particular icon
1635 * was clicked.
1637 static void show_pinboard_menu(GdkEventButton *event, Icon *icon)
1639 int pos[2];
1640 GList *icons;
1642 /* Remove the previous appmenu used on this menu */
1643 appmenu_remove();
1645 if (icon)
1647 if (icon->selected)
1648 tmp_icon_selected = FALSE;
1649 else
1651 pinboard_select_only(icon);
1652 tmp_icon_selected = TRUE;
1656 icons = pinboard_get_selected();
1658 pos[0] = event->x_root;
1659 pos[1] = event->y_root;
1661 if (icons)
1663 menu_set_items_shaded(pinboard_menu,
1664 icons->next ? TRUE : FALSE, 4, 3);
1666 menu_set_items_shaded(pinboard_menu, FALSE, 7, 1);
1668 /* Check for app-specific menu */
1669 if (!icons->next)
1671 Icon *icon = (Icon *) icons->data;
1672 appmenu_add(icon->path, &icon->item, pinboard_menu);
1675 else
1676 menu_set_items_shaded(pinboard_menu, TRUE, 4, 4);
1678 gtk_menu_popup(GTK_MENU(pinboard_menu), NULL, NULL, position_menu,
1679 (gpointer) pos, event->button, event->time);
1682 static void pin_help(gpointer data, guint action, GtkWidget *widget)
1684 Icon *icon;
1686 icon = pinboard_selected_icon();
1688 if (icon)
1689 show_item_help(icon->path, &icon->item);
1690 else
1691 delayed_error(PROJECT,
1692 _("You must first select a single pinned icon to get "
1693 "help on."));
1696 static void pin_remove(gpointer data, guint action, GtkWidget *widget)
1698 pinboard_unpin_selection();
1701 static void menu_closed(GtkWidget *widget)
1703 appmenu_remove();
1705 if (tmp_icon_selected)
1707 pinboard_clear_selection();
1708 tmp_icon_selected = FALSE;
1712 /* Show where this item is stored */
1713 static void show_location(gpointer data, guint action, GtkWidget *widget)
1715 Icon *icon;
1717 icon = pinboard_selected_icon();
1719 if (icon)
1720 open_to_show(icon->path);
1721 else
1723 delayed_error(PROJECT,
1724 _("Select a single item, then use this to find out "
1725 "where it is in the filesystem."));
1729 static void rename_cb(Icon *icon)
1731 reshape_icon(icon);
1733 pinboard_save();
1736 static void edit_icon(gpointer data, guint action, GtkWidget *widget)
1738 Icon *icon;
1740 icon = pinboard_selected_icon();
1742 if (icon)
1743 show_rename_box(icon->widget, icon, rename_cb);
1744 else
1746 delayed_error(PROJECT,
1747 _("First, select a single item to edit"));
1748 return;