Drag and Drop

Drag and drop is a complex subject. Here are a collection of information, both general and Tk-specific, about Drag and Drop, as well as useful links on the matter.

AMG: I like to call it DragonDrop. :^)


See Also

Droplets
A windows trick the takes advantage of what happens when a file is dragged onto a batch file

Drag and Drop in Tk

TkDND
[L1 ] First of all, there is a Tk extension (for Tk > 8.3.3) that supports OLE dnd (that is native dnd) under windows, XDND under unix (that is KDE 2.x and partially GNOME) and limited support for Motif drops (for the various protocols see the links below). TkDND can be found at SourceForge [L2 ] (please note that the binaries there are quite obsolete) and the latest CVS sources can be found as a tarball at [L3 ]. NEW! There are binary RPM packages for Linux with a few minor fixes available [L4 ].

There's an german explanation of the XDND protocol available at [L5 ] with a sample script for TkDND. The site contains also a complete german translation of the XDND protocol at [L6 ]. Links broken as of 2016-01-06.

BLT - drag&drop
[L7 ] The most important existing Tk DnD protocol.
BWidget toolkit
[L8 ] Yet another DnD implementation which is fairly widespread supportable due to the popularity of the rest of the toolkit.
Donal Fellows's Drag and Drop
[L9 ] Various versions there (and much else on that page besides; use the index to skip straight to the interesting bit) explore different ways of implementing a scheme. Link broken as of 2010-04-30.
Drag-N-Drop Example
Some stuff is available within the Wiki too.
SimpleDND
Basic package for intra-application drag and drop. Provides a way to register drag sources/drop targets, show drag visually, and execute callbacks.

Other Drag and Drop Resources

OSF Motif Drag And Drop Protocol
[L10 ] You really need a lot of extra Motif documentation to understand it all. Lesstif [L11 ] is not all that well documented either.
Xdnd Drag and Drop Protocol
[L12 ] (dead link 15jun05). This includes a critique of other protocols, and as well as describing exactly what you do, says why. This is one of the better sites on the topic. Note that this is the protocol that is used (or is going to be used) in both the GNOME and KDE desktop environments for Unix systems. The URL to Caltech above seems no longer functional. [L13 ] might be a replacement.
OffiX Drop Protocol
[L14 ] Easy to implement (no drag protocol there!) but hardly worth it.
Java's java.awt.dnd package
[L15 ] While interoperability here might not be easy/possible, it is certainly a resource that ought to be studied.
The Macintosh drag-and-drop specification is available too.
[L16 ] [92 pages of PDF] - Many thanks for this reference go to Mark Roseman! NEM This link is dead. Try: [L17 ]
Windows OLE
You should start looking somewhere near [L18 ] but I have yet to find a proper summary document. I really don't feel like wading through several tonnes of documentation that distributes the meaning of everything across tens of pages... :^(
More on Windows - Olednd
[L19 ] This offers most of what you might want from a script-level interface, with the exception that only one type of data can be transferred (definitely not sufficient when working with the Mac, and well short of the capabilities provided on the other major platforms too.) Caution, though - Office 2000 specifies Unicode'd RichText as a D'n'D extension [L20 ], [L21 ], and [L22 ]

More details are available in the news:comp.lang.tcl threads [L23 ] [L24 ] where this was raised.

DKF


Specifying a Tcl-level interface to Drag and Drop functionality

Now we will start looking at the business of actually specifying a Tcl-level interface to drag-and-drop. Why do I start with talking about a Tcl interface instead of wading in and defining a whole bunch of C functions? Well, even a cursory read of the above references indicates that there is a lot of difference between them, so much so that writing a common C interface at this stage is very difficult. And in any case, it is the Tcl interface that is interesting to Tcl script authors. Requiring a common C interface to be defined at this stage is probably very precipitous (and would likely need to be changed a lot during its development to cope with the brain-damage of each of these mechanisms!)

It seems that defining the drag source side is much tougher (the details of the protocols that ought to be exposed to the script author are more complex there.) So we will start by looking at the target side.

The target (recipient) side:

The single most important "event" (this is not a standard Tk event for reasons that I'll go into later) that needs to be handled by the target side is Drop. Obviously! The following information will be available to the drop handler:

  • The source window (either as a window name or an id, depending on whether the source end is this application or not. I suppose it would be nice where the remote end is a different Tk app to be able to say what the interp name is so that [send] can be used to perform more complex communications. In that case, the remote window name makes a lot of sense. This all gets immeasurably more complex when using apps that are working on multiple displays...)
  • The location of the attempted drop (x,y)
  • The kind of actions (move, copy, link) in preferred order

It must decide whether to proceed with the drop and how it is going to perform which action (and in that case transfer the dragged data) or whether to reject the drop. It is because of the fact that we need to indicate whether a drop failed or succeeded that we cannot use a normal Tk event. Instead, it looks like something involving callbacks with passed arguments (rather like [trace variable] callbacks?)

Also need DragEnter, DragMotion and DragLeave. And we need a mechanism for providing some commonality for datatype names, since although a piece of text is a piece of text pretty much everywhere, the name for it is not always the same. My hunch is that using MIME types is a good idea; everyone knows what text/plain is. We can then also provide a reasonable algorithm for deciding when to do line-terminator conversions (do it for all text/* and nothing else.)

There probably ought to be a mechanism to ask for no further Drag* notifications when within a particular area, as this would allow some targets to significantly reduce the amount of work they do.

Issues to think about on the source (initiator) side:

  • When is a mouse action a drag? There is no standard for this, even on a single platform...
  • Hysteresis - dragging within a "window" is quite a different business to dragging to another "window" (note that the idea of what is a window is not necessarily the same as the idea of what is a Tk widget.) Want to be able to support auto-scrolling, and might want different actions to be default if going to a local window too...
  • What do to about move actions
  • Some protocols support advanced stuff (like help requests, which are there in Motif, but almost undocumented.) Do we support this at all?
  • IMPORTANT: Are events handled by Tk during a drag action? (I think the answer to this should be yes; if the platform supports [send] then it should definitely be possible to use it during the drop stage...)
  • Do we expose drag-over feedback handling to the script level? If so, how?
  • MacOS provides a nice drag behavior called 'Click through' -- this means the drag initiator need not be the front window. It goes something like this: if you just click/release on a non-front window, it brings that window to the front (nothing to do with DnD), but if you click-drag on an item in that non-front window (e.g. an icon or whatever), a drag is started, without ever bringing that window to the front (i.e. without doing a 'raise' on that window). It would be nice to be able to support this (on those platforms for which it is possible). Vince

On this last point, X certainly supports this sort of behavior, though it depends on whether the window manager performs auto-raise operations. (Some do, some don't, and most are configurable.)

- DKF

  • Some platforms (e.g. MacOS, MacOS X) allow an arbitrary shaped region (not sure if it has to be one connected region or not) to be specified as the source of the drag. This is then shown as a faint dotted outline when dragging. For example, when dragging a block of text from an window, this outline is just the shape of the selected text. Clearly things like Tk's canvas and text widget (and possibly others) would want to be able to specify the shape of such a region Vince (Jan 2003). Is it even sensible to think this could this be exposed to the Tcl level, even?

Other thoughts/comments/ideas of note

I think it's important to have a simple, minimal protocol up soon that allows dropping onto widgets and accepts some obvious string format. Pretty much all major other toolkits have this now (wxWindows, GTK+, Qt, MFC, etc.). Most applications of drag-and-drop can be built with some simple support like that.

Tcl/Tk has always tried to make the common case work well and I think it would be good to continue that philosophy.

Getting coordinates, drag-over feedback, multiple data formats, etc. are niceties that could be added later but may not even be supportable on all platforms.

Java, incidentally, just uses the native drag-and-drop interfaces as much as possible, so there should not be a big problem inter-operating with it. Java DND works, for example, with GTK and Windows.

(tmb at aimnet.com)


I've been using tkdnd a fair bit, and it's led me to conclude that any full dnd implementation must have a data-handle-callback mechanism. This is because information on the drag/drop may potentially be megabytes (contents of a file) or not, and may be in different possible formats, and the drag handler (i.e., Tk) must be able to request at least some attributes of the data to be able to determine if a drop is to be allowed. This should happen during the drag action, not once the user has already dropped something.

Example 1: tkdnd won't currently provide the %D data field during DragEnter or Drag events, which means all Tk has to go on is that the dragged item is, say, a file. No name is provided, even. So, a widget cannot even filter its drag events based upon file extension. (Note: as of Jan 2004, George has a private development version of tkdnd which on Windows does provide this information).

Example 2: tkdnd contains preliminary support for a 'FileDescriptor' -- this is a file that doesn't really exist -- say a file displayed in a browser window's ftp site listing, or a file displayed in a virtual-filesystem listing. We want to be able to drag such a file and then, if and only if the drop is successful do we want to access the contents of the file through a callback mechanism.

A quick implementation of this would be quite easy: the drag/drop receiver would get an opaque handle, and could call 'dnd getdata $draghandle' to get the contents. However, it could be that this is more complex, and there may be different possible formats in which the data is available. One quite general solution would be like 'file attributes', and have a platform-specific 'drag attributes $draghandle' (hopefully some elements of which are cross-platform!).

More comments/details/ideas are deeply welcome! DKF


A simple DnD implementation by SES

SES_home - 2009-12-31 14:25:46

Hello all, I would like to share with you, a simple implementation of Drag-And-Drop between listbox type widgets (as implemented in tG² v1.05.02 and higher )

# Purpose : to process the dropped element inside the target widget
proc place_listbox_proccess_DAD {args} {
  global DAD_content
  [lindex $args 0] insert end $DAD_content
}

# Purpose       : enables Drag-And-Drop controls of any listbox widget
proc place_listbox_controls_DAD {w {cutOrig 0} {procName "place_listbox_proccess_DAD"}} {
  bind $w <ButtonPress-1> {+;
    set DAD_pressed 1
  }

  bind $w <Motion> {+;
    if {[catch {set DAD_pressed $DAD_pressed}]} {set DAD_pressed 0}
    if {$DAD_pressed} {
      %W config -cursor exchange
      if {[catch {set DAD_content [%W get [%W curselection]]}]} {
        set DAD_pressed 0
      }
    } else {
      %W config -cursor ""
    }
  }

  if $cutOrig {set cutCmd "$w delete \[$w curselection\]"} else {set cutCmd ""}
  if {$procName == ""} {set procName place_listbox_proccess_DAD}
    
  set cmd "bind $w \<ButtonRelease-1\> \{+\;
    $w config -cursor \"\"
    set DAD_pressed 0    
    if \{\[catch \{set DAD_content \$DAD_content\}\]\} \{set DAD_content \"\"\}
    if \{\$DAD_content != \"\"\} \{
      set wDAD   \[winfo containing  \[winfo pointerx .\]  \[winfo pointery .\]\]
      if \{\(\$wDAD ne \"\"\) && \(\$wDAD != \"\%W\"\)\} \{
        if !\[catch \{$procName \$wDAD\}\] \{$cutCmd\}
      \}
      set DAD_content \"\"
    \}
  \}"
  eval $cmd
}

# ---------- TEST CODE ------------------------
listbox .ls1 -height 20 -width 10 
foreach item {a b c d e} {.ls1 insert end $item}
listbox .ls2 -height 20 -width 10 
foreach item {f g h i j} {.ls2 insert end $item}
place .ls1 -x 10 -y 50
place .ls2 -x 100 -y 50

place_listbox_controls_DAD .ls1
place_listbox_controls_DAD .ls2 1

ThePianist - 2010-11-13 08:26:46

Hi. I am trying to do drag and drop in a ttk tree. Anyone can tell me the changes i have to make relatively to this code? Thanks!