1 /*
2  * Motif
3  *
4  * Copyright (c) 1987-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22 */
23 #ifdef REV_INFO
24 #ifndef lint
25 static char rcsid[] = "$XConsortium: DropTrans.c /main/15 1996/05/02 13:50:19 pascale $"
26 #endif
27 #endif
28 
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32 
33 
34 #include <Xm/DropTransP.h>
35 #include <Xm/DragCP.h>
36 #include "XmI.h"
37 #include "DragCI.h"
38 #include "DragICCI.h"
39 #include <Xm/AtomMgr.h>
40 #include <stdio.h>
41 
42 /* Deactivated the fix, since it causes crash.
43    For details see http://bugs.motifzone.net/long_list.cgi?buglist=1361
44 
45    #define CR1146
46 */
47 
48 #ifdef CR1146
49 static int isValidStartDropTimerId = 0;
50 #endif
51 /********    Static Function Declarations    ********/
52 
53 static void ClassPartInit(
54                         WidgetClass wc) ;
55 static void Initialize(
56                         Widget rw,
57                         Widget nw,
58                         ArgList args,
59                         Cardinal *num_args) ;
60 static Boolean SetValues(
61                         Widget cw,
62                         Widget rw,
63                         Widget nw,
64                         ArgList args,
65                         Cardinal *num_args) ;
66 static void Destroy(
67                         Widget w) ;
68 static void SourceNotifiedCB(
69                         Widget w,
70                         XtPointer client_data,
71                         Atom *selection,
72                         Atom *type,
73                         XtPointer value,
74                         unsigned long *length,
75                         int *format) ;
76 static void TerminateTransfer(
77                         XmDropTransferObject dt,
78                         Atom *selection) ;
79 static void ProcessTransferEntry(
80                         XmDropTransferObject dt,
81                         Cardinal which) ;
82 static void DropTransferSelectionCB(
83                         Widget w,
84                         XtPointer client_data,
85                         Atom *selection,
86                         Atom *type,
87                         XtPointer value,
88                         unsigned long *length,
89                         int *format) ;
90 static Widget StartDropTransfer(
91                         Widget refWidget,
92                         ArgList args,
93                         Cardinal argCount) ;
94 static void AddDropTransfer(
95                         Widget widget,
96                         XmDropTransferEntry transfers,
97                         Cardinal num_transfers) ;
98 static void DragContextDestroyCB(
99 			Widget widget,
100 			XtPointer client,
101 			XtPointer call) ;
102 
103 /********    End Static Function Declarations    ********/
104 
105 
106 static XtResource resources[] = {
107 	{   XmNdropTransfers, XmCDropTransfers,
108 		XmRDropTransfers, sizeof(XmDropTransferEntry),
109 		XtOffsetOf( struct _XmDropTransferRec,
110 			dropTransfer.drop_transfers),
111 		XmRImmediate, (XtPointer) NULL
112 	},
113 	{   XmNnumDropTransfers, XmCNumDropTransfers,
114 		XmRCardinal, sizeof(Cardinal),
115 		XtOffsetOf( struct _XmDropTransferRec,
116 			dropTransfer.num_drop_transfers),
117 		XmRImmediate, (XtPointer) 0
118 	},
119 	{   XmNincremental, XmCIncremental, XmRBoolean, sizeof(Boolean),
120 		XtOffsetOf( struct _XmDropTransferRec, dropTransfer.incremental),
121 		XmRImmediate, (XtPointer) FALSE
122 	},
123 	{   XmNtransferProc, XmCTransferProc,
124 		XmRCallbackProc, sizeof(XtSelectionCallbackProc),
125 		XtOffsetOf( struct _XmDropTransferRec, dropTransfer.transfer_callback),
126 		XmRImmediate, (XtPointer) 0
127 	},
128 	{   XmNtransferStatus, XmCTransferStatus, XmRTransferStatus,
129 		sizeof(unsigned char),
130 		XtOffsetOf( struct _XmDropTransferRec, dropTransfer.transfer_status),
131 		XmRImmediate, (XtPointer) XmTRANSFER_SUCCESS
132 	},
133 };
134 
135 
136 /*  class record definition  */
137 
138 externaldef(xmgadgetclassrec) XmDropTransferClassRec
139 	xmDropTransferClassRec = {
140    {
141       (WidgetClass) &objectClassRec,    /* superclass	         */
142       "XmDropTransfer",                 /* class_name	         */
143       sizeof(XmDropTransferRec),        /* widget_size	         */
144       NULL,                             /* class_initialize      */
145       ClassPartInit,                    /* class part initialize */
146       False,                  		/* class_inited          */
147       Initialize,                      	/* initialize	         */
148       NULL,                             /* initialize_hook       */
149       NULL,	                       	/* obj1  	         */
150       NULL,				/* obj2                  */
151       0,				/* obj3	                 */
152       resources,                        /* resources	         */
153       XtNumber(resources),              /* num_resources         */
154       NULLQUARK,                        /* xrm_class	         */
155       True,                             /* obj4                  */
156       XtExposeCompressSeries,           /* obj5                  */
157       True,                             /* obj6                  */
158       False,                            /* obj7                  */
159       Destroy,                          /* destroy               */
160       NULL,                             /* obj8                  */
161       NULL,				/* obj9                  */
162       SetValues,                        /* set_values	         */
163       NULL,                             /* set_values_hook       */
164       NULL,         			/* obj10                 */
165       NULL,                             /* get_values_hook       */
166       NULL,                             /* obj11    	         */
167       XtVersion,                        /* version               */
168       NULL,                             /* callback private      */
169       NULL,                             /* obj12                 */
170       NULL,                             /* obj13                 */
171       NULL,				/* obj14                 */
172       NULL,                             /* extension             */
173    },
174    {
175       StartDropTransfer,           	/* start_drop_transfer   */
176       AddDropTransfer,             	/* add_drop_transfer     */
177       NULL,                        	/* extension             */
178    },
179 };
180 
181 externaldef(xmdroptransferobjectclass) WidgetClass
182 	xmDropTransferObjectClass = (WidgetClass)
183 		&xmDropTransferClassRec;
184 
185 /*ARGSUSED*/
186 static void
ClassPartInit(WidgetClass wc)187 ClassPartInit(
188         WidgetClass wc )
189 {
190 }
191 
192 /*ARGSUSED*/
193 static void
Initialize(Widget rw,Widget nw,ArgList args,Cardinal * num_args)194 Initialize(
195         Widget rw,
196         Widget nw,
197         ArgList args,
198         Cardinal *num_args )
199 {
200 	XmDropTransferObject new_w = (XmDropTransferObject) nw;
201 	XmDropTransferPart *dtp =
202 		(XmDropTransferPart *) &(new_w->dropTransfer);
203 
204 	if (dtp->num_drop_transfers != 0)
205 	{
206 		dtp->num_drop_transfer_lists = 1;
207 		dtp->drop_transfer_lists = (XmDropTransferList)
208 			XtMalloc(sizeof(XmDropTransferListRec) *
209 				dtp->num_drop_transfer_lists);
210 		dtp->drop_transfer_lists[0].transfer_list =
211 		        (XmDropTransferEntry)_XmAllocAndCopy(
212 			dtp->drop_transfers, sizeof(XmDropTransferEntryRec)
213 				* dtp->num_drop_transfers);
214 		dtp->drop_transfer_lists[0].num_transfers =
215 			dtp->num_drop_transfers;
216 
217 		/* strictly for hygene... */
218 
219 		dtp->drop_transfers = dtp->drop_transfer_lists[0].transfer_list;
220 	}
221 	else
222 	{
223 		dtp->drop_transfer_lists = NULL;
224 		dtp->num_drop_transfer_lists = 0;
225 	}
226 
227 	dtp->motif_drop_atom = XInternAtom(
228 		XtDisplayOfObject(nw), XmS_MOTIF_DROP, False);
229 
230 	dtp->cur_drop_transfer_list = (Cardinal) -1;
231 	dtp->cur_xfer = (Cardinal) -1;
232 	dtp->cur_targets = (Atom *) NULL;
233 	dtp->cur_client_data = (XtPointer *) NULL;
234 }
235 
236 /*ARGSUSED*/
237 static Boolean
SetValues(Widget cw,Widget rw,Widget nw,ArgList args,Cardinal * num_args)238 SetValues(
239         Widget cw,
240         Widget rw,
241         Widget nw,
242         ArgList args,
243         Cardinal *num_args )
244 {
245 	/* Stub */
246 	/* !!! Should disallow any changes !!! */
247 	return True;
248 }
249 
250 static void
Destroy(Widget w)251 Destroy(
252         Widget w )
253 {
254     XmDropTransferObject new_w = (XmDropTransferObject) w;
255     Cardinal 		 i;
256     XmDragContext	 dc;
257 
258     /*
259      * clean up the hanging dragContext
260      */
261     dc = (XmDragContext)XmGetDragContext((Widget)new_w,
262 					 new_w->dropTransfer.timestamp);
263     if (dc && dc->drag.sourceIsExternal)
264       XtDestroyWidget((Widget)dc);
265 
266     for (i = 0; i < new_w->dropTransfer.num_drop_transfer_lists; i++)
267       {
268 	  XmDropTransferList	currEntry =
269 	    &(new_w->dropTransfer.drop_transfer_lists[i]);
270 
271 	  XtFree((char *)currEntry->transfer_list);
272       }
273     XtFree((char *)new_w->dropTransfer.drop_transfer_lists);
274 }
275 
276 /*ARGSUSED*/
277 static void
SourceNotifiedCB(Widget w,XtPointer client_data,Atom * selection,Atom * type,XtPointer value,unsigned long * length,int * format)278 SourceNotifiedCB(
279         Widget w,
280         XtPointer client_data,
281         Atom *selection,
282         Atom *type,
283         XtPointer value,
284         unsigned long *length,
285         int *format )
286 {
287     XmDropTransferObject	dt = (XmDropTransferObject)client_data;
288 
289     if (value != NULL)
290       XtFree((char *) value);
291     /* self-immolution aaaaaaiii */
292     XtDestroyWidget((Widget)dt);
293 }
294 
295 static void
TerminateTransfer(XmDropTransferObject dt,Atom * selection)296 TerminateTransfer(
297         XmDropTransferObject dt,
298         Atom *selection )
299 {
300 	Atom status;
301 	XmDropTransferPart *dtp =
302 		(XmDropTransferPart *) &(dt->dropTransfer);
303 	XmDragContext	dc = (XmDragContext) dtp->dragContext;
304 
305 	if (dtp->transfer_status == XmTRANSFER_SUCCESS)
306 		status = XInternAtom(XtDisplayOfObject((Widget)dt),
307 			XmSTRANSFER_SUCCESS, False);
308 	else /* XmTRANSFER_FAILURE */
309 		status = XInternAtom(XtDisplayOfObject((Widget)dt),
310 			XmSTRANSFER_FAILURE, False);
311 
312 	/*
313 	 * we need to pass in the shell since that is the only widget
314 	 * visible to the initiator.
315 	 */
316 
317 	XtGetSelectionValue(dc->drag.currReceiverInfo->shell,
318 				*selection, status,
319 				SourceNotifiedCB, (XtPointer)dt,
320 				dtp->timestamp);
321 }
322 
323 static void
ProcessTransferEntry(XmDropTransferObject dt,Cardinal which)324 ProcessTransferEntry(
325         XmDropTransferObject dt,
326         Cardinal which )
327 {
328 	XmDropTransferPart *dtp =
329 		(XmDropTransferPart *) &(dt->dropTransfer);
330 	XmDropTransferList	tl = &(dtp->drop_transfer_lists[which]);
331 	XmDragContext dc = (XmDragContext)dtp->dragContext;
332 	Cardinal i;
333 	Arg args[1];
334 	Atom real_selection_atom;
335 
336 	dtp->cur_drop_transfer_list = which;
337 	dtp->cur_targets = (Atom *)
338 		XtMalloc((tl->num_transfers * sizeof(Atom)));
339 	dtp->cur_client_data = (XtPointer *)
340 		XtMalloc((tl->num_transfers * sizeof(XtPointer)));
341 
342 	i = 0;
343 	XtSetArg(args[i], XmNiccHandle, &real_selection_atom); i++;
344 	XtGetValues(dtp->dragContext, args, i);
345 
346 	for (i=0; i < tl->num_transfers; i++)
347 	{
348 		dtp->cur_targets[i] = tl->transfer_list[i].target;
349 		/*
350 		 * all of the client data have to point to us so that we can
351 		 * bootstrap
352 		 */
353 		dtp->cur_client_data[i] = (XtPointer)dt;
354 	}
355 
356 	dtp->cur_xfer = 0;
357 
358 	/*
359 	 * we need to pass in the destShell since that is the only widget
360 	 * visible to the initiator.
361 	 */
362 
363 	/*
364 	 * As both an optimization and workaround for an Xt bug,
365 	 * if the number of transfers is just one then call the
366 	 * singular version of XtGetSelectionValue{,Incremental}.
367 	 * If there is only one item there is no need to call
368 	 * the plural version; XtGetSelectionValues
369 	 * Also there is a bug in some Xt's which fail to handle
370 	 * both MULTIPLE and INCR together correctly and can hang
371 	 * the drop transfer forever.  By adding this special case
372 	 * we allow knowledgeable clients to request any target that
373 	 * might be large enough to trigger an INCR as a single item
374 	 * that would avoid the MULTIPLE case.
375 	 */
376 
377 	if (dtp->incremental)
378 	{
379            if (tl->num_transfers == 1) {
380                XtGetSelectionValueIncremental(
381                        dc->drag.currReceiverInfo->shell,
382                        real_selection_atom, dtp->cur_targets[0],
383                        DropTransferSelectionCB,
384                        (XtPointer)dtp->cur_client_data[0],
385                        dtp->timestamp);
386            } else {
387 
388 		XtGetSelectionValuesIncremental(
389 			dc->drag.currReceiverInfo->shell,
390 			real_selection_atom, dtp->cur_targets,
391 			tl->num_transfers, DropTransferSelectionCB,
392 			(XtPointer *)dtp->cur_client_data,
393 			dtp->timestamp);
394             }
395 	}
396 	else
397 	{
398            if (tl->num_transfers == 1) {
399                XtGetSelectionValue(dc->drag.currReceiverInfo->shell,
400                        real_selection_atom, dtp->cur_targets[0],
401                        DropTransferSelectionCB,
402                        (XtPointer)dtp->cur_client_data[0],
403                        dtp->timestamp);
404            } else {
405 		XtGetSelectionValues(dc->drag.currReceiverInfo->shell,
406 			real_selection_atom, dtp->cur_targets,
407 			tl->num_transfers, DropTransferSelectionCB,
408 			(XtPointer *)dtp->cur_client_data,
409 			dtp->timestamp);
410            }
411 	}
412 }
413 
414 
415 /*
416  * This routine is called with the shell as the widget and us as the
417  * client data. We can't pass ourselves since we're not windowed
418  */
419 /*ARGSUSED*/
420 static void
DropTransferSelectionCB(Widget w,XtPointer client_data,Atom * selection,Atom * type,XtPointer value,unsigned long * length,int * format)421 DropTransferSelectionCB(
422         Widget w,
423         XtPointer client_data,
424         Atom *selection,
425         Atom *type,
426         XtPointer value,
427         unsigned long *length,
428         int *format )
429 {
430 	XmDropTransferObject dt = (XmDropTransferObject) client_data;
431 	XmDropTransferPart *dtp =
432 		(XmDropTransferPart *) &(dt->dropTransfer);
433 	XmDropTransferList	tl =
434 		&(dtp->drop_transfer_lists[dtp->cur_drop_transfer_list]);
435 
436 
437 	(*(dtp->transfer_callback))
438 		((Widget)dt, tl->transfer_list[dtp->cur_xfer].client_data,
439 			selection, type, value, length, format);
440 
441        /* The transfer list needs to be reassigned at this point in case
442 	* an XmDropTransferAdd() was called in the callback.
443         */
444 	tl = &(dtp->drop_transfer_lists[dtp->cur_drop_transfer_list]);
445 
446 	if ( !(dtp->incremental)
447 		||
448 		((dtp->incremental) && (value != NULL) && (*length == 0)))
449 	{
450 		dtp->cur_xfer++;
451 
452 		if (dtp->cur_xfer == tl->num_transfers)
453 		{
454 			XtFree((char *)dtp->cur_targets);
455 			XtFree((char *)dtp->cur_client_data);
456 
457 			if (++(dtp->cur_drop_transfer_list) <
458 				dtp->num_drop_transfer_lists)
459 			{
460 			/* Get the next transfer entry in the list and process it */
461 				ProcessTransferEntry(dt, dtp->cur_drop_transfer_list);
462 			}
463 			else /* notify the source that we're done */
464 				TerminateTransfer(dt, selection);
465 		}
466 	}
467 }
468 
469 
470 /*ARGSUSED*/
471 static void
StartDropTimer(XtPointer clientData,XtIntervalId * id)472 StartDropTimer(
473         XtPointer clientData,
474         XtIntervalId *id )
475 {
476 	XmDropTransferObject dt = (XmDropTransferObject)clientData;
477 	XmDropTransferPart *dtp;
478 	Arg my_args[1];
479 	int i;
480 	Atom selection;
481 
482 #ifdef CR1146
483     isValidStartDropTimerId = 0;
484 #endif
485 
486 
487 	dtp = (XmDropTransferPart *) &(dt->dropTransfer);
488 
489 	if (dtp->num_drop_transfer_lists)
490 	{
491 		/* Get the first transfer entry in the list and process it */
492 		ProcessTransferEntry(dt, 0);
493 	}
494 	else /* notify the source that we've changed our mind */
495 	{
496 		i = 0;
497 		XtSetArg(my_args[i], XmNiccHandle, &selection); i++;
498 		XtGetValues(dtp->dragContext, my_args, i);
499 
500 		TerminateTransfer(dt, &selection);
501 	}
502 }
503 
504 static void
DragContextDestroyCB(Widget widget,XtPointer client,XtPointer call)505 DragContextDestroyCB(
506         Widget widget,
507         XtPointer client,
508         XtPointer call)
509 {
510         XtIntervalId timer = (XtIntervalId) client;
511 #ifdef CR1146
512         if (!isValidStartDropTimerId) return;
513 #endif
514         XtRemoveTimeOut(timer);
515 }
516 
517 static Widget
StartDropTransfer(Widget refWidget,ArgList args,Cardinal argCount)518 StartDropTransfer(
519         Widget refWidget,
520         ArgList args,
521         Cardinal argCount )
522 {
523 	static int which = 0;
524 	XmDropTransferObject dt;
525 	char buf[30];
526 	XtIntervalId timer;
527 
528 	_XmProcessLock();
529 	sprintf(buf, "Transfer%d", which++);
530 	_XmProcessUnlock();
531 	dt = (XmDropTransferObject) XtCreateWidget(buf,
532 		xmDropTransferObjectClass,
533 		(Widget) XmGetXmDisplay(XtDisplayOfObject(refWidget)),
534 		args, argCount);
535 
536 	dt->dropTransfer.dragContext = refWidget;
537 	dt->dropTransfer.timestamp =
538 	  ((XmDragContext)refWidget)->drag.dragFinishTime;
539 
540 	/*
541 	 * The processing of the dropTransfer should happen after the
542 	 * dropStart message is echoed to the initiator. Since we're
543 	 * being called out of the dropProc of the receiver we can't
544 	 * just proceed or else the entire transfer could happen
545 	 * inline. We therefore add ourselves as a zero length timer
546 	 * which will allow us to get called after the dropProc has
547 	 * returned.
548  	 * To prevent against this code's being called twice by disparate
549  	 * parts of the drag/drop code (especially on failure) self-guard by
550  	 * watching for destruction of the widget and avoid the rest of the
551  	 * operations.
552  	 */
553 
554 	timer = XtAppAddTimeOut(XtWidgetToApplicationContext( (Widget)dt),
555 				0, StartDropTimer, (XtPointer)dt);
556 #ifdef CR1146
557     if (isValidStartDropTimerId) {
558         printf("Motif Error in file %s at line %d: A DropTimer is already active,\n", __FILE__, __LINE__);
559     }
560     isValidStartDropTimerId = 1;
561 #endif
562 	XtAddCallback(refWidget, XmNdestroyCallback, DragContextDestroyCB,
563 		      (XtPointer)timer);
564 
565 	return((Widget) dt);
566 }
567 
568 
569 
570 Widget
XmDropTransferStart(Widget refWidget,ArgList args,Cardinal argCount)571 XmDropTransferStart(
572         Widget refWidget,
573         ArgList args,
574         Cardinal argCount )
575 {
576 	Widget ddo = (Widget) XmGetXmDisplay(XtDisplayOfObject(refWidget));
577 	XmDropTransferObjectClass wc;
578 	Arg lclArgs[1];
579 	int n;
580 	Widget ret_val;
581 	_XmWidgetToAppContext(refWidget);
582 
583 	_XmAppLock(app);
584 	n = 0;
585 	XtSetArg(lclArgs[n], XmNdropTransferClass, &wc); n++;
586 	XtGetValues(ddo, lclArgs, n);
587 
588 	ret_val = (*(wc->dropTransfer_class.start_drop_transfer))
589 		(refWidget, args, argCount);
590 	_XmAppUnlock(app);
591 	return ret_val;
592 }
593 
594 
595 
596 static void
AddDropTransfer(Widget widget,XmDropTransferEntry transfers,Cardinal num_transfers)597 AddDropTransfer(
598         Widget widget,
599         XmDropTransferEntry transfers,
600         Cardinal num_transfers )
601 {
602         XmDropTransferObject dto = (XmDropTransferObject) widget;
603 	XmDropTransferPart *dtp =
604 		(XmDropTransferPart *) &(dto->dropTransfer);
605 	Cardinal index = dtp->num_drop_transfer_lists++;
606 
607 	dtp->drop_transfer_lists = (XmDropTransferList)
608 		XtRealloc((char *)dtp->drop_transfer_lists,
609 			sizeof(XmDropTransferListRec) *
610 			dtp->num_drop_transfer_lists);
611 	dtp->drop_transfer_lists[index].transfer_list =
612 	        (XmDropTransferEntry)_XmAllocAndCopy(
613 		transfers, sizeof(XmDropTransferEntryRec) * num_transfers);
614 	dtp->drop_transfer_lists[index].num_transfers = num_transfers;
615 }
616 
617 
618 void
XmDropTransferAdd(Widget widget,XmDropTransferEntry transfers,Cardinal num_transfers)619 XmDropTransferAdd(
620         Widget widget,
621         XmDropTransferEntry transfers,
622         Cardinal num_transfers )
623 {
624 	XmDropTransferObject dt = (XmDropTransferObject) widget;
625 	XmDropTransferObjectClass wc;
626 	_XmWidgetToAppContext(widget);
627 
628 	_XmAppLock(app);
629 	wc = (XmDropTransferObjectClass) XtClass(dt);
630 	((*(wc->dropTransfer_class.add_drop_transfer))
631 		(widget, transfers, num_transfers));
632 	_XmAppUnlock(app);
633 }
634 
635 
636