#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>

#include "guiutils.h"
#include "cdialog.h"

#include "editor.h"
#include "editorcb.h"
#include "editorop.h"
#include "editorfio.h"
#include "editordnd.h"
#include "viewerdnd.h"

#include "manedit.h"
#include "config.h"


/* Recieved data command string parsing */
void EditorDNDParseCmd(
	const gchar *string,
	editor_struct **editor_rtn,
	GtkCTreeNode ***branch, gint *total_branches
);
gchar *EditorDNDParseURL(
	const gchar *s, const gint len
);

/* Inserting to target editor routines */
static GtkCTreeNode *EditorDNDItemAdd(
	editor_struct *editor, editor_item_struct *item,
	GtkCTreeNode *parent, GtkCTreeNode *sibling
);
static void EditorDNDItemListAdd(
	editor_struct *editor,
	editor_item_struct **item, gint total_items,
	gint insert_row
);
static GtkCTreeNode *EditorDNDTrunkListAddIterate(
	medit_pixmaps_list_struct *pixmaps_list,
	editor_struct *editor, editor_struct *src_editor,
	GtkCTree *ctree, GtkCTree *src_ctree,
	GtkCTreeNode *parent_branch,    /* Parent on target editor */
	GtkCTreeNode *sibling_branch,   /* Sibling to insert before */
	GtkCTreeNode *src_branch        /* From source editor */
);
static void EditorDNDTrunkListAdd(
	editor_struct *editor,
	editor_struct *src_editor,
	GtkCTreeNode **branch, gint total_branches,
	gint insert_row
);
static void EditorDNDViewerIndexItemLoad(
	editor_struct *editor, viewer_struct *src_v,
	GtkCTreeNode **branch, gint total_branches,
	gint insert_row
);


/* DND handling */
void EditorDNDSetIcon(
	editor_struct *editor, editor_item_struct *item
);
void EditorDNDDataRequestCB(
	GtkWidget *widget, GdkDragContext *dc,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data
);
void EditorDNDDataRecievedCB(
	GtkWidget *widget,
	GdkDragContext *dc,
	gint x, gint y,
	GtkSelectionData *selection_data,
	guint info, guint t,
	gpointer data
);
static void EditorDNDDoDeleteItems(
	editor_struct *editor,   
	GtkCTreeNode **branch, gint total_branches 
);
void EditorDNDDataDeleteCB(
	GtkWidget *widget, GdkDragContext *dc, gpointer data
);


#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))


/*
 *	Parses the given string which should have been recieved as
 *	a DND command.
 *
 *	This function is designed to be called by
 *	EditorDNDDataRecievedCB().
 */
void EditorDNDParseCmd(
	const gchar *string,
	editor_struct **editor_rtn,
	GtkCTreeNode ***branch, gint *total_branches
)
{
	gint i;
	guint32 addr_val;
	gchar **strv;

	if(editor_rtn != NULL)
	    *editor_rtn = NULL;
	if(branch != NULL)
	    *branch = NULL;
	if(total_branches != NULL)
	    *total_branches = 0;

	if(string == NULL)
	    return;

	if(g_strcasepfx(string, "error"))
	    return;

	/* Parse command, format is as follows:
	 * <editor_ptr> <branch_ptr...>
	 */
	strv = g_strsplit(string, " ", -1);
	if(strv == NULL)
	    return;

	i = 0;

	/* Get the editor pointer
	 *
	 * Format is in hex with no "0x" prefix
	 */
	if(strv[i] != NULL)
	{
	    const gint status = sscanf(
		strv[i], "%x", (guint32 *)&addr_val
	    );
	    if((status > 0) && (editor_rtn != NULL))
		*editor_rtn = EDITOR(addr_val);

	    i++;
	}

	/* Get the list of branch pointers which should be branches
	 * on the parsed editor's layout ctree
	 */
	for(; strv[i] != NULL; i++)
	{
	    if((branch != NULL) && (total_branches != NULL))
	    {
		const gint n = (*total_branches);
		(*total_branches) = n + 1;
		(*branch) = (GtkCTreeNode **)g_realloc(
		    *branch,
		    (*total_branches) * sizeof(GtkCTreeNode *)
		);
		if((*branch) != NULL)
		{
		    const gint status = sscanf(
			strv[i], "%x", (guint32 *)&addr_val
		    );
		    if(status > 0)
			(*branch)[n] = (GtkCTreeNode *)addr_val;
		    else
			(*branch)[n] = NULL;
		}
		else
		{
		    (*total_branches) = 0;
		}
	    }
	}

	g_strfreev(strv);
}

/*
 *	Parses the URL.
 *
 *	Returns a dynamically allocated string describing the path
 *	portion of the URL.
 */
gchar *EditorDNDParseURL(
	const gchar *s, const gint len
)
{
	const gchar *cs, *cs2;

	if(s == NULL)
	    return(NULL);

	cs = (const gchar *)strstr((const char *)s, "://");
	if(cs != NULL)
	    cs += strlen("://");
	else
	    cs = s;

	cs2 = (const gchar *)strchr((const char *)cs, '/');
	if(cs2 == NULL)
	    cs2 = cs;

	return(g_strdup(cs2));
}


/*
 *      Adds the specified item the editor's layout ctree at the specified
 *	insert row. The given item's reference to the editor and widgets
 *	will be updated here.
 *
 *      The pointer to the given item should not be referenced again
 *      after calling this function.
 *
 *	Inputs assumed valid.
 *
 *	Returns the newly created node or NULL on failure.
 */
static GtkCTreeNode *EditorDNDItemAdd(
	editor_struct *editor, editor_item_struct *item,
	GtkCTreeNode *parent, GtkCTreeNode *sibling
)
{
	gchar *text[1]; 
	GtkCTreeNode *branch;
	GdkPixmap *closed_p = NULL, *opened_p = NULL;
	GdkBitmap *closed_m = NULL, *opened_m = NULL;
	medit_core_struct *core = editor->core;
	medit_pixmaps_list_struct *pixmaps_list = &core->pixmaps_list;
	GtkCTree *ctree = GTK_CTREE(editor->layout_ctree);

	/* Reset the name for the new branch */
	text[0] = NULL;

	/* Item data valid? */
	if(item != NULL)
	{
	    /* Handle by the item data type */
	    switch(item->type)
	    {
	      case EditorItemTypeFile:
		closed_p = pixmaps_list->manual_closed_20x20;
		closed_m = pixmaps_list->manual_closed_20x20_mask;
		opened_p = pixmaps_list->manual_opened_20x20;
		opened_m = pixmaps_list->manual_opened_20x20_mask;
		text[0] = item->name;
		break;

	      case EditorItemTypeHeader:
		closed_p = pixmaps_list->manpage_heading_20x20;
		closed_m = pixmaps_list->manpage_heading_20x20_mask;
		opened_p = closed_p;
		opened_m = closed_m;
		text[0] = "Header";
		break;

	      case EditorItemTypeSection:
		closed_p = pixmaps_list->manpage_section_20x20;
		closed_m = pixmaps_list->manpage_section_20x20_mask;
		opened_p = closed_p;
		opened_m = closed_m;
		text[0] = item->section_name;
		break;
	    }
	}

	/* Create new branch node on the layout ctree */
	if(text[0] == NULL)
	    text[0] = "New";
	branch = gtk_ctree_insert_node(
	    ctree, parent, sibling,
	    text,
	    MEDIT_LIST_ICON_TEXT_SPACING,
	    closed_p, closed_m,
	    opened_p, opened_m,
	    (item != NULL) ? item->is_leaf : TRUE,	/* Is leaf */
	    FALSE		/* Expanded */
	);

	/* Update values on the given item data structure to reflect
	 * the values for the new editor and widgets. Note that the new
	 * pointers for editor and ctree may not match the original ones
	 * so that is why we need to update them.
	 */
	if(item != NULL)
	{
	    item->editor = editor;
	    item->ctree = ctree;
	    item->parent = parent;
	    item->this_branch = branch;

	    /* Item of type file? */
	    if(item->type == EditorItemTypeFile)
	    {
		/* Need to add this to the editor's layout_trunk list */
	    }
	}

	/* Set item data to newly inserted branch */
	EditorBranchSetData(
	    ctree, branch,
	    item, EditorItemDestroyCB
	);
	item = NULL;

	/* Mark new branch's item data as having changes */
	EditorItemSetToplevelHasChanges(
	    editor, branch, TRUE
	);

	return(branch);
}

/*
 *      Adds the list of items to the given editor's layout ctree.
 *	Each item's reference to the editor and widgets will be updated
 *	by this call.
 *
 *      The given list of items will be transfered and should not
 *      longer be referenced (including the pointer array) after calling
 *      this function.
 *
 *	Only headers and sections (leafs, no trunks) can be added.
 */
static void EditorDNDItemListAdd(
	editor_struct *editor,
	editor_item_struct **item, gint total_items,
	gint insert_row
)
{
	gint i;
	GtkCTreeNode *parent_node;
	GtkCList *clist;
	GtkCTree *ctree;

	if((editor == NULL) || (item == NULL) || (total_items <= 0))
	{
	    for(i = 0; i < total_items; i++)
		EditorItemDelete(item[i]);
	    g_free(item);
	    return;
	}

	ctree = GTK_CTREE(editor->layout_ctree);
	clist = GTK_CLIST(ctree);

	/* Clip the insert row */
	if(insert_row < 0)
	    insert_row = 0;

	/* Begin inserting */
	if(TRUE)
	{
	    GtkCTreeNode *insert_node;
	    GtkCTreeRow *insert_node_row;

	    /* Match insert row with the node to insert at */
	    if(insert_row >= clist->rows)
		insert_node = gtk_ctree_node_nth(
		    ctree, MAX(clist->rows - 1, 0)
		);
	    else
		insert_node = gtk_ctree_node_nth(ctree, insert_row);
	    if(insert_node == NULL)
	    {
		for(i = 0; i < total_items; i++)
		    EditorItemDelete(item[i]);
		g_free(item);
		return;
	    }

	    insert_node_row = GTK_CTREE_ROW(insert_node);
	    if(insert_node_row == NULL)
	    {
		for(i = 0; i < total_items; i++)
		    EditorItemDelete(item[i]);
		g_free(item);
		return;
	    }

	    gtk_clist_freeze(clist);

	    /* Is the insert node a leaf (not a trunk)? */
	    if(insert_node_row->is_leaf)
	    {
		/* Insert at this node */
		parent_node = insert_node_row->parent;
/*		insert_node = insert_node_row->sibling; */

		/* Insert last to first */
		for(i = total_items - 1; i >= 0; i--)
		{
		    insert_node = EditorDNDItemAdd(
			editor, item[i],
			parent_node, insert_node
		    );
		    item[i] = NULL;
		}
	    }
	    else
	    {
		/* Inserting to trunk, so we insert before the
		 * first child so it'll become the first child(s).
		 */
		parent_node = insert_node;
		insert_node = insert_node_row->children;

		/* Insert last to first */
		for(i = total_items - 1; i >= 0; i--)
		{
		    insert_node = EditorDNDItemAdd(
			editor, item[i],
			parent_node, insert_node
		    );
		    item[i] = NULL;
		}
	    }

	    gtk_clist_thaw(clist);
	}

	/* Delete the items list */
	g_free(item);
}


/*
 *	Called by EditorDNDTrunkListAdd() to recursivly add src_branch
 *	(which is on the source editor) to the target editor's ctree with
 *	respect to the given parent and sibling branch (on the target
 *	editor).
 *
 *	Inputs for editors and ctree widgets assumed valid.
 *
 *	Returns the newly added branch (but not the added child branches within it).
 */
static GtkCTreeNode *EditorDNDTrunkListAddIterate(
	medit_pixmaps_list_struct *pixmaps_list,
	editor_struct *editor, editor_struct *src_editor,
	GtkCTree *ctree, GtkCTree *src_ctree,
	GtkCTreeNode *parent_branch,	/* Parent on target editor */
	GtkCTreeNode *sibling_branch,	/* Sibling to insert before */
	GtkCTreeNode *src_branch	/* From source editor */
)
{
	gchar *text[1];
	GdkPixmap *closed_p = NULL, *opened_p = NULL;
	GdkBitmap *closed_m = NULL, *opened_m = NULL;
	GtkCTreeNode *new_branch;
	GtkCTreeRow *branch_row; 
	editor_item_struct *item, *src_item;

	if(src_branch == NULL)
	    return(NULL);

	branch_row = GTK_CTREE_ROW(src_branch);

	/* Get source item pointer from source branch */
	src_item = EditorBranchGetData(
	    src_ctree, src_branch
	);

	text[0] = NULL;

	/* Item data valid? */
	if(src_item != NULL)
	{
	    /* Handle by item data type */
	    switch(src_item->type)
	    {
	      case EditorItemTypeFile:
		closed_p = pixmaps_list->manual_closed_20x20;
		closed_m = pixmaps_list->manual_closed_20x20_mask;
		opened_p = pixmaps_list->manual_opened_20x20;
		opened_m = pixmaps_list->manual_opened_20x20_mask;
		text[0] = src_item->name;
		break;

	      case EditorItemTypeHeader:
		closed_p = pixmaps_list->manpage_heading_20x20;
		closed_m = pixmaps_list->manpage_heading_20x20_mask;
		opened_p = closed_p;
		opened_m = closed_m;
		text[0] = "Header";
		break;

	      case EditorItemTypeSection:
		closed_p = pixmaps_list->manpage_section_20x20;
		closed_m = pixmaps_list->manpage_section_20x20_mask;
		opened_p = closed_p;
		opened_m = closed_m;
		text[0] = src_item->section_name;
		break;
	    }
	}

	/* Create new branch node on the layout ctree */
	if(text[0] == NULL)
	    text[0] = "New";

	/* Insert a new branch on target editor's layout_ctree */
	new_branch = gtk_ctree_insert_node(
	    ctree, parent_branch, sibling_branch,
	    text,
	    MEDIT_LIST_ICON_TEXT_SPACING,
	    closed_p, closed_m,
	    opened_p, opened_m,
	    (branch_row != NULL) ? branch_row->is_leaf : TRUE,
	    (branch_row != NULL) ? branch_row->expanded : FALSE
	);

	/* Set item data to newly inserted branch */
	item = EditorItemDup(src_item);
	if(item != NULL)
	{
	    /* Update values on coppied item pointer to reflect new
	     * target editor values
	     */
	    item->editor = editor;
	    item->ctree = ctree;
	    item->parent = parent_branch;
	    item->this_branch = new_branch;
	}
	EditorBranchSetData(
	    ctree, new_branch,
	    item, EditorItemDestroyCB
	);
	item = NULL;

	/* Mark new branch's item data as having changes */
	EditorItemSetToplevelHasChanges(
	    editor, new_branch, TRUE
	);


	/* Does the given source branch have children? */
	branch_row = GTK_CTREE_ROW(src_branch);
	if((branch_row != NULL) ? (branch_row->children != NULL) : FALSE)
	{
	    /* Update parent branch to be the newly added branch on the
	     * target editor.
	     */
	    parent_branch = new_branch;

	    sibling_branch = NULL;

	    /* Update source branch to point to the first child */
	    src_branch = branch_row->children;

	    /* Add each child branch on source editor to the target
	     * editor.
	     */
	    while(src_branch != NULL)
	    {
		EditorDNDTrunkListAddIterate(
		    pixmaps_list,
		    editor, src_editor,
		    ctree, src_ctree,
		    parent_branch,
		    sibling_branch,
		    src_branch
		);

		/* Get next sibling on source editor */
		branch_row = GTK_CTREE_ROW(src_branch);
		if(branch_row != NULL)
		    src_branch = branch_row->sibling;
		else
		    src_branch = NULL;
	    }
	}

	return(new_branch);
}

/*
 *      Adds the list of branches (which all must be of type
 *	EditorItemTypeFile) to the given editor's layout ctree. Each
 *	branch item's reference to the editor and widgets will be updated
 *      by this call.
 *
 *      The given list of branch pointers will not be modified by this
 *	function.
 */
static void EditorDNDTrunkListAdd(
	editor_struct *editor,
	editor_struct *src_editor,
	GtkCTreeNode **branch, gint total_branches,
	gint insert_row
)
{
	gint i, n;
	GtkCTreeNode *sibling_branch;
	GtkCList *clist, *src_clist;
	GtkCTree *ctree, *src_ctree;
	medit_core_struct *core;
	medit_pixmaps_list_struct *pixmaps_list;

	if((editor == NULL) || (src_editor == NULL) ||
	   (branch == NULL) || (total_branches <= 0)
	)
	    return;

	ctree = GTK_CTREE(editor->layout_ctree);
	clist = GTK_CLIST(ctree);

	src_ctree = GTK_CTREE(src_editor->layout_ctree);
	src_clist = GTK_CLIST(src_ctree);

	core = editor->core;
	pixmaps_list = &core->pixmaps_list;

	/* Clip the insert row */
	if(insert_row < 0)
	    insert_row = 0;

	/* Begin inserting */
	if(TRUE)
	{               
	    /* Insert at start or in the middle */
	    GtkCTreeNode *insert_node;
	    GtkCTreeRow *insert_node_row;

	    /* Match insert row with the node to insert at */
	    if(insert_row >= clist->rows)
		insert_node = gtk_ctree_node_nth(
		    ctree, MAX(clist->rows - 1, 0)
		); 
	    else
		insert_node = gtk_ctree_node_nth(ctree, insert_row);
	    if(insert_node == NULL)
	    {
		insert_node_row = NULL;
	    }
	    else
	    {
		insert_node = EditorItemGetToplevel(editor, insert_node);
		if(insert_node == NULL)
		    return;

		insert_node_row = GTK_CTREE_ROW(insert_node);
		if(insert_node_row == NULL)
		    return;
	    }

	    gtk_clist_freeze(clist);

	    /* Begin copying and inserting the branch on the source
	     * editor to the target editor's layout_ctree.
	     */
	    sibling_branch = (insert_node_row != NULL) ?
		insert_node_row->sibling : NULL;
	    for(i = total_branches - 1; i >= 0; i--)
	    {
		sibling_branch = EditorDNDTrunkListAddIterate(
		    pixmaps_list,
		    editor, src_editor,
		    ctree, src_ctree,
		    NULL,		/* Parent branch */
		    sibling_branch,	/* Insert before this branch */
		    branch[i]		/* Branch to copy and insert */
		);

		/* Add trunk branch to editor's list of layout trunks */
		if(editor->total_layout_trunks < 0)
		    editor->total_layout_trunks = 0;
		n = editor->total_layout_trunks;
		editor->total_layout_trunks = n + 1;
		editor->layout_trunk = (GtkCTreeNode **)g_realloc(
		    editor->layout_trunk,
		    editor->total_layout_trunks * sizeof(GtkCTreeNode *)
		);
		if(editor->layout_trunk == NULL)
		{
		    editor->total_layout_trunks = 0;
		}
		else
		{
		    editor->layout_trunk[n] = sibling_branch;
		}
	    }

	    gtk_clist_thaw(clist);
	}
}

/*
 *	Loads each of the given branches which should come from the
 *	source viewer's index ctree into the target editor's layout ctree
 *	if possible at the specified insert_row position.
 *
 *      The given list of branch pointers will not be modified by this
 *      function.
 */
static void EditorDNDViewerIndexItemLoad(
	editor_struct *editor, viewer_struct *src_v,
	GtkCTreeNode **branch, gint total_branches,
	gint insert_row
)
{
	gint i;
	GtkCList *clist, *src_clist;
	GtkCTree *ctree, *src_ctree;
	viewer_index_item_struct *item;

	if((editor == NULL) || (src_v == NULL) ||
	   (branch == NULL) || (total_branches <= 0)
	)
	    return;

	/* Get pointer to target editor's layout ctree */
	ctree = (GtkCTree *)editor->layout_ctree;
	clist = (GtkCList *)ctree;
	if(ctree == NULL)
	    return;

	/* Get pointer to source viewer's index ctree */
	src_ctree = (GtkCTree *)src_v->index_ctree;
	src_clist = (GtkCList *)src_ctree;
	if(src_ctree == NULL)
	    return;

	/* Clip insert row */
	if(insert_row < 0)
	    insert_row = 0;

	/* Begin inserting */
	if(1)
	{
	    /* Insert at start or in the middle */
	    GtkCTreeNode *insert_node;

	    /* Get branch pointer to insert after (can be NULL) */
	    if(insert_row >= clist->rows)
		insert_node = gtk_ctree_node_nth(
		    ctree, MAX(clist->rows - 1, 0)
		);
	    else
		insert_node = gtk_ctree_node_nth(ctree, insert_row);
	    if(insert_node != NULL)
	    {
		insert_node = EditorItemGetToplevel(editor, insert_node);
		if(insert_node == NULL)
		    return;
	    }

	    /* Load each branch from the source viewer's index ctree
	     * last to first.
	     */
	    for(i = total_branches - 1; i >= 0; i--)
	    {
		if(branch[i] == NULL)
		    continue;

		/* Get the source Viewer's Item */
		item = gtk_ctree_node_get_row_data(
		    src_ctree, branch[i]
		);
		if(item == NULL)
		    continue;

		switch(item->type)
		{
		  case ViewerIndexItemTypeManualPage:
		    EditorFileOpen(
			editor, item->full_path,
			insert_node, FALSE
		    );
		    break;
		  case ViewerIndexItemTypeDirectory:
		  case ViewerIndexItemTypeOtherFile:
		    break;
		}
	    }
	}
}


/*
 *	Sets the drag icon depending on the type of item.
 *
 *	This function is not really used, we're letting the layout ctree
 *	set the icon which it seems to be doing a good job of.
 */
void EditorDNDSetIcon(
	editor_struct *editor, editor_item_struct *item
)
{
	medit_core_struct *core;
	medit_pixmaps_list_struct *pixmaps_list;

	if(editor == NULL)
	    return;

	core = editor->core;
	if(core == NULL)
	    return;

	pixmaps_list = &core->pixmaps_list;

	if(item == NULL)
	{
	
	}
	else
	{
	    GdkPixmap *pixmap = NULL;
	    GdkBitmap *mask = NULL; 

	    switch(item->type)
	    {
	      case EditorItemTypeFile:
/* Check if opened or closed */
		pixmap = pixmaps_list->manual_closed_20x20;
		mask = pixmaps_list->manual_closed_20x20_mask;
		break;
	 
	      case EditorItemTypeHeader:
		pixmap = pixmaps_list->manpage_heading_20x20;
		mask = pixmaps_list->manpage_heading_20x20_mask;
		break;

	      case EditorItemTypeSection:
		pixmap = pixmaps_list->manpage_section_20x20;
		mask = pixmaps_list->manpage_section_20x20_mask;
		break;
 	    }
	    if(pixmap != NULL)
	    {
		gint w = 15, h = 15;
		GdkColormap *cm = gdk_colormap_get_system();

		gdk_window_get_size((GdkWindow *)pixmap, &w, &h);

		/* Need to increase reference count on pixmap and mask
		 * pair since the call to gtk_drag_set_default_icon() will
		 * reduce the reference count by one after one drag and
		 * drop operation has completed.
		 */
		gdk_pixmap_ref(pixmap);
		if(mask != NULL)
		    gdk_bitmap_ref(mask);
		gtk_drag_set_default_icon(
		    cm, pixmap, mask,
		    (gint)(w / 2), (gint)(h / 2)
		);
	    }
	}
}


/*
 *	Editor DND data request callback.
 *
 *	Sends out a command containing the information about
 *	the given editor and selected node.
 * 
 *	The selected branches will have their values applied before
 *	the command which contains the selected branch pointers is sent
 *	out.
 */
void EditorDNDDataRequestCB(
	GtkWidget *widget, GdkDragContext *dc,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data 
)
{
	gchar *buf;
	GtkCTreeNode *branch;
	editor_struct *editor = EDITOR(data);
	if((widget == NULL) || (editor == NULL) || (dc == NULL))
	    return;

	/* Get selected branch on editor's layout ctree.
	 * Note currently we only allow one selected branch at
	 * a time
	 */
	branch = editor->selected_branch;

	/* Need to apply values if this is the selected branch to ensure
	 * we send out a command with a branch pointer who's data is up
	 * to date
	 */
	if(branch == editor->selected_branch)
	{
	    /* Fetch values from the editor's widgets and place them to
	     * the branch data depending on its type. Menus will be
	     * updated
	     */
	    EditorDoApplyValues(editor, branch);
	}

	/* Allocate buffer that we will be sending out, containing
	 * the following command format:
	 *
	 * <editor_ptr> <branch_ptr...>
	 */
	if(branch != NULL)
	    buf = g_strdup_printf(
		"%.8x %.8x",
		(guint32)editor,
		(guint32)branch
	    );
	else
	    buf = g_strdup_printf(
		"%.8x",
		(guint32)editor
	    );

	/* Send out the requested data */
	gtk_selection_data_set(
	    selection_data,
	    GDK_SELECTION_TYPE_STRING,
	    8,			/* 8 bits per character */
	    buf, strlen(buf)
	);

	g_free(buf);
}

/*
 *	Editor DND "drag_data_received" signal callback.
 */
void EditorDNDDataRecievedCB(
	GtkWidget *widget,
	GdkDragContext *dc,
	gint x, gint y,
	GtkSelectionData *selection_data,  
	guint info, guint t,  
	gpointer data
)
{
	gboolean need_delete = FALSE;
	gboolean same;
	GtkWidget *source_widget;
	GtkCList *clist;
	GtkCTree *ctree;
	editor_struct *editor = EDITOR(data);
	if((widget == NULL) || (editor == NULL) || (dc == NULL))
	    return;

	if(selection_data == NULL)
	    return;
	if(selection_data->length < 0)
	    return;

	/* Source and target same? */
	source_widget = gtk_drag_get_source_widget(dc);
	same = (source_widget == widget) ? TRUE : FALSE;

	/* Check if data needs to be deleted by testing if this
	 * drag operation is not a copy
	 */
	if(dc->action != GDK_ACTION_COPY)
	    need_delete = TRUE;

	/* Get clist and ctree from the input widget */
	ctree = ((GTK_IS_CLIST(widget)) ? GTK_CTREE(widget) : NULL);
	clist = (GtkCList *)ctree;
	if(clist != NULL)
	{
	    gint row, column;

	    /* Get row and column position from given DND x and y drop
	     * coordinates.
	     */
	    if(gtk_clist_get_selection_info(
		clist, x, y, &row, &column
	    ))
	    {
/*		row -= 1;	*/	/* Need to offset */
	    }
	    else
	    {
		row = clist->rows;
		column = 0;
	    }
	    if(row < 0)
		row = 0;
	    /* Row is now (or atleast should be) positioned where  
	     * we want to insert
	     */

	    /* Editor layout ctree branch node transfer command? */
	    if(info == MEDIT_DND_TYPE_INFO_EDITOR_BRANCH_CMD)
	    {
		gint i, total_branches = 0;
		editor_struct *src_editor = NULL;
		GtkCTreeNode **branch = NULL;
		gboolean	is_branch_file = FALSE,
				is_branch_header = FALSE,
				is_branch_section = FALSE;

		/* Parse command string, src_editor points to the source
		 * editor and branch and total_branches are the selected
		 * branch(es) on the source editor's layout GtkCTree
		 */
		EditorDNDParseCmd(
		    (const gchar *)selection_data->data,
		    &src_editor,
		    &branch, &total_branches
		);

		/* Is the source editor valid? */
		if(src_editor != NULL)
		{
		    editor_item_struct *item;

		    /* Check what types of branch item data types we
		     * just got
		     */
		    for(i = 0; i < total_branches; i++)
		    {
			item = EditorBranchGetData(
			    GTK_CTREE(src_editor->layout_ctree),
			    branch[i]
			);
			if(item == NULL)
			    continue;

			/* Check the item's type */
			switch(item->type)
			{
			  case EditorItemTypeFile:
			    is_branch_file = TRUE;
			    break;

			  case EditorItemTypeHeader:
			    is_branch_header = TRUE;
			    break;

			  case EditorItemTypeSection:
			    is_branch_section = TRUE;
			    break;
			}
		    }
		    /* Did we get a bad combination of types? */
		    if(is_branch_file &&
		       (is_branch_header || is_branch_section)
		    )
		    {
			CDialogGetResponse(
			    "Invalid Operation",
"Unable to drag and drop both file reference and sections\n\
data types at once.",
			    NULL,
			    CDIALOG_ICON_WARNING,
			    CDIALOG_BTNFLAG_OK,
			    CDIALOG_BTNFLAG_OK
			);

			/* Delete the branches list */
			g_free(branch);

			return;
		    }
		    /* `No operation' check */
		    if(same && (total_branches == 1))
		    {
			GtkCTreeNode *insert_row_node = gtk_ctree_node_nth(
			    ctree, CLIP(row, 0, clist->rows - 1)
			);

			/* Drag and drop on same? */
			if(branch[0] == insert_row_node)
			{
			    g_free(branch);
			    return;
			}
		    }

		    /* ************************************************ */
		    /* Handle by branch item data type */
		    /* File (which implies toplevel trunk branch)? */
		    if(is_branch_file)
		    {
			/* Add each toplevel trunk branch to the target
			 * editor.
			 */
			EditorDNDTrunkListAdd(
			    editor, src_editor,
			    branch, total_branches,
			    row
			);

			/* Delete branches that came from source editor
			 * as needed.
			 */
			if(need_delete)
			{
			    /* Delete branches that came from source
			     * editor, the given pointer array will be
			     * deallocated as well.
			     */
			    EditorDNDDoDeleteItems(
				src_editor, branch, total_branches
			    );
			    branch = NULL;
			    total_branches = 0;
			}

			/* Update column widths and menus */
			gtk_clist_set_column_width(
			    GTK_CLIST(ctree),
			    0,			/* Column */
			    gtk_clist_optimal_column_width(
				GTK_CLIST(ctree), 0
			    )
			);
			EditorUpdateMenus(editor);

			/* If not same, update source editor's
			 * column widths and menus as well.
			 */
			if(!same)
			{
			    gtk_clist_set_column_width(
				GTK_CLIST(src_editor->layout_ctree),
				0,		/* Column */
				gtk_clist_optimal_column_width(
				    GTK_CLIST(src_editor->layout_ctree), 0
				)
			    );
			    EditorUpdateMenus(src_editor);
			}
		    }
		    /* ************************************************ */
		    /* Header or section? */
		    else if(is_branch_header || is_branch_section)
		    {
			editor_item_struct **new_item;

			/* Copy new items from source editor */
			gint total_new_items = total_branches;
			if(total_new_items > 0)
			    new_item = (editor_item_struct **)g_malloc0(
				total_new_items * sizeof(editor_item_struct *)
			    );
			else
			    new_item = NULL;
			if(new_item == NULL)
			{
			    total_new_items = 0;
			}
			else
			{
			    /* Make duplicates for each new item */
			    for(i = 0; i < total_new_items; i++)
			    {
				item = EditorBranchGetData(
				    (GtkCTree *)src_editor->layout_ctree,
				    branch[i]
				);
				if(item != NULL)
				{
				    /* Duplicate item */
				    new_item[i] = EditorItemDup(item);
				}
			    }
			}

			/* Insert new items to target editor, new items
			 * will be transfered and should no longer be
			 * referenced after this call (including the
			 * pointer array).
			 */
			EditorDNDItemListAdd(
			    editor,
			    new_item, total_new_items,
			    row
			);
			new_item = NULL;
			total_new_items = 0;

			/* Delete branches that came from source editor
			 * as needed.
			 */
			if(need_delete)
			{
			    /* Delete branches that came from source
			     * editor, the given pointer array will be
			     * deallocated as well.
			     */
			    EditorDNDDoDeleteItems(
				src_editor, branch, total_branches
			    );
			    branch = NULL;
			    total_branches = 0;
			}

			/* Update column widths and menus */
			gtk_clist_set_column_width(
			    GTK_CLIST(ctree),
			    0,			/* Column */
			    gtk_clist_optimal_column_width(
				GTK_CLIST(ctree), 0
			    )
			);
			EditorUpdateMenus(editor);

			/* If not same, update source editor's
			 * column widths and menus as well.
			 */
			if(!same)
			{
			    gtk_clist_set_column_width(
				GTK_CLIST(src_editor->layout_ctree),
				0,			/* Column */
				gtk_clist_optimal_column_width(
				    GTK_CLIST(src_editor->layout_ctree), 0
				)
			    );
			    EditorUpdateMenus(src_editor);
			}
		    }
		}

		/* Delete the branches list from the source editor */
		g_free(branch);
	    }
	    /* ******************************************************** */
	    /* Viewer index ctree branch node transfer command? */
	    else if(info == MEDIT_DND_TYPE_INFO_VIEWER_BRANCH_CMD)
	    {
		viewer_struct *src_viewer = NULL;
		GtkCTreeNode **branch = NULL;
		gint total_branches = 0;

		/* Parse command string. Value of src_viewer points to
		 * the source viewer and branch and total_branches are
		 * the selected branch(es) on the source viewer's index
		 * ctree.
		 */
		ViewerIndexCTreeDNDParseCmd(
		    (const gchar *)selection_data->data,
		    &src_viewer,
		    &branch, &total_branches
		);

		/* Is source viewer valid? */
		if((src_viewer != NULL) ?
		    (src_viewer->index_ctree != NULL) : FALSE
		)
		{
		    /* Iterate through each selected branch from source
		     * viewer and load then into the target editor
		     */
		    EditorDNDViewerIndexItemLoad(
			editor, src_viewer,
			branch, total_branches,
			row
		    );

		    /* Delete branches that came from source editor
		     * as needed.
		     */
		    if(need_delete)
		    {
			/* Do not delete branches on source viewer's index
			 * ctree.
			 */
		    }
		}

		/* Delete the branches list from the source viewer */
		g_free(branch);
	    }
	    /* Other standard DND target types? */
	    else if((info == MEDIT_DND_TYPE_INFO_TEXT_PLAIN) ||
		    (info == MEDIT_DND_TYPE_INFO_TEXT_URI_LIST) ||
		    (info == MEDIT_DND_TYPE_INFO_STRING)
	    )
	    {
		gchar *path = EditorDNDParseURL(
		    selection_data->data, selection_data->length
		);
		EditorFileOpen(editor, path, editor->selected_branch, FALSE);
		g_free(path);
	    }
 
	    /* ******************************************************** */ 
	    /* Add support for other types of DND here */



	}
}

/*
 *	Procedure to delete the specified branches on the given
 *	editor.
 *
 *	The given array of branch pointers will be deallocated as
 *	well.
 */
static void EditorDNDDoDeleteItems(
	editor_struct *editor,
	GtkCTreeNode **branch, gint total_branches
)
{
	gint i;
	GtkCList *clist;
	GtkCTree *ctree;
	gpointer *data_list;
	GtkCTreeNode *branch_ptr;
	if((editor == NULL) || (branch == NULL))
	    return;

	if(total_branches <= 0)
	{
	    g_free(branch);
	    return;
	}

	ctree = (GtkCTree *)editor->layout_ctree;
	clist = (GtkCList *)ctree;
	if(ctree == NULL)
	{
	    g_free(branch);
	    return;
	}


	/* Make a local list of item data pointers before we start
	 * deleting branches
	 */
	data_list = (gpointer *)g_malloc0(
	    total_branches * sizeof(gpointer)
	);
	if(data_list == NULL)
	{
	    g_free(branch);
	    return;
	}
	/* Copy item data pointers from given list of branches */
	for(i = 0; i < total_branches; i++)
	    data_list[i] = (gpointer)EditorBranchGetData(
		ctree, branch[i]
	    );


	/* Match and delete each branch node on the layout ctree by
	 * using the item data list as reference. This way
	 * gtk_ctree_find_by_row_data() will be able to make matches to
	 * only those nodes that actually exist.
	 */
	for(i = 0; i < total_branches; i++)
	{
	    branch_ptr = gtk_ctree_find_by_row_data(
		ctree, NULL, data_list[i]
	    );
	    if(branch_ptr == NULL)
		continue;

	    /* Mark this branch node as having changes, this way its
	     * toplevel trunk will be marked as having changes.
	     */
	    EditorItemSetToplevelHasChanges(
		editor, branch_ptr, TRUE
	    );

	    /* Remove this branch node from the ctree and all subsiquent
	     * child branches if any.
	     *
	     * Editor's reference to the selected branch and layout_trunks
	     * list will be updated as needed when the destroy notify
	     * callback is called upon deleting this branch node.
	     */
	    gtk_ctree_remove_node(ctree, branch_ptr);
	}


	/* Delete local item data list */
	g_free(data_list);

	/* Delete list of branch pointers */
	g_free(branch);
}

/*
 *	Editor DND data delete callback.
 *
 *	This deletes the selected item on editor's layout ctree.
 */
void EditorDNDDataDeleteCB(
	GtkWidget *widget, GdkDragContext *dc, gpointer data
)
{

}
