Changeset 516

Show
Ignore:
Timestamp:
04/17/03 01:33:42 (6 years ago)
Author:
sholloway
Message:

Started on letting skin elements trap exceptions
Added hooks to capture XML exceptions

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/RBFoundation/RBFoundation/Utilities.py

    r468 r516  
    8585 
    8686#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     87 
     88def UnpackCallTuple(calltuple): 
     89    """Seperates calltuple into (call, args or (), kw or {}) 
     90 
     91    Nasty shorthand that consistently allows for variable length tuples. 
     92    """ 
     93    return (calltuple[0], # callable 
     94        (calltuple[1:2] or ((),))[0], # args or () 
     95        (calltuple[2:3] or ({},))[0]) # kw or () 
     96 
     97def CallTuple(calltuple): 
     98    """Uses CallTupleUnpack to invoke the calltuple""" 
     99    call, args, kw = CallTupleUnpack(calltuple) 
     100    return call(*args, **kw) 
     101 
     102#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    87103#~ Testing  
    88104#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
  • trunk/RBFoundation/RBFoundation/XMLBuilder.py

    r509 r516  
    4040from WeakBind import BindCallable as _BindCallable 
    4141from XMLNamespaceMap import XMLNamespaceMap 
     42from Utilities import CallTuple 
    4243 
    4344#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     
    7172    _seperator = '.' 
    7273    NamespaceSynonyms = {} 
     74    ElementExceptions = { 
     75        # '__factory__': (), 
     76        # '__init__': (), 
     77        # '_xmlInitStarted': (), 
     78        # '_xmlInitFinalized': (), 
     79        # '_xmlInitComplete': (), 
     80        # '_xmlGetElement': (), 
     81        # '_addElement': (), 
     82        # '_addData': (), 
     83        # None: (), # defaults 
     84        } 
    7385 
    7486    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     
    89101        must be able to accept the same arguments as this method.""" 
    90102        raise KeyError, 'No Class Registered: %s %s' % node 
     103 
     104    def _OnException(self, element, callname): 
     105        """Can be used with ElementExceptions to catch certain execeptions...""" 
     106        #import sys 
     107        #print sys.exc_info()[1] 
     108        #return 1 
    91109 
    92110    def _GetOwner(self): 
     
    135153        args = (self._GetOwner(), self._GetElement(), node, newattributes, self._current_namespacemap) 
    136154        self._current_namespacemap  = self._current_namespacemap.newchain() 
    137         build_factory = self._GetElementFactory(*args) 
    138         newelement = build_factory(*args) 
     155 
     156        try: 
     157            build_factory = self._GetElementFactory(*args) 
     158        except (self.ElementExceptions.get('__factory__', ()) + self.ElementExceptions.get(None, ())), e: 
     159            if not self._OnException(None, '__factory__'): raise 
     160 
     161        try: 
     162            if isinstance(build_factory, tuple): 
     163                newelement = CallTuple(build_factory) 
     164            else: 
     165                newelement = build_factory(*args) 
     166        except: 
     167            if not self._OnException(newelement, '__init__'): raise 
     168 
    139169        if self._elements: 
    140             self._elements[-1]._addElement(node, newelement._xmlGetElement()) 
     170            try:  
     171                self._elements[-1]._addElement(node, newelement._xmlGetElement()) 
     172            except: 
     173                if not self._OnException(self._elements[-1], '_addElement'): raise 
    141174        self._elements.append(newelement) 
    142         self._elements[-1]._xmlInitStarted() 
     175 
     176        try:  
     177            self._elements[-1]._xmlInitStarted() 
     178        except: 
     179            if not self._OnException(self._elements[-1], '_xmlInitStarted'): raise 
    143180 
    144181    def _end_element(self, name): 
     
    149186            try: _xmlInitFinalized = self._elements[-1]._xmlInitFinalized 
    150187            except AttributeError: pass 
    151             else: _xmlInitFinalized() 
     188            else:  
     189                try:  
     190                    _xmlInitFinalized() 
     191                except: 
     192                    if not self._OnException(self._elements[-1], '_xmlInitFinalized'): raise 
    152193 
    153194            element = self._elements.pop() 
    154195            self._current_namespacemap = self._current_namespacemap.nextmap 
    155196 
    156             element._xmlInitComplete() 
     197            try:  
     198                element._xmlInitComplete() 
     199            except: 
     200                if not self._OnException(element, '_xmlInitComplete'): raise 
    157201 
    158202            # Get the appropriate result, if it is overriden 
    159203            try: xmlGetElement = element._xmlGetElement 
    160204            except AttributeError: result = element 
    161             else: result = xmlGetElement() 
     205            else:  
     206                try:  
     207                    result = xmlGetElement() 
     208                except: 
     209                    if not self._OnException(element, '_xmlGetElement'): raise 
    162210        else:  
    163211            result = None 
     
    167215    def _char_data(self, data): 
    168216        """Part of the tree-style template method, called when PCData is found.""" 
    169         self._elements[-1]._addData(data) 
     217        try:  
     218            self._elements[-1]._addData(data) 
     219        except (self.ElementExceptions.get('_addData', ()) + self.ElementExceptions.get(None, ())), e: 
     220            if not self._OnException(self._elements[-1], '_addData'): raise 
    170221 
    171222    def _SplitQualifiedName(self, combined): 
     
    219270        self._elements, self._LastCompleteElement  = [], None 
    220271        parser = self._CreateParser() 
    221         parser.Parse(*args, **kw) 
     272 
     273        self._do_parse(parser.Parse, *args, **kw) 
     274 
    222275        result, self._LastCompleteElement = self._LastCompleteElement, None 
    223276        return result 
     
    227280        self._elements, self._LastCompleteElement  = [], None 
    228281        parser = self._CreateParser() 
    229         parser.ParseFile(*args, **kw) 
     282 
     283        self._do_parse(parser.ParseFile, *args, **kw) 
     284 
    230285        result, self._LastCompleteElement = self._LastCompleteElement, None 
    231286        return result 
     
    239294        self._LastCompleteElement = XMLBuilderMixin._end_element(self, name) 
    240295 
     296    def _do_parse(self, call, *args, **kw): 
     297        try: 
     298            call(*args, **kw) 
     299        except StandardError, err: 
     300            setattr(err, 'xmlsource', getattr(err, 'xmlsource', args[0])) 
     301            raise err 
     302        except: 
     303            import sys 
     304            err = sys.exc_info()[1] 
     305            setattr(err, 'xmlsource', getattr(err, 'xmlsource', args[0])) 
     306            raise err 
  • trunk/RBSkinning/RBSkinning/XMLSkinner.py

    r499 r516  
    7070        self.context.__root__ = rootPath 
    7171         
    72         parser.Parse(xml) 
     72        self._do_parse(parser.Parse, xml) 
    7373 
    7474        self.context = None 
     
    9494        self.context.__root__ = rootPath 
    9595 
    96         parser.ParseFile(file) 
     96        self._do_parse(parser.ParseFile, file) 
    9797 
    9898        self.context = None 
     
    116116        self._LastCompleteElement.context = self.context 
    117117 
    118         parser.Parse(xml) 
     118        self._do_parse(parser.Parse, xml) 
    119119 
    120120        self.context = None 
     
    144144        self._LastCompleteElement.context = self.context 
    145145 
    146         parser.ParseFile(file) 
     146        self._do_parse(parser.ParseFile, file) 
    147147 
    148148        self.context = None 
     
    163163        element.context._update(kwAddedContext) 
    164164 
    165         parser.Parse(xml) 
     165        self._do_parse(parser.Parse, xml) 
    166166 
    167167        return self._LastCompleteElement 
     
    183183        element.context._update(kwAddedContext) 
    184184 
    185         parser.ParseFile(file) 
     185        self._do_parse(parser.ParseFile, file) 
    186186 
    187187        return self._LastCompleteElement 
  • trunk/RBSkinning/RBSkinning/__init__.py

    r423 r516  
    107107    return StandardXMLSkinner().SkinXML(*args, **kw) 
    108108 
     109try: 
     110    from xml.parsers.expat import ExpatError 
     111 
     112    def DebugExpatError(err, printlines=3): 
     113        xmlsource = err.xmlsource 
     114        cur_line = 0 
     115        line = err.lineno 
     116        column = err.offset 
     117 
     118        if isinstance(xmlsource, (str, unicode)): 
     119            import StringIO 
     120            xmlsource = StringIO.StringIO(xmlsource) 
     121        xmlsource.seek(0) 
     122 
     123        print 
     124        print 
     125        print "XML Expat Error at line %s, column %s" % (line, column) 
     126        print 
     127        for xmlline in xmlsource: 
     128            cur_line += 1 
     129            if (line - cur_line) < printlines: 
     130                print '%4d:%s' % (cur_line, xmlline), 
     131            if cur_line == line: 
     132                print "%s%s" % ('='*(5+column), '^') 
     133                break 
     134        print 
     135        print 
     136        return 1 
     137             
     138except ImportError: 
     139    pass 
  • trunk/RBSkinning/RBSkinning/wxTools/wxDockingTools.py

    r504 r516  
    126126#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    127127 
     128class wxDockHost(wxDockHostBase): 
     129    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     130    #~ Constants / Variables / Etc.  
     131    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     132 
     133    prepend = False 
     134    dockcount = 0 
     135    hideempty = False 
     136 
     137    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     138    #~ Public Methods  
     139    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     140 
     141    def __init__(self, parent, layout, rootparent=None, rootlayout=None, prepend=False, hideempty=False): 
     142        self.parent = parent 
     143        self.rootparent = rootparent or parent 
     144        self.layout = layout 
     145        self.rootlayout = rootlayout or layout 
     146        self.prepend = prepend 
     147        self.hideempty = hideempty 
     148        self.dockitemlist = [] 
     149 
     150    def DockItem(self, container, dockitem=None, *args, **kw): 
     151        if dockitem is None: 
     152            container.DockTo(self) 
     153        else: 
     154            return self._DoDockItem(container, dockitem, *args, **kw) 
     155 
     156    def _DoDockItem(self, container, dockitem, *args, **kw): 
     157        self.dockcount += 1 
     158        try: DIReparent = dockitem.Reparent 
     159        except AttributeError: pass 
     160        else: DIReparent(self.parent) 
     161 
     162        if self.prepend: 
     163            self.layout.Prepend(dockitem, *args, **kw) 
     164        else: self.layout.Add(dockitem, *args, **kw) 
     165        self.dockitemlist.append(container) 
     166        self.OnDockItem(self, container, dockitem) 
     167        self.Layout() 
     168 
     169    def UndockItem(self, container, dockitem=None, *args, **kw): 
     170        if dockitem is None: 
     171            if container.IsDocked(self): 
     172                container.Undock() 
     173        else: 
     174            return self._DoUndockItem(container, dockitem, *args, **kw) 
     175 
     176    def _DoUndockItem(self, container, dockitem, *args, **kw): 
     177        self.dockcount -= 1 
     178        ### The following code is not required, but included so  
     179        ### we don't wonder about the asymetry ;) 
     180        ##try: DIReparent = dockitem.Reparent 
     181        ##except AttributeError: pass 
     182        ##else: DIReparent(None) 
     183 
     184        # Disable size hints so the frame can resize when needed 
     185        # We will re-enable in self.Layout 
     186        self.parent.SetSizeHints(0, 0) 
     187        self.layout.Remove(dockitem) 
     188        self.dockitemlist.remove(container) 
     189        self.OnUndockItem(self, container, dockitem) 
     190        self.Layout() 
     191 
     192    def UndockAll(self): 
     193        for dockitem in self.dockitemlist: 
     194            self.UndockItem(dockitem) 
     195 
     196    def Layout(self): 
     197        self.rootlayout.Layout() 
     198        self.layout.Layout() 
     199        self._SetLayoutContainerSizeHints() 
     200 
     201        if self.hideempty: 
     202            if self.dockcount <= 0: 
     203                self.dockcount = 0 
     204                self.parent.Show(False) 
     205            elif not self.parent.IsShown(): 
     206                self.parent.Show(True) 
     207 
     208    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     209    #~ Protected Methods  
     210    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     211 
     212    def _SetLayoutContainerSizeHints(self): 
     213        SetLayoutContainerSizeHints(self.layout, self.parent) 
     214        SetLayoutContainerSizeHints(self.rootlayout, self.rootparent) 
     215 
     216#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     217 
     218class wxNotebookDockHost(wxDockHostBase): 
     219    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     220    #~ Constants / Variables / Etc.  
     221    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     222 
     223    prepend = False 
     224    dockcount = 0 
     225    hideempty = False 
     226    selectpage = True 
     227 
     228    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     229    #~ Definitions  
     230    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     231 
     232    class wxNotebookDockPage(wx.wxPanel): 
     233        def __init__(self, *args, **kw): 
     234            wx.wxPanel.__init__(self, *args, **kw) 
     235            self.dockitem = None 
     236            self.sizer = wx.wxBoxSizer(wx.wxVERTICAL) 
     237            self.SetSizer(self.sizer) 
     238 
     239        def IsDocked(self, dockitem): 
     240            return self.dockitem is dockitem 
     241 
     242        def UndockItem(self, dockitem, *args, **kw): 
     243            if self.dockitem is dockitem: 
     244                self.DockItem(None) 
     245 
     246        def DockItem(self, dockitem, *args, **kw): 
     247            if self.dockitem is not None: 
     248                self.sizer.Remove(self.dockitem) 
     249            self.dockitem = dockitem 
     250            if self.dockitem is not None: 
     251                self.sizer.Add(self.dockitem, *args, **kw) 
     252            self.Layout() 
     253 
     254        def Layout(self): 
     255            self.sizer.Layout() 
     256 
     257    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     258    #~ Public Methods  
     259    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     260 
     261    def __init__(self, notebook, selectpage=True, prepend=False, hideempty=False): 
     262        self.notebook = notebook 
     263        self.selectpage = selectpage 
     264        self.prepend = prepend 
     265        self.hideempty = hideempty 
     266        self._used_pages = [] 
     267        self._available_pages = [] 
     268 
     269    def DockItem(self, container, dockitem=None, *args, **kw): 
     270        if container is None: 
     271            container.DockTo(self) 
     272        else: 
     273            return self._DoDockItem(container, dockitem, *args, **kw) 
     274 
     275    def _DoDockItem(self, container, dockitem, *args, **kw): 
     276        self.dockcount += 1 
     277 
     278        page = self.AcquireNotebookDockPage() 
     279 
     280        try: DIReparent = dockitem.Reparent 
     281        except AttributeError: pass 
     282        else: DIReparent(page) 
     283 
     284        page.DockItem(dockitem, *args, **kw) 
     285 
     286        text = None 
     287        if text is None: 
     288            try: text = container.pagename 
     289            except AttributeError: pass 
     290        if text is None: 
     291            try: text = dockitem.GetLabel() 
     292            except AttributeError: pass 
     293        if text is None: 
     294            text = text or dockitem.__class__.__name__ 
     295 
     296        if self.prepend: 
     297            self.notebook.InsertPage(0, page, text, self.selectpage) 
     298        else: 
     299            self.notebook.AddPage(page, text, self.selectpage) 
     300        page.Show(True) 
     301        self.OnDockItem(self, container, dockitem) 
     302        self.Layout() 
     303 
     304    def UndockItem(self, container, dockitem=None, *args, **kw): 
     305        if container is None: 
     306            if container.IsDocked(self): 
     307                container.Undock() 
     308        else: 
     309            return self._DoUndockItem(container, dockitem, *args, **kw) 
     310 
     311    def _DoUndockItem(self, container, dockitem, *args, **kw): 
     312        self.dockcount -= 1 
     313        ### The following code is not required, but included so  
     314        ### we don't wonder about the asymetry ;) 
     315        ##try: DIReparent = dockitem.Reparent 
     316        ##except AttributeError: pass 
     317        ##else: DIReparent(None) 
     318 
     319        page = self.FindNotebookDockPage(dockitem) 
     320        if page is None: raise KeyError, "Dockitem not found" 
     321        page.UndockItem(dockitem) 
     322 
     323        for idx in range(self.notebook.GetPageCount()): 
     324            if self.notebook.GetPage(idx) is page: 
     325                self.notebook.RemovePage(idx) 
     326                break 
     327 
     328        self.ReleaseNotebookDockPage(page) 
     329        self.OnUndockItem(self, container, dockitem) 
     330        self.Layout() 
     331 
     332    def Layout(self): 
     333        if self.hideempty: 
     334            if self.dockcount <= 0: 
     335                self.dockcount = 0 
     336                self.notebook.Show(False) 
     337            elif not self.notebook.IsShown(): 
     338                self.notebook.Show(True) 
     339 
     340    #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     341 
     342    def AcquireNotebookDockPage(self): 
     343        try: 
     344            result = self._available_pages.pop() 
     345        except IndexError: 
     346            result = self.wxNotebookDockPage(self.notebook, wx.wxNewId()) 
     347            result.Show(False) 
     348        self._used_pages.append(result) 
     349        return result 
     350 
     351    def ReleaseNotebookDockPage(self, page): 
     352        page.Show(False) 
     353        self._used_pages.remove(page) 
     354        self._available_pages.append(page) 
     355 
     356    def FindNotebookDockPage(self, dockitem): 
     357        for page in self._used_pages: 
     358            if page.IsDocked(dockitem): 
     359                return page 
     360 
     361#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     362#~ wxSingleDockHost related definitions               
     363#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
     364 
    128365class wxSingleDockHostMixin(object): 
    129366    """Adapts a multiple DockHost to only accept a single dock container""" 
     
    170407#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    171408 
    172 class wxDockHost(wxDockHostBase): 
    173     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    174     #~ Constants / Variables / Etc.  
    175     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    176  
    177     prepend = False 
    178     dockcount = 0 
    179     hideempty = False 
    180  
    181     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    182     #~ Public Methods  
    183     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    184  
    185     def __init__(self, parent, layout, rootparent=None, rootlayout=None, prepend=False, hideempty=False): 
    186         self.parent = parent 
    187         self.rootparent = rootparent or parent 
    188         self.layout = layout 
    189         self.rootlayout = rootlayout or layout 
    190         self.prepend = prepend 
    191         self.hideempty = hideempty 
    192  
    193     def DockItem(self, container, dockitem=None, *args, **kw): 
    194         if container is None: 
    195             container.DockTo(self) 
    196         else: 
    197             return self._DoDockItem(container, dockitem, *args, **kw) 
    198  
    199     def _DoDockItem(self, container, dockitem, *args, **kw): 
    200         self.dockcount += 1 
    201         try: DIReparent = dockitem.Reparent 
    202         except AttributeError: pass 
    203         else: DIReparent(self.parent) 
    204  
    205         if self.prepend: 
    206             self.layout.Prepend(dockitem, *args, **kw) 
    207         else: self.layout.Add(dockitem, *args, **kw) 
    208         self.OnDockItem(self, container, dockitem) 
    209         self.Layout() 
    210  
    211     def UndockItem(self, container, dockitem=None, *args, **kw): 
    212         if container is None: 
    213             if container.IsDocked(self): 
    214                 container.Undock() 
    215         else: 
    216             return self._DoUndockItem(container, dockitem, *args, **kw) 
    217  
    218     def _DoUndockItem(self, container, dockitem, *args, **kw): 
    219         self.dockcount -= 1 
    220         ### The following code is not required, but included so  
    221         ### we don't wonder about the asymetry ;) 
    222         ##try: DIReparent = dockitem.Reparent 
    223         ##except AttributeError: pass 
    224         ##else: DIReparent(None) 
    225  
    226         # Disable size hints so the frame can resize when needed 
    227         # We will re-enable in self.Layout 
    228         self.parent.SetSizeHints(0, 0) 
    229         self.layout.Remove(dockitem) 
    230         self.OnUndockItem(self, container, dockitem) 
    231         self.Layout() 
    232  
    233     def Layout(self): 
    234         self.rootlayout.Layout() 
    235         self.layout.Layout() 
    236         self._SetLayoutContainerSizeHints() 
    237  
    238         if self.hideempty: 
    239             if self.dockcount <= 0: 
    240                 self.dockcount = 0 
    241                 self.parent.Show(False) 
    242             elif not self.parent.IsShown(): 
    243                 self.parent.Show(True) 
    244  
    245     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    246     #~ Protected Methods  
    247     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    248  
    249     def _SetLayoutContainerSizeHints(self): 
    250         SetLayoutContainerSizeHints(self.layout, self.parent) 
    251         SetLayoutContainerSizeHints(self.rootlayout, self.rootparent) 
    252  
    253 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    254  
    255 class wxSingleDockHost(wxDockHost, wxSingleDockHostMixin): 
    256     pass 
    257  
    258 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    259  
    260 class wxNotebookDockHost(wxDockHostBase): 
    261     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    262     #~ Constants / Variables / Etc.  
    263     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    264  
    265     prepend = False 
    266     dockcount = 0 
    267     hideempty = False 
    268     selectpage = True 
    269  
    270     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    271     #~ Definitions  
    272     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    273  
    274     class wxNotebookDockPage(wx.wxPanel): 
    275         def __init__(self, *args, **kw): 
    276             wx.wxPanel.__init__(self, *args, **kw) 
    277             self.dockitem = None 
    278             self.sizer = wx.wxBoxSizer(wx.wxVERTICAL) 
    279             self.SetSizer(self.sizer) 
    280  
    281         def IsDocked(self, dockitem): 
    282             return self.dockitem is dockitem 
    283  
    284         def UndockItem(self, dockitem, *args, **kw): 
    285             if self.dockitem is dockitem: 
    286                 self.DockItem(None) 
    287  
    288         def DockItem(self, dockitem, *args, **kw): 
    289             if self.dockitem is not None: 
    290                 self.sizer.Remove(self.dockitem) 
    291             self.dockitem = dockitem 
    292             if self.dockitem is not None: 
    293                 self.sizer.Add(self.dockitem, *args, **kw) 
    294             self.Layout() 
    295  
    296         def Layout(self): 
    297             self.sizer.Layout() 
    298  
    299     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    300     #~ Public Methods  
    301     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    302  
    303     def __init__(self, notebook, selectpage=True, prepend=False, hideempty=False): 
    304         self.notebook = notebook 
    305         self.selectpage = selectpage 
    306         self.prepend = prepend 
    307         self.hideempty = hideempty 
    308         self._used_pages = [] 
    309         self._available_pages = [] 
    310  
    311     def DockItem(self, container, dockitem=None, *args, **kw): 
    312         if container is None: 
    313             container.DockTo(self) 
    314         else: 
    315             return self._DoDockItem(container, dockitem, *args, **kw) 
    316  
    317     def _DoDockItem(self, container, dockitem, *args, **kw): 
    318         self.dockcount += 1 
    319  
    320         page = self.AcquireNotebookDockPage() 
    321  
    322         try: DIReparent = dockitem.Reparent 
    323         except AttributeError: pass 
    324         else: DIReparent(page) 
    325  
    326         page.DockItem(dockitem, *args, **kw) 
    327  
    328         text = None 
    329         if text is None: 
    330             try: text = container.pagename 
    331             except AttributeError: pass 
    332         if text is None: 
    333             try: text = dockitem.GetLabel() 
    334             except AttributeError: pass 
    335         if text is None: 
    336             text = text or dockitem.__class__.__name__ 
    337  
    338         if self.prepend: 
    339             self.notebook.InsertPage(0, page, text, self.selectpage) 
    340         else: 
    341             self.notebook.AddPage(page, text, self.selectpage) 
    342         page.Show(True) 
    343         self.OnDockItem(self, container, dockitem) 
    344         self.Layout() 
    345  
    346     def UndockItem(self, container, dockitem=None, *args, **kw): 
    347         if container is None: 
    348             if container.IsDocked(self): 
    349                 container.Undock() 
    350         else: 
    351             return self._DoUndockItem(container, dockitem, *args, **kw) 
    352  
    353     def _DoUndockItem(self, container, dockitem, *args, **kw): 
    354         self.dockcount -= 1 
    355         ### The following code is not required, but included so  
    356         ### we don't wonder about the asymetry ;) 
    357         ##try: DIReparent = dockitem.Reparent 
    358         ##except AttributeError: pass 
    359         ##else: DIReparent(None) 
    360  
    361         page = self.FindNotebookDockPage(dockitem) 
    362         if page is None: raise KeyError, "Dockitem not found" 
    363         page.UndockItem(dockitem) 
    364  
    365         for idx in range(self.notebook.GetPageCount()): 
    366             if self.notebook.GetPage(idx) is page: 
    367                 self.notebook.RemovePage(idx) 
    368                 break 
    369  
    370         self.ReleaseNotebookDockPage(page) 
    371         self.OnUndockItem(self, container, dockitem) 
    372         self.Layout() 
    373  
    374     def Layout(self): 
    375         if self.hideempty: 
    376             if self.dockcount <= 0: 
    377                 self.dockcount = 0 
    378                 self.notebook.Show(False) 
    379             elif not self.notebook.IsShown(): 
    380                 self.notebook.Show(True) 
    381  
    382     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    383  
    384     def AcquireNotebookDockPage(self): 
    385         try: 
    386             result = self._available_pages.pop() 
    387         except IndexError: 
    388             result = self.wxNotebookDockPage(self.notebook, wx.wxNewId()) 
    389             result.Show(False) 
    390         self._used_pages.append(result) 
    391         return result 
    392  
    393     def ReleaseNotebookDockPage(self, page): 
    394         page.Show(False) 
    395         self._used_pages.remove(page) 
    396         self._available_pages.append(page) 
    397  
    398     def FindNotebookDockPage(self, dockitem): 
    399         for page in self._used_pages: 
    400             if page.IsDocked(dockitem): 
    401                 return page 
    402  
    403 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
    404  
    405 class wxSingleNotebookDockHost(wxNotebookDockHost, wxSingleDockHostMixin): 
    406     pass 
    407  
     409class wxSingleDockHost(wxDockHost, wxSingleDockHostMixin): pass 
     410class wxSingleNotebookDockHost(wxNotebookDockHost, wxSingleDockHostMixin): pass 
     411