/*****************************************************************************
 *****************************************************************************
 **
 **   File:         buff.c
 **
 **   Description:  Buffer transfer functions for the CDE Drag & Drop Demo.
 **
 **  (c) Copyright 1993, 1994 Hewlett-Packard Company
 **  (c) Copyright 1993, 1994 International Business Machines Corp.
 **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
 **  (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of
 **      Novell, Inc.
 **
 ****************************************************************************
 ************************************<+>*************************************/

#include <time.h>

#include <X11/Intrinsic.h>

#include <Xm/Xm.h>
#include <Xm/List.h>

#include <Dt/Dt.h>
#include <Dt/Dnd.h>

#include "icon.h"
#include "file.h"
#include "demo.h"
#include "buff.h"

extern Widget	topLevel;

 /*************************************************************************
 * 
 *	Data Structures & Private Declarations For Appointment Buffers
 * 
 **************************************************************************/

/*
 * Appointment structure used to store appointments internally.
 */

typedef struct _Appointment {
        char           *date;
        char           *start;
        char           *end;
        char           *what;
} Appointment;

/*
 * List of appointments which always appear to be for the current date.
 */

char    today[9];       /* initialized in apptCreateList() */

Appointment todaysApptList[] = {
#ifdef TEST_APPT_NAMES
        { today, "", "", "Staff Meeting" },		  /* no name */
        { today, " 9:30am", "10:00am", "Will's Party1" }, /* duplicate names */
        { today, " 9:30am", "10:00am", "Will's Party2" },
#else
        { today, " 9:00am", " 9:30am", "Staff Meeting" },
        { today, " 9:30am", "10:00am", "Will's Party" },
#endif
        { today, "10:00am", "10:30am", "Conference Call" },
        { today, "10:30am", "11:30am", "Work on Mail" },
        { today, "11:00am", "11:30pm", "B'fast w/ Robert" },
        { today, " 1:30pm", " 2:30pm", "Design Meeting" },
        { today, " 3:00pm", " 4:00pm", "Communications" },
        { today, " 4:00pm", " 4:30pm", "Pick up Dogs" },
        { today, " 5:00pm", " 6:30pm", "Beer Bust" },
        { today, " 7:00pm", " 9:00pm", "Dinner - Stuart" },
        { NULL, NULL, NULL, NULL }
};

/*
 * CDE appointment format used to transfer the appointments via drag & drop.
 */

char *apptFormat =
"   ** Calendar Appointment **\n\
\n\
        Date:    %s\n\
        Start:   %s\n\
        End:     %s\n\
        What:    %s\n";

/*
 * Private Appointment Buffer Function Declarations
 */

static void		apptConvertCallback(Widget, XtPointer, XtPointer);
static int		apptCreateList(XmString**);
static void		apptDestroyList(XmString*,int);
static Appointment*	apptFromListEntry(XmString);
static char*		apptGetLabel(char*);

 /*************************************************************************
 * 
 *	Appointment Drag & Drop
 * 
 **************************************************************************/

/*
 * apptConvertCallback
 *
 * Fills in buffer structure with calendar appointment string based on which
 * appointments are selected in the scrolled list when the drag is started.
 * ihen no appointments are selected, the appointment under the pointer is
 * used. Supply a label for the calendar appointment based on the contents
 * of the appointment.
 */
static void
apptConvertCallback(
        Widget          dragContext,
        XtPointer       clientData,
        XtPointer       callData)
{
        DtDndConvertCallbackStruct *convertInfo =
                                  (DtDndConvertCallbackStruct *) callData;
        DtDndBuffer     *buffers = convertInfo->dragData->data.buffers;
        Widget          apptList = (Widget)clientData;
        int             selectedPos, ii,
                        selectedItemCount;
        XmStringTable   selectedItems;
        char            apptString[1024];
        String          labelString;
        Appointment     *appt;

	if (convertInfo == NULL) {
		return;
	}

        /*
         * Verify the validity of the callback reason
         */

        if (convertInfo->dragData->protocol != DtDND_BUFFER_TRANSFER ||
	    (convertInfo->reason != DtCR_DND_CONVERT_DATA &&
             convertInfo->reason != DtCR_DND_CONVERT_DELETE)) {
                return;
        }

        /*
         * Get selected items from the list
         */

        XtVaGetValues(apptList,
                XmNuserData,            &selectedPos,
                XmNselectedItemCount,   &selectedItemCount,
                XmNselectedItems,       &selectedItems,
                NULL);

        for (ii = 0; ii < convertInfo->dragData->numItems; ii++) {

                /*
                 * Get the actual appointment(s)
                 */

                if (selectedItemCount == 0) {
                        appt = &todaysApptList[selectedPos-1+ii];
                } else {
                        appt = apptFromListEntry(selectedItems[ii]);
                }
                sprintf(apptString, apptFormat,
			appt->date, appt->start, appt->end, appt->what);

                /*
                 * Supply the appointment(s) for transfer
                 */

                if (convertInfo->reason == DtCR_DND_CONVERT_DATA) {

                        /* Copy the appointment into the buffer for transfer */

                        buffers[ii].bp = XtNewString(apptString);
                        buffers[ii].size = strlen(buffers[ii].bp);

                        /* Supply the name for the appointment */

                        labelString = apptGetLabel(apptString);
			if (labelString == NULL) {
                        	buffers[ii].name = (char *)NULL;
			} else {
                        	buffers[ii].name = XtNewString(labelString);
                        	XtFree(labelString);
			}

                /*
                 * Delete the moved appointment(s)
                 */

                } else if (convertInfo->reason == DtCR_DND_CONVERT_DELETE) {
                        printf("Delete appointment for %s\n", appt->what);
                }
        }
}

/*
 * apptDragFinishCallback
 *
 * Free buffer data/names allocated in apptConvertCallback()
 */
void
apptDragFinishCallback(
        Widget          widget,
        XtPointer       clientData,
        XtPointer       callData)
{
	DtDndDragFinishCallbackStruct *dropFinishInfo =
				(DtDndDragFinishCallbackStruct *)callData;
	DtDndContext	*dragData = dropFinishInfo->dragData;
	int		ii;

	for (ii = 0; ii < dragData->numItems; ii++) {
		XtFree(dragData->data.buffers[ii].bp);
		if (dragData->data.buffers[ii].name != NULL)
			XtFree(dragData->data.buffers[ii].name);
	}
}

/*
 * apptTransferCallback
 *
 * Handles the transfer of an appointment to the draw area drop site.
 * Adds the appropriate icon to the list of icons on the draw area.
 */
void
apptTransferCallback(
        Widget          widget,
        XtPointer       clientData,
        XtPointer       callData)
{
        DtDndTransferCallbackStruct *transferInfo =
                                (DtDndTransferCallbackStruct*) callData;
        DtDndBuffer     *buffers;
	IconInfo	*iconList, *iconPtr;
	char		*name;
	char		*filename;
	int		ii;

	if (transferInfo == NULL) {
		return;
	}

	/*
	 * Verify the validity of the callback reason.
	 */
	
	if (transferInfo->dropData->protocol != DtDND_BUFFER_TRANSFER ||
	    transferInfo->reason != DtCR_DND_TRANSFER_DATA) {
		return;
	}

	/*
	 * Use abbreviated method of refering to the data buffers.
	 */

	if (transferInfo != NULL && transferInfo->dropData != NULL) {
        	buffers = transferInfo->dropData->data.buffers;
	} else {
		return;
	}

	/*
	 * Process each item being transfered.
	 */

        XtVaGetValues(widget, XmNuserData, &iconList, NULL);
        for (ii = 0; ii < transferInfo->dropData->numItems; ii++) {

		/*
		 * Check format of buffer
		 */

		/*
		 * Transfer the buffer data. Here there is no actual transfer
		 * taking place. Only the icons representing the appointments
		 * are created to indicate the transfer.
		 */

                name = buffers[ii].name;
		if (name == NULL)
			name = "unnamed";

		/* Create file from buffer */

		filename = fileStoreBuffer(buffers[ii].name,
			buffers[ii].bp, buffers[ii].size);
		printf("Stored buffer into '%s'\n", filename);
		XtFree(filename);

		/* Create icon */

                iconPtr = IconNew();
                IconInitialize(widget, iconPtr,
			transferInfo->x + ii * 10,
                	transferInfo->y + ii * 10,
                	buffers[ii].bp, buffers[ii].size, name, IconByData);

		/* Add to icon list on drop site */

                iconPtr->next = iconList;
		if (iconList != NULL) {
			iconList->prev = iconPtr;
		}
		iconList = iconPtr;
                XtVaSetValues(widget, XmNuserData, iconList, NULL);
	}
}

/*
 * apptDragSetup
 *
 * Prepares the appointment list to source drags of appointments with button 1.
 */
void
apptDragSetup(
        Widget          apptDragSource)
{
        XtAddEventHandler(apptDragSource, Button1MotionMask, False,
                (XtEventHandler)demoDragMotionHandler,
                (XtPointer)DtDND_BUFFER_TRANSFER);
}

/*
 * apptDropSetup
 *
 * Such a function is not needed since the demoDropSetup in demo.c registers
 * the draw area as a drop site for drops of buffers such as appointments.
 */

/*
 * apptDragStart
 *
 * Initiates a drag of an appointment from the appointment list provided
 * the pointer is over an appointment in the list.
 */
void
apptDragStart(
        Widget          widget,
        XEvent          *event)
{
        static XtCallbackRec convertCBRec[] = { {apptConvertCallback, NULL},
                                                {NULL, NULL} };
        static XtCallbackRec dragFinishCBRec[] = 
					      { {demoDragFinishCallback, NULL},
					        {apptDragFinishCallback, NULL},
                                                {NULL, NULL} };
        static IconInfo *iconPtr = NULL;
        Widget          dragIcon;
        Display         *display = XtDisplay(widget);
        int             itemCount,
                        selectedPos,
                        selectedItemCount;
        char            apptString[1024];

        convertCBRec[0].closure = (XtPointer)widget;

	/*
	 * Get list of selected items from the scrolled list of appointments
	 */

        XtVaGetValues(widget,
                XmNitemCount, &itemCount,
                XmNselectedItemCount, &selectedItemCount,
                NULL);

	/*
	 * Find out which item the pointer was over when the drag began
	 */

        selectedPos = XmListYToPos(widget, event->xmotion.y);

        if (selectedPos == 0 || selectedPos > itemCount) {
                return;
        }

        XtVaSetValues(widget, XmNuserData, selectedPos, NULL);

	/*
	 * Copy the appointment information into an appointment string
	 */

        sprintf(apptString, apptFormat,
		todaysApptList[1].date, todaysApptList[1].start,
		todaysApptList[1].end, todaysApptList[1].what);

	/*
	 * Create drag icon for appointment buffer drag
	 */

        if (iconPtr == NULL) {
                iconPtr = IconNew();
                IconInitialize(widget, iconPtr, 0, 0,
			apptString, strlen(apptString), NULL, IconByData);
        }

        if (iconPtr->dragIcon == NULL) {
                iconPtr->dragIcon = DtDndCreateSourceIcon(widget,
                        iconPtr->bitmap, iconPtr->mask);
        }
        if (selectedItemCount > 1) {
                dragIcon = NULL;
                itemCount = selectedItemCount;
        } else {
                dragIcon = iconPtr->dragIcon;
                itemCount = 1;
        }

	/*
	 * Start the drag
	 */

        if (DtDndVaDragStart(widget, event, DtDND_BUFFER_TRANSFER, itemCount,
                        XmDROP_COPY,
                        convertCBRec, dragFinishCBRec,
                        DtNsourceIcon, dragIcon,
			DtNbufferIsText, True,
                        NULL)
            == NULL) {

                printf("DragStart returned NULL.\n");
        }
}

 /*************************************************************************
 *
 *	Appointment Creation, Initialization & Destruction
 *
 *************************************************************************/

/*
 * apptCreateDragSource
 *
 * Create a scrolling list filled with appointments.
 */
Widget
apptCreateDragSource(
	Widget		parent)
{
	Widget		apptList;
	XmString	*apptListItems;
	int		apptCount;

	apptCount = apptCreateList(&apptListItems);

        apptList = XtVaCreateManagedWidget("apptList",
                xmListWidgetClass, parent,
		/*
		 * Uncomment the line specifying the selection policy to
		 * enable multiple buffer transfers from the scrolled list
		 * of appointments. WARNING: There is a bug in the interaction
		 * of the scrolled list and Drag and Drop which causes items
		 * in the scrolled list which are selected to appear unselected
		 * and vice versa.
		 */
                /* XmNselectionPolicy, XmMULTIPLE_SELECT, */
                XmNitems, apptListItems,
                XmNitemCount, apptCount,
                NULL);

	apptDestroyList(apptListItems, apptCount);
        
	return apptList;
}

/*
 * apptCreateDropSite
 *
 * Such a function is not needed since the drop site is the draw area which
 * is created in demoCreateDropSite() in demo.c
 */

/*
 * apptCreateList
 *
 * Creates a list of XmStrings with appointment data in them.
 */
static int
apptCreateList(
        XmString      **appts)
{
        int             ii, apptCount;
        char            tmpStr[256];
        time_t          now;
        struct tm       *tm;

        now = time(&now);
        tm = localtime(&now);

        sprintf(today, "%2d/%2d/%2d", tm->tm_mon+1, tm->tm_mday, tm->tm_year);

        for (ii = 0; todaysApptList[ii].date; ii++);

        apptCount = ii;

        *appts = (XmString *) XtMalloc(sizeof(XmString) * apptCount);

        for (ii = 0; todaysApptList[ii].date; ii++) {

                sprintf(tmpStr, "%s %s", todaysApptList[ii].start,
                                         todaysApptList[ii].what);

                (*appts)[ii] = XmStringCreate(tmpStr, XmFONTLIST_DEFAULT_TAG);

        }
        return apptCount;
}

/*
 * apptDestroyList
 *
 * Destroys a list of XmStrings with appointment data in them.
 */
static void
apptDestroyList(
        XmString       *appts,
	int		apptCount)
{
	int		ii;

        for (ii = 0; ii < apptCount; ii++) {
                XmStringFree(appts[ii]);
        }
	XtFree((char *)appts);
}

 /*************************************************************************
 *
 *	Appointment Utility Functions
 *
 *************************************************************************/

/*
 * apptFromListEntry
 *
 * Returns the full appointment based on the text of the appointment as
 * given in the text entry from the scrolled list of appointments.
 */
static Appointment*
apptFromListEntry(
        XmString        listEntry)
{
        int             ii;
        char            *entryText,
                        *string;

        /*
         * Get text string from XmString for use in comparisons
         */

        XmStringGetLtoR(listEntry, XmFONTLIST_DEFAULT_TAG, &entryText);

        for (ii = 0; todaysApptList[ii].what != NULL; ii++) {
                string = strstr(entryText, todaysApptList[ii].what);
                if (string != NULL) {
                        XtFree(entryText);
                        return(&todaysApptList[ii]);
                }
        }
        XtFree(entryText);
        return NULL;
}

/*
 * apptGetLabel
 *
 * Creates a label for an appointment icon given an appointment.
 */
static char*
apptGetLabel(
        char            *appt)
{
        char            start[128];
        int             count;

        if (appt == NULL) {
                return NULL;
        }
        appt = strstr(appt, "Start:");
        count = sscanf(appt, "Start:%*[ \t]%[^\n]", start);

        if (count != 1) {
                return NULL;
        }

        return XtNewString(start);
}

