summaryrefslogtreecommitdiff
path: root/blt/src/bltDragdrop.c
diff options
context:
space:
mode:
Diffstat (limited to 'blt/src/bltDragdrop.c')
-rw-r--r--blt/src/bltDragdrop.c2715
1 files changed, 2715 insertions, 0 deletions
diff --git a/blt/src/bltDragdrop.c b/blt/src/bltDragdrop.c
new file mode 100644
index 00000000000..f0140a9c173
--- /dev/null
+++ b/blt/src/bltDragdrop.c
@@ -0,0 +1,2715 @@
+/*
+ * bltDnd.c --
+ *
+ * This module implements a drag-and-drop mechanism for the Tk
+ * Toolkit. Allows widgets to be registered as drag&drop sources
+ * and targets for handling "drag-and-drop" operations between
+ * Tcl/Tk applications.
+ *
+ * Copyright 1993-1998 Lucent Technologies, Inc.
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and warranty
+ * disclaimer appear in supporting documentation, and that the names
+ * of Lucent Technologies any of their entities not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.
+ *
+ * Lucent Technologies disclaims all warranties with regard to this
+ * software, including all implied warranties of merchantability and
+ * fitness. In no event shall Lucent Technologies be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether in
+ * an action of contract, negligence or other tortuous action, arising
+ * out of or in connection with the use or performance of this
+ * software.
+ *
+ * The "drag&drop" command was created by Michael J. McLennan.
+ */
+#include "bltInt.h"
+
+#ifndef NO_DRAGDROP
+#include "bltHash.h"
+#include "bltChain.h"
+
+#include <X11/Xatom.h>
+
+#ifdef WIN32
+#define MAX_PROP_SIZE 255 /* Maximum size of property. */
+typedef HWND WINDOW;
+#else
+#define MAX_PROP_SIZE 1000 /* Maximum size of property. */
+typedef Window WINDOW;
+static Atom dndAtom;
+#endif
+
+/*
+ * Each "drag&drop" target widget is tagged with a "BltDrag&DropTarget"
+ * property in XA_STRING format. This property identifies the window
+ * as a "drag&drop" target. It's formated as a Tcl list and contains
+ * the following information:
+ *
+ * "INTERP_NAME TARGET_NAME DATA_TYPE DATA_TYPE ..."
+ *
+ * INTERP_NAME Name of the target application's interpreter.
+ * TARGET_NAME Path name of widget registered as the drop target.
+ * DATA_TYPE One or more "types" handled by the target.
+ *
+ * When the user invokes the "drag" operation, the window hierarchy
+ * is progressively examined. Window information is cached during
+ * the operation, to minimize X server traffic. Windows carrying a
+ * "BltDrag&DropTarget" property are identified. When the token is
+ * dropped over a valid site, the drop information is sent to the
+ * application
+ * via the usual "send" command. If communication fails, the drag&drop
+ * facility automatically posts a rejection symbol on the token window.
+ */
+
+#define INTERP_NAME 0
+#define TARGET_NAME 1
+#define DATA_TYPE 2
+
+/* Error Proc used to report drag&drop background errors */
+#define DEF_ERROR_PROC "bgerror"
+/*
+ * CONFIG PARAMETERS
+ */
+#define DEF_DND_BUTTON_BG_COLOR RGB_YELLOW
+#define DEF_DND_BUTTON_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_DND_BUTTON_NUMBER "3"
+#define DEF_DND_PACKAGE_COMMAND (char *)NULL
+#define DEF_DND_SELF_TARGET "no"
+#define DEF_DND_SEND "all"
+#define DEF_DND_SITE_COMMAND (char *)NULL
+#define DEF_TOKEN_ACTIVE_BG_COLOR STD_COLOR_ACTIVE_BG
+#define DEF_TOKEN_ACTIVE_BG_MONO STD_MONO_ACTIVE_BG
+#define DEF_TOKEN_ACTIVE_BORDERWIDTH "3"
+#define DEF_TOKEN_ACTIVE_RELIEF "sunken"
+#define DEF_TOKEN_ANCHOR "se"
+#define DEF_TOKEN_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TOKEN_BG_MONO STD_MONO_NORMAL_BG
+#define DEF_TOKEN_BORDERWIDTH "3"
+#define DEF_TOKEN_CURSOR "arrow"
+#define DEF_TOKEN_OUTLINE_COLOR RGB_BLACK
+#define DEF_TOKEN_OUTLINE_MONO RGB_BLACK
+#define DEF_TOKEN_REJECT_BG_COLOR STD_COLOR_NORMAL_BG
+#define DEF_TOKEN_REJECT_BG_MONO RGB_WHITE
+#define DEF_TOKEN_REJECT_FG_COLOR RGB_RED
+#define DEF_TOKEN_REJECT_FG_MONO RGB_BLACK
+#define DEF_TOKEN_REJECT_STIPPLE_COLOR (char *)NULL
+#define DEF_TOKEN_REJECT_STIPPLE_MONO RGB_GREY50
+#define DEF_TOKEN_RELIEF "raised"
+
+#if HAVE_NAMESPACES
+static char dragDropCmd[] = "blt::drag&drop";
+#else
+static char dragDropCmd[] = "drag&drop";
+#endif
+
+static char className[] = "DragDropToken"; /* CLASS NAME of token window */
+static char propName[] = "BltDrag&DropTarget"; /* Property name */
+
+static Blt_HashTable sourceTable;
+static Blt_HashTable targetTable;
+static char *errorCmd;
+static int nActive;
+static int locX, locY;
+static int initialized = FALSE;
+
+/*
+ * Percent substitutions
+ */
+typedef struct {
+ char letter; /* character like 'x' in "%x" */
+ char *value; /* value to be substituted in place of "%x" */
+} SubstDescriptors;
+
+
+/*
+ * AnyWindow --
+ *
+ * This structure represents a window hierarchy examined during
+ * a single "drag" operation. It's used to cache information
+ * to reduce the round-trip calls to the server needed to query
+ * window geometry information and grab the target property.
+ */
+typedef struct AnyWindowStruct AnyWindow;
+
+struct AnyWindowStruct {
+ WINDOW nativeWindow; /* Native window: HWINDOW (Win32) or
+ * Window (X11). */
+
+ int initialized; /* If non-zero, the rest of this structure's
+ * information had been previously built. */
+
+ int x1, y1, x2, y2; /* Extents of the window (upper-left and
+ * lower-right corners). */
+
+ AnyWindow *parentPtr; /* Parent node. NULL if root. Used to
+ * compute offset for X11 windows. */
+
+ Blt_Chain *chainPtr; /* List of this window's children. If NULL,
+ * there are no children. */
+
+ char **targetInfo; /* An array of target window drag&drop
+ * information: target interpreter,
+ * pathname, and optionally possible
+ * type matches. NULL if the window is
+ * not a drag&drop target or is not a
+ * valid match for the drop source. */
+
+};
+
+/*
+ * Drag&Drop Registration Data
+ */
+typedef struct {
+
+ /*
+ * This is a goof in the Tk API. It assumes that only an official
+ * Tk "toplevel" widget will ever become a toplevel window (i.e. a
+ * window whose parent is the root window). Because under Win32,
+ * Tk tries to use the widget record associated with the TopLevel
+ * as a Tk frame widget, to read its menu name. What this means
+ * is that any widget that's going to be a toplevel, must also look
+ * like a frame. Therefore we've copied the frame widget structure
+ * fields into the token.
+ */
+
+ Tk_Window tkwin; /* Window that embodies the frame. NULL
+ * means that the window has been destroyed
+ * but the data structures haven't yet been
+ * cleaned up. */
+ Display *display; /* Display containing widget. Used, among
+ * other things, so that resources can be
+ * freed even after tkwin has gone away. */
+ Tcl_Interp *interp; /* Interpreter associated with widget. Used
+ * to delete widget command. */
+ Tcl_Command widgetCmd; /* Token for frame's widget command. */
+ char *className; /* Class name for widget (from configuration
+ * option). Malloc-ed. */
+ int mask; /* Either FRAME or TOPLEVEL; used to select
+ * which configuration options are valid for
+ * widget. */
+ char *screenName; /* Screen on which widget is created. Non-null
+ * only for top-levels. Malloc-ed, may be
+ * NULL. */
+ char *visualName; /* Textual description of visual for window,
+ * from -visual option. Malloc-ed, may be
+ * NULL. */
+ char *colormapName; /* Textual description of colormap for window,
+ * from -colormap option. Malloc-ed, may be
+ * NULL. */
+ char *menuName; /* Textual description of menu to use for
+ * menubar. Malloc-ed, may be NULL. */
+ Colormap colormap; /* If not None, identifies a colormap
+ * allocated for this window, which must be
+ * freed when the window is deleted. */
+ Tk_3DBorder border; /* Structure used to draw 3-D border and
+ * background. NULL means no background
+ * or border. */
+ int borderWidth; /* Width of 3-D border (if any). */
+ int relief; /* 3-d effect: TK_RELIEF_RAISED etc. */
+ int highlightWidth; /* Width in pixels of highlight to draw
+ * around widget when it has the focus.
+ * 0 means don't draw a highlight. */
+ XColor *highlightBgColorPtr;
+ /* Color for drawing traversal highlight
+ * area when highlight is off. */
+ XColor *highlightColorPtr; /* Color for drawing traversal highlight. */
+ int width; /* Width to request for window. <= 0 means
+ * don't request any size. */
+ int height; /* Height to request for window. <= 0 means
+ * don't request any size. */
+ Tk_Cursor cursor; /* Current cursor for window, or None. */
+ char *takeFocus; /* Value of -takefocus option; not used in
+ * the C code, but used by keyboard traversal
+ * scripts. Malloc'ed, but may be NULL. */
+ int isContainer; /* 1 means this window is a container, 0 means
+ * that it isn't. */
+ char *useThis; /* If the window is embedded, this points to
+ * the name of the window in which it is
+ * embedded (malloc'ed). For non-embedded
+ * windows this is NULL. */
+ int flags; /* Various flags; see below for
+ * definitions. */
+
+ /* Token specific fields */
+
+ int lastX, lastY; /* last position of token window */
+ int active; /* non-zero => over target window */
+ Tcl_TimerToken timer; /* token for routine to hide tokenwin */
+ GC rejectFgGC; /* GC used to draw rejection fg: (\) */
+ GC rejectBgGC; /* GC used to draw rejection bg: (\) */
+
+ /* User-configurable fields */
+
+ Tk_Anchor anchor; /* Position of token win relative to mouse */
+ Tk_3DBorder outline; /* Outline border around token window */
+ Tk_3DBorder normalBorder; /* Border/background for token window */
+ Tk_3DBorder activeBorder; /* Border/background for token window */
+ int activeRelief;
+ int activeBorderWidth; /* Border width in pixels */
+ XColor *rejectFg; /* Color used to draw rejection fg: (\) */
+ XColor *rejectBg; /* Color used to draw rejection bg: (\) */
+ Pixmap rejectStipple; /* Stipple used to draw rejection: (\) */
+} Token;
+
+typedef struct {
+ Tcl_Interp *interp; /* Interpreter associated with the Tk source
+ * widget. */
+
+ Tk_Window tkwin; /* Tk window registered as the drag&drop
+ * source. */
+
+ Display *display; /* Drag&drop source window display */
+
+ Blt_HashTable handlerTable; /* Table of data handlers (converters)
+ * registered for this source. */
+
+ int button; /* Button used to invoke drag operation. */
+
+ Token token; /* Token used to provide special cursor. */
+
+ int pkgCmdInProgress; /* Indicates if a pkgCmd is currently active. */
+ char *pkgCmd; /* Tcl command executed at start of "drag"
+ * operation to gather information about
+ * the source data. */
+
+ char *pkgCmdResult; /* Result returned by the most recent
+ * pkgCmd. */
+
+ char *siteCmd; /* Tcl command executed to update token
+ * window. */
+
+ AnyWindow *rootPtr; /* Cached window information: Gathered
+ * and used during the "drag" operation
+ * to see if the mouse pointer is over a
+ * valid target. */
+
+ int selfTarget; /* Indicated if the source should drop onto
+ * itself. */
+
+ Tk_Cursor cursor; /* cursor restored after dragging */
+
+ char **sendTypes; /* list of data handler names or "all" */
+
+ Blt_HashEntry *hashPtr;
+
+ AnyWindow *windowPtr; /* Last target examined. If NULL, mouse
+ * pointer is not currently over a valid
+ * target. */
+} Source;
+
+typedef struct {
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* drag&drop target window */
+ Display *display; /* drag&drop target window display */
+ Blt_HashTable handlerTable; /* Table of data handlers (converters)
+ * registered for this target. */
+ Blt_HashEntry *hashPtr;
+} Target;
+
+
+extern Tk_CustomOption bltListOption;
+
+static Tk_ConfigSpec configSpecs[] =
+{
+ {TK_CONFIG_INT, "-button", "buttonBinding", "ButtonBinding",
+ DEF_DND_BUTTON_NUMBER, Tk_Offset(Source, button), 0},
+ {TK_CONFIG_STRING, "-packagecmd", "packageCommand", "Command",
+ DEF_DND_PACKAGE_COMMAND, Tk_Offset(Source, pkgCmd), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_COLOR, "-rejectbg", "rejectBackground", "Background",
+ DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Source, token.rejectBg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-rejectbg", "rejectBackground", "Background",
+ DEF_TOKEN_REJECT_BG_MONO, Tk_Offset(Source, token.rejectBg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-rejectfg", "rejectForeground", "Foreground",
+ DEF_TOKEN_REJECT_FG_COLOR, Tk_Offset(Source, token.rejectFg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-rejectfg", "rejectForeground", "Foreground",
+ DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Source, token.rejectFg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple",
+ DEF_TOKEN_REJECT_STIPPLE_COLOR, Tk_Offset(Source, token.rejectStipple),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple",
+ DEF_TOKEN_REJECT_STIPPLE_MONO, Tk_Offset(Source, token.rejectStipple),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BOOLEAN, "-selftarget", "selfTarget", "SelfTarget",
+ DEF_DND_SELF_TARGET, Tk_Offset(Source, selfTarget), 0},
+ {TK_CONFIG_CUSTOM, "-send", "send", "Send", DEF_DND_SEND,
+ Tk_Offset(Source, sendTypes), TK_CONFIG_NULL_OK, &bltListOption},
+ {TK_CONFIG_STRING, "-sitecmd", "siteCommand", "Command",
+ DEF_DND_SITE_COMMAND, Tk_Offset(Source, siteCmd), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_ANCHOR, "-tokenanchor", "tokenAnchor", "Anchor",
+ DEF_TOKEN_ANCHOR, Tk_Offset(Source, token.anchor), 0},
+ {TK_CONFIG_BORDER, "-tokenactivebackground", "tokenActiveBackground",
+ "ActiveBackground", DEF_TOKEN_ACTIVE_BG_COLOR,
+ Tk_Offset(Source, token.activeBorder), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-tokenactivebackground", "tokenActiveBackground",
+ "ActiveBackground", DEF_TOKEN_ACTIVE_BG_MONO,
+ Tk_Offset(Source, token.activeBorder), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-tokenbg", "tokenBackground", "Background",
+ DEF_TOKEN_BG_COLOR, Tk_Offset(Source, token.normalBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-tokenbg", "tokenBackground", "Background",
+ DEF_TOKEN_BG_MONO, Tk_Offset(Source, token.normalBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BORDER, "-tokenoutline", "tokenOutline", "Outline",
+ DEF_TOKEN_OUTLINE_COLOR, Tk_Offset(Source, token.outline),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-tokenoutline", "tokenOutline", "Outline",
+ DEF_TOKEN_OUTLINE_MONO, Tk_Offset(Source, token.outline),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_PIXELS, "-tokenborderwidth", "tokenBorderWidth", "BorderWidth",
+ DEF_TOKEN_BORDERWIDTH, Tk_Offset(Source, token.borderWidth), 0},
+ {TK_CONFIG_CURSOR, "-tokencursor", "tokenCursor", "Cursor",
+ DEF_TOKEN_CURSOR, Tk_Offset(Source, token.cursor),
+ TK_CONFIG_NULL_OK},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL,
+ 0, 0},
+};
+
+static Tk_ConfigSpec tokenConfigSpecs[] =
+{
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_TOKEN_ACTIVE_BG_COLOR,
+ Tk_Offset(Token, activeBorder), TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-activebackground", "activeBackground",
+ "ActiveBackground", DEF_TOKEN_ACTIVE_BG_MONO,
+ Tk_Offset(Token, activeBorder), TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_RELIEF, "-activerelief", "activeRelief", "activeRelief",
+ DEF_TOKEN_ACTIVE_RELIEF, Tk_Offset(Token, activeRelief), 0},
+ {TK_CONFIG_ANCHOR, "-anchor", "anchor", "Anchor",
+ DEF_TOKEN_ANCHOR, Tk_Offset(Token, anchor), 0},
+ {TK_CONFIG_PIXELS, "-activeborderwidth", "activeBorderWidth",
+ "ActiveBorderWidth", DEF_TOKEN_ACTIVE_BORDERWIDTH,
+ Tk_Offset(Token, activeBorderWidth), 0},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TOKEN_BG_COLOR, Tk_Offset(Token, normalBorder),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-background", "background", "Background",
+ DEF_TOKEN_BG_MONO, Tk_Offset(Token, normalBorder),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
+ DEF_TOKEN_BORDERWIDTH, Tk_Offset(Token, borderWidth), 0},
+ {TK_CONFIG_CURSOR, "-cursor", "cursor", "Cursor",
+ DEF_TOKEN_CURSOR, Tk_Offset(Token, cursor), TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BORDER, "-outline", "outline", "Outline",
+ DEF_TOKEN_OUTLINE_COLOR, Tk_Offset(Token, outline),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_BORDER, "-outline", "outline", "Outline",
+ DEF_TOKEN_OUTLINE_MONO, Tk_Offset(Token, outline),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-rejectbg", "rejectBackground", "Background",
+ DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Token, rejectBg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-rejectbg", "rejectBackground", "Background",
+ DEF_TOKEN_REJECT_BG_MONO, Tk_Offset(Token, rejectBg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_COLOR, "-rejectfg", "rejectForeground", "Foreground",
+ DEF_TOKEN_REJECT_FG_COLOR, Tk_Offset(Token, rejectFg),
+ TK_CONFIG_COLOR_ONLY},
+ {TK_CONFIG_COLOR, "-rejectfg", "rejectForeground", "Foreground",
+ DEF_TOKEN_REJECT_BG_COLOR, Tk_Offset(Token, rejectFg),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple",
+ DEF_TOKEN_REJECT_STIPPLE_COLOR, Tk_Offset(Token, rejectStipple),
+ TK_CONFIG_COLOR_ONLY | TK_CONFIG_NULL_OK},
+ {TK_CONFIG_BITMAP, "-rejectstipple", "rejectStipple", "Stipple",
+ DEF_TOKEN_REJECT_STIPPLE_MONO, Tk_Offset(Token, rejectStipple),
+ TK_CONFIG_MONO_ONLY},
+ {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
+ DEF_TOKEN_RELIEF, Tk_Offset(Token, relief), 0},
+ {TK_CONFIG_END, (char *)NULL, (char *)NULL, (char *)NULL, (char *)NULL,
+ 0, 0},
+};
+
+
+/*
+ * Forward Declarations
+ */
+static int DragDropCmd _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
+ int argc, char **argv));
+static void TokenEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void TargetEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void MoveToken _ANSI_ARGS_((Source * srcPtr, Token *tokenPtr));
+static void UpdateToken _ANSI_ARGS_((ClientData clientData));
+static void HideToken _ANSI_ARGS_((Token *tokenPtr));
+static void RejectToken _ANSI_ARGS_((Token *tokenPtr));
+
+static int GetSource _ANSI_ARGS_((Tcl_Interp *interp, char *name,
+ Source **srcPtrPtr));
+static Source *CreateSource _ANSI_ARGS_((Tcl_Interp *interp, char *pathname,
+ int *newEntry));
+static void DestroySource _ANSI_ARGS_((Source * srcPtr));
+static void SourceEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static int ConfigureSource _ANSI_ARGS_((Tcl_Interp *interp, Source * srcPtr,
+ int argc, char **argv, int flags));
+static int ConfigureToken _ANSI_ARGS_((Tcl_Interp *interp, Source * srcPtr,
+ int argc, char **argv));
+
+static Target *CreateTarget _ANSI_ARGS_((Tcl_Interp *interp, Tk_Window tkwin));
+static Target *FindTarget _ANSI_ARGS_((Tk_Window tkwin));
+static void DestroyTarget _ANSI_ARGS_((DestroyData dataPtr));
+static int OverTarget _ANSI_ARGS_((Source * srcPtr, int x, int y));
+static void AddTargetProperty _ANSI_ARGS_((Tcl_Interp *interp,
+ Target * targetPtr));
+
+static void DndSend _ANSI_ARGS_((Source * srcPtr));
+
+static void InitRoot _ANSI_ARGS_((Source * srcPtr));
+static void RemoveWindow _ANSI_ARGS_((AnyWindow *wr));
+static void QueryWindow _ANSI_ARGS_((Display *display, AnyWindow * windowPtr));
+
+static char *ExpandPercents _ANSI_ARGS_((char *str, SubstDescriptors * subs,
+ int nsubs, Tcl_DString *resultPtr));
+
+
+
+#ifdef WIN32
+
+#if _MSC_VER
+#include <tchar.h>
+#endif
+
+typedef struct {
+ char *prefix;
+ int prefixSize;
+ char *propReturn;
+} PropertyInfo;
+
+
+static BOOL CALLBACK
+GetEnumWindowsProc(HWND hWnd, LPARAM clientData)
+{
+ Blt_Chain *chainPtr = (Blt_Chain *) clientData;
+
+ Blt_ChainAppend(chainPtr, (ClientData)hWnd);
+ return TRUE;
+}
+
+static WINDOW
+GetNativeWindow(Tk_Window tkwin)
+{
+ return (WINDOW) Tk_GetHWND(Tk_WindowId(tkwin));
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetWindowZOrder --
+ *
+ * Returns a list of the child windows according to their stacking
+ * order. The window handles are ordered from top to bottom.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Blt_Chain *
+GetWindowZOrder(
+ Display *display,
+ HWND parent)
+{
+ Blt_Chain *chainPtr;
+ HWND hWnd;
+
+ chainPtr = Blt_ChainCreate();
+ for (hWnd = GetTopWindow(parent); hWnd != NULL;
+ hWnd = GetNextWindow(hWnd, GW_HWNDNEXT)) {
+ Blt_ChainAppend(chainPtr, (ClientData)hWnd);
+ }
+ return chainPtr;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetEnumPropsExProc --
+ *
+ * ------------------------------------------------------------------------
+ */
+static BOOL CALLBACK
+GetEnumPropsExProc(
+ HWND hwnd,
+ LPCTSTR atom,
+ HANDLE hData,
+ DWORD clientData)
+{
+ PropertyInfo *infoPtr = (PropertyInfo *) clientData;
+
+ if (strncmp(infoPtr->prefix, atom, infoPtr->prefixSize) == 0) {
+ assert(infoPtr->propReturn == NULL);
+ infoPtr->propReturn = (char *)atom;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetPropData --
+ *
+ * This is a bad Windows hack to pass property information between
+ * applications. (Ab)Normally the property data (one-word value) is
+ * stored in the data handle. But the data content is available only
+ * within the application. The pointer value is meaningless outside
+ * of the current application address space. Not really useful at all.
+ *
+ * So the trick here is to encode the property name with all the
+ * necessary information and to loop through all the properties
+ * of a window, looking for one that starts with our property name
+ * prefix. The downside is that the property name is limited to
+ * 255 bytes. But that should be enough. It's also slower since
+ * we examine each property until we find ours.
+ *
+ * We'll plug in the OLE stuff later.
+ *
+ * ------------------------------------------------------------------------
+ */
+
+static char *
+GetPropData(HWND hWnd, char *atom)
+{
+ PropertyInfo propInfo;
+ if (hWnd == NULL) {
+ return NULL;
+ }
+ propInfo.prefix = atom;
+ propInfo.prefixSize = strlen(atom);
+ propInfo.propReturn = NULL;
+ EnumPropsEx(hWnd, (PROPENUMPROCEX)GetEnumPropsExProc, (DWORD)&propInfo);
+ return propInfo.propReturn;
+}
+
+
+static char *
+GetProperty(Display *display, HWND hWnd)
+{
+ ATOM atom;
+
+ atom = (ATOM)GetProp(hWnd, propName);
+ if (atom != (ATOM)0) {
+ char buffer[MAX_PROP_SIZE + 1];
+ UINT nBytes;
+
+ nBytes = GlobalGetAtomName(atom, buffer, MAX_PROP_SIZE);
+ if (nBytes > 0) {
+ buffer[nBytes] = '\0';
+ return Blt_Strdup(buffer);
+ }
+ }
+ return NULL;
+}
+
+static void
+SetProperty(Tk_Window tkwin, char *data)
+{
+ HWND hWnd;
+ ATOM atom;
+
+ hWnd = Tk_GetHWND(Tk_WindowId(tkwin));
+ if (hWnd == NULL) {
+ return;
+ }
+ atom = (ATOM)GetProp(hWnd, propName);
+ if (atom != 0) {
+ GlobalDeleteAtom(atom);
+ }
+ atom = GlobalAddAtom(data);
+ if (atom != (ATOM)0) {
+ SetProp(hWnd, propName, (HANDLE)atom);
+ }
+}
+
+static void
+RemoveProperty(Tk_Window tkwin)
+{
+ HWND hWnd;
+ ATOM atom;
+
+ hWnd = Tk_GetHWND(Tk_WindowId(tkwin));
+ if (hWnd == NULL) {
+ return;
+ }
+ atom = (ATOM)GetProp(hWnd, propName);
+ if (atom != 0) {
+ GlobalDeleteAtom(atom);
+ }
+ RemoveProp(hWnd, propName);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetWindowRegion --
+ *
+ *----------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+GetWindowRegion(
+ Display *display, /* Not used. */
+ HWND hWnd,
+ int *x1Ptr,
+ int *y1Ptr,
+ int *x2Ptr,
+ int *y2Ptr)
+{
+ RECT rect;
+
+ if (GetWindowRect(hWnd, &rect)) {
+ *x1Ptr = rect.left;
+ *y1Ptr = rect.top;
+ *x2Ptr = rect.right;
+ *y2Ptr = rect.bottom;
+ return IsWindowVisible(hWnd);
+ }
+ return FALSE;
+}
+
+#else
+
+static WINDOW
+GetNativeWindow(tkwin)
+ Tk_Window tkwin;
+{
+ return Tk_WindowId(tkwin);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetWindowZOrder --
+ *
+ * Returns a chain of the child windows according to their stacking
+ * order. The window ids are ordered from top to bottom.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Blt_Chain *
+GetWindowZOrder(display, window)
+ Display *display;
+ Window window;
+{
+ Blt_Chain *chainPtr;
+ Window *childArr;
+ unsigned int nChildren;
+ Window dummy;
+
+ chainPtr = NULL;
+ if ((XQueryTree(display, window, &dummy, &dummy, &childArr, &nChildren)) &&
+ (nChildren > 0)) {
+ register int i;
+
+ chainPtr = Blt_ChainCreate();
+ for (i = 0; i < nChildren; i++) {
+ /*
+ * XQuery returns windows in bottom to top order.
+ * We only care about the top window.
+ */
+ Blt_ChainPrepend(chainPtr, (ClientData)childArr[i]);
+ }
+ if (childArr != NULL) {
+ XFree((char *)childArr); /* done with list of kids */
+ }
+ }
+ return chainPtr;
+}
+
+static char *
+GetProperty(display, window)
+ Display *display;
+ Window window;
+{
+ char *data;
+ int result, actualFormat;
+ Atom actualType;
+ unsigned long nItems, bytesAfter;
+
+ if (window == None) {
+ return NULL;
+ }
+ data = NULL;
+ result = XGetWindowProperty(display, window, dndAtom, 0, MAX_PROP_SIZE,
+ False, XA_STRING, &actualType, &actualFormat, &nItems, &bytesAfter,
+ (unsigned char **)&data);
+ if ((result != Success) || (actualFormat != 8) ||
+ (actualType != XA_STRING)) {
+ if (data != NULL) {
+ XFree((char *)data);
+ data = NULL;
+ }
+ }
+ return data;
+}
+
+static void
+SetProperty(tkwin, data)
+ Tk_Window tkwin;
+ char *data;
+{
+ XChangeProperty(Tk_Display(tkwin), Tk_WindowId(tkwin), dndAtom, XA_STRING,
+ 8, PropModeReplace, (unsigned char *)data, strlen(data) + 1);
+}
+
+static int
+GetWindowRegion(display, window, x1Ptr, y1Ptr, x2Ptr, y2Ptr)
+ Display *display;
+ Window window;
+ int *x1Ptr, *y1Ptr, *x2Ptr, *y2Ptr;
+{
+ XWindowAttributes winAttrs;
+
+ if (XGetWindowAttributes(display, window, &winAttrs)) {
+ *x1Ptr = winAttrs.x;
+ *y1Ptr = winAttrs.y;
+ *x2Ptr = winAttrs.x + winAttrs.width - 1;
+ *y2Ptr = winAttrs.y + winAttrs.height - 1;
+ }
+ return (winAttrs.map_state == IsViewable);
+}
+
+#endif /* WIN32 */
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * ChangeToken --
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+ChangeToken(tokenPtr, active)
+ Token *tokenPtr;
+ int active;
+{
+ int relief;
+ Tk_3DBorder border;
+ int borderWidth;
+
+ Tk_Fill3DRectangle(tokenPtr->tkwin, Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->outline, 0, 0, Tk_Width(tokenPtr->tkwin),
+ Tk_Height(tokenPtr->tkwin), 0, TK_RELIEF_FLAT);
+ if (active) {
+ relief = tokenPtr->activeRelief;
+ border = tokenPtr->activeBorder;
+ borderWidth = tokenPtr->activeBorderWidth;
+ } else {
+ relief = tokenPtr->relief;
+ border = tokenPtr->normalBorder;
+ borderWidth = tokenPtr->borderWidth;
+ }
+ Tk_Fill3DRectangle(tokenPtr->tkwin, Tk_WindowId(tokenPtr->tkwin), border,
+ 2, 2, Tk_Width(tokenPtr->tkwin) - 4, Tk_Height(tokenPtr->tkwin) - 4,
+ borderWidth, relief);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * TokenEventProc --
+ *
+ * Invoked by the Tk dispatcher to handle widget events.
+ * Manages redraws for the drag&drop token window.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+TokenEventProc(clientData, eventPtr)
+ ClientData clientData; /* data associated with widget */
+ XEvent *eventPtr; /* information about event */
+{
+ Token *tokenPtr = clientData;
+
+ if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) {
+ if (tokenPtr->tkwin != NULL) {
+ ChangeToken(tokenPtr, tokenPtr->active);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ tokenPtr->tkwin = NULL;
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * HideToken --
+ *
+ * Unmaps the drag&drop token. Invoked directly at the end of a
+ * successful communication, or after a delay if the communication
+ * fails (allowing the user to see a graphical picture of failure).
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+HideToken(tokenPtr)
+ Token *tokenPtr;
+{
+ if (tokenPtr->tkwin != NULL) {
+ Tk_UnmapWindow(tokenPtr->tkwin);
+ }
+ tokenPtr->timer = NULL;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * RaiseToken --
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+RaiseToken(tokenPtr)
+ Token *tokenPtr;
+{
+ Blt_MapTopLevelWindow(tokenPtr->tkwin);
+ Blt_RaiseTopLevelWindow(tokenPtr->tkwin);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * MoveToken --
+ *
+ * Invoked during "drag" operations to move a token window to its
+ * current "drag" coordinate.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+MoveToken(srcPtr, tokenPtr)
+ Source *srcPtr; /* drag&drop source window data */
+ Token *tokenPtr;
+{
+ int x, y;
+ int maxX, maxY;
+ int vx, vy, vw, vh;
+ Screen *screenPtr;
+
+ /* Adjust current location for virtual root windows. */
+ Tk_GetVRootGeometry(srcPtr->tkwin, &vx, &vy, &vw, &vh);
+ x = tokenPtr->lastX + vx - 3;
+ y = tokenPtr->lastY + vy - 3;
+
+ screenPtr = Tk_Screen(srcPtr->tkwin);
+ maxX = WidthOfScreen(screenPtr) - Tk_Width(tokenPtr->tkwin);
+ maxY = HeightOfScreen(screenPtr) - Tk_Height(tokenPtr->tkwin);
+ Blt_TranslateAnchor(x, y, Tk_Width(tokenPtr->tkwin),
+ Tk_Height(tokenPtr->tkwin), tokenPtr->anchor, &x, &y);
+ if (x > maxX) {
+ x = maxX;
+ } else if (x < 0) {
+ x = 0;
+ }
+ if (y > maxY) {
+ y = maxY;
+ } else if (y < 0) {
+ y = 0;
+ }
+ if ((x != Tk_X(tokenPtr->tkwin)) || (y != Tk_Y(tokenPtr->tkwin))) {
+ Tk_MoveToplevelWindow(tokenPtr->tkwin, x, y);
+ }
+ RaiseToken(tokenPtr);
+}
+
+static Tk_Cursor
+GetWidgetCursor(interp, tkwin)
+ Tk_Window tkwin;
+ Tcl_Interp *interp;
+{
+ char *cursorName;
+ Tk_Cursor cursor;
+
+ cursor = None;
+ if (Tcl_VarEval(interp, Tk_PathName(tkwin), " cget -cursor",
+ (char *)NULL) != TCL_OK) {
+ return None;
+ }
+ cursorName = Tcl_GetStringResult(interp);
+ if ((cursorName != NULL) && (cursorName[0] != '\0')) {
+ cursor = Tk_GetCursor(interp, tkwin, Tk_GetUid(cursorName));
+ }
+ Tcl_ResetResult(interp);
+ return cursor;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * UpdateToken --
+ *
+ * Invoked when the event loop is idle to determine whether or not
+ * the current drag&drop token position is over another drag&drop
+ * target.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+UpdateToken(clientData)
+ ClientData clientData; /* widget data */
+{
+ Source *srcPtr = clientData;
+ Token *tokenPtr = &(srcPtr->token);
+
+ ChangeToken(tokenPtr, tokenPtr->active);
+ /*
+ * If the source has a site command, then invoke it to
+ * modify the appearance of the token window. Pass any
+ * errors onto the drag&drop error handler.
+ */
+ if (srcPtr->siteCmd) {
+ char buffer[200];
+ Tcl_DString dString;
+ int result;
+ SubstDescriptors subs[2];
+
+ sprintf(buffer, "%d", tokenPtr->active);
+ subs[0].letter = 's';
+ subs[0].value = buffer;
+ subs[1].letter = 't';
+ subs[1].value = Tk_PathName(tokenPtr->tkwin);
+
+ Tcl_DStringInit(&dString);
+ result = Tcl_Eval(srcPtr->interp,
+ ExpandPercents(srcPtr->siteCmd, subs, 2, &dString));
+ Tcl_DStringFree(&dString);
+ if ((result != TCL_OK) && (errorCmd != NULL) && (*errorCmd)) {
+ (void)Tcl_VarEval(srcPtr->interp, errorCmd, " {",
+ Tcl_GetStringResult(srcPtr->interp), "}", (char *)NULL);
+ }
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * RejectToken --
+ *
+ * Draws a rejection mark on the current drag&drop token, and arranges
+ * for the token to be unmapped after a small delay.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+RejectToken(tokenPtr)
+ Token *tokenPtr;
+{
+ int divisor = 6; /* controls size of rejection symbol */
+ int w, h, lineWidth, x, y, margin;
+
+ margin = 2 * tokenPtr->borderWidth;
+ w = Tk_Width(tokenPtr->tkwin) - 2 * margin;
+ h = Tk_Height(tokenPtr->tkwin) - 2 * margin;
+ lineWidth = (w < h) ? w / divisor : h / divisor;
+ lineWidth = (lineWidth < 1) ? 1 : lineWidth;
+
+ w = h = lineWidth * (divisor - 1);
+ x = (Tk_Width(tokenPtr->tkwin) - w) / 2;
+ y = (Tk_Height(tokenPtr->tkwin) - h) / 2;
+
+ /*
+ * Draw the rejection symbol background (\) on the token window...
+ */
+ XSetLineAttributes(Tk_Display(tokenPtr->tkwin), tokenPtr->rejectBgGC,
+ lineWidth + 4, LineSolid, CapButt, JoinBevel);
+
+ XDrawArc(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->rejectBgGC, x, y, w, h, 0, 23040);
+
+ XDrawLine(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->rejectBgGC, x + lineWidth, y + lineWidth, x + w - lineWidth,
+ y + h - lineWidth);
+
+ /*
+ * Draw the rejection symbol foreground (\) on the token window...
+ */
+ XSetLineAttributes(Tk_Display(tokenPtr->tkwin), tokenPtr->rejectFgGC,
+ lineWidth, LineSolid, CapButt, JoinBevel);
+
+ XDrawArc(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->rejectFgGC, x, y, w, h, 0, 23040);
+
+ XDrawLine(Tk_Display(tokenPtr->tkwin), Tk_WindowId(tokenPtr->tkwin),
+ tokenPtr->rejectFgGC, x + lineWidth, y + lineWidth, x + w - lineWidth,
+ y + h - lineWidth);
+
+ /*
+ * Arrange for token window to disappear eventually.
+ */
+ tokenPtr->timer = Tcl_CreateTimerHandler(1000, (Tcl_TimerProc *) HideToken,
+ tokenPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * ConfigureToken --
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+ConfigureToken(interp, srcPtr, argc, argv)
+ Tcl_Interp *interp;
+ Source *srcPtr;
+ int argc;
+ char **argv;
+{
+ Token *tokenPtr;
+
+ tokenPtr = &(srcPtr->token);
+ if (Tk_ConfigureWidget(interp, srcPtr->tkwin, tokenConfigSpecs, argc, argv,
+ (char *)tokenPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ return ConfigureSource(interp, srcPtr, 0, (char **)NULL,
+ TK_CONFIG_ARGV_ONLY);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * CreateToken --
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+CreateToken(interp, srcPtr)
+ Tcl_Interp *interp;
+ Source *srcPtr;
+{
+ XSetWindowAttributes attrs;
+ Tk_Window tkwin;
+ char string[200];
+ static int nextTokenId = 0;
+ unsigned int mask;
+ Token *tokenPtr = &(srcPtr->token);
+
+ sprintf(string, "dd-token%d", ++nextTokenId);
+
+ /* Create toplevel on parent's screen. */
+ tkwin = Tk_CreateWindow(interp, srcPtr->tkwin, string, "");
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ Tk_SetClass(tkwin, className);
+ Tk_CreateEventHandler(tkwin, ExposureMask | StructureNotifyMask,
+ TokenEventProc, tokenPtr);
+
+ attrs.override_redirect = True;
+ attrs.backing_store = WhenMapped;
+ attrs.save_under = True;
+ mask = CWOverrideRedirect | CWSaveUnder | CWBackingStore;
+ Tk_ChangeWindowAttributes(tkwin, mask, &attrs);
+
+ Tk_SetInternalBorder(tkwin, tokenPtr->borderWidth + 2);
+ tokenPtr->tkwin = tkwin;
+#ifdef WIN32
+ {
+ Tk_FakeWin *winPtr = (Tk_FakeWin *) tkwin;
+ winPtr->dummy18 = tokenPtr;
+ }
+#endif /* WIN32 */
+ Tk_MakeWindowExist(tkwin);
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * CreateSource --
+ *
+ * Looks for a Source record in the hash table for drag&drop source
+ * widgets. Creates a new record if the widget name is not already
+ * registered. Returns a pointer to the desired record.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Source *
+CreateSource(interp, pathName, newPtr)
+ Tcl_Interp *interp;
+ char *pathName; /* widget pathname for desired record */
+ int *newPtr; /* returns non-zero => new record created */
+{
+ Blt_HashEntry *hPtr;
+ Tk_Window tkwin;
+ Source *srcPtr;
+
+ tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return NULL;
+ }
+ hPtr = Blt_CreateHashEntry(&sourceTable, (char *)tkwin, newPtr);
+ if (!(*newPtr)) {
+ return (Source *) Blt_GetHashValue(hPtr);
+ }
+ srcPtr = Blt_Calloc(1, sizeof(Source));
+ assert(srcPtr);
+ srcPtr->tkwin = tkwin;
+ srcPtr->display = Tk_Display(tkwin);
+ srcPtr->interp = interp;
+ srcPtr->token.anchor = TK_ANCHOR_SE;
+ srcPtr->token.relief = TK_RELIEF_RAISED;
+ srcPtr->token.activeRelief = TK_RELIEF_SUNKEN;
+ srcPtr->token.borderWidth = srcPtr->token.activeBorderWidth = 3;
+ srcPtr->hashPtr = hPtr;
+ Blt_InitHashTable(&(srcPtr->handlerTable), BLT_STRING_KEYS);
+ if (ConfigureSource(interp, srcPtr, 0, (char **)NULL, 0) != TCL_OK) {
+ DestroySource(srcPtr);
+ return NULL;
+ }
+ Blt_SetHashValue(hPtr, srcPtr);
+ /*
+ * Arrange for the window to unregister itself when it
+ * is destroyed.
+ */
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask, SourceEventProc, srcPtr);
+ return srcPtr;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DestroySource --
+ *
+ * Looks for a Source record in the hash table for drag&drop source
+ * widgets. Destroys the record if found.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+DestroySource(srcPtr)
+ Source *srcPtr;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *cmd;
+
+ Tcl_CancelIdleCall(UpdateToken, srcPtr);
+ if (srcPtr->token.timer) {
+ Tcl_DeleteTimerHandler(srcPtr->token.timer);
+ }
+ Tk_FreeOptions(configSpecs, (char *)srcPtr, srcPtr->display, 0);
+
+ if (srcPtr->token.rejectFgGC != NULL) {
+ Tk_FreeGC(srcPtr->display, srcPtr->token.rejectFgGC);
+ }
+ if (srcPtr->token.rejectBgGC != NULL) {
+ Tk_FreeGC(srcPtr->display, srcPtr->token.rejectBgGC);
+ }
+ if (srcPtr->pkgCmdResult) {
+ Blt_Free(srcPtr->pkgCmdResult);
+ }
+ if (srcPtr->rootPtr != NULL) {
+ RemoveWindow(srcPtr->rootPtr);
+ }
+ if (srcPtr->cursor != None) {
+ Tk_FreeCursor(srcPtr->display, srcPtr->cursor);
+ }
+ if (srcPtr->token.cursor != None) {
+ Tk_FreeCursor(srcPtr->display, srcPtr->token.cursor);
+ }
+ Blt_Free(srcPtr->sendTypes);
+
+ for (hPtr = Blt_FirstHashEntry(&(srcPtr->handlerTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ cmd = (char *)Blt_GetHashValue(hPtr);
+ if (cmd != NULL) {
+ Blt_Free(cmd);
+ }
+ }
+ Blt_DeleteHashTable(&(srcPtr->handlerTable));
+ if (srcPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&sourceTable, srcPtr->hashPtr);
+ }
+ Blt_Free(srcPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * GetSource --
+ *
+ * Looks for a Source record in the hash table for drag&drop source
+ * widgets. Returns a pointer to the desired record.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+GetSource(interp, pathName, srcPtrPtr)
+ Tcl_Interp *interp;
+ char *pathName; /* widget pathname for desired record */
+ Source **srcPtrPtr;
+{
+ Blt_HashEntry *hPtr;
+ Tk_Window tkwin;
+
+ tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(&sourceTable, (char *)tkwin);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "window \"", pathName,
+ "\" has not been initialized as a drag&drop source", (char *)NULL);
+ return TCL_ERROR;
+ }
+ *srcPtrPtr = (Source *)Blt_GetHashValue(hPtr);
+ return TCL_OK;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * ConfigureSource --
+ *
+ * Called to process an (argc,argv) list to configure (or
+ * reconfigure) a drag&drop source widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+ConfigureSource(interp, srcPtr, argc, argv, flags)
+ Tcl_Interp *interp; /* current interpreter */
+ register Source *srcPtr; /* drag&drop source widget record */
+ int argc; /* number of arguments */
+ char **argv; /* argument strings */
+ int flags; /* flags controlling interpretation */
+{
+ unsigned long gcMask;
+ XGCValues gcValues;
+ GC newGC;
+ Tcl_DString dString;
+ Tcl_CmdInfo cmdInfo;
+ int result;
+
+ /*
+ * Handle the bulk of the options...
+ */
+ if (Tk_ConfigureWidget(interp, srcPtr->tkwin, configSpecs, argc, argv,
+ (char *)srcPtr, flags) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ /*
+ * Check the button binding for valid range (0 or 1-5)
+ */
+ if ((srcPtr->button < 0) || (srcPtr->button > 5)) {
+ Tcl_AppendResult(interp,
+ "button number must be 1-5, or 0 for no bindings",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * Set up the rejection foreground GC for the token window...
+ */
+ gcValues.foreground = srcPtr->token.rejectFg->pixel;
+ gcValues.subwindow_mode = IncludeInferiors;
+ gcValues.graphics_exposures = False;
+ gcMask = GCForeground | GCSubwindowMode | GCGraphicsExposures;
+
+ if (srcPtr->token.rejectStipple != None) {
+ gcValues.stipple = srcPtr->token.rejectStipple;
+ gcValues.fill_style = FillStippled;
+ gcMask |= GCForeground | GCStipple | GCFillStyle;
+ }
+ newGC = Tk_GetGC(srcPtr->tkwin, gcMask, &gcValues);
+
+ if (srcPtr->token.rejectFgGC != NULL) {
+ Tk_FreeGC(srcPtr->display, srcPtr->token.rejectFgGC);
+ }
+ srcPtr->token.rejectFgGC = newGC;
+
+ /*
+ * Set up the rejection background GC for the token window...
+ */
+ gcValues.foreground = srcPtr->token.rejectBg->pixel;
+ gcValues.subwindow_mode = IncludeInferiors;
+ gcValues.graphics_exposures = False;
+ gcMask = GCForeground | GCSubwindowMode | GCGraphicsExposures;
+
+ newGC = Tk_GetGC(srcPtr->tkwin, gcMask, &gcValues);
+
+ if (srcPtr->token.rejectBgGC != NULL) {
+ Tk_FreeGC(srcPtr->display, srcPtr->token.rejectBgGC);
+ }
+ srcPtr->token.rejectBgGC = newGC;
+
+ /*
+ * Reset the border width in case it has changed...
+ */
+ if (srcPtr->token.tkwin) {
+ Tk_SetInternalBorder(srcPtr->token.tkwin,
+ srcPtr->token.borderWidth + 2);
+ }
+ if (!Tcl_GetCommandInfo(interp, "blt::Drag&DropInit", &cmdInfo)) {
+ static char cmd[] = "source [file join $blt_library dragdrop.tcl]";
+
+ if (Tcl_GlobalEval(interp, cmd) != TCL_OK) {
+ Tcl_AddErrorInfo(interp,
+ "\n (while loading bindings for blt::drag&drop)");
+ return TCL_ERROR;
+ }
+ }
+ Tcl_DStringInit(&dString);
+ Blt_DStringAppendElements(&dString, "blt::Drag&DropInit",
+ Tk_PathName(srcPtr->tkwin), Blt_Itoa(srcPtr->button), (char *)NULL);
+ result = Tcl_Eval(interp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ return result;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * SourceEventProc --
+ *
+ * Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
+ * on a registered drag&drop source widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+SourceEventProc(clientData, eventPtr)
+ ClientData clientData; /* drag&drop registration list */
+ XEvent *eventPtr; /* event description */
+{
+ Source *srcPtr = (Source *) clientData;
+
+ if (eventPtr->type == DestroyNotify) {
+ DestroySource(srcPtr);
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * FindTarget --
+ *
+ * Looks for a Target record in the hash table for drag&drop
+ * target widgets. Creates a new record if the widget name is
+ * not already registered. Returns a pointer to the desired
+ * record.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Target *
+FindTarget(tkwin)
+ Tk_Window tkwin; /* Widget pathname for desired record */
+{
+ Blt_HashEntry *hPtr;
+
+ hPtr = Blt_FindHashEntry(&targetTable, (char *)tkwin);
+ if (hPtr == NULL) {
+ return NULL;
+ }
+ return (Target *) Blt_GetHashValue(hPtr);
+}
+
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * CreateTarget --
+ *
+ * Looks for a Target record in the hash table for drag&drop
+ * target widgets. Creates a new record if the widget name is
+ * not already registered. Returns a pointer to the desired
+ * record.
+ *
+ * ------------------------------------------------------------------------
+ */
+static Target *
+CreateTarget(interp, tkwin)
+ Tcl_Interp *interp;
+ Tk_Window tkwin; /* Widget pathname for desired record */
+{
+ Target *targetPtr;
+ int isNew;
+
+ targetPtr = Blt_Calloc(1, sizeof(Target));
+ assert(targetPtr);
+ targetPtr->display = Tk_Display(tkwin);
+ targetPtr->tkwin = tkwin;
+ Blt_InitHashTable(&(targetPtr->handlerTable), BLT_STRING_KEYS);
+ targetPtr->hashPtr = Blt_CreateHashEntry(&targetTable, (char *)tkwin,
+ &isNew);
+ Blt_SetHashValue(targetPtr->hashPtr, targetPtr);
+
+ /*
+ * Arrange for the target to removed if the host window is destroyed.
+ */
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask, TargetEventProc,
+ targetPtr);
+ /*
+ * If this is a new target, attach a property to identify
+ * window as "drag&drop" target, and arrange for the window
+ * to un-register itself when it is destroyed.
+ */
+ Tk_MakeWindowExist(targetPtr->tkwin);
+ AddTargetProperty(interp, targetPtr);
+ return targetPtr;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DestroyTarget --
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+DestroyTarget(data)
+ DestroyData data;
+{
+ Target *targetPtr = (Target *)data;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ char *cmd;
+
+ for (hPtr = Blt_FirstHashEntry(&(targetPtr->handlerTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ cmd = (char *)Blt_GetHashValue(hPtr);
+ if (cmd != NULL) {
+ Blt_Free(cmd);
+ }
+ }
+ Blt_DeleteHashTable(&(targetPtr->handlerTable));
+ if (targetPtr->hashPtr != NULL) {
+ Blt_DeleteHashEntry(&targetTable, targetPtr->hashPtr);
+ }
+ Tk_DeleteEventHandler(targetPtr->tkwin, StructureNotifyMask,
+ TargetEventProc, targetPtr);
+ Blt_Free(targetPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * TargetEventProc --
+ *
+ * Invoked by Tk_HandleEvent whenever a DestroyNotify event is received
+ * on a registered drag&drop target widget.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+TargetEventProc(clientData, eventPtr)
+ ClientData clientData; /* drag&drop registration list */
+ XEvent *eventPtr; /* event description */
+{
+ Target *targetPtr = (Target *) clientData;
+
+ if (eventPtr->type == DestroyNotify) {
+#ifdef WIN32
+ /*
+ * Under Win32 the properties must be removed before the window
+ * can be destroyed.
+ */
+ RemoveProperty(targetPtr->tkwin);
+#endif
+ DestroyTarget((DestroyData)targetPtr);
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DndSend --
+ *
+ * Invoked after a drop operation to send data to the drop
+ * application.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+DndSend(srcPtr)
+ register Source *srcPtr; /* drag&drop source record */
+{
+ int status;
+ SubstDescriptors subs[3];
+ Tcl_DString dString;
+ Blt_HashEntry *hPtr;
+ char *dataType;
+ char **targetInfo;
+ char *cmd;
+
+ /* See if current position is over drop point. */
+ if (!OverTarget(srcPtr, srcPtr->token.lastX, srcPtr->token.lastY)) {
+ return;
+ }
+ targetInfo = srcPtr->windowPtr->targetInfo;
+ Tcl_DStringInit(&dString);
+ Blt_DStringAppendElements(&dString, "send", targetInfo[INTERP_NAME],
+ dragDropCmd, "location", (char *)NULL);
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(srcPtr->token.lastX));
+ Tcl_DStringAppendElement(&dString, Blt_Itoa(srcPtr->token.lastY));
+ status = Tcl_Eval(srcPtr->interp, Tcl_DStringValue(&dString));
+
+ Tcl_DStringFree(&dString);
+ if (status != TCL_OK) {
+ goto reject;
+ }
+ if (targetInfo[DATA_TYPE] == NULL) {
+ Blt_HashSearch cursor;
+
+ hPtr = Blt_FirstHashEntry(&(srcPtr->handlerTable), &cursor);
+ dataType = Blt_GetHashKey(&(srcPtr->handlerTable), hPtr);
+ } else {
+ hPtr = Blt_FindHashEntry(&(srcPtr->handlerTable),
+ targetInfo[DATA_TYPE]);
+ dataType = targetInfo[DATA_TYPE];
+ }
+ /* Start building the command line here, before we invoke any Tcl
+ * commands. The is because the Tcl command may let in another
+ * drag event and change the target property data. */
+ Tcl_DStringInit(&dString);
+ Blt_DStringAppendElements(&dString, "send", targetInfo[INTERP_NAME],
+ dragDropCmd, "target", targetInfo[TARGET_NAME], "handle",
+ dataType, (char *)NULL);
+ cmd = NULL;
+ if (hPtr != NULL) {
+ cmd = (char *)Blt_GetHashValue(hPtr);
+ }
+ if (cmd != NULL) {
+ Tcl_DString cmdString;
+
+ subs[0].letter = 'i';
+ subs[0].value = targetInfo[INTERP_NAME];
+ subs[1].letter = 'w';
+ subs[1].value = targetInfo[TARGET_NAME];
+ subs[2].letter = 'v';
+ subs[2].value = srcPtr->pkgCmdResult;
+
+ Tcl_DStringInit(&cmdString);
+ status = Tcl_Eval(srcPtr->interp,
+ ExpandPercents(cmd, subs, 3, &cmdString));
+ Tcl_DStringFree(&cmdString);
+
+ Tcl_DStringAppendElement(&dString, Tcl_GetStringResult(srcPtr->interp));
+ } else {
+ Tcl_DStringAppendElement(&dString, srcPtr->pkgCmdResult);
+ }
+
+ /*
+ * Part 2: Now tell target application to handle the data.
+ */
+ status = Tcl_Eval(srcPtr->interp, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+ if (status != TCL_OK) {
+ goto reject;
+ }
+ HideToken(&(srcPtr->token));
+ return;
+ reject:
+ /*
+ * Give failure information to user. If an error occurred and an
+ * error proc is defined, then use it to handle the error.
+ */
+ RejectToken(&(srcPtr->token));
+ if (errorCmd != NULL) {
+ Tcl_VarEval(srcPtr->interp, errorCmd, " {",
+ Tcl_GetStringResult(srcPtr->interp), "}", (char *)NULL);
+ }
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * InitRoot --
+ *
+ * Invoked at the start of a "drag" operation to capture the
+ * positions of all windows on the current root. Queries the
+ * entire window hierarchy and determines the placement of each
+ * window. Queries the "BltDrag&DropTarget" property info where
+ * appropriate. This information is used during the drag
+ * operation to determine when the drag&drop token is over a
+ * valid drag&drop target.
+ *
+ * Results:
+ * Returns the record for the root window, which contains records
+ * for all other windows as children.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+InitRoot(srcPtr)
+ Source *srcPtr;
+{
+ srcPtr->rootPtr = Blt_Calloc(1, sizeof(AnyWindow));
+ assert(srcPtr->rootPtr);
+#ifdef WIN32
+ srcPtr->rootPtr->nativeWindow = GetDesktopWindow();
+#else
+ srcPtr->rootPtr->nativeWindow = DefaultRootWindow(srcPtr->display);
+#endif
+ srcPtr->windowPtr = NULL;
+ QueryWindow(srcPtr->display, srcPtr->rootPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * FindTopWindow --
+ *
+ * Searches for the topmost window at a given pair of X-Y coordinates.
+ *
+ * Results:
+ * Returns a pointer to the node representing the window containing
+ * the point. If one can't be found, NULL is returned.
+ *
+ * ------------------------------------------------------------------------
+ */
+static AnyWindow *
+FindTopWindow(srcPtr, x, y)
+ Source *srcPtr;
+ int x, y;
+{
+ AnyWindow *rootPtr;
+ register Blt_ChainLink *linkPtr;
+ register AnyWindow *windowPtr;
+ WINDOW nativeTokenWindow;
+
+ rootPtr = srcPtr->rootPtr;
+ if (!rootPtr->initialized) {
+ QueryWindow(srcPtr->display, rootPtr);
+ }
+ if ((x < rootPtr->x1) || (x > rootPtr->x2) ||
+ (y < rootPtr->y1) || (y > rootPtr->y2)) {
+ return NULL; /* Point is not over window */
+ }
+ windowPtr = rootPtr;
+
+ nativeTokenWindow = (WINDOW)Blt_GetRealWindowId(srcPtr->token.tkwin);
+ /*
+ * The window list is ordered top to bottom, so stop when we find
+ * the first child that contains the X-Y coordinate. It will be
+ * the topmost window in that hierarchy. If none exists, then we
+ * already have the topmost window.
+ */
+ descend:
+ for (linkPtr = Blt_ChainFirstLink(rootPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ rootPtr = Blt_ChainGetValue(linkPtr);
+ if (!rootPtr->initialized) {
+ QueryWindow(srcPtr->display, rootPtr);
+ }
+ if (rootPtr->nativeWindow == nativeTokenWindow) {
+ continue; /* Don't examine the token window. */
+ }
+ if ((x >= rootPtr->x1) && (x <= rootPtr->x2) &&
+ (y >= rootPtr->y1) && (y <= rootPtr->y2)) {
+ /*
+ * Remember the last window containing the pointer and
+ * descend into its window hierarchy. We'll look for a
+ * child that also contains the pointer.
+ */
+ windowPtr = rootPtr;
+ goto descend;
+ }
+ }
+ return windowPtr;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * OverTarget --
+ *
+ * Checks to see if a compatible drag&drop target exists at the
+ * given position. A target is "compatible" if it is a drag&drop
+ * window, and if it has a handler that is compatible with the
+ * current source window.
+ *
+ * Results:
+ * Returns a pointer to a structure describing the target, or NULL
+ * if no compatible target is found.
+ *
+ * ------------------------------------------------------------------------
+ */
+static int
+OverTarget(srcPtr, x, y)
+ Source *srcPtr; /* drag&drop source window */
+ int x, y; /* current drag&drop location
+ * (in virtual coords) */
+{
+ int virtX, virtY;
+ int dummy;
+ AnyWindow *newPtr, *oldPtr;
+ char **elemArr;
+ int nElems;
+ char *data;
+ int result;
+
+ /*
+ * If no window info has been been gathered yet for this target,
+ * then abort this call. This probably means that the token is
+ * moved before it has been properly built.
+ */
+ if (srcPtr->rootPtr == NULL) {
+ return FALSE;
+ }
+ if (srcPtr->sendTypes == NULL) {
+ return FALSE; /* Send is turned off. */
+ }
+
+ /* Adjust current location for virtual root windows. */
+ Tk_GetVRootGeometry(srcPtr->tkwin, &virtX, &virtY, &dummy, &dummy);
+ x += virtX;
+ y += virtY;
+
+ oldPtr = srcPtr->windowPtr;
+ srcPtr->windowPtr = NULL;
+
+ newPtr = FindTopWindow(srcPtr, x, y);
+ if (newPtr == NULL) {
+ return FALSE; /* Not over a window. */
+ }
+ if ((!srcPtr->selfTarget) &&
+ (GetNativeWindow(srcPtr->tkwin) == newPtr->nativeWindow)) {
+ return FALSE; /* If the self-target flag isn't set,
+ * don't allow the source window to
+ * drop onto itself. */
+ }
+ if (newPtr == oldPtr) {
+ srcPtr->windowPtr = oldPtr;
+ /* No need to collect the target information if we're still
+ * over the same window. */
+ return (newPtr->targetInfo != NULL);
+ }
+
+ /* See if this window has a "BltDrag&DropTarget" property. */
+ data = GetProperty(srcPtr->display, newPtr->nativeWindow);
+ if (data == NULL) {
+ return FALSE; /* No such property on window. */
+ }
+ result = Tcl_SplitList(srcPtr->interp, data, &nElems, &elemArr);
+ XFree((char *)data);
+ if (result != TCL_OK) {
+ return FALSE; /* Malformed property list. */
+ }
+ srcPtr->windowPtr = newPtr;
+ /* Interpreter name, target name, type1, type2, ... */
+ if (nElems > 2) {
+ register char **s;
+ int count;
+ register int i;
+
+ /*
+ * The candidate target has a list of possible types.
+ * Compare this with what types the source is willing to
+ * transmit and compress the list down to just the matching
+ * types. It's up to the target to request the specific type
+ * it wants.
+ */
+ count = 2;
+ for (i = 2; i < nElems; i++) {
+ for (s = srcPtr->sendTypes; *s != NULL; s++) {
+ if (((**s == 'a') && (strcmp(*s, "all") == 0)) ||
+ ((**s == elemArr[i][0]) && (strcmp(*s, elemArr[i]) == 0))) {
+ elemArr[count++] = elemArr[i];
+ }
+ }
+ }
+ if (count == 2) {
+ Blt_Free(elemArr);
+ fprintf(stderr, "source/target mismatch: No matching types\n");
+ return FALSE; /* No matching data type. */
+ }
+ elemArr[count] = NULL;
+ }
+ newPtr->targetInfo = elemArr;
+ return TRUE;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * RemoveWindow --
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+RemoveWindow(windowPtr)
+ AnyWindow *windowPtr; /* window rep to be freed */
+{
+ AnyWindow *childPtr;
+ Blt_ChainLink *linkPtr;
+
+ /* Throw away leftover slots. */
+ for (linkPtr = Blt_ChainFirstLink(windowPtr->chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ childPtr = Blt_ChainGetValue(linkPtr);
+ RemoveWindow(childPtr);
+ }
+ Blt_ChainDestroy(windowPtr->chainPtr);
+ if (windowPtr->targetInfo != NULL) {
+ Blt_Free(windowPtr->targetInfo);
+ }
+ Blt_Free(windowPtr);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * QueryWindow --
+ *
+ * Invoked during "drag" operations. Digs into the root window
+ * hierarchy and caches the resulting information.
+ * If a point coordinate lies within an uninitialized AnyWindow,
+ * this routine is called to query window coordinates and
+ * drag&drop info. If this particular window has any children,
+ * more uninitialized AnyWindow structures are allocated.
+ * Further queries will cause these structures to be initialized
+ * in turn.
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+QueryWindow(display, windowPtr)
+ Display *display;
+ AnyWindow *windowPtr; /* window rep to be initialized */
+{
+ int visible;
+
+ if (windowPtr->initialized) {
+ return;
+ }
+ /*
+ * Query for the window coordinates.
+ */
+ visible = GetWindowRegion(display, windowPtr->nativeWindow,
+ &(windowPtr->x1), &(windowPtr->y1), &(windowPtr->x2), &(windowPtr->y2));
+ if (visible) {
+ Blt_ChainLink *linkPtr;
+ Blt_Chain *chainPtr;
+ AnyWindow *childPtr;
+
+#ifndef WIN32
+ /* Add offset from parent's origin to coordinates */
+ if (windowPtr->parentPtr != NULL) {
+ windowPtr->x1 += windowPtr->parentPtr->x1;
+ windowPtr->y1 += windowPtr->parentPtr->y1;
+ windowPtr->x2 += windowPtr->parentPtr->x1;
+ windowPtr->y2 += windowPtr->parentPtr->y1;
+ }
+#endif
+ /*
+ * Collect a list of child windows, sorted in z-order. The
+ * topmost window will be first in the list.
+ */
+ chainPtr = GetWindowZOrder(display, windowPtr->nativeWindow);
+
+ /* Add and initialize extra slots if needed. */
+ for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL;
+ linkPtr = Blt_ChainNextLink(linkPtr)) {
+ childPtr = Blt_Calloc(1, sizeof(AnyWindow));
+ assert(childPtr);
+ childPtr->initialized = FALSE;
+ childPtr->nativeWindow = (WINDOW)Blt_ChainGetValue(linkPtr);
+ childPtr->parentPtr = windowPtr;
+ Blt_ChainSetValue(linkPtr, childPtr);
+ }
+ windowPtr->chainPtr = chainPtr;
+ } else {
+ /* If it's not viewable don't bother doing anything else. */
+ windowPtr->x1 = windowPtr->y1 = windowPtr->x2 = windowPtr->y2 = -1;
+ windowPtr->chainPtr = NULL;
+ }
+ windowPtr->initialized = TRUE;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * AddTargetProperty --
+ *
+ * Attaches a drag&drop property to the given target window.
+ * This property allows us to recognize the window later as a
+ * valid target. It also stores important information including
+ * the interpreter managing the target and the pathname of the
+ * target window. Usually this routine is called when the target
+ * is first registered or first exposed (so that the X-window
+ * really exists).
+ *
+ * ------------------------------------------------------------------------
+ */
+static void
+AddTargetProperty(interp, targetPtr)
+ Tcl_Interp *interp;
+ Target *targetPtr; /* drag&drop target window data */
+{
+ Tcl_DString dString;
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+
+ if (targetPtr->tkwin == NULL) {
+ return;
+ }
+ Tcl_DStringInit(&dString);
+ /*
+ * Each target window's dnd property contains
+ *
+ * 1. name of the application (ie. the interpreter's name).
+ * 2. Tk path name of the target window.
+ * 3. List of all the data types that can be handled. If none
+ * are listed, then all can be handled.
+ */
+ Tcl_DStringAppendElement(&dString, Tk_Name(Tk_MainWindow(interp)));
+ Tcl_DStringAppendElement(&dString, Tk_PathName(targetPtr->tkwin));
+ for (hPtr = Blt_FirstHashEntry(&(targetPtr->handlerTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Tcl_DStringAppendElement(&dString,
+ Blt_GetHashKey(&(targetPtr->handlerTable), hPtr));
+ }
+ SetProperty(targetPtr->tkwin, Tcl_DStringValue(&dString));
+ Tcl_DStringFree(&dString);
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * ExpandPercents --
+ *
+ * Expands all percent substitutions found in the input "str"
+ * that match specifications in the "subs" list. Any percent
+ * field that is not found in the "subs" list is left alone.
+ * Returns a string that remains valid until the next call to
+ * this routine.
+ *
+ * ------------------------------------------------------------------------
+ */
+static char *
+ExpandPercents(string, subsArr, nSubs, resultPtr)
+ char *string; /* Incoming command string */
+ SubstDescriptors *subsArr; /* Array of known substitutions */
+ int nSubs; /* Number of elements in subs array */
+ Tcl_DString *resultPtr;
+{
+ register char *chunk, *p;
+ char letter;
+ char percentSign;
+ int i;
+
+ /*
+ * Scan through the copy of the input string, look for
+ * the next '%' character, and try to make the substitution.
+ * Continue doing this to the end of the string.
+ */
+ chunk = p = string;
+ while ((p = strchr(p, '%')) != NULL) {
+
+ /* Copy up to the percent sign. Repair the string afterwards */
+ percentSign = *p;
+ *p = '\0';
+ Tcl_DStringAppend(resultPtr, chunk, -1);
+ *p = percentSign;
+
+ /* Search for a matching substition rule */
+ letter = *(p + 1);
+ for (i = 0; i < nSubs; i++) {
+ if (subsArr[i].letter == letter) {
+ break;
+ }
+ }
+ if (i < nSubs) {
+ /* Make the substitution */
+ Tcl_DStringAppend(resultPtr, subsArr[i].value, -1);
+ } else {
+ /* Copy in the %letter verbatim */
+ char verbatim[3];
+
+ verbatim[0] = '%';
+ verbatim[1] = letter;
+ verbatim[2] = '\0';
+ Tcl_DStringAppend(resultPtr, verbatim, -1);
+ }
+ p += 2; /* Skip % + letter */
+ if (letter == '\0') {
+ p += 1; /* Premature % substitution (end of string) */
+ }
+ chunk = p;
+ }
+ /* Pick up last chunk if a substition wasn't the last thing in the string */
+ if (*chunk != '\0') {
+ Tcl_DStringAppend(resultPtr, chunk, -1);
+ }
+ return Tcl_DStringValue(resultPtr);
+}
+
+
+static int
+DragOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ int x, y;
+ Token *tokenPtr;
+ int status;
+ Source *srcPtr;
+ SubstDescriptors subst[2];
+ int active;
+ char *result;
+
+ /*
+ * HANDLE: drag&drop drag <path> <x> <y>
+ */
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " drag pathname x y\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((GetSource(interp, argv[2], &srcPtr) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ tokenPtr = &(srcPtr->token);
+
+ tokenPtr->lastX = locX = x; /* Save drag&drop location */
+ tokenPtr->lastY = locY = y;
+
+ /* If HideToken() is pending, then do it now! */
+ if (tokenPtr->timer != 0) {
+ Tcl_DeleteTimerHandler(tokenPtr->timer);
+ HideToken(tokenPtr);
+ }
+
+ /*
+ * If pkgCmd is in progress, then ignore subsequent calls
+ * until it completes. Only perform drag if pkgCmd
+ * completed successfully and token window is mapped.
+ */
+ if ((!Tk_IsMapped(tokenPtr->tkwin)) && (!srcPtr->pkgCmdInProgress)) {
+ Tcl_DString dString;
+
+ /*
+ * No list of send handlers? Then source is disabled.
+ * Abort drag quietly.
+ */
+ if (srcPtr->sendTypes == NULL) {
+ return TCL_OK;
+ }
+ /*
+ * No token command? Then cannot build token.
+ * Signal error.
+ */
+ if (srcPtr->pkgCmd == NULL) {
+ Tcl_AppendResult(interp, "missing -packagecmd: ", argv[2],
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * Execute token command to initialize token window.
+ */
+ srcPtr->pkgCmdInProgress = TRUE;
+ subst[0].letter = 'W';
+ subst[0].value = Tk_PathName(srcPtr->tkwin);
+ subst[1].letter = 't';
+ subst[1].value = Tk_PathName(tokenPtr->tkwin);
+
+ Tcl_DStringInit(&dString);
+ status = Tcl_Eval(srcPtr->interp,
+ ExpandPercents(srcPtr->pkgCmd, subst, 2, &dString));
+ Tcl_DStringFree(&dString);
+
+ srcPtr->pkgCmdInProgress = FALSE;
+
+ result = Tcl_GetStringResult(interp);
+ /*
+ * Null string from the package command?
+ * Then quietly abort the drag&drop operation.
+ */
+ if (result[0] == '\0') {
+ return TCL_OK;
+ }
+
+ /* Save result of token command for send command. */
+ if (srcPtr->pkgCmdResult != NULL) {
+ Blt_Free(srcPtr->pkgCmdResult);
+ }
+ srcPtr->pkgCmdResult = Blt_Strdup(result);
+ if (status != TCL_OK) {
+ /*
+ * Token building failed. If an error handler is defined,
+ * then signal the error. Otherwise, abort quietly.
+ */
+ if ((errorCmd != NULL) && (errorCmd[0] != '\0')) {
+ return Tcl_VarEval(interp, errorCmd, " {", result, "}",
+ (char *)NULL);
+ }
+ return TCL_OK;
+ }
+ /* Install token cursor. */
+ if (tokenPtr->cursor != None) {
+ Tk_Cursor cursor;
+
+ /* Save the old cursor */
+ cursor = GetWidgetCursor(srcPtr->interp, srcPtr->tkwin);
+ if (srcPtr->cursor != None) {
+ Tk_FreeCursor(srcPtr->display, srcPtr->cursor);
+ }
+ srcPtr->cursor = cursor;
+ /* Temporarily install the token cursor */
+ Tk_DefineCursor(srcPtr->tkwin, tokenPtr->cursor);
+ }
+ /*
+ * Get ready to drag token window...
+ * 1) Cache info for all windows on root
+ * 2) Map token window to begin drag operation
+ */
+ if (srcPtr->rootPtr != NULL) {
+ RemoveWindow(srcPtr->rootPtr);
+ }
+ InitRoot(srcPtr);
+
+ nActive++; /* One more drag&drop window active */
+
+ if (Tk_WindowId(tokenPtr->tkwin) == None) {
+ Tk_MakeWindowExist(tokenPtr->tkwin);
+ }
+ if (!Tk_IsMapped(tokenPtr->tkwin)) {
+ Tk_MapWindow(tokenPtr->tkwin);
+ }
+ RaiseToken(tokenPtr);
+ }
+
+ /* Arrange to update status of token window. */
+ Tcl_CancelIdleCall(UpdateToken, srcPtr);
+
+ active = OverTarget(srcPtr, x, y);
+ if (tokenPtr->active != active) {
+ tokenPtr->active = active;
+ Tcl_DoWhenIdle(UpdateToken, srcPtr);
+ }
+ MoveToken(srcPtr, tokenPtr); /* Move token window to current drag point. */
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop drop <path> <x> <y>
+ */
+static int
+DropOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Source *srcPtr;
+ Token *tokenPtr;
+ int x, y;
+
+ if (argc < 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " drop pathname x y\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if ((GetSource(interp, argv[2], &srcPtr) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[3], &x) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ tokenPtr = &(srcPtr->token);
+ tokenPtr->lastX = locX = x; /* Save drag&drop location */
+ tokenPtr->lastY = locY = y;
+
+ /* Put the cursor back to its usual state. */
+ if (srcPtr->cursor == None) {
+ Tk_UndefineCursor(srcPtr->tkwin);
+ } else {
+ Tk_DefineCursor(srcPtr->tkwin, srcPtr->cursor);
+ }
+ Tcl_CancelIdleCall(UpdateToken, srcPtr);
+
+ /*
+ * Make sure that token window was not dropped before it
+ * was either mapped or packed with info.
+ */
+ if ((Tk_IsMapped(tokenPtr->tkwin)) && (!srcPtr->pkgCmdInProgress)) {
+ int active;
+
+ active = OverTarget(srcPtr, tokenPtr->lastX, tokenPtr->lastY);
+ if (tokenPtr->active != active) {
+ tokenPtr->active = active;
+ UpdateToken(srcPtr);
+ }
+ if (srcPtr->sendTypes != NULL) {
+ if (tokenPtr->active) {
+ DndSend(srcPtr);
+ } else {
+ HideToken(tokenPtr);
+ }
+ }
+ nActive--; /* One less active token window. */
+ }
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop errors ?<proc>?
+ */
+static int
+ErrorsOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc == 3) {
+ if (errorCmd) {
+ Blt_Free(errorCmd);
+ }
+ errorCmd = Blt_Strdup(argv[2]);
+ } else if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " errors ?proc?\"",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, errorCmd, TCL_VOLATILE);
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop active
+ */
+static int
+ActiveOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if (argc != 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " active\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ Blt_SetBooleanResult(interp, (nActive > 0));
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop location ?<x> <y>?
+ */
+static int
+LocationOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ if ((argc != 2) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " location ?x y?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 4) {
+ int x, y;
+
+ if ((Tcl_GetInt(interp, argv[2], &x) != TCL_OK) ||
+ (Tcl_GetInt(interp, argv[3], &y) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ locX = x;
+ locY = y;
+ }
+ Tcl_AppendElement(interp, Blt_Itoa(locX));
+ Tcl_AppendElement(interp, Blt_Itoa(locY));
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop token <pathName>
+ */
+static int
+TokenOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Source *srcPtr;
+
+ if (GetSource(interp, argv[2], &srcPtr) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if ((argc > 3) &&
+ (ConfigureToken(interp, srcPtr, argc - 3, argv + 3) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ Tcl_SetResult(interp, Tk_PathName(srcPtr->token.tkwin), TCL_VOLATILE);
+ return TCL_OK;
+}
+
+static int
+HandlerOpOp(srcPtr, interp, argc, argv)
+ Source *srcPtr;
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Blt_HashEntry *hPtr;
+ Blt_HashSearch cursor;
+ int isNew;
+ char *cmd;
+
+ /*
+ * HANDLE: drag&drop source <pathName> handler \
+ * ?<data>? ?<scmd>...?
+ */
+ if (argc == 4) {
+ /* Show source handler data types */
+ for (hPtr = Blt_FirstHashEntry(&(srcPtr->handlerTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Tcl_AppendElement(interp,
+ Blt_GetHashKey(&(srcPtr->handlerTable), hPtr));
+ }
+ return TCL_OK;
+ }
+ hPtr = Blt_CreateHashEntry(&(srcPtr->handlerTable), argv[4], &isNew);
+
+ /*
+ * HANDLE: drag&drop source <pathName> handler <data>
+ *
+ * Create the new <data> type if it doesn't already
+ * exist, and return the code associated with it.
+ */
+ if (argc == 5) {
+ cmd = (char *)Blt_GetHashValue(hPtr);
+ if (cmd == NULL) {
+ cmd = "";
+ }
+ Tcl_SetResult(interp, cmd, TCL_VOLATILE);
+ return TCL_OK;
+ }
+ /*
+ * HANDLE: drag&drop source <pathName> handler \
+ * <data> <cmd> ?<arg>...?
+ *
+ * Create the new <data> type and set its command
+ */
+ cmd = Tcl_Concat(argc - 5, argv + 5);
+ Blt_SetHashValue(hPtr, cmd);
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop source
+ * drag&drop source <pathName> ?options...?
+ * drag&drop source <pathName> handler ?<data>? ?<scmd> <arg>...?
+ */
+static int
+SourceOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ Source *srcPtr;
+ int isNew;
+ Token *tokenPtr;
+
+ if (argc == 2) {
+ Blt_HashSearch cursor;
+ Blt_HashEntry *hPtr;
+ Tk_Window tkwin;
+
+ for (hPtr = Blt_FirstHashEntry(&sourceTable, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tkwin = (Tk_Window)Blt_GetHashKey(&sourceTable, hPtr);
+ Tcl_AppendElement(interp, Tk_PathName(tkwin));
+ }
+ return TCL_OK;
+ }
+ /*
+ * Find or create source info...
+ */
+ srcPtr = CreateSource(interp, argv[2], &isNew);
+ if (srcPtr == NULL) {
+ return TCL_ERROR;
+ }
+ tokenPtr = &(srcPtr->token);
+ if (argc > 3) {
+ char c;
+ int length;
+ int status;
+
+ /*
+ * HANDLE: drag&drop source <pathName> ?options...?
+ */
+ c = argv[3][0];
+ length = strlen(argv[3]);
+
+ if (c == '-') {
+ if (argc == 3) {
+ status = Tk_ConfigureInfo(interp, tokenPtr->tkwin, configSpecs,
+ (char *)srcPtr, (char *)NULL, 0);
+ } else if (argc == 4) {
+ status = Tk_ConfigureInfo(interp, tokenPtr->tkwin, configSpecs,
+ (char *)srcPtr, argv[3], 0);
+ } else {
+ status = ConfigureSource(interp, srcPtr, argc - 3, argv + 3,
+ TK_CONFIG_ARGV_ONLY);
+ }
+ if (status != TCL_OK) {
+ return TCL_ERROR;
+ }
+ } else if ((c == 'h') && strncmp(argv[3], "handler", length) == 0) {
+ return HandlerOpOp(srcPtr, interp, argc, argv);
+ } else {
+ Tcl_AppendResult(interp, "bad operation \"", argv[3],
+ "\": must be \"handler\" or a configuration option",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ }
+ if (isNew) {
+ /*
+ * Create the window for the drag&drop token...
+ */
+ if (CreateToken(interp, srcPtr) != TCL_OK) {
+ DestroySource(srcPtr);
+ return TCL_ERROR;
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ * HANDLE: drag&drop target ?<pathName>? ?handling info...?
+ */
+static int
+TargetOp(interp, argc, argv)
+ Tcl_Interp *interp;
+ int argc;
+ char **argv;
+{
+ SubstDescriptors subst[2];
+ Tk_Window tkwin;
+ Blt_HashEntry *hPtr;
+ Target *targetPtr;
+ int isNew;
+
+ if (argc == 2) {
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&targetTable, &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ tkwin = (Tk_Window)Blt_GetHashKey(&targetTable, hPtr);
+ Tcl_AppendElement(interp, Tk_PathName(tkwin));
+ }
+ return TCL_OK;
+ }
+ tkwin = Tk_NameToWindow(interp, argv[2], Tk_MainWindow(interp));
+ if (tkwin == NULL) {
+ return TCL_ERROR;
+ }
+ targetPtr = FindTarget(tkwin);
+ if (targetPtr == NULL) {
+ targetPtr = CreateTarget(interp, tkwin);
+ }
+ if (targetPtr == NULL) {
+ return TCL_ERROR;
+ }
+
+ if ((argc >= 4) && (strcmp(argv[3], "handler") == 0)) {
+ /*
+ * HANDLE: drag&drop target <pathName> handler
+ * drag&drop target <pathName> handler ?<data> <cmd> <arg>...?
+ */
+ if (argc == 4) {
+ Blt_HashSearch cursor;
+
+ for (hPtr = Blt_FirstHashEntry(&(targetPtr->handlerTable), &cursor);
+ hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) {
+ Tcl_AppendElement(interp,
+ Blt_GetHashKey(&(targetPtr->handlerTable), hPtr));
+ }
+ return TCL_OK;
+ } else if (argc >= 6) {
+ char *cmd;
+
+ /*
+ * Process handler definition
+ */
+ hPtr = Blt_CreateHashEntry(&(targetPtr->handlerTable), argv[4],
+ &isNew);
+ cmd = Tcl_Concat(argc - 5, argv + 5);
+ if (hPtr != NULL) {
+ char *oldCmd;
+
+ oldCmd = (char *)Blt_GetHashValue(hPtr);
+ if (oldCmd != NULL) {
+ Blt_Free(oldCmd);
+ }
+ }
+ Blt_SetHashValue(hPtr, cmd);
+ /*
+ * Update the target property on the window.
+ */
+ AddTargetProperty(interp, targetPtr);
+ return TCL_OK;
+ }
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ", argv[1], " ", argv[2], " ", argv[3],
+ " data command ?arg arg...?", (char *)NULL);
+ return TCL_ERROR;
+ } else if ((argc >= 4) && (strcmp(argv[3], "handle") == 0)) {
+ /*
+ * HANDLE: drag&drop target <pathName> handle <data> ?<value>?
+ */
+ Tcl_DString dString;
+ int result;
+ char *cmd;
+
+ if (argc < 5 || argc > 6) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " ", argv[1], " ", argv[2], " handle data ?value?",
+ (char *)NULL);
+ return TCL_ERROR;
+ }
+ hPtr = Blt_FindHashEntry(&(targetPtr->handlerTable), argv[4]);
+ if (hPtr == NULL) {
+ Tcl_AppendResult(interp, "target can't handle datatype: ",
+ argv[4], (char *)NULL);
+ return TCL_ERROR; /* no handler found */
+ }
+ cmd = (char *)Blt_GetHashValue(hPtr);
+ if (cmd != NULL) {
+ subst[0].letter = 'W';
+ subst[0].value = Tk_PathName(targetPtr->tkwin);
+ subst[1].letter = 'v';
+ if (argc > 5) {
+ subst[1].value = argv[5];
+ } else {
+ subst[1].value = "";
+ }
+ Tcl_DStringInit(&dString);
+ result = Tcl_Eval(interp, ExpandPercents(cmd, subst, 2, &dString));
+ Tcl_DStringFree(&dString);
+ return result;
+ }
+ return TCL_OK;
+ }
+ Tcl_AppendResult(interp, "usage: ", argv[0], " target ", argv[2],
+ " handler ?data command arg arg...?\n or: ",
+ argv[0], " target ", argv[2], " handle <data>",
+ (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * DragDropCmd --
+ *
+ * Invoked by TCL whenever the user issues a drag&drop command.
+ * Handles the following syntax:
+ *
+ * drag&drop source
+ * drag&drop source <pathName> ?options...?
+ * drag&drop source <pathName> handler ?<dataType>? ?<cmd> <arg>...?
+ *
+ * drag&drop target
+ * drag&drop target <pathName> handler ?<dataType> <cmd> <arg>...?
+ * drag&drop target <pathName> handle <dataType> ?<value>?
+ *
+ * drag&drop token <pathName>
+ * drag&drop drag <pathName> <x> <y>
+ * drag&drop drop <pathName> <x> <y>
+ *
+ * drag&drop errors ?<proc>?
+ * drag&drop active
+ * drag&drop location ?<x> <y>?
+ *
+ * ------------------------------------------------------------------------
+ */
+/*ARGSUSED*/
+static int
+DragDropCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter */
+ int argc; /* # of arguments */
+ char **argv; /* Argument strings */
+{
+ int length;
+ char c;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " oper ?args?\"", (char *)NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+
+ if ((c == 's') && strncmp(argv[1], "source", length) == 0) {
+ return SourceOp(interp, argc, argv);
+ } else if ((c == 't') && (length >= 2) &&
+ (strncmp(argv[1], "target", length) == 0)) {
+ return TargetOp(interp, argc, argv);
+ } else if ((c == 't') && (length >= 2) &&
+ (strncmp(argv[1], "token", length) == 0)) {
+ return TokenOp(interp, argc, argv);
+ } else if ((c == 'd') && strncmp(argv[1], "drag", length) == 0) {
+ return DragOp(interp, argc, argv);
+ } else if ((c == 'd') && strncmp(argv[1], "drop", length) == 0) {
+ return DropOp(interp, argc, argv);
+ } else if ((c == 'e') && strncmp(argv[1], "errors", length) == 0) {
+ return ErrorsOp(interp, argc, argv);
+ } else if ((c == 'a') && strncmp(argv[1], "active", length) == 0) {
+ return ActiveOp(interp, argc, argv);
+ } else if ((c == 'l') && strncmp(argv[1], "location", length) == 0) {
+ return LocationOp(interp, argc, argv);
+ }
+ /*
+ * Report improper command arguments
+ */
+ Tcl_AppendResult(interp, "bad operation \"", argv[1],
+ "\": must be active, drag, drop, errors, location, ",
+ "source, target or token",
+ (char *)NULL);
+ return TCL_ERROR;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * Blt_DragDropInit --
+ *
+ * Adds the drag&drop command to the given interpreter. Should
+ * be invoked to properly install the command whenever a new
+ * interpreter is created.
+ *
+ * ------------------------------------------------------------------------
+ */
+int
+Blt_DragDropInit(interp)
+ Tcl_Interp *interp; /* interpreter to be updated */
+{
+ static Blt_CmdSpec cmdSpec =
+ {
+ "drag&drop", DragDropCmd,
+ };
+
+ if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) {
+ return TCL_ERROR;
+ }
+ if (!initialized) {
+ Blt_InitHashTable(&sourceTable, BLT_ONE_WORD_KEYS);
+ Blt_InitHashTable(&targetTable, BLT_ONE_WORD_KEYS);
+ errorCmd = Blt_Strdup(DEF_ERROR_PROC);
+ nActive = 0;
+ locX = locY = 0;
+ initialized = TRUE;
+#ifndef WIN32
+ dndAtom = XInternAtom(Tk_Display(Tk_MainWindow(interp)), propName,
+ False);
+#endif
+ }
+ return TCL_OK;
+}
+#endif /* NO_DRAGDROP */