Merged modifications from the 2.6 branch

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@36607 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
Robin Dunn
2005-12-30 23:02:03 +00:00
parent a780a8dc19
commit 02b800ce7c
104 changed files with 14102 additions and 46560 deletions

View File

@@ -11,37 +11,45 @@
#----------------------------------------------------------------------------
import wx
from IDE import ACTIVEGRID_BASE_IDE, getSplashBitmap
import os.path
from IDE import ACTIVEGRID_BASE_IDE, getSplashBitmap, getIDESplashBitmap
import activegrid.util.sysutils as sysutilslib
_ = wx.GetTranslation
#----------------------------------------------------------------------------
# Package License Data for AboutDialog
# Package, License, URL
# If no information is available, put a None as a place holder.
#
# NO GPL Allowed. Only LGPL, BSD, and Public Domain Based Licenses!
#----------------------------------------------------------------------------
licenseData = [
("ActiveGrid", "ASL 2.0", "http://apache.org/licenses/LICENSE-2.0"),
("Python 2.3", "Python Software Foundation License", "http://www.python.org/2.3/license.html"),
("wxPython 2.5", "wxWidgets 2 - LGPL", "http://wxwidgets.org/newlicen.htm"),
("wxWidgets", "wxWindows Library License 3", "http://www.wxwidgets.org/manuals/2.5.4/wx_wxlicense.html"),
licenseData = [ # add licenses for base IDE features
("ActiveGrid", "Apache License, Version 2.0", "http://apache.org/licenses/LICENSE-2.0"),
("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"),
("process.py", "See file", "http://starship.python.net/~tmick/"),
("pysvn", "Apache License", "http://pysvn.tigris.org/"),
("pysvn", "Apache License, Version 2.0", "http://pysvn.tigris.org/"),
]
if not ACTIVEGRID_BASE_IDE: # add licenses for database connections only if not the base IDE
if not ACTIVEGRID_BASE_IDE: # add licenses for non-base IDE features such as database connections
licenseData += [
("pydb2", "LGPL", "http://sourceforge.net/projects/pydb2"),
("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://http://www.computronix.com/download/License(cxOracle).txt"),
("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/"),
("Sarissa", "LGPL", "http://sourceforge.net/projects/sarissa/"),
("Dynarch DHTML Calendar", "LGPL", "http://www.dynarch.com/projects/calendar/"),
]
if wx.Platform == '__WXMSW__':
if wx.Platform == '__WXMSW__': # add Windows only licenses
licenseData += [("pywin32", "Python Software Foundation License", "http://sourceforge.net/projects/pywin32/")]
class AboutDialog(wx.Dialog):
@@ -56,10 +64,25 @@ class AboutDialog(wx.Dialog):
aboutPage = wx.Panel(nb, -1)
sizer = wx.BoxSizer(wx.VERTICAL)
splash_bmp = getSplashBitmap()
if not ACTIVEGRID_BASE_IDE:
splash_bmp = getSplashBitmap()
else:
splash_bmp = getIDESplashBitmap()
# find version number from
versionFilepath = os.path.join(sysutilslib.mainModuleDir, "version.txt")
if os.path.exists(versionFilepath):
versionfile = open(versionFilepath, 'r')
versionLines = versionfile.readlines()
versionfile.close()
version = "".join(versionLines)
else:
version = _("Version Unknown - %s not found" % versionFilepath)
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() + _("\nVersion 0.7 Early Access\n\nCopyright (c) 2003-2005 ActiveGrid Incorporated and Contributors. All rights reserved.")), 0, wx.ALIGN_LEFT|wx.ALL, 10)
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, _("http://www.activegrid.com")), 0, wx.ALIGN_LEFT|wx.LEFT|wx.BOTTOM, 10)
aboutPage.SetSizer(sizer)
nb.AddPage(aboutPage, _("Copyright"))
@@ -78,12 +101,16 @@ class AboutDialog(wx.Dialog):
maxHeight = h
grid.SetColLabelSize(maxHeight + 6) # add a 6 pixel margin
maxW = 0
for row, data in enumerate(licenseData):
package = data[0]
license = data[1]
url = data[2]
if package:
grid.SetRowLabelValue(row, package)
w, h = dc.GetTextExtent(package)
if w > maxW:
maxW = w
if license:
grid.SetCellValue(row, 0, license)
if url:
@@ -95,8 +122,9 @@ class AboutDialog(wx.Dialog):
grid.EnableDragRowSize(False)
grid.SetRowLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
grid.SetLabelBackgroundColour(wx.WHITE)
grid.AutoSizeColumn(0, 100)
grid.AutoSizeColumn(1, 100)
grid.AutoSizeColumn(0)
grid.AutoSizeColumn(1)
grid.SetRowLabelSize(maxW + 10)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(grid, 1, wx.EXPAND|wx.ALL, 10)
licensePage.SetSizer(sizer)
@@ -104,7 +132,7 @@ class AboutDialog(wx.Dialog):
creditsPage = wx.Panel(nb, -1)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(wx.StaticText(creditsPage, -1, _("ActiveGrid Development Team:\n\nLawrence Bruhmuller\nEric Chu\nMatt Fryer\nJoel Hare\nMorgan Hua\nAlan Mullendore\nJeff Norton\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\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)
creditsPage.SetSizer(sizer)
nb.AddPage(creditsPage, _("Credits"))
@@ -114,7 +142,8 @@ class AboutDialog(wx.Dialog):
sizer.Add(btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
self.SetSizer(sizer)
self.SetAutoLayout(True)
sizer.Fit(self)
self.Layout()
self.Fit()
grid.ForceRefresh() # wxBug: Get rid of unnecessary scrollbars

View File

@@ -23,6 +23,19 @@ SHAPE_BRUSH = wx.Brush("WHEAT", wx.SOLID)
LINE_BRUSH = wx.BLACK_BRUSH
INACTIVE_SELECT_BRUSH = wx.Brush("LIGHT BLUE", wx.SOLID)
NORMALFONT = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
SLANTFONT = wx.Font(NORMALFONT.GetPointSize(), NORMALFONT.GetFamily(), wx.SLANT, NORMALFONT.GetWeight())
BOLDFONT = wx.Font(NORMALFONT.GetPointSize(), NORMALFONT.GetFamily(), NORMALFONT.GetStyle(), wx.BOLD)
DEFAULT_BACKGROUND_COLOR = wx.Colour(0xEE, 0xEE, 0xEE)
HEADER_BRUSH = wx.Brush(wx.Colour(0xDB, 0xEB, 0xFF), wx.SOLID)
BODY_BRUSH = wx.Brush(wx.WHITE, wx.SOLID)
PARKING_VERTICAL = 1
PARKING_HORIZONTAL = 2
PARKING_OFFSET = 30 # space between shapes
def GetRawModel(model):
if hasattr(model, "GetRawModel"):
@@ -32,6 +45,27 @@ def GetRawModel(model):
return rawModel
def GetLabel(model):
model = GetRawModel(model)
if hasattr(model, "__xmlname__"):
label = model.__xmlname__
try:
if (len(label) > 0):
label = label[0].upper() + label[1:]
if (hasattr(model, "complexType")):
label += ': %s/%s' % (model.complexType.name, model.name)
else:
if model.name:
label += ': %s' % model.name
elif model.ref:
label += ': %s' % model.ref
except AttributeError:
pass
else:
label = str(model)
return label
class CanvasView(wx.lib.docview.View):
@@ -40,9 +74,10 @@ class CanvasView(wx.lib.docview.View):
#----------------------------------------------------------------------------
def __init__(self, brush = SHAPE_BRUSH):
def __init__(self, brush=SHAPE_BRUSH, background=DEFAULT_BACKGROUND_COLOR):
wx.lib.docview.View.__init__(self)
self._brush = brush
self._backgroundColor = background
self._canvas = None
self._pt1 = None
self._pt2 = None
@@ -68,6 +103,7 @@ class CanvasView(wx.lib.docview.View):
frame.SetSizer(sizer)
frame.Layout()
self.Activate()
wx.EVT_RIGHT_DOWN(self._canvas, self.OnRightClick)
return True
@@ -145,12 +181,20 @@ class CanvasView(wx.lib.docview.View):
self._canvas.SetScrollbars(20, 20, self._maxWidth / 20, self._maxHeight / 20)
self._canvas.SetBackgroundColour(wx.WHITE)
self._canvas.SetBackgroundColour(self._backgroundColor)
self._diagram = ogl.Diagram()
self._canvas.SetDiagram(self._diagram)
self._diagram.SetCanvas(self._canvas)
self._canvas.SetFont(NORMALFONT)
def OnClear(self, event):
""" Deletion of selected objects from view.
*Must Override*
"""
self.SetPropertyModel(None)
def OnKeyPressed(self, event):
key = event.KeyCode()
if key == wx.WXK_DELETE:
@@ -159,7 +203,42 @@ class CanvasView(wx.lib.docview.View):
event.Skip()
def OnRightClick(self, event):
""" force selection underneath right click position. """
self.Activate()
self._canvas.SetFocus()
dc = wx.ClientDC(self._canvas)
self._canvas.PrepareDC(dc)
x, y = event.GetLogicalPosition(dc) # this takes into account scrollbar offset
shape = self._canvas.FindShape(x, y)[0]
model = None
if not shape:
self.SetSelection(None)
self.SetPropertyShape(None)
elif hasattr(shape, "GetModel"):
self.BringToFront(shape)
self.SetPropertyShape(shape)
self.SetSelection(shape)
shape.Select(True, dc)
model = shape.GetModel()
elif shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape): # ComplexTypeHeader for ComplexTypeShape
self.BringToFront(shape)
self.SetPropertyShape(shape.GetParent())
self.SetSelection(shape.GetParent())
shape.GetParent().Select(True, dc)
model = shape.GetParent().GetModel()
self.SetPropertyModel(model)
return (shape, model)
def OnLeftClick(self, event):
self.Activate()
self._canvas.SetFocus()
self.EraseRubberBand()
dc = wx.ClientDC(self._canvas)
@@ -322,21 +401,24 @@ class CanvasView(wx.lib.docview.View):
dc.EndDrawing()
def FindParkingSpot(self, width, height):
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 """
offset = 30 # space between shapes
x = offset
y = offset
maxX = 700 # max distance to the right where we'll place tables
max = 700 # max distance to the right where we'll place tables
noParkingSpot = True
while noParkingSpot:
point = self.isSpotOccupied(x, y, width, height)
if point:
x = point[0] + offset
if x > maxX:
x = offset
y = point[1] + offset
if parking == PARKING_HORIZONTAL:
x = point[0] + PARKING_OFFSET
if x > max:
x = PARKING_OFFSET
y = point[1] + PARKING_OFFSET
else: # parking == PARKING_VERTICAL:
y = point[1] + PARKING_OFFSET
if y > max:
y = PARKING_OFFSET
x = point[0] + PARKING_OFFSET
else:
noParkingSpot = False
@@ -351,7 +433,7 @@ class CanvasView(wx.lib.docview.View):
y2 = y + height
for shape in self._diagram.GetShapeList():
if isinstance(shape, ogl.RectangleShape) or isinstance(shape, ogl.EllipseShape):
if isinstance(shape, ogl.RectangleShape) or isinstance(shape, ogl.EllipseShape) or isinstance(shape, ogl.PolygonShape):
if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape):
# skip, part of a composite shape
continue
@@ -381,7 +463,7 @@ class CanvasView(wx.lib.docview.View):
# Canvas methods
#----------------------------------------------------------------------------
def AddShape(self, shape, x = None, y = None, pen = None, brush = None, text = None, eventHandler = None):
def AddShape(self, shape, x = None, y = None, pen = None, brush = None, text = None, eventHandler = None, shown=True):
if isinstance(shape, ogl.CompositeShape):
dc = wx.ClientDC(self._canvas)
self._canvas.PrepareDC(dc)
@@ -403,7 +485,7 @@ class CanvasView(wx.lib.docview.View):
shape.AddText(text)
shape.SetShadowMode(ogl.SHADOW_NONE)
self._diagram.AddShape(shape)
shape.Show(True)
shape.Show(shown)
if not eventHandler:
eventHandler = EditorCanvasShapeEvtHandler(self)
eventHandler.SetShape(shape)
@@ -424,16 +506,21 @@ class CanvasView(wx.lib.docview.View):
for line in shape.GetLines():
shape.RemoveLine(line)
self._diagram.RemoveShape(line)
line.Delete()
for obj in self._diagram.GetShapeList():
for line in obj.GetLines():
if self.IsShapeContained(shape, line.GetTo()) or self.IsShapeContained(shape, line.GetFrom()):
obj.RemoveLine(line)
self._diagram.RemoveShape(line)
line.Delete()
if line == shape:
obj.RemoveLine(line)
self._diagram.RemoveShape(line)
line.Delete()
shape.RemoveFromCanvas(self._canvas)
self._diagram.RemoveShape(shape)
shape.Delete()
def IsShapeContained(self, parent, shape):
@@ -448,29 +535,22 @@ class CanvasView(wx.lib.docview.View):
def UpdateShape(self, model):
for shape in self._diagram.GetShapeList():
if hasattr(shape, "GetModel") and shape.GetModel() == model:
oldw, oldh = shape.GetBoundingBoxMax()
oldx = shape.GetX()
oldy = shape.GetY()
x, y, w, h = model.getEditorBounds()
newX = x + w / 2
newY = y + h / 2
changed = False
if isinstance(shape, ogl.CompositeShape):
if shape.GetX() != newX or shape.GetY() != newY:
dc = wx.ClientDC(self._canvas)
self._canvas.PrepareDC(dc)
shape.SetSize(w, h, True) # wxBug: SetSize must be before Move because links won't go to the right place
shape.Move(dc, newX, newY) # wxBug: Move must be before SetSize because links won't go to the right place
changed = True
else:
oldw, oldh = shape.GetBoundingBoxMax()
oldx = shape.GetX()
oldy = shape.GetY()
if oldw != w or oldh != h or oldx != newX or oldy != newY:
shape.SetSize(w, h)
shape.SetX(newX)
shape.SetY(newY)
changed = True
if changed:
if oldw != w or oldh != h or oldx != newX or oldy != newY:
dc = wx.ClientDC(self._canvas)
self._canvas.PrepareDC(dc)
shape.SetSize(w, h, True) # wxBug: SetSize must be before Move because links won't go to the right place
shape.Move(dc, newX, newY) # wxBug: Move must be after SetSize because links won't go to the right place
shape.ResetControlPoints()
self._canvas.Refresh()
break
@@ -481,6 +561,10 @@ class CanvasView(wx.lib.docview.View):
return None
def GetShapeCount(self):
return self._diagram.GetCount()
def GetSelection(self):
return filter(lambda shape: shape.Selected(), self._diagram.GetShapeList())
@@ -526,7 +610,10 @@ class CanvasView(wx.lib.docview.View):
def ScrollVisible(self, shape):
xUnit, yUnit = shape._canvas.GetScrollPixelsPerUnit()
if not shape:
return
xUnit, yUnit = self._canvas.GetScrollPixelsPerUnit()
scrollX, scrollY = self._canvas.GetViewStart() # in scroll units
scrollW, scrollH = self._canvas.GetSize() # in pixels
w, h = shape.GetBoundingBoxMax() # in pixels
@@ -564,7 +651,10 @@ class CanvasView(wx.lib.docview.View):
# erase old selection if it still exists
if self._propShape and self._propShape in self._diagram.GetShapeList():
self._propShape.SetBrush(self._brush)
if hasattr(self._propShape, "DEFAULT_BRUSH"):
self._propShape.SetBrush(self._propShape.DEFAULT_BRUSH)
else:
self._propShape.SetBrush(self._brush)
if (self._propShape._textColourName in ["BLACK", "WHITE"]): # Would use GetTextColour() but it is broken
self._propShape.SetTextColour("BLACK", 0)
self._propShape.Draw(dc)
@@ -667,9 +757,10 @@ class EditorCanvasShapeEvtHandler(ogl.ShapeEvtHandler):
if shape:
model = shape.GetModel()
self._view.SetSelection(model, keys == self.SHIFT_KEY or keys == self.CONTROL_KEY)
self._view.SetPropertyShape(shape)
self._view.SetPropertyModel(model)
if model:
self._view.SetSelection(model, keys == self.SHIFT_KEY or keys == self.CONTROL_KEY)
self._view.SetPropertyShape(shape)
self._view.SetPropertyModel(model)
def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
@@ -686,7 +777,7 @@ class EditorCanvasShapeEvtHandler(ogl.ShapeEvtHandler):
def OnMovePre(self, dc, x, y, oldX, oldY, display):
""" Prevent objects from being dragged outside of viewable area """
if (x > self._view._maxWidth) or (y > self._view._maxHeight):
if (x < 0) or (y < 0) or (x > self._view._maxWidth) or (y > self._view._maxHeight):
return False
return ogl.ShapeEvtHandler.OnMovePre(self, dc, x, y, oldX, oldY, display)

View File

@@ -21,6 +21,7 @@ import string
import sys
import DebuggerService
import MarkerService
from UICommon import CaseInsensitiveCompare
_ = wx.GetTranslation
if wx.Platform == '__WXMSW__':
_WINDOWS = True
@@ -354,18 +355,6 @@ class CodeView(STCTextEditor.TextView):
return ['Put', 'Editor Specific', 'Keywords', 'Here']
def CaseInsensitiveCompare(self, s1, s2):
""" GetAutoCompleteKeywordList() method used to show keywords in case insensitive order """
s1L = s1.lower()
s2L = s2.lower()
if s1L == s2L:
return 0
elif s1L < s2L:
return -1
else:
return 1
def GetAutoCompleteKeywordList(self, context, hint):
""" Replace this method with Editor specific keywords """
kw = self.GetAutoCompleteDefaultKeywords()
@@ -380,7 +369,7 @@ class CodeView(STCTextEditor.TextView):
else:
replaceLen = 0
kw.sort(self.CaseInsensitiveCompare)
kw.sort(CaseInsensitiveCompare)
return " ".join(kw), replaceLen
@@ -410,6 +399,7 @@ class CodeView(STCTextEditor.TextView):
def OnSetIndentWidth(self):
dialog = wx.TextEntryDialog(self._GetParentFrame(), _("Enter new indent width (2-10):"), _("Set Indent Width"), "%i" % self.GetCtrl().GetIndent())
dialog.CenterOnParent()
if dialog.ShowModal() == wx.ID_OK:
try:
indent = int(dialog.GetValue())
@@ -480,7 +470,7 @@ class CodeView(STCTextEditor.TextView):
if hint == "ViewStuff":
self.GetCtrl().SetViewDefaults()
elif hint == "Font":
font, color = self.GetFontAndColorFromConfig()
font, color = self.GetCtrl().GetFontAndColorFromConfig()
self.GetCtrl().SetFont(font)
self.GetCtrl().SetFontColor(color)
else:

View File

@@ -310,6 +310,13 @@ class RunCommandUI(wx.Panel):
self._textCtrl.SetFontColor(wx.BLACK)
self._textCtrl.StyleClearAll()
self._textCtrl.SetReadOnly(True)
def StopAndRemoveUI(self, event):
self.StopExecution()
self.StopExecution()
index = self._noteBook.GetSelection()
self._noteBook.GetPage(index).Show(False)
self._noteBook.RemovePage(index)
#------------------------------------------------------------------------------
# Event handling
@@ -322,10 +329,7 @@ class RunCommandUI(wx.Panel):
self.StopExecution()
elif id == self.CLOSE_TAB_ID:
self.StopExecution()
index = self._noteBook.GetSelection()
self._noteBook.GetPage(index).Show(False)
self._noteBook.RemovePage(index)
self.StopAndRemoveUI(event)
def OnDoubleClick(self, event):
# Looking for a stack trace line.
@@ -368,7 +372,7 @@ class RunCommandUI(wx.Panel):
# FACTOR THIS INTO DocManager
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
for openDoc in openDocs:
if(isinstance(openDoc.GetFirstView(), CodeEditor.CodeView)):
if(isinstance(openDoc, CodeEditor.CodeDocument)):
openDoc.GetFirstView().GetCtrl().ClearCurrentLineMarkers()
foundView.GetCtrl().MarkerAdd(lineNum -1, CodeEditor.CodeCtrl.CURRENT_LINE_MARKER_NUM)
@@ -419,8 +423,9 @@ class DebugCommandUI(wx.Panel):
def ReturnPortToPool(port):
config = wx.ConfigBase_Get()
startingPort = config.ReadInt("DebuggerStartingPort", DEFAULT_PORT)
if port in range(startingPort, startingPort + PORT_COUNT):
DebugCommandUI.debuggerPortList.append(port)
val = int(startingPort) + int(PORT_COUNT)
if int(port) >= startingPort and (int(port) <= val):
DebugCommandUI.debuggerPortList.append(int(port))
ReturnPortToPool = staticmethod(ReturnPortToPool)
@@ -588,7 +593,7 @@ class DebugCommandUI(wx.Panel):
self._tb.EnableTool(self.ADD_WATCH_ID, False)
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
for openDoc in openDocs:
if(isinstance(openDoc.GetFirstView(), CodeEditor.CodeView)):
if(isinstance(openDoc, CodeEditor.CodeDocument)):
openDoc.GetFirstView().GetCtrl().ClearCurrentLineMarkers()
if self.framesTab:
self.framesTab.ClearWhileRunning()
@@ -662,7 +667,7 @@ class DebugCommandUI(wx.Panel):
def DeleteCurrentLineMarkers(self):
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
for openDoc in openDocs:
if(isinstance(openDoc.GetFirstView(), CodeEditor.CodeView)):
if(isinstance(openDoc, CodeEditor.CodeDocument)):
openDoc.GetFirstView().GetCtrl().ClearCurrentLineMarkers()
def LoadFramesListXML(self, framesXML):
@@ -848,7 +853,7 @@ class WatchDialog(wx.Dialog):
self.label_4 = wx.StaticText(self, -1, ",frame.f_globals, frame.f_locals)")
self.radio_box_1 = wx.RadioBox(self, -1, "Watch Information", choices=[WatchDialog.WATCH_ALL_FRAMES, WatchDialog.WATCH_THIS_FRAME, WatchDialog.WATCH_ONCE], majorDimension=0, style=wx.RA_SPECIFY_ROWS)
self._okButton = wx.Button(self, wx.ID_OK, "OK", size=(75,-1))
self._okButton = wx.Button(self, wx.ID_OK, "OK")
self._okButton.SetDefault()
self._okButton.SetHelpText(_("The OK button completes the dialog"))
def OnOkClick(event):
@@ -861,7 +866,7 @@ class WatchDialog(wx.Dialog):
self.EndModal(wx.ID_OK)
self.Bind(wx.EVT_BUTTON, OnOkClick, self._okButton)
self._cancelButton = wx.Button(self, wx.ID_CANCEL, _("Cancel"), size=(75,-1))
self._cancelButton = wx.Button(self, wx.ID_CANCEL, _("Cancel"))
self._cancelButton.SetHelpText(_("The Cancel button cancels the dialog."))
self.__set_properties()
@@ -900,7 +905,6 @@ class WatchDialog(wx.Dialog):
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.SetAutoLayout(True)
self.SetSizer(sizer_1)
self.Layout()
@@ -979,30 +983,31 @@ class FramesUI(wx.SplitterWindow):
#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):
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())
def MakeInspectConsoleTab(self, parent, id):
self.command_list = []
self.command_index = 0
def ExecuteCommand(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())
def ReplaceLastLine(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 OnKeyPressed(event):
key = event.KeyCode()
@@ -1011,13 +1016,13 @@ class FramesUI(wx.SplitterWindow):
return
elif key == wx.WXK_RETURN:
command = self._interCtrl.GetLine(self._interCtrl.GetCurrentLine())[4:]
ExecuteCommand(command)
self.ExecuteCommand(command)
self._interCtrl.AddText("\n>>> ")
return
elif key == wx.WXK_UP:
if not len(self.command_list):
return
ReplaceLastLine(self.command_list[self.command_index])
self.ReplaceLastLine(self.command_list[self.command_index])
if self.command_index == 0:
self.command_index = len(self.command_list) - 1
else:
@@ -1030,7 +1035,7 @@ class FramesUI(wx.SplitterWindow):
self.command_index = self.command_index + 1
else:
self.command_index = 0
ReplaceLastLine(self.command_list[self.command_index])
self.ReplaceLastLine(self.command_list[self.command_index])
return
event.Skip()
@@ -1081,6 +1086,12 @@ class FramesUI(wx.SplitterWindow):
self.Bind(wx.EVT_MENU, self.OnView, id=self.viewID)
item = wx.MenuItem(menu, self.viewID, "View in Dialog")
menu.AppendItem(item)
if not hasattr(self, "toInteractID"):
self.toInteractID = wx.NewId()
self.Bind(wx.EVT_MENU, self.OnSendToInteract, id=self.toInteractID)
item = wx.MenuItem(menu, self.toInteractID, "Send to Interact")
menu.AppendItem(item)
offset = wx.Point(x=0, y=20)
menuSpot = event.GetPoint() + offset
self._treeCtrl.PopupMenu(menu, menuSpot)
@@ -1105,13 +1116,33 @@ class FramesUI(wx.SplitterWindow):
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:
if item.find(prevItem + '[') != -1:
value += item[item.find('['):]
continue
if value != "":
value = value + '.'
if item == 'globals':
item = 'globals()'
if item != 'locals':
value += item
prevItem = item
print value
self.ReplaceLastLine(value)
self.ExecuteCommand(value)
def OnWatch(self, event):
try:
if hasattr(self, '_parentChain'):
wd = WatchDialog(wx.GetApp().GetTopWindow(), "Add a Watch", self._parentChain)
else:
wd = WatchDialog(wx.GetApp().GetTopWindow(), "Add a Watch", None)
wd.CenterOnParent()
if wd.ShowModal() == wx.ID_OK:
name, text, send_frame, run_once = wd.GetSettings()
if send_frame:
@@ -1125,6 +1156,7 @@ class FramesUI(wx.SplitterWindow):
nodeList = domDoc.getElementsByTagName('watch')
if len(nodeList) == 1:
watchValue = nodeList.item(0).getAttribute("message")
wd.Destroy()
except:
tp, val, tb = sys.exc_info()
traceback.print_exception(tp, val, tb)
@@ -1147,6 +1179,8 @@ class FramesUI(wx.SplitterWindow):
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:
@@ -1297,7 +1331,8 @@ class FramesUI(wx.SplitterWindow):
if intro == "True":
tree.SetItemHasChildren(n, True)
tree.SetPyData(n, "Introspect")
if name.find('[') == -1:
self._treeCtrl.SortChildren(treeNode)
return treeNode
def StripOuterSingleQuotes(self, string):
@@ -1613,7 +1648,7 @@ class DebuggerService(Service.Service):
RUN_ID = wx.NewId()
DEBUG_ID = wx.NewId()
DEBUG_WEBSERVER_ID = wx.NewId()
RUN_WEBSERVER_ID = wx.NewId()
def ComparePaths(first, second):
one = DebuggerService.ExpandPath(first)
@@ -1695,6 +1730,9 @@ class DebuggerService(Service.Service):
debuggerMenu.Append(DebuggerService.DEBUG_WEBSERVER_ID, _("Debug Internal Web Server"), _("Debugs the internal webservier"))
wx.EVT_MENU(frame, DebuggerService.DEBUG_WEBSERVER_ID, frame.ProcessEvent)
wx.EVT_UPDATE_UI(frame, DebuggerService.DEBUG_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)
debuggerMenu.AppendSeparator()
@@ -1709,6 +1747,11 @@ class DebuggerService(Service.Service):
viewMenuIndex = menuBar.FindMenu(_("&Project"))
menuBar.Insert(viewMenuIndex + 1, debuggerMenu, _("&Run"))
toolBar.AddSeparator()
toolBar.AddTool(DebuggerService.RUN_ID, getRunningManBitmap(), shortHelpString = _("Run"), longHelpString = _("Run"))
toolBar.AddTool(DebuggerService.DEBUG_ID, getDebuggingManBitmap(), shortHelpString = _("Debug"), longHelpString = _("Debug"))
toolBar.Realize()
return True
@@ -1742,6 +1785,9 @@ class DebuggerService(Service.Service):
elif an_id == DebuggerService.DEBUG_WEBSERVER_ID:
self.OnDebugWebServer(event)
return True
elif an_id == DebuggerService.RUN_WEBSERVER_ID:
self.OnRunWebServer(event)
return True
return False
def ProcessUpdateUIEvent(self, event):
@@ -1778,13 +1824,13 @@ class DebuggerService(Service.Service):
return
self.ShowWindow(True)
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
project = projectService.GetView().GetDocument()
try:
dlg = CommandPropertiesDialog(self.GetView().GetFrame(), 'Debug Python File', projectService, None, pythonOnly=True, okButtonName="Debug", debugging=True)
except:
return
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
fileToDebug, initialArgs, startIn, isPython, environment = dlg.GetSettings()
projectPath, fileToDebug, initialArgs, startIn, isPython, environment = dlg.GetSettings()
dlg.Destroy()
else:
dlg.Destroy()
@@ -1822,7 +1868,13 @@ class DebuggerService(Service.Service):
page.Execute(args, startIn=os.getcwd(), environment=os.environ)
except:
pass
def OnRunWebServer(self, event):
if not Executor.GetPythonExecutablePath():
return
import WebServerService
wsService = wx.GetApp().GetService(WebServerService.WebServerService)
wsService.ShutDownAndRestart()
def HasAnyFiles(self):
docs = wx.GetApp().GetDocumentManager().GetDocuments()
@@ -1849,10 +1901,12 @@ class DebuggerService(Service.Service):
_("Debug"),
wx.YES_NO|wx.ICON_QUESTION
)
yesNoMsg.CenterOnParent()
if yesNoMsg.ShowModal() == wx.ID_YES:
docs = wx.GetApp().GetDocumentManager().GetDocuments()
for doc in docs:
doc.Save()
yesNoMsg.Destroy()
def OnExit(self):
DebugCommandUI.ShutdownAllDebuggers()
@@ -1865,15 +1919,13 @@ class DebuggerService(Service.Service):
if not Executor.GetPythonExecutablePath():
return
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
project = projectService.GetView().GetDocument()
try:
dlg = CommandPropertiesDialog(self.GetView().GetFrame(), 'Run', projectService, None)
except:
return
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
fileToRun, initialArgs, startIn, isPython, environment = dlg.GetSettings()
projectPath, fileToRun, initialArgs, startIn, isPython, environment = dlg.GetSettings()
dlg.Destroy()
else:
dlg.Destroy()
@@ -1881,7 +1933,12 @@ class DebuggerService(Service.Service):
self.PromptToSaveFiles()
# This will need to change when we can run more than .py and .bpel files.
if not isPython:
projectService.RunProcessModel(fileToRun)
projects = projectService.FindProjectByFile(projectPath)
if not projects:
return
project = projects[0]
deployFilePath = project.GenerateDeployment()
projectService.RunProcessModel(fileToRun, project.GetAppInfo().language, deployFilePath)
return
self.ShowWindow(True)
@@ -1901,10 +1958,16 @@ class DebuggerService(Service.Service):
fileName = wx.GetApp().GetDocumentManager().GetCurrentDocument().GetFilename()
if line < 0:
line = view.GetCtrl().GetCurrentLine()
else:
view = None
if self.BreakpointSet(fileName, line + 1):
self.ClearBreak(fileName, line + 1)
if view:
view.GetCtrl().Refresh()
else:
self.SetBreak(fileName, line + 1)
if view:
view.GetCtrl().Refresh()
# Now refresh all the markers icons in all the open views.
self.ClearAllBreakpointMarkers()
self.SetAllBreakpointMarkers()
@@ -1939,6 +2002,10 @@ class DebuggerService(Service.Service):
else:
return self._masterBPDict[expandedName]
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):
@@ -1977,16 +2044,20 @@ class DebuggerService(Service.Service):
def ClearAllBreakpointMarkers(self):
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
for openDoc in openDocs:
if(isinstance(openDoc.GetFirstView(), CodeEditor.CodeView)):
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 GetMasterBreakpointDict(self):
return self._masterBPDict
def SetAllBreakpointMarkers(self):
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
for openDoc in openDocs:
if(isinstance(openDoc.GetFirstView(), CodeEditor.CodeView)):
if(isinstance(openDoc, CodeEditor.CodeDocument)):
self.SetCurrentBreakpointMarkers(openDoc.GetFirstView())
def SetCurrentBreakpointMarkers(self, view):
@@ -2052,6 +2123,11 @@ class DebuggerOptionsPanel(wx.Panel):
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):
@@ -2070,10 +2146,8 @@ class CommandPropertiesDialog(wx.Dialog):
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
if _WINDOWS:
wx.Dialog.__init__(self, parent, -1, title)
else:
wx.Dialog.__init__(self, parent, -1, title, size=(390,270))
wx.Dialog.__init__(self, parent, -1, title)
projStaticText = wx.StaticText(self, -1, _("Project:"))
fileStaticText = wx.StaticText(self, -1, _("File:"))
@@ -2082,43 +2156,43 @@ class CommandPropertiesDialog(wx.Dialog):
pythonPathStaticText = wx.StaticText(self, -1, _("PYTHONPATH:"))
postpendStaticText = _("Postpend win32api path")
cpPanelBorderSizer = wx.BoxSizer(wx.VERTICAL)
self._projList = wx.Choice(self, -1, (200,-1), choices=self._projectNameList)
self._projList = wx.Choice(self, -1, choices=self._projectNameList)
self.Bind(wx.EVT_CHOICE, self.EvtListBox, self._projList)
HALF_SPACE = 5
flexGridSizer = wx.FlexGridSizer(cols = 3, vgap = 10, hgap = 10)
GAP = HALF_SPACE
if wx.Platform == "__WXMAC__":
GAP = 10
flexGridSizer = wx.GridBagSizer(GAP, GAP)
flexGridSizer.Add(projStaticText, 0, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
flexGridSizer.Add(self._projList, 1, flag=wx.EXPAND)
flexGridSizer.Add(wx.StaticText(parent, -1, ""), 0)
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, 0, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
self._fileList = wx.Choice(self, -1, (200,-1))
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, flag=wx.EXPAND)
flexGridSizer.Add(wx.StaticText(parent, -1, ""), 0)
flexGridSizer.Add(self._fileList, (1,1), (1,2), flag=wx.EXPAND)
config = wx.ConfigBase_Get()
self._lastArguments = config.Read("LastRunArguments")
self._argsEntry = wx.TextCtrl(self, -1, str(self._lastArguments))
self._argsEntry.SetToolTipString(str(self._lastArguments))
flexGridSizer.Add(argsStaticText, 0, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
flexGridSizer.Add(self._argsEntry, 1, flag=wx.EXPAND)
flexGridSizer.Add(wx.StaticText(parent, -1, ""), 0)
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, 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")
if not self._lastStartIn:
self._lastStartIn = str(os.getcwd())
self._startEntry = wx.TextCtrl(self, -1, self._lastStartIn)
self._startEntry.SetToolTipString(self._lastStartIn)
flexGridSizer.Add(self._startEntry, 1, wx.EXPAND)
flexGridSizer.Add(self._startEntry, (3,1), flag=wx.EXPAND)
self._findDir = wx.Button(self, -1, _("Browse..."))
self.Bind(wx.EVT_BUTTON, self.OnFindDirClick, self._findDir)
flexGridSizer.Add(self._findDir, 0, wx.RIGHT, 10)
flexGridSizer.Add(self._findDir, (3,2))
flexGridSizer.Add(pythonPathStaticText, 0, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
flexGridSizer.Add(pythonPathStaticText, (4,0), flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT)
if os.environ.has_key('PYTHONPATH'):
startval = os.environ['PYTHONPATH']
else:
@@ -2126,34 +2200,29 @@ class CommandPropertiesDialog(wx.Dialog):
self._lastPythonPath = config.Read("LastPythonPath", startval)
self._pythonPathEntry = wx.TextCtrl(self, -1, self._lastPythonPath)
self._pythonPathEntry.SetToolTipString(self._lastPythonPath)
flexGridSizer.Add(self._pythonPathEntry, 1, wx.EXPAND)
flexGridSizer.Add(wx.StaticText(parent, -1, ""), 0)
flexGridSizer.Add(wx.StaticText(parent, -1, ""), 0)
flexGridSizer.Add(self._pythonPathEntry, (4,1), (1,2), flag=wx.EXPAND)
if debugging and _WINDOWS:
self._postpendCheckBox = wx.CheckBox(self, -1, postpendStaticText)
checked = bool(config.ReadInt("PythonPathPostpend", 1))
self._postpendCheckBox.SetValue(checked)
flexGridSizer.Add(self._postpendCheckBox, 1, wx.EXPAND)
flexGridSizer.Add(wx.StaticText(parent, -1, ""), 0)
cpPanelBorderSizer.Add(flexGridSizer, 0, wx.ALL, 10)
flexGridSizer.Add(self._postpendCheckBox, (5,1), flag=wx.EXPAND)
cpPanelBorderSizer.Add(flexGridSizer, 0, flag=wx.ALL, border=10)
box = wx.BoxSizer(wx.HORIZONTAL)
box = wx.StdDialogButtonSizer()
self._okButton = wx.Button(self, wx.ID_OK, okButtonName)
self._okButton.SetDefault()
self._okButton.SetHelpText(_("The ") + okButtonName + _(" button completes the dialog"))
box.Add(self._okButton, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
box.AddButton(self._okButton)
self.Bind(wx.EVT_BUTTON, self.OnOKClick, self._okButton)
btn = wx.Button(self, wx.ID_CANCEL, _("Cancel"))
btn.SetHelpText(_("The Cancel button cancels the dialog."))
box.Add(btn, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
cpPanelBorderSizer.Add(box, 0, wx.ALIGN_RIGHT|wx.BOTTOM, 5)
box.AddButton(btn)
box.Realize()
cpPanelBorderSizer.Add(box, 0, flag=wx.ALIGN_RIGHT|wx.ALL, border=5)
self.SetSizer(cpPanelBorderSizer)
if _WINDOWS:
self.GetSizer().Fit(self)
self.Layout()
# Set up selections based on last values used.
self._fileNameList = None
self._selectedFileIndex = 0
@@ -2168,6 +2237,9 @@ class CommandPropertiesDialog(wx.Dialog):
self._selectedProjectIndex = selectedIndex
self._selectedProjectDocument = self._projectDocumentList[selectedIndex]
self.PopulateFileList(self._selectedProjectDocument, lastFile)
cpPanelBorderSizer.Fit(self)
def OnOKClick(self, event):
startIn = self._startEntry.GetValue()
@@ -2193,6 +2265,7 @@ class CommandPropertiesDialog(wx.Dialog):
self.EndModal(wx.ID_OK)
def GetSettings(self):
projectPath = self._selectedProjectDocument.GetFilename()
filename = self._fileNameList[self._selectedFileIndex]
args = self._argsEntry.GetValue()
startIn = self._startEntry.GetValue()
@@ -2207,7 +2280,7 @@ class CommandPropertiesDialog(wx.Dialog):
else:
env['PYTHONPATH'] = self._pythonPathEntry.GetValue()
return filename, args, startIn, isPython, env
return projectPath, filename, args, startIn, isPython, env
def OnFileSelected(self, event):
self._selectedFileIndex = self._fileList.GetSelection()
@@ -2229,16 +2302,17 @@ class CommandPropertiesDialog(wx.Dialog):
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)
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
self._startEntry.SetValue(dlg.GetPath())
dlg.Destroy()
def EvtListBox(self, event):
if event.GetString():
index = self._projectNameList.index(event.GetString())
@@ -2524,3 +2598,76 @@ def getAddWatchImage():
def getAddWatchIcon():
return wx.IconFromBitmap(getAddWatchBitmap())
#----------------------------------------------------------------------
def getRunningManData():
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\x01\x86IDAT8\x8d\xa5\x93\xb1K\x02Q\x1c\xc7\xbf\xcf\x9a\x1bZl\x88\xb4\
\x04\x83\x10\xa2\x96\xc0A\xa8\x96\x96\xf4h\xe9\xf0\x1f\xd0\xcd(Bpi\x13nH\xb2\
%\x9d\x1a"\xb9)\xb4\x16i\x10\n\x13MA\x84\xa3&\xa1\xa1A\xa1E\xbdw\x97\xa2\xbd\
\x06\xf1(\xef,\xac\xef\xf6x\xdf\xf7}\x9f\xdf\x97\xf7\x081M\xe0?\x9a\xfc\xcd \
\\\xdc2\x99\xb6A[\x14\x91C\x9e\x8c\x1d\x00\x00\xd5\xa7*\x9a\x8a\xfa7\x82u\
\xfb\x14dj\x03mQ\xc3}\xf2\xb5\x83\xc7B\x9e\x89\xf7/\xda\xba\xd1\x94\x01\x00j\
CF\xe2t\xef\x1b>\x1f\x8c3Q\xf0\x11\xd3p\xa2yf\x1a\xbc\xcb\n\xdee\x85\xdd>\
\x07\xb5!C\xe9\xb4\xb1\xe9=b\x03\x8fc\xc3\xcf\xbcN\xb3\x9e`@\x11\xb9\xaa`\
\x7fg\x19\'\x97y\xd8\x96\xfa\xf8\x95\xf23d\xa5O4\xbfh\x87(\xf8\x88a\xc0 $|~\
\x87n\xf7\x03\xaa\xf2\x8e\xc0\xee\n\x00 \x91\xab\xc3\xeb4\xc3\xed\xe1\xb4qF\
\x96\xb8`\xb3h\xb7\xa6Jo\xa0\x9d\x1eD\xc1G\xc4!\x9f\xae\x03\x00\xa8\xd5jh4e\
\r\xb9\xf0P\x82T,\x83\xf3\x0bl\xd8k\x18\xe0\xf6p\x84vz\xa0M\x8aB\xf2\x98\x84\
\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'
def getRunningManBitmap():
return BitmapFromImage(getRunningManImage())
def getRunningManImage():
stream = cStringIO.StringIO(getRunningManData())
return ImageFromStream(stream)
def getRunningManIcon():
icon = EmptyIcon()
icon.CopyFromBitmap(getRunningManBitmap())
return icon
#----------------------------------------------------------------------
def getDebuggingManData():
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\x01\xafIDAT8\x8d\x8d\x93\xbfK[Q\x14\xc7?7\n:\t\xb5SA\xc1?@\xc1A\x9c,%\
\xd0\xa9\x83\xb5\x98!(b\t\xbc\xa7q("m\x1c\n5V]D\xd4-\xf8\x83\xa7\xa2\t\xa1\
\xa6\xed$8\x08\x92\xa1\x8b\x14A\xd0YB"\xa4\xf4\x87\x90\x97K\xa8\xcb\xed\xf0\
\xc8m\xae\xfa\xd4\x03\x07.\xe7\x9e\xf3\xfd\x9e\x9f\x88@\x1d\xb5\xba\x94\xca\
\xaa\xeb\xb6\xbb4\xc0\x03d&\xb1\xa7\xfc\xfe\x0c\x80L\xdaQ\xd2\xad\x90I;F\x80\
++\xbe\xe0bve\xdf\xd7y\xfemH\xc4\x162\xaa\xbb\xa5D(\x1c\x11\xb7\x02\x88@\x9d\
f?*4\xd1\xf6\xa2\x0f\x80\x93\xf4\x8e\xe1\xb8\xf2\xf1\xb5\x18\x9cH(\x80\xe4bT\
\x83\xd5W\x1f\xa1pD\x8c|\xd8T\x00\xdf\xd6\xd7\xe8\x1f\xb3tp\xf1\n^\xfe\xf8\
\xa5^u7\x00P\x1eYP\xd2\x95\x1c\xa4\xa6\x84\x18\x8do\xab*C&\xed\xa8\xafG\x7f\
\xe9\x1f\xb3x\xdc\x08\xad\x8f \x7f\tg%\xf8Y\x82\xe3\x8de\x86\x82\xcdF9\xba\
\x84\xc1\x89\x84*K\t\xc0\xf0\xbbq:\x9f\xfcO\x7f?\xe7\x01\x9c\xff\x86Br\x8e\
\x83\xd4\x94\x06\xd0SH.F\xc5P\xb0\x19\xe9z \xf9KOmkN\x07\x03\x14/r\xb4?\x8b\
\xe8\xc6\xeb\x1e\x00l\x1f\xfe\xd15\x17\xaf<\xdb\xd37\xef\xd9\x9d\xb4\xe9\x8a\
\xadj\xbfx\xb4\x878(#\x03\x00\xe9JF{[\xf92\xeb\xb1V\x99\xbbb\xab|\x9f\xb7\
\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'
def getDebuggingManBitmap():
return BitmapFromImage(getDebuggingManImage())
def getDebuggingManImage():
stream = cStringIO.StringIO(getDebuggingManData())
return ImageFromStream(stream)
def getDebuggingManIcon():
icon = EmptyIcon()
icon.CopyFromBitmap(getDebuggingManBitmap())
return icon
#----------------------------------------------------------------------

View File

@@ -13,9 +13,10 @@
import wx
import wx.lib.pydocview
import MessageService
import ProjectEditor
import os
import os.path
import pickle
import activegrid.util.xmlutils as xmlutils
_ = wx.GetTranslation
@@ -38,7 +39,7 @@ EXTENSIONS_CONFIG_STRING = "Extensions"
class Extension:
def __init__(self, menuItemName):
def __init__(self, menuItemName=None):
self.menuItemName = menuItemName
self.id = 0
self.menuItemDesc = ''
@@ -46,32 +47,51 @@ class Extension:
self.commandPreArgs = ''
self.commandPostArgs = ''
self.fileExt = None
self.opOnSelectedFile = True
class ExtensionService(wx.lib.pydocview.DocService):
EXTENSIONS_KEY = "/AG_Extensions"
def __init__(self):
self.LoadExtensions()
def __getExtensionKeyName(extensionName):
return "%s/%s" % (ExtensionService.EXTENSIONS_KEY, extensionName)
__getExtensionKeyName = staticmethod(__getExtensionKeyName)
def LoadExtensions(self):
self._extensions = []
extensionNames = []
config = wx.ConfigBase_Get()
pickledExtensions = config.Read(EXTENSIONS_CONFIG_STRING)
if pickledExtensions:
try:
self._extensions = pickle.loads(pickledExtensions.encode('ascii'))
except:
tp, val, tb = sys.exc_info()
traceback.print_exception(tp,val,tb)
self._extensions = []
else:
self._extensions = []
path = config.GetPath()
try:
config.SetPath(ExtensionService.EXTENSIONS_KEY)
cont, value, index = config.GetFirstEntry()
while cont:
extensionNames.append(value)
cont, value, index = config.GetNextEntry(index)
finally:
config.SetPath(path)
for extensionName in extensionNames:
extensionData = config.Read(self.__getExtensionKeyName(extensionName))
if extensionData:
extension = xmlutils.unmarshal(extensionData.encode('utf-8'))
self._extensions.append(extension)
def SaveExtensions(self):
config = wx.ConfigBase_Get()
config.Write(EXTENSIONS_CONFIG_STRING, pickle.dumps(self._extensions))
config.DeleteGroup(ExtensionService.EXTENSIONS_KEY)
for extension in self._extensions:
config.Write(self.__getExtensionKeyName(extension.menuItemName), xmlutils.marshal(extension))
def GetExtensions(self):
@@ -82,6 +102,10 @@ class ExtensionService(wx.lib.pydocview.DocService):
self._extensions = extensions
def CheckSumExtensions(self):
return xmlutils.marshal(self._extensions)
def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
toolsMenuIndex = menuBar.FindMenu(_("&Tools"))
if toolsMenuIndex > -1:
@@ -100,8 +124,15 @@ class ExtensionService(wx.lib.pydocview.DocService):
wx.EVT_UPDATE_UI(frame, ext.id, frame.ProcessUpdateUIEvent)
if toolsMenuIndex == -1:
formatMenuIndex = menuBar.FindMenu(_("&Format"))
menuBar.Insert(formatMenuIndex + 1, toolsMenu, _("&Tools"))
index = menuBar.FindMenu(_("&Run"))
if index == -1:
index = menuBar.FindMenu(_("&Project"))
if index == -1:
index = menuBar.FindMenu(_("&Format"))
if index == -1:
index = menuBar.FindMenu(_("&View"))
menuBar.Insert(index + 1, toolsMenu, _("&Tools"))
def ProcessEvent(self, event):
id = event.GetId()
@@ -126,6 +157,14 @@ class ExtensionService(wx.lib.pydocview.DocService):
if fileExt in doc.GetDocumentTemplate().GetFileFilter():
event.Enable(True)
return True
if extension.opOnSelectedFile and isinstance(doc, ProjectEditor.ProjectDocument):
filename = doc.GetFirstView().GetSelectedFile()
if filename:
template = wx.GetApp().GetDocumentManager().FindTemplateForPath(filename)
for fileExt in extension.fileExt:
if fileExt in template.GetFileFilter():
event.Enable(True)
return True
event.Enable(False)
return False
return False
@@ -136,7 +175,12 @@ class ExtensionService(wx.lib.pydocview.DocService):
doc = wx.GetApp().GetDocumentManager().GetCurrentDocument()
if not doc:
return
filename = doc.GetFilename()
if extension.opOnSelectedFile and isinstance(doc, ProjectEditor.ProjectDocument):
filename = doc.GetFirstView().GetSelectedFile()
if not filename:
filename = doc.GetFilename()
else:
filename = doc.GetFilename()
ext = os.path.splitext(filename)[1]
if not '*' in extension.fileExt:
if not ext or ext[1:] not in extension.fileExt:
@@ -171,99 +215,107 @@ class ExtensionOptionsPanel(wx.Panel):
def __init__(self, parent, id):
wx.Panel.__init__(self, parent, id)
extOptionsPanelBorderSizer = wx.BoxSizer(wx.HORIZONTAL)
extOptionsPanelBorderSizer = wx.BoxSizer(wx.VERTICAL)
extOptionsPanelSizer = wx.FlexGridSizer(cols=2, hgap=SPACE, vgap=HALF_SPACE)
extOptionsPanelSizer = wx.BoxSizer(wx.HORIZONTAL)
extCtrlSizer = wx.BoxSizer(wx.VERTICAL)
extCtrlSizer.Add(wx.StaticText(self, -1, _("Extensions:")), 0)
self._extListBox = wx.ListBox(self, -1, size=(-1,185), style=wx.LB_SINGLE)
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.Bind(wx.EVT_LISTBOX, self.OnListBoxSelect, self._extListBox)
extCtrlSizer.Add(self._extListBox, 1, wx.TOP | wx.BOTTOM | wx.EXPAND, SPACE)
buttonSizer = wx.GridSizer(rows=1, hgap=10, vgap=5)
extCtrlSizer.Add(self._extListBox, 1, wx.BOTTOM | wx.EXPAND, SPACE)
buttonSizer = wx.GridSizer(cols=2, vgap=5, hgap=10)
self._moveUpButton = wx.Button(self, -1, _("Move Up"))
self.Bind(wx.EVT_BUTTON, self.OnMoveUp, self._moveUpButton)
buttonSizer.Add(self._moveUpButton, 0)
buttonSizer.Add(self._moveUpButton, 1, wx.EXPAND)
self._moveDownButton = wx.Button(self, -1, _("Move Down"))
self.Bind(wx.EVT_BUTTON, self.OnMoveDown, self._moveDownButton)
buttonSizer.Add(self._moveDownButton, 0)
extCtrlSizer.Add(buttonSizer, 0, wx.ALIGN_CENTER | wx.BOTTOM, HALF_SPACE)
buttonSizer = wx.GridSizer(rows=1, hgap=10, vgap=5)
self._addButton = wx.Button(self, -1, _("Add"))
buttonSizer.Add(self._moveDownButton, 1, wx.EXPAND)
self._addButton = wx.Button(self, wx.ID_ADD)
self.Bind(wx.EVT_BUTTON, self.OnAdd, self._addButton)
buttonSizer.Add(self._addButton, 0)
self._deleteButton = wx.Button(self, wx.ID_DELETE)
buttonSizer.Add(self._addButton, 1, wx.EXPAND)
self._deleteButton = wx.Button(self, wx.ID_DELETE, label=_("Delete")) # get rid of accelerator for letter d in "&Delete"
self.Bind(wx.EVT_BUTTON, self.OnDelete, self._deleteButton)
buttonSizer.Add(self._deleteButton, 0)
buttonSizer.Add(self._deleteButton, 1, wx.EXPAND)
extCtrlSizer.Add(buttonSizer, 0, wx.ALIGN_CENTER)
extOptionsPanelSizer.Add(extCtrlSizer, 0)
extOptionsPanelSizer.Add(extCtrlSizer, 0, wx.EXPAND)
self._extDetailPanel = wx.Panel(self)
staticBox = wx.StaticBox(self._extDetailPanel, label=_("Selected Extension"))
staticBoxSizer = wx.StaticBoxSizer(staticBox)
self._extDetailPanel.SetSizer(staticBoxSizer)
extDetailSizer = wx.FlexGridSizer(cols=1, hgap=5, vgap=3)
staticBoxSizer.AddSizer(extDetailSizer, 0, wx.ALL, 5)
staticBox = wx.StaticBox(self, label=_("Selected External Tool"))
staticBoxSizer = wx.StaticBoxSizer(staticBox, wx.VERTICAL)
extDetailSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=3)
extDetailSizer.AddGrowableCol(1,1)
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Name:")))
self._menuItemNameTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
extDetailSizer.Add(self._menuItemNameTextCtrl, 1, wx.EXPAND)
extDetailSizer.Add(self._menuItemNameTextCtrl, 0, wx.EXPAND)
self.Bind(wx.EVT_TEXT, self.SaveCurrentItem, self._menuItemNameTextCtrl)
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Menu Item Description:")))
self._menuItemDescTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
extDetailSizer.Add(self._menuItemDescTextCtrl, 1, wx.EXPAND)
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))
findFileButton = wx.Button(self._extDetailPanel, -1, _("Browse..."))
def OnBrowseButton(event):
fileDlg = wx.FileDialog(self, _("Choose an Executable:"), style=wx.OPEN | wx.HIDE_READONLY)
fileDlg = wx.FileDialog(self, _("Choose an Executable:"), style=wx.OPEN|wx.FILE_MUST_EXIST|wx.HIDE_READONLY|wx.CHANGE_DIR)
path = self._commandTextCtrl.GetValue()
if path:
fileDlg.SetPath(path)
# fileDlg.CenterOnParent() # wxBug: caused crash with wx.FileDialog
if fileDlg.ShowModal() == wx.ID_OK:
self._commandTextCtrl.SetValue(fileDlg.GetPath())
self._commandTextCtrl.SetInsertionPointEnd()
self._commandTextCtrl.SetToolTipString(fileDlg.GetPath())
fileDlg.Destroy()
wx.EVT_BUTTON(findFileButton, -1, OnBrowseButton)
hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(self._commandTextCtrl, 1, wx.EXPAND)
hsizer.Add(findFileButton, 0, wx.LEFT, HALF_SPACE)
extDetailSizer.Add(hsizer, 0)
extDetailSizer.Add(hsizer, 0, wx.EXPAND)
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Pre Arguments:")))
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Pre Args:")))
self._commandPreArgsTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
extDetailSizer.Add(self._commandPreArgsTextCtrl, 1, wx.EXPAND)
extDetailSizer.Add(self._commandPreArgsTextCtrl, 0, wx.EXPAND)
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Post Arguments:")))
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("Command Post Args:")))
self._commandPostArgsTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
extDetailSizer.Add(self._commandPostArgsTextCtrl, 1, wx.EXPAND)
extDetailSizer.Add(self._commandPostArgsTextCtrl, 0, wx.EXPAND)
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("File Extensions (Comma Separated):")))
extDetailSizer.Add(wx.StaticText(self._extDetailPanel, -1, _("File Extensions:")))
self._fileExtTextCtrl = wx.TextCtrl(self._extDetailPanel, -1, size = (-1, -1))
self._fileExtTextCtrl.SetToolTipString(_("""For example: "txt, text" or "*" for all files"""))
extDetailSizer.Add(self._fileExtTextCtrl, 1, wx.EXPAND)
self._fileExtTextCtrl.SetToolTipString(_("""For example: "txt, text" (comma separated) or "*" for all files"""))
extDetailSizer.Add(self._fileExtTextCtrl, 0, wx.EXPAND)
extOptionsPanelSizer.Add(self._extDetailPanel, 0)
self._selFileCtrl = wx.CheckBox(self._extDetailPanel, -1, _("Operate on Selected File"))
extDetailSizer.Add(self._selFileCtrl)
self._selFileCtrl.SetToolTipString(_("If focus is in the project, instead of operating on the project file, operate on the selected file."))
extOptionsPanelBorderSizer.Add(extOptionsPanelSizer, 0, wx.ALL | wx.EXPAND, SPACE)
self.SetSizer(extOptionsPanelBorderSizer)
self.Layout()
parent.AddPage(self, _("Extensions"))
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)
self.SetSizer(extOptionsPanelBorderSizer)
if self.PopulateItems():
self._extListBox.SetSelection(0)
self.OnListBoxSelect(None)
self.OnListBoxSelect()
self.Layout()
parent.AddPage(self, _("External Tools"))
def OnOK(self, optionsDialog):
self.SaveCurrentItem()
extensionsService = wx.GetApp().GetService(ExtensionService)
oldExtensions = extensionsService.GetExtensions()
extensionsService.SetExtensions(self._extensions)
extensionsService.SaveExtensions()
if oldExtensions.__repr__() != self._extensions.__repr__():
if extensionsService.CheckSumExtensions() != self._oldExtensions: # see PopulateItems() note about self._oldExtensions
msgTitle = wx.GetApp().GetAppName()
if not msgTitle:
msgTitle = _("Document Options")
@@ -277,6 +329,7 @@ class ExtensionOptionsPanel(wx.Panel):
extensionsService = wx.GetApp().GetService(ExtensionService)
import copy
self._extensions = copy.deepcopy(extensionsService.GetExtensions())
self._oldExtensions = extensionsService.CheckSumExtensions() # wxBug: need to make a copy now since the deepcopy reorders fields, so we must compare the prestine copy with the modified copy
for extension in self._extensions:
self._extListBox.Append(extension.menuItemName, extension)
self._currentItem = None
@@ -284,9 +337,9 @@ class ExtensionOptionsPanel(wx.Panel):
return len(self._extensions)
def OnListBoxSelect(self, event):
def OnListBoxSelect(self, event=None):
self.SaveCurrentItem()
if not self._extListBox.GetSelections():
if self._extListBox.GetSelection() == wx.NOT_FOUND:
self._currentItemIndex = -1
self._currentItem = None
self._deleteButton.Enable(False)
@@ -316,43 +369,52 @@ class ExtensionOptionsPanel(wx.Panel):
extension.fileExt = None
else:
extension.fileExt = fileExt.split(',')
extension.opOnSelectedFile = self._selFileCtrl.GetValue()
def LoadItem(self, extension):
if extension:
self._menuItemDescTextCtrl.SetValue(extension.menuItemDesc or '')
self._commandTextCtrl.SetValue(extension.command or '')
self._commandTextCtrl.SetToolTipString(extension.command or '')
self._commandPreArgsTextCtrl.SetValue(extension.commandPreArgs or '')
self._commandPostArgsTextCtrl.SetValue(extension.commandPostArgs or '')
if extension.fileExt:
self._fileExtTextCtrl.SetValue(extension.fileExt.__repr__()[1:-1].replace("'","")) # Make the list a string, strip the brakcet on either side
list = ""
for ext in extension.fileExt:
if list:
list = list + ", "
list = list + ext
self._fileExtTextCtrl.SetValue(list)
else:
self._fileExtTextCtrl.SetValue('')
self._selFileCtrl.SetValue(extension.opOnSelectedFile)
self._menuItemNameTextCtrl.SetValue(extension.menuItemName or '') # Do the name last since it triggers the write event that updates the entire item
self._extDetailPanel.Enable()
else:
self._menuItemNameTextCtrl.SetValue('')
self._menuItemDescTextCtrl.SetValue('')
self._commandTextCtrl.SetValue('')
self._commandTextCtrl.SetToolTipString(_("Path to executable"))
self._commandPreArgsTextCtrl.SetValue('')
self._commandPostArgsTextCtrl.SetValue('')
self._fileExtTextCtrl.SetValue('')
self._selFileCtrl.SetValue(True)
self._extDetailPanel.Enable(False)
def OnAdd(self, event):
self.SaveCurrentItem()
extensionNames = map(lambda extension: extension.menuItemName, self._extensions)
name = _("Untitled")
count = 1
while name in extensionNames:
while self._extListBox.FindString(name) != wx.NOT_FOUND:
count = count + 1
name = _("Untitled %s") % count
name = _("Untitled%s") % count
extension = Extension(name)
self._extensions.append(extension)
self._extListBox.Append(extension.menuItemName, extension)
self._extListBox.SetSelection(self._extListBox.GetCount() - 1)
self.OnListBoxSelect(None)
self._extListBox.SetStringSelection(extension.menuItemName)
self.OnListBoxSelect()
self._menuItemNameTextCtrl.SetFocus()
self._menuItemNameTextCtrl.SetSelection(-1, -1)
@@ -364,7 +426,7 @@ class ExtensionOptionsPanel(wx.Panel):
if self._currentItemIndex > -1:
self._extListBox.SetSelection(self._currentItemIndex)
self._currentItem = None # Don't update it since it no longer exists
self.OnListBoxSelect(None)
self.OnListBoxSelect()
def OnMoveUp(self, event):
@@ -374,7 +436,7 @@ class ExtensionOptionsPanel(wx.Panel):
self._extListBox.Insert(itemAboveString, self._currentItemIndex)
self._extListBox.SetClientData(self._currentItemIndex, itemAboveData)
self._currentItemIndex = self._currentItemIndex - 1
self.OnListBoxSelect(None) # Reset buttons
self.OnListBoxSelect() # Reset buttons
def OnMoveDown(self, event):
@@ -384,4 +446,4 @@ class ExtensionOptionsPanel(wx.Panel):
self._extListBox.Insert(itemBelowString, self._currentItemIndex)
self._extListBox.SetClientData(self._currentItemIndex, itemBelowData)
self._currentItemIndex = self._currentItemIndex + 1
self.OnListBoxSelect(None) # Reset buttons
self.OnListBoxSelect() # Reset buttons

View File

@@ -79,8 +79,7 @@ class FindInDirService(FindService.FindService):
id = event.GetId()
if id == FindInDirService.FINDALL_ID:
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
view = projectService.GetView()
if view and view.GetDocument() and view.GetDocument().GetFiles():
if projectService.GetFilesFromCurrentProject():
event.Enable(True)
else:
event.Enable(False)
@@ -94,7 +93,7 @@ class FindInDirService(FindService.FindService):
def ShowFindDirDialog(self, findString=None):
config = wx.ConfigBase_Get()
frame = wx.Dialog(None, -1, _("Find in Directory"), size= (320,200))
frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Find in Directory"), size= (320,200))
borderSizer = wx.BoxSizer(wx.HORIZONTAL)
contentSizer = wx.BoxSizer(wx.VERTICAL)
@@ -112,11 +111,11 @@ class FindInDirService(FindService.FindService):
dir = dirCtrl.GetValue()
if len(dir):
dlg.SetPath(dir)
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
dirCtrl.SetValue(dlg.GetPath())
dirCtrl.SetToolTipString(dirCtrl.GetValue())
dirCtrl.SetInsertionPointEnd()
dlg.Destroy()
wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
@@ -128,6 +127,9 @@ class FindInDirService(FindService.FindService):
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)
lineSizer = wx.BoxSizer(wx.HORIZONTAL)
lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
if not findString:
@@ -151,13 +153,17 @@ class FindInDirService(FindService.FindService):
buttonSizer = wx.BoxSizer(wx.VERTICAL)
findBtn = wx.Button(frame, wx.ID_OK, _("Find"))
findBtn.SetDefault()
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(wx.Button(frame, wx.ID_CANCEL), 0)
borderSizer.Add(buttonSizer, 0, wx.ALL, SPACE)
frame.SetSizer(borderSizer)
frame.Fit()
frame.CenterOnParent()
status = frame.ShowModal()
passedCheck = False
@@ -168,6 +174,7 @@ class FindInDirService(FindService.FindService):
_("Find in Directory"),
wx.OK | wx.ICON_EXCLAMATION
)
dlg.CenterOnParent()
dlg.ShowModal()
dlg.Destroy()
@@ -178,6 +185,7 @@ class FindInDirService(FindService.FindService):
_("Find in Directory"),
wx.OK | wx.ICON_EXCLAMATION
)
dlg.CenterOnParent()
dlg.ShowModal()
dlg.Destroy()
@@ -197,10 +205,8 @@ class FindInDirService(FindService.FindService):
regExpr = regExprCtrl.IsChecked()
self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
frame.Destroy()
if status == wx.ID_OK:
frame.Destroy()
messageService = wx.GetApp().GetService(MessageService.MessageService)
messageService.ShowWindow()
@@ -267,7 +273,6 @@ class FindInDirService(FindService.FindService):
return True
else:
frame.Destroy()
return False
@@ -285,7 +290,7 @@ class FindInDirService(FindService.FindService):
def ShowFindAllDialog(self, findString=None):
config = wx.ConfigBase_Get()
frame = wx.Dialog(None, -1, _("Find in Project"), size= (320,200))
frame = wx.Dialog(wx.GetApp().GetTopWindow(), -1, _("Find in Project"), size= (320,200))
borderSizer = wx.BoxSizer(wx.HORIZONTAL)
contentSizer = wx.BoxSizer(wx.VERTICAL)
@@ -310,13 +315,17 @@ class FindInDirService(FindService.FindService):
buttonSizer = wx.BoxSizer(wx.VERTICAL)
findBtn = wx.Button(frame, wx.ID_OK, _("Find"))
findBtn.SetDefault()
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(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
@@ -326,9 +335,8 @@ class FindInDirService(FindService.FindService):
regExpr = regExprCtrl.IsChecked()
self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
frame.Destroy()
if status == wx.ID_OK:
frame.Destroy()
messageService = wx.GetApp().GetService(MessageService.MessageService)
messageService.ShowWindow()
@@ -412,7 +420,6 @@ class FindInDirService(FindService.FindService):
return True
else:
frame.Destroy()
return False
@@ -442,8 +449,9 @@ class FindInDirService(FindService.FindService):
break
if not foundView:
doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT)
foundView = doc.GetFirstView()
doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE)
if doc:
foundView = doc.GetFirstView()
if foundView:
foundView.GetFrame().SetFocus()

View 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._replaceDialog.CenterOnParent()
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._findDialog.CenterOnParent()
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"))
dialog.CenterOnParent()
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)
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))
@@ -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)
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))
@@ -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\
\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():

View File

@@ -76,7 +76,6 @@ class HtmlView(CodeEditor.CodeView):
## sizer = wx.BoxSizer(wx.HORIZONTAL)
## sizer.Add(self._notebook, 1, wx.EXPAND)
## frame.SetSizer(sizer)
## frame.SetAutoLayout(True)
##
##
## def OnNotebookChanging(self, event):
@@ -120,7 +119,7 @@ class HtmlCtrl(CodeEditor.CodeCtrl):
def SetViewDefaults(self):
CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Html", hasWordWrap = False, hasTabs = True)
CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Html", hasWordWrap = True, hasTabs = True)
def GetFontAndColorFromConfig(self):
@@ -160,6 +159,10 @@ class HtmlOptionsPanel(STCTextEditor.TextOptionsPanel):
STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Html", label = "HTML", hasWordWrap = True, hasTabs = True)
def GetIcon(self):
return getHTMLIcon()
HTMLKEYWORDS = [
"A", "ABBR", "ACRONYM", "ADDRESS", "APPLET", "AREA", "B", "BASE", "BASEFONT", "BDO", "BIG", "BLOCKQUOTE",
"BODY", "BR", "BUTTON", "CAPTION", "CENTER", "CITE", "CODE", "COL", "COLGROUP", "DD", "DEL", "DFN", "DIR",
@@ -199,16 +202,26 @@ def getHTMLData():
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\xd3IDAT8\x8dcddbf\xa0\x040\xfe\xbf\xd1\xf3\x9f\x12\x03X\xfe}\xbeI\
\x91\x0b\x98(\xd2=(\x0c`\x90W\xd2\xfc\x0f\x030\xb6\xbc\x92\xe6\x7f\x1d\x03\
\xf3\xffN\xae\xde\xff\xff\xff\xff\xff\xdf\xc0\xd8\xfa\xff\xb5;O\xfe_\xbb\xf3\
\xe4\xbf\x8e\x819\\\xcd\xd7_\xff\xff\xb3<|x\x9dAAY\x0b\xc5\xd0\x07w\xaf1\xd8\
\xdbZ0\xec\xdd\xb5\x85\x81\x81\x81\x81\xe1\xdd\xf3\x1b\x0c\xab\x97\xcef\xe0`\
ca\xf8\xfa\xf1\x19\\\x1d\x17+\x03\x03\x0b\x03\x03\x03Cqq>\xc3\xd3\x17o\x18V,\
]\n\x97de\xe7\x81\xb3\x199\xc4\x18\x0e\x1d:\xc2 "*\xce\xf0\x8f\x11!\x8e\xd3\
\x0b\xd8\xd8\xa7\x8e\xed\xf9\x7f\xf1\xcca\x14o\xca+i\xfeg\xfc{:\x95\xa2\x844\
\xf0\xd1H\xb1\x01\x8c\x94\xe6F\x8a]\x00\x00YXz\xf0\x97\x87\'\x1a\x00\x00\x00\
\x00IEND\xaeB`\x82'
\x00\x01\xeeIDAT8\x8d}\x92?h\x13a\x18\xc6\x7fw=\x8d\xb4\x98\xa9-uP\x1a\xc9p$\
\xdc`\xd0C\x8d\x8b)\xc5!Z\x11\xcc\xd0A\xd0*\xa8\x93\x8b8\x18\x11\x14RD\x07\t\
N\xfe\xc1\x0cRAtS\x1c,\xcd\x10\x8c\xd8S\xba$9"\x11L\r\x96\x92\xa4.\xda#i\xa5\
\xe7p^r\x97?\xbe\xd3\xf1~\xdf\xf3{\x9f\xe7\xbbW\xf8T(\x998j\xb9V\x07 _\xde\
\x0e\xc0\xad\x19U\xe0?%\x01\x0c\xef\x19owv\xf9\xff\x01\x96\x88N\x850\x92)\
\xf3\xde\x95s}!Rgcx\xdb\x9f\xd6\xf7Z\x13"\xa7\xcf\x00\xf4\x85t\x01\xea\x9b\
\xedV\xfa\xd53\x00\xb2z\xb3\x7f\x84\xe5Z\xbd\x15\xc1)>x,\x04\x84,\xc0\xed\'\
\xfd\x01\x9dB\xdb:\xc0\x8a\xb1E\xa3\xb2A8\xe0!\x9cL\x99\x00\x83;\x83D\x0fxQ\
\x15Y\xe8\x19\xc1\x16\xff\xfe\xf2\x11V\xaf\xb1\x03\x90G\xe0\xf5\xe7\n\xd5\
\xf58\xb0\xc4\xfc"\xbcL\xbf7c\x91#\x82h\xff\xae\xb5\xa6{\xf2\xdc\x9bi\x17\
\xf8\xc6\x85\xaf\x9c\xbf:\x03\xc0\xe8P\x82\x8bwN\xa2\xe5\x8a\xa6\xe8\x9cjW\
\xf1\xed\x1c`M\x05P\x94\xa7=\xf3\xcf\xa6&\x91\x8c_\x85\xd6c\xad\x18[\xae\x0b\
\'\xf6\xef\xe6h4\r\xc0\xcf\x1f\xd0\xa8l0:\x94 \x937\x00\xc8\xe4\r\xeb\r:\x85\
\xe3J\x0cy\xe41\xde\xb1\xbb\xd4\xbf\x97\x11\x07|\x00T\xcbz\x97\x0b\xb1\x97\
\xb5jY\xa71\xf6\x0e-Wb65\xc9\x8b\xf9\xe7,\xaenZg\xebq\xd7])\xab7\xc9\xea\xee\
\x8c\xdaB\x90\xf8u\xbde\x13n\xb6\x96I[\x08\xa2N$(~\x8b#\xfb\x12H\x1f\x1e^\
\xeaZQ-W4\x0f\x9f\xaa\x01~\x8eO\r\x92\xc9\x1b\xc8>KlC\xbc{!\x1c\xf0\xf4\x8e\
\xa0*\xb2\x90|\xb4\xcf\xe1\xa0-v\xd6\xe5\xb3\xd3\x08\x828\xd0\x8b\x01X\xcb\
\xa2\xe5J\xdc\x7f\xe0o\xc3\'\n\x84\x03\x1eb\x91C\xa8\x8a,\xfc\x05\xf6\x0e\
\xbfa\x1f\xe7Z\xfb\x00\x00\x00\x00IEND\xaeB`\x82'
def getHTMLBitmap():

File diff suppressed because it is too large Load Diff

View File

@@ -11,6 +11,7 @@
#----------------------------------------------------------------------------
import wx
import wx.lib.docview
import sys
_ = wx.GetTranslation
@@ -36,10 +37,17 @@ class ImageView(wx.lib.docview.View):
_("New Image File"),
wx.OK | wx.ICON_EXCLAMATION)
return False
try:
self._bitmap = wx.Image(doc.GetFilename()).ConvertToBitmap()
except:
wx.MessageBox(_("Error loading '%s'. %s") % (doc.GetPrintableName(), sys.exc_value),
_("Open Image File"),
wx.OK | wx.ICON_EXCLAMATION)
return False
frame = wx.GetApp().CreateDocumentFrame(self, doc, flags)
panel = wx.Panel(frame, -1)
self._bitmap = wx.Image(doc.GetFilename()).ConvertToBitmap()
self._ctrl = wx.StaticBitmap(panel, -1, self._bitmap, (0,0), (self._bitmap.GetWidth(), self._bitmap.GetHeight()))
wx.EVT_LEFT_DOWN(self._ctrl, self.OnFocus)
wx.EVT_LEFT_DCLICK(self._ctrl, self.OnFocus)
@@ -85,15 +93,21 @@ import cStringIO
def getImageData():
return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x02\
\x00\x00\x00\x90\x91h6\x00\x00\x00\x03sBIT\x08\x08\x08\xdb\xe1O\xe0\x00\x00\
\x00\x9aIDAT(\x91\x95\x92\xc1\r\xc50\x08C\xdd\xaa\xeb\xc12\xec\x90\x9b\x97!\
\x0b\xb0\x03\x19\xe8\x1fR\xa9U\xf2\xd5\xb4>DH\xf8\t\x13\xb1E\x04\xbe\xe8\x00\
@\xf2\x8d\xb5\xd6z\x02\x00\xccl\t\x98\x19\xc9}\xe9#y\x8f\xb0\x00H\xba\xc3\
\xfd\x8a\xbd\x9e0\xe8xn\x9b\x99*q[r\x01`\xfa\x8f?\x91\x86-\x07\x8d\x00Iww\
\xf7\xce\xcc\xf0>\xbb\x01\xa8j)e\x80G\xa0\xb7[k\x00J)\xfdU\xd5\xd6Z\x87O_D\
\x88\x88\x88dff>\x17"r\x02y\xd33\xb3E\xc4\xcb\xe3\xeb\xda\xbe\x9e\xf7\x0f\
\xa0B\x86\xd5X\x16\xcc\xea\x00\x00\x00\x00IEND\xaeB`\x82'
'\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\x017IDAT8\x8dcddbfh\x9f\xb3\xf3?\x03\x0e\xf0\xfc\xc5[\x0c\xb1\xfb\x0f\
\x9f10000l\x9e]\xc2\xc8\x02\x13,Ot\xc6e\x06V\xe0\x9f1\x81\x81\x81\x81\x81\
\x81\x05Y\xf0\xdc\x0b\x88C\x1e|\xfc\xc2\xf0\xe3\xf7\x01\x86{\x1fO20000(\xf1\
\x9b3p\xb0:\xc0\xd5\x05\xa9\xf3\xc2\xd9L\xe8&\xa3kf```8\xfah-\xc3\xb57\x9d\
\x0c?~\x1f\xc0p\t\x86\x01\xe8\x9aa\xe0\xf9\xc7\'X\xc5Q\x0c\x90\xe1c\xc4P\xf4\
\xfc\xe3\x13\x14\xb6\x02?\x0f~\x17(\xf1\x9bc\xd5\xcc\xc0\xc0\xc0 \xc9/\x03g_\
\xbbx\ta\x00,\xaa\xc4\xb8\x18\x18\x8c%\xd5\xb0j\x86\x19n$\xc1\x88"\xc6\x82\
\xaeH]X\x9d!T#\x96\xe1\xec\xf3[\x0cG\x1f\xade````\xb0\x96\x0bf0\x96TcP\x17V\
\xc70\x14\xc3\x00\x98!\xea\xc2\xea\x0c\xb7\x1a\x1a\x19\x18\x18\x18\x18\xa2\
\xd6,\xc0\xa6\x0c\xe1\x05J\x00\x0b\x03\x03"iz,\xd3A\x91|l\xf2\x8a\x81\x81\
\x81\x81au\x87\x18\x8a\xf8\xd5\x8aW\xc4\xb9@VN\x0c\x9f4v\x03\x94\x05LH2\x04%\
\x10wD]!h#N\x03`\xb9\x0b\x06`\t\x85\x10\x00\x00\xe4\x0ecz\x94h\xf0\x8e\x00\
\x00\x00\x00IEND\xaeB`\x82'
def getImageBitmap():

View File

@@ -14,6 +14,35 @@ import wx
import Service
import STCTextEditor
#----------------------------------------------------------------------------
# Utility
#----------------------------------------------------------------------------
def ClearMessages():
messageService = wx.GetApp().GetService(MessageService)
view = messageService.GetView()
if view:
view.ClearLines()
def ShowMessages(messages, clear=False):
if ((messages != None) and (len(messages) > 0)):
messageService = wx.GetApp().GetService(MessageService)
messageService.ShowWindow(True)
view = messageService.GetView()
if view:
if (clear):
view.ClearLines()
for message in messages:
view.AddLines(message)
view.AddLines("\n")
#----------------------------------------------------------------------------
# Classes
#----------------------------------------------------------------------------
class MessageView(Service.ServiceView):
""" Reusable Message View for any document.
When an item is selected, the document view is called back (with DoSelectCallback) to highlight and display the corresponding item in the document view.
@@ -36,52 +65,52 @@ class MessageView(Service.ServiceView):
txtCtrl.SetFontColor(wx.BLACK)
txtCtrl.StyleClearAll()
txtCtrl.UpdateStyles()
wx.EVT_SET_FOCUS(txtCtrl, self.OnFocus)
return txtCtrl
def GetDocument(self):
return None
## def ProcessEvent(self, event):
## stcControl = self.GetControl()
## if not isinstance(stcControl, wx.stc.StyledTextCtrl):
## return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
## id = event.GetId()
## if id == wx.ID_CUT:
## stcControl.Cut()
## return True
## elif id == wx.ID_COPY:
## stcControl.Copy()
## return True
## elif id == wx.ID_PASTE:
## stcControl.Paste()
## return True
## elif id == wx.ID_CLEAR:
## stcControl.Clear()
## return True
## elif id == wx.ID_SELECTALL:
## stcControl.SetSelection(0, -1)
## return True
##
##
## def ProcessUpdateUIEvent(self, event):
## stcControl = self.GetControl()
## if not isinstance(stcControl, wx.stc.StyledTextCtrl):
## return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
## id = event.GetId()
## if id == wx.ID_CUT:
## event.Enable(stcControl.CanCut())
## return True
## elif id == wx.ID_COPY:
## event.Enable(stcControl.CanCopy())
## return True
## elif id == wx.ID_PASTE:
## event.Enable(stcControl.CanPaste())
## return True
## elif id == wx.ID_CLEAR:
## event.Enable(True) # wxBug: should be stcControl.CanCut()) but disabling clear item means del key doesn't work in control as expected
## return True
## elif id == wx.ID_SELECTALL:
## event.Enable(stcControl.GetTextLength() > 0)
## return True
def OnFocus(self, event):
wx.GetApp().GetDocumentManager().ActivateView(self)
event.Skip()
def ProcessEvent(self, event):
stcControl = self.GetControl()
if not isinstance(stcControl, wx.stc.StyledTextCtrl):
return wx.lib.docview.View.ProcessEvent(self, event)
id = event.GetId()
if id == wx.ID_COPY:
stcControl.Copy()
return True
elif id == wx.ID_CLEAR:
stcControl.Clear()
return True
elif id == wx.ID_SELECTALL:
stcControl.SetSelection(0, -1)
return True
def ProcessUpdateUIEvent(self, event):
stcControl = self.GetControl()
if not isinstance(stcControl, wx.stc.StyledTextCtrl):
return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
id = event.GetId()
if id == wx.ID_CUT or id == wx.ID_PASTE:
# I don't think cut or paste makes sense from a message/log window.
event.Enable(False)
return True
elif id == wx.ID_COPY:
hasSelection = (stcControl.GetSelectionStart() != stcControl.GetSelectionEnd())
event.Enable(hasSelection)
return True
elif id == wx.ID_CLEAR:
event.Enable(True) # wxBug: should be stcControl.CanCut()) but disabling clear item means del key doesn't work in control as expected
return True
elif id == wx.ID_SELECTALL:
event.Enable(stcControl.GetTextLength() > 0)
return True
#----------------------------------------------------------------------------
@@ -139,5 +168,3 @@ class MessageService(Service.Service):
def _CreateView(self):
return MessageView(self)

View File

@@ -321,7 +321,7 @@ class OutlineService(Service.Service):
def __init__(self, serviceName, embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOM):
Service.Service.__init__(self, serviceName, embeddedWindowLocation)
self._validTemplates = []
self._validViewTypes = []
def _CreateView(self):
@@ -493,9 +493,8 @@ class OutlineService(Service.Service):
if self.GetView():
currView = wx.GetApp().GetDocumentManager().GetCurrentView()
if currView:
for template in self._validTemplates:
type = template.GetViewType()
if isinstance(currView, type):
for viewType in self._validViewTypes:
if isinstance(currView, viewType):
self.LoadOutline(currView)
foundRegisteredView = True
break
@@ -506,14 +505,14 @@ class OutlineService(Service.Service):
self._timer.Start(1000) # 1 second interval
def AddTemplateForBackgroundHandler(self, template):
self._validTemplates.append(template)
def AddViewTypeForBackgroundHandler(self, viewType):
self._validViewTypes.append(viewType)
def GetTemplatesForBackgroundHandler(self):
return self._validTemplates
def GetViewTypesForBackgroundHandler(self):
return self._validViewTypes
def RemoveTemplateForBackgroundHandler(self, template):
self._validTemplates.remove(template)
def RemoveViewTypeForBackgroundHandler(self, viewType):
self._validViewTypes.remove(viewType)

View File

@@ -219,6 +219,10 @@ class PHPOptionsPanel(STCTextEditor.TextOptionsPanel):
STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "PHP", label = "PHP", hasWordWrap = True, hasTabs = True)
def GetIcon(self):
return getPHPIcon()
PHPKEYWORDS = [
"and", "or", "xor", "__FILE__", "exception", "__LINE__", "array", "as", "break", "case",
"class", "const", "continue", "declare", "default", "die", "do", "echo", "else", "elseif",
@@ -274,14 +278,22 @@ import cStringIO
def getPHPData():
return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
"\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{IDAT8\x8dclh8\xf0\x9f\x81\x02\xc0D\x89f\xaa\x18\xc0\x82M0<\\\x1c\
\xce^\xb9\xf2%y.\xd0\xd4\xd4$\xde\x05\xf8l\x0c\x0f\x17\x87\x8baS\xc7x\xfd\
\xfa\xf5\xff\xc8\xb6]\xbf~\x1d\xc3\x05\xf8\xc4\x98\x90\x05\xae_\xbf\x8e\xa1\
\x88\x90\xd8 \x8aF\x98\x93`~\xc3\x05\xd0\xd5\xc1\r\x80\t\xc0B\xf7\xfa\xf5\
\xeb(l\\\xeaP\xbc\x80\x1c\x85\xb8\xd8\xe8|&b\x9c\x8dn;2`\x1c\xf0\xdc\x08\x00\
\x8e\xf2S\xed\xb0\xbe\xaa\xbc\x00\x00\x00\x00IEND\xaeB`\x82'
\x00\x01GIDAT8\x8d\x8d\x931O\xc2@\x14\xc7\x7fW\xfa\x11\x1c\xe430\xb88\x18'\
\x8c\x89\x0e\x0c\xc4\xd9\xdd\x85\xc9\xc5`\x82\x1b\t\x84\x98\x98\xe0\xa4_\x01\
cd`t$\xb8h\xd0\xb8\xabD0\xd4\xd0\xb4%H $\xe7P\x0e{@\xa9\xff\xe4\r\xf7\xee~\
\xff\xf7\xee^+\x1a\xcd\x8ed*\xab\xef\x02\xd0\xea\xda\x00\xb8\xce\x90\xb3\xa3\
}\xc1*5\x9a\x1d\xf9\xf6#g\xf1\xea\xf9qyS\x97\xf5o)\x8f\xcfo\xa50b\x84\x85\
\xb1\xca\xdc\x9b\xc0\xde\xe1\x01'\xa7U\x19v\xc6\xb4\xfa.\xeb\xc4\x01\x18L\
\xfc\xa4;\xf2\xdb\x7f\xac\xdd\xd3s<\xda\x03+\xb4\x88\x19\x04\x15\x0c\xb0\x93\
\xde\xc5\x9b\x80=\x86\xf6\xc5U\xa8\x81v\x05\x05\xab\xf6\xedq(\xf7\xd7A\xabk\
\xb36\xd2\x93A\xd8\x1aF\x18\xcc\x83\xb0\x08\x7f\xbc\xb7\xc2\r\\g8\x03\x97\
\xc1Q2{\x8e\xa7\x81/\xd7\xb5\x85C\xc9\xc46\xc9\x84>\xcaR!-`\xfa\x88\xab\xe0b\
>\xb5\xb4\xb2\xfa6\xcc\xf6\xa7\xc5f\x00V\xc0\xc3\xf3\x17w\x95\xa7YN\xad\x83\
\xfbP\x95\x06@un\xce\xd9\\\x8d\xad\x8d\xf8\xbf\xd6F\xa5\x9c\x11\x95rF\xfbaT\
\xc50\x15\xf3)\xb29\xbfc!\x8c\x98v\xaf\xe0f\x14\\*\xa4\x85f\x10|\x9c(\xa9)\
\xfc\x02?r\xb8\xfc~J.\xd0\x00\x00\x00\x00IEND\xaeB`\x82"
def getPHPBitmap():
return BitmapFromImage(getPHPImage())

View File

@@ -133,6 +133,10 @@ class PerlOptionsPanel(STCTextEditor.TextOptionsPanel):
STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Perl", label = "Perl", hasWordWrap = True, hasTabs = True)
def GetIcon(self):
return getPerlIcon()
PERLKEYWORDS = [
"abs",
"accept",
@@ -392,24 +396,22 @@ import cStringIO
def getPerlData():
return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
"\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\x01\x81IDAT8\x8d\xa5S1n\xe30\x10\x1cJ\xf9\x80;U\x04\x04~\xc06\xdc\xa9\
\x10\xd4\xb3K\xf2\x01\xf9\x01F\x92\xce\x8d:vI\x84\xdc\xd5\xaa\xdc%\xd1\x17\
\x0c\x17ns\xa7\x07,\x16\xe0G6E@B\x91\x9c*\x0b\x10 \xb9\xe4\xecpg\xa8T\x92\
\xe27q5^\xbc\xbf\xbd\n\x00\\\xdf\xdc\xaa\xb0\xf7\xfc\xf4(\xcc\x8c<\xcfqw\xff\
\xa0\xa6\x00PI\x8aa\x18\xa4m[\xd9\xedvb\xad\x95a\x18D%)\xfa\xbe\x97\xc5b!\
\xd6Z\xb1\xd6J\xdb\xb6\xa2\x92\x14\xe3\x91\x00\xc0r\xb5VZ\xeb\x08z<\x1e\xf1\
\xfe\xf6*]\xd7\xc1\x18\x03c\x0c\xea\xba\x063\xe3\xff\xbf\x0f\xf9\xf1\tD\x14\
\xe7\xce9\xec\xf7{\x00\x80\xf7\xfe\x1b\xf88\x920\xf1\xde\xc7j\xcc\x8c,\xcb\
\xe2:\xe4\xeb\xba\x06\x80o,"\x03\xad5\x0e\x87C\xacz>\x9fAD\xb1\xba\xd6\x1aD\
\x04f\x063\xcf\x19\\\xdf\xdc\xaa\xa2(p:\x9d\xe0\xbd\x07\x11\xc19\x07\xad5\
\x98\x19\xce9l\xb7[\x10\x11\xf2<\x9f\x03\x00\xc0\xcb\x9f\xbf\xaa,\xcbX!\xcb2\
t]\x17\xf3M\xd3\xc4\'\xc5\x98\xca\x12d\xddl6\x12d\x0c\x12\xab$\x85\xb5Vf2N\
\x83\x88P\x14\x05\xbc\xf7h\x9a\x06UUE\xda\xc6\x98\xcbM\x1c\x871\x06\xde{TU\
\x05\xe0\xcb\'\xe1RY\x96X\xae\xd6\xd1\x91\x17\x19\x00_]_\xae\xd6\x8a\x88\xf0\
\xfc\xf4(\xe1\xd2\xb4\x07?\x02\x8c\x0f\x8e\x1d85\xd2\xc5\x06\xf6}?\xf3|\x18\
\xb3\xdco\xbf\xf3\'`\xa6\xbc1\xa7\xd6\xcb\xbf\x00\x00\x00\x00IEND\xaeB`\x82'\
\x00\x01CIDAT8\x8d\x95\x93\xbfN\x02A\x10\x87\xbf[\xef\x05\xach|\x02m}\x008\
\x881Z\xd2\x1a\x13\x0bKx\x02{\xa3\xa13\xc6\xc6\xc6\xd2DZ+s\\\x83\x95\x1d\xa2\
&V\x104\x01/p\x01\t\xa0\xe8X\xc0\x9d\x87\xdc^\xf0\x97Lfw3\xdf\xec\xcc\xfe1\
\xca\x95\xba0\x95\xeb\xf5\x01h\xb4\xda\x00x\xde\x90\x83\xfd\r\x838\x95+u\xa9\
\rDj\x03\x91\xe7\xbe\xc8}wb\xa7\xc5\xb2\xdc\xbe\x89\xe4\x8f\xae\xc4PK\xe8L\
\xc5%\xef\x8da{7K\xee\xf0Rt1\xa6\xeb\xf5Y\x01>\xbea\xf45Y\xec\x8e&\xe5\xdf]\
\xdb4\xdd\x0e\xaf/-\xed&&\x110\xc0\xfa\x96\xc5\xf1N\x06\x80\xe5\xb5\xac6\x81\
\x82y\xb87\x9e\r\xeaT\x8b3s+\x95\x14+\x95\x14\x00\xd5h\xb5\xe9\x8e\xe6a\x7f\
\xf70\x14\xf6\xfeXy\xde0\x08\xf2\xe1\xf6gt\xb9V*)v\xc9\t\xae\xd5.9\x86\x19\
\x05\x9e\xefe\xe6i\x8dT\xd3\xed\xfc\x0b\x0e\xb7\x00\xd3C\xd4\x95\x1c'\xbf\
\x15\x15\xbe\xe3\xc6;l\x9e\xdc\x00\xbf\xfe/`\x97\x1c#|\x0e&@\xb1p\x16\x04>U\
\x1fI\x00\x17\xb9<\t\xa0\xc9*\t\x1e\xf4\xa5D\xbd\xeft:-q~\xe1\xbf\xb0\x88b\
\x13\x84{\x8d\x9a\x03\xfc\x00\xea\x7f\xa9A\xa7\xc3Vo\x00\x00\x00\x00IEND\xae\
B`\x82"
def getPerlBitmap():

File diff suppressed because it is too large Load Diff

View File

@@ -25,6 +25,7 @@ import keyword # for GetAutoCompleteKeywordList
import sys # for GetAutoCompleteKeywordList
import MessageService # for OnCheckCode
import OutlineService
from UICommon import CaseInsensitiveCompare
try:
import checker # for pychecker
_CHECKER_INSTALLED = True
@@ -69,8 +70,11 @@ class PythonView(CodeEditor.CodeView):
def OnActivateView(self, activate, activeView, deactiveView):
STCTextEditor.TextView.OnActivateView(self, activate, activeView, deactiveView)
if activate:
wx.CallAfter(self.LoadOutline) # need CallAfter because document isn't loaded yet
if activate and self.GetCtrl():
if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
self.LoadOutline()
else:
wx.CallAfter(self.LoadOutline) # need CallAfter because document isn't loaded yet
def OnClose(self, deleteWindow = True):
@@ -99,7 +103,7 @@ class PythonView(CodeEditor.CodeView):
filterkw = filter(lambda item: item.lower().startswith(lowerHint), kw) # remove variables and methods that don't match hint
kw = filterkw
kw.sort(self.CaseInsensitiveCompare)
kw.sort(CaseInsensitiveCompare)
if hint:
replaceLen = len(hint)
@@ -119,6 +123,7 @@ class PythonView(CodeEditor.CodeView):
# pychecker only works on files, doesn't take a stream or string input
if self.GetDocument().IsModified():
dlg = wx.MessageDialog(self.GetFrame(), _("'%s' has been modfied and must be saved first. Save file and check code?") % filename, _("Check Code"))
dlg.CenterOnParent()
val = dlg.ShowModal()
dlg.Destroy()
if val == wx.ID_OK:
@@ -167,7 +172,7 @@ class PythonView(CodeEditor.CodeView):
break
if not foundView:
doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT)
doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE)
foundView = doc.GetFirstView()
if foundView:
@@ -269,11 +274,29 @@ class PythonInterpreterView(wx.lib.docview.View):
return True
class PythonInterpreterDocument(wx.lib.docview.Document):
""" Generate Unique Doc Type """
pass
class PythonService(CodeEditor.CodeService):
def __init__(self):
CodeEditor.CodeService.__init__(self)
docManager = wx.GetApp().GetDocumentManager()
pythonInterpreterTemplate = wx.lib.docview.DocTemplate(docManager,
_("Python Interpreter"),
"*.Foobar",
"Foobar",
".Foobar",
_("Python Interpreter Document"),
_("Python Interpreter View"),
PythonInterpreterDocument,
PythonInterpreterView,
flags = wx.lib.docview.TEMPLATE_INVISIBLE,
icon = getPythonIcon())
docManager.AssociateTemplate(pythonInterpreterTemplate)
def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
@@ -308,7 +331,7 @@ class PythonService(CodeEditor.CodeService):
docManager = wx.GetApp().GetDocumentManager()
event.Check(False)
for doc in docManager.GetDocuments():
if isinstance(doc.GetFirstView(), PythonInterpreterView):
if isinstance(doc, PythonInterpreterDocument):
event.Check(True)
break
return True
@@ -318,28 +341,20 @@ class PythonService(CodeEditor.CodeService):
def OnViewPythonInterpreter(self, event):
for doc in wx.GetApp().GetDocumentManager().GetDocuments():
if isinstance(doc.GetFirstView(), PythonInterpreterView):
doc.GetFirstView().GetDocument().DeleteAllViews()
if isinstance(doc, PythonInterpreterDocument):
doc.DeleteAllViews()
return
docManager = self.GetDocumentManager()
template = wx.lib.docview.DocTemplate(docManager,
_("Python Interpreter"),
"*.Foobar",
"Foobar",
".Foobar",
_("Python Interpreter Document"),
_("Python Interpreter View"),
wx.lib.docview.Document,
PythonInterpreterView,
flags = wx.lib.docview.TEMPLATE_INVISIBLE)
newDoc = template.CreateDocument('', wx.lib.docview.DOC_SILENT)
if newDoc:
newDoc.SetDocumentName(template.GetDocumentName())
newDoc.SetDocumentTemplate(template)
newDoc.OnNewDocument()
newDoc.SetWriteable(False)
newDoc.GetFirstView().GetFrame().SetTitle(_("Python Interpreter"))
for template in self.GetDocumentManager().GetTemplates():
if template.GetDocumentType() == PythonInterpreterDocument:
newDoc = template.CreateDocument('', wx.lib.docview.DOC_SILENT|wx.lib.docview.DOC_OPEN_ONCE)
if newDoc:
newDoc.SetDocumentName(template.GetDocumentName())
newDoc.SetDocumentTemplate(template)
newDoc.OnNewDocument()
newDoc.SetWriteable(False)
newDoc.GetFirstView().GetFrame().SetTitle(_("Python Interpreter"))
break
class PythonCtrl(CodeEditor.CodeCtrl):
@@ -355,7 +370,7 @@ class PythonCtrl(CodeEditor.CodeCtrl):
def SetViewDefaults(self):
CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Python", hasWordWrap = False, hasTabs = True)
CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Python", hasWordWrap = True, hasTabs = True)
def GetFontAndColorFromConfig(self):
@@ -544,40 +559,56 @@ class PythonOptionsPanel(wx.Panel):
choosePathButton = wx.Button(self, -1, _("Browse..."))
pathSizer = wx.BoxSizer(wx.HORIZONTAL)
HALF_SPACE = 5
pathSizer.Add(pathLabel, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, HALF_SPACE)
pathSizer.Add(self._pathTextCtrl, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.RIGHT, HALF_SPACE)
pathSizer.Add(choosePathButton, 0, wx.ALIGN_RIGHT | wx.LEFT, HALF_SPACE)
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 = wx.BoxSizer(wx.VERTICAL)
mainSizer.Add(pathSizer, 0, wx.LEFT | wx.RIGHT | wx.TOP, 10)
mainSizer.Add(pathSizer, 0, wx.EXPAND|wx.LEFT|wx.RIGHT|wx.TOP, SPACE)
self._otherOptions = STCTextEditor.TextOptionsPanel(self, -1, configPrefix = "Python", label = "Python", hasWordWrap = False, hasTabs = True, addPage=False)
mainSizer.Add(self._otherOptions)
self._otherOptions = STCTextEditor.TextOptionsPanel(self, -1, configPrefix = "Python", label = "Python", hasWordWrap = True, hasTabs = True, addPage=False)
mainSizer.Add(self._otherOptions, 0, wx.EXPAND|wx.BOTTOM, SPACE)
self.SetSizer(mainSizer)
parent.AddPage(self, _("Python"))
def OnChoosePath(self, event):
defaultDir = os.path.dirname(self._pathTextCtrl.GetValue().strip())
defaultFile = os.path.basename(self._pathTextCtrl.GetValue().strip())
if _WINDOWS:
wildcard = _("*.exe")
wildcard = _("Executable (*.exe)|*.exe|All (*.*)|*.*")
if not defaultFile:
defaultFile = "python.exe"
else:
wildcard = _("*")
path = wx.FileSelector(_("Select a File"),
_(""),
_(""),
wildcard = wildcard ,
flags = wx.HIDE_READONLY,
parent = wx.GetApp().GetTopWindow())
if path:
self._pathTextCtrl.SetValue(path)
self._pathTextCtrl.SetToolTipString(self._pathTextCtrl.GetValue())
self._pathTextCtrl.SetInsertionPointEnd()
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 OnOK(self, optionsDialog):
if len(self._pathTextCtrl.GetValue()) > 0:
config = wx.ConfigBase_Get()
config.Write("ActiveGridPythonLocation", self._pathTextCtrl.GetValue())
config = wx.ConfigBase_Get()
config.Write("ActiveGridPythonLocation", self._pathTextCtrl.GetValue().strip())
self._otherOptions.OnOK(optionsDialog)
def GetIcon(self):
return getPythonIcon()
#----------------------------------------------------------------------------
# Icon Bitmaps - generated by encode_bitmaps.py
#----------------------------------------------------------------------------
@@ -587,18 +618,28 @@ import cStringIO
def getPythonData():
return \
"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
'\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\xd5IDAT8\x8d\x8d\x93Y\x0e\xc3 \x0cD\x9fM\xcf\xddNr2.\x96\xb8\x1f\
\x05\n\x84.#Y\x10\xa3\x19o\xb1\x99'*\xe2<\x82\x0e\xe6\xc9\xf8\x01\xef?\xa4\
\xf7)]\x05\x970O\xcdr\xce!\x119\xe7\x00\x02\x88\xfe}i\xb5\x848\x8f\xa8\x19\
\xcc\x19}+\xc5\xcc\xd3\x92<CZ\x0b\x99\xc4\xb2N\x01<\x80\xad\xdc?\x88\xf8\x1c\
X\x8f7\xe1\x1f\xdc*\xa9a+\xe1\xa3\xdc\xe7\xb4\xf6\xd1\xe5\xb6'\xc3@\xc5\xa0#\
\xab\x94\xd1\x0bL\xf0\xe6\x17\xa8v\xc3\x8aS\xa0.\x8be\x13\xe3\x15\x8f\xe1\
\xa5D\xee\xc9\xdb~%\xc7y\x84\xbb'sO\xd6\xd4\x17\xe4~\xc4\xf5\xef\xac\xa7\r\
\xbbp?b&\x0f\x89i\x14\x93\xca\x14z\xc5oh\x02E\xc4<\xd92\x03\xe0:B^\xc4K#\xe7\
\xe5\x00\x02\xfd\xb9H\x9ex\x02\x9a\x05a\xd2\xd3c\xc0\xcc\x00\x00\x00\x00IEND\
\xaeB`\x82"
\x00\x01\xe7IDAT8\x8d}\x921h\x13Q\x18\xc7\x7fw\xb9\x0ei\x9d*\xbd\xeb\x10\x8f\
,\x99\x1c*A[\xaa\x19B\xe8\xd0\xb1\x0e%K\x87\x88T2\x88Cqp\tD\x14i\xe9\xe0V\
\xdaQ\xb7\xe0P\xa1\x8b\xa0(\x95$z\xd5Q1\x90\xa2\xd7\x9a4^\x87\xa0`\x92!w9\
\x87\xf8.\xb9\xa6\xc97\xbd\xef{\xef\xfb\xbd\xff\xfb\xbfO*~;v\xf9\x1f\xad\xba\
\x05@\xf9\xd4\x06\xc0::$\xbb\x96\x92\x18\x11\n@(4\xdd\xcdB\xd3\xd4\x1d\x85\
\x8b\x97\xe1\xe3;\x83\x99\xe5\x15\xb2\xe0\x8e\x82\xc8\xa3\xe8\x003\xcb+\xac\
\xaee\xdda\xfb\xb2\x90\rPw\x14\x00\x9a\xb5\n\xbf\xfflSz\x9d\xa2Y\xdc"zca\xe8\
\x05\xb2h\x14\xcd\xd0\xf3B\x9f\x98\xe5\xf9\xde\x13"\xaaB\xc7\xb1\xcfU!\x0b\
\xc3D4k\x15\xac\x93\x03\xf4\x89Y\xaf\x96\xffT\x028\x17\xa2\xf4\'\xcdZ\x85\
\xf7F\x06{\xaa\x80ev\xc1\x91\xb91>\x18\x0f\xb8\xb7\x95a\xe9\xca\x0b:\x8e\xed\
\xca\x01E\x1a\x00\x98\r\x89\x92\x91\xa1\xda\xd8\x87\x06ha\x1f\x1b\x80\xcd\
\x9d%\xe0\xa5\x0f"[G\x87\x98\x8d\xde/ia\x05-\xac`\x996\xf9\\\x0b\xcb\xb4)\
\x1bmOMn\xf7\xd5\xf0\'\\\x8b\xdces\xe7\x8d\xef\x80h\xd6\xc2\n\xf9\\\x0b]\xf5\
\xab\xf2\xcdApR#\xf1kp4b\xc9 \xf9\\\x0b\x80\xe4\xcdE\xaf\xdeqlW\xaeVL\xaf`~\
\xd9\x03@W\xd3\x00\xc4\x13\x0b\xc4\x92A\xcf\xd0\xf9\xe8:\x89\xebW\x01(|\xfd\
\xe1\xbe-~F\xbas\xff\x91\xf75\x82n\x9d\x1c\xf0}\xfciw\xdd\xe7A<\xd1\x1b\xa8j\
c\x9f\xb2\xd1F\x92\xe4\x80O\x12\xc0\xc6\xb3\x14\xf6Ta\xe0)g\x81\xba\x9a\xf6\
\x9b(\x07\x14I@\x84lq\xb8?\xe6\xa3\xeb\x00\xdc\xba\x9d\xf4+\x10*~\xfem\xf3\
\xf8\xe1\x06\xc7\xa7\xdb\xe8j\x9a\xf8\xdc\xa4\xb7\x1f[\\\xe5\xd2\x851/\xff\
\x07\xac\x9b\xd1e\x12\x96\x0f\xfd\x00\x00\x00\x00IEND\xaeB`\x82'
def getPythonBitmap():

View File

@@ -80,6 +80,13 @@ class TextDocument(wx.lib.docview.Document):
pass
# Use this to override MultiClient.Select to prevent yellow background.
def MultiClientSelectBGNotYellow(a):
a.GetParent().multiView.UnSelect()
a.selected = True
#a.SetBackgroundColour(wx.Colour(255,255,0)) # Yellow
a.Refresh()
class TextView(wx.lib.docview.View):
MARKER_NUM = 0
MARKER_MASK = 0x1
@@ -102,6 +109,12 @@ class TextView(wx.lib.docview.View):
def GetCtrl(self):
if wx.Platform == "__WXMAC__":
# look for active one first
self._textEditor = self._GetActiveCtrl(self._dynSash)
if self._textEditor == None: # it is possible none are active
# look for any existing one
self._textEditor = self._FindCtrl(self._dynSash)
return self._textEditor
@@ -116,9 +129,19 @@ class TextView(wx.lib.docview.View):
def OnCreate(self, doc, flags):
frame = wx.GetApp().CreateDocumentFrame(self, doc, flags, style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
self._dynSash = wx.gizmos.DynamicSashWindow(frame, -1, style=wx.CLIP_CHILDREN)
self._dynSash._view = self
self._textEditor = self.GetCtrlClass()(self._dynSash, -1, style=wx.NO_BORDER)
# wxBug: DynamicSashWindow doesn't work on Mac, so revert to
# multisash implementation
if wx.Platform == "__WXMAC__":
wx.lib.multisash.MultiClient.Select = MultiClientSelectBGNotYellow
self._dynSash = wx.lib.multisash.MultiSash(frame, -1)
self._dynSash.SetDefaultChildClass(self.GetCtrlClass()) # wxBug: MultiSash instantiates the first TextCtrl with this call
self._textEditor = self.GetCtrl() # wxBug: grab the TextCtrl from the MultiSash datastructure
else:
self._dynSash = wx.gizmos.DynamicSashWindow(frame, -1, style=wx.CLIP_CHILDREN)
self._dynSash._view = self
self._textEditor = self.GetCtrlClass()(self._dynSash, -1, style=wx.NO_BORDER)
wx.EVT_LEFT_DOWN(self._textEditor, self.OnLeftClick)
self._CreateSizer(frame)
self.Activate()
frame.Show(True)
@@ -130,14 +153,18 @@ class TextView(wx.lib.docview.View):
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self._dynSash, 1, wx.EXPAND)
frame.SetSizer(sizer)
frame.SetAutoLayout(True)
def OnLeftClick(self, event):
self.Activate()
event.Skip()
def OnUpdate(self, sender = None, hint = None):
if hint == "ViewStuff":
self.GetCtrl().SetViewDefaults()
elif hint == "Font":
font, color = self.GetFontAndColorFromConfig()
font, color = self.GetCtrl().GetFontAndColorFromConfig()
self.GetCtrl().SetFont(font)
self.GetCtrl().SetFontColor(color)
@@ -195,7 +222,7 @@ class TextView(wx.lib.docview.View):
self.GetCtrl().SetViewEOL(not self.GetCtrl().GetViewEOL())
return True
elif id == VIEW_INDENTATION_GUIDES_ID:
self.GetCtrl().SetViewIndentationGuides(not self.GetCtrl().GetViewIndentationGuides())
self.GetCtrl().SetIndentationGuides(not self.GetCtrl().GetIndentationGuides())
return True
elif id == VIEW_RIGHT_EDGE_ID:
self.GetCtrl().SetViewRightEdge(not self.GetCtrl().GetViewRightEdge())
@@ -352,6 +379,29 @@ class TextView(wx.lib.docview.View):
def _GetParentFrame(self):
return wx.GetTopLevelParent(self.GetFrame())
def _GetActiveCtrl(self, parent):
""" Walk through the MultiSash windows and find the active Control """
if isinstance(parent, wx.lib.multisash.MultiClient) and parent.selected:
return parent.child
if hasattr(parent, "GetChildren"):
for child in parent.GetChildren():
found = self._GetActiveCtrl(child)
if found:
return found
return None
def _FindCtrl(self, parent):
""" Walk through the MultiSash windows and find the first TextCtrl """
if isinstance(parent, self.GetCtrlClass()):
return parent
if hasattr(parent, "GetChildren"):
for child in parent.GetChildren():
found = self._FindCtrl(child)
if found:
return found
return None
#----------------------------------------------------------------------------
# Methods for TextDocument to call
@@ -401,6 +451,7 @@ class TextView(wx.lib.docview.View):
data.SetInitialFont(self.GetCtrl().GetFont())
data.SetColour(self.GetCtrl().GetFontColor())
fontDialog = wx.FontDialog(self.GetFrame(), data)
fontDialog.CenterOnParent()
if fontDialog.ShowModal() == wx.ID_OK:
data = fontDialog.GetFontData()
self.GetCtrl().SetFont(data.GetChosenFont())
@@ -614,7 +665,13 @@ class TextView(wx.lib.docview.View):
else:
return False
def GetMarkerLines(self, mask=MARKER_MASK):
retval = []
for lineNum in range(self.GetCtrl().GetLineCount()):
if self.GetCtrl().MarkerGet(lineNum) & mask:
retval.append(lineNum)
return retval
def GetMarkerCount(self):
return self._markerCount
@@ -807,9 +864,9 @@ class TextOptionsPanel(wx.Panel):
textPanelSizer = wx.BoxSizer(wx.VERTICAL)
textFontSizer = wx.BoxSizer(wx.HORIZONTAL)
textFontSizer.Add(fontLabel, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.TOP, HALF_SPACE)
textFontSizer.Add(self._sampleTextCtrl, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.RIGHT, HALF_SPACE)
textFontSizer.Add(self._sampleTextCtrl, 1, wx.ALIGN_LEFT | wx.EXPAND | wx.RIGHT, HALF_SPACE)
textFontSizer.Add(chooseFontButton, 0, wx.ALIGN_RIGHT | wx.LEFT, HALF_SPACE)
textPanelSizer.Add(textFontSizer, 0, wx.ALL, HALF_SPACE)
textPanelSizer.Add(textFontSizer, 0, wx.ALL|wx.EXPAND, HALF_SPACE)
if self._hasWordWrap:
textPanelSizer.Add(self._wordWrapCheckBox, 0, wx.ALL, HALF_SPACE)
textPanelSizer.Add(self._viewWhitespaceCheckBox, 0, wx.ALL, HALF_SPACE)
@@ -823,7 +880,7 @@ class TextOptionsPanel(wx.Panel):
textIndentWidthSizer.Add(indentWidthLabel, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.TOP, HALF_SPACE)
textIndentWidthSizer.Add(self._indentWidthChoice, 0, wx.ALIGN_LEFT | wx.EXPAND, HALF_SPACE)
textPanelSizer.Add(textIndentWidthSizer, 0, wx.ALL, HALF_SPACE)
textPanelBorderSizer.Add(textPanelSizer, 0, wx.ALL, SPACE)
textPanelBorderSizer.Add(textPanelSizer, 0, wx.ALL|wx.EXPAND, SPACE)
## styleButton = wx.Button(self, -1, _("Choose Style..."))
## wx.EVT_BUTTON(self, styleButton.GetId(), self.OnChooseStyle)
## textPanelBorderSizer.Add(styleButton, 0, wx.ALL, SPACE)
@@ -856,6 +913,7 @@ class TextOptionsPanel(wx.Panel):
## #'HTML', 'html',
## #'XML', 'xml',
## config)
## dlg.CenterOnParent()
## try:
## dlg.ShowModal()
## finally:
@@ -868,6 +926,7 @@ class TextOptionsPanel(wx.Panel):
data.SetInitialFont(self._textFont)
data.SetColour(self._textColor)
fontDialog = wx.FontDialog(self, data)
fontDialog.CenterOnParent()
if fontDialog.ShowModal() == wx.ID_OK:
data = fontDialog.GetFontData()
self._textFont = data.GetChosenFont()
@@ -909,6 +968,10 @@ class TextOptionsPanel(wx.Panel):
document.UpdateAllViews(hint = "ViewStuff")
if doFontUpdate:
document.UpdateAllViews(hint = "Font")
def GetIcon(self):
return getTextIcon()
class TextCtrl(wx.stc.StyledTextCtrl):
@@ -958,8 +1021,27 @@ class TextCtrl(wx.stc.StyledTextCtrl):
self.SetFontColor(color)
self.MarkerDefineDefault()
# for multisash initialization
if isinstance(parent, wx.lib.multisash.MultiClient):
while parent.GetParent():
parent = parent.GetParent()
if hasattr(parent, "GetView"):
break
if hasattr(parent, "GetView"):
textEditor = parent.GetView()._textEditor
if textEditor:
doc = textEditor.GetDocPointer()
if doc:
self.SetDocPointer(doc)
def OnFocus(self, event):
# wxBug: On Mac, the STC control may fire a focus/kill focus event
# on shutdown even if the control is in an invalid state. So check
# before handling the event.
if self.IsBeingDeleted():
return
self.SetSelBackground(1, "BLUE")
self.SetSelForeground(1, "WHITE")
if hasattr(self, "_dynSash"):
@@ -968,6 +1050,11 @@ class TextCtrl(wx.stc.StyledTextCtrl):
def OnKillFocus(self, event):
# wxBug: On Mac, the STC control may fire a focus/kill focus event
# on shutdown even if the control is in an invalid state. So check
# before handling the event.
if self.IsBeingDeleted():
return
self.SetSelBackground(0, "BLUE")
self.SetSelForeground(0, "WHITE")
self.SetSelBackground(1, "#C0C0C0")
@@ -1328,13 +1415,21 @@ import cStringIO
def getTextData():
return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
"\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`IDAT8\x8d\xed\x931\x0e\xc00\x08\x03m\x92\xff\xff8q\xa7JU!$\x12\x1d\
\xeb\t\t8n\x81\xb4\x86J\xfa]h\x0ee\x83\xb4\xc6\x14\x00\x00R\xcc \t\xcd\xa1\
\x08\xd2\xa3\xe1\x08*\t$\x1d\xc4\x012\x0b\x00\xce\xe4\xc8\xe0\t}\xf7\x8f\rV\
\xd9\x1a\xec\xe0\xbf\xc1\xd7\x06\xd9\xf5UX\xfdF+m\x03\xb8\x00\xe4\xc74B"x\
\xf1\xf4\x00\x00\x00\x00IEND\xaeB`\x82'
\x00\x015IDAT8\x8d\xad\x90\xb1N\xc2P\x14\x86\xbf\x02/\xe0\xec#\x18g\xc3\xe6T\
\x13':1\x18H\x98\x14\x12\x17G\x177\x17\x9c4a\xc5\xc0d0\xc2\xccdLx\x02^@+\t\
\xc1\x90\xf6r\xdb\xc6\x94\xe5:\\\xdbP)\xc5DOr\x92\x9b{\xff\xfb\xfd\xff9\xc6h\
l+\xbek.\x02\x00\xec\x99\x03\x80\xeb\xf8\\\x9d\x1d\x1bd\xd5hl\xab\xd7O\x15\
\xf7x\xa1\xfb\xeeq\xa4^>\x94\xba\xb8yRF.\xcf\xa6.D\xa0Nw\x18C\xad\xb2\x19\
\x9f\x0f\xca\x165\xd1V\xed\xebZj\x92\xc2\\\x04\xec\x02\xd5\x8a\x89\xb7\xd4\
\x97n\xa8\xe3?\x0f\x86\x08\x19dNP\x00\xf0\x96\xd0\x7f\xd0\t\x84\x0c(U-\x0eK&\
\xd3P\x8bz\xcdV6 \x8a\xed\x86\x99f\xe9\x00{\xe6\xb0\x13\xc2\xa0\xd3\xd7\t\
\x84\x9f\x10\xec\x9dTp\x1d\xb1=A\xa9j\x01\xc4\xb1\x01&\xfe\x9a~\x1d\xe0:Zu\
\x7f\xdb\x05@J/!(\xd6\x1bL\xde\xec\xcd\x00!\x03\xa6!\x1c\x9dVR\x9d\xdf\xe5\
\x96\x04\xd1au\xd3\xab3\xef\x9f_f\x03\xa2\xa5\x15\xeb\x8d\xc4\xc36\xe7\x18 \
\xa5G\xaf\xd9J\xb8f\xcd\xfc\xb3\x0c#\x97\xff\xb58\xadr\x7f\xfa\xfd\x1f\x80/\
\x04\x1f\x8fW\x0e^\xc3\x12\x00\x00\x00\x00IEND\xaeB`\x82"
def getTextBitmap():
@@ -1356,12 +1451,20 @@ def getZoomInData():
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\x00wIDAT8\x8d\xa5\x93Q\x12\x80 \x08D\xb5\xe9X\xee\xe9\xb7{\xd5Gc\xa9\
\xacX\xca\x1f\xa0\x8fE0\x92<\xc3\x82\xed*\x08\xa0\xf2I~\x07\x000\x17T,\xdb\
\xd6;\x08\xa4\x00\xa4GA\xab\xca\x00\xbc*\x1eD\xb4\x90\xa4O\x1e\xe3\x16f\xcc(\
\xc8\x95F\x95\x8d\x02\xef\xa1n\xa0\xce\xc5v\x91zc\xacU\xbey\x03\xf0.\xa8\xb8\
\x04\x8c\xac\x04MM\xa1lA\xfe\x85?\x90\xe5=X\x06\\\xebCA\xb3Q\xf34\x14\x00\
\x00\x00\x00IEND\xaeB`\x82'
\x00\x01TIDAT8\x8d\x8d\x93\xbbJ\x03A\x14\x86\xbf\xd9,\xc6\xd8E%`)VF[{\xc1v\
\xf1\x82\x8f\xb0\xb94\xda\xa5\x13\x11\x8b`\xa9h\x10F\xe3#H.\xa6\x15\xccKhg\
\x10\xc1B\x8bTF\x90\xc0X\x8c3\xbb\xd9\xcdF\x7f\x18\xf6\xec\x9cs\xbe\xfd\xe70\
+\x84\x93"\xacb\xc1W\xe1\xf7\xeb\xfa\x8d`\x82\xdcXcI\x8e\x02AM\x02\t\xe1\xa4\
(\x16|uz)y\x19\xc0\xc9\xdd;\x99\xee!\x00\xd9\xbd\x00\xd6\xaf\x95\xc7B\xac\
\x03\xd3\x1c\xd6\xc2t\x10\xf7\x13\x8e\xe0\x14\x0b\xbe\xa2$m\xf3\xca\xea\xacM\
\xe6\xd2\xc1\xcaWdl>#\x0e\x8c\xed\xe7n\x90|\xa8\x96m\xbc~ y\x04Z\xcd\x86\xda\
\xda\xde\xb1Gq\x00\xb2S\t\xfeB\x9aK\xa8\xb1\x0e\xf2\x15I.\xad\x0bo\x8f\xf4\
\x97\xab\xe7z\x88\x1f\xdf\xf0\xfa9\x1e\xe0x\x9eG\xbf\x16X\xcd\xb8Ar\xc6\xd5\
\x0b4\xd4\xf3\xbcd\x07F_\xc3 \x1e\x0c\xa3Y\x08\x9f\x1f~\xefA\xab\xd9P\x9dN\
\x07\x80\xddcI\xc6\x85\xf9\xb4.8\xabhwK\xbd+6\x16\xf5\xdeZ=%F\x00\xa0\xa7\
\x0b`@F\xc6\xf6\xd3\xc5&@\x0c"\xa2\xff\x82\x01\x85-\xb7\x9a\re\x00QH\x0c0N\
\x06\x1a\x85\xbcym}\x0f\xfe\x92\x19\xdc\xf2~\xdb\xee\xdd\xf7\xf4\xf3_\x0e\
\xa2N\xc2\xfa\x01MYp\xbc\xe4a\x0f\xa9\x00\x00\x00\x00IEND\xaeB`\x82'
def getZoomInBitmap():
return BitmapFromImage(getZoomInImage())
@@ -1375,11 +1478,20 @@ def getZoomOutData():
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\x00qIDAT8\x8d\xa5\x92Q\x0e\xc0 \x08C-z\xff\x13O\xd9\xd7\x16"\x05\x8d\
\xf6O\xa2\x8f"\x05\xa4\x96\x1b5V\xd4\xd1\xd5\x9e!\x15\xdb\x00\x1d]\xe7\x07\
\xac\xf6Iv.B*fW\x0e\x90u\xc9 d\x84\x87v\x82\xb4\xf5\x08\'r\x0e\xa2N\x91~\x07\
\xd9G\x95\xe2W\xeb\x00\x19\xc4\xd6\\FX\x12\xa3 \xb1:\x05\xacdAG[\xb0y9r`u\
\x9d\x83k\xc0\x0b#3@0A\x0c"\x93\x00\x00\x00\x00IEND\xaeB`\x82'
\x00\x01RIDAT8\x8d\x8d\x93\xbbJ\x03A\x14\x86\xbf\xd9\x04\x93\x90J\x0cj#Dl\
\xf4\x01\xec\x05\xdb\xc5\x0b>B\x92]\x1b+\xed,D\xb0\xb4\x08\x9afc|\x04\xc9\
\x85\xb4>\x84\x95`\x93\x80`\x15\xd8*\x98\x84\xc0X\xcc\xce\xde7\xf8\xc30\x97=\
\xf3\xcd\x7f\xce\xcc\na\xe4\x08\xabQ\xaf\xc9\xf0\xfc\xa5\xf3*X\xa1|b\xa3\xe5\
D\x81 W\x81\x840r4\xea5\xf9\xf0\xe40Y@\xf3+\xf8\xb8\xbe\x16\x8c\xdd\x96\x9d\
\n1\xf4\xc0\xdf\xdc\xb6\x01\xa8\xca\x19[\x05\xfc\x96%aY\x96\x0c\xdb\xae\xca\
\x99\xea7\x8b\x91@w.\xf9x\xbcL\xb8\xf0k\xa0O\x1e{\xd31Q\x1d\xdd\xaaC\xfa\xbd\
\xae<=;\xf7!F<\xd7,md\xc4\xf8\x0e\xf6\xaf\x1d\xb6\x8b*p\xa7\x0c\x95\xd0\x86\
\xc9\x02\xbe\xa7\xe9\x00\xc34M\xdc\x96MA\xa8[,y\xc8r>h\x00ow6\xa6if;\x98K\
\x95\xd6\xef\x12(\xc0t\x99~b8\x7f\xf0\xdeA\xbf\xd7\x95\xc3\xe1\x10\x80\x8b{\
\x87R\x1e*\xde\xd55oTq\xf7Fm\x8ew\xd5\xdaa\'\'"\x00P\xd5\x05\xd0 -m\xfb\xf3\
\xf9\x04 \x01\x11\xf1\x7fA\x83\xc2\x96\xfb\xbd\xae\xd4\x808$\x01H\x93\x86\
\xc6!?\xe6 x\xca\xab\xa4\x0bwp5\xf0\xd7\xdeG\xaa\xff\x97\x83\xb8\x93\xb0\xfe\
\x00\xc3\xa8ov\xfd\xe4\x9c\xa2\x00\x00\x00\x00IEND\xaeB`\x82'
def getZoomOutBitmap():

View File

@@ -99,6 +99,7 @@ class SVNService(wx.lib.pydocview.DocService):
_("Comment"),
_("SVN Log Message"))
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
retcode = True
message = dlg.GetValue()
@@ -141,11 +142,12 @@ class SVNService(wx.lib.pydocview.DocService):
dlg.Fit()
dlg.Layout()
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
retcode = True
username = usernameTxt.GetValue().strip()
password = passwordTxt.GetValue()
save = savePasswordCheckBox.IsChecked()
save = savePasswordCheckbox.IsChecked()
else:
retcode = False
username = None
@@ -195,6 +197,7 @@ class SVNService(wx.lib.pydocview.DocService):
acceptedFailures = 0
save = False
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
cert = certRadio.GetStringSelection()
if cert == _("Accept Always"):
@@ -206,6 +209,7 @@ class SVNService(wx.lib.pydocview.DocService):
acceptedFailures = trustDict.get('failures')
save = False
dlg.Destroy()
return retcode, acceptedFailures, save
@@ -238,10 +242,11 @@ class SVNService(wx.lib.pydocview.DocService):
dlg.Fit()
dlg.Layout()
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
retcode = True
password = passwordTxt.GetValue()
save = savePasswordCheckBox.IsChecked()
save = savePasswordCheckbox.IsChecked()
else:
retcode = False
password = None
@@ -253,10 +258,11 @@ class SVNService(wx.lib.pydocview.DocService):
def SSLClientCert(self):
dlg = wx.FileDialog(wx.GetApp().GetTopWindow(),
message="Choose certificate", defaultDir=os.getcwd(),
style=wx.OPEN|wx.CHANGE_DIR
message="Choose certificate",
style=wx.OPEN|wx.FILE_MUST_EXIST|wx.CHANGE_DIR
)
# dlg.CenterOnParent() # wxBug: caused crash with wx.FileDialog
if dlg.ShowModal() == wx.ID_OK:
retcode = True
certfile = dlg.GetPath()
@@ -321,8 +327,14 @@ class SVNService(wx.lib.pydocview.DocService):
if id == SVNService.SVN_UPDATE_ID:
wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
filenames = self.GetCurrentDocuments()[:]
filenames.sort(self.BasenameCaseInsensitiveCompare)
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()
@@ -346,7 +358,10 @@ class SVNService(wx.lib.pydocview.DocService):
_("Updated file '%s' is currently open. Close it?") % os.path.basename(filename),
_("Close File"),
wx.YES_NO|wx.ICON_QUESTION)
if yesNoMsg.ShowModal() == wx.ID_YES:
yesNoMsg.CenterOnParent()
status = yesNoMsg.ShowModal()
yesNoMsg.Destroy()
if status == wx.ID_YES:
doc.DeleteAllViews()
break
else:
@@ -377,8 +392,8 @@ class SVNService(wx.lib.pydocview.DocService):
view.ClearLines()
view.AddLines(_("SVN Update:\n"))
projects = self.GetCurrentProjects()
for project in projects:
project = self.GetCurrentProject()
if project:
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
for doc in openDocs:
if doc.GetFilename() == project:
@@ -400,7 +415,9 @@ class SVNService(wx.lib.pydocview.DocService):
_("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:
@@ -428,8 +445,8 @@ class SVNService(wx.lib.pydocview.DocService):
elif id == SVNService.SVN_CHECKIN_ALL_ID:
filenames = []
projects = self.GetCurrentProjects()
for project in projects:
project = self.GetCurrentProject()
if project:
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
for doc in openDocs:
if doc.GetFilename() == project:
@@ -447,7 +464,9 @@ class SVNService(wx.lib.pydocview.DocService):
_("'%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:
@@ -474,11 +493,12 @@ class SVNService(wx.lib.pydocview.DocService):
fileList.Check(i, True)
sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
buttonSizer = wx.StdDialogButtonSizer()
okBtn = wx.Button(dlg, wx.ID_OK)
okBtn.SetDefault()
buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
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)
@@ -488,6 +508,7 @@ class SVNService(wx.lib.pydocview.DocService):
dlg.Fit()
dlg.Layout()
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
@@ -544,7 +565,9 @@ class SVNService(wx.lib.pydocview.DocService):
_("'%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:
@@ -571,12 +594,13 @@ class SVNService(wx.lib.pydocview.DocService):
fileList.Check(i, True)
sizer.Add(fileList, 0, wx.EXPAND|wx.TOP, HALF_SPACE)
buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
buttonSizer = wx.StdDialogButtonSizer()
okBtn = wx.Button(dlg, wx.ID_OK)
okBtn.SetDefault()
buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
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)
@@ -585,6 +609,7 @@ class SVNService(wx.lib.pydocview.DocService):
dlg.Fit()
dlg.Layout()
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
@@ -654,11 +679,11 @@ class SVNService(wx.lib.pydocview.DocService):
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)
@@ -667,11 +692,12 @@ class SVNService(wx.lib.pydocview.DocService):
sizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
gridSizer.Add(sizer, 0)
buttonSizer = wx.BoxSizer(wx.HORIZONTAL)
buttonSizer = wx.StdDialogButtonSizer()
okBtn = wx.Button(dlg, wx.ID_OK)
okBtn.SetDefault()
buttonSizer.Add(okBtn, 0, wx.RIGHT, HALF_SPACE)
buttonSizer.Add(wx.Button(dlg, wx.ID_CANCEL), 0)
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)
@@ -681,6 +707,7 @@ class SVNService(wx.lib.pydocview.DocService):
dlg.Fit()
dlg.Layout()
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
@@ -737,7 +764,10 @@ class SVNService(wx.lib.pydocview.DocService):
_("Reverted file '%s' is currently open. Close it?") % os.path.basename(doc.GetFilename()),
_("Close File"),
wx.YES_NO|wx.ICON_QUESTION)
if yesNoMsg.ShowModal() == wx.ID_YES:
yesNoMsg.CenterOnParent()
status = yesNoMsg.ShowModal()
yesNoMsg.Destroy()
if status == wx.ID_YES:
doc.DeleteAllViews()
except pysvn.ClientError, e:
@@ -819,8 +849,7 @@ class SVNService(wx.lib.pydocview.DocService):
def ProcessUpdateUIEvent(self, event):
id = event.GetId()
if id in [SVNService.SVN_UPDATE_ID,
SVNService.SVN_CHECKIN_ID,
if id in [SVNService.SVN_CHECKIN_ID,
SVNService.SVN_REVERT_ID,
SVNService.SVN_ADD_ID,
SVNService.SVN_DELETE_ID]:
@@ -830,13 +859,20 @@ class SVNService(wx.lib.pydocview.DocService):
event.Enable(False)
return True
elif id == SVNService.SVN_UPDATE_ID:
if self.GetCurrentDocuments() or self.GetCurrentFolder():
event.Enable(True)
else:
event.Enable(False)
return True
elif id == SVNService.SVN_CHECKOUT_ID:
event.Enable(True)
return True
elif (id == SVNService.SVN_UPDATE_ALL_ID
or id == SVNService.SVN_CHECKIN_ALL_ID):
if self.GetCurrentProjects():
if self.GetCurrentProject():
event.Enable(True)
else:
event.Enable(False)
@@ -845,27 +881,20 @@ class SVNService(wx.lib.pydocview.DocService):
return False
def GetCurrentProjects(self):
def GetCurrentProject(self):
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
if projectService:
projView = projectService.GetView()
if projView.HasFocus():
filenames = projView.GetSelectedProjects()
if len(filenames):
return filenames
else:
return None
return projView.GetSelectedProject()
return None
def GetCurrentDocuments(self):
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
if projectService:
projView = projectService.GetView()
if projView.HasFocus():
if projView.FilesHasFocus():
filenames = projView.GetSelectedFiles()
if len(filenames):
return filenames
@@ -881,6 +910,19 @@ class SVNService(wx.lib.pydocview.DocService):
return filenames
def GetCurrentFolder(self):
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
if projectService:
projView = projectService.GetView()
if projView.FilesHasFocus():
folderPath = projView.GetSelectedPhysicalFolder()
if folderPath:
return folderPath
return None
def BasenameCaseInsensitiveCompare(self, s1, s2):
s1L = os.path.basename(s1).lower()
s2L = os.path.basename(s2).lower()
@@ -903,6 +945,7 @@ class SVNOptionsPanel(wx.Panel):
borderSizer = wx.BoxSizer(wx.VERTICAL)
sizer = wx.FlexGridSizer(cols = 2, hgap = 5, vgap = 5)
sizer.AddGrowableCol(1, 1)
sizer.Add(wx.StaticText(self, -1, _("SVN Config Dir:")), 0, wx.ALIGN_CENTER_VERTICAL)
@@ -919,18 +962,18 @@ class SVNOptionsPanel(wx.Panel):
dir = self._svnConfigDir.GetValue()
if len(dir):
dirDlg.SetPath(dir)
dirDlg.CenterOnParent()
if dirDlg.ShowModal() == wx.ID_OK:
self._svnConfigDir.SetValue(dirDlg.GetPath())
self._svnConfigDir.SetToolTipString(self._svnConfigDir.GetValue())
self._svnConfigDir.SetInsertionPointEnd()
dirDlg.Destroy()
wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(self._svnConfigDir, 1, wx.EXPAND)
hsizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
sizer.Add(hsizer, 0)
sizer.Add(hsizer, 0, wx.EXPAND)
svnUrlList = ReadSvnUrlList()
@@ -941,7 +984,7 @@ class SVNOptionsPanel(wx.Panel):
self._svnURLCombobox.SetStringSelection(svnUrlList[0])
else:
self._svnURLCombobox.SetToolTipString(_("Set Repository URL"))
sizer.Add(self._svnURLCombobox, 0)
sizer.Add(self._svnURLCombobox, 0, wx.EXPAND)
sizer.Add(wx.StaticText(self, -1, _("SVN_SSH:")), 0, wx.ALIGN_CENTER_VERTICAL)
@@ -956,6 +999,7 @@ class SVNOptionsPanel(wx.Panel):
def OnBrowseFileButton(event):
dirDlg = wx.FileDialog(self, _("Choose a file:"), style=wx.OPEN|wx.CHANGE_DIR)
# dirDlg.CenterOnParent() # wxBug: caused crash with wx.FileDialog
if dirDlg.ShowModal() == wx.ID_OK:
self._svnSSH.SetValue(dirDlg.GetPath())
self._svnSSH.SetToolTipString(self._svnSSH.GetValue())
@@ -966,15 +1010,19 @@ class SVNOptionsPanel(wx.Panel):
hsizer = wx.BoxSizer(wx.HORIZONTAL)
hsizer.Add(self._svnSSH, 1, wx.EXPAND)
hsizer.Add(findSSHButton, 0, wx.LEFT, HALF_SPACE)
sizer.Add(hsizer, 0)
sizer.Add(hsizer, 0, wx.EXPAND)
borderSizer.Add(sizer, 0, wx.ALL, SPACE)
borderSizer.Add(sizer, 0, wx.ALL|wx.EXPAND, SPACE)
self.SetSizer(borderSizer)
self.Layout()
parent.AddPage(self, _("SVN"))
def GetIcon(self):
return wx.NullIcon
def OnOK(self, optionsDialog):
config = wx.ConfigBase_Get()

View File

@@ -106,6 +106,8 @@ class ServiceView(wx.EvtHandler):
if (self._service.GetEmbeddedWindowLocation() == wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOM):
if ServiceView.bottomTab == None:
ServiceView.bottomTab = wx.Notebook(frame, wx.NewId(), (0,0), (100,100), wx.LB_DEFAULT, "Bottom Tab")
wx.EVT_RIGHT_DOWN(ServiceView.bottomTab, self.OnNotebookRightClick)
wx.EVT_MIDDLE_DOWN(ServiceView.bottomTab, self.OnNotebookMiddleClick)
sizer.Add(ServiceView.bottomTab, 1, wx.TOP|wx.EXPAND, 4)
def OnFrameResize(event):
ServiceView.bottomTab.SetSize(ServiceView.bottomTab.GetParent().GetSize())
@@ -125,10 +127,46 @@ class ServiceView(wx.EvtHandler):
sizer.Add(self._control, 1, wx.EXPAND, 0)
frame.SetSizer(sizer)
frame.Layout()
self.Activate()
return True
def OnNotebookMiddleClick(self, event):
index, type = ServiceView.bottomTab.HitTest(event.GetPosition())
# 0 tab is always message. This code assumes the rest are run/debug windows
if index > 0:
page = ServiceView.bottomTab.GetPage(index)
if hasattr(page, 'StopAndRemoveUI'):
page.StopAndRemoveUI(event)
def OnNotebookRightClick(self, event):
index, type = ServiceView.bottomTab.HitTest(event.GetPosition())
menu = wx.Menu()
x, y = event.GetX(), event.GetY()
# 0 tab is always message. This code assumes the rest are run/debug windows
if index > 0:
page = ServiceView.bottomTab.GetPage(index)
id = wx.NewId()
menu.Append(id, _("Close"))
def OnRightMenuSelect(event):
if hasattr(page, 'StopAndRemoveUI'):
page.StopAndRemoveUI(event)
wx.EVT_MENU(ServiceView.bottomTab, id, OnRightMenuSelect)
if ServiceView.bottomTab.GetPageCount() > 1:
id = wx.NewId()
menu.Append(id, _("Close All but \"Message\""))
def OnRightMenuSelect(event):
for i in range(ServiceView.bottomTab.GetPageCount()-1, 0, -1): # Go from len-1 to 1
page = ServiceView.bottomTab.GetPage(i)
if hasattr(page, 'StopAndRemoveUI'):
page.StopAndRemoveUI(event)
wx.EVT_MENU(ServiceView.bottomTab, id, OnRightMenuSelect)
ServiceView.bottomTab.PopupMenu(menu, wx.Point(x, y))
menu.Destroy()
def OnCloseWindow(self, event):
frame = self.GetFrame()
config = wx.ConfigBase_Get()

View File

@@ -2,7 +2,7 @@
# Name: UICommon.py
# Purpose: Shared UI stuff
#
# Author: Matt Fryer
# Author: Matt Fryer, Morgan Hua
#
# Created: 3/10/05
# CVS-ID: $Id$
@@ -13,18 +13,67 @@
import os
import os.path
import wx
import string
import ProjectEditor
import activegrid.util as utillib
import activegrid.util.sysutils as sysutils
import activegrid.util.strutils as strutils
import activegrid.util.appdirs as appdirs
_ = wx.GetTranslation
def CreateDirectoryControl( parent, fileLabel, dirLabel, fileExtension, startingName="", startingDirectory=""):
def CreateDirectoryControl( parent, fileLabel=_("File Name:"), dirLabel=_("Directory"), fileExtension="*", startingName="", startingDirectory=None, choiceDirs=None, appDirDefaultStartDir=False, returnAll=False):
if not choiceDirs:
choiceDirs = []
projectDirs = []
if appDirDefaultStartDir:
appDirectory = wx.ConfigBase_Get().Read(ProjectEditor.PROJECT_DIRECTORY_KEY, ProjectEditor.NEW_PROJECT_DIRECTORY_DEFAULT)
else:
appDirectory = wx.ConfigBase_Get().Read(ProjectEditor.PROJECT_DIRECTORY_KEY)
if appDirectory:
choiceDirs.append(appDirectory)
if appDirDefaultStartDir and not startingDirectory:
startingDirectory = appDirectory
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
if projectService:
curProjectDoc = projectService.GetCurrentProject()
if curProjectDoc:
homeDir = curProjectDoc.GetAppDocMgr().homeDir
if homeDir and (homeDir not in choiceDirs):
choiceDirs.append(homeDir)
if not startingDirectory:
startingDirectory = homeDir
for projectDoc in projectService.GetOpenProjects():
if projectDoc == curProjectDoc:
continue
homeDir = projectDoc.GetAppDocMgr().homeDir
if homeDir and (homeDir not in projectDirs):
projectDirs.append(homeDir)
projectDirs.sort(CaseInsensitiveCompare)
for projectDir in projectDirs:
if projectDir not in choiceDirs:
choiceDirs.append(projectDir)
if startingDirectory and (startingDirectory not in choiceDirs):
choiceDirs.insert(0, startingDirectory)
if os.getcwd() not in choiceDirs:
choiceDirs.append(os.getcwd())
if appdirs.documents_folder not in choiceDirs:
choiceDirs.append(appdirs.documents_folder)
if not startingDirectory:
startingDirectory = os.getcwd()
nameControl = wx.TextCtrl(parent, -1, startingName, size=(-1,-1))
nameLabelText = wx.StaticText(parent, -1, fileLabel)
dirLabelText = wx.StaticText(parent, -1, dirLabel)
dirControl = wx.TextCtrl(parent, -1, startingDirectory, size=(-1,-1))
dirControl = wx.ComboBox(parent, -1, startingDirectory, size=(-1,-1), choices=choiceDirs)
dirControl.SetToolTipString(startingDirectory)
button = wx.Button(parent, -1, _("Browse..."), size=(60,-1))
button = wx.Button(parent, -1, _("Browse..."))
allControls = [nameControl, nameLabelText, dirLabelText, dirControl, button]
def OnFindDirClick(event):
name = ""
@@ -35,54 +84,210 @@ def CreateDirectoryControl( parent, fileLabel, dirLabel, fileExtension, starting
name = nameCtrlValue
else:
name = _("%s.%s") % (nameCtrlValue, fileExtension)
path = wx.FileSelector(_("Choose a filename and directory"),
"",
"%s" % name,
wildcard=_("*.%s") % fileExtension ,
flags=wx.SAVE,
parent=parent)
dlg = wx.FileDialog(parent, _("Choose a filename and directory"),
defaultDir = dirControl.GetValue().strip(),
defaultFile = name,
wildcard= "*.%s" % fileExtension,
style=wx.SAVE|wx.CHANGE_DIR)
if dlg.ShowModal() != wx.ID_OK:
dlg.Destroy()
return
path = dlg.GetPath()
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)
parent.Bind(wx.EVT_BUTTON, OnFindDirClick, button)
def Validate(allowOverwriteOnPrompt=False):
if nameControl.GetValue() == "":
wx.MessageBox(_("Please provide a filename."), _("Provide a Filename"))
def Validate(allowOverwriteOnPrompt=False, infoString='', noFirstCharDigit=False):
projName = nameControl.GetValue().strip()
if projName == "":
wx.MessageBox(_("Please provide a %sfile name.") % infoString, _("Provide a File Name"))
return False
if nameControl.GetValue().find(' ') != -1:
wx.MessageBox(_("Please provide a filename that does not contains spaces."), _("Spaces in Filename"))
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 not os.path.exists(dirControl.GetValue()):
wx.MessageBox(_("That directory does not exist. Please choose an existing directory."), _("Provide a Valid Directory"))
wx.MessageBox(_("That %sdirectory does not exist. Please choose an existing directory.") % infoString, _("Provide a Valid Directory"))
return False
filePath = os.path.join(dirControl.GetValue(), MakeNameEndInExtension(nameControl.GetValue(), "." + fileExtension))
filePath = os.path.join(dirControl.GetValue(), MakeNameEndInExtension(projName, "." + fileExtension))
if os.path.exists(filePath):
if allowOverwriteOnPrompt:
res = wx.MessageBox(_("That file already exists. Would you like to overwrite it."), "File Exists", style=wx.YES_NO|wx.NO_DEFAULT)
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 file already exists. Please choose a different name."), "File Exists")
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)
flexGridSizer.Add(nameLabelText, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.TOP|wx.RIGHT, HALF_SPACE)
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|wx.TOP|wx.RIGHT, border=HALF_SPACE)
flexGridSizer.Add(dirControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, 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)
return nameControl, dirControl, flexGridSizer, Validate
if returnAll:
return nameControl, dirControl, flexGridSizer, Validate, allControls
else:
return nameControl, dirControl, flexGridSizer, Validate
def AddFilesToCurrentProject(paths, save=False):
def CreateDirectoryOnlyControl( parent, dirLabel=_("Location:"), startingDirectory=None, choiceDirs=None, appDirDefaultStartDir=False):
if not choiceDirs:
choiceDirs = []
projectDirs = []
if appDirDefaultStartDir:
appDirectory = wx.ConfigBase_Get().Read(ProjectEditor.PROJECT_DIRECTORY_KEY, ProjectEditor.NEW_PROJECT_DIRECTORY_DEFAULT)
else:
appDirectory = wx.ConfigBase_Get().Read(ProjectEditor.PROJECT_DIRECTORY_KEY)
if appDirectory:
choiceDirs.append(appDirectory)
if appDirDefaultStartDir and not startingDirectory:
startingDirectory = appDirectory
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
if projectService:
curProjectDoc = projectService.GetCurrentProject()
if curProjectDoc:
homeDir = curProjectDoc.GetAppDocMgr().homeDir
if homeDir and (homeDir not in choiceDirs):
choiceDirs.append(homeDir)
if not startingDirectory:
startingDirectory = homeDir
for projectDoc in projectService.GetOpenProjects():
if projectDoc == curProjectDoc:
continue
homeDir = projectDoc.GetAppDocMgr().homeDir
if homeDir and (homeDir not in projectDirs):
projectDirs.append(homeDir)
projectDirs.sort(CaseInsensitiveCompare)
for projectDir in projectDirs:
if projectDir not in choiceDirs:
choiceDirs.append(projectDir)
if startingDirectory and (startingDirectory not in choiceDirs):
choiceDirs.insert(0, startingDirectory)
if os.getcwd() not in choiceDirs:
choiceDirs.append(os.getcwd())
if appdirs.documents_folder not in choiceDirs:
choiceDirs.append(appdirs.documents_folder)
if not startingDirectory:
startingDirectory = os.getcwd()
dirLabelText = wx.StaticText(parent, -1, dirLabel)
dirControl = wx.ComboBox(parent, -1, startingDirectory, size=(-1,-1), choices=choiceDirs)
dirControl.SetToolTipString(startingDirectory)
button = wx.Button(parent, -1, _("Browse..."))
def OnFindDirClick(event):
dlg = wx.DirDialog(wx.GetApp().GetTopWindow(),
_("Choose a directory:"),
defaultPath=dirControl.GetValue().strip(),
style=wx.DD_DEFAULT_STYLE|wx.DD_NEW_DIR_BUTTON)
dlg.CenterOnParent()
if dlg.ShowModal() == wx.ID_OK:
dir = dlg.GetPath()
if dirControl.FindString(dir) == wx.NOT_FOUND:
dirControl.Insert(dir, 0)
dirControl.SetValue(dir)
dirControl.SetToolTipString(dir)
dlg.Destroy()
parent.Bind(wx.EVT_BUTTON, OnFindDirClick, button)
def Validate(allowOverwriteOnPrompt=False):
dirName = dirControl.GetValue().strip()
if dirName == "":
wx.MessageBox(_("Please provide a directory."), _("Provide a 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
return True
HALF_SPACE = 5
flexGridSizer = wx.FlexGridSizer(cols = 3, vgap = HALF_SPACE, hgap = HALF_SPACE)
flexGridSizer.AddGrowableCol(1,1)
flexGridSizer.Add(dirLabelText, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.RIGHT, border=HALF_SPACE)
flexGridSizer.Add(dirControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border=HALF_SPACE)
flexGridSizer.Add(button, flag=wx.ALIGN_RIGHT|wx.LEFT, border=HALF_SPACE)
return dirControl, flexGridSizer, Validate
def CreateNameOnlyControl( parent, fileLabel, startingName="", startingDirectoryControl=None):
fileLabelText = wx.StaticText(parent, -1, fileLabel)
nameControl = wx.TextCtrl(parent, -1, startingName, size=(-1,-1))
def Validate(allowOverwriteOnPrompt=False, noFirstCharDigit=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:
wx.MessageBox(_("Spaces in name. Name cannot have spaces.") % infoString, _("Project Name"))
return False
path = os.path.join(startingDirectoryControl.GetValue().strip(), projName)
if os.path.exists(path):
if os.path.isdir(path):
message = _("Project '%s' already exists. Would you like to overwrite the contents of the project?") % projName
else: # os.path.isfile(path):
message = _("'%s' already exists as a file. Would you like to replace it with the project?") % nameControl.GetValue().strip()
yesNoMsg = wx.MessageDialog(wx.GetApp().GetTopWindow(),
message,
_("Project Directory Exists"),
wx.YES_NO|wx.ICON_QUESTION
)
yesNoMsg.CenterOnParent()
status = yesNoMsg.ShowModal()
yesNoMsg.Destroy()
if status == wx.ID_NO:
return False
return True
HALF_SPACE = 5
flexGridSizer = wx.FlexGridSizer(cols = 2, vgap = HALF_SPACE, hgap = HALF_SPACE)
flexGridSizer.AddGrowableCol(1,1)
flexGridSizer.Add(fileLabelText, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.TOP|wx.RIGHT, border=HALF_SPACE)
flexGridSizer.Add(nameControl, 2, flag=wx.ALIGN_CENTER_VERTICAL|wx.EXPAND, border=HALF_SPACE)
return nameControl, flexGridSizer, Validate
def GetCurrentProject():
projectDocument = None
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
if projectService:
projectDocument = projectService.GetCurrentProject()
return projectDocument
def AddFilesToCurrentProject(paths, folderPath=None, types=None, names=None, save=False):
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
if projectService:
projectDocument = projectService.GetCurrentProject()
@@ -92,11 +297,22 @@ def AddFilesToCurrentProject(paths, save=False):
if path in files:
paths.remove(path)
if paths:
projectDocument.GetCommandProcessor().Submit(ProjectEditor.ProjectAddFilesCommand(projectDocument, paths))
projectDocument.GetFirstView().DoSelectFiles([paths[0]])
projectDocument.GetCommandProcessor().Submit(ProjectEditor.ProjectAddFilesCommand(projectDocument, paths, folderPath=folderPath, types=types, names=names))
if save:
projectDocument.OnSaveDocument(projectDocument.GetFilename())
def AddFilesToProject(projectDocument, paths, types=None, names=None, save=False):
if projectDocument:
files = projectDocument.GetFiles()
for path in paths:
if path in files:
paths.remove(path)
if paths:
projectDocument.GetCommandProcessor().Submit(ProjectEditor.ProjectAddFilesCommand(projectDocument, paths, types=types, names=names))
if save:
projectDocument.OnSaveDocument(projectDocument.GetFilename())
def MakeNameEndInExtension(name, extension):
if not name:
return name
@@ -106,23 +322,130 @@ def MakeNameEndInExtension(name, extension):
else:
return name + extension
# Lame
def PluralName(name):
if not name:
return name
if name.endswith('us'):
return name[0:-2] + 'ii'
elif name.endswith('s'):
return name
elif name.endswith('y'):
return name[0:-1] + 'ies'
else:
return name + 's'
def GetPythonExecPath():
pythonExecPath = wx.ConfigBase_Get().Read("ActiveGridPythonLocation")
if not pythonExecPath:
pythonExecPath = utillib.pythonExecPath
pythonExecPath = sysutils.pythonExecPath
return pythonExecPath
def _DoRemoveRecursive(path, skipFile=None, skipped=False):
if path == skipFile:
skipped = True
elif os.path.isdir(path):
for file in os.listdir(path):
file_or_dir = os.path.join(path,file)
if skipFile == file_or_dir:
skipped = True
elif os.path.isdir(file_or_dir) and not os.path.islink(file_or_dir):
if _DoRemoveRecursive(file_or_dir, skipFile): # it's a directory recursive call to function again
skipped = True
else:
os.remove(file_or_dir) # it's a file, delete it
if not skipped:
os.rmdir(path) # delete the directory here
else:
os.remove(path)
return skipped
def RemoveRecursive(path, skipFile=None):
_DoRemoveRecursive(path, skipFile)
def CaseInsensitiveCompare(s1, s2):
""" Method used by sort() to sort values in case insensitive order """
return strutils.caseInsensitiveCompare(s1, s2)
def GetAnnotation(model, elementName):
""" Get an object's annotation used for tooltips """
if hasattr(model, "__xsdcomplextype__"):
ct = model.__xsdcomplextype__
if ct:
el = ct.findElement(elementName)
if el and el.annotation:
return el.annotation
return ""
#----------------------------------------------------------------------------
# Methods for finding application level info
#----------------------------------------------------------------------------
def GetProjectForDoc(doc):
""" Given a document find which project it belongs to.
Tries to intelligently resolve conflicts if it is in more than one open project.
"""
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
projectDoc = projectService.FindProjectFromMapping(doc)
if projectDoc:
return projectDoc
projectDoc = projectService.GetCurrentProject()
if not projectDoc:
return None
if projectDoc.IsFileInProject(doc.GetFilename()):
return projectDoc
projects = []
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
for openDoc in openDocs:
if openDoc == projectDoc:
continue
if(isinstance(openDoc, ProjectEditor.ProjectDocument)):
if openDoc.IsFileInProject(doc.GetFilename()):
projects.append(openDoc)
if projects:
if len(projects) == 1:
return projects[0]
else:
choices = [os.path.basename(project.GetFilename()) for project in projects]
dlg = wx.SingleChoiceDialog(wx.GetApp().GetTopWindow(), _("'%s' found in more than one project.\nWhich project should be used for this operation?") % os.path.basename(doc.GetFilename()), _("Select Project"), choices, wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
dlg.CenterOnParent()
projectDoc = None
if dlg.ShowModal() == wx.ID_OK:
i = dlg.GetSelection()
projectDoc = projects[i]
dlg.Destroy()
return projectDoc
return None
def GetAppInfoForDoc(doc):
""" Get the AppInfo for a given document """
projectDoc = GetProjectForDoc(doc)
if projectDoc:
return projectDoc.GetAppInfo()
return None
def GetAppDocMgrForDoc(doc):
""" Get the AppDocMgr for a given document """
projectDoc = GetProjectForDoc(doc)
if projectDoc:
return projectDoc.GetModel()
return None
def GetAppInfoLanguage(doc=None):
from activegrid.server.deployment import LANGUAGE_DEFAULT
if doc:
language = doc.GetAppInfo().language
else:
language = None
if not language:
config = wx.ConfigBase_Get()
language = config.Read(ProjectEditor.APP_LAST_LANGUAGE, LANGUAGE_DEFAULT)
if doc:
doc.GetAppInfo().language = language # once it is selected, it must be set.
return language

View File

@@ -39,7 +39,7 @@ class BaseWizard(wx.wizard.Wizard):
class TitledWizardPage(wx.wizard.PyWizardPage):
def __init__(self, parent, title):
def __init__(self, parent, title=None):
self._prev = None
self._prevFunc = None
self._next = None
@@ -48,13 +48,22 @@ class TitledWizardPage(wx.wizard.PyWizardPage):
self.SetSizer(wx.BoxSizer(wx.VERTICAL))
self.MakePageTitle(title)
def SetTitle(self, title):
if not title: title = ""
self.title.SetLabel(title)
def MakePageTitle(self, title):
sizer = wx.BoxSizer(wx.VERTICAL)
title = wx.StaticText(self, -1, title)
title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
sizer.Add(title, 0, wx.ALIGN_LEFT | wx.ALL, 5)
sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.ALL, 5)
if not title: title = ""
self.title = wx.StaticText(self, -1, title)
self.title.SetFont(wx.Font(18, wx.SWISS, wx.NORMAL, wx.BOLD))
# the code below used to add a 5 pixel border in all directions
# but I found that the left margin was not aligned properly because
# only a few of the wizards made sure that pages themselves added
# the 5 pixel left border. If we still want to inset 5 more pixels,
# we should add a wx.HORIZONTAL sizer here to take care of it.
sizer.Add(self.title, 0, wx.ALIGN_LEFT | wx.TOP | wx.BOTTOM, 5)
sizer.Add(wx.StaticLine(self, -1), 0, wx.EXPAND | wx.TOP | wx.BOTTOM, 5)
self.GetSizer().Add(sizer)
@@ -115,689 +124,6 @@ import cStringIO
def getWizardData():
return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00}\x00\x00\x00\xfa\x08\x06\
\x00\x00\x00\x8c5HE\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00 \
\x00IDATx\x9c\xec\x9dy\x9cdUy\xf7\xbf\xe7\xdc\xbd\x96\xdegzVfe`\x86u\xd8WY\
\x14\x90M\x10\x11D\x11W4\x89Fc\xe4M\xd4\xa8\xd1\x88\x91D\r\x1a\xb7\x98\x98\
\xa8o\xd4\x08\xae\x08\x18EDE@\x04E\xf6}\xf6\xadgz\xaf\xae\xedn\xe7\xbc\x7f\
\x9c[\xd5\xd53=\xc3$\x8c\xd0\xf3v\xff>\x9f\xea\xea\xbau\xebn\xbf\xf3<\xe79\
\xcfr\x8e\xf8\xe4\'?\xa9\x99\xc1\xb4\xc15\xd7\\\x83\r\xf0\xc0\x03\x0f\xbc\
\xd8\xd72\x83\x17\x00\xdf\xbe\xe1Fq\xcd5\xd7h\xbb\xb1\xe1[\xdf\xfa\xd6\x8by=\
3x\x01\xf0\xed\x1bn\x04@\xbe\xc8\xd71\x83\x17\x013\xa4OC\xcc\x90>\r1C\xfa4\
\xc4\x0c\xe9\xd3\x103\xa4OC\xcc\x90>\r1C\xfa4\xc4\x0c\xe9\xd3\x103\xa4OC\xcc\
\x90>\r1C\xfa4\xc4\x0c\xe9\xd3\x103\xa4OC\xcc\x90>\r1C\xfa4\xc4\x0c\xe9\xd3\
\x103\xa4OC\xcc\x90>\r1C\xfa4\xc4\x0c\xe9\xd3\x103\xa4OC\xcc\x90>\r1C\xfa4\
\xc4\x0c\xe9\xd3\x103\xa4OC\xcc\x90>\r1C\xfa4\xc4\x0c\xe9\xd3\x103\xa4OC\xcc\
\x90>\r1C\xfa4\xc4\x0c\xe9\xd3\x103\xa4OC\xcc\x90>\r1C\xfa4\xc4\x0c\xe9\xd3\
\x103\xa4OC\xcc\x90>\ra?\xf7.S\x05j\xa7\xcf\xbb\xb6\xd7\xddM})P-\xbf\x97\xcd\
w\xdd\xfc~\xb2\xe3\xb7\x9eG\x81V\xd9\x19D\xf6#9\xf1\x1aZO.&\xb9\x18\xd1\xf2\
\xeb\xc9.\xb4\xe5\xd0\x8d\xf7\x89\x87T\x13\xf7\xc9\xce\xbd\xf3i\x9b\xf7\xa1\
\x15\x88\xc9\xe9\xddOHW@\xda\xf2\xb9q\xe7r\xc2MON\x9bB\xa2\x10\xc4\xd9\x1e6`\
\xa1\xb3o\xccQT\xd60tF\xae\xccN!\x18\'\xbd\xde\xf2\x9d\x95\x1dg\x92\x07\xaf[\
64\xfe\x97\xa0\x859\xbbl\xec\xa3v\xfaa\xa4\xc1\x12\xe0\x8c\xef\x9ff\xbbY\x80\
\xa5Ss\xfeD\x83\x10 ]\xb4\xcc\xae:\xdb\x14\x85u\xa4N\xf0<\x07t\x0c\xa20\xe9\
\x13\xd9OH\x87]%u\xf2oa\\ 4*k\x1ez\xc2\x1e\r\xc2\xc7\x05G\xeet\x04\x05\xda\
\xca\xa4\xb9\xf1\xbde\x9e,\xd2H\x90\xb0\x98\x8c;!\x0cI\xe6\xe0\x89aD\x02\x08\
\xf4\xce\x8f\xbb\xd1(\x04`7~\xd8\xd0J\x12\x8d R \xe2\x98\xbcT 5H\x1b\xa4h^\
\xb9\x06\x94\x80z\xa8\xf1\x1c\x1fGj\xd2$\xc4\x92\xbb\xef\xb9\xf7#\xd2w&f\xd7\
o\x1bhH\x94yo\xfc\xc6\xa5\xf1\xe0\x1bD\xb5\xeegag\x92\xdc\xd0"v\x93M-$\xc8`\
\xc2\xefZ\x05\xba\x01A\x82 F\x93"D\x84$E\x88\x86v\xf1\xb1\xd0\x08\xb2\xc6\
\xd4P"\x8d\x83\xc8\x04D\n*F\xa5)JZ\xd8\xb6\x87\x94\x16\x96g\x83\xca$]+\xe2zJ\
\xa4b\xb4t\xb0}\x1b\x04\xd8\x9e \x05R\x04\xca\xf2\xd1)\x14w\xc3\xfb~D:d\xca1\
{\x9f\xfc\x8e\x04\x99\xc0\xa0\xb0\x9a\xf4\x8cKY\xabdN8\x82nl\x91\x93\xf6\xcf\
\x91\x98\xd8\xc1\xec|U\x13\x8f\xdch\\2kt\x12\x81\x06\x92\x96\xe3\xb6\x9c]$\
\xa8z\x19)\x15\xb8\x16\xd2\xb2\x91\xd84\x0c\x810V\xb8\xae\xd3<\x8b\xb0M/\xa0\
Z\xce\xaa\x80zj4\x8d+\xd9#\xb3\xfb\r\xe9:\x93@\xb1\xd3\xf6\x9d?\x1b\x8e[\x1f\
\x87\xc1\xcej\xb8\xf5\xf7\xb2\xf5\x0b\xbd\xd3;\xe6A\xb6jbh4\xac\xf1&(\xb2\
\x06#qv9\x911\x0fB n\xd9\xa0\x0c\xf1\xd9Ae>\xa0A\xa3i0\xc6n\xd0\x02\x12GRJ \
\x95`\xc9\xac\x8fo9\xbe\x02*U\xd8\xb8i\x1b###tuu1\xff\x80^\xda\xbcIn\x98\xfd\
\x86\xf4\xbd\x18Y6\xc8n<\xd0\xe6\x937\xca\xbbU\x9dC\x0bY\x93\x11\xde\xd8\xa1\
y\\\x1ar\xd7\xfc\x9d\xd5\xd8eg=\xbf\x13\xe1\xbaq,\xb1\xd3=\x08\x89F\xa2\x04\
\xe6\x1d\x9b\x88\xc4\xd8iXXB \xb2k\x8e\x05Tm\xd3d$F_lZ\x17\xf3\xe4\xc3\x0f\
\xf0\xd8\xc3\x0f\xd0\xb7e\x1d\xe5\x91!.\xbe\xe8B.\xbe\xf0\x02\xda\x0b\xd6n\
\xb5\x12\xec7\xa4\xc3\xee\x89o\xe9\xe7w?f\x9b\xf0\xef8\xd9\x8d\xe1Mv\xfcV\
\xb5\xa1\xc9\xfax@Il\xdd\xf2\xb0Z\x89nj\x95\x16\xa3O\xec\xfa\xafi<\x99\xf4\
\xe24\xc7\x12\x8d\xf7\x10\x00;k\x04\x06\xe5\x10\xb6\xf7\xc1\xd6Q\xb8\xeb\xe9\
ml\x19\x1ef\xe33O\xb2y\xcdcD#}\xcciw9b\xf9B\x0e?\xe4P\xae~\xc3\x15\xb4\x07\
\x0e\xb6HI\xaa\x15r\xbe\x0f\xb2U\x1f\x8cc?"}o\xd1h\x04\xc20\xab\'6\x16\xa3\
\x8a\x13 \x1d\'\xb59\xe6\xb6h\x92\'\x94y\xa9l\x18\xa7\xedI\x1aUf\x9d\x8bL\
\xb34U\x819F\x9a\xd9\x01\xa6\x8b\xb5@\x0b\xb4\xb0\x891dG\x18\xb2\x13\xa0?\
\x86M[S\x1e{\xeaY\x9exr-\x9b7\xf532P\xa1R\x8e\x18K,\xca\xb9"\xb1\x10\x88\xa8\
\x84\xabl\x16.Z\xc6\xe9G\xaf\xe2\xd5\xe7\x9e\xc2\x91K\x0bx\x80\xd7\xb8\x7fK\
\x82\xae\x01\xfb\xf9\x90m\x97\xbe{\xb7;\xecl\xc7\x9bm\xf5(\xc2w]\xc2p\x0c\
\xdf\x95f\x1c\xab\x12P\nl\x1f\xa4\x83q\x81\x88\x89G\x91\nR\x05:1d\x8a\x86\
\xd2m4\x18\xd5\xb4\xc6+\xd5\x1a^\xae\x8d\xc1J\x85B>O\x04\xd44\x14\x04\xe4\
\x91\xa4\x11\x8cE\x1a\xbf(\xd8R\x82\xdf<\xd4\xc7\xedw\xdf\xc7#k\xd61X\xa9\
\x93\x08\x9b0\x11D1\xd8V\x8e\xc0mC\xdb6\xd54\xc2\xb2]\x92h\x0cG\xa7\xac\\\
\xb1\x8c\xd7]x&\x17\x9f\xd2C\x0e\xa8\xd7\xa0\x18`\xeeIG\xec\xde\xe44\xd8oH\
\xdf\x1b(\x05J)\xb46"i\xdb6B\x18\xbb\xd9q]@\x19)\x17"s^\xa4\x18\tU\x90jb)QBd\
t\n\xa4\x16\xc6\x95c\x8f\xabI\x9di\x84\xc68?\xd3\x19D@\x92\xf3\xa8\x01:\x9fg\
\x04\xd8Q\x87\x8d\x9b\xca<\xf3\xd0\xef\x91\x03\x9bx\xd7\x9f\\I\xe0\x19\xe9\
\x969\xf8\xc2W\xbf\xc3\xc6\xa1*\xa9\x97g8tHq\xb0\xfd\x80\xa0\xadH\x12\x0bv\
\x94C\x84V\x14\x02\x97\xda\xe0f\x8a~\xc2KO9\x8e\xb7^y\x06\x07\xf7\x18\x034\
\xaaAOn\x12\xaf\xe3\x1e\xa4d\xff!}\xa2\xcf\xb4e\xf3\xb8d\x0bI\xd3)\xd1\xb4\
\xdf5\xa4\x99W,\xd2\x8a\x9c\xe7\xd3\xe8A\x8d\xd4\x02\xc2&\xc4%\x15\xb2\xd9\
\xc7\x02XB\x90f\x8f(!#\x96\xf1.=e\\M?\xb5\x06\xd6m\x1e\xe0\xb1\'\xd7\xb1nc\
\x1fO?\xb3\x1e\xe9\xfa(m1\xb8\xeea\xbe\xf0\x81\xd7S\xaaA)\x821\x05\xff\xf9\
\x83\x87\xd98\x10\xe2v\x1f\xc0h\xa4\xc9\xe5<\xeaI\x8a\xd6\x82\x18\x1bK\x82\
\xe3iD*\xf0\xa8q\xf8\x01\x05.;\xffd^~\xd6\xa1t\xb9\x868W\x80\x93\x83\xb8^\
\xc7\xf62\x0f\xa1t\x19\xb7\\&\xc7\xfeCz\x03\x13\xfc\xcf\x06\n\xa8\xd5C\x10\
\x16\x96ec\xb7v\xbfb\xdc\xa5\x19\'\x16\x81\x03B%\xe3\xbf\xb4l\xd2\xd4#\xb1lB\
\xa0\x8e!\xb2q\x1a/{/\x01\xdb\x87a\xe3\x96:k7la\xdd\xc6\xed\xac\xd9\xb4\x95\
\r\x9b\xfb\x19\x18.#\x1c\x9f(\x11X\xd2\xa3\xad\xb3\x8b\xb1\xb4\x83\x9c\xc8\
\x11F1G\x1c\x7f&\xe7_p4\x0eP\x08`\xcdZ\xf8\xfe-?\x03\xa7H-\xb6(\x8d\x96!/\
\xc0\xb2 \rI*#t\xe4\x02\x0e9\xf0\x00\x0e9\xe8 \x96\xcf\xf29\xe7\x88v\x96\xf4\
@\xde5\xd7\x13\xd7S\xb4N\xb0\x02\xc78o\xa0\xe91L\x91\xa4\x18w\xd4d\xd8\xffH\
\x87I\x89\xf7}\xaf\xe9\xa1\x0fS\x9a\x8d\xbd\xe1\xf4J\x00\x1c#E\x8e\xc8\x91VK\
\xd8\xb9<H\x8fjj\xc8.\x01C\x11\x0c\x96\xa0\xaf\x0f\xd6\xaeY\xcb\xfa\'\x1fc\
\xf3\xb6a\x9e\xd9P\xa7\x96\xfa\xc4\x1a\xb4\xe5 \xbd\x1c\xca\x0e\x88\xd4\x01D\
~\x82\x9f\x0b\x08+\xa3H\xa1H\xeb)a\x1a\x11\x96C|Wr\xc5U\xafaL\x81-\xe1\xd9M\
\xf0\xb1O\xfc3\xb1\x16T\xeb\x11\xa9\x8e\xe8\x9e\xddK=\x0e\xe9l\x0f8\xfa\x88\
\x838\xe4\xc0\x85Twl\xa6o\xe3\xb3\xac\xe8\xacp\xd9\xcbz\x99\xef\x80\xa74Q\
\xb5\x8e\x1bx\xb8\x9e&\xac\xd4Ij1\xb6\xefg\xcfc\x9c\xf0=\xad\xbb\xb6\xff\x90\
>\x89zou\x89\xb6\xc6\xc0\xa45\xd1KU\x8f\xc1\xf1!V0:\xa2\x98\xdfe\x11\xe9\x1c\
\xb6\xf6\x88\x04\x8c\xc5\xf0\xf1\xcf\xdc\xcc\x9a\xfe*Ol\x1ab\xdbp\x19\xa5\
\x149\xd7\xa1\xe0YH\xe9\x93x\xb3I\x94G\xac\x04\x89\xb0H\xa5\x8d\xd2\xb69\xab\
L\xa8%\x80\xeda\xb9\x16\xae\x15\x91ss\xc8\xa4\xca\xca\x15\x8b9s\xb5i|)p\xeb\
\xed?\xe3\x91\'\x9e$\xd7s A\x10\xd0\xb3`\x01\x0b\x96-\xe3\xa4\x13\x172\xb0\
\xbd\xca\x03\xf7\xdc\xc67o\xfb:g\x1e\xb7\x8a\xbf~\xeb\xe5,\xea4\x12\xab\xeb\
\x11x\x127pH\xc3\x1a\x96e\xe1\xe5\xf3\x00\xd4jU\x82\\\xa1\xd9\xd5\xed\xd9a\
\xbd?\x91\xfe\x1c\x08\x13\x8c\x07U\x8eG6c\x05\xa52\x8cU\xe1w\x8f\xad\'\xac\
\xd5\xa9\xecX\xc7;\xdfr.\xb6\xef\x11i\xa3\xca\xcb\x11\xfc\xe0\'wSq\xbbH\xfcY\
\x04\xdds\xf0\xfd\x00tB\xbdZ"\xac\x8d!\xdda\x12K\x92Z\x0e\xb1\xb4A8\x803\xee\
t\x11\n\xe9(\xe2\xd2\x10\x92\x10\x9dT\xe8is\xb9\xf4\xa5\'\xe3c\xba\x89\xbb\
\x1ex\x8a[~|\x0b\xc7\x1cw4+\x8fz)\x87\x1e\xb3\x98\xa7\xd6\xc1\x1f\x1e|\x8c\
\xcf}\xea\xdf\x98\xdd&9\xf3\x84\x83\xf9\xf8\x9f}\x88\xc3\x16\xd8\x88z\r\xbfV\
%\x08|\x94\xe3\x92*\x0b\xcb\x12X\xbe\xa1-M5B\x08\x82\\\x9b\xb9\x04\xc8\\\xcf\
0\x1eQ\xdc\x15\x93lm\x98*\xd9a\xf4\xf3h\x17B\x8d\x8f\x93[%U\xb4^\xd8\xc4KiHh\
CE5=ir|\xef\xc66\xd5\xb2\xef\xb3[C\xfa\x06\x86Y\xb3~\x0b\xeb\xd6of\xeb\xb6\
\x1d\xf4\x0f\x8eR*U(\xd7b\x84\x9d#\x8d#t}\x84K_{.s\x03\x9a\x1e/\xed\x80\xdf9\
\x87\x9ah\'T\x1e\xd5jL\xa9j\xa2Z\x16\x02\xcb\xf1\x11B\xa1\x85BK\xc5\xb8\x0f=\
\xc9\x1c.\x1a\x92:mmyjcezr\x16A\xa4X>\xbb\xc0\xa5\xa7xh\x8c\xb17&:\xf9\xd8\
\xa7?\xcb\x83\x0fo\xe4\xde\xdf=\xc1\xf7n\xfa\x11\xe5\xb1*s\xe6t\xf2\xfa\x0bN\
\xe6e\xa7\x1c\xcaQK \x07\xb8h\x02_e!\xb42\xd2\x9fE\x9ay\xf0\x1b\xae i\x8dG\
\xda&\xf4vZ\xed\xe4j\x9c\x88I\x18\xadS+\xf5\x13\xb4u\x9a\xa7\x11+\x90.qX\xc3\
q\x1c\xb0\xed\x89$\xa6\xb1\x19+9\xce\xc4\xc34\x9d\x16\r\x96$$\nt\x089\x87zX\
\xc6\xf7\xbd\xe6-\x84\xf5\x08\xcf\xcb14\x92\x90\xef4FU5\xfb6\x02b\r\xfd\x03\
\xb0q\x8bb\xd3\xe6\xadl\xee\xdb\xce\x86-[\xd8\xb8e\x07\xdb\x07\x86I-3\xe4I\
\x85\x8b\xd6\x0e\xa9\xf0Q\x14\x8cq\x17\x80\xadS|\x1f\xea\xf6\x08\x1b\xab0+\
\x00\'5\xb6S\xa5\nc*\xa2\xee\x08\xa4c\x91\x92\xdd\xb7\xd2\xa4\x96\xc0q\x8b\
\xe4,\x9fr\xb5N\x12\xa6\xe0f\x11\x0f\x9d\x00\t\xc2\xd1\x14r\x16#\x1b\x1ea\
\xf9\xfcY8\xb5a\xda\xa9\xf1\xc1\xb7\xfc\x19\x1d\t\x945|\xe5\xc7\xdb\xb9\xfd\
\xfe\x87x\xfa\xf1\xc7\xb1\x89\t\x1c\xc5\xbcN\x8fs^y\x06\xaf:\xefx\xe6w\x185\
\xeeb\x0emi\r\xda5B\xe7\x18\x15&E\xab\xaf\x7fw\xd8\xf3pmr\xd2\xb5"h+\x00)i=F\
Z\xdd\xa0\xc0\t\x82\xa6\xcb1\x8d\x12\x94RH)\x91\xb6\x8d\xd8\x99p\x1aWe\xa1\
\xb5y6q\x15\xbc\xbc$J\x03,\x01\xa9\xe5\x91 \xb3\xc0\x81\x8d\xed;\x94cp:m\x1e\
\xdb\n\xf7=\xbe\x9d5[w\xb0cd\x98\x07\x1f\x7f\x92\xa1\xe1\x1aa\xa2\x89\x12\r\
\xc2A:>J:\x84\xa9K\x98\xce\xc6q\xf3(lt6\x866\xc1\x8c,\x14\xa2S<\x15\x92\x88\
\x94\x9a\xcc1\x10\x92Y\xb7\x11\x12\xd7\xd8\x00\x96$L5:\x8a\xc0\xb5\x91\xb96\
\xa4\x94\x88\xa4J\x12%\x0c\x8dV\x10\x96\x8b\x1bxX\x96&Ik\xc4\xe1($\x15$!\xb6\
/9\xe2\xc0\xf9\x1c\xb2t!\xe7\x9dv*\xa7\xaf\x0ep1\xae\xd4k\xaf\xbf\x95\xef\
\xdc\xfd4\xe4;\x11B\xe2\xdb\x92\xd3O<\x82\xab.=\x9bU\x0bhz\xd3LzG2\xae\xe2\
\xb4e\x04O\xb4<\xd2=\x12\xdeB\xfc\xff\x88t,\xd2Ha9\x0e2(\xa2\xb4D\x0b(\x95\
\xc6\xb0\x1d\x85\xeb{\xd8\x9e\x8bF6;\x82\x18\xe3\xc0L1\t J\x9b8\xbf\xc4\xb8"\
\x95\x03\xa2\xc3\xec[Q\xc6C\xd8\x19\x04\xa4\x80EL\x14\xc3\xc3On\xe0o\xae\xfd\
<\xc3\xaa\x8b2\x05\x06*\x11N\x90\xa3\xad\xdd\xa7\x16\xa6DV\x8e\xc4\x92h\xc7A\
K\x0f!=\xb4\xf4A\xd9\xd8\xbe \x8e3/\x94H\x91$\x08\x1d\x9b+\x14)B$h\x99\x12\
\x93\xa0\xec\x1aC\x95\x86\xf2\t1\x8a\x14t\x9cP\xc8\xe7\xf1\xbdv\xa2\xd4\xa6T\
IH\xc2\x9a\xb1\x87\x1d\t\xf9<\xaeoc\'U\xc2\xd1\x1dX\xd1(+f\x179\xee\xf0\xc38\
x\xd9B\x0e_u K\x96\xc0C\x8f\x86<\xfa\xd43\x14f\x1f\xca\xfc\xf9\xf0\x85o>\xc1\
-\xf7>\x86\xa5\x13\xba\x04\x9c\xf9\xd2\x13y\xcd\xabN`Yo\x96$\xa3L(t\xe7\xc8\
\x19\x8d\x86\xfb\xdc\x0c\xff\x8f\xb1+\xe9J#\xdd\x80\x08\x9b\x14\x17%`\xf3vX\
\xd0[\x04\x8c[\xa3\xc2\xf8P\xa8\xd1\xc35-h\x01\x89\x80\xb2\x82\xad\xdb\x15\
\x1b\xd6od\xeb\x8e\n\x8f=1\xc4\xb6\xbe\x01|_Q\xf4k|\xfa#WR\x04\x02\x1c\\\x07\
\x0e9l\x11\x1d\x0b\x0e\xe2\xee\xfb6RX\xb8\x84\xb0\xdd&\x12\x82\xca\xe8\x08\
\x96\xe5!\xdc\x1cJ\xbahe\x11k\tI\x16\x96\xd4\x99w\xdbvi\xf8\xc2\x15`i\x85 \
\xc5R\x86x-\x14\x89V`[\x0c\x8fVQ\xe4\x8c\xe7N@\x1c\xa6\xf8\xaeK-\xac18ZC%\
\x0e\x8e_ \x97\x0f\xf0dB\xaa\xea\xa4n\x85\xf2\xd80\x9ecq\xd6)\xab\xb9\xf8\
\x9ccY\xb5\x0c\xfa7\xc1\xe3\x0f\xaf\xe5_\xbf\xf4\x9fl\xdc\xbc\x89TH\xbe\xf4\
\xd5\xf7\x91\xcb\xc3\'\xbft\x1f7\xdd\xfa\xdf\xf4\x14s\\\xf6\xb23\xb8\xfc\xdc\
\x13Y2\xcf<\xbb\xb0\x06\xb9\xc0\x0c\xe3\x06\xb6\x0f1\xaf\xb7\xab%\xa6\xcfN\
\xf1\xf6?6\xe9\x96c\xe2\xb3J"\xa5!\xb9\xad\x17\x06ScLI1\xee\x8d\x1a\xac\xc0\
\xb6~\x18\x1c\x89x\xec\xc9g\xd9\xd6?\xc8\x9aM[\xd8\xda\xb7\x83R\xa5F\xaa\x05\
\x08\x0b%|:\xe7\xae`\xc3\xfa!\xda\xf2\x1a/\x1d\xe0\xee\x87\x868\xfb\x88.\x12\
m2\x85F+p\xe69\x17\xf2\xcbM?\xa6\xe6t\x91\xc6\x02\xb7\xad@4\x9c\x82NI*\x0eX\
\xbe\xb1)l\x99\x89\x87\x00\xdb1\x07\xa8\xd6\xccU\xa5\xc6x\xd4\xa9m\xd4\xbbv\
\x10J\x81%\xd0i\x88\xe3\nF\x86B$9,\xed\x9b\xe0G=\xa4^\x19\xc5o\xeb\xa43\x9f#\
\x8e4\xb5x\x8c4\x1eF\x11\xe3\x881\xce;i5\xa7\x9dq\x11\xc7\x1d\xe2R\x03~~\'|\
\xe8\x1f\xefe\xed\xba\xadDaB\x14\xa6X\xf4\xf0\xe9\x7f|\x1b\xddy\xf8\xfa\x7f\
\xfd\x81_|\xf7\x8b\xbc\xf3u\x97p\xc5+_\xc1\xb2Nsyv\n\x8e\x05E\x17\xa2\xb1\
\x18\xc7s\x987\xab\xab%N\xd4 }\\\x90\x1aMa_q\xbf\x0b\xe9\x89\x82XZHiS\x03\
\x9e\xd8\x92\xc5o7E\x0c\x0e\x0c\xb1i\xcbV6n\xddF\xdf\xc00\xa3\xa5\n\x95zB=Q\
\xe0\xfa$Z\x92`\xa1\xadnd\xe0\xa1\xb4 JS\xa2\x04J\x1b\x07 \xd7A)\xad\x90\x17\
yn\xf8\xe1m\xbc\xec\x88\xd7\xe0d\xb1\xeaYy8\xf5\x84^\xe6\xfc\xb0\x9b5[F\xc0i\
#\xaaE\x90@\xd0\xdeI\xb9&\xd1"\x1b\x17\xa7\n\x92\xba1 E\x96\x15\xe8f\xca13\
\x1e\x95e\x11)3`\xb7\xb4\xc2C b\x85\xeb\xd9\x8c\x0c\x8c!\xe8D\xe2\x98\x04\n\
\xad\t<\x97rRg\xa4RFk\xcd\xbc\xf9\x0b9\xe5\xe4\xe39\xeb\xb4y\xacZ`\x0e\xfd\
\xdb\xdf\x0f\xf3\xc1\xeb~\xc6\x83\x8f\xaeed\xccBY9\x90\x05\xa4\xb0(\x14\xbb\
\xb9\xfa\xaa\x0b9j\x05|\xee\xf3\xbfbx\xeb\xa3\xfc\xfc\x86\x7f\xa1\xb7\xd3\
\xa1\xcdR\x08\xa5Qq\x8c%$\x8e4n57\xe7Ld\xb6\xa5\xd3\x9e,\xffn_a\x17\xd2\x85t\
PH"`\xfd(\xfc\xf5G>\xc7\xe6\xed5\xea\xa9\x0b\xc2EH\x1b--\x94\xb4\xd0\xf8\xc4\
Z\x92h\x89\'\x0b\xc4\x1ab%P\t\x90\xd8Y\x9fd\x81\xa3\xc0\xad\xd3\xde\x99\xa7V\
\x95\xe4l\x87\x87\x1e\x7f\x96\xdb\xefz\x86\xd7\x9cr \xb6N\xb0\x84\xcd\x1c\
\x17\xce>\xfe`\xbeq\xdb\x1fH\xf3\x01\xd50\x81(\xa4\x16V\xd1\xb6\x9b\r\x01\
\x95I:\xd1\n\x07\x10Z R\x8d\xaa\'&\x94)e\xa6\x92\xa4\xe94\x85 M4Bid\x92`\xa5\
\x16\xa5\xc1a,\x0e\xc0B\xa0\x05x\x8e\x8b\xef\xfb,]y8\'\x9dx<\xc7\x9f`3;\x075\
\xe0\xae_\x0e\xf1o_\xb9\x83\xc7\xd7m\xa5\x16\x83\x14\x1e\xbe\xd7\x85\xdd&(\
\x97*X\xc9 m\x81\xc79\'\x1c\xcf%g\xc2\xb3\x0f\xa7\xbc\xe9\x15\xa7r\xe0\xfc\
\xd3(ZP/\xf7\xe1\x15r\xe6z\xbc,\xf6\x1a\xd7M\xc3u<v\xee\xc9\x1b\xe3\xaf?BW\
\xbe{\xd25PU\t\x15i\xb3}(dc\xdf\x00V~\x01R\xe4\x89\x85\x87V\x90jE\xaaMF\xa9\
\x906\xc2\xb2)\xc7\tH\x1b\xe1\xd8\x08a\x99\xfeR7\xee@c;\x92\xd1\xfe\x1d\xa0S\
\xc6|\x89R\x1e\xdf\xbb\xf5W\\z\xca\x81\xd8\xb1"\t+\xcc.\xe6\xb9\xfaU+\xb8\
\xf9\'w\xb0\xb9\xaf\x0c~7^{\'q\x9a\x18\x89N\xe3,\xb6\r\xae\xd48B#u\x8a\x16\
\x1a\xad5If\xb1\xa7\xcd\xfc\xb2\xcc\x18"\xc1\x11\t\xe81\xac4$\xac\x0c\x98\
\x1b\x95\t\x02\x1b\xcfs\xf8\xfa\xd7\xde\x8b\xb6\xa1\x94\xc0\x9dw\xc3On\xff\
\x15\xeb\x9e}\n+)!\xbd\x1c\x83tBl,\xae$\xb5\x10a\x05W\x8fq@O\x91C\x16w\xf3w\
\xefX\x89\x03,9\xdc\xc2V\x10HP\xe1\x18\x85|\xc1$5\xca,\x04\x9b&\xa6\x8f\xb4]\
P\x19\xe1I\xc6\xc4N\x03n\x89B5\xd5\xc0\xbe\xf3\xa3\xedr$\x05\xf8\x99j\x7fv\
\xcd::\xba{\xd8<\\\'\xc9\x07\x84I\x16\x9dr\xb2\x9f\xa5\x11\xd8\x8a \xb0\xa0\
\x1a\x03\xa1q\x056.\xbey\x03)\xc9H\x89\xe2\xac^\xc6*\x11\xf58&\x8a\x03\x9e\
\xdd^\xe7\xa6_<\xc1\xa5g\xac\xc4\xb3lT\x0cK\np\xf1\xa9\xab\xb8\xf1\x97O0\x10\
\x8f\xa1\x9c.T\xea\xc0X\x84W(\xa0uLT\x1b%t \xe8\xcc\xa3I)\x8d\x8e -\x10B \
\xb0\xf0\xb4\x83T.:\x15$\xb1\xc6J\xca\xf8n\x15\xcf\x1b!\xc8\x179\xe2\xd0\xa3\
\xb0t\x02\xa2\x8a\xa4\x8db\x11~\xf5 |\xef\x8e\'\xf8\xc3CO\x90*A\xac%q\xadH \
\x1d,\xbf\x13T\x07\xe4|`\x8c(\x1d\xc1\xaa\x0f\xd3\xa6\x868\xe3\x88\xc5\xbc\
\xe7\xcd\x172\x0b\xf0P\xc6\xb0\x14\nR\x89t\x023\xce\x16\xc6\x85\xa4Q\x08+K\
\xd4\xd0r|d\xe50\xd1y\x95\xa5~\tT\x96\xe8\xd5`f/\xd2\xc6\xfe7\xa4\x9b\x8b\
\x968\xc0\xdc9\xb3\x19\x1d\x1a\xc4\xcb\xcd\'t\xbcq\x07\x8c\x94F\xf2\x92\x04\
\x92\x88Z\xb9d\xbc\x1c\x00\xc4\xd9\r\x8c{\xdb\xa4V\x88\x9cO\xa5n\x86HX.:\xef\
\xb2v\xa8\x8f\x9b\xefy\x9aSOXI\x87\x96\xe4|\xf0\x81W\x9fs"\xb7\xdf\xfb0\xa3Q\
\x88\xf0\x02b\x95\xd0\xd6\xe5\x13U\xcb\x08\xa9i\xef\xea\xa0\x1a\xd7\x18\xd9\
\xbe\x03l\x81\xd7\xd5\x0eI\x84\x8e\xebx2\xc5S\t\xb5\xd1\xad$\xd5\x1aK\xe7\
\xccc\xc5\xaaE\xac8x\x01g_\xb0\x8a\xdeb\xc3\x01\x12C\\\xa3\x9c\xd8\xdc\xf3\
\xdbM\xbc\xf7c\xdf\xc6\xed=\x84Z5!\xad\xc6\xb8\xddsi\xebj\'\x1c\xd9Ne\xb8\n\
\xb3z!\x89!\xa9b\xa7c\x1c~\xd0\x02\xdep\xf1\xe5\\|\xf2B\xda\x01c\xde\xc5Y\
\xb6\x8dm\x98\xd4Y\x9f-%\xa9\x90\xa4Y~,\x183\xa4\x99\xb65A@\x1a<4\xbel\x06z\
\x9f\'\xd5\xe3\xd8\x8d\xce\xd0\xd8\x08\x96.\xe8\xc2\xd61\xa9V$ue,\xe5F\x02\
\xa2\x94&\xf1^H\xd3GY\xc6h\x92Z \xb2\xa1\x90\xcc\xd2\x91\x94\xb0\xd1\xb6\x8b\
\x8a1\x16x\x02\xa2\xbd\x97j%\xe1\x96\xfb\xb7r\xd2\xfdc\\\xf1\x92"hc\x9b\x1d\
\xba\xcc\xe1\x94\xa3W\xb1\xe9\x97O\x12bAm\x8cR8\x8a\xd45\x94v\t\xcb\x1e\xc2.\
`\xb5-@\x0bIRMpH\xb0b\r\xe10\xedm\x82W\x9eu\x14\xe7\x9fy\x12\x07/\xcf\xa1$X\
\x1e\xac/\xc1\xadw\x8e2\xf0\xec\xef\xf8\xe8kO\xa6\xcd+\x1089\xfa\x06\x86\x99\
;o!}\x89&\xd7\xd6\x8e\xd5\xe51Z\xa922R!\xb0S:\x9c\x94t\xe01T\\a\xc5\xf2\xf9\
\x9cz\xc2I\x1c\xb1j\x01G\xae* \x80\x11@\x11\xe0b\x9b<V\x89q\xac\xb4D\x83\xb4\
\x84$\xa3\xbc\xc1\xb1\x16\xe3!}\xa9w\xd3\x8fk\xd8%\xa9\xf2yb\x17\xd2m\x01u\
\x15cI\x97\x9ev8r\xd5r~\xf5\xc8v(ta\xfcF\x1a\xa2\xd0\\\x8c\xef\x984#\xdb\xc9\
2\x15\x1aw\xa8\xb2\xb4b\x95\xa5\xa9IT"\x8d\xe1\x92\nH4\x1a\x17\xd99\x8f\xd1\
\x81\x88/\x7f\xf7\x0eN;\xf5"z\x04\xe8\n\xe4\np\xf9Eg\xf2\xe3{\x1eckm\x14\x02\
\x17\x94C\xb1\x98\xc3q\\\xc2ZD\x1a\xd5\x90i\x88\x8e#\xa2\xea\x18+\x97/\xe1\
\xd4\x13^\xce\xa9\'\xceb\xf1\x02\xe8q\x8dl\xf4\r\xc2\xe3O\xf7s\xdbo\x9e\xe6\
\xb7\x0f?\xcd\xe0\xd8(\x87-\xc8\xe1\xbe\xf9\xa5$qH\x05X\xbba+\xa5j\x8d\xb0\
\xae\t\xed\xd0\xe4\x98\xc5\x11N\xc1\xa1-\'H\xc7\x06\x99\x9b\x83\x8b\xcf9\x93\
b\xde\xa6P\x80\x97\x1c^\xa0-\xcbP\xcd5\xf9\xb1H\x11H\xa4I\x89nI\x9f\xddY9\
\xb7\xfa\xd0M\x8atK\x97\xde\xd4\xe8YU\xcd$\xa1\xe4}J\xbaQ91\x0e.\xbe\x86W\
\x9c\xf5\x12\xee{\xe4[&\x7f\xcb\xf6\x11Bd\xbe\xe9\xd8T\x00H0J\xd9\x90\xac\
\xb4)\xc8i\xcd\xd2j*z\xe9\x98\x84.\xdfC\x8f\x8d\x80\x90\x04\xb3\x96\xf0\xf0\
\x13\x8f\xf0\xef\xdf\xdf\xc2\xbb_5\x9f\xae\x82!\xeb\xc8\xc5p\xf4\x8a\xd9l}`\
\x0b\xd6\xec\x83Pr>\xa3\x83\xfdP\xed\x03;bv.a\xc5\xbc\x0e\xce;\xedX\xce:\xed\
\x10fw\x8d\xfb\x10"\xe0W\x8f\xc1\x8d?\xba\x87\xdf?\xfa,\x89r\xa8\x8e\x85($Aq\
!\xdb\x06\x87\x19\tA\x97\x14\x85Y \xed<q\xaa\xe9\x9c7\x87\x91\xe1~\x1cGr\xc0\
\x8a\x05\xec\xd8\xb6\x96\x81\x1d\xeb9~\xd5R\xde\xf7\xe67\xf3\xab[o\xa2\xbbg\
\x11\xaf:\xff(c*jh\x1708\xd0GoOwCqgn`#\xc9\xaa\x85\xe9\xc9\xd4\xea\x1e}\xe8\
\xc0\x84\xc4\xce}D\xfc$\xd7\xa1\xb0\xb3\xf4@G\xc3K\x8e_\xc6\xd1+\x97\xf3\xf3\
g\x86Q\xb1\x85t\x1c\x1c\xd71\xea*N\x8c\xcf5S\xed\x8d\x0bS;\x8f9\xa4\x04\xd73\
\xdd\x80\xaa3\xcb\xf3\xe9\xdf\xb6\x03\x1d\xa7x\x07\x1eL\xad\xd0\xc3\xf7o\xbb\
\x8b\xcb/\xbc\x9c\xceLBs\xc0k^~"\xf7<r#\xb5\xea\x16\xaa\xd5\x88y\xf3fs\xf4\
\xaaS9\xe9\xa8\x15\x1c\xbb\xd2bI\x0f\x04\x8c\xf7v?\xbfw\x1b?\xfe\xe5\xfd<\
\xb6\xae\x9fm%\x8b\xfe\xaa$\xd5>xy\x9c EJA\xe2\xbbHO#=(\xcc\n\x08\x81\x07\
\x1e~\x8c\xd9\xbd\x8bY\xbbf\x03\x859\xddx\xae\xe0\xd9\xbb\x7f\xcd\xf2\xa3\
\x0f\xe6m\x7f\xfeA\xe6\xb5\xd9|\xea\x13\x1f\xe4\xedW]\xc1Y\xa7\x1d\x86\x0bX\
\xda\xa4+\xd9\x1a\xe6u\xf7d\x9eA\x00\x0bK(\x92,\x99\xa1\x91\r\xeb\xd0\xda+7\
\x8a\x1a`\xd7D\x81F\xdc`\'Z\xfe\x98\x92\x0e\nG(\x14\ty\xcb\xc6/\xc2\xe5\xaf<\
\x87G\xff\xed&\xb6T\x07Qa\x8am\xe5\xb0\x1c\x974\x8e\xd1\x02l\' I\x9b\x01\xbf\
\xf1\x8e\xaa\x19\xf1\xc9\x8a\xeetBQ\xc6\x04\x95>:u\x95\x9a\xb4\xa8\x8d\x0e\
\xe2\x17\x8b\xf4\x0fo\xe4\xa6[\x9fb\xfe\xcb\x0fd\x9e\x1f\xe2j\xc9\xd9G\x1f\
\xc0\x95\xe7\x1c\xc7X\x04\x17\x9dv$\x07-k\xa3\xb38\x9e\xabV\x05\xeez\x10~y\
\xf7}\xfc\xea\xb7\x0f\x80\x93g\xb4\x962X\x8a\xc1q(\xf6\xf4\xa0\xb1)\x97J\xc4\
\xb6\xe9`\xc3\xea(#NJ\x04F\xda]\xa8\xc51k7nd\xf6\xf2\xd5\xec\xd8\xb4\x16\xab\
\x90\xe3o\xae\xfd(K\x0f\x80\x9f|\xffn~w\xe7\xcd|\xf5\x0b\x1fa\xc1,\x1f\x91\
\x19\xdd\xae0\xde\xb5\xfah\x15\xdf\x91\xe0\xdb\x99\xa3H\x83L\xb1E:\xa1\x1a\
\xc6\x82\xac\x05d\x01a\xd1Z\\\x99\xa59\xa5\xae1\xf02\x9d\xaf[\x98\x17z\xdf\r\
\xde\'!]"\xb1HU\x8c+m\x12\r/?\xa5\x8b\x1f\xdd;\x0fwC?[\xfaJ\x08R\x84\xce\xa3\
\x89A\xd9\xa4"\x1d\'\xba9\xe4hI\xfe\x97\x02\xa2\x14\xa4\x83\x1b\xe4\x18\xd9\
\xb1\x81\xae \xcf\x9cb;\xcf\xec\xd8\x84Wpq\xd3*\xb7\xfc\xe0{\xbc\xf3\xdc\x0f\
\x85G\x1aV\xe9\xf4=\xdeu\xe5K(\x16\xc1\xcd\xdc\x97\x03\x11\xdc\xf5\x871~r\
\xd7\x03\xfc\xfe\xa9\r\xec\x18\xaaPM!\x16.\xda\xb6I\xed\x0eh\xb3@\xa5\x8c\
\x95C\x10%\x84\'\xd1q\x82\xdd\xddER\xaf\xa0\x92~$&\xdf\xac\n\x0c\xf4\xef\xa0\
\xa7}\x05\x95\xe1m\xbc\xfd\xea7q\xe1\x05\xf0\xb3\x1f\xc3_\xbf\xfb\xb3\x9c\
\xb4j>\xdf\xf8\x97\xeb\x98?\xdb\x1c\xd6\xce\x08\xac\xd7\x14\x05W\xe2\xb7\xe5\
ZF+\x13+kdV\x9c\xd4,F\xcd\xde\x13=IT\x92\xf1G\xa8\xb5I\x8e\x10b\xcf,k\xad\
\x9b\x99\xbf\x8d\xfd\x1b\x9f\x1b\xdb\xf6\x92t\x07)-\xdc\xacO\xb1\x84\x91\xaa\
\xbf\xfd\xcbsx\xc3\xbb>\x8f\x1d\r\xe1\x06s\x88\x1b\xe3M\xaf\x88\x96\x05\xa3\
\xe6\xa3(;\x84c:\xb3\xd48lp\x02s*\x1d3\xa8\xa0\xadk\x01\xe5\xb0Dep\x0b\xb3\
\xad\x84\xbcR\xac>d1\xe7\x9ev,\x96\x80\x10\xc9`I0\xdb\x87\xf9E\x18\x0ba\xc3\
\xa0\xe6\xab\xdf\xbd\x9d\x1f\xde\xb3\x96\xedQ\x11\x95\xef!\xa1\x17\x9cQ\xa4]\
G\x08H\xb5@[\x018y\xa8W\xa1:\x08N\x9d|\xb1H\x19\x1b\xe9\x17`\xc7v\xba\xba,\
\xbc\x10\xda\x1d\xd8\xbc5&G\xca\xeaUs\xb9\xfa\xbd\xaf`\xf3\x08\xfc\xc5{oc\
\xeb\x9aG9xn\x8e\xeb>p)\x07\xb4\x8d\x9755\x0c//\x90M\x12\x1b\xf9i;[\xd9\xa2\
\xf5\x01?G\xdf\xfe\xbf\xc1d\r\xa3\xf1Y)\xf5?!\xdd\xa8\xe8V\xd5\xe4\x02\xdd\
\x12\xde\xf7\xa7o\xe5\x9a\x0f\xff\x03a\x181T\x1e\x00\x7f\x16\x08\x1bj\x11\
\xe4\xf2\xe0{\xc84E\xa5!\x12\x8d\xe5X(\x95\x92\xd6\x86 \x8d\xb0\xf3.V\xbdDyd\
\x1b\xbd\x05\xc9KO_\xcd\xb9\xa7\x9f\xc8\xaa\x03\x1d|\x0b\xba\x0b0:\n\xbe\x07\
\x1d\xb3\xf3$@R\x8fy\xe4\x91\xb5\xbc\xfd\xfd\x9f\xc5?\xe0\x18\xd6\x0f\x01\
\x9d\x9d\xa0\x02\x88k\xa0$\x85\xf6N\xc2J\t)-T\x14\x9a\xc4\xb8\\\x80=g.Vm;\
\xe5\x1d\x03P\x9cEt\xff\xfd\xc8\x85]ty\x92.\x17\xa8%T#\xc1G?\xf1i\x96\xac\
\xb4\xf8\xd1m\xc3\\\xff\x95o\xe0\xb8\x16\x9d^\xcag\xae\xfd\x13:]c\xa6\xee6\
\xe0\xb1oGS/\x08\xf6\xaa\xd1I\xa0\x08\x1c<\xc7\xe7\x03\xef|\x0f\x9f\xfa\xd27\
\xb1;\xbb\x19S9\xac\\\x07\xa3\xa5\xbaIF\x93\x1a\xdbQ8D\xa4a\x15\x15W\xf1,(\
\xfa)\xbe\x18\xa0\xd7s8\xe9\xd4c8\xef\xec\xd7pp\x16\xc4P\x98\x86\xa5\x81Q`\
\xfd\xd0\x18\xbf\xbe\xff).~\xd91$qL\xbb/8\xfa\xd8\x83Xt\xe8\xa9<S\n\x08zSB+D\
\t\x05i\x1d\x11\x85DC)\x8e\xe3\x92*\xc0\x89\xc1\x11@\x95\xa4\x1c\x93\xc4\x16\
\xf8\xb3\x90\xae\x8b{\xf0"r\xd10\xb5\x91\xed\x0c\x0eV\xe9\xe9\xca1{)\xdc\xf1\
k\xf8\xfbk\xbe\xcb\x86\r\x1b\x98\xdb\xddEyp\x0b\xef\x7f\xd7\x9b\xe8\xb4 \x10\
{ \xbc\x05i\x9abY\xfb\xce\x81\xf2\xc7\xc4^\x91\xeed\xdd\xc4\xe2N\xc8\x1f\xdb\
\xc6\xba\xb5\xa7\xf2\x99\xaf~\x1f\xb7{\x01\xa5r\t\xd7/\xd0\xd6\x9d\xa3V\x1aD\
\xc6U\x8a\xae\xc2\xf5\xeb\x14:m\x0e9h9\xc7\x1d\xbe\x84\x0bN_Jo~\xbchO`\xba\
\x8d\xe1\x14\x1e{&\xe4\xc1G\x1e\xe7\xe7\xbf\xb8\x83\xca\xe80\xfd\xdb\xd6Q\
\xaa\\\xcdU\x17\x9d\xde\xac\xf7z\xef\xfb\xae\xe0\x92\xb7}\x15\xdd\x96G\xd5k`\
[XB\xd2Y,P+UH\x13\x81\xf4]p]\xe3\x9fO#\xd0\x02\xcb\xb1\t,(\x97\x07\xc9\x17\
\x1d\x866\xaf\xe5\xb4c\x97\xd2\xd9\x9dc,\x86/\xdc\xbc\x85/}\xff\x97\xa8(DhI2\
\xb6\x83W\x9f}<\x97\xbfl>\x05\x8c\xd5])W)\x14r\x93?\x9c\x0cS\x8d\xf4=\xd9\
\x03{E\xba\x00t\xa5N[\xdeG\xe4\xe0]o:\x14%\xeb|\xf7\'\xbfF\xfb\x8ajT\x82J\
\x8c\x9b\x96\x99U\xb09\xea\xf0e\xbc\xf4\xd4c9\xe6\xf0\x85\xcc\xca\x19\x97\
\x8e\x93@:\x16\xe2\xe6=\xa4\x84\xa7\xfa\xe0\x87?\xbd\x97_?\xf0$\x83UM\xdfP\
\x19\xcb\n\x08\xdc\x1c\xb3\x97\xcc\xe5[\xdf\xbb\x9b\xf3\xcf>\x9d@A\x90\x87Es\
`\xd5\x8a\x05<\xf0\xf4:\x8a\xdd\xbd\x94\xab!i\x12\xa3}P\xbe&\xd6\n\xe9\xb8&\
\xb9\xa2\x12\x82kS\xec\xca\xe1\xd6G\x18\xda\xbe\x91\xce\xce\x02VX\xe6\xeb_\
\xf88g\x1cl\x0c\xe9\xba\x05Oo\xafR.\x0b\xe6,ZLi\xc3\x83\x9c\xba\xfa >\xf6\
\xce\xb3\x08\x07\xab\xf8]\x86\xe8B~\xcf\x84\x03\xb8\xee\xeeJ\x0b\xa6\x1e\xf6\
\xd2\xa6H\xf0\xac* \xc8I\x8f\x04x\xfb\x95\xc7\xb0f\xcd\x83\xfc\xfe\xf1g\x98\
\xd3\xd9\xc51\xc7\x1c\xc5\x19/9\x91c\x0f)\xd0(\xaf\xf70\xaa\xb1\x86\xc9%\xac\
[\x1ew\xdc\xb3\x9e[\x7f\xfe\x1b\x1e^\xbb\x9d\x92\xca1\x1c{\x0c\x0e\x878\x9d\
\xf3q\x1c\x87\xc1\x91~FD\x9dy\xf96n\xfa\xc9\x1a^\x7f\xd12J5\xf0\x02\xf8\xdb\
\xf7\x9c\xc5\x1b\xff\xfcK8Z\x10\n\x8f\xc4\xf1\x19\xacU!\x8e\xf1\xbb\xda\xa8\
\x97\xab\xa0\x03hk\x87\xb0\xcc\xd8\xda\xa7\xc8\xbb\x15\x96\xf5\x16Y\xbct\x11\
\x7f}\xcd\xd9\x04\xc0wnz\x94KO=\x948\x80{\x1f|\x0c\n\x05\xfa\xd6=\xc3l\'\xe1\
-W\\@\x00\xb4w\xb8\x10\x97Mq\xa30\xc9\xc5\xff\xbf`\xefH\x17\x1a|\tz\x8cz\xad\
D.7\x0b\xe9\xc0\x97\xaf}+w>\xb8\x9e\x13\x8e\\\xdc\xdcUc\xaaE\x1c\x8c\x1a\xdf\
0\x1a\xf3\xe8\xa3\xeb\xb9\xe5\xd6\xfbX\xbba\x98\xbe\x81\x11\xf0\xda\x91\xb9\
\xd9Tt\x8e\x98<\xf4\x16\x89\xcb5b7 7\xbb\x8b\xb1\xcdOS\t$_\xfa\xfa\x0fY}\xd8\
{9|\xb9)0=a>\x9cv\xc8B~\xfa\xbb\x8d\xa4\xb9y\x88\xf6N\xa8h\xc8\x07\xd4\x85\
\x03\xaa\x0eR\xd1i\x01i\r\xe9\xa5\x1c\x7f\xc4R^z\xc6I\x9cs\xc6\\\xb6\x94\xe0\
\xc3\x7f\x7f#\xb5\x81\x8d\xbc\xf9\xa2C\xd9T\x02t\x0c\x952\x8b\xe7\x15\xb8\
\xf6\x1do\xe1\xa8\xa56n\xac!-\x83cS\x1d\x19!\xd7\xd9\xb3\xcf\x1f\xfc\x8b\x89\
\xbd\x94ta\xf4\xa1\xed\x92\x0f\xac,\xc6+\t\xeb1g\x1f\xb9\x98jb\xb2\x98\x1aS`\
\x85\x11\xdc\xf1\xc0Fn\xfd\xc5\xafy\xf8\x89\xb5\x94"\x97\xaa\xee@8=\xa8Ys\
\x88\x12L$\xca\xce\x83]0\xc1\x89B\x11$TGFi_\xb0\x98\x91\xe1\r\xb4[\x01_\xf9\
\xf6\x1d\\\xfbWg\xb2\xc0\x85\x91\x11\xc5\xbb\xdex\x01\x0f<\xfeyv81\xb5$6\xd3\
4tuC\xdf&\x9c\x9ev\xfcz\x85\xe15O\xb0pV\x1b\xaf~\xf5\xb9\xbc\xf2\xc2\xe5\xcc\
i\x87_\xfcA\xf3\x91\x8f\xff#\x96\x17\xb0\xa0\xab\xc3\x14\x1d>;\xcc\xf6\xf5O0\
\xe7\x80\xa5\xbc\xf1\xbc\x938\xfbp;SK\xb1q\x19\xc7)\xb9\xce.j\xf5\x90 \xd8\
\xcd\\\x1eS\x14\xa6\x06\x7fr\x7f\xc0^\x92n\x83\xdd\x91\x1dl\xdc\xad\xd8\xe3\
\x9b\xa8[\xce2\xdd\xe8\x1f\x1e\xde\xc2\xb7~\xf83\xee{r3e\xab\x8b\xe1\xd4\xa7\
"\x0fF\xe5\n\x10V\xc0r\x8c\xcbV\xa6\x99w*\x02Q6\xe3\xdbJ\x05:\xda\xc1\xae0Z\
\x1d\xa5\xa7\xbb\xc0\xf6-\xfd\xdc\xbf\xf6\x19\xfe\xfb\xce\x83\xb8\xec\xc8\
\xb9\xcc\xee\x94\xd0\x06\xab\x0f\x9f\xc5\x9dkK\xd4\x12\t\x85\x1e(\xa7`\xe7\
\xc8\x87\x15D\xe9Y\x8e[\xd5\xce\xdb\xdf>^\xd7\xda\x00\x00 \x00IDATt%/9\xb6@\
\x7f\x15\xfe\xe9+O\xf0\xfd\x9bo\xc2o\xefd\xc3\xf6\x11\x0e=\xec\x08"`\xa0o\
\x0b\xf9h\x07\x97\x9e|\x0e\x7f~\xd1\x12\xda\x00\xdb\xcf&\x1a\xc0\x03O\xa2\
\x85\xc4\xdd\xcf\x08\x070s\xdcLN\xfa^\x8d2\x1b\x89\x90\t&Hf\xf2\xce\xc0R\xe6\
e+(H\xf8\xd5m\xbf`\xfd\x9a\xcdD\x91\xc7\xb6AEd-@yK\x81^(\xce1N\x1a!\xc1s\xc1\
\xb3L.t}\x04\xc2a\xc8\t,+\x04\x99\x80\x8a\xa9#H\x82\x02\xcf\x0e\x96\xb9\xe1\
\xc7?\xc7-Hv\xf4W\xd1\n\xfe\xeeo.G\xd6w\xd0\x11\xc4\xb0c\x1d\xae\x88\tt\rF\
\xb6\xf0\x96W\x9f\xcd\x97\xaf\xffSN?\xb6\xc0\xe3\x1b\xe0\xef?\xf3[\xbe\xf3\
\xdfw\xa3\xbdN\x06\xcb\t\x9d\xf3\x96!\x83NB\xe0\xee_\xde\xc6\x15\xe7\x9d\xca\
\x9b/:\x9ev\xc0V\x15PY\xa2\x88\xb4\xb3\x18\xf8x\x1a\xfa\xfe\x85h\xb7\xdf\xec\
\xb5k!my\xa9\xe6h`\xbc\xc4G\xa5\xf0\xa1\x0f\\\xc9Y/;\x83|>\xc7\xac\xd9s\x88\
\xb4\x00\xe1A52\x81\xf2j\x19\xc6F\xa1V\xc6K*8I\x05WU\x08D\x84\x1dUp\xc3\xd0x\
\xf0\xf0\x08S\x1f\xa7m>8\xb3\xb8\xef\xe9~\xae\xff\xd6]\xf8sr\xd4S\xe8\xb4\
\xe1\xdc\x13V\xd3\x9d\x0c\xd0]\xac\xe0\x0e=\xcci+;\xf8\x8fO\xbe\x9b\xb7\\v\
\x14\xed\x1e\xdcy\x7f\x85\xbf\xf9\xc4\xd7\xf8\xc9\xef\x9e\xa0\xec\xcfB\xe5\
\xbb)G\x92r"\xb0\x1c\x17\x12hs\x15o\xb9\xf4\\\x0e\xea\x06K\x95AU\xcd\x1df^\
\xc8\x98\xf1.k\x7fC\x9a\xec\x03\xd2\'B\x81\x88A\x86F\x8d\xc8\x1a\x8eg\xb4\
\xe2\x9f\xbe\xf5d^{\xd9\xb9xv\x85\xb6\xa0\x0e\xd5\xcd\xe0\xa7Y\x0c\xde\x04\
\x96e\xb5\x8a,W\xc8%\x8a\x82\xb0\xc9i\x0b]OH\xea1\x8e\x95\x03+O\\V\xc4*\xc0n\
[\x88,\xce\xe5\xcb?\xf8\x19\x0fo59\xf5I\n\xef\xbb\xfa\xe5\xe8\xadOrX\xaf\xe4\
\xcas\x8f\xe6C\xd7\x9c\xc3A\xcb\xa1\xe0\xc2\x7f}\xe7I\xde\xf3\xfe\xeb\xd88\
\x10\xa2\xf2\xdd\xd4\xb1\x19\t5^{\x0fq\xa9B{!\xc0Vp\xc5\xc5\xe7q\xd8\xb26\
\x920+\xc3\x990\x9b\x83\xc1\xfe\'\xe1\x06\xd2\xde\xfd0s/\xfbt\x85\xd5\xc8\
\xf1\xca\x1e\x8a6s\x13fQ#\xb3\x8fR\x82j\xd5\xe3\xaaK\x96\xd2\xb5\xa0\x83\x8f\
~\xf6k`\x07DI;I\x1a\xe0{yl\xcb!\xa9\x8c\x81\x8aq\xa5\x8bR\x16\xb5z\x8a\xe7uP\
\x8b\x04\xe0\x99. \xac@\xaaIr\x16Z\xe4\x19\xf5\xba\xf8\xc4\x7f\xdc\xc4\xbf|\
\xf0"\xf2\xc0\xac\x02\xbc\xeb\xf2\xf3Yr\xf8*\x8e:v\x11!\xd0W\x85\xbf\xfc\xe8\
\x0fx\xe0\xa1\xb5\x14{\x0fg0\xc18k\xc6J$qJ\xd0\xdd\x89\xd0\x8a\xde\xbcC^\xc3\
\xc9G\xaf\xa2\x02hG\x80\xf4\x8c6B\x820\x81\x92?V\n\xf2\x0b\x81\x88\x80\xddY"\
{%\xe9f\x94\xaa\xb2)\xed$f\x1eDS0\xa8\xb3W\n\xb8\x96Ew\xce\xc2\x03.8\xae\x8b\
\xaf\\\xf7.\x0e\xed\x8d\xe8T\xdbP\xd5a\xa2Z\x9d8J\xd1\xc2\xc1rrh\xdb\'\xc6\
\xa1\xa6$\xd2+\xa2\x95DG\xb1\x89\xca\x15r&e8\xae\x91\x86!i~\x16\xf7<\xbe\x9e\
\x1f\xfed\xa3\xb1\'\xca\xf0\xee7\x9d\xcbK\x8f]D\n\xdc\xfd\x84\xe6m\x7f\xf5\r\
\xee||\x0b\x03\xa2\x8d-\xa5\x10\xa7\xd8\x8e\xaaW!\x08 \xdf\xc6\xd8H\t_\xa4\
\xe4\xa8\x1b\xdf\xbb\x0e\xa9D&6\x14\x11\x80\xcc\x9b\x94m\xad\x10\x848Y\x94l\
\x7fDy\x0f\xf2\xbcw\xea]KH\x1dH\x02P\x1eB{h<R\x02"\xf2D\xe4\x19\xaeh\x14\x0e\
\x1am\n\xf2\x14\x9c\xb8\xd0\xe6[\x9f|7\xa7\x1d2\x97\x05\x0b\xba\x08\x1d(\x87\
e\xca\x16D\x81\xcb\x88\x80\x92\x05\xba\xad@YG\x10X\xe0& \xc6p\xbc\x1aA\xae\
\x86\xef\x0c\x82]\'J}\xca\x91\xcbm\xb7\xdf\x8cH\xc0\xf7\xc6 \xae04\x04\xff\
\xfe\xcd\x07\xb9\xfa\xff\xfc3\x9b\xc2Nj\x85y\xf8\x0b\x17S\\0\x8b\xd1\xbe\x8d\
P)C\xa4p\x82"\x841\x81\x8cY\xb9\xb0\x0b\xa1b\xd01\x910\xe1\xd52P\xc73\x01$RP\
!B\x87\xcf1{\xcb\xd4E\xf8\xdc99{\x81F\xdaN\xa6\xf3v.\x7fn\xcb\x17\x10H,e<qm\
\xd2\x10\xef\xd7k\xfc\xf3\x87_\xcbY\xc7\x1e\xc0\xb2\xd9 t\x19\xd7J\xd0\x96m2\
o\xd2\x04r\x0e$\x15\xc8K\xc8I\x88*\xc4\xe5\x01\xd2\xea\x00"\x19\xa2 +\xf8\
\xf1\x18\xc7\x1f\xb8\x84O]\xfb\x0e|\x07T\x14\x83\xeb\xa2\x1d\xf8\xef\x9f\xde\
F\xb1\xd0\xc5\xd8H\nv;\xb5\xb1\x1a\xa3\x83\xdb\xa1\xb3\x00\xf9\x0eP\x82\xb8<\
L\xbe\xd3f\xe9\xbc6N=\xae\x83\xc6\xfc\xed9g\xdc\xd7f,u\xcbH{\x16&\xb5&\x95\
\xf6F\x177n\xdcN\xb5n\xc0\xdf\xc3w{Ozc^\xbdl~L)\xcc(\xd0\xce^\x8d\xff\x1d\
\xd9\x98%\xd5\x94\x9b\xcdj\x0f(\x02\xff\xf0\x96\xa3x\xc7Y\x0b9\xa2+\xc1\x1e\
\xddN\\\xa9`\xb5\xb5CG\x01\xa2\x12\x144\xd4\x06\xa1V\xc1w\x1c\xe6\xb4u\xa1+U\
\\"\xda\x19\xe5\x9a\xf3\x8e\xe4\xcb\x7f\xf9r\xe6\xfa0R\x85\x11\xa7\x8b\x92p\
\xf0\x8b\xf0\'\x7fz\x19jp\x03\x8b|\x9bB%\x85\x8a\xc6\xebn\x83\xda\x18\xe4:\
\x8d)^\xde\xcc\xc2\xb6\x903NZE\xff\x08\x84\xb6G"\\\n@\x1b&=\xcb\x06blR\xf2 r\
\xe6\xaet\x8a\xd0I37\xa45\xd5\xc9d\xfa\x8f[\xf9S\t\xb9\xc6\x0cZ\x93`\xefHo\
\xc9\xeal\xfc\xdf\xd8\xd4\xc8\x0c\x11{x\xb9(\xf2*\xe2\xed\x17\xac\xe6\x1f\
\xfe\xeaj\xce8r)A<B:\xda\x07\xe1\x88\t\xd8[\xc2$_T\xabx\x96\xa4\x7f\xd3zf\
\x05\x16\x0b\x8b\x16\xd7\xfd\xd5\xdb\xb9\xf2\xac#X\xdemB\xf7k\xfb\xe1\xbd\
\x1f\xffO\x9e\x1e6\x93\x03\x9d}\xe2b.?\xf74\xca[\xd7\xe0*\x10\xd2&,Ua\xee\\\
\x18+A{\x9e\xb6\x0e\x0f\xea\x83\xcc\xed\xca\xd3\xd1n\xc8\nS\x93\xff\xeek\xd3\
\x7f7\x885s\xc3I\x9a\xd3\x7f\xb7d\xa34*l\x9a\x1f\x99z\x84\x03\xc8\xe7M\xfa\
\xf3\x84\x00<iS\x1a\x1c\xe5\xb8\x95\x0e\x9f\xfb\xbb\x0bx\xfb\xabN\xa6S\x0cBy\
\x00\xd7\xb1a\xb4\x0e\x91\xc0\x9b\xbb\x80\xd1\xd2(]m\x16G/\x9f\xcd-\xff\xfan\
\xceY\xdd\xc9\xc2y0\x96\xc2-\xf7\x0e\xf1\x86\xbf\xba\x8e[\xef{\x86O|\xee\xbb\
\x04@\xbd\x0eW^z*\x9d\xf9\x147\x9fe\x96\xe5\xbba\xa4\x0ev\x82\x15\x0e"\xa3*\
\'\x9fx\x02C\x03\xfd\xe4\x84I\xf4\xb1\xa5\x95\xa5\x1c\xa7H\x93\xc3:i\xdd\x01\
\xd9\x04\x84\xe3\x9fu#\xe7u\xf2\xfd\xa7\x00\xd4n\xbcq\xf0\x82\xd9(\x92$\x8e\
\x99\xd3\xddNA@\x8f\x03\xef~\xfdJ\xaey\xc3\x05\xcc\xcb\xa7Dk\x1f\x07Kc\x17|\
\xc2\xd2\x0e<\'\xe4\xca\xcb\xce\xe3s\xd7\xbe\nQ\x86<F\xfa\xfe\xed\xbb\x0f\
\xf0\xa1\xeb\xbfB_\xdc\x86\xecY\xcem\xf7<\xce}\x8f\x85\x14}Xz\x00\\\xf4\x8a\
\xd3)W\xb6\xe0\xb4\xdbf&\x84\xe1\n\xdd\xed\x01\xe9\xd8v\x1c\x12^{\xf9\t\xfc\
\xfe\xfe\xfb\x01c\x93\xb8\x96d\x8f\xae\x97\x9d\x18U\x13\xfeSMM7\x15\r\xbd\
\xf8E\']\x83my\x10kl\r\x81\x86 \x86\xab/\\\xc6g\xde\xff\x0eN\\\xb5\x88|\xb5\
\x8f\xceh\x07\xf3sU\xae\xfb\xc0\xd5\xbc\xf5\xd5\x07\xa0\x80\xb6\x02\x94\xea\
\xf0\xb5\x9b\x1f\xe7\xdfo\xba\x9d~U@t/f t\xf1\xba\x16\xf3w\xff\xf0\x15\xfa\
\x86\xcdi.\xbf\xe2H\x82\xa0D\xc1\xab\xc2\x8e~D{\x0f\xb2V"\xa7\xab\x9c\xf9\
\x92S\xb0$\x94F\x86M\x97#M\x86:il\xca\xae\xd0M\x1fDv\xc9\x19\xb2\x9c\xb3\xe6\
G\x9d}\x99\xcdx\x91\xed\xa1\xd4\xd4R\xf2\xbb,\x1d\xd2\x82\x17\xac\x91F5S\xad\
Y+\x8f\x10\x87%\xda\x1d#\xc1\xc7.\xb2\xf8\xd6\'\xaf\xe0\x1d\x17\x9c\xc8\x11\
\x1d\x117\\\xff&.\\\r\x8d\xa5q\xaa\xc0\xbf}\xef>>\xf1\x95o\xb3\xa9*Y\xb0\xf2\
(F\x9fXKj\xe7\x19\x8b]\xb6\x95|>\xfd\xa5\xbb(\x01\x9dyx\xfdk\xcf\xa6>\xf2\
\x0c\x9e+\x08\xc2\x98x\xb8\x9f\xee\xb6\x1cW\xbd\xf6X\xbes\xc3}\x1c|\xe0\x8aq\
\xe9\xd4\x8dt\xe4\xd6\x9a\xb1q\x8c\x13\xbf\xf3cjH\xban\xaaxg\x1f\x97\x1e=_\
\xa4{\x18O\xbc`Wj;f\xad\x11\xbf\xd8\x86\xeb{\x94+C\xc8$fQ\x0e\xe6\xc6\xf0\
\xfeKN\xe0\'\xd7_\xcd\x812\xa4\x07S\xc40\x9a\xc2\xb5\xffz?\x1f\xfe\xd2\x0f\
\x18t\xe7"f/a\xfd3\x1bpV\x1e\x86\x9b/\x10)\x8f\x8a\xe8\xe5\xee\xc7w\xf0\xc3_\
\x0c1\x00\xbc\xe1\xc2U\x1c\x7fp7\xf3\x83\x145\xd8\x87\'SN9\xe9D\xe6v\xc2\xbd\
\xf7\xdc\xc3\xa2E\x8b\x80\xcc6\xd3\x1a3\'\xba!\xbd\xd1\xaf\xef\xf2\x88Z;m\
\xdd\xfa\xae\xa6\xec8\xde\xdb\'\xe3\xf4\xe7\tag\x05\nqL\xa8"\x8a\xf9\x00\xdf\
V\x888\xc5\x93\xd0\xe1\x87XI\x8d\xd9\xb9\x1a\x05j\x08\r\x1f\xbf\xeek|\xe9\
\xff~\x87E\x87\x9fD\x12t\x10\xd6\x12h\xef$V\x9ah\xd36\n]s(\xa7\x01\x9bG\x14\
\xd7\xff\xeb\xd7\x19(\x19\xed\xf0\xb6\xd7\xbf\x8a\xda\x8eMty\x82\xae\xc0\xe6\
\x95\xe7\x1f\xcd\x86m`9\x81\x99p7+\xb85\xde7\x9aD\x8a]\x94z\x86\ts\xc6\x8b\t\
U\xb9\x12\x85T\x8a8\x8e\xff\x88O\xef\x7f\x0e\x97\xdd_\xcf\x0bB\xba\x16\xd9,S\
\x12\x1c\xc7\xc3\x95.\xb2\x11\xa1\x93\xcad\xaf\x10\x9by\xc7t\x8cV!?\xff\xe9m\
<\xf9\xe4\xe3\xd8\xb6M\xa9T2\x95\xa0(c\xaa\xc7\t\xcc\xea\xa5\\\xae\x83W\x00\
\xbf\x00^\x9e/~\xf9\xbf\x18Tp\xf4\x81\x1d\x9cu\xfa\xc9x\xaa\xc6\xb1\x87.\xe3\
\xb0\xc5\xf0\xe0\x83\x83l\xeb\xebg\xfe\xc2\xc5(\xc0u \tC\xe3o\xdf\xc3rV{k\
\x9a;\x93M\xab\xf6"\xa2^\x19\xd8\xedw/\x98\xa47\xc2\xb1\xa6\x0f43?5EL\nSK\
\xec\x14@\x06h\xe9s\xc6\x19g\xf0\xbew\xff\x19\xaf8\xed(j}O\xa1\x867c\xcb\x14\
+\xe7\x9b\xf1V\xaa!\xc8\xe3\xe5\x1dR\x15R.Wx\xe4\xd1\xa7\xf9\xf5o\x9e \x01\
\xdez\xf5\xc9\xb4\xb7\xb9\xbc\xee\x92\x97\x91\xc6p\xef\xbd\xf7\xa2\x84G\xef\
\xfcN,a\xe4\xd4\xf6\xf2 mT\xad\x96]\xa5\x98\\]O\x14\xfb\xf1\xe2\x06hv\x08SM\
\xd2\x0b\xf6\xee\xa9}\xc1\xe6\x86m\\\x82\xdd\x9c\x16_e\xb5\xba\xa6\x0e$\x05\
\x12\x0b\x12<"\xa0\xcd\x83\xb3\x8fY\xcc\xbc \xcf\xc1\xbd>\xdf\xbc\xedN\x94\
\xdb\xce\xa6\xda\x08\xa8"t\xcc\x81\xb1\x12\xe1\xf0\x00\xb3{}\xca\xfduB\x0b\
\xbe\xf8\xd5\x9bYu\xf8J\x96\xf5\xc0\x9b\xdf\xf6\x06\x8e]\x01}\xa3\xf0\xe0\
\x03\x0fPS6\xdb\xb6\xa7\x1c4\xcf\xa2\xd0\x1c\x8eI\xa3\x8a\xb4\xdc;\xa9n\x96m\
\x99\xdfj\x00\xadQj\x8aE\xdd\xe5T\xe8\xd3i\xf8\xb8uK\r\xafefi\xb0\xa0\xa4\
\x8d\xa5\x9e\xa51\x00\xc6\x87\x7f\xca!\xb3\xf8\xdb?\xbb\x80\xf7]u!\xc1\xd8V\
\x18\xde\x82\xd7\x93\xb9X\xc3*N\xb7O\xa5\xbc\x83 \xc8\x13%\x01\xa3\x95\x80\
\xf7\x7f\xe4\x87\xc4\xc09g\xe7q\x81;o\xfb5ZX\x08\xb7\xc0m\xbf\xbc\x8b0\xcb\
\xd6\xaaE\x1a\xb0\x90\xb9\xfcs<\x8a\x9d\t5\x8e\xe6\xd6A\x9e\xe7M\xad\x94\xaa\
ZR\xdc\xedw/\x98G\xae\xa1\xd6\r\xe1.\x86R\xb7\x99\xa5b\xd9\xe3\x93\xeb[\x8c\
\xe7\xca\x0b]\xc3\xd55\xae:\xffT\xde\xf3\xba\x8bX>\'G\xb8\xe1qS\xd1\xd8\xe5\
\x12G;PVB\xa8l\x84=\x0b\xd7_\xcc\x93\x0f\xae\xe5\x86\x1f\x0f`\x03C\x031w\xdc\
\xfe3\xa4\x9b\xa3\xd09\x8b\x07\x1f~\x92j\x9c\x15]$\x98\xeb\xc1\x9e\xa0\xb2\'\
b\'\xc2\x9b\xd5\xa3V6-\xe9\xc4B\xc2\xa9\x02\xb9\x07;\xe5\x05\x92t\x13\x9f\
\xceRj1\xe1\x19\x0f\x84\x89\xc3\x0b\x146f\xdc\x9e\xc7\xe4\xdc\xb9\xdaT\xa9"\
\x14Im\x8c\x9cT\xbc\xf1\xa2\xd5\xfc\xc5U\xafd^\xb7\x84\x91\x8dP\x1d\x00\xaa\
\x90\xf7\xa8\xc9\x02\xb1\xecbxX3{\xc12n\xbe\xe9\xbfP@W\x8f\xc3\x8aU\x873:V\
\xa5\x16C\x7f\xa9\xc2\xc3\x8f\xd5I\x01\'\x10\x99\x05\x9fi\x9f=\\\x7f#\x88d\
\xd0\x90\xf4,\xc26\x05I\xdfS\xb5\xcd\x0bF\xba\x91c\x93O\x97\xca,\xedI\x98\
\xef\\\xaa\xe4T?yJ\xe4SE!\xc2,b\x0cD\xe4!\xd7\x03R\x12\x8d\xc2e\xa7\xcf\xe6\
\xb3\x1f|\x0b\xf3;\x81\xda\x00]\x8b\xe7S+WP\xf9\xb9TF-\xda\xf3\xb3\xa9\xf4\
\x0f\xb0j\xe9,\x06\x06+\xa4\xc0\x85\x97]\xca\x8aC\x8ff\xb8TCaq\xcb\xad?\xa5\
\x9a\xad\xdf\x93*\xcd\x1e\xe7`\x9d\x8cKm\xdc2\x8d\xcc\x9a\xc8q\xa6\\\x9f>\
\x05$}\x1c-3\xd3\xb4\xcc\xc3\xa2\xcc?*\x06\x1d\x9a\xb5D]c\xf1\x9b\xc9\xb8$\
\x90P\xc8k\\\r\'/\x83\xaf~\xfc\r\x1c\xbf\xa2\x97\xa1{\xee\xa0\xd8\xde\x85.\
\x83\xd5>\x8f(L\xb1-X\xbet6\x0b\xba\xf3<\xb5\xb5F\xef\\x\xcd\x15\xe7\xe0\xd9\
)\xbeg\xf3\x9b\xdf\xdeK\x98\x18)\x1d\x1e\x8b\xd0\xc2Fc\xd3X\x19a\xbc;\xca\
\xd2\xa1[\'\x01\xda\xa9\x814#\x8dS\xa8\x8e\r\xa6\x04\xe9\x12\x13\xd6w\xccR\
\xd3\xc2\x14\x96\x9a\x95\xd0$\xc6\xff\xd6\x06\xa2\x90\xe5\xc6\x9b\x9f4\xf2\
\xeb]b\xb0\xca\xc4\xaa\x1ft\x8d<p|\x0f|\xe8\x923\xb9\xfc\xd4\xe3\xb1\x9ey\
\x96\x0e\x02\xa8*,K\x10tx\xf4,\xe8$\x02n\xfe\xd5]\xfc\xfc\xfe2g\x1f\x03\xc7\
\x1d1\x9f\xa8\xda\x8f\xe3\n~\xf7\x87~\xb6\x8fA\xd0\xe1\xed\x12\x13\xb7\xb2Y\
\xa4Q1\xa8\x08\xd24K\xa1j\xdc\x8b)\xe5\xce:)s\x1f{\x1a\xebO1\xbc\x80\xa47\
\xd2-&\xc6\xda\xc7\xa9\xf5L\xba\xb4\xb0\'\xc4\xec\x9bk\x9a\xa6\x11\x9ek\x13H\
IX\xab\x91\xd7p\xceQ]\xbc\xed\x15gr\xce\xd1\xab\x10\xa5\xedt\xe5$c\xa5m\xf4\
\xf5\xad\xe5\xf8\xe3V\xa3\x81\xce\xde%\xfc\xed\xb5\x9f\xe2\xa15\xf0\xb67\x9d\
\xcb\xbc9\xb3\xf0\x1c\x97\xdb\xee\xf8\x05\xf9\xa2\xe9pF\xab\xe3\xdag\xc2<``\
\xaey\x17\xbf\xfa\xb8Fh\xcd\'\x98J\xa8V\xab\xbb\xfdn?i\x9e\x02c\xe2\x05\x80G\
!\x08\x8c\xdd%\xe1\xa8\xa3\xe7\xf2\x17\x7f\xfeJ\x96-r\t\xc7\x9ebi\xaf\xcd\
\xa2\xde\x02s-\x18N\x14k\xd7\x0e\x90\xaa\x02\xff\xfe\x1f71\x7f\x01\x9cw\xde\
\xf9\x8c\x8c\x8e\xf1\xfb\xfb\x7f\x8f\x85I\xa7.\xe6\xc6U\xba\xc9\x8a\xc9\x0c<\
\xe9\x19\xa7\x91t\xd8o\x1eU\x86=\x95a\xed\x1fw\xa2m @\x87\x13\x17\xbc\x1f\
\xab&\xd8\x16\xac8\x00\xfe\xe6//\xe4\xc8\xe5yF\xb7>\xc8\x81s\x8b8\xc0,[22\
\xa8h\xebX\xc4\x1f\x1ez\x86\xcf|\xfe>.\xb9h1\x8b\x96\x1c\x08\xc2\xe2\xde\xfb\
\xfbQ\xe9xL\\dG7v\x84m\x82\xa7\x99\x0by\x8f\xc6\xfd\x14\xc4\x9e\xea\xd3\xf7\
\x13\xd2\x81T"\xa4\x83\xc4\xcc.\x12+\xc8\xe7l\\\x11"I9\xe1`x\xff;_\xc1\xca\
\xb9\x82\xa3W\xcc\xc6\xc5t&]]\x07\xb0~\xf3\x08nn\x16?\xbb\xf3>~z\x17|\xf4\
\xef\xff\x84HI~\xff\xc0\xc3\xc6\xed\xae\x1bj:\xcdV;\xd2\x13VWl]\x95q\xbf\x81\
~\xd1\r\xb9\xe7\t\x8dYN"{\xf2\x96\x05\xa96\xf2n\x11b\xa9Qr\xc01\xcb\xe1\xba\
\xf7_\xcd\xc5g\x1c\x83E\xcah\x05vl\x1f\xa2\xd81\x87\x9av)v\xcf\xe7\xba\xeb\
\xff\x85\x91\x1a\\\xf2\x9a\xab\xf8\xcd\xef\x1e\xa4Z3\x06\xfax\xaaD\xd2X\x05\
\x06\xd87\xd9\xae:s\xd36^i\x9a6_\r\x84aH\x9a\xa6\xcd1\x7f\xeb\xfe\x90\x90\
\x00\xa126H\xe3\x15)\xbdk\xad]\xe3\xc3\x1e\xfc\x06\xfb\x07\xe9`\xae4m1\t-A-2\
\xab9\x14\xa5\xc4\'!\x1d+s\xfc\xc1s9b\xc9\x1c\xda\xd1\xcc\xc9\xc3\xe6-\x1b\
\xf1\x82\x1ca"\x19\xa8(B+\xcf\xbb\xfe\xfa\xffr\xd6+VP\x8e-\xd6o\xa9\xb5<\x85\
F\\ \xc6"\xdeg\xb1\xf2\xc6T_\r\x95\xdb\x98\x15J\x08\x01\xf51\x08\xcbx\x0eX"A\
\xc4uD\x1a"E\xb6\x18\xb0NM\xe1\r\x1aK\xea\tF\xb0Nc\x928B\x00a\xbd\x8eNU\xebI\
\xf7\xf8(\xa7>\xb2j\'\x04\xd4\xc2\x908\x1b\\\xe5\xdd<\x16.\xa4\x1a\x11W\xe8-\
z\xa4\xe5\x11\x02+!\xae\x0fa\x03\xed\x05\xc1Xe\x00\xcb\xcf\x91\x96cd\xd0I\
\xea\x16\xf9\xcc\xbf<\xc2\xd5\xef\xfa\x0b~\xfc\x8b{vJa6K\xe9\n\xa2\xac\xa2g\
\xdfX\xe7\x96e\x91\xa6)q\x1c7\xbdwJ)3\x8d\xba#\xcc\xf00\x8e\xb24\xf3F\x94\
\xc2FH\x07!m\xacl\xe6Y\xadb\xd2$D\xa2\xf0\x1c\x1b\xcf\xb1\x89\xe3\x10\xdf\
\xf3\x9b\xc3F\x9d$\xa8=D\xfd\xf6\x0f\xd2\xc1<u[\xe1y`\xb5\xa4)\xa1< \x87#\
\x03\xd0\xe0\x05\x01\xc4!\x05\xdfexx\x84\xa5\x0b\x8b\x946=\x8a\xd2\x11\x08\
\x9bz\xac\xa9&\x92\x9bo\xbf\x8bu\xdb\xeb\x88\xa0\xab\xb9p}sA\xc0Z\x15#\xed!\
\xd5\x91a\xf6\x85\xed\xae\xb5FJ\x89\xe7yMG\x8e\x94\rG\x8f4\xa3\x05\xc76A\x08\
\x04\x08\xdbL\xa4,\xc6\xed\t\x81\xc6\x96\x02\xd7\xb6\x18_:[\xe18\x16f\x1dw\
\xb3\xa7\xb0m\xe4\x1e\xe2\xfb\xfb\t\xe9\xd9\r\xc9\x18M\x8cl\xa8^\x8d\x99\xd1\
By\xd9{f\x87\x0b\x01\xa4t\xb7\xbb\x1c\xb7z1\xe7_|\x06\x1d\x81\xce\x86\xdc\
\x02ay\xb4\xcd\x9e\xc7\xa7\xbe\xf0\x15\x0e:j5\x8f\xae\x87\xaa\x86D\x99\xceC\
\xd8\xd9\x84\xc7:\xa53\x1f`\xe9\xe7/\xe9\xad}5\x8c{\xccT\xa3`S\xca\xcc\x1f\
\x90\x11\xcexN\xfd\xc4\xde\xf9\xf9W\xcb\xef\x1f\xa47:W\xa9\xc8\xca\x14\xc8b1\
-\xa5\xa5\x8d\x87\xd6\xa8\xb5\x81\x82\xd4\x9c\xbc\xfa\x00\xde\xf6\xba3\x99\
\x93O\xa1>\x80\xaf\xebT\x06w0\xdc?\xc4\xdc\xa5\x07q\xed\xf5_\xc3\xeb\xceb\
\x01\xd2\xf8\xff\xb0=\xd3p\xe2\x08a\t\x8c\x1e\xd8w\xf6{\xeb\xd4\x9e\xe6\x9f\
\xec\xda\x85\xd5$|\xe7\xb3\x89\tY\xf6\xcd\x072\xfe\xd2r\x8f\x16{+\xf6\x0f\
\xd2\x1b\x84\n;+\x97\xce\x86V\xad\x8d\xde2\x81\x1c\xe3\xb4i\xe4\x86\xa4\xf4\
\x16\xe0\xb8e\xf0\xe1w\\\xce\x11\x8b\xdah\x17U,\xea89\x9fg\xd7oa\xb8\xa6\xf9\
\xc4\xe7\xef\xa0?1\xd3\xcdTb\xcbx\x06\xa5o\xe6\xb3\xddG\xd3PL0\xdev\xda\xa6\
\x11\xe8\xac\xd3j%\xbc\xb5\xa8\xa8\xf9\x1cv\x8a\xf7M [0\xf1}7\xd8OH7H1+I\x99\
|\xf3\x16CE@$\xcd\xacV\xa1\x04-\xc7\x97\xd2\xc8\xbc\xfa\x9c\xbd\x12>\xf6g\
\xaffA>bqO\x0e\x9d\xd6\xb1\x1d\x17\xa7m\x167\xff\xecn\xbeq\xd3\xd3\x0c\xa4\
\xa0\x1c\x87\x94\x00\x84\x8f\x92\xbe\xe92\xb4\xb5\xd7R\xb4;\x08!\xcc2\x9e-\
\xc47\xc3\xb1\xdaj\xe6\xe26\x08oPkgS\xbdL\xdc\xda\xf2j\x11~\xcd\xc4\xf7\xdda\
\xbf \xbd\xb5:\x14\x1aFL\x16\xaa\xcdn\xd8\xd8\xdc\xe3\xce\x140\x99\xae"I\xc9\
\xa5\x90\x8b\xe1\xb4\x83\x1d\xfe\xe9\x83o\xa5\xdd\x89\xe9\xca\xfb\xd8\x9eK\
\xdf\xb6~f/]\xc9Wo\xb8\x99\xfb\x1e\xad\x9a\x86\x83\x99\xf5^\xfaEcX\xa9\xe7\
\xdb\xa3\xef\xe1\xde\xb2\xbe\xbeU\xa14\x15y\xab\x86\xd1\xbbyg\xa22\x9a\xe4\
\xeb]\xb0_\x90\x0e\xe3s\xbf\x98\xca\r\x05\x8d\tDE\x0cr<Z\xdfxAf\xd8i\x01i\
\x8a\xafJ\xb4i\xc5\x11\x0b\xe1\xf3\xff\xf8&:\n\x0eI\x12\x83\x9fg\xa8\x1c\xd1\
7Z\xe7\xc6[~\xc6\x13[\xcc\xb1\xea\xdat\x17\xc0\xf3\x9e7pgg\x8bRj\xd7~\x9d\
\x86:\xcff\xfb\xd0\x89y\xa9l\xd5jhf\x9aiZ\xde[\xce\xa3vz\xed\x0e\xfb\r\xe9\
\x12\xd3\xf2-\x1c,|\x9a)N\x02\x10\xaa9\x0f\xbb\x85I\x96U\xb1\xa2\x99\xf7)LQC\
82@\x0e\x98\x9b\x83\x1b\xbex)\x07\xf6X\x14\x9d\x04[Z\xcc>\xe0`~t\xfb\xef\xf8\
\xd1\xdd\x8f3\x02\xd4%D\x8d\xfc\xadf\x9e\xfb$\x8fu_\xf5\xf7{\xdca\xef\x8dH\
\xb9\xd3\xfb\x9e\xf6\x99\xd2\x10\x98\xb5tl\x01\x0e\x0e\x16\x05 \xc8\xa2`f\
\x98\xe6Ac\xab\x99\xb9\xd9\xf5Ll\xde\x96\xd9\xd87\x8f\xd76\x1b;IY\xe0\xc2\
\x81\x12\xbe\xf6\xb7\x97q\xc9\x89+\xa8o\xde\xc8\x8e\x11\x8d\xbb\xe4\x04>\xfd\
\xed\x9fs\xe3\x03\xb1I\xd0\x14\x1a\xc6v@2\x04\xba\x86\xd2!Ze\xae\x1c\x9d\x12\
Gu\xb3@`\xc3\xa8\xcc\xd0\xe8\x8e\x1a.W\x98h\xc8M\xf6\x1a\x0f\xfa4\xfa\xeal\
\x18\'\xadl8G3\xdc\xbc\xbb\x97\xdc\xe9}w\xd8/HG\x98\x11\x94\x91d\x89h\xa4.\
\x88\xc6\xd2\x9eF\xa6\x1b\x8b\xceg\xae\x8f\xf1,\x17\x01\xb5\x84\xcc\xf6\xd1P\
\x19&\x9f&\x1c\xde\x0b\xaf;\xef\x18\xde\xf0\xda\x97C8J\x14\'\xf8\xdd\x0b\xf9\
\xf4\x17\xff\x93G\xb7\x82\xb0\x05\xe4\xf3P\xab\x80\x00)\xe4x\xb2\x84\x108\
\xb2\xb1\xd2d\xb6m\xe7~6S\xe1\xad+5\xeci\xc5\x86\x89[[\xc8\xdfK\x9a\xf6\xd2x\
\xdfOH\x7f\x9e\xd0\xc2\xcc[\xa8%\xa0ll\xa7\x13\xb4\x8d\xa3\xe1\xa8e\xf0\xce7\
\x1e\xc6+_v \x0c?\x85]\x1dcd{\x89/|\xf9G\x8c\x01\xa1\x93G\x15g\xa3\x85G\xd2\
\xacv\xcb\xe4\xd2r\xd0\xc2BO\xa2}\x05\xec\x91\xe0\x17\x13\xd3\x82t\x03E=\xa9\
\x93&Y>\x9e\x06B\x85\xa7`\xb1\x0f\x7f\xf1\xfa\x97\xf0\xea3\x0e\xc5\xad\xf6\
\xd3\x95\xcb\xf1\xe0Ck\xf8\xee\x1dCl\x03F,\x8f\n6\x89\xb2\x88\x12\x88\xb54\
\x8e`\x01\xd2\xb6v\x89m4U\xecsH\xf6\x8b\x85iB\xba"%\xc6\xb1m,O\x1a\xc2\xe3\
\x10\xcbI)\xc8\nAR\xe6\xb0N\xf8\xec{\xcf\xe3\xb4C\x17\x10\x8f\xf4\xd1\xd3;\
\x87\xcf~\xf5F\x1e\x19\x82\x1dd\x0b\x13H\t\x96eV\xf2nt\xe3\xcdj\x97\x0c\xcdb\
\xc8\x19\xd2_tX\x8dq\x97P`\xc5&jG\x08:&oC\x1b\x8a\x9c\x86/~\xec2.8c5\x03\xdb\
\xd7SKS>\xfc\x0f7Sa\xbc\x10\xa3\x91I\xd5(\xa7\x9b4\xa3f\x1fy\xf1\xfeX\x98&\
\xa4K\x046\xf50\xa4R\x1f\x05\x19\x82\x15\x9a\xc9\x7fc\x01\xa9G:Z\xa6M\x80\
\x1d\xc2?\xbe\xefB\xce=\xf3p\xfa\xb7\xaf\xa7ZQ|\xeb\x07\xfd\x8cE\xe3\x1exM\
\x96\x9e\xfd\\\xe4NQ\xe2\xa7\x05\xe9\x02H\xa2\x84\xbc\x17\xe0\xfb.!\x11\x91\
\x8a\xc1\xf1\xc1\xc9\x93\x94\x13\xdc|\x1b\xd5\xe1Q\xba=\x93\xac}\xcd\x9f\x9e\
\xcfU\x97\x9cM\xdf\xba\xa7\xf8\xc1\r\xdfah\x08\xc6j\xe3\xf9yM\xe2\x9f+\x7fn\
\n\x12?-H\x07\xf0\\\x89@e\x91x\x0f%\x0b\x84\x04\xa4\xc2\xc6.\x06\xe8\x04\xf2\
m\xed\x08B\x02\x14s\x1cx\xcf\x1b\xce\xe2\xeaW\xbe\x94\xea\x96g\xf8\xfb\xbf\
\xbb\xaeIx\x98dCg\xb1\xc7\xac\xa4qL1\xe2\xa7\t\xe9\r_\xbd\xa9\xa5\xd3\x04\
\xa4\x98!X\x8c\t\xd6\xa4\r\xef\x88\x12\xc84&\x07,*\xc0\xe5/;\x92\xf7\xbd\xf3\
r\xb6\xac\x7f\x86o|\xf3\x87\xa4\xda\xe4:TjF\x83( \x8e\xf7A\xc0\xfd\x05\xc4\
\x0bV\x9f\xfe\xe2"\xc5\x84Q$\x82B\x93\x9f\x863\xd5\xc2\xccLavu\xd1Q\x8a\xed\
\x19g\xdeQ\x8b-\x96\xf5\x9e@2\xb6\x99\x9b\x7f|3\x87\x1dr\x10/9q%A\x00c\xd5\
\x94\xf6\x9cE\xb5^\xc5u\xf2\x13\xdd\xa5\xcf3*\xf7\xc7\xc44!}\x1c\x96\x1e\x0f\
X\x08\xc65\xafB\x91"\xb1$H\xcb2qZKa9u\xba\x02\x87\xab\xdf|)\x96\xedr\xe3\x8d\
7\xb0\xfa\x88\x8f\xd0\x96\x83|\xce\x0c\xdf\xf2\xc5|\x16\xe7\xdf\t\xad\'\x98B\
\x98\xba\xcdq\x9f\xa2Q\x80db\xec\xb66\x93\x15{\xda\x8c\xdcl\x14)\xa9\x19{\
\x9bl\xa5,\x11Sd\xbe\xf5\x08O\xc2[\xde\xf8\n\x0e=t\x15\xd7_\xffO\x84Y\x0e\
\xa3\xd2\x8d\xf8\x9f\xca\xca$\xc6O9\x05\xf9\x06\xa6\r\xe9\xa6B\x86\xc6j\xc6:\
\x1b\xaek\xb0\xb5\xc2\xc5\x04rD\x16\x97O\x9b\x19\x0c\xc2,C\xa5m\xea\xa1i:oy\
\xd3et\xb4\xb7\xf1\xc8#\x0fQ\xadG8V\xebr\x00\xc6\xd4\x9b\x10\xdf\x9e\x82\xd5\
1\xd3\x83t\r\xba\x99V\x94`*\'J\xa0\xca\xa6"5\x95\xd8\n\xb3\xc4\xbb0C\xf7\xa4\
\xe1bW9H=|\x0f\x12m\xfa\xf9w\xbf\xf3\xadl\xd8\xb0\x0e\xcb\x16\x94\xab\xa5\
\x9dO\xd5Dk\x10v*I\xfd\xf4 \x1d&\x96\x80\x88\x14D\xe6j\xc9\xc4R%\xe3\x99*\
\xa6+\xce\xe6\\\x94\xa2\x19\xd2lD\xf0\xd2\x04.\xbf\xe4b6\xad_G1W@L)J\x9f\x1b\
\xd3\xc3\x90\x13-#*aaT}\xf6Yg\x81\xd8,\xc9f\\\n\xb2\x19\xa2\xb3\xe8\x89\x8b\
\xd1\xf6`\x96\x10\x95\xc0\xca\xe5+\x1a\x87\xdf\t\xb2\xe5/\xbb\xd9\xe7\xc5\
\xc3\xf4 \x1dZ\x9ezKB\xe1\x84\xed\xbb!F\x8c\xbf\xfdOgq\x99JD\xb7b\xfa\xa8\
\xf7\x1941C\xfa4\xc4\x0c\xe9\xd3\x103\xa4OC\xcc\x90>\r1C\xfa4\xc4\x0c\xe9\
\xd3\x103\xa4OC\xcc\x90>\r1C\xfa4\xc4\x0c\xe9\xd3\x103\xa4OC\xcc\x90>\r1C\
\xfa4\xc4\x0c\xe9\xd3\x103\xa4OC\xcc\x90>\r1EH\xdf\xbfr\xcc\xf6wL\t\xd2\xe34\
d\x86\xf8\x17\x0eS\x82\xf4hJ\xae\x82\xf2\xff/\xa6\x04\xe9\xbe\x9c\x91\xf2\
\x17\x12S\x82t-\xfe\xb7\xb3\xf3M\xd5)\x1f\xa6\xe25\x8dcJ\xa4@\xdb\x93\xb5=\
\xadI\x84\xc2V\x16J*\xd0\x12\x89&\x15\x02\x89"\xd5\x1a+M\x88\xa5\x8d\x14)\
\xb60\xab\xbc\xbdX\xd0\xa9"\x15\x02\x91\xc6\xc4\x96\xc6\x93\xde\xa4W\xb3~\
\xfdz\x1c\xc7a\xce\x9c9\x13\xb67\xe6\x8d}!0%$}2\xb2T\xa3\xd2@\x98\xd9^\x04)\
\x1a\xcd\xffk\xef\xdc\x83\xa3\xaa\xee\x00\xfc\x9d{\xf6\xbd!\xbby@\x1e\xc4@B\
\x91G\x84\x12\x9fT\xd1RPl3\xad\xb4\x96\xf1Q\x18t\xda\xd2Z\xb1\xd3\x8aN\xad:\
\xe3L\x9d\xda\x87Z-\xed\xd4>\xa6\xd5"\x91\x82\x03\x96\xfa\xc0X\xa7(T#\x85\
\x82$D\xb0\x89\x08I\x0c\xe4\x9d\x05\x93\xecn\xf6\xde{\xfa\xc7n\x96DQ\xab\xd3\
Ml\xef\xf9f2\x9b=\xd9\xdc\xf3\xf8rN\xee9;\xfb\xfb\xc9\xd4g\xc5\x84er\xef\xd6\
F\xae\xb9\xf7y\xdel\xef?\xed5\xc6\n\x05\xbc\xde;\xc0\xf7\xff\xb0\x93\x1d\x07\
;pY\xa7\x17\x0eP]]MMM\r\'N\x9c\xe0\xd1G\x1f\xa5\xb1\xb1\x91G\x1ey\x84\xba\
\xba\xba1k\xef\xc7D\xfa;IJN\xcem\x93\x96\x9e(\x1bw\x1de\xd3\xee\xa3\xe9\xb4\
\x1b]\t\x17?\xdc\xf2\x1a[_\xf3\xf0\xa7\x1dM\xe3\xbe\xa0>\xb3\xfd ?\xdf.\xb8\
\xfb\x99\x06\x94|\xff\xdd\x88\x10\x82p8Lqq1S\xa7N%\x1a\x8d\xd2\xd1\xd11fm\
\xcd\xe8\xf2>\x94\xb0pK\x85e\x08\xa4J~TT\x90\n\xa5-\xe0T<\xae\x91\x03\x94\
\x9c#B\xd9\x08\x1b,\xe1\xe2\x9fotp\xdd/^%\xe8J\xb0\xf4\xfc2\xfcJ\x91\xebMp\
\xe3\xe5\xa5\xec>\x1c\xe1\xf3\x17\xccyG\xcd\n\xc5\xc8x\xab#\xeb\x19\xf98\xfc\
\xddp\xfd\x1fm\xb5\x10(\x96]:\x8b!\xeb0\x8b\xcf\x9d\x82aI\xde/i\xfd0\x15\x15\
\x15H)Y\xb6l\xd9\xa8\xccM\x99&\xa3\xd2=n\x89\xb2m\xa4\x82\x98ms\xe8\xf8 GZ"\
\xc4\xf0p\xd6d?3J\xfc\xf8\x94\x1b%-\x94\xad\x88\x9a\x06\xbb\xdf\xec\xa3\xad;\
BQ\xb6\x97\xca\x99\xc5\xf4\xf4D8\xdc\x97\xc06\x14\t\x95M}k\x07><TL\xf6s\xd5\
\xa2O\xb0t!L\xce\xf5s\xe8X7\xfdC\x10\xf6IJ\'\xfa\x91\xc2\x83\xcbV\xd4\xb7\
\xbf\x8d\x95\x18\xa2(7\x8b\xc2\xa0 nx9~r\x80\xba\xd7\xfbPq\x93Y3\x0b(\x0f\
\xbbp{\rL\xdcH;\x81\xc2`\xa0\x7f\x80H$Bnn.>\x9f\x0f\xc30\xd2\xe1>G\x86\xff\
\xec\xee\xee&\x18\x0cR\x9a\x93\xc5\xf7\xae\x9e\x9b\\:\x85\xc0\xb6m\x84\x10$\
\x12\t:::\x08\x04\x83\x84\xc2\xa1d\xfax\x92\xf9\\JJJ\x00(**\xca\xa4\x86w\x91\
\xf1\x1b9\x05\xf4\x0f\xc6\xf9\xea\xda\xbfSS\xd7\x8de\xc0\x90p\x13\x182\xf8\
\xe6US\xb8\xe7\xaasp+\x83W\x8e\xf6p\xd3\xaf^\xa6\xfe\x98\x00\x97\xc4P6\xb3\n\
\x0f\x92\xe3\x1f\xa2\xf6\x88\x0b\x0bA,\xa1\xb8\xe8\xce\xed\x84d\x90\xa3\x0f/\
e\xf9\x8f\x9f\xa2\xa5g\x90_\x7f\xe7\x12ZZ\x8e\xf1\xe3\'\xdb(\tK\xf6\xfe\xe2J\
\xbcF\x82\x9e\x98\xe4\xf2;\x9f\xa6\'\xe1\xe1\xe9\xdb?E\xce\xec"\xee\xdf\xba\
\x97\x9f<\xf9&\x03\xa6@*APD\xf9\xc1\xf2O\xf2\xed\xcf\xcd\xc1\xad\x12\xf4\x0f\
Fyx\xddz\xfeQ[\x0b@ \x10\xe0\x8a+\xae`\xe3\xc6\x8d\xdcr\xcb-TVV\xb2v\xed\xda\
t\x1e\xb5\x83\x07\x0f\xb2r\xe5J\x16/^\xccu\xd7]\xc7\xea\xd5\xab\x99?\x7f>\
\x00555l\xde\xbc\x99h4\x8a\x90\x06\xf3\xe7\xcf\'\x1e\x8f\x8f{\xae\xf5\x8cK7\
\x0c\x83`\xd0O\xc1\x14\x1f_\xcc\x9d\xcc\xf2\x85g\xd1u\xbc\x9b\x1b\xffX\xc7\
\xef6\xb7q\xd3\xc2\x99d\x07\x13\\\xff\xc0v\x0e\xf7e1=4\xc8\xf9\xb3\xf2\xd9\
\xd3\xd0\xca\xea+?M\x7fg;\x86g\x80\x17\x1bM|\x86\xc9\x17..\xa7\xc0\xb6\xf0\t\
E\xdc\x08\x11w\x05\x10\x18\\\xb3\xe4l~\xba\xad\x87\xa3\'\xa0\xb6\xfe-.\x9dW\
\xc2\xb6\xbdo\xd0\x13\xf70\xbb\xc4\xcf\xe2Y\x93x\xf8\xa5f\xee\xde|\x84)9n~p\
\xd5\\ZNJ~\xb4\xe9\x00\xb7\xafo\xe2\xe2\x8aR\xce)\xf4\xf2\xf0\xba\xc7\xa8\
\xab\xdb\xcf\r7\xdc\xc0\xec\xd9\xb3iiia\xdd\xbau\xa3\xfa$\x84\xe0\xc0\x81\
\x03,X\xb0\x805k\xd6P\\\\\x9c\xce\xc04\x1c!r\xd7\xae]<\xf6\xd8c,Y\xb2\x84\
\xcb.\xbb\x8ch<\xc6\x96-[8r\xe4\x08eee\x99\x1e\xf6\xf7e\x0c\xb6l\n\x03\xc1=_\
\xba\x88\xed\rm\x1c<t\x8c\xde\x84"/\xec\xe7\xf8\x90\xc1\x81\xd6n\xbaO(\x8ev{\
\xc8\t\x98<{O\x15\x85a7\x83q\x93l\xaf@\x1a\x85\x94\x9c\xd1\xceK\x87^\xc2#\
\x03\xfc~\xd5\xd9\xf8p\'\xc3v*\x13C\xc5\x10X\x94\xe5x\xf9\xec\xd9a\xb6\xee\
\xe9e\xfd\xae6\x16W\x96\xf0\x97\xdd\xc7\xb1\xe4\x04\xae\xbfx\n\t\xe9\xa6\xfa\
o\x87\xb0e\x16\xe7\x9d\x99C\xdc\x90\x14g+\xca\x8b\x0c\xea\x8f\xb9\xa9\xd9\
\xd7\xcc\x8c\x85S\xa9\xdd\xb3\x8f\xab\xaf\xacb\xc1\x82\x8bP\n\xc2\xe10+V\xac\
\xe0\xc1\x07\x1f\x1c\xd5\xab\xe2\xe2bV\xadZ\x85\x94\xf2\xb43\xf7\x85\x17^\
\xa0\xbc\xbc\x9c\xe5\xcb\x97\'\x0b\x84\xe0\xa6\xd5\xab\xf9\xee\xcd7\xff\x7f\
\xcf\xf4!\xc0m\xc7\xd9\x7f,\xc65\xf7\xee\xe0H\'\xb8U?\x81,\xc9@\xdc\x83\xe9\
\xf6\x10\x8b\xda\xb4\xf7F\xb1\x85\x8b\x8aBIA\xbe\x17\xaf2\xf1\xf9$\t\xe1B*\
\x0b\x9f\xa5\x007\xb64\x91J \xb0\x91\x02\xd4p\\8\xe5A!\xf8\xda\xc2)<\xb5\xbb\
\x87\xa7_\xe9\xe4_\xcbb\xbcX\xdfF\x96p\xb3\xec\x92R\xb0-\x8e\xf5EQd\xb1\xb5\
\xb6\x93-\xbbZ\x91\xb6A\xc2m\xe25\xfd\x9c8\x99\xa0\xab\xfb8\xb6m2m\xea\x0c\
\x840Rq\xe2\x14\xd3\xa6M{W\xdfJJJ\x90R\x9eJ\xbe\xf3\x0e\x91]]]\xcc\x9b7/\xbd\
\x02\x00\xf8\xbc>\x8a\n\n1\xc6\xf9\xc89\xa3\xd2]\x98\xd8\xb8\xf9\xe5\xb6\xfd\
4\xf5x\xa9\xaa\x94\xfc\xe6[K\x08\x05}|\xee\xee\xed\xbc\xf2F\x84!\xe1\xa6p\
\x92\x1b0y\xbd\xb5\x9f\xe6\x9e(\xd3s<Db6\xd9~\x13\xd4\x10\x83R"\x94\xc44\x87\
\x18\xb4%R\xa6\x02\xfa\x8c\xfa\xec\xb8\xc9EsK\x99^P\xc7\xbf\x8e\x0b\xbe\xf1\
\xd0+D\xd4\x04\xbe|n\x1e\x85\xd9~lC2=\xdb\xc5\xe1>\x0f\xd7.\x9e\xc07/=\x13\
\x054\xf7\xbd\xcd\xe4\xf0\x04\xf2C\x01\xf2I\xe0\xb6\\tt\xb6\x02\xb3\xd2\x15\
\xb4\xb5\xb5}\xe8\xbeggg\xd3\xde\xde\x9e~>\x9c\xd2\xa3\xa7\xa7\'}\x037^d|\
\x9fn+\x85e&\x90v\x82\x81\xb8\xcd\xfe\xd6\x08\x0f=\xf9\x1a{\x9b\xdeF\xe1A\n\
\x9b/\x9d\x13bj\xbe\x8bn\x95\xc7\xa5w>\xcb\xb2\x07j\xa9\xbc\xf9\x19~\xbb\xa3\
\x19Sx\xc8\x91&\xca\x10\x0c\xaa +\xee\xdb\xcew\x7f\xb7sD\n\xdcTG\x0cI\x96\
\x01+?3\x15\xa4E\xed\x1bCH\xcbdY\x1f\x03%\x00\x00\x04\xd0IDAT\xe5%e\xb8\x84\
\x8d[%\xb8\xfe\xb3sq\xa9A\x9e\xd8\xd5\xc6\xb6=\xcd<U\xd7\xce\xad\xbf\xd9\xcb\
\xdam\r\x14\x04\r\xb2\xc2!\xce={.[\x9f\xf83\xfb\xf6\xbdJ4\x1a\xa5\xa9\xa9\
\x89\xea\xea\xeaw\xf5\xeb\x83N\xcf.\xbc\xf0B\x1a\x1a\x1ax\xee\xb9\xe70M\x93X\
,\xc6\xfa\xf5\xeb\xe9\xed\xed\x1d\xf7\xa8\xd0\x19\x9d\xe9&.\xa4=\xc0\x8d\x97\
\xcfb\xfb\xfe\x1d\xbc|\xd0`\xf7k;)\x9c\xe4ez\xa1\x97\xa6\xb7\xfa\xb0\x84 \
\x14\xf0\xf0\xc4\xad\xe7\xb3\xea\xa1W\xd9\xff\x96\xcd\xb6\xbd\'q)\xc9\xa6\
\x9azV\\0\x95\x0b+\xce\xe0\x82i\r\xec9<\xc0_\xeb\x13\x84}p\xff7@*\x13\xc9\
\x10B\xd9\x98H\\\xca\xe4\xdaE\xb3\xb8\xf7\x89\x06N\x0c)\xce\xcc7X4\xa7\x08\
\x13\x85\xa1L\xbe\xb0\xa0\x9c\xfb#\x11\xee~\xbc\x99\x1f\xfe\xe5\x18R\xc4\xf0\
\xd9\x01\xcas\xb3\xf0\xba<\xc4%|\xed\xeb_\xe1\x0f\xbf\xdf\xc0\xcf~\xf6\x00B\
\x80\xcb\xe5b\xe9\xd2\xa5\xb4\xb6\xb6~\xa8\xbe/Z\xb4\x88\xf6\xf6v\xaa\xab\
\xab\xd9\xb0a\x03J)\xca\xcb\xcb)--\xcd\xd0h\xff\xe7\x88\xfb\xee\xbbO\xed\xdb\
\xb7\x8f\r\x1b6d\xa8\x8a\xe4AIg\x7f\x94\x7f4v"%\xcc\x9f9\x99\x13o\xc7\xb0\
\x12\t&\x86\xfc\x84\xfc\x12\x81"\x8a\x87\xfa\xc3]\xb4uE\x99\x98\xebb\xde\'\
\x8a\xc8\x92\n0\x184m\xfe\xd9\xf4\x16]\'\xe3\x14\xe5eq^y!\xcd]\'1M\x8b\xe2\
\x9c \xa1\x80\x07\x10\xd86\x1c\xef:I\x9fm3\xc1\xe7\xe1\x8c\x9c@*\xdbYre\xb0\
\x95\xa2\xaf?N\xed\x9b\x9d\x08s\x889\xe5\xc5\x14\x85\xfdxD\xf2\x88F\xd9\x16\
\xb6\xad\xe8\xeb\x8b\x10\x89\xf4QPP@,\x16c\xcd\x9a5\xdcv\xdbmTTT\x10\x89D\
\x10B\x10\n\x85\xd2\xe93-\xcb\xa2\xb3\xb3\x93\xec\xecl\x02\x81@z6wvv\xd2\xda\
\xdaJ0\x18d\xfa\xf4\xe9\xf4\xf5\xf5!\xa5$\'\'\'C\xe3\xfd\xde\x18\xd2%l\xcbTc\
p\xf7\x9e<\x19\x9b\x18p\xf1\xf9O\x16\xa7\x8a\x14\xd9yA\x0c\x14\x86\x12\x88T\
\x1e\x14\xbf\xa18\xaf<\x8bs\xa7\x05\xb0U \x95t/9\xa8\x01\x15\xe7\xe2\x99%\
\xd8*\xf9\xff\x1baQ61\x94\xec\xcc\x88\xc4|B(\x8a&N\xa00u\xf0f\xa4\xda\x90F)r\
\x82^\xaa\xe6\x94\x80R\x18\xc2D\xa88\x08/\x02\xc1\xbeW\xf7STTDaa!\xf9\xf9y(\
\xa5x\xfe\xf9\xe7\xf1x<\x94\x95\x95\xa5\x8fP\x93u\x9d\xba\xaea\x18\x14\x14\
\x14\xbck\xe9\xce\xcf\xcf\'???\xfd\xfa\xbc\xbc\xbc\xff\xe6\xe0~$\xc6\xec]6e\
\xb8\x91*\x991\xcd\x12.\xdc\xe9S\xf4d\xf8\xa6\x84\x91\xcc\xadh\x08\x1f\x86\r\
R(\x10F\xfa8U\xb9\xfd(\x92\xe5"\x15\xf2\'\x95\x03\x99tbZ\x14B$\xbfP*\x19\xf1\
\x11c\xd4\x91\xec\xf0\xccD\x812\x04\n\x0f\x16"\x99\xed\xcd\xb6\xd9\xb4i\x13\
\xbd\xbd\xbdTVV\x92\x97\x97Gss3\r\r\r\xac\\\xb9\x12\xbf\xdf\xff\x9e\x99\x1a\
\xde\xab<]\xdf\xc7\x881\x93.\x95\x8d\x9dz\xdf\\*\x1b\x84\x1c\x95\x1e\xd2\x95\
\n\xa0;\x1c\xcbMa`\xa5~,\xd5p\x16\xd2\xe1?\x04\x99\x0e\xe4\x0b\xc9\xcc\xab\
\xa76F\xc9P}\xb6p3|\xf9\xf7\xbam\x1a.\x97\xa9\xe5\xdf0\x0c\xee\xba\xeb.v\xee\
\xdc\xc9\xa1C\x87hlld\xd2\xa4I\xdcq\xc7\x1d\xcc\x981c\xdco\xc0\xfe[dT\xba\
\xb2-\x84!1\x01\x97\x10\xa7\xf6\xa7\xa3\x1fF?I\r\xac\x18\xd9\xb8\xf4`\'\x15\
\xa6\xc3{\xa5\x8aO\t\x1f\xfe-\xf5\xc1\xdb\x92\x11\xf5\x8c$++\x8b\xaa\xaa*\
\xaa\xaa\xaa>\xe8\n\xff\xb3dt\xed\xb1\x85L\x07\xe0\x1d=\xbc\x1fe\xc6\xbc\xdf\
\x9c=\xddk?\xec\xb5\x9dCF\xa5\'l\xc5\xe8,\xb1\x9a\x8f\x03\x19]\xde}\xf2t\xeb\
\xb8f\xbc\xf9\xf8\xddZj2\x8e\x96\xee@\xb4t\x07\xa2\xa5;\x10-\xdd\x81h\xe9\
\x0eDKw Z\xba\x03\xd1\xd2\x1d\x88\x96\xee@\xb4t\x07\xa2\xa5;\x10-\xdd\x81h\
\xe9\x0eDKw Z\xba\x03\xd1\xd2\x1d\x88\x96\xee@\xb4t\x07\xa2\xa5;\x10-\xdd\
\x81h\xe9\x0eDKw Z\xba\x03\xd1\xd2\x1d\x88\x96\xee@\xb4t\x07\xa2\xa5;\x10-\
\xdd\x81h\xe9\x0eDKw Z\xba\x03\xd1\xd2\x1d\x88\x96\xee@\xb4t\x07\xa2\xa5;\
\x10-\xdd\x81h\xe9\x0eDKw Z\xba\x03\xd1\xd2\x1d\x88\x96\xee@\xb4t\x07\xa2\
\xa5;\x10-\xdd\x81h\xe9\x0eDKw Z\xba\x03\xd1\xd2\x1d\x88\x96\xee@\xb4t\x07\
\xa2\xa5;\x10-\xdd\x81h\xe9\x0eDKw Z\xba\x03\xd1\xd2\x1d\x88\x96\xee@\xb4t\
\x07\xa2\xa5;\x10-\xdd\x81h\xe9\x0eDKw Z\xba\x03\xd1\xd2\x1d\x88\x0b`\xe3\
\xa6\xc7\xc5\xc6M\x8f\x8fw[4c\x84PJ\xa9\xf1n\x84fl\xf97\x87\xba&\x9e\x12\xca\
GT\x00\x00\x00\x00IEND\xaeB`\x82'
def getWizardDataOld():
return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00t\x00\x00\x01\x04\x08\x06\
\x00\x00\x00\xf9\xcf\x10R\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
\x00\x0f\xdfIDATx\x9c\xed]K\xb6\xe3(\x0cU\xf5\xa9MU\x86\xd9\xd6\xcb\xd0\xd9\
@@ -962,6 +288,6 @@ def getWizardBitmap():
def getWizardImage():
stream = cStringIO.StringIO(getWizardDataOld()) # NOTE: This reverts us to the bitmap Peter likes.
stream = cStringIO.StringIO(getWizardData()) # NOTE: This reverts us to the bitmap Peter likes.
return ImageFromStream(stream)

View File

@@ -109,7 +109,7 @@ class XmlCtrl(CodeEditor.CodeCtrl):
# Tag
self.StyleSetSpec(wx.stc.STC_H_TAG, "face:%(font)s,fore:#00007F,bold,size:%(size)d" % faces)
# Attributes
self.StyleSetSpec(wx.stc.STC_H_ATTRIBUTE, "face:%(font)s,fore:#00007F,bold,size:%(size)d" % faces)
self.StyleSetSpec(wx.stc.STC_H_ATTRIBUTE, "face:%(font)s,fore:#007F7F,bold,size:%(size)d" % faces)
class XmlOptionsPanel(STCTextEditor.TextOptionsPanel):
@@ -118,13 +118,16 @@ class XmlOptionsPanel(STCTextEditor.TextOptionsPanel):
STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Xml", label = "XML", hasWordWrap = True, hasTabs = True)
def GetIcon(self):
return getXMLIcon()
XMLKEYWORDS = [
"ag:connectionstring", "ag:datasource", "ag:editorBounds", "ag:label", "ag:name", "ag:shortLabel", "ag:type",
"element", "fractionDigits", "length", "minOccurs", "name", "objtype", "refer", "schema", "type", "xpath", "xmlns",
"xs:complexType", "xs:element", "xs:enumeration", "xs:field", "xs:key", "xs:keyref", "xs:schema", "xs:selector"
]
#----------------------------------------------------------------------------
# Icon Bitmaps - generated by encode_bitmaps.py
#----------------------------------------------------------------------------
@@ -136,19 +139,17 @@ def getXMLData():
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\x01\x1aIDAT8\x8d\xed\x92?N\xc3P\x0c\xc6\x7fv\xf2\x924\xa8E*7\xe1\n\xbd\
\x01G\xa8\xd8\x989\x05S/\xd0\x0110\xf4$\xa0J\xdd\x18\x18XY\x82\xa0\xad\xd2\
\xfc\xe9{f\x08-\x02\xc1\xd4\x85\x01/\xb6l\xd9\xdf\xf7\xd9\x16\xd1\x88CL\x0f\
\xea\xfe\x13\x03\xc4\xae\xcf\x8d\xb6\x84\xba\x84m\x83e\x03\xa8\x96\x88F`\x86\
\xf5\x06\xc8\xba\x80\xf4\x08\xda\x1a\xf2cB04q\xcc\xe7\x0bb\xbb\xbf\x85\xd75\
\xf2\xb1K\xc9\x12\xa8\x9aO\x84o\x88\xb6\x0bbxx\x04\xc5e\xe0:%\xe5\xd4\xa0j:\
\x0f\xd4\x93\x82\xcd\xe9\x19\xf5\xa4\xd8\xd7\x97\xe2\x904\x82D\x89bA\xa5\xad\
!\x85\xb0\x82|,\x94S#\x1fw\xb8\xbe?\xc4\x8d.\xf0\xfd\xe1\x9e\x81\x7fk\x91\
\xd6\x83\x05\xcc\x0c\xf5\xea\xf0U@\xfb\xec\x9bw\x0c\x00\xe2\xab\xd1\x17\t\
\xd9\xcc \x80o\xc1D\x11\xbb<1^\n\xf0\xbf\xacy\x03\xf4~\xc8g0{R\xe2\x9b\xc5\
\x8aM\x03\xd4\xe0=\xb8\xb4;\x88\xc6 \nQ\x1e\xe1W\x1e\x89\xc1W\xe0\xb7\xa0=Hr\
\xb8{\x0e\xc8\xff+\x1f>\xe0\x1d~\xafr\x13\x04HY"\x00\x00\x00\x00IEND\xaeB`\
\x82'
\x00\x01\x08IDAT8\x8dc\xdc{\xf6\xde\x7f\x064p\xfb\xd1K\x06\x06\x06\x06\x86\
\xd7o\xbf2\xd4\xa5\xb93\xa2\xcb\xa3\x80\xbdg\xef\xfd\xbf\xff\xed?\x1c_\xf9\
\x04\xc13\xd6\x1f\xff\xbf\xeb\xf9\xff\xff\xcds\xf6\xfcgdbf\xc0\x85\x99\xb0\
\x19\xfa\xe5\x17\x82m\xee\xed\xcc\x90Z\xb7\x08\xc3\x950\xc0\x82\xcc\xf9\xfa\
\x07\xc1~\xfd\xf6+\xc3\xeb\xad{\x19\x1e?y\x89\xd7\x07,\xe8\x1aa\xb6\x9b{;300\
00(\xfdb`88\x7f\x19N\x03\x98`\x01\x86\xac\xf9\xd3o\xa8+\xa0\xfc\x07\x0f\x1e\
\xe3w\x01\xb2\x9f\xd15\xbf\xfa\x8e\xd7\x07\x0cL\xaf\xdf~%[3\xdc\x050\x8d\xa4\
jf````LkX\xfa\x7f\x8a\xcdi\xe2T\xe3r\x01\x03\x03\x03\xc3\xa7#\x13H\xd6|\xea1\
\x03jB\x12\xae\xffO\x14\x8d\x0cP\x0cx\xdb\xc8\xc8 \\\xff\x9f\xe1m#"\xf9c\xd3\
\x84b\xc0\xec\xa68\xb8j\x98fdM\xc8\x86\xc1\xd4 \xcb32213\xfc\xdc\x95\xfb\x9f\
\xdc0`A\xe6\x90\x03\x00\x11\x95\x8b;4e.A\x00\x00\x00\x00IEND\xaeB`\x82'
def getXMLBitmap():

View File

@@ -0,0 +1,549 @@
#----------------------------------------------------------------------------
# Name: project.py
# Purpose: project model for wx.lib.pydocview
#
# Author: Morgan Hua
#
# Created: 8/25/05
# CVS-ID: $Id$
# Copyright: (c) 2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
import copy
import os
import os.path
import activegrid.util.xmlutils as xmlutils
from IDE import ACTIVEGRID_BASE_IDE
if not ACTIVEGRID_BASE_IDE:
import activegrid.model.basedocmgr as basedocmgr
import AppInfo
#----------------------------------------------------------------------------
# Constants
#----------------------------------------------------------------------------
# Always add new versions, never edit the version number
# This allows you to upgrade the file by checking the version number
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
#----------------------------------------------------------------------------
class BaseProject(object):
__xmlname__ = "project"
__xmlexclude__ = ('fileName', '_projectDir', '_getDocCallback')
__xmlattributes__ = ("_homeDir", "version")
__xmlrename__ = { "_homeDir":"homeDir", "_appInfo":"appInfo" }
__xmlflattensequence__ = { "_files":("file",) }
__xmldefaultnamespace__ = xmlutils.AG_NS_URL
__xmlattrnamespaces__ = { "ag": ["version", "_homeDir"] }
def __init__(self):
self.__xmlnamespaces__ = { "ag" : xmlutils.AG_NS_URL }
self.version = PROJECT_VERSION_050826
self._files = []
self._projectDir = None # default for homeDir, set on load
self._homeDir = None # user set homeDir for use in calculating relative path
if not ACTIVEGRID_BASE_IDE:
self._appInfo = AppInfo.AppInfo()
def __copy__(self):
clone = Project()
clone._files = [copy.copy(file) for file in self._files]
clone._projectDir = self._projectDir
clone._homeDir = self._homeDir
if not ACTIVEGRID_BASE_IDE:
clone._appInfo = self._appInfo
return clone
def initialize(self):
""" Required method for xmlmarshaller """
pass
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
self.AddFile(file=xyzFile) # normally used for redo/undo
Add newly created file object using filePath and logicalFolder or given file object
"""
if file:
self._files.append(file)
else:
self._files.append(ProjectFile(filePath, logicalFolder, type, name, getDocCallback=self._getDocCallback))
def RemoveFile(self, file):
self._files.remove(file)
def FindFile(self, filePath):
if filePath:
for file in self._files:
if file.filePath == filePath:
return file
return None
def _GetFilePaths(self):
return [file.filePath for file in self._files]
filePaths = property(_GetFilePaths)
def _GetLogicalFolders(self):
folders = []
for file in self._files:
if file.logicalFolder and file.logicalFolder not in folders:
folders.append(file.logicalFolder)
return folders
logicalFolders = property(_GetLogicalFolders)
def _GetPhysicalFolders(self):
physicalFolders = []
for file in self._files:
physicalFolder = file.physicalFolder
if physicalFolder and physicalFolder not in physicalFolders:
physicalFolders.append(physicalFolder)
return physicalFolders
physicalFolders = property(_GetPhysicalFolders)
def _GetHomeDir(self):
if self._homeDir:
return self._homeDir
else:
return self._projectDir
def _SetHomeDir(self, parentPath):
self._homeDir = parentPath
def _IsDefaultHomeDir(self):
return (self._homeDir == None)
isDefaultHomeDir = property(_IsDefaultHomeDir)
homeDir = property(_GetHomeDir, _SetHomeDir)
def GetRelativeFolders(self):
relativeFolders = []
for file in self._files:
relFolder = file.GetRelativeFolder(self.homeDir)
if relFolder and relFolder not in relativeFolders:
relativeFolders.append(relFolder)
return relativeFolders
def AbsToRelativePath(self):
for file in self._files:
file.AbsToRelativePath(self.homeDir)
def RelativeToAbsPath(self):
for file in self._files:
file.RelativeToAbsPath(self.homeDir)
#----------------------------------------------------------------------------
# BaseDocumentMgr methods
#----------------------------------------------------------------------------
def fullPath(self, fileName):
fileName = super(BaseProject, self).fullPath(fileName)
if os.path.isabs(fileName):
absPath = fileName
elif self.homeDir:
absPath = os.path.join(self.homeDir, fileName)
else:
absPath = os.path.abspath(fileName)
return os.path.normpath(absPath)
def documentRefFactory(self, name, fileType, filePath):
return ProjectFile(filePath=self.fullPath(filePath), type=fileType, name=name, getDocCallback=self._getDocCallback)
def findAllRefs(self):
return self._files
def GetXFormsDirectory(self):
forms = self.findRefsByFileType(basedocmgr.FILE_TYPE_XFORM)
filePaths = map(lambda form: form.filePath, forms)
xformdir = os.path.commonprefix(filePaths)
if not xformdir:
xformdir = self.homeDir
return xformdir
def setRefs(self, files):
self._files = files
def findRefsByFileType(self, fileType):
fileList = []
for file in self._files:
if fileType == file.type:
fileList.append(file)
return fileList
def GenerateServiceRefPath(self, wsdlFilePath):
# HACK: temporary solution to getting wsdlag path from wsdl path.
import wx
from WsdlAgEditor import WsdlAgDocument
ext = WsdlAgDocument.WSDL_AG_EXT
for template in wx.GetApp().GetDocumentManager().GetTemplates():
if template.GetDocumentType() == WsdlAgDocument:
ext = template.GetDefaultExtension()
break;
wsdlAgFilePath = os.path.splitext(wsdlFilePath)[0] + ext
return wsdlAgFilePath
def SetDocCallback(self, getDocCallback):
self._getDocCallback = getDocCallback
for file in self._files:
file._getDocCallback = getDocCallback
if ACTIVEGRID_BASE_IDE:
class Project(BaseProject):
pass
else:
class Project(BaseProject, basedocmgr.BaseDocumentMgr):
pass
class ProjectFile(object):
__xmlname__ = "file"
__xmlexclude__ = ('_getDocCallback', '_docCallbackCacheReturnValue', '_docModelCallbackCacheReturnValue', '_doc',)
__xmlattributes__ = ["filePath", "logicalFolder", "type", "name"]
__xmldefaultnamespace__ = xmlutils.AG_NS_URL
def __init__(self, filePath=None, logicalFolder=None, type=None, name=None, getDocCallback=None):
self.filePath = filePath
self.logicalFolder = logicalFolder
self.type = type
self.name = name
self._getDocCallback = getDocCallback
self._docCallbackCacheReturnValue = None
self._docModelCallbackCacheReturnValue = None
self._doc = None
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.
return self._docModelCallbackCacheReturnValue
if self._getDocCallback:
self._docCallbackCacheReturnValue, self._docModelCallbackCacheReturnValue = self._getDocCallback(self.filePath)
return self._docModelCallbackCacheReturnValue
return None
document = property(_GetDocumentModel)
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 self._docCallbackCacheReturnValue
return None
ideDocument = property(_GetDocument)
def _typeEnumeration(self):
return basedocmgr.FILE_TYPE_LIST
def _GetPhysicalFolder(self):
dir = None
if self.filePath:
dir = os.path.dirname(self.filePath)
if os.sep != '/':
dir = dir.replace(os.sep, '/') # require '/' as delimiter
return dir
physicalFolder = property(_GetPhysicalFolder)
def GetRelativeFolder(self, parentPath):
parentPathLen = len(parentPath)
dir = None
if self.filePath:
dir = os.path.dirname(self.filePath)
if dir.startswith(parentPath):
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.
return dir
def AbsToRelativePath(self, parentPath):
""" Used to convert path to relative path for saving (disk format) """
parentPathLen = len(parentPath)
if self.filePath.startswith(parentPath):
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.
else:
pass # not a decendant of project, use absolute path
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))
#----------------------------------------------------------------------------
# BaseDocumentMgr methods
#----------------------------------------------------------------------------
def _GetDoc(self):
# HACK: temporary solution.
import wx
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
return self._doc
def _GetLocalServiceProcessName(self):
# HACK: temporary solution to getting process name from wsdlag file.
return self._GetDoc().GetModel().processName
processName = property(_GetLocalServiceProcessName)
def _GetStateful(self):
# HACK: temporary solution to getting stateful from wsdlag file.
return self._GetDoc().GetModel().stateful
def _SetStateful(self, stateful):
# HACK: temporary solution to setting stateful from wsdlag file.
self._GetDoc().GetModel().stateful = stateful
stateful = property(_GetStateful, _SetStateful)
def _GetLocalServiceCodeFile(self):
# HACK: temporary solution to getting class name from wsdlag file.
return self._GetDoc().GetModel().localServiceCodeFile
def _SetLocalServiceCodeFile(self, codefile):
# HACK: temporary solution to setting class name from wsdlag file.
self._GetDoc().GetModel().localServiceCodeFile = codefile
localServiceCodeFile = property(_GetLocalServiceCodeFile, _SetLocalServiceCodeFile)
def _GetLocalServiceClassName(self):
# HACK: temporary solution to getting class name from wsdlag file.
return self._GetDoc().GetModel().localServiceClassName
def _SetLocalServiceClassName(self, className):
# HACK: temporary solution to setting class name from wsdlag file.
self._GetDoc().GetModel().localServiceClassName = className
localServiceClassName = property(_GetLocalServiceClassName, _SetLocalServiceClassName)
# 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
##
##
## rssServiceBaseURL = property(_GetRssServiceBaseURL, _SetRssServiceBaseURL)
##
##
## def _GetRssServiceRssVersion(self):
## return self._GetDoc().GetModel().rssServiceRssVersion
##
##
## 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.
model = self._GetDoc().GetModel()
if hasattr(model, 'serviceType'):
return model.serviceType
else:
return None
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.
import activegrid.server.deployment as deploymentlib
appInfo = self._GetDoc().GetAppInfo()
if appInfo.language == None:
language = deploymentlib.LANGUAGE_DEFAULT
else:
language = appInfo.language
if language == deploymentlib.LANGUAGE_PYTHON:
suffix = ".py"
elif language == deploymentlib.LANGUAGE_PHP:
suffix = ".php"
pyFilename = self.name + suffix
return self._GetDoc().GetAppDocMgr().fullPath(pyFilename)
#----------------------------------------------------------------------------
# Old Classes
#----------------------------------------------------------------------------
class Project_10:
""" Version 1.0, kept for upgrading to latest version. Over time, this should be deprecated. """
__xmlname__ = "project"
__xmlrename__ = { "_files":"files"}
__xmlexclude__ = ('fileName',)
__xmlattributes__ = ["version"]
def __init__(self):
self.version = PROJECT_VERSION_050730
self._files = []
def initialize(self):
""" Required method for xmlmarshaller """
pass
def upgradeVersion(self):
currModel = Project()
for file in self._files:
currModel._files.append(ProjectFile(file))
return currModel

View File

@@ -1,48 +0,0 @@
#----------------------------------------------------------------------------
# Name: __init__.py
# Purpose: Utilities
#
# Author: Joel Hare
#
# Created: 7/28/04
# CVS-ID: $Id$
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
import traceback
import sys
import os
def isWindows():
return os.name == 'nt'
def _generateMainModuleDir():
if sys.executable.find('python') != -1:
utilModuleDir = os.path.dirname(__file__)
if not os.path.isabs(utilModuleDir):
utilModuleDir = os.path.join(os.getcwd(), utilModuleDir)
mainModuleDir = os.path.normpath(os.path.join(utilModuleDir, os.path.join(os.path.pardir, os.path.pardir)))
if mainModuleDir.endswith('.zip'):
mainModuleDir = os.path.dirname(mainModuleDir) # Get rid of library.zip
else:
mainModuleDir = os.path.dirname(sys.executable)
return mainModuleDir
mainModuleDir = _generateMainModuleDir()
def _generatePythonExecPath():
if sys.executable.find('python') != -1:
pythonExecPath = sys.executable
else:
pythonExecPath = os.path.join(os.path.dirname(sys.executable), '3rdparty\python2.3\python')
return pythonExecPath
pythonExecPath = _generatePythonExecPath()
def getCommandNameForExecPath(execPath):
if isWindows():
return '"%s"' % execPath
return execPath

View File

@@ -15,7 +15,10 @@ import os
import re
import traceback
import logging
import logging.config
from activegrid.util.lang import *
import activegrid.util.objutils as objutils
import activegrid.util.sysutils as sysutils
LEVEL_FATAL = logging.FATAL
LEVEL_ERROR = logging.ERROR
@@ -23,6 +26,90 @@ LEVEL_WARN = logging.WARN
LEVEL_INFO = logging.INFO
LEVEL_DEBUG = logging.DEBUG
EXCEPTION_INFO = 'exceptionInfo'
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)):
if (mode == LOG_MODE_IDE):
configFile = "IDELog"
elif (mode == LOG_MODE_TESTRUN):
configFile = "TestRunLog"
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
ag_debugLogger = logging.getLogger("activegrid.debug")
def log(logger, level, msg, *params):
if (logger == None):
logger = ag_debugLogger
apply(logger.log, (level, msg) + params)
def fatal(logger, msg, *params):
apply(logger.fatal, (msg,) + params)
def error(logger, msg, *params):
apply(logger.error, (msg,) + params)
def warn(logger, msg, *params):
apply(logger.warn, (msg,) + params)
def info(logger, msg, *params):
apply(logger.info, (msg,) + params)
def debug(logger, msg, *params):
if (logger == None):
logger = ag_debugLogger
apply(logger.debug, (msg,) + params)
def setLevelFatal(logger):
logger.setLevel(LEVEL_FATAL)
def setLevelError(logger):
logger.setLevel(LEVEL_ERROR)
def setLevelWarn(logger):
logger.setLevel(LEVEL_WARN)
def setLevelInfo(logger):
logger.setLevel(LEVEL_INFO)
def setLevelDebug(logger):
logger.setLevel(LEVEL_DEBUG)
def isEnabledForError(logger):
return logger.isEnabledFor(LEVEL_ERROR)
def isEnabledForWarn(logger):
return logger.isEnabledFor(LEVEL_WARN)
def isEnabledForInfo(logger):
return logger.isEnabledFor(LEVEL_INFO)
def isEnabledForDebug(logger):
return logger.isEnabledFor(LEVEL_DEBUG)
TEST_MODE_NONE = 0
TEST_MODE_DETERMINISTIC = 1
TEST_MODE_NON_DETERMINISTIC = 2
@@ -38,8 +125,11 @@ def getTestMode():
global agTestMode
return agTestMode
def testMode(normalObj, testObj=None):
if getTestMode() > TEST_MODE_NONE:
def testMode(normalObj, testObj=None, nonDeterministicObj=None):
testMode = getTestMode()
if testMode > TEST_MODE_NONE:
if ((nonDeterministicObj != None) and (testMode == TEST_MODE_NON_DETERMINISTIC)):
return nonDeterministicObj
return testObj
return normalObj
@@ -68,18 +158,41 @@ def _fileNameReplacement(match):
def _fileNameReplacementPHP(match):
return "%s...%s" % (match.group(1), match.group(2).replace(os.sep, "/"))
def getTraceback():
extype, val, tb = sys.exc_info()
tbs = "\n"
for s in traceback.format_tb(tb):
tbs += s
def formatTraceback(tb=None):
if (tb == None):
extype, val, tb = sys.exc_info()
tbs = "\n" + "".join(traceback.format_tb(tb))
return tbs
def formatExceptionCause(cause, stacktrace=False):
if (cause == None):
return ""
tbs = ""
if (stacktrace):
tbs = formatTraceback()
return "Caused by %s.%s: %s%s" % (cause.__module__, cause.__class__.__name__, str(cause), tbs)
def addExceptionInfo(e, key, value):
if not hasattr(e, EXCEPTION_INFO):
try:
setattr(e, EXCEPTION_INFO, {})
except:
return # Make sure we still report the real exception even if we can't add the extra info
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):
if (True): # exception == None):
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 = type(exception)
extype = objutils.typeToString(exception)
val = exception
if (stacktrace):
e,v,t = sys.exc_info()
@@ -87,17 +200,169 @@ def reportException(out=None, stacktrace=False, diffable=False, exception=None):
exstr = removeFileRefs(str(val))
else:
exstr = str(val)
if (out == None):
print "Got Exception = %s: %s" % (extype, exstr)
else:
print >> out, "Got Exception = %s: %s" % (extype, exstr)
if hasattr(val, EXCEPTION_INFO):
firstTime = True
for infoKey, infoValue in getattr(val, EXCEPTION_INFO).items():
if firstTime:
prefix = " EXTRA INFO:"
firstTime = False
else:
prefix = ","
exstr += ("%s %s=%s" % (prefix, infoKey, infoValue))
result = "Got Exception = %s: %s" % (extype, exstr)
if (stacktrace):
fmt = traceback.format_exception(extype, val, t)
for s in fmt:
if (diffable):
s = removeFileRefs(s)
if (out == None):
print s
result = result + "\n" + s
return result
def fileConfig(fname, defaults=None):
"""
This is copied from logging.config so that we could fix the class lookup of
handlers. Previously handlers had to be defined in logging.handlers and we
need to be able to define our own.
"""
import ConfigParser, string
cp = ConfigParser.ConfigParser(defaults)
if hasattr(cp, 'readfp') and hasattr(fname, 'readline'):
cp.readfp(fname)
else:
cp.read(fname)
#first, do the formatters...
flist = cp.get("formatters", "keys")
if len(flist):
flist = string.split(flist, ",")
formatters = {}
for form in flist:
sectname = "formatter_%s" % form
opts = cp.options(sectname)
if "format" in opts:
fs = cp.get(sectname, "format", 1)
else:
print >> out, s
fs = None
if "datefmt" in opts:
dfs = cp.get(sectname, "datefmt", 1)
else:
dfs = None
f = logging.Formatter(fs, dfs)
formatters[form] = f
#next, do the handlers...
#critical section...
logging._acquireLock()
try:
## try:
#first, lose the existing handlers...
logging._handlers.clear()
#now set up the new ones...
hlist = cp.get("handlers", "keys")
if len(hlist):
hlist = string.split(hlist, ",")
handlers = {}
fixups = [] #for inter-handler references
for hand in hlist:
## try:
sectname = "handler_%s" % hand
classname = cp.get(sectname, "class")
opts = cp.options(sectname)
if "formatter" in opts:
fmt = cp.get(sectname, "formatter")
else:
fmt = ""
klass = None
try:
klass = eval(classname, vars(logging))
except:
pass
if (klass == None):
klass = objutils.classForName(classname)
args = cp.get(sectname, "args")
args = eval(args, vars(logging))
h = apply(klass, args)
if "level" in opts:
level = cp.get(sectname, "level")
h.setLevel(logging._levelNames[level])
if len(fmt):
h.setFormatter(formatters[fmt])
#temporary hack for FileHandler and MemoryHandler.
if klass == logging.handlers.MemoryHandler:
if "target" in opts:
target = cp.get(sectname,"target")
else:
target = ""
if len(target): #the target handler may not be loaded yet, so keep for later...
fixups.append((h, target))
handlers[hand] = h
## except Exception, e: #if an error occurs when instantiating a handler, too bad
## pass #this could happen e.g. because of lack of privileges
#now all handlers are loaded, fixup inter-handler references...
for fixup in fixups:
h = fixup[0]
t = fixup[1]
h.setTarget(handlers[t])
#at last, the loggers...first the root...
llist = cp.get("loggers", "keys")
llist = string.split(llist, ",")
llist.remove("root")
sectname = "logger_root"
root = logging.root
log = root
opts = cp.options(sectname)
if "level" in opts:
level = cp.get(sectname, "level")
log.setLevel(logging._levelNames[level])
for h in root.handlers[:]:
root.removeHandler(h)
hlist = cp.get(sectname, "handlers")
if len(hlist):
hlist = string.split(hlist, ",")
for hand in hlist:
log.addHandler(handlers[hand])
#and now the others...
#we don't want to lose the existing loggers,
#since other threads may have pointers to them.
#existing is set to contain all existing loggers,
#and as we go through the new configuration we
#remove any which are configured. At the end,
#what's left in existing is the set of loggers
#which were in the previous configuration but
#which are not in the new configuration.
existing = root.manager.loggerDict.keys()
#now set up the new ones...
for log in llist:
sectname = "logger_%s" % log
qn = cp.get(sectname, "qualname")
opts = cp.options(sectname)
if "propagate" in opts:
propagate = cp.getint(sectname, "propagate")
else:
propagate = 1
logger = logging.getLogger(qn)
if qn in existing:
existing.remove(qn)
if "level" in opts:
level = cp.get(sectname, "level")
logger.setLevel(logging._levelNames[level])
for h in logger.handlers[:]:
logger.removeHandler(h)
logger.propagate = propagate
logger.disabled = 0
hlist = cp.get(sectname, "handlers")
if len(hlist):
hlist = string.split(hlist, ",")
for hand in hlist:
logger.addHandler(handlers[hand])
#Disable any old loggers. There's no point deleting
#them as other threads may continue to hold references
#and by disabling them, you stop them doing any logging.
for log in existing:
root.manager.loggerDict[log].disabled = 1
## except:
## import traceback
## ei = sys.exc_info()
## traceback.print_exception(ei[0], ei[1], ei[2], None, sys.stderr)
## del ei
finally:
logging._releaseLock()

View File

@@ -0,0 +1,59 @@
#----------------------------------------------------------------------------
# Name: appdirs.py
# Purpose: Utilities for retrieving special application dirs
#
# Author: Kevin Ollivier
#
# Created: 8/27/05
# CVS-ID: $Id$
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
# 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.
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
documents_folder = _generateDocumentsDir()
# 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.
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")
return ""

View File

@@ -0,0 +1,348 @@
#----------------------------------------------------------------------------
# Name: fileutils.py
# Purpose: Active grid miscellaneous utilities
#
# Author: Jeff Norton
#
# Created: 12/10/04
# CVS-ID: $Id$
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
import logging
import copy
import os
import shutil
import sys
import zipfile
import activegrid.util.aglogging as aglogging
import activegrid.util.sysutils as sysutils
from activegrid.util.lang import *
global fileutilsLogger
fileutilsLogger = logging.getLogger("activegrid.util.fileutils")
# FATAL : No logging
# ERROR : No logging
# WARN : No logging
# INFO : No logging
# DEBUG : debugging
aglogging.setLevelFatal(fileutilsLogger)
#logging.getLogger().addHandler(logging.StreamHandler(sys.stderr))
def makeDirsForFile(filename):
d = os.path.dirname(filename)
if (not os.path.exists(d)):
os.makedirs(d)
def createFile(filename, mode='w'):
f = None
if (not os.path.exists(filename)):
makeDirsForFile(filename)
f = file(filename, mode)
return f
def compareFiles(file1, file2):
## result = filecmp.cmp(file1, file2)
## if result:
## return 0
## return -1
file1.seek(0)
file2.seek(0)
while True:
line1 = file1.readline()
line2 = file2.readline()
if (len(line1) == 0):
if (len(line2) == 0):
return 0
else:
return -1
elif (len(line2) == 0):
return -1
elif (line1 != line2):
line1 = line1.replace(" ", "")
line2 = line2.replace(" ", "")
if (line1 != line2):
len1 = len(line1)
len2 = len(line2)
if ((abs(len1 - len2) == 1) and (len1 > 0) and (len2 > 0)
and (line1[-1] == "\n") and (line2[-1] == "\n")):
if (len1 > len2):
longer = line1
shorter = line2
else:
shorter = line1
longer = line2
if ((longer[-2] == "\r") and (longer[:-2] == shorter[:-1])):
continue
if ((longer[-2:] == shorter[-2:]) and (longer[-3] == "\r") and (longer[:-3] == shorter[:-2])):
continue
return -1
def expandVars(value):
"""Syntax: ${myvar,default="default value"}"""
import activegrid.runtime as runtime
sx = value.find("${")
if (sx >= 0):
result = asString(value[:sx])
endx = value.find("}")
if (endx > 1):
defaultValue = None
defsx = value.find(",default=\"")
if ((defsx > sx) and (defsx < endx)):
varname = value[sx+2:defsx]
if (value[endx-1] == '"'):
defaultValue = value[defsx+10:endx-1]
if (defaultValue == None):
varname = value[sx+2:endx]
if (varname == "AG_SYSTEM"):
varval = runtime.appInfo.getSystemDir()
elif (varname == "AG_SYSTEM_STATIC"):
varval = runtime.appInfo.getSystemStaticDir()
elif (varname == "AG_APP"):
varval = runtime.appInfo.getAppDir()
elif (varname == "AG_APP_STATIC"):
varval = runtime.appInfo.getAppStaticDir()
else:
varval = os.getenv(varname)
if ((varval == None) and (defaultValue != None)):
varval = defaultValue
if (varval == None):
result += value[sx:endx+1]
else:
result += varval
return result + expandVars(value[endx+1:])
return value
def toPHPpath(path, otherdir=None):
return convertSourcePath(path, "php", otherdir=otherdir)
def toPythonpath(path, otherdir=None):
return convertSourcePath(path, "python", otherdir=otherdir)
def toUnixPath(path):
if (path != None and os.sep != '/'):
path = path.replace(os.sep, '/')
return path
def convertSourcePath(path, to, otherdir=None):
fromname = "python"
if (to == "python"):
fromname = "php"
pythonNode = os.sep + fromname + os.sep
ix = path.find(pythonNode)
if (ix < 0):
ix = path.find(fromname) - 1
if ((ix < 0) or (len(path) <= ix+7)
or (path[ix] not in ("\\", "/")) or (path[ix+7] not in ("\\", "/"))):
raise Exception("Not in a %s source tree. Cannot create file name for %s." % (fromname, path))
if (otherdir == None):
return path[:ix+1] + to + path[ix+7:]
else:
return otherdir + path[ix+7:]
if (otherdir == None):
return path.replace(pythonNode, os.sep + to + os.sep)
else:
return otherdir + path[ix+7:]
def visit(directory, files, extension):
testdirs = os.listdir(directory)
for thing in testdirs:
fullpath = os.path.join(directory, thing)
if (os.path.isdir(fullpath)):
visit(fullpath, files, extension)
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
retval = []
for directory in path:
visit(directory, retval, extension)
return retval
def getFileLastModificationTime(fileName):
return os.path.getmtime(fileName)
def findFileLocation(location, fileName):
i = fileName.rfind(os.sep)
if i > 0:
fileName = fileName[:i]
while location[0:2] == '..' and location[2:3] == os.sep:
location = location[3:]
i = fileName.rfind(os.sep)
fileName = fileName[:i]
absPath = fileName + os.sep + location
return absPath
def getAllExistingFiles(files, basepath=None, forceForwardSlashes=False):
"""For each file in files, if it exists, adds its absolute path to the rtn list. If file is a dir, calls this function recursively on all child files in the dir.
If basepath is set, and if the file being processed is relative to basedir, adds that relative path to rtn list instead of the abs path.
Is this is Windows, and forceForwardSlashes is True, make sure returned paths only have forward slashes."""
if isinstance(files, basestring):
files = [files]
rtn = []
for file in files:
if os.path.exists(file):
if os.path.isfile(file):
if basepath and hasAncestorDir(file, basepath):
rtn.append(getRelativePath(file, basepath))
else:
rtn.append(os.path.abspath(str(file)))
elif os.path.isdir(file):
dircontent = [os.path.join(file, f) for f in os.listdir(file)]
rtn.extend(getAllExistingFiles(dircontent, basepath))
if forceForwardSlashes and sysutils.isWindows():
newRtn = []
for f in rtn:
newRtn.append(f.replace("\\", "/"))
rtn = newRtn
return rtn
def hasAncestorDir(file, parent):
"""Returns true if file has the dir 'parent' as some parent in its path."""
return getRelativePath(file, parent) != None
def getRelativePath(file, basedir):
"""Returns relative path from 'basedir' to 'file', assuming 'file' lives beneath 'basedir'. If it doesn't, returns None."""
file = os.path.abspath(file)
parent = os.path.abspath(basedir)
if file == parent:
return None
if file.startswith(parent):
return file[len(parent)+1:]
return None
def isEmptyDir(dir):
if not os.path.isdir(dir):
return False
return len(os.listdir(dir)) == 0
ifDefPy()
def zip(zipfilepath, basedir=None, files=None):
"""Zip all files in files and save zip as zipfilepath. If files is None, zip all files in basedir. For all files to be zipped, if they are relative to basedir, include the relative path in the archive."""
if not files and not basedir:
raise AssertionError("Either 'basedir' or 'files' must be set")
if not files:
aglogging.debug(fileutilsLogger,\
"Looking for files to zip in %s" % basedir)
files = getAllExistingFiles(basedir)
else:
# removes files that don't exist and gets abs for each
files = getAllExistingFiles(files)
if len(files) == 0:
aglogging.debug(fileutilsLogger, "No files to zip, nothing to do")
return
z = zipfile.ZipFile(zipfilepath, mode="w", compression=zipfile.ZIP_DEFLATED)
try:
for file in files:
arcname = None
if basedir:
arcname = getRelativePath(file, basedir)
if not arcname:
arcname = file
aglogging.debug(fileutilsLogger,\
"%s: adding %s with arcname %s" %\
(zipfilepath, file, arcname))
z.write(file, arcname)
finally:
z.close()
endIfDef()
ifDefPy()
def unzip(zipfilepath, extractdir):
"""Unzip zipfilepath into extractdir."""
z = zipfile.ZipFile(zipfilepath, mode="r")
for info in z.infolist():
filename = os.path.join(extractdir, info.filename)
try:
dir = os.path.dirname(filename)
aglogging.debug(fileutilsLogger, "Creating dir %s" % dir)
os.makedirs(dir) # do we have to worry about permissions?
except:
pass
if os.path.isdir(filename):
continue
aglogging.debug(fileutilsLogger,\
("Writing arcfile %s to %s" % (info.filename, filename)))
f = open(filename, "w")
f.write(z.read(info.filename))
f.close()
endIfDef()
ifDefPy()
def copyFile(src, dest):
"""Copies file src to dest. Creates directories in 'dest' path if necessary."""
destdir = os.path.dirname(dest)
if not os.path.exists(destdir):
os.makedirs(destdir)
shutil.copy(src, dest)
endIfDef()
ifDefPy()
def copyDir(src, dest):
"""Copies dir 'src' into dir 'dest'. Creates 'dest' if it does not exist."""
shutil.copytree(src, dest)
endIfDef()
ifDefPy()
def remove(file):
if not os.path.exists(file):
return
if os.path.isfile(file):
os.remove(file)
elif os.path.isdir(file):
shutil.rmtree(file)
endIfDef()
ifDefPy()
import warnings
warnings.filterwarnings("ignore", message="tmpnam is a potential security risk to your program")
def getTmpFile():
return os.tmpnam()
endIfDef()
ifDefPy()
#@accepts str, dict, str, str, boolean
def replaceToken(infilepath, tokens={}, outfilepath=None, delim="@@",\
useEnv=False):
"""Replaces tokens of form 'delim'<tokenname>'delim' in file at 'infilepath', using values in dict 'tokens'. If 'outfilepath' is set, writes output to 'outfilepath', if not set, overwrites original file. If 'useEnv' is True, adds os.environ to 'tokens'. This makes it possible to define an env var FOO=BLAH, and have @@FOO@@ be replaced with BLAH, without explicitly passing FOO=BLAH in 'tokens'. Note that entries in 'tokens' take precedence over entries in os.environ."""
if useEnv:
for key, val in os.environ.items():
# passed in tokens take precedence
if not tokens.has_key(key):
tokens[key] = val
f = open(infilepath, "r")
try:
content = f.read()
finally:
if f: f.close()
for token, value in tokens.items():
content = content.replace("%s%s%s" % (delim, token , delim), str(value))
if not outfilepath: outfilepath = infilepath
f = open(outfilepath, "w")
try:
f.write(content)
finally:
if f: f.close()
endIfDef()

View File

@@ -65,3 +65,9 @@ def ifDefPy(comment=False):
def endIfDef():
pass
def ag_isPHP():
return False
def ag_isPython():
return True

View File

@@ -14,36 +14,192 @@ import logging
import traceback
import sys
import os
import __builtin__
import types
import xml.sax.saxutils as saxutils
from types import *
from activegrid.util.lang import *
FUNCTION_HAS_ATTR = '_hasAttr'
FUNCTION_GET_ATTR = '_getAttr'
FUNCTION_SET_ATTR = '_setAttr'
FUNCTION_DEL_ATTR = '_delAttr'
def hasRawAttr(obj, name):
if obj == None:
return False
if name != FUNCTION_HAS_ATTR and hasattr(obj, FUNCTION_HAS_ATTR):
return obj._hasAttr(name)
return obj.__dict__.has_key(name)
def getRawAttr(obj, name):
if name != FUNCTION_GET_ATTR and hasattr(obj, FUNCTION_GET_ATTR):
return obj._getAttr(name)
return obj.__dict__.get(name)
def setRawAttr(obj, name, value):
if name != FUNCTION_SET_ATTR and hasattr(obj, FUNCTION_SET_ATTR):
obj._setAttr(name, value)
else:
obj.__dict__[name] = value
def delRawAttr(obj, name):
if name != FUNCTION_DEL_ATTR and hasattr(obj, FUNCTION_DEL_ATTR):
obj._delAttr(name)
else:
del obj.__dict__[name]
def getStaticAttr(obj, attr):
if (isinstance(obj, types.TypeType)):
classDesc = obj
else:
classDesc = obj.__class__
if (hasattr(classDesc, attr)):
return getattr(classDesc, attr)
return None
def setStaticAttr(obj, attr, value):
if (isinstance(obj, types.TypeType)):
classDesc = obj
else:
classDesc = obj.__class__
setattr(classDesc, attr, value)
def moduleForName(moduleName):
module = None
pathList = moduleName.split('.')
if (len(moduleName) > 0):
module = __import__(moduleName)
for name in pathList[1:]:
if (name in module.__dict__):
module = module.__dict__[name]
else:
module = None
break
return module
def typeForName(typeName):
i = typeName.rfind('.')
if (i >= 0):
module = moduleForName(typeName[:i])
if (module != None):
name = typeName[i+1:]
if (name in module.__dict__):
return module.__dict__[name]
elif __builtin__.__dict__.has_key(typeName):
return __builtin__.__dict__[typeName]
return None
def functionForName(functionName):
ftype = typeForName(functionName)
if (isinstance(ftype, (types.FunctionType, types.MethodType, types.BuiltinFunctionType, types.BuiltinMethodType))):
return ftype
return None
def classForName(className):
pathList = className.split('.')
moduleName = '.'.join(pathList[:-1])
code = __import__(moduleName)
for name in pathList[1:]:
code = code.__dict__[name]
return code
ctype = typeForName(className)
if (isinstance(ctype, (types.ClassType, types.TypeType))):
return ctype
return None
def hasPropertyValue(obj, attr):
hasProp = False
try:
prop = obj.__class__.__dict__[attr]
if (isinstance(prop, property)):
hasProp = hasattr(obj, attr)
if (hasProp):
# It's a property and it has a value but sometimes we don't want it.
# If there is a _hasattr method execute it and the
# result will tell us whether to include this value
try:
hasProp = obj._hasattr(attr)
except:
pass
except KeyError:
pass
return hasProp
def newInstance(className, objargs=None):
"dynamically create an object based on the className and return it."
def toDiffableString(value):
s = str(value)
if not isinstance(objargs, list):
objargs = [objargs]
if className == "None":
return None
elif className == "bool":
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 len(objargs) > 0:
try:
return saxutils.unescape(objargs[0]).encode()
except:
return "?"
else:
return ""
classtype = classForName(className)
if (classtype == None):
raise Exception("Could not find class %s" % className)
if (len(objargs) > 0):
return classtype(*objargs)
else:
return classtype()
def getClassProperty(classType, propertyName):
return getattr(classType, propertyName)
def toDiffableRepr(value, exclude=None):
if (value == None):
return "None"
## 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,
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__")):
s = str(value)
elif (hasattr(value, "__dict__")):
s = "%s(%s)" % (type(value), toDiffableString(value.__dict__, exclude))
else:
s = str(type(value))
ix2 = s.find(" object at 0x")
if (ix2 > 0):
ix = s.rfind(".")
if (ix > 0):
s = "<class %s>" %s[ix+1:ix2]
elif (isinstance(value, bool)):
if (value):
return "True"
else:
return "False"
elif (isinstance(value, (tuple, list))):
items = []
for v in value:
if (isinstance(v, basestring)):
if (v.find("'") >= 0):
items.append('"%s"' % v)
else:
items.append("'%s'" % v)
else:
items.append(toDiffableString(v, exclude))
s = "[" + ", ".join(items) + "]"
elif (isinstance(value, dict)):
if (exclude == None):
exclude = []
items = []
for key, val in value.iteritems():
if (isinstance(val, UnicodeType)):
items.append("'%s': u'%s'" % (key, toDiffableString(val, exclude)))
elif (isinstance(val, basestring)):
items.append("'%s': '%s'" % (key, toDiffableString(val, exclude)))
else:
items.append("'%s': %s" % (key, toDiffableString(val, exclude)))
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)
ds = ""
i = s.find(" at 0x")
start = 0
@@ -54,22 +210,43 @@ def toDiffableString(value):
ds += s[start:i]
start = j
i = s.find(" at 0x", start)
return ds + s[start:]
ds = ds + s[start:]
i = ds.find("\\src\\")
if (i < 0):
i = ds.find("/src/")
else:
ds = ds.replace("\\", "/")
if (i > 0):
i += 4
if (ds[i:i+5] == "\\php\\"):
i += 4
elif (ds[i:i+8] == "\\python\\"):
i += 7
ds = "filepath: ..." + ds[i:]
return ds
def toString(value, options=0):
if ((options & PRINT_OBJ_DIFFABLE) > 0):
return toDiffableString(value)
elif (not isinstance(value, basestring)):
return str(value)
return value
def toTypeString(obj):
def typeToString(obj, options=0):
if (isinstance(obj, BooleanType)):
return "bool"
elif (isinstance(obj, UnicodeType)):
if ((options & PRINT_OBJ_DIFFABLE) > 0):
return "string"
return "unicode"
elif (isinstance(obj, basestring)):
return "string"
elif (isinstance(obj, IntType)):
return "int"
elif (isinstance(obj, LongType)):
if ((options & PRINT_OBJ_DIFFABLE) > 0):
return "int"
return "long"
elif (isinstance(obj, FloatType)):
return "float"
elif (type(obj) == ListType):
@@ -79,18 +256,46 @@ def toTypeString(obj):
elif (isinstance(obj, TupleType)):
return "tuple"
elif (isinstance(obj, InstanceType)):
return type(obj)
## ds = str(type(obj))
ds = "<class %s.%s> " % (obj.__module__, obj.__class__.__name__)
else:
return type(obj)
ds = str(type(obj))
if (options == 0):
import activegrid.util.aglogging
options = activegrid.util.aglogging.testMode(0, PRINT_OBJ_DIFFABLE)
if ((options & PRINT_OBJ_DIFFABLE) > 0):
if (ds.startswith("<class ")):
ix = ds.rfind(".")
if (ix < 0):
ix = 8
ds = "<class %s>" % ds[ix+1:-2]
return ds
def nameToString(name, options=0):
if (name.startswith("_v_")):
return name[3:]
if ((options & PRINT_OBJ_DIFFABLE) > 0):
ix = name.find("__")
if ((ix > 1) and name.startswith("_")):
name = name[ix:]
return toDiffableString(name)
return name
PRINT_OBJ_GETATTR = 1
PRINT_OBJ_HIDE_INTERNAL = 2
PRINT_OBJ_COMPACT = 4
PRINT_OBJ_NONONE = 8
PRINT_OBJ_DIFFABLE = 16
PRINT_OBJ_HIDE_EXCLUDED = 32
PRINT_OBJ_INTERNAL = 512
def printObject(out, object, name="", indent=0, flags=0, exclude=None, maxIndent=30):
def printObject(out, object, name=None, indent=0, flags=0, exclude=None, remove=None, maxIndent=30):
if (name == None):
name = ""
## elif (name.endswith("_") and not name.endswith("__")):
## name = name[:-1]
if ((remove != None) and (name in asDict(remove))):
return False
if ((maxIndent != None) and (indent > maxIndent)):
print >> out, " "*indent, "%s: %s" % (name, toString(str(object), flags)),
if ((flags & PRINT_OBJ_INTERNAL) == 0):
@@ -98,12 +303,11 @@ def printObject(out, object, name="", indent=0, flags=0, exclude=None, maxIndent
return True
finalNewLine = False
printed = True
## if (exclude == None):
## exclude = []
if ((flags & PRINT_OBJ_COMPACT) > 0):
if (exclude and object in exclude):
if ((flags & (PRINT_OBJ_COMPACT | PRINT_OBJ_HIDE_EXCLUDED)) > 0):
if ((exclude != None) and ((object in exclude) or (name in exclude))):
return
indent = 0
if ((flags & PRINT_OBJ_COMPACT) > 0):
indent = 0
if ((flags & PRINT_OBJ_INTERNAL) == 0):
finalNewLine = True
flags |= PRINT_OBJ_INTERNAL
@@ -113,23 +317,23 @@ def printObject(out, object, name="", indent=0, flags=0, exclude=None, maxIndent
else:
finalNewLine = False
printed = False
elif (name.startswith("_") and ((flags & PRINT_OBJ_HIDE_INTERNAL) > 0)):
elif (name.startswith("_") and ((flags & PRINT_OBJ_HIDE_INTERNAL) > 0) and not name.startswith("_v_")):
finalNewLine = False
printed = False
elif (isinstance(object, (list, tuple))):
if ((exclude != None) and object in exclude):
print >> out, " "*indent, name, " : ", toTypeString(object), " of length = ", len(object), " (already printed)",
print >> out, " "*indent, name, " : ", typeToString(object, flags), " of length = ", len(object), " (already printed)",
elif ((exclude != None) and name in exclude):
print >> out, " "*indent, name, " : ", toTypeString(object), " of length = ", len(object), " (excluded)",
print >> out, " "*indent, name, " : ", typeToString(object, flags), " of length = ", len(object), " (excluded)",
else:
if ((exclude != None) and (len(object) > 0)): exclude.append(object)
print >> out, " "*indent, name, " : ", toTypeString(object), " of length = %d" % len(object),
print >> out, " "*indent, name, " : ", typeToString(object, flags), " of length = %d" % len(object),
for i, o in enumerate(object):
print >> out
printObject(out, o, name="[%d]" % i, indent=indent+2, flags=flags, exclude=exclude, maxIndent=maxIndent)
printObject(out, o, name="[%d]" % i, indent=indent+2, flags=flags, exclude=exclude, remove=remove, maxIndent=maxIndent)
elif (isinstance(object, dict)):
if ((exclude != None) and object in exclude):
print >> out, " "*indent, name, " : ", toTypeString(object), " (already printed)",
print >> out, " "*indent, name, " : ", typeToString(object, flags), " (already printed)",
else:
if ((exclude != None) and (len(object) > 0)): exclude.append(object)
if (len(name) > 0):
@@ -147,51 +351,62 @@ def printObject(out, object, name="", indent=0, flags=0, exclude=None, maxIndent
n = key
if (not (isinstance(n, basestring))):
n = str(n)
else:
n = nameToString(n, flags)
if ((not n.startswith("_") or ((flags & PRINT_OBJ_HIDE_INTERNAL) == 0))):
if printObject(out, object[key], name=n, indent=indent+2, flags=(flags | PRINT_OBJ_INTERNAL), exclude=exclude, maxIndent=maxIndent):
if printObject(out, object[key], name=n, indent=indent+2, flags=(flags | PRINT_OBJ_INTERNAL), exclude=exclude, remove=remove, maxIndent=maxIndent):
if ((flags & PRINT_OBJ_COMPACT) == 0):
print >> out
else:
print >> out, ",",
print >> out, " "*indent, "}",
elif (hasattr(object, "__dict__")):
if ((exclude != None) and object in exclude):
print >> out, " "*indent, name, " : ", toTypeString(object), " (already printed) = ", toDiffableString(object),
if (name.startswith("_")): ## and ((flags & PRINT_OBJ_HIDE_INTERNAL) > 0)):
print >> out, " "*indent, name, " : ", typeToString(object, flags),
elif ((exclude != None) and ((object in exclude) or (object.__dict__ in exclude))):
print >> out, " "*indent, name, " : ", typeToString(object, flags), " (already printed)",
else:
if (exclude != None): exclude.append(object)
if (name.startswith("_")): ## and ((flags & PRINT_OBJ_HIDE_INTERNAL) > 0)):
print >> out, " "*indent, name, " : ", toTypeString(object),
elif ((exclude != None) and object.__dict__ in exclude):
print >> out, " "*indent, name, " : ", toTypeString(object), " (already printed)",
print >> out, " "*indent, name, " : ", typeToString(object, flags),
if ((flags & PRINT_OBJ_GETATTR) == 0):
if ((flags & PRINT_OBJ_COMPACT) == 0):
print >> out
printObject(out, object.__dict__, indent=indent, flags=flags, exclude=exclude, remove=remove, maxIndent=maxIndent)
else:
print >> out, " "*indent, name, " : ", toTypeString(object),
if ((flags & PRINT_OBJ_GETATTR) == 0):
if ((flags & PRINT_OBJ_COMPACT) == 0):
if ((flags & PRINT_OBJ_COMPACT) == 0):
print >> out
## indent += 2
print >> out, " "*indent, "{",
keys = object.__dict__.keys()
keys.sort()
printed = True
for key in keys:
if ((exclude != None) and (key in exclude)):
continue
if (printed and ((flags & PRINT_OBJ_COMPACT) == 0)):
print >> out
printObject(out, object.__dict__, indent=indent, flags=flags, exclude=exclude, maxIndent=maxIndent)
else:
keys = object.__dict__.keys()
keys.sort()
for n in keys:
if ((flags & PRINT_OBJ_COMPACT) == 0):
print >> out
printObject(out, getattr(object, n), name=n, indent=indent+2, flags=flags, exclude=exclude, maxIndent=maxIndent)
n = nameToString(key, flags)
printed = printObject(out, getattr(object, n), name=n, indent=indent+2, flags=flags, exclude=exclude, remove=remove, maxIndent=maxIndent)
if ((flags & PRINT_OBJ_COMPACT) == 0):
print >> out
print >> out, " "*indent, "}",
elif (indent < 0):
print >> out, object,
elif isinstance(object, basestring):
if ((exclude != None) and name in exclude):
print >> out, " "*indent, name, " : ", toTypeString(object), " of length = ", len(object), " (excluded)",
print >> out, " "*indent, name, " : ", typeToString(object, flags), " of length = ", len(object), " (excluded)",
elif (len(object) > 100):
print >> out, " "*indent, name, ":", toTypeString(object), "[%d] = %s...%s" % (len(object), object[:50], object[-50:]),
object = toString(object, flags)
print >> out, " "*indent, name, ":", typeToString(object, flags), "[%d] = %s...%s" % (len(object), object[:50], object[-50:]),
else:
print >> out, " "*indent, name, ":", toTypeString(object), "=", str(object),
print >> out, " "*indent, name, ":", typeToString(object, flags), "=", toString(object, flags),
## elif (isinstance(object, float)):
## val = str(object)
## if (len(val) > 17):
## val = val[:17]
## print >> out, " "*indent, name, ":", type(object), "=", val,
else:
print >> out, " "*indent, name, ":", toTypeString(object), "=", str(object),
print >> out, " "*indent, name, ":", typeToString(object, flags), "=", toString(object, flags),
if (finalNewLine):
print >> out
return printed

View File

@@ -0,0 +1,23 @@
#----------------------------------------------------------------------------
# Name: strutils.py
# Purpose: String Utilities
#
# Author: Morgan Hua
#
# Created: 11/3/05
# CVS-ID: $Id$
# Copyright: (c) 2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
def caseInsensitiveCompare(s1, s2):
""" Method used by sort() to sort values in case insensitive order """
s1L = s1.lower()
s2L = s2.lower()
if s1L == s2L:
return 0
elif s1L < s2L:
return -1
else:
return 1

View File

@@ -0,0 +1,87 @@
#----------------------------------------------------------------------------
# Name: sysutils.py
# Purpose: System Utilities
#
# Author: Joel Hare
#
# Created: 7/28/04
# CVS-ID: $Id$
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
import sys
import os
# this will be set to true in IDE.py when we are running release builds.
isRelease = False
# Commented out for now.....
# Required for Unicode support with python
# Put over here because of py2exe problems
# Python suggests modifying site.py
#if hasattr(sys,"setdefaultencoding"):
# sys.setdefaultencoding("UTF-8")
MAINMODULE_DIR = "AG_MAINMODULE_DIR"
IS_RELEASE = "AG_IS_RELEASE"
def isRelease():
return 'true' == (str(os.getenv(IS_RELEASE)).lower())
def setRelease(value):
if value:
os.environ[IS_RELEASE]= "TRUE"
else:
os.environ[IS_RELEASE]= "FALSE"
def isWindows():
return os.name == 'nt'
def _generateMainModuleDir():
mainModuleDir = os.getenv(MAINMODULE_DIR)
if mainModuleDir: # if environment variable set, return it
return mainModuleDir
# On Mac, the python executable sometimes has a capital "P" so we need to
# lower the string first
sysExecLower = sys.executable.lower()
if sysExecLower == "/" or sysExecLower.find('python') != -1 or sysExecLower.find('apache') != -1:
utilModuleDir = os.path.dirname(__file__)
if not os.path.isabs(utilModuleDir):
utilModuleDir = os.path.join(os.getcwd(), utilModuleDir)
mainModuleDir = os.path.normpath(os.path.join(utilModuleDir, os.path.join(os.path.pardir, os.path.pardir)))
if mainModuleDir.endswith('.zip'):
mainModuleDir = os.path.dirname(mainModuleDir) # Get rid of library.zip
else:
mainModuleDir = os.path.dirname(sys.executable)
os.environ[MAINMODULE_DIR] = mainModuleDir # pythonBug: os.putenv doesn't work, set environment variable
return mainModuleDir
mainModuleDir = _generateMainModuleDir()
def _generatePythonExecPath():
# On Mac, the python executable sometimes has a capital "P" so we need to
# lower the string first
sysExecLower = sys.executable.lower()
if sysExecLower.find('python') != -1 or sysExecLower.find('apache') != -1:
pythonExecPath = sys.executable
else:
# this is where py2app puts the Python executable
if sys.platform == "darwin":
pythonExecPath = os.path.join(os.path.dirname(sys.executable), "../Frameworks/Python.Framework/Versions/2.4/Python/bin")
else:
pythonExecPath = os.path.join(os.path.dirname(sys.executable), '3rdparty\python2.4\python')
return pythonExecPath
pythonExecPath = _generatePythonExecPath()
def getCommandNameForExecPath(execPath):
if isWindows():
return '"%s"' % execPath
return execPath

File diff suppressed because it is too large Load Diff

View File

@@ -9,8 +9,10 @@
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
# License: wxWindows License
#----------------------------------------------------------------------------
import xml.sax
from activegrid.util.lang import *
ifDefPy()
import xml.sax
endIfDef()
class XMLPrettyPrinter(xml.sax.ContentHandler):
def __init__(self, indentationChar=' ', newlineChar='\n'):

View File

@@ -10,80 +10,275 @@
# License: wxWindows License
#----------------------------------------------------------------------------
from activegrid.util.lang import *
import os
import time
import urllib
import logging
from activegrid.util.lang import *
import activegrid.util.objutils as objutils
import activegrid.util.xmlmarshaller as xmlmarshaller
import activegrid.util.aglogging as aglogging
agKnownTypes = None
def defaultLoad(fileObject, knownTypes=None):
xml = fileObject.read()
loadedObject = unmarshal(xml, knownTypes=knownTypes)
if hasattr(fileObject, 'name'):
loadedObject.fileName = os.path.abspath(fileObject.name)
loadedObject.initialize()
xmlLogger = logging.getLogger("activegrid.util.xml")
def load(fileName, knownTypes=None, knownNamespaces=None):
loadedObject = None
fileObject = file(fileName)
timeStart = time.time()
try:
xml = fileObject.read()
loadedObject = unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=fileName)
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)))
return loadedObject
def unmarshal(xml, knownTypes=None):
if not knownTypes: knownTypes = getAgKnownTypes()
return xmlmarshaller.unmarshal(xml, knownTypes=knownTypes)
def loadURI(uri, knownTypes=None, knownNamespaces=None, xmlSource=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()
return loadedObject
def defaultSave(fileObject, objectToSave, prettyPrint=True, knownTypes=None, encoding='utf-8'):
xml = marshal(objectToSave, prettyPrint=prettyPrint, knownTypes=knownTypes, encoding=encoding)
fileObject.write(xml)
fileObject.flush()
def unmarshal(xml, knownTypes=None, knownNamespaces=None, xmlSource=None):
if (knownTypes == None):
knownTypes, knownNamespaces = getAgKnownTypes()
return xmlmarshaller.unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces, xmlSource=xmlSource)
def marshal(objectToSave, prettyPrint=True, knownTypes=None, encoding='utf-8'):
if not knownTypes: knownTypes = getAgKnownTypes()
return xmlmarshaller.marshal(objectToSave, prettyPrint=prettyPrint, knownTypes=knownTypes, encoding=encoding)
def save(fileName, objectToSave, prettyPrint=True, marshalType=True, knownTypes=None, knownNamespaces=None, encoding='utf-8'):
if hasattr(objectToSave, '_xmlReadOnly') and objectToSave._xmlReadOnly == True:
raise xmlmarshaller.MarshallerException('Error marshalling object to file "%s": object is marked "readOnly" and cannot be written' % (fileName))
timeStart = time.time()
xml = marshal(objectToSave, prettyPrint=prettyPrint, marshalType=marshalType, knownTypes=knownTypes, knownNamespaces=knownNamespaces, encoding=encoding)
fileObject = file(fileName, 'w')
try:
fileObject.write(xml)
fileObject.flush()
except Exception, errorData:
fileObject.close()
raise xmlmarshaller.MarshallerException('Error marshalling object to file "%s": %s' % (fileName, str(errorData)))
fileObject.close()
timeDone = time.time()
aglogging.info(xmlLogger, ('Save statistics for file %s: elapsed time = %f secs' % (fileName, timeDone-timeStart)))
def cloneObject(objectToClone, knownTypes=None, encoding='utf-8'):
if not knownTypes: knownTypes = getAgKnownTypes()
xml = xmlmarshaller.marshal(objectToClone, prettyPrint=True, knownTypes=knownTypes, encoding=encoding)
clonedObject = xmlmarshaller.unmarshal(xml, knownTypes=knownTypes)
def marshal(objectToSave, prettyPrint=True, marshalType=True, knownTypes=None, knownNamespaces=None, encoding='utf-8'):
if (knownTypes == None):
knownTypes, knownNamespaces = getAgKnownTypes()
return xmlmarshaller.marshal(objectToSave, prettyPrint=prettyPrint, marshalType=marshalType, knownTypes=knownTypes, knownNamespaces=knownNamespaces, encoding=encoding)
def addNSAttribute(xmlDoc, shortNamespace, longNamespace):
if not hasattr(xmlDoc, "__xmlnamespaces__"):
xmlDoc.__xmlnamespaces__ = {shortNamespace:longNamespace}
elif shortNamespace not in xmlDoc.__xmlnamespaces__:
if (hasattr(xmlDoc.__class__, "__xmlnamespaces__")
and (xmlDoc.__xmlnamespaces__ is xmlDoc.__class__.__xmlnamespaces__)):
xmlDoc.__xmlnamespaces__ = dict(xmlDoc.__xmlnamespaces__)
xmlDoc.__xmlnamespaces__[shortNamespace] = longNamespace
def genShortNS(xmlDoc, longNamespace=None):
if not hasattr(xmlDoc, "__xmlnamespaces__"):
return "ns1"
elif longNamespace != None and longNamespace in xmlDoc.__xmlnamespaces__.items():
for key, value in xmlDoc.__xmlnamespaces__.iteritems():
if value == longNamespace:
return key
i = 1
while ("ns%d" % i) in xmlDoc.__xmlnamespaces__:
i += 1
return ("ns%d" % i)
def genTargetNS(fileName, applicationName=None, type=None):
if (applicationName != None):
if (type != None):
tns = "urn:%s:%s:%s" % (applicationName, type, fileName)
else:
tns = "urn:%s:%s" % (applicationName, fileName)
else:
tns = "urn:%s" % fileName
return tns
def splitType(typeName):
index = typeName.rfind(':')
if index != -1:
ns = typeName[:index]
complexTypeName = typeName[index+1:]
else:
ns = None
complexTypeName = typeName
return (ns, complexTypeName)
def cloneObject(objectToClone, knownTypes=None, marshalType=True, knownNamespaces=None, encoding='utf-8'):
if (knownTypes == None):
knownTypes, knownNamespaces = getAgKnownTypes()
xml = xmlmarshaller.marshal(objectToClone, prettyPrint=True, marshalType=marshalType, knownTypes=knownTypes, knownNamespaces=knownNamespaces, encoding=encoding)
clonedObject = xmlmarshaller.unmarshal(xml, knownTypes=knownTypes, knownNamespaces=knownNamespaces)
if hasattr(objectToClone, 'fileName'):
clonedObject.fileName = objectToClone.fileName
if hasattr(objectToClone, "_parentDoc"):
clonedObject._parentDoc = objectToClone._parentDoc
try:
clonedObject.initialize()
except AttributeError:
pass
return clonedObject
def getAgKnownTypes():
import activegrid.model.processmodel
import activegrid.model.schema
import activegrid.data.dataservice
import activegrid.server.deployment
global agKnownTypes
if agKnownTypes == None:
tmpAgKnownTypes = {}
AG_TYPE_MAPPING = {
def getAgVersion(fileName):
fileObject = file(fileName)
try:
xml = fileObject.read()
finally:
fileObject.close()
i = xml.find(' ag:version=')
if i >= 0:
i += 12
else:
i2 = xml.find('<ag:')
if i2 >= 0:
i = xml.find(' version=', i2)
if i > 0:
i += 9
elif xml.find('<project version="10"') >= 0:
return "10"
else:
return None
version = None
if xml[i:i+1] == '"':
j = xml.find('"', i+1)
if (j > i+1):
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"
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/"
WSDL_NS_URL = "http://schemas.xmlsoap.org/wsdl/"
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"
XACML_NS_URL = "urn:oasis:names:tc:xacml:2.0:policy:schema:os"
KNOWN_NAMESPACES = { AG_NS_URL : "ag",
BPEL_NS_URL : "bpws",
HTTP_WSDL_NS_URL : "http",
MIME_WSDL_NS_URL : "mime",
SOAP_NS_URL : "soap",
SOAP12_NS_URL : "soap12",
WSDL_NS_URL : "wsdl",
XFORMS_NS_URL : "xforms",
XMLSCHEMA_NS_URL : "xs",
XACML_NS_URL : "xacml",
}
global agXsdToClassName
agXsdToClassName = None
def getAgXsdToClassName():
global agXsdToClassName
if (agXsdToClassName == None):
agXsdToClassName = {
"ag:append" : "activegrid.model.processmodel.AppendOperation",
"ag:attribute" : "activegrid.model.identitymodel.Attribute",
"ag:body" : "activegrid.model.processmodel.Body",
"ag:category_substitutions" : "activegrid.server.layoutrenderer.CategorySubstitutions",
"ag:command" : "activegrid.model.wsdl.Command",
"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:glue" : "activegrid.model.processmodel.Glue",
"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:inputs" : "activegrid.model.processmodel.Inputs",
"ag:label" : "activegrid.model.processmodel.Label",
"ag:processmodel" : "activegrid.model.processmodel.ProcessModel",
"ag:processmodelref" : "activegrid.server.deployment.ProcessModelRef",
"ag:layout" : "activegrid.server.layoutrenderer.Layout",
"ag:layouts" : "activegrid.server.layoutrenderer.Layouts",
"ag:ldapsource" : "activegrid.model.identitymodel.LDAPSource",
"ag:localService" : "activegrid.server.deployment.LocalService",
"ag:parameter" : "activegrid.server.layoutrenderer.Parameter",
"ag:parameters" : "activegrid.server.layoutrenderer.Parameters",
"ag:processref" : "activegrid.server.deployment.ProcessRef",
"ag:query" : "activegrid.model.processmodel.Query",
"ag:restParameter" : "activegrid.server.deployment.RestParameter",
"ag:soapService" : "activegrid.server.deployment.SoapService",
"ag:requiredFile" : "activegrid.server.layoutrenderer.RequiredFile",
"ag:resource" : "activegrid.model.identitymodel.IDResource",
"ag:restService" : "activegrid.server.deployment.RestService",
"ag:rewrite" : "activegrid.model.wsdl.Rewrite",
"ag:role" : "activegrid.model.identitymodel.IDRole",
"ag:roledefn" : "activegrid.model.identitymodel.RoleDefn",
"ag:rssService" : "activegrid.server.deployment.RssService",
"ag:rule" : "activegrid.model.identitymodel.IDRule",
"ag:schemaOptions" : "activegrid.model.schema.SchemaOptions",
"ag:schemaref" : "activegrid.server.deployment.SchemaRef",
"ag:serviceCache" : "activegrid.server.deployment.ServiceCache",
"ag:serviceExtension": "activegrid.model.wsdl.ServiceExtension",
"ag:serviceExtensions": "activegrid.model.wsdl.ServiceExtensions",
"ag:serviceParameter": "activegrid.server.deployment.ServiceParameter",
"ag:serviceref" : "activegrid.server.deployment.ServiceRef",
"ag:set" : "activegrid.model.processmodel.SetOperation",
"ag:skinref" : "activegrid.server.deployment.SkinRef",
"ag:skin" : "activegrid.server.layoutrenderer.Skin",
"ag:skin_element_ref": "activegrid.server.layoutrenderer.SkinElementRef",
"ag:skin_element" : "activegrid.server.layoutrenderer.SkinElement",
"ag:skins" : "activegrid.server.layoutrenderer.Skins",
"ag:substitution" : "activegrid.server.layoutrenderer.Substitution",
"ag:text" : "activegrid.model.processmodel.Text",
"ag:title" : "activegrid.model.processmodel.Title",
"ag:view" : "activegrid.model.processmodel.View",
"ag:usertemplate" : "activegrid.model.identitymodel.UserTemplate",
"ag:xformref" : "activegrid.server.deployment.XFormRef",
"bpws:case" : "activegrid.model.processmodel.BPELCase",
"bpws:catch" : "activegrid.model.processmodel.BPELCatch",
"bpws:faultHandlers" : "activegrid.model.processmodel.BPELFaultHandlers",
"bpws:flow" : "activegrid.model.processmodel.BPELFlow",
"bpws:invoke" : "activegrid.model.processmodel.BPELInvoke",
"bpws:onMessage" : "activegrid.model.processmodel.BPELOnMessage",
"bpws:otherwise" : "activegrid.model.processmodel.BPELOtherwise",
@@ -98,31 +293,117 @@ def getAgKnownTypes():
"bpws:variable" : "activegrid.model.processmodel.BPELVariable",
"bpws:variables" : "activegrid.model.processmodel.BPELVariables",
"bpws:while" : "activegrid.model.processmodel.BPELWhile",
"wsdl:message" : "activegrid.model.processmodel.WSDLMessage",
"wsdl:part" : "activegrid.model.processmodel.WSDLPart",
"http:address" : "activegrid.model.wsdl.HttpAddress",
"http:binding" : "activegrid.model.wsdl.HttpBinding",
"http:operation" : "activegrid.model.wsdl.HttpOperation",
"http:urlEncoded" : "activegrid.model.wsdl.HttpUrlEncoded",
"mime:content" : "activegrid.model.wsdl.MimeContent",
"mime:mimeXml" : "activegrid.model.wsdl.MimeMimeXml",
"soap:address" : "activegrid.model.wsdl.SoapAddress",
"soap:binding" : "activegrid.model.wsdl.SoapBinding",
"soap:body" : "activegrid.model.wsdl.SoapBody",
"soap:fault" : "activegrid.model.wsdl.SoapFault",
"soap:header" : "activegrid.model.wsdl.SoapHeader",
"soap:operation" : "activegrid.model.wsdl.SoapOperation",
"soap12:address" : "activegrid.model.wsdl.Soap12Address",
"soap12:binding" : "activegrid.model.wsdl.Soap12Binding",
"soap12:body" : "activegrid.model.wsdl.Soap12Body",
"soap12:fault" : "activegrid.model.wsdl.Soap12Fault",
"soap12:header" : "activegrid.model.wsdl.Soap12Header",
"soap12:operation" : "activegrid.model.wsdl.Soap12Operation",
"wsdl:binding" : "activegrid.model.wsdl.WsdlBinding",
"wsdl:definitions" : "activegrid.model.wsdl.WsdlDocument",
"wsdl:documentation" : "activegrid.model.wsdl.WsdlDocumentation",
"wsdl:fault" : "activegrid.model.wsdl.WsdlFault",
"wsdl:import" : "activegrid.model.wsdl.WsdlImport",
"wsdl:input" : "activegrid.model.wsdl.WsdlInput",
"wsdl:message" : "activegrid.model.wsdl.WsdlMessage",
"wsdl:operation" : "activegrid.model.wsdl.WsdlOperation",
"wsdl:output" : "activegrid.model.wsdl.WsdlOutput",
"wsdl:part" : "activegrid.model.wsdl.WsdlPart",
"wsdl:port" : "activegrid.model.wsdl.WsdlPort",
"wsdl:portType" : "activegrid.model.wsdl.WsdlPortType",
"wsdl:service" : "activegrid.model.wsdl.WsdlService",
"wsdl:types" : "activegrid.model.wsdl.WsdlTypes",
"xacml:Action" : "activegrid.model.identitymodel.XACMLAction",
"xacml:ActionAttributeDesignator" : "activegrid.model.identitymodel.XACMLActionAttributeDesignator",
"xacml:ActionMatch" : "activegrid.model.identitymodel.XACMLActionMatch",
"xacml:Actions" : "activegrid.model.identitymodel.XACMLActions",
"xacml:AttributeValue" : "activegrid.model.identitymodel.XACMLAttributeValue",
"xacml:Policy" : "activegrid.model.identitymodel.XACMLPolicy",
"xacml:Resource" : "activegrid.model.identitymodel.XACMLResource",
"xacml:ResourceAttributeDesignator" : "activegrid.model.identitymodel.XACMLResourceAttributeDesignator",
"xacml:ResourceMatch" : "activegrid.model.identitymodel.XACMLResourceMatch",
"xacml:Resources" : "activegrid.model.identitymodel.XACMLResources",
"xacml:Rule" : "activegrid.model.identitymodel.XACMLRule",
"xacml:Target" : "activegrid.model.identitymodel.XACMLTarget",
"xforms:copy" : "activegrid.model.processmodel.XFormsCopy",
"xforms:group" : "activegrid.model.processmodel.XFormsGroup",
"xforms:include" : "activegrid.model.processmodel.XFormsInclude",
"xforms:input" : "activegrid.model.processmodel.XFormsInput",
"xforms:item" : "activegrid.model.processmodel.XFormsItem",
"xforms:itemset" : "activegrid.model.processmodel.XFormsItemset",
"xforms:label" : "activegrid.model.processmodel.XFormsLabel",
"xforms:model" : "activegrid.model.processmodel.XFormsModel",
"xforms:output" : "activegrid.model.processmodel.XFormsOutput",
"xforms:secret" : "activegrid.model.processmodel.XFormsSecret",
"xforms:select1" : "activegrid.model.processmodel.XFormsSelect1",
"xforms:submission" : "activegrid.model.processmodel.XFormsSubmission",
"xforms:submit" : "activegrid.model.processmodel.XFormsSubmit",
"xforms:value" : "activegrid.model.processmodel.XFormsValue",
"xforms:xform" : "activegrid.model.processmodel.View",
"xforms:xforms" : "activegrid.model.processmodel.XFormsRoot",
"xs:all" : "activegrid.model.schema.XsdSequence",
"xs:any" : "activegrid.model.schema.XsdAny",
"xs:attribute" : "activegrid.model.schema.XsdAttribute",
"xs:complexContent" : "activegrid.model.schema.XsdComplexContent",
"xs:complexType" : "activegrid.model.schema.XsdComplexType",
"xs:element" : "activegrid.model.schema.XsdElement",
"xs:enumeration" : "activegrid.model.schema.XsdEnumeration",
"xs:extension" : "activegrid.model.schema.XsdExtension",
"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:list" : "activegrid.model.schema.XsdList",
"xs:maxLength" : "activegrid.model.schema.XsdMaxLength",
"xs:restriction" : "activegrid.model.schema.XsdRestriction",
"xs:schema" : "activegrid.model.schema.Schema",
"xs:selector" : "activegrid.model.schema.XsdKeySelector",
"xs:sequence" : "activegrid.model.schema.XsdSequence",
}
for keyName, className in AG_TYPE_MAPPING.iteritems():
try:
tmpAgKnownTypes[keyName] = objutils.classForName(className)
except KeyError:
print "Error mapping knownType", className
pass
if len(tmpAgKnownTypes) > 0:
agKnownTypes = tmpAgKnownTypes
return agKnownTypes
"xs:simpleContent" : "activegrid.model.schema.XsdSimpleContent",
"xs:simpleType" : "activegrid.model.schema.XsdSimpleType",
"xs:totalDigits" : "activegrid.model.schema.XsdTotalDigits",
}
return agXsdToClassName
global agKnownTypes
agKnownTypes = None
def getAgKnownTypes():
global agKnownTypes
if agKnownTypes == None:
try:
tmpAgKnownTypes = {}
import activegrid.model.processmodel
import activegrid.model.schema
import activegrid.server.deployment
import activegrid.model.wsdl
ifDefPy()
import activegrid.data.dataservice
endIfDef()
for keyName, className in getAgXsdToClassName().iteritems():
classType = objutils.classForName(className)
if (classType == None):
raise Exception("Cannot get class type for %s" % className)
else:
tmpAgKnownTypes[keyName] = classType
if len(tmpAgKnownTypes) > 0:
agKnownTypes = tmpAgKnownTypes
except ImportError:
agKnownTypes = {}
if len(agKnownTypes) == 0: # standalone IDE and XmlMarshaller don't contain known AG types
noKnownNamespaces = {}
return agKnownTypes, noKnownNamespaces
return agKnownTypes, KNOWN_NAMESPACES

View File

@@ -12,12 +12,12 @@
import sys
import os.path
import wx
import wx.lib.docview as docview
import wx.lib.pydocview as pydocview
import TextEditor
import FindService
import os.path
_ = wx.GetTranslation
@@ -90,7 +90,7 @@ class TextEditorApplication(pydocview.DocApp):
if os.path.exists("tips.txt"):
wx.CallAfter(self.ShowTip, wx.GetApp().GetTopWindow(), wx.CreateFileTipProvider("tips.txt", 0))
wx.UpdateUIEvent.SetUpdateInterval(400) # Overhead of updating menus was too much. Change to update every 400 milliseconds.
wx.UpdateUIEvent.SetUpdateInterval(1000) # Overhead of updating menus was too much. Change to update every N milliseconds.
# Tell the framework that everything is great
return True