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