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

#include "../include/string.h"
#include "../include/strexp.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_ptr,
	GtkCTreeNode ***branch, gint *total_branches
);
gchar *EditorDNDParseURL(
	const gchar *string, 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_ptr,
	GtkCTreeNode ***branch, gint *total_branches
)
{
	guint32 addr_val;
	gint i, n, strc;
	gchar **strv;

	/* Reset returns */
	if(editor_ptr != NULL)
	    *editor_ptr = NULL;
	if(branch != NULL)
	    *branch = NULL;
	if(total_branches != NULL)
	    *total_branches = 0;

	if(string == NULL)
	    return;

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

	/* Parse command, format is as follows:
	 * <editor_ptr> <branch_ptr...>
	 */
	strv = strexp(string, &strc);
	if(strv == NULL)
	    return;

	/* Get editor_ptr (format in hex with no "0x" prefix) */
	if(strc > 0)
	    i = sscanf(strv[0], "%x", (guint32 *)&addr_val);
	else
	    i = 0;
	if((i > 0) && (editor_ptr != NULL))
	    *editor_ptr = EDITOR(addr_val);

	/* Get list of branch pointers which should be branches
	 * on the parsed editor's layout ctree
	 */
	if((branch != NULL) && (total_branches != NULL))
	{
	    const gchar *cs;

	    /* Start at parsed argument number 1 */
	    for(i = 1; i < strc; i++)
	    {
		cs = (const gchar *)strv[i];
		if(cs == NULL)
		    continue;

		n = (*total_branches);
		(*total_branches) = n + 1;
		(*branch) = (GtkCTreeNode **)realloc(
		    *branch,
		    (*total_branches) * sizeof(GtkCTreeNode *)
		);
		if((*branch) == NULL)
		{
		    (*total_branches) = 0;
		    break;
		}
		else
		{
		    i = sscanf(cs, "%x", (guint32 *)&addr_val);
		    if(i > 0)
			(*branch)[n] = (GtkCTreeNode *)addr_val;
		    else
			(*branch)[n] = NULL;
		}
	    }
	}

	/* Free exploded string */
	strlistfree(strv, strc);
}

/*
 *	Returns a dynamically allocated string containing the
 *	file name portion of the given url string.
 */
gchar *EditorDNDParseURL(
	const gchar *string, gint len
)
{
	const gchar *cs, *cs2;


	if(string == NULL)
	    return(NULL);

	cs = strstr(string, "://");
	if(cs == NULL)
	    cs = string;
	else
	    cs += strlen("://");

	cs2 = strchr(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;
	medit_pixmaps_list_struct *pixmaps_list;
	GdkPixmap *closed_p = NULL, *opened_p = NULL;
	GdkBitmap *closed_m = NULL, *opened_m = NULL;
	medit_core_struct *core_ptr = MEDIT_CORE(editor->core_ptr);
	GtkCTree *ctree = (GtkCTree *)editor->layout_ctree;
	if((core_ptr == NULL) || (ctree == NULL))
	{
	    EditorItemDelete(item);
	    return(NULL);
	}

	pixmaps_list = &core_ptr->pixmaps_list;

	/* Reset name for new branch, it'll be picked below */
	text[0] = NULL;

	/* Item data valid? */
	if(item != NULL)
	{
	    /* Handle by 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 */

/* Need to work on this, currently we can't copy items of type
 * EditorItemTypeFile. They will just be swaped.
 */
g_printerr("Cannot copy item of type EditorItemTypeFile yet.\n");

	    }
	}

	/* 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;
	}

	/* Get pointer to layout ctree */
	ctree = (GtkCTree *)editor->layout_ctree;
	clist = (GtkCList *)ctree;
	if(ctree == NULL)
	{
	    for(i = 0; i < total_items; i++)
		EditorItemDelete(item[i]);
	    g_free(item);
	    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;
	    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;
	    }
	    else
	    {
		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 after this node, so we pick this node's
		 * sibling. The sibling may be NULL in which we
		 * insert at the end.
		 */
		parent_node = insert_node_row->parent;
		insert_node = insert_node_row->sibling;	/* New insert node */
		/* 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 item list */
	g_free(item);
	item = NULL;
	total_items = 0;
}


/*
 *	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) ? 0 : (branch_row->children != NULL))
	{
	    /* 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_ptr;
	medit_pixmaps_list_struct *pixmaps_list;


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

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

	/* Get pointer to layout ctree on source editor */
	src_ctree = (GtkCTree *)src_editor->layout_ctree;
	src_clist = (GtkCList *)src_ctree;
	if(src_ctree == NULL)
	    return;

	/* Get pointer to core structure on target editor */
	core_ptr = MEDIT_CORE(editor->core_ptr);
	if(core_ptr != NULL)
	    pixmaps_list = &core_ptr->pixmaps_list;
	else
	    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;
	    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) ?
		NULL : insert_node_row->sibling
	    );
	    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 **)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:
		    EditorFileLoad(
			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_ptr;
	medit_pixmaps_list_struct *pixmaps_list;

	if(editor == NULL)
	    return;

	core_ptr = MEDIT_CORE(editor->core_ptr);
	if(core_ptr == NULL)
	    return;

	pixmaps_list = &core_ptr->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 
)
{
	gboolean data_sent = FALSE;
	gchar *buf;
	GtkCTreeNode *branch_ptr;
	editor_struct *editor = EDITOR(data);
	if((widget == NULL) || (editor == NULL) || (dc == NULL))
	    return;

	if(!editor->initialized)
	    return;

	if(widget != editor->layout_ctree)
	{
	    g_printerr(
		"EditorDNDDataRequestCB(): widget != editor->layout_ctree\n"
	    );
	    return;
	}

	/* Get selected branch on editor's layout ctree.
	 * Note currently we only allow one selected branch at
	 * a time
	 */
	branch_ptr = 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_ptr == 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_ptr);
	}

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

	/* Send out data */
	gtk_selection_data_set(
	    selection_data,
	    GDK_SELECTION_TYPE_STRING,
	    8,			/* 8 bits per character */
	    buf, strlen(buf)
	);
	data_sent = TRUE;

	g_free(buf);


	/* Failed to send out data? */
	if(!data_sent)
	{
	    const gchar *s = "Error";

	    gtk_selection_data_set(
		selection_data,
		GDK_SELECTION_TYPE_STRING,
		8,      /* 8 bits per character */
		s, strlen(s)
	    );
	    data_sent = TRUE;
	}
}

/*
 *	Editor DND data recieve callback.
 *
 *	Parses the command recieved for item move or copy.
 *
 *	Note that data deletion will be handled here.
 */
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(!editor->initialized)
	    return;

	/* Important, check if we got data */
	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 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(
			    (GtkCTree *)src_editor->layout_ctree, branch[i]
			);
			if(item == NULL)
			    continue;

			/* Check item 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!",
"Cannot drag and drop both file reference and sectional\n\
data types at once.",
"You cannot drag and drop items of type file reference\n\
and sectional data (ie header and section types) at once.\n\
You need to select only one or a group of source items of\n\
the same type to drag and drop.",
			    CDIALOG_ICON_ERROR,
			    CDIALOG_BTNFLAG_OK | CDIALOG_BTNFLAG_HELP,
			    CDIALOG_BTNFLAG_OK
			);

			/* Free only list of branches from source editor */
			g_free(branch);
			branch = NULL;
			total_branches = 0;

			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)
			{
			    /* Free only list of branches from source editor */
			    g_free(branch);
			    branch = NULL;
			    total_branches = 0;

			    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(
			    (GtkCList *)ctree,
			    0,			/* Column */
			    gtk_clist_optimal_column_width(
				(GtkCList *)ctree, 0
			    )
			);
			EditorUpdateMenus(editor);

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

			/* Copy new items from source editor */
			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(
			    (GtkCList *)ctree,
			    0,			/* Column */
			    gtk_clist_optimal_column_width(
				(GtkCList *)ctree, 0
			    )
			);
			EditorUpdateMenus(editor);

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

		/* Free only list of branches from source editor */
		g_free(branch);
		branch = NULL;
		total_branches = 0;
	    }
	    /* ******************************************************** */
	    /* 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.
			 */
		    }
		}

		/* Free only list of branches from source viewer's
		 * index ctree.
		 */
		g_free(branch);
		branch = NULL;
		total_branches = 0;
	    }
	    /* 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
		);
		EditorFileLoad(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
)
{

}
