/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Copyright (C) 2001 Ximian, Inc.
 *
 * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * Authors:
 *   Shane Butler <shane_b@users.sourceforge.net>
 *   Joaquin Cuenca Abela <e98cuenc@yahoo.com>
 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/**
 * SECTION:glade-signal-editor
 * @Title: GladeSignalEditor
 * @Short_Description: An interface to edit signals for a #GladeWidget.
 *
 * This isnt really a dockable widget, since you need to access the
 * #GladeSignalEditor struct's '->main_window' widget, the signal editor
 * is mostly of interest when implementing a custom object editor.
 */

#include <string.h>
#include <glib/gi18n-lib.h>

#include "glade.h"
#include "glade-widget.h"
#include "glade-widget-adaptor.h"
#include "glade-signal.h"
#include "glade-signal-editor.h"
#include "glade-editor.h"
#include "glade-command.h"


enum
{
	COLUMN_SIGNAL,
	COLUMN_HANDLER,
	COLUMN_AFTER,
	COLUMN_USERDATA,
	COLUMN_LOOKUP,

	COLUMN_USERDATA_SLOT,
	COLUMN_LOOKUP_VISIBLE,
	COLUMN_AFTER_VISIBLE,
	COLUMN_HANDLER_EDITABLE,
	COLUMN_USERDATA_EDITABLE,
	COLUMN_SLOT, /* if this row contains a "<Type...>" label */
	COLUMN_BOLD,
	NUM_COLUMNS
};

#define HANDLER_DEFAULT  _("<Type here>")
#define USERDATA_DEFAULT HANDLER_DEFAULT

static void
glade_signal_editor_after_toggled (GtkCellRendererToggle *cell,
				   gchar                 *path_str,
				   gpointer               data)
{
	GladeSignalEditor *editor = (GladeSignalEditor*) data;
	GtkTreeModel *model = GTK_TREE_MODEL (editor->model);
	GtkTreeIter  iter;
	GtkTreeIter iter_parent;
	GladeSignal *old_signal;
	GladeSignal *new_signal;
	GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
	gchar     *signal_name;
	gchar     *handler;
	gchar     *userdata;
	gboolean   lookup;
	gboolean   after;

	/* get toggled iter */
	gtk_tree_model_get_iter (model, &iter, path);
	gtk_tree_model_get (model, &iter,
			    COLUMN_SIGNAL,  &signal_name,
			    COLUMN_HANDLER, &handler,
			    COLUMN_USERDATA,&userdata,
			    COLUMN_LOOKUP,  &lookup,
			    COLUMN_AFTER,   &after, -1);
	if (signal_name == NULL)
	{
		if (!gtk_tree_model_iter_parent (model, &iter_parent, &iter))
			g_assert (FALSE);

		gtk_tree_model_get (model, &iter_parent, COLUMN_SIGNAL, &signal_name, -1);
		g_assert (signal_name != NULL);
	}

	old_signal = glade_signal_new (signal_name, handler, userdata, lookup, after);
	new_signal = glade_signal_new (signal_name, handler, userdata, lookup, !after);

	glade_command_change_signal (editor->widget, old_signal, new_signal);
	gtk_tree_store_set (GTK_TREE_STORE (model), &iter, COLUMN_AFTER, !after, -1);

	glade_signal_free (old_signal);
	glade_signal_free (new_signal);
	gtk_tree_path_free (path);
	g_free (signal_name);
	g_free (handler);
	g_free (userdata);
}

/*
  glade files do not support symbol names as signal handler user_data arguments
  yet, so we disabled the lookup column.
*/
#define LOOKUP_COLUMN 0

#if LOOKUP_COLUMN
static void
glade_signal_editor_lookup_toggled (GtkCellRendererToggle *cell,
				    gchar                 *path_str,
				    gpointer               data)
{
	GladeSignalEditor *editor = (GladeSignalEditor*) data;
	GtkTreeModel *model = GTK_TREE_MODEL (editor->model);
	GtkTreeIter  iter;
	GtkTreeIter iter_parent;
	GladeSignal *old_signal;
	GladeSignal *new_signal;
	GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
	gchar     *signal_name;
	gchar     *handler;
	gchar     *userdata;
	gboolean   lookup;
	gboolean   after;

	/* get toggled iter */
	gtk_tree_model_get_iter (model, &iter, path);
	gtk_tree_model_get (model, &iter,
			    COLUMN_SIGNAL,  &signal_name,
			    COLUMN_HANDLER, &handler,
			    COLUMN_USERDATA,&userdata,
			    COLUMN_LOOKUP,  &lookup,
			    COLUMN_AFTER,   &after, -1);
	if (signal_name == NULL)
	{
		if (!gtk_tree_model_iter_parent (model, &iter_parent, &iter))
			g_assert (FALSE);

		gtk_tree_model_get (model, &iter_parent, COLUMN_SIGNAL, &signal_name, -1);
		g_assert (signal_name != NULL);
	}

	old_signal = glade_signal_new (signal_name, handler, userdata, lookup, after);
	new_signal = glade_signal_new (signal_name, handler, userdata, !lookup, after);

	glade_command_change_signal (editor->widget, old_signal, new_signal);
	gtk_tree_store_set (GTK_TREE_STORE (model), &iter, COLUMN_LOOKUP, !lookup, -1);

	glade_signal_free (old_signal);
	glade_signal_free (new_signal);
	gtk_tree_path_free (path);
	g_free (signal_name);
	g_free (handler);
	g_free (userdata);
}


static gboolean
glade_signal_editor_is_valid_identifier (const char *text)
{
	char ch;

	if (text == NULL)
		return FALSE;

	ch = *text++;
	if (!(ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')))
		return FALSE;

	while ((ch = *text++) != 0)
		if (!(ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')))
			return FALSE;

	return TRUE;
}
#endif

static void
append_slot (GtkTreeModel *model, GtkTreeIter *iter_signal)
{
	GtkTreeIter iter_new_slot;
	GtkTreeIter iter_class;

	gtk_tree_store_append (GTK_TREE_STORE (model), &iter_new_slot, iter_signal);
	gtk_tree_store_set (GTK_TREE_STORE (model), &iter_new_slot,
			    COLUMN_HANDLER,          _(HANDLER_DEFAULT),
			    COLUMN_USERDATA,         _(USERDATA_DEFAULT),
			    COLUMN_LOOKUP,           FALSE,
			    COLUMN_LOOKUP_VISIBLE,   FALSE,
			    COLUMN_HANDLER_EDITABLE, TRUE,
			    COLUMN_USERDATA_EDITABLE,FALSE,
			    COLUMN_AFTER,            FALSE,
			    COLUMN_AFTER_VISIBLE,    FALSE,
			    COLUMN_SLOT,             TRUE,
			    COLUMN_USERDATA_SLOT,    TRUE,
			    -1);
	gtk_tree_model_iter_parent (model, &iter_class, iter_signal);

	/* mark the signal & class name as bold */
	gtk_tree_store_set (GTK_TREE_STORE (model), iter_signal, COLUMN_BOLD, TRUE, -1);
	gtk_tree_store_set (GTK_TREE_STORE (model), &iter_class, COLUMN_BOLD, TRUE, -1);
}

static void
remove_slot (GtkTreeModel *model, GtkTreeIter *iter)
{
	GtkTreeIter iter_next;
	GtkTreeIter iter_class;
	GtkTreeIter iter_signal;
	gboolean removing_top_signal_handler = TRUE;
	char *signal_name;

	gtk_tree_model_get (model, iter, COLUMN_SIGNAL, &signal_name, -1);
	if (signal_name == NULL)
	{
		gtk_tree_model_iter_parent (model, &iter_signal, iter);
		removing_top_signal_handler = FALSE;
	}
	else
		iter_signal = *iter;

	g_free (signal_name);

	gtk_tree_model_iter_parent (model, &iter_class, &iter_signal);

	/* special case for removing the handler of the first row */
	if (removing_top_signal_handler)
		gtk_tree_model_iter_nth_child (model, &iter_next, iter, 0);
	else
	{
		iter_next = *iter;
		gtk_tree_model_iter_next (model, &iter_next);
	}

	do
	{
		gchar *handler;
		gboolean after;
		gboolean slot;
		gboolean visible;

		gtk_tree_model_get (model,                &iter_next,
				    COLUMN_HANDLER,       &handler,
				    COLUMN_AFTER,         &after,
				    COLUMN_SLOT,          &slot,
				    COLUMN_AFTER_VISIBLE, &visible, -1);

		gtk_tree_store_set (GTK_TREE_STORE (model), iter,
				    COLUMN_HANDLER,         handler,
				    COLUMN_AFTER,           after,
				    COLUMN_SLOT,            slot,
				    COLUMN_AFTER_VISIBLE,   visible, -1);
		g_free (handler);

		*iter = iter_next;
	}
	while (gtk_tree_model_iter_next (model, &iter_next));

	gtk_tree_store_remove (GTK_TREE_STORE (model), iter);

	if (!gtk_tree_model_iter_has_child (model, &iter_signal))
	{
		/* mark the signal & class name as normal */
		gtk_tree_store_set (GTK_TREE_STORE (model), &iter_signal,
				    COLUMN_BOLD, FALSE, -1);
		gtk_tree_store_set (GTK_TREE_STORE (model), &iter_class,
				    COLUMN_BOLD, FALSE, -1);
	}
}

static gboolean
is_void_signal_handler (const gchar *signal_handler)
{
	return ( signal_handler == NULL ||
		*signal_handler == 0    ||
		 g_utf8_collate (signal_handler, _(HANDLER_DEFAULT)) == 0);
}

static gboolean
is_void_user_data (const gchar *user_data)
{
	return ( user_data == NULL ||
		*user_data == 0    ||
		 g_utf8_collate (user_data, _(USERDATA_DEFAULT)) == 0);
}

static void
glade_signal_editor_handler_cell_edited (GtkCellRendererText *cell,
					 const gchar         *path_str,
					 const gchar         *new_handler,
					 gpointer             data)
{
	GladeWidget *glade_widget = ((GladeSignalEditor*) data)->widget;
	GtkTreeModel *model = GTK_TREE_MODEL (((GladeSignalEditor*) data)->model);
	GtkTreePath *path = gtk_tree_path_new_from_string (path_str);
	GtkTreeIter iter;
	GtkTreeIter iter_signal;
	gchar    *signal_name;
	gchar    *old_handler;
	gchar    *userdata;
	gboolean  lookup;
	gboolean  after;
	gboolean  slot;

	gtk_tree_model_get_iter (model, &iter, path);
	gtk_tree_model_get (model,           &iter,
			    COLUMN_SIGNAL,   &signal_name,
			    COLUMN_HANDLER,  &old_handler,
			    COLUMN_USERDATA, &userdata,
			    COLUMN_LOOKUP,   &lookup,
			    COLUMN_AFTER,    &after,
			    COLUMN_SLOT,     &slot, -1);

	if (signal_name == NULL)
	{
		if (!gtk_tree_model_iter_parent (model, &iter_signal, &iter))
			g_assert (FALSE);

		gtk_tree_model_get (model, &iter_signal, COLUMN_SIGNAL, &signal_name, -1);
		g_assert (signal_name != NULL);
	}
	else
		iter_signal = iter;

	/* false alarm */
	if (slot && is_void_signal_handler(new_handler))
		return;

	/* we're adding a new handler */
	if (slot && !is_void_signal_handler(new_handler))
	{
		GladeSignal *new_signal = glade_signal_new (signal_name, new_handler,
							    NULL, FALSE, FALSE);
		glade_command_add_signal (glade_widget, new_signal);
		glade_signal_free (new_signal);
		gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
				    COLUMN_HANDLER,          new_handler,
				    COLUMN_AFTER_VISIBLE,    TRUE,
				    COLUMN_SLOT,             FALSE,
				    COLUMN_USERDATA_EDITABLE,TRUE, -1);

		/* append a <Type...> slot */
		append_slot (model, &iter_signal);
	}

	/* we're removing a signal handler */
	if (!slot && is_void_signal_handler(new_handler))
	{
		GladeSignal *old_signal = 
			glade_signal_new (signal_name, 
					  old_handler,
					  is_void_user_data (userdata) ? NULL : userdata, 
					  lookup, after);
		glade_command_remove_signal (glade_widget, old_signal);
		glade_signal_free (old_signal);

		gtk_tree_store_set
			(GTK_TREE_STORE (model),          &iter,
				 COLUMN_HANDLER,          _(HANDLER_DEFAULT),
				 COLUMN_AFTER,            FALSE,
				 COLUMN_USERDATA,         _(USERDATA_DEFAULT),
				 COLUMN_LOOKUP,           FALSE,
				 COLUMN_LOOKUP_VISIBLE,   FALSE,
				 COLUMN_HANDLER_EDITABLE, TRUE,
				 COLUMN_USERDATA_EDITABLE,FALSE,
				 COLUMN_AFTER_VISIBLE,    FALSE,
				 COLUMN_SLOT,             TRUE,
				 COLUMN_USERDATA_SLOT,    TRUE,
				 -1);

		remove_slot (model, &iter);
	}

	/* we're changing a signal handler */
	if (!slot && !is_void_signal_handler(new_handler))
	{
		GladeSignal *old_signal =
			glade_signal_new
			(signal_name,
			 old_handler,
			 is_void_user_data(userdata) ? NULL : userdata,
			 lookup,
			 after);
		GladeSignal *new_signal =
			glade_signal_new
			(signal_name,
			 new_handler,
			 is_void_user_data(userdata) ? NULL : userdata,
			 lookup,
			 after);

		glade_command_change_signal (glade_widget, old_signal, new_signal);

		glade_signal_free (old_signal);
		glade_signal_free (new_signal);

		gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
				    COLUMN_HANDLER,       new_handler,
				    COLUMN_AFTER_VISIBLE, TRUE,
				    COLUMN_SLOT,          FALSE,
				    COLUMN_USERDATA_EDITABLE,TRUE, -1);
	}

	gtk_tree_path_free (path);
	g_free (signal_name);
	g_free (old_handler);
	g_free (userdata);
}

static void
glade_signal_editor_construct_handler_store (GladeSignalEditor *editor)
{
	const gchar *handlers[] = {"gtk_widget_show", 
				   "gtk_widget_hide",
				   "gtk_widget_grab_focus",
				   "gtk_widget_destroy",
				   "gtk_true",
				   "gtk_false",
				   "gtk_main_quit",
				   NULL};
	GtkTreeIter iter, *iters = editor->iters;
	GtkEntryCompletion *completion;
	GtkListStore *store;
	gint i;
				   
	store = gtk_list_store_new (1, G_TYPE_STRING);
		
	gtk_list_store_append (store, &iters[0]);
	gtk_list_store_append (store, &iters[1]);
		
	for (i = 0; handlers[i]; i++)
	{
		gtk_list_store_append (store, &iter);
		gtk_list_store_set (store, &iter, 0, handlers[i], -1);
	}
	
	completion = gtk_entry_completion_new ();
	gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (store));
	gtk_entry_completion_set_text_column (completion, 0);
	gtk_entry_completion_set_inline_completion (completion, TRUE);
	gtk_entry_completion_set_popup_completion (completion, FALSE);
	
	editor->handler_store = store;
	editor->completion = completion;
}

static void
glade_signal_editor_handler_store_update (GladeSignalEditor *editor,
					  const gchar *path)
{
	GtkTreeModel *model = GTK_TREE_MODEL (editor->model);
	GtkListStore *store = editor->handler_store;
	GtkTreeIter iter, *iters = editor->iters;
	gchar *handler, *signal, *name;
	
	name = (gchar *) glade_widget_get_name (editor->widget);
		
	if (gtk_tree_model_get_iter_from_string (model, &iter, path))
	{
		gtk_tree_model_get (model, &iter, COLUMN_SIGNAL, &signal, -1);
		if (signal) glade_util_replace (signal, '-', '_');
		else return;
	}
	else return;

	handler = g_strdup_printf ("on_%s_%s", name, signal);
	gtk_list_store_set (store, &iters[0], 0, handler, -1);
	g_free (handler);

	handler = g_strdup_printf ("%s_%s_cb", name, signal);
	gtk_list_store_set (store, &iters[1], 0, handler, -1);
	g_free (handler);
	
	g_free (signal);
}

static void
glade_signal_editor_editing_started (GtkEntry *entry, gboolean handler)
{
	const gchar *text = gtk_entry_get_text (entry);
	
	if ((handler) ? is_void_signal_handler (text) : is_void_user_data (text))
		gtk_entry_set_text (entry, "");
}

static void
glade_signal_editor_handler_editing_started (GtkCellRenderer *cell,
					     GtkCellEditable *editable,
					     const gchar *path,
					     GladeSignalEditor *editor)
{
	GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (editable)));
	
	glade_signal_editor_editing_started (entry, TRUE);
	
	glade_signal_editor_handler_store_update (editor, path);
	
	gtk_entry_set_completion (entry, editor->completion);
}

static void
glade_signal_editor_user_data_editing_started (GtkCellRenderer *cell,
					       GtkCellEditable *editable,
					       const gchar *path,
					       GladeSignalEditor *editor)
{
	GtkEntry *entry = GTK_ENTRY (editable);
	GtkEntryCompletion *completion;
	GtkListStore *store;
	GtkTreeIter iter;
	GList *list;
	
	g_return_if_fail (editor->widget != NULL);
	
	glade_signal_editor_editing_started (entry, FALSE);
	
	store = gtk_list_store_new (1, G_TYPE_STRING);
	
	for (list = (GList *) glade_project_get_objects (editor->widget->project);
	     list && list->data;
	     list = g_list_next (list))
	{
		GladeWidget *widget = glade_widget_get_from_gobject (list->data);
		
		if (widget)
		{
			gtk_list_store_append (store, &iter);
			gtk_list_store_set (store, &iter, 0, widget->name, -1);
		}
	}
	
	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (store), 0,
					      GTK_SORT_DESCENDING);
	
	completion = gtk_entry_completion_new ();
	
	gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (store));
	gtk_entry_completion_set_text_column (completion, 0);	
	gtk_entry_set_completion (entry, completion);
	
	/* Get rid of references */
	g_object_unref (store);
	g_object_unref (completion);
}

static void
glade_signal_editor_userdata_cell_edited (GtkCellRendererText *cell,
					  const gchar         *path_str,
					  const gchar         *new_userdata,
					  gpointer             data)
{
	GladeWidget  *glade_widget = ((GladeSignalEditor*) data)->widget;
	GtkTreeModel *model = GTK_TREE_MODEL (((GladeSignalEditor*) data)->model);
	GtkTreePath  *path = gtk_tree_path_new_from_string (path_str);
	GtkTreeIter iter;
	GtkTreeIter iter_signal;
	GladeSignal *old_signal, *new_signal;
	gchar *signal_name;
	gchar *old_userdata;
	gchar *handler;
	gboolean after;
	gboolean lookup;
	
	gtk_tree_model_get_iter (model, &iter, path);
	gtk_tree_model_get (model,           &iter,
			    COLUMN_SIGNAL,   &signal_name,
			    COLUMN_HANDLER,  &handler,
			    COLUMN_USERDATA, &old_userdata,
			    COLUMN_LOOKUP,   &lookup,
			    COLUMN_AFTER,    &after, -1);

	if (signal_name == NULL)
	{
		if (!gtk_tree_model_iter_parent (model, &iter_signal, &iter))
			g_assert (FALSE);

		gtk_tree_model_get (model, &iter_signal, COLUMN_SIGNAL, &signal_name, -1);
		g_assert (signal_name != NULL);
	}
	else
		iter_signal = iter;

	/* We are removing userdata */
	if (is_void_user_data(new_userdata))
	{
		gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
				    COLUMN_USERDATA_SLOT,  TRUE,
				    COLUMN_USERDATA,       _(USERDATA_DEFAULT),
				    COLUMN_LOOKUP,         FALSE,
				    COLUMN_LOOKUP_VISIBLE, FALSE, -1);
	}
	else
	{
		gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
			    COLUMN_USERDATA_SLOT,  FALSE,
			    COLUMN_USERDATA,       new_userdata,
			    COLUMN_LOOKUP_VISIBLE, TRUE,
			    -1);
	}

	old_signal =
		glade_signal_new
		(signal_name, handler,
		 is_void_user_data(old_userdata) ? NULL : old_userdata,
		 lookup, after);

	new_signal = 
		glade_signal_new 
		(signal_name, handler,
		 is_void_user_data(new_userdata) ? NULL : new_userdata,
		 lookup, after);

	if (glade_signal_equal (old_signal, new_signal) == FALSE)
		glade_command_change_signal (glade_widget, old_signal, new_signal);
	
	glade_signal_free (old_signal);
	glade_signal_free (new_signal);

	gtk_tree_path_free (path);
	g_free (signal_name);
	g_free (handler);
	g_free (old_userdata);
}

static void
row_activated (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *tree_view_column, gpointer user_data)
{
	gtk_tree_view_set_cursor (view, path, tree_view_column, TRUE);
	gtk_widget_grab_focus (GTK_WIDGET (view));
}

static GtkWidget *
glade_signal_editor_construct_signals_list (GladeSignalEditor *editor)
{
	GtkTreeView *view;
	GtkWidget *view_widget;
	GtkTreeViewColumn *column;
 	GtkCellRenderer *renderer;
	GtkTreeModel *model;

	editor->model = gtk_tree_store_new
		(NUM_COLUMNS,
		 G_TYPE_STRING,   /* Signal  value      */
		 G_TYPE_STRING,   /* Handler value      */
		 G_TYPE_BOOLEAN,  /* After   value      */
		 G_TYPE_STRING,   /* User data value    */
		 G_TYPE_BOOLEAN,  /* module lookup string for
				   * user data */
		 G_TYPE_BOOLEAN,  /* Whether userdata is a slot */
		 G_TYPE_BOOLEAN,  /* Lookup visibility  */
		 G_TYPE_BOOLEAN,  /* After   visibility */
		 G_TYPE_BOOLEAN,  /* Handler editable   */
		 G_TYPE_BOOLEAN,  /* Userdata editable  */
		 G_TYPE_BOOLEAN,  /* New slot           */
		 G_TYPE_BOOLEAN); /* Mark with bold     */

	model = GTK_TREE_MODEL (editor->model);

	view_widget = gtk_tree_view_new_with_model (model);
	g_object_set (G_OBJECT (view_widget), "enable-search", FALSE, NULL);

	view = GTK_TREE_VIEW (view_widget);

	/* the view now holds a reference, we can get rid of our own */
	g_object_unref (G_OBJECT (editor->model));

	g_signal_connect(view, "row-activated", (GCallback) row_activated, NULL);

	/* Contruct handler model */
	glade_signal_editor_construct_handler_store (editor);

	/************************ signal column ************************/
 	renderer = gtk_cell_renderer_text_new ();
	g_object_set (G_OBJECT (renderer), "weight", PANGO_WEIGHT_BOLD, NULL); 
	column = gtk_tree_view_column_new_with_attributes
		(_("Signal"), renderer, "text", COLUMN_SIGNAL, "weight-set", COLUMN_BOLD, NULL);
 	gtk_tree_view_append_column (view, column);

	/************************ handler column ************************/
 	renderer = gtk_cell_renderer_combo_new ();
	g_object_set (G_OBJECT (renderer),
		      "style", PANGO_STYLE_ITALIC,
		      "foreground", "Gray",
		      "model", GTK_TREE_MODEL (editor->handler_store),
		      "text-column", 0,
		      NULL);
	
	g_signal_connect (renderer, "edited",
			  G_CALLBACK (glade_signal_editor_handler_cell_edited), editor);

	g_signal_connect (renderer, "editing-started",
			  G_CALLBACK (glade_signal_editor_handler_editing_started),
			  editor);

	column = gtk_tree_view_column_new_with_attributes
		(_("Handler"),     renderer,
		 "text",           COLUMN_HANDLER,
		 "style_set",      COLUMN_SLOT,
		 "foreground_set", COLUMN_SLOT,
		 "editable",       COLUMN_HANDLER_EDITABLE, NULL);

 	gtk_tree_view_append_column (view, column);

	/************************ userdata column ************************/
 	renderer = gtk_cell_renderer_text_new ();
	g_object_set (G_OBJECT (renderer),
		      "style", PANGO_STYLE_ITALIC,
		      "foreground", "Gray", NULL);

	g_signal_connect (renderer, "edited",
			  G_CALLBACK (glade_signal_editor_userdata_cell_edited), editor);
	
	g_signal_connect (renderer, "editing-started",
			  G_CALLBACK (glade_signal_editor_user_data_editing_started),
			  editor);
	
	column = gtk_tree_view_column_new_with_attributes
		(_("User data"), renderer,
		 "text",           COLUMN_USERDATA,
		 "style_set",      COLUMN_USERDATA_SLOT,
		 "foreground_set", COLUMN_USERDATA_SLOT,
		 "editable",       COLUMN_USERDATA_EDITABLE, NULL);

 	gtk_tree_view_append_column (view, column);
	
#if LOOKUP_COLUMN
	/************************ lookup column ************************/
 	renderer = gtk_cell_renderer_toggle_new ();
	g_signal_connect (renderer, "toggled",
			  G_CALLBACK (glade_signal_editor_lookup_toggled), editor);
	column = gtk_tree_view_column_new_with_attributes
		(_("Lookup"), renderer,
		 "active",  COLUMN_LOOKUP,
		 "visible", COLUMN_LOOKUP_VISIBLE, NULL);

 	gtk_tree_view_append_column (view, column);
#endif
	/************************ after column ************************/
	renderer = gtk_cell_renderer_toggle_new ();
	g_signal_connect (renderer, "toggled",
			  G_CALLBACK (glade_signal_editor_after_toggled), editor);
 	column = gtk_tree_view_column_new_with_attributes
		(_("After"), renderer,
		 "active",  COLUMN_AFTER,
		 "visible", COLUMN_AFTER_VISIBLE, NULL);
 	gtk_tree_view_append_column (view, column);

	return view_widget;
}

static void
glade_signal_editor_construct (GladeSignalEditor *editor)
{
	GtkWidget *vbox;
	GtkWidget *scroll;

	vbox = gtk_vbox_new (FALSE, 0);
	editor->main_window = vbox;

	scroll = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll),
					GTK_POLICY_AUTOMATIC,
					GTK_POLICY_AUTOMATIC);
	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll),
					     GTK_SHADOW_IN);

	editor->signals_list = glade_signal_editor_construct_signals_list (editor);
	gtk_container_add (GTK_CONTAINER (scroll), editor->signals_list);

	gtk_box_pack_start (GTK_BOX (vbox), scroll, TRUE, TRUE, 0);

	gtk_widget_show_all (editor->main_window);
}

/**
 * glade_signal_editor_get_widget:
 * @editor: a #GladeSignalEditor
 *
 * Returns: the #GtkWidget that is the main window for @editor, or %NULL if
 *          it does not exist
 */
GtkWidget *
glade_signal_editor_get_widget (GladeSignalEditor *editor)
{
	g_return_val_if_fail (GLADE_IS_SIGNAL_EDITOR (editor), NULL);
	g_return_val_if_fail (GTK_IS_WIDGET (editor->main_window), NULL);
	
	return editor->main_window;
}

/**
 * glade_signal_editor_new:
 * @editor: a #GladeEditor
 *
 * Returns: a new #GladeSignalEditor associated with @editor
 */
GladeSignalEditor *
glade_signal_editor_new (gpointer *editor)
{
	GladeSignalEditor *signal_editor;

	signal_editor = g_new0 (GladeSignalEditor, 1);

	glade_signal_editor_construct (signal_editor);
	signal_editor->editor = editor;
	
	return signal_editor;
}

/**
 * glade_signal_editor_load_widget:
 * @editor: a #GladeSignalEditor
 * @widget: a #GladeWidget
 *
 * TODO: write me
 */
void 
glade_signal_editor_load_widget (GladeSignalEditor *editor,
				 GladeWidget *widget)
{
	GList *list;
	const gchar *last_type = "";
	GtkTreeIter iter;
	GtkTreeIter parent_class;
	GtkTreeIter parent_signal;
	GtkTreePath *path_first;
	GPtrArray *signals;

	g_return_if_fail (GLADE_IS_SIGNAL_EDITOR (editor));
	g_return_if_fail (widget == NULL || GLADE_IS_WIDGET (widget));

	gtk_tree_store_clear (editor->model);

	editor->widget = widget;
	editor->adaptor = widget ? widget->adaptor : NULL;

	if (!widget)
		return;

	/* Loop over every signal type
	 */
	for (list = editor->adaptor->signals; list; list = list->next)
	{
		GladeSignalClass *signal = (GladeSignalClass *) list->data;
  
		/* Add class name that this signal belongs to.
		 */
		if (strcmp(last_type, signal->type))
		{
			gtk_tree_store_append (editor->model, &parent_class, NULL);
			gtk_tree_store_set    (editor->model,          &parent_class,
					       COLUMN_SIGNAL,           signal->type,
					       COLUMN_AFTER_VISIBLE,    FALSE,
					       COLUMN_HANDLER_EDITABLE, FALSE,
					       COLUMN_USERDATA_EDITABLE,FALSE,
					       COLUMN_SLOT,             FALSE,
					       COLUMN_BOLD,             FALSE, -1);
			last_type = signal->type;
		}

		gtk_tree_store_append (editor->model, &parent_signal, &parent_class);
		signals = glade_widget_list_signal_handlers (widget, signal->name);

		if (!signals || signals->len == 0)
		{
			gtk_tree_store_set
				(editor->model,          &parent_signal,
				 COLUMN_SIGNAL,           signal->name,
				 COLUMN_HANDLER,          _(HANDLER_DEFAULT),
				 COLUMN_AFTER,            FALSE,
				 COLUMN_USERDATA,         _(USERDATA_DEFAULT),
				 COLUMN_LOOKUP,           FALSE,
				 COLUMN_LOOKUP_VISIBLE,   FALSE,
				 COLUMN_HANDLER_EDITABLE, TRUE,
				 COLUMN_USERDATA_EDITABLE,FALSE,
				 COLUMN_AFTER_VISIBLE,    FALSE,
				 COLUMN_SLOT,             TRUE,
				 COLUMN_USERDATA_SLOT,    TRUE,
				 -1);
		}
		else
		{
			guint i;
			GtkTreePath *path_parent_class;
			GladeSignal *widget_signal =
				(GladeSignal*) g_ptr_array_index (signals, 0);

			/* mark the class of this signal as bold and expand it, 
                         * as there is at least one signal with handler */
			gtk_tree_store_set (editor->model, &parent_class, COLUMN_BOLD, TRUE, -1);
			path_parent_class =
				gtk_tree_model_get_path (GTK_TREE_MODEL (editor->model),
							 &parent_class);
			gtk_tree_view_expand_row (GTK_TREE_VIEW (editor->signals_list),
						  path_parent_class, FALSE);
			gtk_tree_path_free (path_parent_class);

			gtk_tree_store_set
				(editor->model,            &parent_signal,
				 COLUMN_SIGNAL,             signal->name,
				 COLUMN_HANDLER,            widget_signal->handler,
				 COLUMN_AFTER,              widget_signal->after,
				 COLUMN_USERDATA,
				 widget_signal->userdata ?
				 widget_signal->userdata : _(USERDATA_DEFAULT),
				 COLUMN_LOOKUP,             widget_signal->lookup,
				 COLUMN_LOOKUP_VISIBLE,
				 widget_signal->userdata ?  TRUE : FALSE,
				 COLUMN_AFTER_VISIBLE,      TRUE,
				 COLUMN_HANDLER_EDITABLE,   TRUE,
				 COLUMN_USERDATA_EDITABLE,  TRUE,
				 COLUMN_SLOT,               FALSE,
				 COLUMN_USERDATA_SLOT,
				 widget_signal->userdata  ? FALSE : TRUE,
				 COLUMN_BOLD,               TRUE, -1);

			for (i = 1; i < signals->len; i++)
			{
				widget_signal = (GladeSignal*) g_ptr_array_index (signals, i);
				gtk_tree_store_append (editor->model, &iter, &parent_signal);

				gtk_tree_store_set
					(editor->model,            &iter,
					 COLUMN_HANDLER,            widget_signal->handler,
					 COLUMN_AFTER,              widget_signal->after,
					 COLUMN_USERDATA,
					 widget_signal->userdata  ?
					 widget_signal->userdata : _(USERDATA_DEFAULT),
					 COLUMN_LOOKUP,           widget_signal->lookup,
					 COLUMN_LOOKUP_VISIBLE,
					 widget_signal->userdata  ? TRUE : FALSE,
					 COLUMN_AFTER_VISIBLE,      TRUE,
					 COLUMN_HANDLER_EDITABLE,   TRUE,
					 COLUMN_USERDATA_EDITABLE,  TRUE,
					 COLUMN_SLOT,               FALSE,
					 COLUMN_USERDATA_SLOT,
					 widget_signal->userdata  ? FALSE : TRUE,
					 -1);
			}

			/* add the <Type...> slot */
			gtk_tree_store_append (editor->model, &iter, &parent_signal);
			gtk_tree_store_set
				(editor->model,          &iter,
				 COLUMN_HANDLER,          _(HANDLER_DEFAULT),
				 COLUMN_AFTER,            FALSE,
				 COLUMN_USERDATA,         _(USERDATA_DEFAULT),
				 COLUMN_LOOKUP,           FALSE,
				 COLUMN_LOOKUP_VISIBLE,   FALSE,
				 COLUMN_HANDLER_EDITABLE, TRUE,
				 COLUMN_USERDATA_EDITABLE,FALSE,
				 COLUMN_AFTER_VISIBLE,    FALSE,
				 COLUMN_SLOT,             TRUE,
				 COLUMN_USERDATA_SLOT,    TRUE, -1);
		}
	}

	path_first = gtk_tree_path_new_first ();
	gtk_tree_view_expand_row (GTK_TREE_VIEW (editor->signals_list), path_first, FALSE);
	gtk_tree_path_free (path_first);
}


syntax highlighted by Code2HTML, v. 0.9.1