r1432: Removed all traces of Pixmaps from MaskedPixmap structure (!).
[rox-filer.git] / ROX-Filer / src / pinboard.c
blob9ddd62a43610d31a076fe30dfd4e801b21655a59
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 <gtk/gtkinvisible.h>
33 #include <stdlib.h>
34 #include <libxml/parser.h>
36 #include "global.h"
38 #include "pinboard.h"
39 #include "main.h"
40 #include "dnd.h"
41 #include "pixmaps.h"
42 #include "type.h"
43 #include "choices.h"
44 #include "support.h"
45 #include "gui_support.h"
46 #include "options.h"
47 #include "diritem.h"
48 #include "bind.h"
49 #include "icon.h"
50 #include "run.h"
51 #include "appinfo.h"
52 #include "menu.h"
54 static gboolean tmp_icon_selected = FALSE; /* When dragging */
56 struct _Pinboard {
57 guchar *name; /* Leaf name */
58 GList *icons;
59 GtkStyle *style;
62 #define IS_PIN_ICON(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), pin_icon_get_type())
64 typedef struct _PinIconClass PinIconClass;
65 typedef struct _PinIcon PinIcon;
67 struct _PinIconClass {
68 IconClass parent;
71 struct _PinIcon {
72 Icon icon;
74 int x, y, width, height;
75 int name_width;
76 PangoLayout *layout; /* The label */
77 GtkWidget *win;
78 GtkWidget *widget; /* The drawing area */
79 GdkBitmap *mask;
82 /* The number of pixels between the bottom of the image and the top
83 * of the text.
85 #define GAP 4
87 /* The size of the border around the icon which is used when winking */
88 #define WINK_FRAME 2
90 /* Grid sizes */
91 #define GRID_STEP_FINE 2
92 #define GRID_STEP_MED 16
93 #define GRID_STEP_COARSE 32
95 static PinIcon *current_wink_icon = NULL;
96 static gint wink_timeout;
98 /* Used for the text colours (only) in the icons */
99 static GdkColor text_fg_col, text_bg_col;
101 /* Style that all the icons should use. NULL => regenerate from text_fg/bg */
102 static GtkStyle *pinicon_style = NULL;
104 Pinboard *current_pinboard = NULL;
105 static gint loading_pinboard = 0; /* Non-zero => loading */
107 static GdkColor mask_solid = {1, 1, 1, 1};
108 static GdkColor mask_transp = {0, 0, 0, 0};
109 static GdkGC *mask_gc = NULL;
111 /* Proxy window for DnD and clicks on the desktop */
112 static GtkWidget *proxy_invisible;
114 /* The window (owned by the wm) which root clicks are forwarded to.
115 * NULL if wm does not support forwarding clicks.
117 static GdkWindow *click_proxy_gdk_window = NULL;
118 static GdkAtom win_button_proxy; /* _WIN_DESKTOP_BUTTON_PROXY */
120 /* The Icon that was used to start the current drag, if any */
121 Icon *pinboard_drag_in_progress = NULL;
123 /* Used when dragging icons around... */
124 static gboolean pinboard_modified = FALSE;
126 typedef enum {
127 TEXT_BG_NONE = 0,
128 TEXT_BG_OUTLINE = 1,
129 TEXT_BG_SOLID = 2,
130 } TextBgType;
132 static Option o_pinboard_clamp_icons, o_pinboard_grid_step;
133 static Option o_pinboard_fg_colour, o_pinboard_bg_colour;
135 /* Static prototypes */
136 static GType pin_icon_get_type(void);
137 static void set_size_and_shape(PinIcon *pi, int *rwidth, int *rheight);
138 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi);
139 static void mask_wink_border(PinIcon *pi, GdkColor *alpha);
140 static gint end_wink(gpointer data);
141 static gboolean button_release_event(GtkWidget *widget,
142 GdkEventButton *event,
143 PinIcon *pi);
144 static gboolean root_property_event(GtkWidget *widget,
145 GdkEventProperty *event,
146 gpointer data);
147 static gboolean root_button_press(GtkWidget *widget,
148 GdkEventButton *event,
149 gpointer data);
150 static gboolean enter_notify(GtkWidget *widget,
151 GdkEventCrossing *event,
152 PinIcon *pi);
153 static gboolean button_press_event(GtkWidget *widget,
154 GdkEventButton *event,
155 PinIcon *pi);
156 static gint icon_motion_notify(GtkWidget *widget,
157 GdkEventMotion *event,
158 PinIcon *pi);
159 static const char *pin_from_file(gchar *line);
160 static gboolean add_root_handlers(void);
161 static GdkFilterReturn proxy_filter(GdkXEvent *xevent,
162 GdkEvent *event,
163 gpointer data);
164 static void snap_to_grid(int *x, int *y);
165 static void offset_from_centre(PinIcon *pi,
166 int width, int height,
167 int *x, int *y);
168 static void offset_to_centre(PinIcon *pi,
169 int width, int height,
170 int *x, int *y);
171 static gboolean drag_motion(GtkWidget *widget,
172 GdkDragContext *context,
173 gint x,
174 gint y,
175 guint time,
176 PinIcon *pi);
177 static void drag_set_pinicon_dest(PinIcon *pi);
178 static void drag_leave(GtkWidget *widget,
179 GdkDragContext *context,
180 guint32 time,
181 PinIcon *pi);
182 static void forward_root_clicks(void);
183 static gboolean bg_drag_motion(GtkWidget *widget,
184 GdkDragContext *context,
185 gint x,
186 gint y,
187 guint time,
188 gpointer data);
189 static gboolean bg_drag_leave(GtkWidget *widget,
190 GdkDragContext *context,
191 guint32 time,
192 gpointer data);
193 static void bg_expose(GdkRectangle *area);
194 static void drag_end(GtkWidget *widget,
195 GdkDragContext *context,
196 PinIcon *pi);
197 static void reshape_all(void);
198 static void pinboard_check_options(void);
199 static void pinboard_load_from_xml(xmlDocPtr doc);
200 static void pinboard_clear(void);
201 static void pinboard_save(void);
202 static PinIcon *pin_icon_new(const char *pathname, const char *name);
203 static void pin_icon_destroyed(PinIcon *pi);
204 static void pin_icon_set_tip(PinIcon *pi);
205 static void pinboard_show_menu(GdkEventButton *event, PinIcon *pi);
206 static void merge_alpha(GdkPixbuf *src, GdkBitmap *mask, int x, int y);
208 /****************************************************************
209 * EXTERNAL INTERFACE *
210 ****************************************************************/
212 void pinboard_init(void)
214 option_add_string(&o_pinboard_fg_colour, "pinboard_fg_colour", "#000");
215 option_add_string(&o_pinboard_bg_colour, "pinboard_bg_colour", "#ddd");
217 option_add_int(&o_pinboard_clamp_icons, "pinboard_clamp_icons", 1);
218 option_add_int(&o_pinboard_grid_step, "pinboard_grid_step",
219 GRID_STEP_COARSE);
220 option_add_notify(pinboard_check_options);
222 gdk_color_parse(o_pinboard_fg_colour.value, &text_fg_col);
223 gdk_color_parse(o_pinboard_bg_colour.value, &text_bg_col);
226 /* Load 'pb_<pinboard>' config file from Choices (if it exists)
227 * and make it the current pinboard.
228 * Any existing pinned items are removed. You must call this
229 * at least once before using the pinboard. NULL disables the
230 * pinboard.
232 void pinboard_activate(const gchar *name)
234 Pinboard *old_board = current_pinboard;
235 guchar *path, *slash;
237 /* Treat an empty name the same as NULL */
238 if (name && !*name)
239 name = NULL;
241 if (old_board)
242 pinboard_clear();
244 if (!name)
246 if (number_of_windows < 1 && gtk_main_level() > 0)
247 gtk_main_quit();
248 return;
251 if (!add_root_handlers())
253 delayed_error(_("Another application is already "
254 "managing the pinboard!"));
255 return;
258 number_of_windows++;
260 slash = strchr(name, '/');
261 if (slash)
263 if (access(name, F_OK))
264 path = NULL; /* File does not (yet) exist */
265 else
266 path = g_strdup(name);
268 else
270 guchar *leaf;
272 leaf = g_strconcat("pb_", name, NULL);
273 path = choices_find_path_load(leaf, PROJECT);
274 g_free(leaf);
277 current_pinboard = g_new(Pinboard, 1);
278 current_pinboard->name = g_strdup(name);
279 current_pinboard->icons = NULL;
281 loading_pinboard++;
282 if (path)
284 xmlDocPtr doc;
285 doc = xmlParseFile(path);
286 if (doc)
288 pinboard_load_from_xml(doc);
289 xmlFreeDoc(doc);
291 else
293 parse_file(path, pin_from_file);
294 info_message(_("Your old pinboard file has been "
295 "converted to the new XML format."));
296 pinboard_save();
298 g_free(path);
300 else
301 pinboard_pin(home_dir, "Home",
302 4 + ICON_WIDTH / 2,
303 4 + ICON_HEIGHT / 2);
304 loading_pinboard--;
307 const char *pinboard_get_name(void)
309 g_return_val_if_fail(current_pinboard != NULL, NULL);
311 return current_pinboard->name;
314 /* Add a new icon to the background.
315 * 'path' should be an absolute pathname.
316 * 'x' and 'y' are the coordinates of the point in the middle of the text
317 * if 'corner' is FALSE, and as the top-left corner of where the icon
318 * image should be if it is TRUE.
319 * 'name' is the name to use. If NULL then the leafname of path is used.
321 * name and path are in UTF-8 for Gtk+-2.0 only.
323 void pinboard_pin(const gchar *path, const gchar *name, int x, int y)
325 PinIcon *pi;
326 Icon *icon;
327 int width, height;
329 g_return_if_fail(path != NULL);
330 g_return_if_fail(current_pinboard != NULL);
332 pi = pin_icon_new(path, name);
333 icon = (Icon *) pi;
334 pi->x = x;
335 pi->y = y;
337 /* Window takes the initial ref of Icon */
338 pi->win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
339 gtk_window_set_wmclass(GTK_WINDOW(pi->win), "ROX-Pinboard", PROJECT);
341 pi->widget = gtk_drawing_area_new();
342 gtk_widget_set_name(pi->widget, "pinboard-icon");
344 pi->layout = gtk_widget_create_pango_layout(pi->widget, NULL);
345 pango_layout_set_width(pi->layout, 140 * PANGO_SCALE);
347 gtk_container_add(GTK_CONTAINER(pi->win), pi->widget);
348 drag_set_pinicon_dest(pi);
349 g_signal_connect(pi->widget, "drag_data_get",
350 G_CALLBACK(drag_data_get), NULL);
352 gtk_widget_realize(pi->win);
353 gtk_widget_realize(pi->widget);
355 set_size_and_shape(pi, &width, &height);
356 snap_to_grid(&x, &y);
357 offset_from_centre(pi, width, height, &x, &y);
358 gtk_window_move(GTK_WINDOW(pi->win), x, y);
359 /* Set the correct position in the icon */
360 offset_to_centre(pi, width, height, &x, &y);
361 pi->x = x;
362 pi->y = y;
364 make_panel_window(pi->win);
366 /* TODO: Use gdk function when it supports this type */
368 GdkAtom desktop_type;
370 desktop_type = gdk_atom_intern("_NET_WM_WINDOW_TYPE_DESKTOP",
371 FALSE);
372 gdk_property_change(pi->win->window,
373 gdk_atom_intern("_NET_WM_WINDOW_TYPE", FALSE),
374 gdk_atom_intern("ATOM", FALSE), 32,
375 GDK_PROP_MODE_REPLACE, (guchar *) &desktop_type, 1);
378 gtk_widget_add_events(pi->widget,
379 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
380 GDK_BUTTON1_MOTION_MASK | GDK_ENTER_NOTIFY_MASK |
381 GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK);
382 g_signal_connect(pi->widget, "enter-notify-event",
383 G_CALLBACK(enter_notify), pi);
384 g_signal_connect(pi->widget, "button-press-event",
385 G_CALLBACK(button_press_event), pi);
386 g_signal_connect(pi->widget, "button-release-event",
387 G_CALLBACK(button_release_event), pi);
388 g_signal_connect(pi->widget, "motion-notify-event",
389 G_CALLBACK(icon_motion_notify), pi);
390 g_signal_connect(pi->widget, "expose-event",
391 G_CALLBACK(draw_icon), pi);
392 g_signal_connect_swapped(pi->win, "destroy",
393 G_CALLBACK(pin_icon_destroyed), pi);
395 current_pinboard->icons = g_list_prepend(current_pinboard->icons,
396 pi);
397 pin_icon_set_tip(pi);
398 gtk_widget_show_all(pi->win);
399 gdk_window_lower(pi->win->window);
401 if (!loading_pinboard)
402 pinboard_save();
405 /* Remove an icon from the pinboard */
406 /* XXX: use destroy */
407 void pinboard_unpin(PinIcon *pi)
409 g_return_if_fail(pi != NULL);
411 gtk_widget_destroy(pi->win);
412 pinboard_save();
415 /* Put a border around the icon, briefly.
416 * If icon is NULL then cancel any existing wink.
417 * The icon will automatically unhighlight unless timeout is FALSE,
418 * in which case you must call this function again (with NULL or another
419 * icon) to remove the highlight.
421 static void pinboard_wink_item(PinIcon *pi, gboolean timeout)
423 if (current_wink_icon == pi)
424 return;
426 if (current_wink_icon)
428 mask_wink_border(current_wink_icon, &mask_transp);
429 if (wink_timeout != -1)
430 gtk_timeout_remove(wink_timeout);
433 current_wink_icon = pi;
435 if (current_wink_icon)
437 mask_wink_border(current_wink_icon, &mask_solid);
438 if (timeout)
439 wink_timeout = gtk_timeout_add(300, end_wink, NULL);
440 else
441 wink_timeout = -1;
445 /* Icon's size, shape or appearance has changed - update the display */
446 void pinboard_reshape_icon(Icon *icon)
448 PinIcon *pi = (PinIcon *) icon;
449 int x = pi->x, y = pi->y;
450 int width, height;
452 set_size_and_shape(pi, &width, &height);
453 gdk_window_resize(pi->win->window, width, height);
454 offset_from_centre(pi, width, height, &x, &y);
455 gtk_window_move(GTK_WINDOW(pi->win), x, y);
456 gtk_widget_queue_draw(pi->win);
460 /****************************************************************
461 * INTERNAL FUNCTIONS *
462 ****************************************************************/
464 static void pinboard_check_options(void)
466 GdkColor n_fg, n_bg;
468 gdk_color_parse(o_pinboard_fg_colour.value, &n_fg);
469 gdk_color_parse(o_pinboard_bg_colour.value, &n_bg);
471 if (gdk_color_equal(&n_fg, &text_fg_col) == 0 ||
472 gdk_color_equal(&n_bg, &text_bg_col) == 0)
474 memcpy(&text_fg_col, &n_fg, sizeof(GdkColor));
475 memcpy(&text_bg_col, &n_bg, sizeof(GdkColor));
477 if (pinicon_style)
479 g_object_unref(G_OBJECT(pinicon_style));
480 pinicon_style = NULL;
483 if (current_pinboard)
484 reshape_all();
488 static gint end_wink(gpointer data)
490 pinboard_wink_item(NULL, FALSE);
491 return FALSE;
494 /* Make the wink border solid or transparent */
495 static void mask_wink_border(PinIcon *pi, GdkColor *alpha)
497 if (!current_pinboard)
498 return;
500 gdk_gc_set_foreground(mask_gc, alpha);
501 gdk_draw_rectangle(pi->mask, mask_gc, FALSE,
502 0, 0, pi->width - 1, pi->height - 1);
503 gdk_draw_rectangle(pi->mask, mask_gc, FALSE,
504 1, 1, pi->width - 3, pi->height - 3);
506 gtk_widget_shape_combine_mask(pi->win, pi->mask, 0, 0);
508 gtk_widget_queue_draw(pi->widget);
509 gdk_window_process_updates(pi->widget->window, TRUE);
512 /* Updates the name_width and layout fields, and resizes and masks the window.
513 * Also sets the style to pinicon_style, generating it if needed.
514 * Returns the new width and height.
516 static void set_size_and_shape(PinIcon *pi, int *rwidth, int *rheight)
518 int width, height;
519 int font_height;
520 Icon *icon = (Icon *) pi;
521 MaskedPixmap *image = icon->item->image;
522 int iwidth = image->width;
523 int iheight = image->height;
524 DirItem *item = icon->item;
525 int text_x, text_y;
527 if (!pinicon_style)
529 pinicon_style = gtk_style_copy(pi->widget->style);
530 memcpy(&pinicon_style->fg[GTK_STATE_NORMAL],
531 &text_fg_col, sizeof(GdkColor));
532 memcpy(&pinicon_style->bg[GTK_STATE_NORMAL],
533 &text_bg_col, sizeof(GdkColor));
535 gtk_widget_set_style(pi->widget, pinicon_style);
538 PangoRectangle logical;
539 pango_layout_set_text(pi->layout, icon->item->leafname, -1);
540 pango_layout_get_pixel_extents(pi->layout, NULL, &logical);
542 pi->name_width = logical.width - logical.x;
543 font_height = logical.height - logical.y;
546 width = MAX(iwidth, pi->name_width + 2) + 2 * WINK_FRAME;
547 height = iheight + GAP + (font_height + 2) + 2 * WINK_FRAME;
548 gtk_widget_set_size_request(pi->win, width, height);
549 pi->width = width;
550 pi->height = height;
552 if (pi->mask)
553 g_object_unref(pi->mask);
554 pi->mask = gdk_pixmap_new(pi->win->window, width, height, 1);
555 if (!mask_gc)
556 mask_gc = gdk_gc_new(pi->mask);
558 /* Clear the mask to transparent */
559 gdk_gc_set_foreground(mask_gc, &mask_transp);
560 gdk_draw_rectangle(pi->mask, mask_gc, TRUE, 0, 0, width, height);
562 gdk_gc_set_foreground(mask_gc, &mask_solid);
564 /* Make the icon area solid.
565 * Note that the highlighted version must have same alpha...
567 merge_alpha(image->src_pixbuf, pi->mask,
568 (width - iwidth) >> 1, WINK_FRAME);
570 if (item->flags & ITEM_FLAG_SYMLINK)
572 merge_alpha(im_symlink->src_pixbuf, pi->mask,
573 (width - iwidth) >> 1, WINK_FRAME);
575 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
577 /* Note: Both mount state pixmaps must have the same mask */
578 merge_alpha(im_mounted->src_pixbuf, pi->mask,
579 (width - iwidth) >> 1, WINK_FRAME);
582 /* Mask off an area for the text */
584 text_x = (width - pi->name_width) >> 1;
585 text_y = WINK_FRAME + iheight + GAP + 1;
587 gdk_draw_rectangle(pi->mask, mask_gc, TRUE,
588 (width - (pi->name_width + 2)) >> 1,
589 WINK_FRAME + iheight + GAP,
590 pi->name_width + 2, font_height + 2);
592 gtk_widget_shape_combine_mask(pi->win, pi->mask, 0, 0);
594 *rwidth = width;
595 *rheight = height;
598 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi)
600 int text_x, text_y;
601 Icon *icon = (Icon *) pi;
602 DirItem *item = icon->item;
603 MaskedPixmap *image = item->image;
604 int iwidth = image->width;
605 int iheight = image->height;
606 int image_x;
607 GtkStateType state = icon->selected ? GTK_STATE_SELECTED
608 : GTK_STATE_NORMAL;
609 PangoRectangle logical;
611 image_x = (pi->width - iwidth) >> 1;
613 gdk_pixbuf_render_to_drawable_alpha(image->pixbuf,
614 widget->window,
615 0, 0, /* src */
616 image_x, WINK_FRAME, /* dest */
617 iwidth, iheight,
618 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
619 GDK_RGB_DITHER_NORMAL, 0, 0);
621 if (item->flags & ITEM_FLAG_SYMLINK)
623 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
624 widget->window,
625 0, 0, /* src */
626 image_x, WINK_FRAME,
627 -1, -1,
628 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
629 GDK_RGB_DITHER_NORMAL, 0, 0);
631 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
633 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
634 ? im_mounted
635 : im_unmounted;
637 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
638 widget->window,
639 0, 0, /* src */
640 image_x, WINK_FRAME,
641 -1, -1,
642 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
643 GDK_RGB_DITHER_NORMAL, 0, 0);
646 text_x = (pi->width - pi->name_width) >> 1;
647 text_y = WINK_FRAME + iheight + GAP + 1;
649 pango_layout_get_pixel_extents(pi->layout, NULL, &logical);
651 gtk_paint_flat_box(widget->style, widget->window,
652 state,
653 GTK_SHADOW_NONE,
654 NULL, widget, "text",
655 text_x - 1,
656 text_y - 1,
657 pi->name_width + 2,
658 logical.height - logical.y + 2);
660 gtk_paint_layout(widget->style, widget->window,
661 state,
662 FALSE, NULL, widget, "text",
663 text_x,
664 text_y,
665 pi->layout);
667 if (current_wink_icon == pi)
669 gdk_draw_rectangle(pi->widget->window,
670 pi->widget->style->white_gc,
671 FALSE,
672 0, 0, pi->width - 1, pi->height - 1);
673 gdk_draw_rectangle(pi->widget->window,
674 pi->widget->style->black_gc,
675 FALSE,
676 1, 1, pi->width - 3, pi->height - 3);
679 return FALSE;
682 static gboolean root_property_event(GtkWidget *widget,
683 GdkEventProperty *event,
684 gpointer data)
686 if (event->atom == win_button_proxy &&
687 event->state == GDK_PROPERTY_NEW_VALUE)
689 /* Setup forwarding on the new proxy window, if possible */
690 forward_root_clicks();
693 return FALSE;
696 static gboolean root_button_press(GtkWidget *widget,
697 GdkEventButton *event,
698 gpointer data)
700 BindAction action;
702 action = bind_lookup_bev(BIND_PINBOARD, event);
704 switch (action)
706 case ACT_CLEAR_SELECTION:
707 icon_select_only(NULL);
708 break;
709 case ACT_POPUP_MENU:
710 dnd_motion_ungrab();
711 pinboard_show_menu(event, NULL);
712 break;
713 case ACT_IGNORE:
714 break;
715 default:
716 g_warning("Unsupported action : %d\n", action);
717 break;
720 return TRUE;
723 static gboolean enter_notify(GtkWidget *widget,
724 GdkEventCrossing *event,
725 PinIcon *pi)
727 icon_may_update((Icon *) pi);
729 return FALSE;
732 static void perform_action(PinIcon *pi, GdkEventButton *event)
734 BindAction action;
735 Icon *icon = (Icon *) pi;
737 action = bind_lookup_bev(BIND_PINBOARD_ICON, event);
739 switch (action)
741 case ACT_OPEN_ITEM:
742 dnd_motion_ungrab();
743 pinboard_wink_item(pi, TRUE);
744 if (event->type == GDK_2BUTTON_PRESS)
745 icon_set_selected(icon, FALSE);
746 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
747 break;
748 case ACT_EDIT_ITEM:
749 dnd_motion_ungrab();
750 pinboard_wink_item(pi, TRUE);
751 if (event->type == GDK_2BUTTON_PRESS)
752 icon_set_selected(icon, FALSE);
753 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
754 break;
755 case ACT_POPUP_MENU:
756 dnd_motion_ungrab();
757 pinboard_show_menu(event, pi);
758 break;
759 case ACT_PRIME_AND_SELECT:
760 if (!icon->selected)
761 icon_select_only(icon);
762 dnd_motion_start(MOTION_READY_FOR_DND);
763 break;
764 case ACT_PRIME_AND_TOGGLE:
765 icon_set_selected(icon, !icon->selected);
766 dnd_motion_start(MOTION_READY_FOR_DND);
767 break;
768 case ACT_PRIME_FOR_DND:
769 dnd_motion_start(MOTION_READY_FOR_DND);
770 break;
771 case ACT_TOGGLE_SELECTED:
772 icon_set_selected(icon, !icon->selected);
773 break;
774 case ACT_SELECT_EXCL:
775 icon_select_only(icon);
776 break;
777 case ACT_IGNORE:
778 break;
779 default:
780 g_warning("Unsupported action : %d\n", action);
781 break;
785 static gboolean button_release_event(GtkWidget *widget,
786 GdkEventButton *event,
787 PinIcon *pi)
789 if (pinboard_modified)
790 pinboard_save();
792 if (dnd_motion_release(event))
793 return TRUE;
795 perform_action(pi, event);
797 return TRUE;
800 static gboolean button_press_event(GtkWidget *widget,
801 GdkEventButton *event,
802 PinIcon *pi)
804 if (dnd_motion_press(widget, event))
805 perform_action(pi, event);
807 return TRUE;
810 static void start_drag(PinIcon *pi, GdkEventMotion *event)
812 GtkWidget *widget = pi->widget;
813 Icon *icon = (Icon *) pi;
815 if (!icon->selected)
817 tmp_icon_selected = TRUE;
818 icon_select_only(icon);
821 g_return_if_fail(icon_selection != NULL);
823 pinboard_drag_in_progress = icon;
825 if (icon_selection->next == NULL)
826 drag_one_item(widget, event, icon->path, icon->item, NULL);
827 else
829 guchar *uri_list;
831 uri_list = icon_create_uri_list();
832 drag_selection(widget, event, uri_list);
833 g_free(uri_list);
837 /* An icon is being dragged around... */
838 static gint icon_motion_notify(GtkWidget *widget,
839 GdkEventMotion *event,
840 PinIcon *pi)
842 if (motion_state == MOTION_READY_FOR_DND)
844 if (dnd_motion_moved(event))
845 start_drag(pi, event);
846 return TRUE;
849 return FALSE;
852 /* Create one pinboard icon for each icon in the doc */
853 static void pinboard_load_from_xml(xmlDocPtr doc)
855 xmlNodePtr node, root;
856 char *tmp, *label, *path;
857 int x, y;
859 root = xmlDocGetRootElement(doc);
861 for (node = root->xmlChildrenNode; node; node = node->next)
863 if (node->type != XML_ELEMENT_NODE)
864 continue;
865 if (strcmp(node->name, "icon") != 0)
866 continue;
868 tmp = xmlGetProp(node, "x");
869 if (!tmp)
870 continue;
871 x = atoi(tmp);
872 g_free(tmp);
874 tmp = xmlGetProp(node, "y");
875 if (!tmp)
876 continue;
877 y = atoi(tmp);
878 g_free(tmp);
880 label = xmlGetProp(node, "label");
881 if (!label)
882 label = g_strdup("<missing label>");
883 path = xmlNodeGetContent(node);
884 if (!path)
885 path = g_strdup("<missing path>");
887 pinboard_pin(path, label, x, y);
889 g_free(path);
890 g_free(label);
894 /* Called for each line in the pinboard file while loading a new board.
895 * Only used for old-format files when converting to XML.
897 static const char *pin_from_file(gchar *line)
899 gchar *leaf = NULL;
900 int x, y, n;
902 if (*line == '<')
904 gchar *end;
906 end = strchr(line + 1, '>');
907 if (!end)
908 return _("Missing '>' in icon label");
910 leaf = g_strndup(line + 1, end - line - 1);
912 line = end + 1;
914 while (isspace(*line))
915 line++;
916 if (*line != ',')
917 return _("Missing ',' after icon label");
918 line++;
921 if (sscanf(line, " %d , %d , %n", &x, &y, &n) < 2)
922 return NULL; /* Ignore format errors */
924 pinboard_pin(line + n, leaf, x, y);
926 g_free(leaf);
928 return NULL;
931 /* Make sure that clicks and drops on the root window come to us...
932 * False if an error occurred (ie, someone else is using it).
934 static gboolean add_root_handlers(void)
936 GdkWindow *root;
938 if (!proxy_invisible)
940 win_button_proxy = gdk_atom_intern("_WIN_DESKTOP_BUTTON_PROXY",
941 FALSE);
942 proxy_invisible = gtk_invisible_new();
943 gtk_widget_show(proxy_invisible);
945 gdk_window_add_filter(proxy_invisible->window,
946 proxy_filter, NULL);
949 g_signal_connect(proxy_invisible, "property_notify_event",
950 G_CALLBACK(root_property_event), NULL);
951 g_signal_connect(proxy_invisible, "button_press_event",
952 G_CALLBACK(root_button_press), NULL);
954 /* Drag and drop handlers */
955 drag_set_pinboard_dest(proxy_invisible);
956 g_signal_connect(proxy_invisible, "drag_motion",
957 G_CALLBACK(bg_drag_motion), NULL);
958 g_signal_connect(proxy_invisible, "drag_leave",
959 G_CALLBACK(bg_drag_leave), NULL);
962 root = gdk_window_lookup(GDK_ROOT_WINDOW());
963 if (!root)
964 root = gdk_window_foreign_new(GDK_ROOT_WINDOW());
966 if (!setup_xdnd_proxy(GDK_ROOT_WINDOW(), proxy_invisible->window))
967 return FALSE;
969 /* Forward events from the root window to our proxy window */
970 gdk_window_add_filter(gdk_get_default_root_window(),
971 proxy_filter, NULL);
972 gdk_window_set_user_data(gdk_get_default_root_window(),
973 proxy_invisible);
974 gdk_window_set_events(gdk_get_default_root_window(),
975 gdk_window_get_events(gdk_get_default_root_window()) |
976 GDK_EXPOSURE_MASK |
977 GDK_PROPERTY_CHANGE_MASK);
979 forward_root_clicks();
981 return TRUE;
984 /* See if the window manager is offering to forward root window clicks.
985 * If so, grab them. Otherwise, do nothing.
986 * Call this whenever the _WIN_DESKTOP_BUTTON_PROXY property changes.
988 static void forward_root_clicks(void)
990 click_proxy_gdk_window = find_click_proxy_window();
991 if (!click_proxy_gdk_window)
992 return;
994 /* Events on the wm's proxy are dealt with by our proxy widget */
995 gdk_window_set_user_data(click_proxy_gdk_window, proxy_invisible);
996 gdk_window_add_filter(click_proxy_gdk_window, proxy_filter, NULL);
998 /* The proxy window for clicks sends us button press events with
999 * SubstructureNotifyMask. We need StructureNotifyMask to receive
1000 * DestroyNotify events, too.
1002 XSelectInput(GDK_DISPLAY(),
1003 GDK_WINDOW_XWINDOW(click_proxy_gdk_window),
1004 SubstructureNotifyMask | StructureNotifyMask);
1007 /* Write the current state of the pinboard to the current pinboard file */
1008 static void pinboard_save(void)
1010 guchar *save = NULL;
1011 guchar *save_new = NULL;
1012 GList *next;
1013 xmlDocPtr doc = NULL;
1014 xmlNodePtr root;
1016 g_return_if_fail(current_pinboard != NULL);
1018 pinboard_modified = FALSE;
1020 if (strchr(current_pinboard->name, '/'))
1021 save = g_strdup(current_pinboard->name);
1022 else
1024 guchar *leaf;
1026 leaf = g_strconcat("pb_", current_pinboard->name, NULL);
1027 save = choices_find_path_save(leaf, PROJECT, TRUE);
1028 g_free(leaf);
1031 if (!save)
1032 return;
1034 doc = xmlNewDoc("1.0");
1035 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "pinboard", NULL));
1037 root = xmlDocGetRootElement(doc);
1039 for (next = current_pinboard->icons; next; next = next->next)
1041 xmlNodePtr tree;
1042 PinIcon *pi = (PinIcon *) next->data;
1043 Icon *icon = (Icon *) pi;
1044 char *tmp;
1046 tree = xmlNewTextChild(root, NULL, "icon", icon->src_path);
1048 tmp = g_strdup_printf("%d", pi->x);
1049 xmlSetProp(tree, "x", tmp);
1050 g_free(tmp);
1052 tmp = g_strdup_printf("%d", pi->y);
1053 xmlSetProp(tree, "y", tmp);
1054 g_free(tmp);
1056 xmlSetProp(tree, "label", icon->item->leafname);
1059 save_new = g_strconcat(save, ".new", NULL);
1060 if (save_xml_file(doc, save_new) || rename(save_new, save))
1061 delayed_error(_("Error saving pinboard %s: %s"),
1062 save, g_strerror(errno));
1063 g_free(save_new);
1065 g_free(save);
1066 if (doc)
1067 xmlFreeDoc(doc);
1071 * Filter that translates proxied events from virtual root windows into normal
1072 * Gdk events for the proxy_invisible widget. Stolen from gmc.
1074 * Also gets events from the root window.
1076 static GdkFilterReturn proxy_filter(GdkXEvent *xevent,
1077 GdkEvent *event,
1078 gpointer data)
1080 XEvent *xev;
1081 GdkWindow *proxy = proxy_invisible->window;
1082 GdkRectangle area;
1084 xev = xevent;
1086 switch (xev->type) {
1087 case ButtonPress:
1088 case ButtonRelease:
1089 /* Translate button events into events that come from
1090 * the proxy window, so that we can catch them as a
1091 * signal from the invisible widget.
1093 if (xev->type == ButtonPress)
1094 event->button.type = GDK_BUTTON_PRESS;
1095 else
1096 event->button.type = GDK_BUTTON_RELEASE;
1098 g_object_ref(proxy);
1100 event->button.window = proxy;
1101 event->button.send_event = xev->xbutton.send_event;
1102 event->button.time = xev->xbutton.time;
1103 event->button.x_root = xev->xbutton.x_root;
1104 event->button.y_root = xev->xbutton.y_root;
1105 event->button.x = xev->xbutton.x;
1106 event->button.y = xev->xbutton.y;
1107 event->button.state = xev->xbutton.state;
1108 event->button.button = xev->xbutton.button;
1109 event->button.axes = NULL;
1111 return GDK_FILTER_TRANSLATE;
1113 case Expose:
1114 area.x = xev->xexpose.x;
1115 area.y = xev->xexpose.y;
1116 area.width = xev->xexpose.width;
1117 area.height = xev->xexpose.height;
1118 bg_expose(&area);
1119 return GDK_FILTER_REMOVE;
1121 case DestroyNotify:
1122 /* XXX: I have no idea why this helps, but it does! */
1123 /* The proxy window was destroyed (i.e. the window
1124 * manager died), so we have to cope with it
1126 if (((GdkEventAny *) event)->window == proxy)
1127 gdk_window_destroy_notify(proxy);
1129 return GDK_FILTER_REMOVE;
1131 default:
1132 break;
1135 return GDK_FILTER_CONTINUE;
1138 static void snap_to_grid(int *x, int *y)
1140 int step = o_pinboard_grid_step.int_value;
1142 *x = ((*x + step / 2) / step) * step;
1143 *y = ((*y + step / 2) / step) * step;
1146 /* Convert (x,y) from a centre point to a window position */
1147 static void offset_from_centre(PinIcon *pi,
1148 int width, int height,
1149 int *x, int *y)
1151 gboolean clamp = o_pinboard_clamp_icons.int_value;
1153 *x -= width >> 1;
1154 *y -= height >> 1;
1155 *x = CLAMP(*x, 0, screen_width - (clamp ? width : 0));
1156 *y = CLAMP(*y, 0, screen_height - (clamp ? height : 0));
1159 /* Convert (x,y) from a window position to a centre point */
1160 static void offset_to_centre(PinIcon *pi,
1161 int width, int height,
1162 int *x, int *y)
1164 *x += width >> 1;
1165 *y += height >> 1;
1168 /* Same as drag_set_dest(), but for pinboard icons */
1169 static void drag_set_pinicon_dest(PinIcon *pi)
1171 GtkObject *obj = GTK_OBJECT(pi->widget);
1173 make_drop_target(pi->widget, 0);
1175 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1176 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1177 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1180 /* Called during the drag when the mouse is in a widget registered
1181 * as a drop target. Returns TRUE if we can accept the drop.
1183 static gboolean drag_motion(GtkWidget *widget,
1184 GdkDragContext *context,
1185 gint x,
1186 gint y,
1187 guint time,
1188 PinIcon *pi)
1190 GdkDragAction action = context->suggested_action;
1191 char *type = NULL;
1192 Icon *icon = (Icon *) pi;
1193 DirItem *item = icon->item;
1195 if (gtk_drag_get_source_widget(context) == widget)
1196 goto out; /* Can't drag something to itself! */
1198 if (icon->selected)
1199 goto out; /* Can't drag a selection to itself */
1201 type = dnd_motion_item(context, &item);
1203 if (!item)
1204 type = NULL;
1205 out:
1206 /* We actually must pretend to accept the drop, even if the
1207 * directory isn't writeable, so that the spring-opening
1208 * thing works.
1211 /* Don't allow drops to non-writeable directories */
1212 if (o_dnd_spring_open.int_value == FALSE &&
1213 type == drop_dest_dir &&
1214 access(icon->path, W_OK) != 0)
1216 type = NULL;
1219 g_dataset_set_data(context, "drop_dest_type", type);
1220 if (type)
1222 gdk_drag_status(context, action, time);
1223 g_dataset_set_data_full(context, "drop_dest_path",
1224 g_strdup(icon->path), g_free);
1225 if (type == drop_dest_dir)
1226 dnd_spring_load(context, NULL);
1228 pinboard_wink_item(pi, FALSE);
1231 return type != NULL;
1234 static gboolean pinboard_shadow = FALSE;
1235 static gint shadow_x, shadow_y;
1236 #define SHADOW_SIZE (ICON_WIDTH)
1238 static void bg_expose(GdkRectangle *area)
1240 GdkWindow *root;
1241 static GdkGC *shadow_gc = NULL;
1242 static GdkColor white, black;
1244 if (!pinboard_shadow)
1246 /* XXX: Should just disable the events */
1247 return;
1250 root = gdk_get_default_root_window(); /* XXX */
1252 if (!shadow_gc)
1254 GdkColormap *cm;
1255 gboolean success;
1257 white.red = white.green = white.blue = 0xffff;
1258 black.red = black.green = black.blue = 0;
1260 cm = gdk_drawable_get_colormap(root);
1261 shadow_gc = gdk_gc_new(root);
1263 gdk_colormap_alloc_colors(cm, &white, 1, FALSE, TRUE, &success);
1264 gdk_colormap_alloc_colors(cm, &black, 1, FALSE, TRUE, &success);
1267 gdk_gc_set_clip_rectangle(shadow_gc, area);
1268 gdk_gc_set_foreground(shadow_gc, &white);
1269 gdk_draw_rectangle(root, shadow_gc, FALSE, shadow_x, shadow_y,
1270 SHADOW_SIZE, SHADOW_SIZE);
1271 gdk_gc_set_foreground(shadow_gc, &black);
1272 gdk_draw_rectangle(root, shadow_gc, FALSE, shadow_x + 1, shadow_y + 1,
1273 SHADOW_SIZE - 2, SHADOW_SIZE - 2);
1274 gdk_gc_set_clip_rectangle(shadow_gc, NULL);
1277 /* Draw a 'shadow' under an icon being dragged, showing where
1278 * it will land.
1280 static void pinboard_set_shadow(gboolean on)
1282 GdkWindow *root;
1284 root = gdk_get_default_root_window();
1286 if (pinboard_shadow)
1288 gdk_window_clear_area_e(root, shadow_x, shadow_y,
1289 SHADOW_SIZE + 1, SHADOW_SIZE + 1);
1292 if (on)
1294 int old_x = shadow_x, old_y = shadow_y;
1296 gdk_window_get_pointer(root, &shadow_x, &shadow_y, NULL);
1297 snap_to_grid(&shadow_x, &shadow_y);
1298 shadow_x -= SHADOW_SIZE / 2;
1299 shadow_y -= SHADOW_SIZE / 2;
1302 if (pinboard_shadow && shadow_x == old_x && shadow_y == old_y)
1303 return;
1305 gdk_window_clear_area_e(root, shadow_x, shadow_y,
1306 SHADOW_SIZE + 1, SHADOW_SIZE + 1);
1309 pinboard_shadow = on;
1312 /* Called when dragging some pinboard icons finishes */
1313 void pinboard_move_icons(void)
1315 int x = shadow_x, y = shadow_y;
1316 PinIcon *pi = (PinIcon *) pinboard_drag_in_progress;
1317 int width, height;
1319 g_return_if_fail(pi != NULL);
1321 x += SHADOW_SIZE / 2;
1322 y += SHADOW_SIZE / 2;
1323 snap_to_grid(&x, &y);
1325 if (pi->x == x && pi->y == y)
1326 return;
1328 pi->x = x;
1329 pi->y = y;
1330 gdk_drawable_get_size(pi->win->window, &width, &height);
1331 offset_from_centre(pi, width, height, &x, &y);
1333 gdk_window_move(pi->win->window, x, y);
1335 pinboard_save();
1338 static void drag_leave(GtkWidget *widget,
1339 GdkDragContext *context,
1340 guint32 time,
1341 PinIcon *pi)
1343 pinboard_wink_item(NULL, FALSE);
1344 dnd_spring_abort();
1347 static gboolean bg_drag_leave(GtkWidget *widget,
1348 GdkDragContext *context,
1349 guint32 time,
1350 gpointer data)
1352 pinboard_set_shadow(FALSE);
1353 return TRUE;
1357 static gboolean bg_drag_motion(GtkWidget *widget,
1358 GdkDragContext *context,
1359 gint x,
1360 gint y,
1361 guint time,
1362 gpointer data)
1364 /* Dragging from the pinboard to the pinboard is not allowed */
1366 if (!provides(context, text_uri_list))
1367 return FALSE;
1369 pinboard_set_shadow(TRUE);
1371 gdk_drag_status(context,
1372 context->suggested_action == GDK_ACTION_ASK
1373 ? GDK_ACTION_LINK : context->suggested_action,
1374 time);
1375 return TRUE;
1378 static void drag_end(GtkWidget *widget,
1379 GdkDragContext *context,
1380 PinIcon *pi)
1382 pinboard_drag_in_progress = NULL;
1383 if (tmp_icon_selected)
1385 icon_select_only(NULL);
1386 tmp_icon_selected = FALSE;
1390 /* Something which affects all the icons has changed - reshape
1391 * and redraw all of them.
1393 static void reshape_all(void)
1395 GList *next;
1397 g_return_if_fail(current_pinboard != NULL);
1399 for (next = current_pinboard->icons; next; next = next->next)
1401 Icon *icon = (Icon *) next->data;
1402 pinboard_reshape_icon(icon);
1406 /* Turns off the pinboard. Does not call gtk_main_quit. */
1407 static void pinboard_clear(void)
1409 GList *next;
1411 g_return_if_fail(current_pinboard != NULL);
1413 next = current_pinboard->icons;
1414 while (next)
1416 PinIcon *pi = (PinIcon *) next->data;
1418 next = next->next;
1420 gtk_widget_destroy(pi->win);
1423 g_free(current_pinboard->name);
1424 g_free(current_pinboard);
1425 current_pinboard = NULL;
1427 release_xdnd_proxy(GDK_ROOT_WINDOW());
1428 gdk_window_remove_filter(gdk_get_default_root_window(),
1429 proxy_filter, NULL);
1430 gdk_window_set_user_data(gdk_get_default_root_window(), NULL);
1432 number_of_windows--;
1435 static gpointer parent_class;
1437 static void pin_icon_destroy(Icon *icon)
1439 PinIcon *pi = (PinIcon *) icon;
1441 g_return_if_fail(pi->win != NULL);
1443 gtk_widget_destroy(pi->win);
1446 static void pin_icon_finalize(GObject *icon)
1448 PinIcon *pi = (PinIcon *) icon;
1450 if (pi->layout)
1452 g_object_unref(G_OBJECT(pi->layout));
1453 pi->layout = NULL;
1456 ((GObjectClass *) parent_class)->finalize(icon);
1459 static void pinboard_remove_items(void)
1461 g_return_if_fail(icon_selection != NULL);
1463 while (icon_selection)
1464 icon_destroy((Icon *) icon_selection->data);
1466 pinboard_save();
1469 static void pin_icon_update(Icon *icon)
1471 pinboard_reshape_icon(icon);
1472 pinboard_save();
1475 static gboolean pin_icon_same_group(Icon *icon, Icon *other)
1477 return IS_PIN_ICON(other);
1480 static void pin_icon_class_init(gpointer gclass, gpointer data)
1482 IconClass *icon = (IconClass *) gclass;
1484 parent_class = g_type_class_peek_parent(gclass);
1486 ((GObjectClass *) icon)->finalize = pin_icon_finalize;
1488 icon->destroy = pin_icon_destroy;
1489 icon->redraw = pinboard_reshape_icon;
1490 icon->update = pin_icon_update;
1491 icon->remove_items = pinboard_remove_items;
1492 icon->same_group = pin_icon_same_group;
1495 static void pin_icon_init(GTypeInstance *object, gpointer gclass)
1497 PinIcon *pi = (PinIcon *) object;
1499 pi->layout = NULL;
1500 pi->mask = NULL;
1503 static GType pin_icon_get_type(void)
1505 static GType type = 0;
1507 if (!type)
1509 static const GTypeInfo info =
1511 sizeof (PinIconClass),
1512 NULL, /* base_init */
1513 NULL, /* base_finalise */
1514 pin_icon_class_init,
1515 NULL, /* class_finalise */
1516 NULL, /* class_data */
1517 sizeof(PinIcon),
1518 0, /* n_preallocs */
1519 pin_icon_init
1522 type = g_type_register_static(icon_get_type(),
1523 "PinIcon", &info, 0);
1526 return type;
1529 static PinIcon *pin_icon_new(const char *pathname, const char *name)
1531 PinIcon *pi;
1532 Icon *icon;
1534 pi = g_object_new(pin_icon_get_type(), NULL);
1535 icon = (Icon *) pi;
1537 icon_set_path(icon, pathname, name);
1539 return pi;
1542 /* Called when the window widget is somehow destroyed */
1543 static void pin_icon_destroyed(PinIcon *pi)
1545 g_return_if_fail(pi->win != NULL);
1547 pi->win = NULL;
1549 pinboard_wink_item(NULL, FALSE);
1551 if (pinboard_drag_in_progress == (Icon *) pi)
1552 pinboard_drag_in_progress = NULL;
1554 if (current_pinboard)
1555 current_pinboard->icons =
1556 g_list_remove(current_pinboard->icons, pi);
1558 g_object_unref(pi);
1561 /* Set the tooltip */
1562 static void pin_icon_set_tip(PinIcon *pi)
1564 XMLwrapper *ai;
1565 xmlNode *node;
1566 Icon *icon = (Icon *) pi;
1568 g_return_if_fail(pi != NULL);
1570 ai = appinfo_get(icon->path, icon->item);
1572 if (ai && ((node = appinfo_get_section(ai, "Summary"))))
1574 guchar *str;
1575 str = xmlNodeListGetString(node->doc,
1576 node->xmlChildrenNode, 1);
1577 if (str)
1579 gtk_tooltips_set_tip(tooltips, pi->win, str, NULL);
1580 g_free(str);
1583 else
1584 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
1586 if (ai)
1587 g_object_unref(ai);
1590 static void pinboard_show_menu(GdkEventButton *event, PinIcon *pi)
1592 int pos[3];
1594 pos[0] = event->x_root;
1595 pos[1] = event->y_root;
1596 pos[2] = 1;
1598 icon_prepare_menu((Icon *) pi);
1600 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
1601 position_menu,
1602 (gpointer) pos, event->button, event->time);
1605 /* Render the mask of 'src' onto 'mask' at (x, y) */
1606 static void merge_alpha(GdkPixbuf *src, GdkBitmap *mask, int x, int y)
1608 GdkGC *gc;
1609 GdkColor color;
1610 GdkPixmap *pixmap;
1611 GdkBitmap *src_mask;
1613 gc = gdk_gc_new(mask);
1614 color.pixel = 1;
1615 gdk_gc_set_foreground(gc, &color);
1617 gdk_pixbuf_render_pixmap_and_mask(src, &pixmap, &src_mask, 10);
1618 g_object_unref(pixmap);
1620 if (src_mask)
1622 gdk_gc_set_function(gc, GDK_OR);
1623 gdk_draw_drawable(mask, gc, src_mask, 0, 0, x, y, -1, -1);
1624 g_object_unref(src_mask);
1626 else
1627 gdk_draw_rectangle(mask, gc, TRUE, x, y,
1628 gdk_pixbuf_get_width(src), gdk_pixbuf_get_height(src));
1630 g_object_unref(gc);