Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / examples / dtdnd / file.c
1 /* $TOG: file.c /main/4 1999/07/20 14:49:49 mgreess $ */
2 /*****************************************************************************
3  *****************************************************************************
4  **
5  **   File:         file.c
6  **
7  **   Description:  File transfer functions for the CDE Drag & Drop Demo.
8  **
9  **  (c) Copyright 1993, 1994 Hewlett-Packard Company
10  **  (c) Copyright 1993, 1994 International Business Machines Corp.
11  **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
12  **  (c) Copyright 1993, 1994 Unix System Labs, Inc., a subsidiary of
13  **      Novell, Inc.
14  **
15  ****************************************************************************
16  ************************************<+>*************************************/
17
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <sys/stat.h>
21 #include <sys/param.h>
22
23 #include <X11/Intrinsic.h>
24
25 #include <Xm/Xm.h>
26 #include <Xm/DrawingA.h>
27 #include <Xm/Frame.h>
28 #include <Xm/Protocols.h>
29
30 #include <Dt/Dt.h>
31 #include <Dt/Dnd.h>
32
33 #include "icon.h"
34 #include "demo.h"
35 #include "file.h"
36
37  /*************************************************************************
38  *
39  *       Data Structures & Private Declarations For Appointment Buffers
40  *
41  **************************************************************************/
42
43 /*
44  * Specification of drag or drop directory
45  */
46
47 typedef enum {
48         DragDirectory,
49         DropDirectory
50 } DragOrDrop;
51
52 /*
53  * File names and contents. The contents are the minimal required by the
54  * data typing service to correctly type the file via content-based typing.
55  * The file names are appropriate to the type of the contents as well. This
56  * demo uses content-based typing to get the appropriate icons for these files.
57  */
58
59 #define FILE_NAME_CSH   "runit"
60 #define FILE_DATA_CSH   "#! /bin/csh"
61
62 #define FILE_NAME_PS    "map.ps"
63 #define FILE_DATA_PS    "%!PS-Adobe-2.0"
64
65 #define FILE_NAME_TEXT  "tasks"
66 #define FILE_DATA_TEXT  "Nothing"
67
68 /*
69  * Private file function declarations
70  */
71
72 static XtActionProc     fileCheckForDragProc(Widget, XEvent*, String*,
73                                                 Cardinal*);
74 static void             fileConvertCallback(Widget, XtPointer, XtPointer);
75 static void             fileCreateDirectory(char*);
76 static void             fileCreateFile(char*, char*, char*);
77 static void             fileCreateFiles(Widget);
78 static void             fileDragStart(Widget, XEvent*, IconInfo*, int);
79 static char*            fileGetContents(char*);
80 static char*            fileGetDemoDirectory();
81 static char*            fileGetDirectoryName(DragOrDrop);
82 static void             fileRemoveDirectory(char*);
83 static void             fileShutdown(Widget, XtPointer, XtPointer);
84
85  /*************************************************************************
86  *
87  *       File Name Drag & Drop
88  *
89  **************************************************************************/
90
91 /*
92  * fileConvertCallback
93  *
94  * When converting the data, fills in the file structure with the name(s) of
95  * the file(s). When converting DELETE, removes the given file(s) and icon(s)
96  * from the filesystem and drawing area respectively.
97  */
98 static void
99 fileConvertCallback(
100         Widget          dragContext,
101         XtPointer       clientData,
102         XtPointer       callData)
103 {
104         DtDndConvertCallbackStruct *convertInfo =
105                                         (DtDndConvertCallbackStruct *) callData;
106         IconInfo        *iconArray = (IconInfo *) clientData;
107         char            filePath[MAXPATHLEN + 1],
108                         command[MAXPATHLEN + 4];
109         int             ii;
110         Widget          fileDraw;
111
112         if (convertInfo == NULL) {
113                 return;
114         }
115
116         /*
117          * Verify the protocol and callback reason
118          */
119
120         if (convertInfo->dragData->protocol != DtDND_FILENAME_TRANSFER ||
121             (convertInfo->reason != DtCR_DND_CONVERT_DATA &&
122              convertInfo->reason != DtCR_DND_CONVERT_DELETE)) {
123                 return;
124         }
125
126         switch (convertInfo->reason) {
127         case DtCR_DND_CONVERT_DATA:
128
129                 /*
130                  * Supply the file names of the dragged files
131                  */
132
133                 for (ii = 0; ii < convertInfo->dragData->numItems; ii++) {
134                         sprintf(filePath, "%s/%s",
135                                 fileGetDirectoryName(DragDirectory),
136                                 iconArray[ii].name);
137                         convertInfo->dragData->data.files[ii] =
138                                 XtNewString(filePath);
139                 }
140                 break;
141         case DtCR_DND_CONVERT_DELETE:
142
143                 /*
144                  * Delete dragged files as second part of a move operation
145                  */
146
147                 fileDraw = XtNameToWidget(demoTopLevel, "*fileDraw");
148
149                 for (ii = 0; ii < convertInfo->dragData->numItems; ii++) {
150
151                         /* Remove file(s) */
152
153                         sprintf(filePath, "%s/%s",
154                                 fileGetDirectoryName(DragDirectory),
155                                 iconArray[ii].name);
156                         sprintf(command, "rm %s", filePath);
157                         if (system(command) != 0) {
158                                 printf("Unable to remove file \"%s\".\n",
159                                          filePath);
160                         }
161
162                         /* Remove icon(s) */
163
164                         if (fileDraw != NULL) {
165                                 IconDelete(fileDraw, &iconArray[ii]);
166
167                                 XClearWindow(XtDisplayOfObject(fileDraw),
168                                         (XtWindow(fileDraw)));
169                                 XtCallCallbacks(fileDraw, XmNexposeCallback,
170                                         NULL);
171                         }
172                 }
173                 break;
174         }
175 }
176
177 /*
178  * fileDragFinishCallback
179  *
180  * Free the file names allocated in fileConvertCallback()
181  */
182 void
183 fileDragFinishCallback(
184         Widget          widget,
185         XtPointer       clientData,
186         XtPointer       callData)
187 {
188         DtDndDragFinishCallbackStruct *dragFinishInfo =
189                                 (DtDndDragFinishCallbackStruct *)callData;
190         DtDndContext    *dragData = dragFinishInfo->dragData;
191         int             ii;
192
193         for (ii = 0; ii < dragData->numItems; ii++) {
194                 XtFree(dragData->data.files[ii]);
195         }
196 }
197
198 /*
199  * fileTransferCallback
200  *
201  * Handles the transfer of a file or appointment to the draw area.
202  * Adds the appropriate icon to the list of icons on the draw area.
203  */
204 void
205 fileTransferCallback(
206         Widget          widget,
207         XtPointer       clientData,
208         XtPointer       callData)
209 {
210         DtDndTransferCallbackStruct *transferInfo =
211                                 (DtDndTransferCallbackStruct *) callData;
212         IconInfo       *iconList = NULL, *iconPtr;
213         char           *filePath, *name, *contents,
214                         command[2*MAXPATHLEN + 5];
215         int             ii;
216
217         if (transferInfo == NULL) {
218                 return;
219         }
220
221         /*
222          * Verify the protocol and callback reasons
223          */
224
225         if (transferInfo->dropData->protocol != DtDND_FILENAME_TRANSFER ||
226             transferInfo->reason != DtCR_DND_TRANSFER_DATA) {
227                 return;
228         }
229
230         if (widget != NULL) {
231                 XtVaGetValues(widget, XmNuserData, &iconList, NULL);
232         }
233
234         /*
235          * Copy the dropped file(s) to the drop directory
236          */
237
238         for (ii = 0; ii < transferInfo->dropData->numItems; ii++) {
239
240                 /* Copy the file(s) */
241
242                 filePath = transferInfo->dropData->data.files[ii];
243
244                 contents = fileGetContents(filePath);
245
246                 if ((name = strrchr(filePath,'/')) == NULL) {
247                         name = filePath;
248                 } else {
249                         name++;
250                 }
251                 sprintf(command, "cp %s %s", filePath,
252                         fileGetDirectoryName(DropDirectory));
253                 if (system(command) != 0) {
254                         printf("Could not copy file \"%s\" to \"%s\".\n",
255                                 filePath, fileGetDirectoryName(DropDirectory));
256                         transferInfo->status = DtDND_FAILURE;
257                         return;
258                 }
259
260                 /* Create icon(s) for new file(s) at the drop site */
261         
262                 iconPtr = IconNew();
263                 IconInitialize(widget, iconPtr,
264                         transferInfo->x + ii * 10,
265                         transferInfo->y + ii * 10,
266                         contents, strlen(contents), name, IconByData);
267                 iconPtr->next = iconList;
268                 if (iconList != NULL) {
269                         iconList->prev = iconPtr;
270                 }
271                 iconList = iconPtr;
272                 XtVaSetValues(widget, XmNuserData, iconList, NULL);
273                 XtFree(contents);
274         }
275 }
276
277 /*
278  * fileDragSetup
279  *
280  * Prepares the file draw area to be a drag source.
281  */
282 void
283 fileDragSetup(
284         Widget          fileDraw)
285 {
286         char            translations[] = "<Btn2Down>: fileCheckForDragProc()";
287         XtTranslations  newTranslations;
288         XtActionsRec    actionTable[] = {
289                 {"fileCheckForDragProc", (XtActionProc)fileCheckForDragProc},
290         };
291
292         XtAppAddActions(demoAppContext, actionTable, 1);
293         newTranslations = XtParseTranslationTable(translations);
294         XtVaSetValues(fileDraw, XmNtranslations, newTranslations, NULL);
295
296         XtAddEventHandler(fileDraw, Button1MotionMask, False,
297                 (XtEventHandler)demoDragMotionHandler,
298                 (XtPointer)DtDND_FILENAME_TRANSFER);
299
300         fileCreateFiles(fileDraw);
301 }
302
303 /*
304  * fileDropSetup
305  *
306  * Such a function is not needed since the demoDropSetup() in demo.c registers
307  * the draw area as a drop site for drops of file names.
308  */
309
310 /*
311  * fileDragStart
312  *
313  * Initiates a file drag. The function fileCheckForDrag() first determines
314  * if the pointer is over a file icon before calling this function.
315  */
316 static void
317 fileDragStart(
318         Widget          widget,
319         XEvent         *event,
320         IconInfo       *iconArray,
321         int             numFiles)
322 {
323         static XtCallbackRec convertCBRec[] = { {fileConvertCallback, NULL},
324                                                 {NULL, NULL} };
325         static XtCallbackRec dragFinishCBRec[] =
326                                               { {demoDragFinishCallback, NULL},
327                                                 {fileDragFinishCallback, NULL},
328                                                 {NULL, NULL} };
329         Widget          dragIcon;
330         Arg             arg[1];
331
332         convertCBRec[0].closure    = (XtPointer) iconArray;
333
334         /*
335          * Set up drag icon
336          */
337
338         if (numFiles == 1) {
339                 if (iconArray[0].dragIcon == NULL) {
340                         iconArray[0].dragIcon = DtDndCreateSourceIcon(widget,
341                                 iconArray[0].bitmap, iconArray[0].mask);
342                 }
343                 dragIcon = iconArray[0].dragIcon;
344         } else {
345                 dragIcon = NULL; /* Use default multiple provided by library */
346         }
347
348         XtSetArg(arg[0], DtNsourceIcon, (XtArgVal)dragIcon);
349
350         /*
351          * Start the drag
352          */
353
354         if (DtDndDragStart(widget, event, DtDND_FILENAME_TRANSFER, numFiles,
355                         XmDROP_COPY | XmDROP_MOVE,
356                         convertCBRec, dragFinishCBRec, arg, 1)
357             == NULL) {
358
359                 printf("DragStart returned NULL.\n");
360         }
361 }
362
363 /*
364  * fileCheckForDragProc
365  *
366  * Called when button 2 is pressed in the file drag area. Calls
367  * fileCheckForDrag() to determine if the button was pressed over a file
368  * icon in which case a drag is started.
369  */
370 static XtActionProc
371 fileCheckForDragProc(
372         Widget          widget,
373         XEvent          *event,
374         String          *params,
375         Cardinal        *numParams)
376 {
377         fileCheckForDrag(widget, event, event->xbutton.x, event->xbutton.y);
378 }
379
380 /*
381  * fileCheckForDrag
382  *
383  * Determine if the pointer is over a file icon (within the drag threshold)
384  * when button 2 is pressed or when button 1 was pressed and the drag
385  * threshold has been exceeded.
386  */ 
387 void
388 fileCheckForDrag(
389         Widget          widget,
390         XEvent         *event,
391         int             initialX,
392         int             initialY)
393 {
394         IconInfo       *iconList, *iconPtr, *iconArray;
395
396         XtVaGetValues(widget, XmNuserData, &iconList, NULL);
397
398         if (iconList == NULL) {
399                 printf("Unable to locate icon list.\n");
400         }
401
402         for (iconPtr = iconList; iconPtr != NULL; iconPtr = iconPtr->next) {
403                 if ((initialX > (int)iconPtr->icon.x &&
404                      initialX < (int)(iconPtr->icon.x + iconPtr->icon.width)) &&
405                     (initialY > (int)iconPtr->icon.y &&
406                      initialY < (int)(iconPtr->icon.y + iconPtr->icon.height))){
407
408                         /*
409                          * This starts a single file drag. To start a multiple
410                          * file drag add elements to the icon array here. The
411                          * convert and transfer callbacks are already written
412                          * to handle multiple file transfers. 
413                          */
414
415                         iconArray = (IconInfo *)XtCalloc(1,sizeof(IconInfo));
416                         iconArray[0] = *iconPtr;
417
418                         fileDragStart(widget, event, iconArray, 1);
419                 }
420         }
421 }
422
423  /*************************************************************************
424  *
425  *      File Creation, Initialization & Destruction
426  *
427  *************************************************************************/
428
429 /*
430  * fileCreateDragSource
431  *
432  * Create draw area with a frame to serve as the drag source for files.
433  */
434 Widget
435 fileCreateDragSource(
436         Widget          parent)
437 {
438         Widget          fileFrame,
439                         fileDraw;
440
441         fileFrame = XtVaCreateManagedWidget("fileFrame",
442                 xmFrameWidgetClass, parent,
443                 NULL);
444
445         fileDraw = XtVaCreateManagedWidget("fileDraw",
446                 xmDrawingAreaWidgetClass, fileFrame,
447                 NULL);
448         XtAddCallback(fileDraw, XmNexposeCallback, demoDrawExposeCallback,NULL);
449
450         return fileDraw;
451 }
452
453 /*
454  * fileCreateDropSite
455  *
456  * Such a function is not needed since the drop site is the draw area which
457  * is created in demoCreateDropSite() in demo.c
458  */
459
460 /*
461  * fileCreateDirectory
462  *
463  * Create the given directory.
464  */
465 static void
466 fileCreateDirectory(
467         char            *directory)
468 {
469         char            command[MAXPATHLEN + 8];
470
471         sprintf(command, "mkdir %s", directory);
472         if (system(command) != 0) {
473                 printf("Unable to create directory \"%s\"\n", directory);
474                 exit(1);
475         }
476 }
477
478 /*
479  * fileCreateFile
480  *
481  * Given a path (partial or absolute), a file name and data create a file
482  * containing the given data using the given path.
483  */
484 static void
485 fileCreateFile(
486         char            *filePath,
487         char            *fileName,
488         char            *fileData)
489 {
490         FILE            *fp;
491         char            filePathAndName[MAXPATHLEN];
492
493         sprintf(filePathAndName, "%s/%s", filePath, fileName);
494
495         if ((fp = fopen(filePathAndName, "w")) == NULL) {
496                 printf("Cannot create file \"%s\" in current directory.\n"
497                         "Exiting...\n", filePathAndName);
498                 exit(1);
499         }
500         if (fwrite(fileData, strlen(fileData), 1, fp) != 1) {
501                 printf("Cannot write file \"%s\" in current directory.\n"
502                         "Exiting...\n", filePathAndName);
503                 exit(1);
504         }
505         fclose(fp);
506 }
507
508 /*
509  * fileCreateFiles
510  *
511  * Create drag and drop directories and the files to drag.
512  */
513 static void
514 fileCreateFiles(
515         Widget          fileDraw)
516 {
517         IconInfo        *iconList,
518                         *iconPtr;
519         char            *dragDirectory,
520                         *dropDirectory;
521         Atom            WM_DELETE_WINDOW;
522
523         WM_DELETE_WINDOW =
524           XmInternAtom(XtDisplay(demoTopLevel), "WM_DELETE_WINDOW", False);
525         XmAddWMProtocolCallback(demoTopLevel, WM_DELETE_WINDOW, fileShutdown,
526                 (XtPointer)NULL);
527
528         dragDirectory = fileGetDirectoryName(DragDirectory);
529         dropDirectory = fileGetDirectoryName(DropDirectory);
530
531         fileRemoveDirectory(dragDirectory);
532         fileCreateDirectory(dragDirectory);
533
534         fileRemoveDirectory(dropDirectory);
535         fileCreateDirectory(dropDirectory);
536
537         fileCreateFile(dragDirectory, FILE_NAME_TEXT, FILE_DATA_TEXT);
538         fileCreateFile(dragDirectory, FILE_NAME_CSH,  FILE_DATA_CSH);
539         fileCreateFile(dragDirectory, FILE_NAME_PS,   FILE_DATA_PS);
540
541         iconPtr = IconNew();
542         IconInitialize(fileDraw, iconPtr, 40, 25,
543                 FILE_DATA_TEXT, strlen(FILE_DATA_TEXT),
544                 FILE_NAME_TEXT, IconByData);
545
546         iconList = iconPtr;
547         iconPtr = IconNew();
548         iconPtr->next = iconList;
549         iconList->prev = iconPtr;
550
551         IconInitialize(fileDraw, iconPtr, 105, 25,
552                 FILE_DATA_CSH, strlen(FILE_DATA_CSH),
553                 FILE_NAME_CSH, IconByData);
554
555         iconList = iconPtr;
556         iconPtr = IconNew();
557         iconPtr->next = iconList;
558         iconList->prev = iconPtr;
559
560         IconInitialize(fileDraw, iconPtr, 75, 95,
561                 FILE_DATA_PS, strlen(FILE_DATA_PS),
562                 FILE_NAME_PS, IconByData);
563
564         iconList = iconPtr;
565
566         XtVaSetValues(fileDraw, XmNuserData, iconList, NULL);
567 }
568
569 /*
570  * fileRemoveDirectory
571  *
572  * Remove the given directory and its contents if the directory exists.
573  */
574 static void
575 fileRemoveDirectory(
576         char            *directory)
577 {
578         struct stat     fileStatus;
579         char            command[MAXPATHLEN + 8];
580
581         if (stat(directory, &fileStatus) == 0) { /* directory exists */
582                 sprintf(command, "rm -rf %s", directory);
583                 if (system(command) != 0) {
584                         printf("Unable to remove directory \"%s\"\n"
585                                 "Please remove this directory by hand "
586                                 "and try again.\n", directory);
587                         exit(1);
588                 }
589         }
590 }
591
592 /*
593  * fileShutdown
594  *
595  * Remove the temporary file
596  */
597 static void
598 fileShutdown(
599         Widget          widget,
600         XtPointer       clientData,
601         XtPointer       callData)
602 {
603         fileRemoveDirectory(fileGetDirectoryName(DragDirectory));
604         fileRemoveDirectory(fileGetDirectoryName(DropDirectory));
605 }
606
607  /*************************************************************************
608  *
609  *      File Utility Functions
610  *
611  *************************************************************************/
612
613 /*
614  * fileGetContents
615  *
616  * Open the specified file and read the contents into a buffer which is
617  * returned.
618  */
619 static char*
620 fileGetContents(
621         char            *filePath)
622 {
623         char            *contents = NULL;
624         struct stat     fileStatus;
625         FILE            *fp;
626
627         if (stat(filePath, &fileStatus) == 0) { /* file exists */
628                 contents = (char *) XtMalloc(fileStatus.st_size + 1);
629                 if ((fp = fopen(filePath, "r")) == NULL) {
630                         printf("Cannot open file \"%s\" for reading.\n",
631                                 filePath);
632                         XtFree(contents);
633                         contents = NULL;
634                 } else if (fread(contents, fileStatus.st_size, 1, fp) != 1) {
635                         printf("Cannot read file \"%s\".\n", filePath);
636                         XtFree(contents);
637                         contents = NULL;
638                 }
639                 if (contents != NULL) {
640                         contents[fileStatus.st_size] = NULL;
641                 }
642                 fclose(fp);
643         }
644         return contents;
645 }
646         
647 /*
648  * fileGetDemoDirectory
649  *
650  * Return the directory where the demo directories reside.
651  */
652 static char*
653 fileGetDemoDirectory()
654 {
655         static char     *demoDirectory = NULL;
656         char            currentDirectory[MAXPATHLEN];
657         int             status;
658
659         if (demoDirectory == NULL) {
660                 demoDirectory = (char *) getenv("DNDDEMODIR");
661                 if (demoDirectory == NULL) {
662                         demoDirectory = (char *) getcwd(NULL, MAXPATHLEN);
663                         if (demoDirectory == NULL) {
664                             sprintf(
665                                 "getcwd() could not get current directory.\n"
666                                 "\tUsing \".\" instead.\n",
667                                 NULL);
668                             demoDirectory = ".";
669                         } else {
670                             /* strip off the /tmp_mnt */
671                             if (strncmp(demoDirectory, "/tmp_mnt/", 9) == 0) {
672                                 demoDirectory += 8;
673                             }
674                         }
675                 }
676         }
677         return demoDirectory;
678 }
679
680 /*
681  * fileGetDirectoryName
682  *
683  * Gets the name of the directory where the files are dragged from or dropped
684  * to depending on which is requested.
685  */
686 static char*
687 fileGetDirectoryName(
688         DragOrDrop      dragOrDrop)
689 {
690         static char     *dragDirectory = NULL;
691         static char     *dropDirectory = NULL;
692
693         switch (dragOrDrop) {
694         case DragDirectory:
695                 if (dragDirectory == NULL) {
696                         dragDirectory = (char *) XtMalloc(MAXPATHLEN + 1);
697                         sprintf(dragDirectory, "%s/FileDragDir",
698                                 fileGetDemoDirectory());
699                 }
700                 return dragDirectory;
701                 break;
702         case DropDirectory:
703                 if (dropDirectory == NULL) {
704                         dropDirectory = (char *) XtMalloc(MAXPATHLEN + 1);
705                         sprintf(dropDirectory, "%s/FileDropDir",
706                                 fileGetDemoDirectory());
707                 }
708                 return dropDirectory;
709                 break;
710         default:
711                 return NULL;
712         }
713 }
714
715 /*
716  * fileStoreBuffer
717  *
718  * Store a buffer into a file in the drop directory.
719  * A temporary file may be created if required.
720  */
721 char *
722 fileStoreBuffer(
723         char            *name,
724         void            *buf,
725         int              len)
726 {
727         char            path[MAXPATHLEN];
728         char            *dir = fileGetDirectoryName(DropDirectory);
729         struct stat     statInfo;
730         FILE            *fp;
731
732         if (name == NULL)
733                 name = "unnamed";
734
735         sprintf(path, "%s/%s", dir, name);
736
737         if (stat(path, &statInfo) == 0) {
738                 char    *tPath;
739                 
740                 if ((tPath = tempnam(dir, name)) == NULL)
741                         return (char *)NULL;
742                 strcpy(path, tPath);
743                 free(tPath);
744         }
745
746         if ((fp = fopen(path, "w")) == NULL) {
747                 printf("Cannot create file \"%s\"\n", path);
748                 return (char *)NULL;
749         }
750
751         if (fwrite(buf, len, 1, fp) != 1) {
752                 printf("Cannot write to file \"%s\".\n", path);
753                 return (char *)NULL;
754         }
755
756         fclose(fp);
757
758         return XtNewString(path);
759 }