Added the ActiveGrid IDE as a sample application
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@33440 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -61,10 +61,12 @@ wxPython/licence
|
||||
wxPython/samples
|
||||
wxPython/samples/StyleEditor
|
||||
wxPython/samples/docview
|
||||
wxPython/samples/docview/activegrid
|
||||
wxPython/samples/docview/activegrid/tool
|
||||
wxPython/samples/docview/activegrid/tool/data
|
||||
wxPython/samples/docview/activegrid/tool/images
|
||||
wxPython/samples/pydocview
|
||||
wxPython/samples/ide
|
||||
wxPython/samples/ide/activegrid
|
||||
wxPython/samples/ide/activegrid/tool
|
||||
wxPython/samples/ide/activegrid/tool/data
|
||||
wxPython/samples/ide/activegrid/util
|
||||
wxPython/samples/doodle
|
||||
wxPython/samples/embedded
|
||||
wxPython/samples/frogedit
|
||||
|
@@ -410,10 +410,15 @@ Source: "samples\doodle\sample.ddl"; DestDir: "{app}\samples\doodle";
|
||||
Source: "samples\doodle\superdoodle.iss"; DestDir: "{app}\samples\doodle";
|
||||
|
||||
Source: "samples\docview\*.py"; DestDir: "{app}\samples\docview";
|
||||
Source: "samples\docview\activegrid\*.py"; DestDir: "{app}\samples\docview\activegrid";
|
||||
Source: "samples\docview\activegrid\tool\*.py"; DestDir: "{app}\samples\docview\activegrid\tool";
|
||||
Source: "samples\docview\activegrid\tool\data\*.txt"; DestDir: "{app}\samples\docview\activegrid\tool\data";
|
||||
Source: "samples\docview\activegrid\tool\images\*.jpg"; DestDir: "{app}\samples\docview\activegrid\tool\images";
|
||||
Source: "samples\pydocview\*.py"; DestDir: "{app}\samples\pydocview";
|
||||
Source: "samples\pydocview\*.jpg"; DestDir: "{app}\samples\pydocview";
|
||||
Source: "samples\pydocview\*.txt"; DestDir: "{app}\samples\pydocview";
|
||||
|
||||
Source: "samples\ide\*.py"; DestDir: "{app}\samples\ide";
|
||||
Source: "samples\ide\activegrid\*.py"; DestDir: "{app}\samples\ide\activegrid";
|
||||
Source: "samples\ide\activegrid\tool\*.py"; DestDir: "{app}\samples\ide\activegrid\tool";
|
||||
Source: "samples\ide\activegrid\tool\data\*.txt"; DestDir: "{app}\samples\ide\activegrid\tool\data";
|
||||
Source: "samples\ide\activegrid\util\*.py"; DestDir: "{app}\samples\ide\activegrid\util";
|
||||
|
||||
Source: "samples\embedded\*.py"; DestDir: "{app}\samples\embedded";
|
||||
Source: "samples\embedded\*.cpp"; DestDir: "{app}\samples\embedded";
|
||||
|
@@ -85,6 +85,7 @@ list of top-level windows that currently exist in the application.
|
||||
Updated docview library modules and sample apps from the ActiveGrid
|
||||
folks.
|
||||
|
||||
Added the ActiveGrid IDE as a sample application.
|
||||
|
||||
|
||||
|
||||
|
33
wxPython/samples/ide/ActiveGridIDE.py
Normal file
33
wxPython/samples/ide/ActiveGridIDE.py
Normal file
@@ -0,0 +1,33 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: ActiveGridIDE.py
|
||||
# Purpose:
|
||||
#
|
||||
# Author: Lawrence Bruhmuller
|
||||
#
|
||||
# Created: 3/30/05
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
import wx.lib.pydocview
|
||||
import activegrid.tool.IDE
|
||||
|
||||
import os
|
||||
import sys
|
||||
sys.stdout = sys.stderr
|
||||
|
||||
# This is here as the base IDE entry point. Only difference is that -baseide is passed.
|
||||
|
||||
sys.argv.append('-baseide');
|
||||
|
||||
# Put activegrid dir in path so python files can be found from py2exe
|
||||
# This code should never do anything when run from the python interpreter
|
||||
execDir = os.path.dirname(sys.executable)
|
||||
try:
|
||||
sys.path.index(execDir)
|
||||
except ValueError:
|
||||
sys.path.append(execDir)
|
||||
app = activegrid.tool.IDE.IDEApplication(redirect = False)
|
||||
app.GetTopWindow().Raise() # sometimes it shows up beneath other windows. e.g. running self in debugger
|
||||
app.MainLoop()
|
||||
|
0
wxPython/samples/ide/activegrid/__init__.py
Normal file
0
wxPython/samples/ide/activegrid/__init__.py
Normal file
119
wxPython/samples/ide/activegrid/tool/AboutDialog.py
Normal file
119
wxPython/samples/ide/activegrid/tool/AboutDialog.py
Normal file
@@ -0,0 +1,119 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: AboutDialog.py
|
||||
# Purpose: AboutBox which has copyright notice, license information, and credits
|
||||
#
|
||||
# Author: Morgan Hua
|
||||
#
|
||||
# Created: 3/22/05
|
||||
# Copyright: (c) 2005 ActiveGrid, Inc.
|
||||
# CVS-ID: $Id$
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import wx
|
||||
from IDE import ACTIVEGRID_BASE_IDE, getSplashBitmap
|
||||
_ = wx.GetTranslation
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Package License Data for AboutDialog
|
||||
# Package, License, URL
|
||||
# If no information is available, put a None as a place holder.
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
|
||||
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"),
|
||||
("pychecker", "MetaSlash - BSD", "http://pychecker.sourceforge.net/COPYRIGHT"),
|
||||
("process.py", "See file", "http://starship.python.net/~tmick/"),
|
||||
]
|
||||
|
||||
if not ACTIVEGRID_BASE_IDE: # add licenses for database connections only if not the base IDE
|
||||
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"),
|
||||
("SQLite", "Public Domain", "http://www.sqlite.org/copyright.html"),
|
||||
("PyGreSQL", "BSD", "http://www.pygresql.org"),
|
||||
]
|
||||
|
||||
if wx.Platform == '__WXMSW__':
|
||||
licenseData += [("pywin32", "Python Software Foundation License", "http://sourceforge.net/projects/pywin32/")]
|
||||
|
||||
class AboutDialog(wx.Dialog):
|
||||
|
||||
def __init__(self, parent):
|
||||
"""
|
||||
Initializes the about dialog.
|
||||
"""
|
||||
wx.Dialog.__init__(self, parent, -1, _("About ") + wx.GetApp().GetAppName(), style = wx.DEFAULT_DIALOG_STYLE)
|
||||
|
||||
nb = wx.Notebook(self, -1)
|
||||
|
||||
aboutPage = wx.Panel(nb, -1)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
splash_bmp = getSplashBitmap()
|
||||
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.6 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, _("http://www.activegrid.com")), 0, wx.ALIGN_LEFT|wx.LEFT|wx.BOTTOM, 10)
|
||||
aboutPage.SetSizer(sizer)
|
||||
nb.AddPage(aboutPage, _("Copyright"))
|
||||
|
||||
licensePage = wx.Panel(nb, -1)
|
||||
grid = wx.grid.Grid(licensePage, -1)
|
||||
grid.CreateGrid(len(licenseData), 2)
|
||||
|
||||
dc = wx.ClientDC(grid)
|
||||
dc.SetFont(grid.GetLabelFont())
|
||||
grid.SetColLabelValue(0, _("License"))
|
||||
grid.SetColLabelValue(1, _("URL"))
|
||||
w, maxHeight = dc.GetTextExtent(_("License"))
|
||||
w, h = dc.GetTextExtent(_("URL"))
|
||||
if h > maxHeight:
|
||||
maxHeight = h
|
||||
grid.SetColLabelSize(maxHeight + 6) # add a 6 pixel margin
|
||||
|
||||
for row, data in enumerate(licenseData):
|
||||
package = data[0]
|
||||
license = data[1]
|
||||
url = data[2]
|
||||
if package:
|
||||
grid.SetRowLabelValue(row, package)
|
||||
if license:
|
||||
grid.SetCellValue(row, 0, license)
|
||||
if url:
|
||||
grid.SetCellValue(row, 1, url)
|
||||
|
||||
grid.EnableEditing(False)
|
||||
grid.EnableDragGridSize(False)
|
||||
grid.EnableDragColSize(False)
|
||||
grid.EnableDragRowSize(False)
|
||||
grid.SetRowLabelAlignment(wx.ALIGN_LEFT, wx.ALIGN_CENTRE)
|
||||
grid.SetLabelBackgroundColour(wx.WHITE)
|
||||
grid.AutoSizeColumn(0, 100)
|
||||
grid.AutoSizeColumn(1, 100)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.Add(grid, 1, wx.EXPAND|wx.ALL, 10)
|
||||
licensePage.SetSizer(sizer)
|
||||
nb.AddPage(licensePage, _("Licenses"))
|
||||
|
||||
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)
|
||||
creditsPage.SetSizer(sizer)
|
||||
nb.AddPage(creditsPage, _("Credits"))
|
||||
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
sizer.Add(nb, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
|
||||
btn = wx.Button(self, wx.ID_OK)
|
||||
sizer.Add(btn, 0, wx.ALIGN_CENTRE|wx.ALL, 5)
|
||||
|
||||
self.SetSizer(sizer)
|
||||
self.SetAutoLayout(True)
|
||||
sizer.Fit(self)
|
||||
|
||||
|
641
wxPython/samples/ide/activegrid/tool/AbstractEditor.py
Normal file
641
wxPython/samples/ide/activegrid/tool/AbstractEditor.py
Normal file
@@ -0,0 +1,641 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: AbstractEditor.py
|
||||
# Purpose: Non-text editor for DataModel and Process
|
||||
#
|
||||
# Author: Peter Yared, Morgan Hua
|
||||
#
|
||||
# Created: 7/28/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
|
||||
import wx
|
||||
import wx.lib.docview
|
||||
import wx.lib.ogl as ogl
|
||||
import PropertyService
|
||||
_ = wx.GetTranslation
|
||||
|
||||
|
||||
SELECT_BRUSH = wx.Brush("BLUE", wx.SOLID)
|
||||
SHAPE_BRUSH = wx.Brush("WHEAT", wx.SOLID)
|
||||
LINE_BRUSH = wx.BLACK_BRUSH
|
||||
|
||||
|
||||
def GetRawModel(model):
|
||||
if hasattr(model, "GetRawModel"):
|
||||
rawModel = model.GetRawModel()
|
||||
else:
|
||||
rawModel = model
|
||||
return rawModel
|
||||
|
||||
|
||||
class CanvasView(wx.lib.docview.View):
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Overridden methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def __init__(self, brush = SHAPE_BRUSH):
|
||||
wx.lib.docview.View.__init__(self)
|
||||
self._brush = brush
|
||||
self._canvas = None
|
||||
self._pt1 = None
|
||||
self._pt2 = None
|
||||
self._needEraseLasso = False
|
||||
self._propShape = None
|
||||
|
||||
|
||||
def OnCreate(self, doc, flags):
|
||||
frame = wx.GetApp().CreateDocumentFrame(self, doc, flags)
|
||||
frame.Show()
|
||||
sizer = wx.BoxSizer()
|
||||
self._CreateCanvas(frame)
|
||||
sizer.Add(self._canvas, 1, wx.EXPAND, 0)
|
||||
frame.SetSizer(sizer)
|
||||
frame.Layout()
|
||||
self.Activate()
|
||||
return True
|
||||
|
||||
|
||||
def OnActivateView(self, activate, activeView, deactiveView):
|
||||
if activate and self._canvas:
|
||||
# In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop
|
||||
if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
|
||||
self._canvas.SetFocus()
|
||||
else:
|
||||
wx.CallAfter(self._canvas.SetFocus)
|
||||
|
||||
|
||||
def OnClose(self, deleteWindow = True):
|
||||
statusC = wx.GetApp().CloseChildDocuments(self.GetDocument())
|
||||
statusP = wx.lib.docview.View.OnClose(self, deleteWindow = deleteWindow)
|
||||
if hasattr(self, "ClearOutline"):
|
||||
wx.CallAfter(self.ClearOutline) # need CallAfter because when closing the document, it is Activated and then Close, so need to match OnActivateView's CallAfter
|
||||
if not (statusC and statusP):
|
||||
return False
|
||||
self.Activate(False)
|
||||
if deleteWindow and self.GetFrame():
|
||||
self.GetFrame().Destroy()
|
||||
return True
|
||||
|
||||
|
||||
def _CreateCanvas(self, parent):
|
||||
self._canvas = ogl.ShapeCanvas(parent)
|
||||
wx.EVT_LEFT_DOWN(self._canvas, self.OnLeftClick)
|
||||
wx.EVT_LEFT_UP(self._canvas, self.OnLeftUp)
|
||||
wx.EVT_MOTION(self._canvas, self.OnLeftDrag)
|
||||
wx.EVT_LEFT_DCLICK(self._canvas, self.OnLeftDoubleClick)
|
||||
wx.EVT_KEY_DOWN(self._canvas, self.OnKeyPressed)
|
||||
|
||||
maxWidth = 2000
|
||||
maxHeight = 16000
|
||||
self._canvas.SetScrollbars(20, 20, maxWidth / 20, maxHeight / 20)
|
||||
|
||||
self._canvas.SetBackgroundColour(wx.WHITE)
|
||||
self._diagram = ogl.Diagram()
|
||||
self._canvas.SetDiagram(self._diagram)
|
||||
self._diagram.SetCanvas(self._canvas)
|
||||
|
||||
|
||||
def OnKeyPressed(self, event):
|
||||
key = event.KeyCode()
|
||||
if key == wx.WXK_DELETE:
|
||||
self.OnClear(event)
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
|
||||
def OnLeftClick(self, event):
|
||||
self.EraseRubberBand()
|
||||
|
||||
dc = wx.ClientDC(self._canvas)
|
||||
self._canvas.PrepareDC(dc)
|
||||
|
||||
# keep track of mouse down for group select
|
||||
self._pt1 = event.GetLogicalPosition(dc) # this takes into account scrollbar offset
|
||||
self._pt2 = None
|
||||
|
||||
shape = self._canvas.FindShape(self._pt1[0], self._pt1[1])[0]
|
||||
if shape:
|
||||
self.BringToFront(shape)
|
||||
|
||||
self._pt1 = None
|
||||
event.Skip() # pass on event to shape handler to take care of selection
|
||||
|
||||
return
|
||||
elif event.ControlDown() or event.ShiftDown(): # extend select, don't deselect
|
||||
pass
|
||||
else:
|
||||
# click on empty part of canvas, deselect everything
|
||||
needRefresh = False
|
||||
for shape in self._diagram.GetShapeList():
|
||||
if hasattr(shape, "GetModel"):
|
||||
if shape.Selected():
|
||||
needRefresh = True
|
||||
shape.Select(False, dc)
|
||||
if needRefresh:
|
||||
self._canvas.Redraw(dc)
|
||||
|
||||
self.SetPropertyModel(None)
|
||||
|
||||
if len(self.GetSelection()) == 0:
|
||||
self.SetPropertyShape(None)
|
||||
|
||||
|
||||
|
||||
def OnLeftDoubleClick(self, event):
|
||||
propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
|
||||
if propertyService:
|
||||
propertyService.ShowWindow()
|
||||
|
||||
|
||||
def OnLeftDrag(self, event):
|
||||
# draw lasso for group select
|
||||
if self._pt1 and event.LeftIsDown(): # we are in middle of lasso selection
|
||||
self.EraseRubberBand()
|
||||
|
||||
dc = wx.ClientDC(self._canvas)
|
||||
self._canvas.PrepareDC(dc)
|
||||
self._pt2 = event.GetLogicalPosition(dc) # this takes into account scrollbar offset
|
||||
self.DrawRubberBand()
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
|
||||
def OnLeftUp(self, event):
|
||||
# do group select
|
||||
if self._needEraseLasso:
|
||||
self.EraseRubberBand()
|
||||
|
||||
dc = wx.ClientDC(self._canvas)
|
||||
self._canvas.PrepareDC(dc)
|
||||
x1, y1 = self._pt1
|
||||
x2, y2 = event.GetLogicalPosition(dc) # this takes into account scrollbar offset
|
||||
|
||||
tol = self._diagram.GetMouseTolerance()
|
||||
if abs(x1 - x2) > tol or abs(y1 - y2) > tol:
|
||||
# make sure x1 < x2 and y1 < y2 to make comparison test easier
|
||||
if x1 > x2:
|
||||
temp = x1
|
||||
x1 = x2
|
||||
x2 = temp
|
||||
if y1 > y2:
|
||||
temp = y1
|
||||
y1 = y2
|
||||
y2 = temp
|
||||
|
||||
for shape in self._diagram.GetShapeList():
|
||||
if not shape.GetParent() and hasattr(shape, "GetModel"): # if part of a composite, don't select it
|
||||
x, y = shape.GetX(), shape.GetY()
|
||||
width, height = shape.GetBoundingBoxMax()
|
||||
selected = x1 < x - width/2 and x2 > x + width/2 and y1 < y - height/2 and y2 > y + height/2
|
||||
if event.ControlDown() or event.ShiftDown(): # extend select, don't deselect
|
||||
if selected:
|
||||
shape.Select(selected, dc)
|
||||
else: # select items in lasso and deselect items out of lasso
|
||||
shape.Select(selected, dc)
|
||||
self._canvas.Redraw(dc)
|
||||
else:
|
||||
event.Skip()
|
||||
else:
|
||||
event.Skip()
|
||||
|
||||
|
||||
def EraseRubberBand(self):
|
||||
if self._needEraseLasso:
|
||||
self._needEraseLasso = False
|
||||
|
||||
dc = wx.ClientDC(self._canvas)
|
||||
self._canvas.PrepareDC(dc)
|
||||
dc.SetLogicalFunction(wx.XOR)
|
||||
pen = wx.Pen(wx.Colour(200, 200, 200), 1, wx.SHORT_DASH)
|
||||
dc.SetPen(pen)
|
||||
brush = wx.Brush(wx.Colour(255, 255, 255), wx.TRANSPARENT)
|
||||
dc.SetBrush(brush)
|
||||
dc.ResetBoundingBox()
|
||||
dc.BeginDrawing()
|
||||
|
||||
x1, y1 = self._pt1
|
||||
x2, y2 = self._pt2
|
||||
|
||||
# make sure x1 < x2 and y1 < y2
|
||||
# this will make (x1, y1) = upper left corner
|
||||
if x1 > x2:
|
||||
temp = x1
|
||||
x1 = x2
|
||||
x2 = temp
|
||||
if y1 > y2:
|
||||
temp = y1
|
||||
y1 = y2
|
||||
y2 = temp
|
||||
|
||||
# erase previous outline
|
||||
dc.SetClippingRegion(x1, y1, x2 - x1, y2 - y1)
|
||||
dc.DrawRectangle(x1, y1, x2 - x1, y2 - y1)
|
||||
dc.EndDrawing()
|
||||
|
||||
|
||||
def DrawRubberBand(self):
|
||||
self._needEraseLasso = True
|
||||
|
||||
dc = wx.ClientDC(self._canvas)
|
||||
self._canvas.PrepareDC(dc)
|
||||
dc.SetLogicalFunction(wx.XOR)
|
||||
pen = wx.Pen(wx.Colour(200, 200, 200), 1, wx.SHORT_DASH)
|
||||
dc.SetPen(pen)
|
||||
brush = wx.Brush(wx.Colour(255, 255, 255), wx.TRANSPARENT)
|
||||
dc.SetBrush(brush)
|
||||
dc.ResetBoundingBox()
|
||||
dc.BeginDrawing()
|
||||
|
||||
x1, y1 = self._pt1
|
||||
x2, y2 = self._pt2
|
||||
|
||||
# make sure x1 < x2 and y1 < y2
|
||||
# this will make (x1, y1) = upper left corner
|
||||
if x1 > x2:
|
||||
temp = x1
|
||||
x1 = x2
|
||||
x2 = temp
|
||||
if y1 > y2:
|
||||
temp = y1
|
||||
y1 = y2
|
||||
y2 = temp
|
||||
|
||||
# draw outline
|
||||
dc.SetClippingRegion(x1, y1, x2 - x1, y2 - y1)
|
||||
dc.DrawRectangle(x1, y1, x2 - x1, y2 - y1)
|
||||
dc.EndDrawing()
|
||||
|
||||
|
||||
def FindParkingSpot(self, width, height):
|
||||
""" 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
|
||||
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
|
||||
else:
|
||||
noParkingSpot = False
|
||||
|
||||
return x, y
|
||||
|
||||
|
||||
def isSpotOccupied(self, x, y, width, height):
|
||||
""" returns None if at x,y,width,height no object occupies that rectangle,
|
||||
otherwise returns lower right corner of object that occupies given x,y position
|
||||
"""
|
||||
x2 = x + width
|
||||
y2 = y + height
|
||||
|
||||
for shape in self._diagram.GetShapeList():
|
||||
if isinstance(shape, ogl.RectangleShape) or isinstance(shape, ogl.EllipseShape):
|
||||
if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape):
|
||||
# skip, part of a composite shape
|
||||
continue
|
||||
|
||||
if hasattr(shape, "GetModel"):
|
||||
other_x, other_y, other_width, other_height = shape.GetModel().getEditorBounds()
|
||||
other_x2 = other_x + other_width
|
||||
other_y2 = other_y + other_height
|
||||
else:
|
||||
# shapes x,y are at the center of the shape, need to transform to upper left coordinate
|
||||
other_width, other_height = shape.GetBoundingBoxMax()
|
||||
other_x = shape.GetX() - other_width/2
|
||||
other_y = shape.GetY() - other_height/2
|
||||
|
||||
other_x2 = other_x + other_width
|
||||
other_y2 = other_y + other_height
|
||||
# intersection check
|
||||
if ((other_x2 < other_x or other_x2 > x) and
|
||||
(other_y2 < other_y or other_y2 > y) and
|
||||
(x2 < x or x2 > other_x) and
|
||||
(y2 < y or y2 > other_y)):
|
||||
return (other_x2, other_y2)
|
||||
return None
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Canvas methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def AddShape(self, shape, x = None, y = None, pen = None, brush = None, text = None, eventHandler = None):
|
||||
if isinstance(shape, ogl.CompositeShape):
|
||||
dc = wx.ClientDC(self._canvas)
|
||||
self._canvas.PrepareDC(dc)
|
||||
shape.Move(dc, x, y)
|
||||
else:
|
||||
shape.SetDraggable(True, True)
|
||||
shape.SetCanvas(self._canvas)
|
||||
|
||||
if x:
|
||||
shape.SetX(x)
|
||||
if y:
|
||||
shape.SetY(y)
|
||||
shape.SetCentreResize(False)
|
||||
if pen:
|
||||
shape.SetPen(pen)
|
||||
if brush:
|
||||
shape.SetBrush(brush)
|
||||
if text:
|
||||
shape.AddText(text)
|
||||
shape.SetShadowMode(ogl.SHADOW_RIGHT)
|
||||
self._diagram.AddShape(shape)
|
||||
shape.Show(True)
|
||||
if not eventHandler:
|
||||
eventHandler = EditorCanvasShapeEvtHandler(self)
|
||||
eventHandler.SetShape(shape)
|
||||
eventHandler.SetPreviousHandler(shape.GetEventHandler())
|
||||
shape.SetEventHandler(eventHandler)
|
||||
return shape
|
||||
|
||||
|
||||
def RemoveShape(self, model = None, shape = None):
|
||||
if not model and not shape:
|
||||
return
|
||||
|
||||
if not shape:
|
||||
shape = self.GetShape(model)
|
||||
|
||||
if shape:
|
||||
shape.Select(False)
|
||||
self._diagram.RemoveShape(shape)
|
||||
if isinstance(shape, ogl.CompositeShape):
|
||||
shape.RemoveFromCanvas(self._canvas)
|
||||
|
||||
|
||||
def UpdateShape(self, model):
|
||||
for shape in self._diagram.GetShapeList():
|
||||
if hasattr(shape, "GetModel") and shape.GetModel() == model:
|
||||
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:
|
||||
shape.ResetControlPoints()
|
||||
self._canvas.Refresh()
|
||||
break
|
||||
|
||||
|
||||
def GetShape(self, model):
|
||||
for shape in self._diagram.GetShapeList():
|
||||
if hasattr(shape, "GetModel") and shape.GetModel() == model:
|
||||
return shape
|
||||
return None
|
||||
|
||||
|
||||
def GetSelection(self):
|
||||
return filter(lambda shape: shape.Selected(), self._diagram.GetShapeList())
|
||||
|
||||
|
||||
def SetSelection(self, models, extendSelect = False):
|
||||
dc = wx.ClientDC(self._canvas)
|
||||
self._canvas.PrepareDC(dc)
|
||||
update = False
|
||||
if not isinstance(models, type([])) and not isinstance(models, type(())):
|
||||
models = [models]
|
||||
for shape in self._diagram.GetShapeList():
|
||||
if hasattr(shape, "GetModel"):
|
||||
if shape.Selected() and not shape.GetModel() in models: # was selected, but not in new list, so deselect, unless extend select
|
||||
if not extendSelect:
|
||||
shape.Select(False, dc)
|
||||
update = True
|
||||
elif not shape.Selected() and shape.GetModel() in models: # was not selected and in new list, so select
|
||||
shape.Select(True, dc)
|
||||
update = True
|
||||
elif extendSelect and shape.Selected() and shape.GetModel() in models: # was selected, but extend select means to deselect
|
||||
shape.Select(False, dc)
|
||||
update = True
|
||||
if update:
|
||||
self._canvas.Redraw(dc)
|
||||
|
||||
|
||||
def BringToFront(self, shape):
|
||||
if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape):
|
||||
self._diagram.RemoveShape(shape.GetParent())
|
||||
self._diagram.AddShape(shape.GetParent())
|
||||
else:
|
||||
self._diagram.RemoveShape(shape)
|
||||
self._diagram.AddShape(shape)
|
||||
|
||||
|
||||
def SendToBack(self, shape):
|
||||
if shape.GetParent() and isinstance(shape.GetParent(), ogl.CompositeShape):
|
||||
self._diagram.RemoveShape(shape.GetParent())
|
||||
self._diagram.InsertShape(shape.GetParent())
|
||||
else:
|
||||
self._diagram.RemoveShape(shape)
|
||||
self._diagram.InsertShape(shape)
|
||||
|
||||
|
||||
def ScrollVisible(self, shape):
|
||||
xUnit, yUnit = shape._canvas.GetScrollPixelsPerUnit()
|
||||
scrollX, scrollY = self._canvas.GetViewStart() # in scroll units
|
||||
scrollW, scrollH = self._canvas.GetSize() # in pixels
|
||||
w, h = shape.GetBoundingBoxMax() # in pixels
|
||||
x = shape.GetX() - w/2 # convert to upper left coordinate from center
|
||||
y = shape.GetY() - h/2 # convert to upper left coordinate from center
|
||||
|
||||
if x >= scrollX*xUnit and x <= scrollX*xUnit + scrollW: # don't scroll if already visible
|
||||
x = -1
|
||||
else:
|
||||
x = x/xUnit
|
||||
|
||||
if y >= scrollY*yUnit and y <= scrollY*yUnit + scrollH: # don't scroll if already visible
|
||||
y = -1
|
||||
else:
|
||||
y = y/yUnit
|
||||
|
||||
self._canvas.Scroll(x, y) # in scroll units
|
||||
|
||||
|
||||
def SetPropertyShape(self, shape):
|
||||
# no need to highlight if no PropertyService is running
|
||||
propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
|
||||
if not propertyService:
|
||||
return
|
||||
|
||||
if shape == self._propShape:
|
||||
return
|
||||
|
||||
if hasattr(shape, "GetPropertyShape"):
|
||||
shape = shape.GetPropertyShape()
|
||||
|
||||
dc = wx.ClientDC(self._canvas)
|
||||
self._canvas.PrepareDC(dc)
|
||||
dc.BeginDrawing()
|
||||
|
||||
# erase old selection if it still exists
|
||||
if self._propShape and self._propShape in self._diagram.GetShapeList():
|
||||
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)
|
||||
|
||||
# set new selection
|
||||
self._propShape = shape
|
||||
|
||||
# draw new selection
|
||||
if self._propShape and self._propShape in self._diagram.GetShapeList():
|
||||
self._propShape.SetBrush(SELECT_BRUSH)
|
||||
if (self._propShape._textColourName in ["BLACK", "WHITE"]): # Would use GetTextColour() but it is broken
|
||||
self._propShape.SetTextColour("WHITE", 0)
|
||||
self._propShape.Draw(dc)
|
||||
|
||||
dc.EndDrawing()
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Property Service methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def GetPropertyModel(self):
|
||||
if hasattr(self, "_propModel"):
|
||||
return self._propModel
|
||||
return None
|
||||
|
||||
|
||||
def SetPropertyModel(self, model):
|
||||
# no need to set the model if no PropertyService is running
|
||||
propertyService = wx.GetApp().GetService(PropertyService.PropertyService)
|
||||
if not propertyService:
|
||||
return
|
||||
|
||||
if hasattr(self, "_propModel") and model == self._propModel:
|
||||
return
|
||||
|
||||
self._propModel = model
|
||||
propertyService.LoadProperties(self._propModel, self.GetDocument())
|
||||
|
||||
|
||||
class EditorCanvasShapeMixin:
|
||||
|
||||
def GetModel(self):
|
||||
return self._model
|
||||
|
||||
|
||||
def SetModel(self, model):
|
||||
self._model = model
|
||||
|
||||
|
||||
class EditorCanvasShapeEvtHandler(ogl.ShapeEvtHandler):
|
||||
|
||||
""" wxBug: Bug in OLG package. With wxShape.SetShadowMode() turned on, when we set the size,
|
||||
the width/height is larger by 6 pixels. Need to subtract this value from width and height when we
|
||||
resize the object.
|
||||
"""
|
||||
SHIFT_KEY = 1
|
||||
CONTROL_KEY = 2
|
||||
|
||||
def __init__(self, view):
|
||||
ogl.ShapeEvtHandler.__init__(self)
|
||||
self._view = view
|
||||
|
||||
|
||||
def OnLeftClick(self, x, y, keys = 0, attachment = 0):
|
||||
shape = self.GetShape()
|
||||
if hasattr(shape, "GetModel"): # Workaround, on drag, we should deselect all other objects and select the clicked on object
|
||||
model = shape.GetModel()
|
||||
else:
|
||||
shape = shape.GetParent()
|
||||
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)
|
||||
|
||||
|
||||
def OnEndDragLeft(self, x, y, keys = 0, attachment = 0):
|
||||
ogl.ShapeEvtHandler.OnEndDragLeft(self, x, y, keys, attachment)
|
||||
shape = self.GetShape()
|
||||
if hasattr(shape, "GetModel"): # Workaround, on drag, we should deselect all other objects and select the clicked on object
|
||||
model = shape.GetModel()
|
||||
else:
|
||||
parentShape = shape.GetParent()
|
||||
if parentShape:
|
||||
model = parentShape.GetModel()
|
||||
self._view.SetSelection(model, keys == self.SHIFT_KEY or keys == self.CONTROL_KEY)
|
||||
|
||||
|
||||
def OnMovePost(self, dc, x, y, oldX, oldY, display):
|
||||
if x == oldX and y == oldY:
|
||||
return
|
||||
if not self._view.GetDocument():
|
||||
return
|
||||
shape = self.GetShape()
|
||||
if isinstance(shape, EditorCanvasShapeMixin) and shape.Draggable():
|
||||
model = shape.GetModel()
|
||||
if hasattr(model, "getEditorBounds") and model.getEditorBounds():
|
||||
x, y, w, h = model.getEditorBounds()
|
||||
newX = shape.GetX() - shape.GetBoundingBoxMax()[0] / 2
|
||||
newY = shape.GetY() - shape.GetBoundingBoxMax()[1] / 2
|
||||
newWidth = shape.GetBoundingBoxMax()[0]
|
||||
newHeight = shape.GetBoundingBoxMax()[1]
|
||||
if shape._shadowMode != ogl.SHADOW_NONE:
|
||||
newWidth -= shape._shadowOffsetX
|
||||
newHeight -= shape._shadowOffsetY
|
||||
newbounds = (newX, newY, newWidth, newHeight)
|
||||
|
||||
if x != newX or y != newY or w != newWidth or h != newHeight:
|
||||
self._view.GetDocument().GetCommandProcessor().Submit(EditorCanvasUpdateShapeBoundariesCommand(self._view.GetDocument(), model, newbounds))
|
||||
|
||||
|
||||
def Draw(self, dc):
|
||||
pass
|
||||
|
||||
|
||||
class EditorCanvasUpdateShapeBoundariesCommand(wx.lib.docview.Command):
|
||||
|
||||
|
||||
def __init__(self, canvasDocument, model, newbounds):
|
||||
wx.lib.docview.Command.__init__(self, canUndo = True)
|
||||
self._canvasDocument = canvasDocument
|
||||
self._model = model
|
||||
self._oldbounds = model.getEditorBounds()
|
||||
self._newbounds = newbounds
|
||||
|
||||
|
||||
def GetName(self):
|
||||
name = self._canvasDocument.GetNameForObject(self._model)
|
||||
if not name:
|
||||
name = ""
|
||||
print "ERROR: AbstractEditor.EditorCanvasUpdateShapeBoundariesCommand.GetName: unable to get name for ", self._model
|
||||
return _("Move/Resize %s") % name
|
||||
|
||||
|
||||
def Do(self):
|
||||
return self._canvasDocument.UpdateEditorBoundaries(self._model, self._newbounds)
|
||||
|
||||
|
||||
def Undo(self):
|
||||
return self._canvasDocument.UpdateEditorBoundaries(self._model, self._oldbounds)
|
||||
|
1014
wxPython/samples/ide/activegrid/tool/CodeEditor.py
Normal file
1014
wxPython/samples/ide/activegrid/tool/CodeEditor.py
Normal file
File diff suppressed because it is too large
Load Diff
690
wxPython/samples/ide/activegrid/tool/DebuggerHarness.py
Normal file
690
wxPython/samples/ide/activegrid/tool/DebuggerHarness.py
Normal file
@@ -0,0 +1,690 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: DebuggerHarness.py
|
||||
# Purpose:
|
||||
#
|
||||
# Author: Matt Fryer
|
||||
#
|
||||
# Created: 7/28/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
import bdb
|
||||
import sys
|
||||
import SimpleXMLRPCServer
|
||||
import threading
|
||||
import xmlrpclib
|
||||
import os
|
||||
import types
|
||||
import Queue
|
||||
import traceback
|
||||
import inspect
|
||||
from xml.dom.minidom import getDOMImplementation
|
||||
import atexit
|
||||
import pickle
|
||||
|
||||
if sys.platform.startswith("win"):
|
||||
import win32api
|
||||
_WINDOWS = True
|
||||
else:
|
||||
_WINDOWS = False
|
||||
|
||||
_VERBOSE = False
|
||||
_DEBUG_DEBUGGER = False
|
||||
|
||||
class Adb(bdb.Bdb):
|
||||
|
||||
def __init__(self, harness, queue):
|
||||
bdb.Bdb.__init__(self)
|
||||
self._harness = harness
|
||||
self._userBreak = False
|
||||
self._queue = queue
|
||||
self._knownCantExpandFiles = {}
|
||||
self._knownExpandedFiles = {}
|
||||
|
||||
def getLongName(self, filename):
|
||||
if not _WINDOWS:
|
||||
return filename
|
||||
if self._knownCantExpandFiles.get(filename):
|
||||
return filename
|
||||
if self._knownExpandedFiles.get(filename):
|
||||
return self._knownExpandedFiles.get(filename)
|
||||
try:
|
||||
newname = win32api.GetLongPathName(filename)
|
||||
self._knownExpandedFiles[filename] = newname
|
||||
return newname
|
||||
except:
|
||||
self._knownCantExpandFiles[filename] = filename
|
||||
return filename
|
||||
|
||||
def canonic(self, orig_filename):
|
||||
if orig_filename == "<" + orig_filename[1:-1] + ">":
|
||||
return orig_filename
|
||||
filename = self.getLongName(orig_filename)
|
||||
|
||||
canonic = self.fncache.get(filename)
|
||||
if not canonic:
|
||||
canonic = os.path.abspath(filename)
|
||||
canonic = os.path.normcase(canonic)
|
||||
self.fncache[filename] = canonic
|
||||
return canonic
|
||||
|
||||
|
||||
# Overriding this so that we continue to trace even if no breakpoints are set.
|
||||
def set_continue(self):
|
||||
self.stopframe = self.botframe
|
||||
self.returnframe = None
|
||||
self.quitting = 0
|
||||
|
||||
def do_clear(self, arg):
|
||||
bdb.Breakpoint.bpbynumber[int(arg)].deleteMe()
|
||||
|
||||
def user_line(self, frame):
|
||||
if self.in_debugger_code(frame):
|
||||
self.set_step()
|
||||
return
|
||||
message = self.__frame2message(frame)
|
||||
self._harness.interaction(message, frame, "")
|
||||
|
||||
def user_call(self, frame, argument_list):
|
||||
if self.in_debugger_code(frame):
|
||||
self.set_step()
|
||||
return
|
||||
if self.stop_here(frame):
|
||||
message = self.__frame2message(frame)
|
||||
self._harness.interaction(message, frame, "")
|
||||
|
||||
def user_return(self, frame, return_value):
|
||||
if self.in_debugger_code(frame):
|
||||
self.set_step()
|
||||
return
|
||||
message = self.__frame2message(frame)
|
||||
self._harness.interaction(message, frame, "")
|
||||
|
||||
def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
|
||||
frame.f_locals['__exception__'] = exc_type, exc_value
|
||||
if type(exc_type) == type(''):
|
||||
exc_type_name = exc_type
|
||||
else:
|
||||
exc_type_name = exc_type.__name__
|
||||
message = "Exception occured: " + repr(exc_type_name) + " See locals.__exception__ for details."
|
||||
traceback.print_exception(exc_type, exc_value, exc_traceback)
|
||||
self._harness.interaction(message, frame, message)
|
||||
|
||||
def in_debugger_code(self, frame):
|
||||
if _DEBUG_DEBUGGER: return False
|
||||
message = self.__frame2message(frame)
|
||||
return message.count('DebuggerHarness') > 0
|
||||
|
||||
def frame2message(self, frame):
|
||||
return self.__frame2message(frame)
|
||||
|
||||
def __frame2message(self, frame):
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
lineno = frame.f_lineno
|
||||
basename = os.path.basename(filename)
|
||||
message = "%s:%s" % (basename, lineno)
|
||||
if code.co_name != "?":
|
||||
message = "%s: %s()" % (message, code.co_name)
|
||||
return message
|
||||
|
||||
def runFile(self, fileName):
|
||||
self.reset()
|
||||
#global_dict = {}
|
||||
#global_dict['__name__'] = '__main__'
|
||||
try:
|
||||
fileToRun = open(fileName, mode='r')
|
||||
if _VERBOSE: print "Running file ", fileName
|
||||
sys.settrace(self.trace_dispatch)
|
||||
import __main__
|
||||
exec fileToRun in __main__.__dict__,__main__.__dict__
|
||||
except SystemExit:
|
||||
pass
|
||||
except:
|
||||
tp, val, tb = sys.exc_info()
|
||||
traceback.print_exception(tp, val, tb)
|
||||
|
||||
sys.settrace(None)
|
||||
self.quitting = 1
|
||||
#global_dict.clear()
|
||||
|
||||
def trace_dispatch(self, frame, event, arg):
|
||||
if self.quitting:
|
||||
return # None
|
||||
# Check for ui events
|
||||
self.readQueue()
|
||||
if event == 'line':
|
||||
return self.dispatch_line(frame)
|
||||
if event == 'call':
|
||||
return self.dispatch_call(frame, arg)
|
||||
if event == 'return':
|
||||
return self.dispatch_return(frame, arg)
|
||||
if event == 'exception':
|
||||
return self.dispatch_exception(frame, arg)
|
||||
print 'Adb.dispatch: unknown debugging event:', `event`
|
||||
return self.trace_dispatch
|
||||
|
||||
def readQueue(self):
|
||||
while self._queue.qsize():
|
||||
try:
|
||||
item = self._queue.get_nowait()
|
||||
if item.kill():
|
||||
self._harness.do_exit(kill=True)
|
||||
elif item.breakHere():
|
||||
self._userBreak = True
|
||||
elif item.hasBreakpoints():
|
||||
self.set_all_breakpoints(item.getBreakpoints())
|
||||
except Queue.Empty:
|
||||
pass
|
||||
|
||||
def set_all_breakpoints(self, dict):
|
||||
self.clear_all_breaks()
|
||||
for fileName in dict.keys():
|
||||
lineList = dict[fileName]
|
||||
for lineNumber in lineList:
|
||||
|
||||
if _VERBOSE: print "Setting break at line ", str(lineNumber), " in file ", self.canonic(fileName)
|
||||
self.set_break(fileName, int(lineNumber))
|
||||
return ""
|
||||
|
||||
def stop_here(self, frame):
|
||||
if( self._userBreak ):
|
||||
return True
|
||||
|
||||
|
||||
# (CT) stopframe may now also be None, see dispatch_call.
|
||||
# (CT) the former test for None is therefore removed from here.
|
||||
if frame is self.stopframe:
|
||||
return True
|
||||
while frame is not None and frame is not self.stopframe:
|
||||
if frame is self.botframe:
|
||||
return True
|
||||
frame = frame.f_back
|
||||
return False
|
||||
|
||||
class BreakNotify(object):
|
||||
def __init__(self, bps=None, break_here=False, kill=False):
|
||||
self._bps = bps
|
||||
self._break_here = break_here
|
||||
self._kill = kill
|
||||
|
||||
def breakHere(self):
|
||||
return self._break_here
|
||||
|
||||
def kill(self):
|
||||
return self._kill
|
||||
|
||||
def getBreakpoints(self):
|
||||
return self._bps
|
||||
|
||||
def hasBreakpoints(self):
|
||||
return (self._bps != None)
|
||||
|
||||
class AGXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
|
||||
def __init__(self, address, logRequests=0):
|
||||
SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, address, logRequests=logRequests)
|
||||
|
||||
class BreakListenerThread(threading.Thread):
|
||||
def __init__(self, host, port, queue):
|
||||
threading.Thread.__init__(self)
|
||||
self._host = host
|
||||
self._port = int(port)
|
||||
self._keepGoing = True
|
||||
self._queue = queue
|
||||
self._server = AGXMLRPCServer((self._host, self._port), logRequests=0)
|
||||
self._server.register_function(self.update_breakpoints)
|
||||
self._server.register_function(self.break_requested)
|
||||
self._server.register_function(self.die)
|
||||
|
||||
def break_requested(self):
|
||||
bn = BreakNotify(break_here=True)
|
||||
self._queue.put(bn)
|
||||
return ""
|
||||
|
||||
def update_breakpoints(self, pickled_Binary_bpts):
|
||||
dict = pickle.loads(pickled_Binary_bpts.data)
|
||||
bn = BreakNotify(bps=dict)
|
||||
self._queue.put(bn)
|
||||
return ""
|
||||
|
||||
def die(self):
|
||||
bn = BreakNotify(kill=True)
|
||||
self._queue.put(bn)
|
||||
return ""
|
||||
|
||||
def run(self):
|
||||
while self._keepGoing:
|
||||
try:
|
||||
self._server.handle_request()
|
||||
except:
|
||||
if _VERBOSE:
|
||||
tp, val, tb = sys.exc_info()
|
||||
print "Exception in BreakListenerThread.run():", str(tp), str(val)
|
||||
self._keepGoing = False
|
||||
|
||||
def AskToStop(self):
|
||||
self._keepGoing = False
|
||||
if type(self._server) is not types.NoneType:
|
||||
if _VERBOSE: print "Before calling server close on breakpoint server"
|
||||
self._server.server_close()
|
||||
if _VERBOSE: print "Calling server close on breakpoint server"
|
||||
|
||||
|
||||
class DebuggerHarness(object):
|
||||
|
||||
def __init__(self):
|
||||
# Host and port for debugger-side RPC server
|
||||
self._hostname = sys.argv[1]
|
||||
self._portNumber = int(sys.argv[2])
|
||||
# Name the gui proxy object is registered under
|
||||
self._breakPortNumber = int(sys.argv[3])
|
||||
# Host and port where the gui proxy can be found.
|
||||
self._guiHost = sys.argv[4]
|
||||
self._guiPort = int(sys.argv[5])
|
||||
# Command to debug.
|
||||
self._command = sys.argv[6]
|
||||
# Strip out the harness' arguments so that the process we run will see argv as if
|
||||
# it was called directly.
|
||||
sys.argv = sys.argv[6:]
|
||||
self._currentFrame = None
|
||||
self._wait = False
|
||||
# Connect to the gui-side RPC server.
|
||||
self._guiServerUrl = 'http://' + self._guiHost + ':' + str(self._guiPort) + '/'
|
||||
if _VERBOSE: print "Connecting to gui server at ", self._guiServerUrl
|
||||
self._guiServer = xmlrpclib.ServerProxy(self._guiServerUrl,allow_none=1)
|
||||
|
||||
# Start the break listener
|
||||
self._breakQueue = Queue.Queue(50)
|
||||
self._breakListener = BreakListenerThread(self._hostname, self._breakPortNumber, self._breakQueue)
|
||||
self._breakListener.start()
|
||||
# Create the debugger.
|
||||
self._adb = Adb(self, self._breakQueue)
|
||||
|
||||
# Create the debugger-side RPC Server and register functions for remote calls.
|
||||
self._server = AGXMLRPCServer((self._hostname, self._portNumber), logRequests=0)
|
||||
self._server.register_function(self.set_step)
|
||||
self._server.register_function(self.set_continue)
|
||||
self._server.register_function(self.set_next)
|
||||
self._server.register_function(self.set_return)
|
||||
self._server.register_function(self.set_breakpoint)
|
||||
self._server.register_function(self.clear_breakpoint)
|
||||
self._server.register_function(self.set_all_breakpoints)
|
||||
self._server.register_function(self.attempt_introspection)
|
||||
self._server.register_function(self.add_watch)
|
||||
|
||||
self.message_frame_dict = {}
|
||||
self.introspection_list = []
|
||||
atexit.register(self.do_exit)
|
||||
|
||||
def run(self):
|
||||
self._adb.runFile(self._command)
|
||||
self.do_exit(kill=True)
|
||||
|
||||
|
||||
def do_exit(self, kill=False):
|
||||
self._adb.set_quit()
|
||||
self._breakListener.AskToStop()
|
||||
self._server.server_close()
|
||||
try:
|
||||
self._guiServer.quit()
|
||||
except:
|
||||
pass
|
||||
if kill:
|
||||
try:
|
||||
sys.exit()
|
||||
except:
|
||||
pass
|
||||
|
||||
def set_breakpoint(self, fileName, lineNo):
|
||||
self._adb.set_break(fileName, lineNo)
|
||||
return ""
|
||||
|
||||
def set_all_breakpoints(self, dict):
|
||||
self._adb.clear_all_breaks()
|
||||
for fileName in dict.keys():
|
||||
lineList = dict[fileName]
|
||||
for lineNumber in lineList:
|
||||
self._adb.set_break(fileName, int(lineNumber))
|
||||
if _VERBOSE: print "Setting break at ", str(lineNumber), " in file ", fileName
|
||||
return ""
|
||||
|
||||
def clear_breakpoint(self, fileName, lineNo):
|
||||
self._adb.clear_break(fileName, lineNo)
|
||||
return ""
|
||||
|
||||
def add_watch(self, name, text, frame_message, run_once):
|
||||
if len(frame_message) > 0:
|
||||
frame = self.message_frame_dict[frame_message]
|
||||
try:
|
||||
item = eval(text, frame.f_globals, frame.f_locals)
|
||||
return self.get_watch_document(item, name)
|
||||
except:
|
||||
tp, val, tb = sys.exc_info()
|
||||
return self.get_exception_document(tp, val, tb)
|
||||
return ""
|
||||
|
||||
def attempt_introspection(self, frame_message, chain):
|
||||
try:
|
||||
frame = self.message_frame_dict[frame_message]
|
||||
if frame:
|
||||
name = chain.pop(0)
|
||||
if name == 'globals':
|
||||
item = frame.f_globals
|
||||
elif name == 'locals':
|
||||
item = frame.f_locals
|
||||
|
||||
for name in chain:
|
||||
item = self.getNextItem(item, name)
|
||||
return self.get_introspection_document(item, name)
|
||||
except:
|
||||
tp, val, tb = sys.exc_info()
|
||||
traceback.print_exception(tp, val, tb)
|
||||
return self.get_empty_introspection_document()
|
||||
|
||||
def getNextItem(self, link, identifier):
|
||||
tp = type(link)
|
||||
if self.isTupleized(identifier):
|
||||
return self.deTupleize(link, identifier)
|
||||
else:
|
||||
if tp == types.DictType or tp == types.DictProxyType:
|
||||
return link[identifier]
|
||||
else:
|
||||
if hasattr(link, identifier):
|
||||
return getattr(link, identifier)
|
||||
if _VERBOSE or True: print "Failed to find link ", identifier, " on thing: ", self.saferepr(link), " of type ", repr(type(link))
|
||||
return None
|
||||
|
||||
def isPrimitive(self, item):
|
||||
tp = type(item)
|
||||
return tp is types.IntType or tp is types.LongType or tp is types.FloatType \
|
||||
or tp is types.BooleanType or tp is types.ComplexType \
|
||||
or tp is types.StringType
|
||||
|
||||
def isTupleized(self, value):
|
||||
return value.count('[')
|
||||
|
||||
def deTupleize(self, link, string1):
|
||||
try:
|
||||
start = string1.find('[')
|
||||
end = string1.find(']')
|
||||
num = int(string1[start+1:end])
|
||||
return link[num]
|
||||
except:
|
||||
tp,val,tb = sys.exc_info()
|
||||
if _VERBOSE: print "Got exception in deTupleize: ", val
|
||||
return None
|
||||
|
||||
def wrapAndCompress(self, stringDoc):
|
||||
import bz2
|
||||
return xmlrpclib.Binary(bz2.compress(stringDoc))
|
||||
|
||||
def get_empty_introspection_document(self):
|
||||
doc = getDOMImplementation().createDocument(None, "replacement", None)
|
||||
return self.wrapAndCompress(doc.toxml())
|
||||
|
||||
def get_watch_document(self, item, identifier):
|
||||
doc = getDOMImplementation().createDocument(None, "watch", None)
|
||||
top_element = doc.documentElement
|
||||
self.addAny(top_element, identifier, item, doc, 2)
|
||||
return self.wrapAndCompress(doc.toxml())
|
||||
|
||||
def get_introspection_document(self, item, identifier):
|
||||
doc = getDOMImplementation().createDocument(None, "replacement", None)
|
||||
top_element = doc.documentElement
|
||||
self.addAny(top_element, identifier, item, doc, 2)
|
||||
return self.wrapAndCompress(doc.toxml())
|
||||
|
||||
def get_exception_document(self, name, tp, val, tb):
|
||||
stack = traceback.format_exception(tp, val, tb)
|
||||
wholeStack = ""
|
||||
for line in stack:
|
||||
wholeStack += line
|
||||
doc = getDOMImplementation().createDocument(None, "watch", None)
|
||||
top_element = doc.documentElement
|
||||
item_node = doc.createElement("dict_nv_element")
|
||||
item_node.setAttribute('value', wholeStack)
|
||||
item_node.setAttribute('name', str(name))
|
||||
top_element.appendChild(item_node)
|
||||
|
||||
def addAny(self, top_element, name, item, doc, ply):
|
||||
tp = type(item)
|
||||
if ply < 1:
|
||||
self.addNode(top_element,name, self.saferepr(item), doc)
|
||||
elif tp is types.TupleType or tp is types.ListType:
|
||||
self.addTupleOrList(top_element, name, item, doc, ply - 1)
|
||||
elif tp is types.DictType or tp is types.DictProxyType:
|
||||
self.addDict(top_element, name, item, doc, ply -1)
|
||||
elif inspect.ismodule(item):
|
||||
self.addModule(top_element, name, item, doc, ply -1)
|
||||
elif inspect.isclass(item) or tp is types.InstanceType:
|
||||
self.addClass(top_element, name, item, doc, ply -1)
|
||||
#elif hasattr(item, '__dict__'):
|
||||
# self.addDictAttr(top_element, name, item, doc, ply -1)
|
||||
elif hasattr(item, '__dict__'):
|
||||
self.addDict(top_element, name, item.__dict__, doc, ply -1)
|
||||
else:
|
||||
self.addNode(top_element,name, self.saferepr(item), doc)
|
||||
|
||||
def addTupleOrList(self, top_node, name, tupple, doc, ply):
|
||||
tupleNode = doc.createElement('tuple')
|
||||
tupleNode.setAttribute('name', str(name))
|
||||
tupleNode.setAttribute('value', str(type(tupple)))
|
||||
top_node.appendChild(tupleNode)
|
||||
count = 0
|
||||
for item in tupple:
|
||||
self.addAny(tupleNode, name +'[' + str(count) + ']',item, doc, ply -1)
|
||||
count += 1
|
||||
|
||||
|
||||
def getFrameXML(self, base_frame):
|
||||
doc = getDOMImplementation().createDocument(None, "stack", None)
|
||||
top_element = doc.documentElement
|
||||
|
||||
stack = []
|
||||
frame = base_frame
|
||||
while frame is not None:
|
||||
if((frame.f_code.co_filename.count('DebuggerHarness.py') == 0) or _DEBUG_DEBUGGER):
|
||||
stack.append(frame)
|
||||
frame = frame.f_back
|
||||
stack.reverse()
|
||||
self.message_frame_dict = {}
|
||||
for f in stack:
|
||||
self.addFrame(f,top_element, doc)
|
||||
return doc.toxml()
|
||||
|
||||
def addFrame(self, frame, root_element, document):
|
||||
frameNode = document.createElement('frame')
|
||||
root_element.appendChild(frameNode)
|
||||
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
frameNode.setAttribute('file', str(filename))
|
||||
frameNode.setAttribute('line', str(frame.f_lineno))
|
||||
message = self._adb.frame2message(frame)
|
||||
frameNode.setAttribute('message', message)
|
||||
#print "Frame: %s %s %s" %(message, frame.f_lineno, filename)
|
||||
self.message_frame_dict[message] = frame
|
||||
self.addDict(frameNode, "locals", frame.f_locals, document, 2)
|
||||
self.addNode(frameNode, "globals", "", document)
|
||||
|
||||
def getRepr(self, varName, globals, locals):
|
||||
try:
|
||||
return repr(eval(varName, globals, locals))
|
||||
except:
|
||||
return 'Error: Could not recover value.'
|
||||
|
||||
def addNode(self, parent_node, name, value, document):
|
||||
item_node = document.createElement("dict_nv_element")
|
||||
item_node.setAttribute('value', self.saferepr(value))
|
||||
item_node.setAttribute('name', str(name))
|
||||
parent_node.appendChild(item_node)
|
||||
|
||||
def addDictAttr(self, root_node, name, thing, document, ply):
|
||||
dict_node = document.createElement('thing')
|
||||
root_node.setAttribute('name', name)
|
||||
root_node.setAttribute('value', str(type(dict)) + " add attr")
|
||||
self.addDict(root_node, name, thing.__dict__, document, ply) # Not decreminting ply
|
||||
|
||||
def saferepr(self, thing):
|
||||
try:
|
||||
return repr(thing)
|
||||
except:
|
||||
tp, val, tb = sys.exc_info()
|
||||
return repr(val)
|
||||
|
||||
def addDict(self, root_node, name, dict, document, ply):
|
||||
dict_node = document.createElement('dict')
|
||||
dict_node.setAttribute('name', name)
|
||||
dict_node.setAttribute('value', str(type(dict)) + " add dict")
|
||||
root_node.appendChild(dict_node)
|
||||
for key in dict.keys():
|
||||
strkey = str(key)
|
||||
try:
|
||||
self.addAny(dict_node, strkey, dict[key], document, ply-1)
|
||||
except:
|
||||
tp,val,tb=sys.exc_info()
|
||||
if _VERBOSE:
|
||||
print "Error recovering key: ", str(key), " from node ", str(name), " Val = ", str(val)
|
||||
traceback.print_exception(tp, val, tb)
|
||||
self.addAny(dict_node, strkey, "Exception getting " + str(name) + "[" + strkey + "]: " + str(val), document, ply -1)
|
||||
|
||||
def addClass(self, root_node, name, class_item, document, ply):
|
||||
item_node = document.createElement('class')
|
||||
item_node.setAttribute('name', str(name))
|
||||
root_node.appendChild(item_node)
|
||||
try:
|
||||
if hasattr(class_item, '__dict__'):
|
||||
self.addAny(item_node, '__dict__', class_item.__dict__, document, ply -1)
|
||||
except:
|
||||
tp,val,tb=sys.exc_info()
|
||||
if _VERBOSE:
|
||||
traceback.print_exception(tp, val, tb)
|
||||
self.addAny(item_node, '__dict__', "Exception getting __dict__: " + str(val), document, ply -1)
|
||||
try:
|
||||
if hasattr(class_item, '__name__'):
|
||||
self.addAny(item_node,'__name__',class_item.__name__, document, ply -1)
|
||||
except:
|
||||
tp,val,tb=sys.exc_info()
|
||||
if _VERBOSE:
|
||||
traceback.print_exception(tp, val, tb)
|
||||
self.addAny(item_node,'__name__',"Exception getting class.__name__: " + val, document, ply -1)
|
||||
try:
|
||||
if hasattr(class_item, '__module__'):
|
||||
self.addAny(item_node, '__module__', class_item.__module__, document, ply -1)
|
||||
except:
|
||||
tp,val,tb=sys.exc_info()
|
||||
if _VERBOSE:
|
||||
traceback.print_exception(tp, val, tb)
|
||||
self.addAny(item_node, '__module__', "Exception getting class.__module__: " + val, document, ply -1)
|
||||
try:
|
||||
if hasattr(class_item, '__doc__'):
|
||||
self.addAny(item_node, '__doc__', class_item.__doc__, document, ply -1)
|
||||
except:
|
||||
tp,val,tb=sys.exc_info()
|
||||
if _VERBOSE:
|
||||
traceback.print_exception(tp, val, tb)
|
||||
self.addAny(item_node, '__doc__', "Exception getting class.__doc__: " + val, document, ply -1)
|
||||
try:
|
||||
if hasattr(class_item, '__bases__'):
|
||||
self.addAny(item_node, '__bases__', class_item.__bases__, document, ply -1)
|
||||
except:
|
||||
tp,val,tb=sys.exc_info()
|
||||
if _VERBOSE:
|
||||
traceback.print_exception(tp, val, tb)
|
||||
self.addAny(item_node, '__bases__', "Exception getting class.__bases__: " + val, document, ply -1)
|
||||
|
||||
def addModule(self, root_node, name, module_item, document, ply):
|
||||
item_node = document.createElement('module')
|
||||
item_node.setAttribute('name', str(name))
|
||||
root_node.appendChild(item_node)
|
||||
try:
|
||||
if hasattr(module_item, '__file__'):
|
||||
self.addAny(item_node, '__file__', module_item.__file__, document, ply -1)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
if hasattr(module_item, '__doc__'):
|
||||
self.addAny(item_node,'__doc__', module_item.__doc__, document, ply -1)
|
||||
except:
|
||||
pass
|
||||
|
||||
# The debugger calls this method when it reaches a breakpoint.
|
||||
def interaction(self, message, frame, info):
|
||||
if _VERBOSE:
|
||||
print 'hit debug side interaction'
|
||||
self._userBreak = False
|
||||
|
||||
self._currentFrame = frame
|
||||
done = False
|
||||
while not done:
|
||||
try:
|
||||
import bz2
|
||||
xml = self.getFrameXML(frame)
|
||||
arg = xmlrpclib.Binary(bz2.compress(xml))
|
||||
if _VERBOSE:
|
||||
print '============== calling gui side interaction============'
|
||||
self._guiServer.interaction(xmlrpclib.Binary(message), arg, info)
|
||||
if _VERBOSE:
|
||||
print 'after interaction'
|
||||
done = True
|
||||
except:
|
||||
tp, val, tb = sys.exc_info()
|
||||
if True or _VERBOSE:
|
||||
print 'Error contacting GUI server!: '
|
||||
try:
|
||||
traceback.print_exception(tp, val, tb)
|
||||
except:
|
||||
print "Exception printing traceback",
|
||||
tp, val, tb = sys.exc_info()
|
||||
traceback.print_exception(tp, val, tb)
|
||||
done = False
|
||||
# Block while waiting to be called back from the GUI. Eventually, self._wait will
|
||||
# be set false by a function on this side. Seems pretty lame--I'm surprised it works.
|
||||
self.waitForRPC()
|
||||
|
||||
|
||||
def waitForRPC(self):
|
||||
self._wait = True
|
||||
while self._wait :
|
||||
try:
|
||||
if _VERBOSE:
|
||||
print "+++ in harness wait for rpc, before handle_request"
|
||||
self._server.handle_request()
|
||||
if _VERBOSE:
|
||||
print "+++ in harness wait for rpc, after handle_request"
|
||||
except:
|
||||
if _VERBOSE:
|
||||
tp, val, tb = sys.exc_info()
|
||||
print "Got waitForRpc exception : ", repr(tp), ": ", val
|
||||
#time.sleep(0.1)
|
||||
|
||||
def set_step(self):
|
||||
self._adb.set_step()
|
||||
self._wait = False
|
||||
return ""
|
||||
|
||||
def set_continue(self):
|
||||
self._adb.set_continue()
|
||||
self._wait = False
|
||||
return ""
|
||||
|
||||
def set_next(self):
|
||||
self._adb.set_next(self._currentFrame)
|
||||
self._wait = False
|
||||
return ""
|
||||
|
||||
def set_return(self):
|
||||
self._adb.set_return(self._currentFrame)
|
||||
self._wait = False
|
||||
return ""
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
harness = DebuggerHarness()
|
||||
harness.run()
|
||||
except SystemExit:
|
||||
print "Exiting..."
|
||||
except:
|
||||
tp, val, tb = sys.exc_info()
|
||||
traceback.print_exception(tp, val, tb)
|
2275
wxPython/samples/ide/activegrid/tool/DebuggerService.py
Normal file
2275
wxPython/samples/ide/activegrid/tool/DebuggerService.py
Normal file
File diff suppressed because it is too large
Load Diff
446
wxPython/samples/ide/activegrid/tool/FindInDirService.py
Normal file
446
wxPython/samples/ide/activegrid/tool/FindInDirService.py
Normal file
@@ -0,0 +1,446 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: IDEFindService.py
|
||||
# Purpose: Find Service for pydocview
|
||||
#
|
||||
# Author: Morgan Hua
|
||||
#
|
||||
# Created: 8/15/03
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import wx
|
||||
import wx.lib.docview
|
||||
import os
|
||||
from os.path import join
|
||||
import re
|
||||
import ProjectEditor
|
||||
import MessageService
|
||||
import FindService
|
||||
import OutlineService
|
||||
_ = wx.GetTranslation
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Constants
|
||||
#----------------------------------------------------------------------------
|
||||
FILENAME_MARKER = _("Found in file: ")
|
||||
PROJECT_MARKER = _("Searching project: ")
|
||||
FIND_MATCHDIR = "FindMatchDir"
|
||||
FIND_MATCHDIRSUBFOLDERS = "FindMatchDirSubfolders"
|
||||
|
||||
SPACE = 10
|
||||
HALF_SPACE = 5
|
||||
|
||||
|
||||
class FindInDirService(FindService.FindService):
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Constants
|
||||
#----------------------------------------------------------------------------
|
||||
FINDALL_ID = wx.NewId() # for bringing up Find All dialog box
|
||||
FINDDIR_ID = wx.NewId() # for bringing up Find Dir dialog box
|
||||
|
||||
|
||||
def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
|
||||
FindService.FindService.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
|
||||
|
||||
editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
|
||||
wx.EVT_MENU(frame, FindInDirService.FINDALL_ID, self.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, FindInDirService.FINDALL_ID, self.ProcessUpdateUIEvent)
|
||||
editMenu.Append(FindInDirService.FINDALL_ID, _("Find in Project...\tCtrl+Shift+F"), _("Searches for the specified text in all the files in the project"))
|
||||
wx.EVT_MENU(frame, FindInDirService.FINDDIR_ID, self.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, FindInDirService.FINDDIR_ID, self.ProcessUpdateUIEvent)
|
||||
editMenu.Append(FindInDirService.FINDDIR_ID, _("Find in Directory..."), _("Searches for the specified text in all the files in the directory"))
|
||||
|
||||
|
||||
def ProcessEvent(self, event):
|
||||
id = event.GetId()
|
||||
if id == FindInDirService.FINDALL_ID:
|
||||
self.ShowFindAllDialog()
|
||||
return True
|
||||
elif id == FindInDirService.FINDDIR_ID:
|
||||
self.ShowFindDirDialog()
|
||||
return True
|
||||
else:
|
||||
return FindService.FindService.ProcessEvent(self, event)
|
||||
|
||||
|
||||
def ProcessUpdateUIEvent(self, event):
|
||||
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():
|
||||
event.Enable(True)
|
||||
else:
|
||||
event.Enable(False)
|
||||
return True
|
||||
elif id == FindInDirService.FINDDIR_ID:
|
||||
event.Enable(True)
|
||||
else:
|
||||
return FindService.FindService.ProcessUpdateUIEvent(self, event)
|
||||
|
||||
|
||||
def ShowFindDirDialog(self):
|
||||
config = wx.ConfigBase_Get()
|
||||
|
||||
frame = wx.Dialog(None, -1, _("Find in Directory"), size= (320,200))
|
||||
borderSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
contentSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
lineSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
lineSizer.Add(wx.StaticText(frame, -1, _("Directory:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
|
||||
dirCtrl = wx.TextCtrl(frame, -1, config.Read(FIND_MATCHDIR, ""), size=(200,-1))
|
||||
dirCtrl.SetToolTipString(dirCtrl.GetValue())
|
||||
lineSizer.Add(dirCtrl, 0, wx.LEFT, HALF_SPACE)
|
||||
findDirButton = wx.Button(frame, -1, "Browse...")
|
||||
lineSizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
|
||||
contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
|
||||
|
||||
def OnBrowseButton(event):
|
||||
dlg = wx.DirDialog(frame, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
|
||||
dir = dirCtrl.GetValue()
|
||||
if len(dir):
|
||||
dlg.SetPath(dir)
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
dirCtrl.SetValue(dlg.GetPath())
|
||||
dirCtrl.SetToolTipString(dirCtrl.GetValue())
|
||||
dirCtrl.SetInsertionPointEnd()
|
||||
|
||||
dlg.Destroy()
|
||||
wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
|
||||
|
||||
subfolderCtrl = wx.CheckBox(frame, -1, _("Search in subfolders"))
|
||||
subfolderCtrl.SetValue(config.ReadInt(FIND_MATCHDIRSUBFOLDERS, True))
|
||||
contentSizer.Add(subfolderCtrl, 0, wx.BOTTOM, SPACE)
|
||||
|
||||
lineSizer = wx.BoxSizer(wx.VERTICAL) # let the line expand horizontally without vertical expansion
|
||||
lineSizer.Add(wx.StaticLine(frame, -1, size = (10,-1)), 0, flag=wx.EXPAND)
|
||||
contentSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.BOTTOM, border=HALF_SPACE)
|
||||
|
||||
lineSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
|
||||
findCtrl = wx.TextCtrl(frame, -1, config.Read(FindService.FIND_MATCHPATTERN, ""), size=(200,-1))
|
||||
lineSizer.Add(findCtrl, 0, wx.LEFT, HALF_SPACE)
|
||||
contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
|
||||
wholeWordCtrl = wx.CheckBox(frame, -1, _("Match whole word only"))
|
||||
wholeWordCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHWHOLEWORD, False))
|
||||
matchCaseCtrl = wx.CheckBox(frame, -1, _("Match case"))
|
||||
matchCaseCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHCASE, False))
|
||||
regExprCtrl = wx.CheckBox(frame, -1, _("Regular expression"))
|
||||
regExprCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHREGEXPR, False))
|
||||
contentSizer.Add(wholeWordCtrl, 0, wx.BOTTOM, SPACE)
|
||||
contentSizer.Add(matchCaseCtrl, 0, wx.BOTTOM, SPACE)
|
||||
contentSizer.Add(regExprCtrl, 0, wx.BOTTOM, SPACE)
|
||||
borderSizer.Add(contentSizer, 0, wx.TOP | wx.BOTTOM | wx.LEFT, SPACE)
|
||||
|
||||
buttonSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
findBtn = wx.Button(frame, wx.ID_OK, _("Find"))
|
||||
findBtn.SetDefault()
|
||||
buttonSizer.Add(findBtn, 0, wx.BOTTOM, HALF_SPACE)
|
||||
buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL, _("Cancel")), 0)
|
||||
borderSizer.Add(buttonSizer, 0, wx.ALL, SPACE)
|
||||
|
||||
frame.SetSizer(borderSizer)
|
||||
frame.Fit()
|
||||
|
||||
status = frame.ShowModal()
|
||||
|
||||
passedCheck = False
|
||||
while status == wx.ID_OK and not passedCheck:
|
||||
if not os.path.exists(dirCtrl.GetValue()):
|
||||
dlg = wx.MessageDialog(frame,
|
||||
_("'%s' does not exist.") % dirCtrl.GetValue(),
|
||||
_("Find in Directory"),
|
||||
wx.OK | wx.ICON_EXCLAMATION
|
||||
)
|
||||
dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
|
||||
status = frame.ShowModal()
|
||||
elif len(findCtrl.GetValue()) == 0:
|
||||
dlg = wx.MessageDialog(frame,
|
||||
_("'Find what:' cannot be empty."),
|
||||
_("Find in Directory"),
|
||||
wx.OK | wx.ICON_EXCLAMATION
|
||||
)
|
||||
dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
|
||||
status = frame.ShowModal()
|
||||
else:
|
||||
passedCheck = True
|
||||
|
||||
|
||||
# save user choice state for this and other Find Dialog Boxes
|
||||
dirString = dirCtrl.GetValue()
|
||||
searchSubfolders = subfolderCtrl.IsChecked()
|
||||
self.SaveFindDirConfig(dirString, searchSubfolders)
|
||||
|
||||
findString = findCtrl.GetValue()
|
||||
matchCase = matchCaseCtrl.IsChecked()
|
||||
wholeWord = wholeWordCtrl.IsChecked()
|
||||
regExpr = regExprCtrl.IsChecked()
|
||||
self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
|
||||
|
||||
|
||||
if status == wx.ID_OK:
|
||||
frame.Destroy()
|
||||
|
||||
messageService = wx.GetApp().GetService(MessageService.MessageService)
|
||||
messageService.ShowWindow()
|
||||
|
||||
view = messageService.GetView()
|
||||
if view:
|
||||
wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
|
||||
view.ClearLines()
|
||||
view.SetCallback(self.OnJumpToFoundLine)
|
||||
|
||||
view.AddLines(_("Searching for '%s' in '%s'\n\n") % (findString, dirString))
|
||||
|
||||
if os.path.isfile(dirString):
|
||||
try:
|
||||
docFile = file(dirString, 'r')
|
||||
lineNum = 1
|
||||
needToDisplayFilename = True
|
||||
line = docFile.readline()
|
||||
while line:
|
||||
count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
|
||||
if count != -1:
|
||||
if needToDisplayFilename:
|
||||
view.AddLines(FILENAME_MARKER + dirString + "\n")
|
||||
needToDisplayFilename = False
|
||||
line = repr(lineNum).zfill(4) + ":" + line
|
||||
view.AddLines(line)
|
||||
line = docFile.readline()
|
||||
lineNum += 1
|
||||
if not needToDisplayFilename:
|
||||
view.AddLines("\n")
|
||||
except IOError, (code, message):
|
||||
print _("Warning, unable to read file: '%s'. %s") % (dirString, message)
|
||||
else:
|
||||
# do search in files on disk
|
||||
for root, dirs, files in os.walk(dirString):
|
||||
if not searchSubfolders and root != dirString:
|
||||
break
|
||||
|
||||
for name in files:
|
||||
filename = os.path.join(root, name)
|
||||
try:
|
||||
docFile = file(filename, 'r')
|
||||
except IOError, (code, message):
|
||||
print _("Warning, unable to read file: '%s'. %s") % (filename, message)
|
||||
continue
|
||||
|
||||
lineNum = 1
|
||||
needToDisplayFilename = True
|
||||
line = docFile.readline()
|
||||
while line:
|
||||
count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
|
||||
if count != -1:
|
||||
if needToDisplayFilename:
|
||||
view.AddLines(FILENAME_MARKER + filename + "\n")
|
||||
needToDisplayFilename = False
|
||||
line = repr(lineNum).zfill(4) + ":" + line
|
||||
view.AddLines(line)
|
||||
line = docFile.readline()
|
||||
lineNum += 1
|
||||
if not needToDisplayFilename:
|
||||
view.AddLines("\n")
|
||||
|
||||
view.AddLines(_("Search completed."))
|
||||
wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
|
||||
|
||||
return True
|
||||
else:
|
||||
frame.Destroy()
|
||||
return False
|
||||
|
||||
|
||||
def SaveFindDirConfig(self, dirString, searchSubfolders):
|
||||
""" Save search dir patterns and flags to registry.
|
||||
|
||||
dirString = search directory
|
||||
searchSubfolders = Search subfolders
|
||||
"""
|
||||
config = wx.ConfigBase_Get()
|
||||
config.Write(FIND_MATCHDIR, dirString)
|
||||
config.WriteInt(FIND_MATCHDIRSUBFOLDERS, searchSubfolders)
|
||||
|
||||
|
||||
def ShowFindAllDialog(self):
|
||||
config = wx.ConfigBase_Get()
|
||||
|
||||
frame = wx.Dialog(None, -1, _("Find in Project"), size= (320,200))
|
||||
borderSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
contentSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
lineSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
|
||||
findCtrl = wx.TextCtrl(frame, -1, config.Read(FindService.FIND_MATCHPATTERN, ""), size=(200,-1))
|
||||
lineSizer.Add(findCtrl, 0, wx.LEFT, HALF_SPACE)
|
||||
contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
|
||||
wholeWordCtrl = wx.CheckBox(frame, -1, _("Match whole word only"))
|
||||
wholeWordCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHWHOLEWORD, False))
|
||||
matchCaseCtrl = wx.CheckBox(frame, -1, _("Match case"))
|
||||
matchCaseCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHCASE, False))
|
||||
regExprCtrl = wx.CheckBox(frame, -1, _("Regular expression"))
|
||||
regExprCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHREGEXPR, False))
|
||||
contentSizer.Add(wholeWordCtrl, 0, wx.BOTTOM, SPACE)
|
||||
contentSizer.Add(matchCaseCtrl, 0, wx.BOTTOM, SPACE)
|
||||
contentSizer.Add(regExprCtrl, 0, wx.BOTTOM, SPACE)
|
||||
borderSizer.Add(contentSizer, 0, wx.TOP | wx.BOTTOM | wx.LEFT, SPACE)
|
||||
|
||||
buttonSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
findBtn = wx.Button(frame, wx.ID_OK, _("Find"))
|
||||
findBtn.SetDefault()
|
||||
buttonSizer.Add(findBtn, 0, wx.BOTTOM, HALF_SPACE)
|
||||
buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL, _("Cancel")), 0)
|
||||
borderSizer.Add(buttonSizer, 0, wx.ALL, SPACE)
|
||||
|
||||
frame.SetSizer(borderSizer)
|
||||
frame.Fit()
|
||||
|
||||
status = frame.ShowModal()
|
||||
|
||||
# save user choice state for this and other Find Dialog Boxes
|
||||
findString = findCtrl.GetValue()
|
||||
matchCase = matchCaseCtrl.IsChecked()
|
||||
wholeWord = wholeWordCtrl.IsChecked()
|
||||
regExpr = regExprCtrl.IsChecked()
|
||||
self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
|
||||
|
||||
if status == wx.ID_OK:
|
||||
frame.Destroy()
|
||||
|
||||
messageService = wx.GetApp().GetService(MessageService.MessageService)
|
||||
messageService.ShowWindow()
|
||||
|
||||
view = messageService.GetView()
|
||||
if view:
|
||||
view.ClearLines()
|
||||
view.SetCallback(self.OnJumpToFoundLine)
|
||||
|
||||
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
|
||||
projectFilenames = projectService.GetFilesFromCurrentProject()
|
||||
|
||||
projView = projectService.GetView()
|
||||
if projView:
|
||||
projName = wx.lib.docview.FileNameFromPath(projView.GetDocument().GetFilename())
|
||||
view.AddLines(PROJECT_MARKER + projName + "\n\n")
|
||||
|
||||
# do search in open files first, open files may have been modified and different from disk because it hasn't been saved
|
||||
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
|
||||
openDocsInProject = filter(lambda openDoc: openDoc.GetFilename() in projectFilenames, openDocs)
|
||||
for openDoc in openDocsInProject:
|
||||
if isinstance(openDoc, ProjectEditor.ProjectDocument): # don't search project model
|
||||
continue
|
||||
|
||||
openDocView = openDoc.GetFirstView()
|
||||
# some views don't have a in memory text object to search through such as the PM and the DM
|
||||
# even if they do have a non-text searchable object, how do we display it in the message window?
|
||||
if not hasattr(openDocView, "GetValue"):
|
||||
continue
|
||||
text = openDocView.GetValue()
|
||||
|
||||
lineNum = 1
|
||||
needToDisplayFilename = True
|
||||
start = 0
|
||||
end = 0
|
||||
count = 0
|
||||
while count != -1:
|
||||
count, foundStart, foundEnd, newText = self.DoFind(findString, None, text, start, end, True, matchCase, wholeWord, regExpr)
|
||||
if count != -1:
|
||||
if needToDisplayFilename:
|
||||
view.AddLines(FILENAME_MARKER + openDoc.GetFilename() + "\n")
|
||||
needToDisplayFilename = False
|
||||
|
||||
lineNum = openDocView.LineFromPosition(foundStart)
|
||||
line = repr(lineNum).zfill(4) + ":" + openDocView.GetLine(lineNum)
|
||||
view.AddLines(line)
|
||||
|
||||
start = text.find("\n", foundStart)
|
||||
if start == -1:
|
||||
break
|
||||
end = start
|
||||
if not needToDisplayFilename:
|
||||
view.AddLines("\n")
|
||||
openDocNames = map(lambda openDoc: openDoc.GetFilename(), openDocs)
|
||||
|
||||
# do search in closed files, skipping the open ones we already searched
|
||||
filenames = filter(lambda filename: filename not in openDocNames, projectFilenames)
|
||||
for filename in filenames:
|
||||
try:
|
||||
docFile = file(filename, 'r')
|
||||
except IOError, (code, message):
|
||||
print _("Warning, unable to read file: '%s'. %s") % (filename, message)
|
||||
continue
|
||||
|
||||
lineNum = 1
|
||||
needToDisplayFilename = True
|
||||
line = docFile.readline()
|
||||
while line:
|
||||
count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
|
||||
if count != -1:
|
||||
if needToDisplayFilename:
|
||||
view.AddLines(FILENAME_MARKER + filename + "\n")
|
||||
needToDisplayFilename = False
|
||||
line = repr(lineNum).zfill(4) + ":" + line
|
||||
view.AddLines(line)
|
||||
line = docFile.readline()
|
||||
lineNum += 1
|
||||
if not needToDisplayFilename:
|
||||
view.AddLines("\n")
|
||||
|
||||
view.AddLines(_("Search for '%s' completed.") % findString)
|
||||
|
||||
return True
|
||||
else:
|
||||
frame.Destroy()
|
||||
return False
|
||||
|
||||
|
||||
def OnJumpToFoundLine(self, event):
|
||||
messageService = wx.GetApp().GetService(MessageService.MessageService)
|
||||
lineText, pos = messageService.GetView().GetCurrLine()
|
||||
if lineText == "\n" or lineText.find(FILENAME_MARKER) != -1 or lineText.find(PROJECT_MARKER) != -1:
|
||||
return
|
||||
lineEnd = lineText.find(":")
|
||||
if lineEnd == -1:
|
||||
return
|
||||
else:
|
||||
lineNum = int(lineText[0:lineEnd])
|
||||
|
||||
text = messageService.GetView().GetText()
|
||||
curPos = messageService.GetView().GetCurrentPos()
|
||||
|
||||
startPos = text.rfind(FILENAME_MARKER, 0, curPos)
|
||||
endPos = text.find("\n", startPos)
|
||||
filename = text[startPos + len(FILENAME_MARKER):endPos]
|
||||
|
||||
foundView = None
|
||||
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
|
||||
for openDoc in openDocs:
|
||||
if openDoc.GetFilename() == filename:
|
||||
foundView = openDoc.GetFirstView()
|
||||
break
|
||||
|
||||
if not foundView:
|
||||
doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT)
|
||||
foundView = doc.GetFirstView()
|
||||
|
||||
if foundView:
|
||||
foundView.GetFrame().SetFocus()
|
||||
foundView.Activate()
|
||||
if hasattr(foundView, "GotoLine"):
|
||||
foundView.GotoLine(lineNum)
|
||||
startPos = foundView.PositionFromLine(lineNum)
|
||||
# wxBug: Need to select in reverse order, (end, start) to put cursor at head of line so positioning is correct
|
||||
# Also, if we use the correct positioning order (start, end), somehow, when we open a edit window for the first
|
||||
# time, we don't see the selection, it is scrolled off screen
|
||||
foundView.SetSelection(startPos - 1 + len(lineText[lineEnd:].rstrip("\n")), startPos)
|
||||
wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
|
||||
|
||||
|
521
wxPython/samples/ide/activegrid/tool/FindService.py
Normal file
521
wxPython/samples/ide/activegrid/tool/FindService.py
Normal file
@@ -0,0 +1,521 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: FindService.py
|
||||
# Purpose: Find Service for pydocview
|
||||
#
|
||||
# Author: Peter Yared, Morgan Hua
|
||||
#
|
||||
# Created: 8/15/03
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2003-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import wx
|
||||
import wx.lib.docview
|
||||
import wx.lib.pydocview
|
||||
import re
|
||||
_ = wx.GetTranslation
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Constants
|
||||
#----------------------------------------------------------------------------
|
||||
FIND_MATCHPATTERN = "FindMatchPattern"
|
||||
FIND_MATCHREPLACE = "FindMatchReplace"
|
||||
FIND_MATCHCASE = "FindMatchCase"
|
||||
FIND_MATCHWHOLEWORD = "FindMatchWholeWordOnly"
|
||||
FIND_MATCHREGEXPR = "FindMatchRegularExpr"
|
||||
FIND_MATCHWRAP = "FindMatchWrap"
|
||||
FIND_MATCHUPDOWN = "FindMatchUpDown"
|
||||
|
||||
FIND_SYNTAXERROR = -2
|
||||
|
||||
SPACE = 10
|
||||
HALF_SPACE = 5
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Classes
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
class FindService(wx.lib.pydocview.DocService):
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Constants
|
||||
#----------------------------------------------------------------------------
|
||||
FIND_ID = wx.NewId() # for bringing up Find dialog box
|
||||
FINDONE_ID = wx.NewId() # for doing Find
|
||||
FIND_PREVIOUS_ID = wx.NewId() # for doing Find Next
|
||||
FIND_NEXT_ID = wx.NewId() # for doing Find Prev
|
||||
REPLACE_ID = wx.NewId() # for bringing up Replace dialog box
|
||||
REPLACEONE_ID = wx.NewId() # for doing a Replace
|
||||
REPLACEALL_ID = wx.NewId() # for doing Replace All
|
||||
GOTO_LINE_ID = wx.NewId() # for bringing up Goto dialog box
|
||||
|
||||
# Extending bitmasks: wx.FR_WHOLEWORD, wx.FR_MATCHCASE, and wx.FR_DOWN
|
||||
FR_REGEXP = max([wx.FR_WHOLEWORD, wx.FR_MATCHCASE, wx.FR_DOWN]) << 1
|
||||
FR_WRAP = FR_REGEXP << 1
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self._replaceDialog = None
|
||||
self._findDialog = None
|
||||
self._findReplaceData = wx.FindReplaceData()
|
||||
self._findReplaceData.SetFlags(wx.FR_DOWN)
|
||||
|
||||
|
||||
def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
|
||||
""" Install Find Service Menu Items """
|
||||
editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
|
||||
editMenu.AppendSeparator()
|
||||
editMenu.Append(FindService.FIND_ID, _("&Find...\tCtrl+F"), _("Finds the specified text"))
|
||||
wx.EVT_MENU(frame, FindService.FIND_ID, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, FindService.FIND_ID, frame.ProcessUpdateUIEvent)
|
||||
editMenu.Append(FindService.FIND_PREVIOUS_ID, _("Find &Previous\tShift+F3"), _("Finds the specified text"))
|
||||
wx.EVT_MENU(frame, FindService.FIND_PREVIOUS_ID, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, FindService.FIND_PREVIOUS_ID, frame.ProcessUpdateUIEvent)
|
||||
editMenu.Append(FindService.FIND_NEXT_ID, _("Find &Next\tF3"), _("Finds the specified text"))
|
||||
wx.EVT_MENU(frame, FindService.FIND_NEXT_ID, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, FindService.FIND_NEXT_ID, frame.ProcessUpdateUIEvent)
|
||||
editMenu.Append(FindService.REPLACE_ID, _("R&eplace...\tCtrl+H"), _("Replaces specific text with different text"))
|
||||
wx.EVT_MENU(frame, FindService.REPLACE_ID, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, FindService.REPLACE_ID, frame.ProcessUpdateUIEvent)
|
||||
editMenu.Append(FindService.GOTO_LINE_ID, _("&Go to Line...\tCtrl+G"), _("Goes to a certain line in the file"))
|
||||
wx.EVT_MENU(frame, FindService.GOTO_LINE_ID, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, FindService.GOTO_LINE_ID, frame.ProcessUpdateUIEvent)
|
||||
|
||||
# wxBug: wxToolBar::GetToolPos doesn't exist, need it to find cut tool and then insert find in front of it.
|
||||
toolBar.InsertTool(6, FindService.FIND_ID, getFindBitmap(), shortHelpString = _("Find"), longHelpString = _("Finds the specified text"))
|
||||
toolBar.InsertSeparator(6)
|
||||
toolBar.Realize()
|
||||
|
||||
frame.Bind(wx.EVT_FIND, frame.ProcessEvent)
|
||||
frame.Bind(wx.EVT_FIND_NEXT, frame.ProcessEvent)
|
||||
frame.Bind(wx.EVT_FIND_REPLACE, frame.ProcessEvent)
|
||||
frame.Bind(wx.EVT_FIND_REPLACE_ALL, frame.ProcessEvent)
|
||||
|
||||
|
||||
def ProcessUpdateUIEvent(self, event):
|
||||
id = event.GetId()
|
||||
if id == FindService.FIND_ID:
|
||||
event.Enable(False)
|
||||
return True
|
||||
elif id == FindService.FIND_PREVIOUS_ID:
|
||||
event.Enable(False)
|
||||
return True
|
||||
elif id == FindService.FIND_NEXT_ID:
|
||||
event.Enable(False)
|
||||
return True
|
||||
elif id == FindService.REPLACE_ID:
|
||||
event.Enable(False)
|
||||
return True
|
||||
elif id == FindService.GOTO_LINE_ID:
|
||||
event.Enable(False)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def ShowFindReplaceDialog(self, findString="", replace = False):
|
||||
""" Display find/replace dialog box.
|
||||
|
||||
Parameters: findString is the default value shown in the find/replace dialog input field.
|
||||
If replace is True, the replace dialog box is shown, otherwise only the find dialog box is shown.
|
||||
"""
|
||||
if replace:
|
||||
if self._findDialog != None:
|
||||
# No reason to have both find and replace dialogs up at the same time
|
||||
self._findDialog.DoClose()
|
||||
self._findDialog = None
|
||||
|
||||
self._replaceDialog = FindReplaceDialog(self.GetDocumentManager().FindSuitableParent(), -1, _("Replace"), size=(320,200), findString=findString)
|
||||
self._replaceDialog.Show(True)
|
||||
else:
|
||||
if self._replaceDialog != None:
|
||||
# No reason to have both find and replace dialogs up at the same time
|
||||
self._replaceDialog.DoClose()
|
||||
self._replaceDialog = None
|
||||
|
||||
self._findDialog = FindDialog(self.GetDocumentManager().FindSuitableParent(), -1, _("Find"), size=(320,200), findString=findString)
|
||||
self._findDialog.Show(True)
|
||||
|
||||
|
||||
|
||||
def OnFindClose(self, event):
|
||||
""" Cleanup handles when find/replace dialog is closed """
|
||||
if self._findDialog != None:
|
||||
self._findDialog = None
|
||||
elif self._replaceDialog != None:
|
||||
self._replaceDialog = None
|
||||
|
||||
|
||||
def GetCurrentDialog(self):
|
||||
""" return handle to either the find or replace dialog """
|
||||
if self._findDialog != None:
|
||||
return self._findDialog
|
||||
return self._replaceDialog
|
||||
|
||||
|
||||
def GetLineNumber(self, parent):
|
||||
""" Display Goto Line Number dialog box """
|
||||
line = -1
|
||||
dialog = wx.TextEntryDialog(parent, _("Enter line number to go to:"), _("Go to Line"))
|
||||
if dialog.ShowModal() == wx.ID_OK:
|
||||
try:
|
||||
line = int(dialog.GetValue())
|
||||
if line > 65535:
|
||||
line = 65535
|
||||
except:
|
||||
pass
|
||||
dialog.Destroy()
|
||||
# This one is ugly: wx.GetNumberFromUser("", _("Enter line number to go to:"), _("Go to Line"), 1, min = 1, max = 65535, parent = parent)
|
||||
return line
|
||||
|
||||
|
||||
def DoFind(self, findString, replaceString, text, startLoc, endLoc, down, matchCase, wholeWord, regExpr = False, replace = False, replaceAll = False, wrap = False):
|
||||
""" Do the actual work of the find/replace.
|
||||
|
||||
Returns the tuple (count, start, end, newText).
|
||||
count = number of string replacements
|
||||
start = start position of found string
|
||||
end = end position of found string
|
||||
newText = new replaced text
|
||||
"""
|
||||
flags = 0
|
||||
if regExpr:
|
||||
pattern = findString
|
||||
else:
|
||||
pattern = re.escape(findString) # Treat the strings as a literal string
|
||||
if not matchCase:
|
||||
flags = re.IGNORECASE
|
||||
if wholeWord:
|
||||
pattern = r"\b%s\b" % pattern
|
||||
|
||||
try:
|
||||
reg = re.compile(pattern, flags)
|
||||
except:
|
||||
# syntax error of some sort
|
||||
import sys
|
||||
msgTitle = wx.GetApp().GetAppName()
|
||||
if not msgTitle:
|
||||
msgTitle = _("Regular Expression Search")
|
||||
wx.MessageBox(_("Invalid regular expression \"%s\". %s") % (pattern, sys.exc_value),
|
||||
msgTitle,
|
||||
wx.OK | wx.ICON_EXCLAMATION,
|
||||
self.GetView())
|
||||
return FIND_SYNTAXERROR, None, None, None
|
||||
|
||||
if replaceAll:
|
||||
newText, count = reg.subn(replaceString, text)
|
||||
if count == 0:
|
||||
return -1, None, None, None
|
||||
else:
|
||||
return count, None, None, newText
|
||||
|
||||
start = -1
|
||||
if down:
|
||||
match = reg.search(text, endLoc)
|
||||
if match == None:
|
||||
if wrap: # try again, but this time from top of file
|
||||
match = reg.search(text, 0)
|
||||
if match == None:
|
||||
return -1, None, None, None
|
||||
else:
|
||||
return -1, None, None, None
|
||||
start = match.start()
|
||||
end = match.end()
|
||||
else:
|
||||
match = reg.search(text)
|
||||
if match == None:
|
||||
return -1, None, None, None
|
||||
found = None
|
||||
i, j = match.span()
|
||||
while i < startLoc and j <= startLoc:
|
||||
found = match
|
||||
if i == j:
|
||||
j = j + 1
|
||||
match = reg.search(text, j)
|
||||
if match == None:
|
||||
break
|
||||
i, j = match.span()
|
||||
if found == None:
|
||||
if wrap: # try again, but this time from bottom of file
|
||||
match = reg.search(text, startLoc)
|
||||
if match == None:
|
||||
return -1, None, None, None
|
||||
found = None
|
||||
i, j = match.span()
|
||||
end = len(text)
|
||||
while i < end and j <= end:
|
||||
found = match
|
||||
if i == j:
|
||||
j = j + 1
|
||||
match = reg.search(text, j)
|
||||
if match == None:
|
||||
break
|
||||
i, j = match.span()
|
||||
if found == None:
|
||||
return -1, None, None, None
|
||||
else:
|
||||
return -1, None, None, None
|
||||
start = found.start()
|
||||
end = found.end()
|
||||
|
||||
if replace and start != -1:
|
||||
newText, count = reg.subn(replaceString, text, 1)
|
||||
return count, start, end, newText
|
||||
|
||||
return 0, start, end, None
|
||||
|
||||
|
||||
def SaveFindConfig(self, findString, wholeWord, matchCase, regExpr = None, wrap = None, upDown = None, replaceString = None):
|
||||
""" Save find/replace patterns and search flags to registry.
|
||||
|
||||
findString = search pattern
|
||||
wholeWord = match whole word only
|
||||
matchCase = match case
|
||||
regExpr = use regular expressions in search pattern
|
||||
wrap = return to top/bottom of file on search
|
||||
upDown = search up or down from current cursor position
|
||||
replaceString = replace string
|
||||
"""
|
||||
config = wx.ConfigBase_Get()
|
||||
|
||||
config.Write(FIND_MATCHPATTERN, findString)
|
||||
config.WriteInt(FIND_MATCHCASE, matchCase)
|
||||
config.WriteInt(FIND_MATCHWHOLEWORD, wholeWord)
|
||||
if replaceString != None:
|
||||
config.Write(FIND_MATCHREPLACE, replaceString)
|
||||
if regExpr != None:
|
||||
config.WriteInt(FIND_MATCHREGEXPR, regExpr)
|
||||
if wrap != None:
|
||||
config.WriteInt(FIND_MATCHWRAP, wrap)
|
||||
if upDown != None:
|
||||
config.WriteInt(FIND_MATCHUPDOWN, upDown)
|
||||
|
||||
|
||||
def GetFindString(self):
|
||||
""" Load the search pattern from registry """
|
||||
return wx.ConfigBase_Get().Read(FIND_MATCHPATTERN, "")
|
||||
|
||||
|
||||
def GetReplaceString(self):
|
||||
""" Load the replace pattern from registry """
|
||||
return wx.ConfigBase_Get().Read(FIND_MATCHREPLACE, "")
|
||||
|
||||
|
||||
def GetFlags(self):
|
||||
""" Load search parameters from registry """
|
||||
config = wx.ConfigBase_Get()
|
||||
|
||||
flags = 0
|
||||
if config.ReadInt(FIND_MATCHWHOLEWORD, False):
|
||||
flags = flags | wx.FR_WHOLEWORD
|
||||
if config.ReadInt(FIND_MATCHCASE, False):
|
||||
flags = flags | wx.FR_MATCHCASE
|
||||
if config.ReadInt(FIND_MATCHUPDOWN, False):
|
||||
flags = flags | wx.FR_DOWN
|
||||
if config.ReadInt(FIND_MATCHREGEXPR, False):
|
||||
flags = flags | FindService.FR_REGEXP
|
||||
if config.ReadInt(FIND_MATCHWRAP, False):
|
||||
flags = flags | FindService.FR_WRAP
|
||||
return flags
|
||||
|
||||
|
||||
class FindDialog(wx.Dialog):
|
||||
""" Find Dialog with regular expression matching and wrap to top/bottom of file. """
|
||||
|
||||
def __init__(self, parent, id, title, size, findString=None):
|
||||
wx.Dialog.__init__(self, parent, id, title, size=size)
|
||||
|
||||
config = wx.ConfigBase_Get()
|
||||
borderSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
gridSizer = wx.GridBagSizer(SPACE, SPACE)
|
||||
|
||||
lineSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
lineSizer.Add(wx.StaticText(self, -1, _("Find what:")), 0, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, SPACE)
|
||||
if not findString:
|
||||
findString = config.Read(FIND_MATCHPATTERN, "")
|
||||
self._findCtrl = wx.TextCtrl(self, -1, findString, size=(200,-1))
|
||||
lineSizer.Add(self._findCtrl, 0)
|
||||
gridSizer.Add(lineSizer, pos=(0,0), span=(1,2))
|
||||
choiceSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self._wholeWordCtrl = wx.CheckBox(self, -1, _("Match whole word only"))
|
||||
self._wholeWordCtrl.SetValue(config.ReadInt(FIND_MATCHWHOLEWORD, False))
|
||||
self._matchCaseCtrl = wx.CheckBox(self, -1, _("Match case"))
|
||||
self._matchCaseCtrl.SetValue(config.ReadInt(FIND_MATCHCASE, False))
|
||||
self._regExprCtrl = wx.CheckBox(self, -1, _("Regular expression"))
|
||||
self._regExprCtrl.SetValue(config.ReadInt(FIND_MATCHREGEXPR, False))
|
||||
self._wrapCtrl = wx.CheckBox(self, -1, _("Wrap"))
|
||||
self._wrapCtrl.SetValue(config.ReadInt(FIND_MATCHWRAP, False))
|
||||
choiceSizer.Add(self._wholeWordCtrl, 0, wx.BOTTOM, SPACE)
|
||||
choiceSizer.Add(self._matchCaseCtrl, 0, wx.BOTTOM, SPACE)
|
||||
choiceSizer.Add(self._regExprCtrl, 0, wx.BOTTOM, SPACE)
|
||||
choiceSizer.Add(self._wrapCtrl, 0)
|
||||
gridSizer.Add(choiceSizer, pos=(1,0), span=(2,1))
|
||||
|
||||
self._radioBox = wx.RadioBox(self, -1, _("Direction"), choices = ["Up", "Down"])
|
||||
self._radioBox.SetSelection(config.ReadInt(FIND_MATCHUPDOWN, 1))
|
||||
gridSizer.Add(self._radioBox, pos=(1,1), span=(2,1))
|
||||
|
||||
buttonSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
findBtn = wx.Button(self, FindService.FINDONE_ID, _("Find Next"))
|
||||
findBtn.SetDefault()
|
||||
wx.EVT_BUTTON(self, FindService.FINDONE_ID, self.OnActionEvent)
|
||||
cancelBtn = wx.Button(self, wx.ID_CANCEL, _("Cancel"))
|
||||
wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnClose)
|
||||
buttonSizer.Add(findBtn, 0, wx.BOTTOM, HALF_SPACE)
|
||||
buttonSizer.Add(cancelBtn, 0)
|
||||
gridSizer.Add(buttonSizer, pos=(0,2), span=(3,1))
|
||||
|
||||
borderSizer.Add(gridSizer, 0, wx.ALL, SPACE)
|
||||
|
||||
self.Bind(wx.EVT_CLOSE, self.OnClose)
|
||||
|
||||
self.SetSizer(borderSizer)
|
||||
self.Fit()
|
||||
self._findCtrl.SetFocus()
|
||||
|
||||
def SaveConfig(self):
|
||||
""" Save find patterns and search flags to registry. """
|
||||
findService = wx.GetApp().GetService(FindService)
|
||||
if findService:
|
||||
findService.SaveFindConfig(self._findCtrl.GetValue(),
|
||||
self._wholeWordCtrl.IsChecked(),
|
||||
self._matchCaseCtrl.IsChecked(),
|
||||
self._regExprCtrl.IsChecked(),
|
||||
self._wrapCtrl.IsChecked(),
|
||||
self._radioBox.GetSelection(),
|
||||
)
|
||||
|
||||
|
||||
def DoClose(self):
|
||||
self.SaveConfig()
|
||||
self.Destroy()
|
||||
|
||||
|
||||
def OnClose(self, event):
|
||||
findService = wx.GetApp().GetService(FindService)
|
||||
if findService:
|
||||
findService.OnFindClose(event)
|
||||
self.DoClose()
|
||||
|
||||
|
||||
def OnActionEvent(self, event):
|
||||
self.SaveConfig()
|
||||
|
||||
if wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_MDI:
|
||||
if wx.GetApp().GetTopWindow().ProcessEvent(event):
|
||||
return True
|
||||
else:
|
||||
view = wx.GetApp().GetDocumentManager().GetLastActiveView()
|
||||
if view and view.ProcessEvent(event):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class FindReplaceDialog(FindDialog):
|
||||
""" Find/Replace Dialog with regular expression matching and wrap to top/bottom of file. """
|
||||
|
||||
def __init__(self, parent, id, title, size, findString=None):
|
||||
wx.Dialog.__init__(self, parent, id, title, size=size)
|
||||
|
||||
config = wx.ConfigBase_Get()
|
||||
borderSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
gridSizer = wx.GridBagSizer(SPACE, SPACE)
|
||||
|
||||
gridSizer2 = wx.GridBagSizer(SPACE, SPACE)
|
||||
gridSizer2.Add(wx.StaticText(self, -1, _("Find what:")), flag=wx.ALIGN_CENTER_VERTICAL, pos=(0,0))
|
||||
if not findString:
|
||||
findString = config.Read(FIND_MATCHPATTERN, "")
|
||||
self._findCtrl = wx.TextCtrl(self, -1, findString, size=(200,-1))
|
||||
gridSizer2.Add(self._findCtrl, pos=(0,1))
|
||||
gridSizer2.Add(wx.StaticText(self, -1, _("Replace with:")), flag=wx.ALIGN_CENTER_VERTICAL, pos=(1,0))
|
||||
self._replaceCtrl = wx.TextCtrl(self, -1, config.Read(FIND_MATCHREPLACE, ""), size=(200,-1))
|
||||
gridSizer2.Add(self._replaceCtrl, pos=(1,1))
|
||||
gridSizer.Add(gridSizer2, pos=(0,0), span=(1,2))
|
||||
choiceSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
self._wholeWordCtrl = wx.CheckBox(self, -1, _("Match whole word only"))
|
||||
self._wholeWordCtrl.SetValue(config.ReadInt(FIND_MATCHWHOLEWORD, False))
|
||||
self._matchCaseCtrl = wx.CheckBox(self, -1, _("Match case"))
|
||||
self._matchCaseCtrl.SetValue(config.ReadInt(FIND_MATCHCASE, False))
|
||||
self._regExprCtrl = wx.CheckBox(self, -1, _("Regular expression"))
|
||||
self._regExprCtrl.SetValue(config.ReadInt(FIND_MATCHREGEXPR, False))
|
||||
self._wrapCtrl = wx.CheckBox(self, -1, _("Wrap"))
|
||||
self._wrapCtrl.SetValue(config.ReadInt(FIND_MATCHWRAP, False))
|
||||
choiceSizer.Add(self._wholeWordCtrl, 0, wx.BOTTOM, SPACE)
|
||||
choiceSizer.Add(self._matchCaseCtrl, 0, wx.BOTTOM, SPACE)
|
||||
choiceSizer.Add(self._regExprCtrl, 0, wx.BOTTOM, SPACE)
|
||||
choiceSizer.Add(self._wrapCtrl, 0)
|
||||
gridSizer.Add(choiceSizer, pos=(1,0), span=(2,1))
|
||||
|
||||
self._radioBox = wx.RadioBox(self, -1, _("Direction"), choices = ["Up", "Down"])
|
||||
self._radioBox.SetSelection(config.ReadInt(FIND_MATCHUPDOWN, 1))
|
||||
gridSizer.Add(self._radioBox, pos=(1,1), span=(2,1))
|
||||
|
||||
buttonSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
findBtn = wx.Button(self, FindService.FINDONE_ID, _("Find Next"))
|
||||
findBtn.SetDefault()
|
||||
wx.EVT_BUTTON(self, FindService.FINDONE_ID, self.OnActionEvent)
|
||||
cancelBtn = wx.Button(self, wx.ID_CANCEL, _("Cancel"))
|
||||
wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnClose)
|
||||
replaceBtn = wx.Button(self, FindService.REPLACEONE_ID, _("Replace"))
|
||||
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)
|
||||
buttonSizer.Add(cancelBtn, 0)
|
||||
gridSizer.Add(buttonSizer, pos=(0,2), span=(3,1))
|
||||
|
||||
borderSizer.Add(gridSizer, 0, wx.ALL, SPACE)
|
||||
|
||||
self.Bind(wx.EVT_CLOSE, self.OnClose)
|
||||
|
||||
self.SetSizer(borderSizer)
|
||||
self.Fit()
|
||||
self._findCtrl.SetFocus()
|
||||
|
||||
|
||||
def SaveConfig(self):
|
||||
""" Save find/replace patterns and search flags to registry. """
|
||||
findService = wx.GetApp().GetService(FindService)
|
||||
if findService:
|
||||
findService.SaveFindConfig(self._findCtrl.GetValue(),
|
||||
self._wholeWordCtrl.IsChecked(),
|
||||
self._matchCaseCtrl.IsChecked(),
|
||||
self._regExprCtrl.IsChecked(),
|
||||
self._wrapCtrl.IsChecked(),
|
||||
self._radioBox.GetSelection(),
|
||||
self._replaceCtrl.GetValue()
|
||||
)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Menu Bitmaps - generated by encode_bitmaps.py
|
||||
#----------------------------------------------------------------------------
|
||||
from wx import ImageFromStream, BitmapFromImage
|
||||
import cStringIO
|
||||
|
||||
|
||||
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'
|
||||
|
||||
|
||||
def getFindBitmap():
|
||||
return BitmapFromImage(getFindImage())
|
||||
|
||||
|
||||
def getFindImage():
|
||||
stream = cStringIO.StringIO(getFindData())
|
||||
return ImageFromStream(stream)
|
||||
|
222
wxPython/samples/ide/activegrid/tool/HtmlEditor.py
Normal file
222
wxPython/samples/ide/activegrid/tool/HtmlEditor.py
Normal file
@@ -0,0 +1,222 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: HtmlEditor.py
|
||||
# Purpose: Abstract Code Editor for pydocview tbat uses the Styled Text Control
|
||||
#
|
||||
# Author: Peter Yared
|
||||
#
|
||||
# Created: 8/15/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
|
||||
import wx
|
||||
import os.path
|
||||
import string
|
||||
import STCTextEditor
|
||||
import CodeEditor
|
||||
_ = wx.GetTranslation
|
||||
|
||||
|
||||
class HtmlDocument(CodeEditor.CodeDocument):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class HtmlView(CodeEditor.CodeView):
|
||||
|
||||
|
||||
def GetCtrlClass(self):
|
||||
""" Used in split window to instantiate new instances """
|
||||
return HtmlCtrl
|
||||
|
||||
|
||||
def GetAutoCompleteHint(self):
|
||||
pos = self.GetCtrl().GetCurrentPos()
|
||||
if pos == 0:
|
||||
return None, None
|
||||
|
||||
validLetters = string.letters + string.digits + '_!-'
|
||||
word = ''
|
||||
while (True):
|
||||
pos = pos - 1
|
||||
if pos < 0:
|
||||
break
|
||||
char = chr(self.GetCtrl().GetCharAt(pos))
|
||||
if char not in validLetters:
|
||||
break
|
||||
word = char + word
|
||||
|
||||
return None, word
|
||||
|
||||
|
||||
def GetAutoCompleteDefaultKeywords(self):
|
||||
return HTMLKEYWORDS
|
||||
|
||||
|
||||
## def _CreateControl(self, parent, id):
|
||||
## import wx # wxBug: When inlining the import of the appropriate html control below, have to specifically import wx for some reason
|
||||
## self._notebook = wx.Notebook(parent, wx.NewId(), style = wx.NB_BOTTOM)
|
||||
## self._textEditor = HtmlCtrl(self._notebook, id)
|
||||
## if wx.Platform =='__WXMSW__':
|
||||
## import wxPython.iewin
|
||||
## self._browserCtrl = wxPython.iewin.wxIEHtmlWin(self._notebook, -1, style = wx.NO_FULL_REPAINT_ON_RESIZE)
|
||||
## else:
|
||||
## import wx.html
|
||||
## self._browserCtrl = wx.html.HtmlWindow(self._notebook, -1, style = wx.NO_FULL_REPAINT_ON_RESIZE)
|
||||
## self._notebook.AddPage(self._textEditor, _("Edit"))
|
||||
## self._notebook.AddPage(self._browserCtrl, _("View"))
|
||||
## self._insertMode = True
|
||||
## wx.EVT_NOTEBOOK_PAGE_CHANGED(self._notebook, self._notebook.GetId(), self.OnNotebookChanging)
|
||||
## return self._textEditor
|
||||
##
|
||||
##
|
||||
## def _CreateSizer(self, frame):
|
||||
## sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
## sizer.Add(self._notebook, 1, wx.EXPAND)
|
||||
## frame.SetSizer(sizer)
|
||||
## frame.SetAutoLayout(True)
|
||||
##
|
||||
##
|
||||
## def OnNotebookChanging(self, event):
|
||||
## if event.GetSelection() == 0: # Going to the edit page
|
||||
## pass # self._textEditor.Refresh()
|
||||
## elif event.GetSelection() == 1: # Going to the browser page
|
||||
## text = self._textEditor.GetText()
|
||||
## if wx.Platform == '__WXMSW__':
|
||||
## path = os.path.join(tempfile.gettempdir(), "temp.html")
|
||||
## file = open(path, 'w')
|
||||
## file.write(text)
|
||||
## file.close()
|
||||
## self._browserCtrl.Navigate("file://" + path)
|
||||
## else:
|
||||
## self._browserCtrl.SetPage(text)
|
||||
## event.Skip()
|
||||
|
||||
|
||||
class HtmlService(CodeEditor.CodeService):
|
||||
|
||||
|
||||
def __init__(self):
|
||||
CodeEditor.CodeService.__init__(self)
|
||||
|
||||
|
||||
class HtmlCtrl(CodeEditor.CodeCtrl):
|
||||
|
||||
|
||||
def __init__(self, parent, ID = -1, style = wx.NO_FULL_REPAINT_ON_RESIZE):
|
||||
CodeEditor.CodeCtrl.__init__(self, parent, ID, style)
|
||||
self.SetLexer(wx.stc.STC_LEX_HTML)
|
||||
self.SetProperty("fold.html", "1")
|
||||
|
||||
def GetMatchingBraces(self):
|
||||
return "<>[]{}()"
|
||||
|
||||
def CanWordWrap(self):
|
||||
return True
|
||||
|
||||
|
||||
def SetViewDefaults(self):
|
||||
CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Html", hasWordWrap = False, hasTabs = True)
|
||||
|
||||
|
||||
def GetFontAndColorFromConfig(self):
|
||||
return CodeEditor.CodeCtrl.GetFontAndColorFromConfig(self, configPrefix = "Html")
|
||||
|
||||
|
||||
def UpdateStyles(self):
|
||||
CodeEditor.CodeCtrl.UpdateStyles(self)
|
||||
|
||||
if not self.GetFont():
|
||||
return
|
||||
|
||||
faces = { 'font' : self.GetFont().GetFaceName(),
|
||||
'size' : self.GetFont().GetPointSize(),
|
||||
'size2': self.GetFont().GetPointSize() - 2,
|
||||
'color' : "%02x%02x%02x" % (self.GetFontColor().Red(), self.GetFontColor().Green(), self.GetFontColor().Blue())
|
||||
}
|
||||
|
||||
# White space
|
||||
self.StyleSetSpec(wx.stc.STC_H_DEFAULT, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
# Comment
|
||||
self.StyleSetSpec(wx.stc.STC_H_COMMENT, "face:%(font)s,fore:#007F00,italic,face:%(font)s,size:%(size)d" % faces)
|
||||
# Number
|
||||
self.StyleSetSpec(wx.stc.STC_H_NUMBER, "face:%(font)s,fore:#007F7F,size:%(size)d" % faces)
|
||||
# String
|
||||
self.StyleSetSpec(wx.stc.STC_H_SINGLESTRING, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_H_DOUBLESTRING, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
# 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)
|
||||
|
||||
|
||||
class HtmlOptionsPanel(STCTextEditor.TextOptionsPanel):
|
||||
|
||||
def __init__(self, parent, id):
|
||||
STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Html", label = "HTML", hasWordWrap = True, hasTabs = True)
|
||||
|
||||
|
||||
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",
|
||||
"DIV", "DL", "DT", "EM", "FIELDSET", "FONT", "FORM", "FRAME", "FRAMESET", "H1", "H2", "H3", "H4", "H5", "H6",
|
||||
"HEAD", "HR", "HTML", "I", "IFRAME", "IMG", "INPUT", "INS", "ISINDEX", "KBD", "LABEL", "LEGEND", "LI", "LINK",
|
||||
"MAP", "MENU", "META", "NOFRAMES", "NOSCRIPT", "OBJECT", "OL", "OPTGROUP", "OPTION", "P", "PARAM",
|
||||
"PRE", "Q", "S", "SAMP", "SCRIPT", "SELECT", "SMALL", "SPAN", "STRIKE", "STRONG", "STYLE", "SUB", "SUP",
|
||||
"TABLE", "TBODY", "TD", "TEXTAREA", "TFOOT", "TH", "THEAD", "TITLE", "TR", "TT", "U", "UL", "VAR", "XML",
|
||||
"XMLNS", "ACCEPT-CHARSET", "ACCEPT", "ACCESSKEY", "ACTION", "ALIGN", "ALINK", "ALT",
|
||||
"ARCHIVE", "AXIS", "BACKGROUND", "BGCOLOR", "BORDER", "CELLPADDING", "CELLSPACING", "CHAR",
|
||||
"CHAROFF", "CHARSET", "CHECKED", "CLASS", "CLASSID", "CLEAR", "CODEBASE", "CODETYPE",
|
||||
"COLOR", "COLS", "COLSPAN", "COMPACT", "CONTENT", "COORDS", "DATA", "DATAFLD", "DATAFORMATAS",
|
||||
"DATAPAGESIZE", "DATASRC", "DATETIME", "DECLARE", "DEFER", "DISABLED", "ENCTYPE",
|
||||
"EVENT", "FACE", "FOR", "FRAMEBORDER", "HEADERS", "HEIGHT", "HREF", "HREFLANG", "HSPACE",
|
||||
"HTTP-EQUIV", "ID", "ISMAP", "LANG", "LANGUAGE", "LEFTMARGIN", "LONGDESC",
|
||||
"MARGINWIDTH", "MARGINHEIGHT", "MAXLENGTH", "MEDIA", "METHOD", "MULTIPLE", "NAME", "NOHREF",
|
||||
"NORESIZE", "NOSHADE", "NOWRAP", "ONBLUR", "ONCHANGE", "ONCLICK", "ONDBLCLICK",
|
||||
"ONFOCUS", "ONKEYDOWN", "ONKEYPRESS", "ONKEYUP", "ONLOAD", "ONMOUSEDOWN", "ONMOUSEMOVE",
|
||||
"ONMOUSEOVER", "ONMOUSEOUT", "ONMOUSEUP", "ONRESET", "ONSELECT", "ONSUBMIT", "ONUNLOAD",
|
||||
"PROFILE", "PROMPT", "READONLY", "REL", "REV", "ROWS", "ROWSPAN", "RULES", "SCHEME", "SCOPE",
|
||||
"SELECTED", "SHAPE", "SIZE", "SRC", "STANDBY", "START", "SUMMARY", "TABINDEX",
|
||||
"TARGET", "TOPMARGIN", "TYPE", "USEMAP", "VALIGN", "VALUE", "VALUETYPE",
|
||||
"VERSION", "VLINK", "VSPACE", "WIDTH", "TEXT", "PASSWORD", "CHECKBOX", "RADIO", "SUBMIT", "RESET",
|
||||
"FILE", "HIDDEN", "IMAGE", "PUBLIC", "!DOCTYPE",
|
||||
"ADD_DATE", "LAST_MODIFIED", "LAST_VISIT"
|
||||
]
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Icon Bitmaps - generated by encode_bitmaps.py
|
||||
#----------------------------------------------------------------------------
|
||||
from wx import ImageFromStream, BitmapFromImage
|
||||
from wx import EmptyIcon
|
||||
import cStringIO
|
||||
|
||||
|
||||
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\xcdIDAT8\x8dcd`f\xf8\xcf@\x01`\xfc\x7f\xa3\x87"\x03X\xfe}\xbeI\x89~\
|
||||
\x06&\x8at\x0f\n\x03\x18\xe4\x954\xff\xc3\x00\x8c-\xaf\xa4\xf9_\xc7\xc0\xfc\
|
||||
\xbf\x93\xab\xf7\xff\xff\xff\xff\xff70\xb6\xfe\x7f\xed\xce\x93\xff\xd7\xee<\
|
||||
\xf9\xafc`\x0eW\xf3\xf5\xd7\xff\xff,\x0f\x1f^gPP\xd6B1\xf4\xc1\xddk\x0c\xf6\
|
||||
\xb6\x16\x0c{wma````x\xf7\xfc\x06\xc3\xea\xa5\xb3\x198\xd8X\x18\xbe~|\x06W\
|
||||
\xc7\xc5\xca\xc0\xc0\xc2\xc0\xc0\xc0P\\\x9c\xcf\xf0\xf4\xc5\x1b\x86\x15K\x97\
|
||||
\xc2%Y\xd9y\xe0lF\x0e1\x86C\x87\x8e0\x88\x88\x8a3\xfccD\x88\xe3\xf4\x026\xf6\
|
||||
\xa9c{\xfe_<s\x18\xc5\x9b\xf2J\x9a\xff\x19\xff\x9eN\xa5(!\r|4\x0e\x03\x03\
|
||||
\x00R\xe4{\xe74\x9e\xbb\xd1\x00\x00\x00\x00IEND\xaeB`\x82'
|
||||
|
||||
|
||||
def getHTMLBitmap():
|
||||
return BitmapFromImage(getHTMLImage())
|
||||
|
||||
def getHTMLImage():
|
||||
stream = cStringIO.StringIO(getHTMLData())
|
||||
return ImageFromStream(stream)
|
||||
|
||||
def getHTMLIcon():
|
||||
icon = EmptyIcon()
|
||||
icon.CopyFromBitmap(getHTMLBitmap())
|
||||
return icon
|
2056
wxPython/samples/ide/activegrid/tool/IDE.py
Normal file
2056
wxPython/samples/ide/activegrid/tool/IDE.py
Normal file
File diff suppressed because it is too large
Load Diff
445
wxPython/samples/ide/activegrid/tool/IDEFindService.py
Normal file
445
wxPython/samples/ide/activegrid/tool/IDEFindService.py
Normal file
@@ -0,0 +1,445 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: IDEFindService.py
|
||||
# Purpose: Find Service for pydocview
|
||||
#
|
||||
# Author: Morgan Hua
|
||||
#
|
||||
# Created: 8/15/03
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import wx
|
||||
import wx.lib.docview
|
||||
import os
|
||||
from os.path import join
|
||||
import re
|
||||
import ProjectEditor
|
||||
import MessageService
|
||||
import FindService
|
||||
import OutlineService
|
||||
_ = wx.GetTranslation
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Constants
|
||||
#----------------------------------------------------------------------------
|
||||
FILENAME_MARKER = _("Found in file: ")
|
||||
PROJECT_MARKER = _("Searching project: ")
|
||||
FIND_MATCHDIR = "FindMatchDir"
|
||||
FIND_MATCHDIRSUBFOLDERS = "FindMatchDirSubfolders"
|
||||
|
||||
SPACE = 10
|
||||
HALF_SPACE = 5
|
||||
|
||||
|
||||
class IDEFindService(FindService.FindService):
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Constants
|
||||
#----------------------------------------------------------------------------
|
||||
FINDALL_ID = wx.NewId() # for bringing up Find All dialog box
|
||||
FINDDIR_ID = wx.NewId() # for bringing up Find Dir dialog box
|
||||
|
||||
|
||||
def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
|
||||
FindService.FindService.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
|
||||
|
||||
editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
|
||||
wx.EVT_MENU(frame, IDEFindService.FINDALL_ID, self.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, IDEFindService.FINDALL_ID, self.ProcessUpdateUIEvent)
|
||||
editMenu.Append(IDEFindService.FINDALL_ID, _("Find in Project...\tCtrl+Shift+F"), _("Searches for the specified text in all the files in the project"))
|
||||
wx.EVT_MENU(frame, IDEFindService.FINDDIR_ID, self.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, IDEFindService.FINDDIR_ID, self.ProcessUpdateUIEvent)
|
||||
editMenu.Append(IDEFindService.FINDDIR_ID, _("Find in Directory..."), _("Searches for the specified text in all the files in the directory"))
|
||||
|
||||
|
||||
def ProcessEvent(self, event):
|
||||
id = event.GetId()
|
||||
if id == IDEFindService.FINDALL_ID:
|
||||
self.ShowFindAllDialog()
|
||||
return True
|
||||
elif id == IDEFindService.FINDDIR_ID:
|
||||
self.ShowFindDirDialog()
|
||||
return True
|
||||
else:
|
||||
return FindService.FindService.ProcessEvent(self, event)
|
||||
|
||||
|
||||
def ProcessUpdateUIEvent(self, event):
|
||||
id = event.GetId()
|
||||
if id == IDEFindService.FINDALL_ID:
|
||||
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
|
||||
view = projectService.GetView()
|
||||
if view and view.GetDocument() and view.GetDocument().GetFiles():
|
||||
event.Enable(True)
|
||||
else:
|
||||
event.Enable(False)
|
||||
return True
|
||||
elif id == IDEFindService.FINDDIR_ID:
|
||||
event.Enable(True)
|
||||
else:
|
||||
return FindService.FindService.ProcessUpdateUIEvent(self, event)
|
||||
|
||||
|
||||
def ShowFindDirDialog(self):
|
||||
config = wx.ConfigBase_Get()
|
||||
|
||||
frame = wx.Dialog(None, -1, _("Find in Directory"), size= (320,200))
|
||||
borderSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
contentSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
lineSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
lineSizer.Add(wx.StaticText(frame, -1, _("Directory:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
|
||||
dirCtrl = wx.TextCtrl(frame, -1, config.Read(FIND_MATCHDIR, ""), size=(200,-1))
|
||||
dirCtrl.SetToolTipString(dirCtrl.GetValue())
|
||||
lineSizer.Add(dirCtrl, 0, wx.LEFT, HALF_SPACE)
|
||||
findDirButton = wx.Button(frame, -1, "Browse...")
|
||||
lineSizer.Add(findDirButton, 0, wx.LEFT, HALF_SPACE)
|
||||
contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
|
||||
|
||||
def OnBrowseButton(event):
|
||||
dlg = wx.DirDialog(frame, _("Choose a directory:"), style=wx.DD_DEFAULT_STYLE)
|
||||
dir = dirCtrl.GetValue()
|
||||
if len(dir):
|
||||
dlg.SetPath(dir)
|
||||
if dlg.ShowModal() == wx.ID_OK:
|
||||
dirCtrl.SetValue(dlg.GetPath())
|
||||
dirCtrl.SetToolTipString(dirCtrl.GetValue())
|
||||
dirCtrl.SetInsertionPointEnd()
|
||||
|
||||
dlg.Destroy()
|
||||
wx.EVT_BUTTON(findDirButton, -1, OnBrowseButton)
|
||||
|
||||
subfolderCtrl = wx.CheckBox(frame, -1, _("Search in subfolders"))
|
||||
subfolderCtrl.SetValue(config.ReadInt(FIND_MATCHDIRSUBFOLDERS, True))
|
||||
contentSizer.Add(subfolderCtrl, 0, wx.BOTTOM, SPACE)
|
||||
|
||||
lineSizer = wx.BoxSizer(wx.VERTICAL) # let the line expand horizontally without vertical expansion
|
||||
lineSizer.Add(wx.StaticLine(frame, -1, size = (10,-1)), 0, flag=wx.EXPAND)
|
||||
contentSizer.Add(lineSizer, flag=wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.BOTTOM, border=HALF_SPACE)
|
||||
|
||||
lineSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
|
||||
findCtrl = wx.TextCtrl(frame, -1, config.Read(FindService.FIND_MATCHPATTERN, ""), size=(200,-1))
|
||||
lineSizer.Add(findCtrl, 0, wx.LEFT, HALF_SPACE)
|
||||
contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
|
||||
wholeWordCtrl = wx.CheckBox(frame, -1, _("Match whole word only"))
|
||||
wholeWordCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHWHOLEWORD, False))
|
||||
matchCaseCtrl = wx.CheckBox(frame, -1, _("Match case"))
|
||||
matchCaseCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHCASE, False))
|
||||
regExprCtrl = wx.CheckBox(frame, -1, _("Regular expression"))
|
||||
regExprCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHREGEXPR, False))
|
||||
contentSizer.Add(wholeWordCtrl, 0, wx.BOTTOM, SPACE)
|
||||
contentSizer.Add(matchCaseCtrl, 0, wx.BOTTOM, SPACE)
|
||||
contentSizer.Add(regExprCtrl, 0, wx.BOTTOM, SPACE)
|
||||
borderSizer.Add(contentSizer, 0, wx.TOP | wx.BOTTOM | wx.LEFT, SPACE)
|
||||
|
||||
buttonSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
findBtn = wx.Button(frame, wx.ID_OK, _("Find"))
|
||||
findBtn.SetDefault()
|
||||
buttonSizer.Add(findBtn, 0, wx.BOTTOM, HALF_SPACE)
|
||||
buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL, _("Cancel")), 0)
|
||||
borderSizer.Add(buttonSizer, 0, wx.ALL, SPACE)
|
||||
|
||||
frame.SetSizer(borderSizer)
|
||||
frame.Fit()
|
||||
|
||||
status = frame.ShowModal()
|
||||
|
||||
# save user choice state for this and other Find Dialog Boxes
|
||||
dirString = dirCtrl.GetValue()
|
||||
searchSubfolders = subfolderCtrl.IsChecked()
|
||||
self.SaveFindDirConfig(dirString, searchSubfolders)
|
||||
|
||||
findString = findCtrl.GetValue()
|
||||
matchCase = matchCaseCtrl.IsChecked()
|
||||
wholeWord = wholeWordCtrl.IsChecked()
|
||||
regExpr = regExprCtrl.IsChecked()
|
||||
self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
|
||||
|
||||
while not os.path.exists(dirString):
|
||||
dlg = wx.MessageDialog(frame,
|
||||
_("'%s' does not exist.") % dirString,
|
||||
_("Find in Directory"),
|
||||
wx.OK | wx.ICON_EXCLAMATION
|
||||
)
|
||||
dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
|
||||
status = frame.ShowModal()
|
||||
|
||||
# save user choice state for this and other Find Dialog Boxes
|
||||
dirString = dirCtrl.GetValue()
|
||||
searchSubfolders = subfolderCtrl.IsChecked()
|
||||
self.SaveFindDirConfig(dirString, searchSubfolders)
|
||||
|
||||
findString = findCtrl.GetValue()
|
||||
matchCase = matchCaseCtrl.IsChecked()
|
||||
wholeWord = wholeWordCtrl.IsChecked()
|
||||
regExpr = regExprCtrl.IsChecked()
|
||||
self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
|
||||
|
||||
if status == wx.ID_CANCEL:
|
||||
break
|
||||
|
||||
|
||||
if status == wx.ID_OK:
|
||||
frame.Destroy()
|
||||
|
||||
messageService = wx.GetApp().GetService(MessageService.MessageService)
|
||||
messageService.ShowWindow()
|
||||
|
||||
view = messageService.GetView()
|
||||
if view:
|
||||
wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
|
||||
view.ClearLines()
|
||||
view.SetCallback(self.OnJumpToFoundLine)
|
||||
|
||||
view.AddLines(_("Searching for '%s' in '%s'\n\n") % (findString, dirString))
|
||||
|
||||
if os.path.isfile(dirString):
|
||||
try:
|
||||
docFile = file(dirString, 'r')
|
||||
lineNum = 1
|
||||
needToDisplayFilename = True
|
||||
line = docFile.readline()
|
||||
while line:
|
||||
count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
|
||||
if count != -1:
|
||||
if needToDisplayFilename:
|
||||
view.AddLines(FILENAME_MARKER + dirString + "\n")
|
||||
needToDisplayFilename = False
|
||||
line = repr(lineNum).zfill(4) + ":" + line
|
||||
view.AddLines(line)
|
||||
line = docFile.readline()
|
||||
lineNum += 1
|
||||
if not needToDisplayFilename:
|
||||
view.AddLines("\n")
|
||||
except IOError, (code, message):
|
||||
print _("Warning, unable to read file: '%s'. %s") % (dirString, message)
|
||||
else:
|
||||
# do search in files on disk
|
||||
for root, dirs, files in os.walk(dirString):
|
||||
if not searchSubfolders and root != dirString:
|
||||
break
|
||||
|
||||
for name in files:
|
||||
filename = os.path.join(root, name)
|
||||
try:
|
||||
docFile = file(filename, 'r')
|
||||
except IOError, (code, message):
|
||||
print _("Warning, unable to read file: '%s'. %s") % (filename, message)
|
||||
continue
|
||||
|
||||
lineNum = 1
|
||||
needToDisplayFilename = True
|
||||
line = docFile.readline()
|
||||
while line:
|
||||
count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
|
||||
if count != -1:
|
||||
if needToDisplayFilename:
|
||||
view.AddLines(FILENAME_MARKER + filename + "\n")
|
||||
needToDisplayFilename = False
|
||||
line = repr(lineNum).zfill(4) + ":" + line
|
||||
view.AddLines(line)
|
||||
line = docFile.readline()
|
||||
lineNum += 1
|
||||
if not needToDisplayFilename:
|
||||
view.AddLines("\n")
|
||||
|
||||
view.AddLines(_("Search completed."))
|
||||
wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
|
||||
|
||||
return True
|
||||
else:
|
||||
frame.Destroy()
|
||||
return False
|
||||
|
||||
|
||||
def SaveFindDirConfig(self, dirString, searchSubfolders):
|
||||
""" Save search dir patterns and flags to registry.
|
||||
|
||||
dirString = search directory
|
||||
searchSubfolders = Search subfolders
|
||||
"""
|
||||
config = wx.ConfigBase_Get()
|
||||
config.Write(FIND_MATCHDIR, dirString)
|
||||
config.WriteInt(FIND_MATCHDIRSUBFOLDERS, searchSubfolders)
|
||||
|
||||
|
||||
def ShowFindAllDialog(self):
|
||||
config = wx.ConfigBase_Get()
|
||||
|
||||
frame = wx.Dialog(None, -1, _("Find in Project"), size= (320,200))
|
||||
borderSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
|
||||
contentSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
lineSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
lineSizer.Add(wx.StaticText(frame, -1, _("Find what:")), 0, wx.ALIGN_CENTER | wx.RIGHT, HALF_SPACE)
|
||||
findCtrl = wx.TextCtrl(frame, -1, config.Read(FindService.FIND_MATCHPATTERN, ""), size=(200,-1))
|
||||
lineSizer.Add(findCtrl, 0, wx.LEFT, HALF_SPACE)
|
||||
contentSizer.Add(lineSizer, 0, wx.BOTTOM, SPACE)
|
||||
wholeWordCtrl = wx.CheckBox(frame, -1, _("Match whole word only"))
|
||||
wholeWordCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHWHOLEWORD, False))
|
||||
matchCaseCtrl = wx.CheckBox(frame, -1, _("Match case"))
|
||||
matchCaseCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHCASE, False))
|
||||
regExprCtrl = wx.CheckBox(frame, -1, _("Regular expression"))
|
||||
regExprCtrl.SetValue(config.ReadInt(FindService.FIND_MATCHREGEXPR, False))
|
||||
contentSizer.Add(wholeWordCtrl, 0, wx.BOTTOM, SPACE)
|
||||
contentSizer.Add(matchCaseCtrl, 0, wx.BOTTOM, SPACE)
|
||||
contentSizer.Add(regExprCtrl, 0, wx.BOTTOM, SPACE)
|
||||
borderSizer.Add(contentSizer, 0, wx.TOP | wx.BOTTOM | wx.LEFT, SPACE)
|
||||
|
||||
buttonSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
findBtn = wx.Button(frame, wx.ID_OK, _("Find"))
|
||||
findBtn.SetDefault()
|
||||
buttonSizer.Add(findBtn, 0, wx.BOTTOM, HALF_SPACE)
|
||||
buttonSizer.Add(wx.Button(frame, wx.ID_CANCEL, _("Cancel")), 0)
|
||||
borderSizer.Add(buttonSizer, 0, wx.ALL, SPACE)
|
||||
|
||||
frame.SetSizer(borderSizer)
|
||||
frame.Fit()
|
||||
|
||||
status = frame.ShowModal()
|
||||
|
||||
# save user choice state for this and other Find Dialog Boxes
|
||||
findString = findCtrl.GetValue()
|
||||
matchCase = matchCaseCtrl.IsChecked()
|
||||
wholeWord = wholeWordCtrl.IsChecked()
|
||||
regExpr = regExprCtrl.IsChecked()
|
||||
self.SaveFindConfig(findString, wholeWord, matchCase, regExpr)
|
||||
|
||||
if status == wx.ID_OK:
|
||||
frame.Destroy()
|
||||
|
||||
messageService = wx.GetApp().GetService(MessageService.MessageService)
|
||||
messageService.ShowWindow()
|
||||
|
||||
view = messageService.GetView()
|
||||
if view:
|
||||
view.ClearLines()
|
||||
view.SetCallback(self.OnJumpToFoundLine)
|
||||
|
||||
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
|
||||
projectFilenames = projectService.GetFilesFromCurrentProject()
|
||||
|
||||
projView = projectService.GetView()
|
||||
if projView:
|
||||
projName = wx.lib.docview.FileNameFromPath(projView.GetDocument().GetFilename())
|
||||
view.AddLines(PROJECT_MARKER + projName + "\n\n")
|
||||
|
||||
# do search in open files first, open files may have been modified and different from disk because it hasn't been saved
|
||||
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
|
||||
openDocsInProject = filter(lambda openDoc: openDoc.GetFilename() in projectFilenames, openDocs)
|
||||
for openDoc in openDocsInProject:
|
||||
if isinstance(openDoc, ProjectEditor.ProjectDocument): # don't search project model
|
||||
continue
|
||||
|
||||
openDocView = openDoc.GetFirstView()
|
||||
# some views don't have a in memory text object to search through such as the PM and the DM
|
||||
# even if they do have a non-text searchable object, how do we display it in the message window?
|
||||
if not hasattr(openDocView, "GetValue"):
|
||||
continue
|
||||
text = openDocView.GetValue()
|
||||
|
||||
lineNum = 1
|
||||
needToDisplayFilename = True
|
||||
start = 0
|
||||
end = 0
|
||||
count = 0
|
||||
while count != -1:
|
||||
count, foundStart, foundEnd, newText = self.DoFind(findString, None, text, start, end, True, matchCase, wholeWord, regExpr)
|
||||
if count != -1:
|
||||
if needToDisplayFilename:
|
||||
view.AddLines(FILENAME_MARKER + openDoc.GetFilename() + "\n")
|
||||
needToDisplayFilename = False
|
||||
|
||||
lineNum = openDocView.LineFromPosition(foundStart)
|
||||
line = repr(lineNum).zfill(4) + ":" + openDocView.GetLine(lineNum)
|
||||
view.AddLines(line)
|
||||
|
||||
start = text.find("\n", foundStart)
|
||||
if start == -1:
|
||||
break
|
||||
end = start
|
||||
if not needToDisplayFilename:
|
||||
view.AddLines("\n")
|
||||
openDocNames = map(lambda openDoc: openDoc.GetFilename(), openDocs)
|
||||
|
||||
# do search in closed files, skipping the open ones we already searched
|
||||
filenames = filter(lambda filename: filename not in openDocNames, projectFilenames)
|
||||
for filename in filenames:
|
||||
try:
|
||||
docFile = file(filename, 'r')
|
||||
except IOError, (code, message):
|
||||
print _("Warning, unable to read file: '%s'. %s") % (filename, message)
|
||||
continue
|
||||
|
||||
lineNum = 1
|
||||
needToDisplayFilename = True
|
||||
line = docFile.readline()
|
||||
while line:
|
||||
count, foundStart, foundEnd, newText = self.DoFind(findString, None, line, 0, 0, True, matchCase, wholeWord, regExpr)
|
||||
if count != -1:
|
||||
if needToDisplayFilename:
|
||||
view.AddLines(FILENAME_MARKER + filename + "\n")
|
||||
needToDisplayFilename = False
|
||||
line = repr(lineNum).zfill(4) + ":" + line
|
||||
view.AddLines(line)
|
||||
line = docFile.readline()
|
||||
lineNum += 1
|
||||
if not needToDisplayFilename:
|
||||
view.AddLines("\n")
|
||||
|
||||
view.AddLines(_("Search for '%s' completed.") % findString)
|
||||
|
||||
return True
|
||||
else:
|
||||
frame.Destroy()
|
||||
return False
|
||||
|
||||
|
||||
def OnJumpToFoundLine(self, event):
|
||||
messageService = wx.GetApp().GetService(MessageService.MessageService)
|
||||
lineText, pos = messageService.GetView().GetCurrLine()
|
||||
if lineText == "\n" or lineText.find(FILENAME_MARKER) != -1 or lineText.find(PROJECT_MARKER) != -1:
|
||||
return
|
||||
lineEnd = lineText.find(":")
|
||||
if lineEnd == -1:
|
||||
return
|
||||
else:
|
||||
lineNum = int(lineText[0:lineEnd])
|
||||
|
||||
text = messageService.GetView().GetText()
|
||||
curPos = messageService.GetView().GetCurrentPos()
|
||||
|
||||
startPos = text.rfind(FILENAME_MARKER, 0, curPos)
|
||||
endPos = text.find("\n", startPos)
|
||||
filename = text[startPos + len(FILENAME_MARKER):endPos]
|
||||
|
||||
foundView = None
|
||||
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
|
||||
for openDoc in openDocs:
|
||||
if openDoc.GetFilename() == filename:
|
||||
foundView = openDoc.GetFirstView()
|
||||
break
|
||||
|
||||
if not foundView:
|
||||
doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT)
|
||||
foundView = doc.GetFirstView()
|
||||
|
||||
if foundView:
|
||||
foundView.GetFrame().SetFocus()
|
||||
foundView.Activate()
|
||||
if hasattr(foundView, "GotoLine"):
|
||||
foundView.GotoLine(lineNum)
|
||||
startPos = foundView.PositionFromLine(lineNum)
|
||||
# wxBug: Need to select in reverse order, (end, start) to put cursor at head of line so positioning is correct
|
||||
# Also, if we use the correct positioning order (start, end), somehow, when we open a edit window for the first
|
||||
# time, we don't see the selection, it is scrolled off screen
|
||||
foundView.SetSelection(startPos - 1 + len(lineText[lineEnd:].rstrip("\n")), startPos)
|
||||
wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
|
||||
|
||||
|
93
wxPython/samples/ide/activegrid/tool/ImageEditor.py
Normal file
93
wxPython/samples/ide/activegrid/tool/ImageEditor.py
Normal file
@@ -0,0 +1,93 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: ImageEditor.py
|
||||
# Purpose: Image Editor for pydocview
|
||||
#
|
||||
# Author: Morgan Hua
|
||||
#
|
||||
# Created: 12/24/04
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# CVS-ID: $Id$
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
import wx
|
||||
import wx.lib.docview
|
||||
_ = wx.GetTranslation
|
||||
|
||||
|
||||
class ImageDocument(wx.lib.docview.Document):
|
||||
pass
|
||||
|
||||
|
||||
class ImageView(wx.lib.docview.View):
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Overridden methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def __init__(self):
|
||||
wx.lib.docview.View.__init__(self)
|
||||
self._ctrl = None
|
||||
|
||||
|
||||
def OnCreate(self, doc, flags):
|
||||
if len(doc.GetFilename()) == 0:
|
||||
wx.MessageBox(_("Cannot create a new image file.\n%s has no paint capability.") % wx.GetApp().GetAppName(),
|
||||
_("New Image File"),
|
||||
wx.OK | wx.ICON_EXCLAMATION)
|
||||
return False
|
||||
|
||||
frame = wx.GetApp().CreateDocumentFrame(self, doc, flags)
|
||||
panel = wx.Panel(frame, -1)
|
||||
bitmap = wx.Image(doc.GetFilename()).ConvertToBitmap()
|
||||
self._ctrl = wx.StaticBitmap(panel, -1, bitmap, (0,0), (bitmap.GetWidth(), bitmap.GetHeight()))
|
||||
panel.SetClientSize(bitmap.GetSize())
|
||||
frame.SetClientSize(panel.GetSize())
|
||||
self.Activate()
|
||||
return True
|
||||
|
||||
|
||||
def OnClose(self, deleteWindow = True):
|
||||
statusC = wx.GetApp().CloseChildDocuments(self.GetDocument())
|
||||
statusP = wx.lib.docview.View.OnClose(self, deleteWindow = deleteWindow)
|
||||
if not (statusC and statusP):
|
||||
return False
|
||||
self.Activate(False)
|
||||
if deleteWindow:
|
||||
self.GetFrame().Destroy()
|
||||
return True
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Icon Bitmaps - generated by encode_bitmaps.py
|
||||
#----------------------------------------------------------------------------
|
||||
from wx import ImageFromStream, BitmapFromImage
|
||||
from wx import EmptyIcon
|
||||
import cStringIO
|
||||
|
||||
|
||||
def getImageData():
|
||||
return \
|
||||
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x0f\x00\x00\x00\x0e\x08\x06\
|
||||
\x00\x00\x00\xf0\x8aF\xef\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
|
||||
\x00\x00\x97IDAT(\x91\x9d\x93Q\n\xc4 \x0cD\'\xda\xd3\xa9\xe9ac\xdb\x8bx\xa0\
|
||||
\xf4C"\xd6mAw@\x0c1/\t\x03\x123+\x16\x95s\x06\x00l\x00p\x9c\x17\xad\xc0\xe4<\
|
||||
R\x0c\xeaf\x81\x14\x83\xa6\x18\x1e[N\xc1)\x06\x15\x01Dj\xbc\x04\x7fi\x9b):\
|
||||
\xce\x8b\xf6\xbdN\xec\xfd\x99\x82G\xc8\xf4\xba\xf6\x9b9o\xfa\x81\xab9\x02\
|
||||
\x11i\xe6|6cf%\xe7A\xce\x83\x99\xd5\xc4\xccZJ\xd11\xd7\xd76\xd8\x8aJ)\xed\
|
||||
\xb6c\x8d,~\xc0\xe3\xe3L\xdc\xe0~\xcaJ\x03\xfa\xe7c\x98n\x01\x88\xc6k\xb1\
|
||||
\x83\x04\x87\x00\x00\x00\x00IEND\xaeB`\x82'
|
||||
|
||||
|
||||
def getImageBitmap():
|
||||
return BitmapFromImage(getImageImage())
|
||||
|
||||
def getImageImage():
|
||||
stream = cStringIO.StringIO(getImageData())
|
||||
return ImageFromStream(stream)
|
||||
|
||||
def getImageIcon():
|
||||
icon = EmptyIcon()
|
||||
icon.CopyFromBitmap(getImageBitmap())
|
||||
return icon
|
||||
|
91
wxPython/samples/ide/activegrid/tool/MarkerService.py
Normal file
91
wxPython/samples/ide/activegrid/tool/MarkerService.py
Normal file
@@ -0,0 +1,91 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: MarkerService.py
|
||||
# Purpose: Adding and removing line markers in text for easy searching
|
||||
#
|
||||
# Author: Morgan Hua
|
||||
#
|
||||
# Created: 10/6/03
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import wx
|
||||
import wx.stc
|
||||
import wx.lib.docview
|
||||
import wx.lib.pydocview
|
||||
import STCTextEditor
|
||||
_ = wx.GetTranslation
|
||||
|
||||
|
||||
class MarkerService(wx.lib.pydocview.DocService):
|
||||
MARKERTOGGLE_ID = wx.NewId()
|
||||
MARKERDELALL_ID = wx.NewId()
|
||||
MARKERNEXT_ID = wx.NewId()
|
||||
MARKERPREV_ID = wx.NewId()
|
||||
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
|
||||
if document and document.GetDocumentTemplate().GetDocumentType() != STCTextEditor.TextDocument:
|
||||
return
|
||||
if not document and wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
|
||||
return
|
||||
|
||||
editMenu = menuBar.GetMenu(menuBar.FindMenu(_("&Edit")))
|
||||
editMenu.AppendSeparator()
|
||||
editMenu.Append(MarkerService.MARKERTOGGLE_ID, _("Toggle &Marker\tCtrl+M"), _("Toggles a jump marker to text line"))
|
||||
wx.EVT_MENU(frame, MarkerService.MARKERTOGGLE_ID, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, MarkerService.MARKERTOGGLE_ID, frame.ProcessUpdateUIEvent)
|
||||
editMenu.Append(MarkerService.MARKERDELALL_ID, _("Clear Markers"), _("Removes all jump markers from selected file"))
|
||||
wx.EVT_MENU(frame, MarkerService.MARKERDELALL_ID, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, MarkerService.MARKERDELALL_ID, frame.ProcessUpdateUIEvent)
|
||||
editMenu.Append(MarkerService.MARKERNEXT_ID, _("Marker Next\tF4"), _("Moves to next marker in selected file"))
|
||||
wx.EVT_MENU(frame, MarkerService.MARKERNEXT_ID, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, MarkerService.MARKERNEXT_ID, frame.ProcessUpdateUIEvent)
|
||||
editMenu.Append(MarkerService.MARKERPREV_ID, _("Marker Previous\tShift+F4"), _("Moves to previous marker in selected file"))
|
||||
wx.EVT_MENU(frame, MarkerService.MARKERPREV_ID, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, MarkerService.MARKERPREV_ID, frame.ProcessUpdateUIEvent)
|
||||
|
||||
|
||||
def ProcessEvent(self, event):
|
||||
id = event.GetId()
|
||||
if id == MarkerService.MARKERTOGGLE_ID:
|
||||
wx.GetApp().GetDocumentManager().GetCurrentView().MarkerToggle()
|
||||
return True
|
||||
elif id == MarkerService.MARKERDELALL_ID:
|
||||
wx.GetApp().GetDocumentManager().GetCurrentView().MarkerDeleteAll()
|
||||
return True
|
||||
elif id == MarkerService.MARKERNEXT_ID:
|
||||
wx.GetApp().GetDocumentManager().GetCurrentView().MarkerNext()
|
||||
return True
|
||||
elif id == MarkerService.MARKERPREV_ID:
|
||||
wx.GetApp().GetDocumentManager().GetCurrentView().MarkerPrevious()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def ProcessUpdateUIEvent(self, event):
|
||||
id = event.GetId()
|
||||
if id == MarkerService.MARKERTOGGLE_ID:
|
||||
view = wx.GetApp().GetDocumentManager().GetCurrentView()
|
||||
event.Enable(hasattr(view, "MarkerToggle"))
|
||||
return True
|
||||
elif id == MarkerService.MARKERDELALL_ID:
|
||||
view = wx.GetApp().GetDocumentManager().GetCurrentView()
|
||||
event.Enable(hasattr(view, "MarkerDeleteAll") and view.GetMarkerCount())
|
||||
return True
|
||||
elif id == MarkerService.MARKERNEXT_ID:
|
||||
view = wx.GetApp().GetDocumentManager().GetCurrentView()
|
||||
event.Enable(hasattr(view, "MarkerNext") and view.GetMarkerCount())
|
||||
return True
|
||||
elif id == MarkerService.MARKERPREV_ID:
|
||||
view = wx.GetApp().GetDocumentManager().GetCurrentView()
|
||||
event.Enable(hasattr(view, "MarkerPrevious") and view.GetMarkerCount())
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
143
wxPython/samples/ide/activegrid/tool/MessageService.py
Normal file
143
wxPython/samples/ide/activegrid/tool/MessageService.py
Normal file
@@ -0,0 +1,143 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: MessageService.py
|
||||
# Purpose: Message View Service for pydocview
|
||||
#
|
||||
# Author: Morgan Hua
|
||||
#
|
||||
# Created: 9/2/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import wx
|
||||
import Service
|
||||
import STCTextEditor
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Overridden methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def _CreateControl(self, parent, id):
|
||||
txtCtrl = STCTextEditor.TextCtrl(parent, id)
|
||||
txtCtrl.SetMarginWidth(1, 0) # hide line numbers
|
||||
txtCtrl.SetReadOnly(True)
|
||||
|
||||
if wx.Platform == '__WXMSW__':
|
||||
font = "Courier New"
|
||||
else:
|
||||
font = "Courier"
|
||||
txtCtrl.SetFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font))
|
||||
txtCtrl.SetFontColor(wx.BLACK)
|
||||
txtCtrl.StyleClearAll()
|
||||
txtCtrl.UpdateStyles()
|
||||
|
||||
return txtCtrl
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Service specific methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def ClearLines(self):
|
||||
self.GetControl().SetReadOnly(False)
|
||||
self.GetControl().ClearAll()
|
||||
self.GetControl().SetReadOnly(True)
|
||||
|
||||
|
||||
def AddLines(self, text):
|
||||
self.GetControl().SetReadOnly(False)
|
||||
self.GetControl().AddText(text)
|
||||
self.GetControl().SetReadOnly(True)
|
||||
|
||||
|
||||
def GetText(self):
|
||||
return self.GetControl().GetText()
|
||||
|
||||
|
||||
def GetCurrentPos(self):
|
||||
return self.GetControl().GetCurrentPos()
|
||||
|
||||
|
||||
def GetCurrLine(self):
|
||||
return self.GetControl().GetCurLine()
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Callback Methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def SetCallback(self, callback):
|
||||
""" Sets in the event table for a doubleclick to invoke the given callback.
|
||||
Additional calls to this method overwrites the previous entry and only the last set callback will be invoked.
|
||||
"""
|
||||
wx.stc.EVT_STC_DOUBLECLICK(self.GetControl(), self.GetControl().GetId(), callback)
|
||||
|
||||
|
||||
|
||||
class MessageService(Service.Service):
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Constants
|
||||
#----------------------------------------------------------------------------
|
||||
SHOW_WINDOW = wx.NewId() # keep this line for each subclass, need unique ID for each Service
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Overridden methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def _CreateView(self):
|
||||
return MessageView(self)
|
||||
|
||||
|
520
wxPython/samples/ide/activegrid/tool/OutlineService.py
Normal file
520
wxPython/samples/ide/activegrid/tool/OutlineService.py
Normal file
@@ -0,0 +1,520 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: OutlineService.py
|
||||
# Purpose: Outline View Service for pydocview
|
||||
#
|
||||
# Author: Morgan Hua
|
||||
#
|
||||
# Created: 8/3/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import wx
|
||||
import wx.lib.docview
|
||||
import wx.lib.pydocview
|
||||
import Service
|
||||
_ = wx.GetTranslation
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Constants
|
||||
#----------------------------------------------------------------------------
|
||||
SORT_NONE = 0
|
||||
SORT_ASC = 1
|
||||
SORT_DESC = 2
|
||||
|
||||
class OutlineView(Service.ServiceView):
|
||||
""" Reusable Outline View for any document.
|
||||
As a default, it uses a modified tree control (OutlineTreeCtrl) that allows sorting.
|
||||
Subclass OutlineTreeCtrl to customize the tree control and call SetTreeCtrl to install a customized tree control.
|
||||
When an item is selected, the document view is called back (with DoSelectCallback) to highlight and display the corresponding item in the document view.
|
||||
"""
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Overridden methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def __init__(self, service):
|
||||
Service.ServiceView.__init__(self, service)
|
||||
self._actionOnSelect = True
|
||||
|
||||
|
||||
def _CreateControl(self, parent, id):
|
||||
treeCtrl = OutlineTreeCtrl(parent, id)
|
||||
wx.EVT_TREE_SEL_CHANGED(treeCtrl, treeCtrl.GetId(), self.DoSelection)
|
||||
wx.EVT_SET_FOCUS(treeCtrl, self.DoSelection)
|
||||
wx.EVT_ENTER_WINDOW(treeCtrl, treeCtrl.CallDoLoadOutlineCallback)
|
||||
wx.EVT_RIGHT_DOWN(treeCtrl, self.OnRightClick)
|
||||
|
||||
return treeCtrl
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Service specific methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def OnRightClick(self, event):
|
||||
menu = wx.Menu()
|
||||
|
||||
menu.AppendRadioItem(OutlineService.SORT_NONE, _("Unsorted"), _("Display items in original order"))
|
||||
menu.AppendRadioItem(OutlineService.SORT_ASC, _("Sort A-Z"), _("Display items in ascending order"))
|
||||
menu.AppendRadioItem(OutlineService.SORT_DESC, _("Sort Z-A"), _("Display items in descending order"))
|
||||
|
||||
config = wx.ConfigBase_Get()
|
||||
sort = config.ReadInt("OutlineSort", SORT_NONE)
|
||||
if sort == SORT_NONE:
|
||||
menu.Check(OutlineService.SORT_NONE, True)
|
||||
elif sort == SORT_ASC:
|
||||
menu.Check(OutlineService.SORT_ASC, True)
|
||||
elif sort == SORT_DESC:
|
||||
menu.Check(OutlineService.SORT_DESC, True)
|
||||
|
||||
self.GetControl().PopupMenu(menu, event.GetPosition())
|
||||
menu.Destroy()
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Tree Methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def DoSelection(self, event):
|
||||
if not self._actionOnSelect:
|
||||
return
|
||||
item = self.GetControl().GetSelection()
|
||||
if item:
|
||||
self.GetControl().CallDoSelectCallback(item)
|
||||
|
||||
|
||||
def ResumeActionOnSelect(self):
|
||||
self._actionOnSelect = True
|
||||
|
||||
|
||||
def StopActionOnSelect(self):
|
||||
self._actionOnSelect = False
|
||||
|
||||
|
||||
def SetTreeCtrl(self, tree):
|
||||
self.SetControl(tree)
|
||||
wx.EVT_TREE_SEL_CHANGED(self.GetControl(), self.GetControl().GetId(), self.DoSelection)
|
||||
wx.EVT_ENTER_WINDOW(self.GetControl(), treeCtrl.CallDoLoadOutlineCallback)
|
||||
wx.EVT_RIGHT_DOWN(self.GetControl(), self.OnRightClick)
|
||||
|
||||
|
||||
def GetTreeCtrl(self):
|
||||
return self.GetControl()
|
||||
|
||||
|
||||
def OnSort(self, sortOrder):
|
||||
treeCtrl = self.GetControl()
|
||||
treeCtrl.SetSortOrder(sortOrder)
|
||||
treeCtrl.SortAllChildren(treeCtrl.GetRootItem())
|
||||
|
||||
|
||||
def ClearTreeCtrl(self):
|
||||
if self.GetControl():
|
||||
self.GetControl().DeleteAllItems()
|
||||
|
||||
|
||||
def GetExpansionState(self):
|
||||
expanded = []
|
||||
|
||||
treeCtrl = self.GetControl()
|
||||
if not treeCtrl:
|
||||
return expanded
|
||||
|
||||
parentItem = treeCtrl.GetRootItem()
|
||||
|
||||
if not parentItem:
|
||||
return expanded
|
||||
|
||||
if not treeCtrl.IsExpanded(parentItem):
|
||||
return expanded
|
||||
|
||||
expanded.append(treeCtrl.GetItemText(parentItem))
|
||||
|
||||
(child, cookie) = treeCtrl.GetFirstChild(parentItem)
|
||||
while child.IsOk():
|
||||
if treeCtrl.IsExpanded(child):
|
||||
expanded.append(treeCtrl.GetItemText(child))
|
||||
(child, cookie) = treeCtrl.GetNextChild(parentItem, cookie)
|
||||
return expanded
|
||||
|
||||
|
||||
def SetExpansionState(self, expanded):
|
||||
if not expanded or len(expanded) == 0:
|
||||
return
|
||||
|
||||
treeCtrl = self.GetControl()
|
||||
parentItem = treeCtrl.GetRootItem()
|
||||
if expanded[0] != treeCtrl.GetItemText(parentItem):
|
||||
return
|
||||
|
||||
(child, cookie) = treeCtrl.GetFirstChild(parentItem)
|
||||
while child.IsOk():
|
||||
if treeCtrl.GetItemText(child) in expanded:
|
||||
treeCtrl.Expand(child)
|
||||
(child, cookie) = treeCtrl.GetNextChild(parentItem, cookie)
|
||||
|
||||
# wxBug: This causes a crash, tried using ScrollTo which crashed as well. Then tried calling it with wx.CallAfter and that crashed as well, with both EnsureVisible and ScrollTo
|
||||
# self.GetControl().EnsureVisible(self.GetControl().GetRootItem())
|
||||
# So doing the following massive hack which forces the treectrl to scroll up to the top item
|
||||
treeCtrl.Collapse(parentItem)
|
||||
treeCtrl.Expand(parentItem)
|
||||
|
||||
|
||||
class OutlineTreeCtrl(wx.TreeCtrl):
|
||||
""" Default Tree Control Class for OutlineView.
|
||||
This class has the added functionality of sorting by the labels
|
||||
"""
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Constants
|
||||
#----------------------------------------------------------------------------
|
||||
ORIG_ORDER = 0
|
||||
VIEW = 1
|
||||
CALLBACKDATA = 2
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Overridden Methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def __init__(self, parent, id, style=wx.TR_HAS_BUTTONS|wx.TR_DEFAULT_STYLE):
|
||||
wx.TreeCtrl.__init__(self, parent, id, style = style)
|
||||
self._origOrderIndex = 0
|
||||
self._sortOrder = SORT_NONE
|
||||
|
||||
|
||||
def DeleteAllItems(self):
|
||||
self._origOrderIndex = 0
|
||||
wx.TreeCtrl.DeleteAllItems(self)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Sort Methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def SetSortOrder(self, sortOrder = SORT_NONE):
|
||||
""" Sort Order constants are defined at top of file """
|
||||
self._sortOrder = sortOrder
|
||||
|
||||
|
||||
def OnCompareItems(self, item1, item2):
|
||||
if self._sortOrder == SORT_ASC:
|
||||
return cmp(self.GetItemText(item1).lower(), self.GetItemText(item2).lower()) # sort A-Z
|
||||
elif self._sortOrder == SORT_DESC:
|
||||
return cmp(self.GetItemText(item2).lower(), self.GetItemText(item1).lower()) # sort Z-A
|
||||
else:
|
||||
return (self.GetPyData(item1)[self.ORIG_ORDER] > self.GetPyData(item2)[self.ORIG_ORDER]) # unsorted
|
||||
|
||||
|
||||
def SortAllChildren(self, parentItem):
|
||||
if parentItem and self.GetChildrenCount(parentItem, False):
|
||||
self.SortChildren(parentItem)
|
||||
(child, cookie) = self.GetFirstChild(parentItem)
|
||||
while child.IsOk():
|
||||
self.SortAllChildren(child)
|
||||
(child, cookie) = self.GetNextChild(parentItem, cookie)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Select Callback Methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def CallDoSelectCallback(self, item):
|
||||
""" Invoke the DoSelectCallback of the given view to highlight text in the document view
|
||||
"""
|
||||
data = self.GetPyData(item)
|
||||
if not data:
|
||||
return
|
||||
|
||||
view = data[self.VIEW]
|
||||
cbdata = data[self.CALLBACKDATA]
|
||||
if view:
|
||||
view.DoSelectCallback(cbdata)
|
||||
|
||||
|
||||
def SelectClosestItem(self, position):
|
||||
tree = self
|
||||
distances = []
|
||||
items = []
|
||||
self.FindDistanceToTreeItems(tree.GetRootItem(), position, distances, items)
|
||||
mindist = 1000000
|
||||
mindex = -1
|
||||
for index in range(0, len(distances)):
|
||||
if distances[index] <= mindist:
|
||||
mindist = distances[index]
|
||||
mindex = index
|
||||
if mindex != -1:
|
||||
item = items[mindex]
|
||||
self.EnsureVisible(item)
|
||||
os_view = wx.GetApp().GetService(OutlineService).GetView()
|
||||
if os_view:
|
||||
os_view.StopActionOnSelect()
|
||||
self.SelectItem(item)
|
||||
if os_view:
|
||||
os_view.ResumeActionOnSelect()
|
||||
|
||||
|
||||
def FindDistanceToTreeItems(self, item, position, distances, items):
|
||||
data = self.GetPyData(item)
|
||||
this_dist = 1000000
|
||||
if data and data[2]:
|
||||
positionTuple = data[2]
|
||||
if position >= positionTuple[1]:
|
||||
items.append(item)
|
||||
distances.append(position - positionTuple[1])
|
||||
|
||||
if self.ItemHasChildren(item):
|
||||
child, cookie = self.GetFirstChild(item)
|
||||
while child and child.IsOk():
|
||||
self.FindDistanceToTreeItems(child, position, distances, items)
|
||||
child, cookie = self.GetNextChild(item, cookie)
|
||||
return False
|
||||
|
||||
|
||||
def SetDoSelectCallback(self, item, view, callbackdata):
|
||||
""" When an item in the outline view is selected,
|
||||
a method is called to select the respective text in the document view.
|
||||
The view must define the method DoSelectCallback(self, data) in order for this to work
|
||||
"""
|
||||
self.SetPyData(item, (self._origOrderIndex, view, callbackdata))
|
||||
self._origOrderIndex = self._origOrderIndex + 1
|
||||
|
||||
|
||||
def CallDoLoadOutlineCallback(self, event):
|
||||
""" Invoke the DoLoadOutlineCallback
|
||||
"""
|
||||
rootItem = self.GetRootItem()
|
||||
if rootItem:
|
||||
data = self.GetPyData(rootItem)
|
||||
if data:
|
||||
view = data[self.VIEW]
|
||||
if view and view.DoLoadOutlineCallback():
|
||||
self.SortAllChildren(self.GetRootItem())
|
||||
|
||||
|
||||
def GetCallbackView(self):
|
||||
rootItem = self.GetRootItem()
|
||||
if rootItem:
|
||||
return self.GetPyData(rootItem)[self.VIEW]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class OutlineService(Service.Service):
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Constants
|
||||
#----------------------------------------------------------------------------
|
||||
SHOW_WINDOW = wx.NewId() # keep this line for each subclass, need unique ID for each Service
|
||||
SORT = wx.NewId()
|
||||
SORT_ASC = wx.NewId()
|
||||
SORT_DESC = wx.NewId()
|
||||
SORT_NONE = wx.NewId()
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Overridden methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def __init__(self, serviceName, embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_BOTTOM):
|
||||
Service.Service.__init__(self, serviceName, embeddedWindowLocation)
|
||||
self._validTemplates = []
|
||||
|
||||
|
||||
def _CreateView(self):
|
||||
return OutlineView(self)
|
||||
|
||||
|
||||
def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
|
||||
Service.Service.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
|
||||
|
||||
wx.EVT_MENU(frame, OutlineService.SORT_ASC, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, OutlineService.SORT_ASC, frame.ProcessUpdateUIEvent)
|
||||
wx.EVT_MENU(frame, OutlineService.SORT_DESC, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, OutlineService.SORT_DESC, frame.ProcessUpdateUIEvent)
|
||||
wx.EVT_MENU(frame, OutlineService.SORT_NONE, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, OutlineService.SORT_NONE, frame.ProcessUpdateUIEvent)
|
||||
|
||||
|
||||
if wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
|
||||
return True
|
||||
|
||||
viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View")))
|
||||
self._outlineSortMenu = wx.Menu()
|
||||
self._outlineSortMenu.AppendRadioItem(OutlineService.SORT_NONE, _("Unsorted"), _("Display items in original order"))
|
||||
self._outlineSortMenu.AppendRadioItem(OutlineService.SORT_ASC, _("Sort A-Z"), _("Display items in ascending order"))
|
||||
self._outlineSortMenu.AppendRadioItem(OutlineService.SORT_DESC, _("Sort Z-A"), _("Display items in descending order"))
|
||||
viewMenu.AppendMenu(wx.NewId(), _("Outline Sort"), self._outlineSortMenu)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Event Processing Methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def ProcessEvent(self, event):
|
||||
if Service.Service.ProcessEvent(self, event):
|
||||
return True
|
||||
|
||||
id = event.GetId()
|
||||
if id == OutlineService.SORT_ASC:
|
||||
self.OnSort(event)
|
||||
return True
|
||||
elif id == OutlineService.SORT_DESC:
|
||||
self.OnSort(event)
|
||||
return True
|
||||
elif id == OutlineService.SORT_NONE:
|
||||
self.OnSort(event)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def ProcessUpdateUIEvent(self, event):
|
||||
if Service.Service.ProcessUpdateUIEvent(self, event):
|
||||
return True
|
||||
|
||||
id = event.GetId()
|
||||
if id == OutlineService.SORT_ASC:
|
||||
event.Enable(True)
|
||||
|
||||
config = wx.ConfigBase_Get()
|
||||
sort = config.ReadInt("OutlineSort", SORT_NONE)
|
||||
if sort == SORT_ASC:
|
||||
self._outlineSortMenu.Check(OutlineService.SORT_ASC, True)
|
||||
else:
|
||||
self._outlineSortMenu.Check(OutlineService.SORT_ASC, False)
|
||||
|
||||
return True
|
||||
elif id == OutlineService.SORT_DESC:
|
||||
event.Enable(True)
|
||||
|
||||
config = wx.ConfigBase_Get()
|
||||
sort = config.ReadInt("OutlineSort", SORT_NONE)
|
||||
if sort == SORT_DESC:
|
||||
self._outlineSortMenu.Check(OutlineService.SORT_DESC, True)
|
||||
else:
|
||||
self._outlineSortMenu.Check(OutlineService.SORT_DESC, False)
|
||||
|
||||
return True
|
||||
elif id == OutlineService.SORT_NONE:
|
||||
event.Enable(True)
|
||||
|
||||
config = wx.ConfigBase_Get()
|
||||
sort = config.ReadInt("OutlineSort", SORT_NONE)
|
||||
if sort == SORT_NONE:
|
||||
self._outlineSortMenu.Check(OutlineService.SORT_NONE, True)
|
||||
else:
|
||||
self._outlineSortMenu.Check(OutlineService.SORT_NONE, False)
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def OnSort(self, event):
|
||||
id = event.GetId()
|
||||
if id == OutlineService.SORT_ASC:
|
||||
wx.ConfigBase_Get().WriteInt("OutlineSort", SORT_ASC)
|
||||
self.GetView().OnSort(SORT_ASC)
|
||||
return True
|
||||
elif id == OutlineService.SORT_DESC:
|
||||
wx.ConfigBase_Get().WriteInt("OutlineSort", SORT_DESC)
|
||||
self.GetView().OnSort(SORT_DESC)
|
||||
return True
|
||||
elif id == OutlineService.SORT_NONE:
|
||||
wx.ConfigBase_Get().WriteInt("OutlineSort", SORT_NONE)
|
||||
self.GetView().OnSort(SORT_NONE)
|
||||
return True
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Service specific methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def LoadOutline(self, view, position=-1, force=False):
|
||||
if not self.GetView():
|
||||
return
|
||||
|
||||
self.SaveExpansionState()
|
||||
if view.DoLoadOutlineCallback(force=force):
|
||||
self.GetView().OnSort(wx.ConfigBase_Get().ReadInt("OutlineSort", SORT_NONE))
|
||||
self.LoadExpansionState()
|
||||
if position >= 0:
|
||||
self.SyncToPosition(position)
|
||||
|
||||
|
||||
def SyncToPosition(self, position):
|
||||
if not self.GetView():
|
||||
return
|
||||
self.GetView().GetTreeCtrl().SelectClosestItem(position)
|
||||
|
||||
|
||||
def OnCloseFrame(self, event):
|
||||
Service.Service.OnCloseFrame(self, event)
|
||||
self.SaveExpansionState(clear = True)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def SaveExpansionState(self, clear = False):
|
||||
if clear:
|
||||
expanded = []
|
||||
elif self.GetView():
|
||||
expanded = self.GetView().GetExpansionState()
|
||||
wx.ConfigBase_Get().Write("OutlineLastExpanded", expanded.__repr__())
|
||||
|
||||
|
||||
def LoadExpansionState(self):
|
||||
expanded = wx.ConfigBase_Get().Read("OutlineLastExpanded")
|
||||
if expanded:
|
||||
self.GetView().SetExpansionState(eval(expanded))
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Timer Methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def StartBackgroundTimer(self):
|
||||
self._timer = wx.PyTimer(self.DoBackgroundRefresh)
|
||||
self._timer.Start(250)
|
||||
|
||||
|
||||
def DoBackgroundRefresh(self):
|
||||
""" Refresh the outline view periodically """
|
||||
self._timer.Stop()
|
||||
|
||||
foundRegisteredView = False
|
||||
if self.GetView():
|
||||
currView = wx.GetApp().GetDocumentManager().GetCurrentView()
|
||||
if currView:
|
||||
for template in self._validTemplates:
|
||||
type = template.GetViewType()
|
||||
if isinstance(currView, type):
|
||||
self.LoadOutline(currView)
|
||||
foundRegisteredView = True
|
||||
break
|
||||
|
||||
if not foundRegisteredView:
|
||||
self.GetView().ClearTreeCtrl()
|
||||
|
||||
self._timer.Start(1000) # 1 second interval
|
||||
|
||||
|
||||
def AddTemplateForBackgroundHandler(self, template):
|
||||
self._validTemplates.append(template)
|
||||
|
||||
|
||||
def GetTemplatesForBackgroundHandler(self):
|
||||
return self._validTemplates
|
||||
|
||||
|
||||
def RemoveTemplateForBackgroundHandler(self, template):
|
||||
self._validTemplates.remove(template)
|
||||
|
297
wxPython/samples/ide/activegrid/tool/PHPEditor.py
Normal file
297
wxPython/samples/ide/activegrid/tool/PHPEditor.py
Normal file
@@ -0,0 +1,297 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: PHPEditor.py
|
||||
# Purpose: PHP Script Editor for pydocview tbat uses the Styled Text Control
|
||||
#
|
||||
# Author: Morgan Hua
|
||||
#
|
||||
# Created: 1/4/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import wx
|
||||
import string
|
||||
import STCTextEditor
|
||||
import CodeEditor
|
||||
import OutlineService
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
class PHPDocument(CodeEditor.CodeDocument):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class PHPView(CodeEditor.CodeView):
|
||||
|
||||
|
||||
def GetCtrlClass(self):
|
||||
""" Used in split window to instantiate new instances """
|
||||
return PHPCtrl
|
||||
|
||||
|
||||
def GetAutoCompleteHint(self):
|
||||
pos = self.GetCtrl().GetCurrentPos()
|
||||
if pos == 0:
|
||||
return None, None
|
||||
|
||||
validLetters = string.letters + string.digits + '_$'
|
||||
word = ''
|
||||
while (True):
|
||||
pos = pos - 1
|
||||
if pos < 0:
|
||||
break
|
||||
char = chr(self.GetCtrl().GetCharAt(pos))
|
||||
if char not in validLetters:
|
||||
break
|
||||
word = char + word
|
||||
|
||||
return None, word
|
||||
|
||||
|
||||
def GetAutoCompleteDefaultKeywords(self):
|
||||
return PHPKEYWORDS
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Methods for OutlineService
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def DoLoadOutlineCallback(self, force=False):
|
||||
outlineService = wx.GetApp().GetService(OutlineService.OutlineService)
|
||||
if not outlineService:
|
||||
return False
|
||||
|
||||
outlineView = outlineService.GetView()
|
||||
if not outlineView:
|
||||
return False
|
||||
|
||||
treeCtrl = outlineView.GetTreeCtrl()
|
||||
if not treeCtrl:
|
||||
return False
|
||||
|
||||
view = treeCtrl.GetCallbackView()
|
||||
newCheckSum = self.GenCheckSum()
|
||||
if not force:
|
||||
if view and view is self:
|
||||
if self._checkSum == newCheckSum:
|
||||
return False
|
||||
self._checkSum = newCheckSum
|
||||
|
||||
treeCtrl.DeleteAllItems()
|
||||
|
||||
document = self.GetDocument()
|
||||
if not document:
|
||||
return True
|
||||
|
||||
filename = document.GetFilename()
|
||||
if filename:
|
||||
rootItem = treeCtrl.AddRoot(os.path.basename(filename))
|
||||
treeCtrl.SetDoSelectCallback(rootItem, self, None)
|
||||
else:
|
||||
return True
|
||||
|
||||
text = self.GetValue()
|
||||
if not text:
|
||||
return True
|
||||
|
||||
INTERFACE_PATTERN = 'interface[ \t]+\w+'
|
||||
CLASS_PATTERN = '((final|abstract)[ \t]+)?((public|private|protected)[ \t]+)?(static[ \t]+)?class[ \t]+\w+((implements|extends)\w+)?'
|
||||
FUNCTION_PATTERN = '(abstract[ \t]+)?((public|private|protected)[ \t]+)?(static[ \t]+)?function[ \t]+?\w+\(.*?\)'
|
||||
interfacePat = re.compile(INTERFACE_PATTERN, re.M|re.S)
|
||||
classPat = re.compile(CLASS_PATTERN, re.M|re.S)
|
||||
funcPat= re.compile(FUNCTION_PATTERN, re.M|re.S)
|
||||
pattern = re.compile('^[ \t]*('+ CLASS_PATTERN + '.*?{|' + FUNCTION_PATTERN + '|' + INTERFACE_PATTERN +'\s*?{).*?$', re.M|re.S)
|
||||
|
||||
iter = pattern.finditer(text)
|
||||
indentStack = [(0, rootItem)]
|
||||
for pattern in iter:
|
||||
line = pattern.string[pattern.start(0):pattern.end(0)]
|
||||
foundLine = classPat.search(line)
|
||||
if foundLine:
|
||||
indent = foundLine.start(0)
|
||||
itemStr = foundLine.string[foundLine.start(0):foundLine.end(0)]
|
||||
else:
|
||||
foundLine = funcPat.search(line)
|
||||
if foundLine:
|
||||
indent = foundLine.start(0)
|
||||
itemStr = foundLine.string[foundLine.start(0):foundLine.end(0)]
|
||||
else:
|
||||
foundLine = interfacePat.search(line)
|
||||
if foundLine:
|
||||
indent = foundLine.start(0)
|
||||
itemStr = foundLine.string[foundLine.start(0):foundLine.end(0)]
|
||||
|
||||
if indent == 0:
|
||||
parentItem = rootItem
|
||||
else:
|
||||
lastItem = indentStack.pop()
|
||||
while lastItem[0] >= indent:
|
||||
lastItem = indentStack.pop()
|
||||
indentStack.append(lastItem)
|
||||
parentItem = lastItem[1]
|
||||
|
||||
item = treeCtrl.AppendItem(parentItem, itemStr)
|
||||
treeCtrl.SetDoSelectCallback(item, self, (pattern.end(0), pattern.start(0) + indent)) # select in reverse order because we want the cursor to be at the start of the line so it wouldn't scroll to the right
|
||||
indentStack.append((indent, item))
|
||||
|
||||
treeCtrl.Expand(rootItem)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class PHPService(CodeEditor.CodeService):
|
||||
|
||||
|
||||
def __init__(self):
|
||||
CodeEditor.CodeService.__init__(self)
|
||||
|
||||
|
||||
class PHPCtrl(CodeEditor.CodeCtrl):
|
||||
|
||||
|
||||
def __init__(self, parent, ID = -1, style = wx.NO_FULL_REPAINT_ON_RESIZE):
|
||||
CodeEditor.CodeCtrl.__init__(self, parent, ID, style)
|
||||
self.SetLexer(wx.stc.STC_LEX_HTML)
|
||||
self.SetStyleBits(7)
|
||||
self.SetKeyWords(4, string.join(PHPKEYWORDS))
|
||||
self.SetProperty("fold.html", "1")
|
||||
|
||||
|
||||
def CanWordWrap(self):
|
||||
return True
|
||||
|
||||
|
||||
def SetViewDefaults(self):
|
||||
CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "PHP", hasWordWrap = True, hasTabs = True)
|
||||
|
||||
|
||||
def GetFontAndColorFromConfig(self):
|
||||
return CodeEditor.CodeCtrl.GetFontAndColorFromConfig(self, configPrefix = "PHP")
|
||||
|
||||
|
||||
def UpdateStyles(self):
|
||||
CodeEditor.CodeCtrl.UpdateStyles(self)
|
||||
|
||||
if not self.GetFont():
|
||||
return
|
||||
|
||||
faces = { 'font' : self.GetFont().GetFaceName(),
|
||||
'size' : self.GetFont().GetPointSize(),
|
||||
'size2': self.GetFont().GetPointSize() - 2,
|
||||
'color' : "%02x%02x%02x" % (self.GetFontColor().Red(), self.GetFontColor().Green(), self.GetFontColor().Blue())
|
||||
}
|
||||
|
||||
|
||||
# HTML Styles
|
||||
# White space
|
||||
self.StyleSetSpec(wx.stc.STC_H_DEFAULT, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
# Comment
|
||||
self.StyleSetSpec(wx.stc.STC_H_COMMENT, "face:%(font)s,fore:#007F00,italic,face:%(font)s,size:%(size)d" % faces)
|
||||
# Number
|
||||
self.StyleSetSpec(wx.stc.STC_H_NUMBER, "face:%(font)s,fore:#007F7F,size:%(size)d" % faces)
|
||||
# String
|
||||
self.StyleSetSpec(wx.stc.STC_H_SINGLESTRING, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_H_DOUBLESTRING, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
# 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)
|
||||
|
||||
|
||||
# PHP Styles
|
||||
self.StyleSetSpec(wx.stc.STC_HPHP_DEFAULT, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_HPHP_COMMENT, "face:%(font)s,fore:#007F00,italic,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_HPHP_COMMENTLINE, "face:%(font)s,fore:#007F00,italic,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_HPHP_NUMBER, "face:%(font)s,fore:#007F7F,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_HPHP_SIMPLESTRING, "face:%(font)s,fore:#7F007F,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_HPHP_HSTRING, "face:%(font)s,fore7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_HPHP_HSTRING_VARIABLE, "face:%(font)s,fore:#007F7F,italic,bold,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_HPHP_VARIABLE, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_HPHP_OPERATOR, "face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_HPHP_WORD, "face:%(font)s,fore:#00007F,bold,size:%(size)d" % faces) # keyword
|
||||
|
||||
|
||||
class PHPOptionsPanel(STCTextEditor.TextOptionsPanel):
|
||||
|
||||
def __init__(self, parent, id):
|
||||
STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "PHP", label = "PHP", hasWordWrap = True, hasTabs = True)
|
||||
|
||||
|
||||
PHPKEYWORDS = [
|
||||
"and", "or", "xor", "__FILE__", "exception", "__LINE__", "array", "as", "break", "case",
|
||||
"class", "const", "continue", "declare", "default", "die", "do", "echo", "else", "elseif",
|
||||
"empty", "enddeclare", "endfor", "endforeach", "endif", "endswith", "endwhile", "eval",
|
||||
"exit", "extends", "for", "foreach", "function", "global", "if", "include", "include_once",
|
||||
"isset", "list", "new", "print", "require", "require_once", "return", "static", "switch",
|
||||
"unset", "use", "var", "while", "__FUNCTION__", "__CLASS__", "__METHOD__", "final", "php_user_filter",
|
||||
"interface", "implements", "extends", "public", "private", "protected", "abstract", "clone", "try", "catch",
|
||||
"throw", "cfunction", "old_function",
|
||||
"$_SERVER", "$_ENV", "$_COOKIE", "$_GET", "$_POST", "$_FILES", "$_REQUEST", "$_SESSION", "$GLOBALS", "$php_errormsg",
|
||||
"PHP_VERSION", "PHP_OS", "PHP_EOL", "DEFAULT_INCLUDE_PATH", "PEAR_INSTALL_DIR", "PEAR_EXTENSION_DIR",
|
||||
"PHP_EXTENSION_DIR", "PHP_BINDIR", "PHP_LIBDIR", "PHP_DATADIR", "PHP_SYSCONFDIR", "PHP_LOCALSTATEDIR",
|
||||
"PHP_CONFIG_FILE_PATH", "PHP_OUTPUT_HANDLER_START", "PHP_OUTPUT_HANDLER_CONT", "PHP_OUTPUT_HANDLER_END",
|
||||
"E_ERROR", "E_WARNING", "E_PARSE", "E_NOTICE", "E_CORE_ERROR", "E_CORE_WARNING", "E_COMPILE_ERROR",
|
||||
"E_COMPILE_WARNING", "E_USER_ERROR", "E_USER_WARNING", "E_USER_NOTICE", "E_ALL", "E_STRICT",
|
||||
"TRUE", "FALSE", "NULL", "ZEND_THREAD_SAFE",
|
||||
"EXTR_OVERWRITE", "EXTR_SKIP", "EXTR_PREFIX_SAME", "EXTR_PREFIX_ALL", "EXTR_PREFIX_INVALID",
|
||||
"EXTR_PREFIX_IF_EXISTS", "EXTR_IF_EXISTS", "SORT_ASC", "SORT_DESC", "SORT_REGULAR", "SORT_NUMERIC",
|
||||
"SORT_STRING", "CASE_LOWER", "CASE_UPPER", "COUNT_NORMAL", "COUNT_RECURSIVE", "ASSERT_ACTIVE",
|
||||
"ASSERT_CALLBACK", "ASSERT_BAIL", "ASSERT_WARNING", "ASSERT_QUIET_EVAL", "CONNECTION_ABORTED",
|
||||
"CONNECTION_NORMAL", "CONNECTION_TIMEOUT", "INI_USER", "INI_PERDIR", "INI_SYSTEM", "INI_ALL",
|
||||
"M_E", "M_LOG2E", "M_LOG10E", "M_LN2", "M_LN10", "M_PI", "M_PI_2", "M_PI_4", "M_1_PI", "M_2_PI",
|
||||
"M_2_SQRTPI", "M_SQRT2", "M_SQRT1_2", "CRYPT_SALT_LENGTH", "CRYPT_STD_DES", "CRYPT_EXT_DES", "CRYPT_MD5",
|
||||
"CRYPT_BLOWFISH", "DIRECTORY_SEPARATOR", "SEEK_SET", "SEEK_CUR", "SEEK_END", "LOCK_SH", "LOCK_EX", "LOCK_UN",
|
||||
"LOCK_NB", "HTML_SPECIALCHARS", "HTML_ENTITIES", "ENT_COMPAT", "ENT_QUOTES", "ENT_NOQUOTES", "INFO_GENERAL",
|
||||
"INFO_CREDITS", "INFO_CONFIGURATION", "INFO_MODULES", "INFO_ENVIRONMENT", "INFO_VARIABLES", "INFO_LICENSE",
|
||||
"INFO_ALL", "CREDITS_GROUP", "CREDITS_GENERAL", "CREDITS_SAPI", "CREDITS_MODULES", "CREDITS_DOCS",
|
||||
"CREDITS_FULLPAGE", "CREDITS_QA", "CREDITS_ALL", "STR_PAD_LEFT", "STR_PAD_RIGHT", "STR_PAD_BOTH",
|
||||
"PATHINFO_DIRNAME", "PATHINFO_BASENAME", "PATHINFO_EXTENSION", "PATH_SEPARATOR", "CHAR_MAX", "LC_CTYPE",
|
||||
"LC_NUMERIC", "LC_TIME", "LC_COLLATE", "LC_MONETARY", "LC_ALL", "LC_MESSAGES", "ABDAY_1", "ABDAY_2",
|
||||
"ABDAY_3", "ABDAY_4", "ABDAY_5", "ABDAY_6", "ABDAY_7", "DAY_1", "DAY_2", "DAY_3", "DAY_4", "DAY_5",
|
||||
"DAY_6", "DAY_7", "ABMON_1", "ABMON_2", "ABMON_3", "ABMON_4", "ABMON_5", "ABMON_6", "ABMON_7", "ABMON_8",
|
||||
"ABMON_9", "ABMON_10", "ABMON_11", "ABMON_12", "MON_1", "MON_2", "MON_3", "MON_4", "MON_5", "MON_6", "MON_7",
|
||||
"MON_8", "MON_9", "MON_10", "MON_11", "MON_12", "AM_STR", "PM_STR", "D_T_FMT", "D_FMT", "T_FMT", "T_FMT_AMPM",
|
||||
"ERA", "ERA_YEAR", "ERA_D_T_FMT", "ERA_D_FMT", "ERA_T_FMT", "ALT_DIGITS", "INT_CURR_SYMBOL", "CURRENCY_SYMBOL",
|
||||
"CRNCYSTR", "MON_DECIMAL_POINT", "MON_THOUSANDS_SEP", "MON_GROUPING", "POSITIVE_SIGN", "NEGATIVE_SIGN",
|
||||
"INT_FRAC_DIGITS", "FRAC_DIGITS", "P_CS_PRECEDES", "P_SEP_BY_SPACE", "N_CS_PRECEDES", "N_SEP_BY_SPACE",
|
||||
"P_SIGN_POSN", "N_SIGN_POSN", "DECIMAL_POINT", "RADIXCHAR", "THOUSANDS_SEP", "THOUSEP", "GROUPING",
|
||||
"YESEXPR", "NOEXPR", "YESSTR", "NOSTR", "CODESET", "LOG_EMERG", "LOG_ALERT", "LOG_CRIT", "LOG_ERR",
|
||||
"LOG_WARNING", "LOG_NOTICE", "LOG_INFO", "LOG_DEBUG", "LOG_KERN", "LOG_USER", "LOG_MAIL", "LOG_DAEMON",
|
||||
"LOG_AUTH", "LOG_SYSLOG", "LOG_LPR", "LOG_NEWS", "LOG_UUCP", "LOG_CRON", "LOG_AUTHPRIV", "LOG_LOCAL0",
|
||||
"LOG_LOCAL1", "LOG_LOCAL2", "LOG_LOCAL3", "LOG_LOCAL4", "LOG_LOCAL5", "LOG_LOCAL6", "LOG_LOCAL7",
|
||||
"LOG_PID", "LOG_CONS", "LOG_ODELAY", "LOG_NDELAY", "LOG_NOWAIT", "LOG_PERROR"
|
||||
]
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Icon Bitmaps - generated by encode_bitmaps.py
|
||||
#----------------------------------------------------------------------------
|
||||
from wx import ImageFromStream, BitmapFromImage
|
||||
from wx import EmptyIcon
|
||||
import cStringIO
|
||||
|
||||
|
||||
def getPHPData():
|
||||
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{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'
|
||||
|
||||
def getPHPBitmap():
|
||||
return BitmapFromImage(getPHPImage())
|
||||
|
||||
def getPHPImage():
|
||||
stream = cStringIO.StringIO(getPHPData())
|
||||
return ImageFromStream(stream)
|
||||
|
||||
def getPHPIcon():
|
||||
icon = EmptyIcon()
|
||||
icon.CopyFromBitmap(getPHPBitmap())
|
||||
return icon
|
425
wxPython/samples/ide/activegrid/tool/PerlEditor.py
Normal file
425
wxPython/samples/ide/activegrid/tool/PerlEditor.py
Normal file
@@ -0,0 +1,425 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: PerlEditor.py
|
||||
# Purpose: Perl Script Editor for pydocview tbat uses the Styled Text Control
|
||||
#
|
||||
# Author: Morgan Hua
|
||||
#
|
||||
# Created: 1/5/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import wx
|
||||
import string
|
||||
import STCTextEditor
|
||||
import CodeEditor
|
||||
|
||||
|
||||
class PerlDocument(CodeEditor.CodeDocument):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class PerlView(CodeEditor.CodeView):
|
||||
|
||||
|
||||
def GetCtrlClass(self):
|
||||
""" Used in split window to instantiate new instances """
|
||||
return PerlCtrl
|
||||
|
||||
|
||||
def GetAutoCompleteHint(self):
|
||||
pos = self.GetCtrl().GetCurrentPos()
|
||||
if pos == 0:
|
||||
return None, None
|
||||
|
||||
validLetters = string.letters + string.digits + '_/'
|
||||
word = ''
|
||||
while (True):
|
||||
pos = pos - 1
|
||||
if pos < 0:
|
||||
break
|
||||
char = chr(self.GetCtrl().GetCharAt(pos))
|
||||
if char not in validLetters:
|
||||
break
|
||||
word = char + word
|
||||
|
||||
return None, word
|
||||
|
||||
|
||||
def GetAutoCompleteDefaultKeywords(self):
|
||||
return PERLKEYWORDS
|
||||
|
||||
|
||||
class PerlService(CodeEditor.CodeService):
|
||||
|
||||
|
||||
def __init__(self):
|
||||
CodeEditor.CodeService.__init__(self)
|
||||
|
||||
|
||||
class PerlCtrl(CodeEditor.CodeCtrl):
|
||||
|
||||
|
||||
def __init__(self, parent, ID = -1, style = wx.NO_FULL_REPAINT_ON_RESIZE):
|
||||
CodeEditor.CodeCtrl.__init__(self, parent, ID, style)
|
||||
self.SetLexer(wx.stc.STC_LEX_PERL)
|
||||
self.SetKeyWords(0, string.join(PERLKEYWORDS))
|
||||
|
||||
|
||||
def CanWordWrap(self):
|
||||
return True
|
||||
|
||||
|
||||
def SetViewDefaults(self):
|
||||
CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Perl", hasWordWrap = True, hasTabs = True)
|
||||
|
||||
|
||||
def GetFontAndColorFromConfig(self):
|
||||
return CodeEditor.CodeCtrl.GetFontAndColorFromConfig(self, configPrefix = "Perl")
|
||||
|
||||
|
||||
def UpdateStyles(self):
|
||||
CodeEditor.CodeCtrl.UpdateStyles(self)
|
||||
|
||||
if not self.GetFont():
|
||||
return
|
||||
|
||||
faces = { 'font' : self.GetFont().GetFaceName(),
|
||||
'size' : self.GetFont().GetPointSize(),
|
||||
'size2': self.GetFont().GetPointSize() - 2,
|
||||
'color' : "%02x%02x%02x" % (self.GetFontColor().Red(), self.GetFontColor().Green(), self.GetFontColor().Blue())
|
||||
}
|
||||
|
||||
# Perl Styles
|
||||
self.StyleSetSpec(wx.stc.STC_PL_DEFAULT, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_COMMENTLINE, "face:%(font)s,fore:#007F00,italic,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_NUMBER, "face:%(font)s,fore:#007F7F,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_CHARACTER, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_STRING, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_STRING_Q, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_STRING_QQ, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_STRING_QX, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_STRING_QR, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_STRING_QW, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_BACKTICKS, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_WORD, "face:%(font)s,fore:#00007F,bold,size:%(size)d" % faces) # keyword
|
||||
self.StyleSetSpec(wx.stc.STC_PL_IDENTIFIER, "face:%(font)s,fore:#%(color)s,face:%(font)s,size:%(size)d" % faces)
|
||||
|
||||
# Default
|
||||
self.StyleSetSpec(wx.stc.STC_PL_ARRAY, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_DATASECTION, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_ERROR, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_HASH, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_HERE_DELIM, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_HERE_Q, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_HERE_QQ, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_HERE_QX, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_LONGQUOTE, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_OPERATOR, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_POD, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_PREPROCESSOR, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_PUNCTUATION, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_REGEX, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_REGSUBST, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_SCALAR, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_PL_SYMBOLTABLE, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
|
||||
|
||||
class PerlOptionsPanel(STCTextEditor.TextOptionsPanel):
|
||||
|
||||
def __init__(self, parent, id):
|
||||
STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Perl", label = "Perl", hasWordWrap = True, hasTabs = True)
|
||||
|
||||
|
||||
PERLKEYWORDS = [
|
||||
"abs",
|
||||
"accept",
|
||||
"alarm",
|
||||
"atan2",
|
||||
"bind",
|
||||
"binmode",
|
||||
"bless",
|
||||
"caller",
|
||||
"chdir",
|
||||
"chmod",
|
||||
"chomp",
|
||||
"chop",
|
||||
"chown",
|
||||
"chr",
|
||||
"chroot",
|
||||
"close",
|
||||
"closedir",
|
||||
"connect",
|
||||
"continue",
|
||||
"cos",
|
||||
"crypt",
|
||||
"dbmclose",
|
||||
"dbmopen",
|
||||
"defined",
|
||||
"delete",
|
||||
"die",
|
||||
"do",
|
||||
"dump",
|
||||
"each",
|
||||
"endgrent",
|
||||
"endhostent",
|
||||
"endnetent",
|
||||
"endprotoent",
|
||||
"endpwent",
|
||||
"endservent",
|
||||
"eof",
|
||||
"eval",
|
||||
"exec",
|
||||
"exists",
|
||||
"exit",
|
||||
"exp",
|
||||
"fcntl",
|
||||
"fileno",
|
||||
"flock",
|
||||
"fork",
|
||||
"format",
|
||||
"formline",
|
||||
"getc",
|
||||
"getgrent",
|
||||
"getgrgid",
|
||||
"getgrnam",
|
||||
"gethostbyaddr",
|
||||
"gethostbyname",
|
||||
"gethostent",
|
||||
"getlogin",
|
||||
"getnetbyaddr",
|
||||
"getnetbyname",
|
||||
"getnetent",
|
||||
"getpeername",
|
||||
"getpgrp",
|
||||
"getppid",
|
||||
"getpriority",
|
||||
"getprotobyname",
|
||||
"getprotobynumber",
|
||||
"getprotoent",
|
||||
"getpwent",
|
||||
"getpwnam",
|
||||
"getpwuid",
|
||||
"getservbyname",
|
||||
"getservbyport",
|
||||
"getservent",
|
||||
"getsockname",
|
||||
"getsockopt",
|
||||
"glob",
|
||||
"gmtime",
|
||||
"goto",
|
||||
"grep",
|
||||
"hex",
|
||||
"import",
|
||||
"index",
|
||||
"int",
|
||||
"ioctl",
|
||||
"join",
|
||||
"keys",
|
||||
"kill",
|
||||
"last",
|
||||
"lc",
|
||||
"lcfirst",
|
||||
"length",
|
||||
"link",
|
||||
"listen",
|
||||
"local",
|
||||
"localtime",
|
||||
"log",
|
||||
"lstat",
|
||||
"m//",
|
||||
"map",
|
||||
"mkdir",
|
||||
"msgctl",
|
||||
"msgget",
|
||||
"msgrcv",
|
||||
"msgsnd",
|
||||
"my",
|
||||
"next",
|
||||
"no",
|
||||
"oct",
|
||||
"open",
|
||||
"opendir",
|
||||
"ord",
|
||||
"pack",
|
||||
"package",
|
||||
"pipe",
|
||||
"pop",
|
||||
"pos",
|
||||
"print",
|
||||
"printf",
|
||||
"prototype",
|
||||
"push",
|
||||
"q/STRING/",
|
||||
"qq/STRING/",
|
||||
"quotemeta",
|
||||
"qw",
|
||||
"qw/STRING/",
|
||||
"qx",
|
||||
"qx/STRING/",
|
||||
"rand",
|
||||
"read",
|
||||
"readdir",
|
||||
"readline",
|
||||
"readlink",
|
||||
"readpipe",
|
||||
"recv",
|
||||
"redo",
|
||||
"ref",
|
||||
"rename",
|
||||
"require",
|
||||
"reset",
|
||||
"return",
|
||||
"reverse",
|
||||
"rewinddir",
|
||||
"rindex",
|
||||
"rmdir",
|
||||
"s///",
|
||||
"scalar",
|
||||
"seek",
|
||||
"seekdir",
|
||||
"select",
|
||||
"semctl",
|
||||
"semget",
|
||||
"semop",
|
||||
"send",
|
||||
"setgrent",
|
||||
"sethostent",
|
||||
"setnetent",
|
||||
"setpgrp",
|
||||
"setpriority",
|
||||
"setprotoent",
|
||||
"setpwent",
|
||||
"setservent",
|
||||
"setsockopt",
|
||||
"shift",
|
||||
"shmctl",
|
||||
"shmget",
|
||||
"shmread",
|
||||
"shmwrite",
|
||||
"shutdown",
|
||||
"sin",
|
||||
"sleep",
|
||||
"socket",
|
||||
"socketpair",
|
||||
"sort",
|
||||
"splice",
|
||||
"split",
|
||||
"sprintf",
|
||||
"sqrt",
|
||||
"srand",
|
||||
"stat",
|
||||
"study",
|
||||
"sub",
|
||||
"substr",
|
||||
"symlink",
|
||||
"syscall",
|
||||
"sysopen",
|
||||
"sysread",
|
||||
"sysseek",
|
||||
"system",
|
||||
"syswrite",
|
||||
"tell",
|
||||
"telldir",
|
||||
"tie",
|
||||
"tied",
|
||||
"times",
|
||||
"tr///",
|
||||
"truncate",
|
||||
"uc",
|
||||
"ucfirst",
|
||||
"umask",
|
||||
"undef",
|
||||
"unlink",
|
||||
"unpack",
|
||||
"unshift",
|
||||
"untie",
|
||||
"use",
|
||||
"utime",
|
||||
"values",
|
||||
"vec",
|
||||
"wait",
|
||||
"waitpid",
|
||||
"wantarray",
|
||||
"warn",
|
||||
"write",
|
||||
"y///",
|
||||
"eq",
|
||||
"ne",
|
||||
"lt",
|
||||
"le",
|
||||
"gt",
|
||||
"ge",
|
||||
"cmp",
|
||||
"if",
|
||||
"else"
|
||||
"not",
|
||||
"and",
|
||||
"xor",
|
||||
"or",
|
||||
"if",
|
||||
"while",
|
||||
"until",
|
||||
"for",
|
||||
"foreach",
|
||||
"last",
|
||||
"next",
|
||||
"redo",
|
||||
"goto",
|
||||
"STDIN",
|
||||
"STDOUT",
|
||||
"STDERR",
|
||||
"WHEncE",
|
||||
"BEGIN",
|
||||
"END",
|
||||
"require",
|
||||
"integer",
|
||||
"less",
|
||||
"sigtrap",
|
||||
"strict",
|
||||
"subs"
|
||||
]
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Icon Bitmaps - generated by encode_bitmaps.py
|
||||
#----------------------------------------------------------------------------
|
||||
from wx import ImageFromStream, BitmapFromImage
|
||||
from wx import EmptyIcon
|
||||
import cStringIO
|
||||
|
||||
|
||||
def getPerlData():
|
||||
return \
|
||||
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x0e\x00\x00\x00\x10\x08\x06\
|
||||
\x00\x00\x00&\x94N:\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\
|
||||
\x01mIDAT(\x91\x9d\x93/\x8e\xf30\x10\xc5\x7f[\xed\x05\xca\x82,Ef\xc6e\x01Vx{\
|
||||
\x89\x04\x16\x94\x97\x045\xa8\xa0\xa44G\x08\xc9\x01\xa2\x80\x1e $ld\xc9\xa8w\
|
||||
\x08\xf0\xa2x\x93~\x1f\xda\x91F\xb2\xfd\xe6=\xcd?\x7f\xcd\xf3\x1c\xf8\x83}/\
|
||||
\x87i\x9a\x000\xc6D\xb0\xeb:\x86a MS\xce\xe7\xf3\x968M\x13}\xdf\xe3\x9cCD\
|
||||
\xb8\xddn\x18c\xe8\xba\x8e\xb2,\xc9\xb2\x0c\x11\x01\xd8\x90w\xc6\x18\x94R\
|
||||
\xf1\xa1\xef{\xba\xae\xa3i\x1a\xb4\xd6h\xad)\x8a\x02\xe7\\\xccj\x93\xea\xa2\
|
||||
\nP\xd75\xd7\xeb\x15\x00\xef\xfdFt)e\xb7\x80\x8b\xbas\x8e$I\xe2}\xc1\x8b\xa2\
|
||||
\xd8\xf4b\x07\xa0\x94\xe2\xf5za\xad\xc5Z\xcb\xfb\xfdFD\xe8\xfb\x9e\x05\x17\
|
||||
\x11\x9cs4M\xf3K<\x9dNdY\xc60\x0cx\xef\x11\x11\xea\xbaF)\x85s\x8e\xba\xae)\
|
||||
\xcb\x12\x11!M\xd3_"\xc0\xfd~\xc7Z\x8bs\x0e\x80$I\xa2:@UU1u\x00\xe6y\x0ek\
|
||||
\x1f\xc71\x1c\x0e\x87\xd0\xb6m\xd8\xef\xf7\xe1\xf1x\x84\xcb\xe5\x12\xe6y\x0e\
|
||||
\xc7\xe31\xc6\xed\xf80\x11!\xcb2\xbc\xf7TUE\x9e\xe71=\xadul\xce\xf7\'Qk\x8d\
|
||||
\xf7\x9e<\xcf\x81\xed&Yk\xb7\xe3\xf84\xa5\x14\xc6\x18D\x84\xe7\xf3\x19\x83\
|
||||
\xd75\xfe\x97\xb8\x0eXo\xcc2\x9e\x7f\x9a3\x8ech\xdb6|6l\xf15\xf6\xf5\xd7o\
|
||||
\xf5\x03\xaf\x9f\xfa@\x02\xe4\xdc\xf9\x00\x00\x00\x00IEND\xaeB`\x82'
|
||||
|
||||
|
||||
def getPerlBitmap():
|
||||
return BitmapFromImage(getPerlImage())
|
||||
|
||||
def getPerlImage():
|
||||
stream = cStringIO.StringIO(getPerlData())
|
||||
return ImageFromStream(stream)
|
||||
|
||||
def getPerlIcon():
|
||||
icon = EmptyIcon()
|
||||
icon.CopyFromBitmap(getPerlBitmap())
|
||||
return icon
|
1847
wxPython/samples/ide/activegrid/tool/ProjectEditor.py
Normal file
1847
wxPython/samples/ide/activegrid/tool/ProjectEditor.py
Normal file
File diff suppressed because it is too large
Load Diff
614
wxPython/samples/ide/activegrid/tool/PythonEditor.py
Normal file
614
wxPython/samples/ide/activegrid/tool/PythonEditor.py
Normal file
@@ -0,0 +1,614 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: PythonEditor.py
|
||||
# Purpose: PythonEditor for wx.lib.pydocview tbat uses the Styled Text Control
|
||||
#
|
||||
# Author: Peter Yared
|
||||
#
|
||||
# Created: 8/15/03
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import CodeEditor
|
||||
import wx
|
||||
import wx.lib.docview
|
||||
import wx.lib.pydocview
|
||||
import string
|
||||
import keyword # So it knows what to hilite
|
||||
import wx.py # For the Python interpreter
|
||||
import wx.stc # For the Python interpreter
|
||||
import cStringIO # For indent
|
||||
import OutlineService
|
||||
import STCTextEditor
|
||||
import keyword # for GetAutoCompleteKeywordList
|
||||
import sys # for GetAutoCompleteKeywordList
|
||||
import MessageService # for OnCheckCode
|
||||
import OutlineService
|
||||
try:
|
||||
import checker # for pychecker
|
||||
_CHECKER_INSTALLED = True
|
||||
except ImportError:
|
||||
_CHECKER_INSTALLED = False
|
||||
import os.path # for pychecker
|
||||
_ = wx.GetTranslation
|
||||
|
||||
if wx.Platform == '__WXMSW__':
|
||||
_WINDOWS = True
|
||||
else:
|
||||
_WINDOWS = False
|
||||
|
||||
|
||||
VIEW_PYTHON_INTERPRETER_ID = wx.NewId()
|
||||
|
||||
|
||||
class PythonDocument(CodeEditor.CodeDocument):
|
||||
pass
|
||||
|
||||
|
||||
class PythonView(CodeEditor.CodeView):
|
||||
|
||||
|
||||
def ProcessUpdateUIEvent(self, event):
|
||||
if not self.GetCtrl():
|
||||
return False
|
||||
|
||||
id = event.GetId()
|
||||
if id == CodeEditor.CHECK_CODE_ID:
|
||||
hasText = self.GetCtrl().GetTextLength() > 0
|
||||
event.Enable(hasText)
|
||||
return True
|
||||
|
||||
return CodeEditor.CodeView.ProcessUpdateUIEvent(self, event)
|
||||
|
||||
|
||||
def GetCtrlClass(self):
|
||||
""" Used in split window to instantiate new instances """
|
||||
return PythonCtrl
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def OnClose(self, deleteWindow = True):
|
||||
status = STCTextEditor.TextView.OnClose(self, deleteWindow)
|
||||
wx.CallAfter(self.ClearOutline) # need CallAfter because when closing the document, it is Activated and then Close, so need to match OnActivateView's CallAfter
|
||||
return status
|
||||
|
||||
|
||||
def GetAutoCompleteKeywordList(self, context, hint):
|
||||
obj = None
|
||||
try:
|
||||
if context and len(context):
|
||||
obj = eval(context, globals(), locals())
|
||||
except:
|
||||
if not hint or len(hint) == 0: # context isn't valid, maybe it was the hint
|
||||
hint = context
|
||||
|
||||
if obj is None:
|
||||
kw = keyword.kwlist[:]
|
||||
else:
|
||||
symTbl = dir(obj)
|
||||
kw = filter(lambda item: item[0] != '_', symTbl) # remove local variables and methods
|
||||
|
||||
if hint and len(hint):
|
||||
lowerHint = hint.lower()
|
||||
filterkw = filter(lambda item: item.lower().startswith(lowerHint), kw) # remove variables and methods that don't match hint
|
||||
kw = filterkw
|
||||
|
||||
kw.sort(self.CaseInsensitiveCompare)
|
||||
|
||||
if hint:
|
||||
replaceLen = len(hint)
|
||||
else:
|
||||
replaceLen = 0
|
||||
|
||||
return " ".join(kw), replaceLen
|
||||
|
||||
|
||||
def OnCheckCode(self):
|
||||
if not _CHECKER_INSTALLED:
|
||||
wx.MessageBox(_("pychecker not found. Please install pychecker."), _("Check Code"))
|
||||
return
|
||||
|
||||
filename = os.path.basename(self.GetDocument().GetFilename())
|
||||
|
||||
# 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"))
|
||||
val = dlg.ShowModal()
|
||||
dlg.Destroy()
|
||||
if val == wx.ID_OK:
|
||||
self.GetDocument().Save()
|
||||
else:
|
||||
return
|
||||
|
||||
messageService = wx.GetApp().GetService(MessageService.MessageService)
|
||||
messageService.ShowWindow()
|
||||
view = messageService.GetView()
|
||||
if not view:
|
||||
return
|
||||
|
||||
view.ClearLines()
|
||||
view.SetCallback(self.OnJumpToFoundLine)
|
||||
|
||||
# Set cursor to Wait cursor
|
||||
wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
|
||||
|
||||
# This takes a while for involved code
|
||||
checker.checkSyntax(self.GetDocument().GetFilename(), view)
|
||||
|
||||
# Set cursor to Default cursor
|
||||
wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
|
||||
|
||||
|
||||
def OnJumpToFoundLine(self, event):
|
||||
messageService = wx.GetApp().GetService(MessageService.MessageService)
|
||||
lineText, pos = messageService.GetView().GetCurrLine()
|
||||
|
||||
lineEnd = lineText.find(".py:")
|
||||
if lineEnd == -1:
|
||||
return
|
||||
|
||||
lineStart = lineEnd + len(".py:")
|
||||
lineEnd = lineText.find(":", lineStart)
|
||||
lineNum = int(lineText[lineStart:lineEnd])
|
||||
|
||||
filename = lineText[0:lineStart - 1]
|
||||
|
||||
foundView = None
|
||||
openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
|
||||
for openDoc in openDocs:
|
||||
if openDoc.GetFilename() == filename:
|
||||
foundView = openDoc.GetFirstView()
|
||||
break
|
||||
|
||||
if not foundView:
|
||||
doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT)
|
||||
foundView = doc.GetFirstView()
|
||||
|
||||
if foundView:
|
||||
foundView.GetFrame().SetFocus()
|
||||
foundView.Activate()
|
||||
foundView.GotoLine(lineNum)
|
||||
startPos = foundView.PositionFromLine(lineNum)
|
||||
endPos = foundView.GetLineEndPosition(lineNum)
|
||||
# wxBug: Need to select in reverse order, (end, start) to put cursor at head of line so positioning is correct
|
||||
# Also, if we use the correct positioning order (start, end), somehow, when we open a edit window for the first
|
||||
# time, we don't see the selection, it is scrolled off screen
|
||||
foundView.SetSelection(endPos, startPos)
|
||||
wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
|
||||
|
||||
|
||||
|
||||
class PythonInterpreterView(wx.lib.docview.View):
|
||||
|
||||
|
||||
def OnCreate(self, doc, flags):
|
||||
frame = wx.GetApp().CreateDocumentFrame(self, doc, flags)
|
||||
sizer = wx.BoxSizer()
|
||||
self._pyCrust = wx.py.crust.Crust(frame)
|
||||
sizer.Add(self._pyCrust, 1, wx.EXPAND, 0)
|
||||
frame.SetSizer(sizer)
|
||||
frame.Layout()
|
||||
self.Activate()
|
||||
frame.Show()
|
||||
return True
|
||||
|
||||
|
||||
def ProcessEvent(self, event):
|
||||
if not hasattr(self, "_pyCrust") or not self._pyCrust:
|
||||
return wx.lib.docview.View.ProcessEvent(self, event)
|
||||
stcControl = wx.Window_FindFocus()
|
||||
if not isinstance(stcControl, wx.stc.StyledTextCtrl):
|
||||
return wx.lib.docview.View.ProcessEvent(self, event)
|
||||
id = event.GetId()
|
||||
if id == wx.ID_UNDO:
|
||||
stcControl.Undo()
|
||||
return True
|
||||
elif id == wx.ID_REDO:
|
||||
stcControl.Redo()
|
||||
return True
|
||||
elif 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
|
||||
else:
|
||||
return wx.lib.docview.View.ProcessEvent(self, event)
|
||||
|
||||
|
||||
def ProcessUpdateUIEvent(self, event):
|
||||
if not hasattr(self, "_pyCrust") or not self._pyCrust:
|
||||
return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
|
||||
stcControl = wx.Window_FindFocus()
|
||||
if not isinstance(stcControl, wx.stc.StyledTextCtrl):
|
||||
return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
|
||||
id = event.GetId()
|
||||
if id == wx.ID_UNDO:
|
||||
event.Enable(stcControl.CanUndo())
|
||||
return True
|
||||
elif id == wx.ID_REDO:
|
||||
event.Enable(stcControl.CanRedo())
|
||||
return True
|
||||
elif 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
|
||||
else:
|
||||
return wx.lib.docview.View.ProcessUpdateUIEvent(self, event)
|
||||
|
||||
|
||||
def OnClose(self, deleteWindow=True):
|
||||
if deleteWindow and self.GetFrame():
|
||||
self.GetFrame().Destroy()
|
||||
return True
|
||||
|
||||
|
||||
class PythonService(CodeEditor.CodeService):
|
||||
|
||||
|
||||
def __init__(self):
|
||||
CodeEditor.CodeService.__init__(self)
|
||||
|
||||
|
||||
def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
|
||||
CodeEditor.CodeService.InstallControls(self, frame, menuBar, toolBar, statusBar, document)
|
||||
|
||||
if document and document.GetDocumentTemplate().GetDocumentType() != PythonDocument:
|
||||
return
|
||||
if not document and wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
|
||||
return
|
||||
|
||||
viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View")))
|
||||
|
||||
viewStatusBarItemPos = self.GetMenuItemPos(viewMenu, wx.lib.pydocview.VIEW_STATUSBAR_ID)
|
||||
viewMenu.InsertCheckItem(viewStatusBarItemPos + 1, VIEW_PYTHON_INTERPRETER_ID, _("Python &Interpreter"), _("Shows or hides the Python interactive window"))
|
||||
wx.EVT_MENU(frame, VIEW_PYTHON_INTERPRETER_ID, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, VIEW_PYTHON_INTERPRETER_ID, frame.ProcessUpdateUIEvent)
|
||||
|
||||
|
||||
def ProcessEvent(self, event):
|
||||
id = event.GetId()
|
||||
if id == VIEW_PYTHON_INTERPRETER_ID:
|
||||
self.OnViewPythonInterpreter(event)
|
||||
return True
|
||||
else:
|
||||
return CodeEditor.CodeService.ProcessEvent(self, event)
|
||||
|
||||
|
||||
def ProcessUpdateUIEvent(self, event):
|
||||
id = event.GetId()
|
||||
if id == VIEW_PYTHON_INTERPRETER_ID:
|
||||
event.Enable(True)
|
||||
docManager = wx.GetApp().GetDocumentManager()
|
||||
event.Check(False)
|
||||
for doc in docManager.GetDocuments():
|
||||
if isinstance(doc.GetFirstView(), PythonInterpreterView):
|
||||
event.Check(True)
|
||||
break
|
||||
return True
|
||||
else:
|
||||
return CodeEditor.CodeService.ProcessUpdateUIEvent(self, event)
|
||||
|
||||
|
||||
def OnViewPythonInterpreter(self, event):
|
||||
for doc in wx.GetApp().GetDocumentManager().GetDocuments():
|
||||
if isinstance(doc.GetFirstView(), PythonInterpreterView):
|
||||
doc.GetFirstView().GetDocument().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"))
|
||||
|
||||
|
||||
class PythonCtrl(CodeEditor.CodeCtrl):
|
||||
|
||||
|
||||
def __init__(self, parent, ID = -1, style = wx.NO_FULL_REPAINT_ON_RESIZE):
|
||||
CodeEditor.CodeCtrl.__init__(self, parent, ID, style)
|
||||
self.SetProperty("tab.timmy.whinge.level", "1")
|
||||
self.SetProperty("fold.comment.python", "1")
|
||||
self.SetProperty("fold.quotes.python", "1")
|
||||
self.SetLexer(wx.stc.STC_LEX_PYTHON)
|
||||
self.SetKeyWords(0, string.join(keyword.kwlist))
|
||||
|
||||
|
||||
def SetViewDefaults(self):
|
||||
CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Python", hasWordWrap = False, hasTabs = True)
|
||||
|
||||
|
||||
def GetFontAndColorFromConfig(self):
|
||||
return CodeEditor.CodeCtrl.GetFontAndColorFromConfig(self, configPrefix = "Python")
|
||||
|
||||
|
||||
def UpdateStyles(self):
|
||||
CodeEditor.CodeCtrl.UpdateStyles(self)
|
||||
|
||||
if not self.GetFont():
|
||||
return
|
||||
|
||||
faces = { 'font' : self.GetFont().GetFaceName(),
|
||||
'size' : self.GetFont().GetPointSize(),
|
||||
'size2': self.GetFont().GetPointSize() - 2,
|
||||
'color' : "%02x%02x%02x" % (self.GetFontColor().Red(), self.GetFontColor().Green(), self.GetFontColor().Blue())
|
||||
}
|
||||
|
||||
# Python styles
|
||||
# White space
|
||||
self.StyleSetSpec(wx.stc.STC_P_DEFAULT, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
# Comment
|
||||
self.StyleSetSpec(wx.stc.STC_P_COMMENTLINE, "face:%(font)s,fore:#007F00,italic,face:%(font)s,size:%(size)d" % faces)
|
||||
# Number
|
||||
self.StyleSetSpec(wx.stc.STC_P_NUMBER, "face:%(font)s,fore:#007F7F,size:%(size)d" % faces)
|
||||
# String
|
||||
self.StyleSetSpec(wx.stc.STC_P_STRING, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
# Single quoted string
|
||||
self.StyleSetSpec(wx.stc.STC_P_CHARACTER, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
# Keyword
|
||||
self.StyleSetSpec(wx.stc.STC_P_WORD, "face:%(font)s,fore:#00007F,bold,size:%(size)d" % faces)
|
||||
# Triple quotes
|
||||
self.StyleSetSpec(wx.stc.STC_P_TRIPLE, "face:%(font)s,fore:#7F0000,size:%(size)d" % faces)
|
||||
# Triple double quotes
|
||||
self.StyleSetSpec(wx.stc.STC_P_TRIPLEDOUBLE, "face:%(font)s,fore:#7F0000,size:%(size)d" % faces)
|
||||
# Class name definition
|
||||
self.StyleSetSpec(wx.stc.STC_P_CLASSNAME, "face:%(font)s,fore:#0000FF,bold,size:%(size)d" % faces)
|
||||
# Function or method name definition
|
||||
self.StyleSetSpec(wx.stc.STC_P_DEFNAME, "face:%(font)s,fore:#007F7F,bold,size:%(size)d" % faces)
|
||||
# Operators
|
||||
self.StyleSetSpec(wx.stc.STC_P_OPERATOR, "face:%(font)s,size:%(size)d" % faces)
|
||||
# Identifiers
|
||||
self.StyleSetSpec(wx.stc.STC_P_IDENTIFIER, "face:%(font)s,fore:#%(color)s,face:%(font)s,size:%(size)d" % faces)
|
||||
# Comment-blocks
|
||||
self.StyleSetSpec(wx.stc.STC_P_COMMENTBLOCK, "face:%(font)s,fore:#7F7F7F,size:%(size)d" % faces)
|
||||
# End of line where string is not closed
|
||||
self.StyleSetSpec(wx.stc.STC_P_STRINGEOL, "face:%(font)s,fore:#000000,face:%(font)s,back:#E0C0E0,eol,size:%(size)d" % faces)
|
||||
|
||||
|
||||
def OnUpdateUI(self, evt):
|
||||
braces = self.GetMatchingBraces()
|
||||
|
||||
# check for matching braces
|
||||
braceAtCaret = -1
|
||||
braceOpposite = -1
|
||||
charBefore = None
|
||||
caretPos = self.GetCurrentPos()
|
||||
if caretPos > 0:
|
||||
charBefore = self.GetCharAt(caretPos - 1)
|
||||
styleBefore = self.GetStyleAt(caretPos - 1)
|
||||
|
||||
# check before
|
||||
if charBefore and chr(charBefore) in braces and styleBefore == wx.stc.STC_P_OPERATOR:
|
||||
braceAtCaret = caretPos - 1
|
||||
|
||||
# check after
|
||||
if braceAtCaret < 0:
|
||||
charAfter = self.GetCharAt(caretPos)
|
||||
styleAfter = self.GetStyleAt(caretPos)
|
||||
if charAfter and chr(charAfter) in braces and styleAfter == wx.stc.STC_P_OPERATOR:
|
||||
braceAtCaret = caretPos
|
||||
|
||||
if braceAtCaret >= 0:
|
||||
braceOpposite = self.BraceMatch(braceAtCaret)
|
||||
|
||||
if braceAtCaret != -1 and braceOpposite == -1:
|
||||
self.BraceBadLight(braceAtCaret)
|
||||
else:
|
||||
self.BraceHighlight(braceAtCaret, braceOpposite)
|
||||
|
||||
evt.Skip()
|
||||
|
||||
|
||||
def DoIndent(self):
|
||||
(text, caretPos) = self.GetCurLine()
|
||||
|
||||
self._tokenizerChars = {} # This is really too much, need to find something more like a C array
|
||||
for i in range(len(text)):
|
||||
self._tokenizerChars[i] = 0
|
||||
|
||||
ctext = cStringIO.StringIO(text)
|
||||
try:
|
||||
tokenize.tokenize(ctext.readline, self)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Left in for debugging purposes:
|
||||
#for i in range(len(text)):
|
||||
# print i, text[i], self._tokenizerChars[i]
|
||||
|
||||
if caretPos == 0 or len(string.strip(text)) == 0: # At beginning of line or within an empty line
|
||||
self.AddText('\n')
|
||||
else:
|
||||
doExtraIndent = False
|
||||
brackets = False
|
||||
commentStart = -1
|
||||
if caretPos > 1:
|
||||
startParenCount = 0
|
||||
endParenCount = 0
|
||||
startSquareBracketCount = 0
|
||||
endSquareBracketCount = 0
|
||||
startCurlyBracketCount = 0
|
||||
endCurlyBracketCount = 0
|
||||
startQuoteCount = 0
|
||||
endQuoteCount = 0
|
||||
for i in range(caretPos - 1, -1, -1): # Go through each character before the caret
|
||||
if i >= len(text): # Sometimes the caret is at the end of the text if there is no LF
|
||||
continue
|
||||
if self._tokenizerChars[i] == 1:
|
||||
continue
|
||||
elif self._tokenizerChars[i] == 2:
|
||||
startQuoteCount = startQuoteCount + 1
|
||||
elif self._tokenizerChars[i] == 3:
|
||||
endQuoteCount = endQuoteCount + 1
|
||||
elif text[i] == '(': # Would be nice to use a dict for this, but the code is much more readable this way
|
||||
startParenCount = startParenCount + 1
|
||||
elif text[i] == ')':
|
||||
endParenCount = endParenCount + 1
|
||||
elif text[i] == "[":
|
||||
startSquareBracketCount = startSquareBracketCount + 1
|
||||
elif text[i] == "]":
|
||||
endSquareBracketCount = endSquareBracketCount + 1
|
||||
elif text[i] == "{":
|
||||
startCurlyBracketCount = startCurlyBracketCount + 1
|
||||
elif text[i] == "}":
|
||||
endCurlyBracketCount = endCurlyBracketCount + 1
|
||||
elif text[i] == "#":
|
||||
commentStart = i
|
||||
break
|
||||
if startQuoteCount > endQuoteCount or startParenCount > endParenCount or startSquareBracketCount > endSquareBracketCount or startCurlyBracketCount > endCurlyBracketCount:
|
||||
if i + 1 >= caretPos: # Caret is right at the open paren, so just do indent as if colon was there
|
||||
doExtraIndent = True
|
||||
break
|
||||
else:
|
||||
spaces = " " * (i + 1)
|
||||
brackets = True
|
||||
break
|
||||
if not brackets:
|
||||
spaces = text[0:len(text) - len(string.lstrip(text))]
|
||||
if caretPos < len(spaces): # If within the opening spaces of a line
|
||||
spaces = spaces[:caretPos]
|
||||
|
||||
# strip comment off
|
||||
if commentStart != -1:
|
||||
text = text[0:commentStart]
|
||||
|
||||
textNoTrailingSpaces = text[0:caretPos].rstrip()
|
||||
if doExtraIndent or len(textNoTrailingSpaces) and textNoTrailingSpaces[-1] == ':':
|
||||
spaces = spaces + ' ' * self.GetIndent()
|
||||
self.AddText('\n' + spaces)
|
||||
|
||||
|
||||
# Callback for tokenizer in self.DoIndent
|
||||
def __call__(self, toktype, toktext, (srow,scol), (erow,ecol), line):
|
||||
if toktype == tokenize.COMMENT:
|
||||
for i in range(scol, ecol + 1):
|
||||
self._validChars[i] = False
|
||||
elif toktype == token.STRING:
|
||||
self._tokenizerChars[scol] = 2 # Open quote
|
||||
self._tokenizerChars[ecol - 1] = 3 # Close quote
|
||||
for i in range(scol + 1, ecol - 2):
|
||||
self._tokenizerChars[i] = 1 # Part of string, 1 == ignore the char
|
||||
|
||||
|
||||
class PythonOptionsPanel(wx.Panel):
|
||||
|
||||
def __init__(self, parent, id):
|
||||
wx.Panel.__init__(self, parent, id)
|
||||
pathLabel = wx.StaticText(self, -1, _("python.exe Path:"))
|
||||
config = wx.ConfigBase_Get()
|
||||
path = config.Read("ActiveGridPythonLocation")
|
||||
self._pathTextCtrl = wx.TextCtrl(self, -1, path, size = (150, -1))
|
||||
self._pathTextCtrl.SetToolTipString(self._pathTextCtrl.GetValue())
|
||||
self._pathTextCtrl.SetInsertionPointEnd()
|
||||
choosePathButton = wx.Button(self, -1, _("Browse..."))
|
||||
pathSizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
HALF_SPACE = 5
|
||||
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)
|
||||
wx.EVT_BUTTON(self, choosePathButton.GetId(), self.OnChoosePath)
|
||||
mainSizer = wx.BoxSizer(wx.VERTICAL)
|
||||
mainSizer.Add(pathSizer, 0, wx.LEFT | wx.RIGHT | wx.TOP, 10)
|
||||
|
||||
self._otherOptions = STCTextEditor.TextOptionsPanel(self, -1, configPrefix = "Python", label = "Python", hasWordWrap = False, hasTabs = True, addPage=False)
|
||||
mainSizer.Add(self._otherOptions)
|
||||
self.SetSizer(mainSizer)
|
||||
parent.AddPage(self, _("Python"))
|
||||
|
||||
def OnChoosePath(self, event):
|
||||
if _WINDOWS:
|
||||
wildcard = _("*.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()
|
||||
|
||||
def OnOK(self, optionsDialog):
|
||||
if len(self._pathTextCtrl.GetValue()) > 0:
|
||||
config = wx.ConfigBase_Get()
|
||||
config.Write("ActiveGridPythonLocation", self._pathTextCtrl.GetValue())
|
||||
|
||||
self._otherOptions.OnOK(optionsDialog)
|
||||
#----------------------------------------------------------------------------
|
||||
# Icon Bitmaps - generated by encode_bitmaps.py
|
||||
#----------------------------------------------------------------------------
|
||||
from wx import ImageFromStream, BitmapFromImage
|
||||
from wx import EmptyIcon
|
||||
import cStringIO
|
||||
|
||||
|
||||
def getPythonData():
|
||||
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\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"
|
||||
|
||||
|
||||
def getPythonBitmap():
|
||||
return BitmapFromImage(getPythonImage())
|
||||
|
||||
def getPythonImage():
|
||||
stream = cStringIO.StringIO(getPythonData())
|
||||
return ImageFromStream(stream)
|
||||
|
||||
def getPythonIcon():
|
||||
icon = EmptyIcon()
|
||||
icon.CopyFromBitmap(getPythonBitmap())
|
||||
return icon
|
1293
wxPython/samples/ide/activegrid/tool/STCTextEditor.py
Normal file
1293
wxPython/samples/ide/activegrid/tool/STCTextEditor.py
Normal file
File diff suppressed because it is too large
Load Diff
327
wxPython/samples/ide/activegrid/tool/Service.py
Normal file
327
wxPython/samples/ide/activegrid/tool/Service.py
Normal file
@@ -0,0 +1,327 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: Service.py
|
||||
# Purpose: Basic Reusable Service View for wx.lib.pydocview
|
||||
#
|
||||
# Author: Morgan Hua
|
||||
#
|
||||
# Created: 11/4/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import wx
|
||||
import wx.lib.docview
|
||||
import wx.lib.pydocview
|
||||
_ = wx.GetTranslation
|
||||
|
||||
|
||||
FLOATING_MINIFRAME = -1
|
||||
|
||||
|
||||
class ServiceView(wx.EvtHandler):
|
||||
""" Basic Service View.
|
||||
"""
|
||||
bottomTab = None
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Overridden methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def __init__(self, service):
|
||||
wx.EvtHandler.__init__(self)
|
||||
self._viewFrame = None
|
||||
self._service = service
|
||||
self._control = None
|
||||
self._embeddedWindow = None
|
||||
|
||||
|
||||
def Destroy(self):
|
||||
wx.EvtHandler.Destroy(self)
|
||||
|
||||
|
||||
def GetFrame(self):
|
||||
return self._viewFrame
|
||||
|
||||
|
||||
def SetFrame(self, frame):
|
||||
self._viewFrame = frame
|
||||
|
||||
|
||||
def _CreateControl(self, parent, id):
|
||||
return None
|
||||
|
||||
|
||||
def GetControl(self):
|
||||
return self._control
|
||||
|
||||
|
||||
def SetControl(self, control):
|
||||
self._control = control
|
||||
|
||||
|
||||
def OnCreate(self, doc, flags):
|
||||
config = wx.ConfigBase_Get()
|
||||
windowLoc = self._service.GetEmbeddedWindowLocation()
|
||||
if windowLoc == FLOATING_MINIFRAME:
|
||||
pos = config.ReadInt(self._service.GetServiceName() + "FrameXLoc", -1), config.ReadInt(self._service.GetServiceName() + "FrameYLoc", -1)
|
||||
# make sure frame is visible
|
||||
screenWidth = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X)
|
||||
screenHeight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
|
||||
if pos[0] < 0 or pos[0] >= screenWidth or pos[1] < 0 or pos[1] >= screenHeight:
|
||||
pos = wx.DefaultPosition
|
||||
|
||||
size = wx.Size(config.ReadInt(self._service.GetServiceName() + "FrameXSize", -1), config.ReadInt(self._service.GetServiceName() + "FrameYSize", -1))
|
||||
title = _(self._service.GetServiceName())
|
||||
if wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI and wx.GetApp().GetAppName():
|
||||
title = title + " - " + wx.GetApp().GetAppName()
|
||||
frame = wx.MiniFrame(wx.GetApp().GetTopWindow(), -1, title, pos = pos, size = size, style = wx.CLOSE_BOX|wx.CAPTION|wx.SYSTEM_MENU)
|
||||
wx.EVT_CLOSE(frame, self.OnCloseWindow)
|
||||
elif wx.GetApp().IsMDI():
|
||||
self._embeddedWindow = wx.GetApp().GetTopWindow().GetEmbeddedWindow(windowLoc)
|
||||
frame = self._embeddedWindow
|
||||
else:
|
||||
pos = config.ReadInt(self._service.GetServiceName() + "FrameXLoc", -1), config.ReadInt(self._service.GetServiceName() + "FrameYLoc", -1)
|
||||
# make sure frame is visible
|
||||
screenWidth = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X)
|
||||
screenHeight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)
|
||||
if pos[0] < 0 or pos[0] >= screenWidth or pos[1] < 0 or pos[1] >= screenHeight:
|
||||
pos = wx.DefaultPosition
|
||||
|
||||
size = wx.Size(config.ReadInt(self._service.GetServiceName() + "FrameXSize", -1), config.ReadInt(self._service.GetServiceName() + "FrameYSize", -1))
|
||||
title = _(self._service.GetServiceName())
|
||||
if wx.GetApp().GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI and wx.GetApp().GetAppName():
|
||||
title = title + " - " + wx.GetApp().GetAppName()
|
||||
frame = wx.GetApp().CreateDocumentFrame(self, doc, flags, pos = pos, size = size)
|
||||
frame.SetTitle(title)
|
||||
if config.ReadInt(self._service.GetServiceName() + "FrameMaximized", False):
|
||||
frame.Maximize(True)
|
||||
wx.EVT_CLOSE(frame, self.OnCloseWindow)
|
||||
|
||||
self.SetFrame(frame)
|
||||
sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
windowLoc = self._service.GetEmbeddedWindowLocation()
|
||||
if self._embeddedWindow or windowLoc == FLOATING_MINIFRAME:
|
||||
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")
|
||||
sizer.Add(ServiceView.bottomTab, 1, wx.TOP|wx.EXPAND, 4)
|
||||
def OnFrameResize(event):
|
||||
ServiceView.bottomTab.SetSize(ServiceView.bottomTab.GetParent().GetSize())
|
||||
frame.Bind(wx.EVT_SIZE, OnFrameResize)
|
||||
# Factor this out.
|
||||
self._control = self._CreateControl(ServiceView.bottomTab, wx.NewId())
|
||||
if self._control != None:
|
||||
ServiceView.bottomTab.AddPage(self._control, self._service.GetServiceName())
|
||||
ServiceView.bottomTab.Layout()
|
||||
else:
|
||||
# Factor this out.
|
||||
self._control = self._CreateControl(frame, wx.NewId())
|
||||
sizer.Add(self._control)
|
||||
else:
|
||||
# Factor this out.
|
||||
self._control = self._CreateControl(frame, wx.NewId())
|
||||
sizer.Add(self._control, 1, wx.EXPAND, 0)
|
||||
frame.SetSizer(sizer)
|
||||
frame.Layout()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def OnCloseWindow(self, event):
|
||||
frame = self.GetFrame()
|
||||
config = wx.ConfigBase_Get()
|
||||
if frame and not self._embeddedWindow:
|
||||
if not frame.IsMaximized():
|
||||
config.WriteInt(self._service.GetServiceName() + "FrameXLoc", frame.GetPositionTuple()[0])
|
||||
config.WriteInt(self._service.GetServiceName() + "FrameYLoc", frame.GetPositionTuple()[1])
|
||||
config.WriteInt(self._service.GetServiceName() + "FrameXSize", frame.GetSizeTuple()[0])
|
||||
config.WriteInt(self._service.GetServiceName() + "FrameYSize", frame.GetSizeTuple()[1])
|
||||
config.WriteInt(self._service.GetServiceName() + "FrameMaximized", frame.IsMaximized())
|
||||
|
||||
if not self._embeddedWindow:
|
||||
windowLoc = self._service.GetEmbeddedWindowLocation()
|
||||
if windowLoc == FLOATING_MINIFRAME:
|
||||
# don't destroy it, just hide it
|
||||
frame.Hide()
|
||||
else:
|
||||
# Call the original OnCloseWindow, could have subclassed SDIDocFrame and MDIDocFrame but this is easier since it will work for both SDI and MDI frames without subclassing both
|
||||
frame.OnCloseWindow(event)
|
||||
|
||||
|
||||
def Activate(self, activate = True):
|
||||
""" Dummy function for SDI mode """
|
||||
pass
|
||||
|
||||
|
||||
def Close(self, deleteWindow = True):
|
||||
"""
|
||||
Closes the view by calling OnClose. If deleteWindow is true, this
|
||||
function should delete the window associated with the view.
|
||||
"""
|
||||
if deleteWindow:
|
||||
self.Destroy()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Callback Methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def SetCallback(self, callback):
|
||||
""" Sets in the event table for a doubleclick to invoke the given callback.
|
||||
Additional calls to this method overwrites the previous entry and only the last set callback will be invoked.
|
||||
"""
|
||||
wx.stc.EVT_STC_DOUBLECLICK(self.GetControl(), self.GetControl().GetId(), callback)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Display Methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def IsShown(self):
|
||||
if not self.GetFrame():
|
||||
return False
|
||||
return self.GetFrame().IsShown()
|
||||
|
||||
|
||||
def Hide(self):
|
||||
self.Show(False)
|
||||
|
||||
|
||||
def Show(self, show = True):
|
||||
self.GetFrame().Show(show)
|
||||
if self._embeddedWindow:
|
||||
mdiParentFrame = wx.GetApp().GetTopWindow()
|
||||
mdiParentFrame.ShowEmbeddedWindow(self.GetFrame(), show)
|
||||
|
||||
|
||||
class Service(wx.lib.pydocview.DocService):
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Constants
|
||||
#----------------------------------------------------------------------------
|
||||
SHOW_WINDOW = wx.NewId() # keep this line for each subclass, need unique ID for each Service
|
||||
|
||||
|
||||
def __init__(self, serviceName, embeddedWindowLocation = wx.lib.pydocview.EMBEDDED_WINDOW_LEFT):
|
||||
self._serviceName = serviceName
|
||||
self._embeddedWindowLocation = embeddedWindowLocation
|
||||
self._view = None
|
||||
|
||||
|
||||
def GetEmbeddedWindowLocation(self):
|
||||
return self._embeddedWindowLocation
|
||||
|
||||
|
||||
def SetEmbeddedWindowLocation(self, embeddedWindowLocation):
|
||||
self._embeddedWindowLocation = embeddedWindowLocation
|
||||
|
||||
|
||||
def InstallControls(self, frame, menuBar = None, toolBar = None, statusBar = None, document = None):
|
||||
viewMenu = menuBar.GetMenu(menuBar.FindMenu(_("&View")))
|
||||
menuItemPos = self.GetMenuItemPos(viewMenu, viewMenu.FindItem(_("&Status Bar"))) + 1
|
||||
|
||||
viewMenu.InsertCheckItem(menuItemPos, self.SHOW_WINDOW, self.GetMenuString(), self.GetMenuDescr())
|
||||
wx.EVT_MENU(frame, self.SHOW_WINDOW, frame.ProcessEvent)
|
||||
wx.EVT_UPDATE_UI(frame, self.SHOW_WINDOW, frame.ProcessUpdateUIEvent)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def GetServiceName(self):
|
||||
""" String used to save out Service View configuration information """
|
||||
return self._serviceName
|
||||
|
||||
|
||||
def GetMenuString(self):
|
||||
""" Need to override this method to provide menu item for showing Service View """
|
||||
return _(self.GetServiceName())
|
||||
|
||||
|
||||
def GetMenuDescr(self):
|
||||
""" Need to override this method to provide menu item for showing Service View """
|
||||
return _("Show or hides the %s window") % self.GetMenuString()
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Event Processing Methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def ProcessEvent(self, event):
|
||||
id = event.GetId()
|
||||
if id == self.SHOW_WINDOW:
|
||||
self.ToggleWindow(event)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def ProcessUpdateUIEvent(self, event):
|
||||
id = event.GetId()
|
||||
if id == self.SHOW_WINDOW:
|
||||
event.Check(self._view != None and self._view.IsShown())
|
||||
event.Enable(True)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# View Methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def _CreateView(self):
|
||||
""" This method needs to be overridden with corresponding ServiceView """
|
||||
return ServiceView(self)
|
||||
|
||||
|
||||
def GetView(self):
|
||||
# Window Menu Service Method
|
||||
return self._view
|
||||
|
||||
|
||||
def SetView(self, view):
|
||||
self._view = view
|
||||
|
||||
|
||||
def ShowWindow(self, show = True):
|
||||
if show:
|
||||
if self._view:
|
||||
if not self._view.IsShown():
|
||||
self._view.Show()
|
||||
else:
|
||||
view = self._CreateView()
|
||||
view.OnCreate(None, flags = 0)
|
||||
self.SetView(view)
|
||||
else:
|
||||
if self._view:
|
||||
if self._view.IsShown():
|
||||
self._view.Hide()
|
||||
|
||||
|
||||
def HideWindow(self):
|
||||
self.ShowWindow(False)
|
||||
|
||||
|
||||
def ToggleWindow(self, event):
|
||||
show = event.IsChecked()
|
||||
wx.ConfigBase_Get().WriteInt(self.GetServiceName()+"Shown", show)
|
||||
self.ShowWindow(show)
|
||||
|
||||
|
||||
def OnCloseFrame(self, event):
|
||||
if not self._view:
|
||||
return True
|
||||
|
||||
if wx.GetApp().IsMDI():
|
||||
self._view.OnCloseWindow(event)
|
||||
# This is called when any SDI frame is closed, so need to check if message window is closing or some other window
|
||||
elif self._view == event.GetEventObject().GetView():
|
||||
self.SetView(None)
|
||||
|
||||
return True
|
||||
|
48
wxPython/samples/ide/activegrid/tool/TabbedView.py
Normal file
48
wxPython/samples/ide/activegrid/tool/TabbedView.py
Normal file
@@ -0,0 +1,48 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: TabbedView.py
|
||||
# Purpose:
|
||||
#
|
||||
# Author: Peter Yared
|
||||
#
|
||||
# Created: 8/17/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import wx
|
||||
import wx.lib.docview
|
||||
|
||||
class TabbedView(dict, wx.lib.docview.View):
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Overridden methods
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
def __init__(self):
|
||||
wx.lib.docview.View.__init__(self)
|
||||
self._views = {}
|
||||
self._currentView = None
|
||||
|
||||
|
||||
def OnCreate(self, doc, flags):
|
||||
frame = wx.GetApp().CreateDocumentFrame(self, doc, flags)
|
||||
sizer = wx.BoxSizer()
|
||||
self._notebook = wx.Notebook(frame, -1, style = wx.NB_BOTTOM)
|
||||
self.Activate()
|
||||
return True
|
||||
|
||||
|
||||
def AddView(self, viewName, view):
|
||||
self._notebook.AddPage(wx.Panel(self._notebook, -1), viewName)
|
||||
self._currentView = view
|
||||
self._views[viewName] = view
|
||||
|
||||
|
||||
def __getattr__(self, attrname):
|
||||
return getattr(self._currentView, attrname)
|
||||
|
||||
|
||||
def SetView(self, viewName):
|
||||
self._currentview = self._views[viewName]
|
||||
|
115
wxPython/samples/ide/activegrid/tool/UICommon.py
Normal file
115
wxPython/samples/ide/activegrid/tool/UICommon.py
Normal file
@@ -0,0 +1,115 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: UICommon.py
|
||||
# Purpose: Shared UI stuff
|
||||
#
|
||||
# Author: Matt Fryer
|
||||
#
|
||||
# Created: 3/10/05
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import wx
|
||||
import ProjectEditor
|
||||
_ = wx.GetTranslation
|
||||
|
||||
def CreateDirectoryControl( parent, fileLabel, dirLabel, fileExtension, startingName="", startingDirectory=""):
|
||||
|
||||
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.SetToolTipString(startingDirectory)
|
||||
button = wx.Button(parent, -1, _("Browse..."), size=(60,-1))
|
||||
|
||||
def OnFindDirClick(event):
|
||||
name = ""
|
||||
nameCtrlValue = nameControl.GetValue()
|
||||
if nameCtrlValue:
|
||||
root, ext = os.path.splitext( nameCtrlValue )
|
||||
if ext == '.' + fileExtension:
|
||||
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)
|
||||
|
||||
if path:
|
||||
dir, filename = os.path.split(path)
|
||||
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"))
|
||||
return False
|
||||
if nameControl.GetValue().find(' ') != -1:
|
||||
wx.MessageBox(_("Please provide a filename that does not contains spaces."), _("Spaces in Filename"))
|
||||
return False
|
||||
filePath = os.path.join(dirControl.GetValue(), MakeNameEndInExtension(nameControl.GetValue(), "." + 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)
|
||||
return (res == wx.YES)
|
||||
else:
|
||||
wx.MessageBox(_("That file already exists. Please choose a different name."), "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(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(wx.StaticText(parent, -1, ""), 0)
|
||||
return nameControl, dirControl, flexGridSizer, Validate
|
||||
|
||||
def AddFilesToCurrentProject(paths, save=False):
|
||||
projectService = wx.GetApp().GetService(ProjectEditor.ProjectService)
|
||||
if projectService:
|
||||
projectDocument = projectService.GetCurrentProject()
|
||||
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))
|
||||
if save:
|
||||
projectDocument.OnSaveDocument(projectDocument.GetFilename())
|
||||
|
||||
def MakeNameEndInExtension(name, extension):
|
||||
if not name:
|
||||
return name
|
||||
root, ext = os.path.splitext(name)
|
||||
if ext == extension:
|
||||
return name
|
||||
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'
|
||||
|
967
wxPython/samples/ide/activegrid/tool/Wizard.py
Normal file
967
wxPython/samples/ide/activegrid/tool/Wizard.py
Normal file
@@ -0,0 +1,967 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: Wizard.py
|
||||
# Purpose:
|
||||
#
|
||||
# Author: Peter Yared
|
||||
#
|
||||
# Created: 10/28/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
import wx
|
||||
import wx.xrc as xrc
|
||||
import wx.wizard
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Classes
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
class BaseWizard(wx.wizard.Wizard):
|
||||
|
||||
|
||||
def __init__(self, parent, title, pos=(-1,-1)):
|
||||
wizardBitMap = getWizardBitmap()
|
||||
wx.wizard.Wizard.__init__(self, parent, wx.NewId(), title, wizardBitMap, pos=pos)
|
||||
|
||||
def GetDocument(self):
|
||||
if self.GetParent() and hasattr(self.GetParent(), 'GetDocument'):
|
||||
return self.GetParent().GetDocument()
|
||||
else:
|
||||
return None
|
||||
|
||||
def SetPrevNext(self, prev, next):
|
||||
prev.SetNext(next)
|
||||
next.SetPrev(prev)
|
||||
|
||||
|
||||
class TitledWizardPage(wx.wizard.PyWizardPage):
|
||||
|
||||
|
||||
def __init__(self, parent, title):
|
||||
self._prev = None
|
||||
self._prevFunc = None
|
||||
self._next = None
|
||||
self._nextFunc = None
|
||||
wx.wizard.PyWizardPage.__init__(self, parent)
|
||||
self.SetSizer(wx.BoxSizer(wx.VERTICAL))
|
||||
self.MakePageTitle(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)
|
||||
self.GetSizer().Add(sizer)
|
||||
|
||||
|
||||
def GetPrev(self):
|
||||
if self._prevFunc:
|
||||
self._prev = self._prevFunc()
|
||||
return self._prev
|
||||
|
||||
|
||||
def SetPrev(self, prev):
|
||||
self._prev = prev
|
||||
self._prevFunc = None
|
||||
|
||||
|
||||
def GetPrevFunc(self):
|
||||
return self._prevFunc
|
||||
|
||||
|
||||
def SetPrevFunc(self, prevFunc):
|
||||
self._prevFunc = prevFunc
|
||||
self._prev = None
|
||||
|
||||
|
||||
def GetNext(self):
|
||||
if self._nextFunc:
|
||||
self._next = self._nextFunc()
|
||||
return self._next
|
||||
|
||||
|
||||
def SetNext(self, next):
|
||||
self._next = next
|
||||
self._nextFunc = None
|
||||
|
||||
|
||||
def GetNextFunc(self):
|
||||
return self._nextFunc
|
||||
|
||||
|
||||
def SetNextFunc(self, nextFunc):
|
||||
self._nextFunc = nextFunc
|
||||
self._next = None
|
||||
|
||||
|
||||
def SetPrevNext(self, prev, next):
|
||||
self._prev = prev
|
||||
self._next = next
|
||||
self._nextFunc = None
|
||||
self._prevFunc = None
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Menu Bitmaps - generated by encode_bitmaps.py
|
||||
#----------------------------------------------------------------------------
|
||||
from wx import ImageFromStream, BitmapFromImage
|
||||
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\
|
||||
\x96\x87\xaeeU\x0f\xfc\t\x06\x01\x12\x16\x1f+\xba\xe7\xf4\xe9\xd4\xb3-d\xf0E\
|
||||
\t\xf8\xb5,\xcb\xbf\xc7c\x06\xc3\x05L\x00\xf0\xfa\xd9~\xbf\xcf\xbf\x81p\r\
|
||||
\xbb\xaf\xf0\xdao\xb1\x97\xfa6L\xdb\xff_?\x00/\xb8\xd6@\xdc\x0f q\xed?\x89w\
|
||||
\xfb\x0e\xfc\xac\x8d8m\xbf\x07\x8514\x8b\x9dU\xefj\xac\x92\xecr\x8d\xa1\x01~\
|
||||
\xe0`\xe3\xc0L\x8c\xc1\x18z\xc2O5\xe6$\xaf\t\xb2\xfd\xd7:\xca\xfd\xc3\x7fw\
|
||||
\xc3\x90\xd8\x18\xfa\xee\xabE\x17\x10\xd8X\xc2\x9c\xa3.\x192\xa5\xd8\x0eoc\
|
||||
\xa86|\x19C\xa5F\x9f\x992\xc8\xacz\xe7\x9fs\xef#80\x8c\xa1\xca\xa0\x9f\xa1\
|
||||
\xaeG\xe7\xca\xe83\xc9J\x80\x13\xfb\xb3\xb6\x17\xa9o\x9f}\xae^~\xd9\x13Du6\
|
||||
\x86*\x83b\x86\xd6\x9a\x1f\x12\xd8\x95\x1d\x01#\xe00t\x7f?c\xa8~\xe8bh\xec+\
|
||||
\xdf\xff\xdd\xcb\xc7J\xaa_\xc2(\x97\xf0\x9c1T\x19\x140\xb4\xd4\xff\x1a{ga\
|
||||
\x1fkvt\xbc\x81mC\x11\xbd\xcd\x86\xea\xc3\x8d\x19\xca\xf4\xbf\x96\xbec\x8a9~\
|
||||
\xd9\xa5\xd7\x04\xed\xfeM\xc3g?\xf1\x97E_\xfcBQ~z\xc9!s\xc0\xc1\xd7=\x03\xdc\
|
||||
?\xe8_\'\x00\x80\xe9\r\xcb\xf3y8\x87\xd6,\x03\x81"_ #\xa7\x01~\xdd+\xeb/1\
|
||||
\x00\xda\x1a\xf31;\xef"\xda\x08\xa5\x83/\xc25A\xb6\xdf\x88\xa183\xa3\xb8\t\
|
||||
\xa3\xa4q\x13\x1b\x9a\xff\xb2wv.\xcf\'\x00\x00<@\xb0\xd7\x99\x00\xf2Ib\x8e\
|
||||
\x9e\x1dm\xe8\r\x1a\x94\xc9\xcc*\xe5gF\xc8Go\xb0\xdb\xec\xb7\xf7\xf7\x8a\xf0\
|
||||
\xca\x1b\xdf\x86\xba\xe1/\x00\xba\r\x95\xaaL\xd2\xb4\xa5dJT\xc7I\xa1\xc6\xb1\
|
||||
\xb0,\x7f\xcf]\xae\x82w*\xc1\xc0\x8e\x05\xaa=\xdau\x7fz\xcfK\xbc\x13\x819Ruw\
|
||||
\xd9\xf5\xb7B\rC\xc3.\xb7\xb7\xed\xed\x83a\x18:M\x00/w\xa4\x08\x90\x19\xe5\
|
||||
\xe5$^x\xa7\t\xf0\x8f\xe3\x02s\xf2(\r\x9fyX\x96\xe5\x1f\xc0\xbf\xae\xff-\xcb\
|
||||
\xf2oY\x96\x7f0M\x97d\xc04}d]\xd4g\x97\xd5\xbbn\xb8\xff\xf5g\xe8\x04p\xb2\
|
||||
\x7f\xc1\xc8\x8e\xcaL\x01\x1b\xba13\xfc\xbb\x90\xa3?S\xb6\x1e\x1b\xba\xbd\
|
||||
\xc01B\x9dg\xbe\r\x14\xb0\xa1\xfbHy\xd7\xa5H\x8f\xce\xe8\xcfP\x00\x80\x17\
|
||||
\xc0\xb2<\xcf\x95\t\xc0\x9a\x87\x85\x9e"\xe6;M\x00\x8f9\xf2Q]\x9es\xd2\xca\
|
||||
\xd7\xc3\xd0\x1d\x17\x98Z<\x0f\xf5\x98\xbd<\x9f\xb7\x9e\xc3\x8e\xe1)r\xbcAh\
|
||||
\xb7\x07\x90\xffJ\x83\x91)\xad\xe8\xbdg8}D\x9b.\xe2\xb1\xd5$dF\xb9\xbf\x0fa=\
|
||||
\xe1T\xd2\xe3\xf1\x07`\xc2>\xb0\x9c\x8e\xfeKg\xee\xdf\x98\xe9be\xa6\xc3N\xbf\
|
||||
w\xa8mO\xa3\xe5\xd1\xcb\xed\xcc\xd0\xb87(\xe8B\xe79mC}\xa6e\xde)\xb0\xd9\xd8\
|
||||
\x9c\xb3\t37\xa8\xb4\xa1>\x1c\x16\xe5F\xafd\x1b\xca\x90yG\xf4c(i\x11\xd1\xe7\
|
||||
\xbe,S\x8964`\xe6c\xae\x18Q\xe1@\xc6\x86v\xecr\t\xe1\xa3\xedE\xd0F\xf0@\xear\
|
||||
\xa3\x1f\x07!5\xe4&]n\x9f\x14\x94)\x7f\x8b\xfbU\xfa\x8d\xb3,O\xa2\x8cOy\xcb\
|
||||
\xe2\x0f\x80\xe6S\x0f\x90\xc4\x8d\xd2Y\xfad, \xc1\xe9\xe37\x00\xfeUb\xce\x87\
|
||||
\xe9\xd3\xfd\xce\x1b;\x1f\xf3\x1c\xb4Q\xe0t8%\x92%\xc2t\xe4@\x80\x00\x92\td\
|
||||
\xeciKK\x10\x06 \x91\n<\xec\x1d\xac\r\xe8\xde\xb7Fjf\xf4\xf1\xd9i@\xb6y\xb9\
|
||||
\x11;\x01z\xd8P,\xa5\x84i\xab>\xa16F\xb1\xd3&\x9a\xb1\x1a\xba\xcd`\xc8\xd3c/\
|
||||
{\xd7\x85iC\x1b3T\xa6\x82\xb8\x8d\x19}\x86c\x87o\x82\xb6\x0c\x1dbz\xe0"7\xd2\
|
||||
n\xac\x8b\xdc\xb4eP\xc7\x82\x81\x8d\xba\xe13g\xde\xe7\x8eN\x01\x00u\xbb\x91\
|
||||
\xaf!\xdd\xe7n#\x97\xe7\xf3\x18\x04\xf9\xdd\xec>u\xc1d^\xd1K\xa4\x87\xbb\x83\
|
||||
\xeboY\xfe\x02@\xbc\xc2J\xaf\xa1\xfaR\xc2`Xx\x0e\xe2\x8dG\xd6k \xd7aU\x86\
|
||||
\xce3\xc0\x13K\xe9\xb8\x0c\x9a\xbeG(n\xfb\xc2\'\xa85\x0b\x11J\x195\x1b\xfa\
|
||||
\x01\x1a\x9d\x81\xfb\x07\xac\xb9h\x93\x82\x92\xdd\x9dD\xa2\x90xO\xe03uE\xca[\
|
||||
\xd5!%\xe7\x0e6\xb4)\\\x1b\xba\xc5S\x01\x10;9\x90\xbd\xab\x81\x06\x0c\xa5,i\
|
||||
\xe0I\xc4<E\xae\xbf\xf6\x01ox<\xe0\x94\x96\xe9^\xcb\xef2f\x0cm\x03/8=\x01\
|
||||
\xc0k\xab\x04\xd7\x86\xba\xef\x13\x1di+ej}\x86\xa6|\xa5\x00\xac\xaf/\x98\x96\
|
||||
\xcc3\xec\xba\xef)\x98\xeb\xdf\\g<\x04\xc1\xef5R\x93H\xe8\xee\xe2\x90\xff\
|
||||
\xa6Q.%m\xc4\xb3\xa1>\xfcl\xc2C\x962\xa66\xf0\xe5\xa6l(u>\xc9\xcbX\x88\xbd\
|
||||
\x0f\xe6):\x1a\xb5e\xec\x13\xc3W\xd8\xd0\xc2$\xb1\xd4\xfbhgj]\x86\xe6\x12\
|
||||
\xc1R_\x1ef\xf7Rz\xba\xf7g\xf6\x84\x8fOi\xb2oT\x11w\xc8XH\xae"K+\x9aL\x1b\
|
||||
\xc9\xdc\x9f\xdc\x01ez\xc3\xe3\xf1\x13\xb0?\xfb\\m\xa4\xd2`\x86H\x12\xcb\x06\
|
||||
\x8f#\x8d\x99J\xe8\x92\x04:pb&\x9f\r\x88z\x0c\xcd%\x82E\x1a\x14\x9f\x9a\xe4q\
|
||||
\xb0,w\xbf\xa3\x8b\xcb\xd4\xf3\xca\xb7\x0eL\x15J\x83\x19g\'\xb1V\xcc\xf4\xa1\
|
||||
\x8c\xa9\xf5\x18\xca\\\x89}5h\xcc\xb1\xa1\xbe.\x8f\xc7\x0f,\xcb\xfa\xa7X\x9a\
|
||||
hu\x0coCSp\x15\x8c1\xb31\xa2]\xf5\xcd\x98Zq\xdaB\x0bO\x1d\xe9\x95p1\x9d\x834\
|
||||
mI\x05\x08V\xbd\\\xe7\x03u\xe5\x9b\x0c\x84RIG\xd8\xe9c\xdaw\x1c\x11\xda\x05\
|
||||
\xe5\xb2N\xae>7\xdb\r\xa5\x9es>u8\x8d\xf7\xb5\xbbY\xef\xe5\xba\xb8\xddv|J\
|
||||
\xb4\x16\x98\xb1U/\x00X\xce\x8f\xe2ArA|\x85\xeb\x8f\x83\x8cs\xbe\x04\xa8Gi\
|
||||
\xf0\xba\xaa\x18>\xeb\x99\xe2\x11\x93\xcf\x0b\xb2?\x1e\xe1\x80\xad\x1eS\xbf)\
|
||||
|F\x00\xd59_*\x1b\xe0\x1e\xe9,}l(\xd4\xb8\x96\xd8\x8dl\x7f.\x1adO\xb1\xe3<\
|
||||
\xe2=\xbf\xa3SoW\xde\x87\xabW\xe2\x9a\x1a\x86\xd6\xb0\xa1>\xee\x90\xce\xa2\
|
||||
\x86\xa1\'O\x910C\xb3\xbe\xdf\xacW\xcc\x18\xcaFM\x1b\x1a+\x0b`\xbc y\xa7Q.\
|
||||
\x88_;G[\xfe\xc8\xf4\x04\x91\xb2\xe3\xbe\xdf\xb7HOp\xe5\x9a\x1a\x86\xb6\xb0\
|
||||
\xa1>Fdj=_nO\x1b\xeaV\xaa\xa0\r\xc5\xae%\xa74fC\xcb\xd1\xd2\x86\xc6\xca\x06\
|
||||
\x88l3\xd7\x10j<E\xb1D\xeb@\'\x00\x99\xa8\x86[\xb6\xe3Q:MiX~XQOQ\xe3.\xb7\
|
||||
\x82\x83\x9b\x92\x97Kv\xce\x97\x82\x9b\xa9\xe8\xeb&\xa0\xd78)(\x1a\x80T|\xeb\
|
||||
t\x96\xee\x01n10\xf2r\xa5\xbb\\\x1fh\x86~\xb6\x8ee\xf4\xea\xc4\xd0\xfe\x13\
|
||||
\xf0\x9a\x88-\xd5h\xc1\xd4\x8e\xd3\x16\xd9\xe2H6\x14\xd3\x0b@\xce\x86":\x01\
|
||||
\x103\xf4\xa5\xf4\xbaSz\x059m\xa4\xb7.\x88^\xad\x0e\xf7i\xb2OQ|\x92]\xa7\xd8\
|
||||
\xf4\xfb\xf0l\x95\x9f\xc4\x06\xc0\xdf\x8a\'\x94\xf9F\xb6\xaa\x13\x9d\xb6\x98\
|
||||
c!\x8a)\\\x01\'\xb1\x91V-\xa7C\xe5\x15\xdc97\x9ad\xb9\x84$1\x80\xa66\xd4\xc7\
|
||||
\xb1\xcd\xab{*\xaf\xb4^_mC\xb74M\xea}w\xa8\xab\xbe{,t\xb4\xa1k\xf7\xb7\xfe\
|
||||
\xe5\x93\xf8\x95\xef5p\xfb\'\x01\xb3\xa1\'\xb0l\xe8Vq\xc1\xfaP\xe2\xca\xef\
|
||||
\x11\x02\xd91\xdcj\x17\x94\x14h\xcey8<J\xeb3\xfe`\xe5\x0f\xc4\xea\xe2t\xd0]\
|
||||
\x95\xdeE\x86\xa1\xf5\x8f\xca:\xb2\xd1\xbd2\xc4\xbfr\xf7\xa5#\xb2\xa7L\xc5\
|
||||
\xb8\x83\xb7\xa4\xfcD\x19W\x10\xad#zY\xb7\xdcI\x0c\x83\xbf\x93X\x80\x083\xf7\
|
||||
\x7f\x1f\xbf\x01\x00\xab\x0f<1\xec\xba\xde\xae~\x12\xa3\xdc6\x87\xd9\xb9_^5\
|
||||
\xa6\xfeu~\x9fe\x9e\xec\x9fS\xee\x03>\x19\x07\x07",]7\xb0\xf2\xbbr\xc1zs\x1b\
|
||||
-U_\x194\xdas\x9e\x90J!\x19\x8b\xf4\xd6\x9f\x02D\x96\xfa;i$\xd4\xc8\x08\xd9g\
|
||||
\xcc\xc6]l\xe8\x8e\x143EX\x1a\xda\xd0\x143\xddc%\xc3#.S\xfa|\x18\xbd,\xc2^)\
|
||||
\x01\x1b\xda\xf0T\x88:\xe9\x1f;\x02\xe6 \xe7\x9c\x9dYuf\x84\x9f\xec\x15\xed1\
|
||||
\xbc\xac\x84u\x03\xc9K\xaa\x1fr%z\xafv\r\xda!\xfd\x03 \x11d\xc6\xc2{\xae\xdf\
|
||||
\x96\xb0\xc9\xd5!_dpt\xb7\x007\xe1\xc8\xe5\x9ae\xb3?\xda\x94>\x03\x1f\x9f\
|
||||
\xd5\xf8\xa8\xacL\xae\xea\x85n7v\x96v|\xf0\x12\xa6\xc8\xe0\x03\xa7H\x81N\x97\
|
||||
>\xcf\xf3\xf5nWe\x92\x984K\x93\xcc\x0c?\x1e\xea&W\xbb\xec\x1d\xcf\xe78\xfb\
|
||||
\x1au8\x106\x97\x01^\xc8\xd2\xa2\x14J<\xbcwrR\x10\xa60\xf4\xf22\xba\xdc\xca\
|
||||
\x86\x92!0\x85!\xdb\xcc|E\xf9K\xf2]\xf4;\x8c>\x8e>G6WH \xdbSE\xd8\xd3\x08,x\
|
||||
\xe0\xd9\xd3\x94L?\x11\xacx\xc0d\xbb\xa0T\x86\x9fz\x92r~\x9c\xa6;}\xeb\xb2\
|
||||
\xcdA<\x01RI\xd8\xd0$\x1d$\xd0\x05c\xc7\xc9\xcfK\xac\xa3b\xdd-\xc0]\x1fN |\
|
||||
\x9d\x9a\xe0,EW\x9fu\n\x82wb\xe8\x8e6\xa15\xb2\x1eA\xd9\x9f\xbf\xafS\x13\\\
|
||||
\x9f\xfd(\x913\x98uj6\xb4\r\xc2HL>M\xa5\xe7\x8ec\x9d\x19\n\xd0$\xb4F\xd5\x03\
|
||||
);\x9c\x00\xa4\xea\xea3\xc5Y\x96\xbf\xcci\x8d\xd9\xd0f\x08#1\xf1\x14\x97p\
|
||||
\xba\xd3\xd6\x96\xf6\x99\x87\xfa\xc8Eb\x00\xfa\xd8Rdn\nP+\x12c\x0cm\x0b\xc6\
|
||||
\xbc\xf4\xc4\xe8\xc6\x0c\x1d\xc0\x86\xee\xc8DbZ\xd8\xd2\x84\x07\x8b\x93$v$p?\
|
||||
\x9f0S\x13\xb3m\x94\xdb\x01\xce\xbc\x94\xe5=j\xc8\xd4\x81\x18\xbac\x00/R,9\
|
||||
\x1c\x08\xe9\xa2\xde}\xc7\xefl\x1d\x9b\r\xed\x02\xf4\xf0\xf6\xad\x01\xa6\xe9\
|
||||
\xbd\xeen\x8e\x06\xd9\xdb\xd4\xf1\x80\x0c\x05\xc8/C\x84\xcaL\x8d{\xb0\xfc\
|
||||
\x13\x9c\x0e\xf6m\xd1\x9ex^Sf&q\xbb$1\x0er+\xbf\x8f\x7f7\xc8\xf8\xc7\xca\x06\
|
||||
b\x16>+\xa4\xa66\xc0\r\xf4/\xb2j\xdaG\xa2\x12\t\x03\x9c\xe3\x98\xaf\xc6\tec2\
|
||||
tG\xac\x1b\xda\xff-\x90\\\x96F\xa6l\x7f$\xbb\xab\\\xd2\x88*\x93\xc4|tgj\xe6C\
|
||||
y\xfd\xc0<\xcf\'6\xd6Y\x0cL\xc7\xd8\x0c\xdd\x91\xfaz\x01\xea2\x95T\xb6D\xb9\
|
||||
\x9am\xa8\x0f\x16S\x85\'\xf0\xd4\x01M\xea\xda\x04\x1f\xdd\xdc\xdf\xfb\xbf\
|
||||
\xb3:\xd3\x9f\xbb\x07CwP6U\xae\xe5\xcc\'m\xe8\xec2\x89\xe0\xca\x0ct\xberm\
|
||||
\xfdm\x8e\x05e8;\x16Ze\x7fK-\x1b 1\xe0bY~\xb9{y"\xac\x8a\xe9\\~\xcd\x18\xaa\
|
||||
\x0c+C\'\xfa(\xaa*\xd8l*`\x87T"\xb4 \xab$\xd9n\x0cU\x86\xb1\x18\x8a\x813m\
|
||||
\xe0\xb0#%7\xbb\xb7\xd2\x85k\xa9\xa30I\xf3P\xb3\xa1_\x85\x0fC\xfd/\x96\xb2\
|
||||
\x82\xd9\xbf\xe7\x85\xfc\x8d\xf2\x1c\x05W\xd28\xf6\x7f\xc7\xae\x1d\x0c \x04\
|
||||
\xd7\xd9\xd7\xa8\xbd\xdf\x05O\x913\xfa6\x86*C\x18\xe0\xe6\xec-\x801\xb4DN\
|
||||
\xee~\xca\xf3\x01\xc6\x9a\x1f\x92X\xc6\x1d\x99#\xe9\xaf\xc6Pe\xa0\x8drs_H\
|
||||
\xea\xbe\xda\xde\'N6\x80\xc4\xfc\x90\xc2\x96\xa2\xb90\xc7\x86\xc6\xdf\xc7\
|
||||
\x18\xaa\x0cc2\xb4D\x0e\'\xc4&bC\x99\xf21\x99\xfe\x98#\xcb\xe8\xfc(|e(g;\x97\
|
||||
\xda\xc8M\xf8K\x9f\xc5d\x89\xb9\xe4<B\x90\xd6\xeap\xc2mo\\&"\x7f\x9c\x007%\
|
||||
\xa1*w\xcf\x11\x04&\x96\'\x96\xe1\xf0\xe3\x94\xfd\x93\xb9\x17y\x8e\x12\xe0&\
|
||||
\xe27\xa3\xf4q\x90s`\xb0\xf0n4mI\\\x13t1\x86\x0c\xad\x99\xe4\x94b\x18\xb7\
|
||||
\x81r\xb2X\xf2\xde\xb4\x1e\xe2\x0683\xf4\xe5\xfd\x1f\xe0:\x13nWIu\x98\x93\
|
||||
\xbc&\xc8\xf6|\x97\xcb\x19\xa4P\x1b/\xd6eN\x0c\x19~\x991y\x1c\xbdNpG\x97o\
|
||||
\xb8\xcb\x11\x99\xf7\xb4\xa1]\xb07p\xa1\x93"u\xcd\x02\xdc\x86\x18\xae\xad>+\
|
||||
\xedrc\xcf\x97\xca\xa1\xc8ma\xcbK\x03\xe3\x82l7\x86*C_\x86\xc6\xe4\\\x91\x95\
|
||||
\x92)!wp\x18C\x95a\x0c\x86b\xb2\xae\xca\x8b\xc9\x94\x90;0\x8c\xa1\xca06C\xaf\
|
||||
\xca\xac)wP\x18C\x95a\x1c\x86b\xf2F\x97; \x8c\xa1\xca0\x16C1\x995\xe5J\xc9\
|
||||
\x1e\x08\xc6Pe\xf8\x1e\x86\xd6\x96=\x08\xc6\xc9)JA*\xdd\xa4G\xdepc\x8c\xd7\
|
||||
\xa0\xca\x18\xd3\x1a\xe35h\x0c5\x99\xa4\x88\xa5c6h\xedD5\xc5\x18\xb3A{0F\tK\
|
||||
\xc7lP\xe5,\xaa\x891\x1b\xb46\x14\x7f0\xe6XP\x86\xf1\x1c\x0b1\xd9w\x93\xdf\t\
|
||||
\xc6Pe\x18\x97\xa1\x98|\xc92\x8c\xa1\x86;\xc0\x18ZCvG\x18C\x95\xc1\x18ZCvG\
|
||||
\x18C\x95a\xfc\xdd8[B\x01C\xd7\x06}\xddc1\xab!\x0fc\xa8\x0b\x05\x0c\xfdN\xe7\
|
||||
|\x0c\nBh[\x97[\xf8\xb4\x8dr\x87\x831\xd4\x85\x02\x86\xde\xabAk3\xc8\x18Z\
|
||||
\x11\n\xd8\xd2\x03\xe36\xa8\xa1\x08\xd6\xa0\xca`\xae?e\x18\xd79\xdfz\xca")\
|
||||
\xbf#\x8c\xa1\xca`\x0c\x95\x96\xdd\x19\xc6Pe\x18\x93\xa15m\x9cbv\x02\x18C\
|
||||
\xd5\xe1>\x0c\xad%[\x11;\x01\x8c\xa1\xea0\x1eCmt{\t\xc6Pe\xf8\x1e\x86*\xf6\
|
||||
\x0e\xb90\x86*\xc3X\x0cm\xc9Ne\xcc\xdc1N\xf8\xacV@\xfb\xcb\x02\xe5\xe34(\x86\
|
||||
Z\xbbp*\xc6\xd8\rZ\x0bJ\xbb[\x80Q\x1a\xd4\xf6\x9b\x17\xc3\x18\rj\x10C\xff\
|
||||
\x06\xada\xe7Fd\xe7~\xf0\xeb\xe9\xe0\xd8\x9f\xf0wp\x8d\x87\xbe\x87\xd9\xd5\
|
||||
\xa8\xf8a\x1a\xd3;\x16\xeb\x05\x02\x87\xd9\xe5\xa7\x97\xe6XP\x86\xcf\xea\xb3\
|
||||
\x92/\xf8\x8acA\x9aI\xa9\xee\xa9*;\x079\x10v{Gc\xa82\x9c\xd7\x87^=.\x92\xfa\
|
||||
\xbc$;\x9b3\x93z \xec\xfb\xfc\xcc\xe9\x1a\xac\xd7\x83\xe3)\x1d\xf9\xe0\\\xc3\
|
||||
\x8e\xd0\xb6\x03a\xbf\x03\xe3\xac\xe0\x96\xb0\xbf%\xb2(eQl!:\x02\xc5X\xec1\
|
||||
\xd7gm\xecZt\x9cs\xd6\xc5\x18\xaa\x0cc0tHv\x96\x8cV==\x83\xe7"e\xc4X\xee\xcb\
|
||||
\xc0\x9e\xf7\xf42\x86*\x03\x9d\xa1\x943O\xb0\xd1\x18\x17\xa5g\xabH2\x13 2\
|
||||
\x92\x8d<\x120)g;\xbd\xe7r\x0c>d@\xa2\x97X\x7f\x1bC\x95\xa1\x1eC\xb1\xeb\x92\
|
||||
\x10\x9fcF\x18\xc0\xb5\xef\xcd=E\xe7\xb63\x86*\x03\x9e$F\xf5\xe4\xc4\x18\xca\
|
||||
\xf1\xdepX\\\xc3\xf3\x83\xb2\x8a9\xeaGmhLF\xc1\x88\xd9/\xcb}\xce\xbboe\xe8<\
|
||||
\xd3\x074\xa9\xfb^\x8cgs\xe5\xe4\x9e\x15A\xda\xd1\xcd\x92S\xd4Ug\xa6-\xb1g\
|
||||
\x0e=\xc3\xe7>\xf1Pj\xe5\xa6\xee\xcb\xc9(\xb1\xa9\xb5"%SD\xb6DyU\xa3;o8\x1au\
|
||||
\x87\xf3.\x1f\x86\x8e\x86\xea\x01\xe9RVyp\xbb@\x00\x9e\x9c\xec\xb4\x85\xfa\
|
||||
\xdc\xa7\x8b\xef\x9f\x82\xe2\xe3\x05m\x1a\x13+\xb7\x9b\x1c&b>]\x18\x89\xa1\
|
||||
\xcdRD\xa8\x8eu\x02\xb2\xae9\xa2.\xc5z\x84\x8e\x8f\xf3n\x9c\xad\x12\x93\x87J\
|
||||
\xa5\x1c \xd2T\x0c\xdf\x9e\x1e\xbe\\\x8f\xa1\xb5\x1a\xb6wC^\xb1w(\x84\xa6;\
|
||||
\xa5\t\x06\xae\x0c\x00\x0bpk\xc4\xb5\xd5gw\xc2e{\xe7B\xca\x0e_\xb5\xa1\xa1.\
|
||||
\xc6Pe\xf8\x0e\x86J\xb8\xf7\x0ePSCJ\xf4*\x94\x03fC\xd5\xe2\x0b\x18\x9a\xf6}\
|
||||
\xb2Q\x93\xed\x02z\x19C\x95\xe1\x0b\x19z\xf1]\xc5\xec\xde\xa6\x9b1\xd4\x90\
|
||||
\x82r\x86\nET0\x99\x83\xb2\xdd\x18\xaa\x0c\xba\x19*\xea\x1d\x82l\xfa\x07\x1f\
|
||||
\x826t\x93g\x0cU\x06\xdd\x0c\xbd\x1a\r\xc1\xe4\x01\x0cjCWyx\xf8L\x0b\xbe\xae\
|
||||
\xcb\x1d1\x05E\n\xe2;\x85"\xe9&\x03B1C\x89\xebR\xa8`\xe5\xde2eJ\xe9\x08\x9a\
|
||||
\x19Z\x1dc\x8e;\xf42\x14M7a\xae!\xe1\xaeQ)\xbd&\xb1\x0b\xcav\xcd\x18\xaa\x0c\
|
||||
\xd6\xa0\xca`\x8e\x05eP\xecX\xa8=\xca\xbd(\xcf\x95)8\xca5\x86*\x83^\x86\xfa\
|
||||
\xa3\\\t\xb7_\xd5P\x1c\x80D;\x18C\x95\xe17\x00\xc0\xd4{\x9f"\x83\x18\xfe\x07\
|
||||
\xd2\x8b\x12\xe7\xc3\x8c\xd4\xb6\x00\x00\x00\x00IEND\xaeB`\x82'
|
||||
|
||||
def getWizardBitmap():
|
||||
return BitmapFromImage(getWizardImage())
|
||||
|
||||
|
||||
def getWizardImage():
|
||||
stream = cStringIO.StringIO(getWizardDataOld()) # NOTE: This reverts us to the bitmap Peter likes.
|
||||
return ImageFromStream(stream)
|
||||
|
165
wxPython/samples/ide/activegrid/tool/XmlEditor.py
Normal file
165
wxPython/samples/ide/activegrid/tool/XmlEditor.py
Normal file
@@ -0,0 +1,165 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: XmlEditor.py
|
||||
# Purpose: Abstract Code Editor for pydocview tbat uses the Styled Text Control
|
||||
#
|
||||
# Author: Peter Yared
|
||||
#
|
||||
# Created: 8/15/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
|
||||
import wx
|
||||
import string
|
||||
import STCTextEditor
|
||||
import CodeEditor
|
||||
|
||||
|
||||
class XmlDocument(CodeEditor.CodeDocument):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class XmlView(CodeEditor.CodeView):
|
||||
|
||||
|
||||
def GetCtrlClass(self):
|
||||
""" Used in split window to instantiate new instances """
|
||||
return XmlCtrl
|
||||
|
||||
|
||||
def GetAutoCompleteHint(self):
|
||||
pos = self.GetCtrl().GetCurrentPos()
|
||||
if pos == 0:
|
||||
return None, None
|
||||
|
||||
validLetters = string.letters + string.digits + '_:'
|
||||
word = ''
|
||||
while (True):
|
||||
pos = pos - 1
|
||||
if pos < 0:
|
||||
break
|
||||
char = chr(self.GetCtrl().GetCharAt(pos))
|
||||
if char not in validLetters:
|
||||
break
|
||||
word = char + word
|
||||
|
||||
return None, word
|
||||
|
||||
|
||||
def GetAutoCompleteDefaultKeywords(self):
|
||||
return XMLKEYWORDS
|
||||
|
||||
|
||||
class XmlService(CodeEditor.CodeService):
|
||||
|
||||
|
||||
def __init__(self):
|
||||
CodeEditor.CodeService.__init__(self)
|
||||
|
||||
|
||||
class XmlCtrl(CodeEditor.CodeCtrl):
|
||||
|
||||
|
||||
def __init__(self, parent, ID = -1, style = wx.NO_FULL_REPAINT_ON_RESIZE):
|
||||
CodeEditor.CodeCtrl.__init__(self, parent, ID, style)
|
||||
self.SetLexer(wx.stc.STC_LEX_XML)
|
||||
self.SetProperty("fold.html", "1")
|
||||
|
||||
|
||||
def GetMatchingBraces(self):
|
||||
return "<>[]{}()"
|
||||
|
||||
|
||||
def CanWordWrap(self):
|
||||
return True
|
||||
|
||||
|
||||
def SetViewDefaults(self):
|
||||
CodeEditor.CodeCtrl.SetViewDefaults(self, configPrefix = "Xml", hasWordWrap = True, hasTabs = True)
|
||||
|
||||
|
||||
def GetFontAndColorFromConfig(self):
|
||||
return CodeEditor.CodeCtrl.GetFontAndColorFromConfig(self, configPrefix = "Xml")
|
||||
|
||||
|
||||
def UpdateStyles(self):
|
||||
CodeEditor.CodeCtrl.UpdateStyles(self)
|
||||
|
||||
if not self.GetFont():
|
||||
return
|
||||
|
||||
faces = { 'font' : self.GetFont().GetFaceName(),
|
||||
'size' : self.GetFont().GetPointSize(),
|
||||
'size2': self.GetFont().GetPointSize() - 2,
|
||||
'color' : "%02x%02x%02x" % (self.GetFontColor().Red(), self.GetFontColor().Green(), self.GetFontColor().Blue())
|
||||
}
|
||||
|
||||
# White space
|
||||
self.StyleSetSpec(wx.stc.STC_H_DEFAULT, "face:%(font)s,fore:#000000,face:%(font)s,size:%(size)d" % faces)
|
||||
# Comment
|
||||
self.StyleSetSpec(wx.stc.STC_H_COMMENT, "face:%(font)s,fore:#007F00,italic,face:%(font)s,size:%(size)d" % faces)
|
||||
# Number
|
||||
self.StyleSetSpec(wx.stc.STC_H_NUMBER, "face:%(font)s,fore:#007F7F,size:%(size)d" % faces)
|
||||
# String
|
||||
self.StyleSetSpec(wx.stc.STC_H_SINGLESTRING, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
self.StyleSetSpec(wx.stc.STC_H_DOUBLESTRING, "face:%(font)s,fore:#7F007F,face:%(font)s,size:%(size)d" % faces)
|
||||
# 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)
|
||||
|
||||
|
||||
class XmlOptionsPanel(STCTextEditor.TextOptionsPanel):
|
||||
|
||||
def __init__(self, parent, id):
|
||||
STCTextEditor.TextOptionsPanel.__init__(self, parent, id, configPrefix = "Xml", label = "XML", hasWordWrap = True, hasTabs = True)
|
||||
|
||||
|
||||
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
|
||||
#----------------------------------------------------------------------------
|
||||
from wx import ImageFromStream, BitmapFromImage
|
||||
from wx import EmptyIcon
|
||||
import cStringIO
|
||||
|
||||
|
||||
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\x18IDAT8\x8d\xed\x92=N\xc3P\x0c\xc7\x7f~\xc9K\xd2\xa0\x16\xa9\xdc\
|
||||
\x84+\xf4\x06\x1c\xa1\x12\x133\xa7`\xea\x05\xba\xc0\xc0\xd0\x93\x80*uc``e\t\
|
||||
\x82\xb6J\xf3Q?3D\x04\x81`\xea\xc2\x80\x17K\xb6\xfc\xff\xb0-ff\x1c\x10\xee\
|
||||
\x90\xe1\xbf\x01\x10s}\x0em\tu\t\xfb\x06\xcbFP\xad\x11\x17\x81\x196\x18!\xdb\
|
||||
\x02\xd2#hk\xc8\x8f\t\xc1p\x89g\xb9\\\x11\xdb\xfd-\xbcn\x91\xa8C\x94,\x81\
|
||||
\xaa\xe9\x19\xe4\x1b\xa3}R\xf3\xf0\x08\x0e\x9f\x81\xef\x9c\x94s\x83\xaa\xe92\
|
||||
P\xcf\nv\xa7g\xd4\xb3\xa2\xef\xaf\xc5#i\x04\x89#\x8a\x05\'m\r)\x84\r\xe4S\
|
||||
\xa1\x9c\x1b\xf9\xb4\xe3\xd5\xe1\x18?\xb9@\x87\xe3^\x81\xbe\xb5H\xab`\x013\
|
||||
\xc3\xa9\xf3h\x15pC\xfa\xe1\x0f\x05\x00\xf1\xd5\xe4\x8b\x85la\x10@[0q\x88]\
|
||||
\x9e\x18/\x05\xe8/k\xde\x01\x83\x1f\xea\x19,\x9e\x1c\xf1\xcdj\xc3\xae\x01jP\
|
||||
\x05\x9fv\x07q1\x88\x83(\x8f\xd0\x8d"1h\x05\xba\x077\x80$\x87\xbb\xe7\x80\
|
||||
\xfc\xbf\xf2\xe1\x00\xef\x8c\xb8x\x06\x07\xd1$\xff\x00\x00\x00\x00IEND\xaeB`\
|
||||
\x82'
|
||||
|
||||
|
||||
def getXMLBitmap():
|
||||
return BitmapFromImage(getXMLImage())
|
||||
|
||||
def getXMLImage():
|
||||
stream = cStringIO.StringIO(getXMLData())
|
||||
return ImageFromStream(stream)
|
||||
|
||||
def getXMLIcon():
|
||||
icon = EmptyIcon()
|
||||
icon.CopyFromBitmap(getXMLBitmap())
|
||||
return icon
|
0
wxPython/samples/ide/activegrid/tool/__init__.py
Normal file
0
wxPython/samples/ide/activegrid/tool/__init__.py
Normal file
896
wxPython/samples/ide/activegrid/tool/checker.py
Normal file
896
wxPython/samples/ide/activegrid/tool/checker.py
Normal file
@@ -0,0 +1,896 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright (c) 2001-2004, MetaSlash Inc. All rights reserved.
|
||||
|
||||
"""
|
||||
Copyright notice from pychecker:
|
||||
|
||||
Copyright (c) 2000-2001, MetaSlash Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
- Neither name of MetaSlash Inc. nor the names of contributors
|
||||
may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
Check python source code files for possible errors and print warnings
|
||||
|
||||
Contact Info:
|
||||
http://pychecker.sourceforge.net/
|
||||
pychecker-list@lists.sourceforge.net
|
||||
"""
|
||||
|
||||
import string
|
||||
import types
|
||||
import sys
|
||||
import imp
|
||||
import os
|
||||
import glob
|
||||
import traceback
|
||||
import re
|
||||
import wx
|
||||
_ = wx.GetTranslation
|
||||
|
||||
# see __init__.py for meaning, this must match the version there
|
||||
LOCAL_MAIN_VERSION = 1
|
||||
|
||||
|
||||
def setupNamespace(path) :
|
||||
# remove pychecker if it's the first component, it needs to be last
|
||||
if sys.path[0][-9:] == 'pychecker' :
|
||||
del sys.path[0]
|
||||
|
||||
# make sure pychecker is last in path, so we can import
|
||||
checker_path = os.path.dirname(os.path.dirname(path))
|
||||
if checker_path not in sys.path :
|
||||
sys.path.append(checker_path)
|
||||
|
||||
if __name__ == '__main__' :
|
||||
setupNamespace(sys.argv[0])
|
||||
|
||||
from pychecker import utils
|
||||
from pychecker import printer
|
||||
from pychecker import warn
|
||||
from pychecker import OP
|
||||
from pychecker import Config
|
||||
from pychecker import function
|
||||
from pychecker.Warning import Warning
|
||||
|
||||
# Globals for storing a dictionary of info about modules and classes
|
||||
_allModules = {}
|
||||
_cfg = None
|
||||
|
||||
# Constants
|
||||
_DEFAULT_MODULE_TOKENS = ('__builtins__', '__doc__', '__file__', '__name__',
|
||||
'__path__')
|
||||
_DEFAULT_CLASS_TOKENS = ('__doc__', '__name__', '__module__')
|
||||
|
||||
_VERSION_MISMATCH_ERROR = '''
|
||||
There seem to be two versions of PyChecker being used.
|
||||
One is probably in python/site-packages, the other in a local directory.
|
||||
If you want to run the local version, you must remove the version
|
||||
from site-packages. Or you can install the current version
|
||||
by doing python setup.py install.
|
||||
'''
|
||||
|
||||
def cfg() :
|
||||
return utils.cfg()
|
||||
|
||||
def _flattenList(list) :
|
||||
"Returns a list which contains no lists"
|
||||
|
||||
new_list = []
|
||||
for element in list :
|
||||
if type(element) == types.ListType :
|
||||
new_list.extend(_flattenList(element))
|
||||
else :
|
||||
new_list.append(element)
|
||||
|
||||
return new_list
|
||||
|
||||
def getModules(arg_list) :
|
||||
"Returns a list of module names that can be imported"
|
||||
|
||||
global _output
|
||||
|
||||
new_arguments = []
|
||||
for arg in arg_list :
|
||||
# is this a wildcard filespec? (necessary for windows)
|
||||
if '*' in arg or '?' in arg or '[' in arg :
|
||||
arg = glob.glob(arg)
|
||||
new_arguments.append(arg)
|
||||
|
||||
PY_SUFFIXES = ['.py']
|
||||
PY_SUFFIX_LENS = [3]
|
||||
if _cfg.quixote:
|
||||
PY_SUFFIXES.append('.ptl')
|
||||
PY_SUFFIX_LENS.append(4)
|
||||
|
||||
modules = []
|
||||
for arg in _flattenList(new_arguments) :
|
||||
fullpath = arg
|
||||
# is it a .py file?
|
||||
for suf, suflen in zip(PY_SUFFIXES, PY_SUFFIX_LENS):
|
||||
if len(arg) > suflen and arg[-suflen:] == suf:
|
||||
arg_dir = os.path.dirname(arg)
|
||||
if arg_dir and not os.path.exists(arg) :
|
||||
txt = _('File or pathname element does not exist: "%s"') % arg
|
||||
_output.AddLines(txt)
|
||||
continue
|
||||
|
||||
module_name = os.path.basename(arg)[:-suflen]
|
||||
if arg_dir not in sys.path :
|
||||
sys.path.insert(0, arg_dir)
|
||||
arg = module_name
|
||||
modules.append((arg, fullpath))
|
||||
|
||||
return modules
|
||||
|
||||
def _q_file(f):
|
||||
# crude hack!!!
|
||||
# imp.load_module requires a real file object, so we can't just
|
||||
# fiddle def lines and yield them
|
||||
import tempfile
|
||||
fd, newfname = tempfile.mkstemp(suffix=".py", text=True)
|
||||
newf = os.fdopen(fd, 'r+')
|
||||
os.unlink(newfname)
|
||||
for line in f:
|
||||
mat = re.match(r'(\s*def\s+\w+\s*)\[(html|plain)\](.*)', line)
|
||||
if mat is None:
|
||||
newf.write(line)
|
||||
else:
|
||||
newf.write(mat.group(1)+mat.group(3)+'\n')
|
||||
newf.seek(0)
|
||||
return newf
|
||||
|
||||
def _q_find_module(p, path):
|
||||
if not _cfg.quixote:
|
||||
return imp.find_module(p, path)
|
||||
else:
|
||||
for direc in path:
|
||||
try:
|
||||
return imp.find_module(p, [direc])
|
||||
except ImportError:
|
||||
f = os.path.join(direc, p+".ptl")
|
||||
if os.path.exists(f):
|
||||
return _q_file(file(f)), f, ('.ptl', 'U', 1)
|
||||
|
||||
def _findModule(name) :
|
||||
"""Returns the result of an imp.find_module(), ie, (file, filename, smt)
|
||||
name can be a module or a package name. It is *not* a filename."""
|
||||
|
||||
path = sys.path[:]
|
||||
packages = string.split(name, '.')
|
||||
for p in packages :
|
||||
# smt = (suffix, mode, type)
|
||||
file, filename, smt = _q_find_module(p, path)
|
||||
if smt[-1] == imp.PKG_DIRECTORY :
|
||||
try :
|
||||
# package found - read path info from init file
|
||||
m = imp.load_module(p, file, filename, smt)
|
||||
finally :
|
||||
if file is not None :
|
||||
file.close()
|
||||
|
||||
# importing xml plays a trick, which replaces itself with _xmlplus
|
||||
# both have subdirs w/same name, but different modules in them
|
||||
# we need to choose the real (replaced) version
|
||||
if m.__name__ != p :
|
||||
try :
|
||||
file, filename, smt = _q_find_module(m.__name__, path)
|
||||
m = imp.load_module(p, file, filename, smt)
|
||||
finally :
|
||||
if file is not None :
|
||||
file.close()
|
||||
|
||||
new_path = m.__path__
|
||||
if type(new_path) == types.ListType :
|
||||
new_path = filename
|
||||
if new_path not in path :
|
||||
path.insert(1, new_path)
|
||||
elif smt[-1] != imp.PY_COMPILED:
|
||||
if p is not packages[-1] :
|
||||
if file is not None :
|
||||
file.close()
|
||||
raise ImportError, "No module named %s" % packages[-1]
|
||||
return file, filename, smt
|
||||
|
||||
# in case we have been given a package to check
|
||||
return file, filename, smt
|
||||
|
||||
|
||||
class Variable :
|
||||
"Class to hold all information about a variable"
|
||||
|
||||
def __init__(self, name, type):
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.value = None
|
||||
|
||||
def __str__(self) :
|
||||
return self.name
|
||||
|
||||
__repr__ = utils.std_repr
|
||||
|
||||
|
||||
def _filterDir(object, ignoreList) :
|
||||
"Return a list of tokens (attributes) in a class, except for ignoreList"
|
||||
|
||||
tokens = dir(object)
|
||||
for token in ignoreList :
|
||||
if token in tokens :
|
||||
tokens.remove(token)
|
||||
return tokens
|
||||
|
||||
def _getClassTokens(c) :
|
||||
return _filterDir(c, _DEFAULT_CLASS_TOKENS)
|
||||
|
||||
|
||||
class Class :
|
||||
"Class to hold all information about a class"
|
||||
|
||||
def __init__(self, name, module) :
|
||||
self.name = name
|
||||
self.classObject = getattr(module, name)
|
||||
|
||||
modname = getattr(self.classObject, '__module__', None)
|
||||
if modname is None:
|
||||
# hm, some ExtensionClasses don't have a __module__ attribute
|
||||
# so try parsing the type output
|
||||
typerepr = repr(type(self.classObject))
|
||||
mo = re.match("^<type ['\"](.+)['\"]>$", typerepr)
|
||||
if mo:
|
||||
modname = ".".join(mo.group(1).split(".")[:-1])
|
||||
|
||||
self.module = sys.modules.get(modname)
|
||||
if not self.module:
|
||||
self.module = module
|
||||
|
||||
global _output
|
||||
txt = _("warning: couldn't find real module for class %s (module name: %s)\n") % (self.classObject, modname)
|
||||
_output.AddLines(txt)
|
||||
|
||||
self.ignoreAttrs = 0
|
||||
self.methods = {}
|
||||
self.members = { '__class__': types.ClassType,
|
||||
'__doc__': types.StringType,
|
||||
'__dict__': types.DictType, }
|
||||
self.memberRefs = {}
|
||||
self.statics = {}
|
||||
self.lineNums = {}
|
||||
|
||||
def __str__(self) :
|
||||
return self.name
|
||||
|
||||
__repr__ = utils.std_repr
|
||||
|
||||
def getFirstLine(self) :
|
||||
"Return first line we can find in THIS class, not any base classes"
|
||||
|
||||
lineNums = []
|
||||
classDir = dir(self.classObject)
|
||||
for m in self.methods.values() :
|
||||
if m != None and m.function.func_code.co_name in classDir:
|
||||
lineNums.append(m.function.func_code.co_firstlineno)
|
||||
if lineNums :
|
||||
return min(lineNums)
|
||||
return 0
|
||||
|
||||
|
||||
def allBaseClasses(self, c = None) :
|
||||
"Return a list of all base classes for this class and it's subclasses"
|
||||
|
||||
baseClasses = []
|
||||
if c == None :
|
||||
c = self.classObject
|
||||
for base in c.__bases__ :
|
||||
baseClasses = baseClasses + [ base ] + self.allBaseClasses(base)
|
||||
return baseClasses
|
||||
|
||||
def __getMethodName(self, func_name, className = None) :
|
||||
if func_name[0:2] == '__' and func_name[-2:] != '__' :
|
||||
if className == None :
|
||||
className = self.name
|
||||
if className[0] != '_' :
|
||||
className = '_' + className
|
||||
func_name = className + func_name
|
||||
return func_name
|
||||
|
||||
def addMethod(self, method, methodName = None) :
|
||||
if type(method) == types.StringType :
|
||||
self.methods[method] = None
|
||||
else :
|
||||
assert methodName is not None, "must supply methodName"
|
||||
self.methods[methodName] = function.Function(method, 1)
|
||||
|
||||
def addMethods(self, classObject) :
|
||||
for classToken in _getClassTokens(classObject) :
|
||||
token = getattr(classObject, classToken, None)
|
||||
if token is None:
|
||||
continue
|
||||
|
||||
# Looks like a method. Need to code it this way to
|
||||
# accommodate ExtensionClass and Python 2.2. Yecchh.
|
||||
if (hasattr(token, "func_code") and
|
||||
hasattr(token.func_code, "co_argcount")):
|
||||
self.addMethod(token, token.__name__)
|
||||
|
||||
elif hasattr(token, '__get__') and \
|
||||
not hasattr(token, '__set__') and \
|
||||
type(token) is not types.ClassType :
|
||||
self.addMethod(getattr(token, '__name__', classToken))
|
||||
else :
|
||||
self.members[classToken] = type(token)
|
||||
self.memberRefs[classToken] = None
|
||||
|
||||
self.cleanupMemberRefs()
|
||||
# add standard methods
|
||||
for methodName in ('__class__',) :
|
||||
self.addMethod(methodName, classObject.__name__)
|
||||
|
||||
def addMembers(self, classObject) :
|
||||
if not cfg().onlyCheckInitForMembers :
|
||||
for classToken in _getClassTokens(classObject) :
|
||||
method = getattr(classObject, classToken, None)
|
||||
if type(method) == types.MethodType :
|
||||
self.addMembersFromMethod(method.im_func)
|
||||
else:
|
||||
try:
|
||||
self.addMembersFromMethod(classObject.__init__.im_func)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def addMembersFromMethod(self, method) :
|
||||
if not hasattr(method, 'func_code') :
|
||||
return
|
||||
|
||||
func_code, code, i, maxCode, extended_arg = OP.initFuncCode(method)
|
||||
stack = []
|
||||
while i < maxCode :
|
||||
op, oparg, i, extended_arg = OP.getInfo(code, i, extended_arg)
|
||||
if op >= OP.HAVE_ARGUMENT :
|
||||
operand = OP.getOperand(op, func_code, oparg)
|
||||
if OP.LOAD_CONST(op) or OP.LOAD_FAST(op) :
|
||||
stack.append(operand)
|
||||
elif OP.STORE_ATTR(op) :
|
||||
if len(stack) > 0 :
|
||||
if stack[-1] == cfg().methodArgName:
|
||||
value = None
|
||||
if len(stack) > 1 :
|
||||
value = type(stack[-2])
|
||||
self.members[operand] = value
|
||||
self.memberRefs[operand] = None
|
||||
stack = []
|
||||
|
||||
self.cleanupMemberRefs()
|
||||
|
||||
def cleanupMemberRefs(self) :
|
||||
try :
|
||||
del self.memberRefs[Config.CHECKER_VAR]
|
||||
except KeyError :
|
||||
pass
|
||||
|
||||
def abstractMethod(self, m):
|
||||
"""Return 1 if method is abstract, None if not
|
||||
An abstract method always raises an exception.
|
||||
"""
|
||||
if not self.methods.get(m, None):
|
||||
return None
|
||||
func_code, bytes, i, maxCode, extended_arg = \
|
||||
OP.initFuncCode(self.methods[m].function)
|
||||
# abstract if the first conditional is RAISE_VARARGS
|
||||
while i < maxCode:
|
||||
op, oparg, i, extended_arg = OP.getInfo(bytes, i, extended_arg)
|
||||
if OP.RAISE_VARARGS(op):
|
||||
return 1
|
||||
if OP.conditional(op):
|
||||
break
|
||||
return None
|
||||
|
||||
def isAbstract(self):
|
||||
"""Return the method names that make a class abstract.
|
||||
An abstract class has at least one abstract method."""
|
||||
result = []
|
||||
for m in self.methods.keys():
|
||||
if self.abstractMethod(m):
|
||||
result.append(m)
|
||||
return result
|
||||
|
||||
def _getLineInFile(moduleName, linenum):
|
||||
line = ''
|
||||
file, filename, smt = _findModule(moduleName)
|
||||
try:
|
||||
lines = file.readlines()
|
||||
line = string.rstrip(lines[linenum - 1])
|
||||
except (IOError, IndexError):
|
||||
pass
|
||||
file.close()
|
||||
return line
|
||||
|
||||
def importError(moduleName):
|
||||
exc_type, exc_value, tb = sys.exc_info()
|
||||
|
||||
# First, try to get a nice-looking name for this exception type.
|
||||
exc_name = getattr(exc_type, '__name__', None)
|
||||
if not exc_name:
|
||||
# either it's a string exception or a user-defined exception class
|
||||
# show string or fully-qualified class name
|
||||
exc_name = str(exc_type)
|
||||
|
||||
# Print a traceback, unless this is an ImportError. ImportError is
|
||||
# presumably the most common import-time exception, so this saves
|
||||
# the clutter of a traceback most of the time. Also, the locus of
|
||||
# the error is usually irrelevant for ImportError, so the lack of
|
||||
# traceback shouldn't be a problem.
|
||||
if exc_type is SyntaxError:
|
||||
# SyntaxErrors are special, we want to control how we format
|
||||
# the output and make it consistent for all versions of Python
|
||||
e = exc_value
|
||||
msg = '%s (%s, line %d)' % (e.msg, e.filename, e.lineno)
|
||||
line = _getLineInFile(moduleName, e.lineno)
|
||||
offset = e.offset
|
||||
if type(offset) is not types.IntType:
|
||||
offset = 0
|
||||
exc_value = '%s\n %s\n %s^' % (msg, line, ' ' * offset)
|
||||
elif exc_type is not ImportError:
|
||||
global _output
|
||||
txt = _(" Caught exception importing module %s:\n") % moduleName
|
||||
_output.AddLines(txt)
|
||||
|
||||
try:
|
||||
tbinfo = traceback.extract_tb(tb)
|
||||
except:
|
||||
tbinfo = []
|
||||
txt = _(" Unable to format traceback\n")
|
||||
_output.AddLines(txt)
|
||||
for filename, line, func, text in tbinfo[1:]:
|
||||
txt = _(" File \"%s\", line %d") % (filename, line)
|
||||
_output.AddLines(txt)
|
||||
if func != "?":
|
||||
txt = _(", in %s()") % func
|
||||
_output.AddLines(txt)
|
||||
_output.AddLines("\n")
|
||||
if text:
|
||||
txt = _(" %s\n") % text
|
||||
_output.AddLines(txt)
|
||||
|
||||
# And finally print the exception type and value.
|
||||
# Careful formatting exc_value -- can fail for some user exceptions
|
||||
txt = " %s: " % exc_name
|
||||
_output.AddLines(txt)
|
||||
try:
|
||||
txt = str(exc_value) + '\n'
|
||||
_output.AddLines(txt)
|
||||
except:
|
||||
txt = _('**error formatting exception value**\n')
|
||||
_output.AddLines(txt)
|
||||
|
||||
|
||||
def _getPyFile(filename):
|
||||
"""Return the file and '.py' filename from a filename which could
|
||||
end with .py, .pyc, or .pyo"""
|
||||
|
||||
if filename[-1] in 'oc' and filename[-4:-1] == '.py':
|
||||
return filename[:-1]
|
||||
return filename
|
||||
|
||||
class Module :
|
||||
"Class to hold all information for a module"
|
||||
|
||||
def __init__(self, moduleName, check = 1, fullpath = None) :
|
||||
self.moduleName = moduleName
|
||||
self.variables = {}
|
||||
self.functions = {}
|
||||
self.classes = {}
|
||||
self.modules = {}
|
||||
self.moduleLineNums = {}
|
||||
self.attributes = [ '__dict__' ]
|
||||
self.main_code = None
|
||||
self.module = None
|
||||
self.check = check
|
||||
self.fullpath = fullpath
|
||||
_allModules[moduleName] = self
|
||||
|
||||
def __str__(self) :
|
||||
return self.moduleName
|
||||
|
||||
__repr__ = utils.std_repr
|
||||
|
||||
def addVariable(self, var, varType) :
|
||||
self.variables[var] = Variable(var, varType)
|
||||
|
||||
def addFunction(self, func) :
|
||||
self.functions[func.__name__] = function.Function(func)
|
||||
|
||||
def __addAttributes(self, c, classObject) :
|
||||
for base in classObject.__bases__ :
|
||||
self.__addAttributes(c, base)
|
||||
c.addMethods(classObject)
|
||||
c.addMembers(classObject)
|
||||
|
||||
def addClass(self, name) :
|
||||
self.classes[name] = c = Class(name, self.module)
|
||||
try:
|
||||
objName = str(c.classObject)
|
||||
except TypeError:
|
||||
# this can happen if there is a goofy __getattr__
|
||||
c.ignoreAttrs = 1
|
||||
else:
|
||||
packages = string.split(objName, '.')
|
||||
c.ignoreAttrs = packages[0] in cfg().blacklist
|
||||
if not c.ignoreAttrs :
|
||||
self.__addAttributes(c, c.classObject)
|
||||
|
||||
def addModule(self, name) :
|
||||
module = _allModules.get(name, None)
|
||||
if module is None :
|
||||
self.modules[name] = module = Module(name, 0)
|
||||
if imp.is_builtin(name) == 0 :
|
||||
module.load()
|
||||
else :
|
||||
globalModule = globals().get(name)
|
||||
if globalModule :
|
||||
module.attributes.extend(dir(globalModule))
|
||||
else :
|
||||
self.modules[name] = module
|
||||
|
||||
def filename(self) :
|
||||
try :
|
||||
filename = self.module.__file__
|
||||
except AttributeError :
|
||||
filename = self.moduleName
|
||||
return _getPyFile(filename)
|
||||
|
||||
def load(self, warnings = None):
|
||||
try :
|
||||
# there's no need to reload modules we already have
|
||||
global _output, _statusDlg, _count
|
||||
txt = _("Loading Module %s\n") % self.moduleName
|
||||
_output.AddLines(txt)
|
||||
_count += 1
|
||||
if _count == 100:
|
||||
_count = 95
|
||||
_statusDlg.Update(_count, txt)
|
||||
|
||||
module = sys.modules.get(self.moduleName)
|
||||
if module :
|
||||
if not _allModules[self.moduleName].module :
|
||||
return self._initModule(module)
|
||||
return 1
|
||||
|
||||
return self._initModule(self.setupMainCode())
|
||||
except (SystemExit, KeyboardInterrupt) :
|
||||
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||
raise exc_type, exc_value
|
||||
except SyntaxError, (message, (fileName, line, col, text)):
|
||||
# ActiveGrid: added this for better feedback when module couldn't be loaded.
|
||||
w = Warning(self.fullpath, line, _("Syntax Error: %s\n%s\n%s^error near here") % (message, text, ' '*(col-1)))
|
||||
warnings.append(w)
|
||||
return 0
|
||||
except:
|
||||
w = Warning(self.moduleName, 1, sys.exc_info()[0] + " NOT PROCESSED UNABLE TO IMPORT")
|
||||
warnings.append(w)
|
||||
importError(self.moduleName)
|
||||
return 0
|
||||
|
||||
def initModule(self, module) :
|
||||
if not self.module:
|
||||
filename = _getPyFile(module.__file__)
|
||||
if string.lower(filename[-3:]) == '.py':
|
||||
try:
|
||||
file = open(filename)
|
||||
except IOError:
|
||||
pass
|
||||
else:
|
||||
self._setupMainCode(file, filename, module)
|
||||
return self._initModule(module)
|
||||
return 1
|
||||
|
||||
def _initModule(self, module):
|
||||
self.module = module
|
||||
self.attributes = dir(self.module)
|
||||
|
||||
pychecker_attr = getattr(module, Config.CHECKER_VAR, None)
|
||||
if pychecker_attr is not None :
|
||||
utils.pushConfig()
|
||||
utils.updateCheckerArgs(pychecker_attr, 'suppressions', 0, [])
|
||||
|
||||
for tokenName in _filterDir(self.module, _DEFAULT_MODULE_TOKENS) :
|
||||
token = getattr(self.module, tokenName)
|
||||
if isinstance(token, types.ModuleType) :
|
||||
# get the real module name, tokenName could be an alias
|
||||
self.addModule(token.__name__)
|
||||
elif isinstance(token, types.FunctionType) :
|
||||
self.addFunction(token)
|
||||
elif isinstance(token, types.ClassType) or \
|
||||
hasattr(token, '__bases__') :
|
||||
self.addClass(tokenName)
|
||||
else :
|
||||
self.addVariable(tokenName, type(token))
|
||||
|
||||
if pychecker_attr is not None :
|
||||
utils.popConfig()
|
||||
return 1
|
||||
|
||||
def setupMainCode(self) :
|
||||
file, filename, smt = _findModule(self.moduleName)
|
||||
# FIXME: if the smt[-1] == imp.PKG_DIRECTORY : load __all__
|
||||
module = imp.load_module(self.moduleName, file, filename, smt)
|
||||
self._setupMainCode(file, filename, module)
|
||||
return module
|
||||
|
||||
def _setupMainCode(self, file, filename, module):
|
||||
try :
|
||||
self.main_code = function.create_from_file(file, filename, module)
|
||||
finally :
|
||||
if file != None :
|
||||
file.close()
|
||||
|
||||
|
||||
def getAllModules() :
|
||||
"Returns a list of all modules that should be checked."
|
||||
modules = []
|
||||
for module in _allModules.values() :
|
||||
if module.check :
|
||||
modules.append(module)
|
||||
return modules
|
||||
|
||||
_BUILTIN_MODULE_ATTRS = { 'sys': [ 'ps1', 'ps2', 'tracebacklimit',
|
||||
'exc_type', 'exc_value', 'exc_traceback',
|
||||
'last_type', 'last_value', 'last_traceback',
|
||||
],
|
||||
}
|
||||
|
||||
def fixupBuiltinModules(needs_init=0):
|
||||
for moduleName in sys.builtin_module_names :
|
||||
if needs_init:
|
||||
_ = Module(moduleName, 0)
|
||||
module = _allModules.get(moduleName, None)
|
||||
if module is not None :
|
||||
try :
|
||||
m = imp.init_builtin(moduleName)
|
||||
except ImportError :
|
||||
pass
|
||||
else :
|
||||
extra_attrs = _BUILTIN_MODULE_ATTRS.get(moduleName, [])
|
||||
module.attributes = [ '__dict__' ] + dir(m) + extra_attrs
|
||||
|
||||
|
||||
def _printWarnings(warnings, stream=None):
|
||||
if stream is None:
|
||||
stream = sys.stdout
|
||||
|
||||
warnings.sort()
|
||||
lastWarning = None
|
||||
for warning in warnings :
|
||||
if lastWarning != None :
|
||||
# ignore duplicate warnings
|
||||
if cmp(lastWarning, warning) == 0 :
|
||||
continue
|
||||
# print blank line between files
|
||||
if lastWarning.file != warning.file :
|
||||
global _output
|
||||
_output.AddLines("\n")
|
||||
|
||||
lastWarning = warning
|
||||
_output.AddLines(warning.format() + "\n")
|
||||
|
||||
|
||||
def processFiles(files, cfg = None, pre_process_cb = None) :
|
||||
# insert this here, so we find files in the local dir before std library
|
||||
if sys.path[0] != '' :
|
||||
sys.path.insert(0, '')
|
||||
|
||||
# ensure we have a config object, it's necessary
|
||||
global _cfg
|
||||
if cfg is not None :
|
||||
_cfg = cfg
|
||||
elif _cfg is None :
|
||||
_cfg = Config.Config()
|
||||
|
||||
warnings = []
|
||||
utils.initConfig(_cfg)
|
||||
for moduleName, filename in getModules(files) :
|
||||
if callable(pre_process_cb) :
|
||||
pre_process_cb(moduleName)
|
||||
module = Module(moduleName, fullpath = filename)
|
||||
|
||||
module.load(warnings)
|
||||
utils.popConfig()
|
||||
return warnings
|
||||
|
||||
|
||||
def getWarnings(files, cfg = None, suppressions = None):
|
||||
warnings = processFiles(files, cfg)
|
||||
fixupBuiltinModules()
|
||||
return warnings + warn.find(getAllModules(), _cfg, suppressions)
|
||||
|
||||
|
||||
def _print_processing(name) :
|
||||
if not _cfg.quiet :
|
||||
global _output, _statusDlg, _count
|
||||
txt = _("Processing %s...\n") % name
|
||||
_output.AddLines(txt)
|
||||
_count += 1
|
||||
_statusDlg.Update(_count, txt)
|
||||
|
||||
|
||||
|
||||
def checkSyntax(filename, messageView):
|
||||
""" Massively hacked version of main for ActiveGrid IDE integration """
|
||||
global _cfg
|
||||
_cfg, files, suppressions = Config.setupFromArgs([filename])
|
||||
if not files :
|
||||
return 0
|
||||
|
||||
global _output, _statusDlg, _count
|
||||
_output = messageView
|
||||
# wxBug: Need to show progress dialog box, or message window never gets updated until the method returns
|
||||
_statusDlg = wx.ProgressDialog(_("Check Code"), _("Checking %s") % filename, maximum = 100, style = wx.PD_AUTO_HIDE | wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME)
|
||||
_count = 0
|
||||
|
||||
# insert this here, so we find files in the local dir before std library
|
||||
sys.path.insert(0, '')
|
||||
|
||||
importWarnings = processFiles(files, _cfg, _print_processing)
|
||||
fixupBuiltinModules()
|
||||
if _cfg.printParse :
|
||||
for module in getAllModules() :
|
||||
printer.module(module)
|
||||
|
||||
warnings = warn.find(getAllModules(), _cfg, suppressions)
|
||||
|
||||
_statusDlg.Update(100, _("Done"))
|
||||
_statusDlg.Destroy()
|
||||
|
||||
if not _cfg.quiet :
|
||||
_output.AddLines(_("\nWarnings and Errors...\n"))
|
||||
if warnings or importWarnings :
|
||||
_printWarnings(importWarnings + warnings)
|
||||
return 1
|
||||
|
||||
if not _cfg.quiet :
|
||||
_output.AddLines(_("No Syntax Errors"))
|
||||
return 0
|
||||
|
||||
##
|
||||
##
|
||||
##def main(argv) :
|
||||
## __pychecker__ = 'no-miximport'
|
||||
## import pychecker
|
||||
## if LOCAL_MAIN_VERSION != pychecker.MAIN_MODULE_VERSION :
|
||||
## sys.stderr.write(_VERSION_MISMATCH_ERROR)
|
||||
## sys.exit(100)
|
||||
##
|
||||
## # remove empty arguments
|
||||
## argv = filter(None, argv)
|
||||
##
|
||||
## # if the first arg starts with an @, read options from the file
|
||||
## # after the @ (this is mostly for windows)
|
||||
## if len(argv) >= 2 and argv[1][0] == '@':
|
||||
## # read data from the file
|
||||
## command_file = argv[1][1:]
|
||||
## try:
|
||||
## f = open(command_file, 'r')
|
||||
## command_line = f.read()
|
||||
## f.close()
|
||||
## except IOError, err:
|
||||
## sys.stderr.write("Unable to read commands from file: %s\n %s\n" % \
|
||||
## (command_file, err))
|
||||
## sys.exit(101)
|
||||
##
|
||||
## # convert to an argv list, keeping argv[0] and the files to process
|
||||
## argv = argv[:1] + string.split(command_line) + argv[2:]
|
||||
##
|
||||
## global _cfg
|
||||
## _cfg, files, suppressions = Config.setupFromArgs(argv[1:])
|
||||
## if not files :
|
||||
## return 0
|
||||
##
|
||||
## # insert this here, so we find files in the local dir before std library
|
||||
## sys.path.insert(0, '')
|
||||
##
|
||||
## importWarnings = processFiles(files, _cfg, _print_processing)
|
||||
## fixupBuiltinModules()
|
||||
## if _cfg.printParse :
|
||||
## for module in getAllModules() :
|
||||
## printer.module(module)
|
||||
##
|
||||
## warnings = warn.find(getAllModules(), _cfg, suppressions)
|
||||
## if not _cfg.quiet :
|
||||
## print "\nWarnings...\n"
|
||||
## if warnings or importWarnings :
|
||||
## _printWarnings(importWarnings + warnings)
|
||||
## return 1
|
||||
##
|
||||
## if not _cfg.quiet :
|
||||
## print "None"
|
||||
## return 0
|
||||
##
|
||||
##
|
||||
##if __name__ == '__main__' :
|
||||
## try :
|
||||
## sys.exit(main(sys.argv))
|
||||
## except Config.UsageError :
|
||||
## sys.exit(127)
|
||||
##
|
||||
##else :
|
||||
## _orig__import__ = None
|
||||
## _suppressions = None
|
||||
## _warnings_cache = {}
|
||||
##
|
||||
## def _get_unique_warnings(warnings):
|
||||
## for i in range(len(warnings)-1, -1, -1):
|
||||
## w = warnings[i].format()
|
||||
## if _warnings_cache.has_key(w):
|
||||
## del warnings[i]
|
||||
## else:
|
||||
## _warnings_cache[w] = 1
|
||||
## return warnings
|
||||
##
|
||||
## def __import__(name, globals=None, locals=None, fromlist=None):
|
||||
## if globals is None:
|
||||
## globals = {}
|
||||
## if locals is None:
|
||||
## locals = {}
|
||||
## if fromlist is None:
|
||||
## fromlist = []
|
||||
##
|
||||
## check = not sys.modules.has_key(name) and name[:10] != 'pychecker.'
|
||||
## pymodule = _orig__import__(name, globals, locals, fromlist)
|
||||
## if check :
|
||||
## try :
|
||||
## module = Module(pymodule.__name__)
|
||||
## if module.initModule(pymodule):
|
||||
## warnings = warn.find([module], _cfg, _suppressions)
|
||||
## _printWarnings(_get_unique_warnings(warnings))
|
||||
## else :
|
||||
## print 'Unable to load module', pymodule.__name__
|
||||
## except Exception:
|
||||
## name = getattr(pymodule, '__name__', str(pymodule))
|
||||
## importError(name)
|
||||
##
|
||||
## return pymodule
|
||||
##
|
||||
## def _init() :
|
||||
## global _cfg, _suppressions, _orig__import__
|
||||
##
|
||||
## args = string.split(os.environ.get('PYCHECKER', ''))
|
||||
## _cfg, files, _suppressions = Config.setupFromArgs(args)
|
||||
## utils.initConfig(_cfg)
|
||||
## fixupBuiltinModules(1)
|
||||
##
|
||||
## # keep the orig __import__ around so we can call it
|
||||
## import __builtin__
|
||||
## _orig__import__ = __builtin__.__import__
|
||||
## __builtin__.__import__ = __import__
|
||||
##
|
||||
## if not os.environ.get('PYCHECKER_DISABLED') :
|
||||
## _init()
|
||||
##
|
7
wxPython/samples/ide/activegrid/tool/data/tips.txt
Normal file
7
wxPython/samples/ide/activegrid/tool/data/tips.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
Ctrl-Space in any editor does code completion.
|
||||
Right-clicking on something in the 'Thing' column of the debugger's frame tab may allow you to introspect it for more information.
|
||||
Right-Mouse-Click in Outline window allows you to change display sorting.
|
||||
In an editor, you can add line markers via Ctrl-M and jump to the next marker with F4 or previous marker with Shift-F4.
|
||||
In an editor. you can use the numpad + and - keys to toggle folding.
|
||||
In 'Find in Directory', if you specify a file, it will display all matches in the Message Window.
|
||||
Breakpoints for the debugger can be set while the process is running
|
2364
wxPython/samples/ide/activegrid/tool/process.py
Normal file
2364
wxPython/samples/ide/activegrid/tool/process.py
Normal file
File diff suppressed because it is too large
Load Diff
72
wxPython/samples/ide/activegrid/util/__init__.py
Normal file
72
wxPython/samples/ide/activegrid/util/__init__.py
Normal file
@@ -0,0 +1,72 @@
|
||||
import logging
|
||||
import cStringIO
|
||||
import traceback
|
||||
import sys
|
||||
import string
|
||||
import os
|
||||
|
||||
def classForName(className):
|
||||
pathList = className.split('.')
|
||||
moduleName = string.join(pathList[:-1], '.')
|
||||
code = __import__(moduleName)
|
||||
for name in pathList[1:]:
|
||||
code = code.__dict__[name]
|
||||
return code
|
||||
|
||||
def hasattrignorecase(object, name):
|
||||
for attr in dir(object):
|
||||
if attr.lower() == name.lower():
|
||||
return True
|
||||
for attr in dir(object):
|
||||
if attr.lower() == '_' + name.lower():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def setattrignorecase(object, name, value):
|
||||
for attr in object.__dict__:
|
||||
if attr.lower() == name.lower():
|
||||
object.__dict__[attr] = value
|
||||
return
|
||||
## for attr in dir(object):
|
||||
## if attr.lower() == '_' + name.lower():
|
||||
## object.__dict__[attr] = value
|
||||
## return
|
||||
object.__dict__[name] = value
|
||||
|
||||
def getattrignorecase(object, name):
|
||||
for attr in object.__dict__:
|
||||
if attr.lower() == name.lower():
|
||||
return object.__dict__[attr]
|
||||
## for attr in dir(object):
|
||||
## if attr.lower() == '_' + name.lower():
|
||||
## return object.__dict__[attr]
|
||||
return object.__dict__[name]
|
||||
|
||||
|
||||
def defaultLoad(fileObject):
|
||||
xml = fileObject.read()
|
||||
loadedObject = xmlmarshaller.unmarshal(xml)
|
||||
if hasattr(fileObject, 'name'):
|
||||
loadedObject.fileName = os.path.abspath(fileObject.name)
|
||||
loadedObject.initialize()
|
||||
return loadedObject
|
||||
|
||||
def defaultSave(fileObject, objectToSave):
|
||||
xml = xmlmarshaller.marshal(objectToSave, prettyPrint=True)
|
||||
fileObject.write(xml)
|
||||
|
||||
|
||||
def clone(objectToClone):
|
||||
xml = xmlmarshaller.marshal(objectToClone, prettyPrint=True)
|
||||
clonedObject = xmlmarshaller.unmarshal(xml)
|
||||
if hasattr(objectToClone, 'fileName'):
|
||||
clonedObject.fileName = objectToClone.fileName
|
||||
clonedObject.initialize()
|
||||
return clonedObject
|
||||
|
||||
def exceptionToString(e):
|
||||
sio = cStringIO.StringIO()
|
||||
traceback.print_exception(e.__class__, e, sys.exc_traceback, file=sio)
|
||||
return sio.getvalue()
|
||||
|
84
wxPython/samples/ide/activegrid/util/aglogging.py
Normal file
84
wxPython/samples/ide/activegrid/util/aglogging.py
Normal file
@@ -0,0 +1,84 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: aglogging.py
|
||||
# Purpose: Utilities to help with logging
|
||||
#
|
||||
# Author: Jeff Norton
|
||||
#
|
||||
# Created: 01/04/05
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import traceback
|
||||
|
||||
global agTestMode
|
||||
agTestMode = False
|
||||
|
||||
def setTestMode(mode):
|
||||
global agTestMode
|
||||
if (mode):
|
||||
agTestMode = True
|
||||
else:
|
||||
agTestMode = False
|
||||
|
||||
def getTestMode():
|
||||
global agTestMode
|
||||
return agTestMode
|
||||
|
||||
def testMode(normalObj, testObj=None):
|
||||
if getTestMode():
|
||||
return testObj
|
||||
return normalObj
|
||||
|
||||
def toDiffableString(value):
|
||||
s = repr(value)
|
||||
ds = ""
|
||||
i = s.find(" at 0x")
|
||||
start = 0
|
||||
while (i >= 0):
|
||||
j = s.find(">", i)
|
||||
if (j < i):
|
||||
break
|
||||
ds += s[start:i]
|
||||
start = j
|
||||
i = s.find(" at 0x", start)
|
||||
return ds + s[start:]
|
||||
|
||||
def removeFileRefs(str):
|
||||
str = re.sub(r'(?<=File ")[^"]*(\\[^\\]*")(, line )[0-9]*', _fileNameReplacement, str)
|
||||
return str
|
||||
|
||||
def _fileNameReplacement(match):
|
||||
return "...%s" % match.group(1)
|
||||
|
||||
def getTraceback():
|
||||
extype, val, tb = sys.exc_info()
|
||||
tbs = "\n"
|
||||
for s in traceback.format_tb(tb):
|
||||
tbs += s
|
||||
return tbs
|
||||
|
||||
def reportException(out=None, stacktrace=False, diffable=False):
|
||||
extype, val, t = sys.exc_info()
|
||||
if (diffable):
|
||||
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 (stacktrace):
|
||||
fmt = traceback.format_exception(extype, val, t)
|
||||
for s in fmt:
|
||||
if (diffable):
|
||||
s = removeFileRefs(s)
|
||||
if (out == None):
|
||||
print s
|
||||
else:
|
||||
print >> out, s
|
||||
|
87
wxPython/samples/ide/activegrid/util/cachedloader.py
Normal file
87
wxPython/samples/ide/activegrid/util/cachedloader.py
Normal file
@@ -0,0 +1,87 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: cachedloader.py
|
||||
# Purpose:
|
||||
#
|
||||
# Author: Joel Hare
|
||||
#
|
||||
# Created: 8/31/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import copy
|
||||
import os.path
|
||||
import string
|
||||
import cStringIO
|
||||
|
||||
import time
|
||||
|
||||
# TODO: Instantiate the database and create a pool
|
||||
|
||||
|
||||
class CachedLoader(object):
|
||||
def __init__(self):
|
||||
self.cache = {}
|
||||
self.baseLoadDir = None
|
||||
|
||||
def fullPath(self, fileName):
|
||||
if os.path.isabs(fileName):
|
||||
absPath = fileName
|
||||
elif self.baseLoadDir:
|
||||
absPath = os.path.join(self.baseLoadDir, fileName)
|
||||
else:
|
||||
absPath = os.path.abspath(fileName)
|
||||
return absPath
|
||||
|
||||
def setPrototype(self, fileName, loadedFile):
|
||||
absPath = self.fullPath(fileName)
|
||||
mtime = time.time() + 31536000.0 # Make sure prototypes aren't replaced by files on disk
|
||||
self.cache[absPath] = (mtime, loadedFile)
|
||||
|
||||
def update(self, loader):
|
||||
self.cache.update(loader.cache)
|
||||
|
||||
def clear(self):
|
||||
self.cache.clear()
|
||||
|
||||
def delete(self, fileName):
|
||||
absPath = self.fullPath(fileName)
|
||||
del self.cache[absPath]
|
||||
|
||||
def needsLoad(self, fileName):
|
||||
absPath = self.fullPath(fileName)
|
||||
try:
|
||||
cached = self.cache[absPath]
|
||||
cachedTime = cached[0]
|
||||
if cachedTime >= os.path.getmtime(absPath):
|
||||
return False
|
||||
except KeyError:
|
||||
pass
|
||||
return True
|
||||
|
||||
def load(self, fileName, loader):
|
||||
absPath = self.fullPath(fileName)
|
||||
loadedFile = None
|
||||
try:
|
||||
cached = self.cache[absPath]
|
||||
except KeyError:
|
||||
cached = None
|
||||
|
||||
if cached:
|
||||
cachedTime = cached[0]
|
||||
# ToDO We might need smarter logic for checking if a file needs to be reloaded
|
||||
# ToDo We need a way to disable checking if this is a production server
|
||||
if cachedTime >= os.path.getmtime(absPath):
|
||||
loadedFile = cached[1]
|
||||
|
||||
if not loadedFile:
|
||||
targetFile = file(absPath)
|
||||
try:
|
||||
mtime = os.path.getmtime(absPath)
|
||||
loadedFile = loader(targetFile)
|
||||
self.cache[absPath] = (mtime, loadedFile)
|
||||
finally:
|
||||
targetFile.close()
|
||||
return loadedFile
|
||||
|
145
wxPython/samples/ide/activegrid/util/dependencymgr.py
Normal file
145
wxPython/samples/ide/activegrid/util/dependencymgr.py
Normal file
@@ -0,0 +1,145 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: dependencymgr.py
|
||||
# Purpose: Dependency Manager
|
||||
#
|
||||
# Author: Jeff Norton
|
||||
#
|
||||
# Created: 01/28/05
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
DM_NO_ID = 0
|
||||
DM_ID_ATTR = "_DependencyMgr__ID"
|
||||
|
||||
##class ManageableObject(object):
|
||||
##
|
||||
## def __init__(self):
|
||||
## self.__id = DM_NO_ID
|
||||
##
|
||||
## def __repr__(self):
|
||||
## return "<ManageableObject id = %s>" % self.__id
|
||||
##
|
||||
## def __getID(self):
|
||||
## return self.__id
|
||||
##
|
||||
## def __setID(self, value):
|
||||
## if (self.__id != DM_NO_ID):
|
||||
## raise DependencyMgrException("Cannot set the dependency ID on object %s to \"%s\" because it already has one (\"%s\")." % (repr(self), value, self.__id))
|
||||
## self.__id = value
|
||||
##
|
||||
## _DependencyMgr__ID = property(__getID, __setID)
|
||||
|
||||
class DependencyMgr(object):
|
||||
|
||||
def __init__(self):
|
||||
self.clear()
|
||||
|
||||
def clear(self):
|
||||
self.__dependencies = {}
|
||||
self.__lastID = DM_NO_ID
|
||||
|
||||
def addDependency(self, parent, child):
|
||||
pid = self._initObjectID(parent)
|
||||
try:
|
||||
parentCollection = self.__dependencies[pid]
|
||||
except KeyError:
|
||||
parentCollection = self._newDependencyCollection()
|
||||
self.__dependencies[pid] = parentCollection
|
||||
if (child not in parentCollection):
|
||||
parentCollection.append(child)
|
||||
|
||||
def removeDependency(self, parent, child):
|
||||
pid = self._getObjectID(parent)
|
||||
if (pid != DM_NO_ID):
|
||||
try:
|
||||
parentCollection = self.__dependencies[pid]
|
||||
parentCollection.remove(child)
|
||||
if (len(parentCollection) == 0):
|
||||
del self.__dependencies[pid]
|
||||
except KeyError, ValueError:
|
||||
pass
|
||||
|
||||
def clearDependencies(self, parent):
|
||||
"Returns a list of objects or an empty list if no dependencies exist as for getDependencies, and then removes the dependency list."
|
||||
pid = self._getObjectID(parent)
|
||||
try:
|
||||
deps = self.__dependencies[pid]
|
||||
del self.__dependencies[pid]
|
||||
return deps
|
||||
except KeyError:
|
||||
return []
|
||||
|
||||
def hasDependency(self, parent):
|
||||
"Returns a boolean"
|
||||
return (self._getObjectID(parent) in self.__dependencies)
|
||||
|
||||
def getDependencies(self, parent):
|
||||
"Returns a list of objects or an empty list if no dependencies exist."
|
||||
try:
|
||||
return self.__dependencies[self._getObjectID(parent)]
|
||||
except KeyError:
|
||||
return []
|
||||
|
||||
def dumpState(self, out):
|
||||
"Writes the state of the dependency manager (as reported by getState) to out"
|
||||
for line in self.getState():
|
||||
print >> out, line
|
||||
|
||||
def getState(self):
|
||||
"Returns the state of the dependency manager including all managed objects as a list of strings"
|
||||
out = []
|
||||
out.append("DependencyMgr %s has %i parent objects, last id assigned is %i" % (repr(self), len(self.__dependencies), self.__lastID))
|
||||
for key, val in self.__dependencies.iteritems():
|
||||
out.append("Object %s has dependents: %s " % (repr(key), ", ".join([repr(d) for d in val])))
|
||||
return out
|
||||
|
||||
def _initObjectID(self, obj):
|
||||
try:
|
||||
id = getattr(obj, DM_ID_ATTR)
|
||||
except AttributeError:
|
||||
id = DM_NO_ID
|
||||
if (id == DM_NO_ID):
|
||||
id = self._newID()
|
||||
setattr(obj, DM_ID_ATTR, id)
|
||||
return id
|
||||
|
||||
def _getObjectID(self, obj):
|
||||
try:
|
||||
id = getattr(obj, DM_ID_ATTR)
|
||||
except AttributeError:
|
||||
id = DM_NO_ID
|
||||
return id
|
||||
|
||||
def _newID(self):
|
||||
self.__lastID += 1
|
||||
return self.__lastID
|
||||
|
||||
def _newDependencyCollection(self):
|
||||
return []
|
||||
|
||||
globalDM = DependencyMgr()
|
||||
|
||||
def addDependency(parent, child):
|
||||
getGlobalDM().addDependency(parent, child)
|
||||
|
||||
def removeDependency(parent, child):
|
||||
getGlobalDM().removeDependency(parent, child)
|
||||
|
||||
def clearDependencies(parent):
|
||||
return getGlobalDM().clearDependencies(parent)
|
||||
|
||||
def hasDependency(parent):
|
||||
return getGlobalDM().hasDependency(parent)
|
||||
|
||||
def getDependencies(parent):
|
||||
return getGlobalDM().getDependencies(parent)
|
||||
|
||||
def getState():
|
||||
return getGlobalDM().getState()
|
||||
|
||||
def dumpState(out):
|
||||
getGlobalDM().dumpState(out)
|
||||
|
||||
def getGlobalDM():
|
||||
return globalDM
|
39
wxPython/samples/ide/activegrid/util/fileutils.py
Normal file
39
wxPython/samples/ide/activegrid/util/fileutils.py
Normal file
@@ -0,0 +1,39 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# 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 os
|
||||
|
||||
def createFile(filename, mode='w'):
|
||||
f = None
|
||||
try:
|
||||
f = file(filename, mode)
|
||||
except:
|
||||
os.makedirs(filename[:filename.rindex(os.sep)])
|
||||
f = file(filename, mode)
|
||||
return f
|
||||
|
||||
def compareFiles(file1, file2):
|
||||
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):
|
||||
return -1
|
||||
|
36
wxPython/samples/ide/activegrid/util/gettersetter.py
Normal file
36
wxPython/samples/ide/activegrid/util/gettersetter.py
Normal file
@@ -0,0 +1,36 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: gettersetter.py
|
||||
# Purpose:
|
||||
#
|
||||
# Author: Peter Yared
|
||||
#
|
||||
# Created: 7/28/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
def gettersetter(list):
|
||||
for attr in list:
|
||||
lowercase = attr[0].lower() + attr[1:]
|
||||
uppercase = attr[0].upper() + attr[1:]
|
||||
print " def get%s(self):" % uppercase
|
||||
print " return self._%s" % lowercase
|
||||
print
|
||||
print " def set%s(self, %s):" % (uppercase, lowercase)
|
||||
print " self._%s = %s" % (lowercase, lowercase)
|
||||
print
|
||||
|
||||
def listgettersetter(list):
|
||||
for attr in list:
|
||||
lowercase = attr[0].lower() + attr[1:]
|
||||
uppercase = attr[0].upper() + attr[1:]
|
||||
print " def get%s(self):" % uppercase
|
||||
print " return self._%s" % lowercase
|
||||
print
|
||||
print " def add%s(self, %s):" % (uppercase[:-1], lowercase[:-1])
|
||||
print " self._%s.append(%s)" % (lowercase, lowercase[:-1])
|
||||
print
|
||||
print " def remove%s(self, %s):" % (uppercase[:-1], lowercase[:-1])
|
||||
print " self._%s.remove(%s)" % (lowercase, lowercase[:-1])
|
||||
print
|
||||
|
649
wxPython/samples/ide/activegrid/util/xmlmarshaller.py
Normal file
649
wxPython/samples/ide/activegrid/util/xmlmarshaller.py
Normal file
@@ -0,0 +1,649 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: xmlmarshaller.py
|
||||
# Purpose:
|
||||
#
|
||||
# Author: John Spurling
|
||||
#
|
||||
# Created: 7/28/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
from activegrid import util
|
||||
import inspect
|
||||
from types import *
|
||||
import xml.sax
|
||||
import xml.sax.handler
|
||||
import __builtin__
|
||||
from xml.sax import saxutils
|
||||
|
||||
### ToDO remove maxOccurs "unbounded" resolves to -1 hacks after bug 177 is fixed
|
||||
|
||||
"""
|
||||
|
||||
More documentation later, but here are some special Python attributes
|
||||
that McLane recognizes:
|
||||
|
||||
name: __xmlname__
|
||||
type: string
|
||||
description: the name of the xml element for the marshalled object
|
||||
|
||||
name: __xmlattributes__
|
||||
type: tuple or list
|
||||
description: the name(s) of the Python string attribute(s) to be
|
||||
marshalled as xml attributes instead of nested xml elements. currently
|
||||
these can only be strings since there's not a way to get the type
|
||||
information back when unmarshalling.
|
||||
|
||||
name: __xmlexclude__
|
||||
type: tuple or list
|
||||
description: the name(s) of the python attribute(s) to skip when
|
||||
marshalling.
|
||||
|
||||
name: __xmlrename__
|
||||
type: dict
|
||||
description: describes an alternate Python <-> XML name mapping.
|
||||
Normally the name mapping is the identity function. __xmlrename__
|
||||
overrides that. The keys are the Python names, the values are their
|
||||
associated XML names.
|
||||
|
||||
name: __xmlflattensequence__
|
||||
type: dict, tuple, or list
|
||||
description: the name(s) of the Python sequence attribute(s) whose
|
||||
items are to be marshalled as a series of xml elements (with an
|
||||
optional keyword argument that specifies the element name to use) as
|
||||
opposed to containing them in a separate sequence element, e.g.:
|
||||
|
||||
myseq = (1, 2)
|
||||
<!-- normal way of marshalling -->
|
||||
<myseq>
|
||||
<item objtype='int'>1</item>
|
||||
<item objtype='int'>2</item>
|
||||
</myseq>
|
||||
<!-- with __xmlflattensequence__ set to {'myseq': 'squish'} -->
|
||||
<squish objtype='int'>1</squish>
|
||||
<squish objtype='int'>2</squish>
|
||||
|
||||
name: __xmlnamespaces__
|
||||
type: dict
|
||||
description: a dict of the namespaces that the object uses. Each item
|
||||
in the dict should consist of a prefix,url combination where the key is
|
||||
the prefix and url is the value, e.g.:
|
||||
|
||||
__xmlnamespaces__ = { "xsd":"http://www.w3c.org/foo.xsd" }
|
||||
|
||||
name: __xmldefaultnamespace__
|
||||
type: String
|
||||
description: the prefix of a namespace defined in __xmlnamespaces__ that
|
||||
should be used as the default namespace for the object.
|
||||
|
||||
name: __xmlattrnamespaces__
|
||||
type: dict
|
||||
description: a dict assigning the Python object's attributes to the namespaces
|
||||
defined in __xmlnamespaces__. Each item in the dict should consist of a
|
||||
prefix,attributeList combination where the key is the prefix and the value is
|
||||
a list of the Python attribute names. e.g.:
|
||||
|
||||
__xmlattrnamespaces__ = { "ag":["firstName", "lastName", "addressLine1", "city"] }
|
||||
|
||||
|
||||
"""
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# module exceptions
|
||||
#
|
||||
################################################################################
|
||||
|
||||
class Error(Exception):
|
||||
"""Base class for errors in this module."""
|
||||
pass
|
||||
|
||||
class UnhandledTypeException(Error):
|
||||
"""Exception raised when attempting to marshal an unsupported
|
||||
type.
|
||||
"""
|
||||
def __init__(self, typename):
|
||||
self.typename = typename
|
||||
def __str__(self):
|
||||
return "%s is not supported for marshalling." % str(self.typename)
|
||||
|
||||
class XMLAttributeIsNotStringType(Error):
|
||||
"""Exception raised when an object's attribute is specified to be
|
||||
marshalled as an XML attribute of the enclosing object instead of
|
||||
a nested element.
|
||||
"""
|
||||
def __init__(self, attrname, typename):
|
||||
self.attrname = attrname
|
||||
self.typename = typename
|
||||
def __str__(self):
|
||||
return """%s was set to be marshalled as an XML attribute
|
||||
instead of a nested element, but the object's type is %s, not
|
||||
string.""" % (self.attrname, self.typename)
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# constants and such
|
||||
#
|
||||
################################################################################
|
||||
|
||||
XMLNS = 'xmlns'
|
||||
XMLNS_PREFIX = XMLNS + ':'
|
||||
XMLNS_PREFIX_LENGTH = len(XMLNS_PREFIX)
|
||||
|
||||
BASETYPE_ELEMENT_NAME = 'item'
|
||||
MEMBERS_TO_SKIP = ('__module__', '__doc__', '__xmlname__', '__xmlattributes__',
|
||||
'__xmlexclude__', '__xmlflattensequence__', '__xmlnamespaces__',
|
||||
'__xmldefaultnamespace__', '__xmlattrnamespaces__')
|
||||
|
||||
WELL_KNOWN_OBJECTS = { "xs:element" : "activegrid.model.schema.XsdElement",
|
||||
"xs:complexType" : "activegrid.model.schema.XsdComplexType",
|
||||
"xs:complexType" : "activegrid.model.schema.XsdComplexType",
|
||||
"xs:element" : "activegrid.model.schema.XsdElement",
|
||||
"xs:key" : "activegrid.model.schema.XsdKey",
|
||||
"xs:field" : "activegrid.model.schema.XsdKeyField",
|
||||
"xs:keyref" : "activegrid.model.schema.XsdKeyRef",
|
||||
"xs:selector" : "activegrid.model.schema.XsdKeySelector",
|
||||
"xs:schema" : "activegrid.model.schema.Schema",
|
||||
"ag:schemaOptions":"activegrid.model.schema.SchemaOptions",
|
||||
"ag:debug" : "activegrid.model.processmodel.DebugOperation",
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
#
|
||||
# classes and functions
|
||||
#
|
||||
################################################################################
|
||||
|
||||
def _objectfactory(objname, objargs=None, xsname=None):
|
||||
try:
|
||||
'''dynamically create an object based on the objname and return
|
||||
it. look it up in the BASETYPE_ELEMENT_MAP first.
|
||||
'''
|
||||
## print "_objectfactory creating an object of type %s and value %s, xsname=%s" % (objname, objargs, xsname)
|
||||
# split the objname into the typename and module path,
|
||||
# importing the module if need be.
|
||||
if not isinstance(objargs, list):
|
||||
objargs = [objargs]
|
||||
|
||||
if (xsname):
|
||||
try:
|
||||
objname = WELL_KNOWN_OBJECTS[xsname]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
objtype = objname.split('.')[-1]
|
||||
pathlist = objname.split('.')
|
||||
modulename = '.'.join(pathlist[0:-1])
|
||||
|
||||
## print "[objectfactory] objtype is %s" % objtype
|
||||
## print "[objectfactory] objargs is %s" % `objargs`
|
||||
|
||||
## since the bool constructor will turn a string of non-zero
|
||||
## length into True, we call it with no argument (yielding a
|
||||
## False) if the string contains 'false'
|
||||
if objtype == 'bool' and objargs[0].lower() == 'false':
|
||||
objargs = None
|
||||
|
||||
## if objtype == 'str':
|
||||
## print type(objargs)
|
||||
## print "string we're unescaping: '%s'" % objargs[0]
|
||||
## objargs = saxutils.unescape(objargs[0])
|
||||
if objtype in ('float', 'int', 'str', 'long'):
|
||||
objargs = [x.strip() for x in objargs]
|
||||
|
||||
if objtype == 'str':
|
||||
objargs = [saxutils.unescape(x) for x in objargs]
|
||||
|
||||
if __builtin__.__dict__.has_key(objname):
|
||||
module = __builtin__
|
||||
else:
|
||||
if modulename:
|
||||
module = __import__(modulename)
|
||||
for name in pathlist[1:-1]:
|
||||
module = module.__dict__[name]
|
||||
if objargs:
|
||||
return module.__dict__[objtype](*objargs)
|
||||
else:
|
||||
if objtype == 'None':
|
||||
return None
|
||||
return module.__dict__[objtype]()
|
||||
except KeyError:
|
||||
raise KeyError("Could not find class %s" % objname)
|
||||
|
||||
class Element:
|
||||
def __init__(self, name, attrs=None):
|
||||
self.name = name
|
||||
self.attrs = attrs
|
||||
self.content = ''
|
||||
self.children = []
|
||||
def getobjtype(self):
|
||||
if self.attrs.has_key('objtype'):
|
||||
return self.attrs.getValue('objtype')
|
||||
else:
|
||||
return 'str'
|
||||
|
||||
|
||||
class XMLObjectFactory(xml.sax.ContentHandler):
|
||||
def __init__(self):
|
||||
self.rootelement = None
|
||||
self.elementstack = []
|
||||
xml.sax.handler.ContentHandler.__init__(self)
|
||||
|
||||
## ContentHandler methods
|
||||
def startElement(self, name, attrs):
|
||||
if name.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
|
||||
name = name[name.index(':') + 1:]
|
||||
## for attrname in attrs.getNames():
|
||||
## print "%s: %s" % (attrname, attrs.getValue(attrname))
|
||||
element = Element(name, attrs.copy())
|
||||
self.elementstack.append(element)
|
||||
## print self.elementstack
|
||||
|
||||
def characters(self, content):
|
||||
## print "got content: %s" % content
|
||||
if content:
|
||||
self.elementstack[-1].content += content
|
||||
|
||||
def endElement(self, name):
|
||||
## print "[endElement] name of element we're at the end of: %s" % name
|
||||
xsname = name
|
||||
if name.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
|
||||
name = name[name.index(':') + 1:]
|
||||
element = self.elementstack.pop()
|
||||
objtype = element.getobjtype()
|
||||
constructorarglist = []
|
||||
if element.content:
|
||||
strippedElementContent = element.content.strip()
|
||||
if strippedElementContent:
|
||||
constructorarglist.append(element.content)
|
||||
obj = _objectfactory(objtype, constructorarglist, xsname)
|
||||
complexType = None
|
||||
if hasattr(obj, '__xsdcomplextype__'):
|
||||
complexType = getattr(obj, '__xsdcomplextype__')
|
||||
if len(self.elementstack) > 0:
|
||||
self.elementstack[-1].children.append((name, obj))
|
||||
else:
|
||||
self.rootelement = obj
|
||||
if element.attrs and not isinstance(obj, list):
|
||||
for attrname, attr in element.attrs.items():
|
||||
if attrname == XMLNS or attrname.startswith(XMLNS_PREFIX):
|
||||
if attrname.startswith(XMLNS_PREFIX):
|
||||
ns = attrname[XMLNS_PREFIX_LENGTH:]
|
||||
else:
|
||||
ns = ""
|
||||
if not hasattr(obj, '__xmlnamespaces__'):
|
||||
obj.__xmlnamespaces__ = {ns:attr}
|
||||
elif ns not in obj.__xmlnamespaces__:
|
||||
if (hasattr(obj.__class__, '__xmlnamespaces__')
|
||||
and obj.__xmlnamespaces__ is obj.__class__.__xmlnamespaces__):
|
||||
obj.__xmlnamespaces__ = dict(obj.__xmlnamespaces__)
|
||||
obj.__xmlnamespaces__[ns] = attr
|
||||
elif not attrname == 'objtype':
|
||||
if attrname.find(':') > -1: # Strip namespace prefixes for now until actually looking them up in xsd
|
||||
attrname = attrname[attrname.index(':') + 1:]
|
||||
if complexType:
|
||||
xsdElement = complexType.findElement(attrname)
|
||||
if xsdElement:
|
||||
type = xsdElement.type
|
||||
if type:
|
||||
type = xsdToPythonType(type)
|
||||
### ToDO remove maxOccurs hack after bug 177 is fixed
|
||||
if attrname == "maxOccurs" and attr == "unbounded":
|
||||
attr = "-1"
|
||||
attr = _objectfactory(type, attr)
|
||||
util.setattrignorecase(obj, _toAttrName(obj, attrname), attr)
|
||||
## obj.__dict__[_toAttrName(obj, attrname)] = attr
|
||||
# stuff any child attributes meant to be in a sequence via the __xmlflattensequence__
|
||||
flattenDict = {}
|
||||
if hasattr(obj, '__xmlflattensequence__'):
|
||||
for sequencename, xmlnametuple in obj.__xmlflattensequence__.items():
|
||||
for xmlname in xmlnametuple:
|
||||
flattenDict[xmlname] = sequencename
|
||||
|
||||
# reattach an object's attributes to it
|
||||
for childname, child in element.children:
|
||||
if flattenDict.has_key(childname):
|
||||
sequencename = _toAttrName(obj, flattenDict[childname])
|
||||
try:
|
||||
sequencevalue = obj.__dict__[sequencename]
|
||||
except AttributeError:
|
||||
sequencevalue = None
|
||||
if sequencevalue == None:
|
||||
sequencevalue = []
|
||||
obj.__dict__[sequencename] = sequencevalue
|
||||
sequencevalue.append(child)
|
||||
elif isinstance(obj, list):
|
||||
obj.append(child)
|
||||
else:
|
||||
## print "childname = %s, obj = %s, child = %s" % (childname, repr(obj), repr(child))
|
||||
util.setattrignorecase(obj, _toAttrName(obj, childname), child)
|
||||
## obj.__dict__[_toAttrName(obj, childname)] = child
|
||||
|
||||
if complexType:
|
||||
for element in complexType.elements:
|
||||
if element.default:
|
||||
elementName = _toAttrName(obj, element.name)
|
||||
if ((elementName not in obj.__dict__) or (obj.__dict__[elementName] == None)):
|
||||
pythonType = xsdToPythonType(element.type)
|
||||
defaultValue = _objectfactory(pythonType, element.default)
|
||||
obj.__dict__[elementName] = defaultValue
|
||||
|
||||
def getRootObject(self):
|
||||
return self.rootelement
|
||||
|
||||
def _toAttrName(obj, name):
|
||||
if (hasattr(obj, "__xmlrename__")):
|
||||
for key, val in obj.__xmlrename__.iteritems():
|
||||
if (name == val):
|
||||
name = key
|
||||
break
|
||||
## if (name.startswith("__") and not name.endswith("__")):
|
||||
## name = "_%s%s" % (obj.__class__.__name__, name)
|
||||
return name
|
||||
|
||||
__typeMappingXsdToPython = {
|
||||
"string": "str",
|
||||
"char": "str",
|
||||
"varchar": "str",
|
||||
"date": "str", # ToDO Need to work out how to create python date types
|
||||
"boolean": "bool",
|
||||
"decimal": "float", # ToDO Does python have a better fixed point type?
|
||||
"int": "int",
|
||||
"long": "long",
|
||||
"float": "float",
|
||||
"bool": "bool",
|
||||
"str": "str",
|
||||
"unicode": "unicode",
|
||||
}
|
||||
|
||||
def xsdToPythonType(xsdType):
|
||||
try:
|
||||
return __typeMappingXsdToPython[xsdType]
|
||||
except KeyError:
|
||||
raise Exception("Unknown xsd type %s" % xsdType)
|
||||
|
||||
def _getXmlValue(pythonValue):
|
||||
if (isinstance(pythonValue, bool)):
|
||||
return str(pythonValue).lower()
|
||||
else:
|
||||
return str(pythonValue)
|
||||
|
||||
def unmarshal(xmlstr):
|
||||
objectfactory = XMLObjectFactory()
|
||||
xml.sax.parseString(xmlstr, objectfactory)
|
||||
return objectfactory.getRootObject()
|
||||
|
||||
|
||||
def marshal(obj, elementName=None, nameSpacePrefix='', nameSpaces=None, prettyPrint=False, indent=0):
|
||||
if prettyPrint or indent:
|
||||
prefix = ' '*indent
|
||||
newline = '\n'
|
||||
increment = 4
|
||||
else:
|
||||
prefix = ''
|
||||
newline = ''
|
||||
increment = 0
|
||||
|
||||
## Determine the XML element name. If it isn't specified in the
|
||||
## parameter list, look for it in the __xmlname__ Python
|
||||
## attribute, else use the default generic BASETYPE_ELEMENT_NAME.
|
||||
if not nameSpaces: nameSpaces = {} # Need to do this since if the {} is a default parameter it gets shared by all calls into the function
|
||||
nameSpaceAttrs = ''
|
||||
if hasattr(obj, '__xmlnamespaces__'):
|
||||
for nameSpaceKey, nameSpaceUrl in getattr(obj, '__xmlnamespaces__').items():
|
||||
if nameSpaceUrl in nameSpaces:
|
||||
nameSpaceKey = nameSpaces[nameSpaceUrl]
|
||||
else:
|
||||
## # TODO: Wait to do this until there is shared state for use when going through the object graph
|
||||
## origNameSpaceKey = nameSpaceKey # Make sure there is no key collision, ie: same key referencing two different URL's
|
||||
## i = 1
|
||||
## while nameSpaceKey in nameSpaces.values():
|
||||
## nameSpaceKey = origNameSpaceKey + str(i)
|
||||
## i += 1
|
||||
nameSpaces[nameSpaceUrl] = nameSpaceKey
|
||||
if nameSpaceKey == '':
|
||||
nameSpaceAttrs += ' xmlns="%s" ' % (nameSpaceUrl)
|
||||
else:
|
||||
nameSpaceAttrs += ' xmlns:%s="%s" ' % (nameSpaceKey, nameSpaceUrl)
|
||||
nameSpaceAttrs = nameSpaceAttrs.rstrip()
|
||||
if hasattr(obj, '__xmldefaultnamespace__'):
|
||||
nameSpacePrefix = getattr(obj, '__xmldefaultnamespace__') + ':'
|
||||
if not elementName:
|
||||
if hasattr(obj, '__xmlname__'):
|
||||
elementName = nameSpacePrefix + obj.__xmlname__
|
||||
else:
|
||||
elementName = nameSpacePrefix + BASETYPE_ELEMENT_NAME
|
||||
else:
|
||||
elementName = nameSpacePrefix + elementName
|
||||
|
||||
members_to_skip = []
|
||||
## Add more members_to_skip based on ones the user has selected
|
||||
## via the __xmlexclude__ attribute.
|
||||
if hasattr(obj, '__xmlexclude__'):
|
||||
members_to_skip += list(obj.__xmlexclude__)
|
||||
# Marshal the attributes that are selected to be XML attributes.
|
||||
objattrs = ''
|
||||
className = obj.__class__.__name__
|
||||
classNamePrefix = "_" + className
|
||||
if hasattr(obj, '__xmlattributes__'):
|
||||
xmlattributes = obj.__xmlattributes__
|
||||
members_to_skip += xmlattributes
|
||||
for attr in xmlattributes:
|
||||
internalAttrName = attr
|
||||
if (attr.startswith("__") and not attr.endswith("__")):
|
||||
internalAttrName = classNamePrefix + attr
|
||||
# Fail silently if a python attribute is specified to be
|
||||
# an XML attribute but is missing.
|
||||
try:
|
||||
value = obj.__dict__[internalAttrName]
|
||||
except KeyError:
|
||||
continue
|
||||
## # But, check and see if it is a property first:
|
||||
## if (hasPropertyValue(obj, attr)):
|
||||
## value = getattr(obj, attr)
|
||||
## else:
|
||||
## continue
|
||||
xsdElement = None
|
||||
if hasattr(obj, '__xsdcomplextype__'):
|
||||
complexType = getattr(obj, '__xsdcomplextype__')
|
||||
xsdElement = complexType.findElement(attr)
|
||||
if xsdElement:
|
||||
default = xsdElement.default
|
||||
if default == value or default == _getXmlValue(value):
|
||||
continue
|
||||
elif value == None:
|
||||
continue
|
||||
|
||||
# ToDO remove maxOccurs hack after bug 177 is fixed
|
||||
if attr == "maxOccurs" and value == -1:
|
||||
value = "unbounded"
|
||||
|
||||
if isinstance(value, bool):
|
||||
if value == True:
|
||||
value = "true"
|
||||
else:
|
||||
value = "false"
|
||||
|
||||
attrNameSpacePrefix = ''
|
||||
if hasattr(obj, '__xmlattrnamespaces__'):
|
||||
for nameSpaceKey, nameSpaceAttributes in getattr(obj, '__xmlattrnamespaces__').items():
|
||||
if nameSpaceKey == nameSpacePrefix[:-1]: # Don't need to specify attribute namespace if it is the same as it selement
|
||||
continue
|
||||
if attr in nameSpaceAttributes:
|
||||
attrNameSpacePrefix = nameSpaceKey + ':'
|
||||
break
|
||||
## if attr.startswith('_'):
|
||||
## attr = attr[1:]
|
||||
if (hasattr(obj, "__xmlrename__") and attr in obj.__xmlrename__):
|
||||
attr = obj.__xmlrename__[attr]
|
||||
|
||||
objattrs += ' %s%s="%s"' % (attrNameSpacePrefix, attr, value)
|
||||
|
||||
objtype = type(obj)
|
||||
if isinstance(obj, NoneType):
|
||||
return ''
|
||||
# return '%s<%s objtype="None"/>%s' % (prefix, elementName, newline)
|
||||
elif isinstance(obj, bool):
|
||||
return '%s<%s objtype="bool">%s</%s>%s' % (prefix, elementName, obj, elementName, newline)
|
||||
elif isinstance(obj, int):
|
||||
return '''%s<%s objtype="int">%s</%s>%s''' % (prefix, elementName, str(obj), elementName, newline)
|
||||
elif isinstance(obj, long):
|
||||
return '%s<%s objtype="long">%s</%s>%s' % (prefix, elementName, str(obj), elementName, newline)
|
||||
elif isinstance(obj, float):
|
||||
return '%s<%s objtype="float">%s</%s>%s' % (prefix, elementName, str(obj), elementName, newline)
|
||||
elif isinstance(obj, basestring):
|
||||
return '''%s<%s>%s</%s>%s''' % (prefix, elementName, saxutils.escape(obj), elementName, newline)
|
||||
## elif isinstance(obj, unicode):
|
||||
## return '''%s<%s>%s</%s>%s''' % (prefix, elementName, obj, elementName, newline)
|
||||
elif isinstance(obj, list):
|
||||
if len(obj) < 1:
|
||||
return ''
|
||||
xmlString = '%s<%s objtype="list">%s' % (prefix, elementName, newline)
|
||||
for item in obj:
|
||||
xmlString += marshal(item, nameSpaces=nameSpaces, indent=indent+increment)
|
||||
xmlString += '%s</%s>%s' % (prefix, elementName, newline)
|
||||
return xmlString
|
||||
elif isinstance(obj, tuple):
|
||||
if len(obj) < 1:
|
||||
return ''
|
||||
xmlString = '%s<%s objtype="list" mutable="false">%s' % (prefix, elementName, newline)
|
||||
for item in obj:
|
||||
xmlString += marshal(item, nameSpaces=nameSpaces, indent=indent+increment)
|
||||
xmlString += '%s</%s>%s' % (prefix, elementName, newline)
|
||||
return xmlString
|
||||
elif isinstance(obj, dict):
|
||||
xmlString = '%s<%s objtype="dict">%s' % (prefix, elementName, newline)
|
||||
subprefix = prefix + ' '*increment
|
||||
subindent = indent + 2*increment
|
||||
for key, val in obj.iteritems():
|
||||
xmlString += "%s<key>%s%s%s</key>%s%s<value>%s%s%s</value>%s" \
|
||||
% (subprefix, newline, marshal(key, indent=subindent), subprefix, newline, subprefix, newline, marshal(val, nameSpaces=nameSpaces, indent=subindent), subprefix, newline)
|
||||
xmlString += '%s</%s>%s' % (prefix, elementName, newline)
|
||||
return xmlString
|
||||
else:
|
||||
moduleName = obj.__class__.__module__
|
||||
if (moduleName == "activegrid.model.schema"):
|
||||
xmlString = '%s<%s%s%s' % (prefix, elementName, nameSpaceAttrs, objattrs)
|
||||
else:
|
||||
xmlString = '%s<%s%s%s objtype="%s.%s"' % (prefix, elementName, nameSpaceAttrs, objattrs, moduleName, className)
|
||||
# get the member, value pairs for the object, filtering out
|
||||
# the types we don't support.
|
||||
xmlMemberString = ''
|
||||
if hasattr(obj, '__xmlbody__'):
|
||||
xmlMemberString = getattr(obj, obj.__xmlbody__)
|
||||
else:
|
||||
entryList = obj.__dict__.items()
|
||||
## # Add in properties
|
||||
## for key in obj.__class__.__dict__.iterkeys():
|
||||
## if (key not in members_to_skip and key not in obj.__dict__
|
||||
## and hasPropertyValue(obj, key)):
|
||||
## value = getattr(obj, key)
|
||||
## entryList.append((key, value))
|
||||
entryList.sort()
|
||||
for name, value in entryList:
|
||||
## # special name handling for private "__*" attributes:
|
||||
## # remove the _<class-name> added by Python
|
||||
## if name.startswith(classNamePrefix): name = name[len(classNamePrefix):]
|
||||
if name in members_to_skip: continue
|
||||
if name.startswith('__') and name.endswith('__'): continue
|
||||
## idx = name.find('__')
|
||||
## if idx > 0:
|
||||
## newName = name[idx+2:]
|
||||
## if newName:
|
||||
## name = newName
|
||||
subElementNameSpacePrefix = nameSpacePrefix
|
||||
if hasattr(obj, '__xmlattrnamespaces__'):
|
||||
for nameSpaceKey, nameSpaceValues in getattr(obj, '__xmlattrnamespaces__').items():
|
||||
if name in nameSpaceValues:
|
||||
subElementNameSpacePrefix = nameSpaceKey + ':'
|
||||
break
|
||||
# handle sequences listed in __xmlflattensequence__
|
||||
# specially: instead of listing the contained items inside
|
||||
# of a separate list, as god intended, list them inside
|
||||
# the object containing the sequence.
|
||||
if hasattr(obj, '__xmlflattensequence__') and name in obj.__xmlflattensequence__ and value:
|
||||
try:
|
||||
xmlnametuple = obj.__xmlflattensequence__[name]
|
||||
xmlname = None
|
||||
if len(xmlnametuple) == 1:
|
||||
xmlname = xmlnametuple[0]
|
||||
except:
|
||||
xmlname = name
|
||||
## xmlname = name.lower()
|
||||
for seqitem in value:
|
||||
xmlMemberString += marshal(seqitem, xmlname, subElementNameSpacePrefix, nameSpaces=nameSpaces, indent=indent+increment)
|
||||
else:
|
||||
if (hasattr(obj, "__xmlrename__") and name in obj.__xmlrename__):
|
||||
xmlname = obj.__xmlrename__[name]
|
||||
else:
|
||||
xmlname = name
|
||||
## xmlname = name.lower()
|
||||
## # skip
|
||||
## if xmlname.startswith('_') and not xmlname.startswith('__'):
|
||||
## xmlname = xmlname[1:]
|
||||
## if (indent > 30):
|
||||
## print "getting pretty deep, xmlname = ", xmlname
|
||||
xmlMemberString += marshal(value, xmlname, subElementNameSpacePrefix, nameSpaces=nameSpaces, indent=indent+increment)
|
||||
# if we have nested elements, add them here, otherwise close the element tag immediately.
|
||||
if xmlMemberString:
|
||||
xmlString += '>'
|
||||
if hasattr(obj, '__xmlbody__'):
|
||||
xmlString += xmlMemberString
|
||||
xmlString += '</%s>%s' % (elementName, newline)
|
||||
else:
|
||||
xmlString += newline
|
||||
xmlString += xmlMemberString
|
||||
xmlString += '%s</%s>%s' % (prefix, elementName, newline)
|
||||
else:
|
||||
xmlString = xmlString + '/>%s' % newline
|
||||
return xmlString
|
||||
|
||||
# We don't use this anymore but in case we want to get properties this is how
|
||||
# you do it
|
||||
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
|
||||
|
||||
if __name__ == '__main__':
|
||||
from xmlmarshallertests import Person, marshalledint, marshalledlist
|
||||
|
||||
l = [1, 2, 3]
|
||||
d = {'1': 1, '2': 2}
|
||||
outerlist = [l]
|
||||
xmlstr = marshal(d, "d", prettyPrint=True)
|
||||
print xmlstr
|
||||
|
||||
person = Person()
|
||||
person.firstName = "Albert"
|
||||
person.lastName = "Camus"
|
||||
person.addressLine1 = "23 Absurd St."
|
||||
person.city = "Ennui"
|
||||
person.state = "MO"
|
||||
person.zip = "54321"
|
||||
person._phoneNumber = "808-303-2323"
|
||||
person.favoriteWords = ['angst', 'ennui', 'existence']
|
||||
person.weight = 150
|
||||
|
||||
xmlstring = marshal(person, 'person', prettyPrint=True)
|
||||
print xmlstring
|
||||
|
||||
obj = unmarshal(marshalledlist)
|
||||
print "obj has type %s and value %s" % (type(obj), str(obj))
|
||||
for item in obj:
|
||||
print "item: %s" % str(item)
|
183
wxPython/samples/ide/activegrid/util/xmlmarshallertests.py
Normal file
183
wxPython/samples/ide/activegrid/util/xmlmarshallertests.py
Normal file
@@ -0,0 +1,183 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: xmlmarshallertests.py
|
||||
# Purpose:
|
||||
#
|
||||
# Author: John Spurling
|
||||
#
|
||||
# Created: 8/16/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
import unittest
|
||||
import xmlmarshaller
|
||||
from xmlprettyprinter import xmlprettyprint
|
||||
|
||||
marshalledPersonObject = """
|
||||
<person objtype="Person">
|
||||
<firstName>Albert</firstName>
|
||||
<lastName>Camus</lastName>
|
||||
<address>23 Absurd St.</address>
|
||||
<city>Ennui</city>
|
||||
<state>MO</state>
|
||||
<zip>54321</zip>
|
||||
<_phoneNumber>808-303-2323</_phoneNumber>
|
||||
<favoriteWords objtype="list">
|
||||
<item>angst</item>
|
||||
<item>ennui</item>
|
||||
<item>existence</item>
|
||||
</favoriteWords>
|
||||
<weight objtype="float">150</weight>
|
||||
</person>
|
||||
"""
|
||||
|
||||
marshalledint = '''
|
||||
<item objtype="int">23</item>
|
||||
'''
|
||||
|
||||
marshalledlist = '''
|
||||
<mylist objtype="list">
|
||||
<item>foo</item>
|
||||
<item>bar</item>
|
||||
</mylist>
|
||||
'''
|
||||
|
||||
## a dummy class taken from the old XmlMarshaller module.
|
||||
## class Person:
|
||||
## def __init__(self):
|
||||
## # These are not necessary but are nice if you want to tailor
|
||||
## # the Python object <-> XML binding
|
||||
|
||||
## # The xml element name to use for this object, otherwise it
|
||||
## # will use a fully qualified Python name like __main__.Person
|
||||
## # which can be ugly.
|
||||
## self.__xmlname__ = "person"
|
||||
## self.firstName = None
|
||||
## self.lastName = None
|
||||
## self.addressLine1 = None
|
||||
## self.addressLine2 = None
|
||||
## self.city = None
|
||||
## self.state = None
|
||||
## self.zip = None
|
||||
## self._phoneNumber = None
|
||||
## self.favoriteWords = None
|
||||
## self.weight = None
|
||||
class Person:
|
||||
__xmlflattensequence__ = {'asequence': ('the_earth_is_flat',)}
|
||||
|
||||
class XmlMarshallerTestFunctions(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
'''common setup code goes here.'''
|
||||
pass
|
||||
|
||||
def testInt(self):
|
||||
xml = xmlmarshaller.marshal(1)
|
||||
print "\n#########################################"
|
||||
print "# testString test case #"
|
||||
print "#########################################"
|
||||
print "marshalled int object:\n"
|
||||
print xmlprettyprint(xml)
|
||||
|
||||
def testDict(self):
|
||||
xml = xmlmarshaller.marshal({'one': 1,
|
||||
'two': 2,
|
||||
'three': 3})
|
||||
print "\n#########################################"
|
||||
print "# testString test case #"
|
||||
print "#########################################"
|
||||
print "marshalled dict object:\n"
|
||||
print xmlprettyprint(xml)
|
||||
|
||||
def testBool(self):
|
||||
xmltrue = xmlmarshaller.marshal(True)
|
||||
xmlfalse = xmlmarshaller.marshal(False)
|
||||
print "\n#########################################"
|
||||
print "# testBool test case #"
|
||||
print "#########################################"
|
||||
print "marshalled boolean true object:\n"
|
||||
print xmlprettyprint(xmltrue)
|
||||
print "\nmarshalled boolean false object:\n"
|
||||
print xmlprettyprint(xmlfalse)
|
||||
pytrue = xmlmarshaller.unmarshal(xmltrue)
|
||||
assert pytrue is True
|
||||
pyfalse = xmlmarshaller.unmarshal(xmlfalse)
|
||||
assert pyfalse is False
|
||||
|
||||
def testString(self):
|
||||
xml = xmlmarshaller.marshal(
|
||||
"all your marshalled objects are belong to us")
|
||||
print "\n#########################################"
|
||||
print "# testString test case #"
|
||||
print "#########################################"
|
||||
print xmlprettyprint(xml)
|
||||
|
||||
def testEmptyElement(self):
|
||||
person = Person()
|
||||
person.firstName = "Albert"
|
||||
person.__xmlattributes__ = ('firstName',)
|
||||
xml = xmlmarshaller.marshal(person, 'person')
|
||||
print "\n#########################################"
|
||||
print "# testEmptyElement test case #"
|
||||
print "#########################################"
|
||||
print xml
|
||||
assert (xml == """<person objtype="__main__.Person" firstName="Albert"/>""")
|
||||
|
||||
def testXMLFlattenSequence(self):
|
||||
person = Person()
|
||||
person.asequence = ('one', 'two')
|
||||
xml = xmlmarshaller.marshal(person, 'person')
|
||||
print "\n#########################################"
|
||||
print "# testXMLFlattenSequence test case #"
|
||||
print "#########################################"
|
||||
print xml
|
||||
assert (xml == """<person objtype="__main__.Person"><the_earth_is_flat>one</the_earth_is_flat><the_earth_is_flat>two</the_earth_is_flat></person>""")
|
||||
unmarshalledperson = xmlmarshaller.unmarshal(xml)
|
||||
assert(hasattr(unmarshalledperson, 'asequence'))
|
||||
assert(len(unmarshalledperson.asequence) == 2)
|
||||
|
||||
def testInstance(self):
|
||||
print "\n#########################################"
|
||||
print "# testInstance test case #"
|
||||
print "#########################################"
|
||||
class Foo:
|
||||
def __init__(self):
|
||||
self.alist = [1,2]
|
||||
self.astring = 'f00'
|
||||
f = Foo()
|
||||
xml = xmlmarshaller.marshal(f, 'foo')
|
||||
print xml
|
||||
|
||||
def testPerson(self):
|
||||
person = Person()
|
||||
person.firstName = "Albert"
|
||||
person.lastName = "Camus"
|
||||
person.addressLine1 = "23 Absurd St."
|
||||
person.city = "Ennui"
|
||||
person.state = "MO"
|
||||
person.zip = "54321"
|
||||
person._phoneNumber = "808-303-2323"
|
||||
person.favoriteWords = ['angst', 'ennui', 'existence']
|
||||
person.weight = 150
|
||||
# __xmlattributes__ = ('fabulousness',)
|
||||
person.fabulousness = "tres tres"
|
||||
xml = xmlmarshaller.marshal(person)
|
||||
print "\n#########################################"
|
||||
print "# testPerson test case #"
|
||||
print "#########################################"
|
||||
print "Person object marshalled into XML:\n"
|
||||
print xml
|
||||
# When encountering a "person" element, use the Person class
|
||||
## elementMappings = { "person" : Person }
|
||||
## obj = unmarshal(xml, elementMappings = elementMappings)
|
||||
## print "Person object recreated from XML with attribute types indicated:"
|
||||
## print obj.person.__class__
|
||||
## for (attr, value) in obj.person.__dict__.items():
|
||||
## if not attr.startswith("__"):
|
||||
## print attr, "=", value, type(value)
|
||||
## print
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
63
wxPython/samples/ide/activegrid/util/xmlprettyprinter.py
Normal file
63
wxPython/samples/ide/activegrid/util/xmlprettyprinter.py
Normal file
@@ -0,0 +1,63 @@
|
||||
#----------------------------------------------------------------------------
|
||||
# Name: xmlprettyprinter.py
|
||||
# Purpose:
|
||||
#
|
||||
# Author: John Spurling
|
||||
#
|
||||
# Created: 9/21/04
|
||||
# CVS-ID: $Id$
|
||||
# Copyright: (c) 2004-2005 ActiveGrid, Inc.
|
||||
# License: wxWindows License
|
||||
#----------------------------------------------------------------------------
|
||||
import xml.sax
|
||||
import xml.sax.handler
|
||||
|
||||
|
||||
class XMLPrettyPrinter(xml.sax.ContentHandler):
|
||||
def __init__(self, indentationChar=' ', newlineChar='\n'):
|
||||
self.xmlOutput = ''
|
||||
self.indentationLevel = 0
|
||||
self.indentationChar = indentationChar
|
||||
self.elementStack = []
|
||||
self.newlineChar = newlineChar
|
||||
self.hitCharData = False
|
||||
|
||||
## ContentHandler methods
|
||||
def startElement(self, name, attrs):
|
||||
indentation = self.newlineChar + (self.indentationLevel * self.indentationChar)
|
||||
# build attribute string
|
||||
attrstring = ''
|
||||
for attr in attrs.getNames():
|
||||
value = attrs[attr]
|
||||
attrstring += ' %s="%s"' % (attr, value)
|
||||
self.xmlOutput += '%s<%s%s>' % (indentation, name, attrstring)
|
||||
self.indentationLevel += 1
|
||||
self.elementStack.append(name)
|
||||
self.hitCharData = False
|
||||
|
||||
def characters(self, content):
|
||||
self.xmlOutput += content
|
||||
self.hitCharData = True
|
||||
|
||||
def endElement(self, name):
|
||||
self.indentationLevel -= 1
|
||||
indentation = ''
|
||||
if not self.hitCharData:
|
||||
## indentation += self.newlineChar + (self.indentationLevel * self.indentationChar)
|
||||
indentation += self.indentationLevel * self.indentationChar
|
||||
else:
|
||||
self.hitCharData = False
|
||||
self.xmlOutput += '%s</%s>%s' % (indentation, self.elementStack.pop(), self.newlineChar)
|
||||
|
||||
def getXMLString(self):
|
||||
return self.xmlOutput[1:]
|
||||
|
||||
def xmlprettyprint(xmlstr, spaces=4):
|
||||
xpp = XMLPrettyPrinter(indentationChar=' ' * spaces)
|
||||
xml.sax.parseString(xmlstr, xpp)
|
||||
return xpp.getXMLString()
|
||||
|
||||
if __name__ == '__main__':
|
||||
simpleTestString = """<one>some text<two anattr="booga">two's data</two></one>"""
|
||||
print prettyprint(simpleTestString)
|
||||
|
Reference in New Issue
Block a user