/**
 *
 * $Id: DropTrans.c,v 1.3 1997/04/02 03:40:40 miers Exp $
 *
 * Copyright (C) 1995 Free Software Foundation, Inc.
 *
 * This file is part of the GNU LessTif Library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 **/

static char rcsid[] = "$Id: DropTrans.c,v 1.3 1997/04/02 03:40:40 miers Exp $";

#include <LTconfig.h>
#include <XmI/XmI.h>

#include <X11/Xfuncs.h>

#include <Xm/XmP.h>
#include <Xm/AtomMgr.h>
#include <Xm/DropTransP.h>
#include <Xm/DragCP.h>
#include <XmI/DragDropI.h>
#include <XmI/AtomMgrI.h>
#include <Xm/DisplayP.h>
#include <string.h>

#include <XmI/DebugUtil.h>

static void class_initialize();
static void class_part_initialize(WidgetClass w_class);
static void initialize(Widget request, Widget new_w, ArgList args, Cardinal *num_args);
static void destroy(Widget w);
static Boolean set_values(Widget current, Widget request, Widget new_w, ArgList args, Cardinal *num_args);

static void
select_callback(Widget w, XtPointer cd, Atom *select, Atom *type,
		XtPointer value, unsigned long *len, int *fmt);

static Widget start_drop_transfer(Widget refWidget,
				  ArgList args, Cardinal argCount);
static void add_drop_transfer(Widget widget,
			      XmDropTransferEntry transfers,
			      Cardinal num_transfers);

#define Offset(field) XtOffsetOf(XmDropTransferRec, dropTransfer.field)
static XtResource resources[] = {
    {
	XmNdropTransfers, XmCDropTransfers, XmRDropTransfers,
	sizeof(XmDropTransferEntry), Offset(drop_transfers),
	XmRImmediate, (XtPointer)NULL
    },
    {
	XmNnumDropTransfers, XmCNumDropTransfers, XmRCardinal,
	sizeof(Cardinal), Offset(num_drop_transfers),
	XmRImmediate, (XtPointer)0
    },
    {
	XmNincremental, XmCIncremental, XmRBoolean,
	sizeof(Boolean), Offset(incremental),
	XmRImmediate, (XtPointer)False
    },
    {
	XmNtransferProc, XmCTransferProc, XmRCallbackProc,
	sizeof(XtSelectionCallbackProc), Offset(transfer_callback),
	XmRImmediate, (XtPointer)NULL
    },
    {
	XmNtransferStatus, XmCTransferStatus, XmRTransferStatus,
	sizeof(unsigned char), Offset(transfer_status),
	XmRImmediate, (XtPointer)XmTRANSFER_SUCCESS
    }
};

static XmBaseClassExtRec _XmDropTransferObjectClassExtRec = {
    /* next_extension            */ NULL,
    /* record_type               */ NULLQUARK,                             
    /* version                   */ XmBaseClassExtVersion,
    /* size                      */ sizeof(XmBaseClassExtRec),
    /* initialize_prehook        */ NULL, /* FIXME */
    /* set_values_prehook        */ NULL, /* FIXME */
    /* initialize_posthook       */ NULL, /* FIXME */
    /* set_values_posthook       */ NULL, /* FIXME */
    /* secondary_object_class    */ NULL, /* FIXME */
    /* secondary_object_create   */ NULL, /* FIXME */
    /* get_secondary_resources   */ NULL, /* FIXME */
    /* fast_subclass             */ { 0 }, /* FIXME */
    /* get_values_prehook        */ NULL, /* FIXME */
    /* get_values_posthook       */ NULL, /* FIXME */
    /* class_part_init_prehook   */ NULL, /* FIXME */
    /* class_part_init_posthook  */ NULL, /* FIXME */
    /* ext_resources             */ NULL, /* FIXME */
    /* compiled_ext_resources    */ NULL, /* FIXME */
    /* num_ext_resources         */ 0, /* FIXME */
    /* use_sub_resources         */ False, /* FIXME */
    /* widget_navigable          */ NULL, /* FIXME */
    /* focus_change              */ NULL, /* FIXME */
    /* wrapper_data              */ NULL
};

XmDropTransferClassRec xmDropTransferClassRec = {
    /* Object class part */
    {
	/* superclass            */ (WidgetClass) &objectClassRec,
        /* class_name            */ "XmDropTransferObject",
	/* widget_size           */ sizeof(XmDropTransferRec),
	/* class_initialize      */ class_initialize,
	/* class_part_initialize */ class_part_initialize,
	/* class_inited          */ False,
	/* initialize            */ initialize,
	/* initialize_hook       */ NULL,
	/* obj1                  */ NULL,
	/* obj2                  */ NULL,
	/* obj3                  */ 0,
	/* resources             */ resources,
	/* num_resources         */ XtNumber(resources),
	/* xrm_class             */ NULLQUARK,
	/* obj4                  */ 0,
	/* obj5                  */ 0,
	/* obj6                  */ 0,
	/* obj7                  */ 0,
	/* destroy               */ destroy,
	/* obj8                  */ NULL,
	/* obj9                  */ NULL,
	/* set_values            */ set_values,
	/* set_values_hook       */ NULL,
	/* obj10                 */ NULL,
	/* get_values_hook       */ NULL,
	/* obj11                 */ NULL,
	/* version               */ XtVersion,
	/* callback offsets      */ NULL,
	/* obj12                 */ NULL,
	/* obj13                 */ NULL,
        /* obj14                 */ NULL,
	/* extension             */ NULL /*(XtPointer)&_XmDropTransferObjectClassExtRec*/
    },
    /* XmDropTranferObject part */
    {
        /* start_drop_transfer*/ start_drop_transfer,
        /* add_drop_transfer  */ add_drop_transfer,
        /* extension          */ NULL
    }
};

WidgetClass xmDropTransferObjectClass = (WidgetClass)&xmDropTransferClassRec;

#ifndef XmUNSPECIFIED
#define XmUNSPECIFIED (~0)
#endif

static void
class_initialize()
{
    XdbDebug(__FILE__, NULL, "DropTransfer class initialize\n");

    _XmDropTransferObjectClassExtRec.record_type = XmQmotif;
}

static void
class_part_initialize(WidgetClass widget_class)
{
    WidgetClass sc;

    XdbDebug(__FILE__, NULL, "DropTransfer class part initialize\n");

    _XmFastSubclassInit(widget_class, XmDROP_TRANSFER_BIT);

    sc = widget_class->core_class.superclass;

    if (DTC_StartTransferProc(widget_class) == XmInheritStartTransferProc)
	DTC_StartTransferProc(widget_class) = DTC_StartTransferProc(sc);

    if (DTC_AddTransferProc(widget_class) == XmInheritAddTransferProc)
	DTC_AddTransferProc(widget_class) = DTC_AddTransferProc(sc);
}

static void
initialize(Widget request,
	   Widget new_w,
	   ArgList args,
	   Cardinal *num_args)
{
    XmDropTransferEntry entries;

    XdbDebug(__FILE__, new_w, "DropTransfer initialize\n");

    if (DT_NumDropTransfers(new_w) != 0) {

	/* Copy the user specified list */
	DT_NumDropTransferLists(new_w) = 1;

	DT_DropTransferLists(new_w) =
		(XmDropTransferList)XtMalloc(sizeof(XmDropTransferListRec));

	entries = (XmDropTransferEntry)XtMalloc(DT_NumDropTransfers(new_w) *
						sizeof(XmDropTransferEntryRec));

	bcopy(DT_DropTransfers(new_w), entries,
	      DT_NumDropTransfers(new_w) * sizeof(XmDropTransferEntryRec));

	DT_DropTransferLists(new_w)[0].transfer_list = entries;
	DT_DropTransferLists(new_w)[0].num_transfers =
						DT_NumDropTransfers(new_w);

	DT_DropTransfers(new_w) = entries;
    }
    else {
	DT_DropTransferLists(new_w) = NULL;
	DT_NumDropTransferLists(new_w) = 0;
    }

    DT_MotifDropAtom(new_w) = XmInternAtom(XtDisplay(new_w),
					   _XA_MOTIF_DROP,
					   False);

    DT_CurDropTransferList(new_w) = XmUNSPECIFIED;
    DT_CurXfer(new_w) = XmUNSPECIFIED;

    DT_CurTargets(new_w) = NULL;
    DT_CurClientData(new_w) = NULL;
}

static void
destroy(Widget w)
{
    Widget dc;
    int i;

    XdbDebug(__FILE__, w, "DropTransfer destroy\n");

    dc = XmGetDragContext(w, DT_Timestamp(w));

    if (dc && DC_SourceIsExternal(dc))
	XtDestroyWidget(dc);

    for (i = 0; i < DT_NumDropTransferLists(w); i++)
	XtFree((char *)DT_DropTransferLists(w)[i].transfer_list);

    XtFree((char *)DT_DropTransferLists(w));
}

static Boolean
set_values(Widget current,
	   Widget request,
	   Widget new_w,
	   ArgList args,
	   Cardinal *num_args)
{
    XdbDebug(__FILE__, new_w, "DropTransfer set_values\n");

    return True;
}

static void
entry_transfer(Widget dt, int which)
{
    XmDropTransferList lst = &DT_DropTransferLists(dt)[which];
    Widget dc = DT_DragContext(dt);
    int i;

    DT_CurDropTransferList(dt) = which;
    DT_CurTargets(dt) = (Atom *)XtMalloc(lst->num_transfers * sizeof(Atom));
    DT_CurClientData(dt) = (XtPointer)XtMalloc(lst->num_transfers *
						 sizeof(XtPointer));

    for (i = 0; i < lst->num_transfers; i++) {
	DT_CurTargets(dt)[i] = lst->transfer_list[i].target;
	DT_CurClientData(dt)[i] = (XtPointer)dt;
    }

    DT_CurXfer(dt) = 0;

    if (DT_Incremental(dt))
	XtGetSelectionValuesIncremental(DC_CurrReceiverInfo(dc)->shell,
					DC_ICCHandle(dc),
					DT_CurTargets(dt),
					lst->num_transfers, select_callback,
					(XtPointer)DT_CurClientData(dt),
					DT_Timestamp(dt));
    else
	XtGetSelectionValues(DC_CurrReceiverInfo(dc)->shell,
			     DC_ICCHandle(dc),
			     DT_CurTargets(dt),
			     lst->num_transfers, select_callback,
			     (XtPointer)DT_CurClientData(dt),
			     DT_Timestamp(dt));
}

static void
notified_callback(Widget w, XtPointer cd, Atom *select, Atom *type,
		  XtPointer value, unsigned long *len, int *fmt)
{
    if (value != NULL)
	XtFree((char *)value);

    XtDestroyWidget((Widget)cd);
}

static void
terminate_transfer(Widget dt, Atom *select)
{
    Atom how;

    if (DT_TransferStatus(dt) == XmTRANSFER_SUCCESS)
	how = XmInternAtom(XtDisplay(dt), _XA_XmTRANSFER_SUCCESS, False);
    else
	how = XmInternAtom(XtDisplay(dt), _XA_XmTRANSFER_FAILURE, False);

    XtGetSelectionValue(DC_CurrReceiverInfo(DT_DragContext(dt))->shell,
			*select, how, notified_callback, (XtPointer)dt,
			DT_Timestamp(dt));
}

static void
select_callback(Widget w, XtPointer cd, Atom *select, Atom *type,
		XtPointer value, unsigned long *len, int *fmt)
{
    Widget dt = (Widget)cd;
    XmDropTransferList lst;

    lst = &DT_DropTransferLists(dt)[DT_CurDropTransferList(dt)];

    if (DT_TransferCallback(dt))
	(*DT_TransferCallback(dt))(dt,
				   lst->transfer_list[DT_CurXfer(dt)].client_data,
				   select, type, value, len, fmt);

    lst = &DT_DropTransferLists(dt)[DT_CurDropTransferList(dt)];

    if (!DT_Incremental(dt) ||
	(DT_Incremental(dt) && value != NULL && *len == 0)) {

	DT_CurXfer(dt)++;

	if (DT_CurXfer(dt) == lst->num_transfers) {

	    XtFree((char *)DT_CurTargets(dt));
	    XtFree((char *)DT_CurClientData(dt));

	    DT_CurDropTransferList(dt)++;

	    if (DT_CurDropTransferList(dt) < DT_NumDropTransferLists(dt))
		entry_transfer(dt, DT_CurDropTransferList(dt));
	    else
		terminate_transfer(dt, select);
	}
    }
}

static void
drop_timer(XtPointer cd, XtIntervalId *id)
{
    Widget dt = (Widget)cd;
    Atom select;

    if (DT_NumDropTransferLists(dt))
	entry_transfer(dt, 0);
    else {
	select = DC_ICCHandle(DT_DragContext(dt));

	terminate_transfer(dt, &select);
    }
}

static Widget
start_drop_transfer(Widget refWidget,
		  ArgList args, Cardinal argCount)
{
    Widget disp = XmGetXmDisplay(XtDisplay(refWidget)), dt;

    dt = XtCreateWidget("drop_transfer",
			Display_DropTransferClass(disp),
			disp, args, argCount);

    /* refWidget had BETTER be a DC */
    DT_DragContext(dt) = refWidget;
    DT_Timestamp(dt) = DC_DragFinishTime(refWidget);

    XtAppAddTimeOut(XtWidgetToApplicationContext(dt), 0,
		    drop_timer, (XtPointer)dt);
    return dt;
}

static void
add_drop_transfer(Widget widget,
		  XmDropTransferEntry transfers,
		  Cardinal num_transfers)
{
    XmDropTransferEntry entries;
    int idx;

    idx = DT_NumDropTransferLists(widget);
    DT_NumDropTransferLists(widget)++;
    DT_DropTransferLists(widget) =
	(XmDropTransferList)XtRealloc((char *)DT_DropTransferLists(widget),
					DT_NumDropTransferLists(widget) *
					sizeof(XmDropTransferListRec));

    entries = (XmDropTransferEntry)XtMalloc(num_transfers *
						sizeof(XmDropTransferEntryRec));
    bcopy(transfers, entries, num_transfers * sizeof(XmDropTransferEntryRec));

    DT_DropTransferLists(widget)[idx].transfer_list = entries;
    DT_DropTransferLists(widget)[idx].num_transfers = num_transfers;
}
