1 /* $Id$ */
2 /* Copyright (c) 2010-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 
30 
31 
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <libgen.h>
38 #include <errno.h>
39 #include <libintl.h>
40 #include <gtk/gtk.h>
41 #include <gdk/gdkkeysyms.h>
42 #include <System.h>
43 #include "../include/Browser/desktop.h"
44 #include "../include/Browser/vfs.h"
45 #include "desktopicon.h"
46 #include "../config.h"
47 #define _(string) gettext(string)
48 
49 #define PROGNAME "desktop"
50 #define COMMON_DND
51 #define COMMON_EXEC
52 #include "common.c"
53 
54 
55 /* constants */
56 #ifndef PREFIX
57 # define PREFIX		"/usr/local"
58 #endif
59 #ifndef BINDIR
60 # define BINDIR		PREFIX "/bin"
61 #endif
62 #ifndef DATADIR
63 # define DATADIR	PREFIX "/share"
64 #endif
65 
66 
67 /* DesktopIcon */
68 /* types */
69 struct _DesktopIcon
70 {
71 	Desktop * desktop;
72 	char * path;
73 	char * name;
74 	gboolean isfirst;
75 	gboolean isdir;
76 	gboolean isexec;
77 	char const * mimetype;
78 
79 	/* applications */
80 	Config * config;
81 
82 	/* callback */
83 	DesktopIconCallback callback;
84 	gpointer data;
85 
86 	gboolean confirm;
87 	gboolean immutable;		/* cannot be deleted */
88 	gboolean selected;
89 	gboolean updated;		/* XXX for desktop refresh */
90 
91 	GtkWidget * window;
92 	GtkWidget * image;
93 	GtkWidget * event;
94 	GtkWidget * label;
95 };
96 
97 
98 /* prototypes */
99 static DesktopIcon * _desktopicon_new_do(Desktop * desktop, GdkPixbuf * image,
100 		char const * name);
101 
102 static void _desktopicon_set_icon(DesktopIcon * desktopicon, GdkPixbuf * icon);
103 static int _desktopicon_set_name(DesktopIcon * desktopicon, char const * name);
104 
105 static void _desktopicon_update_transparency(DesktopIcon * desktopicon);
106 
107 /* callbacks */
108 static gboolean _on_desktopicon_closex(void);
109 static gboolean _on_icon_button_press(GtkWidget * widget,
110 		GdkEventButton * event, gpointer data);
111 static gboolean _on_icon_key_press(GtkWidget * widget, GdkEventKey * event,
112 		gpointer data);
113 static void _on_icon_drag_data_get(GtkWidget * widget, GdkDragContext * context,
114 		GtkSelectionData * seldata, guint info, guint time,
115 		gpointer data);
116 static void _on_icon_drag_data_received(GtkWidget * widget,
117 		GdkDragContext * context, gint x, gint y,
118 		GtkSelectionData * seldata, guint info, guint time,
119 		gpointer data);
120 static void _on_icon_open(gpointer data);
121 static void _on_icon_edit(gpointer data);
122 static void _on_icon_run(gpointer data);
123 static void _on_icon_open_with(gpointer data);
124 static void _on_icon_rename(gpointer data);
125 static void _on_icon_delete(gpointer data);
126 static void _on_icon_properties(gpointer data);
127 
128 
129 /* public */
130 /* functions */
131 /* desktopicon_new */
desktopicon_new(Desktop * desktop,char const * name,char const * path)132 DesktopIcon * desktopicon_new(Desktop * desktop, char const * name,
133 		char const * path)
134 {
135 	DesktopIcon * desktopicon;
136 	struct stat st;
137 	struct stat lst;
138 	struct stat * s = &lst;
139 	Mime * mime;
140 	char const * mimetype = NULL;
141 	gboolean isdir = FALSE;
142 	gboolean isexec = FALSE;
143 	GdkPixbuf * image = NULL;
144 	GError * error = NULL;
145 	char * p;
146 	GtkTargetEntry targets[] = { { "deforaos_browser_dnd", 0, 0 } };
147 	size_t targets_cnt = sizeof(targets) / sizeof(*targets);
148 
149 #ifdef DEBUG
150 	fprintf(stderr, "DEBUG: %s(%p, \"%s\", \"%s\")\n", __func__,
151 			(void *)desktop, name, path);
152 #endif
153 	if(path != NULL && lstat(path, &lst) == 0)
154 	{
155 		if(S_ISLNK(lst.st_mode) && stat(path, &st) == 0)
156 			s = &st;
157 		mime = desktop_get_mime(desktop);
158 		isdir = S_ISDIR(s->st_mode) ? TRUE : FALSE;
159 		isexec = (isdir == FALSE) && (s->st_mode & S_IXUSR)
160 			? TRUE : FALSE;
161 		mimetype = browser_vfs_mime_type(mime, path, s->st_mode);
162 		image = browser_vfs_mime_icon(mime, path, mimetype, &lst, NULL,
163 				DESKTOPICON_ICON_SIZE);
164 	}
165 	if(name == NULL)
166 	{
167 		if((p = g_filename_to_utf8(path, -1, NULL, NULL, &error))
168 				== NULL)
169 		{
170 			fprintf(stderr, "%s%s\n", "desktop: ", error->message);
171 			name = path;
172 		}
173 		else
174 			name = p;
175 		if((name = strrchr(name, '/')) != NULL)
176 			name++;
177 	}
178 	if((desktopicon = _desktopicon_new_do(desktop, image, name)) == NULL)
179 		return NULL;
180 	if(image != NULL)
181 		g_object_unref(image);
182 	gtk_drag_source_set(desktopicon->event, GDK_BUTTON1_MASK, targets,
183 			targets_cnt, GDK_ACTION_COPY | GDK_ACTION_MOVE);
184 	gtk_drag_dest_set(desktopicon->event, GTK_DEST_DEFAULT_ALL, targets,
185 			targets_cnt, GDK_ACTION_COPY | GDK_ACTION_MOVE);
186 	g_signal_connect(G_OBJECT(desktopicon->event), "drag-data-get",
187 			G_CALLBACK(_on_icon_drag_data_get), desktopicon);
188 	g_signal_connect(G_OBJECT(desktopicon->event), "drag-data-received",
189 			G_CALLBACK(_on_icon_drag_data_received), desktopicon);
190 	desktopicon->isdir = isdir;
191 	desktopicon_set_executable(desktopicon, isexec);
192 	desktopicon->mimetype = mimetype;
193 	desktopicon->config = NULL;
194 	if(path != NULL && (desktopicon->path = strdup(path)) == NULL)
195 	{
196 		desktopicon_delete(desktopicon);
197 		return NULL;
198 	}
199 	return desktopicon;
200 }
201 
202 
203 /* desktopicon_new_application */
204 static int _new_application_access(char const * path, int mode);
205 static int _new_application_access_path(char const * path,
206 		char const * filename, int mode);
207 static GdkPixbuf * _new_application_icon(Desktop * desktop, char const * icon,
208 		char const * datadir);
209 
desktopicon_new_application(Desktop * desktop,char const * path,char const * datadir)210 DesktopIcon * desktopicon_new_application(Desktop * desktop, char const * path,
211 		char const * datadir)
212 {
213 	DesktopIcon * desktopicon;
214 	Config * config;
215 	const char section[] = "Desktop Entry";
216 	char const * name;
217 	char const * icon;
218 	char const * p;
219 	GdkPixbuf * image = NULL;
220 
221 #ifdef DEBUG
222 	fprintf(stderr, "DEBUG: %s(%p, \"%s\")\n", __func__, (void *)desktop,
223 			path);
224 #endif
225 	if((config = config_new()) == NULL)
226 		return NULL;
227 	if(config_load(config, path) != 0
228 			|| ((p = config_get(config, section, "Hidden")) != NULL
229 				&& strcmp(p, "true") == 0)
230 			|| (p = config_get(config, section, "Type")) == NULL
231 			|| (strcmp(p, "Application") != 0
232 				&& strcmp(p, "Directory") != 0
233 				&& strcmp(p, "URL") != 0)
234 			|| (name = config_get(config, section, "Name")) == NULL
235 			|| ((p = config_get(config, section, "NoDisplay"))
236 				!= NULL && strcmp(p, "true") == 0)
237 			|| ((p = config_get(config, section, "TryExec")) != NULL
238 				&& _new_application_access(p, X_OK) != 0))
239 	{
240 		config_delete(config);
241 		return NULL;
242 	}
243 	if((icon = config_get(config, section, "Icon")) == NULL)
244 		icon = "application-x-executable";
245 	image = _new_application_icon(desktop, icon, datadir);
246 	desktopicon = _desktopicon_new_do(desktop, image, name);
247 	if(image != NULL)
248 		g_object_unref(image);
249 	if(desktopicon == NULL)
250 	{
251 		config_delete(config);
252 		return NULL;
253 	}
254 	desktopicon->config = config;
255 	desktopicon_set_confirm(desktopicon, FALSE);
256 	desktopicon_set_executable(desktopicon, TRUE);
257 	desktopicon_set_immutable(desktopicon, TRUE);
258 	return desktopicon;
259 }
260 
_new_application_access(char const * path,int mode)261 static int _new_application_access(char const * path, int mode)
262 {
263 	int ret = -1;
264 	char const * p;
265 	char * q;
266 	size_t i;
267 	size_t j;
268 
269 	if(path[0] == '/')
270 		return access(path, mode);
271 	if((p = getenv("PATH")) == NULL)
272 		return 0;
273 	if((q = strdup(p)) == NULL)
274 		return 0;
275 	errno = ENOENT;
276 	for(i = 0, j = 0;; i++)
277 		if(q[i] == '\0')
278 		{
279 			ret = _new_application_access_path(&q[j], path, mode);
280 			break;
281 		}
282 		else if(q[i] == ':')
283 		{
284 			q[i] = '\0';
285 			if((ret = _new_application_access_path(&q[j], path,
286 							mode)) == 0)
287 				break;
288 			j = i + 1;
289 		}
290 	free(q);
291 	return ret;
292 }
293 
_new_application_access_path(char const * path,char const * filename,int mode)294 static int _new_application_access_path(char const * path,
295 		char const * filename, int mode)
296 {
297 	int ret;
298 	char * p;
299 	size_t len;
300 
301 	len = strlen(path) + 1 + strlen(filename) + 1;
302 	if((p = malloc(len)) == NULL)
303 		return -1;
304 	snprintf(p, len, "%s/%s", path, filename);
305 	ret = access(p, mode);
306 	free(p);
307 	return ret;
308 }
309 
_new_application_icon(Desktop * desktop,char const * icon,char const * datadir)310 static GdkPixbuf * _new_application_icon(Desktop * desktop, char const * icon,
311 		char const * datadir)
312 {
313 	const char pixmaps[] = "/pixmaps/";
314 	const int width = DESKTOPICON_ICON_SIZE;
315 	const int height = DESKTOPICON_ICON_SIZE;
316 	String * buf;
317 	GdkPixbuf * pixbuf = NULL;
318 	GError * error = NULL;
319 
320 #ifdef DEBUG
321 	fprintf(stderr, "DEBUG: %s(\"%s\", \"%s\")\n", __func__, icon, datadir);
322 #endif
323 	if(icon[0] == '/')
324 		pixbuf = gdk_pixbuf_new_from_file_at_size(icon, width, height,
325 				&error);
326 	else if(strchr(icon, '.') != NULL)
327 	{
328 		if(datadir == NULL)
329 			datadir = DATADIR;
330 		if((buf = string_new_append(datadir, pixmaps, icon, NULL))
331 				!= NULL)
332 		{
333 			pixbuf = gdk_pixbuf_new_from_file_at_size(buf, width,
334 					height, &error);
335 			string_delete(buf);
336 		}
337 	}
338 	if(error != NULL)
339 	{
340 		desktop_error(NULL, error->message, 1); /* XXX */
341 		g_error_free(error);
342 	}
343 	if(pixbuf == NULL)
344 		pixbuf = gtk_icon_theme_load_icon(desktop_get_theme(desktop),
345 				icon, DESKTOPICON_ICON_SIZE, 0, NULL);
346 	if(pixbuf == NULL)
347 		pixbuf = desktop_get_file(desktop);
348 	return pixbuf;
349 }
350 
351 
352 /* desktopicon_new_category */
desktopicon_new_category(Desktop * desktop,char const * name,char const * icon)353 DesktopIcon * desktopicon_new_category(Desktop * desktop, char const * name,
354 		char const * icon)
355 {
356 	DesktopIcon * desktopicon;
357 	GdkPixbuf * image;
358 
359 #ifdef DEBUG
360 	fprintf(stderr, "DEBUG: %s(%p, \"%s\", \"%s\")\n", __func__,
361 			(void *)desktop, name, icon);
362 #endif
363 	image = gtk_icon_theme_load_icon(desktop_get_theme(desktop), icon,
364 			DESKTOPICON_ICON_SIZE, 0, NULL);
365 	if((desktopicon = _desktopicon_new_do(desktop, image, name)) == NULL)
366 		return NULL;
367 	desktopicon_set_immutable(desktopicon, TRUE);
368 	return desktopicon;
369 }
370 
371 
372 /* desktopicon_delete */
desktopicon_delete(DesktopIcon * desktopicon)373 void desktopicon_delete(DesktopIcon * desktopicon)
374 {
375 	if(desktopicon->config != NULL)
376 		config_delete(desktopicon->config);
377 	free(desktopicon->name);
378 	free(desktopicon->path);
379 	gtk_widget_destroy(desktopicon->window);
380 	object_delete(desktopicon);
381 }
382 
383 
384 /* accessors */
385 /* desktopicon_get_first */
desktopicon_get_first(DesktopIcon * desktopicon)386 gboolean desktopicon_get_first(DesktopIcon * desktopicon)
387 {
388 	return desktopicon->isfirst;
389 }
390 
391 
392 /* desktopicon_get_immutable */
desktopicon_get_immutable(DesktopIcon * desktopicon)393 gboolean desktopicon_get_immutable(DesktopIcon * desktopicon)
394 {
395 	return desktopicon->immutable;
396 }
397 
398 
399 /* desktopicon_get_isdir */
desktopicon_get_isdir(DesktopIcon * desktopicon)400 gboolean desktopicon_get_isdir(DesktopIcon * desktopicon)
401 {
402 	return desktopicon->isdir;
403 }
404 
405 
406 /* desktopicon_get_name */
desktopicon_get_name(DesktopIcon * desktopicon)407 char const * desktopicon_get_name(DesktopIcon * desktopicon)
408 {
409 	return desktopicon->name;
410 }
411 
412 
413 /* desktopicon_get_path */
desktopicon_get_path(DesktopIcon * desktopicon)414 char const * desktopicon_get_path(DesktopIcon * desktopicon)
415 {
416 	return desktopicon->path;
417 }
418 
419 
420 /* desktopicon_get_selected */
desktopicon_get_selected(DesktopIcon * desktopicon)421 gboolean desktopicon_get_selected(DesktopIcon * desktopicon)
422 {
423 	return desktopicon->selected;
424 }
425 
426 
427 /* desktopicon_get_updated */
desktopicon_get_updated(DesktopIcon * desktopicon)428 gboolean desktopicon_get_updated(DesktopIcon * desktopicon)
429 {
430 	return desktopicon->updated;
431 }
432 
433 
434 /* desktopicon_set_background */
435 #if GTK_CHECK_VERSION(3, 0, 0)
desktopicon_set_background(DesktopIcon * desktopicon,GdkRGBA * color)436 void desktopicon_set_background(DesktopIcon * desktopicon, GdkRGBA * color)
437 #else
438 void desktopicon_set_background(DesktopIcon * desktopicon, GdkColor * color)
439 #endif
440 {
441 #if GTK_CHECK_VERSION(3, 0, 0)
442 	gtk_widget_override_background_color(desktopicon->event,
443 			GTK_STATE_NORMAL, color);
444 #else
445 	gtk_widget_modify_bg(desktopicon->event, GTK_STATE_NORMAL, color);
446 #endif
447 }
448 
449 
450 /* desktopicon_set_callback */
desktopicon_set_callback(DesktopIcon * desktopicon,DesktopIconCallback callback,gpointer data)451 void desktopicon_set_callback(DesktopIcon * desktopicon,
452 		DesktopIconCallback callback, gpointer data)
453 {
454 	desktopicon->callback = callback;
455 	desktopicon->data = data;
456 }
457 
458 
459 /* desktopicon_set_confirm */
desktopicon_set_confirm(DesktopIcon * desktopicon,gboolean confirm)460 void desktopicon_set_confirm(DesktopIcon * desktopicon, gboolean confirm)
461 {
462 	desktopicon->confirm = confirm;
463 }
464 
465 
466 /* desktopicon_set_executable */
desktopicon_set_executable(DesktopIcon * desktopicon,gboolean executable)467 void desktopicon_set_executable(DesktopIcon * desktopicon, gboolean executable)
468 {
469 	desktopicon->isexec = executable;
470 }
471 
472 
473 /* desktopicon_set_first */
desktopicon_set_first(DesktopIcon * desktopicon,gboolean first)474 void desktopicon_set_first(DesktopIcon * desktopicon, gboolean first)
475 {
476 	desktopicon->isfirst = first;
477 }
478 
479 
480 /* desktopicon_set_font */
desktopicon_set_font(DesktopIcon * desktopicon,PangoFontDescription * font)481 void desktopicon_set_font(DesktopIcon * desktopicon,
482 		PangoFontDescription * font)
483 {
484 #if GTK_CHECK_VERSION(3, 0, 0)
485 	gtk_widget_override_font(desktopicon->label, font);
486 #else
487 	gtk_widget_modify_font(desktopicon->label, font);
488 #endif
489 }
490 
491 
492 /* desktopicon_set_foreground */
493 #if GTK_CHECK_VERSION(3, 0, 0)
desktopicon_set_foreground(DesktopIcon * desktopicon,GdkRGBA * color)494 void desktopicon_set_foreground(DesktopIcon * desktopicon, GdkRGBA * color)
495 #else
496 void desktopicon_set_foreground(DesktopIcon * desktopicon, GdkColor * color)
497 #endif
498 {
499 #if GTK_CHECK_VERSION(3, 0, 0)
500 	gtk_widget_override_color(desktopicon->event, GTK_STATE_NORMAL, color);
501 #else
502 	gtk_widget_modify_fg(desktopicon->label, GTK_STATE_NORMAL, color);
503 #endif
504 }
505 
506 
507 /* desktopicon_set_icon */
desktopicon_set_icon(DesktopIcon * desktopicon,GdkPixbuf * icon)508 void desktopicon_set_icon(DesktopIcon * desktopicon, GdkPixbuf * icon)
509 {
510 	_desktopicon_set_icon(desktopicon, icon);
511 	_desktopicon_update_transparency(desktopicon);
512 }
513 
514 
515 /* desktopicon_set_immutable */
desktopicon_set_immutable(DesktopIcon * desktopicon,gboolean immutable)516 void desktopicon_set_immutable(DesktopIcon * desktopicon, gboolean immutable)
517 {
518 	desktopicon->immutable = immutable;
519 }
520 
521 
522 /* desktopicon_set_name */
desktopicon_set_name(DesktopIcon * desktopicon,char const * name)523 int desktopicon_set_name(DesktopIcon * desktopicon, char const * name)
524 {
525 	if(_desktopicon_set_name(desktopicon, name) != 0)
526 		return 1;
527 	_desktopicon_update_transparency(desktopicon);
528 	return 0;
529 }
530 
531 
532 /* desktopicon_set_selected */
desktopicon_set_selected(DesktopIcon * desktopicon,gboolean selected)533 void desktopicon_set_selected(DesktopIcon * desktopicon, gboolean selected)
534 {
535 #ifdef DEBUG
536 	fprintf(stderr, "DEBUG: %p is %s\n", (void*)desktopicon,
537 			selected ? "selected" : "deselected");
538 #endif
539 	desktopicon->selected = selected;
540 #if GTK_CHECK_VERSION(3, 0, 0)
541 	gtk_widget_set_state_flags(desktopicon->event, selected
542 			? GTK_STATE_FLAG_SELECTED : GTK_STATE_FLAG_NORMAL,
543 			FALSE);
544 #else
545 	gtk_widget_set_state(desktopicon->event, selected
546 			? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
547 #endif
548 }
549 
550 
551 /* desktopicon_set_updated */
desktopicon_set_updated(DesktopIcon * desktopicon,gboolean updated)552 void desktopicon_set_updated(DesktopIcon * desktopicon, gboolean updated)
553 {
554 	desktopicon->updated = updated;
555 }
556 
557 
558 /* useful */
559 /* desktopicon_move */
desktopicon_move(DesktopIcon * desktopicon,int x,int y)560 void desktopicon_move(DesktopIcon * desktopicon, int x, int y)
561 {
562 #ifdef DEBUG
563 	fprintf(stderr, "DEBUG: %s(%d, %d)\n", __func__, x, y);
564 #endif
565 	gtk_window_move(GTK_WINDOW(desktopicon->window), x, y);
566 }
567 
568 
569 /* desktopicon_show */
desktopicon_show(DesktopIcon * desktopicon)570 void desktopicon_show(DesktopIcon * desktopicon)
571 {
572 	gtk_widget_show_all(desktopicon->window);
573 }
574 
575 
576 /* private */
577 /* desktopicon_new_do */
_desktopicon_new_do(Desktop * desktop,GdkPixbuf * image,char const * name)578 static DesktopIcon * _desktopicon_new_do(Desktop * desktop, GdkPixbuf * image,
579 		char const * name)
580 {
581 	DesktopIcon * desktopicon;
582 	GtkWindow * window;
583 	GtkWidget * vbox;
584 	GdkGeometry geometry;
585 
586 	if((desktopicon = object_new(sizeof(*desktopicon))) == NULL)
587 		return NULL;
588 	memset(desktopicon, 0, sizeof(*desktopicon));
589 	desktopicon->desktop = desktop;
590 	desktopicon->confirm = TRUE;
591 	desktopicon->updated = TRUE;
592 	/* window */
593 	desktopicon->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
594 	window = GTK_WINDOW(desktopicon->window);
595 	gtk_window_set_decorated(window, FALSE);
596 #if GTK_CHECK_VERSION(2, 6, 0)
597 	gtk_window_set_focus_on_map(window, FALSE);
598 #endif
599 	gtk_window_set_keep_below(window, TRUE);
600 	gtk_window_set_resizable(window, FALSE);
601 	gtk_window_set_skip_pager_hint(window, TRUE);
602 #ifdef EMBEDDED
603 	gtk_window_set_type_hint(window, GDK_WINDOW_TYPE_HINT_UTILITY);
604 #else
605 	gtk_window_set_type_hint(window, GDK_WINDOW_TYPE_HINT_DOCK);
606 #endif
607 	g_signal_connect(G_OBJECT(desktopicon->window), "delete-event",
608 			G_CALLBACK(_on_desktopicon_closex), NULL);
609 	/* event */
610 	desktopicon->event = gtk_event_box_new();
611 	g_signal_connect(G_OBJECT(desktopicon->event), "button-press-event",
612 			G_CALLBACK(_on_icon_button_press), desktopicon);
613 	g_signal_connect(G_OBJECT(desktopicon->event), "key-press-event",
614 			G_CALLBACK(_on_icon_key_press), desktopicon);
615 #if GTK_CHECK_VERSION(3, 0, 0)
616 	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
617 #else
618 	vbox = gtk_vbox_new(FALSE, 4);
619 #endif
620 	geometry.min_width = DESKTOPICON_MIN_WIDTH;
621 	geometry.min_height = DESKTOPICON_MIN_HEIGHT;
622 	geometry.max_width = DESKTOPICON_MAX_WIDTH;
623 	geometry.max_height = DESKTOPICON_MAX_HEIGHT;
624 	geometry.base_width = DESKTOPICON_MIN_WIDTH;
625 	geometry.base_height = DESKTOPICON_MIN_HEIGHT;
626 	gtk_window_set_geometry_hints(window, vbox, &geometry, /* XXX check */
627 			GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE
628 			| GDK_HINT_BASE_SIZE);
629 	/* image */
630 	desktopicon->image = gtk_image_new();
631 	gtk_widget_set_size_request(desktopicon->image, DESKTOPICON_ICON_SIZE,
632 			DESKTOPICON_ICON_SIZE);
633 	gtk_box_pack_start(GTK_BOX(vbox), desktopicon->image, FALSE, TRUE, 0);
634 	/* label */
635 	desktopicon->label = gtk_label_new(NULL);
636 #if GTK_CHECK_VERSION(3, 0, 0)
637 	g_object_set(desktopicon->label, "valign", GTK_ALIGN_START, NULL);
638 #else
639 	gtk_misc_set_alignment(GTK_MISC(desktopicon->label), 0.5, 0.0);
640 #endif
641 #if GTK_CHECK_VERSION(2, 10, 0)
642 	gtk_label_set_line_wrap_mode(GTK_LABEL(desktopicon->label),
643 			PANGO_WRAP_WORD_CHAR);
644 #endif
645 	gtk_label_set_line_wrap(GTK_LABEL(desktopicon->label), TRUE);
646 	gtk_box_pack_start(GTK_BOX(vbox), desktopicon->label, TRUE, TRUE, 4);
647 	gtk_container_add(GTK_CONTAINER(desktopicon->event), vbox);
648 	gtk_container_add(GTK_CONTAINER(desktopicon->window),
649 			desktopicon->event);
650 	if(image == NULL)
651 	{
652 		image = desktop_get_file(desktop);
653 		_desktopicon_set_icon(desktopicon, image);
654 		g_object_unref(image);
655 	}
656 	else
657 		_desktopicon_set_icon(desktopicon, image);
658 	_desktopicon_set_name(desktopicon, name);
659 	_desktopicon_update_transparency(desktopicon);
660 	return desktopicon;
661 }
662 
663 
664 /* desktopicon_set_icon */
_desktopicon_set_icon(DesktopIcon * desktopicon,GdkPixbuf * icon)665 static void _desktopicon_set_icon(DesktopIcon * desktopicon, GdkPixbuf * icon)
666 {
667 	GdkPixbuf * i = NULL;
668 #ifdef EMBEDDED
669 	const GdkInterpType interp = GDK_INTERP_NEAREST;
670 #else
671 	const GdkInterpType interp = GDK_INTERP_HYPER;
672 #endif
673 
674 	if(icon == NULL)
675 		return;
676 	if(gdk_pixbuf_get_width(icon) != DESKTOPICON_ICON_SIZE
677 			&& gdk_pixbuf_get_height(icon) != DESKTOPICON_ICON_SIZE
678 			&& (i = gdk_pixbuf_scale_simple(icon,
679 					DESKTOPICON_ICON_SIZE,
680 					DESKTOPICON_ICON_SIZE, interp)) != NULL)
681 		icon = i;
682 	gtk_image_set_from_pixbuf(GTK_IMAGE(desktopicon->image), icon);
683 	if(i != NULL)
684 		g_object_unref(i);
685 }
686 
687 
688 /* desktopicon_set_name */
_desktopicon_set_name(DesktopIcon * desktopicon,char const * name)689 static int _desktopicon_set_name(DesktopIcon * desktopicon, char const * name)
690 {
691 	char * p;
692 
693 	if((p = strdup(name)) == NULL)
694 		return 1;
695 	free(desktopicon->name);
696 	desktopicon->name = p;
697 	gtk_label_set_text(GTK_LABEL(desktopicon->label), p);
698 	return 0;
699 }
700 
701 
702 /* desktopicon_update_transparency */
_desktopicon_update_transparency(DesktopIcon * desktopicon)703 static void _desktopicon_update_transparency(DesktopIcon * desktopicon)
704 {
705 	GdkPixbuf * icon;
706 	int width;
707 	int height;
708 	int iwidth;
709 	int iheight;
710 #if GTK_CHECK_VERSION(3, 0, 0)
711 	GdkRGBA black = { 0.0, 0.0, 0.0, 1.0 };
712 	GdkRGBA white = { 1.0, 1.0, 1.0, 1.0 };
713 #else
714 	GdkBitmap * mask;
715 	GdkBitmap * iconmask;
716 	GdkGC * gc;
717 	GdkColor black = { 0, 0, 0, 0 };
718 	GdkColor white = { 0xffffffff, 0xffff, 0xffff, 0xffff };
719 #endif
720 	GtkRequisition req;
721 	int offset;
722 
723 	if((icon = gtk_image_get_pixbuf(GTK_IMAGE(desktopicon->image))) == NULL)
724 		return; /* XXX report error */
725 	gtk_window_get_size(GTK_WINDOW(desktopicon->window), &width, &height);
726 	iwidth = gdk_pixbuf_get_width(icon);
727 	iheight = gdk_pixbuf_get_height(icon);
728 #ifdef DEBUG
729 	fprintf(stderr, "DEBUG: %s(\"%s\") window is %dx%d\n", __func__,
730 			desktopicon->name, width, height);
731 #endif
732 #if GTK_CHECK_VERSION(3, 0, 0)
733 	/* FIXME re-implement */
734 	gtk_widget_get_preferred_size(desktopicon->label, NULL, &req);
735 #else
736 	mask = gdk_pixmap_new(NULL, width, height, 1);
737 	gdk_pixbuf_render_pixmap_and_mask(icon, NULL, &iconmask, 255);
738 	gc = gdk_gc_new(mask);
739 	gdk_gc_set_foreground(gc, &black);
740 	gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, height);
741 	gdk_draw_drawable(mask, gc, iconmask, 0, 0, (width - iwidth) / 2,
742 			(DESKTOPICON_ICON_SIZE - iheight) / 2, -1, -1);
743 	gdk_gc_set_foreground(gc, &white);
744 	gtk_widget_size_request(desktopicon->label, &req);
745 # ifdef DEBUG
746 	fprintf(stderr, "DEBUG: %s(\"%s\") label is %dx%d\n", __func__,
747 			desktopicon->name, req.width, req.height);
748 # endif
749 	offset = DESKTOPICON_ICON_SIZE + 4;
750 	gdk_draw_rectangle(mask, gc, TRUE, (width - req.width - 8) / 2,
751 			offset /* + ((height - offset - req.height - 8)
752 				/ 2) */, req.width + 8, req.height + 8);
753 	gtk_widget_shape_combine_mask(desktopicon->window, mask, 0, 0);
754 	g_object_unref(gc);
755 	g_object_unref(iconmask);
756 	g_object_unref(mask);
757 #endif
758 }
759 
760 
761 /* callbacks */
762 /* on_desktopicon_closex */
_on_desktopicon_closex(void)763 static gboolean _on_desktopicon_closex(void)
764 {
765 	return TRUE;
766 }
767 
768 
769 /* FIXME some code is duplicated from callbacks.c */
770 /* on_icon_button_press */
771 static void _popup_directory(GtkWidget * menu, DesktopIcon * desktopicon);
772 static void _popup_callback(GtkWidget * menu, DesktopIcon * desktopicon);
773 static void _popup_file(GtkWidget * menu, DesktopIcon * desktopicon);
774 static void _popup_mime(Mime * mime, char const * mimetype, char const * action,
775 		char const * label, GCallback callback, DesktopIcon * icon,
776 		GtkWidget * menu);
777 
_on_icon_button_press(GtkWidget * widget,GdkEventButton * event,gpointer data)778 static gboolean _on_icon_button_press(GtkWidget * widget,
779 		GdkEventButton * event, gpointer data)
780 {
781 	DesktopIcon * desktopicon = data;
782 	GtkWidget * menu;
783 	GtkWidget * menuitem;
784 
785 	if(event->state & GDK_CONTROL_MASK)
786 		desktopicon_set_selected(desktopicon, !desktopicon_get_selected(
787 					desktopicon));
788 	else
789 	{
790 		desktop_unselect_all(desktopicon->desktop);
791 		desktopicon_set_selected(desktopicon, TRUE);
792 	}
793 	/* single click open for applications */
794 	if(desktopicon->path == NULL && event->type == GDK_BUTTON_PRESS
795 			&& event->button == 1)
796 	{
797 		if(desktopicon->isexec == TRUE)
798 			_on_icon_run(desktopicon);
799 		else if(desktopicon->callback != NULL)
800 			_on_icon_open(desktopicon);
801 		return FALSE;
802 	}
803 	if(event->type == GDK_2BUTTON_PRESS && event->button == 1)
804 	{
805 		if(desktopicon->isexec == TRUE) /* XXX slightly ugly */
806 			_on_icon_run(desktopicon);
807 		else
808 			_on_icon_open(desktopicon);
809 		return FALSE;
810 	}
811 	if(event->type != GDK_BUTTON_PRESS || event->button != 3)
812 		return FALSE;
813 	/* popup menu */
814 	menu = gtk_menu_new();
815 	if(desktopicon->isdir == TRUE)
816 		_popup_directory(menu, desktopicon);
817 	else if(desktopicon->callback != NULL)
818 		_popup_callback(menu, desktopicon);
819 	else
820 		_popup_file(menu, desktopicon);
821 	if(desktopicon->immutable == FALSE)
822 	{
823 		menuitem = gtk_separator_menu_item_new();
824 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
825 		menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_DELETE,
826 				NULL);
827 		g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
828 				G_CALLBACK(_on_icon_delete), desktopicon);
829 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
830 	}
831 	if(desktopicon->path != NULL)
832 	{
833 		menuitem = gtk_separator_menu_item_new();
834 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
835 		menuitem = gtk_image_menu_item_new_from_stock(
836 				GTK_STOCK_PROPERTIES, NULL);
837 		g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
838 				G_CALLBACK(_on_icon_properties), desktopicon);
839 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
840 	}
841 	gtk_widget_show_all(menu);
842 	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, event->time);
843 	return TRUE;
844 }
845 
_popup_directory(GtkWidget * menu,DesktopIcon * desktopicon)846 static void _popup_directory(GtkWidget * menu, DesktopIcon * desktopicon)
847 {
848 	GtkWidget * menuitem;
849 
850 	menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);
851 	g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK(
852 				_on_icon_open), desktopicon);
853 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
854 	if(desktopicon->immutable == FALSE)
855 	{
856 		menuitem = gtk_separator_menu_item_new();
857 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
858 		menuitem = gtk_menu_item_new_with_mnemonic(_("_Rename..."));
859 		g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
860 				G_CALLBACK(_on_icon_rename), desktopicon);
861 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
862 	}
863 }
864 
_popup_callback(GtkWidget * menu,DesktopIcon * desktopicon)865 static void _popup_callback(GtkWidget * menu, DesktopIcon * desktopicon)
866 {
867 	GtkWidget * menuitem;
868 
869 	menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL);
870 	g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
871 			G_CALLBACK(_on_icon_open), desktopicon);
872 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
873 }
874 
_popup_file(GtkWidget * menu,DesktopIcon * desktopicon)875 static void _popup_file(GtkWidget * menu, DesktopIcon * desktopicon)
876 {
877 	Mime * mime;
878 	GtkWidget * menuitem;
879 
880 	mime = desktop_get_mime(desktopicon->desktop);
881 	_popup_mime(mime, desktopicon->mimetype, "open", GTK_STOCK_OPEN,
882 			G_CALLBACK(_on_icon_open), desktopicon, menu);
883 	_popup_mime(mime, desktopicon->mimetype, "edit",
884 #if GTK_CHECK_VERSION(2, 6, 0)
885 			GTK_STOCK_EDIT,
886 #else
887 			_("_Edit"),
888 #endif
889 			G_CALLBACK(_on_icon_edit), desktopicon, menu);
890 	if(desktopicon->isexec == TRUE)
891 	{
892 		menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_EXECUTE,
893 				NULL);
894 		g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
895 				G_CALLBACK(_on_icon_run), desktopicon);
896 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
897 	}
898 	if(desktopicon->path != NULL && desktopicon->path[0] == '/')
899 	{
900 		menuitem = gtk_menu_item_new_with_mnemonic(_("Open _with..."));
901 		g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
902 				G_CALLBACK(_on_icon_open_with), desktopicon);
903 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
904 		if(desktopicon->immutable == FALSE)
905 		{
906 			menuitem = gtk_separator_menu_item_new();
907 			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
908 			menuitem = gtk_menu_item_new_with_mnemonic(
909 					_("_Rename..."));
910 			g_signal_connect_swapped(G_OBJECT(menuitem), "activate",
911 					G_CALLBACK(_on_icon_rename),
912 					desktopicon);
913 			gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
914 		}
915 	}
916 }
917 
_popup_mime(Mime * mime,char const * mimetype,char const * action,char const * label,GCallback callback,DesktopIcon * desktopicon,GtkWidget * menu)918 static void _popup_mime(Mime * mime, char const * mimetype, char const * action,
919 		char const * label, GCallback callback,
920 		DesktopIcon * desktopicon, GtkWidget * menu)
921 {
922 	GtkWidget * menuitem;
923 
924 	if(mime_get_handler(mime, mimetype, action) == NULL)
925 		return;
926 	if(strncmp(label, "gtk-", 4) == 0)
927 		menuitem = gtk_image_menu_item_new_from_stock(label, NULL);
928 	else
929 		menuitem = gtk_menu_item_new_with_mnemonic(label);
930 	g_signal_connect_swapped(G_OBJECT(menuitem), "activate", callback,
931 			desktopicon);
932 	gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
933 }
934 
935 
936 /* on_icon_open */
_on_icon_open(gpointer data)937 static void _on_icon_open(gpointer data)
938 {
939 	DesktopIcon * desktopicon = data;
940 	Mime * mime;
941 	char * argv[] = { BINDIR "/browser", "browser", "--", NULL, NULL };
942 	const unsigned int flags = G_SPAWN_FILE_AND_ARGV_ZERO;
943 	GError * error = NULL;
944 
945 	if(desktopicon->path == NULL && desktopicon->callback != NULL)
946 	{
947 		desktopicon->callback(desktopicon->desktop, desktopicon->data);
948 		return;
949 	}
950 	if(desktopicon->isdir == FALSE)
951 	{
952 		mime = desktop_get_mime(desktopicon->desktop);
953 		if(mime != NULL) /* XXX ugly */
954 			if(mime_action(mime, "open", desktopicon->path) != 0)
955 				_on_icon_open_with(desktopicon);
956 		return;
957 	}
958 	argv[3] = desktopicon->path;
959 	if(g_spawn_async(NULL, argv, NULL, flags, NULL, NULL, NULL, &error)
960 			!= TRUE)
961 	{
962 		desktop_error(desktopicon->desktop, error->message, 1);
963 		g_error_free(error);
964 	}
965 }
966 
967 
968 /* on_icon_edit */
_on_icon_edit(gpointer data)969 static void _on_icon_edit(gpointer data)
970 {
971 	DesktopIcon * desktopicon = data;
972 	Mime * mime;
973 
974 	mime = desktop_get_mime(desktopicon->desktop);
975 	mime_action(mime, "edit", desktopicon->path);
976 }
977 
978 
979 /* on_icon_run */
980 static void _run_application(DesktopIcon * desktopicon);
981 static void _run_binary(DesktopIcon * desktopicon);
982 static gboolean _run_confirm(DesktopIcon * desktopicon);
983 static void _run_directory(DesktopIcon * desktopicon);
984 static void _run_url(DesktopIcon * desktopicon);
985 
_on_icon_run(gpointer data)986 static void _on_icon_run(gpointer data)
987 {
988 	DesktopIcon * desktopicon = data;
989 	const char section[] = "Desktop Entry";
990 	char const * p;
991 
992 	if(desktopicon->confirm != FALSE && _run_confirm(desktopicon) != TRUE)
993 		return;
994 	if(desktopicon->config == NULL)
995 		_run_binary(desktopicon);
996 	else if((p = config_get(desktopicon->config, section, "Type")) == NULL)
997 		return;
998 	else if(strcmp(p, "Application") == 0)
999 		_run_application(desktopicon);
1000 	else if(strcmp(p, "Directory") == 0)
1001 		_run_directory(desktopicon);
1002 	else if(strcmp(p, "URL") == 0)
1003 		_run_url(desktopicon);
1004 }
1005 
_run_application(DesktopIcon * desktopicon)1006 static void _run_application(DesktopIcon * desktopicon)
1007 {
1008 	/* XXX code duplicated from DeforaOS Panel */
1009 	const char section[] = "Desktop Entry";
1010 	char * program;
1011 	char * p;
1012 	char const * q;
1013 	pid_t pid;
1014 	GError * error = NULL;
1015 
1016 	if((q = config_get(desktopicon->config, section, "Exec")) == NULL)
1017 		return;
1018 	if((program = strdup(q)) == NULL)
1019 		return; /* XXX report error */
1020 	/* XXX crude way to ignore %f, %F, %u and %U */
1021 	if((p = strchr(program, '%')) != NULL)
1022 		*p = '\0';
1023 #ifdef DEBUG
1024 	fprintf(stderr, "DEBUG: %s() \"%s\"", __func__, program);
1025 #endif
1026 	if((q = config_get(desktopicon->config, section, "Path")) == NULL)
1027 	{
1028 		/* execute the program directly */
1029 		if(g_spawn_command_line_async(program, &error) != TRUE)
1030 		{
1031 			desktop_error(desktopicon->desktop, error->message, 1);
1032 			g_error_free(error);
1033 		}
1034 	}
1035 	else if((pid = fork()) == 0)
1036 	{
1037 		/* change the current working directory */
1038 		if(chdir(q) != 0)
1039 			desktop_error(desktopicon->desktop, strerror(errno), 1);
1040 		else if(g_spawn_command_line_async(program, &error) != TRUE)
1041 		{
1042 			desktop_error(desktopicon->desktop, error->message, 1);
1043 			g_error_free(error);
1044 		}
1045 		exit(0);
1046 	}
1047 	else if(pid < 0)
1048 		desktop_error(desktopicon->desktop, strerror(errno), 1);
1049 	free(program);
1050 }
1051 
_run_binary(DesktopIcon * desktopicon)1052 static void _run_binary(DesktopIcon * desktopicon)
1053 {
1054 	char * argv[] = { NULL, NULL };
1055 	const unsigned int flags = 0;
1056 	GError * error = NULL;
1057 
1058 	argv[0] = desktopicon->path;
1059 	if(g_spawn_async(NULL, argv, NULL, flags, NULL, NULL, NULL, &error)
1060 			!= TRUE)
1061 	{
1062 		desktop_error(desktopicon->desktop, error->message, 1);
1063 		g_error_free(error);
1064 	}
1065 }
1066 
_run_confirm(DesktopIcon * desktopicon)1067 static gboolean _run_confirm(DesktopIcon * desktopicon)
1068 {
1069 	GtkWidget * dialog;
1070 	int res;
1071 
1072 	dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
1073 			GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, "%s",
1074 #if GTK_CHECK_VERSION(2, 6, 0)
1075 			_("Warning"));
1076 	gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1077 			"%s",
1078 #endif
1079 			_("Are you sure you want to execute this file?"));
1080 	gtk_window_set_title(GTK_WINDOW(dialog), _("Warning"));
1081 	res = gtk_dialog_run(GTK_DIALOG(dialog));
1082 	gtk_widget_destroy(dialog);
1083 	return (res == GTK_RESPONSE_YES) ? TRUE : FALSE;
1084 }
1085 
_run_directory(DesktopIcon * desktopicon)1086 static void _run_directory(DesktopIcon * desktopicon)
1087 {
1088 	const char section[] = "Desktop Entry";
1089 	char const * directory;
1090 	/* XXX open with the default file manager instead */
1091 	char * argv[] = { "browser", "--", NULL, NULL };
1092 	const unsigned int flags = G_SPAWN_SEARCH_PATH;
1093 	GError * error = NULL;
1094 
1095 	/* XXX this may not might the correct key */
1096 	if((directory = config_get(desktopicon->config, section, "Path"))
1097 			== NULL)
1098 		return;
1099 	if((argv[2] = strdup(directory)) == NULL)
1100 		desktop_error(desktopicon->desktop, strerror(errno), 1);
1101 	else if(g_spawn_async(NULL, argv, NULL, flags, NULL, NULL, NULL, &error)
1102 			!= TRUE)
1103 	{
1104 		desktop_error(desktopicon->desktop, error->message, 1);
1105 		g_error_free(error);
1106 	}
1107 	free(argv[2]);
1108 }
1109 
_run_url(DesktopIcon * desktopicon)1110 static void _run_url(DesktopIcon * desktopicon)
1111 {
1112 	const char section[] = "Desktop Entry";
1113 	char const * url;
1114 	/* XXX open with the default web browser instead */
1115 	char * argv[] = { "htmlapp", "--", NULL, NULL };
1116 	const unsigned int flags = G_SPAWN_SEARCH_PATH;
1117 	GError * error = NULL;
1118 
1119 	if((url = config_get(desktopicon->config, section, "URL")) == NULL)
1120 		return;
1121 	if((argv[2] = strdup(url)) == NULL)
1122 		desktop_error(desktopicon->desktop, strerror(errno), 1);
1123 	else if(g_spawn_async(NULL, argv, NULL, flags, NULL, NULL, NULL,
1124 				&error) != TRUE)
1125 	{
1126 		desktop_error(desktopicon->desktop, error->message, 1);
1127 		g_error_free(error);
1128 	}
1129 	free(argv[2]);
1130 }
1131 
1132 
1133 /* on_icon_open_with */
_on_icon_open_with(gpointer data)1134 static void _on_icon_open_with(gpointer data)
1135 {
1136 	DesktopIcon * desktopicon = data;
1137 	GtkWidget * dialog;
1138 	char * filename = NULL;
1139 	char * argv[] = { NULL, NULL, NULL, NULL };
1140 	const unsigned int flags = G_SPAWN_SEARCH_PATH
1141 		| G_SPAWN_FILE_AND_ARGV_ZERO;
1142 	GError * error = NULL;
1143 
1144 	dialog = gtk_file_chooser_dialog_new(_("Open with..."),
1145 			GTK_WINDOW(desktopicon->window),
1146 			GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL,
1147 			GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN,
1148 			GTK_RESPONSE_ACCEPT, NULL);
1149 	if(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
1150 		filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(
1151 					dialog));
1152 	gtk_widget_destroy(dialog);
1153 	if(filename == NULL)
1154 		return;
1155 	argv[0] = filename;
1156 	argv[1] = filename;
1157 	argv[2] = desktopicon->path;
1158 	if(g_spawn_async(NULL, argv, NULL, flags, NULL, NULL, NULL, &error)
1159 			!= TRUE)
1160 	{
1161 		desktop_error(desktopicon->desktop, error->message, 1);
1162 		g_error_free(error);
1163 	}
1164 	g_free(filename);
1165 }
1166 
1167 
1168 /* on_icon_rename */
_on_icon_rename(gpointer data)1169 static void _on_icon_rename(gpointer data)
1170 {
1171 	DesktopIcon * desktopicon = data;
1172 	GtkWidget * dialog;
1173 	GtkSizeGroup * group;
1174 	GtkWidget * vbox;
1175 	GtkWidget * hbox;
1176 	GtkWidget * widget;
1177 	int res;
1178 	char * p;
1179 	char * q;
1180 	char * r;
1181 
1182 	dialog = gtk_dialog_new_with_buttons(_("Rename"), NULL, 0,
1183 			GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, _("Rename"),
1184 			GTK_RESPONSE_ACCEPT, NULL);
1185 #if GTK_CHECK_VERSION(2, 14, 0)
1186 	vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
1187 #else
1188 	vbox = GTK_DIALOG(dialog)->vbox;
1189 #endif
1190 	group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1191 #if GTK_CHECK_VERSION(3, 0, 0)
1192 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
1193 #else
1194 	hbox = gtk_hbox_new(FALSE, 4);
1195 #endif
1196 	widget = gtk_label_new(_("Rename: "));
1197 #if GTK_CHECK_VERSION(3, 0, 0)
1198 	g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
1199 #else
1200 	gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
1201 #endif
1202 	gtk_size_group_add_widget(group, widget);
1203 	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
1204 	widget = gtk_entry_new();
1205 	gtk_editable_set_editable(GTK_EDITABLE(widget), FALSE);
1206 	gtk_entry_set_text(GTK_ENTRY(widget), desktopicon->name);
1207 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
1208 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1209 	/* entry */
1210 #if GTK_CHECK_VERSION(3, 0, 0)
1211 	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
1212 #else
1213 	hbox = gtk_hbox_new(FALSE, 4);
1214 #endif
1215 	widget = gtk_label_new(_("To: "));
1216 #if GTK_CHECK_VERSION(3, 0, 0)
1217 	g_object_set(widget, "halign", GTK_ALIGN_START, NULL);
1218 #else
1219 	gtk_misc_set_alignment(GTK_MISC(widget), 0.0, 0.5);
1220 #endif
1221 	gtk_size_group_add_widget(group, widget);
1222 	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
1223 	widget = gtk_entry_new();
1224 	gtk_entry_set_text(GTK_ENTRY(widget), desktopicon->name);
1225 	gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0);
1226 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 0);
1227 	gtk_widget_show_all(vbox);
1228 	res = gtk_dialog_run(GTK_DIALOG(dialog));
1229 	gtk_widget_hide(dialog);
1230 	if(res != GTK_RESPONSE_ACCEPT)
1231 	{
1232 		gtk_widget_destroy(dialog);
1233 		return;
1234 	}
1235 	/* FIXME check errors */
1236 	p = string_new(desktopicon->path);
1237 	q = string_new(gtk_entry_get_text(GTK_ENTRY(widget)));
1238 	/* FIXME convert entry from UTF-8 to filesystem's charset */
1239 	if(q[0] == '/')
1240 		r = string_new(q);
1241 	else
1242 		r = string_new_append(dirname(p), "/", q, NULL);
1243 #ifdef DEBUG
1244 	fprintf(stderr, "DEBUG: %s() rename(\"%s\", \"%s\")\n", __func__,
1245 			desktopicon->path, r);
1246 #else
1247 	if(rename(desktopicon->path, r) != 0)
1248 		desktop_error(desktopicon->desktop, strerror(errno), 1);
1249 #endif
1250 	string_delete(p);
1251 	string_delete(q);
1252 	string_delete(r);
1253 	gtk_widget_destroy(dialog);
1254 }
1255 
1256 
1257 /* on_icon_delete */
_on_icon_delete(gpointer data)1258 static void _on_icon_delete(gpointer data)
1259 {
1260 	DesktopIcon * desktopicon = data;
1261 	GtkWidget * dialog;
1262 	unsigned long cnt = 1; /* FIXME implement */
1263 	int res;
1264 	GList * selection = NULL;
1265 
1266 	/* FIXME duplicated from callbacks.c */
1267 	dialog = gtk_message_dialog_new(GTK_WINDOW(desktopicon->window),
1268 			GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1269 			GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, "%s",
1270 			_("Warning"));
1271 	gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(
1272 				dialog), "%s%lu%s",
1273 			_("Are you sure you want to delete "), cnt,
1274 			_(" file(s)?"));
1275 	gtk_window_set_title(GTK_WINDOW(dialog), _("Warning"));
1276 	res = gtk_dialog_run(GTK_DIALOG(dialog));
1277 	gtk_widget_destroy(dialog);
1278 	if(res == GTK_RESPONSE_YES)
1279 	{
1280 		/* FIXME check if needs UTF-8 conversion */
1281 		selection = g_list_append(selection, desktopicon->path);
1282 		if(_common_exec("delete", "-ir", selection) != 0)
1283 			desktop_error(desktopicon->desktop, strerror(errno),
1284 					1);
1285 		g_list_free(selection);
1286 	}
1287 }
1288 
1289 
1290 /* on_icon_properties */
_on_icon_properties(gpointer data)1291 static void _on_icon_properties(gpointer data)
1292 {
1293 	DesktopIcon * desktopicon = data;
1294 	char * argv[] = { BINDIR "/properties", "properties", "--", NULL,
1295 		NULL };
1296 	const unsigned int flags = G_SPAWN_FILE_AND_ARGV_ZERO;
1297 	GError * error = NULL;
1298 
1299 	argv[3] = desktopicon->path;
1300 	if(g_spawn_async(NULL, argv, NULL, flags, NULL, NULL, NULL, &error)
1301 			!= TRUE)
1302 	{
1303 		desktop_error(desktopicon->desktop, error->message, 1);
1304 		g_error_free(error);
1305 	}
1306 }
1307 
1308 
1309 /* on_icon_key_press */
_on_icon_key_press(GtkWidget * widget,GdkEventKey * event,gpointer data)1310 static gboolean _on_icon_key_press(GtkWidget * widget, GdkEventKey * event,
1311 		gpointer data)
1312 	/* FIXME handle shift and control */
1313 {
1314 	DesktopIcon * desktopicon = data;
1315 
1316 	if(event->type != GDK_KEY_PRESS)
1317 		return FALSE;
1318 	if(event->keyval == GDK_KEY_uparrow)
1319 	{
1320 		desktop_unselect_all(desktopicon->desktop);
1321 		desktop_select_above(desktopicon->desktop, desktopicon);
1322 	}
1323 	else if(event->keyval == GDK_KEY_downarrow)
1324 	{
1325 		desktop_unselect_all(desktopicon->desktop);
1326 		desktop_select_under(desktopicon->desktop, desktopicon);
1327 	}
1328 	else /* not handling it */
1329 		return FALSE;
1330 	return TRUE;
1331 }
1332 
1333 
1334 /* on_icon_drag_data_get */
_on_icon_drag_data_get(GtkWidget * widget,GdkDragContext * context,GtkSelectionData * seldata,guint info,guint time,gpointer data)1335 static void _on_icon_drag_data_get(GtkWidget * widget, GdkDragContext * context,
1336 		GtkSelectionData * seldata, guint info, guint time,
1337 		gpointer data)
1338 {
1339 	DesktopIcon * desktopicon = data;
1340 
1341 	desktop_get_drag_data(desktopicon->desktop, seldata);
1342 }
1343 
1344 
1345 /* on_icon_drag_data_received */
_on_icon_drag_data_received(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * seldata,guint info,guint time,gpointer data)1346 static void _on_icon_drag_data_received(GtkWidget * widget,
1347 		GdkDragContext * context, gint x, gint y,
1348 		GtkSelectionData * seldata, guint info, guint time,
1349 		gpointer data)
1350 {
1351 	DesktopIcon * desktopicon = data;
1352 
1353 	if(_common_drag_data_received(context, seldata, desktopicon->path) != 0)
1354 		desktop_error(desktopicon->desktop, strerror(errno), 1);
1355 }
1356