## 8.5 Another DnD Example

This section explains you through an example the steps needed for implementing drag and drop. The example is about using drag and drop to transfer files.

We use a ListTree widget to list directories and files. The file items will be the drag sources. We use a TextWidget for a drop target. When the user press-drag-release a file item over the window of TextWidget, the file will be loaded into the text widget.

How do we transfere files? The simplest way is to transfer the complete path of files. This will work if the file-lister and text-widget are running under the same environment. It will probabily not work if, say, the file-lister and the text-widget are running on machines across two different local networks. So we'll need a way to convert the contents of a file, just in case the text-widget cannot find the file the file-lister supplied.

So we need two target items. We have to create them first.

      MY_FILE_NAME_ATOM      = EZ_GetAtom("MY_FILE_NAME_ATOM");
      MY_FILE_CONTENTS_ATOM  = EZ_GetAtom("MY_FILE_CONTENTS_ATOM");

Now we come to the most important part, writing conversion routines. We first explain the encoders which will be registered to file-items. For each file-item, we'll record its full path and the length of the path in the client data slots of its private data structure. So encode the path name does not need too much work.

    int encodeFileName(EZ_Item *item, void *data,
                       char **message, int *length, int *needFree)
    {
      if(item)
        {
          char *ptr = (char *)EZ_GetItemPtrData(item);
          int  len = EZ_GetItemIntData(item);
          if(len > 0)
            {
              *length = len;
              *message = ptr;
              *needFree = 0;   /* we don't want to free it */
              return(EZ_DND_SUCCESS);
            }
        }
      return(EZ_DND_FAILURE);
    }

To encode the contents of a file, we'll have to figure out the length of the file and allocate a buffer to copy the the contents. We'll do this naively.

    int encodeFileContents(EZ_Item *item, void *data,
                           char **message, int *length, int *needFree)
    {
      if(item)
        {
          char *ptr = (char *)EZ_GetItemPtrData(item);
          int  len = EZ_GetItemIntData(item);
          if(len > 0)
            {
              char *msg;
              int  c, totalLength = 0;
              FILE *fp = fopen(ptr, "r");
              if(fp)
               {
                  while(fgetc(fp) != EOF) totalLength++;
                  (void)rewind(fp);
                  msg = (char *)malloc( (totalLength + 1)*sizeof(char));
                  ptr = msg;
                  while((c = fgetc(fp)) != EOF) *ptr++ =c;
                  fclose(fp);
                 *length = totalLength;
                 *message = msg;
                 *needFree = 1; /* ask EZWGL to free msg when done */
                 return(EZ_DND_SUCCESS);
              }
            }
        }
      return(EZ_DND_FAILURE);
    }

Now the decoders. They are registered to the text-widget. For file names, we'll try to open the file for reading. If succesful, we'll call `EZ_TextLoadFile` to load the file into the text-widget. Otherwise, we'll return `EZ_DND_FAILURE`.

    int decodeFileName(EZ_Widget *widget, void *data,
                       char *message, int length)
    {
      if(widget)
        {
          if(length > 0)
            {
              FILE *fp = fopen(message, "r");
              if(fp)
                {
                  fclose(fp);
                  EZ_TextLoadFile(widget, message);
                  return(EZ_DND_SUCCESS);
                }
             }
        }
      return(EZ_DND_FAILURE);
    }

To decode the file contents, we'll just treat it as a string and call `EZ_TextInsertString` to insert it into the text-widget.

    int decodeFileContents(EZ_Widget *widget, void *data,
                           char *message, int length)
    {
      if(widget)
        {
          if(length > 0)
            {
              EZ_FreezeWidget(widget);
              EZ_TextClear(widget);
              EZ_TextInsertString(widget, message);
              EZ_UnFreezeWidget(widget);
              return(EZ_DND_SUCCESS);
            }
        }
      return(EZ_DND_FAILURE);
    }

We have done the essential part needed for drag and drop. Now we'll just have to put things together and register the encoders and decoders. Unfortunately the order of registration matters. The EZwgl will try the conversion targets from the first to the last of the target list registered to an object. So we'll register the (en)decoder for target `MY_FILE_NAME_ATOM` first, since converting file names does not need too much data movement. It is the prefered target.

List below is the complete code for this example.

    /******************* Example Source ***************************************/
    #include "EZ.h"

    int     encodeFileName(EZ_Widget *, void *, char **, int *, int *);
    int     encodeFileContents(EZ_Widget *, void *, char **, int *, int *);
    void    destroyCallBack(EZ_Item *, void *);

    EZ_TreeNode *MyCreateFileItem(char *);

    Atom MY_FILE_NAME_ATOM;
    Atom MY_FILE_CONTENTS_ATOM;

    main(int ac, char **av)
    {
      EZ_Widget *frame, *listTree;
      EZ_TreeNode *root;

      EZ_Initialize(ac,av,0);

      /* the two target atoms */
      MY_FILE_NAME_ATOM = EZ_GetAtom("MY_FILE_NAME_ATOM");
      MY_FILE_CONTENTS_ATOM = EZ_GetAtom("MY_FILE_CONTENTS_ATOM");

      frame = EZ_CreateWidget(EZ_WIDGET_FRAME, NULL,
                              EZ_LABEL_STRING, "Drag sources",
                              EZ_FILL_MODE, EZ_FILL_BOTH,
    		          EZ_WIDTH, 300, EZ_HEIGHT, 400, 0);
      listTree = EZ_CreateWidget(EZ_WIDGET_TREE, frame, 0);

      /* we have to use a customized fileNode creater
       * to register DnD encoders and to remember pathnames
       */
      (void)EZ_SetDirTreeFileNodeCreator(MyCreateFileItem);
      root = EZ_CreateDirTree("./*"); /* */
      EZ_SetListTreeWidgetTree(listTree, root);

      EZ_DisplayWidget(frame);
      EZ_EventMainLoop();
    }

    EZ_TreeNode *MyCreateFileItem(char *fname)
    {
      EZ_TreeNode *node = NULL;
      if(fname)
        {
          EZ_Item *item = NULL;
          char *ptr, name[256];
          int len;

          /* don't mess with fname, work on a local copy of it */
          strcpy(name, fname);
          len = strlen(name);
          if(name[len-1] == '/') name[len-1] = 0;
          if((ptr = strrchr(name,'/'))) ptr++;
          else ptr = name;

          item = EZ_CreateLabelItem(ptr, NULL);
          if(item)
             {
              /* store the full pathname. Use destroyCallback to
               * release the allocated memory !!
               */
              char *path = (char *)malloc((len+2)*sizeof(char));
              (void)strcpy(path,fname);
              EZ_ConfigureItem(item, EZ_CLIENT_PTR_DATA, path,
                               EZ_CLIENT_INT_DATA, len,
                               EZ_DESTROY_CALLBACK, destroyCallBack, path, 0);
              EZ_ItemAddDnDDataEncoder(item,MY_FILE_NAME_ATOM,0,
                                       encodeFileName,NULL, NULL, NULL);
              EZ_ItemAddDnDDataEncoder(item,MY_FILE_CONTENTS_ATOM,0,
                                       encodeFileContents, NULL, NULL, NULL);
              node = EZ_CreateTreeNode(NULL, item);
            }
        }
      return(node);
    }

    void  destroyCallBack(EZ_Item *item, void *data)
    {
      if(data) (void)free((char *)data); /* free the allocated path */
    }

    int encodeFileName(EZ_Item *item, void *data,
                       char **message, int *length, int *needFree)
    {
      if(item)
        {
          char *ptr = (char *)EZ_GetItemPtrData(item);
          int  len = EZ_GetItemIntData(item);
          if(len > 0)
            {
              *length = len;
              *message = ptr;
              *needFree = 0;
              return(EZ_DND_SUCCESS);
            }
        }
      return(EZ_DND_FAILURE);
    }

    int encodeFileContents(EZ_Item *item, void *data,
                           char **message, int *length, int *needFree)
    {
      if(item)
        {
          char *ptr = (char *)EZ_GetItemPtrData(item);
          int  len = EZ_GetItemIntData(item);
          if(len > 0)
            {
              char *msg;
              int  c, totalLength = 0;
              FILE *fp = fopen(ptr, "r");
              if(fp) while(fgetc(fp) != EOF) totalLength++;
              (void)fseek(fp, 0L, SEEK_SET);
              msg = (char *)malloc( (totalLength + 1)*sizeof(char));
              ptr = msg;
              while((c = fgetc(fp)) != EOF) *ptr++ =c;
              fclose(fp);
             *length = totalLength;
             *message = msg;
             *needFree = 1;
             return(EZ_DND_SUCCESS);
            }
        }
      return(EZ_DND_FAILURE);
    }

    /******************* Example Target ***************************************/
    #include "EZ.h"

    int decodeFileName(EZ_Widget *, void *, char *, int);
    int decodeFileContents(EZ_Widget *, void *, char *, int);

    Atom MY_FILE_NAME_ATOM;
    Atom MY_FILE_CONTENTS_ATOM;

    main(int ac, char **av)
    {
      EZ_Widget *frame, *textW;

      EZ_Initialize(ac,av,0);

      MY_FILE_NAME_ATOM = EZ_GetAtom("MY_FILE_NAME_ATOM");
      MY_FILE_CONTENTS_ATOM = EZ_GetAtom("MY_FILE_CONTENTS_ATOM");

      frame = EZ_CreateWidget(EZ_WIDGET_FRAME,NULL,
                              EZ_LABEL_STRING, "Drag target",
                              EZ_FILL_MODE, EZ_FILL_BOTH, 0);
      textW = EZ_CreateWidget(EZ_WIDGET_TEXT, frame, 0);

      EZ_WidgetAddDnDDataDecoder(textW, MY_FILE_NAME_ATOM, 0,
                                 decodeFileName, NULL,
                                 NULL, NULL);
      EZ_WidgetAddDnDDataDecoder(textW, MY_FILE_CONTENTS_ATOM, 0,
                                 decodeFileContents, NULL,
                                 NULL, NULL);
      EZ_DisplayWidget(frame);
      EZ_EventMainLoop();
    }

    int decodeFileName(EZ_Widget *widget, void *data,
                       char *message, int length)
    {
      if(widget)
        {
          if(length > 0)
            {
              FILE *fp = fopen(message, "r");
              if(fp)
                {
                  EZ_TextLoadFile(widget, message);
                  fclose(fp);
                  return(EZ_DND_SUCCESS);
                }
            }
        }
      return(EZ_DND_FAILURE);
    }

    int decodeFileContents(EZ_Widget *widget, void *data,
                           char *message, int length)
    {
      if(widget)
        {
          if(length > 0)
            {
              EZ_FreezeWidget(widget);
              EZ_TextClear(widget);
              EZ_TextInsertString(widget, message);
              EZ_UnFreezeWidget(widget);
              return(EZ_DND_SUCCESS);
            }
        }
      return(EZ_DND_FAILURE);
    }

HTML Documentation Maintainance:`Arturo Espinosa <arturo@nuclecu.unam.mx>`