r1447: Middle button clicks on the pinboard are passed to the window manager.
[rox-filer.git] / ROX-Filer / src / pinboard.c
blobb0655364c946bb30b01cfa005fa42bb7fd38fbb2
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"
53 #include "xml.h"
55 static gboolean tmp_icon_selected = FALSE; /* When dragging */
57 struct _Pinboard {
58 guchar *name; /* Leaf name */
59 GList *icons;
60 GtkStyle *style;
62 BackdropStyle backdrop_style;
63 gchar *backdrop; /* Pathname */
65 GtkWidget *window; /* Screen-sized window */
66 GtkWidget *fixed;
69 #define IS_PIN_ICON(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), pin_icon_get_type())
71 typedef struct _PinIconClass PinIconClass;
72 typedef struct _PinIcon PinIcon;
74 struct _PinIconClass {
75 IconClass parent;
78 struct _PinIcon {
79 Icon icon;
81 int x, y, width, height;
82 int name_width;
83 PangoLayout *layout; /* The label */
84 GtkWidget *win;
85 GtkWidget *widget; /* The drawing area */
88 /* The number of pixels between the bottom of the image and the top
89 * of the text.
91 #define GAP 4
93 /* The size of the border around the icon which is used when winking */
94 #define WINK_FRAME 2
96 /* Grid sizes */
97 #define GRID_STEP_FINE 2
98 #define GRID_STEP_MED 16
99 #define GRID_STEP_COARSE 32
101 static PinIcon *current_wink_icon = NULL;
102 static gint wink_timeout;
104 /* Used for the text colours (only) in the icons */
105 static GdkColor text_fg_col, text_bg_col;
107 /* Style that all the icons should use. NULL => regenerate from text_fg/bg */
108 static GtkStyle *pinicon_style = NULL;
110 Pinboard *current_pinboard = NULL;
111 static gint loading_pinboard = 0; /* Non-zero => loading */
113 /* The Icon that was used to start the current drag, if any */
114 Icon *pinboard_drag_in_progress = NULL;
116 typedef enum {
117 TEXT_BG_NONE = 0,
118 TEXT_BG_OUTLINE = 1,
119 TEXT_BG_SOLID = 2,
120 } TextBgType;
122 static Option o_pinboard_clamp_icons, o_pinboard_grid_step;
123 static Option o_pinboard_fg_colour, o_pinboard_bg_colour;
125 /* Static prototypes */
126 static GType pin_icon_get_type(void);
127 static void set_size_and_style(PinIcon *pi);
128 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi);
129 static gint end_wink(gpointer data);
130 static gboolean button_release_event(GtkWidget *widget,
131 GdkEventButton *event,
132 PinIcon *pi);
133 static gboolean enter_notify(GtkWidget *widget,
134 GdkEventCrossing *event,
135 PinIcon *pi);
136 static gboolean button_press_event(GtkWidget *widget,
137 GdkEventButton *event,
138 PinIcon *pi);
139 static gint icon_motion_notify(GtkWidget *widget,
140 GdkEventMotion *event,
141 PinIcon *pi);
142 static const char *pin_from_file(gchar *line);
143 static void snap_to_grid(int *x, int *y);
144 static void offset_from_centre(PinIcon *pi,
145 int width, int height,
146 int *x, int *y);
147 static void offset_to_centre(PinIcon *pi,
148 int width, int height,
149 int *x, int *y);
150 static gboolean drag_motion(GtkWidget *widget,
151 GdkDragContext *context,
152 gint x,
153 gint y,
154 guint time,
155 PinIcon *pi);
156 static void drag_set_pinicon_dest(PinIcon *pi);
157 static void drag_leave(GtkWidget *widget,
158 GdkDragContext *context,
159 guint32 time,
160 PinIcon *pi);
161 static gboolean bg_drag_motion(GtkWidget *widget,
162 GdkDragContext *context,
163 gint x,
164 gint y,
165 guint time,
166 gpointer data);
167 static gboolean bg_drag_leave(GtkWidget *widget,
168 GdkDragContext *context,
169 guint32 time,
170 gpointer data);
171 static void bg_expose(GtkWidget *window, GdkEventExpose *event, gpointer data);
172 static void drag_end(GtkWidget *widget,
173 GdkDragContext *context,
174 PinIcon *pi);
175 static void reshape_all(void);
176 static void pinboard_check_options(void);
177 static void pinboard_load_from_xml(xmlDocPtr doc);
178 static void pinboard_clear(void);
179 static void pinboard_save(void);
180 static PinIcon *pin_icon_new(const char *pathname, const char *name);
181 static void pin_icon_destroyed(PinIcon *pi);
182 static void pin_icon_set_tip(PinIcon *pi);
183 static void pinboard_show_menu(GdkEventButton *event, PinIcon *pi);
184 static void create_pinboard_window(Pinboard *pinboard);
185 static void reload_backdrop(Pinboard *pinboard);
186 static void set_backdrop(Pinboard *pinboard, const gchar *image,
187 const gchar *path, BackdropStyle style);
189 /****************************************************************
190 * EXTERNAL INTERFACE *
191 ****************************************************************/
193 void pinboard_init(void)
195 option_add_string(&o_pinboard_fg_colour, "pinboard_fg_colour", "#000");
196 option_add_string(&o_pinboard_bg_colour, "pinboard_bg_colour", "#ddd");
198 option_add_int(&o_pinboard_clamp_icons, "pinboard_clamp_icons", 1);
199 option_add_int(&o_pinboard_grid_step, "pinboard_grid_step",
200 GRID_STEP_COARSE);
201 option_add_notify(pinboard_check_options);
203 gdk_color_parse(o_pinboard_fg_colour.value, &text_fg_col);
204 gdk_color_parse(o_pinboard_bg_colour.value, &text_bg_col);
207 /* Load 'pb_<pinboard>' config file from Choices (if it exists)
208 * and make it the current pinboard.
209 * Any existing pinned items are removed. You must call this
210 * at least once before using the pinboard. NULL disables the
211 * pinboard.
213 void pinboard_activate(const gchar *name)
215 Pinboard *old_board = current_pinboard;
216 guchar *path, *slash;
218 /* Treat an empty name the same as NULL */
219 if (name && !*name)
220 name = NULL;
222 if (old_board)
223 pinboard_clear();
225 if (!name)
227 if (number_of_windows < 1 && gtk_main_level() > 0)
228 gtk_main_quit();
229 return;
232 number_of_windows++;
234 slash = strchr(name, '/');
235 if (slash)
237 if (access(name, F_OK))
238 path = NULL; /* File does not (yet) exist */
239 else
240 path = g_strdup(name);
242 else
244 guchar *leaf;
246 leaf = g_strconcat("pb_", name, NULL);
247 path = choices_find_path_load(leaf, PROJECT);
248 g_free(leaf);
251 current_pinboard = g_new(Pinboard, 1);
252 current_pinboard->name = g_strdup(name);
253 current_pinboard->icons = NULL;
254 current_pinboard->window = NULL;
255 current_pinboard->backdrop = NULL;
256 current_pinboard->backdrop_style = BACKDROP_NONE;
258 create_pinboard_window(current_pinboard);
260 loading_pinboard++;
261 if (path)
263 xmlDocPtr doc;
264 doc = xmlParseFile(path);
265 if (doc)
267 pinboard_load_from_xml(doc);
268 xmlFreeDoc(doc);
269 reload_backdrop(current_pinboard);
271 else
273 parse_file(path, pin_from_file);
274 info_message(_("Your old pinboard file has been "
275 "converted to the new XML format."));
276 pinboard_save();
278 g_free(path);
280 else
281 pinboard_pin(home_dir, "Home",
282 4 + ICON_WIDTH / 2,
283 4 + ICON_HEIGHT / 2);
284 loading_pinboard--;
287 const char *pinboard_get_name(void)
289 g_return_val_if_fail(current_pinboard != NULL, NULL);
291 return current_pinboard->name;
294 /* Add a new icon to the background.
295 * 'path' should be an absolute pathname.
296 * 'x' and 'y' are the coordinates of the point in the middle of the text
297 * if 'corner' is FALSE, and as the top-left corner of where the icon
298 * image should be if it is TRUE.
299 * 'name' is the name to use. If NULL then the leafname of path is used.
301 * name and path are in UTF-8 for Gtk+-2.0 only.
303 void pinboard_pin(const gchar *path, const gchar *name, int x, int y)
305 PinIcon *pi;
306 Icon *icon;
308 g_return_if_fail(path != NULL);
309 g_return_if_fail(current_pinboard != NULL);
311 pi = pin_icon_new(path, name);
312 icon = (Icon *) pi;
313 pi->x = x;
314 pi->y = y;
316 /* Box takes the initial ref of Icon */
317 pi->win = gtk_event_box_new();
319 pi->widget = gtk_drawing_area_new();
321 pi->layout = gtk_widget_create_pango_layout(pi->widget, NULL);
322 pango_layout_set_width(pi->layout, 140 * PANGO_SCALE);
324 gtk_container_add(GTK_CONTAINER(pi->win), pi->widget);
325 drag_set_pinicon_dest(pi);
326 g_signal_connect(pi->widget, "drag_data_get",
327 G_CALLBACK(drag_data_get), NULL);
329 gtk_fixed_put(GTK_FIXED(current_pinboard->fixed), pi->win, x, y);
330 gtk_widget_realize(pi->win);
331 gtk_widget_realize(pi->widget);
333 set_size_and_style(pi);
334 snap_to_grid(&x, &y);
335 offset_from_centre(pi, pi->width, pi->height, &x, &y);
336 gtk_fixed_move(GTK_FIXED(current_pinboard->fixed), pi->win, x, y);
337 /* Set the correct position in the icon */
338 offset_to_centre(pi, pi->width, pi->height, &x, &y);
339 pi->x = x;
340 pi->y = y;
342 gtk_widget_add_events(pi->widget,
343 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
344 GDK_BUTTON1_MOTION_MASK | GDK_ENTER_NOTIFY_MASK |
345 GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK);
346 g_signal_connect(pi->widget, "enter-notify-event",
347 G_CALLBACK(enter_notify), pi);
348 g_signal_connect(pi->widget, "button-press-event",
349 G_CALLBACK(button_press_event), pi);
350 g_signal_connect(pi->widget, "button-release-event",
351 G_CALLBACK(button_release_event), pi);
352 g_signal_connect(pi->widget, "motion-notify-event",
353 G_CALLBACK(icon_motion_notify), pi);
354 g_signal_connect(pi->widget, "expose-event",
355 G_CALLBACK(draw_icon), pi);
356 g_signal_connect_swapped(pi->win, "destroy",
357 G_CALLBACK(pin_icon_destroyed), pi);
359 current_pinboard->icons = g_list_prepend(current_pinboard->icons, pi);
360 pin_icon_set_tip(pi);
361 gtk_widget_show_all(pi->win);
363 if (!loading_pinboard)
364 pinboard_save();
367 /* Remove an icon from the pinboard */
368 /* XXX: use destroy */
369 void pinboard_unpin(PinIcon *pi)
371 g_return_if_fail(pi != NULL);
373 gtk_widget_destroy(pi->win);
374 pinboard_save();
377 /* Put a border around the icon, briefly.
378 * If icon is NULL then cancel any existing wink.
379 * The icon will automatically unhighlight unless timeout is FALSE,
380 * in which case you must call this function again (with NULL or another
381 * icon) to remove the highlight.
383 static void pinboard_wink_item(PinIcon *pi, gboolean timeout)
385 PinIcon *old = current_wink_icon;
387 if (old == pi)
388 return;
390 current_wink_icon = pi;
392 if (old)
394 gtk_widget_queue_draw(old->widget);
395 gdk_window_process_updates(old->widget->window, TRUE);
397 if (wink_timeout != -1)
398 gtk_timeout_remove(wink_timeout);
401 if (pi)
403 gtk_widget_queue_draw(pi->widget);
404 gdk_window_process_updates(pi->widget->window, TRUE);
406 if (timeout)
407 wink_timeout = gtk_timeout_add(300, end_wink, NULL);
408 else
409 wink_timeout = -1;
413 /* Icon's size, shape or appearance has changed - update the display */
414 void pinboard_reshape_icon(Icon *icon)
416 PinIcon *pi = (PinIcon *) icon;
417 int x = pi->x, y = pi->y;
419 set_size_and_style(pi);
420 gdk_window_resize(pi->win->window, pi->width, pi->height);
421 offset_from_centre(pi, pi->width, pi->height, &x, &y);
422 gtk_fixed_move(GTK_FIXED(current_pinboard->fixed), pi->win, x, y);
423 gtk_widget_queue_draw(current_pinboard->fixed);
426 /* Sets 'app' as the new backdrop handler, and loads 'image'.
427 * If image is NULL, will run program to get image.
429 void pinboard_set_backdrop_from_program(const gchar *app, const gchar *image,
430 BackdropStyle style)
432 XMLwrapper *ai;
433 DirItem *item;
434 gboolean can_set;
436 if (!image)
437 style = BACKDROP_PROGRAM;
439 item = diritem_new("");
440 diritem_restat(app, item, NULL);
441 ai = appinfo_get(app, item);
442 diritem_free(item);
444 can_set = ai && xml_get_section(ai, ROX_NS, "CanSetBackdrop") != NULL;
445 if (ai)
446 g_object_unref(ai);
448 if (can_set)
449 set_backdrop(current_pinboard, image, app, style);
450 else
452 delayed_error(_("This program does not know how to "
453 "manage ROX-Filer's backdrop image."));
454 return;
458 /* Use this icon / program as the backdrop for the current pinboard.
459 * NULL removes the backdrop. If type is BACKDROP_NONE then will try
460 * to work it out ourself.
461 * If no pinboard is in use, activates 'Default'.
463 void pinboard_set_backdrop(DirItem *item, const gchar *path)
465 BackdropStyle type = BACKDROP_NONE;
467 if (path == NULL || item == NULL)
469 /* Remove backdrop */
471 if (current_pinboard && current_pinboard->backdrop)
472 set_backdrop(current_pinboard,
473 NULL, NULL, BACKDROP_NONE);
474 else
475 delayed_error(_("No backdrop image is currently "
476 "set. Use the 'Use for Backdrop' menu "
477 "item to set an image (or program) for "
478 "the backdrop."));
479 return;
482 if (item->flags & ITEM_FLAG_APPDIR)
484 pinboard_set_backdrop_from_program(path, NULL, BACKDROP_NONE);
485 return;
488 if (type == BACKDROP_NONE && item->base_type == TYPE_FILE)
490 int i;
491 i = get_choice(_("Set backdrop"),
492 _("How should this image be displayed?"),
493 4, GTK_STOCK_CANCEL,
494 _("Centred"), _("_Scaled"), _("Tiled"));
495 if (i < 1)
496 return;
497 type = i == 1 ? BACKDROP_CENTRE :
498 i == 2 ? BACKDROP_SCALE :
499 BACKDROP_TILE;
502 if (type == BACKDROP_NONE)
504 delayed_error(_("Only files and certain applications can be "
505 "used to set the background image."));
506 return;
509 if (!current_pinboard)
511 pinboard_activate("Default");
512 delayed_error(_("No pinboard was in use... "
513 "the 'Default' pinboard has been selected. "
514 "Use 'rox -p=Default' to turn it on in "
515 "future."));
518 set_backdrop(current_pinboard, NULL, path, type);
521 /****************************************************************
522 * INTERNAL FUNCTIONS *
523 ****************************************************************/
525 static void pinboard_check_options(void)
527 GdkColor n_fg, n_bg;
529 gdk_color_parse(o_pinboard_fg_colour.value, &n_fg);
530 gdk_color_parse(o_pinboard_bg_colour.value, &n_bg);
532 if (gdk_color_equal(&n_fg, &text_fg_col) == 0 ||
533 gdk_color_equal(&n_bg, &text_bg_col) == 0)
535 memcpy(&text_fg_col, &n_fg, sizeof(GdkColor));
536 memcpy(&text_bg_col, &n_bg, sizeof(GdkColor));
538 if (pinicon_style)
540 g_object_unref(G_OBJECT(pinicon_style));
541 pinicon_style = NULL;
544 if (current_pinboard)
545 reshape_all();
549 static gint end_wink(gpointer data)
551 pinboard_wink_item(NULL, FALSE);
552 return FALSE;
555 /* Updates the width, height, name_width and layout fields, and resizes the
556 * window. Also sets the style to pinicon_style, generating it if needed.
558 static void set_size_and_style(PinIcon *pi)
560 int width, height;
561 int font_height;
562 Icon *icon = (Icon *) pi;
563 MaskedPixmap *image = icon->item->image;
564 int iwidth = image->width;
565 int iheight = image->height;
567 if (!pinicon_style)
569 pinicon_style = gtk_style_copy(pi->widget->style);
570 memcpy(&pinicon_style->fg[GTK_STATE_NORMAL],
571 &text_fg_col, sizeof(GdkColor));
572 memcpy(&pinicon_style->bg[GTK_STATE_NORMAL],
573 &text_bg_col, sizeof(GdkColor));
575 /* XXX */
576 #if 0
577 gtk_widget_set_style(pi->widget, pinicon_style);
578 #endif
581 PangoRectangle logical;
582 pango_layout_set_text(pi->layout, icon->item->leafname, -1);
583 pango_layout_get_pixel_extents(pi->layout, NULL, &logical);
585 pi->name_width = logical.width - logical.x;
586 font_height = logical.height - logical.y;
589 width = MAX(iwidth, pi->name_width + 2) + 2 * WINK_FRAME;
590 height = iheight + GAP + (font_height + 2) + 2 * WINK_FRAME;
591 gtk_widget_set_size_request(pi->win, width, height);
592 pi->width = width;
593 pi->height = height;
596 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi)
598 int text_x, text_y;
599 Icon *icon = (Icon *) pi;
600 DirItem *item = icon->item;
601 MaskedPixmap *image = item->image;
602 int iwidth = image->width;
603 int iheight = image->height;
604 int image_x;
605 GtkStateType state = icon->selected ? GTK_STATE_SELECTED
606 : GTK_STATE_NORMAL;
607 PangoRectangle logical;
609 image_x = (pi->width - iwidth) >> 1;
611 gdk_pixbuf_render_to_drawable_alpha(
612 icon->selected ? image->pixbuf_lit : image->pixbuf,
613 widget->window,
614 0, 0, /* src */
615 image_x, WINK_FRAME, /* dest */
616 iwidth, iheight,
617 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
618 GDK_RGB_DITHER_NORMAL, 0, 0);
620 if (item->flags & ITEM_FLAG_SYMLINK)
622 gdk_pixbuf_render_to_drawable_alpha(im_symlink->pixbuf,
623 widget->window,
624 0, 0, /* src */
625 image_x, WINK_FRAME,
626 -1, -1,
627 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
628 GDK_RGB_DITHER_NORMAL, 0, 0);
630 else if (item->flags & ITEM_FLAG_MOUNT_POINT)
632 MaskedPixmap *mp = item->flags & ITEM_FLAG_MOUNTED
633 ? im_mounted
634 : im_unmounted;
636 gdk_pixbuf_render_to_drawable_alpha(mp->pixbuf,
637 widget->window,
638 0, 0, /* src */
639 image_x, WINK_FRAME,
640 -1, -1,
641 GDK_PIXBUF_ALPHA_FULL, 128, /* (unused) */
642 GDK_RGB_DITHER_NORMAL, 0, 0);
645 text_x = (pi->width - pi->name_width) >> 1;
646 text_y = WINK_FRAME + iheight + GAP + 1;
648 pango_layout_get_pixel_extents(pi->layout, NULL, &logical);
650 gtk_paint_flat_box(widget->style, widget->window,
651 state,
652 GTK_SHADOW_NONE,
653 NULL, widget, "text",
654 text_x - 1,
655 text_y - 1,
656 pi->name_width + 2,
657 logical.height - logical.y + 2);
659 gtk_paint_layout(widget->style, widget->window,
660 state,
661 FALSE, NULL, widget, "text",
662 text_x,
663 text_y,
664 pi->layout);
666 if (current_wink_icon == pi)
668 gdk_draw_rectangle(pi->widget->window,
669 pi->widget->style->white_gc,
670 FALSE,
671 0, 0, pi->width - 1, pi->height - 1);
672 gdk_draw_rectangle(pi->widget->window,
673 pi->widget->style->black_gc,
674 FALSE,
675 1, 1, pi->width - 3, pi->height - 3);
678 return FALSE;
681 static gboolean enter_notify(GtkWidget *widget,
682 GdkEventCrossing *event,
683 PinIcon *pi)
685 icon_may_update((Icon *) pi);
687 return FALSE;
690 static void perform_action(PinIcon *pi, GdkEventButton *event)
692 BindAction action;
693 Icon *icon = (Icon *) pi;
695 action = bind_lookup_bev(pi ? BIND_PINBOARD_ICON : BIND_PINBOARD,
696 event);
698 /* Actions that can happen with or without an icon */
699 switch (action)
701 case ACT_CLEAR_SELECTION:
702 icon_select_only(NULL);
703 return;
704 case ACT_POPUP_MENU:
705 dnd_motion_ungrab();
706 pinboard_show_menu(event, pi);
707 return;
708 case ACT_IGNORE:
709 return;
710 default:
711 break;
714 g_return_if_fail(pi != NULL);
716 switch (action)
718 case ACT_OPEN_ITEM:
719 dnd_motion_ungrab();
720 pinboard_wink_item(pi, TRUE);
721 if (event->type == GDK_2BUTTON_PRESS)
722 icon_set_selected(icon, FALSE);
723 run_diritem(icon->path, icon->item, NULL, NULL, FALSE);
724 break;
725 case ACT_EDIT_ITEM:
726 dnd_motion_ungrab();
727 pinboard_wink_item(pi, TRUE);
728 if (event->type == GDK_2BUTTON_PRESS)
729 icon_set_selected(icon, FALSE);
730 run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
731 break;
732 case ACT_PRIME_AND_SELECT:
733 if (!icon->selected)
734 icon_select_only(icon);
735 dnd_motion_start(MOTION_READY_FOR_DND);
736 break;
737 case ACT_PRIME_AND_TOGGLE:
738 icon_set_selected(icon, !icon->selected);
739 dnd_motion_start(MOTION_READY_FOR_DND);
740 break;
741 case ACT_PRIME_FOR_DND:
742 dnd_motion_start(MOTION_READY_FOR_DND);
743 break;
744 case ACT_TOGGLE_SELECTED:
745 icon_set_selected(icon, !icon->selected);
746 break;
747 case ACT_SELECT_EXCL:
748 icon_select_only(icon);
749 break;
750 default:
751 g_warning("Unsupported action : %d\n", action);
752 break;
756 static void forward_to_root(GdkEventButton *event)
758 XButtonEvent xev;
760 if (event->type == GDK_BUTTON_PRESS)
762 xev.type = ButtonPress;
763 XUngrabPointer(gdk_display, event->time);
765 else
766 xev.type = ButtonRelease;
768 xev.window = gdk_x11_get_default_root_xwindow();
769 xev.root = xev.window;
770 xev.subwindow = None;
771 xev.time = event->time;
772 xev.x = event->x;
773 xev.y = event->y;
774 xev.x_root = event->x_root;
775 xev.y_root = event->y_root;
776 xev.state = event->state;
777 xev.button = event->button;
778 xev.same_screen = True;
780 XSendEvent(gdk_display, xev.window, False,
781 ButtonPressMask | ButtonReleaseMask, (XEvent *) &xev);
784 /* pi is NULL if this is a root event */
785 static gboolean button_release_event(GtkWidget *widget,
786 GdkEventButton *event,
787 PinIcon *pi)
789 if (event->button == 2)
790 forward_to_root(event);
791 else if (dnd_motion_release(event))
792 return TRUE;
794 perform_action(pi, event);
796 return TRUE;
799 /* pi is NULL if this is a root event */
800 static gboolean button_press_event(GtkWidget *widget,
801 GdkEventButton *event,
802 PinIcon *pi)
804 if (event->button == 2)
805 forward_to_root(event);
806 else if (dnd_motion_press(widget, event))
807 perform_action(pi, event);
809 return TRUE;
812 static void start_drag(PinIcon *pi, GdkEventMotion *event)
814 GtkWidget *widget = pi->widget;
815 Icon *icon = (Icon *) pi;
817 if (!icon->selected)
819 tmp_icon_selected = TRUE;
820 icon_select_only(icon);
823 g_return_if_fail(icon_selection != NULL);
825 pinboard_drag_in_progress = icon;
827 if (icon_selection->next == NULL)
828 drag_one_item(widget, event, icon->path, icon->item, NULL);
829 else
831 guchar *uri_list;
833 uri_list = icon_create_uri_list();
834 drag_selection(widget, event, uri_list);
835 g_free(uri_list);
839 /* An icon is being dragged around... */
840 static gint icon_motion_notify(GtkWidget *widget,
841 GdkEventMotion *event,
842 PinIcon *pi)
844 if (motion_state == MOTION_READY_FOR_DND)
846 if (dnd_motion_moved(event))
847 start_drag(pi, event);
848 return TRUE;
851 return FALSE;
854 static void backdrop_from_xml(xmlNode *node)
856 gchar *style;
858 g_free(current_pinboard->backdrop);
859 current_pinboard->backdrop = xmlNodeGetContent(node);
861 style = xmlGetProp(node, "style");
863 if (style)
865 current_pinboard->backdrop_style =
866 g_strcasecmp(style, "Tiled") == 0 ? BACKDROP_TILE :
867 g_strcasecmp(style, "Scaled") == 0 ? BACKDROP_SCALE :
868 g_strcasecmp(style, "Centred") == 0 ? BACKDROP_CENTRE :
869 g_strcasecmp(style, "Program") == 0 ? BACKDROP_PROGRAM :
870 BACKDROP_NONE;
871 g_free(style);
873 else
874 current_pinboard->backdrop_style = BACKDROP_TILE;
877 /* Create one pinboard icon for each icon in the doc */
878 static void pinboard_load_from_xml(xmlDocPtr doc)
880 xmlNodePtr node, root;
881 char *tmp, *label, *path;
882 int x, y;
884 root = xmlDocGetRootElement(doc);
886 for (node = root->xmlChildrenNode; node; node = node->next)
888 if (node->type != XML_ELEMENT_NODE)
889 continue;
890 if (strcmp(node->name, "backdrop") == 0)
892 backdrop_from_xml(node);
893 continue;
895 if (strcmp(node->name, "icon") != 0)
896 continue;
898 tmp = xmlGetProp(node, "x");
899 if (!tmp)
900 continue;
901 x = atoi(tmp);
902 g_free(tmp);
904 tmp = xmlGetProp(node, "y");
905 if (!tmp)
906 continue;
907 y = atoi(tmp);
908 g_free(tmp);
910 label = xmlGetProp(node, "label");
911 if (!label)
912 label = g_strdup("<missing label>");
913 path = xmlNodeGetContent(node);
914 if (!path)
915 path = g_strdup("<missing path>");
917 pinboard_pin(path, label, x, y);
919 g_free(path);
920 g_free(label);
924 /* Called for each line in the pinboard file while loading a new board.
925 * Only used for old-format files when converting to XML.
927 static const char *pin_from_file(gchar *line)
929 gchar *leaf = NULL;
930 int x, y, n;
932 if (*line == '<')
934 gchar *end;
936 end = strchr(line + 1, '>');
937 if (!end)
938 return _("Missing '>' in icon label");
940 leaf = g_strndup(line + 1, end - line - 1);
942 line = end + 1;
944 while (isspace(*line))
945 line++;
946 if (*line != ',')
947 return _("Missing ',' after icon label");
948 line++;
951 if (sscanf(line, " %d , %d , %n", &x, &y, &n) < 2)
952 return NULL; /* Ignore format errors */
954 pinboard_pin(line + n, leaf, x, y);
956 g_free(leaf);
958 return NULL;
961 /* Write the current state of the pinboard to the current pinboard file */
962 static void pinboard_save(void)
964 guchar *save = NULL;
965 guchar *save_new = NULL;
966 GList *next;
967 xmlDocPtr doc = NULL;
968 xmlNodePtr root;
970 g_return_if_fail(current_pinboard != NULL);
972 if (strchr(current_pinboard->name, '/'))
973 save = g_strdup(current_pinboard->name);
974 else
976 guchar *leaf;
978 leaf = g_strconcat("pb_", current_pinboard->name, NULL);
979 save = choices_find_path_save(leaf, PROJECT, TRUE);
980 g_free(leaf);
983 if (!save)
984 return;
986 doc = xmlNewDoc("1.0");
987 xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "pinboard", NULL));
989 root = xmlDocGetRootElement(doc);
991 if (current_pinboard->backdrop)
993 BackdropStyle style = current_pinboard->backdrop_style;
994 xmlNodePtr tree;
996 tree = xmlNewTextChild(root, NULL, "backdrop",
997 current_pinboard->backdrop);
998 xmlSetProp(tree, "style",
999 style == BACKDROP_TILE ? "Tiled" :
1000 style == BACKDROP_CENTRE ? "Centred" :
1001 style == BACKDROP_SCALE ? "Scaled" :
1002 "Program");
1005 for (next = current_pinboard->icons; next; next = next->next)
1007 xmlNodePtr tree;
1008 PinIcon *pi = (PinIcon *) next->data;
1009 Icon *icon = (Icon *) pi;
1010 char *tmp;
1012 tree = xmlNewTextChild(root, NULL, "icon", icon->src_path);
1014 tmp = g_strdup_printf("%d", pi->x);
1015 xmlSetProp(tree, "x", tmp);
1016 g_free(tmp);
1018 tmp = g_strdup_printf("%d", pi->y);
1019 xmlSetProp(tree, "y", tmp);
1020 g_free(tmp);
1022 xmlSetProp(tree, "label", icon->item->leafname);
1025 save_new = g_strconcat(save, ".new", NULL);
1026 if (save_xml_file(doc, save_new) || rename(save_new, save))
1027 delayed_error(_("Error saving pinboard %s: %s"),
1028 save, g_strerror(errno));
1029 g_free(save_new);
1031 g_free(save);
1032 if (doc)
1033 xmlFreeDoc(doc);
1036 static void snap_to_grid(int *x, int *y)
1038 int step = o_pinboard_grid_step.int_value;
1040 *x = ((*x + step / 2) / step) * step;
1041 *y = ((*y + step / 2) / step) * step;
1044 /* Convert (x,y) from a centre point to a window position */
1045 static void offset_from_centre(PinIcon *pi,
1046 int width, int height,
1047 int *x, int *y)
1049 gboolean clamp = o_pinboard_clamp_icons.int_value;
1051 *x -= width >> 1;
1052 *y -= height >> 1;
1053 *x = CLAMP(*x, 0, screen_width - (clamp ? width : 0));
1054 *y = CLAMP(*y, 0, screen_height - (clamp ? height : 0));
1057 /* Convert (x,y) from a window position to a centre point */
1058 static void offset_to_centre(PinIcon *pi,
1059 int width, int height,
1060 int *x, int *y)
1062 *x += width >> 1;
1063 *y += height >> 1;
1066 /* Same as drag_set_dest(), but for pinboard icons */
1067 static void drag_set_pinicon_dest(PinIcon *pi)
1069 GtkObject *obj = GTK_OBJECT(pi->widget);
1071 make_drop_target(pi->widget, 0);
1073 g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1074 g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1075 g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1078 /* Called during the drag when the mouse is in a widget registered
1079 * as a drop target. Returns TRUE if we can accept the drop.
1081 static gboolean drag_motion(GtkWidget *widget,
1082 GdkDragContext *context,
1083 gint x,
1084 gint y,
1085 guint time,
1086 PinIcon *pi)
1088 GdkDragAction action = context->suggested_action;
1089 char *type = NULL;
1090 Icon *icon = (Icon *) pi;
1091 DirItem *item = icon->item;
1093 if (gtk_drag_get_source_widget(context) == widget)
1094 goto out; /* Can't drag something to itself! */
1096 if (icon->selected)
1097 goto out; /* Can't drag a selection to itself */
1099 type = dnd_motion_item(context, &item);
1101 if (!item)
1102 type = NULL;
1103 out:
1104 /* We actually must pretend to accept the drop, even if the
1105 * directory isn't writeable, so that the spring-opening
1106 * thing works.
1109 /* Don't allow drops to non-writeable directories */
1110 if (o_dnd_spring_open.int_value == FALSE &&
1111 type == drop_dest_dir &&
1112 access(icon->path, W_OK) != 0)
1114 type = NULL;
1117 g_dataset_set_data(context, "drop_dest_type", type);
1118 if (type)
1120 gdk_drag_status(context, action, time);
1121 g_dataset_set_data_full(context, "drop_dest_path",
1122 g_strdup(icon->path), g_free);
1123 if (type == drop_dest_dir)
1124 dnd_spring_load(context, NULL);
1126 pinboard_wink_item(pi, FALSE);
1128 else
1129 gdk_drag_status(context, 0, time);
1131 /* Always return TRUE to stop the pinboard getting the events */
1132 return TRUE;
1135 static gboolean pinboard_shadow = FALSE;
1136 static gint shadow_x, shadow_y;
1137 #define SHADOW_SIZE (ICON_WIDTH)
1139 static void bg_expose(GtkWidget *window, GdkEventExpose *event, gpointer data)
1141 GdkRectangle *area = &event->area;
1142 static GdkGC *shadow_gc = NULL;
1143 static GdkColor white, black;
1145 if (!pinboard_shadow)
1147 /* XXX: Should just disable the events */
1148 return;
1151 if (!shadow_gc)
1153 GdkColormap *cm;
1154 gboolean success;
1156 white.red = white.green = white.blue = 0xffff;
1157 black.red = black.green = black.blue = 0;
1159 cm = gtk_widget_get_colormap(window);
1160 shadow_gc = gdk_gc_new(window->window);
1162 gdk_colormap_alloc_colors(cm, &white, 1, FALSE, TRUE, &success);
1163 gdk_colormap_alloc_colors(cm, &black, 1, FALSE, TRUE, &success);
1166 gdk_gc_set_clip_rectangle(shadow_gc, area);
1167 gdk_gc_set_foreground(shadow_gc, &white);
1168 gdk_draw_rectangle(window->window, shadow_gc, FALSE,
1169 shadow_x, shadow_y,
1170 SHADOW_SIZE, SHADOW_SIZE);
1171 gdk_gc_set_foreground(shadow_gc, &black);
1172 gdk_draw_rectangle(window->window, shadow_gc, FALSE,
1173 shadow_x + 1, shadow_y + 1,
1174 SHADOW_SIZE - 2, SHADOW_SIZE - 2);
1175 gdk_gc_set_clip_rectangle(shadow_gc, NULL);
1178 /* Draw a 'shadow' under an icon being dragged, showing where
1179 * it will land.
1181 static void pinboard_set_shadow(gboolean on)
1183 if (pinboard_shadow)
1185 gdk_window_clear_area_e(current_pinboard->window->window,
1186 shadow_x, shadow_y,
1187 SHADOW_SIZE + 1, SHADOW_SIZE + 1);
1190 if (on)
1192 int old_x = shadow_x, old_y = shadow_y;
1194 gdk_window_get_pointer(current_pinboard->window->window,
1195 &shadow_x, &shadow_y, NULL);
1196 snap_to_grid(&shadow_x, &shadow_y);
1197 shadow_x -= SHADOW_SIZE / 2;
1198 shadow_y -= SHADOW_SIZE / 2;
1201 if (pinboard_shadow && shadow_x == old_x && shadow_y == old_y)
1202 return;
1204 gdk_window_clear_area_e(current_pinboard->window->window,
1205 shadow_x, shadow_y,
1206 SHADOW_SIZE + 1, SHADOW_SIZE + 1);
1209 pinboard_shadow = on;
1212 /* Called when dragging some pinboard icons finishes */
1213 void pinboard_move_icons(void)
1215 int x = shadow_x, y = shadow_y;
1216 PinIcon *pi = (PinIcon *) pinboard_drag_in_progress;
1217 int width, height;
1219 g_return_if_fail(pi != NULL);
1221 x += SHADOW_SIZE / 2;
1222 y += SHADOW_SIZE / 2;
1223 snap_to_grid(&x, &y);
1225 if (pi->x == x && pi->y == y)
1226 return;
1228 pi->x = x;
1229 pi->y = y;
1230 gdk_drawable_get_size(pi->win->window, &width, &height);
1231 offset_from_centre(pi, width, height, &x, &y);
1233 gdk_window_move(pi->win->window, x, y);
1235 pinboard_save();
1238 static void drag_leave(GtkWidget *widget,
1239 GdkDragContext *context,
1240 guint32 time,
1241 PinIcon *pi)
1243 pinboard_wink_item(NULL, FALSE);
1244 dnd_spring_abort();
1247 static gboolean bg_drag_leave(GtkWidget *widget,
1248 GdkDragContext *context,
1249 guint32 time,
1250 gpointer data)
1252 pinboard_set_shadow(FALSE);
1253 return TRUE;
1256 static gboolean bg_drag_motion(GtkWidget *widget,
1257 GdkDragContext *context,
1258 gint x,
1259 gint y,
1260 guint time,
1261 gpointer data)
1263 /* Dragging from the pinboard to the pinboard is not allowed */
1265 if (!provides(context, text_uri_list))
1266 return FALSE;
1268 pinboard_set_shadow(TRUE);
1270 gdk_drag_status(context,
1271 context->suggested_action == GDK_ACTION_ASK
1272 ? GDK_ACTION_LINK : context->suggested_action,
1273 time);
1274 return TRUE;
1277 static void drag_end(GtkWidget *widget,
1278 GdkDragContext *context,
1279 PinIcon *pi)
1281 pinboard_drag_in_progress = NULL;
1282 if (tmp_icon_selected)
1284 icon_select_only(NULL);
1285 tmp_icon_selected = FALSE;
1289 /* Something which affects all the icons has changed - reshape
1290 * and redraw all of them.
1292 static void reshape_all(void)
1294 GList *next;
1296 g_return_if_fail(current_pinboard != NULL);
1298 for (next = current_pinboard->icons; next; next = next->next)
1300 Icon *icon = (Icon *) next->data;
1301 pinboard_reshape_icon(icon);
1305 /* Turns off the pinboard. Does not call gtk_main_quit. */
1306 static void pinboard_clear(void)
1308 GList *next;
1310 g_return_if_fail(current_pinboard != NULL);
1312 next = current_pinboard->icons;
1313 while (next)
1315 PinIcon *pi = (PinIcon *) next->data;
1317 next = next->next;
1319 gtk_widget_destroy(pi->win);
1322 gtk_widget_destroy(current_pinboard->window);
1324 g_free(current_pinboard->name);
1325 g_free(current_pinboard);
1326 current_pinboard = NULL;
1328 number_of_windows--;
1331 static gpointer parent_class;
1333 static void pin_icon_destroy(Icon *icon)
1335 PinIcon *pi = (PinIcon *) icon;
1337 g_return_if_fail(pi->win != NULL);
1339 gtk_widget_destroy(pi->win);
1342 static void pin_icon_finalize(GObject *icon)
1344 PinIcon *pi = (PinIcon *) icon;
1346 if (pi->layout)
1348 g_object_unref(G_OBJECT(pi->layout));
1349 pi->layout = NULL;
1352 ((GObjectClass *) parent_class)->finalize(icon);
1355 static void pinboard_remove_items(void)
1357 g_return_if_fail(icon_selection != NULL);
1359 while (icon_selection)
1360 icon_destroy((Icon *) icon_selection->data);
1362 pinboard_save();
1365 static void pin_icon_update(Icon *icon)
1367 pinboard_reshape_icon(icon);
1368 pinboard_save();
1371 static gboolean pin_icon_same_group(Icon *icon, Icon *other)
1373 return IS_PIN_ICON(other);
1376 static void pin_icon_class_init(gpointer gclass, gpointer data)
1378 IconClass *icon = (IconClass *) gclass;
1380 parent_class = g_type_class_peek_parent(gclass);
1382 ((GObjectClass *) icon)->finalize = pin_icon_finalize;
1384 icon->destroy = pin_icon_destroy;
1385 icon->redraw = pinboard_reshape_icon;
1386 icon->update = pin_icon_update;
1387 icon->remove_items = pinboard_remove_items;
1388 icon->same_group = pin_icon_same_group;
1391 static void pin_icon_init(GTypeInstance *object, gpointer gclass)
1393 PinIcon *pi = (PinIcon *) object;
1395 pi->layout = NULL;
1398 static GType pin_icon_get_type(void)
1400 static GType type = 0;
1402 if (!type)
1404 static const GTypeInfo info =
1406 sizeof (PinIconClass),
1407 NULL, /* base_init */
1408 NULL, /* base_finalise */
1409 pin_icon_class_init,
1410 NULL, /* class_finalise */
1411 NULL, /* class_data */
1412 sizeof(PinIcon),
1413 0, /* n_preallocs */
1414 pin_icon_init
1417 type = g_type_register_static(icon_get_type(),
1418 "PinIcon", &info, 0);
1421 return type;
1424 static PinIcon *pin_icon_new(const char *pathname, const char *name)
1426 PinIcon *pi;
1427 Icon *icon;
1429 pi = g_object_new(pin_icon_get_type(), NULL);
1430 icon = (Icon *) pi;
1432 icon_set_path(icon, pathname, name);
1434 return pi;
1437 /* Called when the window widget is somehow destroyed */
1438 static void pin_icon_destroyed(PinIcon *pi)
1440 g_return_if_fail(pi->win != NULL);
1442 pi->win = NULL;
1444 pinboard_wink_item(NULL, FALSE);
1446 if (pinboard_drag_in_progress == (Icon *) pi)
1447 pinboard_drag_in_progress = NULL;
1449 if (current_pinboard)
1450 current_pinboard->icons =
1451 g_list_remove(current_pinboard->icons, pi);
1453 g_object_unref(pi);
1456 /* Set the tooltip */
1457 static void pin_icon_set_tip(PinIcon *pi)
1459 XMLwrapper *ai;
1460 xmlNode *node;
1461 Icon *icon = (Icon *) pi;
1463 g_return_if_fail(pi != NULL);
1465 ai = appinfo_get(icon->path, icon->item);
1467 if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
1469 guchar *str;
1470 str = xmlNodeListGetString(node->doc,
1471 node->xmlChildrenNode, 1);
1472 if (str)
1474 gtk_tooltips_set_tip(tooltips, pi->win, str, NULL);
1475 g_free(str);
1478 else
1479 gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
1481 if (ai)
1482 g_object_unref(ai);
1485 static void pinboard_show_menu(GdkEventButton *event, PinIcon *pi)
1487 int pos[3];
1489 pos[0] = event->x_root;
1490 pos[1] = event->y_root;
1491 pos[2] = 1;
1493 icon_prepare_menu((Icon *) pi);
1494 gtk_widget_show(icon_menu_remove_backdrop);
1495 gtk_widget_set_sensitive(GTK_BIN(icon_menu_remove_backdrop)->child,
1496 current_pinboard->backdrop != NULL);
1498 gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
1499 position_menu,
1500 (gpointer) pos, event->button, event->time);
1503 static void create_pinboard_window(Pinboard *pinboard)
1505 GtkWidget *win;
1507 g_return_if_fail(pinboard->window == NULL);
1509 win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1510 pinboard->window = win;
1511 pinboard->fixed = gtk_fixed_new();
1512 gtk_container_add(GTK_CONTAINER(win), pinboard->fixed);
1514 gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Pinboard", PROJECT);
1515 gtk_widget_set_name(win, "rox-pinboard");
1517 gtk_widget_set_size_request(win, screen_width, screen_height);
1518 gtk_widget_realize(win);
1519 gtk_window_move(GTK_WINDOW(win), 0, 0);
1520 make_panel_window(win);
1522 /* TODO: Use gdk function when it supports this type */
1524 GdkAtom desktop_type;
1526 desktop_type = gdk_atom_intern("_NET_WM_WINDOW_TYPE_DESKTOP",
1527 FALSE);
1528 gdk_property_change(win->window,
1529 gdk_atom_intern("_NET_WM_WINDOW_TYPE", FALSE),
1530 gdk_atom_intern("ATOM", FALSE), 32,
1531 GDK_PROP_MODE_REPLACE, (guchar *) &desktop_type, 1);
1534 gtk_widget_add_events(win,
1535 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1536 GDK_EXPOSURE_MASK);
1537 g_signal_connect(win, "button-press-event",
1538 G_CALLBACK(button_press_event), NULL);
1539 g_signal_connect(win, "button-release-event",
1540 G_CALLBACK(button_release_event), NULL);
1541 g_signal_connect(win, "expose_event", G_CALLBACK(bg_expose), NULL);
1543 /* Drag and drop handlers */
1544 drag_set_pinboard_dest(win);
1545 g_signal_connect(win, "drag_motion", G_CALLBACK(bg_drag_motion), NULL);
1546 g_signal_connect(win, "drag_leave", G_CALLBACK(bg_drag_leave), NULL);
1548 gtk_widget_show_all(win);
1549 gdk_window_lower(win->window);
1552 static void reload_backdrop(Pinboard *pinboard)
1554 GtkStyle *style;
1556 if (pinboard->backdrop && pinboard->backdrop_style == BACKDROP_PROGRAM)
1558 const char *argv[] = {NULL, "--backdrop", NULL};
1560 argv[0] = make_path(pinboard->backdrop, "AppRun")->str;
1562 /* Run the program. It'll send us a SOAP message and we'll
1563 * get back here with a different style and image.
1565 rox_spawn(NULL, argv);
1566 return;
1569 /* Note: Copying a style does not ref the pixmaps! */
1571 style = gtk_style_copy(gtk_widget_get_style(pinboard->window));
1573 style->bg_pixmap[GTK_STATE_NORMAL] = NULL;
1575 if (pinboard->backdrop)
1577 GdkPixbuf *pixbuf;
1578 GError *error = NULL;
1580 pixbuf = gdk_pixbuf_new_from_file(pinboard->backdrop, &error);
1581 if (error)
1583 delayed_error(_("Error loading backdrop image:\n%s\n"
1584 "Backdrop removed."),
1585 error->message);
1586 g_error_free(error);
1587 set_backdrop(pinboard, NULL, NULL, BACKDROP_NONE);
1589 else
1591 GdkPixmap *pixmap;
1593 if (pinboard->backdrop_style == BACKDROP_SCALE)
1595 GdkPixbuf *old = pixbuf;
1597 pixbuf = gdk_pixbuf_scale_simple(old,
1598 screen_width, screen_height,
1599 GDK_INTERP_HYPER);
1601 g_object_unref(old);
1603 else if (pinboard->backdrop_style == BACKDROP_CENTRE)
1605 GdkPixbuf *old = pixbuf;
1606 int x, y, width, height;
1608 width = gdk_pixbuf_get_width(pixbuf);
1609 height = gdk_pixbuf_get_height(pixbuf);
1611 pixbuf = gdk_pixbuf_new(
1612 gdk_pixbuf_get_colorspace(pixbuf), 0,
1613 8, screen_width, screen_height);
1614 gdk_pixbuf_fill(pixbuf, 0);
1616 x = (screen_width - width) / 2;
1617 y = (screen_height - height) / 2;
1618 x = MAX(x, 0);
1619 y = MAX(y, 0);
1621 gdk_pixbuf_composite(old, pixbuf,
1622 x, y,
1623 MIN(screen_width, width),
1624 MIN(screen_height, height),
1625 x, y, 1, 1,
1626 GDK_INTERP_NEAREST, 255);
1627 g_object_unref(old);
1630 gdk_pixbuf_render_pixmap_and_mask(pixbuf,
1631 &pixmap, NULL, 0);
1632 g_object_unref(pixbuf);
1634 style->bg_pixmap[GTK_STATE_NORMAL] = pixmap;
1638 gtk_widget_set_style(pinboard->window, style);
1640 g_object_unref(style);
1641 gtk_widget_queue_draw(pinboard->window);
1644 /* If image is NULL (normal case), (path,style) is both displayed and saved.
1645 * Otherwise, (image, style) is displayed and (path, BACKDROP_PROGRAM) saved.
1647 static void set_backdrop(Pinboard *pinboard, const gchar *image,
1648 const gchar *path, BackdropStyle style)
1650 gchar *tmp;
1652 g_return_if_fail(pinboard != NULL);
1654 g_free(pinboard->backdrop);
1655 pinboard->backdrop = NULL;
1657 /* Set the image to display... */
1658 tmp = g_strdup(image ? image : path);
1659 pinboard->backdrop = tmp;
1660 pinboard->backdrop_style = style;
1661 reload_backdrop(pinboard);
1662 g_free(tmp);
1664 /* Set the stored values... */
1665 if (path)
1666 pinboard->backdrop = g_strdup(path);
1667 else
1668 pinboard->backdrop = NULL;
1670 pinboard->backdrop_style = image ? BACKDROP_PROGRAM : style;
1671 pinboard_save();