/*
 * panel-addto.c:
 *
 * Copyright (C) 2004 Vincent Untz
 *
 * 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; either version 2 of the
 * License, or (at your option) any later version.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 * Authors:
 *	Vincent Untz <vincent@vuntz.net>
 */

#include <config.h>
#include <string.h>

#include <glib/gi18n.h>
#include <gio/gio.h>

#include <matemenu-tree.h>

#include <libpanel-util/panel-glib.h>
#include <libpanel-util/panel-show.h>

#include "launcher.h"
#include "panel.h"
#include "drawer.h"
#include "mate-panel-applets-manager.h"
#include "mate-panel-applet-frame.h"
#include "panel-action-button.h"
#include "panel-menu-bar.h"
#include "panel-separator.h"
#include "panel-toplevel.h"
#include "panel-menu-button.h"
#include "panel-globals.h"
#include "panel-lockdown.h"
#include "panel-util.h"
#include "panel-profile.h"
#include "panel-addto.h"
#include "panel-icon-names.h"
#include "panel-schemas.h"

typedef struct {
	PanelWidget *panel_widget;

	GtkWidget    *addto_dialog;
	GtkWidget    *label;
	GtkWidget    *search_entry;
	GtkWidget    *back_button;
	GtkWidget    *add_button;
	GtkWidget    *tree_view;
	GtkTreeModel *applet_model;
	GtkTreeModel *filter_applet_model;
	GtkTreeModel *application_model;
	GtkTreeModel *filter_application_model;

	MateMenuTree    *menu_tree;

	GSList       *applet_list;
	GSList       *application_list;
	GSList       *settings_list;

	gchar        *search_text;
	gchar        *applet_search_text;

	int           insertion_position;
} PanelAddtoDialog;

static GQuark panel_addto_dialog_quark = 0;

typedef enum {
	PANEL_ADDTO_APPLET,
	PANEL_ADDTO_ACTION,
	PANEL_ADDTO_LAUNCHER_MENU,
	PANEL_ADDTO_LAUNCHER,
	PANEL_ADDTO_LAUNCHER_NEW,
	PANEL_ADDTO_MENU,
	PANEL_ADDTO_MENUBAR,
	PANEL_ADDTO_SEPARATOR,
	PANEL_ADDTO_DRAWER
} PanelAddtoItemType;

typedef struct {
	PanelAddtoItemType     type;
	char                  *name;
	char                  *description;
	char                  *icon;
	PanelActionButtonType  action_type;
	char                  *launcher_path;
	char                  *menu_filename;
	char                  *menu_path;
	char                  *iid;
	gboolean               static_data;
} PanelAddtoItemInfo;

typedef struct {
	GSList             *children;
	PanelAddtoItemInfo  item_info;
} PanelAddtoAppList;

static PanelAddtoItemInfo special_addto_items [] = {

	{ PANEL_ADDTO_LAUNCHER_NEW,
	  N_("Custom Application Launcher"),
	  N_("Create a new launcher"),
	  PANEL_ICON_LAUNCHER,
	  PANEL_ACTION_NONE,
	  NULL,
	  NULL,
	  NULL,
	  "LAUNCHER:ASK",
	  TRUE },

	{ PANEL_ADDTO_LAUNCHER_MENU,
	  N_("Application Launcher..."),
	  N_("Copy a launcher from the applications menu"),
	  PANEL_ICON_LAUNCHER,
	  PANEL_ACTION_NONE,
	  NULL,
	  NULL,
	  NULL,
	  "LAUNCHER:MENU",
	  TRUE }

};

static PanelAddtoItemInfo internal_addto_items [] = {

	{ PANEL_ADDTO_MENU,
	  N_("Main Menu"),
	  N_("The main MATE menu"),
	  PANEL_ICON_MAIN_MENU,
	  PANEL_ACTION_NONE,
	  NULL,
	  NULL,
	  NULL,
	  "MENU:MAIN",
	  TRUE },

	{ PANEL_ADDTO_MENUBAR,
	  N_("Menu Bar"),
	  N_("A custom menu bar"),
	  PANEL_ICON_MAIN_MENU,
	  PANEL_ACTION_NONE,
	  NULL,
	  NULL,
	  NULL,
	  "MENUBAR:NEW",
	  TRUE },

	{ PANEL_ADDTO_SEPARATOR,
	  N_("Separator"),
	  N_("A separator to organize the panel items"),
	  PANEL_ICON_SEPARATOR,
	  PANEL_ACTION_NONE,
	  NULL,
	  NULL,
	  NULL,
	  "SEPARATOR:NEW",
	  TRUE },

	{ PANEL_ADDTO_DRAWER,
	  N_("Drawer"),
	  N_("A pop out drawer to store other items in"),
	  PANEL_ICON_DRAWER,
	  PANEL_ACTION_NONE,
	  NULL,
	  NULL,
	  NULL,
	  "DRAWER:NEW",
	  TRUE }
};

enum {
	COLUMN_ICON,
	COLUMN_TEXT,
	COLUMN_DATA,
	COLUMN_SEARCH,
	NUMBER_COLUMNS
};

enum {
	PANEL_ADDTO_RESPONSE_BACK,
	PANEL_ADDTO_RESPONSE_ADD
};

static void panel_addto_present_applications (PanelAddtoDialog *dialog);
static void panel_addto_present_applets      (PanelAddtoDialog *dialog);
static gboolean panel_addto_filter_func (GtkTreeModel *model,
					 GtkTreeIter  *iter,
					 gpointer      data);

static int
panel_addto_applet_info_sort_func (PanelAddtoItemInfo *a,
				   PanelAddtoItemInfo *b)
{
	return g_utf8_collate (a->name, b->name);
}

static GSList *
panel_addto_prepend_internal_applets (GSList *list)
{
	static gboolean translated = FALSE;
	int             i;

	for (i = 0; i < G_N_ELEMENTS (internal_addto_items); i++) {
		if (!translated) {
			internal_addto_items [i].name        = _(internal_addto_items [i].name);
			internal_addto_items [i].description = _(internal_addto_items [i].description);
		}

                list = g_slist_prepend (list, &internal_addto_items [i]);
        }

	translated = TRUE;

	for (i = PANEL_ACTION_LOCK; i < PANEL_ACTION_LAST; i++) {
		PanelAddtoItemInfo *info;

		if (panel_action_get_is_disabled (i))
			continue;

		info              = g_new0 (PanelAddtoItemInfo, 1);
		info->type        = PANEL_ADDTO_ACTION;
		info->action_type = i;
		info->name        = g_strdup (panel_action_get_text (i));
		info->description = g_strdup (panel_action_get_tooltip (i));
		info->icon        = g_strdup (panel_action_get_icon_name (i));
		info->iid         = g_strdup (panel_action_get_drag_id (i));
		info->static_data = FALSE;

		list = g_slist_prepend (list, info);
	}

        return list;
}

static char *
panel_addto_make_text (const char *name,
		       const char *desc)
{
	const char *real_name;
	char       *result;

	real_name = name ? name : _("(empty)");

	if (!PANEL_GLIB_STR_EMPTY (desc)) {
		result = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>\n%s",
						  real_name, desc);
	} else {
		result = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>",
						  real_name);
	}

	return result;
}

#define ICON_SIZE 32

static GdkPixbuf *
panel_addto_make_pixbuf (const char *filename)
{
	//FIXME: size shouldn't be fixed but should depend on the font size
	return panel_load_icon (gtk_icon_theme_get_default (),
				filename,
				ICON_SIZE, ICON_SIZE, ICON_SIZE,
				NULL);
}

static void
panel_addto_drag_data_get_cb (GtkWidget        *widget,
			      GdkDragContext   *context,
			      GtkSelectionData *selection_data,
			      guint             info,
			      guint             time,
			      const char       *string)
{
	gtk_selection_data_set (selection_data,
				gtk_selection_data_get_target (selection_data), 8,
				(guchar *) string, strlen (string));
}

static void
panel_addto_drag_begin_cb (GtkWidget      *widget,
			   GdkDragContext *context,
			   gpointer        data)
{
	GtkTreeModel *filter_model;
	GtkTreeModel *child_model;
	GtkTreePath  *path;
	GtkTreeIter   iter;
	GtkTreeIter   filter_iter;
	GdkPixbuf    *pixbuf;

	filter_model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));

	gtk_tree_view_get_cursor (GTK_TREE_VIEW (widget), &path, NULL);
	gtk_tree_model_get_iter (filter_model, &filter_iter, path);
	gtk_tree_path_free (path);
	gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (filter_model),
							  &iter, &filter_iter);

	child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
	gtk_tree_model_get (child_model, &iter,
	                    COLUMN_ICON, &pixbuf,
	                    -1);

	gtk_drag_set_icon_pixbuf (context, pixbuf, 0, 0);
	g_object_unref (pixbuf);
}

static void
panel_addto_setup_drag (GtkTreeView          *tree_view,
			const GtkTargetEntry *target,
			const char           *text)
{
	if (!text || panel_lockdown_get_locked_down ())
		return;

	gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tree_view),
						GDK_BUTTON1_MASK|GDK_BUTTON2_MASK,
						target, 1, GDK_ACTION_COPY);

	g_signal_connect_data (G_OBJECT (tree_view), "drag_data_get",
			       G_CALLBACK (panel_addto_drag_data_get_cb),
			       g_strdup (text),
			       (GClosureNotify) g_free,
			       0 /* connect_flags */);
	g_signal_connect_after (G_OBJECT (tree_view), "drag-begin",
	                        G_CALLBACK (panel_addto_drag_begin_cb),
	                        NULL);
}

static void
panel_addto_setup_launcher_drag (GtkTreeView *tree_view,
				 const char  *path)
{
        static GtkTargetEntry target[] = {
		{ "text/uri-list", 0, 0 }
	};
	char *uri;
	char *uri_list;

	uri = g_filename_to_uri (path, NULL, NULL);
	uri_list = g_strconcat (uri, "\r\n", NULL);
	panel_addto_setup_drag (tree_view, target, uri_list);
	g_free (uri_list);
	g_free (uri);
}

static void
panel_addto_setup_applet_drag (GtkTreeView *tree_view,
			       const char  *iid)
{
	static GtkTargetEntry target[] = {
		{ "application/x-mate-panel-applet-iid", 0, 0 }
	};

	panel_addto_setup_drag (tree_view, target, iid);
}

static void
panel_addto_setup_internal_applet_drag (GtkTreeView *tree_view,
					const char  *applet_type)
{
	static GtkTargetEntry target[] = {
		{ "application/x-mate-panel-applet-internal", 0, 0 }
	};

	panel_addto_setup_drag (tree_view, target, applet_type);
}

static GSList *
panel_addto_query_applets (GSList *list)
{
	GList *applet_list, *l;

	applet_list = mate_panel_applets_manager_get_applets ();

	for (l = applet_list; l; l = g_list_next (l)) {
		MatePanelAppletInfo *info;
		const char *iid, *name, *description, *icon;
		PanelAddtoItemInfo *applet;

		info = (MatePanelAppletInfo *)l->data;

		iid = mate_panel_applet_info_get_iid (info);
		name = mate_panel_applet_info_get_name (info);
		description = mate_panel_applet_info_get_description (info);
		icon = mate_panel_applet_info_get_icon (info);

		if (!name || panel_lockdown_is_applet_disabled (iid)) {
			continue;
		}

		applet = g_new0 (PanelAddtoItemInfo, 1);
		applet->type = PANEL_ADDTO_APPLET;
		applet->name = g_strdup (name);
		applet->description = g_strdup (description);
		applet->icon = g_strdup (icon);
		applet->iid = g_strdup (iid);
		applet->static_data = FALSE;

		list = g_slist_prepend (list, applet);
	}

	g_list_free (applet_list);

	return list;
}

static void
panel_addto_append_item (PanelAddtoDialog *dialog,
			 GtkListStore *model,
			 PanelAddtoItemInfo *applet)
{
	char *text;
	GdkPixbuf *pixbuf;
	GtkTreeIter iter;

	if (applet == NULL) {
		gtk_list_store_append (model, &iter);
		gtk_list_store_set (model, &iter,
				    COLUMN_ICON, NULL,
				    COLUMN_TEXT, NULL,
				    COLUMN_DATA, NULL,
				    COLUMN_SEARCH, NULL,
				    -1);
	} else {
		pixbuf = NULL;

		if (applet->icon != NULL) {
			pixbuf = panel_addto_make_pixbuf (applet->icon);
		}

		gtk_list_store_append (model, &iter);

		text = panel_addto_make_text (applet->name,
					      applet->description);

		gtk_list_store_set (model, &iter,
				    COLUMN_ICON, pixbuf,
				    COLUMN_TEXT, text,
				    COLUMN_DATA, applet,
				    COLUMN_SEARCH, applet->name,
				    -1);

		if (pixbuf)
			g_object_unref (pixbuf);

		g_free (text);
	}
}

static void
panel_addto_append_special_applets (PanelAddtoDialog *dialog,
				    GtkListStore *model)
{
	static gboolean translated = FALSE;
	int i;

	for (i = 0; i < G_N_ELEMENTS (special_addto_items); i++) {
		if (!translated) {
			special_addto_items [i].name = _(special_addto_items [i].name);
			special_addto_items [i].description = _(special_addto_items [i].description);
		}

		if (special_addto_items [i].type == PANEL_ADDTO_LAUNCHER_NEW
		    && panel_lockdown_get_disable_command_line ())
			continue;

		panel_addto_append_item (dialog, model, &special_addto_items [i]);
	}

	translated = TRUE;
}

static void
panel_addto_make_applet_model (PanelAddtoDialog *dialog)
{
	GtkListStore *model;
	GSList       *l;

	if (dialog->filter_applet_model != NULL)
		return;

	if (panel_profile_id_lists_are_writable ()) {
		dialog->applet_list = panel_addto_query_applets (dialog->applet_list);
		dialog->applet_list = panel_addto_prepend_internal_applets (dialog->applet_list);
	}

	dialog->applet_list = g_slist_sort (dialog->applet_list,
					    (GCompareFunc) panel_addto_applet_info_sort_func);

	model = gtk_list_store_new (NUMBER_COLUMNS,
				    GDK_TYPE_PIXBUF,
				    G_TYPE_STRING,
				    G_TYPE_POINTER,
				    G_TYPE_STRING);

	if (panel_profile_id_lists_are_writable ()) {
		panel_addto_append_special_applets (dialog, model);
		if (dialog->applet_list)
			panel_addto_append_item (dialog, model, NULL);
	}

	for (l = dialog->applet_list; l; l = l->next)
		panel_addto_append_item (dialog, model, l->data);

	dialog->applet_model = GTK_TREE_MODEL (model);
	dialog->filter_applet_model = gtk_tree_model_filter_new (GTK_TREE_MODEL (dialog->applet_model),
								 NULL);
	gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (dialog->filter_applet_model),
						panel_addto_filter_func,
						dialog, NULL);
}

static void panel_addto_make_application_list (GSList             **parent_list,
					       MateMenuTreeDirectory  *directory,
					       const char          *filename);

static void
panel_addto_prepend_directory (GSList             **parent_list,
			       MateMenuTreeDirectory  *directory,
			       const char          *filename)
{
	PanelAddtoAppList *data;

	data = g_new0 (PanelAddtoAppList, 1);

	data->item_info.type          = PANEL_ADDTO_MENU;
	data->item_info.name          = g_strdup (matemenu_tree_directory_get_name (directory));
	data->item_info.description   = g_strdup (matemenu_tree_directory_get_comment (directory));
	data->item_info.icon          = g_strdup (matemenu_tree_directory_get_icon (directory));
	data->item_info.menu_filename = g_strdup (filename);
	data->item_info.menu_path     = matemenu_tree_directory_make_path (directory, NULL);
	data->item_info.static_data   = FALSE;

	/* We should set the iid here to something and do
	 * iid = g_strdup_printf ("MENU:%s", tfr->name)
	 * but this means we'd have to free the iid later
	 * and this would complexify too much the free
	 * function.
	 * So the iid is built when we select the row.
	 */

	*parent_list = g_slist_prepend (*parent_list, data);

	panel_addto_make_application_list (&data->children, directory, filename);
}

static void
panel_addto_prepend_entry (GSList         **parent_list,
			   MateMenuTreeEntry  *entry,
			   const char      *filename)
{
	PanelAddtoAppList *data;

	data = g_new0 (PanelAddtoAppList, 1);

	data->item_info.type          = PANEL_ADDTO_LAUNCHER;
	data->item_info.name          = g_strdup (matemenu_tree_entry_get_display_name (entry));
	data->item_info.description   = g_strdup (matemenu_tree_entry_get_comment (entry));
	data->item_info.icon          = g_strdup (matemenu_tree_entry_get_icon (entry));
	data->item_info.launcher_path = g_strdup (matemenu_tree_entry_get_desktop_file_path (entry));
	data->item_info.static_data   = FALSE;

	*parent_list = g_slist_prepend (*parent_list, data);
}

static void
panel_addto_prepend_alias (GSList         **parent_list,
			   MateMenuTreeAlias  *alias,
			   const char      *filename)
{
	MateMenuTreeItem *aliased_item;

	aliased_item = matemenu_tree_alias_get_item (alias);

	switch (matemenu_tree_item_get_type (aliased_item)) {
	case MATEMENU_TREE_ITEM_DIRECTORY:
		panel_addto_prepend_directory (parent_list,
					       MATEMENU_TREE_DIRECTORY (aliased_item),
					       filename);
		break;

	case MATEMENU_TREE_ITEM_ENTRY:
		panel_addto_prepend_entry (parent_list,
					   MATEMENU_TREE_ENTRY (aliased_item),
					   filename);
		break;

	default:
		break;
	}

	matemenu_tree_item_unref (aliased_item);
}

static void
panel_addto_make_application_list (GSList             **parent_list,
				   MateMenuTreeDirectory  *directory,
				   const char          *filename)
{
	GSList *items;
	GSList *l;

	items = matemenu_tree_directory_get_contents (directory);

	for (l = items; l; l = l->next) {
		switch (matemenu_tree_item_get_type (l->data)) {
		case MATEMENU_TREE_ITEM_DIRECTORY:
			panel_addto_prepend_directory (parent_list, l->data, filename);
			break;

		case MATEMENU_TREE_ITEM_ENTRY:
			panel_addto_prepend_entry (parent_list, l->data, filename);
			break;

		case MATEMENU_TREE_ITEM_ALIAS:
			panel_addto_prepend_alias (parent_list, l->data, filename);
			break;

		default:
			break;
		}

		matemenu_tree_item_unref (l->data);
	}

	g_slist_free (items);

	*parent_list = g_slist_reverse (*parent_list);
}

static void
panel_addto_populate_application_model (GtkTreeStore *store,
					GtkTreeIter  *parent,
					GSList       *app_list)
{
	PanelAddtoAppList *data;
	GtkTreeIter        iter;
	char              *text;
	GdkPixbuf         *pixbuf;
	GSList            *app;

	for (app = app_list; app != NULL; app = app->next) {
		data = app->data;
		gtk_tree_store_append (store, &iter, parent);

		text = panel_addto_make_text (data->item_info.name,
					      data->item_info.description);
		pixbuf = panel_addto_make_pixbuf (data->item_info.icon);
		gtk_tree_store_set (store, &iter,
				    COLUMN_ICON, pixbuf,
				    COLUMN_TEXT, text,
				    COLUMN_DATA, &(data->item_info),
				    COLUMN_SEARCH, data->item_info.name,
				    -1);

		if (pixbuf)
			g_object_unref (pixbuf);

		g_free (text);

		if (data->children != NULL)
			panel_addto_populate_application_model (store,
								&iter,
								data->children);
	}
}

static void panel_addto_make_application_model(PanelAddtoDialog* dialog)
{
	GtkTreeStore* store;
	MateMenuTree* tree;
	MateMenuTreeDirectory* root;

	if (dialog->filter_application_model != NULL)
		return;

	store = gtk_tree_store_new(NUMBER_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_STRING);

	tree = matemenu_tree_lookup("mate-applications.menu", MATEMENU_TREE_FLAGS_NONE);
	matemenu_tree_set_sort_key(tree, MATEMENU_TREE_SORT_DISPLAY_NAME);

	if ((root = matemenu_tree_get_root_directory (tree)))
	{
		panel_addto_make_application_list(&dialog->application_list, root, "mate-applications.menu");
		panel_addto_populate_application_model(store, NULL, dialog->application_list);

		matemenu_tree_item_unref(root);
	}

	matemenu_tree_unref(tree);

	tree = matemenu_tree_lookup("mate-settings.menu", MATEMENU_TREE_FLAGS_NONE);
	matemenu_tree_set_sort_key(tree, MATEMENU_TREE_SORT_DISPLAY_NAME);

	if ((root = matemenu_tree_get_root_directory(tree)))
	{
		GtkTreeIter iter;

		gtk_tree_store_append(store, &iter, NULL);
		gtk_tree_store_set(store, &iter, COLUMN_ICON, NULL, COLUMN_TEXT, NULL, COLUMN_DATA, NULL, COLUMN_SEARCH, NULL, -1);

		panel_addto_make_application_list(&dialog->settings_list, root, "mate-settings.menu");
		panel_addto_populate_application_model(store, NULL, dialog->settings_list);

		matemenu_tree_item_unref(root);
	}

	matemenu_tree_unref(tree);

	dialog->application_model = GTK_TREE_MODEL(store);
	dialog->filter_application_model = gtk_tree_model_filter_new(GTK_TREE_MODEL(dialog->application_model), NULL);
	gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(dialog->filter_application_model), panel_addto_filter_func, dialog, NULL);
}

static void
panel_addto_add_item (PanelAddtoDialog   *dialog,
	 	      PanelAddtoItemInfo *item_info)
{
	g_assert (item_info != NULL);

	switch (item_info->type) {
	case PANEL_ADDTO_APPLET:
		mate_panel_applet_frame_create (dialog->panel_widget->toplevel,
					   dialog->insertion_position,
					   item_info->iid);
		break;
	case PANEL_ADDTO_ACTION:
		panel_action_button_create (dialog->panel_widget->toplevel,
					    dialog->insertion_position,
					    item_info->action_type);
		break;
	case PANEL_ADDTO_LAUNCHER_MENU:
		panel_addto_present_applications (dialog);
		break;
	case PANEL_ADDTO_LAUNCHER:
		panel_launcher_create (dialog->panel_widget->toplevel,
				       dialog->insertion_position,
				       item_info->launcher_path);
		break;
	case PANEL_ADDTO_LAUNCHER_NEW:
		ask_about_launcher (NULL, dialog->panel_widget,
				    dialog->insertion_position, FALSE);
		break;
	case PANEL_ADDTO_MENU:
		panel_menu_button_create (dialog->panel_widget->toplevel,
					  dialog->insertion_position,
					  item_info->menu_filename,
					  item_info->menu_path,
					  item_info->menu_path != NULL,
					  item_info->name);
		break;
	case PANEL_ADDTO_MENUBAR:
		panel_menu_bar_create (dialog->panel_widget->toplevel,
				       dialog->insertion_position);
		break;
	case PANEL_ADDTO_SEPARATOR:
		panel_separator_create (dialog->panel_widget->toplevel,
					dialog->insertion_position);
		break;
	case PANEL_ADDTO_DRAWER:
		panel_drawer_create (dialog->panel_widget->toplevel,
				     dialog->insertion_position,
				     NULL, FALSE, NULL);
		break;
	}
}

static void
panel_addto_dialog_response (GtkWidget *widget_dialog,
			     guint response_id,
			     PanelAddtoDialog *dialog)
{
	GtkTreeSelection *selection;
	GtkTreeModel     *filter_model;
	GtkTreeModel     *child_model;
	GtkTreeIter       iter;
	GtkTreeIter       filter_iter;

	switch (response_id) {
	case GTK_RESPONSE_HELP:
		panel_show_help (gtk_window_get_screen (GTK_WINDOW (dialog->addto_dialog)),
				 "mate-user-guide", "gospanel-15", NULL);
		break;

	case PANEL_ADDTO_RESPONSE_ADD:
		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->tree_view));
		if (gtk_tree_selection_get_selected (selection, &filter_model,
						     &filter_iter)) {
			PanelAddtoItemInfo *data;

			gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (filter_model),
									  &iter,
									  &filter_iter);
			child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
			gtk_tree_model_get (child_model, &iter,
					    COLUMN_DATA, &data, -1);

			if (data != NULL)
				panel_addto_add_item (dialog, data);
		}
		break;

	case PANEL_ADDTO_RESPONSE_BACK:
		/* This response only happens when we're showing the
		 * application list and the user wants to go back to the
		 * applet list. */
		panel_addto_present_applets (dialog);
		break;

	case GTK_RESPONSE_CLOSE:
		gtk_widget_destroy (widget_dialog);
		break;

	default:
		break;
	}
}

static void
panel_addto_dialog_destroy (GtkWidget *widget_dialog,
			    PanelAddtoDialog *dialog)
{
	panel_toplevel_pop_autohide_disabler (PANEL_TOPLEVEL (dialog->panel_widget->toplevel));
	g_object_set_qdata (G_OBJECT (dialog->panel_widget->toplevel),
			    panel_addto_dialog_quark,
			    NULL);
}

static void
panel_addto_present_applications (PanelAddtoDialog *dialog)
{
	if (dialog->filter_application_model == NULL)
		panel_addto_make_application_model (dialog);
	gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->tree_view),
				 dialog->filter_application_model);
	gtk_window_set_focus (GTK_WINDOW (dialog->addto_dialog),
			      dialog->search_entry);
	gtk_widget_set_sensitive (dialog->back_button, TRUE);

	if (dialog->applet_search_text)
		g_free (dialog->applet_search_text);

	dialog->applet_search_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->search_entry)));
	/* show everything */
	gtk_entry_set_text (GTK_ENTRY (dialog->search_entry), "");
}

static void
panel_addto_present_applets (PanelAddtoDialog *dialog)
{
	if (dialog->filter_applet_model == NULL)
		panel_addto_make_applet_model (dialog);
	gtk_tree_view_set_model (GTK_TREE_VIEW (dialog->tree_view),
				 dialog->filter_applet_model);
	gtk_window_set_focus (GTK_WINDOW (dialog->addto_dialog),
			      dialog->search_entry);
	gtk_widget_set_sensitive (dialog->back_button, FALSE);

	if (dialog->applet_search_text) {
		gtk_entry_set_text (GTK_ENTRY (dialog->search_entry),
				    dialog->applet_search_text);
		gtk_editable_set_position (GTK_EDITABLE (dialog->search_entry),
					   -1);

		g_free (dialog->applet_search_text);
		dialog->applet_search_text = NULL;
	}
}

static void
panel_addto_dialog_free_item_info (PanelAddtoItemInfo *item_info)
{
	if (item_info == NULL || item_info->static_data)
		return;

	if (item_info->name != NULL)
		g_free (item_info->name);
	item_info->name = NULL;

	if (item_info->description != NULL)
		g_free (item_info->description);
	item_info->description = NULL;

	if (item_info->icon != NULL)
		g_free (item_info->icon);
	item_info->icon = NULL;

	if (item_info->iid != NULL)
		g_free (item_info->iid);
	item_info->iid = NULL;

	if (item_info->launcher_path != NULL)
		g_free (item_info->launcher_path);
	item_info->launcher_path = NULL;

	if (item_info->menu_filename != NULL)
		g_free (item_info->menu_filename);
	item_info->menu_filename = NULL;

	if (item_info->menu_path != NULL)
		g_free (item_info->menu_path);
	item_info->menu_path = NULL;
}

static void
panel_addto_dialog_free_application_list (GSList *application_list)
{
	PanelAddtoAppList *data;
	GSList            *app;

	if (application_list == NULL)
		return;

	for (app = application_list; app != NULL; app = app->next) {
		data = app->data;
		if (data->children) {
			panel_addto_dialog_free_application_list (data->children);
		}
		panel_addto_dialog_free_item_info (&data->item_info);
		g_free (data);
	}
	g_slist_free (application_list);
}

static void
panel_addto_name_notify (GSettings        *settings,
			 gchar            *key,
			 PanelAddtoDialog *dialog);

static void
panel_addto_dialog_free (PanelAddtoDialog *dialog)
{
	GSList      *item;

	g_signal_handlers_disconnect_by_func(dialog->panel_widget->toplevel->settings,
					     G_CALLBACK (panel_addto_name_notify),
					     dialog);

	if (dialog->search_text)
		g_free (dialog->search_text);
	dialog->search_text = NULL;

	if (dialog->applet_search_text)
		g_free (dialog->applet_search_text);
	dialog->applet_search_text = NULL;

	if (dialog->addto_dialog)
		gtk_widget_destroy (dialog->addto_dialog);
	dialog->addto_dialog = NULL;

	for (item = dialog->applet_list; item != NULL; item = item->next) {
		PanelAddtoItemInfo *applet;

		applet = (PanelAddtoItemInfo *) item->data;
		if (!applet->static_data) {
			panel_addto_dialog_free_item_info (applet);
			g_free (applet);
		}
	}
	g_slist_free (dialog->applet_list);

	panel_addto_dialog_free_application_list (dialog->application_list);
	panel_addto_dialog_free_application_list (dialog->settings_list);

	if (dialog->filter_applet_model)
		g_object_unref (dialog->filter_applet_model);
	dialog->filter_applet_model = NULL;

	if (dialog->applet_model)
		g_object_unref (dialog->applet_model);
	dialog->applet_model = NULL;

	if (dialog->filter_application_model)
		g_object_unref (dialog->filter_application_model);
	dialog->filter_application_model = NULL;

	if (dialog->application_model)
		g_object_unref (dialog->application_model);
	dialog->application_model = NULL;

	if (dialog->menu_tree)
		matemenu_tree_unref (dialog->menu_tree);
	dialog->menu_tree = NULL;

	g_free (dialog);
}

static void
panel_addto_name_change (PanelAddtoDialog *dialog,
			 const char       *name)
{
	char *title;
	char *label;

	label = NULL;

	if (!PANEL_GLIB_STR_EMPTY (name))
		label = g_strdup_printf (_("Find an _item to add to \"%s\":"),
					 name);

	if (panel_toplevel_get_is_attached (dialog->panel_widget->toplevel)) {
		title = g_strdup_printf (_("Add to Drawer"));
		if (label == NULL)
			label = g_strdup (_("Find an _item to add to the drawer:"));
	} else {
		title = g_strdup_printf (_("Add to Panel"));
		if (label == NULL)
			label = g_strdup (_("Find an _item to add to the panel:"));
	}

	gtk_window_set_title (GTK_WINDOW (dialog->addto_dialog), title);
	g_free (title);

	gtk_label_set_text_with_mnemonic (GTK_LABEL (dialog->label), label);
	g_free (label);
}

static void
panel_addto_name_notify (GSettings        *settings,
			 gchar            *key,
			 PanelAddtoDialog *dialog)
{
	gchar *name = g_settings_get_string (settings, key);
	panel_addto_name_change (dialog, name);
	g_free (name);
}

static gboolean
panel_addto_filter_func (GtkTreeModel *model,
			 GtkTreeIter  *iter,
			 gpointer      userdata)
{
	PanelAddtoDialog   *dialog;
	PanelAddtoItemInfo *data;

	dialog = (PanelAddtoDialog *) userdata;

	if (!dialog->search_text || !dialog->search_text[0])
		return TRUE;

	gtk_tree_model_get (model, iter, COLUMN_DATA, &data, -1);

	if (data == NULL)
		return FALSE;

	/* This is more a workaround than anything else: show all the root
	 * items in a tree store */
	if (GTK_IS_TREE_STORE (model) &&
	    gtk_tree_store_iter_depth (GTK_TREE_STORE (model), iter) == 0)
		return TRUE;

	return (panel_g_utf8_strstrcase (data->name,
					 dialog->search_text) != NULL ||
	        panel_g_utf8_strstrcase (data->description,
					 dialog->search_text) != NULL);
}

static void
panel_addto_search_entry_changed (GtkWidget        *entry,
				  PanelAddtoDialog *dialog)
{
	GtkTreeModel *model;
	char         *new_text;
	GtkTreeIter   iter;
	GtkTreePath  *path;

	new_text = g_strdup (gtk_entry_get_text (GTK_ENTRY (dialog->search_entry)));
	g_strchomp (new_text);

	if (dialog->search_text &&
	    g_utf8_collate (new_text, dialog->search_text) == 0) {
		g_free (new_text);
		return;
	}

	if (dialog->search_text)
		g_free (dialog->search_text);
	dialog->search_text = new_text;

	model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->tree_view));
	gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));

	path = gtk_tree_path_new_first ();
	if (gtk_tree_model_get_iter (model, &iter, path)) {
		GtkTreeSelection *selection;

		gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (dialog->tree_view),
					      path, NULL, FALSE, 0, 0);
		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->tree_view));
		gtk_tree_selection_select_path (selection, path);
	}
	gtk_tree_path_free (path);
}

static void
panel_addto_search_entry_activated (GtkWidget        *entry,
				    PanelAddtoDialog *dialog)
{
	gtk_dialog_response (GTK_DIALOG (dialog->addto_dialog),
			     PANEL_ADDTO_RESPONSE_ADD);
}

static void
panel_addto_selection_changed (GtkTreeSelection *selection,
			       PanelAddtoDialog *dialog)
{
	GtkTreeModel       *filter_model;
	GtkTreeModel       *child_model;
	GtkTreeIter         iter;
	GtkTreeIter         filter_iter;
	PanelAddtoItemInfo *data;
	char               *iid;

	if (!gtk_tree_selection_get_selected (selection,
					      &filter_model,
					      &filter_iter)) {
		gtk_widget_set_sensitive (GTK_WIDGET (dialog->add_button),
					  FALSE);
		return;
	}

	gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (filter_model),
							  &iter, &filter_iter);
	child_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (filter_model));
	gtk_tree_model_get (child_model, &iter, COLUMN_DATA, &data, -1);

	if (!data) {
		gtk_widget_set_sensitive (GTK_WIDGET (dialog->add_button),
					  FALSE);
		return;
	}

	gtk_widget_set_sensitive (GTK_WIDGET (dialog->add_button), TRUE);

	if (data->type == PANEL_ADDTO_LAUNCHER_MENU) {
		gtk_button_set_label (GTK_BUTTON (dialog->add_button),
				      GTK_STOCK_GO_FORWARD);
	} else {
		gtk_button_set_label (GTK_BUTTON (dialog->add_button),
				      GTK_STOCK_ADD);
	}
	gtk_button_set_use_stock (GTK_BUTTON (dialog->add_button),
				  TRUE);

	/* only allow dragging applets if we can add applets */
	if (panel_profile_id_lists_are_writable ()) {
		switch (data->type) {
		case PANEL_ADDTO_LAUNCHER:
			panel_addto_setup_launcher_drag (GTK_TREE_VIEW (dialog->tree_view),
							 data->launcher_path);
			break;
		case PANEL_ADDTO_APPLET:
			panel_addto_setup_applet_drag (GTK_TREE_VIEW (dialog->tree_view),
						       data->iid);
			break;
		case PANEL_ADDTO_LAUNCHER_MENU:
			gtk_tree_view_unset_rows_drag_source (GTK_TREE_VIEW (dialog->tree_view));
			break;
		case PANEL_ADDTO_MENU:
			/* build the iid for menus other than the main menu */
			if (data->iid == NULL) {
				iid = g_strdup_printf ("MENU:%s/%s",
						       data->menu_filename,
						       data->menu_path);
			} else {
				iid = g_strdup (data->iid);
			}
			panel_addto_setup_internal_applet_drag (GTK_TREE_VIEW (dialog->tree_view),
							        iid);
			g_free (iid);
			break;
		default:
			panel_addto_setup_internal_applet_drag (GTK_TREE_VIEW (dialog->tree_view),
							        data->iid);
			break;
		}
	}
}

static void
panel_addto_selection_activated (GtkTreeView       *view,
				 GtkTreePath       *path,
				 GtkTreeViewColumn *column,
				 PanelAddtoDialog  *dialog)
{
	gtk_dialog_response (GTK_DIALOG (dialog->addto_dialog),
			     PANEL_ADDTO_RESPONSE_ADD);
}

static gboolean
panel_addto_separator_func (GtkTreeModel *model,
			    GtkTreeIter *iter,
			    gpointer data)
{
	int column = GPOINTER_TO_INT (data);
	char *text;

	gtk_tree_model_get (model, iter, column, &text, -1);

	if (!text)
		return TRUE;

	g_free(text);
	return FALSE;
}

static PanelAddtoDialog *
panel_addto_dialog_new (PanelWidget *panel_widget)
{
	PanelAddtoDialog *dialog;
	GtkWidget *dialog_vbox;
#if !GTK_CHECK_VERSION (3, 0, 0)
	GtkWidget *vbox;
#endif
	GtkWidget *inner_vbox;
	GtkWidget *find_hbox;
	GtkWidget *sw;
	GtkCellRenderer *renderer;
	GtkTreeSelection *selection;
	GtkTreeViewColumn *column;

	dialog = g_new0 (PanelAddtoDialog, 1);

	g_object_set_qdata_full (G_OBJECT (panel_widget->toplevel),
				 panel_addto_dialog_quark,
				 dialog,
				 (GDestroyNotify) panel_addto_dialog_free);

	dialog->panel_widget = panel_widget;

	g_signal_connect (dialog->panel_widget->toplevel->settings,
			  "changed::" PANEL_TOPLEVEL_NAME_KEY,
			  G_CALLBACK (panel_addto_name_notify),
			  dialog);

	dialog->addto_dialog = gtk_dialog_new ();
	gtk_dialog_add_button (GTK_DIALOG (dialog->addto_dialog),
			       GTK_STOCK_HELP, GTK_RESPONSE_HELP);
	dialog->back_button = gtk_dialog_add_button (GTK_DIALOG (dialog->addto_dialog),
						     GTK_STOCK_GO_BACK,
						     PANEL_ADDTO_RESPONSE_BACK);
	dialog->add_button = gtk_dialog_add_button (GTK_DIALOG (dialog->addto_dialog),
						     GTK_STOCK_ADD,
						     PANEL_ADDTO_RESPONSE_ADD);
	gtk_dialog_add_button (GTK_DIALOG (dialog->addto_dialog),
			       GTK_STOCK_CLOSE,
			       GTK_RESPONSE_CLOSE);
	gtk_widget_set_sensitive (GTK_WIDGET (dialog->add_button), FALSE);
#if !GTK_CHECK_VERSION (3, 0, 0)
	gtk_dialog_set_has_separator (GTK_DIALOG (dialog->addto_dialog),
				      FALSE);
#endif
	gtk_dialog_set_default_response (GTK_DIALOG (dialog->addto_dialog),
					 PANEL_ADDTO_RESPONSE_ADD);

	gtk_container_set_border_width (GTK_CONTAINER (dialog->addto_dialog), 5);

	dialog_vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog->addto_dialog));
#if GTK_CHECK_VERSION (3, 0, 0)
	gtk_box_set_spacing (GTK_BOX (dialog_vbox), 12);
	gtk_container_set_border_width (GTK_CONTAINER (dialog_vbox), 5);
#else
	gtk_box_set_spacing (GTK_BOX (dialog_vbox), 2);
#endif

	g_signal_connect (G_OBJECT (dialog->addto_dialog), "response",
			  G_CALLBACK (panel_addto_dialog_response), dialog);
	g_signal_connect (dialog->addto_dialog, "destroy",
			  G_CALLBACK (panel_addto_dialog_destroy), dialog);

#if !GTK_CHECK_VERSION (3, 0, 0)
	vbox = gtk_vbox_new (FALSE, 12);
	gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
	gtk_container_add (GTK_CONTAINER (dialog_vbox), vbox);
#endif

	inner_vbox = gtk_vbox_new (FALSE, 6);
#if GTK_CHECK_VERSION (3, 0, 0)
	gtk_box_pack_start (GTK_BOX (dialog_vbox), inner_vbox, TRUE, TRUE, 0);
#else
	gtk_box_pack_start (GTK_BOX (vbox), inner_vbox, TRUE, TRUE, 0);
#endif

	find_hbox = gtk_hbox_new (FALSE, 6);
	gtk_box_pack_start (GTK_BOX (inner_vbox), find_hbox, FALSE, FALSE, 0);

	dialog->label = gtk_label_new_with_mnemonic ("");
	gtk_misc_set_alignment (GTK_MISC (dialog->label), 0.0, 0.5);
	gtk_label_set_use_markup (GTK_LABEL (dialog->label), TRUE);

	gtk_box_pack_start (GTK_BOX (find_hbox), dialog->label,
			    FALSE, FALSE, 0);

	dialog->search_entry = gtk_entry_new ();
	g_signal_connect (G_OBJECT (dialog->search_entry), "changed",
			  G_CALLBACK (panel_addto_search_entry_changed), dialog);
	g_signal_connect (G_OBJECT (dialog->search_entry), "activate",
			  G_CALLBACK (panel_addto_search_entry_activated), dialog);

	gtk_box_pack_end (GTK_BOX (find_hbox), dialog->search_entry,
			  TRUE, TRUE, 0);

	gtk_label_set_mnemonic_widget (GTK_LABEL (dialog->label),
				       dialog->search_entry);

	sw = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
					GTK_POLICY_AUTOMATIC,
					GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
					     GTK_SHADOW_IN);
	gtk_box_pack_start (GTK_BOX (inner_vbox), sw, TRUE, TRUE, 0);

	dialog->tree_view = gtk_tree_view_new ();
	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (dialog->tree_view),
					   FALSE);
	gtk_tree_view_expand_all (GTK_TREE_VIEW (dialog->tree_view));

	renderer = g_object_new (GTK_TYPE_CELL_RENDERER_PIXBUF,
				 "xpad", 4,
				 "ypad", 4,
				 NULL);

	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (dialog->tree_view),
						     -1, NULL,
						     renderer,
						     "pixbuf", COLUMN_ICON,
						     NULL);
	renderer = gtk_cell_renderer_text_new ();
	g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
	gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (dialog->tree_view),
						     -1, NULL,
						     renderer,
						     "markup", COLUMN_TEXT,
						     NULL);

	//FIXME use the same search than the one for the search entry?
	gtk_tree_view_set_search_column (GTK_TREE_VIEW (dialog->tree_view),
					 COLUMN_SEARCH);

	gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (dialog->tree_view),
					      panel_addto_separator_func,
					      GINT_TO_POINTER (COLUMN_TEXT),
					      NULL);


	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->tree_view));
	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);

	column = gtk_tree_view_get_column (GTK_TREE_VIEW (dialog->tree_view),
					   COLUMN_TEXT);
	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);

	g_signal_connect (selection, "changed",
			  G_CALLBACK (panel_addto_selection_changed),
			  dialog);

	g_signal_connect (dialog->tree_view, "row-activated",
			  G_CALLBACK (panel_addto_selection_activated),
			  dialog);

	gtk_container_add (GTK_CONTAINER (sw), dialog->tree_view);

#if GTK_CHECK_VERSION (3, 0, 0)
	gtk_widget_show_all (dialog_vbox);
#else
	gtk_widget_show_all (vbox);
#endif

	panel_toplevel_push_autohide_disabler (dialog->panel_widget->toplevel);
	panel_widget_register_open_dialog (panel_widget,
					   dialog->addto_dialog);

	panel_addto_name_change (dialog,
				 panel_toplevel_get_name (dialog->panel_widget->toplevel));

	return dialog;
}

#define MAX_ADDTOPANEL_HEIGHT 490

void
panel_addto_present (GtkMenuItem *item,
		     PanelWidget *panel_widget)
{
	PanelAddtoDialog *dialog;
	PanelToplevel *toplevel;
	PanelData     *pd;
	GdkScreen *screen;
	gint screen_height;
	gint height;

	toplevel = panel_widget->toplevel;
	pd = g_object_get_data (G_OBJECT (toplevel), "PanelData");

	if (!panel_addto_dialog_quark)
		panel_addto_dialog_quark =
			g_quark_from_static_string ("panel-addto-dialog");

	dialog = g_object_get_qdata (G_OBJECT (toplevel),
				     panel_addto_dialog_quark);

	screen = gtk_window_get_screen (GTK_WINDOW (toplevel));
	screen_height = gdk_screen_get_height (screen);
	height = MIN (MAX_ADDTOPANEL_HEIGHT, 3 * (screen_height / 4));

	if (!dialog) {
		dialog = panel_addto_dialog_new (panel_widget);
		panel_addto_present_applets (dialog);
	}

	dialog->insertion_position = pd ? pd->insertion_pos : -1;
	gtk_window_set_screen (GTK_WINDOW (dialog->addto_dialog), screen);
	gtk_window_set_default_size (GTK_WINDOW (dialog->addto_dialog),
				     height * 8 / 7, height);
	gtk_window_present (GTK_WINDOW (dialog->addto_dialog));
}
