[wxPython] newbie's failed attempt at drag-n-drop with wxTreeCtrl

'''HELP: A newbie's failed attempt at drag n drop with wxTreeCtrl

Having shamelessly plagiarized the demo code and XMLTreeview, I am currently
stuck, trying without a great deal of success to implement drag-n-drop
functionality within a wxTreeCtrl

Problem 1:

Objective: I would like to be able to reorganize a rendered xml tree by
arbitrarily moving nodes around.

e.g In the dummy xml string below I would like to move a news channels (e.g.
CNN)
using drag and drop to the 'Favourites' branch which currently only
contains slashdot

I'm stuck however:

+ EVT_TREE_END_DRAG is in the documentation but doesn't
seems to be implemented in wxPython 2.2.5

+ do I implement some conditional logic which turns every branch
    into a wxDropTarget and every leaf into a wxDropSource?

I know I'm way off but even a hit at the proper trajectory would go a long
way...

Problem 2:

Is there a generic method for expanding all nodes in a wxTreeCtrl?

Many thanks for any help....

Sam Anderson
sa@bayt.net

'''

import string, webbrowser

from wxPython.wx import *
from xml.parsers import expat

__title__ = 'wxTreeCtrl DragNDrop Test'

# dummy xml file
xml='''<?xml version="1.0" encoding="UTF-8"?>
<Test title="Channels">
<Channels title="Favourites">
  <Channel type="channel" title="Slashdot" uri="http://slashdot.org"/>
</Channels>
<Channels title="News">
  <Channel title="CNN" uri="http://cnn.com"/>
  <Channel title="BBC" uri="http://bbc.co.uk"/>
  <Channel title="The Economist" uri="http://economist.com"/>
  <Channel title="MSNB" uri="http://msnbc.com"/>
</Channels>
<Channels title="Sports">
  <Channel type="channel" title="ESPN" uri="http://espn.com"/>
</Channels>
<Trash title="Trash"/>
</Test>'''

class TestApp(wxApp):

def OnInit(self):

  # create navigation frame
  frame = NavFrame(NULL, -1, __title__)
  tree = frame.tree

  # expand a few nodes
  # NOTE: I know this is pathetic but I couldn't figure out how
  # to walk through and expand all nodes
  root = tree.nodeStack[0]
  child, cookie = tree.GetFirstChild(root, 0)
  nchild, cookie = tree.GetFirstChild(child, 0)
  tree.Expand(root)
  tree.Expand(child)
  tree.Expand(nchild)

  # display navigation frame
  frame.Show(true)
  self.SetTopWindow(frame)
  return true

class NavFrame(wxFrame):

def __init__(self, parent, ID, title):
  wxFrame.__init__(self, parent, ID, title,
     wxDefaultPosition,
     wxSize(270,500))

  # give the tree a unique id
  tID = NewId()

  # call the tree
  self.tree = XMLTree(self, tID, wxDefaultPosition, wxSize(270,490),
       wxTR_HAS_BUTTONS | wxTR_EDIT_LABELS)

  # Load XML into tree
  self.tree.LoadXML()

  # tree events
  EVT_TREE_ITEM_EXPANDED (self.tree, tID, self.OnItemExpanded)
  EVT_TREE_ITEM_COLLAPSED (self.tree, tID, self.OnItemCollapsed)
  EVT_TREE_SEL_CHANGED (self.tree, tID, self.OnSelChanged)
  EVT_LEFT_DOWN (self.tree, self.OnTreeLeftDown)
   #-------------Drag-N-Drop Event Handlers
  EVT_TREE_BEGIN_DRAG(self.tree, tID, self.OnBeginDrag)
  #EVT_TREE_END_DRAG(self.tree, tID, self.OnEndDrag)

  # other events
  EVT_CLOSE(self, self.OnCloseWindow)

def OnSelChanged(self, event):

  # item is the node selected
  item = event.GetItem()

  # gets item text for selected node
  itemText = self.tree.GetItemText(item)

  # get dictionary of attributed for node
  attrs = self.tree.GetPyData(item)

  # if attribute 'uri' exists, open specified value with browser
  if attrs.has_key('uri'):
   uri = attrs['uri']
   webbrowser.open(uri)

def OnItemExpanded(self, event):
  item = event.GetItem()
  event.Skip()

def OnBeginDrag(self, event):
  event.Allow()
  item = event.GetItem()
  event.Skip()

def OnItemCollapsed(self, event):
  item = event.GetItem()
  event.Skip()

def OnTreeLeftDown(self, event):
  pt = event.GetPosition();
  item, flags = self.tree.HitTest(pt)
  if item == self.tree.GetSelection():
   print 'LeftDown'
  else:
   event.Skip()

def OnCloseWindow(self, event):
  self.Destroy()

class XMLTree(wxTreeCtrl):

def __init__(self, parent, id, pos, size, style):
  wxTreeCtrl.__init__(self, parent, id, pos, size, style)

  # create nodeStack and add root node
  self.nodeStack = [self.AddRoot("My InfoSpace")]

def StartElement(self, name, attrs ):
  name = name.encode()

  id = self.AppendItem(self.nodeStack[-1], name)

  # for each element map xml attributes to an associated dictionary
  self.SetPyData(id, attrs)

  # set title of tree node based on element title attribute
  if attrs.has_key('title'):
   title = attrs['title']
   self.SetItemText(id, str(title))

  # add element to tree
  self.nodeStack.append(id)

def EndElement(self, name ):
  self.nodeStack = self.nodeStack[:-1]

def CharacterData(self, data ):
  if string.strip(data):
   data = data.encode()
   self.AppendItem(self.nodeStack[-1], data)

def LoadXML(self):
  Parser = expat.ParserCreate()

  Parser.StartElementHandler = self.StartElement
  Parser.EndElementHandler = self.EndElement
  Parser.CharacterDataHandler = self.CharacterData

  ParserStatus = Parser.Parse(xml, 1)

if __name__ == '__main__':
app = TestApp(0)
app.MainLoop()