]> git.saurik.com Git - wxWidgets.git/commitdiff
DocView and ActiveGrid IDE updates from Morgan Hua:
authorRobin Dunn <robin@alldunn.com>
Thu, 20 Apr 2006 06:26:03 +0000 (06:26 +0000)
committerRobin Dunn <robin@alldunn.com>
Thu, 20 Apr 2006 06:26:03 +0000 (06:26 +0000)
    New Features: In Tab-View mode, Ctrl-number will take the user to
    the numbered tab view.  Modified files now show an '*' astrisk in
    the view title.  Debugger framework can now support PHP debugging.
    Not important for python development, but at least that means the
    debugger framework is more generalized.

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@38852 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

41 files changed:
wxPython/samples/ide/activegrid/model/projectmodel.py [new file with mode: 0644]
wxPython/samples/ide/activegrid/tool/AboutDialog.py
wxPython/samples/ide/activegrid/tool/AbstractEditor.py
wxPython/samples/ide/activegrid/tool/CodeEditor.py
wxPython/samples/ide/activegrid/tool/DebuggerService.py
wxPython/samples/ide/activegrid/tool/ExtensionService.py
wxPython/samples/ide/activegrid/tool/FindInDirService.py
wxPython/samples/ide/activegrid/tool/HtmlEditor.py
wxPython/samples/ide/activegrid/tool/IDE.py
wxPython/samples/ide/activegrid/tool/MarkerService.py
wxPython/samples/ide/activegrid/tool/MessageService.py
wxPython/samples/ide/activegrid/tool/OutlineService.py
wxPython/samples/ide/activegrid/tool/PHPDebugger.py [new file with mode: 0644]
wxPython/samples/ide/activegrid/tool/PHPEditor.py
wxPython/samples/ide/activegrid/tool/PerlEditor.py
wxPython/samples/ide/activegrid/tool/ProjectEditor.py
wxPython/samples/ide/activegrid/tool/PythonEditor.py
wxPython/samples/ide/activegrid/tool/STCTextEditor.py
wxPython/samples/ide/activegrid/tool/SVNService.py
wxPython/samples/ide/activegrid/tool/UICommon.py
wxPython/samples/ide/activegrid/tool/Wizard.py
wxPython/samples/ide/activegrid/tool/XmlEditor.py
wxPython/samples/ide/activegrid/tool/bmp_source/activegrid.ico [new file with mode: 0644]
wxPython/samples/ide/activegrid/tool/data/tips.txt
wxPython/samples/ide/activegrid/tool/process.py
wxPython/samples/ide/activegrid/tool/project.py
wxPython/samples/ide/activegrid/util/aglogging.py
wxPython/samples/ide/activegrid/util/appdirs.py
wxPython/samples/ide/activegrid/util/datetimeparser.py [new file with mode: 0644]
wxPython/samples/ide/activegrid/util/fileutils.py
wxPython/samples/ide/activegrid/util/objutils.py
wxPython/samples/ide/activegrid/util/parser.py [new file with mode: 0644]
wxPython/samples/ide/activegrid/util/strutils.py
wxPython/samples/ide/activegrid/util/sysutils.py
wxPython/samples/ide/activegrid/util/utillang.py [new file with mode: 0644]
wxPython/samples/ide/activegrid/util/xmlmarshaller.py
wxPython/samples/ide/activegrid/util/xmlutils.py
wxPython/samples/pydocview/FindService.py
wxPython/samples/pydocview/TextEditor.py
wxPython/wx/lib/docview.py
wxPython/wx/lib/pydocview.py

diff --git a/wxPython/samples/ide/activegrid/model/projectmodel.py b/wxPython/samples/ide/activegrid/model/projectmodel.py
new file mode 100644 (file)
index 0000000..aafd8b8
--- /dev/null
@@ -0,0 +1,20 @@
+#----------------------------------------------------------------------------
+# Name:         projectmodel.py
+# Purpose:      This file contains project model information
+#
+# Author:       Morgan Hua
+#
+# Created:      4/18/06
+# CVS-ID:       $Id$
+# Copyright:    (c) 2006 ActiveGrid, Inc.
+# License:      wxWindows License
+#----------------------------------------------------------------------------
+
+
+#----------------------------------------------------------------------------
+# Constants
+#----------------------------------------------------------------------------
+LANGUAGE_PYTHON = "python"
+LANGUAGE_PHP = "php"
+LANGUAGE_DEFAULT = LANGUAGE_PYTHON
+LANGUAGE_LIST = [LANGUAGE_PHP, LANGUAGE_PYTHON]
index 53a00402cab21ea41e0ccc5da6771b40392fe66b..c86043d91dc9d39d69a7534b56e023000ae7908b 100644 (file)
@@ -5,7 +5,7 @@
 # Author:       Morgan Hua
 #
 # Created:      3/22/05
 # Author:       Morgan Hua
 #
 # Created:      3/22/05
-# Copyright:    (c) 2005 ActiveGrid, Inc.
+# Copyright:    (c) 2005-2006 ActiveGrid, Inc.
 # CVS-ID:       $Id$
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 # CVS-ID:       $Id$
 # License:      wxWindows License
 #----------------------------------------------------------------------------
@@ -30,23 +30,25 @@ licenseData = [  # add licenses for base IDE features
     ("Python 2.4", "Python Software Foundation License", "http://www.python.org/2.4/license.html"),
     ("wxPython 2.6", "wxWidgets 2 - LGPL", "http://wxwidgets.org/newlicen.htm"),
     ("wxWidgets", "wxWindows Library License 3", "http://www.wxwidgets.org/manuals/2.6.1/wx_wxlicense.html"),
     ("Python 2.4", "Python Software Foundation License", "http://www.python.org/2.4/license.html"),
     ("wxPython 2.6", "wxWidgets 2 - LGPL", "http://wxwidgets.org/newlicen.htm"),
     ("wxWidgets", "wxWindows Library License 3", "http://www.wxwidgets.org/manuals/2.6.1/wx_wxlicense.html"),
-    ("pychecker", "MetaSlash - BSD", "http://pychecker.sourceforge.net/COPYRIGHT"), 
+    ("pychecker", "MetaSlash - BSD", "http://pychecker.sourceforge.net/COPYRIGHT"),
     ("process.py", "See file", "http://starship.python.net/~tmick/"),
     ("pysvn", "Apache License, Version 2.0", "http://pysvn.tigris.org/"),
 ]
 
 if not ACTIVEGRID_BASE_IDE:    # add licenses for non-base IDE features such as database connections
     licenseData += [
     ("process.py", "See file", "http://starship.python.net/~tmick/"),
     ("pysvn", "Apache License, Version 2.0", "http://pysvn.tigris.org/"),
 ]
 
 if not ACTIVEGRID_BASE_IDE:    # add licenses for non-base IDE features such as database connections
     licenseData += [
-        ("pydb2", "LGPL", "http://sourceforge.net/projects/pydb2"), 
+        ("pydb2", "LGPL", "http://sourceforge.net/projects/pydb2"),
         ("pysqlite", "Python License (CNRI)", "http://sourceforge.net/projects/pysqlite"),
         ("pysqlite", "Python License (CNRI)", "http://sourceforge.net/projects/pysqlite"),
-        ("mysql-python", "GPL, Python License (CNRI), Zope Public License", "http://sourceforge.net/projects/mysql-python"), 
-        ("cx_Oracle", "Computronix", "http://www.computronix.com/download/License(cxOracle).txt"), 
+        ("mysql-python", "GPL, Python License (CNRI), Zope Public License", "http://sourceforge.net/projects/mysql-python"),
+        ("cx_Oracle", "Computronix", "http://www.computronix.com/download/License(cxOracle).txt"),
         ("SQLite", "Public Domain", "http://www.sqlite.org/copyright.html"),
         ("PyGreSQL", "BSD", "http://www.pygresql.org"),
         ("pyXML", "CNRI Python License", "http://sourceforge.net/softwaremap/trove_list.php?form_cat=194"),
         ("Zolera Soap Infrastructure", "Zope Public License 2.0", "http://www.zope.org/Resources/License/"),
         ("SQLite", "Public Domain", "http://www.sqlite.org/copyright.html"),
         ("PyGreSQL", "BSD", "http://www.pygresql.org"),
         ("pyXML", "CNRI Python License", "http://sourceforge.net/softwaremap/trove_list.php?form_cat=194"),
         ("Zolera Soap Infrastructure", "Zope Public License 2.0", "http://www.zope.org/Resources/License/"),
+        ("python-ldap", "Python Software Foundation License", "http://python-ldap.sourceforge.net"),
         ("Sarissa", "LGPL", "http://sourceforge.net/projects/sarissa/"),
         ("Dynarch DHTML Calendar", "LGPL", "http://www.dynarch.com/projects/calendar/"),
         ("Sarissa", "LGPL", "http://sourceforge.net/projects/sarissa/"),
         ("Dynarch DHTML Calendar", "LGPL", "http://www.dynarch.com/projects/calendar/"),
+        ("python-dateutil", "Python Software Foundation License", "http://labix.org/python-dateutil"),        
     ]
 
 if wx.Platform == '__WXMSW__':  # add Windows only licenses
     ]
 
 if wx.Platform == '__WXMSW__':  # add Windows only licenses
@@ -70,7 +72,7 @@ class AboutDialog(wx.Dialog):
         else:
             splash_bmp = getIDESplashBitmap()
 
         else:
             splash_bmp = getIDESplashBitmap()
 
-        # find version number from 
+        # find version number from
         versionFilepath = os.path.join(sysutilslib.mainModuleDir, "version.txt")
         if os.path.exists(versionFilepath):
             versionfile = open(versionFilepath, 'r')
         versionFilepath = os.path.join(sysutilslib.mainModuleDir, "version.txt")
         if os.path.exists(versionFilepath):
             versionfile = open(versionFilepath, 'r')
@@ -82,7 +84,7 @@ class AboutDialog(wx.Dialog):
 
         image = wx.StaticBitmap(aboutPage, -1, splash_bmp, (0,0), (splash_bmp.GetWidth(), splash_bmp.GetHeight()))
         sizer.Add(image, 0, wx.ALIGN_CENTER|wx.ALL, 0)
 
         image = wx.StaticBitmap(aboutPage, -1, splash_bmp, (0,0), (splash_bmp.GetWidth(), splash_bmp.GetHeight()))
         sizer.Add(image, 0, wx.ALIGN_CENTER|wx.ALL, 0)
-        sizer.Add(wx.StaticText(aboutPage, -1, wx.GetApp().GetAppName() + _("\n%s\n\nCopyright (c) 2003-2005 ActiveGrid Incorporated and Contributors.  All rights reserved.") % version), 0, wx.ALIGN_LEFT|wx.ALL, 10)
+        sizer.Add(wx.StaticText(aboutPage, -1, wx.GetApp().GetAppName() + _("\n%s\n\nCopyright (c) 2003-2006 ActiveGrid Incorporated and Contributors.  All rights reserved.") % version), 0, wx.ALIGN_LEFT|wx.ALL, 10)
         sizer.Add(wx.StaticText(aboutPage, -1, _("http://www.activegrid.com")), 0, wx.ALIGN_LEFT|wx.LEFT|wx.BOTTOM, 10)
         aboutPage.SetSizer(sizer)
         nb.AddPage(aboutPage, _("Copyright"))
         sizer.Add(wx.StaticText(aboutPage, -1, _("http://www.activegrid.com")), 0, wx.ALIGN_LEFT|wx.LEFT|wx.BOTTOM, 10)
         aboutPage.SetSizer(sizer)
         nb.AddPage(aboutPage, _("Copyright"))
@@ -90,15 +92,14 @@ class AboutDialog(wx.Dialog):
         licensePage = wx.Panel(nb, -1)
         grid = wx.grid.Grid(licensePage, -1)
         grid.CreateGrid(len(licenseData), 2)
         licensePage = wx.Panel(nb, -1)
         grid = wx.grid.Grid(licensePage, -1)
         grid.CreateGrid(len(licenseData), 2)
-        
+
         dc = wx.ClientDC(grid)
         dc.SetFont(grid.GetLabelFont())
         grid.SetColLabelValue(0, _("License"))
         grid.SetColLabelValue(1, _("URL"))
         dc = wx.ClientDC(grid)
         dc.SetFont(grid.GetLabelFont())
         grid.SetColLabelValue(0, _("License"))
         grid.SetColLabelValue(1, _("URL"))
-        w, maxHeight = dc.GetTextExtent(_("License"))
-        w, h = dc.GetTextExtent(_("URL"))
-        if h > maxHeight:
-            maxHeight = h
+        w, h1 = dc.GetTextExtent(_("License"))
+        w, h2 = dc.GetTextExtent(_("URL"))
+        maxHeight = max(h1, h2)
         grid.SetColLabelSize(maxHeight + 6)  # add a 6 pixel margin
 
         maxW = 0
         grid.SetColLabelSize(maxHeight + 6)  # add a 6 pixel margin
 
         maxW = 0
@@ -115,7 +116,7 @@ class AboutDialog(wx.Dialog):
                 grid.SetCellValue(row, 0, license)
             if url:
                 grid.SetCellValue(row, 1, url)
                 grid.SetCellValue(row, 0, license)
             if url:
                 grid.SetCellValue(row, 1, url)
-        
+
         grid.EnableEditing(False)
         grid.EnableDragGridSize(False)
         grid.EnableDragColSize(False)
         grid.EnableEditing(False)
         grid.EnableDragGridSize(False)
         grid.EnableDragColSize(False)
@@ -132,10 +133,10 @@ class AboutDialog(wx.Dialog):
 
         creditsPage = wx.Panel(nb, -1)
         sizer = wx.BoxSizer(wx.VERTICAL)
 
         creditsPage = wx.Panel(nb, -1)
         sizer = wx.BoxSizer(wx.VERTICAL)
-        sizer.Add(wx.StaticText(creditsPage, -1, _("ActiveGrid Development Team:\n\nLarry Abrahams\nLawrence Bruhmuller\nEric Chu\nBeth Fryer\nMatt Fryer\nJoel Hare\nMorgan Hua\nMatt McNulty\nPratik Mehta\nAlan Mullendore\nJeff Norton\nSimon Toens\nKevin Wang\nPeter Yared")), 0, wx.ALIGN_LEFT|wx.ALL, 10)
+        sizer.Add(wx.StaticText(creditsPage, -1, _("ActiveGrid Development Team:\n\nLarry Abrahams\nLawrence Bruhmuller\nEric Chu\nBeth Fryer\nMatt Fryer\nFrankie Fu\nJoel Hare\nMorgan Hua\nMatt McNulty\nPratik Mehta\nAlan Mullendore\nJeff Norton\nKevin Ollivier\nMatt Small\nSimon Toens\nKevin Wang\nPeter Yared\nJeremy Yun")), 0, wx.ALIGN_LEFT|wx.ALL, 10)
         creditsPage.SetSizer(sizer)
         nb.AddPage(creditsPage, _("Credits"))
         creditsPage.SetSizer(sizer)
         nb.AddPage(creditsPage, _("Credits"))
-            
+
         sizer = wx.BoxSizer(wx.VERTICAL)
         sizer.Add(nb, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
         btn = wx.Button(self, wx.ID_OK)
         sizer = wx.BoxSizer(wx.VERTICAL)
         sizer.Add(nb, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
         btn = wx.Button(self, wx.ID_OK)
@@ -145,5 +146,5 @@ class AboutDialog(wx.Dialog):
         self.Layout()
         self.Fit()
         grid.ForceRefresh()  # wxBug: Get rid of unnecessary scrollbars
         self.Layout()
         self.Fit()
         grid.ForceRefresh()  # wxBug: Get rid of unnecessary scrollbars
-        
+
 
 
index f795ecd6f8e10ed94b8dd72b527644321e4a132d..8a2e9c5232fac8ac742e774344fb87151f1adb62 100644 (file)
@@ -36,6 +36,7 @@ PARKING_VERTICAL = 1
 PARKING_HORIZONTAL = 2
 PARKING_OFFSET = 30    # space between shapes
 
 PARKING_HORIZONTAL = 2
 PARKING_OFFSET = 30    # space between shapes
 
+FORCE_REDRAW_METHOD = "ForceRedraw"
 
 def GetRawModel(model):
     if hasattr(model, "GetRawModel"):
 
 def GetRawModel(model):
     if hasattr(model, "GetRawModel"):
@@ -85,6 +86,7 @@ class CanvasView(wx.lib.docview.View):
         self._propShape = None
         self._maxWidth = 2000
         self._maxHeight = 16000
         self._propShape = None
         self._maxWidth = 2000
         self._maxHeight = 16000
+        self._valetParking = False
 
 
     def OnDraw(self, dc):
 
 
     def OnDraw(self, dc):
@@ -195,6 +197,16 @@ class CanvasView(wx.lib.docview.View):
         self.SetPropertyModel(None)
                   
 
         self.SetPropertyModel(None)
                   
 
+    def SetLastRightClick(self, x, y):
+        self._lastRightClick = (x,y)
+        
+
+    def GetLastRightClick(self):
+        if hasattr(self, "_lastRightClick"):
+            return self._lastRightClick
+        return (-1,-1)
+        
+
     def OnKeyPressed(self, event):
         key = event.KeyCode()
         if key == wx.WXK_DELETE:
     def OnKeyPressed(self, event):
         key = event.KeyCode()
         if key == wx.WXK_DELETE:
@@ -211,6 +223,7 @@ class CanvasView(wx.lib.docview.View):
         dc = wx.ClientDC(self._canvas)
         self._canvas.PrepareDC(dc)
         x, y = event.GetLogicalPosition(dc)  # this takes into account scrollbar offset
         dc = wx.ClientDC(self._canvas)
         self._canvas.PrepareDC(dc)
         x, y = event.GetLogicalPosition(dc)  # this takes into account scrollbar offset
+        self.SetLastRightClick(x, y)
         shape = self._canvas.FindShape(x, y)[0]
         
         model = None
         shape = self._canvas.FindShape(x, y)[0]
         
         model = None
@@ -260,12 +273,15 @@ class CanvasView(wx.lib.docview.View):
             pass
         else:
             # click on empty part of canvas, deselect everything
             pass
         else:
             # click on empty part of canvas, deselect everything
+            forceRedrawShapes = []
             needRefresh = False
             for shape in self._diagram.GetShapeList():
                 if hasattr(shape, "GetModel"):
                     if shape.Selected():
                         needRefresh = True
                         shape.Select(False, dc)
             needRefresh = False
             for shape in self._diagram.GetShapeList():
                 if hasattr(shape, "GetModel"):
                     if shape.Selected():
                         needRefresh = True
                         shape.Select(False, dc)
+                        if hasattr(shape, FORCE_REDRAW_METHOD):
+                            forceRedrawShapes.append(shape)
             if needRefresh:
                 self._canvas.Redraw(dc)
 
             if needRefresh:
                 self._canvas.Redraw(dc)
 
@@ -274,7 +290,8 @@ class CanvasView(wx.lib.docview.View):
         if len(self.GetSelection()) == 0:
             self.SetPropertyShape(None)
 
         if len(self.GetSelection()) == 0:
             self.SetPropertyShape(None)
 
-
+        for shape in forceRedrawShapes:
+            shape.ForceRedraw()
 
     def OnLeftDoubleClick(self, event):
         propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
 
     def OnLeftDoubleClick(self, event):
         propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
@@ -401,8 +418,20 @@ class CanvasView(wx.lib.docview.View):
         dc.EndDrawing()
 
 
         dc.EndDrawing()
 
 
+    def SetValetParking(self, enable=True):
+        """ If valet parking is enabled, remember last parking spot and try for a spot near it """
+        self._valetParking = enable
+        if enable:
+            self._valetPosition = None
+        
+
     def FindParkingSpot(self, width, height, parking=PARKING_HORIZONTAL, x=PARKING_OFFSET, y=PARKING_OFFSET):
     def FindParkingSpot(self, width, height, parking=PARKING_HORIZONTAL, x=PARKING_OFFSET, y=PARKING_OFFSET):
-        """ given a width and height, find a upper left corner where shape can be parked without overlapping other shape """
+        """
+            Given a width and height, find a upper left corner where shape can be parked without overlapping other shape
+        """
+        if self._valetParking and self._valetPosition:
+            x, y = self._valetPosition
+        
         max = 700  # max distance to the right where we'll place tables
         noParkingSpot = True
 
         max = 700  # max distance to the right where we'll place tables
         noParkingSpot = True
 
@@ -422,6 +451,9 @@ class CanvasView(wx.lib.docview.View):
             else:
                 noParkingSpot = False
 
             else:
                 noParkingSpot = False
 
+        if self._valetParking:
+            self._valetPosition = (x, y)
+            
         return x, y
 
 
         return x, y
 
 
@@ -518,7 +550,8 @@ class CanvasView(wx.lib.docview.View):
                         self._diagram.RemoveShape(line)
                         line.Delete()
                     
                         self._diagram.RemoveShape(line)
                         line.Delete()
                     
-            shape.RemoveFromCanvas(self._canvas)
+            if self._canvas:
+                shape.RemoveFromCanvas(self._canvas)
             self._diagram.RemoveShape(shape)
             shape.Delete()
 
             self._diagram.RemoveShape(shape)
             shape.Delete()
 
@@ -698,6 +731,9 @@ class CanvasView(wx.lib.docview.View):
                 self._propShape.SetTextColour("WHITE", 0)
             self._propShape.Draw(dc)
 
                 self._propShape.SetTextColour("WHITE", 0)
             self._propShape.Draw(dc)
 
+            if hasattr(self._propShape, FORCE_REDRAW_METHOD):
+                self._propShape.ForceRedraw()
+
         dc.EndDrawing()
 
 
         dc.EndDrawing()
 
 
index 7e245bcd1f79945232fb2fc114bda740d173b48f..72a5dd2c4f57cb16d2b8475db808c4286088b55f 100644 (file)
@@ -19,7 +19,6 @@ import os
 import re
 import string
 import sys
 import re
 import string
 import sys
-import DebuggerService
 import MarkerService
 from UICommon import CaseInsensitiveCompare
 _ = wx.GetTranslation
 import MarkerService
 from UICommon import CaseInsensitiveCompare
 _ = wx.GetTranslation
@@ -120,16 +119,27 @@ class CodeView(STCTextEditor.TextView):
             return False
         id = event.GetId()
         if id == EXPAND_TEXT_ID:
             return False
         id = event.GetId()
         if id == EXPAND_TEXT_ID:
-            event.Enable(self.GetCtrl().CanLineExpand(self.GetCtrl().GetCurrentLine()))
+            if self.GetCtrl().GetViewFolding():
+                event.Enable(self.GetCtrl().CanLineExpand(self.GetCtrl().GetCurrentLine()))
+            else:
+                event.Enable(False)
             return True
         elif id == COLLAPSE_TEXT_ID:
             return True
         elif id == COLLAPSE_TEXT_ID:
-            event.Enable(self.GetCtrl().CanLineCollapse(self.GetCtrl().GetCurrentLine()))
+            if self.GetCtrl().GetViewFolding():
+                event.Enable(self.GetCtrl().CanLineCollapse(self.GetCtrl().GetCurrentLine()))
+            else:
+                event.Enable(False)
             return True
         elif (id == EXPAND_TOP_ID
         or id == COLLAPSE_TOP_ID
         or id == EXPAND_ALL_ID
             return True
         elif (id == EXPAND_TOP_ID
         or id == COLLAPSE_TOP_ID
         or id == EXPAND_ALL_ID
-        or id == COLLAPSE_ALL_ID
-        or id == AUTO_COMPLETE_ID
+        or id == COLLAPSE_ALL_ID):
+            if self.GetCtrl().GetViewFolding():
+                event.Enable(self.GetCtrl().GetTextLength() > 0)
+            else:
+                event.Enable(False)
+            return True            
+        elif (id == AUTO_COMPLETE_ID
         or id == CLEAN_WHITESPACE
         or id == INDENT_LINES_ID
         or id == DEDENT_LINES_ID
         or id == CLEAN_WHITESPACE
         or id == INDENT_LINES_ID
         or id == DEDENT_LINES_ID
@@ -140,10 +150,12 @@ class CodeView(STCTextEditor.TextView):
         elif id == CHECK_CODE_ID:
             event.Enable(False)
             return True
         elif id == CHECK_CODE_ID:
             event.Enable(False)
             return True
-        elif (id == SET_INDENT_WIDTH_ID
-        or id == FOLDING_ID):
+        elif id == SET_INDENT_WIDTH_ID:
             event.Enable(True)
             return True
             event.Enable(True)
             return True
+        elif id == FOLDING_ID:
+            event.Enable(self.GetCtrl().GetViewFolding())
+            return True
         elif id == USE_TABS_ID:
             event.Enable(True)
             event.Check(self.GetCtrl().GetUseTabs())
         elif id == USE_TABS_ID:
             event.Enable(True)
             event.Check(self.GetCtrl().GetUseTabs())
@@ -210,7 +222,7 @@ class CodeView(STCTextEditor.TextView):
         filename = document.GetFilename()
         if filename:
             rootItem = treeCtrl.AddRoot(os.path.basename(filename))
         filename = document.GetFilename()
         if filename:
             rootItem = treeCtrl.AddRoot(os.path.basename(filename))
-            treeCtrl.SetDoSelectCallback(rootItem, self, None)
+            treeCtrl.SetDoSelectCallback(rootItem, self, (0,0))
         else:
             return True
 
         else:
             return True
 
@@ -232,11 +244,13 @@ class CodeView(STCTextEditor.TextView):
             if classLine:
                 indent = classLine.start(0)
                 itemStr = classLine.string[classLine.start(0):classLine.end(0)-1]  # don't take the closing ':'
             if classLine:
                 indent = classLine.start(0)
                 itemStr = classLine.string[classLine.start(0):classLine.end(0)-1]  # don't take the closing ':'
+                itemStr = itemStr.replace("\n", "").replace("\r", "").replace(",\\", ",").replace("  ", "")  # remove line continuations and spaces from outline view
             else:
                 defLine = defPat.search(line)
                 if defLine:
                     indent = defLine.start(0)
                     itemStr = defLine.string[defLine.start(0):defLine.end(0)]
             else:
                 defLine = defPat.search(line)
                 if defLine:
                     indent = defLine.start(0)
                     itemStr = defLine.string[defLine.start(0):defLine.end(0)]
+                    itemStr = itemStr.replace("\n", "").replace("\r", "").replace(",\\", ",").replace("  ", "")  # remove line continuations and spaces from outline view
 
             if indent == 0:
                 parentItem = rootItem
 
             if indent == 0:
                 parentItem = rootItem
@@ -467,6 +481,9 @@ class CodeView(STCTextEditor.TextView):
 
 
     def OnUpdate(self, sender = None, hint = None):
 
 
     def OnUpdate(self, sender = None, hint = None):
+        if wx.lib.docview.View.OnUpdate(self, sender, hint):
+            return
+
         if hint == "ViewStuff":
             self.GetCtrl().SetViewDefaults()
         elif hint == "Font":
         if hint == "ViewStuff":
             self.GetCtrl().SetViewDefaults()
         elif hint == "Font":
@@ -474,6 +491,7 @@ class CodeView(STCTextEditor.TextView):
             self.GetCtrl().SetFont(font)
             self.GetCtrl().SetFontColor(color)
         else:
             self.GetCtrl().SetFont(font)
             self.GetCtrl().SetFontColor(color)
         else:
+            import DebuggerService
             dbg_service = wx.GetApp().GetService(DebuggerService.DebuggerService)
             if dbg_service:
                 dbg_service.SetCurrentBreakpointMarkers(self)
             dbg_service = wx.GetApp().GetService(DebuggerService.DebuggerService)
             if dbg_service:
                 dbg_service.SetCurrentBreakpointMarkers(self)
@@ -623,7 +641,7 @@ class CodeCtrl(STCTextEditor.TextCtrl):
     BREAKPOINT_MARKER_MASK = 0x2
     
             
     BREAKPOINT_MARKER_MASK = 0x2
     
             
-    def __init__(self, parent, id=-1, style = wx.NO_FULL_REPAINT_ON_RESIZE):
+    def __init__(self, parent, id=-1, style = wx.NO_FULL_REPAINT_ON_RESIZE, clearTab=True):
         STCTextEditor.TextCtrl.__init__(self, parent, id, style)
         
         self.UsePopUp(False)
         STCTextEditor.TextCtrl.__init__(self, parent, id, style)
         
         self.UsePopUp(False)
@@ -635,7 +653,6 @@ class CodeCtrl(STCTextEditor.TextCtrl):
         self.SetMarginType(2, wx.stc.STC_MARGIN_SYMBOL)
         self.SetMarginMask(2, wx.stc.STC_MASK_FOLDERS)
         self.SetMarginSensitive(2, True)
         self.SetMarginType(2, wx.stc.STC_MARGIN_SYMBOL)
         self.SetMarginMask(2, wx.stc.STC_MASK_FOLDERS)
         self.SetMarginSensitive(2, True)
-        self.SetMarginWidth(2, 12)
         
         self.SetMarginSensitive(1, False)
         self.SetMarginMask(1, 0x4)
         
         self.SetMarginSensitive(1, False)
         self.SetMarginMask(1, 0x4)
@@ -657,7 +674,7 @@ class CodeCtrl(STCTextEditor.TextCtrl):
         # Define the breakpoint marker
         self.MarkerDefine(CodeCtrl.BREAKPOINT_MARKER_NUM, wx.stc.STC_MARK_CIRCLE, wx.BLACK, (255,0,0))
         
         # Define the breakpoint marker
         self.MarkerDefine(CodeCtrl.BREAKPOINT_MARKER_NUM, wx.stc.STC_MARK_CIRCLE, wx.BLACK, (255,0,0))
         
-        if _WINDOWS:  # should test to see if menu item exists, if it does, add this workaround
+        if _WINDOWS and clearTab:  # should test to see if menu item exists, if it does, add this workaround
             self.CmdKeyClear(wx.stc.STC_KEY_TAB, 0)  # menu item "Indent Lines" from CodeService.InstallControls() generates another INDENT_LINES_ID event, so we'll explicitly disable the tab processing in the editor
 
         wx.stc.EVT_STC_MARGINCLICK(self, self.GetId(), self.OnMarginClick)
             self.CmdKeyClear(wx.stc.STC_KEY_TAB, 0)  # menu item "Indent Lines" from CodeService.InstallControls() generates another INDENT_LINES_ID event, so we'll explicitly disable the tab processing in the editor
 
         wx.stc.EVT_STC_MARGINCLICK(self, self.GetId(), self.OnMarginClick)
@@ -693,7 +710,7 @@ class CodeCtrl(STCTextEditor.TextCtrl):
         item = wx.MenuItem(menu, TOGGLEBREAKPOINT_ID, _("Toggle Breakpoint"))
         menu.AppendItem(item)
         self.Bind(wx.EVT_MENU, self.OnPopToggleMarker, id=TOGGLEMARKER_ID)
         item = wx.MenuItem(menu, TOGGLEBREAKPOINT_ID, _("Toggle Breakpoint"))
         menu.AppendItem(item)
         self.Bind(wx.EVT_MENU, self.OnPopToggleMarker, id=TOGGLEMARKER_ID)
-        item = wx.MenuItem(menu, TOGGLEMARKER_ID, _("Toggle Marker"))
+        item = wx.MenuItem(menu, TOGGLEMARKER_ID, _("Toggle Bookmark"))
         menu.AppendItem(item)
         menu.AppendSeparator()
                 
         menu.AppendItem(item)
         menu.AppendSeparator()
                 
@@ -715,6 +732,7 @@ class CodeCtrl(STCTextEditor.TextCtrl):
 
     def OnPopToggleBP(self, event):
         """ Toggle break point on right click line, not current line """
 
     def OnPopToggleBP(self, event):
         """ Toggle break point on right click line, not current line """
+        import DebuggerService
         wx.GetApp().GetService(DebuggerService.DebuggerService).OnToggleBreakpoint(event, line=self._rightClickLine)
       
   
         wx.GetApp().GetService(DebuggerService.DebuggerService).OnToggleBreakpoint(event, line=self._rightClickLine)
       
   
@@ -859,6 +877,7 @@ class CodeCtrl(STCTextEditor.TextCtrl):
 
         elif evt.GetMargin() == 0:
             #This is used to toggle breakpoints via the debugger service.
 
         elif evt.GetMargin() == 0:
             #This is used to toggle breakpoints via the debugger service.
+            import DebuggerService
             db_service = wx.GetApp().GetService(DebuggerService.DebuggerService)
             if db_service:
                 db_service.OnToggleBreakpoint(evt, line=self.LineFromPosition(evt.GetPosition()))
             db_service = wx.GetApp().GetService(DebuggerService.DebuggerService)
             if db_service:
                 db_service.OnToggleBreakpoint(evt, line=self.LineFromPosition(evt.GetPosition()))
index 485853611bfbcc7d169bbcabcefb70a81d26ba4c..67ed47fac58ffdc1fbc19e9ec20674e2db2e92df 100644 (file)
@@ -1,6 +1,6 @@
 #----------------------------------------------------------------------------
 # Name:         DebuggerService.py
 #----------------------------------------------------------------------------
 # Name:         DebuggerService.py
-# Purpose:      Debugger Service for Python.
+# Purpose:      Debugger Service for Python and PHP
 #
 # Author:       Matt Fryer
 #
 #
 # Author:       Matt Fryer
 #
@@ -21,6 +21,9 @@ import Service
 import STCTextEditor
 import CodeEditor
 import PythonEditor
 import STCTextEditor
 import CodeEditor
 import PythonEditor
+import PHPEditor
+import PHPDebugger
+import activegrid.model.projectmodel as projectmodel
 from IDE import ACTIVEGRID_BASE_IDE
 if not ACTIVEGRID_BASE_IDE:
     import ProcessModelEditor
 from IDE import ACTIVEGRID_BASE_IDE
 if not ACTIVEGRID_BASE_IDE:
     import ProcessModelEditor
@@ -42,6 +45,10 @@ import DebuggerHarness
 import traceback
 import StringIO
 import UICommon
 import traceback
 import StringIO
 import UICommon
+import activegrid.util.sysutils as sysutilslib
+import subprocess
+import shutil
+
 if wx.Platform == '__WXMSW__':
     try:
         import win32api
 if wx.Platform == '__WXMSW__':
     try:
         import win32api
@@ -60,6 +67,11 @@ _ = wx.GetTranslation
 _VERBOSE = False
 _WATCHES_ON = False
 
 _VERBOSE = False
 _WATCHES_ON = False
 
+import  wx.lib.newevent
+(UpdateTextEvent, EVT_UPDATE_STDTEXT) = wx.lib.newevent.NewEvent()
+(UpdateErrorEvent, EVT_UPDATE_ERRTEXT) = wx.lib.newevent.NewEvent()
+(DebugInternalWebServer, EVT_DEBUG_INTERNAL) = wx.lib.newevent.NewEvent()
+
 # Class to read from stdout or stderr and write the result to a text control.
 # Args: file=file-like object
 #       callback_function= function that takes a single argument, the line of text
 # Class to read from stdout or stderr and write the result to a text control.
 # Args: file=file-like object
 #       callback_function= function that takes a single argument, the line of text
@@ -74,11 +86,11 @@ class OutputReaderThread(threading.Thread):
         self._accumulate = accumulate
         self._callbackOnExit = callbackOnExit
         self.setDaemon(True)
         self._accumulate = accumulate
         self._callbackOnExit = callbackOnExit
         self.setDaemon(True)
-    
+
     def __del__(self):
     def __del__(self):
-        # See comment on DebugCommandUI.StopExecution
+        # See comment on PythonDebuggerUI.StopExecution
         self._keepGoing = False
         self._keepGoing = False
-             
+
     def run(self):
         file = self._file
         start = time.time()
     def run(self):
         file = self._file
         start = time.time()
@@ -95,20 +107,20 @@ class OutputReaderThread(threading.Thread):
                     # Should use a buffer? StringIO?
                     output += text
                 # Seems as though the read blocks if we got an error, so, to be
                     # Should use a buffer? StringIO?
                     output += text
                 # Seems as though the read blocks if we got an error, so, to be
-                # sure that at least some of the exception gets printed, always 
+                # sure that at least some of the exception gets printed, always
                 # send the first hundred lines back as they come in.
                 # send the first hundred lines back as they come in.
-                if self._lineCount < 100 and self._keepGoing:    
+                if self._lineCount < 100 and self._keepGoing:
                     self._callback_function(output)
                     self._lineCount += 1
                     self._callback_function(output)
                     self._lineCount += 1
-                    output = "" 
+                    output = ""
                 elif time.time() - start > 0.25 and self._keepGoing:
                     try:
                 elif time.time() - start > 0.25 and self._keepGoing:
                     try:
-                        self._callback_function(output) 
+                        self._callback_function(output)
                     except wx._core.PyDeadObjectError:
                         # GUI was killed while we were blocked.
                         self._keepGoing = False
                     start = time.time()
                     except wx._core.PyDeadObjectError:
                         # GUI was killed while we were blocked.
                         self._keepGoing = False
                     start = time.time()
-                    output = "" 
+                    output = ""
             #except TypeError:
             #    pass
             except:
             #except TypeError:
             #    pass
             except:
@@ -121,32 +133,61 @@ class OutputReaderThread(threading.Thread):
             except wx._core.PyDeadObjectError:
                 pass
         if _VERBOSE: print "Exiting OutputReaderThread"
             except wx._core.PyDeadObjectError:
                 pass
         if _VERBOSE: print "Exiting OutputReaderThread"
-        
+
     def AskToStop(self):
         self._keepGoing = False
     def AskToStop(self):
         self._keepGoing = False
-                
-import  wx.lib.newevent
-(UpdateTextEvent, EVT_UPDATE_STDTEXT) = wx.lib.newevent.NewEvent()
-(UpdateErrorEvent, EVT_UPDATE_ERRTEXT) = wx.lib.newevent.NewEvent()
-        
+
+
 class Executor:
 class Executor:
-    
+    PHP_CGI_BIN_PATH_WIN  = "../../3rdparty/php"
+    PHP_CGI_BIN_PATH_UNIX = "../../../3rdparty/php/bin"
+    PHP_CGI_BIN_PATH_OSX  = "../3rdparty/php/bin"
+    PHP_CGI_EXEC_WIN      = "php-cgi.exe"
+    PHP_CGI_EXEC_UNIX     = "php"
+    PHP_CGI_EXEC_OSX      = "php-cgi"
+
     def GetPythonExecutablePath():
         path = UICommon.GetPythonExecPath()
         if path:
             return path
     def GetPythonExecutablePath():
         path = UICommon.GetPythonExecPath()
         if path:
             return path
-        wx.MessageBox(_("To proceed I need to know the location of the python.exe you would like to use.\nTo set this, go to Tools-->Options and use the 'Python' tab to enter a value.\n"), _("Python Executable Location Unknown"))
+        wx.MessageBox(_("To proceed we need to know the location of the python.exe you would like to use.\nTo set this, go to Tools-->Options and use the 'Python' tab to enter a value.\n"), _("Python Executable Location Unknown"))
         return None
         return None
-    GetPythonExecutablePath = staticmethod(GetPythonExecutablePath)   
-     
+    GetPythonExecutablePath = staticmethod(GetPythonExecutablePath)
+
+    def GetPHPExecutablePath():
+        if sysutilslib.isWindows():
+            phpCgiBinPath = Executor.PHP_CGI_BIN_PATH_WIN
+            phpCgiExec    = Executor.PHP_CGI_EXEC_WIN
+        elif sys.platform == "darwin":
+            phpCgiBinPath = Executor.PHP_CGI_BIN_PATH_OSX
+            phpCgiExec    = Executor.PHP_CGI_EXEC_OSX
+        else:
+            phpCgiBinPath = Executor.PHP_CGI_BIN_PATH_UNIX
+            phpCgiExec    = Executor.PHP_CGI_EXEC_UNIX
+
+        if sysutilslib.isRelease():
+            phpCgiExecFullPath = os.path.normpath(os.path.join(sysutilslib.mainModuleDir, phpCgiBinPath, phpCgiExec))
+        else:
+            phpCgiExecFullPath = phpCgiExec
+
+        if _VERBOSE:
+            print "php cgi executable full path is: %s" % phpCgiExecFullPath
+
+        return phpCgiExecFullPath
+    GetPHPExecutablePath = staticmethod(GetPHPExecutablePath)
+
     def __init__(self, fileName, wxComponent, arg1=None, arg2=None, arg3=None, arg4=None, arg5=None, arg6=None, arg7=None, arg8=None, arg9=None, callbackOnExit=None):
         self._fileName = fileName
         self._stdOutCallback = self.OutCall
         self._stdErrCallback = self.ErrCall
         self._callbackOnExit = callbackOnExit
         self._wxComponent = wxComponent
     def __init__(self, fileName, wxComponent, arg1=None, arg2=None, arg3=None, arg4=None, arg5=None, arg6=None, arg7=None, arg8=None, arg9=None, callbackOnExit=None):
         self._fileName = fileName
         self._stdOutCallback = self.OutCall
         self._stdErrCallback = self.ErrCall
         self._callbackOnExit = callbackOnExit
         self._wxComponent = wxComponent
-        path = Executor.GetPythonExecutablePath()
-        self._cmd = '"' + path + '" -u \"' + fileName + '\"'
+        if fileName.endswith('.py') or fileName.endswith('.pyc'):
+            self._path = Executor.GetPythonExecutablePath()
+            self._cmd = '"' + self._path + '" -u \"' + fileName + '\"'
+        else:
+            self._path = Executor.GetPHPExecutablePath()
+            self._cmd = '"' + self._path + '"'
         #Better way to do this? Quotes needed for windows file paths.
         def spaceAndQuote(text):
             if text.startswith("\"") and text.endswith("\""):
         #Better way to do this? Quotes needed for windows file paths.
         def spaceAndQuote(text):
             if text.startswith("\"") and text.endswith("\""):
@@ -171,34 +212,40 @@ class Executor:
             self._cmd += spaceAndQuote(arg8)
         if(arg9 != None):
             self._cmd += spaceAndQuote(arg9)
             self._cmd += spaceAndQuote(arg8)
         if(arg9 != None):
             self._cmd += spaceAndQuote(arg9)
-        
+
         self._stdOutReader = None
         self._stdErrReader = None
         self._process = None
         self._stdOutReader = None
         self._stdErrReader = None
         self._process = None
-        
+
     def OutCall(self, text):
         evt = UpdateTextEvent(value = text)
         wx.PostEvent(self._wxComponent, evt)
     def OutCall(self, text):
         evt = UpdateTextEvent(value = text)
         wx.PostEvent(self._wxComponent, evt)
-                   
+
     def ErrCall(self, text):
         evt = UpdateErrorEvent(value = text)
         wx.PostEvent(self._wxComponent, evt)
     def ErrCall(self, text):
         evt = UpdateErrorEvent(value = text)
         wx.PostEvent(self._wxComponent, evt)
-            
+
     def Execute(self, arguments, startIn=None, environment=None):
         if not startIn:
             startIn = str(os.getcwd())
         startIn = os.path.abspath(startIn)
     def Execute(self, arguments, startIn=None, environment=None):
         if not startIn:
             startIn = str(os.getcwd())
         startIn = os.path.abspath(startIn)
-        command = self._cmd + ' ' + arguments
+
+        if arguments and arguments != " ":
+            command = self._cmd + ' ' + arguments
+        else:
+            command = self._cmd
+
+        if _VERBOSE: print "start debugger executable: " + command + "\n"
         self._process = process.ProcessOpen(command, mode='b', cwd=startIn, env=environment)
         # Kick off threads to read stdout and stderr and write them
         self._process = process.ProcessOpen(command, mode='b', cwd=startIn, env=environment)
         # Kick off threads to read stdout and stderr and write them
-        # to our text control. 
+        # to our text control.
         self._stdOutReader = OutputReaderThread(self._process.stdout, self._stdOutCallback, callbackOnExit=self._callbackOnExit)
         self._stdOutReader.start()
         self._stdErrReader = OutputReaderThread(self._process.stderr, self._stdErrCallback, accumulate=False)
         self._stdErrReader.start()
         self._stdOutReader = OutputReaderThread(self._process.stdout, self._stdOutCallback, callbackOnExit=self._callbackOnExit)
         self._stdOutReader.start()
         self._stdErrReader = OutputReaderThread(self._process.stderr, self._stdErrCallback, accumulate=False)
         self._stdErrReader.start()
-                        
+
     def DoStopExecution(self):
     def DoStopExecution(self):
-        # See comment on DebugCommandUI.StopExecution
+        # See comment on PythonDebuggerUI.StopExecution
         if(self._process != None):
             self._stdOutReader.AskToStop()
             self._stdErrReader.AskToStop()
         if(self._process != None):
             self._stdOutReader.AskToStop()
             self._stdErrReader.AskToStop()
@@ -207,30 +254,33 @@ class Executor:
             except:
                 pass
             self._process = None
             except:
                 pass
             self._process = None
-        
+
+    def GetExecPath(self):
+        return self._path
+
 class RunCommandUI(wx.Panel):
     runners = []
 class RunCommandUI(wx.Panel):
     runners = []
-    
+
     def ShutdownAllRunners():
     def ShutdownAllRunners():
-        # See comment on DebugCommandUI.StopExecution
+        # See comment on PythonDebuggerUI.StopExecution
         for runner in RunCommandUI.runners:
             try:
                 runner.StopExecution(None)
             except wx._core.PyDeadObjectError:
                 pass
         for runner in RunCommandUI.runners:
             try:
                 runner.StopExecution(None)
             except wx._core.PyDeadObjectError:
                 pass
-        RunCommandUI.runners = []    
+        RunCommandUI.runners = []
     ShutdownAllRunners = staticmethod(ShutdownAllRunners)
     ShutdownAllRunners = staticmethod(ShutdownAllRunners)
-    
+
     def __init__(self, parent, id, fileName):
         wx.Panel.__init__(self, parent, id)
         self._noteBook = parent
     def __init__(self, parent, id, fileName):
         wx.Panel.__init__(self, parent, id)
         self._noteBook = parent
-        
+
         threading._VERBOSE = _VERBOSE
         threading._VERBOSE = _VERBOSE
-        
+
         self.KILL_PROCESS_ID = wx.NewId()
         self.CLOSE_TAB_ID = wx.NewId()
         self.KILL_PROCESS_ID = wx.NewId()
         self.CLOSE_TAB_ID = wx.NewId()
-        
-        
+
+
         # GUI Initialization follows
         sizer = wx.BoxSizer(wx.HORIZONTAL)
         self._tb = tb = wx.ToolBar(self,  -1, wx.DefaultPosition, (30,1000), wx.TB_VERTICAL| wx.TB_FLAT, "Runner" )
         # GUI Initialization follows
         sizer = wx.BoxSizer(wx.HORIZONTAL)
         self._tb = tb = wx.ToolBar(self,  -1, wx.DefaultPosition, (30,1000), wx.TB_VERTICAL| wx.TB_FLAT, "Runner" )
@@ -240,11 +290,11 @@ class RunCommandUI(wx.Panel):
         close_bmp = getCloseBitmap()
         tb.AddSimpleTool( self.CLOSE_TAB_ID, close_bmp, _('Close Window'))
         wx.EVT_TOOL(self, self.CLOSE_TAB_ID, self.OnToolClicked)
         close_bmp = getCloseBitmap()
         tb.AddSimpleTool( self.CLOSE_TAB_ID, close_bmp, _('Close Window'))
         wx.EVT_TOOL(self, self.CLOSE_TAB_ID, self.OnToolClicked)
-        
+
         stop_bmp = getStopBitmap()
         tb.AddSimpleTool(self.KILL_PROCESS_ID, stop_bmp, _("Stop the Run."))
         wx.EVT_TOOL(self, self.KILL_PROCESS_ID, self.OnToolClicked)
         stop_bmp = getStopBitmap()
         tb.AddSimpleTool(self.KILL_PROCESS_ID, stop_bmp, _("Stop the Run."))
         wx.EVT_TOOL(self, self.KILL_PROCESS_ID, self.OnToolClicked)
-        
+
         tb.Realize()
         self._textCtrl = STCTextEditor.TextCtrl(self, wx.NewId()) #id)
         sizer.Add(self._textCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
         tb.Realize()
         self._textCtrl = STCTextEditor.TextCtrl(self, wx.NewId()) #id)
         sizer.Add(self._textCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
@@ -257,27 +307,27 @@ class RunCommandUI(wx.Panel):
         self._textCtrl.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font))
         self._textCtrl.SetFontColor(wx.BLACK)
         self._textCtrl.StyleClearAll()
         self._textCtrl.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font))
         self._textCtrl.SetFontColor(wx.BLACK)
         self._textCtrl.StyleClearAll()
-     
-        #Disabling for now...may interfere with file open. wx.stc.EVT_STC_DOUBLECLICK(self._textCtrl, self._textCtrl.GetId(), self.OnDoubleClick)
+
+        wx.stc.EVT_STC_DOUBLECLICK(self._textCtrl, self._textCtrl.GetId(), self.OnDoubleClick)
 
         self.SetSizer(sizer)
         sizer.Fit(self)
 
         self.SetSizer(sizer)
         sizer.Fit(self)
-        
+
         self._stopped = False
         # Executor initialization
         self._executor = Executor(fileName, self, callbackOnExit=self.ExecutorFinished)
         self.Bind(EVT_UPDATE_STDTEXT, self.AppendText)
         self.Bind(EVT_UPDATE_ERRTEXT, self.AppendErrorText)
         self._stopped = False
         # Executor initialization
         self._executor = Executor(fileName, self, callbackOnExit=self.ExecutorFinished)
         self.Bind(EVT_UPDATE_STDTEXT, self.AppendText)
         self.Bind(EVT_UPDATE_ERRTEXT, self.AppendErrorText)
-        
+
         RunCommandUI.runners.append(self)
         RunCommandUI.runners.append(self)
-        
+
     def __del__(self):
     def __del__(self):
-        # See comment on DebugCommandUI.StopExecution
+        # See comment on PythonDebuggerUI.StopExecution
         self._executor.DoStopExecution()
         self._executor.DoStopExecution()
-                 
-    def Execute(self, initialArgs, startIn, environment):
+
+    def Execute(self, initialArgs, startIn, environment, onWebServer = False):
         self._executor.Execute(initialArgs, startIn, environment)
         self._executor.Execute(initialArgs, startIn, environment)
-    
+
     def ExecutorFinished(self):
         self._tb.EnableTool(self.KILL_PROCESS_ID, False)
         nb = self.GetParent()
     def ExecutorFinished(self):
         self._tb.EnableTool(self.KILL_PROCESS_ID, False)
         nb = self.GetParent()
@@ -287,27 +337,27 @@ class RunCommandUI(wx.Panel):
                 newText = text.replace("Running", "Finished")
                 nb.SetPageText(i, newText)
                 break
                 newText = text.replace("Running", "Finished")
                 nb.SetPageText(i, newText)
                 break
-           
+
     def StopExecution(self):
         if not self._stopped:
             self._stopped = True
             self.Unbind(EVT_UPDATE_STDTEXT)
             self.Unbind(EVT_UPDATE_ERRTEXT)
             self._executor.DoStopExecution()
     def StopExecution(self):
         if not self._stopped:
             self._stopped = True
             self.Unbind(EVT_UPDATE_STDTEXT)
             self.Unbind(EVT_UPDATE_ERRTEXT)
             self._executor.DoStopExecution()
-        
+
     def AppendText(self, event):
         self._textCtrl.SetReadOnly(False)
         self._textCtrl.AddText(event.value)
         self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
         self._textCtrl.SetReadOnly(True)
     def AppendText(self, event):
         self._textCtrl.SetReadOnly(False)
         self._textCtrl.AddText(event.value)
         self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
         self._textCtrl.SetReadOnly(True)
-        
+
     def AppendErrorText(self, event):
         self._textCtrl.SetReadOnly(False)
     def AppendErrorText(self, event):
         self._textCtrl.SetReadOnly(False)
-        self._textCtrl.SetFontColor(wx.RED)   
+        self._textCtrl.SetFontColor(wx.RED)
         self._textCtrl.StyleClearAll()
         self._textCtrl.AddText(event.value)
         self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
         self._textCtrl.StyleClearAll()
         self._textCtrl.AddText(event.value)
         self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
-        self._textCtrl.SetFontColor(wx.BLACK)   
+        self._textCtrl.SetFontColor(wx.BLACK)
         self._textCtrl.StyleClearAll()
         self._textCtrl.SetReadOnly(True)
 
         self._textCtrl.StyleClearAll()
         self._textCtrl.SetReadOnly(True)
 
@@ -317,26 +367,26 @@ class RunCommandUI(wx.Panel):
         index = self._noteBook.GetSelection()
         self._noteBook.GetPage(index).Show(False)
         self._noteBook.RemovePage(index)
         index = self._noteBook.GetSelection()
         self._noteBook.GetPage(index).Show(False)
         self._noteBook.RemovePage(index)
-        
+
     #------------------------------------------------------------------------------
     # Event handling
     #-----------------------------------------------------------------------------
     #------------------------------------------------------------------------------
     # Event handling
     #-----------------------------------------------------------------------------
-    
+
     def OnToolClicked(self, event):
         id = event.GetId()
     def OnToolClicked(self, event):
         id = event.GetId()
-        
+
         if id == self.KILL_PROCESS_ID:
             self.StopExecution()
         if id == self.KILL_PROCESS_ID:
             self.StopExecution()
-            
+
         elif id == self.CLOSE_TAB_ID:
             self.StopAndRemoveUI(event)
         elif id == self.CLOSE_TAB_ID:
             self.StopAndRemoveUI(event)
-            
+
     def OnDoubleClick(self, event):
         # Looking for a stack trace line.
         lineText, pos = self._textCtrl.GetCurLine()
         fileBegin = lineText.find("File \"")
     def OnDoubleClick(self, event):
         # Looking for a stack trace line.
         lineText, pos = self._textCtrl.GetCurLine()
         fileBegin = lineText.find("File \"")
-        fileEnd = lineText.find("\", line ") 
-        lineEnd = lineText.find(", in ")   
+        fileEnd = lineText.find("\", line ")
+        lineEnd = lineText.find(", in ")
         if lineText == "\n" or  fileBegin == -1 or fileEnd == -1 or lineEnd == -1:
             # Check the line before the one that was clicked on
             lineNumber = self._textCtrl.GetCurrentLine()
         if lineText == "\n" or  fileBegin == -1 or fileEnd == -1 or lineEnd == -1:
             # Check the line before the one that was clicked on
             lineNumber = self._textCtrl.GetCurrentLine()
@@ -344,11 +394,11 @@ class RunCommandUI(wx.Panel):
                 return
             lineText = self._textCtrl.GetLine(lineNumber - 1)
             fileBegin = lineText.find("File \"")
                 return
             lineText = self._textCtrl.GetLine(lineNumber - 1)
             fileBegin = lineText.find("File \"")
-            fileEnd = lineText.find("\", line ") 
-            lineEnd = lineText.find(", in ")   
+            fileEnd = lineText.find("\", line ")
+            lineEnd = lineText.find(", in ")
             if lineText == "\n" or  fileBegin == -1 or fileEnd == -1 or lineEnd == -1:
                 return
             if lineText == "\n" or  fileBegin == -1 or fileEnd == -1 or lineEnd == -1:
                 return
-                
+
         filename = lineText[fileBegin + 6:fileEnd]
         lineNum = int(lineText[fileEnd + 8:lineEnd])
 
         filename = lineText[fileBegin + 6:fileEnd]
         lineNum = int(lineText[fileEnd + 8:lineEnd])
 
@@ -368,103 +418,72 @@ class RunCommandUI(wx.Panel):
             foundView.Activate()
             foundView.GotoLine(lineNum)
             startPos = foundView.PositionFromLine(lineNum)
             foundView.Activate()
             foundView.GotoLine(lineNum)
             startPos = foundView.PositionFromLine(lineNum)
-            
-        # FACTOR THIS INTO DocManager
-        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-        for openDoc in openDocs:  
-            if(isinstance(openDoc, CodeEditor.CodeDocument)): 
-                openDoc.GetFirstView().GetCtrl().ClearCurrentLineMarkers()    
-                
-        foundView.GetCtrl().MarkerAdd(lineNum -1, CodeEditor.CodeCtrl.CURRENT_LINE_MARKER_NUM)
+            lineText = foundView.GetCtrl().GetLine(lineNum - 1)
+            foundView.SetSelection(startPos, startPos + len(lineText.rstrip("\n")))
+            import OutlineService
+            wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
 
 
 DEFAULT_PORT = 32032
 
 
 DEFAULT_PORT = 32032
-DEFAULT_HOST = 'localhost' 
-PORT_COUNT = 21      
+DEFAULT_HOST = 'localhost'
+PORT_COUNT = 21
 
 
-class DebugCommandUI(wx.Panel):
-    debuggerPortList = None   
+class BaseDebuggerUI(wx.Panel):
     debuggers = []
     debuggers = []
-    
+
     def NotifyDebuggersOfBreakpointChange():
     def NotifyDebuggersOfBreakpointChange():
-        for debugger in DebugCommandUI.debuggers:
+        for debugger in BaseDebuggerUI.debuggers:
             debugger.BreakPointChange()
             debugger.BreakPointChange()
-            
+
     NotifyDebuggersOfBreakpointChange = staticmethod(NotifyDebuggersOfBreakpointChange)
     NotifyDebuggersOfBreakpointChange = staticmethod(NotifyDebuggersOfBreakpointChange)
+
     def DebuggerRunning():
     def DebuggerRunning():
-        for debugger in DebugCommandUI.debuggers:
+        for debugger in BaseDebuggerUI.debuggers:
             if debugger._executor:
                 return True
         return False
     DebuggerRunning = staticmethod(DebuggerRunning)
             if debugger._executor:
                 return True
         return False
     DebuggerRunning = staticmethod(DebuggerRunning)
-    
+
+    def DebuggerInWait():
+        for debugger in BaseDebuggerUI.debuggers:
+            if debugger._executor:
+                if debugger._callback._waiting:
+                    return True
+        return False
+    DebuggerInWait = staticmethod(DebuggerInWait)
+
+    def DebuggerPastAutoContinue():
+        for debugger in BaseDebuggerUI.debuggers:
+            if debugger._executor:
+                if debugger._callback._waiting and not debugger._callback._autoContinue:
+                    return True
+        return False
+    DebuggerPastAutoContinue = staticmethod(DebuggerPastAutoContinue)
+
     def ShutdownAllDebuggers():
     def ShutdownAllDebuggers():
-        # See comment on DebugCommandUI.StopExecution
-        for debugger in DebugCommandUI.debuggers:
+        for debugger in BaseDebuggerUI.debuggers:
             try:
                 debugger.StopExecution(None)
             except wx._core.PyDeadObjectError:
                 pass
             try:
                 debugger.StopExecution(None)
             except wx._core.PyDeadObjectError:
                 pass
-        DebugCommandUI.debuggers = []    
+        BaseDebuggerUI.debuggers = []
     ShutdownAllDebuggers = staticmethod(ShutdownAllDebuggers)
     ShutdownAllDebuggers = staticmethod(ShutdownAllDebuggers)
-    
-    def GetAvailablePort():
-        for index in range( 0, len(DebugCommandUI.debuggerPortList)):               
-            port = DebugCommandUI.debuggerPortList[index]
-            if DebugCommandUI.PortAvailable(port):
-                DebugCommandUI.debuggerPortList.pop(index)
-                return port
-        wx.MessageBox(_("Out of ports for debugging!  Please restart the application builder.\nIf that does not work, check for and remove running instances of python."), _("Out of Ports"))
-        assert False, "Out of ports for debugger."
-        
-    GetAvailablePort = staticmethod(GetAvailablePort)
-
-    def ReturnPortToPool(port):
-        config = wx.ConfigBase_Get()
-        startingPort = config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
-        val = int(startingPort) + int(PORT_COUNT)
-        if int(port) >= startingPort and (int(port) <= val):
-            DebugCommandUI.debuggerPortList.append(int(port))
-            
-    ReturnPortToPool = staticmethod(ReturnPortToPool)
-
-    def PortAvailable(port):
-        config = wx.ConfigBase_Get()
-        hostname = config.Read("DebuggerHostName", DEFAULT_HOST)
-        try:
-            server = AGXMLRPCServer((hostname, port))
-            server.server_close()
-            if _VERBOSE: print "Port ", str(port), " available."
-            return True
-        except:
-            tp,val,tb = sys.exc_info()
-            if _VERBOSE: traceback.print_exception(tp, val, tb)
-            if _VERBOSE: print "Port ", str(port), " unavailable."
-            return False
-            
-    PortAvailable = staticmethod(PortAvailable)
-        
-    def NewPortRange():
-        config = wx.ConfigBase_Get()
-        startingPort = config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
-        DebugCommandUI.debuggerPortList = range(startingPort, startingPort + PORT_COUNT)
-    NewPortRange = staticmethod(NewPortRange)
-    
-    def __init__(self, parent, id, command, service):
-        # Check for ports before creating the panel.
-        if not DebugCommandUI.debuggerPortList:
-            DebugCommandUI.NewPortRange()
-        self._debuggerPort = str(DebugCommandUI.GetAvailablePort())
-        self._guiPort = str(DebugCommandUI.GetAvailablePort())
-        self._debuggerBreakPort = str(DebugCommandUI.GetAvailablePort())
 
 
+    def __init__(self, parent, id):
         wx.Panel.__init__(self, parent, id)
         wx.Panel.__init__(self, parent, id)
-        
         self._parentNoteBook = parent
         self._parentNoteBook = parent
-        self._command = command
-        self._service = service
+
+        self._service = None
+        self._executor = None
+        self._callback = None
+        self._stopped = False
+
+        BaseDebuggerUI.debuggers.append(self)
+        self._stopped = True
+        self.Bind(EVT_UPDATE_STDTEXT, self.AppendText)
+        self.Bind(EVT_UPDATE_ERRTEXT, self.AppendErrorText)
         self._executor = None
         self._executor = None
+
         self.STEP_ID = wx.NewId()
         self.CONTINUE_ID = wx.NewId()
         self.STEP_OUT_ID = wx.NewId()
         self.STEP_ID = wx.NewId()
         self.CONTINUE_ID = wx.NewId()
         self.STEP_OUT_ID = wx.NewId()
@@ -478,37 +497,37 @@ class DebugCommandUI(wx.Panel):
         self._tb = tb = wx.ToolBar(self,  -1, wx.DefaultPosition, (1000,30), wx.TB_HORIZONTAL| wx.NO_BORDER| wx.TB_FLAT| wx.TB_TEXT, "Debugger" )
         sizer.Add(tb, 0, wx.EXPAND |wx.ALIGN_LEFT|wx.ALL, 1)
         tb.SetToolBitmapSize((16,16))
         self._tb = tb = wx.ToolBar(self,  -1, wx.DefaultPosition, (1000,30), wx.TB_HORIZONTAL| wx.NO_BORDER| wx.TB_FLAT| wx.TB_TEXT, "Debugger" )
         sizer.Add(tb, 0, wx.EXPAND |wx.ALIGN_LEFT|wx.ALL, 1)
         tb.SetToolBitmapSize((16,16))
-       
+
         close_bmp = getCloseBitmap()
         tb.AddSimpleTool( self.CLOSE_WINDOW_ID, close_bmp, _('Close Window'))
         wx.EVT_TOOL(self, self.CLOSE_WINDOW_ID, self.StopAndRemoveUI)
         close_bmp = getCloseBitmap()
         tb.AddSimpleTool( self.CLOSE_WINDOW_ID, close_bmp, _('Close Window'))
         wx.EVT_TOOL(self, self.CLOSE_WINDOW_ID, self.StopAndRemoveUI)
-        
+
         stop_bmp = getStopBitmap()
         tb.AddSimpleTool( self.KILL_PROCESS_ID, stop_bmp, _("Stop Debugging"))
         wx.EVT_TOOL(self, self.KILL_PROCESS_ID, self.StopExecution)
         stop_bmp = getStopBitmap()
         tb.AddSimpleTool( self.KILL_PROCESS_ID, stop_bmp, _("Stop Debugging"))
         wx.EVT_TOOL(self, self.KILL_PROCESS_ID, self.StopExecution)
-        
+
         tb.AddSeparator()
         tb.AddSeparator()
-        
+
         break_bmp = getBreakBitmap()
         tb.AddSimpleTool( self.BREAK_INTO_DEBUGGER_ID, break_bmp, _("Break into Debugger"))
         wx.EVT_TOOL(self, self.BREAK_INTO_DEBUGGER_ID, self.BreakExecution)
         break_bmp = getBreakBitmap()
         tb.AddSimpleTool( self.BREAK_INTO_DEBUGGER_ID, break_bmp, _("Break into Debugger"))
         wx.EVT_TOOL(self, self.BREAK_INTO_DEBUGGER_ID, self.BreakExecution)
-        
+
         tb.AddSeparator()
         tb.AddSeparator()
-                
+
         continue_bmp = getContinueBitmap()
         tb.AddSimpleTool( self.CONTINUE_ID, continue_bmp, _("Continue Execution"))
         wx.EVT_TOOL(self, self.CONTINUE_ID, self.OnContinue)
         continue_bmp = getContinueBitmap()
         tb.AddSimpleTool( self.CONTINUE_ID, continue_bmp, _("Continue Execution"))
         wx.EVT_TOOL(self, self.CONTINUE_ID, self.OnContinue)
-         
+        self.Bind(EVT_DEBUG_INTERNAL, self.OnContinue)
+
         tb.AddSeparator()
         tb.AddSeparator()
-               
         next_bmp = getNextBitmap()
         tb.AddSimpleTool( self.NEXT_ID, next_bmp, _("Step to next line"))
         wx.EVT_TOOL(self, self.NEXT_ID, self.OnNext)
         next_bmp = getNextBitmap()
         tb.AddSimpleTool( self.NEXT_ID, next_bmp, _("Step to next line"))
         wx.EVT_TOOL(self, self.NEXT_ID, self.OnNext)
-        
+
         step_bmp = getStepInBitmap()
         tb.AddSimpleTool( self.STEP_ID, step_bmp, _("Step in"))
         wx.EVT_TOOL(self, self.STEP_ID, self.OnSingleStep)
         step_bmp = getStepInBitmap()
         tb.AddSimpleTool( self.STEP_ID, step_bmp, _("Step in"))
         wx.EVT_TOOL(self, self.STEP_ID, self.OnSingleStep)
-        
+
         stepOut_bmp = getStepReturnBitmap()
         tb.AddSimpleTool(self.STEP_OUT_ID, stepOut_bmp, _("Stop at function return"))
         wx.EVT_TOOL(self, self.STEP_OUT_ID, self.OnStepOut)
         stepOut_bmp = getStepReturnBitmap()
         tb.AddSimpleTool(self.STEP_OUT_ID, stepOut_bmp, _("Stop at function return"))
         wx.EVT_TOOL(self, self.STEP_OUT_ID, self.OnStepOut)
@@ -519,11 +538,12 @@ class DebugCommandUI(wx.Panel):
             tb.AddSimpleTool(self.ADD_WATCH_ID, watch_bmp, _("Add a watch"))
             wx.EVT_TOOL(self, self.ADD_WATCH_ID, self.OnAddWatch)
             tb.AddSeparator()
             tb.AddSimpleTool(self.ADD_WATCH_ID, watch_bmp, _("Add a watch"))
             wx.EVT_TOOL(self, self.ADD_WATCH_ID, self.OnAddWatch)
             tb.AddSeparator()
-        
+
         clear_bmp = getClearOutputBitmap()
         tb.AddSimpleTool(self.CLEAR_ID, clear_bmp, _("Clear output pane"))
         wx.EVT_TOOL(self, self.CLEAR_ID, self.OnClearOutput)
         clear_bmp = getClearOutputBitmap()
         tb.AddSimpleTool(self.CLEAR_ID, clear_bmp, _("Clear output pane"))
         wx.EVT_TOOL(self, self.CLEAR_ID, self.OnClearOutput)
-        
+
+        self._toolEnabled = True
         self.framesTab = None
         self.DisableWhileDebuggerRunning()
         self.framesTab = self.MakeFramesUI(self, wx.NewId(), None)
         self.framesTab = None
         self.DisableWhileDebuggerRunning()
         self.framesTab = self.MakeFramesUI(self, wx.NewId(), None)
@@ -531,85 +551,72 @@ class DebugCommandUI(wx.Panel):
         self._statusBar = wx.StatusBar( self, -1)
         self._statusBar.SetFieldsCount(1)
         sizer.Add(self._statusBar, 0, wx.EXPAND |wx.ALIGN_LEFT|wx.ALL, 1)
         self._statusBar = wx.StatusBar( self, -1)
         self._statusBar.SetFieldsCount(1)
         sizer.Add(self._statusBar, 0, wx.EXPAND |wx.ALIGN_LEFT|wx.ALL, 1)
-
         self.SetStatusText("Starting debug...")
         self.SetStatusText("Starting debug...")
-        
         self.SetSizer(sizer)
         tb.Realize()
         sizer.Fit(self)
         self.SetSizer(sizer)
         tb.Realize()
         sizer.Fit(self)
-        config = wx.ConfigBase_Get()
-        self._debuggerHost = self._guiHost = config.Read("DebuggerHostName", DEFAULT_HOST)
-        url = 'http://' + self._debuggerHost + ':' + self._debuggerPort + '/'
-        self._breakURL = 'http://' + self._debuggerHost + ':' + self._debuggerBreakPort + '/'
-        self._callback = DebuggerCallback(self._guiHost, self._guiPort, url, self._breakURL, self)
-        if DebuggerHarness.__file__.find('library.zip') > 0:
-            try:
-                fname = DebuggerHarness.__file__
-                parts = fname.split('library.zip')
-                path = os.path.join(parts[0],'activegrid', 'tool', 'DebuggerHarness.py')
-            except:
-                tp, val, tb = sys.exc_info()
-                traceback.print_exception(tp, val, tb)   
-            
-        else:
-            print "Starting debugger on these ports: %s, %s, %s" % (str(self._debuggerPort) , str(self._guiPort) , str(self._debuggerBreakPort))
-            path = DebuggerService.ExpandPath(DebuggerHarness.__file__)
-        self._executor = Executor(path, self, self._debuggerHost, \
-                                                self._debuggerPort, self._debuggerBreakPort, self._guiHost, self._guiPort, self._command, callbackOnExit=self.ExecutorFinished)
-        
-        self.Bind(EVT_UPDATE_STDTEXT, self.AppendText)
-        self.Bind(EVT_UPDATE_ERRTEXT, self.AppendErrorText)
-        DebugCommandUI.debuggers.append(self)
-        self._stopped = False
-        
+
     def OnSingleStep(self, event):
         self._callback.SingleStep()
 
     def OnContinue(self, event):
         self._callback.Continue()
     def OnSingleStep(self, event):
         self._callback.SingleStep()
 
     def OnContinue(self, event):
         self._callback.Continue()
-        
+
     def OnStepOut(self, event):
         self._callback.Return()
     def OnStepOut(self, event):
         self._callback.Return()
-        
+
     def OnNext(self, event):
         self._callback.Next()
 
     def BreakPointChange(self):
         if not self._stopped:
     def OnNext(self, event):
         self._callback.Next()
 
     def BreakPointChange(self):
         if not self._stopped:
-            self._callback.pushBreakpoints()
+            self._callback.PushBreakpoints()
         self.framesTab.PopulateBPList()
         self.framesTab.PopulateBPList()
-        
+
     def __del__(self):
     def __del__(self):
-        # See comment on DebugCommandUI.StopExecution
-        self.StopExecution(None)        
-        
+        # See comment on PythonDebuggerUI.StopExecution
+        self.StopExecution(None)
+
     def DisableWhileDebuggerRunning(self):
     def DisableWhileDebuggerRunning(self):
-        self._tb.EnableTool(self.STEP_ID, False)
-        self._tb.EnableTool(self.CONTINUE_ID, False)
-        self._tb.EnableTool(self.STEP_OUT_ID, False)
-        self._tb.EnableTool(self.NEXT_ID, False)
-        self._tb.EnableTool(self.BREAK_INTO_DEBUGGER_ID, True)
-        if _WATCHES_ON:
-            self._tb.EnableTool(self.ADD_WATCH_ID, False)
-        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-        for openDoc in openDocs:  
-            if(isinstance(openDoc, CodeEditor.CodeDocument)): 
-                openDoc.GetFirstView().GetCtrl().ClearCurrentLineMarkers() 
-        if self.framesTab:   
-            self.framesTab.ClearWhileRunning()
-        
+        if self._toolEnabled:
+            self._tb.EnableTool(self.STEP_ID, False)
+            self._tb.EnableTool(self.CONTINUE_ID, False)
+            self._tb.EnableTool(self.STEP_OUT_ID, False)
+            self._tb.EnableTool(self.NEXT_ID, False)
+            self._tb.EnableTool(self.BREAK_INTO_DEBUGGER_ID, True)
+    
+            if _WATCHES_ON:
+                self._tb.EnableTool(self.ADD_WATCH_ID, False)
+    
+            self.DeleteCurrentLineMarkers()
+    
+            if self.framesTab:
+                self.framesTab.ClearWhileRunning()
+
+            self._toolEnabled = False
+
     def EnableWhileDebuggerStopped(self):
         self._tb.EnableTool(self.STEP_ID, True)
         self._tb.EnableTool(self.CONTINUE_ID, True)
         self._tb.EnableTool(self.STEP_OUT_ID, True)
         self._tb.EnableTool(self.NEXT_ID, True)
     def EnableWhileDebuggerStopped(self):
         self._tb.EnableTool(self.STEP_ID, True)
         self._tb.EnableTool(self.CONTINUE_ID, True)
         self._tb.EnableTool(self.STEP_OUT_ID, True)
         self._tb.EnableTool(self.NEXT_ID, True)
+        self._tb.EnableTool(self.BREAK_INTO_DEBUGGER_ID, False)
+        self._tb.EnableTool(self.KILL_PROCESS_ID, True)
+
         if _WATCHES_ON:
             self._tb.EnableTool(self.ADD_WATCH_ID, True)
         if _WATCHES_ON:
             self._tb.EnableTool(self.ADD_WATCH_ID, True)
-        self._tb.EnableTool(self.BREAK_INTO_DEBUGGER_ID, False)
-    def ExecutorFinished(self):          
+
+        self._toolEnabled = True
+
+    def DisableAfterStop(self):
+        if self._toolEnabled:
+            self.DisableWhileDebuggerRunning()
+            self._tb.EnableTool(self.BREAK_INTO_DEBUGGER_ID, False)
+            self._tb.EnableTool(self.KILL_PROCESS_ID, False)
+
+    def ExecutorFinished(self):
         if _VERBOSE: print "In ExectorFinished"
         if _VERBOSE: print "In ExectorFinished"
-        try:          
+        try:
             self.DisableAfterStop()
         except wx._core.PyDeadObjectError:
             pass
             self.DisableAfterStop()
         except wx._core.PyDeadObjectError:
             pass
@@ -625,15 +632,21 @@ class DebugCommandUI(wx.Panel):
         except:
             if _VERBOSE: print "In ExectorFinished, got exception"
 
         except:
             if _VERBOSE: print "In ExectorFinished, got exception"
 
-    def DisableAfterStop(self):
-        self.DisableWhileDebuggerRunning()
-        self._tb.EnableTool(self.BREAK_INTO_DEBUGGER_ID, False)
-        self._tb.EnableTool(self.KILL_PROCESS_ID, False)
+    def SetStatusText(self, text):
+        self._statusBar.SetStatusText(text,0)
+
+    def BreakExecution(self, event):
+        self._callback.BreakExecution()
+
+    def StopExecution(self, event):
+        self._callback.ShutdownServer()
+
+    def Execute(self, initialArgs, startIn, environment, onWebServer = False):
+        assert False, "Execute not overridden"
 
     def SynchCurrentLine(self, filename, lineNum, noArrow=False):
 
     def SynchCurrentLine(self, filename, lineNum, noArrow=False):
-        # FACTOR THIS INTO DocManager
         self.DeleteCurrentLineMarkers()
         self.DeleteCurrentLineMarkers()
-        
+
         # Filename will be <string> if we're in a bit of code that was executed from
         # a string (rather than a file). I haven't been able to get the original string
         # for display.
         # Filename will be <string> if we're in a bit of code that was executed from
         # a string (rather than a file). I haven't been able to get the original string
         # for display.
@@ -648,7 +661,7 @@ class DebugCommandUI(wx.Panel):
             if DebuggerService.ComparePaths(openDoc.GetFilename(),filename):
                 foundView = openDoc.GetFirstView()
                 break
             if DebuggerService.ComparePaths(openDoc.GetFilename(),filename):
                 foundView = openDoc.GetFirstView()
                 break
-                
+
         if not foundView:
             if _VERBOSE:
                 print "filename=", filename
         if not foundView:
             if _VERBOSE:
                 print "filename=", filename
@@ -660,96 +673,368 @@ class DebugCommandUI(wx.Panel):
             foundView.Activate()
             foundView.GotoLine(lineNum)
             startPos = foundView.PositionFromLine(lineNum)
             foundView.Activate()
             foundView.GotoLine(lineNum)
             startPos = foundView.PositionFromLine(lineNum)
-            
-        if not noArrow:        
+
+        if not noArrow:
             foundView.GetCtrl().MarkerAdd(lineNum -1, CodeEditor.CodeCtrl.CURRENT_LINE_MARKER_NUM)
 
     def DeleteCurrentLineMarkers(self):
         openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
             foundView.GetCtrl().MarkerAdd(lineNum -1, CodeEditor.CodeCtrl.CURRENT_LINE_MARKER_NUM)
 
     def DeleteCurrentLineMarkers(self):
         openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-        for openDoc in openDocs:  
-            if(isinstance(openDoc, CodeEditor.CodeDocument)): 
-                openDoc.GetFirstView().GetCtrl().ClearCurrentLineMarkers()    
-        
-    def LoadFramesListXML(self, framesXML):
-        self.framesTab.LoadFramesListXML(framesXML)  
-          
-    def SetStatusText(self, text):
-        self._statusBar.SetStatusText(text,0)
-            
-    def Execute(self, initialArgs, startIn, environment):
-        self._callback.start()
-        self._executor.Execute(initialArgs, startIn, environment)
-        self._callback.waitForRPC() 
-                
-    def BreakExecution(self, event):
-        self._callback.BreakExecution()
-        
-                       
-    def StopExecution(self, event):
-        # This is a general comment on shutdown for the running and debugged processes. Basically, the
-        # current state of this is the result of trial and error coding. The common problems were memory
-        # access violations and threads that would not exit. Making the OutputReaderThreads daemons seems
-        # to have side-stepped the hung thread issue. Being very careful not to touch things after calling
-        # process.py:ProcessOpen.kill() also seems to have fixed the memory access violations, but if there
-        # were more ugliness discovered I would not be surprised. If anyone has any help/advice, please send
-        # it on to mfryer@activegrid.com.
-        if not self._stopped:
-            self._stopped = True
-            try:
-                self.DisableAfterStop()
-            except wx._core.PyDeadObjectError:
-                pass
-            try:
-                self._callback.ShutdownServer()
-            except:
-                tp,val,tb = sys.exc_info()
-                traceback.print_exception(tp, val, tb)
+        for openDoc in openDocs:
+            if(isinstance(openDoc, CodeEditor.CodeDocument)):
+                openDoc.GetFirstView().GetCtrl().ClearCurrentLineMarkers()
 
 
-            try:
-                self.DeleteCurrentLineMarkers()
-            except:
-                pass
-            try:
-                DebugCommandUI.ReturnPortToPool(self._debuggerPort)
-                DebugCommandUI.ReturnPortToPool(self._guiPort)
-                DebugCommandUI.ReturnPortToPool(self._debuggerBreakPort)
-            except:
-                pass
-            try:
-                if self._executor:
-                    self._executor.DoStopExecution()
-                    self._executor = None
-            except:
-                tp,val,tb = sys.exc_info()
-                traceback.print_exception(tp, val, tb)                
     def StopAndRemoveUI(self, event):
         self.StopExecution(None)
     def StopAndRemoveUI(self, event):
         self.StopExecution(None)
-        if self in DebugCommandUI.debuggers:
-            DebugCommandUI.debuggers.remove(self)
+        if self in BaseDebuggerUI.debuggers:
+            BaseDebuggerUI.debuggers.remove(self)
         index = self._parentNoteBook.GetSelection()
         self._parentNoteBook.GetPage(index).Show(False)
         index = self._parentNoteBook.GetSelection()
         self._parentNoteBook.GetPage(index).Show(False)
-        self._parentNoteBook.RemovePage(index)       
+        self._parentNoteBook.RemovePage(index)
 
     def OnAddWatch(self, event):
         if self.framesTab:
             self.framesTab.OnWatch(event)
 
     def OnAddWatch(self, event):
         if self.framesTab:
             self.framesTab.OnWatch(event)
-                        
+
     def MakeFramesUI(self, parent, id, debugger):
     def MakeFramesUI(self, parent, id, debugger):
-        panel = FramesUI(parent, id, self)
-        return panel
-        
+        assert False, "MakeFramesUI not overridden"
+
     def AppendText(self, event):
         self.framesTab.AppendText(event.value)
     def AppendText(self, event):
         self.framesTab.AppendText(event.value)
-        
+
     def AppendErrorText(self, event):
         self.framesTab.AppendErrorText(event.value)
     def AppendErrorText(self, event):
         self.framesTab.AppendErrorText(event.value)
-        
+
     def OnClearOutput(self, event):
         self.framesTab.ClearOutput(None)
 
     def SwitchToOutputTab(self):
         self.framesTab.SwitchToOutputTab()
     def OnClearOutput(self, event):
         self.framesTab.ClearOutput(None)
 
     def SwitchToOutputTab(self):
         self.framesTab.SwitchToOutputTab()
-        
+
+
+class PHPDebuggerUI(BaseDebuggerUI):
+    DEFAULT_LISTENER_HOST     = "127.0.0.1"
+    DEFAULT_LISTENER_PORT     = 10001
+    DEFAULT_DBG_MOD_TIMEOUT   = 300
+    DEFAULT_DBG_MAX_EXEC_TIME = 240
+    dbgSessSeqId              = 1
+
+    def __init__(self, parent, id, command, service):
+        BaseDebuggerUI.__init__(self, parent, id)
+        #Note host and port need to come out of options or a pool.
+        self._dbgHost        = PHPDebuggerUI.DEFAULT_LISTENER_HOST
+        self._dbgPort        = PHPDebuggerUI.DEFAULT_LISTENER_PORT
+        self._dbgTimeout     = PHPDebuggerUI.DEFAULT_DBG_MOD_TIMEOUT
+        self._dbgMaxExecTime = PHPDebuggerUI.DEFAULT_DBG_MAX_EXEC_TIME
+        self._dbgSessId      = None
+        self._dbgSessParam   = None
+        self._dbgPhpIniFile  = None
+        self._callback       = PHPDebugger.PHPDebuggerCallback(self, service, self._dbgHost, self._dbgPort)
+        self._executor       = Executor(command, self)
+        self._service        = service
+        self._stopped        = False
+        self._allStopped     = False
+        
+        self._createPhpDbgSess()
+
+    def showErrorDialog(self, message, title):
+        wx.MessageBox(_(message), _(title))
+        return
+
+    def _createPhpDbgSess(self):
+        currTimeStr                = str(time.time())
+        (secStr, usecStr)          = currTimeStr.split('.')
+        secLongInt                 = long(secStr)
+        usecLongInt                = long(usecStr)
+        self._dbgSessId            = "%06ld%06ld%04d" % (secLongInt, usecLongInt, PHPDebuggerUI.dbgSessSeqId)
+        PHPDebuggerUI.dbgSessSeqId = PHPDebuggerUI.dbgSessSeqId + 1
+        self._dbgSessParam         = "DBGSESSID=%s@clienthost:%d" % (self._dbgSessId, self._dbgPort)
+
+        if _VERBOSE:
+            print "phpDbgParam=%s" % self._dbgSessParam
+
+        self._service.SetPhpDbgParam(self._dbgSessParam)
+
+    def _preparePhpIniFile(self):
+        success    = False
+
+        phpCgiExec = Executor.GetPHPExecutablePath()
+        phpExec    = phpCgiExec.replace("php-cgi", "php")
+        iniPath    = self._getPhpIniFromRunningPhp(phpExec)
+
+        try:
+            iniDbgPath = os.path.normpath(iniPath + ".ag_debug_enabled")
+            dbgFile    = open(iniDbgPath, "w")
+            oriFile    = open(iniPath, "r")
+    
+            while True:
+                oneOriLine = oriFile.readline()
+                if oneOriLine == '':
+                    break
+    
+                if not oneOriLine.startswith("debugger.") and not oneOriLine.startswith("max_execution_time="):
+                    dbgFile.write(oneOriLine)
+    
+            oriFile.close()
+    
+            if _WINDOWS:
+                dbgExtFile = "php_dbg.dll"
+            else:
+                dbgExtFile = "dbg.so"
+    
+            #
+            # TODO: we should make all of these options configurable.
+            #
+            configStr = "\n; ===============================================================\n; The followings are added by ActiveGrid IDE PHP Debugger Runtime\n; ===============================================================\n\n; As we are running with the dbg module, it takes a much longer time for each script to run.\nmax_execution_time=%d\n\n[debugger]\nextension=%s\ndebugger.enabled=On\ndebugger.JIT_enabled=On\ndebugger.JIT_host=%s\ndebugger.JIT_port=%d\ndebugger.fail_silently=Off\ndebugger.timeout_seconds=%d\ndebugger.ignore_nops=Off\ndebugger.enable_session_cookie=On\ndebugger.session_nocache=On\ndebugger.profiler_enabled=Off\n" % (self._dbgMaxExecTime, dbgExtFile, self._dbgHost, self._dbgPort, self._dbgTimeout)
+            dbgFile.write(configStr)
+            dbgFile.close()
+            success = True
+        except:
+            #TODO: print stack trace.
+            print "Caught exceptions while minipulating php.ini files"
+
+        if success:
+            self._dbgPhpIniFile = iniDbgPath
+        else:
+            self._dbgPhpIniFile = None
+
+    def _getPhpIniFromRunningPhp(self, phpExec):
+        phpIniPath = None
+
+        cmdEnv  = os.environ
+        if cmdEnv.has_key('PYTHONPATH'):
+            del cmdEnv['PYTHONPATH']
+
+        cmdLine = [phpExec, "-r", "phpinfo();"]
+        phpProc = subprocess.Popen(args=cmdLine, bufsize=0, executable=None, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, preexec_fn=None, close_fds=False, shell=False, cwd=None, env=os.environ, universal_newlines=False, startupinfo=None, creationflags=0)
+        phpOutput = phpProc.stdout
+
+        phpIniPattern = "Configuration File (php.ini) Path => "
+        while True:
+            oneLine = phpOutput.readline()
+            if oneLine == '':
+                break
+
+            if oneLine.startswith(phpIniPattern):
+                if oneLine.endswith("\n"):
+                    endIndex   = oneLine.index("\n")
+                    phpIniPath = oneLine[len(phpIniPattern):endIndex]
+                else:
+                    phpIniPath = oneLine[len(phpIniPattern):]
+
+                if phpIniPath and len(phpIniPath) > 0:
+                    phpIniPath = os.path.normpath(phpIniPath)
+                    break
+
+        phpOutput.close()
+
+        if _VERBOSE:
+            print "php.ini path is: %s" % repr(phpIniPath)
+
+        return phpIniPath
+
+    def Execute(self, initialArgs, startIn, environment, onWebServer = False):
+        self._preparePhpIniFile()
+        self._callback.Start()
+
+        if not onWebServer:
+            if self._dbgPhpIniFile:
+                args = '-c "' + self._dbgPhpIniFile + '" ' + initialArgs
+            else:
+                args = initialArgs
+
+            self._executor.Execute(args, startIn, environment)
+
+    def StopExecution(self, event):
+        # This is a general comment on shutdown for the running and debugged processes. Basically, the
+        # current state of this is the result of trial and error coding. The common problems were memory
+        # access violations and threads that would not exit. Making the OutputReaderThreads daemons seems
+        # to have side-stepped the hung thread issue. Being very careful not to touch things after calling
+        # process.py:ProcessOpen.kill() also seems to have fixed the memory access violations, but if there
+        # were more ugliness discovered I would not be surprised. If anyone has any help/advice, please send
+        # it on to mfryer@activegrid.com.
+        if not self._allStopped:
+            self._stopped = True
+            try:
+                self.DisableAfterStop()
+            except wx._core.PyDeadObjectError:
+                pass
+
+            try:
+                #
+                # If this is called by clicking the "Stop" button, we only stop
+                # the current running php script, and keep the listener
+                # running.
+                #
+                if event:
+                    self._callback.ShutdownServer(stopLsnr = False)
+                else:
+                    self._callback.ShutdownServer(stopLsnr = True)
+                    self._allStopped = True
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+            try:
+                self.DeleteCurrentLineMarkers()
+            except:
+                pass
+
+            try:
+                if self._executor:
+                    self._executor.DoStopExecution()
+                    self._executor = None
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+    def MakeFramesUI(self, parent, id, debugger):
+        return PHPFramesUI(parent, id, self)
+
+    def LoadPHPFramesList(self, stackList):
+        self.framesTab.LoadFramesList(stackList)
+
+    #
+    # TODO: this is a hack to overwrite BaseDebuggerUI's function.  The purpose
+    # is to always push breakpoints no matter if a php is running or not.  If
+    # no php is running, an exception will be thrown and handled like nothing
+    # happened.
+    #
+    def BreakPointChange(self):
+        self._callback.PushBreakpoints()
+        self.framesTab.PopulateBPList()
+
+
+class PythonDebuggerUI(BaseDebuggerUI):
+    debuggerPortList = None
+
+    def GetAvailablePort():
+        for index in range( 0, len(PythonDebuggerUI.debuggerPortList)):
+            port = PythonDebuggerUI.debuggerPortList[index]
+            if PythonDebuggerUI.PortAvailable(port):
+                PythonDebuggerUI.debuggerPortList.pop(index)
+                return port
+        wx.MessageBox(_("Out of ports for debugging!  Please restart the application builder.\nIf that does not work, check for and remove running instances of python."), _("Out of Ports"))
+        assert False, "Out of ports for debugger."
+
+    GetAvailablePort = staticmethod(GetAvailablePort)
+
+    def ReturnPortToPool(port):
+        config = wx.ConfigBase_Get()
+        startingPort = config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
+        val = int(startingPort) + int(PORT_COUNT)
+        if int(port) >= startingPort and (int(port) <= val):
+            PythonDebuggerUI.debuggerPortList.append(int(port))
+
+    ReturnPortToPool = staticmethod(ReturnPortToPool)
+
+    def PortAvailable(port):
+        config = wx.ConfigBase_Get()
+        hostname = config.Read("DebuggerHostName", DEFAULT_HOST)
+        try:
+            server = AGXMLRPCServer((hostname, port))
+            server.server_close()
+            if _VERBOSE: print "Port ", str(port), " available."
+            return True
+        except:
+            tp,val,tb = sys.exc_info()
+            if _VERBOSE: traceback.print_exception(tp, val, tb)
+            if _VERBOSE: print "Port ", str(port), " unavailable."
+            return False
+
+    PortAvailable = staticmethod(PortAvailable)
+
+    def NewPortRange():
+        config = wx.ConfigBase_Get()
+        startingPort = config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
+        PythonDebuggerUI.debuggerPortList = range(startingPort, startingPort + PORT_COUNT)
+    NewPortRange = staticmethod(NewPortRange)
+
+    def __init__(self, parent, id, command, service, autoContinue=True):
+        # Check for ports before creating the panel.
+        if not PythonDebuggerUI.debuggerPortList:
+            PythonDebuggerUI.NewPortRange()
+        self._debuggerPort = str(PythonDebuggerUI.GetAvailablePort())
+        self._guiPort = str(PythonDebuggerUI.GetAvailablePort())
+        self._debuggerBreakPort = str(PythonDebuggerUI.GetAvailablePort())
+        BaseDebuggerUI.__init__(self, parent, id)
+        self._command = command
+        self._service = service
+        config = wx.ConfigBase_Get()
+        self._debuggerHost = self._guiHost = config.Read("DebuggerHostName", DEFAULT_HOST)
+
+        url = 'http://' + self._debuggerHost + ':' + self._debuggerPort + '/'
+        self._breakURL = 'http://' + self._debuggerHost + ':' + self._debuggerBreakPort + '/'
+        self._callback = PythonDebuggerCallback(self._guiHost, self._guiPort, url, self._breakURL, self, autoContinue)
+        if DebuggerHarness.__file__.find('library.zip') > 0:
+            try:
+                fname = DebuggerHarness.__file__
+                parts = fname.split('library.zip')
+                path = os.path.join(parts[0],'activegrid', 'tool', 'DebuggerHarness.py')
+            except:
+                tp, val, tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+        else:
+            print "Starting debugger on these ports: %s, %s, %s" % (str(self._debuggerPort) , str(self._guiPort) , str(self._debuggerBreakPort))
+            path = DebuggerService.ExpandPath(DebuggerHarness.__file__)
+        self._executor = Executor(path, self, self._debuggerHost, \
+                                                self._debuggerPort, self._debuggerBreakPort, self._guiHost, self._guiPort, self._command, callbackOnExit=self.ExecutorFinished)
+
+        self._stopped = False
+
+    def LoadPythonFramesList(self, framesXML):
+        self.framesTab.LoadFramesList(framesXML)
+
+    def Execute(self, initialArgs, startIn, environment, onWebServer = False):
+        self._callback.Start()
+        self._executor.Execute(initialArgs, startIn, environment)
+        self._callback.WaitForRPC()
+
+
+    def StopExecution(self, event):
+        # This is a general comment on shutdown for the running and debugged processes. Basically, the
+        # current state of this is the result of trial and error coding. The common problems were memory
+        # access violations and threads that would not exit. Making the OutputReaderThreads daemons seems
+        # to have side-stepped the hung thread issue. Being very careful not to touch things after calling
+        # process.py:ProcessOpen.kill() also seems to have fixed the memory access violations, but if there
+        # were more ugliness discovered I would not be surprised. If anyone has any help/advice, please send
+        # it on to mfryer@activegrid.com.
+        if not self._stopped:
+            self._stopped = True
+            try:
+                self.DisableAfterStop()
+            except wx._core.PyDeadObjectError:
+                pass
+            try:
+                self._callback.ShutdownServer()
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+            try:
+                self.DeleteCurrentLineMarkers()
+            except:
+                pass
+            try:
+                PythonDebuggerUI.ReturnPortToPool(self._debuggerPort)
+                PythonDebuggerUI.ReturnPortToPool(self._guiPort)
+                PythonDebuggerUI.ReturnPortToPool(self._debuggerBreakPort)
+            except:
+                pass
+            try:
+                if self._executor:
+                    self._executor.DoStopExecution()
+                    self._executor = None
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+
+    def MakeFramesUI(self, parent, id, debugger):
+        panel = PythonFramesUI(parent, id, self)
+        return panel
+
+
 class BreakpointsUI(wx.Panel):
     def __init__(self, parent, id, ui):
         wx.Panel.__init__(self, parent, id)
 class BreakpointsUI(wx.Panel):
     def __init__(self, parent, id, ui):
         wx.Panel.__init__(self, parent, id)
@@ -763,19 +1048,19 @@ class BreakpointsUI(wx.Panel):
         p1 = self
         self._bpListCtrl = wx.ListCtrl(p1, -1, pos=wx.DefaultPosition, size=(1000,1000), style=wx.LC_REPORT)
         sizer.Add(self._bpListCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
         p1 = self
         self._bpListCtrl = wx.ListCtrl(p1, -1, pos=wx.DefaultPosition, size=(1000,1000), style=wx.LC_REPORT)
         sizer.Add(self._bpListCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
-        self._bpListCtrl.InsertColumn(0, "File")   
-        self._bpListCtrl.InsertColumn(1, "Line")   
-        self._bpListCtrl.InsertColumn(2, "Path")   
+        self._bpListCtrl.InsertColumn(0, "File")
+        self._bpListCtrl.InsertColumn(1, "Line")
+        self._bpListCtrl.InsertColumn(2, "Path")
         self._bpListCtrl.SetColumnWidth(0, 150)
         self._bpListCtrl.SetColumnWidth(1, 50)
         self._bpListCtrl.SetColumnWidth(2, 450)
         self._bpListCtrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnListRightClick)
         self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.ListItemSelected, self._bpListCtrl)
         self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.ListItemDeselected, self._bpListCtrl)
         self._bpListCtrl.SetColumnWidth(0, 150)
         self._bpListCtrl.SetColumnWidth(1, 50)
         self._bpListCtrl.SetColumnWidth(2, 450)
         self._bpListCtrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnListRightClick)
         self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.ListItemSelected, self._bpListCtrl)
         self.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.ListItemDeselected, self._bpListCtrl)
-        
+
         def OnLeftDoubleClick(event):
             self.SyncBPLine(event)
         def OnLeftDoubleClick(event):
             self.SyncBPLine(event)
-            
+
         wx.EVT_LEFT_DCLICK(self._bpListCtrl, OnLeftDoubleClick)
 
         self.PopulateBPList()
         wx.EVT_LEFT_DCLICK(self._bpListCtrl, OnLeftDoubleClick)
 
         self.PopulateBPList()
@@ -783,7 +1068,7 @@ class BreakpointsUI(wx.Panel):
         p1.SetSizer(sizer)
         sizer.Fit(p1)
         p1.Layout()
         p1.SetSizer(sizer)
         sizer.Fit(p1)
         p1.Layout()
-        
+
     def PopulateBPList(self):
         list = self._bpListCtrl
         list.DeleteAllItems()
     def PopulateBPList(self):
         list = self._bpListCtrl
         list.DeleteAllItems()
@@ -798,7 +1083,7 @@ class BreakpointsUI(wx.Panel):
                     list.InsertStringItem(index, shortFile)
                     list.SetStringItem(index, 1, str(line))
                     list.SetStringItem(index, 2, fileName)
                     list.InsertStringItem(index, shortFile)
                     list.SetStringItem(index, 1, str(line))
                     list.SetStringItem(index, 2, fileName)
-                    
+
     def OnListRightClick(self, event):
         menu = wx.Menu()
         item = wx.MenuItem(menu, self.clearBPID, "Clear Breakpoint")
     def OnListRightClick(self, event):
         menu = wx.Menu()
         item = wx.MenuItem(menu, self.clearBPID, "Clear Breakpoint")
@@ -813,32 +1098,32 @@ class BreakpointsUI(wx.Panel):
             list = self._bpListCtrl
             fileName = list.GetItem(self.currentItem, 2).GetText()
             lineNumber = list.GetItem(self.currentItem, 1).GetText()
             list = self._bpListCtrl
             fileName = list.GetItem(self.currentItem, 2).GetText()
             lineNumber = list.GetItem(self.currentItem, 1).GetText()
-            self._ui.SynchCurrentLine( fileName, int(lineNumber) , noArrow=True)  
+            self._ui.SynchCurrentLine( fileName, int(lineNumber) , noArrow=True)
 
     def ClearBreakPoint(self, event):
         if self.currentItem >= 0:
             list = self._bpListCtrl
             fileName = list.GetItem(self.currentItem, 2).GetText()
             lineNumber = list.GetItem(self.currentItem, 1).GetText()
 
     def ClearBreakPoint(self, event):
         if self.currentItem >= 0:
             list = self._bpListCtrl
             fileName = list.GetItem(self.currentItem, 2).GetText()
             lineNumber = list.GetItem(self.currentItem, 1).GetText()
-            wx.GetApp().GetService(DebuggerService).OnToggleBreakpoint(None, line=int(lineNumber) -1, fileName=fileName )  
-        
+            wx.GetApp().GetService(DebuggerService).OnToggleBreakpoint(None, line=int(lineNumber) -1, fileName=fileName )
+
     def ListItemSelected(self, event):
         self.currentItem = event.m_itemIndex
 
     def ListItemDeselected(self, event):
         self.currentItem = -1
     def ListItemSelected(self, event):
         self.currentItem = event.m_itemIndex
 
     def ListItemDeselected(self, event):
         self.currentItem = -1
+
 class Watch:
     CODE_ALL_FRAMES = 1
     CODE_THIS_BLOCK = 2
     CODE_THIS_LINE = 4
     CODE_RUN_ONCE = 8
 class Watch:
     CODE_ALL_FRAMES = 1
     CODE_THIS_BLOCK = 2
     CODE_THIS_LINE = 4
     CODE_RUN_ONCE = 8
-    
+
     def __init__(self, name, command, show_code=CODE_ALL_FRAMES):
         self._name = name
     def __init__(self, name, command, show_code=CODE_ALL_FRAMES):
         self._name = name
-        self._command = command 
+        self._command = command
         self._show_code = show_code
         self._show_code = show_code
-               
+
 class WatchDialog(wx.Dialog):
     WATCH_ALL_FRAMES = "Watch in all frames"
     WATCH_THIS_FRAME = "Watch in this frame only"
 class WatchDialog(wx.Dialog):
     WATCH_ALL_FRAMES = "Watch in all frames"
     WATCH_THIS_FRAME = "Watch in this frame only"
@@ -865,22 +1150,22 @@ class WatchDialog(wx.Dialog):
                 return
             self.EndModal(wx.ID_OK)
         self.Bind(wx.EVT_BUTTON, OnOkClick, self._okButton)
                 return
             self.EndModal(wx.ID_OK)
         self.Bind(wx.EVT_BUTTON, OnOkClick, self._okButton)
-            
+
         self._cancelButton = wx.Button(self, wx.ID_CANCEL, _("Cancel"))
         self._cancelButton.SetHelpText(_("The Cancel button cancels the dialog."))
         self._cancelButton = wx.Button(self, wx.ID_CANCEL, _("Cancel"))
         self._cancelButton.SetHelpText(_("The Cancel button cancels the dialog."))
-        
+
         self.__set_properties()
         self.__do_layout()
         self.__set_properties()
         self.__do_layout()
-        
+
     def GetSettings(self):
         return self._watchNameTextCtrl.GetValue(), self._watchValueTextCtrl.GetValue(), self.GetSendFrame(), self.GetRunOnce()
     def GetSettings(self):
         return self._watchNameTextCtrl.GetValue(), self._watchValueTextCtrl.GetValue(), self.GetSendFrame(), self.GetRunOnce()
-    
+
     def GetSendFrame(self):
         return (WatchDialog.WATCH_ALL_FRAMES != self.radio_box_1.GetStringSelection())
     def GetSendFrame(self):
         return (WatchDialog.WATCH_ALL_FRAMES != self.radio_box_1.GetStringSelection())
-        
+
     def GetRunOnce(self):
         return (WatchDialog.WATCH_ONCE == self.radio_box_1.GetStringSelection())
     def GetRunOnce(self):
         return (WatchDialog.WATCH_ONCE == self.radio_box_1.GetStringSelection())
-        
+
     def __set_properties(self):
         self.SetTitle("Add a Watch")
         #self.SetSize((400, 250))
     def __set_properties(self):
         self.SetTitle("Add a Watch")
         #self.SetSize((400, 250))
@@ -900,15 +1185,15 @@ class WatchDialog(wx.Dialog):
         grid_sizer_4.Add(self.label_4, 0, wx.ALIGN_CENTER_VERTICAL|wx.FIXED_MINSIZE, 0)
         sizer_1.Add(grid_sizer_4, 0, wx.EXPAND, 0)
         sizer_1.Add(self.radio_box_1, 0, wx.EXPAND, 0)
         grid_sizer_4.Add(self.label_4, 0, wx.ALIGN_CENTER_VERTICAL|wx.FIXED_MINSIZE, 0)
         sizer_1.Add(grid_sizer_4, 0, wx.EXPAND, 0)
         sizer_1.Add(self.radio_box_1, 0, wx.EXPAND, 0)
-        
+
         box = wx.BoxSizer(wx.HORIZONTAL)
         box.Add(self._okButton, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
         box.Add(self._cancelButton, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
         sizer_1.Add(box, 1, wx.EXPAND, 0)
         self.SetSizer(sizer_1)
         self.Layout()
         box = wx.BoxSizer(wx.HORIZONTAL)
         box.Add(self._okButton, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
         box.Add(self._cancelButton, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
         sizer_1.Add(box, 1, wx.EXPAND, 0)
         self.SetSizer(sizer_1)
         self.Layout()
-        
-class FramesUI(wx.SplitterWindow):
+
+class BaseFramesUI(wx.SplitterWindow):
     def __init__(self, parent, id, ui):
         wx.SplitterWindow.__init__(self, parent, id, style = wx.SP_3D)
         self._ui = ui
     def __init__(self, parent, id, ui):
         wx.SplitterWindow.__init__(self, parent, id, style = wx.SP_3D)
         self._ui = ui
@@ -917,7 +1202,7 @@ class FramesUI(wx.SplitterWindow):
         sizer = wx.BoxSizer(wx.HORIZONTAL)
         framesLabel = wx.StaticText(self, -1, "Stack Frame:")
         sizer.Add(framesLabel, 0, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.LEFT, border=2)
         sizer = wx.BoxSizer(wx.HORIZONTAL)
         framesLabel = wx.StaticText(self, -1, "Stack Frame:")
         sizer.Add(framesLabel, 0, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.LEFT, border=2)
-                               
+
         self._framesChoiceCtrl = wx.Choice(p1, -1, choices=["                                           "])
         sizer.Add(self._framesChoiceCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
         self._framesChoiceCtrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnListRightClick)
         self._framesChoiceCtrl = wx.Choice(p1, -1, choices=["                                           "])
         sizer.Add(self._framesChoiceCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
         self._framesChoiceCtrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnListRightClick)
@@ -925,21 +1210,20 @@ class FramesUI(wx.SplitterWindow):
 
         sizer2 = wx.BoxSizer(wx.VERTICAL)
         p1.SetSizer(sizer2)
 
         sizer2 = wx.BoxSizer(wx.VERTICAL)
         p1.SetSizer(sizer2)
-        
         self._treeCtrl = wx.gizmos.TreeListCtrl(p1, -1, style=wx.TR_DEFAULT_STYLE| wx.TR_FULL_ROW_HIGHLIGHT)
         self._treeCtrl.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnRightClick)
         sizer2.Add(sizer, 0, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
         sizer2.Add(self._treeCtrl,1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
         self._treeCtrl = wx.gizmos.TreeListCtrl(p1, -1, style=wx.TR_DEFAULT_STYLE| wx.TR_FULL_ROW_HIGHLIGHT)
         self._treeCtrl.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnRightClick)
         sizer2.Add(sizer, 0, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
         sizer2.Add(self._treeCtrl,1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
-        tree = self._treeCtrl                           
+        tree = self._treeCtrl
         tree.AddColumn("Thing")
         tree.AddColumn("Value")
         tree.SetMainColumn(0) # the one with the tree in it...
         tree.SetColumnWidth(0, 175)
         tree.SetColumnWidth(1, 355)
         self._root = tree.AddRoot("Frame")
         tree.AddColumn("Thing")
         tree.AddColumn("Value")
         tree.SetMainColumn(0) # the one with the tree in it...
         tree.SetColumnWidth(0, 175)
         tree.SetColumnWidth(1, 355)
         self._root = tree.AddRoot("Frame")
+        tree.SetPyData(self._root, "root")
         tree.SetItemText(self._root, "", 1)
         tree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.IntrospectCallback)
         tree.SetItemText(self._root, "", 1)
         tree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.IntrospectCallback)
-
         self._p2 = p2 = wx.Window(self, -1)
         sizer3 = wx.BoxSizer(wx.HORIZONTAL)
         p2.SetSizer(sizer3)
         self._p2 = p2 = wx.Window(self, -1)
         sizer3 = wx.BoxSizer(wx.HORIZONTAL)
         p2.SetSizer(sizer3)
@@ -953,18 +1237,59 @@ class FramesUI(wx.SplitterWindow):
         self._notebook.AddPage(self.consoleTab, "Output")
         self._notebook.AddPage(self.inspectConsoleTab, "Interact")
         self._notebook.AddPage(self.breakPointsTab, "Break Points")
         self._notebook.AddPage(self.consoleTab, "Output")
         self._notebook.AddPage(self.inspectConsoleTab, "Interact")
         self._notebook.AddPage(self.breakPointsTab, "Break Points")
-
         self.SetMinimumPaneSize(20)
         self.SplitVertically(p1, p2, 550)
         self.currentItem = None
         self._notebook.Show(True)
         self.SetMinimumPaneSize(20)
         self.SplitVertically(p1, p2, 550)
         self.currentItem = None
         self._notebook.Show(True)
-   
+
     def PopulateBPList(self):
         self.breakPointsTab.PopulateBPList()
     def PopulateBPList(self):
         self.breakPointsTab.PopulateBPList()
-        
+
     def OnSize(self, event):
         self._notebook.SetSize(self._p2.GetSize())
     def OnSize(self, event):
         self._notebook.SetSize(self._p2.GetSize())
-             
+
+    def OnDoubleClick(self, event):
+        # Looking for a stack trace line.
+        lineText, pos = self._textCtrl.GetCurLine()
+        fileBegin = lineText.find("File \"")
+        fileEnd = lineText.find("\", line ")
+        lineEnd = lineText.find(", in ")
+        if lineText == "\n" or  fileBegin == -1 or fileEnd == -1 or lineEnd == -1:
+            # Check the line before the one that was clicked on
+            lineNumber = self._textCtrl.GetCurrentLine()
+            if(lineNumber == 0):
+                return
+            lineText = self._textCtrl.GetLine(lineNumber - 1)
+            fileBegin = lineText.find("File \"")
+            fileEnd = lineText.find("\", line ")
+            lineEnd = lineText.find(", in ")
+            if lineText == "\n" or  fileBegin == -1 or fileEnd == -1 or lineEnd == -1:
+                return
+
+        filename = lineText[fileBegin + 6:fileEnd]
+        lineNum = int(lineText[fileEnd + 8:lineEnd])
+
+        foundView = None
+        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+        for openDoc in openDocs:
+            if openDoc.GetFilename() == filename:
+                foundView = openDoc.GetFirstView()
+                break
+
+        if not foundView:
+            doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT)
+            foundView = doc.GetFirstView()
+
+        if foundView:
+            foundView.GetFrame().SetFocus()
+            foundView.Activate()
+            foundView.GotoLine(lineNum)
+            startPos = foundView.PositionFromLine(lineNum)
+            lineText = foundView.GetCtrl().GetLine(lineNum - 1)
+            foundView.SetSelection(startPos, startPos + len(lineText.rstrip("\n")))
+            import OutlineService
+            wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
+
     def MakeConsoleTab(self, parent, id):
         panel = wx.Panel(parent, id)
         sizer = wx.BoxSizer(wx.HORIZONTAL)
     def MakeConsoleTab(self, parent, id):
         panel = wx.Panel(parent, id)
         sizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -979,92 +1304,312 @@ class FramesUI(wx.SplitterWindow):
         self._textCtrl.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font))
         self._textCtrl.SetFontColor(wx.BLACK)
         self._textCtrl.StyleClearAll()
         self._textCtrl.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font))
         self._textCtrl.SetFontColor(wx.BLACK)
         self._textCtrl.StyleClearAll()
+        wx.stc.EVT_STC_DOUBLECLICK(self._textCtrl, self._textCtrl.GetId(), self.OnDoubleClick)
+
         panel.SetSizer(sizer)
         #sizer.Fit(panel)
 
         return panel
         panel.SetSizer(sizer)
         #sizer.Fit(panel)
 
         return panel
-
-    def ReplaceLastLine(self, command):
-        line = self._interCtrl.GetLineCount() - 1
-        self._interCtrl.GotoLine(line)
-        start = self._interCtrl.GetCurrentPos()
-        self._interCtrl.SetTargetStart(start)
-        end = self._interCtrl.GetLineEndPosition(line)
-        self._interCtrl.SetTargetEnd(end)
-        self._interCtrl.ReplaceTarget(">>> " + command)
-        self._interCtrl.GotoLine(line)
-        self._interCtrl.SetSelectionStart(self._interCtrl.GetLineEndPosition(line))
-
     def ExecuteCommand(self, command):
     def ExecuteCommand(self, command):
-        if not len(self.command_list) or not command == self.command_list[len(self.command_list) -1]:
-            self.command_list.append(command)
-            self.command_index = len(self.command_list) - 1
-        retval = self._ui._callback._debuggerServer.execute_in_frame(self._framesChoiceCtrl.GetStringSelection(), command)
-        self._interCtrl.AddText("\n" + str(retval))
-        self._interCtrl.ScrollToLine(self._interCtrl.GetLineCount())
-        # Refresh the tree view in case this command resulted in changes there. TODO: Need to reopen tree items.
-        self.PopulateTreeFromFrameMessage(self._framesChoiceCtrl.GetStringSelection())
-        
+        assert False, "ExecuteCommand not overridden"
+
     def MakeInspectConsoleTab(self, parent, id):
     def MakeInspectConsoleTab(self, parent, id):
-        self.command_list = []
-        self.command_index = 0
-            
+        def handleCommand():
+            cmdStr = self._cmdInput.GetValue()
+            if cmdStr:
+                self._cmdList.append(cmdStr)
+                self._cmdIndex = len(self._cmdList)
+            self._cmdInput.Clear()
+            self._cmdOutput.SetDefaultStyle(style=self._cmdOutputTextStyle)
+            self._cmdOutput.AppendText(">>> " + cmdStr + "\n")
+            self._cmdOutput.SetDefaultStyle(style=self._defaultOutputTextStyle)
+            self.ExecuteCommand(cmdStr)
+            return
+
+        def OnCmdButtonPressed(event):
+            handleCommand()
+            return
+
         def OnKeyPressed(event):
             key = event.KeyCode()
         def OnKeyPressed(event):
             key = event.KeyCode()
-            if key == wx.WXK_DELETE or key == wx.WXK_BACK:
-                if self._interCtrl.GetLine(self._interCtrl.GetCurrentLine()) == ">>> ":
-                    return
-            elif key == wx.WXK_RETURN:
-                command = self._interCtrl.GetLine(self._interCtrl.GetCurrentLine())[4:]
-                self.ExecuteCommand(command)             
-                self._interCtrl.AddText("\n>>> ")
-                return
+            if key == wx.WXK_RETURN:
+                handleCommand()
             elif key == wx.WXK_UP:
             elif key == wx.WXK_UP:
-                if not len(self.command_list):
+                if len(self._cmdList) < 1 or self._cmdIndex < 1:
                     return
                     return
-                self.ReplaceLastLine(self.command_list[self.command_index])
-                if self.command_index  == 0:
-                    self.command_index  = len(self.command_list) - 1
-                else:
-                    self.command_index = self.command_index - 1
-                return
+
+                self._cmdInput.Clear()
+                self._cmdInput.AppendText(self._cmdList[self._cmdIndex - 1])
+                self._cmdIndex = self._cmdIndex - 1
             elif key == wx.WXK_DOWN:
             elif key == wx.WXK_DOWN:
-                if not len(self.command_list):
+                if len(self._cmdList) < 1 or self._cmdIndex >= len(self._cmdList):
                     return
                     return
-                if self.command_index  < len(self.command_list) - 1:
-                    self.command_index = self.command_index + 1
-                else:
-                    self.command_index = 0
-                self.ReplaceLastLine(self.command_list[self.command_index])                        
-                return
-            event.Skip()
 
 
-        try:    
-            panel = wx.Panel(parent, id)
-            sizer = wx.BoxSizer(wx.HORIZONTAL)
-            self._interCtrl = STCTextEditor.TextCtrl(panel, wx.NewId())
-            sizer.Add(self._interCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 2)
-            self._interCtrl.SetViewLineNumbers(False)
-            if wx.Platform == '__WXMSW__':
-                font = "Courier New"
+                self._cmdInput.Clear()
+                self._cmdInput.AppendText(self._cmdList[self._cmdIndex - 1])
+                self._cmdIndex = self._cmdIndex + 1
             else:
             else:
-                font = "Courier"
-            self._interCtrl.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font))
-            self._interCtrl.SetFontColor(wx.BLACK)
-            self._interCtrl.StyleClearAll()
-            wx.EVT_KEY_DOWN(self._interCtrl, OnKeyPressed)
-            self._interCtrl.AddText(">>> ")
-            panel.SetSizer(sizer)
-        except:
-            tp, val, tb = sys.exc_info()
-            traceback.print_exception(tp, val, tb)   
-            
-        return panel 
-               
+                event.Skip()
+            return
+
+        def OnClrButtonPressed(event):
+            self._cmdOutput.Clear()
+
+        panel           = wx.Panel(parent, id)
+
+        cmdLabel        = wx.StaticText(panel, -1, "Cmd: ")
+        self._cmdInput  = wx.TextCtrl(panel)
+        cmdButton       = wx.Button(panel, label="Execute")
+        clrButton       = wx.Button(panel, label="Clear")
+        self._cmdOutput = wx.TextCtrl(panel, style=wx.TE_MULTILINE | wx.HSCROLL | wx.TE_READONLY | wx.TE_RICH2)
+
+        hbox            = wx.BoxSizer()
+        hbox.Add(cmdLabel, proportion=0, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
+        hbox.Add(self._cmdInput, proportion=1, flag=wx.EXPAND)
+        hbox.Add(cmdButton, proportion=0, flag=wx.RIGHT)
+        hbox.Add(clrButton, proportion=0, flag=wx.RIGHT)
+
+        vbox            = wx.BoxSizer(wx.VERTICAL)
+        vbox.Add(hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=2)
+        vbox.Add(self._cmdOutput, proportion=1, flag=wx.EXPAND | wx.LEFT, border=2)
+
+        panel.SetSizer(vbox)
+        cmdButton.Bind(wx.EVT_BUTTON, OnCmdButtonPressed)
+        clrButton.Bind(wx.EVT_BUTTON, OnClrButtonPressed)
+        wx.EVT_KEY_DOWN(self._cmdInput, OnKeyPressed)
+
+        fixedFont = wx.Font(self._cmdInput.GetFont().GetPointSize(), family=wx.TELETYPE, style=wx.NORMAL, weight=wx.NORMAL)
+        self._defaultOutputTextStyle = wx.TextAttr("BLACK", wx.NullColour, fixedFont)
+        self._cmdOutputTextStyle = wx.TextAttr("RED", wx.NullColour, fixedFont)
+        self._cmdOutput.SetDefaultStyle(style=self._defaultOutputTextStyle)
+        self._cmdList  = []
+        self._cmdIndex = 0
+
+        panel.Show()
+        return panel
+
     def MakeBreakPointsTab(self, parent, id):
         panel = BreakpointsUI(parent, id, self._ui)
         return panel
     def MakeBreakPointsTab(self, parent, id):
         panel = BreakpointsUI(parent, id, self._ui)
         return panel
-     
+
+    def OnRightClick(self, event):
+        assert False, "OnRightClick not overridden"
+
+    def ClearWhileRunning(self):
+        list = self._framesChoiceCtrl
+        list.Clear()
+        list.Enable(False)
+        tree = self._treeCtrl
+        root = self._root
+        tree.DeleteChildren(root)
+        self._cmdInput.Enable(False)
+        self._cmdOutput.Enable(False)
+
+    def OnListRightClick(self, event):
+        if not hasattr(self, "syncFrameID"):
+            self.syncFrameID = wx.NewId()
+            self.Bind(wx.EVT_MENU, self.OnSyncFrame, id=self.syncFrameID)
+        menu = wx.Menu()
+        item = wx.MenuItem(menu, self.syncFrameID, "Goto Source Line")
+        menu.AppendItem(item)
+        self.PopupMenu(menu, event.GetPosition())
+        menu.Destroy()
+
+    def OnSyncFrame(self, event):
+        assert False, "OnSyncFrame not overridden"
+
+    def LoadFramesList(self, framesXML):
+        assert False, "LoadFramesList not overridden"
+
+    def ListItemSelected(self, event):
+        assert False, "ListItemSelected not overridden"
+
+    def PopulateTreeFromFrameMessage(self, message):
+        assert False, "PopulateTreeFromFrameMessage not overridden"
+
+    def IntrospectCallback(self, event):
+        assert False, "IntrospectCallback not overridden"
+
+    def AppendText(self, text):
+        self._textCtrl.SetReadOnly(False)
+        self._textCtrl.AddText(text)
+        self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
+        self._textCtrl.SetReadOnly(True)
+
+    def AppendErrorText(self, text):
+        self._textCtrl.SetReadOnly(False)
+        self._textCtrl.SetFontColor(wx.RED)
+        self._textCtrl.StyleClearAll()
+        self._textCtrl.AddText(text)
+        self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
+        self._textCtrl.SetFontColor(wx.BLACK)
+        self._textCtrl.StyleClearAll()
+        self._textCtrl.SetReadOnly(True)
+
+    def ClearOutput(self, event):
+        self._textCtrl.SetReadOnly(False)
+        self._textCtrl.ClearAll()
+        self._textCtrl.SetReadOnly(True)
+
+    def SwitchToOutputTab(self):
+        self._notebook.SetSelection(0)
+
+class PHPFramesUI(BaseFramesUI):
+    def __init__(self, parent, id, ui):
+        BaseFramesUI.__init__(self, parent, id, ui)
+
+    def LoadFramesList(self, stackList):
+        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+
+        self._cmdInput.Enable(True)
+        self._cmdOutput.Enable(True)
+        index       = 0
+        self._stack = stackList
+        list        = self._framesChoiceCtrl
+        list.Clear()
+
+        if len(stackList) > 0:
+            self._displayVariableTreeRootNode()
+
+            for stackFrame in stackList:
+                message = stackFrame.getDisplayStr(stackList)
+                list.Append(message)
+
+            self.currentItem = index
+            list.SetSelection(index)
+            list.Enable(True)
+            self.OnSyncFrame(None)
+            self._p1.FitInside()
+
+        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+
+    def PopulateTreeFromStackFrame(self, frameNode):
+        vars         = frameNode.getVariables()
+        tree         = self._treeCtrl
+        rootTreeNode = self._root
+
+        #
+        # Build a new sub variable tree from the root node.
+        #
+        tree.DeleteChildren(rootTreeNode)
+        for var in vars:
+            aTreeNode = self.AppendSubTreeFromAVariable(tree, var, rootTreeNode)
+
+        #
+        # No need to expand the node here, as the IntrospectCallback() has
+        # already called it.
+        #
+        self._p2.FitInside()
+
+    def AppendSubTreeFromAVariable(self, tree, var, parentTreeNode, previousOne = None):
+        varName     = var.getName()
+        varValueStr = var.getValueString()
+
+        #
+        # If previously we already have this item in the tree, replace it.
+        # Otherwise, insert a new one.
+        #
+        if previousOne:
+            newNode = tree.InsertItem(parentTreeNode, previousOne, varName)
+        else:
+            newNode = tree.AppendItem(parentTreeNode, varName)
+
+        #
+        # Associate this variable object with this newNode.
+        #
+        tree.SetPyData(newNode, var)
+
+        #
+        # Set this variable's value string (for displaying).
+        #
+        if varValueStr and len(varValueStr) > 0:
+            tree.SetItemText(newNode, varValueStr, 1)
+
+        #
+        # If this variable has child variables, recursively build the sub
+        # variable tree.
+        #
+        if var.hasChildren():
+            childrenVarList = var.getChildrenVariables()
+            for childVar in childrenVarList:
+                self.AppendSubTreeFromAVariable(tree, childVar, newNode)
+
+            #
+            # If its child variables are sortable, sort it.
+            #
+            if var.childrenIsSortable():
+                tree.SortChildren(newNode)
+
+        return newNode
+
+    def IntrospectCallback(self, event):
+        tree = self._treeCtrl
+        item = event.GetItem()
+
+        #
+        # Only when the introspection happens to root, we get the whole
+        # variable tree.  For all the individual nodes, we have populated
+        # the subtree already, so don't need to do anything.
+        #
+        if tree.GetPyData(item) == "root" and self._stack and self._stack[self.currentItem]:
+            stackFrame = self._stack[self.currentItem]
+            self.PopulateTreeFromStackFrame(stackFrame)
+
+        event.Skip()
+
+    def OnSyncFrame(self, event):
+        stackFrame = self._stack[self.currentItem]
+        fileName   = stackFrame.getFileName()
+        lineNo     = stackFrame.getLineNo()
+        if _VERBOSE:
+            print "OnSyncFrame(): about to sync: fileName: %s, lineNo: %d" % (fileName, lineNo)
+        self._ui.SynchCurrentLine(fileName, lineNo)
+        if _VERBOSE:
+            print "OnSyncFrame(): sync done"
+
+    def ListItemSelected(self, event):
+        selectedStackFrameStr = event.GetString()
+
+        if not self._stack or len(self._stack) < 1:
+            return
+
+        found = False
+        for stackFrame in self._stack:
+            if stackFrame.getDisplayStr() == selectedStackFrameStr:
+                self.currentItem = stackFrame.getFrameIndex()
+                found = True
+                break
+
+        if found:
+            self._displayVariableTreeRootNode()
+            self.OnSyncFrame(None)
+
+        return
+
+    def _displayVariableTreeRootNode(self):
+        #
+        # Add a dummy item to rootTreeNode so that it will be shown as
+        # expandable.  Only do real tree population on the fly when the
+        # rootTreeNode is expanded in OnIntrospection().
+        #
+        tree         = self._treeCtrl
+        rootTreeNode = self._root
+        dummyNode    = tree.AppendItem(rootTreeNode, "dummy")
+        tree.Collapse(rootTreeNode)
+
+        return
+
+
+class PythonFramesUI(BaseFramesUI):
+    def __init__(self, parent, id, ui):
+        BaseFramesUI.__init__(self, parent, id, ui)
+
+    def ExecuteCommand(self, command):
+        retval = self._ui._callback._debuggerServer.execute_in_frame(self._framesChoiceCtrl.GetStringSelection(), command)
+        self._cmdOutput.AppendText(str(retval) + "\n")
+        # Refresh the tree view in case this command resulted in changes there. TODO: Need to reopen tree items.
+        self.PopulateTreeFromFrameMessage(self._framesChoiceCtrl.GetStringSelection())
+
     def OnRightClick(self, event):
         #Refactor this...
         self._introspectItem = event.GetItem()
     def OnRightClick(self, event):
         #Refactor this...
         self._introspectItem = event.GetItem()
@@ -1098,7 +1643,7 @@ class FramesUI(wx.SplitterWindow):
         menu.Destroy()
         self._parentChain = None
         self._introspectItem = None
         menu.Destroy()
         self._parentChain = None
         self._introspectItem = None
-        
+
     def GetItemChain(self, item):
         parentChain = []
         if item:
     def GetItemChain(self, item):
         parentChain = []
         if item:
@@ -1110,18 +1655,18 @@ class FramesUI(wx.SplitterWindow):
                 item = self._treeCtrl.GetItemParent(item)
             parentChain.reverse()
         return parentChain
                 item = self._treeCtrl.GetItemParent(item)
             parentChain.reverse()
         return parentChain
-    
+
     def OnView(self, event):
         title = self._treeCtrl.GetItemText(self._introspectItem,0)
         value = self._treeCtrl.GetItemText(self._introspectItem,1)
         dlg = wx.lib.dialogs.ScrolledMessageDialog(self, value, title, style=wx.DD_DEFAULT_STYLE | wx.RESIZE_BORDER)
         dlg.Show()
     def OnView(self, event):
         title = self._treeCtrl.GetItemText(self._introspectItem,0)
         value = self._treeCtrl.GetItemText(self._introspectItem,1)
         dlg = wx.lib.dialogs.ScrolledMessageDialog(self, value, title, style=wx.DD_DEFAULT_STYLE | wx.RESIZE_BORDER)
         dlg.Show()
-        
+
     def OnSendToInteract(self, event):
         value = ""
         prevItem = ""
         for item in self._parentChain:
     def OnSendToInteract(self, event):
         value = ""
         prevItem = ""
         for item in self._parentChain:
-            
+
             if item.find(prevItem + '[') != -1:
                value += item[item.find('['):]
                continue
             if item.find(prevItem + '[') != -1:
                value += item[item.find('['):]
                continue
@@ -1133,9 +1678,8 @@ class FramesUI(wx.SplitterWindow):
                 value += item
                 prevItem = item
         print value
                 value += item
                 prevItem = item
         print value
-        self.ReplaceLastLine(value)
         self.ExecuteCommand(value)
         self.ExecuteCommand(value)
-        
+
     def OnWatch(self, event):
         try:
             if hasattr(self, '_parentChain'):
     def OnWatch(self, event):
         try:
             if hasattr(self, '_parentChain'):
@@ -1147,7 +1691,7 @@ class FramesUI(wx.SplitterWindow):
                 name, text, send_frame, run_once = wd.GetSettings()
                 if send_frame:
                     frameNode = self._stack[int(self.currentItem)]
                 name, text, send_frame, run_once = wd.GetSettings()
                 if send_frame:
                     frameNode = self._stack[int(self.currentItem)]
-                    message = frameNode.getAttribute("message") 
+                    message = frameNode.getAttribute("message")
                 else:
                     message = ""
                 binType = self._ui._callback._debuggerServer.add_watch(name, text, message, run_once)
                 else:
                     message = ""
                 binType = self._ui._callback._debuggerServer.add_watch(name, text, message, run_once)
@@ -1159,106 +1703,90 @@ class FramesUI(wx.SplitterWindow):
             wd.Destroy()
         except:
             tp, val, tb = sys.exc_info()
             wd.Destroy()
         except:
             tp, val, tb = sys.exc_info()
-            traceback.print_exception(tp, val, tb)   
-            
+            traceback.print_exception(tp, val, tb)
+
     def OnIntrospect(self, event):
         wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
 
         try:
     def OnIntrospect(self, event):
         wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
 
         try:
-            list = self._framesChoiceCtrl
-            frameNode = self._stack[int(self.currentItem)]
-            message = frameNode.getAttribute("message") 
-            binType = self._ui._callback._debuggerServer.attempt_introspection(message, self._parentChain)
-            xmldoc = bz2.decompress(binType.data)
-            domDoc = parseString(xmldoc)
-            #wx.MessageBox(xmldoc, "result of introspection")
-            nodeList = domDoc.getElementsByTagName('replacement')
-            replacementNode = nodeList.item(0)
-            if len(replacementNode.childNodes):
-                thingToWalk = replacementNode.childNodes.item(0)
-                tree = self._treeCtrl
-                parent = tree.GetItemParent(self._introspectItem)
-                treeNode = self.AppendSubTreeFromNode(thingToWalk, thingToWalk.getAttribute('name'), parent, insertBefore=self._introspectItem)
-                if thingToWalk.getAttribute('name').find('[') == -1:
-                    self._treeCtrl.SortChildren(treeNode)
-                self._treeCtrl.Expand(treeNode)
-                tree.Delete(self._introspectItem)
-        except:
-            tp,val,tb = sys.exc_info()
-            traceback.print_exception(tp, val, tb)
-            
-        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-        
-    def ClearWhileRunning(self):
-        list = self._framesChoiceCtrl
-        list.Clear()
-        list.Enable(False)
-        tree = self._treeCtrl  
-        root = self._root   
-        tree.DeleteChildren(root)
-        self._interCtrl.Enable(False)
-   
-        #tree.Hide()
-        
-    def OnListRightClick(self, event):
-        if not hasattr(self, "syncFrameID"):
-            self.syncFrameID = wx.NewId()
-            self.Bind(wx.EVT_MENU, self.OnSyncFrame, id=self.syncFrameID)
-        menu = wx.Menu()
-        item = wx.MenuItem(menu, self.syncFrameID, "Goto Source Line")
-        menu.AppendItem(item)
-        self.PopupMenu(menu, event.GetPosition())
-        menu.Destroy()
+
+            try:
+                list = self._framesChoiceCtrl
+                frameNode = self._stack[int(self.currentItem)]
+                message = frameNode.getAttribute("message")
+                binType = self._ui._callback._debuggerServer.attempt_introspection(message, self._parentChain)
+                xmldoc = bz2.decompress(binType.data)
+                domDoc = parseString(xmldoc)
+                nodeList = domDoc.getElementsByTagName('replacement')
+                replacementNode = nodeList.item(0)
+                if len(replacementNode.childNodes):
+                    thingToWalk = replacementNode.childNodes.item(0)
+                    tree = self._treeCtrl
+                    parent = tree.GetItemParent(self._introspectItem)
+                    treeNode = self.AppendSubTreeFromNode(thingToWalk, thingToWalk.getAttribute('name'), parent, insertBefore=self._introspectItem)
+                    if thingToWalk.getAttribute('name').find('[') == -1:
+                        self._treeCtrl.SortChildren(treeNode)
+                    self._treeCtrl.Expand(treeNode)
+                    tree.Delete(self._introspectItem)
+            except:
+                tp,val,tb = sys.exc_info()
+                traceback.print_exception(tp, val, tb)
+
+        finally:
+            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
     def OnSyncFrame(self, event):
         list = self._framesChoiceCtrl
         frameNode = self._stack[int(self.currentItem)]
         file = frameNode.getAttribute("file")
         line = frameNode.getAttribute("line")
 
     def OnSyncFrame(self, event):
         list = self._framesChoiceCtrl
         frameNode = self._stack[int(self.currentItem)]
         file = frameNode.getAttribute("file")
         line = frameNode.getAttribute("line")
-        self._ui.SynchCurrentLine( file, int(line) )  
-        
-    def LoadFramesListXML(self, framesXML):
-        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-        self._interCtrl.Enable(True)
+        self._ui.SynchCurrentLine( file, int(line) )
 
 
+    def LoadFramesList(self, framesXML):
+        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
         try:
         try:
-            domDoc = parseString(framesXML)
-            list = self._framesChoiceCtrl
-            list.Clear()
-            self._stack = []
-            nodeList = domDoc.getElementsByTagName('frame')
-            frame_count = -1
-            for index in range(0, nodeList.length):
+            self._cmdInput.Enable(True)
+            self._cmdOutput.Enable(True)
+
+            try:
+                domDoc = parseString(framesXML)
+                list = self._framesChoiceCtrl
+                list.Clear()
+                self._stack = []
+                nodeList = domDoc.getElementsByTagName('frame')
+                frame_count = -1
+                for index in range(0, nodeList.length):
+                    frameNode = nodeList.item(index)
+                    message = frameNode.getAttribute("message")
+                    list.Append(message)
+                    self._stack.append(frameNode)
+                    frame_count += 1
+                index = len(self._stack) - 1
+                list.SetSelection(index)
+
+                node = self._stack[index]
+                self.currentItem = index
+                self.PopulateTreeFromFrameNode(node)
+                self.OnSyncFrame(None)
+
+                self._p1.FitInside()
                 frameNode = nodeList.item(index)
                 frameNode = nodeList.item(index)
-                message = frameNode.getAttribute("message")
-                list.Append(message)
-                self._stack.append(frameNode)
-                frame_count += 1
-            index = len(self._stack) - 1
-            list.SetSelection(index) 
+                file = frameNode.getAttribute("file")
+                line = frameNode.getAttribute("line")
+                self._ui.SynchCurrentLine( file, int(line) )
+            except:
+                tp,val,tb=sys.exc_info()
+                traceback.print_exception(tp, val, tb)
 
 
-            node = self._stack[index]
-            self.currentItem = index
-            self.PopulateTreeFromFrameNode(node)
-            self.OnSyncFrame(None)
+        finally:
+            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
 
-            self._p1.FitInside()    
-            frameNode = nodeList.item(index)
-            file = frameNode.getAttribute("file")
-            line = frameNode.getAttribute("line")
-            self._ui.SynchCurrentLine( file, int(line) )
-        except:
-            tp,val,tb=sys.exc_info()
-            traceback.print_exception(tp, val, tb)
-              
-        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
 
-        
     def ListItemSelected(self, event):
         self.PopulateTreeFromFrameMessage(event.GetString())
         self.OnSyncFrame(None)
     def ListItemSelected(self, event):
         self.PopulateTreeFromFrameMessage(event.GetString())
         self.OnSyncFrame(None)
-        
-    def PopulateTreeFromFrameMessage(self, message):    
+
+    def PopulateTreeFromFrameMessage(self, message):
         index = 0
         for node in self._stack:
             if node.getAttribute("message") == message:
         index = 0
         for node in self._stack:
             if node.getAttribute("message") == message:
@@ -1271,13 +1799,13 @@ class FramesUI(wx.SplitterWindow):
                     self.PopulateTreeFromFrameNode(nodeList[0])
                 return
             index = index + 1
                     self.PopulateTreeFromFrameNode(nodeList[0])
                 return
             index = index + 1
-                        
+
     def PopulateTreeFromFrameNode(self, frameNode):
         list = self._framesChoiceCtrl
         list.Enable(True)
     def PopulateTreeFromFrameNode(self, frameNode):
         list = self._framesChoiceCtrl
         list.Enable(True)
-        tree = self._treeCtrl 
+        tree = self._treeCtrl
         #tree.Show(True)
         #tree.Show(True)
-        root = self._root   
+        root = self._root
         tree.DeleteChildren(root)
         children = frameNode.childNodes
         firstChild = None
         tree.DeleteChildren(root)
         children = frameNode.childNodes
         firstChild = None
@@ -1290,9 +1818,9 @@ class FramesUI(wx.SplitterWindow):
         if firstChild:
             tree.Expand(firstChild)
         self._p2.FitInside()
         if firstChild:
             tree.Expand(firstChild)
         self._p2.FitInside()
-        
+
     def IntrospectCallback(self, event):
     def IntrospectCallback(self, event):
-        tree = self._treeCtrl 
+        tree = self._treeCtrl
         item = event.GetItem()
         if _VERBOSE:
             print "In introspectCallback item is %s, pydata is %s" % (event.GetItem(), tree.GetPyData(item))
         item = event.GetItem()
         if _VERBOSE:
             print "In introspectCallback item is %s, pydata is %s" % (event.GetItem(), tree.GetPyData(item))
@@ -1303,16 +1831,16 @@ class FramesUI(wx.SplitterWindow):
         self._parentChain = self.GetItemChain(item)
         self.OnIntrospect(event)
         event.Skip()
         self._parentChain = self.GetItemChain(item)
         self.OnIntrospect(event)
         event.Skip()
-            
+
     def AppendSubTreeFromNode(self, node, name, parent, insertBefore=None):
     def AppendSubTreeFromNode(self, node, name, parent, insertBefore=None):
-        tree = self._treeCtrl 
+        tree = self._treeCtrl
         if insertBefore != None:
             treeNode = tree.InsertItem(parent, insertBefore, name)
         if insertBefore != None:
             treeNode = tree.InsertItem(parent, insertBefore, name)
-        else: 
+        else:
             treeNode = tree.AppendItem(parent, name)
         children = node.childNodes
         intro = node.getAttribute('intro')
             treeNode = tree.AppendItem(parent, name)
         children = node.childNodes
         intro = node.getAttribute('intro')
-            
+
         if intro == "True":
             tree.SetItemHasChildren(treeNode, True)
             tree.SetPyData(treeNode, "Introspect")
         if intro == "True":
             tree.SetItemHasChildren(treeNode, True)
             tree.SetPyData(treeNode, "Introspect")
@@ -1321,7 +1849,7 @@ class FramesUI(wx.SplitterWindow):
         for index in range(0, children.length):
             subNode = children.item(index)
             if self.HasChildren(subNode):
         for index in range(0, children.length):
             subNode = children.item(index)
             if self.HasChildren(subNode):
-                self.AppendSubTreeFromNode(subNode, subNode.getAttribute("name"), treeNode)      
+                self.AppendSubTreeFromNode(subNode, subNode.getAttribute("name"), treeNode)
             else:
                 name = subNode.getAttribute("name")
                 value = self.StripOuterSingleQuotes(subNode.getAttribute("value"))
             else:
                 name = subNode.getAttribute("name")
                 value = self.StripOuterSingleQuotes(subNode.getAttribute("value"))
@@ -1334,73 +1862,44 @@ class FramesUI(wx.SplitterWindow):
         if name.find('[') == -1:
             self._treeCtrl.SortChildren(treeNode)
         return treeNode
         if name.find('[') == -1:
             self._treeCtrl.SortChildren(treeNode)
         return treeNode
-        
+
     def StripOuterSingleQuotes(self, string):
         if string.startswith("'") and string.endswith("'"):
             retval =  string[1:-1]
         elif string.startswith("\"") and string.endswith("\""):
             retval = string[1:-1]
         else:
     def StripOuterSingleQuotes(self, string):
         if string.startswith("'") and string.endswith("'"):
             retval =  string[1:-1]
         elif string.startswith("\"") and string.endswith("\""):
             retval = string[1:-1]
         else:
-            retval = string
-        if retval.startswith("u'") and retval.endswith("'"):
-            retval = retval[1:]
-        return retval
-            
-    def HasChildren(self, node):
-        try:
-            return node.childNodes.length > 0
-        except:
-            tp,val,tb=sys.exc_info()
-            return False
-    def AppendText(self, text):
-        self._textCtrl.SetReadOnly(False)
-        self._textCtrl.AddText(text)
-        self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
-        self._textCtrl.SetReadOnly(True)
-        
-    def AppendErrorText(self, text):
-        self._textCtrl.SetReadOnly(False)
-        self._textCtrl.SetFontColor(wx.RED)   
-        self._textCtrl.StyleClearAll()
-        self._textCtrl.AddText(text)
-        self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
-        self._textCtrl.SetFontColor(wx.BLACK)   
-        self._textCtrl.StyleClearAll()
-        self._textCtrl.SetReadOnly(True)
-        
-    def ClearOutput(self, event):
-        self._textCtrl.SetReadOnly(False)
-        self._textCtrl.ClearAll()  
-        self._textCtrl.SetReadOnly(True)
+            retval = string
+        if retval.startswith("u'") and retval.endswith("'"):
+            retval = retval[1:]
+        return retval
+
+    def HasChildren(self, node):
+        try:
+            return node.childNodes.length > 0
+        except:
+            tp,val,tb=sys.exc_info()
+            return False
+
 
 
-    def SwitchToOutputTab(self):
-        self._notebook.SetSelection(0) 
-                          
 class DebuggerView(Service.ServiceView):
 class DebuggerView(Service.ServiceView):
-    
+
     #----------------------------------------------------------------------------
     # Overridden methods
     #----------------------------------------------------------------------------
 
     def __init__(self, service):
         Service.ServiceView.__init__(self, service)
     #----------------------------------------------------------------------------
     # Overridden methods
     #----------------------------------------------------------------------------
 
     def __init__(self, service):
         Service.ServiceView.__init__(self, service)
-        
+
     def _CreateControl(self, parent, id):
         return None
     def _CreateControl(self, parent, id):
         return None
-        
+
     #------------------------------------------------------------------------------
     # Event handling
     #-----------------------------------------------------------------------------
     #------------------------------------------------------------------------------
     # Event handling
     #-----------------------------------------------------------------------------
-    
+
     def OnToolClicked(self, event):
         self.GetFrame().ProcessEvent(event)
     def OnToolClicked(self, event):
         self.GetFrame().ProcessEvent(event)
-        
-    def ProcessUpdateUIEvent(self, event):
-        return False
-        
-    def ProcessEvent(self, event):
-        return False
 
     #------------------------------------------------------------------------------
     # Class methods
 
     #------------------------------------------------------------------------------
     # Class methods
@@ -1412,23 +1911,23 @@ class Interaction:
         self._message = message
         self._info = info
         self._quit = quit
         self._message = message
         self._info = info
         self._quit = quit
-        
+
     def getFramesXML(self):
         return self._framesXML
     def getFramesXML(self):
         return self._framesXML
-        
+
     def getMessage(self):
         return self._message
     def getMessage(self):
         return self._message
-        
+
     def getInfo(self):
         return self._info
     def getInfo(self):
         return self._info
-        
+
     def getQuit(self):
     def getQuit(self):
-        return self._quit  
-         
+        return self._quit
+
 class AGXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
     def __init__(self, address, logRequests=0):
 class AGXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
     def __init__(self, address, logRequests=0):
-        SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, address, logRequests=logRequests)               
-                           
+        SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, address, logRequests=logRequests)
+
 class RequestHandlerThread(threading.Thread):
     def __init__(self, queue, address):
         threading.Thread.__init__(self)
 class RequestHandlerThread(threading.Thread):
     def __init__(self, queue, address):
         threading.Thread.__init__(self)
@@ -1440,31 +1939,31 @@ class RequestHandlerThread(threading.Thread):
         self._server.register_function(self.quit)
         self._server.register_function(self.dummyOperation)
         if _VERBOSE: print "RequestHandlerThread on fileno %s" % str(self._server.fileno())
         self._server.register_function(self.quit)
         self._server.register_function(self.dummyOperation)
         if _VERBOSE: print "RequestHandlerThread on fileno %s" % str(self._server.fileno())
-        
+
     def run(self):
         while self._keepGoing:
             try:
     def run(self):
         while self._keepGoing:
             try:
-                self._server.handle_request() 
+                self._server.handle_request()
             except:
                 tp, val, tb = sys.exc_info()
                 traceback.print_exception(tp, val, tb)
                 self._keepGoing = False
         if _VERBOSE: print "Exiting Request Handler Thread."
             except:
                 tp, val, tb = sys.exc_info()
                 traceback.print_exception(tp, val, tb)
                 self._keepGoing = False
         if _VERBOSE: print "Exiting Request Handler Thread."
+
     def interaction(self, message, frameXML, info):
         if _VERBOSE: print "In RequestHandlerThread.interaction -- adding to queue"
         interaction = Interaction(message, frameXML, info)
         self._queue.put(interaction)
         return ""
     def interaction(self, message, frameXML, info):
         if _VERBOSE: print "In RequestHandlerThread.interaction -- adding to queue"
         interaction = Interaction(message, frameXML, info)
         self._queue.put(interaction)
         return ""
-        
+
     def quit(self):
         interaction = Interaction(None, None, info=None, quit=True)
         self._queue.put(interaction)
         return ""
     def quit(self):
         interaction = Interaction(None, None, info=None, quit=True)
         self._queue.put(interaction)
         return ""
-    
+
     def dummyOperation(self):
         return ""
     def dummyOperation(self):
         return ""
-        
+
     def AskToStop(self):
         self._keepGoing = False
         if type(self._server) is not types.NoneType:
     def AskToStop(self):
         self._keepGoing = False
         if type(self._server) is not types.NoneType:
@@ -1489,7 +1988,7 @@ class RequestBreakThread(threading.Thread):
             self._pushBreakpoints = pushBreakpoints
             self._breakDict = breakDict
             self._kill = kill
             self._pushBreakpoints = pushBreakpoints
             self._breakDict = breakDict
             self._kill = kill
-            
+
         def run(self):
             try:
                 if _VERBOSE: print "RequestBreakThread, before call"
         def run(self):
             try:
                 if _VERBOSE: print "RequestBreakThread, before call"
@@ -1511,7 +2010,7 @@ class DebuggerOperationThread(threading.Thread):
         def __init__(self, function):
             threading.Thread.__init__(self)
             self._function = function
         def __init__(self, function):
             threading.Thread.__init__(self)
             self._function = function
-            
+
         def run(self):
             if _VERBOSE: print "In DOT, before call"
             try:
         def run(self):
             if _VERBOSE: print "In DOT, before call"
             try:
@@ -1519,19 +2018,46 @@ class DebuggerOperationThread(threading.Thread):
             except:
                 tp,val,tb = sys.exc_info()
                 traceback.print_exception(tp, val, tb)
             except:
                 tp,val,tb = sys.exc_info()
                 traceback.print_exception(tp, val, tb)
-            if _VERBOSE: print "In DOT, after call"
-                
-class DebuggerCallback:
+            if _VERBOSE:
+                print "In DOT, after call"
+
+class BaseDebuggerCallback(object):
+
+    def Start(self):
+        assert False, "Start not overridden"
+
+    def ShutdownServer(self):
+        assert False, "ShutdownServer not overridden"
+
+    def BreakExecution(self):
+        assert False, "BreakExecution not overridden"
+
+    def SingleStep(self):
+        assert False, "SingleStep not overridden"
+
+    def Next(self):
+        assert False, "Next not overridden"
+
+    def Continue(self):
+        assert False, "Start not overridden"
+
+    def Return(self):
+        assert False, "Return not overridden"
+
+    def PushBreakpoints(self):
+        assert False, "PushBreakpoints not overridden"
 
 
-    def __init__(self, host, port, debugger_url, break_url, debuggerUI):
+class PythonDebuggerCallback(BaseDebuggerCallback):
+
+    def __init__(self, host, port, debugger_url, break_url, debuggerUI, autoContinue=False):
         if _VERBOSE: print "+++++++ Creating server on port, ", str(port)
         if _VERBOSE: print "+++++++ Creating server on port, ", str(port)
-        
+        self._timer = None
         self._queue = Queue.Queue(50)
         self._host = host
         self._port = int(port)
         threading._VERBOSE = _VERBOSE
         self._serverHandlerThread = RequestHandlerThread(self._queue, (self._host, self._port))
         self._queue = Queue.Queue(50)
         self._host = host
         self._port = int(port)
         threading._VERBOSE = _VERBOSE
         self._serverHandlerThread = RequestHandlerThread(self._queue, (self._host, self._port))
-        
+
         self._debugger_url = debugger_url
         self._debuggerServer = None
         self._waiting = False
         self._debugger_url = debugger_url
         self._debuggerServer = None
         self._waiting = False
@@ -1541,49 +2067,44 @@ class DebuggerCallback:
         self._breakServer = None
         self._firstInteraction = True
         self._pendingBreak = False
         self._breakServer = None
         self._firstInteraction = True
         self._pendingBreak = False
-        
-    def start(self):
+        self._autoContinue = autoContinue
+
+    def Start(self):
         self._serverHandlerThread.start()
         self._serverHandlerThread.start()
-    
+
     def ShutdownServer(self):
         #rbt = RequestBreakThread(self._breakServer, kill=True)
         #rbt.start()
     def ShutdownServer(self):
         #rbt = RequestBreakThread(self._breakServer, kill=True)
         #rbt.start()
-        self.setWaiting(False)
+        self._waiting = False
         if self._serverHandlerThread:
             self._serverHandlerThread.AskToStop()
             self._serverHandlerThread = None
         if self._serverHandlerThread:
             self._serverHandlerThread.AskToStop()
             self._serverHandlerThread = None
-                            
+
     def BreakExecution(self):
         rbt = RequestBreakThread(self._breakServer, interrupt=True)
         rbt.start()
     def BreakExecution(self):
         rbt = RequestBreakThread(self._breakServer, interrupt=True)
         rbt.start()
-                
+
     def SingleStep(self):
         self._debuggerUI.DisableWhileDebuggerRunning()
         self._debuggerServer.set_step() # Figure out where to set allowNone
     def SingleStep(self):
         self._debuggerUI.DisableWhileDebuggerRunning()
         self._debuggerServer.set_step() # Figure out where to set allowNone
-        self.waitForRPC()
+        self.WaitForRPC()
 
     def Next(self):
         self._debuggerUI.DisableWhileDebuggerRunning()
         self._debuggerServer.set_next()
 
     def Next(self):
         self._debuggerUI.DisableWhileDebuggerRunning()
         self._debuggerServer.set_next()
-        self.waitForRPC()
-        
+        self.WaitForRPC()
+
     def Continue(self):
         self._debuggerUI.DisableWhileDebuggerRunning()
         self._debuggerServer.set_continue()
     def Continue(self):
         self._debuggerUI.DisableWhileDebuggerRunning()
         self._debuggerServer.set_continue()
-        self.waitForRPC()
-        
+        self.WaitForRPC()
+
     def Return(self):
         self._debuggerUI.DisableWhileDebuggerRunning()
         self._debuggerServer.set_return()
     def Return(self):
         self._debuggerUI.DisableWhileDebuggerRunning()
         self._debuggerServer.set_return()
-        self.waitForRPC()
-        
-    def setWaiting(self, value):
-        self._waiting = value
-        
-    def getWaiting(self):
-        return self._waiting
-            
-    def readQueue(self):
+        self.WaitForRPC()
+
+    def ReadQueue(self):
         if self._queue.qsize():
             try:
                 item = self._queue.get_nowait()
         if self._queue.qsize():
             try:
                 item = self._queue.get_nowait()
@@ -1591,37 +2112,37 @@ class DebuggerCallback:
                     self.interaction(None, None, None, True)
                 else:
                     data = bz2.decompress(item.getFramesXML().data)
                     self.interaction(None, None, None, True)
                 else:
                     data = bz2.decompress(item.getFramesXML().data)
-                    self.interaction(item.getMessage().data, data, item.getInfo(), False)               
+                    self.interaction(item.getMessage().data, data, item.getInfo(), False)
             except Queue.Empty:
                 pass
             except Queue.Empty:
                 pass
-                
-    def pushBreakpoints(self):
+
+    def PushBreakpoints(self):
         rbt = RequestBreakThread(self._breakServer, pushBreakpoints=True, breakDict=self._service.GetMasterBreakpointDict())
         rbt.start()
 
         rbt = RequestBreakThread(self._breakServer, pushBreakpoints=True, breakDict=self._service.GetMasterBreakpointDict())
         rbt.start()
 
-                               
-    def waitForRPC(self):
-        self.setWaiting(True)
-        while self.getWaiting():
+
+    def WaitForRPC(self):
+        self._waiting = True
+        while self._waiting:
             try:
             try:
-                self.readQueue()
+                self.ReadQueue()
                 import time
                 time.sleep(0.02)
             except:
                 tp, val, tb = sys.exc_info()
                 traceback.print_exception(tp, val, tb)
             wx.GetApp().Yield(True)
                 import time
                 time.sleep(0.02)
             except:
                 tp, val, tb = sys.exc_info()
                 traceback.print_exception(tp, val, tb)
             wx.GetApp().Yield(True)
-        if _VERBOSE: print "Exiting waitForRPC."
-        
+        if _VERBOSE: print "Exiting WaitForRPC."
+
     def interaction(self, message, frameXML, info, quit):
     def interaction(self, message, frameXML, info, quit):
-        
+
         #This method should be hit as the debugger starts.
         if self._firstInteraction:
             self._firstInteraction = False
             self._debuggerServer = xmlrpclib.ServerProxy(self._debugger_url,  allow_none=1)
             self._breakServer = xmlrpclib.ServerProxy(self._break_url, allow_none=1)
         #This method should be hit as the debugger starts.
         if self._firstInteraction:
             self._firstInteraction = False
             self._debuggerServer = xmlrpclib.ServerProxy(self._debugger_url,  allow_none=1)
             self._breakServer = xmlrpclib.ServerProxy(self._break_url, allow_none=1)
-            self.pushBreakpoints()
-        self.setWaiting(False)
+            self.PushBreakpoints()
+        self._waiting = False
         if _VERBOSE: print "+"*40
         if(quit):
             self._debuggerUI.StopExecution(None)
         if _VERBOSE: print "+"*40
         if(quit):
             self._debuggerUI.StopExecution(None)
@@ -1633,11 +2154,45 @@ class DebuggerCallback:
             self._debuggerUI.SwitchToOutputTab()
         else:
             if _VERBOSE: print "Hit interaction no exception"
             self._debuggerUI.SwitchToOutputTab()
         else:
             if _VERBOSE: print "Hit interaction no exception"
+        #if not self._autoContinue:
         self._debuggerUI.SetStatusText(message)
         self._debuggerUI.SetStatusText(message)
-        self._debuggerUI.LoadFramesListXML(frameXML)        
-        self._debuggerUI.EnableWhileDebuggerStopped()
+        if not self._autoContinue:
+            self._debuggerUI.LoadPythonFramesList(frameXML)
+            self._debuggerUI.EnableWhileDebuggerStopped()
+
+        if self._autoContinue:
+            self._timer = wx.PyTimer(self.DoContinue)
+            self._autoContinue = False
+            self._timer.Start(250)
         if _VERBOSE: print "+"*40
         if _VERBOSE: print "+"*40
-        
+
+    def DoContinue(self):
+        self._timer.Stop()
+        dbgService = wx.GetApp().GetService(DebuggerService)
+        evt = DebugInternalWebServer()
+        evt.SetId(self._debuggerUI.CONTINUE_ID)
+        wx.PostEvent(self._debuggerUI, evt)
+        if _VERBOSE: print "Event Continue posted"
+
+        evt = DebugInternalWebServer()
+        evt.SetId(DebuggerService.DEBUG_WEBSERVER_NOW_RUN_PROJECT_ID)
+        wx.PostEvent(dbgService._frame, evt)
+        if _VERBOSE: print "Event RunProject posted"
+
+    def SendRunEvent(self):
+        class SendEventThread(threading.Thread):
+            def __init__(self):
+                threading.Thread.__init__(self)
+
+            def run(self):
+                dbgService = wx.GetApp().GetService(DebuggerService)
+                evt = DebugInternalWebServer()
+                evt.SetId(DebuggerService.DEBUG_WEBSERVER_NOW_RUN_PROJECT_ID)
+                wx.PostEvent(dbgService._frame, evt)
+                print "Event posted"
+        set = SendEventThread()
+        set.start()
+
 class DebuggerService(Service.Service):
 
     #----------------------------------------------------------------------------
 class DebuggerService(Service.Service):
 
     #----------------------------------------------------------------------------
@@ -1647,9 +2202,12 @@ class DebuggerService(Service.Service):
     CLEAR_ALL_BREAKPOINTS = wx.NewId()
     RUN_ID = wx.NewId()
     DEBUG_ID = wx.NewId()
     CLEAR_ALL_BREAKPOINTS = wx.NewId()
     RUN_ID = wx.NewId()
     DEBUG_ID = wx.NewId()
+    RUN_LAST_ID = wx.NewId()
+    DEBUG_LAST_ID = wx.NewId()
     DEBUG_WEBSERVER_ID = wx.NewId()
     RUN_WEBSERVER_ID = wx.NewId()
     DEBUG_WEBSERVER_ID = wx.NewId()
     RUN_WEBSERVER_ID = wx.NewId()
-            
+    DEBUG_WEBSERVER_CONTINUE_ID = wx.NewId()
+    DEBUG_WEBSERVER_NOW_RUN_PROJECT_ID = wx.NewId()
     def ComparePaths(first, second):
         one = DebuggerService.ExpandPath(first)
         two = DebuggerService.ExpandPath(second)
     def ComparePaths(first, second):
         one = DebuggerService.ExpandPath(first)
         two = DebuggerService.ExpandPath(second)
@@ -1658,7 +2216,7 @@ class DebuggerService(Service.Service):
         else:
             return one == two
     ComparePaths = staticmethod(ComparePaths)
         else:
             return one == two
     ComparePaths = staticmethod(ComparePaths)
-            
+
     # Make sure we're using an expanded path on windows.
     def ExpandPath(path):
         if _WINDOWS:
     # Make sure we're using an expanded path on windows.
     def ExpandPath(path):
         if _WINDOWS:
@@ -1667,11 +2225,11 @@ class DebuggerService(Service.Service):
             except:
                 if _VERBOSE:
                     print "Cannot get long path for %s" % path
             except:
                 if _VERBOSE:
                     print "Cannot get long path for %s" % path
-                
+
         return path
         return path
-        
+
     ExpandPath = staticmethod(ExpandPath)
     ExpandPath = staticmethod(ExpandPath)
+
     #----------------------------------------------------------------------------
     # Overridden methods
     #----------------------------------------------------------------------------
     #----------------------------------------------------------------------------
     # Overridden methods
     #----------------------------------------------------------------------------
@@ -1690,7 +2248,12 @@ class DebuggerService(Service.Service):
                 self._masterBPDict = {}
         else:
             self._masterBPDict = {}
                 self._masterBPDict = {}
         else:
             self._masterBPDict = {}
-            
+        self._frame = None
+        self.projectPath = None
+        self.fileToDebug = None
+        self.phpDbgParam = None
+        self.dbgLanguage = projectmodel.LANGUAGE_DEFAULT
+
     def OnCloseFrame(self, event):
         # IS THIS THE RIGHT PLACE?
         try:
     def OnCloseFrame(self, event):
         # IS THIS THE RIGHT PLACE?
         try:
@@ -1700,7 +2263,7 @@ class DebuggerService(Service.Service):
             tp,val,tb = sys.exc_info()
             traceback.print_exception(tp, val, tb)
         return True
             tp,val,tb = sys.exc_info()
             traceback.print_exception(tp, val, tb)
         return True
-        
+
     def _CreateView(self):
         return DebuggerView(self)
 
     def _CreateView(self):
         return DebuggerView(self)
 
@@ -1711,20 +2274,28 @@ class DebuggerService(Service.Service):
 
     def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
         #Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
 
     def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
         #Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
-
+        self._frame = frame
         config = wx.ConfigBase_Get()
 
         debuggerMenu = wx.Menu()
         if not menuBar.FindItemById(DebuggerService.CLEAR_ALL_BREAKPOINTS):
         config = wx.ConfigBase_Get()
 
         debuggerMenu = wx.Menu()
         if not menuBar.FindItemById(DebuggerService.CLEAR_ALL_BREAKPOINTS):
-                    
+
             debuggerMenu.Append(DebuggerService.RUN_ID, _("&Run...\tCtrl+R"), _("Runs a file"))
             wx.EVT_MENU(frame, DebuggerService.RUN_ID, frame.ProcessEvent)
             wx.EVT_UPDATE_UI(frame, DebuggerService.RUN_ID, frame.ProcessUpdateUIEvent)
             debuggerMenu.Append(DebuggerService.RUN_ID, _("&Run...\tCtrl+R"), _("Runs a file"))
             wx.EVT_MENU(frame, DebuggerService.RUN_ID, frame.ProcessEvent)
             wx.EVT_UPDATE_UI(frame, DebuggerService.RUN_ID, frame.ProcessUpdateUIEvent)
-            
+
             debuggerMenu.Append(DebuggerService.DEBUG_ID, _("&Debug...\tCtrl+D"), _("Debugs a file"))
             wx.EVT_MENU(frame, DebuggerService.DEBUG_ID, frame.ProcessEvent)
             wx.EVT_UPDATE_UI(frame, DebuggerService.DEBUG_ID, frame.ProcessUpdateUIEvent)
             debuggerMenu.Append(DebuggerService.DEBUG_ID, _("&Debug...\tCtrl+D"), _("Debugs a file"))
             wx.EVT_MENU(frame, DebuggerService.DEBUG_ID, frame.ProcessEvent)
             wx.EVT_UPDATE_UI(frame, DebuggerService.DEBUG_ID, frame.ProcessUpdateUIEvent)
-            
+
+            debuggerMenu.Append(DebuggerService.RUN_LAST_ID, _("&Run Using Last Settings\tF5"), _("Runs a file using previous settings"))
+            wx.EVT_MENU(frame, DebuggerService.RUN_LAST_ID, frame.ProcessEvent)
+            wx.EVT_UPDATE_UI(frame, DebuggerService.RUN_LAST_ID, frame.ProcessUpdateUIEvent)
+
+            debuggerMenu.Append(DebuggerService.DEBUG_LAST_ID, _("&Debug Using Last Settings\tF8"), _("Debugs a file using previous settings"))
+            wx.EVT_MENU(frame, DebuggerService.DEBUG_LAST_ID, frame.ProcessEvent)
+            wx.EVT_UPDATE_UI(frame, DebuggerService.DEBUG_LAST_ID, frame.ProcessUpdateUIEvent)
+
             if not ACTIVEGRID_BASE_IDE:
                 debuggerMenu.AppendSeparator()
                 debuggerMenu.Append(DebuggerService.DEBUG_WEBSERVER_ID, _("Debug Internal Web Server"), _("Debugs the internal webservier"))
             if not ACTIVEGRID_BASE_IDE:
                 debuggerMenu.AppendSeparator()
                 debuggerMenu.Append(DebuggerService.DEBUG_WEBSERVER_ID, _("Debug Internal Web Server"), _("Debugs the internal webservier"))
@@ -1733,30 +2304,31 @@ class DebuggerService(Service.Service):
                 debuggerMenu.Append(DebuggerService.RUN_WEBSERVER_ID, _("Restart Internal Web Server"), _("Restarts the internal webservier"))
                 wx.EVT_MENU(frame, DebuggerService.RUN_WEBSERVER_ID, frame.ProcessEvent)
                 wx.EVT_UPDATE_UI(frame, DebuggerService.RUN_WEBSERVER_ID, frame.ProcessUpdateUIEvent)
                 debuggerMenu.Append(DebuggerService.RUN_WEBSERVER_ID, _("Restart Internal Web Server"), _("Restarts the internal webservier"))
                 wx.EVT_MENU(frame, DebuggerService.RUN_WEBSERVER_ID, frame.ProcessEvent)
                 wx.EVT_UPDATE_UI(frame, DebuggerService.RUN_WEBSERVER_ID, frame.ProcessUpdateUIEvent)
-            
+
+                frame.Bind(EVT_DEBUG_INTERNAL, frame.ProcessEvent)
             debuggerMenu.AppendSeparator()
             debuggerMenu.AppendSeparator()
-            
-            debuggerMenu.Append(DebuggerService.TOGGLE_BREAKPOINT_ID, _("&Toggle Breakpoint...\tCtrl+B"), _("Toggle a breakpoint"))
+
+            debuggerMenu.Append(DebuggerService.TOGGLE_BREAKPOINT_ID, _("&Toggle Breakpoint\tCtrl+B"), _("Toggle a breakpoint"))
             wx.EVT_MENU(frame, DebuggerService.TOGGLE_BREAKPOINT_ID, self.ProcessEvent)
             wx.EVT_UPDATE_UI(frame, DebuggerService.TOGGLE_BREAKPOINT_ID, self.ProcessUpdateUIEvent)
 
             debuggerMenu.Append(DebuggerService.CLEAR_ALL_BREAKPOINTS, _("&Clear All Breakpoints"), _("Clear All Breakpoints"))
             wx.EVT_MENU(frame, DebuggerService.CLEAR_ALL_BREAKPOINTS, self.ProcessEvent)
             wx.EVT_UPDATE_UI(frame, DebuggerService.CLEAR_ALL_BREAKPOINTS, self.ProcessUpdateUIEvent)
             wx.EVT_MENU(frame, DebuggerService.TOGGLE_BREAKPOINT_ID, self.ProcessEvent)
             wx.EVT_UPDATE_UI(frame, DebuggerService.TOGGLE_BREAKPOINT_ID, self.ProcessUpdateUIEvent)
 
             debuggerMenu.Append(DebuggerService.CLEAR_ALL_BREAKPOINTS, _("&Clear All Breakpoints"), _("Clear All Breakpoints"))
             wx.EVT_MENU(frame, DebuggerService.CLEAR_ALL_BREAKPOINTS, self.ProcessEvent)
             wx.EVT_UPDATE_UI(frame, DebuggerService.CLEAR_ALL_BREAKPOINTS, self.ProcessUpdateUIEvent)
-            
-            
+
+
         viewMenuIndex = menuBar.FindMenu(_("&Project"))
         menuBar.Insert(viewMenuIndex + 1, debuggerMenu, _("&Run"))
         viewMenuIndex = menuBar.FindMenu(_("&Project"))
         menuBar.Insert(viewMenuIndex + 1, debuggerMenu, _("&Run"))
-        
+
         toolBar.AddSeparator()
         toolBar.AddSeparator()
-        toolBar.AddTool(DebuggerService.RUN_ID, getRunningManBitmap(), shortHelpString = _("Run"), longHelpString = _("Run"))
-        toolBar.AddTool(DebuggerService.DEBUG_ID, getDebuggingManBitmap(), shortHelpString = _("Debug"), longHelpString = _("Debug"))
+        toolBar.AddTool(DebuggerService.RUN_LAST_ID, getRunningManBitmap(), shortHelpString = _("Run Using Last Settings"), longHelpString = _("Run Using Last Settings"))
+        toolBar.AddTool(DebuggerService.DEBUG_LAST_ID, getDebuggingManBitmap(), shortHelpString = _("Debug Using Last Settings"), longHelpString = _("Debug Using Last Settings"))
         toolBar.Realize()
 
         return True
 
 
         toolBar.Realize()
 
         return True
 
 
-            
+
     #----------------------------------------------------------------------------
     # Event Processing Methods
     #----------------------------------------------------------------------------
     #----------------------------------------------------------------------------
     # Event Processing Methods
     #----------------------------------------------------------------------------
@@ -1782,14 +2354,26 @@ class DebuggerService(Service.Service):
         elif an_id == DebuggerService.DEBUG_ID:
             self.OnDebugProject(event)
             return True
         elif an_id == DebuggerService.DEBUG_ID:
             self.OnDebugProject(event)
             return True
+        elif an_id == DebuggerService.RUN_LAST_ID:
+            self.OnRunProject(event, showDialog=False)
+            return True
+        elif an_id == DebuggerService.DEBUG_LAST_ID:
+            self.OnDebugProject(event, showDialog=False)
+            return True
         elif an_id == DebuggerService.DEBUG_WEBSERVER_ID:
             self.OnDebugWebServer(event)
             return True
         elif an_id == DebuggerService.DEBUG_WEBSERVER_ID:
             self.OnDebugWebServer(event)
             return True
+        elif an_id == DebuggerService.DEBUG_WEBSERVER_CONTINUE_ID:
+            self.OnDebugWebServerContinue(event)
+            return True
+        elif an_id == DebuggerService.DEBUG_WEBSERVER_NOW_RUN_PROJECT_ID:
+            self.WaitDebuggerThenRunProject()
+            return True
         elif an_id == DebuggerService.RUN_WEBSERVER_ID:
             self.OnRunWebServer(event)
             return True
         return False
         elif an_id == DebuggerService.RUN_WEBSERVER_ID:
             self.OnRunWebServer(event)
             return True
         return False
-        
+
     def ProcessUpdateUIEvent(self, event):
         if Service.Service.ProcessUpdateUIEvent(self, event):
             return True
     def ProcessUpdateUIEvent(self, event):
         if Service.Service.ProcessUpdateUIEvent(self, event):
             return True
@@ -1803,7 +2387,9 @@ class DebuggerService(Service.Service):
             event.Enable(self.HasBreakpointsSet())
             return True
         elif (an_id == DebuggerService.RUN_ID
             event.Enable(self.HasBreakpointsSet())
             return True
         elif (an_id == DebuggerService.RUN_ID
-        or an_id == DebuggerService.DEBUG_ID):
+        or an_id == DebuggerService.RUN_LAST_ID
+        or an_id == DebuggerService.DEBUG_ID
+        or an_id == DebuggerService.DEBUG_LAST_ID):
             event.Enable(self.HasAnyFiles())
             return True
         else:
             event.Enable(self.HasAnyFiles())
             return True
         else:
@@ -1811,75 +2397,172 @@ class DebuggerService(Service.Service):
 
     #----------------------------------------------------------------------------
     # Class Methods
 
     #----------------------------------------------------------------------------
     # Class Methods
-    #----------------------------------------------------------------------------                    
-   
-    def OnDebugProject(self, event):
+    #----------------------------------------------------------------------------
+
+    def OnDebugProject(self, event, showDialog=True):
         if _WINDOWS and not _PYWIN32_INSTALLED:
             wx.MessageBox(_("Python for Windows extensions (pywin32) is required to debug on Windows machines. Please go to http://sourceforge.net/projects/pywin32/, download and install pywin32."))
             return
         if not Executor.GetPythonExecutablePath():
             return
         if _WINDOWS and not _PYWIN32_INSTALLED:
             wx.MessageBox(_("Python for Windows extensions (pywin32) is required to debug on Windows machines. Please go to http://sourceforge.net/projects/pywin32/, download and install pywin32."))
             return
         if not Executor.GetPythonExecutablePath():
             return
-        if DebugCommandUI.DebuggerRunning():
+        if BaseDebuggerUI.DebuggerRunning():
             wx.MessageBox(_("A debugger is already running. Please shut down the other debugger first."), _("Debugger Running"))
             return
             wx.MessageBox(_("A debugger is already running. Please shut down the other debugger first."), _("Debugger Running"))
             return
+        config = wx.ConfigBase_Get()
+        host = config.Read("DebuggerHostName", DEFAULT_HOST)
+        if not host:
+            wx.MessageBox(_("No debugger host set. Please go to Tools->Options->Debugger and set one."), _("No Debugger Host"))
+            return
+
         self.ShowWindow(True)
         projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
         try:
         self.ShowWindow(True)
         projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
         try:
-            dlg = CommandPropertiesDialog(self.GetView().GetFrame(), 'Debug Python File', projectService, None, pythonOnly=True, okButtonName="Debug", debugging=True)
+            dlg = CommandPropertiesDialog(self.GetView().GetFrame(), 'Debug File', projectService, None, okButtonName="Debug", debugging=True)
         except:
             return
         dlg.CenterOnParent()
         except:
             return
         dlg.CenterOnParent()
-        if dlg.ShowModal() == wx.ID_OK:
+        if not showDialog:
+            showDialog = dlg.MustShowDialog()
+        if showDialog and dlg.ShowModal() == wx.ID_OK:
+            projectPath, fileToDebug, initialArgs, startIn, isPython, environment = dlg.GetSettings()
+        elif not showDialog:
             projectPath, fileToDebug, initialArgs, startIn, isPython, environment = dlg.GetSettings()
             projectPath, fileToDebug, initialArgs, startIn, isPython, environment = dlg.GetSettings()
-            dlg.Destroy()
         else:
             dlg.Destroy()
             return
         else:
             dlg.Destroy()
             return
+        dlg.Destroy()
         self.PromptToSaveFiles()
         self.PromptToSaveFiles()
-
         shortFile = os.path.basename(fileToDebug)
         fileToDebug = DebuggerService.ExpandPath(fileToDebug)
         shortFile = os.path.basename(fileToDebug)
         fileToDebug = DebuggerService.ExpandPath(fileToDebug)
-        try:
-            page = DebugCommandUI(Service.ServiceView.bottomTab, -1, str(fileToDebug), self)
+        if fileToDebug.endswith('.bpel'):
+            self.projectPath = projectPath
+            self.fileToDebug = fileToDebug
+
+            #
+            # TODO: merge getting project stuff and save the results for
+            # WaitDebuggerThenRunProject() which currently does it again.
+            # 
+            projects = projectService.FindProjectByFile(projectPath)
+            if not projects:
+                return
+            project = projects[0]
+            lang    = project.GetAppInfo().language
+            if lang:
+                self.dbgLanguage = lang
+
+            dbgService = wx.GetApp().GetService(DebuggerService)
+
+            evt = DebugInternalWebServer()
+            evt.SetId(DebuggerService.DEBUG_WEBSERVER_CONTINUE_ID)
+            wx.PostEvent(dbgService._frame, evt)
+
+            if lang == projectmodel.LANGUAGE_PHP:
+                evt = DebugInternalWebServer()
+                evt.SetId(DebuggerService.DEBUG_WEBSERVER_NOW_RUN_PROJECT_ID)
+                wx.PostEvent(dbgService._frame, evt)
+
+            return
+
+        elif fileToDebug.endswith('.php'):
+            page = PHPDebuggerUI(Service.ServiceView.bottomTab, -1, str(fileToDebug), self)
             count = Service.ServiceView.bottomTab.GetPageCount()
             Service.ServiceView.bottomTab.AddPage(page, _("Debugging: ") + shortFile)
             Service.ServiceView.bottomTab.SetSelection(count)
             count = Service.ServiceView.bottomTab.GetPageCount()
             Service.ServiceView.bottomTab.AddPage(page, _("Debugging: ") + shortFile)
             Service.ServiceView.bottomTab.SetSelection(count)
+
+            fullPhpScriptPath = os.path.normpath(fileToDebug)
+            environment["REDIRECT_STATUS"] = "200"
+            environment["REDIRECT_URL"] = fullPhpScriptPath
+            environment["SERVER_SOFTWARE"] = "AG PHP Debugger 1.7"
+            environment["SERVER_NAME"] = "localhost"
+            environment["SERVER_ADDR"] = "127.0.0.1"
+            environment["SERVER_PORT"] = "80"
+            environment["REMOTE_ADDR"] = "127.0.0.1"
+            environment["SCRIPT_FILENAME"] = "php"
+            environment["GATEWAY_INTERFACE"] = "CGI/1.1"
+            environment["SERVER_PROTOCOL"] = "HTTP/1.1"
+            environment["REQUEST_METHOD"] = "GET"
+            environment["REQUEST_URI"] = fullPhpScriptPath
+            environment["PATH_INFO"] = fullPhpScriptPath
+            environment["PATH_TRANSLATED"] = fullPhpScriptPath
+            environment["HTTP_COOKIE"] = "DBGSESSID=11439636363807700001@clienthost:10001"
+
             page.Execute(initialArgs, startIn, environment)
             page.Execute(initialArgs, startIn, environment)
-        except:
-            pass
-            
-    def OnDebugWebServer(self, event):
+        else:
+            try:
+                page = PythonDebuggerUI(Service.ServiceView.bottomTab, -1, str(fileToDebug), self)
+                count = Service.ServiceView.bottomTab.GetPageCount()
+                Service.ServiceView.bottomTab.AddPage(page, _("Debugging: ") + shortFile)
+                Service.ServiceView.bottomTab.SetSelection(count)
+                page.Execute(initialArgs, startIn, environment)
+            except:
+                pass
+
+    def WaitDebuggerThenRunProject(self):
+        import time
+        #while not BaseDebuggerUI.DebuggerPastAutoContinue():
+        #    time.sleep(0.2)
+        #    wx.GetApp().Yield(True)
+        #    print "After Yield"
+        time.sleep(2.0)
+        projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
+        projects = projectService.FindProjectByFile(self.projectPath)
+        if not projects:
+            return
+        project = projects[0]
+        try:
+            deployFilePath = project.GenerateDeployment()
+        except ProjectEditor.DataServiceExistenceException, e:
+            dataSourceName = str(e)
+            projectService.PromptForMissingDataSource(dataSourceName)
+            return
+        projectService.RunProcessModel(self.fileToDebug, project.GetAppInfo().language, deployFilePath)
+
+    def OnDebugWebServerContinue(self, event):
+        self.OnDebugWebServer(event, autoContinue=True)
+
+    def OnDebugWebServer(self, event, autoContinue=False):
+        #print "xxxxx debugging OnDebugWebServer"
         if _WINDOWS and not _PYWIN32_INSTALLED:
             wx.MessageBox(_("Python for Windows extensions (pywin32) is required to debug on Windows machines. Please go to http://sourceforge.net/projects/pywin32/, download and install pywin32."))
             return
         if not Executor.GetPythonExecutablePath():
             return
         if _WINDOWS and not _PYWIN32_INSTALLED:
             wx.MessageBox(_("Python for Windows extensions (pywin32) is required to debug on Windows machines. Please go to http://sourceforge.net/projects/pywin32/, download and install pywin32."))
             return
         if not Executor.GetPythonExecutablePath():
             return
-        if DebugCommandUI.DebuggerRunning():
+        if BaseDebuggerUI.DebuggerRunning():
             wx.MessageBox(_("A debugger is already running. Please shut down the other debugger first."), _("Debugger Running"))
             return
         import WebServerService
         wsService = wx.GetApp().GetService(WebServerService.WebServerService)
         fileName, args = wsService.StopAndPrepareToDebug()
             wx.MessageBox(_("A debugger is already running. Please shut down the other debugger first."), _("Debugger Running"))
             return
         import WebServerService
         wsService = wx.GetApp().GetService(WebServerService.WebServerService)
         fileName, args = wsService.StopAndPrepareToDebug()
+        #print "xxxxx OnDebugWebServer: fileName=%s, args=%s" % (repr(fileName), repr(args))
+        config = wx.ConfigBase_Get()
+        host = config.Read("DebuggerHostName", DEFAULT_HOST)
+        if not host:
+            wx.MessageBox(_("No debugger host set. Please go to Tools->Options->Debugger and set one."), _("No Debugger Host"))
+            return
         try:
         try:
-            page = DebugCommandUI(Service.ServiceView.bottomTab, -1, fileName, self)
+            if self.dbgLanguage == projectmodel.LANGUAGE_PHP:
+                page = PHPDebuggerUI(Service.ServiceView.bottomTab, -1, fileName, self)
+            else:
+                page = PythonDebuggerUI(Service.ServiceView.bottomTab, -1, fileName, self, autoContinue)
+
             count = Service.ServiceView.bottomTab.GetPageCount()
             Service.ServiceView.bottomTab.AddPage(page, _("Debugging: Internal WebServer"))
             Service.ServiceView.bottomTab.SetSelection(count)
             count = Service.ServiceView.bottomTab.GetPageCount()
             Service.ServiceView.bottomTab.AddPage(page, _("Debugging: Internal WebServer"))
             Service.ServiceView.bottomTab.SetSelection(count)
-            page.Execute(args, startIn=os.getcwd(), environment=os.environ)
+            page.Execute(args, startIn=sysutilslib.mainModuleDir, environment=os.environ, onWebServer = True)
         except:
             pass
         except:
             pass
-            
+
     def OnRunWebServer(self, event):
         if not Executor.GetPythonExecutablePath():
             return
         import WebServerService
         wsService = wx.GetApp().GetService(WebServerService.WebServerService)
     def OnRunWebServer(self, event):
         if not Executor.GetPythonExecutablePath():
             return
         import WebServerService
         wsService = wx.GetApp().GetService(WebServerService.WebServerService)
-        wsService.ShutDownAndRestart()                
-                               
+        wsService.ShutDownAndRestart()
+
     def HasAnyFiles(self):
         docs = wx.GetApp().GetDocumentManager().GetDocuments()
         return len(docs) > 0
     def HasAnyFiles(self):
         docs = wx.GetApp().GetDocumentManager().GetDocuments()
         return len(docs) > 0
-        
+
     def PromptToSaveFiles(self, running=True):
         filesModified = False
         docs = wx.GetApp().GetDocumentManager().GetDocuments()
     def PromptToSaveFiles(self, running=True):
         filesModified = False
         docs = wx.GetApp().GetDocumentManager().GetDocuments()
@@ -1888,14 +2571,14 @@ class DebuggerService(Service.Service):
                 filesModified = True
                 break
         if filesModified:
                 filesModified = True
                 break
         if filesModified:
-            frame = self.GetView().GetFrame()  
-            if running:              
+            frame = self.GetView().GetFrame()
+            if running:
                 yesNoMsg = wx.MessageDialog(frame,
                           _("Files have been modified.\nWould you like to save all files before running?"),
                           _("Run"),
                           wx.YES_NO|wx.ICON_QUESTION
                           )
                 yesNoMsg = wx.MessageDialog(frame,
                           _("Files have been modified.\nWould you like to save all files before running?"),
                           _("Run"),
                           wx.YES_NO|wx.ICON_QUESTION
                           )
-            else:              
+            else:
                 yesNoMsg = wx.MessageDialog(frame,
                           _("Files have been modified.\nWould you like to save all files before debugging?"),
                           _("Debug"),
                 yesNoMsg = wx.MessageDialog(frame,
                           _("Files have been modified.\nWould you like to save all files before debugging?"),
                           _("Debug"),
@@ -1909,10 +2592,10 @@ class DebuggerService(Service.Service):
             yesNoMsg.Destroy()
 
     def OnExit(self):
             yesNoMsg.Destroy()
 
     def OnExit(self):
-        DebugCommandUI.ShutdownAllDebuggers()
+        BaseDebuggerUI.ShutdownAllDebuggers()
         RunCommandUI.ShutdownAllRunners()
         RunCommandUI.ShutdownAllRunners()
-        
-    def OnRunProject(self, event):
+
+    def OnRunProject(self, event, showDialog=True):
         if _WINDOWS and not _PYWIN32_INSTALLED:
             wx.MessageBox(_("Python for Windows extensions (pywin32) is required to run on Windows machines. Please go to http://sourceforge.net/projects/pywin32/, download and install pywin32."))
             return
         if _WINDOWS and not _PYWIN32_INSTALLED:
             wx.MessageBox(_("Python for Windows extensions (pywin32) is required to run on Windows machines. Please go to http://sourceforge.net/projects/pywin32/, download and install pywin32."))
             return
@@ -1924,30 +2607,38 @@ class DebuggerService(Service.Service):
         except:
             return
         dlg.CenterOnParent()
         except:
             return
         dlg.CenterOnParent()
-        if dlg.ShowModal() == wx.ID_OK:
+        if not showDialog:
+            showDialog = dlg.MustShowDialog()
+        if showDialog and dlg.ShowModal() == wx.ID_OK:
+            projectPath, fileToRun, initialArgs, startIn, isPython, environment = dlg.GetSettings()
+        elif not showDialog:
             projectPath, fileToRun, initialArgs, startIn, isPython, environment = dlg.GetSettings()
             projectPath, fileToRun, initialArgs, startIn, isPython, environment = dlg.GetSettings()
-            dlg.Destroy()
         else:
             dlg.Destroy()
             return
         else:
             dlg.Destroy()
             return
+        dlg.Destroy()
         self.PromptToSaveFiles()
         self.PromptToSaveFiles()
-        # This will need to change when we can run more than .py and .bpel files.
-        if not isPython:
+        if fileToRun.endswith('bpel'):
             projects = projectService.FindProjectByFile(projectPath)
             if not projects:
                 return
             project = projects[0]
             projects = projectService.FindProjectByFile(projectPath)
             if not projects:
                 return
             project = projects[0]
-            deployFilePath = project.GenerateDeployment()
+            try:
+                deployFilePath = project.GenerateDeployment()
+            except ProjectEditor.DataServiceExistenceException, e:
+                dataSourceName = str(e)
+                projectService.PromptForMissingDataSource(dataSourceName)
+                return
             projectService.RunProcessModel(fileToRun, project.GetAppInfo().language, deployFilePath)
             return
             projectService.RunProcessModel(fileToRun, project.GetAppInfo().language, deployFilePath)
             return
-            
+
         self.ShowWindow(True)
         shortFile = os.path.basename(fileToRun)
         page = RunCommandUI(Service.ServiceView.bottomTab, -1, str(fileToRun))
         count = Service.ServiceView.bottomTab.GetPageCount()
         Service.ServiceView.bottomTab.AddPage(page, "Running: " + shortFile)
         Service.ServiceView.bottomTab.SetSelection(count)
         self.ShowWindow(True)
         shortFile = os.path.basename(fileToRun)
         page = RunCommandUI(Service.ServiceView.bottomTab, -1, str(fileToRun))
         count = Service.ServiceView.bottomTab.GetPageCount()
         Service.ServiceView.bottomTab.AddPage(page, "Running: " + shortFile)
         Service.ServiceView.bottomTab.SetSelection(count)
-        page.Execute(initialArgs, startIn, environment)
+        page.Execute(initialArgs, startIn, environment, onWebServer = True)
 
     def OnToggleBreakpoint(self, event, line=-1, fileName=None):
         if not fileName:
 
     def OnToggleBreakpoint(self, event, line=-1, fileName=None):
         if not fileName:
@@ -1970,8 +2661,8 @@ class DebuggerService(Service.Service):
                 view.GetCtrl().Refresh()
         # Now refresh all the markers icons in all the open views.
         self.ClearAllBreakpointMarkers()
                 view.GetCtrl().Refresh()
         # Now refresh all the markers icons in all the open views.
         self.ClearAllBreakpointMarkers()
-        self.SetAllBreakpointMarkers()    
-                 
+        self.SetAllBreakpointMarkers()
+
     def SilentToggleBreakpoint(self, fileName, line):
         found = False
         for lineNumber in self.GetBreakpointList(fileName):
     def SilentToggleBreakpoint(self, fileName, line):
         found = False
         for lineNumber in self.GetBreakpointList(fileName):
@@ -1982,30 +2673,30 @@ class DebuggerService(Service.Service):
             self.SetBreak(fileName, line)
         else:
             self.ClearBreak(fileName, line)
             self.SetBreak(fileName, line)
         else:
             self.ClearBreak(fileName, line)
-                  
+
     def SetBreak(self, fileName, line):
         expandedName = DebuggerService.ExpandPath(fileName)
         if not self._masterBPDict.has_key(expandedName):
             self._masterBPDict[expandedName] = [line]
         else:
             self._masterBPDict[expandedName] += [line]
     def SetBreak(self, fileName, line):
         expandedName = DebuggerService.ExpandPath(fileName)
         if not self._masterBPDict.has_key(expandedName):
             self._masterBPDict[expandedName] = [line]
         else:
             self._masterBPDict[expandedName] += [line]
-        # If we're already debugging, pass this bp off to the DebuggerCallback
+        # If we're already debugging, pass this bp off to the PythonDebuggerCallback
         self.NotifyDebuggersOfBreakpointChange()
         self.NotifyDebuggersOfBreakpointChange()
-    
+
     def NotifyDebuggersOfBreakpointChange(self):
     def NotifyDebuggersOfBreakpointChange(self):
-        DebugCommandUI.NotifyDebuggersOfBreakpointChange()
-        
+        BaseDebuggerUI.NotifyDebuggersOfBreakpointChange()
+
     def GetBreakpointList(self, fileName):
         expandedName = DebuggerService.ExpandPath(fileName)
         if not self._masterBPDict.has_key(expandedName):
             return []
         else:
     def GetBreakpointList(self, fileName):
         expandedName = DebuggerService.ExpandPath(fileName)
         if not self._masterBPDict.has_key(expandedName):
             return []
         else:
-            return self._masterBPDict[expandedName] 
+            return self._masterBPDict[expandedName]
 
     def SetBreakpointList(self, fileName, bplist):
         expandedName = DebuggerService.ExpandPath(fileName)
         self._masterBPDict[expandedName] = bplist
 
     def SetBreakpointList(self, fileName, bplist):
         expandedName = DebuggerService.ExpandPath(fileName)
         self._masterBPDict[expandedName] = bplist
-        
+
     def BreakpointSet(self, fileName, line):
         expandedName = DebuggerService.ExpandPath(fileName)
         if not self._masterBPDict.has_key(expandedName):
     def BreakpointSet(self, fileName, line):
         expandedName = DebuggerService.ExpandPath(fileName)
         if not self._masterBPDict.has_key(expandedName):
@@ -2016,7 +2707,7 @@ class DebuggerService(Service.Service):
                 if(int(number) == int(line)):
                     return True
         return False
                 if(int(number) == int(line)):
                     return True
         return False
-        
+
     def ClearBreak(self, fileName, line):
         expandedName = DebuggerService.ExpandPath(fileName)
         if not self._masterBPDict.has_key(expandedName):
     def ClearBreak(self, fileName, line):
         expandedName = DebuggerService.ExpandPath(fileName)
         if not self._masterBPDict.has_key(expandedName):
@@ -2029,43 +2720,49 @@ class DebuggerService(Service.Service):
                     newList.append(number)
             self._masterBPDict[expandedName] = newList
         self.NotifyDebuggersOfBreakpointChange()
                     newList.append(number)
             self._masterBPDict[expandedName] = newList
         self.NotifyDebuggersOfBreakpointChange()
-       
+
     def HasBreakpointsSet(self):
         for key, value in self._masterBPDict.items():
             if len(value) > 0:
                 return True
         return False
     def HasBreakpointsSet(self):
         for key, value in self._masterBPDict.items():
             if len(value) > 0:
                 return True
         return False
-                 
+
     def ClearAllBreakpoints(self):
         self._masterBPDict = {}
         self.NotifyDebuggersOfBreakpointChange()
         self.ClearAllBreakpointMarkers()
     def ClearAllBreakpoints(self):
         self._masterBPDict = {}
         self.NotifyDebuggersOfBreakpointChange()
         self.ClearAllBreakpointMarkers()
-        
+
     def ClearAllBreakpointMarkers(self):
         openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
     def ClearAllBreakpointMarkers(self):
         openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-        for openDoc in openDocs:  
-            if isinstance(openDoc, CodeEditor.CodeDocument): 
-                openDoc.GetFirstView().MarkerDeleteAll(CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM) 
-                
+        for openDoc in openDocs:
+            if isinstance(openDoc, CodeEditor.CodeDocument):
+                openDoc.GetFirstView().MarkerDeleteAll(CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM)
+
     def UpdateBreakpointsFromMarkers(self, view, fileName):
         newbpLines = view.GetMarkerLines(CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM)
         self.SetBreakpointList(fileName, newbpLines)
     def UpdateBreakpointsFromMarkers(self, view, fileName):
         newbpLines = view.GetMarkerLines(CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM)
         self.SetBreakpointList(fileName, newbpLines)
-        
+
     def GetMasterBreakpointDict(self):
         return self._masterBPDict
     def GetMasterBreakpointDict(self):
         return self._masterBPDict
-        
+
     def SetAllBreakpointMarkers(self):
         openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
     def SetAllBreakpointMarkers(self):
         openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-        for openDoc in openDocs:  
-            if(isinstance(openDoc, CodeEditor.CodeDocument)): 
+        for openDoc in openDocs:
+            if(isinstance(openDoc, CodeEditor.CodeDocument)):
                 self.SetCurrentBreakpointMarkers(openDoc.GetFirstView())
                 self.SetCurrentBreakpointMarkers(openDoc.GetFirstView())
-            
+
     def SetCurrentBreakpointMarkers(self, view):
         if isinstance(view, CodeEditor.CodeView) and hasattr(view, 'GetDocument'):
             view.MarkerDeleteAll(CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM)
     def SetCurrentBreakpointMarkers(self, view):
         if isinstance(view, CodeEditor.CodeView) and hasattr(view, 'GetDocument'):
             view.MarkerDeleteAll(CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM)
-            for linenum in self.GetBreakpointList(view.GetDocument().GetFilename()): 
+            for linenum in self.GetBreakpointList(view.GetDocument().GetFilename()):
                 view.MarkerAdd(lineNum=int(linenum) - 1, marker_index=CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM)
 
                 view.MarkerAdd(lineNum=int(linenum) - 1, marker_index=CodeEditor.CodeCtrl.BREAKPOINT_MARKER_NUM)
 
+    def GetPhpDbgParam(self):
+        return self.phpDbgParam
+
+    def SetPhpDbgParam(self, value = None):
+        self.phpDbgParam = value
+
 class DebuggerOptionsPanel(wx.Panel):
 
 
 class DebuggerOptionsPanel(wx.Panel):
 
 
@@ -2076,7 +2773,7 @@ class DebuggerOptionsPanel(wx.Panel):
         localHostStaticText = wx.StaticText(self, -1, _("Local Host Name:"))
         self._LocalHostTextCtrl = wx.TextCtrl(self, -1, config.Read("DebuggerHostName", DEFAULT_HOST), size = (150, -1))
         portNumberStaticText = wx.StaticText(self, -1, _("Port Range:"))
         localHostStaticText = wx.StaticText(self, -1, _("Local Host Name:"))
         self._LocalHostTextCtrl = wx.TextCtrl(self, -1, config.Read("DebuggerHostName", DEFAULT_HOST), size = (150, -1))
         portNumberStaticText = wx.StaticText(self, -1, _("Port Range:"))
-        dashStaticText = wx.StaticText(self, -1, _("through to")) 
+        dashStaticText = wx.StaticText(self, -1, _("through to"))
         startingPort=config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
         self._PortNumberTextCtrl = wx.lib.intctrl.IntCtrl(self, -1, startingPort, size = (50, -1))
         self._PortNumberTextCtrl.SetMin(1)#What are real values?
         startingPort=config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
         self._PortNumberTextCtrl = wx.lib.intctrl.IntCtrl(self, -1, startingPort, size = (50, -1))
         self._PortNumberTextCtrl.SetMin(1)#What are real values?
@@ -2099,59 +2796,57 @@ class DebuggerOptionsPanel(wx.Panel):
         self._flushPortsButton = wx.Button(self, FLUSH_PORTS_ID, "Reset Port List")
         wx.EVT_BUTTON(parent, FLUSH_PORTS_ID, self.FlushPorts)
         debuggerPanelSizer.Add(self._flushPortsButton, (2,2), (1,2), flag=wx.ALIGN_RIGHT)
         self._flushPortsButton = wx.Button(self, FLUSH_PORTS_ID, "Reset Port List")
         wx.EVT_BUTTON(parent, FLUSH_PORTS_ID, self.FlushPorts)
         debuggerPanelSizer.Add(self._flushPortsButton, (2,2), (1,2), flag=wx.ALIGN_RIGHT)
-        
+
         debuggerPanelBorderSizer.Add(debuggerPanelSizer, 0, wx.ALL, SPACE)
         self.SetSizer(debuggerPanelBorderSizer)
         self.Layout()
         parent.AddPage(self, _("Debugger"))
         debuggerPanelBorderSizer.Add(debuggerPanelSizer, 0, wx.ALL, SPACE)
         self.SetSizer(debuggerPanelBorderSizer)
         self.Layout()
         parent.AddPage(self, _("Debugger"))
-        
+
     def FlushPorts(self, event):
         if self._PortNumberTextCtrl.IsInBounds():
             config = wx.ConfigBase_Get()
             config.WriteInt("DebuggerStartingPort", self._PortNumberTextCtrl.GetValue())
     def FlushPorts(self, event):
         if self._PortNumberTextCtrl.IsInBounds():
             config = wx.ConfigBase_Get()
             config.WriteInt("DebuggerStartingPort", self._PortNumberTextCtrl.GetValue())
-            DebugCommandUI.NewPortRange()
+            PythonDebuggerUI.NewPortRange()
         else:
             wx.MessageBox(_("The starting port is not valid. Please change the value and try again.", "Invalid Starting Port Number"))
         else:
             wx.MessageBox(_("The starting port is not valid. Please change the value and try again.", "Invalid Starting Port Number"))
-                          
+
     def MinPortChange(self, event):
         self._EndPortNumberTextCtrl.Enable( True )
         self._EndPortNumberTextCtrl.SetValue( self._PortNumberTextCtrl.GetValue() + PORT_COUNT)
         self._EndPortNumberTextCtrl.Enable( False )
     def MinPortChange(self, event):
         self._EndPortNumberTextCtrl.Enable( True )
         self._EndPortNumberTextCtrl.SetValue( self._PortNumberTextCtrl.GetValue() + PORT_COUNT)
         self._EndPortNumberTextCtrl.Enable( False )
-        
+
     def OnOK(self, optionsDialog):
         config = wx.ConfigBase_Get()
         config.Write("DebuggerHostName", self._LocalHostTextCtrl.GetValue())
         if self._PortNumberTextCtrl.IsInBounds():
             config.WriteInt("DebuggerStartingPort", self._PortNumberTextCtrl.GetValue())
     def OnOK(self, optionsDialog):
         config = wx.ConfigBase_Get()
         config.Write("DebuggerHostName", self._LocalHostTextCtrl.GetValue())
         if self._PortNumberTextCtrl.IsInBounds():
             config.WriteInt("DebuggerStartingPort", self._PortNumberTextCtrl.GetValue())
-            
+
 
     def GetIcon(self):
         return getContinueIcon()
 
 
 class CommandPropertiesDialog(wx.Dialog):
 
     def GetIcon(self):
         return getContinueIcon()
 
 
 class CommandPropertiesDialog(wx.Dialog):
-    
-    def __init__(self, parent, title, projectService, currentProjectDocument, pythonOnly=False, okButtonName="Run", debugging=False):
+    def __init__(self, parent, title, projectService, currentProjectDocument, okButtonName="Run", debugging=False):
         self._projService = projectService
         self._pmext = None
         self._projService = projectService
         self._pmext = None
-        self._pyext = None
+        self._pyext = '.py'
+        self._phpext = '.php'
         for template in self._projService.GetDocumentManager().GetTemplates():
             if not ACTIVEGRID_BASE_IDE and template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
                 self._pmext = template.GetDefaultExtension()
         for template in self._projService.GetDocumentManager().GetTemplates():
             if not ACTIVEGRID_BASE_IDE and template.GetDocumentType() == ProcessModelEditor.ProcessModelDocument:
                 self._pmext = template.GetDefaultExtension()
-            if template.GetDocumentType() == PythonEditor.PythonDocument:
-                self._pyext = template.GetDefaultExtension()
-        self._pythonOnly = pythonOnly
-        self._currentProj = currentProjectDocument
+                break
+        self._currentProj = projectService.GetCurrentProject()
         self._projectNameList, self._projectDocumentList, selectedIndex = self.GetProjectList()
         if not self._projectNameList:
             wx.MessageBox(_("To run or debug you must have an open runnable file or project containing runnable files. Use File->Open to open the file you wish to run or debug."), _("Nothing to Run"))
         self._projectNameList, self._projectDocumentList, selectedIndex = self.GetProjectList()
         if not self._projectNameList:
             wx.MessageBox(_("To run or debug you must have an open runnable file or project containing runnable files. Use File->Open to open the file you wish to run or debug."), _("Nothing to Run"))
-            raise BadBadBad
+            raise Exception("Nothing to Run or Debug.")
 
         wx.Dialog.__init__(self, parent, -1, title)
 
         wx.Dialog.__init__(self, parent, -1, title)
-           
-        projStaticText = wx.StaticText(self, -1, _("Project:")) 
-        fileStaticText = wx.StaticText(self, -1, _("File:")) 
-        argsStaticText = wx.StaticText(self, -1, _("Arguments:")) 
+
+        projStaticText = wx.StaticText(self, -1, _("Project:"))
+        fileStaticText = wx.StaticText(self, -1, _("File:"))
+        argsStaticText = wx.StaticText(self, -1, _("Arguments:"))
         startInStaticText = wx.StaticText(self, -1, _("Start in:"))
         pythonPathStaticText = wx.StaticText(self, -1, _("PYTHONPATH:"))
         postpendStaticText = _("Postpend win32api path")
         startInStaticText = wx.StaticText(self, -1, _("Start in:"))
         pythonPathStaticText = wx.StaticText(self, -1, _("PYTHONPATH:"))
         postpendStaticText = _("Postpend win32api path")
@@ -2163,25 +2858,25 @@ class CommandPropertiesDialog(wx.Dialog):
         if wx.Platform == "__WXMAC__":
             GAP = 10
         flexGridSizer = wx.GridBagSizer(GAP, GAP)
         if wx.Platform == "__WXMAC__":
             GAP = 10
         flexGridSizer = wx.GridBagSizer(GAP, GAP)
-        
+
         flexGridSizer.Add(projStaticText, (0,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         flexGridSizer.Add(self._projList, (0,1), (1,2), flag=wx.EXPAND)
         flexGridSizer.Add(projStaticText, (0,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         flexGridSizer.Add(self._projList, (0,1), (1,2), flag=wx.EXPAND)
-        
+
         flexGridSizer.Add(fileStaticText, (1,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         self._fileList = wx.Choice(self, -1)
         self.Bind(wx.EVT_CHOICE, self.OnFileSelected, self._fileList)
         flexGridSizer.Add(self._fileList, (1,1), (1,2), flag=wx.EXPAND)
         flexGridSizer.Add(fileStaticText, (1,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         self._fileList = wx.Choice(self, -1)
         self.Bind(wx.EVT_CHOICE, self.OnFileSelected, self._fileList)
         flexGridSizer.Add(self._fileList, (1,1), (1,2), flag=wx.EXPAND)
-        
+
         config = wx.ConfigBase_Get()
         config = wx.ConfigBase_Get()
-        self._lastArguments = config.Read("LastRunArguments")
+        self._lastArguments = config.Read(self.GetKey("LastRunArguments"))
         self._argsEntry = wx.TextCtrl(self, -1, str(self._lastArguments))
         self._argsEntry.SetToolTipString(str(self._lastArguments))
 
         flexGridSizer.Add(argsStaticText, (2,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         flexGridSizer.Add(self._argsEntry, (2,1), (1,2), flag=wx.EXPAND)
         self._argsEntry = wx.TextCtrl(self, -1, str(self._lastArguments))
         self._argsEntry.SetToolTipString(str(self._lastArguments))
 
         flexGridSizer.Add(argsStaticText, (2,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         flexGridSizer.Add(self._argsEntry, (2,1), (1,2), flag=wx.EXPAND)
-        
+
         flexGridSizer.Add(startInStaticText, (3,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         flexGridSizer.Add(startInStaticText, (3,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
-        self._lastStartIn = config.Read("LastRunStartIn")
+        self._lastStartIn = config.Read(self.GetKey("LastRunStartIn"))
         if not self._lastStartIn:
             self._lastStartIn = str(os.getcwd())
         self._startEntry = wx.TextCtrl(self, -1, self._lastStartIn)
         if not self._lastStartIn:
             self._lastStartIn = str(os.getcwd())
         self._startEntry = wx.TextCtrl(self, -1, self._lastStartIn)
@@ -2191,24 +2886,24 @@ class CommandPropertiesDialog(wx.Dialog):
         self._findDir = wx.Button(self, -1, _("Browse..."))
         self.Bind(wx.EVT_BUTTON, self.OnFindDirClick, self._findDir)
         flexGridSizer.Add(self._findDir, (3,2))
         self._findDir = wx.Button(self, -1, _("Browse..."))
         self.Bind(wx.EVT_BUTTON, self.OnFindDirClick, self._findDir)
         flexGridSizer.Add(self._findDir, (3,2))
-        
+
         flexGridSizer.Add(pythonPathStaticText, (4,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         if os.environ.has_key('PYTHONPATH'):
             startval = os.environ['PYTHONPATH']
         else:
             startval = ""
         flexGridSizer.Add(pythonPathStaticText, (4,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
         if os.environ.has_key('PYTHONPATH'):
             startval = os.environ['PYTHONPATH']
         else:
             startval = ""
-        self._lastPythonPath = config.Read("LastPythonPath", startval)
+        self._lastPythonPath = config.Read(self.GetKey("LastPythonPath"), startval)
         self._pythonPathEntry = wx.TextCtrl(self, -1, self._lastPythonPath)
         self._pythonPathEntry.SetToolTipString(self._lastPythonPath)
         flexGridSizer.Add(self._pythonPathEntry, (4,1), (1,2), flag=wx.EXPAND)
         self._pythonPathEntry = wx.TextCtrl(self, -1, self._lastPythonPath)
         self._pythonPathEntry.SetToolTipString(self._lastPythonPath)
         flexGridSizer.Add(self._pythonPathEntry, (4,1), (1,2), flag=wx.EXPAND)
-        
+
         if debugging and _WINDOWS:
             self._postpendCheckBox = wx.CheckBox(self, -1, postpendStaticText)
         if debugging and _WINDOWS:
             self._postpendCheckBox = wx.CheckBox(self, -1, postpendStaticText)
-            checked = bool(config.ReadInt("PythonPathPostpend", 1))
+            checked = bool(config.ReadInt(self.GetKey("PythonPathPostpend"), 1))
             self._postpendCheckBox.SetValue(checked)
             flexGridSizer.Add(self._postpendCheckBox, (5,1), flag=wx.EXPAND)
         cpPanelBorderSizer.Add(flexGridSizer, 0, flag=wx.ALL, border=10)
             self._postpendCheckBox.SetValue(checked)
             flexGridSizer.Add(self._postpendCheckBox, (5,1), flag=wx.EXPAND)
         cpPanelBorderSizer.Add(flexGridSizer, 0, flag=wx.ALL, border=10)
-        
+
         box = wx.StdDialogButtonSizer()
         self._okButton = wx.Button(self, wx.ID_OK, okButtonName)
         self._okButton.SetDefault()
         box = wx.StdDialogButtonSizer()
         self._okButton = wx.Button(self, wx.ID_OK, okButtonName)
         self._okButton.SetDefault()
@@ -2220,14 +2915,15 @@ class CommandPropertiesDialog(wx.Dialog):
         box.AddButton(btn)
         box.Realize()
         cpPanelBorderSizer.Add(box, 0, flag=wx.ALIGN_RIGHT|wx.ALL, border=5)
         box.AddButton(btn)
         box.Realize()
         cpPanelBorderSizer.Add(box, 0, flag=wx.ALIGN_RIGHT|wx.ALL, border=5)
-        
+
         self.SetSizer(cpPanelBorderSizer)
         self.SetSizer(cpPanelBorderSizer)
-                
+
         # Set up selections based on last values used.
         self._fileNameList = None
         # Set up selections based on last values used.
         self._fileNameList = None
-        self._selectedFileIndex = 0
-        lastProject = config.Read("LastRunProject")
-        lastFile = config.Read("LastRunFile")
+        self._selectedFileIndex = -1
+        lastProject = config.Read(self.GetKey("LastRunProject"))
+        lastFile = config.Read(self.GetKey("LastRunFile"))
+        self._mustShow = not lastFile
 
         if lastProject in self._projectNameList:
             selectedIndex = self._projectNameList.index(lastProject)
 
         if lastProject in self._projectNameList:
             selectedIndex = self._projectNameList.index(lastProject)
@@ -2240,7 +2936,14 @@ class CommandPropertiesDialog(wx.Dialog):
 
         cpPanelBorderSizer.Fit(self)
 
 
         cpPanelBorderSizer.Fit(self)
 
-            
+    def MustShowDialog(self):
+        return self._mustShow
+
+    def GetKey(self, lastPart):
+        if self._currentProj:
+            return "%s/%s/%s" % (ProjectEditor.PROJECT_KEY, self._currentProj.GetFilename().replace(os.sep, '|'), lastPart)
+
+
     def OnOKClick(self, event):
         startIn = self._startEntry.GetValue()
         fileToRun = self._fileList.GetStringSelection()
     def OnOKClick(self, event):
         startIn = self._startEntry.GetValue()
         fileToRun = self._fileList.GetStringSelection()
@@ -2252,22 +2955,22 @@ class CommandPropertiesDialog(wx.Dialog):
             wx.MessageBox(_("Starting directory does not exist. Please change this value."))
             return
         config = wx.ConfigBase_Get()
             wx.MessageBox(_("Starting directory does not exist. Please change this value."))
             return
         config = wx.ConfigBase_Get()
-        config.Write("LastRunProject", self._projectNameList[self._selectedProjectIndex])
-        config.Write("LastRunFile", fileToRun)
+        config.Write(self.GetKey("LastRunProject"), self._projectNameList[self._selectedProjectIndex])
+        config.Write(self.GetKey("LastRunFile"), fileToRun)
         # Don't update the arguments or starting directory unless we're runing python.
         if isPython:
         # Don't update the arguments or starting directory unless we're runing python.
         if isPython:
-            config.Write("LastRunArguments", self._argsEntry.GetValue())
-            config.Write("LastRunStartIn", self._startEntry.GetValue())
-            config.Write("LastPythonPath",self._pythonPathEntry.GetValue())
+            config.Write(self.GetKey("LastRunArguments"), self._argsEntry.GetValue())
+            config.Write(self.GetKey("LastRunStartIn"), self._startEntry.GetValue())
+            config.Write(self.GetKey("LastPythonPath"),self._pythonPathEntry.GetValue())
             if hasattr(self, "_postpendCheckBox"):
             if hasattr(self, "_postpendCheckBox"):
-                config.WriteInt("PythonPathPostpend", int(self._postpendCheckBox.GetValue()))
+                config.WriteInt(self.GetKey("PythonPathPostpend"), int(self._postpendCheckBox.GetValue()))
 
         self.EndModal(wx.ID_OK)
 
         self.EndModal(wx.ID_OK)
-            
-    def GetSettings(self):  
+
+    def GetSettings(self):
         projectPath = self._selectedProjectDocument.GetFilename()
         filename = self._fileNameList[self._selectedFileIndex]
         projectPath = self._selectedProjectDocument.GetFilename()
         filename = self._fileNameList[self._selectedFileIndex]
-        args = self._argsEntry.GetValue() 
+        args = self._argsEntry.GetValue()
         startIn = self._startEntry.GetValue()
         isPython = filename.endswith(self._pyext)
         env = os.environ
         startIn = self._startEntry.GetValue()
         isPython = filename.endswith(self._pyext)
         env = os.environ
@@ -2279,15 +2982,15 @@ class CommandPropertiesDialog(wx.Dialog):
             env['PYTHONPATH'] = self._pythonPathEntry.GetValue() + os.pathsep + os.path.join(os.getcwd(), "3rdparty", "pywin32")
         else:
             env['PYTHONPATH'] = self._pythonPathEntry.GetValue()
             env['PYTHONPATH'] = self._pythonPathEntry.GetValue() + os.pathsep + os.path.join(os.getcwd(), "3rdparty", "pywin32")
         else:
             env['PYTHONPATH'] = self._pythonPathEntry.GetValue()
-            
+
         return projectPath, filename, args, startIn, isPython, env
         return projectPath, filename, args, startIn, isPython, env
-                      
+
     def OnFileSelected(self, event):
         self._selectedFileIndex = self._fileList.GetSelection()
         self.EnableForFileType(event.GetString())
     def OnFileSelected(self, event):
         self._selectedFileIndex = self._fileList.GetSelection()
         self.EnableForFileType(event.GetString())
-        
+
     def EnableForFileType(self, fileName):
     def EnableForFileType(self, fileName):
-        show = fileName.endswith(self._pyext)
+        show = fileName.endswith(self._pyext) or fileName.endswith(self._phpext)
         self._startEntry.Enable(show)
         self._findDir.Enable(show)
         self._argsEntry.Enable(show)
         self._startEntry.Enable(show)
         self._findDir.Enable(show)
         self._argsEntry.Enable(show)
@@ -2296,12 +2999,16 @@ class CommandPropertiesDialog(wx.Dialog):
             self._lastStartIn = self._startEntry.GetValue()
             self._startEntry.SetValue("")
             self._lastArguments = self._argsEntry.GetValue()
             self._lastStartIn = self._startEntry.GetValue()
             self._startEntry.SetValue("")
             self._lastArguments = self._argsEntry.GetValue()
-            self._argsEntry.SetValue("")  
+            self._argsEntry.SetValue("")
         else:
         else:
-            self._startEntry.SetValue(self._lastStartIn)
-            self._argsEntry.SetValue(self._lastArguments)  
-                
-            
+            if fileName.endswith(self._phpext):
+                self._startEntry.SetValue(os.path.dirname(fileName))
+            else:
+                self._startEntry.SetValue(self._lastStartIn)
+            self._argsEntry.SetValue(self._lastArguments)
+
+
+
     def OnFindDirClick(self, event):
         dlg = wx.DirDialog(self, "Choose a starting directory:", self._startEntry.GetValue(),
                           style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON)
     def OnFindDirClick(self, event):
         dlg = wx.DirDialog(self, "Choose a starting directory:", self._startEntry.GetValue(),
                           style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON)
@@ -2309,9 +3016,9 @@ class CommandPropertiesDialog(wx.Dialog):
         dlg.CenterOnParent()
         if dlg.ShowModal() == wx.ID_OK:
             self._startEntry.SetValue(dlg.GetPath())
         dlg.CenterOnParent()
         if dlg.ShowModal() == wx.ID_OK:
             self._startEntry.SetValue(dlg.GetPath())
-            
-        dlg.Destroy() 
-           
+
+        dlg.Destroy()
+
 
     def EvtListBox(self, event):
         if event.GetString():
 
     def EvtListBox(self, event):
         if event.GetString():
@@ -2319,14 +3026,11 @@ class CommandPropertiesDialog(wx.Dialog):
             self._selectedProjectDocument = self._projectDocumentList[index]
             self._selectedProjectIndex = index
             self.PopulateFileList(self._selectedProjectDocument)
             self._selectedProjectDocument = self._projectDocumentList[index]
             self._selectedProjectIndex = index
             self.PopulateFileList(self._selectedProjectDocument)
-            
+
     def FilterFileList(self, list):
     def FilterFileList(self, list):
-        if self._pythonOnly:
-            files = filter(lambda f: f.endswith(self._pyext), list)
-        else:
-            files = filter(lambda f: (self._pmext and f.endswith(self._pmext)) or f.endswith(self._pyext), list)
+        files = filter(lambda f: (self._phpext and f.endswith(self._phpext)) or (self._pmext and f.endswith(self._pmext)) or f.endswith(self._pyext), list)
         return files
         return files
-                 
+
     def PopulateFileList(self, project, shortNameToSelect=None):
         self._fileNameList = self.FilterFileList(project.GetFiles()[:])
         self._fileList.Clear()
     def PopulateFileList(self, project, shortNameToSelect=None):
         self._fileNameList = self.FilterFileList(project.GetFiles()[:])
         self._fileList.Clear()
@@ -2336,15 +3040,23 @@ class CommandPropertiesDialog(wx.Dialog):
         strings = map(lambda file: os.path.basename(file), self._fileNameList)
         for index in range(0, len(strings)):
             if shortNameToSelect == strings[index]:
         strings = map(lambda file: os.path.basename(file), self._fileNameList)
         for index in range(0, len(strings)):
             if shortNameToSelect == strings[index]:
-                self._selectedFileIndex = index 
-                break 
+                self._selectedFileIndex = index
+                break
         self._fileList.Hide()
         self._fileList.AppendItems(strings)
         self._fileList.Show()
         self._fileList.Hide()
         self._fileList.AppendItems(strings)
         self._fileList.Show()
-        if self._selectedFileIndex not in range(0, len(strings)) : self._selectedFileIndex = 0
+        if self._selectedFileIndex not in range(0, len(strings)):
+            # Pick first bpel file if there is one.
+            for index in range(0, len(strings)):
+                if strings[index].endswith('.bpel'):
+                    self._selectedFileIndex = index
+                    break
+        # Still no selected file, use first file.      
+        if self._selectedFileIndex not in range(0, len(strings)):
+            self._selectedFileIndex = 0
         self._fileList.SetSelection(self._selectedFileIndex)
         self.EnableForFileType(strings[self._selectedFileIndex])
         self._fileList.SetSelection(self._selectedFileIndex)
         self.EnableForFileType(strings[self._selectedFileIndex])
-        
+
     def GetProjectList(self):
         docList = []
         nameList = []
     def GetProjectList(self):
         docList = []
         nameList = []
@@ -2365,25 +3077,25 @@ class CommandPropertiesDialog(wx.Dialog):
                 if projectDocument.IsFileInProject(fileName):
                     return True
             return False
                 if projectDocument.IsFileInProject(fileName):
                     return True
             return False
-            
+
         unprojectedFiles = []
         for document in self._projService.GetDocumentManager().GetDocuments():
             if not ACTIVEGRID_BASE_IDE and type(document) == ProcessModelEditor.ProcessModelDocument:
                 if not AlreadyInProject(document.GetFilename()):
                     unprojectedFiles.append(document.GetFilename())
         unprojectedFiles = []
         for document in self._projService.GetDocumentManager().GetDocuments():
             if not ACTIVEGRID_BASE_IDE and type(document) == ProcessModelEditor.ProcessModelDocument:
                 if not AlreadyInProject(document.GetFilename()):
                     unprojectedFiles.append(document.GetFilename())
-            if type(document) == PythonEditor.PythonDocument:
+            if type(document) == PythonEditor.PythonDocument or type(document) == PHPEditor.PHPDocument:
                 if not AlreadyInProject(document.GetFilename()):
                     unprojectedFiles.append(document.GetFilename())
                 if not AlreadyInProject(document.GetFilename()):
                     unprojectedFiles.append(document.GetFilename())
-         
+
         if unprojectedFiles:
             unprojProj = ProjectEditor.ProjectDocument()
         if unprojectedFiles:
             unprojProj = ProjectEditor.ProjectDocument()
-            unprojProj.SetFilename(_("Not in any Project")) 
+            unprojProj.SetFilename(_("Not in any Project"))
             unprojProj.AddFiles(unprojectedFiles)
             docList.append(unprojProj)
             unprojProj.AddFiles(unprojectedFiles)
             docList.append(unprojProj)
-            nameList.append(_("Not in any Project"))  
-            
+            nameList.append(_("Not in any Project"))
+
         return nameList, docList, index
         return nameList, docList, index
-        
+
 
 #----------------------------------------------------------------------
 from wx import ImageFromStream, BitmapFromImage
 
 #----------------------------------------------------------------------
 from wx import ImageFromStream, BitmapFromImage
@@ -2400,7 +3112,7 @@ def getBreakData():
 \x03\x98\xa9z\x07\x00%\xd6\xa9\xd27\x90\xac\xbbk\xe5\x15I\xcdD$\xdc\xa7\xceT\
 5a\xce\xf3\xe4\xa0\xaa\x8bO\x12\x11\xabC\xcb\x9c}\xd57\xef\xb0\xf3\xb7\x86p\
 \x97\xf7\xb5\xaa\xde\xb9\xfa|-O\xbdjN\x9b\xf8\x06A\xcb\x00\x00\x00\x00IEND\
 \x03\x98\xa9z\x07\x00%\xd6\xa9\xd27\x90\xac\xbbk\xe5\x15I\xcdD$\xdc\xa7\xceT\
 5a\xce\xf3\xe4\xa0\xaa\x8bO\x12\x11\xabC\xcb\x9c}\xd57\xef\xb0\xf3\xb7\x86p\
 \x97\xf7\xb5\xaa\xde\xb9\xfa|-O\xbdjN\x9b\xf8\x06A\xcb\x00\x00\x00\x00IEND\
-\xaeB`\x82' 
+\xaeB`\x82'
 
 def getBreakBitmap():
     return BitmapFromImage(getBreakImage())
 
 def getBreakBitmap():
     return BitmapFromImage(getBreakImage())
@@ -2426,7 +3138,7 @@ def getClearOutputData():
 \xda\xa7\x92\xfb\xc5w\xdf\t\x07\xc4\x05ym{\xd0\x1a\xe3\xb9xS\x81\x04\x18\x05\
 \xc9\x04\xc9a\x00Dc9\x9d\x82\xa4\xbc\xe8P\xb2\xb5P\xac\xf2\x0c\xd4\xf5\x00\
 \x88>\xac\xe17\x84\xe4\xb9G\x8b7\x9f\xf3\x1fsUl^\x7f\xe7y\x0f\x00\x00\x00\
 \xda\xa7\x92\xfb\xc5w\xdf\t\x07\xc4\x05ym{\xd0\x1a\xe3\xb9xS\x81\x04\x18\x05\
 \xc9\x04\xc9a\x00Dc9\x9d\x82\xa4\xbc\xe8P\xb2\xb5P\xac\xf2\x0c\xd4\xf5\x00\
 \x88>\xac\xe17\x84\xe4\xb9G\x8b7\x9f\xf3\x1fsUl^\x7f\xe7y\x0f\x00\x00\x00\
-\x00IEND\xaeB`\x82' 
+\x00IEND\xaeB`\x82'
 
 def getClearOutputBitmap():
     return BitmapFromImage(getClearOutputImage())
 
 def getClearOutputBitmap():
     return BitmapFromImage(getClearOutputImage())
@@ -2452,7 +3164,7 @@ def getCloseData():
 \x08!\x84\x90y\x9e\x11\xf1\x8bP\x96\xa5\xef\xdd\xb6\xad\xb5VJ\xf9\x9b\xe0\
 \xe9\xa6i8\xe7\xbe\xdb\xb6mi\x9a\x0e\xc3\xf0F\x88\xe3\x18\x00\xfa\xbe\x0f\
 \xc3\xd0\'\x9c\xf3eY\xa2(*\x8ab\xc7\x9e\xaed\x8c\xa1\x94\xben\xf5\xb1\xd2W\
 \x08!\x84\x90y\x9e\x11\xf1\x8bP\x96\xa5\xef\xdd\xb6\xad\xb5VJ\xf9\x9b\xe0\
 \xe9\xa6i8\xe7\xbe\xdb\xb6mi\x9a\x0e\xc3\xf0F\x88\xe3\x18\x00\xfa\xbe\x0f\
 \xc3\xd0\'\x9c\xf3eY\xa2(*\x8ab\xc7\x9e\xaed\x8c\xa1\x94\xben\xf5\xb1\xd2W\
-\xfa,\xfce.\x0b\x0f\xb8\x96e\x90gS\xe0v\x00\x00\x00\x00IEND\xaeB`\x82' 
+\xfa,\xfce.\x0b\x0f\xb8\x96e\x90gS\xe0v\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getCloseBitmap():
     return BitmapFromImage(getCloseImage())
 
 def getCloseBitmap():
     return BitmapFromImage(getCloseImage())
@@ -2477,7 +3189,7 @@ def getContinueData():
 \x98\xc4\x07\x01\x00D\x1dd^\xa8\xa8j\x9ew\xed`\xa9\x16\x99\xde\xa6G\x8b\xd3Y\
 \xe6\x85]\n\r\x7f\x99\xf5\x96Jnlz#\xab\xdb\xc1\x17\x19\xb0XV\xc2\xdf\xa3)\
 \x85<\xe4\x88\x85.F\x9a\xf3H3\xb0\xf3g\xda\xd2\x0b\xc5_|\x17\xe8\xf5R\xd6\
 \x98\xc4\x07\x01\x00D\x1dd^\xa8\xa8j\x9ew\xed`\xa9\x16\x99\xde\xa6G\x8b\xd3Y\
 \xe6\x85]\n\r\x7f\x99\xf5\x96Jnlz#\xab\xdb\xc1\x17\x19\xb0XV\xc2\xdf\xa3)\
 \x85<\xe4\x88\x85.F\x9a\xf3H3\xb0\xf3g\xda\xd2\x0b\xc5_|\x17\xe8\xf5R\xd6\
-\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getContinueBitmap():
     return BitmapFromImage(getContinueImage())
 
 def getContinueBitmap():
     return BitmapFromImage(getContinueImage())
@@ -2500,7 +3212,7 @@ gK\x06\x00 \xa5=k\x00\x00\xb0\xb2]\xd4?5f\xb1\xdb\xaf\xc6\xa2\xcb\xa8\xf0?\
 \x1c\x98\xae\x82\xbf\x81\xa4\x8eA\x16\xe1\n\xd1\xa4\x19\xb3\xe9\n\xce\xe8\
 \xf1\n\x9eg^\x18\x18\x90\xec<\x11\xf9#\x04XMZ\x19\xaac@+\x94\xd4\x99)SeP\xa1\
 )\xd6\x1dI\xe7*\xdc\xf4\x03\xdf~\xe7\x13T^Q?:X\x19d\x00\x00\x00\x00IEND\xaeB\
 \x1c\x98\xae\x82\xbf\x81\xa4\x8eA\x16\xe1\n\xd1\xa4\x19\xb3\xe9\n\xce\xe8\
 \xf1\n\x9eg^\x18\x18\x90\xec<\x11\xf9#\x04XMZ\x19\xaac@+\x94\xd4\x99)SeP\xa1\
 )\xd6\x1dI\xe7*\xdc\xf4\x03\xdf~\xe7\x13T^Q?:X\x19d\x00\x00\x00\x00IEND\xaeB\
-`\x82' 
+`\x82'
 
 def getNextBitmap():
     return BitmapFromImage(getNextImage())
 
 def getNextBitmap():
     return BitmapFromImage(getNextImage())
@@ -2522,7 +3234,7 @@ def getStepInData():
 \x96\xd9#\xf6\x06\xc3;p1I\xd1\x14\x0b#|\x17aF\xec\r\xeeF\xa0eB\xd34\xca\xd0A\
 ]j\x84\xa6\x03\x00""\xb7\xb0tRZ\xf7x\xb7\x83\x91]\xcb\x7fa\xd9\x89\x0fC\xfd\
 \x94\x9d|9\x99^k\x13\xa1 \xb3\x16\x0f#\xd4\x88N~\x14\xe1-\x96\x7f\xe3\x0f\
 \x96\xd9#\xf6\x06\xc3;p1I\xd1\x14\x0b#|\x17aF\xec\r\xeeF\xa0eB\xd34\xca\xd0A\
 ]j\x84\xa6\x03\x00""\xb7\xb0tRZ\xf7x\xb7\x83\x91]\xcb\x7fa\xd9\x89\x0fC\xfd\
 \x94\x9d|9\x99^k\x13\xa1 \xb3\x16\x0f#\xd4\x88N~\x14\xe1-\x96\x7f\xe3\x0f\
-\x11\x91UC\x0cX\'\x1e\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x11\x91UC\x0cX\'\x1e\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getStepInBitmap():
     return BitmapFromImage(getStepInImage())
 
 def getStepInBitmap():
     return BitmapFromImage(getStepInImage())
@@ -2542,7 +3254,7 @@ def getStopData():
 \x00\x00QIDAT8\x8d\xdd\x93A\n\xc00\x08\x04g\xb5\xff\x7fq\x13sn\xda&\x01\x0b\
 \xa5]\xf0"\xec(.J\xe6dd)\xf7\x13\x80\xadoD-12\xc8\\\xd3\r\xe2\xa6\x00j\xd9\
 \x0f\x03\xde\xbf\xc1\x0f\x00\xa7\x18\x01t\xd5\\\x05\xc8\\}T#\xe9\xfb\xbf\x90\
 \x00\x00QIDAT8\x8d\xdd\x93A\n\xc00\x08\x04g\xb5\xff\x7fq\x13sn\xda&\x01\x0b\
 \xa5]\xf0"\xec(.J\xe6dd)\xf7\x13\x80\xadoD-12\xc8\\\xd3\r\xe2\xa6\x00j\xd9\
 \x0f\x03\xde\xbf\xc1\x0f\x00\xa7\x18\x01t\xd5\\\x05\xc8\\}T#\xe9\xfb\xbf\x90\
-\x064\xd8\\\x12\x1fQM\xf5\xd9\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x064\xd8\\\x12\x1fQM\xf5\xd9\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getStopBitmap():
     return BitmapFromImage(getStopImage())
 
 def getStopBitmap():
     return BitmapFromImage(getStopImage())
@@ -2565,7 +3277,7 @@ def getStepReturnData():
 \x84\xbf\x16V\xb0l\x01@\no\x86\xae\x82Q\xa8=\xa4\x0c\x80\xe70\xbd\x10jh\xbd\
 \x07R\x06#\xc9^N\xb6\xde\x03)\x83\x18\xaeU\x90\x9c>a\xb2P\r\xb3&/Y\xa8\xd1^^\
 \xb6\xf0\x16\xdb\xbf\xf1\x02\x81\xa5TK\x1d\x07\xde\x92\x00\x00\x00\x00IEND\
 \x84\xbf\x16V\xb0l\x01@\no\x86\xae\x82Q\xa8=\xa4\x0c\x80\xe70\xbd\x10jh\xbd\
 \x07R\x06#\xc9^N\xb6\xde\x03)\x83\x18\xaeU\x90\x9c>a\xb2P\r\xb3&/Y\xa8\xd1^^\
 \xb6\xf0\x16\xdb\xbf\xf1\x02\x81\xa5TK\x1d\x07\xde\x92\x00\x00\x00\x00IEND\
-\xaeB`\x82" 
+\xaeB`\x82"
 
 def getStepReturnBitmap():
     return BitmapFromImage(getStepReturnImage())
 
 def getStepReturnBitmap():
     return BitmapFromImage(getStepReturnImage())
@@ -2576,7 +3288,7 @@ def getStepReturnImage():
 
 def getStepReturnIcon():
     return wx.IconFromBitmap(getStepReturnBitmap())
 
 def getStepReturnIcon():
     return wx.IconFromBitmap(getStepReturnBitmap())
-    
+
 #----------------------------------------------------------------------
 def getAddWatchData():
     return \
 #----------------------------------------------------------------------
 def getAddWatchData():
     return \
@@ -2587,7 +3299,7 @@ def getAddWatchData():
 \xeb,!w\x100 \x1dK\xac\x10\r\x08\x05".yFL\x85\x8c\x18b\xa8|Ty\xa2\x13\x92\'\
 \xc3\xe4\xff\x9f\x18\x1e3\xb82t\xa2\x88\x13\xedg.\x06aa&\x06VV\x7f\x86\xb9\
 \xcfU\x19\xbc\xb0\xba\x86h\xe0\xc8\xd0\xfc\xbf\x80\xe1>q)\x94\xe6\x00\x00\
 \xeb,!w\x100 \x1dK\xac\x10\r\x08\x05".yFL\x85\x8c\x18b\xa8|Ty\xa2\x13\x92\'\
 \xc3\xe4\xff\x9f\x18\x1e3\xb82t\xa2\x88\x13\xedg.\x06aa&\x06VV\x7f\x86\xb9\
 \xcfU\x19\xbc\xb0\xba\x86h\xe0\xc8\xd0\xfc\xbf\x80\xe1>q)\x94\xe6\x00\x00\
-\x85\x923_\xd22\xa4\xcd\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x85\x923_\xd22\xa4\xcd\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getAddWatchBitmap():
     return BitmapFromImage(getAddWatchImage())
 
 def getAddWatchBitmap():
     return BitmapFromImage(getAddWatchImage())
@@ -2598,7 +3310,7 @@ def getAddWatchImage():
 
 def getAddWatchIcon():
     return wx.IconFromBitmap(getAddWatchBitmap())
 
 def getAddWatchIcon():
     return wx.IconFromBitmap(getAddWatchBitmap())
-    
+
 #----------------------------------------------------------------------
 def getRunningManData():
     return \
 #----------------------------------------------------------------------
 def getRunningManData():
     return \
@@ -2619,7 +3331,7 @@ CF\xe2t\xef\x1b>\x1f\x8c3Q\xf0\x11\xd3p\xa2yf\x1a\xbc\xcb\n\xdee\x85\xdd>\
 \x03[\xb0.XP\xcafu^m\x04>\x18\xd7\x9aM\xe4\xea\xba\xc0x\xec\x8c\xa9\xca*^\
 \xa5\x1b}\xc0u*\xc9B\xd14\x12\xe8\x97%\x15\xcbF`\xdaH\xba\x80P4\r)\x13#R\xc6\
 \xf0\xdc\x8f2\x01\x80\x94\x89\xe9>\xc9(\xcd:\xb6\xd9\x1aw\xa0\x95i\xf8\x0e\
 \x03[\xb0.XP\xcafu^m\x04>\x18\xd7\x9aM\xe4\xea\xba\xc0x\xec\x8c\xa9\xca*^\
 \xa5\x1b}\xc0u*\xc9B\xd14\x12\xe8\x97%\x15\xcbF`\xdaH\xba\x80P4\r)\x13#R\xc6\
 \xf0\xdc\x8f2\x01\x80\x94\x89\xe9>\xc9(\xcd:\xb6\xd9\x1aw\xa0\x95i\xf8\x0e\
-\xc6\xd1\'\'\x86\xa2\xd5\x8d \xbe@\x00\x00\x00\x00IEND\xaeB`\x82' 
+\xc6\xd1\'\'\x86\xa2\xd5\x8d \xbe@\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getRunningManBitmap():
     return BitmapFromImage(getRunningManImage())
 
 def getRunningManBitmap():
     return BitmapFromImage(getRunningManImage())
@@ -2655,7 +3367,7 @@ f?*4\xd1\xf6\xa2\x0f\x80\x93\xf4\x8e\xe1\xb8\xf2\xf1\xb5\x18\x9cH(\x80\xe4bT\
 \x8d\xa9\x9cf\x1dq\x9au\xc4\x8dM\x0c\x85#\xa2x\x91cw\xd2\xd6i\x83\trk\x13\
 \x9f\x0fL\xab\xda\xe6\xd4\xd6Y+\xf1h\x8f\xb9T~G\xd2\x11\xb4\xd4\xe7O[\xf7\
 \x1e\xd6\x9d\xc7\xe4\xb7\xbe\x86\xf8\xb1?\xf4\x9c\xff\x01\xbe\xe9\xaf\x96\
 \x8d\xa9\x9cf\x1dq\x9au\xc4\x8dM\x0c\x85#\xa2x\x91cw\xd2\xd6i\x83\trk\x13\
 \x9f\x0fL\xab\xda\xe6\xd4\xd6Y+\xf1h\x8f\xb9T~G\xd2\x11\xb4\xd4\xe7O[\xf7\
 \x1e\xd6\x9d\xc7\xe4\xb7\xbe\x86\xf8\xb1?\xf4\x9c\xff\x01\xbe\xe9\xaf\x96\
-\xf0\x7fPA\x00\x00\x00\x00IEND\xaeB`\x82' 
+\xf0\x7fPA\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getDebuggingManBitmap():
     return BitmapFromImage(getDebuggingManImage())
 
 def getDebuggingManBitmap():
     return BitmapFromImage(getDebuggingManImage())
index 32e4fcffeb105b6a0ebfc9104dbe2ab8f2b341ea..535c46eee898ddd926c0ef07ce1b3b006dfaa2a5 100644 (file)
@@ -6,7 +6,7 @@
 #
 # Created:      5/23/05
 # CVS-ID:       $ID:$
 #
 # Created:      5/23/05
 # CVS-ID:       $ID:$
-# Copyright:    (c) 2005 ActiveGrid, Inc.
+# Copyright:    (c) 2005-2006 ActiveGrid, Inc.
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
@@ -17,27 +17,22 @@ import ProjectEditor
 import os
 import os.path
 import activegrid.util.xmlutils as xmlutils
 import os
 import os.path
 import activegrid.util.xmlutils as xmlutils
-
 _ = wx.GetTranslation
 
 
 _ = wx.GetTranslation
 
 
+#----------------------------------------------------------------------------
+# Constants
+#----------------------------------------------------------------------------
 SPACE = 10
 HALF_SPACE = 5
 
 
 SPACE = 10
 HALF_SPACE = 5
 
 
-EXTENSIONS_CONFIG_STRING = "Extensions"
-
-
-
-# TODO: Redo extensions menu on OK, or provide alert that it won't happen until restart
-
-
 #----------------------------------------------------------------------------
 # Classes
 #----------------------------------------------------------------------------
 
 class Extension:
 #----------------------------------------------------------------------------
 # Classes
 #----------------------------------------------------------------------------
 
 class Extension:
-    
+
 
     def __init__(self, menuItemName=None):
         self.menuItemName = menuItemName
 
     def __init__(self, menuItemName=None):
         self.menuItemName = menuItemName
@@ -48,7 +43,7 @@ class Extension:
         self.commandPostArgs = ''
         self.fileExt = None
         self.opOnSelectedFile = True
         self.commandPostArgs = ''
         self.fileExt = None
         self.opOnSelectedFile = True
-        
+
 
 class ExtensionService(wx.lib.pydocview.DocService):
 
 
 class ExtensionService(wx.lib.pydocview.DocService):
 
@@ -60,8 +55,8 @@ class ExtensionService(wx.lib.pydocview.DocService):
 
     def __getExtensionKeyName(extensionName):
         return "%s/%s" % (ExtensionService.EXTENSIONS_KEY, extensionName)
 
     def __getExtensionKeyName(extensionName):
         return "%s/%s" % (ExtensionService.EXTENSIONS_KEY, extensionName)
-      
-  
+
+
     __getExtensionKeyName = staticmethod(__getExtensionKeyName)
 
 
     __getExtensionKeyName = staticmethod(__getExtensionKeyName)
 
 
@@ -79,7 +74,7 @@ class ExtensionService(wx.lib.pydocview.DocService):
                 cont, value, index = config.GetNextEntry(index)
         finally:
             config.SetPath(path)
                 cont, value, index = config.GetNextEntry(index)
         finally:
             config.SetPath(path)
-                    
+
         for extensionName in extensionNames:
             extensionData = config.Read(self.__getExtensionKeyName(extensionName))
             if extensionData:
         for extensionName in extensionNames:
             extensionData = config.Read(self.__getExtensionKeyName(extensionName))
             if extensionData:
@@ -112,10 +107,10 @@ class ExtensionService(wx.lib.pydocview.DocService):
             toolsMenu = menuBar.GetMenu(toolsMenuIndex)
         else:
             toolsMenu = wx.Menu()
             toolsMenu = menuBar.GetMenu(toolsMenuIndex)
         else:
             toolsMenu = wx.Menu()
-        
+
         if self._extensions:
             if toolsMenu.GetMenuItems():
         if self._extensions:
             if toolsMenu.GetMenuItems():
-                toolsMenu.AppendSeparator()            
+                toolsMenu.AppendSeparator()
             for ext in self._extensions:
                 # Append a tool menu item for each extension
                 ext.id = wx.NewId()
             for ext in self._extensions:
                 # Append a tool menu item for each extension
                 ext.id = wx.NewId()
@@ -192,7 +187,7 @@ class ExtensionService(wx.lib.pydocview.DocService):
             if extension.commandPostArgs:
                 cmds.append(extension.commandPostArgs)
             os.spawnv(os.P_NOWAIT, extension.command, cmds)
             if extension.commandPostArgs:
                 cmds.append(extension.commandPostArgs)
             os.spawnv(os.P_NOWAIT, extension.command, cmds)
-                      
+
         else:
             cmd = extension.command
             if extension.commandPreArgs:
         else:
             cmd = extension.command
             if extension.commandPreArgs:
@@ -207,24 +202,24 @@ class ExtensionService(wx.lib.pydocview.DocService):
                 view.AddLines(line)
             view.GetControl().EnsureCaretVisible()
             f.close()
                 view.AddLines(line)
             view.GetControl().EnsureCaretVisible()
             f.close()
-            
+
 
 class ExtensionOptionsPanel(wx.Panel):
 
 
     def __init__(self, parent, id):
         wx.Panel.__init__(self, parent, id)
 
 class ExtensionOptionsPanel(wx.Panel):
 
 
     def __init__(self, parent, id):
         wx.Panel.__init__(self, parent, id)
-        
+
         extOptionsPanelBorderSizer = wx.BoxSizer(wx.VERTICAL)
         extOptionsPanelBorderSizer = wx.BoxSizer(wx.VERTICAL)
-        
+
         extOptionsPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
         extOptionsPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
-        
+
         extCtrlSizer = wx.BoxSizer(wx.VERTICAL)
         extCtrlSizer.Add(wx.StaticText(self, -1, _("External Tools:")), 0, wx.BOTTOM, HALF_SPACE)
         extCtrlSizer = wx.BoxSizer(wx.VERTICAL)
         extCtrlSizer.Add(wx.StaticText(self, -1, _("External Tools:")), 0, wx.BOTTOM, HALF_SPACE)
-        self._extListBox = wx.ListBox(self, -1, size=(-1,160), style=wx.LB_SINGLE)
+        self._extListBox = wx.ListBox(self, -1, style=wx.LB_SINGLE)
         self.Bind(wx.EVT_LISTBOX, self.OnListBoxSelect, self._extListBox)
         self.Bind(wx.EVT_LISTBOX, self.OnListBoxSelect, self._extListBox)
-        extCtrlSizer.Add(self._extListBox, 1, wx.BOTTOM | wx.EXPAND, SPACE)        
-        buttonSizer = wx.GridSizer(cols=2, vgap=5, hgap=10)
+        extCtrlSizer.Add(self._extListBox, 1, wx.BOTTOM | wx.EXPAND, SPACE)
+        buttonSizer = wx.GridSizer(cols=2, vgap=HALF_SPACE, hgap=HALF_SPACE)
         self._moveUpButton = wx.Button(self, -1, _("Move Up"))
         self.Bind(wx.EVT_BUTTON, self.OnMoveUp, self._moveUpButton)
         buttonSizer.Add(self._moveUpButton, 1, wx.EXPAND)
         self._moveUpButton = wx.Button(self, -1, _("Move Up"))
         self.Bind(wx.EVT_BUTTON, self.OnMoveUp, self._moveUpButton)
         buttonSizer.Add(self._moveUpButton, 1, wx.EXPAND)
@@ -243,21 +238,21 @@ class ExtensionOptionsPanel(wx.Panel):
         self._extDetailPanel = wx.Panel(self)
         staticBox = wx.StaticBox(self, label=_("Selected External Tool"))
         staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
         self._extDetailPanel = wx.Panel(self)
         staticBox = wx.StaticBox(self, label=_("Selected External Tool"))
         staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
-        
-        extDetailSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=3)
+
+        extDetailSizer = wx.FlexGridSizer(cols=2, vgap=5, hgap=5)
         extDetailSizer.AddGrowableCol(1,1)
 
         extDetailSizer.AddGrowableCol(1,1)
 
-        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Name:")))
+        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Name:")), flag=wx.ALIGN_CENTER_VERTICAL)
         self._menuItemNameTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         extDetailSizer.Add(self._menuItemNameTextCtrl, 0, wx.EXPAND)
         self._menuItemNameTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         extDetailSizer.Add(self._menuItemNameTextCtrl, 0, wx.EXPAND)
-        self.Bind(wx.EVT_TEXT, self.SaveCurrentItem, self._menuItemNameTextCtrl)        
+        self.Bind(wx.EVT_TEXT, self.SaveCurrentItem, self._menuItemNameTextCtrl)
 
 
-        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Description:")))
+        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Description:")), flag=wx.ALIGN_CENTER_VERTICAL)
         self._menuItemDescTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         extDetailSizer.Add(self._menuItemDescTextCtrl, 0, wx.EXPAND)
 
         self._menuItemDescTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         extDetailSizer.Add(self._menuItemDescTextCtrl, 0, wx.EXPAND)
 
-        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Path:")))
-        self._commandTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))        
+        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Path:")), flag=wx.ALIGN_CENTER_VERTICAL)
+        self._commandTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         findFileButton = wx.Button(self._extDetailPanel, -1, _("Browse..."))
         def OnBrowseButton(event):
             fileDlg = wx.FileDialog(self, _("Choose an Executable:"), style=wx.OPEN|wx.FILE_MUST_EXIST|wx.HIDE_READONLY|wx.CHANGE_DIR)
         findFileButton = wx.Button(self._extDetailPanel, -1, _("Browse..."))
         def OnBrowseButton(event):
             fileDlg = wx.FileDialog(self, _("Choose an Executable:"), style=wx.OPEN|wx.FILE_MUST_EXIST|wx.HIDE_READONLY|wx.CHANGE_DIR)
@@ -276,26 +271,26 @@ class ExtensionOptionsPanel(wx.Panel):
         hsizer.Add(findFileButton, 0, wx.LEFT, HALF_SPACE)
         extDetailSizer.Add(hsizer, 0, wx.EXPAND)
 
         hsizer.Add(findFileButton, 0, wx.LEFT, HALF_SPACE)
         extDetailSizer.Add(hsizer, 0, wx.EXPAND)
 
-        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Pre Args:")))
+        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Pre Args:")), flag=wx.ALIGN_CENTER_VERTICAL)
         self._commandPreArgsTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         extDetailSizer.Add(self._commandPreArgsTextCtrl, 0, wx.EXPAND)
 
         self._commandPreArgsTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         extDetailSizer.Add(self._commandPreArgsTextCtrl, 0, wx.EXPAND)
 
-        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Post Args:")))
+        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Post Args:")), flag=wx.ALIGN_CENTER_VERTICAL)
         self._commandPostArgsTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         extDetailSizer.Add(self._commandPostArgsTextCtrl, 0, wx.EXPAND)
 
         self._commandPostArgsTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         extDetailSizer.Add(self._commandPostArgsTextCtrl, 0, wx.EXPAND)
 
-        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("File Extensions:")))
+        extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("File Extensions:")), flag=wx.ALIGN_CENTER_VERTICAL)
         self._fileExtTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         self._fileExtTextCtrl.SetToolTipString(_("""For example: "txt, text" (comma separated) or "*" for all files"""))
         extDetailSizer.Add(self._fileExtTextCtrl, 0, wx.EXPAND)
 
         self._selFileCtrl = wx.CheckBox(self._extDetailPanel, -1, _("Operate on Selected File"))
         self._fileExtTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
         self._fileExtTextCtrl.SetToolTipString(_("""For example: "txt, text" (comma separated) or "*" for all files"""))
         extDetailSizer.Add(self._fileExtTextCtrl, 0, wx.EXPAND)
 
         self._selFileCtrl = wx.CheckBox(self._extDetailPanel, -1, _("Operate on Selected File"))
-        extDetailSizer.Add(self._selFileCtrl)
+        extDetailSizer.Add(self._selFileCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
         self._selFileCtrl.SetToolTipString(_("If focus is in the project, instead of operating on the project file, operate on the selected file."))
 
         self._extDetailPanel.SetSizer(extDetailSizer)
         staticBoxSizer.Add(self._extDetailPanel, 1, wx.ALL|wx.EXPAND, SPACE)
         self._selFileCtrl.SetToolTipString(_("If focus is in the project, instead of operating on the project file, operate on the selected file."))
 
         self._extDetailPanel.SetSizer(extDetailSizer)
         staticBoxSizer.Add(self._extDetailPanel, 1, wx.ALL|wx.EXPAND, SPACE)
-        
+
         extOptionsPanelSizer.Add(staticBoxSizer, 1, wx.LEFT|wx.EXPAND, SPACE)
 
         extOptionsPanelBorderSizer.Add(extOptionsPanelSizer, 1, wx.ALL|wx.EXPAND, SPACE)
         extOptionsPanelSizer.Add(staticBoxSizer, 1, wx.LEFT|wx.EXPAND, SPACE)
 
         extOptionsPanelBorderSizer.Add(extOptionsPanelSizer, 1, wx.ALL|wx.EXPAND, SPACE)
@@ -306,8 +301,8 @@ class ExtensionOptionsPanel(wx.Panel):
         self.OnListBoxSelect()
 
         self.Layout()
         self.OnListBoxSelect()
 
         self.Layout()
-        
-        parent.AddPage(self, _("External Tools"))        
+
+        parent.AddPage(self, _("External Tools"))
 
 
     def OnOK(self, optionsDialog):
 
 
     def OnOK(self, optionsDialog):
@@ -323,7 +318,7 @@ class ExtensionOptionsPanel(wx.Panel):
                           msgTitle,
                           wx.OK | wx.ICON_INFORMATION,
                           self.GetParent())
                           msgTitle,
                           wx.OK | wx.ICON_INFORMATION,
                           self.GetParent())
-        
+
 
     def PopulateItems(self):
         extensionsService = wx.GetApp().GetService(ExtensionService)
 
     def PopulateItems(self):
         extensionsService = wx.GetApp().GetService(ExtensionService)
@@ -335,7 +330,7 @@ class ExtensionOptionsPanel(wx.Panel):
         self._currentItem = None
         self._currentItemIndex = -1
         return len(self._extensions)
         self._currentItem = None
         self._currentItemIndex = -1
         return len(self._extensions)
-      
+
 
     def OnListBoxSelect(self, event=None):
         self.SaveCurrentItem()
 
     def OnListBoxSelect(self, event=None):
         self.SaveCurrentItem()
@@ -370,7 +365,7 @@ class ExtensionOptionsPanel(wx.Panel):
             else:
                 extension.fileExt = fileExt.split(',')
             extension.opOnSelectedFile = self._selFileCtrl.GetValue()
             else:
                 extension.fileExt = fileExt.split(',')
             extension.opOnSelectedFile = self._selFileCtrl.GetValue()
-            
+
 
     def LoadItem(self, extension):
         if extension:
 
     def LoadItem(self, extension):
         if extension:
@@ -401,8 +396,8 @@ class ExtensionOptionsPanel(wx.Panel):
             self._fileExtTextCtrl.SetValue('')
             self._selFileCtrl.SetValue(True)
             self._extDetailPanel.Enable(False)
             self._fileExtTextCtrl.SetValue('')
             self._selFileCtrl.SetValue(True)
             self._extDetailPanel.Enable(False)
-            
-      
+
+
     def OnAdd(self, event):
         self.SaveCurrentItem()
         name = _("Untitled")
     def OnAdd(self, event):
         self.SaveCurrentItem()
         name = _("Untitled")
@@ -417,11 +412,11 @@ class ExtensionOptionsPanel(wx.Panel):
         self.OnListBoxSelect()
         self._menuItemNameTextCtrl.SetFocus()
         self._menuItemNameTextCtrl.SetSelection(-1, -1)
         self.OnListBoxSelect()
         self._menuItemNameTextCtrl.SetFocus()
         self._menuItemNameTextCtrl.SetSelection(-1, -1)
-        
+
 
     def OnDelete(self, event):
         self._extListBox.Delete(self._currentItemIndex)
 
     def OnDelete(self, event):
         self._extListBox.Delete(self._currentItemIndex)
-        self._extensions.remove(self._currentItem)    
+        self._extensions.remove(self._currentItem)
         self._currentItemIndex = min(self._currentItemIndex, self._extListBox.GetCount() - 1)
         if self._currentItemIndex > -1:
             self._extListBox.SetSelection(self._currentItemIndex)
         self._currentItemIndex = min(self._currentItemIndex, self._extListBox.GetCount() - 1)
         if self._currentItemIndex > -1:
             self._extListBox.SetSelection(self._currentItemIndex)
index 1fd557b00252dae40e8f10661fbb523c8cdeaa5f..da4ed91e19391203c2869e77fa0ad8c2a7e2a04a 100644 (file)
@@ -27,6 +27,7 @@ _ = wx.GetTranslation
 #----------------------------------------------------------------------------
 FILENAME_MARKER = _("Found in file: ")
 PROJECT_MARKER = _("Searching project: ")
 #----------------------------------------------------------------------------
 FILENAME_MARKER = _("Found in file: ")
 PROJECT_MARKER = _("Searching project: ")
+FILE_MARKER = _("Searching file: ")
 FIND_MATCHDIR = "FindMatchDir"
 FIND_MATCHDIRSUBFOLDERS = "FindMatchDirSubfolders"
 
 FIND_MATCHDIR = "FindMatchDir"
 FIND_MATCHDIRSUBFOLDERS = "FindMatchDirSubfolders"
 
@@ -39,6 +40,7 @@ class FindInDirService(FindService.FindService):
     #----------------------------------------------------------------------------
     # Constants
     #----------------------------------------------------------------------------
     #----------------------------------------------------------------------------
     # Constants
     #----------------------------------------------------------------------------
+    FINDFILE_ID = wx.NewId()        # for bringing up Find in File dialog box
     FINDALL_ID = wx.NewId()         # for bringing up Find All dialog box
     FINDDIR_ID = wx.NewId()         # for bringing up Find Dir dialog box
 
     FINDALL_ID = wx.NewId()         # for bringing up Find All dialog box
     FINDDIR_ID = wx.NewId()         # for bringing up Find Dir dialog box
 
@@ -47,29 +49,39 @@ class FindInDirService(FindService.FindService):
         FindService.FindService.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
 
         editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
         FindService.FindService.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
 
         editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
+        wx.EVT_MENU(frame, FindInDirService.FINDFILE_ID, self.ProcessEvent)
+        wx.EVT_UPDATE_UI(frame, FindInDirService.FINDFILE_ID, self.ProcessUpdateUIEvent)
+        editMenu.Append(FindInDirService.FINDFILE_ID, _("Find in File...\tCtrl+Shift+F"), _("Searches for the specified text in the current file"))
         wx.EVT_MENU(frame, FindInDirService.FINDALL_ID, self.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, FindInDirService.FINDALL_ID, self.ProcessUpdateUIEvent)
         wx.EVT_MENU(frame, FindInDirService.FINDALL_ID, self.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, FindInDirService.FINDALL_ID, self.ProcessUpdateUIEvent)
-        editMenu.Append(FindInDirService.FINDALL_ID, _("Find in Project...\tCtrl+Shift+F"), _("Searches for the specified text in all the files in the project"))
+        editMenu.Append(FindInDirService.FINDALL_ID, _("Find in Project...\tCtrl+Shift+P"), _("Searches for the specified text in all the files in the project"))
         wx.EVT_MENU(frame, FindInDirService.FINDDIR_ID, self.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, FindInDirService.FINDDIR_ID, self.ProcessUpdateUIEvent)
         wx.EVT_MENU(frame, FindInDirService.FINDDIR_ID, self.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, FindInDirService.FINDDIR_ID, self.ProcessUpdateUIEvent)
-        editMenu.Append(FindInDirService.FINDDIR_ID, _("Find in Directory..."), _("Searches for the specified text in all the files in the directory"))
+        editMenu.Append(FindInDirService.FINDDIR_ID, _("Find in Directory...\tCtrl+Shift+D"), _("Searches for the specified text in all the files in the directory"))
 
 
     def ProcessEvent(self, event):
         id = event.GetId()
 
 
     def ProcessEvent(self, event):
         id = event.GetId()
-        if id == FindInDirService.FINDALL_ID:
+        if id == FindInDirService.FINDFILE_ID:
             view = wx.GetApp().GetDocumentManager().GetCurrentView()
             if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
             view = wx.GetApp().GetDocumentManager().GetCurrentView()
             if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
-                self.ShowFindAllDialog(view.GetCtrl().GetSelectedText())
+                self.ShowFindInFileDialog(view.GetCtrl().GetSelectedText())
             else:
             else:
-                self.ShowFindAllDialog()
+                self.ShowFindInFileDialog()
+            return True
+        elif id == FindInDirService.FINDALL_ID:
+            view = wx.GetApp().GetDocumentManager().GetCurrentView()
+            if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
+                self.ShowFindInProjectDialog(view.GetCtrl().GetSelectedText())
+            else:
+                self.ShowFindInProjectDialog()
             return True
         elif id == FindInDirService.FINDDIR_ID:
             view = wx.GetApp().GetDocumentManager().GetCurrentView()
             if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
             return True
         elif id == FindInDirService.FINDDIR_ID:
             view = wx.GetApp().GetDocumentManager().GetCurrentView()
             if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
-                self.ShowFindDirDialog(view.GetCtrl().GetSelectedText())
+                self.ShowFindInDirDialog(view.GetCtrl().GetSelectedText())
             else:
             else:
-                self.ShowFindDirDialog()
+                self.ShowFindInDirDialog()
             return True
         else:
             return FindService.FindService.ProcessEvent(self, event)
             return True
         else:
             return FindService.FindService.ProcessEvent(self, event)
@@ -77,7 +89,14 @@ class FindInDirService(FindService.FindService):
 
     def ProcessUpdateUIEvent(self, event):
         id = event.GetId()
 
     def ProcessUpdateUIEvent(self, event):
         id = event.GetId()
-        if id == FindInDirService.FINDALL_ID:
+        if id == FindInDirService.FINDFILE_ID:
+            view = wx.GetApp().GetDocumentManager().GetCurrentView()
+            if view and view.GetDocument() and not isinstance(view.GetDocument(), ProjectEditor.ProjectDocument):  # don't search project model
+                event.Enable(True)
+            else:
+                event.Enable(False)
+            return True
+        elif id == FindInDirService.FINDALL_ID:
             projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
             if projectService.GetFilesFromCurrentProject():
                 event.Enable(True)
             projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
             if projectService.GetFilesFromCurrentProject():
                 event.Enable(True)
@@ -90,7 +109,7 @@ class FindInDirService(FindService.FindService):
             return FindService.FindService.ProcessUpdateUIEvent(self, event)
 
 
             return FindService.FindService.ProcessUpdateUIEvent(self, event)
 
 
-    def ShowFindDirDialog(self, findString=None):
+    def ShowFindInDirDialog(self, findString=None):
         config = wx.ConfigBase_Get()
 
         frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Find in Directory"), size= (320,200))
         config = wx.ConfigBase_Get()
 
         frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Find in Directory"), size= (320,200))
@@ -105,7 +124,7 @@ class FindInDirService(FindService.FindService):
         findDirButton = wx.Button(frame, -1, _("Browse..."))
         lineSizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
         contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
         findDirButton = wx.Button(frame, -1, _("Browse..."))
         lineSizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
         contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
-        
+
         def OnBrowseButton(event):
             dlg = wx.DirDialog(frame, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
             dir = dirCtrl.GetValue()
         def OnBrowseButton(event):
             dlg = wx.DirDialog(frame, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
             dir = dirCtrl.GetValue()
@@ -126,10 +145,10 @@ class FindInDirService(FindService.FindService):
         lineSizer = wx.BoxSizer(wx.VERTICAL)    # let the line expand horizontally without vertical expansion
         lineSizer.Add(wx.StaticLine(frame, -1, size = (10,-1)), 0, flag=wx.EXPAND)
         contentSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.BOTTOM, border=HALF_SPACE)
         lineSizer = wx.BoxSizer(wx.VERTICAL)    # let the line expand horizontally without vertical expansion
         lineSizer.Add(wx.StaticLine(frame, -1, size = (10,-1)), 0, flag=wx.EXPAND)
         contentSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.BOTTOM, border=HALF_SPACE)
-        
+
         if wx.Platform == "__WXMAC__":
             contentSizer.Add((-1, 10), 0, wx.EXPAND)
         if wx.Platform == "__WXMAC__":
             contentSizer.Add((-1, 10), 0, wx.EXPAND)
-        
+
         lineSizer = wx.BoxSizer(wx.HORIZONTAL)
         lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
         if not findString:
         lineSizer = wx.BoxSizer(wx.HORIZONTAL)
         lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
         if not findString:
@@ -192,13 +211,13 @@ class FindInDirService(FindService.FindService):
                 status = frame.ShowModal()
             else:
                 passedCheck = True
                 status = frame.ShowModal()
             else:
                 passedCheck = True
-    
+
 
         # save user choice state for this and other Find Dialog Boxes
         dirString = dirCtrl.GetValue()
         searchSubfolders = subfolderCtrl.IsChecked()
 
         # save user choice state for this and other Find Dialog Boxes
         dirString = dirCtrl.GetValue()
         searchSubfolders = subfolderCtrl.IsChecked()
-        self.SaveFindDirConfig(dirString, searchSubfolders)
-        
+        self.SaveFindInDirConfig(dirString, searchSubfolders)
+
         findString = findCtrl.GetValue()
         matchCase = matchCaseCtrl.IsChecked()
         wholeWord = wholeWordCtrl.IsChecked()
         findString = findCtrl.GetValue()
         matchCase = matchCaseCtrl.IsChecked()
         wholeWord = wholeWordCtrl.IsChecked()
@@ -206,52 +225,23 @@ class FindInDirService(FindService.FindService):
         self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
 
         frame.Destroy()
         self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
 
         frame.Destroy()
-        if status == wx.ID_OK:            
+        if status == wx.ID_OK:
             messageService = wx.GetApp().GetService(MessageService.MessageService)
             messageService.ShowWindow()
 
             view = messageService.GetView()
             if view:
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
             messageService = wx.GetApp().GetService(MessageService.MessageService)
             messageService.ShowWindow()
 
             view = messageService.GetView()
             if view:
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-                view.ClearLines()
-                view.SetCallback(self.OnJumpToFoundLine)
-
-                view.AddLines(_("Searching for '%s' in '%s'\n\n") % (findString, dirString))
                 
                 
-                if os.path.isfile(dirString):
-                    try:
-                        docFile = file(dirString, 'r')
-                        lineNum = 1
-                        needToDisplayFilename = True
-                        line = docFile.readline()
-                        while line:
-                            count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
-                            if count != -1:
-                                if needToDisplayFilename:
-                                    view.AddLines(FILENAME_MARKER + dirString + "\n")
-                                    needToDisplayFilename = False
-                                line = repr(lineNum).zfill(4) + ":" + line
-                                view.AddLines(line)
-                            line = docFile.readline()
-                            lineNum += 1
-                        if not needToDisplayFilename:
-                            view.AddLines("\n")
-                    except IOError, (code, message):
-                        print _("Warning, unable to read file: '%s'.  %s") % (dirString, message)
-                else:
-                    # do search in files on disk
-                    for root, dirs, files in os.walk(dirString):
-                        if not searchSubfolders and root != dirString:
-                            break
-                            
-                        for name in files:
-                            filename = os.path.join(root, name)
-                            try:
-                                docFile = file(filename, 'r')
-                            except IOError, (code, message):
-                                print _("Warning, unable to read file: '%s'.  %s") % (filename, message)
-                                continue
-        
+                try:
+                    view.ClearLines()
+                    view.SetCallback(self.OnJumpToFoundLine)
+    
+                    view.AddLines(_("Searching for '%s' in '%s'\n\n") % (findString, dirString))
+    
+                    if os.path.isfile(dirString):
+                        try:
+                            docFile = file(dirString, 'r')
                             lineNum = 1
                             needToDisplayFilename = True
                             line = docFile.readline()
                             lineNum = 1
                             needToDisplayFilename = True
                             line = docFile.readline()
@@ -259,7 +249,7 @@ class FindInDirService(FindService.FindService):
                                 count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
                                 if count != -1:
                                     if needToDisplayFilename:
                                 count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
                                 if count != -1:
                                     if needToDisplayFilename:
-                                        view.AddLines(FILENAME_MARKER + filename + "\n")
+                                        view.AddLines(FILENAME_MARKER + dirString + "\n")
                                         needToDisplayFilename = False
                                     line = repr(lineNum).zfill(4) + ":" + line
                                     view.AddLines(line)
                                         needToDisplayFilename = False
                                     line = repr(lineNum).zfill(4) + ":" + line
                                     view.AddLines(line)
@@ -267,27 +257,186 @@ class FindInDirService(FindService.FindService):
                                 lineNum += 1
                             if not needToDisplayFilename:
                                 view.AddLines("\n")
                                 lineNum += 1
                             if not needToDisplayFilename:
                                 view.AddLines("\n")
-
-                view.AddLines(_("Search completed."))
-                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                        except IOError, (code, message):
+                            print _("Warning, unable to read file: '%s'.  %s") % (dirString, message)
+                    else:
+                        # do search in files on disk
+                        for root, dirs, files in os.walk(dirString):
+                            if not searchSubfolders and root != dirString:
+                                break
+    
+                            for name in files:
+                                filename = os.path.join(root, name)
+                                try:
+                                    docFile = file(filename, 'r')
+                                except IOError, (code, message):
+                                    print _("Warning, unable to read file: '%s'.  %s") % (filename, message)
+                                    continue
+    
+                                lineNum = 1
+                                needToDisplayFilename = True
+                                line = docFile.readline()
+                                while line:
+                                    count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
+                                    if count != -1:
+                                        if needToDisplayFilename:
+                                            view.AddLines(FILENAME_MARKER + filename + "\n")
+                                            needToDisplayFilename = False
+                                        line = repr(lineNum).zfill(4) + ":" + line
+                                        view.AddLines(line)
+                                    line = docFile.readline()
+                                    lineNum += 1
+                                if not needToDisplayFilename:
+                                    view.AddLines("\n")
+    
+                    view.AddLines(_("Search completed."))
+                
+                finally:
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
             return True
         else:
             return False
 
             return True
         else:
             return False
-            
 
 
-    def SaveFindDirConfig(self, dirString, searchSubfolders):
+
+    def SaveFindInDirConfig(self, dirString, searchSubfolders):
         """ Save search dir patterns and flags to registry.
         """ Save search dir patterns and flags to registry.
-        
+
             dirString = search directory
             searchSubfolders = Search subfolders
         """
         config = wx.ConfigBase_Get()
         config.Write(FIND_MATCHDIR, dirString)
         config.WriteInt(FIND_MATCHDIRSUBFOLDERS, searchSubfolders)
             dirString = search directory
             searchSubfolders = Search subfolders
         """
         config = wx.ConfigBase_Get()
         config.Write(FIND_MATCHDIR, dirString)
         config.WriteInt(FIND_MATCHDIRSUBFOLDERS, searchSubfolders)
+
+
+    def DoFindIn(self, findString, matchCase, wholeWord, regExpr, currFileOnly=False, jumpToFound=False):
+        messageService = wx.GetApp().GetService(MessageService.MessageService)
+        if not messageService:
+            return
+
+        messageService.ShowWindow()
+
+        view = messageService.GetView()
+        if not view:
+            return
+
+        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+        
+        try:
+            #Switch to messages tab.
+            view.GetControl().GetParent().SetSelection(0) 
+            view.ClearLines()
+            view.SetCallback(self.OnJumpToFoundLine)
+    
+            projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
+    
+            if wx.GetApp().GetDocumentManager().GetCurrentView():
+                currDoc = wx.GetApp().GetDocumentManager().GetCurrentView().GetDocument()
+            else:
+                currDoc = None
+            if currFileOnly:
+                if currDoc:
+                    projectFilenames = [currDoc.GetFilename()]
+                    view.AddLines(FILE_MARKER + currDoc.GetFilename() + "\n\n")
+                else:
+                    projectFilenames = []
+            else:
+                projectFilenames = projectService.GetFilesFromCurrentProject()
+    
+                projView = projectService.GetView()
+                if projView:
+                    projName = wx.lib.docview.FileNameFromPath(projView.GetDocument().GetFilename())
+                    view.AddLines(PROJECT_MARKER + projName + "\n\n")
+    
+            firstDef = -1
+    
+            # do search in open files first, open files may have been modified and different from disk because it hasn't been saved
+            openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+            openDocsInProject = filter(lambda openDoc: openDoc.GetFilename() in projectFilenames, openDocs)
+            if currDoc and currDoc in openDocsInProject:
+                # make sure current document is searched first.
+                openDocsInProject.remove(currDoc)
+                openDocsInProject.insert(0, currDoc)
+            for openDoc in openDocsInProject:
+                if isinstance(openDoc, ProjectEditor.ProjectDocument):  # don't search project model
+                    continue
+    
+                openDocView = openDoc.GetFirstView()
+                # some views don't have a in memory text object to search through such as the PM and the DM
+                # even if they do have a non-text searchable object, how do we display it in the message window?
+                if not hasattr(openDocView, "GetValue"):
+                    continue
+                text = openDocView.GetValue()
+    
+                lineNum = 1
+                needToDisplayFilename = True
+                start = 0
+                end = 0
+                count = 0
+                while count != -1:
+                    count, foundStart, foundEnd, newText = self.DoFind(findString, None, text, start, end, True, matchCase, wholeWord, regExpr)
+                    if count != -1:
+                        if needToDisplayFilename:
+                            view.AddLines(FILENAME_MARKER + openDoc.GetFilename() + "\n")
+                            needToDisplayFilename = False
+    
+                        lineNum = openDocView.LineFromPosition(foundStart)
+                        line = repr(lineNum).zfill(4) + ":" + openDocView.GetLine(lineNum)
+                        view.AddLines(line)
+                        if firstDef == -1:
+                            firstDef = view.GetControl().GetCurrentLine() - 1
+    
+                        start = text.find("\n", foundStart)
+                        if start == -1:
+                            break
+                        end = start
+                if not needToDisplayFilename:
+                    view.AddLines("\n")
+                wx.GetApp().Yield(True)
+            openDocNames = map(lambda openDoc: openDoc.GetFilename(), openDocs)
+    
+            # do search in closed files, skipping the open ones we already searched
+            filenames = filter(lambda filename: filename not in openDocNames, projectFilenames)
+            for filename in filenames:
+                try:
+                    docFile = file(filename, 'r')
+                except IOError, (code, message):
+                    print _("Warning, unable to read file: '%s'.  %s") % (filename, message)
+                    continue
+    
+                lineNum = 1
+                needToDisplayFilename = True
+                line = docFile.readline()
+                while line:
+                    count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
+                    if count != -1:
+                        if needToDisplayFilename:
+                            view.AddLines(FILENAME_MARKER + filename + "\n")
+                            needToDisplayFilename = False
+                        line = repr(lineNum).zfill(4) + ":" + line
+                        view.AddLines(line)
+                        if firstDef == -1:
+                            firstDef = view.GetControl().GetCurrentLine() - 1
+                    line = docFile.readline()
+                    lineNum += 1
+                if not needToDisplayFilename:
+                    view.AddLines("\n")
+                wx.GetApp().Yield(True)
+    
+            view.AddLines(_("Search for '%s' completed.") % findString)
     
     
+            if jumpToFound:
+                self.OnJumpToFoundLine(event=None, defLineNum=firstDef)
 
 
-    def ShowFindAllDialog(self, findString=None):
+        finally:
+            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+
+    def FindInProject(self, findString):
+        self.DoFindIn(findString, matchCase=True, wholeWord=True, regExpr=True, jumpToFound=True)
+
+
+    def ShowFindInProjectDialog(self, findString=None):
         config = wx.ConfigBase_Get()
 
         frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Find in Project"), size= (320,200))
         config = wx.ConfigBase_Get()
 
         frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Find in Project"), size= (320,200))
@@ -336,97 +485,80 @@ class FindInDirService(FindService.FindService):
         self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
 
         frame.Destroy()
         self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
 
         frame.Destroy()
+
         if status == wx.ID_OK:
         if status == wx.ID_OK:
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
+            self.DoFindIn(findString, matchCase, wholeWord, regExpr)
+            return True
+        else:
+            return False
 
 
-            view = messageService.GetView()
-            if view:
-                view.ClearLines()
-                view.SetCallback(self.OnJumpToFoundLine)
 
 
-                projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
-                projectFilenames = projectService.GetFilesFromCurrentProject()
+    def ShowFindInFileDialog(self, findString=None):
+        config = wx.ConfigBase_Get()
 
 
-                projView = projectService.GetView()
-                if projView:
-                    projName = wx.lib.docview.FileNameFromPath(projView.GetDocument().GetFilename())
-                    view.AddLines(PROJECT_MARKER + projName + "\n\n")
+        frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Find in File"), size= (320,200))
+        borderSizer = wx.BoxSizer(wx.HORIZONTAL)
 
 
-                # do search in open files first, open files may have been modified and different from disk because it hasn't been saved
-                openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                openDocsInProject = filter(lambda openDoc: openDoc.GetFilename() in projectFilenames, openDocs)
-                for openDoc in openDocsInProject:
-                    if isinstance(openDoc, ProjectEditor.ProjectDocument):  # don't search project model
-                        continue
-
-                    openDocView = openDoc.GetFirstView()
-                    # some views don't have a in memory text object to search through such as the PM and the DM
-                    # even if they do have a non-text searchable object, how do we display it in the message window?
-                    if not hasattr(openDocView, "GetValue"):
-                        continue
-                    text = openDocView.GetValue()
-
-                    lineNum = 1
-                    needToDisplayFilename = True
-                    start = 0
-                    end = 0
-                    count = 0
-                    while count != -1:
-                        count, foundStart, foundEnd, newText = self.DoFind(findString, None, text, start, end, True, matchCase, wholeWord, regExpr)
-                        if count != -1:
-                            if needToDisplayFilename:
-                                view.AddLines(FILENAME_MARKER + openDoc.GetFilename() + "\n")
-                                needToDisplayFilename = False
-
-                            lineNum = openDocView.LineFromPosition(foundStart)
-                            line = repr(lineNum).zfill(4) + ":" + openDocView.GetLine(lineNum)
-                            view.AddLines(line)
-
-                            start = text.find("\n", foundStart)
-                            if start == -1:
-                                break
-                            end = start
-                    if not needToDisplayFilename:
-                        view.AddLines("\n")
-                openDocNames = map(lambda openDoc: openDoc.GetFilename(), openDocs)
-
-                # do search in closed files, skipping the open ones we already searched
-                filenames = filter(lambda filename: filename not in openDocNames, projectFilenames)
-                for filename in filenames:
-                    try:
-                        docFile = file(filename, 'r')
-                    except IOError, (code, message):
-                        print _("Warning, unable to read file: '%s'.  %s") % (filename, message)
-                        continue
-
-                    lineNum = 1
-                    needToDisplayFilename = True
-                    line = docFile.readline()
-                    while line:
-                        count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
-                        if count != -1:
-                            if needToDisplayFilename:
-                                view.AddLines(FILENAME_MARKER + filename + "\n")
-                                needToDisplayFilename = False
-                            line = repr(lineNum).zfill(4) + ":" + line
-                            view.AddLines(line)
-                        line = docFile.readline()
-                        lineNum += 1
-                    if not needToDisplayFilename:
-                        view.AddLines("\n")
-
-                view.AddLines(_("Search for '%s' completed.") % findString)
+        contentSizer = wx.BoxSizer(wx.VERTICAL)
+        lineSizer = wx.BoxSizer(wx.HORIZONTAL)
+        lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
+        if not findString:
+            findString = config.Read(FindService.FIND_MATCHPATTERN, "")
+        findCtrl = wx.TextCtrl(frame, -1, findString, size=(200,-1))
+        lineSizer.Add(findCtrl, 0, wx.LEFT, HALF_SPACE)
+        contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
+        wholeWordCtrl = wx.CheckBox(frame, -1, _("Match whole word only"))
+        wholeWordCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHWHOLEWORD, False))
+        matchCaseCtrl = wx.CheckBox(frame, -1, _("Match case"))
+        matchCaseCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHCASE, False))
+        regExprCtrl = wx.CheckBox(frame, -1, _("Regular expression"))
+        regExprCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHREGEXPR, False))
+        contentSizer.Add(wholeWordCtrl, 0, wx.BOTTOM, SPACE)
+        contentSizer.Add(matchCaseCtrl, 0, wx.BOTTOM, SPACE)
+        contentSizer.Add(regExprCtrl, 0, wx.BOTTOM, SPACE)
+        borderSizer.Add(contentSizer, 0, wx.TOP | wx.BOTTOM | wx.LEFT, SPACE)
+
+        buttonSizer = wx.BoxSizer(wx.VERTICAL)
+        findBtn = wx.Button(frame, wx.ID_OK, _("Find"))
+        findBtn.SetDefault()
+        BTM_SPACE = HALF_SPACE
+        if wx.Platform == "__WXMAC__":
+            BTM_SPACE = SPACE
+        buttonSizer.Add(findBtn, 0, wx.BOTTOM, BTM_SPACE)
+        buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL), 0)
+        borderSizer.Add(buttonSizer, 0, wx.ALL, SPACE)
+
+        frame.SetSizer(borderSizer)
+        frame.Fit()
+
+        frame.CenterOnParent()
+        status = frame.ShowModal()
+
+        # save user choice state for this and other Find Dialog Boxes
+        findString = findCtrl.GetValue()
+        matchCase = matchCaseCtrl.IsChecked()
+        wholeWord = wholeWordCtrl.IsChecked()
+        regExpr = regExprCtrl.IsChecked()
+        self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
 
 
+        frame.Destroy()
+
+        if status == wx.ID_OK:
+            self.DoFindIn(findString, matchCase, wholeWord, regExpr, currFileOnly=True)
             return True
         else:
             return False
 
 
             return True
         else:
             return False
 
 
-    def OnJumpToFoundLine(self, event):
+    def OnJumpToFoundLine(self, event=None, defLineNum=-1):
         messageService = wx.GetApp().GetService(MessageService.MessageService)
         messageService = wx.GetApp().GetService(MessageService.MessageService)
-        lineText, pos = messageService.GetView().GetCurrLine()
-        if lineText == "\n" or lineText.find(FILENAME_MARKER) != -1 or lineText.find(PROJECT_MARKER) != -1:
+        if defLineNum == -1:
+            lineText, pos = messageService.GetView().GetCurrLine()
+        else:
+            lineText = messageService.GetView().GetControl().GetLine(defLineNum)
+            pos = 0
+
+        if lineText == "\n" or lineText.find(FILENAME_MARKER) != -1 or lineText.find(PROJECT_MARKER) != -1 or lineText.find(FILE_MARKER) != -1:
             return
         lineEnd = lineText.find(":")
         if lineEnd == -1:
             return
         lineEnd = lineText.find(":")
         if lineEnd == -1:
@@ -435,7 +567,10 @@ class FindInDirService(FindService.FindService):
             lineNum = int(lineText[0:lineEnd])
 
         text = messageService.GetView().GetText()
             lineNum = int(lineText[0:lineEnd])
 
         text = messageService.GetView().GetText()
-        curPos = messageService.GetView().GetCurrentPos()
+        if defLineNum == -1:
+            curPos = messageService.GetView().GetCurrentPos()
+        else:
+            curPos = messageService.GetView().GetControl().GetLineEndPosition(defLineNum)
 
         startPos = text.rfind(FILENAME_MARKER, 0, curPos)
         endPos = text.find("\n", startPos)
 
         startPos = text.rfind(FILENAME_MARKER, 0, curPos)
         endPos = text.find("\n", startPos)
@@ -464,5 +599,3 @@ class FindInDirService(FindService.FindService):
                 #         time, we don't see the selection, it is scrolled off screen
                 foundView.SetSelection(startPos - 1 + len(lineText[lineEnd:].rstrip("\n")), startPos)
                 wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
                 #         time, we don't see the selection, it is scrolled off screen
                 foundView.SetSelection(startPos - 1 + len(lineText[lineEnd:].rstrip("\n")), startPos)
                 wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
-
-
index bcdbe7ed7bafc4fce7e55bda182f380c4454790c..024a0b13f2f2e88a81cd90be21cabdf6e8e4e8e2 100644 (file)
@@ -119,7 +119,7 @@ class HtmlCtrl(CodeEditor.CodeCtrl):
 
 
     def SetViewDefaults(self):
 
 
     def SetViewDefaults(self):
-        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Html", hasWordWrap = True, hasTabs = True)
+        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Html", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetFontAndColorFromConfig(self):
 
 
     def GetFontAndColorFromConfig(self):
@@ -156,7 +156,7 @@ class HtmlCtrl(CodeEditor.CodeCtrl):
 class HtmlOptionsPanel(STCTextEditor.TextOptionsPanel):
 
     def __init__(self, parent, id):
 class HtmlOptionsPanel(STCTextEditor.TextOptionsPanel):
 
     def __init__(self, parent, id):
-        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Html", label = "HTML", hasWordWrap = True, hasTabs = True)
+        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Html", label = "HTML", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetIcon(self):
 
 
     def GetIcon(self):
index a9c6dcc77bcc2cfa0822e52cdbca14d8bbeda292..b1567b4fd2312072cc1023ad36343f9bce5548a8 100644 (file)
@@ -18,9 +18,17 @@ import wx.grid
 import os.path
 import activegrid.util.sysutils as sysutilslib
 import activegrid.util.appdirs as appdirs
 import os.path
 import activegrid.util.sysutils as sysutilslib
 import activegrid.util.appdirs as appdirs
+import shutil
+
+# Required for Unicode support with python
+# site.py sets this, but Windows builds don't have site.py because of py2exe problems
+# If site.py has already run, then the setdefaultencoding function will have been deleted.
+if hasattr(sys,"setdefaultencoding"):
+    sys.setdefaultencoding("utf-8")
 
 _ = wx.GetTranslation
 
 _ = wx.GetTranslation
-ACTIVEGRID_BASE_IDE = False
+ACTIVEGRID_BASE_IDE = False 
+USE_OLD_PROJECTS = False
 #----------------------------------------------------------------------------
 # Helper functions for command line args
 #----------------------------------------------------------------------------
 #----------------------------------------------------------------------------
 # Helper functions for command line args
 #----------------------------------------------------------------------------
@@ -44,6 +52,19 @@ def isInArgs(argname, argv):
         
     return result
 
         
     return result
 
+# The default log action in wx is to prompt with a big message box
+# which is often inappropriate (for example, if the clipboard data
+# is not readable on Mac, we'll get one of these messages repeatedly)
+# so just log the errors instead.
+# NOTE: This does NOT supress fatal system errors. Only non-fatal ones.
+class AppLog(wx.PyLog):
+    def __init__(self):
+        wx.PyLog.__init__(self)
+        self.items = []
+        
+    def DoLogString(self, message, timeStamp):
+        self.items.append(str(timeStamp) + u" " + message.decode())
+
 #----------------------------------------------------------------------------
 # Classes
 #----------------------------------------------------------------------------
 #----------------------------------------------------------------------------
 # Classes
 #----------------------------------------------------------------------------
@@ -55,9 +76,13 @@ class IDEApplication(wx.lib.pydocview.DocApp):
 
     def OnInit(self):
         global ACTIVEGRID_BASE_IDE
 
     def OnInit(self):
         global ACTIVEGRID_BASE_IDE
-        
+        global USE_OLD_PROJECTS
         args = sys.argv
         args = sys.argv
-                    
+
+        # Suppress non-fatal errors that might prompt the user even in cases
+        # when the error does not impact them.
+        wx.Log_SetActiveTarget(AppLog())
+        
         if "-h" in args or "-help" in args or "--help" in args\
             or (wx.Platform == "__WXMSW__" and "/help" in args):
             print "Usage: ActiveGridAppBuilder.py [options] [filenames]\n"
         if "-h" in args or "-help" in args or "--help" in args\
             or (wx.Platform == "__WXMSW__" and "/help" in args):
             print "Usage: ActiveGridAppBuilder.py [options] [filenames]\n"
@@ -81,6 +106,8 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         elif isInArgs("baseide", args):
             self.SetAppName(_("ActiveGrid IDE"))
             ACTIVEGRID_BASE_IDE = True
         elif isInArgs("baseide", args):
             self.SetAppName(_("ActiveGrid IDE"))
             ACTIVEGRID_BASE_IDE = True
+        elif isInArgs("tools", args):
+            USE_OLD_PROJECTS = True
         else:
             self.SetAppName(_("ActiveGrid Application Builder"))
             self.SetDebug(False)
         else:
             self.SetAppName(_("ActiveGrid Application Builder"))
             self.SetDebug(False)
@@ -120,20 +147,22 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         import AboutDialog
         import SVNService
         import ExtensionService
         import AboutDialog
         import SVNService
         import ExtensionService
+##        import UpdateLogIniService
                             
         if not ACTIVEGRID_BASE_IDE:
             import activegrid.model.basedocmgr as basedocmgr
                             
         if not ACTIVEGRID_BASE_IDE:
             import activegrid.model.basedocmgr as basedocmgr
-            import UpdateService as updater
+            import UpdateService
             import DataModelEditor
             import ProcessModelEditor
             import DeploymentService
             import WebServerService
             import WelcomeService
             import DataModelEditor
             import ProcessModelEditor
             import DeploymentService
             import WebServerService
             import WelcomeService
-            import ViewEditor
+            import XFormEditor
             import PropertyService
             import WSDLEditor
             import WsdlAgEditor
             import XPathEditor
             import PropertyService
             import WSDLEditor
             import WsdlAgEditor
             import XPathEditor
+            import XPathExprEditor
             import ImportServiceWizard
             import RoleEditor
             import HelpService
             import ImportServiceWizard
             import RoleEditor
             import HelpService
@@ -158,9 +187,11 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         if not config.Exists("MDIEmbedRightVisible"):  # Make the properties embedded window hidden as default
             config.WriteInt("MDIEmbedRightVisible", False)
 
         if not config.Exists("MDIEmbedRightVisible"):  # Make the properties embedded window hidden as default
             config.WriteInt("MDIEmbedRightVisible", False)
 
-        docManager = wx.lib.docview.DocManager(flags = self.GetDefaultDocManagerFlags())
+        docManager = IDEDocManager(flags = self.GetDefaultDocManagerFlags())
         self.SetDocumentManager(docManager)
 
         self.SetDocumentManager(docManager)
 
+        # Note:  These templates must be initialized in display order for the "Files of type" dropdown for the "File | Open..." dialog
+        
         defaultTemplate = wx.lib.docview.DocTemplate(docManager,
                 _("Any"),
                 "*.*",
         defaultTemplate = wx.lib.docview.DocTemplate(docManager,
                 _("Any"),
                 "*.*",
@@ -184,6 +215,7 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 _("Deployment View"),
                 XmlEditor.XmlDocument,
                 XmlEditor.XmlView,
                 _("Deployment View"),
                 XmlEditor.XmlDocument,
                 XmlEditor.XmlView,
+                wx.lib.docview.TEMPLATE_INVISIBLE,
                 icon = DeploymentService.getDPLIcon())
             docManager.AssociateTemplate(dplTemplate)
 
                 icon = DeploymentService.getDPLIcon())
             docManager.AssociateTemplate(dplTemplate)
 
@@ -199,6 +231,20 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 icon = HtmlEditor.getHTMLIcon())
         docManager.AssociateTemplate(htmlTemplate)
 
                 icon = HtmlEditor.getHTMLIcon())
         docManager.AssociateTemplate(htmlTemplate)
 
+        if not ACTIVEGRID_BASE_IDE:
+            identityTemplate = wx.lib.docview.DocTemplate(docManager,
+                    _("Identity"),
+                    "*.xacml",
+                    _("Identity"),
+                    _(".xacml"),
+                    _("Identity Configuration"),
+                    _("Identity View"),
+                    RoleEditor.RoleEditorDocument,
+                    RoleEditor.RoleEditorView,
+                    wx.lib.docview.TEMPLATE_NO_CREATE,
+                    icon = XmlEditor.getXMLIcon())
+            docManager.AssociateTemplate(identityTemplate)
+
         imageTemplate = wx.lib.docview.DocTemplate(docManager,
                 _("Image"),
                 "*.bmp;*.ico;*.gif;*.jpg;*.jpeg;*.png",
         imageTemplate = wx.lib.docview.DocTemplate(docManager,
                 _("Image"),
                 "*.bmp;*.ico;*.gif;*.jpg;*.jpeg;*.png",
@@ -224,7 +270,6 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                     # XmlEditor.XmlView,
                     LayoutEditor.LayoutEditorDocument,
                     LayoutEditor.LayoutEditorView,
                     # XmlEditor.XmlView,
                     LayoutEditor.LayoutEditorDocument,
                     LayoutEditor.LayoutEditorView,
-                    wx.lib.docview.TEMPLATE_NO_CREATE,
                     icon = LayoutEditor.getLytIcon())
             docManager.AssociateTemplate(layoutTemplate)
 
                     icon = LayoutEditor.getLytIcon())
             docManager.AssociateTemplate(layoutTemplate)
 
@@ -262,6 +307,7 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 _("Process View"),
                 ProcessModelEditor.ProcessModelDocument,
                 ProcessModelEditor.ProcessModelView,
                 _("Process View"),
                 ProcessModelEditor.ProcessModelDocument,
                 ProcessModelEditor.ProcessModelView,
+                wx.lib.docview.TEMPLATE_NO_CREATE,
                 icon = ProcessModelEditor.getProcessModelIcon())
             docManager.AssociateTemplate(processModelTemplate)
 
                 icon = ProcessModelEditor.getProcessModelIcon())
             docManager.AssociateTemplate(processModelTemplate)
 
@@ -274,6 +320,7 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 _("Project View"),
                 ProjectEditor.ProjectDocument,
                 ProjectEditor.ProjectView,
                 _("Project View"),
                 ProjectEditor.ProjectDocument,
                 ProjectEditor.ProjectView,
+                wx.lib.docview.TEMPLATE_NO_CREATE,
                 icon = ProjectEditor.getProjectIcon())
         docManager.AssociateTemplate(projectTemplate)
 
                 icon = ProjectEditor.getProjectIcon())
         docManager.AssociateTemplate(projectTemplate)
 
@@ -302,6 +349,20 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 icon = DataModelEditor.getDataModelIcon())
             docManager.AssociateTemplate(dataModelTemplate)
             
                 icon = DataModelEditor.getDataModelIcon())
             docManager.AssociateTemplate(dataModelTemplate)
             
+        if not ACTIVEGRID_BASE_IDE:
+            wsdlagTemplate = wx.lib.docview.DocTemplate(docManager,
+                    _("Service Reference"),
+                    "*.wsdlag",
+                    _("Project"),
+                    _(".wsdlag"),
+                    _("Service Reference Document"),
+                    _("Service Reference View"),
+                    WsdlAgEditor.WsdlAgDocument,
+                    WsdlAgEditor.WsdlAgView,
+                    wx.lib.docview.TEMPLATE_NO_CREATE,
+                    icon = WSDLEditor.getWSDLIcon())
+            docManager.AssociateTemplate(wsdlagTemplate)
+
         if not ACTIVEGRID_BASE_IDE and _EDIT_LAYOUTS:
             layoutTemplate = wx.lib.docview.DocTemplate(docManager,
                     _("Skin"),
         if not ACTIVEGRID_BASE_IDE and _EDIT_LAYOUTS:
             layoutTemplate = wx.lib.docview.DocTemplate(docManager,
                     _("Skin"),
@@ -317,18 +378,18 @@ class IDEApplication(wx.lib.pydocview.DocApp):
             docManager.AssociateTemplate(layoutTemplate)
 
         if not ACTIVEGRID_BASE_IDE:
             docManager.AssociateTemplate(layoutTemplate)
 
         if not ACTIVEGRID_BASE_IDE:
-            identityTemplate = wx.lib.docview.DocTemplate(docManager,
-                    _("Identity"),
-                    "*.xacml",
-                    _("Identity"),
-                    _(".xacml"),
-                    _("Identity Configuration"),
-                    _("Identity View"),
-                    RoleEditor.RoleEditorDocument,
-                    RoleEditor.RoleEditorView,
+            sqlTemplate = wx.lib.docview.DocTemplate(docManager,
+                    _("SQL"),
+                    "*.sql",
+                    _("SQL"),
+                    _(".sql"),
+                    _("SQL Document"),
+                    _("SQL View"),
+                    SQLEditor.SQLDocument,
+                    SQLEditor.SQLView,
                     wx.lib.docview.TEMPLATE_NO_CREATE,
                     wx.lib.docview.TEMPLATE_NO_CREATE,
-                    icon = XmlEditor.getXMLIcon())
-            docManager.AssociateTemplate(identityTemplate)
+                    icon = SQLEditor.getSQLIcon())
+            docManager.AssociateTemplate(sqlTemplate)
 
         textTemplate = wx.lib.docview.DocTemplate(docManager,
                 _("Text"),
 
         textTemplate = wx.lib.docview.DocTemplate(docManager,
                 _("Text"),
@@ -343,33 +404,6 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         docManager.AssociateTemplate(textTemplate)
 
         if not ACTIVEGRID_BASE_IDE:
         docManager.AssociateTemplate(textTemplate)
 
         if not ACTIVEGRID_BASE_IDE:
-            sqlTemplate = wx.lib.docview.DocTemplate(docManager,
-                    _("SQL"),
-                    "*.sql",
-                    _("SQL"),
-                    _(".sql"),
-                    _("SQL Document"),
-                    _("SQL View"),
-                    SQLEditor.SQLDocument,
-                    SQLEditor.SQLView,
-                    wx.lib.docview.TEMPLATE_NO_CREATE,
-                    icon = SQLEditor.getSQLIcon())
-            docManager.AssociateTemplate(sqlTemplate)
-
-
-            wsdlagTemplate = wx.lib.docview.DocTemplate(docManager,
-                    _("Service Reference"),
-                    "*.wsdlag",
-                    _("Project"),
-                    _(".wsdlag"),
-                    _("Service Reference Document"),
-                    _("Service Reference View"),
-                    WsdlAgEditor.WsdlAgDocument,
-                    WsdlAgEditor.WsdlAgView,
-                    wx.lib.docview.TEMPLATE_NO_CREATE,
-                    icon = WSDLEditor.getWSDLIcon())
-            docManager.AssociateTemplate(wsdlagTemplate)
-
             wsdlTemplate = WSDLEditor.WSDLTemplate(docManager,
                     _("WSDL"),
                     "*.wsdl",
             wsdlTemplate = WSDLEditor.WSDLTemplate(docManager,
                     _("WSDL"),
                     "*.wsdl",
@@ -379,9 +413,11 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                     _("WSDL View"),
                     WSDLEditor.WSDLDocument,
                     WSDLEditor.WSDLView,
                     _("WSDL View"),
                     WSDLEditor.WSDLDocument,
                     WSDLEditor.WSDLView,
+                    wx.lib.docview.TEMPLATE_NO_CREATE,
                     icon = WSDLEditor.getWSDLIcon())
             docManager.AssociateTemplate(wsdlTemplate)
 
                     icon = WSDLEditor.getWSDLIcon())
             docManager.AssociateTemplate(wsdlTemplate)
 
+        if not ACTIVEGRID_BASE_IDE:
             xformTemplate = wx.lib.docview.DocTemplate(docManager,
                     _("XForm"),
                     "*.xform",
             xformTemplate = wx.lib.docview.DocTemplate(docManager,
                     _("XForm"),
                     "*.xform",
@@ -389,10 +425,10 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                     _(".xform"),
                     _("XForm Document"),
                     _("XForm View"),
                     _(".xform"),
                     _("XForm Document"),
                     _("XForm View"),
-                    ViewEditor.ViewEditorDocument,
-                    ViewEditor.ViewEditorView,
+                    XFormEditor.XFormDocument,
+                    XFormEditor.XFormView,
                     wx.lib.docview.TEMPLATE_NO_CREATE,
                     wx.lib.docview.TEMPLATE_NO_CREATE,
-                    icon = ViewEditor.getXFORMIcon())
+                    icon = XFormEditor.getXFormIcon())
             docManager.AssociateTemplate(xformTemplate)
 
         xmlTemplate = wx.lib.docview.DocTemplate(docManager,
             docManager.AssociateTemplate(xformTemplate)
 
         xmlTemplate = wx.lib.docview.DocTemplate(docManager,
@@ -407,19 +443,22 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 icon = XmlEditor.getXMLIcon())
         docManager.AssociateTemplate(xmlTemplate)
 
                 icon = XmlEditor.getXMLIcon())
         docManager.AssociateTemplate(xmlTemplate)
 
+        
+        # Note:  Child document types aren't displayed in "Files of type" dropdown
         if not ACTIVEGRID_BASE_IDE:
             viewTemplate = wx.lib.pydocview.ChildDocTemplate(docManager,
         if not ACTIVEGRID_BASE_IDE:
             viewTemplate = wx.lib.pydocview.ChildDocTemplate(docManager,
-                _("View"),
+                _("XForm"),
                 "*.none",
                 "*.none",
-                _("View"),
+                _("XForm"),
                 _(".bpel"),
                 _(".bpel"),
-                _("ViewEditor Document"),
-                _("ViewEditor View"),
-                ViewEditor.ViewEditorDocument,
-                ViewEditor.ViewEditorView,
-                icon = ProcessModelEditor.getXFORMIcon())
+                _("XFormEditor Document"),
+                _("XFormEditor View"),
+                XFormEditor.XFormDocument,
+                XFormEditor.XFormView,
+                icon = XFormEditor.getXFormIcon())
             docManager.AssociateTemplate(viewTemplate)
 
             docManager.AssociateTemplate(viewTemplate)
 
+        if not ACTIVEGRID_BASE_IDE:
             bpelTemplate = wx.lib.pydocview.ChildDocTemplate(docManager,
                 _("BPEL"),
                 "*.none",
             bpelTemplate = wx.lib.pydocview.ChildDocTemplate(docManager,
                 _("BPEL"),
                 "*.none",
@@ -432,6 +471,7 @@ class IDEApplication(wx.lib.pydocview.DocApp):
                 icon = ProcessModelEditor.getProcessModelIcon())
             docManager.AssociateTemplate(bpelTemplate)
 
                 icon = ProcessModelEditor.getProcessModelIcon())
             docManager.AssociateTemplate(bpelTemplate)
 
+        if not ACTIVEGRID_BASE_IDE:
             dataModelChildTemplate = wx.lib.pydocview.ChildDocTemplate(docManager,
                 _("Schema"),
                 "*.none",
             dataModelChildTemplate = wx.lib.pydocview.ChildDocTemplate(docManager,
                 _("Schema"),
                 "*.none",
@@ -453,8 +493,8 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         projectService          = self.InstallService(ProjectEditor.ProjectService("Projects", embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_TOPLEFT))
         findService             = self.InstallService(FindInDirService.FindInDirService())
         if not ACTIVEGRID_BASE_IDE:
         projectService          = self.InstallService(ProjectEditor.ProjectService("Projects", embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_TOPLEFT))
         findService             = self.InstallService(FindInDirService.FindInDirService())
         if not ACTIVEGRID_BASE_IDE:
-            webServerService    = self.InstallService(WebServerService.WebServerService())
-            webBrowserService   = self.InstallService(WebBrowserService.WebBrowserService())
+            webBrowserService   = self.InstallService(WebBrowserService.WebBrowserService())  # this must be before webServerService since it sets the proxy environment variable that is needed by the webServerService.
+            webServerService    = self.InstallService(WebServerService.WebServerService())  # this must be after webBrowserService since that service sets the proxy environment variables.
         outlineService          = self.InstallService(OutlineService.OutlineService("Outline", embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOMLEFT))
         filePropertiesService   = self.InstallService(wx.lib.pydocview.FilePropertiesService())
         markerService           = self.InstallService(MarkerService.MarkerService())
         outlineService          = self.InstallService(OutlineService.OutlineService("Outline", embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOMLEFT))
         filePropertiesService   = self.InstallService(wx.lib.pydocview.FilePropertiesService())
         markerService           = self.InstallService(MarkerService.MarkerService())
@@ -462,7 +502,7 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         debuggerService         = self.InstallService(DebuggerService.DebuggerService("Debugger", embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOM))
         if not ACTIVEGRID_BASE_IDE:
             processModelService = self.InstallService(ProcessModelEditor.ProcessModelService())
         debuggerService         = self.InstallService(DebuggerService.DebuggerService("Debugger", embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOM))
         if not ACTIVEGRID_BASE_IDE:
             processModelService = self.InstallService(ProcessModelEditor.ProcessModelService())
-            viewEditorService   = self.InstallService(ViewEditor.ViewEditorService())
+            viewEditorService   = self.InstallService(XFormEditor.XFormService())
             deploymentService   = self.InstallService(DeploymentService.DeploymentService())
             dataModelService    = self.InstallService(DataModelEditor.DataModelService())
             dataSourceService   = self.InstallService(DataModelEditor.DataSourceService())
             deploymentService   = self.InstallService(DeploymentService.DeploymentService())
             dataModelService    = self.InstallService(DataModelEditor.DataModelService())
             dataSourceService   = self.InstallService(DataModelEditor.DataSourceService())
@@ -477,6 +517,8 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         if not ACTIVEGRID_BASE_IDE:
             helpPath = os.path.join(sysutilslib.mainModuleDir, "activegrid", "tool", "data", "AGDeveloperGuideWebHelp", "AGDeveloperGuideWebHelp.hhp")
             helpService             = self.InstallService(HelpService.HelpService(helpPath))
         if not ACTIVEGRID_BASE_IDE:
             helpPath = os.path.join(sysutilslib.mainModuleDir, "activegrid", "tool", "data", "AGDeveloperGuideWebHelp", "AGDeveloperGuideWebHelp.hhp")
             helpService             = self.InstallService(HelpService.HelpService(helpPath))
+        if self.GetUseTabbedMDI():
+            windowService       = self.InstallService(wx.lib.pydocview.WindowMenuService())
         
 
         if not ACTIVEGRID_BASE_IDE:
         
 
         if not ACTIVEGRID_BASE_IDE:
@@ -515,24 +557,26 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         if not ACTIVEGRID_BASE_IDE:
             propertyService.AddViewTypeForBackgroundHandler(DataModelEditor.DataModelView)
             propertyService.AddViewTypeForBackgroundHandler(ProcessModelEditor.ProcessModelView)
         if not ACTIVEGRID_BASE_IDE:
             propertyService.AddViewTypeForBackgroundHandler(DataModelEditor.DataModelView)
             propertyService.AddViewTypeForBackgroundHandler(ProcessModelEditor.ProcessModelView)
-            propertyService.AddViewTypeForBackgroundHandler(ViewEditor.ViewEditorView)
+            propertyService.AddViewTypeForBackgroundHandler(XFormEditor.XFormView)
             propertyService.AddViewTypeForBackgroundHandler(BPELEditor.BPELView)
             propertyService.AddViewTypeForBackgroundHandler(WSDLEditor.WSDLView)
             propertyService.StartBackgroundTimer()
             
             propertyService.AddCustomCellRenderers(DataModelEditor.GetCustomGridCellRendererDict())
             propertyService.AddCustomCellRenderers(BPELEditor.GetCustomGridCellRendererDict())
             propertyService.AddViewTypeForBackgroundHandler(BPELEditor.BPELView)
             propertyService.AddViewTypeForBackgroundHandler(WSDLEditor.WSDLView)
             propertyService.StartBackgroundTimer()
             
             propertyService.AddCustomCellRenderers(DataModelEditor.GetCustomGridCellRendererDict())
             propertyService.AddCustomCellRenderers(BPELEditor.GetCustomGridCellRendererDict())
-            propertyService.AddCustomCellRenderers(WsdlAgEditor.GetCustomGridCellRendererDict())
+            propertyService.AddCustomCellRenderers(XFormEditor.GetCustomGridCellRendererDict())
             propertyService.AddCustomCellRenderers(XPathEditor.GetCustomGridCellRendererDict())
             propertyService.AddCustomCellRenderers(XPathEditor.GetCustomGridCellRendererDict())
-            propertyService.AddCustomCellRenderers(ViewEditor.GetCustomGridCellRendererDict())
+            propertyService.AddCustomCellRenderers(XPathExprEditor.GetCustomGridCellRendererDict())
             propertyService.AddCustomCellRenderers(WSDLEditor.GetCustomGridCellRendererDict())
             propertyService.AddCustomCellRenderers(WSDLEditor.GetCustomGridCellRendererDict())
+            propertyService.AddCustomCellRenderers(WsdlAgEditor.GetCustomGridCellRendererDict())
 
             propertyService.AddCustomCellEditors(DataModelEditor.GetCustomGridCellEditorDict())
             propertyService.AddCustomCellEditors(BPELEditor.GetCustomGridCellEditorDict())
 
             propertyService.AddCustomCellEditors(DataModelEditor.GetCustomGridCellEditorDict())
             propertyService.AddCustomCellEditors(BPELEditor.GetCustomGridCellEditorDict())
-            propertyService.AddCustomCellEditors(ViewEditor.GetCustomGridCellEditorDict())
-            propertyService.AddCustomCellEditors(WsdlAgEditor.GetCustomGridCellEditorDict())
+            propertyService.AddCustomCellEditors(XFormEditor.GetCustomGridCellEditorDict())
             propertyService.AddCustomCellEditors(XPathEditor.GetCustomGridCellEditorDict())
             propertyService.AddCustomCellEditors(XPathEditor.GetCustomGridCellEditorDict())
+            propertyService.AddCustomCellEditors(XPathExprEditor.GetCustomGridCellEditorDict())
             propertyService.AddCustomCellEditors(WSDLEditor.GetCustomGridCellEditorDict())
             propertyService.AddCustomCellEditors(WSDLEditor.GetCustomGridCellEditorDict())
+            propertyService.AddCustomCellEditors(WsdlAgEditor.GetCustomGridCellEditorDict())
         
         if not ACTIVEGRID_BASE_IDE:
             projectService.AddNameDefault(".bpel", projectService.GetDefaultNameCallback)
         
         if not ACTIVEGRID_BASE_IDE:
             projectService.AddNameDefault(".bpel", projectService.GetDefaultNameCallback)
@@ -554,26 +598,36 @@ class IDEApplication(wx.lib.pydocview.DocApp):
             projectService.AddFileTypeDefault(".gif", basedocmgr.FILE_TYPE_STATIC)
             projectService.AddFileTypeDefault(".jpg", basedocmgr.FILE_TYPE_STATIC)
             projectService.AddFileTypeDefault(".jpeg", basedocmgr.FILE_TYPE_STATIC)
             projectService.AddFileTypeDefault(".gif", basedocmgr.FILE_TYPE_STATIC)
             projectService.AddFileTypeDefault(".jpg", basedocmgr.FILE_TYPE_STATIC)
             projectService.AddFileTypeDefault(".jpeg", basedocmgr.FILE_TYPE_STATIC)
-            # projectService.AddFileTypeDefault(".xform", basedocmgr.FILE_TYPE_XFORM)  # don't register xform as a default, must be explicitly added
+            projectService.AddFileTypeDefault(".xform", basedocmgr.FILE_TYPE_XFORM)
 
 
-            projectService.AddLogicalViewFolderDefault(".wsdlag", _("Services"))
-            projectService.AddLogicalViewFolderDefault(".wsdl", _("Services"))
-            projectService.AddLogicalViewFolderDefault("wsdl.php", _("Services"))
-            projectService.AddLogicalViewFolderDefault("wsdl.py", _("Services"))
-            projectService.AddLogicalViewFolderDefault(".xsd", _("Schemas"))
-            projectService.AddLogicalViewFolderDefault("xsd.php", _("Schemas"))
-            projectService.AddLogicalViewFolderDefault("xsd.py", _("Schemas"))
-            projectService.AddLogicalViewFolderDefault(".bpel", _("Processes"))
-            projectService.AddLogicalViewFolderDefault(".xform", _("XForms"))
-            projectService.AddLogicalViewFolderDefault(".lyt", _("XForms/Skins_and_Layouts"))
-            projectService.AddLogicalViewFolderDefault(".skn", _("XForms/Skins_and_Layouts"))
-            projectService.AddLogicalViewFolderDefault(".xacml", _("Security"))
-            projectService.AddLogicalViewFolderDefault(".css", _("XForms/Static"))
-            projectService.AddLogicalViewFolderDefault(".js", _("XForms/Static"))
-        projectService.AddLogicalViewFolderDefault(".gif", _("Images"))
-        projectService.AddLogicalViewFolderDefault(".jpeg", _("Images"))
-        projectService.AddLogicalViewFolderDefault(".jpg", _("Images"))
-        projectService.AddLogicalViewFolderDefault(".py", None)
+        projectService.AddLogicalViewFolderDefault(".agp", _("Projects"))
+        projectService.AddLogicalViewFolderDefault(".wsdlag", _("Services"))
+        projectService.AddLogicalViewFolderDefault(".wsdl", _("Services"))
+        projectService.AddLogicalViewFolderDefault(".xsd", _("Data Models"))
+        projectService.AddLogicalViewFolderDefault(".bpel", _("Page Flows"))
+        projectService.AddLogicalViewFolderDefault(".xform", _("Pages"))
+        projectService.AddLogicalViewFolderDefault(".xacml", _("Security"))
+        projectService.AddLogicalViewFolderDefault(".lyt", _("Presentation/Layouts"))
+        projectService.AddLogicalViewFolderDefault(".skn", _("Presentation/Skins"))
+        projectService.AddLogicalViewFolderDefault(".css", _("Presentation/Stylesheets"))
+        projectService.AddLogicalViewFolderDefault(".js", _("Presentation/Javascript"))
+        projectService.AddLogicalViewFolderDefault(".html", _("Presentation/Static"))
+        projectService.AddLogicalViewFolderDefault(".htm", _("Presentation/Static"))
+        projectService.AddLogicalViewFolderDefault(".gif", _("Presentation/Images"))
+        projectService.AddLogicalViewFolderDefault(".jpeg", _("Presentation/Images"))
+        projectService.AddLogicalViewFolderDefault(".jpg", _("Presentation/Images"))
+        projectService.AddLogicalViewFolderDefault(".png", _("Presentation/Images"))
+        projectService.AddLogicalViewFolderDefault(".ico", _("Presentation/Images"))
+        projectService.AddLogicalViewFolderDefault(".bmp", _("Presentation/Images"))
+        projectService.AddLogicalViewFolderDefault(".py", _("Code"))
+        projectService.AddLogicalViewFolderDefault(".php", _("Code"))
+        projectService.AddLogicalViewFolderDefault(".pl", _("Code"))
+        projectService.AddLogicalViewFolderDefault(".sql", _("Code"))
+        projectService.AddLogicalViewFolderDefault(".xml", _("Code"))
+        projectService.AddLogicalViewFolderDefault(".dpl", _("Code"))
+        
+        projectService.AddLogicalViewFolderCollapsedDefault(_("Page Flows"), False)
+        projectService.AddLogicalViewFolderCollapsedDefault(_("Pages"), False)
     
         
         self.SetDefaultIcon(getActiveGridIcon())
     
         
         self.SetDefaultIcon(getActiveGridIcon())
@@ -582,9 +636,9 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         else:
             embeddedWindows = wx.lib.pydocview.EMBEDDED_WINDOW_TOPLEFT | wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOMLEFT |wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOM
         if self.GetUseTabbedMDI():
         else:
             embeddedWindows = wx.lib.pydocview.EMBEDDED_WINDOW_TOPLEFT | wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOMLEFT |wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOM
         if self.GetUseTabbedMDI():
-            self.frame = IDEDocTabbedParentFrame(docManager, None, -1, wx.GetApp().GetAppName(), embeddedWindows=embeddedWindows)
+            self.frame = IDEDocTabbedParentFrame(docManager, None, -1, wx.GetApp().GetAppName(), embeddedWindows=embeddedWindows, minSize=150)
         else:
         else:
-            self.frame = IDEMDIParentFrame(docManager, None, -1, wx.GetApp().GetAppName(), embeddedWindows=embeddedWindows)
+            self.frame = IDEMDIParentFrame(docManager, None, -1, wx.GetApp().GetAppName(), embeddedWindows=embeddedWindows, minSize=150)
         self.frame.Show(True)
 
 
         self.frame.Show(True)
 
 
@@ -599,12 +653,10 @@ class IDEApplication(wx.lib.pydocview.DocApp):
         # wxBug: On Mac, having the updates fire while the tip dialog is at front
         # for some reason messes up menu updates. This seems a low-level wxWidgets bug,
         # so until I track this down, turn off UI updates while the tip dialog is showing.
         # wxBug: On Mac, having the updates fire while the tip dialog is at front
         # for some reason messes up menu updates. This seems a low-level wxWidgets bug,
         # so until I track this down, turn off UI updates while the tip dialog is showing.
-        #wx.UpdateUIEvent.SetUpdateInterval(-1)
-        #appUpdater = updater.AppUpdateService(self)
-        #appUpdater.RunUpdateIfNewer()
         if not ACTIVEGRID_BASE_IDE:
             wx.UpdateUIEvent.SetUpdateInterval(-1)
         if not ACTIVEGRID_BASE_IDE:
             wx.UpdateUIEvent.SetUpdateInterval(-1)
-            appUpdater = updater.AppUpdateService(self)
+            UpdateService.UpdateVersionNag()
+            appUpdater = UpdateService.AppUpdateService(self)
             appUpdater.RunUpdateIfNewer()
             if not welcomeService.RunWelcomeIfFirstTime():
                 if os.path.isfile(tips_path):
             appUpdater.RunUpdateIfNewer()
             if not welcomeService.RunWelcomeIfFirstTime():
                 if os.path.isfile(tips_path):
@@ -613,22 +665,50 @@ class IDEApplication(wx.lib.pydocview.DocApp):
             if os.path.isfile(tips_path):
                 self.ShowTip(docManager.FindSuitableParent(), wx.CreateFileTipProvider(tips_path, 0))
 
             if os.path.isfile(tips_path):
                 self.ShowTip(docManager.FindSuitableParent(), wx.CreateFileTipProvider(tips_path, 0))
 
+        iconPath = os.path.join(sysutilslib.mainModuleDir, "activegrid", "tool", "bmp_source", "activegrid.ico")
+        if os.path.isfile(iconPath):
+            ib = wx.IconBundle()
+            ib.AddIconFromFile(iconPath, wx.BITMAP_TYPE_ANY)
+            wx.GetApp().GetTopWindow().SetIcons(ib)
+        
         wx.UpdateUIEvent.SetUpdateInterval(1000)  # Overhead of updating menus was too much.  Change to update every n milliseconds.
 
         wx.UpdateUIEvent.SetUpdateInterval(1000)  # Overhead of updating menus was too much.  Change to update every n milliseconds.
 
-        # we need this for a while due to the Mac 1.0 release which put things
-        # in ~/Documents/ActiveGrid Projects/demos.
-        # Now it should be ~/Documents/ActiveGrid Demos/ 
-        base_path = appdirs.documents_folder
-        if os.path.isdir(os.path.join(base_path, "ActiveGrid Projects", "demos")):
-            message = _("The location where demo files are stored has changed between the 1.0 and 1.1 release as a result of improved multi-user support across platforms. In order for ActiveGrid Application Builder to find these files, they need to be moved from '%s/ActiveGrid Projects/demos' to '%s/ActiveGrid Demos'. Click OK to move the files.") % (base_path, base_path)
-            wx.MessageBox(message, _("Demo Files Location Update"))
-            import shutil
-            shutil.copytree(os.path.join(base_path, "ActiveGrid Projects", "demos"), os.path.join(base_path, "ActiveGrid Demos"))
-            shutil.rmtree(os.path.join(base_path, "ActiveGrid Projects"))
-
-        
         return True
 
         return True
 
+class IDEDocManager(wx.lib.docview.DocManager):
+    
+    # Overriding default document creation.
+    def OnFileNew(self, event):
+        import NewDialog
+        newDialog = NewDialog.NewDialog(wx.GetApp().GetTopWindow())
+        if newDialog.ShowModal() == wx.ID_OK:
+            isTemplate, object = newDialog.GetSelection()
+            if isTemplate:
+                object.CreateDocument('', wx.lib.docview.DOC_NEW)
+            else:
+                import ProcessModelEditor
+                if object == NewDialog.FROM_DATA_SOURCE:
+                    wiz = ProcessModelEditor.CreateAppWizard(wx.GetApp().GetTopWindow(), title=_("New Database Application"), minimalCreate=False, startingType=object)
+                    wiz.RunWizard()
+                elif object == NewDialog.FROM_DATABASE_SCHEMA:
+                    wiz = ProcessModelEditor.CreateAppWizard(wx.GetApp().GetTopWindow(), title=_("New Database Application"), minimalCreate=False, startingType=object)
+                    wiz.RunWizard()
+                elif object == NewDialog.FROM_SERVICE:
+                    wiz = ProcessModelEditor.CreateAppWizard(wx.GetApp().GetTopWindow(), title=_("New Service Application"), minimalCreate=False, startingType=object)
+                    wiz.RunWizard()
+                elif object == NewDialog.CREATE_SKELETON_APP:
+                    wiz = ProcessModelEditor.CreateAppWizard(wx.GetApp().GetTopWindow(), title=_("New Skeleton Application"), minimalCreate=False, startingType=object)
+                    wiz.RunWizard()
+                elif object == NewDialog.CREATE_PROJECT:
+                    import ProjectEditor
+                    for temp in self.GetTemplates():
+                        if isinstance(temp,ProjectEditor.ProjectTemplate):
+                            temp.CreateDocument('', wx.lib.docview.DOC_NEW)
+                            break
+                else:
+                    assert False, "Unknown type returned from NewDialog"
+
+    
 
 class IDEDocTabbedParentFrame(wx.lib.pydocview.DocTabbedParentFrame):
     
 
 class IDEDocTabbedParentFrame(wx.lib.pydocview.DocTabbedParentFrame):
     
@@ -2129,723 +2209,720 @@ u\x9d\xe9\xa9\xeaTu\xb2\xd3\xe96\x9a\xcdN\xa7\xc79\x17R\xeaq\x85R\xe28N!\x9f\
 \x94Zv\xe0\xfb\x8e{\x8bA\x8dT\xd9=\xcf-\x16\x0b\x04\x80RbY\xb6\xfa\xb8\xc5Ba\
 \x9f\nq]7\xf3]\x18cD\xcaR\xa9X*\x15\xdb\xed\xce\xf6\xeen\xb7\xdb\xd7\xb3.3\
 \x11J\xc9\xc0\xb5t\xaa:1Q\xb6(U\xc3\'!$\xcc\xe5\x84\x90T\xb51\xdb\xb6,+\x9f\
 \x94Zv\xe0\xfb\x8e{\x8bA\x8dT\xd9=\xcf-\x16\x0b\x04\x80RbY\xb6\xfa\xb8\xc5Ba\
 \x9f\nq]7\xf3]\x18cD\xcaR\xa9X*\x15\xdb\xed\xce\xf6\xeen\xb7\xdb\xd7\xb3.3\
 \x11J\xc9\xc0\xb5t\xaa:1Q\xb6(U\xc3\'!$\xcc\xe5\x84\x90T\xb51\xdb\xb6,+\x9f\
-\xcf\x1fD\x15x\xc3E\x07\x1b@\t|\x0f\xb5\xfd\x86{-i\x11\xff\x7f{\xef\xfe\x1b\
-\xd7q\xe6y?u\xae\xdd\xa7\xaf\xbc4u#u#%Y\xa2\xed\xc8\xb2\x13\xc7\xce&\x93\xcc\
-$c\xcf&\xeb\xcc\xcc\xee\xecb\x81\xc5\x02\xefO\x0b,\xf6\xcf\x19\x0c0x\xf1\x02\
-/\x06\x03\x0c2~\xd7\xef$\xefd\xc7\x8e\xb3\x13G\x8e-\xcb\x17I\xbeP\xb2l\x8a\
-\x92LR\x94\xd8$\xc5>};\x97\xaaz\xde\x1f\xaa\xbbX<}QK\xb6(\x92\xaa\x0f\x0c\
-\xa1\xdd\xacS\xa7\xaa\xfa\x9co=\xe7\xa9\xe7<\x15\xdce\xc1*\xc0=\xa6\n\xd3\
-\xcd\xa5r\xfbv\x9e\xb8\xcb+I:\xbe\xc5\r&.Gqo/\r\xff\xe5\xad\xa1?O\xc5\xcb^4\
-\x9f\x8eoY\xbcA\x00\xd1\xb0b\xb3\x18\xa4\x0eE\xa9\t\xee\x8e\x98\xa6i\xb5sgSJ\
-\xabC\xdf[\xcf\x7fWVeY\x96\xeb\xba"/\xa0x\x19\xbd\xb3\x19\xa2\x01\xb0\xd9/a\
-\x9a\xa6\x88\xa0\xe0\xdcdFa\xa5\xf8\'\xe5\xc2\x1f\x1b\xac\xe1\xb2\xb5tt+E\
-\x97-^\x07\xe0\x80\xc0\x8dTd\x8f6\xdd\t\xea\xee\x05wH\xa4!4\x8c\xeeQ\xde\x9d\
-\xbe y\xf30\xc3X\xcf}\xe7n\xf6\xdb&\xab\xa5\xe8r:\xba\xe5\xd2\xb2\x89\xedWH\
-\x0c\x07\xed<\xf7\x0e\x18\xc5c\xe9\xe1\xc3\xe9\xfc\xa8\xeb\xbaD\xc10\x8cf\
-\xfe\xe9\xeb\xee\t1a\x88:-%-b6\x9bM\xa5R\xa2\x97\x03\xfe("\x8cD\xdc\xe7\x0b{\
-\xfe\x8fD|\x8e8\xa9\xf8\xed\xd2\xe9t&\x93\xf1R^\x9f\xa5\xd4\xcesu\xbe\xe2$N\
-\x1d\x86a+\x84\x861\xceM\xdf<]\xf1\xbee`l\xd1\xf5t\xbc\x94\x8e\x97l\xb6n\x00\
-\x03\x04n8\x915\x12\xb8\xe3\x91\xbb\x9f;\xc3r1P\xae\xa3XJ6\xf9\xae\xcd\x90\
-\x13|\xa2\r\xe2\x02\x90\xd6k.\x97\xcd\xe5\xb2\xd0z\xeb\x12\xc5FE\xc2\x08\xb0\
-m[>\x8d\x89\xbe\x8b\xf1\x8fc\x8a(\xfd!-\x97\x0e1\xfa-2\x9b\xa696V\x1a\x1e\
-\x1e"\x848\x8e\x93N\xa7E\xea$\x91\xcfr\x90Q\xed\x1cO\xc30\n\xf9\xfc\x13\xc7\
-\xd3\x88h\xdb\xb6\xf8\xa5\xc4\xbd\xd0k\xceS\']\xb5\x9e\xf6/\xc2\xc5hp\xce\
-\xe3\x98\x06A\x10F\x91\x98\xc9\x08!\xb6m{^\xdaK\xa7\xc5\x8e\xaff{{E\x91Bj\
-\xdf\xde={\xf7\x8c\x99\xa6\xe9\xba\xaeH\x12\xe5y\xde}\xe5U~$lJ6\xc06=\xa8\
-\xab\x0c\xbc\xd7\x12\xc6J\x16\x13\xd6;\xbdEf\xe4(<\x94T\xe3[\x82\xfa\\\xac^I\
-ma\xe5\x88\x18\x9b\xfb+\xa9\xfd\x15\x00Pn\x03q`\xaa\x9d\x8d\x00\x00x{\xfbG)\
-\x9aB\xdd\xa4\xef\xb8\xd3\xa2TM\x15\xa9\x922J/\x8a"q{o8\x85\xad|\x84\xb9(}\
-\xa8\xa2\xd4\x908J\xac\x10\xb6v5\xea\xb6\x9f\xaa\xea\x0b\xc2\xf6n\xf1\xa6i\
-\xca;\x07\xadb\xe0\x14\x02\xef\x98\x1c\x10\x00\x90S\xa0\xe7y\xc4\xc9$d]\x1a\
-\xda\x88h\x18\x86\x14w\xf9R\x92\xd5;\xf0\xbc\x13\xa2,\xab\x8aIHT\x9b\xf0\x0f\
-\xa8\x13\x95\xab\xec1{_\x1a$*\x91\x17\x83*\xac\xe2\x1a\x90\x97\x01\xe7\x16\
-\xb3R\xb5\xd4\xde\x1a<\x93\x18|q\x94\xd3v%Ie\xbfg\x93H{7g\xd1\x86\xc4x\xaa\
-\x17\x80\xaat\x96E:\x0b\xcb\xf5gad\xb4\x1e\x05:^\x81\xf3\xd2\xe9\xce)-\xf1#\
-\x8ao\xd4\xebv\xf0\xf1T\xab\x95\xb7\x89e\xb5\xf4!q/t=\n\xda\x97\xa8Z\x8f\xfa\
-\xa3\xc8\tO\x8c[:\x9dR\x8f\xed\x1c\x13\xf1=\xa5T\\\xe4\x9csQ\xbfzK\xde\xef\
-\xbc\xb5\xc5\xc4\xfeM\xe1\x05\x15;rt-3\xf8^K\xb4~\x9bGU\x00 \x00q\xef\x85Y\'\
-S\xb2\xd3C\xb0\x13\x17T\xa1\xe3J\x92W\xb9eY\xeae$\xa4\n7\xafI&d\x0b\x00\xd4\
-\x8d\xe7\xd5\x8bO\xa8\x8f\xb4\xe0z\xb5A6@\xca\xb4\xf0H\xc8\tCu\xcb&|\x08\xf2\
-.\x92\xe1\x1f\x89\xbd\xea\x13g\x94\xcf\n\xa0\x18Jr>\xebt\x11@[\xdceD\x87\xa9\
-\xbc\xff)O-\xd2\xb1RJe\xf3\xd4\xee\x0f\xe2-\x81\xcdZ\xa3*\xbbt\x9a%:\xa2\xce\
-g\xfd\xdd \xbd.\x00YX\x15#!\xac\x89\xc1\x97W\x02\xc0\xa6iU]\xc6\x90\x8d\xb17\
-oo\xddG\xdfAI\x0f)\xc73q\x01\xa8\xbf\xbe\xfa\xc3%\xd6\'\x08!\xc2\xff \xb6(`\
-\x8c\'\xfc\xec\xb9\x0egHb$\x85\xa1\x03\x00j\xb2\xe2\x07\x982\x13\x17\x86\x98\
-\xf8\x13\xf7\xc2=\xf5\xbd\xebl\xa7\xdaO\t\x7fZb\xf4\xe4\x98\x88\tO\x15w\x19\
-\x8ds\xbf]\xdbzx\\\xa7\x8d\xd6{\xcb}v\xe4\xf0\x86\x8f\xf4\x0fX\x10 \xa7\x1b\
-\xe1\x8f\xd0s\xaa\x00\xc3\xf4FZ>\xe4\x1d)\xee\x02U\xfe\xa4jKqWWuT1\xb5\xd4\
-\xfc\xb4m\xa7\x9e\xaa\x05\xd2(\xb66\xc7\xed\xf6\xd1\xf7\xc4\xa5,\xd2U\xaaO\
-\x03\x9d\xb2\xab\xb6\xc7T\xde\xdd\x97O\x0c}\x8cw\xd8\xac\xec\x8a#"\xb9\xc0H6\
-\x9b\xd2\xc2\x0f`\xb4\x83\xe5\xc5\xf7"\xf0C\xd4\x00\xed\xb9Pu\xb0\x0c~#\x91\
-\xcd\x0f\xe6\x84\x10\xcb\xb2\xc4\x08\x80\xa2\xad\xb2#\xc2X\x96w\xf2\xfd\xca\
-\x90\x98<`\xf3\xe4\x9a\x98Y\x13\x93kb\xf0U;\xb1\xeb\xe0\xf7o\x92:\xc7t\xbd\
-\x00:\x7f}\xb5\xa9\xa2\xb5\xd2\xc8\x10\x87\x18\x86\xb1^\xa90\xa6\xbe\xb0\x03\
-\x84\x90|\xbe\x8b\xa7[*;\x00\x98\xa6)\xc4]\xeac\xd7\x8b\xf6\x9e\xa3\x9a\xb80\
-\x84\x16\xcb\x8b\xa1\xff\x03\x01\xd9l\xf1\xa8#\x9c\x98q;\x17K\xe4\x80\xc8\
-\xc9ULx\x8c11\xa4b\xf2\xeborm+b\xff\x86\xdcH\xaf\xd7\x8e\x1c\xb67,\xac\xec{B\
-\xab\xf3\xc8\x02\xb8\x97\xd9\x9e.L\xc8x\xca\x9d*\xee\x89{[\n\xa5\x0c\x9eI,\
-\xd9\xabf\xb2\xd9\x8e\xa1$m\xa7^\xc2\xc4\x03\xe5I\xbf\xff\xd3\xa8\xa0Z\xad]\
-\xbb6\xc7\x11\xbf\xf5\xf4\x93\xe2~\x90a\x03\xa2f\xf5\xf1\x1c6_\xd0\x89&\xa9\
-\x06W\xe2\x8c\t\xcbH\xde\x84\x9d"\xd2\xa9#\t\x83\x88(&\xb6\xa1x{\xe4\xb9\xd4\
-\xee\xf7\xbf\x91\xc4\xb9\xe6\x17\x16WVV\\\xd7=\xf9\xc4\t9\x03\x19\x86!\x03\
-\x16\x13G\xc9\x9b\xb9\xebL\xd6\xeb,\xbe\xef_\x9b\xbb\x0e\x00g\x9e9\r\x9b\'WY\
-\xa1\x18|1&\x97\xaf\\E\xc4}\xfb\xf6d=O\xb5\x9d\xe5\xc5 \xdb\xa0\xfe\xd0\xf2\
-\xb7\x1ep>Ce\xf7gU\xce\xd4\x9f>1\xb6\xaa\xbe\x9bm\xdf\xa0\xb4\xdc\xdbo\xedo\
-\x98z\xa9\x94\x9brS\x89!J\xfc\x88B\x8e\xa1}\x85\x0cr\xdd\xf6\xea\x8bza\xa8\
-\x0fs]}\x86\x9d\x95\x00\x804\xf0\xa5\x95\xa0*{\xe2vH\xfc\x16\xea\xf3\x01"\nq\
-\x97\xf7\xb2\x9c\x03\xee\xebQo\xeb\xe1\x91\xdfZ\xf9\x04\x88)v7\xdb\x07\xdfk\
-\x89\x06\xb4\xbe\x08\x00\xa4\xaf\x87\xc7\xb0R\xe9\xe2F:\xc9\x9d*\xee\xd0\xcd\
-p\x96vbW\x1f\x85zK\xcb{XX%\xa8\xec\xbc,\xcb\xab\xf6]\xe7\x05\xad\xbaY|\xdf?w\
-\xfe}\x00x\xe6\xf4\xd3\xf2\xf6^^.[\xb6\x95\xf1<Us\xb1\x1d\xdd\xa8\xb6GmR\x7f\
-e\xe9\x943q\xdd\xcb.\xaf\xae\xad9\x8e\x93\xcddd\x17T\xe1\xb3\x14\x07\xba\xda\
-\x0cKY\x04\xeb\xda\xfdN3V\x16\x16\x1f\xca\xcb\xe5s\xe7\xdf\x9f\x18? \xc4\x1d\
-\xdar\xd0\xe9\x94H\xd4\xdf\xdf{\xb0\xbc\\v]\'\x9f\xcf\x8b\x1a*\x15\xff\xdds\
-\xe7\x01\xe0\x99\xd3\xdf\x92\xe5eG\xd4\xc1\x11\xa7\xfel\xe62\x00\x8c\x8d\x95\
-\xb2\xd9l\xe7\xe0\x93\xb6gF\xfd\x89\xd5\xc1\x19\x10q\x15\x01\xc0\x97\xb3\xd7\
-\x0e\x1c\xd8\xef\xa5\xd3\xe2:T/\xc2\xae^\x08\xf5"\x14\xb1\xed\x88\xb8\xb2\
-\xb2\xdahe\xcb\xdah\xc3\xde={\xa4\x87:q%\x88\xd9Z\xf4W\x8a\xbb\xfa\xc3\r\xde\
-\x1192\xb0\xf9\xc2\x00e"\xe9\xf3cu\xb6M\x1e%\x05Z\x9a \x1dq2\x9bn\x04\xd9rq{\
-\x8a;\x14\x15\xff\xea\xfd\xce[[OT\xb9!"\xa3:3\xf1J\xdc\xdc\xde\x81\xf7Z\xba\
-\x8e\xbc\x15C\x15\xf7Y\x98\x1d>B\x8c\rI\xdf\xc1\xe2.\x90\x96\x82a\x18\xc2w,\
-\xaf\x9eN\xb5\x92w\xb5\xbc2\xa4\xb8\'\xd4M=\xa4\xd3@\xc0\xcd\xc8+U^\xd6\x9f_\
-\xfd\xe2\xec\xdb\xef\xb8\xae\xf3\xf2K?\x19\x1d\x19\x91\x95\xab\xfa\xd2\xd9\
-\x9e>\xb2\x9e\xe8\xaf*\x10\xb2\xd7W\xaf~\xf9\xee{\xe7\x1d\xc7\xf9\xd3\x1f\
-\xff\xf1\xe8\xe8HB\xc8\xe4m)\xeb\x97\xdfw\xf6\xbd\x7f\xf7;\x07\x81\xb7_\x1fL\
-\xdc\xd8\xea\x14\xd8\xbf~\xf9s\xc8\xca/_\xfe\xfc\xad\xb3o\xbb\xae\xfb\xef~\
-\xfag\xa3\xa3#\tE\xc0\xcd/\x01\xc8\x9fR\x8c\x86\xdaf\xe1\xfcQ\xbb\x90\x18|\
-\xf5z\xb8/\xbdH\xb4\xf6\xec\x1f\xfe\xb0\x7f\xdf\xbe?\xfe\xd1\x1f\x89(\xbd\
-\xc4E\xd2g`\xa5u\xef\xfb\xd5\x1b7\xe7\x13IT\x081&&\xc6m\xdb\xeel\x9bly\xa7\
-\xe9\xd0\xff\xb7\xeb\x83z\xc1t\xceI\x83W\x98\xb8P\xe5-\x99\xb0\xa2\xa0c\xc2K\
-\x9cBH\xf97\xd5\xbb\xad\x81\x05k<l%!\xef\xe3\x1cO\x17\x0f\rR\x1b\x8f*\xacY\
-\x06\x00"\x92\xb6\xf7\xf0\xf0X\xe9\xa2\x9b\x1d\xdb\xf4\xcd}4y\xbb"\x7f`q\x11\
-\xc8K\x01\x94\xdb/qYt\x1e\xdbK\x80\x12\x1f$\xa8\xbc\xc0\xc2\xda\xa2#\x9e\xac\
-\x11qa\xf1\x16\x00\x84at\xfb\xf6\xf2\xbe\xbd{;\xeb\xef\xda\x9e\xfb\xbdm\xc4\
-\x12\x93\xf8\xd20\x8c\xa5;w\x00 \x8a\xa2\xe5\xf2\xca\xfe\xfd\xfbd\xcd\xea\
-\x87\xc4Y\xc8fI\x1d\xb0\xfbrJS\xff\x95\x7fHt\xea\x9e\x95\xab\xcdP\xa5p~a\x01\
-\x00\xc20\xfcj~\xa1X,$\xc6\xd9\xe8\xb6\xe0\x9c\xb0\x07\xc5\x07\xcb2]\xd7M\
-\x94\xec?,\x83\xa0\xb6v\xe9\xf6\xed\xf3\x1f|\xc89.,\xde\xfa\x977\xde\xfc7/\
-\xbe\xb0\x7f\xff>\xb5\xfb\t-SO\'\x07\xb0\xd9l\xce\\\xf9\xbc\xdehl~\x82\'\xc5\
-b\xbe\xd4\x9e\xaa;{\x01\x1d?b\xd7\xcb{@z\xd5\xd9\xf9\xa7\xc1k#\xed\'\x1bu\
-\xb2\xef: \x89{A\xce+\xdfT\xef\xb6\x8c\xd8\xbf!>\xf41\xdb\xd3\x85\xf1\xc1\
-\xf6Z\xc2\xb8\xfd\x10\x80\xf7\xf0\xf0$\x17fw\x83\xb8\x0b\xd4{\xa6\x97\xa0@\
-\xef\x0b\xe2\xbe.\x14lo\xe2\xda\xf2!\xb6\xdf\x17\x17{m\x03\xc0\xd3ON\xaf\x94\
-W\xf2\xf9\xdc\xf4\xa9\'\xfa??\x0e~^\xd9\xa9\x99\xcbW\xae]\x9b+\x95J\xdf}~#\
-\xed;\xe7\xfc\xe9\xa7\xa6WVV\xc5I\xadv$Y\x9f\xb3<\xc0\xed\xaa6\x06\xdb\xa1{\
-\x1b\xe1(JlR\xffS\xf7\xa9V\x8a\xddSON/\x97Wr\xb9\xec\xb1c\x93"\x92G\x8es\xaf\
-\xdf\x17\x11\xe5R\xc7F\x03\x80HgT\x82\x07V\x079\xbdq\xce\xab\xd5\xda\xd9\xb7\
-\xdfi6\x9b\xe2\xd1\xa5\\^y\xe3\xcd\xdf~\xeb\xe9\xa7N\x9d<\x99N\xa7\xfa\x9cBV\
-\xc2\x18\xbbsg\xf9\xd2\xc7\x9f\x94\xcb\x89\x9d\xcd\xd14\xad\xa9\xc9I\xcf\xf3\
-zY\xa9_\xe7G\xec\xc57^\xe7\x83\xdd\x9b;B\xc7;a\x8de\x11\xb0(\xb4\xb8{!B\xdc\
-\xdc\xbe\xc1j+\xf3p\x1d\x00\x08\x81>\xc9\x06\xdc\xdc\xde\xcet\x92\xbbG\xdcU\
-\x1e\xea\xa5\xa0\xde\x93b\x8d\x88\xb2\x96\xe80\xc6\xc4\xa9\x87\x86\x8a\x7f\
-\xf5\x1f\xfeB\x86\x16|\xcd\xf6\xa8\xd6\x1f"\x86A8\xbf\xb0\x88\x9b[\x82\x88\
-\xc3CC\xff\xf1\x9b;\xe9=\x11\x13[{\x95\x8c\xb5\x1a\xfa\xa0\xef\xbbK+Xv\xa7X,\
-\xfc\xfb\xbfxE\x9dE\xa4d\xab\x0f\xe9\x89\x1a\xe4\xef"["\xdf\\\xff\xa6\x06D\
-\x9e\x88RZ\xad\xd6\xdey\xf7\xbd\xbbw\xd7\xc5\x19D\x81F\xa3y\xee\xbd\xf7\xaf_\
-\xbf\xf0xc\xbe\x00\x00 \x00IDATy\xf8\xf0\xa1\xc9\xa3G\x86\x86\x8a}*YZ\xba=w\
-\xe3\xc6\xdc\xb5\xb9F3\xe8H3@\xf6\xee\x1d;tp\xa2\xab\xbfb\x87\xb2\x0b\xba\
-\xd0\x0f\xe4\x91L\xed\xdb/\x14}t \xb3\x1d\x99|\x08@\xec9U\x10\xd3\xf6\x86\
-\x0ew~\xbf;\xc5}\x0bH\xe8\xbb\xf8R\x8a\xbb\xe0\x1b\xbc\x1bQYE\xe0b\x8f\x11\
-\xdcp\r\xa9\xb1.[#\x01B\x9bD\xf7\x13\x96{\xa7\xec\xdeW\xb5\xb2;r2\x93\xdf\
-\xa8\xe2\xde\xe7\xf0\xa4\xb8?\xe0t\xd3\xb3\x85R\xd9\xe38\xfe\xfc\xea\xd5\x85\
-\xc5E\xd5\x81 \xb9}\xe7\xce\xed;w>\xfdlf\xcfXi\xff\xfe}CCE\x11\xe3!\xc6-\x08\
-\xc2\xd5\xd5\xd5\xc5\xc5[\xabkw\x83@\xca\xfa\xa6\x1a\xf2\xf9\xdc\x93\xa7Nn\
-\xff\xc8\x10\x8ddSj\xdf\x9e;r\x18\xde\xd0\x91\x81j\xab\xddB\xdaDa\xb6\xf7\
-\xcfKcuI\xbd\xb9\x1b\xc4]\xde\xc6\xe5\xf2\xca\xfc\xc2\xc2\xc2\xc2"\x00\xe4\
-\xf3\xf9\xb1\xb1\xd2\xf4\xa9\x93j\x99\x0b\x17/\x01\xc0\xd4\xe4Q\xd7ug._)\x97\
-W\xc20t]\xb7T\x1a}r\xfa\x94\xf4\xcc\xde\xb3\xf0\x13\'\x8e\x8b\x95\xcc\x96\
-\x0c\xb5S)\t\x9f;\x00\xd4j\xb5\xf9\x85E\xc30D\xd0\x9e*F\xe5\xf2\xca\xec\xb5\
-\xb9r\xb9\xac6R\x16\x08\xc3p\xf6\xda\\\xb9\xbc\xe2\xfb>\x00\x8c\x8f\x1f\x98\
-\x9a<\x9a\xcb\xe5\x84f]\xfa\xf8\x13D\xb8\xb5\xb4\x04\x00\xbe\xef_\xb8\xf81\
-\x00\xda\xb6=y\xf4\x08\xe7\xbc\xcfI}\xdf\x9f\xbd6\xd7udz\xf5W\x16\x9e\x9a\
-\x9a\x9c\x18O&\xbeP\xfd\x12\x1b\xef(!r\xceo--\xcd\xcd\xdd\x10\xed/\x95J\x13\
-\x13\xe3\xf2py"B\xc8\xd4\xe4\xd1|>\xaf~?s\xf9J\xb3\x19\x0c\x0f\x15\xc7\xc6J\
-\xa2;7\xbf\x9aG\xc4\'N\x1c\x97\x81\x16\xea\xd9\xd5>\xce/,\xce\xcf\xcf//\x979\
-\xe2P\xb1p\xe8\xe0\x84\'S\xabo.\x1c\x86\xa1\xfc5\xc5P\x88.\xcb\x02\xf3\x0b\
-\x8b\xe5r\xd9u\xdd\xe9S\'\xe7\x17\x16\x17\x16\x16\xcb\xe5\xf2\x0b/<?V*\xc9\
-\xfa\x84\xb8GQT\xc8\xe7\xb3\xd9\x8c\xb2GG\xab\x88\x94\xe9z\xbd6w\xbd~\xfd\
-\xc6M\xc30TqFL\xec\x9aB\x12f{:\x9dzj\xfaT.\x97\xb3\xee\xe7%a\xcd#\x049\x8dk\
-\xf3\xe2s\x9f\xd4\xbenn\xaf\xe9x\xdd\xfe\xb2\xb96\x16\xc5\xd5yqMp\xdes\xf3&\
-\xd3\xf1z\xe5\xa5\xd9\xd9\xe2.o\xf2 \x08~\x7f\xf6\x0f"\x08\xba\xcd\xe2\xcc\
-\xe5+\x9f\xcd\\~\xe5g\xff\xd6u]Q\xec\xdc{\xef\x03\x00\xe7\xfc\xd2\xc7\x9f\
-\x86\xe1\xc6\x1e1\xb3\xd7\xe6.\\\xfc\xf8\xe7\xff\xee\xa7\xa5\xd2\xa8\xac\xb3\
-\x7f\xe1\x97\xfe\xf4O\xf2\xb9\\K\xe00\x19\xc7]\xab7>\xbap\t\x00\xce<sZ1!\xf1\
-\xf7g\xffp\xf9\xca\xe7\x9d\x8d\xfc\xe1\x0f\xbe?::2{m\xee\xec\xdb\xef\xa8\xe7\
-\x9a_X<\xf7\xde\xfb?\xf8\xfe\xf7\x8eMM2\xc6>\xf8\xf0\x82\xfc\x93_\xad\xbe\
-\xff\xc1\x87\x00\xb0\x7f\xdf\xbe\xc9\xa3G\xfa\x9c\xf4\xc2\xc5\x8f\xcf\xbf\
-\xffA\xe7I_\xfe\xd3\x1f\x8b(\xc3\xbe\xfd]\x9c\xb9|\xe5\x87\x7f\xf4\xfd\'\xa7\
-Ou\x1d\xfc\xb6\xc4\xb7\xd4\xf3\xec\xdb\xef\\\xfd\xe2K\xb5\xfd\x17.^\x9a>u\
-\xf2\x87\x7f\xf4}\xd9$q\xa2\xd1\xd1Q\xf1\xd6\xa5T\xdek\xd7\xe6\x16\x16o=\xf7\
-\xec\x99\xb1\xb1\x12\x00Tk\xf5\x8b\x97>\x01\x80\x13\xc7\x8f%B,ZO0\x9c\x8b\
-\x9f\xfe\xbd\xf3\x1f\xa8\xa3\xba\xb4t\xfb\xf2\x95\xab\xcf|\xebi\xb5\x9d\xa2\
-\xf0\xcc\xe5+\xe7\xdf\xff0\xd1\xbb\x8f.\\|\xf9O\x7f2::"\n///\xbfw\xfe\x83\
-\xf1\x03\xfb\x97\x97\xcb\xb2\xdag\x82P>\x91\xc8G\x96(\x8a\xb2\xd9\xcc\xd3ON\
-\x7f\xf1\xe5\xec\xea\xda\x9a\x8c\x18J\xb8V\xe4!\xea\xe0)j.5}\xe3\xa8l63}\xea\
-\x89\xd1\xd1\x11\xf5\x8d\x1e\xd0lohm\x11i\x00 \xf25\xde\x9f\x0b\xa5\x93\xb8z\
-\x13\xd5\xa4\xed=\xc3\x1f\x8f\xf6\xcaK\xb3\x83\xc5]\x15\x17JY\xa5\xe2\xe7r\
-\xd9\xa7\x9fz\xf2\x89\x13\xc7\t!KK\xb7\xdf\xff\xe0\xa3ryE\x88\xa3\xea\xb88\
-\xff\xfe\x87\xae\xe3<\xf7\xec\x99\xd1\x91aBH\xadV\xff\xf8\x93O\xfdj\xf5\x97\
-\xff\xdf\xaf\xff\xc3_\xfe\xb9Hlt\xcf\xc2\xaf\xbf\xf1\xdb?{\xe9\'\xae\xeb$\\\
-\x04\xad\xb5\xa3n\x8d<\xff~K\x83\x9e{\xf6Lit\x04\x08Y]]\xfb\xf8\x93O\x01\x81\
-qF)m6\x9ba\x18\x9e8~\xec\xe9\xa7\x9e\xcc\xe7sqL?\xbf\xfa\xc5\xfb\x1f|x\xf6\
-\xedwFGGR\xae\xfb\xf4S\xd3\x88\xb8\xbc\\\xbe\xb3\\\xcef\xb3\'\x8e\x1f#\x848\
-\x8eM:\x1e\xda\xe5y/^\xfa\xf8\xfd\x0f>\x02\x80\x13\xc7\x8f\x1d9|\x88\x18F\
-\xadV\xfb\xf8\x93O\xcb\xe5\x15\xd1_\xc7qT\x13\xf2\xfc\xfb\x1f\x8e\x8e\x8c\
-\xbc\xf8\xc2\xf3)\xd7\x8d\xa2\xe8\x93Og\xca++o\xfd\xfe\xed\x89\xf1\x03\x85B\
-\x97\r\xc0T\x17\xca\xd2\xed;K\xb7\xef\xec\xdf\xb7\xf7\xc4\x89\xe3)\xd7\x05Bn\
-\xdc\xb8y\xe5\xf3\xab3\x97\xaf8\x8e\xf3\xfcw\x9e\xdb4V\x8aA\x8d\xaarC2\xdef\
-\xa38l\x88\xbbxH\xe2\x9c\xbf{\xee\xfc\xe7W\xbf\x00\x80\xe3\xc7\xa7&\x0e\x1c`\
-\x8c\xf9\xd5\xea\xd5\xab_^\xfc\xf8\x93V\x0b\xdb\x1e\xad\x85\xc5\xc5\xb3o\xbf\
-#\xc6\xff\xe8\x91C\xf9|\xbeV\xaf\x7f\xfc\xf1\xa7W>\xbf\xfa\xfao\xde\xfc\xf7\
-\x7f\xf1s1\x14\x8cq\x00XX\xbc\x05\x8b\xb7FGG&\x8f\x1e\x89\xa2x\xfc\xc0~u`\
-\xa5\xf3\'\x8ec\xd34\x8e\x1e9\x94\xcdxw\x96W\xda9\xdc\xef\t\xc1\x8d\r\xbc\
-\x89j\xb1\x13B\xf6\x8c\x95\xa6&\x8f\x14\n\x05\x99\x9ba\xd78\xdcw1\xc8cZk\xed\
-\xd0\xdb\'GX\xaa0nX\xf7\xcew\xc6\xe3\x9a\xcc8\xc6\xfa\xbe\xe0\xeadF{U\xb2S\
-\xc5]\x15M\xc6\x98i\x1a?\xf9\xf1\x8f\x10Q\xbc\xe8L\x08\xd9\xb3g\xec\xb9\xe7\
-\xce\xfc\xcb\xeb\xbf\xb967\xf7\xc2w\xbf\xa3\xea\xb5\xe38\x7f\xf2\xc7?\x14)\
-\xf4D\xc9C\x87\x0f\xbe\xfe\xc6\x9b++\xab\x17.^\xfa\xde\x8b\xdfU=\x00\x9d\x85\
-\x0f\x1e\x9ax\xe3\x8d\xdf\xae\xac\xae\xce\\\xbe\xf2\xcc\xe9\xa7\xef\xd9N\xa1\
-,\xeb\xeb\x95\x8f?\xf9\x0c\x00\x9e\xff\xces\xc7\xa6&\xc5\x83\xf6\xbe\xbd{\
-\xf6\xed\xdb\x93\xcf\xe5\x1d\xc7f\x8cMM\x1e-\x95F\xc5+H\x94R\xc3 \xd3\xa7\
-\x9eXX\\\xbcukin\xee\xfa\xb1\xa9\xc9\xa3G\x0e\x8bP\x90;\xcb\xe5l\xc6;u\xf2\
-\x84|\xbfQ=\xa9\x8c\xc8\\_\xaf\x08e\x17\'%\x84\x18\x86\xb1w\xcf\xd8\xe1C\x87\
-\xfe\xe5\x8d\xdf\xac\xac\xac\x9e{\xef\xfdD\x7f\xb3\xd9\xec\x1f\xff\xe8\x07\
-\x8e\xe3\x88\xc2\xc3\xc3\xc3\xaf\xfe\xcf\xff\x17\x00\xe6\xe7\x17\xf3\xf9|W}\
-\xe1\x9cc\xfb\xd9e\xf2\xe8\x91\xef>\xffm1V\xa2\x83##\xc3\x7fx\xe7\xdc\xc5K\
-\x1f\x9f8>\xa5\xeey\xcf\x95_P5\xcc7\xe9\xfc\xbdF\xb5R\xf1\x85\xb2?\xff\x9d\
-\xe7&\x8f\x1e\x11\x82[,\x16\xc6J\xa3\xef\x9d\xffp\xbdR\x01\x00l;\xeb\xdf\xfe\
-\xc3\xbb\x00\xf0\xdd\xe7\xbf-\x86"\x8e\xe3t*\xf5\xe2\x0b\xcf\x87a8w\xfd\xc6g\
-3W\x9ez\xf2\x14\xe7\x9c\xb5\x97\xc7\x9f\x9c>\xf5\x9do?+\xe3\xacep^bN\x12-\
-\x19\x1e\x1e\xf2\xbc\xf4\xddu\x7f\xbdRi6\xbbJ\xbc\xe8T;\x10E\xcd.\xd06\xe1\
-\x0b\x85\xc2Xi\xf4\xc0\xfe}\xe9t:\x91\x9c\xb2\xff\x80h\x1e9\xb1\xff\x950\xb4\
-\xc5\x1b\xa4]\x7f0b\xb9\xe9\xc2@\xa9}\xe3\xcau\xb9X\xd4;\xfc\xd1\xe8\xff\x82\
-\xeb\xcev\xe4Iqg\x8c\t\xeb&\x8ec\x91\xa5(\x8a\xa2\xb1\xd2(\x00\x84a$\x9e\xa0\
-\xe5\xc3\xf8\xd1#\x87\xb2\xd9L\xa4`\x10\xf2\xd4\xf4)\x00\xb8\xf2\xf9Uql\xff\
-\xc2\xd3\xa7\x9e\x00\x80\xd9ks\x9d\xedi\xdd\xf0\xed\x1fF4/\x8e\xe3\xd9k\xd7\
-\x00`h\xa8x\xe8\xe0D\xdc\x86R:T,Z\x96)\x93\x97\xa5S)\xf1\'q\xae8\x8e\xf7\xee\
-\x19\x03\x00\xc6Z+xq\x1c\x0b\xd3\x12\xdba\x7f\x1b+\xaa\x9bOJ)\xbd67\x07\x00\
-\xc3CC\x87\x0f\x1d\xdc\xd4\x05\x83<\xf5\xe44\x00\xcc\xcd]\x97\xe7\x12\xc7\
-\x1e9|\x08\x11e\xc9T\xca\xdd\xbfo\x1f\x00\x04a\x17\xc1R\x05N|s\xea\xd4\x13r\
-\xfc\x05\x93G\x8f\x8c\x8c\x0c\x03\xc0\x97\xb3s\xa2\xfd\xadc\x95eR\xd1w)\x9a\
-\xed\x95\x8c\x9e)My;\x1d\x90\x1c\xd5\xc3\x87\x0e\xaa\xf9 \r\xc3\x98<zX-|gy\
-\xb9Z\xade\xb3\x19QR4R\x94?tp\x02\x00\x16\x17\x17\xc5\xf7\x942\x00p\x1c\xe7\
-\xe9\xa7\xa6e.9Q\x95\x1a\xd2\'\xdf\xa8\x94\x96\xb5\xeb\xba{\xf7\x94\x8e\x1e>\
-81~`h\xa8\xe0&\x13\xd2&\xd7K%\xae\xeb\x0e\x0f\x0f\x1d9|\xe8\xc4\xb1\xc9\x03\
-\xfb\xf7\xa5R\xa9T*\x95\xc8\x97\xa9\xf5};\x834\xa0\r%\xb5o\x8fb\x99\xcdo\x90\
-\xf6\x82\x05k\xac\xbd\x7fr\x9fH\xf9T~_\xff\x17\\w\xaa\xe5\x0e\x1d\x0f\xc8\
-\xcdfp\xf3\xab\xaf\xee,\x97\xd7\xd7\xd7\xeb\xf5M\xdb\xdfDQ\xa4\xc6\xb4\x8c\
-\x8c\x8c\xc8\x80th\xbf[q\xa0\xfd\xdc=\xbf\xb002<\xdc\xa70\xe7\\\xbe\xa2r\xe7\
-\xce\xb2P.\xd9$\xbe\xf9\xedj\xf1Z9\xa5\xf4\xd6\xd2m\x008\xb0\x7f\xbf\x88\xa8\
-I\x18\xa7\xb2#++\xab\xf3\x0b\x0bw\xefV\x96\xcb\xe5\xcd\x05\xb8\x92\x97\xa3\
-\x15\x9a",t\xe1\x9dP\x1fM\xc4\n\'c\xac\xbc\xb2\n\x00\xfb\xf7\xef\xa3\xed\x08\
-q\xd2~]P\xf8\x19\xc2(\xbasg9\x97\xcb\xca\xfe\xe6\xf39\xd9_\xd2z\xf5\x91\xcb\
-\xd1\xee\x94\x18\xd9e\x00(\x8d\x8e\x98\x86!\x9f\x03H;\xf1\xd3\xc1\xf1\xf1\
-\xd5\xd5\xb5\xc5[\xb7\x8e\x1f\x9b\x945pe=\xb6\xa5\xa1\x1c\x01\x80\xb1v>\x96\
-\xcd\x1ev\x15\xd1Z\xce\xb9\x1c\xd5V \xbc\x92cg\xef\xdeV\x1aU\xcey\x1c\xc7b}8\
-\n\xa3\x7f}\xeb\xac\xac\x87\x00!\x04\xa28\x06\x80[K\xb7\xd5\xeb\xa4X,\x08\
-\x8b\x01\x15\xe4\xb0$\x12\xc8\x88\x0bC\xf4\xc2u\xdd\x92\xeb\x8e\x0c\x17\x19\
-\xe7A\x10\xd4\xeb\xcd \x0c)\xa5\x9c3\xe4\xadH\'\x83\x18\xc4 \x96i\xa6Rn6\x93\
-\xc9d<\x99\xfdX\xec" \xc4\xfd\x01\xf2\xc3h\x1e\t\xb1\x7f\x038\x07\x02\xbc\
-\xf7\x1b\xa4\xa6\x93\x19,\xb5/\xc6\x95\x96\xd5\xd8?R>U\xb8\xc7\xb6|;X\xdcA\
-\xb1\x1c\xc30\xfc\xcdo\xff\xb5^\xaf\xdb\xb6],\x16\x0e\x1f:(T\xef\xd3\xcf.C;\
-\xdf\x9e\xba\xa2e\xb4\xb7\xe9\x10\x8a\xb0Y\x8e[\x86d\x9f\xc2\x8aBq\xd5\x8b\
-\xc0\xdb\xa9eQ\x11wy\x94\xf8\x86t{\xcdZT~\xf9\xca\xd5O>\xfd\x0c\x00D\x172\
-\x19\x8f\x10\xb2\\^Y^.\x8b\xa3I+\xf5\xc7\xa6wL\x94\xa0\x9d\xe4I\xe5\xf3\x87\
-\\\t\xec\xeco3h\xa6\xd3\xa9\xcd\xcb}\x1b\x9e\x07Ji;\x10f\x93\xc0%J\xaac%\x87\
-K\x88\xaca\x18\x8c3\x00@\x8e\xea4\x99\xb0\xdc\xf9F\xec\xa3L\xb6\xd53\xf0Qv\
-\xa1\xfd\xa7M\xaf\xc8\xcb\xd6\xca\xd1\x88\xda\x9bBDq\xbc\xbc\\\x86\x1e\xb4\
-\x93E\xb3v\x95\xa0V+\x95\x9d\xb4\xf3\xae\x88\x18\x1b\xa2$\xd6\x97\xb9\xb1\
-\xc4\x04\xe0\xd8v\xa1\x1d\x11\xd4\x15y\xac\x9aP>\x95JIo\xbb\x96\xf5\xed\x0f\
-\x0b\xee\xd2f\x19\x00\x00\xfb\xbdA\x9a)\x1d\x87\x01~\xcd\xd8\xbf\xc9\xe3V\
-\xf0\x15\xa5\xd8s7\xed\xfc\x81{F\xca\xeflq\x87\xf6\x9d\xff\xc1\x87\x17\xea\
-\xf5\xfa\xd8X\xe9\xdb\xcf>c\xb6\xf7O0\x0cC\x88;\xdb\x9c!\xb2\xd9l\x8a\xb4\
-\x85mC\x189\xe72\x9a-\x9dJ\xa9Z\xdcY\x981\xd6h\x88\xd4N\xe0\xa57\x854Iq\xef4\
-\xa23\x9e\x07\x00\x8dFC<hwF.\xaf\xae\xad\te\xff\xce\xb7\x9f\xdd\xb7w\x8f\x90\
-\'!\x94\xcb\xcbeB6t\xd30\x88\xec\xa00\xae\xf9\xe6HA)\xee\xc5Bay\xb9\xdch4d\
-\x02U\xf9`Q\xab\x89\xd4\x83\x90\xcf\xe5\xd4\xfe\x1a\x84\xa8\x99\xd48\xe7b\
-\x19S\x95K\x15U\xf8\x1a\xcd\xa6\xa5d\xc9\x1750\xc6\xaa\xd5\x1a\x00x^Z8LZ?\\{\
-\xbe\x90\x12/\\C\x9c\xb7\xa6\x1f\xec\xd8\xabS\x9e_\xfej\xedQm\xcaL\xc8\xb2\
-\xa9\xf27\x12n\x19qU\x94FG^|\xe1yy\xe5H\xa5\x96\xc3\xb8\xb1zA@\xa6ZK\xfcRD\
-\xd9\'D\xb5\xe2\x13\xe9\x97\xf9\xa6H\xc7.\x83&\x1d;B\xd9\xd5\x84\xfe\x83$\
-\x94\xd7l\x0f0\xf6\xe7\xc4\xfa)\xa5}\xde \xdd\xd7\xf9\x06i\'\x9c6h\xb5\x15L\
-\xc99P\xd6\xbd6\xc3Jy\xc3\x87\xefY\xdbN\xf5\xb9K]\x10\xff~5\xbf\x00\x00\'\
-\x8eMY\xca\xb6\xc5\xabkkjay\xec\xe2\xad%K\xd9\x01G\x14\x98_X\x04\x80L\xc6K\
-\xa7S\xea\xfad\xd7\xc2\x0b\x8b\x8b\x00\x90\xf1\xbct:\xa5\xd6\xbc\xa1V\xb8!\
-\xee\xe2V/\x16\x0b\x00\xb0\xb0x\x8bs.w\\2\x95\xec\xd8\xb7o/\x03\xc0\xd8X\xe9\
-\xc0\xfe}\xd2L\xe6\x9c\xcb\xb7\x1f\xd5\xb4|\x00@\xda\xf9\xb96\xce\xab\x9cT\
-\x8c\xcc\x90rR\x99\\W\xedo\xb1X\x10R.\xbb\xa0\xa6\xe1\x15%\xa1e\xb9\xf7\xfc-\
-\x08!by\xb0^o\xac\xac\xae&\xa2\xf7\xc20\x14\x99v\xf2\xf9\x9c:\x03U\xfd\x9a\
-\xda\xcdZ\xad~w]\xf4\xb4\xfd\xb3\xb6\xbb\xd39\xaf\xc8\x87\x95ba\xa3\x83\x89}\
-6\x96n\xb7\xf6I`\x9c3\xc6\x86\x87\x86\x00\xa0\xbc\xb2\xda>\xcbF\xcd\xd5Z-\
-\x0cCuJ\x86\xb6jK\x97\xfa\xa6\xce*\xe6\xb60\xb4\xe5\x16t\x12\xcf\xf3\xd2\xe9\
-\xb4X\x17\x95\xbb;\xc9m\xfc\xe4Q\x9e\xe7%\x0e\x91\t\xee\xb5\xb2\xef\x08hc\
-\x99Gu }\xc3\x1f\r+]<8Hmq\xe5\xba\x8cM\x88Y\xcf{\xce\x1b><\x88\xef~G\x8a;\
-\xb6\xc3\x9c\xa5\xc5\xea86\x00\xc8\xfb\x16\x11\xc30\x14\xd1)\xd0\xe1/\xbesg\
-\xf9\x83\x0f/\x08\xbd\x13Z\xf9\xc5\x97\xb33\x97\xaf\x00\xc0\xe1C\x07\xf9\xe6\
-u\xbc\xce\xc2_\xce^\x9b\xb9\xfc9\x00\x1c:4\xd1i\x9a\xb5\xf5*\xf1\xbfx\xe4\
-\xf0\xa1L&\x13E\xd1\xff\xfe\xdd\xef\xc5\xd3\x80\x90\xbf\x0f?\xbax\xf5\x8b/\
-\xa5\x95W\xaf\xd7\x85\x01+\x8e\xba\xf9\xd5\xfc\xe2\xad%\x00\x10\x96\xbb\xd0\
-\x1aB\x0c\x00X\xbb\xbb.\xbc\x1c\x8a\xbeo\x9cTTx\xe0\xc0\xfe\xa1\xa1b\x14E\
-\xff\xfa\xd6\xd9 \x08\xe5I\xaf~1\xfb\xd9\xcc\x15\x008~lJ\x1c"\x07\xc7\xdc\
-\x9cY^\xd4\'\xbb\xd2\xf9[\xb4\xec\xf6\xf6\x0e\x9f\x7fx\xe7\xbdryE*l\xbd\xdex\
-\xe7\xdd\xf3q\x1c{\x9ew\xe8\xe0\x84\xe8\xd4\x9e\xb1\x12\x00|\xfa\xd9L\x14E\
-\xe2\x9b0\x8a\xce\x7f\xf0a\xbb\xf1r\xdc\xfa]\x00\xadQ=r\xc8q\xecD\x07\r\xc3\
-\x98\x9b\xbb!\xc3\xed\xc5E\x92J\xb9\xe2]\xaas\xef}\xa0\xa6p\xb9\xbb\xbe\xfe\
-\xc1\x87\x17\xfe\xf0\xee{\xeb\xeb\x15)\xdc\x00@\x80$\xccg9D\xaa\xbe[\xed\xcd\
-f\xa5Rg\xb3\xd9l6+>\xc8\x7fU\xb2\x9b\x91\xb2\xaezc\xb4\xb2\xef\x0c\x90\xcb\
-\xdd\x91\x18\xeb\xedB\x19,G\x18\x0b\xd7Y\xb3ue\xf6YG\x1d|[\xbe\x9d\xea\x96\
-\x91\x16\x9f\xf0\xd8\x1e?v\xec\xb3\x99\xcb\x9f\xcd\\\xa9\xd5\xea\x9e\x97\x0e\
-\xa3hi\xe9\xb6m\xdb\xc5Ba\xbdRI\xe8\xc4\xf0\xf0\xd0\xd5/\xbe\xbcq\xe3\xe6\
-\xf0\xc80r^\xad\xd5\xeb\xf5:\x00\x1c>t\xf0\xd8\xd4\xa4\\\x0c\xec_\xf8\xd0\
-\xc1\x89\xa9\xc9\xa3\xd0\xcd#\xdc\xd9N\xf1\xf9{/<\xff\xd6\xd9?\xac\xae\xae\
-\xfd\xe2\xd5\xd7\xc4\xfa\xad\xefW\xab\xd5*\x00\xa4\\\xf7\xf0\xe1\x83\x9f\x7f\
-~\xb5^o\xbcu\xf6\x0fB\x07\xd7+\xfe\xd2\xd2\xed#\x87\x0f]\xbfqS\xea\x8ba\x18{\
-\xc6J3\x97\xafDQ\xf4\xcf\xff\xeb\xf5l6[\xadV\xff\xec\xa5\x9f\xf4j\xc0\x8b/<\
-\xff\xfb\xb3\xef\xac\xae\xae\xfd?\xaf\xfd\xd3\xbe}{\xd5.L\x9f:\xb9\x7f\xdf^\
-\xd8p\xe5\x03(;\xb5b\xf2\xbd\x9b{\x93\xcdf\xa2(~\xfd7\xbf\x1d\x19\x19vl\x9b#\
-\xde\xb9\xb3\x0c\x00\xb6m?w\xa6\xf5^\x15"\x1e9r\xf8\xcer\xb9Z\xab\xbd\xfa?\
-\xffidx\x88#\xae\xad\xdd\x05\x80\x8c\xe7\xd5\x1b\x8d\xfe\xa7\xe8\x1c\xd5\x17\
-\xbf\xfb\xfc\xbb\xef\xbd/;(<N\xf5z\xa3\x90\xcfW|_\x96\x07\x80\x13\xc7\xa7\
-\xfcj\xb5R\xf1\xdf}\xef\xfdb\xb1 6,\x14B_,\x16\xb2\xd9\x8c\xf4\xaa\x03\x001\
-\x88\x1a\t\xd3\xd9\x06\xf1\xa5\x98,\xe5\xbc+\x97X7\x16B:\xa6)\xd5\'\xa3\xc6\
-\xdbt\x9dH4\xdb\x19Z_B\xda\x04\x02\x88\xd8\xeb\rRb\xb9\xea\x06\x1a\xbd\xc1\
-\xb8\xd2z\x07\xb3\xdf:*@fdj\x10\xdf=\xechqW-\xf7cSG\x11\xf1\x8b/go\xdclM\xa4\
-\x07\x0f\x8e\x9f<q\xfc\xc2\xa5O\xd4C\xc4\x87o=\xfd\x94\xef\xfb\x9f~vyi\xe9\
-\xb6\xf8\xc6\xb6\xed\xe3\xc7&\x8f\x1f\x9bB\xe5-\xc4>\x85\x8fM\x1d\x9d\x9a<:\
-\xc8\x1d\xa8\x96)\x14\xf2?\xfa\xe1\xf7?\xf9tfi\xe9\xf6\xe2\xe2-\xf1e6\x9b=\
-\xf9\xc4\xf1\x91\x91a\xce\xf9\xf7^\xfc\xee\xcc\xe5+\xcb\xe5\x95\xcbW\xae\x02\
-\x80\xe7y\xcf=\xfbL*\xe5^\xbfqS\x9a\xc8\xa2\x9e\'N\x1c\xff\xfc\xea\x17\xd5jM\
-\xb8\xb3\xbb\x9eTt!\xe3y/\xff\xe9\x9f|\xf2\xe9\xcc\x17_\xce\xca.d2\xde\xf4\
-\xa9\x93\x07\xda1?\xaa~\x11E_HGTOW\xa4~e2\x99\x1f\xfc\x9b\xd3\x97>\xfe\xe4V\
-\xfbD\x00P*\x8d\x9e:y\xa2X(Hs{\xff\xbe\xbd\xcf\x9dy\xe6\x93\xcff\xa2(\x12\
-\xce\x93Ri\xf4\xe4\x13\xc7\xaf|\xfe\xc5\x80\xe2\xaev\xb0T\x1a\x95\xa3*;x\xf0\
-\xe0\xf8\x89cSo\xfe\xef\xb7\xa0\xfd\xd0\x03\x00\x8e\xe3|\xf7;\xcf]\x9b\xbbq\
-\xf3\xab\xf9\xf5\xf5\xd6^\xe5\x8em\x8f\x8f\x1fx\xf2\xc9S\x8em\xb7\x9f\x9f6Y\
-\xe8}~e\xd2\x8e\x89\x84\xf6J2\xaak\x15\n\x89\xa3\xd4\xa9\xda\xb8\xcf\x84\xfe\
-\x9am\x02r\x1a\xb7\xfd\xe3\x94\xf6~\x83t\xe8\xd0 .\x14Z\xbf\xc3\xa3\x96-\xd2\
-\xe7!\xc0\xcd\xed\xb5R\xfd\x96\xe8U\x06\xba{\xb7\x1b\x88(\x82\xbeE@\xba\x9a%\
-\xaa\\^A\xc4|>\'\xec2\xf9\x8e\x8f0B\x7f\xfd/\xbf\x01\x80\x1f|\xff{\xe3\x07\
-\xf6\x13Bn\xddZb\x9c[\xa6Y(\xe4\xa5\x96\x89\xf57\xce\xf9\xffz\xfd\xcd^\x85\
-\x13\x8d\x11f\x97\xfc_a\xfb\xab\xbb\xc9\xf0\xcd\xfbn3\xc6\xee\xde]G\xc4L\xc6\
-K\xa7\xd3r\'eQ\xbe\xdeh4\xea\r\xd32\x0b\xf9\xbc\xd1\xde2\x8d\xb4\xb7\x1c\x13\
-a<bia}\xbd\x02\x84\x0c\x15\x0b\x84\x10\xd1A\xe9K\x91\x93\x9fj\x12.-\xddf\x9c\
-{\xe9\xb4\xe7\xa5U\x7f\x8e8\xb5\xbao\xb8\x08\x1f\x8c\xa2\x88sn\x9a\xa6\xdc<\
-\xdaR\xb6-\x16\x83\x1f\x86\xa1\xf0\xae\xc8\rHM\xd3\x0c\xc3pee\x951V,\x16\xe4\
-R\xa7\x1c\x07\xb9r\xb0\\.#G\x11\xf8\'\xbeW[E\x94u\xcb^\xa3*;h\x9af\x1c\xc7++\
-\xab\x94\xd1\\6+\x05W\xf1YmD\xd4\x18\x86Q^Y%\x04\x0cb\xec\xdd\xbb\'Q\xa1\xd9\
-\xde/T\x86\x99\x0fxM\xaa\xff&d]\xbd\xcb\xa4\x82\xab\x93\x87\x96\xf5\x1dG\xec\
-\xdf\x88\xfd\xaf\x80\x00r\x0c\xa2\x9e\x89_\n\xe3\xcf\t?j\x1f\x90\xd3\xe0\xce\
-\x87\xc8B\x00@\x84 \xc6\xaeS\x051\xac\xc2\xf8s\x83e\x81\x07\xd8\xb9\x96\xbbD\
-\xbd\x1f\x08!\xa5\xd2(\xb4\xef\xa5\xc4\x9d\xa3F\xb0\x88\xb7XGF\x86\xc5\x1d(\
-\xfd\x12\xf2\x86\x94wcgaP\xb6\x00N\x1c\x0emU\x82\xcd2\x04J\xd8\x8cP1\x91\xc9\
-\xa4\xf3\xc9\x9d\x10\x92\xf1<\x11\x04"\xa6\ri\xdc\xc9s\x89\x0f\x96e\xed\xd93\
-&E_\xaa\xa1jq\x8b\x92r\x19sttD:\x9dT\xe1\x93\x16\xa8\xb1\xd9lOHdb\xa8\x13\
-\x06\xbe\x9cA\xc5T!\xcf%\xeb\x94-\x91*?:2"\xdd\x17\xf2\x8c\xb2Ur\xe9\xb8\xcf\
-\xa8\xca\x0e\x8a\x02##\xc3b\x05[\x8eF".S\xb6\x7f\xac4\xaa\x8e\xadz\xcd\xa8_\
-\xde\xefu\xa8V\x05\x9b5\xbdky\xd0\x82\xbe3A\x1e\xd3Z\xeb\xe1;f\xc9L\xcd\x12o\
-\xe8\xc8=\x95\x1dDR\x1a\xd6\x8aZ\xa6\xac\xbb\xb2\x03@\xba81\xb8\xb2\xc3\xce\
-\x15w\xa2l\xa5h\xb6\xf7}\x87\x8e{FJ\xa1z\xac\xb0\xcd\x8d\xf6\xbe0\xa4\x1d\
-\x17\x01m\xb5\xbdg\xe1\x84I\x98\x10wQRU\x169\xbbH%\xda\xe4\ti\xbfX\x94\xd0P\
-\xa1w\xedE\xd4\x96^K\r\x15\x82%\xf57\xd1\xb6\xc4I\xd5\xf1!\x84\xc8`\x0cP\x8c\
-bU\xec\xe4\xf0\x12e\xa3\xd1\xce\x9f@\xc6\x93\xa8UI\xd9U\x7f#\xd9$9\xd3H\xdbV\
-\xceI\t\x07\xf7=GU=\xa3\x1c\x13l\x078\xaam\x93\xf3\x8a\x9c\xdbd\x1fe\xd7\xc4\
-\x8f"\x86\xf4\xc1\xf4]\x1d\x99\xc4\x07\xcd.#\xf6\xbfBN\x81\x88\xc8\xdd\x9e\
-\xfb\xde\xf5I\xfc"A\x16\xd2\xea\x82\xf8\xcc\xb1w\xf8\xa3\x9dN\x15\x06\xf1\
-\xdd+\r\xb8\xaf\xd2\xdb\x07\xa2\x84\xa3A[\xc2\xe4\x9f\x04\xe2\x7f\xa5\x1d\
-\xd7\xcb\xf0\x94+Z\xd2*\xbfga\xd5]\xa0\xda\xf2\xd0M\x86\xe4_\xd5@@\xb5N\xe9\
-\xaeMt\x01\xda\xcb\x9b2\x04\x13:\x0cm\xa9\x9b\x86\xb2\xe3\xb3\xfc\x06:b\xfc\
-\xa5\x13CUd\xd5\xa5#EM\xcev]\xd7\x15\x89\x12."\x07Y\xfe)Q@\xb6_|\xa9\x8e\x83\
-,\x9f8\xfb\x80S\xa6:[@[\xa0\xe5\xd9et\x90\xe8\x85:\x14j\xf3\xd4\x876\xe9\xe7\
-I\\E\x1a\xcd\x06\xc8\xc5\xa6\xa6\x00\x10\xd3>f\xfb\xe1\x81\xdeZ\xaa\xdc@\xa4\
-\xed\xdaz\xbc\x03\x05\x90\xe9\x9d\xfd\xb1\x17;^\xdc\xa1\xfd\x9a{Wq\x97F\xa2j\
-xZ\xa6i\xdb\xb6\x14q\xa9t\xd2\xa6\x96B\xdf\xabp\xa7\xe8\xa8* \x85I\x1c\x92\
-\x90!9\x19\xa8\xb3H\xa7\xdf@\xd6\xa0\xca\x998Jh1\xe9p \x90\xcd\xebr"\xcfA\
-\xc2b\x15\x87$\xe6\'i\xfe\xab\xa2&\xeb\xe9<\x11\xb4\xcdvDt\x1cG\x18\xbc\x89\
-\'$\xf9\x03%\xe6\x1b\xe96I\xfc^\xaa\x11\xadV\x92\x18UU\xf1\xe5ht\x0e\x1dQ\
-\x1e8\xc8\xe6\xf7\xbc:\x87B\x96\x91\x95\xf4\t\x92\xd1hhc\x19Y$\xccv\xce\xbb\
-\x9b\xedN\xa6d\xa7\xbb\xec\xc0\x95\x80GU\xda\x90/d\xf4\x0c\x7f\xb4\xbda\'[\
-\xba\xdfv\xeeHq\x97\xa6+\xb4\xefd\xf9\x8c\x0f\x8a\xb8K\x1f\x88H\x12"\x0f7\
-\x15\xbdNh\x8a\xea\xfc\xedSX\x15\xf7>\x8d\x94\xea uD\xb5\x1c\xc9f\x17s\xd7.$\
-&\t\xdc\xbcx\xab\xd6\x908JN0Ry\xd5\x93\xf6\x9a\x9fTA\xef\xfc\xac~#\xfe\x15\
-\xeb\xab\xea)T\xc9\xee\x1c.9\x0e]{\xaa\x9e\x02;\xf2\x1c\x0c2\xaa\x9dC\xa7Z\
-\xe5F;\x9aE=<1\xc5\xca?\xa9\'\xd2h6@&\x83d\xfa\xe6k<2HeQeNf\xba\xe8\x9dFf\
-\xd0\xda\x12\xecHq\x07E\xdf\xa5\xb8\xc0\xe6uTB\x88\xf8R<h\x8bbcc%B\x88\xe7y\
-\xea\xd6e\xa4\xed\xf2N({\x9f\xc2\xf2\xb6\xef/\xee\xf23\xb6\x17B\x13:\x9b\x90\
-\x95\xc4s@\xa2\x8cZ\x8fz\x16\xb2Y\xdc\xd5\xb3\xcb\xae\t\xd4\xf6\'\x9a\x978\n\
-7;\xb2\xba*\x9d\xd1^\'\xc0\xcd\xc8C\xe4\x89\x04\xb2N\xb5=\xd01\x91\x0c\xf2\
-\xbbw\x8e*(\xf3\x01\xd9\x0c\xf4\x16\xf7\xae\xbfBb\xfc\xfb\xb7G\xf3\xb8\x11\
-\xd7n\x89\xd8v\xc6\x90\xf7\xca\xd78\xd8^K\xac\xb9"6\xbf\x06\x00\xda\xe7\x1d\
-\xa8\xfc>\xcb\xcd=@Swd(\xa4D\xb5\x16\x13w&\xb4\xd7B\xd5\x94\xb6\xa6\x92\xc7C\
-\xd58B\x88\x08\xfe\x1b\xb0\xf0\x03\xb76\xa1\xcb\xf2_\xf9\xbd*\xee]\xcf%;\xab\
-\xfe\xa9\xb3\x18Q\x02f:\x07\xe7\xc1\xba\x908J\xedK\xa2\xfd\x89\x13%&\xb0\xce\
-b\x0f\xdc\xa4\xfb\xaa\xf0~\x87B+\xbb&\x01\xf28\xb8\xf3\x11\xb2\x08\x00\x82\
-\x88w\xd5Nb\xda\xc5\xf1o\xdf{G\x0e\xe4\xcd;\x1f!m@\xff\xf0G\xd3)N<g\x98\xf7\
-\xde\xdf\xa3\x93\x9dj\xb9\x0b\x06\xb9?I\xdb\x11\x81\x88r-Q\xfa\x97\x13%\x07,\
-\xfcuZ{\xbf\x7f\xfa\x9a%\x1f\x9eB\xf5\x9f]\xb6\xac=\x0fc\xe84\x9a\xae\xd0\
-\xda\xa2\xf0\xb6S\xd6\xd3*N\x0f\xb6\xd7\x12\xad\xdd\x12\xca\x0e}\xc3\x1f\xbd\
-\xa1\x83\x0f\xa6\xec\xb0\xd3\xc5\xfd\x9eH\xb1\x96\xaf\xc0\xf4Y+\xbb\xaf\xc2\
-\x1a\x8d\xe6\xb1\x02Y\xd8\xdaH\x0f\x81\xf6\xf0\x8f\x1bV*5\xd0^K\x18\xb7\xf7\
-\xe4\xeb\x13\xfeh:\x197\xbf\xff\xc1Z\x0b\xbb^\xdc\xe5\x9a\x9b\xfa\xbf\xbd\
-\x96\xcb\xee\xab\xb0F\xa3y\xac\x88\xab_!g"H\xa6\xa7\xd9>X\xb2\x01\xd6\\E\xd6\
-\xda\xda\xac_\xf8\xe3\xc8\xe4 \xef@\xf5b7\x8b\xbb\x10e\xb3\x9d\xda\t\xdb\xa1\
-\x84\xbd\xcc\xf6\xc1\x0bk4\x9a\xc7\nN\x1b2f\xb1Wl\xbb\xe9fS\xb9\xbd\xf7\xae\
-\x0b\xb9\xcc\x11\xc6x\xcfUY;3j{\xc3\xdd\xfe2(\xbbY\xdcA\x89\xd9P\xa3\xf1zY\
-\xe2\xf7UX\xa3\xd1<>\xc4\xfeM\xb1\x91\x1ec=c\xdb\x07|k\x89\xd6nq\xe9m\xa7=\n\
-\x11#\xd3w\xf3\xebA\xd8\xcd\xe2.W\xf0:C\x0c\xbffa\x8dF\xf3\xf8\xc0#\xbf\x95i\
-\xbd\xf7\x8e\x1c\xb67<P\xb2\x01\x1e\xc7\xd5\x9b\xe2\xb3\x08\x7f\xecJ\xba0>H0\
-e\x7fv\xb3\xb8C\xb7\x90m\xe8\xad\xd7\xf7UX\xa3\xd1<&\xc4\xfeW"\xa5/e\xd8\xeb\
-%Ro\xe8\xf0`U\xdd@N\x01\x00\x11h\x8f\xed\x12\x0c\xcbM\x0f\r\xb4sS\x7fv\xb9\
-\xb8\xc3}\xaa\xb3\x96r\x8dF\xa3\xc2\x82\xbb,X\x03!\xc7\xb4\xbbC\xc6\xcd\xed\
-\x19$\xcd:\x8f\xeb\xb4\xde\xdar\xa0\xcf[K\xe9\xa1\x81v\xd1\xbb\';r\x9b=\x8dF\
-\xa3\xd9\x1aZ^\x14\xd2\xdaC\xa3\x13b\x98\xe9\x01\xcd\xf6\xcau@\xf16u\xbf]\
-\xf4\x06Z\x95\x1d\x00-\xee\x1a\x8dF\xd3\x1d\xd6,\xf3\xd0\x97\xfb_\xf70\xdb\
-\xf7\x99v\xfa\xdeU\x05wY\xb0\xda\xfa\xdc+\x98\x92\x90\xcc\xc8\xe4 \xab\xb2\
-\x83\xa0\xc5]\xa3\xd1h\xba\x82r\xff\xeb^\x8b\x9f\xc4t\x06\xf3\x8fc\xec\xb7\
-\xb7H\xed\xedmw2%+Ux\x90\x96vC\x8b\xbbF\xa3\xd1t\x81\xd6o\xf3\xb8.\xf6\xbf\
-\xeee\xb6\xa7\x8b\xe3\x83\xa4\x07\xa0\xf5\xdb<\xaa\xb6>\xf7t\xefX\x99\x91\
-\xc9\xaf\xd3\xe0\x04Z\xdc5\x1a\x8d&\t\xf28\xae\xb6\xcc\xf6\x98BW/\x8a\xe9dR\
-\xf9{\'\x1b@\x16\xc5\xfe\r\xf1\xb9O\xb2\x81\xf4\xd0!\xc3r\x1f\xb0\xb9\xdd\
-\xd0\xe2\xae\xd1h4Ib\xff\x06\xd2P\xac\xa3v\xdfH\x8f\x90l\xe9\xc4 \xbb#E\x95k\
-"\x91$\xb4\x93\rtb\xa7\x8b\xe9\xe2\xc4\xd7jq\x07Z\xdc5\x1a\x8df\x13\x9c6h\
-\xfd\x0e@\xeb\xad\xa5\xaer\xecx\xa3\x03\x85?\x86\x15\xd6X\x06\x00\x02@Y\x8f \
-\x19B\xbco\xd4!#\xd0\xe2\xae\xd1h4\x9b\x88\xfd\x9b"f\x91\xf6\xca\xfd2\xe8\
-\xeeH\x18\xb5\xd3\xc8p\xe8\x19o\x93\xca=\xe0v\x1c\xfd\xd1\xe2\xae\xd1h4\x1bl\
-$\x1b\x80\x9e\xa9}S\xf9\xfd\x03\xed\xb5\xd4(\xf3\xa8\x02\xc2l\xa7\xd0k\x1du\
-\xc00\xf9\xfbE\x8b\xbbF\xa3\xd1l\x10\xfb7\xc5\xfai\xcf\xdc/\x84\xa4\x06I\xb3\
-\x8e,j\x87?\xf2\xdea\xf2\xe9\xa1C\x83l\xee\xf1\x00hq\xd7h4\x9a\x16,\\g\xc1]\
-\x00\xc0\xde^\x14;=4\x88\xd9\x1e\xd7\x16\x91\xb6\x93\xb63\xe8:M\x98\x8e7\xd8\
-\xe6\x1e\x0f\x82\x16w\x8dF\xa3i!3\xad\xf71\xdb\x07\xf1\xb6#\x0biu\x1e\x00\
-\x08\x00\xe7\xd0=\xde\x06\xc0\x1b\xfeZ\xdbq\xf4G\x8b\xbbF\xa3\xd1\x00\x00\
-\xd0\xc6\xb2x\xd5H\xbc\xb5\xd4\xb5\x8c\x9b\xdd3\xc8\xe2g\xec\xdfle\x7f\x04\
-\x88{\xc5\xdbdF\x9d\xcc\xc8\xd7ip\x7f\xb4\xb8k4\x1a\r\x00\xa0\xb0\xb5\x01\
-\x802\xe8\xbac\xf5\x809\xc2x\\\xa3\x8d\xdb \xd6Q9|\xbdx\x9b\x07g\xf7\xa7\xfc\
-\xd5h4\x9a{\xd2J6\x00\xc0{\'\x1bp\xf3\xfbM;\xd5\xaf\x12\xca(e\xdc\xbfA\x90\
-\x03\x10\xec\x13oS8`:\x99o\xa0\xdd\xbd\xd1\xe2\xae\xd1h\x1e{\x90\xc9d\x03\
-\x94u\xdf"\x95\x98\x8eW\xec\x99#\x8cQ\x1a\x06QH\t\x89W\x9ch\x15\x81\x10\x80\
-\x98A\xf7\xa4\xed\x84\x0c\x92\xb7\xe0k\xa2\xdd2\x1a\x8d\xe6q\'\xae-"\r\x01\
-\x80c\x8fd\x03\x00\xe9\xe2\x041\xed\xae\x87s\xc6\xd7\xcb\xe5 lzi\xf4\xe0\x0e\
- \'}\xc3\x1f\x9dL\xa9\xff\x13\xc07\x82\x16w\x8dF\xf3X\x83\x9c\xd2\xda\xa2\
-\xf8\x1c\xd3\xee1\x8b\x86\x9d\xea\x13\xdb\xde\xa8\xd5\xd6\xca\xcb<\xa0f|\x97\
-\x06>\x10\x03D\xf8c\x8f\xcd=\xbc\xaf\xbd\xf9\xf5 h\xb7\x8cF\xa3y\xac\x89\xab\
-\xf3\xc8b\x00`\x1c\x19\xefnkg\x86\'\xfb\xe4\x08C\xc0\xc2\xd0(\xd2 \\\x9f\'\
-\xc4 \x00\xacw\xf8c\xaa0\xb1\x05f;h\xcb]\xa3\xd1<\xce \x0bi}\t\xda1\x8b]\xcb\
-X\xa9\xbc\x93-\xf5\xa9\xc42\xadT~\xc4\xb5j\x04C\xb1\x8e\xda+\xfc\xd1\xb0S\
-\xdfx\xf6\xc7\x9e\xad\xda\x9a\xd3h4\x1a\xcd6$\xf6o\x02\xa7\xad-R{\xeck\xda?f\
-\xb1Y\xab\x87\x11\x02\xad8\xb8\x06`\x12\x00\xca\xb0\xd7\x16\xa9\xfd\x9f\x00\
-\xbeY\xb4\xb8k4\x9a\xc7\x14N\x1b\xb4\xb1\x0cb\xeb\xbb\x1e1\x8bN\xa6d\xa7\x87\
-z\xd5\x10\x06a\xa3\x11"\x18._!\x18#\x98\x1c!\xee\xb1\x1d\x87\x9d\x1e\xea\xff\
-\x04\xf0\xcd\xa2\xc5]\xa3\xd1<\xa6\xc4\x95\x1b\x80\x1c\x080\x8a\xbdb\x16\xd3\
-C\x87\xba\x1f\x8cP\xaf\xd5\xe3(\xe6`:F\xd3\x8c\xca\x08\x06!\x10Sd\x8c\x13\
-\xb1.K\xc8Fv\x01B\xbc\x91\xadXG\x95hq\xd7h4\x8f#<\xf2Y\xb0\n\xad\x1d\xab{$\
-\x1b\xc8\xed\xb3\xdcl\x97? \xd4k\xf5\xa0\x19\x12\xd36\rfE\x8b\x04\x11\x89!^\
-\x80\xb2\xd3\x05;]4\x80\xc7a5\x0e|\xa1\xf3nn\xef\xc3H\xda\xde\x07-\xee\x1a\
-\x8d\xe6q$\xf2o\x88$\x03\xb4\x87\xd9N\x0c\xcb\xeba\xb6W+~\x18\xc6\x86esF\x1d\
-R!\xac\x82\xc4$\x00\xf5\x80\xbb\xf9\x83\xf9=\x93\x00\x040L\x03m\xac|\xd5\xac\
-\xdc&\x96\xe3\r=\xdcd\x03\x9d\xe8h\x19\x8dF\xf3\xd8\xc1\x82\xbb<X\x07\xd2\
-\xcflO\x15\xc6\xbb\xeeX\xdd\xac7\x9aAlZ6c\xd4\xb6\r;\xbe\r\x84\x10\x80\x88b\
-\xcc\\@oc\xa6 \xe0zC\x80\xb67t\xf0!%m\xef\x83\xb6\xdc5\x1a\xcd\xe3\x06\xc6\
-\xfe\r\xf1\x89R\xec\x9e#\xccr\xd3\xc5\xf1\xce#\xeb~5\x08"\xcb2)\xa5\x8e\x9b\
-\xb6\xa2\x9b\xc0\x9b\x04\x0c\x00\xa8\x87h\x18N\xbd\xd6LGq\xc4 l\xc6\x801\x01\
-34FF\xf2]\xaaz\xd8h\xcb]\xa3\xd1<^\xd0F\x99GU \xc0yO\xb3\xdd+\x1e$F\xd2\xf6\
-\xadU\xaa\xcd &\x86\xc59\xda\xb6c\xa1O\xe2\x15\x04\x02\x04\x02\na\xcc9R\xcb\
-\xb5\xeb\x8df\x18\x04\x1c9\xe5\x06\x037=r h\x06\x0f\xbf[I\xb4\xe5\xae\xd1h\
-\x1e/d\xb2\x01\xca\xb0k\x8e0\xd3\xc9\xb8\xf9}\x89/\xabw+a\xccM\xd3\xa0\x94;\
-\x8e\x814\x8c\x9b\x8b\xae\x11\x03\x18\x88Po2\xcb\xb2mo$\x8cXpw\r\x10\x01\x90\
-\x00!\x86A\x88Q\xa3\xf1\xf8\xd1#\x84t\x8d\x90|Xhq\xd7h4\x8f\x11<\xaa\xf2\xb8\
-\x06\xf7\xd8 \xe9Hb\x83$\xffn%\x8c\xb8m\x19\x1c\x88m\x03\x8f\xa3\xb0y7gT\x01\
-\x0c \xd0\x088\x07\xb30z\xacY\xa7\xf5\xda]\xc30\x81\x00\x08\x8f>@kG\xd6(\xb6\
-\xdd-u\xbbkq\xd7h4\x8f\x11qu\x1e\x11\t@L\xbb\x9b\xedV\xaa\xe0dF\xe5\xff"\xe7\
-\xfe\xba\x1f3b\x1a\x18sp,0\x90E\x9c\xdaP!\x06\x00\x10\xcaXH\xcd\xfc\xc8d\x14\
-\x134m\'\x9d\xe6\x8c\x8bC\xdbU\x80i\x99\xa6\xbd\xd5b\xab\xc5]\xa3\xd1<.\xd0f\
-\x996W\x08\x00\xed\x95#\xcc03\xa3\xc76\xca\xc7q\xbd\xda\x88)\x02p4L\xd7"4hR\
-\x00\xdbA\x88\x1a\x04\x00\x91S\x92\xf7\x8a{\x83\x08\x18gn\xcaK\xb9V\xadQg\
-\x8c\x03\xe7\xad*\rc\xa4T2\x8c\xad^\xe0\xd4\xe2\xae\xd1h\x1e\x0f\x90\xc5\x95\
-\x1b\x00\xd0o\x83\xa4\xfc~\xf9\xd6R\x14\x84\xd5J\x8d\x986!`\x98\x8em\xf2\xb0\
-Q3m7?\\\\\x9b\xff(c! \x82=b\x18{\x1a!g,v\xbd\xbc\xe3Xa}\x1dXL\x0c\xe2\xa4\
-\xd2\x04\xc0yg\xfe\x95\x00\x00\x1cPIDATq\xdcl>\xe78[\x1d\x07\tZ\xdc5\x1a\xcd\
-cB\\\xbf\xcdi\x93\x00\xc4\xac\xc7[K\x96+\xf7Z\n\x1a\xcdf3DB\x90S\xc3r,B\xc3z\
-\xc3J\xa5\xf2CC\xcd\xda\x9a\xc1\x1a\xc4\x02L\xed\x8bx1\x88\x90\xb3\xd8\xcb\r\
-\x19@\xa3\xba\xbf\xbez\xdb@\x7fhd\xac\xb8\xe701\x8c-^DU\xd1\xa1\x90\x1a\x8df\
-\xf7\x83\x9c\xc6\xd5\x05\x00@\x00\xd63\xfc\xb1\xb5\xd7R\xd0h\xd6j\x01r@\x04\
-\xc3\xb4\x1d\x935\xeb5\xcbM\x15\x86\x87\x01`\xed\xf6\x97\xb6\x85\xdc\x1d\x8f\
-`\xb4\x112\xc6\xa8\x97\x1b\x06\x1e\xb1\xa0\xb1V\xfe\xca\x84j&m\x16\xc6\x8e\
-\x18\xa6\xf9\x08\x95\x1d\xb4\xe5\xae\xd1h\x1e\x07hm\x01Y(\xbc\xed]\xcdv\xd3N\
-\xb9\xf9\xfd\x00\xd0\xac7\xeb\xb5\xa6a\x1a\x8cq\xcb\xb2,B\x1b\xd5z\xca\xf3\
-\xf2\xc3\xc3\x00\xb0\xbe\xb2`a\xd3H\x1f\x8c\xa0Xo4\r\xd3N\xa7\xb3\xc8\x02\
-\x8c\x9a\xab\xab\x8b\x96\xd1\xcc\xb8\x98\x1b\x197mo\xcb\xbb\x98D\x8b\xbbF\
-\xa3\xd9\xe5 \x8f\xe3\xda-\xe8\xebmO\x0f\x1f%\xc4\xa8W\xebA\x10\x1b\x06A$\
-\x96e9\x164j\x8dT&\x93\x1f\x1a\x02\x00F#\x7f\xe5F.\xbf\x8f\x99#\xcdZ\xc5rs\
-\xb6e\x01F4\xa8\xfb\xeb\xb7,\xd2\xcc\xa5\xc1r\xd2\xe9\xde\xfbho%Z\xdc5\x1a\
-\xcd.\'\xf6\xbfBN\x01\x80\xf5\xf0\xb6\xdb\xa9\xbc\x9b\x1d\xabUjAH\t0N,\x83\
-\x80mb\xcd\xf7\xbd\\.W,\x8abkwnx\xb9=\xa6;\xd2hT\xad\xf4\x10pj\x12\xacW\xd6\
-\xea\xd5e\x934\x0b\x9e\x01\x00\xde\xd0\x91\xceW[\x1f\t\xdb\xa2\x11\x1a\x8dF\
-\xf3\x90\xe0\xb4\x19\xd7\x97\x00\x10\x90tO6@ 5<U\xab\xd4\x1a\xcd\xc84\x10\
-\x0c\xcb@n\x12h6\xea\xd9b1\x93k\xe5\xe9\r\x83\xbaA,+=\x145\xab\xa6\x93G\x16\
-\xa5\\\xbb\xbav\xa7Y_\xb1\x8cf\xc13\x11\xc1J\x15\xdc\xdc\x9e-\xed^o\xb4\xb8k\
-4\x9a\xddL\xec\xdf\x04\xce\x80\xf4\xf2\xb6s;\xb3?\x0c0\x8c\x99i\x12 \x96A8\
-\x01\x8c\x82F\xb68\x9c\xce\xb4\\\xe7\x88\x18\xd4\x1bv\xaa\x184|\xe2\x14\x01i\
-\xca\xb1*+\xb7\xc2\xe6\x9am\x86\xb9\xb4\x89\xf8\x08\xb6\xe3\xe8\x8f\x16w\x8d\
-F\xb3k\xe1q\x8d6\xca\xd0sG\x0e\x04bq\xa3\xc8\xb9A\x08%\x86i\x00\x07\xce\xa2\
-\xa0^\x18\x1ds\xd3\xa9V!\xce\xa3 \xe4\x8c\x04Q`\xbbE\xce\xa8k\x93\xbb\xe5\
-\xc5(XM9\xd4s\r\x91e\xc0\xcd\x8e\xd9\xa9\xc2V\xf6\xae?Z\xdc5\x1a\xcd\xae%\
-\xaa\xdc\x04@ \x84uM\xed\x8b\x1c\x9d\x12\'\x19\xe4\xcc0L\xc3@Nc\x1a\x05\xc3{\
-\xf7Y\xb6-\x8ap\xce\x9b\xb5F\xb3Q\x8d)\xb1\xec\x14r\x96v\xad\xca\xcab\xd0,g\
-\xd3\x98\xb2\x8c\xd6\x86z\x86\xe5\ro#\xb3\x1d\xb4\xb8k4\x9a\xdd\n\x0b\xd7\
-\xdb\x1b\xe9aW\xb3\x1d\r\xd7J\xefc\x80\x84\x10\xd3\x04\x16\x854\x8e\x86\xf7\
-\xee7M\xb3U\x03\xa5A\xbd\x194k\x0c-\xd32\r\xd3\xb4\x0c\xbe\xbe\xb2\xd4l\x94\
-\x0b\x1e\xda\xe6\xc6d\x91.Nt\xdd\xd9\xe3\x11\xa2_b\xd2h4\xbb\x93\xd8\xbf\t\
-\x00\x04\x802\xe8\x96#\x0c\xcd\xf4\x1e\n\x0epf\xdb\x06\x8b\x9a4\x8eJ\xfb\x0f\
-l({L\x83z\xc0y\xcc\x88\x8bHl\xc76\x90\xd6\xd7n7\xebwr)\xae*\xbba\xa7S\xc5\
-\x89\xad\xea\xd6\xa0h\xcb]\xa3\xd1\xecBXs\x85\x85\x15\x00\xe0\xd85\xb5/"Iqs\
-\x048u\\\x9b\x065JY\xe9\xc0\xc6~I\x9c\xb2\xa0\xd1\xa4q\xcc\xc0\xe4,J\xa7\xd3\
-4\xac\x07\xd5\x95Z\xbd\x9c\xf7\xd017\xd5\x97\x19\x99J\xa4\x08\xde\x0ehq\xd7h\
-4\xbb\x0f\x8c\xfc\xaf\xc4\xa7\xae;r "I\xef\x05\xc3t-3n\xfa\x1cI\xe9\xc0\x01\
-\xf9W\x1a\xc5A3\xa01\xe5`Fq\xec\xa5\xd3aP\x0b\xab\xcb\xf5\xc6j\xc1\x03{\xb3\
-\xb2\xdb\xe9!\'3\xf2\xd0;t\xffhq\xd7h4\xbb\rZ\xbf\xc3\xe3\x1a\x01`\xdd\xccvD\
-N\xec\x1cq\x86,\xd3\x88\x1a\xeb\x86\xed\x8e\x96J\xf2\xafq\x10FQ\x14\xc7\x94\
-\xa3A\x19M\xb9N\xd8\xf0\x83\xear\x14\xaf\x17=\xb06+;\x10\xe2\x8dLnE\x97\xee\
-\x1f-\xee\x1a\x8dfw\x81<\xae\xceC;\xd9@\x17o;!\xc4\xddc\x1af\xdc\xac\x98N\
-\xaa8\xba\xb15G\x1c\x86\xf5Z\r\x89\xc9\x90 \xf2\xb4k\xc7\xcdZ\xdd\xbf\xcd\
-\xa8\x9fM\xa1m\x12\xdc\xbc.\xab\xa6\x08\xdenhq\xd7h4\xbb\x8a\xb8vK\xa4\xf6e\
-\xddw\xe4\xe0\x86]4\xed\x02m\xfa\xb6\xd7J\x1a#h\xd6\x1aQ\x10049\xe7\x061L\
-\x93\xc4\xcd\xea\xfa\xda\x12\xf2Z.\xddE\xd9\x89i\xa7\x87\x0e=\xfc\x0e= \xdbn\
-\x11@\xa3\xd1h\x1e\x18\xe4q\\k\xa5\xf6\x8di\x97\x02\x1c\tqKQ\xb3\xeaf\xf3\
-\x1b\xca\x8e\x10\x05a\x14\x04\x14\rJ\x99eZ\x048\x0f\xaa\xab\xe5\x85 \xaaeS\
-\xd0\xa9\xec\x00\xe0\r\x1d6\xccG\xb0\x0b\xc7\x80h\xcb]\xa3\xd1\xec\x1e\xe2\
-\xaaH\xedK(C\x8eI\xb3\x9d#R\xf0\x08%\x99b\xd1\xcb\xb6\xdd)\x88A#\x08\x82\x80\
-\x83I)u\x1c\x1b9\xe5A\xb5\xea/5\xc2\xe6p\x968\x16t*\xbb\xe9f\xdd\xfc\xbe-\
-\xe8\xd1\x03\xa3\xc5]\xa3\xd1\xec\x12\x90E\xb4\xbe\x04]\xdfZ"\xc094#\xb4S\
-\xd9\xec\xf0H*\x9dn\x1d\x82\x10\xd6\x1bA\x18RfPF]\xc7\xe2,\xc6\xb0\x1a\x07K\
-\xf5 H;\xc4s\xbb(;\x00d\x86\x8fn\xc3\xf0G\x15-\xee\x1a\x8df\x97\x10W\xbfB\
-\x16\x03\x01\xcaaS\x8e0\x02\x8cC\x10!\x92Tvx|C\xd99\x06\xf5F\x18\x86\x0c\r\
-\xc6\xb9\xebX\xc8\x19\x0f\xab,\xb8\x83<\xb0-\x92Mu\xc9\x0f\x0c\x00N\xa6d{\
-\xc3[\xd2\xa7\x07G\x8b\xbbF\xa3\xd9\rp\xda\x88\xeb\xb7\x81t\xe4\x08#\xc0\x18\
-\x041"\xc2\xe8\xf8\xc9L\xbe\x95\xdb\x8bs\x1e\xd6\xebA\x183\x0e\x9cs\xcb\x00d\
-\x94\xb0\xa6\x01U \x8d\x98\x13\xdb\x04\xc7\xec~\xae\xf4\xd0\xb6\xd8\x8e\xa3?\
-\xdb\xfa\xb1B\xa3\xd1h\x06$\xf6o\x022\x00`\x0c6r\x84\x11\xa0\x0c\x9a1"\x82\
-\x97\x1b\xc9\xe4[+\xa8\x9c\xb1F\xc5\x0fBF9A0L\x13\x08R\x03\xe2T&\xc5\xe3\x8a\
-m\x19\x8c\x83c\x82\xd9M \xadT\xc1r\xb6i\xf8\xa3\x8a\x16w\x8dF\xb3\xe3\xe1Q\
-\x956V\x00\x08"P\xbea\xb6\xc7\x14\x82\x18\x01\x81\x18\xa4P:\xd2*\xccxP\xab\
-\xc5\x9c\xc4\x94\x13 \x06\xe1\x843\x02,?<\x1c5\x96S\x16C\x04\xc6!\xe5t\xf3\
-\xb6\x8b\xa4\xed\x8ft\xe7\xeb\x01\xd1\xe2\xae\xd1hv<\x91\x7f\x13\x00A\xe4\
-\x08k\x9b\xed\x11\x83\x90\xa2\xc8\x19\xe6\xe5\xc6\x9ct\x0e\x00\x90\xb1f\xad\
-\x1eD<\x8a\xa9e\x9a\x04\x18a1\x00/\x8c\x96h\xdc\x84\xb0l\x99\xa4\x19A\xca\
-\xee.\xe0nv\xcf\xb6J\xda\xde\x07\xeds\xd7h4;\x1b\x16\xac\xb2`\r\x00\x10Q&\
-\x1b\x88(Db/l\x02\x86a\x15\xc7\x8e\x02\x00\xa3\xb4Y\xad\xc5\x0cb\x86\x96i\
-\x02p\xc2c\xdb&\xd9\xa1Q $X\xfb\xc2\xb1\x88\xf8k\xc6\x15\x93\xc5&\x88\xe9x\
-\xc3G\xb6\xb4o_\x03-\xee\x1a\x8dfG\x83\xd1\xfa5\xf1I\xbc\xb5\x84\x00A\x04L\
-\x86\xcb \x14\xf7LZN\x8aEQ\xbd\xd6\x08)p\xc6\\\xc7b,\x8e\x1b\xb5\xa1\xd2\x90\
-\x93)\x00@\xb4~\xcd\x82&\x004BL\xd9\xdd\x83d\xb2\xa5\xe3\xdb-i{\x1f\xb4\xb8k\
-4\x9a\x1d\t\x8di\x18#\x84\xcb$\xae\x13bR\x86\x9c#G\x08b\xe0J \xa4\xedz\xb9\
-\xa1\xfd\\({\x8c\x9cs\xdb6\x19\x8bI\xdc\x18\xde3b\xa7s\x00\xc0i\x83\xd5\x97\
-\x00\x80q0\t\x18\xdd\xa4\xddN\x0f9\x99\xd1.\x7f\xd8\xaehq\xd7h4;\x0cD\xf4\
-\xd7+\x14,\xdb\xe0Fc\x1e\x88\x01\x00\xac\xa5\xec\xc8\xb9L\x15F\x90\xb3\xd1\
-\xfd\'Y\x18\xd4\xebA\x18#\xe3\xcc\xb1L\xe4\x0c\xe2F\xb14j8\xad\xfd\xaf\xa9\
-\x7f\x13\x91\x13"b(\xbb\xbd\xb3\xb4\xcd6\xbf\x1e\x04\xbd\xa0\xaa\xd1hv\x18\
-\xeb+\xab\xeb\xe5\x15\xd72\xd2d\x8d\xf0&\x01\xc2\x18\xc6\x14\x9a\x91\xaa\xec\
-\x80\x9ce\x0b{\x08\x10\xbf\xda\x0c)0\xce]\xdbB\xe4\x845\x8b\xa5\x92Tv\x16\
-\xae\xcbM\xb4\x11\xbb\xaf\xa3\xa6\xf2\xfb-7\xb7E\xdd\xfb\x86\xd0\xe2\xae\xd1\
-hv\x18\x8dz\x1d(\xc7\xb0F\xeb\xb7\x01\x0c\x04\x08c\x14\xaf))\xca\x8e\xa6e{\
-\xb9\xb1z\x00\x94\x01r\xee\xda\x06\xe7\x94\x05\xd5\xe2\xe8\xa8\xe9\xa4\xdb\
-\x95a\\\xb9\x0e\x00\x84\x00\xc7\xaeY$\xb7{\xf6\xc7^hq\xd7h4;\x0c/\x9f\x07\
-\xcb\x8a\xaa_!\x0b\x88A\xc2\x18\x1bQB\xd9\xb9e\x99CcS\x11s\xe3\x98\x02\x80c\
-\x1b\x8c\xc6\x185\x87\xc6\xc6\x0c;%\xab\xa2\x8de\x1eU\x01\x00\x11b\xda\xcd!\
-\x03\xe0\r\x1d\xda\xce\xd9\x1f{\xa1}\xee\x1a\x8df\x87Q(\xe4ic\xdd\xe1u\x00\
-\x83q\xa8\x07\xa8\xbaS\x90s\xdbv\xf3\xa3\x93\x11\xf7(e\x86A,\x93\xd0(D\x16\
-\x8e\xec\xd9\xa4\xec\xc8i\xec\xdf\x04@BH\xcc\x90\xf1.A2\xa6\x93q\xf3\xfb\xb7\
-\xa8c\xdf(Z\xdc5\x1a\xcd\x0e\xc30\xcd\\:dM\x00\x02\xcd\x803\x8e\xaa\xb2;\xae\
-W\x18\x9dl\xc46\x00\x9a\xa6A\x08\xf28B\x1e\x8f\xec\xdd\x93\x08d\xa4\xb5\x05\
-\xa4M\x10\xa9\x80{\x98\xed\x99\x91\xc9m\x9e\xfd\xb1\x17Z\xdc5\x1a\xcd\x0e\
-\x83GU\xd6,\x13 \x94b\x10mR\xf6T:\x97\x1b>\x1a\xc4&g\xdc\xb6-\xc3@\x1a\x04\
-\x96\x89\xf9\xd2\x98amr\xad \x0b\xe3\xda\xa2\xf8L\xd9\xe6,\x92m\xec\xcc\xe8\
-\xf6\xcf\xfe\xd8\x0b-\xee\x1a\x8df\x87\x11Un\x00r \xd0\x08\x91s\x0e\x00\x84\
-\x18\x88<\xe5\x15\xbc\xc2\xa1Fdr\xce-\xd3\x00`\xb4\x19\x98\x16\x14Jc\xc4Hj]T\
-\xb9\x0e\x9c\x02\x00"t7\xdb\x89\x91\xd99\xef\xa3v\xa2\xc5]\xa3\xd1\xec$Xp\
-\x97\x05k@ \xa2\x18QpRY\x00\xa0q\xd3\xf3F\xd2\xf9\x83\x8d\x90 2\xc7\xb1\x019\
-\r\x1an\xca\xc9\r\x8f\x12#\xe9W\xe1Q\x955\x97\xc5\xe7\x98A\x97M\xb4\x01\xd2\
-\x85q\xd3\xc9<\xf4\xfe<4\xb4\xb8k4\x9a\x9d\x04\xad\xdf\x06\x00@\xa4\x98\x1a;\
-\xf4\x84\xeb\xe5y\xdc\x8c\x9a\xf5(6\x1a!!\xc0\x1d\xd7F\xceh\xb3\x91\xce\xa62\
-\xc5\x11\xd2-p=\xaa\xcc\x89\x94\x8f\x9c\x83LG\xa3B,7]\x9cx\xd8}y\xa8\xec\xc8\
-\x85\x02\x8dF\xf3x\x824\xa0\xc1] \x103\x12\xb3\xa2\xe9\xe4\t1\x10L\xcaS!5\
-\x01\xb8i r\x167k^\xce\xcb\x0euWv\xd6\\\xe1aE\x18\xeb1\xeb\xba\x89\x1exC\x87\
-\x89i?\xe4\xde<\\\xb4\xe5\xae\xd1hv\x0c\x91\xff\x15\xf0\x18\x01\x83\xc8\x8c9\
-\xe1\x8cr\xd3\x89cdH,\x0b\x80#\x02\xa1\xf5jv\xb8\xe8\xe5z&\xe6\x8d\xab\xf3\
-\x00\xad}<\xba\x86?Z\xa9|*\xb7\xf7!vcK\xd0\xe2\xae\xd1hv\x06<\xae\xd1\xc6\
-\x1d \x10E\x10RN\x08\x8f\xa38b$\n8 \xb1\x08DqL0\x1e\xda3b\xa7z\xee\x94\xc4\
-\xa3*\x8fk\xd0g\x1d\x15\xc0\x1b\xde\x19\xdbq\xf4G\x8b\xbbF\xa3\xd9\x19D\xfeW\
-\x80\x1c\x01\x1a\x11"\x07\'\x97\x8e(E\xca\x18r\x02\xc0\r\xd30\xa08<j\xba^\
-\xbfJ*\xd7\xc5v\x1e\x94b\xd7\xf0G7\xb7\xc7N\x17\x1f^/\xb6\x0c-\xee\x1a\x8df\
-\x07\xc0B\x9f5V\x80@\x18!e\xc8\xc1\xa9\xd7\x9aa\x18\x03\x80A\x08\x00\x10\xc3\
-\x04d\xc5\xd1\x91~\x954Wx\xb4\xdee\x13\xed6\xc4\xb0\xd2C;8\xfcQE\x8b\xbbF\
-\xa3\xd9\x01\xc4\xfeM\x04$\x08\x94A\xca\x86\x18\xdd\x80\x12\x1a\xc7\x00\x04\
-\x00\x01\x01\x01\x10\x91\x06u\xd3\xee\x91\x07\x06Y\xe4_\x07\x00\x02\x10Q\xc4\
-nf{\xaa8a*\xf9\tv4Z\xdc5\x1a\xcdv\x876Wi\xb0F\xc4f\x1a\x06\x18@L\xc2\xb8\xed\
-2\xca\x84\xb2\x03 "\xb1,b\xb9\xe9\x9e\x95\xd4\x97D\xb2\x01\xd6#\xfc\xd1\xb0\
-\xd3\xe9\xc2\x81\x87\xd8\x8d\xadE\x8b\xbbF\xa3\xd9\xe6`\xec\x7f\x05\x00\x08\
-\xc08\x02\x00\x021\xb0\x91K\xd5\xa87\x1c\x86\x9cs\x06\x00\xa6AFG\n\xa6\xd3\
-\xdd\xeeF\x16\xb5\x82d\x00b\x8a]\xdfZ\xf2\x86\x8ft\xbe\xc8\xbas\xd9==\xd1h4\
-\xbb\x12\xdaXf\x91/\xccv\x19\xb9\x88@ XqS\xa1\xe3flo\x8fiY\xb6\x932\xad\x9e\
-\x89y\xe3\xea<\xf2\x18\x00\x10\x81wK\xdan\xa5\x8bnv\xec\xa1u\xe2\x11\xa0\xc5\
-]\xa3\xd1l_\x10Y\xd46\xdb\xd5%P\x02\x00@\xe2f\xd5\xb4\xe3\xcc\xde\'\xba\x19\
-\xe2\xb2\n\x86q\x85\xd6n\x89"\xdd\xcdvB2#\x93\xdf|\xeb\x1f)Z\xdc5\x1a\xcd\
-\xf6\x85\xd6\xef\xf0\xb8A\x00(\xef\x92\xb8\x91\xb4\xb66\xed\xa6\xec<\x00ZCZ\
-\x8d\x9a\x15\x1e\xd5\xc5\xce\xa8\x9ccWo\xbb\x9b\xdb\xb7\xe3v\xd1\xbb\'Z\xdc5\
-\x1a\xcd6\x059\x8d\xfcy\x10\xde\xf6n\x91\x8bf\xaa\xe0f\xf7(\x070\xe0M\xa0U\
-\x16\xf9a\xb3\x1a4\xa30\xa4\x94\xf1\xa1,1\x0c\x02\xd8\xddl\'\xa6\xed\r\x1f~\
-\x98\xfdx4hq\xd7h4\xdb\x94\xb8\xb6\x88, \xbd\xf2\xad\x13\x92+\x1d\x03\x00\
-\xe0!\xf0:F\x958\xac\x05\x8dz\x18\xd2(b\x94qD@\x00\xdb$\x86A\x00\x80\xf2\xee\
-{-\xa5\x8b\x07w\xe2.z\xf7D\x8b\xbbF\xa3\xd9\x8e \x8b\xe3\xea-\x10y\x02x\x17\
-\xb3\xddIgY\xe4\x87\xd5y`\xf5\xa0\x19\x85\x11\xa51\x17\xe14@\x80\x00\x18\x06\
-\xc4\x0c,\x8b\x10h\xbd\xb5\xd4m\x17=/\xb5\x8b\xc2\x1fU\xb4\xb8k4\x9a\xedHT\
-\xb9\x8e<\x02\x80\x98w\xbcpD\x802\xb8s\xbb\x16\xce\x7fQ\xf4\xc04\x90\xf3\x96\
-\xa0\xab;\xe2q\x04\xc6\xc1\xb5\x10\x10\x82\x18\xc2\x08\x88\x01&\x01\x00@\x04\
-\xc3\x00B\x8c\xcc\xe8\xb1\x1d\xba\x8b\xde=\xd1\xe2\xae\xd1h\xb6\x1d,\xac\xc4\
-\xf5\xdb\x00\xc09p\xd6\xe1%\x07X\xab\xe1J\x85\x8e\xe6\r\xd3 \x88\x9b4]\xbe\
-\xd5\x141b\xdbF\xdas,\xcbD\x1b\x89IM\x03\x08gB\xdb\xc3\x98\x071)\xda;x;\x8e\
-\xfehq\xd7h4\xdb\r\x8c*7Z\xe9\xbdXr\t\x94\x10\x08bX\xafq\xcf52)\x82\x002\xf4\
-\x1d\x11\x00\x90\x18\x86m\x99\xaek9\x8e\xe9\xda\x06A\x16\x061\xa5\x0c8\xa71"\
-\x10B\xc04\xb9k\x11\xbfA\xe38\xec\x13\x1d\xbf\xa3\xd1\xe2\xae\xd1h\xb6\r\xac\
-\n\xbcJ\x9bwY\xb0\x0e\x04X\xb7\xf0G\x00X\xabr\xc6a8M\x0c\x02\x1c[\xb2nZ\x86\
-\xebZ\xaec9\x8ei\x12\xe4\x9c\xd1(j\x04\x8cq\x04D \x04\x89\x83\x04\x0c\x8c\
-\x01yL\xc120\x93",\x0e!\xbd\xdb\x82 \x05Z\xdc5\x1a\xcd6\x80\xc7\xf1\xdd\xcf\
-\xeb\r?\x8c\xd01\xa8m\x11\xc0.\xe1\x8f\x84@#\xc4J\x03\xbd\x14I9\xc0\x18p\x04\
-\xc71\x8aE\xcf\xb1\x08pN)\x8d\x9a\x11\xa3\x8csq\x00\x10@Nl\xf0\xc6M\xb7\xc8\
-\x18\x1a\xd8\xc4\xfau\xc2#\x86\xc4$@\xe3p\xeb\xfb\xba5hq\xd7h4\x8f\x1c\xac\
-\x95/\xaf\xdf\xbdK)"\x80\x9b5\xba\xbf\xb5D\x00\x00\xd6j\x08\x00Y\x97\x10Bl\
-\x87\xd8&\xb1-\x03\xe3\xb0\xde\x10Fz\xfb\xed\xd5\x8d#I-\xc8\xf0(6\xcd\x15B\
-\x08Gbc:mE\xa2$\x8d\x9a[\xd9\xcf\xadD\x8b\xbbF\xa3y\xc4D\xcd\xca\xfa\xdd\xbb\
-\xc2N7\x0cb\x99\x80\x08Ld\x80i\x07\xb7p\x0e\x8c#\xe3$\x932F\x0b\xc4\xb5\r\
-\xc3\x00\x82\xc8\x11\x19\xa54nE\xcbt:q\x10\xc0\xcb\xa6\xea\xa1A)\x05@ f\xda1\
-\xc5\x9f\x0c\x00\x1a\x07[\xd8\xd1-E\x8b\xbbF\xa3y\xc4\xc4Q$b\x19)\x03\xcf\
-\x06\x02@9"\x02G`\x0c\x18\x07\x04 \x84X&q\x1c\x925\x00\x119g\x9c\x82\xf0\xda\
-\x10\xe8\xbf)\x1e:\xc4\xb7\x0bc!\xcdp\x8e6\x89,ZC\x06\x00@\x08p\x1a"b\xd7}\
-\xb4w:Z\xdc5\x1a\xcd#\xc6I\xe5\x80\x10\xe4H\x19\xba\x16!\xa6\x19\xc5\x18D\
-\x94\x10b\x1a\xc4\xb1\x89i\x00!\x88\x1c\x90s\xca\x01\xda\x06\xfa \x92L\x80\
-\xb0\xa8a\xb0\xf9\xb4\x9d\x02 H\x03\xce\xa8<\x14Y\x84\xc8\t1\x1fV\xdf\x1e\
-\x1d\xbb3z\xff1\xc1\xf7\xfd\xf9\x85E\xdf\xf7\x07/\x10\x86a\xe7!\xf3\x0b\x8b\
-\xcb\xe5\xf2Cl\xe8\xfd\xb3\\.\xdfo\xabf._\xe93\x14\xfd\x0f\xb9p\xf1\xd2\xfd\
-\xb5\xef\x9b8\xfb\xf6\xc1\xf7\xfd\x99\xcbW\x1ea\x03l7\xedx#\x84\x90\x91\x91L\
-a\xb4\xe0d2\xe9\x94\x95v\x0c\xcf%\xae\r\x06\xe1\xc89c\x88\xd8\x8a{\xbc_3\x9b\
-\x10\xc29\xa7A\x9d\x865\xc6\xd8\xa6\n\x90\x89\x08\xca\xdd\x87\xb6\xdcw$a\x18\
-\xbe\xfe\xc6\x9b\xae\xeb\x8e\x8d\x95fg\xaf\xf9\xbe\xff\xf2K?q]W-3s\xf9\xca\
-\xef\xde:;1~`\xb9\xbc\xf2\xec\x99\xd3g\x9e9\xed\xfb\xfe/^}m\xac4\xba\\^\xf9\
-\xd1\x0f\x7f05y\x14\x00\xfe\xf1\xd5\xd7\x00 \x08\xc3\xa9\xc9\xa3/\xbe\xf0<\
-\x00\xfc\xf5\xdf\xfc\xad\xac\xe4\xe7\xaf\xfclb<\xf9r\xb6\xef\xfb\x7f\xf7\xf7\
-\xff\xd0\xf5O\x9d\xcc^\x9b\xbbp\xe1\xd2\x7f\xfc\xab\xbf\x1c\xb0k\xcb\xe5\xf2\
-\xebo\xfc6\xe5\xba\xae\xeb\x84a\x14\x84\xe1\x9f\xbf\xf2\xd3|>\xaf\x96\xf9\
-\xc7W_;s\xe6\xb4h\xff\xc6\x89f\xaf\xe5\xf3\xf9D\xc9\xfe\xcd\x96\x87\xbc{\xee\
-\xfc\x99gN\xab\x85\xe5 \xb8\xae+F\xaf\x7fG\xeey\xf6^t\x9e\xe8~k\xf8\xfaT\xfc\
-\xea\xec\xec\xb5\xe9S\'\xb7\xfe\xd4\x92\xa1\xe1\x91\xc8X\xb5l\xde\xf0\xeb4\
-\xa6\x88H\x00\x90\x03\xb6=._\xd3oB\xa0\xbb\xef\xc6\xb4\xcc\xfe>\x9d\x9d\x8b\
-\x16\xf7\x9d\x87P\xf63g\x9e)\x97\xcb\xf3\xf3\x0b\x13\x13\xe3\xd3\xd3\'_\x7f\
-\xe3\xcd\x84\xbe\xcf\xcf/\xbc\xfc\xd2O\xa6&\x8f\nM?\xf3\xcc\xe9\xcff\xae\x08\
-\xf9\x98_X<w\xee\xfc\xd4\xe4\xd1\xd9ks\xae\xeb\xfc\xfc\x95\x9f\x85a\xf8w\x7f\
-\xff\x0f\xcf\x9e9-j\xf8\x1f\xff\xfd\xbf\xf5i\xc0G\x17.\xe5\xf3\xf9\x99\x99\
-\xcb}\xc4\xfd\x97\xbf\xfa\xe7\x9f\xbf\xf23\x00\x18d\x02\x90\xf8\xbe\xff\xcb_\
-\xfdZN<\x000\xbf\xb0\xd8\xa9\x98g\xce\x9c\xbe\xafj\x07o\xb6\x8a\x18\x04\xdf\
-\xf7\xff\xe9W\xbf\x9e\x9a<\xfa\x00g\x94\x83p_\'z\x80\x19b\xa7\x83\xb4\x19U\
-\xaeG1\x86a\x00\x00\x84\x00\x1a.Gb`D\x80\x7fma\xef}^\x00\xcb4\x8d]\x9a~`w\
-\xf6jw3s\xf9\xca\xc4\xc4\xf8\xfc\xfcB\xa5\xe2\x9f9\xf3L\xa5\xe2/,,NMM&\x9e\
-\xac\x85\xb2\xab\xdf\xcc^\x9b\x13\xdfL\x8c\x1f\x08\xc2\xd0\xf7\xfd\xf9\xf9\
-\x85\xa9\xa9I\x00p]Wh\xbd(\xb9\\.\xf7r2\x08\xc7\xce\x9f\xbf\xf2\xd3\xd9ksj\
-\x99\xf9\x85\xc5\x0b\x17/\xcd\\\xbe"\n\x88\xffD\x81|>\'\xbeT\x0b\x87a(\x9at\
-\xe1\xe2%\xf9\'\xd1B\xb5\xd9RRE\x93.\\\xbc\xe4\xfb~>\xbf\xf1\xd6\xc9\xcc\xe5\
-+j\r\xbd\xe8\xd5\xec{\x92\xcf\xe7S\xae[\xf1\xab\xa2#\x89\xce\xfa\xbe/k\x0b\
-\xc3\xf0\xc2\xc5K\x17.^\n\xc3\xb0s\x10DO\xe5\x08\xf7?Q\x18\x86\xa2_\xb2\xf2\
-\xe5rY\x0e\xaf\x1c+\xb5\xe3r|\x84GKV+\x87Z\xd6 \xff$\xca\x8b\x06\x0f> \x0f\
-\x83\xda\xdd[A\x10\x01\x02!\x04\x810g\x1f\xc9?!\xfe\x03+\x8f\x0f\xcfm\x82\
-\x00H\xb9X]\xdduhq\xdfy\xcc\xcf/\x08/\xc1\x8f~\xf8\x83\x89\xf1\x03/\xbe\xf0\
-\xfc\xf8\xf8\x81\xe9S\'\xe7\xe7\x17\xba\x96\xff\xdd[g\x9f=s\x1a\x00|\xdf\x97\
-Va!\x9f\xab\xf8\xd5M\xdf\x14\xf2\xe2&w]\xf7\xdc\xb9\xf3\xff\xf4\xab_\xff\xf2\
-W\xff\xdcY\xdb\xcc\xe5+\x13\xe3\x07\xf2\xf9\xfc\xf4\xa9\x93\x9f\xcd\xb4\x94b\
-\xf6\xda\xdc\xb9s\xe7E\xdbf._\xb9p\xe1"\x00\\\xb8pq~aq\xb9\xbcr\xee\xdcy\xd7\
-u\x7f\xf7\xd6Y!:\xbe\xef\xff\xee\xad\xb3\xae\xeb\xbe{\xee\xfc\xec\xec5QRx\
-\xbd\xc5\x83\x88\xa8SJ\x8f8J4)\x08B\xf1y\xb9\xbc\x02\x00j\r\xe2\x9b^tmv\x7f\
-\xc4\xd9_\x7f\xe3M\x00\x10\xde-\xd1G\xe1\xef\x02\x80\xe5\xe5\xf2/^}M\xea\xf5\
-\xbb\xe7\xce\x8b/_\x7f\xe3\xcd\x8a\xef\xab\x830\xbf\xb0x\xe1\xc2%\x00\x98\
-\x99\xb9\xdc9\xb5$N\x04\x00\xbf\xfc\xd5\xaf+\x15\x1f\x00^\x7f\xe3\xb7B\x9d_\
-\x7f\xe3\xb7b|\xc4ht\x0e\x9d\x1c\x9fT\xb7\xa1\x9e\xbd6\xf7\xd6[o\xcb\xe6A\
-\xfbA!\x08\xc2 \x08E\xcb\x1f!Q\x14\xb5\x9d/\x181\xaf\xe2\x13\xbf\xbc\xd2X[\
-\xad\xde\xad\xf9\r\x0f\xe0\x1bZ\xedD\x8e-\xf8\xc6w,fq\xf4\xcd\xd4\xbf\xcd\
-\xd0n\x99\x1dL\x18\x86\t?{\'B\x86\xee\xcb\x93\xfb_\xff\xcb\x7fv]7\x0c\xc3_\
-\xfe\xea\xd7\xd2\xd8\x97|6s\xe5\xe5\x97~\x0c\x00\xd3\xd3\'\x7f\xf9\xab_\x0bO\
-\xce\x85\x0b\x97^~\xe9\xc7r\x9e8\xf3\xcc\xe9\xbf\xfe\x9b\xbf\x15\x1e\tiE>9}r\
-v\xf6\xda\xc4\xf8\x81\xcff\xae<9}2\x0cC1?\x01\x80\xea\xf5N\xf4H\xccXB\xf2\
-\x9e=sZ\xf5\x0b\x8b\x1a\xfe\xeb\x7f\xf9\xcf\xe2\x8c\x7f\xf7\xf7\xff\xd0\xa7S\
-]\x9b}\xcf\xa1\x98\x9d\x9d\xcb\xe7s?\x7f\xe5\xa7\xea\x97\x1f]\xb8\xd4\xb9\
-\x0c\x00\x00/\xbe\xf0\xbc\x18\xab\xbf\xfe\x9b\xbf\xfdy\xa9\xf4\xf3W~&\x07\
-\xe1\xc2\xc5K\xae\xeb\x8c\x8f\x1f\xe8\xf5C\xa8\'\x9a\xbd6\x97\xcf\xe7\xe4$7;\
-{\xcdu\x1d1J\xe2\xf0^C\'\xc7\'1\xd4\x00p\xe1\xc2\xa53gN\xbb\xae[*\x95~\xf7\
-\xd6Y\xdf\xf7\x85\x8fN\x94/\x14\xf2b\xaaxT\xd8\xa9|\xd0\xbcc\x12\x00\x04\xd3\
-6m\xcbaq\xcc(\x03BR\x8ei\x18\x06r\xf6u\x9d3\x84@j\xaf\xe1\x0c1\xc6Mv\x17\x83\
-\x96)@\x009\x8b\x00\xbco\xa0\x1b\xdb\x0c-\xee;\x8f\x89\x89\xf1\x99\xcbW\x9e\
-\x9c>\xf9\xee\xb9\xf3SS\x93\xb3\xb3\xd7\n\x85|\xb9\xbc"\xe5@\xf2\xbb\xb7\xce\
-\x96\xcb+R\x9b\xf2\xf9\xbc4\xd5+~\xb5\x90\xcf\x89o\x00\x0e\x00@\xa5\xe2\x8f\
-\x8d\x95\xa0-\xaf\xae\xebNM\x1dM\x98\x99\xc2\x11!\xd6`\x05\xb3\xd7\xe6\xa6O\
-\x9d\\.\x97\xef\xe9)\x9e>u\xf2\xff\xfc\xbf\xfe\xef\x17_x~\xe6\xf2\x95\xff\
-\xf4W\x7f\xb9\\^\t\xc3P\x98\xb7\x00P\xc8\xe7D\xd7\x84*\x81\xa2e\x92\xc4)\x96\
-\xcb+\x05\xc5?\xa3~N\xd0\xab\xd9\xfd\x1b|\xe6\x99\xd3S\x93G\x7f\xf1\xeak\x89\
-I4\xf1\xb8#\xbf\xef3[\x88s\t\xeb\xbbsbH\x9c\xc8\xf7\xfd\xe5\xf2J\xd8\x1e\x99\
-R\xa94V*={\xe6\xf4\x85\x0b\x97~\xf7\xd6\xd9\x1f\xfd\xf0\x07b\xeaM\x0c\x1d(\
-\xe3\x93\x18j\x00X.\x97gf.\xcb\xf2A\x18\x96\xcbey\xc1<r/\xbf\x97\x1f\xad\xaf\
-\xcd\x99\x06\x03$\x16\xd6\n\x99\xb4\x93\xceS\x06H)\x89\xd6h\x18\x7fm\xb7;\
-\xd6\xc3,\xe7\x9e\x15S\xc30BV0Y\xdd1\x1b\xe2\xc5\'\x1a5]\xaf\xf8\xcd\xf4d;\
-\xa1\xc5}\xe71}\xea\xe4\xebo\xbc\xf9\xc2\x0b\xcf\x03\xc0\xcc\xcc\xe5\xb1\xb1\
-\xd2\xf8\xf8\x81\xb7\xdez;a`\n\x9b]\x8d\xee\x10^u\xb1\xa0\x9ar\xdd|>?11>3sy\
-\xfaT\xcb\x8e~\xf1\x85\xe7\xe7\x17\x16\xcb\xe5\xb2P\xd5\xf9\xf9\x85\xe9\xe9S\
-j\x9d33W~\xf4\xc3\x1fHY\x9c\xb9|\xe5\xa3\x0b\x97\xa6O\x9d\x9c\x9a<:\xbf\xb08\
-1~@:v:\x9b\xed\xba\xaeh\xb9p\x8f\xe4\xf3y\xd7u\xc5"p\x18\x86\x15\xdf\x17-\
-\xfc\xc5\xab\xaf\x15\nIY\xef\xca\xc4\xf8\x81\xdf\xbduV\x08b\x18\x86\xd2-3{mn\
-b\xfc\x80\xda\x86^\xcd\xbe\xe7)\xf2\xf9\xfc\xb3gN\xbf{\xee\xfc\xcb/\xfdD\x1d\
-\xc6\x0b\x17/\x89\xe8#i\x1a\xdf\x93\xe9S\'\xcf<s\xfa\xdds\xe7\xc5O\xd0\xe7D\
-\xe3\xe3\x07\xe6\xe7\x17\x84\xc9\xbf\\.\x17\xf2y\xd1\xd9\xe9S\'g\xaf\xcd\xcd\
-\xcc\\\xfe\xf9+?\xeb\x1c:\x95\xc4P\x8b6OO\x9f\x12\xb3\xe6\xfc\xc2\xe2X\xa941\
-1^.\x97\xc57\xe5v\xc8\xa9\x88=\x1d+\x95\x06\xe9\xd17\x88e\xbb\xe9\xe2\xa1\
-\xa82g\x8a\x97N\xebw\xe2h\xdd0MNcF\xbf\xbe\xb2\x03\x00q\xd3\x99j#\x8cCN\x00\
-\x11\xcd\xac\x9b"\xa4\x81"\xa5\x98\xb1\x0b\x83\xdcA\x8b\xfbND\xdc\xd8\xaf\
-\xbf\xf1f>\x9f\x1f\x1b+U*\xfe\xfc\xfc\xc2\xcb/\xfd\xb83\x14R\xfe\x0b\x00\xff\
-\xe3\xbf\xff\xb7\'\xa7O\xfe\xe2\xd5\xd7\xe6\xe7\x17D($\x08\x9d\xbap\xe9\x1f_\
-}-\x08\xc3\xe9S\']\xd7-\xe4s\xaf\xbf\xf1\xe6\xfc\xfcB\x18F\xa5\xd2\xa8\xea\
-\x93\x99_X\xac\xf8\xbe\xfa\xcd\xd4\xe4\xd1w\xcf\x9d\x9f_X<s\xe6\xf4/\x7f\xf5\
-\xeb\xb1\xd2h\xc5\xaf>9}\xf2\xcc3\xa7\xc7J\xa5\x7f|\xf5\xb5\xe9\xe9\x93\xaaU\
-855\xf9\xcb_\xfd\xb3\x14\xcag\xcf\x9c\xfe\xbb\xbf\xff\x07q\xd4\xb3gN\x8f\x95\
-J\xf9|\xfe\xe7\xaf\xfc\xf4\xf57~;;;\'|\x11\xf3\x0b\x8b\xa2\xa9]Qk\x90\xdd\
-\x17\xdem5\xde\xa6W\xb3\x07\x19m\xe1\xa3W\x0b\xff\xe8\x87?x\xfd\x8d7\xdf=w>\
-\x9f\xcf\x8f\x95F\xfb\x1c+\x07!\x0c\xc3\xcff\xae\x88u\x8e?\xdf<\x07w\x9eH(\
-\xf2\xdf\xfd\xfd?\x88\xf2/\xbf\xf4\xe30\x8c^\x7f\xe3M\xd1M\x11\xae\xda9t\x89\
-\xda\x12C-\x7f \xf1\xb3\x8a\xa9\xe2\x97\xbf\xfa\xf5\xeclk\xc1@\x8c\xb6\xf8\
-\xdf\xad\x17w\x00(\x8cN\xac\xd10\xae-Y\x06\'@X\x1c\xb2\x18\x00D\xe8\x0c\x00\
-\x01\xf8\x1a\xab\xaa\x88\xe8X\x8d\xc2\xd0X\x10\x01"\xda$v\xb1)\x96Q9\xd8\xbb\
-\xd2l\x07\x00\xf2\x10W\xa25\x0f\x19\xdf\xf7\xa5we\xc0C\x84\x85\x9b8d~a\xd1u\
-\x1dyK\x8b2\xea7\x0fPs\xd7\x13\r\xd8\x1e\x00X.\x97\xc30\x1a\xa4\rb\x10\xc6J\
-\xa3\x83\xf8\xd0\xbfq\xde=w\xbeP\xc8\xf7z\x08P{\'z4x;\x13\xfd\xea\xfc\xad\
-\x07\x1ca\x95\xc4\x0f\xdd\xf5\x9bGK\x14\xd4\x83\xc6:\x8b\x1a\x80\x94\xc5!"\
-\x03N\x018g\x8c\x00\x07\x10q\xefHHWc\x9e\xb4\xfe\xe9&i\x88h:)\xc3\xcer\x8e\
-\x18W\x91E\x00\x84q\xf0FOd\x87\xf6=\xdc^="\xb4\xb8k4\xf7\xc7\xebo\xbc96V*\
-\x95J\xbe\xef\x7ft\xe1\xd2\x7f\xfa\xab\xbf|$\xf3\xca\xe3\x02""g,\xa6\x8d2k\
-\xdc\xaa\x86\xf98\x8a)\xe7\x06A\x038\x10$\xc0\tp\xd3@\x03\xa8a\x00G\x93\x10\
-\xf1\xde)kW\xd0zK\xc9 \xc2\x0b\x03\x00\x84#A\xc3\xf5\x86\x8fd\x8b{\x1fa\xe7\
-\x1e*Z\xdc5\x9a\xfbc\xb9\\\x9e\x99\xb9"\x96U\x9f=s\xfa\x91\xafF>&D\xfeM\x08\
-\x97\xc1\x1a[\xb9\x1b\x07\x14L\x83\xb0v@#\x01\xdc3d\x86\xe1\xdd\x8cMy\xe6h\
-\xdc\xac\xc4\xcd5\x1a\xd5\x11\xc1 \xe0\xb9\x04\x01\xb85D!E\x90\x11\x02\x1c\
-\x0c\']\xf0r#\x86i?\xd2>=\\\xb4\xb8k4\x9a\x1d\x80\xbf\xfc\x85Co\xbb\xe9\x92\
-\xdft\x10\xa0\x1e`\x10#!\x04\x11-\x83\xec\x1f6W\xd7\xcb\x9e\xd5\xe0h\xd0\xf6\
-\x16\x1f\x08\x90u!\x9d&\x80\x00V\x01\x8a\xdfz\xb4]\xd8b\xf4KL\x1a\x8df\x07\
-\x10Ea\x18s\x1e\xafy\x0e-d,\xdb\xb6,\x93\x98\x06\xa4\x1d\xa3\x901\xa3(\xe6,\
-\xa86QUv\x93@\xca!\xc0\x01\x10 \xae@\xe3\xfa\xa3\xed\xc2\x16\xa3\xa3e4\x1a\
-\xcd\x0e\x80\xd1\xb8\xd6\x00\xcb\x88\x11VjA\xb1\x90\xc9\xa7m\xe4\xc8-\x93\
-\xb0\xb8Y\xad\xdf\xe5\x9c!\x92\x8d\xdd\xb4\x11R.\x10CY_\xad/\x80=\x02\xf6\
-\xe3\xe2F\xd3n\x19\x8dF\xb3MA\x1ec\xdc@Z5h\xe5\xf6\xf2j\x10q\xcb\x00\xcf\x85\
-\x88"\x80E\x0c\x0b\x08AN9\xa3\x00\x88H8\x82\xe7\x12h\x9b\xedC\xd9\x8eM8L\x0f\
-\x86\x9e\x81\xdd\x98\xbd\xbd\x13m\xb9k4\x9am\x04\xf2\x18i\x83\x87\x15\x1e\
-\xf9\xad\x98E\x02\x88\xc0\x18\n\xa1nF`\x19\x04\x80\x02\xa7\x00@\x00,\x93\x00\
-\x90\x88n\xe4\xeeE\x84t\xc2l\x17\xb0\x06\xd4o@vrK\xbb\xf4\x88\xd0\xe2\xae\
-\xd1h\x1e)\xc8\x90S\xa4\r\x1eUxT\xc5\xb8\x86<n\xa9r[\x9a9\xdf\x88h\x14\x1b\
-\xa5\x9a\x84\xc8hw\xde*\x80\x86A\x08\x01\xce\xc12 \xe5\x90\xee\xef=5oAz\x1f\
-\x98\xbb0\x99L\x02-\xee\x1a\x8df\xcb\xe1\x11\xd0:\xc6>\r*\x9c6\x81\xc7j\xa6\
-\xc6\x04\x04\x80#pT\xfe_\x01\x01\x00A$\x120\xdao0eR\xdd\xccvyD\xf5\x0b(<\r\
-\xbb4\x8d\xbb\xe4\xff\x07_\xffI\xbb\x17\xf3\xcc{\x00\x00\x00\x00IEND\xaeB`\
-\x82' 
+\xcf\x1fD\x15x\xc3E\x07\x1b@\t|\x0f\xb5\xfd\x86{-i\x11\xff\x7f{\xef\xfe\x1c\
+\xd7q\xe5y\x9e\xbc\xcf\xaa[O<\n|\x01|\x01$EB\x92IJ\xb6,y\xec\xb6\xbb\xed\x96\
+z\xed\x91\xdc=\xd33\xbf\xee\xc6\xc6\xc6DL\xcc\x9f\xb3\xb1\x11\x1b\x13\x131\
+\xd1\xd1\x11\x1d\xdd\xda\xf1\xb6\xbd\xedi\xc9\xf2\x8c\xd5\x94%\x8az\x90\xd4\
+\x03\xa4(\x81 )\x00\x04\x89\x02@\xd4\xad\xd7}d\xe6\xd9\x1f\xb2*\x91\xb8\xf5`\
+\x91\x12A\x00\xccO(\x18\xa5B\xde\xbc\x99Y\xf7~\xf3\xdc\x93\xe7\x9e\x0c\xee\
+\xb1`\x15\xe0>S\x85\xe9\xe6R\xb9};O\xdc\xe5\x95$\x1d\xdf\xe2\x06\x13\x97\xa3\
+\xb8\xb7\x97\x86\xff\xea\xf6\xd0/S\xf1\xb2\x17\xcd\xa7\xe3\xdb\x16o\x10@4\
+\xac\xd8,\x06\xa9CQj\x82\xbb#\xa6iZ\xed\xdc\xd9\x94\xd2\xea\xd0\x0f\xd6\xf3\
+\xdf\x97UY\x96\xe5\xba\xae\xc8\x0b(^F\xefl\x86h\x00l\xf6K\x98\xa6)"(87\x99QX\
+)\xfeY\xb9\xf0\xa7\x06k\xb8l-\x1d\xddN\xd1e\x8b\xd7\x018 p#\x15\xd9\xa3Mw\
+\x82\xba{\xc1\x1d\x12i\x08\r\xa3{\x94w\xa7/H\xde<\xcc0\xd6s\xdf\xbb\x97\xfd\
+\xae\xc9j)\xba\x9c\x8en\xbb\xb4lb\xfb\x15\x12\xc3A;\xcf\xbd\x03F\xf1Xz\xf8p:\
+?\xea\xba.Q0\x0c\xa3\x99\x7f\xf6\x86{BL\x18\xa2NKI\x8b\x98\xcdfS\xa9\x94\xe8\
+\xe5\x80?\x8a\x08#\x11\xf7\xf9\xc2\x9e\xff-\x11\x9f#N*~\xbbt:\x9d\xc9d\xbc\
+\x94\xd7g)\xb5\xf3\\\x9d\xaf8\x89S\x87a\xd8\n\xa1a\x8cs\xd37OW\xbc\xef\x18\
+\x18[t=\x1d/\xa5\xe3%\x9b\xad\x1b\xc0\x00\x81\x1bNd\x8d\x04\xeex\xe4\xee\xe7\
+\xce\xb0\\\x0c\x94\xeb(\x96\x92M\xbek3\xe4\x04\x9fh\x83\xb8\x00\xa4\xf5\x9a\
+\xcbes\xb9,\xb4\xde\xbaD\xb1Q\x910\x02l\xdb\x96Oc\xa2\xefb\xfc\xe3\x98"J\x7f\
+H\xcb\xa5C\x8c~\x8b\xcc\xa6i\x8e\x8d\x95\x86\x87\x87\x08!\x8e\xe3\xa4\xd3i\
+\x91:I\xe4\xb3\x1cdT;\xc7\xd30\x8cB>\xff\xd4\xf14"\xda\xb6-~)q/\xf4\x9a\xf3\
+\xd4IW\xad\xa7\xfd\x8bp1\x1a\x9c\xf38\xa6A\x10\x84Q$f2B\x88m\xdb\x9e\x97\xf6\
+\xd2i\xb1\xe3\xab\xd9\xde^Q\xa4\x90\xda\xb7w\xcf\xde=c\xa6i\xba\xae+\x92Dy\
+\x9e\xf7@y\x95\x1f\x0b\x9b\x92\r\xb0M\x0f\xea*\x03\xef\xb5\x84\xb1\x92\xc5\
+\x84\xf5No\x91\x199\n\x8f$\xd5\xf8\x96\xa0>\x17\xabWR[X9"\xc6\xe6\xfeJj\x7f\
+\x05\x00\x94\xdb@\x1c\x98jg#\x00\x00\xde\xde\xfeQ\x8a\xa6P7\xe9;\xee\xb4(USE\
+\xaa\xa4\x8c\xd2\x8b\xa2H\xdc\xde\x1bNa+\x1fa.J\x1f\xaa(5$\x8e\x12+\x84\xad]\
+\x8d\xba\xed\xa7\xaa\xfa\x82\xb0\xbd[\xbci\x9a\xf2\xceA\xab\x188\x85\xc0;&\
+\x07\x04\x00\xe4\x14\xe8y\x1eq2\tY\x97\x866"\x1a\x86!\xc5]\xbe\x94d\xf5\x0e<\
+\xef\x84(\xcb\xaab\x12\x12\xd5&\xfc\x03\xeaD\xe5*{\xcc>\x90\x06\x89J\xe4\xc5\
+\xa0\n\xab\xb8\x06\xe4e\xc0\xb9\xc5\xacT-\xb5\xb7\x06g\x12\x83/\x8er\xda\xae\
+$\xa9\xec\xf7m\x12i\xef\xe6,\xda\x90\x18O\xf5\x02P\x95\xce\xb2Hga\xb9\xfe,\
+\x8c\x8c\xd6\xa3@\xc7+p^:\xdd9\xa5%~D\xf1\x8dz\xdd\x0e>\x9ej\xb5\xf26\xb1\
+\xac\x96>$\xee\x85\xaeGA\xfb\x12U\xebQ\x7f\x149\xe1\x89qK\xa7S\xea\xb1\x9dc"\
+\xbe\xa7\x94\x8a\x8b\x9cs.\xeaWo\xc9\x07\x9d\xb7\xb6\x98\xd8\xbf%\xbc\xa0bG\
+\x8e\xaee\x06\xdfk\x89\xd6\xef\xf0\xa8\n\x00\x04 \xee\xbd0\xebdJvz\x08v\xe2\
+\x82*t\\I\xf2*\xb7,K\xbd\x8c\x84T\xe1\xe65\xc9\x84l\x01\x80\xba\xf1\xbcz\xf1\
+\t\xf5\x91\x16\\\xaf6\xc8\x06H\x99\x16\x1e\t9a\xa8n\xd9\x84\x0fA\xdeE2\xfc#\
+\xb1W}\xe2\x8c\xf2Y\x01\x14CI\xceg\x9d.\x02h\x8b\xbb\x8c\xe80\x95\xf7?\xe5\
+\xa9E:VJ\xa9l\x9e\xda\xfdA\xbc%\xb0YkTe\x97N\xb3DG\xd4\xf9\xac\xbf\x1b\xa4\
+\xd7\x05 \x0b\xabb$\x8451\xf8\xf2J\x00\xd84\xad\xaa\xcb\x18\xb21\xf6\xe6\xed\
+\xad\xfb\xe8;(\xe9!\xe5x&.\x00\xf5\xd7W\x7f\xb8\xc4\xfa\x04!D\xf8\x1f\xc4\
+\x16\x05\x8c\xf1\x84\x9f=\xd7\xe1\x0cI\x8c\xa40t\x00@MV\xfc\x10Sf\xe2\xc2\
+\x10\x13\x7f\xe2^\xb8\xaf\xbew\x9d\xedT\xfb)\xe1OK\x8c\x9e\x1c\x131\xe1\xa9\
+\xe2.\xa3q\x1e\xb4k[\x0f\x8f\xeb\xb4\xd1zo\xb9\xcf\x8e\x1c\xde\xf0\x91\xfe\
+\x01\x0b\x02\xe4t#\xfc\x11zN\x15`\x98\xdeH\xcb\x87\xbc#\xc5]\xa0\xca\x9fTm)\
+\xee\xea\xaa\x8e*\xa6\x96\x9a\x9f\xb6\xed\xd4S\xb5@\x1a\xc5\xd6\xe6\xb8\xdd>\
+\xfa\x9e\xb8\x94E\xbaJ\xf5i\xa0Sv\xd5\xf6\x98\xca\xbb\xfb\xf2\x89\xa1\x8f\
+\xf1\x0e\x9b\x95]qD$\x17\x18\xc9fSZ\xf8\x01\x8cv\xb0\xbc\xf8^\x04~\x88\x1a\
+\xa0=\x17\xaa\x0e\x96\xc1o$\xb2\xf9\xc1\x9c\x10bY\x96\x18\x01P\xb4UvD\x18\
+\xcb\xf2N~P\x19\x12\x93\x07l\x9e\\\x133kbrM\x0c\xbej\'v\x1d\xfc\xfeMR\xe7\
+\x98\xae\x17@\xe7\xaf\xaf6U\xb4V\x1a\x19\xe2\x10\xc30\xd6+\x15\xc6\xd4\x17v\
+\x80\x10\x92\xcfw\xf1tKe\x07\x00\xd34\x85\xb8K}\xecz\xd1\xdewT\x13\x17\x86\
+\xd0by1\xf4\x7f  \x9b-\x1eu\x84\x133n\xe7b\x89\x1c\x109\xb9\x8a\t\x8f1&\x86T\
+L~\xfdM\xaemE\xec\xdf\x94\x1b\xe9\xf5\xda\x91\xc3\xf6\x86\x85\x95}_hu\x1eY\
+\x00\xf73\xdb\xd3\x85\t\x19O\xb9S\xc5=qoK\xa1\x94\xc13\x89%{\xd5L6\xdb1\x94\
+\xa4\xed\xd4K\x98x\xa0<\xe9\xf7\x7f\x1a\x15T\xab\xb5\xeb\xd7\xe78\xe2w\x9e}Z\
+\xdc\x0f2l@\xd4\xac>\x9e\xc3\xe6\x0b:\xd1$\xd5\xe0J\x9c1a\x19\xc9\x9b\xb0SD:\
+u$a\x10\x11\xc5\xc46\x14o\x8f<\x97\xda\xfd\xfe7\x928\xd7\xfc\xc2\xe2\xca\xca\
+\x8a\xeb\xba\'\x9f:!g \xc30d\xc0b\xe2(y3w\x9d\xc9z\x9d\xc5\xf7\xfd\xebs7\x00\
+\xe0\xec\x99\xd3\xb0yr\x95\x15\x8a\xc1\x17cr\xe5\xea5D\xdc\xb7oO\xd6\xf3T\
+\xdbY^\x0c\xb2\r\xea\x0f-\x7f\xeb\x01\xe73Tv\x7fV\xe5L\xfd\xe9\x13c\xab\xea\
+\xbb\xd9\xf6\rJ\xcb\xbd\xfd\xd6\xfe\x86\xa9\x97J\xb9)7\x95\x18\xa2\xc4\x8f(\
+\xe4\x18\xdaW\xc8 \xd7m\xaf\xbe\xa8\x17\x86\xfa0\xd7\xd5g\xd8Y\t\x00H\x03_Z\
+\t\xaa\xb2\'n\x87\xc4o\xa1>\x1f \xa2\x10wy/\xcb9\xe0\x81\x1e\xf5\xb6\x1e\x1e\
+\xf9\xad\x95O\x80\x98bw\xb3}\xf0\xbd\x96h@\xeb\x8b\x00@\xfazx\x0c+\x95.n\xa4\
+\x93\xdc\xa9\xe2\x0e\xdd\x0cgi\'v\xf5Q\xa8\xb7\xb4\xbc\x87\x85U\x82\xca\xce\
+\xcb\xb2\xbcj\xdfu^\xd0\xaa\x9b\xc5\xf7\xfd\xf3\x17>\x00\x803\xa7\x9f\x95\
+\xb7\xf7\xf2r\xd9\xb2\xad\x8c\xe7\xa9\x9a\x8b\xed\xe8F\xb5=j\x93\xfa+K\xa7\
+\x9c\x89\xeb^vyum\xcdq\x9cl&#\xbb\xa0\n\x9f\xa58\xd0\xd5fX\xca"X\xd7\xeew\
+\x9a\xb1\xb2\xb0\xf8P^.\x9f\xbf\xf0\xc1\xc4\xf8\x01!\xee\xd0\x96\x83N\xa7D\
+\xa2\xfe\xfe\xde\x83\xe5\xe5\xb2\xeb:\xf9|^\xd4P\xa9\xf8\xef\x9d\xbf\x00\x00\
+gN\x7fG\x96\x97\x1dQ\x07G\x9c\xfa\xf3\x99+\x0006V\xcaf\xb3\x9d\x83O\xda\x9e\
+\x19\xf5\'V\x07g@\xc4U\x04\x00_\xcd^?p`\xbf\x97N\x8b\xebP\xbd\x08\xbbz!\xd4\
+\x8bP\xc4\xb6#\xe2\xca\xcaj\xa3\x95-k\xa3\r{\xf7\xec\x91\x1e\xea\xc4\x95 fk\
+\xd1_)\xee\xea\x0f7xG\xe4\xc8\xc0\xe6\x0b\x03\x94\x89\xa4\xcf\x8f\xd5\xd96y\
+\x94\x14hi\x82t\xc4\xc9l\xba\x11d\xcb\xc5\xed)\xeePT\xfc\xab\x0f:om=Q\xe5\
+\xa6\x88\x8c\xea\xcc\xc4+qs{\x07\xdek\xe9\x06\xf2V\x0cU\xdcgav\xf8\x0816$}\
+\x07\x8b\xbb@Z\n\x86a\x08\xdf\xb1\xbcz:\xd5J\xde\xd5\xf2\xca\x90\xe2\x9eP7\
+\xf5\x90N\x03\x017#\xafTyY\x7fq\xed\xcbs\xef\xbc\xeb\xba\xce+/\xffltdDV\xae\
+\xeaKg{\xfa\xc8z\xa2\xbf\xaa@\xc8^_\xbb\xf6\xd5{\xef_p\x1c\xe7\xcf\x7f\xfa\
+\xa7\xa3\xa3#\t!\x93\xb7\xa5\xac_~\xdf\xd9\xf7\xfe\xdd\xef\x1c\x04\xde~}0qc\
+\xabS`\xff\xfa\xe5\xcf!+\xbfr\xe5\x8b\xb7\xcf\xbd\xe3\xba\xee\xbf\xfe\xf9_\
+\x8c\x8e\x8e$\x14\x017\xbf\x04 \x7fJ1\x1aj\x9b\x85\xf3G\xedBb\xf0\xd5\xeb\
+\xe1\x81\xf4"\xd1\xdas\x7f\xfc\xe3\xfe}\xfb\xfe\xf4\'\x7f"\xa2\xf4\x12\x17I\
+\x9f\x81\x95\xd6\xbd\xefWo\xde\x9aO$Q!\xc4\x98\x98\x18\xb7m\xbb\xb3m\xb2\xe5\
+\x9d\xa6C\xff\xdf\xae\x0f\xea\x05\xd39\'\r^a\xe2B\x95\xb7d\xc2\x8a\x82\x8e\t\
+/q\n!\xe5\xdfV\xef\xb6\x06\x16\xac\xf1\xb0\x95\x84\xbc\x8fs<]<4Hm<\xaa\xb0f\
+\x19\x00\x88H\xda\xde\xc3\xc3c\xa5\x8bnvl\xd37\x0f\xd0\xe4\xed\x8a\xfc\x81\
+\xc5E /\x05Pn\xbf\xc4e\xd1yl/\x01J|\x90\xa0\xf2\x02\x0bk\x8b\x8ex\xb2F\xc4\
+\x85\xc5\xdb\x00\x10\x86\xd1\x9d;\xcb\xfb\xf6\xee\xed\xac\xbfk{\x1e\xf4\xb6\
+\x11KL\xe2K\xc30\x96\xee\xde\x05\x80(\x8a\x96\xcb+\xfb\xf7\xef\x935\xab\x1f\
+\x12g!\x9b%u\xc0\xee\xcb)M\xfdW\xfe!\xd1\xa9\xfbV\xae6C\x95\xc2\xf9\x85\x05\
+\x00\x08\xc3\xf0\xeb\xf9\x85b\xb1\x90\x18g\xa3\xdb\x82s\xc2\x1e\x14\x1f,\xcb\
+t]7Q\xb2\xff\xb0\x0c\x82\xda\xda\xa5;w.|\xf8\x11\xe7\xb8\xb0x\xfb\x9f\xdf|\
+\xeb_\xbd\xf4\xe2\xfe\xfd\xfb\xd4\xee\'\xb4L=\x9d\x1c\xc0f\xb39s\xf5\x8bz\
+\xa3\xb1\xf9\t\x9e\x14\x8b\xf9R{\xaa\xee\xec\x05t\xfc\x88]/\xef\x01\xe9Ug\
+\xe7\x9f\x06\xaf\x8d\xb4\x9fl\xd4\xc9\xbe\xeb\x80$\xee\x059\xaf|[\xbd\xdb2b\
+\xff\xa6\xf8\xd0\xc7lO\x17\xc6\x07\xdbk\t\xe3\xf6C\x00\xde\xc7\xc3\x93\\\x98\
+\xdd\r\xe2.P\xef\x99^\x82\x02\xbd/\x88\x07\xbaP\xb0\xbd\x89k\xcb\x87\xd8~_\\\
+\xec\xb5\r\x00\xcf>=\xbdR^\xc9\xe7s\xd3\xa7\x9e\xea\xff\xfc8\xf8ye\xa7f\xae\
+\\\xbd~}\xaeT*}\xff\x85\x8d\xb4\xef\x9c\xf3g\x9f\x99^YY\x15\'\xb5\xda\x91d}\
+\xce\xf2\x10\xb7\xab\xda\x18l\x87\xeem\x84\xa3(\xb1I\xfdO\xdd\xa7Z)v\xcf<=\
+\xbd\\^\xc9\xe5\xb2\xc7\x8eM\x8aH\x1e9\xce\xbd~_D\x94K\x1d\x1b\r\x00"\x9dQ\t\
+\x1eZ\x1d\xe4\xf4\xc69\xafVk\xe7\xdey\xb7\xd9l\x8aG\x97ry\xe5\xcd\xb7~\xff\
+\x9dg\x9f9u\xf2d:\x9d\xeas\nY\tc\xec\xee\xdd\xe5\xcb\x9f|Z.\'v6G\xd3\xb4\xa6\
+&\'=\xcf\xebe\xa5~\x93\x1f\xb1\x17\xdfz\x9d\x0fwo\xee\x08\x1d\xef\x845\x96E\
+\xc0\xa2\xd0\xe2\xee\x85\x08qs\xfb\x06\xab\xad\xcc\xc3u\x00 \x04\xfa$\x1bps{\
+;\xd3I\xee\x1eqWy\xa4\x97\x82zO\x8a5"\xcaZ\xa2\xc3\x18\x13\xa7\x1e\x1a*\xfe\
+\xf5\xbf\xfdK\x19Z\xf0\r\xdb\xa3Z\x7f\x88\x18\x06\xe1\xfc\xc2"nn\t"\x0e\x0f\
+\r\xfd\xbbo\xef\xa4\xf7ELl\xedU2\xd6j\xe8\xc3\xbe\xef.\xad`\xd9\x9db\xb1\xf0\
+o\xfe\xf2Uu\x16\x91\x92\xad>\xa4\'j\x90\xbf\x8bl\x89|s\xfd\xdb\x1a\x10y"Ji\
+\xb5Z{\xf7\xbd\xf7\xef\xdd[\x17g\x10\x05\x1a\x8d\xe6\xf9\xf7?\xb8q\xe3\xd6\
+\xe1\xc6 3\x08\x00\x00 \x00IDAT\xc3\x87&\x8f\x1e\x19\x1a*\xf6\xa9di\xe9\xce\
+\xdc\xcd\x9bs\xd7\xe7\x1a\xcd\xa0#\xcd\x00\xd9\xbbw\xec\xd0\xc1\x89\xae\xfe\
+\x8a\x1d\xca.\xe8B?\x90G2\xb5o\xbfP\xf4\xd1\x81\xccvd\xf2!\x00\xb1\xe7TAL\
+\xdb\x1b:\xdc\xf9\xfd\xee\x14\xf7- \xa1\xef\xe2K)\xee\x82o\xf1nDe\x15\x81\
+\x8b=Fp\xc35\xa4\xc6\xbal\x8d\x04\x08m\x12\xddOX\xee\x9d\xb2\xfb@\xd5\xca\
+\xee\xc8\xc9L~\xa3\x8a{\x9f\xc3\x93\xe2\xfe\x90\xd3M\xcf\x16Je\x8f\xe3\xf8\
+\x8bk\xd7\x16\x16\x17U\x07\x82\xe4\xce\xdd\xbbw\xee\xde\xfd\xec\xf3\x99=c\
+\xa5\xfd\xfb\xf7\r\r\x15E\x8c\x87\x18\xb7 \x08WWW\x17\x17o\xaf\xae\xdd\x0b\
+\x02)\xeb\x9bj\xc8\xe7sO\x9f:\xb9\xfd#C4\x92M\xa9}{\xee\xc8axCG\x06\xaa\xadv\
+\x1bi\x13\x85\xd9\xde?/\x8d\xd5%\xf5\xe6n\x10wy\x1b\x97\xcb+\xf3\x0b\x0b\x0b\
+\x0b\x8b\x00\x90\xcf\xe7\xc7\xc6J\xd3\xa7N\xaae.^\xba\x0c\x00S\x93G]\xd7\x9d\
+\xb9r\xb5\\^\t\xc3\xd0u\xddRi\xf4\xe9\xe9S\xd23{\xdf\xc2O\x9d8.V2[2\xd4N\xa5\
+$|\xee\x00P\xab\xd5\xe6\x17\x16\r\xc3\x10A{\xaa\x18\x95\xcb+\xb3\xd7\xe7\xca\
+\xe5\xb2\xdaHY \x0c\xc3\xd9\xebs\xe5\xf2\x8a\xef\xfb\x000>~`j\xf2h.\x97\x13\
+\x9au\xf9\x93O\x11\xe1\xf6\xd2\x12\x00\xf8\xbe\x7f\xf1\xd2\'\x00h\xdb\xf6\
+\xe4\xd1#\x9c\xf3>\'\xf5}\x7f\xf6\xfa\\\xd7\x91\xe9\xd5_Yxjjrb<\x99\xf8B\xf5\
+Kl\xbc\xa3\x84\xc89\xbf\xbd\xb447wS\xb4\xbfT*ML\x8c\xcb\xc3\xe5\x89\x08!S\
+\x93G\xf3\xf9\xbc\xfa\xfd\xcc\x95\xab\xcdf0<T\x1c\x1b+\x89\xee\xdc\xfaz\x1e\
+\x11\x9f:q\\\x06Z\xa8gW\xfb8\xbf\xb08??\xbf\xbc\\\xe6\x88C\xc5\xc2\xa1\x83\
+\x13\x9eL\xad\xbe\xb9p\x18\x86\xf2\xd7\x14C!\xba,\x0b\xcc/,\x96\xcbe\xd7u\
+\xa7O\x9d\x9c_X\\XX,\x97\xcb/\xbe\xf8\xc2X\xa9$\xeb\x13\xe2\x1eEQ!\x9f\xcff3\
+\xca\x1e\x1d\xad"R\xa6\xeb\xf5\xda\xdc\x8d\xfa\x8d\x9b\xb7\x0c\xc3P\xc5\x191\
+\xb1k\nI\x98\xed\xe9t\xea\x99\xe9S\xb9\\\xcez\x90\x97\x845\x8f\x11\xe44\xae\
+\xcd\x8b\xcf}R\xfb\xba\xb9\xbd\xa6\xe3u\xfb\xcb\xe6\xdaX\x14W\xe7\xc55\xc1y\
+\xcf\xcd\x9bL\xc7\xeb\x95\x97fg\x8b\xbb\xbc\xc9\x83 \xf8\x97s\x7f\x14A\xd0m\
+\x16g\xae\\\xfd|\xe6\xca\xab\xbf\xf8_\\\xd7\x15\xc5\xce\xbf\xff\x01\x00p\xce\
+/\x7f\xf2Y\x18n\xec\x113{}\xee\xe2\xa5O^\xfb\xd7?/\x95Fe\x9d\xfd\x0b\xbf\xfc\
+\xe7\x7f\x96\xcf\xe5Z\x02\x87\xc98\xeeZ\xbd\xf1\xf1\xc5\xcb\x00p\xf6\xcci\
+\xc5\x84\xc4\x7f9\xf7\xc7+W\xbf\xe8l\xe4\x8f\x7f\xf4\xc3\xd1\xd1\x91\xd9\xeb\
+s\xe7\xdeyW=\xd7\xfc\xc2\xe2\xf9\xf7?\xf8\xd1\x0f\x7fplj\x921\xf6\xe1G\x17\
+\xe5\x9f\xfcj\xf5\x83\x0f?\x02\x80\xfd\xfb\xf6M\x1e=\xd2\xe7\xa4\x17/}r\xe1\
+\x83\x0f;O\xfa\xca\x9f\xffTD\x19\xf6\xed\xef\xe2\xcc\x95\xab?\xfe\x93\x1f>=}\
+\xaa\xeb\xe0\xb7%\xbe\xa5\x9e\xe7\xdey\xf7\xda\x97_\xa9\xed\xbfx\xe9\xf2\xf4\
+\xa9\x93?\xfe\x93\x1f\xca&\x89\x13\x8d\x8e\x8e\x8a\xb7.\xa5\xf2^\xbf>\xb7\
+\xb0x\xfb\xf9\xe7\xce\x8e\x8d\x95\x00\xa0Z\xab_\xba\xfc)\x00\x9c8~,\x11b\xd1\
+z\x82\xe1\\\xfc\xf4\xef_\xf8P\x1d\xd5\xa5\xa5;W\xae^;\xf3\x9dg\xd5v\x8a\xc23\
+W\xae^\xf8\xe0\xa3D\xef>\xbex\xe9\x95?\xff\xd9\xe8\xe8\x88(\xbc\xbc\xbc\xfc\
+\xfe\x85\x0f\xc7\x0f\xec_^.\xcbj\xcf\x04\xa1|"\x91\x8f,Q\x14e\xb3\x99g\x9f\
+\x9e\xfe\xf2\xab\xd9\xd5\xb55\x191\x94p\xad\xc8C\xd4\xc1S\xd4\\j\xfa\xc6Q\
+\xd9lf\xfa\xd4S\xa3\xa3#\xea\x1b=\xa0\xd9\xde\xd0\xda"\xd2\x00@\xe4k|0\x17J\
+\'q\xf5\x16\xaaI\xdb{\x86?\x1e\xed\x95\x97f\x07\x8b\xbb*.\x94\xb2J\xc5\xcf\
+\xe5\xb2\xcf>\xf3\xf4S\'\x8e\x13B\x96\x96\xee|\xf0\xe1\xc7\xe5\xf2\x8a\x10G\
+\xd5qq\xe1\x83\x8f\\\xc7y\xfe\xb9\xb3\xa3#\xc3\x84\x90Z\xad\xfe\xc9\xa7\x9f\
+\xf9\xd5\xea\xaf\xff\xbf\xdf\xfe\xdb\xbf\xfa\xa5Hlt\xdf\xc2o\xbc\xf9\xfb\xbf\
+x\xf9g\xae\xeb$\\\x04\xad\xb5\xa3n\x8d\xbc\xf0AK\x83\x9e\x7f\xeelit\x04\x08Y\
+]]\xfb\xe4\xd3\xcf\x00\x81qF)m6\x9ba\x18\x9e8~\xec\xd9g\x9e\xce\xe7sqL\xbf\
+\xb8\xf6\xe5\x07\x1f~t\xee\x9dwGGGR\xae\xfb\xec3\xd3\x88\xb8\xbc\\\xbe\xbb\\\
+\xcef\xb3\'\x8e\x1f#\x848\x8eM:\x1e\xda\xe5y/]\xfe\xe4\x83\x0f?\x06\x80\x13\
+\xc7\x8f\x1d9|\x88\x18F\xadV\xfb\xe4\xd3\xcf\xca\xe5\x15\xd1_\xc7qT\x13\xf2\
+\xc2\x07\x1f\x8d\x8e\x8c\xbc\xf4\xe2\x0b)\xd7\x8d\xa2\xe8\xd3\xcff\xca++o\
+\xff\xcb;\x13\xe3\x07\n\x85.\x1b\x80\xa9.\x94\xa5;w\x97\xee\xdc\xdd\xbfo\xef\
+\x89\x13\xc7S\xae\x0b\x84\xdc\xbcy\xeb\xea\x17\xd7f\xae\\u\x1c\xe7\x85\xef=\
+\xbfi\xac\x14\x83\x1aU\xe5\x86d\xbc\xcdFq\xd8\x10w\xf1\x90\xc49\x7f\xef\xfc\
+\x85/\xae}\t\x00\xc7\x8fOM\x1c8\xc0\x18\xf3\xab\xd5k\xd7\xbe\xba\xf4\xc9\xa7\
+\xad\x16\xb6=Z\x0b\x8b\x8b\xe7\xdeyW\x8c\xff\xd1#\x87\xf2\xf9|\xad^\xff\xe4\
+\x93\xcf\xae~q\xed\x8d\xdf\xbd\xf5o\xfe\xf251\x14\x8cq\x00XX\xbc\r\x8b\xb7GG\
+G&\x8f\x1e\x89\xa2x\xfc\xc0~u`\xa5\xf3\'\x8ec\xd34\x8e\x1e9\x94\xcdxw\x97W\
+\xda9\xdc\xef\x0b\xc1\x8d\r\xbc\x89j\xb1\x13B\xf6\x8c\x95\xa6&\x8f\x14\n\x05\
+\x99\x9ba\xd78\xdcw1\xc8cZk\xed\xd0\xdb\'GX\xaa0nX\xf7\xcfw\xc6\xe3\x9a\xcc8\
+\xc6\xfa\xbe\xe0\xeadF{U\xb2S\xc5]\x15M\xc6\x98i\x1a?\xfb\xe9O\x10Q\xbc\xe8L\
+\x08\xd9\xb3g\xec\xf9\xe7\xcf\xfe\xf3\x1b\xbf\xbb>7\xf7\xe2\xf7\xbf\xa7\xea\
+\xb5\xe38\x7f\xf6\xa7?\x16)\xf4D\xc9C\x87\x0f\xbe\xf1\xe6[++\xab\x17/]\xfe\
+\xc1K\xdfW=\x00\x9d\x85\x0f\x1e\x9ax\xf3\xcd\xdf\xaf\xac\xae\xce\\\xb9z\xe6\
+\xf4\xb3\xf7m\xa7P\x96\xf5\xf5\xca\'\x9f~\x0e\x00/|\xef\xf9cS\x93\xe2A{\xdf\
+\xde=\xfb\xf6\xed\xc9\xe7\xf2\x8ec3\xc6\xa6&\x8f\x96J\xa3\xe2\x15$J\xa9a\x90\
+\xe9SO-,.\xde\xbe\xbd47w\xe3\xd8\xd4\xe4\xd1#\x87E(\xc8\xdd\xe5r6\xe3\x9d:yB\
+\xbe\xdf\xa8\x9eTFd\xae\xafW\x84\xb2\x8b\x93\x12B\x0c\xc3\xd8\xbbg\xec\xf0\
+\xa1C\xff\xfc\xe6\xefVVV\xcf\xbf\xffA\xa2\xbf\xd9l\xf6O\x7f\xf2#\xc7qD\xe1\
+\xe1\xe1\xe1\xd7\xff\xdb\xff\x0b\x00\xf3\xf3\x8b\xf9|\xbe\xab\xbep\xce\xb1\
+\xfd\xec2y\xf4\xc8\xf7_\xf8\xae\x18+\xd1\xc1\x91\x91\xe1?\xbe{\xfe\xd2\xe5ON\
+\x1c\x9fR\xf7\xbc\xe7\xca/\xa8\x1a\xe6\x9bt\xfe~\xa3Z\xa9\xf8B\xd9_\xf8\xde\
+\xf3\x93G\x8f\x08\xc1-\x16\x0bc\xa5\xd1\xf7/|\xb4^\xa9\x00\x00\xb6\x9d\xf5\
+\xef\xfc\xf1=\x00\xf8\xfe\x0b\xdf\x15C\x11\xc7q:\x95z\xe9\xc5\x17\xc20\x9c\
+\xbbq\xf3\xf3\x99\xab\xcf<}\x8as\xce\xda\xcb\xe3OO\x9f\xfa\xdew\x9f\x93q\xd6\
+28/1\'\x89\x96\x0c\x0f\x0fy^\xfa\xde\xba\xbf^\xa94\x9b]%^t\xaa\x1d\x88\xa2f\
+\x17h\x9b\xf0\x85Ba\xac4z`\xff\xbet:\x9dHN\xd9\x7f@4\x8f\x9d\xd8\xffZ\x18\
+\xda\xe2\r\xd2\xae?\x18\xb1\xdcta\xa0\xd4\xbeq\xe5\x86\\,\xea\x1d\xfeh\xf4\
+\x7f\xc1ug;\xf2\xa4\xb83\xc6\x84u\x13\xc7\xb1\xc8R\x14E\xd1Xi\x14\x00\xc20\
+\x12O\xd0\xf2a\xfc\xe8\x91C\xd9l&R0\x08yf\xfa\x14\x00\\\xfd\xe2\x9a8\xb6\x7f\
+\xe1\xe9SO\x01\xc0\xec\xf5\xb9\xce\xf6\xb4n\xf8\xf6\x0f#\x9a\x17\xc7\xf1\xec\
+\xf5\xeb\x0004T<tp"nC)\x1d*\x16-\xcb\x94\xc9\xcb\xd2\xa9\x94\xf8\x938W\x1c\
+\xc7{\xf7\x8c\x01\x00c\xad\x15\xbc8\x8e\x85i\x89\xed\xb0\xbf\x8d\x15\xd5\xcd\
+\'\xa5\x94^\x9f\x9b\x03\x80\xe1\xa1\xa1\xc3\x87\x0en\xea\x82A\x9eyz\x1a\x00\
+\xe6\xe6n\xc8s\x89c\x8f\x1c>\x84\x88\xb2d*\xe5\xee\xdf\xb7\x0f\x00\x82\xb0\
+\x8b`\xa9\x02\'\xbe9u\xea)9\xfe\x82\xc9\xa3GFF\x86\x01\xe0\xab\xd99\xd1\xfe\
+\xd6\xb1\xca2\xa9\xe8\xbb\x14\xcd\xf6JF\xcf\x94\xa6\xbc\x9d\x0eH\x8e\xea\xe1\
+C\x07\xd5|\x90\x86aL\x1e=\xac\x16\xbe\xbb\xbc\\\xad\xd6\xb2\xd9\x8c()\x1a)\
+\xca\x1f:8\x01\x00\x8b\x8b\x8b\xe2{J\x19\x008\x8e\xf3\xec3\xd32\x97\x9c\xa8J\
+\r\xe9\x93oTJ\xcb\xdau\xdd\xbd{JG\x0f\x1f\x9c\x18?04Tp\x93\ti\x93\xeb\xa5\
+\x12\xd7u\x87\x87\x87\x8e\x1c>t\xe2\xd8\xe4\x81\xfd\xfbR\xa9T*\x95J\xe4\xcb\
+\xd4\xfa\xbe\x9dA\x1a\xd0\x86\x92\xda\xb7G\xb1\xcc\xe67H{\xc1\x825\xd6\xde?\
+\xb9O\xa4|*\xbf\xaf\xff\x0b\xae;\xd5r\x87\x8e\x07\xe4f3\xb8\xf5\xf5\xd7w\x97\
+\xcb\xeb\xeb\xeb\xf5\xfa\xa6\xedo\xa2(RcZFFFd@:\xb4\xdf\xad8\xd0~\xee\x9e_X\
+\x18\x19\x1e\xeeS\x98s._Q\xb9{wY(\x97l\x12\xdf\xfcv\xb5x\xad\x9cRz{\xe9\x0e\
+\x00\x1c\xd8\xbf_D\xd4$\x8cS\xd9\x91\x95\x95\xd5\xf9\x85\x85{\xf7*\xcb\xe5\
+\xf2\xe6\x02\\\xc9\xcb\xd1\nM\x11\x16\xba\xf0N\xa8\x8f&b\x85\x931V^Y\x05\x80\
+\xfd\xfb\xf7\xd1v\x848i\xbf.(\xfc\x0ca\x14\xdd\xbd\xbb\x9c\xcbee\x7f\xf3\xf9\
+\x9c\xec/i\xbd\xfa\xc8\xe5hwJ\x8c\xec2\x00\x94FGL\xc3\x90\xcf\x01\xa4\x9d\
+\xf8\xe9\xe0\xf8\xf8\xea\xea\xda\xe2\xed\xdb\xc7\x8fM\xca\x1a\xb8\xb2\x1e\
+\xdb\xd2P\x8e\x00\xc0X;\x1f\xcbf\x0f\xbb\x8ah-\xe7\\\x8ej+\x10^\xc9\xb1\xb3w\
+o+\x8d*\xe7<\x8ec\xb1>\x1c\x85\xd1\xff|\xfb\x9c\xac\x87\x00!\x04\xa28\x06\
+\x80\xdbKw\xd4\xeb\xa4X,\x08\x8b\x01\x15\xe4\xb0$\x12\xc8\x88\x0bC\xf4\xc2u\
+\xdd\x92\xeb\x8e\x0c\x17\x19\xe7A\x10\xd4\xeb\xcd \x0c)\xa5\x9c3\xe4\xadH\'\
+\x83\x18\xc4 \x96i\xa6Rn6\x93\xc9d<\x99\xfdX\xec" \xc4\xfd!\xf2\xc3h\x1e\x0b\
+\xb1\x7f\x138\x07\x02\xbc\xf7\x1b\xa4\xa6\x93\x19,\xb5/\xc6\x95\x96\xd5\xd8?\
+R>U\xb8\xcf\xb6|;X\xdcA\xb1\x1c\xc30\xfc\xdd\xef\xffg\xbd^\xb7m\xbbX,\x1c>tP\
+\xa8\xdeg\x9f_\x81v\xbe=uE\xcbho\xd3!\x14a\xb3\x1c\xb7\x0c\xc9>\x85\x15\x85\
+\xe2\xaa\x17\x81\xb7S\xcb\xa2"\xee\xf2(\xf1\r\xe9\xf6\x9a\xb5\xa8\xfc\xca\
+\xd5k\x9f~\xf69\x00\x88.d2\x1e!d\xb9\xbc\xb2\xbc\\\x16G\x93V\xea\x8fM\xef\
+\x98(A;\xc9\x93\xca\xe7\x0f\xb9\x12\xd8\xd9\xdff\xd0L\xa7S\x9b\x97\xfb6<\x0f\
+\x94\xd2v \xcc&\x81K\x94T\xc7J\x0e\x97\x10Y\xc30\x18g\x00\x80\x1c\xd5i2a\xb9\
+\xf3\x8d\xd8G\x99l\xabg\xe0\xa3\xecB\xfbO\x9b^\x91\x97\xad\x95\xa3\x11\xb57\
+\x85\x88\xe2xy\xb9\x0c=h\'\x8bf\xed*A\xadV*;i\xe7]\x1116DI\xac/sc\x89\t\xc0\
+\xb1\xedB;"\xa8+\xf2X5\xa1|*\x95\x92\xdev-\xeb\xdb\x1f\x16\xdc\xa3\xcd2\x00\
+\x00\xf6{\x834S:\x0e\x03\xfc\x9a\xb1\x7f\x8b\xc7\xad\xe0+J\xb1\xe7n\xda\xf9\
+\x03\xf7\x8d\x94\xdf\xd9\xe2\x0e\xed;\xff\xc3\x8f.\xd6\xeb\xf5\xb1\xb1\xd2w\
+\x9f;c\xb6\xf7O0\x0cC\x88;\xdb\x9c!\xb2\xd9l\x8a\xb4\x85mC\x189\xe72\x9a-\
+\x9dJ\xa9Z\xdcY\x981\xd6h\x88\xd4N\xe0\xa57\x854Iq\xef4\xa23\x9e\x07\x00\x8d\
+FC<hwF.\xaf\xae\xad\te\xff\xdew\x9f\xdb\xb7w\x8f\x90\'!\x94\xcb\xcbeB6t\xd30\
+\x88\xec\xa00\xae\xf9\xe6HA)\xee\xc5Bay\xb9\xdch4d\x02U\xf9`Q\xab\x89\xd4\
+\x83\x90\xcf\xe5\xd4\xfe\x1a\x84\xa8\x99\xd48\xe7b\x19S\x95K\x15U\xf8\x1a\
+\xcd\xa6\xa5d\xc9\x1750\xc6\xaa\xd5\x1a\x00x^Z8LZ?\\{\xbe\x90\x12/\\C\x9c\
+\xb7\xa6\x1f\xec\xd8\xabS\x9e_\xfej\xedQm\xcaL\xc8\xb2\xa9\xf27\x12n\x19qU\
+\x94FG^z\xf1\x05y\xe5H\xa5\x96\xc3\xb8\xb1zA@\xa6ZK\xfcRD\xd9\'D\xb5\xe2\x13\
+\xe9\x97\xf9\xa6H\xc7.\x83&\x1d;B\xd9\xd5\x84\xfe\x83$\x94\xd7l\x0f0\xf6\xe7\
+\xc4\xfa)\xa5}\xde \xdd\xd7\xf9\x06i\'\x9c6h\xb5\x15L\xc99P\xd6\xbd6\xc3Jy\
+\xc3\x87\xef[\xdbN\xf5\xb9K]\x10\xff~=\xbf\x00\x00\'\x8eMY\xca\xb6\xc5\xabkk\
+jay\xec\xe2\xed%K\xd9\x01G\x14\x98_X\x04\x80L\xc6K\xa7S\xea\xfad\xd7\xc2\x0b\
+\x8b\x8b\x00\x90\xf1\xbct:\xa5\xd6\xbc\xa1V\xb8!\xee\xe2V/\x16\x0b\x00\xb0\
+\xb0x\x9bs.w\\2\x95\xec\xd8w\xee,\x03\xc0\xd8X\xe9\xc0\xfe}\xd2L\xe6\x9c\xcb\
+\xb7\x1f\xd5\xb4|\x00@\xda\xf9\xb96\xce\xab\x9cT\x8c\xcc\x90rR\x99\\W\xedo\
+\xb1X\x10R.\xbb\xa0\xa6\xe1\x15%\xa1e\xb9\xf7\xfc-\x08!by\xb0^o\xac\xac\xae&\
+\xa2\xf7\xc20\x14\x99v\xf2\xf9\x9c:\x03U\xfd\x9a\xda\xcdZ\xad~o]\xf4\xb4\xfd\
+\xb3\xb6\xbb\xd39\xaf\xc8\x87\x95ba\xa3\x83\x89}6\x96\xee\xb4\xf6I`\x9c3\xc6\
+\x86\x87\x86\x00\xa0\xbc\xb2\xda>\xcbF\xcd\xd5Z-\x0cCuJ\x86\xb6jK\x97\xfa\
+\xa6\xce*\xe6\xb60\xb4\xe5\x16t\x12\xcf\xf3\xd2\xe9\xb4X\x17\x95\xbb;\xc9m\
+\xfc\xe4Q\x9e\xe7%\x0e\x91\t\xee\xb5\xb2\xef\x08hc\x99Gu }\xc3\x1f\r+]<8Hmq\
+\xe5\x86\x8cM\x88Y\xcf{\xce\x1b><\x88\xef~G\x8a;\xb6\xc3\x9c\xa5\xc5\xea86\
+\x00\xc8\xfb\x16\x11\xc30\x14\xd1)\xd0\xe1/\xbe{w\xf9\xc3\x8f.\n\xbd\x13Z\
+\xf9\xe5W\xb33W\xae\x02\xc0\xe1C\x07\xf9\xe6u\xbc\xce\xc2_\xcd^\x9f\xb9\xf2\
+\x05\x00\x1c:4\xd1i\x9a\xb5\xf5*\xf1\xbfx\xe4\xf0\xa1L&\x13E\xd1\xff\xf8\xc3\
+\xbf\x88\xa7\x01!\x7f\x1f}|\xe9\xda\x97_I+\xaf^\xaf\x0b\x03V\x1cu\xeb\xeb\
+\xf9\xc5\xdbK\x00 ,w\xa15\x84\x18\x00\xb0vo]x9\x14}\xdf8\xa9\xa8\xf0\xc0\x81\
+\xfdCC\xc5(\x8a\xfe\xe7\xdb\xe7\x82 \x94\'\xbd\xf6\xe5\xec\xe73W\x01\xe0\xf8\
+\xb1)q\x88\x1c\x1cssfyQ\x9f\xecJ\xe7o\xd1\xb2\xdb\xdb;|\xfe\xf1\xdd\xf7\xcb\
+\xe5\x15\xa9\xb0\xf5z\xe3\xdd\xf7.\xc4q\xecy\xde\xa1\x83\x13\xa2S{\xc6J\x00\
+\xf0\xd9\xe73Q\x14\x89o\xc2(\xba\xf0\xe1G\xed\xc6\xcbq\xebw\x01\xb4F\xf5\xc8\
+!\xc7\xb1\x13\x1d4\x0ccn\xee\xa6\x0c\xb7\x17\x17I*\xe5\x8aw\xa9\xce\xbf\xff\
+\xa1\x9a\xc2\xe5\xde\xfa\xfa\x87\x1f]\xfc\xe3{\xef\xaf\xafW\xa4p\x03\x00\x01\
+\x920\x9f\xe5\x10\xa9\xfan\xb57\x9b\x95J\x9d\xcdf\xb3\xd9\xac\xf8 \xffU\xc9n\
+F\xca\xba\xea\x8d\xd1\xca\xbe3@.wGb\xac\xb7\x0be\xb0\x1ca,\\g\xcd\xd6\x95\
+\xd9g\x1du\xf0m\xf9v\xaa[FZ|\xc2c{\xfc\xd8\xb1\xcfg\xae|>s\xb5V\xab{^:\x8c\
+\xa2\xa5\xa5;\xb6m\x17\x0b\x85\xf5J%\xa1\x13\xc3\xc3C\xd7\xbe\xfc\xea\xe6\
+\xcd[\xc3#\xc3\xc8y\xb5V\xaf\xd7\xeb\x00p\xf8\xd0\xc1cS\x93r1\xb0\x7f\xe1C\
+\x07\'\xa6&\x8fB7\x8fpg;\xc5\xe7\x1f\xbc\xf8\xc2\xdb\xe7\xfe\xb8\xba\xba\xf6\
+\xf7\xaf\xffJ\xac\xdf\xfa~\xb5Z\xad\x02@\xcau\x0f\x1f>\xf8\xc5\x17\xd7\xea\
+\xf5\xc6\xdb\xe7\xfe(tp\xbd\xe2/-\xdd9r\xf8\xd0\x8d\x9b\xb7\xa4\xbe\x18\x86\
+\xb1g\xac4s\xe5j\x14E\xff\xf4\xdf\xdf\xc8f\xb3\xd5j\xf5/^\xfeY\xaf\x06\xbc\
+\xf4\xe2\x0b\xffr\xee\xdd\xd5\xd5\xb5\xff\xe7W\xff\xb8o\xdf^\xb5\x0b\xd3\xa7\
+N\xee\xdf\xb7\x176\\\xf9\x00\xcaN\xad\x98|\xef\xe6\xfed\xb3\x99(\x8a\xdf\xf8\
+\xdd\xefGF\x86\x1d\xdb\xe6\x88w\xef.\x03\x80m\xdb\xcf\x9fm\xbdW\x85\x88G\x8e\
+\x1c\xbe\xbb\\\xae\xd6j\xaf\xff\xb7\x7f\x1c\x19\x1e\xe2\x88kk\xf7\x00 \xe3y\
+\xf5F\xa3\xff):G\xf5\xa5\xef\xbf\xf0\xde\xfb\x1f\xc8\x0e\n\x8fS\xbd\xde(\xe4\
+\xf3\x15\xdf\x97\xe5\x01\xe0\xc4\xf1)\xbfZ\xadT\xfc\xf7\xde\xff\xa0X,\x88\r\
+\x0b\x85\xd0\x17\x8b\x85l6#\xbd\xea\x00@\x0c\xa2F\xc2t\xb6A|)&K9\xef\xca%\
+\xd6\x8d\x85\x90\x8eiJ\xf5\xc9\xa8\xf16]\'\x12\xcdv\x86\xd6\x97\x906\x81\x00\
+"\xf6z\x83\x94X\xae\xba\x81Fo0\xae\xb4\xde\xc1\xec\xb7\x8e\n\x90\x19\x99\x1a\
+\xc4w\x0f;Z\xdcU\xcb\xfd\xd8\xd4QD\xfc\xf2\xab\xd9\x9b\xb7Z\x13\xe9\xc1\x83\
+\xe3\'O\x1c\xbfx\xf9S\xf5\x10\xf1\xe1;\xcf>\xe3\xfb\xfeg\x9f_YZ\xba#\xbe\xb1\
+m\xfb\xf8\xb1\xc9\xe3\xc7\xa6Py\x0b\xb1O\xe1cSG\xa7&\x8f\x0er\x07\xaae\n\x85\
+\xfcO~\xfc\xc3O?\x9bYZ\xba\xb3\xb8x[|\x99\xcdfO>u|dd\x98s\xfe\x83\x97\xbe?s\
+\xe5\xeary\xe5\xca\xd5k\x00\xe0y\xde\xf3\xcf\x9dI\xa5\xdc\x1b7oI\x13Y\xd4\
+\xf3\xd4\x89\xe3_\\\xfb\xb2Z\xad\twv\xd7\x93\x8a.d<\xef\x95?\xff\xb3O?\x9b\
+\xf9\xf2\xabY\xd9\x85L\xc6\x9b>u\xf2@;\xe6G\xd5/\xa2\xe8\x0b\xe9\x88\xea\xe9\
+\x8a\xd4\xafL&\xf3\xa3\x7fu\xfa\xf2\'\x9f\xden\x9f\x08\x00J\xa5\xd1S\'O\x14\
+\x0b\x05in\xef\xdf\xb7\xf7\xf9\xb3g>\xfd|&\x8a"\xe1<)\x95FO>u\xfc\xea\x17_\
+\x0e(\xeej\x07K\xa5Q9\xaa\xb2\x83\x07\x0f\x8e\x9f86\xf5\xd6\xffx\x1b\xda\x0f\
+=\x00\xe08\xce\xf7\xbf\xf7\xfc\xf5\xb9\x9b\xb7\xbe\x9e__o\xedU\xee\xd8\xf6\
+\xf8\xf8\x81\xa7\x9f>\xe5\xd8v\xfb\xf9i\x93\x85\xde\xe7W&\xed\x98Hh\xaf$\xa3\
+\xbaV\xa1\x908J\x9d\xaa\x8d\x07L\xe8\xaf\xd9& \xa7q\xdb?Ni\xef7H\x87\x0e\r\
+\xe2B\xa1\xf5\xbb<j\xd9"}\x1e\x02\xdc\xdc^+\xd5o\x89^e\xa0\xbbw\xbb\x81\x88"\
+\xe8[\x04\xa4\xabY\xa2\xca\xe5\x15D\xcc\xe7s\xc2.\x93\xef\xf8\x08#\xf4\xb7\
+\xff\xfc;\x00\xf8\xd1\x0f\x7f0~`?!\xe4\xf6\xed%\xc6\xb9e\x9a\x85B^j\x99X\x7f\
+\xe3\x9c\xff\xf77\xde\xeaU8\xd1\x18av\xc9\xff\x15\xb6\xbf\xba\x9b\x0c\xdf\
+\xbc\xef6c\xec\xde\xbduD\xccd\xbct:-wR\x16\xe5\xeb\x8dF\xa3\xde0-\xb3\x90\
+\xcf\x1b\xed-\xd3H{\xcb1\x11\xc6#\x96\x16\xd6\xd7+@\xc8P\xb1@\x08\x11\x1d\
+\x94\xbe\x149\xf9\xa9&\xe1\xd2\xd2\x1d\xc6\xb9\x97N{^Z\xf5\xe7\x88S\xab\xfb\
+\x86\x8b\xf0\xc1(\x8a8\xe7\xa6i\xca\xcd\xa3-e\xdbb1\xf8a\x18\n\xef\x8a\xdc\
+\x80\xd44\xcd0\x0cWVV\x19c\xc5bA.u\xcaq\x90+\x07\xcb\xe52r\x14\x81\x7f\xe2{\
+\xb5UDY\xb7\xec5\xaa\xb2\x83\xa6i\xc6q\xbc\xb2\xb2J\x19\xcde\xb3Rp\x15\x9f\
+\xd5FD\x8da\x18\xe5\x95UB\xc0 \xc6\xde\xbd{\x12\x15\x9a\xed\xfdBe\x98\xf9\
+\x80\xd7\xa4\xfaoB\xd6\xd5\xbbL*\xb8:yhY\xdfq\xc4\xfe\xcd\xd8\xff\x1a\x08 \
+\xc7 \xea\x99\xf8\xa50\xfe\xbc\xf0\xa3\xf6\x019\r\xee~\x84,\x04\x00D\x08b\
+\xec:U\x10\xc3*\x8c??X\x16x\x80\x9dk\xb9K\xd4\xfb\x81\x10R*\x8dB\xfb^J\xdc9j\
+\x04\x8bx\x8buddX\xdc\x81\xd2/!oHy7v\x16\x06e\x0b\xe0\xc4\xe1\xd0V%\xd8,C\
+\xa0\x84\xcd\x08\x15\x13\x99L:\x9f\xdc\t!\x19\xcf\x13A b\xda\x90\xc6\x9d<\
+\x97\xf8`Y\xd6\x9e=cR\xf4\xa5\x1a\xaa\x16\xb7()\x971GGG\xa4\xd3I\x15>i\x81\
+\x1a\x9b\xcd\xf6\x84D&\x86:a\xe0\xcb\x19TL\x15\xf2\\\xb2N\xd9\x12\xa9\xf2\
+\xa3##\xd2}!\xcf([%\x97\x8e\xfb\x8c\xaa\xec\xa0(022,V\xb0\xe5h$\xe22e\xfb\
+\xc7J\xa3\xea\xd8\xaa\xd7\x8c\xfa\xe5\x83^\x87jU\xb0Y\xd3\xbb\x96\x07-\xe8;\
+\x13\xe41\xad\xb5\x1e\xbec\x96\xcc\xd4,\xf1\x86\x8e\xdcW\xd9A$\xa5a\xad\xa8e\
+\xca\xba+;\x00\xa4\x8b\x13\x83+;\xec\\q\'\xcaV\x8af{\xdfw\xe8\xb8g\xa4\x14\
+\xaa\xc7\n\xdb\xdch\xef\x0bC\xdaq\x11\xd0V\xdb\xfb\x16N\x98\x84\tq\x17%Ue\
+\x91\xb3\x8bT\xa2M\x9e\x90\xf6\x8bE\t\r\x15z\xd7^Dm\xe9\xb5\xd4P!XR\x7f\x13m\
+K\x9cT\x1d\x1fB\x88\x0c\xc6\x00\xc5(V\xc5N\x0e/Q6\x1a\xed\xfc\td<\x89Z\x95\
+\x94]\xf57\x92M\x923\x8d\xb4m\xe5\x9c\x94pp\xdfwT\xd53\xca1\xc1v\x80\xa3\xda\
+69\xaf\xc8\xb9M\xf6QvM\xfc(bH\x1fN\xdf\xd5\x91I|\xd0\xec2b\xffk\xe4\x14\x88\
+\x88\xdc\xed\xb9\xef]\x9f\xc4/\x12d!\xad.\x88\xcf\x1c{\x87?\xda\xe9Ta\x10\
+\xdf\xbd\xd2\x80\x07*\xbd} J8\x1a\xb4%L\xfeI \xfeW\xdaq\xbd\x0cO\xb9\xa2%\
+\xad\xf2\xfb\x16V\xdd\x05\xaa-\x0f\xdddH\xfeU\r\x04T\xeb\x94\xee\xdaD\x17\
+\xa0\xbd\xbc)C0\xa1\xc3\xd0\x96\xbai(;>\xcbo\xa0#\xc6_:1TEV]:R\xd4\xe4l\xd7u\
+]\x91(\xe1"r\x90\xe5\x9f\x12\x05d\xfb\xc5\x97\xea8\xc8\xf2\x89\xb3\x0f8e\xaa\
+\xb3\x05\xb4\x05Z\x9e]F\x07\x89^\xa8C\xa16O}h\x93~\x9e\xc4U\xa4\xd1l\x80\\lj\
+\n\x001\xedc\xb6\x1f\x1e\xe8\xad\xa5\xcaMD\xda\xae\xad\xc7;P\x00\x99\xde\xd9\
+\x1f{\xb1\xe3\xc5\x1d\xda\xaf\xb9w\x15wi$\xaa\x86\xa7e\x9a\xb6mK\x11\x97J\'m\
+j)\xf4\xbd\nw\x8a\x8e\xaa\x02R\x98\xc4!\t\x19\x92\x93\x81:\x8bt\xfa\rd\r\xaa\
+\x9c\x89\xa3\x84\x16\x93\x0e\x07\x02\xd9\xbc.\'\xf2\x1c$,VqHb~\x92\xe6\xbf*j\
+\xb2\x9e\xce\x13A\xdblGD\xc7q\x84\xc1\x9bxB\x92?Pb\xbe\x91n\x93\xc4\xef\xa5\
+\x1a\xd1j%\x89QU\x15_\x8eF\xe7\xd0\x11\xe5\x81\x83l~\xcf\xabs(d\x19YI\x9f \
+\x19\x8d\x866\x96\x91E\xc2l\xe7\xbc\xbb\xd9\xeedJv\xba\xcb\x0e\\\txT\xa5\r\
+\xf9BF\xcf\xf0G\xdb\x1bv\xb2\xa5\x07m\xe7\x8e\x14wi\xbaB\xfbN\x96\xcf\xf8\
+\xa0\x88\xbb\xf4\x81\x88$!\xf2pS\xd1\xeb\x84\xa6\xa8\xce\xdf>\x85Uq\xef\xd3H\
+\xa9\x0eRGT\xcb\x91lv1w\xedBb\x92\xc0\xcd\x8b\xb7j\r\x89\xa3\xe4\x04#\x95W=i\
+\xaf\xf9I\x15\xf4\xce\xcf\xea7\xe2_\xb1\xbe\xaa\x9eB\x95\xec\xce\xe1\x92\xe3\
+\xd0\xb5\xa7\xea)\xb0#\xcf\xc1 \xa3\xda9t\xaaUn\xb4\xa3Y\xd4\xc3\x13S\xac\
+\xfc\x93z"\x8df\x03d2H\xa6o\xbe\xc6#\x83T\x16U\xe6d\xa6\x8b\xdeid\x06\xad-\
+\xc1\x8e\x14wP\xf4]\x8a\x0bl^G%\x84\x88/\xc5\x83\xb6(66V"\x84x\x9e\xa7n]F\
+\xda.\xef\x84\xb2\xf7),o\xfb\xfe\xe2.?c{!4\xa1\xb3\tYI<\x07$\xca\xa8\xf5\xa8\
+g!\x9b\xc5]=\xbb\xec\x9a@m\x7f\xa2y\x89\xa3p\xb3#\xab\xab\xd2\x19\xedu\x02\
+\xdc\x8c<D\x9eH \xebT\xdb\x03\x1d\x13\xc9 \xbf{\xe7\xa8\x822\x1f\x90\xcd@oq\
+\xef\xfa+$\xc6\xbf\x7f{4O\x1aq\xed\xb6\x88mg\x0cy\xaf|\x8d\x83\xed\xb5\xc4\
+\x9a+b\xf3k\x00\xa0}\xde\x81\xca\xef\xb3\xdc\xdcC4uG\x86BJTk1qgB{-TMik*y<T\
+\x8d#\x84\x88\xe0\xbf\x01\x0b?tk\x13\xba,\xff\x95\xdf\xab\xe2\xde\xf5\\\xb2\
+\xb3\xea\x9f:\x8b\x11%`\xa6sp\x1e\xae\x0b\x89\xa3\xd4\xbe$\xda\x9f8Qb\x02\
+\xeb,\xf6\xd0Mz\xa0\n\x1ft(\xb4\xb2k\x12 \x8f\x83\xbb\x1f#\x8b\x00 \x88xW\
+\xed$\xa6]\x1c\xff\xee\xfdw\xe4@\xde\xbc\xfb1\xd2\x06\xf4\x0f\x7f4\x9d\xe2\
+\xc4\xf3\x86y\xff\xfd=:\xd9\xa9\x96\xbb`\x90\xfb\x93\xb4\x1d\x11\x88(\xd7\
+\x12\xa5\x7f9Qr\xc0\xc2\xdf\xa4\xb5\x0f\xfa\xa7oX\xf2\xd1)T\xff\xd9e\xcb\xda\
+\xf3(\x86N\xa3\xe9\n\xad-\no;e=\xad\xe2\xf4`{-\xd1\xdam\xa1\xec\xd07\xfc\xd1\
+\x1b:\xf8p\xca\x0e;]\xdc\xef\x8b\x14k\xf9\nL\x9f\xb5\xb2\x07*\xac\xd1h\x9e(\
+\x90\x85\xad\x8d\xf4\x10h\x0f\xff\xb8a\xa5R\x03\xed\xb5\x84q{O\xbe>\xe1\x8f\
+\xa6\x93q\xf3\xfb\x1f\xae\xb5\xb0\xeb\xc5]\xae\xb9\xa9\xff\xdbk\xb9\xec\x81\
+\nk4\x9a\'\x8a\xb8\xfa5r&\x82dz\x9a\xed\x83%\x1b`\xcdUd\xad\xad\xcd\xfa\x85?\
+\x8eL\x0e\xf2\x0eT/v\xb3\xb8\x0bQ6\xdb\xa9\x9d\xb0\x1dJ\xd8\xcbl\x1f\xbc\xb0\
+F\xa3y\xa2\xe0\xb4!c\x16{\xc5\xb6\x9bn6\x95\xdb{\xff\xba\x90\xcb\x1ca\x8c\
+\xf7\\\x95\xb53\xa3\xb67\xdc\xed/\x83\xb2\x9b\xc5\x1d\x94\x98\r5\x1a\xaf\x97\
+%\xfe@\x855\x1a\xcd\x93C\xec\xdf\x12\x1b\xe91\xd63\xb6}\xc0\xb7\x96h\xed6\
+\x97\xdev\xda\xa3\x1012}7\xbf\x1e\x84\xdd,\xeer\x05\xaf3\xc4\xf0\x1b\x16\xd6\
+h4O\x0e<\xf2[\x99\xd6{\xef\xc8a{\xc3\x03%\x1b\xe0q\\\xbd%>\x8b\xf0\xc7\xae\
+\xa4\x0b\xe3\x83\x04S\xf6g7\x8b;t\x0b\xd9\x86\xdez\xfd@\x855\x1a\xcd\x13B\
+\xec\x7f-R\xfaR\x86\xbd^"\xf5\x86\x0e\x0fV\xd5M\xe4\x14\x00\x10\x81\xf6\xd8.\
+\xc1\xb0\xdc\xf4\xd0@;7\xf5g\x97\x8b;<\xa0:k)\xd7h4*,\xb8\xc7\x825\x10rL\xbb\
+;d\xdc\xdc\x9eA\xd2\xac\xf3\xb8N\xeb\xad-\x07\xfa\xbc\xb5\x94\x1e\x1ah\x17\
+\xbd\xfb\xb2#\xb7\xd9\xd3h4\x9a\xad\xa1\xe5E!\xad=4:!\x86\x99\x1e\xd0l\xaf\
+\xdc\x00\x14oS\xf7\xdbEo\xa0U\xd9\x01\xd0\xe2\xae\xd1h4\xdda\xcd2\x0f}\xb9\
+\xffu\x0f\xb3}\x9fi\xa7\xef_Up\x8f\x05\xab\xad\xcf\xbd\x82)\t\xc9\x8cL\x0e\
+\xb2*;\x08Z\xdc5\x1a\x8d\xa6+(\xf7\xbf\xee\xb5\xf8ILg0\xff8\xc6~{\x8b\xd4\
+\xde\xdev\'S\xb2R\x85\x87ii7\xb4\xb8k4\x1aM\x17h\xfd\x0e\x8f\xebb\xff\xeb^f{\
+\xba8>Hz\x00Z\xbf\xc3\xa3j\xebsO\xf7\x8e\x95\x19\x99\xfc&\rN\xa0\xc5]\xa3\
+\xd1h\x92 \x8f\xe3j\xcbl\x8f)t\xf5\xa2\x98N&\x95\xbf\x7f\xb2\x01dQ\xec\xdf\
+\x14\x9f\xfb$\x1bH\x0f\x1d2,\xf7!\x9b\xdb\r-\xee\x1a\x8dF\x93$\xf6o"\r\xc5:j\
+\xf7\x8d\xf4\x08\xc9\x96N\x0c\xb2;RT\xb9.\x12IB;\xd9@\'v\xba\x98.N|\xa3\x16w\
+\xa0\xc5]\xa3\xd1h6\xc1i\x83\xd6\xef\x02\xb4\xdeZ\xea*\xc7\x8e7:P\xf8cXa\x8d\
+e\x00 \x00\x94\xf5\x08\x92!\xc4\xfbV\x1d2\x02-\xee\x1a\x8dF\xb3\x89\xd8\xbf%\
+b\x16i\xaf\xdc/\x83\xee\x8e\x84Q;\x8d\x0c\x87\x9e\xf16\xa9\xdcCn\xc7\xd1\x1f\
+-\xee\x1a\x8dF\xb3\xc1F\xb2\x01\xe8\x99\xda7\x95\xdf?\xd0^K\x8d2\x8f* \xccv\
+\n\xbd\xd6Q\x07\x0c\x93\x7fP\xb4\xb8k4\x1a\xcd\x06\xb1\x7fK\xac\x9f\xf6\xcc\
+\xfdBHj\x904\xeb\xc8\xa2v\xf8#\xef\x1d&\x9f\x1e:4\xc8\xe6\x1e\x0f\x81\x16w\
+\x8dF\xa3i\xc1\xc2u\x16\xdc\x03\x00\xec\xedE\xb1\xd3C\x83\x98\xedqm\x11i;i;\
+\x83\xae\xd3\x84\xe9x\x83m\xee\xf10hq\xd7h4\x9a\x162\xd3z\x1f\xb3}\x10o;\xb2\
+\x90V\xe7\x01\x80\x00p\x0e\xdd\xe3m\x00\xbc\xe1o\xb4\x1dG\x7f\xb4\xb8k4\x1a\
+\r\x00\x00m,\x8bW\x8d\xc4[K]\xcb\xb8\xd9=\x83,~\xc6\xfe\xadV\xf6G\x80\xb8W\
+\xbcMf\xd4\xc9\x8c|\x93\x06\xf7G\x8b\xbbF\xa3\xd1\x00\x00\n[\x1b\x00(\x83\
+\xae;V\x0f\x98#\x8c\xc75\xda\xb8\x03b\x1d\x95\xc37\x8b\xb7yxv\x7f\xca_\x8dF\
+\xa3\xb9/\xadd\x03\x00\xbcw\xb2\x017\xbf\xdf\xb4S\xfd*\xa1\x8cR\xc6\xfd\x9b\
+\x049\x00\xc1>\xf16\x85\x03\xa6\x93\xf9\x16\xda\xdd\x1b-\xee\x1a\x8d\xe6\x89\
+\x07\x99L6@Y\xf7-R\x89\xe9x\xc5\x9e9\xc2\x18\xa5a\x10\x85\x94\x90x\xc5\x89V\
+\x11\x08\x01\x88\x19tO\xdaN\xc8 y\x0b\xbe!\xda-\xa3\xd1h\x9et\xe2\xda"\xd2\
+\x10\x008\xf6H6\x00\x90.N\x10\xd3\xeez8g|\xbd\\\x0e\xc2\xa6\x97F\x0f\xee\x02\
+r\xd27\xfc\xd1\xc9\x94\xfa?\x01|+hq\xd7h4O4\xc8)\xad-\x8a\xcf1\xed\x1e\xb3h\
+\xd8\xa9>\xb1\xed\x8dZm\xad\xbc\xcc\x03j\xc6\xf7h\xe0\x031@\x84?\xf6\xd8\xdc\
+\xc3\xfb\xc6\x9b_\x0f\x82v\xcbh4\x9a\'\x9a\xb8:\x8f,\x06\x00\xc6\x91\xf1\xee\
+\xb6vfx\xb2O\x8e0\x04,\x0c\x8d"\r\xc2\xf5yB\x0c\x02\xc0z\x87?\xa6\n\x13[`\
+\xb6\x83\xb6\xdc5\x1a\xcd\x93\x0c\xb2\x90\xd6\x97\xa0\x1d\xb3\xd8\xb5\x8c\
+\x95\xca;\xd9R\x9fJ,\xd3J\xe5G\\\xabF0\x14\xeb\xa8\xbd\xc2\x1f\r;\xf5\xadg\
+\x7f\xec\xd9\xaa\xad9\x8dF\xa3\xd1lCb\xff\x16p\xda\xda"\xb5\xc7\xbe\xa6\xfdc\
+\x16\x9b\xb5z\x18!\xd0\x8a\x83k\x00&\x01\xa0\x0c{m\x91\xda\xff\t\xe0\xdbE\
+\x8b\xbbF\xa3yB\xe1\xb4A\x1b\xcb \xb6\xbe\xeb\x11\xb3\xe8dJvz\xa8W\ra\x106\
+\x1a!\x82\xe1\xf2\x15\x821\x82\xc9\x11\xe2\x1e\xdbq\xd8\xe9\xa1\xfeO\x00\xdf\
+.Z\xdc5\x1a\xcd\x13J\\\xb9\t\xc8\x81\x00\xa3\xd8+f1=t\xa8\xfb\xc1\x08\xf5Z=\
+\x8eb\x0e\xa6c4\xcd\xa8\x8c`\x10\x021E\xc68\x11\xeb\xb2\x84ld\x17 \xc4\x1b\
+\xd9\x8auT\x89\x16w\x8dF\xf3$\xc2#\x9f\x05\xab\xd0\xda\xb1\xbaG\xb2\x81\xdc>\
+\xcb\xcdv\xf9\x03B\xbdV\x0f\x9a!1m\xd3`V\xb4H\x10\x91\x18\xe2\x05(;]\xb0\xd3\
+E\x03x\x1cV\xe3\xc0\x17:\xef\xe6\xf6>\x8a\xa4\xed}\xd0\xe2\xae\xd1h\x9eD"\
+\xff\xa6H2@{\x98\xed\xc4\xb0\xbc\x1ef{\xb5\xe2\x87alX6g\xd4!\x15\xc2*HL\x02P\
+\x0f\xb8\x9b?\x98\xdf3\t@\x00\xc34\xd0\xc6\xca\xd7\xcd\xca\x1db9\xde\xd0\xa3\
+M6\xd0\x89\x8e\x96\xd1h4O\x1c,\xb8\xc7\x83u \xfd\xcc\xf6Ta\xbc\xeb\x8e\xd5\
+\xcdz\xa3\x19\xc4\xa6e3Fm\xdb\xb0\xe3;@\x08\x01\x88(\xc6\xcc\x05\xf46f\n\x02\
+\xae7\x04h{C\x07\x1fQ\xd2\xf6>h\xcb]\xa3\xd1<i`\xec\xdf\x14\x9f(\xc5\xee9\
+\xc2,7]\x1c\xef<\xb2\xeeW\x83 \xb2,\x93R\xea\xb8i+\xba\x05\xbcI\xc0\x00\x80z\
+\x88\x86\xe1\xd4k\xcdt\x14G\x0c\xc2f\x0c\x18\x130Ccd$\xdf\xa5\xaaG\x8d\xb6\
+\xdc5\x1a\xcd\x93\x05m\x94yT\x05\x02\x9c\xf74\xdb\xbd\xe2Ab$m\xdfZ\xa5\xda\
+\x0cbbX\x9c\xa3m;\x16\xfa$^A @ \xa0\x10\xc6\x9c#\xb5\\\xbb\xdeh\x86A\xc0\x91\
+Sn0p\xd3#\x07\x82f\xf0\xe8\xbb\x95D[\xee\x1a\x8d\xe6\xc9B&\x1b\xa0\x0c\xbb\
+\xe6\x083\x9d\x8c\x9b\xdf\x97\xf8\xb2z\xaf\x12\xc6\xdc4\rJ\xb9\xe3\x18H\xc3\
+\xb8\xb9\xe8\x1a1\x80\x81\x08\xf5&\xb3,\xdb\xf6F\xc2\x88\x05\xf7\xd6\x00\x11\
+\x00\t\x10b\x18\x84\x185\x1a\x8f\x1f=BH\xd7\x08\xc9G\x85\x16w\x8dF\xf3\x04\
+\xc1\xa3*\x8fkp\x9f\r\x92\x8e$6H\xf2\xefU\xc2\x88\xdb\x96\xc1\x81\xd86\xf08\
+\n\x9b\xf7rF\x15\xc0\x00\x02\x8d\x80s0\x0b\xa3\xc7\x9auZ\xaf\xdd3\x0c\x13\
+\x08\x80\xf0\xe8\x03\xb4vd\x8db\xdb\xddR\xb7\xbb\x16w\x8dF\xf3\x04\x11W\xe7\
+\x11\x91\x00\xc4\xb4\xbb\xd9n\xa5\nNfT\xfe/r\xee\xaf\xfb1#\xa6\x811\x07\xc7\
+\x02\x03Y\xc4\xa9\r\x15b\x00\x00\xa1\x8c\x85\xd4\xcc\x8fLF1A\xd3v\xd2i\xce\
+\xb88\xb4]\x05\x98\x96i\xda[-\xb6Z\xdc5\x1a\xcd\x93\x02m\x96is\x85\x00\xd0^9\
+\xc2\x0c33zl\xa3|\x1c\xd7\xab\x8d\x98"\x00G\xc3t-B\x83&\x05\xb0\x1d\x84\xa8A\
+\x00\x109%y\xaf\xb87\x88\x80q\xe6\xa6\xbc\x94k\xd5\x1au\xc68p\xde\xaa\xd20FJ\
+%\xc3\xd8\xea\x05N-\xee\x1a\x8d\xe6\xc9\x00Y\\\xb9\t\x00\xfd6H\xca\xef\x97o-\
+EAX\xad\xd4\x88i\x13\x02\x86\xe9\xd8&\x0f\x1b5\xd3v\xf3\xc3\xc5\xb5\xf9\x8f3\
+\x16\x02"\xd8#\x86\xb1\xa7\x11r\xc6b\xd7\xcb;\x8e\x15\xd6\xd7\x81\xc5\xc4 N*\
+M\x00\x1c\xc7V\xd5#\x86\x00\x00\x1c[IDAT\xcd\xe6s\x8e\xb3\xd5q\x90\xa0\xc5]\
+\xa3\xd1<!\xc4\xf5;\x9c6\t@\xccz\xbc\xb5d\xb9r\xaf\xa5\xa0\xd1l6C$\x0495,\
+\xc7"4\xac7\xacT*?4\xd4\xac\xad\x19\xacA,\xc0\xd4\xbe\x88\x17\x83\x089\x8b\
+\xbd\xdc\x90\x014\xaa\xfb\xeb\xabw\x0c\xf4\x87F\xc6\x8a{\x0e\x13\xc3\xd8\xe2\
+ET\x15\x1d\n\xa9\xd1hv?\xc8i\\]\x00\x00\x04`=\xc3\x1f[{-\x05\x8df\xad\x16 \
+\x07D0L\xdb1Y\xb3^\xb3\xdcTax\x18\x00\xd6\xee|e[\xc8\xdd\xf1\x08F\x1b!c\x8cz\
+\xb9a\xe0\x11\x0b\x1ak\xe5\xafM\xa8f\xd2fa\xec\x88a\x9a\x8fQ\xd9A[\xee\x1a\
+\x8d\xe6I\x80\xd6\x16\x90\x85\xc2\xdb\xde\xd5l7\xed\x94\x9b\xdf\x0f\x00\xcdz\
+\xb3^k\x1a\xa6\xc1\x18\xb7,\xcb"\xb4Q\xad\xa7</?<\x0c\x00\xeb+\x0b\x166\x8d\
+\xf4\xc1\x08\x8a\xf5F\xd30\xedt:\x8b,\xc0\xa8\xb9\xba\xbah\x19\xcd\x8c\x8b\
+\xb9\x91q\xd3\xf6\xb6\xbc\x8bI\xb4\xb8k4\x9a]\x0e\xf28\xae\xdd\x86\xbe\xde\
+\xf6\xf4\xf0QB\x8cz\xb5\x1e\x04\xb1a\x10DbY\x96cA\xa3\xd6He2\xf9\xa1!\x00`4\
+\xf2Wn\xe6\xf2\xfb\x989\xd2\xacU,7g[\x16`D\x83\xba\xbf~\xdb"\xcd\\\x1a,\'\
+\x9d\xee\xbd\x8f\xf6V\xa2\xc5]\xa3\xd1\xecrb\xffk\xe4\x14\x00X\x0fo\xbb\x9d\
+\xca\xbb\xd9\xb1Z\xa5\x16\x84\x94\x00\xe3\xc42\x08\xd8&\xd6|\xdf\xcb\xe5r\
+\xc5\xa2(\xb6v\xf7\xa6\x97\xdbc\xba#\x8dF\xd5J\x0f\x01\xa7&\xc1ze\xad^]6I\
+\xb3\xe0\x19\x00\xe0\r\x1d\xe9|\xb5\xf5\xb1\xb0-\x1a\xa1\xd1h4\x8f\x08N\x9bq\
+}\t\x00\x01I\xf7d\x03\x04R\xc3S\xb5J\xad\xd1\x8cL\x03\xc1\xb0\x0c\xe4&\x81f\
+\xa3\x9e-\x163\xb9V\x9e\xde0\xa8\x1b\xc4\xb2\xd2CQ\xb3j:ydQ\xca\xb5\xabkw\
+\x9b\xf5\x15\xcbh\x16<\x13\x11\xacT\xc1\xcd\xed\xd9\xd2\xee\xf5F\x8b\xbbF\
+\xa3\xd9\xcd\xc4\xfe-\xe0\x0cH/o;\xb73\xfb\xc3\x00\xc3\x98\x99&\x01b\x19\x84\
+\x13\xc0(hd\x8b\xc3\xe9L\xcbu\x8e\x88A\xbda\xa7\x8aA\xc3\'N\x11\x90\xa6\x1c\
+\xab\xb2r;l\xae\xd9f\x98K\x9b\x88\x8fa;\x8e\xfehq\xd7h4\xbb\x16\x1e\xd7h\xa3\
+\x0c=w\xe4@ \x167\x8a\x9c\x1b\x84Pb\x98\x06p\xe0,\n\xea\x85\xd117\x9dj\x15\
+\xe2<\nB\xceH\x10\x05\xb6[\xe4\x8c\xba6\xb9W^\x8c\x82\xd5\x94C=\xd7\x10Y\x06\
+\xdc\xec\x98\x9d*le\xef\xfa\xa3\xc5]\xa3\xd1\xecZ\xa2\xca-\x00\x04BX\xd7\xd4\
+\xbe\xc8\xd1)q\x92A\xce\x0c\xc34\x0c\xe44\xa6Q0\xbcw\x9fe\xdb\xa2\x08\xe7\
+\xbcYk4\x1b\xd5\x98\x12\xcbN!gi\xd7\xaa\xac,\x06\xcdr6\x8d)\xcbhm\xa8gX\xde\
+\xf062\xdbA\x8b\xbbF\xa3\xd9\xad\xb0p\xbd\xbd\x91\x1ev5\xdb\xd1p\xad\xf4>\
+\x06H\x081M`QH\xe3hx\xef~\xd34[5P\x1a\xd4\x9bA\xb3\xc6\xd02-\xd30M\xcb\xe0\
+\xeb+K\xcdF\xb9\xe0\xa1mnL\x16\xe9\xe2D\xd7\x9d=\x1e#\xfa%&\x8dF\xb3;\x89\
+\xfd[\x00@\x00(\x83n9\xc2\xd0L\xef\xa1\xe0\x00g\xb6m\xb0\xa8I\xe3\xa8\xb4\
+\xff\xc0\x86\xb2\xc74\xa8\x07\x9c\xc7\x8c\xb8\x88\xc4vl\x03i}\xedN\xb3~7\x97\
+\xe2\xaa\xb2\x1bv:U\x9c\xd8\xaan\r\x8a\xb6\xdc5\x1a\xcd.\x845WXX\x01\x00\x8e\
+]S\xfb"\x92\x147G\x80S\xc7\xb5iP\xa3\x94\x95\x0el\xec\x97\xc4)\x0b\x1aM\x1a\
+\xc7\x0cL\xce\xa2t:M\xc3zP]\xa9\xd5\xcby\x0f\x1dsS}\x99\x91\xa9D\x8a\xe0\xed\
+\x80\x16w\x8dF\xb3\xfb\xc0\xc8\xffZ|\xea\xba#\x07"\x92\xf4^0L\xd72\xe3\xa6\
+\xcf\x91\x94\x0e\x1c\x90\x7f\xa5Q\x1c4\x03\x1aS\x0ef\x14\xc7^:\x1d\x06\xb5\
+\xb0\xba\\o\xac\x16<\xb07+\xbb\x9d\x1er2#\x8f\xbcC\x0f\x8e\x16w\x8dF\xb3\xdb\
+\xa0\xf5\xbb<\xae\x11\x00\xd6\xcdlG\xe4\xc4\xce\x11g\xc82\x8d\xa8\xb1n\xd8\
+\xeeh\xa9$\xff\x1a\x07a\x14EqL9\x1a\x94\xd1\x94\xeb\x84\r?\xa8.G\xf1z\xd1\
+\x03k\xb3\xb2\x03!\xde\xc8\xe4Vt\xe9\xc1\xd1\xe2\xae\xd1hv\x17\xc8\xe3\xea<\
+\xb4\x93\rt\xf1\xb6\x13B\xdc=\xa6a\xc6\xcd\x8a\xe9\xa4\x8a\xa3\x1b[s\xc4aX\
+\xaf\xd5\x90\x98\x0c\t"O\xbbv\xdc\xac\xd5\xfd;\x8c\xfa\xd9\x14\xda&\xc1\xcd\
+\xeb\xb2j\x8a\xe0\xed\x86\x16w\x8dF\xb3\xab\x88k\xb7Ej_\xd6}G\x0en\xd8E\xd3.\
+\xd0\xa6o{\xad\xa41\x82f\xad\x11\x05\x01C\x93sn\x10\xc34I\xdc\xac\xae\xaf-!\
+\xaf\xe5\xd2]\x94\x9d\x98vz\xe8\xd0\xa3\xef\xd0C\xb2\xed\x16\x014\x1a\x8d\
+\xe6\xa1A\x1e\xc7\xb5Vj\xdf\x98v)\xc0\x91\x10\xb7\x145\xabn6\xbf\xa1\xec\x08\
+Q\x10FA@\xd1\xa0\x94Y\xa6E\x80\xf3\xa0\xbaZ^\x08\xa2Z6\x05\x9d\xca\x0e\x00\
+\xde\xd0a\xc3|\x0c\xbbp\x0c\x88\xb6\xdc5\x1a\xcd\xee!\xae\x8a\xd4\xbe\x842\
+\xe4\x984\xdb9"\x05\x8fP\x92)\x16\xbdl\xdb\x9d\x82\x184\x82 \x088\x98\x94R\
+\xc7\xb1\x91S\x1eT\xab\xfeR#l\x0eg\x89cA\xa7\xb2\x9bn\xd6\xcd\xef\xdb\x82\
+\x1e=4Z\xdc5\x1a\xcd.\x01YD\xebK\xd0\xf5\xad%\x02\x9cC3B;\x95\xcd\x0e\x8f\
+\xa4\xd2\xe9\xd6!\x08a\xbd\x11\x84!e\x06e\xd4u,\xceb\x0c\xabq\xb0T\x0f\x82\
+\xb4C<\xb7\x8b\xb2\x03@f\xf8\xe86\x0c\x7fT\xd1\xe2\xae\xd1hv\tq\xf5kd1\x10\
+\xa0\x1c6\xe5\x08#\xc08\x04\x11"Ie\x87\xc77\x94\x9dcPo\x84a\xc8\xd0`\x9c\xbb\
+\x8e\x85\x9c\xf1\xb0\xca\x82\xbb\xc8\x03\xdb"\xd9T\x97\xfc\xc0\x00\xe0dJ\xb6\
+7\xbc%}zx\xb4\xb8k4\x9a\xdd\x00\xa7\x8d\xb8~\x07HG\x8e0\x02\x8cA\x10#"\x8c\
+\x8e\x9f\xcc\xe4[\xb9\xbd8\xe7a\xbd\x1e\x841\xe3\xc09\xb7\x0c@F\tk\x1aP\x05\
+\xd2\x889\xb1Mp\xcc\xee\xe7J\x0fm\x8b\xed8\xfa\xb3\xad\x1f+4\x1a\x8df@b\xff\
+\x16 \x03\x00\xc6`#G\x18\x01\xca\xa0\x19#"x\xb9\x91L\xbe\xb5\x82\xca\x19kT\
+\xfc d\x94\x13\x04\xc34\x81 5 NeR<\xae\xd8\x96\xc188&\x98\xdd\x04\xd2J\x15,g\
+\x9b\x86?\xaahq\xd7h4;\x1e\x1eUic\x05\x80 \x02\xe5\x1bf{L!\x88\x11\x10\x88A\
+\n\xa5#\xad\xc2\x8c\x07\xb5Z\xccIL9\x01b\x10N8#\xc0\xf2\xc3\xc3Qc9e1D`\x1cRN\
+7o\xbbH\xda\xfeXw\xbe\x1e\x10-\xee\x1a\x8df\xc7\x13\xf9\xb7\x00\x10D\x8e\xb0\
+\xb6\xd9\x1e1\x08)\x8a\x9ca^n\xccI\xe7\x00\x00\x19k\xd6\xeaA\xc4\xa3\x98Z\
+\xa6I\x80\x11\x16\x03\xf0\xc2h\x89\xc6M\x08\xcb\x96I\x9a\x11\xa4\xec\xee\x02\
+\xeef\xf7l\xab\xa4\xed}\xd0>w\x8dF\xb3\xb3a\xc1*\x0b\xd6\x00\x00\x11e\xb2\
+\x81\x88B$\xf6\xc2&`\x18Vq\xec(\x000J\x9b\xd5Z\xcc fh\x99&\x00\'<\xb6m\x92\
+\x1d\x1a\x05B\x82\xb5/\x1d\x8b\x88\xbff\\1Yl\x82\x98\x8e7|dK\xfb\xf6\r\xd0\
+\xe2\xae\xd1hv4\x18\xad_\x17\x9f\xc4[K\x08\x10D\xc0d\xb8\x0cBq\xcf\xa4\xe5\
+\xa4X\x14\xd5k\x8d\x90\x02g\xccu,\xc6\xe2\xb8Q\x1b*\r9\x99\x02\x00D\xeb\xd7-\
+h\x02@#\xc4\x94\xdd=H&[:\xbe\xdd\x92\xb6\xf7A\x8b\xbbF\xa3\xd9\x91\xd0\x98\
+\x861B\xb8L\xe2:!&e\xc89r\x84 \x06\xae\x04B\xda\xae\x97\x1b\xda\xcf\x85\xb2\
+\xc7\xc89\xb7m\x93\xb1\x98\xc4\x8d\xe1=#v:\x07\x00\x9c6X}\t\x00\x18\x07\x93\
+\x80\xd1M\xda\xed\xf4\x90\x93\x19\xed\xf2\x87\xed\x8a\x16w\x8dF\xb3\xc3@D\
+\x7f\xbdB\xc1\xb2\rn4\xe6\x81\x18\x00\xc0Z\xca\x8e\x9c\xcbTa\x049\x1b\xdd\
+\x7f\x92\x85A\xbd\x1e\x8412\xce\x1c\xcbD\xce n\x14K\xa3\x86\xd3\xda\xff\x9a\
+\xfa\xb7\x109!"\x86\xb2\xdb;K\xdbl\xf3\xebA\xd0\x0b\xaa\x1a\x8df\x87\xb1\xbe\
+\xb2\xba^^q-#M\xd6\x08o\x12 \x8caL\xa1\x19\xa9\xca\x0e\xc8Y\xb6\xb0\x87\x00\
+\xf1\xab\xcd\x90\x02\xe3\xdc\xb5-DNX\xb3X*Ieg\xe1\xba\xdcD\x1b\xb1\xfb:j*\
+\xbf\xdfrs[\xd4\xbdo\t-\xee\x1a\x8df\x87\xd1\xa8\xd7\x81r\x0ck\xb4~\x07\xc0@\
+\x800F\xf1\x9a\x92\xa2\xechZ\xb6\x97\x1b\xab\x07@\x19 \xe7\xaempNYP-\x8e\x8e\
+\x9aN\xba]\x19\xc6\x95\x1b\x00@\x08p\xec\x9aEr\xbbg\x7f\xec\x85\x16w\x8dF\
+\xb3\xc3\xf0\xf2y\xb0\xac\xa8\xfa5\xb2\x80\x18$\x8c\xb1\x11%\x94\x9d[\x96946\
+\x1517\x8e)\x008\xb6\xc1h\x8cQshl\xcc\xb0S\xb2*\xdaX\xe6Q\x15\x00\x10!\xa6\
+\xdd\x1c2\x00\xde\xd0\xa1\xed\x9c\xfd\xb1\x17\xda\xe7\xae\xd1hv\x18\x85B\x9e\
+6\xd6\x1d^\x070\x18\x87z\x80\xaa;\x059\xb7m7?:\x19q\x8fRf\x18\xc42\t\x8dBd\
+\xe1\xc8\x9eM\xca\x8e\x9c\xc6\xfe-\x00$\x84\xc4\x0c\x19\xef\x12$c:\x197\xbf\
+\x7f\x8b:\xf6\xad\xa2\xc5]\xa3\xd1\xec0\x0c\xd3\xcc\xa5C\xd6\x04 \xd0\x0c8\
+\xe3\xa8*\xbb\xe3z\x85\xd1\xc9Fl\x03\xa0i\x1a\x84 \x8f#\xe4\xf1\xc8\xde=\x89\
+@FZ[@\xda\x04\x91\n\xb8\x87\xd9\x9e\x19\x99\xdc\xe6\xd9\x1f{\xa1\xc5]\xa3\
+\xd1\xec0xTe\xcd2\x01B)\x06\xd1&eO\xa5s\xb9\xe1\xa3Alr\xc6m\xdb2\x0c\xa4A`\
+\x99\x98/\x8d\x19\xd6&\xd7\n\xb20\xae-\x8a\xcf\x94m\xce"\xd9\xc6\xce\x8cn\
+\xff\xec\x8f\xbd\xd0\xe2\xae\xd1hv\x18Q\xe5& \x07\x02\x8d\x109\xe7\x00@\x88\
+\x81\xc8S^\xc1+\x1cjD&\xe7\xdc2\r\x00F\x9b\x81iA\xa14F\x8c\xa4\xd6E\x95\x1b\
+\xc0)\x00 Bw\xb3\x9d\x18\x99\x9d\xf3>j\'Z\xdc5\x1a\xcdN\x82\x05\xf7X\xb0\x06\
+\x04"\x8a\x11\x05\'\x95\x05\x00\x1a7=o$\x9d?\xd8\x08\t"s\x1c\x1b\x90\xd3\xa0\
+\xe1\xa6\x9c\xdc\xf0(1\x92~\x15\x1eUYsY|\x8e\x19t\xd9D\x1b ]\x187\x9d\xcc#\
+\xef\xcf#C\x8b\xbbF\xa3\xd9I\xd0\xfa\x1d\x00\x00D\x8a\xa9\xb1CO\xb9^\x9e\xc7\
+\xcd\xa8Y\x8fb\xa3\x11\x12\x02\xdcqm\xe4\x8c6\x1b\xe9l*S\x1c!\xdd\x02\xd7\
+\xa3\xca\x9cH\xf9\xc89\xc8t4*\xc4r\xd3\xc5\x89G\xdd\x97G\xca\x8e\\(\xd0h4O&H\
+\x03\x1a\xdc\x03\x021#1+\x9aN\x9e\x10\x03\xc1\xa4<\x15R\x13\x80\x9b\x06"gq\
+\xb3\xe6\xe5\xbc\xecPweg\xcd\x15\x1eV\x84\xb1\x1e\xb3\xae\x9b\xe8\x817t\x98\
+\x98\xf6#\xee\xcd\xa3E[\xee\x1a\x8df\xc7\x10\xf9_\x03\x8f\x110\x88\xcc\x98\
+\x13\xce(7\x9d8F\x86\xc4\xb2\x008"\x10Z\xaff\x87\x8b^\xaegb\xde\xb8:\x0f\xd0\
+\xda\xc7\xa3k\xf8\xa3\x95\xca\xa7r{\x1fa7\xb6\x04-\xee\x1a\x8dfg\xc0\xe3\x1a\
+m\xdc\x05\x02Q\x04!\xe5\x84\xf08\x8a#F\xa2\x80\x03\x12\x8b@\x14\xc7\x04\xe3\
+\xa1=#v\xaa\xe7NI<\xaa\xf2\xb8\x06}\xd6Q\x01\xbc\xe1\x9d\xb1\x1dG\x7f\xb4\
+\xb8k4\x9a\x9dA\xe4\x7f\r\xc8\x11\xa0\x11!rpr\xe9\x88R\xa4\x8c!\'\x00\xdc0\r\
+\x03\x8a\xc3\xa3\xa6\xeb\xf5\xab\xa4rCl\xe7A)v\r\x7fts{\xect\xf1\xd1\xf5b\
+\xcb\xd0\xe2\xae\xd1hv\x00,\xf4Yc\x05\x08\x84\x11R\x86\x1c\x9cz\xad\x19\x861\
+\x00\x18\x84\x00\x001L@V\x1c\x1d\xe9WIs\x85G\xeb]6\xd1nC\x0c+=\xb4\x83\xc3\
+\x1fU\xb4\xb8k4\x9a\x1d@\xec\xdfB@\x82@\x19\xa4l\x88\xd1\r(\xa1q\x0c@\x00\
+\x10\x10\x10\x00\x11iP7\xed\x1ey`\x90E\xfe\r\x00 \x00\x11E\xecf\xb6\xa7\x8a\
+\x13\xa6\x92\x9f`G\xa3\xc5]\xa3\xd1lwhs\x95\x06kDl\xa6a\x80\x01\xc4$\x8c\xdb\
+.\xa3L(;\x00"\x12\xcb"\x96\x9b\xeeYI}I$\x1b`=\xc2\x1f\r;\x9d.\x1cx\x84\xdd\
+\xd8Z\xb4\xb8k4\x9am\x0e\xc6\xfe\xd7\x00\x80\x00\x8c#\x00 \x10\x03\x1b\xb9T\
+\x8dz\xc3a\xc89g\x00`\x1adt\xa4`:\xdd\xedndQ+H\x06 \xa6\xd8\xf5\xad%o\xf8H\
+\xe7\x8b\xac;\x97\xdd\xd3\x13\x8dF\xb3+\xa1\x8de\x16\xf9\xc2l\x97\x91\x8b\
+\x08\x04\x82\x157\x15:n\xc6\xf6\xf6\x98\x96e;)\xd3\xea\x99\x987\xae\xce#\x8f\
+\x01\x00\x11x\xb7\xa4\xedV\xba\xe8f\xc7\x1eY\'\x1e\x03Z\xdc5\x1a\xcd\xf6\x05\
+\x91Em\xb3]]\x02%\x00\x00$nVM;\xce\xec}\xaa\x9b!.\xab`\x18Wh\xed\xb6(\xd2\
+\xddl\'$32\xf9\xed\xb7\xfe\xb1\xa2\xc5]\xa3\xd1l_h\xfd.\x8f\x1b\x04\x80\xf2.\
+\x89\x1bIkk\xd3n\xca\xce\x03\xa05\xa4\xd5\xa8Y\xe1Q]\xec\x8c\xca9v\xf5\xb6\
+\xbb\xb9};n\x17\xbd\xfb\xa2\xc5]\xa3\xd1lS\x90\xd3\xc8\x9f\x07\xe1m\xef\x16\
+\xb9h\xa6\nnv\x8fr\x00\x03\xde\x04Ze\x91\x1f6\xabA3\nCJ\x19\x1f\xca\x12\xc3 \
+\x80\xdd\xcdvb\xda\xde\xf0\xe1G\xd9\x8f\xc7\x83\x16w\x8dF\xb3M\x89k\x8b\xc8\
+\x02\xd2+\xdf:!\xb9\xd21\x00\x00\x1e\x02\xafcT\x89\xc3Z\xd0\xa8\x87!\x8d"F\
+\x19G\x04\x04\xb0Mb\x18\x04\x00(\xef\xbe\xd7R\xbaxp\'\xee\xa2w_\xb4\xb8k4\
+\x9a\xed\x08\xb28\xae\xde\x06\x91\'\x80w1\xdb\x9dt\x96E~X\x9d\x07V\x0f\x9aQ\
+\x18Q\x1as\x11N\x03\x04\x08\x80a@\xcc\xc0\xb2\x08\x81\xd6[K\xddv\xd1\xf3R\
+\xbb(\xfcQE\x8b\xbbF\xa3\xd9\x8eD\x95\x1b\xc8#\x00\x88y\xc7\x0bG\x04(\x83\
+\xbbwj\xe1\xfc\x97E\x0fL\x039o\t\xba\xba#\x1eG`\x1c\\\x0b\x01!\x88!\x8c\x80\
+\x18`\x12\x00\x00D0\x0c \xc4\xc8\x8c\x1e\xdb\xa1\xbb\xe8\xdd\x17-\xee\x1a\
+\x8df\xdb\xc1\xc2J\\\xbf\x03\x00\x9c\x03g\x1d^r\x80\xb5\x1a\xaeT\xe8h\xde0\r\
+\x82\xb8I\xd3\xe5[M\x11#\xb6m\xa4=\xc7\xb2L\xb4\x91\x98\xd44\x80p&\xb4=\x8cy\
+\x10\x93\xa2\xbd\x83\xb7\xe3\xe8\x8f\x16w\x8dF\xb3\xdd\xc0\xa8r\xb3\x95\xde\
+\x8b%\x97@\t\x81 \x86\xf5\x1a\xf7\\#\x93"\x08 C\xdf\x11\x01\x00\x89a\xd8\x96\
+\xe9\xba\x96\xe3\x98\xaem\x10da\x10S\xca\x80s\x1a#\x02!\x04L\x93\xbb\x16\xf1\
+\x1b4\x8e\xc3>\xd1\xf1;\x1a-\xee\x1a\x8df\xdb\xc0\xaa\xc0\xab\xb4y\x8f\x05\
+\xeb@\x80u\x0b\x7f\x04\x80\xb5*g\x1c\x86\xd3\xc4 \xc0\xb1%\xeb\xa6e\xb8\xae\
+\xe5:\x96\xe3\x98&A\xce\x19\x8d\xa2F\xc0\x18G@\x04B\x908H\xc0\xc0\x18\x90\
+\xc7\x14,\x033)\xc2\xe2\x10\xd2\xbb-\x08R\xa0\xc5]\xa3\xd1l\x03x\x1c\xdf\xfb\
+\xa2\xde\xf0\xc3\x08\x1d\x83\xda\x16\x01\xec\x12\xfeH\x084B\xac4\xd0K\x91\
+\x94\x03\x8c\x01Gp\x1c\xa3X\xf4\x1c\x8b\x00\xe7\x94\xd2\xa8\x191\xca8\x17\
+\x07\x00\x01\xe4\xc4\x06o\xdct\x8b\x8c\xa1\x81M\xac\xdf <bHL\x024\x0e\xb7\
+\xbe\xaf[\x83\x16w\x8dF\xf3\xd8\xc1Z\xf9\xca\xfa\xbd{\x94"\x02\xb8Y\xa3\xfb[\
+K\x04\x00`\xad\x86\x00\x90u\t!\xc4v\x88m\x12\xdb20\x0e\xeb\ra\xa4\xb7\xdf^\
+\xdd8\x92\xd4\x82\x0c\x8fb\xd3\\!\x84p$6\xa6\xd3V$J\xd2\xa8\xb9\x95\xfd\xdcJ\
+\xb4\xb8k4\x9a\xc7L\xd4\xac\xac\xdf\xbb\'\xect\xc3 \x96\t\x88\xc0D\x06\x98vp\
+\x0b\xe7\xc082N2)c\xb4@\\\xdb0\x0c \x88\x1c\x91QJ\xe3V\xb4L\xa7\x13\x07\x01\
+\xbcl\xaa\x1e\x1a\x94R\x00\x04b\xa6\x1dS\xfc\xc9\x00\xa0q\xb0\x85\x1d\xddR\
+\xb4\xb8k4\x9a\xc7L\x1cE"\x96\x912\xf0l \x00\x94#"p\x04\xc6\x80q@\x00B\x88e\
+\x12\xc7!Y\x03\x10\x91s\xc6)\x08\xaf\r\x81\xfe\x9b\xe2\xa1C|\xbb0\x16\xd2\
+\x0c\xe7h\x93\xc8\xa25d\x00\x00\x84\x00\xa7!"v\xddG{\xa7\xa3\xc5]\xa3\xd1<f\
+\x9cT\x0e\x08A\x8e\x94\xa1k\x11b\x9aQ\x8cAD\t!\xa6A\x1c\x9b\x98\x06\x10\x82\
+\xc8\x019\xa7\x1c\xa0m\xa0\x0f"\xc9\x04\x08\x8b\x1a\x06\x9bO\xdb)\x00\x824\
+\xe0\x8c\xcaC\x91E\x88\x9c\x10\xf3Q\xf5\xed\xf1\xb1;\xa3\xf7\x9f\x10|\xdf\
+\x9f_X\xf4}\x7f\xf0\x02a\x18v\x1e2\xbf\xb0\xb8\\.?\xc2\x86>8\xcb\xe5\xf2\x83\
+\xb6j\xe6\xca\xd5>C\xd1\xff\x90\x8b\x97.?X\xfb\xbe\x8d\xb3o\x1f|\xdf\x9f\xb9\
+r\xf516\xc0v\xd3\x8e7B\x08\x19\x19\xc9\x14F\x0bN&\x93NYi\xc7\xf0\\\xe2\xda`\
+\x10\x8e\x9c3\x86\x88\xad\xb8\xc7\x075\xb3\t!\x9cs\x1a\xd4iXc\x8cm\xaa\x00\
+\x99\x88\xa0\xdc}h\xcb}G\x12\x86\xe1\x1bo\xbe\xe5\xba\xee\xd8Xiv\xf6\xba\xef\
+\xfb\xaf\xbc\xfc3\xd7u\xd523W\xae\xfe\xe1\xeds\x13\xe3\x07\x96\xcb+\xcf\x9d=\
+}\xf6\xcci\xdf\xf7\xff\xfe\xf5_\x8d\x95F\x97\xcb+?\xf9\xf1\x8f\xa6&\x8f\x02\
+\xc0?\xbc\xfe+\x00\x08\xc2pj\xf2\xe8K/\xbe \x8e\x15%_{\xf5\xe7c\xa5R\xe7\xd9\
+}\xdf\xff\x9b\xbf\xfd\xbb\xd7^\xfd\xc5\xc4\xf8\xfd\xdf\xdb\x9e\xbd>w\xf1\xe2\
+\xe5\x7f\xf7\xd7\x7f5`\xd7\x96\xcb\xe57\xde\xfc}\xcau]\xd7\t\xc3(\x08\xc3_\
+\xbe\xfa\xf3|>\xaf\x96\xf9\x87\xd7\x7fu\xf6\xeci\xd1\xfe\x8d\x13\xcd^\xcf\
+\xe7\xf3\x89\x92\xfd\x9b-\x0fy\xef\xfc\x85\xb3gN\xab\x85\xff\xcf\xff\xeb\xff\
+\x16\x1f\\\xd7\x15\xa3\xd7\xbf#\xf7={/:O\xf4\xa05|s*~uv\xf6\xfa\xf4\xa9\x93[\
+\x7fj\xc9\xd0\xf0Hd\xacZ6o\xf8u\x1aSD$\x00\xc8\x01\xdb\x1e\x97o\xe87!\xd0\
+\xddwcZf\x7f\x9f\xce\xceE\x8b\xfb\xceC(\xfb\xd9\xb3g\xca\xe5\xf2\xfc\xfc\xc2\
+\xc4\xc4\xf8\xf4\xf4\xc97\xde|+\xa1\xef\xf3\xf3\x0b\xaf\xbc\xfc\xb3\xa9\xc9\
+\xa3B\xa9\xcf\x9e9\xfd\xf9\xccU!\x1f\xf3\x0b\x8b\xe7\xcf_\x98\x9a<:{}\xceu\
+\x9d\xd7^\xfdE\x18\x86\x7f\xf3\xb7\x7f\xf7\xdc\xd9\xd3\xa2\x86\xf7\xce_x\xee\
+\xec\xe9\xae\xca\x0e\x00\x1f_\xbc\x9c\xcf\xe7gf\xae\xf4\x11\xf7_\xff\xe6\x9f\
+^{\xf5\x17\x000\xc8\x04 \xf1}\xff\xd7\xbf\xf9\xad\x9cx\x00`~a\xb1S1\xcf\x9e=\
+\xfd@\xd5\x0e\xdel\x95\xff\xf4\x1f\xff\x83h\xd2?\xfe\xe6\xb7S\x93G\x1f\xe2\
+\x8cr\x10\x1e\xe8D\x0f1C\xect\x906\xa3\xca\x8d(\xc60\x0c\x00\x80\x10@\xc3\
+\xe5H\x0c\x8c\x08\xf0o,\xec\xbd\xcf\x0b`\x99\xa6\xb1K\xd3\x0f\xec\xce^\xednf\
+\xae\\\x9d\x98\x18\x9f\x9f_\xa8T\xfc\xb3g\xcfT*\xfe\xc2\xc2\xe2\xd4\xd4d\xe2\
+\xc9Z(\xbb\xfa\xcd\xec\xf59\xf1\xcd\xc4\xf8\x81 \x0c}\xdf\x9f\x9f_\x98\x9a\
+\x9a\x04\x00\xd7u\x85\xd6C\xcbK\xb3\xd2\xcb\x8e\x13\x8e\x9d_\xbe\xfa\xf3\xd9\
+\xebs\xaa#b~a\xf1\xe2\xa5\xcb3W\xae\x8a\x02\xe2?Q \x9f\xcf\x89/\xd5\xc2a\x18\
+\x8a&]\xbctY\xfeI\xb4Pm\xb6\x94\xd4\xe5r\xd9\xf7\xfd\x8b\x97.\xfb\xbe\x9f\
+\xcfo\xbcu2s\xe5\xaaZC/z5\xfb\xbe\xe4\xf3\xf9\x94\xebV\xfc\xaa\xe8H\xa2\xb3\
+\xbe\xef\xcb\xda\xc20\xbcx\xe9\xf2\xc5K\x97\xc30\xec\x1c\x04\xd1S1\xc2\xf7=Q\
+\x18\x86\xa2_\xb2\xf2\xe5rY\x0e\xaf\x1c+\xb5\xe3r|\x84GKV+\x87Z\xd6 \xff$\
+\xca\x8b\x06\x0f> \x8f\x82\xda\xbd\xdbA\x10\x01\x02!\x04\x810g\x1f\xc9?%\xfe\
+\x03+\x8f\x8f\xcem\x82\x00H\xb9X]\xdduhq\xdfy\xcc\xcf/\x08/\xc1O~\xfc\xa3\
+\x89\xf1\x03/\xbd\xf8\xc2\xf8\xf8\x81\xe9S\'\xe7\xe7\x17\xba\x96\xff\xc3\xdb\
+\xe7\x9e;{\x1a\x00|\xdf\x97Va!\x9f\xab\xf8\xd5M\xdf\x14\xf2\xe2&\xff\xc3\xdb\
+\xe7\x00\xe0?\xff\x97\xff\xfa\x87\xb7\xcfu\xde\xf63W\xaeN\x8c\x1f\xc8\xe7\
+\xf3\xd3\xa7N~>\xd3R\x8a\xd9\xebs\xe7\xcf_\x10m\x9b\xb9r\xf5\xe2\xc5K\x00p\
+\xf1\xe2%1O\x9c?\x7f\xc1u\xdd?\xbc}N\x88\x8e\xef\xfb\x7fx\xfb\x9c\xeb\xba\
+\xef\x9d\xbf0;{]\x94\x14^o\xf1 "\xea\x94\xd2#\x8e:\x7f\xfe\xc2?\xfe\xe6\xb7A\
+\x10\x8a\xcf\xcb\xe5\x15\x00Pk\x10\xdf\xf4\xa2k\xb3\xfb#\xce\xfe\xc6\x9bo\
+\x01\x80\xf0n\x89>\n\x7f\x17\x00,/\x97\xff\xfe\xf5_I\xbd~\xef\xfc\x05\xf1\
+\xe5\x1bo\xbeU\xf1}u\x10\xe6\x17\x16/^\xbc\x0c\x0033W:\xa7\x96\xc4\x89\x00\
+\xe0\xd7\xbf\xf9m\xa5\xe2\x03\xc0\x1bo\xfe^\xa8\xf3\x1bo\xfe^\x8c\x8f\x18\
+\x8d\xce\xa1\x93\xe3\x93\xea6\xd4\xb3\xd7\xe7\xde~\xfb\x1d\xd9<h?(\x04A\x18\
+\x04\xa1h\xf9c$\x8a\xa2\xb6\xf3\x05#\xe6U|\xe2\x97W\x1ak\xab\xd5{5\xbf\xe1\
+\x01|K\xab\x9d\xc8\xb1\x05\xdf\xf8\x8e\xc5,\x8e\xbe\x9d\xfa\xb7\x19Z\xdcw0\
+\x83\x18\\B\x86\x06\xf7\xe4\nQ\xf8\xe5\xab?\xff?\xfe\xf7\xffU\xd8\x8f\x89\
+\x02\x9f\xcf\\\x9d\x9e>\t\x00\xd3\xd3\'\xa5!y\xf1\xe2\xe5W^\xfe\xe9\xd93\xa7\
+_y\xf9gg\xcf\x9c\x16\xbe\x88\xd7^\xfd\x85j\xfe?=}R\xe8\xd1\xe73W\x9f\x9e>\
+\x19\x86\xe1\xec\xf5\xb9\xe9\xe9S\xa5Riz\xfa\x94\x14\xdc\xc4\xca\xc1\xfc\xfc\
+B\xb9\xbd\xac\xfa\xdc\xd9\xd3/\xbd\xf8\x82\x9c\x8dD\r\xaf\xbd\xfa\x0bq\xc6\
+\xc4\x81\x834\xfb\xbe\xcc\xce\xce\x01\xc0k\xaf\xfe\\\xfd\xf2\xe3\x8b\x97\x7f\
+\xf9\xea\xcf\xcf\x9e9\xad\xba\x8f\x00\xe0\xa5\x17_\x10#0\xbf\xb08V*\xa9\x83P\
+.\x97]\xd7\x19\x1f?\xf0\xda\xab\xbf\xe8\xeauQO4{}.\x9f\xcfML\x8c\x97J\xa5\
+\xa9\xa9\xa3\xb3\xb3\xd7+\xad\x07\xa0\xbcx\x1a\xeb5tr|\x12C\r\x00\x17/^>{\
+\xf6t\xa9T\x9a\x9a\x9a\\.\xaf\xf8\xbe/|t/\xbd\xf8\xc2K/\xbe \xe6\xfe\xc7\x88\
+\x9d\xca\xb7r\xfa"\x98\xb6i\xa7\x1c\x0e\x10SF\x19\xb3\x1d\xd30\r\x80ol\xbc\
+\x13\x02\xe9\xbdF\xe1)\xcc\x1e7\xd2\x1b{\xa5\x12@\xcev\xa7\xb8k\x9f\xfb\xcec\
+bb|\xe6\xca\xd5\xa7\xa7O\xbew\xfe\xc2\xd4\xd4\xe4\xec\xec\xf5B!_.\xafH\x9bW\
+\xf2\x87\xb7\xcf\x95\xcb+R\x9b\xf2\xf9\xbc4\xd5+~\xb5\x90\xcf\x89o\x00\x0e\
+\x00@\xa5\xe2\x8f\x8d\x95\xca\xe5\xf2\xd3\xd3\'E\x99\xe9\xe9S\x17/^R\'\x06\
+\xe1\x88\x10k\xb0\x82\xd9\xebs\xd3\xa7N.\x97\xcb\xf7\xf5\x14O\x9f:\xf9\x9f\
+\xff\xcb\x7f}\xe9\xc5\x17f\xae\\\xfd\xf7\x7f\xfdW\xcb\xe5\x950\x0c\x85y\x0b\
+\x00\x85|Ntmv\xf6\xba\xb0^\xf3\xf9|bNJ\x9cb\xb9\xbcRP\xfc3\xea\xe7\x04\xbd\
+\x9a\xdd\xbf\xc1g\xcf\x9c\x9e\x9a<\xfa\xf7\xaf\xff*\x0cCu\xe6H<\xee\xc8\xef\
+\xfb\xcc.\xe2\\\xc2\xfa\xee\\\x1fN\x9c\xc8\xf7\xfd\xe5\xf2J\xd8\x1e\x99R\xa9\
+4V*=w\xf6\xf4\xc5\x8b\x97\xff\xf0\xf6\xb9\x9f\xfc\xf8G\xae\xebv\x0e\x1d(\xe3\
+\x93\x18j\x00X.\x97gf\xae\xc8\xf2A\x18\x96\xcbey\xc1<v/\xbf\x97\x1f\xad\xaf\
+\xcd\x99\x06\x03$\x16\xd6\n\x99\xb4\x93\xceS\x06H)\x89\xd6h\x18\x7fc\xb7;\
+\xd6\xc3,\xe7\x9e\x15S\xc30BV0Y\xdd1\x1b\xe2\xc5\'\x1a5]\xaf\xf8\xed\xf4d;\
+\xa1\xc5}\xe71}\xea\xe4\x1bo\xbe\xf5\xe2\x8b/\x00\xc0\xcc\xcc\x95\xb1\xb1\
+\xd2\xf8\xf8\x81\xb7\xdf~\'a`\n\x9b]\x8d\xee\x10^u\xb1\xa0\x9ar\xdd|>?11>3se\
+\xfaT\xcb\x8e~\xe9\xc5\x17\xe6\x17\x16gf\xae\x08U-\x97\xcb\xa5\xcdk\xaa33W\
+\x7f\xf2\xe3\x1fIY\x9c\xb9r\xf5\xe3\x8b\x97\xa7O\x9d\x9c\x9a<:\xbf\xb081~@X\
+\xc4]e\xceu]\xd1r\xe1\x1e\xc9\xe7\xf3\xae\xeb\x8aE\xe00\x0c\x85q*4\xaePH\xca\
+zW&\xc6\x0f\x08\xc7\x91\xa8A\xbaef\xaf\xcfM\x8c\x1fP\xdb\xd0\xab\xd9\xf7=E>\
+\x9f\x7f\xee\xec\xe9\xf7\xce_x\xe5\xe5\x9f\xa9\xc3x\xf1\xd2e\x11}$M\xe3\xfb2\
+}\xea\xe4\xd93\xa7\xdf;\x7fA\xfc\x04}N4>~`~~A\x18\xfe\xcb\xe5r!\x9f\x17\x9d\
+\x9d>ur\xf6\xfa\xdc\xcc\xcc\x15\xf1\x98\x92\x18:\x95\xc4P\x8b6OO\x9f\x12\xb3\
+\xa6x\xb0\x98\x98\x18/\x97\xcb\xe2\x1b\xf9l$bO{-\xa4?:,\xdbM\x17\x0fE\x959S\
+\xbctZ\xbf\x1bG\xeb\x86ir\x1a3\xfa\xcd\x95\x1d\x00\x88\x9b\xceT\x1ba\x1cr\
+\x02\x88hf\xdd\x14!\r\x14)\xc5\x8c]\x18\xe4\x0eZ\xdcw"\xe2\xc6~\xe3\xcd\xb7\
+\xf2\xf9\xfc\xd8X\xa9R\xf1\xe7\xe7\x17^y\xf9\xa7\x9d\xa1\x90\xf2_\x00\xf8O\
+\xff\xf1?<=}\xf2\xef_\xff\xd5\xfc\xfc\x82\x08\x85\x04\xa1S\x17/\xff\xc3\xeb\
+\xbf\n\xc2p\xfa\xd4\xc9\xd6\xb2\xea\xec\xf5\x7fx\xfdW\xae\xebT\xfc\xea/\x95\
+\tc~a\xb1\xe2\xfb\xaa#bj\xf2\xe8{\xe7/\xcc/,\x9e={\xfa\xd7\xbf\xf9\xedXi\xb4\
+\xe2W\x9f\x9e>y\xf6\xcc\xe9\xb1R\xe9\x1f^\xff\xd5t\xfb!\xa0U~j\xf2\xd7\xbf\
+\xf9\')\x94\xcf\x9d=\xfd7\x7f\xfbw\xe2(\x11\x9c\x93\xcf\xe7_{\xf5\xe7o\xbc\
+\xf9\xfb\xd9\xd99\xd7u\xc4IES\xbb\xa2\xd6 \xbb/\xbc\xdbj\xbcM\xaff\x0f2\xda\
+\xc2G\xaf\x16\xfe\xc9\x8f\x7f\xf4\xc6\x9bo\xbdw\xfeB>\x9f\x1f+\x8d\xf69V\x0e\
+B\x18\x86\x9f\xcf\\\x15\xeb\x1c\xbf\xdc<\x07w\x9eH(\xf2\xdf\xfc\xed\xdf\x89\
+\xf2\xaf\xbc\xfc\xd30\x8c\xdex\xf3-\xd1M\x11\xae\xda9t\x89\xda\x12C-\x7f\xa0\
+0\x8cJ\xa5Q1U\xfc\xfa7\xbf\x15\xee \x00\x10\xa3-\xfew\xeb\xc5\x1d\x00\n\xa3\
+\x13k4\x8ckK\x96\xc1\t\x10\x16\x87,\x06\x00\x11:\x03@\xbe\x91c\x06\x11\x1d\
+\xabQ\x18\x1a\x0b"@D\x9b\xc4.6\xc52*\x07{W\x9a\xed\x00@\x1e\xe1J\xb4\xe6\x11\
+\xe3\xfb\xbe\xf4\xae\x0cx\x88\xb0p\x13\x87\xcc/,\xba\xae\xa3\xde\xd2\xcb\xe5\
+r\x18F\x0f\x14\xfc\x97\xa8\xb9\xeb\x89\x06l\x8fl@\xa2U]\x11\x830V\x1a\xed\
+\xefs\x7fD\xbcw\xfeB\xa1\x90\xef\x13Y${\'z4x;\x13\xfd\xea\xfc\xad\x07\x1ca\
+\x95\xce\x1f\xba\xf3\x9b\xc7K\x14\xd4\x83\xc6:\x8b\x1a\x80\x94\xc5!"\x03N\
+\x018g\x8c\x00\x07\x10q\xefHHWc\x9e\xb4\xfe\xe9&i\x88h:)\xc3\xcer\x8e\x18W\
+\x91E\x00\x84q\xf0FOd\x87\xf6=\xda^=&\xb4\xb8k4\x0f\xc6\x1bo\xbe56V*\x95J\
+\xbe\xef\x7f|\xf1\xf2\xbf\xff\xeb\xbfz,\xf3\xca\x93\x02""g,\xa6\x8d2k\xdc\
+\xae\x86\xf98\x8a)\xe7\x06A\x038\x10$\xc0\tp\xd3@\x03\xa8a\x00G\x93\x10\xf1\
+\xde)kW\xd0zK\xc9 \xc2\x0b\x03\x00\x84#A\xc3\xf5\x86\x8fd\x8b{\x1fc\xe7\x1e)\
+Z\xdc5\x9a\x07c\xb9\\\x9e\x99\xb9*\x96U\x9f;{\xfa\xb1\xafF>!D\xfe-\x08\x97\
+\xc1\x1a[\xb9\x17\x07\x14L\x83\xb0v@#\x01\xdc3d\x86\xe1\xbd\x8cMy\xe6h\xdc\
+\xac\xc4\xcd5\x1a\xd5\x11\xc1 \xe0\xb9\x04\x01\xb85D!E\x90\x11\x02\x1c\x0c\'\
+]\xf0r#\x86i?\xd6>=Z\xb4\xb8k4\x9a\x1d\x80\xbf\xfc\xa5C\xef\xb8\xe9\x92\xdft\
+\x10\xa0\x1e`\x10#!\x04\x11-\x83\xec\x1f6W\xd7\xcb\x9e\xd5\xe0h\xd0\xf6\x16\
+\x1f\x08\x90u!\x9d&\x80\x00V\x01\x8a\xdfy\xbc]\xd8bt\x9c\xbbF\xa3\xd9\x01DQ\
+\x18\xc6\x9c\xc7k\x9eC\x0b\x19\xcb\xb6-\xcb$\xa6\x01i\xc7(d\xcc(\x8a9\x0b\
+\xaaMT\x95\xdd$\x90r\x08p\x00\x04\x88+\xd0\xb8\xf1x\xbb\xb0\xc5\xe8h\x19\x8d\
+F\xb3\x03`4\xae5\xc02b\x84\x95ZP,d\xf2i\x1b9r\xcb$,nV\xeb\xf78g\x88dc7m\x84\
+\x94\x0bD}\xff\xa9\xbe\x00\xf6\x08\xd8O\x8a\x1bM\xbbe4\x1a\xcd6\x05y\x8cq\
+\x03i\xd5\xa0\x95;\xcb\xabA\xc4-\x03<\x17"\x8a\x00\x161, \x049\xe5\x8c\x02 "\
+\xe1\x08\x9eK\xa0m\xb6\x0fe;6\xe10=\x18:\x03\xbb1{{\'\xdar\xd7h4\xdb\x08\xe4\
+1\xd2\x06\x0f+<\xf2[1\x8b\x04\x10\x811\x14B\xdd\x8c\xc02\x08\x00\x05N\x01\
+\x80\x00X&\x01 \x11\xdd\xc8\xdd\x8b\x08\xe9\x84\xd9.`\r\xa8\xdf\x84\xec\xe4\
+\x96v\xe91\xa1\xc5]\xa3\xd1<V\x90!\xa7H\x1b<\xaa\xf0\xa8\x8aq\ry\xdcR\xe5\
+\xb64s\xbe\x11\xd1(6J5\t\x91\xd1\xee\xbcU\x00\r\x83\x10\x02\x9c\x83e@\xca!\
+\xdd\xdf{j\xde\x86\xf4>0\xbd-\xe8\xd9\xe3E\x8b\xbbF\xa3\xd9rx\x04\xb4\x8e\
+\xb1O\x83\n\xa7M\xe0\xb1\x9a\xa91\x01\x01\xe0\x08\x1c\x95\xffW@\x00@\x10\x89\
+\x04\x8c\xf6\x1bL\x99T7\xb3]\x1eQ\xfd\x12\n\xcf\xc2.M\xe3.\xf9\xff\x01\x9e\
+\xe6W\xce\xb2\x99\x1b\xa3\x00\x00\x00\x00IEND\xaeB`\x82' 
 
 def getSplashBitmap():
     return BitmapFromImage(getSplashImage())
 
 def getSplashBitmap():
     return BitmapFromImage(getSplashImage())
@@ -4330,562 +4407,563 @@ u\x9d\xe9\xa9\xeaTu\xb2\xd3\xe96\x9a\xcdN\xa7\xc79\x17R\xeaq\x85R\xe28N!\x9f\
 \x94Zv\xe0\xfb\x8e{\x8bA\x8dT\xd9=\xcf-\x16\x0b\x04\x80RbY\xb6\xfa\xb8\xc5Ba\
 \x9f\nq]7\xf3]\x18cD\xcaR\xa9X*\x15\xdb\xed\xce\xf6\xeen\xb7\xdb\xd7\xb3.3\
 \x11J\xc9\xc0\xb5t\xaa:1Q\xb6(U\xc3\'!$\xcc\xe5\x84\x90T\xb51\xdb\xb6,+\x9f\
 \x94Zv\xe0\xfb\x8e{\x8bA\x8dT\xd9=\xcf-\x16\x0b\x04\x80RbY\xb6\xfa\xb8\xc5Ba\
 \x9f\nq]7\xf3]\x18cD\xcaR\xa9X*\x15\xdb\xed\xce\xf6\xeen\xb7\xdb\xd7\xb3.3\
 \x11J\xc9\xc0\xb5t\xaa:1Q\xb6(U\xc3\'!$\xcc\xe5\x84\x90T\xb51\xdb\xb6,+\x9f\
-\xcf\x1fD\x15x\xc3E\x07\x1b@\t|\x0f\xb5\xfd\x86{-i\x11\xff\x7f{\xef\xfe\x1b\
-\xc9u\xdey?\xe7\xd4\xbd\x9a}#\xd9\x9c\x1b\xa9\xb9*\xf2\xcc(\xc9h\x94D\x91w\
-\xe1\xd7\xce\xeb\xbc\xf6\xc2\x86\x9c56\xbbX`\xb1\xc0\xfe\xb4\xc0b\xff\x9c @\
-\xb0X\xe0\x85a\xc0p\x04\x08+!\xc6+\xad\xbck\xbdr\xa2\x89\x12{4N\xc4\x19\xc7\
-\x99;\xc9\xb9\xb09$\xbb\xfaR\x97sy\xf6\x87\xd3],V_\xd8\x9c\x19r\xd8\x9c\xf3\
-\x81!\xf74\xabO\xd59]\xf5=O?\xe79\xcf\x13m\x88\xe8\t\xc0\x0eS\x85\xe1\x14\
-\xdd\xe2\xb1\xc9\x13\xf7\xf4NJ\x1d\xdf\xea\x01S\xb7\xa3z\xb6\x1fN\x7f\xffA\
-\xf5O\\\xb6\xea\'K\x1e{`\xca\x0e\x01Dj2\xa3\x12\xb9\'\x13wA:3\x86a\x98\xbd\
-\xdc\xd9\x9c\xf3f\xf5_l\x96\xfe0m\xca4M\xc7qT^@\xb5\x19\xbd\xff2\xd4\x05\xc0\
-v\xbf\x84a\x18*\x82BJC\xd0\xf2Z\xe5\xff\xae\x97\xff\x88\x8a\x8e#\xd6\xbd\xe4\
-\x81\xcbWM\xd9\x06\x90\x80 \xa9\x9bX\xb3\xa1\xb3\xc0\x9d\xa3\xe0TU\x1aBJ\x07\
-Gy\xf7\xfb\x82\xd2\x87GP\xbaY\xfc\x83\x8d\xa9\xdf7D\xcb\xe5\xab^\xf2\xc0\xe1\
-u\x03{[H\xa8\x8dVI\xfa\'h\xe5Uo\xfa\x94W\x9au\x1c\x87d\xa0\x94\x86\xa5\xdf\
-\xb9\xe3\xbc\xa6&\x0c\xd5\xa6\x99I\x8b855\xe5\xba\xae\xea\xe5\x98_\x8a\n#Q\
-\xcf\xf9\xf2\x91\xff\x94\x8b\xcfQ\'U\xdf\x9d\xe7y\x85B\xc1w\xfd\x11K\xa9\xfd\
-\xe7\xea\xdf\xe2\xa4N\x1d\xc7q7\x84F\x08)\x8d\xc0\xb8\xd4\xf0\x7f\x97"3\xf9\
-\xa6\xc7\x1ez\xec\xa1%6)\x08@\x90\xd4N\xcc\x99\xc8\x99O\x9c\xe3\xd2\x9eN\x17\
-\x03\xd3u\x143\x93M~\xe0e\xa4\x13|\xee\x1a\xd4\r\x90Z\xaf\xc5\xe2T\xb18\x05\
-\xdd]\x97\xa8\n\x15)#\xc0\xb2\xac\xf4\xd7\x98\xea\xbb\x1a\x7f\xc68b\xea\x0f\
-\xe9\xbat\x08\x1d\xb5\xc8l\x18\xc6\xdc\\mz\xbaJ\x08\xb1m\xdb\xf3<\x95:I\xe5\
-\xb3\x1cgT\xfb\xc7\x93RZ.\x95\xbe\xf2[\x1e"Z\x96\xa5\xbe)\xf5,\x0c\x9b\xf3\
-\xb2\x93n\xb6\x9d\xde7"\xd5hH)\x19\xe3Q\x14\xc5I\xa2f2B\x88eY\xbe\xef\xf9\
-\x9e\xa7*\xbe\x1a\xbd\xf2\x8a*\x85\xd4\xb1\xa3G\x8e\x1e\x993\x0c\xc3q\x1c\
-\x95$\xca\xf7\xfd]\xe5U~!lK6 \xb6\xfdP\xcf2v\xad%d\x99,&bxz\x8b\xc2\xcc\x19\
-\xd8\x93T\xe3\xfbB\xf6wq\xf6N\xea\t\xabDDf\x1co\xb8\xc7\x1b\x00\x90y\x0c\xd4\
-\x07\xdd^6\x02\x00\x90\xbd\xf2\x8f\xa9h*uK}\xc7\xfd\x16e\xd6TIU2\x8d\xd2K\
-\x92D=\xde[Na\xb3\x94`1\xf1N62-\xe4>\xa5V\x08\xbbU\x8d\x06\xd5S\xcd\xfa\x82\
-\xb0W-\xde0\x8c\xf4\xc9A\xb3\x12\xd9\xe5\xc8\x7f5\x1d\x10\x00H\xa7@\xdf\xf7\
-\x89]\xc8\xc9zjh#"\xa54\x15\xf7tS\x929<\xf0\xbc\x1f\x92YVU\x93\x90j6\xe7\x1f\
-\xc8NTN\xa6\xc6\xec\xae4H5\x92\xde\x0cYaU\xf7@z\x1bHi\n\xd3m\xb9G[\xf0Fn\xf0\
-\xd5\xa7\xec\x9e+)U\xf6\x1d/\x89\xf4\xaa9\xabk\xc8\x8dg\xf6\x06\xc8*\x9di\
-\x92\xfe\x83\xd3\xf5gedt\x7f\n\xf4m\x81\xf3=\xaf\x7fJ\xcb}\x89\xea\x9d\xec};\
-\xfexf\x9bM\x1f\x13\xd3\xec\xeaC\xeeY\x18\xf8)\xe8\xdd\xa2\xd9v\xb2_J:\xe1\
-\xa9q\xf3<7\xfb\xd9\xfe1Q\xefs\xce\xd5M.\xa5T\xedg\x1f\xc9\xdd\xce[\xfb\x0c\
-\x0b\xee)/\xa8\xaa\xc81\xf0\x98\xf1k-\xf1\xf6#\x994\x01\x80\x00\xb0\xe1\x0b\
-\xb3v\xa1fyU\x98\xc4\x05U\xe8\xbb\x93\xd2\xbb\xdc4\xcd\xecm\xa4\xa4\n\xb7\
-\xafI\xe6d\x0b\x00\xb2\x85\xe7\xb37\x9fR\x9f\xd4\x82\x1bv\r\xe9\x05\xa42\xad\
-<\x12\xe9\x84\x91u\xcb\xe6|\x08\xe9S\x94\x86\x7f\xe4j\xd5\xe7\xce\x98\xfeV\
-\x80\x8c\xa1\x94\xceg\xfd.\x02\xe8\x89{\x1a\xd1ad\xf6\x7f\xa6\xa7V\xe9X9\xe7\
-\xe9\xe5e\xbb?\x8e\xb7\x04\xb6kMV\xd9S\xa7Y\xae#\xd9\xf9l\xb4\x1bd\xd8\r\x90\
-\x1e\x9c\x15#%\xac\xb9\xc1O\xef\x04\x80m\xd3jv\x19#\xbd\x18k{y\xeb\x11\xfa\
-\x0e\x99\xf4\x90\xe9x\xe6n\x80\xec\xb7\x9f\xfd\xe2r\xeb\x13\x84\x10\xe5\x7fP\
-%\n\x84\x909?{\xb1\xcf\x19\x92\x1bIe\xe8\x00@6Y\xf1SL\x99\xb9\x1bCM\xfc\xb9g\
-aG}\x1f8\xdbe\xed\xa7\x9c?-7z\xe9\x98\xa8\t/+\xeei4\xcen\xbb\xb6\xffH\xd6\
-\xe6\x9d\xee\xbe\xe5\x11\x159\xfc\xe9\xd3\xa3\x03\x16\x14(\xf9V\xf8#\x0c\x9d\
-*\x80\x1a\xfeL\xd7\x87<\x91\xe2\xae\xc8\xca_\xaa\xda\xa9\xb8gWu\xb2bjf\xf3\
-\xd3\xf6\x9czY-H\x8dbs{\xdc\xee\x08}\xcf\xdd\xca*]e\xf6\xd7@\xbf\xecf\xaf\
-\xc7\xc8\xec\xddO\x7f1\x8c0\xdea\xbb\xb2g\x1c\x11\xf9\x05F\xb2\xdd\x94V~\x00\
-\xda\x0b\x96W\xef\xab\xc0\x0f\xd5\x02\xf4\xe6\xc2\xac\x83e\xfc\x07\x89l\xffa\
-N\x081MS\x8d\x00d\xb45\xed\x882\x96\xd3\'y\xb72\xa4&\x0f\xd8>\xb9\xe6f\xd6\
-\xdc\xe4\x9a\x1b\xfc\xac\x9d8p\xf0G_Rv\x8e\x19x\x03\xf4\x7f\xfb\xd9KUW\x9b\
-\x1a\x19\xea#\x94\xd2\xcdFC\x88\xec\x86\x1d \x84\x94J\x03<\xdd\xa9\xb2\x03\
-\x80a\x18J\xdcS}\x1cx\xd3\xee8\xaa\xb9\x1bCiqz3\x8c\xfeA@\xb6[<\xd9\x11\xce\
-\xcd\xb8\xfd\x8b%\xe9\x80\xa4\x93\xab\x9a\xf0\x84\x10jH\xd5\xe47\xda\xe4:P\
-\xb0\xe0nZHoXE\x0e\xcb\x9fVV\xf6\x8e\xf0\xe6\x12\x8a\x08v2\xdb\xbd\xf2B\x1aO\
-9\xa9\xe2\x9e{\xb6S\xa1L\x83grK\xf6\xa9\xa8\x99\xa6\xb9\xb6\xf6\xa4\x11\x04\
-\xae\xe3\xbe\xf6\xda\xab\x00\x90\x95\x80t\x85\'\xfb\xf0\xef\xf8k4k\x0c\xaa\
-\xe7!\r\x1bP-g\x7f\x9e\xc3\xf6\x1b:\xd5w\xb3\xb7\x8e\x97^j\xee\x8c9\xcb(}\
-\x08\xfbE\xa4_Gr\x06\x11\xc9\x98\xd84\xe3\xedI\xcf\x95\xed\xfe\xf8\x0fR\xd6\
-\x04S\x8d\xa4\x01\x8b\xb9#\xd3\x87y\xe0L6&\xd9\xc95m07\xf8\xfd\xe2\x9e\xf6.{\
-\r\xd9/:\xfd\xae\xc7\xec2f\xaa?g\xe5,\xfb\xd5\xe7\xc66\xab\xefF\xcf7\x98Z\
-\xee\xbd]\xfb[\xa6\x9e\xeb:\xae\xe3\xe6\x86(\xf7%*9\x86\xde\x1d2\xfa\xbe\x1d\
-\xdd\x97\xec\x8d\x91\xfd17\xd0g8\xf0KI\r\xfc\xd4J\xc8*{\xeeq\xc8}\x17\xd9\
-\xdf\x07\x88\xa8\xc4=\xfd\x06\xd39`W?\xf5\xf6\x1f\x99\x04\xdd\x95O\x00\xc6q\
-\xb0\xd9>~\xad%\x1e\xf1\xf6\n\x00\x90\x91\x1e\x1ej\xba^e+\x9d\xe4\xa4\x8a;\
-\xf4\x19\xce\xeb\xeb\x1b\x96e\xfa\xbe\x9f\x0b\x7f\x86\x8c5\xaa\xfe\x1b4[W\
-\xbf\xf8\xd5\x89\xe3\xc7_\x7f\xfd\x02\x00\x18\x86\x81\x99\xca\xcbi\xe3Y\xfbn\
-\xc4\r\x9d\xbei\xf4*=\xa9{4;a`\x86\xac\xa1\x97U\x99\xf4\xf5\x08e\xe9\x973u\
-\xdf\x8fp\xfed\x85\xcf\xcc8\xd0\xb3\x97af\x16\xc1\x06v?mj\x9c/%m_\xd9\x929a\
-\xcd\xb5\xfft\xde\x83\xfe1\xc9\xcd\xafr;\x03\x07\x9f\xf4<3\xd9\xaf8;8\xe3_\
-\x80\xea\xe0?\xdf\xbcu\xe2\xc4q\xdf\xf3\xb2\xdf~\xf6\xab\x87\xed_J\xdaq\xe5|\
-\x00\x00D\\[{\xd2\xe9f\xcb\xda\xba\x86\xa3G\x8e\xa4\x1e\xeal\xaf\xd3\x99^u9\
-\x15\xf7\xec\x17\xb7\xdb\xc1\xcc\x1aLYq\x1f\xff\xcb\xea\xbfKS\x81NM\x10\x99\
-\x8f\x93\xd9\xf6 \xa4W\xae~4\xa8\'\x143\xfe\xd5\xdd\xce[\xfbO\xd2\xb8\xab"\
-\xa3\xfa3\xf1\xa68\xc5\xa3c\xd7Z\xba\x83\xb2\x1bC\xc5F,\xccN\x9f&tK\xd2\'X\
-\xdcS\xc9@\xc4\x1b\xbf\xfe\xa7\xff\xff\xd3\xbfv\x1c\xe7;\xff\xea[33\xd3\x98Y\
-h\xcd\x1e\t\xdd;I=\xe1\xa0\x8c\xa6\xf4\xd6\xc9i\x10\xd9\xce\xe8\x8bIoh\xf5_u\
-Sf\xe7\x8c\xec4\x03\xdb\x1f\xf2\xf4\xbf#d=w\xa2\xec]\x9e\x13\x11\xd8\xee\x00\
-\xc9\x9e"7E\xa5\xef\xf7\xf7}\xb7\xdd\xef\x1f\x87\xb4e\xd8>\xfe\xc3\xda\x7f\
-\x96\x07\x95d\xe6We{fG\xbe\x7fv\xc9\r~\xfa\x02v)\xeb\xd9\x1b\xec\xfa\xf5_\
-\x7f\xfa\xd7\x7f}\xfc\xd8\xb1?\xfa\xc6\xff\xa5\xa2\xf4p;\xc3:\x0e\x00\xa9u\
-\x1f\x04\xcd\xbb\xf7\x96rIT\x08\xa1\x0b\x0b\xf3\x96e\xf5_[z\xe5\xfd\xb3\xd7S\
-\x0fl\xf6\x86\xe9\x9f\x93\xc6o0w\xa3\xa6_D\xce\x8a\x82\xbe\t/w\nu\x93?\xaf\
-\xde\xed\x0f"Z\x97q7\t\xf9\x08\xe7\xb8W99Nk2i\x88\xb0\x0e\x00D%m\x1f\xe2\xe1\
-1\xbd\x8a35\xb7\xed\x9d]\\\xf2A"\xf7\xd8,/\xaf\x00@\x1c\xc7\x0f\x1e><rd\x0ez\
-\x0f\x1e\xf6\xb1\xd5\x02\x00\xf4n\x14\x18.@\xb9\x17\xa3\xc9\x1e\x9f}6\x06\
-\xce\x1c\x90\xb9M\x9f\xe2,\xa4g0f5t\xc4Y\xb2\xff\xcc\x1d0\xb0\xef\xb0\xfb\
-\xee\xe7N\xaa\xfe\xb9c\xe3\xbbm\x7f\xc7\xf3\xa6\xc3\x02\x991\xc9\xa9\t\x8c\
-\x1c\x96q\xc8\xdeT\x0f\x1f=\xfa\xfc\xef\x7f!%.\xaf<\xf8\xff>\xfa\xf8_~\xf5\
-\xed\xe3\xc7\x8f\xa5\x87\r;\xbb\xfag*ya\x18.\xde\xf8u\xbb\xd3\xd9\xfe\x0b\
-\x9eT*\xa5\xda\xec\xcc@e\xcf\xbeHO\xf1,\x03;\xac\xcd\xfe?\x8d\xdf\xda\x8e7\
-\xea\xb0g!k\xa3<\x97\xde\xed\x1b,\xb8\xab^\x8c0\xdb\xbd\xf2\xfcx\xb5\x96\x90\
-\xf5~\x04\xe0\x0e\x1e\x9e\xfc\xc2\xec\xa4\x8a;\x00dm\x81\xdf~\xfdb\xbd\xbeV*\
-\x15/\x9c\xff\n\xdd\xbe\xe0\x83\xbdm\x84\x99\xd8\xb8\xbc\xd5\x00\xcf\xf5F\
-\x19\xf1\x84\x8c8\xf8\xa9O\x94>\x06\xbb=\xcb\xd3i\xf7\xf8\x17\xb6G\x8d\x8fy\
-\xf6=\x1d\xfc\xf47\x81\x94\xb2\xd9l}\xfa\xf3\xbf\t\xc3Py\xc9\xeb\xf5\xb5\x8f\
->\xfe\xe9\xef\xfe\xceo_8\x7f\xde\xf3\xdc\x11\xa7H\x1b\x11B<~\xbcz\xedW\xffP\
-\xaf\xe7*\x9b\xa3a\x98\xe7\xce\x9e\xf5}\x7f\x98\x95\xba\x17_\xe2sos\xcco$w\
-\xba\x89\xd0\xf1~DgU\x05,*-\x1e|\x10!N\xf1\xd8x\xad\xd5e\xbc\t\x00\x84\xc0\
-\x88d\x03N\xf1h\x7f:\xc9\x89\x14\xf7\xd4bJ\x17g\xaa\xd5\xca\x9f\xfe\x9b\x7f\
-\xad<q\xd8[hM!\x84(Y\xef\xad\xb0\x89\xb4\x95}\xb8\xda}\xb8/\'\xe8\xd6\xdfg\
-\xf6bd\xd2{\x8fs\xdel\xb6\xfe\xe6\xb3\xbf\xdd\xd8\xd8TgS\x07t:\xe1\x95\xbf\
-\xfd\xbb;w\xee\x9d:u\xf2\xec\x99\xd3\xd5jeD#\x0f\x1f>\xba}\xf7\xee\xed[\xb7;\
-a\xd4\x97f\x80\x1c=:w\xf2\x95\x85\x81\xfe\x8a\t\xe5\x10ta\x14(\x934\xb5\xef\
-\xa8P\xf4\xd9\xb1\xccv\x14\xe9\x8f\x00\xc4\xa1S\x051,\xbfz\xaa\xff\xfd\x89\
-\x14wEj:\xa5\x8b3\xc3\xee~\xf5 \xa9x\xf6\xf4x\xec\xbd\x7f\xc8\xef6\xcds%\xab\
-\xec\x8c\xb1_\xff\xd3?-\xaf\xacd\x1d\x08)\x8f\x1e?~\xf4\xf8\xf1?~\xb9xd\xaev\
-\xfc\xf8\xb1j\xb5\xa2b<\xd4\xad\x18E\xf1\x93\'OVV\x1e<Y\xdf\x88\xa2T\xd6\xb7\
-\xb5P*\x15_\xbfp\xfe\xe0G\x86hR\xb6\xa5\xf6\x1dZ\x91\x83\xfa\xd5\xd3c\xb5\
-\xd6z\x80<De\xb6\x8f\xceKc\x0eH\xbd9\xa9\xe2\x9e\xf5xJ)[\xad\xd6\xd2\xf2\n\
-\xa5\xf4\xf2\x1b\x97 \xf3\xd3/\x8e\xe3\xc5\xc5\x1b\x8fWW\xa3(\xb6,\xb3V\x9b=\
-u\xf2\x95\xee_1\xef\x0f\x8d\xe3x\xf1\xfa\x8dz}-\x8e\xe3R\xa947W;w\xf6\x8c\
-\xe3\x8cNX\xaay\xe9H\xc5=I\x92r\xa945U\xc8\xd4\xe8vd\x9b\xa7\x00\x00 \x00IDA\
-T\xe8\x1e\x92\xcat\xbb\xdd\xba}\xa7}\xe7\xee=JiV\x9c\x11sUSH\xcel\xf7<\xf7\
-\xb7/^(\x16\x8b\xe6n6\tk^ (9k-\xa9\xd7#R\xfb:\xc5\xa3\x86\xed\x0f\xfa\xcb\
-\xf6\xd6D\xc2\x9aK\xea\x9e\x90rh\xf1&\xc3\xf6\x87\xe5\xa5\x99Tq\xcf\xd1jw~y\
-\xf5\x1a\x00\\~\xe3R*\xd9K\xcb+\x1f\xff\xf4\x7f\xc7\xf1V-\x98{\xf7\x97n\xde\
-\xba}dN\xad)o-\xb1"\xe2\xe2\xf5\x1b\x9f\xff\xdd/2\x07\xaf,^\xbf\xf1\xcb\xab\
-\xd7\xbe\xfd\xff|sn\xae\xb6\xcf\xdd\xd1\x1cd\xd2_\x81I\x92LM\x15~\xe7\xf5\
-\x8b\xbf\xf9\xe7\x9bO\xd6\xd7{\x8b^9\x03\x9c\xa4\x1f\xc9\xb6\x91Q\xf3T\xd3\
-\xb7>55U\xb8x\xe1+\xb3\xb33\xd9\x1d=\xfb\xd17\xcd3\xc0[+\xc8#\xe8\xe6k\xdc\
-\x9d\x0b\xa5\x1f\xd6\xbc\x87\xd9\xa4\xedC\xc3\x1f\xcf\x0c\xcbK3\xa9\xe2\x9e[\
-a\xdf\n\x1c\xeb\x85\xd0\xae\xae\xd6?\xfe\xf8\x7f\xc5IR,N]\xbcp\xde\xf3<!\xc4\
-\xfd\xa5\xa5\xbbw\xefon6\xa0\xe7\x96Q\x07/-/\x7f\xfa\xf3\xbf\x01\x80\xdf{\
-\xf3\xf2\x99\xd3\'K\xa5R\xab\xdd\xfe\x87\x7f\xf8\xf2\xfa\x8d_\x7f\xf8??\xfe\
-\xb7\xff\xe6\xfb*\x01\x93F\x93]\xecQ\x9e\x19\xc3\xa0gN\x9f\x9c*\xf8\x8fW\xd7\
-z9\xdcw\x84\xe0V\x01o\xb2-\x8f\x0c!G\xe6j\xe7\xce\x9e.\x97\xcbin\x86C\xe3p?\
-\xc4\xa0d\xbc\xd5\xad\xd0;"G\x98[\x9e\xa7\xe6\xce\xf9\xce$k\xa5\x19\xc7\xc4\
-\xc8\r\xaevavX#\x93*\xee9\xd2YR\xf6\xb2\x88,\xde\xb8\x11\'\xc9\xcc\xf4\xf4\
-\x1f}\xe3kj\xfb\xb2\x10\xa2Z)\x97K\xa5_\xfd\xc3\x97\xb9\x83\x7f\xfe\xd7\x9f\
-\x01\xc0\x1f\xbe\xf5\xfb\xaf\x9e;K\x08a\x8cy\xae\xfb\xd5\xb7\xdf\x8a\xe2\xf8\
-\xf6\xed;\x8b\x8b7._\xbe\xa4\x1f-M\xd6\x8f\x97\xf5\n"\xe2\xf4t\xd5\xf7\xbd\
-\x8d\xcd`\xb3\xd1\x08\xc3\x81\x12\xdf\r\xbe\x85\xee\xffeBAz&|\xb9\\\x9e\xab\
-\xcd\x9e8~\xcc\xf3\xbc\\r\xca\xbd\xee\x9a\xe6\x19a\xc1}eh\xab\x1d\xa4\x03\
-\xbf0b:^y\xac\xd4\xbe\xacq\'-B0<\xfc\x91\x8e\xde\xe0:\xa9\x8e\xbc\xdc\x03\
-\x96\xfa.S{\xea7\xbf\xb9\t\x00*\xc1@6o\xd1\xc2\xfc\x89J\xa5\xac\x9aP\x07?z\
-\xfc\xb8\xd9lMM\x15N\x9d|\x851\xa6\x0eV\x9c:\xf9\n\x00,-\xaf\xbc\xa8nj\x0e\
-\x14\xd9\x90\xbetGejY;\x8es\xf4H\xed\xcc\xa9W\x16\xe6OT\xabe\'\x9f\x906\xbf^\
-\x9a\xe28\xce\xf4t\xf5\xf4\xa9\x93\xaf\xbdz\xf6\xc4\xf1c\xae\xeb\xba\xae\x9b\
-\xcb\x97\xa9\xf5\xfd \x83<\xe2\x9dLj\xdf!\x87\x15\xb6\xef \x1d\x86\x88\xd6E\
-\xaf~\xf2\x88Hy\xb7tl\xf4\x06\xd7\t\xb6\xdc\xd3\x18\x98l\xf6\x12\x15\x0f\xf3\
-\xe0Aw\xa0\x8f\x1e\x99K\xd3Y\xa4\xe9;\x8e\x1f;\xba\xb9\xd9\xc0\xde\x9eo\xb5\
-\x01*\x89\x93\xff\xfd\xc9\xa7i\xe3\x04\x08!\x84q\x06\x00\xc3\xc2!4/\'$\x93\
-\x8e-\xbd\xf7(\xa5\xea\xdes\x1c\xa7\xe683\xd3\x15!e\x14E\xedv\x18\xc51\xe7\\\
-J\x81\x12\xa4\xca\xe7N(\xa1\xc44\x0c\xd7u\xa6\n\x85B\xc1O\xb3\x1f\xab*\x02J\
-\xdc\x9f"?\x8c\xe6\x85\xc0\x82\xbb %\x10\x90\xc3w\x90\x1ava\xbc\xd4\xbe\xc8\
-\x1a\xb7\xbb\xafFF\xca\xbb\xe5\x1d\xca\xf2M\xb6\xb8+\xbd\xde\n]\x07`\x8ce\
-\x17\xafR#\xab?w\x15bw&\x10B\x02@\xc2\xd8\xeaj\x1d\x86\xa0#&5\neD\xab\xbc+*\
-\x92\x8ad\x12\xeb\xa7\xb9\xb1\xd4\x04`[V\xb9T\x1a\xdd\x1a\xcd\xa4\xa1V\t\xe5\
-]\xd7M\xbd\xed\xfa\xae;\xf8\x88h\x83\x87u\x00\x00\x1c\xb5\x83\xb4P\xfb-\x18\
-\xe3\xdbd\xc1=\xc9\xba\xc1W\x9c\xe3\xd0j\xda\xa5\x13;F\xcaO\xaa\xb8g\xb7\x08\
-\xa6Ie\x01@%%O\x0b\'EQT,\x16s\xca\xden\xab@\xd4nm\x04\xc3\xa0\x00P\x9b\x9d\
-\xf9\xea\xdbo\xa5\x8d\xab\xa7NYRi\xbe=\x8d\x06z\x96{\xf6u\x9a\xd56\x9b~y`\
-\xc6\xb4\xf4S\xa4\xaf\xc0@6\xa1\xff8\t\xe55\x07\x03d\xc1m\xb5~\xca\xf9\x88\
-\x1d\xa4\xc7\xfaw\x90\xf6#y\x877\xbb\xc1\x94R\x02\x17\x83[\xa3\xa6\xebO\x9f\
-\xda\xb1\xb5I\x15w\xc8\xe5\x8d\xe9e\x14PO\x94\xef{\x85\x82\xdfnwV\x1e<|\xfdb\
-5\xeb\xa0O\x92\xe4\xc1\xc3G\xea\xf3\xea!\x9c\x99\x9e\x06\x80\xfa\xda\x93\x8d\
-\xcd\xcdj\xa5\x92m\xbc\xd9l"b\x9a\x1bO\xa3\xc9\xba\xddS\xbb\xdb\xc8\xd4\x12H\
-30g\x93\xfd\xa6\xf7O\xff\x07\xd3l\xcc\xd9\x84\xfe\xbb\xca\xc4\xa9y\x81\xf0\
-\xce\xaaL\xda@F\x86?R\xd3\xab\xbc2Nk\xacqG\x15b\x04\x00&\x86\xfc\x08\x00\xf0\
-\xa7O\x8d\xe3\xbb\x9f`qW\xe4\x16TS#\xfd\xf4\xa9S_.^\xbf\xf6\xab\x7f4M\xf3\
-\xe2\x85\xf3\xea1k\xb6Z\x7f\xff\x8b\xab\xaaD=\x02(\xab\xdfq\xec\x85\xf9\x13K\
-\xcb+W\xfe\xf6\xef\x7f\xff\xcd7j\xb5nh\xd1\xe6f\xe3W\xff\xf8%!\xe4_\xfe\x8b\
-\xb7\x8f\x1d=\xaa=3\x1aE\xea\xebK\r\xf04\x8f|\xaa\xec\xb9\xdaXYq\xcf~*\x9b\
-\xc4<\x97\xd0\x1f\xf4\x1a\xcf\xc1\x07eZ\x1dI\x88\xe1.\x94\xf1r\x84\x89xS\x84\
-\xdd\xe4B#\xd6Q\xc7/\xcb7\xf1\xe2\xde\x8fR\xe1\xaf\xbc\xf6j\x10\x04\xf7\x97\
-\x96\x7f\xf1\xcb/\xfe\xe97\xff\\(\x14P\xca\xc7\xabu\x00(\x97K\x8dF\x00\x99\
-\xect\xaf\xfd\xd6\xb9\xa0\xd9l4\x82\xcf\xfe\xf6\xef*\x95\xb2*e\xa7\xb28\xcdL\
-O\x97J%mCir\xa8\xfb!\x9b\xc4_\xd9\xef\xd9<\xfe\xfd\xe9ma\xbbO&\x1bocLNiP\x8d\
-\x82\xb7\x1f"\x0f\x81\x00"\x0e\xdbAJL\'[@c8\xc8\x1aw\xba\xafF\xac\xa3\x02\
-\x14f\xce\x8d\xe3\xbb\x87C#\xee\xfdy\xec\x10\xf1\xed?\xfc\x83\x99\x99\xe9\
-\xc5\xeb\xbfn6[\xcdf\x0b\x00|\xdf?\xff\xda\xab\x9d0T\xe2\x9e\xa6\x99\xb6m\
-\xfb\x0f\xff\xe0\xf7n\xdd\xbe{\xef\xfe\x92\xda\xe2\x04\x00\xb6m\xbd\xb2\xb0\
-\xf0\x07\xbf\xff\xa6\xab3\x10h\x06\x91\x9a\xf0\xd0\x8b\x8c\xc4\xbe\xac\xe5\
-\xc3\xc4=+\xf1d7\t\xfd5\x07\x04\x94\x9c\xf5\xfc\xe3\x9c\x0f\xdfAZ=9\x8e\x0b\
-\x85\xb7\x1f\xcb$P\xafG\xfc\x08p\x8aGMw\xd4\x12}\x96\x9d\xb3\xc5\x1e@T<\x8c\
-\nHW\xff\xc5L&H\xf58\xa5!\r\x94\xd2\'\xeb\xeba\x18QB\x8a\xc5)\xec\x15\x12#\
-\xbd:\t\xb2W7U}\xa4\xbe\xf6\x84\x10\xa0\x84\x1e;v4[CUG\xa4iF0p\x7fS\xfape\
-\x9f\xb2\xfc\xe6\xeag\xc8)\xafy\x81\xb0\xe0.\x0b\xee\x03\x01\x94\x18%C\x13\
-\xbf\x94\xe7\x7f\x8f\x90\x1d\xb6\x13\xa1\xe4\xd1\xe3_\xa0\x88\x01\x00\x11"\
-\x86\x03\xa7\nB\xcd\xf2\xfc\xef\x8d\x97\x05\x1e`\xa2-\xf7\xac\xed\x03=\x1fhv\
-\xd3\xa0\x8ad\'\x84\x94\x8a\xc5\x82\xef\xab@\x9a\xf4G\xb4\xb2\xb3\x08!\xb9"\
-\x9fs\xb5Y\x9aA\xffL\xd6\x8cCV\x9d\x07j\xfa\xc0\xe3A\x0b\xfad\x82\x92\xf1\
-\xd6\x03\xf5\x9a\x89|\xa6\xe6\x14\xbfzzGe\x07\x95\x94Ft\xb3Zq1X\xd9\x01\xc0\
-\xab,\x8c\xaf\xec0\xb9\xe2\x9eU\xf6\xb4\x1c\xb3zN\xd2\xe0\xc8\xdcBk\xfa#:-\
-\xca\xac\xde\xa7\x94\xa6\xfa\x9e\xfb\xbd\xac\xf5]\xf3\x14h\xe1>\xf4\xb0\xe0>\
-J\x0e\x04\xa4\xc4\x11u\xefF$~IA\x11\xf3\xe6\xb2z-qx\xf8\xa3\xe5\xb9\xe5q|\
-\xf7\x99\x0b\xd8\xd5\xd1\x07\x87\xac\xb2g\xa3\x17\x00@9^\x94d\xa7?\x93\xd3\
-\xc0\xe4\xac\xb8\x13B\xd2\x0cPiTrj\xdako\x8cF\xa3\x19\x00JU\xd4\x14\x00\x18\
-\x1fa\xb6\x9f\x1ak\xd7R\xe3."\xef\xb564\xfc\xb10<\xfb\xe30&R\xdc\x95\xfb%U\
-\xf6\xac[\x06zR\xdeo\xbc\xa7\x06x\x1a\x9f\x00\x99\x99 -\xe2A2\xbbK\xf4.\x12\
-\x8dF\x93\x83wVQ$\xcal\x97r\xb0\xd9n\x17j\x967\xa0\x02W\x0e\x994y\xe7\xb1z="\
-\xfc\xd1\xf2\xa7\xed\xa9]\'\x1e\x9fHq\x87\x9e\x04C\xa6\x82h\xaa\xc2\xca\xe7N\
-3E\xd3\xd3\x8f\xa4.\x97tA5\x15\xf7t\xbf\t\xf4"\x1f\xd2\xc3\xb4\xbek4\x9a.(\
-\xd2 \x99\x91\xf9\x1a\xc7\xaa\xb5\x944n\xa79m\x87\xa7\x91\x19\xb7\xb5\x1c\
-\x93*\xee\xd0\xd3\xf7\xdc\xde?\x85\xb2\xc4\xb3\x11\x0b\xb9\xc8\x84\xac^\x0f\
-\x14\xf7\\t\x9aF\xa3\xd1\x00\x00k=P\xb1\xedB\xa0\x1c\x96\xafq\xbcZK"\\S\xc5\
-\xaf\x01\x80\x8f\xd8\x03U:f:\xc5\xa7\xb8\xd4I\x15we\xb0\x0f\x13\xdf\xac\x15\
-\x9f}3\xf7b\xd8\xc1::M\xa3\xd1\xf4\xb3U\x91cd\xb9jo\x9cZK(\x93t\xd7\x12\x02\
-\x13\x83\x8f"\x86\xedUO>\xcd\xb5N\xae\xb8\xc3N\xb2\xbb+Q\xd6\n\xae\xd1hv\x84\
-\xb7V\x94\xb7\x9d\x8b\xa1a\xae\xdex\xb5\x96x\xeb\x01\xf2N\xf7\xf5\xf0\xf0G\
-\xbf\xfa\n5vnm \x93Z\xacC\xa3\xd1h\xf6\x13\x14qj\xb6\xf3!f;5]w\xacZK\xc8z5\
-\xf9F\x84?\x1av\xc1)\x1d\x7f\xba\xab\x05-\xee\x1a\x8dF3\x0e\xacy\x1f\xa5\x00\
-\x022\x9fQb\x0bo\xbcd\x03"|\x82\xa2[\x8bQ\xad\xca\x0e\xa40sv\x9c=P\xc3\xd0\
-\xe2\xae\xd1h4; y\'\x8dYT\xb1\xed\xfd\x18\xce\x94[<\xbas[(\xd3\x1caB\x0e]\
-\x95\xb5\n\xb3\x96?\xfdtW\xab\xd0\xe2\xae\xd1h4;\xc0\x82{\xaa\x90\x9e\x10Cc\
-\xdb\xc7\xdc\xb5\xc4[\x0fd\xeam\xe7C\x0e"\xb40\xb2\xf8\xf58hq\xd7h4\x9aQ\xc8\
-$\xe8fZ\x1f^\x91\xc3\xf2\xa7\xc7J6 \x19k\xdeS\xafU\xf8\xe3@\xbc\xf2\xfc8\xc1\
-\x94\xa3\xd1\xe2\xae\xd1h4\xa3`\xc1\xfdn!=\x89"_\x8c\xb9\x8b?N\xf8#\x00\x0b\
-\xee\xa2\xe4\xa0j8\x0f\t\x7f\xa4\xa6\xe3U\xc7\xaa\xdc4\x1a-\xee\x1a\x8dF3\
-\x14\x11m\x88h\x1d\x94\x1c\xf3\xc1\x0e\x19\xa7xd\x9c4\xeb\x92\xb5y\xfb\x91z=\
-\xcal\xaf\x8eUEoG\xb4\xb8k4\x1a\xcdP\xba^\x14\xd2\xad\xa1\xd1\x0f\xa1\xc6X\
-\xbb\x96\x00X\xe3\x0e\xa0\xaa\xa9;\xaa\x8a\xdeX\xab\xb2c\xa0\xc5]\xa3\xd1h\
-\x06#\xc2\xba\x8c\x83\xb4\xfe\xf5\x10\xb3\xfd\x98ay;7\x15m\x88\xe8I\xf7\xb5\
-\x1c\x12LIHa\xe6\xec\x98U\xf4vD\x8b\xbbF\xa3\xd1\x0c\x04\xd3\xfa\xd7\xc3\xbc\
-(\xc4\xb0\xc7\xf3\x8f#\x0b\xb6\x92\r\x0c\xf3\xb6\xdb\x85\x9a\xe9\x96\x9f\xe6\
-J\x07\xa1\xc5]\xa3\xd1h\x06\xc0\xdb\x8f$k\xab\xfa\xd7\xc3\xccv\xaf2?Nz\x00\
-\xde~$\x93f\xf7\xf5P\xf7\x8eY\x989\xfb,\x17\x9cC\x8b\xbbF\xa3\xd1\xe4A\xc9X\
-\xb3k\xb63\x0e\x03\xbd(\x86]pK;\'\x1b@\x91\xb0\xe0\xaez="\xd9\x80W=IM\xe7)/w\
-\x10Z\xdc5\x1a\x8d&\x0f\x0b\xee"\x8f\xd5:\xea\xe0Bz\x84L\xd5^\x1b\xa7:R\xd2\
-\xb8\x85"\xe96;$\xd9\x80\xe5U\xbc\xca\xc23]q\x1fZ\xdc5\x1a\x8df\x1b\x92wx\
-\xfb1@w\xd7\xd2@9\xb6\xfd\xd9\xb1\xc2\x1f\xe3\x86\xe8\xac\x02\x00\x01\xe0bH\
-\x90\x0c!\xfesu\xc8(\xb4\xb8k4\x1a\xcd6XpO\xc5,\xf2a\xb9_\xc6\xad\x8e\x84i\
-\xd2v\tC\xe3m\xdc\xe2S\x96\xe3\x18\x8d\x16w\x8dF\xa3\xd9b+\xd9\x00\x0cM\xed\
-\xeb\x96\x8e\x8fUk\xa9S\x97I\x03\x94\xd9\xcea\xd8:\xea\x98a\xf2\xbbE\x8b\xbb\
-F\xa3\xd1l\xc1\x82{j\xfdt\xe8&RB\xdcq\xd2\xac\xa3Hz\xe1\x8frx\x98\xbcW=9Nq\
-\x8f\xa7@\x8b\xbbF\xa3\xd1t\x11\xf1\xa6\x886\x00\x00\x87{Q,\xaf:\x8e\xd9\xce\
-Z+\xc8{I\xdb\xc5\x90,\xc1\xb6?^q\x8f\xa7A\x8b\xbbF\xa3\xd1tI3\xad\x8f0\xdb\
-\xc7\xf1\xb6\xa3\x88ys\t\x00\x08\x80\x9408\xde\x06\xc0\x9f~\xa6r\x1c\xa3\xd1\
-\xe2\xae\xd1h4\x00\x00\xbc\xb3\xaa\xb6\x1a\xa9]K\x03\x8fq\xa6\x8e\x8c\xb3\
-\xf8\xc9\x82{\xdd\xec\x8f\x00lX\xbcMa\xd6.\xcc<\xcb\x05\x8fF\x8b\xbbF\xa3\
-\xd1\x00\x00*[\x1b\x00\xb8\x80\x81\x15\xab\xc7\xcc\x11&Y\x8bw\x1e\x81ZG\x95\
-\xf0l\xf16O\xcfsH,\xa9\xd1h4\x93N7\xd9\x00\x80\x1c\x9el\xc0)\x1d7,wT#\\p.dp\
-\x97\xa0\x04 8"\xde\xa6|\xc2\xb0\x0b\xcf\xe1\xba\x87\xa3\xc5]\xa3\xd1\xbc\
-\xf4\xa0H\x93\rp\x01\x08\x83\xccv\xc3\xf6+Cs\x84\t\xce\xe3(\x899!l\xcdN\x9e \
-\x10\x02\xc0\x04\xc8A\xbf\x00\x80\x90q\xf2\x16<#\xda-\xa3\xd1h^vXk\x05y\x0c\
-\x00\x12\x87$\x1b\x00\xf0*\x0b\xc4\xb0\x06~\\\n\xb9Y\xafGq\xe8{\xe8\xc3c@IF\
-\x86?\xda\x85\xda\xe8_\x00\xcf\x05-\xee\x1a\x8d\xe6\xa5\x06%\xe7\xad\x15\xf5\
-\x9a\xf1\xc11\x8b\xd4rG\xc4\xb6wZ\xad\xf5\xfa\xaa\x8c\xb8\xc16x\x14\x00\xa1\
-\xa0\xc2\x1f\x87\x14\xf7\xf0\x9f\xb9\xf8\xf58h\xb7\x8cF\xa3y\xa9a\xcd%\x14\
-\x0c\x00\x84D!\x07\xdb\xda\x85\xe9\xb3#r\x84!`\xb9:\x8b<\x8a7\x97\x08\xa1\
-\x04@\x0c\x0f\x7ft\xcb\x0b\xfb`\xb6\x83\xb6\xdc5\x1a\xcd\xcb\x0c\x8a\x98\xb7\
-\x1fB/fq\xe01\xa6[\xb2\xa7j#\x1a1\r\xd3-\xcd8f\x8b`\xac\xd6Q\x87\x85?R\xcb}\
-\xee\xd9\x1f\x87^\xd5\xfe\x9cF\xa3\xd1h\x0e ,\xb8\x07\x92wK\xa4\x0e\xa9k::f1\
-l\xb5\xe3\x04\x817l\\\x070\x08\x00\x178\xacD\xea\xe8_\x00\xcf\x17-\xee\x1a\
-\x8d\xe6%E\xf2\x0e\xef\xac\x82*}7$f\xd1.\xd4,\xaf:\xac\x858\x8a;\x9d\x18\x81\
-:r\x8d C0$\x02\x1bR\x8e\xc3\xf2\xaa\xa3\x7f\x01<_\xb4\xb8k4\x9a\x97\x14\xd6\
-\xb8\x0b(\x81\x80\xe08,f\xd1\xab\x9e\x1c\xfca\x84v\xab\xcd\x12&\xc1\xb0ih$u\
-\x04J\x080\x8eBH\xa2\xd6e\t\xd9\xca.@\x88?\xb3\x1f\xeb\xa8)Z\xdc5\x1a\xcd\
-\xcb\x88L\x02\x11=\x81n\xc5\xea!\xc9\x06\x8a\xc7Lgj\xc0\x1f\x10\xda\xadv\x14\
-\xc6\xc4\xb0\x0c*\xccd\x85 "\xa1j\x03\x94\xe5\x95-\xafBA\xb2\xb8\xc9\xa2@\
-\xe9\xbcS<\xba\x17I\xdbG\xa0\xc5]\xa3\xd1\xbc\x8c$\xc1]\x95d\x80\x0f1\xdb\t5\
-\xfd!f{\xb3\x11\xc41\xa3\xa6%\x05\xb7I\x83\x88\x06\x12\x83\x00\xb4#\xe9\x94^\
-)\x1d9\x0b@\x00c\x0fxg\xed~\xd8xDL\xdb\xaf\xeem\xb2\x81~t\xb4\x8cF\xa3y\xe9\
-\x10\xd1\x86\x8c6\x81\x8c2\xdb\xdd\xf2\xfc\xc0\x8a\xd5a\xbb\x13F\xcc0-!\xb8e\
-Q\x8b=\x02B\x08@\xc2\x91\t\x07\xd0\xdf\x9a)\x088~\x15\xd0\xf2\xab\xaf\xecQ\
-\xd2\xf6\x11h\xcb]\xa3\xd1\xbcl \x0b\xee\xaaW\x9c\xe3\xe0\x1ca\xa6\xe3U\xe6\
-\xfb?\xd9\x0e\x9aQ\x94\x98\xa6\xc19\xb7\x1d\xcfL\xee\x81\x0c\tP\x00h\xc7H\
-\xa9\xddn\x85^\xc2\x12\x01q\xc8\x00\x19\x01#\xa633\xa5\x01M\xed5\xdar\xd7h4/\
-\x17\xbcS\x97I\x13\x08H9\xd4l\xf7+\xaf\x10\x9a\xb7}[\x8df\x181BM)\xd1\xb2l\
-\x13\x03\xc2\xd6\x10\x08\x10\x888\xc4LJ\xe4\xa6c\xb5;a\x1cE\x12%\x97T\x80\
-\xe3\xcd\x9c\x88\xc2h\xef\xbb\x95G[\xee\x1a\x8d\xe6\xe5"M6\xc0\x05\x0e\xcc\
-\x11f\xd8\x05\xa7t,\xf7fs\xa3\x113i\x18\x94si\xdb\x14y\xcc\xc2\x15\x872\x00\
-\x8a\x08\xedP\x98\xa6e\xf93q"\xa2\x8du@\x04@\x02\x84PJ\x08mq6\x7f\xe64!\x03#\
-$\xf7\n-\xee\x1a\x8d\xe6%B&M\xc9Z\xb0C\x81\xa4\xd3\xb9\x02I\xc1F#N\xa4eR\t\
-\xc4\xb2@\xb2$\x0e7\x8a\xb4\t@\x81@\'\x92\x12\x8c\xf2\xec\xaba\x9b\xb7[\x1b\
-\x94\x1a@\x00\x94G\x1f\xa0[\x915a\x96\xb3\xafnw-\xee\x1a\x8d\xe6%\x825\x97\
-\x10\x91\x000>\xd8l7\xdd\xb2]\x98M\xff\x89R\x06\x9b\x01\x13\xc4\xa0\xc8$\xd8\
-&P\x14\x89\xe4\x164\x08\x05\x00\xc2\x85\x88\xb9Q\x9a9\x9b0\x82\x86e{\x9e\x14\
-R}\xb4\xd7\x04\x18\xa6aX\xfb-\xb6Z\xdc5\x1a\xcd\xcb\x02\x0f\xeb<\\#\x00|X\
-\x8e0j\x14f_\xdd:\x9e\xb1v\xb3\xc38\x02H\xa4\x86c\x12\x1e\x85\x1c\xc0\xb2\
-\x11\x92\x0e\x01@\x94\x9c\x94\xfc\xca\xd1(\x01!\x85\xe3\xfa\xaec\xb6:m!$H\
-\xd9m\x92\xd2\x99Z\x8d\xd2\xfd^\xe0\xd4\xe2\xae\xd1h^\x0eP\xb0\xc6]\x00\x18U\
- \xa9t<\xdd\xb5\x94Dq\xb3\xd1"\x86E\x08P\xc3\xb6\x0c\x19wZ\x86\xe5\x94\xa6+\
-\xebK\xbf,\x98\x08\x88`\xcdPz\xa4\x13K!\x98\xe3\x97l\xdb\x8c\xdb\x9b \x18\
-\xa1\xc4v=\x02`\xdb\xceT\xa9h\xdb\xfb\x1d\x07\tZ\xdc5\x1a\xcdK\x02k?\x92<$\
-\x00L\x0c\xd9\xb5d:i\xad\xa5\xa8\x13\x86a\x8c\x84\xa0\xe4\xd4\xb4M\xc2\xe3v\
-\xc7t\xddR\xb5\x1a\xb6\xd6\xa9\xe8\x10\x13\xd0=\x96\xc8J\x94\xa0\x14\xcc/V)\
-\xf0\xa4\x1dl>yD1\xa8\xce\xccU\x8e\x9c"\x94\xee\xf3"j\x16\x1d\n\xa9\xd1h\x0e\
-?(9k.\x03\x00\x02\x88\xa1\xe1\x8f\xddZKQ\'l\xb5"\x94\x80\x08\xd4\xb0lC\x84\
-\xed\x96\xe9\xb8\xe5\xe9i\x00X\x7f\xf4\xcf\x96\x89\xd2\x99O`\xb6\x13\x0b!\
-\xb8_\x9c\x06\x99\x88\xa8\xb3^\xbfo@\xb3\xe0\x19\xe5\xb9\xd3\xd40^\xa0\xb2\
-\x83\xb6\xdc5\x1a\xcd\xcb\x00o-\xa3\x88\x95\xb7}\xa0\xd9nX\xaeS:\x0e\x00a;l\
-\xb7BjP!\xa4i\x9a&\xe1\x9df\xdb\xf5\xfd\xd2\xf44\x00l\xae-\x9b\x18R\xef\x95\
-\x04*\xedNH\r\xcb\xf3\xa6PD\x98\x84O\x9e\xac\x984,8X\x9c\x997,\x7f\xdf\xbb\
-\x98G\x8b\xbbF\xa39\xe4\xa0d\xac\xf5\x00Fz\xdb\xbd\xe93\x84\xd0v\xb3\x1dE\
-\x8cR\x82HL\xd3\xb4M\xe8\xb4:n\xa1P\xaaV\x01@\xf0$X\xbb[,\x1d\x13\xc6L\xd8j\
-\x98N\xd12M\xc0\x84G\xed`\xf3\x81I\xc2\xa2\x07\xa6\xedy\xc3\xebh\xef\'Z\xdc5\
-\x1a\xcd!\x87\x05\xf7Qr\x00\x10C\xbc\xed\x96[r\xa6\xe6Z\x8dV\x14s\x02B\x12\
-\x93\x12\xb0\x0cl\x05\x81_,\x16+\x15u\xd8\xfa\xe3\xbb~\xf1\x88\xe1\xcct:M\
-\xd3\xab\x82\xe4\x06\xc1vc\xbd\xdd\\5HX\xf6)\x00\xf8\xd5\xd3\xfd[[_\x08\x07\
-\xe2"4\x1a\x8df\x8f\x90<d\xed\x87\x00\x08H\x06\'\x1b \xe0N\x9fk5Z\x9d01(\x02\
-5)J\x83@\xd8iOU*\x85b7Oo\x1c\xb5)1M\xaf\x9a\x84M\xc3.\xa1H\\\xc7j\xae?\x0e\
-\xdbk&\r\xcb\xbe\x81\x08\xa6[v\x8aG\xf6\xb5{\xc3\xd1\xe2\xae\xd1h\x0e3,\xb8\
-\x07R\x00\x19\xe6m\x97V\xe1x\x1ca\xcc\x84a\x10 &%\x92\x00&Qg\xaa2\xed\x15\
-\xba\xaesD\x8c\xda\x1d\xcb\xadD\x9d\x80\xd8\x15@\xee\xdafc\xedA\x1c\xae[F\\\
-\xf4\x0c\xc4\x17P\x8ec4Z\xdc5\x1a\xcd\xa1E\xb2\x16\xef\xd4ahE\x0e\x04bJZ\x91\
-\x92\x12\xc2\t5(H\x90"\x89\xda\xe5\xd99\xc7s\xbb\x07I\x99D\xb1\x14$J"\xcb\
-\xa9H\xc1\x1d\x8bl\xd4W\x92\xe8\x89ks\xdf\xa1*\xcb\x8035g\xb9\xe5\xfd\xec\
-\xddh\xb4\xb8k4\x9aCK\xd2\xb8\x07\x80@\x88\x18\x98\xda\x17%\xda5I\n(\x05\xa5\
-\x06\xa5(9\xe3I4}\xf4\x98iY\xea\x10)e\xd8\xea\x84\x9d&\xe3\xc4\xb4\\\x94\xc2\
-s\xcc\xc6\xdaJ\x14\xd6\xa7<tM\xda-\xa8GM\x7f\xfa\x00\x99\xed\xa0\xc5]\xa3\
-\xd1\x1cVD\xbc\xd9+\xa4\x87\x03\xcdv\xa4\x8e\xe9\x1d\x13\x80\x84\x10\xc3\x00\
-\x91\xc4\x9c%\xd3G\x8f\x1b\x86\xd1m\x81\xf3\xa8\x1dFaK\xa0i\x98\x065\x0c\x93\
-\xca\xcd\xb5\x87a\xa7^\xf6\xd12\xb6&\x0b\xaf\xb20\xb0\xb2\xc7\x0bDob\xd2h4\
-\x87\x13\x16\xdc\x03\x00\x02\xc0\x05\x0c\xca\x11\x86\x86w\x84\x83\rRX\x16\
-\x15I\xc8YR;~bK\xd9\x19\x8f\xda\x91\x94L\x10\x07\x91X\xb6E\x91\xb7\xd7\x1f\
-\x85\xed\xc7EWf\x95\x9dZ\x9e[Y\xd8\xafn\x8d\x8b\xb6\xdc5\x1a\xcd!D\x84k"n\
-\x00\x80\xc4\x81\xa9}\x11\x89+\x8d\x19\x90\xdcv,\x1e\xb58\x17\xb5\x13[\xf5\
-\x92$\x17Q\'\xe4\x8c\t0\xa4H<\xcf\xe3q;j\xae\xb5\xda\xf5\x92\x8f\xb6\xb1\xad\
-\xbd\xc2\xcc\xb9\\\x8a\xe0\x83\x80\x16w\x8dFs\xf8\xc0$\xb8\xaf^\r\xac\xc8\
-\x81\x88\xc4;\n\xd4pL\x83\x85\x81DR;q"\xfd+OX\x14F\x9cq\tF\xc2\x98\xefyq\xd4\
-\x8a\x9b\xab\xed\xce\x93\xb2\x0f\xd6ve\xb7\xbc\xaa]\x98\xd9\xf3\x0e\xed\x1e-\
-\xee\x1a\x8d\xe6\xb0\xc1\xdb\x8f%k\x11\x001\xc8lG\x94\xc4*\x12\xbbj\x1a4\xe9\
-lR\xcb\x99\xad\xd5\xd2\xbf\xb2(N\x92\x841.\x91r\xc1]\xc7\x8e;A\xd4\\M\xd8f\
-\xc5\x07s\xbb\xb2\x03!\xfe\xcc\xd9\xfd\xe8\xd2\xee\xd1\xe2\xae\xd1h\x0e\x17(\
-Ys\tz\xc9\x06\x06x\xdb\t!\xce\x11\x83\x1a,l\x18\xb6[\x99\xdd*\xcd\xc1\xe2\
-\xb8\xddj!1\x04\x12D\xe99\x16\x0b[\xed\xe0\x91\xe0\xc1\x94\x8b\x96Ap\xfb\xba\
-l6E\xf0AC\x8b\xbbF\xa39T\xb0\xd6\x03\x95\xdaW\x0c\xae\xc8!\xa9U1\xac2\x0f\
-\x03\xcb\xef&\x8dQ\x84\xadN\x12E\x02\r)%%\xd40\x08\x0b\x9b\x9b\xeb\x0fQ\xb6\
-\x8a\xde\x00e\'\x86\xe5UO\xee}\x87\x9e\x92\x03\xb7\x08\xa0\xd1h4O\rJ\xc6Z\
-\xdd\xd4\xbe\x8c\x0f8@"!N-\t\x9b\xceTiK\xd9\x11\x92(N\xa2\x88#\xe5\\\x98\x86\
-I@\xca\xa8\xf9\xa4\xbe\x1c%\xad)\x17\xfa\x95\x1d\x00\xfc\xea)j\xbc\x80*\x1cc\
-\xa2-w\x8dFsx`M\x95\xda\x97p\x81\x12\xf3f\xbbD\xe4\xe0\x13N\n\x95\x8a?\xd5s\
-\xa7 F\x9d(\x8a"\t\x06\xe7\xdc\xb6-\x94\\F\xcdf\xf0\xb0\x13\x87\xd3S\xc46\
-\xa1_\xd9\rg\xca)\x1d\xdb\x87\x1e=5Z\xdc5\x1a\xcd!\x01E\xc2\xdb\x0fa\xe0\xae\
-%\x02RB\x98\xa0\xe5NMM\xcf\xb8\x9e\xd7\xfd\x08B\xdc\xeeDq\xcc\x05\xe5\x82;\
-\xb6)\x05\xc3\xb8\xc9\xa2\x87\xed(\xf2l\xe2;\x03\x94\x1d\x00\n\xd3g\x0e`\xf8\
-c\x16-\xee\x1a\x8d\xe6\x90\xc0\x9a\xf7Q0 \xc0%l\xcb\x11F@H\x88\x12D\xe2NM\
-\xcfo)\xbb\xc4\xa8\xdd\x89\xe3X \x15R:\xb6\x89R\xc8\xb8)\xa2\xc7(#\xcb$S\xee\
-\x80\xfc\xc0\x00`\x17j\x96?\xbd/}zz\xb4\xb8k4\x9a\xc3\x80\xe4\x1d\xd6~\x04\
-\xa4/G\x18\x01! b\x88\x08\xb3\xf3\xe7\x0b\xa5nn/)e\xdcnG1\x13\x12\xa4\x94&\
-\x05\x14\x9c\x88\x90B\x13H\x87Ib\x19`\x1b\x83\xcf\xe5U\x0fD9\x8e\xd1\x1c\xe8\
-\x9f\x15\x1a\x8dF3&,\xb8\x07(\x00@\x08\xd8\xca\x11F\x80\x0b\x08\x19"\x82_\
-\x9c)\x94\xba+\xa8R\x88N#\x88b\xc1%A\xa0\x86\x01\x049\x05\xe6\x16\\\xc9\x1a\
-\x96I\x85\x04\xdb\x00c\x90@\x9an\xd9\xb4\x0fh\xf8c\x16-\xee\x1a\x8df\xe2\x91\
-I\x93w\xd6\x00\x08"p\xb9e\xb63\x0e\x11C@ \x94\x94k\xa7\xbb\x07\x0b\x19\xb5ZL\
-\x12\xc6%\x01B\x89$R\x10\x10\xa5\xe9\xe9\xa4\xb3\xea\x9a\x02\x11\x84\x04\xd7\
-\x1e\xe4mWI\xdb_h\xe5\xeb1\xd1\xe2\xae\xd1h&\x9e$\xb8\x07\x80\xa0r\x84\xf5\
-\xcc\xf6D@\xccQ\xe5\x0c\xf3\x8bs\xb6W\x04\x00\x14"l\xb5\xa3D&\x8c\x9b\x86A@\
-\x10\xc1\x00dy\xb6\xc6Y\x08q\xdd4H\x98\x80k\r\x16pg\xea\xc8\x81J\xda>\x02\
-\xeds\xd7h4\x93\x8d\x88\x9e\x88h\x1d\x00\x101M6\x90pHT-l\x02\x94\x9a\x95\xb9\
-3\x00 8\x0f\x9b-&\x80\t4\r\x03@\x12\xc9,\x8bLUg\x81\x90h\xfd7\xb6I\xd4_\x0b\
-\x8e\x9a,\xb6A\x0c\xdb\x9f>\xbd\xaf}{\x06\xb4\xb8k4\x9a\x89\x06\x93\xcd[\xea\
-\x95\xda\xb5\x84\x00Q\x02"\r\x97A\xa8\x1c9k\xda\xaeH\x92v\xab\x13s\x90B8\xb6\
-)\x04c\x9dV\xb5V\xb5\x0be\x00H6o\x99\x10\x02@\'F\xd7\x1a\x1c$3U\xfb\xad\x83\
-\x96\xb4}\x04Z\xdc5\x1a\xcdD\xc2\x19\x8f\x19B\xbcJX\x9b\x10\x83\x0b\x94\x12%\
-B\xc4@f\x02!-\xc7/V\x8fK\xa5\xec\x0c\xa5\x94\x96e\x08\xc1\x08\xebL\x1f\x99\
-\xb1\xbc"\x00H\xde\x11\xed\x87\x00 $\x18\x04\xe8 i\xb7\xbc\xaa]\x98\x1d\xf0\
-\x87\x83\x8a\x16w\x8dF3a b\xb0\xd9\xe0`ZT\xd2\xce\x12\x10\n\x00\xa2\xab\xec(\
-e\x9a*\x8c\xa0\x14\xb3\xc7\xcf\x8b8j\xb7\xa3\x98\xa1\x90\xc26\r\x94\x02X\xa7\
-R\x9b\xa5v\xb7\xfe5\x0f\xee!JBT\x0c\xe5\xa0=K\x07\xac\xf8\xf58\xe8\x05U\x8dF\
-3al\xae=\xd9\xac\xaf9&\xf5\xc8:\x91!\x01"\x042\x0ea\x92Uv@)\xa6\xcaG\x08\x90\
-\xa0\x19\xc6\x1c\x84\x94\x8ee"J"\xc2J\xad\x96*\xbb\x887\xd3"\xda\x88\x83\xd7\
-Q\xdd\xd2q\xd3)\xeeS\xf7\x9e\x13Z\xdc5\x1a\xcd\x84\xd1i\xb7\x81K\x8c[\xbc\
-\xfd\x08\x80"@\xccPmS\xca(;\x1a\xa6\xe5\x17\xe7\xda\x11p\x01(\xa5cQ)\xb9\x88\
-\x9a\x95\xd9Y\xc3\xf6z\x8d!k\xdc\x01\x00B@\xe2\xc0,\x92\x07=\xfb\xe30\xb4\
-\xb8k4\x9a\t\xc3/\x95\xc04\x93\xe6}\x14\x11\xa1$f\xd8Ir\xca.M\xd3\xa8\xce\
-\x9dK\x84\xc3\x18\x07\x00\xdb\xa2\x823L\xc2\xea\xdc\x1c\xb5\xdc\xb4)\xdeY\
-\x95I\x13\x00\x10\x81\xf1A\x0e\x19\x00\xbfz\xf2 g\x7f\x1c\x86\xf6\xb9k4\x9a\
-\t\xa3\\.\xf1\xce\xa6-\xdb\x00THhG\x98u\xa7\xa0\x94\x96\xe5\x94f\xcf&\xd2\
-\xe7\\PJL\x83\xf0$F\x11\xcf\x1c\xd9\xa6\xec(9\x0b\xee\x01 !\x84\t\x14r@\x90\
-\x8ca\x17\x9c\xd2\xf1}\xea\xd8sE\x8b\xbbF\xa3\x990\xa8a\x14\xbdX\x84\x00\x04\
-\xc2H\n\x89Ye\xb7\x1d\xbf<{\xb6\xc3,\x004\x0cJ\x08J\x96\xa0d3G\x8f\xe4\x02\
-\x19yk\x19y\x08*\x15\xf0\x10\xb3\xbd0s\xf6\x80g\x7f\x1c\x86\x16w\x8dF3a\xc8\
-\xa4)\xc2:\x01\xc29F\xc96ew\xbdbq\xfaL\xc4\x0c)\xa4e\x99\x94"\x8f"\xd3\xc0Rm\
-\x8e\x9a\xdb\\+(b\xd6ZQ\xaf\xb9\xd8\x9eE\xb2\x87U\x98=\xf8\xd9\x1f\x87\xa1\
-\xc5]\xa3\xd1L\x18I\xe3.\xa0\x04\x02\x9d\x18\xa5\x94\x00@\x08E\x94\xae_\xf6\
-\xcb\';\x89!\xa54\r\n x\x18\x19&\x94ks\x84\xe6\xb5.i\xdc\x01\xc9\x01\x00\x11\
-\x06\x9b\xed\x84\x16&g?j?Z\xdc5\x1a\xcd$!\xa2\r\x11\xad\x03\x81\x84c\xc2\xc1\
-v\xa7\x00\x80\xb3\xd0\xf7g\xbc\xd2+\x9d\x98 \n\xdb\xb6\x00%\x8f:\x8ek\x17\
-\xa7g\t\xcd\xfbUd\xd2\x14\xe1\xaaz\xcd\x04\x0c(\xa2\r\xe0\x95\xe7\r\xbb\xb0\
-\xe7\xfd\xd93\xb4\xb8k4\x9aI\x82\xb7\x1f\x01\x00 rt\xe7N~\xc5\xf1K\x92\x85I\
-\xd8N\x18\xed\xc4\x84\x80\xb4\x1d\x0b\xa5\xe0a\xc7\x9br\x0b\x95\x192(p=i\xdc\
-V)\x1f\xa5\x844\x1dM\x16b:^ea\xaf\xfb\xb2\xa7L\xe4B\x81F\xa3y9A\x1e\xf1h\x03\
-\x080A\x98\xa8\x18v\x89\x10\x8a`p\xe9\xc6\xdc\x00\x90\x06E\x94\x82\x85-\xbf\
-\xe8OU\x07+\xbb\x08\xd7d\xdcP\xc6:\x13\x03\x8b\xe8\x81_=E\x0ck\x8f{\xb3\xb7h\
-\xcb]\xa3\xd1L\x0cIp\x1f$C\xc0(1\x98$Rpi\xd8\x8c\xa1@b\x9a\x00\x12\x11\x08o7\
-\xa7\xa6+~qhb^\xd6\\\x02\xe8\xd6\xf1\x18\x18\xfeh\xba%\xb7xt\x0f\xbb\xb1/hq\
-\xd7h4\x93\x81d-\xdey\x0c\x04\x92\x04b.\t\x91,a\x89 I$\x01\x89I a\x8c \xab\
-\x1e\x99\xb1\xdc\xa1\x95\x92d\xd2\x94\xac\x05#\xd6Q\x01\xfc\xe9\xc9(\xc71\
-\x1a-\xee\x1a\x8df2H\x82\xfb\x80\x12\x01:\t\xa2\x04\xbb\xe8%\x9c#\x17\x02%\
-\x01\x90\xd4\xa0\x14*\xd3\xb3\x86\xe3\x8fj\xa4qG\x95\xf3\xe0\x1c\x07\x86?:\
-\xc5#\x96W\xd9\xbb^\xec\x1bZ\xdc5\x1a\xcd\x04 \xe2@t\xd6\x80@\x9c \x17(\xc1n\
-\xb7\xc28f\x00@\t\x01\x00B\r@Q\x99\x9d\x19\xd5H\xb8&\x93\xcd\x01E\xb4{\x10jz\
-\xd5\t\x0e\x7f\xcc\xa2\xc5]\xa3\xd1L\x00,\xb8\x87\x80\x04\x81\x0bp-`\xe8D\
-\x9cp\xc6\x00\x08\x00\x02\x02\x02 "\x8f\xda\x865$\x0f\x0c\x8a$\xb8\x03\x00\
-\x04 \xe1\x88\x83\xccv\xb7\xb2`d\xf2\x13L4Z\xdc5\x1a\xcdA\x87\x87Ox\xb4NT1\r\
-\n\x14\x88A\x84\xb4\x1c\xc1\x85Rv\x00D$\xa6IL\xc7\x1b\xdaH\xfb\xa1J6 \x86\
-\x84?R\xcb\xf3\xca\'\xf6\xb0\x1b\xfb\x8b\x16w\x8dFs\xc0A\x16\xdc\x07\x00\x04\
-\x10\x12\x01\x00\x81P\xec\x14\xdd\x16\xf7\xa7\xe3XJ)\x00\xc0\xa0dv\xa6l\xd8\
-\x83\xedn\x14I7H\x06\x80q\x1c\xb8k\xc9\x9f>\xdd\xbf\x91ur9<=\xd1h4\x87\x12\
-\xdeY\x15I\xa0\xcc\xf64r\x11\x81@\xb4\xe6\xb8\xb1\xed\x14,\xff\x88a\x9a\x96\
-\xed\x1a\xe6\xd0\xc4\xbc\xac\xb9\x84\x92\x01\x00"\xc8AI\xdbM\xaf\xe2L\xcd\
-\xedY\'^\x00Z\xdc5\x1a\xcd\xc1\x05Q$=\xb3=\xbb\x04J\x00\x00\x08\x0b\x9b\x86\
-\xc5\nG\xbf2\xc8\x10O\x9b\x10\xc8\x1a\xbc\xf5@\x1d2\xd8l\'\xa40s\xf6\xf9_\
-\xfd\x0bE\x8b\xbbF\xa39\xb8\xf0\xf6c\xc9:\x04\x80\xcb\x01\x89\x1bI\xb7\xb4\
-\xe9 e\x97\x11\xf0\x16\xf2f\x126d\xd2V\x95Q\xa5\xc4\x81\xdev\xa7xl\xe2\xaa\
-\xe8\xed\x88\x16w\x8dFs@A\xc9\x93`\t\x94\xb7}P\xe4\xa2\xe1\x96\x9d\xa9#\x99\
-\x0f\x08\x90!\xf0\xa6H\x828lFa\x12\xc7\x9c\x0bY\x9d"\x94\x12\xc0\xc1f;1,\x7f\
-\xfa\xd4^\xf6\xe3\xc5\xa0\xc5]\xa3\xd1\x1cPXk\x05ED\x86\xe5[\'\xa4X{\x15\x00\
-@\xc6 \xdb\x984X\xdc\x8a:\xed8\xe6I"\xb8\x90\x88\x80\x00\x96A(%\x00\xc0\xe5\
-\xe0ZK^\xe5\x95I\xac\xa2\xb7#Z\xdc5\x1a\xcdA\x04\x05c\xcd\x07\xa0\xf2\x04\
-\xc8\x01f\xbb\xedM\x89$\x88\x9bK \xdaQ\x98\xc4\t\xe7L\xaap\x1a @\x00(\x05&\
-\xc04\t\x81\xee\xae\xa5AU\xf4|\xf7\x10\x85?f\xd1\xe2\xae\xd1h\x0e"I\xe3\x0e\
-\xca\x04\x00\x98\xec\xdbpD\x80\x0bx\xfc\xa8\x15/\xfd\xa6\xe2\x83AQ\xca\xae\
-\xa0g+\xe2I\x04!\xc11\x11\x10"\x06q\x02\x84\x82A\x00\x00\x10\x81R \x84\x16f_\
-\x9d\xd0*z;\xa2\xc5]\xa3\xd1\x1c8D\xdc`\xedG\x00 %H\xd1\xe7%\x07Xo\xe1Z\x83\
-\xcf\x96\xa8A\t\xe26MOw5%\x82X\x16\xf5|\xdb4\r\xb4\x90\x18\xdc\xa0@\xa4P\xda\
-\x1e3\x191R\xb1&\xb8\x1c\xc7h\xb4\xb8k4\x9a\x83\x06&\x8d\xbb\xdd\xf4^"\xbf\
-\x04J\x08D\x0c6[\xd2wh\xc1%\x08\x90\x86\xbe#\x02\x00\x12J-\xd3p\x1c\xd3\xb6\
-\r\xc7\xa2\x04E\x1c1\xce\x05H\xc9\x19"\x10B\xc00\xa4c\x92\xa0\xc3\x19\x8bGD\
-\xc7O4Z\xdc5\x1a\xcd\x81A4A6y\xb8!\xa2M  \x06\x85?\x02\xc0zS\n\t\xd3\x1e\xa1\
-\x04$ve\xdd0\xa9\xe3\x98\x8em\xda\xb6a\x10\x94R\xf0$\xe9DBH\x04D \x04\x89\
-\x8d\x04(2@\xc98\x98\x14\x0b.\x11,\x06\xef\xb0\x05A*\xb4\xb8k4\x9a\x03\x80dl\
-\xe3\xd7\xedN\x10\'hSn\x99\x04p@\xf8#!\xd0\x89\xb1\xd1A\xdf%\xae\rB\x80D\xb0\
-mZ\xa9\xf8\xb6I@J\xcey\x12&\x82\x0b)\xd5\x07\x80\x00Jb\x81?o8\x15!\x90b\x88\
-\xed;D&\x02\x89A\x80\xb3x\xff\xfb\xba?hq\xd7h4/\x1cl\xd5\xafonlp\x8e\x08\xe0\
-L\xd1\xc1\xbb\x96\x08\x00\xc0z\x0b\x01`\xca!\x84\x10\xcb&\x96A,\x93"\x8b\xdb\
-\x1de\xa4\xf7v\xafn}\x92\xb4\xa2\x82L\x98a\xac\x11B$\x12\x0b=\xcfL\xd4\x91<\
-\t\xf7\xb3\x9f\xfb\x89\x16w\x8dF\xf3\x82I\xc2\xc6\xe6\xc6\x86\xb2\xd3)%\xa6\
-\x01\x88 T\x06\x98^p\x8b\x94 $\nI\n.\x9d-\x13\xc7\xa2\x94\x02A\x94\x88\x82s\
-\xce\xba\xd12\xfdN\x1c\x04\xf0\xa7\xdcvL9\xe7\x00\x08\xc4\xf0lC\xfd\x89\x02p\
-\x16\xedcG\xf7\x15-\xee\x1a\x8d\xe6\x05\xc3\x92D\xc52r\x01\xbe\x05\x04\x80KD\
-\x04\x89 \x04\x08\t\x08@\x081\rb\xdbd\x8a\x02"J)$\x07\xe5\xb5!0\xba(\x1e\xda\
-$\xb0\xcas1/H\x89\x16IL\xdeB\x01\x00@9\x1e!C\x00\x00\x0c\x95IDAT\x08H\x1e#\
-\xe2\xc0:\xda\x93\x8e\x16w\x8dF\xf3\x82\xb1\xdd"\x10\x82\x12\xb9@\xc7$\xc40\
-\x12\x86Q\xc2\t!\x06%\xb6E\x0c\n\x84 J@)\xb9\x04\xe8\x19\xe8\xe3H2\x01"\x92\
-\x0e\x15K\x9e\xe5\x02\x10\xe4\x91\x14<\xfd(\x8a\x04Q\x12b\xecU\xdf^\x1c\x873\
-z\xff%!\x08\x82\xa5\xe5\x95 \x08\xc6? \x8e\xe3\xfe\x8f,-\xaf\xac\xd6\xeb{x\
-\xa1\xbbg\xb5^\xdf\xedU-^\xbf1b(F\x7f\xe4\xea\x17\xd7vw}\xcf\xe3\xec\x07\x87\
- \x08\x16\xaf\xdfx\x81\x17`9\x9e\xed\xcf\x10Bff\n\xe5\xd9\xb2](x\xae\xe9\xd9\
-\xd4w\x88c\x01%\x12\xa5\x14\x02\x11\xbbq\x8f\xbb5\xb3\t!RJ\x1e\xb5y\xdc\x12B\
-lk\x00\x85\x8a\xa0<|h\xcb}"\x89\xe3\xf8\xc3\x8f>v\x1cgn\xaev\xf3\xe6\xad \
-\x08\xbe\xfd\xad?v\x1c\'{\xcc\xe2\xf5\x1b?\xfb\xe4\xd3\x85\xf9\x13\xab\xf5\
-\xb57/_\xba\xfc\xc6\xa5 \x08~\xfc\xee{s\xb5\xd9\xd5\xfa\xda7\xbe\xfe\xb5sg\
-\xcf\x00\xc0_\xbe\xfb\x1e\x00Dq|\xee\xec\x99\xaf\xbe\xfd\x16\x00\xfc\xd9\x9f\
-\xffE\xda\xc8\xf7\xde\xf9\xee\xc2|~sv\x10\x04?\xf8\xe1\x8f\x06\xfe\xa9\x9f\
-\x9b\xb7n_\xbdz\xed\xdf\xfe\xe9\xf7\xc7\xec\xdaj\xbd\xfe\xe1G?u\x1d\xc7q\xec\
-8N\xa28\xfe\x93w\xbeS*\x95\xb2\xc7\xfc\xe5\xbb\xef]\xbe|I]\xff\xd6\x89n\xde*\
-\x95J\xb9#G_v\xfa\x91\xcf\xae|~\xf9\x8dK\xd9\x83\xd3Ap\x1cG\x8d\xde\xe8\x8e\
-\xecx\xf6a\xf4\x9fh\xb7-<;\x8d\xa0y\xf3\xe6\xad\x8b\x17\xce\xef\xff\xa9S\xaa\
-\xd33\t}bZ\xb2\x13\xb49\xe3\x88H\x00P\x02\xf6<.\xcf\xe87!0\xd8wc\x98\xc6h\
-\x9f\xce\xe4\xa2\xc5}\xf2P\xca~\xf9\xf2\x1b\xf5z}iiyaa\xfe\xe2\xc5\xf3\x1f~\
-\xf4qN\xdf\x97\x96\x96\xbf\xfd\xad?>w\xf6\x8c\xd2\xf4\xcbo\\\xfar\xf1\x86\
-\x92\x8f\xa5\xe5\x95+W>?w\xf6\xcc\xcd[\xb7\x1d\xc7\xfe\xde;\xdf\x8d\xe3\xf8\
-\x07?\xfc\xd1\x9b\x97/\xa9\x16\xfe\xeb\x7f\xf9\xcf#.\xe0\x97W\xaf\x95J\xa5\
-\xc5\xc5\xeb#\xc4\xfd\xfd\x0f\xfe\xea{\xef|\x17\x00\xc6\x99\x00R\x82 x\xff\
-\x83\x9f\xa4\x13\x0f\x00,-\xaf\xf4+\xe6\xe5\xcb\x97v\xd5\xec\xf8\x97\x9dE\rB\
-\x10\x04\xff\xe3\x83\x9f\x9c;{\xe6)\xce\x98\x0e\xc2\xaeN\xf4\x143\xc4\xa4\
-\x83<L\x1aw\x12\x86q\x1c\x01\x00!\x80\xd4\x91H(&\x04\xe43\x0b\xfb\xf0\xf3\
-\x02\x98\x86A\x0fi\xfa\x81\xc3\xd9\xab\xc3\xcd\xe2\xf5\x1b\x0b\x0b\xf3KK\xcb\
-\x8dFp\xf9\xf2\x1b\x8dF\xb0\xbc\xbcr\xee\xdc\xd9\xdc/k\xa5\xec\xd9wn\xde\xba\
-\xad\xdeY\x98?\x11\xc5q\x10\x04KK\xcb\xe7\xce\x9d\x05\x00\xc7q\x94\xd6\xab#W\
-\xeb\xf5aN\x06\xe5\xd8\xf9\x93w\xbes\xf3\xd6\xed\xec1K\xcb+W\xbf\xb8\xb6x\
-\xfd\x86:@\xfdO\x1dP*\x15\xd5\x9b\xd9\x83\xe38V\x97t\xf5\x8bk\xe9\x9f\xd4\
-\x15f/;\x95TuIW\xbf\xb8\x16\x04A\xa9\xb4\xb5\xebd\xf1\xfa\x8dl\x0b\xc3\x18v\
-\xd9;R*\x95\\\xc7i\x04M\xd5\x91\\g\x83 H[\x8b\xe3\xf8\xea\x17\xd7\xae~q-\x8e\
-\xe3\xfeAP=MGx\xf4\x89\xe28V\xfdJ\x1b_\xad\xd7\xd3\xe1M\xc7*\xdb\xf1t|\x94G+\
-m6\x1d\xea\xb4\x85\xf4O\xeaxu\xc1\xe3\x0f\xc8^\xd0\xdax\x10E\t \x10B\x10\x88\
-\xb0\x8f\x91\xd2W\xd4\xff\xc0,\xe1\xde\xb9M\x10\x00\xb9T\xab\xab\x87\x0e-\
-\xee\x93\xc7\xd2\xd2\xb2\xf2\x12|\xe3\xeb_[\x98?\xf1\xd5\xb7\xdf\x9a\x9f?q\
-\xf1\xc2\xf9\xa5\xa5\xe5\x81\xc7\xff\xec\x93O\xdf\xbc|\t\x00\x82 H\xad\xc2r\
-\xa9\xd8\x08\x9a\xdb\xde)\x97\xd4C\xee8\xce\x95+\x9f\xff\x8f\x0f~\xf2\xfe\
-\x07\x7f\xd5\xdf\xda\xe2\xf5\x1b\x0b\xf3\'J\xa5\xd2\xc5\x0b\xe7\xbf\\\xec*\
-\xc5\xcd[\xb7\xaf\\\xf9\\]\xdb\xe2\xf5\x1bW\xaf~\x01\x00W\xaf~\xb1\xb4\xbc\
-\xb2Z_\xbbr\xe5s\xc7q~\xf6\xc9\xa7Jt\x82 \xf8\xd9\'\x9f:\x8e\xf3\xd9\x95\xcf\
-o\xde\xbc\xa5\x8eT^o\xf5CD\xb5\x99J\x8f\xfa\x94\xba\xa4(\x8a\xd5\xeb\xd5\xfa\
-\x1a\x00d[P\xef\x0cc\xe0e\x8fF\x9d\xfd\xc3\x8f>\x06\x00\xe5\xddR}T\xfe.\x00X\
-]\xad\xff\xf8\xdd\xf7R\xbd\xfe\xec\xca\xe7\xea\xcd\x0f?\xfa\xb8\x11\x04\xd9A\
-XZ^\xb9z\xf5\x1a\x00,.^\xef\x9fZr\'\x02\x80\xf7?\xf8I\xa3\x11\x00\xc0\x87\
-\x1f\xfdT\xa9\xf3\x87\x1f\xfdT\x8d\x8f\x1a\x8d\xfe\xa1K\xc7\xc7\x1d4\xd47o\
-\xdd\xfe\xe4\x93\x9f\xa7\x97\x07\xbd\x1f\nQ\x14GQ\xac\xae\xfc\x05\x92$I\xcf\
-\xf9\x82\x89\xf0\x1b\x01\t\xeak\x9d\xf5\'\xcd\x8dV\xd0\xf1\x01\x9e\xd3j\'J\
-\xec"\xb7\xde\x13L\xb0\xe4\xf9\xb4\x7f\xc0\xd0n\x99\t&\x8e\xe3\x9c\x9f\xbd\
-\x1f%C\xbb\xf2\xe4\xfe\xc7\xff\xf0\xef\x1d\xc7\x89\xe3\xf8\xfd\x0f~\x92\x1a\
-\xfb)_.\xde\xf8\xf6\xb7\xbe\t\x00\x17/\x9e\x7f\xff\x83\x9f(O\xce\xd5\xab\xd7\
-\xbe\xfd\xado\xa6\xf3\xc4\xe57.\xfd\xd9\x9f\xff\x85\xf2H\xa4V\xe4\xeb\x17\
-\xcf\xdf\xbcyka\xfe\xc4\x97\x8b7^\xbfx>\x8ec5?\x01@\xd6\xeb\x9d\xeb\x91\x9a\
-\xb1\x94\xe4\xbdy\xf9R\xd6/\xacZ\xf8\x8f\xff\xe1\xdf\xab3\xfe\xe0\x87?\x1a\
-\xd1\xa9\x81\x97\xbd\xe3P\xdc\xbcy\xbbT*~\xef\x9d\xefd\xdf\xfc\xe5\xd5k\xfd\
-\xcb\x00\x00\xf0\xd5\xb7\xdfRc\xf5g\x7f\xfe\x17\xdf\xab\xd5\xbe\xf7\xcew\xd3\
-A\xb8\xfa\xc55\xc7\xb1\xe7\xe7O\x0c\xfb"\xb2\'\xbay\xebv\xa9TL\'\xb9\x9b7o9\
-\x8e\xadFI}|\xd8\xd0\xa5\xe3\x93\x1bj\x00\xb8z\xf5\xda\xe5\xcb\x97\x1c\xc7\
-\xa9\xd5j?\xfb\xe4\xd3 \x08\x94\x8fN\x1d_.\x97\xd4T\xf1\xa2\xb0\xdcR\x14>6\
-\x08\x00\x82a\x19\x96i\x0b\xc6\x04\x17@\x88k\x1b\x94R\x94\xe2Y\x9d3\x84\x80{\
-\x94\xdaU!\xa4!60\xea\x9a\x02\x04P\x8a\x04\xc0\x7f\x0e\xdd8`hq\x9f<\x16\x16\
-\xe6\x17\xaf\xdfx\xfd\xe2\xf9\xcf\xae|~\xee\xdc\xd9\x9b7o\x95\xcb\xa5z}-\x95\
-\x83\x94\x9f}\xf2i\xbd\xbe\x96jS\xa9TJM\xf5F\xd0,\x97\x8a\xea\x1d\x80\x13\
-\x00\xd0h\x04ss5\xe8\xc9\xab\xe38\xe7\xce\x9d\xc9\x99\x99\xca\x11\xa1\xd6`\
-\x157o\xdd\xbex\xe1\xfcj\xbd\xbe\xa3\xa7\xf8\xe2\x85\xf3\xff\xed\xbf\xff\xbf\
-_}\xfb\xad\xc5\xeb7\xfe\xdd\x9f~\x7f\xb5\xbe\x16\xc7\xb12o\x01\xa0\\*\xaa\
-\xae)U\x82\x8c\x96\xa5\xe4N\xb1Z_+g\xfc3\xd9\xd79\x86]\xf6\xe8\x0b\xbe\xfc\
-\xc6\xa5sg\xcf\xfc\xf8\xdd\xf7r\x93h\xee\xe7N\xfa\xfe\x88\xd9B\x9dKY\xdf\xfd\
-\x13C\xeeDA\x10\xac\xd6\xd7\xe2\xde\xc8\xd4j\xb5\xb9Z\xed\xcd\xcb\x97\xae^\
-\xbd\xf6\xb3O>\xfd\xc6\xd7\xbf\xa6\xa6\xde\xdc\xd0Af|rC\r\x00\xab\xf5\xfa\
-\xe2\xe2\xf5\xf4\xf8(\x8e\xeb\xf5zz\xc3\xbcp/\xbf_\x9am\xaf\xdf6\xa8\x00$&\
-\xb6\xca\x05\xcf\xf6J\\\x00rN\x92u\x1e\xb3gv\xbbc;\x9e\x92\xd27\x19\xa7\x94\
-\xc6\xa2l\x88\xb6mt\xd4\xc6\'\x9e\x84\x8e_y>=9Hhq\x9f<.^8\xff\xe1G\x1f\xbf\
-\xfd\xf6[\x00\xb0\xb8x}n\xae6?\x7f\xe2\x93O~\x9e30\x95\xcd\x9e\x8d\xeeP^u\
-\xb5\xa0\xea:N\xa9TZX\x98_\\\xbc~\xf1B\xd7\x8e\xfe\xea\xdbo--\xaf\xd4\xebu\
-\xa5\xaaKK\xcb\x17/^\xc8\xb6\xb9\xb8x\xe3\x1b_\xffZ*\x8b\x8b\xd7o\xfc\xf2\
-\xea\xb5\x8b\x17\xce\x9f;{fiyea\xfeD\xea\xd8\xe9\xbfl\xc7q\xd4\x95+\xf7H\xa9\
-Tr\x1cG-\x02\xc7q\xdc\x08\x02u\x85?~\xf7\xbdr9/\xeb\x03Y\x98?\xf1\xb3O>U\x82\
-\x18\xc7q\xea\x96\xb9y\xeb\xf6\xc2\xfc\x89\xec5\x0c\xbb\xec\x1dOQ*\x95\xde\
-\xbc|\xe9\xb3+\x9f\x7f\xfb[\x7f\x9c\x1d\xc6\xab_\\S\xd1G\xa9i\xbc#\x17/\x9c\
-\xbf\xfc\xc6\xa5\xcf\xae|\xae\xbe\x82\x11\'\x9a\x9f?\xb1\xb4\xb4\xacL\xfe\
-\xd5z\xbd\\*\xa9\xce^\xbcp\xfe\xe6\xad\xdb\x8b\x8b\xd7\xbf\xf7\xcew\xfb\x87.\
-Kn\xa8\xd55_\xbcxA\xcd\x9aK\xcb+s\xb5\xda\xc2\xc2|\xbd^W\xef\xd4{!\xa7*\xf6t\
-\xaeV\x1b\xa7G\xcf\x11\xd3r\xbc\xca\xc9\xa4q\xdbP\x9bN\xdb\x8fY\xb2I\rCr&\
-\xf8\xb3+;\x00\x10\xc7+4;1\x8b%\x01D4\xa6\x1c\x97\x90\x0e\xaa\x94b\xf4\x10\
-\x06\xb9\x83\x16\xf7ID=\xd8\x1f~\xf4q\xa9T\x9a\x9b\xab5\x1a\xc1\xd2\xd2\xf2\
-\xb7\xbf\xf5\xcd\xfeP\xc8\xf4\xbf\x00\xf0_\xff\xcb\x7f~\xfd\xe2\xf9\x1f\xbf\
-\xfb\xde\xd2\xd2\xb2\n\x85\x04\xa5SW\xaf\xfd\xe5\xbb\xefEq|\xf1\xc2y\xc7q\
-\xca\xa5\xe2\x87\x1f}\xbc\xb4\xb4\x1c\xc7I\xad6\x9b\xf5\xc9,-\xaf4\x82 \xfb\
-\xce\xb9\xb3g>\xbb\xf2\xf9\xd2\xf2\xca\xe5\xcb\x97\xde\xff\xe0\'s\xb5\xd9F\
-\xd0|\xfd\xe2\xf9\xcbo\\\x9a\xab\xd5\xfe\xf2\xdd\xf7.^<\x9f\xb5\n\xcf\x9d;\
-\xfb\xfe\x07\x7f\x95\n\xe5\x9b\x97/\xfd\xe0\x87?R\x9fz\xf3\xf2\xa5\xb9Z\xadT\
-*}\xef\x9d\xef|\xf8\xd1Oo\xde\xbc\xad|\x11K\xcb+\xeaR\x07\x92m!\xed\xbe\xf2n\
-g\xe3m\x86]\xf68\xa3\xad|\xf4\xd9\x83\xbf\xf1\xf5\xaf}\xf8\xd1\xc7\x9f]\xf9\
-\xbcT*\xcd\xd5fG|6\x1d\x848\x8e\xbf\\\xbc\xa1\xd69\xfed\xfb\x1c\xdc\x7f"\xa5\
-\xc8?\xf8\xe1\x8f\xd4\xf1\xdf\xfe\xd67\xe38\xf9\xf0\xa3\x8fU7U\xb8j\xff\xd0\
-\xe5Z\xcb\ru\xfa\x05\xa9\xafUM\x15\xef\x7f\xf0\x93\x9b7\xbb\x0b\x06j\xb4\xd5\
-?\xf7_\xdc\x01\xa0<\xbb\xb0\xcec\xd6zhRI\x80\x08\x16\x0b\x06\x00*t\x06\x80\
-\x00<\xc3\xaa*"\xdaf\xa7\\\x9d\x8b\x12@D\x8b0\x07C\xb5\x8c*\xc1:\x94f;\x00\
-\x90=\\\x89\xd6\xec1A\x10\xa4\xde\x951?\xa2,\xdc\xdcG\x96\x96W\x1c\xc7N\x1fi\
-uL\xf6\x9d\xa7hy\xe0\x89\xc6\xbc\x1e\x00X\xad\xd7\xe38\x19\xe7\x1a\xd4 \xcc\
-\xd5f\xc7\xf1\xa1?w>\xbb\xf2y\xb9\\\x1a\xf6# \xdb;\xd5\xa3\xf1\xaf3\xd7\xaf\
-\xfe\xefz\xcc\x11\xce\x92\xfb\xa2\x07\xbe\xf3bI\xa2v\xd4\xd9\x14I\x07\x90\
-\x0b\x16#\n\x90\x1c@J!\x08H\x00\x15\xf7\x8e\x84\x0c4\xe6I\xf7?\x83$\r\x11\r\
-\xdb\xa5\xd6\x94\x94\x88\xac\x89"\x01 B\x82?\xfb\xdaT\xf5\xd8\xde\xf6\xea\
-\x05\xa1\xc5]\xa3\xd9\x1d\x1f~\xf4\xf1\xdc\\\xadV\xab\x05A\xf0\xcb\xab\xd7\
-\xfe\xdd\x9f~\xff\x85\xcc+/\x0b\x88\x88R\x08\xc6;u\xd1y\xd0\x8cK,a\\JJ\x90\
-\x82\x04\x82\x04$\x01iP\xa4\xc0)\x05\x89\x06!j\xdf\xa9\xe85\xd0\xdd\xa5D\x89\
-\xf2\xc2\x00\x00\x91H\x90:\xfe\xf4\xe9\xa9\xca\xd1\x17\xd8\xb9=E\x8b\xbbF\
-\xb3;V\xeb\xf5\xc5\xc5\x1bjY\xf5\xcd\xcb\x97^\xf8j\xe4KB\x12\xdc\x83x\x15\
-\xcc\xb9\xb5\r\x16q0(\x11\xbd\x80F\x02x\xa4j\xc4\xf1F\xc1\xe2\xb2p\x86\x85\r\
-\x16\xae\xf3\xa4\x8d\x08\x94\x80\xef\x10\x04\x90f\x95\x83KP\x10\x02\x12\xa8\
-\xed\x95\xfd\xe2\x0c5\xac\x17\xda\xa7\xbdE\x8b\xbbF\xa3\x99\x00\x82\xd5\xdf\
-\xd8\xfc\x91\xe3\xd5\x82\xd0F\x80v\x84\x11CB\x08"\x9a\x94\x1c\x9f6\x9el\xd6}\
-\xb3#\x91\xf2^\x89\x0f\x04\x98r\xc0\xf3\x08 \x80Y\x86\xca\xef\xbe\xd8.\xec3z\
-\x13\x93F\xa3\x99\x00\x92$\x8e\x99\x94l\xdd\xb7y\xb9`Z\x96i\x1a\xc4\xa0\xe0\
-\xd9\xb4\\0\x92\x84I\x115C\xcc*\xbbA\xc0\xb5\tH\x00\x04`\r\xe8\xdcy\xb1]\xd8\
-gt\xb4\x8cF\xa3\x99\x00\x04g\xad\x0e\x98\x94!\xac\xb5\xa2J\xb9P\xf2,\x94(M\
-\x83\x08\x166\xdb\x1bR\nD\xb2UM\x1b\xc1u\x80\xd0\xcc\xfaj{\x19\xac\x19\xb0^\
-\x167\x9av\xcbh4\x9a\x03\nJ\x86\xac\x83\xbcIy\xe3\xd1\xea\x93(\x91&\x05\xdf\
-\x81\x84#\x80I\xa8\t\x84\xa0\xe4Rp\x00D$\x12\xc1w\x08\xf4\xcc\xf6\xeaT_\x11\
-\x0e\xc3\x87\xea\x1bp\x18\xb3\xb7\xf7\xa3-w\x8dFs\x80@\xc9\x90wd\xdc\x90I\
-\xd0\x8dY$\x80\x08B\xa0\x12\xea0\x01\x93\x12\x00\x0e\x92\x03\x00\x010\r\x02@\
-\x12\xbe\x95\xbb\x17\x11\xbc\x9c\xd9\xae\x10\x1dh\xdf\x85\xa9\xb3\xfb\xda\
-\xa5\x17\x84\x16w\x8dF\xf3BA\x81\x92#\xef\xc8\xa4!\x93&\xb2\x16J\xd6U\xe5\
-\x9e4K\xb9\x15\xd1\xa8\n\xa5\x1a\x84\xa4\xd1\xee\xb2{\x00RJ\x08\x01)\xc1\xa4\
-\xe0\xdad\xf0\xbe\xa7\xf0\x01x\xc7\xc08\x84\xc9drhq\xd7h4\xfb\x8eL\x80\xb7\
-\x91\x05<jH\x1e\x82d\xd9L\x8d9\x08\x80D\x90\x98\xf9w\x06\x04\x00\x04\x95H\
-\x80\xf6v0\x15\xdcAf{\xfa\x89\xe6o\xa0\xfc;pH\xd3\xb8\xa7\xfc\x1f\x1b\x081\
-\x83\xa9\x05{\xd0\x00\x00\x00\x00IEND\xaeB`\x82' 
+\xcf\x1fD\x15x\xc3E\x07\x1b@\t|\x0f\xb5\xfd\x86{-i\x11\xff\x7f{\xef\xfe\x1bI\
+v\xddy\x9e{\xe3\x1d\xc9|\x91L\xd6\x8b\xecz\xb6ZUl\xdb\xd5l\xdb\xed\xee\x19h%\
+\x8f\xbd\xd2BB\xb7F\x18\xcf\xfc:\x8b\xc5b\x80\xc1\xfc9\x8b\x05\x16\x83\x01\
+\x06\x86\x01\xc3j@\x18\t#l\xf7\xb6v\xd4\xdb\xb2\xbb\xdc\xb6T]\xb2\x9bU\xb2\\\
+o\x92\xf5`\xb2X\xcc\xc8G<\xee\xe3\xec\x0f73\x18\x8c|0\xeb\xc1G\xb2\xee\x07B+\
++\x19q#\xee\xcd\x88o\x9c8\xf7\xdcs\xa2\xa7"z\x02\xb0\xcb\xa3\xc2p\x8an\xf1\
+\xc4\xe4\x89{z%\xa5\x8eou\x83\xa9\xcbQ\xdd\xdb\x0f\xa7\x7f\xf0\xa0\xfa}\x97\
+\xad\xfb\xc9\x8a\xc7\x1e\x98\xb2C\x00\x91\x9a\xcc\xa8D\xee\xe9\xc4]\x90\xce\
+\x8ca\x18f/w6\xe7\xbcY\xfd\x17[\xa5?J\x9b2M\xd3q\x1c\x95\x17P-F\xef?\ru\x02\
+\xb0\xd3/a\x18\x86\x8a\xa0\x90\xd2\x10\xb4\xbcQ\xf9W\xf5\xf2\x1fS\xd1q\xc4\
+\xa6\x97<p\xf9\xba)\xdb\x00\x12\x10$u\x13k6t\x16\xb8s\x1c\x9c\xaaJCH\xe9\xe0\
+(\xef~_Pz\xf3\x08J\xb7\x8a\x7f\xf8t\xea\x0f\x0c\xd1r\xf9\xba\x97<px\xdd\xc0\
+\xde\x12\x12j\xa3U\x92\xfe)Zy\xdd\x9b>\xe3\x95f\x1d\xc7!\x19(\xa5a\xe9w\xef8\
+o\xa8\x07\x86j\xd3\xcc\xa4E\x9c\x9a\x9ar]W\xf5r\xcc\x1fE\x85\x91\xa8\xfb|\
+\xf5\xd8\xff\x9a\x8b\xcfQ\x07U\xbf\x9d\xe7y\x85B\xc1w\xfd\x11S\xa9\xfd\xc7\
+\xea_\xe2\xa4\x0e\x1d\xc7q7\x84F\x08)\x8d\xc0\xb8\xdc\xf0\x7f\x8f"3\xf9\x96\
+\xc7\x1ez\xec\xa1%\xb6(\x08@\x90\xd4N\xcc\x99\xc8\x99O\x9c\x93\xd2\x9eN\'\
+\x03\xd3y\x143\x93M~\xe0i\xa4\x0f\xf8\xdc9\xa8\x0b \xb5^\x8b\xc5\xa9bq\n\xba\
+\xab.Q\x15*RF\x80eY\xe9\xdb\x98\xea\xbb\x1a\x7f\xc68b\xea\x0f\xe9\xbat\x08\
+\x1d5\xc9l\x18\xc6\xdc\\mz\xbaJ\x08\xb1m\xdb\xf3<\x95:I\xe5\xb3\x1cgT\xfb\
+\xc7\x93RZ.\x95\xbe\xfe5\x0f\x11-\xcbR\xbf\x94\xba\x17\x86=\xf3\xb2\x0f\xddl\
+;\xbd_D\xaa\xd1\x90R2\xc6\xa3(\x8a\x93D=\xc9\x08!\x96e\xf9\xbe\xe7{\x9e\xaa\
+\xf8j\xf4\xca+\xaa\x14R\'\x8e\x1f;~l\xce0\x0c\xc7qT\x92(\xdf\xf7\x9f)\xaf\
+\xf2\x81\xb0#\xd9\x80\xd8\xf1\xa2\x9ee\xecZK\xc82YL\xc4\xf0\xf4\x16\x85\x99s\
+\xb0\'\xa9\xc6\xf7\x85\xec{q\xf6J\xea\t\xabDDf\x9cl\xb8\'\x1b\x00\x90\xb9\r\
+\xd4\x8en/\x1b\x01\x00\xc8^\xf9\xc7T4\x95\xba\xa5\xbe\xe3~\x8b2k\xaa\xa4*\
+\x99F\xe9%I\xa2n\xefm\xa7\xb0YJ\xb0\x98x\xa7\x1b\x99\x16r{\xa9\x19\xc2nU\xa3\
+A\xf5T\xb3\xbe \xecU\x8b7\x0c#\xbds\xd0\xacDv9\xf2_O\x07\x04\x00\xd2G\xa0\
+\xef\xfb\xc4.\xe4d=5\xb4\x11\x91R\x9a\x8a{\xba(\xc9\x1c\x1ex\xde\x0f\xc9L\
+\xab\xaa\x87\x90j6\xe7\x1f\xc8>\xa8\x9cL\x8d\xd9g\xd2 \xd5Hz1d\x85U]\x03\xe9\
+e \xa5)L\xb7\xe5\x1eo\xc1[\xb9\xc1W{\xd9=WR\xaa\xec\xbb\x9e\x12\xe9UsV\xe7\
+\x90\x1b\xcf\xec\x05\x90U:\xd3$\xfd\x1b\xa7\xf3\xcf\xca\xc8\xe8\xbe\n\xf4-\
+\x81\xf3=\xaf\xff\x91\x96\xfb\x11\xd57\xd9\xebv\xfc\xf1\xcc6\x9b\xde&\xa6\
+\xd9\xd5\x87\xdc\xbd0p/\xe8]\xa2\xd9v\xb2?J\xfa\xc0S\xe3\xe6ynv\xdf\xfe1Q\
+\xdfs\xce\xd5E.\xa5T\xedgo\xc9g}n\xed3,\xb8\xa7\xbc\xa0\xaa"\xc7\xc0m\xc6\
+\xaf\xb5\xc4\xdb\x8fd\xd2\x04\x00\x02\xc0\x86O\xcc\xda\x85\x9a\xe5Ua\x12\'T\
+\xa1\xefJJ\xafr\xd34\xb3\x97\x91\x92*\xdc9\'\x99\x93-\x00\xc8\x16\x9e\xcf^|J\
+}R\x0bn\xd89\xa4\'\x90\xca\xb4\xf2H\xa4\x0f\x8c\xac[6\xe7CH\xef\xa24\xfc#W\
+\xab>w\xc4\xf4]\x012\x86R\xfa<\xebw\x11@O\xdc\xd3\x88\x0e#\xb3\xfe3=\xb4J\
+\xc7\xca9OO/\xdb\xfdq\xbc%\xb0Sk\xb2\xca\x9e:\xcdr\x1d\xc9>\xcfF\xbbA\x86]\
+\x00\xe9\xc6Y1R\xc2\x9a\x1b\xfc\xf4J\x00\xd8\xf1X\xcdNc\xa4\'c\xed,o=B\xdf!\
+\x93\x1e2\x1d\xcf\xdc\x05\x90\xfd\xf5\xb3?\\n~\x82\x10\xa2\xfc\x0f\xaaD\x81\
+\x102\xe7g/\xf69Cr#\xa9\x0c\x1d\x00\xc8&+~\x8eGf\xee\xc2P\x0f\xfe\xdc\xbd\
+\xb0\xab\xbe\x0f|\xdae\xed\xa7\x9c?-7z\xe9\x98\xa8\x07^V\xdc\xd3h\x9cg\xed\
+\xda\xfe#Y\x9bw\xba\xeb\x96GT\xe4\xf0\xa7\xcf\x8e\x0eXP\xa0\xe4\xdb\xe1\x8f0\
+\xf4Q\x01\xd4\xf0g\xba>\xe4\x89\x14wEV\xfeR\xd5N\xc5=;\xab\x93\x15S3\x9b\x9f\
+\xb6\xe7\xd4\xcbjAj\x14\x9b;\xe3vG\xe8{\xeeRV\xe9*\xb3o\x03\xfd\xb2\x9b=\x1f\
+#\xb3v?}c\x18a\xbc\xc3Ne\xcf8"\xf2\x13\x8cd\xa7)\xad\xfc\x00\xb4\x17,\xaf\
+\xbeW\x81\x1f\xaa\x05\xe8=\x0b\xb3\x0e\x96\xf1o$\xb2\xf3\xc5\x9c\x10b\x9a\
+\xa6\x1a\x01\xc8hk\xda\x11e,\xa7w\xf2\xb3\xca\x90zx\xc0\xce\x87k\xee\xc9\x9a\
+{\xb8\xe6\x06?k\'\x0e\x1c\xfc\xd1\xa7\x94}\xc6\x0c\xbc\x00\xfa\x7f\xfd\xec\
+\xa9\xaa\xb3M\x8d\x0c\xb5\x0b\xa5t\xab\xd1\x10"\xbb`\x07\x08!\xa5\xd2\x00Ow\
+\xaa\xec\x00`\x18\x86\x12\xf7T\x1f\x07^\xb4\xbb\x8ej\xee\xc2PZ\x9c^\x0c\xa3_\
+\x08\xc8N\x8b\';\xc2\xb9\'n\xffdI: \xe9\xc3U=\xf0\x84\x10jH\xd5\xc3o\xb4\xc9\
+u\xa8`\xc1\xdd\xb4\x90\xde\xb0\x8a\x1c\x96?\xad\xac\xec]\xe1\xcd\x15\x14\x11\
+\xecf\xb6{\xe5\x854\x9erR\xc5=wo\xa7B\x99\x06\xcf\xe4\xa6\xecSQ3Msc\xe3I#\
+\x08\\\xc7}\xe3\x8d\xd7\x01 +\x01\xe9\x0cO\xf6\xe6\xdf\xf5m4k\x0c\xaa\xfb!\r\
+\x1bP-g_\xcfa\xe7\x05\x9d\xea\xbb\xd9\x9b\xc7KO5w\xc4\x9ce\x94\xde\x84\xfd"\
+\xd2\xaf#9\x83\x88dLl\x9a\xf1\xf6\xa4\xc7\xcav\x7f\xfc\x1b)k\x82\xa9F\xd2\
+\x80\xc5\xdc\x96\xe9\xcd<\xf0I6&\xd9\x87k\xda`n\xf0\xfb\xc5=\xed]\xf6\x1c\
+\xb2?t\xfa[\x8f\xd9e\xccT\x7f\xce\xcaY\xf6\xa7\xcf\x8dmV\xdf\x8d\x9eo0\xb5\
+\xdc{\xab\xf6\xb7M=\xd7u\\\xc7\xcd\rQ\xeeGTr\x0c\xbd+d\xf4u;\xba/\xd9\x0b#\
+\xfb27\xd0g8\xf0GI\r\xfc\xd4J\xc8*{\xeev\xc8\xfd\x16\xd9\xf7\x03DT\xe2\x9e\
+\xfe\x82\xe93\xe0\x99^\xf5\xf6\x1f\x99\x04\xdd\x99O\x00\xc6q\xb0\xd9>~\xad%\
+\x1e\xf1\xf6\x1a\x00\x90\x91\x1e\x1ej\xba^e;\x9d\xe4\xa4\x8a;\xf4\x19\xce\
+\x9b\x9bO-\xcb\xf4}?\x17\xfe\x0c\x19kT\xfd7h\xb6\xae~\xf9\xebS\'O\xbe\xf9\
+\xe6%\x000\x0c\x033\x95\x97\xd3\xc6\xb3\xf6\xdd\x88\x0b:\xfd\xd2\xe8UzR\xd7h\
+\xf6\x81\x81\x19\xb2\x86^Ve\xd2\xcf#\x94\xa5_\xce\xd4u?\xc2\xf9\x93\x15>3\
+\xe3@\xcf\x9e\x86\x99\x99\x04\x1b\xd8\xfd\xb4\xa9q~\x94\xb4}eK\xe6\x845\xd7\
+\xfe\xf3y\x0f\xfa\xc7$\xf7|\x95;\x198\xf8\xa4\xe7\x99\xc9\xfe\xc4\xd9\xc1\
+\x19\xff\x04T\x07\xff\xf9\xe6\xadS\xa7N\xfa\x9e\x97\xfd\xf5\xb3?=\xec\xfcQ\
+\xd2\x8e+\xe7\x03\x00 \xe2\xc6\xc6\x93N7[\xd6\xf69\x1c?v,\xf5Pg{\x9d>\xe9U\
+\x97Sq\xcf\xfep\xcf:\x98Y\x83)+\xee\xe3\xffX\xfdWi*\xd0\xa9\t"\xf3q2;n\x84\
+\xf4\xcc\xd5K\x83\xbaC1\xe3_}\xd6\xe7\xd6\xfe\x934\xee\xaa\xc8\xa8\xfeL\xbc)\
+N\xf1\xf8\xd8\xb5\x96\xee\xa0\xec\xc6P\xb1\x11\x13\xb3\xd3g\t\xdd\x96\xf4\t\
+\x16\xf7T2\x10\xf1\xc6o\xfe\xe9\xff\xfb\xec\xaf\x1d\xc7\xf9\xee\xff\xf2\xed\
+\x99\x99i\xccL\xb4f\xb7\x84\xee\x95\xa4\xeepPFSz\xe9\xe44\x88\xecd\xf4\xc9\
+\xa4\x17\xb4\xfa\xaf\xba(\xb3\xcf\x8c\xecc\x06v\xde\xe4\xe9\x7fG\xc8z\xee@\
+\xd9\xab<\'"\xb0\xd3\x01\x92=D\xee\x11\x95~\xdf\xdf\xf7g\xed~\xff8\xa4-\xc3\
+\xce\xf1\x1f\xd6\xfe\x8b\xdc\xa8$\xf3|U\xb6gv\xe4\xfb\x9f.\xb9\xc1O?\xc03\
+\xcaz\xf6\x02\xbb~\xfd7\x9f\xfd\xf5_\x9f<q\xe2\x8f\xbf\xf5?\xa9(=\xdc\xc9\
+\xb0\x8e\x03@j\xdd\x07A\xf3\xee\xbd\x95\\\x12\x15B\xe8\xc2\xc2\xbceY\xfd\xe7\
+\x96\x9ey\xff\xd3\xeb\xb9\x076{\xc1\xf4?\x93\xc6o0w\xa1\xa6?D\xce\x8a\x82\
+\xbe\x07^\xee\x10\xea"\x7fY\xbd\xdb\x1fD\xb4)\xe3n\x12\xf2\x11\xceq\xafrz\
+\x9c\xd6d\xd2\x10a\x1d\x00\x88J\xda>\xc4\xc3cz\x15gjn\xc77\xcfp\xca\x87\x89\
+\xdcm\xb3\xba\xba\x06\x00q\x1c?x\xf8\xf0\xd8\xb19\xe8\xddx\xd8\xc7v\x0b\x00\
+\xd0\xbbP`\xb8\x00\xe5>\x8c&\xbb}\xf6\xde\x18\xf8\xe4\x80\xcce\xfa\x1cG!=\
+\x831\xab\xa1#\x8e\x92\xfdgn\x83\x81}\x87g\xef~\xee\xa0\xea\x9f\xbb6\xfe\xac\
+\xed\xefz\xdctX 3&95\x81\x91\xc32\x0e\xd9\x8b\xea\xe1\xa3G_\xfc\xfd/\xa5\xc4\
+\xd5\xb5\x07\xff\xf7\xc7\x9f\xfc\xcb\xf7\xde=y\xf2D\xba\xd9\xb0\xa3\xab\x7f\
+\xa6\x92\x17\x86\xe1\xf2\x8d\xdf\xb4;\x9d\x9do\xf0\xa4R)\xd5fg\x06*{\xf6Cz\
+\x88\x17\x19\xd8am\xf6\xffi\xfc\xd6v\xbdP\x87\xdd\x0bY\x1b\xe5\xa5\xf4n\xdf`\
+\xc1]\xf5a\x84\xd9\xee\x95\xe7\xc7\xab\xb5\x84\xac\xf7\x12\x80\xbbxx\xf2\x13\
+\xb3\x93*\xee\x00\x90\xb5\x05~\xe7\xcd\xc5z}\xa3T*^\xba\xf8u\xbas\xc2\x07{\
+\xcb\x083\xb1qy\xab\x01^\xea\x852\xe2\x0e\x19\xb1\xf1s\x1f(\xbd\r\x9e\xf5(\
+\xcf\xa7\xdd\xe3\x9f\xd8\x1e5>\xe6\xd1\xf7t\xf0\xd3w\x02)e\xb3\xd9\xfa\xec\
+\x17\x7f\x13\x86\xa1\xf2\x92\xd7\xeb\x1b\x1f\x7f\xf2\xb3\xdf\xfb\xdd\xdf\xb9\
+t\xf1\xa2\xe7\xb9#\x0e\x916"\x84x\xfcx\xfd\xda\xaf\xff\xa1^\xcfU6G\xc30/\x9c\
+?\xef\xfb\xfe0+u/~\xc4\x97\xde\xe6\x98\xbfH\xeep\x13\xa1\xe3\xfd\x88\xce\xba\
+\nXTZ<x#B\x9c\xe2\x89\xf1Z\xab\xcbx\x0b\x00\x08\x81\x11\xc9\x06\x9c\xe2\xf1\
+\xfet\x92\x13)\xee\xa9\xc5\x94N\xceT\xab\x95?\xfb7\xffZy\xe2\xb07\xd1\x9aB\
+\x08Q\xb2\xde\x9ba\x13i+\xfbp\xb6\xfbp]N\xd0\xa5\xbf\xcf\xec\xc5\xc8\xa4\xd7\
+\x1e\xe7\xbc\xd9l\xfd\xcd\xe7\x7f\xfb\xf4\xe9\x96:\x9a\xda\xa0\xd3\t\xaf\xfc\
+\xed\xdf\xdd\xb9s\xef\xcc\x99\xd3\xe7\xcf\x9d\xadV+#\x1ay\xf8\xf0\xd1\xed\
+\xbbwo\xdf\xba\xdd\t\xa3\xbe4\x03\xe4\xf8\xf1\xb9\xd3\xaf-\x0c\xf4WL(G\xa0\
+\x0b\xa3@\x99\xa4\xa9}G\x85\xa2\xcf\x8ee\xb6\xa3H_\x02\x10\x87>*\x88a\xf9\
+\xd53\xfd\xdfO\xa4\xb8+R\xd3)\x9d\x9c\x19v\xf5\xab\x1bI\xc5\xb3\xa7\xdbc\xef\
+\xfb#~\xb5i^*Yeg\x8c\xfd\xe6\x9f\xfeium-\xeb@Hy\xf4\xf8\xf1\xa3\xc7\x8f\xff\
+\xf1\xab\xe5cs\xb5\x93\'OT\xab\x15\x15\xe3\xa1.\xc5(\x8a\x9f<y\xb2\xb6\xf6\
+\xe0\xc9\xe6\xd3(Je}G\x0b\xa5R\xf1\xcdK\x17\x0f\x7fd\x88&eGj\xdf\xa1\x159\
+\xa8_=;Vk\xad\x07\xc8CTf\xfb\xe8\xbc4\xe6\x80\xd4\x9b\x93*\xeeY\x8f\xa7\x94\
+\xb2\xd5j\xad\xac\xaeQJ\x97\xde\xba\x0c\x99W\xbf8\x8e\x97\x97o<^_\x8f\xa2\
+\xd8\xb2\xccZm\xf6\xcc\xe9\xd7\xba\x7f\xc5\xbc?4\x8e\xe3\xe5\xeb7\xea\xf5\
+\x8d8\x8eK\xa5\xd2\xdc\\\xed\xc2\xf9s\x8e3:a\xa9\xe6\x95#\x15\xf7$I\xca\xa5\
+\xd2\xd4T\xd5\x8e\xa82\x00\x00 \x00IDAT!S\xa3\xa3\xbbI*\xd3\xedv\xeb\xf6\x9d\
+\xf6\x9d\xbb\xf7(\xa5YqF\xccUM!9\xb3\xdd\xf3\xdc\xdfY\xbcT,\x16\xcdgY$\xac9@\
+Pr\xd6ZQ\x9fG\xa4\xf6u\x8a\xc7\r\xdb\x1f\xf4\x97\x9d\xad\x89\x845W\xd45!\xe5\
+\xd0\xe2M\x86\xed\x0f\xcbK3\xa9\xe2\x9e\xa3\xd5\xee\xfc\xea\xea5\x00Xz\xebr*\
+\xd9+\xabk\x9f\xfc\xec\x7f\xc4\xf1v-\x98{\xf7Wn\xde\xba}lN\xcd)oO\xb1"\xe2\
+\xf2\xf5\x1b_\xfc\xdd/3\x1b\xaf-_\xbf\xf1\xab\xab\xd7\xbe\xf3?\xff\xc9\xdc\\\
+m\x9f\xbb\xa39\xcc\xa4o\x81I\x92LM\x15~\xf7\xcd\xc5\xdf\xfe\xf3\xcd\'\x9b\
+\x9b\xbdI\xaf\x9c\x01N\xd2]\xb2md\xd4<\xd5\xf4\xed\xbd\xa6\xa6\n\x8b\x97\xbe\
+>;;\x93]\xd1\xb3\x1f}\xd3\xbc\x00\xbc\xb5\x86<\x82n\xbe\xc6gs\xa1\xf4\xc3\
+\x9a\xf70\x9b\xb4}h\xf8\xe3\xb9ayi&U\xdcs3\xec\xdb\x81c\xbd\x10\xda\xf5\xf5\
+\xfa\'\x9f\xfc\xbfq\x92\x14\x8bS\x8b\x97.z\x9e\'\x84\xb8\xbf\xb2r\xf7\xee\
+\xfd\xad\xad\x06\xf4\xdc2j\xe3\x95\xd5\xd5\xcf~\xf17\x00\xf0\xfbo/\x9d;{\xba\
+T*\xb5\xda\xed\x7f\xf8\x87\xaf\xae\xdf\xf8\xcdG\xff\xcf\'\xff\xf6\xdf\xfc@%`\
+\xd2h\xb2\x93=\xca3c\x18\xf4\xdc\xd9\xd3S\x05\xff\xf1\xfaF/\x87\xfb\xae\x10\
+\xdc.\xe0Mv\xe4\x91!\xe4\xd8\\\xed\xc2\xf9\xb3\xe5r9\xcd\xcdpd\x1c\xeeG\x18\
+\x94\x8c\xb7\xba\x15zG\xe4\x08s\xcb\xf3\xd4\xdc=\xdf\x99d\xad4\xe3\x98\x18\
+\xb9\xc0\xd5.\xcc\x0ekdR\xc5=G\xfa\x94\x94\xbd,"\xcb7n\xc4I23=\xfd\xc7\xdf\
+\xfa\x86Z\xbe,\x84\xa8V\xca\xe5R\xe9\xd7\xff\xf0Un\xe3_\xfc\xf5\xe7\x00\xf0G\
+\xef\xfc\xc1\xeb\x17\xce\x13B\x18c\x9e\xeb\xbe\xf7\xee;Q\x1c\xdf\xbe}gy\xf9\
+\xc6\xd2\xd2e}ki\xb2~\xbc\xacW\x10\x11\xa7\xa7\xab\xbe\xef=\xdd\n\xb6\x1a\
+\x8d0\x1c(\xf1\xdd\xe0[\xe8\xfe_&\x14\xa4g\xc2\x97\xcb\xe5\xb9\xda\xec\xa9\
+\x93\'<\xcf\xcb%\xa7\xdc\xeb\xaei^\x10\x16\xdcW\x86\xb6ZA:\xf0\x07#\xa6\xe3\
+\x95\xc7J\xed\xcb\x1aw\xd2"\x04\xc3\xc3\x1f\xe9\xe8\x05\xae\x93\xea\xc8\xcb\
+\xdd`\xa9\xef2\xb5\xa7~\xfb\xdb\x9b\x00\xa0\x12\x0cd\xf3\x16-\xcc\x9f\xaaT\
+\xca\xaa\t\xb5\xf1\xa3\xc7\x8f\x9b\xcd\xd6\xd4T\xe1\xcc\xe9\xd7\x18cjc\xc5\
+\x99\xd3\xaf\x01\xc0\xca\xea\xdaAuSs\xa8\xc8\x86\xf4\xa5+*S\xcb\xdaq\x9c\xe3\
+\xc7j\xe7\xce\xbc\xb60\x7f\xaaZ-;\xf9\x84\xb4\xf9\xf9\xd2\x14\xc7q\xa6\xa7\
+\xabg\xcf\x9c~\xe3\xf5\xf3\xa7N\x9ep]\xd7u\xdd\\\xbeL\xad\xef\x87\x19\xe4\
+\x11\xefdR\xfb\x0e\xd9\xac\xb0s\x05\xe90D\xb4)z\xf5\x93GD\xca\xbb\xa5\x13\
+\xa3\x17\xb8N\xb0\xe5\x9e\xc6\xc0d\xb3\x97\xa8x\x98\x07\x0f\xba\x03}\xfc\xd8\
+\\\x9a\xce"M\xdfq\xf2\xc4\xf1\xad\xad\x06\xf6\xd6|\xab\x05PI\x9c\xfc\x8fO?K\
+\x1b\'@\x08!\x8c3\x00\x18\x16\x0e\xa1y5!\x99tl\xe9\xb5G)U\xd7\x9e\xe385\xc7\
+\x99\x99\xae\x08)\xa3(j\xb7\xc3(\x8e9\xe7R\n\x94 U>wB\t%\xa6a\xb8\xae3U(\x14\
+\n~\x9a\xfdXU\x11P\xe2\xfe\x1c\xf9a4\x07\x02\x0b\xee\x82\x94@@\x0e_Aj\xd8\
+\x85\xf1R\xfb"k\xdc\xee~\x1a\x19)\xef\x96w)\xcb7\xd9\xe2\xae\xf4z;t\x1d\x801\
+\x96\x9d\xbcJ\x8d\xac\xfe\xdcU\x88\xdd\'\x81\x10\x12\x00\x12\xc6\xd6\xd7\xeb\
+0\x04\x1d1\xa9Q(#Z\xe5]Q\x91T$\x93X?\xcd\x8d\xa5\x1e\x00\xb6e\x95K\xa5\xd1\
+\xad\xd1L\x1aj\x95P\xdeu\xdd\xd4\xdb\xae\xaf\xba\xc3\x8f\x88\x9e\xf2\xb0\x0e\
+\x00\x80\xa3V\x90\x16j_\x831~M\x16\xdc\x93\xac\x1b|\xc59\x0e\xad\xa6]:\xb5k\
+\xa4\xfc\xa4\x8a{v\x89`\x9aT\x16\x00TR\xf2\xb4pR\x14E\xc5b1\xa7\xec\xed\xb6\
+\nD\xed\xd6F0\x0c\n\x00\xb5\xd9\x99\xf7\xde}\'m\\\xddu\xca\x92J\xf3\xedi4\
+\xd0\xb3\xdc\xb3\x9f\xd3\xac\xb6\xd9\xf4\xcb\x033\xa6\xa5{\x91\xbe\x02\x03\
+\xd9\x84\xfe\xe3$\x94\xd7\x1c\x0e\x90\x05\xb7\xd5\xfc)\xe7#V\x90\x9e\xe8_A\
+\xda\x8f\xe4\x1d\xde\xec\x06SJ\t\\\x0cn\x8d\x9a\xae?}f\xd7\xd6&U\xdc!\x977\
+\xa6\x97Q@\xddQ\xbe\xef\x15\n~\xbb\xddY{\xf0\xf0\xcd\xc5j\xd6A\x9f$\xc9\x83\
+\x87\x8f\xd4\xfe\xea&\x9c\x99\x9e\x06\x80\xfa\xc6\x93\xa7[[\xd5J%\xdbx\xb3\
+\xd9D\xc447\x9eF\x93u\xbb\xa7v\xb7\x91\xa9%\x90f`\xce&\xfbM\xaf\x9f\xfe\x1d\
+\xd3l\xcc\xd9\x84\xfe\xcf\x94\x89Ss\x80\xf0\xce\xbaL\xda@F\x86?R\xd3\xab\xbc\
+6Nk\xacqG\x15b\x04\x00&\x86\xbc\x04\x00\xf8\xd3g\xc6\xf1\xddO\xb0\xb8+r\x13\
+\xaa\xa9\x91~\xf6\xcc\x99\xaf\x96\xaf_\xfb\xf5?\x9a\xa6\xb9x\xe9\xa2\xba\xcd\
+\x9a\xad\xd6\xdf\xff\xf2\xaa*Q\x8f\x00\xca\xeaw\x1c{a\xfe\xd4\xca\xea\xda\
+\x95\xbf\xfd\xfb?x\xfb\xadZ\xad\x1bZ\xb4\xb5\xd5\xf8\xf5?~E\x08\xf9\x97\xff\
+\xe2\xdd\x13\xc7\x8fk\xcf\x8cF\x91\xfa\xfaR\x03<\xcd#\x9f*{\xae6VV\xdc\xb3{e\
+\x93\x98\xe7\x12\xfa\x83\x9e\xe39\xfc\xa0L\xab#\t1\xdc\x852^\x8e0\x11o\x89\
+\xb0\x9b\\h\xc4<\xea\xf8e\xf9&^\xdc\xfbQ*\xfc\xf57^\x0f\x82\xe0\xfe\xca\xea/\
+\x7f\xf5\xe5?\xfd\xf6\x9f\x0b\x85\x02J\xf9x\xbd\x0e\x00\xe5r\xa9\xd1\x08 \
+\x93\x9d\xee\x8d\xaf]\x08\x9a\xcdF#\xf8\xfco\xff\xaeR)\xabRv*\x8b\xd3\xcc\
+\xf4t\xa9T\xd26\x94&\x87\xba\x1e\xb2I\xfc\x95\xfd\x9e\xcd\xe3\xdf\x9f\xde\
+\x16v\xfad\xb2\xf16\xc6\xe4\x94\x06\xd5(x\xfb!\xf2\x10\x08 \xe2\xb0\x15\xa4\
+\xc4t\xb2\x054\x86\x83\xacq\xa7\xfbi\xc4<*@a\xe6\xc28\xbe{82\xe2\xde\x9f\xc7\
+\x0e\x11\xdf\xfd\xa3?\x9c\x99\x99^\xbe\xfe\x9bf\xb3\xd5l\xb6\x00\xc0\xf7\xfd\
+\x8bo\xbc\xde\tC%\xeei\x9ai\xdb\xb6\xff\xe8\x0f\x7f\xff\xd6\xed\xbb\xf7\xee\
+\xaf\xa8%N\x00`\xdb\xd6k\x0b\x0b\x7f\xf8\x07o\xbb:\x03\x81f\x10\xa9\t\x0f\
+\xbd\xc8H\xec\xcbZ>L\xdc\xb3\x12O\x9e%\xa1\xbf\xe6\x90\x80\x92\xb3\x9e\x7f\
+\x9c\xf3\xe1+H\xab\xa7\xc7q\xa1\xf0\xf6c\x99\x04\xea\xf3\x88\x97\x00\xa7x\
+\xdctGM\xd1g\xd9=[\xec!D\xc5\xc3\xa8\x80t\xf5_\xccd\x82T\xb7S\x1a\xd2@)}\xb2\
+\xb9\x19\x86\x11%\xa4X\x9c\xc2^!1\xd2\xab\x93 {uS\xd5.\xf5\x8d\'\x84\x00%\
+\xf4\xc4\x89\xe3\xd9\x1a\xaa:"M3\x82\x81\xeb\x9b\xd2\x9b+{\x97\xe5\x17W\xbf@\
+Ny\xcd\x01\xc2\x82\xbb,\xb8\x0f\x04Pb\x94\x0cM\xfcR\x9e\xff}BvYN\x84\x92G\
+\x8f\x7f\x89"\x06\x00D\x88\x18\x0e|T\x10j\x96\xe7\x7f\x7f\xbc,\xf0\x00\x13m\
+\xb9gm\x1f\xe8\xf9@\xb3\x8b\x06U$;!\xa4T,\x16|_\x05\xd2\xa4/\xd1\xca\xce"\
+\x84\xe4\x8a|\xce\xd5fi\x06\xfd\x9a\xac\x19\x87\xac:\x0f\xd4\xf4\x81\xdb\x83\
+\x16\xf4\xc9\x04%\xe3\xad\x07\xea3\x13\xf9L\xcd)~\xf5\xec\xae\xca\x0e*)\x8d\
+\xe8f\xb5\xe2b\xb0\xb2\x03\x80WY\x18_\xd9ar\xc5=\xab\xeci9fu\x9f\xa4\xc1\x91\
+\xb9\x89\xd6\xf4%:-\xca\xac\xbe\xa7\x94\xa6\xfa\x9e{_\xd6\xfa\xaey\x0e\xb4p\
+\x1fyXp\x1f%\x07\x02R\xe2\x88\xbaw#\x12\xbf\xa4\xa0\x88ysU}\x968<\xfc\xd1\
+\xf2\xdc\xf28\xbe\xfb\xcc\t<\xd3\xd6\x87\x87\xac\xb2g\xa3\x17\x00@9^\x94d\
+\xa7\xaf\xc9i`rV\xdc\t!i\x06\xa84*95\xed\xb57F\xa3\xd1\x0c\x00\xa5*j\n\x00\
+\x8c\x8f0\xdb\xcf\x8c\xb5j\xa9q\x17\x91\xf7Z\x1b\x1a\xfeX\x18\x9e\xfdq\x18\
+\x13)\xee\xca\xfd\x92*{\xd6-\x03=)\xef7\xdeS\x03<\x8dO\x80\xcc\x93 -\xe2A2\
+\xabK\xf4*\x12\x8dF\x93\x83w\xd6Q$\xcal\x97r\xb0\xd9n\x17j\x967\xa0\x02W\x0e\
+\x994y\xe7\xb1\xfa<"\xfc\xd1\xf2\xa7\xed\xa9gN<>\x91\xe2\x0e=\t\x86L\x05\xd1\
+T\x85\x95\xcf\x9df\x8a\xa6\xa7\xbb\xa4.\x97tB5\x15\xf7t\xbd\t\xf4"\x1f\xd2\
+\xcd\xb4\xbek4\x9a.(\xd2 \x99\x91\xf9\x1a\xc7\xaa\xb5\x944n\xa79m\x87\xa7\
+\x91\x19\xb7\xb5\x1c\x93*\xee\xd0\xd3\xf7\xdc\xda?\x85\xb2\xc4\xb3\x11\x0b\
+\xb9\xc8\x84\xac^\x0f\x14\xf7\\t\x9aF\xa3\xd1\x00\x00k=P\xb1\xedB\xa0\x1c\
+\x96\xafq\xbcZK"\xdcP\xc5\xaf\x01\x80\x8fX\x03U:a:\xc5\xe78\xd5I\x15we\xb0\
+\x0f\x13\xdf\xac\x15\x9f\xfd2\xf7a\xd8\xc6::M\xa3\xd1\xf4\xb3]\x91cd\xb9jo\
+\x9cZK(\x93t\xd5\x12\x02\x13\x83\xb7"\x86\xedUO?\xcf\xb9N\xae\xb8\xc3n\xb2\
+\xfbL\xa2\xac\x15\\\xa3\xd1\xec\no\xad)o;\x17C\xc3\\\xbd\xf1j-\xf1\xd6\x03\
+\xe4\x9d\xee\xe7\xe1\xe1\x8f~\xf55j\xec\xde\xda@&\xb5X\x87F\xa3\xd1\xec\'(\
+\xe2\xd4l\xe7C\xccvj\xba\xeeX\xb5\x96\x90\xf5j\xf2\x8d\x08\x7f4\xec\x82S:\
+\xf9|g\x0bZ\xdc5\x1a\x8df\x1cX\xf3>J\x01\x04d>\xa3\xc46\xdex\xc9\x06D\xf8\
+\x04E\xb7\x16\xa3\x9a\x95\x1dHa\xe6\xfc8k\xa0\x86\xa1\xc5]\xa3\xd1hvA\xf2N\
+\x1a\xb3\xa8b\xdb\xfb1\x9c)\xb7x|\xf7\xb6P\xa69\xc2\x84\x1c:+k\x15f-\x7f\xfa\
+\xf9\xceV\xa1\xc5]\xa3\xd1hv\x81\x05\xf7T!=!\x86\xc6\xb6\x8f\xb9j\x89\xb7\
+\x1e\xc8\xd4\xdb\xce\x87lDhad\xf1\xebq\xd0\xe2\xae\xd1h4\xa3\x90I\xd0\xcd\
+\xb4>\xbc"\x87\xe5O\x8f\x95l@2\xd6\xbc\xa7>\xab\xf0\xc7\x81x\xe5\xf9q\x82)G\
+\xa3\xc5]\xa3\xd1hF\xc1\x82\xfb\xddBz\x12E\xbe\x18s\x17\x7f\x9c\xf0G\x00\x16\
+\xdcE\xc9A\xd5p\x1e\x12\xfeHM\xc7\xab\x8eU\xb9i4Z\xdc5\x1a\x8df("z*\xa2MPr\
+\xcc\x07;d\x9c\xe2\xb1q\xd2\xacK\xd6\xe6\xedG\xea\xf3(\xb3\xbd:V\x15\xbd]\
+\xd1\xe2\xae\xd1h4C\xe9zQH\xb7\x86F?\x84\x1ac\xadZ\x02`\x8d;\x80\xaa\xa6\xee\
+\xa8*zc\xcd\xca\x8e\x81\x16w\x8dF\xa3\x19\x8c\x08\xeb2\x0e\xd2\xfa\xd7C\xcc\
+\xf6\x13\x86\xe5\xed\xdeT\xf4TDO\xba\x9f\xe5\x90`JB\n3\xe7\xc7\xac\xa2\xb7+Z\
+\xdc5\x1a\x8df \x98\xd6\xbf\x1e\xe6E!\x86=\x9e\x7f\x1cY\xb0\x9dl`\x98\xb7\
+\xdd.\xd4L\xb7\xfc<g:\x08-\xee\x1a\x8dF3\x00\xde~$Y[\xd5\xbf\x1ef\xb6{\x95\
+\xf9q\xd2\x03\xf0\xf6#\x994\xbb\x9f\x87\xbaw\xcc\xc2\xcc\xf9\x179\xe1\x1cZ\
+\xdc5\x1a\x8d&\x0fJ\xc6\x9a]\xb3\x9dq\x18\xe8E1\xec\x82[\xda=\xd9\x00\x8a\
+\x84\x05w\xd5\xe7\x11\xc9\x06\xbc\xeaij:\xcfy\xba\x83\xd0\xe2\xae\xd1h4yXp\
+\x17y\xac\xe6Q\x07\x17\xd2#d\xaa\xf6\xc68\xd5\x91\x92\xc6-\x14I\xb7\xd9!\xc9\
+\x06,\xaf\xe2U\x16^\xe8\x8c\xfb\xd0\xe2\xae\xd1h4;\x90\xbc\xc3\xdb\x8f\x01\
+\xba\xab\x96\x06\xca\xb1\xed\xcf\x8e\x15\xfe\x187Dg\x1d\x00\x08\x00\x17C\x82\
+d\x08\xf1_\xaaCF\xa1\xc5]\xa3\xd1hv\xc0\x82{*f\x91\x0f\xcb\xfd2nu$L\x93\xb6K\
+\x18\x1ao\xe3\x16\x9f\xb3\x1c\xc7h\xb4\xb8k4\x1a\xcd6\xdb\xc9\x06`hj_\xb7tr\
+\xacZK\x9d\xbaL\x1a\xa0\xccv\x0e\xc3\xe6Q\xc7\x0c\x93\x7fV\xb4\xb8k4\x1a\xcd\
+6,\xb8\xa7\xe6O\x87."%\xc4\x1d\'\xcd:\x8a\xa4\x17\xfe(\x87\x87\xc9{\xd5\xd3\
+\xe3\x14\xf7x\x0e\xb4\xb8k4\x1aM\x17\x11o\x89\xe8)\x00\xe0p/\x8a\xe5U\xc71\
+\xdbYk\ry/i\xbb\x18\x92%\xd8\xf6\xc7+\xee\xf1<hq\xd7h4\x9a.i\xa6\xf5\x11f\
+\xfb8\xdev\x141o\xae\x00\x00\x01\x90\x12\x06\xc7\xdb\x00\xf8\xd3/T\x8ec4Z\
+\xdc5\x1a\x8d\x06\x00\x80w\xd6\xd5R#\xb5ji\xe06\xce\xd4\xb1q&?Yp\xaf\x9b\xfd\
+\x11\x80\r\x8b\xb7)\xcc\xda\x85\x99\x179\xe1\xd1hq\xd7h4\x1a\x00@ek\x03\x00\
+\x170\xb0b\xf5\x989\xc2$k\xf1\xce#P\xf3\xa8\x12^,\xde\xe6\xf9y\t\x89%5\x1a\
+\x8df\xd2\xe9&\x1b\x00\x90\xc3\x93\r8\xa5\x93\x86\xe5\x8ej\x84\x0b\xce\x85\
+\x0c\xee\x12\x94\x00\x04G\xc4\xdb\x94O\x19v\xe1%\x9c\xf7p\xb4\xb8k4\x9aW\x1e\
+\x14i\xb2\x01.\x00a\x90\xd9n\xd8~eh\x8e0\xc1y\x1c%1\'\x84m\xd8\xc9\x13\x04B\
+\x00\x98\x009\xe8\r\x00\x08\x19\'o\xc1\x0b\xa2\xdd2\x1a\x8d\xe6U\x87\xb5\xd6\
+\x90\xc7\x00 qH\xb2\x01\x00\xaf\xb2@\x0ck\xe0\xeeR\xc8\xadz=\x8aC\xdfC\x1f\
+\x1e\x03J22\xfc\xd1.\xd4F\xbf\x01\xbc\x14\xb4\xb8k4\x9aW\x1a\x94\x9c\xb7\xd6\
+\xd4g\xc6\x07\xc7,R\xcb\x1d\x11\xdb\xdei\xb56\xeb\xeb2\xe2\x06{\xca\xa3\x00\
+\x08\x05\x15\xfe8\xa4\xb8\x87\xff\xc2\xc5\xaf\xc7A\xbbe4\x1a\xcd+\rk\xae\xa0\
+`\x00 $\n9\xd8\xd6.L\x9f\x1f\x91#\x0c\x01\xcb\xd5Y\xe4Q\xbc\xb5B\x08%\x00bx\
+\xf8\xa3[^\xd8\x07\xb3\x1d\xb4\xe5\xae\xd1h^eP\xc4\xbc\xfd\x10z1\x8b\x03\xb7\
+1\xdd\x92=U\x1b\xd1\x88i\x98ni\xc61[\x04c5\x8f:,\xfc\x91Z\xeeK\xcf\xfe8\xf4\
+\xac\xf6\xe70\x1a\x8dFs\x08a\xc1=\x90\xbc["uH]\xd3\xd11\x8ba\xab\x1d\'\x08\
+\xbca\xe3&\x80A\x00\xb8\xc0a%RG\xbf\x01\xbc\\\xb4\xb8k4\x9aW\x14\xc9;\xbc\
+\xb3\x0e\xaa\xf4\xdd\x90\x98E\xbbP\xb3\xbc\xea\xb0\x16\xe2(\xeetb\x04\xea\
+\xc8\r\x82\x0c\xc1\x90\x08lH9\x0e\xcb\xab\x8e~\x03x\xb9hq\xd7h4\xaf(\xacq\
+\x17P\x02\x01\xc1qX\xcc\xa2W==xg\x84v\xab\xcd\x12&\xc1\xb0ih$u\x04J\x080\x8e\
+BH\xa2\xe6e\t\xd9\xce.@\x88?\xb3\x1f\xf3\xa8)Z\xdc5\x1a\xcd\xab\x88L\x02\x11\
+=\x81n\xc5\xea!\xc9\x06\x8a\'Lgj\xc0\x1f\x10\xda\xadv\x14\xc6\xc4\xb0\x0c*\
+\xccd\x8d "\xa1j\x01\x94\xe5\x95-\xafBA\xb2\xb8\xc9\xa2@\xe9\xbcS<\xbe\x17I\
+\xdbG\xa0\xc5]\xa3\xd1\xbc\x8a$\xc1]\x95d\x80\x0f1\xdb\t5\xfd!f{\xb3\x11\xc4\
+1\xa3\xa6%\x05\xb7I\x83\x88\x06\x12\x83\x00\xb4#\xe9\x94^+\x1d;\x0f@\x00c\
+\x0fxg\xe3~\xd8xDL\xdb\xaf\xeem\xb2\x81~t\xb4\x8cF\xa3y\xe5\x10\xd1S\x19m\
+\x01\x19e\xb6\xbb\xe5\xf9\x81\x15\xab\xc3v\'\x8c\x98aZBp\xcb\xa2\x16{\x04\
+\x84\x10\x80\x84#\x13\x0e\xa0\xbf\xfd\xa4 \xe0\xf8U@\xcb\xaf\xbe\xb6GI\xdbG\
+\xa0-w\x8dF\xf3\xaa\x81,\xb8\xab>q\x8e\x83s\x84\x99\x8eW\x99\xef\xdf\xb3\x1d\
+4\xa3(1M\x83sn;\x9e\x99\xdc\x03\x19\x12\xa0\x00\xd0\x8e\x91R\xbb\xdd\n\xbd\
+\x84%\x02\xe2\x90\x012\x02FLgfJ\x03\x9a\xdak\xb4\xe5\xae\xd1h^-x\xa7.\x93&\
+\x10\x90r\xa8\xd9\xeeW^#4o\xfb\xb6\x1a\xcd0b\x84\x9aR\xa2e\xd9&\x06\x84m \
+\x10 \x10q\x88\x99\x94\xc8M\xc7jw\xc28\x8a$J.\xa9\x00\xc7\x9b9\x15\x85\xd1\
+\xdew+\x8f\xb6\xdc5\x1a\xcd\xabE\x9al\x80\x0b\x1c\x98#\xcc\xb0\x0bN\xe9D\xee\
+\xcb\xe6\xd3F\xcc\xa4aP\xce\xa5mS\xe41\x0b\xd7\x1c\xca\x00("\xb4Ca\x9a\x96\
+\xe5\xcf\xc4\x89\x88\x9en\x02"\x00\x12 \x84RBh\x8b\xb3\xf9sg\t\x19\x18!\xb9W\
+hq\xd7h4\xaf\x102iJ\xd6\x82]\n$\x9d\xcd\x15H\n\x9e6\xe2DZ&\x95@,\x0b$K\xe2\
+\xf0i\x916\x01(\x10\xe8DR\x82Q\x9e}=l\xf3v\xeb)\xa5\x06\x10\x00\xe5\xd1\x07\
+\xe8VdM\x98\xe5\xec\xab\xdb]\x8b\xbbF\xa3y\x85`\xcd\x15D$\x00\x8c\x0f6\xdbM\
+\xb7l\x17f\xd3\x7f\xa2\x94\xc1V\xc0\x041(2\t\xb6\t\x14E"\xb9\x05\rB\x01\x80p\
+!bn\x94f\xce\'\x8c\xa0a\xd9\x9e\'\x85T\xbb\xf6\x9a\x00\xc34\x0ck\xbf\xc5V\
+\x8b\xbbF\xa3yU\xe0a\x9d\x87\x1b\x04\x80\x0f\xcb\x11F\x8d\xc2\xec\xeb\xdb\
+\xdb3\xd6nv\x18G\x00\x89\xd4pL\xc2\xa3\x90\x03X6B\xd2!\x00\x88\x92\x93\x92_9\
+\x1e% \xa4p\\\xdfu\xccV\xa7-\x84\x04)\xbbMR:S\xabQ\xba\xdf\x13\x9cZ\xdc5\x1a\
+\xcd\xab\x01\n\xd6\xb8\x0b\x00\xa3\n$\x95N\xa6\xab\x96\x92(n6Z\xc4\xb0\x08\
+\x01j\xd8\x96!\xe3N\xcb\xb0\x9c\xd2tes\xe5W\x05\x13\x01\x11\xac\x19J\x8fub)\
+\x04s\xfc\x92m\x9bq{\x0b\x04#\x94\xd8\xaeG\x00l\xdb\x99*\x15m{\xbf\xe3 A\x8b\
+\xbbF\xa3yE`\xedG\x92\x87\x04\x80\x89!\xab\x96L\'\xad\xb5\x14u\xc20\x8c\x91\
+\x10\x94\x9c\x9a\xb6Ix\xdc\xee\x98\xae[\xaaV\xc3\xd6&\x15\x1db\x02\xba\'\x12\
+Y\x89\x12\x94\x82\xf9\xc5*\x05\x9e\xb4\x83\xad\'\x8f(\x06\xd5\x99\xb9\xca\
+\xb13\x84\xd2}\x9eD\xcd\xa2C!5\x1a\xcd\xd1\x07%g\xcdU\x00@\x0014\xfc\xb1[k)\
+\xea\x84\xadV\x84\x12\x10\x81\x1a\x96m\x88\xb0\xdd2\x1d\xb7<=\r\x00\x9b\x8f\
+\xfe\xd92Q:\xf3\t\xccvb!\x04\xf7\x8b\xd3 \x13\x11u6\xeb\xf7\rh\x16<\xa3<w\
+\x96\x1a\xc6\x01*;h\xcb]\xa3\xd1\xbc\n\xf0\xd6*\x8aXy\xdb\x07\x9a\xed\x86\
+\xe5:\xa5\x93\x00\x10\xb6\xc3v+\xa4\x06\x15B\x9a\xa6i\x12\xdei\xb6]\xdf/MO\
+\x03\xc0\xd6\xc6\xaa\x89!\xf5^K\xa0\xd2\xee\x84\xd4\xb0<o\nE\x84I\xf8\xe4\
+\xc9\x9aI\xc3\x82\x83\xc5\x99y\xc3\xf2\xf7\xbd\x8by\xb4\xb8k4\x9a#\x0eJ\xc6Z\
+\x0f`\xa4\xb7\xdd\x9b>G\x08m7\xdbQ\xc4(%\x88\xc44M\xdb\x84N\xab\xe3\x16\n\
+\xa5j\x15\x00\x04O\x82\x8d\xbb\xc5\xd2\ta\xcc\x84\xad\x86\xe9\x14-\xd3\x04Lx\
+\xd4\x0e\xb6\x1e\x98$,z`\xda\x9e7\xbc\x8e\xf6~\xa2\xc5]\xa3\xd1\x1cqXp\x1f%\
+\x07\x001\xc4\xdbn\xb9%gj\xae\xd5hE1\' $1)\x01\xcb\xc0V\x10\xf8\xc5b\xb1RQ\
+\x9bm>\xbe\xeb\x17\x8f\x19\xceL\xa7\xd34\xbd*Hn\x10l76\xdb\xcdu\x83\x84e\x9f\
+\x02\x80_=\xdb\xbf\xb4\xf5@8\x14\'\xa1\xd1h4{\x84\xe4!k?\x04@@28\xd9\x00\x01\
+w\xfaB\xab\xd1\xea\x84\x89A\x11\xa8IQ\x1a\x04\xc2N{\xaaR)\x14\xbbyz\xe3\xa8M\
+\x89iz\xd5$l\x1av\tE\xe2:Vs\xf3q\xd8\xde0iX\xf6\rD0\xdd\xb2S<\xb6\xaf\xdd\
+\x1b\x8e\x16w\x8dFs\x94a\xc1=\x90\x02\xc80o\xbb\xb4\n\'\xe3\x08c&\x0c\x83\
+\x001)\x91\x040\x89:S\x95i\xaf\xd0u\x9d#b\xd4\xeeXn%\xea\x04\xc4\xae\x00r\
+\xd76\x1b\x1b\x0f\xe2p\xd32\xe2\xa2g \x1e@9\x8e\xd1hq\xd7h4G\x16\xc9Z\xbcS\
+\x87\xa1\x159\x10\x88)iEJJ\x08\'\xd4\xa0 A\x8a$j\x97g\xe7\x1c\xcf\xedn$e\x12\
+\xc5R\x90(\x89,\xa7"\x05w,\xf2\xb4\xbe\x96DO\\\x9b\xfb\x0eUY\x06\x9c\xa99\
+\xcb-\xefg\xefF\xa3\xc5]\xa3\xd1\x1cY\x92\xc6=\x00\x04B\xc4\xc0\xd4\xbe(\xd1\
+\xaeIR@)(5(E\xc9\x19O\xa2\xe9\xe3\'L\xcbR\x9bH)\xc3V\'\xec4\x19\'\xa6\xe5\
+\xa2\x14\x9ec66\xd6\xa2\xb0>\xe5\xa1k\xd2nA=j\xfa\xd3\x87\xc8l\x07-\xee\x1a\
+\x8d\xe6\xa8"\xe2\xad^!=\x1ch\xb6#uL\xef\x84\x00$\x84\x18\x06\x88$\xe6,\x99>\
+~\xd20\x8cn\x0b\x9cG\xed0\n[\x02M\xc34\xa8a\x98Tnm<\x0c;\xf5\xb2\x8f\x96\xb1\
+\xfd\xb0\xf0*\x0b\x03+{\x1c z\x11\x93F\xa39\x9a\xb0\xe0\x1e\x00\x10\x00.`P\
+\x8e04\xbcc\x1cl\x90\xc2\xb2\xa8HB\xce\x92\xda\xc9S\xdb\xca\xcex\xd4\x8e\xa4\
+d\x828\x88\xc4\xb2-\x8a\xbc\xbd\xf9(l?.\xba2\xab\xec\xd4\xf2\xdc\xca\xc2~uk\
+\\\xb4\xe5\xae\xd1h\x8e "\xdc\x10q\x03\x00$\x0eL\xed\x8bH\\i\xcc\x80\xe4\xb6\
+c\xf1\xa8\xc5\xb9\xa8\x9d\xda\xae\x97$\xb9\x88:!gL\x80!E\xe2y\x1e\x8f\xdbQs\
+\xa3\xd5\xae\x97|\xb4\x8d\x1d\xed\x15f.\xe4R\x04\x1f\x06\xb4\xb8k4\x9a\xa3\
+\x07&\xc1}\xf5i`E\x0eD$\xdeq\xa0\x86c\x1a,\x0c$\x92\xda\xa9S\xe9_y\xc2\xa20\
+\xe2\x8cK0\x12\xc6|\xcf\x8b\xa3V\xdc\\ow\x9e\x94}\xb0v*\xbb\xe5U\xed\xc2\xcc\
+\x9ew\xe8\xd9\xd1\xe2\xae\xd1h\x8e\x1a\xbc\xfdX\xb2\x16\x01\x10\x83\xccvDI\
+\xac"\xb1\xab\xa6A\x93\xce\x16\xb5\x9c\xd9Z-\xfd+\x8b\xe2$I\x18\xe3\x12)\x17\
+\xdcu\xec\xb8\x13D\xcd\xf5\x84mU|0w*;\x10\xe2\xcf\x9c\xdf\x8f.=;Z\xdc5\x1a\
+\xcd\xd1\x02%k\xae@/\xd9\xc0\x00o;!\xc49fP\x83\x85\r\xc3v+\xb3\xdb\xa59X\x1c\
+\xb7[-$\x86@\x82(=\xc7ba\xab\x1d<\x12<\x98r\xd12\x08\xee\x9c\x97\xcd\xa6\x08\
+>lhq\xd7h4G\n\xd6z\xa0R\xfb\x8a\xc1\x159$\xb5*\x86U\xe6a`\xf9\xdd\xa41\x8a\
+\xb0\xd5I\xa2H\xa0!\xa5\xa4\x84\x1a\x06aask\xf3!\xcaV\xd1\x1b\xa0\xec\xc4\
+\xb0\xbc\xea\xe9\xbd\xef\xd0sr\xe8&\x014\x1a\x8d\xe6\xb9A\xc9X\xab\x9b\xda\
+\x97\xf1\x01\x1bH$\xc4\xa9%a\xd3\x99*m+;B\x12\xc5I\x14q\xa4\x9c\x0b\xd30\tH\
+\x195\x9f\xd4W\xa3\xa45\xe5B\xbf\xb2\x03\x80_=C\x8d\x03\xa8\xc21&\xdar\xd7h4\
+G\x07\xd6T\xa9}\t\x17(1o\xb6KD\x0e>\xe1\xa4P\xa9\xf8S=w\nb\xd4\x89\xa2(\x92`\
+p\xcem\xdbB\xc9e\xd4l\x06\x0f;q8=El\x13\xfa\x95\xddp\xa6\x9c\xd2\x89}\xe8\
+\xd1s\xa3\xc5]\xa3\xd1\x1c\x11P$\xbc\xfd\x10\x06\xaeZ" %\x84\tZ\xee\xd4\xd4\
+\xf4\x8c\xeby\xdd]\x10\xe2v\'\x8ac.(\x17\xdc\xb1M)\x18\xc6M\x16=lG\x91g\x13\
+\xdf\x19\xa0\xec\x00P\x98>w\x08\xc3\x1f\xb3hq\xd7h4G\x04\xd6\xbc\x8f\x82\x01\
+\x01.aG\x8e0\x02BB\x94 \x12wjz~[\xd9%F\xedN\x1c\xc7\x02\xa9\x90\xd2\xb1M\x94\
+B\xc6M\x11=F\x19Y&\x99r\x07\xe4\x07\x06\x00\xbbP\xb3\xfc\xe9}\xe9\xd3\xf3\
+\xa3\xc5]\xa3\xd1\x1c\x05$\xef\xb0\xf6# }9\xc2\x08\x08\x01\x11CD\x98\x9d\xbf\
+X(us{I)\xe3v;\x8a\x99\x90 \xa54)\xa0\xe0D\x84\x14\x9a@:L\x12\xcb\x00\xdb\x18\
+|,\xafz(\xcaq\x8c\xe6P\xbfVh4\x1a\xcd\x98\xb0\xe0\x1e\xa0\x00\x00!`;G\x18\
+\x01. d\x88\x08~q\xa6P\xea\xce\xa0J!:\x8d \x8a\x05\x97\x04\x81\x1a\x06\x10\
+\xe4\x14\x98[p%kX&\x15\x12l\x03\x8cA\x02i\xbae\xd3>\xa4\xe1\x8fY\xb4\xb8k4\
+\x9a\x89G&M\xde\xd9\x00 \x88\xc0\xe5\xb6\xd9\xce8D\x0c\x01\x81PR\xae\x9d\xed\
+n,d\xd4j1I\x18\x97\x04\x08%\x92HA@\x94\xa6\xa7\x93\xce\xbak\nD\x10\x12\\{\
+\x90\xb7]%m?\xd0\xca\xd7c\xa2\xc5]\xa3\xd1L<Ip\x0f\x00A\xe5\x08\xeb\x99\xed\
+\x89\x80\x98\xa3\xca\x19\xe6\x17\xe7l\xaf\x08\x00(D\xd8jG\x89L\x187\r\x83\
+\x80 \x82\x01\xc8\xf2l\x8d\xb3\x10\xe2\xbai\x900\x01\xd7\x1a,\xe0\xce\xd4\
+\xb1C\x95\xb4}\x04\xda\xe7\xae\xd1h&\x1b\x11=\x11\xd1&\x00 b\x9al \xe1\x90\
+\xa8Z\xd8\x04(5+s\xe7\x00@p\x1e6[L\x00\x13h\x1a\x06\x80$\x92Y\x16\x99\xaa\
+\xce\x02!\xd1\xe6om\x93\xa8\xbf\x16\x1c\xf5\xb0\xd8\x011l\x7f\xfa\xec\xbe\
+\xf6\xed\x05\xd0\xe2\xae\xd1h&\x1aL\xb6n\xa9Oj\xd5\x12\x02D\t\x884\\\x06\xa1\
+r\xec\xbci\xbb"I\xda\xadN\xccA\n\xe1\xd8\xa6\x10\x8cuZ\xd5Z\xd5.\x94\x01 \
+\xd9\xbaeB\x08\x00\x9d\x18]kp\x90\xccT\xedk\x87-i\xfb\x08\xb4\xb8k4\x9a\x89\
+\x843\x1e3\x84x\x9d\xb06!\x06\x17(%J\x84\x88\x81\xcc\x04BZ\x8e_\xac\x9e\x94J\
+\xd9\x19J)-\xcb\x10\x82\x11\xd6\x99>6cyE\x00\x90\xbc#\xda\x0f\x01@H0\x08\xd0\
+A\xd2nyU\xbb0;\xe0\x0f\x87\x15-\xee\x1a\x8df\xc2@\xc4`\xab\xc1\xc1\xb4\xa8\
+\xa4\x9d\x15 \x14\x00DW\xd9Q\xca4U\x18A)fO^\x14q\xd4nG1C!\x85m\x1a(\x05\xb0N\
+\xa56K\xedn\xfdk\x1e\xdcC\x94\x84\xa8\x18\xcaAk\x96\x0eY\xf1\xebq\xd0\x13\
+\xaa\x1a\x8df\xc2\xd8\xdax\xb2U\xdfpL\xea\x91M"C\x02D\x08d\x1c\xc2$\xab\xec\
+\x80RL\x95\x8f\x11 A3\x8c9\x08)\x1d\xcbD\x94D\x84\x95Z-Uv\x11o\xa5E\xb4\x11\
+\x07\xcf\xa3\xba\xa5\x93\xa6S\xdc\xa7\xee\xbd$\xb4\xb8k4\x9a\t\xa3\xd3n\x03\
+\x97\x18\xb7x\xfb\x11\x00E\x80\x98\xa1Z\xa6\x94Qv4L\xcb/\xce\xb5#\xe0\x02PJ\
+\xc7\xa2Rr\x115+\xb3\xb3\x86\xed\xf5\x1aC\xd6\xb8\x03\x00\x84\x80\xc4\x81Y$\
+\x0f{\xf6\xc7ahq\xd7h4\x13\x86_*\x81i&\xcd\xfb("BI\xcc\xb0\x93\xe4\x94]\x9a\
+\xa6Q\x9d\xbb\x90\x08\x871\x0e\x00\xb6E\x05g\x98\x84\xd5\xb99j\xb9iS\xbc\xb3\
+.\x93&\x00 \x02\xe3\x83\x1c2\x00~\xf5\xf4a\xce\xfe8\x0c\xeds\xd7h4\x13F\xb9\
+\\\xe2\x9d-[\xb6\x01\xa8\x90\xd0\x8e0\xebNA)-\xcb)\xcd\x9eO\xa4\xcf\xb9\xa0\
+\x94\x98\x06\xe1I\x8c"\x9e9\xb6C\xd9Qr\x16\xdc\x03@B\x08\x13(\xe4\x80 \x19\
+\xc3.8\xa5\x93\xfb\xd4\xb1\x97\x8a\x16w\x8dF3aP\xc3(z\xb1\x08\x01\x08\x84\
+\x91\x14\x12\xb3\xcan;~y\xf6|\x87Y\x00h\x18\x94\x10\x94,A\xc9f\x8e\x1f\xcb\
+\x052\xf2\xd6*\xf2\x10T*\xe0!f{a\xe6\xfc!\xcf\xfe8\x0c-\xee\x1a\x8df\xc2\x90\
+IS\x84u\x02\x84s\x8c\x92\x1d\xca\xeez\xc5\xe2\xf4\xb9\x88\x19RH\xcb2)E\x1eE\
+\xa6\x81\xa5\xda\x1c5w\xb8VP\xc4\xac\xb5\xa6>s\xb13\x8bd\x0f\xab0{\xf8\xb3?\
+\x0eC\x8b\xbbF\xa3\x990\x92\xc6]@\t\x04:1J)\x01\x80\x10\x8a(]\xbf\xec\x97Ow\
+\x12CJi\x1a\x14@\xf002L(\xd7\xe6\x08\xcdk]\xd2\xb8\x03\x92\x03\x00"\x0c6\xdb\
+\t-L\xcez\xd4~\xb4\xb8k4\x9aIBDOE\xb4\t\x04\x12\x8e\t\x07\xdb\x9d\x02\x00\
+\xceB\xdf\x9f\xf1J\xafub\x82(l\xdb\x02\x94<\xea8\xae]\x9c\x9e%4\xefW\x91IS\
+\x84\xeb\xea3\x130\xa0\x886\x80W\x9e7\xec\xc2\x9e\xf7g\xcf\xd0\xe2\xae\xd1h&\
+\t\xde~\x04\x00\x80\xc8\xd1\x9d;\xfdu\xc7/I\x16&a;a\xb4\x13\x13\x02\xd2v,\
+\x94\x82\x87\x1do\xca-Tf\xc8\xa0\xc0\xf5\xa4q[\xa5|\x94\x12\xd2t4Y\x88\xe9x\
+\x95\x85\xbd\xee\xcb\x9e2\x91\x13\x05\x1a\x8d\xe6\xd5\x04y\xc4\xa3\xa7@\x80\
+\t\xc2D\xc5\xb0K\x84P\x04\x83K7\xe6\x06\x804(\xa2\x14,l\xf9E\x7f\xaa:X\xd9E\
+\xb8!\xe3\x862\xd6\x99\x18XD\x0f\xfc\xea\x19bX{\xdc\x9b\xbdE[\xee\x1a\x8dfbH\
+\x82\xfb \x19\x02F\x89\xc1$\x91\x82K\xc3f\x0c\x05\x12\xd3\x04\x90\x88@x\xbb9\
+5]\xf1\x8bC\x13\xf3\xb2\xe6\n@\xb7\x8e\xc7\xc0\xf0G\xd3-\xb9\xc5\xe3{\xd8\
+\x8d}A\x8b\xbbF\xa3\x99\x0c$k\xf1\xcec \x90$\x10sI\x88d\tK\x04I"\tHL\x02\tc\
+\x04Y\xf5\xd8\x8c\xe5\x0e\xad\x94$\x93\xa6d-\x181\x8f\n\xe0OOF9\x8e\xd1hq\
+\xd7h4\x93A\x12\xdc\x07\x94\x08\xd0I\x10%\xd8E/\xe1\x1c\xb9\x10(\t\x80\xa4\
+\x06\xa5P\x99\x9e5\x1c\x7fT#\x8d;\xaa\x9c\x07\xe780\xfc\xd1)\x1e\xb3\xbc\xca\
+\xde\xf5b\xdf\xd0\xe2\xae\xd1h&\x00\x11\x07\xa2\xb3\x01\x04\xe2\x04\xb9@\tv\
+\xbb\x15\xc61\x03\x00J\x08\x00\x10j\x00\x8a\xca\xec\xcc\xa8F\xc2\r\x99l\r(\
+\xa2\xdd\x83P\xd3\xabNp\xf8c\x16-\xee\x1a\x8df\x02`\xc1=\x04$\x08\\\x80k\x01\
+C\'\xe2\x843\x06@\x00\x10\x10\x10\x00\x11y\xd46\xac!y`P$\xc1\x1d\x00 \x00\tG\
+\x1cd\xb6\xbb\x95\x05#\x93\x9f`\xa2\xd1\xe2\xae\xd1h\x0e;<|\xc2\xa3M\xa2\x8a\
+iP\xa0@\x0c"\xa4\xe5\x08.\x94\xb2\x03 "1Mb:\xde\xd0F\xda\x0fU\xb2\x011$\xfc\
+\x91Z\x9eW>\xb5\x87\xdd\xd8_\xb4\xb8k4\x9aC\x0e\xb2\xe0>\x00 \x80\x90\x08\
+\x00\x08\x84b\xa7\xe8\xb6\xb8?\x1d\xc7RJ\x01\x00\x06%\xb33e\xc3\x1elw\xa3H\
+\xbaA2\x00\x8c\xe3\xc0UK\xfe\xf4\xd9\xfe\x85\xac\x93\xcb\xd1\xe9\x89F\xa39\
+\x92\xf0\xce\xbaH\x02e\xb6\xa7\x91\x8b\x08\x04\xa2\r\xc7\x8dm\xa7`\xf9\xc7\
+\x0c\xd3\xb4l\xd70\x87&\xe6e\xcd\x15\x94\x0c\x00\x10A\x0eJ\xdanz\x15gjn\xcf:\
+q\x00hq\xd7h4\x87\x17D\x91\xf4\xcc\xf6\xec\x14(\x01\x00 ,l\x1a\x16+\x1c\xff\
+\xfa C<mB k\xf0\xd6\x03\xb5\xc9`\xb3\x9d\x90\xc2\xcc\xf9\x97\x7f\xf6\x07\x8a\
+\x16w\x8dFsx\xe1\xed\xc7\x92u\x08\x00\x97\x03\x127\x92ni\xd3A\xca.#\xe0-\xe4\
+\xcd$l\xc8\xa4\xad*\xa3J\x89\x03\xbd\xedN\xf1\xc4\xc4U\xd1\xdb\x15-\xee\x1a\
+\x8d\xe6\x90\x82\x92\'\xc1\n(o\xfb\xa0\xc8E\xc3-;S\xc72;\x08\x90!\xf0\xa6H\
+\x828lFa\x12\xc7\x9c\x0bY\x9d"\x94\x12\xc0\xc1f;1,\x7f\xfa\xcc^\xf6\xe3`\xd0\
+\xe2\xae\xd1h\x0e)\xac\xb5\x86""\xc3\xf2\xad\x13R\xac\xbd\x0e\x00 c\x90mL\
+\x1a,nE\x9dv\x1c\xf3$\x11\\HD@\x00\xcb \x94\x12\x00\xe0rp\xad%\xaf\xf2\xda$V\
+\xd1\xdb\x15-\xee\x1a\x8d\xe60\x82\x82\xb1\xe6\x03Py\x02\xe4\x00\xb3\xdd\xf6\
+\xa6D\x12\xc4\xcd\x15\x10\xed(L\xe2\x84s&U8\r\x10 \x00\x94\x02\x13`\x9a\x84@\
+w\xd5\xd2\xa0*z\xbe{\x84\xc2\x1f\xb3hq\xd7h4\x87\x91\xa4q\x07e\x02\x00L\xf6-\
+8"\xc0\x05<~\xd4\x8aW~[\xf1\xc1\xa0(eW\xd0\xb3\x15\xf1$\x82\x90\xe0\x98\x08\
+\x08\x11\x838\x01B\xc1 \x00\x00\x88@)\x10B\x0b\xb3\xafOh\x15\xbd]\xd1\xe2\
+\xae\xd1h\x0e\x1d"n\xb0\xf6#\x00\x90\x12\xa4\xe8\xf3\x92\x03l\xb6p\xa3\xc1gK\
+\xd4\xa0\x04q\x87\xa6\xa7\xab\x9a\x12A,\x8bz\xbem\x9a\x06ZH\x0cnP R(m\x8f\
+\x99\x8c\x18\xa9X\x13\\\x8ec4Z\xdc5\x1a\xcda\x03\x93\xc6\xddnz/\x91\x9f\x02%\
+\x04"\x06[-\xe9;\xb4\xe0\x12\x04HC\xdf\x11\x01\x00\t\xa5\x96i8\x8ei\xdb\x86c\
+Q\x82"\x8e\x18\xe7\x02\xa4\xe4\x0c\x11\x08!`\x18\xd21I\xd0\xe1\x8c\xc5#\xa2\
+\xe3\'\x1a-\xee\x1a\x8d\xe6\xd0 \x9a \x9b<|*\xa2-  \x06\x85?\x02\xc0fS\n\t\
+\xd3\x1e\xa1\x04$ve\xdd0\xa9\xe3\x98\x8em\xda\xb6a\x10\x94R\xf0$\xe9DBH\x04D\
+ \x04\x89\x8d\x04(2@\xc98\x98\x14\x0b.\x11,\x06\xef\xa8\x05A*\xb4\xb8k4\x9aC\
+\x80d\xec\xe9o\xda\x9d N\xd0\xa6\xdc2\t\xe0\x80\xf0GB\xa0\x13c\xa3\x83\xbeK\
+\\\x1b\x84\x00\x89`\xdb\xb4R\xf1m\x93\x80\x94\x9c\xf3$L\x04\x17R\xaa\x1d\x80\
+\x00Jb\x81?o8\x15!\x90b\x88\xed;D&\x02\x89A\x80\xb3x\xff\xfb\xba?hq\xd7h4\
+\x07\x0e\xb6\xea\xd7\xb7\x9e>\xe5\x1c\x11\xc0\x99\xa2\x83W-\x11\x00\x80\xcd\
+\x16\x02\xc0\x94C\x08!\x96M,\x83X&E\x16\xb7;\xcaH\xef\xad^\xdd\xde\x93\xb4\
+\xa2\x82L\x98al\x10B$\x12\x0b=\xcfL\xd4\x96<\t\xf7\xb3\x9f\xfb\x89\x16w\x8dF\
+s\xc0$ac\xeb\xe9Se\xa7SJL\x03\x10A\xa8\x0c0\xbd\xe0\x16)AH\x14\x92\x14\\:[&\
+\x8eE)\x05\x82(\x11\x05\xe7\x9cu\xa3e\xfa\x9d8\x08\xe0O\xb9\xed\x98r\xce\x01\
+\x10\x88\xe1\xd9\x86\xfa\x13\x05\xe0,\xda\xc7\x8e\xee+Z\xdc5\x1a\xcd\x01\xc3\
+\x92D\xc52r\x01\xbe\x05\x04\x80KD\x04\x89 \x04\x08\t\x08@\x081\rb\xdbd\x8a\
+\x02"J)$\x07\xe5\xb5!0\xba(\x1e\xda$\xb0\xcas1/H\x89\x16IL\xdeB\x01\x00\xf7\
+\x9f\x18\xb1\x00\x00\x0c\xa1IDAT@\x08H\x1e#\xe2\xc0:\xda\x93\x8e\x16w\x8dFs\
+\xc0\xd8n\x11\x08A\x89\\\xa0c\x12b\x18\t\xc3(\xe1\x84\x10\x83\x12\xdb"\x06\
+\x05B\x10%\xa0\x94\\\x02\xf4\x0c\xf4q$\x99\x00\x11I\x87\x8a\x15\xcfr\x01\x08\
+\xf2H\n\x9e\xee\x8a"A\x94\x84\x18{\xd5\xb7\x83\xe3hF\xef\xbf"\x04A\xb0\xb2\
+\xba\x16\x04\xc1\xf8\x1b\xc4q\xdc\xbf\xcb\xca\xea\xdaz\xbd\xbe\x87\'\xfa\xec\
+\xac\xd7\xeb\xcfzV\xcb\xd7o\x8c\x18\x8a\xd1\xbb\\\xfd\xf2\xda\xb3\x9d\xdf\
+\xcb8\xfa\xe1!\x08\x82\xe5\xeb7\x0e\xf0\x04,\xc7\xb3\xfd\x19B\xc8\xccL\xa1<[\
+\xb6\x0b\x05\xcf5=\x9b\xfa\x0eq,\xa0D\xa2\x94B b7\xee\xf1Y\xcdlB\x88\x94\x92\
+Gm\x1e\xb7\x84\x10;\x1a@\xa1"(\x8f\x1e\xdar\x9fH\xe28\xfe\xe8\xe3O\x1c\xc7\
+\x99\x9b\xab\xdd\xbcy+\x08\x82\xef|\xfbO\x1d\xc7\xc9n\xb3|\xfd\xc6\xcf?\xfdl\
+a\xfe\xd4z}\xe3\xed\xa5\xcbKo]\x0e\x82\xe0\xaf>\xfc\xd1\\mv\xbd\xbe\xf1\xado\
+~\xe3\xc2\xf9s\x00\xf0\xc3\x0f\x7f\x04\x00Q\x1c_8\x7f\xee\xbdw\xdfQ\xfb\xaa-\
+?x\xff\xbbs\xb5Z\xff\xd1\x83 \xf8\xf3\xbf\xf8\xcb\x0f\xde\xff\xde\xc2\xfc\
+\xee\xeb\xb6o\xde\xba}\xf5\xea\xb5\x7f\xfbg?\x18\xb3k\xeb\xf5\xfaG\x1f\xff\
+\xccu\x1c\xc7\xb1\xe38\x89\xe2\xf8\xfb\xef\x7f\xb7T*e\xb7\xf9\xe1\x87?ZZ\xba\
+\xac\xce\x7f\xfb@7o\x95J\xa5\xdc\x96\xa3O;\xdd\xe5\xf3+_,\xbdu9\xbb\xf1\xff\
+\xf1\x7f\xfe_\xea\x83\xe38j\xf4Fwd\xd7\xa3\x0f\xa3\xff@\xcf\xda\xc2\x8b\xd3\
+\x08\x9a7o\xdeZ\xbctq\xff\x0f\x9dR\x9d\x9eI\xe8\x13\xd3\x92\x9d\xa0\xcd\x19G\
+D\x02\x80\x12\xb0\xe7qyA\xbf\t\x81\xc1\xbe\x1b\xc34F\xfbt&\x17-\xee\x93\x87R\
+\xf6\xa5\xa5\xb7\xea\xf5\xfa\xca\xca\xea\xc2\xc2\xfc\xe2\xe2\xc5\x8f>\xfe$\
+\xa7\xef++\xab\xdf\xf9\xf6\x9f^8\x7fN)\xf5\xd2[\x97\xbfZ\xbe\xa1\xe4ceu\xed\
+\xca\x95/.\x9c?w\xf3\xd6m\xc7\xb1?x\xff{q\x1c\xff\xf9_\xfc\xe5\xdbK\x97U\x0b\
+\x9f_\xf9\xe2\xed\xa5\xcb\x03\x95\x1d\x00~u\xf5Z\xa9TZ^\xbe>B\xdc\x7f\xfc\
+\x93\xff\xfe\xc1\xfb\xdf\x03\x80q\x1e\x00)A\x10\xfc\xf8\'?M\x1f<\x00\xb0\xb2\
+\xba\xd6\xaf\x98KK\x97\x9f\xa9\xd9\xf1O;\xcb\x7f\xfa\x8f\xffA\x9d\xd2\x7f\
+\xfb\xc9O/\x9c?\xf7\x1cGL\x07\xe1\x99\x0e\xf4\x1cO\x88I\x07y\x984\xee$\x0c\
+\xe38\x02\x00B\x00\xa9#\x91PL\x08\xc8\x17\x16\xf6\xe1\xc7\x050\r\x83\x1e\xd1\
+\xf4\x03G\xb3WG\x9b\xe5\xeb7\x16\x16\xe6WVV\x1b\x8d`i\xe9\xadF#X]]\xbbp\xe1|\
+\xee\xcdZ){\xf6\x9b\x9b\xb7n\xabo\x16\xe6OEq\x1c\x04\xc1\xca\xca\xea\x85\x0b\
+\xe7\x01\xc0q\x1c\xa5\xf5\xd0\xf5\xd2l\x0c\xb3\xe3\x94c\xe7\xfb\xef\x7f\xf7\
+\xe6\xad\xdbYG\xc4\xca\xea\xda\xd5/\xaf-_\xbf\xa16P\xffS\x1b\x94JE\xf5ev\xe3\
+8\x8e\xd5)]\xfd\xf2Z\xfa\'u\x86\xd9\xd3N%u\xbd^\x0f\x82\xe0\xea\x97\xd7\x82 \
+(\x95\xb6W\x9d,_\xbf\x91ma\x18\xc3N{WJ\xa5\x92\xeb8\x8d\xa0\xa9:\x92\xebl\
+\x10\x04ikq\x1c_\xfd\xf2\xda\xd5/\xaf\xc5q\xdc?\x08\xaa\xa7j\x84w=P\x1c\xc7\
+\xaa_i\xe3\xeb\xf5z:\xbc\xe9Xe;\x9e\x8e\x8f\xf2h\xa5\xcd\xa6C\x9d\xb6\x90\
+\xfeIm\xafNx\xfc\x01\xd9\x0bZO\x1fDQ\x02\x08\x84\x10\x04"\xec\x13\xa4\xf4u\
+\xf5?0K\xb8wn\x13\x04@.\xd5\xec\xea\x91C\x8b\xfb\xe4\xb1\xb2\xb2\xaa\xbc\x04\
+\xdf\xfa\xe67\x16\xe6O\xbd\xf7\xee;\xf3\xf3\xa7\x16/]\\YY\x1d\xb8\xfd\xcf?\
+\xfd\xec\xed\xa5\xcb\x00\x10\x04Aj\x15\x96K\xc5F\xd0\xdc\xf1M\xb9\xa4n\xf2\
+\x9f\x7f\xfa\x19\x00\xfc\xe7\xff\xf2_\x7f\xfe\xe9g\xfd\xb7\xfd\xf2\xf5\x1b\
+\x0b\xf3\xa7J\xa5\xd2\xe2\xa5\x8b_-w\x95\xe2\xe6\xad\xdbW\xae|\xa1\xcem\xf9\
+\xfa\x8d\xabW\xbf\x04\x80\xabW\xbfT\xcf\x89+W\xbep\x1c\xe7\xe7\x9f~\xa6D\'\
+\x08\x82\x9f\x7f\xfa\x99\xe38\x9f_\xf9\xe2\xe6\xcd[jK\xe5\xf5V/"\xaa\xcdTz\
+\xd4^W\xae|\xf1\xdf~\xf2\xd3(\x8a\xd5\xe7\xf5\xfa\x06\x00d[P\xdf\x0cc\xe0i\
+\x8fF\x1d\xfd\xa3\x8f?\x01\x00\xe5\xddR}T\xfe.\x00X_\xaf\xff\xd5\x87?J\xf5\
+\xfa\xf3+_\xa8/?\xfa\xf8\x93F\x10d\x07aeu\xed\xea\xd5k\x00\xb0\xbc|\xbd\xff\
+\xd1\x92;\x10\x00\xfc\xf8\'?m4\x02\x00\xf8\xe8\xe3\x9f)u\xfe\xe8\xe3\x9f\xa9\
+\xf1Q\xa3\xd1?t\xe9\xf8\xb8\x83\x86\xfa\xe6\xad\xdb\x9f~\xfa\x8b\xf4\xf4\xa0\
+\xf7\xa2\x10Eq\x14\xc5\xea\xcc\x0f\x90$Iz\xce\x17L\x84\xdf\x08HP\xdf\xe8l>i>\
+m\x05\x1d\x1f\xe0%\xcdv\xa2\xc4.r\xfb;\xc1\x04K^N\xfb\x87\x0c-\xee\x13\xcc8\
+\x06\x97\x92\xa1\xf1=\xb9J\x14\xbe\xff\xfew\xff\xf7\xff\xed\xdf+\xfb1\xb7\
+\xc1W\xcb7\x16\x17/\x02\xc0\xe2\xe2\xc5\xd4\x90\xbcz\xf5\xdaw\xbe\xfd\'Ko]\
+\xfe\xce\xb7\xfft\xe9\xad\xcb\xca\x17\xf1\xc1\xfb\xdf\xcb\x9a\xffo.^Tz\xf4\
+\xd5\xf2\x8d7\x17/\xc6q|\xf3\xd6\xed\xc5\xc5K\xb5Zmq\xf1R*\xb8\xb9\x99\x83\
+\x95\x95\xd5zoZ\xf5\xed\xa5\xcb\xef\xbd\xfbN\xfa4R-|\xf0\xfe\xf7\xd4\x11s;\
+\x8es\xda\xbbr\xf3\xe6m\x00\xf8\xe0\xfd\xeff\xbf\xfc\xd5\xd5k\xdf\x7f\xff\
+\xbbKo]\xce\xba\x8f\x00\xe0\xbdw\xdfQ#\xb0\xb2\xba6W\xabe\x07\xa1^\xaf;\x8e=\
+?\x7f\xea\x83\xf7\xbf7\xd0\xeb\x92=\xd0\xcd[\xb7K\xa5\xe2\xc2\xc2|\xadV\xbbp\
+\xe1\xdc\xcd\x9b\xb7\x1a\xdd\x17\xa0\x92z\x1b\x1b6t\xe9\xf8\xe4\x86\x1a\x00\
+\xae^\xbd\xb6\xb4t\xb9V\xab]\xb8p~\xbd\xbe\x11\x04\x81\xf2\xd1\xbd\xf7\xee;\
+\xef\xbd\xfb\x8ez\xf6\x1f \x96[\xea\xe6\xf4E0,\xc3rm\t\xc0\xb8\xe0BX\xb6A\r\
+\n\xf0\xc2\xc6;!\xe0\x1d\xa7\xe5\xaf\xe3\xd4\xd7\xa8\xb7]+\x95\x00Jq4\xc5]\
+\xfb\xdc\'\x8f\x85\x85\xf9\xe5\xeb7\xde\\\xbc\xf8\xf9\x95/.\\8\x7f\xf3\xe6\
+\xadr\xb9T\xafo\xa46o\xca\xcf?\xfd\xac^\xdfH\xb5\xa9T*\xa5\xa6z#h\x96KE\xf5\
+\r\xc0)\x00h4\x82\xb9\xb9Z\xbd^\x7fs\xf1\xa2\xdafq\xf1\xd2\xd5\xab_f\x1f\x0c\
+\xca\x11\xa1\xe6`\x157o\xdd^\xbctq\xbd^\xdf\xd5S\xbcx\xe9\xe2\x7f\xfe/\xff\
+\xf5\xbdw\xdfY\xbe~\xe3\xdf\xfd\xd9\x0f\xd6\xeb\x1bq\x1c+\xf3\x16\x00\xca\
+\xa5\xa2\xea\xda\xcd\x9b\xb7\x94\xf5Z*\x95r\xcf\xa4\xdc!\xd6\xeb\x1b\xe5\x8c\
+\x7f&\xfb9\xc7\xb0\xd3\x1e}\xc2Ko]\xbep\xfe\xdc_}\xf8\xa38\x8e\xb3O\x8e\xdc\
+\xebN\xfa\xfd\x88\xa7\x8b:\x96\xb2\xbe\xfb\xe7\x87s\x07\n\x82`\xbd\xbe\x11\
+\xf7F\xa6V\xab\xcd\xd5jo/]\xbez\xf5\xda\xcf?\xfd\xec[\xdf\xfc\x86\xe38\xfdC\
+\x07\x99\xf1\xc9\r5\x00\xac\xd7\xeb\xcb\xcb\xd7\xd3\xed\xa38\xae\xd7\xeb\xe9\
+\x05s\xe0^~\xbf4\xdb\xde\xbcmP\x01HLl\x95\x0b\x9e\xed\x95\xb8\x00\xe4\x9c$\
+\x9b<f/\xecv\xc7v<%\xa5o2N)\x8dE\xd9\x10m\xdb\xe8\xa8\x85O<\t\x1d\xbf\xf2rzr\
+\x98\xd0\xe2>y,^\xba\xf8\xd1\xc7\x9f\xbc\xfb\xee;\x00\xb0\xbc|}n\xae6?\x7f\
+\xea\xd3O\x7f\x9130\x95\xcd\x9e\x8d\xeeP^u5\xa1\xea:N\xa9TZX\x98_^\xbe\xbex\
+\xa9kG\xbf\xf7\xee;+\xabk\xcb\xcb\xd7\x95\xaa\xd6\xeb\xf5\xda\xce9\xd5\xe5\
+\xe5\x1b\xdf\xfa\xe67RY\\\xbe~\xe3WW\xaf-^\xbax\xe1\xfc\xb9\x95\xd5\xb5\x85\
+\xf9S\xca"\x1e(s\x8e\xe3\xa83W\xee\x91R\xa9\xe48\x8e\x9a\x04\x8e\xe3X\x19\
+\xa7J\xe3\xca\xe5\xbc\xac\x0fda\xfe\x94r\x1c\xa9\x16R\xb7\xcc\xcd[\xb7\x17\
+\xe6Oe\xcfa\xd8i\xefz\x88R\xa9\xf4\xf6\xd2\xe5\xcf\xaf|\xf1\x9do\xffiv\x18\
+\xaf~yME\x1f\xa5\xa6\xf1\xae,^\xba\xb8\xf4\xd6\xe5\xcf\xaf|\xa1~\x82\x11\x07\
+\x9a\x9f?\xb5\xb2\xb2\xaa\x0c\xff\xf5z\xbd\\*\xa9\xce.^\xbax\xf3\xd6\xed\xe5\
+\xe5\xeb\xea5%7tYrC\xad\xceyq\xf1\x92zj\xaa\x17\x8b\x85\x85\xf9z\xbd\xae\xbe\
+I\xdf\x8dT\xec\xe9\xb0\x89\xf4\xbd\xc3\xb4\x1c\xafr:i\xdc6\xd4\xa2\xd3\xf6c\
+\x96lQ\xc3\x90\x9c\t\xfe\xe2\xca\x0e\x00\xc4\xf1\n\xcdN\xccbI\x00\x11\x8d)\
+\xc7%\xa4\x83*\xa5\x18=\x82A\xee\xa0\xc5}\x12Q7\xf6G\x1f\x7fR*\x95\xe6\xe6j\
+\x8dF\xb0\xb2\xb2\xfa\x9do\xffI\x7f(d\xfa_\x00\xf8O\xff\xf1?\xbc\xb9x\xf1\
+\xaf>\xfc\xd1\xca\xca\xaa\n\x85\x04\xa5SW\xaf\xfd\xf0\xc3\x1fEq\xbcx\xe9bwZ\
+\xf5\xe6\xad\x1f~\xf8#\xc7\xb1\x1bA\xf3\xfb\x99\x07\xc6\xca\xeaZ#\x08\xb2\
+\x8e\x88\x0b\xe7\xcf}~\xe5\x8b\x95\xd5\xb5\xa5\xa5\xcb?\xfe\xc9O\xe7j\xb3\
+\x8d\xa0\xf9\xe6\xe2\xc5\xa5\xb7.\xcf\xd5j?\xfc\xf0G\x8b\xbd\x97\x80\xee\xf6\
+\x17\xce\xff\xf8\'\xff=\x15\xca\xb7\x97.\xff\xf9_\xfc\xa5\xdaK\x05\xe7\x94J\
+\xa5\x0f\xde\xff\xeeG\x1f\xff\xec\xe6\xcd\xdb\x8ec\xab\x83\xaaS\x1dH\xb6\x85\
+\xb4\xfb\xca\xbb\x9d\x8d\xb7\x19v\xda\xe3\x8c\xb6\xf2\xd1g7\xfe\xd67\xbf\xf1\
+\xd1\xc7\x9f|~\xe5\x8bR\xa94W\x9b\x1d\xb1o:\x08q\x1c\x7f\xb5|C\xcds|\x7f\xe7\
+3\xb8\xff@J\x91\xff\xfc/\xfeRm\xff\x9do\xffI\x1c\'\x1f}\xfc\x89\xea\xa6\nW\
+\xed\x1f\xba\\k\xb9\xa1N\x7f\xa08Nj\xb5Y\xf5\xa8\xf8\xf1O~\xaa\xdcA\x00\xa0F\
+[\xfds\xff\xc5\x1d\x00\xca\xb3\x0b\x9b<f\xad\x87&\x95\x04\x88`\xb1`\x00\xa0B\
+g\x00\xc8\x0b9f\x10\xd16;\xe5\xea\\\x94\x00"Z\x849\x18\xaaiT\t\xd6\x914\xdb\
+\x01\x80\xec\xe1L\xb4f\x8f\t\x82 \xf5\xae\x8c\xb9\x8b\xb2ps\xbb\xac\xac\xae9\
+\x8e\x9d\xbd\xa5\xd7\xeb\xf58N\x9e)\xf8/\xd7\xf2\xc0\x03\x8dy>\xe9\t\xe4\xce\
+j j\x10\xe6j\xb3\xa3}\xee{\xc4\xe7W\xbe(\x97K#"\x8b\xd2\xde\xa9\x1e\x8d\x7f\
+\x9e\xb9~\xf5\xff\xd6c\x8ep\x96\xfe\x1f\xba\xff\x9b\x83%\x89\xdaQgK$\x1d@.X\
+\x8c(@r\x00)\x85  \x01T\xdc;\x122\xd0\x98\'\xdd\xff\x0c\x924D4l\x97ZSR"\xb2&\
+\x8a\x04\x80\x08\t\xfe\xec\x1bS\xd5\x13{\xdb\xab\x03B\x8b\xbbF\xf3l|\xf4\xf1\
+\'ss\xb5Z\xad\x16\x04\xc1\xaf\xae^\xfbw\x7f\xf6\x83\x03y\xae\xbc* "J!\x18\
+\xef\xd4E\xe7A3.\xb1\x84q))A\n\x12\x08\x12\x90\x04\xa4A\x91\x02\xa7\x14$\x1a\
+\x84\xa8u\xa7\xa2\xd7@w\x95\x12%\xca\x0b\x03\x00D"A\xea\xf8\xd3g\xa7*\xc7\
+\x0f\xb0s{\x8a\x16w\x8d\xe6\xd9X\xaf\xd7\x97\x97o\xa8i\xd5\xb7\x97.\x1f\xf8l\
+\xe4+B\x12\xdc\x83x\x1d\xcc\xb9\x8d\xa7,\xe2`P"z\x01\x8d\x04\xf0X\xd5\x88\
+\xe3\xa7\x05\x8b\xcb\xc29\x166X\xb8\xc9\x936"P\x02\xbeC\x10@\x9aU\x0e.AA\x08\
+H\xa0\xb6W\xf6\x8b3\xd4\xb0\x0e\xb4O{\x8b\x16w\x8dF3\x01\x04\xeb\xbf\xb5\xf9\
+#\xc7\xab\x05\xa1\x8d\x00\xed\x08#\x86\x84\x10D4)99m<\xd9\xaa\xfbfG"\xe5\xbd\
+\x12\x1f\x080\xe5\x80\xe7\x11@\x00\xb3\x0c\x95\xdf;\xd8.\xec3:\xce]\xa3\xd1L\
+\x00I\x12\xc7LJ\xb6\xe9\xdb\xbc\\0-\xcb4\rbP\xf0lZ.\x18I\xc2\xa4\x88\x9a!f\
+\x95\xdd \xe0\xda\x04$\x00\x02\xb0\x06t\xee\x1cl\x17\xf6\x19\x1d-\xa3\xd1h&\
+\x00\xc1Y\xab\x03&e\x08\x1b\xad\xa8R.\x94<\x0b%J\xd3 \x82\x85\xcd\xf6S)\x05"\
+\xd9\xae\xa6\x8d\xe0:@\xb2\xeb\x9f\xda\xab`\xcd\x80\xf5\xaa\xb8\xd1\xb4[F\
+\xa3\xd1\x1cRP2d\x1d\xe4M\xca\x1b\x8f\xd6\x9fD\x894)\xf8\x0e$\x1c\x01LBM \
+\x04%\x97\x82\x03 "\x91\x08\xbeC\xa0g\xb6W\xa7\xfa\x8ap\x18>T\xdf\x82\xa3\
+\x98\xbd\xbd\x1fm\xb9k4\x9aC\x04J\x86\xbc#\xe3\x86L\x82n\xcc"\x01D\x10\x02\
+\x95P\x87\t\x98\x94\x00p\x90\x1c\x00\x08\x80i\x10\x00\x92\xf0\xed\xdc\xbd\
+\x88\xe0\xe5\xccv\x85\xe8@\xfb.L\x9d\xdf\xd7.\x1d\x10Z\xdc5\x1a\xcd\x81\x82\
+\x02%G\xde\x91IC&Md-\x94\xac\xab\xca=i\x96r;\xa2Q\x15J5\x08I\xa3\xddew\x03\
+\xa4\x94\x10\x02R\x82I\xc1\xb5\xc9\xe0uO\xe1\x03\xf0N\x80\xe1\xefC\xcf\x0e\
+\x16-\xee\x1a\x8df\xdf\x91\t\xf06\xb2\x80G\r\xc9C\x90,\x9b\xa91\x07\x01\x90\
+\x08\x123\xff\xce\x80\x00\x80\xa0\x12\t\xd0\xde\n\xa6\x82;\xc8lO\xf7h\xfe\
+\x16\xca\xbf\x0bG4\x8d{\xca\xff\x0fY\xef?\x965\xa5@\x17\x00\x00\x00\x00IEND\
+\xaeB`\x82' 
 
 def getIDESplashBitmap():
     return BitmapFromImage(getIDESplashImage())
 
 def getIDESplashBitmap():
     return BitmapFromImage(getIDESplashImage())
index 54375dd482324f8ac30d126ab9ebc7eb9554f466..5fd24b5c0f1ad9998744dd227d5e07c438381953 100644 (file)
@@ -36,16 +36,16 @@ class MarkerService(wx.lib.pydocview.DocService):
 
         editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
         editMenu.AppendSeparator()
 
         editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
         editMenu.AppendSeparator()
-        editMenu.Append(MarkerService.MARKERTOGGLE_ID, _("Toggle &Marker\tCtrl+M"), _("Toggles a jump marker to text line"))
+        editMenu.Append(MarkerService.MARKERTOGGLE_ID, _("Toggle &Bookmark\tCtrl+M"), _("Toggles a bookmark at text line"))
         wx.EVT_MENU(frame, MarkerService.MARKERTOGGLE_ID, frame.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, MarkerService.MARKERTOGGLE_ID, frame.ProcessUpdateUIEvent)
         wx.EVT_MENU(frame, MarkerService.MARKERTOGGLE_ID, frame.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, MarkerService.MARKERTOGGLE_ID, frame.ProcessUpdateUIEvent)
-        editMenu.Append(MarkerService.MARKERDELALL_ID, _("Clear Markers"), _("Removes all jump markers from selected file"))
+        editMenu.Append(MarkerService.MARKERDELALL_ID, _("Clear Bookmarks"), _("Removes all jump bookmarks from selected file"))
         wx.EVT_MENU(frame, MarkerService.MARKERDELALL_ID, frame.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, MarkerService.MARKERDELALL_ID, frame.ProcessUpdateUIEvent)
         wx.EVT_MENU(frame, MarkerService.MARKERDELALL_ID, frame.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, MarkerService.MARKERDELALL_ID, frame.ProcessUpdateUIEvent)
-        editMenu.Append(MarkerService.MARKERNEXT_ID, _("Marker Next\tF4"), _("Moves to next marker in selected file"))
+        editMenu.Append(MarkerService.MARKERNEXT_ID, _("Bookmark Next\tF4"), _("Moves to next bookmark in selected file"))
         wx.EVT_MENU(frame, MarkerService.MARKERNEXT_ID, frame.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, MarkerService.MARKERNEXT_ID, frame.ProcessUpdateUIEvent)
         wx.EVT_MENU(frame, MarkerService.MARKERNEXT_ID, frame.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, MarkerService.MARKERNEXT_ID, frame.ProcessUpdateUIEvent)
-        editMenu.Append(MarkerService.MARKERPREV_ID, _("Marker Previous\tShift+F4"), _("Moves to previous marker in selected file"))
+        editMenu.Append(MarkerService.MARKERPREV_ID, _("Bookmark Previous\tShift+F4"), _("Moves to previous bookmark in selected file"))
         wx.EVT_MENU(frame, MarkerService.MARKERPREV_ID, frame.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, MarkerService.MARKERPREV_ID, frame.ProcessUpdateUIEvent)
 
         wx.EVT_MENU(frame, MarkerService.MARKERPREV_ID, frame.ProcessEvent)
         wx.EVT_UPDATE_UI(frame, MarkerService.MARKERPREV_ID, frame.ProcessUpdateUIEvent)
 
index 1dd15ebd886179505fe485db6edcc576a127017a..4765a40ead3e317f372be83c6ffebde05351f749 100644 (file)
@@ -124,6 +124,7 @@ class MessageView(Service.ServiceView):
 
 
     def AddLines(self, text):
 
 
     def AddLines(self, text):
+        self.GetControl().SetCurrentPos(self.GetControl().GetTextLength())
         self.GetControl().SetReadOnly(False)
         self.GetControl().AddText(text)
         self.GetControl().SetReadOnly(True)
         self.GetControl().SetReadOnly(False)
         self.GetControl().AddText(text)
         self.GetControl().SetReadOnly(True)
index 75fc67e0d224acd63fafd0dcb17090029473711a..289d4315cb336a476d58db02d16d35c06020711a 100644 (file)
@@ -147,7 +147,11 @@ class OutlineView(Service.ServiceView):
             return
 
         treeCtrl = self.GetControl()
             return
 
         treeCtrl = self.GetControl()
+        
         parentItem = treeCtrl.GetRootItem()
         parentItem = treeCtrl.GetRootItem()
+        if not parentItem:
+            return
+            
         if expanded[0] != treeCtrl.GetItemText(parentItem):
             return
 
         if expanded[0] != treeCtrl.GetItemText(parentItem):
             return
 
@@ -157,8 +161,7 @@ class OutlineView(Service.ServiceView):
                 treeCtrl.Expand(child)
             (child, cookie) = treeCtrl.GetNextChild(parentItem, cookie)
 
                 treeCtrl.Expand(child)
             (child, cookie) = treeCtrl.GetNextChild(parentItem, cookie)
 
-        if parentItem:
-            treeCtrl.EnsureVisible(parentItem)
+        treeCtrl.EnsureVisible(parentItem)
 
 
 class OutlineTreeCtrl(wx.TreeCtrl):
 
 
 class OutlineTreeCtrl(wx.TreeCtrl):
@@ -267,7 +270,7 @@ class OutlineTreeCtrl(wx.TreeCtrl):
 
         if self.ItemHasChildren(item):
             child, cookie = self.GetFirstChild(item)
 
         if self.ItemHasChildren(item):
             child, cookie = self.GetFirstChild(item)
-            while child and child.IsOk():
+            while child.IsOk():
                 self.FindDistanceToTreeItems(child, position, distances, items)
                 child, cookie = self.GetNextChild(item, cookie)
         return False
                 self.FindDistanceToTreeItems(child, position, distances, items)
                 child, cookie = self.GetNextChild(item, cookie)
         return False
diff --git a/wxPython/samples/ide/activegrid/tool/PHPDebugger.py b/wxPython/samples/ide/activegrid/tool/PHPDebugger.py
new file mode 100644 (file)
index 0000000..8dd0d17
--- /dev/null
@@ -0,0 +1,2105 @@
+#---------------------------------------------------------------------------
+# Name:         PHPDebugger.py
+# Purpose:      php dbg client and supporting code
+# Author:       Matt Fryer, Kevin Wang
+# Created:      2/1/06
+# Copyright:    (c) 2006 ActiveGrid, Inc.
+# License:      wxWindows License
+#---------------------------------------------------------------------------
+
+
+import os
+import socket
+import sys
+import threading
+import traceback
+import wx
+import DebuggerService
+import activegrid.util.sysutils as sysutils
+
+
+DBGC_REPLY                              = 0x0               # reply to previous DBGA_REQUEST request
+DBGC_STARTUP                            = 0x0001            # script startup
+DBGC_END                                = 0x0002            # script done
+DBGC_BREAKPOINT                         = 0x0003            # user definded breakpoint occured
+DBGC_STEPINTO_DONE                      = 0x0004            # step to the next statement is completed
+DBGC_STEPOVER_DONE                      = 0x0005            # step to the next statement is completed
+DBGC_STEPOUT_DONE                       = 0x0006            # step to the next statement is completed
+DBGC_EMBEDDED_BREAK                     = 0x0007            # breakpoint caused by DebugBreak() function
+DBGC_ERROR                              = 0x0010            # error occured
+DBGC_LOG                                = 0x0011            # logging support
+DBGC_SID                                = 0x0012            # send SID
+DBGC_PAUSE                              = 0x0013            # pause current session as soon as possible
+DBGC_AG_SHUTDOWN_REQ                    = 0x0201            # special ActiveGrid UI shutdown listening thread command
+
+
+FRAME_STACK                             = 100000            # "call:stack" - e.g. backtrace
+FRAME_SOURCE                            = 100100            # source text
+FRAME_SRC_TREE                          = 100200            # tree of source files
+FRAME_RAWDATA                           = 100300            # raw data or string
+FRAME_ERROR                             = 100400            # error notification
+FRAME_EVAL                              = 100500            # evaluating/watching
+FRAME_BPS                               = 100600            # set/remove breakpoint
+FRAME_BPL                               = 100700            # breakpoint(s) request = get the list
+FRAME_VER                               = 100800            # version request
+FRAME_SID                               = 100900            # session id info
+FRAME_SRCLINESINFO                      = 101000            # source lines info
+FRAME_SRCCTXINFO                        = 101100            # source contexts info
+FRAME_LOG                               = 101200            # logging
+FRAME_PROF                              = 101300            # profiler
+FRAME_PROF_C                            = 101400            # profiler counter/accuracy
+FRAME_SET_OPT                           = 101500            # set/update options
+
+
+DBGF_STARTED                            = 0x0001            # debugger has been started
+DBGF_FINISHED                           = 0x0002            # DBGC_END notification has been sent
+DBGF_WAITACK                            = 0x0004            # awaiting replay|request
+DBGF_UNSYNC                             = 0x0008            # protocol has been unsynchronized
+DBGF_REQUESTPENDING                     = 0x0010            # Debug session request pending
+DBGF_REQUESTFOUND                       = 0x0020            # Debug session request found
+DBGF_REJECTIONFOUND                     = 0x0040            # DBGSESSID=-1 found - session rejection
+
+
+E_ERROR                                 = 1 << 0
+E_WARNING                               = 1 << 1
+E_PARSE                                 = 1 << 2
+E_NOTICE                                = 1 << 3
+E_CORE_ERROR                            = 1 << 4
+E_CORE_WARNING                          = 1 << 5
+E_COMPILE_ERROR                         = 1 << 6
+E_COMPILE_WARNING                       = 1 << 7
+E_USER_ERROR                            = 1 << 8
+E_USER_WARNING                          = 1 << 9
+E_USER_NOTICE                           = 1 << 10
+
+
+BPS_DELETED                             = 0
+BPS_DISABLED                            = 1
+BPS_ENABLED                             = 2
+BPS_UNRESOLVED                          = 0x100
+
+
+DBG_SYNC                                = 0x5953
+DBG_SYNC2_STR                           = chr(0) + chr(0) + chr(89) + chr(83)
+RESPONSE_HEADER_SIZE                    = 16
+
+
+_VERBOSE = False
+def myprint(format, vlist=None):
+    if _VERBOSE:
+        if vlist:
+            print format % vlist
+        else:
+            print format
+
+
+#
+# 4 Char's to an Integer
+#
+def C4ToInt(ch, startPos):
+    retval = 0
+    pos    = startPos
+
+    retval = retval + (CharToInt(ch[pos]) << 24)
+    pos    = pos + 1
+    retval = retval + (CharToInt(ch[pos]) << 16)
+    pos    = pos + 1
+    retval = retval + (CharToInt(ch[pos]) << 8)
+    pos    = pos + 1
+    retval = retval + (CharToInt(ch[pos]) << 0)
+
+    return retval
+
+
+def CharToInt(ch):
+    return int((ord(ch) & 0x00FF));
+
+
+#
+# An Integer to 4 Char's
+#
+def IntToC4(num):
+    retval  = chr((num >> 24) & 0x00FF)
+    retval += chr((num >> 16) & 0x00FF)
+    retval += chr((num >> 8 ) & 0x00FF)
+    retval += chr((num >> 0 ) & 0x00FF)
+
+    return retval
+
+
+DBGA_CONTINUE                           = IntToC4(0x8001)
+DBGA_STOP                               = IntToC4(0x8002)
+DBGA_STEPINTO                           = IntToC4(0x8003)
+DBGA_STEPOVER                           = IntToC4(0x8004)
+DBGA_STEPOUT                            = IntToC4(0x8005)
+DBGA_IGNORE                             = IntToC4(0x8006)
+DBGA_REQUEST                            = IntToC4(0x8010)
+
+
+def getCommandString(code):
+    if code == DBGC_REPLY:
+        return "REPLY"
+    elif code == DBGC_STARTUP:
+        return "STARTUP"
+    elif code == DBGC_END:
+        return "END"
+    elif code == DBGC_BREAKPOINT:
+        return "BREAKPOINT"
+    elif code == DBGC_STEPINTO_DONE:
+        return "STEPINTO DONE"
+    elif code == DBGC_STEPOVER_DONE:
+        return "STEPOVER DONE"
+    elif code == DBGC_STEPOUT_DONE:
+        return "STEPOUT DONE"
+    elif code == DBGC_EMBEDDED_BREAK:
+        return "EMBEDDED BREAK"
+    elif code == DBGC_PAUSE:
+        return "PAUSE"
+    elif code == DBGC_ERROR:
+        return "ERROR"
+    elif code == DBGC_LOG:
+        return "LOG"
+    elif code == DBGC_SID:
+        return "SEND SID"
+    elif code == DBGC_AG_SHUTDOWN_REQ:
+        return "AG SHUTDOWN REQ"
+
+
+def reportFlags(flagsValue):
+    flagsRetVal = ""
+    if flagsValue & DBGF_STARTED:                  # debugger has been started
+        flagsRetVal += "started+"
+    if flagsValue & DBGF_FINISHED:                 # DBGC_END notification has been sent
+        flagsRetVal += "finished+"
+    if flagsValue & DBGF_WAITACK:                  # awaiting replay|request
+        flagsRetVal += "awaiting ack+"
+    if flagsValue & DBGF_UNSYNC:                   # protocol has been unsynchronized
+        flagsRetVal += "protocol unsynchronized+"
+    if flagsValue & DBGF_REQUESTPENDING:           # Debug session request pending
+        flagsRetVal += "request pending+"
+    if flagsValue & DBGF_REQUESTFOUND:             # Debug session request found
+        flagsRetVal += "request found+"
+    if flagsValue & DBGF_REJECTIONFOUND :          # DBGSESSID=-1 found - session rejection
+        flagsRetVal += "session rejection+"
+    return flagsRetVal
+
+
+def getErrorTypeString(code):
+    if code == E_ERROR:
+        return "[Error]"
+    elif code == E_WARNING:
+        return "[Warning]"
+    elif code == E_PARSE:
+         return "[Parse Error]"
+    elif code == E_NOTICE:
+        return "[Notice]"
+    elif code == E_CORE_ERROR:
+        return "[Core Error]"
+    elif code == E_CORE_WARNING:
+        return "[Core Warning]"
+    elif code == E_COMPILE_ERROR:
+        return  "[Compile Error]"
+    elif code == E_COMPILE_WARNING:
+        return  "[Compile Warning]"
+    elif code == E_USER_ERROR:
+        return  "[User Error]"
+    elif code == E_USER_WARNING:
+        return  "[User Warning]"
+    elif code == E_USER_NOTICE:
+        return  "[User Notice]"
+    else:
+        return "[Unexpected Error]"
+
+
+class ResponseHeader(object):
+    def __init__(self, conn, blocking = False):
+        self.isValid = False
+        receivedData = conn.recv(RESPONSE_HEADER_SIZE, blocking)
+
+        if not receivedData:
+            myprint("Tried to get %d bytes of PHP DBG header, got None\n" % RESPONSE_HEADER_SIZE)
+            return
+        elif len(receivedData) != RESPONSE_HEADER_SIZE:
+            myprint("Tried to get %d bytes of PHP DBG header, got %d\n" % (RESPONSE_HEADER_SIZE, len(receivedData)))
+            return
+
+        self.sync    = C4ToInt(receivedData, 0)
+        self.command = C4ToInt(receivedData, 4)
+        self.flags   = C4ToInt(receivedData, 8)
+        self.toRead  = C4ToInt(receivedData, 12)
+
+        myprint("ResponseHeader: sync=%x, command=%s, flags=(%s), toRead=%s\n", (self.sync, getCommandString(self.command), reportFlags(self.flags), self.toRead))
+        if self.sync != DBG_SYNC:
+            myprint("Sync wrong for header! Expected %x, got %s\n" % (DBG_SYNC, self.sync))
+            return
+
+        self.isValid = True
+
+
+class ResponsePacketFrame(object):
+    def __init__(self, conn, size, data, blocking = False):
+        self.isValid      = False
+        self.conn         = conn
+        self.data         = ''
+
+        if data:
+            self.data     = data
+            newlyReceived = False
+        elif conn:
+            newlyReceived = True
+
+            sizeToReceive = size
+            while True:
+                oneChunk  = conn.recv(sizeToReceive, blocking)
+                sizeReceived = len(oneChunk)
+                if sizeReceived > 0:
+                    self.data = self.data + oneChunk
+
+                if sizeReceived < sizeToReceive:
+                    sizeToReceive = sizeToReceive - sizeReceived
+                    continue
+                else:
+                    break
+
+            if len(self.data) != size:
+                myprint("Expected to get %d bytes of a PHP DBG packet, got %d\n" % (size, len(self.data)))
+                return
+        else:
+            return
+
+        self.frameName = C4ToInt(self.data, 0)
+        self.frameSize = C4ToInt(self.data, 4)
+        if newlyReceived:
+            myprint("Newly received ResponsePacketFrame: frameName=%d, frameSize=%d", (self.frameName, self.frameSize))
+        else:
+            myprint("Created from existing ResponsePacketFrame: frameName=%d, frameSize=%d", (self.frameName, self.frameSize))
+
+        if self.frameSize == 0:
+            return
+
+        self.currPos      = 8
+        self.totalDataLen = len(self.data)
+        self.length       = 8 + self.frameSize
+        self.isValid      = True
+        myprint("new ResponsePacketFrame: currPos=%s, totalDataLen=%s, length=%s", (repr(self.currPos), repr(self.totalDataLen), repr(self.length)))
+        return
+
+    def getNextInt(self):
+        myprint("getNextInt(): currPos=%s, totalDataLen=%s, length=%s", (repr(self.currPos), repr(self.totalDataLen), repr(self.length)))
+        if self.isValid and self.currPos + 4 <= self.length:
+            val = C4ToInt(self.data, self.currPos)
+            self.currPos = self.currPos + 4
+            myprint("getNextInt(): got an integar: %s", repr(val))
+            return val
+        else:
+            return self._errorReturn("getNextInt(): no more integar available with current frame: ")
+
+    def getNextString(self, strLen):
+        endPos = self.currPos + strLen
+        if self.isValid and endPos <= self.length:
+            #
+            # Trim the ending '\0'.  TODO: confirm this applies to all raw string data.
+            #
+            str          = self.data[self.currPos:endPos - 1]
+            self.currPos = endPos
+            myprint("getNextString(): got a string: %s", str)
+            return str
+        else:
+            return self._errorReturn("getNextString(): no more string available with current frame: ")
+
+    def getNextFrame(self, useAbsolutePos = False):
+        if useAbsolutePos:
+            #
+            # Skip this frame's header (8 bytes for frameSize and frameSize) and frame data (frameSize).
+            #
+            self.currPos = self.length
+
+        if self.isValid and self.currPos < self.totalDataLen:
+            return ResponsePacketFrame(None, None, self.data[self.currPos:])
+        else:
+            return self._errorReturn("getNextFrame(): no more frame available with current frame: ")
+
+    def _errorReturn(self, preMsg = ''):
+        myprint(preMsg + "frameName=%s, frameSize=%s, totalDataLen=%s, length=%s, currPos:%s", (repr(self.frameName), repr(self.frameSize), repr(self.totalDataLen), repr(self.length), repr(self.currPos)))
+        self.isValid = False
+        return None
+
+
+class PHPDBGFrame(object):
+    FRAME_HEADER_SIZE = 8
+    def __init__(self, frameType):
+        self.frameType = IntToC4(frameType)
+        self.frameData = ""
+
+    def addInt(self, intVal):
+        self.frameData = self.frameData + IntToC4(intVal)
+
+    def addChar(self, charVal):
+        self.frameData = self.frameData + charVal
+
+    def addStr(self, string):
+        #
+        # Add the trailing '\0'.
+        #
+        self.frameData = self.frameData + string + '\0'
+
+    def getSize(self):
+        return len(self.frameData) + PHPDBGFrame.FRAME_HEADER_SIZE
+
+    def writeFrame(self, conn):
+        header = self.frameType + IntToC4(len(self.frameData))
+        conn.sendall(header)
+        conn.sendall(self.frameData)
+
+
+class PHPDBGPacket(object):
+    def __init__(self, packetType):
+        self.header     = DBG_SYNC2_STR + packetType
+        self.frames     = []
+        self.packetSize = 0
+
+    def addFrame(self, frame):
+        self.frames.append(frame)
+        self.packetSize += frame.getSize()
+
+    def sendPacket(self, conn, flags = 0):
+        self.header += IntToC4(flags)
+        self.header += IntToC4(self.packetSize)
+        conn.sendall(self.header)
+        for frame in self.frames:
+            frame.writeFrame(conn)
+
+
+class PHPDBGException(Exception):
+    def __init__(self, msg = None, cause = None):
+        if (msg == None):
+            Exception.__init__(self)
+        elif (cause == None):
+            Exception.__init__(self, msg)
+        else:
+            Exception.__init__(self, "PHPDBGException: message:%s\n, cause:%s" % (msg, cause))
+
+
+class PHPDBGConnException(PHPDBGException):
+    pass
+
+
+class PHPDebuggerCallback(object):
+    ACTION_NONE              = 0
+    ACTION_STOP              = 1
+    ACTION_LISTEN            = 2
+
+    def __init__(self, ui, service, lsnrHosti, lsnrPorti):
+        self.ui              = ui
+        self.service         = service
+        self.lsnrHost        = lsnrHosti
+        self.lsnrPort        = lsnrPorti
+        self.lsnrThr         = None
+        self.lsnrAction      = PHPDebuggerCallback.ACTION_NONE
+        self.clearInternals()
+        self.initLsnrThr()
+
+
+    ############################################################################
+    #                      Public callback functions begin
+    #
+    def Start(self):
+        self.lsnrThr.start()
+
+    def ShutdownServer(self, stopLsnr = True):
+        #
+        # First to tell php debugger to stop execution of the current PHP
+        # program.  Disconnect with php dbg module too.
+        #
+        self.stopPhpDbg()
+
+        #
+        # Stop debug listener.
+        #
+        if stopLsnr:
+            self.stopLsnr()
+
+    def BreakExecution(self):
+        reqPacket = PHPDBGPacket(IntToC4(DBGC_PAUSE))
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.awaitAndHandleResponse()
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return
+
+        self.ui.LoadPHPFramesList(self.stackList)
+        return
+
+    def SingleStep(self):
+        reqPacket = PHPDBGPacket(DBGA_STEPINTO)
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.lastCommand = DBGA_STEPINTO
+            self.awaitAndHandleResponse(blocking = True)
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return
+
+        self.ui.LoadPHPFramesList(self.stackList)
+        return
+
+    def Next(self):
+        reqPacket = PHPDBGPacket(DBGA_STEPOVER)
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.lastCommand = DBGA_STEPOVER
+            self.awaitAndHandleResponse(blocking = True)
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return
+
+        self.ui.LoadPHPFramesList(self.stackList)
+        return
+
+    def Continue(self):
+        reqPacket = PHPDBGPacket(DBGA_CONTINUE)
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.lastCommand = DBGA_CONTINUE
+            self.awaitAndHandleResponse(blocking = True)
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return
+
+        self.ui.LoadPHPFramesList(self.stackList)
+        return
+
+    def Return(self):
+        reqPacket = PHPDBGPacket(DBGA_STEPOUT)
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.lastCommand = DBGA_STEPOUT
+            self.awaitAndHandleResponse(blocking = True)
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return
+
+        self.ui.LoadPHPFramesList(self.stackList)
+        return
+
+    def PushBreakpoints(self, noRemove = False):
+        tmpList = []
+        bps     = self.service.GetMasterBreakpointDict()
+        for fileName in bps.keys():
+            if fileName.endswith('.php'):
+                lines = bps[fileName]
+                if lines:
+                    for lineNo in lines:
+                        if lineNo:
+                            #
+                            # A tuple (fileName, lineNo) is an item which is
+                            # used as a key in self.bpDict.
+                            #
+                            tmpList.append(self.createBpKey(fileName, lineNo))
+                            myprint("PushBreakpoints(): global breakpoint \'%s:%i\'", (fileName, lineNo))
+
+        #
+        # Check to see if we have any new breakpoints added.
+        #
+        for oneKey in tmpList:
+            if not self.bpDict.has_key(oneKey):
+                #
+                # A new breakpoint.
+                #
+                newBp = BreakPoint(self, oneKey[0], oneKey[1])
+                newBp.addSelf()
+                self.bpDict[oneKey] = newBp
+                myprint("PushBreakpoints(): newly added global breakpoint \'%s:%i\'", (oneKey[0], oneKey[1]))
+
+        if noRemove:
+            return
+
+        #
+        # Check to see if any bp that is in our list, but not in the latest
+        # global list.  If so, it must have been removed recently in the
+        # global one.  Remove it from our list and tell php debugger to do
+        # so as well.
+        #
+        toRemoveList = []
+        for oneKey in self.bpDict.keys():
+            if tmpList.count(oneKey) == 0:
+                toRemoveList.append((oneKey, self.bpDict[oneKey]))
+                myprint("PushBreakpoints(): recently removed global breakpoint \'%s:%i\'", (oneKey[0], oneKey[1]))
+
+        for bp in toRemoveList:
+            bp[1].removeSelf()
+            del self.bpDict[bp[0]]
+            myprint("PushBreakpoints(): successfully removed breakpoint \'%s:%i\' from both our local list and php debugger", (bp[0][0], bp[0][1]))
+
+        return
+    #
+    #                      Public callback functions end
+    ############################################################################
+
+
+    def newConnEventHandler(self):
+        #
+        # Ok, we've got a connection from the php debugger, and some initial
+        # frame data from it.  Everything is ready and let's make some initial
+        # actions.
+        #
+        self.clearInternals()
+
+        try:
+            self.awaitAndHandleResponse(self.lsnrThr.getConnHeader())
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return
+
+        self.PushBreakpoints(True)
+        self.ui.LoadPHPFramesList(self.stackList)
+
+    #
+    # This could be called when this object is constructed or when self is
+    # re-initialized after getting a new dbg module connection as a new
+    # session.
+    #
+    def clearInternals(self):
+        self.stackList       = []
+        self.errStackList    = []
+        self.stackFrameIndex = 0
+        self.isErrStack      = False
+        self.errStr          = ''
+        self.modList         = []
+        self.stopOnError     = True
+        self.lastCommand     = None
+        self.evalRet         = ''
+        self.modDict         = {}
+        self.bpDict          = {}
+        self.rawDataDict     = {}
+        self.sessID          = 0
+        self.sessType        = 0
+        self.sessEnded       = False
+        self.frameCounter    = 1000
+        self.variableList    = []
+        self.verMajor        = 0
+        self.verMinor        = 0
+        self.verDesc         = None
+
+    def initLsnrThr(self):
+        self.actionEvent = threading.Event()
+        self.lsnrThr     = PHPDBGLsnrThr(self, self.lsnrHost, self.lsnrPort, self.actionEvent, self.ui)
+
+    def awaitAndHandleResponse(self, header = None, blocking = False, disable = True, stopping = False):
+        if disable:
+            self.ui.DisableWhileDebuggerRunning()
+
+        while self.readResponse(header, blocking) != 0:
+            myprint("Waiting for response")
+
+        if stopping:
+            self.ui.DisableAfterStop()
+        else:
+            self.ui.EnableWhileDebuggerStopped()
+
+    def requestDBGVersion(self):
+        #TODO: necessary?
+        pass
+
+    def getSourceTree(self):
+        #TODO: necessary?
+        pass
+
+    def addDBGModName(self):
+        #TODO: necessary?
+        pass
+
+    def getNextFrameCounter(self):
+        self.frameCounter = self.frameCounter + 1
+        return self.frameCounter
+
+    def getVariables(self, stack):
+        self.variableList = []
+
+        reqPacket = PHPDBGPacket(DBGA_REQUEST)
+        reqFrame  = PHPDBGFrame(FRAME_EVAL)
+
+        reqFrame.addInt(0)
+        reqFrame.addInt(stack.getFrameScopeId())
+        reqPacket.addFrame(reqFrame)
+        myprint("PHPDebuggerCallback::getVariables(): about to send eval request")
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.awaitAndHandleResponse(disable = False)
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return self.variableList
+
+        myprint("PHPDebuggerCallback::getVariables(): evalRet=%s", self.evalRet)
+        evalStr = PHPDBGEvalString(stack, self.evalRet)
+        if evalStr:
+            self.variableList = evalStr.getVars()
+        myprint("PHPDebuggerCallback::getVariables(): about to return")
+
+        return self.variableList
+
+    def evalBlock(self, stack, evalStr):
+        reqPacket = PHPDBGPacket(DBGA_REQUEST)
+        reqFrame1 = PHPDBGFrame(FRAME_EVAL)
+        reqFrame2 = PHPDBGFrame(FRAME_RAWDATA)
+
+        frameID = self.getNextFrameCounter()
+        reqFrame1.addInt(frameID)
+        reqFrame1.addInt(1)
+
+        reqFrame2.addInt(frameID)
+        reqFrame2.addInt(len(evalStr) + 1)
+        reqFrame2.addStr(evalString)
+
+        reqPacket.addFrame(reqFrame2)
+        reqPacket.addFrame(reqFrame1)
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.awaitAndHandleResponse(disable = False)
+        except PHPDBGConnException:
+            self.currConnFinished()
+            return None
+
+        evalStr = PHPDBGEvalString(stack, self.evalRet)
+
+        return evalStr.getVars()
+
+    def getBPUnderHit(self):
+        for bp in self.bpDict.values():
+            if bp.isUnderHit():
+                return bp
+
+        return None
+
+    def getRawFrameData(self, frameNo):
+        if self.rawDataDict.has_key(frameNo):
+            #
+            # Once the frameData is consumed, remove it from rawDataDict.
+            #
+            return self.rawDataDict.pop(frameNo)
+        else:
+            #
+            # TODO: do we need to handle the case when the raw frame data hasn't
+            # been received before?
+            #
+            return None
+
+    def getModByNum(self, modNum):
+        if self.modDict.has_key(modNum):
+            return self.modDict[modNum]
+        else:
+            return None
+
+    def getModByFileName(self, fileName):
+        for mn, fn in self.modDict.iteritems():
+            if fn == fileName:
+                return mn
+
+        return 0
+
+    def setMod(self, modNum, fileName):
+        if modNum != 0 and fileName:
+            self.modDict[modNum] = fileName
+
+        return
+
+    def readResponse(self, headeri = None, blockingi = False):
+        inHeader        = headeri
+        header          = None
+        cmdReceived     = 0
+        isFirstPacket   = True
+        blocking        = blockingi
+        self.isErrStack = False
+        self.rawDataDict.clear()
+
+        while True:
+            #
+            # If we have already received the first packet, we can't block any
+            # more.
+            #
+            if not isFirstPacket:
+                blocking = False
+
+            #
+            # If this is the first loop and we have a non-empty header passed in, use it.  Otherwise,
+            # read in a new header.  For subsequent loops, inHeader is None so we always read a new
+            # header from the wire.
+            #
+            if inHeader:
+                header   = inHeader
+                inHeader = None
+            else:
+                header   = ResponseHeader(self.lsnrThr, blocking)
+
+            if not header.isValid:
+                return 0
+
+            cmdReceived = header.command
+            frame       = ResponsePacketFrame(self.lsnrThr, header.toRead, None, blocking)
+            if not frame.isValid:
+                return 0
+
+            isFirstPacket     = False
+            isFirstStackFrame = True
+            while frame and frame.isValid:
+                frameName = frame.frameName
+                if frameName == FRAME_STACK:
+                    if self.isErrStack:
+                        self.errStackList = self.handleRespFrameStack(self.errStackList, frame, isFirstStackFrame)
+                    else:
+                        self.stackList = self.handleRespFrameStack(self.stackList, frame, isFirstStackFrame)
+
+                    if isFirstStackFrame:
+                        isFirstStackFrame = False
+                elif frameName == FRAME_SOURCE:
+                    self.handleRespFrameSource(frame)
+                elif frameName == FRAME_SRC_TREE:
+                    self.handleRespFrameSrcTree(frame)
+                elif frameName == FRAME_RAWDATA:
+                    self.handleRespFrameRawdata(frame)
+                elif frameName == FRAME_ERROR:
+                    self.handleRespFrameError(frame)
+                elif frameName == FRAME_EVAL:
+                    self.handleRespFrameEval(frame)
+                elif frameName == FRAME_BPS:
+                    self.handleRespFrameBps(frame)
+                elif frameName == FRAME_BPL:
+                    self.handleRespFrameBpl(frame)
+                elif frameName == FRAME_VER:
+                    self.handleRespFrameVer(frame)
+                elif frameName == FRAME_SID:
+                    self.handleRespFrameSid(frame)
+                elif frameName == FRAME_SRCLINESINFO:
+                    self.handleRespFrameSrclinesinfo(frame)
+                elif frameName == FRAME_SRCCTXINFO:
+                    self.handleRespFrameSrcctxinfo(frame)
+                elif frameName == FRAME_LOG:
+                    self.handleRespFrameLog(frame)
+                elif frameName == FRAME_PROF:
+                    self.handleRespFrameProf(frame)
+                elif frameName == FRAME_PROF_C:
+                    self.handleRespFrameProfC(frame)
+                elif frameName == FRAME_SET_OPT:
+                    self.handleRespFrameSetOpt(frame)
+                else:
+                    self.handleRespFrameUnknown(frame)
+                    return 0
+
+                #
+                # After handling of this frame, force frame to point to the
+                # next one based on current frame's absolute size.
+                #
+                frame = frame.getNextFrame(True)
+
+            if cmdReceived == DBGC_REPLY:
+                self.handleRespCmdReply()
+            elif cmdReceived == DBGC_STARTUP:
+                self.handleRespCmdStartup()
+            elif cmdReceived == DBGC_END:
+                self.handleRespCmdEnd()
+            elif cmdReceived == DBGC_BREAKPOINT:
+                self.handleRespCmdBreakpoint()
+                cmdReceived = 0
+            elif cmdReceived == DBGC_STEPINTO_DONE:
+                self.handleRespCmdStepintoDone()
+            elif cmdReceived == DBGC_STEPOVER_DONE:
+                self.handleRespCmdStepoverDone()
+            elif cmdReceived == DBGC_STEPOUT_DONE:
+                self.handleRespCmdStepoutDone()
+            elif cmdReceived == DBGC_EMBEDDED_BREAK:
+                self.handleRespCmdEmbeddedBreak()
+            elif cmdReceived == DBGC_PAUSE:
+                self.handleRespCmdPause()
+            elif cmdReceived == DBGC_ERROR:
+                self.handleRespCmdError()
+            elif cmdReceived == DBGC_LOG:
+                self.handleRespCmdLog()
+            elif cmdReceived == DBGC_SID:
+                self.handleRespCmdSid()
+            else:
+                self.handleRespCmdUnknown()
+
+        return cmdReceived
+
+    def handleRespFrameStack(self, stackList, frame, isFirst):
+        if isFirst:
+            stackList            = []
+            self.stackFrameIndex = 0
+
+        lineNo  = frame.getNextInt()
+        modNo   = frame.getNextInt()
+        scopeId = frame.getNextInt()
+        frameId = frame.getNextInt()
+        if modNo != 0:
+            newStackFrame = PHPStackFrame(self, self.getModByNum(modNo), lineNo, self.stackFrameIndex, scopeId, self.getRawFrameData(frameId), modNo)
+            stackList.append(newStackFrame)
+            self.stackFrameIndex = self.stackFrameIndex + 1
+
+        return stackList
+
+    def handleRespFrameSource(self, frame):
+        modNo           = frame.getNextInt()
+        fromFilePos     = frame.getNextInt()
+        error           = frame.getNextInt()
+        fullSize        = frame.getNextInt()
+        fileNameFrameId = frame.getNextInt()
+        textFrameId     = frame.getNextInt()
+
+        fileName = self.getModByNum(modNo)
+        if not fileName:
+            self.setFileMod(modNo, fileNameFrameId)
+
+        #
+        # TODO: fullSize string and textFrameId are not handled here.
+        #
+        return
+
+    def handleRespFrameSrcTree(self, frame):
+        parentModNo     = frame.getNextInt()
+        parentLineNo    = frame.getNextInt()
+        modNo           = frame.getNextInt()
+        fileNameFrameId = frame.getNextInt()
+
+        fileName = self.getModByNum(modNo)
+        if not fileName:
+            self.setFileMod(modNo, fileNameFrameId)
+
+        return
+
+    def handleRespFrameRawdata(self, frame):
+        frameNo = frame.getNextInt()
+        if frameNo > 0:
+            toRead = frame.getNextInt()
+            if toRead > 0:
+                str = frame.getNextString(toRead)
+                self.rawDataDict[frameNo] = str
+                myprint("handleRespFrameRawdata(): added \'%d\'=\'%s\' to rawDataDict.", (frameNo, str))
+
+        return
+
+    def handleRespFrameError(self, frame):
+        self.isErrStack = True
+
+        #
+        # Type of the error.
+        #
+        errInt0         = frame.getNextInt()
+        #
+        # ID of error message.
+        #
+        errInt1         = frame.getNextInt()
+
+        if errInt0 == E_ERROR:
+            errIdStr = "[Error]"
+        elif errInt0 == E_WARNING:
+            errIdStr = "[Warning]"
+        elif errInt0 == E_PARSE:
+            errIdStr = "[Parse Error]"
+        elif errInt0 == E_NOTICE:
+            errIdStr = "[Notice]"
+        elif errInt0 == E_CORE_ERROR:
+            errIdStr = "[Core Error]"
+        elif errInt0 == E_CORE_WARNING:
+            errIdStr = "[Core Warning]"
+        elif errInt0 == E_COMPILE_ERROR:
+            errIdStr = "[Compile Error]"
+        elif errInt0 == E_COMPILE_WARNING:
+            errIdStr = "[Compile Warning]"
+        elif errInt0 == E_USER_ERROR:
+            errIdStr = "[User Error]"
+        elif errInt0 == E_USER_WARNING:
+            errIdStr = "[User Warning]"
+        elif errInt0 == E_USER_NOTICE:
+            errIdStr = "[User Notice]"
+        else:
+            errIdStr = "[Unexpected Error]"
+
+        errMsg = self.getRawFrameData(errInt1)
+        if errMsg and len(errMsg) > 0:
+            self.errStr = errIdStr + ": " + errMsg + "\n"
+        else:
+            self.errStr = errIdStr + ": <Invalid Error Message>\n"
+
+        if not self.stopOnError:
+            if self.lastCommand == DBGA_CONTINUE:
+                self.Continue()
+            elif self.lastCommand == DBGA_STEPINTO:
+                self.SingleStep()
+            elif self.lastCommand == DBGA_STEPOUT:
+                self.Return()
+            elif self.lastCommand == DBGA_STEPOVER:
+                self.Next()
+
+        return
+
+    def handleRespFrameEval(self, frame):
+        evalInt0        = frame.getNextInt()
+        evalInt1        = frame.getNextInt()
+        evalInt2        = frame.getNextInt()
+        self.evalRet    = self.getRawFrameData(evalInt1)
+        #TODO: is the following necessary?
+        evalStr         = self.getRawFrameData(evalInt0)
+
+        return
+
+    def handleRespFrameBps(self, frame):
+        return
+
+    def handleRespFrameBpl(self, frame):
+        #
+        # Get this breakpoint.
+        #
+        dbgBp = []
+        for i in range(10):
+            dbgBp.append(frame.getNextInt())
+
+        if dbgBp[2] != 0:
+            #
+            # If filename is sent, get it from the rawDataDict.
+            #
+            fileName = self.getRawFrameData(dbgBp[2])
+            if not fileName:
+                return
+
+            #
+            # If this filename comes with a mod number, store this
+            # modNum/fileName into this session's modDict.  Notice it might
+            # overwrite previous value.
+            #
+            if dbgBp[0] != 0:
+                self.setMod(dbgBp[0], fileName)
+        elif dbgBp[0] != 0:
+            #
+            # Use modNum to get the fileName.
+            #
+            fileName = self.getModByNum(dbgBp[0])
+            if not fileName:
+                return
+        else:
+            #
+            # Couldn't get the filename; nothing we can do with.
+            #
+            return
+
+        bpKey = self.createBpKey(fileName, dbgBp[1])
+        if not self.bpDict.has_key(bpKey):
+            #
+            # Not in our bp list?  Anyway, create one for it.
+            #
+            ourBp              = BreakPoint(self, fileName, dbgBp[1], dbgBp[0], dbgBp[3], dbgBp[4], dbgBp[5], dbgBp[6], dbgBp[7], dbgBp[8], dbgBp[9])
+            self.bpDict[bpKey] = ourBp
+            newlyCreated       = True
+        else:
+            ourBp              = self.bpDict[bpKey]
+            newlyCreated       = False
+
+        #
+        # Update with the latest bp information.
+        #
+        if not newlyCreated:
+            ourBp.update(dbgBp)
+
+        return
+
+    def handleRespFrameVer(self, frame):
+        self.verMajor = frame.getNextInt()
+        self.verMinor = frame.getNextInt()
+        verFrameNo    = frame.getNextInt()
+        self.verDesc  = self.getRawFrameData(verFrameNo)
+        myprint("respFrameVer: verMajor=%s, verMinor=%s, verDesc=%s", (repr(self.verMajor), repr(self.verMinor), repr(self.verDesc)))
+
+        return
+
+    def handleRespFrameSid(self, frame):
+        self.sessID   = frame.getNextInt()
+        self.sessType = frame.getNextInt()
+        myprint("respFrameSid: sessID=%s, sessType=%s", (self.sessID, self.sessType))
+
+        return
+
+    def handleRespFrameSrclinesinfo(self, frame):
+        return
+
+    def handleRespFrameSrcctxinfo(self, frame):
+        return
+
+    def handleRespFrameLog(self, frame):
+        #
+        # TODO:
+        # Now we don't do much here besides following the protocol to retrieve
+        # the data.
+        #
+        logId           = frame.getNextInt()
+        logType         = frame.getNextInt()
+        modNo           = frame.getNextInt()
+        lineNo          = frame.getNextInt()
+        fileNameFrameId = frame.getNextInt()
+        extInfo         = frame.getNextInt()
+
+        fileName = self.getModByNum(modNo)
+        if not fileName:
+            self.setFileMod(modNo, fileNameFrameId)
+
+        return
+
+    def handleRespFrameProf(self, frame):
+        return
+
+    def handleRespFrameProfC(self, frame):
+        return
+
+    def handleRespFrameSetOpt(self, frame):
+        return
+
+    def handleRespCmdReply(self):
+        return
+
+    def handleRespCmdStartup(self):
+        return
+
+    def handleRespCmdEnd(self):
+        self.sessEnded = True
+        return
+
+    def handleRespCmdBreakpoint(self):
+        return
+
+    def handleRespCmdStepintoDone(self):
+        return
+
+    def handleRespCmdStepoverDone(self):
+        return
+
+    def handleRespCmdStepoutDone(self):
+        return
+
+    def handleRespCmdEmbeddedBreak(self):
+        return
+
+    def handleRespCmdPause(self):
+        return
+
+    def handleRespCmdError(self):
+        self.stackList = []
+
+        if len(self.errStackList) > 0:
+            self.errStr = self.errStr + "Stack Trace:\n"
+
+        while len(self.errStackList) > 0:
+            oneStack = self.errStackList.pop()
+            self.errStr = self.errStr + "%s\n" % oneStack.getLongDisplayStr()
+
+        self.ui.showErrorDialog(self.errStr, "PHP Error")
+        myprint("Got PHP Error:\n%s", self.errStr)
+
+        return
+
+    def handleRespCmdLog(self):
+        return
+
+    def handleRespCmdSid(self):
+        return
+
+    def setFileMod(self, modNo, fileNameFrameId):
+        if fileNameFrameId != 0:
+            fileName = self.getRawFrameData(fileNameFrameId)
+            if fileName and modNo != 0:
+                self.setMod(modNo, fileName)
+
+        return
+
+    def createBpKey(self, fileName, lineNo):
+        #
+        # This is to work around a bug in dbg module where it changes the path
+        # names that we pass to it to lower cases.
+        #
+        if sysutils.isWindows():
+            fileName = fileName.lower()
+
+        return (fileName, lineNo)
+
+    def setLsnrAction(self, actioni):
+        self.lsnrAction = actioni
+        return
+
+    def getLsnrAction(self):
+        return self.lsnrAction
+
+    def currConnFinished(self):
+        self.clearInternals()
+        self.setLsnrAction(PHPDebuggerCallback.ACTION_LISTEN)
+        self.actionEvent.set()
+        self.ui.DisableAfterStop()
+
+    def stopPhpDbg(self):
+        #
+        # TODO: should send a request to stop the current running PHP program.
+        #       should handle network blocking issue correctly, otherwise, we
+        #       might hang here.
+        #
+        reqPacket = PHPDBGPacket(DBGA_STOP)
+
+        try:
+            reqPacket.sendPacket(self.lsnrThr)
+            self.awaitAndHandleResponse(stopping = True)
+        except PHPDBGConnException:
+            pass
+
+        self.currConnFinished()
+        return
+
+    def stopLsnr(self):
+        if not self.lsnrThr:
+            return
+
+        #
+        # Then we try to stop our listener thread.
+        #
+        if self.lsnrThr.hasBeenConnected():
+            #
+            # If the listener thread has already accepted a connection from a
+            # php debug module/client, it is sleeping now and wait for this
+            # condition object to be set so that it can exit.
+            #
+            self.setLsnrAction(PHPDebuggerCallback.ACTION_STOP)
+            self.actionEvent.set()
+        else:
+            #
+            # If the listener thread has never been connected from a php debug
+            # module/client, it is still blocking on a accept() call.  We
+            # connect to it here and send a special shutdown command asking it
+            # to exit.
+            #
+            shutdownMessage = IntToC4(DBG_SYNC) + IntToC4(DBGC_AG_SHUTDOWN_REQ) + IntToC4(0) + IntToC4(0)
+            tempSocket      = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            try:
+                tempSocket.connect((self.lsnrHost, self.lsnrPort))
+                tempSocket.sendall(shutdownMessage)
+            except:
+                myprint("shutdown connection/send message got exception!")
+
+            tempSocket.close()
+
+        self.lsnrThr.join()
+        self.lsnrThr = None
+
+
+class PHPDBGLsnrThr(threading.Thread):
+    def __init__(self, interfacei, hosti, porti, actionEventi, uii):
+        threading.Thread.__init__(self)
+        self.interface          = interfacei
+        self.svrHost            = hosti
+        self.svrPort            = porti
+        self.actionEvent        = actionEventi
+        self.svrSocket          = None
+        self.clntConn           = None
+        self.clntAddr           = None
+        self.nonBlockingTimeout = 1
+        self.connHeader         = None
+        self.ui                 = uii
+
+    def initSvrSocket(self):
+        self.svrSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.svrSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        self.svrSocket.bind((self.svrHost, self.svrPort))
+
+    def waitForClntConn(self):
+        self.svrSocket.listen(5)
+        self.clntConn, self.clntAddr = self.svrSocket.accept()
+        self.clntConn.settimeout(self.nonBlockingTimeout)
+
+    def run(self):
+        #
+        # Initialize this server socket.
+        #
+        self.initSvrSocket()
+
+        while True:
+            #
+            # Block until we get a new connection from a php debug client or our
+            # debugger ui (with a special shutting down header/command).
+            #
+            self.waitForClntConn()
+    
+            #
+            # Ok, a new connection comes in ...  Read the header to see where it
+            # comes from.
+            #
+            self.connHeader = ResponseHeader(self)
+            if self.connHeader.command == DBGC_AG_SHUTDOWN_REQ:
+                #
+                # This is a special command coming from our UI asking this
+                # thread to exit.  This only happens if after this thread has
+                # been waiting for new connections from PHP debug module, no one
+                # connects, and UI is ready to shutdown this thread.
+                #
+                self.shutdown()
+                break
+            else:
+                #
+                # Tell the main gui thread to handle this new connection.
+                #
+                wx.CallAfter(self.interface.newConnEventHandler)
+    
+                #
+                # From now on, PHPDebuggerCallback will communicate with the php
+                # debug module using this thread's clntConn socket.  This thread
+                # itself will keep sleeping until get notified to make some
+                # actions.
+                #
+                self.actionEvent.wait()
+                self.actionEvent.clear()
+
+                action = self.interface.getLsnrAction()
+                if action == PHPDebuggerCallback.ACTION_STOP:
+                    self.shutdown()
+                    break
+                elif action == PHPDebuggerCallback.ACTION_LISTEN:
+                    if self.clntConn:
+                        self.clntConn.shutdown(socket.SHUT_RDWR)
+                        self.clntConn.close()
+                        self.clntConn = None
+
+                    continue
+                else:
+                    continue
+
+    def shutdown(self):
+        #
+        # Cleanup and ready to exit.
+        #
+        self.clntConn.shutdown(socket.SHUT_RDWR)
+        self.clntConn.close()
+        self.svrSocket.close()
+
+    def recv(self, size, blocking = False):
+        if self.clntConn:
+            myprint("recv: trying to receive %d bytes of data ...", size)
+            if blocking:
+                self.clntConn.settimeout(None)
+            else:
+                self.clntConn.settimeout(self.nonBlockingTimeout)
+
+            try:
+                rData = self.clntConn.recv(size)
+            except socket.timeout:
+                myprint("recv: got timed out")
+                rData = None
+            except:
+                myprint("recv: got an unexpected exception: %s", sys.exc_info()[0])
+                raise PHPDBGConnException
+
+            return rData
+        else:
+            raise PHPDBGConnException
+
+    def sendall(self, message):
+        if self.clntConn:
+            try:
+                self.clntConn.sendall(message)
+            except:
+                myprint("sendall: got an unexpected exception: %s", sys.exc_info()[0])
+                raise PHPDBGConnException
+        else:
+            raise PHPDBGConnException
+
+    def hasBeenConnected(self):
+        return self.clntConn != None
+
+    def getConnHeader(self):
+        return self.connHeader
+
+
+class PHPValue(object):
+    PEV_NAMES           = ("undefined", "long", "double", "string", "array", "object", "boolean", "resource", "reference", "soft reference", "null")
+    PEVT_UNKNOWN        = 0
+    PEVT_LONG           = 1
+    PEVT_DOUBLE         = 2
+    PEVT_STRING         = 3
+    PEVT_ARRAY          = 4
+    PEVT_OBJECT         = 5
+    PEVT_BOOLEAN        = 6
+    PEVT_RESOURCE       = 7
+    PEVT_REF            = 8
+    PEVT_SOFTREF        = 9
+    PEVT_NULL           = 10
+
+    NULL_VALUE_STR      = "NULL"
+    TRUE_VALUE_STR      = "True"
+    FALSE_VALUE_STR     = "False"
+    OBJECT_VALUE_STR    = "<%s> object"
+    STRING_VALUE_STR    = "\"%s\""
+    REFERENCE_VALUE_STR = "<reference><%s>"
+    RESOURCE_VALUE_STR  = "<%s><%s>"
+
+    def __init__(self, frame, type, valueList):
+        self.fStackFrame = frame
+        self.fValueType  = type
+
+        if type == self.PEVT_OBJECT:
+            self.fValueString = self.OBJECT_VALUE_STR % valueList[0]
+            self.fVariables   = valueList[1:]
+        elif type == self.PEVT_ARRAY:
+            self.fValueString = ''
+            self.fVariables   = valueList
+        else:
+            self.fVariables = []
+            if type == self.PEVT_STRING:
+                self.fValueString = self.STRING_VALUE_STR % valueList[0]
+            elif type == self.PEVT_NULL:
+                self.fValueString = self.NULL_VALUE_STR
+            elif type == self.PEVT_BOOLEAN:
+                if valueList[0] == "0":
+                    self.fValueString = self.FALSE_VALUE_STR
+                else:
+                    self.fValueString = self.TRUE_VALUE_STR
+            elif type == self.PEVT_REF or type == self.PEVT_SOFTREF:
+                self.fValueString = self.REFERENCE_VALUE_STR % valueList[0]
+            elif type == self.PEVT_RESOURCE:
+                self.fValueString = self.RESOURCE_VALUE_STR % (valueList[0], valueList[1])
+            else:
+                self.fValueString = valueList[0]
+
+    def addVariable(self, item):
+        if item != None:
+            self.fVariables.append(item)
+
+        return self.fVariables
+
+    def setParent(self, parent):
+        if self.fVariables != None and len(self.fVariables) > 0:
+            for item in self.fVariables:
+                item.setParent(parent)
+
+    def getReferenceType(self):
+        return self.fValueType
+
+    def getReferenceTypeName(self):
+        return self.PEV_NAMES[self.fValueType]
+
+    def setReferenceType(self, type):
+        self.fValueType = type
+
+    def getValueString(self):
+        return self.fValueString
+
+    def getChildrenVariables(self):
+        return self.fVariables
+
+    def hasVariables(self):
+        return len(self.fVariables) > 0
+
+    def childrenIsSortable(self):
+        #
+        # TODO: if self.fValueType != self.PEVT_ARRAY:
+        #
+        return True
+
+
+class PHPVariable(object):
+    def __init__(self, frame, parent, valueType, name, valueList):
+        self.fStackFrame = frame
+        self.fName       = None
+        self.fLongName   = None
+        self.fPureName   = None
+        self.fValue      = PHPValue(frame, valueType, valueList)
+        self.fParent     = parent
+        self.setName(name)
+        self.setChildrensParent(valueList)
+
+    def setName(self, name):
+        self.fPureName = name
+
+        type = self.getReferenceType()
+        if type == PHPValue.PEVT_ARRAY:
+            numItems = len(self.getChildrenVariables())
+            self.fName = name + "[" + str(numItems) + "]"
+        else:
+            self.fName = name
+
+        if not self.fParent or self.fParent.getName() == None:
+            self.fLongName = name
+        else:
+            self.setLongName()
+
+        return
+
+    def setLongName(self):
+        parentType = self.fParent.getReferenceType()
+        if parentType == PHPValue.PEVT_ARRAY:
+            self.fLongName = self.fParent.getLongName() + "['" + self.fPureName + "']"
+        elif parentType == PHPValue.PEVT_OBJECT:
+            self.fLongName = self.fParent.getLongName() + "." + self.fName
+        else:
+            self.fLongName = self.fName
+
+        return
+
+    def getValue(self):
+        return self.fValue
+
+    def getValueString(self):
+        return self.fValue.getValueString()
+
+    def getChildrenVariables(self):
+        return self.fValue.getChildrenVariables()
+
+    def getName(self):
+        return self.fName
+
+    def getParent(self):
+        return self.fParent
+
+    def setParent(self, parent):
+        self.fParent = parent
+        self.setLongName()
+        return
+
+    def setChildrensParent(self, childrenList):
+        if self.fValue.hasVariables():
+            for child in self.fValue.getChildrenVariables():
+                child.setParent(self)
+
+    def getLongName(self):
+        return self.fLongName
+
+    def getReferenceTypeName(self):
+        return self.fValue.getReferenceTypeName()
+
+    def getReferenceType(self):
+        return self.fValue.getReferenceType()
+
+    def setReferenceType(self, type):
+        tp = self.getValue.setReferenceType(type)
+        return tp
+
+    def setValue(self, expression):
+        if self.fValue.getReferenceType() == PHPValue.PEVT_STRING:
+            evalString = self.fLongName + "=\"" + expression + "\""
+        else:
+            evalString = self.fLongName + "=" + expression
+
+        vars = self.fStackFrame.getPHPDBGInterface().evalBlock(self.fStackFrame, evalString)
+        self.fValue = vars[0].fValue
+
+    def toString(self):
+        rtype = self.getReferenceType()
+        if rtype == PHPValue.PEVT_ARRAY:
+            elements = len(self.fValue.getChildrenVariables())
+            if elements == 0:
+                tmpStr = self.getName() + " [no elements]"
+            elif elements == 1:
+                tmpStr = self.getName() + " [1 element]"
+            else:
+                tmpStr = self.getName() + " [" + str(elements) + " elements]"
+        elif rtype == PHPValue.PEVT_OBJECT:
+            tmpStr = self.getName() + " [ class: " + self.fValue.getValueString() + "]"
+        elif rtype == PHPValue.PEVT_STRING:
+            tmpStr = self.getName() + " = \"" + self.fValue.getValueString() + "\""
+        else:
+            tmpStr = self.getName() + " = " + self.fValue.getValueString()
+
+        return tmpStr
+
+    def hasChildren(self):
+        return self.fValue.hasVariables()
+
+    def childrenIsSortable(self):
+        return self.fValue.childrenIsSortable()
+
+
+class PHPStackFrame(object):
+    def __init__(self, interface, file, line, frameIndex, scopeId, desc, modNum):
+        self.interface      = interface
+        self.fileName       = file
+        self.lineNo         = line
+        self.frameIndex     = frameIndex
+        self.scopeId        = scopeId
+        self.desc           = desc
+        self.modNum         = modNum
+        self.variables      = []
+        self.shortFileName  = None
+        self.shortDesc      = None
+        self.displayStr     = None
+        self.longDisplayStr = None
+
+        self._getFileNamesAndShortDesc()
+
+        myprint("PHPStackFrame::__init__(): new PHPStackFrame: file=%s, lineNo=%s, frameIndex=%s, scopeId=%s, desc=%s, modNum=%s, shortFileName=%s, shortDesc=%s", (repr(file), repr(line), repr(frameIndex), repr(scopeId), repr(desc), repr(modNum), repr(self.shortFileName), repr(self.shortDesc)))
+
+    def _getFileNamesAndShortDesc(self):
+        tmp = []
+        if self.desc:
+            tmp = self.desc.split("::")
+
+        if self.fileName:
+            self.shortFileName = os.path.basename(self.fileName)
+            if len(tmp) == 2:
+                self.shortDesc = tmp[1]
+            elif len(tmp) == 1:
+                self.shortDesc = tmp[0]
+            else:
+                self.shortDesc = None
+
+            return
+
+        #
+        # The fileName is None, we will try our best efforts to get it.
+        #
+        if len(tmp) == 2:
+            #
+            # We retrieved long finename from the description.  If we haven't
+            # stored the file mod before, set this one as the new one.
+            # Otherwise, we prefer to keep the stored one.
+            #
+            if self.modNum != 0:
+                storedFileName = self.interface.getModByNum(self.modNum)
+                if not storedFileName:
+                    self.interface.setMod(self.modNum, tmp[0])
+
+            self.fileName      = tmp[0]
+            self.shortFileName = os.path.basename(tmp[0])
+            self.shortDesc     = tmp[1]
+        elif len(tmp) == 1:
+            self.fileName      = None
+            self.shortFileName = None
+            self.shortDesc     = tmp[0]
+        else:
+            self.shortFileName = None
+            self.shortDesc     = None
+            myprint("PHPStackFrame::_getFileNamesAndShortDesc(): something wrong with desc: %s?", self.desc)
+
+        return
+
+    def getShortFileName(self):
+        return self.shortFileName
+
+    def setShortFileName(self, shortFileName):
+        self.shortFileName = shortFileName
+
+    def getShortDesc(self):
+        return self.shortDesc
+
+    def getLineNo(self):
+        return self.lineNo
+
+    def getInterface(self):
+        return self.interface
+
+    def getVariables(self):
+        if len(self.variables) == 0:
+            self.variables = self.interface.getVariables(self)
+
+        return self.variables
+
+    def findVariables(self, s):
+        if self.hasVariables():
+            name = "$" + s
+            for var in self.variables:
+                if var.getName() == name:
+                    return var
+
+        return None
+
+    def hasVariables(self):
+        if len(self.variables) == 0:
+            return False
+        else:
+            return True
+
+    def getName(self):
+        if self.getDesc():
+            return self.getDesc() + " [line: " + str(self.getLineNo()) + "]"
+        else:
+            return self.getFileName() + " [line: " + str(self.getLineNo()) + "]"
+
+    def getFileName(self):
+        return self.fileName
+
+    def setFileName(self, fileName):
+        self.fileName = fileName
+
+    def setDesc(self, desc):
+        self.desc = desc
+
+    def getDesc(self):
+        return self.desc
+
+    def getFrameScopeId(self):
+        return self.scopeId
+
+    def getFrameIndex(self):
+        return self.frameIndex
+
+    def getDisplayStr(self, stackList = None):
+        if self.displayStr:
+            return self.displayStr
+
+        if not self.shortFileName:
+            if stackList:
+                i = stackList.index(self)
+                for j in range(i + 1, len(stackList)):
+                    self.shortFileName = stackList[j].getShortFileName()
+                    if self.shortFileName:
+                        self.fileName = stackList[j].getFileName()
+
+        if self.shortFileName:
+            if self.shortDesc:
+                self.displayStr = "<%s> at %s:%d" % (self.shortDesc, self.shortFileName, self.lineNo)
+            else:
+                self.displayStr = "%s:%d" % (self.shortFileName, self.lineNo)
+        else:
+            if self.shortDesc:
+                self.displayStr = "<%s>" % self.shortDesc
+            else:
+                self.displayStr = "<internal stack error>"
+
+        return self.displayStr
+
+    def getLongDisplayStr(self):
+        if self.longDisplayStr:
+            return self.longDisplayStr
+
+        if self.fileName:
+            if self.shortDesc:
+                self.longDisplayStr = "<%s> at %s:%d" % (self.shortDesc, self.fileName, self.lineNo)
+            else:
+                self.longDisplayStr = "%s:%d" % (self.fileName, self.lineNo)
+        else:
+            if self.shortDesc:
+                self.longDisplayStr = "<%s>" % self.shortDesc
+            else:
+                self.longDisplayStr = "<internal stack error>"
+
+        return self.longDisplayStr
+
+class BreakPoint(object):
+    def __init__(self, interface, fileName, lineNo, modNum = 0, state = BPS_ENABLED + BPS_UNRESOLVED, isTemp = 0, hitCount = 0, skipHits = 0, condition = 0, bpId = 0, isUnderHit = 0):
+        self.interface  = interface
+        self.fileName   = fileName
+        self.lineNo     = lineNo
+        self.bpID       = bpId
+        self.state      = state
+        self.isTemp     = isTemp
+        self.hitCount   = hitCount
+        self.skipHits   = skipHits
+        self.condition  = condition
+        self.isUnderHit = 0
+        if modNum == 0:
+            self.modNum = self.interface.getModByFileName(fileName)
+        else:
+            self.modNum = modNum
+
+        if self.modNum:
+            self.fCounterOrZero = 0
+        else:
+            self.fCounterOrZero = interface.getNextFrameCounter()
+
+    def sendSelf(self):
+        reqPacket = PHPDBGPacket(DBGA_REQUEST)
+        reqFrame1 = PHPDBGFrame(FRAME_BPS)
+
+        if self.modNum:
+            reqFrame1.addInt(self.modNum)
+        else:
+            #
+            # 0 in modNum to tell to use fileName instead.
+            #
+            reqFrame1.addInt(0)
+
+        reqFrame1.addInt(self.lineNo)             # lineNo
+        reqFrame1.addInt(self.fCounterOrZero)     # fileName frameCounter or 0
+        reqFrame1.addInt(self.state)              # state
+        reqFrame1.addInt(self.isTemp)             # isTemp
+        reqFrame1.addInt(self.hitCount)           # hitCount
+        reqFrame1.addInt(self.skipHits)           # skipHits
+        reqFrame1.addInt(self.condition)          # condition
+        reqFrame1.addInt(self.bpID)               # breakpoint sequence id
+        reqFrame1.addInt(self.isUnderHit)         # isUnderHit
+
+        if not self.modNum:
+            reqFrame2 = PHPDBGFrame(FRAME_RAWDATA)
+            reqFrame2.addInt(self.fCounterOrZero)
+            reqFrame2.addInt(len(self.fileName) + 1)
+            reqFrame2.addStr(self.fileName)
+            reqPacket.addFrame(reqFrame2)
+
+        reqPacket.addFrame(reqFrame1)
+
+        try:
+            reqPacket.sendPacket(self.interface.lsnrThr)
+            self.interface.awaitAndHandleResponse()
+        except PHPDBGConnException:
+            self.interface.currConnFinished()
+
+        return
+
+    def addSelf(self):
+        self.sendSelf()
+
+    def removeSelf(self):
+        self.state = BPS_DISABLED
+        self.sendSelf()
+
+    def isUnderHit(self):
+        return self.isUnderHit == 1
+
+    def update(self, dbgBp):
+        self.modNum     = dbgBp[0]
+        self.state      = dbgBp[3]
+        self.isTemp     = dbgBp[4]
+        self.hitCount   = dbgBp[5]
+        self.skipHits   = dbgBp[6]
+        self.condition  = dbgBp[7]
+        self.bpID       = dbgBp[8]
+        self.isUnderHit = dbgBp[9]
+
+
+class PHPDBGEvalString(object):
+    def __init__(self, stackFrame, dataStr):
+        self.stackFrame = stackFrame
+        self.dataStr    = dataStr
+
+    #
+    # Get a list of variables under self.stackFrame.
+    #
+    def getVars(self):
+        return self.parseAVariable(isRealVar = False)
+
+    #
+    # if isRealVar:
+    #    returnList[0] = The Variable
+    # else:
+    #    returnList    = list of variables.
+    #
+    def parseAVariable(self, isRealVar = True):
+        returnList = []
+
+        #
+        # Get the variable name first.  Notice we ignore this entity's data
+        # type here.
+        #
+        if isRealVar:
+            nameEntity = self.parseAnEntity()
+            if not nameEntity or len(nameEntity) != 2 or type(nameEntity[1]) != str:
+                myprint("PHPDBGEvalStr::parseAVariable() got a wrong name entity")
+                return returnList
+            else:
+                varName = nameEntity[1]
+
+        #
+        # Get the variable's value.
+        #
+        valueEntity = self.parseAnEntity()
+        if not valueEntity or len(valueEntity) < 1:
+            myprint("PHPDBGEvalStr::parseAVariable(): couldn't get a variable's value entity.")
+            return returnList
+
+        #
+        # This variable's data type.
+        #
+        varType = valueEntity[0]
+
+        if isRealVar:
+            #
+            # If this is a real variable, return a list which contains only
+            # this variable item.
+            #
+            #valueEntity = valueEntity[1:]
+            variable    = PHPVariable(self.stackFrame, None, varType, varName, valueEntity[1:])
+            #myprint("xxxxCreated variable varName=%s, valueEntity=%s", (repr(varName), repr(valueEntity[1])))
+            myprint("xxxxCreated variable: %s", repr(variable.toString()))
+            returnList.append(variable)
+        else:
+            #
+            # If this is a root variable container, returns a list of
+            # variables under the root.  Do a sanity check here.
+            #
+            if valueEntity[0] != PHPValue.PEVT_ARRAY:
+                myprint("PHPDBGEvalStr::parseAVariable(): failed to parse the root variable container.")
+            else:
+                returnList = valueEntity[1:]
+
+        return returnList
+
+    #
+    # An entity could be a variable's name or its value.
+    #
+    # returnList[0]  = variable data type
+    # returnList[1:] = the real list
+    #
+    def parseAnEntity(self):
+        if not self.dataStr or len(self.dataStr) < 2  or (self.dataStr[1] != ':' and self.dataStr[1] != ';'):
+            myprint("PHPDBGEvalStr::parseAnEntity(): failed to parse %s.", repr(self.dataStr))
+            return None
+
+        returnList    = []
+        typeChar      = self.dataStr[0]
+        self.dataStr  = self.dataStr[2:]
+        if typeChar   == 'i':
+            returnList.append(PHPValue.PEVT_LONG)
+            self.parseInt(returnList)
+        elif typeChar == 'a':
+            returnList.append(PHPValue.PEVT_ARRAY)
+            self.parseArray(returnList)
+        elif typeChar == 's':
+            returnList.append(PHPValue.PEVT_STRING)
+            self.parseString(returnList)
+        elif typeChar == 'O':
+            returnList.append(PHPValue.PEVT_OBJECT)
+            self.parseObject(returnList)
+        elif typeChar == 'r':
+            returnList.append(PHPValue.PEVT_SOFTREF)
+            self.parseReference(returnList, isSoftRef = True)
+        elif typeChar == 'R':
+            returnList.append(PHPValue.PEVT_REF)
+            self.parseReference(returnList, isSoftRef = False)
+        elif typeChar == 'b':
+            returnList.append(PHPValue.PEVT_BOOLEAN)
+            self.parseBoolean(returnList)
+        elif typeChar == 'd':
+            returnList.append(PHPValue.PEVT_DOUBLE)
+            self.parseDouble(returnList)
+        elif typeChar == 'z':
+            returnList.append(PHPValue.PEVT_RESOURCE)
+            self.parseResource(returnList)
+        elif typeChar == 'N':
+            returnList.append(PHPValue.PEVT_NULL)
+            self.parseNull(returnList)
+        else:
+            myprint("PHPDBGEvalStr::parseAnEntity(): unknown data type: %s", typeChar)
+
+        return returnList
+
+    def parseInt(self, returnList):
+        myprint("enter parseInt().")
+        returnList.append(self.getAnIntStr(';'))
+
+        return
+
+    def parseArray(self, returnList):
+        myprint("enter parseArray().")
+        #
+        # The shortest array is 'a:0:{}'.
+        #
+        if len(self.dataStr) < 4:
+            myprint("PHPDBGEvalStr::parseArray(): failed (1) to parse an array: %s.", repr(self.dataStr))
+            return
+
+        expectedNumItems = self.getAnInt(':')
+        if len(self.dataStr) < 2 or self.dataStr[0] != '{':
+            myprint("PHPDBGEvalStr::parseArray(): failed (3) to parse an array: %s.", repr(self.dataStr))
+            return
+
+        self.dataStr = self.dataStr[1:]
+        varList      = []
+        while self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] != '}':
+            tmpList = self.parseAVariable()
+            if not tmpList or len(tmpList) != 1 or not tmpList[0]:
+                myprint("PHPDBGEvalStr::parseArray(): failed (4) to parse an array.  dataStr=%s.", repr(self.dataStr))
+                break
+            else:
+                varList.append(tmpList[0])
+
+        if expectedNumItems != len(varList):
+            myprint("PHPDBGEvalStr::parseArray(): failed (5) expected no. of items=%d, but got %d", (expectedNumItems, len(varList)))
+
+        #
+        # An array should end with a '}'.
+        #
+        if self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] == '}':
+            self.dataStr = self.dataStr[1:]
+            returnList.extend(varList)
+        else:
+            myprint("PHPDBGEvalStr::parseArray(): failed (6) to parse an array.  dataStr=%s.", repr(self.dataStr))
+
+        myprint("parseArray() ends.")
+        return
+
+    def parseString(self, returnList, endChar = ';'):
+        myprint("enter parseString().")
+        #
+        # The shortest string is 's:<str_len>:"<str>"<endChar>'.
+        #
+        if len(self.dataStr) < 5:
+            myprint("PHPDBGEvalStr::parseString(): failed (1) to parse a string.  dataStr=%s.", repr(self.dataStr))
+            return
+
+        expectedStrLen = self.getAnInt(':')
+        if len(self.dataStr) < expectedStrLen + 3 or self.dataStr[0] != '"':
+            myprint("PHPDBGEvalStr::parseString(): failed (3) to parse a string.  dataStr=%s.", repr(self.dataStr))
+            return
+
+        strValue = self.dataStr[1:expectedStrLen + 1]
+        if self.dataStr[expectedStrLen + 1:expectedStrLen + 3] != '"' + endChar:
+            myprint("PHPDBGEvalStr::parseString(): failed (4) to parse a string.  dataStr=%s.", repr(self.dataStr))
+            return
+
+        #
+        # Skip the starting double quote, real string, ending double quote, and ending semicolon.
+        #
+        self.dataStr = self.dataStr[expectedStrLen + 3:]
+        returnList.append(strValue)
+
+        return
+
+    def parseObject(self, returnList):
+        #
+        # A simple sanity check.  The shortest object is:
+        # 'O:<class_name_len>:"<class_name>":<num_of_items>:{<list_of_items>}'
+        #
+        if not self.dataStr or len(self.dataStr) < 10:
+            myprint("PHPDBGEvalStr::parseObject(): failed (1) to parse an object: %s.", repr(self.dataStr))
+
+        #
+        # Get the class name in classNameList[0].
+        #
+        classNameList = []
+        self.parseString(classNameList, ':')
+
+        expectedNumItems = self.getAnInt(':')
+        if len(self.dataStr) < 2 or self.dataStr[0] != '{':
+            myprint("PHPDBGEvalStr::parseObject(): failed (2) to parse an object: %s.", repr(self.dataStr))
+            return
+
+        self.dataStr = self.dataStr[1:]
+        varList      = []
+        while self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] != '}':
+            tmpList = self.parseAVariable()
+            if not tmpList or len(tmpList) != 1 or not tmpList[0]:
+                myprint("PHPDBGEvalStr::parseObject(): failed (3) to parse an object.  dataStr=%s.", repr(self.dataStr))
+                break
+            else:
+                varList.append(tmpList[0])
+
+        if expectedNumItems != len(varList):
+            myprint("PHPDBGEvalStr::parseObject(): failed (4) expected no. of items=%d, but got %d", (expectedNumItems, len(varList)))
+
+        #
+        # An object should end with a '}'.
+        #
+        if self.dataStr and len(self.dataStr) > 0 and self.dataStr[0] == '}':
+            self.dataStr = self.dataStr[1:]
+            returnList.append(classNameList[0])
+            returnList.extend(varList)
+        else:
+            myprint("PHPDBGEvalStr::parseObject(): failed (6) to parse an object.  dataStr=%s.", repr(self.dataStr))
+
+        myprint("parseObject() ends.")
+        return
+
+    def parseReference(self, returnList, isSoftRef):
+        myprint("enter parseReference().")
+        intStr = self.getAnIntStr(';')
+        if intStr:
+            returnList.append(intStr)
+
+        return
+
+    def parseBoolean(self, returnList):
+        tmpBooleanStr = self.getAnIntStr(';')
+        returnList.append(tmpBooleanStr)
+
+        return
+
+    def parseDouble(self, returnList):
+        tmpStr = self.getAStrTillEndChar(';')
+        if tmpStr:
+            returnList.append(tmpStr)
+
+        return
+
+    def parseResource(self, returnList):
+        tmpList = []
+        self.parseString(tmpList, ':')
+
+        if len(tmpList) == 1:
+            returnList.extend(tmpList)
+        else:
+            return
+
+        resourceId = self.getAnIntStr(';')
+        if resourceId:
+            returnList.append(resourceId)
+
+        return
+
+    def parseNull(self, returnList):
+        return
+
+    def getAStrTillEndChar(self, endChar):
+        if len(self.dataStr) < 1:
+            myprint("PHPDBGEvalStr::getAStrTillEndChar(): no more data string to work with.")
+            return
+
+        i = self.findNextChar(self.dataStr, endChar)
+        if i == -1:
+            myprint("PHPDBGEvalStr::getAStrTillEndChar(): no double/float string supplied.")
+            return
+
+        tmpStr       = self.dataStr[:i]
+        self.dataStr = self.dataStr[i + 1:]
+
+        if self.isFloat(tmpStr):
+            return tmpStr
+        else:
+            myprint("PHPDBGEvalStr::getAStrTillEndChar(): parsing error.  tried to get an float number, but get %s.", tmpStr)
+            return None
+
+    def getAnInt(self, endChar):
+        tmpStr = self.getAnIntStr(endChar)
+        if tmpStr:
+            return int(tmpStr)
+        else:
+            return 0
+
+    def getAnIntStr(self, endChar):
+        if len(self.dataStr) == 0:
+            myprint("PHPDBGEvalStr::getAnIntStr(): no more data string to work with.")
+            return
+
+        i = self.findNextChar(self.dataStr, endChar)
+        if i == -1:
+            tmpStr = self.dataStr
+            self.dataStr = ''
+        else:
+            tmpStr = self.dataStr[:i]
+            self.dataStr = self.dataStr[i + 1:]
+
+        if self.isInt(tmpStr):
+            return tmpStr
+        else:
+            myprint("PHPDBGEvalStr::getAnIntStr(): parsing error.  tried to get an integer, but get %s.", tmpStr)
+            return None
+
+    def isInt(self, aStr):
+        try:
+            int(aStr)
+        except ValueError:
+            return False
+
+        return True
+
+    def isFloat(self, aStr):
+        try:
+            float(aStr)
+        except ValueError:
+            return False
+
+        return True
+
+    def findNextChar(self, aStr, aChar):
+        try:
+            index = aStr.index(aChar)
+        except ValueError:
+            index = -1
+
+        return index
index 5e78d118f676f14aef3f0eb22445c48dbdd9b2d8..840ad1fc4448ba6fdf0585f5053bdd2effa3c7c2 100644 (file)
@@ -17,6 +17,10 @@ import CodeEditor
 import OutlineService
 import os
 import re
 import OutlineService
 import os
 import re
+import FindInDirService
+import activegrid.util.appdirs as appdirs
+import activegrid.util.sysutils as sysutils
+_ = wx.GetTranslation
 
 
 class PHPDocument(CodeEditor.CodeDocument):
 
 
 class PHPDocument(CodeEditor.CodeDocument):
@@ -153,18 +157,52 @@ class PHPCtrl(CodeEditor.CodeCtrl):
 
     def __init__(self, parent, id=-1, style=wx.NO_FULL_REPAINT_ON_RESIZE):
         CodeEditor.CodeCtrl.__init__(self, parent, id, style)
 
     def __init__(self, parent, id=-1, style=wx.NO_FULL_REPAINT_ON_RESIZE):
         CodeEditor.CodeCtrl.__init__(self, parent, id, style)
-        self.SetLexer(wx.stc.STC_LEX_PHP)
+        self.SetLexer(wx.stc.STC_LEX_HTML)
         self.SetStyleBits(7)
         self.SetKeyWords(4, string.join(PHPKEYWORDS))
         self.SetProperty("fold.html", "1")
 
 
         self.SetStyleBits(7)
         self.SetKeyWords(4, string.join(PHPKEYWORDS))
         self.SetProperty("fold.html", "1")
 
 
+    def CreatePopupMenu(self):
+        FINDCLASS_ID = wx.NewId()
+        FINDDEF_ID = wx.NewId()
+        
+        menu = CodeEditor.CodeCtrl.CreatePopupMenu(self)
+
+        self.Bind(wx.EVT_MENU, self.OnPopFindDefinition, id=FINDDEF_ID)
+        menu.Insert(1, FINDDEF_ID, _("Find 'function'"))
+
+        self.Bind(wx.EVT_MENU, self.OnPopFindClass, id=FINDCLASS_ID)
+        menu.Insert(2, FINDCLASS_ID, _("Find 'class'"))
+
+        return menu
+        
+
+    def OnPopFindDefinition(self, event):
+        view = wx.GetApp().GetDocumentManager().GetCurrentView()
+        if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
+            pattern = view.GetCtrl().GetSelectedText().strip()
+            if pattern:
+                searchPattern = "function\s+%s" % pattern
+                wx.GetApp().GetService(FindInDirService.FindInDirService).FindInProject(searchPattern)
+
+
+    def OnPopFindClass(self, event):
+        view = wx.GetApp().GetDocumentManager().GetCurrentView()
+        if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
+            definition = "class\s+%s"
+            pattern = view.GetCtrl().GetSelectedText().strip()
+            if pattern:
+                searchPattern = definition % pattern
+                wx.GetApp().GetService(FindInDirService.FindInDirService).FindInProject(searchPattern)
+
+
     def CanWordWrap(self):
         return True
 
 
     def SetViewDefaults(self):
     def CanWordWrap(self):
         return True
 
 
     def SetViewDefaults(self):
-        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "PHP", hasWordWrap = True, hasTabs = True)
+        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "PHP", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetFontAndColorFromConfig(self):
 
 
     def GetFontAndColorFromConfig(self):
@@ -216,8 +254,110 @@ class PHPCtrl(CodeEditor.CodeCtrl):
 class PHPOptionsPanel(STCTextEditor.TextOptionsPanel):
 
     def __init__(self, parent, id):
 class PHPOptionsPanel(STCTextEditor.TextOptionsPanel):
 
     def __init__(self, parent, id):
-        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "PHP", label = "PHP", hasWordWrap = True, hasTabs = True)
-
+        wx.Panel.__init__(self, parent, id)
+        mainSizer = wx.BoxSizer(wx.VERTICAL)                
+
+        config = wx.ConfigBase_Get()
+
+        pathLabel = wx.StaticText(self, -1, _("PHP Executable Path:"))
+        path = config.Read("ActiveGridPHPLocation")
+        self._pathTextCtrl = wx.TextCtrl(self, -1, path, size = (150, -1))
+        self._pathTextCtrl.SetToolTipString(self._pathTextCtrl.GetValue())
+        self._pathTextCtrl.SetInsertionPointEnd()
+        choosePathButton = wx.Button(self, -1, _("Browse..."))
+        pathSizer = wx.BoxSizer(wx.HORIZONTAL)
+        HALF_SPACE = 5
+        SPACE = 10
+        pathSizer.Add(pathLabel, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.TOP, HALF_SPACE)
+        pathSizer.Add(self._pathTextCtrl, 1, wx.EXPAND|wx.LEFT|wx.TOP, HALF_SPACE)
+        pathSizer.Add(choosePathButton, 0, wx.ALIGN_RIGHT|wx.LEFT|wx.RIGHT|wx.TOP, HALF_SPACE)
+        wx.EVT_BUTTON(self, choosePathButton.GetId(), self.OnChoosePath)
+        mainSizer.Add(pathSizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, SPACE)
+
+        iniLabel = wx.StaticText(self, -1, _("php.ini Path:"))
+        ini = config.Read("ActiveGridPHPINILocation")
+        if not ini:
+            if sysutils.isRelease():
+                ini = os.path.normpath(os.path.join(appdirs.getSystemDir(), "php.ini"))
+            else:
+                tmp = self._pathTextCtrl.GetValue().strip()
+                if tmp and len(tmp) > 0:
+                    ini = os.path.normpath(os.path.join(os.path.dirname(tmp), "php.ini"))
+
+        self._iniTextCtrl = wx.TextCtrl(self, -1, ini, size = (150, -1))
+        self._iniTextCtrl.SetToolTipString(self._iniTextCtrl.GetValue())
+        self._iniTextCtrl.SetInsertionPointEnd()
+        chooseIniButton = wx.Button(self, -1, _("Browse..."))
+        iniSizer = wx.BoxSizer(wx.HORIZONTAL)
+        HALF_SPACE = 5
+        SPACE = 10
+        iniSizer.Add(iniLabel, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.TOP, HALF_SPACE)
+        iniSizer.Add(self._iniTextCtrl, 1, wx.EXPAND|wx.LEFT|wx.TOP, HALF_SPACE)
+        iniSizer.Add(chooseIniButton, 0, wx.ALIGN_RIGHT|wx.LEFT|wx.RIGHT|wx.TOP, HALF_SPACE)
+        wx.EVT_BUTTON(self, chooseIniButton.GetId(), self.OnChooseIni)
+        mainSizer.Add(iniSizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, SPACE)
+
+        self._otherOptions = STCTextEditor.TextOptionsPanel(self, -1, configPrefix = "PHP", label = "PHP", hasWordWrap = True, hasTabs = True, addPage=False, hasFolding=False)
+        #STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "PHP", label = "PHP", hasWordWrap = True, hasTabs = True)
+        mainSizer.Add(self._otherOptions, 0, wx.EXPAND|wx.BOTTOM, SPACE)
+
+        self.SetSizer(mainSizer)
+        parent.AddPage(self, _("PHP"))
+
+    def OnChoosePath(self, event):
+        defaultDir = os.path.dirname(self._pathTextCtrl.GetValue().strip())
+        defaultFile = os.path.basename(self._pathTextCtrl.GetValue().strip())
+        if wx.Platform == '__WXMSW__':
+            wildcard = _("Executable (*.exe)|*.exe|All|*.*")
+            if not defaultFile:
+                defaultFile = "php-cgi.exe"
+        else:
+            wildcard = _("*")
+        dlg = wx.FileDialog(wx.GetApp().GetTopWindow(),
+                               _("Select a File"),
+                               defaultDir=defaultDir,
+                               defaultFile=defaultFile,
+                               wildcard=wildcard,
+                               style=wx.OPEN|wx.FILE_MUST_EXIST|wx.HIDE_READONLY)
+        # dlg.CenterOnParent()  # wxBug: caused crash with wx.FileDialog
+        if dlg.ShowModal() == wx.ID_OK:
+            path = dlg.GetPath()
+            if path:  
+                self._pathTextCtrl.SetValue(path)
+                self._pathTextCtrl.SetToolTipString(self._pathTextCtrl.GetValue())
+                self._pathTextCtrl.SetInsertionPointEnd()
+        dlg.Destroy()            
+
+    def OnChooseIni(self, event):
+        defaultDir = os.path.dirname(self._iniTextCtrl.GetValue().strip())
+        defaultFile = os.path.basename(self._iniTextCtrl.GetValue().strip())
+        if wx.Platform == '__WXMSW__':
+            wildcard = _("Ini (*.ini)|*.ini|All|*.*")
+            if not defaultFile:
+                defaultFile = "php.ini"
+        else:
+            wildcard = _("*")
+        dlg = wx.FileDialog(wx.GetApp().GetTopWindow(),
+                               _("Select a File"),
+                               defaultDir=defaultDir,
+                               defaultFile=defaultFile,
+                               wildcard=wildcard,
+                               style=wx.OPEN|wx.FILE_MUST_EXIST|wx.HIDE_READONLY)
+        # dlg.CenterOnParent()  # wxBug: caused crash with wx.FileDialog
+        if dlg.ShowModal() == wx.ID_OK:
+            ini = dlg.GetPath()
+            if ini:  
+                self._iniTextCtrl.SetValue(ini)
+                self._iniTextCtrl.SetToolTipString(self._iniTextCtrl.GetValue())
+                self._iniTextCtrl.SetInsertionPointEnd()
+        dlg.Destroy()            
+
+    def OnOK(self, optionsDialog):
+        config = wx.ConfigBase_Get()
+        config.Write("ActiveGridPHPLocation", self._pathTextCtrl.GetValue().strip())
+        config.Write("ActiveGridPHPINILocation", self._iniTextCtrl.GetValue().strip())
+
+        self._otherOptions.OnOK(optionsDialog)
 
     def GetIcon(self):
         return getPHPIcon()
 
     def GetIcon(self):
         return getPHPIcon()
index bc7c48f0c12b7f557b6dc9d6626328d87361cc26..544d0fbd534c5e88acfdfc08790f298f61a62555 100644 (file)
@@ -73,7 +73,7 @@ class PerlCtrl(CodeEditor.CodeCtrl):
 
 
     def SetViewDefaults(self):
 
 
     def SetViewDefaults(self):
-        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Perl", hasWordWrap = True, hasTabs = True)
+        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Perl", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetFontAndColorFromConfig(self):
 
 
     def GetFontAndColorFromConfig(self):
@@ -130,7 +130,7 @@ class PerlCtrl(CodeEditor.CodeCtrl):
 class PerlOptionsPanel(STCTextEditor.TextOptionsPanel):
 
     def __init__(self, parent, id):
 class PerlOptionsPanel(STCTextEditor.TextOptionsPanel):
 
     def __init__(self, parent, id):
-        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Perl", label = "Perl", hasWordWrap = True, hasTabs = True)
+        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Perl", label = "Perl", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetIcon(self):
 
 
     def GetIcon(self):
index af95b890d3edd8e718c9c069cb3e6c3fbaba4427..d826156d34ac49f28d27e17dbfacf7723b5a782b 100644 (file)
@@ -6,7 +6,7 @@
 #
 # Created:      8/15/03
 # CVS-ID:       $Id$
 #
 # Created:      8/15/03
 # CVS-ID:       $Id$
-# Copyright:    (c) 2003, 2004, 2005 ActiveGrid, Inc.
+# Copyright:    (c) 2003-2006 ActiveGrid, Inc.
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
@@ -25,6 +25,7 @@ import time
 import types
 import activegrid.util.appdirs as appdirs
 import activegrid.util.fileutils as fileutils
 import types
 import activegrid.util.appdirs as appdirs
 import activegrid.util.fileutils as fileutils
+import activegrid.util.aglogging as aglogging
 import UICommon
 import Wizard
 import SVNService
 import UICommon
 import Wizard
 import SVNService
@@ -36,14 +37,19 @@ if not ACTIVEGRID_BASE_IDE:
     import activegrid.server.deployment as deploymentlib
     import ProcessModelEditor
     import DataModelEditor
     import activegrid.server.deployment as deploymentlib
     import ProcessModelEditor
     import DataModelEditor
+    import DeploymentGeneration
     import WsdlAgEditor
     import WsdlAgEditor
+    import WsdlAgModel
     APP_LAST_LANGUAGE = "LastLanguage"
     import activegrid.model.basedocmgr as basedocmgr
     import activegrid.model.basemodel as basemodel
     APP_LAST_LANGUAGE = "LastLanguage"
     import activegrid.model.basedocmgr as basedocmgr
     import activegrid.model.basemodel as basemodel
+    import activegrid.model.projectmodel as projectmodel
     import PropertyService
     from activegrid.server.toolsupport import GetTemplate
     import activegrid.util.xmlutils as xmlutils
     import activegrid.util.sysutils as sysutils
     import PropertyService
     from activegrid.server.toolsupport import GetTemplate
     import activegrid.util.xmlutils as xmlutils
     import activegrid.util.sysutils as sysutils
+    DataServiceExistenceException = DeploymentGeneration.DataServiceExistenceException
+    import WebBrowserService
 
 from SVNService import SVN_INSTALLED
 
 
 from SVNService import SVN_INSTALLED
 
@@ -61,6 +67,10 @@ SPACE = 10
 HALF_SPACE = 5
 PROJECT_EXTENSION = ".agp"
 
 HALF_SPACE = 5
 PROJECT_EXTENSION = ".agp"
 
+if not ACTIVEGRID_BASE_IDE:
+    PRE_17_TMP_DPL_NAME = "RunTime_tmp" + deploymentlib.DEPLOYMENT_EXTENSION
+    _17_TMP_DPL_NAME = ".tmp" + deploymentlib.DEPLOYMENT_EXTENSION
+
 # wxBug: the wxTextCtrl and wxChoice controls on Mac do not correctly size
 # themselves with sizers, so we need to add a right border to the sizer to
 # get the control to shrink itself to fit in the sizer.
 # wxBug: the wxTextCtrl and wxChoice controls on Mac do not correctly size
 # themselves with sizers, so we need to add a right border to the sizer to
 # get the control to shrink itself to fit in the sizer.
@@ -72,70 +82,85 @@ if wx.Platform == "__WXMAC__":
 PROJECT_KEY = "/AG_Projects"
 PROJECT_DIRECTORY_KEY = "NewProjectDirectory"
 
 PROJECT_KEY = "/AG_Projects"
 PROJECT_DIRECTORY_KEY = "NewProjectDirectory"
 
-NEW_PROJECT_DIRECTORY_DEFAULT = appdirs.documents_folder
+NEW_PROJECT_DIRECTORY_DEFAULT = appdirs.getSystemDir()
 
 #----------------------------------------------------------------------------
 # Methods
 #----------------------------------------------------------------------------
 
 
 #----------------------------------------------------------------------------
 # Methods
 #----------------------------------------------------------------------------
 
-def getProjectKeyName(projectName, mode):
-    return "%s/%s/%s" % (PROJECT_KEY, projectName.replace(os.sep, '|'), mode)
+def AddProjectMapping(doc, projectDoc=None, hint=None):
+    projectService = wx.GetApp().GetService(ProjectService)
+    if projectService:
+        if not projectDoc:
+            if not hint:
+                hint = doc.GetFilename()
+            projectDocs = projectService.FindProjectByFile(hint)
+            if projectDocs:
+                projectDoc = projectDocs[0]
+                
+        projectService.AddProjectMapping(doc, projectDoc)
+        if hasattr(doc, "GetModel"):
+            projectService.AddProjectMapping(doc.GetModel(), projectDoc)
+
+
+def getProjectKeyName(projectName, mode=None):
+    if mode:
+        return "%s/%s/%s" % (PROJECT_KEY, projectName.replace(os.sep, '|'), mode)
+    else:
+        return "%s/%s" % (PROJECT_KEY, projectName.replace(os.sep, '|'))
 
 
 def GetDocCallback(filepath):
     """ Get the Document used by the IDE and the in-memory document model used by runtime engine """
     docMgr = wx.GetApp().GetDocumentManager()
     
 
 
 def GetDocCallback(filepath):
     """ Get the Document used by the IDE and the in-memory document model used by runtime engine """
     docMgr = wx.GetApp().GetDocumentManager()
     
-    doc = docMgr.CreateDocument(filepath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
-    if (doc == None):  # already open
-        for d in docMgr.GetDocuments():
-            if os.path.normcase(d.GetFilename()) == os.path.normcase(filepath):
-                doc = d
-                break
-    else:
-        projectService = wx.GetApp().GetService(ProjectService)
-        if projectService:
-            projectDocs = projectService.FindProjectByFile(filepath)
-            if projectDocs:
-                projectDoc = projectDocs[0]
-                projectService.AddProjectMapping(doc, projectDoc)
-                if hasattr(doc, "GetModel"):
-                    projectService.AddProjectMapping(doc.GetModel(), projectDoc)
-                
-            
-    if doc and doc.GetDocumentTemplate().GetDocumentType() == WsdlAgEditor.WsdlAgDocument:
-        # get referenced wsdl doc instead
-        if os.path.isabs(doc.GetModel().filePath):  # if absolute path, leave it alone
-            filepath = doc.GetModel().filePath
-        else:
-            filepath = doc.GetAppDocMgr().fullPath(doc.GetModel().filePath)  # check relative to project homeDir
-    
-            if not os.path.isfile(filepath):
-                filepath = os.path.normpath(os.path.join(os.path.dirname(doc.GetFilename()), doc.GetModel().filePath))  # check relative to wsdlag file
-                
-                if not os.path.isfile(filepath):
-                    filename = os.sep + os.path.basename(doc.GetModel().filePath)  # check to see if in project file
-                    filePaths = findDocumentMgr(doc).filePaths
-                    for fp in filePaths:
-                        if fp.endswith(filename):
-                            filepath = fp
-                            break
-                
+    try:
         doc = docMgr.CreateDocument(filepath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
         doc = docMgr.CreateDocument(filepath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
-        if (doc == None):  # already open
+        if doc:
+            AddProjectMapping(doc)
+        else:  # already open
             for d in docMgr.GetDocuments():
                 if os.path.normcase(d.GetFilename()) == os.path.normcase(filepath):
                     doc = d
                     break
             for d in docMgr.GetDocuments():
                 if os.path.normcase(d.GetFilename()) == os.path.normcase(filepath):
                     doc = d
                     break
+    except Exception,e:
+        doc = None            
+        aglogging.reportException(e, stacktrace=True)
+            
+    if doc and doc.GetDocumentTemplate().GetDocumentType() == WsdlAgEditor.WsdlAgDocument:
+        # get referenced wsdl doc instead
+        if doc.GetModel().filePath:
+            if os.path.isabs(doc.GetModel().filePath):  # if absolute path, leave it alone
+                filepath = doc.GetModel().filePath
+            else:
+                filepath = doc.GetAppDocMgr().fullPath(doc.GetModel().filePath)  # check relative to project homeDir
+        
+                if not os.path.isfile(filepath):
+                    filepath = os.path.normpath(os.path.join(os.path.dirname(doc.GetFilename()), doc.GetModel().filePath))  # check relative to wsdlag file
+                    
+                    if not os.path.isfile(filepath):
+                        filename = os.sep + os.path.basename(doc.GetModel().filePath)  # check to see if in project file
+                        filePaths = findDocumentMgr(doc).filePaths
+                        for fp in filePaths:
+                            if fp.endswith(filename):
+                                filepath = fp
+                                break
+        
+            try:
+                doc = docMgr.CreateDocument(filepath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
+            except Exception,e:
+                doc = None
+                aglogging.reportException(e, stacktrace=True)
+                
+            if doc: 
+                AddProjectMapping(doc)
+            else:  # already open
+                for d in docMgr.GetDocuments():
+                    if os.path.normcase(d.GetFilename()) == os.path.normcase(filepath):
+                        doc = d
+                        break
         else:
         else:
-            projectService = wx.GetApp().GetService(ProjectService)
-            if projectService:
-                projectDocs = projectService.FindProjectByFile(filepath)
-                if projectDocs:
-                    projectDoc = projectDocs[0]
-                    projectService.AddProjectMapping(doc, projectDoc)
-                    if hasattr(doc, "GetModel"):
-                        projectService.AddProjectMapping(doc.GetModel(), projectDoc)
+            doc = None
 
     if doc:
         docModel = doc.GetModel()
 
     if doc:
         docModel = doc.GetModel()
@@ -202,8 +227,82 @@ if not ACTIVEGRID_BASE_IDE:
 # Classes
 #----------------------------------------------------------------------------
 
 # Classes
 #----------------------------------------------------------------------------
 
-class ProjectDocument(wx.lib.docview.Document):
+if not ACTIVEGRID_BASE_IDE:
+    class IDEResourceFactory(DeploymentGeneration.DeploymentResourceFactory):
+        
+        def __init__(self, openDocs, dataSourceService, projectDir,
+                     preview=False, deployFilepath=None):
+
+            self.openDocs = openDocs
+            self.dataSourceService = dataSourceService
+            self.projectDir = projectDir
+            self.preview = preview
+            self.deployFilepath = deployFilepath
+            
+            self.defaultFlagsNoView = (
+                wx.GetApp().GetDocumentManager().GetFlags()|
+                wx.lib.docview.DOC_SILENT|
+                wx.lib.docview.DOC_OPEN_ONCE|
+                wx.lib.docview.DOC_NO_VIEW)
+            
+        def getModel(self, projectFile):
+            doc = wx.GetApp().GetDocumentManager().CreateDocument(
+                projectFile.filePath, flags=self.defaultFlagsNoView)
+            if (doc == None):  # already open
+                doc = self._findOpenDoc(projectFile.filePath)
+            else:
+                AddProjectMapping(doc)
+            if (doc != None):
+                return doc.GetModel()
+
+        def getDataSource(self, dataSourceName):
+            # in preview mode, runtime needs the generated Deployment
+            # to contain the requried data source. But runtime doesn't
+            # actually need to communicate to db. So here is the logic to
+            # make preview works if the required data soruce has not
+            # yet been defined.            
+            dataSource = self.dataSourceService.getDataSource(dataSourceName)
+            if (dataSource != None):
+                return dataSource
+            elif not self.preview:
+                raise DataServiceExistenceException(dataSourceName)
+            else:
+                # first to see if an existing dpl file is there, if so,
+                # use the data source in dpl file
+                if (self.deployFilepath != None):
+                    tempDply = None
+                    try:
+                        tempDply = xmlutils.load(deployFilepath)
+                    except:
+                        pass
+                    if (tempDply != None):
+                        for tempDataSource in tempDply.dataSources:
+                            if (tempDataSource.name == dataSourceName):
+                                return tempDataSource
+
+                # if unable to use dpl file, then create a dummy data source
+                import activegrid.data.dataservice as dataservice
+                return dataservice.DataSource(
+                    name=dataSourceName, dbtype=dataservice.DB_TYPE_SQLITE)
+
+        def initDocumentRef(self, projectFile, documentRef, dpl):
+            doc = self._findOpenDoc(projectFile.filePath)
+            if (doc and hasattr(doc, 'GetModel')):
+                documentRef.document = doc.GetModel()
+                if isinstance(documentRef, deploymentlib.XFormRef):
+                    doc.GetModel().linkDeployment(dpl, dpl.loader)
+
+        def _findOpenDoc(self, filePath):
+            for openDoc in self.openDocs:
+                if openDoc.GetFilename() == filePath:
+                    return openDoc
+            return None
+
+        def getProjectDir(self):
+            return self.projectDir
+
 
 
+class ProjectDocument(wx.lib.docview.Document):
 
     def __init__(self, model=None):
         wx.lib.docview.Document.__init__(self)
 
     def __init__(self, model=None):
         wx.lib.docview.Document.__init__(self)
@@ -482,24 +581,22 @@ class ProjectDocument(wx.lib.docview.Document):
 
         return [fileRef.filePath for fileRef in invalidFileRefs]
 
 
         return [fileRef.filePath for fileRef in invalidFileRefs]
 
+
     def SetStageProjectFile(self):
         self._stageProjectFile = True
 
     def SetStageProjectFile(self):
         self._stageProjectFile = True
 
-    def ArchiveProject(self, zipdest, tmpdir=None, stagedir=None):
-        """Stages the application files in tmpdir, and zips the stagedir, creating a zipfile that has the projectname, in zipdest. Returns path to zipfile. Optionally, pass in stagedir and we assume the app is already staged at stagedir (we don't stage again in that case)."""
-        if not stagedir:
-            if not tmpdir:
-                raise AssertionError("'tmpdir' must be set when not passing 'stagedir' so we know where to stage the app")
-            stagedir = self.StageProject(tmpdir)
+
+    def ArchiveProject(self, zipdest, stagedir):
+        """Zips stagedir, creates a zipfile that has as name the projectname, in zipdest. Returns path to zipfile."""
         if os.path.exists(zipdest):
             raise AssertionError("Cannot archive project, %s already exists" % zipdest)
         fileutils.zip(zipdest, stagedir)
 
         return zipdest
 
         if os.path.exists(zipdest):
             raise AssertionError("Cannot archive project, %s already exists" % zipdest)
         fileutils.zip(zipdest, stagedir)
 
         return zipdest
 
-        
-    def StageProject(self, tmpdir):
-        """ Copies all files that project knows about into staging location. Files that live outside of the project dir are copied into the root of the stage dir, and their recorded file path is updated. Files that live inside of the project dir keep their relative path. Generates .dpl file into staging dir. Returns path to staging dir."""
+
+    def StageProject(self, tmpdir, targetDataSourceMapping={}):
+        """ Copies all files this project knows about into staging location. Files that live outside of the project dir are copied into the root of the stage dir, and their recorded file path is updated. Files that live inside of the project dir keep their relative path. Generates .dpl file into staging dir. Returns path to staging dir."""
 
         projname = self.GetProjectName()
         stagedir = os.path.join(tmpdir, projname)
 
         projname = self.GetProjectName()
         stagedir = os.path.join(tmpdir, projname)
@@ -523,6 +620,9 @@ class ProjectDocument(wx.lib.docview.Document):
         # copy files to staging dir
         self._StageFiles(fileDict)
 
         # copy files to staging dir
         self._StageFiles(fileDict)
 
+        # set target data source for schemas
+        self._SetSchemaTargetDataSource(fileDict, targetDataSourceMapping)
+
         # it is unfortunate we require this. it would be nice if filepaths
         # were only in the project
         self._FixWsdlAgFiles(stagedir)
         # it is unfortunate we require this. it would be nice if filepaths
         # were only in the project
         self._FixWsdlAgFiles(stagedir)
@@ -530,19 +630,27 @@ class ProjectDocument(wx.lib.docview.Document):
         # generate .dpl file
         dplfilename = projname + deploymentlib.DEPLOYMENT_EXTENSION
         dplfilepath = os.path.join(stagedir, dplfilename)
         # generate .dpl file
         dplfilename = projname + deploymentlib.DEPLOYMENT_EXTENSION
         dplfilepath = os.path.join(stagedir, dplfilename)
-        self.GenerateDeployment(dplfilepath, productionDeployment=True)
+        self.GenerateDeployment(dplfilepath)
 
         if self._stageProjectFile:
             # save project so we get the .agp file. not required for deployment
             # but convenient if user wants to open the deployment in the IDE
             agpfilename = projname + PROJECT_EXTENSION
 
         if self._stageProjectFile:
             # save project so we get the .agp file. not required for deployment
             # but convenient if user wants to open the deployment in the IDE
             agpfilename = projname + PROJECT_EXTENSION
-            agpfilepath = os.path.join(stagedir, agpfilename)        
+            agpfilepath = os.path.join(stagedir, agpfilename)
+
+            # if this project has deployment data sources configured, remove
+            # them. changing the project is fine, since this is a clone of
+            # the project the IDE has.
+            self.GetModel().GetAppInfo().ResetDeploymentDataSources()
+            
             f = None
             try:
                 f = open(agpfilepath, "w")
             f = None
             try:
                 f = open(agpfilepath, "w")
+                
                 # setting homeDir correctly is required for the "figuring out
                 # relative paths" logic when saving the project
                 self.GetModel().homeDir = stagedir
                 # setting homeDir correctly is required for the "figuring out
                 # relative paths" logic when saving the project
                 self.GetModel().homeDir = stagedir
+                
                 projectlib.save(f, self.GetModel(), productionDeployment=True)
             finally:
                 try:
                 projectlib.save(f, self.GetModel(), productionDeployment=True)
             finally:
                 try:
@@ -551,24 +659,36 @@ class ProjectDocument(wx.lib.docview.Document):
 
         return stagedir
 
 
         return stagedir
 
-
     def _FixWsdlAgFiles(self, stagedir):
     def _FixWsdlAgFiles(self, stagedir):
-        """For each wsdlag file in the stagedir:
-           Ensure the referenced wsdl file lives in root of stagedir. This
-           should be the case if wsdl is part of project (and staging has run).
-           If it is not at root of stagedir, copy it. Then update path in
-           wsdlag."""
+        """For each wsdlag file in the stagedir: if referenced artifact (wsdl or code file) is a known product file (such as securityservice.wsdl), make sure patch to it is parameterized with special env var. We do not want to copy those files. For user artifacts, ensure the file lives in root of stagedir. This should be the case if it is part of project (since staging has run). If it is not at root of stagedir, copy it. Then update path in wsdlag."""
         files = os.listdir(stagedir)
         for f in files:
         files = os.listdir(stagedir)
         for f in files:
-            if f.endswith(WsdlAgEditor.WsdlAgDocument.WSDL_AG_EXT):
+            if (f.endswith(WsdlAgEditor.WsdlAgDocument.WSDL_AG_EXT)):
                 wsdlagpath = os.path.join(stagedir, f)
                 fileObject = None
                 wsdlagpath = os.path.join(stagedir, f)
                 fileObject = None
-                mod = False
+                modified = False
                 try:
                     fileObject = open(wsdlagpath)
                     serviceref = WsdlAgEditor.load(fileObject)
                 try:
                     fileObject = open(wsdlagpath)
                     serviceref = WsdlAgEditor.load(fileObject)
-                    if hasattr(serviceref, "filePath") and serviceref.filePath:
-                        mod = self.UpdateServiceRefFilePath(stagedir,serviceref)
+
+                    # referenced wsdl
+                    if (hasattr(serviceref, WsdlAgModel.WSDL_FILE_ATTR)):
+                        modified = (modified |
+                                    self._UpdateServiceRefPathAttr(
+                                        stagedir, serviceref,
+                                        WsdlAgModel.WSDL_FILE_ATTR))
+
+                    # referenced code file
+                    if (hasattr(serviceref, WsdlAgModel.LOCAL_SERVICE_ELEMENT)):
+                        lse = getattr(serviceref,
+                                      WsdlAgModel.LOCAL_SERVICE_ELEMENT)
+                        if (hasattr(lse, WsdlAgModel.LOCAL_SERVICE_FILE_ATTR)):
+                            modified = (modified |
+                                        self._UpdateServiceRefPathAttr(
+                                            stagedir, lse,
+                                            WsdlAgModel.LOCAL_SERVICE_FILE_ATTR))
+
+                    
                 finally:
                     try:
                         fileObject.close()
                 finally:
                     try:
                         fileObject.close()
@@ -576,7 +696,7 @@ class ProjectDocument(wx.lib.docview.Document):
                         pass
 
                 # no need to save the file if we did not change anything
                         pass
 
                 # no need to save the file if we did not change anything
-                if not mod: continue
+                if not modified: continue
 
                 # write the wsdlag file
                 fileObject = open(wsdlagpath)
 
                 # write the wsdlag file
                 fileObject = open(wsdlagpath)
@@ -589,31 +709,81 @@ class ProjectDocument(wx.lib.docview.Document):
                         pass
                     
 
                         pass
                     
 
-    def UpdateServiceRefFilePath(self, stagedir, serviceref):
-        """Returns True if serviceref.filePath has been updated, False otherwise."""
-        if not os.path.exists(serviceref.filePath):
-            # should be an error? wrong place to
-            # validate that referenced file exists
-            # could print warning
+    def _UpdateServiceRefPathAttr(self, stagedir, serviceref, attrName):
+        """Returns True if serviceref path has been updated, False otherwise."""
+
+        filePath = getattr(serviceref, attrName)
+
+        if (filePath == None):
+            return False
+
+        filePath = filePath.strip()
+
+        if (len(filePath) == 0):
+            return False
+            
+
+        # if filePath starts with one of the AG systems vars, we don't
+        # have to do anything
+        if (fileutils.startsWithAgSystemVar(filePath)):
+            return False
+
+        # remove any known env var refs (we'll put them back a little below)
+        # we remove them here so that paths that do not have env vars also
+        # get parameterized correctly below
+        filePath = fileutils.expandKnownAGVars(filePath)
+
+        # make sure we have forward slashes. this is a workaround, which
+        # would not be necessary if we only write paths with forward slashes
+        # into our files
+        filePath = filePath.replace("\\", "/")
+        
+        filePath = os.path.abspath(filePath)        
+
+        if (not os.path.exists(filePath)):
+            # Wrong place to validate that referenced file exists, so just
+            # give up
             return False
             
         # If the referenced file is in stagedir already, there's nothing to do
             return False
             
         # If the referenced file is in stagedir already, there's nothing to do
-        if fileutils.hasAncestorDir(serviceref.filePath, stagedir):
+        if (fileutils.hasAncestorDir(filePath, stagedir)):
             return False
 
         # The path points outside of stagedir.
 
         # Check if we already have the referenced wsdl file at root, should be
             return False
 
         # The path points outside of stagedir.
 
         # Check if we already have the referenced wsdl file at root, should be
-        # the case if the referenced wsdl is part of project
-        # Copy it if we don't have it
-        relPath = os.path.basename(serviceref.filePath)
-        stagepath = os.path.join(stagedir, relPath)
-        if not os.path.exists(stagepath):
-            fileutils.copyFile(serviceref.filePath, stagepath)
-
-        serviceref.filePath = relPath
+        # the case if the referenced wsdl is part of project.
+        # Copy it if we don't have it, unless it lives in one of the known
+        # product directories - in which case we parameterize the known path
+        # with one of our AG system vars
+        relPath = os.path.basename(filePath)
+        stagePath = os.path.join(stagedir, relPath)
+
+        if (not os.path.exists(stagePath)):
+            pFilePath = fileutils.parameterizePathWithAGSystemVar(filePath)
+            if pFilePath == filePath: # no parameterization happened, copy
+                fileutils.copyFile(filePath, stagePath)
+                setattr(serviceref, attrName, relPath)
+            else:
+                setattr(serviceref, attrName, pFilePath.replace("\\", "/"))
+        else:
+            setattr(serviceref, attrName, relPath)
 
         return True
 
         return True
+
+
+    def _SetSchemaTargetDataSource(self, projectFiles, dsmapping):
+        """Update schema's default data source, if necessary."""
+
+        for projectFile in projectFiles:
+            if (projectFile.type == basedocmgr.FILE_TYPE_SCHEMA):
+                name = os.path.basename(projectFile.filePath)
+                if (dsmapping.has_key(name)):
+                    schema = xmlutils.load(projectFile.filePath)
+                    defaultName = schema.getDefaultDataSourceName()
+                    if (defaultName != dsmapping[name]):
+                        schema.setDefaultDataSourceName(dsmapping[name])
+                        xmlutils.save(projectFile.filePath, schema)
         
         
     def _StageFiles(self, fileDict):
         
         
     def _StageFiles(self, fileDict):
@@ -664,11 +834,6 @@ class ProjectDocument(wx.lib.docview.Document):
         for filename in foreignFiles:
             if filename in projectRootFiles:
                 raise IOError("File outside of project, \"%s\", cannot have same name as file at project root" % filename)
         for filename in foreignFiles:
             if filename in projectRootFiles:
                 raise IOError("File outside of project, \"%s\", cannot have same name as file at project root" % filename)
-
-        # REVIEW stoens@activegrid.com 19-Oct-05 --
-        # We could also validate that user does not already have a .dpl file
-        # since we're going to generate one...
-        
         return rtn
     
                             
         return rtn
     
                             
@@ -680,7 +845,19 @@ class ProjectDocument(wx.lib.docview.Document):
         self.Modify(True)
         return True
 
         self.Modify(True)
         return True
 
+    def GetSchemas(self):
+        """Returns list of schema models (activegrid.model.schema.schema) for all schemas in this project."""
+        
+        rtn = []
+        resourceFactory = self._GetResourceFactory()
+        for projectFile in self.GetModel().projectFiles:
+            if (projectFile.type == basedocmgr.FILE_TYPE_SCHEMA):
+                schema = resourceFactory.getModel(projectFile)
+                if (schema != None):
+                    rtn.append(schema)
 
 
+        return rtn
+        
     def GetFiles(self):
         return self.GetModel().filePaths
 
     def GetFiles(self):
         return self.GetModel().filePaths
 
@@ -709,199 +886,117 @@ class ProjectDocument(wx.lib.docview.Document):
         return os.path.splitext(os.path.basename(self.GetFilename()))[0]
 
 
         return os.path.splitext(os.path.basename(self.GetFilename()))[0]
 
 
-    def GetDeploymentFilepath(self):
-        projectName = self.GetProjectName()
-        return os.path.join(self.GetModel().homeDir, projectName + "RunTime_tmp" + deploymentlib.DEPLOYMENT_EXTENSION)
-        
+    def GetDeploymentFilepath(self, pre17=False):
+        if (pre17):
+            name = self.GetProjectName() + PRE_17_TMP_DPL_NAME
+        else:
+            name = self.GetProjectName() + _17_TMP_DPL_NAME
+        return os.path.join(self.GetModel().homeDir, name)
+    
 
 
-    def GenerateDeployment(self, deployFilepath=None, preview=False, productionDeployment=False):
+    def _GetResourceFactory(self, preview=False, deployFilepath=None):
+        return IDEResourceFactory(
+            openDocs=wx.GetApp().GetDocumentManager().GetDocuments(),
+            dataSourceService=wx.GetApp().GetService(DataModelEditor.DataSourceService),
+            projectDir=os.path.dirname(self.GetFilename()),
+            preview=preview,
+            deployFilepath=deployFilepath)
+
+    def GenerateDeployment(self, deployFilepath=None, preview=False):
+        
         if ACTIVEGRID_BASE_IDE:
             return
         if ACTIVEGRID_BASE_IDE:
             return
-        
-        def FindOpenDoc(filePath):
-            openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-            for openDoc in openDocs:
-                if openDoc.GetFilename() == filePath:
-                    return openDoc
-            return None
 
         if not deployFilepath:
             deployFilepath = self.GetDeploymentFilepath()
 
         if not deployFilepath:
             deployFilepath = self.GetDeploymentFilepath()
-            
-        deployment = deploymentlib.Deployment(deployFilepath)
 
 
-        defaultFlagsNoView = wx.GetApp().GetDocumentManager().GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW
-        self.GetAppInfo().CopyToDeployment(deployment)
-
-        for file in self.GetModel()._files:
-            if not file.type:
-                continue
-            elif file.type == basedocmgr.FILE_TYPE_SERVICE:  # set serviceRefs
-                doc = wx.GetApp().GetDocumentManager().CreateDocument(file.filePath, flags=defaultFlagsNoView)
-                if (doc == None):  # already open
-                    doc = FindOpenDoc(file.filePath)
-                if doc:
-                    serviceRef = doc.GetModel()
-                    if serviceRef:
-                        documentRef = copy.copy(serviceRef)
-                        deployment.serviceRefs.append(documentRef)
-
-                        if not productionDeployment:
-                            # filePath should point to location of wsdl file
-                            # wsdlag filePath points to relative path to wsdl file from wsdlag location
-                            # but deployment needs relative path from deployment location, so here's the conversion
-                            curDir = os.path.dirname(self.GetFilename()) + os.sep
-                            filePath = file.document.fileName
-                            if (filePath == None):
-                                raise Exception("Cannot find file \"%s\"" % file.filePath)
-                            if filePath.startswith(curDir):
-                                filePath = filePath[len(curDir):]
-                            if os.sep != '/':
-                                filePath = filePath.replace(os.sep, "/")
-                            documentRef.filePath = filePath
-                        
-                            documentRef.document = file.document
-                            
-                        if serviceRef.serviceType == deploymentlib.SERVICE_DATABASE and serviceRef.databaseService:
-                            dataSourceService = wx.GetApp().GetService(DataModelEditor.DataSourceService)
-                            ds = dataSourceService.getDataSource(serviceRef.databaseService.datasourceName)
-                            if ds:
-                                found = False
-                                for d in deployment.dataSources:
-                                    if d.name == ds.name:
-                                        found = True
-                                        break
-                                if not found:
-                                    deployment.dataSources.append(ds)
-            else:
-                curDir = os.path.dirname(self.GetFilename()) + os.sep
-                filePath = file.filePath
-                if filePath.startswith(curDir):
-                    filePath = filePath[len(curDir):]
-                if os.sep != '/':
-                    filePath = filePath.replace(os.sep, "/")
-                    
-                if file.type == basedocmgr.FILE_TYPE_XFORM:
-                    documentRef = deploymentlib.XFormRef()
-                    deployment.xformRefs.append(documentRef)
-                elif file.type == basedocmgr.FILE_TYPE_PROCESS:
-                    documentRef = deploymentlib.ProcessRef()
-                    deployment.processRefs.append(documentRef)
-                elif file.type == basedocmgr.FILE_TYPE_SCHEMA:
-                    # set schemaRefs
-                    documentRef = deploymentlib.SchemaRef()
-                    deployment.schemaRefs.append(documentRef)
-
-                    # set dataSources
-                    doc = wx.GetApp().GetDocumentManager().CreateDocument(file.filePath, flags=defaultFlagsNoView)
-                    if (doc == None):  # already open
-                        doc = FindOpenDoc(file.filePath)
-                    if doc:
-                        dataSourceService = wx.GetApp().GetService(DataModelEditor.DataSourceService)
-                        ds = dataSourceService.getDataSource(doc.GetModel().getDefaultDataSourceName())
-                        if ds:
-                            found = False
-                            for d in deployment.dataSources:
-                                if d.name == ds.name:
-                                    found = True
-                                    break
-                            if not found:
-                                deployment.dataSources.append(ds)
-                                
-                        # set keyServices
-                        keyServices = doc.GetModel().keyServices
-                        for keyService in keyServices:        
-                            # add default key service to deployment
-                            if not productionDeployment:
-                                mainModuleDir = sysutils.mainModuleDir
-                            else:
-                                mainModuleDir = sysutils.MAINMODULE_DIR_VAR
-                            wsdlFullPath = os.path.join(mainModuleDir, "..", "wsdl", DataModelEditor.DEFAULT_KEYSERVICE_WSDL_FILENAME)
-                            keyServiceRef = deploymentlib.ServiceRef(filePath=wsdlFullPath)
-                            deployment.serviceRefs.append(keyServiceRef)
-                            
-                            keyServiceRef.name = keyService
-                            keyServiceRef.serviceType = deploymentlib.SERVICE_LOCAL
-                            keyServiceRef.localService = deploymentlib.LocalService()
-                            if keyService == DataModelEditor.DEFAULT_KEYSERVICE:
-                                keyServiceRef.filePath = wsdlFullPath
-                                keyServiceRef.localServiceClassName = DataModelEditor.DEFAULT_KEYSERVICE_CLASSNAME
-                            
+        d = DeploymentGeneration.DeploymentGenerator(
+            self.GetModel(), self._GetResourceFactory(preview,
+                                                      deployFilepath))
+                
+        dpl = d.getDeployment(deployFilepath)
 
 
-                elif file.type == basedocmgr.FILE_TYPE_SKIN:
-                    documentRef = deploymentlib.SkinRef(deployment)
-                    deployment.skinref = documentRef
-                elif file.type == basedocmgr.FILE_TYPE_IDENTITY:
-                    documentRef = deploymentlib.IdentityRef()
-                    deployment.identityRefs.append(documentRef)
-                else:
-                    continue
-                        
-                documentRef.name = file.name
-                documentRef.filePath = filePath
-                doc = FindOpenDoc(file.filePath)
-                if doc and hasattr(doc, 'GetModel'):
-                    documentRef.document = doc.GetModel()
-                    if isinstance(documentRef, deploymentlib.XFormRef):
-                        doc.GetModel().linkDeployment(deployment, deployment.loader)
-       
         if preview:
         if preview:
-            deployment.initialize()  # used in preview only
-            
-        if 0: # preview:  # setPrototype not working, commented this out
-            deploymentlib._deploymentCache.setPrototype(deployment.fileName, deployment)
-        else:
-            deploymentlib.saveThroughCache(deployment.fileName, deployment)
+            dpl.initialize()  # used in preview only
 
 
-        return deployFilepath        
-        
+        # REVIEW 07-Apr-06 stoens@activegrid.com -- Check if there's a
+        # tmp dpl file with pre 17 name, if so, delete it, so user doesn't end
+        # up with unused file in project dir. We should probably remove this
+        # check after 1.7 goes out.
+        fileutils.remove(self.GetDeploymentFilepath(pre17=True))
 
 
+        deploymentlib.saveThroughCache(dpl.fileName, dpl)
+        return deployFilepath
+        
     def AddNameSpaces(self, filePaths):
     def AddNameSpaces(self, filePaths):
-        """ Add any new wsdl namespaces to bpel files """
+        """ Add any new wsdl and schema namespaces to bpel files """
         """ Add any new schema namespaces to wsdl files """
         if ACTIVEGRID_BASE_IDE:
             return
 
         """ Add any new schema namespaces to wsdl files """
         if ACTIVEGRID_BASE_IDE:
             return
 
-        serviceRefs = self.GetAppDocMgr().allServiceRefs  # wsdl
-
         processRefs = self.GetAppDocMgr().findRefsByFileType(basedocmgr.FILE_TYPE_PROCESS) # bpel
         processRefs = self.GetAppDocMgr().findRefsByFileType(basedocmgr.FILE_TYPE_PROCESS) # bpel
-        if processRefs and serviceRefs:
+        schemaRefs = self.GetAppDocMgr().findRefsByFileType(basedocmgr.FILE_TYPE_SCHEMA) # xsd
+        serviceRefs = self.GetAppDocMgr().allServiceRefs  # wsdl
+        
+        # update bpel files
+        if processRefs and (serviceRefs or schemaRefs):
             for processRef in processRefs:
             for processRef in processRefs:
-                processDoc = processRef._GetDoc()
+                processDoc = processRef.ideDocument
                 process = processDoc.GetModel()
                 
                 process = processDoc.GetModel()
                 
-                modified = False
-                for serviceRef in serviceRefs:
-                    wsdl = serviceRef.document
-                    if (wsdl.fileName in filePaths
-                    or serviceRef.filePath in filePaths):
-                        wsdlLongNS = wsdl.targetNamespace
-                        wsdlShortNS = self.GetAppDocMgr().findShortNS(wsdlLongNS)
-                        if not wsdlShortNS:
-                            wsdlShortNS = xmlutils.genShortNS(process, wsdlLongNS)
-                        xmlutils.addNSAttribute(process, wsdlShortNS, wsdlLongNS)
-                        modified = True
-                if modified:
-                    processDoc.OnSaveDocument(processDoc.GetFilename())
-
-        schemaRefs = self.GetAppDocMgr().findRefsByFileType(basedocmgr.FILE_TYPE_SCHEMA)
-        if schemaRefs and serviceRefs:
+                if processDoc and process:
+                    modified = False
+                    
+                    # add wsdl namespaces to bpel file
+                    for serviceRef in serviceRefs:
+                        wsdl = serviceRef.document
+                        if (wsdl
+                        and (wsdl.fileName in filePaths
+                        or serviceRef.filePath in filePaths)):
+                            wsdlLongNS = wsdl.targetNamespace
+                            wsdlShortNS = self.GetAppDocMgr().findShortNS(wsdlLongNS)
+                            if not wsdlShortNS:
+                                wsdlShortNS = xmlutils.genShortNS(process, wsdlLongNS)
+                            xmlutils.addNSAttribute(process, wsdlShortNS, wsdlLongNS)
+                            modified = True
+                            
+                    # add schema namespaces to bpel file
+                    for schemaRef in schemaRefs:
+                        schema = schemaRef.document
+                        if schema and schema.fileName in filePaths:
+                            schemaLongNS = schema.targetNamespace
+                            schemaShortNS = self.GetAppDocMgr().findShortNS(schemaLongNS)
+                            if not schemaShortNS:
+                                schemaShortNS = xmlutils.genShortNS(process, schemaLongNS)
+                            xmlutils.addNSAttribute(process, schemaShortNS, schemaLongNS)
+                            modified = True
+    
+                    if modified:
+                        processDoc.OnSaveDocument(processDoc.GetFilename())
+
+
+        # update wsdl files
+        if serviceRefs and schemaRefs:
             for serviceRef in serviceRefs:
                 wsdl = serviceRef.document
                 wsdlDoc = serviceRef.ideDocument
                 
             for serviceRef in serviceRefs:
                 wsdl = serviceRef.document
                 wsdlDoc = serviceRef.ideDocument
                 
-                modified = False
-                for schemaRef in schemaRefs:
-                    schema = schemaRef.document
-                    if schema.fileName in filePaths:
-                        schemaLongNS = schema.targetNamespace
-                        schemaShortNS = self.GetAppDocMgr().findShortNS(schemaLongNS)
-                        if not schemaShortNS:
-                            schemaShortNS = xmlutils.genShortNS(process, schemaLongNS)
-                        xmlutils.addNSAttribute(wsdl, schemaShortNS, schemaLongNS)
-                        modified = True
-                if modified:
-                    wsdlDoc.OnSaveDocument(wsdlDoc.GetFilename())
+                if wsdl and wsdlDoc:
+                    modified = False
+                    
+                    # add schema namespace to wsdl file
+                    for schemaRef in schemaRefs:
+                        schema = schemaRef.document
+                        if schema and schema.fileName in filePaths:
+                            schemaLongNS = schema.targetNamespace
+                            schemaShortNS = self.GetAppDocMgr().findShortNS(schemaLongNS)
+                            if not schemaShortNS:
+                                schemaShortNS = xmlutils.genShortNS(wsdl, schemaLongNS)
+                            xmlutils.addNSAttribute(wsdl, schemaShortNS, schemaLongNS)
+                            modified = True
+                            
+                    if modified:
+                        wsdlDoc.OnSaveDocument(wsdlDoc.GetFilename())
 
 
 class NewProjectWizard(Wizard.BaseWizard):
 
 
 class NewProjectWizard(Wizard.BaseWizard):
@@ -918,10 +1013,10 @@ class NewProjectWizard(Wizard.BaseWizard):
 
 
     def CreateProjectLocation(self,wizard):
 
 
     def CreateProjectLocation(self,wizard):
-        page = Wizard.TitledWizardPage(wizard, _("Project File Location"))
+        page = Wizard.TitledWizardPage(wizard, _("Name and Location"))
 
 
-        page.GetSizer().Add(wx.StaticText(page, -1, _("\nSelect the directory and filename for the project.\n\n")))
-        self._projectName, self._dirCtrl, sizer, self._fileValidation = UICommon.CreateDirectoryControl(page, fileExtension="agp", appDirDefaultStartDir=True)
+        page.GetSizer().Add(wx.StaticText(page, -1, _("\nEnter the name and location for the project.\n")))
+        self._projectName, self._dirCtrl, sizer, self._fileValidation = UICommon.CreateDirectoryControl(page, fileExtension="agp", appDirDefaultStartDir=True, fileLabel=_("Name:"), dirLabel=_("Location:"))
         page.GetSizer().Add(sizer, 1, flag=wx.EXPAND)
 
         wizard.Layout()
         page.GetSizer().Add(sizer, 1, flag=wx.EXPAND)
 
         wizard.Layout()
@@ -930,7 +1025,7 @@ class NewProjectWizard(Wizard.BaseWizard):
 
 
     def RunWizard(self, existingTables = None, existingRelationships = None):
 
 
     def RunWizard(self, existingTables = None, existingRelationships = None):
-        status = wx.wizard.Wizard.RunWizard(self, self._projectLocationPage)
+        status = Wizard.BaseWizard.RunWizard(self, self._projectLocationPage)
         if status:
             wx.ConfigBase_Get().Write(PROJECT_DIRECTORY_KEY, self._dirCtrl.GetValue())
             docManager = wx.GetApp().GetTopWindow().GetDocumentManager()
         if status:
             wx.ConfigBase_Get().Write(PROJECT_DIRECTORY_KEY, self._dirCtrl.GetValue())
             docManager = wx.GetApp().GetTopWindow().GetDocumentManager()
@@ -959,7 +1054,7 @@ class NewProjectWizard(Wizard.BaseWizard):
     def OnWizPageChanging(self, event):
         if event.GetDirection():  # It's going forwards
             if event.GetPage() == self._projectLocationPage:
     def OnWizPageChanging(self, event):
         if event.GetDirection():  # It's going forwards
             if event.GetPage() == self._projectLocationPage:
-                if not self._fileValidation(noFirstCharDigit=True):
+                if not self._fileValidation(validClassName=True):
                     event.Veto()
                     return
                 self._fullProjectPath = os.path.join(self._dirCtrl.GetValue(),UICommon.MakeNameEndInExtension(self._projectName.GetValue(), PROJECT_EXTENSION))
                     event.Veto()
                     return
                 self._fullProjectPath = os.path.join(self._dirCtrl.GetValue(),UICommon.MakeNameEndInExtension(self._projectName.GetValue(), PROJECT_EXTENSION))
@@ -1473,8 +1568,6 @@ class ProjectView(wx.lib.docview.View):
 
         self._treeCtrl = ProjectTreeCtrl(panel, -1, style = wx.TR_HIDE_ROOT | wx.TR_HAS_BUTTONS | wx.TR_EDIT_LABELS | wx.TR_DEFAULT_STYLE | wx.TR_MULTIPLE | wx.TR_EXTENDED)
         self._treeCtrl.AddRoot(_("Projects"))
 
         self._treeCtrl = ProjectTreeCtrl(panel, -1, style = wx.TR_HIDE_ROOT | wx.TR_HAS_BUTTONS | wx.TR_EDIT_LABELS | wx.TR_DEFAULT_STYLE | wx.TR_MULTIPLE | wx.TR_EXTENDED)
         self._treeCtrl.AddRoot(_("Projects"))
-        wx.EVT_TREE_BEGIN_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginDrag)
-        wx.EVT_TREE_END_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndDrag)
         if self._embeddedWindow:
             sizer.Add(self._treeCtrl, 1, wx.EXPAND|wx.BOTTOM, HALF_SPACE)  # allow space for embedded window resize-sash
         else:
         if self._embeddedWindow:
             sizer.Add(self._treeCtrl, 1, wx.EXPAND|wx.BOTTOM, HALF_SPACE)  # allow space for embedded window resize-sash
         else:
@@ -1484,7 +1577,7 @@ class ProjectView(wx.lib.docview.View):
         sizer = wx.BoxSizer(wx.VERTICAL)
         
         if wx.GetApp().IsMDI():
         sizer = wx.BoxSizer(wx.VERTICAL)
         
         if wx.GetApp().IsMDI():
-            sizer.Add(panel, 1, wx.EXPAND|wx.BOTTOM, 1) # wx.Bug: without bottom margin, can't resize embedded window
+            sizer.Add(panel, 1, wx.EXPAND|wx.BOTTOM, 3) # wxBug: without bottom margin, can't resize embedded window
         else:
             sizer.Add(panel, 1, wx.EXPAND)
             
         else:
             sizer.Add(panel, 1, wx.EXPAND)
             
@@ -1503,16 +1596,17 @@ class ProjectView(wx.lib.docview.View):
         wx.EVT_TREE_BEGIN_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginLabelEdit)
         wx.EVT_TREE_END_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndLabelEdit)
         wx.EVT_RIGHT_DOWN(self._treeCtrl, self.OnRightClick)
         wx.EVT_TREE_BEGIN_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginLabelEdit)
         wx.EVT_TREE_END_LABEL_EDIT(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndLabelEdit)
         wx.EVT_RIGHT_DOWN(self._treeCtrl, self.OnRightClick)
-        wx.EVT_LEFT_DOWN(self._treeCtrl, self.OnLeftClick)
         wx.EVT_KEY_DOWN(self._treeCtrl, self.OnKeyPressed)
         wx.EVT_TREE_ITEM_COLLAPSED(self._treeCtrl, self._treeCtrl.GetId(), self.SaveFolderState)
         wx.EVT_TREE_ITEM_EXPANDED(self._treeCtrl, self._treeCtrl.GetId(), self.SaveFolderState)
         wx.EVT_KEY_DOWN(self._treeCtrl, self.OnKeyPressed)
         wx.EVT_TREE_ITEM_COLLAPSED(self._treeCtrl, self._treeCtrl.GetId(), self.SaveFolderState)
         wx.EVT_TREE_ITEM_EXPANDED(self._treeCtrl, self._treeCtrl.GetId(), self.SaveFolderState)
-        # wx.EVT_COMMAND_RIGHT_CLICK(self._treeCtrl, self._treeCtrl.GetId(), self.OnRightClick) # wxBug: This isn't working for some reason
+        wx.EVT_TREE_BEGIN_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnBeginDrag)
+        wx.EVT_TREE_END_DRAG(self._treeCtrl, self._treeCtrl.GetId(), self.OnEndDrag)
+        wx.EVT_LEFT_DOWN(self._treeCtrl, self.OnLeftClick)
 
         # drag-and-drop support
         dt = ProjectFileDropTarget(self)
         self._treeCtrl.SetDropTarget(dt)
 
         # drag-and-drop support
         dt = ProjectFileDropTarget(self)
         self._treeCtrl.SetDropTarget(dt)
-
+        
         return True
 
 
         return True
 
 
@@ -1625,9 +1719,11 @@ class ProjectView(wx.lib.docview.View):
         else:
             # need this to accelerate closing down app if treeCtrl has lots of items
             self._treeCtrl.Freeze()
         else:
             # need this to accelerate closing down app if treeCtrl has lots of items
             self._treeCtrl.Freeze()
-            rootItem = self._treeCtrl.GetRootItem()
-            self._treeCtrl.DeleteChildren(rootItem)
-            self._treeCtrl.Thaw()
+            try:
+                rootItem = self._treeCtrl.GetRootItem()
+                self._treeCtrl.DeleteChildren(rootItem)
+            finally:
+                self._treeCtrl.Thaw()
 
         # We don't need to delete the window since it is a floater/embedded
         return True
 
         # We don't need to delete the window since it is a floater/embedded
         return True
@@ -1638,7 +1734,8 @@ class ProjectView(wx.lib.docview.View):
 
 
     def OnUpdate(self, sender = None, hint = None):
 
 
     def OnUpdate(self, sender = None, hint = None):
-        wx.lib.docview.View.OnUpdate(self, sender, hint)
+        if wx.lib.docview.View.OnUpdate(self, sender, hint):
+            return
         
         if hint:
             if hint[0] == "add":
         
         if hint:
             if hint[0] == "add":
@@ -1648,54 +1745,56 @@ class ProjectView(wx.lib.docview.View):
                     
                 self._treeCtrl.Freeze()
 
                     
                 self._treeCtrl.Freeze()
 
-                newFilePaths = hint[2]  # need to be added and selected, and sorted
-                oldFilePaths = hint[3]  # need to be selected
-                self._treeCtrl.UnselectAll()
-                
-                mode = self.GetMode()
-                
-                project = projectDoc.GetModel()
-                projectDir = project.homeDir
-                rootItem = self._treeCtrl.GetRootItem()
+                try:
+                    newFilePaths = hint[2]  # need to be added and selected, and sorted
+                    oldFilePaths = hint[3]  # need to be selected
+                    self._treeCtrl.UnselectAll()
                     
                     
-                # add new folders and new items
-                addList = []                    
-                for filePath in newFilePaths:
-                    file = project.FindFile(filePath)
-                    if file:
-                        if mode == ProjectView.LOGICAL_MODE:
-                            folderPath = file.logicalFolder
-                        else:  # ProjectView.PHYSICAL_MODE
-                            folderPath = file.GetRelativeFolder(projectDir)
-                        if folderPath:
-                            self._treeCtrl.AddFolder(folderPath)
-                            folder = self._treeCtrl.FindFolder(folderPath)
-                        else:
-                            folder = rootItem
-                        item = self._treeCtrl.AppendItem(folder, os.path.basename(file.filePath), file)
-                        addList.append(item)
-
-                # sort folders with new items
-                parentList = []
-                for item in addList:
-                    parentItem = self._treeCtrl.GetItemParent(item)
-                    if parentItem not in parentList:
-                        parentList.append(parentItem)
-                for parentItem in parentList:
-                    self._treeCtrl.SortChildren(parentItem)
-
-                # select all the items user wanted to add
-                lastItem = None
-                for filePath in (oldFilePaths + newFilePaths):
-                    item = self._treeCtrl.FindItem(filePath)
-                    if item:
-                        self._treeCtrl.SelectItem(item)
-                        lastItem = item
+                    mode = self.GetMode()
+                    
+                    project = projectDoc.GetModel()
+                    projectDir = project.homeDir
+                    rootItem = self._treeCtrl.GetRootItem()
                         
                         
-                if lastItem:        
-                    self._treeCtrl.EnsureVisible(lastItem)
+                    # add new folders and new items
+                    addList = []                    
+                    for filePath in newFilePaths:
+                        file = project.FindFile(filePath)
+                        if file:
+                            if mode == ProjectView.LOGICAL_MODE:
+                                folderPath = file.logicalFolder
+                            else:  # ProjectView.PHYSICAL_MODE
+                                folderPath = file.physicalFolder
+                            if folderPath:
+                                self._treeCtrl.AddFolder(folderPath)
+                                folder = self._treeCtrl.FindFolder(folderPath)
+                            else:
+                                folder = rootItem
+                            item = self._treeCtrl.AppendItem(folder, os.path.basename(file.filePath), file)
+                            addList.append(item)
+    
+                    # sort folders with new items
+                    parentList = []
+                    for item in addList:
+                        parentItem = self._treeCtrl.GetItemParent(item)
+                        if parentItem not in parentList:
+                            parentList.append(parentItem)
+                    for parentItem in parentList:
+                        self._treeCtrl.SortChildren(parentItem)
+    
+                    # select all the items user wanted to add
+                    lastItem = None
+                    for filePath in (oldFilePaths + newFilePaths):
+                        item = self._treeCtrl.FindItem(filePath)
+                        if item:
+                            self._treeCtrl.SelectItem(item)
+                            lastItem = item
+                            
+                    if lastItem:        
+                        self._treeCtrl.EnsureVisible(lastItem)
 
 
-                self._treeCtrl.Thaw()
+                finally:
+                    self._treeCtrl.Thaw()
                 return
 
             elif hint[0] == "remove":
                 return
 
             elif hint[0] == "remove":
@@ -1705,17 +1804,19 @@ class ProjectView(wx.lib.docview.View):
                     
                 self._treeCtrl.Freeze()
 
                     
                 self._treeCtrl.Freeze()
 
-                filePaths = hint[2]
-                self._treeCtrl.UnselectAll()
-                
-                for filePath in filePaths:
-                    item = self._treeCtrl.FindItem(filePath)
-                    if item:
-                        self._treeCtrl.Delete(item)
-
-                self._treeCtrl.UnselectAll()  # wxBug: even though we unselected earlier, an item still gets selected after the delete
+                try:
+                    filePaths = hint[2]
+                    self._treeCtrl.UnselectAll()
+                    
+                    for filePath in filePaths:
+                        item = self._treeCtrl.FindItem(filePath)
+                        if item:
+                            self._treeCtrl.Delete(item)
+    
+                    self._treeCtrl.UnselectAll()  # wxBug: even though we unselected earlier, an item still gets selected after the delete
                 
                 
-                self._treeCtrl.Thaw()
+                finally:
+                    self._treeCtrl.Thaw()
                 return
                 
             elif hint[0] == "rename":
                 return
                 
             elif hint[0] == "rename":
@@ -1724,10 +1825,12 @@ class ProjectView(wx.lib.docview.View):
                     return
                     
                 self._treeCtrl.Freeze()
                     return
                     
                 self._treeCtrl.Freeze()
-                item = self._treeCtrl.FindItem(hint[2])
-                self._treeCtrl.SetItemText(item, os.path.basename(hint[3]))
-                self._treeCtrl.EnsureVisible(item)
-                self._treeCtrl.Thaw()
+                try:
+                    item = self._treeCtrl.FindItem(hint[2])
+                    self._treeCtrl.SetItemText(item, os.path.basename(hint[3]))
+                    self._treeCtrl.EnsureVisible(item)
+                finally:
+                    self._treeCtrl.Thaw()
                 return
                 
             elif hint[0] == "rename folder":
                 return
                 
             elif hint[0] == "rename folder":
@@ -1736,14 +1839,16 @@ class ProjectView(wx.lib.docview.View):
                     return
                     
                 self._treeCtrl.Freeze()
                     return
                     
                 self._treeCtrl.Freeze()
-                item = self._treeCtrl.FindFolder(hint[2])
-                if item:
-                    self._treeCtrl.UnselectAll()
-                    self._treeCtrl.SetItemText(item, os.path.basename(hint[3]))
-                    self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
-                    self._treeCtrl.SelectItem(item)
-                    self._treeCtrl.EnsureVisible(item)
-                self._treeCtrl.Thaw()
+                try:
+                    item = self._treeCtrl.FindFolder(hint[2])
+                    if item:
+                        self._treeCtrl.UnselectAll()
+                        self._treeCtrl.SetItemText(item, os.path.basename(hint[3]))
+                        self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
+                        self._treeCtrl.SelectItem(item)
+                        self._treeCtrl.EnsureVisible(item)
+                finally:
+                    self._treeCtrl.Thaw()
                 return
      
 
                 return
      
 
@@ -1776,9 +1881,23 @@ class ProjectView(wx.lib.docview.View):
     def ProcessEvent(self, event):
         id = event.GetId()
         if id == ProjectService.CLOSE_PROJECT_ID:
     def ProcessEvent(self, event):
         id = event.GetId()
         if id == ProjectService.CLOSE_PROJECT_ID:
-            document = self.GetDocument()
-            if document:
-                if self.GetDocumentManager().CloseDocument(document, False):
+            projectDoc = self.GetDocument()
+            if projectDoc:
+                projectService = wx.GetApp().GetService(ProjectService)
+                if projectService:
+                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                    for openDoc in openDocs[:]:  # need to make a copy, as each file closes we're off by one
+                        if projectDoc == openDoc:  # close project last
+                            continue
+                            
+                        if projectDoc == projectService.FindProjectFromMapping(openDoc):
+                            self.GetDocumentManager().CloseDocument(openDoc, False)
+                            
+                            projectService.RemoveProjectMapping(openDoc)
+                            if hasattr(openDoc, "GetModel"):
+                                projectService.RemoveProjectMapping(openDoc.GetModel())
+                    
+                if self.GetDocumentManager().CloseDocument(projectDoc, False):
                     self.RemoveCurrentDocumentUpdate()
             return True
         elif id == ProjectService.ADD_FILES_TO_PROJECT_ID:
                     self.RemoveCurrentDocumentUpdate()
             return True
         elif id == ProjectService.ADD_FILES_TO_PROJECT_ID:
@@ -1881,6 +2000,19 @@ class ProjectView(wx.lib.docview.View):
             return True
         elif (id == wx.ID_CLEAR
         or id == ProjectService.RENAME_ID):
             return True
         elif (id == wx.ID_CLEAR
         or id == ProjectService.RENAME_ID):
+            items = self._treeCtrl.GetSelections()
+            if items:
+                hasViewSelected = False
+                for item in items:
+                    if self._IsItemFile(item):
+                        file = self._GetItemFile(item)
+                        if file.type == 'xform':
+                            hasViewSelected = True
+                            break
+                if hasViewSelected:
+                    event.Enable(False)
+                    return True
+
             event.Enable(self._HasFilesSelected() or (self.GetDocument() != None and self.GetMode() == ProjectView.LOGICAL_MODE and self._HasFoldersSelected()))
             return True
         elif id == wx.ID_PASTE:
             event.Enable(self._HasFilesSelected() or (self.GetDocument() != None and self.GetMode() == ProjectView.LOGICAL_MODE and self._HasFoldersSelected()))
             return True
         elif id == wx.ID_PASTE:
@@ -1979,61 +2111,70 @@ class ProjectView(wx.lib.docview.View):
         wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
         self._treeCtrl.Freeze()
 
         wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
         self._treeCtrl.Freeze()
 
-        rootItem = self._treeCtrl.GetRootItem()
-        self._treeCtrl.DeleteChildren(rootItem)
-            
-        if document:
-            mode = self.GetMode()
-            docFilePath = document.GetFilename()
+        try:
+            rootItem = self._treeCtrl.GetRootItem()
+            self._treeCtrl.DeleteChildren(rootItem)
             
             
-            if mode == ProjectView.LOGICAL_MODE:
-                folders = document.GetModel().logicalFolders
-            else:
-                folders = document.GetModel().GetRelativeFolders()
+            if document:
+                mode = self.GetMode()
+                docFilePath = document.GetFilename()
                 
                 
-            folders.sort()
-            folderItems = []
-            for folderPath in folders:
-                folderItems = folderItems + self._treeCtrl.AddFolder(folderPath)
-                                        
-            for file in document.GetModel()._files:
                 if mode == ProjectView.LOGICAL_MODE:
                 if mode == ProjectView.LOGICAL_MODE:
-                    folder = file.logicalFolder
+                    folders = document.GetModel().logicalFolders
                 else:
                 else:
-                    folder = file.GetRelativeFolder(document.GetModel().homeDir)
-                if folder:
-                    folderTree = folder.split('/')
-                
-                    item = rootItem
-                    for folderName in folderTree:
-                        found = False
-                        (child, cookie) = self._treeCtrl.GetFirstChild(item)
-                        while child.IsOk():
-                            if self._treeCtrl.GetItemText(child) == folderName:
-                                item = child 
-                                found = True
+                    folders = document.GetModel().physicalFolders
+                    
+                folders.sort()
+                folderItems = []
+                for folderPath in folders:
+                    folderItems = folderItems + self._treeCtrl.AddFolder(folderPath)
+                                            
+                for file in document.GetModel()._files:
+                    if mode == ProjectView.LOGICAL_MODE:
+                        folder = file.logicalFolder
+                    else:
+                        folder = file.physicalFolder
+                    if folder:
+                        folderTree = folder.split('/')
+                    
+                        item = rootItem
+                        for folderName in folderTree:
+                            found = False
+                            (child, cookie) = self._treeCtrl.GetFirstChild(item)
+                            while child.IsOk():
+                                if self._treeCtrl.GetItemText(child) == folderName:
+                                    item = child 
+                                    found = True
+                                    break
+                                (child, cookie) = self._treeCtrl.GetNextChild(item, cookie)
+                                
+                            if not found:
+                                print "error folder '%s' not found for %s" % (folder, file.filePath)
                                 break
                                 break
-                            (child, cookie) = self._treeCtrl.GetNextChild(item, cookie)
-                            
-                        if not found:
-                            print "error folder '%s' not found for %s" % (folder, file.filePath)
-                            break
-                else:
-                    item = rootItem
+                    else:
+                        item = rootItem
+                        
+                    fileItem = self._treeCtrl.AppendItem(item, os.path.basename(file.filePath), file)
                     
                     
-                fileItem = self._treeCtrl.AppendItem(item, os.path.basename(file.filePath), file)
+                self._treeCtrl.SortChildren(rootItem)
+                for item in folderItems:
+                    self._treeCtrl.SortChildren(item)
+    
+                self.LoadFolderState()
+    
+                self._treeCtrl.SetFocus()
+                (child, cookie) = self._treeCtrl.GetFirstChild(self._treeCtrl.GetRootItem())
+                if child.IsOk():
+                    self._treeCtrl.UnselectAll()
+                    self._treeCtrl.SelectItem(child)
+                    self._treeCtrl.ScrollTo(child)
                 
                 
-            self._treeCtrl.SortChildren(rootItem)
-            for item in folderItems:
-                self._treeCtrl.SortChildren(item)
-
-            self.LoadFolderState()
-
-            if self._embeddedWindow:
-                document.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame()))
+                if self._embeddedWindow:
+                    document.GetCommandProcessor().SetEditMenu(wx.GetApp().GetEditMenu(self._GetParentFrame()))
 
 
-        self._treeCtrl.Thaw()
-        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+        finally:
+            self._treeCtrl.Thaw()
+            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
 
     def ProjectHasFocus(self):
 
 
     def ProjectHasFocus(self):
@@ -2053,6 +2194,11 @@ class ProjectView(wx.lib.docview.View):
         return False
 
 
         return False
 
 
+    def ClearFolderState(self):
+        config = wx.ConfigBase_Get()
+        config.DeleteGroup(getProjectKeyName(self.GetDocument().GetFilename()))
+        
+
     def SaveFolderState(self, event=None):
         """ Save the open/close state of folders """
 
     def SaveFolderState(self, event=None):
         """ Save the open/close state of folders """
 
@@ -2070,25 +2216,32 @@ class ProjectView(wx.lib.docview.View):
 
 
     def LoadFolderState(self):
 
 
     def LoadFolderState(self):
-        """ Load the open/close state of folders """
+        """ Load the open/close state of folders. """
         self._loading = True
         self._loading = True
-        
+      
         config = wx.ConfigBase_Get()
         config = wx.ConfigBase_Get()
-        openFolderData = config.Read(getProjectKeyName(self.GetDocument().GetFilename(), self.GetMode()))
+        openFolderData = config.Read(getProjectKeyName(self.GetDocument().GetFilename(), self.GetMode()), "")
         if openFolderData:
             folderList = eval(openFolderData)
                 
             folderItemList = self._GetFolderItems(self._treeCtrl.GetRootItem())
             for item in folderItemList:
         if openFolderData:
             folderList = eval(openFolderData)
                 
             folderItemList = self._GetFolderItems(self._treeCtrl.GetRootItem())
             for item in folderItemList:
-                f = self._GetItemFolderPath(item)
-                if f in folderList:
+                folderPath = self._GetItemFolderPath(item)
+                if folderPath in folderList:
                     self._treeCtrl.Expand(item)
                     self._treeCtrl.Expand(item)
-##                else:  # not needed, initial state is collapsed
-##                    self._treeCtrl.Collapse(item)
-        else:  # default is to open all folders    
+                else:
+                    self._treeCtrl.Collapse(item)
+
+        else:
+            projectService = wx.GetApp().GetService(ProjectService)
+            
             folderItemList = self._GetFolderItems(self._treeCtrl.GetRootItem())
             for item in folderItemList:
             folderItemList = self._GetFolderItems(self._treeCtrl.GetRootItem())
             for item in folderItemList:
-                self._treeCtrl.Expand(item)
+                folderPath = self._GetItemFolderPath(item)
+                if projectService.FindLogicalViewFolderCollapsedDefault(folderPath):  # get default initial state
+                    self._treeCtrl.Collapse(item)
+                else:
+                    self._treeCtrl.Expand(item)
             
         self._loading = False
 
             
         self._loading = False
 
@@ -2190,7 +2343,7 @@ class ProjectView(wx.lib.docview.View):
                     if len(descr) > 0:
                         descr = descr + _('|')
                     descr = descr + temp.GetDescription() + _(" (") + temp.GetFileFilter() + _(") |") + temp.GetFileFilter()  # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
                     if len(descr) > 0:
                         descr = descr + _('|')
                     descr = descr + temp.GetDescription() + _(" (") + temp.GetFileFilter() + _(") |") + temp.GetFileFilter()  # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
-            descr = _("All (*.*)|*.*|%s") % descr # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
+            descr = _("All|*.*|%s") % descr # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
         else:
             descr = _("*.*")
 
         else:
             descr = _("*.*")
 
@@ -2254,7 +2407,7 @@ class ProjectView(wx.lib.docview.View):
                 descr = descr + _('|')
             descr = template.GetDescription() + _(" (") + template.GetFileFilter() + _(")")
             choices.append(descr)
                 descr = descr + _('|')
             descr = template.GetDescription() + _(" (") + template.GetFileFilter() + _(")")
             choices.append(descr)
-        choices.insert(0, _("All (*.*)"))  # first item
+        choices.insert(0, _("All"))  # first item
         filterChoice = wx.Choice(frame, -1, size=(250, -1), choices=choices)
         filterChoice.SetSelection(0)
         filterChoice.SetToolTipString(_("Select file type filter."))
         filterChoice = wx.Choice(frame, -1, size=(250, -1), choices=choices)
         filterChoice.SetSelection(0)
         filterChoice.SetToolTipString(_("Select file type filter."))
@@ -2301,63 +2454,54 @@ class ProjectView(wx.lib.docview.View):
         if status == wx.ID_OK:
             wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
 
         if status == wx.ID_OK:
             wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
 
-            doc = self.GetDocument()
-            searchSubfolders = subfolderCtrl.IsChecked()
-            dirString = dirCtrl.GetValue()
-
-            if os.path.isfile(dirString):
-                # If they pick a file explicitly, we won't prevent them from adding it even if it doesn't match the filter.
-                # We'll assume they know what they're doing.
-                paths = [dirString]
-            else:
-                paths = []
-
-                index = filterChoice.GetSelection()
-                lastIndex = filterChoice.GetCount()-1
-                if index and index != lastIndex:  # if not All or Any
-                    template = visibleTemplates[index-1]
-
-                # do search in files on disk
-                for root, dirs, files in os.walk(dirString):
-                    if not searchSubfolders and root != dirString:
-                        break
-
-                    for name in files:
-                        if index == 0:  # All
-                            for template in visibleTemplates:
-                                if template.FileMatchesTemplate(name):
-                                    filename = os.path.join(root, name)
-
-                                    # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
-                                    if doc.IsFileInProject(filename):
-                                        break
-
-                                    paths.append(filename)
-                                    break
-                        elif index == lastIndex:  # Any
-                            filename = os.path.join(root, name)
-                            # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
-                            if not doc.IsFileInProject(filename):
-                                paths.append(filename)
-                        else:  # use selected filter
-                            if template.FileMatchesTemplate(name):
+            try:
+                doc = self.GetDocument()
+                searchSubfolders = subfolderCtrl.IsChecked()
+                dirString = dirCtrl.GetValue()
+    
+                if os.path.isfile(dirString):
+                    # If they pick a file explicitly, we won't prevent them from adding it even if it doesn't match the filter.
+                    # We'll assume they know what they're doing.
+                    paths = [dirString]
+                else:
+                    paths = []
+    
+                    index = filterChoice.GetSelection()
+                    lastIndex = filterChoice.GetCount()-1
+                    if index and index != lastIndex:  # if not All or Any
+                        template = visibleTemplates[index-1]
+    
+                    # do search in files on disk
+                    for root, dirs, files in os.walk(dirString):
+                        if not searchSubfolders and root != dirString:
+                            break
+    
+                        for name in files:
+                            if index == 0:  # All
                                 filename = os.path.join(root, name)
                                 # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
                                 if not doc.IsFileInProject(filename):
                                     paths.append(filename)
                                 filename = os.path.join(root, name)
                                 # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
                                 if not doc.IsFileInProject(filename):
                                     paths.append(filename)
+                            else:  # use selected filter
+                                if template.FileMatchesTemplate(name):
+                                    filename = os.path.join(root, name)
+                                    # if already in project, don't add it, otherwise undo will remove it from project even though it was already in it.
+                                    if not doc.IsFileInProject(filename):
+                                        paths.append(filename)
+    
+                folderPath = None
+                if self.GetMode() == ProjectView.LOGICAL_MODE:
+                    selections = self._treeCtrl.GetSelections()
+                    if selections:
+                        item = selections[0]
+                        if not self._IsItemFile(item):
+                            folderPath = self._GetItemFolderPath(item)
 
 
-            folderPath = None
-            if self.GetMode() == ProjectView.LOGICAL_MODE:
-                selections = self._treeCtrl.GetSelections()
-                if selections:
-                    item = selections[0]
-                    if not self._IsItemFile(item):
-                        folderPath = self._GetItemFolderPath(item)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-
-            doc.GetCommandProcessor().Submit(ProjectAddFilesCommand(doc, paths, folderPath=folderPath))
-            self.Activate()  # after add, should put focus on project editor
+                doc.GetCommandProcessor().Submit(ProjectAddFilesCommand(doc, paths, folderPath=folderPath))
+                self.Activate()  # after add, should put focus on project editor
+                
+            finally:
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
 
     def DoAddFilesToProject(self, filePaths, folderPath):
 
 
     def DoAddFilesToProject(self, filePaths, folderPath):
@@ -2385,23 +2529,19 @@ class ProjectView(wx.lib.docview.View):
 
 
     def OnLeftClick(self, event):
 
 
     def OnLeftClick(self, event):
-        """ wxBug: If tree has selection, but focus is in another window, single click in tree should do
-            single selection of item at mouse position.  But what it does is just put focus back into the
-            window and all items go from inactive selection to active selection.  Another click on the item
-            either activates double-click or edit for that item.  This behavior is odd.
-            
-            This fix makes gives the tree view the focus and makes the item under the mouse position the
-            only active selection, as expected.
-        """
-        if not self.ProjectHasFocus() and not self.FilesHasFocus() and not event.ShiftDown() and not event.ControlDown() and not event.MetaDown():
-            self._treeCtrl.UnselectAll()
-            event.Skip()
+        """ 
+            wxBug: We also spurious drag events on a single click of on item that is already selected,
+            so the solution was to consume the left click event.  But his broke the single click expand/collapse
+            of a folder, so if it is a folder, we do an event.Skip() to allow the expand/collapse,
+            otherwise we consume the event.
+        """            
+        # if folder let it collapse/expand
+        if wx.Platform == '__WXMSW__':
             item, flags = self._treeCtrl.HitTest(event.GetPosition())
             item, flags = self._treeCtrl.HitTest(event.GetPosition())
-            self._treeCtrl.SelectItem(item)
-            return
-            
-        event.Skip()
-
+            if item.IsOk() and self._treeCtrl.GetChildrenCount(item, False):
+                event.Skip()
+        else:
+            event.Skip()
 
     def OnRightClick(self, event):
         self.Activate()
 
     def OnRightClick(self, event):
         self.Activate()
@@ -2444,17 +2584,39 @@ class ProjectView(wx.lib.docview.View):
             if not itemID:
                 menu.AppendSeparator()
             else:
             if not itemID:
                 menu.AppendSeparator()
             else:
-                if itemID == ProjectService.RUN_SELECTED_PM_ID:
-                    menu.Append(ProjectService.RUN_SELECTED_PM_ID, _("Run Process"))
-                    wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_ID, self.OnRunSelectedPM)
+                if itemID == ProjectService.RUN_SELECTED_PM_ID and not ACTIVEGRID_BASE_IDE:
+                    webBrowserService = wx.GetApp().GetService(WebBrowserService.WebBrowserService)
+                    if webBrowserService:
+                        if wx.Platform == '__WXMSW__':
+                            menu.Append(ProjectService.RUN_SELECTED_PM_ID, _("Run Process"))
+                            wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_ID, self.ProjectServiceProcessEvent)
+
+                        if wx.Platform == '__WXMSW__':
+                            menuLabel = _("Run Process in External Browser")
+                        else:
+                            menuLabel = _("Run Process")
+                        menu.Append(ProjectService.RUN_SELECTED_PM_EXTERNAL_BROWSER_ID, menuLabel)
+                        wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_EXTERNAL_BROWSER_ID, self.ProjectServiceProcessEvent)
+                        
+                        if wx.Platform == '__WXMSW__':
+    
+                            if wx.GetApp().GetUseTabbedMDI():
+                                menuLabel = _("Run Process in new Tab")
+                            else:
+                                menuLabel = _("Run Process in new Window")
+                            menu.Append(ProjectService.RUN_SELECTED_PM_INTERNAL_WINDOW_ID, menuLabel)
+                            wx.EVT_MENU(self._GetParentFrame(), ProjectService.RUN_SELECTED_PM_INTERNAL_WINDOW_ID, self.ProjectServiceProcessEvent)
+                        
                 elif itemID == ProjectService.REMOVE_FROM_PROJECT:
                     menu.Append(ProjectService.REMOVE_FROM_PROJECT, _("Remove Selected Files from Project"))
                     wx.EVT_MENU(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self.OnClear)
                     wx.EVT_UPDATE_UI(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self._GetParentFrame().ProcessUpdateUIEvent)
                 else:
                 elif itemID == ProjectService.REMOVE_FROM_PROJECT:
                     menu.Append(ProjectService.REMOVE_FROM_PROJECT, _("Remove Selected Files from Project"))
                     wx.EVT_MENU(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self.OnClear)
                     wx.EVT_UPDATE_UI(self._GetParentFrame(), ProjectService.REMOVE_FROM_PROJECT, self._GetParentFrame().ProcessUpdateUIEvent)
                 else:
-                    svnService = wx.GetApp().GetService(SVNService.SVNService)
                     item = menuBar.FindItemById(itemID)
                     if item:
                     item = menuBar.FindItemById(itemID)
                     if item:
+                        if SVN_INSTALLED:
+                            svnService = wx.GetApp().GetService(SVNService.SVNService)
+                            
                         if itemID in svnIDs:
                             if SVN_INSTALLED and svnService:
                                 wx.EVT_MENU(self._GetParentFrame(), itemID, svnService.ProcessEvent)
                         if itemID in svnIDs:
                             if SVN_INSTALLED and svnService:
                                 wx.EVT_MENU(self._GetParentFrame(), itemID, svnService.ProcessEvent)
@@ -2467,21 +2629,35 @@ class ProjectView(wx.lib.docview.View):
         menu.Destroy()
 
 
         menu.Destroy()
 
 
-    def OnRunSelectedPM(self, event):
+    def ProjectServiceProcessEvent(self, event):
         projectService = wx.GetApp().GetService(ProjectService)
         if projectService:
         projectService = wx.GetApp().GetService(ProjectService)
         if projectService:
-            projectService.OnRunProcessModel(event, runSelected=True)
+            projectService.ProcessEvent(event)
 
 
     def OnRename(self, event=None):
         items = self._treeCtrl.GetSelections()
 
 
     def OnRename(self, event=None):
         items = self._treeCtrl.GetSelections()
-        if items:
-            self._treeCtrl.EditLabel(items[0])
+        if not items:
+            return
+        item = items[0]
+        if wx.Platform == "__WXGTK__":
+            dlg = wx.TextEntryDialog(self.GetFrame(), _("Enter New Name"), _("Enter New Name"))
+            dlg.CenterOnParent()
+            if dlg.ShowModal() == wx.ID_OK:
+                text = dlg.GetValue()
+                self.ChangeLabel(item, text)
+        else:
+            if items:
+                self._treeCtrl.EditLabel(item)
 
 
     def OnBeginLabelEdit(self, event):
         self._editingSoDontKillFocus = True
         item = event.GetItem()
 
 
     def OnBeginLabelEdit(self, event):
         self._editingSoDontKillFocus = True
         item = event.GetItem()
+        if self._IsItemFile(item):
+            file = self._GetItemFile(item)
+            if file.type == 'xform':
+                event.Veto()
         if (self.GetMode() == ProjectView.PHYSICAL_MODE) and not self._IsItemFile(item):
             event.Veto()
 
         if (self.GetMode() == ProjectView.PHYSICAL_MODE) and not self._IsItemFile(item):
             event.Veto()
 
@@ -2490,16 +2666,19 @@ class ProjectView(wx.lib.docview.View):
         self._editingSoDontKillFocus = False
         item = event.GetItem()
         newName = event.GetLabel()
         self._editingSoDontKillFocus = False
         item = event.GetItem()
         newName = event.GetLabel()
-        if not newName:
+        if not self.ChangeLabel(item, newName):
             event.Veto()
             event.Veto()
-            return
+            
+
+    def ChangeLabel(self, item, newName):
+        if not newName:
+            return False
         if self._IsItemFile(item):
             oldFilePath = self._GetItemFilePath(item)
             newFilePath = os.path.join(os.path.dirname(oldFilePath), newName)
             doc = self.GetDocument()
             if not doc.GetCommandProcessor().Submit(ProjectRenameFileCommand(doc, oldFilePath, newFilePath)):
         if self._IsItemFile(item):
             oldFilePath = self._GetItemFilePath(item)
             newFilePath = os.path.join(os.path.dirname(oldFilePath), newName)
             doc = self.GetDocument()
             if not doc.GetCommandProcessor().Submit(ProjectRenameFileCommand(doc, oldFilePath, newFilePath)):
-                event.Veto()
-                return
+                return False
             self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
         else:
             oldFolderPath = self._GetItemFolderPath(item)
             self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
         else:
             oldFolderPath = self._GetItemFolderPath(item)
@@ -2512,26 +2691,25 @@ class ProjectView(wx.lib.docview.View):
                             "Rename Folder",
                             wx.OK | wx.ICON_EXCLAMATION,
                             self.GetFrame())
                             "Rename Folder",
                             wx.OK | wx.ICON_EXCLAMATION,
                             self.GetFrame())
-                event.Veto()
-                return
+                return False
             doc = self.GetDocument()
             if not doc.GetCommandProcessor().Submit(ProjectRenameFolderCommand(doc, oldFolderPath, newFolderPath)):
             doc = self.GetDocument()
             if not doc.GetCommandProcessor().Submit(ProjectRenameFolderCommand(doc, oldFolderPath, newFolderPath)):
-                event.Veto()
-                return
+                return False
             self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
 
             self._treeCtrl.SortChildren(self._treeCtrl.GetItemParent(item))
 
+        return True
+        
 
     def CanPaste(self):
         # wxBug: Should be able to use IsSupported/IsSupportedFormat here
         #fileDataObject = wx.FileDataObject()
         #hasFilesInClipboard = wx.TheClipboard.IsSupportedFormat(wx.FileDataObject)
 
     def CanPaste(self):
         # wxBug: Should be able to use IsSupported/IsSupportedFormat here
         #fileDataObject = wx.FileDataObject()
         #hasFilesInClipboard = wx.TheClipboard.IsSupportedFormat(wx.FileDataObject)
+        hasFilesInClipboard = False
         if not wx.TheClipboard.IsOpened():
             if wx.TheClipboard.Open():
                 fileDataObject = wx.FileDataObject()
                 hasFilesInClipboard = wx.TheClipboard.GetData(fileDataObject)
                 wx.TheClipboard.Close()
         if not wx.TheClipboard.IsOpened():
             if wx.TheClipboard.Open():
                 fileDataObject = wx.FileDataObject()
                 hasFilesInClipboard = wx.TheClipboard.GetData(fileDataObject)
                 wx.TheClipboard.Close()
-        else:
-            hasFilesInClipboard = False
         return hasFilesInClipboard
 
 
         return hasFilesInClipboard
 
 
@@ -2660,7 +2838,12 @@ class ProjectView(wx.lib.docview.View):
 
         if closeFiles or delFiles:
             filesInProject = doc.GetFiles()
 
         if closeFiles or delFiles:
             filesInProject = doc.GetFiles()
-            filesInProject.append(self.GetDocument().GetDeploymentFilepath())  # remove deployment file also.
+            deploymentFilePath = self.GetDocument().GetDeploymentFilepath()
+            if deploymentFilePath:
+                filesInProject.append(deploymentFilePath)  # remove deployment file also.
+                import activegrid.server.secutils as secutils
+                keystoreFilePath = os.path.join(os.path.dirname(deploymentFilePath), secutils.AGKEYSTORE_FILENAME)
+                filesInProject.append(keystoreFilePath)  # remove keystore file also.
                 
             # don't remove self prematurely
             filePath = doc.GetFilename()
                 
             # don't remove self prematurely
             filePath = doc.GetFilename()
@@ -2705,8 +2888,10 @@ class ProjectView(wx.lib.docview.View):
                                       
         filePath = doc.GetFilename()
         
                                       
         filePath = doc.GetFilename()
         
+        self.ClearFolderState()  # remove from registry folder settings
+
         # close project
         # close project
-        if doc:
+        if doc:            
             doc.Modify(False)  # make sure it doesn't ask to save the project
             if self.GetDocumentManager().CloseDocument(doc, True):
                 self.RemoveCurrentDocumentUpdate()
             doc.Modify(False)  # make sure it doesn't ask to save the project
             if self.GetDocumentManager().CloseDocument(doc, True):
                 self.RemoveCurrentDocumentUpdate()
@@ -2813,11 +2998,7 @@ class ProjectView(wx.lib.docview.View):
                     if not doc and filepath.endswith(PROJECT_EXTENSION):  # project already open
                         self.SetProject(filepath)
                     elif doc:
                     if not doc and filepath.endswith(PROJECT_EXTENSION):  # project already open
                         self.SetProject(filepath)
                     elif doc:
-                        projectService = wx.GetApp().GetService(ProjectService)
-                        if projectService:
-                            projectService.AddProjectMapping(doc)
-                            if hasattr(doc, "GetModel"):
-                                projectService.AddProjectMapping(doc.GetModel())
+                        AddProjectMapping(doc)
                         
 
         except IOError, (code, message):
                         
 
         except IOError, (code, message):
@@ -3056,18 +3237,20 @@ class ProjectPropertiesDialog(wx.Dialog):
         tab.SetSizer(spacerGrid)
         notebook.AddPage(tab, _("Physical View"))
 
         tab.SetSizer(spacerGrid)
         notebook.AddPage(tab, _("Physical View"))
 
+        if wx.Platform == "__WXMSW__":
+            notebook.SetPageSize((310,300))
+
         if not ACTIVEGRID_BASE_IDE:
             tab = wx.Panel(notebook, -1)
             self._appInfoCtrl = PropertyService.PropertyCtrl(tab, header=False)
             self._appInfoCtrl.SetDocument(document)
             self._appInfoCtrl.SetModel(document.GetAppInfo())
             sizer = wx.BoxSizer(wx.HORIZONTAL)
         if not ACTIVEGRID_BASE_IDE:
             tab = wx.Panel(notebook, -1)
             self._appInfoCtrl = PropertyService.PropertyCtrl(tab, header=False)
             self._appInfoCtrl.SetDocument(document)
             self._appInfoCtrl.SetModel(document.GetAppInfo())
             sizer = wx.BoxSizer(wx.HORIZONTAL)
-            sizer.Add(self._appInfoCtrl, 1, wx.EXPAND)
+            sizer.Add(self._appInfoCtrl, 1, wx.EXPAND|wx.ALL, PropertyService.LEAVE_MARGIN)
             tab.SetSizer(sizer)
             notebook.AddPage(tab, _("App Info"))
             tab.SetSizer(sizer)
             notebook.AddPage(tab, _("App Info"))
+            self._appInfoCtrl._grid.AutoSizeColumns()
 
 
-        if wx.Platform == "__WXMSW__":
-            notebook.SetPageSize((310,300))
 
         sizer = wx.BoxSizer(wx.VERTICAL)
         sizer.Add(notebook, 0, wx.ALL | wx.EXPAND, SPACE)
 
         sizer = wx.BoxSizer(wx.VERTICAL)
         sizer.Add(notebook, 0, wx.ALL | wx.EXPAND, SPACE)
@@ -3092,13 +3275,13 @@ class ProjectOptionsPanel(wx.Panel):
         projectSizer.Add(self._projSaveDocsCheckBox, 0, wx.ALL, HALF_SPACE)
         if not ACTIVEGRID_BASE_IDE:
             self._projShowWelcomeCheckBox = wx.CheckBox(self, -1, _("Show Welcome Dialog"))
         projectSizer.Add(self._projSaveDocsCheckBox, 0, wx.ALL, HALF_SPACE)
         if not ACTIVEGRID_BASE_IDE:
             self._projShowWelcomeCheckBox = wx.CheckBox(self, -1, _("Show Welcome Dialog"))
-            self._projShowWelcomeCheckBox.SetValue(config.ReadInt("RunWelcomeDialog", True))
+            self._projShowWelcomeCheckBox.SetValue(config.ReadInt("RunWelcomeDialog2", True))
             projectSizer.Add(self._projShowWelcomeCheckBox, 0, wx.ALL, HALF_SPACE)
             
             sizer = wx.BoxSizer(wx.HORIZONTAL)
             sizer.Add(wx.StaticText(self, -1, _("Default language for projects:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, HALF_SPACE)
             projectSizer.Add(self._projShowWelcomeCheckBox, 0, wx.ALL, HALF_SPACE)
             
             sizer = wx.BoxSizer(wx.HORIZONTAL)
             sizer.Add(wx.StaticText(self, -1, _("Default language for projects:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, HALF_SPACE)
-            self._langCtrl = wx.Choice(self, -1, choices=deploymentlib.LANGUAGE_LIST)            
-            self._langCtrl.SetStringSelection(config.Read(APP_LAST_LANGUAGE, deploymentlib.LANGUAGE_DEFAULT))
+            self._langCtrl = wx.Choice(self, -1, choices=projectmodel.LANGUAGE_LIST)            
+            self._langCtrl.SetStringSelection(config.Read(APP_LAST_LANGUAGE, projectmodel.LANGUAGE_DEFAULT))
             self._langCtrl.SetToolTipString(_("Programming language to be used throughout the project."))
             sizer.Add(self._langCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, MAC_RIGHT_BORDER)
             projectSizer.Add(sizer, 0, wx.ALL, HALF_SPACE)
             self._langCtrl.SetToolTipString(_("Programming language to be used throughout the project."))
             sizer.Add(self._langCtrl, 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, MAC_RIGHT_BORDER)
             projectSizer.Add(sizer, 0, wx.ALL, HALF_SPACE)
@@ -3125,7 +3308,7 @@ class ProjectOptionsPanel(wx.Panel):
         config = wx.ConfigBase_Get()
         config.WriteInt("ProjectSaveDocs", self._projSaveDocsCheckBox.GetValue())
         if not ACTIVEGRID_BASE_IDE:
         config = wx.ConfigBase_Get()
         config.WriteInt("ProjectSaveDocs", self._projSaveDocsCheckBox.GetValue())
         if not ACTIVEGRID_BASE_IDE:
-            config.WriteInt("RunWelcomeDialog", self._projShowWelcomeCheckBox.GetValue())
+            config.WriteInt("RunWelcomeDialog2", self._projShowWelcomeCheckBox.GetValue())
             config.Write(APP_LAST_LANGUAGE, self._langCtrl.GetStringSelection())
 
 
             config.Write(APP_LAST_LANGUAGE, self._langCtrl.GetStringSelection())
 
 
@@ -3140,7 +3323,11 @@ class ProjectService(Service.Service):
     #----------------------------------------------------------------------------
     SHOW_WINDOW = wx.NewId()  # keep this line for each subclass, need unique ID for each Service
     RUN_SELECTED_PM_ID = wx.NewId()
     #----------------------------------------------------------------------------
     SHOW_WINDOW = wx.NewId()  # keep this line for each subclass, need unique ID for each Service
     RUN_SELECTED_PM_ID = wx.NewId()
+    RUN_SELECTED_PM_INTERNAL_WINDOW_ID = wx.NewId()
+    RUN_SELECTED_PM_EXTERNAL_BROWSER_ID = wx.NewId()
     RUN_CURRENT_PM_ID = wx.NewId()
     RUN_CURRENT_PM_ID = wx.NewId()
+    RUN_CURRENT_PM_INTERNAL_WINDOW_ID = wx.NewId()
+    RUN_CURRENT_PM_EXTERNAL_BROWSER_ID = wx.NewId()
     RENAME_ID = wx.NewId()
     OPEN_SELECTION_ID = wx.NewId()
     REMOVE_FROM_PROJECT = wx.NewId()
     RENAME_ID = wx.NewId()
     OPEN_SELECTION_ID = wx.NewId()
     REMOVE_FROM_PROJECT = wx.NewId()
@@ -3163,6 +3350,7 @@ class ProjectService(Service.Service):
         self._runHandlers = []
         self._suppressOpenProjectMessages = False
         self._logicalViewDefaults = []
         self._runHandlers = []
         self._suppressOpenProjectMessages = False
         self._logicalViewDefaults = []
+        self._logicalViewOpenDefaults = []
         self._fileTypeDefaults = []
         self._nameDefaults = []
         self._mapToProject = dict()
         self._fileTypeDefaults = []
         self._nameDefaults = []
         self._mapToProject = dict()
@@ -3230,7 +3418,7 @@ class ProjectService(Service.Service):
                 wx.EVT_MENU(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessEvent)
                 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
             if not menuBar.FindItemById(ProjectService.ADD_FOLDER_ID):
                 wx.EVT_MENU(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessEvent)
                 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID, frame.ProcessUpdateUIEvent)
             if not menuBar.FindItemById(ProjectService.ADD_FOLDER_ID):
-                projectMenu.Append(ProjectService.ADD_FOLDER_ID, _("Add Folder to Project"), _("Adds a new folder"))
+                projectMenu.Append(ProjectService.ADD_FOLDER_ID, _("New Folder"), _("Creates a new folder"))
                 wx.EVT_MENU(frame, ProjectService.ADD_FOLDER_ID, frame.ProcessEvent)
                 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_FOLDER_ID, frame.ProcessUpdateUIEvent)
             if not menuBar.FindItemById(ProjectService.CLOSE_PROJECT_ID):
                 wx.EVT_MENU(frame, ProjectService.ADD_FOLDER_ID, frame.ProcessEvent)
                 wx.EVT_UPDATE_UI(frame, ProjectService.ADD_FOLDER_ID, frame.ProcessUpdateUIEvent)
             if not menuBar.FindItemById(ProjectService.CLOSE_PROJECT_ID):
@@ -3309,6 +3497,12 @@ class ProjectService(Service.Service):
         self._mapToProject[key] = projectDoc
         
 
         self._mapToProject[key] = projectDoc
         
 
+    def RemoveProjectMapping(self, key):
+        """ Remove mapping from model or document to project.  """
+        if self._mapToProject.has_key(key):
+            del self._mapToProject[key]
+        
+
     #----------------------------------------------------------------------------
     # Default Logical View Folder Methods
     #----------------------------------------------------------------------------
     #----------------------------------------------------------------------------
     # Default Logical View Folder Methods
     #----------------------------------------------------------------------------
@@ -3324,6 +3518,18 @@ class ProjectService(Service.Service):
         return None
 
 
         return None
 
 
+    def AddLogicalViewFolderCollapsedDefault(self, folderName, collapsed=True):
+        # default is collapsed, don't add to list if collapse is True
+        if not collapsed:
+            self._logicalViewOpenDefaults.append(folderName)
+        
+
+    def FindLogicalViewFolderCollapsedDefault(self, folderName):
+        if folderName in self._logicalViewOpenDefaults:
+            return False
+        return True
+
+
     #----------------------------------------------------------------------------
     # Default File Type Methods
     #----------------------------------------------------------------------------
     #----------------------------------------------------------------------------
     # Default File Type Methods
     #----------------------------------------------------------------------------
@@ -3406,9 +3612,21 @@ class ProjectService(Service.Service):
         if id == ProjectService.RUN_SELECTED_PM_ID:
             self.OnRunProcessModel(event, runSelected=True)
             return True
         if id == ProjectService.RUN_SELECTED_PM_ID:
             self.OnRunProcessModel(event, runSelected=True)
             return True
+        elif id == ProjectService.RUN_SELECTED_PM_INTERNAL_WINDOW_ID:
+            self.OnRunProcessModel(event, runSelected=True, newWindow=True, forceInternal=True)
+            return True
+        elif id == ProjectService.RUN_SELECTED_PM_EXTERNAL_BROWSER_ID:
+            self.OnRunProcessModel(event, runSelected=True, newWindow=True, forceExternal=True)
+            return True
         elif id == ProjectService.RUN_CURRENT_PM_ID:
             self.OnRunProcessModel(event, runCurrentFile=True)
             return True
         elif id == ProjectService.RUN_CURRENT_PM_ID:
             self.OnRunProcessModel(event, runCurrentFile=True)
             return True
+        elif id == ProjectService.RUN_CURRENT_PM_INTERNAL_WINDOW_ID:
+            self.OnRunProcessModel(event, runCurrentFile=True, newWindow=True, forceInternal=True)
+            return True
+        elif id == ProjectService.RUN_CURRENT_PM_EXTERNAL_BROWSER_ID:
+            self.OnRunProcessModel(event, runCurrentFile=True, newWindow=True, forceExternal=True)
+            return True
         elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
             self.OnAddCurrentFileToProject(event)
             return True
         elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
             self.OnAddCurrentFileToProject(event)
             return True
@@ -3430,27 +3648,31 @@ class ProjectService(Service.Service):
             return True
 
         id = event.GetId()
             return True
 
         id = event.GetId()
-        if (id == ProjectService.RUN_SELECTED_PM_ID
-        or id == ProjectService.RUN_CURRENT_PM_ID):
+        if id in [ProjectService.RUN_SELECTED_PM_ID,
+        ProjectService.RUN_SELECTED_PM_INTERNAL_WINDOW_ID,
+        ProjectService.RUN_SELECTED_PM_EXTERNAL_BROWSER_ID,
+        ProjectService.RUN_CURRENT_PM_ID,
+        ProjectService.RUN_CURRENT_PM_INTERNAL_WINDOW_ID,
+        ProjectService.RUN_CURRENT_PM_EXTERNAL_BROWSER_ID]:
             event.Enable(True)
             return True
         elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
             event.Enable(self._CanAddCurrentFileToProject())
             return True
             event.Enable(True)
             return True
         elif id == ProjectService.ADD_CURRENT_FILE_TO_PROJECT_ID:
             event.Enable(self._CanAddCurrentFileToProject())
             return True
-        elif (id == ProjectService.ADD_FILES_TO_PROJECT_ID
-        or id == ProjectService.ADD_DIR_FILES_TO_PROJECT_ID
-        or id == ProjectService.RENAME_ID
-        or id == ProjectService.OPEN_SELECTION_ID
-        or id == ProjectService.DELETE_FILE_ID):
+        elif id in [ProjectService.ADD_FILES_TO_PROJECT_ID,
+        ProjectService.ADD_DIR_FILES_TO_PROJECT_ID,
+        ProjectService.RENAME_ID,
+        ProjectService.OPEN_SELECTION_ID,
+        ProjectService.DELETE_FILE_ID]:
             event.Enable(False)
             return True
         elif id == ProjectService.PROJECT_PROPERTIES_ID:
             event.Enable(self._HasOpenedProjects())
             return True
             event.Enable(False)
             return True
         elif id == ProjectService.PROJECT_PROPERTIES_ID:
             event.Enable(self._HasOpenedProjects())
             return True
-        elif (id == wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID
-        or id == ProjectService.ADD_FOLDER_ID
-        or id == ProjectService.DELETE_PROJECT_ID
-        or id == ProjectService.CLOSE_PROJECT_ID):
+        elif id in [wx.lib.pydocview.FilePropertiesService.PROPERTIES_ID,
+        ProjectService.ADD_FOLDER_ID,
+        ProjectService.DELETE_PROJECT_ID,
+        ProjectService.CLOSE_PROJECT_ID]:
             if self.GetView():
                 return self.GetView().ProcessUpdateUIEvent(event)
             else:
             if self.GetView():
                 return self.GetView().ProcessUpdateUIEvent(event)
             else:
@@ -3459,7 +3681,7 @@ class ProjectService(Service.Service):
             return False
 
 
             return False
 
 
-    def OnRunProcessModel(self, event, runSelected=False, runCurrentFile=False):
+    def OnRunProcessModel(self, event, runSelected=False, runCurrentFile=False, newWindow=False, forceExternal=False, forceInternal=False):
         project = self.GetCurrentProject()
 
         if runCurrentFile:
         project = self.GetCurrentProject()
 
         if runCurrentFile:
@@ -3537,14 +3759,19 @@ class ProjectService(Service.Service):
                 fileToRun = files[res]
             else:
                 fileToRun = files[0]
                 fileToRun = files[res]
             else:
                 fileToRun = files[0]
-
-            deployFilePath = project.GenerateDeployment()
-            self.RunProcessModel(fileToRun, project.GetAppInfo().language, deployFilePath)
+                
+            try:
+                deployFilePath = project.GenerateDeployment()
+            except DataServiceExistenceException, e:
+                dataSourceName = str(e)
+                self.PromptForMissingDataSource(dataSourceName)
+                return
+            self.RunProcessModel(fileToRun, project.GetAppInfo().language, deployFilePath, newWindow, forceExternal, forceInternal)
 
 
 
 
-    def RunProcessModel(self, fileToRun, language, deployFilePath):
+    def RunProcessModel(self, fileToRun, language, deployFilePath, newWindow=True, forceExternal=False, forceInternal=False):
         for runHandler in self.GetRunHandlers():
         for runHandler in self.GetRunHandlers():
-            if runHandler.RunProjectFile(fileToRun, language, deployFilePath):
+            if runHandler.RunProjectFile(fileToRun, language, deployFilePath, newWindow, forceExternal, forceInternal):
                 return
         os.system('"' + fileToRun + '"')
 
                 return
         os.system('"' + fileToRun + '"')
 
@@ -3623,13 +3850,24 @@ class ProjectService(Service.Service):
                     retval.append(document)
                 elif document.IsFileInProject(filename):
                     retval.append(document)
                     retval.append(document)
                 elif document.IsFileInProject(filename):
                     retval.append(document)
+                    
+        # make sure current project is first in list
+        currProject = self.GetCurrentProject()
+        if currProject and currProject in retval:
+            retval.remove(currProject)
+            retval.insert(0, currProject)
+                
         return retval
 
 
     def OnAddCurrentFileToProject(self, event):
         return retval
 
 
     def OnAddCurrentFileToProject(self, event):
-        file = self.GetDocumentManager().GetCurrentDocument().GetFilename()
-        document = self.GetView().GetDocument()
-        document.GetCommandProcessor().Submit(ProjectAddFilesCommand(document, [file]))
+        doc = self.GetDocumentManager().GetCurrentDocument()
+        file = doc.GetFilename()
+        projectDoc = self.GetView().GetDocument()
+        projectDoc.GetCommandProcessor().Submit(ProjectAddFilesCommand(projectDoc, [file]))
+
+        AddProjectMapping(doc, projectDoc)
+
         self.GetView().Activate()  # after add, should put focus on project editor
 
 
         self.GetView().Activate()  # after add, should put focus on project editor
 
 
@@ -3649,10 +3887,13 @@ class ProjectService(Service.Service):
             if docString:
                 doc = None
                 docList = eval(docString)
             if docString:
                 doc = None
                 docList = eval(docString)
+                self.GetView()._treeCtrl.Freeze()
+
                 for fileName in docList:
                     if isinstance(fileName, types.StringTypes):
                         if os.path.exists(fileName):
                             doc = self.GetDocumentManager().CreateDocument(fileName, wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE)
                 for fileName in docList:
                     if isinstance(fileName, types.StringTypes):
                         if os.path.exists(fileName):
                             doc = self.GetDocumentManager().CreateDocument(fileName, wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE)
+                self.GetView()._treeCtrl.Thaw()
 
                 if doc:
                     openedDocs = True
 
                 if doc:
                     openedDocs = True
@@ -3664,6 +3905,38 @@ class ProjectService(Service.Service):
         return openedDocs
 
 
         return openedDocs
 
 
+    def PromptForMissingDataSource(self, dataSourceName):
+        prompt = "A required Data Source '%s' was not found.  The process cannot be run without this Data Source.\n\nWould you like to configure this Data Source now?" % dataSourceName
+        msgTitle = "Unknown Data Source"
+        dataSourceMissingDlg = wx.MessageDialog(self.GetView().GetFrame(), prompt, msgTitle, wx.YES_NO|wx.ICON_QUESTION)
+        dataSourceMissingDlg.CenterOnParent()
+        if dataSourceMissingDlg.ShowModal() == wx.ID_YES:
+            dataSourceMissingDlg.Destroy()
+            self._AddDataSource(dataSourceName)
+        else:
+            dataSourceMissingDlg.Destroy()
+
+
+    def _AddDataSource(self, defaultDataSourceName=None):
+        dataSourceService = wx.GetApp().GetService(DataModelEditor.DataSourceService)
+        dsChoices = dataSourceService.getDataSourceNames()
+        dlg = DataModelEditor.AddDataSourceDialog(self.GetView().GetFrame(), 'Add Data Source', dsChoices, defaultDataSourceName)
+        dlg.CenterOnParent()
+        if dlg.ShowModal() == wx.ID_OK:
+            dataSource = dlg.GetDataSource()
+            dlg.Destroy()
+        else:
+            dlg.Destroy()
+            return False
+        if (dataSource == None):
+            wx.MessageBox(_("Error getting data source."), self._title)
+        dataSourceService.updateDataSource(dataSource)
+        if ((dsChoices == None) or (len(dsChoices) <= 0)):
+            wx.ConfigBase_Get().Write(DataModelEditor.SchemaOptionsPanel.DEFAULT_DATASOURCE_KEY, dataSource.name)
+        dataSourceService.save()
+        return True
+
+
 #----------------------------------------------------------------------------
 # Icon Bitmaps - generated by encode_bitmaps.py
 #----------------------------------------------------------------------------
 #----------------------------------------------------------------------------
 # Icon Bitmaps - generated by encode_bitmaps.py
 #----------------------------------------------------------------------------
index ea5f6b71d1ccb834073f229223c47746964aeb65..abde6fd97b8a51c4465e65ef651e745242f4b01a 100644 (file)
@@ -25,6 +25,7 @@ import keyword # for GetAutoCompleteKeywordList
 import sys # for GetAutoCompleteKeywordList
 import MessageService # for OnCheckCode
 import OutlineService
 import sys # for GetAutoCompleteKeywordList
 import MessageService # for OnCheckCode
 import OutlineService
+import FindInDirService
 from UICommon import CaseInsensitiveCompare
 try:
     import checker # for pychecker
 from UICommon import CaseInsensitiveCompare
 try:
     import checker # for pychecker
@@ -143,11 +144,13 @@ class PythonView(CodeEditor.CodeView):
         # Set cursor to Wait cursor
         wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
 
         # Set cursor to Wait cursor
         wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
 
-        # This takes a while for involved code
-        checker.checkSyntax(self.GetDocument().GetFilename(), view)
+        try:
+            # This takes a while for involved code
+            checker.checkSyntax(self.GetDocument().GetFilename(), view)
 
 
-        # Set cursor to Default cursor
-        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+        finally:
+            # Set cursor to Default cursor
+            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
 
 
     def OnJumpToFoundLine(self, event):
 
 
     def OnJumpToFoundLine(self, event):
@@ -369,8 +372,42 @@ class PythonCtrl(CodeEditor.CodeCtrl):
         self.SetKeyWords(0, string.join(keyword.kwlist))
 
 
         self.SetKeyWords(0, string.join(keyword.kwlist))
 
 
+    def CreatePopupMenu(self):
+        FINDCLASS_ID = wx.NewId()
+        FINDDEF_ID = wx.NewId()
+
+        menu = CodeEditor.CodeCtrl.CreatePopupMenu(self)
+
+        self.Bind(wx.EVT_MENU, self.OnPopFindDefinition, id=FINDDEF_ID)
+        menu.Insert(1, FINDDEF_ID, _("Find 'def'"))
+
+        self.Bind(wx.EVT_MENU, self.OnPopFindClass, id=FINDCLASS_ID)
+        menu.Insert(2, FINDCLASS_ID, _("Find 'class'"))
+
+        return menu
+
+
+    def OnPopFindDefinition(self, event):
+        view = wx.GetApp().GetDocumentManager().GetCurrentView()
+        if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
+            pattern = view.GetCtrl().GetSelectedText().strip()
+            if pattern:
+                searchPattern = "def\s+%s" % pattern
+                wx.GetApp().GetService(FindInDirService.FindInDirService).FindInProject(searchPattern)
+
+
+    def OnPopFindClass(self, event):
+        view = wx.GetApp().GetDocumentManager().GetCurrentView()
+        if hasattr(view, "GetCtrl") and view.GetCtrl() and hasattr(view.GetCtrl(), "GetSelectedText"):
+            definition = "class\s+%s"
+            pattern = view.GetCtrl().GetSelectedText().strip()
+            if pattern:
+                searchPattern = definition % pattern
+                wx.GetApp().GetService(FindInDirService.FindInDirService).FindInProject(searchPattern)
+
+
     def SetViewDefaults(self):
     def SetViewDefaults(self):
-        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Python", hasWordWrap = True, hasTabs = True)
+        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix="Python", hasWordWrap=True, hasTabs=True, hasFolding=True)
 
 
     def GetFontAndColorFromConfig(self):
 
 
     def GetFontAndColorFromConfig(self):
@@ -567,7 +604,7 @@ class PythonOptionsPanel(wx.Panel):
         mainSizer = wx.BoxSizer(wx.VERTICAL)                
         mainSizer.Add(pathSizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, SPACE)
 
         mainSizer = wx.BoxSizer(wx.VERTICAL)                
         mainSizer.Add(pathSizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, SPACE)
 
-        self._otherOptions = STCTextEditor.TextOptionsPanel(self, -1, configPrefix = "Python", label = "Python", hasWordWrap = True, hasTabs = True, addPage=False)
+        self._otherOptions = STCTextEditor.TextOptionsPanel(self, -1, configPrefix = "Python", label = "Python", hasWordWrap = True, hasTabs = True, addPage=False, hasFolding=True)
         mainSizer.Add(self._otherOptions, 0, wx.EXPAND|wx.BOTTOM, SPACE)
         self.SetSizer(mainSizer)
         parent.AddPage(self, _("Python"))
         mainSizer.Add(self._otherOptions, 0, wx.EXPAND|wx.BOTTOM, SPACE)
         self.SetSizer(mainSizer)
         parent.AddPage(self, _("Python"))
@@ -577,7 +614,7 @@ class PythonOptionsPanel(wx.Panel):
         defaultDir = os.path.dirname(self._pathTextCtrl.GetValue().strip())
         defaultFile = os.path.basename(self._pathTextCtrl.GetValue().strip())
         if _WINDOWS:
         defaultDir = os.path.dirname(self._pathTextCtrl.GetValue().strip())
         defaultFile = os.path.basename(self._pathTextCtrl.GetValue().strip())
         if _WINDOWS:
-            wildcard = _("Executable (*.exe)|*.exe|All (*.*)|*.*")
+            wildcard = _("Executable (*.exe)|*.exe|All|*.*")
             if not defaultFile:
                 defaultFile = "python.exe"
         else:
             if not defaultFile:
                 defaultFile = "python.exe"
         else:
index fd9b84a91a6fa94c91aa4b05735d14fad174a57a..f3ab9508e2b75441874e99db2bb061aa87326682 100644 (file)
@@ -6,7 +6,7 @@
 #
 # Created:      8/10/03
 # CVS-ID:       $Id$
 #
 # Created:      8/10/03
 # CVS-ID:       $Id$
-# Copyright:    (c) 2003-2005 ActiveGrid, Inc.
+# Copyright:    (c) 2003-2006 ActiveGrid, Inc.
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
@@ -47,9 +47,15 @@ TEXT_STATUS_BAR_ID = wx.NewId()
 class TextDocument(wx.lib.docview.Document):
 
 
 class TextDocument(wx.lib.docview.Document):
 
 
+    def __init__(self):
+        wx.lib.docview.Document .__init__(self)
+        self._inModify = False
+
+
     def SaveObject(self, fileObject):
         view = self.GetFirstView()
         fileObject.write(view.GetValue())
     def SaveObject(self, fileObject):
         view = self.GetFirstView()
         fileObject.write(view.GetValue())
+        view.SetModifyFalse()
         return True
         
 
         return True
         
 
@@ -57,24 +63,31 @@ class TextDocument(wx.lib.docview.Document):
         view = self.GetFirstView()
         data = fileObject.read()
         view.SetValue(data)
         view = self.GetFirstView()
         data = fileObject.read()
         view.SetValue(data)
+        view.SetModifyFalse()
         return True
 
 
     def IsModified(self):
         view = self.GetFirstView()
         if view:
         return True
 
 
     def IsModified(self):
         view = self.GetFirstView()
         if view:
-            return wx.lib.docview.Document.IsModified(self) or view.IsModified()
-        else:
-            return wx.lib.docview.Document.IsModified(self)
+            return view.IsModified()
+        return False
 
 
 
 
-    def Modify(self, mod):
+    def Modify(self, modify):
+        if self._inModify:
+            return
+        self._inModify = True
+        
         view = self.GetFirstView()
         view = self.GetFirstView()
-        wx.lib.docview.Document.Modify(self, mod)
-        if not mod and view:
+        if not modify and view:
             view.SetModifyFalse()
 
             view.SetModifyFalse()
 
+        wx.lib.docview.Document.Modify(self, modify)  # this must called be after the SetModifyFalse call above.
 
 
+        self._inModify = False
+        
+    
     def OnCreateCommandProcessor(self):
         # Don't create a command processor, it has its own
         pass
     def OnCreateCommandProcessor(self):
         # Don't create a command processor, it has its own
         pass
@@ -142,6 +155,8 @@ class TextView(wx.lib.docview.View):
             self._dynSash._view = self
             self._textEditor = self.GetCtrlClass()(self._dynSash, -1, style=wx.NO_BORDER)
         wx.EVT_LEFT_DOWN(self._textEditor, self.OnLeftClick)
             self._dynSash._view = self
             self._textEditor = self.GetCtrlClass()(self._dynSash, -1, style=wx.NO_BORDER)
         wx.EVT_LEFT_DOWN(self._textEditor, self.OnLeftClick)
+        self._textEditor.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModify)
+        
         self._CreateSizer(frame)
         self.Activate()
         frame.Show(True)
         self._CreateSizer(frame)
         self.Activate()
         frame.Show(True)
@@ -149,6 +164,10 @@ class TextView(wx.lib.docview.View):
         return True
 
 
         return True
 
 
+    def OnModify(self, event):
+        self.GetDocument().Modify(self._textEditor.GetModify())
+        
+
     def _CreateSizer(self, frame):
         sizer = wx.BoxSizer(wx.HORIZONTAL)
         sizer.Add(self._dynSash, 1, wx.EXPAND)
     def _CreateSizer(self, frame):
         sizer = wx.BoxSizer(wx.HORIZONTAL)
         sizer.Add(self._dynSash, 1, wx.EXPAND)
@@ -161,6 +180,9 @@ class TextView(wx.lib.docview.View):
 
 
     def OnUpdate(self, sender = None, hint = None):
 
 
     def OnUpdate(self, sender = None, hint = None):
+        if wx.lib.docview.View.OnUpdate(self, sender, hint):
+            return
+
         if hint == "ViewStuff":
             self.GetCtrl().SetViewDefaults()
         elif hint == "Font":
         if hint == "ViewStuff":
             self.GetCtrl().SetViewDefaults()
         elif hint == "Font":
@@ -571,9 +593,11 @@ class TextView(wx.lib.docview.View):
     def EnsureVisible(self, line):
         self.GetCtrl().EnsureVisible(line-1)  # line numbering for editor is 0 based, we are 1 based.
 
     def EnsureVisible(self, line):
         self.GetCtrl().EnsureVisible(line-1)  # line numbering for editor is 0 based, we are 1 based.
 
+
     def EnsureVisibleEnforcePolicy(self, line):
         self.GetCtrl().EnsureVisibleEnforcePolicy(line-1)  # line numbering for editor is 0 based, we are 1 based.
 
     def EnsureVisibleEnforcePolicy(self, line):
         self.GetCtrl().EnsureVisibleEnforcePolicy(line-1)  # line numbering for editor is 0 based, we are 1 based.
 
+
     def LineFromPosition(self, pos):
         return self.GetCtrl().LineFromPosition(pos)+1  # line numbering for editor is 0 based, we are 1 based.
 
     def LineFromPosition(self, pos):
         return self.GetCtrl().LineFromPosition(pos)+1  # line numbering for editor is 0 based, we are 1 based.
 
@@ -813,11 +837,12 @@ class TextStatusBar(wx.StatusBar):
 class TextOptionsPanel(wx.Panel):
 
 
 class TextOptionsPanel(wx.Panel):
 
 
-    def __init__(self, parent, id, configPrefix = "Text", label = "Text", hasWordWrap = True, hasTabs = False, addPage=True):
+    def __init__(self, parent, id, configPrefix = "Text", label = "Text", hasWordWrap = True, hasTabs = False, addPage=True, hasFolding=False):
         wx.Panel.__init__(self, parent, id)
         self._configPrefix = configPrefix
         self._hasWordWrap = hasWordWrap
         self._hasTabs = hasTabs
         wx.Panel.__init__(self, parent, id)
         self._configPrefix = configPrefix
         self._hasWordWrap = hasWordWrap
         self._hasTabs = hasTabs
+        self._hasFolding = hasFolding
         SPACE = 10
         HALF_SPACE   = 5
         config = wx.ConfigBase_Get()
         SPACE = 10
         HALF_SPACE   = 5
         config = wx.ConfigBase_Get()
@@ -854,6 +879,9 @@ class TextOptionsPanel(wx.Panel):
         self._viewRightEdgeCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewRightEdge", False))
         self._viewLineNumbersCheckBox = wx.CheckBox(self, -1, _("Show line numbers"))
         self._viewLineNumbersCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True))
         self._viewRightEdgeCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewRightEdge", False))
         self._viewLineNumbersCheckBox = wx.CheckBox(self, -1, _("Show line numbers"))
         self._viewLineNumbersCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True))
+        if self._hasFolding:
+            self._viewFoldingCheckBox = wx.CheckBox(self, -1, _("Show folding"))
+            self._viewFoldingCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewFolding", True))
         if self._hasTabs:
             self._hasTabsCheckBox = wx.CheckBox(self, -1, _("Use spaces instead of tabs"))
             self._hasTabsCheckBox.SetValue(not wx.ConfigBase_Get().ReadInt(self._configPrefix + "EditorUseTabs", False))
         if self._hasTabs:
             self._hasTabsCheckBox = wx.CheckBox(self, -1, _("Use spaces instead of tabs"))
             self._hasTabsCheckBox.SetValue(not wx.ConfigBase_Get().ReadInt(self._configPrefix + "EditorUseTabs", False))
@@ -874,6 +902,8 @@ class TextOptionsPanel(wx.Panel):
         textPanelSizer.Add(self._viewIndentationGuideCheckBox, 0, wx.ALL, HALF_SPACE)
         textPanelSizer.Add(self._viewRightEdgeCheckBox, 0, wx.ALL, HALF_SPACE)
         textPanelSizer.Add(self._viewLineNumbersCheckBox, 0, wx.ALL, HALF_SPACE)
         textPanelSizer.Add(self._viewIndentationGuideCheckBox, 0, wx.ALL, HALF_SPACE)
         textPanelSizer.Add(self._viewRightEdgeCheckBox, 0, wx.ALL, HALF_SPACE)
         textPanelSizer.Add(self._viewLineNumbersCheckBox, 0, wx.ALL, HALF_SPACE)
+        if self._hasFolding:
+            textPanelSizer.Add(self._viewFoldingCheckBox, 0, wx.ALL, HALF_SPACE)
         if self._hasTabs:
             textPanelSizer.Add(self._hasTabsCheckBox, 0, wx.ALL, HALF_SPACE)
             textIndentWidthSizer = wx.BoxSizer(wx.HORIZONTAL)
         if self._hasTabs:
             textPanelSizer.Add(self._hasTabsCheckBox, 0, wx.ALL, HALF_SPACE)
             textIndentWidthSizer = wx.BoxSizer(wx.HORIZONTAL)
@@ -947,6 +977,9 @@ class TextOptionsPanel(wx.Panel):
         config.WriteInt(self._configPrefix + "EditorViewRightEdge", self._viewRightEdgeCheckBox.GetValue())
         doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True) != self._viewLineNumbersCheckBox.GetValue()
         config.WriteInt(self._configPrefix + "EditorViewLineNumbers", self._viewLineNumbersCheckBox.GetValue())
         config.WriteInt(self._configPrefix + "EditorViewRightEdge", self._viewRightEdgeCheckBox.GetValue())
         doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True) != self._viewLineNumbersCheckBox.GetValue()
         config.WriteInt(self._configPrefix + "EditorViewLineNumbers", self._viewLineNumbersCheckBox.GetValue())
+        if self._hasFolding:
+            doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewFolding", True) != self._viewFoldingCheckBox.GetValue()
+            config.WriteInt(self._configPrefix + "EditorViewFolding", self._viewFoldingCheckBox.GetValue())
         if self._hasWordWrap:
             doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorWordWrap", False) != self._wordWrapCheckBox.GetValue()
             config.WriteInt(self._configPrefix + "EditorWordWrap", self._wordWrapCheckBox.GetValue())
         if self._hasWordWrap:
             doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorWordWrap", False) != self._wordWrapCheckBox.GetValue()
             config.WriteInt(self._configPrefix + "EditorWordWrap", self._wordWrapCheckBox.GetValue())
@@ -1062,13 +1095,15 @@ class TextCtrl(wx.stc.StyledTextCtrl):
         event.Skip()
         
 
         event.Skip()
         
 
-    def SetViewDefaults(self, configPrefix = "Text", hasWordWrap = True, hasTabs = False):
+    def SetViewDefaults(self, configPrefix="Text", hasWordWrap=True, hasTabs=False, hasFolding=False):
         config = wx.ConfigBase_Get()
         self.SetViewWhiteSpace(config.ReadInt(configPrefix + "EditorViewWhitespace", False))
         self.SetViewEOL(config.ReadInt(configPrefix + "EditorViewEOL", False))
         self.SetIndentationGuides(config.ReadInt(configPrefix + "EditorViewIndentationGuides", False))
         self.SetViewRightEdge(config.ReadInt(configPrefix + "EditorViewRightEdge", False))
         self.SetViewLineNumbers(config.ReadInt(configPrefix + "EditorViewLineNumbers", True))
         config = wx.ConfigBase_Get()
         self.SetViewWhiteSpace(config.ReadInt(configPrefix + "EditorViewWhitespace", False))
         self.SetViewEOL(config.ReadInt(configPrefix + "EditorViewEOL", False))
         self.SetIndentationGuides(config.ReadInt(configPrefix + "EditorViewIndentationGuides", False))
         self.SetViewRightEdge(config.ReadInt(configPrefix + "EditorViewRightEdge", False))
         self.SetViewLineNumbers(config.ReadInt(configPrefix + "EditorViewLineNumbers", True))
+        if hasFolding:
+            self.SetViewFolding(config.ReadInt(configPrefix + "EditorViewFolding", True))
         if hasWordWrap:
             self.SetWordWrap(config.ReadInt(configPrefix + "EditorWordWrap", False))
         if hasTabs:  # These methods do not exist in STCTextEditor and are meant for subclasses
         if hasWordWrap:
             self.SetWordWrap(config.ReadInt(configPrefix + "EditorWordWrap", False))
         if hasTabs:  # These methods do not exist in STCTextEditor and are meant for subclasses
@@ -1237,6 +1272,17 @@ class TextCtrl(wx.stc.StyledTextCtrl):
             self.SetMarginWidth(1, 0)
 
 
             self.SetMarginWidth(1, 0)
 
 
+    def GetViewFolding(self):
+        return self.GetMarginWidth(2) > 0
+
+
+    def SetViewFolding(self, viewFolding = True):
+        if viewFolding:
+            self.SetMarginWidth(2, 12)
+        else:
+            self.SetMarginWidth(2, 0)
+
+
     def CanWordWrap(self):
         return True
 
     def CanWordWrap(self):
         return True
 
index 785acf3866b442e4920aa75c0fc79a23f89aae13..c5e2af652f52e01bb3716f5703974ed25a149455 100644 (file)
@@ -324,526 +324,529 @@ class SVNService(wx.lib.pydocview.DocService):
             return False
 
 
             return False
 
 
-        if id == SVNService.SVN_UPDATE_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-            
-            filenames = self.GetCurrentDocuments()
-            if filenames:
-                filenames = filenames[:]
-                filenames.sort(self.BasenameCaseInsensitiveCompare)
-            else:
-                folderPath = self.GetCurrentFolder()
-                if folderPath:
-                    filenames = [folderPath]
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Update:\n"))
-
-            for filename in filenames:
-                view.AddLines("%s\n" % filename)
-                try:
-                    status = self._client.update(filename)
-    
-                    if status.number > 0:
-                        view.AddLines(_("Updated to revision %s\n") % status.number)
-
-                        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                        for doc in openDocs:
-                            if doc.GetFilename() == filename:
-                                yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                         _("Updated file '%s' is currently open.  Close it?") % os.path.basename(filename),
-                                                         _("Close File"),
-                                                         wx.YES_NO|wx.ICON_QUESTION)
-                                yesNoMsg.CenterOnParent()
-                                status = yesNoMsg.ShowModal()
-                                yesNoMsg.Destroy()
-                                if status == wx.ID_YES:
-                                    doc.DeleteAllViews()
-                                break
-                    else:
-                        view.AddLines(_("Update failed.\n"))
+        try:
+            if id == SVNService.SVN_UPDATE_ID:
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+                
+                filenames = self.GetCurrentDocuments()
+                if filenames:
+                    filenames = filenames[:]
+                    filenames.sort(self.BasenameCaseInsensitiveCompare)
+                else:
+                    folderPath = self.GetCurrentFolder()
+                    if folderPath:
+                        filenames = [folderPath]
     
     
-                except pysvn.ClientError, e:
-                    view.AddLines("%s\n" % str(e))
-                    wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
-                except:
-                    extype, ex, tb = sys.exc_info()
-                    view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
-                    for line in traceback.format_tb(tb):
-                        view.AddLines(line)
+                messageService = wx.GetApp().GetService(MessageService.MessageService)
+                messageService.ShowWindow()
     
     
-                    wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-
-            return True
-            
-        elif id == SVNService.SVN_UPDATE_ALL_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Update:\n"))
-            
-            project = self.GetCurrentProject()
-            if project:
-                openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                for doc in openDocs:
-                    if doc.GetFilename() == project:
-                        filenames = doc.GetFiles()[:]  # make a copy and sort it.
-                        filenames.sort(self.BasenameCaseInsensitiveCompare)
-
-                        for filename in filenames:
-                            view.AddLines("%s\n" % filename)
-                            try:
-                                status = self._client.update(filename)
+                view = messageService.GetView()
+                view.ClearLines()
+                view.AddLines(_("SVN Update:\n"))
+    
+                for filename in filenames:
+                    view.AddLines("%s\n" % filename)
+                    try:
+                        status = self._client.update(filename)
+        
+                        if status.number > 0:
+                            view.AddLines(_("Updated to revision %s\n") % status.number)
+    
+                            openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                            for doc in openDocs:
+                                if doc.GetFilename() == filename:
+                                    yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                             _("Updated file '%s' is currently open.  Close it?") % os.path.basename(filename),
+                                                             _("Close File"),
+                                                             wx.YES_NO|wx.ICON_QUESTION)
+                                    yesNoMsg.CenterOnParent()
+                                    status = yesNoMsg.ShowModal()
+                                    yesNoMsg.Destroy()
+                                    if status == wx.ID_YES:
+                                        doc.DeleteAllViews()
+                                    break
+                        else:
+                            view.AddLines(_("Update failed.\n"))
+        
+                    except pysvn.ClientError, e:
+                        view.AddLines("%s\n" % str(e))
+                        wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+                    except:
+                        extype, ex, tb = sys.exc_info()
+                        view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
+                        for line in traceback.format_tb(tb):
+                            view.AddLines(line)
+        
+                        wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+    
+                return True
                 
                 
-                                if status.number > 0:
-                                    view.AddLines(_("Updated to revision %s\n") % status.number)
-            
-                                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                                    for doc in openDocs:
-                                        if doc.GetFilename() == filename:
-                                            yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                                     _("Updated file '%s' is currently open.  Close it?") % os.path.basename(filename),
-                                                                     _("Close File"),
-                                                                     wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
-                                            yesNoMsg.CenterOnParent()
-                                            status = yesNoMsg.ShowModal()
-                                            yesNoMsg.Destroy()
-                                            if status == wx.ID_YES:
-                                                doc.DeleteAllViews()
-                                            elif status == wx.ID_NO:
-                                                pass
-                                            else: # elif status == wx.CANCEL:
-                                                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-                                                return True
-                                            break
-                                else:
-                                    view.AddLines(_("Update failed.\n"))
+            elif id == SVNService.SVN_UPDATE_ALL_ID:
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+    
+                messageService = wx.GetApp().GetService(MessageService.MessageService)
+                messageService.ShowWindow()
+    
+                view = messageService.GetView()
+                view.ClearLines()
+                view.AddLines(_("SVN Update:\n"))
                 
                 
-                            except pysvn.ClientError, e:
-                                view.AddLines("%s\n" % str(e))
-                                wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
-                            except:
-                                extype, ex, tb = sys.exc_info()
-                                view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
-                                for line in traceback.format_tb(tb):
-                                    view.AddLines(line)
+                project = self.GetCurrentProject()
+                if project:
+                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                    for doc in openDocs:
+                        if doc.GetFilename() == project:
+                            filenames = doc.GetFiles()[:]  # make a copy and sort it.
+                            filenames.sort(self.BasenameCaseInsensitiveCompare)
+    
+                            for filename in filenames:
+                                view.AddLines("%s\n" % filename)
+                                try:
+                                    status = self._client.update(filename)
+                    
+                                    if status.number > 0:
+                                        view.AddLines(_("Updated to revision %s\n") % status.number)
                 
                 
-                                wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            return True
-
-        elif id == SVNService.SVN_CHECKIN_ALL_ID:
-            filenames = []
-            project = self.GetCurrentProject()
-            if project:
+                                        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                                        for doc in openDocs:
+                                            if doc.GetFilename() == filename:
+                                                yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                                         _("Updated file '%s' is currently open.  Close it?") % os.path.basename(filename),
+                                                                         _("Close File"),
+                                                                         wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
+                                                yesNoMsg.CenterOnParent()
+                                                status = yesNoMsg.ShowModal()
+                                                yesNoMsg.Destroy()
+                                                if status == wx.ID_YES:
+                                                    doc.DeleteAllViews()
+                                                elif status == wx.ID_NO:
+                                                    pass
+                                                else: # elif status == wx.CANCEL:
+                                                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                                                    return True
+                                                break
+                                    else:
+                                        view.AddLines(_("Update failed.\n"))
+                    
+                                except pysvn.ClientError, e:
+                                    view.AddLines("%s\n" % str(e))
+                                    wx.MessageBox(str(e), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+                                except:
+                                    extype, ex, tb = sys.exc_info()
+                                    view.AddLines("Update failed: (%s) %s\n" % (extype, str(ex)))
+                                    for line in traceback.format_tb(tb):
+                                        view.AddLines(line)
+                    
+                                    wx.MessageBox(_("Update failed."), _("SVN Update"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                return True
+    
+            elif id == SVNService.SVN_CHECKIN_ALL_ID:
+                filenames = []
+                project = self.GetCurrentProject()
+                if project:
+                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                    for doc in openDocs:
+                        if doc.GetFilename() == project:
+                            for filename in doc.GetFiles():
+                                if filename not in filenames:
+                                    filenames.append(filename)
+                filenames.sort(self.BasenameCaseInsensitiveCompare)
+    
+                # ask user if dirty files should be saved first
                 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
                 openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                for doc in openDocs:
-                    if doc.GetFilename() == project:
-                        for filename in doc.GetFiles():
-                            if filename not in filenames:
-                                filenames.append(filename)
-            filenames.sort(self.BasenameCaseInsensitiveCompare)
-
-            # ask user if dirty files should be saved first
-            openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-            for filename in filenames:
-                for doc in openDocs:
-                    if doc.GetFilename() == filename and doc.IsModified():
-                        yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                 _("'%s' has unsaved modifications.  Save it before commit?") % os.path.basename(filename),
-                                                 _("SVN Commit"),
-                                                 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
-                        yesNoMsg.CenterOnParent()
-                        status = yesNoMsg.ShowModal()
-                        yesNoMsg.Destroy()
-                        if status == wx.ID_YES:
-                            doc.Save()
-                        elif status == wx.ID_NO:
-                            pass
-                        else: # elif status == wx.CANCEL:
-                            return True
-                        break
-
-            shortFilenames = []
-            for i, filename in enumerate(filenames):
-                shortFilename = os.path.basename(filename)
-                shortFilenames.append(shortFilename)
-
-            dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
-
-            sizer = wx.BoxSizer(wx.VERTICAL)
-            sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
-            commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
-            sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
-
-            sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
-            fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
-            for i in range(fileList.GetCount()):
-                fileList.Check(i, True)
-            sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
-
-            buttonSizer = wx.StdDialogButtonSizer()
-            okBtn = wx.Button(dlg, wx.ID_OK)
-            okBtn.SetDefault()
-            buttonSizer.AddButton(okBtn)
-            buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
-            buttonSizer.Realize()
-
-            contentSizer = wx.BoxSizer(wx.VERTICAL)
-            contentSizer.Add(sizer, 0, wx.ALL, SPACE)
-            contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
-
-            dlg.SetSizer(contentSizer)
-            dlg.Fit()
-            dlg.Layout()
-
-            dlg.CenterOnParent()
-            if dlg.ShowModal() == wx.ID_OK:
+                for filename in filenames:
+                    for doc in openDocs:
+                        if doc.GetFilename() == filename and doc.IsModified():
+                            yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                     _("'%s' has unsaved modifications.  Save it before commit?") % os.path.basename(filename),
+                                                     _("SVN Commit"),
+                                                     wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
+                            yesNoMsg.CenterOnParent()
+                            status = yesNoMsg.ShowModal()
+                            yesNoMsg.Destroy()
+                            if status == wx.ID_YES:
+                                doc.Save()
+                            elif status == wx.ID_NO:
+                                pass
+                            else: # elif status == wx.CANCEL:
+                                return True
+                            break
+    
+                shortFilenames = []
+                for i, filename in enumerate(filenames):
+                    shortFilename = os.path.basename(filename)
+                    shortFilenames.append(shortFilename)
+    
+                dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
+    
+                sizer = wx.BoxSizer(wx.VERTICAL)
+                sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
+                commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
+                sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
+    
+                sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
+                fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
+                for i in range(fileList.GetCount()):
+                    fileList.Check(i, True)
+                sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
+    
+                buttonSizer = wx.StdDialogButtonSizer()
+                okBtn = wx.Button(dlg, wx.ID_OK)
+                okBtn.SetDefault()
+                buttonSizer.AddButton(okBtn)
+                buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
+                buttonSizer.Realize()
+    
+                contentSizer = wx.BoxSizer(wx.VERTICAL)
+                contentSizer.Add(sizer, 0, wx.ALL, SPACE)
+                contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
+    
+                dlg.SetSizer(contentSizer)
+                dlg.Fit()
+                dlg.Layout()
+    
+                dlg.CenterOnParent()
+                if dlg.ShowModal() == wx.ID_OK:
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+    
+                    messageService = wx.GetApp().GetService(MessageService.MessageService)
+                    messageService.ShowWindow()
+    
+                    view = messageService.GetView()
+                    view.ClearLines()
+                    view.AddLines(_("SVN Commit:\n"))
+    
+                    try:
+                        selFilenames = []
+                        for i in range(fileList.GetCount()):
+                            if fileList.IsChecked(i):
+                                selFilenames.append(filenames[i])
+                                view.AddLines("%s\n" % filenames[i])
+                            
+                        if len(selFilenames):
+                            comment = commentText.GetValue()
+                            status = self._client.checkin(selFilenames, comment)
+    
+                            if status is None:
+                                view.AddLines(_("Nothing to commit.\n"))
+                            elif status.number > 0:
+                                view.AddLines(_("Committed as revision %s.\n") % status.number)
+                            else:
+                                view.AddLines(_("Commit failed.\n"))
+    
+                    except pysvn.ClientError, e:
+                        view.AddLines("%s\n" % str(e))
+                        wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+                    except:
+                        extype, ex, tb = sys.exc_info()
+                        view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
+                        for line in traceback.format_tb(tb):
+                            view.AddLines(line)
+                        wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                dlg.Destroy()
+                return True
+    
+    
+            elif id == SVNService.SVN_CHECKIN_ID:
+                filenames = self.GetCurrentDocuments()[:]
+                filenames.sort(self.BasenameCaseInsensitiveCompare)            
+    
+                # ask user if dirty files should be saved first
+                openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                for filename in filenames:
+                    for doc in openDocs:
+                        if doc.GetFilename() == filename and doc.IsModified():
+                            yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                     _("'%s' has unsaved modifications.  Save it before commit?") % os.path.basename(filename),
+                                                     _("SVN Commit"),
+                                                     wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
+                            yesNoMsg.CenterOnParent()
+                            status = yesNoMsg.ShowModal()
+                            yesNoMsg.Destroy()
+                            if status == wx.ID_YES:
+                                doc.Save()
+                            elif status == wx.ID_NO:
+                                pass
+                            else: # elif status == wx.CANCEL:
+                                return True
+                            break
+    
+                shortFilenames = []
+                for i, filename in enumerate(filenames):
+                    shortFilename = os.path.basename(filename)
+                    shortFilenames.append(shortFilename)
+    
+                dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
+    
+                sizer = wx.BoxSizer(wx.VERTICAL)
+                sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
+                commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
+                sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
+    
+                sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
+                fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
+                for i in range(fileList.GetCount()):
+                    fileList.Check(i, True)
+                sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
+    
+                buttonSizer = wx.StdDialogButtonSizer()
+                okBtn = wx.Button(dlg, wx.ID_OK)
+                okBtn.SetDefault()
+                buttonSizer.AddButton(okBtn)
+                buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
+                buttonSizer.Realize()
+                
+                contentSizer = wx.BoxSizer(wx.VERTICAL)
+                contentSizer.Add(sizer, 0, wx.ALL, SPACE)
+                contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
+    
+                dlg.SetSizer(contentSizer)
+                dlg.Fit()
+                dlg.Layout()
+    
+                dlg.CenterOnParent()
+                if dlg.ShowModal() == wx.ID_OK:
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+    
+                    messageService = wx.GetApp().GetService(MessageService.MessageService)
+                    messageService.ShowWindow()
+    
+                    view = messageService.GetView()
+                    view.ClearLines()
+                    view.AddLines(_("SVN Commit:\n"))
+    
+                    try:
+                        selFilenames = []
+                        for i in range(fileList.GetCount()):
+                            if fileList.IsChecked(i):
+                                selFilenames.append(filenames[i])
+                                view.AddLines("%s\n" % filenames[i])
+                            
+                        if len(selFilenames):
+                            comment = commentText.GetValue()
+                            status = self._client.checkin(selFilenames, comment)
+    
+                            if status is None:
+                                view.AddLines(_("Nothing to commit.\n"))
+                            elif status.number > 0:
+                                view.AddLines(_("Committed as revision %s.\n") % status.number)
+                            else:
+                                view.AddLines(_("Commit failed.\n"))
+    
+                    except pysvn.ClientError, e:
+                        view.AddLines("%s\n" % str(e))
+                        wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+                    except:
+                        extype, ex, tb = sys.exc_info()
+                        view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
+                        for line in traceback.format_tb(tb):
+                            view.AddLines(line)
+                        wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                dlg.Destroy()
+                return True
+    
+            elif id == SVNService.SVN_CHECKOUT_ID:
+                config = wx.ConfigBase_Get()
+                svnUrl = config.Read(SVN_REPOSITORY_URL, self._defaultURL)
+    
+                dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Checkout"))
+    
+                gridSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
+                gridSizer.Add(wx.StaticText(dlg, -1, _("Repository URL:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
+                svnUrlList = ReadSvnUrlList()
+                svnURLCombobox = wx.ComboBox(dlg, -1, size=(200, -1), choices=svnUrlList, style=wx.CB_DROPDOWN)
+                if len(svnUrlList):
+                    svnURLCombobox.SetToolTipString(svnUrlList[0])
+                    svnURLCombobox.SetStringSelection(svnUrlList[0])
+                else:
+                    svnURLCombobox.SetToolTipString(_("Set Repository URL"))
+                gridSizer.Add(svnURLCombobox, 0)
+    
+                gridSizer.Add(wx.StaticText(dlg, -1, _("Checkout to dir:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
+                localPath = wx.TextCtrl(dlg, -1, size = (200, -1))
+                localPath.SetToolTipString(_("Path in local file system where files will be located."))
+                findDirButton = wx.Button(dlg, -1, _("Browse..."))
+    
+                def OnBrowseButton(event):
+                    dirDlg = wx.DirDialog(wx.GetApp().GetTopWindow(), _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
+                    dir = localPath.GetValue()
+                    if len(dir):
+                        dirDlg.SetPath(dir)
+                    dirDlg.CenterOnParent()
+                    if dirDlg.ShowModal() == wx.ID_OK:
+                        localPath.SetValue(dirDlg.GetPath())
+                        localPath.SetToolTipString(localPath.GetValue())
+                        localPath.SetInsertionPointEnd()
+                    dirDlg.Destroy()
+                wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
+    
+                sizer = wx.BoxSizer(wx.HORIZONTAL)
+                sizer.Add(localPath, 1, wx.EXPAND)
+                sizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
+                gridSizer.Add(sizer, 0)
+    
+                buttonSizer = wx.StdDialogButtonSizer()
+                okBtn = wx.Button(dlg, wx.ID_OK)
+                okBtn.SetDefault()
+                buttonSizer.AddButton(okBtn)
+                buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
+                buttonSizer.Realize()
+    
+                contentSizer = wx.BoxSizer(wx.VERTICAL)
+                contentSizer.Add(gridSizer, 0, wx.ALL, SPACE)
+                contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
+    
+                dlg.SetSizer(contentSizer)
+                dlg.Fit()
+                dlg.Layout()
+    
+                dlg.CenterOnParent()
+                if dlg.ShowModal() == wx.ID_OK:
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+                    
+                    WriteSvnUrlList(svnURLCombobox)
+    
+                    messageService = wx.GetApp().GetService(MessageService.MessageService)
+                    messageService.ShowWindow()
+    
+                    view = messageService.GetView()
+                    view.ClearLines()
+                    view.AddLines(_("SVN Checkout:\n"))
+    
+                    svnUrl = svnURLCombobox.GetValue()
+                    toLocation = localPath.GetValue()
+                    try:
+                        self._client.checkout(svnUrl, toLocation)
+                        view.AddLines(_("Checkout completed.\n"))
+                    except pysvn.ClientError, e:
+                        view.AddLines(_("Checkout failed.  %s\n") % str(e))
+                        wx.MessageBox(_("Checkout failed.  %s") % str(e), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
+                    except:
+                        extype, ex, tb = sys.exc_info()
+                        view.AddLines("Checkout failed: (%s) %s\n" % (extype, str(ex)))
+                        for line in traceback.format_tb(tb):
+                            view.AddLines(line)
+                        wx.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
+    
+                    wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+                dlg.Destroy()
+                return True
+    
+            elif id == SVNService.SVN_REVERT_ID:
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
+    
+                filenames = self.GetCurrentDocuments()
+    
                 messageService = wx.GetApp().GetService(MessageService.MessageService)
                 messageService.ShowWindow()
                 messageService = wx.GetApp().GetService(MessageService.MessageService)
                 messageService.ShowWindow()
-
+    
                 view = messageService.GetView()
                 view.ClearLines()
                 view = messageService.GetView()
                 view.ClearLines()
-                view.AddLines(_("SVN Commit:\n"))
-
+                view.AddLines(_("SVN Revert:\n"))
+                for filename in filenames:
+                    view.AddLines("%s\n" % filename)
+    
                 try:
                 try:
-                    selFilenames = []
-                    for i in range(fileList.GetCount()):
-                        if fileList.IsChecked(i):
-                            selFilenames.append(filenames[i])
-                            view.AddLines("%s\n" % filenames[i])
-                        
-                    if len(selFilenames):
-                        comment = commentText.GetValue()
-                        status = self._client.checkin(selFilenames, comment)
-
-                        if status is None:
-                            view.AddLines(_("Nothing to commit.\n"))
-                        elif status.number > 0:
-                            view.AddLines(_("Committed as revision %s.\n") % status.number)
-                        else:
-                            view.AddLines(_("Commit failed.\n"))
-
+                    self._client.revert(filenames)
+                    view.AddLines(_("Revert completed.\n"))
+                    
+                    openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+                    for doc in openDocs[:]:   # need to make a copy of the list otherwise ordinality changes as we close the files
+                        if doc.GetFilename() in filenames:
+                            yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
+                                                     _("Reverted file '%s' is currently open.  Close it?") % os.path.basename(doc.GetFilename()),
+                                                     _("Close File"),
+                                                     wx.YES_NO|wx.ICON_QUESTION)
+                            yesNoMsg.CenterOnParent()
+                            status = yesNoMsg.ShowModal()
+                            yesNoMsg.Destroy()
+                            if status == wx.ID_YES:
+                                doc.DeleteAllViews()
+    
                 except pysvn.ClientError, e:
                     view.AddLines("%s\n" % str(e))
                 except pysvn.ClientError, e:
                     view.AddLines("%s\n" % str(e))
-                    wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+                    wx.MessageBox(str(e), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
                 except:
                     extype, ex, tb = sys.exc_info()
                 except:
                     extype, ex, tb = sys.exc_info()
-                    view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
+                    view.AddLines("Revert failed: (%s) %s\n" % (extype, str(ex)))
                     for line in traceback.format_tb(tb):
                         view.AddLines(line)
                     for line in traceback.format_tb(tb):
                         view.AddLines(line)
-                    wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
-
+                    wx.MessageBox(_("Revert failed."), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
+    
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            dlg.Destroy()
-            return True
-
-
-        elif id == SVNService.SVN_CHECKIN_ID:
-            filenames = self.GetCurrentDocuments()[:]
-            filenames.sort(self.BasenameCaseInsensitiveCompare)            
-
-            # ask user if dirty files should be saved first
-            openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-            for filename in filenames:
-                for doc in openDocs:
-                    if doc.GetFilename() == filename and doc.IsModified():
-                        yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                 _("'%s' has unsaved modifications.  Save it before commit?") % os.path.basename(filename),
-                                                 _("SVN Commit"),
-                                                 wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION)
-                        yesNoMsg.CenterOnParent()
-                        status = yesNoMsg.ShowModal()
-                        yesNoMsg.Destroy()
-                        if status == wx.ID_YES:
-                            doc.Save()
-                        elif status == wx.ID_NO:
-                            pass
-                        else: # elif status == wx.CANCEL:
-                            return True
-                        break
-
-            shortFilenames = []
-            for i, filename in enumerate(filenames):
-                shortFilename = os.path.basename(filename)
-                shortFilenames.append(shortFilename)
-
-            dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Commit"))
-
-            sizer = wx.BoxSizer(wx.VERTICAL)
-            sizer.Add(wx.StaticText(dlg, -1, _("Comment:")), 0, wx.ALIGN_CENTER_VERTICAL)
-            commentText = wx.TextCtrl(dlg, -1, size=(250,-1), style=wx.TE_MULTILINE)
-            sizer.Add(commentText, 1, wx.EXPAND|wx.TOP, HALF_SPACE)
-
-            sizer.Add(wx.StaticText(dlg, -1, _("Files:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.TOP, SPACE)
-            fileList = wx.CheckListBox(dlg, -1, choices = shortFilenames)
-            for i in range(fileList.GetCount()):
-                fileList.Check(i, True)
-            sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
-
-            buttonSizer = wx.StdDialogButtonSizer()
-            okBtn = wx.Button(dlg, wx.ID_OK)
-            okBtn.SetDefault()
-            buttonSizer.AddButton(okBtn)
-            buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
-            buttonSizer.Realize()
-            
-            contentSizer = wx.BoxSizer(wx.VERTICAL)
-            contentSizer.Add(sizer, 0, wx.ALL, SPACE)
-            contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
-
-            dlg.SetSizer(contentSizer)
-            dlg.Fit()
-            dlg.Layout()
-
-            dlg.CenterOnParent()
-            if dlg.ShowModal() == wx.ID_OK:
+                return True
+    
+            elif id == SVNService.SVN_ADD_ID:
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
+    
+                filenames = self.GetCurrentDocuments()
+    
                 messageService = wx.GetApp().GetService(MessageService.MessageService)
                 messageService.ShowWindow()
                 messageService = wx.GetApp().GetService(MessageService.MessageService)
                 messageService.ShowWindow()
-
+    
                 view = messageService.GetView()
                 view.ClearLines()
                 view = messageService.GetView()
                 view.ClearLines()
-                view.AddLines(_("SVN Commit:\n"))
-
+                view.AddLines(_("SVN Add:\n"))
+                for filename in filenames:
+                    view.AddLines("%s\n" % filename)
+    
                 try:
                 try:
-                    selFilenames = []
-                    for i in range(fileList.GetCount()):
-                        if fileList.IsChecked(i):
-                            selFilenames.append(filenames[i])
-                            view.AddLines("%s\n" % filenames[i])
-                        
-                    if len(selFilenames):
-                        comment = commentText.GetValue()
-                        status = self._client.checkin(selFilenames, comment)
-
-                        if status is None:
-                            view.AddLines(_("Nothing to commit.\n"))
-                        elif status.number > 0:
-                            view.AddLines(_("Committed as revision %s.\n") % status.number)
-                        else:
-                            view.AddLines(_("Commit failed.\n"))
-
+                    self._client.add(filenames)
+                    view.AddLines(_("Add completed.\n"))
                 except pysvn.ClientError, e:
                     view.AddLines("%s\n" % str(e))
                 except pysvn.ClientError, e:
                     view.AddLines("%s\n" % str(e))
-                    wx.MessageBox(str(e), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
+                    wx.MessageBox(str(e), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
                 except:
                     extype, ex, tb = sys.exc_info()
                 except:
                     extype, ex, tb = sys.exc_info()
-                    view.AddLines("Commit failed: (%s) %s\n" % (extype, str(ex)))
+                    view.AddLines("Add failed: (%s) %s\n" % (extype, str(ex)))
                     for line in traceback.format_tb(tb):
                         view.AddLines(line)
                     for line in traceback.format_tb(tb):
                         view.AddLines(line)
-                    wx.MessageBox(_("Commit failed."), _("SVN Commit"), wx.OK | wx.ICON_EXCLAMATION)
-
+                    wx.MessageBox(_("Add failed."), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
+    
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            dlg.Destroy()
-            return True
-
-        elif id == SVNService.SVN_CHECKOUT_ID:
-            config = wx.ConfigBase_Get()
-            svnUrl = config.Read(SVN_REPOSITORY_URL, self._defaultURL)
-
-            dlg = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("SVN Checkout"))
-
-            gridSizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
-            gridSizer.Add(wx.StaticText(dlg, -1, _("Repository URL:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
-            svnUrlList = ReadSvnUrlList()
-            svnURLCombobox = wx.ComboBox(dlg, -1, size=(200, -1), choices=svnUrlList, style=wx.CB_DROPDOWN)
-            if len(svnUrlList):
-                svnURLCombobox.SetToolTipString(svnUrlList[0])
-                svnURLCombobox.SetStringSelection(svnUrlList[0])
-            else:
-                svnURLCombobox.SetToolTipString(_("Set Repository URL"))
-            gridSizer.Add(svnURLCombobox, 0)
-
-            gridSizer.Add(wx.StaticText(dlg, -1, _("Checkout to dir:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, HALF_SPACE)
-            localPath = wx.TextCtrl(dlg, -1, size = (200, -1))
-            localPath.SetToolTipString(_("Path in local file system where files will be located."))
-            findDirButton = wx.Button(dlg, -1, _("Browse..."))
-
-            def OnBrowseButton(event):
-                dirDlg = wx.DirDialog(wx.GetApp().GetTopWindow(), _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
-                dir = localPath.GetValue()
-                if len(dir):
-                    dirDlg.SetPath(dir)
-                dirDlg.CenterOnParent()
-                if dirDlg.ShowModal() == wx.ID_OK:
-                    localPath.SetValue(dirDlg.GetPath())
-                    localPath.SetToolTipString(localPath.GetValue())
-                    localPath.SetInsertionPointEnd()
-                dirDlg.Destroy()
-            wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
-
-            sizer = wx.BoxSizer(wx.HORIZONTAL)
-            sizer.Add(localPath, 1, wx.EXPAND)
-            sizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
-            gridSizer.Add(sizer, 0)
-
-            buttonSizer = wx.StdDialogButtonSizer()
-            okBtn = wx.Button(dlg, wx.ID_OK)
-            okBtn.SetDefault()
-            buttonSizer.AddButton(okBtn)
-            buttonSizer.AddButton(wx.Button(dlg, wx.ID_CANCEL))
-            buttonSizer.Realize()
-
-            contentSizer = wx.BoxSizer(wx.VERTICAL)
-            contentSizer.Add(gridSizer, 0, wx.ALL, SPACE)
-            contentSizer.Add(buttonSizer, 0, wx.ALL|wx.ALIGN_RIGHT, SPACE)
-
-            dlg.SetSizer(contentSizer)
-            dlg.Fit()
-            dlg.Layout()
-
-            dlg.CenterOnParent()
-            if dlg.ShowModal() == wx.ID_OK:
+                return True
+    
+            elif id == SVNService.SVN_DELETE_ID:
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-                
-                WriteSvnUrlList(svnURLCombobox)
-
+    
+                filenames = self.GetCurrentDocuments()
+    
                 messageService = wx.GetApp().GetService(MessageService.MessageService)
                 messageService.ShowWindow()
                 messageService = wx.GetApp().GetService(MessageService.MessageService)
                 messageService.ShowWindow()
-
+    
                 view = messageService.GetView()
                 view.ClearLines()
                 view = messageService.GetView()
                 view.ClearLines()
-                view.AddLines(_("SVN Checkout:\n"))
-
-                svnUrl = svnURLCombobox.GetValue()
-                toLocation = localPath.GetValue()
+                view.AddLines(_("SVN Delete:\n"))
+                for filename in filenames:
+                    view.AddLines("%s\n" % filename)
+    
                 try:
                 try:
-                    self._client.checkout(svnUrl, toLocation)
-                    view.AddLines(_("Checkout completed.\n"))
+                    self._client.remove(filenames)
+                    view.AddLines(_("Delete completed.\n"))
                 except pysvn.ClientError, e:
                 except pysvn.ClientError, e:
-                    view.AddLines(_("Checkout failed.  %s\n") % str(e))
-                    wx.MessageBox(_("Checkout failed.  %s") % str(e), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
+                    view.AddLines("%s\n" % str(e))
+                    wx.MessageBox(str(e), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
                 except:
                     extype, ex, tb = sys.exc_info()
                 except:
                     extype, ex, tb = sys.exc_info()
-                    view.AddLines("Checkout failed: (%s) %s\n" % (extype, str(ex)))
+                    view.AddLines("Delete failed: (%s) %s\n" % (extype, str(ex)))
                     for line in traceback.format_tb(tb):
                         view.AddLines(line)
                     for line in traceback.format_tb(tb):
                         view.AddLines(line)
-                    wx.MessageBox(_("Checkout failed."), _("SVN Checkout"), wx.OK | wx.ICON_EXCLAMATION)
-
+                    wx.MessageBox(_("Delete failed."), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
+    
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
                 wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            dlg.Destroy()
-            return True
-
-        elif id == SVNService.SVN_REVERT_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
-            filenames = self.GetCurrentDocuments()
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Revert:\n"))
-            for filename in filenames:
-                view.AddLines("%s\n" % filename)
-
-            try:
-                self._client.revert(filenames)
-                view.AddLines(_("Revert completed.\n"))
-                
-                openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
-                for doc in openDocs[:]:   # need to make a copy of the list otherwise ordinality changes as we close the files
-                    if doc.GetFilename() in filenames:
-                        yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
-                                                 _("Reverted file '%s' is currently open.  Close it?") % os.path.basename(doc.GetFilename()),
-                                                 _("Close File"),
-                                                 wx.YES_NO|wx.ICON_QUESTION)
-                        yesNoMsg.CenterOnParent()
-                        status = yesNoMsg.ShowModal()
-                        yesNoMsg.Destroy()
-                        if status == wx.ID_YES:
-                            doc.DeleteAllViews()
-
-            except pysvn.ClientError, e:
-                view.AddLines("%s\n" % str(e))
-                wx.MessageBox(str(e), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
-            except:
-                extype, ex, tb = sys.exc_info()
-                view.AddLines("Revert failed: (%s) %s\n" % (extype, str(ex)))
-                for line in traceback.format_tb(tb):
-                    view.AddLines(line)
-                wx.MessageBox(_("Revert failed."), _("SVN Revert"), wx.OK | wx.ICON_EXCLAMATION)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            return True
-
-        elif id == SVNService.SVN_ADD_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
-            filenames = self.GetCurrentDocuments()
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Add:\n"))
-            for filename in filenames:
-                view.AddLines("%s\n" % filename)
-
-            try:
-                self._client.add(filenames)
-                view.AddLines(_("Add completed.\n"))
-            except pysvn.ClientError, e:
-                view.AddLines("%s\n" % str(e))
-                wx.MessageBox(str(e), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
-            except:
-                extype, ex, tb = sys.exc_info()
-                view.AddLines("Add failed: (%s) %s\n" % (extype, str(ex)))
-                for line in traceback.format_tb(tb):
-                    view.AddLines(line)
-                wx.MessageBox(_("Add failed."), _("SVN Add"), wx.OK | wx.ICON_EXCLAMATION)
-
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            return True
-
-        elif id == SVNService.SVN_DELETE_ID:
-            wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
-
-            filenames = self.GetCurrentDocuments()
-
-            messageService = wx.GetApp().GetService(MessageService.MessageService)
-            messageService.ShowWindow()
-
-            view = messageService.GetView()
-            view.ClearLines()
-            view.AddLines(_("SVN Delete:\n"))
-            for filename in filenames:
-                view.AddLines("%s\n" % filename)
-
-            try:
-                self._client.remove(filenames)
-                view.AddLines(_("Delete completed.\n"))
-            except pysvn.ClientError, e:
-                view.AddLines("%s\n" % str(e))
-                wx.MessageBox(str(e), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
-            except:
-                extype, ex, tb = sys.exc_info()
-                view.AddLines("Delete failed: (%s) %s\n" % (extype, str(ex)))
-                for line in traceback.format_tb(tb):
-                    view.AddLines(line)
-                wx.MessageBox(_("Delete failed."), _("SVN Delete"), wx.OK | wx.ICON_EXCLAMATION)
-
+                return True
+    
+            return False
+        finally:
             wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
             wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
-            return True
-
-        return False
 
 
     def ProcessUpdateUIEvent(self, event):
 
 
     def ProcessUpdateUIEvent(self, event):
index cb16f7bbf55d7448581dcb4dcb3e17fda6a49fce..a70cdf3bc445817cc47118a69eee42196597850f 100644 (file)
@@ -6,7 +6,7 @@
 #
 # Created:      3/10/05
 # CVS-ID:       $Id$
 #
 # Created:      3/10/05
 # CVS-ID:       $Id$
-# Copyright:    (c) 2005 ActiveGrid, Inc.
+# Copyright:    (c) 2005-2006 ActiveGrid, Inc.
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
@@ -15,12 +15,14 @@ import os.path
 import wx
 import string
 import ProjectEditor
 import wx
 import string
 import ProjectEditor
-import activegrid.util.sysutils as sysutils
-import activegrid.util.strutils as strutils
 import activegrid.util.appdirs as appdirs
 import activegrid.util.appdirs as appdirs
+import activegrid.util.fileutils as fileutils
+import activegrid.util.strutils as strutils
+import activegrid.util.sysutils as sysutils
+import activegrid.util.xmlutils as xmlutils
 _ = wx.GetTranslation
 
 _ = wx.GetTranslation
 
-def CreateDirectoryControl( parent, fileLabel=_("File Name:"), dirLabel=_("Directory"), fileExtension="*", startingName="", startingDirectory=None, choiceDirs=None, appDirDefaultStartDir=False, returnAll=False):
+def CreateDirectoryControl( parent, fileLabel=_("File Name:"), dirLabel=_("Directory:"), fileExtension="*", startingName="", startingDirectory=None, choiceDirs=None, appDirDefaultStartDir=False, returnAll=False, useDirDialog=False):
 
     if not choiceDirs:
         choiceDirs = []
 
     if not choiceDirs:
         choiceDirs = []
@@ -61,8 +63,8 @@ def CreateDirectoryControl( parent, fileLabel=_("File Name:"), dirLabel=_("Direc
             
         if os.getcwd() not in choiceDirs:
             choiceDirs.append(os.getcwd())                
             
         if os.getcwd() not in choiceDirs:
             choiceDirs.append(os.getcwd())                
-        if appdirs.documents_folder not in choiceDirs:
-            choiceDirs.append(appdirs.documents_folder
+        if appdirs.getSystemDir() not in choiceDirs:
+            choiceDirs.append(appdirs.getSystemDir()
 
     if not startingDirectory:
         startingDirectory = os.getcwd()
 
     if not startingDirectory:
         startingDirectory = os.getcwd()
@@ -85,12 +87,18 @@ def CreateDirectoryControl( parent, fileLabel=_("File Name:"), dirLabel=_("Direc
             else:
                 name = _("%s.%s") % (nameCtrlValue, fileExtension)
                 
             else:
                 name = _("%s.%s") % (nameCtrlValue, fileExtension)
                 
-        dlg = wx.FileDialog(parent, _("Choose a filename and directory"),
+        if not useDirDialog:
+            dlg = wx.FileDialog(parent, _("Choose a filename and directory"),
                        defaultDir = dirControl.GetValue().strip(),
                        defaultFile = name,
                        wildcard= "*.%s" % fileExtension,
                        style=wx.SAVE|wx.CHANGE_DIR)
                        defaultDir = dirControl.GetValue().strip(),
                        defaultFile = name,
                        wildcard= "*.%s" % fileExtension,
                        style=wx.SAVE|wx.CHANGE_DIR)
-
+        else:
+            dlg = wx.DirDialog(wx.GetApp().GetTopWindow(),
+                        _("Choose a directory:"),
+                        defaultPath=dirControl.GetValue().strip(),
+                        style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON)
+            
         if dlg.ShowModal() != wx.ID_OK:
             dlg.Destroy()
             return
         if dlg.ShowModal() != wx.ID_OK:
             dlg.Destroy()
             return
@@ -98,49 +106,78 @@ def CreateDirectoryControl( parent, fileLabel=_("File Name:"), dirLabel=_("Direc
         dlg.Destroy()
         
         if path:
         dlg.Destroy()
         
         if path:
-            dir, filename = os.path.split(path)
-            if dirControl.FindString(dir) == wx.NOT_FOUND:
-                dirControl.Insert(dir, 0)
-            dirControl.SetValue(dir)
-            dirControl.SetToolTipString(dir)
-            nameControl.SetValue(filename)
+            if not useDirDialog:
+                dir, filename = os.path.split(path)
+                if dirControl.FindString(dir) == wx.NOT_FOUND:
+                    dirControl.Insert(dir, 0)
+                dirControl.SetValue(dir)
+                dirControl.SetToolTipString(dir)
+                nameControl.SetValue(filename)
+            else:
+                dirControl.SetValue(path)
+                dirControl.SetToolTipString(path)
 
     parent.Bind(wx.EVT_BUTTON, OnFindDirClick, button)
     
 
     parent.Bind(wx.EVT_BUTTON, OnFindDirClick, button)
     
-    def Validate(allowOverwriteOnPrompt=False, infoString='', noFirstCharDigit=False):
+    def Validate(allowOverwriteOnPrompt=False, infoString='', validClassName=False, ignoreFileConflicts=False):
         projName = nameControl.GetValue().strip()
         if projName == "":
             wx.MessageBox(_("Please provide a %sfile name.") % infoString, _("Provide a File Name"))            
             return False
         projName = nameControl.GetValue().strip()
         if projName == "":
             wx.MessageBox(_("Please provide a %sfile name.") % infoString, _("Provide a File Name"))            
             return False
-        if noFirstCharDigit and projName[0].isdigit():
-            wx.MessageBox(_("File name cannot start with a number.  Please enter a different name."), _("Invalid File Name"))            
-            return False
         if projName.find(' ') != -1:
             wx.MessageBox(_("Please provide a %sfile name that does not contains spaces.") % infoString, _("Spaces in File Name"))            
             return False
         if projName.find(' ') != -1:
             wx.MessageBox(_("Please provide a %sfile name that does not contains spaces.") % infoString, _("Spaces in File Name"))            
             return False
-        if not os.path.exists(dirControl.GetValue()):
-            wx.MessageBox(_("That %sdirectory does not exist. Please choose an existing directory.") % infoString, _("Provide a Valid Directory"))            
+        if validClassName:
+            if projName[0].isdigit():
+                wx.MessageBox(_("File name cannot start with a number.  Please enter a different name."), _("Invalid File Name"))            
+                return False
+            if projName.endswith(".agp"):
+                projName2 = projName[:-4]
+            else:
+                projName2 = projName
+            if not projName2.replace("_", "a").isalnum():  # [a-zA-Z0-9_]  note '_' is allowed and ending '.agp'.
+                wx.MessageBox(_("Name must be alphanumeric ('_' allowed).  Please enter a valid name."), _("Project Name"))
+                return False
+
+        dirName = dirControl.GetValue().strip()
+        if dirName == "":
+            wx.MessageBox(_("No directory.  Please provide a directory."), _("Provide a Directory"))            
             return False
             return False
-        
-        filePath = os.path.join(dirControl.GetValue(), MakeNameEndInExtension(projName, "." + fileExtension))
-        if os.path.exists(filePath):
-            if allowOverwriteOnPrompt:
-                res = wx.MessageBox(_("That %sfile already exists. Would you like to overwrite it.") % infoString, "File Exists", style=wx.YES_NO|wx.NO_DEFAULT)
-                return (res == wx.YES)  
-            else:                
-                wx.MessageBox(_("That %sfile already exists. Please choose a different name.") % infoString, "File Exists")
-                return False                  
+        if os.sep == "\\" and dirName.find("/") != -1:
+            wx.MessageBox(_("Wrong delimiter '/' found in directory path.  Use '%s' as delimiter.") % os.sep, _("Provide a Valid Directory"))            
+            return False
+        if not os.path.exists(dirName):
+            wx.MessageBox(_("That %sdirectory does not exist.  Please choose an existing directory.") % infoString, _("Provide a Valid Directory"))            
+            return False
+        if not ignoreFileConflicts:
+            filePath = os.path.join(dirName, MakeNameEndInExtension(projName, "." + fileExtension))
+            if os.path.exists(filePath):
+                if allowOverwriteOnPrompt:
+                    res = wx.MessageBox(_("That %sfile already exists. Would you like to overwrite it.") % infoString, "File Exists", style=wx.YES_NO|wx.NO_DEFAULT)
+                    return (res == wx.YES)  
+                else:                
+                    wx.MessageBox(_("That %sfile already exists. Please choose a different name.") % infoString, "File Exists")
+                    return False
+
         return True    
     HALF_SPACE = 5
     flexGridSizer = wx.FlexGridSizer(cols = 3, vgap = HALF_SPACE, hgap = HALF_SPACE)
     flexGridSizer.AddGrowableCol(1,1)
         return True    
     HALF_SPACE = 5
     flexGridSizer = wx.FlexGridSizer(cols = 3, vgap = HALF_SPACE, hgap = HALF_SPACE)
     flexGridSizer.AddGrowableCol(1,1)
-    flexGridSizer.Add(nameLabelText, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
-    flexGridSizer.Add(nameControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
-    flexGridSizer.Add(button, flag=wx.ALIGN_RIGHT|wx.LEFT, border=HALF_SPACE)
+    if not useDirDialog:
+        flexGridSizer.Add(nameLabelText, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
+        flexGridSizer.Add(nameControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
+        flexGridSizer.Add(button, flag=wx.ALIGN_RIGHT|wx.LEFT, border=HALF_SPACE)        
+        flexGridSizer.Add(dirLabelText, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
+        flexGridSizer.Add(dirControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
+        flexGridSizer.Add(wx.StaticText(parent, -1, ""), 0)
+    else:
+        flexGridSizer.Add(nameLabelText, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
+        flexGridSizer.Add(nameControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
+        flexGridSizer.Add(wx.StaticText(parent, -1, ""), 0)
+        flexGridSizer.Add(dirLabelText, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
+        flexGridSizer.Add(dirControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
+        flexGridSizer.Add(button, flag=wx.ALIGN_RIGHT|wx.LEFT, border=HALF_SPACE)        
         
         
-    flexGridSizer.Add(dirLabelText, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
-    flexGridSizer.Add(dirControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND)
-    flexGridSizer.Add(wx.StaticText(parent, -1, ""), 0)
     if returnAll:
         return nameControl, dirControl, flexGridSizer, Validate, allControls
     else:
     if returnAll:
         return nameControl, dirControl, flexGridSizer, Validate, allControls
     else:
@@ -188,8 +225,8 @@ def CreateDirectoryOnlyControl( parent, dirLabel=_("Location:"), startingDirecto
 
         if os.getcwd() not in choiceDirs:
             choiceDirs.append(os.getcwd())                
 
         if os.getcwd() not in choiceDirs:
             choiceDirs.append(os.getcwd())                
-        if appdirs.documents_folder not in choiceDirs:
-            choiceDirs.append(appdirs.documents_folder)                
+        if appdirs.getSystemDir() not in choiceDirs:
+            choiceDirs.append(appdirs.getSystemDir())                
             
 
     if not startingDirectory:
             
 
     if not startingDirectory:
@@ -221,6 +258,9 @@ def CreateDirectoryOnlyControl( parent, dirLabel=_("Location:"), startingDirecto
         if dirName == "":
             wx.MessageBox(_("Please provide a directory."), _("Provide a Directory"))            
             return False
         if dirName == "":
             wx.MessageBox(_("Please provide a directory."), _("Provide a Directory"))            
             return False
+        if os.sep == "\\" and dirName.find("/") != -1:
+            wx.MessageBox(_("Wrong delimiter '/' found in directory path.  Use '%s' as delimiter.") % os.sep, _("Provide a Valid Directory"))            
+            return False
         if not os.path.exists(dirName):
             wx.MessageBox(_("That directory does not exist. Please choose an existing directory."), _("Provide a Valid Directory"))            
             return False
         if not os.path.exists(dirName):
             wx.MessageBox(_("That directory does not exist. Please choose an existing directory."), _("Provide a Valid Directory"))            
             return False
@@ -241,17 +281,25 @@ def CreateNameOnlyControl( parent, fileLabel, startingName="", startingDirectory
     fileLabelText = wx.StaticText(parent, -1, fileLabel)
     nameControl = wx.TextCtrl(parent, -1, startingName, size=(-1,-1))
         
     fileLabelText = wx.StaticText(parent, -1, fileLabel)
     nameControl = wx.TextCtrl(parent, -1, startingName, size=(-1,-1))
         
-    def Validate(allowOverwriteOnPrompt=False, noFirstCharDigit=False):
+    def Validate(allowOverwriteOnPrompt=False, validClassName=False):
         projName = nameControl.GetValue().strip()
         if projName == "":
             wx.MessageBox(_("Blank name.  Please enter a valid name."), _("Project Name"))            
             return False
         projName = nameControl.GetValue().strip()
         if projName == "":
             wx.MessageBox(_("Blank name.  Please enter a valid name."), _("Project Name"))            
             return False
-        if noFirstCharDigit and projName[0].isdigit():
-            wx.MessageBox(_("Name cannot start with a number.  Please enter a valid name."), _("Project Name"))            
-            return False
         if projName.find(' ') != -1:
         if projName.find(' ') != -1:
-            wx.MessageBox(_("Spaces in name.  Name cannot have spaces.") % infoString, _("Project Name"))            
+            wx.MessageBox(_("Spaces in name.  Name cannot have spaces."), _("Project Name"))            
             return False
             return False
+        if validClassName:
+            if projName[0].isdigit():
+                wx.MessageBox(_("Name cannot start with a number.  Please enter a valid name."), _("Project Name"))            
+                return False
+            if projName.endswith(".agp"):
+                projName2 = projName[:-4]
+            else:
+                projName2 = projName
+            if not projName2.replace("_", "a").isalnum():  # [a-zA-Z0-9_]  note '_' is allowed and ending '.agp'.
+                wx.MessageBox(_("Name must be alphanumeric ('_' allowed).  Please enter a valid name."), _("Project Name"))
+                return False
         path = os.path.join(startingDirectoryControl.GetValue().strip(), projName)
         if os.path.exists(path):
             if os.path.isdir(path):
         path = os.path.join(startingDirectoryControl.GetValue().strip(), projName)
         if os.path.exists(path):
             if os.path.isdir(path):
@@ -280,6 +328,29 @@ def CreateNameOnlyControl( parent, fileLabel, startingName="", startingDirectory
     return nameControl, flexGridSizer, Validate
     
 
     return nameControl, flexGridSizer, Validate
     
 
+def ValidateName(name, ext=None, hint="name"):
+    """ Returns an error string if there is something wrong with the name.
+        Otherwise it returns None
+    """
+    if name == "":
+        return _("Blank %s.  Please enter a valid %s.") % (hint, hint)
+
+    if name.find(' ') != -1:
+        return _("Spaces in %s.  %s cannot have spaces.") % (hint, hint.title())
+
+    if name[0].isdigit():
+        return _("%s cannot start with a number.  Please enter a valid %s.") % (hint.title(), hint)
+
+    if ext and name.endswith(ext):  # strip extension if provided
+        lenExt = len(ext)
+        name = name[:-lenExt]
+        
+    if not name.replace("_", "a").isalnum():  # [a-zA-Z0-9_]  note '_' is allowed and ext ending.
+        return _("%s must be alphanumeric ('_' allowed).  Please enter a valid %s.") % (hint.title(), hint)
+
+    return None
+
+
 def GetCurrentProject():
     projectDocument = None
     projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
 def GetCurrentProject():
     projectDocument = None
     projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
@@ -330,6 +401,16 @@ def GetPythonExecPath():
     return pythonExecPath
     
 
     return pythonExecPath
     
 
+def GetPHPExecPath():
+    PHPExecPath = wx.ConfigBase_Get().Read("ActiveGridPHPLocation")
+    return PHPExecPath
+
+
+def GetPHPINIPath():
+    PHPINIPath = wx.ConfigBase_Get().Read("ActiveGridPHPINILocation")
+    return PHPINIPath
+
+
 def _DoRemoveRecursive(path, skipFile=None, skipped=False):
     if path == skipFile:
         skipped = True
 def _DoRemoveRecursive(path, skipFile=None, skipped=False):
     if path == skipFile:
         skipped = True
@@ -362,16 +443,62 @@ def CaseInsensitiveCompare(s1, s2):
 
 def GetAnnotation(model, elementName):
     """ Get an object's annotation used for tooltips """
 
 def GetAnnotation(model, elementName):
     """ Get an object's annotation used for tooltips """
-    if hasattr(model, "__xsdcomplextype__"):
+    if hasattr(model, "_complexType"):
+        ct = model._complexType
+    elif hasattr(model, "__xsdcomplextype__"):
         ct = model.__xsdcomplextype__
         ct = model.__xsdcomplextype__
-        if ct:
-            el = ct.findElement(elementName)
-            if el and el.annotation:
-                return el.annotation
+    else:
+        ct = None
+            
+    if ct:
+        el = ct.findElement(elementName)
+        if el and el.annotation:
+            return el.annotation
             
     return ""
 
 
             
     return ""
 
 
+def GetDisplayName(doc, name):
+    if name:
+        appDocMgr = doc.GetAppDocMgr()
+        if appDocMgr:
+            name = appDocMgr.toDisplayTypeName(name)
+        else:
+            namespace, name = xmlutils.splitType(name)
+            if namespace and hasattr(doc.GetModel(), "getXmlNamespaces"):
+                for xmlkey, xmlval in doc.GetModel().getXmlNamespaces().iteritems():
+                    if xmlval == namespace:
+                        name = "%s:%s" % (xmlkey, name)
+                        break                    
+    
+        if name:
+            import activegrid.model.schema as schemalib
+            baseTypeName = schemalib.mapXsdType(name)
+            if baseTypeName:
+                name = baseTypeName
+
+    return name
+
+
+def GetInternalName(doc, name):
+    if name:
+        appDocMgr = doc.GetAppDocMgr()
+        if appDocMgr:
+            name = appDocMgr.toInternalTypeName(name)
+        else:
+            namespace, name = xmlutils.splitType(name)
+            if namespace and hasattr(doc.GetModel(), "getXmlNamespaces"):
+                for xmlkey, xmlval in doc.GetModel().getXmlNamespaces().iteritems():
+                    if xmlkey == namespace:
+                        name = "%s:%s" % (xmlval, name)
+                        break  
+                                          
+        import activegrid.model.schema as schemalib
+        name = schemalib.mapAGType(name)
+
+    return name
+
+
 #----------------------------------------------------------------------------
 # Methods for finding application level info
 #----------------------------------------------------------------------------
 #----------------------------------------------------------------------------
 # Methods for finding application level info
 #----------------------------------------------------------------------------
@@ -435,7 +562,8 @@ def GetAppDocMgrForDoc(doc):
 
 
 def GetAppInfoLanguage(doc=None):
 
 
 def GetAppInfoLanguage(doc=None):
-    from activegrid.server.deployment import LANGUAGE_DEFAULT
+    from activegrid.server.projectmodel import LANGUAGE_DEFAULT
+    
     if doc:
         language = doc.GetAppInfo().language
     else:
     if doc:
         language = doc.GetAppInfo().language
     else:
@@ -449,3 +577,159 @@ def GetAppInfoLanguage(doc=None):
             doc.GetAppInfo().language = language  # once it is selected, it must be set.
         
     return language
             doc.GetAppInfo().language = language  # once it is selected, it must be set.
         
     return language
+
+def AddWsdlAgToProjectFromWsdlRegistration(wsdlRegistration):
+    """Add wsdl ag for registry entry."""
+
+    wsdlPath = wsdlRegistration.path
+    rootPath = None
+    serviceRefName = wsdlRegistration.name
+    
+    agwsDoc = _InitWsdlAg(wsdlPath, rootPath, serviceRefName)
+
+    if (agwsDoc == None):
+        return
+
+    serviceRef = agwsDoc.GetModel()    
+    
+    serviceRef.serviceType = wsdlRegistration.type
+
+    import activegrid.server.deployment as deployment
+
+    if (serviceRef.serviceType == deployment.SERVICE_LOCAL):
+        serviceRef.localService = deployment.LocalService(
+            wsdlRegistration.codeFile)
+        
+    elif (serviceRef.serviceType == deployment.SERVICE_DATABASE):
+        serviceRef.databaseService = deployment.DatabaseService(
+            wsdlRegistration.datasourceName)
+        
+    elif (serviceRef.serviceType == deployment.SERVICE_SOAP):
+        pass
+    
+    elif (serviceRef.serviceType == deployment.SERVICE_RSS):
+        serviceRef.rssService = deployment.RssService(wsdlRegistration.feedUrl)
+        
+    elif (serviceRef.serviceType == deployment.SERVICE_REST):
+        serviceRef.restService = deployment.RestService(
+            wsdlRegistration.baseUrl)
+    else:
+        raise AssertionError("Unknown service type")
+
+    _AddToProject(agwsDoc, addWsdl=True)
+    
+
+def AddWsdlAgToProject(wsdlPath, rootPath=fileutils.AG_SYSTEM_STATIC_VAR_REF,
+                       serviceRefName=None, className=None, serviceType=None,
+                       dataSourceName=None):
+    """
+       wsdlPath: path to wsdl from rootPath. If wsdlPath is absolute, rootPath
+       is ignored. rootPath is also ignored when rootPath is set to None. 
+       rootPath: defaults to ${AG_SYSTEM_STATIC}.
+       serviceRefName: If None, it will be set to the wsdl file name without
+       the .wsdl file extension.
+       className: if not None, will be used for the the wsdlag's ClassName.
+       serviceType: defaults to local.
+       dataSourceName: if serviceType is deployment.DATABASE, the ds must be
+       provided.
+    """
+    import WsdlAgEditor
+    import XFormWizard
+    import activegrid.model.basedocmgr as basedocmgr
+    import activegrid.server.deployment as deployment
+
+    if (serviceType == None):
+        serviceType = deployment.SERVICE_LOCAL
+
+
+    agwsDoc = _InitWsdlAg(wsdlPath, rootPath, serviceRefName)
+
+    if (agwsDoc == None):
+        return
+
+    serviceRef = agwsDoc.GetModel()    
+    
+    serviceRef.serviceType = serviceType
+
+    if (serviceType == deployment.SERVICE_DATABASE and dataSourceName != None):
+        serviceRef.databaseService = deployment.DatabaseService(dataSourceName)
+    else:
+        serviceRef.localService = deployment.LocalService(className=className)
+
+    _AddToProject(agwsDoc)
+        
+
+def _AddToProject(agwsDoc, addWsdl=False):
+    import activegrid.model.basedocmgr as basedocmgr    
+    projectDoc = GetCurrentProject()
+    agwsDoc.OnSaveDocument(agwsDoc.GetFilename())
+
+    files = [agwsDoc.fileName]
+    types = [basedocmgr.FILE_TYPE_SERVICE]
+    names = [agwsDoc.GetModel().name]
+    if (addWsdl):
+        m = agwsDoc.GetModel()        
+        wsdlName = os.path.splitext(os.path.basename(m.filePath))[0]
+        appDocMgr = projectDoc.GetAppDocMgr()
+        if (appDocMgr.findService(wsdlName) == None):
+            m = agwsDoc.GetModel()            
+            files.append(m.filePath)
+            types.append(None)
+            names.append(wsdlName)
+    
+    ProjectEditor.ProjectAddFilesCommand(projectDoc, files, types=types,
+                                         names=names).Do()
+    
+
+def _InitWsdlAg(wsdlPath, rootPath=fileutils.AG_SYSTEM_STATIC_VAR_REF,
+                serviceRefName=None):
+
+    projectDoc = GetCurrentProject()
+    appDocMgr = projectDoc.GetAppDocMgr()
+
+    if (serviceRefName == None):
+        serviceRefName = os.path.splitext(os.path.basename(wsdlPath))[0]
+    
+    if (appDocMgr.findServiceRef(serviceRefName) != None):
+        return None
+
+    import WsdlAgEditor
+    import XFormWizard
+    import activegrid.server.deployment as deployment
+
+    template = XFormWizard.GetTemplate(WsdlAgEditor.WsdlAgDocument)
+    ext = template.GetDefaultExtension()
+    fullPath = os.path.join(appDocMgr.homeDir, serviceRefName + ext)
+
+    agwsDoc = template.CreateDocument(
+        fullPath, flags=(wx.lib.docview.DOC_NO_VIEW|wx.lib.docview.DOC_NEW|
+                         wx.lib.docview.DOC_OPEN_ONCE))
+    
+    serviceRef = agwsDoc.GetModel()
+    serviceRef.name = serviceRefName
+
+    if (rootPath == None or os.path.isabs(wsdlPath)):
+        serviceRef.filePath = wsdlPath
+    else:
+        # make sure to use forward slashes for the path to the .wsdl
+        wsdlPath = wsdlPath.replace("\\", "/")
+        
+        if not wsdlPath.startswith("/"):
+            wsdlPath = "/%s" % wsdlPath
+        serviceRef.filePath = "%s%s" % (rootPath, wsdlPath)
+
+    agwsDoc.fileName = fullPath        
+
+    return agwsDoc
+
+
+def GetSchemaName(schema):
+    return os.path.basename(schema.fileName)
+
+
+class AGChoice(wx.Choice):
+    """Extension to wx.Choice that fixes linux bug where first item of choices
+    passed into ctor would be visible, but not selected."""
+    def __init__(self, parent, id, choices=[]):
+        super(AGChoice, self).__init__(parent=parent, id=id)
+        self.AppendItems(choices)
index af2b9dad1b6b78f914895d7826d77a932cb48e50..ab70874adf0c2bee5c4436b9c5f082815be97aa9 100644 (file)
@@ -13,7 +13,12 @@ import wx
 import wx.xrc as xrc
 import wx.wizard
 
 import wx.xrc as xrc
 import wx.wizard
 
-
+WHITE_COLOR = wx.Color(0xFF, 0xFF, 0xFF)
+LABEL_FONT = wx.Font(8, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName="Arial")  
+SELECTED_LABEL_FONT = wx.Font(8, wx.SWISS, wx.NORMAL, wx.BOLD, faceName="Arial")  
+TINY_FONT = wx.Font(6, wx.SWISS, wx.NORMAL, wx.BOLD, faceName="Arial")
+ELLIPSIS_FONT = wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName="Arial")
+ACTIVEGRID_ORANGE_COLOR = wx.Color(0xF9, 0x9E, 0x1B)
 #----------------------------------------------------------------------------
 # Classes
 #----------------------------------------------------------------------------
 #----------------------------------------------------------------------------
 # Classes
 #----------------------------------------------------------------------------
@@ -22,9 +27,14 @@ class BaseWizard(wx.wizard.Wizard):
 
 
     def __init__(self, parent, title, pos=(-1,-1)):
 
 
     def __init__(self, parent, title, pos=(-1,-1)):
-        wizardBitMap = getWizardBitmap()
-        wx.wizard.Wizard.__init__(self, parent, wx.NewId(), title, wizardBitMap, pos=pos)
+        self.bitmap = getWizardBGShorterBitmap()
+        self.title = title
+        wx.wizard.Wizard.__init__(self, parent, wx.NewId(), title, self.bitmap, pos=pos)
+        self.myDC = wx.MemoryDC()
+        self.crumbs = []
+        self.firstPage = None
 
 
+        
     def GetDocument(self):
         if self.GetParent() and hasattr(self.GetParent(), 'GetDocument'):
             return self.GetParent().GetDocument()
     def GetDocument(self):
         if self.GetParent() and hasattr(self.GetParent(), 'GetDocument'):
             return self.GetParent().GetDocument()
@@ -34,12 +44,94 @@ class BaseWizard(wx.wizard.Wizard):
     def SetPrevNext(self, prev, next):
         prev.SetNext(next)
         next.SetPrev(prev)
     def SetPrevNext(self, prev, next):
         prev.SetNext(next)
         next.SetPrev(prev)
-
-
+        
+    def RunWizard(self, firstPage):
+        self.firstPage = firstPage
+        return wx.wizard.Wizard.RunWizard(self, firstPage)
+        
+    def BuildCrumbsList(self, onPage):
+        def PastThisPage(currentPage, pageToCheck):
+            foundPageToCheck = False
+            tempPage = self.firstPage
+            while hasattr(tempPage, '_next'):
+                if tempPage == currentPage:
+                    return foundPageToCheck
+                if tempPage == pageToCheck:
+                    foundPageToCheck = True
+                tempPage = tempPage._next
+        self.crumbs = []
+        currPage = self.firstPage
+        while hasattr(currPage, '_next'):
+            self.crumbs.append(currPage.title.GetLabel())
+            if currPage.pauseCrumbTrail and not PastThisPage(onPage, currPage):
+                self.crumbs.append('?')
+                return
+            currPage = currPage._next
+            
+        
+    def GetBreadcrumbsBitmap(self, page):
+        bitmap = getWizardBGShorterBitmap()
+        highlightText = page.title.GetLabel()
+        self.BuildCrumbsList(page)
+        self.myDC.BeginDrawing()
+        self.myDC.SelectObject(bitmap)
+        #self.myDC.SetFont(TINY_FONT)
+        #self.myDC.DrawText(self.title, 10, 35)
+        #print "Title was w=%i, h=%i" % self.myDC.GetTextExtent(self.title)
+        x = 20
+        y = 50
+        for crumb in self.crumbs:
+            if crumb == highlightText:
+                self.myDC.SetTextForeground(ACTIVEGRID_ORANGE_COLOR)
+                self.myDC.SetFont(SELECTED_LABEL_FONT)
+            else:
+                self.myDC.SetTextForeground(WHITE_COLOR)
+                self.myDC.SetFont(LABEL_FONT)
+            lines = self.BreakIntoLines(crumb)
+            offset = 0
+            w = h = 0
+            for line in lines:                  
+                offset += h + 3
+                if line == '? ':
+                    decisionBM = getDecisionBitmap()
+                    x1 = (bitmap.GetWidth() - decisionBM.GetWidth()) / 2
+                    self.myDC.DrawBitmap(decisionBM, x1, y + offset, True)
+                else:
+                    self.myDC.DrawText(line, x, y + offset)
+                w, h = self.myDC.GetTextExtent(line)
+            y += 30 + offset
+        self.myDC.EndDrawing()
+        self.myDC.SelectObject(wx.NullBitmap)
+        return bitmap
+    
+    def CenterTextUnderParent(self, dc, parentWidth, parentX, text):
+        xbase = parentX + parentWidth / 2
+        w,h = dc.GetTextExtent(text)
+        return xbase - w / 2 
+           
+    def BreakIntoLines(self, text, maxLineLength=22):
+        words = text.split(' ')
+        retval = []
+        count = 0
+        currentLineLength = 0
+        currentLine = ''
+        for word in words:
+            if len(word) + currentLineLength >= maxLineLength:
+                retval.append(currentLine)
+                currentLine = word + ' '
+                currentLineLength = len(word) + 1
+            else:
+                currentLine += word + ' '
+                currentLineLength += len(word) + 1
+        if currentLine:
+            retval.append(currentLine)
+        return retval
+        
 class TitledWizardPage(wx.wizard.PyWizardPage):
 
 
 class TitledWizardPage(wx.wizard.PyWizardPage):
 
 
-    def __init__(self, parent, title=None):
+    def __init__(self, parent, title=None, pauseCrumbTrail=False):
+        self.pauseCrumbTrail = pauseCrumbTrail
         self._prev = None
         self._prevFunc = None
         self._next = None
         self._prev = None
         self._prevFunc = None
         self._next = None
@@ -48,6 +140,9 @@ class TitledWizardPage(wx.wizard.PyWizardPage):
         self.SetSizer(wx.BoxSizer(wx.VERTICAL))
         self.MakePageTitle(title)
 
         self.SetSizer(wx.BoxSizer(wx.VERTICAL))
         self.MakePageTitle(title)
 
+    def GetBitmap(self):
+        return self.GetParent().GetBreadcrumbsBitmap(self)
+        
     def SetTitle(self, title):
         if not title: title = ""
         self.title.SetLabel(title)
     def SetTitle(self, title):
         if not title: title = ""
         self.title.SetLabel(title)
@@ -291,3 +386,2042 @@ def getWizardImage():
     stream = cStringIO.StringIO(getWizardData()) # NOTE: This reverts us to the bitmap Peter likes.
     return ImageFromStream(stream)
 
     stream = cStringIO.StringIO(getWizardData()) # NOTE: This reverts us to the bitmap Peter likes.
     return ImageFromStream(stream)
 
+#----------------------------------------------------------------------
+def getWizardBackgroundData():
+    return \
+'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\xac\x00\x00\x01\x87\x08\x02\
+\x00\x00\x00\xa2O.\xce\x00\x00\x00\x03sBIT\x08\x08\x08\xdb\xe1O\xe0\x00\x00 \
+\x00IDATx\x9c\xc5\xbdy\x94^\xc7u\x1fx\xeb\xeb\xd7\xdd@\xa3\xb1\x10\x04\x08\
+\x80\x04\x08\x82+\xb8\x8b\xa4HK\xa4<\x92M*\x8e#Y\x96\x9c\x13I\xf6$\xb2\x8e\
+\x97\xf1L&c+\x93\x8cg\xec\xe33\xf6\xc9\xccx\xc6g\x12\'>\x89c;\xb6\x13\xc7\
+\x1eG\x923^\x8emY\x96M\x91>\xb1\xb9J$ER\xe0N\x80\x00\x01\x02\x8d\xbd\x81\x06\
+\x1a\xbd\xd6\xfc\xf1^U\xdd\xb5\xaa\xde\xd7MM\x11\xec\xef\xbdZn\xdd\xaa\xfb\
+\xab{o-\xef=\xf7\xde\xef\xfdI\xd0\x82\x07\x9f\xae\xbdo\xa3x\x8c\xc8\xd9\x06\
+\x07\xae\xbbp\x0e\xe7\xe79=/H\xa9\x04"\x81\x9a\x91\xcbH\x8d\xd1\xd9J0{\x89\
+\xc3<c\x887T\x9b#\x9c\xe0K\xe7@\xb6\xc2n\x13\xcb)\xbb7\x17<)\xe2\x8b\r\x01\
+\x00\x80\x81\xc5P\xa6\xebu\x04x\xdf\xfd\xab\xe4\xbb\x8e\xbfoMp\x0cp\x16\xb0B\
+n5Z\xed\xf1\xbe\x08 y\\b\xc9\x81\xb3\xfee(;\xe7\xd4A\xc2\xe2\x9b\xae\x983G\
+\x0ck\x1b\x1f4!\x96\\;\xe7\xc1;p:\x12\xfb\x88\x7f\xa5j`u\x03\xadN\xf2\xe6\
+\xbd\xefX\xf2\x89\x07\x92M0\xd6\xa6\xb6\x9d\xe9P\x99t\xe1\x0b\x9d\xa0\xb0\
+\x89\xf2{\xf0V/\xc5\xf8F\xa6q\x86\xb4@\x14\x80\x92\x9cp\xa0\x14\xa9\t\x06~\
+\x03y\x83\x94\xc5\xb2\r\xf1*\x1e\xda\xea\xd41\xdd\x83\x1a\xbet\xac\x93s@\xb1\
+*\xack\x11\x06\x99\x15\x02\x08D\x1ea\xbf\x01\x00\xbc\xf7\xb5\xb2\x0c8P\xe2\
+\xab\x03\xee\x1a\x0cgS\x01\x18T\xac \xc1\x94\x18&\x06\xbe\xca;\xa9\xe4!\xaa\
+\xf7bN\x91\x12\xc6@\xcbd@v\x8d\x98\xd5\xeab\x11E\x13\x90|\xd8\xfcG\x04\x18\
+\x82\xf4\xac\x03\xd1\x00\xca\x94Jyh\xf9az\xdc\xe7:\x91\xe7\xad\x80#\xee\xf7\
+\x1a~\x12:M\x11\xbb\x94\xeaI\x82U\xb5E\xc4\x83W4\x9c\xe7Yy<\x8a\x89\n\xa9IiQ\
+\x04!-u\x13&-G\x8f6\xda\xd3@\xea\xeb\x00\xf6\x1a\xe52\xf4\xc1\x01*\xa4\xf9tX\
+\x0f\xf5B\x80\x1dS\x19Lw\xcf\xf3<\xdey\xf0\x86\x1ap\xc6u\x8c\t\xde\x86\xa9\t\
+\x18\x02:5P\x81\x80\x18\xcfz@\xe6\xe4]\xe4\\\x061x\xe0\x16:W\xe2@\xed"\xe7\
+\xf4\x89\xab\x96Se\xa3\x86\x1f\x05I\xf9\x99\x87\xe2\x1c\xb4z\xb1u"\x02\xb3R\
+\x97P*UF;Te\xfa\x048\xa8\xae@\xaf\x11^\xc8\x1c!c[\xdf\x81\x83\x91\xc1\xf2\
+\xc2\xd2\xa0G\xad\x1d\xf1\xde%0\x03L\xc0\xedm\xc1\x94\xac\xca\xdc\xc4\x01\
+\x1b&a\xe8"\x148\xef\xbc\xbb\xf3\x96\xdd\x9f\xf9\xc4\x87\x00\xe0O\x1f\xf9\
+\xfa\x13\xcf\xbd66\xda\xc9t\xe6\xc2\xec\xd8\xd8\xe8\xa5\xf9\x85\xe5\xe5\xe5\
+\xb1\xa6i\x9a\x91\x8b\x97\xe6\xd4\xaa\xcc\xd9A\xbcA\xd7\x9e\xfe\x92\xcc\xda|\
+)?\xb6\xcd\xc0H9\xe7\x9a\xc1\xd2\x87v\xbc\xbdu\xed\xec\x93S;\x0e\x9c\xdf\xd4\
+\x83V\xdd\xbcWe 7\xbe]\x9a\xfd\xf2l\x0e\x94x\x0f\xe0\x82ca\x18\xac\x8d\x93\
+\x13\xdb\xb6n\x9a:q\xf6\xdc\xcc\xac\xa5\xc9\x9dO\xf7\xb1\x01\x9b7N~\xe0\xbd{\
+\x01\xe0\xf1g_}\xff]7\xde\x7f\xcf^\x00\x98:y\xf6/\xfe\xfa\xf9\xef\xfb\xaeo{\
+\xf4\xc9o>\xf7\xd2\x81o\xbf\xf7\xe6\xebvo\xffw_\xf8K\xb59\x1c\x04\xda\x887\
+\xdc\xc0\x8a!\x86L\n\xc7J\xa5\xb9t\xce\x8d\x8f,~\xee\xf6g?\xbe\xe7M\x00\xf8\
+\x077\xbe\xf2\x8b\xcf\xdf\xf5\xf0\x91k\xb2e\x12\x8bC\x87\xa29\xd73\x88\xb8\
+\xe4Q2\x1c\xd0\xf0\x9e\x9b\xaf\xf9\xdcg?r\xfd5;\xde>z\xea\xd7\xbf\xf0\x97\
+\x8f>\xb5\x8fRm=A\x08j\xc0l\xdaKo\x1c\xbe\xf5\xc6\xabo\xbda\xe7\x9f>\xf2\xf5\
+\xcb6\xae\xfb\xfe\xef\xf9\xc0\xe2\xd2\xf2\x0b\xaf\x1e\xfa\xe4G\xee\xcf\xb4\
+\x85hWk\xb9\x91.!g\xa8AX5L\xff@\x83\x8b\xd2\x81\x82n\\\xd5\xfa\xf6\xed\x87\
+\xbf\xfb\xea\xb7\xda\xc8\xcb\xc6/\xfd\xfd\x1b_\x19\x1fY49\xe8\x89\x80\xcc\
+\x9a\xf7\xbb\x14d\x8dk\xc6\xc7>\xf3\x89\x0f\xddr\xc3\xae\xb1\xd1\xe6\xba\xab\
+\xb7}\xe6\x13\x1f\xdc\xb0n\xad,\xe8:\xde\xba_\x95\xcb\xa3\'\xce\xec{\xed\xd0\
+\xab\xfb\xdfy\xee\xa5\x03\x00\xf0\xec\xbe\xfdKKK\xdf\xf7\xb7\xbe\xed\xf0\xb1\
+\xd3\xfb^?l\xb1\xd4\xb4|\x81\x8a\x80\xec\xa4\x80\xf2gv\x1cn\xb3\x9a\xed}\xdb\
+\xa6\xee\xdcr\xb2\xbd\x9eY\x18\xfd\x93\x83\xd7\x9e\x9b\x1f\xc59\xb7\xae\x9d\
+\x1d\x1fY\x8a\xb7\x9b\xc6\xe7F\x07KsK\x9aK\xdbS|\x19e\x96\xd6\xfe\xeaC>;1\
+\x0ed\xc29\xd2\x0c&\xd6\x8e\xc5\xdb\xf1\xf1\xb1\xb5k\xc6\xce]\x98E\xf9S\xee\
+\xbcG\xd8\x86u\x13k\x06\x83\x01\x00\x1c>v\xfa\xf8\xa9s?\xf0\xb1\x0f\xfc\x9b\
+\xdf\xfe\xf2u\xbb\xb7\xeb\xb9\xbd4\x07b\x91\x18O\n\xa4z_yx\xff\xf6c\x9f\xbe\
+\xfe\xd5\xf6zni\xe4\xc9\xa9\x1d\xe7\xe6G\x01\x89\xe1\xc9\xa9\x1d\x9f\xba\xfe\
+\xb5mk/\xb6y\x9e8\xb6\xe3\xc2\xc2\x98B\xa8\xff\x00v\xa0,gE\t\x15p`\xad\x04XE\
+\xb0WO\xed\xe3\x85\x0b\x97~\xefK\x8f\xef\xd9y\xc5e\x1b\'/\xce\xce\xfd\xfe\
+\x9f?y\xfc\xf4\xb9\xb8Z\x12\n\x8bu\xc4\xb04\xcfbOO\xcf\x9c>{\xde/\xfb\x8b\
+\x97\xe6\xa7N\x9e}\xe3\xe0\xd1W\xde<rx\xea\xf4\xe6M\xeb\x19\x0f14\x8c\'\xce\
+\xb4\xde\x9c\xd4\x00VP\xed\xd6\xfa\xb0\xb0<\x88U\xc4\xb1r\xe0\xfc\xa6\x9f\
+\x7f\xe6\xde\xcf\xdc\xf4\xca\xe5kf\xbfqr\xeb/\xef\xbb\xd3\xc7\xf5yT\xf1p\x01\
+\xaf\xe0\xaa\xfeP\xa5>\xd0\xf7i\xaa\xd9z\xe4\xc9o\xbeyh\xea\xba\xab\xb7\x1d\
+\x9e:\xfd\xfa[G\x8b.\x93\x8b\xb8\x10n\xe9\x8b\xaf\xbe\xfd\xf2\x1bG\xbc\xf7o\
+\xbcu\xec\xe0\xe1\x13s\xf3\x0b/\xbczhyiy\xff\xdb\xc7\xbb<\x02\nD\x13\xe4\xd7\
+\x06\xd2\x82^\xcfU\x14\x96\x93\xb1}q1\xf1\xb0\xb0<rq\xb1IK\xce\xbe\xeb\xdc\
+\xa7O\\\xf9\xf4\x89+\x9b\xc1\xd2\xa2\x1f\xc1\x14S\x93z\x06\xbc\r\xc1\x16\xf0\
+\x19\x14\x8a8\xe0;\x90\xc3\x86\x83\xef\x9c8\xf4\xce\xc9\x96\x10\xaa\x1ex\x0c\
+\xaaY\x1dqKKK\x8bK\x8b\xe0`iiiii\xc9\x83_^\\\x06\x80\xf9\xf9ye\x83\n\x00"\
+\x08$=f\x17v\xac\x9b\xdd=y\xee\xaa\xc9\x995#K\x97\x96F\xce\xcf\x8f\xbdyn\xd3\
+\x1bg\xd7\xe7\x1b\xb6i\xcd\xd2-\x9bN\xc5RGf&_:{\xf9\xb9\xb9\x06\x006\x8c/\
+\xee\x9e<wqqt\xf3\xf8\xa5\x98\x7ft\xb0\xb4{\xf2\xdcD\xd3\xf9}\xef\\\x9c\xbc\
+\xb482:\xb2\xbck\xdd\xf96f\xa2Y8yi\xed\xd4\xa5\xc9\xf1\xc1\xe2\x95\x133\x13\
+\xcd\xc2\xc5\xc5\xd16\xf2\xf8\xec:\xcfw\xf9\xfc5\xeb\xa7c\x9e\x8b\x8b\xa3rz\
+\xb9~l\x9eqxpf\xc3\xb1\x8b\xebXW\xc8y\xe0X\xd3l\xbd|\xe3\x86\xf5\x13ss\x0bG\
+\x8e\x9fY\x98_P\xdao\xa3b\xc7\xd6\xcb6\xac\x9f\x98:qv\xfa\xfc\xc5\x90\x17/%y\
+\x1f\xd6\x88pX\xb3ft\xcb\xa6\xf5\xe3\xe3\xa3ss\x0bGO\x9cY\\\xf2bi\xd1\xf9\
+\xf6\xbf\xb0\xa8,7\xab\xa4a\xd1\xdc+\n\xae\xdb/?\xfd\xd1\xdd\x07\xde\xb3\xe5\
+\xc4\x8e\x89\x0b\xd8A\x9b\x9a\x9dx\xe2\xd8\x8e\xdfz\xf5\x96\xa3\x17\xd6J_\
+\xe1\xaa\xc9K\x1f\xbbf\xffw^\xf5\xf6\x965\xb3\x93\xa3]\x07\xcd,\x8c\x1e\xb90\
+\xf9\x95\xb7w\xff\xc1\x81\x1b>\xbc\xf3\xe0\x0f\xde\xf42\x00\xackR\xf7M\x8e.\
+\xfc\xdc\xbdO\xcd/\x0f\x00`v\xb1\xf9\xdf\x9f\xb9\xef\x9b\xa7\xb7\\>>\xfb\xb3\
+\xef}r\xd3x\xb7\xd0\xf1\xa5\x83{~\xfd\x95;6\x8d]\xfa\'w>\xbbs\xb2\x03\xc7\
+\x89\xd9\x89\x9fy\xfa\xfdS\xb3\x93]#=\x00\xc05\xeb\xa7\x7f\xfe\xdb\x1e[\x1b \
+\xf5\xa5\x83{~\xfd\xe5\x04\x82\xf1\x91\xc5\xef\xdb\xf3\xfaw\xed:x\xd5\xba\
+\x99\xc8\xe1\xdc\xd2\xc8\xd1\x8b\xeb\xfe\xf8\xadk\xbf\xf0\xc6\xdeeC\xc7l\xde\
+\xb4\xfe\xc3\x1f\xb8\xf3\xa1\x07n\xbf\xe2\xf2\x8dc\xa3\xcd\xfc\xc2\xe2\xf1S\
+\xd3\x0f?\xf6\xe2\x1f\x7f\xf5k;\xb6^\xf6\x13\x9f\xfd\xc8\xc4\xda\xb1\x87\x1f\
+{\xf1?\x7f\xf9\xf16\xff\xe8\xe8\xe8\x7f\xf3\xa9\x07\xef\xd8\xbb\xfb\xd5\xfd\
+\xef\xfc\xeb\xdf\xf9\xf3\xdbn\xd8\xf9\xc9\xbfs\xff\xde\xeb\xae\x1a\x1bm~\xeb\
+\xf7\xff\xea\xff\xfd\xf2\x13\xe0`l\xb4\xf9\x91O>x\xc7\xde\xdd\x87\x8f\x9d\
+\xfe\xd7\xff\xf1\xcf\x82W\x98\xd6]wn\xdf\xfc\x91\xef\xb8\xfb\xbe;\xae\xdf\
+\xb0~\x02W\xfa\x07\x7f\xf1\x94\xcee\x1b\xd0\xdapw\xab\x05\xd3\'p\xce9\xf0?r\
+\xf37?y\xddk\xb1\x8fp\xd8\xb6\xf6\xe2\xc7\xf7\xbcy\xfd\xc6\xe9\x9fy\xfa\xfd\
+\xef\xcc\xac\xc5D\xee\xdcr\xe6g\xeey\xfa\x9a\xf5\xe7X\x91\xc9\xd1\x85\x9b6\
+\x9d\xb9i\xd3\x99\xc9\xd1\x85\x99\x85\xd1\xe8\xeb\xe1p\x19R\x0cm\x98h\x16\
+\xafX;\x1b\xe3[\xcdqbn\xc3\xeb\xd3\x9b\xee\xd9:\x15\x99\xb9\x7f\xfb;\x7fx\
+\xe0\xc6\xb61mx\xdf\xb6\xa3\x91\x87\xb9\xa5\x91\xc7\x8f\xed\x884\xf7\xac?\
+\xfbO\xee|6\x16\x8fa|d\xe9\x9a\xf5\xe7~\xfc\xf6o\\=y\xfe_\xbcp\x8f\\\xa0\xbc\
+\xe7\xf6\xeb>\xf7\xd9\x8f\xdcr\xc3.\x1c\xb9s\xfb\xe5w\xdfz\xed\xdd\xb7\xeeyv\
+\xdf\x81v\xdd\xe6\xd5\xfd\xef\xc4\xd4\x81\x83\xfb\xee\xb8\xfe\x96\x1bvm\\\
+\xbf\xee\xe3GO}\xf6\xef~\xe8\x8a\xcb7\xb6IW\\\xbe\xa1\x15\xcc\xc8`\xd0\xe6\
+\xd9\xb3\xf3\x8a\xdf\xf8\xe2\xc3\xe7.\xcc\xe2\xb1\xf8\xa1\xfbn\xf9G\x9f\xf9\
+\xee\x9d\xdb/\x97\x95\xee\xbd\xee\xaaW\xde<"\xbb\x91l2\x19!\xe6i26ud\xb0\x0c\
+\x00\x11\x01S\xb3\x13\x87g\xd6\x03\xc0\xce\xc9\xf3Q~\xb7m>\xf9\xe9\xeb_\xfb\
+\xc5o\xdc\t\xc1\xa6\xdc\xb0iFE@\x0co\x9d\xdf\xf0\xf0\xe1\xab\xdf\xb7\xed\xa8\
+Y\xb1\x08\xf3\xcbI\x18\x97\x96\x1a\x00X\xf6\xcb\x0f\x1f\xde\xf5\xd1\xdd\xfb#\
+{\xf7n\x9d\xfa\xb3C\xd7\xc6\xa9\xa3\x03\xff\xc0\xf6T\xc57Ooy\xe9\xcc\x96\xf6\
+zrt\xee\xa7\xef\xfe\xdam\x9bO\xc6\xd4\xa9\xd9\x89\xd9\xc5\x06+\xad\x8f\xefy\
+\xf3\xd0\xcc\xfa\xcf\xbfq3f\xe3\xda\xab\xb7\xff\xaf\xff\xc3\xdfk\x85qqv\xee\
+\xd9}\x07\x0e\x1c>\xde\x8c\x0cv\xed\xd8r\xf7\xad{\x1ez\xe0\x8e{n\xbbv~aql\
+\xb4Y\\Z\xc6\x05/\xcd/\x00\xc0\xd6\xcd\x1b\xfe\xd1?\xf8\xdb\x13k\xc7\xcf\xcd\
+\xcc\x9e8}n|\xac9~\xaa\xeb%\xef\xbb<\x17f\xe7\x00\xf0l\xc0\xdf\xb1w\xf7O\xff\
+\xc3\xbf\xbbar-\x00\x9c\x99\x9e\xd9\xf7\xfa\xe1\xb7\x8f\x9e\x04\x80\x9b\xae\
+\xbd\xf2\xb6\x1b\xaf\xfe\xd8\x83\xef}\xdf{n\xe0\xfd\x15t\xa1\x93\x1e\xb4\x08\
+\x0e\x9c1\xdb\xf6\x00\x00\x0bK\x83\xdfx\xf9\xf6\x89f\xf1\xa3\xbb\xf7?|\xf8\
+\xea\xdf{\xf3\xc67\xa7\xd7\x03\xc0u\x1b\xcf\xff\xe8\xcd/~\xc7U\xdd\xe2\xc3\
+\xfb\xb6\x1d\xdd\xb8\xe6\xd6ss\x8d\xf70>\xb2\xfc\xa37\xbf\x88\x110\xb30\xfa\
+\xb5\xe3\xdb\xa6\xe7\xc7wM\xce\xdc\xb3ujjv\xe2\xa7\x9fz\xe0\xf5\xb3\x93[\xd7\
+l|\xf4\xc8\xce\xe9\xf9\xf1\x9b/;}\xd3\xa63m\xe6v\xbcN\xcf\x8f\xb7\xb7\'/)k&1\
+\xbc:\xbd\xf9k\xc7\xb7E6\xee\xbdb\xea\xca\x89\x99h\xf5\xafY?\x8d\xc5\xfcG\
+\x07\xae\x8d\x83\xe2\x87\xf7\xee\x8bI3\x0b\xa3\xff\xfe\x95[\x1f9\xb2\xeb\xc2\
+\xe2\xe8-\x9bN\xfd\xc3\xdb^\x88\xcc|\xea\xfa\xd7\x1e9\xb2\xab31\x00\xa3c\xa3\
+?\xf6\xfd\x1fn\x11\xf0\xe6\xa1\xa9_\xf8\xb5?|v\xdf~\x08\xe3\xe9\xc1\xfbo\xff\
+\xd1O\x7f\xf8\xba\xab\xb7\xa9\xbd\xdc^\xb4\x82\xfc\xe3\xaf~\xfdO\x1f\xf9\xfa\
+\x81\xb7\x8f\xaf]36{i\x1e,\x8f\xd2\x038\xd8\xb0n\xed\xe7>\xfb\x91\xb6\xe0\
+\xb3\xfb\xf6\xff\xea\x7f\xfa\x8b\x17^=\xd4\xa6\x8f\x8c\x0c\xbe\xf7\xc1{\xb1^\
+a\xb5\xea\xd7\xac\n\x00h\xcd\x01\xdf8Fa\xc9\xfb_\xdew\xe7\xc3\x87w\xbdxjs\
+\x8c|\xe3\xec\xfa\x9f\x7f\xee\xbe=\x1b\xce\xb5\xc2\xde\xb2f\xf6\x96M\xa7\x9e\
+8\xb6\r\x00\xee\xd9z2J\x05\x00\xde:\xbf\xe1_>\x7f\xd73\'\xb6\xcc-\x0e6\xad]\
+\xfa\xf0\xce\x83\xaf\x9d\xbd\xec\xf5\xb3\x93\x00\xf0\xc4\xb1mO\x1c\xdb\xe6\
+\x1c\xfc\xd8\xad\xfbb\xbf/,\x0f~\xe9\xc5\xbb\x8e^X\x0b\xd0\xc28\xe7o/\xfa\
+\x91G\x8e\xec\xba\x7f\xfb\xd1\xd6S\x99\x1c]xh\xe7\xa1h\xf5\x1f\xday(z0S\xb3\
+\x13O\x1e\xefl\xc1\x9e\xf5g\xbfk\xd7\xc1H\xe4\xdf\xee\xbb\xa33"\x00O\x9f\xb8\
+\xf2\xc4\xd7\'\xfe\xdd\x07\x1fn\xf5\x0111\x00w\xdct\xf5\x7fu\xdf-\x00p\xfc\
+\xd4\xf4\xcf\xfe\xd2\x17_y\xf30f\xef\xab\x8f\xbfxzz\xe6\x9f\xff\xd4\x0f\xb6\
+\x02\x03C\xb4\x0f?\xf6\xc2/\xfc\xea\x1f.,-\x01\xc0\xf4\x8cb\rY\xf8\xf0\x07\
+\xeelM\xcf\x9b\x87\xa6~\xee\x97~\xef\xf8\xe9\xf31iii\xf9\x0f\xbe\xf2\xd4\xe9\
+\xb3\xe7\xff\xd9?\xfet\xdc4\x02:\xe9\xb5\xc8\xa6c)\x00\x90|\x02\xba)\x84)\
+\xce/\xc1\xbe\xd3\x9b\xaf\xdbx\xfe=[\x8eoYsi\xa2Y\\3\xb2\x08\x00\xd1\xe1\x1a\
+\x1d,o\x18\x9bo\xaf\xe3\xda\x1f\x00\xcc-\x8d\xfc\xe6\xcb\xb7>~\xec\x8a\xf6\
+\xf6\xec\xec\xc8\x7f~\xe3\xdab\xb3\xe3\xd4 \x06<\x87\xec\xb8\xf2\xbeuw\x9e<\
+\xbe\xe3\xe8\xc5uQ\xf1<\xb0\xfd\x9d\xdf~\xed\x96\xb9\xa5f|d\xf1\xbe+\x92\xbd\
+\xff\xd2\xc1=3\x0b\x9dvy\xcf\x96\xe3\xd1\xbd\x98[\x1a\x01\x80\xfb\xb6&\xfb\
+\xbdal\xfe\xc2\xe2h4\n7n<\x1b\x93\xee\xbf\xfb\xa6\xb6\xaf\xbf\xfa\xf8\x8b\
+\xaf\xbc\xa9,\xc1>\xf7\xd2\x81\xbfzj\xdf\xc7\x1e|/\x004#\xcan\xe7\xc5\xd9\
+\xb9\xdf\xfb\xb3\xc7[\x04\xe0`\xad\xac4\x83\xc1\xde\xeb\xaej\xaf\xc3\n\x12\
+\x0f\x7f\xf5\xd4KO?\xffF\xeb\x88\xc4P8\xb6D\x0f-z\xf0Mfm\xa7\xdd\xbci]\xe88X\
+e\x18\x1fYZ?6\xef\x1c\x80\x87=\xeb\xa7c\xfc\xd9\xf9\xf18\xfe\x043i&$\xa5\xde\
+\xd6\r\xc5uI\x0f3\x0b\xe3\x8f\x1c\xd9\xf5C{\xbb\xed\x96k\xd6\x9f\xbbs\xf3\
+\xf1\xa7O\\y\xfd\x863\xd7\x04N\x98K\xb8eMr<\xc7G\x96~\xf2=\xcfdj\xd88\xd6MI\
+\x06#\x83\xe8\x97=\xff\xf2[V\xfe\xc7\x9fy\xa5\x05\x01\x80\xb2\x9dxzz\xe6\xcc\
+\xf4\x85\\\x8bh\x18\x1b\x1f\xdd\xb9}3\x00\xcc/,\xbeqPs\xa1\x1c\x80\x87\x97\
+\xdf<L@\xe0\x0c\'\xc0^y\x1e\x80\xbdO89:\xf7\xb3\xf7<\xf1\xe3\xb7\x7f#\x83\
+\x00\x1c\x06\x03\xd80\x96\xe6\x11g\xe7\xc6\t\x07\xab\xb7/\x83]\xde\x87\x0f_=\
+\xb30\xda^\x8f\x8f,\xb5\xc6\xe8\xfe\xedG\xe3h~\xfc\xd8\x8e\x833\x1bb~\x1dsF\
+\x88\xcdq\xe0\xd6\x8c\x8f\x01\xc0\xfc\xc2\xe2\xe1\xa9\xd3JV\x07\x0378==\x83"\
+\xf8\x91\xf0\xb9\xf9\xc5\xb9\xf9\x05\xfd\xa8\xb8\x16\x9a\xc1`\xcd\xd8(\x00\\\
+\x9a[83}\x01\xbc.\xdd7\x0f\x1e\xc3lt\x7f\xe5?t\x95r\xb6\x15\xa9\xedi+\xfb\
+\xfe\xeb_e\x06\xfe\xc9\xa9\x1d\x87f&\xcf\xcf\x8f\x01\xc0\x0f\xdf\xbc\x8fM\
+\x01\x96=\xb4\xcb\xfem\xd84>\xb7\xaeY8\x0b# B\xd5\x91=y8)\xa5\xa4\xd5\x9b\
+\xb7\xceo|\xf8\xf0\xd5\xedF3\x00\xbcg\xcb\x89mkg\xee\xbc<Y\xa5\xaf\x9d\xd8\
+\x16m\x01P\xe32\xb74\xf2\xd6\xf9\r3\xeaN\x04\xc0\xf8\xc8\xd2\xeb\xd3i]\xe1\
+\xd2\xdc<\x00\x8c\x8d6\x13k\xc6\x80\xa9\\\xd7q\xb5y\xe3d\xa1Q(d\x96\xd8Y\xfc\
+\x9a\xf1\xd1\xf1\xf1\xd1n\xc6\xe0\xc9P\xf6\x00\x9b7\xe1%;t\xae\x8f\xd4\x85\
+\x89sU\xd0d\x8eW<\xb0=\x19\xcbgNl\xfb\xdf\x9e\xb9/.\xa5\r\x1c||\xcf~9\x0f|\
+\xf1\xf4\x96\x88\x9bmk/~\xe7Uo\xff\xce\xab7\x00\xd6\xffq\xaf\xd2\xc0\xc1D\
+\xbbvT:\x8b\x12q\xe0\xc1=zdg\x04\xc1\x965\xb3?q\xfbsq\x11\xe9\xad\xf3\x1b\
+\x1e?v%.x\xe0\\\xd2\n\xe3#K\xff\xcfk{-\x9b\x05\x00\x0b\xcb\x1d\x82\x97\x96\
+\x97\x0e\x1f;\xd5^\xdf{\xc7\xf5\xed\xbc\x00\x87\xb6-w\xde|M\xe2\xb0\xeelj&\
+\xcc\xce\xcd\xbfs\xfcL\xbb\xc5|\xc7M\xbb_\x7f\xeb\xa8\x0ff2T\x01\x00\xfe\xee\
+[\xf7 6\xd0Pg]\x98\x1c\x82$\xffV\x1c\xb9\xd3ZW\xacM\xbb\x99\xcf\x9f\xda\x12\
+\'K\x00p\xef\xd6\xa37m\xa2Z\xd19\x00x\xe1\xd4\x96\xd6\xdbj\xc3\x0f\xed\xdd\
+\xf7\xf7\xae\xdf\xbfi\xcd\xd2\xc65\x8bWN\xce\xfe\xf4=\xcf\xfc\xed\xab\x93j\
+\x91\xe3`rt\xe1\xfe\xedG\xc7\x1a\x188\xd80\xbe86R\xb51\xf0\xd2\xd9\xcb\x9f9\
+\xb1-R\xf8\x8e\xab\x0e\xc7e\x8co\x9c\xdc\x8a\xd9\x06\x80\x17Oo\x99\x9a\x9d\
+\x88\xb7\x1f\xdf\xb3\x7f]\xb30\xb30\xde\xfe\x03\x80\xff\xfe\xd6\xe7\x7fx\xef\
+>\x00\x98Y\x18O\x1b\xd6\x1e\x1e\x7f\xb6\xdb\xea\xfc\xee\x0f\xdeu\xe5\x15\x9b\
+\xd1\xe1\xf4\xee\xf7\xc6k\xaf|\xf0\xfe\xdb\x8b\xdc\xe2\x93\x16\x99L\x000?\
+\xbf\xf8\xec\xbe\x03m\xc4G\xbf\xe3\xee\xb1\xb11<8Z\x04\xdc\xb9w\xf7}w\xa2u\
+\x02\xac\xf6\xb99 \xdc\xe2\xbar\xcb\xc6\xd3\xf3c\xd1\x91~`\xfb;\xcf\x9f\xdcr\
+pf\xc3D\xb3x\xdd\x86\xb3?|\xf3>u\x19\xf1\x953\x1b\xbf|\xe8\x9a8.\'G\x17~\xf2\
+=\xcf|\xf2\xba\xd7O]Z\xdb.1}\xf0\xca#\xe7\xe6\xc7\xe2\x94\xc1\x83g\xce\xff\
+\x7f}\xc3+{\xd6O\xb7\xeb\n\xbf\xb2\xef\xf6\x17O_\xce\xeb\x88l\x06e0\xb30\xfe\
+\xd8\xb1\x1dr\xf9oni\xe4\xd1#;Y\xe4\xd4\xec\xe4\x17\xdf\xb8\xf1\xc7o\xffF{{\
+\xcf\xd6\xa9_x\xdf\xdf|\xe5\xed\xdd\'f\xd7n];\xfb\x9dW\x1dn\x97\x10n\xdb|\
+\xea\xd7_\xba\xf5k\'vD\xff\xe3\xe57\x8e<\xfc\xd8\x0b\x0f=p\xc7\xce\xed\x97\
+\xff\xdcO|\xf2W\xff\xd3_\xec{\xed\xed\xf9\xc5E\x00\x18\x1f\x1b\xbd\xe5\xfa\
+\x9d\x9f\xfb\xecG\xf4)\xbb\xc5\x7f\xe64\x03x\xe7\x1d\x00\xfc\xcd\xd7_\xfe\
+\x81\x8f}`\xe7\xf6\xcbo\xb9a\xd7\xff\xf4#\xdf\xf3\x1b_|x\xea\xd4t,v\xe7\xde\
+\xdd\xff\xf3\x8f}"NJ\x01\x80n\xcfevt=\xfe\xcf~\xee\xc0\xc1\x93S;\xa2\xc2\xbf\
+i\xd3\x99\xff\xe3\xdb\x1e?ra\xb2]X5\x8a\xb8\x05\xdf\xfc\xd6\xab\xb7\\\xbf\
+\x91,\xd4\\\xb3\xfe\\,r\xd9\xf8\xa5\x9f\xbb\xf7\xa9_|\xfe\xae??\xd4\x89\xe7\
+\xf9\x93[0\x8d\xe8\xdc\x01\x00\xc0\xedy\xbb`\x9d9h\xc3\xeb\xd3\x97={JY\xbd\
+\xf9\x83\x037\xdc\xbe9\xadg\xb4+\xd9,\xcfm\x9bO~|\xcf\x9b\xcf\x9e\xda\xb6\
+\x18,\xc2\xc5\xd9K\xbf\xf6\xf9\xbf\xdc{\xddU\xedz\xed?\xff\xa9\x1f|\xfa\xf9\
+\xd7\x8f\x9e8\x0b\x00{v^q\xf7\xad{&\xd6\x8e\x1f>v\xaa\xddP\xc8\xf0l5\x84\xc7\
+\x80\x07\x80\xa3\'\xce\xfc\x9b\xdf\xfer\xbb\x12\xf0\xb1\x07\xdf{\xeb\r\xbb\
+\xfe\xfak/\xb5\xeb\x8c{\xaf\xbb\xea\xdb\xdf\xbb\xb7=\x820\xb1\xb6sz\xc48\x8f\
+\xba\xdf\x9a3\x00\x10\xc7P\xe4\xf9\xc2\x1b7\xde\xb6\xf9T\x14g\xbb\xf2\xdf^?z\
+d\xe7\x95\xeb.\xc4\xdb5#K\xd1\xcd9zq\xe2g\x9e~\xffO\xdc\xfe\x1cv*q\xb8l\xfc\
+\xd2\x9e\r\tF\xcf\x9c\xd8\xf2G\x07\xae\x8b\xca#\x86hV&\x9a\xc5\xb1AZ\x85]3\
+\xb2\xe8`\xd9\x07C\xd6\xe2\xe0\xc0\xf9MO\x1c\xdb\xc1\x88<rdg\x14!\xa1\xbc\
+\xdc\xfc\xfcs\xf7M\xcd\xae\x8b\x87Ydx\xf4\xc8\xce_z\xf1.V|\xff\xdb\xc7\xfe\
+\x97\xff\xfbw\xff\xc7\x1f\xfa\xe8\xdd\xb7^\xbbar\xedC\x0f\xdc\x81S\x1f~\xec\
+\x85\x87\x1f{\xe1\x9f\xfd\xe3O3R\xceA\xeb\xe4\x8f\x8fQp \xe9D?W\x02\xe8\xd1\
+\'\xf7\xfd\xab\xff\xf0\xa5\xff\xf6\x07\xfe\xd6\x86\xc9\xb5\xd7]\xbd\x8d-J>\
+\xbbo\xff3\xdf\xdc\xff\xa3\x9fzHm\x85\xa33\xa9T)\xd5C#;n\xba\x1f\xa7\xa5\xd2\
+\x00\x17\x16\xc7\x9e\x9e\xda\x06\xe0\xb6\xae\x9d\x1d\x1d,/\xf9\xc1\xa5\xa5\
+\xe6\x9d\x8b\x93\xbf\xfd\xda\xcd\xbf\xf9\xcam;&.\x8e\x0e\x96\x8f]\x9c<>;\xf1\
+\xf4\xf1m\xfb\xcfm\x8a~\xd0\xcc\xc2\xe8\x7f9\xb6\xeb\xc0\xb9\r\x0e`bt\x11\
+\x00<\xb8KK\xcd\x99\xf95_;\xbe\xed_\xbdp\xd7\x9f\x1d\xbcz\xc9w\x99\x97\xfd\
+\xe0\xa9\xe3\xdbf\x16\xc7\xb7M\\l\x06\xcb\x1e\\[\xd1\xf1\xd9\x89?\x7f\xfb\
+\x9a3s\xe3k\x9a\xe5k\xd6\x9f\x9b_\x1e9vq\xf2\xcc\xdc\xf8\xb3\'\xafx\xf1\xf4V\
+\xda\xcb\x0e\x00\xce\xce\x8d]\xb5\xee\xc2\xf9\x85\xb1\x96\xa5\xb7g\xd6\xff\
+\xc7Wo\xb9\xb0(<\x7f\x07\x000\xbf\xdc<}\xe2\xca\x17Nn^3\xb2\x149\\\xf2\x83\
+\xf3\x0b\xe3/\x9d\xb9\xfcW\xf6\xdd\xf1\x1f^\xbb\xed\xfc\xc2\x1aY\xf6\xe4\xe9\
+s\x8f<\xbe\xef\xc4\xe9s\xde\xc3`\xe0\x00\xdc\xf4\xf9\x8b\xaf\x1ex\xe7w\xfe\
+\xe8\xbf\xfc\xea\xe7\x1f^\xbfn\xcd\xc7?|\x1f\x00|\xf3\xb5CO~\xe3\xb5\xb6C\
+\x06\x83\xc1\xcd\xd7\xefl\x9a\x917\xde:\xfa\xd4\xf3\xaf\xcf\\\xbc$;\xdc\r\
+\x06w\xec\xdd\xdd4#\xaf\xee?\xf2WO\xed\xbb4\xb7\x10Y\xf5\xe0_~\xe3\xf0\x0b\
+\xaf\x1e\xf4\x00\x97m\\\xe7\x9c\xf3\x00s\xf3\x0bS\xa7\xa6\xbf\xf8\xa5\xc7\
+\xff\xc5o\xfe\xc9\xa5\xb9\xf9[n\xd8uf\xfa\xc2c_\x7f\xf9\xd0;\xa7\xb8\x13 \
+\x10\x90\x9eU\tW\xee\xee\x8f\xfdS\x0b\x04\xb1\xe4\xb6\xb53\xbb\'\xcf\x01\xc0\
+\x85\xc5\xd1\x833\x1bZ\x07\n\x9f\xf6\\\xf2\xae\xddp\x93Fh\xc7\xc4\xc5-kf\xd7\
+5\x0b\x17\x16GO^Z\xcb\xf6\x1bqX7\x06\xd7o8\xd3\xee,\xb7\x15\xb5\'\x0f\x00`t\
+\xb0\x14\xd7\xe0\x96\xbc\x9b_r\xea\xa9\xf0\xc8\xd2\xe8`iayD9\x87\xa8u\xcd\
+\xb653-\x87\x00p\xe2\xd2\xc4[\x17.\xf3\xd6.r\xbb\xe3\xef\x9c\x03\xe7\x06n\
+\xe3\xe4\xc4\xa6\r\xeb\xe6\xe6\x17N\x9d9\xbf\xb0\xb8\x04\x00\xdf\xf9\xfe\xdb\
+\xfe\xaf\x9f\xfc\xfb\x00\xf0[\xbf\xff\xe8\xbf\xfd\xdd\xafD&\xc7F\x9b\x91f\
+\xb0\xbc\xb4|i~A\xbe\xec\xa1\xa5<6\xda\x8c\x8e6\x8b\x0bKs1O\x9a\xd2wW\xdb\
+\xb7n\xda\xbeu\xd3\xe6\x8d\x93\xa7\xa7g\x0e\xbc}\xfc\xdc\x85K\x0e`\xa4\x19\
+\x8c\x8d6\xcd`07\xbfH\x96#y=Q\xfa\xc4!\x00\xf3\t$z\xa0ejv\x92\xf9\xd8\x00\
+\x10\xbb\x98<\x1b$\x9c\x91\xa3\x17\xd6v{\x01\xa5Z.\xcc\xc3\xf3\'/\xa3\x0f\
+\xa1v\x17\x0b\xcb#\x0b\xcb)\xd6z.`n\xa9{\xd2~n\xd9\xd8\x1d\xd5\x0e\xeaL]\x9a\
+\x9c\xba4I2I\x03\x8a\x96\xff\xe2\t\xc4\xb3\xe7/\x9c=\x7f\x013\x1c\x0fs\xb6\
+\xdb\xbbqSfn~\x01\xe6\x01\x0c\xf4\xc7<s\xf1d\nW\xe1\xddl\xf3\xd8\x89\xb3\xc7\
+N\x9c\x85x\xd8\xd8\x01\x80[ZZ\x9e]\x9aG1V\xb3\xcd0\xc8\xe51\x96\xa8V\x18<\
+\x83\xa8\xe7\xa9#\x03\x88K\xef\xe1\xbd\x17<(\x8f|\xd4/J\xd64J\x9bV]\xb5\xed\
+\xf2O\xfe\x9d\x07&\xd6\x8e\xab\x15m\xdf\xba\xe9\xbb?x\x17\x00\x1c?5\xfd\xe6!\
+2U\xa9\x9a\x16\x16X\xd6\x8b{\x90#\x9e\xa6VT\x9a;OP\x19r\x0f\xf5\x99\x8f*\xda\
+\xb7\x0e\x96:m\xec\x94\xd2rkq\xf5V\xa3\x95\x80\x88ON\xac\xfd\x91O=\xf4\xb1\
+\x07\xdf{\xf7\xad{~\xed\xf3\x7fy\xe8\xe8\xc9\xe5\xe5e\x00\xf0\xe0\x07np\xf5\
+\x95[~\xea\xbf\xfbD\xbb\xbf\xf0\xd5\xc7_<\xf4\xce\xc9\xd4(mj\x9e\xad\xb3b\
+\xdf$\xd2q\x1e\xc0\xd1\x0b\\\x05ZW\xb2;\xaa\xf7d\xa66\x0c\xfd6\x1a\x0f\xc1e\
+\xf4A\xed\x87\xa4x\xa4\xb3\xf4\x04x\x97\xb4\x1aj,\xd6\xf5\xfe\xbbnl7\x87\xda\
+\xc3#\x7f\xfd\xf5W^y\xf3\xc8\xe9\xb3\xe77oZ\xbfk\xc7\xe5\x0f\xde\x7f{\xbbH\
+\xf0\xd2\xebo\x7f\xfeO\xfef\xd9\xa7\xe9\x8c\xf3\x1a\x97tuW\xae-b((\x8f\xf8\
+\xa1\xe5\xc8\xb8\x9c\x1c\x8e\xcac\xc2\xa6\xc6\xed\xea\xf0\xce\x83ww\x7f\xcf?\
+%l\xf5\xef8\xe5\x056\xd9\x17\x18ps\x80\xd2\x13\x1f\xdd\x1f\x0e\x02\x87\xdf\
+\x0f\x85\xec4f\x83\xd3SCV\x7f(\x0f\x99\x83\xdb\xb4a\xdd\'?r\xff\xf7>tofE\xe8\
+\xd9}\xfb\xff\xcf_\xf9\xc3\xb7\x8e\x1c\xb72\xe8\xc7\xfd,\xaf<\xbb\x19HI\xc5W\
+:D\x18\xe0\xbef$\x89\x7f\x08i\x8a\x08f\xbf\xb4\x9d+\xfb\xa5\xf2\xcdX`A"\'\
+\x06\xa7\x80\x80\xf9\x83\xd1Q\x1f\xe2\x1d\x00F\t\x17\x02\xe6\xa3\xdb\xf1spi~\
+\xe1\x99o\xee\x7f\xe6\x9b\xfb\xcf\xcd\\\\\xf6~dd0:\xda\x8c\x8c\x0c\xe6\x17\
+\x16O\x9d=\xff\xdcKo}\xe1O\x1f\xfb\xe5\xdf\xfd\xca\xf1S\xd3\x00\xfa\xb0Kms"I\
+\xae\xef\x82\x16\xc9-ar\x0f\x1dK\xe5C\xcd\xb1\x04\x1c\x1a\x9e\x07\xd3\xa9{[\
+\x00\xa7\xdd\xfa\x07^Kb\x81hlO0k\xd80\x86\x00\x96T\xc6\xa5\r\x18&{V$*\xe7\
+\xd7\xf6\xbf\xf3\xda\xfew6\xae\x9f\xd8\xb4a\xdde\x1b\xbb\xed\xb43\xd3\x17\
+\x8e\x9f\x9aN\x0f~[N\x9aS\xaek\x1f\xd7\xa9\x81:1\x04\x19\xb7\xab\xbdO1\x15/\
+\xa9\x08\xb7\xf2\xbdQEOp\x15\x9fV\xab\t\x1c\x07\xcc-\xa8@\x80\x9ch\xe1\x18\
+\x17\x0c\xf1\xf4\xf9\x8b\xd3\xe7/f\xd4~-\xc3\xe0\x80b\x0e?\xdd%#Y`\x9eA\xfa\
+\xb5ZmH\xa3\x87cX\x1eg\xf4Y%\xdb\xf6\xbf\xcb.=\xab\xa8\xa8\x1d\xd8+\x92\xd0\
+\xd0\x8f\xb7\xcau\xbeR\xa8\xa8\xd7 Wq\xd2"e\xed\xb6\x9a2\x8f\xac\xf3\xf9B{I\
+\xdc\x06m5\xbb\xa5_\xf3J\x8e\x1c\x9f}\x0b\xba\xf0\xd8>\x19\x8b+|\x83\x11&\
+\x85j\xe2Mc\xfd\xce\xf4\x01?\x0cQ\xd9\xb2\x0c\xfe\xccG\xcby|M7\xc6-\xc7L\x0e\
+\x85ZZ=\xae{]\x8dU7\xbd/\x92\xc8L\x9c\xf3\xf1\x069\xed)\xc1\x15b\x97\xb9`\
+\x12\x1c\xdd"`e\x7f\xe1!\xd8\x1f\xcdz-^\\;\x92\xb3\xea$\x8b\xc7\x180\xcc\x81\
+\xda\x89x\xce\x9a\xdb\x0b\x97\x13\x1e\x0f\x1ce\xd2M\xe9\xb2jS \x10\x93\xc3R\
+\xa8\xc9\xa9\xe4Q}\xef\xca\x1ai\xb7\x90[\xe9aT\x84\x1e\xaa\x94\x0e.\xea(\xe8\
+\xddNv\x11\xcc\xa7\x92\xfb27\xf4\xc8\xc3\xac\x89P\x14\xe50\xaf\x92\xc8\x86\
+\xec\n\xbc\xac\x1e\xf2\x99\x87~P?W\xcaQY\xe2\x14\xdf1\x9f?\xae\xd2\xfe\xe0\
+\xe2\t\x04\xd6\xaco\x85\x07\xe5"\x87:KF\xaeB\xa5h\x8a%q`\x8e\x03\x933*x\xcd\
+\x10\xe8<\x90;\xbd\xae\xbe8`f\x1b\xb3\x84\x89\x1a\x86\xc2~1\x16]\x1dJ\'=\xbd\
+w\xce5@\xc5/9\xe6\x07&\xb3o(\xc4\xef0\x10\xa4x\x9bTn\xd3\xdb\x02\x92Tz\xd8\
+\x02\xb2\ti\xcf\xb2\xa2\xba^%\x88\x9b\xdc\x80\x17u\x05\xa5-\xf9IA\xed96OA\
+\x8f\x9dc\xe7\xa0\xc3\x01\'@Q@\x85\xde(\x08`*A\xbe\xa2\x98\xbd\xafv\xd8\xc0M\
+\xb0\x9e\xa9\xea\x88~ \xd1\x0f(\xfd\x94?*\xdf\xe1WE\xb1\x9a\xb9T\x97\xb1C\
+\xd8\xdb\xdd\xcb\xd9\x82\x80\x00\xb9\x02\x94\x1eM\xef\xaa\xc9\xbf\xa4vE\xb6_\
+\x1b\xf6\xc6\x1d\xdf(6\xde\x158\\\xb0\x14\x00\xda\xa6\x17\xd5y\x9e\xd3\x83\
+\xf9\xd4wol10yE\x90\xea<P\x99\xb2\x96\x97r\x94\x1c\xc8\'\xc8\x10Pe\x9f}Ye\
+\xc1\x10\xaeD\x8a\xf9\xae7\x0b\x15&5\\=\xb0\x8a4\x1c\xe0[\xbe\xd2\xd7\xb7\
+\x81r%\xa7\\\xa2\xdf\x80\xcc\xe4\xa7\xcf"\xaa\xd6=\xd3\x9e\xe1\x14\xc3\xd03\
+\xb1\xea\x8c\xea\xe2\xabR\x97t\xb8\xf8u\x8c\xa2o\x7f\xc1\x0b\x00\x92r\x9d\
+\x1fg\xf1\x1c\xefSY\xcd\x8d\xa8\xa5\x83\x93\x8c\xc7\x8e\x1bU\x90^\x00C{\x19}\
+\xae\x14\tb\xcd\xa40j\xa9K\x98\xc9\xa9\x86!feF-\x8eC d\x96\x0b\x00\xa6\'\x9f\
+\x89$\xa5MC^\x0e\x95 \xb3G\xac\xf6nc\xa1\x9a\x9c8\xe4#\xd1\xc0\xbdI9;\xa0\
+\xd3\xbf\xe2;\xd0S\xb9\x92\x07J6\xb7h\xa5\xe6,\xb7\xe4\x9f\x84\xe24\x05yyV\
+\xebz\x87\x1a1\xb3<\x8e\xc6\xdbU\x9b\x82\xc7\x90\xd5\x9f@2\xba\x92\xb2\x11:=\
+\xb7\xaa\xa1\xbc\xe5\xb0\xbb\xc8,\xb08yU\x15\x8a+$\xfd\x95J\x98M\x19^\xa4Zc\
+\xa6\x96U\xdbS\x95\xb3\x12a\x9e\x8c\xafO\xa1\xcc\xf1B\xbe\xbd\xcc\xa3\x05\
+\x05^\x07\x8a\xf4\xfci\xc7r\x17\xe7\x11`\xbc.ZY\x1e(\xee\x0e\xa8\xe2\xc9\xb0\
+\xa7\xbaxq\xf5\xad\x8bc\xde\xa2\xd0\x07C\xae7\xa0e\x15\xd0PR\x98%\x1a\x9dV\
+\xdet\xa0\x05\x9b8\x01\xcc\xed\xffzq%&Y\xb2;\x92)\xc1-\xa9\xec\xab\xd2\xc2>\
+\xcd+\x1c{^T\xc4\x07\xb1\xa6$\xd5\xa8\xa7\xa1\xa3cN\xf74\xbd~[\\=d\x8b?R\x1f\
+\x13n{MA\xb3\xa1]1\xa4U\x06-(\xbb\x05U\xed\xe3yP\x89\x86\xdaa\xa1:\x14\xaaD\
+\xd1e\xee\xbcSO\xfd\xac\'\x85%\x17\xb3\xc7\xf3\xfe/\x08\xf1\x18\xcaC\xab\xb9\
+ .\xce\xd5\x10D\xb4\x167\xfa\xd0\xa7\xb6\xde\xf6.\\J\xc6r2\\\x01\x9b3\xa1\
+\xf3;\xe7q\xa89\xb7\xacZ\x983\x1c2*79\x13\x96kfT+l4t\xe7><\xc5\x01r\xec+\x0f\
++\xe4\xfd\x1buM\xb0\xc8v\x13K(\x1d%T\x81\xe2\x1bdh\xe7\x83a\x18\x90\xebPE\
+\xd6\xda=\xc2Qj5\xc5\x90\x96\xd9\xf5~\xe7\x92vb8P\xf5\xa1M2c&Oi\th\x16]\xf5\
+\x94$\x11Pjl\xe3\xa9<\x19+z5\xf4Z:O}\x83\x94\xdcp\x1b\xc4\x16\x02\xc8\xcc\
+\x9e\x9a\xb4z\xe2l\x07\x88T\x19\x84\xef\xc4\xb0\x81\x90\xdaA\x00M2-\x06Tg\
+\xb3r7r\xb8\xd34\rs\x05".Qw%VT%\xe1!\xedc\x13\\w\xb6\x02\xbdF\\\x9b\xfe\xad\
+\x16\x02(\t|\xe9\x80\xe1\xc0\x0e\xba+\xe0\xb5\xb6$R\xa8\x05\x08\x05iL\x11C\
+\x80\xdc\xcc\x98\xb7\xbf\xe0\x98\xb0\x87~\x1f\x7fK\x87?\x86\xe6Q\x0f@l\xbe\
+\xce\xa5\x8f\x05\xd8\x9f\x94\x8e\xfa\xaer\x01\xa0/\x02\xf0c\x11\xcc\'\x95k\
+\xc6\xb5C?x\xca\xfa\xd9=\xcf@\x86S\x1c\x89qX\xf6\xf1O\xba\xe9^G.\xf2\xabL\r}\
+D%\xffE/\x00\x18\xf8\xf0\xa4\xa3\x8fk\x04\xf1_l\x1a\xe0\xeb\xee_\xb8\xd2\xfe\
+\xc4\xf2\x92g\xe4\xfc\xb7\xffVe\tE\xaaA\xb2\x0f\x14\xfe\xd5L[<zx4^p\x9a\xd0%\
+\x8bJ\x91\xa2p\xe1"V\x8e\xe2\x19<+\x83\xe50y\xef\xadg\x84j\x06\xd5 \x88\x8e8\
+A\x816\xfe\xe3c\xc3\xbd\x82\x0b\x1f\x8b\xd6\x88\xd4\xda\x82\x03)\xce\x15Z\
+\x06\xc5I\xa8}\x8d`bI\xda\xee\x04\x81\n:N\xb9\xc2\xfc\xf0\xcc\xd6?\xc4? \xe9\
+\xb2\xe7A\x1c\re\xf6\xd2\xdb\xcb\x90`\xf1P\xc0\x89)\x8bWD\x1d4(N\xaf\x02Eq\
+\x93\xb72\xe8_p[\x05-#h\xa2@\xe4Kf\x05\xae\x02\x1f\xd8\x99\xa8\xd0R\x14\n\
+\x10\xe4M8\x0c*!\x1f|\x08\xed\xed@\xaf:\xaa\x07\xd5\x14\xc4[Q\x08\xc7\xf7\
+\x15BR\xc2+8\xba\xa2\xac\xb8\xad\x16\x0e0\xcd\xce\xd7\x14yT\xd1\x0b-\xc2\x06\
+\x11>\xca\xc0\xfe)l$\x83b\xea3&\xe3b\x08\x9a\x00sO\x0c\x82\xe7\xb2\xa7\xed\
+\xd1\xa3\xaa\xfb\x9d\xa9\x1c+S/A2\x1c\x10(;\xd2\x895\xd4l7-VbpA\x13\xc5| \\\
+\x13\xfdQ[;\xf3r,\xb5\xaf?:@N\x8dx\xef\xfd@\xb3v\x8cq\xaef\xb9\xdf\xa0z\x11\
+\xd9 -N|%\tW\x06C\x8dc\xa6\x03\x14\xa3\x0e\x00}q\xa0C\x07\xf1i\x8f\x13\xa1\
+\x9d\xec*\x84\x1f\xc0\xb4\x02W\x00\x08\x07E\'\xc0R\x0f\xd4\'`\n=\xc4K\xb7Q\
+\x81\x82r\xa93\x92\x8b\xf4\x1e\xfc\xf0FAi\xa4\xc0\xb4\x1a,\xd5J"\xa9\x10\xbd\
+\x88\x11\xdc(\x08\xd0\xf9\xa0\xf0"\xcc\x084X\xc8H\xc4\x0c4\xf4:T\xe2\x05\xa3\
+\xddc\x8f\x16\tR.8\xaa\xca6ckL\x1d(\x87\xd6%\xbf\xccQZ\xe1\x1c\x811\x19X\\\
+\x11\x1d\xf4z\x18o3\xc8T\xa3&\xff\xe8d\x90w\x9ap\x7f\xd9)\x85\xdal\xe1\xd3g\
+\xdc\xbf\x96O\x91[\xa1\xe1\xf26\x1bb\xeaN)p\xd9)i[\xd9\x0b\x1cd\x8e\xaa\xfa\
+\xec4\x97\x95c\x98\xcbnf\xae\x04\x01>\x9c\xed\xf7N\xab\x02\r\x06f\x93\x8c\
+\xd0\xcd%<\x00y\'u\x87\r1\xa0\x11h\x88\xaf\x03\x00h\xa1I\x99\xc8\xd8\xe70\
+\x1aJ\'*9\xc1\xa6:\xb8\x91uP\x8ceH\x1b5\x99\x00\x00 \x00IDAT\xec\xa4\xd0/tQ6\
++\x06\xa6-\xbch\xb6R$0\xba\xda\xcf\xa6uA#\xc9\xe1\xee\xc5E\x96\x90\xe3\xb1\
+\xe8\x11s\x9c\xe6q6{F\xcd4D\xb8!\xef\xdf\xd0\xa0\xd0\x10\xa4:\x9d\xfb\xf4\
+\xf5w\xb6\x8cJ\xc4d\x8d<O\xc6\x85\xf3`\xcb\x88=\xd5B\x94\xc1pC\x17\x91\xca,\
+\xc7\x82\xecVOQ\xab\xaa\x99\xd6(\xf8\xa4\xe0d\x83$\xe7<\x1b\x15\xb6\x13\xb1D\
+CH\xe5\xc7\x0c\x01\x9a\xe2e\xe0\xc2\xde\xe6A\xcd\x81\xde\xd1\xb83<\x93\xbc\
+\xd6,\x12\x82\xd1\x8c\\\xb7\xa2m\x1dZ\x80\x8cF\xf0\xe9c\xb9`\xcf\x820\x9b\
+\xdd/{\xe9\x84\x9e\xd7^\xae\xee\x85\xb6V\xdfu\xaf\x890a\xc0y\xb4\x80 \x1d\
+\xafx\xd2\x9dZ\x01\xbe\x1f\xe6E\x8b\xa8\x81\x88\xe68]#\x1c\xc4\'\x90\xf4\x90\
+\x04Op@\xb3x\x02@^\x06\xb0\xfa\xe8,D\x1c>\x80\xa0\xaa\xd9\xd7\x0e\x07\x0e\
+\xf4\xf7\x11\xa9\xc8 \xe6 \xe3\xb3\x15\x03k7O$\xfa\xd1\t\x17\xa1\xe4\x03\x90\
+,\x9a\x02\x082\xf6\\\x17Y\x9e` \x81\xdc\x02\xe1+\x00\x1a\x00\xb1\x97\x82O@\
+\x9c\x81T\xa9&S\xa1\x1a\x81\x8e<\xa6\xfe\x13\xe1\x16\x10>\xee\xa0\xf1\xde\
+\x89\x14\xb1Q\x88\xfa@4\xb2WP6\xacC\'Zs]\xca\x93\x02&\x84\x8f\x88\xd6\x12\
+\xe8\x92\x06L\xba_Q\x0fq\xef\x11\x84z\x08\x1e\xb6d\xd5\xdc5\x15V\x9e\x01\xa8\
+\xd1|\xc1$>\x00\xaa\x00\xb8L\x8d\x16s\x0f9R\n\xf8t\xda\x93ul$E(\xd0\x87\x1c\
+\xb0f\xab\x0c\xc3\xe1&q"\x1f\xf9N@O\xceA\x8b\x03E\xa5\x9b\\\xb1\x1b\x14A\xf7\
+\x9d\x01;\x17.<\x03E\x7fT\xf8r\xabA[\x15S\x9b,\xf8\xdb_,/\x9f\x08R\xb5\xa0N\
+\x1e\xba\x0civ\x90(z\'\xb0DW#:\xb4b\x8f\x0b\xafw:hmDa\xf3\xb4\xbf\xf4U\x90%a\
+g\x8bv8\xc0\xf5\x1a\xe2\x11\x1c*\x8f\xdc\xfa\x84yd\x16\x84\xfdO8\x08\xbcj\
+\x95\x08C\x83\xe2\x1bbj\xf5\xc1\xcd4\x0fz!\x06@\xfbPj\x9c9+u\xcbYSZ|\x8a\xe9\
+\\\xed\x00\x12\x86\xeey\xcb\x05%\xa9\x89m\x04\x98\x90\x15\xf9\x18?\x90\xc7\
+\x87\x0b\xfa+\x15D\x12\x93\xfc8\xf4?\xb9\xec4\x81\xee\x02\x96\xc0HkP\xec v\
+\x17@\xcc\x0e:\x00R\xf3\x84\x18n#:\xa3\x0e-\xa3\x98\x1c\x0b\xfa3*\xc8\xb08$}\
+\xc0~G\xc8]\x9a\x81\x95\x16\x94\x10\xc7\xf5\x91y\xf3\x91[\x86\xf2Qy\xb7\x19\
+\x9c\xa3\xf6=\x8d\xf8\xae\xbd\xcc#\xa4\xeeBZ\x0c\x8c:4\xaa\x04O\x95=U\x1a6\
+\xeb\xa16\xe2.\x0c<@\xfc\xd7\xfdz\xf0\xd0\xf9K\x1eR2\xca\x06\xc8\xa3\no8W\
+\x1c\x0bT&\x11\t[G\xe8\x16@\xfc\x89\xec\x84\xb6e\xd6\x96\xd8n\x13\xdf|\x92\
+\xac\xc9\xaeA\xbdS\xeb@di\x1a\x89\x18\x01a#\xc0E\x05\xe0\xc2/\x82\x86\x8b\
+\x11)\xc1\x01\xca\xe3\x80\xe4\xce\x07#3\xd9@J}\xef\xc9=\xce\x13\xc5\xd8F\xc5\
+n\xf78\x07\xb1\x01\x1c\x05D\xfc\x12\x00\x95\xca\xaer\xf4G\x18\x19m)TV\x81\
+\x87\x92\xda\xe8r9\x8e\x00l\x03\\\xf8_\x12M\x82g\xba\x83\xf0W\x8f\x03P\x1a\
+\xd5\x04Nc\x97\xc5\x13\xb1\xe1\xde \xd5\xf9d\xdd\xb7\xab=uN4\x1b\xc1\x96\x19\
+\x88\xd5K~\x90\x13\x9e8\xe45\xb0\xce\x9bwhb\xd9\xf2\x08\xd1\x86!\x9a\xba\xa5\
+W\xab\x92>G]p\xf4\xca\xa9\x92\xcaSV\xdc\x00\xdc\xe7\xcce\xe8L\x83\xe5\x0c\
+\xca \xdfO\xc0\x07\xa3z\xfc\xb5\x15g\xf0\xcd<\x92],\xc6P@\xcd}p\xa3\x82\xdc\
+\x83\x97A\xf8\xe7\xbd\x9e<\x83\xfc\xaeR`\x8e\x96U\xec\xa5\xde5\x8a\x13j\xd6f\
+\x05\'.\xa8\x02\xc8\x0cgd\x02Y\xa6(p4=\xe4\x13\x83\x04\tVE\xee@o\x03H\x11$&"\
+O\x8a\xd7\x8fG*\x12?\x9f\xf9{\xde\xe1\t$Q\x19\xe0u#\xd7M\x1c\t\xaa\x80\xd44\
+\xdcH\xc4L\x14\x8f\xeb\xe7k\xb0\x94\x01\x8ft<:"\xc0\xcc\x84a\xa3J\xcb\xa5s\
+\xeaAC{\xb5\x80\xd4\xc1\xa9\xbc\x88\xf5\x00\x00\x83\xe0tI\x0b\xd9\xd9\xff\
+\xf6\xa0\x06ZWc\x96;)\xdd\xe0\xf3u\xff\x92k\x89\x08zTY\xf2\x0e\x02\x1d/o\xb5\
+\xf6\x90\x05\x83\xcaq\xaay\x00U\x07\xf14E\t`\xc1%\xd8\xfe8\xdc\x1d\xcd\xe9\
+\xe8\xff\xc8%$\xd6\x88k\n\x97.\xc2\x7f@\xfd\xc6\xbe\x01\xf7\xdb \xf9x\xf6\
+\xbf\x04\x08J!\xdez\x0e\x80(\xc8\xe4\x93\xf9\xa8r\xa2oH\xe4\x1d/4\x1c(\x8d\
+\xf0\x04\nX\x96\x82{\x89\xed\xca~Rr:vG$\x9cw\xfd4:\xc468\xe3\x9a\xde\n\x1c\
+\xa8,\xd6\xe0"\xf4r0\x07)V\x81zZ\x1bLJ\x99\xac\x16(\n\xa93Z\xd2\x9ap>\xbcsq\
+\xf5\xa8[\x7f\xc7v\xc1r\x0b\xd0}\xc1R\xa8\xb5GM\nb\xc3\xa9\x14\xac\xba\xb0^\
+\xc7\xc3Z\xd8\x86\x1a\x01E\xe5\xcf\xab\x08\x16?\xac\xba\xc6E\xf8\xe0\x1ct\
+\x1e0\x80\xe9\x06\x88\xc8\xc6@\x00\xbdN\x8bH\x1e\xa2\'b\x0b\x18\x89\x1f\xcd5\
+\x04\xba\x02~|\x87\x83xb\x0b\xef[\xb7\xff\xa3\xbdG\xadY\x88\x8c\x1d\xc8\xbb\
+\xed\x87;uR,$\x05\x9e\x8f\xcf\x91\x0c\xae`\x1b\x02j\xc9|\xc6#\x0f!.\xc4\x07\
+\x14\x10\xc7\x81\x06&\xb68E\xcc\x87Vq8$\x1dL\t+\xaaTK\x10-\xf0\x99\x82\\#\
+\x8e8\xc0\xdb\xb3\xe4\x08\x03Jj\xc7\x82\xd6y\x96\x81\x97_\xc6\x91\x88\xa9\
+\x9c+\xa2\xf5\x1an\x0bRm*\x13\xc4@\xe4r\xea\x82Cv"\xcd\x0c\x85F \x8c\xd2)d&\
+\xa8\xbb\x88\x00\ty4\x8eS\x8d\x1e<2\x01\xb1}\xde\xfb\xae\xe1\xcc\xcc\xf8\xf8\
+\x8bp`\rs6\x01\xf1\xc0z_\xec5\xb3\xe0\x80\x7f\xb6\x11cUZ\x04\xc9\x08\xe7\xcb\
+4\xc6j\xed\xc1E\xb0\xb2W\x08)C;M\xaat\x81#ub\xd7\xc2w\x11U_\x98A\x81\x883\
+\xf9\x8a\xc1\x04t\x99\\\xdc\x05f\x87\xa6<\xfe\xa5\xba?%"\x06\xd0\xfc\x13\xa9\
+\x07\xb6\xbb\xa8\x9eRB[\xcff\xd0p\x80\xb9\xc5C\x1f\xa5T\x99ua\x06\xb04\xc8\
+\xd3\xf9\x9c\xa5\x1a\xda]^\x17\x8f\xb8Q/\xa2\xb4\xcd\x9cH9\xe6\x18\xea\xb9\
+\xc3\x08\xe2/\xb1\xf5\x80\xdb\x12\x1f\xb3\xee\xd0\xe0}\xba&\x84\xd2\x1e\x1b\
+\xb52\xedj\x12v\xf5|\xd4\x1b$Sw\x9e\xcb\xd8eF\xfcE{\xc55?\xd5%\t\x07@\xc7\
+\xec\x10\x08\xe0i\t\n\xaa\xed\x901\x19\x81%\xbf0\x85\x14AV\x8e\x0c\xfd\xae\
+\x87\xb0N@g\\z\xb0::-\x0c\xc4\x03c>L\xaf\xd0J\x03\xe5\x1b\xcd\xbf\xd0J\x03\
+\xaa&\xcd/Q6\x9f.\xbc\xc5\x91\xc9$\x8f\xc3\x9eo\x15\x99\x1e\xdbK\nx\x88c\xac\
+\xe8\x1c\x9dJ!)i\xab8oD\xda\'\xf4\xb4\xd1\xc0@{\xa0%\xae $$\xf8 \xc6.A.3\x90\
+r\xf8\'Zpm\xc5\x08]\xf4\xc1\x01(P\xc8\xe1\xc0\xf1\x8b\x1eK2\x9dtR~\x8f\xfe\'\
+\xe9\x12\x0fe\xd2*20\n\x02\xaf\xb6ramQ_k\x9b5!\xe2Yh\x99J\xb4=>\x87\xcb=\x00\
+3\x04;\xc0<8\xa4\xc8\x0b\x04\n!\xcd\xab\xb0\tX\x01M\x07D\xa2\xf4\xc7\xe3N`95\
+J\x16\x1eY\xb27\n85\x89L\xe6\xe8\xe9\xa4Anph\xc1\xd3 S9\x152\x8e\x91\x82\x0f\
+\xc7\x108\x05\x92\x8d,\x1a\xcaa\xab*\x03\xb9|i\xb6\x05\x9d\x89\x88,w\xb6\xcb\
+(\xa5$d\xa49\\\xe8\xb5\x0c\xcc\xec\x0e\x03\x8c\xb5\xb6H\xf5\x1c1\x07+y1\x00\
+\x0fB\xd3Z8P\x8a\x86l(\xbfW \xb0J\xec1\xab\x05F?\x90l10\xf7\x7fu\x025\xee\
+\xd4\x0005\x80n\xb8\xed\x8f\xa2\xb7p\x10o\x13\x08V\x13\x016\xbb]\x8c\xa2\xa4\
+2\x14*\xea\xf2\xfc\xa2\x9c;\xeb*\x16&\x96m\x08\x08\xd0!`\x88\xa96\x84!.\xbcK\
+\xaf\x93\xe6\xd1\x88\x06\xc5AroC|\xb3\xea\xb2G\xdc\xf1\xa9w\xa5\xcdM\x8e\x80\
+\xb1~\x84\x86H\x99^n\x81\xd8\x98C\xa2\xfa\xf5\x87N\x8b!\xfa0-\x97\xc5 \x9e\
+\xce\xed&}\x89\x1c&\x8d\xb2DWZ\xd1\x03\x1e\xff\xe6\xc2*\xcf\x0e\xec\xa7\x0c\
+\xcd\x08:\x89\xe6\x8e\xc2\xea#\xb4\xef\xdeqO\xea\xe1\x8ff;b\xf5\xcc\x913Q\
+\xc2\x12\x92o\xe4\xa3\xeb\x93\xe23\xce\nQ\x06)*\x84\xd5\x04\x81\xbd4\xeaM\
+\xfd*K(B\xa2\xa8\x10\x94\xd0\xf9=q|??\n\xe3\xcb\x81\x14.\xb8\xa3P\x0ex0\xf6W\
+\xff\x98#\x83\xb6\x8c\xf3j\x87 r\xf1O\xb8\xd7\x88\xaf\xf6:\x01\x0b\xfa\xb0s\
+\x92\x19f\xe5\xacV\xd5t\xab\x0e\x08\x86\x14\xab\xb3E}R\xd1\xcaL\x9a9NIQ-\xd7\
+\x9b\x15nD\t?(6\r\xaeb\xc7\xf0>\xc7}\xac\x7f\xf9\xa4W(l\xe0"\x0fZ\xe4s8\x9fO\
+y*\x9d\x87\x0cK\xc6\xbd\\\x12F\x95[!\xeed\x07\xe6\xe4bG=\xc3\xaa\xd6a\x87\
+\x0eLf\xa4j\x1aV\xe3\x102\xef\xa6&p\x0e\x01$\xdfG\xf9f8-W\x95\xff\xde\xb1\
+\xc1\xc9\xf1\xd5\xdf\xe2\x99\x14s\xbc%%\x10\x07}f\xde[\x19\x9cq]\xc8\x9a\xc9\
+\x91\xcf\xb6\n \xc8\xcc/L/\x11[N\x90\xd7\xc6\xe2\x87M\xc8\x0c\x8e\x0b;#\xef\
+\x92J\xab\x1du\xf9\x81\\i\x17\x12/\x99\xcc\xc3\xadR\x08\xca\xef\xb2O\x80\x83\
+*1]\x8cF7Z\x98\xaa\x1beQ\xfc\xea\xfb\xbdz\x9d5\xb2<\x7fq\xa1\xf8\xbd\nc\xd8.\
+!\xdd"\x0f\xa6\xa9jU\x19\xea\xb6\x93\xa2\x86w\x13\x04^\x19\xe86o\xb2\xbf\xf2\
+ND\xa4\x9b!\xc9=\x00&\xfb\xe2;\x80\xd9\xe8\xc7\x1b\x9b\x95K\x98\xab\xbb\x94h\
+\x11\xd6U>B\x94gyiX\x1d\x10\x0c\xbb\xe2\xd4{\x1d\x86\xf7\x7f\x14\x8f\xdc#\
+\xd0\x10\xd0\xaf\x1a|\x1e\xc9\x13((8\x88\xe5<\xb9h\xab/\xd4\xd6\xbb\xf3\x107\
+\xae\xfbWa\xfaIx\xb7\xd6\td\x90\xad#\x1a\x8e\xae\x90\x8b\\\xa2\xb4\xb12\x82\
+\xf6\x81$!\x87\x11\xd0\xf7\xa5\xbf\xf8\xa9\x8bN\xf6\x85\xd5\x0e\xe6\x1e\x1a\
+\x1d\xd0e\xf0\xb4X\xcc\xd0\xb2\xaa4\xc7\xe3?\x1c\xf4\n\x0e<\xcb(\x07\x9d\x03\
+`S\xc4\xe5\xe5e\x96g0\xe8\x8d\x12z\x82[}\xa6\\;\xc6Eid@\xedX6\xdf\xc5\xfa\
+\xecC\xec9&AC\x1db!n7\x9b\xeb\xd8\x99\x8a,/7\x9a\xc7^\x0b\x08\xa4,\x0e\xae%%\
+\x0e\x19\x1b\x98E\xaf>p\xe0\x06\xcb(\xf4cEe/3\xc8\x92cF~\xb2\xe4\x8a\x89r\
+\xe6\x9cXa\x8c\xf5\xb6Y4;\xdf^b\x88QG\xad\xa6\x0e\xb0)\xc3j\xc3{\x12m\xf2a\
+\x86N\x11p\x98\nz1_\x0c\xab\xbfw`\x98\xde4/1\x98\xd5=\x04da\xb1\x1d\x8e\xe7\
+\x8e\x92-\x88\x89t\x13\xbb\xa2\x07k&c\x98\xa0E\x92\x0e>sz,\xe5\x82<\x1b\xb5B\
+\x96M\x16\x0c\x01\xb5\xa4n\xe1\xa3\xbd*\x80\xa0R=\x98>\x97\\+r\xf46rE\x8b\
+\xf1\x08\xca\xb8\xa7(\x00\x94Hp\xd0\x9e|D\xe7K\xca\x0cg\x83r\x88&\x0f2VINE\
+\xc7*\xa87\xe0\xe9\x1f\x95,\xbb\x97\xfbD\xf1^\x8e$\x0fP\xa3\tV\xc5L\x80&}\
+\xe2q\xeb\xfd\xa9E\x12\x0b\xc0\xfb\'\xe5\xf2\xf2\xaaB1\xe8b3B\xe8JJV\x81\xb9\
+^\xb2l\x18\x93I\xe1!L\n\xec t\xad6Z\xda\xd0\x81\xa0\xaf\xdbLj\xeb1\xaa\xa2\
+\xaf\xc5\xe4O\xf94\x0b\xf3\x8aT\x08\xe0\xc9\x02V\t\xc1so\x93<\xa3\xb2Z\x81\
+\xf9y\xab\xb4N AV\x1f\xe2|\x04\xf7\xb3\x8f\x89\x0e\xdc\x8a|\x82\xc2W\x16\x94\
+\x8d\x03t\xe0\x18\xb4\xde\xd7\x14&R]\xa6\xb84J\x02\n\xa1~\x85N\x06\x07FR\xf5\
+\xd6\x05\xa5\xa4:\x89\xc9OPhj\x0b\x00\x8e\xfe\x8bT\xa8\xb2\xa7\xd5\x98:s%\
+\x8eaA\x01\x88d!_d\xa1\xe3\x1f\xd5\x93E%X\xd7\xd3\x0e\xf2\xa9\'\x91\xd7\xc3\
+\xbd\xc5\x80\x03\xae\xf6L)\xa4\xc8\n\x9dg\xafz\x18Q\xd8\xcd\x110\x19\xe6PS\n\
+\x15.H[\xc9\x004\x7f\x07\x87!\x96\n\x00\x84\xa7\x8bdO\x1fF\x90\x05c\xae\x0cq\
+%\xc6\x07\x91\x935>\xe9-\x02\xc1A_#\xa8L\xc0X\x1a\x01\xa6X\xdc\xd5V\x05\x84\
+\xde\xf32Z\xab[$\x0b\xa2\x96\x7f\xc9(\x11\x01K\xa4\x0f\x89\x00\x1e<\xbf\xd55\
+\xb2\x84\xae\xed+\xaa\xe8\xb1iX8\x08w\xab\xea\x17(S\xf1\x14\x1d/\xb3\x12\xf5\
+\x8a\xf1X\xd5\x807T\x87\x97q\xa1\xe3\xd43[\xea\xa5Y\x81\xa6\xcf\xe2u\x7f=\
+\x99\xc7A1\xf0]\xc7\xf2\x9a\xa6>\xd1e9\xe3Z%\x01\xb0\xa7\x17!^\xe3\x15\xe7\
+\x1e&t\x8eaf\'-??4q\xd0k\xaf\xc6\\\x92Om\xcb85\xb4^\x03\x1c\xef\xde\x882\xc6\
+<\xcb\xd02\xc1\xc0\x9c\xc13N\x95\x17\x083"\x91\x8e\x9b\xac7\x928_\xa9\xb6\
+\xcf=\x8d\x94\xef|\xfd\x1c\'7e\x9ef\x05\xda\xe3$\xb79\x00yAu\t\xa1\x18\xf8k.\
+\xf2\xc8bO\x03\xa2b\xa5^!\x82M\xde\r\xd6\x12\x19\x1f\xb6\x8fn\x8b\xa1\x00\
+\x82\xa1}\x02\xaay\xbb }}^\xa4\x18\xf0\x84(W\x8a\x03~EAN\xd0\xf4<\xe5j\xa8\
+\x1fI\xae\xb5\xc2\xcc\xc7\x11\nQ\xf1\xb5\xb4x\xe9\xad\xd2\xfaVs\xef\x80+\x83\
+\xe1\xde\x0bd\x11\x0f\x7f\x88\x06\xc1\x8a\xa2\xa0y,\x93\x9a\x1eG\xcc3P\xef\
+\x13\xd4\x87\xdcx\xc6#\xde6\t"O\xfe\xb0\x8b\xce\xf3\xbbs\x9e\xa0\xdd{\xc5uV\
+\xf4\x98\x92Y\xee\x91K\xf3\x90b\xca\xaa\x04M\x07\x94x\x1d\x07|!\x9e\'\xf1\
+\xcdQJ7\xcbL\x99W$_}Y]\xec30\xfaxm\xb0\xfd\xe7\xe4)\x8e\x1c\x08V4?$=j(\xd0\
+\xe1\xc6\x92\x89\x03\x88n&\xef\x10\x84\x90<\x0e\xfa\x06\x84\xd2\xd8\xb7t\xd5\
+\xab\n\x9cJ\x90c^\xa4J\xd7\x86\xad\xc0\x98mbC\xeb[v\xc6\x10\x805\x85b\xa0\
+\xb0\xd2\xc6TB\x108Y\x1a\xaa\xeem\xe1\x8cxt\xc9,Z\xf8\xa5+\xe0\xe21o\xeb\t\
+\xa8J~\x98\x9c5\xb3\xdf\x07I\x96g\xc0C\xe0u\x95A\xc0\xa6\t\x08\x97b\x84V\xb0\
+\x18;\\\xd7\xb4C\x8d0=h\x07\xd3\xd4\xd9\x14\x08@\x82\xe9\x14f\x9e\xbc\xb7X\
+\xf7\n\x9c\x05@t\xd7F\xc6I\x96\x84K\xd8^\x9b X\x89-@\xaf/\xc2\xa0`8H\x8f\x04\
+S\x1e3V\x02\x9b\xbf\xfc\xd8\xaf\x1a\x90\xbd\xd7\t\x1d\xb9\x08\xb0\x18\xda\
+\xaa\xc9\x7f]\x8a5\xfc\xb3\x0e\xb0\xe1\xd0\x14\xb9s\x06\x08Ve\xb5\x18I_1Z\
+\x19\x9e\xf0\x8f\xa4\xaa\xfb\xcaJ\xb6\xd5\x0cD\r\xd8f\x8c\xad`\xc8<u\x13ID\
+\x02\xc1\x01\xdd\x0b\xf0Q\x14\x05MUS\xd3j,\x16\xe5\x83\x8e\x83\x0cG\xda+5Ju\
+\x0c\xe9\xd3A\xb5\x01w\xe1D\x1c\x89T\xd0\x9a!\xe7\x92h\x94x\xa3`\xd6\xa7\xca\
+\x07R\x9fV\t\xe6\xe6]\x7f\x02I\x99wu\x11\xd5\x8f\xe533 \x1dB\x96I\x92\x18\
+\xce[S\xf8\xa2 \x8d\xe3\xda\xd9\x85\xcc\xb1o\xca^\xda9\xad\x07\xad\x86\x8b\
+\xca+\xec\xc1\xb7\xf014\x93\x07}\xf4\x13\xd5\x1aO\x06i\x8e3U\x06\x9a\x8bG\
+\xc8U\xeb\x19s~\x95\x10\x10D\xacK\xd4eoQ5\x86D9UG\xb3\xaf\xc0\xe4Qt\xe8 X\
+\xads\x85$HG\x90\x06\x97OF\x84\xcc\xf6\xa7\rX\xdfQ\xaa\xd4\x01\xb5\x86!]tO\\\
+\x07(T\xb8_,\x93\x98\x18\x02v\x99\xa4\xf0\xc1\x98\x16\x94\xb9\xd6kC8\xf8\x96\
+j\x02sz\x0e\xc8\x1f\xccug\xed\xbc0\x93\t=\x96\x1a\xa3\\\x8c\'\xeb\x01m79\x87\
+_\xf6\xe2\x80\xdc\xe1W]\x10(\x10\x99\xd7\t\x8a\xbb6\x86\x1b\xe1J\x84\x1dC\r\
+\xc7\x99,\xf5\xad\x00\x01v\x0f[>\x14\xe6\xa3\x8a3{\xcc\xd0\xf3\xcaE\xd9\x07-\
+\xbd\xc6&f\x13\x8f\xb2\x13\x04\x94h\xe4s\xd0\xe9a\x1e)\xbau\x97*\xa8R\xf3\
+\x91\xc9\x86\xd7\xdfT\xb2J\x07\x8a\xf4\xda\xe5\xf7LP\x12t\xfd\xc1^\x82\xcd\
+\x03r\xc9T\x87\xc2C\xfc\x82F\xc1\x1b\x90j (\x00`\x9e \x99\x1d\xf6^\x19\xec8\
+\x82j9\x89\xe0x_D\xa8\xf6s\x0e\xb8\x03\xe2\xa1i\xe5\xdd:\x01\xef\xa2\xec\xb3\
+\x9f%\xe9\xc4\x85_\x17\x97\x01\x8b\x08\xea#\xb9\x89\x80\xa0\x923\x04N\x13?!n\
+\xac`R\x86\xd4\xd0a\x00\xe9)\xa1\xd7%L\x98\xe8\x1d\xce\xc3\xbc(\xa2\xfc\x1d\
+\x80\xe7\x7fx\x9ep\xd9i\x82wO\xfc<\xf8\xee\x13\xdb\x1d[\x8ew\x06\xfe\xf6\t\
+\xa0/-\xf2\x8e\x1dz<\x95\x10\xc0\xc6>\xaa\xb0B\xfc\x89\xb1\xa0\xc5D\x81a|z2\
+\xe6\xb9y\xca\xd3\x15\xfaC\x99\x89|\xeb\x1cC\xfc\xf2\xe0\xe4\x1ddKh\xbe\xb2C\
+\x83\x00\xc9\xc5\xe1b\x80f\x8d\xd9\xb5)\x1b\x01,[\xdd\x012\x12U\xb4\xf1\x9a\
+\xf8\xb8\x91\xe7\xdc8\xf1/^i{l\xe97\xdb\x17\xab\xf0\xf6\xb2!CF_\xd3\xe0\xe4\
+\xa5\xeb\xfe\xaa2\x13\x95\xa8D\xc3\x80\x8dr\xa7\x08P\xcdA\x9a"\x08\xce\xcc\
+\x18\x1a\xab\\\x15\x96z\xa26\x8fYs4\xd8\xb8\xaf\xdcb\xfd\xffq\xb1\x08\xb1\
+\xa60n\x07&\xf9\xe1\xfd\xact\xb6\x16\xcf\x0c\xbbq/\xcc\x0f\x9f\x140\xc3\xcd\
+\xc7p\x0f\x848\x94!\xdb\x9a\x94\xee \xacN8\xd4!\x943\xb6\xa4\x14\n+\x954\xd2\
+_\xab9\x84\x9f>\x1b\xb5\x82\x13\xfbH\r`OA\xc9\x06\xc8\xc6\x16\xc6>U\xa1TRZ)"\
+}F\x02\xa7\x81\x89\x00B\xc8\x93t\x0c\xd4\x0c\xcb\x0e\xd8\xd7\xec\x18\xc9D\
+\x8d\xbbHl\xe4\xbb\xf4\x15]\xcc\x97 \xce\xbcE\xc5\x1c\xc8\x0f\xc8\xc9\x0c\
+\x99\xd4\xd5-\x15\x1b@\x01\x82\xee\x8a\xc6\x84ITc\x8cM\x05q\xb1\x18\xa9L\x13\
+\x80F\x04y\x99\x08Q\xd9K\xe5\x8c\xcc\xb8F\xe7E\x82C\xcbn\xc4,$;B?\x95\'5\xad\
+\xee\x130\x1cd\xe4\x17\xdf\x05\x94\xcf\xa0\xa7\xe0\xbb8/ \xe2\x8a\xadK\xb1h9\
+A\xf9\x8e*\x15\xdd\xcaB\x1c\xfa\x04\x01\xca\xbb1#gN\x89\xafy\x9d\xa7\x95$\
+\xe7F\x15mr\xdd[h\x10?a\xfc#*\xf8\x03\xeb\r\xb8\xf0\x8d3\xfc\xa2\xa1\x9e\x03\
+\xd7\x82B\xff\'8\xbd\xf1Z O\xbf\x8e(p\xa0S\x8d?96\xbc\xef\xbe2\xc9\xd4\x7fD\
+\x00Q\x08\x9cet\xd3s\xd1F\r\x08Q\x848\xd57`\xdc%\xb9\xba\xf0\x11A\xc4\x9af\n\
+\x1c\x00@#\xc1\xed\x1d\xff\xf4]\xa8\xa2\xed\xf9!\x06\xbd\x96m\x88q\x1a\xd7\
+\x0e\xa4\xe4=\x918\xd6\x07C\x06\xe9\x07\x84x)\x1b\xaf\xdc9V\xc4\x96")\xac\
+\xda\x87\nf#\xe5T>.*D\x00\xe0O\xab\xb6Y\xdaR\x9aO\xd0\xe2\xc0\xaa\x86*\x8c\
+\xdeA\xfa\xa1\xac\xc3\xf2\xad\x8f\x9a\xc2\xa7\xfe\xd2>\xae\xcd\x8cJ05\x92x\
+\xeb\x9d\xda\x0b\x9a\x04Z\x8c\x86\xd2\xf7\xc0p\xa7\xdc\x88\x0f \x03A@\xb0\
+\xe2\xa5\xa9#\xaaNf\x95\xc6\xa0\xfb\x83\xafc\xeeF\xd545\xe6k\xa8\x95/\xe5\
+\xb5\x15l\x0c\xd0\xaa\xe9HK!\xe7\r\xd2\xb1\x8a+\xc7l\x94F\x9an\x97\x9cJ<\x01\
+\xc5\x0b=\x90\x1c\x0b\xdct\xac\xa5U\xbe]\x1e\x07\xb1\x84\xa7W\x14\x96\x1e\
+\xe7sQ?\xc4\x0f\x9e\x87iz\xc3\xb1\xca*\xd6e\x1e|\x08\xf9\xc8Q\xd5#\xaa+\xf7\
+\xd90\xe5LJ\xdb\xce\xaa\x97\x0f\x06\xff\xc2\xca\x1c\xfd\x84,\x0fN\xee\x12Q\
+\x04\xb0\x02\x045"\x0f\xf7\xe7\x14\x1aH\xa6\xd2U\xe0\x90@F\x80\xb8\x07\x8dcJ\
+\x89\xc2\x00Y\x01Z\xbf\xf7\xaazU\xfc\t>x\x8cU\xbeJX\xa8C<k\xfe\xb9\x12\xaf>?\
+\x92\xcdg\x9bx\xae\xd9u\x18\xf88\x84s\xd5\xd0\x11\x0bBT\x105<\xf3"\xbcsx\xd9\
+\rC\xa0\x8b\x89>\x02\xb4>\x81\x81RT^\xe4\xf1\x8e}\n\xdd\x0e\xe4\x1d\xa3\xc5u\
+^\xb9\x1e\xa0\xb3\x85\xa2\x15\x1b\x9d\x0fd\xcb\xa0\x8d\xc9\x1b\x08\x1dy\x91E\
+%\x19\x8b\x1f8\x02\x9c\xe5|\xb7\x91Z\x12\x91\x1d\xad\xdf!\x7f\x8f\xe6\x0f\
+\xf3\x03\x8fb0\xd31\x7f# \x1a(#\x8aH\xdc\xc8ip\xb4\xfd\xe1\xf9C\x05\x17\xb9w\
+\xcd\xa6\xa9B\xb7\x80\xe8\x9d6H\x0cC\x8fT\xb4\x13eX\x1d\xed\x15G\x00\xceoo\
+\x13\xb2\x81\x81F;\x19\xa8*\xbfZ\xd3\x1d\xf2\xb0\xdb\x1f\x8f3;\xde\xaf\x06#\
+\x89o/\xc1\x18\xcd\x04\x85\x82"\x9f\xc6T\xcbi\x16\xd0\x89\x9b\xbb\xbe1\xae\
+\xbd\x8b\xbaAC2^\xe1a\xfd.\x82\x99\x97\x14\x13\xfa\nE\xd2q\'H\xf0g\xcarj\x00\
+\xa1LI\x11H\xf2\x1e\'\xa9b\xd6\xca\xe3\x91)D\xa9\xb2\xc5\xfbX\xb6 \xca\xde!\
+\x8fP\r>h\x02\xa2\xdb\xc9\x88\xf0.\xfd/[\x11\xca\xa5\\\x0e<Qv9\x1bL\xd2\xa8\
+\x15\xc8\x96r\x91Im\xc1\xb7\xaa\xb6\x84\x00R\xbdc\xffU\xdb\x98T\x85G\x08\xc8\
+\xe1=\xa5S{\x8d\x90\xd1\x91d\x8by\xb25\xe6Ch\xae\xfd\x1a\xb4\xa2\x00\xf0}\
+\x13k\xa6L`C\xeb\x81k\xfet\x95\xec\x82\'8@\xe7\xc38o\x9a"NT-u\x1cK\x15\x10\
+\x90\x95Y\xda9D\x08\xd0\n\x0b\xf9\xa7\x81]t,s\xe9d\xbc\xd3I\xa31R\xb1\xe10+r\
+\x9aM\xf2\x01D.\xac\x89Z\xaa \xad\x13P\x1d\x1a)\x05\x86\xb9\xe4C\xbe\xa4\x06\
+\xe2\x94\x81\x1a\x10\xc1qw\xdbc\x84%\xb9u\x8e\xb6\xc3BSww\x0c\x1a\x88\x17\
+\x8e\x00\x02.R \x99\x04\x16U\xa8M\xebQe\x0e\x19\xd3\xdb\xf5~\xa9\x06\xcb\xd6\
+\x044\xff4\xed\xff\x04\xe89\xa6\xf1C\xc6\x86\xf5\x1e\xe7 JRH\x1e(B\x82\x15\
+\x80x\x0f\xdabzF?\n)Qx&a\xa1T*HT>\x14%D\xc5w\x8f\xa8\x0e@\xea\x05[s\x87\xffr\
+&1\xb5"\xac\x13\x9dr\x90S\x07e\x82\x88\xe0\xc1\xa8v\x06\xc4\x85o5\x88\xd7fa_\
+\x91;\x86\x92\xc3\xae"bVR\x1c\xa4+\xe7|\x07G\xe4F\xaa\n\x08KM\x18\x9f\x94\
+\x05\xba\x86\xb4?\xdd\r\xd1\x01\\!\xe0:\x9cu\xa3s\xc3\x11@P\xe4\xc8T_\x8er\
+\x85\xbc\xc8T\x08\xe5\xb5Z\xdeM\xb1\x06M\xcf;\x05Ff\xa0\xcb\xc6D_\x08:\xc4F0\
+~Z\xab\x90\xacCB\x030\xbf\x05\x8dF\xea=c\x92\x04"\x8c\x01u\xf42\x02\x02\x01\
+\xd2\x1b\xc0\x95Hg\x10\xd9\x17\xea\n\x94\x11`\xc8\xb1\x80\x8ad\xb3-\xf5i\xe8\
+\x01\xa8\x97\xb8\xb2\xfa\xd0-\x16IU\xe0U#\xa4\xf1-\x8a\xc5\xffI\x0e\'\xd68\
+\x88\xcd4T9\x16.\xefA.5P\x84\x81+bb\xa3\xe2Wi\x12E\xe4D\xbfc\xf2 \xc4$[U\x13\
+tY\xe2\x01_\x1f(\x9c\xc4t\x92./5\x9c}\xcf\xef\n\x8da&\x019\x87\xd2~\x04\x16\
+\xa8\rp\xa9\x1a\x96-\xc4(\xf2\xe5\xd0Ur$\xf1\xcb\xa5!M\r\xe8\xe5\xc3\x85\xa6\
+\xf1\x8b*\x1f\xebN\x0f`L\x98\n\x04$\n\xe8\xc2\x1d\xbeA8B\xa7;\xbc\xcc\xde\
+\xe6\xec\xe0\xd0\x00.,\x97\x06\xd5z4%m\x9a\x04\x99Sv:6v\x8eEsi\x01\xc9\xab\t\
+(\rO\x1d\x01\x1a\x17xN\xe8\xe2\xaf\x8a\x00\xbc\xdc\xac\xb7\x8fkl,<\xaf\xc4\
+\x0b*\x86\x19\x8e\r\xa1\xe9^^\x92w\x06Q\xad\xe0\xf0\xbaD\x1b\xdb\x98\x16%\
+\x88\\\x82\xc2\x91t\x1a\x9fT\x01\xab8]fl\xa6H\xc5\x136l\xa6\x13\xcdN$\xd2\
+\xed\xef\xa2\x85\x86\xe1\xdb\x99\t\x0b\xc8r`+ \xcd\x8dr\x95\x99\r\xe3\x0c.\
+\xdeU\xe8\xf6\x94\xd9D\x9bJ\xc5\xcb\x99@\xb6\x12\xe5YD\xa7B\xcbfQ\xc4\x9b\
+\x03\x05\xc5\xe7\x81@rQ\x13]R\xa7|\xf4czXC(\x16\x80\xd7\xab!\x80\xfeT\x05!o{\
+\xc4i\xdc\xe4\xed\x88\x1c\xc0\xe2\xed\xbb(\xaf\x9cF8\x00\xef:s`(\x03B\xa8\
+\xb2\xe1\x86\xee\xad*\xaf\xa8^,/v\x91\xa6\xf3\xd4\xfb\x93\x08p\xd4@ \xaa\xc2\
+\x11\xc48L\xe8\xa9n\x01\x00\xc4\x91\xeb\x85\xdeVW|\xa8\xb5\xd0+\xaa\xf5\'\
+\x04\xdc\xf4\tG\x10wg\x14\\\xd4\x04a\xc1\xcfc\x8f\xae\x8f?\xdaRecY\xa4g\xa8*\
+}\xad\xc8\x89\xabw\xdd\x01\xc4LT \x80\x1a#dl"y\xd1\x88|[\xba\xc0\xfb\x91\x8b\
+\x99\xa9\xdc\xb0G\xc0\xe1\x83 %q`\x0f\xe0\xfc\xf3G\x08\xe8\xc6chU\xd2\xd7-\
+\x13\x1d\xb4\x1am\xa7\x94T\xc1\xa3 \x00\xfd\x90\x89\x9ef\xfe\xdbH\xe6j\x9aZ@\
+B-\xfcU\x1a\xa2\xb5\xcd\x92\x866\xa24\xb7;9u|\xe3\xa5\xf7\xa4\x82U!\x0f\xbd\
+\xd1\xa0\x80\xa0\xef\xf0WG\xbe\xa9\x0c\xd2:c;\xc4\x10{\xcc(\x03\xa8rbkM\x16\
+\x02\xd8\xd6\x80\xa4\'t\x80\xf4\x12\xad\xb9iP\x02\xc2YVZ\x1b\xfe\xa8;\x81)\
+\x9b\xe7\xb7rm%\x13X-U\xd9Q\x88 (|\xc7\x84\xfe\x89\xb4\x84\xf5E\xd7$\x8a,\
+\x19\xe0\x82T\xe2\x9c\x9a\xc3\xe6\xbe\x80\x80\x94B\xcbH\x84!Q;\x12\x85U\x8c\
+\x18\x88E\xcf@\xa8\x02)\x1b\t\x05\xab\xdfQN\xdc]\x12\x19|.(\xa2\xf3q\x00\x01\
+\x04\\\x9f+\xb93~\xb9\xd4\xe2"\x89\x125\xc6=h\xa2\x92\xfa\xdfr\x03\xbb\xb1\
+\xeb\xb0\xd4\xb9!\x01$\xcf\xa0\x08\x1c\xba\n\xd4\x83\xcd\xe6\x9a\x19\xf7\x81\
+S\x0e\x82j}\xa7\xbc\xc3\xaf\xf0U\xef\xa4\x1dINR\x1132\xf6\xa4\x80\xf3\xa5\
+\xc86\xf3h\xba\xb4\xf4\xe2\x12\x80\xf4m\xf8e#\xc9i\xa7\x9f8\xa8\xec\xe1Z\xa5\
+\x00l\x048\xce\xa21\xfa\x05\x02z\x87\x16-\xfa\x18\x12y\xcd<\x01X.\xe5t\xb5\
+\x1f\x83\xd7\x08U0\x93\xa6\x886\xf3X^\x8aB0L\x02K\xc86\x01\x0fV\xe6\xa7a\xa1\
+\xc8\x13\x82\xa8\x16\xfc\\i,/\xb2I\x04\x98\xcb\xc3\xbc\xd7-\xff\\\xda;\xfd\
+\xf8Fq=\xa9G\xba,\x90y\x8a\xb8\x08\x83\x8c&H\xc6[\xd5\xdc1\x93\x96\xae\t\xbd\
+\x08\x03\xa1\xc0\xd9f\xa0\x8b;\x81\x82\x1a\x7f\xb2XPK2\xc5\xbe_r\r\xf3\xfb\
+\x00a jS\x7f\xbd5\x15\x8f\x8e\x98e\xf3<twCP61\xd2\x04\xf5\xe5\x90)"\x8f+\xca\
+\xce\xe3L\xab\x1e\x9d\xa9\xed\xd9\xc0q4\x97i\x02$\xb5\x90H]\x04\x05\x01\xf2|\
+@T&L\xd7\x14\x0c\xb5\xe3\x8cc>\x86\x94y\xa0]\xa8=\x93O\xba\x1c\x9a\x11`\x1dN\
+\xd2\x85&\x88\x18p\xd82Q\x8b@\xf3\x93\x1et<7\xbaW\xb437\x195N\x00E\x80E[ \
+\x80 \x01\xa9~\xbeFe\x9d\x0f\xe8\xa3\xa1\r5\x90\x04a%\xeb\x95`E\x04\xfa\x88&\
+{\x86\xb95\xa2\xc0\x01\xce\xd0\xa0\x18\xe7\xc2S\xe9x\x17\x88\x8ekU\x1f\xe4\
+\xc4\x9f"\x8d^t\xfc\x0f\x11r\x8c\x91\x0f\x8c\xf2yAN\x07\x90#!\x0e\xfd\xd5y\
+\x12\xf1\x04\x08z\xcf\xf6\x01I9\x88\xb5\xc5\x84\x03V#[\x1eP\xd6\x88\x8a\x01\
+\x9f6\x06\xf4`L\xac\xc0\x89\xde\x05\xd2`Er\xa4\x02\xcdZ\xd0\x15"\xaa\x0cT\'X\
+9\x10@\xd7\x82\x86C@Qn\xba\xfa\xeb\'\xee\xe2\x8b_B(9\x1c\xe2\xf1>/\xe2k\xde\
+\xd9\xc6\xa8\xb4>A\x90K0\x01Q\x0f\x00R\xa1\\\x19\x00\x93\x1e\x80\x94\xb42\
+\xc8e^JJ\xfa\x01\xaa3\xa8\xae\x06\x1a\x08\xa0\xea\xac\x1a\x01\x94C\xa6\x01*\
+\xc0\xa3>\x1aV\xf4\x1bj}\x03\x00e\x92\x99\xf9\xf0Ca!\xd0x]\r\xbdS\xb6`\x8c\
+\x02,]\xa2\x810\x16rs\x81\xb0\xfaT\x9e\x80\xf3D#\xa9\x06\xd7\xe6\x9b\x82\x7f\
+\x87o\xc4%\xf2\x9a\x87\x0f\x08L\x16\x19g\xde\xd8\x81\xad\x08\xe8\xb7\x19\xaa\
+\xca\x8a!g$\x8d:S\xf6@:[\xd4#\xa0\x83\x9e\x91\xe7xC\x85\xd2\xb0\xa5Y8\xc4\
+\x84E\xa0\xfa\x84!\x80d\x10\r\x92H\x8d?\x04\x10\xb5\xca \xb0\x8c-\xb7\x8d\
+\xa4,o"\xa8`*!@\x0bD\x13\x04\x8b\x10\x19\xe2\xe3\xc8\xe2\x90#@A\x8bSo\xd5y J\
+\xd5\x11@\xd4\x80\xddi\xd4~\xe5\xf87\x17\x82bb\xca\x93\xa1"\xeaO/\x94s|0\xa2\
+\xb3\xc5\xf5b\'L\x99\xfein\xa1\xca\x01\x92o\x88\x8d+\x86\x80V\x060\xaf\\\xb9\
+k\x1c\xd3\x91h\x01\x81\x95q\xfc\x86LC\xb55\xc1\x9e\x81\xb0\xc3\r\x84\x11\xe2\
+f\x01\xebH\x84\x83\x9e< \x1c\xb0W\xc2\xf5\xb2,m\x7f\xa8\x0ef/`\xa6\xcc\x88T\
+\x83\x98\x89P\xc0C\x88H>k\xa4c\x01\x85\x9f\x9c\x1b\xa6.\x06\x1b\xb5(\xf1B0\
+\x18\xc7xy\x90\xa2[\x92\xc2q\xb2\xb3e\xea\x90\xd8\xc4\xa1\x12\x05\xc6\x89\
+\x06\x8f(\x84\xe7~\xa4{\xa8\xcd\x1e\xd9NDX\'\x00\x04\x05m(;\xfa\x87\xdf\xa4\
+\xa2R\x81\xcb\x06\x90d\x1d\x01*\x1e\x84\xc2h\xdb\x9b^1C$\x1d\xd1A\x17\x858QR\
+Y\xcc$\x1f\xaa\x16+\x86+\x84\x82\x00@\x85C\xc0\xf4\x01\xfde\xde\x00\xa7M\xe3\
+\xc9"t\x83\xb5}\xd7\xa7\xba\xde\xd7\x10`\xb9\x08\xb5F\xb3\x16\x01|~\x02\xa6f\
+\xb6%-!\xc2K\xa8*\x17\x1d\xf9\x02\xf2\xc2\xd2\x0e\x03\xc3\xda\xf4>\x81\x9dBp\
+N\xb3\x0b\xb9\xa5b\x11Am\x1bs\x0c\x89\x90\xd1\xa5\xf9g\xc8@6\xeb\x94JA"C\x86\
+B\xff\x17T\x90\xcaN\xe9\x88\x87\x80\xc2\xf0\xc1\x81|\xe5\xc3*\x04\xbd\x01jl\
+\x8c\x0c\x8f\xa1\xa9\x965^\x1a\xe2/z\x7fz\x10Z\x9d\xd5g\x96\x93\x99z\x8dCKyT\
+S`\x07A\x83u\x90\xaf\x97\x10UD\xc5\x81\x07\xb1\xd0\xd1\xfd\x02-\xee\xe9\x9fB\
+\xa0\xb6M\x7f\x99e\x1e\x03\x90\x15\x7f\xde\x07\xc4\xc53c\xbd\xf2\xe5\xa8\x99\
+\xa0\xbb\x07Y\xd4T\xbe\x9d\x13\xcf *\xa8v\xd2J\x7fL\xba\x00}\xce\x15\xeaf\
+\xc1\x84\x81a\x1a\x1c@\xfa\xde\x81Z\xa9\xa3nb\xb8\x1a\x12\x01<\'\xcd\xba\x92\
+1Q \x13\xbe\xdd\r\xd4\xa1\xee\xd8\xb0\xc8x\x9aS\x8d\xc1Q\xd9\x96+FTs\xe1\x12\
+\xb9\xe1\xbb\x83j\x87\xf0\xcf\xe7HrM\x80\xb9\xadT\x00\xf5n`\xa4\x90\x1f\xe8\
+\xca\xab$\xf4|"&\x8dO\xc7sx\x00\xc7\x8f\x0cVC\xd6:\xb3]\xb4F+\x81\xb6I\x1c\
+\x0f\xff*\x05S\n\xf2\xcb\'\x8e(\x00\xe0\x08p\x8e\xb8\xa9z?*\xc7@J\x08\xe8\
+\xa5\xfe\x9d\x91?T2\x84\xe7\x96\xb3\x05R\x95*\x99-\x972\x8d\xef\xbc\xc5\xe6c\
+\x95*-\xa3\x9f-b\xc5\xdaH\x1f\xf2ecp\\\xf2\xe1\xd7aV\xca\x86\xdf\x18\xcdE\
+\x04(\xb3A#g\xca\x86\xd6\'L\xcf\xcf\xa5\x021\x94\x9c\x80\xe83\xe73\xc4\\>\
+\xc4\x85\x82\xdcq3J\xb3\xb4\xd2\xd8ESYE\x07\xa4\xa8j5\x94@\x80d\xaf#\xa0\x1c\
+\xb4\xc5\x1cs1\x98V,\xe2\x14i\x97\xf3P%0\x84>\xa0\xc1\x1cL\xf4\xb0\x9ftn<\
+\xb9\xc3\t\\>m\xaf\xa7\xc3\xd8\x84\x1c\xf5>\xf5@`\xa0na\x0b\x16\x19=\x9f^kK\
+\xbd\x16<4k:\xd2Z\xfc\xf7\xf4\xd6\xe2\x83\x11\x83Bf}\xf5\x1a/\x1bfBr\xc2s\
+\xdaLA\x80\x82\xa9$R\xd5?!y\xaa\xc7\xa5\xca\x1c;s\x9e\xa3\xd6\xf1\xde\x01"g~\
+Bh\x18\x02p\xdd|\xe5X\x19x\xaa\x9f8\xcc\xd0\xc7\xf93\xaf2U\xc5\xaf\x9c-\xa1\
+\x81\x8f\xa8\xae\x9b\x8b\x13\xbb\x00\x17\xcd\'`\xca\x80I\xae\n\x01\xb4~]Z\
+\x88u\xebA6:\xee\xb3N\xa2V/\xb0\x17Wu0@?5\xfesY\xe1\xf7\x0f\x12\x07\x96z\xd0\
+\x8e\x11D"\xe0\xc4\xffi\xbd\xcf\x80\x06\xa5\x81\xa0\x90\xe7\xd7\xd0\x05\xc6\
+\x90\x8d\xa2\xea@V?\x8b\xa8\xd6(d\x0f\x9b\xf3GA\xca\xa6\x88\t\x01\x15\x07\
+\xf0\x88\t0\xdf\n\x03\x14\x9aY\xb2\xe1\xad{\\\x1f\xf0\xed\x8c47\xcf\x1b+\x0f\
+\xe00\x16 =\xe8)\x14{q\xbaW\x08JyC`(g\xc9\x857\x08\xa3\xb4*\x00\x85\xa6\x1bI\
+\xca\xc71\xeb\x9d\x00t\x17\x84\xa4\x16u\xe8_E\xd0\xcfe\n\x95\x8f\xe7\x04h\
+\xae*\xe6Y\xf4R|\x9f\xd7W\x8c\xc4Z\xee\xc9\xe8.g,\xe7+\xd6G=\xcclM\xe0\xd1t\
+\x95$\x05MP#\x1ek3v\x95L\x00\'\x9b\xe5\t\xa5\xe2\xc3\xc4\x00q\xe4h#\xb3\xcb\
+\x87\x15\x04\x0f\xc5^\xc5UR\x8eR\xf9\x8c!\x10\xf5P\xae\r\xab\xa4R\xa0$\x1c\
+\x9f\x98\xd8\xb5\xf2v\xf3\xd7\xda\xf6\n\xa6\xfb&:\xa9\xd7\x87Tk\x8f\x15a\xa7\
+%\xdf\x00"\xd9\xf85V\xe0\xfb\x81U\x00\xc8! R\xa9G\x00r\xe4\xc8\xb4\x8e\xcd\
+\x06\x14\xfbe\xfb\x92\xda\xab\x89\xcc\xec\x80^k[\x82\x81\xba\xf5W\x1d\xaa\
+\x0f\xdfGn\x046\xd1\x08\xe1KC\x19U\xe8\xa4#\x0f\x00\xe1 \x0e\xdb\x07\x12\x85\
+i\x13lf)\x97\xd9\x86\xf2\xa3\x85\x8aF\x08$\xd3%\x93xv\x06\xe0\xc9\x8fZ\x0b/\
+\xdc\x94\xc5\x9f\xaan\x87^;\x9a:B\x85]\x80\xa4$=z\xdbr\x88d/\xda4\xd6\x12\
+\x86F^[\xfa\x03\x915\x00\x00 \x00IDAT18:\xb7b>\xa2<"\xa0\xd5\x96G\x00\x16J\
+\xee\x99s\xea,H\x7f\xa5\x18\xb06\xe7\xfac\x05\xceE8cHT\x8fw|\x04u!\xad\xe9\
+\xaa\xd3U\x1e\xe1\xf8\xb5-M\xf9l\x83$b\x94\x94Q\xf4\xfb#\xa1\xe7\xbc\x12\x03\
+\xa0\x7f\x10\xd1\xf2oI-\x80\xa1\xd3\xd6kIB\xac\x14""\xca\xa5R\x8dR\x86\x94\
+\xc5\xa6\xac\n\x0fH\xe5\xa03\x86\x99\xc0\x1e\x0bW2\xc8\x08\xb5k\x93b\x00\x81\
+$q\xac\xb9\x8c3%\xa4\xce\xa0\x1e\x16\x1d\xf4(\xbd\xc3\xfc\xb0~Q\xac\xb3/\x02\
+\xba\xe1\xacXo\x06-\xb3B\x83fK\xa6\xf2\xd9x\x0f\xe0\xf0\x19CH\\U~K0\xd6\xa8\
+\x11\xf7L\xa8\x98I\xc9#\xdd\xb6\xac\xad:c\xd2\xc3\x14\xc0\x13C\x00\xc95\xeel\
+D\xc2\xc00o\x03\x81"\x02P\xcd\xf2\x92\xba\x07JptR\x17\x8b\xf55%m\xbfZ\xd8i\
+\xd8\xf1:m\xfcQ5 $\xca\xf3\x96xi\xcbZ\x03}\x18\x05@\xbd,\xe2O\x85\xe9@"Fg\nm\
+?\x07\xbb\xa0\x1c\x1a\xb2\xab\x8c\x7fK\x9e \xbf\xe8\xee\x10\xb3\xf5z\x08\xbb\
+\x88\xbd\xbd\x01\xc5R$s\xd0\xde24Tred\xeb\xe7\xcda\xf1\xf7\x1c\x8c>J\x93\xcd\
+\xb1(\x0ep<n\\\xf7\xa5e\x00p\xed\x07\x93\xa3>\xb0m1\x8b\xd5\xfd{TA\xf8\xf1|\
+\x04yQ\x8a\x8f0\xfe]e\xd6\x0ea\x0c\xf2\x0e\x81\x95\xd4p\xe13\xf1\xf7\xf1\x03\
+z\x04\xd5\x87\xa8#ha\xa5b\xf5\x0c\xe5\x85\xf4\x81md\xb9:\x1c@\x80E\xa4V\xf5m\
+7\xc6E\xa8\t_T\xa9q\x9c\xe2H\toe\x8bV)\x9b\x8a\xc8\x92\xd4\xce\'\xf0\x10\x07\
+\x00in\xcdi\xb0\x90\x9d\xba\xf7\xa2&\xbd\x94\xc8\xcc\x9c\xc7\x8a@\xfd.Y\xa9$\
+\xe3\x82\xe0}\xbcM~\xa2\x0f>Br\x1e\x90\xa7V\xb3\x06@k\xf6\x82E\x94\xcc<c\xc9\
+l\xf4S\xd83\xa6\x94F^\x19\x15C\xfc\xdeAh$\x15\xc6\xf0\xcf\x04V"\xc0\x89\x18\
+\xed\xda\x0e\xaa\x19\xe0\xbdK\x86\x16$\x9f\xd1\x05U\x90\xd6\x12|\xb0\x0b\xf4\
+\x95\xfd\xe8\xd3\xa0\x16W\xea\xeb!p\x94\x92l\x02\x17i5\xe5\x05\x85\x86\xb2\
+\xc17\x02\x06\xa2 \xaa\xb4\xe1\x8b?\xd6\xd0_\x89\xf2\xd7\x82\x89\x80\xcayn\
+\xde\xf4\xa5\xd9 \xb6\x11]$\xc2AR;\xed\x0f~M\x87\xf3a\x12\x13?\x0b\x89\xbf\
+\xf3G\xfc\n\xcc\x91\xfa\xab!\x800l%\xeb\x02\xd7D\x9a\xc1\x9aF\x1b\x8d\x94\
+\xa0\t\xfaK\xddXd\xe1\x915g\x85\xb9\x99\xafr\xc8\xb2!v+2\xeb1\xcdK?(T\x9c\
+\xe6\x8a\xedG\x9b[m\x90\xac\x05\xf2+\xb9\xfd-\xcb\x9a1\x08\x91\xb5x\x9f\xe9s\
+\xeeMh*\xc5\x80\x01\x1f\x07\xb1\xaap\xa3\xbd\xbd\xac"TN\x05\xf5s`$\xa3\xe3\
+\x11\xe8\'h\xa7\\\xd7\x92\xe7\'C\x8cC#\x96\x94u4R\xf4\x8aO8h\xbf4\xed\x01\
+\xc8G\x82[PxN\x88\xf9\x03\x15\x031$\x90\xc7\x97$\x14\x14\xa2fGt\\\xa8j\xa9\
+\xbbI\xfd\x8a=\x1de+\xb9rk8?\xc4{\x8b\x1f\x99[\xa1\x11\x9co\xff\xe0>\xb2\x87\
+[\xce\x99\x08:P\xd7\x14\x18\x07\x10?\xdfN\x9e@\xf4@\x14\x03\x80&\xfe~\xaa+\
+\x8e_\xda\xef:\x11-V\x08\xdcV\x05ldt\x15\xa6O\xe2\xadT\xf6!Y\xbd.\x1a\x85\
+\xd2\xe9\x81n\xbc\xf7\xe6\x07\x00\xa2n\xc0\x11\xc2\x9b\x0c\xb6\x00\x92\xbd\
+\xe80\xc0\x9e@\xa4\x8eAIO\xb3\xb1\xed\xf5F(\x1ck\x11\x05]\xa03\xa1\xcb\xbd\
+\xbbo[\xda@\x10\x7f\xe9\xa0\x16\xd7\xbaJ\xc8\x08\xc9,\'4\x00swB\xf5.o\x15\
+\xea\x83$\x10\xcc\xb2\'8\x00\xe0P\x88\'\xd3\xf4\x91\x15\x89\xab\xc6\'&z\x92\
+\x82gC\n\x11\xc7#V\x1c\xb8\xc5\xf1\xbe\x9d\x1d\x109\xf5\xaa\x8b\xdb\xd4x\xe9\
+\xc8E\xb8+\x0cvv!\xd0\x10\xac\x02t7\xc5QFu\xb6\x89`V\x10Y\x08q\xce\x1b\xc3\
+\x02d\x8f\x82\xe8\x0f\xda\x1a/r\xf1\xe2\x8c3?\x8cT\x0cZ\xf60n\x14\xcf\\\xab\
+\xd1\\\xd2\xe7\x96]\x8c{\x86\x802\x10<\xb9\xc37\x08\x05I5D\x8e\x1dh\xc2\xec\
+\xb8\xf2\xe0=\x8d\x91\xb5k\xac\xf17\xca&X\xd8\xea\x80\xf3^\x1a\xcaY\xedZ+~\
+\xe9\x9d\xe2\xd21!y\xcd\xa8\xb3\xc5gr\xc1\xeaI#\x83\xee\xe2\xb1x\x89\x00KZ ;\
+\x95\xd8\x848c@)\xd8\xc1&f\xd3\x00t\xc8\x91\xdd)M\xeaA\xe2 U\xedpN\r\xbd%3\
+\xee\x8a\x9dm\xa5\t-d\xe4P(\xf1\xd9S\xfb\x04\x12\xf6\x94\x19\x93\x94\xa8\xea\
+\xc7\x95\xfc5\x89\x00J\x95\xa7[\x92qQ\x01x\x19\x8f\x83:4I\x1b\x95\xef\x17\
+\xc9\xef\x91H\xbb\x80\x90\x11\x99Of\x812o\xbar\xc2\x0b*\x84\xb2\x1b8D)\x92\
+\xcc\xcf\x13\xb4A_\xc0\xb7\x11\x90u\xfe\x0b-\xd5\x93\xd5X\xf2\n\xfe\x14\xa9]\
+\x0e\x17\xe8\xa6s$\xcap@\x93q\x07Q\xaeM#P\xd0P\x88zE\x9al\xbfG\x11e\x05\x02\
+\x00\x03,\xda\xf8\x0f\xb1k\x8b\xd0Q\x04\xb8\x14\x932\xb0C\x9e\x95!\xa3\n\x0c\
+\x96t\x83\x04\x82\xa52\'\xde\xd6\xab\xca*>-\x91\x0e\xf6\xe7\xdb*S\x8bE\x8c\
+\xcc\x1c\x01\x02\x06Y\x12\x81\x10~\xd5}\x08\\Y\x13E\xaa\x9f\x01T<A+\xd4\x9d\
+\xdaM\x9bg \xdb\x14XR\x92L\xca\x99\x17\x80\x1b>\xbevLY\x9cY\x15\x19\x14\xcaJ\
+H\xd6\xc4`\x82\x97,\xf8\x16\x9e\xfe\xc9V\xcb\xcfty\x87V\x0cU=\x94\xd5\\N\x11\
+\xbe\xa1\xcd\x1c\xfb\xd5S\xe3\r\xf6\x0f\xac7q$\xd3\x80]\xb5\x8e\x89*\x83\xd1\
+\xef\x14\x1du\x118\xd7@|\xa62\x1d\xd0@c(!\xd3\xbd \x19y\x06cc[\xc2\x0b\x7f0\
+\xbb\xba7:\x8a\xce\xeaD\xe9\xab\xdbuX\xe2GC"\x83\x83\x04\x12P\xbc\xb3\x82\
+\xd6w\xed/\xdeP\xce\x14`\x05=\xa4\x8a\x11\xbf\xbd\xbc\x12[\xfb\x8a\\^\xbb\
+\xc1\x9d\xc4\xd6\xaeu>\xacx\xebU\xf7\xc4\x04\xc4\x98\x8a \xa4j\xdf\xe2h\xd1\
+\x9dh\x80WA\x00\xf8\xd4\x12\x97b\xdd\xe6\xc3\xdfl\x9b\xccD1G\xa8\x1f@\x96\
+\xdc\x13\xcd\xac\xb5g\x89j\x9fT?\xa1\x9a\x98\xc0o*\xe13\x82\x0e\n\xea\xe1e\
+\xcdG3\r\x1aD\xcd\xa1\xf7\x96\xd0\xad.\xfc\xd5T\x89\xe7\x11\xec2p\xaf\xbc\
+\xb3#\x178g\x94\xb0T\xdbT\x86\xd6Ie\xcb\xc9\x84\x00_\xd5q\xe6\xd6]jr\x15\xd6\
+1\x8f\xb4\xc9\xd9\xc7\x97\xd5\xf7\x18"_\x0f\x93\xa3\xa43\x9b\x02\xfa\xb8\xf0\
+\xd0m\xd1\x9a\x05\x89)\x10z\x80\xe8\x86L\x08\xc5\xed\xb3\xf7\x92\x027B\x1a$2\
+\xe6;8\n\x9aR/\x80P\x93\x17h00<\x9b\x0cy\xa1](\xc3)\xf0\xe3e2S\xeaK\x1a)\xeb\
+$\x9e3\xa3\x8640\x1dE\xf1\xadR\xf8\x7f\x00\xe0\x12$f\xc1i\xe8\xec\x15\x18\
+\x94\xad\x1e\xc0\x86\xa3\x87V\x89\xc1v\xd8\x1d )\x99\x88\xb1\x9c\xbcb\x9d\
+\x06==\xff\x00]{\xd5\x9f\x04\xdaAl!A\x92\xd4\xeb-\xb8\x03\x1c\x014\x8b\x8b\
+\x7f\xb0\xf4j\x0c\xb16\x06T\xe74\xd0v\x94r\xc6\xa9\xa9t\x03\x02\x0c\x0c\xf1z\
+\xf2\x0f\xe7Kg\xd5k\x11\x90\xcf\xa7K\xcd\x03DMP\xf1\x9a\x86Z\xdfG1\x90\xfd\
+\x06\x90=\xba\x1d\xddB\x92\x85Huru9\r\xfaR{\x91{C\xd5\x98Y\xa9\x12\xea\xcfK\
+\xc7?\x00\xa4yx\xdeG\x1d\xb5a\xb4\x9f\x19\x8c\xcf\xe4\xf6\xae\x829\x85\x86\
+\xa3d;_\x8a\x1a \xb2"S\x00\x0e\x05\xe1Kp\xf6\x15w2/\xc0(\xf0`\x85b\xb4\x19r[\
+A\xa6\x93\x94~\xbd\x88\xd6\xfc\xc1^\xe2\xa7l\x9bK\xc8\xea\xec\xa0_\x08\xdde\
+\x8e_!x\xbd\x1a\xc3\x13\x13d\x89oP\x08\xda#\x8f*\x97\xdcE\'\x06\xbbX\x19o\
+\x91=MWjT\xdd@\x85\x14s \xa5Z\x1a6\xa4\xc5\xa2!\x11\xa0\x04\xa7\xfd\xa4T\x14\
+\x9f\\Bd\xe7UbA(\xd4-D\x83T\x1d\xf9\xd8{\xf4f\xfb\xb0C\x10\x83\xd7p`\x80Ww\
+\xec\x11\x1f(J\x1b\xee\xa4\xa4\x0e\x84r\x0c\xe1HSD\xd9"i+\xb9_ \xfe9sq\xad\
+\x02\xa0!\x80\'f\t\xa0\x89\n;`\xd2\xf9T\xc4\xbe\xb4\xb3D\xf53\x95\x06g($\x1c\
+T\xb4(\x1b\x02\x0cL\xbf,\\\xfa|\xbcr\x84:\x87\xec*\xb6Z\xb2\xfaVr\x15\xdd R3\
+\x05\xdda\xc5\x80\x1e;!z Fq?\x80$\xfb\xe0\x17%P8\x82\x03A*\x00\x06t\xb5k7\
+\xdfg\xd2\xebN\xe5z\xf3\x06G\xd4/\xf2\xa7\xea[\xb6V\xc9(\x88\x93E\x16\xf4y}\
+\xba\xec\xf9`\xd4\xdevB\xe25\xb3\xe1\x8c\xfeON \x86B\xd6\xd3\xd74\x06\xafK\
+\xf6\xa3\xc7\xa9\xe4\xc6*\xd1?\xf4D@Ju)\x039(\xc6(\xf7a\x11\x7f\x17\x11\n>W\
+\xaaO\x886\x98f>\xe2q**\x98\xe9\xc8\xb6\x12\x8a$J<^FU\xef\xe8\xd26u\xba\x1c\
+\x96\xbe\xed\x1b\xa4\xb2>\x15V\xe78\xfdu\x00\x8d)\x95\xaf\xf5\x0cj\x18\xd0]\
+\xa6\x90\x14\x00\xd7`\x89\xd6|\xa4\x85#\xc0\xe9\xd7\xe69c\x17\x13\xa9\xac$\
+\x8b\xec\xca\xa5\xb1c0h\xf2\xadC\xa0\xa0K\x89\xa3\xe3R\xfe\x8a\x17\x9a /\xa0\
+\x87\xec\xf2:\xc0\xd0\xa854\x8b\xa1\xa9\xc2\xb5\xc1\x87\x95\xa6X|\x1e\x85\
+\xcf\x8cd\xe8j\x10\xa3\xfa\xdfq\x070\x06\x02\x96\xe0A`zf\x0f\x99\xf0\xec\x10\
+\xc1\x17\xc8\xa5\x1b\xd7\xfe\xc9\x0e\xa5\xd8\xf8\xd4\x9e\xfa5\x00M\xcb\x02\
+\x89\xc9C\x8af\x10\x87J2!\x19$V\xbba\x1d\xd8\x14\x80\xdf\xd2\x0ePB\xa2\xc2\
+\xdeu\x97\x1d\xf1\xb9(\x81\x11PH\x15\xbb\x82BA\x12\xd1\xa1\xc3\x8b\xeb8\x182\
+h\xb6&\xe7X\xd0 >\x98][\xa1\xa3\x7f\x14\xc3\x1f"\x99CE\xffw\xfaN5\x0b\x02\
+\x07\x81$.\x1c\x06;\x9d\\\x08/\x00%+\xa2\xa3\x99s\xba/@\x01\xd3\x848\xc0Duz\
+\xa86\x18\xb9\'g\xd8\xad\xcch\x82\xa1K\xa0\'\x8b|-_\x8e]\x96<A\x92\xc1\xd1ZV\
+8\x10\x04\xdb\x8a_X\x0c\x1e]8NB\xe6J\x15q\x99\xa3\xbb\xbeM\xaa\x1c\x11\x9a\
+\xfe2\xd7\x12l6pE\xca\xd7\xd0L$0g\xcd\xd18~\xaf\x8f|\x94\xa3\x16\x07\xc5\xd7\
+\xe1\x06=A]\xbf\xa4\xeeu\x1cx\xe5\x0fv\x009\x8fR\xdc&_\xa9H\xcb\x96\xe2\xd4e\
+\xcb\xcb\xecbt\x05\n\xc3\xef,\xc4"\xde\x1b\x1bH\x05\x9e\xa8854(b\xe7\x1e\x01\
+0\xac\x91\x1b\x0f\xd97\x14\x92\xba\x00\xc2\x9b\x04\x08\x17\xe9\x14D\xc2\x81\
+\xec"\x1a\xedyC\xba\xa2\x1c,\xa5\xe0IS\xca\xc7Y\xe3(\xd0\xb6;l\x1f\x90VIz\
+\xb0\x0f\xceT\x10\xe4U\xa8\xf0\x01\xe2]\x94\xba\x18\xf5\x1a\x02\x94\xbaH\x0e\
+\x1f,\xaeVP\xb6Q\xa8\xf0\xf8\xb0b\xef\x93f\x9cR{\xed\xc5U\xa8F\x8d\xa7\x84(\
+\x0e\xac\x8c\x1d\xea\x0c\xcd!\x892\xd3C\xf9\xcd0\x93\xae\xbd\x07\x80\xc6#\
+\xdb\x8681\x03\xe7\x87\n=\xbd\xfb\x18\xd3*\xaf\xb1\x8a\x01P,\x91\x02u\x04\
+\x81\x81J\xf7\xbf5\xfd\x83\x06\x11\x1dU:\x16\xa0\x1b|\xf2\xac\xf7P\xad\x90\
+\xfcT\x07\x03\x9d\x81^+^/\x0f"\xc4\x17U5`x\x94\x96s\xa4gt\x08\x07(\x96\xd0\
+\xc9\x12@\x03w\x88\x9e\x93J\x9c%\x04\xca\x8e\xeai-\xc4QA\xa4\xed\x81\xc4a\
+\xfb\xc12\x85\x9a(w5\xbdP\xed\x1fcf\xb2\x08\xc0\xf1\xaa\xf8\xdb`\xf9\x04\xd4\
+\xbf\xb2\xacs\x9b3\n\x91\x1a\x85\xfea\xb8R\x1aG\xb2K\xd4H\xa6\xd7\x83V\xe5\
+\xb6#v\xba\xe1\x93\xcb\xe0\xd9\x13\xe0Z\x11\x89\x8c*\x1c\xd8\x08 _\x90\xb2\
+\xbfE \xdf\xc3\x1c\xbf\x8b\xc8\xb8\xc15r\xdb\x8eG\xb9\xa3\xb1C\xbd"\xbag\xe0\
+\xfe\x99\xa6\xb2HwR3\xa3\xf5\xb3\x98G(z\xc0|\x87\xb5\x1a\xed\x08\x0eT\xd1\
+\x9a\x0e#k\x10\xceRB\x80\xe43\xff\x8d\x896\x18\xcb\xc6I\x89:\x1e]\'e\xf6\x86\
+\x8f\xd5\x0b\x1e\n\x06Js\x8b5\xa9gF1\x04\t%]*\xbe\x9fUfS\xbc7U:=\x85\x89\x03\
+K\xf1\xba\xf8!\x03\xd0X\xd4\x8b\xef: \x17H3\x07N\xbd$9Z4T\x9eE`\xef\xcb]A\
+\x90*\x99\xd4\xc3\xee4g0\xcf\x84\xc75\xa0>\'\xf6 ?\x1dH}\xa2\xb9\x8d,\x15\
+\x04\x0eL\xe2\xc2\x0b\xf1\xe0u\xd9\x1b\x9e\x15\xc9L\xfb%\xcc\x0e\xb4b$\x94\
+\x91\x81(9\x99w5\xb5AEpD\xbb[\x08`\x19hG{^X\xf1\xb0d @\xd1\x86>IuZ\xa9,\xe5x\
+\xad \x80A\xde"&\xcae\x17\x8b\x84)\xa0w\xfd\xdc@\xbc\xfeo\xbdQ\xb4\x8a\x8e\
+\x16\xd9\x83\x8c\x06\x87\x18\xc7%\xcd\xd0 \x1c.,\t\xaco\x15=\x8f\x06(;\x12b\
+\xa1\xca:]^{\xe0@\xc5\x81V\xb4\x91\xb2\xa5yM!\x93\x84\xaay\xa0\x97\xfb@\xc6\
+\xceP\xef`\xcc\xeaE\xf0\xf4\x0f\x8eCj\xa0\x88\x00v\xcdb\xe2\x17^\x03o\xd8?\n\
+\xec\xf6\n\xac\xd3\xa4sk5\xba\xd4\xb7\xddb\x11\x8bu\xecW\r:0\xd080\x9f:\xf0\
+\xd5\xb3\x07\xa1I\xc9\xa5\xe22)\xbe^\xc9\x01\xa8\xcb\x97-^\xd1\x1cE\xd5#\x05\
+\x10>\xb9\xa1\x14\x91K\x935+\x9f\x85o\xcci9\xb57\x95\xd4\x07E\xe1dl\x11H\xd7\
+\xbd\xea#\x92\xce\x00D\xb6\x14#\xacL\xfc\xbb+s=\x98SU\x1d+\x8d5\xc5\x15\x97P\
+\xd0\x1c\xc3\x1ao#\x1f2\xfa)\xb1)\x0c\x19{\xb71\x1a\xc3\xdaT\x0b\xe5\xe4\x0c\
+\xa6w~\x86\xb9\x00{\xdf\x17\xb24E\xd0\xd9V\xb2kFW\x85]\x9e\xfe\x16\xb2\x19Uv\
+\xcee\x1f\xdd\x9b\xc5A&\xbfN\xb3\xa7\xd5HK\x05v\xc3\x1d(_)\xad\xdeED\x0e\x7f\
+e\xe3\xf0;\x81E\x91\x0e\x07B\x9c\xc3\x9d,\xc8oE\xc7?<\x92\xd7\xbar\xef$\xaf\
+\x07\x8d\xfc5S\x03\xa0\x8dl\xa7\xe7\xea\x141\xb4Ek\x8csj]\x83\x8e\x95R\xfd\
+\x1d\x8d\xb8W\x14\xffQ\x06ZS\x16^\xe3\xd57t\xba9\xfe\x0bU\xeb\x95e\xb8\xc5nz\
+\xfaS\xc1\x91\xaf\x7f\x06\xb8H\xca+\xb38\x85\xb8O\xfc\xa5\xd4>\xcd\x8d\xa1B\
+\xf7\xf8 "\xc2F\xc3\xb0\xcb\xbaO\xab\xaaX\x8f\x03\xe0\x87\x01\xea\xfa\x9f\
+\x8d\xc7\x8cG\xa3\x96\xcf\\\x0b\'PZ\xa4\xfcBT}`\r\x17sH}\':\xb8\x99|\x91\x91\
+1H\x83\xf9\x05\xea\xc2W\x1a=S\t\x83\\nT\x7f9=\r^\x99\x1b\xfbQd\x9c\'\x86\xe9\
+\xb4\xacwP \xa0\x8f\xba\xaa\xc6P\x1eW\xebAMK{\x93U)\x8fbB\xcf\x00\xee\x9e\
+\xd5\xf0\x1e\x98J\x18\x88\xc9V\xdf:\xa2g\x8b\xcb\xa6\xa5v\xf4!0\xad\x0b$n\
+\xd4\x8eb\xa3\xc7\xe5^\x80\xc0:M\x90\xa2x\xc5\xff\xe9\xb5\xf7\xd7\x10vv\x8f\
+\x02\xb9eP\xe0\x83\xc2v\xf4V\xb2c\x17p \xbf\x95\\z\xb8\xa2\xac\xadQN\xe1\xc6\
+\xebk\x85\xd9M\xd9P\xd2v\xba\xb0\xb2\xb7\xe0\xe4\xc9E\xbd\xc2\xe1[\x88\xab\
+\xe30\x00\xa1\x19\xaf\xbb%\x03c%\xb1\xde\xb5\xe9\xc3\x04\xe8o9\x07\xd1\xdd\
+\x9e\xfa\xfb\x92\xb5.X\xab\x94d\x1dI\xcb\x80\xae\xf9\x9a\x8arC\x01\x84\xe4\
+\xc3\xb6z\x8c@\x87XN\r\xe4\x11\xc0viy\x1d\xfd\x87h\x84\x029\x8b@`-\xba\xcf\
+\x98\x05\xe4\xdd\x026\xd0\xab\xa7\x88I\x9ar\t\x80fs\xb2\x94\xcc*\x87\xac\xea\
+I\x94>Z\x8c\xb7xM\xc1#\x93\x93\xf1<\x0c\x04\x0c\x1f0\xeb|,\x18\x95v\x8a3=k)\
+\xd9\x88\xc83w\x05;R\x85\xb5\x14<\xa0\x1bM\x96v\xb7\'\x95\x10\xa6\x00\xb4\
+\x04\x1d\xaeaf\xe9CV>\xa2b\xdfP\xa9\xa8\xc3_\xb2\xc2wzS<\xe5\x98]\xe7\x96\
+\x08\xd5\x1e_\xb5\xa0\rn\x9e\x01\xa0]\x00p\xc1\x81\xd7\xdd\xc9\x9aH~\xbaD\
+\xab\xd0\x03h\x9a\xa0\xd2\xd3h\xc7\x95\x96\x99Xq\x88\x18P\xed\x1b"\x8580EO\
+\xc0S\xa1\xf9u\xd0\x89L:\x01\xa5\xa3\xb5\xa5\xce\xdc\xe2\xa0\xaa\xdd\xb4Z\
+\x08\x05\x0f\xd6BP\xb1\x16\x04\xf0\xac9\x00\x0fh\xd6\x83A\xd0\xad\xcbTU\x99\
+\xe6\x04\xca\xe6AG.\xa8\r \xe2\x96\x9ck\xe5\xd0\x1d\xfb\xe2\x0c\x03B%\x02|\
+\xfc\x9f\x95\xf38O"VsVG^\xf7\xf2\xd5s\x14\xea]\x8a$t\xdc\xb9\xb6\x15\xc0m\
+\x0c\xdb\x16\xc41\xec\xd3\x02\x94[n\x0e\x000Y\x96EEc\xe4\xe7PPn\xd3\xacs\xb0\
+Q\x18\xf8\x94\t\xdb\x03\xa1\xa3*\xf6$\xcc\xf4\x9a-\x83l-\x89\x02\x1el\x15\
+\xf5B\x02x\x0f\x13\xd6\x16\xa9u\x0c\xc9\xf8W\x82\xdc\t!\xb2\xec\x01\x03<\xea\
+;\'\xd9\x13:\xd6"\x10#\xc5\x10\x10z\x89O \xa8\x0eX\xb9\x13P\x83\x03"3\x1a\\\
+\xf7\xbdn\xe4dU\xd4\x98\x11\x7f\xabH\xbd\xd63m}\x00\xd0\x0c3\x95\x019\xfaM\
+\x0es\xd1\xb6\xcf\x17R}\xfb%J\x87\xe0\xa4\xf8\x16J\x02\xd6\x05\xde{\x00\x82\
+\x02E\xfc\xc0\x10P\xea}s\xc9V\xc3Aa\x13\x01y\xea\xad\x8a\xceM>E]\x12\x01\x96\
+\x83\xa8\x0f\x1f\xfd\xa0\xa9\xac\x89\x14aWvn\xa8s\x86\xb1;\x808\xc5_\xa2\xc4\
+\x1f%\xad\x85\x01\x96r\xea\xa9\n\x05\xb0R]`\x06ix\xf8-\xda\\`{\xbe\xd6\xe3\
+\x03\x0c\x01YS\x86\x9d\xa3(\x18\x07\xed\xf1\xb2^\xad6\xa4\x9f\x1d\xf1\x96\
+\xca\x17I\xd4mA\x1f\x9e\xb2\xde*\xc2e\xc70Om\xbe\xb6\x98T#\xfe!lDT\x065{\xfc\
+\xb8Xw\xe1\xe8\xae\xb1\xc1\x83\x8a\x00^P\x1f%\x84\x9a\xfe\xf0\t\xad\xc9\x0c5\
+\x05\x9d\x82\x84\xe2\xa2I\x17\x99\xbe\x08P\xc6\x00\x00\x117p?\x80\xf9\x03\
+\xb6\xf8Wkm@\x17\x7f%q2%R\x1e#\x94\x04\x19\x02D\xbdV\xf5\xdey\xd7\x144\xb6\
+\x08\xc8\x1900@t\x8b\xde\xe80j\x95uQZ\x93\xa7\xcbL.W\x80\xaay\x1f*\'\xf2\x07\
+\xf0\xe0\x8b\xa3\x7fU\xd6\xe7\xb5\x81\xd8\x87,u\x14\xf2D\x08\x02Ls\xac\xec\
+\xd6\xb5<\xb2\xe3ez\xa7\x94\x9c\xddRV\xae\x02<I\xc8{\x0c\x11\x03i\xf3\xc2\
+\xccLL\x80q\x93\xd9\n\xd2\'Z\xf9\xe3\xf1\x99c\x9c\\O\xad\x18X\xa6\x1f*\xab\
+\x8bm\x14re4<x\xc7g\x07J5\x19\x04(\xfe\x81\xa77\xa2Ju\xe2\x9f\xcb\xe2\x8d\
+\x9cI#qW\x8f\r}\xc4\x8b\x89\x00>g\xab[x\xe1\xa9|gM\xc0\xa86\x91\xe6\x93G\xf0\
+\x94N\xc3\xed"\xdc\xd7\xcc\xfd\x1a\xae`{8\x8am\xc7\xb1\x0f\x18\x92\t=\x8a\
+\x07\xc0\x8e\x9eI\x91\xd0,T\x8e\xc8)\x83?\xd5-0\xa1y\x00\xf9\xa96\x14\xa5\
+\xa5\x96\x89\xd7\xb9s\xdf\x158P\xa9\xd6\xc9\xa9\x9c\xc9\xf3)b\xb7{\xa5\x93\
+\xd1\xf1\x11Wr|\xfa\xab\xfd\xc1\xef|\xc3\x05\x1c+\x16h\xe6\x97\x990\x9e\xb0\
+\xe5\x8f\x97 l\xa4\xfa\xb8.\xe0\xde\xcc\x8f\xb6"\x142\xf2,m\xecF\xca\x19\r\
+\xa1 \xc0\xa0\xaa\x08\n\xf7\xb5\x08\xdd\x19Cj\x12h^\x8c\tL>\xd1Ut,\x1d\x89\
+\x98.\x9d\xf3\xa7\xea(\xb2\xed\xb7\x04\xc6R\x18=^\xa9\x8d\xb0`\x1a\x02\x81\
+\x80\xcchsd\'\xc3\x0eC\x99\x7fk\x85G\xee\tk\x08\xf0\xf8\xca\xb4\xd1x<\xc7[u\
+\xb1HXmM\x03\x18\xa6\x9a\x0e=\x9e\xbbC<\xb7\xee\x1a\xa8\xa9\xb6\xd7\x02\xd7\
+\xe3\xa0\xf7\x8ei\x08P~E\xfc\x9cZ\x87__\xc0\xc1p\xe2\x07\x0fhO/C\xd2\xf3\xbe\
+\xa5?\x00\xfc\xd6\xd4\xa2\xe4\x02=\x8bh8\xf6\x96\x93\xc0P\x15b\x98+\xe0\x80\
+\tF\x18\x05\xd5\xb8\xb9XZ\xd6\xc7\xe3\x14\xe5\xcf\x0c\x83\xfa\\Nf\xa5%\x9d\
+\xf0\xc7X\xa78\x80\xbe^\x82\x16z\xcdEm\xef\xcf,P\x13\x9cx\x02\x89\xdd\x98\
+\x10\xc0=D|\x02\xea\x94\x99*\xc4y-M2\xa0\x97&\x976\x022\x85y\x8ajJdq\xe1\xfe\
+\x7f\x0b^\xcc\x02\\\xfc\x80\xb5 \x00\xb2\xd1\xa51c\xfa\x04\xd9E\x80\xccT\xc1\
++\xd7^\x17\xbd\xecS\x97\x99\xb9(J@r\xc1t\xbc\xe7,\xe0TM\r\xc4+`\x08\xc8\x8c\
+\x9e\xb8^\x102E\x95\x00\x15n\xddp\xa1 ~v\xad\xb8\x03f\xc0\x19\x1b\x14\x99\
+\xe0$\xfc1\x89\x86\xf2TR\xce\xd4cQ\xb6\x14\xaa=\xb1\x8b\xa2$\x02X\x15\x06#\
+\xf5\xca\xb6\x1e\x01\xacT\xc6\x90\xaf\x9a\xb1\xc8\x88_+S\x13\x18kx\xb1\x88s\
+\xedZUO\xde"\xefIRe\x9d\x9a\xdf\xe2\x1d\x97%\xedP\xef\x91\x0b\xc9T?.\xc5\x88\
+\xa0\\\xd4\x1b0\x94S\xad\'g\x0c\xb2\x84fi\xf7\xfaL\x18%\x98P\xeb\xf0\xcc\x15\
+\xec>\xaf\x00]\x18\xe6\x8cD\xc3\x19U\xaa\xe8p\x00\x01\x13\xe1\n\xd0d\xb2\xa7\
+V\xc0\x16\x01\xa9Y\x81\x03 Z\xa9\x87\x17\xd5\x96W\x10\xc0lA\r\x99\x8eKiw\xbb\
+\xe4Z}P\xbby\xa4b\x9e\x82VN\x9d\xb53\xea\x16\x06y\x046\x07\xed\xafbU\xbbt\
+\xaf\xfc\t9\xb0j\xa8|\x1d\x9f\x1c\xfd*\x97\n!\x93\xba\xa1$\xbaHcW7\xdd\xea\
+\xaa\xcd\xc9\xce\xe8\xa2\r~\xc8\x03\xb1\xd9~\xc8}\xf1\x82{*D\tH\xaa\xc1\x8a\
+\xdaX\xa5I\x98\x82<Yd\x8b\xd0V\xff\x11\x02\x8e\xf4\x92A\xa8\x8f\xffR\x19z\
+\xcd\xb5\x00\x00\xbb\x84$H\xc6\xbc\x1a[`&\xf3\xde\t\x15\xf7\xc2\x01D7\xd5J\
+\x909\x8b\xd69\x1cI\xaa;T\xc2\xd5:\x11\xa0\xa1\xf33\xaby\xf4NY\xaeaU\n\xc7\
+\xbbG\x90\xc6X\x1dR\xbd\x0ew\xe0P9C\xc2\xf9Ak\xb2f\xf5L\xdf\x99\xbb\x81\x1aD\
+J\xc8\xe4\xdc\xd9\xedn\xa2\x95\x8f\x94\xd9\xf3/v\xf1.[\xd2Y\xf9\xee\xf5\xe2\
+\x96\xbe\xf6s\x98PU\xf1\x8a\x83:\n\x8a\x8de\x83\x04A!#{\x95\xb6\x19\x9bw\xc9\
+\xb4T<\xbf\x8d\x91M\xb8M\xd2v\xf8\xd74\x93%\xe3`\x874\x14\x18\x0e\xcaE+\xd6C\
+\xacr\xf6\xb9Pf\xb6\xd4\xb9\xab\x813B\xb1\x1d\x13\x96\x89\x87\x98\xaa4\x81\
+\xd8{\x9ad\xdcf"E\xaa\xbap\x8eggr\x9d\xa0\x10z\x8fX\x8d\xb0\xc4A\x97\xd7\x9b\
+\x9d._\x00\x89\x93d~\xf5\x85M\xe1\xd1\xc5rK\xe5k\xca=\x1f\x12\x9cJ\xd1v\xc7\
+\x87\x8d9\xa7\x05^\xf4tuL\x9b\xd9TO\x12\xba\xce\xcc\x9f6\x8e+\x05A7T\xbd\x8d\
+\xbd\xa0\x06\xe2\x05{I\x07h\xb2\xc7\x05\xcb\xdf\x8d\xa8^\xb9PY\x02\xda\xa1\
+\xd9#E\xc6\xd2MVQ!\x8d\x97\xe3\x86\xd1)6(\xe3{f\xf7\x1b\x899\xc0e\xa3\xb4\
+\xed\xba\x1d\xc6\xbc\xa1\xc3\xb8\x07\xa9c\x87`\xd9\xaaOw\xd7e.\xfa%\x03c\x85\
+\xc0\x83\xb7\x06^f<\xf5x\xe3&w\x05\xa8\xb9\xf0\xfc\xa0\x8f\xb3\xc83/AS\xa5<\
+\'\x9d\xb2\xf6z\xbb\x8a8TB6v\x19\x07\x82\xb5\x9e\x86\xca\x1a\xe89\xcd\xa6\
+\xe9\x7f\xb5\x91=>\x1a\xd1\x12\xb1<\x1a7\xc4\xa2/\x12\x85a/\xba\x04\x8c\x828\
+\\\xaby\xe4\xd6]*\x9e\xbe\xf3,\x0f\xa0\x9d\'\xe0k#\x061\xb6e\xccx\xa9bB\x7f\
+\xecW2\x94\xecFO]\x1fGK:v\xaczk\xdc\x04\xa8\x08\xa0n\x01\xf9\x02\x9bZ\xb7\
+\xea\xd8\xb1u\xc7\xdcxQ\\C\xab\xb3-\x02jP\xdaVz\x02\x894\xd2u=I*\xca\xe9y\
+\x93C\xf3\xe0\xac\xe2\xcdu\x95P\xe6-\x7f\xde\xa2\xc9W\t\xfb\xbb\x0e2\xe8.o\
+\x85h\x88\x87\xa8.\x1d\x89;c\x83\xbeJq\x16C\xd914\xbc{\xcaMd4\xdb\xb9}5v\xac\
+\x8c;\r6 \xb2\x84<\xfd\xa5\xbce\xfb\xae\xb3\x11T\x19Ty\xa1\x9a\xa4\x11\x04\
+\xa0p\x1eXU\xb2\\\xaf\x10NU\xf3\x8d\x98P\x18\xea\xf5]D\xa9\x06\x18\x0cB\xa6\
+\xa1\x83\xe00\xbe\xb3Cn7\x93\x8b\x02U\xaf\xae\x13\xf72\xfcY\xf7\xd0R\n\x19\
+\x96\xc8e`\xc4i\x19p\xbf\x93Y\x89\xfenl\xc4Lb\x98\x0f#\xd2q\xe6{\x0c\xed\xfe\
+\xe1j\xc0W\x8d\x08J\\9K\x0b\x02_\xe1N\xd3\xe75\x05W=\xb8\xf8\xb1\x17\xcc\xbe\
+#?Z\xb12\xe5\xd0\x08\x9f5(\xe1<\xad\'\xc5$\xfa<\x80\xf7\x0c/*H\xbb\xb8\xa6<\
+\x9a4%\xcemS&\x14\x8d\x84QJ\xd5\x07`\xcd\xe5\xeaj\xa8\x9a\xec\x95v\x7fk\x8dZ\
+\xdd\xe4\x16g.\xb3\xa6\x9ag\xaa\'\x11\x880X\xb4ok\xb8n\xda:(\xb92\x89\xc5R\
+\xf0I\xde>\xfd\xb3V\xac\xaa^\x02\x8e\xeftK\x1e\xfea&Pq\xcd\xd3L\xff4\x82\xfd\
+\xdd*\xec\xa1\x82\xab$\xe1\xc1\xd7\x8f\x8d\xa0\x80\xd0\xffF\x1d\x1a\x02\xe2\
+\xad\xf8\x17d3`E\xa9\x1f*\x0c~\x9f\x90\xda\xe9\xe9\xed\xaa\x04\xaa\x12c\x8d\
+\xa0\n\x1e2b\x1f\xa2\xe6\x96\xbe\xae%Z)\xe5k2\xcf\x10\xe4\x03\xc6\x81B\x93#@\
+\x9ai\x0c\x81X\xbf\x07\x18\xf0R\x1a\x02\x88\xcd\xc5\x84\xb1\x1f\xc3\x06.\xb1\
+qzkW\xf4JV\xd0Z\x19\x11\x10\xe2\xd3w{{\xcd\x9cZ\x8b\xaa\x9cPMU\x8b\xf1\xdf\
+\xd5\x11%\xa5\x80N\xb7cBS\nY\x95\xf9\'\xf9\xd9\x98\x96\xc4p\x85\x1e<\xfeL.\
+\xd5\xa3B\x0b\xf8\xa2\xe1\xe0\xed\xb1\xc5\x1fC\xda\xd4\xa9^\xf6W\x97\x17\xcb\
++\x13m\x86\xbcSP\xf1\xed>\xab"\xec.:\xf0\xc1r;W\xec\xb5h\xd3WAK\xda\xd6\x80\
+\xdf\xe3\r|\xcf\xa7\x88^\\\x04>\x93\rQk\'K\xf7\t\x0f\xe5\x96\x11\x1c(\xdc\
+\xa3J\x82x\xd4w\xc6g\xde\xefX\xe7\xc7\x15\x10\xc0\xa7\x97\x99\xbb\x18\xe7!\
+\xa8\x06\xb6\x84\xcc]K\x1b[\x95\nL\x9a\x02\x91\xcc}\xa7\x04\xbcnv\xe0Y~\xcfo\
+\xbd\x8c\xe2U\x00\xcb9\xa4\x07\xa0B\xc1\x93T\xf62\x9f\xfcK\'z\xb0QB\x80\xe6\
+\xe4\x8a\x19cP\x00\x1ep[\x0c\x15Tb\xcd\x875\xf3l+\xbc\x14?\x83\x18bMY\x8bhx\
+\x11\xaf\xdc\x08\xc2\xd8D\x10%\x81~<h\xa3\xb3\xd6\x0f\x90[#\xf8\x9a\xce\x1e\
+\x95lC\x84J\x04\xc8\xe0\xc4\xadP\xbdA%\xd4\x1c\xd3gxG\xa7?\xa2\x19TIT\x1eL\
+\xf7\x8ca\x0f\xe9\xdd\xc6\x12\xe2X\xb2\x80\xe5\x8e\xd3\xb5\xa1\x1fs\x1a\xfa\
+\xb9\x9f?\xa86F@!\xd3\xb3\xe8\xc1\xc2\x1e\x9b\x83\xfa\xc3\xa9\x8c\xb0]\xa5\
+\xbe\xda.\xb9\xe2\xb94\xbcs\xb1i\x9czv\x8f\xa3\x1c\xbd\x8e\xf4R\x99\xc6\xb4 \
+\xa6Y\xd7\x0c\x01\x07L\xae\xfd\xab\xf6\xd6\xe8j2\x04\x07E\xaa+1(+\n\x16\xde3\
+>cD\x00\x93\xbca\xac\x11I\x1c\x1a\x9a\x98~\x85\xe7BQ\xeb\xc5\x7f\xb4\xa0n\
+\xa7+\xc6\xa1\xfcj_"S\xfd\xb2\xd0\x0c\x91lYz\x9b\xd1\xff\xb8;\xe4H\xcd\x8a\
+\x80\x10\xd7,\xb8\x92\xb7\xab\x95\xc5U\x00\xda^T\xc0\xa1\xd1\x8c\x00\x00aQ\
+\x0c\xa2\xe1\x10\x00e4X\x9fw\xcf\x8b3\xf7\xc8i\xe47\x1bj\xc5\x1fC\x06\x8aUr\
+\xb5rj\x8b\xbf]B\x84\x02^\xad\xc0\x05\x19\x9dD]\xeeN1\xee\x1a\x8d[\r\x08\xa8\
+0\x9e+\xc8\xd9`\xd5\xdbA\x0bv\xce\x0c\xb9w:\x8aP\xe1W\xb745"\xac\x90\xc5m\
+\xdb\xdbUc\x12\xdf\xf4\xb10Q\x0c.L51B\xba\xeb\xba\xa5j#[\x83\xd8!\x933\xd23\
+\xc4sL\xa3\x9f\xbc-\xb6-U\x83\x80\x15\x07!\xb0p\xcb?\x8fU\xab\x00\xb4\xb9_Jw\
+\x00\xfa\xa1\xbdb\xcf\xcb\xceP8\xc2\x838+N\xb9\x80\'\xc83\x97\xb4r\xa0!M\xe0\
+\xb0\x9c\x01\xe3\xd5q\x8d\xafM\x0b\xfb"\xa0\xbf2\xa0\xb3W\xad\x02\xeb\xb8H\
+\xcd"`\x1a^\x8e\xd1\xf2\x00\xe9\x90h\xdd^\x1a\x1a\xab\x99v\xaa\\\xa5H\xa7\
+\xa1\xa8\x18\x02\x0eJ\x93\xd18\xe4];;0\x96\x8dY\xdfH\x0c\x80\x85\x80\xca \
+\xfag\x88\xaf\xbaT\xd4\xa2\xe7\'\xd1>E8\xd1dH\xc7\xc1\xac\xa7G\x04aR\xde8\
+\x9f>TPa\xce\xa2\x11\x0e\x8c\x10\xf4Z\x9b\xa5\tb&.\t\xd7\x8f4N\x995\x0e=\xeb\
+\xab\x1e\'r\x8eWx\xe07\xbf\xe1\xc2\x0c\x81\xac\x96A\xa4\xeaQ9f\xaf\xbdg\x1e\
+\x9aQ\x9d\xd3\xae\r~\x05o]\xd0\xc6}F\x17xb\xdf\xe2\x87\xb0\x98\xbc\xd9\x1fS\
+\xfc`!\xa0zCHozd\xc7X\xfc\xa7\x96\'ZD\x873T\x9cX\xc0d\xf5%\xb3Z\x86\xf9p\xef\
+n)\x96t\xfa\xb6\xa4\x88\x8a\xc2\xc4)\xdbC\x85`\xdf\xa0{\x99%\x1fs\x80\xfbGA\
+\xc0j\xa85YeE6\r\x82\xe0\xe5\xb5\xfa\xf6\x90"&\x0c\x1b,\xb5?[\xbd\x11\xea?3p\
+\xa9<=\xac\\\xa05}\xa7\xe6I\xadhD\x16\xa1\x00\xc0\xd4\x01\x1d1\xebY\xcfze@\
+\x03\xfb\xee\xc7\x10\x0bv\xacH\xc4\x84\x89\x83d!\xf9\xa8c\xfe@\xcc\xc7\xa6\
+\xec\xa9\x80\x16)\x9a\xa0!\x00\x88\x96V\xd4\x00\xf4\xec\xd2B\xce\x84re\x03\t\
+\xa8\xf8\xd3m\xe8\x15\xccs\x81\x0f\xe4\x82\xe6\xf3*j\x9fw\x90\xee\r\x0cw\x8c\
+\xdd\n\xd6\xe6\x0c\x00`?\xcb\xc7);\xb0\xd6e\x11\x80\xd4\x85\xc9q\x9b[\xe5C\
+\xb1\x05\\&\xc2p\x00J7\xcdN\xc3(K1pK\xa6\xe6\xc4\x15\xc6\x11\x9c\xa6\xefF\
+\xed\x8c\x17\xf0x\xc6o\xd1_\xc9\xce\x83\xeaI\xc4\xfbP\xb5\xaa\x1b\x01\xa1\
+\x00=UH,\xbf2\x0b\xb0\xfc\x00\x8bAZ\x95\xb6\x8f!\xafpL\x9f\xce\x89\x00j\x82\
+\xae\xd7\x86\xbeU\x1b\xb3\xc4\xf4\xacG\x0c\x99W\x02\x10RQ\x07\x10\x11\xe5\
+\x10\xb0\xf2\xa1_X\x1f\x04\x88X\xc0r\rf\x80\xd8\x06\xcen\x01\x01\x19\xceu\
+\xdf\xc1TM\xd4\x99\xe0\xbcvM\x00\x92IU&\xed\xec \xb7\x01H\xc7}f!V\xdd\xda\
+\xa9\xc5A\x85P3\x93:\xc6\xaa`"K_\x95\xa53$\x92\xa0\xe0\x15\rkk\xd3\x9a\xaa\
+\xf5t"mg\xd9\xab\xea1\xa1\x17\x0f\xbb\x88\xded]u\xbf9\xeda\x8f\x8c\xaa/\x94\
+\xcb\xc0\xa6\xa4]\xf9\x80\xe4\x8b|\xd9\x0e\xd4U\xad\xaav\x95\xf9\x81\xc5^\
+\x8d\x80\x94\x01\xaau\xa8O\xd1\xa4\x84\xb5\xd7\xec-K\xccb\x9b\xbcbd\xbb\x82l\
+\x8b\xb6\xbd\x1d\x0e\x01\xe6\x0b\xe5\xc4x"\xea\x87Y\xdfb-\x80\x8f\xce\xf1BeH\
+X\xd5(\xb6\x9a\xb0W\xe4\xaa2\'\x00P\xb7\xaf\xaf\x1d\xcc\xe4OR\xeb\xd6\t\xe4\
+\x1cP]\x0fV\xbf\x1b\xd1\x17\x04\x99\x17\x89\xa9:_\xb1V\xf5]\x11|\xb8^:4c>\
+\xf4$\n\x1d\xae\xc2\xcd5\xc1\n(k~\xbd\x82@m~\xc0n\xa8\x90\x08[M\xdf\xadz5\
+\x8f\x9c\x11X\xc8\xa8\xf9P\x9c\xb6*E\xceRJ\xcfGr\x99|8f\xdc\x8c"\xd6,K\x91zv\
+\x10{\x96"T\xb2\x92A2\xa1S\x1fv\xe1\x853\x92\xaai\xaf\xd4\x15CE\x07\x98}&s\
+\x86\x98\xdc\x87dk\x1a\x83\xc5\x8en\xf5\x0e\x94\x92rZl\xb16zo\xd8J~\xac/KL\
+\xf0\xe1\x95\xffq\xe6h\xe8\r\xcep\xc8\xe9`\xe66\x80\x00$\xdd;(\x06\x99\xa7\
+\xe4\xc3@\x1eC}\x82O\x17e[\x90f1\xac+\xfb\xf3`yy\xc5=A^s\xce\x80\xe8 \xcb\
+\x92\xaf\n\xba\xbeQ \xe0\x00;\x86\xe6\xf4\xaf\xc8\x8d\xac\xc0\xa0P2= \x19\
+\x88\x8d\xb1w_\x94~l\x8fi\xcb\xc7zr\x16=\xe9J\xe6\x87\xea\x06\xbf\xa6W\xb4\
+\xe1\x07Y\x14\xadL\xf2DcJj.p\xe3\x89\xb5v\xc1\'p\xf6\n]\xaa\x81-c\xcb\xf3\
+\x80\xc3\xcd\x13\xf5\xe3!\xec\x87 \xa0<\x11\xf7\xc6\xf2@\xa6\xdf\xf5W(\xea\
+\x9a\x9fs\xd7\x05~\xfa(\xa0\xcb\x1b\xca\x9d\xd0\x969zvg\x154\xd5\xd0\xad\x13\
+\x14\xbf\xf6nY\xfd:\xde\x08\x85\x8a\x03B(\r\xa9\x02\xad8\xad\xa3\x0bN\x89c\
+\xdc\x88\xfb\xb8\x17@\x0c\x08\xd7J\x8es\xc5\x88E\xad\x1a\x01\x10\xa1\x85k\
+\xf4\xda\xb5\x98\x08\xe0\xcd$\x92h@Y\xaf#\xb6\xcf\x0e\xa5\xd7\xd5\x90\x0e\
+\x0f:\xb6>\xe4\x11\x10s)\x02N}\x9d\x95f\r\xf4\xb3\x9dC2\xb5\xe9\x19?"\x83\
+\x00\xb5F\xdf>Hff\x90f[\xe6\xeb\x0c\x9c\x99\xa7\x82!\x87\x8c\x1dH@\xc4C%i\
+\xd2_\xb0\xdc\x16\x14ll\x98\x04\xd5\x99a\x14\xbc\xde)\xa5n\x93%\x84\xa9\xe4\
+\xc9Z\x91"=\xab\x8c\xe8\x06\xdaa\x98\x15N\n\xcb\x19+3\x8c\x02\x9aAe\x88\x19\
+\x19\xc4\x90\x13\x17m\x08S\xc4\xb8\x1c\xf9n\x1e\x17F\x0b\x00htz\x94D\x10@\
+\xab\xc7\x93d\xa6!\x98\xea\xd5\xfbD\xed,\xfd\xa6\xa2!\x84/\x1ck\x1cG5\x17f\
+\xa5\xcc\x8d\xaa\xb8NR\x99\xa1\xc1\xe9\x83UD\xa5\xbd\x83\xc8\x8b\xa4\xcd\xbf\
+"\x88\x96\x86\x92[0\x14b\x12\x02\xb0\\\xb3gX2\xdd\xd2\x0e\xb96\x88\x81\x97\
+\xd3\x07J\rlh{6\xf6Pf\xee\xfb\xa3R\xb5u\xac(\x93\x16:$\x06@Zz\xaa\xcbA}\x02Q\
+\xa1\xfe5\xf9\xd6\xd6A\xb7\x8fPt\x0f-\x13\x83\x11\x10q\xc0\xb90\xac\xa0fT\
+\x01o\xa5d\x07\x9e\x19{\xe3[\xc0\x00\x00\x0c\x15IDAT\x14\xa0DkL\x8a"\xd5]\
+\xf1a\xf2DV\x98\xf6>fU\xc4\xa83\n\xc5\xfc\xab\x1eA\xc2Hc\xad\xff\x17\xbe&\
+\x8f\xde-\xd8\xefAc\xe9\x07P\x14\xf0V\x89\xcc\xa1\xebu\xea"\xc61c\xa0\xfb\'\
+\xc2\xc2\xa2^UJ\xd8\xef\x04\xf4h:\x9eg\xb0\xe7\x08Wq\xa4\xbe/\x1b\xdf;\xd4~\
+\xc7\x93b\xe6\xc6w\xdf}\xe5<\xf5:\xb8QXB\xd0\x08R\xb5\x82}\x82\x95\x06\xaa\
+\xfb\x88K`;\x9cI\x8aD\x118\x89\n\x80\xf4\xb6eY\xa7\xc1\x8f\xec\xdd\x1c\xc3r\
+\xb2hU\x81\x8a\xb2Q\x8e~\x1cPH:\xc7r\xb6\x07M\x153\xa0U)Y\xd0_\xa9^\xf9\x18\
+\x82\xb6yY\n\x0e\xbaM\xc1\x0e\xe1N\xefP6N\xb0\xc3\x81\xb3%\xaa\xa8\x1c\xf7#\
+\x0c\xc6\x1d\xef[&\x02:\xe65kh\xc4\x0c\xfd\xf6".\t]\x17q\x04\x80\x83fEg\xf6D\
+P\'\x90\xbaM\x11\xaci2ey\xe2\x8b\x1d\x1c~9\x147\xe3I\xf5S\xef\x90QF\n6\x9fQ\
+\xe5\xb6}\xb4\xc4;>\xf8(\xfd\xee\xca$(\xc0\x08\xc2\xbat\xf1\xec3\x92\xfc\xbb\
+s\x06\x93\xdaeR\x19\xa1\x9c\xb2XD\xce\xf2Z\xb6>\x9c\xdfV\x8f\x9d\x19K\xf4v\
+\xaf8\xd4*\x17\xbaA\xa3\x81\xce\x16\xeb8\xa0B\xd4\x9d\x0c1\xcc\xd9\x1a&V\xcf\
+\xea5\xf8\xce\xdc\xfa\xf4R!\xde\xd8D\x9f\xfe(\xf9H\xfb\xa4\xad\x8f\xa0 *=\
+\xf6\x98|\xd1n\x0e\x11Q\xf88S\\6VY\xacz\x8e\x07j\x1c\x88\xba\xd3c]\x88m\xb6r\
+\x06\xd9c\xaf-3\x8c\x89\x88\xf3\xd9B:\xd3\xe8\x8a\x1a\xe9\xc4\x8fM8\x9f-\x17\
+-\x82\xc1(\xeeN\xd5\xb1\x0f\xc8\xa7\xb6_\xc9\x07,\x01\x10\x02R\xa6&s\x80\x93\
+=\xc7#>\xa8k\x00\x84\x15\xe0{\x10<7\xe6Rr\xd0\xd3>\xe6\xbb]\x05\x8c\xeeR\xa8\
+\xf3>\xee\x0c\xb6J \xcd\x8e\xb0"\xaf\xd2\x03\x19\xfeQ\x0c\x97\xb4A\xcb\xb2Kx\
+\xdcG\xe0\xe0(\xd7(\x13\xb3\x04\xe8\xd2\xa7t\xf2\x83\xbab\xf9\x99\xd4\xab\
+\xb8\xc5F)\xde\xb3\xdc\xeb\xf7B\x02\xd4\xe8\xcb\x1b\x1c\x81\xab\x00\xdc\xb9\
+\xb4{\x10e\x82\x03\x85\xe2\xbb\x14\x14\xd9p$\x90\xcf\xba\xb02Q#h\x87JP[\x87\
+\xd942\x82\xf2\xe0\x876\x08\x15N\xf4R\xa9\x83\xf9\xb8\x96c\x9c\x8cl\xbbV]fQU\
+\xa6\xaf\xb7abx\xb5Di\x82\xa8\xa8\xd8\x95\x02j\x9e}ATP(\x90\x14\x9e\xa0\x8b\
+\xff\xb5A{q\x95\xd2V\x9bc\xd4i\x99NP\x0e\x8cD\x0b,\xaag\xf7\xea\xde\xb1p\xbc\
+\xb0\x1d\xe6\x08H`&ex\x11\xc6\x86\xe3\xf2\xf0qc\xc0\xe3>B8\x10]\xd6\xcf\x18\
+\xb0\x92\x12\x07\x88\x9d\x1c\xa8T9\xe0\xb7<3\xfd\xd0\xf0>\x10mL\x8c\x94\x9a"\
+\x9d\x04e\xf4\x83\x10\xbfJV\x82&\xe54\x9d;\xcf3\xb2<8\xda\x83`\x84\xc5\xf8\
+\xe0\xd3\xa0%\x81\x96-G\xfb\x084\xb3i\x18\xa4\xda\xc1U\x0c:\r\xc7\xee\xd0\
+\xd0\x07rA\xf4\x83|*\x99\x06\x8au;Wg5$P4A2\x13\xaeJ\x1a\x8c\x85\xa4t2\x81\
+\x11\xe0\xc8\xb0P\xa2\x1b\x8clh\xf9\xc5{eu\xdf\x1d\xac\xae\xa2\xe8\x0bi&\x19\
+/\tc2Z\x01n\x0cp\xa4\\\'pQ\xee1"\xe0\xc0\xe2\x8d-\x0f\xa0\xc34\x8atU\xcfN\
+\xe2\x81l\x13\xe0D2\x80\xb9\xcaW%\x9c\'\x113(2m5\x9b\xfe\xe2k>\x9e\x89b Uu\
+\xd4\xb1\xc0\x94\xedf4\x0f\xb2\xd0E\xaa\x0c\xbd\xec@\x9d\xc8hKB"!\xa67\x9e\
+\x14\xb5\x9b\x08Z>\xae\xe4\r\xb8p5\x0e\x19m\x9e=Z\xa2\x8fb!}=ID*-\x93\xa1\
+\x1b\xa4\xdd\xcaX\x8a\xe1\xac[\xabFP\xf4\xab\xf1\xc8\xb4\xd8\x91j\xdeJ\x03>\
+\'\xacXL"\x87J\xa4\xfd\xa4\x8b!\x90\xe2\x18-\x8f.\x94x&\x07s\xb1\x00\x99\x00\
+/\xa7y\x98\x92\x94l\r\xd2rJ\xa2&`\xaf\x87\xdb\x84\x96w\xd9x\x87obp\xca\x15\
+\x8e\xa2*^h\x0e>JqYE\x0bpC\xc0\xa8\x15\x9e;\x88\xa6A\x81\x80UPH\x98\rL\xa9\
+\xfc\x81*\x00\x94b\x0c\xea$D\x97\xb2U\x99\x03\xa5\xf2Z7MZ\x04t~\x10\xb9}\xbc\
+\x189f\xc8\xc4\xc9\'T\xe2\x96\xe4\x97V\x84\x05\xe2\tRe\xa0\x16\xf3\xd4\'\xb0\
+\xbbD\x1aC\xe0\x10\x10\x93)\xb5\xb4FHx\x7fd \xf3_ \xd2\x15D\x15\x13R\x1c\xf6\
+R\x08^\xbc\xf7\x90@\x97\x9d\x9e\x88\xd1E\x01\xa9z\xdb\xba\xf3l\xe4J\xe3\xae\
+\x95\xe4\x08P\x14\x00f<\xc6\xea\xa7\x8d\xb1s\xa0\x06\n\x015WY\xd7\xea\xa7\
+\x89\x14\xc9\xdbF \x95\xf28o*j\x97\x18>\xa8\xeb\x83\xe8C"\xaa\xa8\x14\x9b\
+\xac\xe1\xc6+I%\x04\x08z\x08\n\\S\xa9\x01\x81\xa0V-\xf2\xa0\xd9\'\x13<\xea\
+\xb0\x94\xe3\xd5P\xed\xd6\xa8\xee-\xee\xa44\xe8\x01\x18f\xaf\xec\xf2r\xdf\
+\x84?\x00\x07\xb8[\x98\x03Q%\x1a\x12\x8a9\x85\x15\xe8\x11\x02\x08\x8aEe\xcb8\
+\xec\xa5\xfa\x88^\xafT\xaf\x92\xb8\xa2\xc7U\x15\xa1J{\xf8C\x11\xe8x\x9d\x8a\
+\x00\x1b\xce!\x85\xc0\x08-P\x9b]\xea\xd1x\x8d\xbfX\x05\xe0\xa0\x9a\x199\xe7\
+\xc3|TX&\x9a\xa9Qs+-\x8fv?\xd7\xdd=E\xd1\xf6\xba\x82\x80\xf2\xc8\x16\xa9\x1c\
+\x92\xa9Sk\x98j\x97\x05\x95\xa5.\x0f \xdd7\xad,\xf1\rK\x12P\xb2HF\x99\xa5\
+\x17\x81\xce\x1b\x12\x0bj9\xbd\xc6.4\xf1\xa9\x08s\x08\xf0\xc2\xa8\xbb\x9d\
+\x8c\xd7\xcb\xc8\xc0\x16\x83\xd8\x80/\xebv=\x87ZF\x01\x82\x12\x95Q&5\x0b\xe6\
+\xcc\xb2\xe8n>-\x94\xf3\x0cK\xe3\x19#\x80\x1a\x9a\x1a\x83\xe0\xc8\x8f\xf5\
+\x18\x9a\xe5\x80\xe3\x1b\x1dg\xbaW\x84N\x01QjX\x05\xd7Zu\xcePnN\x92\xaeB\xb7\
+\xc5Z\xe2\xd0\xaf\xf8\x1c\xe2\x8aB\xa5{\xa6\x171\xd3\xda?\xcc\x04\x0cQ\x15t\
+\xe6\xa0\xaa\xf95\x03]q\x81\xa9^\x8eB\x10\xeb\x8a+p\xe5\xb1%0\x8a\xd3\x8d\
+\x93\xf8*x\xa4\xc6usP\x19\xd8\xf9]\xaa\x95-\xe7>3\xca\xeb4\x84\xe1\x04ht\xf3\
+\x9578\x9ay\xe8\xa6\x8d\x8a\xd9*\xc0\x90\t\xfa\x14\xa1,\x08\x1d\xb6\x8a~\xa0\
+\x16\xcd\x11@v\xff\xafJ\xc8\xd0\xe9\xe4\x84\xc4\xc4\x9d\x17J\xa8h\x12\xa8\
+\x1bX\xd5\x82\xa2ai\xd0u\xb4\xc6`+\xc7|\xad\x19\\h\xf6\x9b\xba\xe3\xb5z\x80\
+\xe8\x14[\x91\xd1\xce\xd6\xfc\xb0Z\x1dH\xa8\x1a\xcf\xea\xd8\xbcv\x7f\x8d\x81\
+Jt\'O\xd2\xa5\xa7\xc5Z\xda\x86\xbb\x8f!x\x92\xdat\x97\xe5\x97\x06(\xb4\xab\
+\x83p\xf7\x15[\xa0\xdf\x94Ig\xee\xab<\xa4~\xd5U\xbe\x9e3[#\xb9\x13\xe8LKN\
+\xa6\x0e\xd7V\x02\xed}"\r\x1b4\xa6q\xc5\x17D\xe7\xf4\x97\x9a\xdb|\xe5k~\xc6g\
+Eqc\x9fW\x001P\x86\xed\xcc\x88|\xda\x1c\xca\xd1V\x1e\xd9\x13\x15\x8a\xca\x83\
+J\xd0\xbc\xa6\x14!l\x87\xca\xaf\x93?VV\xa3.6\xc1\xd3|\x02\x9c\x91F\xb0\x035\
+\xb8:\xaf\\\x86\x8c\x9d\x9d\xf1\xe9B\xad\x08Ylsv\xc2j\x1dN\xa7\xc7\xd2]\x05\
+\x1eh7\x91\xd3\x82+\x19\xf7e\x1f\xad\xc6\x9d\xcf\x8e\xf7l]Z\x0e\x1f\xb4M\xaa\
+8\x9c6^Y\x7fb\x9a\x9a~\xf6\x04\n)\xdb\xca\xa6e*\xbf\xd9\xbeHr/\x11\x8e\xa1\
+\x12\x10\x8a\xeeM\x1a#\xa9\x81\xe0\xce\x13.\xeba\xd0\x13\x01\xaa\x8b\xa6\xe8\
+$\xfbu5\xc2\xbb\xa6\xd4E\xfd\x86q&2O\xbe\'V\x07\xdcY\xb0\xcd\xbc*\xf3\x95\
+\xcf\xef\xa9\xab\xd9\xc5\x88\xeaT\x04\x98\x93C\x87#\x91\x9aO\xe0\xe0\x9e\xab\
+\x81\x01\xcb\xd9\xcb\xe6\xe8\xe8\xc7\x11O\xe0 4\x0b\x01AP\x07\xc8\xf3V\xea\
+\xcawz\xd56A\x7f\xb9\xa9\xfa%1\xb6R\x8d\xd2\xd98:&\xad/\xcb\x91\xe99\xa2\x12\
+\x11\xe0$\x02\xd8YaV\xdc\x81)\xca\x9cf\xab\xdd-\xc2H\x90\xb6%.\x1b\xc7\x07=\
+\xdb&k\x03\xa1\xe8w+;\x84\xd9S\xa6\xe6s\xe2\xc5\x80\n"?B\xcf\xa7s\xac\xe7\
+\xcf\xca\x01\xaf&i=\x99~\xa9\xe3\x06\x0c\nH\xa53\xff\x8c]\xe7\xbd\xa1\xb2\ra\
+\x81\xe9\x04l\x9a\xb4\x83\xa6\xdd\xff\x80]\xbc\xbcc\xcf\xf2\x19/\x1b\xc0\xf3\
+\xc4\xcc:D\x1a|\t\x92\x15X1r\xa5\x8eV\x95\xa8\xc4\x89\x842\xe2\xccR\xfe)\xc6\
+\xb1D\xe5\xb8\x1f\xad\xd0I\x82]\x84w\xa5fW#@\x94#%\xa5O\xa0\xae\xf3;v\xc8\
+\x0c\xdb3\xd6k\xf8\x9c \x08|\xd8l\xf5H\x0f(M?J\t\x9c\xbd\xe4re\' \x89\x9c.o\
+\x8e\x06\xb2Dm#@\xe8f\x11W<\xa0\xda+Q\xb5a\x00\xe0=\x03\x01\x1fz\x8e\xa6\x81\
+\n\x05B\x11\xc8\xee\xa0\xb0\x10\xa9\xcfQU\xc8\x05\x91\xd6\x03W\x0e\tW\xb1I\
+\x1et\xc3\x9dJ\xca\xf1*jh\xcb[\xde\x13\xf0*2\xe3\x9b$E\x85\xab\xe8n\xfd\xf8\
+\xa0\xa2)\x86\x08\x9a\x86"\xe4\xd9\x19\x97&f\xf3\x86$\x12A.\xb4\x10\xa4\xe7\
+\xcf\x1c\x83\x84)|\xadT\x1290\xa5J\xcc\x94O\x11\xb2\x84.v\xda\xcf\x9e6\xac\
+\xa8\x8d\x1c\xf6\xaer\xe2\x87\x08\x01\xe7\x92Hbm\xd4a\x10l\x11\xa6K\xba\n@\
+\x11Z\x0f\xf8x\xf0x\xb1\xa8\xf5\x1b\x90\xa8\xf5\xaaB\xb7\x07\x1a@\x10\x10~\
+\x19\n\xe8\x05dd\xacA\xcd\xb1R\xd2\x168\x00\x0fj\x11\xdc>\xa9\x90k\x9dS\xa9\
+\xe1u\x04pd\xb4\x91d]@\\g\x14@\x84\x89\xa6\xc5y!}t1\xc5\x87\x94A\x8cn\x94\
+\x1dT\x07\xe6\xc6\xaa+J\x92o\x13\xe8~\x96l\x93\x96\xcd2l\x02\n\xc9\x8f4\x86\
+\x80y\xd6\x82\x83G\x80\xc8\x00\x16\x99\x04\x86$z\xaa\xc4\xe1b!Sj\xa7`HA\x82a\
+\xc4 \xf8\xa8\xa6\xc7\xe51\x01\x87#\xbd$\x05\rn\x90\xf7\xc1.\xb4\xfa,\xe7\
+\xc6c"\xba%\xa7\x0cJ7\\!$\xafD\x11[\x83\xe8f\x84\x8d5\xd1\x01Y\xbaNgW\xba~\
+\x96\x9f\x88\x06\xb1\x03\x07\xaes\xf8K\x8f2J\x05\x91\xd5\x85<\x9aR\x11A4\x95\
+.\x16!\xd2\xe4\x15Q\x8ctY\x7fV/\x07E|Tj\x0ca)"?\x02\x07L(\x00b\xe3\xa7\xe4\
+\x02\x18\xd1\x1cR|=X1\xf5!K\x04\x88\xbaiG\xc8:\x11C\xae\xc2\x86\x87\x1798A\
+\xdc[\xd2Qw\x000\x90]#\x16;bC\x86\r\xc4\xa8\x85\x8e\xf1\xaa\x17)\x9aP"\x178v\
+@\xfew\xed\x1f)\x1eW\xd3\x10>\xa0\x08\xf3R\xe5\x87\x0eC\x08p\xe0R\x0f*\x15\
+\xca\x05\x85T\x97\x03\x14g\xf1\xcb\x96-\x9d\xf8G\x82\xa7\xffP\xbc\x87\xce14\
+\x1d\x00\'\xdeT"\x8c\xb6\x07\xecQ\x86HJ\x11\xeb*\xc2\x1e\x9a\xa19\xec\xa4\
+\xa3|\xba\xe2q\xc0\x96\x08\x98>\xe0\xdd\x80q#t\t\xcfH~\x83\xed\x15\xc8Cj\x00\
+\xff"\xa0\x98\x11\x88oQ\xb34\x02*\x06<$\xb1\xc9\xc6h\x9d\xacV\n\x00^\xae\x18\
+\x82\xeaw\x06\x12T\xfdx\xfc\x8b\xb3\x87\xf3{\xc2\x8e\xd8\x81\xe2\xa0r\xb4\
+\xe6\xe5H\xa2\xb8\x02G\xbed\xfc#\x8b\x89\t\xbe\x10\x91\xe6\xd9\x1b\x8a<\xc7T\
+\xd2\x1f*\xd2l\x1aNn\x84\xd3re\xcf\x8c/\x16i\x8cz^\xd2\xa3?\x89\x9a\xf3\xce\
+\xb7\xef;V\xd7}Q\xb5\x96\x11H%\xd9d^c\xab>\x16:Ee\xf6\'kM\x1b\x97\x8cd\xf6\
+\xa8\x8f#\x17\xba\xfc\x94\xed\x02\x95\x0bd!\xcaA\x19\x8eJ\r\xb4\x9b\xbd\x0e\
+\x81t\xb2\x08\xef\x8e\xd0!\xcc\xd6\x91\xda\x15\x93tV\x17\xe5\xed\xbc\xdf\xf4\
+\xda\xd9\xa4\xb4\xb3\xad\x01\x02\xa7\x9a.\xa0\xe5\xfb\x14\xf6\xa2\xff\x08\
+\xb7m,~\xbd\x0f\xfe\xb5\xc4L\xa2y\x96\x92\x02$%\xc4\xda\x1e\xb6\xb36\tZ\xbf\
+\xf7\x1cl\xaa\xe3\x95\x9cD\xfc,"\xc1\x01 \xf1&\xab\xdf\x89,\xec/\x00\xea\xbb\
+\x00\x91\x16\x07a\x11\x0f\x8f0\xad\xf7W\x14\x88\xa2a\xb3y\xac\x8a\xaap\xe5\
+\x80v\xa8\xb6\xae\x0bRl\x00\xd8\xb5.\xc8\xdcb\x85\x98\x81"\x8d\x1e\x81M\xf2\
+\x91\xc6\xc0]\xdf\xe0Z\x83\\\t\x14\x10E\x04\x05d\xf2\xb9&\xedf\xc3\xcey\x00\
+\xe7=\xd1\x05H\xf8DMd\x01\xa1&&\x08\x02v)\xe5\xe0t\xbc\x0ce\x83\xe7\x89\x8d\
+\xc0\xf1\xe6:\x83\xe2%h0Q\x03&\xee\xd2E\xd0B\xf1\xd1\x988\x02\x8d\xf2)("\xd7\
+^\xc8 \x86\xc5\xff\x075\x19\xcc\xa5\xaa\xbc\x13V\x00\x00\x00\x00IEND\xaeB`\
+\x82' 
+
+def getWizardBackgroundBitmap():
+    return BitmapFromImage(getWizardBackgroundImage())
+
+def getWizardBackgroundImage():
+    stream = cStringIO.StringIO(getWizardBackgroundData())
+    return ImageFromStream(stream)
+
+def getWizardBackgroundIcon():
+    icon = EmptyIcon()
+    icon.CopyFromBitmap(getWizardBackgroundBitmap())
+    return icon
+
+#----------------------------------------------------------------------
+def getWizardBGShorterData():
+    return \
+'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\xac\x00\x00\x01,\x08\x02\
+\x00\x00\x00\xd1\x94\xcf\xe3\x00\x00\x00\x03sBIT\x08\x08\x08\xdb\xe1O\xe0\
+\x00\x00 \x00IDATx\x9c\xc5\xbdy\x94^\xc7u\x1fx\xeb\xeb\xaf\xbb\x81Fc!\x16\
+\x02 \x01\x82 \xb8\x80\xbbH\x8a\xb4D\xca#\xd9\xa4\xe28\x92e\xc99\x91dO"\xebx\
+\x19\xcfd2\xb62\xc9x\xc6>>c\x9f\xcc\x8cg|&q\xe2\x938\xb6c;q\xecq$9\xe3\xe5\
+\xd8\x96e\xd9\x14\xe9\x13\x9b\xabDR$\x05\x92 \t\x80\x00\x01\x02\x8d\xbd\x81\
+\x06\x1a\xbd|]\xf3\xc7{Uu\xd7\xaaz_75E\xb0\xbf\xf7j\xb9u\xab\xee\xaf\xee\xbd\
+\xb5\xbc\xf7\xdc{\xbf\xf7\'A\x0b\x1e|\xba\xf6\xbe\x89\xe21"g\x13\x1c\xb8\xf6\
+\xc29\x9c\x9f\xe7\xf4\xbc \xa5\x12\x88\x04jF.#5Fg+\xc1\xec%\x0e\xf3\x8c!\xde\
+Pm\x8ep\x82/\x9d\x03\xd9\n\xbbM,\xa7\xec\xde\\\xf0\xa4\x88/6\x04\x00\x00z\
+\x16C\x99\xae\xd7\x11\xe0}\xfb\xaf\x92\xef:\xfe\xbe5\xc11\xc0Y\xc0\n\xb9\xd5\
+h\xb5\xc7\xbb"\x80\xe4q\x89%\x07\xce\xfa\x97\xa1\xec\x9cS\x07\t\x8b\xef\xb7\
+\xc5\x9c9bX\xdb\xf8\xa0\t\xb1\xe4\xda9\x0f\xde\x81\xd3\x91\xd8E\xfc\xcbU\x03\
++\x1bhu\x927\xef}\xcb\x92O<\x90l\x82\xb1&\xb5\xe9L\x87\xca\xa4\x0b_\xe8\x04\
+\x85M\x94\xdf\x83\xb7z)\xc6\xf7e\x1agH\x0bD\x01(\xc9\t\x07J\x91\x9a`\xe07\
+\x907HY,\xdb\x10\xaf\xe2\xa1\xa9N\x1d\xd3\x1d\xa8\xe1K\xc7:9\x07\x14\xab\xc2\
+\xba\x16a\x90Y!\x80@\xe4\x11\xf6\x1b\x00\xc0{_+\xcb\x80\x03%\xbe:\xe0\xae\
+\xc1p6\x15\x80A\xc5\n\x12L\x89ab\xe0\xab\xbc\x93J\x1e\xa2z/\xe6\x14)a\x0c4L\
+\x06d\xd7\x88Y\xad.\x16Q4\x01\xc9\x87\xcd\x7fD\x80!H\xcf:\x10\r\xa0L\xa9\x94\
+\x87\x96\x1f\xa6\xc7}\xae\x13y\xde\n8\xe2~\xaf\xe1\'\xa1\xd3\x14\xb1K\xa9\
+\x9e$XU[D<xE\xc3y\x9e\x95\xc7\xa3\x98\xa8\x90\xfa)-\x8a \xa4\xa5n\xc2\xa4\
+\xe5\xe8\xd1F{\x1aH]\x1d\xc0N\xa3\\\x86.8@\x854\x9f\x0e\xeb\xa1N\x08\xb0c*\
+\x83\xe9\xeey\x9e\xc7;\x0f\xdeP\x03\xce\xb8\x8e1\xc1\xdb05\x01C@\xab\x06*\
+\x10\x10\xe3Y\x0f\xc8\x9c\xbc\x8b\x9c\xcb \x06\x0f\xdcB\xe7J\x1c\xa8]\xe4\
+\x9c>q\xd5r\xaal\xd4\xf0\xa3 )?\xf3P\x9c\x83F/6ND`V\xea\x12J\xa5\xcah\x87\
+\xaaL\x9f\x00\x07\xd5\x15\xe84\xc2\x0b\x99#dl\xeb\xdbs0\xd2[Z\x18\xf4:\xd4\
+\xda\x12\xef\\\x023\xc0\x04\xdc\xdc\x16L\xc9\x8a\xccM\x1c\xb0a\x12\x86.B\x81\
+\xf3\xce\xbb\xbbo\xdb\xf5\x99O|\x08\x00\xfe\xf4\xb1\xaf?\xf5\xc2\xebc\xa3\
+\xadLg.\xcd\x8e\x8d\x8d^\x99_XZZ\x1a\xeb\xf7\xfb\xfd\x91\xcbW\xe6\xd4\xaa\
+\xcc\xd9A\xbcA\xd7\x9e\xfe\x92\xcc\xda|)?\xb6\xcd\xc0H9\xe7\xfa\xbd\xc1\x87\
+\xb6\xbf\xbde\xf5\xec\xd3S\xdb\x0f]\xdc\xd0\x81V\xdd\xbcWe 7\xbe]\x9a\xfd\
+\xf2l\x0e\x94x\x0f\xe0\x82ca\x18\xac\xf5\x93\x13[\xb7l\x98:u\xfe\xc2\xcc\xac\
+\xa5\xc9\x9dO\xf7\xb1\x01\x1b\xd7O~\xe0\xbd{\x01\xe0\xc9\xe7\xf7\xbf\xff\x9e\
+\x9b\x1f\xbco/\x00L\x9d>\xff\x17\x7f\xfd\xe2\xf7}\xd7\xb7=\xfe\xf47_x\xe5\
+\xd0\xb7\xdf\x7f\xeb\x9e]\xdb\xfe\xdd\x17\xfeRm\x0e\x07\x816\xe2\r7\xb0b\x88\
+!\x93\xc2\xb1Ri.\x9ds\xe3#\x8b\x9f\xbb\xf3\xf9\x8f\xef>\x00\x00\xff\xe0\xe6\
+\xd7~\xf1\xc5{\x1e=v}\xb6Lbq\xe8P4\xe7z\x06\x11\x97<J\x86\x03\x1a\xdes\xeb\
+\xf5\x9f\xfb\xecGn\xbc~\xfb\xdb\xc7\xcf\xfc\xfa\x17\xfe\xf2\xf1g\xf6Q\xaa\
+\x8d\'\x08A\r\x98M{\xe5\xcd\xa3\xb7\xdf|\xdd\xed7\xed\xf8\xd3\xc7\xbe~\xd5\
+\xfa5\xdf\xff=\x1fX\x1c,\xbd\xb4\xff\xc8\'?\xf2`\xa6-D\xbbZ\xcb\x8dt\t9C\r\
+\xc2\xaaa\xfa\x07\x1a\\\x94\x0e\x14t\xe3\xaa\xd6\xb7o;\xfa\xdd\xd7\xbd\xd5D^\
+5~\xe5\xef\xdf\xfc\xda\xf8\xc8\xa2\xc9AG\x04d\xd6\xbc\xdf\xa5 k\\5>\xf6\x99O\
+|\xe8\xb6\x9bv\x8e\x8d\xf6\xf7\\\xb7\xf53\x9f\xf8\xe0\xba5\xabeA\xd7\xf2\xd6\
+\xfe\xaa\\\x1e?un\xdf\xebG\xf6\x1f|\xe7\x85W\x0e\x01\xc0\xf3\xfb\x0e\x0e\x06\
+\x83\xef\xfb[\xdfv\xf4\xc4\xd9}o\x1c\xb5X\xea7|\x81\x8a\x80\xec\xa4\x80\xf2g\
+v\x1cn\xb3\x9a\xed}[\xa7\xee\xde|\xba\xb9\x9eY\x18\xfd\x93\xc37\\\x98\x1f\
+\xc59\xb7\xac\x9e\x1d\x1f\x19\xc4\xdb\r\xe3s\xa3\xbd\xc1\xdc@si;\x8a/\xa3\
+\xcc\xd2\xda_}\xc8g\'\xc6\x81L8G\xfa\xbd\x89\xd5c\xf1v||l\xf5\xaa\xb1\x0b\
+\x97fQ\xfe\x94;\xef\x116a\xcd\xc4\xaa^\xaf\x07\x00GO\x9c=y\xe6\xc2\x0f|\xec\
+\x03\xff\xe6\xb7\xbf\xbcg\xd76=\xb7\x97\xe6@,\x12\xe3I\x81T\xef\xcb\x0f\xef\
+\xdfv\xe2\xd37\xeeo\xae\xe7\x06#OOm\xbf0?\nH\x0cOOm\xff\xd4\x8d\xafo]}\xb9\
+\xc9\xf3\xd4\x89\xed\x97\x16\xc6\x14B\xdd\x07\xb0\x03e9+J\xa8\x80\x03k%\xc0*\
+\x82\xbdzj\x1f/]\xba\xf2{_zr\xf7\x8e\xab\xafZ?yyv\xee\xf7\xff\xfc\xe9\x93g/\
+\xc4\xd5\x92PX\xac#\x86\xa5y\x16{vz\xe6\xec\xf9\x8b~\xc9_\xbe2?u\xfa\xfc\x9b\
+\x87\x8f\xbfv\xe0\xd8\xd1\xa9\xb3\x1b7\xace<\xc4\xd0g<q\xa6\xf5\xe6\xa4\x06\
+\xb0\x82j\xb7\xd6\x87\x85\xa5^\xac"\x8e\x95C\x177\xfc\xfcs\xf7\x7f\xe6\x96\
+\xd76\xad\x9a\xfd\xc6\xe9-\xbf\xbc\xefn\x1f\xd7\xe7Q\xc5\xc3\x05\xbc\x82\xab\
+\xfaC\x95\xfa@\xdf\xa7\xa9f\xeb\xb1\xa7\xbfy\xe0\xc8\xd4\x9e\xeb\xb6\x1e\x9d\
+:\xfb\xc6[\xc7\x8b.\x93\x8b\xb8\x10n\xe9\xcb\xfb\xdf~\xf5\xcdc\xde\xfb7\xdf:\
+q\xf8\xe8\xa9\xb9\xf9\x85\x97\xf6\x1fY\x1a,\x1d|\xfbd\x9bG@\x81h\x82\xfc\xda\
+@Z\xd0\xeb\xb8\x8a\xc2r2\xb6//&\x1e\x16\x96F./\xf6\xd3\x92\xb3o;\xf7\xd9S\
+\xd7<{\xea\x9a~o\xb0\xe8G0\xc5\xd4\xa4\x8e\x01oC\xb0\x05|\x06\x85"\x0e\xf8\
+\x0e\xe4\xb0\xe1\xf0;\xa7\x8e\xbcs\xba!\x84\xaa\x07\x1e\x83jVG\xdc`0X\x1c,\
+\x82\x83\xc1`0\x18\x0c<\xf8\xa5\xc5%\x00\x98\x9f\x9fW6\xa8\x00 \x82@\xd2cva\
+\xfb\x9a\xd9]\x93\x17\xae\x9d\x9cY52\xb82\x18\xb98?v\xe0\xc2\x867\xcf\xaf\
+\xcd7l\xc3\xaa\xc1m\x1b\xce\xc4R\xc7f&_9\xbf\xe9\xc2\\\x1f\x00\xd6\x8d/\xee\
+\x9a\xbcpyqt\xe3\xf8\x95\x98\x7f\xb47\xd85ya\xa2\xdf\xfa}\xef\\\x9e\xbc\xb28\
+2:\xb2\xb4s\xcd\xc5&f\xa2\xbfp\xfa\xca\xea\xa9+\x93\xe3\xbd\xc5k&f&\xfa\x0b\
+\x97\x17G\x9b\xc8\x93\xb3k<\xdf\xe5\xf3\xd7\xaf\x9d\x8ey./\x8e\xca\xe9\xe5\
+\xda\xb1y\xc6\xe1\xe1\x99u\'.\xafa]!\xe7\x81c\xfd\xfe\x96M\xeb\xd7\xad\x9d\
+\x98\x9b[8v\xf2\xdc\xc2\xfc\x82\xd2~\x1b\x15\xdb\xb7\\\xb5n\xed\xc4\xd4\xa9\
+\xf3\xd3\x17/\x87\xbcx)\xc9\xfb\xb0F\x84\xc3\xaaU\xa3\x9b7\xac\x1d\x1f\x1f\
+\x9d\x9b[8~\xea\xdc\xe2\xc0\x8b\xa5E\xe7\x9b\xff\xc2\xa2\xb2\xdc\xac\x92\x86\
+Es\xaf(\xb8\xee\xdct\xf6\xa3\xbb\x0e\xbdg\xf3\xa9\xed\x13\x97\xb0\x8365;\xf1\
+\xd4\x89\xed\xbf\xb5\xff\xb6\xe3\x97VK_\xe1\xda\xc9+\x1f\xbb\xfe\xe0w^\xfb\
+\xf6\xe6U\xb3\x93\xa3m\x07\xcd,\x8c\x1e\xbb4\xf9\x95\xb7w\xfd\xc1\xa1\x9b>\
+\xbc\xe3\xf0\x0f\xde\xf2*\x00\xac\xe9\xa7\xee\x9b\x1c]\xf8\xb9\xfb\x9f\x99_\
+\xea\x01\xc0\xecb\xff\x7f\x7f\xee\x81o\x9e\xdd\xbci|\xf6g\xdf\xfb\xf4\x86\
+\xf1v\xa1\xe3K\x87w\xff\xfakwm\x18\xbb\xf2O\xee~~\xc7d\x0b\x8eS\xb3\x13?\xf3\
+\xec\xfb\xa7f\'\xdbFz\x00\x80\xeb\xd7N\xff\xfc\xb7=\xb1:@\xeaK\x87w\xff\xfa\
+\xab\t\x04\xe3#\x8b\xdf\xb7\xfb\x8d\xef\xday\xf8\xda53\x91\xc3\xb9\xc1\xc8\
+\xf1\xcbk\xfe\xf8\xad\x1b\xbe\xf0\xe6\xde%C\xc7l\xdc\xb0\xf6\xc3\x1f\xb8\xfb\
+\x91\x87\xee\xbcz\xd3\xfa\xb1\xd1\xfe\xfc\xc2\xe2\xc93\xd3\x8f>\xf1\xf2\x1f\
+\x7f\xf5k\xdb\xb7\\\xf5\x13\x9f\xfd\xc8\xc4\xea\xb1G\x9fx\xf9?\x7f\xf9\xc9&\
+\xff\xe8\xe8\xe8\x7f\xf3\xa9\x87\xef\xda\xbbk\xff\xc1w\xfe\xf5\xef\xfc\xf9\
+\x1d7\xed\xf8\xe4\xdfyp\xef\x9ek\xc7F\xfb\xbf\xf5\xfb\x7f\xf5\xff~\xf9)p06\
+\xda\xff\x91O>|\xd7\xde]GO\x9c\xfd\xd7\xff\xf1\xcf\x82W\x98\xd6]wl\xdb\xf8\
+\x91\xef\xb8\xf7\x81\xbbn\\\xb7v\x02W\xfa\x07\x7f\xf1\x8c\xcee\x13\xd0\xdap{\
+\xab\x05\xd3\'p\xce9\xf0?r\xeb7?\xb9\xe7\xf5\xd8G8l]}\xf9\xe3\xbb\x0f\xdc\
+\xb8~\xfag\x9e}\xff;3\xab1\x91\xbb7\x9f\xfb\x99\xfb\x9e\xbd~\xed\x05Vdrt\xe1\
+\x96\r\xe7n\xd9pnrtafa4\xfaz8\\\x85\x14C\x13&\xfa\x8bW\xaf\x9e\x8d\xf1\x8d\
+\xe685\xb7\xee\x8d\xe9\r\xf7m\x99\x8a\xcc<\xb8\xed\x9d?<ts\xd3\x98&\xbco\xeb\
+\xf1\xc8\xc3\xdc`\xe4\xc9\x13\xdb#\xcd\xddk\xcf\xff\x93\xbb\x9f\x8f\xc5c\x18\
+\x1f\x19\\\xbf\xf6\xc2\x8f\xdf\xf9\x8d\xeb&/\xfe\x8b\x97\xee\x93\x0b\x94\xf7\
+\xdd\xb9\xe7s\x9f\xfd\xc8m7\xed\xc4\x91;\xb6m\xba\xf7\xf6\x1b\xee\xbd}\xf7\
+\xf3\xfb\x0e5\xeb6\xfb\x0f\xbe\x13S{\x0e\x1e\xb8\xeb\xc6\xdbn\xda\xb9~\xed\
+\x9a\x8f\x1f?\xf3\xd9\xbf\xfb\xa1\xab7\xado\x92\xae\xde\xb4\xae\x11\xccH\xaf\
+\xd7\xe4\xd9\xbd\xe3\xea\xdf\xf8\xe2\xa3\x17.\xcd\xe2\xb1\xf8\xa1\x07n\xfbG\
+\x9f\xf9\xee\x1d\xdb6\xc9J\xf7\xee\xb9\xf6\xb5\x03\xc7d7\x92M&#\xc4<\xfd\x8c\
+M\x1d\xe9-\x01@D\xc0\xd4\xec\xc4\xd1\x99\xb5\x00\xb0c\xf2b\x94\xdf\x1d\x1bO\
+\x7f\xfa\xc6\xd7\x7f\xf1\x1bwC\xb0)7m\x98Q\x11\x10\xc3[\x17\xd7=z\xf4\xba\
+\xf7m=nV,\xc2\xfcR\x12\xc6\x95A\x1f\x00\x96\xfc\xd2\xa3Gw~t\xd7\xc1\xc8\xde\
+\xfd[\xa6\xfe\xec\xc8\rq\xea\xe8\xc0?\xb4-U\xf1\xcd\xb3\x9b_9\xb7\xb9\xb9\
+\x9e\x1c\x9d\xfb\xe9{\xbfv\xc7\xc6\xd31ujvbv\xb1\x8f\x95\xd6\xc7w\x1f82\xb3\
+\xf6\xf3o\xde\x8a\xd9\xb8\xe1\xbam\xff\xeb\xff\xf0\xf7\x1aa\\\x9e\x9d{~\xdf\
+\xa1CGO\xf6Gz;\xb7o\xbe\xf7\xf6\xdd\x8f<t\xd7}w\xdc0\xbf\xb086\xda_\x1c,\xe1\
+\x82W\xe6\x17\x00`\xcb\xc6u\xff\xe8\x1f\xfc\xed\x89\xd5\xe3\x17ffO\x9d\xbd0>\
+\xd6?y\xa6\xed%\xef\xdb<\x97f\xe7\x00\xf0l\xc0\xdf\xb5w\xd7O\xff\xc3\xbf\xbb\
+nr5\x00\x9c\x9b\x9e\xd9\xf7\xc6\xd1\xb7\x8f\x9f\x06\x80[n\xb8\xe6\x8e\x9b\
+\xaf\xfb\xd8\xc3\xef}\xdf{n\xe2\xfd\x15t\xa1\x93\x1e\xb4\x08\x0e\x9c1\xdb\
+\xf6\x00\x00\x0b\x83\xdeo\xbcz\xe7D\x7f\xf1\xa3\xbb\x0e>z\xf4\xba\xdf;p\xf3\
+\x81\xe9\xb5\x00\xb0g\xfd\xc5\x1f\xbd\xf5\xe5\xef\xb8\xb6]|x\xdf\xd6\xe3\xeb\
+W\xdd~a\xae\xef=\x8c\x8f,\xfd\xe8\xad/c\x04\xcc,\x8c~\xed\xe4\xd6\xe9\xf9\
+\xf1\x9d\x933\xf7m\x99\x9a\x9a\x9d\xf8\xe9g\x1ez\xe3\xfc\xe4\x96U\xeb\x1f?\
+\xb6cz~\xfc\xd6\xab\xce\xde\xb2\xe1\\\x93\xb9\x19\xaf\xd3\xf3\xe3\xcd\xed\
+\xe9+\xca\x9aI\x0c\xfb\xa77~\xed\xe4\xd6\xc8\xc6\xfdWO]31\x13\xad\xfe\xf5k\
+\xa7\xb1\x98\xff\xe8\xd0\rqP\xfc\xf0\xde}1ifa\xf4\xdf\xbfv\xfbc\xc7v^Z\x1c\
+\xbdm\xc3\x99\x7fx\xc7K\x91\x99O\xdd\xf8\xfac\xc7v\xb6&\x06`tl\xf4\xc7\xbe\
+\xff\xc3\r\x02\x0e\x1c\x99\xfa\x85_\xfb\xc3\xe7\xf7\x1d\x840\x9e\x1e~\xf0\
+\xce\x1f\xfd\xf4\x87\xf7\\\xb7U\xed\xe5\xe6\xa2\x11\xe4\x1f\x7f\xf5\xeb\x7f\
+\xfa\xd8\xd7\x0f\xbd}r\xf5\xaa\xb1\xd9+\xf3`y\x94\x1e\xc0\xc1\xba5\xab?\xf7\
+\xd9\x8f4\x05\x9f\xdfw\xf0W\xff\xd3_\xbc\xb4\xffH\x93>2\xd2\xfb\xde\x87\xef\
+\xc7z\x85\xd5\xaa_\xb3*\x00\xa01\x07|\xe3\x18\x85\x81\xf7\xbf\xbc\xef\xeeG\
+\x8f\xee|\xf9\xcc\xc6\x18\xf9\xe6\xf9\xb5?\xff\xc2\x03\xbb\xd7]h\x84\xbdy\
+\xd5\xecm\x1b\xce<ub+\x00\xdc\xb7\xe5t\x94\n\x00\xbcuq\xdd\xbf|\xf1\x9e\xe7N\
+m\x9e[\xecmX=\xf8\xf0\x8e\xc3\xaf\x9f\xbf\xea\x8d\xf3\x93\x00\xf0\xd4\x89\
+\xadO\x9d\xd8\xea\x1c\xfc\xd8\xed\xfbb\xbf/,\xf5~\xe9\xe5{\x8e_Z\r\xd0\xc08\
+\xe7o/\xfa\x91\xc7\x8e\xed|p\xdb\xf1\xc6S\x99\x1c]xd\xc7\x91h\xf5\x1f\xd9q$z\
+0S\xb3\x13O\x9flm\xc1\xee\xb5\xe7\xbfk\xe7\xe1H\xe4\xdf\xee\xbb\xab5"\x00\
+\xcf\x9e\xba\xe6\xd4\xd7\'\xfe\xdd\x07\x1fm\xf4\x0111\x00w\xddr\xdd\x7f\xf5\
+\xc0m\x00p\xf2\xcc\xf4\xcf\xfe\xd2\x17_;p\x14\xb3\xf7\xd5\'_>;=\xf3\xcf\x7f\
+\xea\x07\x1b\x81\x81!\xdaG\x9fx\xe9\x17~\xf5\x0f\x17\x06\x03\x00\x98\x9eQ\
+\xac!\x0b\x1f\xfe\xc0\xdd\x8d\xe99pd\xea\xe7~\xe9\xf7N\x9e\xbd\x18\x93\x06\
+\x83\xa5?\xf8\xca3g\xcf_\xfcg\xff\xf8\xd3q\xd3\x08\xe8\xa4\xd7"\x9b\x8e\xa5\
+\x00@\xf2\t\xe8\xa6\x10\xa68?\x80}g7\xeeY\x7f\xf1=\x9bOn^ue\xa2\xbf\xb8jd\
+\x11\x00\xa2\xc35\xda[Z76\xdf\\\xc7\xb5?\x00\x98\x1b\x8c\xfc\xe6\xab\xb7?y\
+\xe2\xea\xe6\xf6\xfc\xec\xc8\x7f~\xf3\x86b\xb3\xe3\xd4 \x06<\x87l\xb9\xf2\
+\xbeqw\x9e>\xb9\xfd\xf8\xe55Q\xf1<\xb4\xed\x9d\xdf~\xfd\xb6\xb9A\x7f|d\xf1\
+\x81\xab\x93\xbd\xff\xd2\xe1\xdd3\x0b\xadvy\xcf\xe6\x93\xd1\xbd\x98\x1b\x8c\
+\x00\xc0\x03[\x92\xfd^76\x7fiq4\x1a\x85\x9b\xd7\x9f\x8fI\x0f\xde{K\xd3\xd7_}\
+\xf2\xe5\xd7\x0e(K\xb0/\xbcr\xe8\xaf\x9e\xd9\xf7\xb1\x87\xdf\x0b\x00\xfd\x11\
+e\xb7\xf3\xf2\xec\xdc\xef\xfd\xd9\x93\r\x02p\xb0VV\xfa\xbd\xde\xde=\xd76\xd7\
+a\x05\x89\x87\xbfz\xe6\x95g_|\xb3qDb(\x1c[\xa2\x87\x16=\xf8~fm\xa7\xd9\xbci\
+\\\xe88Xe\x18\x1f\x19\xac\x1d\x9bw\x0e\xc0\xc3\xee\xb5\xd31\xfe\xfc\xfcx\x1c\
+\x7f\x82\x994\x13\x92Ro\xea\x86\xe2\xba\xa4\x87\x99\x85\xf1\xc7\x8e\xed\xfc\
+\xa1\xbd\xedv\xcb\xf5k/\xdc\xbd\xf1\xe4\xb3\xa7\xae\xb9q\xdd\xb9\xeb\x03\'\
+\xcc%\xdc\xbc*9\x9e\xe3#\x83\x9f|\xcfs\x99\x1a\xd6\x8f\xb5S\x92\xdeH/\xfae/\
+\xbe\xfa\x96\x95\xff\xc9\xe7^k@\x00\xa0l\'\x9e\x9d\x9e97})\xd7"\x1a\xc6\xc6G\
+wl\xdb\x08\x00\xf3\x0b\x8bo\x1e\xd6\\(\x07\xe0\xe1\xd5\x03G\t\x08\x9c\xe1\
+\x04\xd8+\xcf=\xb0\xf7\t\'G\xe7~\xf6\xbe\xa7~\xfc\xceod\x10\x80C\xaf\x07\xeb\
+\xc6\xd2<\xe2\xfc\xdc8\xe1`\xe5\xf6e\xb0\xcb\xfb\xe8\xd1\xebf\x16F\x9b\xeb\
+\xf1\x91Ac\x8c\x1e\xdcv<\x8e\xe6\'Ol?<\xb3.\xe6\xd71g\x84\xd8\x1c\x07n\xd5\
+\xf8\x18\x00\xcc/,\x1e\x9d:\xabdu\xd0s\xbd\xb3\xd33(\x82\x1f\t\x9f\x9b_\x9c\
+\x9b_\xd0\x8f\x8ak\xa1\xdf\xeb\xad\x1a\x1b\x05\x80+s\x0b\xe7\xa6/\x81\xd7\
+\xa5{\xe0\xf0\t\xccF\xfbW\xfeCW)gS\x91\xda\x9e\xa6\xb2\xef\xbfq?3\xf0OOm?23y\
+q~\x0c\x00~\xf8\xd6}l\n\xb0\xe4\xa1Y\xf6o\xc2\x86\xf1\xb95\xfd\x85\xf30\x02"\
+T\x1d\xd9\x93\x87\x93RJZ\xbdy\xeb\xe2\xfaG\x8f^\xd7l4\x03\xc0{6\x9f\xda\xbaz\
+\xe6\xeeM\xc9*}\xed\xd4\xd6h\x0b\x80\x1a\x97\xb9\xc1\xc8[\x17\xd7\xcd\xa8;\
+\x11\x00\xe3#\x837\xa6\xd3\xba\xc2\x95\xb9y\x00\x18\x1b\xedO\xac\x1a\x03\xa6\
+r]\xcb\xd5\xc6\xf5\x93\x85F\xa1\x90Ybg\xf1\xab\xc6G\xc7\xc7G\xdb\x19\x83\'C\
+\xd9\x03l\xdc\x80\x97\xec\xd0\xb9>R\x17&\xceUA?s\xbc\xe2\xa1m\xc9X>wj\xeb\
+\xff\xf6\xdc\x03q)\xad\xe7\xe0\xe3\xbb\x0f\xcay\xe0\xcbg7G\xdcl]}\xf9;\xaf}\
+\xfbw\xf6\xdf\x04X\xff\xc7\xbdJ\x03\x07\x13\xcd\xdaQ\xe9,J\xc4\x81\x07\xf7\
+\xf8\xb1\x1d\x11\x04\x9bW\xcd\xfe\xc4\x9d/\xc4E\xa4\xb7.\xae{\xf2\xc45\xb8\
+\xe0\xa1\x0bI+\x8c\x8f\x0c\xfe\x9f\xd7\xf7Z6\x0b\x00\x16\x96Z\x04\x0f\x96\
+\x06GO\x9ci\xae\xef\xbf\xeb\xc6f^\x80C\xd3\x96\xbbo\xbd>qXw65\x13f\xe7\xe6\
+\xdf9y\xae\xd9b\xbe\xeb\x96]o\xbcu\xdc\x073\x19\xaa\x00\x00\x7f\xef\xed\xbb\
+\x11\x1bh\xa8\xb3.L\x0eA\x92\x7f#\x8e\xdci\xad\xabW\xa7\xdd\xcc\x17\xcfl\x8e\
+\x93%\x00\xb8\x7f\xcb\xf1[6P\xad\xe8\x1c\x00\xbctfs\xe3m5\xe1\x87\xf6\xee\
+\xfb{7\x1e\xdc\xb0j\xb0~\xd5\xe25\x93\xb3?}\xdfs\x7f\xfb\xba\xa4Z\xe48\x98\
+\x1c]xp\xdb\xf1\xb1>\xf4\x1c\xac\x1b_\x1c\x1b\xa9\xda\x18x\xe5\xfc\xa6\xe7Nm\
+\x8d\x14\xbe\xe3\xda\xa3q\x19\xe3\x1b\xa7\xb7`\xb6\x01\xe0\xe5\xb3\x9b\xa7f\
+\'\xe2\xed\xc7w\x1f\\\xd3_\x98Y\x18o\xfe\x01\xc0\x7f\x7f\xfb\x8b?\xbcw\x1f\
+\x00\xcc,\x8c\xa7\rk\x0fO>\xdfnu~\xf7\x07\xef\xb9\xe6\xea\x8d\xe8pz\xfb{\xf3\
+\r\xd7<\xfc\xe0\x9dEn\xf1I\x8bL&\x00\x98\x9f_|~\xdf\xa1&\xe2\xa3\xdfq\xef\
+\xd8\xd8\x18\x1e\x1c\r\x02\xee\xde\xbb\xeb\x81\xbb\xd1:\x01V\xfb\xdc\x1c\x10\
+nq]\xb9e\xe3\xe9\xf9\xb1\xe8H?\xb4\xed\x9d\x17Oo><\xb3n\xa2\xbf\xb8g\xdd\xf9\
+\x1f\xbeu\x9f\xba\x8c\xf8\xda\xb9\xf5_>r}\x1c\x97\x93\xa3\x0b?\xf9\x9e\xe7>\
+\xb9\xe7\x8d3WV7KL\x1f\xbc\xe6\xd8\x85\xf9\xb18e\xf0\xe0\x99\xf3\xff_\xdf\
+\xf4\xda\xee\xb5\xd3\xcd\xba\xc2\xaf\xec\xbb\xf3\xe5\xb3\x9bx\x1d\x91\xcd\
+\xa0\x0cf\x16\xc6\x9f8\xb1].\xff\xcd\rF\x1e?\xb6\x83EN\xcdN~\xf1\xcd\x9b\x7f\
+\xfc\xceo4\xb7\xf7m\x99\xfa\x85\xf7\xfd\xcdW\xde\xdeujv\xf5\x96\xd5\xb3\xdfy\
+\xed\xd1f\t\xe1\x8e\x8dg~\xfd\x95\xdb\xbfvj{\xf4?^}\xf3\xd8\xa3O\xbc\xf4\xc8\
+Cw\xed\xd8\xb6\xe9\xe7~\xe2\x93\xbf\xfa\x9f\xfeb\xdf\xebo\xcf/.\x02\xc0\xf8\
+\xd8\xe8m7\xee\xf8\xdcg?\xa2O\xd9-\xfe3\xa7\x19\xc0;\xef\x00\xe0o\xbe\xfe\
+\xea\x0f|\xec\x03;\xb6m\xba\xed\xa6\x9d\xff\xd3\x8f|\xcfo|\xf1\xd1\xa93\xd3\
+\xb1\xd8\xdd{w\xfd\xcf?\xf6\x898)\x05\x00\xba=\x97\xd9\xd1\xf5\xf8?\xfb\xb9\
+\x03\x07OOm\x8f\n\xff\x96\r\xe7\xfe\x8fo{\xf2\xd8\xa5\xc9fa\xd5(\xe2\x16|\
+\xff\xb7\xf6\xdfv\xe3z\xb2Ps\xfd\xda\x0b\xb1\xc8U\xe3W~\xee\xfeg~\xf1\xc5{\
+\xfe\xfcH+\x9e\x17Oo\xc64\xa2s\x07\x00\x00w\xe6\xed\x82u\xe6\xa0\toL_\xf5\
+\xfc\x19e\xf5\xe6\x0f\x0e\xddt\xe7\xc6\xb4\x9e\xd1\xacd\xb3<wl<\xfd\xf1\xdd\
+\x07\x9e?\xb3u1X\x84\xcb\xb3W~\xed\xf3\x7f\xb9w\xcf\xb5\xcdz\xed?\xff\xa9\
+\x1f|\xf6\xc57\x8e\x9f:\x0f\x00\xbbw\\}\xef\xed\xbb\'V\x8f\x1f=q\xa6\xd9P\
+\xc8\xf0l5\x84\xc7\x80\x07\x80\xe3\xa7\xce\xfd\x9b\xdf\xfer\xb3\x12\xf0\xb1\
+\x87\xdf{\xfbM;\xff\xfak\xaf4\xeb\x8c{\xf7\\\xfb\xed\xef\xdd\xdb\x1cA\x98X\
+\xdd:=b\x9cG\xddo\xcd\x19\x00\x88c(\xf2|\xe1\xcd\x9b\xef\xd8x&\x8a\xb3Y\xf9o\
+\xae\x1f?\xb6\xe3\x9a5\x97\xe2\xed\xaa\x91Ats\x8e_\x9e\xf8\x99g\xdf\xff\x13w\
+\xbe\x80\x9dJ\x1c\xae\x1a\xbf\xb2{]\x82\xd1s\xa76\xff\xd1\xa1=Qy\xc4\x10\xcd\
+\xcaD\x7fq\xac\x97VaW\x8d,:X\xf2\xc1\x90588tq\xc3S\'\xb63"\x8f\x1d\xdb\x11EH\
+(/\xf5\x7f\xfe\x85\x07\xa6f\xd7\xc4\xc3,2<~l\xc7/\xbd|\x0f+~\xf0\xed\x13\xff\
+\xcb\xff\xfd\xbb\xff\xe3\x0f}\xf4\xde\xdboX7\xb9\xfa\x91\x87\xee\xc2\xa9\x8f\
+>\xf1\xd2\xa3O\xbc\xf4\xcf\xfe\xf1\xa7\x19)\xe7\xa0q\xf2\xc7\xc7(8\x90t\xa2\
+\x9f+\x01\xf4\xf8\xd3\xfb\xfe\xd5\x7f\xf8\xd2\x7f\xfb\x03\x7fk\xdd\xe4\xea=\
+\xd7me\x8b\x92\xcf\xef;\xf8\xdc7\x0f\xfe\xe8\xa7\x1eQ[\xe1\xe8L*UJ\xf5\xd0\
+\xc8\xf6[\x1e\xc4i\xa94\xc0\xa5\xc5\xb1g\xa7\xb6\x02\xb8-\xabgG{K\x03\xdf\
+\xbb2\xe8\xbfsy\xf2\xb7_\xbf\xf57_\xbbc\xfb\xc4\xe5\xd1\xde\xd2\x89\xcb\x93\
+\'g\'\x9e=\xb9\xf5\xe0\x85\r\xd1\x0f\x9aY\x18\xfd/\'v\x1e\xba\xb0\xce\x01L\
+\x8c.\x02\x80\x07we\xd0?7\xbf\xeak\'\xb7\xfe\xab\x97\xee\xf9\xb3\xc3\xd7\r|\
+\x9by\xc9\xf7\x9e9\xb9ufq|\xeb\xc4\xe5~o\xc9\x83k*:9;\xf1\xe7o_\x7fnn|U\x7f\
+\xe9\xfa\xb5\x17\xe6\x97FN\\\x9e<77\xfe\xfc\xe9\xab_>\xbb\x85\xf6\xb2\x03\
+\x80\xf3sc\xd7\xae\xb9tqa\xaca\xe9\xed\x99\xb5\xffq\xffm\x97\x16\x85\xe7\xef\
+\x00\x00\xe6\x97\xfa\xcf\x9e\xba\xe6\xa5\xd3\x1bW\x8d\x0c"\x87\x03\xdf\xbb\
+\xb80\xfe\xca\xb9M\xbf\xb2\xef\xae\xff\xf0\xfa\x1d\x17\x17V\xc9\xb2\xa7\xcf^\
+x\xec\xc9}\xa7\xce^\xf0\x1ez=\x07\xe0\xa6/^\xde\x7f\xe8\x9d\xdf\xf9\xa3\xff\
+\xf2\xab\x9f\x7ft\xed\x9aU\x1f\xff\xf0\x03\x00\xf0\xcd\xd7\x8f<\xfd\x8d\xd7\
+\x9b\x0e\xe9\xf5z\xb7\xde\xb8\xa3\xdf\x1fy\xf3\xad\xe3\xcf\xbc\xf8\xc6\xcc\
+\xe5+\xb2\xc3]\xafw\xd7\xde]\xfd\xfe\xc8\xfe\x83\xc7\xfe\xea\x99}W\xe6\x16"\
+\xab\x1e\xfc\xabo\x1e}i\xffa\x0fp\xd5\xfa5\xce9\x0f07\xbf0uf\xfa\x8b_z\xf2_\
+\xfc\xe6\x9f\\\x99\x9b\xbf\xed\xa6\x9d\xe7\xa6/=\xf1\xf5W\x8f\xbcs\x86;\x01\
+\x02\x01\xe9Y\x95p\xe5\xee\xfd\xd8?\xb5@\x10Kn]=\xb3k\xf2\x02\x00\\Z\x1c=<\
+\xb3\xaeq\xa0\xf0i\xcf\x81w\xcd\x86\x9b4B\xdb\'.o^5\xbb\xa6\xbfpiq\xf4\xf4\
+\x95\xd5l\xbf\x11\x875cp\xe3\xbas\xcd\xcerSQs\xf2\x00\x00F{\x83\xb8\x067\xf0\
+n~\xe0\xd4S\xe1\x91\xa5\xd1\xde`aiD9\x87\xa8u\xcd\xd6U3\r\x87\x00p\xea\xca\
+\xc4[\x97\xae\xf2\xd6.r\xb3\xe3\xef\x9c\x03\xe7zn\xfd\xe4\xc4\x86uk\xe6\xe6\
+\x17\xce\x9c\xbb\xb8\xb08\x00\x80\xef|\xff\x1d\xff\xd7O\xfe}\x00\xf8\xad\xdf\
+\x7f\xfc\xdf\xfe\xeeW"\x93c\xa3\xfd\x91~oi\xb0te~A\xbe\xec\xa1\xa1<6\xda\x1f\
+\x1d\xed/.\x0c\xe6b\x9e4\xa5o\xaf\xb6m\xd9\xb0m\xcb\x86\x8d\xeb\'\xcfN\xcf\
+\x1cz\xfb\xe4\x85KW\x1c\xc0H\xbf76\xda\xef\xf7zs\xf3\x8bd9\x92\xd7\x13\xa5O\
+\x1c\x020\x9f@\xa2\x07Z\xa6f\'\x99\x8f\r\x00\xb1\x8b\xc9\xb3A\xc2\x199~iu\
+\xbb\x17P\xaa\xe5\xd2<\xbcx\xfa*\xfa\x10j{\xb1\xb04\xb2\xb0\x94b\xad\xe7\x02\
+\xe6\x06\xed\x93\xf6sK\xc6\xee\xa8vPg\xea\xca\xe4\xd4\x95I\x92I\x1aP\xb4\xfc\
+\x17O \x9e\xbfx\xe9\xfc\xc5K\x98\xe1x\x98\xb3\xd9\xde\x8d\x9b2s\xf3\x0b0\x0f\
+`\xa0?\xe6\x99\x8b\'S\xb8\nog\x9b\'N\x9d?q\xea<\xc4\xc3\xc6\x0e\x00\xdc`\xb0\
+4;\x98G1V\xb3\xcd\xd0\xcb\xe51\x96\xa8\x96\x19<\x83\xa8\xe7\xa9#=\x88K\xef\
+\xe1\xbd\x17<(\x8f|\xd4/J\xd64J\x9bV]\xbbu\xd3\'\xff\xceC\x13\xab\xc7\xd5\
+\x8a\xb6m\xd9\xf0\xdd\x1f\xbc\x07\x00N\x9e\x99>p\x84LU\xaa\xa6\x85\x05\x96\
+\xf5\xe2\x1e\xe4\x88\xa7\xa9\x15\x95\xe6\xce\x13T\x86\xdcC}\xe6\xa3\x8a\xf6\
+\xad\x83A\xab\x8d\x9dRZn-\xae\xdcj\xb4\x12\x10\xf1\xc9\x89\xd5?\xf2\xa9G>\
+\xf6\xf0{\xef\xbd}\xf7\xaf}\xfe/\x8f\x1c?\xbd\xb4\xb4\x04\x00\x1e|\xcf\xf5\
+\xae\xbbf\xf3O\xfdw\x9fh\xf6\x17\xbe\xfa\xe4\xcbG\xde9\x9d\x1a\xa5M\xcd\xb3u\
+V\xec\x9bD:\xce\x038z\x81\xab@\xebJvGu\x9e\xcc\xd4\x86\xa1\xdfF\xe3!\xb8\x8c\
+>\xa8\xfd\x90\x14\x8ft\x96\x9e\x00o\x93VB\x8d\xc5\xba\xde\x7f\xcf\xcd\xcd\
+\xe6Psx\xe4\xaf\xbf\xfe\xdak\x07\x8e\x9d=\x7fq\xe3\x86\xb5;\xb7oz\xf8\xc1;\
+\x9bE\x82W\xdex\xfb\xf3\x7f\xf27K>Mg\x9c\xd7\xb8\xa4\xab\xbbrm\x11CAy\xc4\
+\x0f-G\xc6\xe5\xe4pT\x1e\x1365n[\x87w\x1e\xbc\xbb\xf7{\xfe)a\xab{\xc7)/\xb0\
+\xc9\xbe\xc0\x80\x9b\x03\x94\x9e\xf8h\xffp\x108\xfc~(d\xa71\x1b\x9c\x9e\x1a\
+\xb2\xfaCy\xc8\x1c\xdc\x86uk>\xf9\x91\x07\xbf\xf7\x91\xfb3+B\xcf\xef;\xf8\
+\x7f\xfe\xca\x1f\xbeu\xec\xa4\x95A?\xeegy\xe5\xd9\xcd@J*\xbe\xd2!\xc2\x00\
+\xf75#I\xfcCHSD0\xfb\xa5\xe9\\\xd9/\x95o\xc6\x02\x0b\x12918\x05\x04\xcc\x1f\
+\x8c\x8e\xfa\x10\xef\x000J\xb8\x100\x1f\xed\x8e\x9f\x83+\xf3\x0b\xcf}\xf3\
+\xe0s\xdf<xa\xe6\xf2\x92\xf7##\xbd\xd1\xd1\xfe\xc8Ho~a\xf1\xcc\xf9\x8b/\xbc\
+\xf2\xd6\x17\xfe\xf4\x89_\xfe\xdd\xaf\x9c<3\r\xa0\x0f\xbb\xd46\'\x92\xe4\xfa\
+.h\x91\xdc\x12&\xf7\xd0\xb1T>\xd4\x1cK\xc0\xa1\xcf\xf3`:uo\x0b\xe0\xb4\x1b\
+\xff\xc0kI,\x10\x8d\xed\tf\r\x1b\xc6\x10\xc0\x92\xca\xb8\xb4\x01\xc3d\xcf\
+\x8aD\xe5\xfc\xfa\xc1w^?\xf8\xce\xfa\xb5\x13\x1b\xd6\xad\xb9j}\xbb\x9dvn\xfa\
+\xd2\xc93\xd3\xe9\xc1o\xcbIs\xcau\xed\xe3:5P\'\x86 \xe3v5\xf7)\xa6\xe2%\x15\
+\xe1V\xbe7\xaa\xe8\t\xae\xe0\xd3j5\x81\xe3\x80\xb9\x05\x15\x08\x90\x13-\x1c\
+\xe3\x82!\x9e\xbexy\xfa\xe2\xe5\x8c\xda\xafe\x18\x1cP\xcc\xe1\xa7\xbbd$\x0b\
+\xcc3H\xbfV\xab\ritp\x0c\xcb\xe3\x8c>\xabd\xdb\xfew\xd9\xa5g\x15\x15\xb5\x03\
+{E\x12\x1a\xfa\xf1V\xb9\xceW\n\x15\xf5\x1a\xe4*NZ\xa4\xac\xedVS\xe6\x91u>_h.\
+\x89\xdb\xa0\xadf7\xf4k^\xc9\x91\xe3\xb3kA\x17\x1e\xdb\'cq\x99o0\xc2\xa4PM\
+\xbci\xac\xdf\x99>\xe0\x87!*[\x96\xc1\x9f\xf9h9\x8f\xaf\xe9\xc6\xb8\xe5\x98\
+\xc9\xa1PK\xab\xc7u\xaf\xab\xb1\xea\xa6\xf7E\x12\x99\x89s>\xde \xa7=%\xb8L\
+\xec2\x17L\x82\xa3]\x04\xac\xec/<\x04\xbb\xa3Y\xaf\xc5\x8bkGrV\x9dd\xf1\x18\
+\x03\x869P;\x11\xcfYs{\xe1r\xc2\xe3\x81\xa3L\xba)mVm\n\x04brX\n59\x95<\xaa\
+\xef]Y#\xed\x16r+=\x8c\x8a\xd0A\x95\xd2\xc1E\x1d\x05\xbd\xdb\xc9.\x82\xf9TrW\
+\xe6\x86\x1ey\x985\x11\x8a\xa2\x1c\xe6U\x12\xd9\x90]\x81\x97\xd5C>\xf3\xd0\
+\x0f\xea\xe7J9*K\x9c\xe2[\xe6\xf3\xc7U\x9a\x1f\\<\x81\xc0\x9a\xf5-\xf3\xa0\\\
+\xe4Pg\xc9\xc8U\xa8\x14M\xb1$\x0e\xccq`rF\x05\xaf\x19\x02\x9d\x07r\xa7\xd7\
+\xd5\x15\x07\xcclc\x960Q\xc3P\xd8/\xc6\xa2\xabC\xe9\xa4\xa7\xf7\xce\xb9>P\
+\xf1K\x8e\xf9\x81\xc9\xec\x1b\n\xf1;\x0c\x04)\xde&\x95\xdb\xf4\xb6\x80$\x95\
+\x0e\xb6\x80lB\xda\xb3\xac\xa8\xaeW\x08\xe2&7\xe0E]AiK~RP{\x8e\xcdS\xd0c\xe7\
+\xd89hq\xc0\tP\x14P\xa1\xf7\x15\x040\x95 _Q\xcc\xdeW;l\xe0&X\xcfTuD?\x90\xe8\
+\x06\x94n\xca\x1f\x95o\xf1\xab\xa2X\xcd\\\xaa\xcb\xd8!\xec\xec\xee\xe5lA@\
+\x80\\\x01J\x8f\xa6\xb7\xd5\xe4_R\xbb,\xdb\xaf\r{\xe3\x8eo\x14\x1b\xef\n\x1c\
+.X\n\x00m\xd3\x8b\xea<\xcf\xe9\xc1|\xea\xbb3\xb6\x18\x98\xbc"Hu\x1e\xa8LY\
+\xcbK9J\x0e\xe4\x13d\x08\xa8\xb2\xcf\xbe\xac\xb2`\x08\x97#\xc5|\xd7\x9b\x85\
+\n\x93\x1a\xae\x1eXE\x1a\x0e\xf0-_\xe9\xeb\xda@\xb9\x92S.\xd1m@f\xf2\xd3g\
+\x11U\xeb\x9ei\xcfp\x8aa\xe8\x99XuFu\xf1U\xa9K:\\\xfc:F\xd1\xb7\xbf\xe0\x05\
+\x00I\xb9\xce\x8f\xb3x\x8e\xf7\xa9\xac\xe6F\xd4\xd2\xc1I\xc6c\xc7}U\x90^\x00\
+C{\x19}\xae\x14\tb\xcd\xa40j\xa9K\x98\xc9\xa9\x86!feF-\x8eC d\x96\x0b\x00\
+\xa6\'\x9f\x89$\xa5MC^\x0e\x95 \xb3G\xac\xf6nc\xa1\x9a\x9c8\xe4#\xd1\xc0\xbd\
+I9;\xa0\xd3\xbf\xe2;\xd0S\xb9\x92\x07J6\xb7h\xa5\xe6,\xb7\xe4\x9f\x84\xe24\
+\x05yyV\xeb:\x87\x1a1\xb3<\x8e\xc6\xdbU\x9b\x82\xc7\x90\xd5\x9f@2\xba\x92\
+\xb2\x11:=\xb7\xaa\xa1\xbc\xe5\xb0\xbd\xc8,\xb08yU\x15\x8a+$\xdd\x95J\x98M\
+\x19^\xa4Zc\xa6\x96\x15\xdbS\x95\xb3\x12a\x9e\x8c\xafO\xa1\xcc\xf1B\xbe\xbd\
+\xcc\xa3\x05\x05^\x07\x8a\xf4\xfci\xc7r\x17\xe7\x11`\xbc.ZY\x1e(\xee\x0e\xa8\
+\xe2\xc9\xb0\xa7\xbaxq\xf5\xad\x8dc\xde\xa2\xd0\x07C\xae7\xa0e\x15\xd0PR\x98\
+%\x1a\x9dV\xdet\xa0\x05\xfbq\x02\x98\xdb\xff\xf5\xe2JL\xb2dw$S\x82[R\xd9W\
+\xa5\x85}\x9aW8\xf6\xbc\xa8\x88\x0fbMI\xaaQOCG\xc7\x9c\xeeiz\xfd\xb6\xb8z\
+\xc8\x16\x7f\xa4>&\xdcv\x9a\x82fC\xb3bH\xab\x0cZPv\x0b\xaa\xda\xc7\xf3\xa0\
+\x12\r\xb5\xc3Bu(T\x89\xa2\xcb\xdcy\xa7\x8e\xfaYO\nK.f\x8f\xe7\xfd_\x10\xe21\
+\x94\x87VsA\\\x9c\xab!\x88h-\xee\xebC\x9f\xdaz\xdb\xbbp)\x19\xcb\xc9p\x05l\
+\xce\x84\xceo\x9d\xc7\xa1\xe6\xdc\xb2ja\xcep\xc8\xa8\xdc\xe4LX\xae\x99Q\xad\
+\xb0\xd1\xd0\x9e\xfb\xf0\x14\x07\xc8\xb1\xaf<\xac\x90\xf7o\xd45\xc1"\xdb\xfd\
+XB\xe9(\xa1\n\x14\xdf C;\x1f\x0c\xc3\x80\\\x87*\xb2\xd6\xee\x11\x8eR\xab)\
+\x86\xb4\xcc\xae\xf7;\x97\xb4\x13\xc3\x81\xaa\x0fm\x92\x193yJK@\xb3\xe8\xaa\
+\xa7$\x89\x80Rc\xfb\x9e\xca\x93\xb1\xa2WC\xaf\xa5\xf3\xd45H\xc9\r\xb7Al!\x80\
+\xcc\xec\xa9I\xab\'\xcev\x80H\x95A\xf8N\x0c\x1b\x08\xa9-\x04\xd0$\xd3b@u6+w#\
+\x87;M\xd3g\xae@\xc4%\xea\xae\xc4\x8a\xaa$<\xa4}l\x82\xeb\xd6V\xa0\xd7\x88k\
+\xd3\xbf\x95B\x00%\x81/\x1d0\x1c\xd8Aw\x05\xbc\xd6\x96D\n\xb5\x00\xa1 \x8d)b\
+\x08\x90\x9b\x19\xf3v\x17\x1c\x13\xf6\xd0\xef\xe3o\xe8\xf0\xc7\xd0<\xea\x01\
+\x88\xcd\xd7\xb9\xf4\xb1\x00\xfb\x93\xd2Q\xdfU.\x00tE\x00~,\x82\xf9\xa4r\xcd\
+\xb8v\xe8\x07OY?\xbb\xe7\x19\xc8p\x8a#1\x0e\xcb>\xfeI7\xed\xeb\xc8E~\x95\xa9\
+\xa1\x8f\xa8\xe4\xbf\xe8\x05\x00=\x1f\x9et\xf4q\x8d \xfe\x8bM\x03|\xdd\xfe\
+\x0bW\xda\x9fX^\xf2\x8c\x9c\xff\xe6\xdf\x8a,\xa1H5H\xf6\x81\xc2\xbf\x9ai\x8b\
+G\x0f\x8f\xc6\x0bN\x13\xdadQ)R\x14.\\\xc4\xcaQ<\x83ge\xb0\x1c&\xef\xbd\xf5\
+\x8cP\xcd\xa0\xea\x05\xd1\x11\'(\xd0\xc6\x7f|l\xb8Wp\xe1c\xd1\x1a\x91Z[p \
+\xc5\xb9L\xcb\xa08\t\xb5\xaf\x11L,I\xdb\x9d PA\xc7)W\x98\x1f\x9e\xd9\xfa\x87\
+\xf8\x07$]\xf6<\x88\xa3\xa1\xcc^z{\x19\x12,\x1e\n81e\xf1\x8a\xa8\x83\x06\xc5\
+\xe9U\xa0(n\xf2V\x06\xfd\x0bn+\xa0e\x04M\x14\x88|\xc9\xac\xc0U\xe0\x03;\x13\
+\x15Z\x8aB\x01\x82\xbc\t\x87A%\xe4\x83\x0f\xa1\xb9\xed\xe9UG\xf5\xa0\x9a\x82\
+x+\n\xe1\xf8\xaeBHJx\x19GW\x94\x15\xb7\x95\xc2\x01\xa6\xd9\xfa\x9a"\x8f*z\
+\xa1E\xd8 \xc2G\x19\xd8?\x85\x8ddPL}\xc6d\\\x0cA\x13`\xee\x89A\xf0\\\xf6\xb4\
+=zTu\xbf3\x95ce\xea$H\x86\x03\x02eG:\xb1\x86\x9a\xed\xa6\xc5J\x0c.h\xa2\x98\
+\x0f\x84k\xa2?jkg^\x8e\xa5\xf6\xf5G\x07\xc8\xa9\x11\xef\xbd\xefi\xd6\x8e1\
+\xce\xd5,\xf7\x1bT/"\x1b\xa4\xc5\x89\xaf$\xe1\xca`\xa8q\xcct\x80b\xd4\x01\
+\xa0+\x0et\xe8 >\xedq"\xb4\x93]\x85\xf0\x03\x98V\xe0\n\x00\xe1\xa0\xe8\x04X\
+\xea\x81\xfa\x04L\xa1\x87x\xe96*PP.uFr\x91\xde\x83\x1f\xde((\x8d\x14\x98V\
+\x83\xa5ZI$\x15\xa2\x171\x82\x1b\x05\x01:\x1f\x14^\x84\x19\x81\x06\x0b\x19\
+\x89\x98\x81\x86N\x87J\xbc`\xb4}\xec\xd1"A\xca\x05GU\xd9fl\x8c\xa9\x03\xe5\
+\xd0\xba\xe4\x979J\xcb\x9c#0&\x03\x8b\xcb\xa2\x83^\x0f\xe3m\x06\x99j\xd4\xe4\
+\x1f\x9d\x0c\xf2N\x13\xee/;\xa5P\x93-|\xfa\x8c\xfb\xd7\xf2)r+\xf4\xb9\xbc\
+\xcd\x86\x98\xbaS\n\\vJ\xdaV\xf6\x02\x07\x99\xa3\xaa>;\xcde\xe5\x18\xe6\xb2\
+\x9b\x99\xcbA\x80\x0fg\xfb\xbd\xd3\xaa@\x83\x81\xd9$#\xb4s\t\x0f@\xdeI\xddbC\
+\x0ch\x04\x1a\xe2\xeb\x00\x00ZhR&2\xf69\x8c>\xa5\x13\x95\x9c`S\x1d\xdc\xc8:(\
+\xc62vR\xe8\x17\xba\xc5>\x19C\x00\x00 \x00IDAT(\x9b\x15\x03\xd3\x16^4[)\x12\
+\x18]\xe9g\xd3\xda\xa0\x91\xe4p\xf7\xe2"K\xc8\xf1X\xf4\x889N\xf38\x9b=\xa3f\
+\x1a"\xdc\x90\xf7ohP\xe8\x13\xa4:\x9d\xfb\xf4\xf5w\xb6\x8cJ\xc4d\x8d<O\xc6\
+\x85\xf3`\xcb\x88=\xd5B\x94\xc1pC\x17\x91\xca,\xc7\x82\xecVOQ\xab\xaa\x99\
+\xc6(\xf8\xa4\xe0d\x83$\xe7<\x1b\x15\xb6\x13\xb1DCH\xe5\xc7\x0c\x01\x9a\xe2e\
+\xe0\xc2\xde\xe6A\xcd\x81\xde\xd1\xb83<\x93\xbc\xd6,\x12\x82\xd1\x8c\\7\xa2m\
+\x1cZ\x80\x8cF\xf0\xe9c\xb9`\xcf\x820\x9b\xed/{\xe9\x84\x9e\xd7^\xae\xee\x84\
+\xb6F\xdf\xb5\xaf\x890a\xc0y\xb4\x80 \x1d\xafx\xd2\x9dZ\x01\xbe\x1f\xe6E\x8b\
+\xa8\x81\x88\xe68]#\x1c\xc4\'\x90\xf4\x90\x04Op@\xb3x\x02@^\x06\xb0\xfah-D\
+\x1c>\x80\xa0\xaa\xd9\xd7\x16\x07\x0e\xf4\xf7\x11\xa9\xc8 \xe6 \xe3\xb3\x15\
+\x03k7O$\xfa\xd1\t\x17\xa1\xe4\x03\x90,\x9a\x02\x082\xf6\\\x17Y\x9e` \x81\
+\xdc\x02\xe1+\x00\x1a\x00\xb1\x97\x82O@\x9c\x81T\xa9&S\xa1\x1a\x81\x8e<\xa6\
+\xfe\x13\xe1\x06\x10>\xee\xa0\xf1\xde\x89\x14\xb1Q\x88\xfa@4\xb2SP6\xacC\'Zs\
+]\xca\x93\x02&\x84\x8f\x88\xd6\x12\xe8\x92\x06L\xba_Q\x0fq\xef\x11\x84z\x08\
+\x1e\xb6d\xd5\xdc5\x15V\x9e\x01\xa8\xaf\xf9\x82I|\x00T\x01p\x99\x1a-\xe6\x1e\
+r\xa4\x14\xf0\xe9\xb4\'\xeb\xd8H\x8aP\xa0\x0f9`\xcdV\x19\x86\xc3M\xe2D>\xf2\
+\x9d\x80\x9e\x9c\x83\x06\x07\x8aJ7\xb9b7(\x82\xee;\x03v.\\x\x06\x8a\xfe\xa8\
+\xf0\xe5V\x83\xb6*\xa6\xf6\xb3\xe0o~\xb1\xbc|"H\xd5\x82:yh3\xa4\xd9A\xa2\xe8\
+\x9d\xc0\x12]\x8dh\xd1\x8a=.\xbc\xde\xe9\xa0\xb1\x11\x85\xcd\xd3\xee\xd2WA\
+\x96\x84\x9d-\xda\xe2\x00\xd7k\x88Gp\xa8<r\xeb\x13\xe6\x91Y\x10\xf6?\xe1 \
+\xf0\xaaU"\x0c\r\x8a\xef\x13S\xab\x0fn\xa6y\xd0\x0b1\x00\x9a\x87R\xe3\xccY\
+\xa9[\xce\x9a\xd2\xe2SL\xe7j\x07\x900t\xcf[.(IMl#\xc0\x84\xac\xc8\xc7\xf8\
+\x81<>\\\xd0_\xa9 \x92\x98\xe4\xc7\xa1\xff\xc9e\xab\tt\x17\xb0\x04FZ\x83b\
+\x07\xb1\xbb\x00bv\xd0\x02\x90\x9a\'\xc4p\x13\xd1\x1auh\x18\xc5\xe4X\xd0\x9f\
+QA\x86\xc5!\xe9\x03\xf6;B\xee\xd2\x0c\xac\xb4\xa0\x848\xae\x8f\xcc\x9b\x8f\
+\xdc2\x94\x8f\xca\xbb\xc9\xe0\x1c\xb5\xefi\xc4\xb7\xede\x1e!u\x17\xd2b`\xd4\
+\xa1Q%x\xaa\xec\xa9\xd2\xb0Y\x0f\xb5\x11w\xa1\xe7\x01\xe2\xbf\xf6\xd7\x83\
+\x87\xd6_\xf2\x90\x92Q6@\x1eUx\xc3\xb9\xe2X\xa02\x89H\xd8:B\xb7\x00\xe2Od\'\
+\xb4-\xb3\xb6\xc4v\x9b\xf8\xe6\x93dMv\r\xea\x9dZ\x07"K\xd3H\xc4\x08\x08\x1b\
+\x01.*\x00\x17~\x114\\\x8cH\t\x0eP\x1e\x07$w>\x18\x99\xc9\x06R\xea{O\xeeq\
+\x9e(\xc6&*v\xbb\xc79\x88\r\xe0( \xe2\x97\x00\xa8Tv\x95\xa3?\xc2\xc8hK\xa1\
+\xb2\n<\x94\xd4F\x9b\xcbq\x04`\x1b\xe0\xc2\xff\x92h\x12<\xd3\x1d\x84\xbfz\
+\x1c\x80\xd2\xa8~\xe04vY<\x11\x1b\xee\rR\xadO\xd6~\xbb\xdaS\xe7D\xb3\x11l\
+\x99\x81X\xbd\xe4\x079\xe1\x89C^\x03\xeb\xbcy\x87&\x96\r\x8f\x10m\x18\xa2\
+\xa9[z\xb5*\xe9s\xd4\x05G\xaf\x9c*\xa9<e\xc5\r\xc0}\xce\\\x86\xd64X\xce\xa0\
+\x0c\xf2\xfd\x04|0\xaa\xc7_\x1bq\x06\xdf\xcc#\xd9\xc5b\x0c\x05\xd4\xdc\x077*\
+\xc8=x\x19\x84\x7f\xde\xeb\xc93\xc8\xef*\x05\xe6hY\xc5^\xea]\xa38\xa1fmVp\
+\xe2\x82*\x80\xccpF&\x90e\x8a\x02G\xd3C>1H\x90`U\xe4\x0e\xf4\xf6\x01)\x82\
+\xc4D\xe4I\xf1\xfa\xf1HE\xe2\xe73\x7f\xcf;<\x81$*\x03\xbcn\xe4\xda\x89#A\x15\
+\x90\x9a\x86\x1b\x89\x98\x89\xe2q\xfd|\r\x962\xe0\x91\x8eGG\x04\x98\x990lTi\
+\xb9tN=hh\xaf\x16\x90:8\x95\x17\xb1\x1e\x00\xa0\x17\x9c.i![\xfb\xdf\x1c\xd4@\
+\xebj\xccr\'\xa5\x1b|\xbe\xf6_r-\x11A\x8f*K\xdeA\xa0\xe3\xe5\xad\xd6\x1e\xb2\
+`P9N5\x0f\xa0\xea \x9e\xa6(\x01,\xb8\x04\xdb\x1f\x87\xbb\xa39\x1d\xfd\x1f\
+\xb9\x84\xc4\x1aqM\xe1\xd2E\xf8\x0f\xa8\xdf\xd85\xe0~\xeb%\x1f\xcf\xfe\x97\
+\x00A)\xc4[\xcf\x01\x10\x05\x99|2\x1fUN\xf4\r\x89\xbc\xe3\x85\x86\x03\xa5\
+\x11\x9e@\x01\xcbRp/\xb1]\xd9OJN\xc7\xee\x88\x84\xf3\xae\x9fF\x87\xd8\x06g\\\
+\xd3[\x81\x03\x95\xc5\x1a\\\x84^\x0e\xe6 \xc5*POk\x83I)\x93\xd5\x02E!\xb5FKZ\
+\x13\xce\x87w.\xae\x1e\xb5\xeb\xef\xd8.Xn\x01\xba/X\n\xb5\xf6\xa8IAl8\x95\
+\x82U\x17\xd6\xebxX\x0b\xdbP#\xa0\xa8\xfcy\x15\xc1\xe2\x87U\xd7\xb8\x08\x1f\
+\x9c\x83\xd6\x03\x060\xdd\x00\x11\xd97\x10@\xaf\xd3"\x92\x87\xe8\x89\xd8\x02\
+F\xe2Gs\r\x81\xae\x80\x1f\xdf\xe2 \x9e\xd8\xc2\xfb\xd6\xcd\xffh\xefQk\x16"c\
+\x07\xf2n\xfb\xe1N\x9d\x14\x0bI\x81\xe7\xe3s$\x83+\xd8\x84\x80Z2\x9f\xf1\xc8\
+C\x88\x0b\xf1\x01\x05\xc4q\xa0\x81\x89-N\x11\xf3\xa1Q\x1c\x0eI\x07S\xc2\x8a*\
+\xd5\x12D\x0b|\xa6 \xd7\x88#\x0e\xf0\xf6,9\xc2\x80\x92\x9a\xb1\xa0u\x9ee\xe0\
+\xe5\x97q$b*\xe7\x8ah\xbd\x86\xdb\x82T\x9b\xca\x041\x10\xb9\x9c\xba\xe0\x90\
+\x9dH3C\xa1\x11\x08\xa3t\n\x99\t\xea."@B\x1e\x8d\xe3T\xa3\x07\x8fL@l\x9f\xf7\
+\xbem833>\xfe"\x1cX\xc3\x9cM@<\xb0\xde\x17{\xcd,8\xe0\x9fm\xc4X\x95\x16A2\
+\xc2\xf92\x8d\xb1Z{p\x11\xac\xec\x15B\xca\xd0N\x93*]\xe0H\x9d\xd8\xb5\xf0]D\
+\xd5\x17fP \xe2L\xbeb0\x01m&\x17w\x81\xd9\xa1)\x8f\x7f\xa9\xeeO\x89\x88\x014\
+\xffD\xea\x81\xed.\xaa\xa7\x94\xd0\xd6\xb3\x194\x1c`n\xf1\xd0G)Uf]\x98\x01,\
+\r\xf2t>g\xa9\x86v\x9b\xd7\xc5#n\xd4\x8b(m3\'R\x8e9\x86z\xee0\x82\xf8Kl=\xe0\
+\xb6\xc4\xc7\xac[4x\x9f\xae\t\xa1\xb4\xc7F\xadL\xb3\x9a\x84]=\x1f\xf5\x06\
+\xc9\xd4\x9e\xe72v\x99\x11\x7f\xd1^q\xcdOuI\xc2\x01\xd01;\x04\x02xZ\x82\x82j\
+;dLF`\xc9/L!E\x90\x95#C\xbf\xeb!\xac\x13\xd0\x19\x97\x1e\xac\x8eN\x0b\x03\
+\xf1\xc0\x98\x0f\xd3+\xb4\xd2@\xf9F\xf3/\xb4\xd2\x80\xaaI\xf3K\x94\xcd\xa7\
+\x0boqd2\xc9\xe3\xb0\xe7[E\xa6\xc3\xf6\x92\x02\x1e\xe2\x18+:G\xa7RHJ\xda*\
+\xce\x1b\x91\xf6\t=m40\xd0\xeei\x89\xcb\x08\t\t>\x88\xb1M\x90\xcb\x0c\xa4\
+\x1c\xfe\x89\x16\\[1B\x17]p\x00\n\x14r8p\xfc\xa2\xc3\x92L+\x9d\x94\xdf\xa3\
+\xffI\xba\xc4C\x99\xb4\x8a\x0c\x8c\x82\xc0\xab\xad\\X[\xd4\xd7\xdafM\x88x\
+\x16Z\xa6\x12m\x8f\xcf\xe1r\x0f\xc0\x0c\xc1\x0e0\x0f\x0e)\xf2\x02\x81BH\xf3*\
+l\x02\x96A\xd3\x01\x91(\xfd\xf1\xb8\x13XN\x8d\x92\x85G\x96\xec\x8d\x02NM"\
+\x939z:\xa9\x97\x1b\x1cZ\xf04\xc8TN\x85\x8cc\xa4\xe0\xc31\x04N\x81d#\x8b\x86\
+r\xd8\xaa\xca@._\x9amAg""\xcb\xad\xed2J)\t\x19i\x0e\x17:-\x033\xbb\xc3\x00c\
+\xad-R=G\xcc\xc1r^\x0c\xc0\x83\xd0\xb4\x16\x0e\x94\xa2!\x1b\xca\xef\x15\x08\
+\xac\x10{\xccj\x81\xd1\x0f$[\x0c\xcc\xfd_\x99@\x8d;5\x00L\r\xa0\x1bn\xfb\xa3\
+\xe8-\x1c\xc4\xdb\x04\x82\x95D\x80\xcdn\x1b\xa3(\xa9\x0c\x85\x8a\xba<\xbf(\
+\xe7\xce\xba\x8a\x85\x89e\x13\x02\x02t\x08\x18b\xaa\ra\x88\x0b\xef\xd2\xeb\
+\xa4y4\xa2Aq\x90\xdc\xdb\x10\xdf_q\xd9#\xee\xf8\xd4\xbb\xd2\xe6&G\xc0X?BC\
+\xa4L/\xb7@l\xcc!Q\xfd\xfaC\xa7\xc5\x10}\x98\x86\xcbb\x10O\xe7\xb6\x93\xbeD\
+\x0e\x93FY\xa2+\xad\xe8\x01\x8f\x7fsa\x85g\x07\xf6S\x86f\x04\x9dDsGa\xe5\x11\
+\xdau\xef\xb8#\xf5\xf0G\xb3\x1d\xb1z\xe6\xc8\x99(a\t\xc97\xf2\xd1\xf5I\xf1\
+\x19g\x85(\x83\x14\x15\xc2J\x82\xc0^\x1a\xf5\xa6~\x95%\x14!QT\x08J\xe8\xfc\
+\x9e8\xbe\x9f\x1f\x85\xf1\xe5@\n\x17\xdcQ(\x07<\x18\xbb\xab\x7f\xcc\x91A[\
+\xc6y\xb5C\x10\xb9\xf8\'\xdck\xc4Wz\x9d\x80\x05}\xd89\xc9\x0c\xb3rV\xabj\xba\
+U\x07\x04C\x8a\xd5\xd9\xa2>\xa9he&\xcd\x1c\xa7\xa4\xa8\x96\xeb\xcd\n7\xa2\
+\x84\x1f\x14\x9b\x06W\xb1cx\x9f\xe3>\xd6\xbf|\xd2)\x146p\x91\x07-\xf29\x9c\
+\xcf\xa7<\x95\xceC\x86%\xe3^.\t\xa3\xca\xad\x10w\xb2\x03sr\xb1\xa3\x9eaU\xeb\
+\xb0C\x07&3R5\r\xabq\x08\x99wS\x138\x87\x00\x92\xef\xa3|3\x9c\x96\xab\xca\
+\x7fo\xd9\xe0\xe4\xf8\xeao\xf1L\x8a9\xde\x92\x12\x88\x83>3\xef\xad\x0c\xce\
+\xb8.d\xcd\xe4\xc8g[\x01\x10d\xe6\x17\xa6\x97\x88-\'\xc8kc\xf1\xc3&d\x06\xc7\
+\x85\x9d\x91wI\xa5\xd5\x8e\xba\xfc@\xae\xb4\x0b\x89\x97L\xe6\xe1V)\x04\xe5w\
+\xd9\'\xc0A\x95\x98.F\xa3\x1b-L\xd5\x8d\xb2(~\xf5\xfd^\x9d\xce\x1aY\x9e\xbf\
+\xb8P\xfc^\x851l\x97\x90n\x91\x07\xd3T\xb5\xaa\x0cu\xdbIQ\xc3\xbb\t\x02\xaf\
+\x0ct\x9b7\xd9_y\'"\xd2\xcd\x90\xe4\x1e\x00\x93}\xf1\x1d\xc0l\xf4\xe3\x8d\
+\xcd\xca%\xcc\x95]J\xb4\x08\xeb*\x1f!\xca\xb3\xbc4\xac\x0c\x08\x86]q\xea\xbc\
+\x0e\xc3\xfb?\x8aG\xee\x11h\x08\xe8V\r>\x8f\xe4\t\x14\x14\x1c\xc4r\x9e\\4\
+\xd5\x17j\xeb\xdcy\x88\x1b\xd7\xfe\xab0\xfd$\xbc[\xeb\x042\xc8\xd6\x11\rGW\
+\xc8E.Q\xdaX\x19A\xfb@\x92\x90\xc3\x08\xe8\xfa\xd2_\xfc\xd4E+\xfb\xc2j\x07s\
+\x0f\x8d\x0eh3xZ,fhXU\x9a\xe3\xf1\x1f\x0ez\x05\x07\x9ee\x94\x83\xce\x01\xb0)\
+\xe2\xd2\xd2\x12\xcb\xd3\xebuF\t=\xc1\xad>S\xae\x1d\xe3\xa242\xa0v,\x9boc}\
+\xf6!\xf6\x1c\x93\xa0\xa1\x0e\xb1\x10\xb7\x9b\xcdu\xecLE\x96\x97\x1b\xcdc\
+\xa7\x05\x04R\x16\x07\xd7\x90\x12\x87\x8c\r\xcc\xa2W\x1f8p\xbd%\x14\xba\xb1\
+\xa2\xb2\x97\x19d\xc91#?Yr\xc5D9sN\xac0\xc6:\xdb,\x9a\x9do/1\xc4\xa8\xa3VS\
+\x07\xd8\x94a\xb5\xe1=\x896\xf90C\xab\x088L\x05\xbd\x98/\x86\x95\xdf;0Lo\x9a\
+\x97\x18\xcc\xea\x1e\x02\xb2\xb0\xd8\x0e\xc7sG\xc9\x16\xc4D\xba\x89]\xd1\x83\
+5\x931L\xd0"I\x07\x9f9=\x96rA\x9e\x8dZ!\xcb&\x0b\x86\x80ZR\xb7\xf0\xd1\\\x15\
+@P\xa9\x1eL\x9fK\xae\x159z\x1b\xb9\xa2\xc5x\x04e\xdcS\x14\x00J$8hN>\xa2\xf3%\
+e\x86\xb3A9D\x93\x07\x19\xab$\xa7\xa2c\x15\xd4\x1b\xf0\xf4\x8fJ\x96\xdd\xcb}\
+\xa2x/G\x92\x07\xa8\xd1\x04+b&@\x93>\xf1\xb8\xf5\xfe\xd4"\x89\x05\xe0\xfd\
+\x93ryyU\xa1\x18t\xb1\x19!t%%\xab\xc0\\/Y6\x8c\xc9\xa4\xf0\x10&\x05v\x10\xba\
+V\x1b-MhA\xd0\xd5m&\xb5u\x18U\xd1\xd7b\xf2\xa7|\x9a\x85yE*\x04\xf0d\x01\xab\
+\x84\xe0\xb97I\x9eQY\xa9\xc0\xfc\xbc\x15Z\'\x90 \xab\x0fq>\x82\xfb\xd9\xc7D\
+\x07nY>A\xe1+\x0b\xca\xc6\x01:p\x0cZ\xefk\n\x13\xa9.S\\\x1a%\x01\x85P\xbfB\'\
+\x83\x03#\xa9z\xeb\x82RR\x9d\xc4\xe4\'(4\xb5\x05\x00G\xffE*T\xd9\xd3jL\x9d\
+\xb9\x1c\xc7\xb0\xa0\x00D\xb2\x90/\xb2\xd0\xf1\x8f\xea\xc9\xa2\x12\xac\xebi\
+\x07\xf9\xd4\x93\xc8\xeb\xe1\xdeb\xc0\x01W{\xa6\x14Rd\x85\xce\xb3W=\x8c(\xec\
+\xe6\x08\x98\x0cs\xa8)\x85\n\x17\xa4\xa9\xa4\x07\x9a\xbf\x83\xc3\x10K\x05\
+\x00\xc2\xd3E\xb2\xa7\x0f#\xc8\x821W\x86\xb8\x12\xe3\x83\xc8\xc9\x1a\x9f\xf4\
+\x16\x81\xe0\xa0\xab\x11T&`,\x8d\x00S,\xeej\xab\x02B\xefy\x19\xad\xd5-\x92\
+\x05Q\xcb\xbfd\x94\x88\x80%\xd2\x87D\x00\x0f\x9e\xdf\xea\x1aYB\xd7\xf6\x15U\
+\xf4\xd84,\x1c\x84\xbb\x15\xf5\x0b\x94\xa9x\x8a\x8e\x97Y\x89z\xc5x\xach\xc0\
+\x1b\xaa\xc3\xcb\xb8\xd0q\xea\x99-\xf5\xd2\xac@\xd3g\xf1\xba\xbb\x9e\xcc\xe3\
+\xa0\x18\xf8\xaecyMS\x9f\xe8\xb2\x9cq\xad\x92\x00\xd8\xd3\x8b\x10\xaf\xf1\
+\x8as\x0f\x13Z\xc70\xb3\x93\x96\x9f\x1f\x9a8\xe8\xb4Wc.\xc9\xa7\xb6e\x9c\x1a\
+Z\xaf\x01\x8ewoD\x19c\x9eeh\x98``\xce\xe0\x19\xa7\xca\x0b\x84\x19\x91H\xc7M\
+\xd6\x1bI\x9c/W\xdb\xe7\x9eF\xcaw\xbe~\x8e\x93\x9b2O\xb3\x02\xedq\x92\xdb\
+\x1c\x80\xbc\xa0\xba\x84P\x0c\xfc5\x17yd\xb1\xa7\x01Q\xb1R\xaf\x10\xc1&\xef\
+\x06k\x89\x8c\x0f\xdbE\xb7\xc5P\x00\xc1\xd0>\x01\xd5\xbcm\x90\xbe>/R\x0cxB\
+\x94+\xc5\x01\xbf\xac \'hz\x9er5\xd4\x8f$\xd7Za\xe6\xe3\x08\x85\xa8\xf8ZZ\
+\xbc\xf4Vi}+\xb9w\xc0\x95\xc1p\xef\x05\xb2\x88\x87?D\x83`EQ\xd0<\x96IM\x8f#\
+\xe6\x19\xa8\xf7\t\xeaCn<\xe3\x11o\x9b\x04\x91\'\x7f\xd8E\xe7\xf9\xdd9O\xd0\
+\xec\xbd\xe2:+zL\xc9,\xf7\xc8\xa5yH1eU\x82\xa6\x03J\xbc\x8e\x03\xbe\x10\xcf\
+\x93\xf8\xe6(\xa5\x9be\xa6\xcc+\x92\xaf\xbe\xac.\xf6\x19\x18}\xbc6\xd8\xfcs\
+\xf2\x14G\x0e\x04\xcb\x9a\x1f\x92\x1e5\x14\xe8pc\xc9\xc4\x01D7\x93w\x08BH\
+\x1e\x07]\x03Bi\xec[\xba\xeaU\x05N%\xc81/R\xa5k\xc3V`\xcc6\xb1\xa1\xf5-;c\
+\x08\xc0\x9aB1PXic*!\x08\x9c,\rU\xf7\xb6pF<\xbad\x16-\xfc\xd2\x15p\xf1\x98\
+\xb7\xf5\x04T%?L\xce\x9a\xd9\xef\x82$\xcb3\xe0!\xf0\xba\xc2 `\xd3\x04\x84K1B\
++X\x8c\x1d\xaek\xda\xa1F\x98\x1e\xb4\x83i\xeal\n\x04 \xc1t\n3O\xde[\xac{\x05\
+\xce\x02 \xbak#\xe3$K\xc2%l\xaeM\x10,\xc7\x16\xa0\xd7\x17aP0\x1c\xa4G\x82)\
+\x8f\x19+\x81\xcd_~\xecW\r\xc8\xce\xeb\x84\x8e\\\x04X\x0cm\xd5\xe4\xbf6\xc5\
+\x1a\xfeY\x07\xd8ph\x8a\xdc9\x03\x04+\xb2Z\x8c\xa4\xaf\x18\xad\x0cO\xf8GR\
+\xd5}e%\xdbJ\x06\xa2\x06l3\xc6V0d\x9e\xba\x89$"\x81\xe0\x80\xee\x05\xf8(\x8a\
+\x82\xa6\xaa\xa9i%\x16\x8b\xf2A\xc7A\x86#\xed\x95\x1a\xa5:\x86\xf4\xe9\xa0\
+\xda\x80\xbbp"\x8eD*h\xcd\x90sI4J\xbcQ0\xebS\xe5\x03\xa9O\xab\x04s\xf3\xae?\
+\x81\xa4\xcc\xbb\xda\x88\xea\xc7\xf2\x99\x19\x90\x0e!\xcb$I\x0c\xe7\xad)|Q\
+\x90\xc6q\xed\xecB\xe6\xd87e/\xed\x9c\xd6\x83V\xc3E\xe5\x15\xf6\xe0[\xf8\x18\
+\x9a\xc9\x83>\xfa\x89j\x8d\'\x834\xc7\x99*\x03\xcd\xc5#\xe4\xaa\xf5\x8c9\xbf\
+J\x08\x08"\xd6%\xea\xb2\xb7\xa8\x1aC\xa2\x9c\xaa\xa3\xd9\x97a\xf2(:t\x10\xac\
+\xd4\xb9B\x12\xa4#H\x83\xcb\'#Bf\xfb\xd3\x06\xaco)U\xea\x80Z\xc3\x90.\xda\'\
+\xae\x03\x14*\xdc/\x96IL\x0c\x01\xbbLR\xf8`L\x0b\xca\\\xeb\xb5!\x1c|K5\x819=\
+\x07\xe4\x0f\xe6\xba\xb3v^\x98\xc9\x84\x1eK\x8dQ.\xc6\x93\xf5\x80\xa6\x9b\
+\x9c\xc3/{q@\xee\xf0\xab.\x08\x14\x88\xcc\xeb\x04\xc5]\x1b\xc3\x8dp%\xc2\x8e\
+\xa1\x86\xe3L\x96\xfaV\x80\x00\xbb\x87\r\x1f\n\xf3Q\xc5\x99=f\xe8y\xe5\xa2\
+\xec\x83\x96^c\x13\xb3\x89G\xd9\t\x02J4\xf29\xe8\xf40\x8f\x14\xdd\xbaK\x15T\
+\xa9\xf9\xc8d\xc3\xebo*Y\xa1\x03Ez\xed\xf2{&(\t\xda\xfe`/\xc1\xe6\x01\xb9d\
+\xaaC\xe1!~A\xa3\xe0\rH5\x10\x14\x000O\x90\xcc\x0e;\xaf\x0c\xb6\x1cA\xb5\x9c\
+Dp\xbc/"T\xbb9\x07\xdc\x01\xf1\xd0o\xe4\xdd8\x01\xef\xa2\xec\xb3\x9f%i\xc5\
+\x85_\x17\x97\x01\x8b\x08\xea#\xb9\x89\x80\xa0\x923\x04N\x13?!n\xac`R\x86\
+\xd4\xd0b\x00\xe9)\xa1\xd7%L\x98\xe8\x1d\xce\xc3\xbc(\xa2\xfc\x1d\x80\xe7\
+\x7fx\x9ep\xd9j\x82wO\xfc<\xf8\xf6\x13\xdb-[\x8ew\x06\xfe\xf6\t\xa0/-\xf2\
+\x8e\x1dz<\x95\x10\xc0\xc6>\xaa\xb0B\xfc\x89\xb1\xa0\xc5D\x81a|z2\xe6\xb9y\
+\xca\xd3\x15\xfaC\x99\x89|\xeb\x1cC\xfc\xf2\xe0\xe4\x1ddKh\xbe\xb2C\x83\x00\
+\xc9\xc5\xe1b\x80f\x8d\xd9\xb5)\x1b\x01,[\xdd\x012\x12U\xb4\xf1\x9a\xf8\xb8\
+\x91\xe7\xdc8\xf1/^i{l\xe97\xdb\x17+\xf0\xf6\xb2!CF_\xd3\xe0\xe4\xa5k\xff\
+\xaa2\x13\x95\xa8D\xc3\x80\x8dr\xa7\x08P\xcdA\x9a"\x08\xce\xcc\x18\x1a\xab\\\
+\x15\x96z\xa26\x8fYs4\xd8\xb8\xaf\xdcb\xfd\xffq\xb1\x08\xb1\xa60n\x07&\xf9\
+\xe1\xfd\xact\xb6\x16\xcf\x0c\xdbq/\xcc\x0f\x9f\x140\xc3\xcd\xc7p\x07\x848\
+\x94!\xdb\x9a\x94\xee \xacN8\xd4!\x943\xb6\xa4\x14\n+\x95\xf4\xa5\xbfVs\x08?\
+}6j\x19\'\xf6\x91\x1a\xc0\x9e\x82\x92\r\x90\x8d-\x8c}\xaaB\xa9\xa4\xb4RD\xfa\
+\x8c\x04N\x03\x13\x01\x84\x90\'\xe9\x18\xa8\x19\x96\x1d\xb0\xaf\xd91\x92\x89\
+\x1aw\x91\xd8\xc8w\xe9+\xba\x98/A\x9cy\x8b\x8a9\x90\x1f\x90\x93\x192\xa9+[*6\
+\x80\x02\x04\xdd\x15\x8d\t\x93\xa8\xc6\x18\x9b\n\xe2b1R\x99&\x00\x8d\x08\xf2\
+2\x11\xa2\xb2\x97\xca\x19\x99q\x8d\xce\x8b\x04\x87\x96\xdd\x88YHv\x84~*OjZ\
+\xdd\'`8\xc8\xc8/\xbe\x0b(\x9fAO\xc1wq^@\xc4\x15[\x97b\xd1r\x82\xf2\x1dU*\
+\xba\xe5\x858\xf4\t\x02\x94wcF\xce\x9c\x12_\xf3:O+I\xce\x8d*\xda\xe4\xda\xb7\
+\xd0 ~\xc2\xf8GT\xf0\x07\xd6\xfb\xe0\xc27\xce\xf0\x8b\x86:\x0e\\\x0b\n\xdd\
+\x9f\xe0\xf4\xc6k\x81<\xfd:\xa2\xc0\x81N5\xfe\xe4\xd8\xf0\xbe\xfd\xca$S\xff\
+\x11\x01D!p\x96\xd1M\xc7E\x1b5 D\x11\xe2T\xdf\x80q\x97\xe4\xea\xc2G\x04\x11k\
+\x9a)p\x00\x00}\tn\xef\xf8\xa7\xefB\x15M\xcf\x0f1\xe8\xb5lC\x8c\xd3\xb8v %\
+\xef\x89\xc4\xb1>\x182H? \xc4K\xd9x\xe5\xce\xb1"\xb6\x14Ia\xd5>T0\x1b)\xa7\
+\xf2qQ!\x02\x00\x7fZ\xb5\xc9\xd2\x94\xd2|\x82\x06\x07V5Tat\x0e\xd2\x0fe\x1d\
+\x96o}\xd4\x14>\xf5\x97\xf6qmfT\x82\xa9\x91\xc4\x1b\xef\xd4^\xd0$\xd0b4\x94\
+\xbe\x07\x86;\xe5F|\x00\x19\x08\x02\x82\x15/M\x1dQu2\xab4\x06\xed\x1f|\x1ds\
+\xf7UMSc\xbe\x86Z\xf9R^[\xc1\xc6\x00\xad\x9a\x8e\xb4\x14r\xde \x1d\xab\xb8r\
+\xccFi\xa4\xe9v\xc9\xa9\xc4\x13P\xbc\xd0\x03\xc9\xb1\xc0M\xc7ZZ\xe5\xdb\xe5q\
+\x10KxzEa\xe9q>\x17\xf5C\xfc\xe0y\x98\xa6\xf79VY\xc5\xba\xcc\x83\x0f!\x1f9\
+\xaazDu\xf9>\x1b\xa6\x9cIi\xdaY\xf5\xf2\xc1\xe0_X\x99\xa3\x9f\x90\xe5\xc1\
+\xc9]"\x8a\x00V\x80\xa0F\xe4\xe1\xfe\x9cB\x03\xc9T\xba\n\x1c\x12\xc8\x08\x10\
+\xf7\xa0\xef\x98R\xa20@V\x80\xd6\xef\xbd\xaa^\x15\x7f\x82\x0f\x1ec\x95\xaf\
+\x12\x16\xea\x10\xcf\x9a\x7f\xae\xc4\xab\xcf\x8fd\xf3\xd9&\x9ekv\x1d\x06>\
+\x0e\xe1\\5t\xc4\x82\x10\x15D\r\xcf\xbc\x08\xef\x1c^v\xc3\x10hc\xa2\x8f\x00\
+\x8dO`\xa0\x14\x95\x17y\xbcc\x9fB\xb7\x03y\xc7hq\x9dW\xae\x07\xe8l\xa1h\xc5F\
+\xe7\x03\xd92hb\xf2\x06BG^dQI\xc6\xe2\x07\x8e\x00g9\xdfM\xa4\x96DdG\xebw\xc8\
+\xdf\xa3\xf9\xc3\xfc\xc0\xa3\x18\xcct\xcc\xdf\x17\x10\r\x94\x11E$n\xe448\xda\
+\xfe\xf0\xfc\xa1\x82\x8b\xdc\xbbf\xd3T\xa1]@\xf4N\x1b$\x86\xa1G*\xda\x892\
+\xac\x8e\xe6\x8a#\x00\xe7\xb7\xb7\t\xd9\xc0@\xa3\x9d\x0cT\x95_\xad\xe9\x0ey\
+\xd8\xcd\x8f\xc7\x99\x1d\xefW\x83\x91\xc4\xb7\x97`\x8cf\x82BA\x91O\xdfT\xcbi\
+\x16\xd0\x8a\x9b\xbb\xbe1\xae\xb9\x8b\xbaAC2^\xe1a\xfd.\x82\x99\x97\x14\x13\
+\xfa\nE\xd2q\'H\xf0g\xcarj\x00\xa1LI\x11H\xf2\x1e\'\xa9b\xd6\xca\xe3\x91)D\
+\xa9\xb2\xc5\xfbX\xb6 \xca\xde!\x8fP\r>h\x02\xa2\xdb\xc9\x88\xf0.\xfd/[\x11\
+\xca\xa5\\\x0e<Qv9\x1bL\xd2\xa8\x15\xc8\x96r\x91Im\xc1\xb7\xaa\xb6\x84\x00R\
+\xbdc\xffU\xdb\x98T\x85G\x08\xc8\xe1=\xa5S{\x8d\x90\xd1\x92d\x8by\xb25\xe6Ch\
+\xae\xf9\x1a\xb4\xa2\x00\xf0}?\xd6L\x99\xc0\x86\xd6\x03\xd7\xfc\xe9*\xd9\x05\
+Op\x80\xce\x87q\xde4E\x9c\xa8Z\xea8\x96*  +\xb3\xb4s\x88\x10\xa0\x15\x16\xf2\
+O\x03\xbb\xe8X\xe6\xd2\xc9x\xa7\x93Fc\xa4b\xc3aV\xe44\x9b\xe4\x03\x88\\X\x13\
+\xb5TAZ\'\xa0:4R\n\x0cs\xc9\x87|I\r\xc4)\x035 \x82\xe3\xf6\xb6\xc3\x08Krk\
+\x1dm\x87\x85\xa6\xee\xee\x184\x10/\x1c\x01\x04\\\xa4@2\t,\xaaP\x9b\xd6\xa3\
+\xca\x1c2\xa67\xeb\xfdR\r\x96\xad\th\xfei\xda\xff\t\xd0sL\xe3\x87\x8c}\xd6{\
+\x9c\x83(I!y\xa0\x08\tV\x00\xe2=h\x8b\xe9\x19\xfd(\xa4D\xe1\x99\x84\x85R\xa9\
+ Q\xf9P\x94\x10\x15\xdf=\xa2:\x00\xa9\x17l\xcd\x1d\xfe\xcb\x99\xc4\xd4\x8a\
+\xb0Nt\xcaAN\x1d\x94\t"\x82\x07\xa3\xda\x1a\x10\x17\xbe\xd5 ^\x9b\x85}E\xee\
+\x18J\x0e\xdb\x8a\x88YIq\x90\xae\x9c\xf3-\x1c\x91\x1b\xa9* ,5a|R\x16h\x1b\
+\xd2\xfc\xb47D\x07p\x85\x80\xebp\xd6\x8d\xce\rG\x00A\x91#S}9\xca\x15\xf2"S!\
+\x94\xd7jy7\xc5\x1a4=\xef\x14\x18\x99\x81.\x1b\x13}!\xe8\x10\x1b\xc1\xf8i\
+\xacB\xb2\x0e\t\r\xc0\xfc\x164\x1a\xa9\xf7\x8cI\x12\x880\x06\xd4\xd1\xcb\x08\
+\x08\x04Ho\x00W"\x9dAd_\xa8+PF\x80!\xc7\x02*\x92\xcd\xb6\xd4\xa7\xa1\x07\xa0\
+^\xe2\xca\xeaC\xbbX$U\x81W\x8d\x90\xc6\xb7(\x16\xff\'9\x9cX\xe3 6\xd3P\xe5X\
+\xb8\xbc\x07\xb9\xd4@\x11\x06\xae\x88\x89\x8d\x8a_\xa5I\x14\x91\x13\xfd\x8e\
+\xc9\x83\x10\x93lUM\xd0e\x89\x07|}\xa0p\x12\xd3I\xba\xbc\xd4\xe7\xec{~Wh\x0c\
+3\t\xc89\x94\xf6#\xb0@m\x80K\xd5\xb0l!F\x91/\x87\xae\x92#\x89_.\rij@/\x1f.4\
+\x8d_T\xf9Xwz\x00c\xc2T  Q@\x17\xee\xf0\r\xc2\x11:\xdd\xe1e\xf6&g\x0b\x87>\
+\xe0\xc2riP\xadGS\xd2\xa6I\x909e\xa7cc\xe7X4\x97\x16\x90\xbc\x9a\x80\xd2\xf0\
+\xd4\x11\xa0q\x81\xe7\x84.\xfe\xaa\x08\xc0\xcb\xcdz\xfb\xb8\xc6\xc6\xc2\xf3J\
+\xbc\xa0b\x98\xe1\xd8\x10\x9a\xee\xe5%yg\x10\xd5\n\x0e\xafK4\xb1}\xd3\xa2\
+\x04\x91KP8\x92N\xe3\x93*`\x15\xa7\xcb\x8c\xcd\x14\xa9x\xc2\x86\xcdt\xa2\xd9\
+\x8aD\xba\xfdm\xb4\xd00|;3a\x01Y\x0el\x05\xa4\xb9Q\xae2\xb3a\x9c\xc1\xc5\xbb\
+\n\xdd\x9e2\x9bhS\xa9x9\x13\xc8V\xa2<\x8b\xe8Th\xd9,\x8axs\xa0\xa0\xf8<\x10H\
+.j\xa2K\xea\x94\x8f~L\x0fk\x08\xc5\x02\xf0z5\x04\xd0\x9f\xaa \xe4m\x8f8\x8d\
+\x9b\xbc\x1d\x91\x03X\xbc}\x17\xe5\x95\xd3\x08\x07\xe0]k\x0e\x0ce@\x08U6\xdc\
+\xd0\xbdU\xe5\x15\xd5\x8b\xe5\xc5.\xd2t\x9ez\x7f\x12\x01\x8e\x1a\x08DU8\x82\
+\x18\x87\t=\xd5-\x00\x808r\xbd\xd0\xdb\xea\x8a\x0f\xb5\x16zE\xb5\xfe\x84\x80\
+\x9b>\xe1\x08\xe2n\x8d\x82\x8b\x9a ,\xf8y\xec\xd1u\xf1G\x1b\xaal,\x8b\xf4\
+\x0cU\xa5\xaf\x159q\xf5\xae;\x80\x98\x89\n\x04Pc\x84\x8cM$/\x1a\x91oK\x1bx?r\
+13\x95\x1b\xf6\x088|\x10\xa4$\x0e\xec\x01\x9c\x7f\xfe\x08\x01\xddx\x0c\xadJ\
+\xfa\xbae\xa2\x83V\xa3\xed\x94\x92*x\x14\x04\xa0\x1f2\xd1\xd3\xcc\x7f\x13\
+\xc9\\MS\x0bH\xa8\x85\xbfJC\xb4\xb6Y\xd2\xd0F\x94\xe6v\'\xa7\x8eo\xbct\x9eT\
+\xb0*\xe4\xa17\x1a\x14\x10t\x1d\xfe\xea\xc87\x95AZgl\x86\x18b\x8f\x19e\x00UN\
+l\xad\xc9B\x00\xdb\x1a\x90\xf4\x84\x0e\x90^\xa257\rJ@8\xcbJk\xc3\x1fu\'0e\
+\xf3\xfcV\xae\xadd\x02\xab\xa5*;\n\x11\x04\x85\xef\x98\xd0?\x91\x96\xb0\xbe\
+\xe8\x9aD\x91%\x03\\\x90J\x9cSs\xd8\xdc\x17\x10\x90Rh\x19\x890$jG\xa2\xb0\
+\x8a\x11\x03\xb1\xe8\x19\x08U e#\xa1`\xf5;\xca\x89\xbbK"\x83\xcf\x05Et>\x0e \
+\x80\x80\xebs%w\xc6/\x97Z\\$Q\xa2\xc6\xb8\x07MTR\xff[n`;v\x1d\x96:7$\x80\xe4\
+\x19\x14\x81CW\x81z\xb0\xd9\\3\xe3>p\xcaAP\xad\xef\x94w\xf8\x15\xbe\xea\x9d\
+\xb4#\xc9I*bF\xc6\x9e\x14p\xbe\x14\xd9f\x1eM\x97\x96^\\\x02\x90\xbe\r\xbfl$9\
+\xed\xf4\x13\x07\x95=\\\xab\x14\x80\x8d\x00\xc7Y4F\xbf@@\xe7\xd0\xa0E\x1fC"\
+\xaf\x99\'\x00\xcb\xa5\x9c\xae\xf6c\xf0\x1a\xa1\nf\xd2\x14\xd1f\x1e\xcbKQ\
+\x08\x86I`\t\xd9&\xe0\xc1\xca\xfc4,\x14yB\x10\xd5\x82\x9f+\x8d\xe5E6\x89\x00\
+sy\x98\xf7\xba\xe5\x9fK{\xa7\x1f\xdf(\xae\'uH\x97\x052O\x11\x17a\x90\xd1\x04\
+\xc9x\xab\x9a;f\xd2\xd25\xa1\x17a \x148\xdb\x0ctq\'PP\xe3O\x16\x0bjI\xa6\xd8\
+\xf7K\xaea~\x1f \x0cDm\xea\xaf\xb7\xa6\xe2\xd1\x11\xb3l\x9e\x87\xf6n\x08\xca\
+&F\xfaA}9d\x8a\xc8\xe3\x8a\xb2\xf38\xd3\xaaGgj{6p\x1c\xcde\x9a\x00I-$R\x17AA\
+\x80<\x1f\x10\x95\t\xd35\x05C\xed8\xe3\x98\x8f!e\x1eh\x17j\xcf\xe4\x93.\x87f\
+\x04X\x87\x93t\xa1\t"\x06\x1c\xb6L\xd4"\xd0\xfc\xa4\x07\x1d\xcf\x8d\xee\x15\
+\xed\xccMF\x8d\x13@\x11`\xd1\x16\x08 H@\xaa\x9f\xafQY\xe7\x03\xbahhC\r$AX\
+\xc9z%X\x11\x81>\xa2\xc9\x9ean\x8d(p\x803\xf4Q\x8cs\xe1\xa9t\xbc\x0bD\xc7\
+\xb5\xaa\x0fr\xe2O\x91F/:\xfe\x87\x089\xc6\xc8\x07F\xf9\xbc \xa7\x03\xc8\x91\
+\x10\x87\xfe\xea<\x89x\x02\x04\xbdg\xbb\x80\xa4\x1c\xc4\xdab\xc2\x01\xab\x91\
+-\x0f(kD\xc5\x80O\x1b\x03z0&V\xe0D\xef\x02i\xb0"9R\x81f-\xe8\n\x11U\x06\xaa\
+\x13\xac\x1c\x08\xa0kA\xc3!\xa0(7]\xfdu\x13w\xf1\xc5/!\x94\x1c\x0e\xf1x\x9f\
+\x17\xf15\xeflcT\x1a\x9f \xc8%\x98\x80\xa8\x07\x00\xa9P\xae\x0c\x80I\x0f@JZ\
+\x19\xe42/%%\xfd\x00\xd5\x19TW\x03\r\x04PuV\x8d\x00\xca!\xd3\x00\x15\xe0Q\
+\x1f\r+\xfa\r\xb5\xbe\x01\x802\xc9\xcc|\xf8\xa1\xb0\x10h\xbc\xae\x86\xde)[0F\
+\x01\x96.\xd1@\x18\x0b\xb9\xb9@X}*O\xc0y\xa2\x91T\x83k\xf3M\xc1\xbf\xc37\xe2\
+\x12y\xcd\xc3\x07\x04&\x8b\x8c3o\xec\xc0V\x04\xf4\xdb\x0cUe\xc5\x903\x92F\
+\x9d){ \x9d-\xea\x11\xd0A\xcf\xc8s\xbc\xa1Bi\xd8\xd2,\x1cb\xc2"P}\xc2\x10@2\
+\x88\x06I\xa4\xc6\x1f\x02\x88Ze\x10X\xc6\x96\xdbFR\x967\x11T0\x95\x10\xa0\
+\x05\xa2\t\x82E\x88\x0c\xf1qdq\xc8\x11\xa0\xa0\xc5\xa9\xb7\xea<\x10\xa5\xea\
+\x08 j\xc0\xee4j\xbfr\xfc\x9b\x0bA11\xe5\xc9P\x11\xf5\xa7\x17\xca9>\x18\xd1\
+\xd9\xe2z\xb1\x13\xa6L\xff4\xb7P\xe5\x00\xc97\xc4\xc6\x15C@+\x03\x98W\xae\
+\xdc5\x8e\xe9H\xb4\x80\xc0\xca8~C\xa6\xa1\xda\x9a`\xc7@\xd8\xe1\x06\xc2\x08q\
+\xb3\x80u$\xc2AG\x1e\x10\x0e\xd8+\xe1:Y\x96\xa6?T\x07\xb3\x130SfD\xaa\x8f\
+\x98\x89P\xc0C\x88H>k\xa4c\x01\x85\x9f\x9c\x1b\xa6.\x06\x1b\xb5(\xf1B0\x18\
+\xc7xy\x90\xa2[\x92\xc2q\xb2\xb3e\xea\x90\xd8\xc4\xa1\x12\x05\xc6\x89\x06\
+\x8f(\x84\xe7~\xa4{\xa8\xcd\x1e\xd9NDX\'\x00\x04\x05m(;\xfa\x87\xdf\xa4\xa2R\
+\x81\xcb\x06\x90d\x1d\x01*\x1e\x84\xc2h\xda\x9b^1C$\x1d\xd1A\x17\x858QRY\xcc\
+$\x1f\xaa\x16+\x86\xcb\x84\x82\x00@\x85C\xc0\xf4\x01\xfde\xde\x00\xa7M\xe3\
+\xc9"t\x1fk\xfb\xb6Ou\xbd\xaf!\xc0r\x11j\x8df-\x02\xf8\xfc\x04L\xcdlKZB\x84\
+\x97PU.:\xf2\x05\xe4\x85\xa5-\x06\x86\xb5\xe9]\x02;\x85\xe0\x9cf\x17rK\xc5"\
+\x82\xda6\xe6\x18\x12!\xa3K\xf3\xcf\x90\x81l\xd6)\x95\x82D\x86\x0c\x85\xfe/\
+\xa8 \x95\x9d\xd2\x11\x0f\x01\x85\xe1\x83\x03\xf9\xca\x87\x15\x08z\x03\xd4\
+\xd8\x18\x19\x1eCS-k\xbc4\xc4_\xf4\xfe\xf4 \xb4:\xab\xcf,\'3u\x1a\x87\x96\
+\xf2\xa8\xa6\xc0\x0e\x82\x06\xeb _/!\xaa\x88\x8a\x03\x0fb\xa1\xa3\xbb\x05Z\
+\xdc\xd3?\x85@m\x9b\xfe2\xcb<\x06 +\xfe\xbc\x0f\x88\x8bg\xc6z\xe5\xcbQ3Aw\
+\x0f\xb2\xa8\xa9|;\'\x9eATPm\xa5\x95\xfe\x98t\x01\xba\x9c+\xd4\xcd\x82\t\x03\
+\xc348\x80\xf4\xbd\x03\xb5RG\xdd\xc4p5$\x02xN\x9au9c\xa2@&|\xbb\x1b\xa8C\xdd\
+\xb2a\x91\xf14\xa7\x1a\x83\xa3\xb2-W\x8c\xa8\xe6\xc2%r\xc3w\x07\xd5\x0e\xe1\
+\x9f\xcf\x91\xe4\x9a\x00s[\xa9\x00\xea\xdd\xc0H!?\xd0\x95WI\xe8\xf9DL\x1a\
+\x9f\x8e\xe7\xf0\x00\x8e\x1f\x19\xac\x86\xacuf\xbbh\x8d\x96\x03m\x938\x1e\
+\xfeU\n\xa6\x14\xe4\x97O\x1cQ\x00\xc0\x11\xe0\x1cqS\xf5~T\x8e\x81\x94\x10\
+\xd0I\xfd;#\x7f\xa8d\x08\xcf-g\x0b\xa4*U2[.e\x1a\xdfy\x8b\xcd\xc7*UZF?[\xc4\
+\x8a\xb5\x91>\xe4\xcb\xc6\xe0\xb8\xe4\xc3\xaf\xc3\xac\x94\r\xbf1\x9a\x8b\x08\
+Pf\x83F\xce\x94\r\xadO\x98\x9e\x9fK\x05b(9\x01\xd1g\xceg\x88\xb9|\x88\x0b\
+\x05\xb9\xe3f\x94fi\xa5\xb1\x8b\xa6\xb2\x8a\x0eHQ\xd5j(\x81\x00\xc9^G@9h\x8b\
+9\xe6b0\xadX\xc4)\xd2.\xe7\xa1J`\x08}@\x839\x98\xe8a?\xe9\xdcxr\x87\x13\xb8|\
+\x9a^O\x87\xb1\t9\xea}\xea\x81\xc0@\xdd\xc2\x16,2z>\xbd\xd6\x96z-xh\xd6t\xa4\
+\xb5\xf8\xef\xe9\xad\xc5\x07#\x06\x85\xcc\xfa\xea5^6\xcc\x84\xe4\x84\xe7\xb4\
+\x99\x82\x00\x05SI\xa4\xaa\x7fB\xf2T\x8fK\x959v\xe6<G\xad\xe5\xbd\x05D\xce\
+\xfc\x84\xd0g\x08\xc0u\xf3\x95ce\xe0\xa9~\xe20C\x1f\xe7\xcf\xbc\xcaT\x15\xbf\
+r\xb6\x84\x06>\xa2\xdan.N\xec\x02\\4\x9f\x80)\x03&\xb9*\x04\xd0\xfaui!\xd6\
+\xad\x07\xd9\xe8\xb8\xcf:\x89Z\xbd\xc0^\\\xd5\xc2\x00\xfd\xd4\xf8\xcfe\x85\
+\xdf=H\x1cX\xeaA;F\x10\x89\x80\x13\xff\xa7\xf5>\x03\x1a\x94\x06\x82B\x9e_C\
+\x17\x18C6\x8a\xaa\x05Y\xfd,\xa2Z\xa3\x90=l\xce\x1f\x05)\x9b"&\x04T\x1c\xc0#\
+&\xc0|+\x0cPhf\xc9\x86\xb7\xeeq}\xc0\xb73\xd2\xdc<o\xac<\x80\xc3X\x80\xf4\
+\xa0\xa7P\xec\xc5\xe9^!(\xe5\r\x81\xa1\x9c%\x17\xde \x8c\xd2\xaa\x00\x14\x9a\
+n$)\x1f\xc7\xacw\x02\xd0]\x10\x92Z\xd4\xa1\x7f\x15A?\x97)T>\x9e\x13\xa0\xb9\
+\xaa\x98g\xd1K\xf1}^_1\x12k\xb9\'\xa3\xbb\x9c\xb1\x9c\xafX\x1f\xf50\xb35\x81\
+G\xd3U\x92\x144A\x8dx\xac\xcd\xd8\x152\x01\x9cl\x96\'\x94\x8a\x0f\x13\x03\
+\xc4\x91\xa3\x8d\xcc6\x1fV\x10<\x14{\x15WI9J\xe53\x86@\xd4C\xb96\xac\x92J\
+\x81\x92p|bb\xd7\xca\xdb\xcd_k\xdb)\x98\xee\x9b\xe8\xa4N\x1fR\xad=V\x84\x9d\
+\x96|\x03\x88d\xe3\xd7X\x81\xef\x07V\x01 \x87\x80H\xa5\x1e\x01\xc8\x91#\xd3:\
+6\x1bP\xec\x97\xedKj\xaf&2\xb3\x03z\xadm\t\x06\xea\xd6_u\xa8>|\x1f\xb9\x11\
+\xd8D#\x84/\reT\xa1\x93\x8e<\x00\x84\x838l\x1fH\x14\xa6M\xb0\x99\xa5\\f\x1b\
+\xca\x8f\x16*\x1a!\x90L\x97L\xe2\xd9\x19\x80\'?j-\xbcp\xbf,\xfeTu3\xf4\x9a\
+\xd1\xd4\x12*\xec\x02$%\xe9\xd1\xdb\x96C${\xd1\xa6\xb1\x9604\xf2\x9a\x8a\xc1\
+1\xcf\xa9\xf5\x00\x00\x16mIDAT\xd1\xb9\x15\xf3\x11\xe5\x11\x01\xad\xb6<\x02\
+\xb0Pr\xcf\x9cSgA\xfa+\xc5\x80\xb59\xd7\x1f\xcbp.\xc2\x19C\xa2z\xbc\xe3#\xa8\
+\riMW\x9d\xae\xf2\x08\xc7\xafmi\xcag\x1b$\x11\xa3\xa4\x8c\xa2\xdf\x1f\t=\xe7\
+\x95\x18\x00\xfd\x83\x88\x96\x7fKj\x01\x0c\x9d\xa6^K\x12b\xa5\x10\x11Q.\x95j\
+\x942\xa4,6eUx@*\x07\x9d1\xcc\x04\xf6X\xb8\x92AF\xa8]\x9b\x14\x03\x08$\x89c\
+\xcde\x9c)!u\x06\xf5\xb0\xe8\xa0G\xe9-\xe6\x87\xf5\x8bb\x9d]\x11\xd0\x0eg\
+\xc5z3h\x99\x15\x1a4\x1b2\x95\xcf\xc6{\x00\x87\xcf\x18B\xe2\xaa\xf2[\x82\xb1\
+F\x8d\xb8gB\xc5LJ\x1e\xe9\xb6em\xd5\x19\x93\x1e\xa6\x00\x9e\x18\x02H\xaeqk#\
+\x12\x06\x86y\x1b\x08\x14\x11\x80j\x96\x97\xd4=P\x82\xa3\x93\xbaX\xac\xab)i\
+\xfa\xd5\xc2N\x9f\x1d\xaf\xd3\xc6\x1fU\x03B\xa2<o\x89\x97\xa6\xac5\xd0\x87Q\
+\x00\xd4\xcb"\xfeT\x98\x0e$bt\xa6\xd0\xf4s\xb0\x0b\xca\xa1!\xbb\xca\xf8\xb7\
+\xe4\t\xf2\x8b\xf6\x0e1[\xaf\x87\xb0\x8b\xd8\xd9\x1bP,E2\x07\xcd-CC%WF\xb6n\
+\xde\x1c\x16\x7f\xc7\xc1\xe8\xa34\xd9\x1c\x8b\xe2\x00\xc7\xe3\xc6\xb5_Z\x06\
+\x00\xd7|09\xea\x03\xdb\x16\xb3X\xdd\xbfG\x15\x84\x1f\xcfG\x90\x17\xa5\xf8\
+\x08\xe3\xdfUf\xed\x10\xc6 \xef\x10XI}.|&\xfe.~@\x87\xa0\xfa\x10u\x04-\xacT\
+\xac\x9e\xa1\xbc\x90>\xb0\x8d,W\x8b\x03\x08\xb0\x88\xd4\xaa\xbe\xed\xc6\xb8\
+\x085\xe1\x8b*5\x8eS\x1c)\xe1\xadl\xd1*eS\x11Y\x92\xda\xfa\x04\x1e\xe2\x00 \
+\xcd\xad9\r\x16\xb2S\xf7^\xd4\xa4\x97\x12\x99\x99\xf3X\x11\xa8\xdf%+\x95d\\\
+\x10\xbc\x8f\xb7\xc9O\xf4\xc1GH\xce\x03\xf2\xd4j\xd6\x00h\xcd^\xb0\x88\x92\
+\x99g,\x99\x8d~\n{\xc6\x94\xd2\xc8+\xa3b\x88\xdf;\x08\x8d\xa4\xc2\x18\xfe\
+\x99\xc0J\x048\x11\xa3]\xdbA5\x03\xbcw\xc9\xd0\x82\xe43\xba\xa0\n\xd2Z\x82\
+\x0fv\x81\xbe\xb2\x1f}\x1a\xd4\xe2J}=\x04\x8eR\x92M\xe0"\xad\xa6\xbc\xa0\xd0\
+P6\xf8F\xc0@\x14D\x95\xf6\xf9\xe2\x8f5\xf4\x97\xa3\xfc\xb5`"\xa0r\x9e\x9b7}i\
+6\x88mD\x1b\x89p\x90\xd4N\xf3\x83_\xd3\xe1|\x98\xc4\xc4\xcfB\xe2\xef\xfc\x11\
+\xbf\x02s\xa4\xfej\x08 \x0c[\xc9\xba\xc05\x91f\xb0\xa6\xd1F#%h\x82\xeeR7\x16\
+Yxd\xcdYan\xe6\xab\x1c\xb2l\x88\xdd\x8a\xcczL\xf3\xd2\x0f\n\x15\xa7\xb9b\xf3\
+\xd1\xe6F\x1b$k\x81\xfcJn\x7f\xcb\xb2f\x0cBd-\xdeg\xfa\x9c{\x13\x9aJ1`\xc0\
+\xc7A\xac*\xdcho/\xab\x08\x95SA\xfd\x1c\x18\xc9\xe8x\x04\xfa\t\xda)\xd7\xb5\
+\xe4\xf9\xc9\x10\xe3\xd0\x88%e\x1d\x8d\x14\xbd\xe2\x13\x0e\x9a/M{\x00\xf2\
+\x91\xe0\x06\x14\x9e\x13b\xfe@\xc5@\x0c\t\xe4\xf1%\t\x05\x85\xa8\xd9\x11-\
+\x17\xaaZjoR\xbfbOG\xd9J\xae\xdc\x1a\xce\x0f\xf1\xce\xe2G\xe6Vh\x04\xe7\x9b?\
+\xb8\x8f\xec\xe1\x96s&\x82\x0e\xd45\x05\xc6\x01\xc4\xcf\xb7\x93\'\x10=\x10\
+\xc5\x00\xa0\x89\xbf\x9b\xea\x8a\xe3\x97\xf6\xbbND\x8b\x15\x02\xb7U\x01\x1b\
+\x19m\x85\xe9\x93x\xcb\x95}HV\xaf\x8bF\xa1tz\xa0\x1d\xef\x9d\xf9\x01\x80\xa8\
+\x1bp\x84\xf0&\x83-\x80d/Z\x0c\xb0\'\x10\xa9cP\xd2\xd3ll{\xbd\x11\n\xc7ZDA\
+\x17\xe8L\xe8ro\xef\x9b\x96\xf6!\x88\xbftP\x8bk]%d\x84d\x96\x13\x1a\x80\xb9;\
+\xa1z\x97\xb7\n\xf5A\x12\x08f\xd9\x13\x1c\x00p(\xc4\x93i\xfa\xc8\x8a\xc4U\
+\xe3\x13\x13=I\xc1\xb3!\x85\x88\xe3\x11\xcb\x0e\xdc\xe2x\xdf\xcc\x0e\x88\x9c\
+:\xd5\xc5mj\xbct\xe4"\xdc\x15\x06;\xbb\x10h\x08V\x01\xda\x9b\xe2(\xa3:\xdbD0\
++\x88,\x848\xe7\x8da\x01\xb2GA\xf4\x07m\x8d\x17\xb9xq\xc6\x99\x1fF*\x06-{\
+\x18\xf7\x15\xcf\\\xab\xd1\\\xd2\xe7\x96]\x8c{\x86\x802\x10<\xb9\xc37\x08\
+\x05I5D\x8e\x1dh\xc2l\xb9\xf2\xe0=\x8d\x91\xb5k\xac\xf17\xca&X\xd8\xea\x80\
+\xf3^\x1a\xcaY\xedZ+~\xe9\x9d\xe2\xd21!y\xcd\xa8\xb3\xc5gr\xc1\xeaI#\x83\xee\
+\xe2\xb1x\x89\x00KZ ;\x95\xd8\x848c@)\xd8\xc1&f\xd3\x00t\xc8\x91\xdd)M\xeaA\
+\xe2 U\xedpN\r\xbd%3\xee\x8a\x9dm\xa5\t-d\xe4P(\xf1\xd9S\xf3\x04\x12\xf6\x94\
+\x19\x93\x94\xa8\xea\xc7\x95\xfc5\x89\x00J\x95\xa7[\x92qQ\x01x\x19\x8f\x83:4\
+I\x1b\x95\xef\x17\xc9\xef\x91H\xbb\x80\x90\x11\x99Of\x812o\xbar\xc2\x0b*\x84\
+\xb2\x1b8D)\x92\xcc\xcf\x134A_\xc0\xb7\x11\x90u\xfe\x0b-\xd5\x93\xd5X\xf2\n\
+\xfe\x14\xa9]\x0e\x17\xe8\xa6s$\xcap@\x93q\x07Q\xaeM#P\xd0P\x88zE\x9al\xbfG\
+\x11e\x05\x02\x00=,\xda\xf8\x0f\xb1k\x8b\xd0Q\x04\xb8\x14\x932\xb0C\x9e\x95!\
+\xa3\n\x0c\x96t\x83\x04\x82\xa52\'\xde\xd6\xab\xca*>-\x91\x0e\xf6\xe7\xdb*S\
+\x8bE\x8c\xcc\x1c\x01\x02\x06Y\x12\x81\x10~\xd5}\x08\\Y\x13E\xaa\x9f\x01T<A+\
+\xd4\x9d\xdaM\x9bg \xdb\x14XR\x92L\xca\x99\x17\x80\x1b>\xbevLY\x9cY\x15\x19\
+\x14\xcaJH\xd6\xc4`\x82\x97,\xf8\x16\x9e\xfe\xc9V\xcb\xcfty\x87V\x0cU=\x94\
+\xd5\\N\x11\xbe\xa1\xcd\x1c\xfb\xd5S\xe3\r\xf6\x0f\xac7q$\xd3\x80]\xb5\x96\
+\x89*\x83\xd1\xed\x14\x1du\x118\xd7@|\xa62\x1d\xd0@c(!\xd3\xbd \x19y\x06cc[\
+\xc2\x0b\x7f0\xbb\xba7Z\x8a\xce\xeaD\xe9\xab\xdbuX\xe2GC"\x83\x83\x04\x12P\
+\xbc\xb3\x82\xd6w\xcd/\xdeP\xce\x14`\x05=\xa4\x8a\x11\xbf\x9d\xbc\x12[\xfb\
+\x8a\\^\xbb\xc1\x9d\xc4\xd6\xaeu>\xacx\xebU\xf7\xc4\x04\xc4\x98\x8a \xa4j\
+\xdf\xe2h\xd1\x9dh\x80WA\x00\xf8\xd4\x12\x97b\xdd\xe6\xc3\xdfl\x9b\xccD1G\
+\xa8\x1f@\x96\xdc\x13\xcd\xac\xb5g\x89j\x9fT?\xa1\x9a\x98\xc0o*\xe13\x82\x16\
+\n\xea\xe1e\xcdG3\r\x1aD\xcd\xa1\xf7\x96\xd0\xad.\xfc\xd5T\x89\xe7\x11\xec2p\
+\xaf\xbc\xb3#\x178g\x94\xb0T\xdbT\x86\xd6Ie\xcb\xc9\x84\x00_\xd5q\xe6\xd6]jr\
+\x15\xd61\x8f\xb4\xc9\xd9\xc7\x97\xd5\xf7\x18"_\x0f\x93\xa3\xa43\x9b\x02\xfa\
+\xb8\xf0\xd0n\xd1\x9a\x05\x89)\x10z\x80\xe8\x86L\x08\xc5\xed\xb3\xf7\x92\x02\
+7B\x1a$2\xe6;8\n\x9aR/\x80P\x93\x17h00<\x9b\x0cy\xa1](\xc3)\xf0\xe3e2S\xeaK\
+\x1a)\xeb$\x9e3\xa3\x8640\x1dE\xf1\xadR\xf8\x7f\x00\xe0\x12$f\xc1i\xe8\xec\
+\x14\x18\x94\xad\x1e\xc0\x86\xa3\x83V\x89\xc1v\xd8\x1d )\x99\x88\xb1\x9c\xbc\
+b\x9d\x06==\x7f\x0f]{\xd5\x9f\x04\xdaAl!A\x92\xd4\xeb-\xb8\x03\x1c\x014\x8b\
+\x8b\x7f\xb0\xf4j\x0c\xb16\x06T\xe74\xd0v\x94r\xc6\xa9\xa9t\x03\x02\x0c\x0c\
+\xf1z\xf2\x0f\xe7Kg\xd5k\x11\x90\xcf\xa7K\xcd\x03DMP\xf1\x9a\x86Z\xdfG1\x90\
+\xdd\x06\x90=\xba\x1d\xddB\x92\x85Huru9\r\xfaR{\x91{C\xd5\x98Y\xa9\x12\xea\
+\xcfK\xc7?\x00\xa4yx\xdeG\x1d\xb5a\xb4\x9f\x19\x8c\xcf\xe4v\xae\x829\x85\x86\
+\xa3d;_\x8a\x1a \xb2"S\x00\x0e\x05\xe1Kp\xf6\x15w2/\xc0(\xf0`\x85b\xb4\x19r[\
+A\xa6\x93\x94~\xbd\x88\xd6\xfc\xc1N\xe2\xa7l\x9bK\xc8\xea\xec\xa0[\x08\xdde\
+\x8e_!x\xbd\x1a\xc3\x13\x13d\x89oP\x08\xda#\x8f*\x97\xdcE\'\x06\xbbX\x19o\
+\x91=MWjT\xdd@\x85\x14s \xa5Z\x1a6\xa4\xc5\xa2!\x11\xa0\x04\xa7\xfd\xa4T\x14\
+\x9f\\Bd\xe7UbA(\xd4-D\x83T\x1d\xf9\xd8{\xf4f\xfb\xb0C\x10\x83\xd7p`\x80Ww\
+\xec\x11\x1f(J\x1b\xee\xa4\xa4\x0e\x84r\x0c\xe1HSD\xd9"i+\xb9[ \xfe9sq\xad\
+\x02\xa0!\x80\'f\t\xa0\x89\n;`\xd2\xfaT\xc4\xbe4\xb3D\xf53\x95\x06g($\x1cT\
+\xb4(\x1b\x02\x0cL\xbf,\\\xfa|\xbcr\x84:\x87\xec*\xb6\x1a\xb2\xfaVr\x15\xdd \
+R3\x05\xdda\xc5\x80\x1e;!z Fq?\x80$\xfb\xe0\x17%P8\x82\x03A*\x00\x06t\xb5k7\
+\xdfg\xd2\xebN\xe5z\xf3\x06G\xd4/\xf2\xa7\xea\x1b\xb6V\xc8(\x88\x93E\x16\xf4\
+y}\xba\xec\xf9`\xd4\xdevB\xe25\xb3\xe1\x8c\xfeON \x86B\xd6\xd3\xd74\x06\xafK\
+\xf6\xa3\xc7\xa9\xe4\xc6*\xd1=tD@Ju)\x039(\xc6(wa\x11\x7f\x17\x11\n>W\xaaO\
+\x886\x98f>\xe2q**\x98\xe9\xc8\xa6\x12\x8a$J<^FU\xef\xe8\xd26u\xba\x1c\x96\
+\xbe\xed\x1b\xa4\xb2>\x15V\xe78\xddu\x00\x8d)\x95\xaf\xf5\x0cj\x18\xd0]\xa6\
+\x90\x14\x00\xd7\xc7\x12\xad\xf9H\x0bG\x80\xd3\xaf\xcds\xc6.&RYI\x16\xd9\x95\
+Kc\xc7`\xd0\xe4[\x87@A\x97\x12G\xc7\xa5\xfc\x15/4A^@\x07\xd9\xe5u\x80\xa1Qkh\
+\x16C\xbf\n\xd7\x06\x1fV\x9ab\xf1y\x14>3\x92\xa1\xabA\x8c\xea\x7f\xc7\x1d\
+\xc0\x18\x08X\x82\x07\x81\xe9\x99=d\xc2\xb3E\x04_ \x97n\\\xf3\';\x94b\xe3S{\
+\xea\xd7\x004-\x0b$&\x0f)\x9aA\x1c*\xc9\x84d\x90X\xed\x86u`S\x00~K;@\t\x89\n\
+{\xd7]v\xc4\xe7\xa2\x04F@!U\xec\n\n\x05ID\x87\x0e/\xae\xe3`\xc8\xa0\xd9\x9a\
+\x9ccA\x83\xf8`vm\x85\x8e\xfeQ\x0c\x7f\x88d\x0e\x15\xfd\xdf\xe9;\xd5,\x08\
+\x1c\x04\x92\xb8p\x18\xectr!\xbc\x00\x94\xac\x88\x8ef\xce\xe9\xbe\x00\x05L\
+\x13\xe2\x00\x13\xd5\xe9\xa1\xda`\xe4\x9e\x9ca\xb72\xa3\t\x866\x81\x9e,\xf2\
+\xb5|9vY\xf2\x04I\x06GkY\xe6@\x10l+~a1xt\xe18\t\x99+U\xc4e\x8e\xee\xba6\xa9r\
+Dh\xfa\xcb\\K\xb0\xd9\xc0\x15)_C3\x91\xc0\x9c5G\xe3\xf8\xbd>\xf2Q\x8eZ\x1c\
+\x14_\x87\x1b\xf4\x04u\xfd\x92\xba\xd7q\xe0\x95?\xd8\x01\xe4<Jq\x9b|\xa5"\r[\
+\x8aS\x97-/\xb3\x8b\xd1\x15(\x0c\xbf\xb3\x10\x8bxol \x15x\xa2\xe2\xd4\xd0\
+\xa0\x88\x9d{\x04\xc0\xb0Fn<d\xdfPH\xea\x02\x08o\x12 \\\xa4S\x10\t\x07\xb2\
+\x8bh\xb4\xe7\ri\x8br\xb0\x94\x82\'M)\x1fg\x8d\xa3@\xdb\xee\xb0}@Z%\xe9\xc1.\
+8SA\x90W\xa1\xc2\x07\x88wQ\xeab\xd4k\x08P\xea"9|\xb0\xb8ZA\xd9F\xa1\xc2\xe3\
+\xc3\x8a\x9dO\x9aqJ\xcd\xb5\x17W\xa1\x1a5\x9e\x12\xa28\xb02\xb6\xa834\x87$\
+\xcaL\x0f\xe57\xc3L\xba\xf6\x1e\x00\xfa\x1e\xd96\xc4\x89\x198?T\xe8\xe9\xdd\
+\xc7\x98Vy\x8dU\x0c\x80b\x89\x14\xa8#\x08\x0cT\xba\xff\xad\xe9\x1f4\x88\xe8\
+\xa8\xd2\xb1\x00\xed\xe0\x93g\xbd\x87j\x85\xe4\xa7:\x18\xe8\x0c\xf4\x1a\xf1z\
+y\x10!\xbe\xa8\xaa\x0f\x86Gi9GzF\x87p\x80b\t\x9d,\x014p\x87\xe89\xa9\xc4YB\
+\xa0\xec\xa8\x9e\xd6B\x1c\x15D\xda\x1eH\x1c\xb6\x1f,S\xa8\x89rW\xd3\x0b\xd5\
+\xfe1f&\x8b\x00\x1c\xaf\x8a\xbf\t\x96O@\xfd+\xcb:79\xa3\x10\xa9Q\xe8\x1e\x86\
++\xa5q$\xbbD\x8ddz=hUn;b\xa7\x1b>\xb9\x0c\x9e=\x01\xae\x15\x91\xc8\xa8\xc2\
+\x81\x8d\x00\xf2\x05)\xfb[\x04\xf2=\xcc\xf1\xbb\x88\x8c\x1b\\#\xb7\xedx\x94;\
+\x1a;\xd4+\xa2;\x06\xee\x9fi*\x8bt\'53Z?\x8by\x84\xa2\x07\xccwX\xab\xd1\x8e\
+\xe0@\x15\xad\xe90\xb2\x06\xe1,%\x04H>\xf3\xdf\x98h\x82\xb1l\x9c\x94\xa8\xe3\
+\xd1uRfo\xf8X\xb9\xe0\xa1`\xa04\xb7X\x93zf\x14C\x90P\xd2\xa5\xe2\xfbYe6\xc5{\
+S\xa5\xd3S\x988\xb0\x14\xaf\x8b\x1f2\x00\x8dE\xbd\xf8\xae\x03r\x814s\xe0\xd4\
+K\x92\xa3AC\xe5Y\x04\xf6\xbe\xdce\x04\xa9\x92I=\xecNs\x06\xf3Lx\\\x03\xeasb\
+\x0f\xf2\xd3\x81\xd4\'\x9a\xdb\xc8RA\xe0\xc0$.\xbc\x10\x0f^\x97\xbd\xe1Y\x91\
+\xcc\xb4_\xc2\xec@+FB\x19\x19\x88\x92\x93yWR\x1bT\x04G\xb4\xbb\x85\x00\x96\
+\x81v\xb4\xe7\x85\x15\x0fK\x06\x02\x14m\xe8\x93T\xa7\x95\xcaR\x8e\xd7\n\x02\
+\x18\xe4-b\xa2\\v\xb1H\x98\x02z\xd7\xcd\r\xc4\xeb\xff\xd6\x1bE\xab\xe8h\x91\
+\x1d\xc8hp\x88q\\\xd2\x0c\r\xc2\xe1\xc2\x92\xc0\xfaV\xd1\xf3h\x80\xb2#!\x16\
+\xaa\xac\xd3\xe5\xb5\x07\x0eT\x1chE\xfbR\xb64\xaf)d\x92P5\x0f\xf4r\x1f\xc8\
+\xd8\x19\xea\x1c\x8cY\xbd\x08\x9e\xfe\xc1qH\r\x14\x11\xc0\xaeYL\xfc\xc2k\xe0\
+\r\xfbG\x81\xddN\x81u\x9atn\xadF\x97\xfa\xb6],b\xb1\x8e\xfd\xaaA\x07\x06\x1a\
+\x07\xe6S\x07\xbez\xf6 4)\xb9T\\&\xc5\xd7+9\x00u\xf9\xb2\xc5+\x9a\xa3\xa8z\
+\xa4\x00\xc2\'7\x94"ri\xb2f\xe5\xb3\xf0\x8d9-\xa7\xf6\xa6\x92\xfa\xa0(\x9c\
+\x8c-\x02\xe9\xbaW}D\xd2\x19\x80\xc8\x96b\x84\x95\x89\x7f{e\xae\x07s\xaa\xaa\
+c\xa5\xb1\xa6\xb8\xe2\x12\n\x9acX\xe3m\xe4CF?%6\x85!c\xef6FcX\x9bj\xa1\x9c\
+\x9c\xc1\xf4\xce\xcf0\x17`\xef\xfbB\x96\xa6\x08:\xdbJ\xb6\xcdh\xab\xb0\xcb\
+\xd3\xdfB6\xa3\xca\xd6\xb9\xec\xa2{\xb38\xc8\xe4\xd7iv\xb4\x1ai\xa9\xc0n\xb8\
+\x03\xe5+\xa5\xd5\xbb\x88\xc8\xe1\xafl\x1c~\'\xb0(\xd2\xe2@\x88s\xb8\x93\x05\
+\xf9\xad\xe8\xf8\x87G\xf2Z\x97\xef\x9d\xe4\xf5\xa0\x91\xbffj\x00\xb4\x91\xcd\
+\xf4\\\x9d"\x86\xb6h\x8dqN\xad\xab\xd7\xb2R\xaa\xbf\xa5\x11\xf7\x8a\xe2?\xca\
+@c\xca\xc2k\xbc\xba\x86V7\xc7\x7f\xa1j\xbd\xb2\x0c\xb7\xd8MO\x7f*8\xf2\xf5\
+\xcf\x00\x17Iye\x16\xa7\x10\xf7\x89\xbf\x94\xda\xa5\xb91T\xe8\x1e\x1fDD\xd8\
+\xe83\xec\xb2\xee\xd3\xaa*\xd6\xe3\x00\xf8a\x80\xba\xfeg\xe31\xe3\xd1\xa8\
+\xe53\xd7\xc2\t\x94\x16)\xbf\x10U\x1fX\xc3\xc5\x1cR\xdf\x89\x0en&_dd\x0c\xd2\
+`~\x81\xba\xf0\x95F\xcfTB/\x97\x1b\xd5_NO\x83W\xe6\xc6~\x14\x19\xe7\x89a:-\
+\xeb\x1c\x14\x08\xe8\xa3\xae\xaa1\x94\xc7\x95zP\xd3\xd2\xdedU\xca\xa3\x98\
+\xd03\x80\xbbg%\xbc\x07\xa6\x12zb\xb2\xd5\xb5\x8e\xe8\xd9\xe2\xb2i\xa9\x1d}\
+\x08L\xeb\x02\x89\x1b\xb5\xa3\xd8\xe8q\xb9\x17 \xb0N\x13\xa4(^\xf1\x7fz\xed\
+\xdd5\x84\x9d\xdd\xa3@n\x19\x14\xf8\xa0\xb0\x1d\xbd\xe5\xec\xd8\x05\x1c\xc8o\
+%\x97\x1e\xae(kk\x94S\xb8\xf1\xfaZavS6\x94\xb4\x9d.\xac\xec-8yrQ\xafp\xf8\
+\x16\xe2\xca8\x0c@h\xc6\xebv\xc9\xc0XI\xacwm\xba0\x01\xfa[\xceAt\xb7\xa7\xfe\
+\xbed\xad\r\xd6*%YG\xd22\xa0k\xbe\xa6\xa2\xdcP\x00!\xf9\xb0\xad\x1e#\xd0!\
+\x96S\x03y\x04\xb0]Z^G\xf7!\x1a\xa1@\xce"\x10X\x8b\xee3f\x01y\xb7\x80\r\xf4\
+\xea)b\x92\xa6\\\x02\xa0\xd9\x9c,%\xb3\xca!\xabz\x12\xa5\x8f\x16\xe3-^S\xf0\
+\xc8\xe4d<\x0f\x03\x01\xc3\x07\xcc:\x1f\x0bF\xa5\xad\xe2L\xcfZJ6"\xf2\xcc]\
+\xc1\x96Ta-\x05\x0f\xe8\xbe&K\xbb\xdb\x93J\x08S\x00Z\x82\x0e\xd70\xb3\xf4!+\
+\x1fQ\xb1o\xa8T\xd4\xe1/Y\xe1;\xbd)\x9er\xcc\xaesK\x84j\x8f\xafX\xd0\x067\
+\xcf\x00\xd0,\x00\xb8\xe0\xc0\xeb\xeedM$?]\xa2U\xe8\x014MP\xe9i4\xe3J\xcbL\
+\xac8D\x0c\xa8\xf6\r\x91B\x1c\x98\xa2\'\xe0\xa9\xd0\xfc:\xe8D&\x9d\x80\xd2\
+\xd1\xdaRgnqP\xd5nZ-\x84\x82\x07k!\xa8X\x0b\x02x\xd6\x1c\x80\x074\xeb\xc1 h\
+\xd7e\xaa\xaaLs\x02e\xf3\xa0%\x17\xd4\x06\x10qK\xce\xb5r\xe8\x8e}q\x86\x01\
+\xa1\x12\x01>\xfe\xcf\xcay\x9c\'\x11\xab9\xab#\xaf;\xf9\xea9\n\xf5.E\x12:\
+\xee\\\xdb\n\xe06\x86m\x0b\xe2\x18vi\x01\xca-7\x07\x00\x98,\xcb\xa2\xa21\xf2\
+s((\xb7i\xd69\xd8(\x0c|\xca\x84\xed\x81\xd0Q\x15{\x12fz\xcd\x96A\xb6\x96D\
+\x01\x0f\xb6\x8az!\x01\xbc\x83\tk\x8a\xd4:\x86d\xfc+A\xee\x84\x10Yv\x80\x01\
+\x1e\xf5\xad\x93\xec\t\x1dk\x11\x88\x91b\x08\x08\xbd\xc4\'\x10T\x07,\xdf\t\
+\xa8\xc1\x01\x91\x19\r\xae\xfd^7r\xb2*j\xcc\x88\xbfQ\xa4^\xeb\x99\xa6>\x00\
+\xe8\x0f3\x95\x019\xfaM\x0es\xd1\xb6\xcf\x17R}\xf3%J\x87\xe0\xa4\xf8\x16J\
+\x02\xd6\x05\xde{\x00\x82\x02E\xfc\xc0\x10P\xea}s\xc9V\xc3Aa\x13\x01y\xea\
+\x8d\x8a\xceM>E]\x12\x01\x96\x83\xa8\x0f\x1f\xfd\xa0\xa9\xac\x89\x14aWvn\xa8\
+s\x86\xb1;\x808\xc5_\xa2\xc4\x1f%\xad\x85\x01\x96r\xea\xa9\n\x05\xb0\\]`\x06\
+ix\xf8-\xda\\`{\xbe\xd6\xe3\x03\x0c\x01YS\x86\x9d\xa3(\x18\x07\xcd\xf1\xb2N\
+\xad6\xa4\x9f\x1d\xf1\x96\xca\x17I\xd4mA\x1f\x9e\xb2\xde*\xc2e\xc70Om\xbe\
+\xb6\x98T#\xfe!lDT\x065{\xfc\xb8X{\xe1\xe8\xae\xb1\xc1\x83\x8a\x00^P\x1f%\
+\x84\x9a\xfe\xf0\t\xad\xc9\x0c5\x05\x9d\x82\x84\xe2\xa2I\x1b\x99\xbe\x08P\
+\xc6\x00\x00\x117p?\x80\xf9\x03\xb6\xf8Wjm@\x17\x7f%q2%R\x1e#\x94\x04\x19\
+\x02D\xbdV\xf5\xdey\xd7/hl\x11\x903``\x80\xe8\x16\xbd\xd1a\xd4*\xeb\xa2\xb4&\
+O\x97\x99\\\xae\x00U\xf3>TN\xe4\x0f\xe0\xc1\x17G\xff\x8a\xac\xcfk\x03\xb1\
+\x0bY\xea(\xe4\x89\x10\x04\x98\xe6X\xd9\xadkxd\xc7\xcb\xf4N)9\xbb\xa5\xac\\\
+\x05x\x92\x90\xf7\x18"\x06\xd2\xe6\x85\x99\x99\x98\x00\xe3&\xb3\x15\xa4O\xb4\
+\xf2\xc7\xe33\xc78\xb9\x9eZ6\xb0L?TV\x17\xdb(\xe4\xcahx\xf0\x8e\xcf\x0e\x94j\
+2\x08P\xfc\x03OoD\x95\xea\xc4?\x97\xc5\x1b9\x93F\xe2\xae\x1e\x1b\xfa\x88\x17\
+\x13\x01|\xceV\xb7\xf0\xc2S\xf9\xce\x9a\x80Qm"\xcd\'\x8f\xe0)\x9d\x86\xdbE\
+\xb8\xaf\x99\xfb\xf5\xb9\x82\xed\xe0(6\x1d\xc7>`H&\xf4(\x1e\x00;z&EB\xb3P9"\
+\xa7\x0c\xfeT\xb7\xc0\x84\xe6\x01\xe4\xa7\xdaP\x94\x96Z&^\xe7\xce}W\xe0@\xa5\
+Z\'\xa7r&\xcf\xa7\x88\xed\xee\x95NF\xc7G\\\xc9\xf1\xe9\xaf\xf6\x07\xbf\xf3\r\
+\x17p\xacX\xa0\x99_f\xc2x\xc2\x96?^\x82\xb0\x91\xea\xe3\xba\x80{3?\xda\x8aP\
+\xc8\xc8\xb3\xb4\xb1\x1b)g4\x84\x82\x00\x83\xaa"(\xdc\xd7"\xb4g\x0c\xa9I\xa0\
+y1&0\xf9DW\xd1\xb1t$b\xbat\xce\x9f\xaa\xa3\xc8\xb6\xdf\x12\x18Ka\xf4x\xa56\
+\xc2\x82i\x08\x04\x022\xa3\xcd\x91\x9d\x0c;\x0ce\xfe\xad\x15\x1e\xb9\'\xac!\
+\xc0\xe3+\xd3F\xe3\xf1\x1co\xd5\xc5"a\xb55\r`\x98j:\xf4x\xee\x16\xf1\xdc\xba\
+k\xa0\xa6\xda^\x0b\\\x8f\x83\xde;\xa6!@\xf9\x15\xf1sj-~}\x01\x07\xc3\x89\x1f\
+<\xa0=\xbd\x0cI\xcf\xfb\x96\xfe\x00\xf0[S\x8b\x92\x0b\xf4,\xa2\xe1\xd8[N\x02\
+CU\x88a\xae\x80\x03&\x18a\x14T\xe3\xe6biY\x1f\x8fS\x94?3\x0c\xeas9\x99\x95\
+\x96t\xc2\x1fc\x9d\xe2\x00\xbaz\tZ\xe84\x17\xb5\xbd?\xb3@Mp\xe2\t$vcB\x00\
+\xf7\x10\xf1\t\xa8Sf\xaa\x10\xe7\xb54\xc9\x80^\x9a\\\xda\x08\xc8\x14\xe6)\
+\xaa)\x91\xc5\x85\xfb\xff-x1\x0bp\xf1\x03\xd6\x82\x00\xc8F\x97\xc6\x8c\xe9\
+\x13d\x17\x012S\x05\xaf\\{]\xf4\xb2O]f\xe6\xa2(\x01\xc9\x05\xd3\xf1\x9e\xb3\
+\x80S55\x10\xaf\x80! 3z\xe2zA\xc8\x14U\x02T\xb8u\xc3\x85\x82\xf8\xd9\xb5\xe2\
+\x0e\x98\x01g\xec\xa3\xc8\x04\'\xe1\x8fI4\x94\xa7\x92r\xa6\x1e\x8b\xb2\xa5P\
+\xed\x89]\x14%\x11\xc0\xaa0\x18\xa9W\xb6\xf5\x08`\xa52\x86|\xc5\x8cEF\xfcZ\
+\x99\x9a\xc0X\xc3\x8bE\x9ck\xd7\xa8z\xf2\x16yO\x92*\xeb\xd4\xfc\x16\xef\xb8,\
+i\x87z\x8f\\H\xa6\xfaq)F\x04\xe5\xa2\xde\x80\xa1\x9cj=9c\x90%4K\xbb\xd7e\xc2\
+(\xc1\x84Z\x87g\xae`\xf7y\x05\xe8\xc20g$\xfa\x9cQ\xa5\x8a\x16\x07\x100\x11\
+\xae\x00M&;j\x05l\x11\x90\x9a\x158\x00\xa2\x95:xQMy\x05\x01\xcc\x16\xd4\x90i\
+\xb9\x94v\xb7M\xae\xd5\x07\xb5\x9bG*\xe6)h\xe5\xd4Y;\xa3na\x90G`s\xd0\xfc*V\
+\xb5M\xf7\xca\x9f\x90\x03\xab\x86\xca\xd7\xf1\xc9\xd1\xafr\xa9\x102\xa9\x1bJ\
+\xa2\x8d4vu\xd3\xad\xae\xda\x9c\xec\x8c6\xda\xe0\x87<\x10\x9b\xed\x87\xdc\
+\x17/\xb8\xa7B\x94\x80\xa4\x1a\xac\xa8\x8dU\x9a\x84)\xc8\x93E\xb6\x08m\xf5\
+\x1f!\xe0H/\x19\x84\xba\xf8/\x95\xa1\xd3\\\x0b\x00\xb0KH\x82d\xcc\xab\xb1\
+\x05f2\xef\x9dPq/\x1c@tS\xad\x04\x99\xb3h\x9d\xc3\x91\xa4\xdaC%\\\xad\x13\
+\x01\x1a:?\xb3\x9aG\xef\x94\xe5\x1aV\xa5p\xbc;\x04i\x8c\xd5!\xd5\xe9p\x07\
+\x0e\x953$\x9c\x1f\xb4&kV\xcf\xf4\x9d\xb9\x1b\xa8A\xa4\x84L\xce\x9d\xdd\xee~\
+\xb4\xf2\x912{\xfe\xc5.\xdefK:+\xdf\xbd^\xdc\xd2\xd7~\x0e\x13\xaa*^vPGA\xb1\
+\xb1l\x90 (dd\xaf\xd26c\xf3.\x99\x96\x8a\xe7\xb71\xb2\x1fn\x93\xb4\x1d\xfe5\
+\xcdd\xc98\xd8!\r\x05\x86\x83r\xd1\x8a\xf5\x10\xab\x9c}.\x94\x99-u\xeej\xe0\
+\x8cPl\xc6\x84e\xe2!\xa6*M \xf6\x9e&\x19\xb7\x99H\x91\xaa.\x9c\xe3\xd9\x99\\\
+\'(\x84\xce#V#,q\xd0\xe6\xf5f\xa7\xcb\x17@\xe2$\x99_}aSxt\xb1\xdcR\xf9\x9ar\
+\xcf\x87\x04\xa7R\xb4\xdd\xf1ac\xcei\x81\x17=]\x1d\xd3f6\xd5\x93\x84\xb63\
+\xf3\xa7\x8d\xe3JA\xd0\rUoc/\xa8\x81x\xc1^\xd2\x01\x9a\xecq\xc1\xf2w#\xaaW.T\
+\x96\x80vh\xf6H\x91\xb1t\x93UTH\xe3\xe5\xb8at\x8a\r\xca\xf8\x9e\xd9\xfdFb\
+\x0ep\xd9(m\xbbn\x871o\xe80\xeeA\xea\xd8!X\xb6\xea\xd3\xddu\x99\x8b~\xc9\xc0\
+X!\xf0\xe0\xad\x81\x97\x19O\x1d\xde\xb8\xc9]\x01j.<?\xe8\xe3,\xf2\xccK\xd0T)\
+\xcfI\xa7\xac\x9d\xde\xae"\x0e\x95\x90\x8d]\xc6\x81`\xad\xa3\xa1\xb2\x06zN\
+\xb3i\xfa_md\x87\x8fF4D,\x8f\xc6\r\xb1\xe8\x8bDa\xd8\x8b6\x01\xa3 \x0e\xd7j\
+\x1e\xb9u\x97\x8a\xa7\xeb<\xcb\x038\xf8\xff\x00\xddb\xc0\x06\xa4k\x96\x16\
+\x00\x00\x00\x00IEND\xaeB`\x82' 
+
+def getWizardBGShorterBitmap():
+    return BitmapFromImage(getWizardBGShorterImage())
+
+def getWizardBGShorterImage():
+    stream = cStringIO.StringIO(getWizardBGShorterData())
+    return ImageFromStream(stream)
+
+def getWizardBGShorterIcon():
+    icon = EmptyIcon()
+    icon.CopyFromBitmap(getWizardBGShorterBitmap())
+    return icon
+#----------------------------------------------------------------------
+def getDecisionData():
+    return \
+'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00A\x00\x00\x00+\x08\x06\x00\
+\x00\x00\'\x98\xe5y\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\
+\x05kIDATh\x81\xed\x9a]lSe\x18\xc7\x7f\xa7_k\xd7n\xeb\xea\xbe`\x02]\xecpt\
+\x02c[\xa7|\x04\x13\x16\x13/\x0c\xc3\xa8\x89\x91\xc4xa4^3\x83\x04\tC \x18\
+\xaf\xf5B\x83\x89\xa0D\x83^h\xd4\x04T\x96\x19>6\xb6e\x1d n\x8e\x91-\x84\x91\
+\xc9\xa0v\xec\xbbk\xcf\xeb\xc5Y\xbb\xb6\xfb\xea\xd89-\xc6\xfd\xaf\x9a\x9esv\
+\xde\xfd\xf2\xff?\xcfs\xdeSI\xd2\xe9I\x95v\xbd}L\x84?\x7f\xff\xe9>)U\xeb\xd0\
+\xa5\xea\xc6\x8f\x92\x96!\xb0\x0c\x01X\x86\x00\x80\x94\xca\xc2\xf8\xa8h\xd9\
+\t,C\x00\xc0\x90\xca\x9bW\xec\xdc#\xca\xdcNzo\x0fp\xee\xab\x0f\xfe\x7fs\x82\
+\xa7\xa6V<\xbd\xd1\xc5S\xc5\xab\xa9\xda\xe8b\xcb\xcb\xef\x89\x85\xaf\xd2F))\
+\x8c\x955\xb5\xc2\xb3\xe1\t\xd6:WN}#\xb8\xf3\xb7\x1foG\x0f\xbf}y(\xe9\x8eH\
+\x89\x13b\x01\x00H\xac\xcc\xcf\xa6\xbc\xb4\x88\xcd/\xedM\xba#\x92\xea\x04\
+\xcf\x94\x03\x8a\xa3\x00\x8c\x8eM`1\x9b\x90$\xc5\x00\xfd\xf7\xfc\xb4^\xbb\
+\x99\xd4\x1a\x914\'\xcc\x05`l<\xc0\xf0\xc88B(\x06(\xc8\xb1\xb3\xc9]\xc4\x96W\
+\x92W#\x92\x06\xa1r\x0e\x00\x00\x81\xc9`\x0c\x88\xc2\xfcl\xb6{\xdcT\xef>\x90\
+\x14\x10\x9a\xc7\xa1\xb2\xa6VT\xcd\x03 Z&\xa3\x01\x9b\xd5\x1c\x89\xc6\xed\
+\xfe\xfb4z\xbb\xb8\xf4\xdd\x87\x9aFCS\x08\x9e\x9aZQ\x19W\x04\xe7\x02\x10V\
+\x9a\xc9\x805}\x1a\xc4]\xdf \x8dm]4|}D3\x10\x9a\xc5\xa1b\xe7\x9eE\x03\x00\
+\x98\x08\x04\x19\x19\x9d\x8eF\x9e#\x8bMn\'\xcf\xbe\xba_\xb3hh\xe2\x84\x8a\
+\x9d{D\xd5F\xd7\xbc\x00\xba{\xfb\xf0\xf9\x87\x18\x1e\x19\x03\xa0 \xcf\x81\
+\xcbY\x88\xc9\xa8\x0c\xb1\xf1\x8e\xb8\xf7\xcf\x03.\xb6v\xd2\xf0\xcdQ\xd5\x1d\
+\xa1:\x84D"\xd0\xdc\xdeIwo\xdf\x8ckMF\x03;\xb6\x96\xe3\xb0g\x00\n\x88tK\x1a:\
+\x9db\xd8[w\x06h\xbb\xde\xc3\xef*\x83\xd0K\x92z\x89H\x04@wo\x1f\xd7:{p\xd83\
+\xd8\\Q\xca\xe6\x8aR\x8aV\xad $\xcb\x0c\xf8\x06\xf1\xf9\x1f\xe0r\x16\x02\x10\
+\n\xc9\x08!0\x18\xf4H\x92DV\x86\x95\x1cG\x06\xc2^Rw\xeb\xfa\x85Cj\xad[\xd5\
+\x9a\x90H\r\x08;`\x9bg=\x05\xb9\x0e\x00lV\x0bUe%\x14\xe4:\xf0\xf9\x87\xf0\
+\xf9\x87"\xe7O\x04\x82\x8c\x8eM \xcbJI\xc8\xc9\xce\xa4j\x83\x8b\x1d*\xb6OU\
+\x9e"\x17\xd3\x05\nr\x1d8\xec\x99\xd8\xac\x96\x99\xc7\xf2\x1c\xf4\x0f\xf8\
+\x08LN\xc6|?\x11\x08\x02\xe3\xa4[\xcc\xe8t\x12\xabV\xe4`2\x19\tL\xee\x13\x17\
+N\x1f[r4\x96\xec\x04\xcf\x8c\x87\xa1\xf9\xbb@Y\xa9\x8b\xaa\xb2\x92Y\x8f\xf5\
+\xdf\xf5\x01\xe0\xb0g\xce8\xa68b<\xe2\x88\xfc\xc7\xb2x\xa6l\xad*\x8e0D\xef\
+\xfd\x87\x15\xfd\x0e`\xa1\xe3[\xca\x9f\xc4\xf9x^\xe4X"mp65\xb7w\xd2?\xe0\xa3\
+\xac\xd4\x15\xe9\x10\xf1\x8awDa\xbe\x834\x93\x81\xfaS\xd3\xe7<\xcc\xff\xb3\
+\xe48\xe4\xe7\xda#\x9fC!\x99\xc9`hQ\xd7\x07&\x83\xd4_l\xc3\xe7\x1f\xc2\xe5,\
+\xc4]\xbcf\x81\xf3C\x18\x8dA\xd2LF\x00r\xb23\x16\xbf\xe88-9\x0e\xf5\x8d\x7f0\
+<6\x0e\x80^\xaf\xc3jI\xc3`H\xbc\xed^h\xbe\x16\x010WL\xc2\x92\x00[\xba9\x02 \
+\x18\x92i\xf2\xdex\xe8\xb5G\xfe\xae\x1as\xc2\x0bo\x1e\x11\x9b\xdcE8\xb2l\x00\
+\x04\x83!F\xc6&\x08.\xe0\n\x9f\x7f\x883\r\xcd\xac.\xccc\x9bg\xfd\xfc\x0bE\
+\xe9"&\x93b\xde\x90,\xd3|\xa5\x9b\xd3\x1f\xbf\x9b\xfa\xc2\x08\xf0\xd3\xf1\
+\xf7\xa5\xe6+7"\x8e0\x18\xf4\t9\xa2\x7f@)\x84\xabW\xe6\xcf{^<\x00\x10\xaa\
+\x01\x00\x15\xe7\x84\xb3\'\xea\xa4&o\x17\xbe\xc1a 1\x10&\xa3\x81\x82\\\xc7\
+\xac\xed2,\t\xb0Z\xcd\x11\x00AY\xe6R\xdb_\xaa\x01\x00\x90^|\xe7#U\xdf\x0c?\
+\xff\xc6!\xb1\xdd\xe3\xc6b6\x01\x89Gc\xd6\xc5I\x12\xb6\xf4X\x00-*9 \xbaK\xa8\
+\xfe\x14y\xe6\x8b\x83R\xc3\xe5\xeb\xdc\x9f\x9a\xfa\xe6sDwo\x1f\xf5\x17\xbd1\
+\x13bX\xf1\x00d!T\x03\x10/M\x1e\xa5\x7f9Y\'y\xff\xeca$\xaaF\xd8\xd2\xcd3@\
+\x04&\x83\xb3N\x88\xf1\x00B\xb2\xcc\xe5\xf6\x1b\x9a\x00\x00\r\xf7\x13~\xfe\
+\xfc\x80t\xbe\xa5#R#\xf4z\x9d\x02B?}Kw\xf1\x1a^\xdbU\x1dy\x86\x80Y"\x10\x92\
+\xb9\xdc\xde\xa5\x19\x00\xd0x\x8f\xf1\xec\t\xc5\x11\xd1s\x84\xcdj\x89\x01\
+\x11\xad\xd9\x1c\xd0r\xb5\x9bo?\xd9\xfb\xdf\xdd^\x0b\xabz\xf7\x01\xb1\xadr\
+\x1d\xf6L+\xa0L\x96\xc3#c\x04Cr\xd4Bb\x01\x08!h\xd20\x02\xd1J\xcan\xf3\xb9S\
+\x87\xa5\xf6\x8e\x1e\x86\xa6v\x91\xe2\x1d\x11\x9e\x04\xa3#\xd0\xe8U\xb7\r\
+\xce\xa7\xa4\xbe|y\xee\xf5\x83bky\tY\x19\xe9\xc0\xb4#,\xe64M&\xc1D\xa5\xfa\
+\x9c\xb0\x90j\xde:*\xca\xd6\x15\xc5DC\x1f\xa9\x11\x82For"\xa0\xe9\x9c\xb0\
+\x90~\xf8l\xbft\xbe\xb5#&\x1a\xa08@\xedI0Q\xa5\xe4\x85l\xfd\xa9\xc3R\xcb\xd5\
+\x9b\xf8\x1f\x8c\x00\xca$\xd8|E\xfb.0\x97R\xf6\xfb\x84\x1f\x8f\xef\x97\x9a\
+\xda\xbb\x18\x1c\x1a\xa55\xc95 ^)\xfd\xa5\xca\xd9\x13u\x92,\xea\xc4\xaf\'\
+\xebR\x06\x00\xe0_Y\xc4y\xb5x\x97\x05\x86\x00\x00\x00\x00IEND\xaeB`\x82' 
+
+def getDecisionBitmap():
+    return BitmapFromImage(getDecisionImage())
+
+def getDecisionImage():
+    stream = cStringIO.StringIO(getDecisionData())
+    return ImageFromStream(stream)
+
+def getDecisionIcon():
+    icon = EmptyIcon()
+    icon.CopyFromBitmap(getDecisionBitmap())
+    return icon
+
+#----------------------------------------------------------------------
+
index 66fd95cf1ef1ea69e335b21484629c11a0aa5100..4a6ed85ad1502fb7173c49cf3f81b4e22ca03494 100644 (file)
@@ -78,7 +78,7 @@ class XmlCtrl(CodeEditor.CodeCtrl):
 
 
     def SetViewDefaults(self):
 
 
     def SetViewDefaults(self):
-        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Xml", hasWordWrap = True, hasTabs = True)
+        CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Xml", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetFontAndColorFromConfig(self):
 
 
     def GetFontAndColorFromConfig(self):
@@ -115,7 +115,7 @@ class XmlCtrl(CodeEditor.CodeCtrl):
 class XmlOptionsPanel(STCTextEditor.TextOptionsPanel):
 
     def __init__(self, parent, id):
 class XmlOptionsPanel(STCTextEditor.TextOptionsPanel):
 
     def __init__(self, parent, id):
-        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Xml", label = "XML", hasWordWrap = True, hasTabs = True)
+        STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Xml", label = "XML", hasWordWrap = True, hasTabs = True, hasFolding=True)
 
 
     def GetIcon(self):
 
 
     def GetIcon(self):
diff --git a/wxPython/samples/ide/activegrid/tool/bmp_source/activegrid.ico b/wxPython/samples/ide/activegrid/tool/bmp_source/activegrid.ico
new file mode 100644 (file)
index 0000000..fb15dfe
Binary files /dev/null and b/wxPython/samples/ide/activegrid/tool/bmp_source/activegrid.ico differ
index 1d2008f10a6df4ea902d5d2dc444cf7940f42cf9..7992a831f0947c394f87b0b120c5c08e6886b34e 100644 (file)
@@ -1,7 +1,7 @@
+Middle-Mouse-Click on the tab for an open file will close the file.
 Ctrl-Space in any editor does code completion.
 Ctrl-Space in any editor does code completion.
-Right-clicking on something in the 'Thing' column of the debugger's frame tab may allow you to introspect it for more information.
 Right-Mouse-Click in Outline window allows you to change display sorting.
 In an editor, you can add line markers via Ctrl-M and jump to the next marker with F4 or previous marker with Shift-F4.
 In an editor. you can use the numpad + and - keys to toggle folding.
 Right-Mouse-Click in Outline window allows you to change display sorting.
 In an editor, you can add line markers via Ctrl-M and jump to the next marker with F4 or previous marker with Shift-F4.
 In an editor. you can use the numpad + and - keys to toggle folding.
-In 'Find in Directory', if you specify a file, it will display all matches in the Message Window.
-Breakpoints for the debugger can be set while the process is running
\ No newline at end of file
+Breakpoints for the debugger can be set while the process is running.
+A split window can be closed by dragging the sash to one of its borders.
\ No newline at end of file
index a521f0cf509d27cc5dbc2663c0cf9d12341867be..1de68e1bc91eeda0f3470bdffa1d4f8455ba123d 100644 (file)
@@ -407,7 +407,13 @@ _SaferCreateProcess(appName=%r,
                 elif env:
                     uenv = {}
                     for key, val in env.items():
                 elif env:
                     uenv = {}
                     for key, val in env.items():
-                        uenv[unicode(key)] = unicode(val)
+                        try:
+                            uenv[unicode(key)] = unicode(val)   # default encoding
+                        except UnicodeError:
+                            try:
+                                uenv[unicode(key, 'iso-8859-1')] = unicode(val, 'iso-8859-1')   # backup encoding
+                            except UnicodeError:
+                                log.warn('Skipping environment variable "%s" in execution process: unable to convert to unicode using either the default encoding or ISO-8859-1' % (key))
                     env = uenv
                 hProcess, hThread, processId, threadId\
                     = win32process.CreateProcess(appName, cmd, processSA,
                     env = uenv
                 hProcess, hThread, processId, threadId\
                     = win32process.CreateProcess(appName, cmd, processSA,
index babdadea4286142b6b0dad6c117e564216f933ea..6b8035f33558231c30823174d576278548ac9ee4 100644 (file)
@@ -14,7 +14,16 @@ import copy
 import os
 import os.path
 import activegrid.util.xmlutils as xmlutils
 import os
 import os.path
 import activegrid.util.xmlutils as xmlutils
-from IDE import ACTIVEGRID_BASE_IDE
+import activegrid.util.aglogging as aglogging
+
+# REVIEW 07-Mar-06 stoens@activegrid.com -- Ideally move the pieces required
+# to generate the .dpl file out of this module so there's no dependency on wx,
+# instead of doing this try/catch (IDE drags in wx).
+try:
+    from IDE import ACTIVEGRID_BASE_IDE
+except:
+    ACTIVEGRID_BASE_IDE = False
+    
 if not ACTIVEGRID_BASE_IDE:
     import activegrid.model.basedocmgr as basedocmgr
     import AppInfo
 if not ACTIVEGRID_BASE_IDE:
     import activegrid.model.basedocmgr as basedocmgr
     import AppInfo
@@ -27,64 +36,6 @@ if not ACTIVEGRID_BASE_IDE:
 PROJECT_VERSION_050730 = '10'
 PROJECT_VERSION_050826 = '11'
 
 PROJECT_VERSION_050730 = '10'
 PROJECT_VERSION_050826 = '11'
 
-    
-#----------------------------------------------------------------------------
-# XML Marshalling Methods
-#----------------------------------------------------------------------------
-
-def load(fileObject):
-    version = xmlutils.getAgVersion(fileObject.name)
-    # most current versions on top
-    if version == PROJECT_VERSION_050826:
-        fileObject.seek(0)
-        if ACTIVEGRID_BASE_IDE:
-            KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile}
-        else:
-            KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile, "ag:appInfo" : AppInfo.AppInfo}
-        project = xmlutils.load(fileObject.name, knownTypes=KNOWNTYPES, knownNamespaces=xmlutils.KNOWN_NAMESPACES)
-    elif version == PROJECT_VERSION_050730:
-        fileObject.seek(0)
-        project = xmlutils.load(fileObject.name, knownTypes={"project" : Project_10})
-        project = project.upgradeVersion()
-    else:
-        # assume it is old version without version number
-        fileObject.seek(0)
-        project = xmlutils.load(fileObject.name, knownTypes={"project" : Project_10})
-        if project:
-            project = project.upgradeVersion()
-        else:
-            print "Project, unknown version:", version
-            return None
-            
-    if project:
-        project._projectDir = os.path.dirname(fileObject.name)
-        project.RelativeToAbsPath()
-        
-    return project
-
-
-def save(fileObject, project, productionDeployment=False):
-    if not project._projectDir:
-        project._projectDir = os.path.dirname(fileObject.name)
-    project.AbsToRelativePath()  # temporarily change it to relative paths for saving
-    if ACTIVEGRID_BASE_IDE:
-        KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile}
-    else:
-        KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile, "ag:appInfo" : AppInfo.AppInfo}
-
-    savedHomeDir = project.homeDir        
-    if productionDeployment:
-        # for deployments, we don't want an abs path in homeDir since that
-        # would tie the app to the current filesystem. So unset it.
-        project.homeDir = None
-        
-    xmlutils.save(fileObject.name, project, prettyPrint=True, knownTypes=KNOWNTYPES, knownNamespaces=xmlutils.KNOWN_NAMESPACES)
-
-    if productionDeployment:
-        project.homeDir = savedHomeDir
-    
-    project.RelativeToAbsPath()  # swap it back to absolute path
-
 
 #----------------------------------------------------------------------------
 # Classes
 
 #----------------------------------------------------------------------------
 # Classes
@@ -93,7 +44,7 @@ def save(fileObject, project, productionDeployment=False):
 class BaseProject(object):
 
     __xmlname__ = "project"
 class BaseProject(object):
 
     __xmlname__ = "project"
-    __xmlexclude__ = ('fileName', '_projectDir', '_getDocCallback')
+    __xmlexclude__ = ('fileName', '_projectDir', '_getDocCallback', '_cacheEnabled')
     __xmlattributes__ = ("_homeDir", "version")
     __xmlrename__ = { "_homeDir":"homeDir", "_appInfo":"appInfo" }
     __xmlflattensequence__ = { "_files":("file",) }
     __xmlattributes__ = ("_homeDir", "version")
     __xmlrename__ = { "_homeDir":"homeDir", "_appInfo":"appInfo" }
     __xmlflattensequence__ = { "_files":("file",) }
@@ -107,9 +58,15 @@ class BaseProject(object):
         self._files = []
         self._projectDir = None  # default for homeDir, set on load
         self._homeDir = None         # user set homeDir for use in calculating relative path
         self._files = []
         self._projectDir = None  # default for homeDir, set on load
         self._homeDir = None         # user set homeDir for use in calculating relative path
+        self._cacheEnabled = 0
         if not ACTIVEGRID_BASE_IDE:
             self._appInfo = AppInfo.AppInfo()
         if not ACTIVEGRID_BASE_IDE:
             self._appInfo = AppInfo.AppInfo()
-        
+
+
+    def initialize(self):
+        for file in self._files:
+            file._parentProj = self
+
 
     def __copy__(self):
         clone = Project()
 
     def __copy__(self):
         clone = Project()
@@ -117,18 +74,13 @@ class BaseProject(object):
         clone._projectDir = self._projectDir
         clone._homeDir = self._homeDir
         if not ACTIVEGRID_BASE_IDE:
         clone._projectDir = self._projectDir
         clone._homeDir = self._homeDir
         if not ACTIVEGRID_BASE_IDE:
-            clone._appInfo = self._appInfo
+            clone._appInfo = copy.copy(self._appInfo)
         return clone
 
         return clone
 
-    
-    def initialize(self):
-        """ Required method for xmlmarshaller """
-        pass
-
 
     def GetAppInfo(self):
         return self._appInfo
 
     def GetAppInfo(self):
         return self._appInfo
-        
+
 
     def AddFile(self, filePath=None, logicalFolder=None, type=None, name=None, file=None):
         """ Usage: self.AddFile(filePath, logicalFolder, type, name)  # used for initial generation of object
 
     def AddFile(self, filePath=None, logicalFolder=None, type=None, name=None, file=None):
         """ Usage: self.AddFile(filePath, logicalFolder, type, name)  # used for initial generation of object
@@ -138,7 +90,7 @@ class BaseProject(object):
         if file:
             self._files.append(file)
         else:
         if file:
             self._files.append(file)
         else:
-            self._files.append(ProjectFile(filePath, logicalFolder, type, name, getDocCallback=self._getDocCallback))
+            self._files.append(ProjectFile(self, filePath, logicalFolder, type, name, getDocCallback=self._getDocCallback))
 
 
     def RemoveFile(self, file):
 
 
     def RemoveFile(self, file):
@@ -150,7 +102,7 @@ class BaseProject(object):
             for file in self._files:
                 if file.filePath == filePath:
                     return file
             for file in self._files:
                 if file.filePath == filePath:
                     return file
-                
+
         return None
 
 
         return None
 
 
@@ -160,6 +112,10 @@ class BaseProject(object):
 
     filePaths = property(_GetFilePaths)
 
 
     filePaths = property(_GetFilePaths)
 
+    def _GetProjectFiles(self):
+        return self._files
+    projectFiles = property(_GetProjectFiles)
+
 
     def _GetLogicalFolders(self):
         folders = []
 
     def _GetLogicalFolders(self):
         folders = []
@@ -170,7 +126,7 @@ class BaseProject(object):
 
 
     logicalFolders = property(_GetLogicalFolders)
 
 
     logicalFolders = property(_GetLogicalFolders)
-    
+
 
     def _GetPhysicalFolders(self):
         physicalFolders = []
 
     def _GetPhysicalFolders(self):
         physicalFolders = []
@@ -189,11 +145,11 @@ class BaseProject(object):
             return self._homeDir
         else:
             return self._projectDir
             return self._homeDir
         else:
             return self._projectDir
-        
+
 
     def _SetHomeDir(self, parentPath):
         self._homeDir = parentPath
 
     def _SetHomeDir(self, parentPath):
         self._homeDir = parentPath
-        
+
 
     def _IsDefaultHomeDir(self):
         return (self._homeDir == None)
 
     def _IsDefaultHomeDir(self):
         return (self._homeDir == None)
@@ -212,7 +168,7 @@ class BaseProject(object):
             if relFolder and relFolder not in relativeFolders:
                 relativeFolders.append(relFolder)
         return relativeFolders
             if relFolder and relFolder not in relativeFolders:
                 relativeFolders.append(relFolder)
         return relativeFolders
-        
+
 
     def AbsToRelativePath(self):
         for file in self._files:
 
     def AbsToRelativePath(self):
         for file in self._files:
@@ -224,6 +180,33 @@ class BaseProject(object):
             file.RelativeToAbsPath(self.homeDir)
 
 
             file.RelativeToAbsPath(self.homeDir)
 
 
+    def _SetCache(self, enable):
+        """
+            Only turn this on if your operation assumes files on disk won't change.
+            Once your operation is done, turn this back off.
+            Nested enables are allowed, only the last disable will disable the cache.
+            
+            This bypasses the IsDocumentModificationDateCorrect call because the modification date check is too costly, it hits the disk and takes too long.
+        """
+        if enable:
+            if self._cacheEnabled == 0:
+                # clear old cache, don't want to accidentally return stale value
+                for file in self._files:
+                    file.ClearCache()        
+
+            self._cacheEnabled += 1
+        else:
+            self._cacheEnabled -= 1
+            
+
+
+    def _GetCache(self):
+        return (self._cacheEnabled > 0)
+
+
+    cacheEnabled = property(_GetCache, _SetCache)
+
+
     #----------------------------------------------------------------------------
     # BaseDocumentMgr methods
     #----------------------------------------------------------------------------
     #----------------------------------------------------------------------------
     # BaseDocumentMgr methods
     #----------------------------------------------------------------------------
@@ -231,7 +214,7 @@ class BaseProject(object):
 
     def fullPath(self, fileName):
         fileName = super(BaseProject, self).fullPath(fileName)
 
     def fullPath(self, fileName):
         fileName = super(BaseProject, self).fullPath(fileName)
-        
+
         if os.path.isabs(fileName):
             absPath = fileName
         elif self.homeDir:
         if os.path.isabs(fileName):
             absPath = fileName
         elif self.homeDir:
@@ -242,7 +225,7 @@ class BaseProject(object):
 
 
     def documentRefFactory(self, name, fileType, filePath):
 
 
     def documentRefFactory(self, name, fileType, filePath):
-        return ProjectFile(filePath=self.fullPath(filePath), type=fileType, name=name, getDocCallback=self._getDocCallback)
+        return ProjectFile(self, filePath=self.fullPath(filePath), type=fileType, name=name, getDocCallback=self._getDocCallback)
 
 
     def findAllRefs(self):
 
 
     def findAllRefs(self):
@@ -256,8 +239,8 @@ class BaseProject(object):
         if not xformdir:
             xformdir = self.homeDir
         return xformdir
         if not xformdir:
             xformdir = self.homeDir
         return xformdir
-         
-   
+
+
     def setRefs(self, files):
         self._files = files
 
     def setRefs(self, files):
         self._files = files
 
@@ -295,16 +278,17 @@ if ACTIVEGRID_BASE_IDE:
 else:
     class Project(BaseProject, basedocmgr.BaseDocumentMgr):
         pass
 else:
     class Project(BaseProject, basedocmgr.BaseDocumentMgr):
         pass
-        
+
 
 class ProjectFile(object):
     __xmlname__ = "file"
 
 class ProjectFile(object):
     __xmlname__ = "file"
-    __xmlexclude__ = ('_getDocCallback', '_docCallbackCacheReturnValue', '_docModelCallbackCacheReturnValue', '_doc',)
+    __xmlexclude__ = ('_parentProj', '_getDocCallback', '_docCallbackCacheReturnValue', '_docModelCallbackCacheReturnValue', '_doc',)
     __xmlattributes__ = ["filePath", "logicalFolder", "type", "name"]
     __xmldefaultnamespace__ = xmlutils.AG_NS_URL
 
 
     __xmlattributes__ = ["filePath", "logicalFolder", "type", "name"]
     __xmldefaultnamespace__ = xmlutils.AG_NS_URL
 
 
-    def __init__(self, filePath=None, logicalFolder=None, type=None, name=None, getDocCallback=None):
+    def __init__(self, parent=None, filePath=None, logicalFolder=None, type=None, name=None, getDocCallback=None):
+        self._parentProj = parent
         self.filePath = filePath
         self.logicalFolder = logicalFolder
         self.type = type
         self.filePath = filePath
         self.logicalFolder = logicalFolder
         self.type = type
@@ -316,35 +300,44 @@ class ProjectFile(object):
 
 
     def _GetDocumentModel(self):
 
 
     def _GetDocumentModel(self):
-        # possible bug is if document gets replaced outside of IDE, where we'll return previous doc.
-        # originally added a timestamp check but that increased the time again to 4x
-        if self._docModelCallbackCacheReturnValue:  # accelerator for caching document, got 4x speed up.
+        if (self._docCallbackCacheReturnValue
+        and (self._parentProj.cacheEnabled or self._docCallbackCacheReturnValue.IsDocumentModificationDateCorrect())):
             return self._docModelCallbackCacheReturnValue
             return self._docModelCallbackCacheReturnValue
-            
+
         if self._getDocCallback:
             self._docCallbackCacheReturnValue, self._docModelCallbackCacheReturnValue = self._getDocCallback(self.filePath)
             return self._docModelCallbackCacheReturnValue
         if self._getDocCallback:
             self._docCallbackCacheReturnValue, self._docModelCallbackCacheReturnValue = self._getDocCallback(self.filePath)
             return self._docModelCallbackCacheReturnValue
-            
+
         return None
 
         return None
 
-        
+
     document = property(_GetDocumentModel)
     document = property(_GetDocumentModel)
-    
+
 
     def _GetDocument(self):
 
     def _GetDocument(self):
-        # Hack, just return the IDE document wrapper that corresponds to the runtime document model
-        # callers should have called ".document" before calling ".ideDocument"
-        if self._docCallbackCacheReturnValue:  # accelerator for caching document, got 4x speed up.
+        # Return the IDE document wrapper that corresponds to the runtime document model
+        if (self._docCallbackCacheReturnValue
+        and (self._parentProj.cacheEnabled or self._docCallbackCacheReturnValue.IsDocumentModificationDateCorrect())):
             return self._docCallbackCacheReturnValue
             return self._docCallbackCacheReturnValue
+
+        if self._getDocCallback:
+            self._docCallbackCacheReturnValue, self._docModelCallbackCacheReturnValue = self._getDocCallback(self.filePath)
+            return self._docCallbackCacheReturnValue
+
         return None
         return None
-    
+
 
     ideDocument = property(_GetDocument)
 
 
 
     ideDocument = property(_GetDocument)
 
 
+    def ClearCache(self):
+        self._docCallbackCacheReturnValue = None
+        self._docModelCallbackCacheReturnValue = None
+
+
     def _typeEnumeration(self):
         return basedocmgr.FILE_TYPE_LIST
     def _typeEnumeration(self):
         return basedocmgr.FILE_TYPE_LIST
-        
+
 
     def _GetPhysicalFolder(self):
         dir = None
 
     def _GetPhysicalFolder(self):
         dir = None
@@ -356,7 +349,7 @@ class ProjectFile(object):
 
 
     physicalFolder = property(_GetPhysicalFolder)
 
 
     physicalFolder = property(_GetPhysicalFolder)
-    
+
 
     def GetRelativeFolder(self, parentPath):
         parentPathLen = len(parentPath)
 
     def GetRelativeFolder(self, parentPath):
         parentPathLen = len(parentPath)
@@ -364,7 +357,7 @@ class ProjectFile(object):
         dir = None
         if self.filePath:
             dir = os.path.dirname(self.filePath)
         dir = None
         if self.filePath:
             dir = os.path.dirname(self.filePath)
-            if dir.startswith(parentPath):
+            if dir.startswith(parentPath + os.sep):
                 dir = "." + dir[parentPathLen:]  # convert to relative path
             if os.sep != '/':
                 dir = dir.replace(os.sep, '/')  # always save out with '/' as path separator for cross-platform compatibility.
                 dir = "." + dir[parentPathLen:]  # convert to relative path
             if os.sep != '/':
                 dir = dir.replace(os.sep, '/')  # always save out with '/' as path separator for cross-platform compatibility.
@@ -375,7 +368,7 @@ class ProjectFile(object):
         """ Used to convert path to relative path for saving (disk format) """
         parentPathLen = len(parentPath)
 
         """ Used to convert path to relative path for saving (disk format) """
         parentPathLen = len(parentPath)
 
-        if self.filePath.startswith(parentPath):
+        if self.filePath.startswith(parentPath + os.sep):
             self.filePath = "." + self.filePath[parentPathLen:]  # convert to relative path
             if os.sep != '/':
                 self.filePath = self.filePath.replace(os.sep, '/')  # always save out with '/' as path separator for cross-platform compatibility.
             self.filePath = "." + self.filePath[parentPathLen:]  # convert to relative path
             if os.sep != '/':
                 self.filePath = self.filePath.replace(os.sep, '/')  # always save out with '/' as path separator for cross-platform compatibility.
@@ -385,8 +378,8 @@ class ProjectFile(object):
 
     def RelativeToAbsPath(self, parentPath):
         """ Used to convert path to absolute path (for any necessary disk access) """
 
     def RelativeToAbsPath(self, parentPath):
         """ Used to convert path to absolute path (for any necessary disk access) """
-        if self.filePath.startswith("."):  # relative to project file
-            self.filePath = os.path.normpath(os.path.join(parentPath, self.filePath))
+        if self.filePath.startswith("./"):  # relative to project file
+            self.filePath = os.path.normpath(os.path.join(parentPath, self.filePath))  # also converts '/' to os.sep
 
 
     #----------------------------------------------------------------------------
 
 
     #----------------------------------------------------------------------------
@@ -399,21 +392,29 @@ class ProjectFile(object):
         import wx.lib.docview
         if not self._doc:
             docMgr = wx.GetApp().GetDocumentManager()
         import wx.lib.docview
         if not self._doc:
             docMgr = wx.GetApp().GetDocumentManager()
-            
-            doc = docMgr.CreateDocument(self.filePath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
-            if (doc == None):  # already open
-                docs = docMgr.GetDocuments()
-                for d in docs:
-                    if d.GetFilename() == self.filePath:
-                        doc = d
-                        break
-            self._doc = doc
+
+            try:
+                doc = docMgr.CreateDocument(self.filePath, docMgr.GetFlags()|wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE|wx.lib.docview.DOC_NO_VIEW)
+                if (doc == None):  # already open
+                    docs = docMgr.GetDocuments()
+                    for d in docs:
+                        if d.GetFilename() == self.filePath:
+                            doc = d
+                            break
+                self._doc = doc
+            except Exception,e:
+                aglogging.reportException(e, stacktrace=True)
+                
         return self._doc
         return self._doc
-        
+
 
     def _GetLocalServiceProcessName(self):
         # HACK: temporary solution to getting process name from wsdlag file.
 
     def _GetLocalServiceProcessName(self):
         # HACK: temporary solution to getting process name from wsdlag file.
-        return self._GetDoc().GetModel().processName
+        doc = self._GetDoc()
+        if doc:
+            return doc.GetModel().processName
+        else:
+            return None
 
 
     processName = property(_GetLocalServiceProcessName)
 
 
     processName = property(_GetLocalServiceProcessName)
@@ -458,33 +459,39 @@ class ProjectFile(object):
     localServiceClassName = property(_GetLocalServiceClassName, _SetLocalServiceClassName)
 
 
     localServiceClassName = property(_GetLocalServiceClassName, _SetLocalServiceClassName)
 
 
+    def getServiceParameter(self, message, part):
+        return self._GetDoc().GetModel().getServiceParameter(message, part)
+
 
 # only activate this code if we programatically need to access these values
 ##    def _GetRssServiceBaseURL(self):
 ##        return self._GetDoc().GetModel().rssServiceBaseURL
 ##
 
 # only activate this code if we programatically need to access these values
 ##    def _GetRssServiceBaseURL(self):
 ##        return self._GetDoc().GetModel().rssServiceBaseURL
 ##
-##        
+##
 ##    def _SetRssServiceBaseURL(self, baseURL):
 ##        self._GetDoc().GetModel().rssServiceBaseURL = baseURL
 ##    def _SetRssServiceBaseURL(self, baseURL):
 ##        self._GetDoc().GetModel().rssServiceBaseURL = baseURL
-##         
-##   
+##
+##
 ##    rssServiceBaseURL = property(_GetRssServiceBaseURL, _SetRssServiceBaseURL)
 ##
 ##
 ##    def _GetRssServiceRssVersion(self):
 ##        return self._GetDoc().GetModel().rssServiceRssVersion
 ##    rssServiceBaseURL = property(_GetRssServiceBaseURL, _SetRssServiceBaseURL)
 ##
 ##
 ##    def _GetRssServiceRssVersion(self):
 ##        return self._GetDoc().GetModel().rssServiceRssVersion
-##        
+##
 ##
 ##    def _SetRssServiceRssVersion(self, rssVersion):
 ##        self._GetDoc().GetModel().rssServiceRssVersion = rssVersion
 ##
 ##    def _SetRssServiceRssVersion(self, rssVersion):
 ##        self._GetDoc().GetModel().rssServiceRssVersion = rssVersion
-##            
+##
 ##
 ##    rssServiceRssVersion = property(_GetRssServiceRssVersion, _SetRssServiceRssVersion)
 
 
     def _GetServiceRefServiceType(self):
         # HACK: temporary solution to getting service type from wsdlag file.
 ##
 ##    rssServiceRssVersion = property(_GetRssServiceRssVersion, _SetRssServiceRssVersion)
 
 
     def _GetServiceRefServiceType(self):
         # HACK: temporary solution to getting service type from wsdlag file.
-        model = self._GetDoc().GetModel()
+        doc = self._GetDoc()
+        if not doc:
+            return None
+        model = doc.GetModel()
         if hasattr(model, 'serviceType'):
             return model.serviceType
         else:
         if hasattr(model, 'serviceType'):
             return model.serviceType
         else:
@@ -494,25 +501,27 @@ class ProjectFile(object):
     def _SetServiceRefServiceType(self, serviceType):
         # HACK: temporary solution to getting service type from wsdlag file.
         self._GetDoc().GetModel().serviceType = serviceType
     def _SetServiceRefServiceType(self, serviceType):
         # HACK: temporary solution to getting service type from wsdlag file.
         self._GetDoc().GetModel().serviceType = serviceType
-        
+
 
     serviceType = property(_GetServiceRefServiceType, _SetServiceRefServiceType)
 
 
     def getExternalPackage(self):
         # HACK: temporary solution to getting custom code filename from wsdlag file.
 
     serviceType = property(_GetServiceRefServiceType, _SetServiceRefServiceType)
 
 
     def getExternalPackage(self):
         # HACK: temporary solution to getting custom code filename from wsdlag file.
-        import activegrid.server.deployment as deploymentlib    
-        
+        import activegrid.model.projectmodel as projectmodel
+        import wx
+        import ProjectEditor
+
         appInfo = self._GetDoc().GetAppInfo()
 
         if appInfo.language == None:
         appInfo = self._GetDoc().GetAppInfo()
 
         if appInfo.language == None:
-            language = deploymentlib.LANGUAGE_DEFAULT
+            language = wx.ConfigBase_Get().Read(ProjectEditor.APP_LAST_LANGUAGE, projectmodel.LANGUAGE_DEFAULT)
         else:
             language = appInfo.language
         else:
             language = appInfo.language
-            
-        if language == deploymentlib.LANGUAGE_PYTHON:
+
+        if language == projectmodel.LANGUAGE_PYTHON:
             suffix = ".py"
             suffix = ".py"
-        elif language == deploymentlib.LANGUAGE_PHP:
+        elif language == projectmodel.LANGUAGE_PHP:
             suffix = ".php"
         pyFilename = self.name + suffix
         return self._GetDoc().GetAppDocMgr().fullPath(pyFilename)
             suffix = ".php"
         pyFilename = self.name + suffix
         return self._GetDoc().GetAppDocMgr().fullPath(pyFilename)
@@ -543,7 +552,63 @@ class Project_10:
     def upgradeVersion(self):
         currModel = Project()
         for file in self._files:
     def upgradeVersion(self):
         currModel = Project()
         for file in self._files:
-            currModel._files.append(ProjectFile(file))
+            currModel._files.append(ProjectFile(currModel, file))
         return currModel
 
 
         return currModel
 
 
+#----------------------------------------------------------------------------
+# XML Marshalling Methods
+#----------------------------------------------------------------------------
+
+if ACTIVEGRID_BASE_IDE:
+    KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile}
+else:
+    KNOWNTYPES = {"ag:project" : Project, "ag:file" : ProjectFile,
+                  "ag:appInfo" : AppInfo.AppInfo,
+                  "ag:deploymentDataSource" : AppInfo.DeploymentDataSource,
+                  "ag:dataSourceBinding" : AppInfo.DataSourceBinding}
+
+def load(fileObject):
+    version = xmlutils.getAgVersion(fileObject.name)
+    # most current versions on top
+    if version == PROJECT_VERSION_050826:
+        fileObject.seek(0)
+        project = xmlutils.load(fileObject.name, knownTypes=KNOWNTYPES, knownNamespaces=xmlutils.KNOWN_NAMESPACES, createGenerics=True)
+    elif version == PROJECT_VERSION_050730:
+        fileObject.seek(0)
+        project = xmlutils.load(fileObject.name, knownTypes={"project" : Project_10}, createGenerics=True)
+        project = project.upgradeVersion()
+    else:
+        # assume it is old version without version number
+        fileObject.seek(0)
+        project = xmlutils.load(fileObject.name, knownTypes={"project" : Project_10}, createGenerics=True)
+        if project:
+            project = project.upgradeVersion()
+        else:
+            print "Project, unknown version:", version
+            return None
+
+    if project:
+        project._projectDir = os.path.dirname(fileObject.name)
+        project.RelativeToAbsPath()
+
+    return project
+
+
+def save(fileObject, project, productionDeployment=False):
+    if not project._projectDir:
+        project._projectDir = os.path.dirname(fileObject.name)
+    project.AbsToRelativePath()  # temporarily change it to relative paths for saving
+    savedHomeDir = project.homeDir
+    if productionDeployment:
+        # for deployments, we don't want an abs path in homeDir since that
+        # would tie the app to the current filesystem. So unset it.
+        project.homeDir = None
+
+    xmlutils.save(fileObject.name, project, prettyPrint=True, knownTypes=KNOWNTYPES, knownNamespaces=xmlutils.KNOWN_NAMESPACES)
+
+    if productionDeployment:
+        project.homeDir = savedHomeDir
+
+    project.RelativeToAbsPath()  # swap it back to absolute path
+
index 227e9726fa17f9bf64eb6b69853feeb38a2d7111..17ab688e1c617c384a4dc48d0a5fab6fb45d0dbb 100644 (file)
@@ -19,6 +19,7 @@ import logging.config
 from activegrid.util.lang import *
 import activegrid.util.objutils as objutils
 import activegrid.util.sysutils as sysutils
 from activegrid.util.lang import *
 import activegrid.util.objutils as objutils
 import activegrid.util.sysutils as sysutils
+import activegrid.util.appdirs as appdirs
 
 LEVEL_FATAL = logging.FATAL
 LEVEL_ERROR = logging.ERROR
 
 LEVEL_FATAL = logging.FATAL
 LEVEL_ERROR = logging.ERROR
@@ -27,38 +28,47 @@ LEVEL_INFO = logging.INFO
 LEVEL_DEBUG = logging.DEBUG
 
 EXCEPTION_INFO = 'exceptionInfo'
 LEVEL_DEBUG = logging.DEBUG
 
 EXCEPTION_INFO = 'exceptionInfo'
+loggingInitialized = False
 
 LOG_MODE_IDE = 1
 LOG_MODE_TESTRUN = 2
 LOG_MODE_RUN = 3
 
 LOG_MODE_IDE = 1
 LOG_MODE_TESTRUN = 2
 LOG_MODE_RUN = 3
-def initLogging(mode):
-    configFile = None
-    if (mode == LOG_MODE_IDE):
-        configFile = os.getenv("AG_LOGCONFIG_IDE")
-    elif (mode == LOG_MODE_TESTRUN):
-        configFile = os.getenv("AG_LOGCONFIG_TESTRUN")
-    else:
-        configFile = os.getenv("AG_LOGCONFIG_RUN")
-    if ((configFile == None) or not os.path.exists(configFile)):
+def initLogging(mode, force=False):
+    global ag_debugLogger, loggingInitialized
+    if (force or not loggingInitialized):
+        loggingInitialized = True
+        configFile = None
         if (mode == LOG_MODE_IDE):
         if (mode == LOG_MODE_IDE):
-            configFile = "IDELog"
+            configFile = os.getenv("AG_LOGCONFIG_IDE")
         elif (mode == LOG_MODE_TESTRUN):
         elif (mode == LOG_MODE_TESTRUN):
-            configFile = "TestRunLog"
+            configFile = os.getenv("AG_LOGCONFIG_PYTESTRUN")
         else:
         else:
-            configFile = "RunLog"
-        configFile = sysutils.mainModuleDir + "/py" + configFile + ".ini"
-    if (os.path.exists(configFile)):
-        fileConfig(configFile)
-    else:
-        defaultStream = sys.stderr
-        if (mode == LOG_MODE_RUN):
-            defaultStream = sys.stdout
-        handler = logging.StreamHandler(defaultStream)
-        handler.setLevel(logging.INFO)
-        handler.setFormatter(logging.Formatter("%(asctime)s %(name)s %(levelname)s: %(message)s"))
-        logging.getLogger().addHandler(handler)
-    return configFile
-
+            configFile = os.getenv("AG_LOGCONFIG_RUN")
+        if ((configFile == None) or not os.path.exists(configFile)):
+            if (mode == LOG_MODE_IDE):
+                configFile = "IDELog"
+            elif (mode == LOG_MODE_TESTRUN):
+                configFile = "TestRunLog"
+            else:
+                configFile = "RunLog"
+            configFile = os.path.join(appdirs.getSystemDir(appdirs.AG_LOGS_DIR), "py" + configFile + ".ini")
+        if (os.path.exists(configFile)):
+            print "Using logging configuration file: %s" % configFile
+            fileConfig(configFile)
+        else:
+            print "*** Cannot find logging configuration file (%s) -- setting default logging level to WARN ***" % (configFile)
+            defaultStream = sys.stderr
+            if (mode == LOG_MODE_RUN):
+                defaultStream = sys.stdout
+            handler = logging.StreamHandler(defaultStream)
+            handler.setLevel(logging.DEBUG)
+            handler.setFormatter(logging.Formatter("%(asctime)s %(name)s %(levelname)s: %(message)s"))
+            logging.getLogger().addHandler(handler)
+            logging.getLogger().setLevel(logging.WARN)
+        ag_debugLogger = logging.getLogger("activegrid.debug")
+        ag_debugLogger.setLevel(logging.DEBUG)
+        return configFile
+    
 ag_debugLogger = logging.getLogger("activegrid.debug")
 
 def log(logger, level, msg, *params):
 ag_debugLogger = logging.getLogger("activegrid.debug")
 
 def log(logger, level, msg, *params):
@@ -181,21 +191,18 @@ def addExceptionInfo(e, key, value):
     if not e.exceptionInfo.has_key(key): # Never overwrite exception info since we assume earlier info is more specific
         e.exceptionInfo[key] = value
             
     if not e.exceptionInfo.has_key(key): # Never overwrite exception info since we assume earlier info is more specific
         e.exceptionInfo[key] = value
             
-def reportException(out=None, stacktrace=False, diffable=False, exception=None):
+def reportException(exception, out=None, stacktrace=False, diffable=False):
     exstr = exceptionToString(exception, stacktrace, diffable)
     if (out == None):
         print exstr
     else:
         print >> out, exstr
 
     exstr = exceptionToString(exception, stacktrace, diffable)
     if (out == None):
         print exstr
     else:
         print >> out, exstr
 
-def exceptionToString(exception=None, stacktrace=False, diffable=False):
-    if (exception == None):
-        extype, val, t = sys.exc_info()
-    else:
-        extype = objutils.typeToString(exception)
-        val = exception
-        if (stacktrace):
-            e,v,t = sys.exc_info()
+def exceptionToString(exception, stacktrace=False, diffable=False):
+    extype = objutils.typeToString(exception)
+    val = exception
+    if (stacktrace):
+        e,v,t = sys.exc_info()
     if (diffable):
         exstr = removeFileRefs(str(val))
     else:
     if (diffable):
         exstr = removeFileRefs(str(val))
     else:
index 7b73d5c4f489c91211ac56f2ffeb97b5fa6205b6..d3e038b04c8aa9d3b333cf016ec7e98fdd5c7989 100644 (file)
@@ -2,7 +2,7 @@
 # Name:         appdirs.py
 # Purpose:      Utilities for retrieving special application dirs
 #
 # Name:         appdirs.py
 # Purpose:      Utilities for retrieving special application dirs
 #
-# Author:       Kevin Ollivier
+# Author:       Kevin Ollivier, Jeff Norton
 #
 # Created:      8/27/05
 # CVS-ID:       $Id$
 #
 # Created:      8/27/05
 # CVS-ID:       $Id$
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
-# NOTE: This was made a separate file because it depends upon the 
-# wx.StandardPaths module, and thus, on wxWidgets, unlike other
-# utils modules. I wanted to ensure this module is never loaded
-# from the web server, etc.
-
+from activegrid.util.lang import *
 import sys
 import os
 import string
 import sys
 import os
 import string
-import wx
-
-def isWindows():
-    return os.name == 'nt'
-
-def _generateDocumentsDir():
-    path = ""
-    if sys.platform == "win32":
-        from win32com.shell import shell, shellcon
-        path=shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
-    elif sys.platform == "darwin":
-        import macfs, MACFS
-        fsspec_disk, fsspec_desktop = macfs.FindFolder( MACFS.kOnSystemDisk, MACFS.kDocumentsFolderType, 0)
-        path = macfs.FSSpec((fsspec_disk, fsspec_desktop, '')).as_pathname()
-    
-    if path == "":
-        path = os.path.expanduser("~")
-
-    return path
+import activegrid.util.sysutils as sysutils
+
+def _getSystemDir(kind):
+    if (kind == AG_LOGS_DIR):
+        return os.path.join(getSystemDir(AG_SYSTEM_DIR) , "logs")
+    elif (kind == AG_DEMOS_DIR):
+        return os.path.join(getSystemDir(AG_SYSTEM_DIR), "demos")
+    else:
+        path = ""
+        if (sysutils.isServer()):
+            path = os.getenv("ACTIVEGRID_SERVER_HOME")
+            if ((path is None) or (len(path) < 1)):
+                path = sysutils.mainModuleDir
+        else:
+            path = os.getenv("AG_DOCUMENTS_DIR")
+            if ((path is None) or (len(path) < 1)):
+                if sysutils.isWindows():
+                    ifDefPy()
+                    try:
+                        from win32com.shell import shell, shellcon
+                        path = shell.SHGetFolderPath(0, shellcon.CSIDL_PERSONAL, None, 0)
+                    except:
+                        pass
+                    endIfDef()
+                    if ((path is None) or (len(path) < 1)):
+                        homedrive = asString(os.getenv("HOMEDRIVE"))
+                        homepath = os.getenv("HOMEPATH")
+##                        if ((homedrive is not None) and (len(homedrive) > 0) and (homepath is not None) and (len(homepath) > 0)):
+                        path = os.path.join(homedrive, homepath, "MYDOCU~1")
+                else:
+                    ifDefPy()
+                    if sys.platform == "darwin":
+                        try:
+                            import macfs
+                            import MACFS
+                            fsspec_disk, fsspec_desktop = macfs.FindFolder(MACFS.kOnSystemDisk, MACFS.kDocumentsFolderType, 0)
+                            path = macfs.FSSpec((fsspec_disk, fsspec_desktop, '')).as_pathname()
+                        except:
+                            pass
+                    endIfDef()
+                
+                ifDefPy()
+                if ((path is None) or (len(path) < 1)):
+                    path = os.path.expanduser("~")
+                endIfDef()
+                if ((path is None) or (len(path) < 1)):
+                    path = "/"
+                path = os.path.join(path, "ActiveGrid")
             
             
-documents_folder = _generateDocumentsDir()
+        return path
+        
+
+AG_SYSTEM_DIR = 0
+AG_LOGS_DIR = 1
+AG_DEMOS_DIR = 2
+
+__systemDir = None
+__logsDir = None
+__demosDir = None
+
+def getSystemDir(kind=0):
+    if (kind == AG_SYSTEM_DIR):
+        global __systemDir
+        if (__systemDir is None):
+            __systemDir = _getSystemDir(kind)
+        return __systemDir
+    elif (kind == AG_LOGS_DIR):
+        global __logsDir
+        if (__logsDir is None):
+            __logsDir = _getSystemDir(kind)
+        return __logsDir
+    elif (kind == AG_DEMOS_DIR):
+        global __demosDir
+        if (__demosDir is None):
+            __demosDir = _getSystemDir(kind)
+        return __demosDir
+    return None
+
 
 # NOTE: We don't set this at startup because wxStandardPaths needs a running
 # application object. This makes sure the wxApp will always be created when
 # we get the folder.
 
 # NOTE: We don't set this at startup because wxStandardPaths needs a running
 # application object. This makes sure the wxApp will always be created when
 # we get the folder.
+ifDefPy()
 def getAppDataFolder():
 def getAppDataFolder():
-    # wxStandardPaths requires a running app
-    if wx.GetApp() and wx.Platform != "__WXGTK__":
-        data_folder = wx.StandardPaths.Get().GetUserDataDir()
-        if not os.path.exists(data_folder):
-            os.mkdir(data_folder)
-        return data_folder
-    else:
-        # wxBug: on *nix, it wants to point to ~/.appname, but
-        # so does wxConfig... For now, redirect this to ~/.appbuilder
-        # when this is fixed, we'll migrate settings to the correct place
-        return os.path.join(os.path.expanduser("~"), ".appbuilder")
+    try:
+        # NOTE: cannot import wx from the server
+        import wx
+        # wxStandardPaths requires a running app
+        if wx.GetApp() and wx.Platform != "__WXGTK__":
+            data_folder = wx.StandardPaths.Get().GetUserDataDir()
+            if not os.path.exists(data_folder):
+                os.mkdir(data_folder)
+            return data_folder
+    except:
+        pass
+    # wxBug: on *nix, it wants to point to ~/.appname, but
+    # so does wxConfig... For now, redirect this to ~/.appbuilder
+    # when this is fixed, we'll migrate settings to the correct place
+    return os.path.join(os.path.expanduser("~"), ".appbuilder")
+endIfDef()
 
 
-    return ""
+ifDefPy()
+def createSystemDirs():
+    if (not os.path.exists(getSystemDir())):
+        os.mkdir(getSystemDir())
+    if (not os.path.exists(getSystemDir(AG_LOGS_DIR))):
+        os.mkdir(getSystemDir(AG_LOGS_DIR))
+    if (not os.path.exists(getSystemDir(AG_DEMOS_DIR))):
+        os.mkdir(getSystemDir(AG_DEMOS_DIR))
+endIfDef()
diff --git a/wxPython/samples/ide/activegrid/util/datetimeparser.py b/wxPython/samples/ide/activegrid/util/datetimeparser.py
new file mode 100644 (file)
index 0000000..3731658
--- /dev/null
@@ -0,0 +1,118 @@
+#----------------------------------------------------------------------------
+# Name:         datetimeparser.py
+#
+# Purpose:      - Instantiate datetime.datetime/date instance from a string
+#                 date representation.
+#                 Uses dateutil from http://labix.org/python-dateutil.
+#
+#               - Creates string representation of datetime/date instance.
+#
+#
+# Author:       Simon Toens
+#
+# Created:      28-Feb-06
+# CVS-ID:
+# Copyright:    (c) 2005 ActiveGrid, Inc.
+# License:      wxWindows License
+#----------------------------------------------------------------------------
+
+import datetime
+
+try:
+    import dateutil.parser
+    DATEUTIL_INSTALLED = True
+except ImportError:
+    DATEUTIL_INSTALLED = False
+
+ISO_8601_DATE_FORMAT = "%Y-%m-%d"
+ISO_8601_TIME_FORMAT = "%H:%M:%S"
+ISO_8601_DATETIME_FORMAT = "%s %s" %(ISO_8601_DATE_FORMAT,
+                                     ISO_8601_TIME_FORMAT)
+
+DEFAULT_DATETIME = datetime.datetime(1, 1, 1, 0, 0, 0, 0)
+
+
+def format(dateobj, formatstr=None):
+    if (formatstr != None and _isDateTimeObject(dateobj)):
+        return dateobj.strftime(str(formatstr))
+    return str(dateobj)
+
+
+def parse(datestr, formatstr=None, asdate=False, astime=False):
+    """Instantiates and returns a datetime instance from the datestr datetime
+       representation.
+       
+       Optionally, a format string may be used. The format is only loosely
+       interpreted, its only purpose beeing to determine if the year is first
+       or last in datestr, and whether the day is in front or follows the
+       month. If no formatstr is passed in, dateutil tries its best to parse
+       the datestr. The default date format is YYYY-mm-dd HH:SS.
+       
+       If asdate is True, returns a date instance instead of a datetime
+       instance, if astime is True, returns a time instance instead of a
+       datetime instance."""
+    
+       
+    dayfirst, yearfirst = _getDayFirstAndYearFirst(formatstr)
+
+    rtn = None
+
+    try:
+        if DATEUTIL_INSTALLED:
+            rtn = dateutil.parser.parse(str(datestr), fuzzy=True,
+                                        dayfirst=dayfirst, yearfirst=yearfirst,
+                                        default=DEFAULT_DATETIME)
+        else:
+            rtn = DEFAULT_DATETIME
+    except:
+        rtn = DEFAULT_DATETIME
+
+    if (asdate and isinstance(rtn, datetime.datetime)):
+        rtn = datetime.date(rtn.year, rtn.month, rtn.day)
+    elif (astime and isinstance(rtn, datetime.datetime)):
+        rtn = datetime.time(rtn.hour, rtn.minute, rtn.second, rtn.microsecond)
+
+    return rtn
+
+
+def _isDateTimeObject(obj):
+    return (isinstance(obj, datetime.datetime) or
+            isinstance(obj, datetime.date) or
+            isinstance(obj, datetime.time))
+
+
+def _getDayFirstAndYearFirst(formatstr):
+    dayFirst = False
+    yearFirst = False
+    
+    gotYear = False
+    gotMonth = False
+    gotDay = False
+
+    if (formatstr == None):
+        formatstr = ""
+    
+    for c in formatstr:
+        if (c.lower() == "y"):
+            if (gotYear):
+                continue
+            if (not gotDay and not gotMonth):
+                yearFirst = True
+            gotYear = True
+
+        elif (c.lower() == "m"):
+            if (gotMonth):
+                continue
+            if (not gotDay):
+                dayFirst = False
+            gotMonth = True
+            
+        elif (c.lower() == "d"):
+            if (gotDay):
+                continue
+            if (not gotMonth):
+                dayFirst = True
+            gotDay = True
+        
+
+    return dayFirst, yearFirst
index 2a75997aeab4b4e184018483b5439379209cfdf3..ca5ee1fb8fa63a646aa60f22dbad96eb291143e6 100644 (file)
@@ -19,6 +19,7 @@ import zipfile
 
 import activegrid.util.aglogging as aglogging
 import activegrid.util.sysutils as sysutils
 
 import activegrid.util.aglogging as aglogging
 import activegrid.util.sysutils as sysutils
+import activegrid.util.utillang as utillang
 from activegrid.util.lang import *
 
 global fileutilsLogger
 from activegrid.util.lang import *
 
 global fileutilsLogger
@@ -31,6 +32,65 @@ fileutilsLogger = logging.getLogger("activegrid.util.fileutils")
 aglogging.setLevelFatal(fileutilsLogger)
 #logging.getLogger().addHandler(logging.StreamHandler(sys.stderr))
 
 aglogging.setLevelFatal(fileutilsLogger)
 #logging.getLogger().addHandler(logging.StreamHandler(sys.stderr))
 
+def addRef(varname):
+    return "${%s}" % varname
+
+AG_SYSTEM_VAR_NAMES = [] # all AG System vars, with ${} syntax
+
+AG_SYSTEM_VAR = "AG_SYSTEM"
+AG_SYSTEM_VAR_REF = addRef(AG_SYSTEM_VAR)
+AG_SYSTEM_VAR_NAMES.append(AG_SYSTEM_VAR_REF)
+
+AG_SYSTEM_STATIC_VAR = "AG_SYSTEM_STATIC"
+AG_SYSTEM_STATIC_VAR_REF = addRef(AG_SYSTEM_STATIC_VAR)
+AG_SYSTEM_VAR_NAMES.append(AG_SYSTEM_STATIC_VAR_REF)
+
+AG_APP_VAR = "AG_APP"
+AG_APP_STATIC_VAR = "AG_APP_STATIC"
+
+# _initAGSystemVars needs to be called to initialize the following two
+# containers:
+EXPANDED_AG_SYSTEM_VARS = {} # ${varname} -> value (path)
+# ${varname}, ordered from longest to shortest path value
+AG_SYSTEM_VARS_LENGTH_ORDER = [] 
+
+def _initAGSystemVars():
+    if (len(EXPANDED_AG_SYSTEM_VARS) > 0):
+        return
+    
+    for v in AG_SYSTEM_VAR_NAMES:
+        EXPANDED_AG_SYSTEM_VARS[v] = os.path.abspath(expandVars(v))
+        AG_SYSTEM_VARS_LENGTH_ORDER.append(v)
+        
+    AG_SYSTEM_VARS_LENGTH_ORDER.sort(_sortByValLength)
+
+
+def parameterizePathWithAGSystemVar(inpath):
+    """Returns parameterized path if path starts with a known AG directory. Otherwise returns path as it was passed in."""
+    _initAGSystemVars()
+    path = inpath
+    if not sysutils.isWindows():
+        # ensure we have forward slashes
+        path = path.replace("\\", "/")
+        
+    path = os.path.abspath(path)
+
+    for varname in AG_SYSTEM_VARS_LENGTH_ORDER:
+        varval = EXPANDED_AG_SYSTEM_VARS[varname]
+        if path.startswith(varval):
+            return path.replace(varval, varname)
+        
+    return inpath
+
+def startsWithAgSystemVar(path):
+    """Returns True if path starts with a known AG system env var, False otherwise."""
+    for varname in AG_SYSTEM_VAR_NAMES:
+        if path.startswith(varname):
+            return True
+    return False
+        
+def _sortByValLength(v1, v2):
+    return len(EXPANDED_AG_SYSTEM_VARS[v2]) - len(EXPANDED_AG_SYSTEM_VARS[v1])
 
 def makeDirsForFile(filename):
     d = os.path.dirname(filename)
 
 def makeDirsForFile(filename):
     d = os.path.dirname(filename)
@@ -44,7 +104,7 @@ def createFile(filename, mode='w'):
     f = file(filename, mode)
     return f
 
     f = file(filename, mode)
     return f
 
-def compareFiles(file1, file2):
+def compareFiles(file1, file2, ignore=None):
 ##    result = filecmp.cmp(file1, file2)
 ##    if result:
 ##        return 0
 ##    result = filecmp.cmp(file1, file2)
 ##    if result:
 ##        return 0
@@ -62,6 +122,9 @@ def compareFiles(file1, file2):
         elif (len(line2) == 0):
             return -1
         elif (line1 != line2):
         elif (len(line2) == 0):
             return -1
         elif (line1 != line2):
+            if (ignore != None):
+                if (line1.startswith(ignore) or line2.startswith(ignore)):
+                    continue
             line1 = line1.replace(" ", "")
             line2 = line2.replace(" ", "")
             if (line1 != line2):
             line1 = line1.replace(" ", "")
             line2 = line2.replace(" ", "")
             if (line1 != line2):
@@ -81,7 +144,10 @@ def compareFiles(file1, file2):
                         continue
                 return -1
 
                         continue
                 return -1
 
-def expandVars(value):
+def expandKnownAGVars(value):
+    return expandVars(value, includeEnv=False)
+
+def expandVars(value, includeEnv=True):
     """Syntax: ${myvar,default="default value"}"""
     import activegrid.runtime as runtime
     sx = value.find("${")
     """Syntax: ${myvar,default="default value"}"""
     import activegrid.runtime as runtime
     sx = value.find("${")
@@ -97,16 +163,19 @@ def expandVars(value):
                     defaultValue = value[defsx+10:endx-1]
             if (defaultValue == None):
                 varname = value[sx+2:endx]
                     defaultValue = value[defsx+10:endx-1]
             if (defaultValue == None):
                 varname = value[sx+2:endx]
-            if (varname == "AG_SYSTEM"):
+            if (varname == AG_SYSTEM_VAR):
                 varval = runtime.appInfo.getSystemDir()
                 varval = runtime.appInfo.getSystemDir()
-            elif (varname == "AG_SYSTEM_STATIC"):
+            elif (varname == AG_SYSTEM_STATIC_VAR):
                 varval = runtime.appInfo.getSystemStaticDir()
                 varval = runtime.appInfo.getSystemStaticDir()
-            elif (varname == "AG_APP"):
+            elif (varname == AG_APP_VAR):
                 varval = runtime.appInfo.getAppDir()
                 varval = runtime.appInfo.getAppDir()
-            elif (varname == "AG_APP_STATIC"):
+            elif (varname == AG_APP_STATIC_VAR):
                 varval = runtime.appInfo.getAppStaticDir()
             else:
                 varval = runtime.appInfo.getAppStaticDir()
             else:
-                varval = os.getenv(varname)
+                if (includeEnv):
+                    varval = os.getenv(varname)
+                else:
+                    varval = None
             if ((varval == None) and (defaultValue != None)):
                 varval = defaultValue
             if (varval == None):
             if ((varval == None) and (defaultValue != None)):
                 varval = defaultValue
             if (varval == None):
@@ -148,22 +217,21 @@ def convertSourcePath(path, to, otherdir=None):
         return otherdir + path[ix+7:]
 
 
         return otherdir + path[ix+7:]
 
 
-def visit(directory, files, extension):
+def visit(directory, files, extension, maxLevel=None, level=1):
     testdirs = os.listdir(directory)
     for thing in testdirs:
         fullpath = os.path.join(directory, thing)
     testdirs = os.listdir(directory)
     for thing in testdirs:
         fullpath = os.path.join(directory, thing)
-        if (os.path.isdir(fullpath)):
-            visit(fullpath, files, extension)
+        if (os.path.isdir(fullpath) and (maxLevel == None or level < maxLevel)):
+            visit(fullpath, files, extension, maxLevel, level+1)
         elif thing.endswith(extension):
             fullname = os.path.normpath(os.path.join(directory, thing))
             if not fullname in files:
                 files.append(fullname)
  
         elif thing.endswith(extension):
             fullname = os.path.normpath(os.path.join(directory, thing))
             if not fullname in files:
                 files.append(fullname)
  
-def listFilesByExtensionInPath(path=[], extension='.lyt'):
-    #Collect input and output arguments into one bunch
+def listFilesByExtensionInPath(path=[], extension='.lyt', maxLevel=None):
     retval = []
     for directory in path:
     retval = []
     for directory in path:
-        visit(directory, retval, extension)
+        visit(directory, retval, extension, maxLevel)
     return retval
 
 def getFileLastModificationTime(fileName):
     return retval
 
 def getFileLastModificationTime(fileName):
@@ -311,6 +379,21 @@ def remove(file):
         shutil.rmtree(file)
 endIfDef()
 
         shutil.rmtree(file)
 endIfDef()
 
+def getUserTempDir():
+    systemTempDir = utillang.getSystemTempDir()
+    userName = sysutils.getUserName()
+    userNameNoSpace = userName.replace('_','__').replace(' ','_')
+    userTempDir = systemTempDir + os.sep + "activegrid_" + userNameNoSpace
+    return userTempDir
+
+def createUserTempDir():
+    userTempDir = getUserTempDir()
+    if not os.path.exists(userTempDir):
+        os.makedirs(userTempDir)
+        os.chmod(userTempDir, 0700)
+
+createUserTempDir()
+
 ifDefPy()
 import warnings
 warnings.filterwarnings("ignore", message="tmpnam is a potential security risk to your program")
 ifDefPy()
 import warnings
 warnings.filterwarnings("ignore", message="tmpnam is a potential security risk to your program")
index 79cb02919e323217b1039171b650b2196b812a80..96e96df38c5e434ad22eef36c624aa22c8f4886c 100644 (file)
@@ -16,7 +16,8 @@ import sys
 import os
 import __builtin__
 import types
 import os
 import __builtin__
 import types
-import xml.sax.saxutils as saxutils
+import activegrid.util.utillang as utillang
+import activegrid.util.datetimeparser as datetimeparser
 from types import *
 from activegrid.util.lang import *
 
 from types import *
 from activegrid.util.lang import *
 
@@ -65,6 +66,18 @@ def setStaticAttr(obj, attr, value):
         classDesc = obj.__class__
     setattr(classDesc, attr, value)
 
         classDesc = obj.__class__
     setattr(classDesc, attr, value)
 
+def hasAttrFast(obj, name):
+    if hasRawAttr(obj, name):
+        return True
+    if hasattr(obj, '_complexType'):
+        complexType=obj._complexType
+        element=complexType.findElement(name)
+        if element:
+            return True
+    if hasattr(obj, name):
+        return True
+    return False
+
 def moduleForName(moduleName):
     module = None
     pathList = moduleName.split('.')
 def moduleForName(moduleName):
     module = None
     pathList = moduleName.split('.')
@@ -114,15 +127,22 @@ def newInstance(className, objargs=None):
         if ((len(objargs) < 1) or (objargs[0].lower() == "false") or (not objargs[0])):
             return False
         return True
         if ((len(objargs) < 1) or (objargs[0].lower() == "false") or (not objargs[0])):
             return False
         return True
-    if className == "str" or className == "unicode": # don"t strip: blanks are significant
+    if className == "str" or className == "unicode": # don't strip: blanks are significant
         if len(objargs) > 0:
             try:
         if len(objargs) > 0:
             try:
-                return saxutils.unescape(objargs[0]).encode()
+                return utillang.unescape(objargs[0]).encode()
             except:
                 return "?"
         else:
             return ""
             except:
                 return "?"
         else:
             return ""
-
+            
+    if className == "date":
+        return datetimeparser.parse(objargs[0], asdate=True)
+    if className == "datetime":
+        return datetimeparser.parse(objargs[0])
+    if className == "time":
+        return datetimeparser.parse(objargs[0], astime=True)
+        
     classtype = classForName(className)
     if (classtype == None):
         raise Exception("Could not find class %s" % className)
     classtype = classForName(className)
     if (classtype == None):
         raise Exception("Could not find class %s" % className)
@@ -135,23 +155,35 @@ def newInstance(className, objargs=None):
 def getClassProperty(classType, propertyName):
     return getattr(classType, propertyName)
     
 def getClassProperty(classType, propertyName):
     return getattr(classType, propertyName)
     
-def toDiffableRepr(value, exclude=None):
+def toDiffableRepr(value, maxLevel=None):
     if (value == None):
         return "None"
     if (value == None):
         return "None"
+    if (maxLevel == None):
+        maxLevel = 8
+    maxLevel -= 1
+    if (maxLevel < 0):
+        return typeToString(value, PRINT_OBJ_DIFFABLE)
+##    if ((exclude != None) and not isinstance(value, (basestring, int))):
+##        for v in exclude:
+##            if (v is value):
+##                return "<recursive reference>"
+##        exclude.append(value)
 ##    elif (isinstance(value, ObjectType) and hasattr(value, "__dict__")):
 ##        if (exclude == None):
 ##            exclude = []
 ##        s = "%s(%s)" % (type(value), toDiffableString(value.__dict__, exclude))
 ##    elif (isinstance(value, ObjectType) and hasattr(value, "__dict__")):
 ##        if (exclude == None):
 ##            exclude = []
 ##        s = "%s(%s)" % (type(value), toDiffableString(value.__dict__, exclude))
-    elif (not isinstance(value, (BooleanType, ClassType, ComplexType, DictType, DictionaryType, 
+    if (not isinstance(value, (BooleanType, ClassType, ComplexType, DictType, DictionaryType, 
                                FloatType, IntType, ListType, LongType, StringType, TupleType, 
                                UnicodeType, BufferType, BuiltinFunctionType, BuiltinMethodType,
                                CodeType, FrameType, FunctionType, GeneratorType, InstanceType,
                                LambdaType, MethodType, ModuleType, SliceType, TracebackType,
                                TypeType, XRangeType))):
                                FloatType, IntType, ListType, LongType, StringType, TupleType, 
                                UnicodeType, BufferType, BuiltinFunctionType, BuiltinMethodType,
                                CodeType, FrameType, FunctionType, GeneratorType, InstanceType,
                                LambdaType, MethodType, ModuleType, SliceType, TracebackType,
                                TypeType, XRangeType))):
-        if (hasattr(value, "__str__")):
+        if (hasattr(value, "_toDiffableString")):
+            s = value._toDiffableString(maxLevel)
+        elif (hasattr(value, "__str__")):
             s = str(value)
         elif (hasattr(value, "__dict__")):
             s = str(value)
         elif (hasattr(value, "__dict__")):
-            s = "%s(%s)" % (type(value), toDiffableString(value.__dict__, exclude))
+            s = "%s(%s)" % (type(value), toDiffableString(value.__dict__, maxLevel))
         else:
             s = str(type(value))
         ix2 = s.find(" object at 0x")
         else:
             s = str(type(value))
         ix2 = s.find(" object at 0x")
@@ -173,33 +205,31 @@ def toDiffableRepr(value, exclude=None):
                 else:
                     items.append("'%s'" % v)
             else:
                 else:
                     items.append("'%s'" % v)
             else:
-                items.append(toDiffableString(v, exclude))
+                items.append(toDiffableString(v, maxLevel))
         s = "[" + ", ".join(items) + "]"
     elif (isinstance(value, dict)):
         s = "[" + ", ".join(items) + "]"
     elif (isinstance(value, dict)):
-        if (exclude == None):
-            exclude = []
         items = []
         for key, val in value.iteritems():
             if (isinstance(val, UnicodeType)):
         items = []
         for key, val in value.iteritems():
             if (isinstance(val, UnicodeType)):
-                items.append("'%s': u'%s'" % (key, toDiffableString(val, exclude)))
+                items.append("'%s': u'%s'" % (key, toDiffableString(val, maxLevel)))
             elif (isinstance(val, basestring)):
             elif (isinstance(val, basestring)):
-                items.append("'%s': '%s'" % (key, toDiffableString(val, exclude)))
+                items.append("'%s': '%s'" % (key, toDiffableString(val, maxLevel)))
             else:
             else:
-                items.append("'%s': %s" % (key, toDiffableString(val, exclude)))
+                items.append("'%s': %s" % (key, toDiffableString(val, maxLevel)))
         s = "{" + ", ".join(items) + "}"
     else:
         s = str(value)
     return s
     
         s = "{" + ", ".join(items) + "}"
     else:
         s = str(value)
     return s
     
-def toDiffableString(value, exclude=None):
-    if (value == None):
-        return "None"
-    if ((exclude != None) and not isinstance(value, (basestring, int))):
-        for v in exclude:
-            if (v is value):
-                return "<recursive reference>"
-        exclude.append(value)
-    s = toDiffableRepr(value)
+def toDiffableString(value, maxLevel=None):
+##    if (value == None):
+##        return "None"
+##    if ((exclude != None) and not isinstance(value, (basestring, int))):
+##        for v in exclude:
+##            if (v is value):
+##                return "<recursive reference>"
+##        exclude.append(value)
+    s = toDiffableRepr(value, maxLevel)
     ds = ""
     i = s.find(" at 0x") 
     start = 0
     ds = ""
     i = s.find(" at 0x") 
     start = 0
diff --git a/wxPython/samples/ide/activegrid/util/parser.py b/wxPython/samples/ide/activegrid/util/parser.py
new file mode 100644 (file)
index 0000000..364099b
--- /dev/null
@@ -0,0 +1,380 @@
+#----------------------------------------------------------------------------
+# Name:         parser.py
+# Purpose:      parsing utilities
+#
+# Author:       Jeff Norton
+#
+# Created:      8/9/05
+# CVS-ID:       $Id$
+# Copyright:    (c) 2004-2005 ActiveGrid, Inc.
+# License:      wxWindows License
+#----------------------------------------------------------------------------
+
+import re
+from activegrid.util.lang import *
+ifDefPy()
+import string
+import array
+endIfDef()
+
+XPATH_ROOT_VAR = '__rootObj__'
+GETOBJECTPARTNAMES  =   ["primaryRef", "ref", "orderings", "limit"]
+
+class Tokenizer(object):
+    
+    TOKEN_IDENT = 1
+    TOKEN_STRING = 2
+    TOKEN_OP = 3
+    TOKEN_WS = 4
+##    TOKEN_PLACEHOLDER = 5
+    
+    def __init__(self, text, identStart=None, tokenSep=None, ignoreWhitespace=True):
+        """
+Turn a string into individual tokens.  Three types of tokens are recognized:
+    TOKEN_IDENT:   identifiers (those that start with the identStart pattern)
+    TOKEN_STRING:  quoted string
+    TOKEN_OP:      everything else
+Tokens are separated by white space or the tokenSep pattern.  
+Constructor parameters:
+    text:  The string to tokenize
+    identStart:  A regular expression describing characters which start an identifier
+                 The default expression accepts letters, "_", and "/".
+    tokenSep:    A regular expression describing the characters which end a token 
+                 (in addition to whitespace).  The default expression accepts
+                 anything except alpha-numerics, "_", "/", and ":".
+Usage:
+    Invoke getNextToken (or next) to get the next token.  The instance variables
+    token, and tokenVal will be populated with the current token type (TOKEN_IDENT,
+    TOKEN_STRING, or TOEKN_OP) and value respectively.  nextToken and nextTokenVal
+    will also be available for lookahead.   The next method is similar to
+    getNextToken but also returns the token value.  A value of None signals end
+    of stream.                                           
+        """
+        self.ignoreWhitespace=ignoreWhitespace
+        ifDefPy()
+        if (isinstance(text, array.array)):
+            text = text.tostring()
+        endIfDef()
+        self.text = asString(text)
+        self.textIndex = 0
+        self.textLen = len(self.text)
+        self.token = None
+        self.tokenVal = None
+        self.nextToken = None
+        self.nextTokenVal = None
+        if (identStart == None):
+            identStart = "[a-zA-Z_/]"
+        if (tokenSep == None):
+            tokenSep = "[^a-zA-Z0-9_/:]"
+        self.identStart = re.compile(identStart)
+        self.tokenSep = re.compile(tokenSep)
+        self.getNextToken() # Prime the pump
+        
+    def isEscaped(text, index):
+        if ((index > 0) and (text[index-1] == '\\') and ((index < 2) or (text[index-2] != '\\'))):
+            return True
+        return False
+    isEscaped = staticmethod(isEscaped)
+
+    def findClosingQuote(text, index, char):
+        index = index + 1
+        while True:
+            endIndex = text.find(char, index)
+            if (endIndex < 1):
+                return -1
+            if (Tokenizer.isEscaped(text, endIndex)):
+                index = endIndex+1
+            else:
+                break
+        return endIndex + 1
+    findClosingQuote = staticmethod(findClosingQuote)
+
+    def _findClosing(self, char):
+        if (self.textIndex >= self.textLen):
+            raise Exception("The text \"%s\" has an unmatched string starting at %d" % (self.text, self.textIndex))
+        index = Tokenizer.findClosingQuote(self.text, self.textIndex, char)
+        if (index < 0):
+            raise Exception("The text \"%s\" has an unmatched string starting at %d" % (self.text, self.textIndex-1))
+        return index
+        
+    def next(self):
+        self.getNextToken()
+        if (self.token == None):
+            raise StopIteration()
+        return self.tokenVal
+
+    def getNextToken(self):
+        self.token = self.nextToken
+        self.tokenVal = self.nextTokenVal
+        while (self.textIndex < self.textLen):
+            c = self.text[self.textIndex]
+            if (c not in string.whitespace):
+                if (c == '"' or c == "'" or c == '`'):
+                    endIndex = self._findClosing(c)
+                    self.nextToken = self.TOKEN_STRING
+                    self.nextTokenVal = self.text[self.textIndex:endIndex]
+                    self.textIndex = endIndex
+                    return
+                elif (self.identStart.search(c)):
+                    endMatch = self.tokenSep.search(self.text, self.textIndex+1)
+                    if (endMatch):
+                        endIndex = endMatch.start()
+                    else:
+                        endIndex = self.textLen
+                    self.nextToken = self.TOKEN_IDENT
+                    self.nextTokenVal = self.text[self.textIndex:endIndex]
+                    self.textIndex = endIndex
+                    return
+                else:
+                    self.nextToken = self.TOKEN_OP
+                    endIndex = self.textIndex + 1
+                    if (c == '<' or c == '>' or c == '!' or c == '='):
+                        if ((endIndex < self.textLen) and (self.text[endIndex] == '=')):
+                            endIndex += 1
+                    elif ((c == '%') and (endIndex < self.textLen)):
+                        c = self.text[endIndex]
+                        if (c in ['d', 'i', 'o', 'u', 'x', 'X', 'e', 'E', 'f', 'F', 'g', 'G', 'c', 'r', 's', '%']):
+                            endIndex += 1
+##                            self.nextToken = self.TOKEN_PLACEHOLDER # Should really be this but no one can handle it yet
+                    self.nextTokenVal = self.text[self.textIndex:endIndex]
+                    self.textIndex = endIndex
+                    return
+            elif not self.ignoreWhitespace:
+                self.nextToken=self.TOKEN_WS
+                self.nextTokenVal=""
+                while c in string.whitespace:
+                    self.nextTokenVal+=c
+                    self.textIndex+=1
+                    if self.textIndex==len(self.text):
+                        break
+                    c=self.text[self.textIndex]
+                return
+            self.textIndex += 1
+        self.nextToken = None
+        self.nextTokenVal = None
+
+def isXPathNonVar(var):
+    """Returns true iff var is a string ("foo" or 'foo') or a number."""
+    if (var.startswith("'") and var.endswith("'")) or \
+            (var.startswith('"') and var.endswith('"')):
+        return True
+
+    # list from XPathToCode, below
+    if var.lower() in ["count", "empty", "true", "false", "null", "and", "or", \
+            "like", "not"]:
+        return True
+
+    try:
+        t=int(var)
+        return True
+    except TypeError, e:
+        pass
+    except ValueError, e:
+        pass
+
+    return False
+
+def xpathToCode(xpaths, convertBracket=True):
+    if ((xpaths == None) or (len(xpaths) < 1)):
+        return "True"
+    if (not isinstance(xpaths, (list, tuple))):
+        xpaths = [xpaths]
+    result = []
+    for xpath in xpaths:
+        t = Tokenizer(xpath, "[a-zA-Z0-9_/:\.]", "[^a-zA-Z0-9_/:\.]", ignoreWhitespace=False)
+        expr = []
+        lastToken=None
+        while t.nextToken != None:
+            t.getNextToken()
+            if (t.token == Tokenizer.TOKEN_WS):
+                expr.append(" ")
+            elif (t.token == Tokenizer.TOKEN_OP):
+                if (t.tokenVal == "="):
+                    expr.append("==")
+                elif (t.tokenVal == "[" and convertBracket):
+                    expr.append("(")
+                elif (t.tokenVal == "]" and convertBracket):
+                    expr.append(")")
+                else:
+                    expr.append(t.tokenVal)
+            elif (t.token == Tokenizer.TOKEN_IDENT):
+                if (t.tokenVal == "and"):
+                    expr.append(" and ")
+                elif (t.tokenVal == "or"):
+                    expr.append(" or ")
+                elif (t.tokenVal == "not"):
+                    expr.append(" not ")
+                elif (t.tokenVal == "like"):
+                    # REVIEW stoens@activegrid.com 02-Nov-05 --
+                    # This is very limited support for like:
+                    # typically like queries look like this: "foo like 'blah%'".
+                    # So translate this into "foo.startswith(blah)".
+                    # We should use a regular expression to support '%'s in
+                    # arbitrary places in the string. After 1.1.
+                    if t.nextToken and t.nextTokenVal.endswith("%'"):
+                        t.getNextToken() # throw away the "like" token
+                        last = len(expr) - 1
+                        expr[last] = "%s.startswith(%s')"\
+                            % (expr[last], t.tokenVal[:-2])
+                    else:
+                        # old behavior
+                        expr.append(t.tokenVal)
+                    
+                elif (t.tokenVal == "count"):
+                    expr.append("len")
+                elif (t.tokenVal == 'empty'):
+                    expr.append('ctx.isEmptyPath')
+                elif (t.tokenVal == 'true'):
+                    expr.append(_parseConstantFunction(t, 'True'))
+                elif (t.tokenVal == 'false'):
+                    expr.append(_parseConstantFunction(t, 'False'))
+                elif (t.tokenVal == 'null'):
+                    expr.append(_parseConstantFunction(t, 'None'))
+                elif (-1!=t.tokenVal.find(':')):
+                    serviceDef, args=_parseServiceFunction(t)
+
+                    # XXX handle serviceDef, args being None
+
+                    for i in range(len(args)):
+                        args[i]=xpathToCode(args[i], False)
+                    jargs="[%s]" % (",".join(args))
+
+                    # XXX should be processmodel.DATASERVICE_PROCESS_NAME, not "dataservice"
+                    if serviceDef[0]=='dataservice':
+                        expr.append("runtimesupport.invokeDataServiceWrapper(%s, %s, ctx, locals())" % \
+                                (serviceDef, jargs))
+                    else:
+                        expr.append("runtimesupport.invokeServiceWrapper(%s, %s, ctx)" % \
+                                (serviceDef, jargs))
+                else:
+                    if (lastToken==')' or lastToken==']'):
+                        wasFunc=True
+                    else:
+                        wasFunc=False
+                    if (t.tokenVal.startswith('/')) and not wasFunc:
+                        expr.append(XPATH_ROOT_VAR)
+                    expr.append(t.tokenVal.replace('/','.'))
+                lastToken=t.tokenVal
+            else: 
+                expr.append(t.tokenVal)
+                
+
+        if (len(expr) == 2 and expr[0]==" "):
+            expr = "".join(expr)
+            result.append(expr)
+        elif (len(expr) > 1):
+            expr = "".join(expr)
+            result.append("(%s)" % expr)
+        elif (len(expr) > 0):
+            result.append(expr[0])
+        
+    return " and ".join(result)
+
+def _parseArgs(t):
+    args=[]
+    argcon=""
+
+    if t.tokenVal!='(':
+        return []
+    if t.nextTokenVal==')':
+        t.getNextToken()
+        return []
+
+    depth=1
+
+    while(depth!=0):
+        if not t.nextToken:
+            raise Exception("parameters list with no closing ) after token: %s" % t.tokenVal)
+        t.getNextToken()
+
+        if t.tokenVal=='(':
+            depth+=1
+        if t.tokenVal==')':
+            depth-=1
+
+        if depth==0 or (depth==1 and t.tokenVal==','):
+            args.append(argcon)
+            argcon=""
+        else:
+            argcon+=t.tokenVal
+    return args
+
+def _parseServiceFunction(t):
+    """Parses what appears to be a service function call into serviceDefs and args lists.
+
+    Returns None, None if the serviceFunction appears to be invalid.
+    """
+    if t.nextTokenVal!='(':
+        return t.tokenVal, None
+
+    serviceDef=t.tokenVal.split(':')
+    t.getNextToken()
+    args=_parseArgs(t)
+
+    return serviceDef, args
+    
+def _parseConstantFunction(t, outputValue):
+    firstVal = t.tokenVal
+    if t.nextTokenVal != '(':
+        return firstVal
+    t.getNextToken()
+    if t.nextTokenVal != ')':
+        return "%s%s" % (firstVal, '(')
+    t.getNextToken()
+    return outputValue
+
+def parseDSPredicate(ctx, str, vars, valueList=None):
+    from activegrid.util.utillang import evalCode
+    from activegrid.util.utillang import ObjAsDict
+
+    if valueList == None:
+        valueList = []
+    indexVar=0
+    oldIndexVar=0
+    sourceStr=str
+    inlinedPredicate=[]
+    qualifications=[]
+    while True:
+        oldIndexVar = indexVar
+        dollarCurlForm = False
+        quoted = False
+        indexVar = sourceStr.find("bpws:getVariableData", indexVar)
+        if indexVar == -1:
+            indexVar = sourceStr.find("${", oldIndexVar)
+            if indexVar == -1:
+                break
+            dollarCurlForm = True
+        if indexVar > 0 and sourceStr[indexVar-1] in ('"',"'"):
+            quoted = True
+        if not dollarCurlForm:
+            openParen = sourceStr.find("(", indexVar)
+            if openParen == -1:
+                break
+            closeParen = sourceStr.find(")", openParen)
+            if closeParen == -1:
+                break
+        else:
+            openParen = indexVar+1
+            closeParen = sourceStr.find("}", openParen)
+            if closeParen == -1:
+                break
+        varRef = sourceStr[openParen+1: closeParen]
+        if varRef.startswith('"') or varRef.startswith("'"):
+            varRef = varRef[1:]
+        if varRef.endswith('"') or varRef.endswith("'"):
+            varRef = varRef[:-1]
+        if isinstance(vars, dict) or isinstance(vars, ObjAsDict):
+            varRefCode = xpathToCode(varRef)
+            value = evalCode(varRefCode, vars)
+        else:
+            value = ctx.evalPath(vars, varRef)
+        inlinedPredicate.append(sourceStr[oldIndexVar:indexVar])
+        if quoted:
+            inlinedPredicate.append("%s" % value)
+        else:
+            inlinedPredicate.append('%s')
+            valueList.append(value)
+        indexVar = closeParen+1
+    inlinedPredicate.append(sourceStr[oldIndexVar:])
+    qualifications.append(''.join(inlinedPredicate))
+    return qualifications, valueList
index b79bd2a474d6f6e6c04939148da1f737bd24f360..f70d03ee548f34f6c3f26b3dd3c919d8a8a0eacb 100644 (file)
@@ -21,3 +21,91 @@ def caseInsensitiveCompare(s1, s2):
         return -1
     else:
         return 1
         return -1
     else:
         return 1
+
+def multiSplit(stringList, tokenList=[" "]):
+    """Splits strings in stringList by tokens, returns list of string."""
+    if not stringList: return []
+    if isinstance(tokenList, basestring):
+        tokenList = [tokenList]
+    if isinstance(stringList, basestring):
+        stringList = [stringList]
+    rtnList = stringList
+    for token in tokenList:
+        rtnList = rtnList[:]
+        for string in rtnList:
+            if string.find(token) > -1:
+                rtnList.remove(string)
+                names = string.split(token)
+                for name in names:
+                    name = name.strip()
+                    if name:
+                        rtnList.append(name)
+    return rtnList
+
+QUOTES = ("\"", "'")
+
+def _findArgStart(argStr):
+    i = -1
+    for c in argStr:
+        i += 1
+        if (c == " "):
+            continue
+        elif (c == ","):
+            continue
+        return i
+    return None
+
+def _findArgEnd(argStr):
+    quotedArg = True
+    argEndChar = argStr[0]
+    if (not argEndChar in QUOTES):
+        argEndChar = ","
+        quotedArg = False
+    i = -1
+    firstChar = True
+    for c in argStr:
+        i+= 1
+        if (firstChar):
+            firstChar = False
+            if (quotedArg):
+                continue
+        if (c == argEndChar):
+            if (quotedArg):
+                return min(i+1, len(argStr))
+            else:
+                return i
+    return i
+
+def parseArgs(argStr, stripQuotes=False):
+    """
+    Given a str representation of method arguments, returns list arguments (as
+    strings).
+    
+    Input: "('[a,b]', 'c', 1)" -> Output: ["'[a,b]'", "'c'", "1"].
+
+    If stripQuotes, removes quotes from quoted arg.
+    """
+    if (argStr.startswith("(")):
+        argStr = argStr[1:]
+        if (argStr.endswith(")")):
+            argStr = argStr[:-1]
+        else:
+            raise AssertionError("Expected argStr to end with ')'")
+
+    rtn = []
+    argsStr = argStr.strip()
+    while (True):
+        startIndex = _findArgStart(argStr)
+        if (startIndex == None):
+            break
+        argStr = argStr[startIndex:]
+        endIndex = _findArgEnd(argStr)
+        if (endIndex == len(argStr) - 1):
+            rtn.append(argStr.strip())
+            break        
+        t = argStr[:endIndex].strip()
+        if (stripQuotes and t[0] in QUOTES and t[-1] in QUOTES):
+            t = t[1:-1]
+        rtn.append(t)
+        argStr = argStr[endIndex:]
+    return rtn
index eb8bbf08f10fdff626eb5841248772c43e3564c3..d7e68bd6857bbbe790e728dc8f4472a8772ce57e 100644 (file)
@@ -12,6 +12,7 @@
 
 import sys
 import os
 
 import sys
 import os
+import time
 
 # this will be set to true in IDE.py when we are running release builds.
 isRelease = False
 
 # this will be set to true in IDE.py when we are running release builds.
 isRelease = False
@@ -26,6 +27,12 @@ isRelease = False
 
 MAINMODULE_DIR = "AG_MAINMODULE_DIR"
 IS_RELEASE = "AG_IS_RELEASE"
 
 MAINMODULE_DIR = "AG_MAINMODULE_DIR"
 IS_RELEASE = "AG_IS_RELEASE"
+IS_COMMERCIAL = "AG_IS_COMMERCIAL"
+AG_SYSTEM_START_TIME_ENV_NAME = "AG_SYSTEM_START_TIME"
+
+def isCommercial():
+    
+    return os.path.exists(os.path.join(mainModuleDir,"commercial.txt")) or 'true' == (str(os.getenv(IS_COMMERCIAL)).lower())
  
 def isRelease():
     return 'true' == (str(os.getenv(IS_RELEASE)).lower())
  
 def isRelease():
     return 'true' == (str(os.getenv(IS_RELEASE)).lower())
@@ -39,7 +46,16 @@ def setRelease(value):
 def isWindows():
     return os.name == 'nt'
 
 def isWindows():
     return os.name == 'nt'
 
+__isServer = False
 
 
+def setServerMode(isServer):
+    global __isServer
+    __isServer = isServer
+    
+def isServer():
+    global __isServer
+    return __isServer
+    
 def _generateMainModuleDir():
     mainModuleDir = os.getenv(MAINMODULE_DIR)
     if mainModuleDir:  # if environment variable set, return it
 def _generateMainModuleDir():
     mainModuleDir = os.getenv(MAINMODULE_DIR)
     if mainModuleDir:  # if environment variable set, return it
@@ -85,3 +101,16 @@ def getCommandNameForExecPath(execPath):
         return '"%s"' % execPath
     return execPath
 
         return '"%s"' % execPath
     return execPath
 
+def getUserName():
+    if isWindows():
+        return os.getenv('USERNAME')
+    else:
+        # 06-Feb-06 stoens@activegrid.com --
+        # this blows up the linux cc runs with "Inappropriate ioctl for device"
+        #return os.getlogin()
+        return os.getenv('USER')        
+
+def getCurrentTimeAsFloat():
+    return time.time()
+
+systemStartTime = getCurrentTimeAsFloat()
diff --git a/wxPython/samples/ide/activegrid/util/utillang.py b/wxPython/samples/ide/activegrid/util/utillang.py
new file mode 100644 (file)
index 0000000..fad7e20
--- /dev/null
@@ -0,0 +1,146 @@
+#----------------------------------------------------------------------------
+# Name:         utillang.py
+# Purpose:      Provide language specific utilities 
+#
+# Author:       Joel Hare
+#
+# Created:      8/23/05
+# CVS-ID:       $Id$
+# Copyright:    (c) 2004-2005 ActiveGrid, Inc.
+# License:      wxWindows License
+#----------------------------------------------------------------------------
+
+import os
+import sys
+import UserDict
+import tempfile
+import xml.sax.saxutils as saxutils
+
+import activegrid.util.parser as parser
+
+PY2WEB_codepages = {
+     'cp1251' : 'CP-1251',
+     'koi8_r' : 'KOI8-R',
+}
+
+def evalXPath(xpath, data, specialEntries=None):
+    codeStr = parser.xpathToCode(xpath)
+    return evalCode(codeStr, data, specialEntries)
+
+def evalCode(codeStr, data, specialEntries=None):
+    if isinstance(data, ObjAsDict):
+        namespace = data
+    elif isinstance(data, dict):
+        namespace = dict(data)
+    else:
+        namespace = ObjAsDict(data)
+    if specialEntries:
+        for key, value in specialEntries.items():
+            namespace.addSpecialEntry(key, value)
+    return eval(codeStr, {}, namespace)
+
+def deriveCharset():
+    charset = None
+    encodingString = sys.getdefaultencoding()
+    if encodingString != 'ascii':
+        charset = PY2WEB_codepages.get(encodingString.lower())
+        if charset == None:
+            charset = encodingString
+    return charset
+
+def toUTF8(value):
+    """
+    Converts all unicode and non-string values to utf-8.
+    This assumes string instances are already encoded in utf-8.
+    Note that us-ascii is a subset of utf-8.
+    """
+    if isinstance(value, unicode):
+        return value.encode('utf-8')
+    return str(value)
+
+def toUnicode(value):
+    """
+    Converts all strings non-string values to unicode.
+    This assumes string instances are encoded in utf-8.
+    Note that us-ascii is a subset of utf-8.
+    """
+    if not isinstance(value, unicode):
+        if not isinstance(value, str):
+            return unicode(value)
+        return unicode(value, 'utf-8')
+    return value
+    
+
+def getSystemTempDir():
+    return tempfile.gettempdir()
+
+def getEnvVar(name, defaultVal=None):
+    if os.environ.has_key(name):
+        return os.environ[name]
+    return defaultVal
+
+class ObjAsDict(UserDict.DictMixin):
+    """
+    Passing this to eval as the local variables dictionary allows the
+    evaluated code to access properties in the wrapped object
+    """
+    def __init__(self, obj):
+        self.obj = obj
+        self.specialEntries = {}
+
+    def __getitem__(self, key):
+        try:
+            return getattr(self.obj, key)
+        except AttributeError, e:
+            if self.specialEntries.has_key(key):
+                return self.specialEntries[key]
+            raise KeyError(e.args)
+    def __setitem__(self, key, item): setattr(self.obj, key, item)
+    def __delitem__(self, key): delattr(self.obj, key)
+    def keys(self):
+        ret=[]
+        for i in list(dir(self.obj)+self.specialEntries.keys()):
+            if i=="__doc__" or i=="__module__":
+                pass
+            elif i not in ret:
+                ret.append(i)
+        return ret
+
+    def addSpecialEntry(self, key, value):
+        self.specialEntries[key] = value
+
+global saxXMLescapeDoubleQuote
+saxXMLescapeDoubleQuote = {'"':'&quot;'}
+
+global saxXMLescapesAllQuotes
+# IE doesn't support &apos; but it doesn't seem like we should need this escaped at all so I took it out.
+saxXMLescapesAllQuotes = {'"':'&quot;', "'":"&#039;"}
+
+global saxXMLunescapes
+saxXMLunescapes = {'&quot;':'"', "&#039;":"'"}
+
+def escape(data, extraEscapes=None):
+    """Escape ', ", &, <, and > in a string of data.
+
+    Basically, everything that saxutils.escape does (and this calls that, at
+    least for now), but with " and ' added as well.
+
+    TODO: make this faster; saxutils.escape() is really slow
+    """
+    
+    global saxXMLescapeDoubleQuote
+    if (extraEscapes == None):
+        extraEscapes = saxXMLescapeDoubleQuote
+    return saxutils.escape(data, extraEscapes)
+
+def unescape(data):
+    """Unescape ', ", &, <, and > in a string of data.
+
+    Basically, everything that saxutils.unescape does (and this calls that, at
+    least for now), but with " and ' added as well.
+
+    TODO: make this faster; saxutils.unescape() is really slow
+    """
+    
+    global saxXMLunescapes
+    return saxutils.unescape(data, saxXMLunescapes)
index 6a74d2c486bd2805438516918170f6f2d33f6900..ee758350d79e0c1a885ac30ebfe1767c1a17e562 100644 (file)
@@ -2,7 +2,7 @@
 # Name:         xmlmarshaller.py
 # Purpose:
 #
 # Name:         xmlmarshaller.py
 # Purpose:
 #
-# Authors:       John Spurling, Joel Hare, Alan Mullendore
+# Authors:      John Spurling, Joel Hare, Jeff Norton, Alan Mullendore
 #
 # Created:      7/28/04
 # CVS-ID:       $Id$
 #
 # Created:      7/28/04
 # CVS-ID:       $Id$
@@ -16,14 +16,18 @@ import logging
 ifDefPy()
 import xml.sax
 import xml.sax.handler
 ifDefPy()
 import xml.sax
 import xml.sax.handler
+import xml.sax.saxutils
+import datetime
 endIfDef()
 endIfDef()
-import xml.sax.saxutils as saxutils
+import activegrid.util.utillang as utillang
 import activegrid.util.objutils as objutils
 import activegrid.util.objutils as objutils
+import activegrid.util.sysutils as sysutils
 import activegrid.util.aglogging as aglogging
 
 MODULE_PATH = "__main__"
 
 ## ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed
 import activegrid.util.aglogging as aglogging
 
 MODULE_PATH = "__main__"
 
 ## ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed
+##unboundedVal = 2147483647 # value used for maxOccurs == "unbounded"
 
 """
 Special attributes that we recognize:
 
 """
 Special attributes that we recognize:
@@ -109,7 +113,6 @@ __xmlcdatacontent__ = "messyContent"
 
 global xmlMarshallerLogger
 xmlMarshallerLogger = logging.getLogger("activegrid.util.xmlmarshaller.marshal")
 
 global xmlMarshallerLogger
 xmlMarshallerLogger = logging.getLogger("activegrid.util.xmlmarshaller.marshal")
-xmlMarshallerLogger.setLevel(aglogging.LEVEL_WARN)
 # INFO  : low-level info
 # DEBUG : debugging info
 
 # INFO  : low-level info
 # DEBUG : debugging info
 
@@ -184,6 +187,7 @@ DICT_ITEM_VALUE_NAME = "value"
 ################################################################################
 
 def setattrignorecase(object, name, value):
 ################################################################################
 
 def setattrignorecase(object, name, value):
+##    print "[setattrignorecase] name = %s, value = %s" % (name, value)
     if (name not in object.__dict__):
         namelow = name.lower()
         for attr in object.__dict__:
     if (name not in object.__dict__):
         namelow = name.lower()
         for attr in object.__dict__:
@@ -193,27 +197,95 @@ def setattrignorecase(object, name, value):
     object.__dict__[name] = value
 
 def getComplexType(obj):
     object.__dict__[name] = value
 
 def getComplexType(obj):
+    if (hasattr(obj, "_instancexsdcomplextype")):
+        return obj._instancexsdcomplextype
     if (hasattr(obj, "__xsdcomplextype__")):
         return obj.__xsdcomplextype__
     return None
 
     if (hasattr(obj, "__xsdcomplextype__")):
         return obj.__xsdcomplextype__
     return None
 
-def _objectfactory(objname, objargs=None, objclass=None):
-    "dynamically create an object based on the objname and return it."
-##    print "[objectfactory] objname [%s]" % (objname)
+def _objectfactory(objtype, objargs=None, objclass=None):
+    "dynamically create an object based on the objtype and return it."
     if not isinstance(objargs, list):
         objargs = [objargs]
     if (objclass != None):
     if not isinstance(objargs, list):
         objargs = [objargs]
     if (objclass != None):
+        obj = None
         if (len(objargs) > 0):
             if (hasattr(objclass, "__xmlcdatacontent__")):
                 obj = objclass()
                 contentAttr = obj.__xmlcdatacontent__
                 obj.__dict__[contentAttr] = str(objargs[0])
         if (len(objargs) > 0):
             if (hasattr(objclass, "__xmlcdatacontent__")):
                 obj = objclass()
                 contentAttr = obj.__xmlcdatacontent__
                 obj.__dict__[contentAttr] = str(objargs[0])
-                return obj
-            return objclass(*objargs)
+            else:
+                obj = objclass(*objargs)
         else:
         else:
-            return objclass()
-    return objutils.newInstance(objname, objargs)
+            obj = objclass()
+        if ((obj != None) and (hasattr(obj, 'postUnmarshal'))):
+            obj.postUnmarshal()
+        return obj
+    return objutils.newInstance(objtype, objargs)
+
+class GenericXMLObject(object):
+    def __init__(self, content=None):
+        if content != None:
+            self._content = content
+            self.__xmlcontent__ = '_content'
+
+    def __str__(self):
+        return "GenericXMLObject(%s)" % objutils.toDiffableString(self.__dict__)
 
 
+    def setXMLAttributes(self, xmlName, attrs=None, children=None, nsMap=None, defaultNS=None):
+        if xmlName != None:
+            i = xmlName.rfind(':')
+            if i < 0:
+                self.__xmlname__ = xmlName
+                if defaultNS != None:
+                    self.__xmldefaultnamespace__ = str(defaultNS)
+            else:
+                self.__xmlname__ = xmlName[i+1:]
+                prefix = xmlName[:i]
+                if nsMap.has_key(prefix):
+                    self.__xmldefaultnamespace__ = str(nsMap[prefix])
+        if attrs != None:
+            for attrname, attr in attrs.items():
+                attrname = str(attrname)
+                if attrname == XMLNS or attrname.startswith(XMLNS_PREFIX):
+                    pass
+                elif attrname == "objtype":
+                    pass
+                else:
+                    if not hasattr(self, '__xmlattributes__'):
+                        self.__xmlattributes__ = []
+                    i = attrname.rfind(':')
+                    if i >= 0:
+                        prefix = attrname[:i]
+                        attrname = attrname[i+1:]
+                        if not hasattr(self, '__xmlattrnamespaces__'):
+                            self.__xmlattrnamespaces__ = {}
+                        if self.__xmlattrnamespaces__.has_key(prefix):
+                            alist = self.__xmlattrnamespaces__[prefix]
+                        else:
+                            alist = []
+                        alist.append(attrname)
+                        self.__xmlattrnamespaces__[prefix] = alist
+                    self.__xmlattributes__.append(attrname)
+            if hasattr(self, '__xmlattributes__'):
+                self.__xmlattributes__.sort()
+        if children != None and len(children) > 0:
+            childList = []
+            flattenList = {}
+            for childname, child in children:
+                childstr = str(childname)
+                if childstr in childList:
+                    if not flattenList.has_key(childstr):
+                        flattenList[childstr] = (childstr,)
+                else:
+                    childList.append(childstr)
+            if len(flattenList) > 0:
+                self.__xmlflattensequence__ = flattenList
+                
+    def initialize(self, arg1=None):
+        pass
+            
+    
 class Element:
     def __init__(self, name, attrs=None, xsname=None):
         self.name = name
 class Element:
     def __init__(self, name, attrs=None, xsname=None):
         self.name = name
@@ -222,9 +294,11 @@ class Element:
         self.children = []
         self.objclass = None
         self.xsname = xsname
         self.children = []
         self.objclass = None
         self.xsname = xsname
+        self.objtype = None
         
     def getobjtype(self):
         
     def getobjtype(self):
-        objtype = self.attrs.get("objtype")
+#        objtype = self.attrs.get("objtype")
+        objtype = self.objtype
         if (objtype == None):
             if (len(self.children) > 0):
                 objtype = "dict"
         if (objtype == None):
             if (len(self.children) > 0):
                 objtype = "dict"
@@ -238,16 +312,35 @@ class NsElement(object):
         self.targetNS = None
         self.defaultNS = None
         self.prefix = None
         self.targetNS = None
         self.defaultNS = None
         self.prefix = None
-        
-    def isEmpty(self):
-        return ((self.nsMap == {}) and (self.targetNS == None) and (self.defaultNS == None))
 
 
+    def __str__(self):
+        if self.prefix == None:
+            strVal = 'prefix = None; '
+        else:
+            strVal = 'prefix = "%s"; ' % (self.prefix)
+        if self.targetNS == None:
+            strVal += 'targetNS = None; '
+        else:
+            strVal += 'targetNS = "%s"; ' % (self.targetNS)
+        if self.defaultNS == None:
+            strVal += 'defaultNS = None; '
+        else:
+            strVal += 'defaultNS = "%s"; ' % (self.defaultNS)
+        if len(self.nsMap) == 0:
+            strVal += 'nsMap = None; '
+        else:
+            strVal += 'nsMap = {'
+            for ik, iv in self.nsMap.iteritems():
+                strVal += '%s=%s; ' % (ik,iv)
+            strVal += '}'
+        return strVal
+               
     def setKnownTypes(self, masterKnownTypes, masterKnownNamespaces, parentNSE):
         # if we're a nested element, extend our parent element's mapping
         if parentNSE != None:
             self.knownTypes = parentNSE.knownTypes.copy()
             # but if we have a different default namespace, replace the parent's default mappings
     def setKnownTypes(self, masterKnownTypes, masterKnownNamespaces, parentNSE):
         # if we're a nested element, extend our parent element's mapping
         if parentNSE != None:
             self.knownTypes = parentNSE.knownTypes.copy()
             # but if we have a different default namespace, replace the parent's default mappings
-            if parentNSE.defaultNS != self.defaultNS:
+            if (self.defaultNS != None) and (parentNSE.defaultNS != self.defaultNS):
                 newKT = self.knownTypes.copy()
                 for tag in newKT:
                     if tag.find(':') < 0:
                 newKT = self.knownTypes.copy()
                 for tag in newKT:
                     if tag.find(':') < 0:
@@ -283,7 +376,6 @@ class NsElement(object):
                     self.knownTypes[knownTagName] = mapClass
             else:                                           # e.g. "ItemSearchRequest"
                 self.knownTypes[tag] = mapClass
                     self.knownTypes[knownTagName] = mapClass
             else:                                           # e.g. "ItemSearchRequest"
                 self.knownTypes[tag] = mapClass
-##                print 'mapping <%s> to class "%s"' % (tag, mapClass.__name__)
 
     def expandQName(self, eName, attrName, attrValue):
         bigValue = attrValue
 
     def expandQName(self, eName, attrName, attrValue):
         bigValue = attrValue
@@ -298,38 +390,57 @@ class NsElement(object):
                 if shortNs == attrNS:
                     bigValue = '%s:%s' % (longNs, attrNCName)
                     break
                 if shortNs == attrNS:
                     bigValue = '%s:%s' % (longNs, attrNCName)
                     break
-##        print '[expandQName] input attrName = "%s" and attrValue "%s"; output = "%s"' % (attrName, attrValue, bigValue)
         return bigValue
 
 class XMLObjectFactory(xml.sax.ContentHandler):
         return bigValue
 
 class XMLObjectFactory(xml.sax.ContentHandler):
-    def __init__(self, knownTypes=None, knownNamespaces=None):
+    def __init__(self, knownTypes=None, knownNamespaces=None, xmlSource=None, createGenerics=False):
         self.rootelement = None
         self.rootelement = None
-        if (knownTypes == None):
-            self.knownTypes = {}
-        else:
-            self.knownTypes = knownTypes
-        if (knownNamespaces == None):
-            self.knownNamespaces = {}
+        if xmlSource == None:
+            self.xmlSource = "unknown"
         else:
         else:
-            self.knownNamespaces = knownNamespaces
+            self.xmlSource = xmlSource
+        self.createGenerics = createGenerics
         self.skipper = False
         self.elementstack = []
         self.nsstack = []
         self.collectContent = None
         self.skipper = False
         self.elementstack = []
         self.nsstack = []
         self.collectContent = None
+        if (knownNamespaces == None):
+            self.knownNamespaces = {}
+        else:
+            self.knownNamespaces = knownNamespaces
+        self.reversedNamespaces = {}
+        for longns, shortns in self.knownNamespaces.iteritems():
+            self.reversedNamespaces[shortns] = longns
+        self.knownTypes = {}
+        if (knownTypes != None):
+            for tag, cls in knownTypes.iteritems():
+                i = tag.rfind(':')
+                if i >= 0:
+                    shortns = tag[:i]
+                    tag = tag[i+1:]
+                    if not self.reversedNamespaces.has_key(shortns):
+                        errorString = 'Error unmarshalling XML document from source "%s": knownTypes specifies an unmapped short namespace "%s" for element "%s"' % (self.xmlSource, shortns, tag)
+                        raise UnmarshallerException(errorString)
+                    longns = self.reversedNamespaces[shortns]
+                    tag = '%s:%s' % (longns, tag)
+                self.knownTypes[tag] = cls
+        #printKnownTypes(self.knownTypes, 'Unmarshaller.XMLObjectFactory.__init__')
         xml.sax.handler.ContentHandler.__init__(self)
 
     def appendElementStack(self, newElement, newNS):
         self.elementstack.append(newElement)
         xml.sax.handler.ContentHandler.__init__(self)
 
     def appendElementStack(self, newElement, newNS):
         self.elementstack.append(newElement)
-        if (newNS.isEmpty()):
-            if (len(self.nsstack) > 0):
-                newNS = self.nsstack[-1]
-            else:
-                newNS.knownTypes = self.knownTypes.copy()
-        else:
-            if (len(self.nsstack) > 0):
-                newNS.setKnownTypes(self.knownTypes, self.knownNamespaces, self.nsstack[-1])
-            else:
-                newNS.setKnownTypes(self.knownTypes, self.knownNamespaces, None)
+        if (len(self.nsstack) > 0):
+            oldNS = self.nsstack[-1]
+            if newNS.defaultNS == None:
+                newNS.defaultNS = oldNS.defaultNS
+            if newNS.targetNS == None:
+                newNS.targetNS = oldNS.targetNS
+            if len(newNS.nsMap) == 0:
+                newNS.nsMap = oldNS.nsMap
+            elif len(oldNS.nsMap) > 0:
+                map = oldNS.nsMap.copy()
+                map.update(newNS.nsMap)
+                newNS.nsMap = map
         self.nsstack.append(newNS)
         return newNS
     
         self.nsstack.append(newNS)
         return newNS
     
@@ -353,11 +464,16 @@ class XMLObjectFactory(xml.sax.ContentHandler):
             strVal += '>'
             self.collectContent.content += strVal
         xsname = name
             strVal += '>'
             self.collectContent.content += strVal
         xsname = name
-        if name.find(':') > -1:  # Strip namespace prefixes for now until actually looking them up in xsd
-            name = name[name.rfind(":") + 1:]
+        i = name.rfind(':')
+        if i >= 0:
+            nsname = name[:i]
+            name = name[i+1:]
+        else:
+            nsname = None
         element = Element(name, attrs.copy(), xsname=xsname)
         # if the element has namespace attributes, process them and add them to our stack
         nse = NsElement()
         element = Element(name, attrs.copy(), xsname=xsname)
         # if the element has namespace attributes, process them and add them to our stack
         nse = NsElement()
+        objtype = None
         for k in attrs.getNames():
             if k.startswith('xmlns'):
                 longNs = attrs[k]
         for k in attrs.getNames():
             if k.startswith('xmlns'):
                 longNs = attrs[k]
@@ -371,8 +487,28 @@ class XMLObjectFactory(xml.sax.ContentHandler):
                     nse.nsMap[shortNs] = longNs
             elif k == 'targetNamespace':
                 nse.targetNS = attrs.getValue(k)
                     nse.nsMap[shortNs] = longNs
             elif k == 'targetNamespace':
                 nse.targetNS = attrs.getValue(k)
+            elif k == 'objtype':
+                objtype = attrs.getValue(k)
         nse = self.appendElementStack(element, nse)
         nse = self.appendElementStack(element, nse)
-        element.objclass = nse.knownTypes.get(xsname)
+        if nsname != None:
+            if nse.nsMap.has_key(nsname):
+                longname = '%s:%s' % (nse.nsMap[nsname], name)
+##            elif objtype == None:
+##                errorString = 'Error unmarshalling XML document from source "%s": tag "%s" at line "%d", column "%d" has an undefined namespace' % (self.xmlSource, xsname, self._locator.getLineNumber(), self._locator.getColumnNumber())
+##                raise UnmarshallerException(errorString)
+            elif self.reversedNamespaces.has_key(nsname):
+                longname = '%s:%s' % (self.reversedNamespaces[nsname], name)
+            else:
+                longname = xsname
+        elif nse.defaultNS != None:
+            longname = '%s:%s' % (nse.defaultNS, name)
+        else:
+            longname = name
+        element.objtype = objtype
+        element.objclass = self.knownTypes.get(longname)
+        if element.objclass == None and len(self.knownNamespaces) == 0:
+            # handles common case where tags are unqualified and knownTypes are too, but there's a defaultNS
+            element.objclass = self.knownTypes.get(name)
         if (hasattr(element.objclass, "__xmlcontent__")):
             self.collectContent = element
 
         if (hasattr(element.objclass, "__xmlcontent__")):
             self.collectContent = element
 
@@ -387,8 +523,9 @@ class XMLObjectFactory(xml.sax.ContentHandler):
     def endElement(self, name):
 ##        print "[endElement] </%s>" % name
         xsname = name
     def endElement(self, name):
 ##        print "[endElement] </%s>" % name
         xsname = name
-        if name.find(":") > -1:  # Strip namespace prefixes for now until actually looking them up in xsd
-            name = name[name.find(":") + 1:]
+        i = name.rfind(':')
+        if i >= 0:  # Strip namespace prefixes for now until actually looking them up in xsd
+            name = name[i+1:]
         if self.skipper:
             if xsname == "xs:annotation" or xsname == "xsd:annotation":     # here too
                 self.skipper = False
         if self.skipper:
             if xsname == "xs:annotation" or xsname == "xsd:annotation":     # here too
                 self.skipper = False
@@ -405,34 +542,36 @@ class XMLObjectFactory(xml.sax.ContentHandler):
         element, nse = self.popElementStack()
         if ((len(self.elementstack) > 1) and (self.elementstack[-1].getobjtype() == "None")):
             parentElement = self.elementstack[-2]
         element, nse = self.popElementStack()
         if ((len(self.elementstack) > 1) and (self.elementstack[-1].getobjtype() == "None")):
             parentElement = self.elementstack[-2]
-##            print "[endElement] %s: found parent with objtype==None: using its grandparent" % name
         elif (len(self.elementstack) > 0):
             parentElement = self.elementstack[-1]
         objtype = element.getobjtype()
         elif (len(self.elementstack) > 0):
             parentElement = self.elementstack[-1]
         objtype = element.getobjtype()
-##        print "element objtype is: ", objtype
         if (objtype == "None"):
         if (objtype == "None"):
-##            print "[endElement] %s: skipping a (objtype==None) end tag" % name
             return
         constructorarglist = []
         if (len(element.content) > 0):
             strippedElementContent = element.content.strip()
             if (len(strippedElementContent) > 0):
                 constructorarglist.append(element.content)
             return
         constructorarglist = []
         if (len(element.content) > 0):
             strippedElementContent = element.content.strip()
             if (len(strippedElementContent) > 0):
                 constructorarglist.append(element.content)
+        # If the element requires an object, but none is known, use the GenericXMLObject class
+        if ((element.objclass == None) and (element.attrs.get("objtype") == None) and ((len(element.attrs) > 0) or (len(element.children) > 0))):
+            if self.createGenerics:
+                element.objclass = GenericXMLObject
         obj = _objectfactory(objtype, constructorarglist, element.objclass)
         obj = _objectfactory(objtype, constructorarglist, element.objclass)
+        if element.objclass == GenericXMLObject:
+            obj.setXMLAttributes(str(xsname), element.attrs, element.children, nse.nsMap, nse.defaultNS)
         complexType = getComplexType(obj)
         if (obj != None):
             if (hasattr(obj, "__xmlname__") and getattr(obj, "__xmlname__") == "sequence"):
                 self.elementstack[-1].children = oldChildren
                 return
         if (len(element.attrs) > 0) and not isinstance(obj, list):
         complexType = getComplexType(obj)
         if (obj != None):
             if (hasattr(obj, "__xmlname__") and getattr(obj, "__xmlname__") == "sequence"):
                 self.elementstack[-1].children = oldChildren
                 return
         if (len(element.attrs) > 0) and not isinstance(obj, list):
-##            print "[endElement] %s: element has attrs and the obj is not a list" % name
             for attrname, attr in element.attrs.items():
                 if attrname == XMLNS or attrname.startswith(XMLNS_PREFIX):
                     if attrname.startswith(XMLNS_PREFIX):
                         ns = attrname[XMLNS_PREFIX_LENGTH:]
                     else:
                         ns = ""
             for attrname, attr in element.attrs.items():
                 if attrname == XMLNS or attrname.startswith(XMLNS_PREFIX):
                     if attrname.startswith(XMLNS_PREFIX):
                         ns = attrname[XMLNS_PREFIX_LENGTH:]
                     else:
                         ns = ""
-                    if complexType != None:
+                    if complexType != None or element.objclass == GenericXMLObject:
                         if not hasattr(obj, "__xmlnamespaces__"):
                             obj.__xmlnamespaces__ = {ns:attr}
                         elif ns not in obj.__xmlnamespaces__:
                         if not hasattr(obj, "__xmlnamespaces__"):
                             obj.__xmlnamespaces__ = {ns:attr}
                         elif ns not in obj.__xmlnamespaces__:
@@ -447,7 +586,6 @@ class XMLObjectFactory(xml.sax.ContentHandler):
                         xsdElement = complexType.findElement(attrname)
                         if (xsdElement != None):
                             type = xsdElement.type
                         xsdElement = complexType.findElement(attrname)
                         if (xsdElement != None):
                             type = xsdElement.type
-##                            print 'Unmarshalling element "%s", attribute "%s" with type "%s"' % (name, xsdElement.name, type)
                             if (type != None):
                                 if (type == TYPE_QNAME):
                                     attr = nse.expandQName(name, attrname, attr)
                             if (type != None):
                                 if (type == TYPE_QNAME):
                                     attr = nse.expandQName(name, attrname, attr)
@@ -455,11 +593,15 @@ class XMLObjectFactory(xml.sax.ContentHandler):
                                 ### ToDO remove maxOccurs hack after bug 177 is fixed
                                 if attrname == "maxOccurs" and attr == "unbounded":
                                     attr = "-1"
                                 ### ToDO remove maxOccurs hack after bug 177 is fixed
                                 if attrname == "maxOccurs" and attr == "unbounded":
                                     attr = "-1"
-                                attr = _objectfactory(type, attr)
+                                try:
+                                    attr = _objectfactory(type, attr)
+                                except Exception, exceptData:
+                                    errorString = 'Error unmarshalling attribute "%s" at line %d, column %d in XML document from source "%s": %s' % (attrname, self._locator.getLineNumber(), self._locator.getColumnNumber(), self.xmlSource, str(exceptData))
+                                    raise UnmarshallerException(errorString)
                     try:
                         setattrignorecase(obj, _toAttrName(obj, attrname), attr)
                     except AttributeError:
                     try:
                         setattrignorecase(obj, _toAttrName(obj, attrname), attr)
                     except AttributeError:
-                        errorString = 'Error unmarshalling XML document at line %i, column %i: The object type of attribute "%s" of XML element "%s": not specified or known' % (self._locator.getLineNumber(), self._locator.getColumnNumber(), attrname, name)
+                        errorString = 'Error setting value of attribute "%s" at line %d, column %d in XML document from source "%s": object type of XML element "%s" is not specified or known' % (attrname, self._locator.getLineNumber(), self._locator.getColumnNumber(), self.xmlSource, name)
                         raise UnmarshallerException(errorString)
 ##                    obj.__dict__[_toAttrName(obj, attrname)] = attr
         # stuff any child attributes meant to be in a sequence via the __xmlflattensequence__
                         raise UnmarshallerException(errorString)
 ##                    obj.__dict__[_toAttrName(obj, attrname)] = attr
         # stuff any child attributes meant to be in a sequence via the __xmlflattensequence__
@@ -474,14 +616,12 @@ class XMLObjectFactory(xml.sax.ContentHandler):
                         flattenDict[str(xmlnametuple)] = sequencename
                     else:
                         for xmlname in xmlnametuple:
                         flattenDict[str(xmlnametuple)] = sequencename
                     else:
                         for xmlname in xmlnametuple:
-##                            print "[endElement]: adding flattenDict[%s] = %s" % (xmlname, sequencename)
                             flattenDict[xmlname] = sequencename
             else:
                 raise Exception("Invalid type for __xmlflattensequence___ : it must be a dict")
 
         # reattach an object"s attributes to it
         for childname, child in element.children:
                             flattenDict[xmlname] = sequencename
             else:
                 raise Exception("Invalid type for __xmlflattensequence___ : it must be a dict")
 
         # reattach an object"s attributes to it
         for childname, child in element.children:
-##            print "[endElement] childname is: ", childname, "; child is: ", child
             if (childname in flattenDict):
                 sequencename = _toAttrName(obj, flattenDict[childname])
                 if (not hasattr(obj, sequencename)):
             if (childname in flattenDict):
                 sequencename = _toAttrName(obj, flattenDict[childname])
                 if (not hasattr(obj, sequencename)):
@@ -499,12 +639,13 @@ class XMLObjectFactory(xml.sax.ContentHandler):
                 else:
                     obj[childname] = child
             else:
                 else:
                     obj[childname] = child
             else:
-##                print "childname = %s, obj = %s, child = %s" % (childname, repr(obj), repr(child))
-                try:
-                    setattrignorecase(obj, _toAttrName(obj, childname), child)
-                except AttributeError:
-                    raise MarshallerException("Error unmarshalling child element \"%s\" of XML element \"%s\": object type not specified or known" % (childname, name))
-##                obj.__dict__[_toAttrName(obj, childname)] = child
+                # don't replace a good attribute value with a bad one
+                childAttrName = _toAttrName(obj, childname)
+                if (not hasattr(obj, childAttrName)) or (getattr(obj, childAttrName) == None) or (getattr(obj, childAttrName) == []) or (not isinstance(child, GenericXMLObject)):
+                    try:
+                        setattrignorecase(obj, childAttrName, child)
+                    except AttributeError:
+                        raise MarshallerException("Error unmarshalling child element \"%s\" of XML element \"%s\": object type not specified or known" % (childname, name))
 
         if (complexType != None):
             for element in complexType.elements:
 
         if (complexType != None):
             for element in complexType.elements:
@@ -524,7 +665,6 @@ class XMLObjectFactory(xml.sax.ContentHandler):
         if (len(self.elementstack) > 0):
 ##            print "[endElement] appending child with name: ", name, "; objtype: ", objtype
             parentElement.children.append((name, obj))
         if (len(self.elementstack) > 0):
 ##            print "[endElement] appending child with name: ", name, "; objtype: ", objtype
             parentElement.children.append((name, obj))
-##            print "parentElement now has ", len(parentElement.children), " children"
         else:
             self.rootelement = obj
             
         else:
             self.rootelement = obj
             
@@ -539,7 +679,12 @@ def _toAttrName(obj, name):
                 break
 ##    if (name.startswith("__") and not name.endswith("__")):
 ##        name = "_%s%s" % (obj.__class__.__name__, name)
                 break
 ##    if (name.startswith("__") and not name.endswith("__")):
 ##        name = "_%s%s" % (obj.__class__.__name__, name)
-    return name
+    return str(name)
+    
+def printKnownTypes(kt, where):
+    print 'KnownTypes from %s' % (where)
+    for tag, cls in kt.iteritems():
+        print '%s => %s' % (tag, str(cls))
 
 __typeMappingXsdToLang = {
     "string": "str",
 
 __typeMappingXsdToLang = {
     "string": "str",
@@ -569,7 +714,7 @@ def xsdToLangType(xsdType):
     if xsdType.startswith(XMLSCHEMA_XSD_URL):
         xsdType = xsdType[len(XMLSCHEMA_XSD_URL)+1:]
     elif xsdType.startswith(AG_URL):
     if xsdType.startswith(XMLSCHEMA_XSD_URL):
         xsdType = xsdType[len(XMLSCHEMA_XSD_URL)+1:]
     elif xsdType.startswith(AG_URL):
-        xsdType = xsdType[len(AG_URL)+1:]        
+        xsdType = xsdType[len(AG_URL)+1:]
     langType = __typeMappingXsdToLang.get(xsdType)
     if (langType == None):
         raise Exception("Unknown xsd type %s" % xsdType)
     langType = __typeMappingXsdToLang.get(xsdType)
     if (langType == None):
         raise Exception("Unknown xsd type %s" % xsdType)
@@ -588,8 +733,11 @@ def _getXmlValue(langValue):
     else:
         return str(langValue)
 
     else:
         return str(langValue)
 
-def unmarshal(xmlstr, knownTypes=None, knownNamespaces=None, xmlSource=None):
-    objectfactory = XMLObjectFactory(knownTypes, knownNamespaces)
+def unmarshal(xmlstr, knownTypes=None, knownNamespaces=None, xmlSource=None, createGenerics=False):
+    objectfactory = XMLObjectFactory(knownTypes, knownNamespaces, xmlSource, createGenerics)
+    # on Linux, pyXML's sax.parseString fails when passed unicode
+    if (not sysutils.isWindows()):
+        xmlstr = str(xmlstr)
     try:
         xml.sax.parseString(xmlstr, objectfactory)
     except xml.sax.SAXParseException, errorData:
     try:
         xml.sax.parseString(xmlstr, objectfactory)
     except xml.sax.SAXParseException, errorData:
@@ -600,17 +748,19 @@ def unmarshal(xmlstr, knownTypes=None, knownNamespaces=None, xmlSource=None):
     return objectfactory.getRootObject()
 
 def marshal(obj, elementName=None, prettyPrint=False, marshalType=True, indent=0, knownTypes=None, knownNamespaces=None, encoding=-1):
     return objectfactory.getRootObject()
 
 def marshal(obj, elementName=None, prettyPrint=False, marshalType=True, indent=0, knownTypes=None, knownNamespaces=None, encoding=-1):
-##    print '[marshal] entered with elementName = "%s"' % (elementName)
     worker = XMLMarshalWorker(prettyPrint=prettyPrint, marshalType=marshalType, knownTypes=knownTypes, knownNamespaces=knownNamespaces)    
     if obj != None and hasattr(obj, '__xmldeepexclude__'):
         worker.xmldeepexclude = obj.__xmldeepexclude__
     xmlstr = "".join(worker._marshal(obj, elementName, indent=indent))
     worker = XMLMarshalWorker(prettyPrint=prettyPrint, marshalType=marshalType, knownTypes=knownTypes, knownNamespaces=knownNamespaces)    
     if obj != None and hasattr(obj, '__xmldeepexclude__'):
         worker.xmldeepexclude = obj.__xmldeepexclude__
     xmlstr = "".join(worker._marshal(obj, elementName, indent=indent))
-    if (isinstance(encoding, basestring)):
-        return '<?xml version="1.0" encoding="%s"?>\n%s' % (encoding, xmlstr.encode(encoding))
-    elif (encoding == None):
+    aglogging.info(xmlMarshallerLogger, "marshal produced string of type %s", type(xmlstr))
+    if (encoding == None):
         return xmlstr
         return xmlstr
-    else:
-        return '<?xml version="1.0" encoding="%s"?>\n%s' % (sys.getdefaultencoding(), xmlstr)
+    if (not isinstance(encoding, basestring)):
+        encoding = sys.getdefaultencoding()
+    if (not isinstance(xmlstr, unicode)):
+        xmlstr = xmlstr.decode()
+    xmlstr = u'<?xml version="1.0" encoding="%s"?>\n%s' % (encoding, xmlstr)
+    return xmlstr.encode(encoding)
 
 class XMLMarshalWorker(object):
     def __init__(self, marshalType=True, prettyPrint=False, knownTypes=None, knownNamespaces=None):
 
 class XMLMarshalWorker(object):
     def __init__(self, marshalType=True, prettyPrint=False, knownTypes=None, knownNamespaces=None):
@@ -695,7 +845,7 @@ class XMLMarshalWorker(object):
             newNS.prefix = self.nsstack[-1].prefix
         else:
             newNS.prefix = ''
             newNS.prefix = self.nsstack[-1].prefix
         else:
             newNS.prefix = ''
-        if hasattr(obj, "__xmldefaultnamespace__"):
+        if obj != None and hasattr(obj, "__xmldefaultnamespace__"):
             longPrefixNS = getattr(obj, "__xmldefaultnamespace__")
             if longPrefixNS == defaultLongNS:
                 newNS.prefix = ''
             longPrefixNS = getattr(obj, "__xmldefaultnamespace__")
             if longPrefixNS == defaultLongNS:
                 newNS.prefix = ''
@@ -705,13 +855,12 @@ class XMLMarshalWorker(object):
                         if v == longPrefixNS:
                             newNS.prefix = k + ':'
                             break;
                         if v == longPrefixNS:
                             newNS.prefix = k + ':'
                             break;
-##                    print '[appendNSStack] found longPrefixNS in nameSpaces = "%s"' % (newNS.prefix)
                 except:
                     if (longPrefixNS in asDict(self.knownNamespaces)):
                         newNS.prefix = self.knownNamespaces[longPrefixNS] + ':'
                     else:
                         raise MarshallerException('Error marshalling __xmldefaultnamespace__ ("%s") not defined in namespace stack' % (longPrefixNS))
                 except:
                     if (longPrefixNS in asDict(self.knownNamespaces)):
                         newNS.prefix = self.knownNamespaces[longPrefixNS] + ':'
                     else:
                         raise MarshallerException('Error marshalling __xmldefaultnamespace__ ("%s") not defined in namespace stack' % (longPrefixNS))
-        if hasattr(obj, "targetNamespace"):
+        if obj != None and hasattr(obj, "targetNamespace"):
             newNS.targetNS = obj.targetNamespace
         elif len(self.nsstack) > 0:
             newNS.targetNS = self.nsstack[-1].targetNS
             newNS.targetNS = obj.targetNamespace
         elif len(self.nsstack) > 0:
             newNS.targetNS = self.nsstack[-1].targetNS
@@ -749,9 +898,11 @@ class XMLMarshalWorker(object):
             
     def _marshal(self, obj, elementName=None, nameSpacePrefix="", indent=0):
         if (obj != None):
             
     def _marshal(self, obj, elementName=None, nameSpacePrefix="", indent=0):
         if (obj != None):
-            xmlMarshallerLogger.debug("--> _marshal: elementName=%s%s, type=%s, obj=%s, indent=%d" % (nameSpacePrefix, elementName, type(obj), str(obj), indent))
+            aglogging.debug(xmlMarshallerLogger, "--> _marshal: elementName=%s%s, type=%s, obj=%s, indent=%d", nameSpacePrefix, elementName, type(obj), str(obj), indent)
         else:
         else:
-            xmlMarshallerLogger.debug("--> _marshal: elementName=%s%s, obj is None, indent=%d" % (nameSpacePrefix, elementName, indent))
+            aglogging.debug(xmlMarshallerLogger, "--> _marshal: elementName=%s%s, obj is None, indent=%d", nameSpacePrefix, elementName, indent)
+        if ((obj != None) and (hasattr(obj, 'preMarshal'))):
+            obj.preMarshal()
         excludeAttrs = []
         excludeAttrs.extend(self.xmldeepexclude)
         if hasattr(obj, "__xmlexclude__"):
         excludeAttrs = []
         excludeAttrs.extend(self.xmldeepexclude)
         if hasattr(obj, "__xmlexclude__"):
@@ -768,8 +919,8 @@ class XMLMarshalWorker(object):
             newline = ""
             increment = 0
         ## Determine the XML element name. If it isn"t specified in the
             newline = ""
             increment = 0
         ## Determine the XML element name. If it isn"t specified in the
-        ## parameter list, look for it in the __xmlname__ Lang
-        ## attribute, else use the default generic BASETYPE_ELEMENT_NAME.
+        ## parameter list, look for it in the __xmlname__ attribute,
+        ## else use the default generic BASETYPE_ELEMENT_NAME.
         nameSpaceAttrs = self.appendNSStack(obj)
         nameSpacePrefix = self.getNSPrefix()       
         if not elementName:
         nameSpaceAttrs = self.appendNSStack(obj)
         nameSpacePrefix = self.getNSPrefix()       
         if not elementName:
@@ -779,13 +930,15 @@ class XMLMarshalWorker(object):
                 elementName = nameSpacePrefix + BASETYPE_ELEMENT_NAME
         else:
             elementName = nameSpacePrefix + elementName
                 elementName = nameSpacePrefix + BASETYPE_ELEMENT_NAME
         else:
             elementName = nameSpacePrefix + elementName
-##        print '[XMLMarshalWorker._marshal] elementName "%s"; nameSpaceAttrs is "%s"' % (elementName, nameSpaceAttrs)
     
         if (hasattr(obj, "__xmlsequencer__")) and (obj.__xmlsequencer__ != None):
             if (XMLSCHEMA_XSD_URL in self.nsstack[-1].nameSpaces.values()):
                 for kShort, vLong in self.nsstack[-1].nameSpaces.iteritems():
                     if vLong == XMLSCHEMA_XSD_URL:
     
         if (hasattr(obj, "__xmlsequencer__")) and (obj.__xmlsequencer__ != None):
             if (XMLSCHEMA_XSD_URL in self.nsstack[-1].nameSpaces.values()):
                 for kShort, vLong in self.nsstack[-1].nameSpaces.iteritems():
                     if vLong == XMLSCHEMA_XSD_URL:
-                        xsdPrefix = kShort + ':'
+                        if kShort != DEFAULT_NAMESPACE_KEY:
+                            xsdPrefix = kShort + ':'
+                        else:
+                            xsdPrefix = ''
                         break
             else:
                 xsdPrefix = 'xs:'
                         break
             else:
                 xsdPrefix = 'xs:'
@@ -793,7 +946,6 @@ class XMLMarshalWorker(object):
         else:
             elementAdd = None
                    
         else:
             elementAdd = None
                    
-    ##    print "marshal: entered with elementName: ", elementName
         members_to_skip = []
         ## Add more members_to_skip based on ones the user has selected
         ## via the __xmlexclude__ and __xmldeepexclude__ attributes.
         members_to_skip = []
         ## Add more members_to_skip based on ones the user has selected
         ## via the __xmlexclude__ and __xmldeepexclude__ attributes.
@@ -806,7 +958,6 @@ class XMLMarshalWorker(object):
             xmlattributes = obj.__xmlattributes__
             members_to_skip.extend(xmlattributes)
             for attr in xmlattributes:
             xmlattributes = obj.__xmlattributes__
             members_to_skip.extend(xmlattributes)
             for attr in xmlattributes:
-##                print 'Processing element "%s"; attribute "%s"' % (elementName, attr)
                 internalAttrName = attr
                 ifDefPy()
                 if (attr.startswith("__") and not attr.endswith("__")): 
                 internalAttrName = attr
                 ifDefPy()
                 if (attr.startswith("__") and not attr.endswith("__")): 
@@ -814,7 +965,6 @@ class XMLMarshalWorker(object):
                 endIfDef()
                 # Fail silently if a python attribute is specified to be
                 # an XML attribute but is missing.
                 endIfDef()
                 # Fail silently if a python attribute is specified to be
                 # an XML attribute but is missing.
-##            print "marshal:   processing attribute ", internalAttrName
                 attrNameSpacePrefix = ""
                 if hasattr(obj, "__xmlattrnamespaces__"):
                     for nameSpaceKey, nameSpaceAttributes in getattr(obj, "__xmlattrnamespaces__").iteritems():
                 attrNameSpacePrefix = ""
                 if hasattr(obj, "__xmlattrnamespaces__"):
                     for nameSpaceKey, nameSpaceAttributes in getattr(obj, "__xmlattrnamespaces__").iteritems():
@@ -856,8 +1006,7 @@ class XMLMarshalWorker(object):
                 else:
                     value = objutils.toDiffableRepr(value)
     
                 else:
                     value = objutils.toDiffableRepr(value)
     
-                objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, saxutils.escape(value))
-    ##            print "marshal:   new objattrs is: ", objattrs
+                objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, utillang.escape(value))
         if (obj == None):
             xmlString = [""]
         elif isinstance(obj, bool):
         if (obj == None):
             xmlString = [""]
         elif isinstance(obj, bool):
@@ -873,9 +1022,18 @@ class XMLMarshalWorker(object):
             objTypeStr = self._genObjTypeStr("float")
             xmlString = ['%s<%s%s>%s</%s>%s' % (prefix, elementName, objTypeStr, str(obj), elementName, newline)]
         elif isinstance(obj, unicode): # have to check before basestring - unicode is instance of base string
             objTypeStr = self._genObjTypeStr("float")
             xmlString = ['%s<%s%s>%s</%s>%s' % (prefix, elementName, objTypeStr, str(obj), elementName, newline)]
         elif isinstance(obj, unicode): # have to check before basestring - unicode is instance of base string
-            xmlString = ['%s<%s>%s</%s>%s' % (prefix, elementName, saxutils.escape(obj.encode()), elementName, newline)]
+            xmlString = ['%s<%s>%s</%s>%s' % (prefix, elementName, utillang.escape(obj.encode()), elementName, newline)]
         elif isinstance(obj, basestring):
         elif isinstance(obj, basestring):
-            xmlString = ['%s<%s>%s</%s>%s' % (prefix, elementName, saxutils.escape(obj), elementName, newline)]
+            xmlString = ['%s<%s>%s</%s>%s' % (prefix, elementName, utillang.escape(obj), elementName, newline)]
+        elif isinstance(obj, datetime.datetime):
+            objTypeStr = self._genObjTypeStr("datetime")
+            xmlString = ['%s<%s%s>%s</%s>%s' % (prefix, elementName, objTypeStr, str(obj), elementName, newline)]
+        elif isinstance(obj, datetime.date):
+            objTypeStr = self._genObjTypeStr("date")
+            xmlString = ['%s<%s%s>%s</%s>%s' % (prefix, elementName, objTypeStr, str(obj), elementName, newline)]
+        elif isinstance(obj, datetime.time):
+            objTypeStr = self._genObjTypeStr("time")
+            xmlString = ['%s<%s%s>%s</%s>%s' % (prefix, elementName, objTypeStr, str(obj), elementName, newline)]
         elif isinstance(obj, list):
             if len(obj) < 1:
                 xmlString = ""
         elif isinstance(obj, list):
             if len(obj) < 1:
                 xmlString = ""
@@ -910,13 +1068,15 @@ class XMLMarshalWorker(object):
         elif hasattr(obj, "__xmlcontent__"):
             contentValue = getattr(obj, obj.__xmlcontent__)
             if contentValue == None: 
         elif hasattr(obj, "__xmlcontent__"):
             contentValue = getattr(obj, obj.__xmlcontent__)
             if contentValue == None: 
-                contentValue = ''
+                xmlString = ["%s<%s%s%s/>%s" % (prefix, elementName, nameSpaceAttrs, objattrs, newline)]        
             else:
             else:
-                contentValue = saxutils.escape(contentValue)
-            xmlString = ["%s<%s%s%s>%s</%s>%s" % (prefix, elementName, nameSpaceAttrs, objattrs, contentValue, elementName, newline)]        
+                contentValue = utillang.escape(contentValue)
+                xmlString = ["%s<%s%s%s>%s</%s>%s" % (prefix, elementName, nameSpaceAttrs, objattrs, contentValue, elementName, newline)]        
         else:
             # Only add the objtype if the element tag is unknown to us.
         else:
             # Only add the objtype if the element tag is unknown to us.
-            if (self.isKnownType(elementName) == True): 
+            if (isinstance(obj, GenericXMLObject)):
+                objTypeStr = ""
+            elif (self.isKnownType(elementName) == True):
                 objTypeStr = ""
             else:
                 objTypeStr = self._genObjTypeStr("%s.%s" % (obj.__class__.__module__, className))
                 objTypeStr = ""
             else:
                 objTypeStr = self._genObjTypeStr("%s.%s" % (obj.__class__.__module__, className))
@@ -929,7 +1089,7 @@ class XMLMarshalWorker(object):
             if hasattr(obj, "__xmlbody__"):
                 xmlbody = getattr(obj, obj.__xmlbody__)
                 if xmlbody != None:
             if hasattr(obj, "__xmlbody__"):
                 xmlbody = getattr(obj, obj.__xmlbody__)
                 if xmlbody != None:
-                    xmlMemberString.append(xmlbody)           
+                    xmlMemberString.append(utillang.escape(xmlbody))
             else:
                 if hasattr(obj, "__xmlattrgroups__"):
                     attrGroups = obj.__xmlattrgroups__.copy()
             else:
                 if hasattr(obj, "__xmlattrgroups__"):
                     attrGroups = obj.__xmlattrgroups__.copy()
@@ -975,21 +1135,17 @@ class XMLMarshalWorker(object):
                             xmlname = None
                             if (len(xmlnametuple) == 1):
                                 xmlname = xmlnametuple[0]
                             xmlname = None
                             if (len(xmlnametuple) == 1):
                                 xmlname = xmlnametuple[0]
-    ##                        ix = 0
                             if not isinstance(value, (list, tuple)):
                               value = [value]
                             for seqitem in value:
                             if not isinstance(value, (list, tuple)):
                               value = [value]
                             for seqitem in value:
-    ##                            xmlname = xmlnametuple[ix]
-    ##                            ix += 1
-    ##                            if (ix >= len(xmlnametuple)):
-    ##                                ix = 0
                                 xmlMemberString.extend(self._marshal(seqitem, xmlname, subElementNameSpacePrefix, indent=indent+increment))
                         else:
                             if (hasattr(obj, "__xmlrename__") and name in asDict(obj.__xmlrename__)):
                                 xmlname = obj.__xmlrename__[name]
                             else:
                                 xmlname = name
                                 xmlMemberString.extend(self._marshal(seqitem, xmlname, subElementNameSpacePrefix, indent=indent+increment))
                         else:
                             if (hasattr(obj, "__xmlrename__") and name in asDict(obj.__xmlrename__)):
                                 xmlname = obj.__xmlrename__[name]
                             else:
                                 xmlname = name
-                            xmlMemberString.extend(self._marshal(value, xmlname, subElementNameSpacePrefix, indent=indent+increment))
+                            if (value != None):
+                                xmlMemberString.extend(self._marshal(value, xmlname, subElementNameSpacePrefix, indent=indent+increment))
                     if (eName != "__nogroup__"):
                         xmlMemberString.append("%s</%s>%s" % (prefix, eName, newline))
                         prefix = prefix[:-increment]
                     if (eName != "__nogroup__"):
                         xmlMemberString.append("%s</%s>%s" % (prefix, eName, newline))
                         prefix = prefix[:-increment]
@@ -1022,8 +1178,8 @@ class XMLMarshalWorker(object):
                     xmlString.append("><![CDATA[%s]]></%s>%s" % (cdataContent, elementName, newline))
                 else:
                     xmlString.append("/>%s" % newline)
                     xmlString.append("><![CDATA[%s]]></%s>%s" % (cdataContent, elementName, newline))
                 else:
                     xmlString.append("/>%s" % newline)
-    ##        return xmlString
-        xmlMarshallerLogger.debug("<-- _marshal: %s" % str(xmlString))
+        if aglogging.isEnabledForDebug(xmlMarshallerLogger):
+            aglogging.debug(xmlMarshallerLogger, "<-- _marshal: %s", objutils.toDiffableString(xmlString))
         #print "<-- _marshal: %s" % str(xmlString)
         self.popNSStack()
         return xmlString
         #print "<-- _marshal: %s" % str(xmlString)
         self.popNSStack()
         return xmlString
index 02c0c8b2ca890acdcd3b612f77f63af8963ab734..411d55e087a7debf456032ab065e26b79fb6e151 100644 (file)
@@ -22,35 +22,44 @@ import activegrid.util.aglogging as aglogging
 
 xmlLogger = logging.getLogger("activegrid.util.xml")
     
 
 xmlLogger = logging.getLogger("activegrid.util.xml")
     
-def load(fileName, knownTypes=None, knownNamespaces=None):
+def load(fileName, knownTypes=None, knownNamespaces=None, createGenerics=False):
     loadedObject = None
     fileObject = file(fileName)
     timeStart = time.time()
     loadedObject = None
     fileObject = file(fileName)
     timeStart = time.time()
+    xml = ""
     try:
         xml = fileObject.read()
     try:
         xml = fileObject.read()
-        loadedObject = unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=fileName)
+        loadedObject = unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=fileName, createGenerics=createGenerics)
         loadedObject.fileName = os.path.abspath(fileName)
         if hasattr(loadedObject, 'initialize'):
             loadedObject.initialize()
     finally:
         fileObject.close()
         loadedObject.fileName = os.path.abspath(fileName)
         if hasattr(loadedObject, 'initialize'):
             loadedObject.initialize()
     finally:
         fileObject.close()
-        timeDone = time.time()
-        aglogging.info(xmlLogger, ('Load statistics for file %s: elapsed time = %f secs' % (fileName, timeDone-timeStart)))
+        if xmlLogger.isEnabledFor(aglogging.LEVEL_INFO):
+            timeDone = time.time()
+            aglogging.info(xmlLogger, ('Load statistics for file %s (%d bytes): elapsed time = %f secs' % (fileName, len(xml), timeDone-timeStart)))
     return loadedObject
 
     return loadedObject
 
-def loadURI(uri, knownTypes=None, knownNamespaces=None, xmlSource=None):
+def loadURI(uri, knownTypes=None, knownNamespaces=None, xmlSource=None, createGenerics=False):
     loadedObject = None
     loadedObject = None
-    xml = urllib.urlopen(uri).read()
-    loadedObject = unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=xmlSource)
-    loadedObject.fileName = uri
-    if hasattr(loadedObject, 'initialize'):
-        loadedObject.initialize()
+    timeStart = time.time()
+    xml = ""
+    try:
+        xml = urllib.urlopen(uri).read()
+        loadedObject = unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=xmlSource, createGenerics=createGenerics)
+        loadedObject.fileName = uri
+        if hasattr(loadedObject, 'initialize'):
+            loadedObject.initialize()
+    finally:
+        if xmlLogger.isEnabledFor(aglogging.LEVEL_INFO):
+            timeDone = time.time()
+            aglogging.info(xmlLogger, ('Load statistics for URI %s (%d bytes): elapsed time = %f secs' % (uri, len(xml), timeDone-timeStart)))
     return loadedObject
 
     return loadedObject
 
-def unmarshal(xml, knownTypes=None, knownNamespaces=None, xmlSource=None):
+def unmarshal(xml, knownTypes=None, knownNamespaces=None, xmlSource=None, createGenerics=False):
     if (knownTypes == None): 
         knownTypes, knownNamespaces = getAgKnownTypes()
     if (knownTypes == None): 
         knownTypes, knownNamespaces = getAgKnownTypes()
-    return xmlmarshaller.unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=xmlSource)    
+    return xmlmarshaller.unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=xmlSource, createGenerics=createGenerics)    
 
 def save(fileName, objectToSave, prettyPrint=True, marshalType=True, knownTypes=None, knownNamespaces=None, encoding='utf-8'):
     if hasattr(objectToSave, '_xmlReadOnly') and objectToSave._xmlReadOnly == True:
 
 def save(fileName, objectToSave, prettyPrint=True, marshalType=True, knownTypes=None, knownNamespaces=None, encoding='utf-8'):
     if hasattr(objectToSave, '_xmlReadOnly') and objectToSave._xmlReadOnly == True:
@@ -155,41 +164,6 @@ def getAgVersion(fileName):
             version = xml[i+1:j]
     return version
 
             version = xml[i+1:j]
     return version
 
-def escape(data):
-    """Escape ', ", &, <, and > in a string of data.
-
-    Basically, everything that saxutils.escape does (and this calls that, at
-    least for now), but with " added as well.
-
-    XXX TODO make this faster; saxutils.escape() is really slow
-    """
-
-    import xml.sax.saxutils as saxutils
-
-    data=saxutils.escape(data)
-    data=data.replace("\"", "&quot;")
-
-    # IE doesn't support &apos;
-    # data=data.replace("\'", "&apos;")
-    data=data.replace("\'", "&#039;")
-
-    return data
-
-def unescape(data):
-    """Unescape ', ", &, <, and > in a string of data.
-
-    Basically, everything that saxutils.unescape does (and this calls that, at
-    least for now), but with " added as well.
-
-    XXX TODO make this faster; saxutils.unescape() is really slow
-    """
-
-    import xml.sax.saxutils as saxutils
-
-    data=data.replace("&quot;", "\"")
-    data=data.replace("&apos;", "\'")
-    return saxutils.unescape(data)
-
     
 AG_NS_URL = "http://www.activegrid.com/ag.xsd"
 BPEL_NS_URL = "http://schemas.xmlsoap.org/ws/2003/03/business-process"
     
 AG_NS_URL = "http://www.activegrid.com/ag.xsd"
 BPEL_NS_URL = "http://schemas.xmlsoap.org/ws/2003/03/business-process"
@@ -197,7 +171,9 @@ HTTP_WSDL_NS_URL = "http://schemas.xmlsoap.org/wsdl/http/"
 MIME_WSDL_NS_URL = "http://schemas.xmlsoap.org/wsdl/mime/"
 SOAP_NS_URL = "http://schemas.xmlsoap.org/wsdl/soap/"
 SOAP12_NS_URL = "http://schemas.xmlsoap.org/wsdl/soap12/"
 MIME_WSDL_NS_URL = "http://schemas.xmlsoap.org/wsdl/mime/"
 SOAP_NS_URL = "http://schemas.xmlsoap.org/wsdl/soap/"
 SOAP12_NS_URL = "http://schemas.xmlsoap.org/wsdl/soap12/"
+SOAP_NS_ENCODING = "http://schemas.xmlsoap.org/soap/encoding/"
 WSDL_NS_URL = "http://schemas.xmlsoap.org/wsdl/"
 WSDL_NS_URL = "http://schemas.xmlsoap.org/wsdl/"
+WSSE_NS_URL = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
 XFORMS_NS_URL = "http://www.w3c.org/xform.xsd"
 XMLSCHEMA_NS_URL = "http://www.w3.org/2001/XMLSchema"
 XSI_NS_URL = "http://www.w3.org/2001/XMLSchema-instance"
 XFORMS_NS_URL = "http://www.w3c.org/xform.xsd"
 XMLSCHEMA_NS_URL = "http://www.w3.org/2001/XMLSchema"
 XSI_NS_URL = "http://www.w3.org/2001/XMLSchema-instance"
@@ -209,7 +185,8 @@ KNOWN_NAMESPACES = { AG_NS_URL          :  "ag",
                      MIME_WSDL_NS_URL   :  "mime",
                      SOAP_NS_URL        :  "soap",
                      SOAP12_NS_URL      :  "soap12",
                      MIME_WSDL_NS_URL   :  "mime",
                      SOAP_NS_URL        :  "soap",
                      SOAP12_NS_URL      :  "soap12",
-                     WSDL_NS_URL        :  "wsdl", 
+                     WSDL_NS_URL        :  "wsdl",
+                     WSSE_NS_URL        :  "wsse", 
                      XFORMS_NS_URL      :  "xforms",                             
                      XMLSCHEMA_NS_URL   :  "xs",
                      XACML_NS_URL       :  "xacml",
                      XFORMS_NS_URL      :  "xforms",                             
                      XMLSCHEMA_NS_URL   :  "xs",
                      XACML_NS_URL       :  "xacml",
@@ -226,19 +203,23 @@ def getAgXsdToClassName():
             "ag:body"            : "activegrid.model.processmodel.Body",
             "ag:category_substitutions"    : "activegrid.server.layoutrenderer.CategorySubstitutions",
             "ag:command"         : "activegrid.model.wsdl.Command",
             "ag:body"            : "activegrid.model.processmodel.Body",
             "ag:category_substitutions"    : "activegrid.server.layoutrenderer.CategorySubstitutions",
             "ag:command"         : "activegrid.model.wsdl.Command",
+            "ag:setElement"      : "activegrid.model.processmodel.SetElementOperation",
             "ag:css"             : "activegrid.server.layoutrenderer.CSS", 
             "ag:css"             : "activegrid.server.layoutrenderer.CSS", 
-            "ag:cssRule"         : "activegrid.model.processmodel.CssRule",
             "ag:databaseService" : "activegrid.server.deployment.DatabaseService",
             "ag:datasource"      : "activegrid.data.dataservice.DataSource",
             "ag:dataObjectList"  : "activegrid.data.datalang.DataObjectList",
             "ag:debug"           : "activegrid.model.processmodel.DebugOperation",
             "ag:deployment"      : "activegrid.server.deployment.Deployment",
             "ag:databaseService" : "activegrid.server.deployment.DatabaseService",
             "ag:datasource"      : "activegrid.data.dataservice.DataSource",
             "ag:dataObjectList"  : "activegrid.data.datalang.DataObjectList",
             "ag:debug"           : "activegrid.model.processmodel.DebugOperation",
             "ag:deployment"      : "activegrid.server.deployment.Deployment",
+            "ag:formData"        : "activegrid.model.processmodel.FormData",
+            "ag:formVar"         : "activegrid.model.processmodel.FormVar",
             "ag:generator"       : "activegrid.server.layoutrenderer.SerializableGenerator", 
             "ag:head"            : "activegrid.server.layoutrenderer.Head", 
             "ag:hr"              : "activegrid.model.processmodel.HorizontalRow",
             "ag:identity"        : "activegrid.model.identitymodel.Identity",
             "ag:identityref"     : "activegrid.server.deployment.IdentityRef",
             "ag:image"           : "activegrid.model.processmodel.Image",
             "ag:generator"       : "activegrid.server.layoutrenderer.SerializableGenerator", 
             "ag:head"            : "activegrid.server.layoutrenderer.Head", 
             "ag:hr"              : "activegrid.model.processmodel.HorizontalRow",
             "ag:identity"        : "activegrid.model.identitymodel.Identity",
             "ag:identityref"     : "activegrid.server.deployment.IdentityRef",
             "ag:image"           : "activegrid.model.processmodel.Image",
+            "ag:inputPart"       : "activegrid.model.processmodel.InputPart",
+            "ag:keystore"        : "activegrid.model.identitymodel.KeyStore",
             "ag:label"           : "activegrid.model.processmodel.Label",
             "ag:layout"          : "activegrid.server.layoutrenderer.Layout", 
             "ag:layouts"         : "activegrid.server.layoutrenderer.Layouts", 
             "ag:label"           : "activegrid.model.processmodel.Label",
             "ag:layout"          : "activegrid.server.layoutrenderer.Layout", 
             "ag:layouts"         : "activegrid.server.layoutrenderer.Layouts", 
@@ -246,9 +227,11 @@ def getAgXsdToClassName():
             "ag:localService"    : "activegrid.server.deployment.LocalService",
             "ag:parameter"       : "activegrid.server.layoutrenderer.Parameter",
             "ag:parameters"      : "activegrid.server.layoutrenderer.Parameters",
             "ag:localService"    : "activegrid.server.deployment.LocalService",
             "ag:parameter"       : "activegrid.server.layoutrenderer.Parameter",
             "ag:parameters"      : "activegrid.server.layoutrenderer.Parameters",
+            "ag:postInitialize"  : "activegrid.model.processmodel.PostInitialize",
             "ag:processref"      : "activegrid.server.deployment.ProcessRef",
             "ag:query"           : "activegrid.model.processmodel.Query",
             "ag:soapService"     : "activegrid.server.deployment.SoapService",
             "ag:processref"      : "activegrid.server.deployment.ProcessRef",
             "ag:query"           : "activegrid.model.processmodel.Query",
             "ag:soapService"     : "activegrid.server.deployment.SoapService",
+            "ag:redirect"        : "activegrid.server.layoutrenderer.Redirect", 
             "ag:requiredFile"    : "activegrid.server.layoutrenderer.RequiredFile", 
             "ag:resource"        : "activegrid.model.identitymodel.IDResource",
             "ag:restService"     : "activegrid.server.deployment.RestService",
             "ag:requiredFile"    : "activegrid.server.layoutrenderer.RequiredFile", 
             "ag:resource"        : "activegrid.model.identitymodel.IDResource",
             "ag:restService"     : "activegrid.server.deployment.RestService",
@@ -355,27 +338,38 @@ def getAgXsdToClassName():
             "xforms:xforms"      : "activegrid.model.processmodel.XFormsRoot",
             "xs:all"             : "activegrid.model.schema.XsdSequence",
             "xs:any"             : "activegrid.model.schema.XsdAny",
             "xforms:xforms"      : "activegrid.model.processmodel.XFormsRoot",
             "xs:all"             : "activegrid.model.schema.XsdSequence",
             "xs:any"             : "activegrid.model.schema.XsdAny",
+            "xs:anyAttribute"    : "activegrid.model.schema.XsdAnyAttribute",
             "xs:attribute"       : "activegrid.model.schema.XsdAttribute",
             "xs:attribute"       : "activegrid.model.schema.XsdAttribute",
+            "xs:choice"          : "activegrid.model.schema.XsdChoice",
             "xs:complexContent"  : "activegrid.model.schema.XsdComplexContent",
             "xs:complexType"     : "activegrid.model.schema.XsdComplexType",
             "xs:complexContent"  : "activegrid.model.schema.XsdComplexContent",
             "xs:complexType"     : "activegrid.model.schema.XsdComplexType",
+            "xs:documentation"   : "activegrid.model.schema.XsdDocumentation",
             "xs:element"         : "activegrid.model.schema.XsdElement",
             "xs:element"         : "activegrid.model.schema.XsdElement",
-            "xs:enumeration"     : "activegrid.model.schema.XsdEnumeration",
+            "xs:enumeration"     : "activegrid.model.schema.XsdFacetEnumeration",
             "xs:extension"       : "activegrid.model.schema.XsdExtension",
             "xs:extension"       : "activegrid.model.schema.XsdExtension",
+            "xs:fractionDigits"  : "activegrid.model.schema.XsdFacetFractionDigits",
             "xs:field"           : "activegrid.model.schema.XsdKeyField",
             "xs:import"          : "activegrid.model.schema.XsdInclude",
             "xs:include"         : "activegrid.model.schema.XsdInclude",
             "xs:key"             : "activegrid.model.schema.XsdKey",
             "xs:keyref"          : "activegrid.model.schema.XsdKeyRef",
             "xs:field"           : "activegrid.model.schema.XsdKeyField",
             "xs:import"          : "activegrid.model.schema.XsdInclude",
             "xs:include"         : "activegrid.model.schema.XsdInclude",
             "xs:key"             : "activegrid.model.schema.XsdKey",
             "xs:keyref"          : "activegrid.model.schema.XsdKeyRef",
-            "xs:length"          : "activegrid.model.schema.XsdLength",
+            "xs:length"          : "activegrid.model.schema.XsdFacetLength",
             "xs:list"            : "activegrid.model.schema.XsdList",
             "xs:list"            : "activegrid.model.schema.XsdList",
-            "xs:maxLength"       : "activegrid.model.schema.XsdMaxLength",
+            "xs:maxExclusive"    : "activegrid.model.schema.XsdFacetMaxExclusive",
+            "xs:maxInclusive"    : "activegrid.model.schema.XsdFacetMaxInclusive",
+            "xs:maxLength"       : "activegrid.model.schema.XsdFacetMaxLength",
+            "xs:minExclusive"    : "activegrid.model.schema.XsdFacetMinExclusive",
+            "xs:minInclusive"    : "activegrid.model.schema.XsdFacetMinInclusive",
+            "xs:minLength"       : "activegrid.model.schema.XsdFacetMinLength",
+            "xs:pattern"         : "activegrid.model.schema.XsdFacetPattern",
             "xs:restriction"     : "activegrid.model.schema.XsdRestriction",
             "xs:schema"          : "activegrid.model.schema.Schema",
             "xs:selector"        : "activegrid.model.schema.XsdKeySelector",              
             "xs:sequence"        : "activegrid.model.schema.XsdSequence",
             "xs:simpleContent"   : "activegrid.model.schema.XsdSimpleContent",
             "xs:simpleType"      : "activegrid.model.schema.XsdSimpleType",
             "xs:restriction"     : "activegrid.model.schema.XsdRestriction",
             "xs:schema"          : "activegrid.model.schema.Schema",
             "xs:selector"        : "activegrid.model.schema.XsdKeySelector",              
             "xs:sequence"        : "activegrid.model.schema.XsdSequence",
             "xs:simpleContent"   : "activegrid.model.schema.XsdSimpleContent",
             "xs:simpleType"      : "activegrid.model.schema.XsdSimpleType",
-            "xs:totalDigits"     : "activegrid.model.schema.XsdTotalDigits",
+            "xs:totalDigits"     : "activegrid.model.schema.XsdFacetTotalDigits",
+            "xs:whiteSpace"      : "activegrid.model.schema.XsdFacetWhiteSpace",
         }
     return agXsdToClassName
     
         }
     return agXsdToClassName
     
index 2fac20da2319fce20316142822779c24b7cf1cc8..3d5e2e40836a308c57d9e7970d3e960d29364112 100644 (file)
@@ -121,6 +121,7 @@ class FindService(wx.lib.pydocview.DocService):
                 self._findDialog = None
 
             self._replaceDialog = FindReplaceDialog(self.GetDocumentManager().FindSuitableParent(), -1, _("Replace"), size=(320,200), findString=findString)
                 self._findDialog = None
 
             self._replaceDialog = FindReplaceDialog(self.GetDocumentManager().FindSuitableParent(), -1, _("Replace"), size=(320,200), findString=findString)
+            self._replaceDialog.CenterOnParent()
             self._replaceDialog.Show(True)
         else:
             if self._replaceDialog != None:
             self._replaceDialog.Show(True)
         else:
             if self._replaceDialog != None:
@@ -129,6 +130,7 @@ class FindService(wx.lib.pydocview.DocService):
                 self._replaceDialog = None
 
             self._findDialog = FindDialog(self.GetDocumentManager().FindSuitableParent(), -1, _("Find"), size=(320,200), findString=findString)
                 self._replaceDialog = None
 
             self._findDialog = FindDialog(self.GetDocumentManager().FindSuitableParent(), -1, _("Find"), size=(320,200), findString=findString)
+            self._findDialog.CenterOnParent()
             self._findDialog.Show(True)
 
 
             self._findDialog.Show(True)
 
 
@@ -152,6 +154,7 @@ class FindService(wx.lib.pydocview.DocService):
         """ Display Goto Line Number dialog box """
         line = -1
         dialog = wx.TextEntryDialog(parent, _("Enter line number to go to:"), _("Go to Line"))
         """ Display Goto Line Number dialog box """
         line = -1
         dialog = wx.TextEntryDialog(parent, _("Enter line number to go to:"), _("Go to Line"))
+        dialog.CenterOnParent()
         if dialog.ShowModal() == wx.ID_OK:
             try:
                 line = int(dialog.GetValue())
         if dialog.ShowModal() == wx.ID_OK:
             try:
                 line = int(dialog.GetValue())
@@ -356,7 +359,10 @@ class FindDialog(wx.Dialog):
         wx.EVT_BUTTON(self, FindService.FINDONE_ID, self.OnActionEvent)
         cancelBtn = wx.Button(self, wx.ID_CANCEL)
         wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnClose)
         wx.EVT_BUTTON(self, FindService.FINDONE_ID, self.OnActionEvent)
         cancelBtn = wx.Button(self, wx.ID_CANCEL)
         wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnClose)
-        buttonSizer.Add(findBtn, 0, wx.BOTTOM, HALF_SPACE)
+        BTM_SPACE = HALF_SPACE
+        if wx.Platform == "__WXMAC__":
+            BTM_SPACE = SPACE
+        buttonSizer.Add(findBtn, 0, wx.BOTTOM, BTM_SPACE)
         buttonSizer.Add(cancelBtn, 0)
         gridSizer.Add(buttonSizer, pos=(0,2), span=(3,1))
 
         buttonSizer.Add(cancelBtn, 0)
         gridSizer.Add(buttonSizer, pos=(0,2), span=(3,1))
 
@@ -455,9 +461,14 @@ class FindReplaceDialog(FindDialog):
         wx.EVT_BUTTON(self, FindService.REPLACEONE_ID, self.OnActionEvent)
         replaceAllBtn = wx.Button(self, FindService.REPLACEALL_ID, _("Replace All"))
         wx.EVT_BUTTON(self, FindService.REPLACEALL_ID, self.OnActionEvent)
         wx.EVT_BUTTON(self, FindService.REPLACEONE_ID, self.OnActionEvent)
         replaceAllBtn = wx.Button(self, FindService.REPLACEALL_ID, _("Replace All"))
         wx.EVT_BUTTON(self, FindService.REPLACEALL_ID, self.OnActionEvent)
-        buttonSizer.Add(findBtn, 0, wx.BOTTOM, HALF_SPACE)
-        buttonSizer.Add(replaceBtn, 0, wx.BOTTOM, HALF_SPACE)
-        buttonSizer.Add(replaceAllBtn, 0, wx.BOTTOM, HALF_SPACE)
+        
+        BTM_SPACE = HALF_SPACE
+        if wx.Platform == "__WXMAC__":
+            BTM_SPACE = SPACE
+            
+        buttonSizer.Add(findBtn, 0, wx.BOTTOM, BTM_SPACE)
+        buttonSizer.Add(replaceBtn, 0, wx.BOTTOM, BTM_SPACE)
+        buttonSizer.Add(replaceAllBtn, 0, wx.BOTTOM, BTM_SPACE)
         buttonSizer.Add(cancelBtn, 0)
         gridSizer.Add(buttonSizer, pos=(0,2), span=(3,1))
 
         buttonSizer.Add(cancelBtn, 0)
         gridSizer.Add(buttonSizer, pos=(0,2), span=(3,1))
 
@@ -495,12 +506,24 @@ def getFindData():
     return \
 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
     return \
 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
-\x00\x00\x81IDAT8\x8d\xa5S\xc1\x16\xc0\x10\x0ckk\xff\xff\xc7d\x87\xad^U\r\
-\x93S\xe5U$\n\xb3$:\xc1e\x17(\x19Z\xb3$\x9e\xf1DD\xe2\x15\x01x\xea\x93\xef\
-\x04\x989\xea\x1b\xf2U\xc0\xda\xb4\xeb\x11\x1f:\xd8\xb5\xff8\x93\xd4\xa9\xae\
-@/S\xaaUwJ3\x85\xc0\x81\xee\xeb.q\x17C\x81\xd5XU \x1a\x93\xc6\x18\x8d\x90\
-\xe8}\x89\x00\x9a&\x9b_k\x94\x0c\xdf\xd78\xf8\x0b\x99Y\xb4\x08c\x9e\xfe\xc6\
-\xe3\x087\xf9\xd0D\x180\xf1#\x8e\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x00\x01\xb1IDAT8\x8d\xa5\x93=o\xd3P\x14\x86\x1f\xa7\x11\x95<\xdc\xc6\xecN+5\
+[\x86B\x99\xacLQ2Zr[\x89\xa1\xfd\x0b%\x95\x90\x00\xf1\x03\x80\x01\x98\x80\
+\x19G\xac\x0cm\xff@Y\xd9:\xd9Ck\x94\xd6\xddb\x94\x9b\x98\xc8\xd2e1C\xe5\x8b\
+\xdd\x14\x96\xbe\xdb=\x1f\xefy\xef\xf90\x8c\xda\x12wA\xbd\xfc\x18\xfa\x9fs\
+\x80\xf9|\x0e\xc0\x93\xc1\x81\x01\xf0\xe6\xf5\xab\x1c`:\x9d\x02\xf0\xf6\xdd{\
+\xa3\xc8\xa9\xddd\xec\xf5z\xb4Z\xeb\x00\x1c\x1f\x1d\xe6\x85\xdd\xf3<\x06\x83\
+\xc1\x82\xbd\xa2 \x0cCL\xd3d<\x1e\x13\xc71\xb6m\x030\x1a\x8d\x08\x82\x00\x80\
+\xb3\xb3s:\x9d\x8e\xce\xa9(h6\x9b8\x8e\x83m\xdb4\x1a\r\x82 \xe0\xc5\xf3g\xb9\
+eY\xb4\xdbm\x1c\xc7Y\xe8\x81&\xf8\xf4\xf1C\xde\xedv+\xce\x97Owx\xfc\xe8k\xc5\
+\xb6\xb7\xb7\x8b\xef\x0foW \x84\xe0\xea\xea\x02\xa5\x94n\x18\x80\x94\x92\xd9\
+l\x02@\x96e\x95>\xd4nVO\xd3\xb9\x0e\xba\r\xa6i\xd2\xef\xf7\xf0\xfd!\xc7G\x87\
+y\xed:)\xd5\x01J\xfd\xd6c\xfc~\x9a\xfc\x93\xe8\xf2\xf2\x02(Ma6\x9b \x84@)\
+\xa5\t}\xff\x0b\xd0\'I~R\x14\xca\xb2L\xfb\x97\x97\xef-\xeeA!_J\x89\xeb\xba\
+\xb8\xae\xab\xbf\x06\x7f\x97\xacP[\x87\xeb9\x0b!H\x92\ta\x18"\xa5\xd4U\xbd\
+\xadm\xe3\xe1\x83\x8d<\x8a~\x90\xa6\xbf\x88\xe3\x18)\xa5&\xa9\x03X\x96E\xab\
+\xb5\x8em7\xf5\xc2\x94\xb1\xba\xba\xc6\xe6\xe6\x06++\xf7\x89\xa2\xa8\xe2\xd3\
+=89\xf9Va.\x14\x14\xd8\xdf?X VJa\x14\xd7X\xde\xef2\xbc\xadm\xe3\x7f~\xe3\xae\
+\xe7\xfc\x07\x84;\xc5\x82\xa1m&\x95\x00\x00\x00\x00IEND\xaeB`\x82' 
 
 
 def getFindBitmap():
 
 
 def getFindBitmap():
index 496c489802c2987a1472f51ba4cbbf9f63b39a34..9fa41a92ca1eccb1b4d3508ca59f6be32b5375d3 100644 (file)
@@ -17,45 +17,45 @@ import FindService
 _ = wx.GetTranslation
 
 class TextDocument(wx.lib.docview.Document):
 _ = wx.GetTranslation
 
 class TextDocument(wx.lib.docview.Document):
+    
+    def __init__(self):
+        wx.lib.docview.Document .__init__(self)
+        self._inModify = False
 
 
 
 
-    def OnSaveDocument(self, filename):
+    def SaveObject(self, fileObject):
         view = self.GetFirstView()
         view = self.GetFirstView()
-        if not view.GetTextCtrl().SaveFile(filename):
-            return False
-        self.Modify(False)
-        self.SetDocumentSaved(True)
-        #if wx.Platform == "__WXMAC__":
-        #    fn = wx.Filename(filename)
-        #    fn.MacSetDefaultTypeAndCreator()
+        fileObject.write(view.GetTextCtrl().GetValue())
         return True
 
 
         return True
 
 
-    def OnOpenDocument(self, filename):
+    def LoadObject(self, fileObject):
         view = self.GetFirstView()
         view = self.GetFirstView()
-        if not view.GetTextCtrl().LoadFile(filename):
-            return False
-        self.SetFilename(filename, True)
-        self.Modify(False)
-        self.UpdateAllViews()
-        self._savedYet = True
+        data = fileObject.read()
+        view.GetTextCtrl().SetValue(data)
         return True
 
 
     def IsModified(self):
         view = self.GetFirstView()
         if view and view.GetTextCtrl():
         return True
 
 
     def IsModified(self):
         view = self.GetFirstView()
         if view and view.GetTextCtrl():
-            return wx.lib.docview.Document.IsModified(self) or view.GetTextCtrl().IsModified()
-        else:
-            return wx.lib.docview.Document.IsModified(self)
+            return view.GetTextCtrl().IsModified()
+        return False
 
 
 
 
-    def Modify(self, mod):
+    def Modify(self, modify):
+        if self._inModify:
+            return
+        self._inModify = True
+        
         view = self.GetFirstView()
         view = self.GetFirstView()
-        wx.lib.docview.Document.Modify(self, mod)
-        if not mod and view and view.GetTextCtrl():
+        if not modify and view and view.GetTextCtrl():
             view.GetTextCtrl().DiscardEdits()
 
             view.GetTextCtrl().DiscardEdits()
 
+        wx.lib.docview.Document.Modify(self, modify)  # this must called be after the DiscardEdits call above.
+
+        self._inModify = False
+
 
 class TextView(wx.lib.docview.View):
 
 
 class TextView(wx.lib.docview.View):
 
@@ -75,6 +75,7 @@ class TextView(wx.lib.docview.View):
         sizer = wx.BoxSizer()
         font, color = self._GetFontAndColorFromConfig()
         self._textCtrl = self._BuildTextCtrl(frame, font, color = color)
         sizer = wx.BoxSizer()
         font, color = self._GetFontAndColorFromConfig()
         self._textCtrl = self._BuildTextCtrl(frame, font, color = color)
+        self._textCtrl.Bind(wx.EVT_TEXT, self.OnModify)
         sizer.Add(self._textCtrl, 1, wx.EXPAND, 0)
         frame.SetSizer(sizer)
         frame.Layout()
         sizer.Add(self._textCtrl, 1, wx.EXPAND, 0)
         frame.SetSizer(sizer)
         frame.Layout()
@@ -83,6 +84,10 @@ class TextView(wx.lib.docview.View):
         return True
 
 
         return True
 
 
+    def OnModify(self, event):
+        self.GetDocument().Modify(True)
+
+
     def _BuildTextCtrl(self, parent, font, color = wx.BLACK, value = "", selection = [0, 0]):
         if self._wordWrap:
             wordWrapStyle = wx.TE_WORDWRAP
     def _BuildTextCtrl(self, parent, font, color = wx.BLACK, value = "", selection = [0, 0]):
         if self._wordWrap:
             wordWrapStyle = wx.TE_WORDWRAP
@@ -131,6 +136,9 @@ class TextView(wx.lib.docview.View):
 
 
     def OnUpdate(self, sender = None, hint = None):
 
 
     def OnUpdate(self, sender = None, hint = None):
+        if wx.lib.docview.View.OnUpdate(self, sender, hint):
+            return
+
         if hint == "Word Wrap":
             self.SetWordWrap(wx.ConfigBase_Get().ReadInt("TextEditorWordWrap", True))
         elif hint == "Font":
         if hint == "Word Wrap":
             self.SetWordWrap(wx.ConfigBase_Get().ReadInt("TextEditorWordWrap", True))
         elif hint == "Font":
index 85b6253205acf4b06810cce6e85ca3948cc6b014..3d24b224d3f8c7fa384ec73cf2248392c02b302d 100644 (file)
@@ -6,7 +6,7 @@
 #
 # Created:      5/15/03
 # CVS-ID:       $Id$
 #
 # Created:      5/15/03
 # CVS-ID:       $Id$
-# Copyright:    (c) 2003-2005 ActiveGrid, Inc. (Port of wxWindows classes by Julian Smart et al)
+# Copyright:    (c) 2003-2006 ActiveGrid, Inc. (Port of wxWindows classes by Julian Smart et al)
 # License:      wxWindows license
 #----------------------------------------------------------------------------
 
 # License:      wxWindows license
 #----------------------------------------------------------------------------
 
@@ -214,8 +214,10 @@ class Document(wx.EvtHandler):
         false otherwise. You may need to override this if your document view
         maintains its own record of being modified (for example if using
         xTextWindow to view and edit the document).
         false otherwise. You may need to override this if your document view
         maintains its own record of being modified (for example if using
         xTextWindow to view and edit the document).
+        This method has been extended to notify its views that the dirty flag has changed.
         """
         self._documentModified = modify
         """
         self._documentModified = modify
+        self.UpdateAllViews(hint=("modify", self, self._documentModified))
 
 
     def SetDocumentModificationDate(self):
 
 
     def SetDocumentModificationDate(self):
@@ -236,6 +238,16 @@ class Document(wx.EvtHandler):
         return self._documentModificationDate
 
 
         return self._documentModificationDate
 
 
+    def IsDocumentModificationDateCorrect(self):
+        """
+        Returns False if the file has been modified outside of the application.
+        This method has been added to wxPython and is not in wxWindows.
+        """
+        if not os.path.exists(self.GetFilename()):  # document must be in memory only and can't be out of date
+            return True
+        return self._documentModificationDate == os.path.getmtime(self.GetFilename())
+
+
     def GetViews(self):
         """
         Returns the list whose elements are the views on the document.
     def GetViews(self):
         """
         Returns the list whose elements are the views on the document.
@@ -271,6 +283,7 @@ class Document(wx.EvtHandler):
         Destructor. Removes itself from the document manager.
         """
         self.DeleteContents()
         Destructor. Removes itself from the document manager.
         """
         self.DeleteContents()
+        self._documentModificationDate = None
         if self.GetDocumentManager():
             self.GetDocumentManager().RemoveDocument(self)
         wx.EvtHandler.Destroy(self)
         if self.GetDocumentManager():
             self.GetDocumentManager().RemoveDocument(self)
         wx.EvtHandler.Destroy(self)
@@ -364,7 +377,7 @@ class Document(wx.EvtHandler):
             return True
 
         """ check for file modification outside of application """
             return True
 
         """ check for file modification outside of application """
-        if os.path.exists(self.GetFilename()) and os.path.getmtime(self.GetFilename()) != self.GetDocumentModificationDate():
+        if not self.IsDocumentModificationDateCorrect():
             msgTitle = wx.GetApp().GetAppName()
             if not msgTitle:
                 msgTitle = _("Application")
             msgTitle = wx.GetApp().GetAppName()
             if not msgTitle:
                 msgTitle = _("Application")
@@ -485,9 +498,9 @@ class Document(wx.EvtHandler):
                           self.GetDocumentWindow())
             return False
 
                           self.GetDocumentWindow())
             return False
 
+        self.SetDocumentModificationDate()
         self.SetFilename(filename, True)
         self.Modify(False)
         self.SetFilename(filename, True)
         self.Modify(False)
-        self.SetDocumentModificationDate()
         self.SetDocumentSaved(True)
         #if wx.Platform == '__WXMAC__':  # Not yet implemented in wxPython
         #    wx.FileName(file).MacSetDefaultTypeAndCreator()
         self.SetDocumentSaved(True)
         #if wx.Platform == '__WXMAC__':  # Not yet implemented in wxPython
         #    wx.FileName(file).MacSetDefaultTypeAndCreator()
@@ -529,9 +542,9 @@ class Document(wx.EvtHandler):
                           self.GetDocumentWindow())
             return False
 
                           self.GetDocumentWindow())
             return False
 
+        self.SetDocumentModificationDate()
         self.SetFilename(filename, True)
         self.Modify(False)
         self.SetFilename(filename, True)
         self.Modify(False)
-        self.SetDocumentModificationDate()
         self.SetDocumentSaved(True)
         self.UpdateAllViews()
         return True
         self.SetDocumentSaved(True)
         self.UpdateAllViews()
         return True
@@ -614,7 +627,7 @@ class Document(wx.EvtHandler):
             return True
 
         """ check for file modification outside of application """
             return True
 
         """ check for file modification outside of application """
-        if os.path.exists(self.GetFilename()) and os.path.getmtime(self.GetFilename()) != self.GetDocumentModificationDate():
+        if not self.IsDocumentModificationDateCorrect():
             msgTitle = wx.GetApp().GetAppName()
             if not msgTitle:
                 msgTitle = _("Warning")
             msgTitle = wx.GetApp().GetAppName()
             if not msgTitle:
                 msgTitle = _("Warning")
@@ -844,8 +857,14 @@ class View(wx.EvtHandler):
         unused but may in future contain application-specific information for
         making updating more efficient.
         """
         unused but may in future contain application-specific information for
         making updating more efficient.
         """
-        pass
-
+        if hint:
+            if hint[0] == "modify":  # if dirty flag changed, update the view's displayed title
+                frame = self.GetFrame()
+                if frame and hasattr(frame, "OnTitleIsModified"):
+                    frame.OnTitleIsModified()
+                    return True
+        return False
+        
 
     def OnChangeFilename(self):
         """
 
     def OnChangeFilename(self):
         """
@@ -916,11 +935,11 @@ class View(wx.EvtHandler):
         Call this from your view frame's OnActivate member to tell the
         framework which view is currently active. If your windowing system
         doesn't call OnActivate, you may need to call this function from
         Call this from your view frame's OnActivate member to tell the
         framework which view is currently active. If your windowing system
         doesn't call OnActivate, you may need to call this function from
-        any place where you know the view must be active, and
+        OnMenuCommand or any place where you know the view must be active, and
         the framework will need to get the current view.
 
         The prepackaged view frame wxDocChildFrame calls wxView.Activate from
         the framework will need to get the current view.
 
         The prepackaged view frame wxDocChildFrame calls wxView.Activate from
-        its OnActivate member.
+        its OnActivate member and from its OnMenuCommand member.
         """
         if self.GetDocument() and self.GetDocumentManager():
             self.OnActivateView(activate, self, self.GetDocumentManager().GetCurrentView())
         """
         if self.GetDocument() and self.GetDocumentManager():
             self.OnActivateView(activate, self, self.GetDocumentManager().GetCurrentView())
@@ -1865,7 +1884,7 @@ class DocManager(wx.EvtHandler):
             for document in self._docs:
                 if document.GetFilename() and os.path.normcase(document.GetFilename()) == os.path.normcase(path):
                     """ check for file modification outside of application """
             for document in self._docs:
                 if document.GetFilename() and os.path.normcase(document.GetFilename()) == os.path.normcase(path):
                     """ check for file modification outside of application """
-                    if os.path.exists(path) and os.path.getmtime(path) != document.GetDocumentModificationDate():
+                    if not document.IsDocumentModificationDateCorrect():
                         msgTitle = wx.GetApp().GetAppName()
                         if not msgTitle:
                             msgTitle = _("Warning")
                         msgTitle = wx.GetApp().GetAppName()
                         if not msgTitle:
                             msgTitle = _("Warning")
@@ -2148,7 +2167,7 @@ class DocManager(wx.EvtHandler):
                     if len(descr) > 0:
                         descr = descr + _('|')
                     descr = descr + temp.GetDescription() + _(" (") + temp.GetFileFilter() + _(") |") + temp.GetFileFilter()  # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
                     if len(descr) > 0:
                         descr = descr + _('|')
                     descr = descr + temp.GetDescription() + _(" (") + temp.GetFileFilter() + _(") |") + temp.GetFileFilter()  # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
-            descr = _("All (*.*)|*.*|%s") % descr  # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
+            descr = _("All|*.*|%s") % descr  # spacing is important, make sure there is no space after the "|", it causes a bug on wx_gtk
         else:
             descr = _("*.*")
 
         else:
             descr = _("*.*")
 
@@ -2791,6 +2810,7 @@ class DocMDIChildFrame(wx.MDIChildFrame):
             self._childView.Activate(event.GetActive())
         self._activated = 0
 
             self._childView.Activate(event.GetActive())
         self._activated = 0
 
+
     def OnCloseWindow(self, event):
         """
         Closes and deletes the current view and document.
     def OnCloseWindow(self, event):
         """
         Closes and deletes the current view and document.
@@ -2846,6 +2866,28 @@ class DocMDIChildFrame(wx.MDIChildFrame):
         self._childView = view
 
 
         self._childView = view
 
 
+    def OnTitleIsModified(self):
+        """
+        Add/remove to the frame's title an indication that the document is dirty.
+        If the document is dirty, an '*' is appended to the title
+        This method has been added to wxPython and is not in wxWindows.
+        """
+        title = self.GetTitle()
+        if title:
+            if self.GetDocument().IsModified():
+                if title.endswith("*"):
+                    return
+                else:
+                    title = title + "*"
+                    self.SetTitle(title)
+            else:
+                if title.endswith("*"):
+                    title = title[:-1]
+                    self.SetTitle(title)                
+                else:
+                    return
+
+
 class DocPrintout(wx.Printout):
     """
     DocPrintout is a default Printout that prints the first page of a document
 class DocPrintout(wx.Printout):
     """
     DocPrintout is a default Printout that prints the first page of a document
@@ -2892,15 +2934,6 @@ class DocPrintout(wx.Printout):
         return pageNum == 1
 
 
         return pageNum == 1
 
 
-    def OnBeginDocument(self, startPage, endPage):
-        """
-        Not quite sure why this was overridden, but it was in wxWindows! :)
-        """
-        if not wx.Printout.OnBeginDocument(self, startPage, endPage):
-            return False
-        return True
-
-
     def GetPageInfo(self):
         """
         Indicates that the DocPrintout only has a single page.
     def GetPageInfo(self):
         """
         Indicates that the DocPrintout only has a single page.
index 2ac99a3e6b5cca8db710f8a2359345ec0a6d7f02..4e8d665c79ad744e89277f2d051da40bda360269 100644 (file)
@@ -2,11 +2,11 @@
 # Name:         pydocview.py
 # Purpose:      Python extensions to the wxWindows docview framework
 #
 # Name:         pydocview.py
 # Purpose:      Python extensions to the wxWindows docview framework
 #
-# Author:       Peter Yared, Morgan Hua
+# Author:       Peter Yared, Morgan Hua, Matt Fryer
 #
 # Created:      5/15/03
 # CVS-ID:       $Id$
 #
 # Created:      5/15/03
 # CVS-ID:       $Id$
-# Copyright:    (c) 2003-2005 ActiveGrid, Inc.
+# Copyright:    (c) 2003-2006 ActiveGrid, Inc.
 # License:      wxWindows license
 #----------------------------------------------------------------------------
 
 # License:      wxWindows license
 #----------------------------------------------------------------------------
 
@@ -57,7 +57,7 @@ class DocFrameMixIn:
     Class with common code used by DocMDIParentFrame, DocTabbedParentFrame, and
     DocSDIFrame.
     """
     Class with common code used by DocMDIParentFrame, DocTabbedParentFrame, and
     DocSDIFrame.
     """
-    
+
 
     def GetDocumentManager(self):
         """
 
     def GetDocumentManager(self):
         """
@@ -132,7 +132,7 @@ class DocFrameMixIn:
         if sdi:
             if self.GetDocument() and self.GetDocument().GetCommandProcessor():
                 self.GetDocument().GetCommandProcessor().SetEditMenu(editMenu)
         if sdi:
             if self.GetDocument() and self.GetDocument().GetCommandProcessor():
                 self.GetDocument().GetCommandProcessor().SetEditMenu(editMenu)
-            
+
         viewMenu = wx.Menu()
         viewMenu.AppendCheckItem(VIEW_TOOLBAR_ID, _("&Toolbar"), _("Shows or hides the toolbar"))
         wx.EVT_MENU(self, VIEW_TOOLBAR_ID, self.OnViewToolBar)
         viewMenu = wx.Menu()
         viewMenu.AppendCheckItem(VIEW_TOOLBAR_ID, _("&Toolbar"), _("Shows or hides the toolbar"))
         wx.EVT_MENU(self, VIEW_TOOLBAR_ID, self.OnViewToolBar)
@@ -151,7 +151,7 @@ class DocFrameMixIn:
 
         if sdi:  # TODO: Is this really needed?
             wx.EVT_COMMAND_FIND_CLOSE(self, -1, self.ProcessEvent)
 
         if sdi:  # TODO: Is this really needed?
             wx.EVT_COMMAND_FIND_CLOSE(self, -1, self.ProcessEvent)
-            
+
         return menuBar
 
 
         return menuBar
 
 
@@ -194,12 +194,12 @@ class DocFrameMixIn:
         Saves all of the currently open documents.
         """
         docs = wx.GetApp().GetDocumentManager().GetDocuments()
         Saves all of the currently open documents.
         """
         docs = wx.GetApp().GetDocumentManager().GetDocuments()
-        
+
         # save child documents first
         for doc in docs:
             if isinstance(doc, wx.lib.pydocview.ChildDocument):
                 doc.Save()
         # save child documents first
         for doc in docs:
             if isinstance(doc, wx.lib.pydocview.ChildDocument):
                 doc.Save()
-                
+
         # save parent and other documents later
         for doc in docs:
             if not isinstance(doc, wx.lib.pydocview.ChildDocument):
         # save parent and other documents later
         for doc in docs:
             if not isinstance(doc, wx.lib.pydocview.ChildDocument):
@@ -258,7 +258,7 @@ class DocMDIParentFrameMixIn:
     """
     Class with common code used by DocMDIParentFrame and DocTabbedParentFrame.
     """
     """
     Class with common code used by DocMDIParentFrame and DocTabbedParentFrame.
     """
-    
+
 
     def _GetPosSizeFromConfig(self, pos, size):
         """
 
     def _GetPosSizeFromConfig(self, pos, size):
         """
@@ -275,13 +275,13 @@ class DocMDIParentFrameMixIn:
 
             if wx.Display_GetFromPoint(pos) == -1:  # Check if the frame position is offscreen
                 pos = wx.DefaultPosition
 
             if wx.Display_GetFromPoint(pos) == -1:  # Check if the frame position is offscreen
                 pos = wx.DefaultPosition
-                
+
             if size == wx.DefaultSize:
                 size = wx.Size(config.ReadInt("MDIFrameXSize", 450), config.ReadInt("MDIFrameYSize", 300))
         return pos, size
 
 
             if size == wx.DefaultSize:
                 size = wx.Size(config.ReadInt("MDIFrameXSize", 450), config.ReadInt("MDIFrameYSize", 300))
         return pos, size
 
 
-    def _InitFrame(self, embeddedWindows):
+    def _InitFrame(self, embeddedWindows, minSize):
         """
         Initializes the frame and creates the default menubar, toolbar, and status bar.
         """
         """
         Initializes the frame and creates the default menubar, toolbar, and status bar.
         """
@@ -306,7 +306,7 @@ class DocMDIParentFrameMixIn:
             # wxBug: On maximize, statusbar leaves a residual that needs to be refereshed, happens even when user does it
             self.Maximize()
 
             # wxBug: On maximize, statusbar leaves a residual that needs to be refereshed, happens even when user does it
             self.Maximize()
 
-        self.CreateEmbeddedWindows(embeddedWindows)
+        self.CreateEmbeddedWindows(embeddedWindows, minSize)
         self._LayoutFrame()
 
         if wx.Platform == '__WXMAC__':
         self._LayoutFrame()
 
         if wx.Platform == '__WXMAC__':
@@ -333,7 +333,7 @@ class DocMDIParentFrameMixIn:
         if id == SAVEALL_ID:
             self.OnFileSaveAll(event)
             return True
         if id == SAVEALL_ID:
             self.OnFileSaveAll(event)
             return True
-            
+
         return wx.GetApp().ProcessEvent(event)
 
 
         return wx.GetApp().ProcessEvent(event)
 
 
@@ -367,25 +367,24 @@ class DocMDIParentFrameMixIn:
                 if doc.IsModified():
                     filesModified = True
                     break
                 if doc.IsModified():
                     filesModified = True
                     break
-                
+
             event.Enable(filesModified)
             return True
         else:
             return wx.GetApp().ProcessUpdateUIEvent(event)
 
 
             event.Enable(filesModified)
             return True
         else:
             return wx.GetApp().ProcessUpdateUIEvent(event)
 
 
-    def CreateEmbeddedWindows(self, windows=0):
+    def CreateEmbeddedWindows(self, windows=0, minSize=20):
         """
         Create the specified embedded windows around the edges of the frame.
         """
         frameSize = self.GetSize()   # TODO: GetClientWindow.GetSize is still returning 0,0 since the frame isn't fully constructed yet, so using full frame size
         """
         Create the specified embedded windows around the edges of the frame.
         """
         frameSize = self.GetSize()   # TODO: GetClientWindow.GetSize is still returning 0,0 since the frame isn't fully constructed yet, so using full frame size
-        MIN_SIZE = 20
-        defaultHSize = max(MIN_SIZE, int(frameSize[0] / 6))
-        defaultVSize = max(MIN_SIZE, int(frameSize[1] / 7))
+        defaultHSize = max(minSize, int(frameSize[0] / 6))
+        defaultVSize = max(minSize, int(frameSize[1] / 7))
         defaultSubVSize = int(frameSize[1] / 2)
         config = wx.ConfigBase_Get()
         if windows & (EMBEDDED_WINDOW_LEFT | EMBEDDED_WINDOW_TOPLEFT | EMBEDDED_WINDOW_BOTTOMLEFT):
         defaultSubVSize = int(frameSize[1] / 2)
         config = wx.ConfigBase_Get()
         if windows & (EMBEDDED_WINDOW_LEFT | EMBEDDED_WINDOW_TOPLEFT | EMBEDDED_WINDOW_BOTTOMLEFT):
-            self._leftEmbWindow = self._CreateEmbeddedWindow(self, (max(MIN_SIZE,config.ReadInt("MDIEmbedLeftSize", defaultHSize)), -1), wx.LAYOUT_VERTICAL, wx.LAYOUT_LEFT, visible = config.ReadInt("MDIEmbedLeftVisible", 1), sash = wx.SASH_RIGHT)
+            self._leftEmbWindow = self._CreateEmbeddedWindow(self, (max(minSize,config.ReadInt("MDIEmbedLeftSize", defaultHSize)), -1), wx.LAYOUT_VERTICAL, wx.LAYOUT_LEFT, visible = config.ReadInt("MDIEmbedLeftVisible", 1), sash = wx.SASH_RIGHT)
         else:
             self._leftEmbWindow = None
         if windows & EMBEDDED_WINDOW_TOPLEFT:
         else:
             self._leftEmbWindow = None
         if windows & EMBEDDED_WINDOW_TOPLEFT:
@@ -397,7 +396,7 @@ class DocMDIParentFrameMixIn:
         else:
             self._bottomLeftEmbWindow = None
         if windows & (EMBEDDED_WINDOW_RIGHT | EMBEDDED_WINDOW_TOPRIGHT | EMBEDDED_WINDOW_BOTTOMRIGHT):
         else:
             self._bottomLeftEmbWindow = None
         if windows & (EMBEDDED_WINDOW_RIGHT | EMBEDDED_WINDOW_TOPRIGHT | EMBEDDED_WINDOW_BOTTOMRIGHT):
-            self._rightEmbWindow = self._CreateEmbeddedWindow(self, (max(MIN_SIZE,config.ReadInt("MDIEmbedRightSize", defaultHSize)), -1), wx.LAYOUT_VERTICAL, wx.LAYOUT_RIGHT, visible = config.ReadInt("MDIEmbedRightVisible", 1), sash = wx.SASH_LEFT)
+            self._rightEmbWindow = self._CreateEmbeddedWindow(self, (max(minSize,config.ReadInt("MDIEmbedRightSize", defaultHSize)), -1), wx.LAYOUT_VERTICAL, wx.LAYOUT_RIGHT, visible = config.ReadInt("MDIEmbedRightVisible", 1), sash = wx.SASH_LEFT)
         else:
             self._rightEmbWindow = None
         if windows & EMBEDDED_WINDOW_TOPRIGHT:
         else:
             self._rightEmbWindow = None
         if windows & EMBEDDED_WINDOW_TOPRIGHT:
@@ -409,11 +408,11 @@ class DocMDIParentFrameMixIn:
         else:
             self._bottomRightEmbWindow = None
         if windows & EMBEDDED_WINDOW_TOP:
         else:
             self._bottomRightEmbWindow = None
         if windows & EMBEDDED_WINDOW_TOP:
-            self._topEmbWindow = self._CreateEmbeddedWindow(self, (-1, max(MIN_SIZE,config.ReadInt("MDIEmbedTopSize", defaultVSize))), wx.LAYOUT_HORIZONTAL, wx.LAYOUT_TOP, visible = config.ReadInt("MDIEmbedTopVisible", 1), sash = wx.SASH_BOTTOM)
+            self._topEmbWindow = self._CreateEmbeddedWindow(self, (-1, max(minSize,config.ReadInt("MDIEmbedTopSize", defaultVSize))), wx.LAYOUT_HORIZONTAL, wx.LAYOUT_TOP, visible = config.ReadInt("MDIEmbedTopVisible", 1), sash = wx.SASH_BOTTOM)
         else:
             self._topEmbWindow = None
         if windows & EMBEDDED_WINDOW_BOTTOM:
         else:
             self._topEmbWindow = None
         if windows & EMBEDDED_WINDOW_BOTTOM:
-            self._bottomEmbWindow = self._CreateEmbeddedWindow(self, (-1, max(MIN_SIZE,config.ReadInt("MDIEmbedBottomSize", defaultVSize))), wx.LAYOUT_HORIZONTAL, wx.LAYOUT_BOTTOM, visible = config.ReadInt("MDIEmbedBottomVisible", 1), sash = wx.SASH_TOP)
+            self._bottomEmbWindow = self._CreateEmbeddedWindow(self, (-1, max(minSize,config.ReadInt("MDIEmbedBottomSize", defaultVSize))), wx.LAYOUT_HORIZONTAL, wx.LAYOUT_BOTTOM, visible = config.ReadInt("MDIEmbedBottomVisible", 1), sash = wx.SASH_TOP)
         else:
             self._bottomEmbWindow = None
 
         else:
             self._bottomEmbWindow = None
 
@@ -499,7 +498,7 @@ class DocMDIParentFrameMixIn:
 
     def _CreateEmbeddedWindow(self, parent, size, orientation, alignment, visible=True, sash=None):
         """
 
     def _CreateEmbeddedWindow(self, parent, size, orientation, alignment, visible=True, sash=None):
         """
-        Creates the embedded window with the specified size, orientation, and alignment.  If the 
+        Creates the embedded window with the specified size, orientation, and alignment.  If the
         window is not visible it will retain the size with which it was last viewed.
         """
         window = wx.SashLayoutWindow(parent, wx.NewId(), style = wx.NO_BORDER | wx.SW_3D)
         window is not visible it will retain the size with which it was last viewed.
         """
         window = wx.SashLayoutWindow(parent, wx.NewId(), style = wx.NO_BORDER | wx.SW_3D)
@@ -544,7 +543,7 @@ class DocMDIParentFrameMixIn:
         if isinstance(window.GetParent(), wx.SashLayoutWindow):  # It is a parent sashwindow with multiple embedded sashwindows
             parentSashWindow = window.GetParent()
             if show:  # Make sure it is visible in case all of the subwindows were hidden
         if isinstance(window.GetParent(), wx.SashLayoutWindow):  # It is a parent sashwindow with multiple embedded sashwindows
             parentSashWindow = window.GetParent()
             if show:  # Make sure it is visible in case all of the subwindows were hidden
-                parentSashWindow.Show()                
+                parentSashWindow.Show()
             if show and window._sizeBeforeHidden:
                 if window._sizeBeforeHidden[1] == parentSashWindow.GetClientSize()[1]:
                     if window == self.GetEmbeddedWindow(EMBEDDED_WINDOW_BOTTOMLEFT) and self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPLEFT).IsShown():
             if show and window._sizeBeforeHidden:
                 if window._sizeBeforeHidden[1] == parentSashWindow.GetClientSize()[1]:
                     if window == self.GetEmbeddedWindow(EMBEDDED_WINDOW_BOTTOMLEFT) and self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPLEFT).IsShown():
@@ -568,7 +567,7 @@ class DocMDIParentFrameMixIn:
                             self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPRIGHT).SetDefaultSize(otherWindowSize)
                         elif window == self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPRIGHT):
                             self.GetEmbeddedWindow(EMBEDDED_WINDOW_BOTTOMRIGHT).SetDefaultSize(otherWindowSize)
                             self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPRIGHT).SetDefaultSize(otherWindowSize)
                         elif window == self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPRIGHT):
                             self.GetEmbeddedWindow(EMBEDDED_WINDOW_BOTTOMRIGHT).SetDefaultSize(otherWindowSize)
-                    
+
             if not show:
                 if window == self.GetEmbeddedWindow(EMBEDDED_WINDOW_BOTTOMRIGHT) and not self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPRIGHT).IsShown() \
                     or window == self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPRIGHT) and not self.GetEmbeddedWindow(EMBEDDED_WINDOW_BOTTOMRIGHT).IsShown() \
             if not show:
                 if window == self.GetEmbeddedWindow(EMBEDDED_WINDOW_BOTTOMRIGHT) and not self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPRIGHT).IsShown() \
                     or window == self.GetEmbeddedWindow(EMBEDDED_WINDOW_TOPRIGHT) and not self.GetEmbeddedWindow(EMBEDDED_WINDOW_BOTTOMRIGHT).IsShown() \
@@ -618,7 +617,7 @@ class DocTabbedChildFrame(wx.Panel):
         Dummy method since the icon of tabbed frames are managed by the notebook.
         """
         return None
         Dummy method since the icon of tabbed frames are managed by the notebook.
         """
         return None
-        
+
 
     def SetIcon(self, icon):
         """
 
     def SetIcon(self, icon):
         """
@@ -664,6 +663,23 @@ class DocTabbedChildFrame(wx.Panel):
         wx.GetApp().GetTopWindow().SetNotebookPageTitle(self, title)
 
 
         wx.GetApp().GetTopWindow().SetNotebookPageTitle(self, title)
 
 
+    def OnTitleIsModified(self):
+        """
+        Add/remove to the frame's title an indication that the document is dirty.
+        If the document is dirty, an '*' is appended to the title
+        """
+        title = self.GetTitle()
+        if title:
+            if self.GetDocument().IsModified():
+                if not title.endswith("*"):
+                    title = title + "*"
+                    self.SetTitle(title)
+            else:
+                if title.endswith("*"):
+                    title = title[:-1]
+                    self.SetTitle(title)
+        
+
     def ProcessEvent(event):
         """
         Processes an event, searching event tables and calling zero or more
     def ProcessEvent(event):
         """
         Processes an event, searching event tables and calling zero or more
@@ -719,7 +735,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
     """
 
 
     """
 
 
-    def __init__(self, docManager, frame, id, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, name = "DocTabbedParentFrame", embeddedWindows = 0):
+    def __init__(self, docManager, frame, id, title, pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE, name = "DocTabbedParentFrame", embeddedWindows = 0, minSize=20):
         """
         Constructor.  Note that the event table must be rebuilt for the
         frame since the EvtHandler is not virtual.
         """
         Constructor.  Note that the event table must be rebuilt for the
         frame since the EvtHandler is not virtual.
@@ -764,20 +780,20 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
         # End From docview.MDIParentFrame
 
         self.CreateNotebook()
         # End From docview.MDIParentFrame
 
         self.CreateNotebook()
-        self._InitFrame(embeddedWindows)
-        
+        self._InitFrame(embeddedWindows, minSize)
+
 
     def _LayoutFrame(self):
         """
         Lays out the frame.
         """
         wx.LayoutAlgorithm().LayoutFrame(self, self._notebook)
 
     def _LayoutFrame(self):
         """
         Lays out the frame.
         """
         wx.LayoutAlgorithm().LayoutFrame(self, self._notebook)
-        
+
 
     def CreateNotebook(self):
         """
         Creates the notebook to use for the tabbed document interface.
 
     def CreateNotebook(self):
         """
         Creates the notebook to use for the tabbed document interface.
-        """     
+        """
         if wx.Platform != "__WXMAC__":
             self._notebook = wx.Notebook(self, wx.NewId())
         else:
         if wx.Platform != "__WXMAC__":
             self._notebook = wx.Notebook(self, wx.NewId())
         else:
@@ -789,7 +805,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
             wx.EVT_LISTBOOK_PAGE_CHANGED(self, self._notebook.GetId(), self.OnNotebookPageChanged)
         wx.EVT_RIGHT_DOWN(self._notebook, self.OnNotebookRightClick)
         wx.EVT_MIDDLE_DOWN(self._notebook, self.OnNotebookMiddleClick)
             wx.EVT_LISTBOOK_PAGE_CHANGED(self, self._notebook.GetId(), self.OnNotebookPageChanged)
         wx.EVT_RIGHT_DOWN(self._notebook, self.OnNotebookRightClick)
         wx.EVT_MIDDLE_DOWN(self._notebook, self.OnNotebookMiddleClick)
-        
+
         # wxBug: wx.Listbook does not implement HitTest the same way wx.Notebook
         # does, so for now don't fire MouseOver events.
         if wx.Platform != "__WXMAC__":
         # wxBug: wx.Listbook does not implement HitTest the same way wx.Notebook
         # does, so for now don't fire MouseOver events.
         if wx.Platform != "__WXMAC__":
@@ -808,7 +824,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
                         print "Warning: icon for '%s' isn't 16x16, not crossplatform" % template._docTypeName
                 iconIndex = iconList.AddIcon(icon)
                 self._iconIndexLookup.append((template, iconIndex))
                         print "Warning: icon for '%s' isn't 16x16, not crossplatform" % template._docTypeName
                 iconIndex = iconList.AddIcon(icon)
                 self._iconIndexLookup.append((template, iconIndex))
-                
+
         icon = getBlankIcon()
         if icon.GetHeight() != 16 or icon.GetWidth() != 16:
             icon.SetHeight(16)
         icon = getBlankIcon()
         if icon.GetHeight() != 16 or icon.GetWidth() != 16:
             icon.SetHeight(16)
@@ -824,7 +840,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
         Returns the notebook used by the tabbed document interface.
         """
         return self._notebook
         Returns the notebook used by the tabbed document interface.
         """
         return self._notebook
-        
+
 
     def GetActiveChild(self):
         """
 
     def GetActiveChild(self):
         """
@@ -849,14 +865,16 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
     def OnNotebookMouseOver(self, event):
         # wxBug: On Windows XP the tooltips don't automatically disappear when you move the mouse and it is on a notebook tab, has nothing to do with this code!!!
         index, type = self._notebook.HitTest(event.GetPosition())
     def OnNotebookMouseOver(self, event):
         # wxBug: On Windows XP the tooltips don't automatically disappear when you move the mouse and it is on a notebook tab, has nothing to do with this code!!!
         index, type = self._notebook.HitTest(event.GetPosition())
-        
+
         if index > -1:
             doc = self._notebook.GetPage(index).GetView().GetDocument()
         if index > -1:
             doc = self._notebook.GetPage(index).GetView().GetDocument()
-            self._notebook.SetToolTip(wx.ToolTip(doc.GetFilename()))
+            # wxBug: Tooltips no longer appearing on tabs except on
+            # about a 2 pixel area between tab top and contents that will show tip.
+            self._notebook.GetParent().SetToolTip(wx.ToolTip(doc.GetFilename()))
         else:
             self._notebook.SetToolTip(wx.ToolTip(""))
         event.Skip()
         else:
             self._notebook.SetToolTip(wx.ToolTip(""))
         event.Skip()
-            
+
 
     def OnNotebookMiddleClick(self, event):
         """
 
     def OnNotebookMiddleClick(self, event):
         """
@@ -868,7 +886,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
             doc = self._notebook.GetPage(index).GetView().GetDocument()
             if doc:
                 doc.DeleteAllViews()
             doc = self._notebook.GetPage(index).GetView().GetDocument()
             if doc:
                 doc.DeleteAllViews()
-                
+
     def OnNotebookRightClick(self, event):
         """
         Handles right clicks for the notebook, enabling users to either close
     def OnNotebookRightClick(self, event):
         """
         Handles right clicks for the notebook, enabling users to either close
@@ -911,11 +929,11 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
                 def OnRightMenuSelect(event):
                     self._notebook.SetSelection(selectIDs[event.GetId()])
                 wx.EVT_MENU(self, id, OnRightMenuSelect)
                 def OnRightMenuSelect(event):
                     self._notebook.SetSelection(selectIDs[event.GetId()])
                 wx.EVT_MENU(self, id, OnRightMenuSelect)
-        
+
         self._notebook.PopupMenu(menu, wx.Point(x, y))
         menu.Destroy()
         self._notebook.PopupMenu(menu, wx.Point(x, y))
         menu.Destroy()
-            
-            
+
+
     def AddNotebookPage(self, panel, title):
         """
         Adds a document page to the notebook.
     def AddNotebookPage(self, panel, title):
         """
         Adds a document page to the notebook.
@@ -934,7 +952,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
                     break
         if not found:
             self._notebook.SetPageImage(index, self._blankIconIndex)
                     break
         if not found:
             self._notebook.SetPageImage(index, self._blankIconIndex)
-        
+
         # wxBug: the wxListbook used on Mac needs its tabs list resized
         # whenever a new tab is added, but the only way to do this is
         # to resize the entire control
         # wxBug: the wxListbook used on Mac needs its tabs list resized
         # whenever a new tab is added, but the only way to do this is
         # to resize the entire control
@@ -942,9 +960,12 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
             content_size = self._notebook.GetSize()
             self._notebook.SetSize((content_size.x+2, -1))
             self._notebook.SetSize((content_size.x, -1))
             content_size = self._notebook.GetSize()
             self._notebook.SetSize((content_size.x+2, -1))
             self._notebook.SetSize((content_size.x, -1))
-        
+
         self._notebook.Layout()
 
         self._notebook.Layout()
 
+        windowMenuService = wx.GetApp().GetService(WindowMenuService)
+        if windowMenuService:
+            windowMenuService.BuildWindowMenu(wx.GetApp().GetTopWindow())  # build file menu list when we open a file
 
 
     def RemoveNotebookPage(self, panel):
 
 
     def RemoveNotebookPage(self, panel):
@@ -953,7 +974,18 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
         """
         index = self.GetNotebookPageIndex(panel)
         if index > -1:
         """
         index = self.GetNotebookPageIndex(panel)
         if index > -1:
+            if self._notebook.GetPageCount() == 1 or index < 2:
+                pass
+            elif index >= 1:
+                self._notebook.SetSelection(index - 1)
+            elif index < self._notebook.GetPageCount():
+                self._notebook.SetSelection(index + 1)
             self._notebook.DeletePage(index)
             self._notebook.DeletePage(index)
+            self._notebook.GetParent().SetToolTip(wx.ToolTip(""))
+
+        windowMenuService = wx.GetApp().GetService(WindowMenuService)
+        if windowMenuService:
+            windowMenuService.BuildWindowMenu(wx.GetApp().GetTopWindow())  # build file menu list when we open a file
 
 
     def ActivateNotebookPage(self, panel):
 
 
     def ActivateNotebookPage(self, panel):
@@ -964,15 +996,19 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
         if index > -1:
             self._notebook.SetFocus()
             self._notebook.SetSelection(index)
         if index > -1:
             self._notebook.SetFocus()
             self._notebook.SetSelection(index)
-        
+
 
     def GetNotebookPageTitle(self, panel):
 
     def GetNotebookPageTitle(self, panel):
-        return self._notebook.GetPageText(self.GetNotebookPageIndex(panel))
-        
+        index = self.GetNotebookPageIndex(panel)
+        if index != -1:
+            return self._notebook.GetPageText(self.GetNotebookPageIndex(panel))
+        else:
+            return None
+
 
     def SetNotebookPageTitle(self, panel, title):
         self._notebook.SetPageText(self.GetNotebookPageIndex(panel), title)
 
     def SetNotebookPageTitle(self, panel, title):
         self._notebook.SetPageText(self.GetNotebookPageIndex(panel), title)
-        
+
 
     def GetNotebookPageIndex(self, panel):
         """
 
     def GetNotebookPageIndex(self, panel):
         """
@@ -984,7 +1020,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
                 index = i
                 break
         return index
                 index = i
                 break
         return index
-        
+
 
     def ProcessEvent(self, event):
         """
 
     def ProcessEvent(self, event):
         """
@@ -1065,7 +1101,7 @@ class DocTabbedParentFrame(wx.Frame, DocFrameMixIn, DocMDIParentFrameMixIn):
             self.Destroy()
         else:
             event.Veto()
             self.Destroy()
         else:
             event.Veto()
-            
+
 
 class DocMDIChildFrame(wx.MDIChildFrame):
     """
 
 class DocMDIChildFrame(wx.MDIChildFrame):
     """
@@ -1121,6 +1157,27 @@ class DocMDIChildFrame(wx.MDIChildFrame):
             self._childView.Activate(True)
 
 
             self._childView.Activate(True)
 
 
+    def OnTitleIsModified(self):
+        """
+        Add/remove to the frame's title an indication that the document is dirty.
+        If the document is dirty, an '*' is appended to the title
+        """
+        title = self.GetTitle()
+        if title:
+            if self.GetDocument().IsModified():
+                if title.endswith("*"):
+                    return
+                else:
+                    title = title + "*"
+                    self.SetTitle(title)
+            else:
+                if title.endswith("*"):
+                    title = title[:-1]
+                    self.SetTitle(title)                
+                else:
+                    return
+
+
     def ProcessEvent(event):
         """
         Processes an event, searching event tables and calling zero or more
     def ProcessEvent(event):
         """
         Processes an event, searching event tables and calling zero or more
@@ -1216,9 +1273,6 @@ class DocMDIChildFrame(wx.MDIChildFrame):
         self._childView = view
 
 
         self._childView = view
 
 
-
-
-
 class DocService(wx.EvtHandler):
     """
     An abstract class used to add reusable services to a docview application.
 class DocService(wx.EvtHandler):
     """
     An abstract class used to add reusable services to a docview application.
@@ -1247,7 +1301,7 @@ class DocService(wx.EvtHandler):
 
     def ProcessEventBeforeWindows(self, event):
         """
 
     def ProcessEventBeforeWindows(self, event):
         """
-        Processes an event before the main window has a chance to process the window. 
+        Processes an event before the main window has a chance to process the window.
         Override this method for a particular service.
         """
         return False
         Override this method for a particular service.
         """
         return False
@@ -1360,7 +1414,7 @@ class DocOptionsService(DocService):
                 toolsMenu.AppendSeparator()
             toolsMenu.Append(self._toolOptionsID, _("&Options..."), _("Sets options"))
             wx.EVT_MENU(frame, self._toolOptionsID, frame.ProcessEvent)
                 toolsMenu.AppendSeparator()
             toolsMenu.Append(self._toolOptionsID, _("&Options..."), _("Sets options"))
             wx.EVT_MENU(frame, self._toolOptionsID, frame.ProcessEvent)
-            
+
 
     def ProcessEvent(self, event):
         """
 
     def ProcessEvent(self, event):
         """
@@ -1379,7 +1433,7 @@ class DocOptionsService(DocService):
         Return the modes supported by the application.  Use docview.DOC_SDI and
         docview.DOC_MDI flags to check if SDI and/or MDI modes are supported.
         """
         Return the modes supported by the application.  Use docview.DOC_SDI and
         docview.DOC_MDI flags to check if SDI and/or MDI modes are supported.
         """
-        return self._supportedModes        
+        return self._supportedModes
 
 
     def SetSupportedModes(self, _supportedModessupportedModes):
 
 
     def SetSupportedModes(self, _supportedModessupportedModes):
@@ -1400,7 +1454,7 @@ class DocOptionsService(DocService):
 
     def AddOptionsPanel(self, optionsPanel):
         """
 
     def AddOptionsPanel(self, optionsPanel):
         """
-        Adds an options panel to the options dialog. 
+        Adds an options panel to the options dialog.
         """
         self._optionsPanels.append(optionsPanel)
 
         """
         self._optionsPanels.append(optionsPanel)
 
@@ -1429,7 +1483,7 @@ class OptionsDialog(wx.Dialog):
         """
         Initializes the options dialog with a notebook page that contains new
         instances of the passed optionsPanelClasses.
         """
         Initializes the options dialog with a notebook page that contains new
         instances of the passed optionsPanelClasses.
-        """        
+        """
         wx.Dialog.__init__(self, parent, -1, _("Options"))
 
         self._optionsPanels = []
         wx.Dialog.__init__(self, parent, -1, _("Options"))
 
         self._optionsPanels = []
@@ -1445,15 +1499,15 @@ class OptionsDialog(wx.Dialog):
         else:
             optionsNotebook = wx.Notebook(self, wx.NewId(), style=wx.NB_MULTILINE)  # NB_MULTILINE is windows platform only
         sizer.Add(optionsNotebook, 0, wx.ALL | wx.EXPAND, SPACE)
         else:
             optionsNotebook = wx.Notebook(self, wx.NewId(), style=wx.NB_MULTILINE)  # NB_MULTILINE is windows platform only
         sizer.Add(optionsNotebook, 0, wx.ALL | wx.EXPAND, SPACE)
-        
+
         if wx.Platform == "__WXMAC__":
             iconList = wx.ImageList(16, 16, initialCount = len(optionsPanelClasses))
             self._iconIndexLookup = []
         if wx.Platform == "__WXMAC__":
             iconList = wx.ImageList(16, 16, initialCount = len(optionsPanelClasses))
             self._iconIndexLookup = []
-               
+
             for optionsPanelClass in optionsPanelClasses:
                 optionsPanel = optionsPanelClass(optionsNotebook, -1)
                 self._optionsPanels.append(optionsPanel)
             for optionsPanelClass in optionsPanelClasses:
                 optionsPanel = optionsPanelClass(optionsNotebook, -1)
                 self._optionsPanels.append(optionsPanel)
-             
+
                 # We need to populate the image list before setting notebook images
                 if hasattr(optionsPanel, "GetIcon"):
                     icon = optionsPanel.GetIcon()
                 # We need to populate the image list before setting notebook images
                 if hasattr(optionsPanel, "GetIcon"):
                     icon = optionsPanel.GetIcon()
@@ -1467,18 +1521,18 @@ class OptionsDialog(wx.Dialog):
                             print "Warning: icon for '%s' isn't 16x16, not crossplatform" % template._docTypeName
                     iconIndex = iconList.AddIcon(icon)
                     self._iconIndexLookup.append((optionsPanel, iconIndex))
                             print "Warning: icon for '%s' isn't 16x16, not crossplatform" % template._docTypeName
                     iconIndex = iconList.AddIcon(icon)
                     self._iconIndexLookup.append((optionsPanel, iconIndex))
-            
+
                 else:
                     # use -1 to represent that this panel has no icon
                     self._iconIndexLookup.append((optionsPanel, -1))
                 else:
                     # use -1 to represent that this panel has no icon
                     self._iconIndexLookup.append((optionsPanel, -1))
-                
+
             optionsNotebook.AssignImageList(iconList)
             optionsNotebook.AssignImageList(iconList)
-        
+
             # Add icons to notebook
             for index in range(0, len(optionsPanelClasses)-1):
                 iconIndex = self._iconIndexLookup[index][1]
                 if iconIndex >= 0:
             # Add icons to notebook
             for index in range(0, len(optionsPanelClasses)-1):
                 iconIndex = self._iconIndexLookup[index][1]
                 if iconIndex >= 0:
-                    optionsNotebook.SetPageImage(index, iconIndex)        
+                    optionsNotebook.SetPageImage(index, iconIndex)
         else:
             for optionsPanelClass in optionsPanelClasses:
                 optionsPanel = optionsPanelClass(optionsNotebook, -1)
         else:
             for optionsPanelClass in optionsPanelClasses:
                 optionsPanel = optionsPanelClass(optionsNotebook, -1)
@@ -1525,7 +1579,7 @@ class GeneralOptionsPanel(wx.Panel):
     def __init__(self, parent, id):
         """
         Initializes the panel by adding an "Options" folder tab to the parent notebook and
     def __init__(self, parent, id):
         """
         Initializes the panel by adding an "Options" folder tab to the parent notebook and
-        populating the panel with the generic properties of a pydocview application. 
+        populating the panel with the generic properties of a pydocview application.
         """
         wx.Panel.__init__(self, parent, id)
         SPACE = 10
         """
         wx.Panel.__init__(self, parent, id)
         SPACE = 10
@@ -1544,7 +1598,7 @@ class GeneralOptionsPanel(wx.Panel):
             choices.append(self._mdiChoice)
             if wx.Platform == "__WXMSW__":
                 choices.append(self._winMdiChoice)
             choices.append(self._mdiChoice)
             if wx.Platform == "__WXMSW__":
                 choices.append(self._winMdiChoice)
-            self._documentRadioBox = wx.RadioBox(self, -1, _("Document Interface"),
+            self._documentRadioBox = wx.RadioBox(self, -1, _("Document Display Style"),
                                           choices = choices,
                                           majorDimension=1,
                                           )
                                           choices = choices,
                                           majorDimension=1,
                                           )
@@ -1574,7 +1628,7 @@ class GeneralOptionsPanel(wx.Panel):
         self.SetSizer(optionsBorderSizer)
         self.Layout()
         self._documentInterfaceMessageShown = False
         self.SetSizer(optionsBorderSizer)
         self.Layout()
         self._documentInterfaceMessageShown = False
-        parent.AddPage(self, _("Options"))
+        parent.AddPage(self, _("General"))
 
 
     def _AllowModeChanges(self):
 
 
     def _AllowModeChanges(self):
@@ -1619,7 +1673,7 @@ class DocApp(wx.PySimpleApp):
             self._debug = False
         if not hasattr(self, "_singleInstance"):  # only set if not already initialized
             self._singleInstance = True
             self._debug = False
         if not hasattr(self, "_singleInstance"):  # only set if not already initialized
             self._singleInstance = True
-            
+
         # if _singleInstance is TRUE only allow one single instance of app to run.
         # When user tries to run a second instance of the app, abort startup,
         # But if user also specifies files to open in command line, send message to running app to open those files
         # if _singleInstance is TRUE only allow one single instance of app to run.
         # When user tries to run a second instance of the app, abort startup,
         # But if user also specifies files to open in command line, send message to running app to open those files
@@ -1655,14 +1709,14 @@ class DocApp(wx.PySimpleApp):
                         break
                     else:
                         time.sleep(1)  # give enough time for buffer to be available
                         break
                     else:
                         time.sleep(1)  # give enough time for buffer to be available
-                        
+
                 return False
             else:
                 self._timer = wx.PyTimer(self.DoBackgroundListenAndLoad)
                 self._timer.Start(250)
 
         return True
                 return False
             else:
                 self._timer = wx.PyTimer(self.DoBackgroundListenAndLoad)
                 self._timer.Start(250)
 
         return True
-        
+
 
     def OpenMainFrame(self):
         docManager = self.GetDocumentManager()
 
     def OpenMainFrame(self):
         docManager = self.GetDocumentManager()
@@ -1670,19 +1724,19 @@ class DocApp(wx.PySimpleApp):
             if self.GetUseTabbedMDI():
                 frame = wx.lib.pydocview.DocTabbedParentFrame(docManager, None, -1, self.GetAppName())
             else:
             if self.GetUseTabbedMDI():
                 frame = wx.lib.pydocview.DocTabbedParentFrame(docManager, None, -1, self.GetAppName())
             else:
-                frame = wx.lib.pydocview.DocMDIParentFrame(docManager, None, -1, self.GetAppName())                
+                frame = wx.lib.pydocview.DocMDIParentFrame(docManager, None, -1, self.GetAppName())
             frame.Show(True)
 
     def MacOpenFile(self, filename):
         self.GetDocumentManager().CreateDocument(os.path.normpath(filename), wx.lib.docview.DOC_SILENT)
             frame.Show(True)
 
     def MacOpenFile(self, filename):
         self.GetDocumentManager().CreateDocument(os.path.normpath(filename), wx.lib.docview.DOC_SILENT)
-            
+
         # force display of running app
         topWindow = wx.GetApp().GetTopWindow()
         if topWindow.IsIconized():
             topWindow.Iconize(False)
         else:
             topWindow.Raise()
         # force display of running app
         topWindow = wx.GetApp().GetTopWindow()
         if topWindow.IsIconized():
             topWindow.Iconize(False)
         else:
             topWindow.Raise()
-                
+
     def DoBackgroundListenAndLoad(self):
         """
         Open any files specified in the given command line argument passed in via shared memory
     def DoBackgroundListenAndLoad(self):
         """
         Open any files specified in the given command line argument passed in via shared memory
@@ -1699,15 +1753,15 @@ class DocApp(wx.PySimpleApp):
             for arg in args:
                 if (wx.Platform != "__WXMSW__" or arg[0] != "/") and arg[0] != '-' and os.path.exists(arg):
                     self.GetDocumentManager().CreateDocument(os.path.normpath(arg), wx.lib.docview.DOC_SILENT)
             for arg in args:
                 if (wx.Platform != "__WXMSW__" or arg[0] != "/") and arg[0] != '-' and os.path.exists(arg):
                     self.GetDocumentManager().CreateDocument(os.path.normpath(arg), wx.lib.docview.DOC_SILENT)
-            
+
             # force display of running app
             topWindow = wx.GetApp().GetTopWindow()
             if topWindow.IsIconized():
                 topWindow.Iconize(False)
             else:
                 topWindow.Raise()
             # force display of running app
             topWindow = wx.GetApp().GetTopWindow()
             if topWindow.IsIconized():
                 topWindow.Iconize(False)
             else:
                 topWindow.Raise()
-            
-        
+
+
         self._timer.Start(1000) # 1 second interval
 
 
         self._timer.Start(1000) # 1 second interval
 
 
@@ -1742,7 +1796,7 @@ class DocApp(wx.PySimpleApp):
     def ProcessEventBeforeWindows(self, event):
         """
         Enables services to process an event before the main window has a chance to
     def ProcessEventBeforeWindows(self, event):
         """
         Enables services to process an event before the main window has a chance to
-        process the window. 
+        process the window.
         """
         for service in self._services:
             if service.ProcessEventBeforeWindows(event):
         """
         for service in self._services:
             if service.ProcessEventBeforeWindows(event):
@@ -1824,11 +1878,11 @@ class DocApp(wx.PySimpleApp):
             service.OnExit()
         config = wx.ConfigBase_Get()
         self._docManager.FileHistorySave(config)
             service.OnExit()
         config = wx.ConfigBase_Get()
         self._docManager.FileHistorySave(config)
-        
+
         if hasattr(self, "_singleInstanceChecker"):
             del self._singleInstanceChecker
 
         if hasattr(self, "_singleInstanceChecker"):
             del self._singleInstanceChecker
 
-    
+
     def GetDefaultDocManagerFlags(self):
         """
         Returns the default flags to use when creating the DocManager.
     def GetDefaultDocManagerFlags(self):
         """
         Returns the default flags to use when creating the DocManager.
@@ -1875,7 +1929,7 @@ class DocApp(wx.PySimpleApp):
         Returns True if Windows MDI should use folder tabs instead of child windows.
         """
         return self._useTabbedMDI
         Returns True if Windows MDI should use folder tabs instead of child windows.
         """
         return self._useTabbedMDI
-        
+
 
     def SetUseTabbedMDI(self, useTabbedMDI):
         """
 
     def SetUseTabbedMDI(self, useTabbedMDI):
         """
@@ -1886,7 +1940,7 @@ class DocApp(wx.PySimpleApp):
 
     def CreateDocumentFrame(self, view, doc, flags, id = -1, title = "", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE):
         """
 
     def CreateDocumentFrame(self, view, doc, flags, id = -1, title = "", pos = wx.DefaultPosition, size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE):
         """
-        Called by the DocManager to create and return a new Frame for a Document. 
+        Called by the DocManager to create and return a new Frame for a Document.
         Chooses whether to create an MDIChildFrame or SDI Frame based on the
         DocManager's flags.
         """
         Chooses whether to create an MDIChildFrame or SDI Frame based on the
         DocManager's flags.
         """
@@ -2057,7 +2111,7 @@ class DocApp(wx.PySimpleApp):
         """
         Creates a child window of a document that edits an object.  The child window
         is managed by the parent document frame, so it will be prompted to close if its
         """
         Creates a child window of a document that edits an object.  The child window
         is managed by the parent document frame, so it will be prompted to close if its
-        parent is closed, etc.  Child Documents are useful when there are complicated 
+        parent is closed, etc.  Child Documents are useful when there are complicated
         Views of a Document and users will need to tunnel into the View.
         """
         for document in self.GetDocumentManager().GetDocuments()[:]:  # Cloning list to make sure we go through all docs even as they are deleted
         Views of a Document and users will need to tunnel into the View.
         """
         for document in self.GetDocumentManager().GetDocuments()[:]:  # Cloning list to make sure we go through all docs even as they are deleted
@@ -2117,7 +2171,7 @@ class DocApp(wx.PySimpleApp):
             splash_bmp = image
         else:
             splash_bmp = wx.Image(image).ConvertToBitmap()
             splash_bmp = image
         else:
             splash_bmp = wx.Image(image).ConvertToBitmap()
-        self._splash = wx.SplashScreen(splash_bmp, wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_NO_TIMEOUT, 0, None, -1, style=wx.SIMPLE_BORDER|wx.FRAME_NO_TASKBAR) 
+        self._splash = wx.SplashScreen(splash_bmp, wx.SPLASH_CENTRE_ON_SCREEN|wx.SPLASH_NO_TIMEOUT, 0, None, -1, style=wx.SIMPLE_BORDER|wx.FRAME_NO_TASKBAR)
         self._splash.Show()
 
 
         self._splash.Show()
 
 
@@ -2127,8 +2181,8 @@ class DocApp(wx.PySimpleApp):
         """
         if self._splash:
             self._splash.Close(True)
         """
         if self._splash:
             self._splash.Close(True)
-        
-    
+
+
 class _DocFrameFileDropTarget(wx.FileDropTarget):
     """
     Class used to handle drops into the document frame.
 class _DocFrameFileDropTarget(wx.FileDropTarget):
     """
     Class used to handle drops into the document frame.
@@ -2155,7 +2209,7 @@ class _DocFrameFileDropTarget(wx.FileDropTarget):
             msgTitle = wx.GetApp().GetAppName()
             if not msgTitle:
                 msgTitle = _("File Error")
             msgTitle = wx.GetApp().GetAppName()
             if not msgTitle:
                 msgTitle = _("File Error")
-            wx.MessageBox("Could not open '%s'.  '%s'" % (docview.FileNameFromPath(file), sys.exc_value),
+            wx.MessageBox("Could not open '%s'.  '%s'" % (wx.lib.docview.FileNameFromPath(file), sys.exc_value),
                           msgTitle,
                           wx.OK | wx.ICON_EXCLAMATION,
                           self._docManager.FindSuitableParent())
                           msgTitle,
                           wx.OK | wx.ICON_EXCLAMATION,
                           self._docManager.FindSuitableParent())
@@ -2167,9 +2221,9 @@ class DocMDIParentFrame(wx.lib.docview.DocMDIParentFrame, DocFrameMixIn, DocMDIP
     features such as a default menubar, toolbar, and status bar, and a mechanism to manage embedded windows
     on the edges of the DocMDIParentFrame.
     """
     features such as a default menubar, toolbar, and status bar, and a mechanism to manage embedded windows
     on the edges of the DocMDIParentFrame.
     """
-    
 
 
-    def __init__(self, docManager, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, name="DocMDIFrame", embeddedWindows=0):
+
+    def __init__(self, docManager, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, name="DocMDIFrame", embeddedWindows=0, minSize=20):
         """
         Initializes the DocMDIParentFrame with the default menubar, toolbar, and status bar.  Use the
         optional embeddedWindows parameter with the embedded window constants to create embedded
         """
         Initializes the DocMDIParentFrame with the default menubar, toolbar, and status bar.  Use the
         optional embeddedWindows parameter with the embedded window constants to create embedded
@@ -2177,7 +2231,7 @@ class DocMDIParentFrame(wx.lib.docview.DocMDIParentFrame, DocFrameMixIn, DocMDIP
         """
         pos, size = self._GetPosSizeFromConfig(pos, size)
         wx.lib.docview.DocMDIParentFrame.__init__(self, docManager, parent, id, title, pos, size, style, name)
         """
         pos, size = self._GetPosSizeFromConfig(pos, size)
         wx.lib.docview.DocMDIParentFrame.__init__(self, docManager, parent, id, title, pos, size, style, name)
-        self._InitFrame(embeddedWindows)
+        self._InitFrame(embeddedWindows, minSize)
 
 
     def _LayoutFrame(self):
 
 
     def _LayoutFrame(self):
@@ -2186,7 +2240,7 @@ class DocMDIParentFrame(wx.lib.docview.DocMDIParentFrame, DocFrameMixIn, DocMDIP
         """
         wx.LayoutAlgorithm().LayoutMDIFrame(self)
         self.GetClientWindow().Refresh()
         """
         wx.LayoutAlgorithm().LayoutMDIFrame(self)
         self.GetClientWindow().Refresh()
-        
+
 
     def ProcessEvent(self, event):
         """
 
     def ProcessEvent(self, event):
         """
@@ -2239,7 +2293,7 @@ class DocMDIParentFrame(wx.lib.docview.DocMDIParentFrame, DocFrameMixIn, DocMDIP
                 wx.IDM_WINDOWPREV = 4006  # wxBug: Not defined for some reason
                 windowMenu.Enable(wx.IDM_WINDOWPREV, has2OrMoreWindows)
                 windowMenu.Enable(wx.IDM_WINDOWNEXT, has2OrMoreWindows)
                 wx.IDM_WINDOWPREV = 4006  # wxBug: Not defined for some reason
                 windowMenu.Enable(wx.IDM_WINDOWPREV, has2OrMoreWindows)
                 windowMenu.Enable(wx.IDM_WINDOWNEXT, has2OrMoreWindows)
-                
+
 
 
     def OnSize(self, event):
 
 
     def OnSize(self, event):
@@ -2272,7 +2326,7 @@ class DocSDIFrame(wx.lib.docview.DocChildFrame, DocFrameMixIn):
     The DocSDIFrame host DocManager Document windows.  It offers features such as a default menubar,
     toolbar, and status bar.
     """
     The DocSDIFrame host DocManager Document windows.  It offers features such as a default menubar,
     toolbar, and status bar.
     """
-    
+
 
     def __init__(self, doc, view, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, name="DocSDIFrame"):
         """
 
     def __init__(self, doc, view, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE, name="DocSDIFrame"):
         """
@@ -2308,7 +2362,7 @@ class DocSDIFrame(wx.lib.docview.DocChildFrame, DocFrameMixIn):
         Lays out the Frame.
         """
         self.Layout()
         Lays out the Frame.
         """
         self.Layout()
-        
+
 
     def OnExit(self, event):
         """
 
     def OnExit(self, event):
         """
@@ -2403,7 +2457,7 @@ class DocSDIFrame(wx.lib.docview.DocChildFrame, DocFrameMixIn):
                 if doc.IsModified():
                     filesModified = True
                     break
                 if doc.IsModified():
                     filesModified = True
                     break
-                
+
             event.Enable(filesModified)
             return True
         else:
             event.Enable(filesModified)
             return True
         else:
@@ -2436,7 +2490,7 @@ class AboutService(DocService):
         else:
             self._dlg = AboutDialog  # use default AboutDialog
             self._image = image
         else:
             self._dlg = AboutDialog  # use default AboutDialog
             self._image = image
-        
+
 
     def ShowAbout(self):
         """
 
     def ShowAbout(self):
         """
@@ -2462,7 +2516,7 @@ class AboutDialog(wx.Dialog):
     """
     Opens an AboutDialog.  Shared by DocMDIParentFrame and DocSDIFrame.
     """
     """
     Opens an AboutDialog.  Shared by DocMDIParentFrame and DocSDIFrame.
     """
-    
+
     def __init__(self, parent, image=None):
         """
         Initializes the about dialog.
     def __init__(self, parent, image=None):
         """
         Initializes the about dialog.
@@ -2474,13 +2528,13 @@ class AboutDialog(wx.Dialog):
             imageItem = wx.StaticBitmap(self, -1, image.ConvertToBitmap(), (0,0), (image.GetWidth(), image.GetHeight()))
             sizer.Add(imageItem, 0, wx.ALIGN_CENTER|wx.ALL, 0)
         sizer.Add(wx.StaticText(self, -1, wx.GetApp().GetAppName()), 0, wx.ALIGN_CENTRE|wx.ALL, 5)
             imageItem = wx.StaticBitmap(self, -1, image.ConvertToBitmap(), (0,0), (image.GetWidth(), image.GetHeight()))
             sizer.Add(imageItem, 0, wx.ALIGN_CENTER|wx.ALL, 0)
         sizer.Add(wx.StaticText(self, -1, wx.GetApp().GetAppName()), 0, wx.ALIGN_CENTRE|wx.ALL, 5)
-    
+
         btn = wx.Button(self, wx.ID_OK)
         sizer.Add(btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
         btn = wx.Button(self, wx.ID_OK)
         sizer.Add(btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
-    
+
         self.SetSizer(sizer)
         sizer.Fit(self)
         self.SetSizer(sizer)
         sizer.Fit(self)
-    
+
 
 
 class FilePropertiesService(DocService):
 
 
 class FilePropertiesService(DocService):
@@ -2680,7 +2734,7 @@ class ChildDocument(wx.lib.docview.Document):
     """
     A ChildDocument is a document that represents a portion of a Document.  The child
     document is managed by the parent document, so it will be prompted to close if its
     """
     A ChildDocument is a document that represents a portion of a Document.  The child
     document is managed by the parent document, so it will be prompted to close if its
-    parent is closed, etc.  Child Documents are useful when there are complicated 
+    parent is closed, etc.  Child Documents are useful when there are complicated
     Views of a Document and users will need to tunnel into the View.
     """
 
     Views of a Document and users will need to tunnel into the View.
     """
 
@@ -2709,7 +2763,7 @@ class ChildDocument(wx.lib.docview.Document):
     def SetParentDocument(self, parentDocument):
         """
         Sets the parent Document of the ChildDocument.
     def SetParentDocument(self, parentDocument):
         """
         Sets the parent Document of the ChildDocument.
-        """        
+        """
         self._parentDocument = parentDocument
 
 
         self._parentDocument = parentDocument
 
 
@@ -2749,7 +2803,7 @@ class ChildDocument(wx.lib.docview.Document):
         Called when the ChildDocument is saved and does the minimum such that the
         ChildDocument looks like a real Document to the framework.
         """
         Called when the ChildDocument is saved and does the minimum such that the
         ChildDocument looks like a real Document to the framework.
         """
-        return self.OnSaveDocument(self._documentFile)        
+        return self.OnSaveDocument(self._documentFile)
 
 
 class ChildDocTemplate(wx.lib.docview.DocTemplate):
 
 
 class ChildDocTemplate(wx.lib.docview.DocTemplate):
@@ -2758,7 +2812,7 @@ class ChildDocTemplate(wx.lib.docview.DocTemplate):
     that represents a portion of a Document.  The child document is managed by the parent document,
     so it will be prompted to close if its parent is closed, etc.  Child Documents are useful
     when there are complicated  Views of a Document and users will need to tunnel into the View.
     that represents a portion of a Document.  The child document is managed by the parent document,
     so it will be prompted to close if its parent is closed, etc.  Child Documents are useful
     when there are complicated  Views of a Document and users will need to tunnel into the View.
-    """    
+    """
 
 
     def __init__(self, manager, description, filter, dir, ext, docTypeName, viewTypeName, docType, viewType, flags=wx.lib.docview.TEMPLATE_INVISIBLE, icon=None):
 
 
     def __init__(self, manager, description, filter, dir, ext, docTypeName, viewTypeName, docType, viewType, flags=wx.lib.docview.TEMPLATE_INVISIBLE, icon=None):
@@ -2794,23 +2848,24 @@ class WindowMenuService(DocService):
     by the DocSDIFrame.  The MDIFrame automatically includes a Window menu and does not use
     the WindowMenuService.
     """
     by the DocSDIFrame.  The MDIFrame automatically includes a Window menu and does not use
     the WindowMenuService.
     """
-    
+
+    #----------------------------------------------------------------------------
+    # Constants
+    #----------------------------------------------------------------------------
+    ARRANGE_WINDOWS_ID = wx.NewId()
+    SELECT_MORE_WINDOWS_ID = wx.NewId()
+    SELECT_NEXT_WINDOW_ID = wx.NewId()
+    SELECT_PREV_WINDOW_ID = wx.NewId()
+    CLOSE_CURRENT_WINDOW_ID = wx.NewId()
+
 
     def __init__(self):
         """
         Initializes the WindowMenu and its globals.
         """
 
     def __init__(self):
         """
         Initializes the WindowMenu and its globals.
         """
-        self.ARRANGE_WINDOWS_ID = wx.NewId()
-        self.SELECT_WINDOW_1_ID = wx.NewId()
-        self.SELECT_WINDOW_2_ID = wx.NewId()
-        self.SELECT_WINDOW_3_ID = wx.NewId()
-        self.SELECT_WINDOW_4_ID = wx.NewId()
-        self.SELECT_WINDOW_5_ID = wx.NewId()
-        self.SELECT_WINDOW_6_ID = wx.NewId()
-        self.SELECT_WINDOW_7_ID = wx.NewId()
-        self.SELECT_WINDOW_8_ID = wx.NewId()
-        self.SELECT_WINDOW_9_ID = wx.NewId()
-        self.SELECT_MORE_WINDOWS_ID = wx.NewId()
+        self._selectWinIds = []
+        for i in range(0, 9):
+            self._selectWinIds.append(wx.NewId())
 
 
     def InstallControls(self, frame, menuBar=None, toolBar=None, statusBar=None, document=None):
 
 
     def InstallControls(self, frame, menuBar=None, toolBar=None, statusBar=None, document=None):
@@ -2818,35 +2873,56 @@ class WindowMenuService(DocService):
         Installs the Window menu.
         """
 
         Installs the Window menu.
         """
 
-        if not self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
-            return  # Only need windows menu for SDI mode, MDI frame automatically creates one
-
-        if not _WINDOWS:  # Arrange All and window navigation doesn't work on Linux
-            return
+        windowMenu = None
+        if hasattr(frame, "GetWindowMenu"):
+            windowMenu = frame.GetWindowMenu()
+        if not windowMenu:
+            needWindowMenu = True
+            windowMenu = wx.Menu()
+        else:
+            needWindowMenu = False
             
             
-        windowMenu = wx.Menu()
-        item = windowMenu.Append(self.ARRANGE_WINDOWS_ID, _("&Arrange All"), _("Arrange the open windows"))
-        windowMenu.AppendSeparator()
-
-        wx.EVT_MENU(frame, self.ARRANGE_WINDOWS_ID, frame.ProcessEvent)
-        wx.EVT_UPDATE_UI(frame, self.ARRANGE_WINDOWS_ID, frame.ProcessUpdateUIEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_1_ID, frame.ProcessEvent)  # wxNewId may have been nonsequential, so can't use EVT_MENU_RANGE
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_2_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_3_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_4_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_5_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_6_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_7_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_8_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_WINDOW_9_ID, frame.ProcessEvent)
-        wx.EVT_MENU(frame, self.SELECT_MORE_WINDOWS_ID, frame.ProcessEvent)
-
-        helpMenuIndex = menuBar.FindMenu(_("&Help"))
-        menuBar.Insert(helpMenuIndex, windowMenu, _("&Window"))
+        if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
+            if not _WINDOWS:  # Arrange All and window navigation doesn't work on Linux
+                return
+                
+            item = windowMenu.Append(self.ARRANGE_WINDOWS_ID, _("&Arrange All"), _("Arrange the open windows"))
+            wx.EVT_MENU(frame, self.ARRANGE_WINDOWS_ID, frame.ProcessEvent)
+            wx.EVT_UPDATE_UI(frame, self.ARRANGE_WINDOWS_ID, frame.ProcessUpdateUIEvent)
+            windowMenu.AppendSeparator()
+
+            for i, id in enumerate(self._selectWinIds):
+                wx.EVT_MENU(frame, id, frame.ProcessEvent)
+            wx.EVT_MENU(frame, self.SELECT_MORE_WINDOWS_ID, frame.ProcessEvent)
+        elif wx.GetApp().GetUseTabbedMDI():
+            item = windowMenu.Append(self.SELECT_PREV_WINDOW_ID, _("Previous"), _("Previous Tab"))
+            wx.EVT_MENU(frame, self.SELECT_PREV_WINDOW_ID, frame.ProcessEvent)
+            wx.EVT_UPDATE_UI(frame, self.SELECT_PREV_WINDOW_ID, frame.ProcessUpdateUIEvent)
+            item = windowMenu.Append(self.SELECT_NEXT_WINDOW_ID, _("Next"), _("Next Tab"))
+            wx.EVT_MENU(frame, self.SELECT_NEXT_WINDOW_ID, frame.ProcessEvent)
+            wx.EVT_UPDATE_UI(frame, self.SELECT_NEXT_WINDOW_ID, frame.ProcessUpdateUIEvent)
+            item = windowMenu.Append(self.CLOSE_CURRENT_WINDOW_ID, _("Close Current\tCtrl+F4"), _("Close Current Tab"))
+            wx.EVT_MENU(frame, self.CLOSE_CURRENT_WINDOW_ID, frame.ProcessEvent)
+            wx.EVT_UPDATE_UI(frame, self.CLOSE_CURRENT_WINDOW_ID, frame.ProcessUpdateUIEvent)
+            self._sep = None
+
+            for i, id in enumerate(self._selectWinIds):
+                wx.EVT_MENU(frame, id, self.OnCtrlKeySelect)
+
+        if needWindowMenu:
+            helpMenuIndex = menuBar.FindMenu(_("&Help"))
+            menuBar.Insert(helpMenuIndex, windowMenu, _("&Window"))
 
         self._lastFrameUpdated = None
 
 
 
         self._lastFrameUpdated = None
 
 
+    def OnCtrlKeySelect(self, event):
+        i = self._selectWinIds.index(event.GetId())
+        notebook = wx.GetApp().GetTopWindow()._notebook
+        if i < notebook.GetPageCount():
+            notebook.SetSelection(i)
+
+
     def ProcessEvent(self, event):
         """
         Processes a Window menu event.
     def ProcessEvent(self, event):
         """
         Processes a Window menu event.
@@ -2858,9 +2934,27 @@ class WindowMenuService(DocService):
         elif id == self.SELECT_MORE_WINDOWS_ID:
             self.OnSelectMoreWindows(event)
             return True
         elif id == self.SELECT_MORE_WINDOWS_ID:
             self.OnSelectMoreWindows(event)
             return True
-        elif id == self.SELECT_WINDOW_1_ID or id == self.SELECT_WINDOW_2_ID or id == self.SELECT_WINDOW_3_ID or id == self.SELECT_WINDOW_4_ID or id == self.SELECT_WINDOW_5_ID or id == self.SELECT_WINDOW_6_ID or id == self.SELECT_WINDOW_7_ID or id == self.SELECT_WINDOW_8_ID or id == self.SELECT_WINDOW_9_ID:
+        elif id in self._selectWinIds:
             self.OnSelectWindowMenu(event)
             return True
             self.OnSelectWindowMenu(event)
             return True
+        elif wx.GetApp().GetUseTabbedMDI():
+            if id == self.SELECT_NEXT_WINDOW_ID:
+                notebook = wx.GetApp().GetTopWindow()._notebook
+                i = notebook.GetSelection()
+                notebook.SetSelection(i+1)
+                return True
+            elif id == self.SELECT_PREV_WINDOW_ID:
+                notebook = wx.GetApp().GetTopWindow()._notebook
+                i = notebook.GetSelection()
+                notebook.SetSelection(i-1)
+                return True
+            elif id == self.CLOSE_CURRENT_WINDOW_ID:
+                notebook = wx.GetApp().GetTopWindow()._notebook
+                i = notebook.GetSelection()
+                if i != -1:
+                    doc = notebook.GetPage(i).GetView().GetDocument()
+                    wx.GetApp().GetDocumentManager().CloseDocument(doc, False)
+                return True
         else:
             return False
 
         else:
             return False
 
@@ -2876,6 +2970,38 @@ class WindowMenuService(DocService):
                 self.BuildWindowMenu(frame)  # It's a new frame, so update the windows menu... this is as if the View::OnActivateMethod had been invoked
                 self._lastFrameUpdated = frame
             return True
                 self.BuildWindowMenu(frame)  # It's a new frame, so update the windows menu... this is as if the View::OnActivateMethod had been invoked
                 self._lastFrameUpdated = frame
             return True
+        elif wx.GetApp().GetUseTabbedMDI():
+            if id == self.SELECT_NEXT_WINDOW_ID:
+                self.BuildWindowMenu(event.GetEventObject())  # build file list only when we are updating the windows menu
+                
+                notebook = wx.GetApp().GetTopWindow()._notebook
+                i = notebook.GetSelection()
+                if i == -1:
+                    event.Enable(False)
+                    return True
+                i += 1
+                if i >= notebook.GetPageCount():
+                    event.Enable(False)
+                    return True
+                event.Enable(True)
+                return True
+            elif id == self.SELECT_PREV_WINDOW_ID:
+                notebook = wx.GetApp().GetTopWindow()._notebook
+                i = notebook.GetSelection()
+                if i == -1:
+                    event.Enable(False)
+                    return True
+                i -= 1
+                if i < 0:
+                    event.Enable(False)
+                    return True
+                event.Enable(True)
+                return True
+            elif id == self.CLOSE_CURRENT_WINDOW_ID:
+                event.Enable(wx.GetApp().GetTopWindow()._notebook.GetSelection() != -1)
+                return True
+
+            return False
         else:
             return False
 
         else:
             return False
 
@@ -2884,42 +3010,70 @@ class WindowMenuService(DocService):
         """
         Builds the Window menu and adds menu items for all of the open documents in the DocManager.
         """
         """
         Builds the Window menu and adds menu items for all of the open documents in the DocManager.
         """
+        if wx.GetApp().GetUseTabbedMDI():
+            currentFrame = wx.GetApp().GetTopWindow()
+
         windowMenuIndex = currentFrame.GetMenuBar().FindMenu(_("&Window"))
         windowMenu = currentFrame.GetMenuBar().GetMenu(windowMenuIndex)
         windowMenuIndex = currentFrame.GetMenuBar().FindMenu(_("&Window"))
         windowMenu = currentFrame.GetMenuBar().GetMenu(windowMenuIndex)
-        ids = self._GetWindowMenuIDList()
-        frames = self._GetWindowMenuFrameList(currentFrame)
-        max = WINDOW_MENU_NUM_ITEMS
-        if max > len(frames):
-            max = len(frames)
-        i = 0
-        for i in range(0, max):
-            frame = frames[i]
-            item = windowMenu.FindItemById(ids[i])
-            label = '&' + str(i + 1) + ' ' + frame.GetTitle()
-            if not item:
-                item = windowMenu.AppendCheckItem(ids[i], label)
-            else:
-                windowMenu.SetLabel(ids[i], label)
-            windowMenu.Check(ids[i], (frame == currentFrame))
-        if len(frames) > WINDOW_MENU_NUM_ITEMS:  # Add the more items item
-            if not windowMenu.FindItemById(self.SELECT_MORE_WINDOWS_ID):
-                windowMenu.Append(self.SELECT_MORE_WINDOWS_ID, _("&More Windows..."))
-        else:  # Remove any extra items
-            if windowMenu.FindItemById(self.SELECT_MORE_WINDOWS_ID):
-                windowMenu.Remove(self.SELECT_MORE_WINDOWS_ID)
-
-
 
 
-            for j in range(i + 1, WINDOW_MENU_NUM_ITEMS):
-                if windowMenu.FindItemById(ids[j]):
-                    windowMenu.Remove(ids[j])
+        if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
+            frames = self._GetWindowMenuFrameList(currentFrame)
+            max = WINDOW_MENU_NUM_ITEMS
+            if max > len(frames):
+                max = len(frames)
+            i = 0
+            for i in range(0, max):
+                frame = frames[i]
+                item = windowMenu.FindItemById(self._selectWinIds[i])
+                label = '&' + str(i + 1) + ' ' + frame.GetTitle()
+                if not item:
+                    item = windowMenu.AppendCheckItem(self._selectWinIds[i], label)
+                else:
+                    windowMenu.SetLabel(self._selectWinIds[i], label)
+                windowMenu.Check(self._selectWinIds[i], (frame == currentFrame))
+            if len(frames) > WINDOW_MENU_NUM_ITEMS:  # Add the more items item
+                if not windowMenu.FindItemById(self.SELECT_MORE_WINDOWS_ID):
+                    windowMenu.Append(self.SELECT_MORE_WINDOWS_ID, _("&More Windows..."))
+            else:  # Remove any extra items
+                if windowMenu.FindItemById(self.SELECT_MORE_WINDOWS_ID):
+                    windowMenu.Remove(self.SELECT_MORE_WINDOWS_ID)
+
+                for j in range(i + 1, WINDOW_MENU_NUM_ITEMS):
+                    if windowMenu.FindItemById(self._selectWinIds[j]):
+                        windowMenu.Remove(self._selectWinIds[j])
+
+        elif wx.GetApp().GetUseTabbedMDI():
+            notebook = wx.GetApp().GetTopWindow()._notebook
+            numPages = notebook.GetPageCount()
+
+            for id in self._selectWinIds:
+                item = windowMenu.FindItemById(id)
+                if item:
+                    windowMenu.DeleteItem(item)
+            if numPages == 0 and self._sep:
+                windowMenu.DeleteItem(self._sep)
+                self._sep = None
+
+            if numPages > len(self._selectWinIds):
+                for i in range(len(self._selectWinIds), numPages):
+                    self._selectWinIds.append(wx.NewId())
+                    wx.EVT_MENU(currentFrame, self._selectWinIds[i], self.OnCtrlKeySelect)                    
+            
+            for i in range(0, numPages):
+                if i == 0 and not self._sep:
+                    self._sep = windowMenu.AppendSeparator()
+                if i < 9:
+                    menuLabel = "%s\tCtrl+%s" % (notebook.GetPageText(i), i+1)
+                else:
+                    menuLabel = notebook.GetPageText(i)
+                windowMenu.Append(self._selectWinIds[i], menuLabel)
 
 
     def _GetWindowMenuIDList(self):
         """
         Returns a list of the Window menu item IDs.
         """
 
 
     def _GetWindowMenuIDList(self):
         """
         Returns a list of the Window menu item IDs.
         """
-        return [self.SELECT_WINDOW_1_ID, self.SELECT_WINDOW_2_ID, self.SELECT_WINDOW_3_ID, self.SELECT_WINDOW_4_ID, self.SELECT_WINDOW_5_ID, self.SELECT_WINDOW_6_ID, self.SELECT_WINDOW_7_ID, self.SELECT_WINDOW_8_ID, self.SELECT_WINDOW_9_ID]
+        return self._selectWinIds
 
 
     def _GetWindowMenuFrameList(self, currentFrame=None):
 
 
     def _GetWindowMenuFrameList(self, currentFrame=None):
@@ -2988,7 +3142,7 @@ class WindowMenuService(DocService):
         Frame to the front of the desktop.
         """
         id = event.GetId()
         Frame to the front of the desktop.
         """
         id = event.GetId()
-        index = self._GetWindowMenuIDList().index(id)
+        index = self._selectWinIds.index(id)
         if index > -1:
             currentFrame = event.GetEventObject()
             frame = self._GetWindowMenuFrameList(currentFrame)[index]
         if index > -1:
             currentFrame = event.GetEventObject()
             frame = self._GetWindowMenuFrameList(currentFrame)[index]
@@ -3036,7 +3190,7 @@ def getNewData():
 \x902\xb2@\xc8\xc2\x8b\xd9\xbcX\xc0\x045\xac\xc1 Jg\xe6\x08\xe8)\xa7o\xd5\
 \xb0\xbf\xcb\nd\x86x\x0b\x9c+p\x0b\x0c\xa9\x16~\xbc_\xeb\x9d\xd3\x03\xcb3q\
 \xefo\xbc\xfa/\x14\xd9\x19\x1f\xfb\x8aa\x87\xf2\xf7\x16\x00\x00\x00\x00IEND\
 \x902\xb2@\xc8\xc2\x8b\xd9\xbcX\xc0\x045\xac\xc1 Jg\xe6\x08\xe8)\xa7o\xd5\
 \xb0\xbf\xcb\nd\x86x\x0b\x9c+p\x0b\x0c\xa9\x16~\xbc_\xeb\x9d\xd3\x03\xcb3q\
 \xefo\xbc\xfa/\x14\xd9\x19\x1f\xfb\x8aa\x87\xf2\xf7\x16\x00\x00\x00\x00IEND\
-\xaeB`\x82" 
+\xaeB`\x82"
 
 def getNewBitmap():
     return BitmapFromImage(getNewImage())
 
 def getNewBitmap():
     return BitmapFromImage(getNewImage())
@@ -3064,7 +3218,7 @@ D\xee\xf4\x88\xb2\xfa5)\xab(\x99"\x00\xb9\x87c\x0b;\x19\xf1\x0b\x80\xb9pZ\
 \xad\xb1\xab\x99\x98bdb\xd4q\xa7\xefd\xbb\x05\xa7\xdd\x8f\x0e/\x9d\x01\x85\
 \xbc\nX+8K\\\x99\xe5\x02x\x16\xf6\xba\x02$\xc9\xe56\x1fF[\xda\x8bn\x9er\xa7\
 \x02\xc1\x90\xedoH\xed\xdf\x18\x8fE\xc5o\x0c\x8e\x80\xbf\xea\x13\xa8\x18\x89\
 \xad\xb1\xab\x99\x98bdb\xd4q\xa7\xefd\xbb\x05\xa7\xdd\x8f\x0e/\x9d\x01\x85\
 \xbc\nX+8K\\\x99\xe5\x02x\x16\xf6\xba\x02$\xc9\xe56\x1fF[\xda\x8bn\x9er\xa7\
 \x02\xc1\x90\xedoH\xed\xdf\x18\x8fE\xc5o\x0c\x8e\x80\xbf\xea\x13\xa8\x18\x89\
-5\xe7L\xb3:\x00\x00\x00\x00IEND\xaeB`\x82' 
+5\xe7L\xb3:\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getOpenBitmap():
     return BitmapFromImage(getOpenImage())
 
 def getOpenBitmap():
     return BitmapFromImage(getOpenImage())
@@ -3091,7 +3245,7 @@ def getCopyData():
 \xb9\xe7\x1fE\xae\xb7\\\xb6\x1f\xe0\x8d\x15H$\x99\x1b?\x12@\xd7\xdf\xd0\x0f\
 \nN!\x91\x98\x9e\xd8\x0c\x10\xbd>\xdeU\xeco\np\xf7\xf8\xebK\x14fvF\xc8ds\xce\
 \xff\xbd\xb6u(\xbc\x89\xbc\x17\xf6\x9f\x14E\x8d\x04\x8a\xdeDa\xcads\xca\x1f\
 \xb9\xe7\x1fE\xae\xb7\\\xb6\x1f\xe0\x8d\x15H$\x99\x1b?\x12@\xd7\xdf\xd0\x0f\
 \nN!\x91\x98\x9e\xd8\x0c\x10\xbd>\xdeU\xeco\np\xf7\xf8\xebK\x14fvF\xc8ds\xce\
 \xff\xbd\xb6u(\xbc\x89\xbc\x17\xf6\x9f\x14E\x8d\x04\x8a\xdeDa\xcads\xca\x1f\
-\x0cI\xd4\xda\x88E\x9d\xc4\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x0cI\xd4\xda\x88E\x9d\xc4\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getCopyBitmap():
     return BitmapFromImage(getCopyImage())
 
 def getCopyBitmap():
     return BitmapFromImage(getCopyImage())
@@ -3120,7 +3274,7 @@ pz\x8d\x82\x12\x0b\x82\xc1HM\xect-c\xf7\xaa!\x10\xc9\xe0]rR\xac\xb4\x01\xc8\
 \xb0\x98\x1f\x00x-\xd5\xb0\xce\xc3\xd1~LW\x98\x15\xab\xccM\x8f\xfe\xaf\x03\
 \x00w0\xccS\xfdgm\xfb\xc3\xd7\xf7++w\xd5\x16\x0f\x92\t\xe4\xe9zN\x86\xbe\xa7\
 1\xaa\xfbLY\xb1:\x10 (\xe3\x0c?\x03\xf2_\xb9W=\xc2\x17\x1c\xf8\x87\x9a\x03\
 \xb0\x98\x1f\x00x-\xd5\xb0\xce\xc3\xd1~LW\x98\x15\xab\xccM\x8f\xfe\xaf\x03\
 \x00w0\xccS\xfdgm\xfb\xc3\xd7\xf7++w\xd5\x16\x0f\x92\t\xe4\xe9zN\x86\xbe\xa7\
 1\xaa\xfbLY\xb1:\x10 (\xe3\x0c?\x03\xf2_\xb9W=\xc2\x17\x1c\xf8\x87\x9a\x03\
-\x12\xd7\xb9\x00\x00\x00\x00IEND\xaeB`\x82" 
+\x12\xd7\xb9\x00\x00\x00\x00IEND\xaeB`\x82"
 
 def getPasteBitmap():
     return BitmapFromImage(getPasteImage())
 
 def getPasteBitmap():
     return BitmapFromImage(getPasteImage())
@@ -3146,7 +3300,7 @@ def getSaveData():
 g\x1f!U\xac\xe0y^\xe62\xc6p\xd6h\x14\x8e4s\x89\xc6\xa4\xcb[\xa9V\xffG\xa0\
 \xb5\xce\x8a\x97j[\xb4\xe3\xb8\x90@)\'\xfd\xbe\xd7\xf5\xe2\x83\xeau\xec~w\'\
 \x9a\x12\x00\\6\xc3\xd2\xab,\xec`^|\x03\xb6\xdf|Q.\xa7\x15\x89\x00\x00\x00\
 g\x1f!U\xac\xe0y^\xe62\xc6p\xd6h\x14\x8e4s\x89\xc6\xa4\xcb[\xa9V\xffG\xa0\
 \xb5\xce\x8a\x97j[\xb4\xe3\xb8\x90@)\'\xfd\xbe\xd7\xf5\xe2\x83\xeau\xec~w\'\
 \x9a\x12\x00\\6\xc3\xd2\xab,\xec`^|\x03\xb6\xdf|Q.\xa7\x15\x89\x00\x00\x00\
-\x00IEND\xaeB`\x82' 
+\x00IEND\xaeB`\x82'
 
 def getSaveBitmap():
     return BitmapFromImage(getSaveImage())
 
 def getSaveBitmap():
     return BitmapFromImage(getSaveImage())
@@ -3173,7 +3327,7 @@ def getSaveAllData():
 \xa9&\xb3\x86c\xd3r![\xe47\x14 |\x14\xcf\xb7\x13JNZ7\xab\xc2\xe9\xddn7\x9e\
 \xbb>\xcb\x01\x98\xc9\xa0T\x93Y\x93\xdbH\xa2\xaa*4MC\xb5Z\xcdt \x84\x98\xfa(\
 S\xf2\xf9\xfc\xdc+0&\xc9\xa9\xc1\x86\xf3}\x1d\xbf\r\xacm\x84\xf5\xc2\x02\x00\
 \xa9&\xb3\x86c\xd3r![\xe47\x14 |\x14\xcf\xb7\x13JNZ7\xab\xc2\xe9\xddn7\x9e\
 \xbb>\xcb\x01\x98\xc9\xa0T\x93Y\x93\xdbH\xa2\xaa*4MC\xb5Z\xcdt \x84\x98\xfa(\
 S\xf2\xf9\xfc\xdc+0&\xc9\xa9\xc1\x86\xf3}\x1d\xbf\r\xacm\x84\xf5\xc2\x02\x00\
-Pw\xefR\x99d\xf1\x05z\x94\xd0b\xcb S\xf3\x00\x00\x00\x00IEND\xaeB`\x82' 
+Pw\xefR\x99d\xf1\x05z\x94\xd0b\xcb S\xf3\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getSaveAllBitmap():
     return BitmapFromImage(getSaveAllImage())
 
 def getSaveAllBitmap():
     return BitmapFromImage(getSaveAllImage())
@@ -3203,7 +3357,7 @@ def getPrintData():
 \xe95n4\xea\x01\xab\x9dN\xc7\xe3"9\x1fGr>\xeeYs\x8fr:\x9d\x06c\x0c\x86ax\nL\
 \xcb;\xbb\x1f\x84\xd0\x10*\xe5\x12WU\x15\xcd7`f\xf2\xc7z\x00\x80\xae\xeb\xc8\
 \xe5rXI\xad\x12"nc\xa5\\\xe2{G*\xba\xef\xfa\xaf\x02\xa2\xd9u \xe0?\xe7\xdfA4\
 \xe95n4\xea\x01\xab\x9dN\xc7\xe3"9\x1fGr>\xeeYs\x8fr:\x9d\x06c\x0c\x86ax\nL\
 \xcb;\xbb\x1f\x84\xd0\x10*\xe5\x12WU\x15\xcd7`f\xf2\xc7z\x00\x80\xae\xeb\xc8\
 \xe5rXI\xad\x12"nc\xa5\\\xe2{G*\xba\xef\xfa\xaf\x02\xa2\xd9u \xe0?\xe7\xdfA4\
-\x03\xc0\'\xe3\x82\xc9\x18g\x90\x8e]\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x03\xc0\'\xe3\x82\xc9\x18g\x90\x8e]\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getPrintBitmap():
     return BitmapFromImage(getPrintImage())
 
 def getPrintBitmap():
     return BitmapFromImage(getPrintImage())
@@ -3230,7 +3384,7 @@ pz\x0e\xa2~\x91\x0bx\x00-m\xe9D-W>%h\xc0\x1f_\xbf\x15\xef\xeb\x90\xaf\xc1\
 \xf2#u\xc3\xb2m`t\x00&\x07E4\xcb]x.QH\xa6\xec$\x13\xf83q^\xb44^\x8f\xb8\xa5"\
 p\x9c\x88\xa3\x91\xe1\x9d5\x00\x14Eu\xc9y\x9c\xa4\xeb\xba\xe5}\xb6\x9a\x01`\
 \xc1\x07\xf39\x97\xa2(\xaa\xab\x17+\xd5]\xe0\xf5dC\x9a\xfc\xcb\xc0\xc9\xd00\
 \xf2#u\xc3\xb2m`t\x00&\x07E4\xcb]x.QH\xa6\xec$\x13\xf83q^\xb44^\x8f\xb8\xa5"\
 p\x9c\x88\xa3\x91\xe1\x9d5\x00\x14Eu\xc9y\x9c\xa4\xeb\xba\xe5}\xb6\x9a\x01`\
 \xc1\x07\xf39\x97\xa2(\xaa\xab\x17+\xd5]\xe0\xf5dC\x9a\xfc\xcb\xc0\xc9\xd00\
-\xf9\x011\xc9\x87\xf3\xb4\xd1t\xaf\x00\x00\x00\x00IEND\xaeB`\x82' 
+\xf9\x011\xc9\x87\xf3\xb4\xd1t\xaf\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getPrintPreviewBitmap():
     return BitmapFromImage(getPrintPreviewImage())
 
 def getPrintPreviewBitmap():
     return BitmapFromImage(getPrintPreviewImage())
@@ -3257,7 +3411,7 @@ def getCutData():
 \x00,\xa6\x9f\x00\x14o+\xec\x9f\x15X\xba\x97\xf1\tTC\x1c\xfe]e\x80v\xa9\xcc\
 \xb8\xeb2\xfb\xf8\xe2\xf5\xaeA\xbbT\xd6\xea"c\x1c\xf4{r\xfbe\xf5Y?\xa7\xd5\
 \x80W\xd1w\n7k\xa3\xd4\xee\x81\x8a\x18\x16\xea8\x80_\\\xa2\x8b\x88!\xd2S\x08\
 \x00,\xa6\x9f\x00\x14o+\xec\x9f\x15X\xba\x97\xf1\tTC\x1c\xfe]e\x80v\xa9\xcc\
 \xb8\xeb2\xfb\xf8\xe2\xf5\xaeA\xbbT\xd6\xea"c\x1c\xf4{r\xfbe\xf5Y?\xa7\xd5\
 \x80W\xd1w\n7k\xa3\xd4\xee\x81\x8a\x18\x16\xea8\x80_\\\xa2\x8b\x88!\xd2S\x08\
-\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x00\x00\x00\x00IEND\xaeB`\x82'
 
 def getCutBitmap():
     return BitmapFromImage(getCutImage())
 
 def getCutBitmap():
     return BitmapFromImage(getCutImage())
@@ -3288,7 +3442,7 @@ def getUndoData():
 \x06#\x13\x0c}\x1a\x06 \xdc\xfc\xc87\xf0?\xb8\x1e\xc1\n\xa1\xac\x10Zk\xe9\
 \x18k\x95\x9fGS\xf2\xa58*\x9f7S\xd2\x92\x0c\x8b\xd6Z\xccL\xd0\xf6\x1d\xb4\
 \xd6\xd2\x92\x0c\xcb\xea\xdf\x0f\r\xc1w\x047%\x8d\xc0\x81\x02#i\x04VV\x88k\
 \x06#\x13\x0c}\x1a\x06 \xdc\xfc\xc87\xf0?\xb8\x1e\xc1\n\xa1\xac\x10Zk\xe9\
 \x18k\x95\x9fGS\xf2\xa58*\x9f7S\xd2\x92\x0c\x8b\xd6Z\xccL\xd0\xf6\x1d\xb4\
 \xd6\xd2\x92\x0c\xcb\xea\xdf\x0f\r\xc1w\x047%\x8d\xc0\x81\x02#i\x04VV\x88k\
-\x82\xbe\xde\xc2\xb0\xb2\xea\xa7\x00\x00\x00\x00IEND\xaeB`\x82" 
+\x82\xbe\xde\xc2\xb0\xb2\xea\xa7\x00\x00\x00\x00IEND\xaeB`\x82"
 
 def getUndoBitmap():
     return BitmapFromImage(getUndoImage())
 
 def getUndoBitmap():
     return BitmapFromImage(getUndoImage())
@@ -3317,7 +3471,7 @@ Eqc~S\xec\xd7\x94\x18\xaa\xafY*e^l\x10\x87\xf5\xb4,W\xb1<\x98\x16q\x98W\xa1\
 \xbd\xd1\xfe\x10=\xfc\xe8\x1eg\x91\xbc\xfc\x06\x81\xa0\xc2\xd2\x13\xa789\xbe\
 \x91\xde\xce\x14\x07\x82\nC\xaf\xeb\xd6\xe0\x9c\x93/9Lj%L\xa9\xf2\x1c\xa5\
 \xcas\xe4\r\xb9m\xaf\xf0'\xc0\x84xCnR+\xe1_\xe2\xbe\x00V\x88\xec\x9f\xf4\x05\
 \xbd\xd1\xfe\x10=\xfc\xe8\x1eg\x91\xbc\xfc\x06\x81\xa0\xc2\xd2\x13\xa789\xbe\
 \x91\xde\xce\x14\x07\x82\nC\xaf\xeb\xd6\xe0\x9c\x93/9Lj%L\xa9\xf2\x1c\xa5\
 \xcas\xe4\r\xb9m\xaf\xf0'\xc0\x84xCnR+\xe1_\xe2\xbe\x00V\x88\xec\x9f\xf4\x05\
-0!\xb2\xfc\x0f\xe0\xc4\xb6\xad\x97R\xe5z\x00\x00\x00\x00IEND\xaeB`\x82" 
+0!\xb2\xfc\x0f\xe0\xc4\xb6\xad\x97R\xe5z\x00\x00\x00\x00IEND\xaeB`\x82"
 
 def getRedoBitmap():
     return BitmapFromImage(getRedoImage())
 
 def getRedoBitmap():
     return BitmapFromImage(getRedoImage())
@@ -3325,7 +3479,7 @@ def getRedoBitmap():
 def getRedoImage():
     stream = cStringIO.StringIO(getRedoData())
     return ImageFromStream(stream)
 def getRedoImage():
     stream = cStringIO.StringIO(getRedoData())
     return ImageFromStream(stream)
-    
+
 #----------------------------------------------------------------------------
 
 def getBlankData():
 #----------------------------------------------------------------------------
 
 def getBlankData():
@@ -3336,7 +3490,7 @@ def getBlankData():
 \xa8X:\xd4\x13\x03:\x1b\x01\xa45T\xd4\xefBsh\xd7Hk\xdc\x02\x00@\x8a\x19$\xa1\
 9\x14A,\x95\xf3\x82G)\xd3\x00\xf24\xf7\x90\x1ev\x07\xee\x1e\xf4:\xc1J?\xe0\
 \x0b\x80\xc7\x1d\xf8\x1dg\xc4\xea7\x96G8\x00\xa8\x91\x19(\x85#P\x7f\x00\x00\
 \xa8X:\xd4\x13\x03:\x1b\x01\xa45T\xd4\xefBsh\xd7Hk\xdc\x02\x00@\x8a\x19$\xa1\
 9\x14A,\x95\xf3\x82G)\xd3\x00\xf24\xf7\x90\x1ev\x07\xee\x1e\xf4:\xc1J?\xe0\
 \x0b\x80\xc7\x1d\xf8\x1dg\xc4\xea7\x96G8\x00\xa8\x91\x19(\x85#P\x7f\x00\x00\
-\x00\x00IEND\xaeB`\x82' 
+\x00\x00IEND\xaeB`\x82'
 
 
 def getBlankBitmap():
 
 
 def getBlankBitmap():
@@ -3348,5 +3502,5 @@ def getBlankImage():
 
 def getBlankIcon():
     return wx.IconFromBitmap(getBlankBitmap())
 
 def getBlankIcon():
     return wx.IconFromBitmap(getBlankBitmap())
-    
+