]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/wx/tools/XRCed/tree.py
test needed for wxGTK
[wxWidgets.git] / wxPython / wx / tools / XRCed / tree.py
index 3c5ddf95fbc4a718402d1e1469eba2cf31fb92d9..1e77eac3a4554da23f5ecea7c69332ee609c4a68 100644 (file)
@@ -86,10 +86,12 @@ class ID_NEW:
     CHOICEBOOK = wx.NewId()
     LISTBOOK = wx.NewId()
     SPLITTER_WINDOW = wx.NewId()
+    GRID = wx.NewId()
     SCROLLED_WINDOW = wx.NewId()
     HTML_WINDOW = wx.NewId()
     CALENDAR_CTRL = wx.NewId()
     DATE_CTRL = wx.NewId()
+    FILE_PICKER_CTRL = wx.NewId()
     GENERIC_DIR_CTRL = wx.NewId()
     SPIN_CTRL = wx.NewId()
     UNKNOWN = wx.NewId()
@@ -124,6 +126,10 @@ class ID_NEW:
     CONTEXT_HELP_BUTTON = wx.NewId()
 
     REF = wx.NewId()
+    COMMENT = wx.NewId()
+
+    CUSTOM = wx.NewId()
+    for i in range(99): wx.NewId()   # reserve IDs for custom controls
 
     LAST = wx.NewId()
 
@@ -193,10 +199,12 @@ class PullDownMenu:
             ID_NEW.CHOICEBOOK: 'wxChoicebook',
             ID_NEW.LISTBOOK: 'wxListbook',
             ID_NEW.SPLITTER_WINDOW: 'wxSplitterWindow',
+            ID_NEW.GRID: 'wxGrid',
             ID_NEW.SCROLLED_WINDOW: 'wxScrolledWindow',
             ID_NEW.HTML_WINDOW: 'wxHtmlWindow',
             ID_NEW.CALENDAR_CTRL: 'wxCalendarCtrl',
             ID_NEW.DATE_CTRL: 'wxDatePickerCtrl',
+            ID_NEW.FILE_PICKER_CTRL: 'wxFilePickerCtrl',
             ID_NEW.GENERIC_DIR_CTRL: 'wxGenericDirCtrl',
             ID_NEW.SPIN_CTRL: 'wxSpinCtrl',
 
@@ -268,10 +276,12 @@ class PullDownMenu:
              (ID_NEW.SCROLL_BAR, 'ScrollBar', 'Create scroll bar'),
              (ID_NEW.TREE_CTRL, 'TreeCtrl', 'Create tree'),
              (ID_NEW.LIST_CTRL, 'ListCtrl', 'Create list'),
+#             (ID_NEW.GRID, 'Grid', 'Create grid'),
              (ID_NEW.SCROLLED_WINDOW, 'ScrolledWindow', 'Create scrolled window'),
              (ID_NEW.HTML_WINDOW, 'HtmlWindow', 'Create HTML window'),
              (ID_NEW.CALENDAR_CTRL, 'CalendarCtrl', 'Create calendar control'),
              (ID_NEW.DATE_CTRL, 'DatePickerCtrl', 'Create date picker control'),
+#             (ID_NEW.FILE_PICKER_CTRL, 'FilePickerCtrl', 'Create file picker control'),
              (ID_NEW.GENERIC_DIR_CTRL, 'GenericDirCtrl', 'Create generic dir control'),
              (ID_NEW.UNKNOWN, 'Unknown', 'Create custom control placeholder'),
              ],
@@ -370,7 +380,13 @@ class PullDownMenu:
             ID_NEW.HELP_BUTTON: ('wxID_HELP', '&Help'),
             ID_NEW.CONTEXT_HELP_BUTTON: ('wxID_CONTEXT_HELP', '&Help'),
             }
-            
+        self.custom = ['custom', 'User-defined controls']
+        self.customMap = {}        
+        
+    def addCustom(self, klass):
+        n = len(self.custom)-2
+        self.custom.append((ID_NEW.CUSTOM + n, klass))
+        self.customMap[ID_NEW.CUSTOM + n] = klass
 
 
 ################################################################################
@@ -396,19 +412,32 @@ def SetMenu(m, list, shift=False):
 ################################################################################
 
 class HighLightBox:
+    colour = None
     def __init__(self, pos, size):
+        colour = g.tree.COLOUR_HL
         if size.width == -1: size.width = 0
         if size.height == -1: size.height = 0
         w = g.testWin.panel
         l1 = wx.Window(w, -1, pos, wx.Size(size.width, 2))
-        l1.SetBackgroundColour(wx.RED)
+        l1.SetBackgroundColour(colour)
         l2 = wx.Window(w, -1, pos, wx.Size(2, size.height))
-        l2.SetBackgroundColour(wx.RED)
+        l2.SetBackgroundColour(colour)
         l3 = wx.Window(w, -1, wx.Point(pos.x + size.width - 2, pos.y), wx.Size(2, size.height))
-        l3.SetBackgroundColour(wx.RED)
+        l3.SetBackgroundColour(colour)
         l4 = wx.Window(w, -1, wx.Point(pos.x, pos.y + size.height - 2), wx.Size(size.width, 2))
-        l4.SetBackgroundColour(wx.RED)
+        l4.SetBackgroundColour(colour)
         self.lines = [l1, l2, l3, l4]
+        if wx.Platform == '__WXMSW__':
+            for l in self.lines:
+                l.Bind(wx.EVT_PAINT, self.OnPaint)
+        g.testWin.highLight = self
+        self.size = size
+    # Repainting is not always done for these windows on Windows
+    def OnPaint(self, evt):
+        w = evt.GetEventObject()
+        dc = wx.PaintDC(w)
+        w.ClearBackground()
+        dc.Destroy()
     # Move highlight to a new position
     def Replace(self, pos, size):
         if size.width == -1: size.width = 0
@@ -417,29 +446,78 @@ class HighLightBox:
         self.lines[1].SetDimensions(pos.x, pos.y, 2, size.height)
         self.lines[2].SetDimensions(pos.x + size.width - 2, pos.y, 2, size.height)
         self.lines[3].SetDimensions(pos.x, pos.y + size.height - 2, size.width, 2)
-    # Remove it
+        self.size = size
     def Remove(self):
         map(wx.Window.Destroy, self.lines)
         g.testWin.highLight = None
     def Refresh(self):
         map(wx.Window.Refresh, self.lines)
 
+# Same for drop target
+class HighLightDTBox(HighLightBox):
+    colour = None
+    def __init__(self, pos, size):
+        colour = g.tree.COLOUR_DT
+        if size.width == -1: size.width = 0
+        if size.height == -1: size.height = 0
+        w = g.testWin.panel
+        l1 = wx.Window(w, -1, pos, wx.Size(size.width, 2))
+        l1.SetBackgroundColour(colour)
+        l2 = wx.Window(w, -1, pos, wx.Size(2, size.height))
+        l2.SetBackgroundColour(colour)
+        l3 = wx.Window(w, -1, wx.Point(pos.x + size.width - 2, pos.y), wx.Size(2, size.height))
+        l3.SetBackgroundColour(colour)
+        l4 = wx.Window(w, -1, wx.Point(pos.x, pos.y + size.height - 2), wx.Size(size.width, 2))
+        l4.SetBackgroundColour(colour)
+        self.lines = [l1, l2, l3, l4]
+        self.item = None
+        self.size = size
+    # Remove it
+    def Remove(self):
+        map(wx.Window.Destroy, self.lines)
+        g.testWin.highLightDT = None
+
+def updateHL(hl, hlClass, pos, size):
+    # Need to recreate window if size did not change to force update
+    if hl and hl.size == size:
+        hl.Remove()
+        hl = None
+    if hl:
+        hl.Replace(pos, size)
+    else:
+        hl = hlClass(pos, size)
+    hl.Refresh()
+    return hl
+
 ################################################################################
 
 class XML_Tree(wx.TreeCtrl):
     def __init__(self, parent, id):
-        wx.TreeCtrl.__init__(self, parent, id, style = wx.TR_HAS_BUTTONS | wx.TR_MULTIPLE)
+        # Item colour
+        self.COLOUR_COMMENT  = wx.Colour(0, 0, 255)
+        self.COLOUR_REF      = wx.Colour(0, 0, 128)
+        self.COLOUR_HIDDEN   = wx.Colour(128, 128, 128)
+        self.COLOUR_HL       = wx.Colour(255, 0, 0)
+        self.COLOUR_DT       = wx.Colour(0, 64, 0)
+
+        wx.TreeCtrl.__init__(self, parent, id,
+                             style = wx.TR_HAS_BUTTONS | wx.TR_MULTIPLE | wx.TR_EDIT_LABELS)
         self.SetBackgroundColour(wx.Colour(224, 248, 224))
+        self.fontComment = wx.FFont(self.GetFont().GetPointSize(),
+                                    self.GetFont().GetFamily(),
+                                    wx.FONTFLAG_ITALIC)
         # Register events
         wx.EVT_TREE_SEL_CHANGED(self, self.GetId(), self.OnSelChanged)
         # One works on Linux, another on Windows
-        if wx.Platform == '__WXGTK__':
+        if wx.Platform == '__WXGTK__': # !!! MAC too?
             wx.EVT_TREE_ITEM_ACTIVATED(self, self.GetId(), self.OnItemActivated)
         else:
             wx.EVT_LEFT_DCLICK(self, self.OnDClick)
         wx.EVT_RIGHT_DOWN(self, self.OnRightDown)
         wx.EVT_TREE_ITEM_EXPANDED(self, self.GetId(), self.OnItemExpandedCollapsed)
         wx.EVT_TREE_ITEM_COLLAPSED(self, self.GetId(), self.OnItemExpandedCollapsed)
+        self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnBeginLabelEdit)
+        self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnEndLabelEdit)
 
         self.selection = None
         self.selectionChanging = False
@@ -450,6 +528,7 @@ class XML_Tree(wx.TreeCtrl):
         # Create image list
         il = wx.ImageList(16, 16, True)
         self.rootImage = il.Add(images.getTreeRootImage().Scale(16,16).ConvertToBitmap())
+        xxxComment.image = il.Add(images.getTreeCommentImage().Scale(16,16).ConvertToBitmap())
         xxxObject.image = il.Add(images.getTreeDefaultImage().Scale(16,16).ConvertToBitmap())
         xxxPanel.image = il.Add(images.getTreePanelImage().Scale(16,16).ConvertToBitmap())
         xxxDialog.image = il.Add(images.getTreeDialogImage().Scale(16,16).ConvertToBitmap())
@@ -498,7 +577,6 @@ class XML_Tree(wx.TreeCtrl):
 
     # Clear tree
     def Clear(self):
-        self.selection = None
         self.UnselectAll()
         self.DeleteAllItems()
         # Add minimal structure
@@ -511,6 +589,7 @@ class XML_Tree(wx.TreeCtrl):
         self.rootObj = xxxMainNode(self.dom)
         self.root = self.AddRoot('XML tree', self.rootImage,
                                  data=wx.TreeItemData(self.rootObj))
+        self.itemColour = self.GetItemTextColour(self.root)
         self.SetItemHasChildren(self.root)
         self.testElem = self.dom.createElement('dummy')
         self.mainNode.appendChild(self.testElem)
@@ -518,7 +597,6 @@ class XML_Tree(wx.TreeCtrl):
 
     # Clear old data and set new
     def SetData(self, dom):
-        self.selection = None
         self.UnselectAll()
         self.DeleteAllItems()
         # Add minimal structure
@@ -560,19 +638,22 @@ class XML_Tree(wx.TreeCtrl):
         item = self.AppendItem(itemParent, treeObj.treeName(),
                                image=treeObj.treeImage(),
                                data=wx.TreeItemData(xxx))
-        # Different color for references
-        if treeObj.ref:
-            self.SetItemTextColour(item, 'DarkGreen')
+        # Different color for comments and references
+        if xxx.className == 'comment':
+            self.SetItemTextColour(item, self.COLOUR_COMMENT)
+            self.SetItemFont(item, self.fontComment)
+        elif treeObj.ref:
+            self.SetItemTextColour(item, self.COLOUR_REF)
         elif treeObj.hasStyle and treeObj.params.get('hidden', False):
-            self.SetItemTextColour(item, 'Grey')
+            self.SetItemTextColour(item, self.COLOUR_HIDDEN)
         # Try to find children objects
         if treeObj.hasChildren:
-            nodes = treeObj.element.childNodes[:]
+            nodes = treeObj.node.childNodes[:]
             for n in nodes:
                 if IsObject(n):
                     self.AddNode(item, treeObj, n)
                 elif n.nodeType != minidom.Node.ELEMENT_NODE:
-                    treeObj.element.removeChild(n)
+                    treeObj.node.removeChild(n)
                     n.unlink()
 
     # Insert new item at specific position
@@ -581,23 +662,30 @@ class XML_Tree(wx.TreeCtrl):
         xxx = MakeXXXFromDOM(parent, elem)
         # If nextItem is None, we append to parent, otherwise insert before it
         if nextItem.IsOk():
-            node = self.GetPyData(nextItem).element
-            parent.element.insertBefore(elem, node)
+            node = self.GetPyData(nextItem).node
+            parent.node.insertBefore(elem, node)
             # Inserting before is difficult, se we insert after or first child
             index = self.ItemIndex(nextItem)
             newItem = self.InsertItemBefore(itemParent, index,
                         xxx.treeName(), image=xxx.treeImage())
             self.SetPyData(newItem, xxx)
         else:
-            parent.element.appendChild(elem)
+            parent.node.appendChild(elem)
             newItem = self.AppendItem(itemParent, xxx.treeName(), image=xxx.treeImage(),
                                       data=wx.TreeItemData(xxx))
-        # Different color for references
-        if xxx.treeObject().ref:  self.SetItemTextColour(newItem, 'DarkGreen')
+        treeObj = xxx.treeObject()
+        # Different color for references and comments
+        if xxx.className == 'comment':
+            self.SetItemTextColour(newItem, self.COLOUR_COMMENT)
+            self.SetItemFont(newItem, self.fontComment)
+        elif treeObj.ref:
+            self.SetItemTextColour(newItem, self.COLOUR_REF)
+        elif treeObj.hasStyle and treeObj.params.get('hidden', False):
+            self.SetItemTextColour(newItem, self.COLOUR_HIDDEN)
         # Add children items
         if xxx.hasChildren:
             treeObj = xxx.treeObject()
-            for n in treeObj.element.childNodes:
+            for n in treeObj.node.childNodes:
                 if IsObject(n):
                     self.AddNode(newItem, treeObj, n)
         return newItem
@@ -605,12 +693,10 @@ class XML_Tree(wx.TreeCtrl):
     # Remove leaf of tree, return it's data object
     def RemoveLeaf(self, leaf):
         xxx = self.GetPyData(leaf)
-        node = xxx.element
+        node = xxx.node
         parent = node.parentNode
         parent.removeChild(node)
         self.Delete(leaf)
-        # Reset selection object
-        self.selection = None
         return node
     
     # Find position relative to the top-level window
@@ -686,9 +772,27 @@ class XML_Tree(wx.TreeCtrl):
     def OnSelChanged(self, evt):
         if self.selectionChanging: return
         self.selectionChanging = True
-        self.UnselectAll()
-        self.SelectItem(evt.GetItem())
+        wx.TreeCtrl.UnselectAll(self)
+        self.ChangeSelection(evt.GetItem())
+        wx.TreeCtrl.SelectItem(self, evt.GetItem())
         self.selectionChanging = False
+        g.frame.SetStatusText('')
+        evt.Skip()
+
+    # Override to use like single-selection tree
+    def GetSelection(self):
+        return self.selection
+
+    def SelectItem(self, item):
+        self.UnselectAll()
+        self.ChangeSelection(item)
+        wx.TreeCtrl.SelectItem(self, item)
+
+    def UnselectAll(self):
+        self.selection = None
+        g.tools.UpdateUI()
+        wx.TreeCtrl.UnselectAll(self)
+        wx.Yield()
 
     def ChangeSelection(self, item):
         # Apply changes
@@ -696,8 +800,7 @@ class XML_Tree(wx.TreeCtrl):
         #oldItem = evt.GetOldItem()
         status = ''
         oldItem = self.selection
-        # use GetItemParent as a way to determine if the itemId is still valid
-        if oldItem and self.GetItemParent(oldItem):
+        if oldItem:
             xxx = self.GetPyData(oldItem)
             # If some data was modified, apply changes
             if g.panel.IsModified():
@@ -707,13 +810,14 @@ class XML_Tree(wx.TreeCtrl):
                         g.testWin.highLight.Remove()
                     self.needUpdate = True
                 status = 'Changes were applied'
-        g.frame.SetStatusText(status)
+        if status: g.frame.SetStatusText(status)
         # Generate view
-        self.selection = item
-        if not self.selection.IsOk():
+        if not item:
             self.selection = None
             return
-        xxx = self.GetPyData(self.selection)
+        else:
+            self.selection = item
+        xxx = self.GetPyData(item)
         # Update panel
         g.panel.SetData(xxx)
         # Update tools
@@ -748,16 +852,15 @@ class XML_Tree(wx.TreeCtrl):
         if not obj or xxx.hasStyle and xxx.params.get('hidden', False):
             if g.testWin.highLight: g.testWin.highLight.Remove()
             return
-        pos = self.FindNodePos(item, obj)
+        pos = self.FindNodePos(item, obj)         
         size = obj.GetSize()
         # Highlight
         # Negative positions are not working quite well
-        if g.testWin.highLight:
-            g.testWin.highLight.Replace(pos, size)
-        else:
-            g.testWin.highLight = HighLightBox(pos, size)
-        g.testWin.highLight.Refresh()
+        # If highlight object has the same size SetDimension does not repaint it
+        # so we must remove the old HL window
+        g.testWin.highLight = updateHL(g.testWin.highLight, HighLightBox, pos, size)
         g.testWin.highLight.item = item
+        g.testWin.highLight.obj = obj
 
     def ShowTestWindow(self, item):
         xxx = self.GetPyData(item)
@@ -811,19 +914,6 @@ class XML_Tree(wx.TreeCtrl):
         testWin = g.testWin
         # Create a window with this resource
         xxx = self.GetPyData(item).treeObject()
-
-        # If frame
-#        if xxx.__class__ == xxxFrame:
-            # Frame can't have many children,
-            # but it's first child possibly can...
-#            child = self.GetFirstChild(item)[0]
-#            if child.IsOk() and self.GetPyData(child).__class__ == xxxPanel:
-#                # Clean-up before recursive call or error
-#                wx.MemoryFSHandler.RemoveFile('xxx.xrc')
-#                wx.EndBusyCursor()
-#                self.CreateTestWin(child)
-#                return
-
         # Close old window, remember where it was
         highLight = None
         if testWin:
@@ -849,7 +939,7 @@ class XML_Tree(wx.TreeCtrl):
         # Save in memory FS
         memFile = MemoryFile('xxx.xrc')
         # Create memory XML file
-        elem = xxx.element.cloneNode(True)
+        elem = xxx.node.cloneNode(True)
         if not xxx.name:
             name = 'noname'
         else:
@@ -878,6 +968,11 @@ class XML_Tree(wx.TreeCtrl):
         if not g.currentEncoding:
             xmlFlags != xrc.XRC_USE_LOCALE
         res = xrc.XmlResource('', xmlFlags)
+        res.InitAllHandlers()
+        xrc.XmlResource.Set(res)        # set as global
+        # Register handlers
+        addHandlers()
+        # Same module list
         res.Load('memory:xxx.xrc')
         try:
             if xxx.__class__ == xxxFrame:
@@ -903,12 +998,13 @@ class XML_Tree(wx.TreeCtrl):
                 # Create new frame
                 if not testWin:
                     testWin = g.testWin = wx.Frame(g.frame, -1, 'Panel: ' + name,
-                                                  pos=pos, name=STD_NAME)
+                                                   pos=pos, name=STD_NAME)
                 testWin.panel = res.LoadPanel(testWin, STD_NAME)
-                testWin.SetClientSize(testWin.GetBestSize())
+                testWin.panel.SetSize(testWin.GetClientSize())
+                #testWin.SetClientSize(testWin.GetSize())
                 testWin.Show(True)
             elif xxx.__class__ == xxxDialog:
-                testWin = g.testWin = res.LoadDialog(None, STD_NAME)
+                testWin = g.testWin = res.LoadDialog(g.frame, STD_NAME)
                 testWin.panel = testWin
                 testWin.Layout()
                 testWin.SetPosition(pos)
@@ -918,7 +1014,7 @@ class XML_Tree(wx.TreeCtrl):
                 wx.EVT_BUTTON(testWin, wx.ID_CANCEL, self.OnCloseTestWin)
             elif xxx.__class__ == xxxWizard:
                 wiz = wx.wizard.PreWizard()
-                res.LoadOnObject(wiz, None, STD_NAME, 'wxWizard')
+                res.LoadOnObject(wiz, g.frame, STD_NAME, 'wxWizard')
                 # Find first page (don't know better way)
                 firstPage = None
                 for w in wiz.GetChildren():
@@ -965,7 +1061,13 @@ class XML_Tree(wx.TreeCtrl):
                 testWin.item = item
                 wx.EVT_CLOSE(testWin, self.OnCloseTestWin)
                 wx.EVT_SIZE(testWin, self.OnSizeTestWin)
-                testWin.highLight = None
+                # Add drop target
+                if testWin.panel:
+                    testWin.panel.SetDropTarget(DropTarget())
+                else:
+                    testWin.SetDropTarget(DropTarget())
+                # Reset highlights
+                testWin.highLight = testWin.highLightDT = None
                 if highLight and not self.pendingHighLight:
                     self.HighLight(highLight)
         except:
@@ -977,6 +1079,9 @@ class XML_Tree(wx.TreeCtrl):
             inf = sys.exc_info()
             wx.LogError(traceback.format_exception(inf[0], inf[1], None)[-1])
             wx.LogError('Error loading resource')
+        # Cleanup
+        res.Unload('xxx.xrc')
+        xrc.XmlResource.Set(None)
         wx.MemoryFSHandler.RemoveFile('xxx.xrc')
 
     def CloseTestWindow(self):
@@ -991,8 +1096,12 @@ class XML_Tree(wx.TreeCtrl):
         self.CloseTestWindow()
 
     def OnSizeTestWin(self, evt):
-        if g.testWin.highLight:
-            self.HighLight(g.testWin.highLight.item)
+        # Update highlight after size change
+        hl = g.testWin.highLight
+        if hl:
+            hl.Replace(self.FindNodePos(hl.item), hl.obj.GetSize())
+            hl.Refresh()
+            #self.HighLight(g.testWin.highLight.item)
         evt.Skip()
 
     # Return index in parent, for real window children
@@ -1040,14 +1149,6 @@ class XML_Tree(wx.TreeCtrl):
             return False
         return not (self.IsExpanded(item) and self.GetChildrenCount(item, False))
 
-    # Override to use like single-selection tree
-    def GetSelection(self):
-        return self.selection
-    def SelectItem(self, item):
-        self.UnselectAll()
-        self.ChangeSelection(item)
-        wx.TreeCtrl.SelectItem(self, item)
-
     # Pull-down
     def OnRightDown(self, evt):
         pullDownMenu = g.pullDownMenu
@@ -1076,6 +1177,7 @@ class XML_Tree(wx.TreeCtrl):
                 SetMenu(m, pullDownMenu.topLevel)
                 m.AppendSeparator()
                 m.Append(ID_NEW.REF, 'reference...', 'Create object_ref node')
+                m.Append(ID_NEW.COMMENT, 'comment', 'Create comment node')
             else:
                 xxx = self.GetPyData(item).treeObject()
                 # Check parent for possible child nodes if inserting sibling
@@ -1097,8 +1199,12 @@ class XML_Tree(wx.TreeCtrl):
                         m.Enable(ID_NEW.SPACER, False)
                     if xxx.__class__ is not xxxFrame:
                         m.Enable(ID_NEW.MENU_BAR, False)
+                # Add custom controls menu
+                if len(pullDownMenu.custom) > 2:
+                    SetMenu(m, [pullDownMenu.custom])
                 m.AppendSeparator()
                 m.Append(ID_NEW.REF, 'reference...', 'Create object_ref node')
+                m.Append(ID_NEW.COMMENT, 'comment', 'Create comment node')
             # Select correct label for create menu
             if not needInsert:
                 if self.shift:
@@ -1134,11 +1240,12 @@ class XML_Tree(wx.TreeCtrl):
                     SetMenu(m, pullDownMenu.sizers, shift=True)
                 else:
                     SetMenu(m, pullDownMenu.controls, shift=True)
-                id = wx.NewId()
-                menu.AppendMenu(id, 'Replace With', m)
-                if not m.GetMenuItemCount(): menu.Enable(id, False)
-                menu.Append(pullDownMenu.ID_SUBCLASS, 'Subclass...',
-                            'Set "subclass" property')
+                if xxx.isElement:
+                    id = wx.NewId()
+                    menu.AppendMenu(id, 'Replace With', m)
+                    if not m.GetMenuItemCount(): menu.Enable(id, False)
+                    menu.Append(pullDownMenu.ID_SUBCLASS, 'Subclass...',
+                                'Set "subclass" property')
             menu.AppendSeparator()
             # Not using standart IDs because we don't want to show shortcuts
             menu.Append(wx.ID_CUT, 'Cut', 'Cut to the clipboard')
@@ -1173,9 +1280,115 @@ class XML_Tree(wx.TreeCtrl):
             # Item width may have changed
             # !!! Tric to update tree width (wxGTK, ??)
             self.SetIndent(self.GetIndent())
+        elif xxx.className == 'comment':
+            self.SetItemText(item, xxx.treeName())
         # Change tree icon for sizers
         if isinstance(xxx, xxxBoxSizer):
             self.SetItemImage(item, xxx.treeImage())
         # Set global modified state
         g.frame.SetModified()
 
+    def OnBeginLabelEdit(self, evt):
+        xxx = self.GetPyData(evt.GetItem())
+        if xxx.isElement:
+            evt.Veto()
+        else:
+            evt.Skip()
+
+    def OnEndLabelEdit(self, evt):
+        xxx = self.GetPyData(evt.GetItem())
+        node = xxx.node
+        if not xxx.isElement:
+            node.data = evt.GetLabel()
+            g.panel.SetData(xxx)
+        evt.Skip()
+
+################################################################################
+
+# DragAndDrop
+
+class DropTarget(wx.PyDropTarget):
+    def __init__(self):
+        self.do = MyDataObject()
+        wx.DropTarget.__init__(self, self.do)
+
+    # Find best object for dropping
+    def WhereToDrop(self, x, y, d):
+        # Find object by position
+        obj = wx.FindWindowAtPoint(g.testWin.ClientToScreen((x,y)))
+        if not obj:
+            return wx.DragNone, ()
+        item = g.frame.FindObject(g.testWin.item, obj)
+        if not item:
+            return wx.DragNone, ()
+        xxx = g.tree.GetPyData(item).treeObject()
+        parentItem = None
+        # Check if window has a XRC sizer, then use it as parent
+        if obj.GetSizer():
+            sizer = obj.GetSizer()
+            sizerItem = g.frame.FindObject(g.testWin.item, sizer)
+            if sizerItem:
+                parentItem = sizerItem
+                obj = sizer
+                item = wx.TreeItemId()
+        # if not sizer but can have children, it is parent with free placement
+        elif xxx.hasChildren:
+            parentItem = item
+            item = wx.TreeItemId()
+        # Otherwise, try to add to item's parent
+        if not parentItem:
+            parentItem = g.tree.GetItemParent(item)
+            obj = g.tree.FindNodeObject(parentItem)
+        parent = g.tree.GetPyData(parentItem).treeObject()
+        return d,(obj,parent,parentItem,item)
+        
+    # Drop
+    def OnData(self, x, y, d):
+        self.GetData()
+        id = int(self.do.GetDataHere())
+        d,other = self.WhereToDrop(x, y, d)
+        if d != wx.DragNone:
+            obj,parent,parentItem,item = other
+            g.tree.selection = parentItem
+            xxx = g.frame.CreateXXX(parent, parentItem, item,  id)
+            # Set coordinates if parent is not sizer
+            if not parent.isSizer:
+                xxx.set('pos', '%d,%d' % (x, y))
+                g.panel.SetData(xxx)
+            g.frame.SetStatusText('Object created')
+        self.RemoveHL()
+        return d
+
+    def OnDragOver(self, x, y, d):
+        d,other = self.WhereToDrop(x, y, d)
+        if d != wx.DragNone:
+            obj,parent,parentItem,item = other
+            pos, size = g.tree.FindNodePos(parentItem, obj), obj.GetSize()
+            hl = g.testWin.highLightDT
+            # Set color of highlighted item back to normal
+            if hl and hl.item:
+                if hl.item != parentItem:
+                    g.tree.SetItemTextColour(hl.item, g.tree.itemColour)
+                    # Highlight future parent
+                    g.tree.itemColour = g.tree.GetItemTextColour(parentItem) # save current
+            if not hl or hl.item != parentItem:
+                g.testWin.highLightDT = updateHL(hl, HighLightDTBox, pos, size)
+                g.testWin.highLightDT.item = parentItem
+            g.tree.SetItemTextColour(parentItem, g.tree.COLOUR_DT)
+            g.tree.EnsureVisible(parentItem)
+            g.frame.SetStatusText('Drop target: %s' % parent.treeName())
+        else:
+            g.frame.SetStatusText('Inappropriate drop target')
+            self.RemoveHL()
+        return d
+
+    def OnLeave(self):
+        self.RemoveHL()
+
+    def RemoveHL(self):
+        hl = g.testWin.highLightDT
+        if hl:
+            if hl.item:
+                g.tree.SetItemTextColour(hl.item, g.tree.itemColour)
+            hl.Remove()
+