1 /* $Id$ */
2 /* Copyright (c) 2007-2015 Pierre Pronchery <khorben@defora.org> */
3 /* This file is part of DeforaOS Desktop Browser */
4 /* Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the authors nor the names of the contributors may be
16  *    used to endorse or promote products derived from this software without
17  *    specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND ANY
20  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
29 /* TODO:
30  * - let the user define the desktop folder (possibly default to FDO's)
31  * - track multiple selection on delete/properties
32  * - avoid code duplication with DeforaOS Panel ("main" applet) */
33 
34 
35 
36 #include <sys/stat.h>
37 #include <sys/wait.h>
38 #include <fcntl.h>
39 #include <unistd.h>
40 #include <dirent.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <errno.h>
45 #include <locale.h>
46 #include <libintl.h>
47 #include <X11/Xlib.h>
48 #include <gtk/gtk.h>
49 #include <gdk/gdkx.h>
50 #include <X11/Xatom.h>
51 #include <X11/extensions/Xrandr.h>
52 #include <System.h>
53 #include "../include/Browser/desktop.h"
54 #include "../include/Browser/vfs.h"
55 #include "desktop.h"
56 #include "../config.h"
57 #define _(string) gettext(string)
58 #define N_(string) string
59 
60 #define COMMON_SYMLINK
61 #include "common.c"
62 
63 
64 /* constants */
65 #ifndef PROGNAME
66 # define PROGNAME	"desktop"
67 #endif
68 #ifndef PREFIX
69 # define PREFIX		"/usr/local"
70 #endif
71 #ifndef DATADIR
72 # define DATADIR	PREFIX "/share"
73 #endif
74 #ifndef LOCALEDIR
75 # define LOCALEDIR	DATADIR "/locale"
76 #endif
77 
78 
79 /* Desktop */
80 /* private */
81 /* types */
82 typedef struct _DesktopCategory DesktopCategory;
83 
84 struct _Desktop
85 {
86 	DesktopPrefs prefs;
87 	PangoFontDescription * font;
88 #if GTK_CHECK_VERSION(3, 0, 0)
89 	GdkRGBA background;
90 	GdkRGBA foreground;
91 #else
92 	GdkColor background;
93 	GdkColor foreground;
94 #endif
95 
96 	/* workarea */
97 	GdkRectangle window;
98 	GdkRectangle workarea;
99 
100 	/* icons */
101 	DesktopIcon ** icon;
102 	size_t icon_cnt;
103 
104 	/* common */
105 	char * path;
106 	size_t path_cnt;
107 	DIR * refresh_dir;
108 	time_t refresh_mti;
109 	guint refresh_source;
110 	/* files */
111 	Mime * mime;
112 	char const * home;
113 	GdkPixbuf * file;
114 	GdkPixbuf * folder;
115 	gboolean show_hidden;
116 	/* applications */
117 	DesktopCategory * category;
118 	/* categories */
119 	GSList * apps;
120 
121 	/* preferences */
122 	GtkWidget * pr_window;
123 	GtkWidget * pr_color;
124 	GtkWidget * pr_background;
125 	GtkWidget * pr_background_how;
126 	GtkWidget * pr_background_extend;
127 	GtkWidget * pr_ilayout;
128 	GtkWidget * pr_imonitor;
129 	GtkWidget * pr_ibcolor;
130 	GtkWidget * pr_ifcolor;
131 	GtkWidget * pr_ifont;
132 	GtkWidget * pr_monitors;
133 	GtkWidget * pr_monitors_res;
134 	GtkWidget * pr_monitors_size;
135 
136 	/* internal */
137 	GdkScreen * screen;
138 	GdkDisplay * display;
139 	GdkWindow * root;
140 	GtkWidget * desktop;
141 	GdkWindow * back;
142 	GtkIconTheme * theme;
143 	GtkWidget * menu;
144 #if GTK_CHECK_VERSION(3, 0, 0)
145 	cairo_t * cairo;
146 #else
147 	GdkPixmap * pixmap;
148 #endif
149 };
150 
151 struct _DesktopCategory
152 {
153 	gboolean show;
154 	char const * category;
155 	char const * name;
156 	char const * icon;
157 };
158 
159 typedef enum _DesktopHows
160 {
161 	DESKTOP_HOW_NONE = 0,
162 	DESKTOP_HOW_CENTERED,
163 	DESKTOP_HOW_SCALED,
164 	DESKTOP_HOW_SCALED_RATIO,
165 	DESKTOP_HOW_TILED
166 } DesktopHows;
167 #define DESKTOP_HOW_LAST	DESKTOP_HOW_TILED
168 #define DESKTOP_HOW_COUNT	(DESKTOP_HOW_LAST + 1)
169 
170 
171 /* constants */
172 #define DESKTOP			".desktop"
173 #define DESKTOPRC		".desktoprc"
174 
175 static DesktopCategory _desktop_categories[] =
176 {
177 	{ FALSE, "Audio",	"Audio",	"gnome-mime-audio",	},
178 	{ FALSE, "Development",	"Development",	"applications-development"},
179 	{ FALSE, "Education",	"Education",	"applications-science"	},
180 	{ FALSE, "Game",	"Games",	"applications-games"	},
181 	{ FALSE, "Graphics",	"Graphics",	"applications-graphics"	},
182 	{ FALSE, "AudioVideo",	"Multimedia",	"applications-multimedia"},
183 	{ FALSE, "Network",	"Network",	"applications-internet" },
184 	{ FALSE, "Office",	"Office",	"applications-office"	},
185 	{ FALSE, "Settings",	"Settings",	"gnome-settings"	},
186 	{ FALSE, "System",	"System",	"applications-system"	},
187 	{ FALSE, "Utility",	"Utilities",	"applications-utilities"},
188 	{ FALSE, "Video",	"Video",	"video"			}
189 };
190 static const size_t _desktop_categories_cnt = sizeof(_desktop_categories)
191 	/ sizeof(*_desktop_categories);
192 
193 static const char * _desktop_hows[DESKTOP_HOW_COUNT] =
194 {
195 	"none",
196 	"centered",
197 	"scaled",
198 	"scaled_ratio",
199 	"tiled"
200 };
201 
202 static const char * _desktop_icons_config[DESKTOP_ICONS_COUNT] =
203 {
204 	"none", "applications", "categories", "files", "homescreen"
205 };
206 
207 static const char * _desktop_icons[DESKTOP_ICONS_COUNT] =
208 {
209 	N_("Do not draw icons"),
210 	N_("Applications"),
211 	N_("Categories"),
212 	N_("Desktop contents"),
213 	N_("Home screen")
214 };
215 
216 
217 /* prototypes */
218 static int _desktop_error(Desktop * desktop, char const * message,
219 		char const * error, int ret);
220 static int _desktop_serror(Desktop * desktop, char const * message, int ret);
221 
222 /* accessors */
223 static Config * _desktop_get_config(Desktop * desktop);
224 static int _desktop_get_workarea(Desktop * desktop);
225 
226 /* useful */
227 #if GTK_CHECK_VERSION(3, 0, 0)
228 static void _desktop_draw_background(Desktop * desktop, GdkRGBA * color,
229 		char const * filename, DesktopHows how, gboolean extend);
230 #else
231 static void _desktop_draw_background(Desktop * desktop, GdkColor * color,
232 		char const * filename, DesktopHows how, gboolean extend);
233 #endif
234 
235 static int _desktop_icon_add(Desktop * desktop, DesktopIcon * icon);
236 static int _desktop_icon_remove(Desktop * desktop, DesktopIcon * icon);
237 
238 static void _desktop_show_preferences(Desktop * desktop);
239 
240 /* callbacks */
241 static gboolean _desktop_on_refresh(gpointer data);
242 
243 
244 /* public */
245 /* functions */
246 /* desktop_new */
247 /* callbacks */
248 static void _new_events(Desktop * desktop, GdkWindow * window,
249 		GdkEventMask mask);
250 static void _new_icons(Desktop * desktop);
251 static int _on_message(void * data, uint32_t value1, uint32_t value2,
252 		uint32_t value3);
253 static void _on_popup(gpointer data);
254 static void _on_popup_event(gpointer data, XButtonEvent * xbev);
255 static void _on_realize(gpointer data);
256 static GdkFilterReturn _on_root_event(GdkXEvent * xevent, GdkEvent * event,
257 		gpointer data);
258 
desktop_new(DesktopPrefs * prefs)259 Desktop * desktop_new(DesktopPrefs * prefs)
260 {
261 	Desktop * desktop;
262 #if !GTK_CHECK_VERSION(2, 24, 0)
263 	gint depth;
264 #endif
265 	GdkEventMask mask = GDK_PROPERTY_CHANGE_MASK;
266 
267 	if((desktop = object_new(sizeof(*desktop))) == NULL)
268 		return NULL;
269 	memset(desktop, 0, sizeof(*desktop));
270 	/* set default foreground to white */
271 	memset(&desktop->foreground, 0xff, sizeof(desktop->foreground));
272 	desktop->prefs.alignment = DESKTOP_ALIGNMENT_VERTICAL;
273 	desktop->prefs.icons = DESKTOP_ICONS_FILES;
274 	desktop->prefs.monitor = -1;
275 	if(prefs != NULL)
276 		desktop->prefs = *prefs;
277 	desktop->font = NULL;
278 	/* workarea */
279 	desktop->screen = gdk_screen_get_default();
280 	desktop->display = gdk_screen_get_display(desktop->screen);
281 	desktop->root = gdk_screen_get_root_window(desktop->screen);
282 	desktop->theme = gtk_icon_theme_get_default();
283 	desktop->menu = NULL;
284 	if((desktop->home = getenv("HOME")) == NULL
285 			&& (desktop->home = g_get_home_dir()) == NULL)
286 		desktop->home = "/";
287 	desktop_message_register(NULL, DESKTOP_CLIENT_MESSAGE, _on_message,
288 			desktop);
289 	/* query the root window */
290 #if GTK_CHECK_VERSION(2, 24, 0)
291 	gdk_window_get_position(desktop->root, &desktop->window.x,
292 			&desktop->window.y);
293 	desktop->window.width = gdk_window_get_width(desktop->root);
294 	desktop->window.height = gdk_window_get_height(desktop->root);
295 #else
296 	gdk_window_get_geometry(desktop->root, &desktop->window.x,
297 			&desktop->window.y, &desktop->window.width,
298 			&desktop->window.height, &depth);
299 #endif
300 	if(desktop->prefs.window)
301 	{
302 		/* create the desktop window */
303 		desktop->desktop = gtk_window_new(GTK_WINDOW_TOPLEVEL);
304 		gtk_window_set_default_size(GTK_WINDOW(desktop->desktop),
305 				desktop->window.width, desktop->window.height);
306 		gtk_window_set_type_hint(GTK_WINDOW(desktop->desktop),
307 				GDK_WINDOW_TYPE_HINT_DESKTOP);
308 		/* support pop-up menus on the desktop window if enabled */
309 		if(desktop->prefs.popup)
310 			g_signal_connect_swapped(desktop->desktop, "popup-menu",
311 					G_CALLBACK(_on_popup), desktop);
312 		/* draw the icons and background when realized */
313 		g_signal_connect_swapped(desktop->desktop, "realize",
314 				G_CALLBACK(_on_realize), desktop);
315 		gtk_window_move(GTK_WINDOW(desktop->desktop), desktop->window.x,
316 				desktop->window.y);
317 		gtk_widget_show(desktop->desktop);
318 	}
319 	else
320 	{
321 		desktop->desktop = NULL;
322 		desktop->back = desktop->root;
323 		/* draw the icons and background when idle */
324 		desktop_reset(desktop);
325 		/* support pop-up menus on the root window if enabled */
326 		if(desktop->prefs.popup)
327 			mask |= GDK_BUTTON_PRESS_MASK;
328 	}
329 	/* manage events on the root window */
330 	_new_events(desktop, desktop->root, mask);
331 	/* load the default icons */
332 	_new_icons(desktop);
333 	return desktop;
334 }
335 
_new_events(Desktop * desktop,GdkWindow * window,GdkEventMask mask)336 static void _new_events(Desktop * desktop, GdkWindow * window,
337 		GdkEventMask mask)
338 {
339 	mask = gdk_window_get_events(window) | mask;
340 	gdk_window_set_events(window, mask);
341 	gdk_window_add_filter(window, _on_root_event, desktop);
342 }
343 
_new_icons(Desktop * desktop)344 static void _new_icons(Desktop * desktop)
345 {
346 	const char * file[] = { "gnome-fs-regular",
347 #if GTK_CHECK_VERSION(2, 6, 0)
348 		GTK_STOCK_FILE,
349 #endif
350 		GTK_STOCK_MISSING_IMAGE, NULL };
351 	const char * folder[] = { "gnome-fs-directory",
352 #if GTK_CHECK_VERSION(2, 6, 0)
353 		GTK_STOCK_DIRECTORY,
354 #endif
355 		GTK_STOCK_MISSING_IMAGE, NULL };
356 	char const ** p;
357 
358 	for(p = file; *p != NULL && desktop->file == NULL; p++)
359 		desktop->file = gtk_icon_theme_load_icon(desktop->theme,
360 				*p, DESKTOPICON_ICON_SIZE, 0, NULL);
361 	for(p = folder; *p != NULL && desktop->folder == NULL; p++)
362 		desktop->folder = gtk_icon_theme_load_icon(desktop->theme, *p,
363 				DESKTOPICON_ICON_SIZE, 0, NULL);
364 }
365 
_on_message(void * data,uint32_t value1,uint32_t value2,uint32_t value3)366 static int _on_message(void * data, uint32_t value1, uint32_t value2,
367 		uint32_t value3)
368 {
369 	Desktop * desktop = data;
370 	DesktopMessage message;
371 	DesktopAlignment alignment;
372 	DesktopIcons icons;
373 	DesktopLayout layout;
374 
375 	switch((message = value1))
376 	{
377 		case DESKTOP_MESSAGE_SET_ALIGNMENT:
378 			alignment = value2;
379 			desktop_set_alignment(desktop, alignment);
380 			break;
381 		case DESKTOP_MESSAGE_SET_ICONS:
382 			icons = value2;
383 			desktop_set_icons(desktop, icons);
384 			break;
385 		case DESKTOP_MESSAGE_SET_LAYOUT:
386 			layout = value2;
387 			desktop_set_layout(desktop, layout);
388 			break;
389 		case DESKTOP_MESSAGE_SHOW:
390 			if(value2 == DESKTOP_SHOW_SETTINGS)
391 				_desktop_show_preferences(desktop);
392 			break;
393 	}
394 	return GDK_FILTER_CONTINUE;
395 }
396 
397 static void _on_popup_new_folder(gpointer data);
398 static void _on_popup_new_text_file(gpointer data);
399 static void _on_popup_paste(gpointer data);
400 static void _on_popup_preferences(gpointer data);
401 static void _on_popup_symlink(gpointer data);
402 
_on_popup(gpointer data)403 static void _on_popup(gpointer data)
404 {
405 	Desktop * desktop = data;
406 
407 	_on_popup_event(desktop, NULL);
408 }
409 
_on_popup_event(gpointer data,XButtonEvent * xbev)410 static void _on_popup_event(gpointer data, XButtonEvent * xbev)
411 {
412 	Desktop * desktop = data;
413 	GtkWidget * menuitem;
414 	GtkWidget * submenu;
415 	GtkWidget * image;
416 
417 	desktop->menu = gtk_menu_new();
418 	menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL);
419 	submenu = gtk_menu_new();
420 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
421 	gtk_menu_shell_append(GTK_MENU_SHELL(desktop->menu), menuitem);
422 	/* submenu for new documents */
423 	menuitem = gtk_image_menu_item_new_with_label(_("Folder"));
424 	image = gtk_image_new_from_icon_name("folder-new", GTK_ICON_SIZE_MENU);
425 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
426 	g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(
427 				_on_popup_new_folder), desktop);
428 	gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
429 	menuitem = gtk_separator_menu_item_new();
430 	gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
431 	menuitem = gtk_image_menu_item_new_with_label(_("Symbolic link..."));
432 	g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(
433 				_on_popup_symlink), desktop);
434 	gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
435 	menuitem = gtk_image_menu_item_new_with_label(_("Text file"));
436 	image = gtk_image_new_from_icon_name("stock_new-text",
437 			GTK_ICON_SIZE_MENU);
438 	gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image);
439 	g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(
440 				_on_popup_new_text_file), desktop);
441 	gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
442 	/* edition */
443 	menuitem = gtk_separator_menu_item_new();
444 	gtk_menu_shell_append(GTK_MENU_SHELL(desktop->menu), menuitem);
445 	menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_PASTE, NULL);
446 	g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(
447 				_on_popup_paste), desktop);
448 	gtk_menu_shell_append(GTK_MENU_SHELL(desktop->menu), menuitem);
449 	/* preferences */
450 	menuitem = gtk_separator_menu_item_new();
451 	gtk_menu_shell_append(GTK_MENU_SHELL(desktop->menu), menuitem);
452 	menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES,
453 			NULL);
454 	g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(
455 				_on_popup_preferences), desktop);
456 	gtk_menu_shell_append(GTK_MENU_SHELL(desktop->menu), menuitem);
457 	gtk_widget_show_all(desktop->menu);
458 	gtk_menu_popup(GTK_MENU(desktop->menu), NULL, NULL, NULL, NULL, 3,
459 			(xbev != NULL)
460 			? xbev->time : gtk_get_current_event_time());
461 }
462 
_on_popup_new_folder(gpointer data)463 static void _on_popup_new_folder(gpointer data)
464 {
465 	static char const newfolder[] = N_("New folder");
466 	Desktop * desktop = data;
467 	String * path;
468 
469 	gtk_widget_destroy(desktop->menu);
470 	desktop->menu = NULL;
471 	if((path = string_new_append(desktop->path, "/", newfolder, NULL))
472 			== NULL)
473 	{
474 		_desktop_serror(desktop, newfolder, 0);
475 		return;
476 	}
477 	if(mkdir(path, 0777) != 0)
478 		desktop_error(desktop, path, 0);
479 	string_delete(path);
480 }
481 
_on_popup_new_text_file(gpointer data)482 static void _on_popup_new_text_file(gpointer data)
483 {
484 	static char const newtext[] = N_("New text file.txt");
485 	Desktop * desktop = data;
486 	String * path;
487 	int fd;
488 
489 	gtk_widget_destroy(desktop->menu);
490 	desktop->menu = NULL;
491 	if((path = string_new_append(desktop->path, "/", _(newtext), NULL))
492 			== NULL)
493 	{
494 		_desktop_serror(desktop, _(newtext), 0);
495 		return;
496 	}
497 	if((fd = creat(path, 0666)) < 0)
498 		desktop_error(desktop, path, 0);
499 	else
500 		close(fd);
501 	string_delete(path);
502 }
503 
_on_popup_paste(gpointer data)504 static void _on_popup_paste(gpointer data)
505 {
506 	Desktop * desktop = data;
507 
508 	/* FIXME implement */
509 	gtk_widget_destroy(desktop->menu);
510 	desktop->menu = NULL;
511 }
512 
_on_popup_preferences(gpointer data)513 static void _on_popup_preferences(gpointer data)
514 {
515 	Desktop * desktop = data;
516 
517 	_desktop_show_preferences(desktop);
518 }
519 
_on_popup_symlink(gpointer data)520 static void _on_popup_symlink(gpointer data)
521 {
522 	Desktop * desktop = data;
523 
524 	if(_common_symlink(NULL, desktop->path) != 0)
525 		desktop_error(desktop, "symlink", 0);
526 }
527 
_on_realize(gpointer data)528 static void _on_realize(gpointer data)
529 {
530 	Desktop * desktop = data;
531 	GdkEventMask mask = desktop->prefs.popup ? GDK_BUTTON_PRESS_MASK : 0;
532 
533 #if GTK_CHECK_VERSION(2, 14, 0)
534 	desktop->back = gtk_widget_get_window(desktop->desktop);
535 #else
536 	desktop->back = desktop->desktop->window;
537 #endif
538 	/* support pop-up menus on the desktop window if enabled */
539 	if(mask != 0)
540 		_new_events(desktop, desktop->back, mask);
541 	desktop_reset(desktop);
542 }
543 
544 static GdkFilterReturn _event_button_press(XButtonEvent * xbev,
545 		Desktop * desktop);
546 static GdkFilterReturn _event_configure(XConfigureEvent * xevent,
547 		Desktop * desktop);
548 static GdkFilterReturn _event_property(XPropertyEvent * xevent,
549 		Desktop * desktop);
550 
_on_root_event(GdkXEvent * xevent,GdkEvent * event,gpointer data)551 static GdkFilterReturn _on_root_event(GdkXEvent * xevent, GdkEvent * event,
552 		gpointer data)
553 {
554 	Desktop * desktop = data;
555 	XEvent * xev = xevent;
556 
557 	if(xev->type == ButtonPress)
558 		return _event_button_press(xevent, desktop);
559 	else if(xev->type == ConfigureNotify)
560 		return _event_configure(xevent, desktop);
561 	else if(xev->type == PropertyNotify)
562 		return _event_property(xevent, desktop);
563 #ifdef DEBUG
564 	fprintf(stderr, "DEBUG: %s() %d\n", __func__, xev->type);
565 #endif
566 	return GDK_FILTER_CONTINUE;
567 }
568 
_event_button_press(XButtonEvent * xbev,Desktop * desktop)569 static GdkFilterReturn _event_button_press(XButtonEvent * xbev,
570 		Desktop * desktop)
571 {
572 	if(xbev->button != 3 || desktop->menu != NULL)
573 	{
574 		if(desktop->menu != NULL)
575 		{
576 			gtk_widget_destroy(desktop->menu);
577 			desktop->menu = NULL;
578 		}
579 		return GDK_FILTER_CONTINUE;
580 	}
581 	/* ignore if not managing files */
582 	if(desktop->prefs.icons != DESKTOP_ICONS_FILES)
583 		return GDK_FILTER_CONTINUE;
584 	_on_popup_event(desktop, xbev);
585 	return GDK_FILTER_CONTINUE;
586 }
587 
_event_configure(XConfigureEvent * xevent,Desktop * desktop)588 static GdkFilterReturn _event_configure(XConfigureEvent * xevent,
589 		Desktop * desktop)
590 {
591 	desktop->window.x = xevent->x;
592 	desktop->window.y = xevent->y;
593 	desktop->window.width = xevent->width;
594 	desktop->window.height = xevent->height;
595 #ifdef DEBUG
596 	fprintf(stderr, "DEBUG: %s() (%dx%d) @ (%d,%d))\n", __func__,
597 			desktop->window.width, desktop->window.height,
598 			desktop->window.x, desktop->window.y);
599 #endif
600 	/* FIXME run it directly? */
601 	desktop_reset(desktop);
602 	return GDK_FILTER_CONTINUE;
603 }
604 
_event_property(XPropertyEvent * xevent,Desktop * desktop)605 static GdkFilterReturn _event_property(XPropertyEvent * xevent,
606 		Desktop * desktop)
607 {
608 	Atom atom;
609 
610 	atom = gdk_x11_get_xatom_by_name("_NET_WORKAREA");
611 	if(xevent->atom != atom)
612 		return GDK_FILTER_CONTINUE;
613 	_desktop_get_workarea(desktop);
614 	return GDK_FILTER_CONTINUE;
615 }
616 
617 
618 /* desktop_delete */
desktop_delete(Desktop * desktop)619 void desktop_delete(Desktop * desktop)
620 {
621 	size_t i;
622 
623 	if(desktop->desktop != NULL)
624 		gtk_widget_destroy(desktop->desktop);
625 	if(desktop->refresh_source != 0)
626 		g_source_remove(desktop->refresh_source);
627 	for(i = 0; i < desktop->icon_cnt; i++)
628 		desktopicon_delete(desktop->icon[i]);
629 	free(desktop->icon);
630 	if(desktop->mime != NULL)
631 		mime_delete(desktop->mime);
632 	g_slist_foreach(desktop->apps, (GFunc)config_delete, NULL);
633 	g_slist_free(desktop->apps);
634 	free(desktop->path);
635 	if(desktop->font != NULL)
636 		pango_font_description_free(desktop->font);
637 	object_delete(desktop);
638 }
639 
640 
641 /* accessors */
642 /* desktop_get_drag_data */
desktop_get_drag_data(Desktop * desktop,GtkSelectionData * seldata)643 int desktop_get_drag_data(Desktop * desktop, GtkSelectionData * seldata)
644 {
645 #if !GTK_CHECK_VERSION(3, 0, 0)
646 	int ret = 0;
647 	size_t i;
648 	size_t len;
649 	char const * path;
650 	unsigned char * p;
651 
652 	seldata->format = 0;
653 	seldata->data = NULL;
654 	seldata->length = 0;
655 	for(i = 0; i < desktop->icon_cnt; i++)
656 	{
657 		if(desktopicon_get_selected(desktop->icon[i]) != TRUE)
658 			continue;
659 		if((path = desktopicon_get_path(desktop->icon[i])) == NULL)
660 			continue;
661 		len = strlen(path + 1);
662 		if((p = realloc(seldata->data, seldata->length + len)) == NULL)
663 		{
664 			ret = -error_set_code(1, "%s", strerror(errno));
665 			continue;
666 		}
667 		seldata->data = p;
668 		memcpy(&p[seldata->length], path, len);
669 		seldata->length += len;
670 	}
671 	return ret;
672 #else
673 	return -1;
674 #endif
675 }
676 
677 
678 /* desktop_get_file */
desktop_get_file(Desktop * desktop)679 GdkPixbuf * desktop_get_file(Desktop * desktop)
680 {
681 	g_object_ref(desktop->file);
682 	return desktop->file;
683 }
684 
685 
686 /* desktop_get_folder */
desktop_get_folder(Desktop * desktop)687 GdkPixbuf * desktop_get_folder(Desktop * desktop)
688 {
689 	g_object_ref(desktop->folder);
690 	return desktop->folder;
691 }
692 
693 
694 /* desktop_get_mime */
desktop_get_mime(Desktop * desktop)695 Mime * desktop_get_mime(Desktop * desktop)
696 {
697 	return desktop->mime;
698 }
699 
700 
701 /* desktop_get_theme */
desktop_get_theme(Desktop * desktop)702 GtkIconTheme * desktop_get_theme(Desktop * desktop)
703 {
704 	return desktop->theme;
705 }
706 
707 
708 /* desktop_set_alignment */
709 static void _alignment_horizontal(Desktop * desktop);
710 static void _alignment_vertical(Desktop * desktop);
711 
desktop_set_alignment(Desktop * desktop,DesktopAlignment alignment)712 void desktop_set_alignment(Desktop * desktop, DesktopAlignment alignment)
713 {
714 	switch(alignment)
715 	{
716 		case DESKTOP_ALIGNMENT_VERTICAL:
717 			_alignment_vertical(desktop);
718 			break;
719 		case DESKTOP_ALIGNMENT_HORIZONTAL:
720 			_alignment_horizontal(desktop);
721 			break;
722 	}
723 }
724 
_alignment_horizontal(Desktop * desktop)725 static void _alignment_horizontal(Desktop * desktop)
726 {
727 	size_t i;
728 	int x = desktop->workarea.x;
729 	int y = desktop->workarea.y;
730 	int width = x + desktop->workarea.width;
731 
732 	for(i = 0; i < desktop->icon_cnt; i++)
733 	{
734 		if(x + DESKTOPICON_MAX_WIDTH > width)
735 		{
736 			y += DESKTOPICON_MAX_HEIGHT;
737 			x = desktop->workarea.x;
738 		}
739 		desktopicon_move(desktop->icon[i], x, y);
740 		x += DESKTOPICON_MAX_WIDTH;
741 	}
742 }
743 
_alignment_vertical(Desktop * desktop)744 static void _alignment_vertical(Desktop * desktop)
745 {
746 	size_t i;
747 	int x = desktop->workarea.x;
748 	int y = desktop->workarea.y;
749 	int height = desktop->workarea.y + desktop->workarea.height;
750 
751 	for(i = 0; i < desktop->icon_cnt; i++)
752 	{
753 		if(y + DESKTOPICON_MAX_HEIGHT > height)
754 		{
755 			x += DESKTOPICON_MAX_WIDTH;
756 			y = desktop->workarea.y;
757 		}
758 		desktopicon_move(desktop->icon[i], x, y);
759 		y += DESKTOPICON_MAX_HEIGHT;
760 	}
761 }
762 
763 
764 /* desktop_set_icons */
765 static int _icons_applications(Desktop * desktop);
766 static int _icons_categories(Desktop * desktop);
767 static int _icons_files(Desktop * desktop);
768 static int _icons_files_add_home(Desktop * desktop);
769 static int _icons_homescreen(Desktop * desktop);
770 static void _icons_reset(Desktop * desktop);
771 static void _icons_set_categories(Desktop * desktop, gpointer data);
772 static void _icons_set_homescreen(Desktop * desktop, gpointer data);
773 
desktop_set_icons(Desktop * desktop,DesktopIcons icons)774 void desktop_set_icons(Desktop * desktop, DesktopIcons icons)
775 {
776 	_icons_reset(desktop);
777 	desktop->prefs.icons = icons;
778 	switch(icons)
779 	{
780 		case DESKTOP_ICONS_APPLICATIONS:
781 			_icons_applications(desktop);
782 			break;
783 		case DESKTOP_ICONS_CATEGORIES:
784 			_icons_categories(desktop);
785 			break;
786 		case DESKTOP_ICONS_FILES:
787 			_icons_files(desktop);
788 			break;
789 		case DESKTOP_ICONS_HOMESCREEN:
790 			_icons_homescreen(desktop);
791 			break;
792 		case DESKTOP_ICONS_NONE:
793 			/* nothing to do */
794 			break;
795 	}
796 	desktop_refresh(desktop);
797 }
798 
_icons_applications(Desktop * desktop)799 static int _icons_applications(Desktop * desktop)
800 {
801 	DesktopIcon * desktopicon;
802 	GdkPixbuf * icon;
803 
804 	if(desktop->category == NULL)
805 		return 0;
806 	if((desktopicon = desktopicon_new(desktop, _("Back"), NULL)) == NULL)
807 		return -_desktop_serror(desktop, "Back", 1);
808 	desktopicon_set_callback(desktopicon, _icons_set_categories, NULL);
809 	desktopicon_set_first(desktopicon, TRUE);
810 	desktopicon_set_immutable(desktopicon, TRUE);
811 	icon = gtk_icon_theme_load_icon(desktop->theme, "back",
812 			DESKTOPICON_ICON_SIZE, 0, NULL);
813 	if(icon != NULL)
814 		desktopicon_set_icon(desktopicon, icon);
815 	if(_desktop_icon_add(desktop, desktopicon) != 0)
816 	{
817 		desktopicon_delete(desktopicon);
818 		return -1;
819 	}
820 	return 0;
821 }
822 
_icons_categories(Desktop * desktop)823 static int _icons_categories(Desktop * desktop)
824 {
825 	DesktopIcon * desktopicon;
826 	GdkPixbuf * icon;
827 
828 	desktop->category = NULL;
829 	if((desktopicon = desktopicon_new(desktop, _("Back"), NULL)) == NULL)
830 		return -_desktop_serror(desktop, "Back", 1);
831 	desktopicon_set_callback(desktopicon, _icons_set_homescreen, NULL);
832 	desktopicon_set_first(desktopicon, TRUE);
833 	desktopicon_set_immutable(desktopicon, TRUE);
834 	icon = gtk_icon_theme_load_icon(desktop->theme, "back",
835 			DESKTOPICON_ICON_SIZE, 0, NULL);
836 	if(icon != NULL)
837 		desktopicon_set_icon(desktopicon, icon);
838 	if(_desktop_icon_add(desktop, desktopicon) != 0)
839 	{
840 		desktopicon_delete(desktopicon);
841 		return -1;
842 	}
843 	return 0;
844 }
845 
_icons_files(Desktop * desktop)846 static int _icons_files(Desktop * desktop)
847 {
848 	const char path[] = "/" DESKTOP;
849 	struct stat st;
850 
851 	if(desktop->mime == NULL)
852 		desktop->mime = mime_new(NULL);
853 	_icons_files_add_home(desktop);
854 	desktop->path_cnt = strlen(desktop->home) + 1 + sizeof(path);
855 	if((desktop->path = malloc(desktop->path_cnt)) == NULL)
856 		return -desktop_error(NULL, "malloc", 1);
857 	snprintf(desktop->path, desktop->path_cnt, "%s/%s", desktop->home,
858 			path);
859 	if(stat(desktop->path, &st) == 0)
860 	{
861 		if(!S_ISDIR(st.st_mode))
862 			return _desktop_error(NULL, desktop->path,
863 					strerror(ENOTDIR), 1);
864 	}
865 	else if(errno != ENOENT || mkdir(desktop->path, 0777) != 0)
866 		return desktop_error(NULL, desktop->path, 1);
867 	return 0;
868 }
869 
_icons_files_add_home(Desktop * desktop)870 static int _icons_files_add_home(Desktop * desktop)
871 {
872 	DesktopIcon * desktopicon;
873 	GdkPixbuf * icon;
874 
875 	if((desktopicon = desktopicon_new(desktop, _("Home"), desktop->home))
876 			== NULL)
877 		return -_desktop_serror(desktop, "Home", 1);
878 	desktopicon_set_first(desktopicon, TRUE);
879 	desktopicon_set_immutable(desktopicon, TRUE);
880 	icon = gtk_icon_theme_load_icon(desktop->theme, "gnome-home",
881 			DESKTOPICON_ICON_SIZE, 0, NULL);
882 	if(icon == NULL)
883 		icon = gtk_icon_theme_load_icon(desktop->theme, "gnome-fs-home",
884 				DESKTOPICON_ICON_SIZE, 0, NULL);
885 	if(icon != NULL)
886 		desktopicon_set_icon(desktopicon, icon);
887 	if(_desktop_icon_add(desktop, desktopicon) != 0)
888 	{
889 		desktopicon_delete(desktopicon);
890 		return -1;
891 	}
892 	return 0;
893 }
894 
_icons_homescreen(Desktop * desktop)895 static int _icons_homescreen(Desktop * desktop)
896 {
897 	DesktopIcon * desktopicon;
898 	GdkPixbuf * icon;
899 #ifdef EMBEDDED
900 	char const * paths[] =
901 	{
902 		DATADIR "/applications/deforaos-phone-contacts.desktop",
903 		DATADIR "/applications/deforaos-phone-dialer.desktop",
904 		DATADIR "/applications/deforaos-phone-messages.desktop",
905 		NULL
906 	};
907 	char const ** p;
908 #endif
909 
910 	if((desktopicon = desktopicon_new(desktop, _("Applications"), NULL))
911 			== NULL)
912 		return _desktop_serror(desktop, "Applications", 1);
913 	desktopicon_set_callback(desktopicon, _icons_set_categories, NULL);
914 	desktopicon_set_immutable(desktopicon, TRUE);
915 	icon = gtk_icon_theme_load_icon(desktop->theme, "gnome-applications",
916 			DESKTOPICON_ICON_SIZE, 0, NULL);
917 	if(icon != NULL)
918 		desktopicon_set_icon(desktopicon, icon);
919 	_desktop_icon_add(desktop, desktopicon);
920 #ifdef EMBEDDED
921 	for(p = paths; *p != NULL; p++)
922 		if(access(*p, R_OK) == 0
923 				&& (desktopicon = desktopicon_new_application(
924 						desktop, *p, DATADIR)) != NULL)
925 			_desktop_icon_add(desktop, desktopicon);
926 #endif
927 	return 0;
928 }
929 
_icons_reset(Desktop * desktop)930 static void _icons_reset(Desktop * desktop)
931 {
932 	size_t i;
933 
934 	if(desktop->path != NULL)
935 		free(desktop->path);
936 	desktop->path = NULL;
937 	desktop->path_cnt = 0;
938 	for(i = 0; i < desktop->icon_cnt; i++)
939 	{
940 		desktopicon_set_immutable(desktop->icon[i], FALSE);
941 		desktopicon_set_updated(desktop->icon[i], FALSE);
942 	}
943 	for(i = 0; i < _desktop_categories_cnt; i++)
944 		_desktop_categories[i].show = FALSE;
945 }
946 
_icons_set_categories(Desktop * desktop,gpointer data)947 static void _icons_set_categories(Desktop * desktop, gpointer data)
948 {
949 #ifdef DEBUG
950 	fprintf(stderr, "DEBUG: %s()\n", __func__);
951 #endif
952 	desktop_set_icons(desktop, DESKTOP_ICONS_CATEGORIES);
953 }
954 
_icons_set_homescreen(Desktop * desktop,gpointer data)955 static void _icons_set_homescreen(Desktop * desktop, gpointer data)
956 {
957 #ifdef DEBUG
958 	fprintf(stderr, "DEBUG: %s()\n", __func__);
959 #endif
960 	desktop_set_icons(desktop, DESKTOP_ICONS_HOMESCREEN);
961 }
962 
963 
964 /* desktop_set_layout */
desktop_set_layout(Desktop * desktop,DesktopLayout layout)965 int desktop_set_layout(Desktop * desktop, DesktopLayout layout)
966 {
967 	XRRScreenConfiguration * sc;
968 	Rotation r;
969 	SizeID size;
970 
971 	sc = XRRGetScreenInfo(GDK_DISPLAY_XDISPLAY(desktop->display),
972 			GDK_WINDOW_XID(desktop->root));
973 	size = XRRConfigCurrentConfiguration(sc, &r);
974 	switch(layout)
975 	{
976 		case DESKTOP_LAYOUT_NORMAL:
977 		case DESKTOP_LAYOUT_LANDSCAPE:
978 			r = RR_Rotate_0;
979 			break;
980 		case DESKTOP_LAYOUT_PORTRAIT:
981 			r = RR_Rotate_90;
982 			break;
983 		case DESKTOP_LAYOUT_ROTATE:
984 			r <<= 1;
985 			r = (r > 16) ? 1 : r;
986 			break;
987 		case DESKTOP_LAYOUT_TOGGLE:
988 			r = (r != RR_Rotate_0) ? RR_Rotate_0 : RR_Rotate_90;
989 			break;
990 	}
991 	gdk_error_trap_push();
992 	XRRSetScreenConfig(GDK_DISPLAY_XDISPLAY(desktop->display), sc,
993 			GDK_WINDOW_XID(desktop->root), size, r, CurrentTime);
994 	return gdk_error_trap_pop();
995 }
996 
997 
998 /* useful */
999 /* desktop_error */
desktop_error(Desktop * desktop,char const * message,int ret)1000 int desktop_error(Desktop * desktop, char const * message, int ret)
1001 {
1002 	/* FIXME no longer assume errno is properly set */
1003 	return _desktop_error(desktop, message, strerror(errno), ret);
1004 }
1005 
1006 
1007 /* desktop_refresh */
1008 static void _refresh_categories(Desktop * desktop);
1009 static void _refresh_files(Desktop * desktop);
1010 static void _refresh_homescreen(Desktop * desktop);
1011 
desktop_refresh(Desktop * desktop)1012 void desktop_refresh(Desktop * desktop)
1013 {
1014 #ifdef DEBUG
1015 	fprintf(stderr, "DEBUG: %s()\n", __func__);
1016 #endif
1017 	if(desktop->refresh_source != 0)
1018 		g_source_remove(desktop->refresh_source);
1019 	switch(desktop->prefs.icons)
1020 	{
1021 		case DESKTOP_ICONS_APPLICATIONS:
1022 		case DESKTOP_ICONS_CATEGORIES:
1023 			_refresh_categories(desktop);
1024 			break;
1025 		case DESKTOP_ICONS_FILES:
1026 			_refresh_files(desktop);
1027 			break;
1028 		case DESKTOP_ICONS_HOMESCREEN:
1029 		case DESKTOP_ICONS_NONE:
1030 			_refresh_homescreen(desktop);
1031 			break;
1032 	}
1033 }
1034 
_refresh_categories(Desktop * desktop)1035 static void _refresh_categories(Desktop * desktop)
1036 {
1037 	g_slist_foreach(desktop->apps, (GFunc)config_delete, NULL);
1038 	g_slist_free(desktop->apps);
1039 	desktop->apps = NULL;
1040 	desktop->refresh_source = g_idle_add(_desktop_on_refresh, desktop);
1041 }
1042 
_refresh_files(Desktop * desktop)1043 static void _refresh_files(Desktop * desktop)
1044 {
1045 	struct stat st;
1046 
1047 	if(desktop->path == NULL)
1048 		return;
1049 	if((desktop->refresh_dir = browser_vfs_opendir(desktop->path, &st))
1050 			== NULL)
1051 	{
1052 		desktop_error(NULL, desktop->path, 1);
1053 		desktop->refresh_source = 0;
1054 		return;
1055 	}
1056 	desktop->refresh_mti = st.st_mtime;
1057 	desktop->refresh_source = g_idle_add(_desktop_on_refresh, desktop);
1058 }
1059 
_refresh_homescreen(Desktop * desktop)1060 static void _refresh_homescreen(Desktop * desktop)
1061 {
1062 	/* for cleanup */
1063 	desktop->refresh_source = g_idle_add(_desktop_on_refresh, desktop);
1064 }
1065 
1066 
1067 /* desktop_reset */
1068 static void _reset_background(Desktop * desktop, Config * config);
1069 static void _reset_icons(Desktop * desktop, Config * config);
1070 static void _reset_icons_colors(Desktop * desktop, Config * config);
1071 static void _reset_icons_font(Desktop * desktop, Config * config);
1072 static void _reset_icons_monitor(Desktop * desktop, Config * config);
1073 /* callbacks */
1074 static gboolean _reset_on_idle(gpointer data);
1075 
desktop_reset(Desktop * desktop)1076 void desktop_reset(Desktop * desktop)
1077 {
1078 	if(desktop->refresh_source != 0)
1079 		g_source_remove(desktop->refresh_source);
1080 	desktop->refresh_source = g_idle_add(_reset_on_idle, desktop);
1081 }
1082 
_reset_background(Desktop * desktop,Config * config)1083 static void _reset_background(Desktop * desktop, Config * config)
1084 {
1085 #if GTK_CHECK_VERSION(3, 0, 0)
1086 	GdkRGBA color = { 0.0, 0.0, 0.0, 0.0 };
1087 #else
1088 	GdkColor color = { 0, 0, 0, 0 };
1089 #endif
1090 	char const * filename;
1091 	DesktopHows how = DESKTOP_HOW_SCALED;
1092 	gboolean extend = FALSE;
1093 	size_t i;
1094 	char const * p;
1095 
1096 	if((p = config_get(config, "background", "color")) != NULL)
1097 #if GTK_CHECK_VERSION(3, 0, 0)
1098 		gdk_rgba_parse(&color, p);
1099 #else
1100 		gdk_color_parse(p, &color);
1101 #endif
1102 	filename = config_get(config, "background", "wallpaper");
1103 	if((p = config_get(config, "background", "how")) != NULL)
1104 		for(i = 0; i < DESKTOP_HOW_COUNT; i++)
1105 			if(strcmp(_desktop_hows[i], p) == 0)
1106 				how = i;
1107 	if((p = config_get(config, "background", "extend")) != NULL)
1108 		extend = strtol(p, NULL, 10) ? TRUE : FALSE;
1109 	_desktop_draw_background(desktop, &color, filename, how, extend);
1110 }
1111 
_reset_icons(Desktop * desktop,Config * config)1112 static void _reset_icons(Desktop * desktop, Config * config)
1113 {
1114 	String const * p;
1115 	String * q;
1116 	size_t i;
1117 	int j;
1118 
1119 	_reset_icons_colors(desktop, config);
1120 	_reset_icons_font(desktop, config);
1121 	for(i = 0; i < desktop->icon_cnt; i++)
1122 	{
1123 		desktopicon_set_background(desktop->icon[i],
1124 				&desktop->background);
1125 		desktopicon_set_font(desktop->icon[i], desktop->font);
1126 		desktopicon_set_foreground(desktop->icon[i],
1127 				&desktop->foreground);
1128 	}
1129 	_reset_icons_monitor(desktop, config);
1130 	/* icons layout */
1131 	if(desktop->prefs.icons < 0
1132 			&& (p = config_get(config, "icons", "layout")) != NULL)
1133 	{
1134 		for(i = 0; i < DESKTOP_ICONS_COUNT; i++)
1135 			if(strcmp(_desktop_icons_config[i], p) == 0)
1136 			{
1137 				desktop->prefs.icons = i;
1138 				break;
1139 			}
1140 	}
1141 	if(desktop->prefs.icons < 0
1142 			|| desktop->prefs.icons >= DESKTOP_ICONS_COUNT)
1143 		desktop->prefs.icons = DESKTOP_ICONS_FILES;
1144 	/* icons alignment */
1145 	if(desktop->prefs.alignment < 0)
1146 		desktop->prefs.alignment = (desktop->prefs.icons
1147 				== DESKTOP_ICONS_FILES)
1148 			? DESKTOP_ALIGNMENT_VERTICAL
1149 			: DESKTOP_ALIGNMENT_HORIZONTAL;
1150 	/* show hidden */
1151 	if((p = config_get(config, "icons", "show_hidden")) != NULL)
1152 	{
1153 		j = strtol(p, &q, 10);
1154 		if(p[0] == '\0' || *q != '\0' || j < 0)
1155 			j = 0;
1156 		desktop->show_hidden = j ? 1 : 0;
1157 	}
1158 }
1159 
_reset_icons_colors(Desktop * desktop,Config * config)1160 static void _reset_icons_colors(Desktop * desktop, Config * config)
1161 {
1162 	String const * p;
1163 #if GTK_CHECK_VERSION(3, 0, 0)
1164 	GdkRGBA color;
1165 #else
1166 	GdkColor color;
1167 #endif
1168 
1169 	if((p = config_get(config, "icons", "background")) != NULL)
1170 	{
1171 #if GTK_CHECK_VERSION(3, 0, 0)
1172 		gdk_rgba_parse(&color, p);
1173 #else
1174 		gdk_color_parse(p, &color);
1175 #endif
1176 		desktop->background = color;
1177 	}
1178 	if((p = config_get(config, "icons", "foreground")) != NULL)
1179 	{
1180 #if GTK_CHECK_VERSION(3, 0, 0)
1181 		gdk_rgba_parse(&color, p);
1182 #else
1183 		gdk_color_parse(p, &color);
1184 #endif
1185 		desktop->foreground = color;
1186 	}
1187 }
1188 
_reset_icons_font(Desktop * desktop,Config * config)1189 static void _reset_icons_font(Desktop * desktop, Config * config)
1190 {
1191 	String const * p;
1192 
1193 	if(desktop->font != NULL)
1194 		pango_font_description_free(desktop->font);
1195 	if((p = config_get(config, "icons", "font")) != NULL)
1196 		desktop->font = pango_font_description_from_string(p);
1197 	else
1198 	{
1199 		desktop->font = pango_font_description_new();
1200 		pango_font_description_set_weight(desktop->font,
1201 				PANGO_WEIGHT_BOLD);
1202 	}
1203 }
1204 
_reset_icons_monitor(Desktop * desktop,Config * config)1205 static void _reset_icons_monitor(Desktop * desktop, Config * config)
1206 {
1207 	String const * p;
1208 	char * q;
1209 
1210 	/* icons monitor */
1211 	if(desktop->prefs.monitor < 0
1212 			&& (p = config_get(config, "icons", "monitor")) != NULL)
1213 	{
1214 		desktop->prefs.monitor = strtol(p, &q, 10);
1215 		if(p[0] == '\0' || *q != '\0')
1216 			desktop->prefs.monitor = -1;
1217 	}
1218 }
1219 
1220 /* callbacks */
_reset_on_idle(gpointer data)1221 static gboolean _reset_on_idle(gpointer data)
1222 {
1223 	Desktop * desktop = data;
1224 	Config * config;
1225 
1226 #ifdef DEBUG
1227 	fprintf(stderr, "DEBUG: %s()\n", __func__);
1228 #endif
1229 	desktop->refresh_source = 0;
1230 	if((config = _desktop_get_config(desktop)) == NULL)
1231 		return FALSE;
1232 	_reset_background(desktop, config);
1233 	_reset_icons(desktop, config);
1234 	config_delete(config);
1235 	_desktop_get_workarea(desktop);
1236 	desktop_set_icons(desktop, desktop->prefs.icons);
1237 	return FALSE;
1238 }
1239 
1240 
1241 /* desktop_icon_add */
desktop_icon_add(Desktop * desktop,DesktopIcon * icon)1242 void desktop_icon_add(Desktop * desktop, DesktopIcon * icon)
1243 {
1244 	if(_desktop_icon_add(desktop, icon) == 0)
1245 		desktop_icons_align(desktop);
1246 }
1247 
1248 
1249 /* desktop_icon_remove */
desktop_icon_remove(Desktop * desktop,DesktopIcon * icon)1250 void desktop_icon_remove(Desktop * desktop, DesktopIcon * icon)
1251 {
1252 	if(_desktop_icon_remove(desktop, icon) == 0)
1253 		desktop_icons_align(desktop);
1254 }
1255 
1256 
1257 /* desktop_icons_align */
1258 static int _align_compare(const void * a, const void * b);
1259 
desktop_icons_align(Desktop * desktop)1260 void desktop_icons_align(Desktop * desktop)
1261 {
1262 	qsort(desktop->icon, desktop->icon_cnt, sizeof(void *), _align_compare);
1263 	desktop_set_alignment(desktop, desktop->prefs.alignment);
1264 }
1265 
_align_compare(const void * a,const void * b)1266 static int _align_compare(const void * a, const void * b)
1267 {
1268 	DesktopIcon * icona = *(DesktopIcon**)a;
1269 	DesktopIcon * iconb = *(DesktopIcon**)b;
1270 	gboolean firsta = desktopicon_get_first(icona);
1271 	gboolean firstb = desktopicon_get_first(iconb);
1272 	gboolean dira;
1273 	gboolean dirb;
1274 
1275 	if(firsta && !firstb)
1276 		return -1;
1277 	else if(!firsta && firstb)
1278 		return 1;
1279 	dira = desktopicon_get_isdir(icona);
1280 	dirb = desktopicon_get_isdir(iconb);
1281 	if(dira && !dirb)
1282 		return -1;
1283 	else if(!dira && dirb)
1284 		return 1;
1285 	return strcmp(desktopicon_get_name(icona), desktopicon_get_name(iconb));
1286 }
1287 
1288 
1289 /* desktop_select_all */
desktop_select_all(Desktop * desktop)1290 void desktop_select_all(Desktop * desktop)
1291 {
1292 	size_t i;
1293 
1294 	for(i = 0; i < desktop->icon_cnt; i++)
1295 		desktopicon_set_selected(desktop->icon[i], TRUE);
1296 }
1297 
1298 
1299 /* desktop_select_above */
desktop_select_above(Desktop * desktop,DesktopIcon * icon)1300 void desktop_select_above(Desktop * desktop, DesktopIcon * icon)
1301 	/* FIXME icons may be wrapped */
1302 {
1303 	size_t i;
1304 
1305 	for(i = 1; i < desktop->icon_cnt; i++)
1306 		if(desktop->icon[i] == icon)
1307 		{
1308 			desktopicon_set_selected(desktop->icon[i], TRUE);
1309 			return;
1310 		}
1311 }
1312 
1313 
1314 /* desktop_select_under */
desktop_select_under(Desktop * desktop,DesktopIcon * icon)1315 void desktop_select_under(Desktop * desktop, DesktopIcon * icon)
1316 	/* FIXME icons may be wrapped */
1317 {
1318 	size_t i;
1319 
1320 	for(i = 0; i < desktop->icon_cnt; i++)
1321 		if(desktop->icon[i] == icon && i + 1 < desktop->icon_cnt)
1322 		{
1323 			desktopicon_set_selected(desktop->icon[i], TRUE);
1324 			return;
1325 		}
1326 }
1327 
1328 
1329 /* desktop_unselect_all */
desktop_unselect_all(Desktop * desktop)1330 void desktop_unselect_all(Desktop * desktop)
1331 {
1332 	size_t i;
1333 
1334 	for(i = 0; i < desktop->icon_cnt; i++)
1335 		desktopicon_set_selected(desktop->icon[i], FALSE);
1336 }
1337 
1338 
1339 /* private */
1340 /* functions */
1341 /* desktop_error */
1342 static int _error_text(char const * message, char const * error, int ret);
1343 
_desktop_error(Desktop * desktop,char const * message,char const * error,int ret)1344 static int _desktop_error(Desktop * desktop, char const * message,
1345 		char const * error, int ret)
1346 {
1347 	GtkWidget * dialog;
1348 
1349 	if(desktop == NULL)
1350 		return _error_text(message, error, ret);
1351 	dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR,
1352 			GTK_BUTTONS_CLOSE, "%s",
1353 #if GTK_CHECK_VERSION(2, 6, 0)
1354 			_("Error"));
1355 	gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1356 			"%s: %s", message,
1357 #endif
1358 			error);
1359 	gtk_window_set_title(GTK_WINDOW(dialog), _("Error"));
1360 	if(ret < 0)
1361 	{
1362 		g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(
1363 					gtk_main_quit), NULL);
1364 		ret = -ret;
1365 	}
1366 	else
1367 		g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(
1368 					gtk_widget_destroy), NULL);
1369 	gtk_widget_show(dialog);
1370 	return ret;
1371 }
1372 
_error_text(char const * message,char const * error,int ret)1373 static int _error_text(char const * message, char const * error, int ret)
1374 {
1375 	fprintf(stderr, "%s: %s%s%s\n", PROGNAME,
1376 			(message != NULL) ? message : "",
1377 			(message != NULL) ? ": " : "", error);
1378 	return ret;
1379 }
1380 
1381 
1382 /* desktop_serror */
_desktop_serror(Desktop * desktop,char const * message,int ret)1383 static int _desktop_serror(Desktop * desktop, char const * message, int ret)
1384 {
1385 	return _desktop_error(desktop, message, error_get(NULL), ret);
1386 }
1387 
1388 
1389 /* desktop_get_config */
_desktop_get_config(Desktop * desktop)1390 static Config * _desktop_get_config(Desktop * desktop)
1391 {
1392 	Config * config;
1393 	String * pathname = NULL;
1394 
1395 	if((config = config_new()) == NULL
1396 			|| (pathname = string_new_append(desktop->home,
1397 					"/" DESKTOPRC, NULL)) == NULL)
1398 	{
1399 		if(config != NULL)
1400 			config_delete(config);
1401 		if(pathname != NULL)
1402 			object_delete(pathname);
1403 		_desktop_serror(NULL, _("Could not load preferences"), FALSE);
1404 		return NULL;
1405 	}
1406 	config_load(config, pathname); /* XXX ignore errors */
1407 	return config;
1408 }
1409 
1410 
1411 /* desktop_get_workarea */
_desktop_get_workarea(Desktop * desktop)1412 static int _desktop_get_workarea(Desktop * desktop)
1413 {
1414 	Atom atom;
1415 	Atom type;
1416 	int format;
1417 	unsigned long cnt;
1418 	unsigned long bytes;
1419 	unsigned char * p = NULL;
1420 	unsigned long * u;
1421 	int res;
1422 
1423 	if(desktop->prefs.monitor >= 0 && desktop->prefs.monitor
1424 			< gdk_screen_get_n_monitors(desktop->screen))
1425 	{
1426 		gdk_screen_get_monitor_geometry(desktop->screen,
1427 				desktop->prefs.monitor, &desktop->workarea);
1428 #ifdef DEBUG
1429 		fprintf(stderr, "DEBUG: %s() (%d, %d) %dx%d\n", __func__,
1430 				desktop->workarea.x, desktop->workarea.y,
1431 				desktop->workarea.width,
1432 				desktop->workarea.height);
1433 #endif
1434 		desktop_icons_align(desktop);
1435 		return 0;
1436 	}
1437 	atom = gdk_x11_get_xatom_by_name("_NET_WORKAREA");
1438 	gdk_error_trap_push();
1439 	res = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(desktop->display),
1440 				GDK_WINDOW_XID(desktop->root), atom, 0,
1441 				G_MAXLONG, False, XA_CARDINAL, &type, &format,
1442 				&cnt, &bytes, &p);
1443 	if(gdk_error_trap_pop() || res != Success || cnt < 4)
1444 		gdk_screen_get_monitor_geometry(desktop->screen, 0,
1445 				&desktop->workarea);
1446 	else
1447 	{
1448 		u = (unsigned long *)p;
1449 		desktop->workarea.x = u[0];
1450 		desktop->workarea.y = u[1];
1451 		if((desktop->workarea.width = u[2]) == 0
1452 				|| (desktop->workarea.height = u[3]) == 0)
1453 			gdk_screen_get_monitor_geometry(desktop->screen, 0,
1454 					&desktop->workarea);
1455 	}
1456 	if(p != NULL)
1457 		XFree(p);
1458 #ifdef DEBUG
1459 	fprintf(stderr, "DEBUG: %s() (%d, %d) %dx%d\n", __func__,
1460 			desktop->workarea.x, desktop->workarea.y,
1461 			desktop->workarea.width, desktop->workarea.height);
1462 #endif
1463 	desktop_icons_align(desktop);
1464 	return 0;
1465 }
1466 
1467 
1468 /* useful */
1469 /* desktop_background */
1470 static gboolean _background_how_centered(Desktop * desktop,
1471 		GdkRectangle * window, char const * filename, GError ** error);
1472 static gboolean _background_how_scaled(Desktop * desktop, GdkRectangle * window,
1473 		char const * filename, GError ** error);
1474 static gboolean _background_how_scaled_ratio(Desktop * desktop,
1475 		GdkRectangle * window, char const * filename, GError ** error);
1476 static gboolean _background_how_tiled(Desktop * desktop, GdkRectangle * window,
1477 		char const * filename, GError ** error);
1478 static void _background_monitor(Desktop * desktop, char const * filename,
1479 		DesktopHows how, gboolean extend, GdkRectangle * window,
1480 		int monitor);
1481 static void _background_monitors(Desktop * desktop, char const * filename,
1482 		DesktopHows how, gboolean extend, GdkRectangle * window);
1483 
1484 #if GTK_CHECK_VERSION(3, 0, 0)
_desktop_draw_background(Desktop * desktop,GdkRGBA * color,char const * filename,DesktopHows how,gboolean extend)1485 static void _desktop_draw_background(Desktop * desktop, GdkRGBA * color,
1486 		char const * filename, DesktopHows how, gboolean extend)
1487 #else
1488 static void _desktop_draw_background(Desktop * desktop, GdkColor * color,
1489 		char const * filename, DesktopHows how, gboolean extend)
1490 #endif
1491 {
1492 #if !GTK_CHECK_VERSION(3, 0, 0)
1493 	GdkGC * gc;
1494 	GtkStyle * style;
1495 #endif
1496 
1497 #ifdef DEBUG
1498 	fprintf(stderr, "DEBUG: %s(\"%s\", %u, %s)\n", __func__, filename, how,
1499 			extend ? "TRUE" : "FALSE");
1500 #endif
1501 	if(how == DESKTOP_HOW_NONE)
1502 		return;
1503 #if GTK_CHECK_VERSION(3, 0, 0)
1504 	desktop->cairo = gdk_cairo_create(desktop->root);
1505 	cairo_set_source_rgba(desktop->cairo, color->red, color->green,
1506 			color->blue, 1.0);
1507 	gdk_cairo_rectangle(desktop->cairo, &desktop->window);
1508 	if(filename != NULL)
1509 		/* draw the background */
1510 		_background_monitors(desktop, filename, how, extend,
1511 				&desktop->window);
1512 	cairo_paint(desktop->cairo);
1513 	cairo_destroy(desktop->cairo);
1514 	desktop->cairo = NULL;
1515 #else
1516 	/* draw default color */
1517 	desktop->pixmap = gdk_pixmap_new(desktop->back, desktop->window.width,
1518 			desktop->window.height, -1);
1519 	gc = gdk_gc_new(desktop->pixmap);
1520 	gdk_gc_set_rgb_fg_color(gc, color);
1521 	gdk_draw_rectangle(desktop->pixmap, gc, TRUE, 0, 0,
1522 			desktop->window.width, desktop->window.height);
1523 	if(filename != NULL)
1524 		/* draw the background */
1525 		_background_monitors(desktop, filename, how, extend,
1526 				&desktop->window);
1527 	if(desktop->desktop != NULL)
1528 	{
1529 		style = gtk_style_new();
1530 		style->bg_pixmap[GTK_STATE_NORMAL] = desktop->pixmap;
1531 		gtk_widget_set_style(desktop->desktop, style);
1532 	}
1533 	else
1534 	{
1535 		gdk_window_set_back_pixmap(desktop->back, desktop->pixmap,
1536 				FALSE);
1537 		gdk_window_clear(desktop->back);
1538 		gdk_pixmap_unref(desktop->pixmap);
1539 	}
1540 	desktop->pixmap = NULL;
1541 #endif
1542 }
1543 
_background_how_centered(Desktop * desktop,GdkRectangle * window,char const * filename,GError ** error)1544 static gboolean _background_how_centered(Desktop * desktop,
1545 		GdkRectangle * window, char const * filename, GError ** error)
1546 {
1547 	GdkPixbuf * background;
1548 	gint w;
1549 	gint h;
1550 	gint x;
1551 	gint y;
1552 
1553 	if((background = gdk_pixbuf_new_from_file(filename, error)) == NULL)
1554 		return FALSE;
1555 	w = gdk_pixbuf_get_width(background);
1556 	h = gdk_pixbuf_get_height(background);
1557 	x = (window->width - w) / 2 + window->x;
1558 	y = (window->height - h) / 2 + window->y;
1559 #if GTK_CHECK_VERSION(3, 0, 0)
1560 	gdk_cairo_set_source_pixbuf(desktop->cairo, background, x, y);
1561 #else
1562 	gdk_draw_pixbuf(desktop->pixmap, NULL, background, 0, 0, x, y, w, h,
1563 			GDK_RGB_DITHER_NONE, 0, 0);
1564 #endif
1565 	g_object_unref(background);
1566 	return TRUE;
1567 }
1568 
_background_how_scaled(Desktop * desktop,GdkRectangle * window,char const * filename,GError ** error)1569 static gboolean _background_how_scaled(Desktop * desktop, GdkRectangle * window,
1570 		char const * filename, GError ** error)
1571 {
1572 	GdkPixbuf * background;
1573 	gint w;
1574 	gint h;
1575 	gint x;
1576 	gint y;
1577 
1578 #if GTK_CHECK_VERSION(2, 6, 0)
1579 	background = gdk_pixbuf_new_from_file_at_scale(filename, window->width,
1580 			window->height, FALSE, error);
1581 #elif GTK_CHECK_VERSION(2, 4, 0)
1582 	background = gdk_pixbuf_new_from_file_at_size(filename, window->width,
1583 			window->height, error);
1584 #else
1585 	background = gdk_pixbuf_new_from_file(filename, error);
1586 #endif
1587 	if(background == NULL)
1588 		return FALSE;
1589 	w = gdk_pixbuf_get_width(background);
1590 	h = gdk_pixbuf_get_height(background);
1591 	x = (window->width - w) / 2 + window->x;
1592 	y = (window->height - h) / 2 + window->y;
1593 #if GTK_CHECK_VERSION(3, 0, 0)
1594 	gdk_cairo_set_source_pixbuf(desktop->cairo, background, x, y);
1595 #else
1596 	gdk_draw_pixbuf(desktop->pixmap, NULL, background, 0, 0, x, y, w, h,
1597 			GDK_RGB_DITHER_NONE, 0, 0);
1598 #endif
1599 	g_object_unref(background);
1600 	return TRUE;
1601 }
1602 
_background_how_scaled_ratio(Desktop * desktop,GdkRectangle * window,char const * filename,GError ** error)1603 static gboolean _background_how_scaled_ratio(Desktop * desktop,
1604 		GdkRectangle * window, char const * filename, GError ** error)
1605 {
1606 #if GTK_CHECK_VERSION(2, 4, 0)
1607 	GdkPixbuf * background;
1608 	gint w;
1609 	gint h;
1610 	gint x;
1611 	gint y;
1612 
1613 	background = gdk_pixbuf_new_from_file_at_size(filename, window->width,
1614 			window->height, error);
1615 	if(background == NULL)
1616 		return FALSE;
1617 	w = gdk_pixbuf_get_width(background);
1618 	h = gdk_pixbuf_get_height(background);
1619 	x =(window->width - w) / 2 + window->x;
1620 	y = (window->height - h) / 2 + window->y;
1621 #if GTK_CHECK_VERSION(3, 0, 0)
1622 	gdk_cairo_set_source_pixbuf(desktop->cairo, background, x, y);
1623 #else
1624 	gdk_draw_pixbuf(desktop->pixmap, NULL, background, 0, 0, x, y, w, h,
1625 			GDK_RGB_DITHER_NONE, 0, 0);
1626 #endif
1627 	g_object_unref(background);
1628 	return TRUE;
1629 #else
1630 	return _background_how_scaled(desktop, window, filename, error);
1631 #endif
1632 }
1633 
_background_how_tiled(Desktop * desktop,GdkRectangle * window,char const * filename,GError ** error)1634 static gboolean _background_how_tiled(Desktop * desktop, GdkRectangle * window,
1635 		char const * filename, GError ** error)
1636 {
1637 	GdkPixbuf * background;
1638 	gint w;
1639 	gint h;
1640 	gint i;
1641 	gint j;
1642 
1643 	if((background = gdk_pixbuf_new_from_file(filename, error)) == NULL)
1644 		return FALSE;
1645 	w = gdk_pixbuf_get_width(background);
1646 	h = gdk_pixbuf_get_height(background);
1647 	for(j = 0; j < window->height; j += h)
1648 		for(i = 0; i < window->width; i += w)
1649 #if GTK_CHECK_VERSION(3, 0, 0)
1650 			gdk_cairo_set_source_pixbuf(desktop->cairo, background,
1651 					i + window->x, j + window->y);
1652 #else
1653 			gdk_draw_pixbuf(desktop->pixmap, NULL, background, 0, 0,
1654 					i + window->x, j + window->y, w, h,
1655 					GDK_RGB_DITHER_NONE, 0, 0);
1656 #endif
1657 	g_object_unref(background);
1658 	return TRUE;
1659 }
1660 
_background_monitor(Desktop * desktop,char const * filename,DesktopHows how,gboolean extend,GdkRectangle * window,int monitor)1661 static void _background_monitor(Desktop * desktop, char const * filename,
1662 		DesktopHows how, gboolean extend, GdkRectangle * window,
1663 		int monitor)
1664 {
1665 	GError * error = NULL;
1666 
1667 	if(extend != TRUE)
1668 		gdk_screen_get_monitor_geometry(desktop->screen, monitor,
1669 				window);
1670 	switch(how)
1671 	{
1672 		case DESKTOP_HOW_NONE:
1673 			break;
1674 		case DESKTOP_HOW_CENTERED:
1675 			_background_how_centered(desktop, window, filename,
1676 					&error);
1677 			break;
1678 		case DESKTOP_HOW_SCALED_RATIO:
1679 			_background_how_scaled_ratio(desktop, window, filename,
1680 					&error);
1681 			break;
1682 		case DESKTOP_HOW_TILED:
1683 			_background_how_tiled(desktop, window, filename,
1684 					&error);
1685 			break;
1686 		case DESKTOP_HOW_SCALED:
1687 			_background_how_scaled(desktop, window, filename,
1688 					&error);
1689 			break;
1690 	}
1691 	if(error != NULL)
1692 	{
1693 		desktop_error(desktop, error->message, 1);
1694 		g_error_free(error);
1695 	}
1696 }
1697 
_background_monitors(Desktop * desktop,char const * filename,DesktopHows how,gboolean extend,GdkRectangle * window)1698 static void _background_monitors(Desktop * desktop, char const * filename,
1699 		DesktopHows how, gboolean extend, GdkRectangle * window)
1700 {
1701 	gint n;
1702 	gint i;
1703 
1704 	n = (extend != TRUE) ? gdk_screen_get_n_monitors(desktop->screen) : 1;
1705 	for(i = 0; i < n; i++)
1706 		_background_monitor(desktop, filename, how, extend, window, i);
1707 }
1708 
1709 
1710 /* desktop_icon_add */
_desktop_icon_add(Desktop * desktop,DesktopIcon * icon)1711 static int _desktop_icon_add(Desktop * desktop, DesktopIcon * icon)
1712 {
1713 	DesktopIcon ** p;
1714 
1715 	if((p = realloc(desktop->icon, sizeof(*p) * (desktop->icon_cnt + 1)))
1716 			== NULL)
1717 		return -desktop_error(desktop, desktopicon_get_name(icon), 1);
1718 	desktop->icon = p;
1719 	desktop->icon[desktop->icon_cnt++] = icon;
1720 	desktopicon_set_background(icon, &desktop->background);
1721 	desktopicon_set_font(icon, desktop->font);
1722 	desktopicon_set_foreground(icon, &desktop->foreground);
1723 	desktopicon_show(icon);
1724 	return 0;
1725 }
1726 
1727 
1728 /* desktop_icon_remove */
_desktop_icon_remove(Desktop * desktop,DesktopIcon * icon)1729 static int _desktop_icon_remove(Desktop * desktop, DesktopIcon * icon)
1730 {
1731 	size_t i;
1732 	DesktopIcon ** p;
1733 
1734 	for(i = 0; i < desktop->icon_cnt; i++)
1735 	{
1736 		if(desktop->icon[i] != icon)
1737 			continue;
1738 		desktopicon_delete(icon);
1739 		for(desktop->icon_cnt--; i < desktop->icon_cnt; i++)
1740 			desktop->icon[i] = desktop->icon[i + 1];
1741 		if((p = realloc(desktop->icon, sizeof(*p)
1742 						* (desktop->icon_cnt))) != NULL)
1743 			desktop->icon = p; /* we can ignore errors... */
1744 		else if(desktop->icon_cnt == 0)
1745 			desktop->icon = NULL; /* ...except when it's not one */
1746 		return 0;
1747 	}
1748 	return 1;
1749 }
1750 
1751 
1752 /* desktop_show_preferences */
1753 static void _preferences_background(Desktop * desktop, GtkWidget * notebook);
1754 static void _preferences_icons(Desktop * desktop, GtkWidget * notebook);
1755 static void _preferences_monitors(Desktop * desktop, GtkWidget * notebook);
1756 static void _preferences_set(Desktop * desktop);
1757 static void _preferences_set_color(Config * config, char const * section,
1758 		char const * variable, char const * fallback,
1759 		GtkWidget * widget);
1760 static gboolean _on_preferences_closex(gpointer data);
1761 static void _on_preferences_monitors_changed(gpointer data);
1762 static void _on_preferences_monitors_refresh(gpointer data);
1763 static void _on_preferences_response(GtkWidget * widget, gint response,
1764 		gpointer data);
1765 static void _on_preferences_response_apply(gpointer data);
1766 static void _on_preferences_response_cancel(gpointer data);
1767 static void _on_preferences_response_ok(gpointer data);
1768 static void _on_preferences_update_preview(gpointer data);
1769 
_desktop_show_preferences(Desktop * desktop)1770 static void _desktop_show_preferences(Desktop * desktop)
1771 {
1772 	GtkWidget * vbox;
1773 	GtkWidget * notebook;
1774 
1775 	if(desktop->menu != NULL)
1776 		gtk_widget_destroy(desktop->menu);
1777 	desktop->menu = NULL;
1778 	if(desktop->pr_window != NULL)
1779 	{
1780 		gtk_window_present(GTK_WINDOW(desktop->pr_window));
1781 		return;
1782 	}
1783 	/* window */
1784 	desktop->pr_window = gtk_dialog_new_with_buttons(
1785 			_("Desktop preferences"), NULL,
1786 			GTK_DIALOG_DESTROY_WITH_PARENT,
1787 			GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1788 			GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
1789 			GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
1790 	gtk_window_set_position(GTK_WINDOW(desktop->pr_window),
1791 			GTK_WIN_POS_CENTER);
1792 	gtk_window_set_resizable(GTK_WINDOW(desktop->pr_window), FALSE);
1793 	g_signal_connect_swapped(G_OBJECT(desktop->pr_window), "delete-event",
1794 			G_CALLBACK(_on_preferences_closex), desktop);
1795 	g_signal_connect(G_OBJECT(desktop->pr_window), "response", G_CALLBACK(
1796 				_on_preferences_response), desktop);
1797 #if GTK_CHECK_VERSION(2, 14, 0)
1798 	vbox = gtk_dialog_get_content_area(GTK_DIALOG(desktop->pr_window));
1799 #else
1800 	vbox = GTK_DIALOG(desktop->pr_window)->vbox;
1801 #endif
1802 	/* notebook */
1803 	notebook = gtk_notebook_new();
1804 	_preferences_background(desktop, notebook);
1805 	_preferences_icons(desktop, notebook);
1806 	_preferences_monitors(desktop, notebook);
1807 	_on_preferences_monitors_refresh(desktop);
1808 	gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
1809 	/* container */
1810 	_preferences_set(desktop);
1811 	gtk_widget_show_all(desktop->pr_window);
1812 }
1813 
_preferences_background(Desktop * desktop,GtkWidget * notebook)1814 static void _preferences_background(Desktop * desktop, GtkWidget * notebook)
1815 {
1816 	GtkSizeGroup * group;
1817 	GtkWidget * vbox2;
1818 	GtkWidget * hbox;
1819 	GtkWidget * label;
1820 	GtkFileFilter * filter;
1821 
1822 #if GTK_CHECK_VERSION(3, 0, 0)
1823 	vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
1824 #else
1825 	vbox2 = gtk_vbox_new(FALSE, 4);
1826 #endif
1827 	gtk_container_set_border_width(GTK_CONTAINER(vbox2), 4);
1828 	group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1829 #if GTK_CHECK_VERSION(3, 0, 0)
1830 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1831 #else
1832 	hbox = gtk_hbox_new(FALSE, 0);
1833 #endif
1834 	label = gtk_label_new(_("Default color: "));
1835 #if GTK_CHECK_VERSION(3, 0, 0)
1836 	g_object_set(label, "halign", GTK_ALIGN_START, NULL);
1837 #else
1838 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1839 #endif
1840 	gtk_size_group_add_widget(group, label);
1841 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
1842 	desktop->pr_color = gtk_color_button_new();
1843 	gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_color, TRUE, TRUE, 0);
1844 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
1845 #if GTK_CHECK_VERSION(3, 0, 0)
1846 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1847 #else
1848 	hbox = gtk_hbox_new(FALSE, 0);
1849 #endif
1850 	label = gtk_label_new(_("Wallpaper: "));
1851 #if GTK_CHECK_VERSION(3, 0, 0)
1852 	g_object_set(label, "halign", GTK_ALIGN_START, NULL);
1853 #else
1854 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1855 #endif
1856 	gtk_size_group_add_widget(group, label);
1857 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
1858 	desktop->pr_background = gtk_file_chooser_button_new(_("Background"),
1859 			GTK_FILE_CHOOSER_ACTION_OPEN);
1860 	filter = gtk_file_filter_new();
1861 	gtk_file_filter_set_name(filter, _("Picture files"));
1862 	gtk_file_filter_add_mime_type(filter, "image/bmp");
1863 	gtk_file_filter_add_mime_type(filter, "image/gif");
1864 	gtk_file_filter_add_mime_type(filter, "image/jpeg");
1865 	gtk_file_filter_add_mime_type(filter, "image/pbm");
1866 	gtk_file_filter_add_mime_type(filter, "image/png");
1867 	gtk_file_filter_add_mime_type(filter, "image/svg+xml");
1868 	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(desktop->pr_background),
1869 			filter);
1870 	filter = gtk_file_filter_new();
1871 	gtk_file_filter_set_name(filter, _("All files"));
1872 	gtk_file_filter_add_pattern(filter, "*");
1873 	gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(desktop->pr_background),
1874 			filter);
1875 	gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(
1876 				desktop->pr_background), gtk_image_new());
1877 	g_signal_connect_swapped(desktop->pr_background, "update-preview",
1878 			G_CALLBACK(_on_preferences_update_preview), desktop);
1879 	gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_background, TRUE, TRUE,
1880 			0);
1881 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
1882 #if GTK_CHECK_VERSION(3, 0, 0)
1883 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1884 #else
1885 	hbox = gtk_hbox_new(FALSE, 0);
1886 #endif
1887 	label = gtk_label_new(_("Position: "));
1888 #if GTK_CHECK_VERSION(3, 0, 0)
1889 	g_object_set(label, "halign", GTK_ALIGN_START, NULL);
1890 #else
1891 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1892 #endif
1893 	gtk_size_group_add_widget(group, label);
1894 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
1895 #if GTK_CHECK_VERSION(2, 24, 0)
1896 	desktop->pr_background_how = gtk_combo_box_text_new();
1897 	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(
1898 				desktop->pr_background_how), _("Do not draw"));
1899 	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(
1900 				desktop->pr_background_how), _("Centered"));
1901 	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(
1902 				desktop->pr_background_how), _("Scaled"));
1903 	gtk_combo_box_text_append_text(
1904 			GTK_COMBO_BOX_TEXT(desktop->pr_background_how),
1905 			_("Scaled (keep ratio)"));
1906 	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(
1907 				desktop->pr_background_how), _("Tiled"));
1908 #else
1909 	desktop->pr_background_how = gtk_combo_box_new_text();
1910 	gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_background_how),
1911 			_("Do not draw"));
1912 	gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_background_how),
1913 			_("Centered"));
1914 	gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_background_how),
1915 			_("Scaled"));
1916 	gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_background_how),
1917 			_("Scaled (keep ratio)"));
1918 	gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_background_how),
1919 			_("Tiled"));
1920 #endif
1921 	gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_background_how, TRUE,
1922 			TRUE, 0);
1923 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
1924 	desktop->pr_background_extend = gtk_check_button_new_with_mnemonic(
1925 			_("E_xtend background to all monitors"));
1926 	gtk_box_pack_start(GTK_BOX(vbox2), desktop->pr_background_extend, FALSE,
1927 			TRUE, 0);
1928 	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(
1929 				_("Background")));
1930 }
1931 
_preferences_icons(Desktop * desktop,GtkWidget * notebook)1932 static void _preferences_icons(Desktop * desktop, GtkWidget * notebook)
1933 {
1934 	GtkSizeGroup * group;
1935 	GtkWidget * vbox2;
1936 	GtkWidget * hbox;
1937 	GtkWidget * label;
1938 	size_t i;
1939 
1940 #if GTK_CHECK_VERSION(3, 0, 0)
1941 	vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
1942 #else
1943 	vbox2 = gtk_vbox_new(FALSE, 4);
1944 #endif
1945 	gtk_container_set_border_width(GTK_CONTAINER(vbox2), 4);
1946 	group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1947 	/* icons */
1948 #if GTK_CHECK_VERSION(3, 0, 0)
1949 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1950 #else
1951 	hbox = gtk_hbox_new(FALSE, 0);
1952 #endif
1953 	label = gtk_label_new(_("Layout: "));
1954 #if GTK_CHECK_VERSION(3, 0, 0)
1955 	g_object_set(label, "halign", GTK_ALIGN_START, NULL);
1956 #else
1957 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1958 #endif
1959 	gtk_size_group_add_widget(group, label);
1960 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
1961 #if GTK_CHECK_VERSION(2, 24, 0)
1962 	desktop->pr_ilayout = gtk_combo_box_text_new();
1963 #else
1964 	desktop->pr_ilayout = gtk_combo_box_new_text();
1965 #endif
1966 	for(i = 0; i < sizeof(_desktop_icons) / sizeof(*_desktop_icons);
1967 			i++)
1968 #if GTK_CHECK_VERSION(2, 24, 0)
1969 		gtk_combo_box_text_append_text(
1970 				GTK_COMBO_BOX_TEXT(desktop->pr_ilayout),
1971 				_(_desktop_icons[i]));
1972 #else
1973 		gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_ilayout),
1974 				_(_desktop_icons[i]));
1975 #endif
1976 	gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_ilayout, TRUE, TRUE, 0);
1977 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
1978 	/* monitor */
1979 #if GTK_CHECK_VERSION(3, 0, 0)
1980 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
1981 #else
1982 	hbox = gtk_hbox_new(FALSE, 0);
1983 #endif
1984 	label = gtk_label_new(_("Monitor: "));
1985 #if GTK_CHECK_VERSION(3, 0, 0)
1986 	g_object_set(label, "halign", GTK_ALIGN_START, NULL);
1987 #else
1988 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
1989 #endif
1990 	gtk_size_group_add_widget(group, label);
1991 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
1992 #if GTK_CHECK_VERSION(2, 24, 0)
1993 	desktop->pr_imonitor = gtk_combo_box_text_new();
1994 #else
1995 	desktop->pr_imonitor = gtk_combo_box_new_text();
1996 #endif
1997 	gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_imonitor, TRUE, TRUE, 0);
1998 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
1999 	/* background color */
2000 #if GTK_CHECK_VERSION(3, 0, 0)
2001 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2002 #else
2003 	hbox = gtk_hbox_new(FALSE, 0);
2004 #endif
2005 	label = gtk_label_new(_("Background color: "));
2006 #if GTK_CHECK_VERSION(3, 0, 0)
2007 	g_object_set(label, "halign", GTK_ALIGN_START, NULL);
2008 #else
2009 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2010 #endif
2011 	gtk_size_group_add_widget(group, label);
2012 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
2013 	desktop->pr_ibcolor = gtk_color_button_new();
2014 	gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_ibcolor, TRUE, TRUE, 0);
2015 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
2016 	/* foreground color */
2017 #if GTK_CHECK_VERSION(3, 0, 0)
2018 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2019 #else
2020 	hbox = gtk_hbox_new(FALSE, 0);
2021 #endif
2022 	label = gtk_label_new(_("Foreground color: "));
2023 #if GTK_CHECK_VERSION(3, 0, 0)
2024 	g_object_set(label, "halign", GTK_ALIGN_START, NULL);
2025 #else
2026 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2027 #endif
2028 	gtk_size_group_add_widget(group, label);
2029 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
2030 	desktop->pr_ifcolor = gtk_color_button_new();
2031 	gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_ifcolor, TRUE, TRUE, 0);
2032 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
2033 	/* font */
2034 #if GTK_CHECK_VERSION(3, 0, 0)
2035 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2036 #else
2037 	hbox = gtk_hbox_new(FALSE, 0);
2038 #endif
2039 	label = gtk_label_new(_("Font: "));
2040 #if GTK_CHECK_VERSION(3, 0, 0)
2041 	g_object_set(label, "halign", GTK_ALIGN_START, NULL);
2042 #else
2043 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2044 #endif
2045 	gtk_size_group_add_widget(group, label);
2046 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
2047 	desktop->pr_ifont = gtk_font_button_new();
2048 	gtk_font_button_set_use_font(GTK_FONT_BUTTON(desktop->pr_ifont), TRUE);
2049 	gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_ifont, TRUE, TRUE, 0);
2050 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
2051 	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(
2052 				_("Icons")));
2053 }
2054 
_preferences_monitors(Desktop * desktop,GtkWidget * notebook)2055 static void _preferences_monitors(Desktop * desktop, GtkWidget * notebook)
2056 {
2057 	GtkSizeGroup * group;
2058 	GtkWidget * vbox2;
2059 	GtkWidget * hbox;
2060 	GtkWidget * label;
2061 	GtkWidget * widget;
2062 
2063 	group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
2064 #if GTK_CHECK_VERSION(3, 0, 0)
2065 	vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
2066 #else
2067 	vbox2 = gtk_vbox_new(FALSE, 4);
2068 #endif
2069 	gtk_container_set_border_width(GTK_CONTAINER(vbox2), 4);
2070 	/* selector */
2071 #if GTK_CHECK_VERSION(3, 0, 0)
2072 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2073 #else
2074 	hbox = gtk_hbox_new(FALSE, 0);
2075 #endif
2076 	label = gtk_label_new(_("Monitor: "));
2077 #if GTK_CHECK_VERSION(3, 0, 0)
2078 	g_object_set(label, "halign", GTK_ALIGN_START, NULL);
2079 #else
2080 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2081 #endif
2082 	gtk_size_group_add_widget(group, label);
2083 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
2084 #if GTK_CHECK_VERSION(2, 24, 0)
2085 	desktop->pr_monitors = gtk_combo_box_text_new();
2086 #else
2087 	desktop->pr_monitors = gtk_combo_box_new_text();
2088 #endif
2089 	gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_monitors, TRUE, TRUE, 0);
2090 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
2091 	/* geometry */
2092 #if GTK_CHECK_VERSION(3, 0, 0)
2093 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2094 #else
2095 	hbox = gtk_hbox_new(FALSE, 0);
2096 #endif
2097 	label = gtk_label_new(_("Resolution: "));
2098 #if GTK_CHECK_VERSION(3, 0, 0)
2099 	g_object_set(label, "halign", GTK_ALIGN_START, NULL);
2100 #else
2101 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2102 #endif
2103 	gtk_size_group_add_widget(group, label);
2104 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
2105 	desktop->pr_monitors_res = gtk_label_new(NULL);
2106 #if GTK_CHECK_VERSION(3, 0, 0)
2107 	g_object_set(desktop->pr_monitors_res, "halign", GTK_ALIGN_START, NULL);
2108 #else
2109 	gtk_misc_set_alignment(GTK_MISC(desktop->pr_monitors_res), 0.0, 0.5);
2110 #endif
2111 	gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_monitors_res, TRUE, TRUE,
2112 			0);
2113 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
2114 	/* size */
2115 #if GTK_CHECK_VERSION(3, 0, 0)
2116 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2117 #else
2118 	hbox = gtk_hbox_new(FALSE, 0);
2119 #endif
2120 	label = gtk_label_new(_("Size: "));
2121 #if GTK_CHECK_VERSION(3, 0, 0)
2122 	g_object_set(label, "halign", GTK_ALIGN_START, NULL);
2123 #else
2124 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
2125 #endif
2126 	gtk_size_group_add_widget(group, label);
2127 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
2128 	desktop->pr_monitors_size = gtk_label_new(NULL);
2129 #if GTK_CHECK_VERSION(3, 0, 0)
2130 	g_object_set(desktop->pr_monitors_size, "halign", GTK_ALIGN_START,
2131 			NULL);
2132 #else
2133 	gtk_misc_set_alignment(GTK_MISC(desktop->pr_monitors_size), 0.0, 0.5);
2134 #endif
2135 	gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_monitors_size, TRUE, TRUE,
2136 			0);
2137 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
2138 	/* refresh */
2139 #if GTK_CHECK_VERSION(3, 0, 0)
2140 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
2141 #else
2142 	hbox = gtk_hbox_new(FALSE, 0);
2143 #endif
2144 	label = gtk_label_new(NULL);
2145 	gtk_size_group_add_widget(group, label);
2146 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
2147 	widget = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
2148 	g_signal_connect_swapped(widget, "clicked", G_CALLBACK(
2149 				_on_preferences_monitors_refresh), desktop);
2150 	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
2151 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
2152 	/* updates */
2153 	g_signal_connect_swapped(desktop->pr_monitors, "changed", G_CALLBACK(
2154 				_on_preferences_monitors_changed), desktop);
2155 	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(
2156 				_("Monitors")));
2157 }
2158 
_on_preferences_closex(gpointer data)2159 static gboolean _on_preferences_closex(gpointer data)
2160 {
2161 	_on_preferences_response_cancel(data);
2162 	return TRUE;
2163 }
2164 
_on_preferences_monitors_changed(gpointer data)2165 static void _on_preferences_monitors_changed(gpointer data)
2166 {
2167 	Desktop * desktop = data;
2168 	gint active;
2169 	GdkRectangle geometry;
2170 	gint width;
2171 	gint height;
2172 	char buf[64];
2173 
2174 	active = gtk_combo_box_get_active(GTK_COMBO_BOX(desktop->pr_monitors));
2175 	geometry.x = 0;
2176 	geometry.y = 0;
2177 	geometry.width = gdk_screen_get_width(desktop->screen);
2178 	geometry.height = gdk_screen_get_height(desktop->screen);
2179 	width = gdk_screen_get_width_mm(desktop->screen);
2180 	height = gdk_screen_get_height_mm(desktop->screen);
2181 #if GTK_CHECK_VERSION(2, 14, 0)
2182 	if(active-- > 0)
2183 	{
2184 		gdk_screen_get_monitor_geometry(desktop->screen, active,
2185 				&geometry);
2186 		width = gdk_screen_get_monitor_width_mm(desktop->screen,
2187 				active);
2188 		height = gdk_screen_get_monitor_height_mm(desktop->screen,
2189 				active);
2190 	}
2191 #endif
2192 	if(width < 0 || height < 0)
2193 		snprintf(buf, sizeof(buf), "%s", _("Unknown size"));
2194 	else
2195 		snprintf(buf, sizeof(buf), _("%dx%d (at %d,%d)"),
2196 				geometry.width, geometry.height,
2197 				geometry.x, geometry.y);
2198 	gtk_label_set_text(GTK_LABEL(desktop->pr_monitors_res), buf);
2199 	if(width < 0 || height < 0)
2200 		snprintf(buf, sizeof(buf), "%s", _("Unknown resolution"));
2201 	else
2202 		snprintf(buf, sizeof(buf), _("%dx%d mm (%.0fx%.0f DPI)"),
2203 				width, height, geometry.width * 25.4 / width,
2204 				geometry.height * 25.4 / height);
2205 	gtk_label_set_text(GTK_LABEL(desktop->pr_monitors_size), buf);
2206 }
2207 
_on_preferences_monitors_refresh(gpointer data)2208 static void _on_preferences_monitors_refresh(gpointer data)
2209 {
2210 	Desktop * desktop = data;
2211 	GtkTreeModel * model1;
2212 	GtkTreeModel * model2;
2213 	gint active;
2214 #if GTK_CHECK_VERSION(2, 14, 0)
2215 	gint n;
2216 	gint i;
2217 	char * name;
2218 	char buf[32];
2219 #endif
2220 
2221 	active = gtk_combo_box_get_active(GTK_COMBO_BOX(desktop->pr_imonitor));
2222 	model1 = gtk_combo_box_get_model(GTK_COMBO_BOX(desktop->pr_imonitor));
2223 	model2 = gtk_combo_box_get_model(GTK_COMBO_BOX(desktop->pr_monitors));
2224 	gtk_list_store_clear(GTK_LIST_STORE(model1));
2225 	gtk_list_store_clear(GTK_LIST_STORE(model2));
2226 #if GTK_CHECK_VERSION(2, 24, 0)
2227 	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(desktop->pr_imonitor),
2228 			_("Default monitor"));
2229 	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(desktop->pr_monitors),
2230 			_("Whole screen"));
2231 #else
2232 	gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_imonitor),
2233 			_("Default monitor"));
2234 	gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_monitors),
2235 			_("Whole screen"));
2236 #endif
2237 #if GTK_CHECK_VERSION(2, 14, 0)
2238 	n = gdk_screen_get_n_monitors(desktop->screen);
2239 	for(i = 0; i < n; i++)
2240 	{
2241 		snprintf(buf, sizeof(buf), _("Monitor %d"), i);
2242 		name = gdk_screen_get_monitor_plug_name(desktop->screen, i);
2243 # if GTK_CHECK_VERSION(2, 24, 0)
2244 		gtk_combo_box_text_append_text(
2245 				GTK_COMBO_BOX_TEXT(desktop->pr_imonitor),
2246 				(name != NULL) ? name : buf);
2247 		gtk_combo_box_text_append_text(
2248 				GTK_COMBO_BOX_TEXT(desktop->pr_monitors),
2249 				(name != NULL) ? name : buf);
2250 # else
2251 		gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_imonitor),
2252 				(name != NULL) ? name : buf);
2253 		gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_monitors),
2254 				(name != NULL) ? name : buf);
2255 # endif
2256 		g_free(name);
2257 	}
2258 #endif
2259 	gtk_combo_box_set_active(GTK_COMBO_BOX(desktop->pr_imonitor), active);
2260 	gtk_combo_box_set_active(GTK_COMBO_BOX(desktop->pr_monitors), 0);
2261 }
2262 
_on_preferences_response(GtkWidget * widget,gint response,gpointer data)2263 static void _on_preferences_response(GtkWidget * widget, gint response,
2264 		gpointer data)
2265 {
2266 	Desktop * desktop = data;
2267 
2268 	if(response == GTK_RESPONSE_OK)
2269 		_on_preferences_response_ok(desktop);
2270 	else if(response == GTK_RESPONSE_APPLY)
2271 		_on_preferences_response_apply(desktop);
2272 	else if(response == GTK_RESPONSE_CANCEL)
2273 		_on_preferences_response_cancel(desktop);
2274 }
2275 
_on_preferences_response_apply(gpointer data)2276 static void _on_preferences_response_apply(gpointer data)
2277 {
2278 	Desktop * desktop = data;
2279 	Config * config;
2280 #if GTK_CHECK_VERSION(3, 0, 0)
2281 	GdkRGBA color;
2282 #else
2283 	GdkColor color;
2284 #endif
2285 	char * p;
2286 	char const * q;
2287 	int i;
2288 	char buf[12];
2289 
2290 	if((config = _desktop_get_config(desktop)) == NULL)
2291 		return;
2292 	/* XXX not very efficient */
2293 	desktop_reset(desktop);
2294 	/* background */
2295 	p = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(
2296 				desktop->pr_background));
2297 	config_set(config, "background", "wallpaper", p);
2298 	g_free(p);
2299 #if GTK_CHECK_VERSION(3, 4, 0)
2300 	gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(desktop->pr_color),
2301 			&color);
2302 	p = gdk_rgba_to_string(&color);
2303 #elif GTK_CHECK_VERSION(3, 0, 0)
2304 	gtk_color_button_get_rgba(GTK_COLOR_BUTTON(desktop->pr_color), &color);
2305 	p = gdk_rgba_to_string(&color);
2306 #else
2307 	gtk_color_button_get_color(GTK_COLOR_BUTTON(desktop->pr_color), &color);
2308 	p = gdk_color_to_string(&color);
2309 #endif
2310 	config_set(config, "background", "color", p);
2311 	g_free(p);
2312 	i = gtk_combo_box_get_active(GTK_COMBO_BOX(desktop->pr_background_how));
2313 	if(i >= 0 && i < DESKTOP_HOW_COUNT)
2314 		config_set(config, "background", "how", _desktop_hows[i]);
2315 	p = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
2316 				desktop->pr_background_extend)) ? "1" : "0";
2317 	config_set(config, "background", "extend", p);
2318 	/* icons */
2319 	i = gtk_combo_box_get_active(GTK_COMBO_BOX(desktop->pr_ilayout));
2320 	if(i >= 0 && i < DESKTOP_ICONS_COUNT)
2321 		config_set(config, "icons", "layout", _desktop_icons_config[i]);
2322 	desktop->prefs.icons = i; /* applied by _new_idle() */
2323 #if GTK_CHECK_VERSION(3, 4, 0)
2324 	gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(desktop->pr_ibcolor),
2325 			&color);
2326 	p = gdk_rgba_to_string(&color);
2327 #elif GTK_CHECK_VERSION(3, 0, 0)
2328 	gtk_color_button_get_rgba(GTK_COLOR_BUTTON(desktop->pr_ibcolor),
2329 			&color);
2330 	p = gdk_rgba_to_string(&color);
2331 #else
2332 	gtk_color_button_get_color(GTK_COLOR_BUTTON(desktop->pr_ibcolor),
2333 			&color);
2334 	p = gdk_color_to_string(&color);
2335 #endif
2336 	config_set(config, "icons", "background", p);
2337 	g_free(p);
2338 #if GTK_CHECK_VERSION(3, 4, 0)
2339 	gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(desktop->pr_ifcolor),
2340 			&color);
2341 	p = gdk_rgba_to_string(&color);
2342 #elif GTK_CHECK_VERSION(3, 0, 0)
2343 	gtk_color_button_get_rgba(GTK_COLOR_BUTTON(desktop->pr_ifcolor),
2344 			&color);
2345 	p = gdk_rgba_to_string(&color);
2346 #else
2347 	gtk_color_button_get_color(GTK_COLOR_BUTTON(desktop->pr_ifcolor),
2348 			&color);
2349 	p = gdk_color_to_string(&color);
2350 #endif
2351 	config_set(config, "icons", "foreground", p);
2352 	g_free(p);
2353 	q = gtk_font_button_get_font_name(GTK_FONT_BUTTON(desktop->pr_ifont));
2354 	config_set(config, "icons", "font", q);
2355 	i = gtk_combo_box_get_active(GTK_COMBO_BOX(desktop->pr_imonitor));
2356 	desktop->prefs.monitor = (i >= 0) ? i - 1 : i;
2357 	snprintf(buf, sizeof(buf), "%d", desktop->prefs.monitor);
2358 	config_set(config, "icons", "monitor", buf);
2359 	config_set(config, "icons", "show_hidden", desktop->show_hidden
2360 			? "1" : "0");
2361 	/* XXX code duplication */
2362 	if((p = string_new_append(desktop->home, "/" DESKTOPRC, NULL)) != NULL)
2363 	{
2364 		/* FIXME save configuration in _on_preferences_ok() instead */
2365 		if(config_save(config, p) != 0)
2366 			error_print(PROGNAME);
2367 		string_delete(p);
2368 	}
2369 	config_delete(config);
2370 }
2371 
_on_preferences_response_cancel(gpointer data)2372 static void _on_preferences_response_cancel(gpointer data)
2373 {
2374 	Desktop * desktop = data;
2375 
2376 	gtk_widget_hide(desktop->pr_window);
2377 	_preferences_set(desktop);
2378 }
2379 
_on_preferences_response_ok(gpointer data)2380 static void _on_preferences_response_ok(gpointer data)
2381 {
2382 	Desktop * desktop = data;
2383 
2384 	gtk_widget_hide(desktop->pr_window);
2385 	_on_preferences_response_apply(desktop);
2386 }
2387 
_on_preferences_update_preview(gpointer data)2388 static void _on_preferences_update_preview(gpointer data)
2389 {
2390 	Desktop * desktop = data;
2391 #if !GTK_CHECK_VERSION(2, 6, 0)
2392 	gint ratio = desktop->window.width / desktop->window.height;
2393 #endif
2394 	GtkFileChooser * chooser = GTK_FILE_CHOOSER(desktop->pr_background);
2395 	GtkWidget * widget;
2396 	char * filename;
2397 	GdkPixbuf * pixbuf;
2398 	gboolean active = FALSE;
2399 	GError * error = NULL;
2400 
2401 	widget = gtk_file_chooser_get_preview_widget(chooser);
2402 	if((filename = gtk_file_chooser_get_preview_filename(chooser)) != NULL)
2403 	{
2404 #if GTK_CHECK_VERSION(2, 6, 0)
2405 		pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, 96, -1,
2406 				TRUE, &error);
2407 #else
2408 		pixbuf = gdk_pixbuf_new_from_file_at_size(filename, 96,
2409 				96 / ratio, &error);
2410 #endif
2411 		if(error != NULL)
2412 		{
2413 			_desktop_error(NULL, NULL, error->message, 1);
2414 			g_error_free(error);
2415 		}
2416 		if(pixbuf != NULL)
2417 		{
2418 			gtk_image_set_from_pixbuf(GTK_IMAGE(widget), pixbuf);
2419 			g_object_unref(pixbuf);
2420 			active = TRUE;
2421 		}
2422 	}
2423 	g_free(filename);
2424 	gtk_file_chooser_set_preview_widget_active(chooser, active);
2425 }
2426 
_preferences_set(Desktop * desktop)2427 static void _preferences_set(Desktop * desktop)
2428 {
2429 	Config * config;
2430 	String const * p;
2431 	String * q;
2432 	String const * filename = NULL;
2433 	const char black[] = "#000000000000";
2434 	const char white[] = "#ffffffffffff";
2435 	int how;
2436 	gboolean extend = FALSE;
2437 	size_t i;
2438 	int j;
2439 
2440 	/* FIXME:
2441 	 * - cache the current configuration within the Desktop class
2442 	 * - apply the configuration values to the widgets */
2443 	if((config = _desktop_get_config(desktop)) != NULL)
2444 	{
2445 		/* background */
2446 		filename = config_get(config, "background", "wallpaper");
2447 		_preferences_set_color(config, "background", "color", NULL,
2448 				desktop->pr_color);
2449 		how = 0;
2450 		if((p = config_get(config, "background", "how")) != NULL)
2451 			for(i = 0; i < DESKTOP_HOW_COUNT; i++)
2452 				if(strcmp(_desktop_hows[i], p) == 0)
2453 				{
2454 					how = i;
2455 					break;
2456 				}
2457 		gtk_combo_box_set_active(GTK_COMBO_BOX(
2458 					desktop->pr_background_how), how);
2459 		if((p = config_get(config, "background", "extend")) != NULL)
2460 			extend = strtol(p, NULL, 10) ? TRUE : FALSE;
2461 		/* icons */
2462 		how = DESKTOP_ICONS_FILES;
2463 		if((p = config_get(config, "icons", "layout")) != NULL)
2464 			for(i = 0; i < DESKTOP_ICONS_COUNT; i++)
2465 				if(strcmp(_desktop_icons_config[i], p) == 0)
2466 				{
2467 					how = i;
2468 					break;
2469 				}
2470 		gtk_combo_box_set_active(GTK_COMBO_BOX(desktop->pr_ilayout),
2471 				how);
2472 		_preferences_set_color(config, "icons", "background", black,
2473 				desktop->pr_ibcolor);
2474 		_preferences_set_color(config, "icons", "foreground", white,
2475 				desktop->pr_ifcolor);
2476 		if((p = config_get(config, "icons", "font")) != NULL)
2477 			gtk_font_button_set_font_name(GTK_FONT_BUTTON(
2478 						desktop->pr_ifont), p);
2479 		else if((q = pango_font_description_to_string(desktop->font))
2480 				!= NULL)
2481 		{
2482 			gtk_font_button_set_font_name(GTK_FONT_BUTTON(
2483 						desktop->pr_ifont), q);
2484 			g_free(q);
2485 		}
2486 		if((p = config_get(config, "icons", "monitor")) != NULL)
2487 		{
2488 			j = strtol(p, &q, 10);
2489 			if(p[0] == '\0' || *q != '\0' || j < 0)
2490 				j = -1;
2491 			gtk_combo_box_set_active(GTK_COMBO_BOX(
2492 						desktop->pr_imonitor), j + 1);
2493 		}
2494 		config_delete(config);
2495 	}
2496 	else
2497 	{
2498 		gtk_combo_box_set_active(GTK_COMBO_BOX(
2499 					desktop->pr_background_how), 0);
2500 		gtk_combo_box_set_active(GTK_COMBO_BOX(desktop->pr_ilayout),
2501 				DESKTOP_ICONS_FILES);
2502 		gtk_combo_box_set_active(GTK_COMBO_BOX(desktop->pr_imonitor),
2503 				0);
2504 	}
2505 	if(filename != NULL)
2506 		gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(
2507 					desktop->pr_background), filename);
2508 	else
2509 		gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(
2510 					desktop->pr_background));
2511 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
2512 				desktop->pr_background_extend), extend);
2513 }
2514 
_preferences_set_color(Config * config,char const * section,char const * variable,char const * fallback,GtkWidget * widget)2515 static void _preferences_set_color(Config * config, char const * section,
2516 		char const * variable, char const * fallback,
2517 		GtkWidget * widget)
2518 {
2519 	char const * p;
2520 #if GTK_CHECK_VERSION(3, 0, 0)
2521 	GdkRGBA color = { 0.0, 0.0, 0.0, 0.0 };
2522 #else
2523 	GdkColor color = { 0, 0, 0, 0 };
2524 #endif
2525 
2526 	if((p = config_get(config, section, variable)) == NULL)
2527 		p = fallback;
2528 #if GTK_CHECK_VERSION(3, 4, 0)
2529 	if(p != NULL && gdk_rgba_parse(&color, p) == TRUE)
2530 		gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(widget), &color);
2531 #elif GTK_CHECK_VERSION(3, 0, 0)
2532 	if(p != NULL && gdk_rgba_parse(&color, p) == TRUE)
2533 		gtk_color_button_set_rgba(GTK_COLOR_BUTTON(widget), &color);
2534 #else
2535 	if(p != NULL && gdk_color_parse(p, &color) == TRUE)
2536 		gtk_color_button_set_color(GTK_COLOR_BUTTON(widget), &color);
2537 #endif
2538 }
2539 
2540 
2541 /* callbacks */
2542 /* desktop_on_refresh */
2543 static void _refresh_cleanup(Desktop * desktop);
2544 static void _refresh_done_applications(Desktop * desktop);
2545 static void _refresh_done_categories(Desktop * desktop);
2546 static void _refresh_done_categories_open(Desktop * desktop, gpointer data);
2547 static gboolean _refresh_done_timeout(gpointer data);
2548 static int _refresh_loop(Desktop * desktop);
2549 static gint _categories_apps_compare(gconstpointer a, gconstpointer b);
2550 static int _refresh_loop_categories(Desktop * desktop);
2551 static void _refresh_loop_categories_path(Desktop * desktop, char const * path,
2552 		char const * apppath);
2553 static void _refresh_loop_categories_xdg(Desktop * desktop,
2554 		void (*callback)(Desktop * desktop, char const * path,
2555 			char const * apppath));
2556 static void _refresh_loop_categories_xdg_home(Desktop * desktop,
2557 		void (*callback)(Desktop * desktop, char const * path,
2558 			char const * apppath));
2559 static void _refresh_loop_categories_xdg_path(Desktop * desktop,
2560 		void (*callback)(Desktop * desktop, char const * path,
2561 			char const * apppath), char const * path);
2562 static int _refresh_loop_files(Desktop * desktop);
2563 static int _refresh_loop_lookup(Desktop * desktop, char const * name);
2564 static gboolean _refresh_done(Desktop * desktop);
2565 
_desktop_on_refresh(gpointer data)2566 static gboolean _desktop_on_refresh(gpointer data)
2567 {
2568 	Desktop * desktop = data;
2569 	unsigned int i;
2570 
2571 	for(i = 0; i < 16 && _refresh_loop(desktop) == 0; i++);
2572 	if(i == 16)
2573 		return TRUE;
2574 	return _refresh_done(desktop);
2575 }
2576 
_refresh_cleanup(Desktop * desktop)2577 static void _refresh_cleanup(Desktop * desktop)
2578 {
2579 	size_t i;
2580 
2581 	for(i = 0; i < desktop->icon_cnt;)
2582 		if(desktopicon_get_immutable(desktop->icon[i]) == TRUE)
2583 			i++;
2584 		else if(desktopicon_get_updated(desktop->icon[i]) != TRUE)
2585 			_desktop_icon_remove(desktop, desktop->icon[i]);
2586 		else
2587 			desktopicon_set_updated(desktop->icon[i++], FALSE);
2588 }
2589 
_refresh_done(Desktop * desktop)2590 static gboolean _refresh_done(Desktop * desktop)
2591 {
2592 	switch(desktop->prefs.icons)
2593 	{
2594 		case DESKTOP_ICONS_APPLICATIONS:
2595 			_refresh_done_applications(desktop);
2596 			break;
2597 		case DESKTOP_ICONS_CATEGORIES:
2598 			_refresh_done_categories(desktop);
2599 			break;
2600 		default:
2601 			break;
2602 	}
2603 	_refresh_cleanup(desktop);
2604 	if(desktop->refresh_dir != NULL)
2605 		browser_vfs_closedir(desktop->refresh_dir);
2606 	desktop->refresh_dir = NULL;
2607 	desktop_icons_align(desktop);
2608 	desktop->refresh_source = g_timeout_add(1000, _refresh_done_timeout,
2609 			desktop);
2610 	return FALSE;
2611 }
2612 
_refresh_done_applications(Desktop * desktop)2613 static void _refresh_done_applications(Desktop * desktop)
2614 {
2615 	GSList * p;
2616 	Config * config;
2617 	const char section[] = "Desktop Entry";
2618 	char const * q;
2619 	char const * r;
2620 	DesktopCategory * dc = desktop->category;
2621 	char const * path;
2622 	DesktopIcon * icon;
2623 
2624 	for(p = desktop->apps; p != NULL; p = p->next)
2625 	{
2626 		config = p->data;
2627 		if(dc != NULL)
2628 		{
2629 			if((q = config_get(config, section, "Categories"))
2630 					== NULL)
2631 				continue;
2632 			if((r = string_find(q, dc->category)) == NULL)
2633 				continue;
2634 			r += string_length(dc->category);
2635 			if(*r != '\0' && *r != ';')
2636 				continue;
2637 		}
2638 		path = config_get(config, NULL, "path");
2639 		q = config_get(config, NULL, "datadir");
2640 		if((icon = desktopicon_new_application(desktop, path, q))
2641 				== NULL)
2642 			continue;
2643 		_desktop_icon_add(desktop, icon);
2644 	}
2645 }
2646 
_refresh_done_categories(Desktop * desktop)2647 static void _refresh_done_categories(Desktop * desktop)
2648 {
2649 	GSList * p;
2650 	Config * config;
2651 	const char section[] = "Desktop Entry";
2652 	char const * q;
2653 	char const * r;
2654 	size_t i;
2655 	DesktopCategory * dc;
2656 	char const * path;
2657 	DesktopIcon * icon;
2658 
2659 	for(p = desktop->apps; p != NULL; p = p->next)
2660 	{
2661 		config = p->data;
2662 		path = config_get(config, NULL, "path");
2663 		if((q = config_get(config, section, "Categories")) == NULL)
2664 		{
2665 			if((icon = desktopicon_new_application(desktop, path,
2666 							NULL)) != NULL)
2667 				_desktop_icon_add(desktop, icon);
2668 			continue;
2669 		}
2670 		for(i = 0; i < _desktop_categories_cnt; i++)
2671 		{
2672 			dc = &_desktop_categories[i];
2673 			if((r = string_find(q, dc->category)) == NULL)
2674 				continue;
2675 			r += string_length(dc->category);
2676 			if(*r == '\0' || *r == ';')
2677 				break;
2678 		}
2679 		if(i == _desktop_categories_cnt)
2680 		{
2681 			if((icon = desktopicon_new_application(desktop, path,
2682 					NULL)) != NULL)
2683 				_desktop_icon_add(desktop, icon);
2684 			continue;
2685 		}
2686 		if(dc->show == TRUE)
2687 			continue;
2688 		dc->show = TRUE;
2689 		if((icon = desktopicon_new_category(desktop, dc->name,
2690 						dc->icon)) == NULL)
2691 			continue;
2692 		desktopicon_set_callback(icon, _refresh_done_categories_open,
2693 				dc);
2694 		_desktop_icon_add(desktop, icon);
2695 	}
2696 }
2697 
_refresh_done_categories_open(Desktop * desktop,gpointer data)2698 static void _refresh_done_categories_open(Desktop * desktop, gpointer data)
2699 {
2700 	DesktopCategory * dc = data;
2701 
2702 #ifdef DEBUG
2703 	fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, dc->name);
2704 #endif
2705 	desktop->category = dc;
2706 	desktop_set_icons(desktop, DESKTOP_ICONS_APPLICATIONS);
2707 }
2708 
_refresh_done_timeout(gpointer data)2709 static gboolean _refresh_done_timeout(gpointer data)
2710 {
2711 	Desktop * desktop = data;
2712 	struct stat st;
2713 
2714 	desktop->refresh_source = 0;
2715 	if(desktop->path == NULL)
2716 		return FALSE;
2717 	if(stat(desktop->path, &st) != 0)
2718 		return desktop_error(NULL, desktop->path, FALSE);
2719 	if(st.st_mtime == desktop->refresh_mti)
2720 		return TRUE;
2721 	desktop_refresh(desktop);
2722 	return FALSE;
2723 }
2724 
_refresh_loop(Desktop * desktop)2725 static int _refresh_loop(Desktop * desktop)
2726 {
2727 	switch(desktop->prefs.icons)
2728 	{
2729 		case DESKTOP_ICONS_APPLICATIONS:
2730 		case DESKTOP_ICONS_CATEGORIES:
2731 			return _refresh_loop_categories(desktop);
2732 		case DESKTOP_ICONS_FILES:
2733 			return _refresh_loop_files(desktop);
2734 		case DESKTOP_ICONS_HOMESCREEN:
2735 		case DESKTOP_ICONS_NONE:
2736 			return 1; /* nothing to do */
2737 	}
2738 	return -1;
2739 }
2740 
_refresh_loop_categories(Desktop * desktop)2741 static int _refresh_loop_categories(Desktop * desktop)
2742 {
2743 	_refresh_loop_categories_xdg(desktop, _refresh_loop_categories_path);
2744 	return 1;
2745 }
2746 
_refresh_loop_categories_path(Desktop * desktop,char const * path,char const * apppath)2747 static void _refresh_loop_categories_path(Desktop * desktop, char const * path,
2748 		char const * apppath)
2749 {
2750 	DIR * dir;
2751 	int fd;
2752 	struct stat st;
2753 	struct dirent * de;
2754 	size_t len;
2755 	const char ext[] = ".desktop";
2756 	const char section[] = "Desktop Entry";
2757 	char * name = NULL;
2758 	char * p;
2759 	Config * config = NULL;
2760 	String const * q;
2761 	String const * r;
2762 
2763 #if defined(__sun)
2764 	if((fd = open(apppath, O_RDONLY)) < 0
2765 			|| fstat(fd, &st) != 0
2766 			|| (dir = fdopendir(fd)) == NULL)
2767 #else
2768 	if((dir = opendir(apppath)) == NULL
2769 			|| (fd = dirfd(dir)) < 0
2770 			|| fstat(fd, &st) != 0)
2771 #endif
2772 	{
2773 		if(errno != ENOENT)
2774 			desktop_error(NULL, apppath, 1);
2775 		return;
2776 	}
2777 	if(st.st_mtime > desktop->refresh_mti)
2778 		desktop->refresh_mti = st.st_mtime;
2779 	while((de = readdir(dir)) != NULL)
2780 	{
2781 		if(de->d_name[0] == '.')
2782 			if(de->d_name[1] == '\0' || (de->d_name[1] == '.'
2783 						&& de->d_name[2] == '\0'))
2784 				continue;
2785 		len = strlen(de->d_name);
2786 		if(len < sizeof(ext))
2787 			continue;
2788 		if(strncmp(&de->d_name[len - sizeof(ext) + 1], ext,
2789 					sizeof(ext)) != 0)
2790 			continue;
2791 		if((p = realloc(name, strlen(apppath) + len + 2)) == NULL)
2792 		{
2793 			desktop_error(NULL, apppath, 1);
2794 			continue;
2795 		}
2796 		name = p;
2797 		snprintf(name, strlen(apppath) + len + 2, "%s/%s", apppath,
2798 				de->d_name);
2799 #ifdef DEBUG
2800 		fprintf(stderr, "DEBUG: %s() name=\"%s\" path=\"%s\"\n",
2801 				__func__, name, path);
2802 #endif
2803 		if(config == NULL)
2804 			config = config_new();
2805 		else
2806 			config_reset(config);
2807 		if(config == NULL || config_load(config, name) != 0)
2808 		{
2809 			desktop_error(NULL, NULL, 1); /* XXX */
2810 			continue;
2811 		}
2812 		q = config_get(config, section, "Name");
2813 		r = config_get(config, section, "Type");
2814 		if(q == NULL || r == NULL)
2815 			continue;
2816 		/* remember the path */
2817 		config_set(config, NULL, "path", name);
2818 		config_set(config, NULL, "datadir", path);
2819 		desktop->apps = g_slist_insert_sorted(desktop->apps, config,
2820 				_categories_apps_compare);
2821 		config = NULL;
2822 	}
2823 	free(name);
2824 	closedir(dir);
2825 	if(config != NULL)
2826 		config_delete(config);
2827 }
2828 
_refresh_loop_categories_xdg(Desktop * desktop,void (* callback)(Desktop * desktop,char const * path,char const * apppath))2829 static void _refresh_loop_categories_xdg(Desktop * desktop,
2830 		void (*callback)(Desktop * desktop, char const * path,
2831 			char const * apppath))
2832 {
2833 	char const * path;
2834 	char * p;
2835 	size_t i;
2836 	size_t j;
2837 
2838 	if((path = getenv("XDG_DATA_DIRS")) == NULL || strlen(path) == 0)
2839 		/* FIXME use DATADIR */
2840 		path = "/usr/local/share:/usr/share";
2841 	if((p = strdup(path)) == NULL)
2842 	{
2843 		desktop_error(NULL, "strdup", 1);
2844 		return;
2845 	}
2846 	for(i = 0, j = 0;; i++)
2847 		if(p[i] == '\0')
2848 		{
2849 			_refresh_loop_categories_xdg_path(desktop, callback,
2850 					&p[j]);
2851 			break;
2852 		}
2853 		else if(p[i] == ':')
2854 		{
2855 			p[i] = '\0';
2856 			_refresh_loop_categories_xdg_path(desktop, callback,
2857 					&p[j]);
2858 			j = i + 1;
2859 		}
2860 	free(p);
2861 	_refresh_loop_categories_xdg_home(desktop, callback);
2862 }
2863 
_refresh_loop_categories_xdg_home(Desktop * desktop,void (* callback)(Desktop * desktop,char const * path,char const * apppath))2864 static void _refresh_loop_categories_xdg_home(Desktop * desktop,
2865 		void (*callback)(Desktop * desktop, char const * path,
2866 			char const * apppath))
2867 {
2868 	char const fallback[] = ".local/share";
2869 	char const * path;
2870 	char const * homedir;
2871 	size_t len;
2872 	char * p;
2873 
2874 	/* use $XDG_DATA_HOME if set and not empty */
2875 	if((path = getenv("XDG_DATA_HOME")) != NULL && strlen(path) > 0)
2876 	{
2877 		_refresh_loop_categories_xdg_path(desktop, callback, path);
2878 		return;
2879 	}
2880 	/* fallback to "$HOME/.local/share" */
2881 	if((homedir = getenv("HOME")) == NULL)
2882 		homedir = g_get_home_dir();
2883 	len = strlen(homedir) + 1 + sizeof(fallback);
2884 	if((p = malloc(len)) == NULL)
2885 	{
2886 		desktop_error(NULL, homedir, 1);
2887 		return;
2888 	}
2889 	snprintf(p, len, "%s/%s", homedir, fallback);
2890 	_refresh_loop_categories_xdg_path(desktop, callback, p);
2891 	free(p);
2892 }
2893 
_refresh_loop_categories_xdg_path(Desktop * desktop,void (* callback)(Desktop * desktop,char const * path,char const * apppath),char const * path)2894 static void _refresh_loop_categories_xdg_path(Desktop * desktop,
2895 		void (*callback)(Desktop * desktop, char const * path,
2896 			char const * apppath), char const * path)
2897 {
2898 	const char applications[] = "/applications";
2899 	char * apppath;
2900 
2901 	if((apppath = string_new_append(path, applications, NULL)) == NULL)
2902 		desktop_error(NULL, path, 1);
2903 	callback(desktop, path, apppath);
2904 	string_delete(apppath);
2905 }
2906 
_categories_apps_compare(gconstpointer a,gconstpointer b)2907 static gint _categories_apps_compare(gconstpointer a, gconstpointer b)
2908 {
2909 	Config * ca = (Config *)a;
2910 	Config * cb = (Config *)b;
2911 	char const * cap;
2912 	char const * cbp;
2913 	const char section[] = "Desktop Entry";
2914 	const char variable[] = "Name";
2915 
2916 	/* these should not fail */
2917 	cap = config_get(ca, section, variable);
2918 	cbp = config_get(cb, section, variable);
2919 	return string_compare(cap, cbp);
2920 }
2921 
_refresh_loop_files(Desktop * desktop)2922 static int _refresh_loop_files(Desktop * desktop)
2923 {
2924 	struct dirent * de;
2925 	String * p;
2926 	DesktopIcon * desktopicon;
2927 
2928 	while((de = browser_vfs_readdir(desktop->refresh_dir)) != NULL)
2929 	{
2930 		if(de->d_name[0] == '.')
2931 		{
2932 			if(de->d_name[1] == '\0')
2933 				continue;
2934 			if(de->d_name[1] == '.' && de->d_name[2] == '\0')
2935 				continue;
2936 			if(desktop->show_hidden == 0)
2937 				continue;
2938 		}
2939 		if(_refresh_loop_lookup(desktop, de->d_name) == 1)
2940 			continue;
2941 		break;
2942 	}
2943 	if(de == NULL)
2944 		return -1;
2945 	if((p = string_new_append(desktop->path, "/", de->d_name, NULL))
2946 			== NULL)
2947 		return -_desktop_serror(desktop, de->d_name, 1);
2948 	if((desktopicon = desktopicon_new(desktop, de->d_name, p)) != NULL)
2949 		desktop_icon_add(desktop, desktopicon);
2950 	string_delete(p);
2951 	return 0;
2952 }
2953 
_refresh_loop_lookup(Desktop * desktop,char const * name)2954 static int _refresh_loop_lookup(Desktop * desktop, char const * name)
2955 {
2956 	size_t i;
2957 	char const * p;
2958 
2959 	for(i = 0; i < desktop->icon_cnt; i++)
2960 	{
2961 		if(desktopicon_get_updated(desktop->icon[i]) == TRUE)
2962 			continue;
2963 		if((p = desktopicon_get_path(desktop->icon[i])) == NULL
2964 				|| (p = strrchr(p, '/')) == NULL)
2965 			continue;
2966 		if(strcmp(name, ++p) != 0)
2967 			continue;
2968 		desktopicon_set_updated(desktop->icon[i], TRUE);
2969 		return 1;
2970 	}
2971 	return 0;
2972 }
2973 
2974 
2975 /* error */
_error(char const * message,int ret)2976 static int _error(char const * message, int ret)
2977 {
2978 	fputs("desktop: ", stderr);
2979 	perror(message);
2980 	return ret;
2981 }
2982 
2983 
2984 /* usage */
_usage(void)2985 static int _usage(void)
2986 {
2987 	fprintf(stderr, _("Usage: %s [-H|-V][-a|-c|-f|-h|-n][-m monitor][-N][-w]\n"
2988 "  -H	Place icons horizontally\n"
2989 "  -V	Place icons vertically\n"
2990 "  -a	Display the applications registered\n"
2991 "  -c	Sort the applications registered by category\n"
2992 "  -f	Display contents of the desktop folder (default)\n"
2993 "  -h	Display the homescreen\n"
2994 "  -m	Monitor where to display the desktop\n"
2995 "  -n	Do not display icons on the desktop\n"
2996 "  -N	Do not intercept mouse clicks on the desktop\n"
2997 "  -w	Draw the desktop as a window\n"), PROGNAME);
2998 	return 1;
2999 }
3000 
3001 
3002 /* main */
main(int argc,char * argv[])3003 int main(int argc, char * argv[])
3004 {
3005 	int o;
3006 	Desktop * desktop;
3007 	DesktopPrefs prefs;
3008 	char * p;
3009 
3010 	if(setlocale(LC_ALL, "") == NULL)
3011 		_error("setlocale", 1);
3012 	bindtextdomain(PACKAGE, LOCALEDIR);
3013 	textdomain(PACKAGE);
3014 	prefs.alignment = -1;
3015 	prefs.icons = -1;
3016 	prefs.monitor = -1;
3017 	prefs.popup = 1;
3018 	prefs.window = 0;
3019 	gtk_init(&argc, &argv);
3020 	while((o = getopt(argc, argv, "HVacfhm:nNw")) != -1)
3021 		switch(o)
3022 		{
3023 			case 'H':
3024 				prefs.alignment = DESKTOP_ALIGNMENT_HORIZONTAL;
3025 				break;
3026 			case 'V':
3027 				prefs.alignment = DESKTOP_ALIGNMENT_VERTICAL;
3028 				break;
3029 			case 'a':
3030 				prefs.icons = DESKTOP_ICONS_APPLICATIONS;
3031 				break;
3032 			case 'c':
3033 				prefs.icons = DESKTOP_ICONS_CATEGORIES;
3034 				break;
3035 			case 'f':
3036 				prefs.icons = DESKTOP_ICONS_FILES;
3037 				break;
3038 			case 'h':
3039 				prefs.icons = DESKTOP_ICONS_HOMESCREEN;
3040 				break;
3041 			case 'm':
3042 				prefs.monitor = strtol(optarg, &p, 0);
3043 				if(optarg[0] == '\0' || *p != '\0')
3044 					return _usage();
3045 				break;
3046 			case 'n':
3047 				prefs.icons = DESKTOP_ICONS_NONE;
3048 				break;
3049 			case 'N':
3050 				prefs.popup = 0;
3051 				break;
3052 			case 'w':
3053 				prefs.window = 1;
3054 				break;
3055 			default:
3056 				return _usage();
3057 		}
3058 	if(optind < argc)
3059 		return _usage();
3060 	if((desktop = desktop_new(&prefs)) == NULL)
3061 	{
3062 		gtk_main();
3063 		return 2;
3064 	}
3065 	gtk_main();
3066 	desktop_delete(desktop);
3067 	return 0;
3068 }
3069