#-----------------------------------------------------------------------------
# Name:        ContainerCompanions.py
# Purpose:
#
# Author:      Riaan Booysen
#
# Created:     2002
# RCS-ID:      $Id: ContainerCompanions.py,v 1.14 2005/05/18 11:36:41 riaan Exp $
# Copyright:   (c) 2002 - 2005
# Licence:     GPL
#-----------------------------------------------------------------------------
print 'importing Companions.ContainerCompanions'

import copy

import wx

import Preferences, Utils

from BaseCompanions import ContainerDTC, CollectionDTC, CollectionIddDTC
import PaletteStore

import Constructors
from EventCollections import *

from PropEdit.PropertyEditors import *
from PropEdit.Enumerations import *

import methodparse, sourceconst

EventCategories['PanelEvent'] = ('wx.EVT_SYS_COLOUR_CHANGED',)

class PanelDTC(Constructors.WindowConstr, ContainerDTC):
    def __init__(self, name, designer, parent, ctrlClass):
        ContainerDTC.__init__(self, name, designer, parent, ctrlClass)
        self.editors['DefaultItem'] = ButtonClassLinkPropEdit
        self.windowStyles.insert(0, 'wx.TAB_TRAVERSAL')

    def designTimeSource(self, position = 'wx.DefaultPosition', size = 'wx.DefaultSize'):
        return {'pos':   position,
                'size': self.getDefCtrlSize(),
                'style': 'wx.TAB_TRAVERSAL',
                'name':  `self.name`}

    def events(self):
        return ContainerDTC.events(self) + ['PanelEvent']

    def dependentProps(self):
        return ContainerDTC.dependentProps(self) + ['DefaultItem']

EventCategories['SashEvent'] = ('wx.EVT_SASH_DRAGGED', )
commandCategories.append('SashEvent')
class SashWindowDTC(Constructors.WindowConstr, ContainerDTC):
    def __init__(self, name, designer, parent, ctrlClass):
        ContainerDTC.__init__(self, name, designer, parent, ctrlClass)
        self.editors.update({'SashVisibleLeft' : SashVisiblePropEdit,
                             'SashVisibleTop' : SashVisiblePropEdit,
                             'SashVisibleRight' : SashVisiblePropEdit,
                             'SashVisibleBottom' : SashVisiblePropEdit})
        self.windowStyles = ['wx.SW_3D', 'wx.SW_3DSASH', 'wx.SW_3DBORDER',
                             'wx.SW_BORDER'] + self.windowStyles
        self.edgeNameMap = {'SashVisibleLeft'  : wx.SASH_LEFT,
                            'SashVisibleTop'   : wx.SASH_TOP,
                            'SashVisibleRight' : wx.SASH_RIGHT,
                            'SashVisibleBottom': wx.SASH_BOTTOM}
        for name in self.edgeNameMap.keys() + ['SashVisible']:
            self.customPropEvaluators[name] = self.EvalSashVisible
        #self.customPropEvaluators['SashVisible'] = self.EvalSashVisible

    def properties(self):
        props = ContainerDTC.properties(self)
        prop = ('NameRoute', self.GetSashVisible, self.SetSashVisible)
        for name in self.edgeNameMap.keys():
            props[name] = prop
        return props

    def designTimeSource(self, position = 'wx.DefaultPosition', size = 'wx.DefaultSize'):
        return {'pos':   position,
                'size': self.getDefCtrlSize(),
                'style': 'wx.CLIP_CHILDREN | wx.SW_3D',
                'name':  `self.name`}

    def events(self):
        return ContainerDTC.events(self) + ['SashEvent']

    def GetSashVisible(self, name):
        return self.edgeNameMap[name], self.control.GetSashVisible(self.edgeNameMap[name])

    def SetSashVisible(self, name, value):
        self.control.SetSashVisible(self.edgeNameMap[name], value[1])

    def EvalSashVisible(self, exprs, objects):
        res = []
        for expr in exprs:
            res.append(self.eval(expr))
        return tuple(res)

    def persistProp(self, name, setterName, value):
        if setterName == 'SetSashVisible':
            edge, visbl = value.split(',')
            for prop in self.textPropList:
                if prop.prop_setter == setterName and prop.params[0] == edge:
                    prop.params = [edge.strip(), visbl.strip()]
                    return
            self.textPropList.append(methodparse.PropertyParse( None, self.name,
                setterName, [edge.strip(), visbl.strip()], 'SetSashVisible'))
        else:
            ContainerDTC.persistProp(self, name, setterName, value)

class SashLayoutWindowDTC(SashWindowDTC):
    def __init__(self, name, designer, parent, ctrlClass):
        SashWindowDTC.__init__(self, name, designer, parent, ctrlClass)
        self.editors.update({'Alignment'   : EnumPropEdit,
                             'Orientation' : EnumPropEdit,
                            })
        self.options.update({'Alignment'   : sashLayoutAlignment,
                             'Orientation' : sashLayoutOrientation
                            })
        self.names.update({'Alignment'   : sashLayoutAlignmentNames,
                           'Orientation' : sashLayoutOrientationNames
                          })

    def designTimeSource(self, position = 'wx.DefaultPosition', size = 'wx.DefaultSize'):
        return {'pos':   position,
                'size': self.getDefCtrlSize(),
                'style': 'wx.CLIP_CHILDREN | wx.SW_3D',
                'name':  `self.name`}

    def properties(self):
        props = SashWindowDTC.properties(self)
        props.update({'DefaultSize': ('CompnRoute', self.GetDefaultSize,
          self.SetDefaultSize)})
        return props

    def getSizeDependentProps(self):
        return SashWindowDTC.getSizeDependentProps(self)+[('prop', 'DefaultSize')]

##    def events(self):
##        return ContainerDTC.events(self) + ['SashEvent']

    def GetDefaultSize(self, something):
        if self.control:
            return self.control.GetSize()
        else:
            return wx.Size(15, 15)

    def SetDefaultSize(self, size):
        if self.control:
            self.control.SetSize(size)

    def defaultAction(self):
        # should be called from 'Relayout' command
        self.control.SetDefaultSize(self.control.GetSize())
        wx.LayoutAlgorithm().LayoutWindow(self.control.GetParent())

class ScrolledWindowDTC(Constructors.WindowConstr, ContainerDTC):
    def __init__(self, name, designer, parent, ctrlClass):
        ContainerDTC.__init__(self, name, designer, parent, ctrlClass)
        self.editors['TargetWindow'] = WindowClassLinkPropEdit

    def designTimeSource(self, position = 'wx.DefaultPosition', size = 'wx.DefaultSize'):
        return {'pos':   position,
                'size': self.getDefCtrlSize(),
                'style': 'wx.HSCROLL | wx.VSCROLL',
                'name':  `self.name`}

    def dependentProps(self):
        return ContainerDTC.dependentProps(self) + ['TargetWindow']

    def events(self):
        return ContainerDTC.events(self) + ['ScrollWinEvent']

    def notification(self, compn, action):
        ContainerDTC.notification(self, compn, action)
        if action == 'delete':
            if self.control.GetTargetWindow() == compn.control:
                self.propRevertToDefault('TargetWindow', 'SetTargetWindow')
                self.control.SetTargetWindow(self.control)

EventCategories['NotebookEvent'] = ('wx.EVT_NOTEBOOK_PAGE_CHANGED',
                                    'wx.EVT_NOTEBOOK_PAGE_CHANGING')
commandCategories.append('NotebookEvent')
class NotebookDTC(Constructors.WindowConstr, ContainerDTC):
    def __init__(self, name, designer, parent, ctrlClass):
        ContainerDTC.__init__(self, name, designer, parent, ctrlClass)
        self.editors.update({'Pages':     CollectionPropEdit,
                             'ImageList': ImageListClassLinkPropEdit})
        self.subCompanions['Pages'] = NotebookPagesCDTC
        self.windowStyles = ['wx.NB_FIXEDWIDTH', 'wx.NB_LEFT', 'wx.NB_RIGHT',
                             'wx.NB_BOTTOM', 'wx.NB_MULTILINE'] + \
                             self.windowStyles
        self.letClickThru = True

    def properties(self):
        props = ContainerDTC.properties(self)
        props.update({'Pages': ('NoneRoute', None, None)})
        del props['Sizer']
        return props

    def designTimeControl(self, position, size, args = None):
        ctrl = ContainerDTC.designTimeControl(self, position, size, args)
        ctrl.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChanged, id=ctrl.GetId())
        return ctrl

    def designTimeSource(self, position = 'wx.DefaultPosition', size = 'wx.DefaultSize'):
        return {'pos':   position,
                'size':  self.getDefCtrlSize(),
                'style': '0',
                'name':  `self.name`}

    def dependentProps(self):
        return ContainerDTC.dependentProps(self) + ['ImageList', 'Pages']

    def dontPersistProps(self):
        return ContainerDTC.dontPersistProps(self) + ['Selection']

    def events(self):
        return ContainerDTC.events(self) + ['NotebookEvent']

    def defaultAction(self):
        self.designer.inspector.props.getNameValue('Pages').propEditor.edit(None)

    def OnPageChanged(self, event):
        try:
            if self.collections.has_key('Pages'):
                self.collections['Pages'].updateSelection(event.GetSelection())
                wx.PostEvent(self.control, wx.SizeEvent( self.control.GetSize() ))
        except Exception, err:
            print 'OnPageChanged exception', str(err)
        event.Skip()

    def notification(self, compn, action):
        ContainerDTC.notification(self, compn, action)
        if action == 'delete':
            if self.control.GetImageList() == compn.control:
                self.propRevertToDefault('ImageList', 'SetImageList')
                self.control.SetImageList(None)

class NotebookPagesCDTC(CollectionDTC):
    propName = 'Pages'
    displayProp = 'text'
    indexProp = '(None)'
    insertionMethod = 'AddPage'
    deletionMethod = 'RemovePage'

    def __init__(self, name, designer, parentCompanion, ctrl):
        CollectionDTC.__init__(self, name, designer, parentCompanion, ctrl)
        self.editors = {'Page': ClassLinkConstrPropEdit,
                        'Text': StrConstrPropEdit,
                        'Selected' : BoolConstrPropEdit,
                        'ImageId': IntConstrPropEdit}

        self.tempPlaceHolders = []

    def constructor(self):
        return {'Page': 'page', 'Text': 'text',
                'Selected' : 'select', 'ImageId': 'imageId'}

    def properties(self):
        props = CollectionDTC.properties(self)
        props.update({'Page': ('NoneRoute', None, None),
                      'Text': ('IndexRoute', wx.Notebook.GetPageText.im_func, wx.Notebook.SetPageText.im_func),
                      'Selected' : ('CompnRoute', self.GetPageSelected, self.SetPageSelected),
                      'ImageId': ('IndexRoute', wx.Notebook.GetPageImage.im_func, wx.Notebook.SetPageImage.im_func)})
        return props

##    def appendItem(self):
##        tph = wx.Window(self.control, -1)
##        self.tempPlaceHolders.append(tph)
##        CollectionDTC.appendItem(self)

    def designTimeSource(self, wId, method=None):
        return {'page': 'None',
                'text': `'%s%d'%(self.propName, wId)`,
                'select': 'True',
                'imageId': `-1`}

    def initDesignTimeEvents(self, ctrl):
        ctrlEvtHandler = self.designer.ctrlEvtHandler
        ctrl.Bind(wx.EVT_MOTION, ctrlEvtHandler.OnMouseOver)
        ctrl.Bind(wx.EVT_LEFT_DOWN, ctrlEvtHandler.OnControlSelect)
        ctrl.Bind(wx.EVT_LEFT_UP, ctrlEvtHandler.OnControlRelease)
        ctrl.Bind(wx.EVT_LEFT_DCLICK, ctrlEvtHandler.OnControlDClick)
        ctrl.Bind(wx.EVT_SIZE, ctrlEvtHandler.OnControlResize)

    def applyDesignTimeDefaults(self, params, method=None):
        prms = copy.copy(params)

        page = BlankWindowPage(self.control, self.designer, params, 'page')
        self.tempPlaceHolders.append(page)
        if params['page'] != 'None':
            ctrl = self.designer.objects[prms['page'][5:]][1]
            ctrl.Reparent(page)
            page.ctrl = ctrl

        del prms['page']
        params = self.designTimeDefaults(prms, method)
        params['page'] = page

        getattr(self.control, self.insertionMethod)(**params)

    def moveItem(self, idx, dir):
        newIdx = CollectionDTC.moveItem(self, idx, dir)
        if newIdx != idx:
            focus = self.control.GetSelection() == idx
            text = self.control.GetPageText(idx)
            img = self.control.GetPageImage(idx)
            page = self.control.GetPage(idx)
            if self.control.RemovePage(idx):
                self.control.InsertPage(newIdx, page, text, focus, img)
        return newIdx

    def deleteItem(self, idx):
        activePageDeleted = self.control.GetSelection() == idx

        CollectionDTC.deleteItem(self, idx)

        # Delete BlankWindow
        self.tempPlaceHolders[idx].Destroy()
        del self.tempPlaceHolders[idx]

        # Set new active page if necesary
        if activePageDeleted:
            newIdx = idx
            if idx == len(self.textConstrLst):
                newIdx = newIdx - 1
            elif len(self.textConstrLst) == 0:
                newIdx = None

            if newIdx is not None:
                if newIdx < self.control.GetPageCount():
                    self.updateSelection(newIdx)
                    self.control.SetSelection(newIdx)
                    self.control.Refresh()

        del self.textConstrLst[idx]

    def updateSelection(self, newSelection):
        idx = 0
        for constr in self.textConstrLst:
            constr.params['select'] = newSelection == idx and 'True' or 'False'
            idx = idx + 1

    def defaultAction(self):
        self.SetPageSelected(True)

    def GetPageSelected(self, compn):
        pass

    def SetPageSelected(self, value):
        if value:
            self.control.SetSelection(self.index)

    def notification(self, compn, action):
        if action == 'delete':
            if compn == self:
                # Notebook page being deleted
                # XXX Consider overwriting deleteItem for this
                constr = self.textConstrLst[compn.index]
                if constr.params['page'] != 'None':
                    self.designer.deleteCtrl(Utils.ctrlNameFromSrcRef(constr.params['page']))
            else:
                for constr in self.textConstrLst:
                    if Utils.srcRefFromCtrlName(compn.name) == constr.params['page']:
                        constr.params['page'] == 'None'

                for tph in self.tempPlaceHolders:
                    if tph.ctrl == compn.control:
                        tph.clearCtrl()

    def writeCollectionItems(self, output, stripFrmId=''):
        CollectionDTC.writeCollectionItems(self, output, stripFrmId)
        warn = 0
        for constr in self.textConstrLst:
            if constr.params['page'] == 'None':
                wx.LogWarning('No control for %s, page %s'%(
                      self.parentCompanion.name, constr.params['text']))
                warn = 1
        if warn:
            wx.LogWarning('The red-dashed area of a wx.Notebook page must contain\n'\
            'a control or the generated source will be invalid outside the Designer')


[wxID_CTRLPARENT, wxID_EDITPASTE, wxID_EDITDELETE] = Utils.wxNewIds(3)

class BlankWindowPage(wx.Window):
    """ Window representing uninitialised space, it grabs the first control
        dropped onto it and replaces None with new ctrl in code.

        Used by wx.Notebook for a default page
    """

    # XXX A delete of this object should remove the page from the notebook

    # XXX Copy/Cutting a blank page does not make sense ???

    def __init__(self, parent, designer, params, nameKey):
        wx.Window.__init__(self, parent, -1)
        self.params = params
        self.designer = designer

        # XXX how/when can I delete this menu, it will leak !!
        self.menu = wx.Menu()
        self.menu.Append(wxID_CTRLPARENT, 'Up')
        self.menu.AppendSeparator()
        self.menu.Append(wxID_EDITPASTE, 'Paste')
        self.menu.Append(wxID_EDITDELETE, 'Delete')
        self.Bind(wx.EVT_MENU, self.OnCtrlParent, id=wxID_CTRLPARENT)
        self.Bind(wx.EVT_MENU, self.OnCtrlPaste, id=wxID_EDITPASTE)
        self.Bind(wx.EVT_MENU, self.OnCtrlDelete, id=wxID_EDITDELETE)

        ctrlEvtHandler = self.designer.ctrlEvtHandler
        self.Bind(wx.EVT_MOTION, ctrlEvtHandler.OnMouseOver)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnControlSelect)
        self.Bind(wx.EVT_LEFT_UP, ctrlEvtHandler.OnControlRelease)
        self.Bind(wx.EVT_LEFT_DCLICK, ctrlEvtHandler.OnControlDClick)
        self.Bind(wx.EVT_SIZE, self.OnControlResize)
        self.Bind(wx.EVT_MOVE, self.OnControlMove)
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_RIGHT_UP, self.OnPopupMenu)

        self.SetName(parent.GetName())
        self.ctrl = None
        self.nameKey = nameKey

        self.proxyContainer = True

    def clearCtrl(self):
        self.ctrl = None
        self.params[self.nameKey] = 'None'

    def OnControlSelect(self, event):
        """ Select parent of proxy container or create new control, update
            page reference to it and resize new ctrl to fill proxy container
        """
        dsgn = self.designer
        new = dsgn.compPal.selection
        if self.ctrl:
            dsgn.selectControlByPos(self.ctrl, event.GetPosition(), event.ShiftDown())
        else:
            dsgn.ctrlEvtHandler.OnControlSelect(event)

        if new:
            self.linkToNewestControl()

    def OnControlResize(self, event):
        """ Child ctrl of the proxy container should be sized to fill the control
        """
        if self.ctrl:
            p = self.GetPosition()
            s = self.GetSize()
            self.ctrl.SetDimensions(0, 0, s.x, s.y)
        else:
            self.designer.ctrlEvtHandler.OnControlResize(event)

    dashSize = 8
    def OnPaint(self, event):
        """ Draw red dash pattern, hopefully implying danger are that must be
            filled
        """
        try:
            if self.ctrl is None:
                dc = wx.PaintDC(self)
                sze = self.GetClientSize()

                dc.SetPen(wx.RED_PEN)
                dc.BeginDrawing()
                try:
                    for i in range((sze.x + sze.y) / self.dashSize):
                        dc.DrawLine(0, i*self.dashSize, i*self.dashSize, 0)
                finally:
                    dc.EndDrawing()
        finally:
            event.Skip()

    def OnControlMove(self, event):
        """ When moved or sized, ignore and fill the proxy container
        """
        parent = self.GetParent()
        wx.PostEvent(parent, wx.SizeEvent( parent.GetSize() ))

    def OnCtrlParent(self, event):
        self.designer.OnSelectParent(event)

    def OnCtrlPaste(self, event):
        self.designer.OnPasteSelected(event)

    def OnCtrlDelete(self, event):
        self.designer.OnControlDelete(event)

    def OnPopupMenu(self, event):
        self.PopupMenu(self.menu, event.GetPosition())

    def linkToNewestControl(self):
        self.ctrl = self.designer.objects[self.designer.objectOrder[-1:][0]][1]
        self.params[self.nameKey] = Utils.srcRefFromCtrlName(self.ctrl.GetName())
        self.OnControlResize(None)


EventCategories['SplitterWindowEvent'] = ('wx.EVT_SPLITTER_SASH_POS_CHANGING',
                                          'wx.EVT_SPLITTER_SASH_POS_CHANGED',
                                          'wx.EVT_SPLITTER_UNSPLIT',
                                          'wx.EVT_SPLITTER_DOUBLECLICKED')
commandCategories.append('SplitterWindowEvent')

class SplitterWindowDTC(ContainerDTC):
    def __init__(self, name, designer, parent, ctrlClass):
        ContainerDTC.__init__(self, name, designer, parent, ctrlClass)
        self.editors.update({'SplitMode': EnumPropEdit,
                             'Window1'  : SplitterWindow1LinkPropEdit,
                             'Window2'  : SplitterWindow2LinkPropEdit})
        self.options['SplitMode'] = splitterWindowSplitMode
        self.names['SplitMode'] = splitterWindowSplitModeNames
        self.windowStyles = ['wx.SP_3D', 'wx.SP_3DSASH', 'wx.SP_3DBORDER',
                             'wx.SP_BORDER', 'wx.SP_NOBORDER', 
                             'wx.SP_PERMIT_UNSPLIT', 'wx.SP_LIVE_UPDATE',
                             ] + self.windowStyles 
        self.win1 = None
        self.win2 = None

    def constructor(self):
        return {'Position': 'point', 'Size': 'size', 'Style': 'style',
                'Name': 'name'}

    def properties(self):
        props = ContainerDTC.properties(self)
        props.update({'Window1'  : ('CompnRoute', self.GetWindow1, self.SetWindow1),
                      'Window2'  : ('CompnRoute', self.GetWindow2, self.SetWindow2),
                      'SplitMode': ('CompnRoute', self.GetSplitMode, self.SetSplitMode),})
        return props

    def designTimeSource(self, position = 'wx.DefaultPosition', size = 'wx.DefaultSize'):
        return {'point': position,
                'size': self.getDefCtrlSize(),
                'style': 'wx.SP_3D',
                'name':  `self.name`}

    def hideDesignTime(self):
        return ContainerDTC.hideDesignTime(self) + ['SplitMode']

    def dependentProps(self):
        return ContainerDTC.dependentProps(self) + \
          ['SplitVertically', 'SplitHorizontally']

    def events(self):
        return ContainerDTC.events(self) + ['SplitterWindowEvent']

    modeMethMap = {wx.SPLIT_VERTICAL:   'SplitVertically',
                   wx.SPLIT_HORIZONTAL: 'SplitHorizontally'}

    def renameCtrlRefs(self, oldName, newName):
        ContainerDTC.renameCtrlRefs(self, oldName, newName)
        # Check if subwindow references have changed
        # XXX should maybe be done with notification, action = 'rename'
        oldSrc = Utils.srcRefFromCtrlName(oldName)
        for prop in self.textPropList:
            if prop.prop_setter in ('SplitVertically', 'SplitHorizontally'):
                if oldSrc in prop.params:
                    prop.params[prop.params.index(oldSrc)] = \
                          Utils.srcRefFromCtrlName(newName)

    def persistProp(self, name, setterName, value):
        """ When attempting to persist the Window properties and the
            SplitMode property, add, or update a previously
            defined  SplitVertically or SplitHorizontally method."""

        if setterName in ('SetWindow1', 'SetWindow2'):
            propSN = setterName
            setterName = self.modeMethMap[self.control.GetSplitMode()]

            win1, win2 = self.GetWindow1(None), self.GetWindow2(None)
            sashPos = `self.control.GetSashPosition()`

            if win1: win1 = 'self.'+win1.GetName()
            else: win1 = 'None'
            if win2: win2 = 'self.'+win2.GetName()
            else: win2 = 'None'

            for prop in self.textPropList:
                if prop.prop_setter in ('SplitVertically', 'SplitHorizontally'):
                    prop.prop_setter = setterName
                    prop.params = [win1, win2, sashPos]
                    return
            self.textPropList.append(methodparse.PropertyParse( None, self.name,
                setterName, [win1, win2, sashPos], setterName))
        elif setterName == 'SetSplitMode':
            sm = self.control.GetSplitMode()
            setterName = self.modeMethMap[sm]
            for prop in self.textPropList:
                if prop.prop_setter == 'SplitVertically' and sm == wx.SPLIT_HORIZONTAL or \
                   prop.prop_setter == 'SplitHorizontally' and sm == wx.SPLIT_VERTICAL:
                    prop.prop_setter = setterName
                    return

        elif setterName == 'SetSashPosition':
            for prop in self.textPropList:
                if prop.prop_setter in ('SplitVertically', 'SplitHorizontally'):
                    prop.params[2] = value
                    return
        else:
            ContainerDTC.persistProp(self, name, setterName, value)

    def persistedPropVal(self, name, setterName):
        # Unlike usual properties, SashPosition is persisted as a Split* method
        # Therefore it needs to check the Split* method for the source value
        if setterName == 'SetSashPosition':
            for prop in self.textPropList:
                if prop.prop_setter in ('SplitVertically', 'SplitHorizontally'):
                    return prop.params[2]
        return ContainerDTC.persistedPropVal(self, name, setterName)

    def notification(self, compn, action):
        ContainerDTC.notification(self, compn, action)
        if action == 'delete':
            # If the splitter itself is deleted it should unsplit so that
            # deletion notifications from it's children won't cause
            # access to deleted controls
            if compn.control == self.control:
                self.win1 = self.win2 = None
                self.splitWindow(None, None)
                return
            # Win 1
            # If Window1 is None, splitter can only be unsplit
            if compn.control == self.win1:#self.GetWindow1(None):
                self.control.Unsplit(self.win1)
                if self.win2: self.win2.Show(True)
                self.win1 = self.win2 = None

                setterName = self.modeMethMap[self.control.GetSplitMode()]
                self.propRevertToDefault('Window1', setterName)
                self.designer.inspector.propertyUpdate('Window1')
                self.designer.inspector.propertyUpdate('Window2')
                return
            if compn.control == self.win2:#self.GetWindow2(None):
                self.SetWindow2(None)
                setterName = self.modeMethMap[self.control.GetSplitMode()]
                self.persistProp('Window2', setterName, None)
                self.designer.inspector.propertyUpdate('Window2')
                return

#---Split mode------------------------------------------------------------------
    def GetSplitMode(self, compn):
        return self.control.GetSplitMode()

    def SetSplitMode(self, value):
        ctrl = self.control
        ctrl.SetSplitMode(value)

        # Resplit to force splitter to update
        w1, w2 = ctrl.GetWindow1(), ctrl.GetWindow2()
        self.splitWindow(w1, w2)

#---Window1&2 methods---------------------------------------------------------
    def splitWindow(self, window1, window2):
        ctrl = self.control
        sm = ctrl.GetSplitMode()
        sp = ctrl.GetSashPosition()

        if ctrl.IsSplit():
            ctrl.Unsplit()

        if sm == wx.SPLIT_VERTICAL:
            splitMeth = ctrl.SplitVertically
        elif sm == wx.SPLIT_HORIZONTAL:
            splitMeth = ctrl.SplitHorizontally
        else:
            return

        if window1 and window2:
            splitMeth(window1, window2, sp)
        elif window1 or window2:
            if window1:
                ctrl.Initialize(window1)
            else:
                ctrl.Initialize(window2)

        if window1: window1.Show(True)
        if window2: window2.Show(True)

        self.designer.inspector.propertyUpdate('SashPosition')

    def getWindow(self, win, winGetter):
        if not win:
            w = winGetter()
            if w:
                wId = w.GetId()
                for cmpn, ctrl, prnt in self.designer.objects.values():
                    if ctrl.GetId() == wId:
                        return ctrl
            return None
        else:
            return win

    def GetWindow1(self, compn):
        self.win1 = self.getWindow(self.win1, self.control.GetWindow1)
        return self.win1

    def SetWindow1(self, value):
        # XXX check win1!=win2
        self.win1 = value
        w2 = self.GetWindow2(None)
        if value:
            self.splitWindow(value, w2)
        elif w2:
            self.win1 = self.win2 = None
            self.control.Unsplit()
            self.propRevertToDefault('Window1', self.modeMethMap[self.control.GetSplitMode()])
            self.designer.inspector.propertyUpdate('Window2')

    def GetWindow2(self, compn):
        self.win2 = self.getWindow(self.win2, self.control.GetWindow2)
        return self.win2

    def SetWindow2(self, value):
        self.win2 = value
        self.splitWindow(self.control.GetWindow1(), value)

EventCategories['ToolEvent'] = ('wx.EVT_TOOL', 'wx.EVT_TOOL_RCLICKED')
commandCategories.append('ToolEvent')
class ToolBarDTC(Constructors.WindowConstr, ContainerDTC):
    def __init__(self, name, designer, parent, ctrlClass):
        ContainerDTC.__init__(self, name, designer, parent, ctrlClass)
        self.editors.update({'Tools': CollectionPropEdit})
        self.subCompanions['Tools'] = ToolBarToolsCDTC
        self.windowStyles = ['wx.TB_FLAT', 'wx.TB_DOCKABLE', 'wx.TB_HORIZONTAL',
                             'wx.TB_VERTICAL', 'wx.TB_3DBUTTONS', 'wx.TB_TEXT',
                             'wx.TB_NOICONS', 'wx.TB_NODIVIDER', 'wx.TB_NOALIGN',
                            ] + self.windowStyles

    def properties(self):
        props = ContainerDTC.properties(self)
        props.update({'Tools': ('NoneRoute', None, None)})
        return props

    def designTimeSource(self, position = 'wx.DefaultPosition', size = 'wx.DefaultSize'):
        return {'pos': position,
                'size': size,
                'style': 'wx.TB_HORIZONTAL | wx.NO_BORDER',
                'name': `self.name`}

    def dependentProps(self):
        return ContainerDTC.dependentProps(self) + ['Tools']

    def defaultAction(self):
        self.designer.inspector.props.getNameValue('Tools').propEditor.edit(None)


def BlankToolBitmap(width, height):
    bmp = wx.EmptyBitmap(width, height)
    dc = wx.MemoryDC()
    dc.SelectObject(bmp)
    dc.SetBrush(wx.Brush(wx.RED, wx.BDIAGONAL_HATCH))
    dc.SetBackground(wx.Brush(wx.WHITE))
    dc.SetPen(wx.Pen(wx.WHITE, 0, wx.TRANSPARENT))
    dc.BeginDrawing()
    dc.DrawRectangle(0, 0, width, height)

    return bmp

class BlankToolControl(wx.StaticBitmap):
    def __init__(self, toolbar):
        width, height = toolbar.GetToolSize().Get()
        bmp = BlankToolBitmap(width, height)
        wx.StaticBitmap.__init__(self, toolbar, -1, bmp, style=wx.SIMPLE_BORDER)


class ToolBarToolsCDTC(CollectionIddDTC):
    propName = 'Tools'
    displayProp = 'shortHelp'
    indexProp = '(None)'
    insertionMethod = 'DoAddTool'
    deletionMethod = 'DeleteToolByPos'
    idProp = 'id'
    idPropNameFrom = 'tools'

    additionalMethods = {'AddSeparator': ('Add separator', '', '(None)'),
                         'AddControl': ('Add control', 'control', '(None)'),
                         'AddTool': ('Old add tool', 'shortHelpString', '(None)')
                        }

    def __init__(self, name, designer, parentCompanion, ctrl):
        CollectionIddDTC.__init__(self, name, designer, parentCompanion, ctrl)
        self.editors.update({'Bitmap': BitmapConstrPropEdit,
                             'PushedBitmap': BitmapConstrPropEdit,
                             'BitmapDisabled': BitmapConstrPropEdit,
                             'IsToggle': BoolConstrPropEdit,
                             'Label': StrConstrPropEdit,
                             'ShortHelpString': StrConstrPropEdit,
                             'LongHelpString': StrConstrPropEdit,
                             'ShortHelp': StrConstrPropEdit,
                             'LongHelp': StrConstrPropEdit,
                             'Kind': EnumConstrPropEdit,
                             'Control': WinEnumConstrPropEdit})
        self.names['Kind'] = ['wx.ITEM_NORMAL', 'wx.ITEM_CHECK', 'wx.ITEM_RADIO']

    def constructor(self):
        tcl = self.textConstrLst[self.index]
        if tcl.method == 'DoAddTool':
            return {'ItemId': 'id',
                    'Label': 'label',
                    'Bitmap': 'bitmap',
                    'BitmapDisabled': 'bmpDisabled',
                    'Kind': 'kind',
                    'ShortHelp': 'shortHelp',
                    'LongHelp': 'longHelp'}
        elif tcl.method == 'AddTool':
            return {'ItemId': 'id', 'Bitmap': 'bitmap',
                    'PushedBitmap': 'pushedBitmap',
                    'IsToggle': 'isToggle',
                    'ShortHelpString': 'shortHelpString',
                    'LongHelpString': 'longHelpString'}
        elif tcl.method == 'AddSeparator':
            return {}
        elif tcl.method == 'AddControl':
            return {'Control': 'control'}

    def properties(self):
        tcl = self.textConstrLst[self.index]
        if tcl.method in ('DoAddTool', 'AddTool'):
            return CollectionIddDTC.properties(self)
        elif tcl.method == 'AddSeparator':
            return {}
        elif tcl.method == 'AddControl':
            return {}

    def designTimeSource(self, wId, method=None):
        if method is None:
            method = self.insertionMethod

        newItemName, winId = self.newUnusedItemNames(wId)

        if method == 'DoAddTool':
            return {'id': winId,
                    'label': `''`,
                    'bitmap': 'wx.NullBitmap',
                    'bmpDisabled': 'wx.NullBitmap',
                    'kind': 'wx.ITEM_NORMAL',
                    'shortHelp': `newItemName`,
                    'longHelp': `''`}
        elif method == 'AddTool':
            return {'id': winId,
                    'bitmap': 'wx.NullBitmap',
                    'pushedBitmap': 'wx.NullBitmap',
                    'isToggle': 'False',
                    'shortHelpString': `newItemName`,
                    'longHelpString': `''`}
        elif method == 'AddSeparator':
            return {}
        elif method == 'AddControl':
            return {'control': 'None'}

    def finaliser(self):
        return ['', sourceconst.bodyIndent+'parent.Realize()']

    def appendItem(self, method=None):
        CollectionIddDTC.appendItem(self, method)
        self.control.Realize()

    def deleteItem(self, idx):
        CollectionIddDTC.deleteItem(self, idx)
        del self.textConstrLst[idx]

    def moveItem(self, idx, dir):
        newIdx = CollectionIddDTC.moveItem(self, idx, dir)
        if newIdx != idx:
            self.control.ClearTools()
            for crt in self.textConstrLst:
                self.applyDesignTimeDefaults(crt.params, crt.method)
        return newIdx

    def applyDesignTimeDefaults(self, params, method=None):
        if method is None:
            method = self.insertionMethod

        prms = copy.copy(params)
        control = None
        if 'control' in params:
            if params['control'] == 'None':
                control = BlankToolControl(self.control)
            else:
                control = self.designer.objects[Utils.ctrlNameFromSrcRef(params['control'])][1]
            del prms['control']

        bitmaps = {}
        for bmp in ('bitmap', 'bmpDisabled', 'pushedBitmap'):
            if bmp in params and params[bmp] == 'wx.NullBitmap':
                bitmaps[bmp] = BlankToolBitmap(*self.control.GetToolBitmapSize().Get())
                del prms[bmp]

        params = self.designTimeDefaults(prms)

        if method in ('AddSeparator', 'AddControl'):
            del params['id']

        if control:
            params['control'] = control

        for bmp in bitmaps:
            params[bmp] = bitmaps[bmp]

        getattr(self.control, method)(**params)

        self.control.Realize()

    def events(self):
        return ['ToolEvent']

    def defaultAction(self):
        insp = self.designer.inspector
        insp.pages.SetSelection(2)
        insp.events.doAddEvent('ToolEvent', 'wx.EVT_TOOL')

    def notification(self, compn, action):
        if action == 'delete':
            if compn != self:
                compnSrcRef = Utils.srcRefFromCtrlName(compn.name)
                for constr in self.textConstrLst:
                    if constr.params.has_key('control'):
                        if compnSrcRef == constr.params['control']:
                            constr.params['control'] = 'None'
                            idx = self.textConstrLst.index(constr)
                            self.control.DeleteToolByPos(idx)
                            del self.textConstrLst[idx]
                            return

    def writeCollectionItems(self, output, stripFrmId=''):
        CollectionIddDTC.writeCollectionItems(self, output, stripFrmId)
        warn = 0
        for constr in self.textConstrLst:
            if constr.params.has_key('control') and constr.params['control'] == 'None':
                wx.LogWarning("Invalid None control for toolbar %s's AddControl"%(
                      self.parentCompanion.name))
                warn = 1
        if warn:
            wx.LogWarning('Control may not be None or the generated source will '
                         'be invalid outside the Designer.')

    def GetOtherWin(self):
        return self.textConstrLst[self.index].params['control']

    def SetOtherWin(self, value):
        self.textConstrLst[self.index].params['control'] = value

class ToolBarSimpleDTC(ToolBarDTC):
    def designTimeSource(self, position = 'wx.DefaultPosition', size = 'wx.DefaultSize'):
        return {'pos': position,
                'size': self.getDefCtrlSize(),
                'style': 'wx.TB_HORIZONTAL | wx.NO_BORDER',
                'name': `self.name`}

class StatusBarDTC(ContainerDTC):
    def __init__(self, name, designer, parent, ctrlClass):
        ContainerDTC.__init__(self, name, designer, parent, ctrlClass)
        self.editors['Fields'] = CollectionPropEdit
        self.subCompanions['Fields'] = StatusBarFieldsCDTC
        self.windowStyles = ['wx.ST_SIZEGRIP'] + self.windowStyles

    def constructor(self):
        return {'Style': 'style', 'Name': 'name'}

    def properties(self):
        props = ContainerDTC.properties(self)
        props.update({'Fields': ('NoneRoute', None, None)})
        return props

    def designTimeSource(self, position='wx.DefaultPosition', size='wx.DefaultSize'):
        return {'style': '0',
                'name': `self.name`}

    def hideDesignTime(self):
        return ContainerDTC.hideDesignTime(self) + ['Position', 'Size', 'ClientSize']

class StatusBarFieldsCDTC(CollectionDTC):
    propName = 'Fields'
    displayProp = 'text'
    indexProp = 'number'
    insertionMethod = 'SetStatusText'
    deletionMethod = '(None)'

    def __init__(self, name, designer, parentCompanion, ctrl):
        CollectionDTC.__init__(self, name, designer, parentCompanion, ctrl)
        self.editors = {'Number' : IntConstrPropEdit,
                        'Text': StrConstrPropEdit,
                        'Width': SBFWidthConstrPropEdit}
        self.widths = []

    def constructor(self):
        return {'Number': 'number', 'Text': 'text', 'Width': 'width'}

    def properties(self):
        props = CollectionDTC.properties(self)
        props.update({'Number':  ('NoneRoute', None, None),
                      'Text':  ('CompnRoute', self.GetText, self.SetText),
                      'Width': ('NoneRoute', None, None)})
        return props

    def designTimeSource(self, wId, method=None):
        return {'number': `wId`,
                'text': `'%s%d'%(self.propName, wId)`}

    def initialiser(self):
        return [sourceconst.bodyIndent+'parent.SetFieldsCount(%d)'%(
                self.getCount())]+CollectionDTC.initialiser(self)

    def finaliser(self):
        return ['', sourceconst.bodyIndent+'parent.SetStatusWidths(%s)'%`self.widths`]

    def initCollection(self):
        if self.widths:
            self.control.SetStatusWidths(self.widths)

    def appendItem(self, method=None):
        self.widths.append(-1)
        self.control.SetFieldsCount(len(self.widths))
        CollectionDTC.appendItem(self, method)

    def deleteItem(self, index):
        CollectionDTC.deleteItem(self, index)
        for idx in range(index, len(self.widths)):
            self.control.SetStatusText(self.control.GetStatusText(idx+1), idx)
        del self.widths[index]
        self.control.SetStatusWidths(self.widths)
        self.control.SetFieldsCount(len(self.widths))

    def moveItem(self, idx, dir):
        newIdx = CollectionDTC.moveItem(self, idx, dir)
        if newIdx != idx:
            w = self.widths[idx]
            del self.widths[idx]
            self.widths.insert(newIdx, w)
            self.control.SetStatusWidths(self.widths)
            oldText = self.control.GetStatusText(idx)
            newText = self.control.GetStatusText(newIdx)
            self.control.SetStatusText(newText, idx)
            self.control.SetStatusText(oldText, newIdx)
        return newIdx

    def setConstrs(self, constrLst, inits, fins):
        CollectionDTC.setConstrs(self, constrLst, inits, fins)
        if len(fins):
            self.widths = self.eval(fins[0]\
              [fins[0].find('(')+1 : fins[0].rfind(')')])
            self.control.SetFieldsCount(len(self.widths))

    def GetWidth(self):
        return `self.widths[self.index]`

    def SetWidth(self, value):
        self.widths[self.index] = int(value)
        self.control.SetStatusWidths(self.widths)

    def GetText(self):
        return `self.control.GetStatusText(self.index)`

    def SetText(self, value):
        self.control.SetStatusText(value, self.index)

#-------------------------------------------------------------------------------

import Plugins

Plugins.registerPalettePage('ContainersLayout', 'Containers/Layout')

Plugins.registerComponents('ContainersLayout',
      (wx.Panel, 'wx.Panel', PanelDTC),
      (wx.ScrolledWindow, 'wx.ScrolledWindow', ScrolledWindowDTC),
      (wx.Notebook, 'wx.Notebook', NotebookDTC),
      (wx.SplitterWindow, 'wx.SplitterWindow', SplitterWindowDTC),
      (wx.SashWindow, 'wx.SashWindow', SashWindowDTC),
      (wx.SashLayoutWindow, 'wx.SashLayoutWindow', SashLayoutWindowDTC),
      (wx.ToolBar, 'wx.ToolBar', ToolBarDTC),
      (wx.StatusBar, 'wx.StatusBar', StatusBarDTC),
      (wx.Window, 'wx.Window', ContainerDTC),
    )
