<commit_before>/* $Id$ */
/* Copyright (c) 2011 <NAME> <<EMAIL>> */
/* This file is part of DeforaOS Desktop Browser */
/* This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. */



#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include <errno.h>
#include <libintl.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <System.h>
#include "mime.h"
#include "desktopicon.h"
#include "../config.h"
#define _(string) gettext(string)

#define PROGNAME "desktop"
#define COMMON_DND
#define COMMON_EXEC
#include "common.c"


/* constants */
#ifndef PREFIX
# define PREFIX	"/usr/local"
#endif


/* DesktopIcon */
/* types */
struct _DesktopIcon
{
	Desktop * desktop;
	char * path;
	char * name;
	gboolean isfirst;
	gboolean isdir;
	gboolean isexec;
	char const * mimetype;
	/* applications */
	char * exec;
	char * tryexec;

	/* callback */
	DesktopIconCallback callback;
	gpointer data;

	gboolean confirm;
	gboolean immutable;		/* cannot be deleted */
	gboolean selected;
	gboolean updated;		/* XXX for desktop refresh */

	GtkWidget * window;
	GtkWidget * image;
	GtkWidget * event;
	GtkWidget * label;
};


/* prototypes */
static DesktopIcon * _desktopicon_new_do(Desktop * desktop, GdkPixbuf * image,
		char const * name);

static void _desktopicon_set_icon(DesktopIcon * desktopicon, GdkPixbuf * icon);
static int _desktopicon_set_name(DesktopIcon * desktopicon, char const * name);

static void _desktopicon_update_transparency(DesktopIcon * desktopicon);

/* callbacks */
static gboolean _on_desktopicon_closex(void);
static gboolean _on_icon_button_press(GtkWidget * widget,
		GdkEventButton * event, gpointer data);
static gboolean _on_icon_key_press(GtkWidget * widget, GdkEventKey * event,
		gpointer data);
static void _on_icon_drag_data_get(GtkWidget * widget, GdkDragContext * context,
		GtkSelectionData * seldata, guint info, guint time,
		gpointer data);
static void _on_icon_drag_data_received(GtkWidget * widget,
		GdkDragContext * context, gint x, gint y,
		GtkSelectionData * seldata, guint info, guint time,
		gpointer data);
static void _on_icon_open(gpointer data);
static void _on_icon_edit(gpointer data);
static void _on_icon_run(gpointer data);
static void _on_icon_open_with(gpointer data);
static void _on_icon_rename(gpointer data);
static void _on_icon_delete(gpointer data);
static void _on_icon_properties(gpointer data);


/* public */
/* functions */
/* desktopicon_new */
DesktopIcon * desktopicon_new(Desktop * desktop, char const * name,
		char const * path)
{
	DesktopIcon * desktopicon;
	struct stat st;
	Mime * mime;
	char const * mimetype = NULL;
	gboolean isdir = FALSE;
	gboolean isexec = FALSE;
	GdkPixbuf * image = NULL;
	GError * error = NULL;
	char * p;

#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(%p, \"%s\", \"%s\")\n", __func__,
			(void *)desktop, name, path);
#endif
	if(path != NULL && stat(path, &st) == 0)
	{
		mime = desktop_get_mime(desktop);
		if(S_ISDIR(st.st_mode))
		{
			isdir = TRUE;
			image = desktop_get_folder(desktop);
		}
		else if(st.st_mode & S_IXUSR)
		{
			/* FIXME use access() for this */
			isexec = TRUE;
			mime_icons(desktop_get_mime(desktop),
					desktop_get_theme(desktop),
					"application/x-executable",
					DESKTOPICON_ICON_SIZE, &image, -1);
		}
		else if((mimetype = mime_type(mime, path)) != NULL)
			mime_icons(mime, desktop_get_theme(desktop), mimetype,
					DESKTOPICON_ICON_SIZE, &image, -1);
	}
	if(name == NULL)
	{
		if((p = g_filename_to_utf8(path, -1, NULL, NULL, &error))
				== NULL)
		{
			fprintf(stderr, "%s%s\n", "desktop: ", error->message);
			name = path;
		}
		else
			name = p;
		if((name = strrchr(name, '/')) != NULL)
			name++;
	}
	if((desktopicon = _desktopicon_new_do(desktop, image, name)) == NULL)
		return NULL;
	desktopicon->isdir = isdir;
	desktopicon_set_executable(desktopicon, isexec);
	desktopicon->mimetype = mimetype;
	if(path != NULL && (desktopicon->path = strdup(path)) == NULL)
	{
		desktopicon_delete(desktopicon);
		return NULL;
	}
	return desktopicon;
}


/* desktopicon_new_application */
DesktopIcon * desktopicon_new_application(Desktop * desktop, char const * path)
{
	DesktopIcon * desktopicon;
	Config * config;
	const char section[] = "Desktop Entry";
	char const * name;
	char const * icon;
	char * exec = NULL;
	char * tryexec = NULL;
	size_t len;
	String * buf;
	GError * error = NULL;
	GdkPixbuf * image = NULL;

#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(%p, \"%s\")\n", __func__, (void *)desktop,
			path);
#endif
	if((config = config_new()) == NULL
			|| config_load(config, path) != 0)
	{
		if(config != NULL)
			config_delete(config);
		return NULL;
	}
	if((name = config_get(config, section, "Exec")) != NULL)
		exec = strdup(name);
	if((name = config_get(config, section, "TryExec")) != NULL)
		tryexec = strdup(name);
	if(exec == NULL || (name = config_get(config, section, "Name")) == NULL
			|| (icon = config_get(config, section, "Icon")) == NULL)
	{
		free(exec);
		free(tryexec);
		config_delete(config);
		return NULL;
	}
	/* image */
	if((len = strlen(icon)) > 4 && (strcmp(&icon[len - 4], ".png") == 0
				|| strcmp(&icon[len - 4], ".xpm") == 0)
			&& (buf = string_new_append(PREFIX, "/share/pixmaps/",
					icon, NULL)) != NULL)
	{
		image = gdk_pixbuf_new_from_file_at_size(buf,
				DESKTOPICON_ICON_SIZE, DESKTOPICON_ICON_SIZE,
				&error);
		string_delete(buf);
	}
	if(image == NULL)
		image = gtk_icon_theme_load_icon(desktop_get_theme(desktop),
				icon, DESKTOPICON_ICON_SIZE, 0, NULL);
	if(image == NULL)
		image = desktop_get_file(desktop);
	desktopicon = _desktopicon_new_do(desktop, image, name);
	config_delete(config); /* XXX also remove reference to the pixbuf */
	if(desktopicon == NULL)
	{
		free(exec);
		free(tryexec);
		return NULL;
	}
	desktopicon->exec = exec;
	desktopicon->tryexec = tryexec;
	desktopicon_set_confirm(desktopicon, FALSE);
	desktopicon_set_executable(desktopicon, TRUE);
	desktopicon_set_immutable(desktopicon, TRUE);
	return desktopicon;
}


/* desktopicon_new_category */
DesktopIcon * desktopicon_new_category(Desktop * desktop, char const * name,
		char const * icon)
{
	DesktopIcon * desktopicon;
	GdkPixbuf * image;

#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(%p, \"%s\", \"%s\")\n", __func__,
			(void *)desktop, name, icon);
#endif
	image = gtk_icon_theme_load_icon(desktop_get_theme(desktop), icon,
			DESKTOPICON_ICON_SIZE, 0, NULL);
	if((desktopicon = _desktopicon_new_do(desktop, image, name)) == NULL)
		return NULL;
	desktopicon_set_immutable(desktopicon, TRUE);
	return desktopicon;
}


/* desktopicon_delete */
void desktopicon_delete(DesktopIcon * desktopicon)
{
	free(desktopicon->tryexec);
	free(desktopicon->exec);
	free(desktopicon->name);
	free(desktopicon->path);
	gtk_widget_destroy(desktopicon->window);
	free(desktopicon);
}


/* accessors */
/* desktopicon_get_first */
gboolean desktopicon_get_first(DesktopIcon * desktopicon)
{
	return desktopicon->isfirst;
}


/* desktopicon_get_immutable */
gboolean desktopicon_get_immutable(DesktopIcon * desktopicon)
{
	return desktopicon->immutable;
}


/* desktopicon_get_isdir */
gboolean desktopicon_get_isdir(DesktopIcon * desktopicon)
{
	return desktopicon->isdir;
}


/* desktopicon_get_name */
char const * desktopicon_get_name(DesktopIcon * desktopicon)
{
	return desktopicon->name;
}


/* desktopicon_get_path */
char const * desktopicon_get_path(DesktopIcon * desktopicon)
{
	return desktopicon->path;
}


/* desktopicon_get_selected */
gboolean desktopicon_get_selected(DesktopIcon * desktopicon)
{
	return desktopicon->selected;
}


/* desktopicon_get_updated */
gboolean desktopicon_get_updated(DesktopIcon * desktopicon)
{
	return desktopicon->updated;
}


/* desktopicon_set_callback */
void desktopicon_set_callback(DesktopIcon * desktopicon,
		DesktopIconCallback callback, gpointer data)
{
	desktopicon->callback = callback;
	desktopicon->data = data;
}


/* desktopicon_set_confirm */
void desktopicon_set_confirm(DesktopIcon * desktopicon, gboolean confirm)
{
	desktopicon->confirm = confirm;
}


/* desktopicon_set_executable */
void desktopicon_set_executable(DesktopIcon * desktopicon, gboolean executable)
{
	desktopicon->isexec = executable;
}


/* desktopicon_set_first */
void desktopicon_set_first(DesktopIcon * desktopicon, gboolean first)
{
	desktopicon->isfirst = first;
}


/* desktopicon_set_icon */
void desktopicon_set_icon(DesktopIcon * desktopicon, GdkPixbuf * icon)
{
	_desktopicon_set_icon(desktopicon, icon);
	_desktopicon_update_transparency(desktopicon);
}


/* desktopicon_set_immutable */
void desktopicon_set_immutable(DesktopIcon * desktopicon, gboolean immutable)
{
	desktopicon->immutable = immutable;
}


/* desktopicon_set_name */
int desktopicon_set_name(DesktopIcon * desktopicon, char const * name)
{
	if(_desktopicon_set_name(desktopicon, name) != 0)
		return 1;
	_desktopicon_update_transparency(desktopicon);
	return 0;
}


/* desktopicon_set_selected */
void desktopicon_set_selected(DesktopIcon * desktopicon, gboolean selected)
{
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %p is %s\n", (void*)desktopicon,
			selected ? "selected" : "deselected");
#endif
	desktopicon->selected = selected;
	gtk_widget_set_state(desktopicon->event, selected
			? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
}


/* desktopicon_set_updated */
void desktopicon_set_updated(DesktopIcon * desktopicon, gboolean updated)
{
	desktopicon->updated = updated;
}


/* useful */
/* desktopicon_move */
void desktopicon_move(DesktopIcon * desktopicon, int x, int y)
{
	gtk_window_move(GTK_WINDOW(desktopicon->window), x, y);
}


/* desktopicon_show */
void desktopicon_show(DesktopIcon * desktopicon)
{
	gtk_widget_show_all(desktopicon->window);
}


/* private */
/* desktopicon_new_do */
static DesktopIcon * _desktopicon_new_do(Desktop * desktop, GdkPixbuf * image,
		char const * name)
{
	DesktopIcon * desktopicon;
	GtkWindow * window;
	GtkTargetEntry targets[] = { { "deforaos_browser_dnd", 0, 0 } };
	size_t targets_cnt = sizeof(targets) / sizeof(*targets);
	GtkWidget * vbox;
	GdkGeometry geometry;

	if((desktopicon = malloc(sizeof(*desktopicon))) == NULL)
		return NULL;
	memset(desktopicon, 0, sizeof(*desktopicon));
	desktopicon->desktop = desktop;
	desktopicon->confirm = TRUE;
	desktopicon->updated = TRUE;
	/* window */
	desktopicon->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	window = GTK_WINDOW(desktopicon->window);
	gtk_window_set_type_hint(window, GDK_WINDOW_TYPE_HINT_DOCK);
	gtk_window_set_resizable(window, FALSE);
	gtk_window_set_decorated(window, FALSE);
	gtk_window_set_keep_below(window, TRUE);
#if GTK_CHECK_VERSION(2, 6, 0)
	gtk_window_set_focus_on_map(window, FALSE);
#endif
	g_signal_connect(G_OBJECT(desktopicon->window), "delete-event",
			G_CALLBACK(_on_desktopicon_closex), NULL);
	/* event */
	desktopicon->event = gtk_event_box_new();
	gtk_drag_source_set(desktopicon->event, GDK_BUTTON1_MASK, targets,
			targets_cnt, GDK_ACTION_COPY | GDK_ACTION_MOVE);
	gtk_drag_dest_set(desktopicon->event, GTK_DEST_DEFAULT_ALL, targets,
			targets_cnt, GDK_ACTION_COPY | GDK_ACTION_MOVE);
	g_signal_connect(G_OBJECT(desktopicon->event), "button-press-event",
			G_CALLBACK(_on_icon_button_press), desktopicon);
	g_signal_connect(G_OBJECT(desktopicon->event), "key-press-event",
			G_CALLBACK(_on_icon_key_press), desktopicon);
	g_signal_connect(G_OBJECT(desktopicon->event), "drag-data-get",
			G_CALLBACK(_on_icon_drag_data_get), desktopicon);
	g_signal_connect(G_OBJECT(desktopicon->event), "drag-data-received",
			G_CALLBACK(_on_icon_drag_data_received), desktopicon);
	vbox = gtk_vbox_new(FALSE, 4);
	geometry.min_width = DESKTOPICON_MIN_WIDTH;
	geometry.min_height = DESKTOPICON_MIN_HEIGHT;
	geometry.max_width = DESKTOPICON_MAX_WIDTH;
	geometry.max_height = DESKTOPICON_MAX_HEIGHT;
	geometry.base_width = DESKTOPICON_MIN_WIDTH;
	geometry.base_height = DESKTOPICON_MIN_HEIGHT;
	gtk_window_set_geometry_hints(window, vbox, &geometry, /* XXX check */
			GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE
			| GDK_HINT_BASE_SIZE);
	/* image */
	desktopicon->image = gtk_image_new();
	gtk_widget_set_size_request(desktopicon->image, DESKTOPICON_MIN_WIDTH,
			DESKTOPICON_ICON_SIZE);
	gtk_box_pack_start(GTK_BOX(vbox), desktopicon->image, FALSE, TRUE, 4);
	/* label */
	desktopicon->label = gtk_label_new(NULL);
	gtk_label_set_justify(GTK_LABEL(desktopicon->label),
			GTK_JUSTIFY_CENTER);
#if GTK_CHECK_VERSION(2, 10, 0)
	gtk_label_set_line_wrap_mode(GTK_LABEL(desktopicon->label),
			PANGO_WRAP_WORD_CHAR);
#endif
	gtk_label_set_line_wrap(GTK_LABEL(desktopicon->label), TRUE);
	gtk_box_pack_start(GTK_BOX(vbox), desktopicon->label, TRUE, FALSE, 4);
	gtk_container_add(GTK_CONTAINER(desktopicon->event), vbox);
	gtk_container_add(GTK_CONTAINER(desktopicon->window),
			desktopicon->event);
	if(image == NULL)
		image = desktop_get_file(desktop);
	_desktopicon_set_icon(desktopicon, image);
	_desktopicon_set_name(desktopicon, name);
	_desktopicon_update_transparency(desktopicon);
	return desktopicon;
}


/* desktopicon_set_icon */
static void _desktopicon_set_icon(DesktopIcon * desktopicon, GdkPixbuf * icon)
{
	gtk_image_set_from_pixbuf(GTK_IMAGE(desktopicon->image), icon);
}


/* desktopicon_set_name */
static int _desktopicon_set_name(DesktopIcon * desktopicon, char const * name)
{
	char * p;

	if((p = strdup(name)) == NULL)
		return 1;
	free(desktopicon->name);
	desktopicon->name = p;
	gtk_label_set_text(GTK_LABEL(desktopicon->label), p);
	return 0;
}


/* desktopicon_update_transparency */
static void _desktopicon_update_transparency(DesktopIcon * desktopicon)
{
	GdkPixbuf * icon;
	int width;
	int height;
	int iwidth;
	int iheight;
	GdkBitmap * mask;
	GdkBitmap * iconmask;
	GdkGC * gc;
	GdkColor black = { 0, 0, 0, 0 };
	GdkColor white = { 0xffffffff, 0xffff, 0xffff, 0xffff };
	GtkRequisition req;
	int offset;

	if((icon = gtk_image_get_pixbuf(GTK_IMAGE(desktopicon->image))) == NULL)
		return; /* XXX report error */
	gtk_window_get_size(GTK_WINDOW(desktopicon->window), &width, &height);
	iwidth = gdk_pixbuf_get_width(icon);
	iheight = gdk_pixbuf_get_height(icon);
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(\"%s\") window is %dx%d\n", __func__,
			desktopicon->name, width, height);
#endif
	mask = gdk_pixmap_new(NULL, width, height, 1);
	gdk_pixbuf_render_pixmap_and_mask(icon, NULL, &iconmask, 255);
	gc = gdk_gc_new(mask);
	gdk_gc_set_foreground(gc, &black);
	gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, height);
	gdk_draw_drawable(mask, gc, iconmask, 0, 0,
			(width - iwidth) / 2,
			(DESKTOPICON_ICON_SIZE + 8 - iheight) / 2, -1, -1);
	gdk_gc_set_foreground(gc, &white);
	gtk_widget_size_request(desktopicon->label, &req);
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(\"%s\") label is %dx%d\n", __func__,
			desktopicon->name, req.width, req.height);
#endif
	offset = DESKTOPICON_ICON_SIZE + 8;
	gdk_draw_rectangle(mask, gc, TRUE, (width - req.width - 8) / 2,
			offset + ((height - offset - req.height - 8)
				/ 2), req.width + 8, req.height + 8);
	gtk_widget_shape_combine_mask(desktopicon->window, mask, 0, 0);
	g_object_unref(gc);
	g_object_unref(iconmask);
	g_object_unref(mask);
}


/* callbacks */
static gboolean _on_desktopicon_closex(void)<commit_msg>Updated the transparency calculation and window hints and attributes<commit_after>/* $Id$ */
/* Copyright (c) 2011 <NAME> <<EMAIL>> */
/* This file is part of DeforaOS Desktop Browser */
/* This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. */



#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libgen.h>
#include <errno.h>
#include <libintl.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <System.h>
#include "mime.h"
#include "desktopicon.h"
#include "../config.h"
#define _(string) gettext(string)

#define PROGNAME "desktop"
#define COMMON_DND
#define COMMON_EXEC
#include "common.c"


/* constants */
#ifndef PREFIX
# define PREFIX	"/usr/local"
#endif


/* DesktopIcon */
/* types */
struct _DesktopIcon
{
	Desktop * desktop;
	char * path;
	char * name;
	gboolean isfirst;
	gboolean isdir;
	gboolean isexec;
	char const * mimetype;
	/* applications */
	char * exec;
	char * tryexec;

	/* callback */
	DesktopIconCallback callback;
	gpointer data;

	gboolean confirm;
	gboolean immutable;		/* cannot be deleted */
	gboolean selected;
	gboolean updated;		/* XXX for desktop refresh */

	GtkWidget * window;
	GtkWidget * image;
	GtkWidget * event;
	GtkWidget * label;
};


/* prototypes */
static DesktopIcon * _desktopicon_new_do(Desktop * desktop, GdkPixbuf * image,
		char const * name);

static void _desktopicon_set_icon(DesktopIcon * desktopicon, GdkPixbuf * icon);
static int _desktopicon_set_name(DesktopIcon * desktopicon, char const * name);

static void _desktopicon_update_transparency(DesktopIcon * desktopicon);

/* callbacks */
static gboolean _on_desktopicon_closex(void);
static gboolean _on_icon_button_press(GtkWidget * widget,
		GdkEventButton * event, gpointer data);
static gboolean _on_icon_key_press(GtkWidget * widget, GdkEventKey * event,
		gpointer data);
static void _on_icon_drag_data_get(GtkWidget * widget, GdkDragContext * context,
		GtkSelectionData * seldata, guint info, guint time,
		gpointer data);
static void _on_icon_drag_data_received(GtkWidget * widget,
		GdkDragContext * context, gint x, gint y,
		GtkSelectionData * seldata, guint info, guint time,
		gpointer data);
static void _on_icon_open(gpointer data);
static void _on_icon_edit(gpointer data);
static void _on_icon_run(gpointer data);
static void _on_icon_open_with(gpointer data);
static void _on_icon_rename(gpointer data);
static void _on_icon_delete(gpointer data);
static void _on_icon_properties(gpointer data);


/* public */
/* functions */
/* desktopicon_new */
DesktopIcon * desktopicon_new(Desktop * desktop, char const * name,
		char const * path)
{
	DesktopIcon * desktopicon;
	struct stat st;
	Mime * mime;
	char const * mimetype = NULL;
	gboolean isdir = FALSE;
	gboolean isexec = FALSE;
	GdkPixbuf * image = NULL;
	GError * error = NULL;
	char * p;

#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(%p, \"%s\", \"%s\")\n", __func__,
			(void *)desktop, name, path);
#endif
	if(path != NULL && stat(path, &st) == 0)
	{
		mime = desktop_get_mime(desktop);
		if(S_ISDIR(st.st_mode))
		{
			isdir = TRUE;
			image = desktop_get_folder(desktop);
		}
		else if(st.st_mode & S_IXUSR)
		{
			/* FIXME use access() for this */
			isexec = TRUE;
			mime_icons(desktop_get_mime(desktop),
					desktop_get_theme(desktop),
					"application/x-executable",
					DESKTOPICON_ICON_SIZE, &image, -1);
		}
		else if((mimetype = mime_type(mime, path)) != NULL)
			mime_icons(mime, desktop_get_theme(desktop), mimetype,
					DESKTOPICON_ICON_SIZE, &image, -1);
	}
	if(name == NULL)
	{
		if((p = g_filename_to_utf8(path, -1, NULL, NULL, &error))
				== NULL)
		{
			fprintf(stderr, "%s%s\n", "desktop: ", error->message);
			name = path;
		}
		else
			name = p;
		if((name = strrchr(name, '/')) != NULL)
			name++;
	}
	if((desktopicon = _desktopicon_new_do(desktop, image, name)) == NULL)
		return NULL;
	desktopicon->isdir = isdir;
	desktopicon_set_executable(desktopicon, isexec);
	desktopicon->mimetype = mimetype;
	if(path != NULL && (desktopicon->path = strdup(path)) == NULL)
	{
		desktopicon_delete(desktopicon);
		return NULL;
	}
	return desktopicon;
}


/* desktopicon_new_application */
DesktopIcon * desktopicon_new_application(Desktop * desktop, char const * path)
{
	DesktopIcon * desktopicon;
	Config * config;
	const char section[] = "Desktop Entry";
	char const * name;
	char const * icon;
	char * exec = NULL;
	char * tryexec = NULL;
	size_t len;
	String * buf;
	GError * error = NULL;
	GdkPixbuf * image = NULL;

#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(%p, \"%s\")\n", __func__, (void *)desktop,
			path);
#endif
	if((config = config_new()) == NULL
			|| config_load(config, path) != 0)
	{
		if(config != NULL)
			config_delete(config);
		return NULL;
	}
	if((name = config_get(config, section, "Exec")) != NULL)
		exec = strdup(name);
	if((name = config_get(config, section, "TryExec")) != NULL)
		tryexec = strdup(name);
	if(exec == NULL || (name = config_get(config, section, "Name")) == NULL
			|| (icon = config_get(config, section, "Icon")) == NULL)
	{
		free(exec);
		free(tryexec);
		config_delete(config);
		return NULL;
	}
	/* image */
	if((len = strlen(icon)) > 4 && (strcmp(&icon[len - 4], ".png") == 0
				|| strcmp(&icon[len - 4], ".xpm") == 0)
			&& (buf = string_new_append(PREFIX, "/share/pixmaps/",
					icon, NULL)) != NULL)
	{
		image = gdk_pixbuf_new_from_file_at_size(buf,
				DESKTOPICON_ICON_SIZE, DESKTOPICON_ICON_SIZE,
				&error);
		string_delete(buf);
	}
	if(image == NULL)
		image = gtk_icon_theme_load_icon(desktop_get_theme(desktop),
				icon, DESKTOPICON_ICON_SIZE, 0, NULL);
	if(image == NULL)
		image = desktop_get_file(desktop);
	desktopicon = _desktopicon_new_do(desktop, image, name);
	config_delete(config); /* XXX also remove reference to the pixbuf */
	if(desktopicon == NULL)
	{
		free(exec);
		free(tryexec);
		return NULL;
	}
	desktopicon->exec = exec;
	desktopicon->tryexec = tryexec;
	desktopicon_set_confirm(desktopicon, FALSE);
	desktopicon_set_executable(desktopicon, TRUE);
	desktopicon_set_immutable(desktopicon, TRUE);
	return desktopicon;
}


/* desktopicon_new_category */
DesktopIcon * desktopicon_new_category(Desktop * desktop, char const * name,
		char const * icon)
{
	DesktopIcon * desktopicon;
	GdkPixbuf * image;

#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(%p, \"%s\", \"%s\")\n", __func__,
			(void *)desktop, name, icon);
#endif
	image = gtk_icon_theme_load_icon(desktop_get_theme(desktop), icon,
			DESKTOPICON_ICON_SIZE, 0, NULL);
	if((desktopicon = _desktopicon_new_do(desktop, image, name)) == NULL)
		return NULL;
	desktopicon_set_immutable(desktopicon, TRUE);
	return desktopicon;
}


/* desktopicon_delete */
void desktopicon_delete(DesktopIcon * desktopicon)
{
	free(desktopicon->tryexec);
	free(desktopicon->exec);
	free(desktopicon->name);
	free(desktopicon->path);
	gtk_widget_destroy(desktopicon->window);
	free(desktopicon);
}


/* accessors */
/* desktopicon_get_first */
gboolean desktopicon_get_first(DesktopIcon * desktopicon)
{
	return desktopicon->isfirst;
}


/* desktopicon_get_immutable */
gboolean desktopicon_get_immutable(DesktopIcon * desktopicon)
{
	return desktopicon->immutable;
}


/* desktopicon_get_isdir */
gboolean desktopicon_get_isdir(DesktopIcon * desktopicon)
{
	return desktopicon->isdir;
}


/* desktopicon_get_name */
char const * desktopicon_get_name(DesktopIcon * desktopicon)
{
	return desktopicon->name;
}


/* desktopicon_get_path */
char const * desktopicon_get_path(DesktopIcon * desktopicon)
{
	return desktopicon->path;
}


/* desktopicon_get_selected */
gboolean desktopicon_get_selected(DesktopIcon * desktopicon)
{
	return desktopicon->selected;
}


/* desktopicon_get_updated */
gboolean desktopicon_get_updated(DesktopIcon * desktopicon)
{
	return desktopicon->updated;
}


/* desktopicon_set_callback */
void desktopicon_set_callback(DesktopIcon * desktopicon,
		DesktopIconCallback callback, gpointer data)
{
	desktopicon->callback = callback;
	desktopicon->data = data;
}


/* desktopicon_set_confirm */
void desktopicon_set_confirm(DesktopIcon * desktopicon, gboolean confirm)
{
	desktopicon->confirm = confirm;
}


/* desktopicon_set_executable */
void desktopicon_set_executable(DesktopIcon * desktopicon, gboolean executable)
{
	desktopicon->isexec = executable;
}


/* desktopicon_set_first */
void desktopicon_set_first(DesktopIcon * desktopicon, gboolean first)
{
	desktopicon->isfirst = first;
}


/* desktopicon_set_icon */
void desktopicon_set_icon(DesktopIcon * desktopicon, GdkPixbuf * icon)
{
	_desktopicon_set_icon(desktopicon, icon);
	_desktopicon_update_transparency(desktopicon);
}


/* desktopicon_set_immutable */
void desktopicon_set_immutable(DesktopIcon * desktopicon, gboolean immutable)
{
	desktopicon->immutable = immutable;
}


/* desktopicon_set_name */
int desktopicon_set_name(DesktopIcon * desktopicon, char const * name)
{
	if(_desktopicon_set_name(desktopicon, name) != 0)
		return 1;
	_desktopicon_update_transparency(desktopicon);
	return 0;
}


/* desktopicon_set_selected */
void desktopicon_set_selected(DesktopIcon * desktopicon, gboolean selected)
{
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %p is %s\n", (void*)desktopicon,
			selected ? "selected" : "deselected");
#endif
	desktopicon->selected = selected;
	gtk_widget_set_state(desktopicon->event, selected
			? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
}


/* desktopicon_set_updated */
void desktopicon_set_updated(DesktopIcon * desktopicon, gboolean updated)
{
	desktopicon->updated = updated;
}


/* useful */
/* desktopicon_move */
void desktopicon_move(DesktopIcon * desktopicon, int x, int y)
{
	gtk_window_move(GTK_WINDOW(desktopicon->window), x, y);
}


/* desktopicon_show */
void desktopicon_show(DesktopIcon * desktopicon)
{
	gtk_widget_show_all(desktopicon->window);
}


/* private */
/* desktopicon_new_do */
static DesktopIcon * _desktopicon_new_do(Desktop * desktop, GdkPixbuf * image,
		char const * name)
{
	DesktopIcon * desktopicon;
	GtkWindow * window;
	GtkTargetEntry targets[] = { { "deforaos_browser_dnd", 0, 0 } };
	size_t targets_cnt = sizeof(targets) / sizeof(*targets);
	GtkWidget * vbox;
	GdkGeometry geometry;

	if((desktopicon = malloc(sizeof(*desktopicon))) == NULL)
		return NULL;
	memset(desktopicon, 0, sizeof(*desktopicon));
	desktopicon->desktop = desktop;
	desktopicon->confirm = TRUE;
	desktopicon->updated = TRUE;
	/* window */
	desktopicon->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	window = GTK_WINDOW(desktopicon->window);
	gtk_window_set_decorated(window, FALSE);
#if GTK_CHECK_VERSION(2, 6, 0)
	gtk_window_set_focus_on_map(window, FALSE);
#endif
	gtk_window_set_keep_below(window, TRUE);
	gtk_window_set_resizable(window, FALSE);
	gtk_window_set_skip_pager_hint(window, TRUE);
	gtk_window_set_type_hint(window, GDK_WINDOW_TYPE_HINT_DOCK);
	g_signal_connect(G_OBJECT(desktopicon->window), "delete-event",
			G_CALLBACK(_on_desktopicon_closex), NULL);
	/* event */
	desktopicon->event = gtk_event_box_new();
	gtk_drag_source_set(desktopicon->event, GDK_BUTTON1_MASK, targets,
			targets_cnt, GDK_ACTION_COPY | GDK_ACTION_MOVE);
	gtk_drag_dest_set(desktopicon->event, GTK_DEST_DEFAULT_ALL, targets,
			targets_cnt, GDK_ACTION_COPY | GDK_ACTION_MOVE);
	g_signal_connect(G_OBJECT(desktopicon->event), "button-press-event",
			G_CALLBACK(_on_icon_button_press), desktopicon);
	g_signal_connect(G_OBJECT(desktopicon->event), "key-press-event",
			G_CALLBACK(_on_icon_key_press), desktopicon);
	g_signal_connect(G_OBJECT(desktopicon->event), "drag-data-get",
			G_CALLBACK(_on_icon_drag_data_get), desktopicon);
	g_signal_connect(G_OBJECT(desktopicon->event), "drag-data-received",
			G_CALLBACK(_on_icon_drag_data_received), desktopicon);
	vbox = gtk_vbox_new(FALSE, 4);
	geometry.min_width = DESKTOPICON_MIN_WIDTH;
	geometry.min_height = DESKTOPICON_MIN_HEIGHT;
	geometry.max_width = DESKTOPICON_MAX_WIDTH;
	geometry.max_height = DESKTOPICON_MAX_HEIGHT;
	geometry.base_width = DESKTOPICON_MIN_WIDTH;
	geometry.base_height = DESKTOPICON_MIN_HEIGHT;
	gtk_window_set_geometry_hints(window, vbox, &geometry, /* XXX check */
			GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE
			| GDK_HINT_BASE_SIZE);
	/* image */
	desktopicon->image = gtk_image_new();
	gtk_widget_set_size_request(desktopicon->image, DESKTOPICON_ICON_SIZE,
			DESKTOPICON_ICON_SIZE);
	gtk_box_pack_start(GTK_BOX(vbox), desktopicon->image, FALSE, TRUE, 0);
	/* label */
	desktopicon->label = gtk_label_new(NULL);
	gtk_misc_set_alignment(GTK_MISC(desktopicon->label), 0.5, 0.0);
#if GTK_CHECK_VERSION(2, 10, 0)
	gtk_label_set_line_wrap_mode(GTK_LABEL(desktopicon->label),
			PANGO_WRAP_WORD_CHAR);
#endif
	gtk_label_set_line_wrap(GTK_LABEL(desktopicon->label), TRUE);
	gtk_box_pack_start(GTK_BOX(vbox), desktopicon->label, TRUE, TRUE, 4);
	gtk_container_add(GTK_CONTAINER(desktopicon->event), vbox);
	gtk_container_add(GTK_CONTAINER(desktopicon->window),
			desktopicon->event);
	if(image == NULL)
		image = desktop_get_file(desktop);
	_desktopicon_set_icon(desktopicon, image);
	_desktopicon_set_name(desktopicon, name);
	_desktopicon_update_transparency(desktopicon);
	return desktopicon;
}


/* desktopicon_set_icon */
static void _desktopicon_set_icon(DesktopIcon * desktopicon, GdkPixbuf * icon)
{
	gtk_image_set_from_pixbuf(GTK_IMAGE(desktopicon->image), icon);
}


/* desktopicon_set_name */
static int _desktopicon_set_name(DesktopIcon * desktopicon, char const * name)
{
	char * p;

	if((p = strdup(name)) == NULL)
		return 1;
	free(desktopicon->name);
	desktopicon->name = p;
	gtk_label_set_text(GTK_LABEL(desktopicon->label), p);
	return 0;
}


/* desktopicon_update_transparency */
static void _desktopicon_update_transparency(DesktopIcon * desktopicon)
{
	GdkPixbuf * icon;
	int width;
	int height;
	int iwidth;
	int iheight;
	GdkBitmap * mask;
	GdkBitmap * iconmask;
	GdkGC * gc;
	GdkColor black = { 0, 0, 0, 0 };
	GdkColor white = { 0xffffffff, 0xffff, 0xffff, 0xffff };
	GtkRequisition req;
	int offset;

	if((icon = gtk_image_get_pixbuf(GTK_IMAGE(desktopicon->image))) == NULL)
		return; /* XXX report error */
	gtk_window_get_size(GTK_WINDOW(desktopicon->window), &width, &height);
	iwidth = gdk_pixbuf_get_width(icon);
	iheight = gdk_pixbuf_get_height(icon);
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(\"%s\") window is %dx%d\n", __func__,
			desktopicon->name, width, height);
#endif
	mask = gdk_pixmap_new(NULL, width, height, 1);
	gdk_pixbuf_render_pixmap_and_mask(icon, NULL, &iconmask, 255);
	gc = gdk_gc_new(mask);
	gdk_gc_set_foreground(gc, &black);
	gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, height);
	gdk_draw_drawable(mask, gc, iconmask, 0, 0, (width - iwidth) / 2,
			(DESKTOPICON_ICON_SIZE - iheight) / 2, -1, -1);
	gdk_gc_set_foreground(gc, &white);
	gtk_widget_size_request(desktopicon->label, &req);
#ifdef DEBUG
	fprintf(stderr, "DEBUG: %s(\"%s\") label is %dx%d\n", __func__,
			desktopicon->name, req.width, req.height);
#endif
	offset = DESKTOPICON_ICON_SIZE + 4;
	gdk_draw_rectangle(mask, gc, TRUE, (width - req.width - 8) / 2,
			offset /* + ((height - offset - req.height - 8)
				/ 2) */, req.width + 8, req.height + 8);
	gtk_widget_shape_combine_mask(desktopicon->window, mask, 0, 0);
	g_object_unref(gc);
	g_object_unref(iconmask);
	g_object_unref(mask);
}


/* callbacks */
static gboolean _on_desktopicon_closex(void)
{'fraction_non_alphanumeric': 0.1260672231567588, 'fraction_numerical': 0.004348240073634431, 'mean_word_length': 5.4394032291028, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 161, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3663077', 'n_tokens_mistral': 12160, 'n_tokens_neox': 11191, 'n_words': 2333}