/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
/*
 *   grun: Popup a command dialog. Original version by Elliot Lee, 
 *    bloatware edition by Havoc Pennington. Both versions written in 10
 *    minutes or less. :-)
 *   Copyright (C) 1998 Havoc Pennington <hp@pobox.com>
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 */

#include <config.h>
#include <errno.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>

#include <libgnome/libgnome.h>
#include <libgnomeui/libgnomeui.h>

#include "gnome-run.h"

#include "foobar-widget.h"
#include "menu-fentry.h"
#include "menu.h"
#include "nothing.h"
#include "multiscreen-stuff.h"
#include "quick-desktop-reader.h"

#include "applet.h"
#include "button-widget.h"
#include "foobar-widget.h"
#include "menu-fentry.h"
#include "menu.h"
#include "multiscreen-stuff.h"
#include "panel-util.h"

#define ADVANCED_DIALOG_KEY "advanced_run_dialog"

#define ICON_SIZE 20

enum {
	COLUMN_ICON,
	COLUMN_ICON_FILE,
	COLUMN_FULLNAME,
	COLUMN_COMMENT,
	COLUMN_NAME,
	NUM_COLUMNS
};

typedef enum {
	PANEL_RESPONSE_RUN,
} PanelResponseType;

#define PANEL_STOCK_RUN "panel-run"

extern GtkTooltips *panel_tooltips;
extern gboolean no_run_box;

static GtkWidget *run_dialog = NULL;
static GSList *add_icon_iters = NULL;
static guint add_icon_idle_id = 0;
static guint add_items_idle_id = 0;

static GList *executables = NULL;
static GCompletion *exe_completion = NULL;

static GtkWidget* create_advanced_contents (void);
static void       update_contents          (GtkWidget *dialog);
static void       unset_selected           (GtkWidget *dialog);

static void
fill_executables_from (const char *dirname)
{
	struct dirent *dent;
	DIR *dir;

	dir = opendir (dirname);

	if (dir == NULL)
		return;

	while ( (dent = readdir (dir)) != NULL) {
		char *file = g_strconcat (dirname, "/", dent->d_name, NULL);

		if (access (file, X_OK) == 0)
			executables = g_list_prepend (executables,
						      g_strdup (dent->d_name));
	}

	closedir (dir);
}

static void
fill_executables (void)
{
	int i;
	const char *path;
	char **pathv;

	panel_g_list_deep_free (executables);
	executables = NULL;

	path = g_getenv ("PATH");

	if (path == NULL ||
	    path[0] == '\0')
		return;

	pathv = g_strsplit (path, ":", 0);

	for (i = 0; pathv[i] != NULL; i++)
		fill_executables_from (pathv[i]);

	g_strfreev (pathv);
}

static void
ensure_completion (void)
{
	if (exe_completion == NULL) {
		exe_completion = g_completion_new (NULL);
		fill_executables ();

		g_completion_add_items (exe_completion, executables);
	}
}

static void
kill_completion (void)
{
	if (executables != NULL) {
		panel_g_list_deep_free (executables);
		executables = NULL;
	}

	if (exe_completion != NULL) {
		g_completion_free (exe_completion);
		exe_completion = NULL;
	}
}

static void
get_environment (int *argc, char ***argv, int *envc, char ***envv)
{
	GList *envar = NULL, *li;
	int i, moveby;

	*envv = NULL;
	*envc = 0;

	moveby = 0;
	for (i = 0; i < *argc; i++) {
		if (strchr ((*argv)[i], '=') == NULL) {
			break;
		}
		envar = g_list_append (envar, g_strdup ((*argv)[i]));
		moveby ++;
	}

	if (moveby == *argc) {
		panel_g_list_deep_free (envar);
		return;
	}

	if (envar == NULL)
		return;

	for (i = 0; i < *argc; i++) {
		g_free ((*argv)[i]);
		if (i + moveby < *argc) {
			(*argv)[i] = (*argv)[i+moveby];
			(*argv)[i+moveby] = NULL;
		} else {
			(*argv)[i] = NULL;
		}
	}
	*argc -= moveby;

	*envc = g_list_length (envar);
	*envv = g_new0 (char *, *envc + 1);
	for (i = 0, li = envar; li != NULL; li = li->next, i++) {
		(*envv)[i] = li->data;
		li->data = NULL;
	}	
	(*envv)[i] = NULL;
	g_list_free (envar);
}

static void 
run_dialog_response (GtkWidget *w, int response, gpointer data)
{
	GtkEntry *entry;
        GtkWidget *list;
	GtkToggleButton *terminal;
	char **argv = NULL;
	char **temp_argv = NULL;
	int argc, temp_argc;
	const char *s;
	char **envv = NULL;
	int envc;
        gboolean use_advanced;
        
        use_advanced = gnome_config_get_bool ("/panel/State/"ADVANCED_DIALOG_KEY"=false");
        
	if (response == GTK_RESPONSE_HELP) {
		panel_show_help ("specialobjects", "RUNBUTTON");
		/* just return as we don't want to close */
		return;
	} else if (response != PANEL_RESPONSE_RUN) {
		goto return_and_close;
	}
        
        list = g_object_get_data (G_OBJECT (run_dialog), "dentry_list");
        terminal = GTK_TOGGLE_BUTTON (g_object_get_data (G_OBJECT(w),
							 "terminal"));
        
        if (g_object_get_data (G_OBJECT (run_dialog), "use_list")) {
                char *name;
		GtkTreeSelection *selection;
		GtkTreeModel *model;
		GtkTreeIter iter;
		GValue value = {0, };

		selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));

		/* just return if nothing selected */
		if ( ! gtk_tree_selection_get_selected (selection, 
							&model, &iter))
			return;

		gtk_tree_model_get_value (model, &iter,
					  COLUMN_NAME,
					  &value);
		name = g_strdup (g_value_get_string (&value));
		g_value_unset (&value);

                if (name != NULL) {
			GError *error = NULL;
                        GnomeDesktopItem *ditem;
                        
                        ditem = gnome_desktop_item_new_from_uri (name,
								 GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS,
								 &error);
			if (ditem != NULL) {
                                /* Honor "run in terminal" button */
				gnome_desktop_item_set_boolean (ditem,
								GNOME_DESKTOP_ITEM_TERMINAL,
								terminal->active);
			}

                        if (ditem == NULL) {
                                panel_error_dialog ("failed_to_load_desktop",
						    _("Failed to load this program!\n%s"),
						    error->message);
				g_clear_error (&error);
			} else if ( ! gnome_desktop_item_launch (ditem,
								 NULL /* file_list */,
								 0 /* flags */,
								 &error)) {
                                panel_error_dialog ("failed_to_load_desktop",
						    _("Failed to load this program!\n%s"),
						    error->message);
				g_clear_error (&error);
			}

			if (ditem != NULL) {
				gnome_desktop_item_unref (ditem);
			}

			g_free (name);
                }
        } else {
                entry = GTK_ENTRY (g_object_get_data (G_OBJECT (w), "entry"));

                s = gtk_entry_get_text(entry);

                if (string_empty (s))
                        goto return_and_close;

                /* evil eggies, do not translate! */
                if (strcmp (s, "time shall be unixey") == 0) {
                        foobar_widget_global_set_clock_format ("%s");
                        goto return_and_close;
                } else if (strcmp (s, "you shall bring us a shrubbery") == 0) {
                        panel_info_dialog ("ni_ni_ni_ni",
					   "NI! NI! NI! NI! NI! NI!");
                        goto return_and_close;
                } else if (strcmp (s, "supreme executive power") == 0) {
                        panel_info_dialog ("evil",
					   "Listen -- strange women lying in\n"
					   "ponds distributing swords is no\n"
					   "basis for a system of government.\n"
					   "Supreme executive power derives from\n"
					   "a mandate from the masses, not from\n"
					   "some farcical aquatic ceremony!");
                        goto return_and_close;
                } else if (strcmp (s, "free the fish") == 0) {
			start_screen_check ();
                        goto return_and_close;
		} else if (strcmp (s, "gegls from outer space") == 0) {
			start_geginv ();
                        goto return_and_close;
		} else if (strcmp (s, "End world hunger") == 0) {
			gnome_url_show ("http://www.wfp.org", NULL);
                        goto return_and_close;
		}

                /* Somewhat of a hack I suppose */
                if (panel_is_url (s)) {
                        gnome_url_show (s, NULL);
                        goto return_and_close;
                }

                if ( ! g_shell_parse_argv (s, &temp_argc, &temp_argv, NULL)) {
                        panel_error_dialog (_("Failed to execute command:\n"
                                              "%s"), s);
                        goto return_and_close;
                }

                get_environment (&temp_argc, &temp_argv, &envc, &envv);

                if (terminal->active) {
                        char **term_argv;
                        int term_argc;
                        gnome_config_get_vector ("/Gnome/Applications/Terminal",
                                                 &term_argc, &term_argv);
                        if (term_argv) {
                                int i;
                                argv = g_new(char *, term_argc + temp_argc + 1);
                                argc = term_argc + temp_argc;
                                for(i = 0; i < term_argc; i++) {
                                        argv[i] = term_argv[i];
                                        term_argv[i] = NULL;
                                }
                                for(i = term_argc; i < term_argc+temp_argc; i++) {
                                        argv[i] = temp_argv[i-term_argc];
                                        temp_argv[i-term_argc] = NULL;
				}
                                argv[i] = NULL;
				g_free (term_argv);
                        } else {
                                char *check;
                                int i;
                                check = g_find_program_in_path ("gnome-terminal");
                                argv = g_new(char *, 2 + temp_argc + 1);
                                argc = 2 + temp_argc;
                                if(!check) {
                                        argv[0] = g_strdup ("xterm");
                                        argv[1] = g_strdup ("-e");
                                } else {
                                        argv[0] = check;
                                        argv[1] = g_strdup ("-x");
                                }
                                for(i = 2; i < 2+temp_argc; i++) {
                                        argv[i] = temp_argv[i-2];
                                        temp_argv[i-2] = NULL;
				}
                                argv[i] = NULL;
                        }
                } else {
                        argv = temp_argv;
                        temp_argv = NULL;
                        argc = temp_argc;
                }

                if (gnome_execute_async_with_env (g_get_home_dir (),
                                                  argc, argv,
                                                  envc, envv) < 0) {
                        panel_error_dialog(_("Failed to execute command:\n"
                                             "%s\n"
                                             "%s"),
                                           s, g_strerror (errno));
                }
        }
        
return_and_close:
	g_strfreev (argv);
	g_strfreev (temp_argv);
	g_strfreev (envv);
	gtk_widget_destroy (w);
}

static void
browse_ok (GtkWidget *widget, GtkFileSelection *fsel)
{
	const char *fname;
	GtkWidget *entry;

	g_return_if_fail (GTK_IS_FILE_SELECTION (fsel));

	entry = g_object_get_data (G_OBJECT (fsel), "entry");

	fname = gtk_file_selection_get_filename (fsel);
	if (fname != NULL) {
		const char *s = gtk_entry_get_text (GTK_ENTRY (entry));
		if (string_empty (s)) {
			gtk_entry_set_text (GTK_ENTRY (entry), fname);
		} else {
			char *str = g_strconcat (s, " ", fname, NULL);
			gtk_entry_set_text (GTK_ENTRY (entry), str);
			g_free (str);
		}
	}
	gtk_widget_destroy (GTK_WIDGET (fsel));
}

static void
browse (GtkWidget *w, GtkWidget *entry)
{
	GtkFileSelection *fsel;

	fsel = GTK_FILE_SELECTION(gtk_file_selection_new(_("Choose a program to run")));
	gtk_window_set_transient_for (GTK_WINDOW (fsel),
				      GTK_WINDOW (run_dialog));
	g_object_set_data (G_OBJECT (fsel), "entry", entry);

	g_signal_connect (G_OBJECT (fsel->ok_button), "clicked",
			    G_CALLBACK (browse_ok), fsel);
	g_signal_connect_swapped (G_OBJECT (fsel->cancel_button), "clicked",
		 		  G_CALLBACK (gtk_widget_destroy), 
		 		  G_OBJECT (fsel));
	panel_signal_connect_object_while_alive
		(G_OBJECT (entry), "destroy",
		 G_CALLBACK (gtk_widget_destroy),
		 G_OBJECT (fsel));

	gtk_window_set_position (GTK_WINDOW (fsel), GTK_WIN_POS_MOUSE);

	gtk_window_present (GTK_WINDOW (fsel));
}

static gboolean
entry_event (GtkEntry * entry, GdkEventKey * event, gpointer data)
{
	if (event->type != GDK_KEY_PRESS)
		return FALSE;

	/* completion */
	if ((event->keyval == GDK_Tab) &&
	    (event->state & GDK_CONTROL_MASK)) {
		gchar* prefix;
		gchar* nprefix = NULL;
		gint pos;

		ensure_completion ();

		pos = gtk_editable_get_position (GTK_EDITABLE (entry));
		prefix = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, pos);

		g_completion_complete (exe_completion, prefix, &nprefix);

		if (nprefix != NULL &&
		    strlen (nprefix) > strlen (prefix)) {
			gtk_editable_insert_text (GTK_EDITABLE (entry),
						  nprefix + pos, 
						  strlen (nprefix) -
						    strlen (prefix),
						  &pos);
			gtk_editable_set_position (GTK_EDITABLE (entry), pos);
		} else {
                        gdk_beep ();
                }

		g_free (nprefix);
		g_free (prefix);

		return TRUE;
	}

	return FALSE;
}

static void
sync_entry_to_list (GtkWidget *dialog)
{
        GtkWidget *clist;
        GtkWidget *entry;
        gboolean blocked;

        blocked = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (dialog),
						      "sync_entry_to_list_blocked"));
        if (blocked)
                return;
        
        clist = g_object_get_data (G_OBJECT (dialog), "dentry_list");
        entry = g_object_get_data (G_OBJECT (dialog), "entry");

        unset_selected (dialog);
}

static void
sync_list_to_entry (GtkWidget *dialog)
{
        GtkWidget *list;
        GtkWidget *entry;
        GtkWidget *terminal_toggle;
        gchar *name;
	GtkTreeSelection *selection;
	GtkTreeModel *model;
	GtkTreeIter iter;

        g_object_set_data (G_OBJECT (dialog),
			   "sync_entry_to_list_blocked",
			   GINT_TO_POINTER (TRUE));
        
        list = g_object_get_data (G_OBJECT (dialog), "dentry_list");
        entry = g_object_get_data (G_OBJECT (dialog), "entry");
        terminal_toggle = g_object_get_data (G_OBJECT (dialog), "terminal");

	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));

	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
		GValue value = {0, };

		gtk_tree_model_get_value (model, &iter,
					  COLUMN_NAME,
					  &value);
		name = g_strdup (g_value_get_string (&value));
		g_value_unset (&value);

                if (name != NULL) {
                        GnomeDesktopItem *ditem;

                        ditem = gnome_desktop_item_new_from_uri (name,
								 GNOME_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS,
								 NULL /* error */);
                        if (ditem != NULL) {
				gboolean terminal;
                                const char *exec;

				exec = gnome_desktop_item_get_string
					(ditem, GNOME_DESKTOP_ITEM_EXEC);
				if (exec == NULL)
					exec = gnome_desktop_item_get_string
						(ditem, GNOME_DESKTOP_ITEM_URL);
				terminal = gnome_desktop_item_get_boolean
					(ditem, GNOME_DESKTOP_ITEM_TERMINAL);

                                gtk_entry_set_text (GTK_ENTRY (entry),
						    sure_string (exec));

                                gtk_toggle_button_set_active
					(GTK_TOGGLE_BUTTON (terminal_toggle),
					 terminal);
				
                                gnome_desktop_item_unref (ditem);
                        }

			g_free (name);
                }
        }

	g_object_set_data (G_OBJECT (dialog),
			   "sync_entry_to_list_blocked",
			   GINT_TO_POINTER (FALSE));

	g_object_set_data (G_OBJECT (dialog), "use_list",
			   GINT_TO_POINTER (TRUE));
}

static void
toggle_contents (GtkWidget *button,
                 GtkWidget *dialog)
{
        gboolean use_advanced;
        
        use_advanced = gnome_config_get_bool ("/panel/State/"ADVANCED_DIALOG_KEY"=false");

        gnome_config_set_bool ("/panel/State/"ADVANCED_DIALOG_KEY, !use_advanced);
        gnome_config_sync ();

        update_contents (dialog);
}

static GtkWidget*
create_toggle_advanced_button (const char *label)
{
        GtkWidget *align;
        GtkWidget *button;

        align = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);

        button = gtk_button_new_with_label (label);

        gtk_container_add (GTK_CONTAINER (align), button);

        g_signal_connect (G_OBJECT (button), "clicked",
                            G_CALLBACK (toggle_contents),
                            run_dialog);

        g_object_set_data (G_OBJECT (run_dialog),
			   "advanced_toggle_label",
			   GTK_BIN (button)->child);
        
        return align;
}

static void
entry_changed (GtkWidget *entry,
               gpointer   data)
{
        sync_entry_to_list (GTK_WIDGET (data));
}

/* Called when advanced contents are switched to or first shown */
static void
advanced_contents_shown (GtkWidget *vbox,
                         GtkWidget *dialog)
{
        /* does nothing at the moment */
}

static void
activate_run (GtkWidget *entry, GtkWidget *dialog)
{
	gtk_dialog_response (GTK_DIALOG (dialog), PANEL_RESPONSE_RUN);
}

static GtkWidget*
create_advanced_contents (void)
{
        GtkWidget *vbox;
        GtkWidget *entry;
        GtkWidget *gentry;
        GtkWidget *hbox;
        GtkWidget *w;
        
        vbox = gtk_vbox_new (FALSE, 0);

        hbox = gtk_hbox_new(0, FALSE);
	
        gentry = gnome_entry_new ("gnome-run");
        gtk_box_pack_start (GTK_BOX (hbox), gentry, TRUE, TRUE, 0);
        /* 1/4 the width of the first screen should be a good value */
	g_object_set (G_OBJECT (gentry),
		      "width_request", (int)(multiscreen_width (0) / 4),
		      NULL);

        entry = gnome_entry_gtk_entry (GNOME_ENTRY (gentry));

        g_signal_connect (G_OBJECT (entry), "event",
			  G_CALLBACK (entry_event),
			  NULL);
	g_signal_connect (G_OBJECT (entry), "destroy",
			  G_CALLBACK (kill_completion),
			  NULL);
 
        gtk_combo_set_use_arrows_always (GTK_COMBO (gentry), TRUE);
        g_object_set_data (G_OBJECT (run_dialog), "entry", entry);

	g_signal_connect (G_OBJECT (entry), "activate",
			    G_CALLBACK (activate_run),
			    run_dialog);
        g_signal_connect (G_OBJECT (entry),
                            "changed",
                            G_CALLBACK (entry_changed),
                            run_dialog);
        
        w = gtk_button_new_with_mnemonic (_("_Browse..."));
        g_signal_connect(G_OBJECT(w), "clicked",
                           G_CALLBACK (browse), entry);
        gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE,
                            GNOME_PAD_SMALL);

        gtk_box_pack_start (GTK_BOX (vbox), hbox,
                            FALSE, FALSE, GNOME_PAD_SMALL);

        w = gtk_check_button_new_with_mnemonic(_("Run in _terminal"));
        g_object_set_data (G_OBJECT (run_dialog), "terminal", w);
        gtk_box_pack_start (GTK_BOX (vbox), w,
                            FALSE, FALSE, GNOME_PAD_SMALL);
        
        g_object_set_data_full (G_OBJECT (run_dialog),
				"advanced",
				g_object_ref (vbox),
				(GDestroyNotify) g_object_unref);

        g_object_set_data_full (G_OBJECT (run_dialog),
				"advanced-entry",
				g_object_ref (entry),
				(GDestroyNotify) g_object_unref);
        
        g_signal_connect (G_OBJECT (vbox),
			  "show",
			  G_CALLBACK (advanced_contents_shown),
			  run_dialog);

        return vbox;
}

static void
add_columns (GtkTreeView *treeview)
{
	GtkCellRenderer *renderer;
	GtkTreeViewColumn *column;

	renderer = gtk_cell_renderer_pixbuf_new ();
	column = gtk_tree_view_column_new ();
                
        gtk_tree_view_column_pack_start (column, renderer, FALSE);
        gtk_tree_view_column_set_attributes (column, renderer,
                                             "pixbuf", COLUMN_ICON,
                                             NULL);
        
	renderer = gtk_cell_renderer_text_new ();
        gtk_tree_view_column_pack_start (column, renderer, TRUE);

        gtk_tree_view_column_set_attributes (column, renderer,
                                             "text", COLUMN_FULLNAME,
                                             NULL);
	gtk_tree_view_append_column (treeview, column);
}

static gboolean
add_icon_idle (gpointer data)
{
	GtkTreeIter *iter;
	GValue value = {0, };
	char *file;
	GdkPixbuf *pixbuf;
	gboolean long_operation;

add_icon_again:

	if (add_icon_iters == NULL) {
		add_icon_idle_id = 0;
		return FALSE;
	}

	iter = add_icon_iters->data;
	add_icon_iters = g_slist_remove (add_icon_iters, iter);

	gtk_tree_model_get_value (GTK_TREE_MODEL (data), iter,
				  COLUMN_ICON_FILE, &value);
	file = g_strdup (g_value_get_string (&value));
	g_value_unset (&value);

	pixbuf = panel_make_menu_icon (file,
				       NULL /* fallback */,
				       ICON_SIZE,
				       &long_operation);
	if (pixbuf != NULL) {
		gtk_list_store_set (GTK_LIST_STORE (data), iter,
				    COLUMN_ICON, pixbuf,
				    -1);
		g_object_unref (G_OBJECT (pixbuf));
	}

	g_free (file);
	g_free (iter);

	/* don't go back into the main loop if this wasn't very hard to do */
	if ( ! long_operation)
		goto add_icon_again;

	if (add_icon_iters != NULL) {
		return TRUE;
	} else {
		add_icon_idle_id = 0;
		return FALSE;
	}
}

/* Called when simple contents are switched to or first shown */
static void
fill_list (GtkWidget *list)
{
        GSList *tmp;
        GSList *files;
        GSList *prev;
        char *prev_name;
	GtkListStore *store;
	FileRec *all_dir;
        
	/* create list store */
	store = gtk_list_store_new (NUM_COLUMNS,
				    GDK_TYPE_PIXBUF,
				    G_TYPE_STRING,
				    G_TYPE_STRING,
				    G_TYPE_STRING,
				    G_TYPE_STRING);

	all_dir = fr_get_dir ("all-applications:/");
	if (all_dir != NULL) {
		files = g_slist_copy (((DirRec *)all_dir)->recs);
	} else {
		files = NULL;
	}

	/* Collate */
	files = g_slist_sort (files, (GCompareFunc) fr_compare);

	/* Strip duplicates */
	tmp = files;
	prev = NULL;
	prev_name = NULL;
	while (tmp) {
		FileRec *fr;

		fr = tmp->data;
		if (prev_name && strcmp (fr->fullname, prev_name) == 0) {
			GSList *del = tmp;

			prev->next = del->next;
			g_slist_free_1 (del);
			tmp = prev->next;
		} else {
			prev = tmp;
			prev_name = fr->fullname;
			tmp = tmp->next;
		}
	}

	tmp = files;
	while (tmp != NULL) {
		GtkTreeIter *iter;
		FileRec *fr;

		fr = tmp->data;

		iter = g_new0 (GtkTreeIter, 1);

		gtk_list_store_append (store, iter);
		gtk_list_store_set (store, iter,
				    COLUMN_ICON, NULL,
				    COLUMN_ICON_FILE, fr->icon,
				    COLUMN_FULLNAME, fr->fullname,
				    COLUMN_COMMENT, fr->comment,
				    COLUMN_NAME, fr->name,
				    -1);

		add_icon_iters = g_slist_prepend (add_icon_iters, iter);

		tmp = tmp->next;
	}

	g_slist_free (files);

	gtk_tree_view_set_model (GTK_TREE_VIEW (list), 
				 GTK_TREE_MODEL (store));

	add_columns (GTK_TREE_VIEW (list));

	add_icon_iters = g_slist_reverse (add_icon_iters);

	if (add_icon_idle_id == 0)
		add_icon_idle_id = g_idle_add (add_icon_idle, store);
}

#define DEFAULT_ICON "document-icons/i-executable.png"
#define FALLBACK_DEFAULT_ICON "gnome-logo-icon-transparent.png"

static void
unset_pixmap (GtkWidget *gpixmap)
{
        gchar *file;

        file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_PIXMAP, 
					  DEFAULT_ICON, TRUE, NULL);

	if (file == NULL)
		file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP, 
						  DEFAULT_ICON, TRUE, NULL);
        if (file == NULL)
                file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_PIXMAP, 
						  FALLBACK_DEFAULT_ICON, TRUE, NULL);
        if (file == NULL)
                file = gnome_program_locate_file (NULL, GNOME_FILE_DOMAIN_APP_PIXMAP, 
						  FALLBACK_DEFAULT_ICON, TRUE, NULL);
        
	gtk_image_set_from_file (GTK_IMAGE (gpixmap), file);

	g_free (file);
}

static void
unset_selected (GtkWidget *dialog)
{
        GtkWidget *label;
        GtkWidget *gpixmap;
        GtkWidget *desc_label;
        GtkWidget *entry;
        GtkWidget *list;
        char *text;
        
        label = g_object_get_data (G_OBJECT (dialog), "label");
        gpixmap = g_object_get_data (G_OBJECT (dialog), "pixmap");
        desc_label = g_object_get_data (G_OBJECT (dialog), "desc_label");
        entry = g_object_get_data (G_OBJECT (dialog), "entry");
        list = g_object_get_data (G_OBJECT (dialog), "dentry_list");
        
	if (entry != NULL) {
		text = gtk_editable_get_chars (GTK_EDITABLE (entry),
					       0, -1);
	} else {
		text = NULL;
	}

        if ( ! string_empty (text)) {
                char *msg;
                msg = g_strdup_printf (_("Will run '%s'"),
                                       text);
                if (label)
                        gtk_label_set_text (GTK_LABEL (label), msg);
                
                if (desc_label)
                        gtk_label_set_text (GTK_LABEL (desc_label), msg);

                g_free (msg);
        } else {
                if (label)
                        gtk_label_set_text (GTK_LABEL (label), _("No program selected"));
                
                if (desc_label)
                        gtk_label_set_text (GTK_LABEL (desc_label), _("No program selected"));
        }

        g_free (text);
        
        unset_pixmap (gpixmap);

        g_object_set_data (G_OBJECT (dialog), "use_list",
			   GPOINTER_TO_INT (FALSE));
	gtk_tree_selection_unselect_all
		(gtk_tree_view_get_selection (GTK_TREE_VIEW (list)));
}

static void
selection_changed (GtkTreeSelection *selection,
		   gpointer data)
{
        GtkWidget *label;
        GtkWidget *gpixmap;
        GtkWidget *desc_label;
        GtkWidget *dialog = data;
        gchar *name;
	GtkTreeModel *model;
	GtkTreeIter iter;
	GValue value = {0, };

	if ( ! gtk_tree_selection_get_selected (selection, &model, &iter))
		return;

	gtk_tree_model_get_value (model, &iter,
				  COLUMN_NAME,
				  &value);
	name = g_strdup (g_value_get_string (&value));
	g_value_unset (&value);

        label = g_object_get_data (G_OBJECT (dialog), "label");
        gpixmap = g_object_get_data (G_OBJECT (dialog), "pixmap");
        desc_label = g_object_get_data (G_OBJECT (dialog), "desc_label");

        if (name != NULL) {
                QuickDesktopItem *qitem;

		qitem = quick_desktop_item_load_uri (name /*file */,
						     "Application" /* expected type */,
						     TRUE /* run tryexec */);
		if (qitem != NULL) {
                        GdkPixbuf *pixbuf;
			char *icon;

			if (label != NULL)
				gtk_label_set_text (GTK_LABEL (label),
						    qitem->name);

			if (desc_label != NULL)
				gtk_label_set_text (GTK_LABEL (desc_label),
						    sure_string (qitem->comment));

			icon = gnome_desktop_item_find_icon (qitem->icon,
							     48 /* desired size */,
							     0 /* flags */);
			if (icon != NULL) {
				pixbuf = gdk_pixbuf_new_from_file (icon, NULL);
				g_free (icon);
			} else {
				pixbuf = NULL;
			}
                        
                        if (pixbuf != NULL) {
				gtk_image_set_from_pixbuf (GTK_IMAGE (gpixmap), pixbuf);
                        } else {
                                unset_pixmap (gpixmap);
                        }
                        
			quick_desktop_item_destroy (qitem);
                }

		g_free (name);
        }

        sync_list_to_entry (dialog);
}

static gboolean
add_items_idle (gpointer data)
{
	GtkWidget *list = data;
	add_items_idle_id = 0;
	fill_list (list);
	return FALSE;
}

static GtkWidget*
create_simple_contents (void)
{
        GtkWidget *vbox;
        GtkWidget *w;
        GtkWidget *label;
        GtkWidget *pixmap;
        GtkWidget *list;
        GtkWidget *hbox;
	GtkTreeSelection *selection;
        
        vbox = gtk_vbox_new (FALSE, 1);
        
        label = gtk_label_new_with_mnemonic (_("A_pplications:"));
        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
        gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);

        list = gtk_tree_view_new ();
        g_object_set_data (G_OBJECT (run_dialog), "dentry_list", list);

	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list), FALSE);
	gtk_label_set_mnemonic_widget (GTK_LABEL (label), list);

	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));

	gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);

        g_signal_connect (G_OBJECT (selection),
			  "changed",
			  G_CALLBACK (selection_changed),
			  run_dialog);
        
        w = gtk_scrolled_window_new (NULL, NULL);
        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (w),
                                             GTK_SHADOW_IN);
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
                                        GTK_POLICY_AUTOMATIC,
                                        GTK_POLICY_AUTOMATIC);
        gtk_container_add (GTK_CONTAINER (w), list);
        
        gtk_box_pack_start (GTK_BOX (vbox), w,
                            TRUE, TRUE, GNOME_PAD_SMALL);


        w = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
        gtk_box_pack_start (GTK_BOX (vbox), w, FALSE, FALSE, 0);
        hbox = gtk_hbox_new (FALSE, 3);
        gtk_container_add (GTK_CONTAINER (w), hbox);
        
        pixmap = gtk_image_new ();
        gtk_box_pack_start (GTK_BOX (hbox), pixmap, FALSE, FALSE, 0);
        g_object_set_data (G_OBJECT (run_dialog), "pixmap", pixmap);
        
        label = gtk_label_new ("");
        gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
        gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
        gtk_label_set_selectable (GTK_LABEL (label), TRUE);
        gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
        g_object_set_data (G_OBJECT (run_dialog), "desc_label", label);

#if 0
        label = gtk_label_new ("");
        g_object_set_data (G_OBJECT (run_dialog), "label", label);
        gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
#endif

        unset_selected (run_dialog);
        
        g_object_ref (G_OBJECT (vbox));
        
        g_object_set_data_full (G_OBJECT (run_dialog),
				"simple",
				vbox,
				(GtkDestroyNotify) g_object_unref);



	if (add_items_idle_id == 0)
		add_items_idle_id = g_idle_add (add_items_idle, list);

        gtk_box_pack_start (GTK_BOX (GTK_DIALOG (run_dialog)->vbox),
                            vbox,
                            TRUE, TRUE, 0);
        
        w = create_toggle_advanced_button ("");
        gtk_box_pack_start (GTK_BOX (GTK_DIALOG (run_dialog)->vbox),
			    w, FALSE, FALSE, GNOME_PAD_SMALL);
        
        return vbox;
}


static void
update_contents (GtkWidget *dialog)
{
        GtkWidget *advanced = NULL;
        GtkWidget *advanced_toggle;
        gboolean use_advanced;
        GtkWidget *clist;
        
        use_advanced = gnome_config_get_bool ("/panel/State/"ADVANCED_DIALOG_KEY"=false");        
        advanced_toggle = g_object_get_data (G_OBJECT (dialog),
					     "advanced_toggle_label");

        clist = g_object_get_data (G_OBJECT (dialog), "dentry_list");
        
        if (use_advanced) {
                advanced = g_object_get_data (G_OBJECT (dialog), "advanced");
                
                if (advanced && advanced->parent == NULL) {
			GtkWidget *advanced_entry;

			advanced_entry =
				g_object_get_data (G_OBJECT (dialog), "advanced-entry");

                        gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                                            advanced,
                                            FALSE, FALSE, 0);
                
                        gtk_widget_show_all (advanced);

                        gtk_widget_grab_focus (advanced_entry);
                }

                gtk_label_set_text_with_mnemonic (GTK_LABEL (advanced_toggle),
                                    		  _("_Advanced <<"));

                gtk_tooltips_set_tip (panel_tooltips, advanced_toggle->parent,
                                      _("Hide the advanced controls below this button."),
                                      NULL);                

        } else {                
                advanced = g_object_get_data (G_OBJECT (dialog), "advanced");
                
                if (advanced && advanced->parent != NULL)
                        gtk_container_remove (GTK_CONTAINER (advanced->parent), advanced);                
                gtk_label_set_text_with_mnemonic (GTK_LABEL (advanced_toggle),
                                    		  _("_Advanced >>"));

                gtk_tooltips_set_tip (panel_tooltips, advanced_toggle->parent,
                                      _("Allow typing in a command line instead of choosing an application from the list"),
                                      NULL);

                gtk_widget_grab_focus (clist);
        }
}

static inline void
register_run_stock_item (void)
{
	static gboolean registered = FALSE;

	if (!registered) {
		GtkIconFactory      *factory;
		GtkIconSet          *execute_icons;

		static GtkStockItem  run_item [] = {
			{ PANEL_STOCK_RUN, N_("_Run"), 0, 0, GETTEXT_PACKAGE },
		};

		execute_icons = gtk_icon_factory_lookup_default (GTK_STOCK_EXECUTE);

		factory = gtk_icon_factory_new ();

		gtk_icon_factory_add (factory, PANEL_STOCK_RUN, execute_icons);

		gtk_icon_factory_add_default (factory);

		gtk_stock_add_static (run_item, 1);

		registered = TRUE;
	}
}

static void
run_dialog_destroyed (GtkWidget *widget)
{
	run_dialog = NULL;
	g_slist_foreach (add_icon_iters, (GFunc)g_free, NULL);
	g_slist_free (add_icon_iters);
	add_icon_iters = NULL;
	if (add_icon_idle_id != 0)
		g_source_remove (add_icon_idle_id);
	add_icon_idle_id = 0;
	if (add_items_idle_id != 0)
		g_source_remove (add_items_idle_id);
	add_items_idle_id = 0;
}

void
show_run_dialog (void)
{
        gboolean  use_advanced;          
	char     *run_icon;

	if (no_run_box)
		return;

	if (run_dialog != NULL) {
		gtk_window_present (GTK_WINDOW (run_dialog));
		return;
	}

	register_run_stock_item ();

        use_advanced = gnome_config_get_bool ("/panel/State/"ADVANCED_DIALOG_KEY"=false");
        
	run_dialog = gtk_dialog_new_with_buttons (_("Run Program"),
						  NULL /* parent */,
						  0 /* flags */,
						  GTK_STOCK_HELP,
						  GTK_RESPONSE_HELP,
						  GTK_STOCK_CLOSE,
						  GTK_RESPONSE_CLOSE,
						  PANEL_STOCK_RUN,
						  PANEL_RESPONSE_RUN,
						  NULL);

        /* This is lame in advanced mode, but if you change it on mode
         * toggle it creates weird effects, so always use this policy
         */
	g_object_set (G_OBJECT (run_dialog),
		      "allow_grow", FALSE,
		      "allow_shrink", TRUE,
		      "resizable", TRUE,
		      NULL);

        /* Get some reasonable height in simple list mode */
        if (!use_advanced)
                gtk_window_set_default_size (GTK_WINDOW (run_dialog),
                                             -1, 400);

	run_icon = gnome_program_locate_file (
			NULL, GNOME_FILE_DOMAIN_PIXMAP, "gnome-run.png", TRUE, NULL);
	if (run_icon) {
		gnome_window_icon_set_from_file (GTK_WINDOW (run_dialog), run_icon);
		g_free (run_icon);
	}

	g_signal_connect (G_OBJECT (run_dialog), "destroy",
			  G_CALLBACK (run_dialog_destroyed),
			  NULL);
	gtk_window_set_position (GTK_WINDOW (run_dialog), GTK_WIN_POS_MOUSE);
	gtk_window_set_wmclass (GTK_WINDOW (run_dialog), "run_dialog", "Panel");

	gtk_dialog_set_default_response (GTK_DIALOG (run_dialog), PANEL_RESPONSE_RUN);

        g_signal_connect (G_OBJECT (run_dialog), "response", 
			  G_CALLBACK (run_dialog_response), NULL);

        create_simple_contents ();
        create_advanced_contents ();
        update_contents (run_dialog);
        
	gtk_widget_show_all (run_dialog);
}

void
show_run_dialog_with_text (const char *text)
{
	GtkWidget *entry;

	g_return_if_fail(text != NULL);

	show_run_dialog ();

	if(run_dialog == NULL) {
		return;
	}
        
	entry = g_object_get_data (G_OBJECT (run_dialog), "entry");

	gtk_entry_set_text(GTK_ENTRY(entry), text);
}
