diff --git a/ShowSymmetries.glyphsReporter/Contents/Info.plist b/ShowSymmetries.glyphsReporter/Contents/Info.plist new file mode 100644 index 0000000..c6d39a0 --- /dev/null +++ b/ShowSymmetries.glyphsReporter/Contents/Info.plist @@ -0,0 +1,90 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ShowSymmetries + CFBundleExecutable + ShowSymmetries + CFBundleIdentifier + org.simon-cozens.ShowSymmetries + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ShowSymmetries + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 4 + CFBundleShortVersionString + 1.1.1 + UpdateFeedURL + + productPageURL + + LSHasLocalizedDisplayName + + NSAppleScriptEnabled + + NSHumanReadableCopyright + Copyright, by Simon Cozens, 2015 + NSMainNibFile + MainMenu + NSPrincipalClass + ShowSymmetries + PyMainFileNames + + __boot__ + + PyOptions + + alias + + argv_emulation + + no_chdir + + optimize + 0 + prefer_ppc + + site_packages + + use_pythonpath + + + PyResourcePackages + + lib/python2.6 + lib/python2.6/lib-dynload + lib/python2.6/site-packages.zip + lib/python26.zip + + PyRuntimeLocations + + @executable_path/../Frameworks/Python.framework/Versions/2.6/Python + /System/Library/Frameworks/Python.framework/Versions/2.6/Python + + PythonInfoDict + + PythonExecutable + /usr/bin/python2.6 + PythonLongVersion + 2.6.7 (r267:88850, Oct 11 2012, 20:15:00) +[GCC 4.2.1 Compatible Apple Clang 4.0 (tags/Apple/clang-418.0.60)] + PythonShortVersion + 2.6 + py2app + + template + bundle + version + 0.6.3 + + + + diff --git a/ShowSymmetries.glyphsReporter/Contents/MacOS/ShowSymmetries b/ShowSymmetries.glyphsReporter/Contents/MacOS/ShowSymmetries new file mode 100755 index 0000000..b21f0e4 Binary files /dev/null and b/ShowSymmetries.glyphsReporter/Contents/MacOS/ShowSymmetries differ diff --git a/ShowSymmetries.glyphsReporter/Contents/MacOS/python b/ShowSymmetries.glyphsReporter/Contents/MacOS/python new file mode 120000 index 0000000..c84e063 --- /dev/null +++ b/ShowSymmetries.glyphsReporter/Contents/MacOS/python @@ -0,0 +1 @@ +/System/Library/Frameworks/Python.framework/Versions/2.6/bin/python \ No newline at end of file diff --git a/ShowSymmetries.glyphsReporter/Contents/PkgInfo b/ShowSymmetries.glyphsReporter/Contents/PkgInfo new file mode 100644 index 0000000..19a9cf6 --- /dev/null +++ b/ShowSymmetries.glyphsReporter/Contents/PkgInfo @@ -0,0 +1 @@ +BNDL???? \ No newline at end of file diff --git a/ShowSymmetries.glyphsReporter/Contents/Resources/ShowSymmetries.py b/ShowSymmetries.glyphsReporter/Contents/Resources/ShowSymmetries.py new file mode 100755 index 0000000..1b7edb4 --- /dev/null +++ b/ShowSymmetries.glyphsReporter/Contents/Resources/ShowSymmetries.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python +# encoding: utf-8 +import objc +from Foundation import * +from AppKit import * +import sys, os, re +from Quartz import CGContextGetCTM, CGAffineTransformInvert, CGContextConcatCTM, CGContextRestoreGState, CGContextSaveGState + +MainBundle = NSBundle.mainBundle() +path = MainBundle.bundlePath() + "/Contents/Scripts" +if not path in sys.path: + sys.path.append( path ) + +import GlyphsApp +import glyphmonkey +from glyphmonkey import GSNodeSet + +GlyphsReporterProtocol = objc.protocolNamed( "GlyphsReporter" ) + +class ShowSymmetries ( NSObject, GlyphsReporterProtocol ): + + def init( self ): + bundle = NSBundle.bundleForClass_(ShowSymmetries) + self.rotational = bundle.imageForResource_("rotational") + self.reflectional = bundle.imageForResource_("reflectional") + self.reflecty = bundle.imageForResource_("reflecty") + return self + + def interfaceVersion( self ): + return 1 + + def title( self ): + return "Symmetries" + + def keyEquivalent( self ): + return None + + def modifierMask( self ): + return 0 + + def drawForegroundForLayer_( self, Layer ): + pass + + def drawSymmetries( self, Layer ): + currentZoom = self.getScale() + l = Layer.copy() + if l.pathCount() > 1: + l.removeOverlap() + ns = l.selectedNodeSet() + if len(ns) == 1: return + if len(ns) == 0: + sel = [] + for p in Layer.paths: + for n in p.nodes: + sel.append(n) + ns = GSNodeSet(sel) + + ox, oy = ns.center + + height = self.controller.view().bounds().size.height + width = self.controller.view().bounds().size.width + context = NSGraphicsContext.currentContext().CGContext() + oldat = CGContextGetCTM(context) + CGContextSaveGState(context) + inverted = CGAffineTransformInvert(oldat) + CGContextConcatCTM(context, inverted) + + if ns.equal(ns.copy().rotate(angle=180, ox=ox, oy=oy)): + self.rotational.drawInRect_(NSMakeRect(width-125,height-(15+25),25,25)) + + if ns.equal(ns.copy().reflect()): + self.reflectional.drawInRect_(NSMakeRect(width-95,height-(15+25),25,25)) + + if ns.equal(ns.copy().reflect(NSMakePoint(ox,oy), NSMakePoint(ox+100,oy))): + self.reflecty.drawInRect_(NSMakeRect(width-65,height-(15+25),25,25)) + + CGContextConcatCTM(context, oldat) + CGContextRestoreGState(context) + + def drawBackgroundForLayer_( self, Layer ): + try: + self.drawSymmetries( Layer ) + except Exception as e: + self.logToConsole( "drawBackgroundForLayer_: %s" % str(e) ) + + def drawBackgroundForInactiveLayer_( self, Layer ): + pass + + def drawTextAtPoint( self, text, textPosition, fontSize=10.0, fontColor=NSColor.colorWithCalibratedRed_green_blue_alpha_( 1, 0, .5, 1 ) ): + """ + Use self.drawTextAtPoint( "blabla", myNSPoint ) to display left-aligned text at myNSPoint. + """ + try: + glyphEditView = self.controller.graphicView() + currentZoom = self.getScale() + fontAttributes = { + NSFontAttributeName: NSFont.labelFontOfSize_( fontSize/currentZoom ), + NSForegroundColorAttributeName: fontColor } + displayText = NSAttributedString.alloc().initWithString_attributes_( text, fontAttributes ) + textAlignment = 2 # top left: 6, top center: 7, top right: 8, center left: 3, center center: 4, center right: 5, bottom left: 0, bottom center: 1, bottom right: 2 + glyphEditView.drawText_atPoint_alignment_( displayText, textPosition, textAlignment ) + except Exception as e: + self.logToConsole( "drawTextAtPoint: %s" % str(e) ) + + def needsExtraMainOutlineDrawingForInactiveLayer_( self, Layer ): + return True + + def getHandleSize( self ): + """ + Returns the current handle size as set in user preferences. + Use: self.getHandleSize() / self.getScale() + to determine the right size for drawing on the canvas. + """ + try: + Selected = NSUserDefaults.standardUserDefaults().integerForKey_( "GSHandleSize" ) + if Selected == 0: + return 5.0 + elif Selected == 2: + return 10.0 + else: + return 7.0 # Regular + except Exception as e: + self.logToConsole( "getHandleSize: HandleSize defaulting to 7.0. %s" % str(e) ) + return 7.0 + + def getScale( self ): + """ + self.getScale() returns the current scale factor of the Edit View UI. + Divide any scalable size by this value in order to keep the same apparent pixel size. + """ + try: + return self.controller.graphicView().scale() + except: + self.logToConsole( "Scale defaulting to 1.0" ) + return 1.0 + + def setController_( self, Controller ): + """ + Use self.controller as object for the current view controller. + """ + try: + self.controller = Controller + except Exception as e: + self.logToConsole( "Could not set controller" ) + + def logToConsole( self, message ): + """ + The variable 'message' will be passed to Console.app. + Use self.logToConsole( "bla bla" ) for debugging. + """ + myLog = "Show %s plugin:\n%s" % ( self.title(), message ) + NSLog( myLog ) diff --git a/ShowSymmetries.glyphsReporter/Contents/Resources/__boot__.py b/ShowSymmetries.glyphsReporter/Contents/Resources/__boot__.py new file mode 100644 index 0000000..86a4db8 --- /dev/null +++ b/ShowSymmetries.glyphsReporter/Contents/Resources/__boot__.py @@ -0,0 +1,47 @@ +# def _site_packages(): +# import site, sys, os +# paths = [] +# prefixes = [sys.prefix] +# if sys.exec_prefix != sys.prefix: +# prefixes.append(sys.exec_prefix) +# for prefix in prefixes: +# if prefix == sys.prefix: +# paths.append(os.path.join("/Library/Python", sys.version[:3], "site-packages")) +# paths.append(os.path.join(sys.prefix, "Extras", "lib", "python")) +# else: +# paths.append(os.path.join(prefix, 'lib', 'python' + sys.version[:3], 'site-packages')) +# if os.path.join('.framework', '') in os.path.join(sys.prefix, ''): +# home = os.environ.get('HOME') +# if home: +# paths.append(os.path.join(home, 'Library', 'Python', sys.version[:3], 'site-packages')) +# +# # Workaround for a misfeature in setuptools: easy_install.pth places +# # site-packages way too early on sys.path and that breaks py2app bundles. +# # NOTE: this hacks into an undocumented feature of setuptools and +# # might stop to work without warning. +# sys.__egginsert = len(sys.path) +# +# for path in paths: +# site.addsitedir(path) +# +# _site_packages() +# +# def _path_inject(): +# import sys +# sys.path[:0] = sys.path[0] +# +# _path_inject() + +def _run(*scripts): + global __file__ + import os, sys# , site + sys.frozen = 'macosx_plugin' + base = os.environ['RESOURCEPATH'] + # site.addsitedir(base) + # site.addsitedir(os.path.join(base, 'Python', 'site-packages')) + for script in scripts: + path = os.path.join(base, script) + __file__ = path + execfile(path, globals(), globals()) + +_run('ShowSymmetries.py') diff --git a/ShowSymmetries.glyphsReporter/Contents/Resources/__error__.sh b/ShowSymmetries.glyphsReporter/Contents/Resources/__error__.sh new file mode 100755 index 0000000..b3fc73d --- /dev/null +++ b/ShowSymmetries.glyphsReporter/Contents/Resources/__error__.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# +# This is the default bundletemplate error script +# Note that this DOES NOT present a GUI dialog, because +# it has no output on stdout, and has a return value of 0. +# +if ( test -n "$2" ) ; then + echo "[$1] Unexpected Exception:" 1>&2 + echo "$2: $3" 1>&2 +else + echo "[$1] Could not find a suitable Python runtime" 1>&2 +fi diff --git a/ShowSymmetries.glyphsReporter/Contents/Resources/glyphmonkey.py b/ShowSymmetries.glyphsReporter/Contents/Resources/glyphmonkey.py new file mode 100644 index 0000000..8f90058 --- /dev/null +++ b/ShowSymmetries.glyphsReporter/Contents/Resources/glyphmonkey.py @@ -0,0 +1,342 @@ +#MenuTitle: +# -*- coding: utf-8 -*- +__doc__="" +import GlyphsApp +from GlyphsApp import Proxy +from math import atan2, sqrt, cos, sin, radians +from Foundation import NSMakePoint, NSValue, NSMakeRect + +# Make GSNodes hashable + +class GSLineSegment(object): + def __init__(self, tuple = None, owner = None, idx = 0): + self._seg = tuple + if not self._seg: self._seg = owner._segments[idx] + self._owner = owner + self._owneridx = idx + + def __repr__(self): + """Return list-lookalike of representation string of objects""" + return "" % (self.start.x,self.start.y,self.end.x,self.end.y) + + def _seg(self): return self._seg + @property + def start(self): return self._seg[0].position + @property + def end(self): return self._seg[-1].position + + # For backward compatibility + def __getitem__(self, Key): + if Key < 0: + Key = self.__len__() + Key + # There is a horribly subtle distinction between an NSValue and + # an NSPoint. SpeedPunk expects to see an NSValue here and dies + # if it doesn't have one. + return NSValue.valueWithPoint_(self._seg[Key].position) + + @property + def area(self): + xa, ya = self.start.x, self.start.y/20 + xb, yb = xa, ya + xc, yc = self.end.x, self.end.y/20 + xd, yd = xc, yc + return (xb-xa)*(10*ya + 6*yb + 3*yc + yd) + (xc-xb)*( 4*ya + 6*yb + 6*yc + 4*yd) +(xd-xc)*( ya + 3*yb + 6*yc + 10*yd) + + @property + def length(self): + l1 = self.start.x - self.end.x + l2 = self.start.y - self.end.y + return sqrt(l1 * l1 + l2 * l2) + + @property + def angle(self): + e = self.end + s = self.start + return atan2(e.y - s.y, e.x - s.x) + + @property + def selected(self): + return self.start.selected and self.end.selected + + def __len__(self): + return 2 + +class GSCurveSegment(GSLineSegment): + def __repr__(self): + return "" % ( + self.start.x, self.start.y, + self.handle1.x, self.handle1.y, + self.handle2.x, self.handle2.y, + self.end.x,self.end.y + ) + + @property + def handle1(self): return self._seg[1].position + @property + def handle2(self): return self._seg[2].position + + def area(self): + xa, ya = self.start.x, self.start.y/20 + xb, yb = self.handle1.x, self.handle1.y/20 + xc, yc = self.handle2.x, self.handle2.y/20 + xd, yd = self.end.x, self.end.y/20 + return (xb-xa)*(10*ya + 6*yb + 3*yc + yd) + (xc-xb)*( 4*ya + 6*yb + 6*yc + 4*yd) +(xd-xc)*( ya + 3*yb + 6*yc + 10*yd) + + def angle(self): + e = self.end + s = self.start + return atan2(e.y - s.y, e.x - s.x) + + def interpolate_at_fraction(self, t): + if t < 0 or t > 1: + raise Exception("interpolate_at_fraction should be called with a number between 0 and 1") + t1 = 1.0 - t; + t1_3 = t1*t1*t1 + t1_3a = (3*t)*(t1*t1) + t1_3b = (3*(t*t))*t1; + t1_3c = (t * t * t ) + x = (self.start.x * t1_3) + (t1_3a * self.handle1.x) + (t1_3b * self.handle2.x) + (t1_3c * self.end.x) + y = (self.start.y * t1_3) + (t1_3a * self.handle1.y) + (t1_3b * self.handle2.y) + (t1_3c * self.end.y) + return (x,y) + + @property + def length(self): + steps = 50 + t = 0.0 + length = 0 + previous = () + while t < 1.0: + this = self.interpolate_at_fraction(t) + if t > 0: + dx = previous[0] - this[0] + dy = previous[1] - this[1] + length = length + sqrt(dx*dx+dy*dy) + t = t + 1.0/steps + previous = this + return length + + def __len__(self): + return 4 + +class PathSegmentsProxy (Proxy): + # Actually we're not going to use .segments at all, because we + # want to be able to access things like GSNode.selected + def toSegments(p): + segList = [] + nodeList = p._owner.nodes + thisSeg = (nodeList[-1],) + for i in range(0,len(nodeList)): + thisSeg = thisSeg + (nodeList[i],) + if nodeList[i].type != GlyphsApp.GSOFFCURVE: + segList.append(thisSeg) + thisSeg = (nodeList[i],) + return segList + def __getitem__(self, Key): + if Key < 0: + Key = self.__len__() + Key + segs = self.toSegments() + if len(segs[Key]) == 2: + return GSLineSegment( owner = self._owner, idx = Key, tuple = segs[Key]) + else: + return GSCurveSegment( owner = self._owner, idx = Key, tuple = segs[Key]) + def __setitem__(self, Key, Layer): + if Key < 0: + Key = self.__len__() + Key + # XXX + def __len__(self): + return len(self.toSegments()) + def values(self): + return map(self.__getitem__, range(0,self.__len__())) + +# Unfortunately working with segments doesn't always *work*. So we +# map a segment list to a node list +GSNode = GlyphsApp.GSNode + +def toNodeList(segments): + nodelist = [] + for i in range(0,len(segments)): + s = segments[i] + t = GlyphsApp.GSCURVE + c = GlyphsApp.GSSMOOTH + if type(s) is GSLineSegment: + t = GlyphsApp.GSLINE + else: + s1 = s.handle1 + nodelist.append(GSNode((s1.x,s1.y), GlyphsApp.GSOFFCURVE)) + s2 = s.handle2 + nodelist.append(GSNode((s2.x,s2.y), GlyphsApp.GSOFFCURVE)) + + ns = i+1 + if ns >= len(segments): ns = 0 + if type(segments[ns]) is GSLineSegment: + c = GlyphsApp.GSSHARP + e = s.end + node = GSNode((e.x, e.y), t) + node.connection = c + nodelist.append(node) + return nodelist + +GlyphsApp.GSPath.segments = property( lambda self: PathSegmentsProxy(self), + lambda self, value: + self.setNodes_(toNodeList(value)) +) + +def nodeRotate(self, ox, oy, angle): + angle = radians(angle) + newX = ox + (self.position.x-ox)*cos(angle) - (self.position.y-oy)*sin(angle) + newY = oy + (self.position.x-ox)*sin(angle) + (self.position.y-oy)*cos(angle) + self.position = (round(newX,2), round(newY,2)) + +def nodeReflect(self, p0, p1): + dx = p1.x - p0.x + dy = p1.y - p0.y + a = (dx * dx - dy * dy) / (dx * dx + dy * dy) + b = 2 * dx * dy / (dx * dx + dy * dy) + x = a * (self.position.x - p0.x) + b * (self.position.y - p0.y) + p0.x + y = b * (self.position.x - p0.x) - a * (self.position.y - p0.y) + p0.y + self.position =(round(x,2), round(y,2)) + +GlyphsApp.GSNode.rotate = nodeRotate +GlyphsApp.GSNode.reflect = nodeReflect + +### additional GSPath methods + +def layerCenter(self): + bounds = self.parent.bounds + ox = bounds.origin.x + bounds.size.width / 2 + oy = bounds.origin.y + bounds.size.height / 2 + return NSMakePoint(ox, oy) + +def pathCenter(self): + bounds = self.bounds + ox = bounds.origin.x + bounds.size.width / 2 + oy = bounds.origin.y + bounds.size.height / 2 + return NSMakePoint(ox, oy) + +def pathRotate(self, angle=-1, ox=-1, oy=-1): + if angle == -1: angle = 180 + if ox == -1 and oy == -1: + if self.parent: # Almost always + ox, oy = self.layerCenter().x, self.layerCenter().y + else: + ox, oy = self.center().x, self.center().y + + for n in self.nodes: + n.rotate(ox, oy, angle) + return self + +def pathReflect(self, p0 = -1, p1 = -1): + if p0 == -1 and p1 == -1: + if self.parent: # Almost always + p0 = self.layerCenter() + p1 = self.layerCenter() + else: + p0 = self.center() + p1 = self.center() + p1.y = p1.y + 100 + + for n in self.nodes: + n.reflect(p0, p1) + return self + +def pathDiff(p1, p2): + nodes1 = set((n.position.x,n.position.y) for n in p1.nodes) + nodes2 = set((n.position.x,n.position.y) for n in p2.nodes) + return nodes1 - nodes2 + +def pathEqual(p1, p2): + pd = pathDiff(p1, p2) + return len(pd) == 0 + +def pathToNodeSet(self): + return GSNodeSet(self.nodes) + +GlyphsApp.GSPath.layerCenter = layerCenter +GlyphsApp.GSPath.center = pathCenter +GlyphsApp.GSPath.rotate = pathRotate +GlyphsApp.GSPath.reflect = pathReflect +GlyphsApp.GSPath.equal = pathEqual +GlyphsApp.GSPath.diff = pathDiff +GlyphsApp.GSPath.toNodeSet = pathToNodeSet + +class GSNodeSet(object): + def toKey(self,n): + return "%s %s %s" % (n.position.x, n.position.y, n.type) + + def __init__(self, nodes): + self._dict = {} + for n in nodes: + self._dict[self.toKey(n)] = n + + def __repr__(self): + return "" % (len(self)) + + def __len__(self): + return len(self._dict) + + @property + def nodes(self): + return self._dict.values() + + @property + def bounds(self): + minx, maxx, miny, maxy = None, None, None, None + if len(self) < 1: return None + for p in self.nodes: + pos = p.position + if minx == None or pos.x < minx: minx = pos.x + if maxx == None or pos.x > maxx: maxx = pos.x + if miny == None or pos.y < minx: miny = pos.y + if maxy == None or pos.y > maxx: maxy = pos.y + + return NSMakeRect(minx, miny, maxx-minx, maxy-miny) + + @property + def center(self): + if len(self) < 1: return None + b = self.bounds + return NSMakePoint(b.origin.x + b.size.width / 2, b.origin.y + b.size.height / 2, ) + + def copy(self): + return GSNodeSet(n.copy() for n in self.nodes) + + def diff(ns1, ns2): + nodes1 = set((n.position.x,n.position.y) for n in ns1.nodes) + nodes2 = set((n.position.x,n.position.y) for n in ns2.nodes) + return nodes1 - nodes2 + + def equal(p1, p2): + pd = p1.diff(p2) + return len(pd) == 0 + + def rotate(self, angle=-1, ox=-1, oy=-1): + if angle == -1: angle = 180 + if ox == -1 and oy == -1: + ox, oy = self.center.x, self.center.y + + for n in self.nodes: + n.rotate(ox, oy, angle) + return self + + def reflect(self, p0 = -1, p1 = -1): + if p0 == -1 and p1 == -1: + p0 = self.center + p1 = self.center + p1.y = p1.y + 100 + + for n in self.nodes: + n.reflect(p0, p1) + return self + +def selectedNodeSet(layer): + sel = [] + for n in layer.selection: + if isinstance(n, GSNode): + sel.append(n) + return GSNodeSet(sel) + +GlyphsApp.GSLayer.selectedNodeSet = selectedNodeSet + +# Does p have rotational symmetry? +# ox, oy = p.layerCenter() +# p.equal(p.copy().rotate(angle=180, ox=ox, oy=oy)) diff --git a/ShowSymmetries.glyphsReporter/Contents/Resources/glyphmonkey.pyc b/ShowSymmetries.glyphsReporter/Contents/Resources/glyphmonkey.pyc new file mode 100644 index 0000000..c4423c4 Binary files /dev/null and b/ShowSymmetries.glyphsReporter/Contents/Resources/glyphmonkey.pyc differ diff --git a/ShowSymmetries.glyphsReporter/Contents/Resources/reflectional.png b/ShowSymmetries.glyphsReporter/Contents/Resources/reflectional.png new file mode 100644 index 0000000..0ac9e5e Binary files /dev/null and b/ShowSymmetries.glyphsReporter/Contents/Resources/reflectional.png differ diff --git a/ShowSymmetries.glyphsReporter/Contents/Resources/reflecty.png b/ShowSymmetries.glyphsReporter/Contents/Resources/reflecty.png new file mode 100644 index 0000000..1723181 Binary files /dev/null and b/ShowSymmetries.glyphsReporter/Contents/Resources/reflecty.png differ diff --git a/ShowSymmetries.glyphsReporter/Contents/Resources/rotational.png b/ShowSymmetries.glyphsReporter/Contents/Resources/rotational.png new file mode 100644 index 0000000..5602866 Binary files /dev/null and b/ShowSymmetries.glyphsReporter/Contents/Resources/rotational.png differ diff --git a/ShowSymmetries.glyphsReporter/Contents/Resources/site.py b/ShowSymmetries.glyphsReporter/Contents/Resources/site.py new file mode 100644 index 0000000..889eb41 --- /dev/null +++ b/ShowSymmetries.glyphsReporter/Contents/Resources/site.py @@ -0,0 +1,130 @@ +""" +Append module search paths for third-party packages to sys.path. + +This is stripped down and customized for use in py2app applications +""" + +import sys +# os is actually in the zip, so we need to do this here. +# we can't call it python24.zip because zlib is not a built-in module (!) +_libdir = '/lib/python' + sys.version[:3] +_parent = '/'.join(__file__.split('/')[:-1]) +if not _parent.endswith(_libdir): + _parent += _libdir +sys.path.append(_parent + '/site-packages.zip') + +# Stuffit decompresses recursively by default, that can mess up py2app bundles, +# add the uncompressed site-packages to the path to compensate for that. +sys.path.append(_parent + '/site-packages') + +import os +try: + basestring +except NameError: + basestring = str + +def makepath(*paths): + dir = os.path.abspath(os.path.join(*paths)) + return dir, os.path.normcase(dir) + +for m in sys.modules.values(): + f = getattr(m, '__file__', None) + if isinstance(f, basestring) and os.path.exists(f): + m.__file__ = os.path.abspath(m.__file__) +del m + +# This ensures that the initial path provided by the interpreter contains +# only absolute pathnames, even if we're running from the build directory. +L = [] +_dirs_in_sys_path = {} +dir = dircase = None # sys.path may be empty at this point +for dir in sys.path: + # Filter out duplicate paths (on case-insensitive file systems also + # if they only differ in case); turn relative paths into absolute + # paths. + dir, dircase = makepath(dir) + if not dircase in _dirs_in_sys_path: + L.append(dir) + _dirs_in_sys_path[dircase] = 1 +sys.path[:] = L +del dir, dircase, L +_dirs_in_sys_path = None + +def _init_pathinfo(): + global _dirs_in_sys_path + _dirs_in_sys_path = d = {} + for dir in sys.path: + if dir and not os.path.isdir(dir): + continue + dir, dircase = makepath(dir) + d[dircase] = 1 + +def addsitedir(sitedir): + global _dirs_in_sys_path + if _dirs_in_sys_path is None: + _init_pathinfo() + reset = 1 + else: + reset = 0 + sitedir, sitedircase = makepath(sitedir) + if not sitedircase in _dirs_in_sys_path: + sys.path.append(sitedir) # Add path component + try: + names = os.listdir(sitedir) + except os.error: + return + names.sort() + for name in names: + if name[-4:] == os.extsep + "pth": + addpackage(sitedir, name) + if reset: + _dirs_in_sys_path = None + +def addpackage(sitedir, name): + global _dirs_in_sys_path + if _dirs_in_sys_path is None: + _init_pathinfo() + reset = 1 + else: + reset = 0 + fullname = os.path.join(sitedir, name) + try: + f = open(fullname) + except IOError: + return + while 1: + dir = f.readline() + if not dir: + break + if dir[0] == '#': + continue + if dir.startswith("import"): + exec(dir) + continue + if dir[-1] == '\n': + dir = dir[:-1] + dir, dircase = makepath(sitedir, dir) + if not dircase in _dirs_in_sys_path and os.path.exists(dir): + sys.path.append(dir) + _dirs_in_sys_path[dircase] = 1 + if reset: + _dirs_in_sys_path = None + + +#sys.setdefaultencoding('utf-8') + +# +# Run custom site specific code, if available. +# +try: + import sitecustomize +except ImportError: + pass + +# +# Remove sys.setdefaultencoding() so that users cannot change the +# encoding after initialization. The test for presence is needed when +# this module is run as a script, because this code is executed twice. +# +if hasattr(sys, "setdefaultencoding"): + del sys.setdefaultencoding