1 /*
2  * ROX-Filer, filer for the ROX desktop project
3  * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17  * Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 /* pinboard.c - icons on the desktop background */
21 
22 #include "config.h"
23 
24 #include <ctype.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <errno.h>
28 #include <gtk/gtk.h>
29 #include <gdk/gdkx.h>
30 #include <stdlib.h>
31 #include <math.h>
32 #include <libxml/parser.h>
33 #include <signal.h>
34 
35 #include "global.h"
36 
37 #include "pinboard.h"
38 #include "main.h"
39 #include "dnd.h"
40 #include "pixmaps.h"
41 #include "type.h"
42 #include "choices.h"
43 #include "support.h"
44 #include "gui_support.h"
45 #include "options.h"
46 #include "diritem.h"
47 #include "bind.h"
48 #include "icon.h"
49 #include "run.h"
50 #include "appinfo.h"
51 #include "menu.h"
52 #include "xml.h"
53 #include "tasklist.h"
54 #include "panel.h"		/* For panel_mark_used() */
55 #include "wrapped.h"
56 #include "dropbox.h"
57 
58 static gboolean tmp_icon_selected = FALSE;		/* When dragging */
59 
60 static GtkWidget *set_backdrop_dialog = NULL;
61 
62 struct _Pinboard {
63 	guchar		*name;		/* Leaf name */
64 	GList		*icons;
65 	GtkStyle	*style;
66 
67 	gchar		*backdrop;	/* Pathname */
68 	BackdropStyle	backdrop_style;
69 	gint		to_backdrop_app; /* pipe FD, or -1 */
70 	gint		from_backdrop_app; /* pipe FD, or -1 */
71 	gint		input_tag;
72 	GString		*input_buffer;
73 
74 	GtkWidget	*window;	/* Screen-sized window */
75 	GtkWidget	*fixed;
76 	GdkGC		*shadow_gc;
77 };
78 
79 #define IS_PIN_ICON(obj) G_TYPE_CHECK_INSTANCE_TYPE((obj), pin_icon_get_type())
80 
81 typedef struct _PinIconClass PinIconClass;
82 typedef struct _PinIcon PinIcon;
83 
84 struct _PinIconClass {
85 	IconClass parent;
86 };
87 
88 struct _PinIcon {
89 	Icon		icon;
90 
91 	int		x, y;
92 	GtkWidget	*win;
93 	GtkWidget	*widget;	/* The drawing area for the icon */
94 	GtkWidget	*label;
95 };
96 
97 /* The number of pixels between the bottom of the image and the top
98  * of the text.
99  */
100 #define GAP 4
101 
102 /* The size of the border around the icon which is used when winking */
103 #define WINK_FRAME 2
104 
105 /* Grid sizes */
106 #define GRID_STEP_FINE   2
107 #define GRID_STEP_MED    16
108 #define GRID_STEP_COARSE 32
109 
110 /* Used in options */
111 #define CORNER_TOP_LEFT 0
112 #define CORNER_TOP_RIGHT 1
113 #define CORNER_BOTTOM_LEFT 2
114 #define CORNER_BOTTOM_RIGHT 3
115 
116 #define DIR_HORZ 0
117 #define DIR_VERT 1
118 
119 static PinIcon	*current_wink_icon = NULL;
120 static gint	wink_timeout;
121 
122 /* Used for the text colours (only) in the icons */
123 GdkColor pin_text_fg_col, pin_text_bg_col;
124 PangoFontDescription *pinboard_font = NULL; /* NULL => Gtk default */
125 
126 static GdkColor pin_text_shadow_col;
127 
128 Pinboard	*current_pinboard = NULL;
129 static gint	loading_pinboard = 0;		/* Non-zero => loading */
130 
131 /* The Icon that was used to start the current drag, if any */
132 Icon *pinboard_drag_in_progress = NULL;
133 
134 /* For selecting groups of icons */
135 static gboolean lasso_in_progress = FALSE;
136 static int lasso_rect_x1, lasso_rect_x2;
137 static int lasso_rect_y1, lasso_rect_y2;
138 
139 /* Tracking icon positions */
140 static GHashTable *placed_icons=NULL;
141 
142 Option o_pinboard_tasklist_per_workspace;
143 static Option o_pinboard_clamp_icons, o_pinboard_grid_step;
144 static Option o_pinboard_fg_colour, o_pinboard_bg_colour;
145 static Option o_pinboard_tasklist, o_forward_buttons_13;
146 static Option o_iconify_start, o_iconify_dir;
147 static Option o_label_font, o_pinboard_shadow_colour;
148 static Option o_pinboard_shadow_labels;
149 static Option o_blackbox_hack;
150 
151 static Option o_top_margin, o_bottom_margin, o_left_margin, o_right_margin;
152 static Option o_pinboard_image_scaling;
153 
154 /* Static prototypes */
155 static GType pin_icon_get_type(void);
156 static void set_size_and_style(PinIcon *pi);
157 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi);
158 static gint end_wink(gpointer data);
159 static gboolean button_release_event(GtkWidget *widget,
160 			    	     GdkEventButton *event,
161                             	     PinIcon *pi);
162 static gboolean enter_notify(GtkWidget *widget,
163 			     GdkEventCrossing *event,
164 			     PinIcon *pi);
165 static gint leave_notify(GtkWidget *widget,
166 			      GdkEventCrossing *event,
167 			      PinIcon *pi);
168 static gboolean button_press_event(GtkWidget *widget,
169 			    GdkEventButton *event,
170                             PinIcon *pi);
171 static gboolean scroll_event(GtkWidget *widget,
172                              GdkEventScroll *event);
173 static gint icon_motion_notify(GtkWidget *widget,
174 			       GdkEventMotion *event,
175 			       PinIcon *pi);
176 static const char *pin_from_file(gchar *line);
177 static void snap_to_grid(int *x, int *y);
178 static void offset_from_centre(PinIcon *pi, int *x, int *y);
179 static gboolean drag_motion(GtkWidget		*widget,
180                             GdkDragContext	*context,
181                             gint		x,
182                             gint		y,
183                             guint		time,
184 			    PinIcon		*pi);
185 static void drag_set_pinicon_dest(PinIcon *pi);
186 static void drag_leave(GtkWidget	*widget,
187                        GdkDragContext	*context,
188 		       guint32		time,
189 		       PinIcon		*pi);
190 static gboolean bg_drag_motion(GtkWidget	*widget,
191                                GdkDragContext	*context,
192                                gint		x,
193                                gint		y,
194                                guint		time,
195 			       gpointer		data);
196 static gboolean bg_drag_leave(GtkWidget		*widget,
197 			      GdkDragContext	*context,
198 			      guint32		time,
199 			      gpointer		data);
200 static gboolean bg_expose(GtkWidget *window,
201 			  GdkEventExpose *event, gpointer data);
202 static void drag_end(GtkWidget *widget,
203 			GdkDragContext *context,
204 			PinIcon *pi);
205 static void reshape_all(void);
206 static void pinboard_check_options(void);
207 static void pinboard_load_from_xml(xmlDocPtr doc);
208 static void pinboard_clear(void);
209 static void pinboard_save(void);
210 static PinIcon *pin_icon_new(const char *pathname, const char *name);
211 static void pin_icon_destroyed(PinIcon *pi);
212 static void pin_icon_set_tip(PinIcon *pi);
213 static void pinboard_show_menu(GdkEventButton *event, PinIcon *pi);
214 static void create_pinboard_window(Pinboard *pinboard);
215 static void reload_backdrop(Pinboard *pinboard,
216 			    const gchar *backdrop,
217 			    BackdropStyle backdrop_style);
218 static void pinboard_reshape_icon(Icon *icon);
219 static gint draw_wink(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi);
220 static void abandon_backdrop_app(Pinboard *pinboard);
221 static void drag_backdrop_dropped(GtkWidget	*drop_box,
222 				  const guchar	*path,
223 				  GtkWidget	*dialog);
224 static void backdrop_response(GtkWidget *dialog, gint response, gpointer data);
225 static void find_free_rect(Pinboard *pinboard, GdkRectangle *rect,
226 			   gboolean old, int start, int direction);
227 static void update_pinboard_font(void);
228 static void draw_lasso(void);
229 static gint lasso_motion(GtkWidget *widget, GdkEventMotion *event, gpointer d);
230 static void clear_backdrop(GtkWidget *drop_box, gpointer data);
231 static void radios_changed(Radios *radios, gpointer data);
232 static void update_radios(GtkWidget *dialog);
233 static void pinboard_set_backdrop_box(void);
234 
235 /****************************************************************
236  *			EXTERNAL INTERFACE			*
237  ****************************************************************/
238 
pinboard_init(void)239 void pinboard_init(void)
240 {
241 	option_add_string(&o_pinboard_fg_colour, "pinboard_fg_colour", "#fff");
242 	option_add_string(&o_pinboard_bg_colour, "pinboard_bg_colour", "#888");
243 	option_add_string(&o_pinboard_shadow_colour, "pinboard_shadow_colour",
244 			"#000");
245 	option_add_string(&o_label_font, "label_font", "");
246 	option_add_int(&o_pinboard_shadow_labels, "pinboard_shadow_labels", 1);
247 
248 	option_add_int(&o_pinboard_clamp_icons, "pinboard_clamp_icons", 1);
249 	option_add_int(&o_pinboard_grid_step, "pinboard_grid_step",
250 							GRID_STEP_COARSE);
251 	option_add_int(&o_pinboard_tasklist, "pinboard_tasklist", TRUE);
252 	option_add_int(&o_pinboard_tasklist_per_workspace, "pinboard_tasklist_per_workspace", FALSE);
253 	option_add_int(&o_forward_buttons_13, "pinboard_forward_buttons_13",
254 			FALSE);
255 
256 	option_add_int(&o_iconify_start, "iconify_start", CORNER_TOP_RIGHT);
257 	option_add_int(&o_iconify_dir, "iconify_dir", DIR_VERT);
258 
259 	option_add_int(&o_blackbox_hack, "blackbox_hack", FALSE);
260 
261 	option_add_int(&o_top_margin, "pinboard_top_margin", 0);
262 	option_add_int(&o_bottom_margin, "pinboard_bottom_margin", 0);
263 	option_add_int(&o_left_margin, "pinboard_left_margin", 0);
264 	option_add_int(&o_right_margin, "pinboard_right_margin", 0);
265 
266 	option_add_int(&o_pinboard_image_scaling, "pinboard_image_scaling", 0);
267 
268 	option_add_notify(pinboard_check_options);
269 
270 	gdk_color_parse(o_pinboard_fg_colour.value, &pin_text_fg_col);
271 	gdk_color_parse(o_pinboard_bg_colour.value, &pin_text_bg_col);
272 	gdk_color_parse(o_pinboard_shadow_colour.value, &pin_text_shadow_col);
273 	update_pinboard_font();
274 
275 	placed_icons=g_hash_table_new(g_str_hash, g_str_equal);
276 }
277 
278 /* Load 'pb_<pinboard>' config file from Choices (if it exists)
279  * and make it the current pinboard.
280  * Any existing pinned items are removed. You must call this
281  * at least once before using the pinboard. NULL disables the
282  * pinboard.
283  */
pinboard_activate(const gchar * name)284 void pinboard_activate(const gchar *name)
285 {
286 	Pinboard	*old_board = current_pinboard;
287 	guchar		*path, *slash;
288 
289 	/* Treat an empty name the same as NULL */
290 	if (name && !*name)
291 		name = NULL;
292 
293 	if (old_board)
294 		pinboard_clear();
295 
296 	if (!name)
297 	{
298 		if (number_of_windows < 1 && gtk_main_level() > 0)
299 			gtk_main_quit();
300 
301 		gdk_property_delete(gdk_get_default_root_window(),
302 				gdk_atom_intern("_XROOTPMAP_ID", FALSE));
303 		return;
304 	}
305 
306 	number_of_windows++;
307 
308 	slash = strchr(name, '/');
309 	if (slash)
310 	{
311 		if (access(name, F_OK))
312 			path = NULL;	/* File does not (yet) exist */
313 		else
314 			path = g_strdup(name);
315 	}
316 	else
317 	{
318 		guchar	*leaf;
319 
320 		leaf = g_strconcat("pb_", name, NULL);
321 		path = choices_find_xdg_path_load(leaf, PROJECT, SITE);
322 		g_free(leaf);
323 	}
324 
325 	current_pinboard = g_new(Pinboard, 1);
326 	current_pinboard->name = g_strdup(name);
327 	current_pinboard->icons = NULL;
328 	current_pinboard->window = NULL;
329 	current_pinboard->backdrop = NULL;
330 	current_pinboard->backdrop_style = BACKDROP_NONE;
331 	current_pinboard->to_backdrop_app = -1;
332 	current_pinboard->from_backdrop_app = -1;
333 	current_pinboard->input_tag = -1;
334 	current_pinboard->input_buffer = NULL;
335 
336 	create_pinboard_window(current_pinboard);
337 
338 	loading_pinboard++;
339 	if (path)
340 	{
341 		xmlDocPtr doc;
342 		doc = xmlParseFile(path);
343 		if (doc)
344 		{
345 			pinboard_load_from_xml(doc);
346 			xmlFreeDoc(doc);
347 			reload_backdrop(current_pinboard,
348 					current_pinboard->backdrop,
349 					current_pinboard->backdrop_style);
350 		}
351 		else
352 		{
353 			parse_file(path, pin_from_file);
354 			info_message(_("Your old pinboard file has been "
355 					"converted to the new XML format."));
356 			pinboard_save();
357 		}
358 		g_free(path);
359 	}
360 	else
361 		pinboard_pin(home_dir, "Home",
362 				4 + ICON_WIDTH / 2,
363 				4 + ICON_HEIGHT / 2,
364 				NULL);
365 	loading_pinboard--;
366 
367 	if (o_pinboard_tasklist.int_value)
368 		tasklist_set_active(TRUE);
369 }
370 
371 /* Return the window of the current pinboard, or NULL.
372  * Used to make sure lowering the panels doesn't lose them...
373  */
pinboard_get_window(void)374 GdkWindow *pinboard_get_window(void)
375 {
376 	if (current_pinboard)
377 		return current_pinboard->window->window;
378 	return NULL;
379 }
380 
pinboard_get_name(void)381 const char *pinboard_get_name(void)
382 {
383 	g_return_val_if_fail(current_pinboard != NULL, NULL);
384 
385 	return current_pinboard->name;
386 }
387 
388 /* Add widget to the pinboard. Caller is responsible for coping with pinboard
389  * being cleared.
390  */
pinboard_add_widget(GtkWidget * widget,const gchar * name)391 void pinboard_add_widget(GtkWidget *widget, const gchar *name)
392 {
393 	GtkRequisition req;
394 	GdkRectangle *rect=NULL;
395 	gboolean found=FALSE;
396 
397 	g_return_if_fail(current_pinboard != NULL);
398 
399 	gtk_fixed_put(GTK_FIXED(current_pinboard->fixed), widget, 0, 0);
400 
401 	gtk_widget_size_request(widget, &req);
402 
403 	if(name) {
404 		rect=g_hash_table_lookup(placed_icons, name);
405 		if(rect)
406 			found=TRUE;
407 	}
408 	/*printf("%s at %p %d\n", name? name: "(nil)", rect, found);*/
409 
410 	if(rect) {
411 		if(rect->width<req.width || rect->height<req.height) {
412 			found=FALSE;
413 		}
414 	} else {
415 		rect=g_new(GdkRectangle, 1);
416 		rect->width = req.width;
417 		rect->height = req.height;
418 	}
419 	/*printf("%s at %d,%d %d\n", name? name: "(nil)", rect->x,
420 	  rect->y, found);*/
421 	find_free_rect(current_pinboard, rect, found,
422 			o_iconify_start.int_value, o_iconify_dir.int_value);
423 	/*printf("%s at %d,%d %d\n", name? name: "(nil)", rect->x,
424 	  rect->y, found);*/
425 
426 	gtk_fixed_move(GTK_FIXED(current_pinboard->fixed),
427 			widget, rect->x, rect->y);
428 
429 	/* Store the new position (key and value are never freed) */
430 	if(name)
431 		g_hash_table_insert(placed_icons, g_strdup(name),
432 				    rect);
433 }
434 
pinboard_moved_widget(GtkWidget * widget,const gchar * name,int x,int y)435 void pinboard_moved_widget(GtkWidget *widget, const gchar *name,
436 			   int x, int y)
437 {
438 	GdkRectangle *rect;
439 
440 	if(!name)
441 		return;
442 	rect=g_hash_table_lookup(placed_icons, name);
443 	if(!rect)
444 		return;
445 
446 	rect->x=x;
447 	rect->y=y;
448 }
449 
450 /* Add a new icon to the background.
451  * 'path' should be an absolute pathname.
452  * 'x' and 'y' are the coordinates of the point in the middle of the text.
453  *   If they are negative, the icon is placed automatically.
454  *   The values then indicate where they should be added.
455  *   x: -1 means left, -2 means right
456  *   y: -1 means top, -2 means bottom
457  * 'name' is the name to use. If NULL then the leafname of path is used.
458  * If update is TRUE and there's already an icon for that path, it is updated.
459  */
pinboard_pin_with_args(const gchar * path,const gchar * name,int x,int y,const gchar * shortcut,const gchar * args,gboolean locked,gboolean update)460 void pinboard_pin_with_args(const gchar *path, const gchar *name,
461 				   int x, int y, const gchar *shortcut,
462 				   const gchar *args, gboolean locked, gboolean update)
463 {
464 	GtkWidget	*align, *vbox;
465 	GdkWindow	*events;
466 	PinIcon		*pi;
467 	Icon		*icon;
468 
469 	g_return_if_fail(path != NULL);
470 	g_return_if_fail(current_pinboard != NULL);
471 
472 	pi = NULL;
473 
474 	if (update)
475 	{
476 		GList		*iter;
477 
478 		for (iter = current_pinboard->icons; iter; iter = iter->next)
479 		{
480 			icon = (Icon *) iter->data;
481 			if (strcmp(icon->path, path) == 0)
482 			{
483 				pi = (PinIcon *) icon;
484 				break;
485 			}
486 		}
487 		if (pi)
488 		{
489 			if (x < 0)
490 				x = pi->x;
491 			if (y < 0)
492 				y = pi->y;
493 			icon_set_path(icon, path, name);
494 			goto out; /* The icon is already on the pinboard. */
495 		}
496 	}
497 
498 	pi = pin_icon_new(path, name);
499 	icon = (Icon *) pi;
500 
501 	/* This is a bit complicated...
502 	 *
503 	 * An icon needs to be a NO_WINDOW widget so that the image can
504 	 * blend with the background (A ParentRelative window also works, but
505 	 * is slow, causes the xfree86's memory consumption to grow without
506 	 * bound, and doesn't even get freed when the filer quits!).
507 	 *
508 	 * However, the icon also needs to have a window, so we get events
509 	 * delivered correctly. The solution is to float an InputOnly window
510 	 * over the icon. Since GtkButton works the same way, we just use
511 	 * that :-)
512 	 */
513 
514 	 /* Button takes the initial ref of Icon */
515 	pi->win = gtk_button_new();
516 	gtk_container_set_border_width(GTK_CONTAINER(pi->win), WINK_FRAME);
517 	g_signal_connect(pi->win, "expose-event", G_CALLBACK(draw_wink), pi);
518 	gtk_button_set_relief(GTK_BUTTON(pi->win), GTK_RELIEF_NONE);
519 
520 	vbox = gtk_vbox_new(FALSE, 0);
521 	gtk_container_add(GTK_CONTAINER(pi->win), vbox);
522 
523 	align = gtk_alignment_new(0.5, 0.5, 0, 0);
524 	pi->widget = gtk_hbox_new(FALSE, 0);	/* Placeholder */
525 	gtk_container_add(GTK_CONTAINER(align), pi->widget);
526 
527 	gtk_box_pack_start(GTK_BOX(vbox), align, FALSE, TRUE, 0);
528 	drag_set_pinicon_dest(pi);
529 	g_signal_connect(pi->win, "drag_data_get",
530 				G_CALLBACK(drag_data_get), NULL);
531 
532 	pi->label = wrapped_label_new(icon->item->leafname, 180);
533 	gtk_box_pack_start(GTK_BOX(vbox), pi->label, TRUE, TRUE, 0);
534 
535 	gtk_fixed_put(GTK_FIXED(current_pinboard->fixed), pi->win, 0, 0);
536 
537 	/* find free space for item if position is not given */
538 	if (x < 0 || y < 0)
539 	{
540 		GtkRequisition req;
541 		GdkRectangle rect;
542 		int placement = CORNER_TOP_LEFT;
543 
544 		switch (x)
545 		{
546 			case -1:
547 				if (y == -2)
548 					placement = CORNER_BOTTOM_LEFT;
549 				break;
550 			case -2:
551 				switch (y)
552 				{
553 					case -1:
554 						placement = CORNER_TOP_RIGHT;
555 						break;
556 					case -2:
557 						placement = CORNER_BOTTOM_RIGHT;
558 						break;
559 				}
560 				break;
561 		}
562 
563 		pinboard_reshape_icon((Icon *) pi);
564 		gtk_widget_size_request(pi->widget, &req);
565 		rect.width = req.width;
566 		rect.height = req.height;
567 		gtk_widget_size_request(pi->label, &req);
568 		rect.width = MAX(rect.width, req.width);
569 		rect.height += req.height;
570 		find_free_rect(current_pinboard, &rect, FALSE, placement, DIR_VERT);
571 		x = rect.x + rect.width/2;
572 		y = rect.y + rect.height/2;
573 	}
574 
575 	gtk_widget_show_all(pi->win);
576 
577 	events = GTK_BUTTON(pi->win)->event_window;
578 	gdk_window_set_events(events,
579 			GDK_EXPOSURE_MASK |
580 			GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
581 			GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
582 			GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
583 			GDK_BUTTON3_MOTION_MASK);
584 	g_signal_connect(pi->win, "enter-notify-event",
585 			G_CALLBACK(enter_notify), pi);
586 	g_signal_connect(pi->win, "leave-notify-event",
587 			G_CALLBACK(leave_notify), pi);
588 	g_signal_connect(pi->win, "button-press-event",
589 			G_CALLBACK(button_press_event), pi);
590 	g_signal_connect(pi->win, "button-release-event",
591 			G_CALLBACK(button_release_event), pi);
592 	g_signal_connect(pi->win, "motion-notify-event",
593 			G_CALLBACK(icon_motion_notify), pi);
594 	g_signal_connect(pi->win, "expose-event",
595 			G_CALLBACK(draw_icon), pi);
596 	g_signal_connect_swapped(pi->win, "style-set",
597 			G_CALLBACK(pinboard_reshape_icon), pi);
598 	g_signal_connect_swapped(pi->win, "destroy",
599 			G_CALLBACK(pin_icon_destroyed), pi);
600 
601 	current_pinboard->icons = g_list_prepend(current_pinboard->icons, pi);
602 
603 out:
604 	snap_to_grid(&x, &y);
605 
606 	pi->x = x;
607 	pi->y = y;
608 
609 	pinboard_reshape_icon((Icon *) pi);
610 
611 	gtk_widget_realize(pi->win);
612 
613 	pin_icon_set_tip(pi);
614 
615 	icon_set_shortcut(icon, shortcut);
616 	icon_set_arguments(icon, args);
617 	icon->locked = locked;
618 
619 	if (!loading_pinboard)
620 		pinboard_save();
621 }
622 
pinboard_pin(const gchar * path,const gchar * name,int x,int y,const gchar * shortcut)623 void pinboard_pin(const gchar *path, const gchar *name, int x, int y,
624 		  const gchar *shortcut)
625 {
626 	pinboard_pin_with_args(path, name, x, y, shortcut, NULL, FALSE, FALSE);
627 }
628 
629 /*
630  * Remove an icon from the background.  The first icon with a matching
631  * path is removed.  If name is not NULL then that also must match
632  */
pinboard_remove(const gchar * path,const gchar * name)633 gboolean pinboard_remove(const gchar *path, const gchar *name)
634 {
635 	GList *item;
636 	PinIcon		*pi;
637 	Icon		*icon;
638 
639 	g_return_val_if_fail(current_pinboard != NULL, FALSE);
640 
641 	for(item=current_pinboard->icons; item; item=g_list_next(item)) {
642 		pi=(PinIcon *) item->data;
643 		icon=(Icon *) pi;
644 
645 		if(strcmp(icon->path, path)!=0)
646 			continue;
647 
648 		if(name && strcmp(name, icon->item->leafname)!=0)
649 			continue;
650 
651 		icon->locked = FALSE;
652 		icon_destroy(icon);
653 
654 		pinboard_save();
655 
656 		return TRUE;
657 	}
658 
659 	return FALSE;
660 }
661 
662 /* Put a border around the icon, briefly.
663  * If icon is NULL then cancel any existing wink.
664  * The icon will automatically unhighlight unless timeout is FALSE,
665  * in which case you must call this function again (with NULL or another
666  * icon) to remove the highlight.
667  */
pinboard_wink_item(PinIcon * pi,gboolean timeout)668 static void pinboard_wink_item(PinIcon *pi, gboolean timeout)
669 {
670 	PinIcon *old = current_wink_icon;
671 
672 	if (old == pi)
673 		return;
674 
675 	current_wink_icon = pi;
676 
677 	if (old)
678 	{
679 		gtk_widget_queue_draw(old->win);
680 		gdk_window_process_updates(old->widget->window, TRUE);
681 
682 		if (wink_timeout != -1)
683 			g_source_remove(wink_timeout);
684 	}
685 
686 	if (pi)
687 	{
688 		gtk_widget_queue_draw(pi->win);
689 		gdk_window_process_updates(pi->widget->window, TRUE);
690 
691 		if (timeout)
692 			wink_timeout = g_timeout_add(300, end_wink, NULL);
693 		else
694 			wink_timeout = -1;
695 	}
696 }
697 
698 /* 'app' is saved as the new application to set the backdrop. It will then be
699  * run, and should communicate with the filer as described in the manual.
700  */
pinboard_set_backdrop_app(const gchar * app)701 void pinboard_set_backdrop_app(const gchar *app)
702 {
703 	XMLwrapper *ai;
704 	DirItem *item;
705 	gboolean can_set;
706 
707 	item = diritem_new("");
708 	diritem_restat(app, item, NULL);
709 	if (!(item->flags & ITEM_FLAG_APPDIR))
710 	{
711 		delayed_error(_("The backdrop handler must be an application "
712 				"directory. Drag an application directory "
713 				"into the Set Backdrop dialog box, or (for "
714 				"programmers) pass it to the SOAP "
715 				"SetBackdropApp method."));
716 		diritem_free(item);
717 		return;
718 	}
719 
720 	ai = appinfo_get(app, item);
721 	diritem_free(item);
722 
723 	can_set = ai && xml_get_section(ai, ROX_NS, "CanSetBackdrop") != NULL;
724 	if (ai)
725 		g_object_unref(ai);
726 
727 	if (can_set)
728 		pinboard_set_backdrop(app, BACKDROP_PROGRAM);
729 	else
730 		delayed_error(_("You can only set the backdrop to an image "
731 				"or to a program which knows how to "
732 				"manage ROX-Filer's backdrop.\n\n"
733 				"Programmers: the application's AppInfo.xml "
734 				"must contain the CanSetBackdrop element, as "
735 				"described in ROX-Filer's manual."));
736 }
737 
738 /* Open a dialog box allowing the user to set the backdrop */
pinboard_set_backdrop_box(void)739 static void pinboard_set_backdrop_box(void)
740 {
741 	GtkWidget *dialog, *frame, *label, *hbox;
742 	GtkBox *vbox;
743 	Radios *radios;
744 
745 	g_return_if_fail(current_pinboard != NULL);
746 
747 	if (set_backdrop_dialog)
748 		gtk_widget_destroy(set_backdrop_dialog);
749 
750 	dialog = gtk_dialog_new_with_buttons(_("Set backdrop"), NULL,
751 			GTK_DIALOG_NO_SEPARATOR,
752 			GTK_STOCK_CLOSE, GTK_RESPONSE_OK,
753 			NULL);
754 	set_backdrop_dialog = dialog;
755 	g_signal_connect(dialog, "destroy",
756 			G_CALLBACK(gtk_widget_destroyed), &set_backdrop_dialog);
757 	vbox = GTK_BOX(GTK_DIALOG(dialog)->vbox);
758 
759 	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
760 
761 	label = gtk_label_new(_("Choose a style and drag an image in:"));
762 	gtk_misc_set_padding(GTK_MISC(label), 4, 0);
763 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
764 	gtk_box_pack_start(vbox, label, TRUE, TRUE, 4);
765 
766 	/* The Centred, Scaled, Fit, Tiled radios... */
767 	hbox = gtk_hbox_new(TRUE, 2);
768 	gtk_box_pack_start(vbox, hbox, TRUE, TRUE, 4);
769 
770 	radios = radios_new(radios_changed, dialog);
771 	g_object_set_data(G_OBJECT(dialog), "rox-radios", radios);
772 	g_object_set_data(G_OBJECT(dialog), "rox-radios-hbox", hbox);
773 
774 	radios_add(radios, _("Centre the image without scaling it"),
775 			BACKDROP_CENTRE, _("Centre"));
776 	radios_add(radios, _("Scale the image to fit the backdrop area, "
777 			     "without distorting it"),
778 			BACKDROP_SCALE, _("Scale"));
779 	radios_add(radios, _("Scale the image to fit the backdrop area, "
780 			     "regardless of image dimensions - overscale"),
781 			BACKDROP_FIT, _("Fit"));
782 	radios_add(radios, _("Stretch the image to fill the backdrop area"),
783 			BACKDROP_STRETCH, _("Stretch"));
784 	radios_add(radios, _("Tile the image over the backdrop area"),
785 			BACKDROP_TILE, _("Tile"));
786 
787 	update_radios(dialog);
788 
789 	/* The drop area... */
790 	frame = drop_box_new(_("Drop an image here"));
791 	g_object_set_data(G_OBJECT(dialog), "rox-dropbox", frame);
792 
793 	radios_pack(radios, GTK_BOX(hbox));
794 	gtk_box_pack_start(vbox, frame, TRUE, TRUE, 4);
795 
796 	drop_box_set_path(DROP_BOX(frame), current_pinboard->backdrop);
797 
798 	g_signal_connect(frame, "path_dropped",
799 			G_CALLBACK(drag_backdrop_dropped), dialog);
800 	g_signal_connect(frame, "clear",
801 			G_CALLBACK(clear_backdrop), dialog);
802 
803 	g_signal_connect(dialog, "response",
804 			 G_CALLBACK(backdrop_response), NULL);
805 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
806 
807 	gtk_widget_show_all(dialog);
808 }
809 
810 /* Also used by tasklist.c */
draw_label_shadow(WrappedLabel * wl,GdkRegion * region)811 void draw_label_shadow(WrappedLabel *wl, GdkRegion *region)
812 {
813 	GtkWidget	*widget;
814 	gint		x, y;
815 
816 	if (!o_pinboard_shadow_labels.int_value)
817 		return;
818 
819 	gdk_gc_set_clip_region(current_pinboard->shadow_gc, region);
820 
821 	widget = GTK_WIDGET(wl);
822 
823 	y = widget->allocation.y - wl->y_off;
824 	x = widget->allocation.x - wl->x_off -
825 		((wl->text_width - widget->allocation.width) >> 1);
826 
827 	gdk_draw_layout(widget->window, current_pinboard->shadow_gc,
828 			x + 1, y + 1, wl->layout);
829 
830 	if (o_pinboard_shadow_labels.int_value > 1)
831 		gdk_draw_layout(widget->window, current_pinboard->shadow_gc,
832 				x + 2, y + 2, wl->layout);
833 
834 	gdk_gc_set_clip_region(current_pinboard->shadow_gc, NULL);
835 }
836 
837 /* Set and save (path, style) as the new backdrop.
838  * If style is BACKDROP_PROGRAM, the program is run to get the backdrop.
839  * Otherwise, the image is displayed now.
840  */
pinboard_set_backdrop(const gchar * path,BackdropStyle style)841 void pinboard_set_backdrop(const gchar *path, BackdropStyle style)
842 {
843 	g_return_if_fail((path == NULL && style == BACKDROP_NONE) ||
844 			 (path != NULL && style != BACKDROP_NONE));
845 
846 	if (!current_pinboard)
847 	{
848 		if (!path)
849 			return;
850 		pinboard_activate("Default");
851 		delayed_error(_("No pinboard was in use... "
852 			"the 'Default' pinboard has been selected. "
853 			"Use 'rox -p=Default' to turn it on in "
854 			"future."));
855 		g_return_if_fail(current_pinboard != NULL);
856 	}
857 
858 	/* We might have just run the old backdrop program and now
859 	 * we're going to set a new one! Seems a bit mean...
860 	 */
861 
862 	abandon_backdrop_app(current_pinboard);
863 
864 	g_free(current_pinboard->backdrop);
865 	current_pinboard->backdrop = g_strdup(path);
866 	current_pinboard->backdrop_style = style;
867 	reload_backdrop(current_pinboard,
868 			current_pinboard->backdrop,
869 			current_pinboard->backdrop_style);
870 
871 	pinboard_save();
872 
873 	if (set_backdrop_dialog)
874 	{
875 		DropBox *box = g_object_get_data(G_OBJECT(set_backdrop_dialog),
876 							  "rox-dropbox");
877 		g_return_if_fail(box != NULL);
878 		drop_box_set_path(box, current_pinboard->backdrop);
879 		update_radios(set_backdrop_dialog);
880 	}
881 }
882 
883 /* Called on xrandr screen resizes */
pinboard_update_size(void)884 void pinboard_update_size(void)
885 {
886 	int width, height;
887 
888 	if (!current_pinboard)
889 		return;
890 
891 	gtk_window_get_size(GTK_WINDOW(current_pinboard->window),
892 			&width, &height);
893 
894 	/* Only update the pinboard's size if the screen gets bigger,
895 	 * not smaller. Not sure what to do about icons that end up
896 	 * offscreen if the screen shrinks, but perhaps a good policy
897 	 * is to leave them there until the screen size is increased
898 	 * again rather than mess them around. */
899 	width = MAX(width, screen_width);
900 	height = MAX(height, screen_height);
901 
902 	gtk_widget_set_size_request(current_pinboard->window, width, height);
903 }
904 
905 /****************************************************************
906  *			INTERNAL FUNCTIONS			*
907  ****************************************************************/
908 
backdrop_response(GtkWidget * dialog,gint response,gpointer data)909 static void backdrop_response(GtkWidget *dialog, gint response, gpointer data)
910 {
911 	gtk_widget_destroy(dialog);
912 }
913 
clear_backdrop(GtkWidget * drop_box,gpointer data)914 static void clear_backdrop(GtkWidget *drop_box, gpointer data)
915 {
916 	pinboard_set_backdrop(NULL, BACKDROP_NONE);
917 }
918 
drag_backdrop_dropped(GtkWidget * drop_box,const guchar * path,GtkWidget * dialog)919 static void drag_backdrop_dropped(GtkWidget	*drop_box,
920 				  const guchar	*path,
921 				  GtkWidget	*dialog)
922 {
923 	struct stat info;
924 	Radios *radios;
925 
926 	radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
927 	g_return_if_fail(radios != NULL);
928 
929 	if (mc_stat(path, &info))
930 	{
931 		delayed_error(
932 			_("Can't access '%s':\n%s"), path,
933 			g_strerror(errno));
934 		return;
935 	}
936 
937 	if (S_ISDIR(info.st_mode))
938 	{
939 		/* Use this program to set the backdrop */
940 		pinboard_set_backdrop_app(path);
941 	}
942 	else if (S_ISREG(info.st_mode))
943 		pinboard_set_backdrop(path, radios_get_value(radios));
944 	else
945 		delayed_error(_("Only files (and certain applications) can be "
946 				"used to set the background image."));
947 }
948 
949 /* Do this in the idle loop so that we don't try to put an unmanaged
950  * pinboard behind a managed panel (crashes some WMs).
951  */
recreate_pinboard(gchar * name)952 static gboolean recreate_pinboard(gchar *name)
953 {
954 	pinboard_activate(name);
955 	g_free(name);
956 
957 	return FALSE;
958 }
959 
pinboard_check_options(void)960 static void pinboard_check_options(void)
961 {
962 	GdkColor	n_fg, n_bg, n_shadow;
963 
964 	gdk_color_parse(o_pinboard_fg_colour.value, &n_fg);
965 	gdk_color_parse(o_pinboard_bg_colour.value, &n_bg);
966 	gdk_color_parse(o_pinboard_shadow_colour.value, &n_shadow);
967 
968 	if (o_override_redirect.has_changed && current_pinboard)
969 	{
970 		gchar *name;
971 		name = g_strdup(current_pinboard->name);
972 		pinboard_activate(NULL);
973 		g_idle_add((GtkFunction) recreate_pinboard, name);
974 	}
975 
976 	tasklist_set_active(o_pinboard_tasklist.int_value && current_pinboard);
977 
978 	if (gdk_color_equal(&n_fg, &pin_text_fg_col) == 0 ||
979 		gdk_color_equal(&n_bg, &pin_text_bg_col) == 0 ||
980 		gdk_color_equal(&n_shadow, &pin_text_shadow_col) == 0 ||
981 		o_pinboard_shadow_labels.has_changed ||
982 		o_label_font.has_changed)
983 	{
984 		pin_text_fg_col = n_fg;
985 		pin_text_bg_col = n_bg;
986 		pin_text_shadow_col = n_shadow;
987 		update_pinboard_font();
988 
989 		if (current_pinboard)
990 		{
991 			GtkWidget *w = current_pinboard->window;
992 			GdkColormap *cm;
993 
994 			cm = gtk_widget_get_colormap(w);
995 
996 			gdk_colormap_alloc_color(cm, &n_bg, FALSE, TRUE);
997 			gtk_widget_modify_bg(w, GTK_STATE_NORMAL, &n_bg);
998 
999 			gdk_gc_set_rgb_fg_color(current_pinboard->shadow_gc,
1000 					&n_shadow);
1001 
1002 			abandon_backdrop_app(current_pinboard);
1003 			reload_backdrop(current_pinboard,
1004 					current_pinboard->backdrop,
1005 					current_pinboard->backdrop_style);
1006 
1007 			reshape_all();
1008 		}
1009 
1010 		tasklist_style_changed();
1011 	}
1012 }
1013 
end_wink(gpointer data)1014 static gint end_wink(gpointer data)
1015 {
1016 	pinboard_wink_item(NULL, FALSE);
1017 	return FALSE;
1018 }
1019 
1020 /* Sets the appearance from the options and updates the size request of
1021  * the image.
1022  */
set_size_and_style(PinIcon * pi)1023 static void set_size_and_style(PinIcon *pi)
1024 {
1025 	Icon		*icon = (Icon *) pi;
1026 	MaskedPixmap	*image = di_image(icon->item);
1027 	int		iwidth = image->width;
1028 	int		iheight = image->height;
1029 
1030 	gtk_widget_modify_fg(pi->label, GTK_STATE_PRELIGHT, &pin_text_fg_col);
1031 	gtk_widget_modify_bg(pi->label, GTK_STATE_PRELIGHT, &pin_text_bg_col);
1032 	gtk_widget_modify_fg(pi->label, GTK_STATE_NORMAL, &pin_text_fg_col);
1033 	gtk_widget_modify_bg(pi->label, GTK_STATE_NORMAL, &pin_text_bg_col);
1034 	widget_modify_font(pi->label, pinboard_font);
1035 
1036 	wrapped_label_set_text(WRAPPED_LABEL(pi->label), icon->item->leafname);
1037 
1038 	gtk_widget_set_size_request(pi->widget, iwidth, iheight);
1039 }
1040 
get_stock_icon(GtkWidget * widget,const char * stock_id)1041 static GdkPixbuf *get_stock_icon(GtkWidget *widget,
1042 				 const char *stock_id)
1043 {
1044 	GtkIconSet      *icon_set;
1045 	GdkPixbuf	*pixbuf;
1046 
1047 	icon_set = gtk_style_lookup_icon_set(widget->style,
1048 					     stock_id);
1049 	if (icon_set)
1050 	{
1051 		pixbuf = gtk_icon_set_render_icon(icon_set,
1052 						  widget->style,
1053 						  GTK_TEXT_DIR_LTR,
1054 						  GTK_STATE_NORMAL,
1055 						  mount_icon_size,
1056 						  NULL,
1057 						  NULL);
1058 	}
1059 	else
1060 	{
1061 		pixbuf=im_unknown->pixbuf;
1062 		g_object_ref(pixbuf);
1063 	}
1064 
1065 	return pixbuf;
1066 }
1067 
draw_icon(GtkWidget * widget,GdkEventExpose * event,PinIcon * pi)1068 static gint draw_icon(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi)
1069 {
1070 	static GtkWidgetClass *parent_class = NULL;
1071 	Icon		*icon = (Icon *) pi;
1072 	DirItem		*item = icon->item;
1073 	MaskedPixmap	*image = di_image(item);
1074 	int		iwidth = image->width;
1075 	int		iheight = image->height;
1076 	int		x, y;
1077 	GdkPixbuf	*pixbuf;
1078 
1079 	if (!parent_class)
1080 	{
1081 		gpointer c = ((GTypeInstance *) widget)->g_class;
1082 		parent_class = (GtkWidgetClass *) g_type_class_peek_parent(c);
1083 	}
1084 
1085 	x = pi->widget->allocation.x;
1086 	y = pi->widget->allocation.y;
1087 
1088 	gdk_gc_set_clip_region(pi->widget->style->black_gc, event->region);
1089 
1090 	pixbuf = icon->selected
1091 		? create_spotlight_pixbuf(image->pixbuf,
1092 			&pi->widget->style->base[GTK_STATE_SELECTED])
1093 		: image->pixbuf;
1094 
1095 	render_pixbuf(pixbuf,
1096 			pi->widget->window,
1097 			pi->widget->style->black_gc,
1098 			x, y, iwidth, iheight);
1099 
1100 	if (icon->selected)
1101 		g_object_unref(pixbuf);
1102 
1103 	if (item->flags & ITEM_FLAG_SYMLINK)
1104 	{
1105 		GdkPixbuf *emblem = get_stock_icon(pi->widget,
1106 						   ROX_STOCK_SYMLINK);
1107 		render_pixbuf(emblem, pi->widget->window,
1108 				pi->widget->style->black_gc,
1109 				x, y, -1, -1);
1110 		g_object_unref(emblem);
1111 	}
1112 	else if (item->flags & ITEM_FLAG_MOUNT_POINT)
1113 	{
1114 		GdkPixbuf *emblem = get_stock_icon(pi->widget,
1115 					     item->flags & ITEM_FLAG_MOUNTED
1116 						   ? ROX_STOCK_MOUNTED
1117 						   : ROX_STOCK_MOUNT);
1118 
1119 		render_pixbuf(emblem, pi->widget->window,
1120 				pi->widget->style->black_gc,
1121 				x, y, -1, -1);
1122 		g_object_unref(emblem);
1123 	}
1124 
1125 	gdk_gc_set_clip_region(pi->widget->style->black_gc, NULL);
1126 
1127 	if (icon->selected)
1128 	{
1129 		GtkStyle *style = pi->label->style;
1130 		GdkGC *gc = style->bg_gc[GTK_STATE_SELECTED];
1131 
1132 		gdk_gc_set_clip_region(gc, event->region);
1133 		gdk_draw_rectangle(pi->label->window, gc, TRUE,
1134 				pi->label->allocation.x,
1135 				pi->label->allocation.y,
1136 				pi->label->allocation.width,
1137 				pi->label->allocation.height);
1138 		gdk_gc_set_clip_region(gc, NULL);
1139 	}
1140 	else if (o_pinboard_shadow_labels.int_value)
1141 		draw_label_shadow((WrappedLabel *) pi->label, event->region);
1142 
1143 	/* Draw children */
1144 	gdk_gc_set_clip_region(pi->label->style->fg_gc[GTK_STATE_NORMAL],
1145 				event->region);
1146 	(parent_class->expose_event)(widget, event);
1147 	gdk_gc_set_clip_region(pi->label->style->fg_gc[GTK_STATE_NORMAL], NULL);
1148 
1149 	/* Stop the button effect */
1150 	return TRUE;
1151 }
1152 
draw_wink(GtkWidget * widget,GdkEventExpose * event,PinIcon * pi)1153 static gint draw_wink(GtkWidget *widget, GdkEventExpose *event, PinIcon *pi)
1154 {
1155 	gint x, y, width, height;
1156 
1157 	if (current_wink_icon != pi)
1158 		return FALSE;
1159 
1160 	x = widget->allocation.x;
1161 	y = widget->allocation.y;
1162 	width = widget->allocation.width;
1163 	height = widget->allocation.height;
1164 
1165 	gdk_draw_rectangle(widget->window,
1166 			pi->widget->style->white_gc,
1167 			FALSE,
1168 			x, y, width - 1, height - 1);
1169 	gdk_draw_rectangle(widget->window,
1170 			pi->widget->style->black_gc,
1171 			FALSE,
1172 			x + 1, y + 1, width - 3, height - 3);
1173 
1174 	return FALSE;
1175 }
1176 
enter_notify(GtkWidget * widget,GdkEventCrossing * event,PinIcon * pi)1177 static gboolean enter_notify(GtkWidget *widget,
1178 			     GdkEventCrossing *event,
1179 			     PinIcon *pi)
1180 {
1181 	icon_may_update((Icon *) pi);
1182 	pin_icon_set_tip(pi);
1183 	return TRUE;
1184 }
1185 
leave_notify(GtkWidget * widget,GdkEventCrossing * event,PinIcon * pi)1186 static gint leave_notify(GtkWidget *widget,
1187 			      GdkEventCrossing *event,
1188 			      PinIcon *pi)
1189 {
1190 	return TRUE;
1191 }
1192 
select_lasso(void)1193 static void select_lasso(void)
1194 {
1195 	GList *next;
1196 	int minx, miny, maxx, maxy;
1197 
1198 	g_return_if_fail(lasso_in_progress == TRUE);
1199 
1200 	minx = MIN(lasso_rect_x1, lasso_rect_x2);
1201 	miny = MIN(lasso_rect_y1, lasso_rect_y2);
1202 	maxx = MAX(lasso_rect_x1, lasso_rect_x2);
1203 	maxy = MAX(lasso_rect_y1, lasso_rect_y2);
1204 
1205 	for (next = current_pinboard->icons; next; next = next->next)
1206 	{
1207 		PinIcon *pi = (PinIcon *) next->data;
1208 		GtkAllocation *alloc = &pi->win->allocation;
1209 		int cx = alloc->x + alloc->width / 2;
1210 		int cy = alloc->y + alloc->height / 2;
1211 
1212 		if (cx > minx && cx < maxx && cy > miny && cy < maxy)
1213 			icon_set_selected((Icon *) pi, TRUE);
1214 	}
1215 }
1216 
cancel_lasso(void)1217 static void cancel_lasso(void)
1218 {
1219 	draw_lasso();
1220 	lasso_in_progress = FALSE;
1221 }
1222 
pinboard_lasso_box(int start_x,int start_y)1223 static void pinboard_lasso_box(int start_x, int start_y)
1224 {
1225 	if (lasso_in_progress)
1226 		cancel_lasso();
1227 	lasso_in_progress = TRUE;
1228 	lasso_rect_x1 = lasso_rect_x2 = start_x;
1229 	lasso_rect_y1 = lasso_rect_y2 = start_y;
1230 
1231 	draw_lasso();
1232 }
1233 
lasso_motion(GtkWidget * widget,GdkEventMotion * event,gpointer d)1234 static gint lasso_motion(GtkWidget *widget, GdkEventMotion *event, gpointer d)
1235 {
1236 	if (!lasso_in_progress)
1237 		return FALSE;
1238 
1239 	if (lasso_rect_x2 != event->x || lasso_rect_y2 != event->y)
1240 	{
1241 		draw_lasso();
1242 		lasso_rect_x2 = event->x;
1243 		lasso_rect_y2 = event->y;
1244 		draw_lasso();
1245 	}
1246 
1247 	return FALSE;
1248 }
1249 
1250 /* Mark the area of the screen covered by the lasso box for redraw */
draw_lasso(void)1251 static void draw_lasso(void)
1252 {
1253 	GdkRectangle area, edge;
1254 
1255 	if (!lasso_in_progress)
1256 		return;
1257 
1258 	area.x = MIN(lasso_rect_x1, lasso_rect_x2);
1259 	area.y = MIN(lasso_rect_y1, lasso_rect_y2);
1260 	area.width = ABS(lasso_rect_x1 - lasso_rect_x2);
1261 	area.height = ABS(lasso_rect_y1 - lasso_rect_y2);
1262 
1263 	edge.x = area.x;
1264 	edge.y = area.y;
1265 	edge.width = area.width;
1266 
1267 	edge.height = 2;		/* Top */
1268 	gdk_window_invalidate_rect(current_pinboard->window->window,
1269 				   &edge, TRUE);
1270 
1271 	edge.y += area.height - 2;	/* Bottom */
1272 	gdk_window_invalidate_rect(current_pinboard->window->window,
1273 				   &edge, TRUE);
1274 
1275 	edge.y = area.y;
1276 	edge.height = area.height;
1277 	edge.width = 2;			/* Left */
1278 	gdk_window_invalidate_rect(current_pinboard->window->window,
1279 				   &edge, TRUE);
1280 
1281 	edge.x += area.width - 2;	/* Right */
1282 	gdk_window_invalidate_rect(current_pinboard->window->window,
1283 				   &edge, TRUE);
1284 }
1285 
perform_action(PinIcon * pi,GdkEventButton * event)1286 static void perform_action(PinIcon *pi, GdkEventButton *event)
1287 {
1288 	BindAction	action;
1289 	Icon		*icon = (Icon *) pi;
1290 
1291 	action = bind_lookup_bev(pi ? BIND_PINBOARD_ICON : BIND_PINBOARD,
1292 				 event);
1293 
1294 	/* Actions that can happen with or without an icon */
1295 	switch (action)
1296 	{
1297 		case ACT_LASSO_CLEAR:
1298 			icon_select_only(NULL);
1299 			/* (no break) */
1300 		case ACT_LASSO_MODIFY:
1301 			pinboard_lasso_box(event->x, event->y);
1302 			return;
1303 		case ACT_CLEAR_SELECTION:
1304 			icon_select_only(NULL);
1305 			return;
1306 		case ACT_POPUP_MENU:
1307 			dnd_motion_ungrab();
1308 			pinboard_show_menu(event, pi);
1309 			return;
1310 		case ACT_IGNORE:
1311 			return;
1312 		default:
1313 			break;
1314 	}
1315 
1316 	g_return_if_fail(pi != NULL);
1317 
1318 	switch (action)
1319 	{
1320 		case ACT_OPEN_ITEM:
1321 			dnd_motion_ungrab();
1322 			pinboard_wink_item(pi, TRUE);
1323 			if (event->type == GDK_2BUTTON_PRESS)
1324 				icon_set_selected(icon, FALSE);
1325 			icon_run(icon);
1326 			break;
1327 		case ACT_EDIT_ITEM:
1328 			dnd_motion_ungrab();
1329 			pinboard_wink_item(pi, TRUE);
1330 			if (event->type == GDK_2BUTTON_PRESS)
1331 				icon_set_selected(icon, FALSE);
1332 			run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
1333 			break;
1334 		case ACT_PRIME_AND_SELECT:
1335 			if (!icon->selected)
1336 				icon_select_only(icon);
1337 			dnd_motion_start(MOTION_READY_FOR_DND);
1338 			break;
1339 		case ACT_PRIME_AND_TOGGLE:
1340 			icon_set_selected(icon, !icon->selected);
1341 			dnd_motion_start(MOTION_READY_FOR_DND);
1342 			break;
1343 		case ACT_PRIME_FOR_DND:
1344 			dnd_motion_start(MOTION_READY_FOR_DND);
1345 			break;
1346 		case ACT_TOGGLE_SELECTED:
1347 			icon_set_selected(icon, !icon->selected);
1348 			break;
1349 		case ACT_SELECT_EXCL:
1350 			icon_select_only(icon);
1351 			break;
1352 		default:
1353 			g_warning("Unsupported action : %d\n", action);
1354 			break;
1355 	}
1356 }
1357 
forward_to_root(GdkEventButton * event)1358 static void forward_to_root(GdkEventButton *event)
1359 {
1360 	XButtonEvent xev;
1361 
1362 	if (event->type == GDK_BUTTON_PRESS)
1363 	{
1364 		xev.type = ButtonPress;
1365 		if (!o_blackbox_hack.int_value)
1366 			XUngrabPointer(gdk_display, event->time);
1367 	}
1368 	else
1369 		xev.type = ButtonRelease;
1370 
1371 	xev.window = gdk_x11_get_default_root_xwindow();
1372 	xev.root =  xev.window;
1373 	xev.subwindow = None;
1374 	xev.time = event->time;
1375 	xev.x = event->x_root;	/* Needed for icewm */
1376 	xev.y = event->y_root;
1377 	xev.x_root = event->x_root;
1378 	xev.y_root = event->y_root;
1379 	xev.state = event->state;
1380 	xev.button = event->button;
1381 	xev.same_screen = True;
1382 
1383 	XSendEvent(gdk_display, xev.window, False,
1384 		ButtonPressMask | ButtonReleaseMask, (XEvent *) &xev);
1385 }
1386 
1387 #define FORWARDED_BUTTON(pi, b) ((b) == 2 || \
1388 	(((b) == 3 || (b) == 1) && o_forward_buttons_13.int_value && !pi))
1389 
1390 /* pi is NULL if this is a root event */
button_release_event(GtkWidget * widget,GdkEventButton * event,PinIcon * pi)1391 static gboolean button_release_event(GtkWidget *widget,
1392 			    	     GdkEventButton *event,
1393                             	     PinIcon *pi)
1394 {
1395 	if (FORWARDED_BUTTON(pi, event->button))
1396 		forward_to_root(event);
1397 	else if (dnd_motion_release(event))
1398 	{
1399 		if (motion_buttons_pressed == 0 && lasso_in_progress)
1400 		{
1401 			select_lasso();
1402 			cancel_lasso();
1403 		}
1404 		return FALSE;
1405 	}
1406 
1407 	perform_action(pi, event);
1408 
1409 	return TRUE;
1410 }
1411 
1412 /* pi is NULL if this is a root event */
button_press_event(GtkWidget * widget,GdkEventButton * event,PinIcon * pi)1413 static gboolean button_press_event(GtkWidget *widget,
1414 			    	   GdkEventButton *event,
1415                             	   PinIcon *pi)
1416 {
1417 	/* Just in case we've jumped in front of everything... */
1418 	gdk_window_lower(current_pinboard->window->window);
1419 
1420 	if (FORWARDED_BUTTON(pi, event->button))
1421 		forward_to_root(event);
1422 	else if (dnd_motion_press(widget, event))
1423 		perform_action(pi, event);
1424 
1425 	return TRUE;
1426 }
1427 
1428 /* Forward mouse scroll events as buttons 4 and 5 to the window manager
1429  * (for old window managers that don't catch the buttons themselves)
1430  */
scroll_event(GtkWidget * widget,GdkEventScroll * event)1431 static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event)
1432 {
1433 	XButtonEvent xev;
1434 
1435 	xev.type = ButtonPress;
1436 	xev.window = gdk_x11_get_default_root_xwindow();
1437 	xev.root =  xev.window;
1438 	xev.subwindow = None;
1439 	xev.time = event->time;
1440 	xev.x = event->x_root;	/* Needed for icewm */
1441 	xev.y = event->y_root;
1442 	xev.x_root = event->x_root;
1443 	xev.y_root = event->y_root;
1444 	xev.state = event->state;
1445 	xev.same_screen = True;
1446 
1447 	if (event->direction == GDK_SCROLL_UP)
1448 		xev.button = 4;
1449 	else if (event->direction == GDK_SCROLL_DOWN)
1450 		xev.button = 5;
1451 	else
1452 		return FALSE;
1453 
1454 	XSendEvent(gdk_display, xev.window, False,
1455 			ButtonPressMask, (XEvent *) &xev);
1456 
1457 	return TRUE;
1458 }
1459 
start_drag(PinIcon * pi,GdkEventMotion * event)1460 static void start_drag(PinIcon *pi, GdkEventMotion *event)
1461 {
1462 	GtkWidget *widget = pi->win;
1463 	Icon	  *icon = (Icon *) pi;
1464 
1465 	if (!icon->selected)
1466 	{
1467 		tmp_icon_selected = TRUE;
1468 		icon_select_only(icon);
1469 	}
1470 
1471 	g_return_if_fail(icon_selection != NULL);
1472 
1473 	pinboard_drag_in_progress = icon;
1474 
1475 	if (icon_selection->next == NULL)
1476 		drag_one_item(widget, event, icon->path, icon->item, NULL);
1477 	else
1478 	{
1479 		guchar	*uri_list;
1480 
1481 		uri_list = icon_create_uri_list();
1482 		drag_selection(widget, event, uri_list);
1483 		g_free(uri_list);
1484 	}
1485 }
1486 
1487 /* An icon is being dragged around... */
icon_motion_notify(GtkWidget * widget,GdkEventMotion * event,PinIcon * pi)1488 static gint icon_motion_notify(GtkWidget *widget,
1489 			       GdkEventMotion *event,
1490 			       PinIcon *pi)
1491 {
1492 	if (motion_state == MOTION_READY_FOR_DND)
1493 	{
1494 		if (dnd_motion_moved(event))
1495 			start_drag(pi, event);
1496 		return TRUE;
1497 	}
1498 
1499 	return FALSE;
1500 }
1501 
backdrop_from_xml(xmlNode * node)1502 static void backdrop_from_xml(xmlNode *node)
1503 {
1504 	gchar *style;
1505 
1506 	g_free(current_pinboard->backdrop);
1507 	current_pinboard->backdrop = xmlNodeGetContent(node);
1508 
1509 	style = xmlGetProp(node, "style");
1510 
1511 	if (style)
1512 	{
1513 		current_pinboard->backdrop_style =
1514 		  g_strcasecmp(style, "Tiled") == 0 ? BACKDROP_TILE :
1515 		  g_strcasecmp(style, "Scaled") == 0 ? BACKDROP_SCALE :
1516 		  g_strcasecmp(style, "Fit") == 0 ? BACKDROP_FIT :
1517 		  g_strcasecmp(style, "Stretched") == 0 ? BACKDROP_STRETCH :
1518 		  g_strcasecmp(style, "Centred") == 0 ? BACKDROP_CENTRE :
1519 		  g_strcasecmp(style, "Program") == 0 ? BACKDROP_PROGRAM :
1520 							     BACKDROP_NONE;
1521 		g_free(style);
1522 	}
1523 	else
1524 		current_pinboard->backdrop_style = BACKDROP_TILE;
1525 }
1526 
1527 /* Create one pinboard icon for each icon in the doc */
pinboard_load_from_xml(xmlDocPtr doc)1528 static void pinboard_load_from_xml(xmlDocPtr doc)
1529 {
1530 	xmlNodePtr node, root;
1531 	char	   *tmp, *label, *path, *shortcut, *args;
1532 	int	   x, y;
1533 	gboolean locked;
1534 
1535 	root = xmlDocGetRootElement(doc);
1536 
1537 	for (node = root->xmlChildrenNode; node; node = node->next)
1538 	{
1539 		if (node->type != XML_ELEMENT_NODE)
1540 			continue;
1541 		if (strcmp(node->name, "backdrop") == 0)
1542 		{
1543 			backdrop_from_xml(node);
1544 			continue;
1545 		}
1546 		if (strcmp(node->name, "icon") != 0)
1547 			continue;
1548 
1549 		tmp = xmlGetProp(node, "x");
1550 		if (!tmp)
1551 			continue;
1552 		x = atoi(tmp);
1553 		g_free(tmp);
1554 
1555 		tmp = xmlGetProp(node, "y");
1556 		if (!tmp)
1557 			continue;
1558 		y = atoi(tmp);
1559 		g_free(tmp);
1560 
1561 		label = xmlGetProp(node, "label");
1562 		if (!label)
1563 			label = g_strdup("<missing label>");
1564 		path = xmlNodeGetContent(node);
1565 		if (!path)
1566 			path = g_strdup("<missing path>");
1567 		shortcut = xmlGetProp(node, "shortcut");
1568 		args = xmlGetProp(node, "args");
1569 
1570 		tmp = xmlGetProp(node, "locked");
1571 		if (tmp)
1572 		{
1573 			locked = text_to_boolean(tmp, FALSE);
1574 			g_free(tmp);
1575 		}
1576 		else
1577 			locked = FALSE;
1578 
1579 		pinboard_pin_with_args(path, label, x, y, shortcut, args, locked, FALSE);
1580 
1581 		g_free(path);
1582 		g_free(label);
1583 		g_free(shortcut);
1584 		g_free(args);
1585 	}
1586 }
1587 
1588 /* Called for each line in the pinboard file while loading a new board.
1589  * Only used for old-format files when converting to XML.
1590  */
pin_from_file(gchar * line)1591 static const char *pin_from_file(gchar *line)
1592 {
1593 	gchar	*leaf = NULL;
1594 	int	x, y, n;
1595 
1596 	if (*line == '<')
1597 	{
1598 		gchar	*end;
1599 
1600 		end = strchr(line + 1, '>');
1601 		if (!end)
1602 			return _("Missing '>' in icon label");
1603 
1604 		leaf = g_strndup(line + 1, end - line - 1);
1605 
1606 		line = end + 1;
1607 
1608 		while (g_ascii_isspace(*line))
1609 			line++;
1610 		if (*line != ',')
1611 			return _("Missing ',' after icon label");
1612 		line++;
1613 	}
1614 
1615 	if (sscanf(line, " %d , %d , %n", &x, &y, &n) < 2)
1616 		return NULL;		/* Ignore format errors */
1617 
1618 	pinboard_pin(line + n, leaf, x, y, NULL);
1619 
1620 	g_free(leaf);
1621 
1622 	return NULL;
1623 }
1624 
1625 /* Write the current state of the pinboard to the current pinboard file */
pinboard_save(void)1626 static void pinboard_save(void)
1627 {
1628 	guchar	*save = NULL;
1629 	guchar	*save_new = NULL;
1630 	GList	*next;
1631 	xmlDocPtr doc = NULL;
1632 	xmlNodePtr root;
1633 
1634 	g_return_if_fail(current_pinboard != NULL);
1635 
1636 	if (strchr(current_pinboard->name, '/'))
1637 		save = g_strdup(current_pinboard->name);
1638 	else
1639 	{
1640 		guchar	*leaf;
1641 
1642 		leaf = g_strconcat("pb_", current_pinboard->name, NULL);
1643 		save = choices_find_xdg_path_save(leaf, PROJECT, SITE, TRUE);
1644 		g_free(leaf);
1645 	}
1646 
1647 	if (!save)
1648 		return;
1649 
1650 	doc = xmlNewDoc("1.0");
1651 	xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "pinboard", NULL));
1652 
1653 	root = xmlDocGetRootElement(doc);
1654 
1655 	if (current_pinboard->backdrop)
1656 	{
1657 		BackdropStyle style = current_pinboard->backdrop_style;
1658 		xmlNodePtr tree;
1659 
1660 		tree = xmlNewTextChild(root, NULL, "backdrop",
1661 				current_pinboard->backdrop);
1662 		xmlSetProp(tree, "style",
1663 			style == BACKDROP_TILE   ? "Tiled" :
1664 			style == BACKDROP_CENTRE ? "Centred" :
1665 			style == BACKDROP_SCALE  ? "Scaled" :
1666 			style == BACKDROP_FIT    ? "Fit" :
1667 			style == BACKDROP_STRETCH  ? "Stretched" :
1668 						   "Program");
1669 	}
1670 
1671 	for (next = current_pinboard->icons; next; next = next->next)
1672 	{
1673 		xmlNodePtr tree;
1674 		PinIcon *pi = (PinIcon *) next->data;
1675 		Icon	*icon = (Icon *) pi;
1676 		char *tmp;
1677 
1678 		tree = xmlNewTextChild(root, NULL, "icon", icon->src_path);
1679 
1680 		tmp = g_strdup_printf("%d", pi->x);
1681 		xmlSetProp(tree, "x", tmp);
1682 		g_free(tmp);
1683 
1684 		tmp = g_strdup_printf("%d", pi->y);
1685 		xmlSetProp(tree, "y", tmp);
1686 		g_free(tmp);
1687 
1688 		xmlSetProp(tree, "label", icon->item->leafname);
1689 		if (icon->shortcut)
1690 			xmlSetProp(tree, "shortcut", icon->shortcut);
1691 		if (icon->args)
1692 			xmlSetProp(tree, "args", icon->args);
1693 		if (icon->locked)
1694 			xmlSetProp(tree, "locked", "true");
1695 	}
1696 
1697 	save_new = g_strconcat(save, ".new", NULL);
1698 	if (save_xml_file(doc, save_new) || rename(save_new, save))
1699 		delayed_error(_("Error saving pinboard %s: %s"),
1700 				save, g_strerror(errno));
1701 	g_free(save_new);
1702 
1703 	g_free(save);
1704 	if (doc)
1705 		xmlFreeDoc(doc);
1706 }
1707 
snap_to_grid(int * x,int * y)1708 static void snap_to_grid(int *x, int *y)
1709 {
1710 	int step = o_pinboard_grid_step.int_value;
1711 
1712 	*x = ((*x + step / 2) / step) * step;
1713 	*y = ((*y + step / 2) / step) * step;
1714 }
1715 
1716 /* Convert (x,y) from a centre point to a window position */
offset_from_centre(PinIcon * pi,int * x,int * y)1717 static void offset_from_centre(PinIcon *pi, int *x, int *y)
1718 {
1719 	gboolean clamp = o_pinboard_clamp_icons.int_value;
1720 	GtkRequisition req;
1721 
1722 	gtk_widget_size_request(pi->win, &req);
1723 
1724 	*x -= req.width >> 1;
1725 	*y -= req.height >> 1;
1726 	*x = CLAMP(*x, 0, screen_width - (clamp ? req.width : 0));
1727 	*y = CLAMP(*y, 0, screen_height - (clamp ? req.height : 0));
1728 }
1729 
1730 /* Same as drag_set_dest(), but for pinboard icons */
drag_set_pinicon_dest(PinIcon * pi)1731 static void drag_set_pinicon_dest(PinIcon *pi)
1732 {
1733 	GtkObject	*obj = GTK_OBJECT(pi->win);
1734 
1735 	make_drop_target(pi->win, 0);
1736 
1737 	g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1738 	g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1739 	g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1740 }
1741 
1742 /* Called during the drag when the mouse is in a widget registered
1743  * as a drop target. Returns TRUE if we can accept the drop.
1744  */
drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,PinIcon * pi)1745 static gboolean drag_motion(GtkWidget		*widget,
1746                             GdkDragContext	*context,
1747                             gint		x,
1748                             gint		y,
1749                             guint		time,
1750 			    PinIcon		*pi)
1751 {
1752 	GdkDragAction	action = context->suggested_action;
1753 	const char	*type = NULL;
1754 	Icon		*icon = (Icon *) pi;
1755 	DirItem		*item = icon->item;
1756 
1757 	if (gtk_drag_get_source_widget(context) == widget)
1758 	{
1759 		g_dataset_set_data(context, "drop_dest_type",
1760 				   (gpointer) drop_dest_pass_through);
1761 		return FALSE;	/* Can't drag something to itself! */
1762 	}
1763 
1764 	if (icon->selected)
1765 		goto out;	/* Can't drag a selection to itself */
1766 
1767 	type = dnd_motion_item(context, &item);
1768 
1769 	if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value
1770 		&& type != drop_dest_prog)
1771 	{
1772 		guint state;
1773 		gdk_window_get_pointer(NULL, NULL, NULL, &state);
1774 		if (state & GDK_BUTTON1_MASK)
1775 			action = GDK_ACTION_ASK;
1776 	}
1777 
1778 	if (!item)
1779 		type = NULL;
1780 out:
1781 	/* We actually must pretend to accept the drop, even if the
1782 	 * directory isn't writeable, so that the spring-opening
1783 	 * thing works.
1784 	 */
1785 
1786 	/* Don't allow drops to non-writeable directories */
1787 	if (o_dnd_spring_open.int_value == FALSE &&
1788 			type == drop_dest_dir &&
1789 			access(icon->path, W_OK) != 0)
1790 	{
1791 		type = NULL;
1792 	}
1793 
1794 	g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1795 	if (type)
1796 	{
1797 		gdk_drag_status(context, action, time);
1798 		g_dataset_set_data_full(context, "drop_dest_path",
1799 				g_strdup(icon->path), g_free);
1800 		if (type == drop_dest_dir)
1801 			dnd_spring_load(context, NULL);
1802 
1803 		pinboard_wink_item(pi, FALSE);
1804 	}
1805 	else
1806 		gdk_drag_status(context, 0, time);
1807 
1808 	/* Always return TRUE to stop the pinboard getting the events */
1809 	return TRUE;
1810 }
1811 
1812 static gboolean pinboard_shadow = FALSE;
1813 static gint shadow_x, shadow_y;
1814 #define SHADOW_SIZE (ICON_WIDTH)
1815 
bg_expose(GtkWidget * widget,GdkEventExpose * event,gpointer data)1816 static gboolean bg_expose(GtkWidget *widget,
1817 			  GdkEventExpose *event, gpointer data)
1818 {
1819 	GdkRectangle clipbox;
1820 	gpointer gclass = ((GTypeInstance *) widget)->g_class;
1821 	gboolean double_buffer;
1822 
1823 	gdk_gc_set_clip_region(widget->style->white_gc, event->region);
1824 	gdk_gc_set_clip_region(widget->style->black_gc, event->region);
1825 
1826 	gdk_region_get_clipbox(event->region, &clipbox);
1827 
1828 	double_buffer = (clipbox.width * clipbox.height) < 20000;
1829 	if (double_buffer)
1830 		gdk_window_begin_paint_region(widget->window, event->region);
1831 
1832 	/* Clear the area to the background image */
1833 	{
1834 		GtkStyle *style = current_pinboard->window->style;
1835 		GdkGC *gc = style->bg_gc[GTK_STATE_NORMAL];
1836 
1837 		gdk_gc_set_clip_region(gc, event->region);
1838 		if (style->bg_pixmap[GTK_STATE_NORMAL])
1839 		{
1840 			gdk_gc_set_ts_origin(gc, 0, 0);
1841 			gdk_gc_set_fill(gc, GDK_TILED);
1842 			gdk_gc_set_tile(gc, style->bg_pixmap[GTK_STATE_NORMAL]);
1843 		}
1844 
1845 		gdk_draw_rectangle(current_pinboard->window->window, gc, TRUE,
1846 				   clipbox.x, clipbox.y,
1847 				   clipbox.width, clipbox.height);
1848 		if (style->bg_pixmap[GTK_STATE_NORMAL])
1849 			gdk_gc_set_fill(gc, GDK_SOLID);
1850 
1851 		gdk_gc_set_clip_region(gc, NULL);
1852 	}
1853 
1854 	if (pinboard_shadow)
1855 	{
1856 		gdk_draw_rectangle(widget->window,
1857 				widget->style->white_gc, FALSE,
1858 				shadow_x, shadow_y,
1859 				SHADOW_SIZE, SHADOW_SIZE);
1860 		gdk_draw_rectangle(widget->window,
1861 				widget->style->black_gc, FALSE,
1862 				shadow_x + 1, shadow_y + 1,
1863 				SHADOW_SIZE - 2, SHADOW_SIZE - 2);
1864 	}
1865 
1866 	if (lasso_in_progress)
1867 	{
1868 		GdkRectangle area;
1869 
1870 		area.x = MIN(lasso_rect_x1, lasso_rect_x2);
1871 		area.y = MIN(lasso_rect_y1, lasso_rect_y2);
1872 		area.width = ABS(lasso_rect_x1 - lasso_rect_x2);
1873 		area.height = ABS(lasso_rect_y1 - lasso_rect_y2);
1874 
1875 		if (area.width > 4 && area.height > 4)
1876 		{
1877 			gdk_draw_rectangle(widget->window,
1878 					widget->style->white_gc, FALSE,
1879 					area.x, area.y,
1880 					area.width - 1, area.height - 1);
1881 			gdk_draw_rectangle(widget->window,
1882 					widget->style->black_gc, FALSE,
1883 					area.x + 1, area.y + 1,
1884 					area.width - 3, area.height - 3);
1885 		}
1886 	}
1887 
1888 	gdk_gc_set_clip_region(widget->style->white_gc, NULL);
1889 	gdk_gc_set_clip_region(widget->style->black_gc, NULL);
1890 
1891 	((GtkWidgetClass *) gclass)->expose_event(widget, event);
1892 
1893 	if (double_buffer)
1894 		gdk_window_end_paint(widget->window);
1895 
1896 	return TRUE;
1897 }
1898 
1899 /* Draw a 'shadow' under an icon being dragged, showing where
1900  * it will land.
1901  */
pinboard_set_shadow(gboolean on)1902 static void pinboard_set_shadow(gboolean on)
1903 {
1904 	GdkRectangle area;
1905 
1906 	if (pinboard_shadow)
1907 	{
1908 		area.x = shadow_x;
1909 		area.y = shadow_y;
1910 		area.width = SHADOW_SIZE + 1;
1911 		area.height = SHADOW_SIZE + 1;
1912 
1913 		gdk_window_invalidate_rect(current_pinboard->window->window,
1914 					&area, TRUE);
1915 	}
1916 
1917 	if (on)
1918 	{
1919 		int	old_x = shadow_x, old_y = shadow_y;
1920 
1921 		gdk_window_get_pointer(current_pinboard->fixed->window,
1922 					&shadow_x, &shadow_y, NULL);
1923 		snap_to_grid(&shadow_x, &shadow_y);
1924 		shadow_x -= SHADOW_SIZE / 2;
1925 		shadow_y -= SHADOW_SIZE / 2;
1926 
1927 
1928 		if (pinboard_shadow && shadow_x == old_x && shadow_y == old_y)
1929 			return;
1930 
1931 		area.x = shadow_x;
1932 		area.y = shadow_y;
1933 		area.width = SHADOW_SIZE + 1;
1934 		area.height = SHADOW_SIZE + 1;
1935 
1936 		gdk_window_invalidate_rect(current_pinboard->window->window,
1937 					&area, TRUE);
1938 	}
1939 
1940 	pinboard_shadow = on;
1941 }
1942 
1943 /* Called when dragging some pinboard icons finishes */
pinboard_move_icons(void)1944 void pinboard_move_icons(void)
1945 {
1946 	int	x = shadow_x, y = shadow_y;
1947 	PinIcon	*pi = (PinIcon *) pinboard_drag_in_progress;
1948 	int	width, height;
1949 	int	dx, dy;
1950 	GList	*next;
1951 
1952 	g_return_if_fail(pi != NULL);
1953 
1954 	x += SHADOW_SIZE / 2;
1955 	y += SHADOW_SIZE / 2;
1956 	snap_to_grid(&x, &y);
1957 
1958 	if (pi->x == x && pi->y == y)
1959 		return;
1960 
1961 	/* Find out how much the dragged icon moved (after snapping).
1962 	 * Move all selected icons by the same amount.
1963 	 */
1964 	dx = x - pi->x;
1965 	dy = y - pi->y;
1966 
1967 	/* Move the other selected icons to keep the same relative
1968 	 * position.
1969 	 */
1970 	for (next = icon_selection; next; next = next->next)
1971 	{
1972 		PinIcon *pi = (PinIcon *) next->data;
1973 		int	nx, ny;
1974 
1975 		g_return_if_fail(IS_PIN_ICON(pi));
1976 
1977 		pi->x += dx;
1978 		pi->y += dy;
1979 		nx = pi->x;
1980 		ny = pi->y;
1981 
1982 		gdk_drawable_get_size(pi->win->window, &width, &height);
1983 		offset_from_centre(pi, &nx, &ny);
1984 
1985 		fixed_move_fast(GTK_FIXED(current_pinboard->fixed),
1986 				pi->win, nx, ny);
1987 	}
1988 
1989 	pinboard_save();
1990 }
1991 
drag_leave(GtkWidget * widget,GdkDragContext * context,guint32 time,PinIcon * pi)1992 static void drag_leave(GtkWidget	*widget,
1993                        GdkDragContext	*context,
1994 		       guint32		time,
1995 		       PinIcon		*pi)
1996 {
1997 	pinboard_wink_item(NULL, FALSE);
1998 	dnd_spring_abort();
1999 }
2000 
bg_drag_leave(GtkWidget * widget,GdkDragContext * context,guint32 time,gpointer data)2001 static gboolean bg_drag_leave(GtkWidget		*widget,
2002 			      GdkDragContext	*context,
2003 			      guint32		time,
2004 			      gpointer		data)
2005 {
2006 	pinboard_set_shadow(FALSE);
2007 	return TRUE;
2008 }
2009 
bg_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,gpointer data)2010 static gboolean bg_drag_motion(GtkWidget	*widget,
2011                                GdkDragContext	*context,
2012                                gint		x,
2013                                gint		y,
2014                                guint		time,
2015 			       gpointer		data)
2016 {
2017 	/* Dragging from the pinboard to the pinboard is not allowed */
2018 
2019 	if (!provides(context, text_uri_list))
2020 		return FALSE;
2021 
2022 	pinboard_set_shadow(TRUE);
2023 
2024 	gdk_drag_status(context,
2025 			context->suggested_action == GDK_ACTION_ASK
2026 				? GDK_ACTION_LINK : context->suggested_action,
2027 			time);
2028 	return TRUE;
2029 }
2030 
drag_end(GtkWidget * widget,GdkDragContext * context,PinIcon * pi)2031 static void drag_end(GtkWidget *widget,
2032 		     GdkDragContext *context,
2033 		     PinIcon *pi)
2034 {
2035 	pinboard_drag_in_progress = NULL;
2036 	if (tmp_icon_selected)
2037 	{
2038 		icon_select_only(NULL);
2039 		tmp_icon_selected = FALSE;
2040 	}
2041 }
2042 
2043 /* Something which affects all the icons has changed - reshape
2044  * and redraw all of them.
2045  */
reshape_all(void)2046 static void reshape_all(void)
2047 {
2048 	GList	*next;
2049 
2050 	g_return_if_fail(current_pinboard != NULL);
2051 
2052 	for (next = current_pinboard->icons; next; next = next->next)
2053 	{
2054 		Icon *icon = (Icon *) next->data;
2055 		pinboard_reshape_icon(icon);
2056 	}
2057 }
2058 
2059 /* Turns off the pinboard. Does not call gtk_main_quit. */
pinboard_clear(void)2060 static void pinboard_clear(void)
2061 {
2062 	GList	*next;
2063 
2064 	g_return_if_fail(current_pinboard != NULL);
2065 
2066 	tasklist_set_active(FALSE);
2067 
2068 	next = current_pinboard->icons;
2069 	while (next)
2070 	{
2071 		PinIcon	*pi = (PinIcon *) next->data;
2072 
2073 		next = next->next;
2074 
2075 		gtk_widget_destroy(pi->win);
2076 	}
2077 
2078 	gtk_widget_destroy(current_pinboard->window);
2079 
2080 	abandon_backdrop_app(current_pinboard);
2081 
2082 	g_object_unref(current_pinboard->shadow_gc);
2083 	current_pinboard->shadow_gc = NULL;
2084 
2085 	g_free(current_pinboard->name);
2086 	null_g_free(&current_pinboard);
2087 
2088 	number_of_windows--;
2089 }
2090 
2091 static gpointer parent_class;
2092 
pin_icon_destroy(Icon * icon)2093 static void pin_icon_destroy(Icon *icon)
2094 {
2095 	PinIcon *pi = (PinIcon *) icon;
2096 
2097 	g_return_if_fail(pi->win != NULL);
2098 
2099 	gtk_widget_hide(pi->win);	/* Stops flicker - stupid GtkFixed! */
2100 	gtk_widget_destroy(pi->win);
2101 }
2102 
pinboard_remove_items(void)2103 static void pinboard_remove_items(void)
2104 {
2105 	g_return_if_fail(icon_selection != NULL);
2106 
2107 	while (icon_selection)
2108 		icon_destroy((Icon *) icon_selection->data);
2109 
2110 	pinboard_save();
2111 }
2112 
pin_icon_update(Icon * icon)2113 static void pin_icon_update(Icon *icon)
2114 {
2115 	pinboard_reshape_icon(icon);
2116 	pinboard_save();
2117 }
2118 
pin_icon_same_group(Icon * icon,Icon * other)2119 static gboolean pin_icon_same_group(Icon *icon, Icon *other)
2120 {
2121 	return IS_PIN_ICON(other);
2122 }
2123 
pin_wink_icon(Icon * icon)2124 static void pin_wink_icon(Icon *icon)
2125 {
2126 	pinboard_wink_item((PinIcon *) icon, TRUE);
2127 }
2128 
pin_icon_class_init(gpointer gclass,gpointer data)2129 static void pin_icon_class_init(gpointer gclass, gpointer data)
2130 {
2131 	IconClass *icon = (IconClass *) gclass;
2132 
2133 	parent_class = g_type_class_peek_parent(gclass);
2134 
2135 	icon->destroy = pin_icon_destroy;
2136 	icon->redraw = pinboard_reshape_icon;
2137 	icon->update = pin_icon_update;
2138 	icon->wink = pin_wink_icon;
2139 	icon->remove_items = pinboard_remove_items;
2140 	icon->same_group = pin_icon_same_group;
2141 }
2142 
pin_icon_init(GTypeInstance * object,gpointer gclass)2143 static void pin_icon_init(GTypeInstance *object, gpointer gclass)
2144 {
2145 }
2146 
pin_icon_get_type(void)2147 static GType pin_icon_get_type(void)
2148 {
2149 	static GType type = 0;
2150 
2151 	if (!type)
2152 	{
2153 		static const GTypeInfo info =
2154 		{
2155 			sizeof (PinIconClass),
2156 			NULL,			/* base_init */
2157 			NULL,			/* base_finalise */
2158 			pin_icon_class_init,
2159 			NULL,			/* class_finalise */
2160 			NULL,			/* class_data */
2161 			sizeof(PinIcon),
2162 			0,			/* n_preallocs */
2163 			pin_icon_init
2164 		};
2165 
2166 		type = g_type_register_static(icon_get_type(),
2167 						"PinIcon", &info, 0);
2168 	}
2169 
2170 	return type;
2171 }
2172 
pin_icon_new(const char * pathname,const char * name)2173 static PinIcon *pin_icon_new(const char *pathname, const char *name)
2174 {
2175 	PinIcon *pi;
2176 	Icon	  *icon;
2177 
2178 	pi = g_object_new(pin_icon_get_type(), NULL);
2179 	icon = (Icon *) pi;
2180 
2181 	icon_set_path(icon, pathname, name);
2182 
2183 	return pi;
2184 }
2185 
2186 /* Called when the window widget is somehow destroyed */
pin_icon_destroyed(PinIcon * pi)2187 static void pin_icon_destroyed(PinIcon *pi)
2188 {
2189 	g_return_if_fail(pi->win != NULL);
2190 
2191 	pi->win = NULL;
2192 
2193 	pinboard_wink_item(NULL, FALSE);
2194 
2195 	if (pinboard_drag_in_progress == (Icon *) pi)
2196 		pinboard_drag_in_progress = NULL;
2197 
2198 	if (current_pinboard)
2199 		current_pinboard->icons =
2200 			g_list_remove(current_pinboard->icons, pi);
2201 
2202 	g_object_unref(pi);
2203 }
2204 
2205 /* Set the tooltip */
pin_icon_set_tip(PinIcon * pi)2206 static void pin_icon_set_tip(PinIcon *pi)
2207 {
2208 	XMLwrapper	*ai;
2209 	xmlNode 	*node;
2210 	Icon		*icon = (Icon *) pi;
2211 
2212 	g_return_if_fail(pi != NULL);
2213 
2214 	ai = appinfo_get(icon->path, icon->item);
2215 
2216 	if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
2217 	{
2218 		guchar *str;
2219 		str = xmlNodeListGetString(node->doc,
2220 				node->xmlChildrenNode, 1);
2221 		if (str)
2222 		{
2223 			gtk_tooltips_set_tip(tooltips, pi->win, str, NULL);
2224 			g_free(str);
2225 		}
2226 	}
2227 	else
2228 		gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
2229 
2230 	if (ai)
2231 		g_object_unref(ai);
2232 }
2233 
pinboard_show_menu(GdkEventButton * event,PinIcon * pi)2234 static void pinboard_show_menu(GdkEventButton *event, PinIcon *pi)
2235 {
2236 	GtkWidget	*option_item;
2237 	GtkWidget	*new_panel_item;
2238 	int		pos[3];
2239 	GList		*list;
2240 
2241 	pos[0] = event->x_root;
2242 	pos[1] = event->y_root;
2243 
2244 	option_item = gtk_image_menu_item_new_with_label(_("Backdrop..."));
2245 	g_signal_connect(option_item, "activate",
2246 			 G_CALLBACK(pinboard_set_backdrop_box), NULL);
2247 	new_panel_item = gtk_image_menu_item_new_with_label(_("Add Panel"));
2248 	add_stock_to_menu_item(new_panel_item, GTK_STOCK_ADD);
2249 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(new_panel_item),
2250 			panel_new_panel_submenu());
2251 	icon_prepare_menu((Icon *) pi, option_item, new_panel_item, NULL);
2252 
2253 	list = gtk_container_get_children(GTK_CONTAINER(icon_menu));
2254 	pos[2] = g_list_length(list) - 6;
2255 	g_list_free(list);
2256 
2257 	gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
2258 			position_menu,
2259 			(gpointer) pos, event->button, event->time);
2260 }
2261 
create_pinboard_window(Pinboard * pinboard)2262 static void create_pinboard_window(Pinboard *pinboard)
2263 {
2264 	GtkWidget	*win;
2265 
2266 	g_return_if_fail(pinboard->window == NULL);
2267 
2268 	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2269 	gtk_widget_set_style(win, gtk_widget_get_default_style());
2270 
2271 	gtk_widget_set_double_buffered(win, FALSE);
2272 	gtk_widget_set_app_paintable(win, TRUE);
2273 	gtk_widget_set_name(win, "rox-pinboard");
2274 	pinboard->window = win;
2275 	pinboard->fixed = gtk_fixed_new();
2276 	gtk_container_add(GTK_CONTAINER(win), pinboard->fixed);
2277 
2278 	gtk_window_set_wmclass(GTK_WINDOW(win), "ROX-Pinboard", PROJECT);
2279 
2280 	gtk_widget_set_size_request(win, screen_width, screen_height);
2281 	gtk_widget_realize(win);
2282 	gtk_window_move(GTK_WINDOW(win), 0, 0);
2283 	make_panel_window(win);
2284 
2285 	/* TODO: Use gdk function when it supports this type */
2286 	{
2287 		GdkAtom desktop_type;
2288 
2289 		desktop_type = gdk_atom_intern("_NET_WM_WINDOW_TYPE_DESKTOP",
2290 						FALSE);
2291 		gdk_property_change(win->window,
2292 			gdk_atom_intern("_NET_WM_WINDOW_TYPE", FALSE),
2293 			gdk_atom_intern("ATOM", FALSE), 32,
2294 			GDK_PROP_MODE_REPLACE, (guchar *) &desktop_type, 1);
2295 	}
2296 
2297 	gtk_widget_add_events(win,
2298 			GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2299 			GDK_EXPOSURE_MASK |
2300 			GDK_BUTTON1_MOTION_MASK |
2301 			GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK);
2302 	g_signal_connect(win, "button-press-event",
2303 			G_CALLBACK(button_press_event), NULL);
2304 	g_signal_connect(win, "button-release-event",
2305 			G_CALLBACK(button_release_event), NULL);
2306 	g_signal_connect(win, "motion-notify-event",
2307 			G_CALLBACK(lasso_motion), NULL);
2308 	g_signal_connect(pinboard->fixed, "expose_event",
2309 			G_CALLBACK(bg_expose), NULL);
2310 
2311 	/* Some window managers use scroll events on the root to switch
2312 	 * desktops, but don't cope with our pinboard window, so we forward
2313 	 * them manually in that case.
2314 	 */
2315 	g_signal_connect(win, "scroll-event", G_CALLBACK(scroll_event), NULL);
2316 
2317 	/* Drag and drop handlers */
2318 	drag_set_pinboard_dest(win);
2319 	g_signal_connect(win, "drag_motion", G_CALLBACK(bg_drag_motion), NULL);
2320 	g_signal_connect(win, "drag_leave", G_CALLBACK(bg_drag_leave), NULL);
2321 
2322 	pinboard->shadow_gc = gdk_gc_new(win->window);
2323 	gdk_gc_set_rgb_fg_color(pinboard->shadow_gc, &pin_text_shadow_col);
2324 
2325 	reload_backdrop(current_pinboard, NULL, BACKDROP_NONE);
2326 
2327 	gtk_widget_show_all(win);
2328 	gdk_window_lower(win->window);
2329 }
2330 
2331 /* Load image 'path' and scale according to 'style' */
load_backdrop(const gchar * path,BackdropStyle style)2332 static GdkPixmap *load_backdrop(const gchar *path, BackdropStyle style)
2333 {
2334 	GdkPixmap *pixmap;
2335 	GdkPixbuf *pixbuf;
2336 	GError *error = NULL;
2337 
2338 	pixbuf = gdk_pixbuf_new_from_file(path, &error);
2339 	if (error)
2340 	{
2341 		delayed_error(_("Error loading backdrop image:\n%s\n"
2342 				"Backdrop removed."),
2343 				error->message);
2344 		g_error_free(error);
2345 		pinboard_set_backdrop(NULL, BACKDROP_NONE);
2346 		return NULL;
2347 	}
2348 
2349 	if (style == BACKDROP_STRETCH)
2350 	{
2351 		GdkPixbuf *old = pixbuf;
2352 
2353 		pixbuf = gdk_pixbuf_scale_simple(old,
2354 				screen_width, screen_height,
2355 				GDK_INTERP_HYPER);
2356 
2357 		g_object_unref(old);
2358 	}
2359 	else if (style == BACKDROP_CENTRE || style == BACKDROP_SCALE || style == BACKDROP_FIT)
2360 	{
2361 		GdkPixbuf *old = pixbuf;
2362 		int	  x, y, width, height;
2363 		int   offset_x, offset_y;
2364 		float	  scale;
2365 
2366 		width = gdk_pixbuf_get_width(pixbuf);
2367 		height = gdk_pixbuf_get_height(pixbuf);
2368 
2369 		if (style == BACKDROP_SCALE)
2370 		{
2371 			float	  scale_x, scale_y;
2372 			scale_x = screen_width / ((float) width);
2373 			scale_y = screen_height / ((float) height);
2374 			scale = MIN(scale_x, scale_y);
2375 		}
2376 		else if (style == BACKDROP_FIT)
2377 		{
2378 			float	  scale_x, scale_y;
2379 			scale_x = screen_width / ((float) width);
2380 			scale_y = screen_height / ((float) height);
2381 			scale = MAX(scale_x, scale_y);
2382 		}
2383 		else
2384 			scale = 1;
2385 
2386 		pixbuf = gdk_pixbuf_new(
2387 				gdk_pixbuf_get_colorspace(pixbuf), FALSE,
2388 				8, screen_width, screen_height);
2389 		gdk_pixbuf_fill(pixbuf, ((pin_text_bg_col.red & 0xff00) << 16) |
2390 					((pin_text_bg_col.green & 0xff00) << 8) |
2391 					((pin_text_bg_col.blue & 0xff00)));
2392 
2393 		x = (screen_width - width * scale) / 2;
2394 		y = (screen_height - height * scale) / 2;
2395 
2396 		if (style == BACKDROP_CENTRE)
2397 		{
2398 			offset_x = x;
2399 			offset_y = y;
2400 			x = MAX(x, 0);
2401 			y = MAX(y, 0);
2402 		}
2403 		else {
2404 			x = MAX(x, 0);
2405 			y = MAX(y, 0);
2406 			offset_x = x;
2407 			offset_y = y;
2408 		}
2409 
2410 
2411 		gdk_pixbuf_composite(old, pixbuf,
2412 				x, y,
2413 				MIN(screen_width, width * scale),
2414 				MIN(screen_height, height * scale),
2415 				offset_x, offset_y, scale, scale,
2416 				o_pinboard_image_scaling.int_value?
2417 				     GDK_INTERP_BILINEAR: GDK_INTERP_HYPER,
2418 				255);
2419 		g_object_unref(old);
2420 	}
2421 
2422 	gdk_pixbuf_render_pixmap_and_mask(pixbuf,
2423 			&pixmap, NULL, 0);
2424 	g_object_unref(pixbuf);
2425 
2426 	return pixmap;
2427 }
2428 
abandon_backdrop_app(Pinboard * pinboard)2429 static void abandon_backdrop_app(Pinboard *pinboard)
2430 {
2431 	g_return_if_fail(pinboard != NULL);
2432 
2433 	if (pinboard->to_backdrop_app != -1)
2434 	{
2435 		close(pinboard->to_backdrop_app);
2436 		close(pinboard->from_backdrop_app);
2437 		g_source_remove(pinboard->input_tag);
2438 		g_string_free(pinboard->input_buffer, TRUE);
2439 		pinboard->to_backdrop_app = -1;
2440 		pinboard->from_backdrop_app = -1;
2441 		pinboard->input_tag = -1;
2442 		pinboard->input_buffer = NULL;
2443 	}
2444 
2445 	g_return_if_fail(pinboard->to_backdrop_app == -1);
2446 	g_return_if_fail(pinboard->from_backdrop_app == -1);
2447 	g_return_if_fail(pinboard->input_tag == -1);
2448 	g_return_if_fail(pinboard->input_buffer == NULL);
2449 }
2450 
2451 /* A single line has been read from the child.
2452  * Processes the command, and replies 'ok' (or abandons the child on error).
2453  */
command_from_backdrop_app(Pinboard * pinboard,const gchar * command)2454 static void command_from_backdrop_app(Pinboard *pinboard, const gchar *command)
2455 {
2456 	BackdropStyle style;
2457 	const char *ok = "ok\n";
2458 
2459 	if (strncmp(command, "tile ", 5) == 0)
2460 	{
2461 		style = BACKDROP_TILE;
2462 		command += 5;
2463 	}
2464 	else if (strncmp(command, "scale ", 6) == 0)
2465 	{
2466 		style = BACKDROP_SCALE;
2467 		command += 6;
2468 	}
2469 	else if (strncmp(command, "fit ", 4) == 0)
2470 	{
2471 		style = BACKDROP_FIT;
2472 		command += 4;
2473 	}
2474 	else if (strncmp(command, "stretch ", 8) == 0)
2475 	{
2476 		style = BACKDROP_STRETCH;
2477 		command += 8;
2478 	}
2479 	else if (strncmp(command, "centre ", 7) == 0)
2480 	{
2481 		style = BACKDROP_CENTRE;
2482 		command += 7;
2483 	}
2484 	else
2485 	{
2486 		g_warning("Invalid command '%s' from backdrop app\n",
2487 				command);
2488 		abandon_backdrop_app(pinboard);
2489 		return;
2490 	}
2491 
2492 	/* Load the backdrop. May abandon the program if loading fails. */
2493 	reload_backdrop(pinboard, command, style);
2494 
2495 	if (pinboard->to_backdrop_app == -1)
2496 		return;
2497 
2498 	while (*ok)
2499 	{
2500 		int sent;
2501 
2502 		sent = write(pinboard->to_backdrop_app, ok, strlen(ok));
2503 		if (sent <= 0)
2504 		{
2505 			/* Remote app quit? Not an error. */
2506 			abandon_backdrop_app(pinboard);
2507 			break;
2508 		}
2509 		ok += sent;
2510 	}
2511 }
2512 
backdrop_from_child(Pinboard * pinboard,int src,GdkInputCondition cond)2513 static void backdrop_from_child(Pinboard *pinboard,
2514 				int src, GdkInputCondition cond)
2515 {
2516 	char buf[256];
2517 	int got;
2518 
2519 	got = read(src, buf, sizeof(buf));
2520 
2521 	if (got <= 0)
2522 	{
2523 		if (got < 0)
2524 			g_warning("backdrop_from_child: %s\n",
2525 					g_strerror(errno));
2526 		abandon_backdrop_app(pinboard);
2527 		return;
2528 	}
2529 
2530 	g_string_append_len(pinboard->input_buffer, buf, got);
2531 
2532 	while (pinboard->from_backdrop_app != -1)
2533 	{
2534 		int len;
2535 		char *nl, *command;
2536 
2537 		nl = strchr(pinboard->input_buffer->str, '\n');
2538 		if (!nl)
2539 			return;		/* Haven't got a whole line yet */
2540 
2541 		len = nl - pinboard->input_buffer->str;
2542 		command = g_strndup(pinboard->input_buffer->str, len);
2543 		g_string_erase(pinboard->input_buffer, 0, len + 1);
2544 
2545 		command_from_backdrop_app(pinboard, command);
2546 
2547 		g_free(command);
2548 	}
2549 }
2550 
reload_backdrop(Pinboard * pinboard,const gchar * backdrop,BackdropStyle backdrop_style)2551 static void reload_backdrop(Pinboard *pinboard,
2552 			    const gchar *backdrop,
2553 			    BackdropStyle backdrop_style)
2554 {
2555 	GtkStyle *style;
2556 
2557 	if (backdrop && backdrop_style == BACKDROP_PROGRAM)
2558 	{
2559 		const char *argv[] = {NULL, "--backdrop", NULL};
2560 		GError	*error = NULL;
2561 
2562 		g_return_if_fail(pinboard->to_backdrop_app == -1);
2563 		g_return_if_fail(pinboard->from_backdrop_app == -1);
2564 		g_return_if_fail(pinboard->input_tag == -1);
2565 		g_return_if_fail(pinboard->input_buffer == NULL);
2566 
2567 		argv[0] = make_path(backdrop, "AppRun");
2568 
2569 		/* Run the program. It'll send us a SOAP message and we'll
2570 		 * get back here with a different style and image.
2571 		 */
2572 
2573 		if (g_spawn_async_with_pipes(NULL, (gchar **) argv, NULL,
2574 				G_SPAWN_DO_NOT_REAP_CHILD |
2575 				G_SPAWN_SEARCH_PATH,
2576 				NULL, NULL,		/* Child setup fn */
2577 				NULL,			/* Child PID */
2578 				&pinboard->to_backdrop_app,
2579 				&pinboard->from_backdrop_app,
2580 				NULL,			/* Standard error */
2581 				&error))
2582 		{
2583 			pinboard->input_buffer = g_string_new(NULL);
2584 			pinboard->input_tag = gdk_input_add_full(
2585 					pinboard->from_backdrop_app,
2586 					GDK_INPUT_READ,
2587 					(GdkInputFunction) backdrop_from_child,
2588 					pinboard, NULL);
2589 		}
2590 		else
2591 		{
2592 			delayed_error("%s", error ? error->message : "(null)");
2593 			g_error_free(error);
2594 		}
2595 		return;
2596 	}
2597 
2598 	/* Note: Copying a style does not ref the pixmaps! */
2599 
2600 	style = gtk_style_copy(gtk_widget_get_style(pinboard->window));
2601 	style->bg_pixmap[GTK_STATE_NORMAL] = NULL;
2602 
2603 	if (backdrop)
2604 		style->bg_pixmap[GTK_STATE_NORMAL] =
2605 			load_backdrop(backdrop, backdrop_style);
2606 
2607 	gdk_color_parse(o_pinboard_bg_colour.value,
2608 			&style->bg[GTK_STATE_NORMAL]);
2609 
2610 	gtk_widget_set_style(pinboard->window, style);
2611 
2612 	g_object_unref(style);
2613 
2614 	gtk_widget_queue_draw(pinboard->window);
2615 
2616 	/* Also update root window property (for transparent xterms, etc) */
2617 	if (style->bg_pixmap[GTK_STATE_NORMAL])
2618 	{
2619 		XID id = GDK_DRAWABLE_XID(style->bg_pixmap[GTK_STATE_NORMAL]);
2620 		gdk_property_change(gdk_get_default_root_window(),
2621 				gdk_atom_intern("_XROOTPMAP_ID", FALSE),
2622 				gdk_atom_intern("PIXMAP", FALSE),
2623 				32, GDK_PROP_MODE_REPLACE,
2624 				(guchar *) &id, 1);
2625 	}
2626 	else
2627 	{
2628 		gdk_property_delete(gdk_get_default_root_window(),
2629 				gdk_atom_intern("_XROOTPMAP_ID", FALSE));
2630 	}
2631 }
2632 
2633 #define SEARCH_STEP 32
2634 
2635 /* Search the area (omin, imin) to (omax, imax) for a free region the size of
2636  * 'rect' that doesn't overlap 'used'.  Which of inner and outer is the
2637  * vertical axis depends on the configuration.
2638  *
2639  * id and od give the direction of the search (step size).
2640  *
2641  * Returns the start of the found region in inner/outer, or -1 if there is no
2642  * free space.
2643  */
search_free(GdkRectangle * rect,GdkRegion * used,int * outer,int od,int omin,int omax,int * inner,int id,int imin,int imax)2644 static void search_free(GdkRectangle *rect, GdkRegion *used,
2645 			int *outer, int od, int omin, int omax,
2646 			int *inner, int id, int imin, int imax)
2647 {
2648 	*outer = od > 0 ? omin : omax;
2649 	while (*outer >= omin && *outer <= omax)
2650 	{
2651 		*inner = id > 0 ? imin : imax;
2652 		while (*inner >= imin && *inner <= imax)
2653 		{
2654 			if (gdk_region_rect_in(used, rect) ==
2655 					GDK_OVERLAP_RECTANGLE_OUT)
2656 				return;
2657 			*inner += id;
2658 		}
2659 
2660 		*outer += od;
2661 	}
2662 
2663 	rect->x = -1;
2664 	rect->y = -1;
2665 }
2666 
2667 /* Search the width x height area from (x0, y0) for a free region of size
2668  * 'rect'. direction indicates whether to search rows or columns. dx, dy gives
2669  * the direction of the search.
2670  */
search_free_area(GdkRectangle * rect,GdkRegion * used,int direction,int dx,int dy,int x0,int y0,int width,int height)2671 static void search_free_area(GdkRectangle *rect, GdkRegion *used,
2672 		int direction, int dx, int dy, int x0, int y0, int width, int height)
2673 {
2674 	if (direction == DIR_VERT)
2675 	{
2676 		search_free(rect, used,
2677 			    &rect->x, dx, x0, width,
2678 			    &rect->y, dy, y0, height);
2679 	}
2680 	else
2681 	{
2682 		search_free(rect, used,
2683 			    &rect->y, dy, x0, height,
2684 			    &rect->x, dx, y0, width);
2685 	}
2686 }
2687 
search_free_xinerama(GdkRectangle * rect,GdkRegion * used,int direction,int dx,int dy,int rwidth,int rheight)2688 static gboolean search_free_xinerama(GdkRectangle *rect, GdkRegion *used,
2689 		int direction, int dx, int dy, int rwidth, int rheight)
2690 {
2691 	GdkRectangle *geom = &monitor_geom[get_monitor_under_pointer()];
2692 
2693 	search_free_area(rect, used, direction, dx, dy,
2694 			geom->x, geom->y, geom->width - rwidth, geom->height - rheight);
2695 	return rect->x != -1;
2696 }
2697 
2698 /* Finds a free area on the pinboard large enough for the width and height
2699  * of the given rectangle, by filling in the x and y fields of 'rect'.
2700  * If 'old' is true, 'rect' has a previous position and we first check
2701  * if it is viable.
2702  * The search order respects user preferences.
2703  * If no area is free, returns any old area.
2704  */
find_free_rect(Pinboard * pinboard,GdkRectangle * rect,gboolean old,int start,int direction)2705 static void find_free_rect(Pinboard *pinboard, GdkRectangle *rect,
2706 			   gboolean old, int start, int direction)
2707 {
2708 	GdkRegion *used;
2709 	GList *next;
2710 	GdkRectangle used_rect;
2711 	int dx = SEARCH_STEP, dy = SEARCH_STEP;
2712 
2713 	used = gdk_region_new();
2714 
2715 	panel_mark_used(used);
2716 
2717 	/* Subtract the no-go areas... */
2718 
2719 	if (o_top_margin.int_value > 0)
2720 	{
2721 		used_rect.x = 0;
2722 		used_rect.y = 0;
2723 		used_rect.width = gdk_screen_width();
2724 		used_rect.height = o_top_margin.int_value;
2725 		gdk_region_union_with_rect(used, &used_rect);
2726 	}
2727 
2728 	if (o_bottom_margin.int_value > 0)
2729 	{
2730 		used_rect.x = 0;
2731 		used_rect.y = gdk_screen_height() - o_bottom_margin.int_value;
2732 		used_rect.width = gdk_screen_width();
2733 		used_rect.height = o_bottom_margin.int_value;
2734 		gdk_region_union_with_rect(used, &used_rect);
2735 	}
2736 
2737 	if (o_left_margin.int_value > 0)
2738 	{
2739 		used_rect.x = 0;
2740 		used_rect.y = 0;
2741 		used_rect.width = o_left_margin.int_value;
2742 		used_rect.height = gdk_screen_height();
2743 		gdk_region_union_with_rect(used, &used_rect);
2744 	}
2745 
2746 	if (o_right_margin.int_value > 0)
2747 	{
2748 		used_rect.x = gdk_screen_width() - o_right_margin.int_value;
2749 		used_rect.y = 0;
2750 		used_rect.width = o_right_margin.int_value;
2751 		used_rect.height = gdk_screen_height();
2752 		gdk_region_union_with_rect(used, &used_rect);
2753 	}
2754 
2755 	/* Subtract the used areas... */
2756 
2757 	next = GTK_FIXED(pinboard->fixed)->children;
2758 	for (; next; next = next->next)
2759 	{
2760 		GtkFixedChild *fix = (GtkFixedChild *) next->data;
2761 
2762 		if (!GTK_WIDGET_VISIBLE(fix->widget))
2763 			continue;
2764 
2765 		used_rect.x = fix->x;
2766 		used_rect.y = fix->y;
2767 		used_rect.width = fix->widget->requisition.width;
2768 		used_rect.height = fix->widget->requisition.height;
2769 
2770 		gdk_region_union_with_rect(used, &used_rect);
2771 	}
2772 
2773 	/* Check the previous area */
2774 	if(old) {
2775 		if(gdk_region_rect_in(used, rect)==GDK_OVERLAP_RECTANGLE_OUT) {
2776 			gdk_region_destroy(used);
2777 			return;
2778 		}
2779 	}
2780 
2781 	/* Find the first free area (yes, this isn't exactly pretty, but
2782 	 * it works). If you know a better (fast!) algorithm, let me know!
2783 	 */
2784 
2785 	if (start == CORNER_TOP_RIGHT ||
2786 	    start == CORNER_BOTTOM_RIGHT)
2787 		dx = -SEARCH_STEP;
2788 
2789 	if (start == CORNER_BOTTOM_LEFT ||
2790 	    start == CORNER_BOTTOM_RIGHT)
2791 		dy = -SEARCH_STEP;
2792 
2793 	/* If pinboard covers more than one monitor, try to find free space on
2794 	 * monitor under pointer first, then whole screen if that fails */
2795 	if (n_monitors == 1 || !search_free_xinerama(rect, used,
2796 			direction, dx, dy, rect->width, rect->height))
2797 	{
2798 		search_free_area(rect, used, direction, dx, dy,
2799 			0, 0, screen_width - rect->width, screen_height - rect->height);
2800 	}
2801 
2802 	gdk_region_destroy(used);
2803 
2804 	if (rect->x == -1)
2805 	{
2806 		rect->x = 0;
2807 		rect->y = 0;
2808 	}
2809 }
2810 
2811 /* Icon's size, shape or appearance has changed - update the display */
pinboard_reshape_icon(Icon * icon)2812 static void pinboard_reshape_icon(Icon *icon)
2813 {
2814 	PinIcon	*pi = (PinIcon *) icon;
2815 	int	x = pi->x, y = pi->y;
2816 
2817 	set_size_and_style(pi);
2818 	offset_from_centre(pi, &x, &y);
2819 
2820 	if (pi->win->allocation.x != x || pi->win->allocation.y != y)
2821 	{
2822 		fixed_move_fast(GTK_FIXED(current_pinboard->fixed),
2823 				pi->win, x, y);
2824 	}
2825 
2826 	/* Newer versions of GTK seem to need this, or the icon doesn't
2827 	 * get redrawn.
2828 	 */
2829 	gtk_widget_queue_draw(pi->win);
2830 }
2831 
2832 /* Sets the pinboard_font global from the option. Doesn't do anything else. */
update_pinboard_font(void)2833 static void update_pinboard_font(void)
2834 {
2835 	if (pinboard_font)
2836 		pango_font_description_free(pinboard_font);
2837 	pinboard_font = o_label_font.value[0] != '\0'
2838 			? pango_font_description_from_string(o_label_font.value)
2839 			: NULL;
2840 }
2841 
radios_changed(Radios * radios,gpointer data)2842 static void radios_changed(Radios *radios, gpointer data)
2843 {
2844 	GObject *dialog = G_OBJECT(data);
2845 	DropBox *drop_box;
2846 	const guchar *path;
2847 
2848 	g_return_if_fail(dialog != NULL);
2849 
2850 	drop_box = g_object_get_data(G_OBJECT(dialog), "rox-dropbox");
2851 
2852 	g_return_if_fail(radios != NULL);
2853 	g_return_if_fail(drop_box != NULL);
2854 	g_return_if_fail(current_pinboard != NULL);
2855 
2856 	if (current_pinboard->backdrop_style != BACKDROP_PROGRAM)
2857 	{
2858 		path = drop_box_get_path(drop_box);
2859 		if (path)
2860 			pinboard_set_backdrop(path, radios_get_value(radios));
2861 	}
2862 }
2863 
update_radios(GtkWidget * dialog)2864 static void update_radios(GtkWidget *dialog)
2865 {
2866 	Radios *radios;
2867 	GtkWidget *hbox;
2868 
2869 	g_return_if_fail(dialog != NULL);
2870 
2871 	radios = g_object_get_data(G_OBJECT(dialog), "rox-radios");
2872 	hbox = g_object_get_data(G_OBJECT(dialog), "rox-radios-hbox");
2873 
2874 	g_return_if_fail(current_pinboard != NULL);
2875 	g_return_if_fail(radios != NULL);
2876 	g_return_if_fail(hbox != NULL);
2877 
2878 	switch (current_pinboard->backdrop_style)
2879 	{
2880 		case BACKDROP_TILE:
2881 		case BACKDROP_STRETCH:
2882 		case BACKDROP_SCALE:
2883 		case BACKDROP_CENTRE:
2884 		case BACKDROP_FIT:
2885 			radios_set_value(radios,
2886 					 current_pinboard->backdrop_style);
2887 			gtk_widget_set_sensitive(hbox, TRUE);
2888 			break;
2889 		default:
2890 			gtk_widget_set_sensitive(hbox, FALSE);
2891 			radios_set_value(radios, BACKDROP_TILE);
2892 			break;
2893 	}
2894 }
2895