home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / TCL / BLT / BLT1.7L1 / BLT1 / blt-1.7 / src / bltDragDrop.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-14  |  70.2 KB  |  2,565 lines

  1. /*
  2.  * ------------------------------------------------------------------------
  3.  *  PURPOSE:  drag&drop widget registration facility
  4.  *
  5.  *  Allows widgets to be registered as drag&drop sources and targets
  6.  *  for handling "drag-and-drop" operations between Tcl/Tk applications.
  7.  *
  8.  *  USAGE:
  9.  *    blt_drag&drop source
  10.  *    blt_drag&drop source <pathName>
  11.  *    blt_drag&drop source <pathName> config ?options...?
  12.  *    blt_drag&drop source <pathName> handler ?<dataType> <cmd>...?
  13.  *
  14.  *    blt_drag&drop target
  15.  *    blt_drag&drop target <pathName> handler
  16.  *    blt_drag&drop target <pathName> handler ?<dataType> <cmd>...?
  17.  *    blt_drag&drop target <pathName> handle <dataType>
  18.  *
  19.  *    blt_drag&drop drag <pathName> <x> <y>
  20.  *    blt_drag&drop drop <pathName> <x> <y>
  21.  *
  22.  *    blt_drag&drop errors ?<proc>?
  23.  *    blt_drag&drop active
  24.  *    blt_drag&drop location ?<x> <y>?
  25.  *
  26.  * ------------------------------------------------------------------------
  27.  *  AUTHOR:  Michael J. McLennan       Phone: (215)770-2842
  28.  *           AT&T Bell Laboratories   E-mail: aluxpo!mmc@att.com
  29.  *
  30.  *     RCS:  bltDragDrop.c,v 1.10 1994/04/14 21:11:03 gah Exp
  31.  * ========================================================================
  32.  *                 Copyright (c) 1993-1994  AT&T Bell Laboratories
  33.  * ========================================================================
  34.  * Permission to use, copy, modify, and distribute this software and its
  35.  * documentation for any purpose and without fee is hereby granted,
  36.  * provided that the above copyright notice appear in all copies and that
  37.  * both that the copyright notice and warranty disclaimer appear in
  38.  * supporting documentation, and that the names of AT&T Bell Laboratories
  39.  * any of their entities not be used in advertising or publicity
  40.  * pertaining to distribution of the software without specific, written
  41.  * prior permission.
  42.  * 
  43.  * AT&T disclaims all warranties with regard to this software, including
  44.  * all implied warranties of merchantability and fitness.  In no event
  45.  * shall AT&T be liable for any special, indirect or consequential
  46.  * damages or any damages whatsoever resulting from loss of use, data or
  47.  * profits, whether in an action of contract, negligence or other
  48.  * tortuous action, arising out of or in connection with the use or
  49.  * performance of this software.
  50.  * ========================================================================
  51.  */
  52. #include "blt.h"
  53. #include <X11/Xatom.h>
  54.  
  55. #ifndef DRAGDROP_VERSION
  56. #define DRAGDROP_VERSION "2.1"
  57. #endif
  58.  
  59. #define DRAGDROP_COMMAND    "blt_drag&drop"   /* Command name */
  60. #define DRAGDROP_CLASS        "DragDrop"        /* CLASS NAME for token window */
  61. #define DRAGDROP_PROPINFO    "BltDragDropInfo" /* Property name */
  62. #define DRAGDROP_ERRORPROC    "tkerror"          /* Error Proc used to report 
  63.                                                * drag&drop background errors */
  64.  
  65. #ifndef _BLT_H
  66. /*
  67.  *  -------------  The following color definitions are taken directly
  68.  *  >> WARNING <<  from "default.h" in the Tk distribution, and should
  69.  *  -------------  be kept up to date with those conventions.
  70.  */
  71. #define BLACK       "Black"
  72. #define WHITE       "White"
  73. #define GRAY        "#b0b0b0"
  74.  
  75. #define BISQUE1     "#ffe4c4"
  76. #define BISQUE2     "#eed5b7"
  77. #define BISQUE3     "#cdb79e"
  78. #endif                            /* _BLT_H */
  79. #define DEF_BUTTON_ACTIVE_BG_COLOR  BISQUE2
  80. #define DEF_BUTTON_ACTIVE_BG_MONO   BLACK
  81. #define DEF_BUTTON_ACTIVE_FG_COLOR  BLACK
  82. #define DEF_BUTTON_ACTIVE_FG_MONO   WHITE
  83. #define DEF_BUTTON_BG_COLOR     BISQUE1
  84. #define DEF_BUTTON_BG_MONO      WHITE
  85. #define DEF_TOKEN_OUTLINE_COLOR     BLACK
  86. #define DEF_TOKEN_OUTLINE_MONO      BLACK
  87.  
  88. /*
  89.  *  DRAG&DROP ROOT WINDOW HIERARCHY (cached during "drag" operations)
  90.  */
  91. typedef struct DD_WinRep {
  92.     Window win;               /* X window for this record */
  93.     int initialized;          /* non-zero => rest of info is valid */
  94.     int x0, y0;               /* upper-left corner of window */
  95.     int x1, y1;               /* lower-right corner of window */
  96.     char *ddprop;             /* drag&drop property info */
  97.     char *ddinterp;           /* interp name within ddprop */
  98.     char *ddwin;              /* target window name within ddprop */
  99.     char *ddhandlers;         /* list of handlers within ddprop */
  100.     struct DD_WinRep* parent; /* window containing this as a child */
  101.     struct DD_WinRep* kids;   /* list of child windows */
  102.     struct DD_WinRep* next;   /* next sibling */
  103. } DD_WinRep;
  104.  
  105. /*
  106.  *  DRAG&DROP REGISTRATION DATA
  107.  */
  108. typedef struct {
  109.     Tcl_Interp *interp;       /* interpreter managing this drag&drop command */
  110.     Tk_Window root;           /* main window for application */
  111.     Tcl_HashTable srcList;    /* list of source widgets */
  112.     Tcl_HashTable trgList;    /* list of target widgets */
  113.     char *errorProc;          /* proc invoked for drag&drop errors */
  114.     int numactive;            /* number of active drag&drop operations */
  115.     int locx, locy;           /* last location point */
  116.     DD_WinRep *pool;          /* pool of available DD_WinRep records */
  117. } DD_RegList;
  118.  
  119. typedef struct {
  120.     DD_RegList *ddlist;       /* parent registration list */
  121.     Tk_Window tkwin;          /* registered window */
  122. } DD_RegEntry;
  123.  
  124. /*
  125.  *  DRAG&DROP SOURCE REGISTRATION RECORD
  126.  */
  127. typedef struct DD_SourceHndl {
  128.     char *dataType;               /* name of data type */
  129.     char *cmd;                    /* command used to send data */
  130.     struct DD_SourceHndl* next;   /* next handler in linked list */
  131. } DD_SourceHndl;
  132.  
  133. typedef struct {
  134.     DD_RegList *ddlist;           /* registration list containing this */
  135.  
  136.     Display *display;             /* drag&drop source window display */
  137.     Tk_Window tkwin;              /* drag&drop source window */
  138.     Atom ddAtom;                  /* X atom referring to "DragDropInfo" */
  139.     int button;                   /* button used to invoke drag for sources */
  140.  
  141.     Tk_Window tokenwin;           /* window representing drag item */
  142.     Tk_Anchor tokenAnchor;        /* position of token win relative to mouse */
  143.     Cursor tokenCursor;           /* cursor used when dragging token */
  144.     Tk_3DBorder tokenOutline;     /* outline around token window */
  145.     Tk_3DBorder tokenBorder;      /* border/background for token window */
  146.     int tokenBorderWidth;         /* border width in pixels */
  147.     XColor *rejectFg;             /* color used to draw rejection fg: (\) */
  148.     XColor *rejectBg;             /* color used to draw rejection bg: (\) */
  149.     Pixmap rejectSt;              /* stipple used to draw rejection: (\) */
  150.     GC rejectFgGC;                /* GC used to draw rejection fg: (\) */
  151.     GC rejectBgGC;                /* GC used to draw rejection bg: (\) */
  152.  
  153.     int pkgcmdInProg;             /* non-zero => executing pkgcmd */
  154.     char *pkgcmd;                 /* cmd executed on start of drag op */
  155.     char *pkgcmdResult;           /* result returned by recent pkgcmd */
  156.     char *sitecmd;                /* cmd executed to update token win */
  157.  
  158.     DD_WinRep *allwins;           /* window info (used during "drag") */
  159.     int selfTarget;               /* non-zero => source can drop onto itself */
  160.     int overTargetWin;            /* non-zero => over target window */
  161.     int tokenx, tokeny;           /* last position of token window */
  162.     Tk_TimerToken hidetoken;      /* token for routine to hide tokenwin */
  163.     Cursor normalCursor;          /* cursor restored after dragging */
  164.  
  165.     char *send;                   /* list of data handler names or "all" */
  166.     DD_SourceHndl *handlers;      /* list of data handlers */
  167. } DD_Source;
  168.  
  169. /*
  170.  *  STACK INFO
  171.  */
  172. typedef struct DD_Stack {
  173.     ClientData *values;     /* values on stack */
  174.     int len;                /* number of values on stack */
  175.     int max;                /* maximum size of stack */
  176.     ClientData space[5];    /* initial space for stack data */
  177. } DD_Stack;
  178.  
  179. /*
  180.  *  CONFIG PARAMETERS
  181.  */
  182. static Tk_ConfigSpec SourceConfigSpecs[] = {
  183.  
  184.     {TK_CONFIG_INT,
  185.         "-button", "buttonBinding", "ButtonBinding",
  186.         "3", Tk_Offset(DD_Source, button),
  187.         0},
  188.  
  189.     {TK_CONFIG_STRING,
  190.         "-packagecmd", "packageCommand", "Command",
  191.         NULL, Tk_Offset(DD_Source, pkgcmd),
  192.         TK_CONFIG_NULL_OK},
  193.  
  194.     {TK_CONFIG_COLOR,
  195.         "-rejectbg", "rejectBackground", "Background",
  196.         DEF_BUTTON_BG_COLOR, Tk_Offset(DD_Source, rejectBg),
  197.         TK_CONFIG_COLOR_ONLY},
  198.     {TK_CONFIG_COLOR,
  199.         "-rejectbg", "rejectBackground", "Background",
  200.         "white", Tk_Offset(DD_Source, rejectBg),
  201.         TK_CONFIG_MONO_ONLY},
  202.  
  203.     {TK_CONFIG_COLOR,
  204.         "-rejectfg", "rejectForeground", "Foreground",
  205.         "red", Tk_Offset(DD_Source, rejectFg),
  206.         TK_CONFIG_COLOR_ONLY},
  207.     {TK_CONFIG_COLOR,
  208.         "-rejectfg", "rejectForeground", "Foreground",
  209.         "black", Tk_Offset(DD_Source, rejectFg),
  210.         TK_CONFIG_MONO_ONLY},
  211.  
  212.     {TK_CONFIG_BITMAP,
  213.         "-rejectstipple", "rejectStipple", "Stipple",
  214.         (char*)NULL, Tk_Offset(DD_Source, rejectSt),
  215.         TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
  216.     {TK_CONFIG_BITMAP,
  217.         "-rejectstipple", "rejectStipple", "Stipple",
  218.         "gray50", Tk_Offset(DD_Source, rejectSt),
  219.         TK_CONFIG_MONO_ONLY},
  220.  
  221.     {TK_CONFIG_BOOLEAN,
  222.         "-selftarget", "selfTarget", "SelfTarget",
  223.         "no", Tk_Offset(DD_Source, selfTarget),
  224.         0},
  225.  
  226.     {TK_CONFIG_STRING,
  227.         "-send", "send", "Send",
  228.         "all", Tk_Offset(DD_Source, send),
  229.         TK_CONFIG_NULL_OK},
  230.  
  231.     {TK_CONFIG_STRING,
  232.         "-sitecmd", "siteCommand", "Command",
  233.         NULL, Tk_Offset(DD_Source, sitecmd),
  234.         TK_CONFIG_NULL_OK},
  235.  
  236.     {TK_CONFIG_ANCHOR,
  237.         "-tokenanchor", "tokenAnchor", "Anchor",
  238.         "center", Tk_Offset(DD_Source, tokenAnchor),
  239.         0},
  240.  
  241.     {TK_CONFIG_BORDER,
  242.         "-tokenbg", "tokenBackground", "Background",
  243.         DEF_BUTTON_BG_COLOR, Tk_Offset(DD_Source, tokenBorder),
  244.         TK_CONFIG_COLOR_ONLY},
  245.     {TK_CONFIG_BORDER,
  246.         "-tokenbg", "tokenBackground", "Background",
  247.         DEF_BUTTON_BG_MONO, Tk_Offset(DD_Source, tokenBorder),
  248.         TK_CONFIG_MONO_ONLY},
  249.  
  250.     {TK_CONFIG_BORDER,
  251.         "-tokenoutline", "tokenOutline", "Outline",
  252.         DEF_TOKEN_OUTLINE_COLOR, Tk_Offset(DD_Source, tokenOutline),
  253.         TK_CONFIG_COLOR_ONLY},
  254.     {TK_CONFIG_BORDER,
  255.         "-tokenoutline", "tokenOutline", "Outline",
  256.         DEF_TOKEN_OUTLINE_MONO, Tk_Offset(DD_Source, tokenOutline),
  257.         TK_CONFIG_MONO_ONLY},
  258.  
  259.     {TK_CONFIG_PIXELS,
  260.         "-tokenborderwidth", "tokenBorderWidth", "BorderWidth",
  261.         "3", Tk_Offset(DD_Source, tokenBorderWidth),
  262.         0},
  263.  
  264.     {TK_CONFIG_CURSOR,
  265.         "-tokencursor", "tokenCursor", "Cursor",
  266.         "center_ptr", Tk_Offset(DD_Source, tokenCursor),
  267.         TK_CONFIG_NULL_OK},
  268.  
  269.     {TK_CONFIG_END,
  270.         (char*)NULL, (char*)NULL, (char*)NULL,
  271.         (char*)NULL, 0,
  272.         0},
  273. };
  274.  
  275.  
  276. /*
  277.  *  DRAG&DROP TARGET REGISTRATION RECORD
  278.  */
  279. typedef struct DD_TargetHndl {
  280.     char *dataType;               /* Name of data type (malloc-ed) */
  281.     char *command;                /* command to handle data type (malloc-ed) */
  282.     struct DD_TargetHndl* next;   /* next handler in linked list */
  283. } DD_TargetHndl;
  284.  
  285. typedef struct {
  286.     DD_RegList *ddlist;           /* registration list containing this */
  287.  
  288.     Display *display;             /* drag&drop target window display */
  289.     Tk_Window tkwin;              /* drag&drop target window */
  290.     DD_TargetHndl *handlers;      /* list of data handlers */
  291. } DD_Target;
  292.  
  293. /*
  294.  * Each "drag&drop" widget window is tagged with a "DragDropInfo"
  295.  * property in XA_STRING format.  This property identifies the
  296.  * window as a "drag&drop" widget, and contains the following:
  297.  *
  298.  *     "<interp-name>]<drag&drop-path>]<handler-list>"
  299.  *
  300.  * The <drag&drop-path> is the window path name of the drag&drop
  301.  * widget, <interp-name> is the name of the interpreter controlling
  302.  * the widget (useful for the "send" command), and <handler-list>
  303.  * is the list of handler types recognized by the widget.
  304.  *
  305.  * When the user invokes the "drag" operation, a snapshot of the
  306.  * entire window hierarchy is made, and windows carrying a
  307.  * "DragDropInfo" property are identified.  As the token window is
  308.  * dragged around, * this snapshot can be queried to determine when
  309.  * the token is over a valid target window.  When the token is
  310.  * dropped over a valid site, the drop information is sent to the
  311.  * application via the usual "send" command.  If communication fails,
  312.  * the drag&drop facility automatically posts a rejection symbol on
  313.  * the token window.
  314.  */
  315.  
  316. /*
  317.  *  Maximum size property that can be read at one time:
  318.  */
  319. #define MAX_PROP_SIZE 1000
  320.  
  321. /*
  322.  *  FORWARD DECLARATIONS
  323.  */
  324. int Blt_DragDropInit _ANSI_ARGS_((Tcl_Interp* interp));
  325.  
  326. static void DragDrop_Delete _ANSI_ARGS_((ClientData clientData));
  327. static int DragDrop_Cmd _ANSI_ARGS_((ClientData clientData,
  328.     Tcl_Interp *interp, int argc, char **argv));
  329.  
  330. static DD_Source* GetSourceInfo _ANSI_ARGS_((DD_RegList *ddlist,
  331.     char *pathname, int *newEntry));
  332. static void DestroySourceInfo _ANSI_ARGS_((DD_RegList *ddlist,
  333.     char *pathName));
  334. static int ConfigSource _ANSI_ARGS_((Tcl_Interp *interp, DD_Source *dsPtr,
  335.     int argc, char **argv, int flags));
  336. static char* FindSourceHandler _ANSI_ARGS_((DD_Source* dsPtr, char *dtname));
  337. static void PutSourceHandler _ANSI_ARGS_((DD_Source* dsPtr, char *dtname,
  338.     char *cmd));
  339. static DD_SourceHndl* CreateSourceHandler _ANSI_ARGS_((char *dtname,
  340.     char *cmd));
  341. static void DestroySourceHandler _ANSI_ARGS_((DD_SourceHndl *dsHndl));
  342. static void UnregSource _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr));
  343.  
  344. static DD_Target* GetTargetInfo _ANSI_ARGS_((DD_RegList *ddlist,
  345.     char *pathname, int *newEntry));
  346. static void DestroyTargetInfo _ANSI_ARGS_((DD_RegList *ddlist,
  347.     char *pathName));
  348. static char* FindTargetHandler _ANSI_ARGS_((DD_Target* dtPtr, char *dtname));
  349. static void PutTargetHandler _ANSI_ARGS_((DD_Target* dtPtr, char *dtname,
  350.     char *cmd));
  351. static DD_TargetHndl* CreateTargetHandler _ANSI_ARGS_((char *dtname,
  352.     char *cmd));
  353. static void DestroyTargetHandler _ANSI_ARGS_((DD_TargetHndl *dtHndl));
  354. static void UnregTarget _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr));
  355.  
  356. static void DragDropSend _ANSI_ARGS_((DD_Source *dsPtr));
  357. static char* DragDropSendHndlr _ANSI_ARGS_((DD_Source *dsPtr,
  358.     char *interpName, char *ddName));
  359.  
  360. static DD_WinRep* GetWinRepInfo _ANSI_ARGS_((DD_Source *dsPtr,
  361.     DD_RegList *ddlist));
  362. static DD_WinRep* FindTargetWin _ANSI_ARGS_((DD_Source *dsPtr, int x, int y));
  363. static DD_WinRep* WinRepAlloc _ANSI_ARGS_((DD_RegList* ddlist));
  364. static void WinRepRelease _ANSI_ARGS_((DD_WinRep* wr, DD_RegList* ddlist));
  365. static void WinRepInit _ANSI_ARGS_((DD_WinRep* wr, DD_Source *dsPtr));
  366.  
  367. static void AddDDProp _ANSI_ARGS_((DD_Target *dtPtr));
  368. static void DDTokenEventProc _ANSI_ARGS_((ClientData, XEvent*));
  369. static void MoveDDToken _ANSI_ARGS_((DD_Source *dsPtr));
  370. static void UpdateDDToken _ANSI_ARGS_((ClientData clientData));
  371. static void HideDDToken _ANSI_ARGS_((ClientData clientData));
  372. static void RejectDDToken _ANSI_ARGS_((DD_Source* dsPtr));
  373.  
  374. static void StackInit _ANSI_ARGS_((DD_Stack *stack));
  375. static void StackDelete _ANSI_ARGS_((DD_Stack *stack));
  376. static void StackPush _ANSI_ARGS_((ClientData cdata, DD_Stack *stack));
  377. static ClientData StackPop _ANSI_ARGS_((DD_Stack *stack));
  378.  
  379.  
  380. /*
  381.  * ------------------------------------------------------------------------
  382.  *  Blt_DragDropInit()
  383.  *
  384.  *  Adds the drag&drop command to the given interpreter.  Should be
  385.  *  invoked to properly install the command whenever a new interpreter
  386.  *  is created.
  387.  * ------------------------------------------------------------------------
  388.  */
  389. int
  390. Blt_DragDropInit(interp)
  391.     Tcl_Interp *interp;  /* interpreter to be updated */
  392. {
  393.     DD_RegList *ddlist;
  394.     Tk_Window tkwin;
  395.     Tcl_CmdInfo cmdInfo;
  396.  
  397.     /*
  398.      *  See if drag&drop is already installed.
  399.      */
  400.     if (Tcl_GetCommandInfo(interp, DRAGDROP_COMMAND, &cmdInfo))
  401.     {
  402.         Tcl_ResetResult(interp);
  403.         Tcl_AppendResult(interp, "already installed: ", DRAGDROP_COMMAND,
  404.                          (char*)NULL);
  405.         return TCL_ERROR;
  406.     }
  407.  
  408.     /*
  409.      *  Make sure that this is a Tk application.
  410.      */
  411.     tkwin = Tk_MainWindow(interp);
  412.     if (tkwin == None)
  413.     {
  414.         Tcl_ResetResult(interp);
  415.         Tcl_AppendResult(interp, "requires Tk facilities: ", DRAGDROP_COMMAND,
  416.                          (char*)NULL);
  417.         return TCL_ERROR;
  418.     }
  419.  
  420.     /*
  421.      *  Install drag&drop facilities.
  422.      */
  423.     ddlist = (DD_RegList*)malloc(sizeof(DD_RegList));
  424.     ddlist->interp = interp;
  425.     ddlist->root = tkwin;
  426.     Tcl_InitHashTable(&ddlist->srcList,TCL_STRING_KEYS);
  427.     Tcl_InitHashTable(&ddlist->trgList,TCL_STRING_KEYS);
  428.     ddlist->errorProc = strdup(DRAGDROP_ERRORPROC);
  429.     ddlist->numactive = 0;
  430.     ddlist->locx = ddlist->locy = 0;
  431.     ddlist->pool = NULL;
  432.  
  433.     Tcl_CreateCommand(interp, DRAGDROP_COMMAND, DragDrop_Cmd,
  434.         (ClientData)ddlist, DragDrop_Delete);
  435.  
  436.     Tcl_SetVar2(interp, "blt_versions", DRAGDROP_COMMAND, DRAGDROP_VERSION,
  437.         TCL_GLOBAL_ONLY);
  438.  
  439.     return TCL_OK;
  440. }
  441.  
  442. /*
  443.  * ------------------------------------------------------------------------
  444.  *  DragDrop_Delete()
  445.  *
  446.  *  Invoked when the drag&drop command is removed from an interpreter
  447.  *  to free up allocated memory.
  448.  * ------------------------------------------------------------------------
  449.  */
  450. static void
  451. DragDrop_Delete(cdata)
  452.     ClientData cdata;    /* client data for drag&drop command */
  453. {
  454.     DD_RegList *ddlist = (DD_RegList*)cdata;
  455.     DD_WinRep *wrpool, *wrnext;
  456.  
  457.     Tcl_DeleteHashTable(&ddlist->srcList);
  458.     Tcl_DeleteHashTable(&ddlist->trgList);
  459.     if (ddlist->errorProc != NULL) {
  460.         free((char*)ddlist->errorProc);
  461.     }
  462.     for (wrpool=ddlist->pool; wrpool; wrpool=wrnext)
  463.     {
  464.         wrnext = wrpool->next;
  465.         free((char*)wrpool);
  466.     }
  467.     free((char*)ddlist);
  468. }
  469.  
  470. /*
  471.  * ------------------------------------------------------------------------
  472.  *  DragDrop_Cmd()
  473.  *
  474.  *  Invoked by TCL whenever the user issues a drag&drop command.
  475.  *  Handles the following syntax:
  476.  *
  477.  *    blt_drag&drop source
  478.  *    blt_drag&drop source <pathName>
  479.  *    blt_drag&drop source <pathName> config ?options...?
  480.  *    blt_drag&drop source <pathName> handler <dataType> <command> ?...?
  481.  *
  482.  *    blt_drag&drop target
  483.  *    blt_drag&drop target <pathName> handler
  484.  *    blt_drag&drop target <pathName> handler <dataType> <command> ?...?
  485.  *    blt_drag&drop target <pathName> handle <dataType>
  486.  *
  487.  *    blt_drag&drop drag <pathName> <x> <y>
  488.  *    blt_drag&drop drop <pathName> <x> <y>
  489.  *
  490.  *    blt_drag&drop errors ?<proc>?
  491.  *    blt_drag&drop active
  492.  *    blt_drag&drop location ?<x> <y>?
  493.  *
  494.  * ------------------------------------------------------------------------
  495.  */
  496. static int
  497. DragDrop_Cmd(clientData, interp, argc, argv)
  498.     ClientData clientData;   /* main window associated with interp */
  499.     Tcl_Interp *interp;      /* current interpreter */
  500.     int argc;                /* number of arguments */
  501.     char **argv;             /* argument strings */
  502. {
  503.     DD_RegList *ddlist = (DD_RegList*)clientData;
  504.     DD_RegEntry *ddentry;
  505.     register DD_Source *dsPtr;
  506.     register DD_Target *dtPtr;
  507.  
  508.     int status, length, x, y, newEntry;
  509.     char c;
  510.  
  511.     Tk_Window tokenwin;
  512.     XSetWindowAttributes atts;
  513.     char buffer[1024];
  514.  
  515.     if (argc < 2)
  516.     {
  517.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  518.             argv[0], " option ?args?\"",
  519.             (char*)NULL);
  520.         return TCL_ERROR;
  521.     }
  522.     c = argv[1][0];
  523.     length = strlen(argv[1]);
  524.  
  525.     /*
  526.      *  HANDLE:  blt_drag&drop source
  527.      *           blt_drag&drop source <pathName>
  528.      *           blt_drag&drop source <pathName> config ?options...?
  529.      *           blt_drag&drop source <pathName> handler <data> <scmd> ?...?
  530.      */
  531.     if ((c == 's') && strncmp(argv[1], "source", length) == 0)
  532.     {
  533.         /*
  534.          *  HANDLE:  blt_drag&drop source
  535.          */
  536.         if (argc == 2)
  537.         {
  538.             Tcl_HashSearch cursor;
  539.             Tcl_HashEntry *entryPtr;
  540.             char *name;
  541.             
  542.             for (entryPtr = Tcl_FirstHashEntry(&ddlist->srcList, &cursor);
  543.                  entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) 
  544.             {
  545.                 name = Tcl_GetHashKey(&ddlist->srcList, entryPtr);
  546.                 Tcl_AppendElement(interp, name);
  547.             }
  548.             return TCL_OK;
  549.         }
  550.  
  551.         /*
  552.          *  Find or create source info...
  553.          */
  554.         dsPtr = GetSourceInfo(ddlist, argv[2], &newEntry);
  555.         dsPtr->tkwin = Tk_NameToWindow(interp, argv[2], ddlist->root);
  556.         if (!dsPtr->tkwin)
  557.         {
  558.             Tcl_AppendResult(interp, "window does not exist: ", argv[2],
  559.                 (char*)NULL);
  560.             DestroySourceInfo(ddlist,argv[2]);
  561.             return TCL_ERROR;
  562.         }
  563.         dsPtr->display = Tk_Display(dsPtr->tkwin);
  564.         dsPtr->ddAtom  = XInternAtom(dsPtr->display, DRAGDROP_PROPINFO, False);
  565.  
  566.         if (newEntry)
  567.             ConfigSource(interp, dsPtr, 0, (char**)NULL, 0);
  568.  
  569.         /*
  570.          *  HANDLE:  blt_drag&drop source <pathName> config ?options...?
  571.          */
  572.         if (argc > 3)
  573.         {
  574.             c = argv[3][0];
  575.             length = strlen(argv[3]);
  576.  
  577.             if ((c == 'c') && strncmp(argv[3], "config", length) == 0)
  578.             {
  579.                 if (argc == 4)
  580.                     status = Tk_ConfigureInfo(interp, dsPtr->tokenwin,
  581.                         SourceConfigSpecs, (char*)dsPtr, (char*)NULL, 0);
  582.  
  583.                 else if (argc == 5)
  584.                     status = Tk_ConfigureInfo(interp, dsPtr->tokenwin,
  585.                         SourceConfigSpecs, (char*)dsPtr, argv[4], 0);
  586.  
  587.                 else
  588.                     status = ConfigSource(interp, dsPtr, argc-4, argv+4,
  589.                         TK_CONFIG_ARGV_ONLY);
  590.             }
  591.  
  592.             /*
  593.              *  HANDLE:  blt_drag&drop source <pathName> handler \
  594.              *             ?<data> <scmd>...?
  595.              */
  596.             else if ((c == 'h') && strncmp(argv[3], "handler", length) == 0)
  597.             {
  598.                 if (argc == 4)
  599.                 {
  600.                     DD_SourceHndl *dsHndl = dsPtr->handlers;
  601.                     while (dsHndl)
  602.                     {
  603.                         Tcl_AppendElement(interp, dsHndl->dataType);
  604.                         dsHndl = dsHndl->next;
  605.                     }
  606.                     return TCL_OK;
  607.                 }
  608.  
  609.                 /*
  610.                  *  Process handler definitions
  611.                  */
  612.                 status = TCL_OK;
  613.                 for (x=4; (x < argc) && (status == TCL_OK); x+=2)
  614.                 {
  615.                     if (x+1 < argc)
  616.                     {
  617.                         char *p;
  618.                         for (p=argv[x]; *p != '\0'; p++)
  619.                             if (*p == ' ')
  620.                             {
  621.                                 Tcl_AppendResult(interp,
  622.                                     "bad source handler name \"",
  623.                                     argv[x], "\" (should not contain spaces)",
  624.                                     (char*)NULL);
  625.                                 return TCL_ERROR;
  626.                             }
  627.  
  628.                         PutSourceHandler(dsPtr, argv[x], argv[x+1]);
  629.                     }
  630.                     else
  631.                     {
  632.                         Tcl_AppendResult(interp,
  633.                             "missing command for source handler: should be \"",
  634.                             argv[x], " command\"");
  635.                         return TCL_ERROR;
  636.                     }
  637.                 }
  638.                 return TCL_OK;
  639.             }
  640.             else
  641.             {
  642.                 Tcl_AppendResult(interp, "bad option \"", argv[3],
  643.                     "\": must be config or handler",
  644.                     (char*)NULL);
  645.                 return TCL_ERROR;
  646.             }
  647.         }
  648.  
  649.         if (newEntry)
  650.         {
  651.             /*
  652.              *  Create the window for the drag&drop token...
  653.              */
  654.             sprintf(buffer, "dd-token%x", (int)dsPtr);
  655.             tokenwin = Tk_CreateWindow(dsPtr->ddlist->interp, dsPtr->tkwin,
  656.                 buffer, "");
  657.  
  658.             if (!tokenwin)
  659.             {
  660.                 Tcl_AppendResult(interp, "could not create token window",
  661.                     (char*)NULL);
  662.                 DestroySourceInfo(ddlist,argv[2]);
  663.                 return TCL_ERROR;
  664.             }
  665.             Tk_SetClass(tokenwin, DRAGDROP_CLASS);
  666.             Tk_CreateEventHandler(tokenwin, ExposureMask|StructureNotifyMask,
  667.                 DDTokenEventProc, (ClientData)dsPtr);
  668.  
  669.             atts.override_redirect = True;
  670.             atts.save_under = True;
  671.             Tk_ChangeWindowAttributes(tokenwin,
  672.                 CWOverrideRedirect|CWSaveUnder, &atts);
  673.  
  674.             Tk_SetInternalBorder(tokenwin, 2*dsPtr->tokenBorderWidth);
  675.             dsPtr->tokenwin = tokenwin;
  676.  
  677.             if (dsPtr->button > 0)
  678.             {
  679.                 sprintf(buffer,
  680.                     "bind %.100s <ButtonPress-%d> {%s drag %.100s %%X %%Y}; \
  681.                     bind %.100s <B%d-Motion> {%s drag %.100s %%X %%Y}; \
  682.                     bind %.100s <ButtonRelease-%d> {%s drop %.100s %%X %%Y}",
  683.                     argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2],
  684.                     argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2],
  685.                     argv[2], dsPtr->button, DRAGDROP_COMMAND, argv[2]);
  686.  
  687.                 if (Tcl_Eval(interp, buffer) != TCL_OK)
  688.                 {
  689.                     Tk_DestroyWindow(tokenwin);
  690.                     DestroySourceInfo(ddlist,argv[2]);
  691.                     return TCL_ERROR;
  692.                 }
  693.             }
  694.  
  695.             /*
  696.              *  Arrange for the window to unregister itself when it
  697.              *  is destroyed.
  698.              */
  699.             ddentry = (DD_RegEntry*)malloc(sizeof(DD_RegEntry));
  700.             ddentry->ddlist = ddlist;
  701.             ddentry->tkwin = dsPtr->tkwin;
  702.             Tk_CreateEventHandler(dsPtr->tkwin, StructureNotifyMask,
  703.                 UnregSource, (ClientData)ddentry);
  704.         }
  705.     }
  706.  
  707.     /*
  708.      *  HANDLE:  blt_drag&drop target ?<pathName>? ?handling info...?
  709.      */
  710.     else if ((c == 't') && strncmp(argv[1], "target", length) == 0)
  711.     {
  712.         /*
  713.          *  HANDLE:  blt_drag&drop target
  714.          */
  715.         if (argc == 2)
  716.         {
  717.             Tcl_HashSearch pos;
  718.             Tcl_HashEntry *entry = Tcl_FirstHashEntry(&ddlist->trgList,&pos);
  719.             while (entry)
  720.             {
  721.                 Tcl_AppendElement(interp,
  722.                     Tcl_GetHashKey(&ddlist->trgList,entry));
  723.                 entry = Tcl_NextHashEntry(&pos);
  724.             }
  725.             return TCL_OK;
  726.         }
  727.  
  728.         dtPtr = GetTargetInfo(ddlist,argv[2],&newEntry);
  729.         dtPtr->tkwin = Tk_NameToWindow(interp, argv[2], ddlist->root);
  730.  
  731.         if (!dtPtr->tkwin)
  732.         {
  733.             Tcl_AppendResult(interp, "window does not exist: ", argv[2],
  734.                 (char*)NULL);
  735.             DestroyTargetInfo(ddlist,argv[2]);
  736.             return TCL_ERROR;
  737.         }
  738.         dtPtr->display = Tk_Display(dtPtr->tkwin);
  739.  
  740.         /*
  741.          *  If this is a new target, attach a property to identify
  742.          *  window as "drag&drop" target, and arrange for the window
  743.          *  to un-register itself when it is destroyed.
  744.          */
  745.         if (newEntry)
  746.         {
  747.             Tk_MakeWindowExist(dtPtr->tkwin);
  748.             AddDDProp(dtPtr);
  749.  
  750.             /*
  751.              *  Arrange for the window to unregister itself when it
  752.              *  is destroyed.
  753.              */
  754.             ddentry = (DD_RegEntry*)malloc(sizeof(DD_RegEntry));
  755.             ddentry->ddlist = ddlist;
  756.             ddentry->tkwin = dtPtr->tkwin;
  757.             Tk_CreateEventHandler(dtPtr->tkwin, StructureNotifyMask,
  758.                 UnregTarget, (ClientData)ddentry);
  759.         }
  760.  
  761.         /*
  762.          *  HANDLE:  blt_drag&drop target <pathName> handler
  763.          *           blt_drag&drop target <pathName> handler <data> <cmd> ?...?
  764.          */
  765.         if ((argc >= 4) && (strcmp(argv[3], "handler") == 0))
  766.         {
  767.             if (argc == 4)
  768.             {
  769.                 DD_TargetHndl *dtHndl = dtPtr->handlers;
  770.                 while (dtHndl)
  771.                 {
  772.                     Tcl_AppendElement(interp, dtHndl->dataType);
  773.                     dtHndl = dtHndl->next;
  774.                 }
  775.                 return TCL_OK;
  776.             }
  777.  
  778.             /*
  779.              *  Process handler definitions
  780.              */
  781.             status = TCL_OK;
  782.             for (x=4; (x < argc) && (status == TCL_OK); x+=2)
  783.             {
  784.                 if (x+1 < argc)
  785.                     PutTargetHandler(dtPtr, argv[x], argv[x+1]);
  786.                 else
  787.                 {
  788.                     Tcl_AppendResult(interp,
  789.                         "missing command for target handler: should be \"",
  790.                         argv[x], " command\"",
  791.                         (char*)NULL);
  792.                     return TCL_ERROR;
  793.                 }
  794.             }
  795.             return TCL_OK;
  796.         }
  797.  
  798.         /*
  799.          *  HANDLE:  blt_drag&drop target <pathName> handle <data>
  800.          */
  801.         else if ((argc == 5) && (strcmp(argv[3], "handle") == 0))
  802.         {
  803.             char *cmd;
  804.             if ((cmd=FindTargetHandler(dtPtr, argv[4])) != NULL)
  805.                 return Tcl_Eval(interp, cmd);
  806.  
  807.             Tcl_AppendResult(interp, "target cannot handle datatype: ",
  808.                 argv[4], (char*)NULL);
  809.             return TCL_ERROR;  /* no handler found */
  810.         }
  811.         else
  812.         {
  813.             Tcl_AppendResult(interp,"usage: ", argv[0], " target ", argv[2],
  814.                 " handler ?defns?\n   or: ", argv[0], " target ", argv[2],
  815.                 " handle <data>",
  816.                 (char*)NULL);
  817.             return TCL_ERROR;
  818.         }
  819.     }
  820.  
  821.     /*
  822.      *  HANDLE:  blt_drag&drop drag <path> <x> <y>
  823.      */
  824.     else if ((c == 'd') && strncmp(argv[1], "drag", length) == 0)
  825.     {
  826.         if (argc < 5)
  827.         {
  828.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  829.                 argv[0], " drag pathname x y\"",
  830.                 (char*)NULL);
  831.             return TCL_ERROR;
  832.         }
  833.  
  834.         dsPtr = GetSourceInfo(ddlist,argv[2],&newEntry);
  835.         if (newEntry)
  836.         {
  837.             Tcl_AppendResult(interp, "not a drag&drop source: ", argv[2],
  838.                 (char*)NULL);
  839.             DestroySourceInfo(ddlist,argv[2]);
  840.             return TCL_ERROR;
  841.         }
  842.         if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
  843.             (Tcl_GetInt(interp, argv[4], &y) != TCL_OK))
  844.             return TCL_ERROR;
  845.  
  846.         ddlist->locx = x;   /* save drag&drop location */
  847.         ddlist->locy = y;
  848.         dsPtr->tokenx = x;
  849.         dsPtr->tokeny = y;
  850.  
  851.         /*
  852.          *  If HideDDToken() is pending, then do it now!
  853.          */
  854.         if (dsPtr->hidetoken)
  855.         {
  856.             Tk_DeleteTimerHandler(dsPtr->hidetoken);
  857.             HideDDToken((ClientData)dsPtr);
  858.         }
  859.  
  860.         /*
  861.          *  If pkgcmd is in progress, then ignore subsequent calls
  862.          *  until it completes.  Only perform drag if pkgcmd
  863.          *  completed successfully and token window is mapped.
  864.          */
  865.         if (!Tk_IsMapped(dsPtr->tokenwin) && !dsPtr->pkgcmdInProg)
  866.         {
  867.             /*
  868.              *  No list of send handlers?  Then source is disabled.
  869.              *  Abort drag quietly.
  870.              */
  871.             if (dsPtr->send == NULL)
  872.                 return TCL_OK;
  873.  
  874.             /*
  875.              *  No token command?  Then cannot build token.
  876.              *  Signal error.
  877.              */
  878.             if (!dsPtr->pkgcmd)
  879.             {
  880.                 Tcl_AppendResult(interp, "missing -packagecmd: ", argv[2],
  881.                     (char*)NULL);
  882.                 return TCL_ERROR;
  883.             }
  884.  
  885.             /*
  886.              *  Execute token command to initialize token window.
  887.              */
  888.             dsPtr->pkgcmdInProg = ~0;
  889.             status = Tcl_VarEval(dsPtr->ddlist->interp,
  890.                 dsPtr->pkgcmd, " ", Tk_PathName(dsPtr->tokenwin),
  891.                 (char*)NULL);
  892.             dsPtr->pkgcmdInProg = 0;
  893.  
  894.             /*
  895.              *  Null string from the package command?
  896.              *  Then quietly abort the drag&drop operation.
  897.              */
  898.             if (*interp->result == '\0')
  899.                 return TCL_OK;
  900.  
  901.             /*
  902.              *  Save result of token command for send command.
  903.              */
  904.             if (dsPtr->pkgcmdResult)
  905.                 free(dsPtr->pkgcmdResult);
  906.             dsPtr->pkgcmdResult = strdup(interp->result);
  907.  
  908.             /*
  909.              *  Token building failed?  If an error handler is defined,
  910.              *  then signal the error.  Otherwise, abort quietly.
  911.              */
  912.             if (status != TCL_OK)
  913.             {
  914.                 if (ddlist->errorProc && *ddlist->errorProc)
  915.                 {
  916.                     return Tcl_VarEval(ddlist->interp,
  917.                         ddlist->errorProc, " {", ddlist->interp->result, "}",
  918.                         (char*)NULL);
  919.                 }
  920.                 else
  921.                     return TCL_OK;
  922.             }
  923.  
  924.             /*
  925.              *  Install token cursor...
  926.              */
  927.             if (dsPtr->tokenCursor != None)
  928.             {
  929.                 status = Tcl_VarEval(dsPtr->ddlist->interp,
  930.                     Tk_PathName(dsPtr->tkwin), " config -cursor",
  931.                     (char*)NULL);
  932.  
  933.                 if (status == TCL_OK)
  934.                 {
  935.                     char *cname = interp->result;
  936.                     while (*cname != '\0')
  937.                         cname++;
  938.  
  939.                     while ((cname > interp->result) && (*(cname-1) != ' '))
  940.                         cname--;
  941.  
  942.                     if (dsPtr->normalCursor != None)
  943.                     {
  944.                         Tk_FreeCursor(dsPtr->display, dsPtr->normalCursor);
  945.                         dsPtr->normalCursor = None;
  946.                     }
  947.  
  948.                     if (strcmp(cname,"{}") != 0)
  949.                         dsPtr->normalCursor = Tk_GetCursor(interp,
  950.                             dsPtr->tkwin, Tk_GetUid(cname));
  951.                 }
  952.                 Tk_DefineCursor(dsPtr->tkwin, dsPtr->tokenCursor);
  953.             }
  954.  
  955.             /*
  956.              *  Get ready to drag token window...
  957.              *  1) Cache info for all windows on root
  958.              *  2) Map token window to begin drag operation
  959.              */
  960.             if (dsPtr->allwins)
  961.                 WinRepRelease(dsPtr->allwins, ddlist);
  962.             dsPtr->allwins = GetWinRepInfo(dsPtr, ddlist);
  963.  
  964.             ddlist->numactive++;   /* one more drag&drop window active */
  965.             Tk_MapWindow(dsPtr->tokenwin);
  966.             XRaiseWindow(Tk_Display(dsPtr->tokenwin),
  967.                 Tk_WindowId(dsPtr->tokenwin));
  968.         }
  969.  
  970.         /*
  971.          *  Arrange to update status of token window...
  972.          */
  973.         Tk_CancelIdleCall(UpdateDDToken, (ClientData)dsPtr);
  974.         Tk_DoWhenIdle(UpdateDDToken, (ClientData)dsPtr);
  975.  
  976.         /*
  977.          *  Move the token window to the current drag point...
  978.          */
  979.         MoveDDToken(dsPtr);
  980.     }
  981.  
  982.     /*
  983.      *  HANDLE:  blt_drag&drop drop <path> <x> <y>
  984.      */
  985.     else if ((c == 'd') && strncmp(argv[1], "drop", length) == 0)
  986.     {
  987.         if (argc < 5)
  988.         {
  989.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  990.                 argv[0], " drop pathname x y\"",
  991.                 (char*)NULL);
  992.             return TCL_ERROR;
  993.         }
  994.  
  995.         dsPtr = GetSourceInfo(ddlist,argv[2],&newEntry);
  996.         if (newEntry)
  997.         {
  998.             Tcl_AppendResult(interp, "not a drag&drop source: ", argv[2],
  999.                 (char*)NULL);
  1000.             DestroySourceInfo(ddlist,argv[2]);
  1001.             return TCL_ERROR;
  1002.         }
  1003.         if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
  1004.             (Tcl_GetInt(interp, argv[4], &y) != TCL_OK))
  1005.             return TCL_ERROR;
  1006.  
  1007.         ddlist->locx = x;  /* save drag&drop location */
  1008.         ddlist->locy = y;
  1009.         dsPtr->tokenx = x;
  1010.         dsPtr->tokeny = y;
  1011.  
  1012.         /*
  1013.          *  Put the cursor back to its usual state.
  1014.          */
  1015.         if (dsPtr->normalCursor == None)
  1016.             Tk_UndefineCursor(dsPtr->tkwin);
  1017.         else
  1018.             Tk_DefineCursor(dsPtr->tkwin, dsPtr->normalCursor);
  1019.  
  1020.         Tk_CancelIdleCall(UpdateDDToken, (ClientData)dsPtr);
  1021.  
  1022.         /*
  1023.          *  Make sure that token window was not dropped before it
  1024.          *  was either mapped or packed with info.
  1025.          */
  1026.         if (Tk_IsMapped(dsPtr->tokenwin) && !dsPtr->pkgcmdInProg)
  1027.         {
  1028.             UpdateDDToken((ClientData)dsPtr);
  1029.  
  1030.             if (dsPtr->send)
  1031.             {
  1032.                 if (dsPtr->overTargetWin)
  1033.                     DragDropSend(dsPtr);
  1034.                 else
  1035.                     HideDDToken((ClientData)dsPtr);
  1036.             }
  1037.             ddlist->numactive--;  /* one fewer active token window */
  1038.         }
  1039.     }
  1040.  
  1041.     /*
  1042.      *  HANDLE:  blt_drag&drop errors ?<proc>?
  1043.      */
  1044.     else if ((c == 'e') && strncmp(argv[1], "errors", length) == 0)
  1045.     {
  1046.         if (argc == 3)
  1047.         {
  1048.             if (ddlist->errorProc) {
  1049.                 free(ddlist->errorProc);
  1050.             }
  1051.             ddlist->errorProc = strdup(argv[2]);
  1052.         }
  1053.         else if (argc != 2)
  1054.         {
  1055.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  1056.                 argv[0], " errors ?proc?\"",
  1057.                 (char*)NULL);
  1058.             return TCL_ERROR;
  1059.         }
  1060.         Tcl_SetResult(interp, ddlist->errorProc, TCL_VOLATILE);
  1061.         return TCL_OK;
  1062.     }
  1063.  
  1064.     /*
  1065.      *  HANDLE:  blt_drag&drop active
  1066.      */
  1067.     else if ((c == 'a') && strncmp(argv[1], "active", length) == 0)
  1068.     {
  1069.         if (argc != 2)
  1070.         {
  1071.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  1072.                 argv[0], " active\"",
  1073.                 (char*)NULL);
  1074.             return TCL_ERROR;
  1075.         }
  1076.         Tcl_SetResult(interp, (ddlist->numactive > 0) ? "1" : "0", TCL_STATIC);
  1077.         return TCL_OK;
  1078.     }
  1079.  
  1080.     /*
  1081.      *  HANDLE:  blt_drag&drop location ?<x> <y>?
  1082.      */
  1083.     else if ((c == 'l') && strncmp(argv[1], "location", length) == 0)
  1084.     {
  1085.         if (argc == 2)
  1086.         {
  1087.             sprintf(buffer, "%d %d", ddlist->locx, ddlist->locy);
  1088.             Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  1089.             return TCL_OK;
  1090.         }
  1091.         else if ((argc == 4) &&
  1092.             (Tcl_GetInt(interp, argv[2], &x) == TCL_OK) &&
  1093.             (Tcl_GetInt(interp, argv[3], &y) == TCL_OK))
  1094.         {
  1095.             ddlist->locx = x;
  1096.             ddlist->locy = y;
  1097.             sprintf(buffer, "%d %d", ddlist->locx, ddlist->locy);
  1098.             Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  1099.             return TCL_OK;
  1100.         }
  1101.         else
  1102.         {
  1103.             Tcl_AppendResult(interp, "wrong # args: should be \"",
  1104.                 argv[0], " location ?x y?\"",
  1105.                 (char*)NULL);
  1106.             return TCL_ERROR;
  1107.         }
  1108.     }
  1109.  
  1110.     /*
  1111.      *  Report improper command arguments
  1112.      */
  1113.     else
  1114.     {
  1115.         Tcl_AppendResult(interp, "bad option \"", argv[1], "\": must be source, target, drag, drop, errors, active or location",
  1116.             (char*)NULL);
  1117.         return TCL_ERROR;
  1118.     }
  1119.     return TCL_OK;
  1120. }
  1121.  
  1122. /*
  1123.  * ------------------------------------------------------------------------
  1124.  *  GetSourceInfo()
  1125.  *
  1126.  *  Looks for a DD_Source record in the hash table for drag&drop source
  1127.  *  widgets.  Creates a new record if the widget name is not already
  1128.  *  registered.  Returns a pointer to the desired record.
  1129.  * ------------------------------------------------------------------------
  1130.  */
  1131. static DD_Source*
  1132. GetSourceInfo(ddlist,pathname,newEntry)
  1133.     DD_RegList* ddlist;  /* drag&drop records for all registered widgets */
  1134.     char* pathname;      /* widget pathname for desired record */
  1135.     int* newEntry;       /* returns non-zero => new record created */
  1136. {
  1137.     DD_Source *dsPtr;
  1138.     Tcl_HashEntry *ddEntry;
  1139.  
  1140.     ddEntry = Tcl_CreateHashEntry(&ddlist->srcList, pathname, newEntry);
  1141.     if (*newEntry)
  1142.     {
  1143.         /*
  1144.          *  Initialize a data structure for the widget...
  1145.          */
  1146.         dsPtr = (DD_Source*)malloc(sizeof(DD_Source));
  1147.         dsPtr->ddlist = ddlist;
  1148.         dsPtr->display = NULL;
  1149.         dsPtr->tkwin = NULL;
  1150.         dsPtr->ddAtom = None;
  1151.         dsPtr->button = 0;
  1152.  
  1153.         dsPtr->tokenwin = NULL;
  1154.         dsPtr->tokenAnchor = TK_ANCHOR_CENTER;
  1155.         dsPtr->tokenCursor = None;
  1156.         dsPtr->tokenOutline = NULL;
  1157.         dsPtr->tokenBorder = NULL;
  1158.         dsPtr->tokenBorderWidth = 0;
  1159.         dsPtr->rejectFg = NULL;
  1160.         dsPtr->rejectBg = NULL;
  1161.         dsPtr->rejectSt = None;
  1162.         dsPtr->rejectFgGC = None;
  1163.         dsPtr->rejectBgGC = None;
  1164.  
  1165.         dsPtr->pkgcmdInProg = 0;
  1166.         dsPtr->pkgcmd = NULL;
  1167.         dsPtr->pkgcmdResult = NULL;
  1168.         dsPtr->sitecmd = NULL;
  1169.  
  1170.         dsPtr->allwins = NULL;
  1171.         dsPtr->selfTarget = 0;
  1172.         dsPtr->overTargetWin = 0;
  1173.         dsPtr->tokenx = dsPtr->tokeny = 0;
  1174.         dsPtr->hidetoken = NULL;
  1175.         dsPtr->normalCursor = None;
  1176.  
  1177.         dsPtr->send = NULL;
  1178.         dsPtr->handlers = NULL;
  1179.  
  1180.         Tcl_SetHashValue(ddEntry, (ClientData)dsPtr);
  1181.     }
  1182.     return (DD_Source*)Tcl_GetHashValue(ddEntry);
  1183. }
  1184.  
  1185. /*
  1186.  * ------------------------------------------------------------------------
  1187.  *  DestroySourceInfo()
  1188.  *
  1189.  *  Looks for a DD_Source record in the hash table for drag&drop source
  1190.  *  widgets.  Destroys the record if found.
  1191.  * ------------------------------------------------------------------------
  1192.  */
  1193. static void
  1194. DestroySourceInfo(ddlist,pathname)
  1195.     DD_RegList* ddlist;     /* drag&drop records for all registered widgets */
  1196.     char* pathname;         /* widget pathname for desired record */
  1197. {
  1198.     DD_Source *dsPtr;
  1199.     DD_SourceHndl *dsHndl, *next;
  1200.     Tcl_HashEntry *ddEntry;
  1201.  
  1202.     ddEntry = Tcl_FindHashEntry(&ddlist->srcList, pathname);
  1203.     if (ddEntry == NULL) {
  1204.         return;
  1205.     }
  1206.     dsPtr = (DD_Source*)Tcl_GetHashValue(ddEntry);
  1207.     if (dsPtr)
  1208.     {
  1209.         Tk_CancelIdleCall(UpdateDDToken, (ClientData)dsPtr);
  1210.         if (dsPtr->hidetoken)           
  1211.             Tk_DeleteTimerHandler(dsPtr->hidetoken);
  1212.  
  1213.         Tk_FreeOptions(SourceConfigSpecs, (char *)dsPtr, dsPtr->display, 0);
  1214.  
  1215.         if (dsPtr->rejectFgGC != None) 
  1216.             Tk_FreeGC(dsPtr->display, dsPtr->rejectFgGC);
  1217.         if (dsPtr->rejectBgGC != None) 
  1218.             Tk_FreeGC(dsPtr->display, dsPtr->rejectBgGC);
  1219.         if (dsPtr->pkgcmdResult)       
  1220.             free(dsPtr->pkgcmdResult);
  1221.  
  1222.         if (dsPtr->allwins)               
  1223.             WinRepRelease(dsPtr->allwins, ddlist);
  1224.  
  1225.         if (dsPtr->normalCursor != None)
  1226.             Tk_FreeCursor(dsPtr->display, dsPtr->normalCursor);
  1227.  
  1228.         dsHndl = dsPtr->handlers;
  1229.         while (dsHndl)
  1230.         {
  1231.             next = dsHndl->next;
  1232.             DestroySourceHandler(dsHndl);
  1233.             dsHndl = next;
  1234.         }
  1235.         free((char*)dsPtr);
  1236.     }
  1237.     Tcl_DeleteHashEntry(ddEntry);
  1238. }
  1239.  
  1240. /*
  1241.  * ------------------------------------------------------------------------
  1242.  *  ConfigSource()
  1243.  *
  1244.  *  Called to process an (argc,argv) list to configure (or reconfigure)
  1245.  *  a drag&drop source widget.
  1246.  * ------------------------------------------------------------------------
  1247.  */
  1248. static int
  1249. ConfigSource(interp, dsPtr, argc, argv, flags)
  1250.     Tcl_Interp *interp;        /* current interpreter */
  1251.     register DD_Source *dsPtr; /* drag&drop source widget record */
  1252.     int argc;                  /* number of arguments */
  1253.     char **argv;               /* argument strings */
  1254.     int flags;                 /* flags controlling interpretation */
  1255. {
  1256.     unsigned long gcMask;
  1257.     XGCValues gcValues;
  1258.     GC newGC;
  1259.  
  1260.     /*
  1261.      *  Handle the bulk of the options...
  1262.      */
  1263.     if (Tk_ConfigureWidget(interp, dsPtr->tkwin, SourceConfigSpecs,
  1264.         argc, argv, (char*)dsPtr, flags) != TCL_OK)
  1265.         return TCL_ERROR;
  1266.  
  1267.     /*
  1268.      *  Check the button binding for valid range (0 or 1-5)
  1269.      */
  1270.     if (dsPtr->button < 0 || dsPtr->button > 5)
  1271.     {
  1272.         Tcl_SetResult(interp,
  1273.             "invalid button binding: should be 1-5 or 0 for no bindings",
  1274.             TCL_STATIC);
  1275.         return TCL_ERROR;
  1276.     }
  1277.     /*
  1278.      *  Set up the rejection foreground GC for the token window...
  1279.      */
  1280.     gcValues.foreground = dsPtr->rejectFg->pixel;
  1281.     gcValues.subwindow_mode = IncludeInferiors;
  1282.     gcValues.graphics_exposures = False;
  1283.     gcMask = GCForeground|GCSubwindowMode|GCGraphicsExposures;
  1284.  
  1285.     if (dsPtr->rejectSt != None)
  1286.     {
  1287.         gcValues.stipple = dsPtr->rejectSt;
  1288.         gcValues.fill_style = FillStippled;
  1289.         gcMask |= GCForeground|GCStipple|GCFillStyle;
  1290.     }
  1291.     newGC = Tk_GetGC(dsPtr->tkwin, gcMask, &gcValues);
  1292.  
  1293.     if (dsPtr->rejectFgGC != None)
  1294.         Tk_FreeGC(dsPtr->display, dsPtr->rejectFgGC);
  1295.     dsPtr->rejectFgGC = newGC;
  1296.  
  1297.     /*
  1298.      *  Set up the rejection background GC for the token window...
  1299.      */
  1300.     gcValues.foreground = dsPtr->rejectBg->pixel;
  1301.     gcValues.subwindow_mode = IncludeInferiors;
  1302.     gcValues.graphics_exposures = False;
  1303.     gcMask = GCForeground|GCSubwindowMode|GCGraphicsExposures;
  1304.  
  1305.     newGC = Tk_GetGC(dsPtr->tkwin, gcMask, &gcValues);
  1306.  
  1307.     if (dsPtr->rejectBgGC != None)
  1308.         Tk_FreeGC(dsPtr->display, dsPtr->rejectBgGC);
  1309.     dsPtr->rejectBgGC = newGC;
  1310.  
  1311.     /*
  1312.      *  Reset the border width in case it has changed...
  1313.      */
  1314.     if (dsPtr->tokenwin)
  1315.         Tk_SetInternalBorder(dsPtr->tokenwin, 2*dsPtr->tokenBorderWidth);
  1316.  
  1317.     return TCL_OK;
  1318. }
  1319.  
  1320. /*
  1321.  * ------------------------------------------------------------------------
  1322.  *  FindSourceHandler()
  1323.  *
  1324.  *  Looks for the requested data type of the list of handlers for the
  1325.  *  given drag&drop source.
  1326.  * ------------------------------------------------------------------------
  1327.  */
  1328. static char*
  1329. FindSourceHandler(dsPtr, dtname)
  1330.     register DD_Source *dsPtr; /* drag&drop source widget record */
  1331.     char *dtname;              /* name of requested data type */
  1332. {
  1333.     DD_SourceHndl *dsHndl;
  1334.  
  1335.     for (dsHndl=dsPtr->handlers; dsHndl; dsHndl=dsHndl->next)
  1336.         if (strcmp(dsHndl->dataType,dtname) == 0)
  1337.             return dsHndl->cmd;
  1338.  
  1339.     return NULL;
  1340. }
  1341.  
  1342. /*
  1343.  * ------------------------------------------------------------------------
  1344.  *  PutSourceHandler()
  1345.  *
  1346.  *  Looks for the requested data type of the list of handlers for the
  1347.  *  given drag&drop source.  If found, then its associated commands are
  1348.  *  changed to the given commands.  If not found, then a new handler
  1349.  *  is created.
  1350.  * ------------------------------------------------------------------------
  1351.  */
  1352. static void
  1353. PutSourceHandler(dsPtr, dtname, cmd)
  1354.     register DD_Source *dsPtr; /* drag&drop target widget record */
  1355.     char *dtname;              /* name of data type */
  1356.     char *cmd;                 /* command used to send data */
  1357. {
  1358.     DD_SourceHndl *tail = NULL;
  1359.     DD_SourceHndl *dsHndl;
  1360.  
  1361.     for (dsHndl=dsPtr->handlers; dsHndl; tail=dsHndl,dsHndl=dsHndl->next)
  1362.         if (strcmp(dsHndl->dataType,dtname) == 0)
  1363.         {
  1364.             if (*cmd == '\0')
  1365.             {
  1366.                 if (tail)
  1367.                     tail->next = dsHndl->next;
  1368.                 else
  1369.                     dsPtr->handlers = dsHndl->next;
  1370.  
  1371.                 DestroySourceHandler(dsHndl);
  1372.                 return;
  1373.             }
  1374.             else
  1375.             {
  1376.                 if (dsHndl->cmd != NULL) {
  1377.                     free(dsHndl->cmd);
  1378.                 }
  1379.                 dsHndl->cmd = strdup(cmd);
  1380.                 return;
  1381.             }
  1382.         }
  1383.  
  1384.     if (tail)
  1385.         tail->next = CreateSourceHandler(dtname,cmd);
  1386.     else
  1387.         dsPtr->handlers = CreateSourceHandler(dtname,cmd);
  1388. }
  1389.  
  1390. /*
  1391.  * ------------------------------------------------------------------------
  1392.  *  CreateSourceHandler()
  1393.  *
  1394.  *  Creates a new source handler record and returns a pointer to it.
  1395.  * ------------------------------------------------------------------------
  1396.  */
  1397. static DD_SourceHndl*
  1398. CreateSourceHandler(dtname, cmd)
  1399.     char *dtname;              /* name of data type */
  1400.     char *cmd;                 /* command used to send data */
  1401. {
  1402.     DD_SourceHndl *retn;
  1403.     retn = (DD_SourceHndl*)malloc(sizeof(DD_SourceHndl));
  1404.  
  1405.     retn->dataType = strdup(dtname);
  1406.     retn->cmd = strdup(cmd);
  1407.     retn->next = NULL;
  1408.     return retn;
  1409. }
  1410.  
  1411. /*
  1412.  * ------------------------------------------------------------------------
  1413.  *  DestroySourceHandler()
  1414.  *
  1415.  *  Destroys a source handler record.
  1416.  * ------------------------------------------------------------------------
  1417.  */
  1418. static void
  1419. DestroySourceHandler(dsHndl)
  1420.     DD_SourceHndl *dsHndl;
  1421. {
  1422.     if (dsHndl->dataType != NULL) {
  1423.         free(dsHndl->dataType);
  1424.     }
  1425.     if (dsHndl->cmd != NULL) {
  1426.         free(dsHndl->cmd);
  1427.     }
  1428.     free((char*)dsHndl);
  1429. }
  1430.  
  1431. /*
  1432.  * ------------------------------------------------------------------------
  1433.  *  UnregSource()
  1434.  *
  1435.  *  Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
  1436.  *  on a registered drag&drop source widget.
  1437.  * ------------------------------------------------------------------------
  1438.  */
  1439. static void
  1440. UnregSource(cdata, eventPtr)
  1441.     ClientData cdata;   /* drag&drop registration list */
  1442.     XEvent *eventPtr;   /* event description */
  1443. {
  1444.     DD_RegEntry *ddentry = (DD_RegEntry*)cdata;
  1445.     DD_RegList *ddlist = ddentry->ddlist;
  1446.     char *ddname = Tk_PathName(ddentry->tkwin);
  1447.  
  1448.     if (eventPtr->type == DestroyNotify)
  1449.     {
  1450.         DestroySourceInfo(ddlist,ddname);
  1451.         free((char*)ddentry);
  1452.     }
  1453. }
  1454.  
  1455. /*
  1456.  * ------------------------------------------------------------------------
  1457.  *  GetTargetInfo()
  1458.  *
  1459.  *  Looks for a DD_Target record in the hash table for drag&drop target
  1460.  *  widgets.  Creates a new record if the widget name is not already
  1461.  *  registered.  Returns a pointer to the desired record.
  1462.  * ------------------------------------------------------------------------
  1463.  */
  1464. static DD_Target*
  1465. GetTargetInfo(ddlist,pathname,newEntry)
  1466.     DD_RegList* ddlist;  /* drag&drop records for all registered widgets */
  1467.     char* pathname;      /* widget pathname for desired record */
  1468.     int* newEntry;       /* returns non-zero => new record created */
  1469. {
  1470.     DD_Target *dtPtr;
  1471.     Tcl_HashEntry *ddEntry;
  1472.  
  1473.     ddEntry = Tcl_CreateHashEntry(&ddlist->trgList, pathname, newEntry);
  1474.     if (*newEntry)
  1475.     {
  1476.         /*
  1477.          *  Initialize a data structure for the widget...
  1478.          */
  1479.         dtPtr = (DD_Target*)malloc(sizeof(DD_Target));
  1480.         dtPtr->ddlist = ddlist;
  1481.         dtPtr->display = NULL;
  1482.         dtPtr->tkwin = NULL;
  1483.         dtPtr->handlers = NULL;
  1484.  
  1485.         Tcl_SetHashValue(ddEntry, (ClientData)dtPtr);
  1486.     }
  1487.     return (DD_Target*)Tcl_GetHashValue(ddEntry);
  1488. }
  1489.  
  1490. /*
  1491.  * ------------------------------------------------------------------------
  1492.  *  DestroyTargetInfo()
  1493.  *
  1494.  *  Looks for a DD_Target record in the hash table for drag&drop target
  1495.  *  widgets.  Destroys the record if found.
  1496.  * ------------------------------------------------------------------------
  1497.  */
  1498. static void
  1499. DestroyTargetInfo(ddlist,pathname)
  1500.     DD_RegList* ddlist;  /* drag&drop records for all registered widgets */
  1501.     char* pathname;      /* widget pathname for desired record */
  1502. {
  1503.     DD_Target *dtPtr;
  1504.     DD_TargetHndl *dtHndl, *next;
  1505.     Tcl_HashEntry *ddEntry;
  1506.  
  1507.     ddEntry = Tcl_FindHashEntry(&ddlist->trgList, pathname);
  1508.     dtPtr = (ddEntry) ? (DD_Target*)Tcl_GetHashValue(ddEntry) : NULL;
  1509.  
  1510.     if (dtPtr)
  1511.     {
  1512.         dtHndl = dtPtr->handlers;
  1513.         while (dtHndl)
  1514.         {
  1515.             next = dtHndl->next;
  1516.             DestroyTargetHandler(dtHndl);
  1517.             dtHndl = next;
  1518.         }
  1519.         free((char*)dtPtr);
  1520.     }
  1521.     if (ddEntry) Tcl_DeleteHashEntry(ddEntry);
  1522. }
  1523.  
  1524. /*
  1525.  * ------------------------------------------------------------------------
  1526.  *  FindTargetHandler()
  1527.  *
  1528.  *  Looks for the requested data type of the list of handlers for the
  1529.  *  given drag&drop target.
  1530.  * ------------------------------------------------------------------------
  1531.  */
  1532. static char*
  1533. FindTargetHandler(dtPtr, dtname)
  1534.     register DD_Target *dtPtr; /* drag&drop target widget record */
  1535.     char *dtname;              /* name of requested data type */
  1536. {
  1537.     DD_TargetHndl *dtHndl;
  1538.  
  1539.     for (dtHndl=dtPtr->handlers; dtHndl; dtHndl=dtHndl->next)
  1540.         if (strcmp(dtHndl->dataType,dtname) == 0)
  1541.             return dtHndl->command;
  1542.  
  1543.     return NULL;
  1544. }
  1545.  
  1546. /*
  1547.  * ------------------------------------------------------------------------
  1548.  *  PutTargetHandler()
  1549.  *
  1550.  *  Looks for the requested data type of the list of handlers for the
  1551.  *  given drag&drop target.  If found, then its associated command is
  1552.  *  changed to the given command.  If not found, then a new handler
  1553.  *  is created.
  1554.  * ------------------------------------------------------------------------
  1555.  */
  1556. static void
  1557. PutTargetHandler(dtPtr, dtname, cmd)
  1558.     register DD_Target *dtPtr; /* drag&drop target widget record */
  1559.     char *dtname;              /* name of data type */
  1560.     char *cmd;                 /* command string for data type */
  1561. {
  1562.     DD_TargetHndl *tail = NULL;
  1563.     DD_TargetHndl *dtHndl;
  1564.  
  1565.     for (dtHndl=dtPtr->handlers; dtHndl; tail=dtHndl,dtHndl=dtHndl->next)
  1566.         if (strcmp(dtHndl->dataType,dtname) == 0)
  1567.         {
  1568.             if (*cmd == '\0')
  1569.             {
  1570.                 if (tail)
  1571.                     tail->next = dtHndl->next;
  1572.                 else
  1573.                     dtPtr->handlers = dtHndl->next;
  1574.  
  1575.                 DestroyTargetHandler(dtHndl);
  1576.                 return;
  1577.             }
  1578.             else
  1579.             {
  1580.                 if (dtHndl->command != NULL) {
  1581.                     free(dtHndl->command);
  1582.                 }
  1583.                 dtHndl->command = strdup(cmd);
  1584.                 return;
  1585.             }
  1586.         }
  1587.  
  1588.     if (tail)
  1589.         tail->next = CreateTargetHandler(dtname,cmd);
  1590.     else
  1591.         dtPtr->handlers = CreateTargetHandler(dtname,cmd);
  1592.  
  1593.     /*
  1594.      *  Update handler list stored in target window property.
  1595.      */
  1596.     AddDDProp(dtPtr);
  1597. }
  1598.  
  1599. /*
  1600.  * ------------------------------------------------------------------------
  1601.  *  CreateTargetHandler()
  1602.  *
  1603.  *  Creates a new target handler record and returns a pointer to it.
  1604.  * ------------------------------------------------------------------------
  1605.  */
  1606. static DD_TargetHndl*
  1607. CreateTargetHandler(dtname, cmd)
  1608.     char *dtname;              /* name of data type */
  1609.     char *cmd;                 /* command string for data type */
  1610. {
  1611.     DD_TargetHndl *retn;
  1612.     retn = (DD_TargetHndl*)malloc(sizeof(DD_TargetHndl));
  1613.  
  1614.     retn->dataType = strdup(dtname);
  1615.     retn->command = strdup(cmd);
  1616.  
  1617.     retn->next = NULL;
  1618.     return retn;
  1619. }
  1620.  
  1621. /*
  1622.  * ------------------------------------------------------------------------
  1623.  *  DestroyTargetHandler()
  1624.  *
  1625.  *  Destroys a target handler record.
  1626.  * ------------------------------------------------------------------------
  1627.  */
  1628. static void
  1629. DestroyTargetHandler(dtHndl)
  1630.     DD_TargetHndl *dtHndl;
  1631. {
  1632.     if (dtHndl->dataType != NULL) {
  1633.         free(dtHndl->dataType);
  1634.     }
  1635.     if (dtHndl->command != NULL) {
  1636.         free(dtHndl->command);
  1637.     }
  1638.     free((char*)dtHndl);
  1639. }
  1640.  
  1641. /*
  1642.  * ------------------------------------------------------------------------
  1643.  *  UnregTarget()
  1644.  *
  1645.  *  Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
  1646.  *  on a registered drag&drop target widget.
  1647.  * ------------------------------------------------------------------------
  1648.  */
  1649. static void
  1650. UnregTarget(cdata, eventPtr)
  1651.     ClientData cdata;   /* drag&drop registration list */
  1652.     XEvent *eventPtr;   /* event description */
  1653. {
  1654.     DD_RegEntry *ddentry = (DD_RegEntry*)cdata;
  1655.     DD_RegList *ddlist = ddentry->ddlist;
  1656.     char *ddname = Tk_PathName(ddentry->tkwin);
  1657.  
  1658.     if (eventPtr->type == DestroyNotify)
  1659.     {
  1660.         DestroyTargetInfo(ddlist,ddname);
  1661.         free((char*)ddentry);
  1662.     }
  1663. }
  1664.  
  1665. /*
  1666.  * ------------------------------------------------------------------------
  1667.  *  DragDropSend()
  1668.  *
  1669.  *  Invoked after a drop operation to send data to the drop application.
  1670.  * ------------------------------------------------------------------------
  1671.  */
  1672. static void
  1673. DragDropSend(dsPtr)
  1674.     register DD_Source *dsPtr;  /* drag&drop source record */
  1675. {
  1676.     DD_RegList *ddlist = dsPtr->ddlist;
  1677.  
  1678.     int status;
  1679.     char *sendcmd;
  1680.     DD_WinRep *target;
  1681.  
  1682.     /*
  1683.      *  See if current position is over drop point...
  1684.      */
  1685.     target = FindTargetWin(dsPtr, dsPtr->tokenx,dsPtr->tokeny);
  1686.  
  1687.     if (target)
  1688.     {
  1689.         char buffer[256];
  1690.  
  1691.         sprintf(buffer, "%d %d", dsPtr->tokenx, dsPtr->tokeny);
  1692.         status = Tcl_VarEval(ddlist->interp,
  1693.             "send {",target->ddinterp,"} ", DRAGDROP_COMMAND," location ",buffer,
  1694.             (char*)NULL);
  1695.  
  1696.         if (status == TCL_OK)
  1697.         {
  1698.             sendcmd = DragDropSendHndlr(dsPtr, target->ddinterp, target->ddwin);
  1699.             if (sendcmd)
  1700.             {
  1701.                 status = Tcl_VarEval(ddlist->interp,
  1702.                     sendcmd, " {",target->ddinterp,"} {",target->ddwin,
  1703.                     "} {",dsPtr->pkgcmdResult,"}",
  1704.                     (char*)NULL);
  1705.             }
  1706.             else
  1707.             {
  1708.                 Tcl_AppendResult(ddlist->interp, "target \"",
  1709.                     target->ddwin,
  1710.                     "\" does not recognize handlers for source \"",
  1711.                     Tk_PathName(dsPtr->tkwin), "\"",
  1712.                     (char*)NULL);
  1713.                 status = TCL_ERROR;
  1714.             }
  1715.         }
  1716.  
  1717.         /*
  1718.          *  Give success/failure feedback to user.
  1719.          *  If an error occurred and an error proc is defined,
  1720.          *  then use it to handle the error.
  1721.          */
  1722.         if (status == TCL_OK)
  1723.             HideDDToken((ClientData)dsPtr);
  1724.         else
  1725.         {
  1726.             RejectDDToken(dsPtr);
  1727.  
  1728.             if (ddlist->errorProc && *ddlist->errorProc)
  1729.                 (void) Tcl_VarEval(ddlist->interp,
  1730.                     ddlist->errorProc, " {", ddlist->interp->result, "}",
  1731.                     (char*)NULL);
  1732.         }
  1733.     }
  1734. }
  1735.  
  1736. /*
  1737.  * ------------------------------------------------------------------------
  1738.  *  DragDropSendHndlr()
  1739.  *
  1740.  *  Queries the drag&drop target under the specified interpreter for a
  1741.  *  handler that is compatible with one of the handlers defined for the
  1742.  *  source.  Returns a pointer to the appropriate send command, or
  1743.  *  NULL if none is found.
  1744.  * ------------------------------------------------------------------------
  1745.  */
  1746. static char*
  1747. DragDropSendHndlr(dsPtr,interpName,ddName)
  1748.     register DD_Source *dsPtr;  /* drag&drop source record */
  1749.     char *interpName;           /* interpreter containing drag&drop target */
  1750.     char *ddName;               /* drag&drop target pathname */
  1751. {
  1752.     char *retn = NULL;  /* no handler found yet */
  1753.     Tcl_Interp *interp = dsPtr->ddlist->interp;
  1754.  
  1755.     int hndlc, hi, ei;
  1756.     char **hndlv, *hlist;
  1757.     DD_SourceHndl *dsHndl;
  1758.     char buffer[1024];
  1759.  
  1760.     /*
  1761.      *  Query the drag&drop target for its list of known handlers.
  1762.      */
  1763.     Tcl_ResetResult(interp); /* for Tcl_AppendResult() below */
  1764.     if (Tcl_VarEval(interp,
  1765.         "send {",interpName,"} ", DRAGDROP_COMMAND," target {",ddName,"} handler",
  1766.         (char*)NULL) != TCL_OK)
  1767.         return NULL;
  1768.  
  1769.     hlist = strdup(interp->result);
  1770.     if (Tcl_SplitList(interp, hlist, &hndlc, &hndlv) == TCL_OK)
  1771.     {
  1772.         /*
  1773.          *  If the list of send handlers is specified as "all", then
  1774.          *  search through the handlers in order.
  1775.          */
  1776.         if (strcmp(dsPtr->send,"all") == 0)
  1777.             for (dsHndl=dsPtr->handlers; dsHndl && !retn; dsHndl=dsHndl->next)
  1778.             {
  1779.                 for (hi=0; (hi < hndlc) && !retn; hi++)
  1780.                     if (strcmp(dsHndl->dataType, hndlv[hi]) == 0)
  1781.                         retn = dsHndl->cmd;
  1782.             }
  1783.  
  1784.         /*
  1785.          *  Otherwise, search through the specified send handlers.
  1786.          */
  1787.         else
  1788.         {
  1789.             int elemc;
  1790.             char **elemv;
  1791.             if (Tcl_SplitList(interp,dsPtr->send,&elemc,&elemv)==TCL_OK)
  1792.             {
  1793.                 for (ei=0; (ei < elemc) && !retn; ei++)
  1794.                     for (hi=0; (hi < hndlc) && !retn; hi++)
  1795.                         if (strcmp(elemv[ei], hndlv[hi]) == 0)
  1796.                         {
  1797.                             retn = FindSourceHandler(dsPtr, elemv[ei]);
  1798.                             if (!retn)
  1799.                             {
  1800.                                 sprintf(buffer, "unknown handler \"%.50s\" requested for drag&drop source \"%.200s\"", elemv[ei], Tk_PathName(dsPtr->tkwin));
  1801.                                 Tcl_ResetResult(interp);
  1802.                                 Tcl_AddErrorInfo(interp, buffer);
  1803.                                 Tk_BackgroundError(interp);
  1804.                             }
  1805.                         }
  1806.  
  1807.                 free((char*)elemv);
  1808.             }
  1809.             else
  1810.             {
  1811.                 sprintf(buffer, "drag&drop source has invalid -send: %.200s",
  1812.                     dsPtr->send);
  1813.                 Tcl_ResetResult(interp);
  1814.                 Tcl_AddErrorInfo(interp, buffer);
  1815.                 Tk_BackgroundError(interp);
  1816.             }
  1817.         }
  1818.         free((char*)hndlv);
  1819.     }
  1820.     free(hlist);
  1821.  
  1822.     return retn;
  1823. }
  1824.  
  1825.  
  1826. /*
  1827.  * ------------------------------------------------------------------------
  1828.  *  GetWinRepInfo()
  1829.  *
  1830.  *  Invoked at the start of a "drag" operation to capture the positions
  1831.  *  of all windows on the current root.  Queries the entire window
  1832.  *  hierarchy and determines the placement of each window.  Queries
  1833.  *  "DragDropInfo" property info where appropriate.  This information
  1834.  *  is used during the drag operation to determine when the drag&drop
  1835.  *  token is over a valid drag&drop target.
  1836.  *
  1837.  *  Returns the record for the root window, which contains records for
  1838.  *  all other windows as children.
  1839.  * ------------------------------------------------------------------------
  1840.  */
  1841. static DD_WinRep*
  1842. GetWinRepInfo(dsPtr,ddlist)
  1843.     DD_Source *dsPtr;     /* drag&drop source window */
  1844.     DD_RegList *ddlist;   /* drag&drop registration info */
  1845. {
  1846.     DD_WinRep *wr;
  1847.  
  1848.     wr = WinRepAlloc(ddlist);
  1849.     wr->win = DefaultRootWindow(dsPtr->display);
  1850.     WinRepInit(wr, dsPtr);
  1851.  
  1852.     return wr;
  1853. }
  1854.  
  1855. /*
  1856.  * ------------------------------------------------------------------------
  1857.  *  FindTargetWin()
  1858.  *
  1859.  *  Checks to see if a compatible drag&drop target exists at the given
  1860.  *  position.  A target is "compatible" if it is a drag&drop window,
  1861.  *  and if it has a handler that is compatible with the current source
  1862.  *  window.
  1863.  *
  1864.  *  Returns a pointer to a structure describing the target, or NULL
  1865.  *  if no compatible target is found.
  1866.  * ------------------------------------------------------------------------
  1867.  */
  1868. static DD_WinRep*
  1869. FindTargetWin(dsPtr,x,y)
  1870.     DD_Source *dsPtr;     /* drag&drop source window */
  1871.     int x,y;              /* current drag&drop location (virtual coords) */
  1872. {
  1873.     int vx, vy;
  1874.     unsigned int vw, vh;
  1875.     register char *type;
  1876.  
  1877.     DD_WinRep *wr, *wrkid;
  1878.     DD_Stack stack;
  1879.     DD_SourceHndl *shandl;
  1880.  
  1881.     /*
  1882.      *  If window representations have not yet been built, then
  1883.      *  abort this call.  This probably means that the token is being
  1884.      *  moved before it has been properly built.
  1885.      */
  1886.     if (!dsPtr->allwins)
  1887.         return NULL;
  1888.  
  1889.     /*
  1890.      *  Adjust current location for virtual root windows.
  1891.      */
  1892.     Tk_GetVRootGeometry(dsPtr->tkwin, &vx, &vy, &vw, &vh);
  1893.     x += vx;
  1894.     y += vy;
  1895.  
  1896.     /*
  1897.      *  Build a stack of all windows containing the given point,
  1898.      *  in order from least to most specific.
  1899.      */
  1900.     StackInit(&stack);
  1901.  
  1902.     wr = dsPtr->allwins;
  1903.     if ((x >= wr->x0) && (x <= wr->x1) &&
  1904.         (y >= wr->y0) && (y <= wr->y1))
  1905.         StackPush((ClientData)wr, &stack);
  1906.  
  1907.     while (wr)
  1908.     {
  1909.         for (wrkid=wr->kids; wrkid; wrkid=wrkid->next)
  1910.         {
  1911.             if (!wrkid->initialized)
  1912.                 WinRepInit(wrkid, dsPtr);
  1913.  
  1914.             if ((x >= wrkid->x0) && (x <= wrkid->x1) &&
  1915.                 (y >= wrkid->y0) && (y <= wrkid->y1))
  1916.             {
  1917.                 StackPush((ClientData)wrkid, &stack);
  1918.                 break;
  1919.             }
  1920.         }
  1921.         wr = wrkid;  /* continue search */
  1922.     }
  1923.  
  1924.     /*
  1925.      *  Pop windows from the stack until one containing a
  1926.      *  "DragDropInfo" property is found.  See if the handlers
  1927.      *  listed in this property are compatible with the
  1928.      *  given source.
  1929.      */
  1930.     while ((wr=(DD_WinRep*)StackPop(&stack)) != NULL)
  1931.         if (wr->ddprop)
  1932.             break;
  1933.  
  1934.     if (wr && wr->ddhandlers)
  1935.     {
  1936.         type = wr->ddhandlers;
  1937.         while (*type != '\0')
  1938.         {
  1939.             for (shandl=dsPtr->handlers; shandl; shandl=shandl->next)
  1940.                 if (strcmp(shandl->dataType, type) == 0)
  1941.                     break;
  1942.  
  1943.             if (shandl)   /* found a match? */
  1944.                 break;    /* then stop searching */
  1945.             else          /* otherwise, move to next handler type */
  1946.             {
  1947.                 while (*type++ != '\0')
  1948.                     ;
  1949.             }
  1950.         }
  1951.         if (*type == '\0')  /* no handler match? */
  1952.             wr = NULL;      /* then return NULL */
  1953.     }
  1954.     StackDelete(&stack);
  1955.  
  1956.     return wr;
  1957. }
  1958.  
  1959.  
  1960. /*
  1961.  * ------------------------------------------------------------------------
  1962.  *  WinRepAlloc()
  1963.  *
  1964.  *  Returns a new structure for representing X window position info.
  1965.  *  Such structures are typically allocated at the start of a drag&drop
  1966.  *  operation to capture the placement of all windows on the root
  1967.  *  window.  The drag&drop registration list keeps a pool of such
  1968.  *  structures so that they can be allocated quickly when needed.
  1969.  *  Returns a pointer to an empty structure.
  1970.  * ------------------------------------------------------------------------
  1971.  */
  1972. static DD_WinRep*
  1973. WinRepAlloc(ddlist)
  1974.     DD_RegList *ddlist;  /* drag&drop registration list */
  1975. {
  1976.     DD_WinRep *wr;
  1977.  
  1978.     /*
  1979.      *  Return the top-most structure in the pool.
  1980.      *  If the pool is empty, add a new structure to it.
  1981.      */
  1982.     if (!ddlist->pool)
  1983.     {
  1984.         wr = (DD_WinRep*)malloc(sizeof(DD_WinRep));
  1985.         wr->next = NULL;
  1986.         ddlist->pool = wr;
  1987.     }
  1988.     wr = ddlist->pool;
  1989.     ddlist->pool = wr->next;
  1990.  
  1991.     wr->initialized = 0;
  1992.     wr->ddprop = NULL;
  1993.     wr->ddinterp = wr->ddwin = wr->ddhandlers = NULL;
  1994.     wr->parent = wr->kids = wr->next = NULL;
  1995.     return wr;
  1996. }
  1997.  
  1998. /*
  1999.  * ------------------------------------------------------------------------
  2000.  *  WinRepRelease()
  2001.  *
  2002.  *  Puts a window representation structure back into the global pool,
  2003.  *  making it available for future calls to WinRepAlloc().  Any
  2004.  *  associated resources (within the structure) are automatically freed.
  2005.  * ------------------------------------------------------------------------
  2006.  */
  2007. static void
  2008. WinRepRelease(wr,ddlist)
  2009.     DD_WinRep *wr;       /* window rep to be freed */
  2010.     DD_RegList *ddlist;  /* drag&drop registration list */
  2011. {
  2012.     DD_WinRep *wrkid, *wrnext;
  2013.  
  2014.     for (wrkid=wr->kids; wrkid; wrkid=wrnext)
  2015.     {
  2016.         wrnext = wrkid->next;
  2017.         WinRepRelease(wrkid,ddlist);
  2018.     }
  2019.  
  2020.     if (wr->ddprop)
  2021.         XFree(wr->ddprop);
  2022.  
  2023.     wr->next = ddlist->pool;  /* put back into pool */
  2024.     ddlist->pool = wr;
  2025. }
  2026.  
  2027.  
  2028. /*
  2029.  * ------------------------------------------------------------------------
  2030.  *  WinRepInit()
  2031.  *
  2032.  *  Invoked during "drag" operations to dig a little deeper into the
  2033.  *  root window hierarchy and cache the resulting information.  If a
  2034.  *  point coordinate lies within an uninitialized DD_WinRep, this
  2035.  *  routine is called to query window coordinates and drag&drop info.
  2036.  *  If this particular window has any children, more uninitialized
  2037.  *  DD_WinRep structures are allocated.  Further queries will cause
  2038.  *  these structures to be initialized in turn.
  2039.  * ------------------------------------------------------------------------
  2040.  */
  2041. static void
  2042. WinRepInit(wr,dsPtr)
  2043.     DD_WinRep *wr;         /* window rep to be initialized */
  2044.     DD_Source *dsPtr;      /* drag&drop source managing win rep */
  2045. {
  2046.     Window ignoreSource = Tk_WindowId(dsPtr->tkwin);
  2047.     Window ignoreToken  = Tk_WindowId(dsPtr->tokenwin);
  2048.  
  2049.     DD_WinRep *wrkid, *wrtail;
  2050.  
  2051.     Window root, parent, *kids;
  2052.     unsigned int nkids;
  2053.     XWindowAttributes winInfo;
  2054.  
  2055.     char *propInfo;
  2056.     int i, result, actualFormat;
  2057.     Atom actualType;
  2058.     unsigned long numItems, bytesAfter;
  2059.  
  2060.     /*
  2061.      *  If the self-target flag is set, allow the source window to
  2062.      *  drop onto itself.  Do not ignore source window during search.
  2063.      */
  2064.     if (dsPtr->selfTarget)
  2065.         ignoreSource = None;
  2066.  
  2067.     if (!wr->initialized)
  2068.     {
  2069.         /*
  2070.          *  Query for the window coordinates.
  2071.          */
  2072.         if (XGetWindowAttributes(dsPtr->display, wr->win, &winInfo) &&
  2073.             (winInfo.map_state == IsViewable) &&
  2074.             (wr->win != ignoreToken) &&
  2075.             (wr->win != ignoreSource))
  2076.         {
  2077.             wr->x0  = winInfo.x;
  2078.             wr->y0  = winInfo.y;
  2079.             wr->x1  = winInfo.x + winInfo.width;
  2080.             wr->y1  = winInfo.y + winInfo.height;
  2081.  
  2082.             if (wr->parent)  /* offset by parent coords */
  2083.             {
  2084.                 wr->x0 += wr->parent->x0;
  2085.                 wr->y0 += wr->parent->y0;
  2086.                 wr->x1 += wr->parent->x0;
  2087.                 wr->y1 += wr->parent->y0;
  2088.             }
  2089.         }
  2090.         else
  2091.         {
  2092.             wr->x0 = wr->y0 = -1;
  2093.             wr->x1 = wr->y1 = -1;
  2094.         }
  2095.  
  2096.         /*
  2097.          *  See if this window has a "DragDropInfo" property.
  2098.          */
  2099.         result = XGetWindowProperty(dsPtr->display, wr->win,
  2100.             dsPtr->ddAtom, 0, MAX_PROP_SIZE, False, XA_STRING,
  2101.             &actualType, &actualFormat,
  2102.             &numItems, &bytesAfter, (unsigned char**)&propInfo);
  2103.  
  2104.         if ((result != Success) ||
  2105.             (actualFormat != 8) ||
  2106.             (actualType != XA_STRING))
  2107.         {
  2108.             if (propInfo != NULL) {
  2109.                 XFree((caddr_t)propInfo);
  2110.             }
  2111.             propInfo = NULL;
  2112.         }
  2113.  
  2114.         wr->ddprop = propInfo;
  2115.         if (wr->ddprop)
  2116.         {
  2117.             char *p = wr->ddprop;
  2118.             wr->ddinterp = wr->ddprop;
  2119.  
  2120.             while ((*p != '\0') && (*p != ']'))
  2121.                 p++;
  2122.  
  2123.             if (*p != '\0')
  2124.             {
  2125.                 *p++ = '\0';         /* terminate interp name */
  2126.                 wr->ddwin = p;       /* get start of window name */
  2127.             }
  2128.  
  2129.             while ((*p != '\0') && (*p != ']'))
  2130.                 p++;
  2131.  
  2132.             if (*p != '\0')
  2133.             {
  2134.                 *p++ = '\0';         /* terminate window name */
  2135.                 wr->ddhandlers = p;  /* get start of handler list */
  2136.  
  2137.                 /*
  2138.                  *  Handler strings are of the form:
  2139.                  *  "<type> <type> ... <type> "
  2140.                  */
  2141.                 while (*p != '\0')
  2142.                 {
  2143.                     while ((*p != ' ') && (*p != '\0'))
  2144.                         p++;
  2145.  
  2146.                     *p++ = '\0';  /* null terminate handler type */
  2147.                 }
  2148.             }
  2149.         }
  2150.  
  2151.         /*
  2152.          *  If this window has any children, then create DD_WinReps
  2153.          *  for them as well.
  2154.          */
  2155.         if (XQueryTree(dsPtr->display, wr->win, &root, &parent, &kids, &nkids))
  2156.         {
  2157.             wrtail = NULL;
  2158.             for (i=nkids-1; i >= 0; i--)
  2159.             {
  2160.                 wrkid = WinRepAlloc(dsPtr->ddlist);
  2161.                 wrkid->win = kids[i];
  2162.                 wrkid->parent = wr;
  2163.  
  2164.                 if (wrtail)
  2165.                     wrtail->next = wrkid;
  2166.                 else
  2167.                     wr->kids = wrkid;
  2168.  
  2169.                 wrtail = wrkid;
  2170.             }
  2171.             if (kids != NULL) {
  2172.                 XFree((caddr_t)kids);   /* done with list of kids */
  2173.             }
  2174.         }
  2175.     }
  2176.     wr->initialized = ~0;
  2177. }
  2178.  
  2179.  
  2180. /*
  2181.  * ------------------------------------------------------------------------
  2182.  *  AddDDProp()
  2183.  *
  2184.  *  Attaches a "DragDropInfo" property to the given target window.  This
  2185.  *  property allows the drag&drop mechanism to recognize the window as
  2186.  *  a valid target, and stores important information including the
  2187.  *  interpreter managing the target and the pathname for the target
  2188.  *  window.  Usually invoked when the target is first registered or
  2189.  *  first exposed (so that the X-window really exists).
  2190.  * ------------------------------------------------------------------------
  2191.  */
  2192. static void
  2193. AddDDProp(dtPtr)
  2194.     DD_Target* dtPtr;  /* drag&drop target window data */
  2195. {
  2196.     Tcl_Interp *interp = dtPtr->ddlist->interp;
  2197.  
  2198.     Atom ddProperty;
  2199.     char buffer[MAX_PROP_SIZE], *path, *info;
  2200.     DD_TargetHndl *thandl;
  2201.  
  2202.     if (dtPtr->tkwin != None)
  2203.     {
  2204.         static char command[] = { "winfo name ." };
  2205.  
  2206.         path = Tk_PathName(dtPtr->tkwin);
  2207.         if (Tcl_Eval(interp, command)==TCL_OK)
  2208.             sprintf(buffer, "%s]%s]", interp->result, path);
  2209.         else
  2210.             sprintf(buffer, "]%s]", path);
  2211.  
  2212.         Tcl_SetResult(interp, buffer, TCL_VOLATILE);
  2213.         for (thandl=dtPtr->handlers; thandl; thandl=thandl->next)
  2214.             Tcl_AppendResult(interp, thandl->dataType, " ", (char*)NULL);
  2215.  
  2216.         ddProperty = XInternAtom(dtPtr->display, DRAGDROP_PROPINFO, False);
  2217.         info = interp->result;
  2218.  
  2219.         XChangeProperty(dtPtr->display, Tk_WindowId(dtPtr->tkwin),
  2220.             ddProperty, XA_STRING, 8, PropModeReplace,
  2221.             (unsigned char*)info, strlen(info)+1);
  2222.     }
  2223. }
  2224.  
  2225. /*
  2226.  * ------------------------------------------------------------------------
  2227.  *  DDTokenEventProc()
  2228.  *
  2229.  *  Invoked by the Tk dispatcher to handle widget events.
  2230.  *  Manages redraws for the drag&drop token window.
  2231.  * ------------------------------------------------------------------------
  2232.  */
  2233. static void
  2234. DDTokenEventProc(clientData, eventPtr)
  2235.     ClientData clientData;     /* data associated with widget */
  2236.     XEvent *eventPtr;          /* information about event */
  2237. {
  2238.     register DD_Source *dsPtr = (DD_Source*)clientData;
  2239.  
  2240.     if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0))
  2241.     {
  2242.         if (dsPtr->tokenwin)
  2243.         {
  2244.             Tk_Window tkwin = dsPtr->tokenwin;
  2245.             int bd;
  2246.  
  2247.             bd = dsPtr->tokenBorderWidth;
  2248.             Tk_Fill3DRectangle(dsPtr->display, Tk_WindowId(tkwin),
  2249.                 dsPtr->tokenOutline,
  2250.                 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  2251.                 0, TK_RELIEF_FLAT);
  2252.             Tk_Fill3DRectangle(Tk_Display(tkwin), Tk_WindowId(tkwin),
  2253.                 dsPtr->tokenBorder,
  2254.                 bd, bd, Tk_Width(tkwin)-2*bd, Tk_Height(tkwin)-2*bd,
  2255.                 bd, (dsPtr->overTargetWin) ? TK_RELIEF_RAISED : TK_RELIEF_FLAT);
  2256.         }
  2257.     }
  2258.     else if (eventPtr->type == DestroyNotify)
  2259.         dsPtr->tokenwin = NULL;
  2260. }
  2261.  
  2262. /*
  2263.  * ------------------------------------------------------------------------
  2264.  *  MoveDDToken()
  2265.  *
  2266.  *  Invoked during "drag" operations to move a token window to its
  2267.  *  current "drag" coordinate.
  2268.  * ------------------------------------------------------------------------
  2269.  */
  2270. static void
  2271. MoveDDToken(dsPtr)
  2272.     DD_Source *dsPtr;  /* drag&drop source window data */
  2273. {
  2274.     int x = dsPtr->tokenx;
  2275.     int y = dsPtr->tokeny;
  2276.     Tk_Window tokenwin = dsPtr->tokenwin;
  2277.  
  2278.     int max;
  2279.     int vx, vy;
  2280.     unsigned int vw, vh;
  2281.  
  2282.     /*
  2283.      *  Adjust current location for virtual root windows.
  2284.      */
  2285.     Tk_GetVRootGeometry(dsPtr->tkwin, &vx, &vy, &vw, &vh);
  2286.     x += vx;
  2287.     y += vy;
  2288.  
  2289.     max = WidthOfScreen(Tk_Screen(dsPtr->tkwin)) - Tk_Width(tokenwin);
  2290.     switch (dsPtr->tokenAnchor)
  2291.     {
  2292.     case TK_ANCHOR_NW:
  2293.     case TK_ANCHOR_W:
  2294.     case TK_ANCHOR_SW:
  2295.         break;
  2296.  
  2297.     case TK_ANCHOR_N:
  2298.     case TK_ANCHOR_CENTER:
  2299.     case TK_ANCHOR_S:
  2300.         x -= Tk_Width(tokenwin)/2;
  2301.         break;
  2302.  
  2303.     case TK_ANCHOR_NE:
  2304.     case TK_ANCHOR_E:
  2305.     case TK_ANCHOR_SE:
  2306.         x -= Tk_Width(tokenwin);
  2307.         break;
  2308.     }
  2309.     if (x > max) {
  2310.         x = max;
  2311.     } else if (x < 0) {
  2312.         x = 0;
  2313.     }
  2314.     max = HeightOfScreen(Tk_Screen(dsPtr->tkwin)) - Tk_Height(tokenwin);
  2315.     switch (dsPtr->tokenAnchor)
  2316.     {
  2317.     case TK_ANCHOR_NW:
  2318.     case TK_ANCHOR_N:
  2319.     case TK_ANCHOR_NE:
  2320.         break;
  2321.  
  2322.     case TK_ANCHOR_W:
  2323.     case TK_ANCHOR_CENTER:
  2324.     case TK_ANCHOR_E:
  2325.         y -= Tk_Height(tokenwin)/2;
  2326.         break;
  2327.  
  2328.     case TK_ANCHOR_SW:
  2329.     case TK_ANCHOR_S:
  2330.     case TK_ANCHOR_SE:
  2331.         y -= Tk_Height(tokenwin);
  2332.         break;
  2333.     }
  2334.     if (y > max) {
  2335.         y = max;
  2336.     } else if (y < 0) {
  2337.         y = 0;
  2338.     }
  2339.     if ((x != Tk_X(tokenwin)) || (y != Tk_Y(tokenwin)))
  2340.         Tk_MoveWindow(tokenwin, x, y);
  2341. }
  2342.  
  2343. /*
  2344.  * ------------------------------------------------------------------------
  2345.  *  UpdateDDToken()
  2346.  *
  2347.  *  Invoked when the event loop is idle to determine whether or not
  2348.  *  the current drag&drop token position is over another drag&drop
  2349.  *  target.
  2350.  * ------------------------------------------------------------------------
  2351.  */
  2352. static void
  2353. UpdateDDToken(clientData)
  2354.     ClientData clientData;  /* widget data */
  2355. {
  2356.     register DD_Source *dsPtr = (DD_Source*)clientData;
  2357.     Tk_Window tkwin = dsPtr->tokenwin;
  2358.  
  2359.     int status, bd;
  2360.  
  2361.     status = (FindTargetWin(dsPtr, dsPtr->tokenx, dsPtr->tokeny) != NULL);
  2362.  
  2363.     bd = dsPtr->tokenBorderWidth;
  2364.     if (dsPtr->overTargetWin ^ status)
  2365.     {
  2366.         Tk_Fill3DRectangle(dsPtr->display, Tk_WindowId(tkwin),
  2367.             dsPtr->tokenOutline,
  2368.             0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  2369.             0, TK_RELIEF_FLAT);
  2370.         Tk_Fill3DRectangle(dsPtr->display, Tk_WindowId(tkwin),
  2371.             dsPtr->tokenBorder,
  2372.             bd, bd, Tk_Width(tkwin)-2*bd, Tk_Height(tkwin)-2*bd,
  2373.             bd, (status) ? TK_RELIEF_RAISED : TK_RELIEF_FLAT);
  2374.  
  2375.         /*
  2376.          *  If the source has a site command, then invoke it to
  2377.          *  modify the appearance of the token window.  Pass any
  2378.          *  errors onto the drag&drop error handler.
  2379.          */
  2380.         if (dsPtr->sitecmd)
  2381.         {
  2382.             char buffer[200];
  2383.  
  2384.             sprintf(buffer, "%d", status);
  2385.             if ((Tcl_VarEval(dsPtr->ddlist->interp,
  2386.                     dsPtr->sitecmd," ",buffer," ",Tk_PathName(dsPtr->tokenwin),
  2387.                     (char*)NULL) != TCL_OK) &&
  2388.                 dsPtr->ddlist->errorProc && *dsPtr->ddlist->errorProc)
  2389.  
  2390.                 (void) Tcl_VarEval(dsPtr->ddlist->interp,
  2391.                     dsPtr->ddlist->errorProc, " {",
  2392.                     dsPtr->ddlist->interp->result, "}",
  2393.                     (char*)NULL);
  2394.         }
  2395.     }
  2396.     dsPtr->overTargetWin = status;
  2397. }
  2398.  
  2399. /*
  2400.  * ------------------------------------------------------------------------
  2401.  *  HideDDToken()
  2402.  *
  2403.  *  Unmaps the drag&drop token.  Invoked directly at the end of a
  2404.  *  successful communication, or after a delay if the communication
  2405.  *  fails (allowing the user to see a graphical picture of failure).
  2406.  * ------------------------------------------------------------------------
  2407.  */
  2408. static void
  2409. HideDDToken(clientData)
  2410.     ClientData clientData;  /* widget data */
  2411. {
  2412.     register DD_Source *dsPtr = (DD_Source*)clientData;
  2413.  
  2414.     if (dsPtr->tokenwin)
  2415.         Tk_UnmapWindow(dsPtr->tokenwin);
  2416.  
  2417.     dsPtr->hidetoken = NULL;
  2418. }
  2419.  
  2420. /*
  2421.  * ------------------------------------------------------------------------
  2422.  *  RejectDDToken()
  2423.  *
  2424.  *  Draws a rejection mark on the current drag&drop token, and arranges
  2425.  *  for the token to be unmapped after a small delay.
  2426.  * ------------------------------------------------------------------------
  2427.  */
  2428. static void
  2429. RejectDDToken(dsPtr)
  2430.     DD_Source* dsPtr;  /* widget data */
  2431. {
  2432.     int div = 6;      /* controls size of rejection symbol */
  2433.     int w,h,lwid,x,y,margin;
  2434.  
  2435.     Tk_Window tkwin = dsPtr->tokenwin;
  2436.  
  2437.     margin = 2*dsPtr->tokenBorderWidth;
  2438.     w = Tk_Width(tkwin) - 2*margin;
  2439.     h = Tk_Height(tkwin) - 2*margin;
  2440.  
  2441.     lwid = (w < h) ? w/div : h/div;
  2442.     lwid = (lwid < 1) ? 1 : lwid;
  2443.  
  2444.     w = h = lwid*(div-1);
  2445.     x = (Tk_Width(tkwin) - w)/2;
  2446.     y = (Tk_Height(tkwin) - h)/2;
  2447.  
  2448.     /*
  2449.      *  Draw the rejection symbol background (\) on the token window...
  2450.      */
  2451.     XSetLineAttributes(Tk_Display(tkwin), dsPtr->rejectBgGC,
  2452.         lwid+4, LineSolid, CapButt, JoinBevel);
  2453.  
  2454.     XDrawArc(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectBgGC,
  2455.         x, y, w, h, 0, 23040);
  2456.  
  2457.     XDrawLine(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectBgGC,
  2458.         x+lwid, y+lwid, x+w-lwid, y+h-lwid);
  2459.  
  2460.     /*
  2461.      *  Draw the rejection symbol foreground (\) on the token window...
  2462.      */
  2463.     XSetLineAttributes(Tk_Display(tkwin), dsPtr->rejectFgGC,
  2464.         lwid, LineSolid, CapButt, JoinBevel);
  2465.  
  2466.     XDrawArc(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectFgGC,
  2467.         x, y, w, h, 0, 23040);
  2468.  
  2469.     XDrawLine(Tk_Display(tkwin), Tk_WindowId(tkwin), dsPtr->rejectFgGC,
  2470.         x+lwid, y+lwid, x+w-lwid, y+h-lwid);
  2471.  
  2472.     /*
  2473.      *  Arrange for token window to disappear eventually.
  2474.      */
  2475.     dsPtr->hidetoken
  2476.         = Tk_CreateTimerHandler(1000, HideDDToken, (ClientData)dsPtr);
  2477. }
  2478.  
  2479.  
  2480. /*
  2481.  * ------------------------------------------------------------------------
  2482.  *  StackInit()
  2483.  *
  2484.  *  Initializes a stack structure, allocating a certain amount of memory
  2485.  *  for the stack and setting the stack length to zero.
  2486.  * ------------------------------------------------------------------------
  2487.  */
  2488. static void
  2489. StackInit(stack)
  2490.     DD_Stack *stack;     /* stack to be initialized */
  2491. {
  2492.     stack->values = stack->space;
  2493.     stack->max = sizeof(stack->space)/sizeof(ClientData);
  2494.     stack->len = 0;
  2495. }
  2496.  
  2497. /*
  2498.  * ------------------------------------------------------------------------
  2499.  *  StackDelete()
  2500.  *
  2501.  *  Destroys a stack structure, freeing any memory that may have been
  2502.  *  allocated to represent it.
  2503.  * ------------------------------------------------------------------------
  2504.  */
  2505. static void
  2506. StackDelete(stack)
  2507.     DD_Stack *stack;     /* stack to be deleted */
  2508. {
  2509.     if (stack->values != stack->space)  /* allocated extra memory? */
  2510.         free((char*)stack->values);   /* then free it */
  2511.  
  2512.     stack->values = NULL;
  2513.     stack->len = stack->max = 0;
  2514. }
  2515.  
  2516. /*
  2517.  * ------------------------------------------------------------------------
  2518.  *  StackPush()
  2519.  *
  2520.  *  Pushes a piece of client data onto the top of the given stack.
  2521.  * ------------------------------------------------------------------------
  2522.  */
  2523. static void
  2524. StackPush(cdata,stack)
  2525.     ClientData cdata;    /* data to be pushed onto stack */
  2526.     DD_Stack *stack;     /* stack */
  2527. {
  2528.     ClientData *newStack;
  2529.  
  2530.     if (stack->len+1 >= stack->max)
  2531.     {
  2532.         stack->max = (stack->max == 0) ? 5 : 2*stack->max;
  2533.         newStack = (ClientData*)
  2534.             malloc((unsigned)(stack->max*sizeof(ClientData)));
  2535.  
  2536.         if (stack->values)
  2537.         {
  2538.             memcpy((char *)newStack, (char *)stack->values,
  2539.                 stack->len*sizeof(ClientData));
  2540.  
  2541.             if (stack->values != stack->space)
  2542.                 free((char*)stack->values);
  2543.         }
  2544.         stack->values = newStack;
  2545.     }
  2546.     stack->values[stack->len++] = cdata;
  2547. }
  2548.  
  2549. /*
  2550.  * ------------------------------------------------------------------------
  2551.  *  StackPop()
  2552.  *
  2553.  *  Pops a bit of client data from the top of the given stack.
  2554.  * ------------------------------------------------------------------------
  2555.  */
  2556. static ClientData
  2557. StackPop(stack)
  2558.     DD_Stack *stack;  /* stack to be manipulated */
  2559. {
  2560.     if ((stack->values != NULL) && (stack->len > 0)) {
  2561.         return (stack->values[--stack->len]);
  2562.     }
  2563.     return (ClientData) 0;
  2564. }
  2565.