changeset 816:cf19fd45a40e

drop support and hover helper for tkdnd.py Ignore-this: 3d75faad9787092e0cbee7c505aa2ffa
author drewp@bigasterisk.com
date Thu, 01 Nov 2012 03:59:53 +0000
parents d7f1f868eb6c
children 6885c2fa9369
files light9/tkdnd.py
diffstat 1 files changed, 113 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/light9/tkdnd.py	Sat Oct 20 21:52:10 2012 +0000
+++ b/light9/tkdnd.py	Thu Nov 01 03:59:53 2012 +0000
@@ -1,6 +1,60 @@
 from glob import glob
 from os.path import join, basename
 
+class TkdndEvent(object):
+    """
+    see http://www.ellogon.org/petasis/tcltk-projects/tkdnd/tkdnd-man-page
+    for details on the fields
+
+    The longer attribute names (action instead of %A) were made up for
+    this API.
+
+    Not all attributes are visible yet, since I have not thought
+    through what conversions they should receive and I don't want to
+    unnecessarily change their types later.
+    """
+    substitutions = {
+        "%A" : "action",
+        "%b" : "button",
+        "%D" : "data",
+        "%m" : "modifiers",
+        "%T" : "type",
+        "%W" : "targetWindow",
+        "%X" : "mouseX",
+        "%Y" : "mouseY",
+        }
+
+    @classmethod
+    def makeEvent(cls, *args):
+        ev = cls()
+        for (k, v), arg in zip(sorted(TkdndEvent.substitutions.items()), args):
+            setattr(ev, v, arg)
+        # it would be cool for this to decode text data according to the charset in the type
+        for attr in ['button', 'mouseX', 'mouseY']:
+            setattr(ev, attr, int(getattr(ev, attr)))
+        return (ev,)
+
+    tclSubstitutions = ' '.join(sorted(substitutions.keys()))
+
+    def __repr__(self):
+        return "<TkdndEvent %r>" % self.__dict__
+
+class Hover(object):
+    def __init__(self, widget, style):
+        self.widget, self.style = widget, style
+        self.oldStyle = {}
+
+    def set(self, ev):
+        for k, v in self.style.items():
+            self.oldStyle[k] = self.widget.cget(k)
+        self.widget.configure(**self.style)
+        print "set with", ev
+        return ev.action
+
+    def restore(self, ev):
+        print "restore", self.oldStyle
+        self.widget.configure(**self.oldStyle)
+
 def initTkdnd(tk, tkdndBuildDir):
     """
     pass the 'tk' attribute of any Tkinter object, and the top dir of
@@ -16,14 +70,72 @@
 
 def dragSourceRegister(widget,
                        action='copy', datatype='text/uri-list', data=''):
+    """
+    if the 'data' param is callable, it will be called every time to
+    look up the current data
+    """
     widget.tk.call('tkdnd::drag_source', 'register', widget._w)
 
     # with normal Tkinter bind(), the result of your handler isn't
     # actually returned so the drag doesn't get launched. This is a
     # corrected version of what bind() does when you pass a function,
     # but I don't block my tuple from getting returned (as a tcl list)
-    funcId = widget._register(lambda: (action, datatype, data),
+
+    def init():
+        return (action, datatype, data() if callable(data) else data)
+    
+    funcId = widget._register(init, 
                               widget._substitute,
                               1 # needscleanup
                               )
     widget.bind("<<DragInitCmd>>", funcId)
+
+def dropTargetRegister(widget, typeList=None,
+                       onDropEnter=None,
+                       onDropPosition=None,
+                       onDropLeave=None,
+                       onDrop=None,
+                       hoverStyle=None,
+                       ):
+    """
+    the optional callbacks will be called with a TkdndEvent
+    argument.
+
+    onDropEnter, onDropPosition, and onDrop are supposed to return an
+    action (perhaps the value in event.action). The return value seems
+    to have no effect, but it might just be that errors are getting
+    silenced.
+
+    Passing hoverStyle sets onDropEnter to call
+    widget.configure(**hoverStyle) and onDropLeave to restore the
+    widget's style. onDrop is also wrapped to do a restore.
+    """
+
+    if hoverStyle is not None:
+        hover = Hover(widget, hoverStyle)
+        def wrappedDrop(ev):
+            hover.restore(ev)
+            if onDrop:
+                return onDrop(ev)
+        return dropTargetRegister(widget, typeList=typeList,
+                                  onDropEnter=hover.set,
+                                  onDropLeave=hover.restore,
+                                  onDropPosition=onDropPosition,
+                                  onDrop=wrappedDrop)
+    
+    if typeList is None:
+        typeList = ['*']
+    widget.tk.call(*(['tkdnd::drop_target', 'register', widget._w]+typeList))
+
+    for sequence, handler in [
+        ('<<DropEnter>>', onDropEnter),
+        ('<<DropPosition>>', onDropPosition),
+        ('<<DropLeave>>', onDropLeave),
+        ('<<Drop>>', onDrop),
+        ]:
+        if not handler:
+            continue
+        func = widget._register(handler, subst=TkdndEvent.makeEvent, needcleanup=1)
+        widget.bind(sequence, func + " " + TkdndEvent.tclSubstitutions)
+
+