Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / examples / dtdnd / demo.c
1 /* $TOG: demo.c /main/5 1999/07/20 14:48:53 mgreess $ */
2 /*****************************************************************************
3  *****************************************************************************
4  **
5  **   File:         demo.c
6  **
7  **   Description:  The Drag & Drop Demo program demonstrates the
8  **                 CDE DnD functions in the Desktop Services library:
9  **               
10  **                     DtDndDragStart.3x
11  **                     DtDndDropRegister.3x
12  **                     DtDndCreateSourceIcon.3x
13  **               
14  **                 The demo consists of a row of three different sources
15  **                 for text, filename and appointment buffer drags.
16  **                 It also has a text field that can accept either
17  **                 text or filename drops.  Finally there is a data
18  **                 area that accepts filename or buffer drops.
19  **
20  **  (c) Copyright 1993, 1994 Hewlett-Packard Company
21  **  (c) Copyright 1993, 1994 International Business Machines Corp.
22  **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
23  **  (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of
24  **      Novell, Inc.
25  **
26  ****************************************************************************
27  ************************************<+>*************************************/
28
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <time.h>
33
34 #include <X11/Intrinsic.h>
35
36 #include <Xm/Xm.h>
37 #include <Xm/DrawingA.h>
38 #include <Xm/Frame.h>
39 #include <Xm/List.h>
40 #include <Xm/MainW.h>
41 #include <Xm/MwmUtil.h>
42 #include <Xm/RowColumn.h>
43 #include <Xm/Separator.h>
44 #include <Xm/DragDrop.h>
45 #include <Xm/Screen.h>
46
47 #include <Dt/Dt.h>
48 #include <Dt/Dnd.h>
49
50 #include "icon.h"
51 #include "text.h"
52 #include "file.h"
53 #include "buff.h"
54
55  /*************************************************************************
56  *
57  *      Data Structures & Declarations For Drag & Drop Demo
58  *
59  *************************************************************************/
60
61 /*
62  * The Drag Threshold is the distance, measured in pixels, over which the
63  * pointer must travel while the BSelect button (first mouse button) is held
64  * down in order to start a drag. CDE defines this to be 10 pixels.
65  */
66
67 #define DRAG_THRESHOLD 10
68
69 /*
70  * Absolute value macro
71  */
72
73 #ifndef ABS
74 #define ABS(x) (((x) > 0) ? (x) : (-(x)))
75 #endif
76
77 /*
78  * Global variables
79  */
80
81 Widget          demoTopLevel;
82 XtAppContext    demoAppContext;
83 Boolean         demoDoingDrag = False;
84
85 /*
86  * Private Drag & Drop Demo Function Declarations
87  */
88
89 void            demoTransferCallback(Widget, XtPointer, XtPointer);
90
91  /*************************************************************************
92  *
93  *      General-Purpose Drag & Drop Functions
94  *
95  *************************************************************************/
96
97 /*
98  * demoDragFinishCallback
99  *
100  * Resets drag state to indicate the drag is over.
101  */
102 void
103 demoDragFinishCallback(
104         Widget          widget,
105         XtPointer       clientData,
106         XtPointer       callData)
107 {
108         demoDoingDrag = False;
109 }
110
111 /*
112  * demoDragMotionHandler
113  *
114  * Determine if the pointer has moved beyond the drag threshold while button 1
115  * was being held down.
116  */
117 void
118 demoDragMotionHandler(
119         Widget          dragInitiator,
120         XtPointer       clientData,
121         XEvent         *event)
122 {
123         static int      initialX = -1;
124         static int      initialY = -1;
125         int             diffX, diffY;
126         long            dragProtocol = (long)clientData;
127
128         if (!demoDoingDrag) {
129
130                 /*
131                  * If the drag is just starting, set initial button down coords
132                  */
133
134                 if (initialX == -1 && initialY == -1) {
135                         initialX = event->xmotion.x;
136                         initialY = event->xmotion.y;
137                 }
138
139                 /*
140                  * Find out how far pointer has moved since button press
141                  */
142
143                 diffX = initialX - event->xmotion.x;
144                 diffY = initialY - event->xmotion.y;
145                 
146                 if ((ABS(diffX) >= DRAG_THRESHOLD) ||
147                     (ABS(diffY) >= DRAG_THRESHOLD)) {
148                         demoDoingDrag = True;
149                         switch (dragProtocol) {
150                         case DtDND_TEXT_TRANSFER:
151                                 textDragStart(dragInitiator, event);
152                                 break;
153                         case DtDND_FILENAME_TRANSFER:
154                                 fileCheckForDrag(dragInitiator, event,
155                                         initialX, initialY);
156                                 break;
157                         case DtDND_BUFFER_TRANSFER:
158                                 apptDragStart(dragInitiator, event);
159                                 break;
160                         }
161                         initialX = -1;
162                         initialY = -1;
163                 }
164         }
165 }
166
167 /*
168  * demoLookForButton
169  */
170
171 /* ARGSUSED */
172 static Bool demoLookForButton(Display *d, XEvent *event, XPointer arg)
173 {
174 #define DAMPING 5
175 #define ABS_DELTA(x1, x2) (x1 < x2 ? x2 - x1 : x1 - x2)
176
177     if( event->type == MotionNotify) 
178     {
179         XEvent * press = (XEvent *) arg;
180
181         if (ABS_DELTA(press->xbutton.x_root, event->xmotion.x_root) > DAMPING ||
182             ABS_DELTA(press->xbutton.y_root, event->xmotion.y_root) > DAMPING)
183           return(True);
184     }
185     else if (event->type == ButtonRelease) return(True);
186
187     return(False);
188 }
189
190 /*
191  * demoProcessPress
192  */
193
194 void demoProcessPress(Widget w, XEvent *event, String *parms, Cardinal *nparms)
195 {
196 #define SELECTION_ACTION        0
197 #define TRANSFER_ACTION         1
198
199    int i, action, cur_item;
200    int *selected_positions, nselected_positions;
201
202    /* 
203     * This action happens when Button1 is pressed and the Selection
204     * and Transfer are integrated on Button1.  It is passed two
205     * parameters: the action to call when the event is a selection,
206     * and the action to call when the event is a transfer.
207     */
208
209     if (*nparms != 2 || !XmIsList(w)) return;
210
211     action = SELECTION_ACTION;
212     cur_item = XmListYToPos(w, event->xbutton.y);
213     if (cur_item > 0)
214     {
215         XtVaGetValues(w,
216                 XmNselectedPositions, &selected_positions,
217                 XmNselectedPositionCount, &nselected_positions,
218                 NULL);
219
220         for (i=0; i<nselected_positions; i++)
221         {
222             if (cur_item == selected_positions[i])
223             {
224                 /*
225                  * The determination of whether this is a transfer drag
226                  * cannot be made until a Motion event comes in.  It is
227                  * not a drag as soon as a ButtonUp event happens.
228                  */
229                 XEvent new_event;
230                 
231                 XPeekIfEvent(
232                         XtDisplay(w),
233                         &new_event,
234                         &demoLookForButton,
235                         (XPointer) event);
236                 switch (new_event.type)
237                 {
238                     case MotionNotify:
239                        action = TRANSFER_ACTION;
240                        break;
241                     case ButtonRelease:
242                        action = SELECTION_ACTION;
243                        break;
244                 }
245                 break;
246             }
247         }
248     }
249
250     XtCallActionProc(w, parms[action], event, parms, *nparms);
251 }
252
253 /*
254  * demoDrawAnimateCallback
255  *
256  * Expands the icon melted into the draw area by Motif.
257  */
258 void
259 demoDrawAnimateCallback(
260         Widget          dragContext, /* WARNING: This is being destroyed. */
261         XtPointer       clientData,
262         XtPointer       callData)
263 {
264         DtDndDropAnimateCallbackStruct *animateInfo =
265                                 (DtDndDropAnimateCallbackStruct *) callData;
266         Widget          dropDraw = (Widget)clientData;
267         Display         *display = XtDisplayOfObject(dropDraw);
268         Screen          *screen = XtScreen(dropDraw);
269         Window          window = XtWindow(dropDraw);
270         int             expandWidth, expandHeight,
271                         sourceX, sourceY;
272         static GC       graphicsContext = NULL;
273         XGCValues       gcValues;
274         IconInfo        *iconPtr;
275         
276         /*
277          * Create graphics context if it doesn't yet exist
278          */
279
280         if (graphicsContext == NULL) {
281                 gcValues.foreground = BlackPixelOfScreen(screen);
282                 gcValues.background = WhitePixelOfScreen(screen);
283                 graphicsContext = XCreateGC(display, window,
284                                 GCForeground | GCBackground, &gcValues);
285         }
286
287         /*
288          * Get the dragged icon from the dropDraw area
289          */
290
291         XtVaGetValues(dropDraw, XmNuserData, &iconPtr, NULL);
292
293         if (iconPtr == NULL) {
294                 return;
295         }
296
297         /*
298          * Set clip mask and coordinates for this icon in the graphics context
299          */
300
301         gcValues.clip_mask = iconPtr->mask;
302         gcValues.clip_x_origin = iconPtr->icon.x;
303         gcValues.clip_y_origin = iconPtr->icon.y;
304         XChangeGC(display, graphicsContext,
305                         GCClipMask | GCClipXOrigin | GCClipYOrigin,
306                         &gcValues);
307
308         /*
309          * Reconstitute the icon after Motif melts it
310          */
311
312         for (expandWidth = expandHeight = 0;
313              expandWidth < (int)iconPtr->icon.width &&
314                 expandHeight < (int)iconPtr->icon.height;
315              expandWidth += 2, expandHeight += 2) {
316
317                 sourceX = ((int)iconPtr->icon.width - expandWidth)/2;
318                 sourceY = ((int)iconPtr->icon.height - expandHeight)/2;
319
320                 XCopyPlane(display, iconPtr->bitmap, window, graphicsContext,
321                         sourceX, sourceY, expandWidth, expandHeight,
322                         iconPtr->icon.x + sourceX, iconPtr->icon.y + sourceY,
323                         1L);
324
325                 XmeMicroSleep(25000L);
326                 XFlush(display);
327         }
328 }
329
330 /*
331  * demoDrawExposeCallback
332  *
333  * Draws all the icons associated with the drawing area.
334  */
335 void
336 demoDrawExposeCallback(
337         Widget          widget,
338         XtPointer       clientData,
339         XtPointer       callData)
340 {
341         IconInfo       *iconPtr;
342
343         XtVaGetValues(widget, XmNuserData, &iconPtr, NULL);
344
345         while (iconPtr != NULL) {
346                 IconDraw(widget, iconPtr);
347                 iconPtr = iconPtr->next;
348         }
349 }
350
351 /*
352  * demoDropSetup
353  *
354  * Registers draw area to accept drops of files or buffers such as appointments.
355  */
356 void
357 demoDropSetup(
358         Widget          dropDraw)
359 {
360         static XtCallbackRec transferCBRec[] = { {demoTransferCallback, NULL},
361                                                  {NULL, NULL} };
362         static XtCallbackRec animateCBRec[] = { {demoDrawAnimateCallback, NULL},
363                                                 {NULL, NULL} };
364
365         animateCBRec[0].closure = (XtPointer)dropDraw;
366
367         DtDndVaDropRegister(dropDraw,
368                 DtDND_FILENAME_TRANSFER | DtDND_BUFFER_TRANSFER,
369                 XmDROP_COPY | XmDROP_MOVE, transferCBRec, 
370                 DtNdropAnimateCallback,         animateCBRec,
371                 DtNtextIsBuffer,                True,
372                 DtNpreserveRegistration,        False,
373                 NULL);
374 }
375
376 /*
377  * demoTransferCallback
378  *
379  * Called when something is dropped on the drawing area drop site. If the
380  * transfer protocol is DtDND_FILENAME_TRANSFER or DtDND_BUFFER_TRANSFER
381  * the fileTransferCallback and apptTransferCallback are called respectively.
382  */
383 void
384 demoTransferCallback(
385         Widget          widget,
386         XtPointer       clientData,
387         XtPointer       callData)
388 {
389         DtDndTransferCallbackStruct *transferInfo =
390                                 (DtDndTransferCallbackStruct *) callData;
391
392         if (transferInfo == NULL) {
393                 return;
394         }
395
396         switch (transferInfo->dropData->protocol) {
397
398         case DtDND_FILENAME_TRANSFER:
399                 fileTransferCallback(widget, clientData, callData);
400                 break;
401         case DtDND_BUFFER_TRANSFER:
402                 apptTransferCallback(widget, clientData, callData);
403                 break;
404         }
405 }
406
407  /*************************************************************************
408  *
409  *      Demo Client Creation
410  *
411  *************************************************************************/
412
413 /*
414  * Fallback resources are used if app-defaults file is not found
415  */
416
417 String  fallbackResources[] = {
418         "title:                                 CDE Drag & Drop Demo",
419
420         "*outerRowColumn.orientation:           VERTICAL",
421         "*outerRowColumn.spacing:               15",
422         "*outerRowColumn.marginHeight:          15",
423         "*outerRowColumn.marginWidth:           15",
424
425         "*upperRowColumn.orientation:           HORIZONTAL",
426         "*upperRowColumn.packing:               PACK_COLUMN",
427         "*upperRowColumn.spacing:               15",
428         "*upperRowColumn.marginHeight:          0",
429         "*upperRowColumn.marginWidth:           0",
430
431         "*fileDraw.height:                      175",
432         "*fileDraw.resizePolicy:                RESIZE_NONE",
433
434         "*fruitList.listSizePolicy:             CONSTANT",
435         "*fruitList.scrollBarDisplayPolicy:     STATIC",
436         "*fruitList.selectionPolicy:            MULTIPLE_SELECT",
437
438         "*apptList.listSizePolicy:              CONSTANT",
439         "*apptList.scrollBarDisplayPolicy:      STATIC",
440         "*apptList.selectionPolicy:             MULTIPLE_SELECT",
441
442         "*textRowColumn.orientation:            HORIZONTAL",
443         "*textRowColumn.packing:                PACK_TIGHT",
444         "*textRowColumn*textLabel.labelString: Name:",
445         "*textRowColumn*textField.width:        550",
446         "*textRowColumn.marginWidth:            0",
447
448         "*dropDraw.height:                      100",
449         "*dropDraw.resizePolicy:                RESIZE_NONE",
450         NULL
451 };
452
453 /*
454  * demoCreateDropSite
455  *
456  * Creates the drawing area at the bottom of the demo which is used as a drop
457  * site for files and appointments.
458  */
459 Widget
460 demoCreateDropSite(
461         Widget          parent)
462 {
463         Widget          dropFrame,
464                         dropDraw;
465
466         dropFrame = XtVaCreateManagedWidget("dropFrame",
467                 xmFrameWidgetClass, parent,
468                 NULL);
469
470         dropDraw = XtVaCreateManagedWidget("dropDraw",
471                 xmDrawingAreaWidgetClass, dropFrame,
472                 NULL);
473
474         XtAddCallback(dropDraw,
475                 XmNexposeCallback, demoDrawExposeCallback,
476                 NULL);
477
478         return dropDraw;
479 }
480
481 /*
482  * main
483  *
484  * Create widgets for the demo program. Call protocol-specific functions
485  * to initialize the drag sources and drop sites.
486  */
487 void
488 main(
489         int             argc,
490         String         *argv)
491 {
492         Widget          outerRowColumn,
493                         upperRowColumn,
494                         textDragSource,
495                         fileDragSource,
496                         apptDragSource,
497                         separator,
498                         textDropSite,
499                         drawDropSite;
500
501         /*
502          * Create basic widgets and layout widgets
503          */
504
505         XtSetLanguageProc(NULL, NULL, NULL);
506
507         demoTopLevel = XtAppInitialize(&demoAppContext, "Dtdnddemo", 
508                 (XrmOptionDescList)NULL, 0, &argc, argv, 
509                 fallbackResources, (ArgList)NULL, 0);
510
511         DtAppInitialize(demoAppContext, XtDisplay(demoTopLevel), 
512                 demoTopLevel, argv[0], "Dtdnddemo"); 
513
514         outerRowColumn = XtVaCreateManagedWidget("outerRowColumn",
515                 xmRowColumnWidgetClass, demoTopLevel,
516                 NULL);
517
518         upperRowColumn = XtVaCreateManagedWidget("upperRowColumn",
519                 xmRowColumnWidgetClass, outerRowColumn,
520                 NULL);
521
522         /*
523          * Create the drag sources
524          */
525
526         textDragSource = textCreateDragSource(upperRowColumn);
527         fileDragSource = fileCreateDragSource(upperRowColumn);
528         apptDragSource = apptCreateDragSource(upperRowColumn);
529
530         /*
531          * Create a line separating the drag sources from the drop sites
532          */
533
534         separator = XtVaCreateManagedWidget("separator",
535                 xmSeparatorWidgetClass, outerRowColumn,
536                 NULL);
537         
538         /*
539          * Create the drop sites
540          */
541
542         textDropSite = textCreateDropSite(outerRowColumn);
543         drawDropSite = demoCreateDropSite(outerRowColumn);
544
545         /*
546          * Realize the widget tree
547          */
548
549         XtRealizeWidget(demoTopLevel);
550
551         /*
552          * Load the data typing database which is used to get icons
553          */
554
555         DtDtsLoadDataTypes();
556
557         /*
558          * Set up the drag sources
559          */
560
561         textDragSetup(textDragSource);
562         fileDragSetup(fileDragSource);
563         apptDragSetup(apptDragSource);
564
565         /*
566          * Set up the drop sites
567          */
568
569         textDropSetup(textDropSite);
570         demoDropSetup(drawDropSite);
571
572         /*
573          * Start the event processing loop
574          */
575
576         XtAppMainLoop(demoAppContext);
577 }