Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / lib / DtSvc / DtUtil1 / DndDrop.c
1 /* $XConsortium: DndDrop.c /main/6 1996/09/27 19:00:45 drk $ */
2  /*********************************************************************
3  *
4  *      File:           DndDrop.c
5  *
6  *      Description:    Implementation of DND Drop Registration
7  *
8  *********************************************************************
9  *
10  *+SNOTICE
11  *
12  *      RESTRICTED CONFIDENTIAL INFORMATION:
13  *      
14  *      The information in this document is subject to special
15  *      restrictions in a confidential disclosure agreement between
16  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
17  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
18  *      Sun's specific written approval.  This documment and all copies
19  *      and derivative works thereof must be returned or destroyed at
20  *      Sun's request.
21  *
22  *      Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
23  *
24  * (c) Copyright 1993, 1994 Hewlett-Packard Company
25  * (c) Copyright 1993, 1994 International Business Machines Corp.
26  * (c) Copyright 1993, 1994 Sun Microsystems, Inc.
27  * (c) Copyright 1993, 1994 Novell, Inc.
28  *
29  *+ENOTICE
30  */
31
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <X11/Intrinsic.h> 
35 #include <Xm/AtomMgr.h> 
36 #include <Xm/DragDrop.h>
37 #include <Xm/DropSMgr.h> 
38 #include <Xm/DropTrans.h> 
39 #include "Dnd.h"
40 #include "DndP.h"
41 #include "DtSvcLock.h"
42
43 /* 
44  * Drop Receiver Callbacks
45  */
46
47 static void             dndDropProc(Widget, XtPointer, XtPointer);
48 static void             dndTransferStart(Widget, XmDropProcCallbackStruct*, 
49                                 DtDropInfo*, DtDndTransfer*, Atom*, Cardinal);
50 static void             dndTransferProc(Widget, XtPointer, Atom*, Atom*, 
51                                 XtPointer, unsigned long*, int*);
52 static void             dndAppTransfer(Widget, DtDropInfo*);
53 static void             dndDropAnimateCallback(Widget, XtPointer, XtPointer);
54
55 /*
56  * Drop Support Functions
57  */
58
59 static  void            dndDropSiteDestroy(Widget, XtPointer, XtPointer);
60 static  XID             dndGetContextXID(Display*);
61 static  Boolean         dndSaveDropInfo(Display*, Widget, DtDropInfo*);
62 static  DtDropInfo *    dndFindDropInfo(Display*, Widget);
63 static  void            dndTransferStartFailed(Widget);
64 static  void            dndTransferFail(DtDropInfo*, Widget);
65  
66 /*
67  * Drop Register Resources
68  */
69
70 typedef struct {
71         XtCallbackList  dropAnimateCallback;
72         Boolean         preserveRegistration;
73         Boolean         registerChildren;
74         Boolean         textIsBuffer;
75 } DropSettings;
76
77 #define Offset(field)   XtOffsetOf(DropSettings, field)
78
79 static XtResource dropResources[] = {
80       { DtNdropAnimateCallback, DtCDropAnimateCallback, 
81         XtRCallback, sizeof(XtCallbackList), Offset(dropAnimateCallback), 
82         XtRImmediate, (XtPointer)NULL },
83       { DtNpreserveRegistration, DtCPreserveRegistration, 
84         XtRBoolean, sizeof(Boolean), Offset(preserveRegistration), 
85         XtRImmediate, (XtPointer)True },
86       { DtNregisterChildren, DtCRegisterChildren, 
87         XtRBoolean, sizeof(Boolean), Offset(registerChildren), 
88         XtRImmediate, (XtPointer)False },
89       { DtNtextIsBuffer, DtCTextIsBuffer, 
90         XtRBoolean, sizeof(Boolean), Offset(textIsBuffer), 
91         XtRImmediate, (XtPointer)False },
92 };
93
94 #undef Offset
95
96 /*
97  * DtDndVaDropRegister
98  *
99  *      Drop Site Registration - varargs version
100  */
101
102 void
103 DtDndVaDropRegister(
104         Widget          dropReceiver,
105         DtDndProtocol   protocols,
106         unsigned char   operations,
107         XtCallbackList  transferCallback,
108         ...)
109 {
110         va_list         vaList;
111         ArgList         argList;
112         Cardinal        argCount;
113         _DtSvcWidgetToAppContext(dropReceiver);
114
115         _DtSvcAppLock(app);
116         va_start(vaList, transferCallback);
117         argCount = _DtDndCountVarArgs(vaList);
118         va_end(vaList);
119
120         va_start(vaList, transferCallback);
121         _DtDndArgListFromVarArgs(vaList, argCount, &argList, &argCount);
122         va_end(vaList);
123
124         DtDndDropRegister(dropReceiver, protocols, operations, 
125                                 transferCallback, argList, argCount);
126
127         XtFree((char *)argList);
128         _DtSvcAppUnlock(app);
129 }
130
131 /*
132  * DtDndDropRegister
133  *
134  *      Drop Site Registration - arglist version
135  */
136
137 void
138 DtDndDropRegister(
139         Widget          dropReceiver,
140         DtDndProtocol   protocols,
141         unsigned char   operations,
142         XtCallbackList  dropTransferCallback,
143         ArgList         argList,
144         Cardinal        argCount)
145 {
146         Display *       display         = XtDisplayOfObject(dropReceiver);
147         DropSettings    settings;
148         DtDropInfo *    dtDropInfo;
149         DtDropSiteInfo* dsInfo = NULL;
150         DtDndTransfer * transfers;
151         Cardinal        numTransfers;
152         Atom            *importTargets;
153         Cardinal        numImportTargets;
154         unsigned char   mergedOperations;
155         Arg *           args;
156         int             ii, jj, kk, nn;
157         _DtSvcWidgetToAppContext(dropReceiver);
158
159         _DtSvcAppLock(app);
160
161         /*
162          * Reject a noop registration
163          */
164         if (protocols == DtDND_NOOP_TRANSFER) {
165                 _DtSvcAppUnlock(app);
166                 return;
167         }
168
169         /*
170          * Parse resources into dropResources
171          */
172         XtGetSubresources(dropReceiver, &settings,
173                                 (String)NULL, (String)NULL,
174                                 dropResources, XtNumber(dropResources),
175                                 argList, argCount);
176         /*
177          * Initialize DropInfo
178          */
179
180         dtDropInfo = (DtDropInfo *) XtMalloc(sizeof(DtDropInfo));
181
182         dtDropInfo->dropReceiver                = dropReceiver;
183         dtDropInfo->protocols                   = protocols;
184         dtDropInfo->operations                  = operations;
185         dtDropInfo->textIsBuffer                = settings.textIsBuffer;
186         dtDropInfo->dropSiteInfo                = NULL;
187         dtDropInfo->transferInfo                = NULL;
188         dtDropInfo->status                      = DtDND_SUCCESS;
189
190         dtDropInfo->dropTransferCallback 
191                         = _DtDndCopyCallbackList(dropTransferCallback);
192         dtDropInfo->dropAnimateCallback
193                         = _DtDndCopyCallbackList(settings.dropAnimateCallback);
194
195         /*
196          * Initialize Transfer Methods
197          */
198
199         transfers = _DtDndCreateImportTransfers(dtDropInfo, &numTransfers);
200
201         dtDropInfo->transfers           = transfers;
202         dtDropInfo->numTransfers        = numTransfers;
203
204 #ifdef DEBUG
205         printf("DtDndDropRegister: registering widget 0x%p\n", dropReceiver);
206         _DtDndPrintTransfers(display,transfers,numTransfers);
207 #endif
208
209         /*
210          * Preserve existing drop registration info if requested
211          */
212
213         if (settings.preserveRegistration) {
214                 Arg             pArgs[4];
215
216                 dsInfo = (DtDropSiteInfo *)XtMalloc(sizeof(DtDropSiteInfo));
217
218                 dsInfo->dropProc                = (XtCallbackProc)NULL;
219                 dsInfo->operations              = XmDROP_NOOP;
220                 dsInfo->importTargets           = NULL;
221                 dsInfo->numImportTargets        = 0;
222
223                 nn = 0;
224
225                 XtSetArg(pArgs[nn], XmNimportTargets,
226                                 &(dsInfo->importTargets));              nn++;
227                 XtSetArg(pArgs[nn], XmNdropSiteOperations,
228                                 &(dsInfo->operations));                 nn++;
229                 XtSetArg(pArgs[nn], XmNnumImportTargets,
230                                 &(dsInfo->numImportTargets));           nn++;
231                 XtSetArg(pArgs[nn], XmNdropProc,
232                                 &(dsInfo->dropProc));                   nn++;
233
234                 XmDropSiteRetrieve(dropReceiver, pArgs, nn);
235
236                 if ( (dsInfo->dropProc != NULL) && 
237                      (dsInfo->numImportTargets > 0)  &&
238                      (dsInfo->operations != XmDROP_NOOP) ){
239
240                         dtDropInfo->dropSiteInfo = dsInfo;
241                 } else {
242                         XtFree((char *)dsInfo);
243                         dsInfo = NULL;
244                 }
245         }
246         
247         /*
248          * Create list of import targets based on the requested transfer
249          * protocols and any preserved drop site registration.
250          */
251
252         numImportTargets = 0;
253
254         for (ii = 0; ii < numTransfers; ii++) {
255                 numImportTargets += transfers[ii].numTargets;
256         }
257
258         if (dsInfo != NULL) { 
259                 numImportTargets += dsInfo->numImportTargets;
260         }
261
262         importTargets = (Atom *) XtMalloc(numImportTargets * sizeof(Atom));
263
264         kk = 0;
265         for (ii = 0; ii < numTransfers; ii++) {
266                 for (jj = 0; jj < transfers[ii].numTargets; jj++) {
267                         importTargets[kk++] = transfers[ii].targets[jj];
268                 }
269         }
270
271         if (dsInfo != NULL) {
272                 for (jj = 0; jj < dsInfo->numImportTargets; jj++) {
273                         importTargets[kk++] = dsInfo->importTargets[jj];
274                 }
275         }
276
277         /*
278          * Merge operations
279          */
280  
281         if (dsInfo != NULL) {
282                 mergedOperations = dsInfo->operations | operations;
283         } else {
284                 mergedOperations = operations;
285         }
286
287         /*
288          * Create an argument list
289          */
290
291 #define NUM_DROP_ARGS   5
292         args = (Arg *) XtMalloc(sizeof(Arg) * (NUM_DROP_ARGS + argCount));
293 #undef  NUM_DROP_ARGS
294
295         /*
296          * Copy in passed arguments
297          */
298
299         nn = 0;
300
301         for (ii = 0; ii < argCount; ii++) {
302                 XtSetArg(args[nn], argList[ii].name, argList[ii].value); nn++;
303         }
304
305         /*
306          * Set argument list for drop site registration
307          */
308
309         XtSetArg(args[nn], XmNimportTargets,      importTargets);    nn++;
310         XtSetArg(args[nn], XmNnumImportTargets,   numImportTargets); nn++;
311         XtSetArg(args[nn], XmNdropSiteOperations, mergedOperations);       nn++;
312         XtSetArg(args[nn], XmNdropProc,           dndDropProc);            nn++;
313
314         if (settings.registerChildren) {
315                XtSetArg(args[nn], XmNdropSiteType, XmDROP_SITE_COMPOSITE); nn++;
316         }
317
318         /*
319          * Register the drop site
320          */
321
322         if (dsInfo != NULL) {
323                 XmDropSiteUnregister(dropReceiver);
324                 XmDropSiteRegister(dropReceiver, args, nn);
325         } else {
326                 XmDropSiteRegister(dropReceiver, args, nn);
327         }
328
329         /*
330          * Add a destroy callback to unregister the drop site
331          * Store the dropInfo using the dropReceiver as the context
332          * Free locally allocated memory
333          */
334
335         XtAddCallback(dropReceiver, XtNdestroyCallback, 
336                         dndDropSiteDestroy, NULL);
337
338         (void)dndSaveDropInfo(display, dropReceiver, dtDropInfo);
339
340         XtFree((char *)importTargets);
341         XtFree((char *)args);
342         _DtSvcAppUnlock(app);
343 }
344
345 /*
346  * DtDndDropUnregister
347  *
348  *      Unregister the drop site
349  */
350 void
351 DtDndDropUnregister(
352         Widget          dropReceiver)
353 {
354         Display        *display         = XtDisplayOfObject(dropReceiver);
355         DtDropInfo     *dtDropInfo;
356         DtDropSiteInfo *dsInfo;
357         _DtSvcWidgetToAppContext(dropReceiver);
358
359         _DtSvcAppLock(app);
360
361         /*
362          * Retrieve dropInfo.  If there isn't one then the drop site
363          * was not registered by DtDndDropRegister() so return.
364          */
365
366         if ((dtDropInfo = dndFindDropInfo(display, dropReceiver)) == NULL) {
367                 _DtSvcAppUnlock(app);
368                 return;
369         }
370         dsInfo = dtDropInfo->dropSiteInfo;
371
372         /*
373          * Unregister the drop site
374          */
375
376         XmDropSiteUnregister(dropReceiver);
377
378         /*
379          * If we have preserved drop site registration
380          * then restore the drop site to it's original state.
381          */
382
383         if (dsInfo != NULL) {
384                 Arg             args[4];
385                 Cardinal        nn = 0;
386  
387                 XtSetArg(args[nn], XmNimportTargets, 
388                         dsInfo->importTargets);                 nn++;
389                 XtSetArg(args[nn], XmNnumImportTargets,
390                         dsInfo->numImportTargets);              nn++;
391                 XtSetArg(args[nn], XmNdropProc,
392                         dsInfo->dropProc);                      nn++;
393                 XtSetArg(args[nn], XmNdropSiteOperations,
394                         dsInfo->operations);                    nn++;
395
396                 XmDropSiteRegister(dropReceiver, args, nn);
397
398                 XtFree((char *)dsInfo);
399         }
400  
401         /*
402          * Free callback, context and memory associated with DtDndDropRegister
403          */
404
405         XtRemoveCallback(dropReceiver, XtNdestroyCallback, 
406                         dndDropSiteDestroy, NULL);
407
408         XDeleteContext(display, dndGetContextXID(display), 
409                 (XContext)dropReceiver);
410
411         _DtDndDestroyTransfers(dtDropInfo->transfers, dtDropInfo->numTransfers);
412
413         XtFree((char *)dtDropInfo->dropTransferCallback);
414         XtFree((char *)dtDropInfo->dropAnimateCallback);
415         XtFree((char *)dtDropInfo);
416         _DtSvcAppUnlock(app);
417 }
418
419
420 /*********************************************************************
421  *
422  * Drop Receiver Callbacks
423  *
424  *********************************************************************/
425
426
427 /*
428  * dndDropProc
429  *
430  *      Determine transfer target and operation and initiate transfer.
431  */
432 static void
433 dndDropProc(
434         Widget          dropReceiver,
435         XtPointer       clientData,
436         XtPointer       callData)
437 {
438         XmDropProcCallbackStruct *xmDropInfo =
439                         (XmDropProcCallbackStruct *) callData;
440         Display *       display         = XtDisplayOfObject(dropReceiver);
441         Boolean         compatible;
442         Atom *          exportTargets    = NULL;
443         Cardinal        numExportTargets = 0;
444         DtDropInfo *    dtDropInfo;
445         DtDropSiteInfo* dsInfo;
446         DtDndTransfer * transfer;
447  
448         /*
449          * Reject invalid drops
450          */
451
452         if ( (xmDropInfo->dropSiteStatus != XmDROP_SITE_VALID) || 
453              (xmDropInfo->operation == XmDROP_NOOP) || 
454              (xmDropInfo->dropAction != XmDROP) ) {
455
456 #ifdef DEBUG
457                 printf("dndDropProc: failed drop status or operation\n");
458 #endif
459                 dndTransferStartFailed(xmDropInfo->dragContext);
460                 return;
461         }
462
463         /*
464          * Get the cached DropInfo
465          */
466
467         if ((dtDropInfo = dndFindDropInfo(display, dropReceiver)) == NULL) {
468                 dndTransferStartFailed(xmDropInfo->dragContext);
469                 return;
470         }
471
472         /*
473          * Get the export targets from the dragContext
474          */
475
476         XtVaGetValues(xmDropInfo->dragContext,
477                 XmNexportTargets, &exportTargets,
478                 XmNnumExportTargets, &numExportTargets,
479                 NULL);
480
481         if (exportTargets == NULL) {
482                 dndTransferStartFailed(xmDropInfo->dragContext);
483                 dtDropInfo->status = DtDND_FAILURE;
484                 return;
485         }
486
487 #ifdef DEBUG
488         printf("dndDropProc: Export Targets: ");
489         _DtDndPrintTargets(display,exportTargets,numExportTargets);
490 #endif
491
492         /*
493          * Search the DnD protocol import targets list to see
494          * if it's a target we handle.  If it is then start our transfer.
495          */
496
497         transfer = _DtDndTransferFromTargets(
498                         dtDropInfo->transfers, dtDropInfo->numTransfers,
499                         exportTargets, numExportTargets);
500
501         if (transfer != NULL) {
502
503                 dndTransferStart(dropReceiver, xmDropInfo, dtDropInfo,
504                         transfer, exportTargets, numExportTargets);
505                 return;
506         }
507
508         /*
509          * If there isn't any alternate drop registration
510          * then fail the transfer
511          */
512
513         dsInfo = dtDropInfo->dropSiteInfo;
514
515         if (dsInfo == NULL) {
516                 dndTransferStartFailed(xmDropInfo->dragContext);
517                 dtDropInfo->status = DtDND_FAILURE;
518                 return;
519         }
520
521         /*
522          * Determine if the exportTargets are compatible with
523          * the alternate drop registration targets
524          * If they are not compatible then fail the transfer
525          * Otherwise call the original dropProc.
526          */
527
528         compatible = XmTargetsAreCompatible(display,
529                         dsInfo->importTargets, dsInfo->numImportTargets,
530                         exportTargets, numExportTargets);
531
532         if (!compatible) {
533                 dndTransferStartFailed(xmDropInfo->dragContext);
534                 dtDropInfo->status = DtDND_FAILURE;
535                 return;
536         }
537
538         if (dsInfo->dropProc != NULL) {
539                 (*(dsInfo->dropProc))(dropReceiver, clientData, callData);
540         }
541 }
542
543 /*
544  * dndTransferStart
545  *
546  *      Start the transfer using protocol specific transfer entries
547  *
548  */
549 static void
550 dndTransferStart(
551         Widget          dropReceiver,
552         XmDropProcCallbackStruct *xmDropInfo,
553         DtDropInfo *    dtDropInfo,
554         DtDndTransfer * transfer,
555         Atom *          exportTargets,
556         Cardinal        numExportTargets)
557 {
558         XtCallbackRec   dropAnimateCbRec[] = { {dndDropAnimateCallback,
559                                                         NULL}, {NULL, NULL} };
560         Display *       display            = XtDisplayOfObject(dropReceiver);
561         DtDndContext *  dropData;
562         DtTransferInfo *transferInfo;
563         Atom *          transferTargets;
564         Cardinal        numTransferTargets;
565         Boolean         owCompat;
566         XmDropTransferEntryRec * transferEntries;
567         Cardinal        numTransferEntries;
568         int             posOffsetX, posOffsetY;
569         Boolean         status;
570         Arg             args[10];
571         Cardinal        ii, nn;
572
573 #ifdef DEBUG
574         printf("dndTransferStart: transfer method = %s\n",
575                                         transfer->methods->name);
576 #endif
577         /*
578          * If the operation is a move but not registered for a move
579          * then force it to a copy drop.
580          */
581
582         if ((xmDropInfo->operation == XmDROP_MOVE) && 
583            !(dtDropInfo->operations & XmDROP_MOVE)) {
584                 xmDropInfo->operation = XmDROP_COPY;
585         }
586
587         /*
588          * Determine icon adjustment to drop position
589          */
590
591         _DtDndGetIconOffset(xmDropInfo->dragContext,
592                         transfer->methods->sourceType,
593                         &posOffsetX, &posOffsetY);
594
595         /*
596          * Initialize drop transfer info
597          */
598
599         transferInfo = (DtTransferInfo *)XtMalloc(sizeof(DtTransferInfo));
600
601         transferInfo->dragContext               = xmDropInfo->dragContext;
602         transferInfo->protocol                  = transfer->methods->protocol;
603         transferInfo->operation                 = xmDropInfo->operation;
604         transferInfo->methods                   = transfer->methods;
605         transferInfo->transferTargets           = NULL;
606         transferInfo->numTransferTargets        = 0;
607         transferInfo->currentTransfer           = 0;
608         transferInfo->appTransferCalled         = False;
609         transferInfo->event                     = xmDropInfo->event;
610         transferInfo->x                         = xmDropInfo->x + posOffsetX;
611         transferInfo->y                         = xmDropInfo->y + posOffsetY;
612         transferInfo->clientData                = NULL;
613
614         /*
615          * Initialize drop data
616          */
617
618         dropData = (DtDndContext *)XtCalloc(1,sizeof(DtDndContext));
619
620         dropData->protocol              = transferInfo->protocol;
621
622         /*
623          * Initialize drop transfer fields of DtDropInfo
624          */
625
626         dtDropInfo->transferInfo        = transferInfo;
627         dtDropInfo->dropData            = dropData;
628         dtDropInfo->status              = DtDND_SUCCESS;
629
630         /* 
631          * Get protocol specific transfer targets 
632          */
633
634         (*transferInfo->methods->transferTargets)(dtDropInfo,
635                         exportTargets, numExportTargets,
636                         &transferTargets, &numTransferTargets);
637
638         if (transferTargets == NULL) {
639
640                 XtFree((char *)dtDropInfo->transferInfo);
641                 XtFree((char *)dtDropInfo->dropData);
642
643                 dtDropInfo->transferInfo        = NULL;
644                 dtDropInfo->dropData            = NULL;
645                 dtDropInfo->status              = DtDND_FAILURE;
646
647                 dndTransferStartFailed(xmDropInfo->dragContext);
648                 return;
649         }
650
651         /*
652          * Convert _SUN_ENUMERATION_COUNT if available
653          * Insert into transfer targets list
654          */
655         owCompat = XmTargetsAreCompatible(display, 
656                         exportTargets, numExportTargets,
657                         &XA_SUN_ENUM_COUNT, 1);
658
659         if (owCompat) {
660                 Cardinal        jj, numTargets;
661                 Atom            *targets;
662
663                 numTargets      = numTransferTargets + 1;
664                 targets         = (Atom *)XtMalloc(numTargets * sizeof(Atom));
665
666                 jj = 0;
667                 targets[jj++] = XA_SUN_ENUM_COUNT;
668
669                 for (ii = 0; ii < numTransferTargets; ii++, jj++) {
670                         targets[jj] = transferTargets[ii];
671                 }
672
673                 XtFree((char *)transferTargets);
674
675                 transferTargets         = targets;
676                 numTransferTargets      = numTargets;
677         }
678
679         /*
680          * Create a transfer entries list from the transfer targets
681          */
682
683         numTransferEntries = numTransferTargets;
684         transferEntries = (XmDropTransferEntryRec *)
685                 XtMalloc(numTransferEntries * sizeof(XmDropTransferEntryRec));
686
687         for (ii = 0; ii < numTransferEntries; ii++) {
688                 transferEntries[ii].target      = transferTargets[ii];
689                 transferEntries[ii].client_data = (XtPointer)dtDropInfo;
690         }
691
692         transferInfo->transferTargets           = transferTargets;
693         transferInfo->numTransferTargets        = numTransferTargets;
694
695 #ifdef DEBUG
696         printf("Requesting transfers: ");
697         _DtDndPrintTargets(display,transferTargets,numTransferTargets);
698 #endif
699
700         /*
701          * Start the drop transfer
702          */
703
704         dropAnimateCbRec[0].closure = (XtPointer) dtDropInfo;
705         transferInfo->dropAnimateCallback
706                         = _DtDndCopyCallbackList(dropAnimateCbRec);
707         nn = 0;
708         XtSetArg(args[nn], XmNdropTransfers,    transferEntries); nn++;
709         XtSetArg(args[nn], XmNnumDropTransfers, numTransferEntries);nn++;
710         XtSetArg(args[nn], XmNtransferProc,     dndTransferProc); nn++;
711         XtSetArg(args[nn], XmNdestroyCallback,  transferInfo->dropAnimateCallback); nn++;
712
713         (void)XmDropTransferStart(xmDropInfo->dragContext, args, nn);
714
715         XtFree((char *)transferEntries);
716 }
717
718 /*
719  * dndTransferProc
720  *
721  *      Process the transfers
722  */
723 static void
724 dndTransferProc(
725         Widget          dropTransfer,
726         XtPointer       clientData,
727         Atom *          selection,
728         Atom *          type,
729         XtPointer       value,
730         unsigned long * length,
731         int *           format)
732 {
733         DtDropInfo *    dtDropInfo      = (DtDropInfo *)clientData;
734         DtTransferInfo *transferInfo    = dtDropInfo->transferInfo;
735         int             index;
736         Atom            target;
737
738         /*
739          * Ignore if no dtDropInfo or failed transfer
740          * Ignore null transfer (which are responses to DELETE)
741          */
742
743         if (value == NULL || dtDropInfo == NULL || 
744             dtDropInfo->status == DtDND_FAILURE) {
745                 dndTransferFail(dtDropInfo, dropTransfer);
746                 XtFree(value);
747                 return;
748         } else if (*type == XA_NULL) {
749                 XtFree(value);
750                 return;
751         }
752
753         /*
754          * Determine current transfer target
755          */
756
757         index = transferInfo->currentTransfer;
758         
759         if (index < transferInfo->numTransferTargets) {
760                 target = transferInfo->transferTargets[index];
761         } else {
762                 target = None;
763         }
764         transferInfo->currentTransfer++;
765
766 #ifdef DEBUG
767         {
768         Display *display = XtDisplayOfObject(dropTransfer);
769         char *targetname = XGetAtomName(display,target);
770         char *typename   = XGetAtomName(display,*type);
771         printf("dndTransferProc: target = %s type = %s fmt = %d len = %ld\n",
772                         (targetname ? targetname : "Null"), 
773                         (typename ? typename : "Null"),
774                          *format, *length);
775         if (targetname) XFree(targetname);
776         if (typename) XFree(typename);
777         }
778 #endif
779
780         /*
781          * Handle _SUN_ENUMERATION_COUNT request; reject multiple items
782          */
783         if (target == XA_SUN_ENUM_COUNT) {
784                 int *   enumCount = (int *)value;
785                 if (enumCount[0] > 1) {
786                         dndTransferFail(dtDropInfo, dropTransfer);
787                 }
788                 XtFree(value);
789                 return;
790         }
791
792         /*
793          * Invoke protocol specific transfer proc
794          */
795
796         (*transferInfo->methods->transfer)( dropTransfer, dtDropInfo, 
797                 selection, &target, type, value, length, format);
798
799         /*
800          * If the transfer failed, set up to terminate unsuccessfully
801          */
802
803         if (dtDropInfo->status == DtDND_FAILURE) {
804                 dndTransferFail(dtDropInfo, dropTransfer);
805                 return;
806         }
807
808         /*
809          * If the transfers are complete;
810          * If the dropData isn't ready then fail the transfer
811          * Otherwise call the application transfer callback
812          */
813
814         if (transferInfo->currentTransfer == transferInfo->numTransferTargets) {
815
816                 if (dtDropInfo->dropData->numItems == 0) {
817                         dndTransferFail(dtDropInfo, dropTransfer);
818                         return;
819                 } else if (!transferInfo->appTransferCalled) {
820                         dndAppTransfer(dropTransfer, dtDropInfo);
821                         transferInfo->appTransferCalled = True;
822                 }
823         }
824 }
825
826 /*
827  * dndAppTransfer
828  *
829  *       Call the application transfer callback
830  */
831 static void
832 dndAppTransfer(
833         Widget          dropTransfer,
834         DtDropInfo *    dtDropInfo)
835 {
836         DtTransferInfo *transferInfo    = dtDropInfo->transferInfo;
837         DtDndTransferCallbackStruct     transferCallData;
838
839         /*
840          * If the transfer failed or there isn't a callback simply return
841          */
842
843         if (dtDropInfo->status == DtDND_FAILURE ||
844             dtDropInfo->dropTransferCallback == NULL) {
845                 return;
846         }
847
848         /*
849          * Fill in the callback structure and call the
850          * application-defined dropTransferCallback.
851          */
852
853         transferCallData.reason         = DtCR_DND_TRANSFER_DATA;
854         transferCallData.event          = transferInfo->event;
855         transferCallData.x              = transferInfo->x;
856         transferCallData.y              = transferInfo->y;
857         transferCallData.operation      = transferInfo->operation;
858         transferCallData.dropData       = dtDropInfo->dropData;
859         transferCallData.dragContext    = transferInfo->dragContext;
860         transferCallData.status         = DtDND_SUCCESS;
861
862         if (transferInfo->operation == XmDROP_MOVE) {
863                 transferCallData.completeMove = True;
864         } else {
865                 transferCallData.completeMove = False;
866         }
867
868         _DtDndCallCallbackList(dtDropInfo->dropReceiver, 
869                 dtDropInfo->dropTransferCallback, 
870                 (XtPointer)&transferCallData);
871
872         dtDropInfo->status = transferCallData.status;
873
874         /*
875          * If the app requests it then fail the transfer
876          */
877         if (transferCallData.status == DtDND_FAILURE) {
878                 dndTransferFail(dtDropInfo, dropTransfer);
879                 return;
880         }
881
882         /*
883          * If the transfer succeeded and this is a move operation
884          * then transfer DELETE to delete the original.
885          */
886
887         if (transferCallData.status == DtDND_SUCCESS &&
888             transferCallData.completeMove &&
889             transferInfo->operation == XmDROP_MOVE) {
890
891                 XmDropTransferEntryRec  transferEntries[1];
892
893                 transferEntries[0].target       = XA_DELETE;
894                 transferEntries[0].client_data  = (XtPointer)dtDropInfo;
895
896                 XmDropTransferAdd(dropTransfer, transferEntries, 1);
897         }
898 }
899
900 /*
901  * dndDropAnimateCallback
902  *
903  *      Call the application dropAnimateCallback
904  */
905 static void
906 dndDropAnimateCallback(
907         Widget          dropTransfer,
908         XtPointer       clientData,
909         XtPointer       callData)
910 {
911         DtDropInfo *    dtDropInfo      = (DtDropInfo *)clientData;
912         DtTransferInfo *transferInfo    = dtDropInfo->transferInfo;
913         DtDndDropAnimateCallbackStruct  dropAnimateCallData;
914
915         /*
916          * Fill in the callback structure and call the
917          * application-defined dropAnimateCallback.
918          */
919
920         if (dtDropInfo->status == DtDND_SUCCESS &&
921             dtDropInfo->dropAnimateCallback != NULL) {
922
923                 dropAnimateCallData.reason      = DtCR_DND_DROP_ANIMATE;
924                 dropAnimateCallData.event       = transferInfo->event;
925                 dropAnimateCallData.x           = transferInfo->x;
926                 dropAnimateCallData.y           = transferInfo->y;
927                 dropAnimateCallData.operation   = transferInfo->operation;
928                 dropAnimateCallData.dropData    = dtDropInfo->dropData;
929
930                 _DtDndCallCallbackList(dtDropInfo->dropReceiver, 
931                         dtDropInfo->dropAnimateCallback, 
932                         (XtPointer)&dropAnimateCallData);
933         }
934
935         /*
936          * Invoke the protocol specific transfer finish proc
937          */
938
939         (*transferInfo->methods->transferFinish)(dtDropInfo);
940
941         /*
942          * Free transfer created memory 
943          */
944         XtFree((char *)dtDropInfo->transferInfo->transferTargets);
945         XtFree((char *)dtDropInfo->transferInfo->dropAnimateCallback); 
946         XtFree((char *)dtDropInfo->transferInfo);
947         XtFree((char *)dtDropInfo->dropData);
948         
949         dtDropInfo->transferInfo        = NULL;
950         dtDropInfo->dropData            = NULL;
951 }
952
953 /*
954  * dndDropSiteDestroy
955  *
956  * Destroy callback unregisters the widget being destroyed as a drop site
957  */
958 static void
959 dndDropSiteDestroy(
960         Widget          widget,
961         XtPointer       clientData,
962         XtPointer       callData)
963 {
964         DtDndDropUnregister(widget);
965 }
966         
967 /*
968  * dndGetContextXID
969  *
970  *      Returns a pixmap to use as the XID for Save/Find Context
971  */
972 static XID
973 dndGetContextXID(
974         Display *       display)
975 {
976         static XID contextXID;
977
978         _DtSvcProcessLock();
979         if (contextXID == NULL) {
980                 contextXID = XCreatePixmap(display, 
981                                         DefaultRootWindow(display), 1, 1, 1);
982         }
983         _DtSvcProcessUnlock();
984         return contextXID;
985 }
986
987 /*
988  * dndSaveDropInfo
989  *
990  *      Saves the DtDropInfo relative to the dropReceiver
991  */
992 static Boolean          
993 dndSaveDropInfo(
994         Display *       display,
995         Widget          dropReceiver,
996         DtDropInfo *    dtDropInfo)
997 {
998         int             status;
999
1000         status = XSaveContext(display, dndGetContextXID(display),
1001                 (XContext)dropReceiver, (XPointer)dtDropInfo);
1002
1003         return (status == 0);
1004 }
1005
1006 /*
1007  * dndFindDropInfo
1008  *
1009  *      Finds the DtDropInfo saved relative to the dropReceiver
1010  */
1011 static DtDropInfo *
1012 dndFindDropInfo(
1013         Display *       display,
1014         Widget          dropReceiver)
1015 {
1016         int             status;
1017         DtDropInfo *    dtDropInfo;
1018
1019         status = XFindContext(display, dndGetContextXID(display), 
1020                 (XContext)dropReceiver, (XPointer *)&dtDropInfo);
1021
1022         if (status != 0)
1023                 dtDropInfo = (DtDropInfo *)NULL;
1024         
1025         return dtDropInfo;
1026 }
1027
1028 /*
1029  * dndTransferStartFailed
1030  *
1031  *      Fail the transfer by starting a 'failure' transfer
1032  *      Calls XmDropTransferStart()
1033  */
1034 static void
1035 dndTransferStartFailed(
1036         Widget  dragContext)
1037 {
1038         Arg     args[2];
1039         int     nn = 0;
1040
1041         XtSetArg(args[nn], XmNtransferStatus, XmTRANSFER_FAILURE); nn++;
1042         XtSetArg(args[nn], XmNnumDropTransfers, 0); nn++;
1043
1044         XmDropTransferStart(dragContext, args, nn);
1045 }
1046
1047 /*
1048  * dndTransferFail
1049  *
1050  *      Fail the transfer by setting the dropTransfer widget to failure
1051  *      Assumes XmDropTransferStart() already called.
1052  */
1053 static void
1054 dndTransferFail(
1055         DtDropInfo *    dtDropInfo,
1056         Widget          dropTransfer)
1057 {
1058         if (dtDropInfo)
1059                 dtDropInfo->status = DtDND_FAILURE;
1060
1061         XtVaSetValues(dropTransfer,
1062                 XmNtransferStatus,      XmTRANSFER_FAILURE,
1063                 XmNnumDropTransfers,    0,
1064                 NULL);
1065 }
1066