r496: Many internal changes to the Options system.
[rox-filer.git] / ROX-Filer / src / pinboard.c
blobc7f5e05eb2e531137afb1d973bee6923eab1d94b
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2000, Thomas Leonard, <tal197@users.sourceforge.net>.
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;
115 /* Static prototypes */
116 static void set_size_and_shape(Icon *icon, int *rwidth, int *rheight);
117 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, Icon *icon);
118 static void mask_wink_border(Icon *icon, GdkColor *alpha);
119 static gint end_wink(gpointer data);
120 static gboolean button_release_event(GtkWidget *widget,
121 GdkEventButton *event,
122 Icon *icon);
123 static gboolean root_property_event(GtkWidget *widget,
124 GdkEventProperty *event,
125 gpointer data);
126 static gboolean root_button_press(GtkWidget *widget,
127 GdkEventButton *event,
128 gpointer data);
129 static gboolean enter_notify(GtkWidget *widget,
130 GdkEventCrossing *event,
131 Icon *icon);
132 static gboolean button_press_event(GtkWidget *widget,
133 GdkEventButton *event,
134 Icon *icon);
135 static gint icon_motion_notify(GtkWidget *widget,
136 GdkEventMotion *event,
137 Icon *icon);
138 static char *pin_from_file(guchar *line);
139 static gboolean add_root_handlers(void);
140 static void pinboard_save(void);
141 static GdkFilterReturn proxy_filter(GdkXEvent *xevent,
142 GdkEvent *event,
143 gpointer data);
144 static void icon_destroyed(GtkWidget *widget, Icon *icon);
145 static void snap_to_grid(int *x, int *y);
146 static void offset_from_centre(Icon *icon,
147 int width, int height,
148 int *x, int *y);
149 static void offset_to_centre(Icon *icon,
150 int width, int height,
151 int *x, int *y);
152 static gboolean drag_motion(GtkWidget *widget,
153 GdkDragContext *context,
154 gint x,
155 gint y,
156 guint time,
157 Icon *icon);
158 static void drag_set_pinicon_dest(Icon *icon);
159 static void drag_leave(GtkWidget *widget,
160 GdkDragContext *context,
161 guint32 time,
162 Icon *icon);
163 static void forward_root_clicks(void);
164 static void change_number_selected(int delta);
165 static gint lose_selection(GtkWidget *widget, GdkEventSelection *event);
166 static void selection_get(GtkWidget *widget,
167 GtkSelectionData *selection_data,
168 guint info,
169 guint time,
170 gpointer data);
171 static gboolean bg_drag_motion(GtkWidget *widget,
172 GdkDragContext *context,
173 gint x,
174 gint y,
175 guint time,
176 gpointer data);
177 static void drag_end(GtkWidget *widget,
178 GdkDragContext *context,
179 Icon *icon);
180 static void reshape_icon(Icon *icon);
181 static void reshape_all(void);
182 static void menu_closed(GtkWidget *widget);
183 static void edit_icon(gpointer data, guint action, GtkWidget *widget);
184 static void show_location(gpointer data, guint action, GtkWidget *widget);
185 static void pin_help(gpointer data, guint action, GtkWidget *widget);
186 static void pin_remove(gpointer data, guint action, GtkWidget *widget);
187 static void show_pinboard_menu(GdkEventButton *event, Icon *icon);
189 static void pinboard_check_options(void);
191 static GtkItemFactoryEntry menu_def[] = {
192 {N_("ROX-Filer Help"), NULL, menu_rox_help, 0, NULL},
193 {N_("ROX-Filer Options..."), NULL, menu_show_options, 0, NULL},
194 {N_("Open Home Directory"), NULL, open_home, 0, NULL},
195 {"", NULL, NULL, 0, "<Separator>"},
196 {N_("Edit Icon"), NULL, edit_icon, 0, NULL},
197 {N_("Show Location"), NULL, show_location, 0, NULL},
198 {N_("Show Help"), NULL, pin_help, 0, NULL},
199 {N_("Remove Item(s)"), NULL, pin_remove, 0, NULL},
202 static GtkWidget *pinboard_menu; /* The popup pinboard menu */
204 /****************************************************************
205 * EXTERNAL INTERFACE *
206 ****************************************************************/
208 void pinboard_init(void)
210 GtkStyle *style;
212 option_add_string("pinboard_fg_colour", "#000", NULL);
213 option_add_string("pinboard_bg_colour", "#ddd", NULL);
215 option_add_int("pinboard_text_bg", TEXT_BG_SOLID, NULL);
216 option_add_int("pinboard_clamp_icons", 1, NULL);
217 option_add_int("pinboard_grid_step", GRID_STEP_COARSE, NULL);
218 option_add_notify(pinboard_check_options);
220 style = gtk_widget_get_default_style();
222 gdk_color_parse(option_get_static_string("pinboard_fg_colour"),
223 &text_fg_col);
224 gdk_color_parse(option_get_static_string("pinboard_bg_colour"),
225 &text_bg_col);
227 pinboard_menu = menu_create(menu_def,
228 sizeof(menu_def) / sizeof(*menu_def),
229 "<pinboard>");
230 gtk_signal_connect(GTK_OBJECT(pinboard_menu), "unmap_event",
231 GTK_SIGNAL_FUNC(menu_closed), NULL);
232 /* This is used for AppMenus */
233 gtk_object_set_data(GTK_OBJECT(pinboard_menu), "last_appmenu", 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 if (path)
296 loading_pinboard++;
297 parse_file(path, pin_from_file);
298 loading_pinboard--;
299 g_free(path);
303 /* Add a new icon to the background.
304 * 'path' should be an absolute pathname.
305 * 'x' and 'y' are the coordinates of the point in the middle of the text.
306 * 'name' is the name to use. If NULL then the leafname of path is used.
308 void pinboard_pin(guchar *path, guchar *name, int x, int y)
310 Icon *icon;
311 int width, height;
313 g_return_if_fail(path != NULL);
314 g_return_if_fail(current_pinboard != NULL);
316 icon = g_new(Icon, 1);
317 icon->type = ICON_PINBOARD;
318 icon->selected = FALSE;
319 icon->src_path = g_strdup(path);
320 icon->path = icon_convert_path(path);
321 icon->mask = NULL;
322 snap_to_grid(&x, &y);
323 icon->x = x;
324 icon->y = y;
326 icon_hash_path(icon);
328 dir_stat(icon->path, &icon->item, FALSE);
330 if (!name)
332 name = strrchr(icon->path, '/');
333 if (name && name[1])
334 name++;
335 else
336 name = icon->path;
339 icon->item.leafname = g_strdup(name);
341 icon->win = gtk_window_new(GTK_WINDOW_DIALOG);
342 gtk_window_set_wmclass(GTK_WINDOW(icon->win), "ROX-Pinboard", PROJECT);
344 icon->widget = gtk_drawing_area_new();
345 gtk_container_add(GTK_CONTAINER(icon->win), icon->widget);
346 drag_set_pinicon_dest(icon);
347 gtk_signal_connect(GTK_OBJECT(icon->widget), "drag_data_get",
348 drag_data_get, NULL);
350 gtk_widget_realize(icon->win);
351 gtk_widget_realize(icon->widget);
353 set_size_and_shape(icon, &width, &height);
354 offset_from_centre(icon, width, height, &x, &y);
355 gtk_widget_set_uposition(icon->win, x, y);
356 /* Set the correct position in the icon */
357 offset_to_centre(icon, width, height, &x, &y);
358 icon->x = x;
359 icon->y = y;
361 make_panel_window(icon->win->window);
363 gtk_widget_add_events(icon->widget,
364 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
365 GDK_BUTTON1_MOTION_MASK | GDK_ENTER_NOTIFY_MASK |
366 GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK);
367 gtk_signal_connect(GTK_OBJECT(icon->widget), "enter-notify-event",
368 GTK_SIGNAL_FUNC(enter_notify), icon);
369 gtk_signal_connect(GTK_OBJECT(icon->widget), "button-press-event",
370 GTK_SIGNAL_FUNC(button_press_event), icon);
371 gtk_signal_connect(GTK_OBJECT(icon->widget), "button-release-event",
372 GTK_SIGNAL_FUNC(button_release_event), icon);
373 gtk_signal_connect(GTK_OBJECT(icon->widget), "motion-notify-event",
374 GTK_SIGNAL_FUNC(icon_motion_notify), icon);
375 gtk_signal_connect(GTK_OBJECT(icon->widget), "expose-event",
376 GTK_SIGNAL_FUNC(draw_icon), icon);
377 gtk_signal_connect(GTK_OBJECT(icon->win), "destroy",
378 GTK_SIGNAL_FUNC(icon_destroyed), icon);
380 current_pinboard->icons = g_list_prepend(current_pinboard->icons,
381 icon);
382 gtk_widget_show_all(icon->win);
383 gdk_window_lower(icon->win->window);
385 if (!loading_pinboard)
386 pinboard_save();
389 /* Remove an icon from the pinboard */
390 void pinboard_unpin(Icon *icon)
392 g_return_if_fail(icon != NULL);
394 gtk_widget_destroy(icon->win);
395 pinboard_save();
398 /* Unpin all selected items */
399 void pinboard_unpin_selection(void)
401 GList *next;
403 g_return_if_fail(current_pinboard != NULL);
405 if (number_selected == 0)
407 delayed_error(PROJECT,
408 _("You should first select some pinned icons to "
409 "unpin. Hold down the Ctrl key to select some icons."));
410 return;
413 next = current_pinboard->icons;
414 while (next)
416 Icon *icon = (Icon *) next->data;
418 next = next->next;
420 if (icon->selected)
421 gtk_widget_destroy(icon->win);
424 pinboard_save();
427 /* Put a border around the icon, briefly.
428 * If icon is NULL then cancel any existing wink.
429 * The icon will automatically unhighlight unless timeout is FALSE,
430 * in which case you must call this function again (with NULL or another
431 * icon) to remove the highlight.
433 void pinboard_wink_item(Icon *icon, gboolean timeout)
435 if (current_wink_icon == icon)
436 return;
438 if (current_wink_icon)
440 mask_wink_border(current_wink_icon, &mask_transp);
441 if (wink_timeout != -1)
442 gtk_timeout_remove(wink_timeout);
445 current_wink_icon = icon;
447 if (current_wink_icon)
449 mask_wink_border(current_wink_icon, &mask_solid);
450 if (timeout)
451 wink_timeout = gtk_timeout_add(300, end_wink, NULL);
452 else
453 wink_timeout = -1;
457 /* Remove everything on the current pinboard and disables the pinboard.
458 * Does not change any files. Does not change number_of_windows.
460 void pinboard_clear(void)
462 GList *next;
464 g_return_if_fail(current_pinboard != NULL);
466 next = current_pinboard->icons;
467 while (next)
469 Icon *icon = (Icon *) next->data;
471 next = next->next;
473 gtk_widget_destroy(icon->win);
476 g_free(current_pinboard->name);
477 g_free(current_pinboard);
478 current_pinboard = NULL;
480 release_xdnd_proxy(GDK_ROOT_WINDOW());
481 gdk_window_remove_filter(GDK_ROOT_PARENT(), proxy_filter, NULL);
482 gdk_window_set_user_data(GDK_ROOT_PARENT(), NULL);
485 /* Return the single selected icon, or NULL */
486 Icon *pinboard_selected_icon(void)
488 GList *next;
489 Icon *found = NULL;
491 g_return_val_if_fail(current_pinboard != NULL, NULL);
493 for (next = current_pinboard->icons; next; next = next->next)
495 Icon *icon = (Icon *) next->data;
497 if (icon->selected)
499 if (found)
500 return NULL; /* >1 icon selected */
501 else
502 found = icon;
506 return found;
509 void pinboard_clear_selection(void)
511 pinboard_select_only(NULL);
514 /* Set whether an icon is selected or not */
515 void pinboard_set_selected(Icon *icon, gboolean selected)
517 g_return_if_fail(icon != NULL);
519 if (icon->selected == selected)
520 return;
522 if (selected)
523 change_number_selected(+1);
524 else
525 change_number_selected(-1);
527 icon->selected = selected;
528 gtk_widget_queue_draw(icon->win);
531 /* Return a list of all the selected icons.
532 * g_list_free() the result.
534 GList *pinboard_get_selected(void)
536 GList *next;
537 GList *selected = NULL;
539 for (next = current_pinboard->icons; next; next = next->next)
541 Icon *i = (Icon *) next->data;
543 if (i->selected)
544 selected = g_list_append(selected, i);
547 return selected;
550 /* Clear the selection and then select this icon.
551 * Doesn't release and claim the selection unnecessarily.
552 * If icon is NULL, then just clears the selection.
554 void pinboard_select_only(Icon *icon)
556 GList *next;
558 g_return_if_fail(current_pinboard != NULL);
560 if (icon)
561 pinboard_set_selected(icon, TRUE);
563 for (next = current_pinboard->icons; next; next = next->next)
565 Icon *i = (Icon *) next->data;
567 if (i->selected && i != icon)
568 pinboard_set_selected(i, FALSE);
573 /****************************************************************
574 * INTERNAL FUNCTIONS *
575 ****************************************************************/
577 static void pinboard_check_options(void)
579 int old_text_bg = o_text_bg;
580 GdkColor n_fg, n_bg;
582 o_text_bg = option_get_int("pinboard_text_bg");
583 o_grid_step = option_get_int("pinboard_grid_step");
584 o_clamp_icons = option_get_int("pinboard_clamp_icons");
586 gdk_color_parse(option_get_static_string("pinboard_fg_colour"), &n_fg);
587 gdk_color_parse(option_get_static_string("pinboard_bg_colour"), &n_bg);
589 if (o_text_bg != old_text_bg ||
590 gdk_color_equal(&n_fg, &text_fg_col) == 0 ||
591 gdk_color_equal(&n_bg, &text_bg_col) == 0)
593 memcpy(&text_fg_col, &n_fg, sizeof(GdkColor));
594 memcpy(&text_bg_col, &n_bg, sizeof(GdkColor));
596 if (pinicon_style)
598 gtk_style_unref(pinicon_style);
599 pinicon_style = NULL;
602 reshape_all();
606 /* Icon's size, shape or appearance has changed - update the display */
607 static void reshape_icon(Icon *icon)
609 int x = icon->x, y = icon->y;
610 int width, height;
612 set_size_and_shape(icon, &width, &height);
613 gdk_window_resize(icon->win->window, width, height);
614 offset_from_centre(icon, width, height, &x, &y);
615 gtk_widget_set_uposition(icon->win, x, y);
616 gtk_widget_queue_draw(icon->win);
619 /* See if the file the icon points to has changed. Update the icon
620 * if so.
622 void pinboard_icon_may_update(Icon *icon)
624 MaskedPixmap *image = icon->item.image;
625 int flags = icon->item.flags;
627 pixmap_ref(image);
628 mount_update(FALSE);
629 dir_restat(icon->path, &icon->item, FALSE);
631 if (icon->item.image != image || icon->item.flags != flags)
632 reshape_icon(icon);
634 pixmap_unref(image);
637 static gint end_wink(gpointer data)
639 pinboard_wink_item(NULL, FALSE);
640 return FALSE;
643 /* Make the wink border solid or transparent */
644 static void mask_wink_border(Icon *icon, GdkColor *alpha)
646 int width, height;
648 gdk_window_get_size(icon->widget->window, &width, &height);
650 gdk_gc_set_foreground(mask_gc, alpha);
651 gdk_draw_rectangle(icon->mask, mask_gc, FALSE,
652 0, 0, width - 1, height - 1);
653 gdk_draw_rectangle(icon->mask, mask_gc, FALSE,
654 1, 1, width - 3, height - 3);
656 gtk_widget_shape_combine_mask(icon->win, icon->mask, 0, 0);
658 gtk_widget_draw(icon->widget, NULL);
661 #define TEXT_AT(dx, dy) \
662 gdk_draw_string(icon->mask, font, mask_gc, \
663 text_x + dx, y + dy, \
664 item->leafname);
666 /* Updates the name_width field and resizes and masks the window.
667 * Also sets the style to pinicon_style, generating it if needed.
668 * Returns the new width and height.
670 static void set_size_and_shape(Icon *icon, int *rwidth, int *rheight)
672 int width, height;
673 GdkFont *font;
674 int font_height;
675 MaskedPixmap *image = icon->item.image;
676 DirItem *item = &icon->item;
677 int text_x, text_y;
679 if (!pinicon_style)
681 pinicon_style = gtk_style_copy(icon->widget->style);
682 memcpy(&pinicon_style->fg[GTK_STATE_NORMAL],
683 &text_fg_col, sizeof(GdkColor));
684 memcpy(&pinicon_style->bg[GTK_STATE_NORMAL],
685 &text_bg_col, sizeof(GdkColor));
687 gtk_widget_set_style(icon->widget, pinicon_style);
689 font = pinicon_style->font;
690 font_height = font->ascent + font->descent;
691 item->name_width = gdk_string_width(font, item->leafname);
693 width = MAX(image->width, item->name_width + 2) +
694 2 * WINK_FRAME;
695 height = image->height + GAP + (font_height + 2) + 2 * WINK_FRAME;
696 gtk_widget_set_usize(icon->win, width, height);
698 if (icon->mask)
699 gdk_pixmap_unref(icon->mask);
700 icon->mask = gdk_pixmap_new(icon->win->window, width, height, 1);
701 if (!mask_gc)
702 mask_gc = gdk_gc_new(icon->mask);
704 /* Clear the mask to transparent */
705 gdk_gc_set_foreground(mask_gc, &mask_transp);
706 gdk_draw_rectangle(icon->mask, mask_gc, TRUE, 0, 0, width, height);
708 gdk_gc_set_foreground(mask_gc, &mask_solid);
709 /* Make the icon area solid */
710 if (image->mask)
712 gdk_draw_pixmap(icon->mask, mask_gc, image->mask,
713 0, 0,
714 (width - image->width) >> 1,
715 WINK_FRAME,
716 image->width,
717 image->height);
719 else
721 gdk_draw_rectangle(icon->mask, mask_gc, TRUE,
722 (width - image->width) >> 1,
723 WINK_FRAME,
724 image->width,
725 image->height);
728 gdk_gc_set_function(mask_gc, GDK_OR);
729 if (item->flags & ITEM_FLAG_SYMLINK)
731 gdk_draw_pixmap(icon->mask, mask_gc, im_symlink->mask,
732 0, 0, /* Source x,y */
733 (width - image->width) >> 1, /* Dest x */
734 WINK_FRAME, /* Dest y */
735 -1, -1);
737 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
739 /* Note: Both mount state pixmaps must have the same mask */
740 gdk_draw_pixmap(icon->mask, mask_gc, im_mounted->mask,
741 0, 0, /* Source x,y */
742 (width - image->width) >> 1, /* Dest x */
743 WINK_FRAME, /* Dest y */
744 -1, -1);
746 gdk_gc_set_function(mask_gc, GDK_COPY);
748 /* Mask off an area for the text (from o_text_bg) */
750 text_x = (width - item->name_width) >> 1;
751 text_y = WINK_FRAME + image->height + GAP + 1;
753 if (o_text_bg == TEXT_BG_SOLID)
755 gdk_draw_rectangle(icon->mask, mask_gc, TRUE,
756 (width - (item->name_width + 2)) >> 1,
757 WINK_FRAME + image->height + GAP,
758 item->name_width + 2, font_height + 2);
760 else
762 int y = text_y + font->ascent;
764 TEXT_AT(0, 0);
766 if (o_text_bg == TEXT_BG_OUTLINE)
768 TEXT_AT(1, 0);
769 TEXT_AT(1, 1);
770 TEXT_AT(0, 1);
771 TEXT_AT(-1, 1);
772 TEXT_AT(-1, 0);
773 TEXT_AT(-1, -1);
774 TEXT_AT(0, -1);
775 TEXT_AT(1, -1);
779 gtk_widget_shape_combine_mask(icon->win, icon->mask, 0, 0);
781 *rwidth = width;
782 *rheight = height;
785 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, Icon *icon)
787 GdkFont *font = icon->widget->style->font;
788 int font_height;
789 int width, height;
790 int text_x, text_y;
791 DirItem *item = &icon->item;
792 MaskedPixmap *image = item->image;
793 int image_x;
794 GdkGC *gc = widget->style->black_gc;
795 GtkStateType state = icon->selected ? GTK_STATE_SELECTED
796 : GTK_STATE_NORMAL;
798 font_height = font->ascent + font->descent;
800 gdk_window_get_size(widget->window, &width, &height);
801 image_x = (width - image->width) >> 1;
803 /* TODO: If the shape extension is missing we might need to set
804 * the clip mask here...
806 gdk_draw_pixmap(widget->window, gc,
807 image->pixmap,
808 0, 0,
809 image_x,
810 WINK_FRAME,
811 image->width,
812 image->height);
814 if (item->flags & ITEM_FLAG_SYMLINK)
816 gdk_gc_set_clip_origin(gc, image_x, WINK_FRAME);
817 gdk_gc_set_clip_mask(gc, im_symlink->mask);
818 gdk_draw_pixmap(widget->window, gc,
819 im_symlink->pixmap,
820 0, 0, /* Source x,y */
821 image_x, WINK_FRAME, /* Dest x,y */
822 -1, -1);
823 gdk_gc_set_clip_mask(gc, NULL);
824 gdk_gc_set_clip_origin(gc, 0, 0);
826 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
828 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
829 ? im_mounted
830 : im_unmounted;
832 gdk_gc_set_clip_origin(gc, image_x, WINK_FRAME);
833 gdk_gc_set_clip_mask(gc, mp->mask);
834 gdk_draw_pixmap(widget->window, gc,
835 mp->pixmap,
836 0, 0, /* Source x,y */
837 image_x, WINK_FRAME, /* Dest x,y */
838 -1, -1);
839 gdk_gc_set_clip_mask(gc, NULL);
840 gdk_gc_set_clip_origin(gc, 0, 0);
843 text_x = (width - item->name_width) >> 1;
844 text_y = WINK_FRAME + image->height + GAP + 1;
846 if (o_text_bg != TEXT_BG_NONE)
848 gtk_paint_flat_box(widget->style, widget->window,
849 state,
850 GTK_SHADOW_NONE,
851 NULL, widget, "text",
852 text_x - 1,
853 text_y - 1,
854 item->name_width + 2,
855 font_height + 2);
858 gtk_paint_string(widget->style, widget->window,
859 state,
860 NULL, widget, "text",
861 text_x,
862 text_y + font->ascent,
863 item->leafname);
865 if (current_wink_icon == icon)
867 gdk_draw_rectangle(icon->widget->window,
868 icon->widget->style->white_gc,
869 FALSE,
870 0, 0, width - 1, height - 1);
871 gdk_draw_rectangle(icon->widget->window,
872 icon->widget->style->black_gc,
873 FALSE,
874 1, 1, width - 3, height - 3);
877 return FALSE;
880 static gboolean root_property_event(GtkWidget *widget,
881 GdkEventProperty *event,
882 gpointer data)
884 if (event->atom == win_button_proxy &&
885 event->state == GDK_PROPERTY_NEW_VALUE)
887 /* Setup forwarding on the new proxy window, if possible */
888 forward_root_clicks();
891 return FALSE;
894 static gboolean root_button_press(GtkWidget *widget,
895 GdkEventButton *event,
896 gpointer data)
898 BindAction action;
900 action = bind_lookup_bev(BIND_PINBOARD, event);
902 switch (action)
904 case ACT_CLEAR_SELECTION:
905 pinboard_clear_selection();
906 break;
907 case ACT_POPUP_MENU:
908 dnd_motion_ungrab();
909 show_pinboard_menu(event, NULL);
910 break;
911 case ACT_IGNORE:
912 break;
913 default:
914 g_warning("Unsupported action : %d\n", action);
915 break;
918 return TRUE;
921 static gboolean enter_notify(GtkWidget *widget,
922 GdkEventCrossing *event,
923 Icon *icon)
925 pinboard_icon_may_update(icon);
927 return FALSE;
930 static void perform_action(Icon *icon, GdkEventButton *event)
932 BindAction action;
934 action = bind_lookup_bev(BIND_PINBOARD_ICON, event);
936 switch (action)
938 case ACT_OPEN_ITEM:
939 dnd_motion_ungrab();
940 pinboard_wink_item(icon, TRUE);
941 run_diritem(icon->path, &icon->item, NULL, FALSE);
942 break;
943 case ACT_EDIT_ITEM:
944 dnd_motion_ungrab();
945 pinboard_wink_item(icon, TRUE);
946 run_diritem(icon->path, &icon->item, NULL, TRUE);
947 break;
948 case ACT_POPUP_MENU:
949 dnd_motion_ungrab();
950 show_pinboard_menu(event, icon);
951 break;
952 case ACT_MOVE_ICON:
953 dnd_motion_start(MOTION_REPOSITION);
954 break;
955 case ACT_PRIME_AND_SELECT:
956 if (!icon->selected)
957 pinboard_select_only(icon);
958 dnd_motion_start(MOTION_READY_FOR_DND);
959 break;
960 case ACT_PRIME_AND_TOGGLE:
961 pinboard_set_selected(icon, !icon->selected);
962 dnd_motion_start(MOTION_READY_FOR_DND);
963 break;
964 case ACT_PRIME_FOR_DND:
965 pinboard_wink_item(icon, TRUE);
966 dnd_motion_start(MOTION_READY_FOR_DND);
967 break;
968 case ACT_TOGGLE_SELECTED:
969 pinboard_set_selected(icon, !icon->selected);
970 break;
971 case ACT_SELECT_EXCL:
972 pinboard_select_only(icon);
973 break;
974 case ACT_IGNORE:
975 break;
976 default:
977 g_warning("Unsupported action : %d\n", action);
978 break;
982 static gboolean button_release_event(GtkWidget *widget,
983 GdkEventButton *event,
984 Icon *icon)
986 if (pinboard_modified)
987 pinboard_save();
989 if (dnd_motion_release(event))
990 return TRUE;
992 perform_action(icon, event);
994 return TRUE;
997 static gboolean button_press_event(GtkWidget *widget,
998 GdkEventButton *event,
999 Icon *icon)
1001 if (dnd_motion_press(widget, event))
1002 perform_action(icon, event);
1004 return TRUE;
1007 /* Return a text/uri-list of all the icons in the list */
1008 static guchar *create_uri_list(GList *list)
1010 GString *tmp;
1011 guchar *retval;
1012 guchar *leader;
1014 tmp = g_string_new(NULL);
1015 leader = g_strdup_printf("file://%s", our_host_name());
1017 for (; list; list = list->next)
1019 Icon *icon = (Icon *) list->data;
1021 g_string_append(tmp, leader);
1022 g_string_append(tmp, icon->path);
1023 g_string_append(tmp, "\r\n");
1026 g_free(leader);
1027 retval = tmp->str;
1028 g_string_free(tmp, FALSE);
1030 return retval;
1033 static void start_drag(Icon *icon, GdkEventMotion *event)
1035 GtkWidget *widget = icon->widget;
1036 GList *selected;
1038 if (!icon->selected)
1040 tmp_icon_selected = TRUE;
1041 pinboard_select_only(icon);
1044 selected = pinboard_get_selected();
1045 g_return_if_fail(selected != NULL);
1047 pinboard_drag_in_progress = TRUE;
1049 if (selected->next == NULL)
1050 drag_one_item(widget, event, icon->path, &icon->item);
1051 else
1053 guchar *uri_list;
1055 uri_list = create_uri_list(selected);
1056 drag_selection(widget, event, uri_list);
1057 g_free(uri_list);
1060 g_list_free(selected);
1063 /* An icon is being dragged around... */
1064 static gint icon_motion_notify(GtkWidget *widget,
1065 GdkEventMotion *event,
1066 Icon *icon)
1068 int x, y;
1069 int width, height;
1071 x = event->x_root;
1072 y = event->y_root;
1074 if (motion_state == MOTION_READY_FOR_DND)
1076 if (dnd_motion_moved(event))
1077 start_drag(icon, event);
1078 return TRUE;
1080 else if (motion_state != MOTION_REPOSITION)
1081 return FALSE;
1083 snap_to_grid(&x, &y);
1084 if (icon->x == x && icon->y == y)
1085 return TRUE;
1087 icon->x = x;
1088 icon->y = y;
1089 gdk_window_get_size(icon->win->window, &width, &height);
1090 offset_from_centre(icon, width, height, &x, &y);
1092 gdk_window_move(icon->win->window, x, y);
1094 /* Store the fixed position for the center of the icon */
1095 offset_to_centre(icon, width, height, &x, &y);
1096 icon->x = x;
1097 icon->y = y;
1099 pinboard_modified = TRUE;
1101 return TRUE;
1104 /* Called for each line in the pinboard file while loading a new board */
1105 static char *pin_from_file(guchar *line)
1107 guchar *leaf = NULL;
1108 int x, y, n;
1110 if (*line == '<')
1112 guchar *end;
1114 end = strchr(line + 1, '>');
1115 if (!end)
1116 return _("Missing '>' in icon label");
1118 leaf = g_strndup(line + 1, end - line - 1);
1120 line = end + 1;
1122 while (isspace(*line))
1123 line++;
1124 if (*line != ',')
1125 return _("Missing ',' after icon label");
1126 line++;
1129 if (sscanf(line, " %d , %d , %n", &x, &y, &n) < 2)
1130 return NULL; /* Ignore format errors */
1132 pinboard_pin(line + n, leaf, x, y);
1134 g_free(leaf);
1136 return NULL;
1139 /* Make sure that clicks and drops on the root window come to us...
1140 * False if an error occurred (ie, someone else is using it).
1142 static gboolean add_root_handlers(void)
1144 GdkWindow *root;
1146 if (!proxy_invisible)
1148 GtkTargetEntry target_table[] =
1150 {"text/uri-list", 0, TARGET_URI_LIST},
1151 {"STRING", 0, TARGET_STRING},
1154 win_button_proxy = gdk_atom_intern("_WIN_DESKTOP_BUTTON_PROXY",
1155 FALSE);
1156 proxy_invisible = gtk_invisible_new();
1157 gtk_widget_show(proxy_invisible);
1159 gdk_window_add_filter(proxy_invisible->window,
1160 proxy_filter, NULL);
1163 gtk_signal_connect(GTK_OBJECT(proxy_invisible),
1164 "property_notify_event",
1165 GTK_SIGNAL_FUNC(root_property_event), NULL);
1166 gtk_signal_connect(GTK_OBJECT(proxy_invisible),
1167 "button_press_event",
1168 GTK_SIGNAL_FUNC(root_button_press), NULL);
1170 /* Drag and drop handlers */
1171 drag_set_pinboard_dest(proxy_invisible);
1172 gtk_signal_connect(GTK_OBJECT(proxy_invisible), "drag_motion",
1173 GTK_SIGNAL_FUNC(bg_drag_motion),
1174 NULL);
1176 /* The proxy window is also used to hold the selection... */
1177 gtk_signal_connect(GTK_OBJECT(proxy_invisible),
1178 "selection_clear_event",
1179 GTK_SIGNAL_FUNC(lose_selection),
1180 NULL);
1182 gtk_signal_connect(GTK_OBJECT(proxy_invisible),
1183 "selection_get",
1184 GTK_SIGNAL_FUNC(selection_get), NULL);
1186 gtk_selection_add_targets(proxy_invisible,
1187 GDK_SELECTION_PRIMARY,
1188 target_table,
1189 sizeof(target_table) / sizeof(*target_table));
1192 root = gdk_window_lookup(GDK_ROOT_WINDOW());
1193 if (!root)
1194 root = gdk_window_foreign_new(GDK_ROOT_WINDOW());
1196 if (!setup_xdnd_proxy(GDK_ROOT_WINDOW(), proxy_invisible->window))
1197 return FALSE;
1199 /* Forward events from the root window to our proxy window */
1200 gdk_window_add_filter(GDK_ROOT_PARENT(), proxy_filter, NULL);
1201 gdk_window_set_user_data(GDK_ROOT_PARENT(), proxy_invisible);
1202 gdk_window_set_events(GDK_ROOT_PARENT(),
1203 gdk_window_get_events(GDK_ROOT_PARENT()) |
1204 GDK_PROPERTY_CHANGE_MASK);
1206 forward_root_clicks();
1208 return TRUE;
1211 /* See if the window manager is offering to forward root window clicks.
1212 * If so, grab them. Otherwise, do nothing.
1213 * Call this whenever the _WIN_DESKTOP_BUTTON_PROXY property changes.
1215 static void forward_root_clicks(void)
1217 click_proxy_gdk_window = find_click_proxy_window();
1218 if (!click_proxy_gdk_window)
1219 return;
1221 /* Events on the wm's proxy are dealt with by our proxy widget */
1222 gdk_window_set_user_data(click_proxy_gdk_window, proxy_invisible);
1223 gdk_window_add_filter(click_proxy_gdk_window, proxy_filter, NULL);
1225 /* The proxy window for clicks sends us button press events with
1226 * SubstructureNotifyMask. We need StructureNotifyMask to receive
1227 * DestroyNotify events, too.
1229 XSelectInput(GDK_DISPLAY(),
1230 GDK_WINDOW_XWINDOW(click_proxy_gdk_window),
1231 SubstructureNotifyMask | StructureNotifyMask);
1234 /* Write the current state of the pinboard to the current pinboard file */
1235 static void pinboard_save(void)
1237 guchar *save = NULL;
1238 GString *tmp;
1239 FILE *file = NULL;
1240 GList *next;
1241 guchar *save_new = NULL;
1243 g_return_if_fail(current_pinboard != NULL);
1245 pinboard_modified = FALSE;
1247 if (strchr(current_pinboard->name, '/'))
1248 save = g_strdup(current_pinboard->name);
1249 else
1251 guchar *leaf;
1253 leaf = g_strconcat("pb_", current_pinboard->name, NULL);
1254 save = choices_find_path_save(leaf, "ROX-Filer", TRUE);
1255 g_free(leaf);
1258 if (!save)
1259 return;
1261 save_new = g_strconcat(save, ".new", NULL);
1262 file = fopen(save_new, "wb");
1263 if (!file)
1264 goto err;
1266 tmp = g_string_new(NULL);
1267 for (next = current_pinboard->icons; next; next = next->next)
1269 Icon *icon = (Icon *) next->data;
1271 g_string_sprintf(tmp, "<%s>, %d, %d, %s\n",
1272 icon->item.leafname, icon->x, icon->y, icon->src_path);
1273 if (fwrite(tmp->str, 1, tmp->len, file) < tmp->len)
1274 goto err;
1277 g_string_free(tmp, TRUE);
1279 if (fclose(file))
1281 file = NULL;
1282 goto err;
1285 file = NULL;
1287 if (rename(save_new, save))
1288 goto err;
1290 goto out;
1291 err:
1292 delayed_error(_("Error saving pinboard"), g_strerror(errno));
1293 out:
1294 if (file)
1295 fclose(file);
1296 g_free(save_new);
1297 g_free(save);
1301 * Filter that translates proxied events from virtual root windows into normal
1302 * Gdk events for the proxy_invisible widget. Stolen from gmc.
1304 * Also gets events from the root window.
1306 static GdkFilterReturn proxy_filter(GdkXEvent *xevent,
1307 GdkEvent *event,
1308 gpointer data)
1310 XEvent *xev;
1311 GdkWindow *proxy = proxy_invisible->window;
1313 xev = xevent;
1315 switch (xev->type) {
1316 case ButtonPress:
1317 case ButtonRelease:
1318 /* Translate button events into events that come from
1319 * the proxy window, so that we can catch them as a
1320 * signal from the invisible widget.
1322 if (xev->type == ButtonPress)
1323 event->button.type = GDK_BUTTON_PRESS;
1324 else
1325 event->button.type = GDK_BUTTON_RELEASE;
1327 gdk_window_ref(proxy);
1329 event->button.window = proxy;
1330 event->button.send_event = xev->xbutton.send_event;
1331 event->button.time = xev->xbutton.time;
1332 event->button.x_root = xev->xbutton.x_root;
1333 event->button.y_root = xev->xbutton.y_root;
1334 event->button.x = xev->xbutton.x;
1335 event->button.y = xev->xbutton.y;
1336 event->button.state = xev->xbutton.state;
1337 event->button.button = xev->xbutton.button;
1339 return GDK_FILTER_TRANSLATE;
1341 case DestroyNotify:
1342 /* XXX: I have no idea why this helps, but it does! */
1343 /* The proxy window was destroyed (i.e. the window
1344 * manager died), so we have to cope with it
1346 if (((GdkEventAny *) event)->window == proxy)
1347 gdk_window_destroy_notify(proxy);
1349 return GDK_FILTER_REMOVE;
1351 default:
1352 break;
1355 return GDK_FILTER_CONTINUE;
1358 /* Does not save the new state */
1359 static void icon_destroyed(GtkWidget *widget, Icon *icon)
1361 g_return_if_fail(icon != NULL);
1363 icon_unhash_path(icon);
1365 if (icon->selected)
1366 change_number_selected(-1);
1368 if (current_wink_icon == icon)
1369 current_wink_icon = NULL;
1371 gdk_pixmap_unref(icon->mask);
1372 dir_item_clear(&icon->item);
1373 g_free(icon->src_path);
1374 g_free(icon->path);
1375 g_free(icon);
1377 if (current_pinboard)
1378 current_pinboard->icons =
1379 g_list_remove(current_pinboard->icons, icon);
1382 static void snap_to_grid(int *x, int *y)
1384 *x = ((*x + o_grid_step / 2) / o_grid_step) * o_grid_step;
1385 *y = ((*y + o_grid_step / 2) / o_grid_step) * o_grid_step;
1388 /* Convert (x,y) from a centre point to a window position */
1389 static void offset_from_centre(Icon *icon,
1390 int width, int height,
1391 int *x, int *y)
1393 *x -= width >> 1;
1394 *y -= height - (icon->widget->style->font->descent >> 1);
1395 *x = CLAMP(*x, 0, screen_width - (o_clamp_icons ? width : 0));
1396 *y = CLAMP(*y, 0, screen_height - (o_clamp_icons ? height : 0));
1399 /* Convert (x,y) from a window position to a centre point */
1400 static void offset_to_centre(Icon *icon,
1401 int width, int height,
1402 int *x, int *y)
1404 *x += width >> 1;
1405 *y += height - (icon->widget->style->font->descent >> 1);
1408 /* Same as drag_set_dest(), but for pinboard icons */
1409 static void drag_set_pinicon_dest(Icon *icon)
1411 GtkObject *obj = GTK_OBJECT(icon->widget);
1413 make_drop_target(icon->widget, 0);
1415 gtk_signal_connect(obj, "drag_motion",
1416 GTK_SIGNAL_FUNC(drag_motion), icon);
1417 gtk_signal_connect(obj, "drag_leave",
1418 GTK_SIGNAL_FUNC(drag_leave), icon);
1419 gtk_signal_connect(obj, "drag_end",
1420 GTK_SIGNAL_FUNC(drag_end), icon);
1423 /* Called during the drag when the mouse is in a widget registered
1424 * as a drop target. Returns TRUE if we can accept the drop.
1426 static gboolean drag_motion(GtkWidget *widget,
1427 GdkDragContext *context,
1428 gint x,
1429 gint y,
1430 guint time,
1431 Icon *icon)
1433 GdkDragAction action = context->suggested_action;
1434 char *type = NULL;
1435 DirItem *item = &icon->item;
1437 if (gtk_drag_get_source_widget(context) == widget)
1438 goto out; /* Can't drag something to itself! */
1440 if (icon->selected)
1441 goto out; /* Can't drag a selection to itself */
1443 type = dnd_motion_item(context, &item);
1445 if (!item)
1446 type = NULL;
1447 out:
1448 /* We actually must pretend to accept the drop, even if the
1449 * directory isn't writeable, so that the spring-opening
1450 * thing works.
1453 /* Don't allow drops to non-writeable directories */
1454 if (option_get_int("dnd_spring_open") == FALSE &&
1455 type == drop_dest_dir &&
1456 access(icon->path, W_OK) != 0)
1458 type = NULL;
1461 g_dataset_set_data(context, "drop_dest_type", type);
1462 if (type)
1464 gdk_drag_status(context, action, time);
1465 g_dataset_set_data_full(context, "drop_dest_path",
1466 g_strdup(icon->path), g_free);
1467 if (type == drop_dest_dir)
1468 dnd_spring_load(context);
1470 pinboard_wink_item(icon, FALSE);
1473 return type != NULL;
1476 static void drag_leave(GtkWidget *widget,
1477 GdkDragContext *context,
1478 guint32 time,
1479 Icon *icon)
1481 pinboard_wink_item(NULL, FALSE);
1482 dnd_spring_abort();
1485 /* When changing the 'selected' attribute of an icon, call this
1486 * to update the global counter and claim or release the primary
1487 * selection as needed.
1489 static void change_number_selected(int delta)
1491 guint32 time;
1493 g_return_if_fail(delta != 0);
1494 g_return_if_fail(number_selected + delta >= 0);
1496 if (number_selected == 0)
1498 time = gdk_event_get_time(gtk_get_current_event());
1500 gtk_selection_owner_set(proxy_invisible,
1501 GDK_SELECTION_PRIMARY,
1502 time);
1505 number_selected += delta;
1507 if (number_selected == 0)
1509 time = gdk_event_get_time(gtk_get_current_event());
1511 gtk_selection_owner_set(NULL,
1512 GDK_SELECTION_PRIMARY,
1513 time);
1517 /* Called when another application wants the contents of our selection */
1518 static void selection_get(GtkWidget *widget,
1519 GtkSelectionData *selection_data,
1520 guint info,
1521 guint time,
1522 gpointer data)
1524 GString *str;
1525 GList *next;
1526 guchar *leader = NULL;
1528 str = g_string_new(NULL);
1530 if (info == TARGET_URI_LIST)
1531 leader = g_strdup_printf("file://%s", our_host_name());
1533 for (next = current_pinboard->icons; next; next = next->next)
1535 Icon *icon = (Icon *) next->data;
1537 if (!icon->selected)
1538 continue;
1540 if (leader)
1541 g_string_append(str, leader);
1542 g_string_append(str, icon->path);
1543 g_string_append_c(str, ' ');
1546 g_free(leader);
1548 gtk_selection_data_set(selection_data,
1549 gdk_atom_intern("STRING", FALSE),
1551 str->str,
1552 str->len ? str->len - 1 : 0);
1554 g_string_free(str, TRUE);
1557 /* Called when another application takes the selection away from us */
1558 static gint lose_selection(GtkWidget *widget, GdkEventSelection *event)
1560 /* 'lock' number_selected so that we don't send any events */
1561 number_selected++;
1562 pinboard_clear_selection();
1563 number_selected--;
1565 return TRUE;
1568 static gboolean bg_drag_motion(GtkWidget *widget,
1569 GdkDragContext *context,
1570 gint x,
1571 gint y,
1572 guint time,
1573 gpointer data)
1575 /* Dragging from the pinboard to the pinboard is not allowed */
1576 if (pinboard_drag_in_progress)
1577 return FALSE;
1579 gdk_drag_status(context, context->suggested_action, time);
1580 return TRUE;
1583 static void drag_end(GtkWidget *widget,
1584 GdkDragContext *context,
1585 Icon *icon)
1587 pinboard_drag_in_progress = FALSE;
1588 if (tmp_icon_selected)
1590 pinboard_clear_selection();
1591 tmp_icon_selected = FALSE;
1595 /* Something which affects all the icons has changed - reshape
1596 * and redraw all of them.
1598 static void reshape_all(void)
1600 GList *next;
1602 for (next = current_pinboard->icons; next; next = next->next)
1604 Icon *icon = (Icon *) next->data;
1605 reshape_icon(icon);
1609 /* Display the pinboard menu. Set icon to NULL if no particular icon
1610 * was clicked.
1612 static void show_pinboard_menu(GdkEventButton *event, Icon *icon)
1614 int pos[2];
1615 GList *icons;
1616 AppMenus *menus = NULL;
1618 if (icon)
1620 if (icon->selected)
1621 tmp_icon_selected = FALSE;
1622 else
1624 pinboard_select_only(icon);
1625 tmp_icon_selected = TRUE;
1627 /* Check for icon-specific menu */
1628 if (icon->path)
1629 menus = appmenu_query(icon->path, &icon->item);
1632 /* Remove the previous appmenu used on this menu */
1633 appmenu_remove(pinboard_menu);
1635 icons = pinboard_get_selected();
1637 pos[0] = event->x_root;
1638 pos[1] = event->y_root;
1640 if (icons)
1642 menu_set_items_shaded(pinboard_menu,
1643 icons->next ? TRUE : FALSE, 4, 3);
1645 menu_set_items_shaded(pinboard_menu, FALSE, 7, 1);
1647 else
1648 menu_set_items_shaded(pinboard_menu, TRUE, 4, 4);
1650 /* Add the AppMenu items if necessary */
1651 if (menus)
1652 appmenu_add(menus, pinboard_menu);
1654 gtk_menu_popup(GTK_MENU(pinboard_menu), NULL, NULL, position_menu,
1655 (gpointer) pos, event->button, event->time);
1658 static void pin_help(gpointer data, guint action, GtkWidget *widget)
1660 Icon *icon;
1662 icon = pinboard_selected_icon();
1664 if (icon)
1665 show_item_help(icon->path, &icon->item);
1666 else
1667 delayed_error(PROJECT,
1668 _("You must first select a single pinned icon to get "
1669 "help on."));
1672 static void pin_remove(gpointer data, guint action, GtkWidget *widget)
1674 pinboard_unpin_selection();
1677 static void menu_closed(GtkWidget *widget)
1679 if (tmp_icon_selected)
1681 pinboard_clear_selection();
1682 tmp_icon_selected = FALSE;
1686 /* Show where this item is stored */
1687 static void show_location(gpointer data, guint action, GtkWidget *widget)
1689 Icon *icon;
1691 icon = pinboard_selected_icon();
1693 if (icon)
1694 open_to_show(icon->path);
1695 else
1697 delayed_error(PROJECT,
1698 _("Select a single item, then use this to find out "
1699 "where it is in the filesystem."));
1703 static void rename_cb(Icon *icon)
1705 reshape_icon(icon);
1707 pinboard_save();
1710 static void edit_icon(gpointer data, guint action, GtkWidget *widget)
1712 Icon *icon;
1714 icon = pinboard_selected_icon();
1716 if (icon)
1717 show_rename_box(icon->widget, icon, rename_cb);
1718 else
1720 delayed_error(PROJECT,
1721 _("First, select a single item to edit"));
1722 return;