
    Using InterDesk's Graphical Shell's Drag and Drop Mediation Facility


                                Introduction

  It  is  not  extremely  difficult  to  write  an application (or modify an
  existing application) to support InterDesk's drag and drop features.   The
  resultant functionality is well worth the effort.

  Many  interesting  features  can  be  implemented  using the drag and drop
  facility.  An editor or word processor could accept files dropped  on  it,
  and  open  them  for  editing.   An interface editor could directly accept
  images and symbols from a symbol editor by having the user drag the symbol
  from the editor into the dialog, window, or picture.  A  drawing  program,
  with  its  own  palette  of  drawing  tools,  could  also accept extension
  programs, each with their own palette(s) from  which  the  user  can  drag
  drawing  tools.   It could then utilize the extension applications' tools,
  communicating with the extensions to enable them to draw the  objects  for
  the drawing program.  Many other capabilities can be designed as well.

  The InterDesk File Manager uses this same facility to drag and drop files.
  Therefore, user applications can easily  accept  dropped  files  from  the
  InterDesk  File  Manager,  or  any  other compliant file manager, for that
  matter.

  InterDesk's drag and drop (henceforth called D+D) facility is an extension
  of QNXWin's D+D capabilities.  While QNXWin allows objects to  be  dragged
  from  window  to  window on the screen, InterDesk permits the applications
  involved to actually transfer data, and tell each other exactly *WHAT* was
  dragged.

  The InterDesk Graphical Shell (henceforth called IGS) is  the  application
  which  performs this D+D "mediation."  Acting as a D+D "clearinghouse," it
  allows windows to register with it as D+D sources, targets,  or  both.   A
  source  window  can  have  objects  dragged from it, while a target window
  accepts dragged objects.

  When something is dragged, the source window will then hand IGS  the  data
  to  be  transferred, in "chunks" of up to 8k each.  The target window will
  then ask IGS for the data, a chunk at a time.

  Different standard data formats are recognized, and user applications  may
  define their own, as well.

  If IGS isn't running, then applications can simply carry on  their  normal
  duties without supporting D+D.  Most applications will not absolutely need
  the D+D facility.

  IGS does NOT support D+D operations  between  one  window  and  that  same
  window,  but  it  does  support  D+D  operations  between separate windows
  belonging to the same process; however, it is  strongly  recommended  that
  you  program  your application to handle such local D+D operations itself.
  Much speed will  be  gained,  in  that  the  data  will  not  have  to  be
  transferred  back and forth between the application and IGS.  Furthermore,
  if IGS isn't running, the application will STILL be able to drag and  drop
  locally.

  A  D+D  interface  library  has  been  provided.   Comprehensive  function
  descriptions  are  included  in  the  file "FuncRef.txt".  Versions of the
  library are supplied for each memory model.

  The InterDesk header file automatically tells the linker which library  to
  include.   All  you need to do is specify the path (in your environment or
  in a parameter to the linker) so  the  linker  knows  where  to  find  the
  libraries.   Alternatively,  you  could  link  the  libraries to /usr/lib.
  (This is automatically done by InterDesk's  install  program:  it  creates
  symlinks to the library files in /usr/lib.)

  All  IGS  D+D  interface  functions  return an error value.  This value is
  ID_SUCCESS(0) if the intended operation was successful,  or  an  InterDesk
  error value (ID_ERR_* -- defined in InterDesk.h) if something went wrong.

  A detailed description of how to use the D+D interface library follows.


                                Legal Issues

  This library, source code, and documentation are all Copyright (C) 1996 by 
  Scott C. Moonen.  All Rights Reserved.

  You may use this library, code, and  documentation  free  of  charge.   No
  royalty  fee  whatsoever  is  charged  for  use of this library, code, and
  documentation, whether by individuals, governments, or corporate entities.

  You may  freely  distribute  this  code,  but  only  under  the  following
  conditions:  If  you  redistribute this code, library, or documentation to
  others, you must distribute it in  full,  including  ALL  files  in  their
  original form, completely unmodified.


                                 Disclaimer

  Anyone who uses this library, code, and documentation does so at their own 
  risk, and bears ALL responsibility as to the outcome.  Scott  Moonen  will 
  not be held liable for any errors in this library, code, or documentation; 
  or for any results arising from their use.

  The  information  in  this  code  and  documentation  is subject to change 
  without notice.  Scott Moonen reserves the right to update or change  such 
  information at his discretion and without the consent of or any obligation 
  to any users.


                             Basic Requirements

  To  use  the  IGS  D+D  interface  library,  you  must  include  the  file
  'InterDesk.h' (in the same directory as the library and  source  code)  in
  your  source  code files.  The proper library will automatically be linked
  with your program.

  Using  the  D+D  facility  adds  a  negligible  amount  of  code  to  your
  application, and a little more than 8k of data.


                               Error Messages

  The library provides a function, IDErrMsg(), which returns a pointer to an
  error  message,  when  passed  an InterDesk error value.  The messages are
  static and SHOULD NOT BE MODIFIED BY THE USER APPLICATION.

    printf("Something's wrong!\n%s!\n",IDErrMsg(err_value));


                            Registering Windows

  For  a window to become a D+D source and/or target, it must first register
  with IGS.  It can do so using  two  functions  in  the  IGS  D+D  library:
  IDRegDragSrc(), and IDRegDragTarget().

  Each of these functions must be passed the identifier of the window to  be
  registered.   IDRegDragSrc()  takes  another parameter -- 'is_dir_win.' If
  this parameter is TRUE, then the window will be  treated  as  a  directory
  window.   Every  time  files are dragged from it, and they are modified in
  any way, it will be sent a QNXWin message with the  event  action  set  to
  QW_REDRAW  ('rd'  --  defined  in  InterDesk.h), telling it to re-read and
  redisplay its window.  If this parameter is FALSE, the window will not  be
  treated as a directory window.

    int idError;

    if((idError = IDRegDragSrc(MyWindowId,FALSE)) != ID_SUCCESS)
      Notice(NULL,"My program",NULL,NULL,"Error: %s!",IDErrMsg(idError));

    IDRegDragSrc(MyDirWinId,TRUE);

    IDRegDragTarget(MyDirWinId);

  Source  windows  should  have the 'd' (drag OUT OF pane) pane notification
  option set for the pane(s) which objects  can  be  dragged  from.   Target
  windows  should have the 'D' (drag INTO pane) pane notification option set
  for the pane(s) which objects can be dragged to.

  When closing a window, it  should  be  un-registered  with  IGS.   Windows
  registered  as  both  a source and target are not fully unregistered until
  unregistered   as   both.    The   following   functions   perform    this
  de-registration:  IDUnRegDragSrc(),  and  IDUnRegDragTarget().   They each
  take one parameter -- the window identification number.

    IDUnRegDragSrc(MyWindowId);
    IDUnRegDragSrc(MyDirWinId);
    IDUnRegDragTarget(MyDirWinId);

  Since IGS is a QNX server process,  it  will  automatically  unregister  a 
  process's  windows  when  the  process dies.  However, it is best to still 
  manually unregister a D+D window whenever the window is closed.


                   Dragging Objects From a Source Window

  When  the user selects something in a source window that can be dragged to
  any other window, the source application should call  the  DragArea()  and
  Drag() functions to drag the object anywhere on the screen.

    int delta_row,delta_col;

    DragArea(0,0,0,0,"M");
    Drag("DummyDragElement",&delta_row,&delta_col);

  The program should then compare the delta values of the  element  and  the
  initial  coordinates  of  the  element, against the size and offset of the
  pane's view.  It may use this to determine whether the element was dragged
  somewhere within the window, or outside  the  window,  and  handle  either
  case.   If  the element was dragged outside the window, the program should
  do nothing for now.

    /* Test the delta values against the initial coordinates of the  */
    /*   window, and the size and offset of the pane.                */

    if( ... )
    {
      /* Object was dragged somewhere WITHIN the window.  */

      /* Handle local dragging.  */
    }

  Later, when the source window receives a QW_DRAGGED event message, stating
  that  an  object  was  dragged out of the window, the process should first
  determine whether the object was dragged to any other drag-enabled windows
  of the current process, if any such exist.  If so, it should handle  local
  dragging between those windows.  Otherwise, it should then ask IGS whether
  the other window involved is registered as a drag target.  It can do so by
  calling the library function IDTargetQuery().

    QW_EVENT_MSG  event;
    int           event_id;
    short         is_target;

    ...

    for(;;)
    {
      GetEvent(0,&event,sizeof(QW_EVENT_MSG));

      event_id = Event(&event);

      /* Handle dialog messages.  */

      ...

      switch(event_id)
      {
        ...

        case QW_DRAGGED:                /* Element dragged from window.  */
          if(event.hdr.code != 'F')
            break;

          if(event.window.other_window == MyOtherWindow)
          {
            HandleLocalDrags();
            break;
          }

          IDTargetQuery(event.window.other_window,&is_target);

          if(!is_target)
            break;

          /* Process drag.  */

          ...

          break;

        ...
      }
    }

  If the other window involved IS registered as a target window, it may then
  proceed  to begin a drag transfer operation, using the IDDrag*() functions
  (these are used by a drag source -- the IDDrop*() functions are used by  a
  drag target).

  The  IDDragStart()  function  begins  the drag transfer.  The program must
  specify the source and target windows, the number of chunks of  data  that
  will be transferred, and the data type (a 32-char, NULL-terminated string.
  See  below for more details).  The function returns, through the 'drag_id'
  parameter, an ID number for the drag operation, to be used in all  further
  "correspondence" for that drag transfer.

    /* Process drag.  */

    short   num_chunks;
    short   ix;
    long    drag_id;
    void   *cur_chunk;
    short   sizeof_chunk;

    ...

    num_chunks = CountNumChunks();

    IDDragStart(MyWindowId,event.window.other_window,num_chunks,
                "MyOwnDataFormat",&drag_id);

  The  source process may then transfer the drag data, a chunk at a time, to
  IGS, using the IDDragTransfer() function.  Chunks may  not  exceed  8k  in
  size.

    for(ix = 0;ix < num_chunks;ix++)
    {
      cur_chunk = FormatChunk(ix,&sizeof_chunk);
      IDDragTransfer(drag_id,cur_chunk,sizeof_chunk);
      free(cur_chunk);
    }

  Once  this  is  completed,  the  source  process  must call IDDragEnd() to
  terminate the drag operation.

    IDDragEnd(drag_id);

  This will end all further interaction between the source process  and  IGS
  for  that  drag operation, unless the source window is a directory window,
  and files were dragged, and they were modified in some way.  In this case,
  the source window will receive a QNXWin event with the event action set to
  QW_REDRAW ('rd' -- defined  in  InterDesk.h),  telling  it  that  it  must
  re-read and redisplay its window.

    switch(event_id)
    {
      ...

      case QW_REDRAW:                   /* Re-read and redisplay.  */
        ReReadWindow();
        ReDisplayWindow();
        break;

      ...
    }

  Please  note  that in real-world applications, you should check the return
  values of the InterDesk library functions to see if an error occurred.


                    Dragging Objects To a Target Window

  When a target window receives a QW_DRAGGED event message, stating that  an
  object was dragged into the window, the process should ask IGS whether the
  other  window  involved is registered as a drag source, using the function
  IDSourceQuery().

    QW_EVENT_MSG  event;
    int           event_id;
    short         is_source;

    ...

    for(;;)
    {
      GetEvent(0,&event,sizeof(QW_EVENT_MSG));

      event_id = Event(&event);

      /* Handle dialog messages.  */

      ...

      switch(event_id)
      {
        ...

        case QW_DRAGGED:                /* Element dragged to window.  */
          if(event.hdr.code != 'T')
            break;

          IDSourceQuery(event.window.other_window,&is_source);

          if(!is_source)
            break;

          /* Process drop.  */

          ...

          break;

        ...
      }
    }

  If the other window involved IS registered as a source window, it may then
  proceed  to begin a drop transfer operation, using the IDDrop*() functions
  (these are used by a drag target -- the IDDrag*() functions are used by  a
  drag source).

  The  IDDropStart()  function  begins  the drop transfer.  The program must
  specify the source and target  windows.   The  function  returns,  through
  pointers, the number of chunks, data type, and drag identification number,
  which  is  to  be  used  in  all  further  "correspondence"  for that drop
  transfer.

    /* Process drop.  */

    short   num_chunks;
    short   ix;
    long    drop_id;
    char    cur_chunk[8192];
    short   sizeof_chunk;
    char    data_format;

    ...

    IDDropStart(event.window.other_window,MyWindowId,&num_chunks,
                data_format,&drop_id);

  The  target process may then check the drag data format, to see whether it
  wishes to accept the data.  If not, it may call IDDropEnd() right then and
  there.  Otherwise, it should call IDDropTransfer()  to  receive  the  drop
  data, one chunk at a time.

    if(strcmp(data_format,"MyOwnDataFormat") == 0)
    {
      for(ix = 0;ix < num_chunks;ix++)
      {
        IDDropTransfer(drag_id,cur_chunk,&sizeof_chunk);
        InsertChunk(cur_chunk,sizeof_chunk);
      }

      IDDropEnd(drop_id,FALSE);
    }
    else if(strcmp(data_format,"TheirOwnDataFormat") == 0)
    {
      ...
    }
    else if(strcmp(data_format,"SomebodyElse'sDataFormat") == 0)
    {
      ...
    }
    else
      IDDropEnd(drop_id,FALSE);

  When calling IDDropEnd() to terminate the drop transfer, you must  specify
  whether  the  drag  operation  was a file operation, and whether any files
  were changed, using the 'file_changed' parameter.  If TRUE, and the source
  window is a directory window, it will be told to re-read and redisplay its
  window as a result of the operation.

  Calling IDDropEnd() will end all further interaction  between  the  target
  process and IGS for that drop operation.

  Please  note  that in real-world applications, you should check the return
  values of the InterDesk library functions to see if an error occurred.


                                  Caveats

  It should be noted that IGS will delete a D+D operation from  its  buffers 
  if  more  than  twenty  minutes  has  elapsed since the operation was last 
  accessed (by either the source or  the  target).   This  is  considered  a 
  reasonable  amount  of  time: not too long for any programs which may take 
  some time to process the data, but not too short for  any  programs  which 
  sloppily leave the drag operation unfinished, and the data lying around.


                    InterDesk Drag and Drop Data Formats

  InterDesk's D+D data  format  names  can  be  up  to  32  characters  long
  (including terminating NULL).  *ALL* names starting with a slash character
  ('/')   are   reserved   for  InterDesk's  standard  data  formats.   *NO*
  user-defined names should start with  this  character.   However,  if  you
  would  like  to  submit  a  user-defined format for consideration as a new
  standard format, you may do so.

  Applications  need  not  accept  all  of  these  formats.   They  may call
  IDDropEnd() before they have even read one chunk of data.

  Applications  need not use all of the data in a transfer.  For instance, a
  program may only want the first filename; or the first picture element; or
  the first symbol or image in a picture.

  Consult other appropriate documentation to find out the  exact  format  of 
  data types, such as RTF.

  IGS does not check the data to ascertain  that  it  is  of  the  specified
  format.

  The names and descriptions of the InterDesk standard data formats follow:

    /FileList

    A  list of files and directories.  These files need not be from the same 
    directory; in fact, they need not be from the same device or node.  Each 
    file name is terminated by a single NULL character.  Two NULL characters 
    terminate the last filename in a chunk (the  chunk  size  includes  both 
    terminating  NULL's).   The  list  may  only  be  broken  apart  BETWEEN 
    filenames  to split it into multiple chunks -- the end of any chunk must 
    be the end of a filename, followed by two NULLS.  The file names  should 
    include  the  COMPLETE  path,  INCLUDING NODE NUMBER, of the files.  The 
    InterDesk  File  Manager  will  not   accept   any   files   without   a 
    fully-qualified, network-unique path.

    If an application wishes to perform file operations (delete, move, copy,
    etc.) on files that it receives, it  should  check  the  status  of  the
    Shift, Alt, and Control keys when the files were dropped before deciding
    which operation to perform.  (This data is in the QNXWin event message.)
    InterDesk  uses  the  following  key combinations, and it is recommended
    that you use these as well.

      Ctrl+Drop             Copy operation.
      Shift+Drop            Link operation.
      Drop(no modifiers)    Simple move operation.

    If multiple keys are held down, the ones at the top  of  the  list  take
    precedence over others.

    Deletion can be implemented in the same manner as moving -- 'moving' the 
    files to a 'trashcan' or 'shredder' to be destroyed.

    It is recommended that, if a program plans to process  the  files  in  a 
    lengthy  operation,  it read all of the file names into its own internal 
    buffer or array.  It should then terminate the drag operation with  IGS, 
    and  continue  its  operation  using  the  downloaded data.  This is the 
    method that the InterDesk File Manager uses for all of its external  D+D 
    operations.


    /RTF

    Microsoft's Rich Text Format (RTF).  This is a standard, highly capable,
    text-based (i.e., only ASCII characters) data  format  for  transferring
    formatted  text  between  differing, incompatible word processors.  This
    data format may be broken apart at ANY point to split it  into  multiple
    chunks.  RTF chunks must, however, be NULL-terminated.


    /ASCII_para

    ASCII  text,  with  paragraphs.  Each paragraph is a single line.  It is
    the responsibility of  the  recipient  to  wrap  the  paragraph  out  to
    multiple  lines,  if  it  is  long  enough  to  require that.  Linefeeds
    separate paragraphs.  This data format may be broken apart at ANY  point
    to  split it into multiple chunks.  An "end-of-chunk" is NOT interpreted
    as a linefeed, or a  space,  or  anything  else  --  chunks  are  merely
    concatenated  after  receiving  them.   This  format  of  chunk  must be
    NULL-terminated.


    /ASCII_line

    Line-based ASCII, without paragraphs.  Even what may appear to the  user
    to  be  a  "paragraph"  is transmitted as multiple lines, with linefeeds
    separating them.  Please note that very  long  lines  can  still  exist;
    however,  in  this  format,  they  are  long  lines, NOT paragraphs.  An
    "end-of-chunk" is NOT interpreted as a linefeed, or a space, or anything
    else -- chunks are  merely  concatenated  after  receiving  them.   This
    format of chunk must be NULL-terminated.


    /Pict

    A standard QNXWin picture, which can  contain  multiple  elements.   ALL
    QNXWin  elements,  even  groups, may be included.  However, inclusion of
    link elements in drag-and-drop  pictures  is  highly  discouraged.   The
    picture  may  be  broken  apart  at  any point to split it into multiple
    chunks.  No terminating NULL is needed  at  the  end  of  a  chunk  (QNX
    Windows mandates, however, that a NULL terminate a picture).
