File indexing completed on 2024-07-14 15:05:11

0001 # -*- coding: utf-8 -*-
0002 #     Copyright 2009-2010 Simon Edwards <simon@simonzone.com>
0003 #
0004 # This program is free software; you can redistribute it and/or modify
0005 # it under the terms of the GNU General Public License as published by
0006 # the Free Software Foundation; either version 2 of the License, or
0007 # (at your option) any later version.
0008 #
0009 # This program is distributed in the hope that it will be useful,
0010 # but WITHOUT ANY WARRANTY; without even the implied warranty of
0011 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012 # GNU General Public License for more details.
0013 #
0014 # You should have received a copy of the GNU General Public License
0015 # along with this program; if not, write to the
0016 # Free Software Foundation, Inc.,
0017 # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018 
0019 #from argvalidate import accepts,returns,one_of
0020 import types
0021 from .sealed import sealed
0022 import kbindinggenerator.cmake as cmake
0023 import kbindinggenerator.cpplexer as cpplexer
0024 import kbindinggenerator.cppparser as cppparser
0025 import kbindinggenerator.cppsymboldata as cppsymboldata
0026 import kbindinggenerator.sipparser as sipparser
0027 import kbindinggenerator.sipsymboldata as sipsymboldata
0028 import kbindinggenerator.cpptosiptransformer as cpptosiptransformer
0029 import kbindinggenerator.sipmerger as sipmerger
0030 import os
0031 import os.path
0032 import glob
0033 from .reducetxt import Reduce
0034 
0035 
0036 def cmp(a, b):
0037     return (a > b) - (a < b)
0038 
0039 reducer = Reduce()
0040 
0041 class ModuleGenerator(object):
0042     @sealed
0043     def __init__(self,module,cmakelists=[], cmakeVariables=None, headers=[],ignoreHeaders=[],noUpdateSip=[],outputDirectory=None,
0044             preprocessorValues=[],preprocessSubstitutionMacros=[],macros=[],bareMacros=[],exportMacros=None,
0045             ignoreBases=None,noCTSCC=[],sipImportDirs=[],sipImports=[],copyrightNotice=None,
0046             annotationRules=[],docsOutputDirectory=None,mainDocs=None,filenameMappingFunction=None,
0047             cppHeaderMappingFunction=None):
0048             
0049         self._module = module
0050         self._cmakelists = [cmakelists] if (isinstance(cmakelists,str) or isinstance(cmakelists,unicode)) else cmakelists
0051         self._cmakeVariables = cmakeVariables if cmakeVariables is not None else {}
0052         self._headers = headers
0053         self._ignoreHeaders = set([ignoreHeaders] if isinstance(ignoreHeaders,str) else ignoreHeaders)
0054         self._noUpdateSip = noUpdateSip
0055         self._outputDirectory = outputDirectory
0056         
0057         self._preprocessorValues = preprocessorValues
0058         self._preprocessSubstitutionMacros = preprocessSubstitutionMacros
0059         self._macros = macros
0060         self._bareMacros = bareMacros
0061 
0062         self._filenameMappingFunction = filenameMappingFunction
0063         self._cppHeaderMappingFunction = cppHeaderMappingFunction
0064 
0065         self._symbolData = cppsymboldata.SymbolData()
0066         self._cppParser = cppparser.CppParser()
0067         self._cppParser.preprocessorValues = self._preprocessorValues
0068         self._cppParser.bareMacros = self._bareMacros
0069         self._cppParser.macros = self._macros
0070         self._cppParser.preprocessorSubstitutionMacros = self._preprocessSubstitutionMacros
0071         
0072         self._cppScopeList = []
0073 
0074         self._transformer = cpptosiptransformer.CppToSipTransformer()
0075         self._transformer.setExportMacros(exportMacros)
0076         self._transformer.setIgnoreBaseClasses(ignoreBases)
0077         self._transformer.setCopyrightNotice(copyrightNotice)
0078         self._noCTSCC = noCTSCC
0079         
0080         self._copyrightNotice = copyrightNotice
0081         
0082         self._sipImportDirs = sipImportDirs
0083         self._sipImports = sipImports
0084         
0085         self._sipParser = sipparser.SipParser()
0086         self._sipSymbolData = sipsymboldata.SymbolData()
0087         
0088         self._annotator = cpptosiptransformer.SipAnnotator()
0089         self._annotator.setMethodAnnotationRules(annotationRules)
0090         
0091         self._docsOutputDirectory = docsOutputDirectory
0092         self._mainDocs = mainDocs
0093         
0094     def run(self):
0095         print("Extracting header file list from CMake:")
0096         cppHeaderFilenameSet = self.extractCmakeListsHeaders()
0097 
0098         extraHeaderSet = self.expandHeaders()
0099         cppHeaderFilenameSet.update(extraHeaderSet)
0100 
0101         cppHeaderFilenameList = list(cppHeaderFilenameSet)
0102         cppHeaderFilenameList.sort()
0103         for filename in cppHeaderFilenameList:
0104             print("    Found %s" % (filename,))
0105         
0106         print("\nParsing Cpp headers:")
0107         headerScopeTuples = self._parseHeaders(cppHeaderFilenameList)
0108         
0109         print("\nParsing imported Sip files:")
0110         self._parseImportedSip()
0111         
0112         print("\nConverting header files into Sip files.")
0113         moduleSipScopes = self._convertCppToSip(headerScopeTuples)
0114         
0115         print("\nExpanding class names:")
0116         for scope in moduleSipScopes:
0117             cpptosiptransformer.ExpandClassNames(self._sipSymbolData,scope)
0118 
0119         print("\nAnnotating Sip files.")
0120         self._annotateSipScopes(moduleSipScopes)
0121         
0122         _indexFilename = self._indexFilename()
0123         if os.path.exists(_indexFilename):
0124             print("\nParsing previous Sip files.")
0125             moduleSipScopes = self._updateScopes(moduleSipScopes)
0126         else:
0127             print("(%s not found. Skipping merge with previous sip files.)" % (_indexFilename,))
0128             
0129         print("\n")
0130 
0131         print("Computing 'Convert To Sub Class Code'.")
0132         cpptosiptransformer.UpdateConvertToSubClassCodeDirectives(self._sipSymbolData,moduleSipScopes,self._noCTSCC)
0133         
0134         #print("Sanity check.")
0135         #cpptosiptransformer.SanityCheckSip(self._sipSymbolData,moduleSipScopes)
0136         #return
0137         print("Writing Sip files.")
0138         if self._outputDirectory is not None:
0139 
0140             if os.path.exists(self._outputDirectory) and not os.path.isdir(self._outputDirectory):
0141                 print("Error: Output directory '%s' is not a directory.")
0142             else:
0143                 if not os.path.exists(self._outputDirectory):
0144                     os.mkdir(self._outputDirectory)
0145                 self._writeScopes(moduleSipScopes)
0146                 self._writeIndexSip(moduleSipScopes)
0147         else:        
0148             print("Warning: Skipping writing because no output directory was specified.")
0149         
0150         #for scope in moduleSipScopes:
0151         #    print(scope.format())
0152         print("Done.")
0153         
0154     def extractCmakeListsHeaders(self):
0155         filenames = []
0156         for cmakeFilename in self._cmakelists:
0157             dirName = os.path.dirname(cmakeFilename)
0158             for header in cmake.ExtractInstallFiles(cmakeFilename, variables=self._cmakeVariables):
0159                 if os.path.basename(header) not in self._ignoreHeaders:
0160                     filenames.append(os.path.join(dirName,header))
0161         print(repr(set(filenames)))
0162         return set(filenames)
0163         
0164     def expandHeaders(self):
0165         expanded_headers = set()
0166         for pattern in self._headers:
0167             for header in glob.iglob(pattern):
0168                 expanded_headers.add(header)
0169         return expanded_headers
0170         
0171     def _parseHeaders(self,cppHeaderFilenameList):
0172         headerScopeTuples = []
0173         
0174         for filename in cppHeaderFilenameList:
0175             print("    Parsing %s" % (filename,))
0176             with open(filename) as fhandle:
0177                 text = fhandle.read()
0178 
0179             if self._cppHeaderMappingFunction is not None:
0180                 basename = self._cppHeaderMappingFunction(self,filename)
0181             else:
0182                 basename = os.path.basename(filename)
0183 
0184             scope = self._cppParser.parse(self._symbolData, text, filename=filename, debugLevel=0)
0185             scope.setHeaderFilename(basename)
0186             headerScopeTuples.append( (basename,scope) )
0187             #print(scope.format())
0188         return headerScopeTuples
0189     
0190     def _parseImportedSip(self):
0191         scopes = []
0192         
0193         for sipImport in self._sipImports:
0194             filename = self._findSipMod(sipImport)
0195             if filename is None:
0196                 raise SystemExit
0197             print("    Parsing %s" % (filename,))
0198             scopes.append(self._importSipFile(filename))
0199         return scopes
0200         
0201     def _findSipMod(self,sipModName):
0202         for sipImportDir in self._sipImportDirs:
0203             filename = os.path.join(sipImportDir,sipModName)
0204             if os.path.exists(filename):
0205                 return filename
0206         else:
0207             print("Error: Unable to find sip import '%s'. (sipImportDirs=%s" % (sipModName,repr(self._sipImportDirs)))
0208         return None
0209     
0210     def _importSipFile(self,sipFilename,noUpdateSip=[],module=None):
0211         with open(sipFilename) as fhandle:
0212             text = fhandle.read()
0213 
0214         scope = self._sipParser.parse(self._sipSymbolData,text,filename=sipFilename,debugLevel=0)
0215         
0216         # Figure out the Cpp header file name.
0217         def extractHeader(directives,directiveName):
0218             for directive in directives:
0219                 if directive.name()==directiveName and directive.body() is not None:
0220                     for line in directive.body().split('\n'):
0221                         if line.startswith("#include <"):
0222                             return line[10:-1]
0223 
0224         directives = self._findAllInstance(scope,self._sipSymbolData.SipDirective)
0225         scope.setHeaderFilename(extractHeader(directives,"%TypeHeaderCode") \
0226             or extractHeader(directives,"%ModuleHeaderCode") \
0227             or sipFilename)
0228         
0229         # Set the correct module name
0230         for item in scope:
0231             if isinstance(item,self._sipSymbolData.SipDirective):
0232                 if item.name()=="Module":
0233                     module = item.body().split(' ')[1]
0234                     break
0235         scope.setModule(module)
0236         
0237         # print(sipFilename + " -> " + scope.headerFilename())
0238         
0239         scopeList = [scope]
0240         
0241         #print("********************************************")
0242         #print(scope.format())
0243         
0244         modDir = os.path.dirname(sipFilename)
0245         
0246         for item in scope:
0247             if isinstance(item,self._sipSymbolData.SipDirective):
0248                 if item.body().startswith("%Include"):
0249                     sipIncludeFilename = item.body()[len("%Include")+1:]
0250                     if sipIncludeFilename not in noUpdateSip:
0251                         sipIncludeFullFilename = os.path.join(modDir,sipIncludeFilename)
0252                         if os.path.exists(sipIncludeFullFilename):
0253                             scopeList.extend(self._importSipFile(sipIncludeFullFilename,module=module))
0254                         else:
0255                             print("Error: Unable to find sip import '%s'. (sipImportDirs=%s" % (sipIncludeFilename,repr(self._sipImportDirs)))
0256                     
0257         return scopeList
0258         
0259     def _findAllInstance(self,scope,matchType):
0260         result = []
0261         for item in scope:
0262             if isinstance(item,matchType):
0263                 result.append(item)
0264             if isinstance(item,self._sipSymbolData.SipClass) or isinstance(item,self._sipSymbolData.Namespace):
0265                 result.extend(self._findAllInstance(item,matchType))
0266         return result
0267 
0268     def _convertCppToSip(self,headerScopeTuples):
0269         sipScopes = []
0270         for headerName,cppScope in headerScopeTuples:
0271             print("    Converting %s" % (headerName,))
0272             sipscope = self._transformer.convert(cppScope,self._sipSymbolData)
0273             sipScopes.append(sipscope)
0274         return sipScopes
0275         
0276     def _annotateSipScopes(self,moduleSipScopes):
0277         for scope in moduleSipScopes:
0278             self._annotator.applyRules(scope)
0279         
0280     def _convertHeaderNameToSip(self,headerName):
0281         if self._filenameMappingFunction is not None:
0282             return self._filenameMappingFunction(self,headerName)
0283 
0284         filename = os.path.basename(headerName)
0285         if filename.endswith(".h"):
0286             return filename[:-2]+".sip"
0287         return filename
0288         
0289     def _writeScopes(self,moduleSipScopes):
0290         for scope in moduleSipScopes:
0291             fullPath = os.path.join(self._outputDirectory,self._convertHeaderNameToSip(scope.headerFilename()))
0292             with open(fullPath,'w') as fhandle:
0293                 fhandle.write(scope.format())
0294                 
0295     def _writeIndexSip(self,scopes):
0296         moduleName = self._module
0297         if '.' in self._module:
0298             moduleName = self._module[self._module.rfind('.')+1:]
0299         
0300         indexFilename = self._indexFilename()
0301         with open(self._indexFilename(),'w') as fhandle:
0302             fhandle.write(self._indexSip( [s for s in scopes if s.headerFilename()!=indexFilename] ))
0303             
0304     def _indexFilename(self):
0305         module = self._module
0306         if '.' in module:
0307             module = module.rpartition('.')[2]
0308         return os.path.join(self._outputDirectory,module) + "mod.sip"
0309         
0310     def _updateScopes(self,updateSipScopes):
0311         previousSipScopes = self._importSipFile(self._indexFilename(),self._noUpdateSip)
0312         
0313         # Match updateSipScopes
0314         updateSipMap = {}
0315         for scope in updateSipScopes:
0316             #print("header sip name: " + self._convertHeaderNameToSip(scope.headerFilename()))
0317             updateSipMap[self._convertHeaderNameToSip(scope.headerFilename())] = scope
0318             
0319         for scope in previousSipScopes:
0320             filename = self._convertHeaderNameToSip(scope.headerFilename())
0321             previousScope = updateSipMap.get(filename,None)
0322             if previousScope is not None:
0323                 print("    Merging %s" % (filename,))
0324                 sipmerger.MergeSipScope(self._sipSymbolData,scope,updateSipMap[filename])
0325                 self._sipSymbolData.removeScope(updateSipMap[filename])
0326                 del updateSipMap[filename]
0327             else:
0328                 print("    (Missing header file to match %s. Skipping merge.)" % (filename,) )
0329             
0330         for key in updateSipMap.keys():
0331             print("    Adding new header "+key)
0332             previousSipScopes.append(updateSipMap[key])
0333             
0334         return previousSipScopes
0335         
0336     def _indexSip(self,scopes):
0337         def key(x): return x.headerFilename()
0338         scopes.sort(key=key)
0339         
0340         accu = []
0341         
0342         if self._copyrightNotice is not None:
0343             accu.append(self._copyrightNotice)
0344         
0345         accu.append("\n%Module ")
0346         accu.append(self._module)
0347         accu.append("\n\n")
0348         
0349         accu.append("%ModuleHeaderCode\n")
0350         accu.append("#pragma GCC visibility push(default)\n")
0351         accu.append("%End\n\n")
0352         
0353         # %Import
0354         for sipImport in self._sipImports:
0355             accu.append("%Import ")
0356             accu.append(sipImport)
0357             accu.append("\n")
0358         accu.append("\n")
0359         
0360         for sipFile in self._noUpdateSip:
0361             accu.append("%Include ")
0362             accu.append(sipFile)
0363             accu.append("\n")
0364         
0365         for scope in scopes:
0366             accu.append("%Include ")
0367             accu.append(self._convertHeaderNameToSip(scope.headerFilename()))
0368             accu.append("\n")
0369         
0370         return ''.join(accu)
0371 
0372     def docs(self):
0373         print("Extracting header file list from CMake:")
0374         cppHeaderFilenameSet = self.extractCmakeListsHeaders()
0375         
0376         extraHeaderSet = self.expandHeaders()
0377         cppHeaderFilenameSet.update(extraHeaderSet)
0378         
0379         cppHeaderFilenameList = list(cppHeaderFilenameSet)
0380         cppHeaderFilenameList.sort()
0381         for filename in cppHeaderFilenameList:
0382             print("    Found %s" % (filename,))
0383         
0384         print("\nParsing Cpp headers:")
0385         headerScopeTuples = self._parseHeaders(cppHeaderFilenameList)
0386         cppScopes = [ x[1] for x in headerScopeTuples]
0387 
0388         print("\nParsing imported Sip files:")
0389         self._parseImportedSip()
0390         
0391         print("\nParsing module files:")
0392         previousSipScopes = self._importSipFile(self._indexFilename(),self._noUpdateSip)
0393         
0394         print("\nWriting index page:")
0395         self.writeModuleIndexPage(previousSipScopes)
0396         
0397         print("\nWriting class pages:")
0398         subclassMapping = BuildSubclassMap(previousSipScopes,self._sipSymbolData)
0399         #print(repr(subclassMapping))
0400         
0401         # Write out the
0402         def writeScopeDocs(scope):
0403             for item in scope:
0404                 if isinstance(item,self._sipSymbolData.SipClass):
0405                     self.writeClassDoc(item,subclassMapping)
0406                     writeScopeDocs(item)
0407                 elif isinstance(item,self._sipSymbolData.Namespace):
0408                     writeScopeDocs(item)
0409         for scope in previousSipScopes:
0410             writeScopeDocs(scope)
0411 
0412         print("\nWriting namespace pages:")
0413         self.writeNamespaces(previousSipScopes,cppScopes)
0414         return previousSipScopes
0415 
0416     # @accepts(sipsymboldata.SymbolData.SipClass, dict)
0417     def writeClassDoc(self, sipClass, subclassMapping):
0418         classComment = ""
0419         cppClass = None
0420         try:
0421             cppClass = self._symbolData.lookupType(sipClass.fqName(),sipClass)
0422             
0423             #print("\nHit " + sipClass.fqName())
0424             #print("parent "+repr(cppClass.parentScope()))
0425             
0426             parentScope = cppClass.parentScope()
0427             for item in parentScope:
0428                 if isinstance(item,self._symbolData.Comment):
0429                     classComment = item.value()
0430                     #print("classComment:"+classComment)
0431                 if item is cppClass:
0432                     break
0433             
0434         except KeyError:
0435             print("Unable to find Cpp class info for '" + sipClass.fqName() +" ' while writing class docs.")
0436 
0437         self.writeClassPage(sipClass,subclassMapping,cppClass,classComment)
0438 
0439     # @accepts(sipsymboldata.SymbolData.Entity)
0440     # @returns(str)
0441     def commentMapKey(self, item):
0442         if isinstance(item, (self._sipSymbolData.Constructor, self._symbolData.Constructor)):
0443             argNames = [(x.name() if x.name() is not None else "") for x in item.arguments()]
0444             key = item.name() + "|" + '|'.join(argNames)
0445         else:
0446             key = item.name()
0447         return key
0448 
0449     # @accepts(sipsymboldata.SymbolData.CppClass)
0450     # @returns(dict)
0451     def buildCommentMap(self,cppClass):
0452         lastComment = None
0453         commentMap = {}
0454 
0455         for item in cppClass:
0456             if isinstance(item,self._symbolData.Comment):
0457                 lastComment = item.value()
0458             elif lastComment is not None:
0459                 if isinstance(item, (self._symbolData.Function,self._symbolData.Enum) ):
0460                     commentMap[self.commentMapKey(item)] = lastComment
0461                     lastComment = None
0462         return commentMap
0463             
0464     # @accepts(sipsymboldata.SymbolData.SipClass, dict, one_of(sipsymboldata.SymbolData.CppClass,types.NoneType), str)
0465     def writeClassPage(self,sipClass,subclassMapping,cppClass,classComment):
0466         def nameKey(a): return a.name() if a.name() is not None else ""
0467         enumList = [item for item in sipClass if isinstance(item,self._sipSymbolData.Enum) and not item.ignore()]
0468         enumList.sort(key=nameKey)
0469         
0470         methodList = [item for item in sipClass if isinstance(item,self._sipSymbolData.Function) and not item.ignore()]
0471         methodList.sort(key=nameKey)
0472         constructorList = [item for item in sipClass if isinstance(item,self._sipSymbolData.Constructor) and not item.ignore()]
0473         constructorList.extend(methodList)
0474         methodList = constructorList
0475         
0476         classList = [item for item in sipClass if isinstance(item,self._sipSymbolData.SipClass) and not item.ignore()]
0477         classList.sort(key=nameKey)
0478         
0479         variableList = [item for item in sipClass if isinstance(item,self._sipSymbolData.Variable) and not item.ignore()]
0480         variableList.sort(key=nameKey)
0481 
0482         commentMap = self.buildCommentMap(cppClass) if cppClass is not None else {}
0483 
0484         #if cls in self.flagged:
0485         #    flags = self.flagged [cls]
0486         #else:
0487         #    flags = None
0488         
0489         # create class page and add header
0490         className = sipClass.fqPythonName()    # FIXME fqn required plus the python name.
0491 
0492         page = open(os.path.join(self._docsOutputDirectory, "%s.html" % className), 'w')
0493         page.write(htmlHeader % {'title': className, 'path': '../'})
0494         
0495         if isSipClassAbstract(sipClass):
0496             abstract = """<dl class="abstract" compact><dt><b>Abstract class:</b></dt>
0497 <dd>This class can be used as a base class for new classes, but can not be instantiated directly.</dd></dl>"""
0498         else:
0499             abstract = ''
0500         # &#x2192;  arrow
0501         if len(sipClass.bases())!=0:
0502             bases = []
0503             for base in sipClass.bases():
0504                 try:
0505                     baseClass = self._sipSymbolData.lookupType(base,sipClass)
0506                 except KeyError:
0507                     baseClass = None
0508                 if baseClass is not None:
0509                     try:
0510                         if isinstance(baseClass,self._sipSymbolData.Typedef):
0511                             classHierarchy = self._sipSymbolData.lookupType(baseClass.argumentType(),baseClass).classHierarchy()
0512                         else:
0513                             classHierarchy = baseClass.classHierarchy()
0514                     except KeyError:
0515                         print("KeyError: Look up error on '"+baseClass.name()+"'")
0516                         classHierarchy = []
0517 
0518                     bases.append(' &#x2192; '.join([self.formatType(x.fqPythonName(),sipClass) for x in classHierarchy]))
0519             
0520             baseClasses = 'Inherits: '+ (','.join(bases)) + '<br />'
0521         else:
0522             baseClasses = ''
0523         
0524         subClassSet = subclassMapping.get(sipClass,None)
0525         if subClassSet is not None:
0526             subClassList = list(subClassSet)
0527             subClassList.sort(key=nameKey)
0528             subClasses = 'Subclasses: ' + (', '.join ( [self.formatType(x.fqName(),sipClass) for x in subClassList] )) + '<br />'
0529         else:
0530             subClasses = ''
0531         
0532         parentScope = sipClass.parentScope()
0533         if parentScope.name() is not None:
0534             namespace = "Namespace: " + self.linkType(parentScope.fqName(),None) + "<br />"
0535         else:
0536             namespace = ""
0537         
0538         page.write (classHeader %
0539             {'classname': pyName(sipClass),
0540             'abstract': abstract,
0541             'modulename': self._module,
0542             'namespace': namespace,
0543             'baseclasses': baseClasses,
0544             'subclasses': subClasses,
0545             'description': self.formatDoxygen(classComment)} )
0546             
0547         page.write("""<table border="0" cellpadding="0" cellspacing="0">""")
0548         
0549         # enums
0550         page.write(self.writeEnums(enumList))
0551         
0552         page.write(self.writeVariables(variableList))
0553         
0554         count = [0]
0555         def SignalFilter(obj):
0556             if obj.access() is self._sipSymbolData.ACCESS_SIGNALS:
0557                 count[0] += 1
0558                 return True
0559             else:
0560                 return False
0561         s = self.writeMethodIndex(methodList, SignalFilter, "Signals", False)
0562         if count[0]!=0:
0563             page.write(s)
0564         
0565         count = [0]
0566         def MethodFilter(obj):
0567             if (not obj.storage() == 'static') and not (obj.access() is self._sipSymbolData.ACCESS_SIGNALS):
0568                 count[0] += 1
0569                 return True
0570             else:
0571                 return False
0572         s = self.writeMethodIndex(methodList, MethodFilter, "Methods")
0573         if count[0]!=0:
0574             page.write(s)
0575         
0576         count = [0]
0577         def StaticFilter(obj):
0578             if obj.storage() == 'static' and not (obj.access() is self._sipSymbolData.ACCESS_SIGNALS):
0579                 count[0] += 1
0580                 return True
0581             else:
0582                 return False
0583         s = self.writeMethodIndex(methodList, StaticFilter, "Static Methods", False)
0584         if count[0]!=0:
0585             page.write(s)
0586 
0587         page.write("</table>\n")
0588         
0589         
0590         # signals
0591         self.writeMethodDetails(page, [x for x in methodList if SignalFilter(x)], commentMap,
0592             "Signal Documentation",True)
0593         
0594         # methods
0595         self.writeMethodDetails(page, [x for x in methodList if MethodFilter(x)], commentMap)
0596 
0597         # Static methods
0598         self.writeMethodDetails(page, [x for x in methodList if StaticFilter(x)], commentMap,
0599             "Static Method Documentation",True)
0600 
0601         # variables
0602         self.writeVariableDetails(page, variableList, commentMap)
0603         
0604         # enum detail
0605         self.writeEnumDetail(page, enumList, commentMap)#, flags)
0606     
0607         # footer
0608         page.write(htmlFooter % {'path': '../'})
0609         page.close()
0610         
0611     # @accepts(list, types.FunctionType, str, useSelf=bool)
0612     # @returns(str)
0613     def writeMethodIndex(self, methodList, methodFilter, title, useSelf=True):
0614         result = []
0615         result.append("""<tr><td colspan="2"><br><h2>""")
0616         result.append(title)
0617         result.append("""</h2></td></tr>\n""")
0618         
0619         for obj in methodList:
0620             if not methodFilter(obj):
0621                 continue
0622                 
0623             retList = []
0624             if obj.return_():
0625                 retList.append(self.formatArgument(obj.return_(),obj))
0626                 
0627             for arg in obj.arguments():
0628                 if 'Out' in arg.annotations():
0629                     if arg.name():
0630                         retList.append ('%s %s' % (self.formatType(arg.argumentType(),obj), arg.name()))
0631                     else:
0632                         retList.append (self.formatType(arg.argumentType(),obj))
0633             
0634             if retList:
0635                 ret = ', '.join ( (item for item in retList if item!='') )
0636             else:
0637                 ret = ''
0638                 
0639 #            param = ''
0640 #            if 'pure' in obj.attributes.functionQualifier:
0641 #                param += '<b style="color : #00aa00">pure virtual</b>'
0642 #            if obj.ignore or obj.access == 'private':
0643 #                param = '<i style="color : #aa0000">Not implemented</i>'
0644 #            
0645 #            if param != '':
0646 #                print(obj.name + "~> "+param)
0647                 
0648             if isinstance(obj, self._sipSymbolData.Constructor):
0649                 memname = "__init__"
0650                 ret = ""
0651             else:
0652                 memname = pyName(obj)
0653             memname = '<a class="el" href="#' + linkId(obj) + '">' + memname + '</a>'
0654                 
0655             result.append('<tr><td class="memItemLeft" nowrap align="right" valign="top">' + ret +'&nbsp;</td><td class="memItemRight" valign="bottom">' + memname + ' (')
0656             
0657             comma = ""
0658             if useSelf:
0659                 result.append('self')
0660                 if len(obj.arguments())!=0:
0661                     comma = ", "
0662 
0663             i = 0
0664             for a in obj.arguments():
0665                 if 'In' in a.annotations() or not 'Out' in a.annotations():
0666                     paramtype = self.formatType(a.argumentType(),obj)
0667                     paramname = a.name()
0668                     if paramname is None:
0669                         paramname = 'a%i' % i
0670                     if a.defaultValue() is not None:
0671                         paramname += "=" + a.defaultValue().replace ('::', '.')
0672                         
0673                     result.append(comma + paramtype + " " + paramname)
0674                     comma = ", "
0675                     i += 1
0676             result.append(')</td></tr>\n')
0677         return "".join(result)
0678         
0679     # @accepts(file, list, dict, title=str, functions=bool)
0680     def writeMethodDetails(self, page, methodList, commentMap, title="Method Documentation", functions=False):
0681         if not methodList:
0682             return
0683             
0684         page.write('<hr><h2>' + title + '</h2>')
0685         for obj in methodList:
0686             comment = commentMap.setdefault(self.commentMapKey(obj),"")
0687             self.writeMethodDetail(page, obj, comment, function=functions)
0688         
0689     # @accepts(file, one_of(sipsymboldata.SymbolData.Function,sipsymboldata.SymbolData.Constructor), str, function=bool)
0690     def writeMethodDetail(self, page, obj, comment, function=False):
0691         retList = []
0692         if obj.return_():
0693             retList.append(self.formatArgument(obj.return_(),obj))
0694             
0695         for arg in obj.arguments():
0696             if 'Out' in arg.annotations():
0697                 if arg.name():
0698                     retList.append ('%s %s' % (self.formatArgument(arg,obj), arg.name()))
0699                 else:
0700                     retList.append (self.formatArgument(arg,obj))
0701         
0702         if retList:
0703             ret = ', '.join ( (item for item in retList if item!='') )
0704         else:
0705             ret = ''
0706 
0707 #            param = ''
0708 #            if obj.ignore or obj.access == 'private':
0709 #                param = '<i style="color : #aa0000">Not implemented</i>'
0710         #i = 0
0711         #for arg in obj.arguments:
0712         #    if not arg.name():
0713         #        arg.name = 'a%i' % i
0714         #        i += 1
0715             
0716         args = """<a class="anchor" name=\"""" + linkId(obj) + """"></a>
0717 <div class="memitem">
0718 <div class="memproto">
0719 <table class="memname">"""
0720         if isinstance(obj, self._sipSymbolData.Constructor):
0721             memname = "__init__"
0722             ret = ""
0723         else:
0724             memname = ret + " " + pyName(obj)
0725         
0726         filteredArguments = [a for a in obj.arguments() if 'In' in a.annotations() or not 'Out' in a.annotations()]
0727         
0728         if not filteredArguments:
0729             selfarg = '<em>self</em>&nbsp;' if 'static' not in obj.qualifier() and not function else ''
0730             args += """<tr>
0731 <td class="memname">%s</td>
0732 <td>(</td>
0733 <td class="paramtype">&nbsp;</td>
0734 <td class="paramname">%s)</td>
0735 <td width="100%%"> </td>
0736 </tr>
0737 """ % (memname,selfarg)
0738         
0739         else:
0740             bracket = "("
0741             if not function:
0742                 args += """<tr>
0743 <td class="memname">%s</td>
0744 <td>%s</td>
0745 <td class="paramtype">&nbsp;<em>self</em>, </td>
0746 <td class="paramname"></td>
0747 </tr>""" % (memname,bracket)
0748                 memname = ""
0749                 bracket = ""
0750             
0751             i = 0
0752             for a in filteredArguments:
0753                 paramtype = self.formatArgument(a,obj)
0754                 paramname = a.name()
0755                 if paramname is None:
0756                     paramname = 'a%i' % i
0757                 if a.defaultValue() is not None:
0758                     paramname += "=" + a.defaultValue().replace ('::', '.')
0759                     
0760                 comma = ", " if a is not filteredArguments[-1] else ""
0761             
0762                 args += """<tr>
0763 <td class="memname">%s</td>
0764 <td>%s</td>
0765 <td class="paramtype">%s&nbsp;</td>
0766 <td class="paramname"><em>%s</em>%s</td>
0767 </tr>
0768 """ % (memname,bracket,paramtype,paramname,comma)
0769                 memname = ""
0770                 bracket = ""
0771                 i += 1
0772             args += """<tr>
0773 <td></td>
0774 <td>)</td>
0775 <td></td>
0776 <td></td>
0777 <td width="100%"> </td>
0778 </tr>"""
0779         args += """</table>
0780 </div>
0781 """
0782         page.write(args)
0783     
0784         page.write('<div class="memdoc">')
0785         if 'pure' in obj.qualifier():
0786             page.write("""<dl compact><dt><b>Abstract method:</b></dt><dd>""")
0787             page.write("This method is abstract and can be overridden but not called directly.")
0788             page.write("""</dd></dl>""")
0789 
0790         page.write(self.formatDoxygen(comment))
0791         if obj.access() is self._sipSymbolData.ACCESS_SIGNALS:
0792             args = [arg.argumentType() for arg in obj.arguments()]
0793             page.write("""<dl compact><dt><b>Signal syntax:</b></dt><dd>""")
0794             page.write("""<code>QObject.connect(source, SIGNAL("%s(%s)"), target_slot)</code>""" % (pyName(obj), ', '.join (args)))
0795             page.write("""</dd></dl>""")
0796         
0797         page.write('</div></div>')
0798 
0799     # @accepts(list)
0800     # @returns(str)
0801     def writeEnums(self, enumList): #:, flags = None):
0802         if len(enumList)==0:
0803             return ""
0804             
0805         result = []
0806         result.append("""<tr><td colspan="2"><br><h2>Enumerations</h2></td></tr>\n""")
0807 
0808         for obj in enumList:
0809             typeSafe = None
0810             if not obj.name():
0811                 objName = "&lt;anonymous&gt;"
0812             else:
0813                 #if flags and obj.name() in flags:
0814                 #    typeSafe = flags [obj.name()]
0815                 objName = obj.name()
0816            
0817             result.append('<tr><td class="memItemLeft" nowrap align="right" valign="top"><a class="el" href="#' + linkId(obj) + '">' + objName +'</a>&nbsp;</td><td class="memItemRight" valign="bottom">{&nbsp;')
0818                
0819             result.append(", ".join( [x.name() for x in obj] ))
0820                 
0821             if typeSafe:
0822                 result.append('&nbsp;}<br><i>Typesafe wrapper:</i> %s</td></tr>\n' % (typeSafe))
0823             else:
0824                 result.append("&nbsp;}</td></tr>\n")
0825 
0826         return "".join(result)
0827 
0828     # @accepts(list)
0829     # @returns(str)
0830     def writeVariables(self, variableList):
0831         if not variableList:
0832             return ""
0833         result = []
0834         result.append("""<tr><td colspan="2"><br><h2>""")
0835         result.append("Attributes")
0836         result.append("""</h2></td></tr>\n""")
0837         
0838         for obj in variableList:
0839             result.append('<tr><td class="memItemLeft" nowrap align="right" valign="top">' + self.formatArgument(obj.argument(),obj) + '&nbsp;</td><td class="memItemRight" valign="bottom"><a class="el" href="#var' + linkId(obj) + '">' + obj.name() +'</a></td></tr>')
0840         return "".join(result)
0841 
0842     # @accepts(file,list,dict)
0843     def writeVariableDetails(self, page, variableList, commentMap):
0844         if not variableList:
0845             return
0846         
0847         page.write('<hr><h2>Attribute Documentation</h2>')
0848 
0849         for obj in variableList:
0850             page.write("""<a class="anchor" name="var""" + linkId(obj) + """"></a>
0851 <div class="memitem">
0852 <div class="memproto">
0853 <table class="memname">
0854 <tr><td class="memname">""")
0855             page.write(self.formatArgument(obj.argument(),obj))
0856             page.write(" ")
0857             page.write(pyName(obj))
0858             page.write("""</td>
0859 </tr>
0860 </table>
0861 </div>
0862 <div class="memdoc">""")
0863             page.write(self.formatDoxygen(commentMap.setdefault(self.commentMapKey(obj),"")))
0864             page.write("""</div></div><p>""")            
0865 
0866     # @accepts(file,list,dict)
0867     def writeEnumDetail(self, page, enumList, commentMap): #flags = None):
0868         if not enumList:
0869             return
0870             
0871         page.write('<hr><h2>Enumeration Documentation</h2>')
0872         
0873         for obj in enumList:
0874             page.write("""<a class="anchor" name=\"""" + linkId(obj) + """"></a>
0875 <div class="memitem">
0876 <div class="memproto">
0877 <table class="memname">
0878 <tr><td class="memname">""")
0879             typeSafe = None
0880             if not obj.name():
0881                 name = 'anonymous'
0882             else:
0883                 #if flags and obj.name() in flags:
0884                 #    typeSafe = flags [obj.name()]
0885                 name = obj.name()
0886             page.write(name)
0887             page.write("""</td>
0888 </tr>
0889 </table>
0890 </div>
0891 <div class="memdoc">""")
0892             page.write(self.formatDoxygen(commentMap.setdefault(self.commentMapKey(obj),"")))
0893             
0894             if typeSafe:
0895                 page.write("""<dl compact><dt><b>Note:</b></dt><dd>""")
0896                 page.write("It is necessary to wrap members of this enumeration in a <code>" + typeSafe + "</code> instance when passing them to a method as group of flags. ")
0897                 if len(obj.enumerators())>=2:
0898                     page.write("For example: <code>" + typeSafe + "( " + obj[0].name() + " | " + obj[1].name() + ")</code>")
0899                 page.write("""</dd></dl>""")
0900                 
0901             page.write("""<dl compact><dt><b>Enumerator: </b></dt><dd>
0902 <table border="0" cellspacing="2" cellpadding="0">""")
0903 
0904             if obj.name() is not None:
0905                 try:
0906                     cppEnum = self._symbolData.lookupType(obj.name(),obj)
0907                     print("Enum type is :" + repr(cppEnum));
0908                     obj = cppEnum
0909                 except KeyError:
0910                     print("Couldn't find enum:"+obj.name())
0911 
0912             for e in obj:
0913                 if isinstance(e,self._symbolData.Enumerator):
0914                     #docLine = self.formatDoxygen(e.doc).strip().replace('/', ' ')
0915                     docLine = ''
0916                     if e.value():
0917                         val = '=&nbsp;' + str(e.value())
0918                     else:
0919                         val = ''
0920 
0921                     page.write('<tr><td valign="top"><em>')
0922                     page.write(e.name())
0923                     page.write('</em>&nbsp;')
0924                     page.write(val)
0925                     page.write(docLine)
0926                     page.write('</td><td>')
0927             page.write("""</table>
0928 </dl>
0929 </div></div><p>""")
0930 
0931     # @accepts(list)
0932     def writeModuleIndexPage(self,sipScopes):
0933         #nsNames = self.namespaces.keys()
0934         #nsNames.sort()
0935         nsList = list(set( (item.name() for scope in sipScopes for item in scope if isinstance(item,self._sipSymbolData.Namespace) and not item.ignore()) ))
0936         nsList.append('global')
0937         nsList.sort()
0938         
0939         if self._mainDocs:
0940             mainDocs = FetchDocs(self._mainDocs)
0941         else:
0942             mainDocs = ''
0943 
0944         page = open(os.path.join(self._docsOutputDirectory, 'index.html'), 'w')
0945         page.write(htmlHeader % {'title': ('Module %s' % self._module), 'path': '../'})
0946         page.write("<h1>%s Module</h1>\n" % self._module)
0947         page.write("<hr>")
0948         page.write(self.formatDoxygen(mainDocs))
0949 
0950         self.writeNSNamespacesIndex(page, nsList)
0951         
0952         classList = []
0953         for scope in sipScopes:
0954             classList.extend(self._findAllInstance(scope,self._sipSymbolData.SipClass))
0955         #print(repr(classList))
0956         classList = list( set (class_ for class_ in classList if not class_.ignore()) )
0957         def nameKey(a): return a.name()
0958         classList.sort(key=nameKey)
0959         
0960         self.writeNSClassIndex(page, classList)
0961 
0962         page.write(htmlFooter % {'path': '../'})
0963         page.close()
0964 
0965     # @accepts(list,list)
0966     def writeNamespaces(self,sipScopes,cppScopes):
0967         # Figure out what namespace we have.
0968         namespaceCollector = {} # namespace FQN -> list of namespace objects
0969         for scope in sipScopes:
0970             for item in scope:
0971                 if isinstance(item, self._sipSymbolData.Namespace):
0972                     nsList = namespaceCollector.setdefault(item.fqName(),[])
0973                     nsList.append(item)
0974 
0975         #print("nsList: " + repr(nsList))
0976 
0977         self.writeNamespacePage("global",sipScopes,cppScopes)
0978 
0979         for fqName,ns in namespaceCollector.items():
0980             self.writeNamespacePage(fqName,ns,cppScopes)
0981 
0982     # @accepts(str,list,list)
0983     def writeNamespacePage(self,nsName,nsList,cppScopes):
0984 #        print(repr(cppScopes))
0985         # create namespace page and add header
0986         nspage = open(os.path.join(self._docsOutputDirectory, "%s.html" % nsName), 'w')
0987         nspage.write(htmlHeader % {'title': nsName, 'path': '../'})
0988 
0989         importcode = "from %s.%s import *" % (self._module,nsName)
0990         if nsName=='global':
0991             importcode = "from " + self._module + " import *"
0992 
0993         description = ""
0994         if nsName!='global':
0995             for cppNS in self._symbolData.lookupNamespace(nsName):
0996                 lastComment = None
0997                 for item in cppNS.parentScope():
0998                     if isinstance(item,self._symbolData.Comment):
0999                         lastComment = item.value()
1000                     elif isinstance(item,self._symbolData.Namespace) and item.fqName()==nsName and lastComment is not None:
1001                         description = lastComment
1002                         lastComment = None
1003 
1004         nspage.write(namespaceHeader % {'namespacename': nsName,
1005             'modulename': self._module,
1006             'import': importcode,
1007             'description': self.formatDoxygen(description)})
1008 
1009         def nameKey(a): return a.name()
1010         classList = []
1011         for scope in nsList:
1012             classList.extend(self._findAllInstance(scope,self._sipSymbolData.SipClass))
1013         classList.sort(key=nameKey)
1014 
1015         # Class index (to other pages)
1016         self.writeNSClassIndex(nspage, classList)
1017 
1018         # Intra-page index
1019         nspage.write("""<table border="0" cellpadding="0" cellspacing="0">""")
1020 
1021         # enums
1022         enumList = [item for scope in nsList for item in scope
1023                 if isinstance(item, self._sipSymbolData.Enum)]
1024         enumList.sort(key=nameKey)
1025         nspage.write(self.writeEnums(enumList))
1026 
1027         # Functions
1028         functionList = [item for scope in nsList for item in scope
1029                 if isinstance(item, self._sipSymbolData.Function)]
1030         functionList.sort(key=nameKey)
1031         def all(x): return True
1032         if len(functionList)!=0:
1033             nspage.write(self.writeMethodIndex(functionList, all, "Functions", False))
1034 
1035         # Variables
1036         variableList = [item for scope in nsList for item in scope
1037                 if isinstance(item, self._sipSymbolData.Variable)]
1038         variableList.sort(key=nameKey)
1039         nspage.write(self.writeVariables(variableList))
1040 
1041         nspage.write("</table>\n")
1042 
1043         # Build the comment map.
1044         commentMap ={}
1045         scopes = self._symbolData.lookupNamespace(nsName) if nsName!='global' else cppScopes
1046         for cppNS in scopes:
1047             lastComment = None
1048             for item in cppNS:
1049                 if isinstance(item,self._symbolData.Comment):
1050                     lastComment = item.value()
1051                 elif (isinstance(item,self._symbolData.Function) or isinstance(item,self._symbolData.Variable)
1052                         or isinstance(item,self._symbolData.Enum)) and lastComment is not None:
1053                     commentMap[self.commentMapKey(item)] = lastComment
1054                     lastComment = None
1055 
1056         # enum detail
1057         self.writeEnumDetail(nspage, enumList, commentMap)
1058 
1059         # Functions.
1060         self.writeMethodDetails(nspage, functionList, commentMap, "Function Documentation", True)
1061 
1062         # variables
1063         self.writeVariableDetails(nspage, variableList, commentMap)
1064 
1065         # footer
1066         nspage.write (htmlFooter % {'path': '../'})
1067         nspage.close ()
1068 
1069     # @accepts(file,list)
1070     def writeNSNamespacesIndex(self, page, namespaces):
1071         if not namespaces:
1072             return
1073 
1074         page.write("<h2>Namespaces</h2>\n")
1075 
1076         def format(obj):
1077             indexstring = obj
1078             if indexstring[0].upper()=='K':
1079                 indexstring = indexstring[1:]
1080             name = obj
1081             if name=='global':
1082                 name = '<i>' + name + '</i>'
1083             cellcontents = '<a class="el" href="%s.html">%s</a>&nbsp;&nbsp;&nbsp;' % (obj,name)
1084             return (indexstring,cellcontents)
1085             
1086         page.write(FormatTable(namespaces, format, key))
1087         
1088     # @accepts(file,list)
1089     def writeNSClassIndex(self, page, rawClassList):
1090         if not rawClassList:
1091             return
1092 
1093         page.write ("<h2>Class Index</h2>\n")
1094 
1095         def format(obj):
1096             if not obj.ignore():
1097                 classname = indexstring = obj.name()
1098                 if indexstring[0].upper()=='K':
1099                     indexstring = indexstring[1:]
1100                 parentScope = obj.parentScope()
1101                 scope = parentScope.fqPythonName()
1102                 if not scope:
1103                     cellcontents = '<a class="el" href="%s.html">%s</a>&nbsp;&nbsp;&nbsp;' \
1104                                     % (classname,classname)
1105                 else:
1106                     cellcontents = '<a class="el" href="%s.%s.html">%s</a>&nbsp;(<a class="el" href="%s.html">%s</a>)&nbsp;&nbsp;&nbsp;' \
1107                                     % (scope,classname,classname,scope,scope)
1108 
1109                 return (indexstring,cellcontents)
1110             else:
1111                 return None
1112         
1113         def key_obj(a):
1114             return a.name()
1115         page.write(FormatTable(rawClassList, format, key_obj))
1116 
1117     @staticmethod
1118     def WriteAllClasses(htmldst, nsNames, classNames):
1119 
1120 #        self.mainIndex = open(os.path.join(htmldst, 'index.txt'), 'r')
1121 #        for line in self.mainIndex:
1122 #            specifier, name = line.split (':')
1123 #            name = name.strip ()
1124 #            if specifier == 'm':
1125 #                module = name
1126 #            elif specifier == 'n':
1127 #                if name in ['global', 'Sonnet']:
1128 #                    nsNames.append ((module, name, '%s - %s' % (name, module)))
1129 #                else:
1130 #                    nsNames.append ((module, name, name))
1131 #            elif specifier == 'c':
1132 #                classNames.append ((module, name, name))
1133 
1134         def key(x): return x[1]
1135         nsNames.sort(key=key)
1136         classNames.sort(key=key)
1137 
1138         page = open(os.path.join(htmldst, 'allclasses.html'), 'w')
1139         page.write(htmlHeader % {'title': 'PyKDE Namespace and Class Index', 'path': ''})
1140 
1141         # Namespaces
1142         page.write("<p>\n<h2>All Namespaces</h2>\n")
1143 
1144         def extract_name(x):
1145             nameparts = x.split('.')
1146             indexstring = nameparts[-1].lower()
1147             if indexstring.startswith('k'):
1148                 indexstring = indexstring[1:]
1149             return indexstring
1150 
1151         def format(t):
1152             nameparts = t[1].split('.')
1153             indexstring = nameparts[-1].lower()
1154             if indexstring.startswith('k'):
1155                 indexstring = indexstring[1:]
1156 
1157             name = nameparts[-1] + '&nbsp;(' + ('.'.join( [t[0]] + nameparts[:-1] )) + ')'
1158 
1159             cellcontents = '<a href="' + t[0] + '/' + t[1] + '.html">' + name + '</a>'
1160             return (indexstring,cellcontents)
1161         def t_key(a):
1162             return key(extract_name(a[1]))
1163         page.write(FormatTable(nsNames, format, t_key, columns=2))
1164 
1165         # Classes
1166         page.write("<p>\n<h2>All Classes</h2>\n")
1167         page.write(FormatTable(classNames, format, t_key, columns=2))
1168 
1169         page.write(htmlFooter % {'path': ''})
1170         page.close()
1171 
1172     @staticmethod
1173     def WriteMainPage(htmldst):
1174         page = open(os.path.join(htmldst, 'modules.html'), 'w')
1175         page.write(htmlHeader % {'title': 'KDE 5 PyKDE API Reference', 'path': ''})
1176 
1177         page.write("""<p>
1178 <h2>KDE 5.0 PyKDE API Reference</h2>
1179 </p>
1180 <p>
1181 This is the reference to the KDE API as it appears to Python programs using PyKDE. This is just
1182 reference material, additional information about developing KDE applications using Python and the KDE API
1183 in general can be found on <a href="http://techbase.kde.org/">Techbase</a> and in the
1184 <a href="http://techbase.kde.org/Development/Languages/Python">Python section</a>. The reference for
1185 <a href="http://pyqt.sourceforge.net/Docs/PyQt5/class_reference.html">PyQt 5 is here</a>.
1186 </p>
1187 
1188 <p>
1189 This documentation is automatically generated from the original C++ headers and embedded documentation.
1190 The classes, methods, functions, namespaces etc are described as they appear to Python code and
1191 also include addition type information about expected parameters and return types. Note that
1192 code fragments in the documentation have not been translated from C++ to Python.
1193 </p>
1194 """)
1195 
1196         page.write(htmlFooter % {'path': ''})
1197 
1198         page.close()
1199 
1200     # @accepts(sipsymboldata.SymbolData.Argument,sipsymboldata.SymbolData.Entity)
1201     # @returns(str)
1202     def formatArgument(self, arg, context):
1203         #print("argument: " + arg.argumentType())
1204         return self.formatType(arg.argumentType(),context)
1205 
1206     # formatType()
1207     # rObj - string, type name, may contain C++ '&' and '*' characters.
1208     # @accepts(str,sipsymboldata.SymbolData.Entity)
1209     # @returns(str)
1210     def formatType(self, ret, context):
1211         r = ''
1212         if not ret or ret == 'void':
1213             return ''
1214             
1215         for i in range(len(ret)):
1216             if not ret[i] in ('*', '&'):
1217                 r += ret[i]
1218 
1219         if ret=="char":
1220             ret = "QString"
1221         
1222         #if rObj.parentScope():
1223         #    r = '.'.join ([rObj.parentScope().fqName(), r])
1224         r = self.linkType(r, context)
1225         return r
1226 
1227     CppPythonTypeMapping = {
1228         'bool': 'bool',
1229         'int': 'int',
1230         'float': 'float',
1231         'long': 'long',
1232         'short': 'int',
1233         'qint32': 'int',
1234         'quint16': 'int',
1235         'qint64': 'long',
1236         'quint32': 'long',
1237         'uint': 'long',
1238         'qlonglong': 'long',
1239         'qulonglong': 'long',
1240         'double': 'float',
1241         'unsigned int': 'long',
1242         'unsigned long': 'long',
1243         'qreal': 'float',
1244         'unsigned short': 'int',
1245         'long long': 'long',
1246         'unsigned long long': 'long'
1247     }
1248         
1249     def linkType(self, typeName, context):
1250         if typeName.startswith("const "):
1251             typeName = typeName[6:]
1252         while typeName.endswith('*') or typeName.endswith('&'):
1253             typeName = typeName[:-1]
1254 
1255         if typeName == '...':
1256             return typeName
1257 
1258         if typeName == 'char':
1259             typeName = 'QString'
1260 
1261         # Handle QList<...>
1262         if typeName.startswith("QList<"):
1263             return '[' + self.linkType(typeName[6:-1],context) + ']'
1264             
1265         if typeName.startswith("QFlags<"):
1266             return 'QFlags&lt;' + self.linkType(typeName[7:-1],context) + '&gt;'
1267 
1268         if typeName.startswith("QHash<"):
1269             pivot = typeName.index(",")
1270             leftType = self.linkType(typeName[6:pivot],context)
1271             rightType = self.linkType(typeName[pivot+1:-1],context)
1272             return '{' + leftType + ':'+ rightType + '}'
1273 
1274         if typeName.startswith("QMap<"):
1275             pivot = typeName.index(",")
1276             leftType = self.linkType(typeName[5:pivot],context)
1277             rightType = self.linkType(typeName[pivot+1:-1],context)
1278             return '{' + leftType + ':'+ rightType + '}'
1279             
1280         if typeName.startswith("QPair<"):
1281             pivot = typeName.index(",")
1282             leftType = self.linkType(typeName[6:pivot],context)
1283             rightType = self.linkType(typeName[pivot+1:-1],context)
1284             return '(' + leftType + ','+ rightType + ')'
1285 
1286         if typeName.startswith("Qt.") or typeName.startswith("Qt::"):
1287             return '<a href="http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qt.html">' + typeName + '</a>'
1288         
1289         pyType = self.CppPythonTypeMapping.get(typeName)
1290         if pyType is not None:
1291             return pyType
1292         
1293         fileName = typeName.replace('::','.')
1294         try:
1295             typeObject = self._sipSymbolData.lookupType(typeName.replace('.','::'),context)
1296             
1297             if isinstance(typeObject,self._sipSymbolData.Typedef) and not (typeObject.argumentType() is not None and typeObject.argumentType().startswith('QFlags')) and typeObject.argumentType() is not None:
1298                 return self.linkType(typeObject.argumentType(),context)
1299 
1300             #print("typeObject: "+repr(typeObject))
1301             #print("typeObject.topScope():"+repr(typeObject.topScope()))
1302             typeModule = typeObject.topScope().module()
1303             #print("typeObject.topScope().module(): " + repr(typeModule))
1304             if typeModule.startswith("PyQt"):
1305                 return '<a href="http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/' + typeName.lower().replace('::','-') + '.html">' + typeObject.fqPythonName() + '</a>'
1306             elif typeModule.startswith("PyKDE4"):
1307                 if isinstance(typeObject,self._sipSymbolData.Enum):
1308                     parentScope = typeObject.parentScope()
1309                     if isinstance(parentScope,self._sipSymbolData.TopLevelScope):
1310                         parentPythonName = "global"
1311                     else:
1312                         parentPythonName = parentScope.fqPythonName()
1313                     fileName = parentPythonName + ".html#" + linkId(typeObject)
1314                     
1315                 elif isinstance(typeObject,self._sipSymbolData.Typedef):
1316                     parentScope = typeObject.parentScope()
1317                     if isinstance(parentScope,self._sipSymbolData.TopLevelScope):
1318                         parentPythonName = "global"
1319                     else:
1320                         parentPythonName = parentScope.fqPythonName()
1321                     fileName = parentPythonName + ".html"
1322                     
1323                 else:
1324                     fileName = typeObject.fqPythonName() + ".html"
1325                 return '<a href="../' + typeModule[7:] + '/' + fileName + '">' + typeObject.fqPythonName() + '</a>'
1326         except KeyError:
1327             print("KeyError: Couldn't resolve '"+typeName+"'")
1328 
1329         return typeName
1330         
1331     # @accepts(str)
1332     # @returns(str)
1333     def stripComment(self, text):
1334         parts = text.split('\n')
1335         parts[0] = parts[0].strip()
1336         if parts[0]=='/**':
1337             parts[0] = None
1338         parts[-1] = parts[-1].strip()
1339         if parts[-1]=='**/' or parts[-1]=='*/':
1340             parts[-1] = None
1341         
1342         # Transform for each line
1343         def clean(line):
1344             line = line.strip()
1345             if line.startswith('*'):
1346                 return line[1:]
1347             else:
1348                 return line
1349         return '\n'.join( (clean(line) for line in parts if line is not None) )
1350 
1351     # @accepts(str)
1352     # @returns(str)
1353     def formatDoxygen(self, text):
1354         if text:
1355             return reducer.do_txt(self.stripComment(text))
1356         else:
1357             return ''
1358 
1359 def AnnotationRule(methodTypeMatch,parameterTypeMatch,parameterNameMatch,annotations):
1360     return cpptosiptransformer.MethodAnnotationRule(methodTypeMatch,parameterTypeMatch,parameterNameMatch,annotations)
1361 
1362 def PySlotRule(className=None,namespaceName=None,arg1Name=None,arg2Name=None):
1363     return cpptosiptransformer.PySlotRule(className,namespaceName,arg1Name,arg2Name)
1364 
1365 
1366 def pyName(obj):
1367     for anno in obj.annotations():
1368         if anno.startswith("PyName"):
1369             return anno[7:]
1370     return obj.name()
1371 
1372 def linkId(obj):
1373     if obj.name() is not None:
1374         return obj.name()
1375     else:
1376         return 'obj'+str(id(obj))
1377 
1378 def isSipClassAbstract(sipClass):
1379     for anno in sipClass.annotations():
1380         if anno=="Abstract":
1381             return True
1382     return False
1383 
1384 def BuildSubclassMap(scopeList,symbolData):
1385     mapping = {}
1386     def processScope(scope):
1387         for item in scope:
1388             if isinstance(item,symbolData.SipClass):
1389                 class_ = item
1390                 for baseName in class_.bases():
1391                     try:
1392                         base = symbolData.lookupType(baseName,class_.parentScope())
1393                         if isinstance(base, symbolData.Typedef):
1394                             print("Warning: %s Skipping typedef base '%s'." % (class_.sourceLocation(),baseName))
1395                             continue
1396 
1397                         subClassList = mapping.setdefault(base,set())
1398                         subClassList.add(class_)
1399                     except KeyError:
1400                         print("Warning: %s Unrecognized type '%s' found when building subclass map." % (class_.sourceLocation(),baseName))
1401             elif isinstance(item,symbolData.Namespace):
1402                 processScope(item)
1403 
1404     for scope in scopeList:
1405         processScope(scope)
1406     return mapping
1407     
1408 def FetchDocs(filename):
1409     lex = cpplexer.CppLexer()
1410     
1411     with open(filename) as fhandle:
1412         data = fhandle.read()
1413         
1414     lex.input(data)
1415     while True:
1416         tok = lex.token()
1417         if not tok:
1418             break      # No more input
1419         if tok.type=='DOC':
1420             print(tok)
1421             return tok.value
1422     return ""
1423     
1424 def key(a):
1425     if a.startswith('K'):
1426         a = a[1:]
1427     return a.lower()
1428 
1429 def identity(x):
1430     return x
1431 
1432 # (indexstring,cellcontents) = cellFunc(obj)
1433 def FormatTable(rawObjectList, cellFunc, keyFunc=identity, columns=3):
1434     if not rawObjectList:
1435         return
1436 
1437     # Sort the class list with special treatment for the letter K.
1438     objectList = rawObjectList[:]
1439     objectList.sort(key=keyFunc)
1440 
1441     result = []
1442 
1443     cells = []
1444     letter = None
1445     for obj in objectList:
1446         tup = cellFunc(obj)
1447         if tup is None:
1448             continue
1449         indexstring, cellcontents = tup
1450 
1451         newletter = indexstring[0].upper()
1452         if newletter!=letter:
1453             letter = newletter
1454             cells.append('<a name="letter_'+letter+'">&nbsp;&nbsp;'+letter+'&nbsp;&nbsp;</a>')
1455         cells.append(cellcontents)
1456 
1457     # Write it out.
1458     result.append("""<table width="95%" align="center" border="0" cellpadding="0" cellspacing="0">
1459 <tbody>""")
1460 
1461     def cell(i): return cells[i] if i<len(cells) else ''
1462 
1463     section = int((len(cells)+columns-1)/columns)
1464     for i in range(section):
1465         result.append("<tr>")
1466         for j in range(columns):
1467             result.append("<td>" + cell(j*section + i) + "</td>")
1468         result.append("</tr>\n")
1469     result.append("</table>\n")
1470 
1471     return "".join(result)
1472 
1473 
1474 # name
1475 htmlHeader = """<?xml version="1.0" encoding="UTF-8"?>
1476 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
1477 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
1478 
1479 <head>
1480   <title>%(title)s</title>
1481   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
1482   <meta http-equiv="Content-Style-Type" content="text/css" />
1483   <link rel="stylesheet" type="text/css" href="%(path)scommon/doxygen.css" />
1484   <link rel="stylesheet" media="screen" type="text/css" title="KDE Colors" href="%(path)scommon/kde.css" />
1485 </head>
1486 <body>
1487 <div id="container">
1488 <div id="header">
1489   <div id="header_top">
1490     <div>
1491       <div>
1492         <img alt ="" src="%(path)scommon/top-kde.jpg"/>
1493         KDE 5.0 PyKDE API Reference
1494       </div>
1495     </div>
1496   </div>
1497   <div id="header_bottom">
1498     <div id="location">
1499       <ul>
1500         <li>KDE's Python API</li>
1501       </ul>
1502     </div>
1503 
1504     <div id="menu">
1505       <ul>
1506         <li><a href="%(path)smodules.html">Overview</a></li>
1507 <li><a href="http://techbase.kde.org/Development/Languages/Python">PyKDE Home</a></li>
1508 <li><a href="http://kde.org/family/">Sitemap</a></li>
1509 <li><a href="http://kde.org/contact/">Contact Us</a></li>
1510 </ul>
1511     </div>
1512   </div>
1513 </div>
1514 
1515 <div id="body_wrapper">
1516 <div id="body">
1517 <div id="right">
1518 <div class="content">
1519 <div id="main">
1520 <div class="clearer">&nbsp;</div>
1521 """
1522 # nothing
1523 htmlFooter = """
1524 </div>
1525 </div>
1526 </div>
1527 
1528 <div id="left">
1529 
1530 <div class="menu_box">
1531 <div class="nav_list">
1532 <ul>
1533 <li><a href="%(path)sallclasses.html">Full Index</a></li>
1534 </ul>
1535 </div>
1536 
1537 <a name="cp-menu" /><div class="menutitle"><div>
1538   <h2 id="cp-menu-project">Modules</h2>
1539 </div></div>
1540 <div class="nav_list">
1541 <ul>""" + (
1542 """<li><a href="%(path)skarchive/index.html">karchive</a></li>
1543 <li><a href="%(path)skcoreaddons/index.html">kcoreaddons</a></li>
1544 <li><a href="%(path)skguiaddons/index.html">kguiaddons</a></li>
1545 <li><a href="%(path)skitemmodels/index.html">kitemmodels</a></li>
1546 <li><a href="%(path)skitemviews/index.html">kitemviews</a></li>
1547 <li><a href="%(path)skplotting/index.html">kplotting</a></li>
1548 <li><a href="%(path)skwidgetsaddons/index.html">kwidgetsaddons</a></li>
1549 <li><a href="%(path)ssolid/index.html">solid</a></li>
1550 <li><a href="%(path)ssonnet/index.html">sonnet</a></li>
1551 """ if True else
1552 """<li><a href="%(path)smarble/index.html">marble</a></li>""") + \
1553 """
1554 </ul></div></div>
1555 
1556 </div>
1557 
1558 </div>
1559   <div class="clearer"/>
1560 </div>
1561 
1562 <div id="end_body"></div>
1563 </div>
1564 <div id="footer"><div id="footer_text">
1565 This documentation is maintained by <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;simon&#64;simonzone&#46;com">Simon Edwards</a>.<br />
1566         KDE<sup>&#174;</sup> and <a href="../images/kde_gear_black.png">the K Desktop Environment<sup>&#174;</sup> logo</a> are registered trademarks of <a href="http://ev.kde.org/" title="Homepage of the KDE non-profit Organization">KDE e.V.</a> |
1567         <a href="http://www.kde.org/contact/impressum.php">Legal</a>
1568     </div></div>
1569 </body>
1570 </html>
1571 """
1572 
1573 #nsName, module, nsName, text
1574 namespaceHeader = """
1575 <h1>%(namespacename)s Namespace Reference</h1>
1576 <code>%(import)s</code>
1577 <p>
1578 <h2>Detailed Description</h2>
1579 %(description)s
1580 """
1581 
1582 classListEntry = '<a href="%s.html">%s</a>'
1583 igclassListEntry = '<i style="color : #999999">%s</i>'
1584 
1585 # classname, abstract, modulename, namespace, classname, baseclasses, description
1586 classHeader = """
1587 <h1>%(classname)s Class Reference</h1>
1588 <code>from %(modulename)s import *</code>
1589 <p>
1590 %(baseclasses)s
1591 %(subclasses)s
1592 %(namespace)s
1593 <h2>Detailed Description</h2>
1594 %(abstract)s
1595 %(description)s
1596 """
1597 
1598 varEntry   = '<tr><td width="60%%" valign="top"><b>%s</b> <i>%s</i></td><td width="40%%" align="left">%s</td></tr>\n'