diff options
author | TizenOpenSource <tizenopensrc@samsung.com> | 2022-12-27 15:39:10 +0900 |
---|---|---|
committer | TizenOpenSource <tizenopensrc@samsung.com> | 2022-12-27 15:39:10 +0900 |
commit | cfd886868fa8595b045007a2ad673c18c5f222b3 (patch) | |
tree | 5d1b3cd3c098c0e5238e4a6a9be351a67f2d6bc1 /src | |
parent | 9cf4982ab5fc6d964e1a024ff91a72d1fee5dc00 (diff) | |
download | doxygen-cfd886868fa8595b045007a2ad673c18c5f222b3.tar.gz doxygen-cfd886868fa8595b045007a2ad673c18c5f222b3.tar.bz2 doxygen-cfd886868fa8595b045007a2ad673c18c5f222b3.zip |
Imported Upstream version 1.9.5upstream/1.9.5
Diffstat (limited to 'src')
177 files changed, 7992 insertions, 4313 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9f7e653..e9df895 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -112,27 +112,59 @@ set(LEX_FILES scanner lexscanner configimpl) +if (NOT depfile_supported) + # In case the DEPFILE possibility is not supported the complete list of lex include files for the dependency has to be used + set(LEX_INC_FILES) +endif() + # unfortunately ${LEX_FILES_H} and ${LEX_FILES_CPP} don't work in older versions of CMake (like 3.6.2) for add_library foreach(lex_file ${LEX_FILES}) set(LEX_FILES_H ${LEX_FILES_H} " " ${GENERATED_SRC}/${lex_file}.l.h CACHE INTERNAL "Stores generated files") set(LEX_FILES_CPP ${LEX_FILES_CPP} " " ${GENERATED_SRC}/${lex_file}.cpp CACHE INTERNAL "Stores generated files") + + if (depfile_supported) + add_custom_command( + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/pre_lex.py ${CMAKE_CURRENT_LIST_DIR}/${lex_file}.l ${GENERATED_SRC}/${lex_file}.l ${GENERATED_SRC}/${lex_file}.corr ${GENERATED_SRC}/${lex_file}.d ${CMAKE_CURRENT_LIST_DIR} + DEPENDS ${CMAKE_CURRENT_LIST_DIR}/pre_lex.py ${CMAKE_CURRENT_LIST_DIR}/${lex_file}.l + DEPFILE ${GENERATED_SRC}/${lex_file}.d + OUTPUT ${GENERATED_SRC}/${lex_file}.l ${GENERATED_SRC}/${lex_file}.corr ${GENERATED_SRC}/${lex_file}.d + ) + else() + add_custom_command( + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/pre_lex.py ${CMAKE_CURRENT_LIST_DIR}/${lex_file}.l ${GENERATED_SRC}/${lex_file}.l ${GENERATED_SRC}/${lex_file}.corr ${GENERATED_SRC}/${lex_file}.d ${CMAKE_CURRENT_LIST_DIR} + DEPENDS ${CMAKE_CURRENT_LIST_DIR}/pre_lex.py ${CMAKE_CURRENT_LIST_DIR}/${lex_file}.l ${LEX_INC_FILES} + OUTPUT ${GENERATED_SRC}/${lex_file}.l ${GENERATED_SRC}/${lex_file}.corr ${GENERATED_SRC}/${lex_file}.d + ) + endif() + set_source_files_properties(${GENERATED_SRC}/${lex_file}.l PROPERTIES GENERATED 1) + set_source_files_properties(${GENERATED_SRC}/${lex_file}.corr PROPERTIES GENERATED 1) + set_source_files_properties(${GENERATED_SRC}/${lex_file}.d PROPERTIES GENERATED 1) + add_custom_command( - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/scan_states.py ${CMAKE_CURRENT_LIST_DIR}/${lex_file}.l > ${GENERATED_SRC}/${lex_file}.l.h - DEPENDS ${CMAKE_CURRENT_LIST_DIR}/scan_states.py ${CMAKE_CURRENT_LIST_DIR}/${lex_file}.l + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/scan_states.py ${GENERATED_SRC}/${lex_file}.l > ${GENERATED_SRC}/${lex_file}.l.h + DEPENDS ${CMAKE_CURRENT_LIST_DIR}/scan_states.py ${GENERATED_SRC}/${lex_file}.l OUTPUT ${GENERATED_SRC}/${lex_file}.l.h ) set_source_files_properties(${GENERATED_SRC}/${lex_file}.l.h PROPERTIES GENERATED 1) - # for code coverage we need the flex sources in the build src directory - add_custom_command( - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/src/${lex_file}.l ${PROJECT_BINARY_DIR}/src/${lex_file}.l - DEPENDS ${PROJECT_SOURCE_DIR}/src/${lex_file}.l - OUTPUT ${PROJECT_BINARY_DIR}/src/${lex_file}.l - ) + + if(enable_coverage) + # for code coverage we need the flex sources in the build src directory + add_custom_command( + COMMAND ${CMAKE_COMMAND} -E copy ${GENERATED_SRC}/${lex_file}.l ${PROJECT_BINARY_DIR}/src/${lex_file}.l + DEPENDS ${GENERATED_SRC}/${lex_file}.l + OUTPUT ${PROJECT_BINARY_DIR}/src/${lex_file}.l + ) + endif() FLEX_TARGET(${lex_file} - ${CMAKE_CURRENT_LIST_DIR}/${lex_file}.l - ${GENERATED_SRC}/${lex_file}.cpp + ${GENERATED_SRC}/${lex_file}.l + ${GENERATED_SRC}/${lex_file}_intermediate.cpp COMPILE_FLAGS "${LEX_FLAGS}") + add_custom_command( + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/post_lex.py ${GENERATED_SRC}/${lex_file}_intermediate.cpp ${GENERATED_SRC}/${lex_file}.cpp ${GENERATED_SRC}/${lex_file}.corr ${CMAKE_CURRENT_LIST_DIR}/${lex_file}.l ${GENERATED_SRC}/${lex_file}.l + DEPENDS ${CMAKE_CURRENT_LIST_DIR}/post_lex.py ${GENERATED_SRC}/${lex_file}_intermediate.cpp ${GENERATED_SRC}/${lex_file}.corr + OUTPUT ${GENERATED_SRC}/${lex_file}.cpp + ) endforeach() @@ -141,11 +173,13 @@ BISON_TARGET(constexp ${GENERATED_SRC}/ce_parse.cpp COMPILE_FLAGS "${YACC_FLAGS}") -add_custom_command( - COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/src/constexp.y ${PROJECT_BINARY_DIR}/src - DEPENDS ${PROJECT_SOURCE_DIR}/src/constexp.y - OUTPUT ${PROJECT_BINARY_DIR}/src/constexp.y -) +if(enable_coverage) + add_custom_command( + COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/src/constexp.y ${PROJECT_BINARY_DIR}/src + DEPENDS ${PROJECT_SOURCE_DIR}/src/constexp.y + OUTPUT ${PROJECT_BINARY_DIR}/src/constexp.y + ) +endif() add_library(doxycfg STATIC ${GENERATED_SRC}/configvalues.h @@ -218,6 +252,7 @@ add_library(doxymain STATIC condparser.cpp context.cpp cppvalue.cpp + datetime.cpp defgen.cpp definition.cpp dia.cpp diff --git a/src/cache.h b/src/cache.h index 0ff3092..e218eb2 100644 --- a/src/cache.h +++ b/src/cache.h @@ -19,6 +19,7 @@ #include <list> #include <unordered_map> #include <mutex> +#include <utility> #include <ctype.h> /*! Fixed size cache for value type V using keys of type K. diff --git a/src/cite.cpp b/src/cite.cpp index 611a4da..a20de12 100644 --- a/src/cite.cpp +++ b/src/cite.cpp @@ -32,6 +32,13 @@ const char *bibTmpFile = "bibTmpFile_"; const char *bibTmpDir = "bibTmpDir/"; +static QCString getBibFile(const QCString &inFile) +{ + QCString name = inFile; + if (!name.isEmpty() && !name.endsWith(".bib")) name+=".bib"; + return name; +} + class CiteInfoImpl : public CiteInfo { public: @@ -202,8 +209,7 @@ void CitationManager::generatePage() const StringVector &citeDataList = Config_getList(CITE_BIB_FILES); for (const auto &bibdata : citeDataList) { - QCString bibFile = bibdata.c_str(); - if (!bibFile.isEmpty() && bibFile.right(4)!=".bib") bibFile+=".bib"; + QCString bibFile = getBibFile(QCString(bibdata)); insertCrossReferencesForBibFile(bibFile); } @@ -252,8 +258,7 @@ void CitationManager::generatePage() int i = 0; for (const auto &bibdata : citeDataList) { - QCString bibFile = bibdata.c_str(); - if (!bibFile.isEmpty() && bibFile.right(4)!=".bib") bibFile+=".bib"; + QCString bibFile = getBibFile(QCString(bibdata)); FileInfo fi(bibFile.str()); if (fi.exists()) { @@ -341,9 +346,7 @@ void CitationManager::generatePage() i = 0; for (const auto &bibdata : citeDataList) { - QCString bibFile = bibdata.c_str(); - // Note: file can now have multiple dots - if (!bibFile.isEmpty() && bibFile.right(4)!=".bib") bibFile+=".bib"; + QCString bibFile = getBibFile(QCString(bibdata)); FileInfo fi(bibFile.str()); if (fi.exists()) { @@ -387,9 +390,7 @@ QCString CitationManager::latexBibFiles() int i = 0; for (const auto &bibdata : citeDataList) { - QCString bibFile = bibdata.c_str(); - // Note: file can now have multiple dots - if (!bibFile.isEmpty() && bibFile.right(4)!=".bib") bibFile+=".bib"; + QCString bibFile = getBibFile(QCString(bibdata)); FileInfo fi(bibFile.str()); if (fi.exists()) { diff --git a/src/clangparser.cpp b/src/clangparser.cpp index a771d4f..d4c5de6 100644 --- a/src/clangparser.cpp +++ b/src/clangparser.cpp @@ -239,18 +239,18 @@ void ClangTUParser::parse() SrcLangExt lang = getLanguageFromFileName(fileName); if (lang==SrcLangExt_ObjC || p->detectedLang!=DetectedLang::Cpp) { - QCString fn = fileName; + QCString fn = fileName.lower(); if (p->detectedLang!=DetectedLang::Cpp && - (fn.right(4).lower()==".cpp" || fn.right(4).lower()==".cxx" || - fn.right(3).lower()==".cc" || fn.right(2).lower()==".c")) + (fn.endsWith(".cpp") || fn.endsWith(".cxx") || + fn.endsWith(".cc") || fn.endsWith(".c"))) { // fall back to C/C++ once we see an extension that indicates this p->detectedLang = DetectedLang::Cpp; } - else if (fn.right(3).lower()==".mm") // switch to Objective C++ + else if (fn.endsWith(".mm")) // switch to Objective C++ { p->detectedLang = DetectedLang::ObjCpp; } - else if (fn.right(2).lower()==".m") // switch to Objective C + else if (fn.endsWith(".m")) // switch to Objective C { p->detectedLang = DetectedLang::ObjC; } diff --git a/src/classdef.cpp b/src/classdef.cpp index c9d4906..ab95f3b 100644 --- a/src/classdef.cpp +++ b/src/classdef.cpp @@ -18,6 +18,7 @@ #include <cstdio> #include <algorithm> +#include "types.h" #include "classdef.h" #include "classlist.h" #include "entry.h" @@ -134,15 +135,10 @@ static QCString makeDisplayName(const ClassDef *cd,bool includeScope) { n=substitute(n,"::",sep); } - if (cd->compoundType()==ClassDef::Protocol && n.right(2)=="-p") + if (cd->compoundType()==ClassDef::Protocol && n.endsWith("-p")) { n="<"+n.left(n.length()-2)+">"; } - //else if (n.right(2)=="-g") - //{ - // n = n.left(n.length()-2); - //} - //printf("ClassDefImpl::displayName()=%s\n",qPrint(n)); return n; } @@ -191,7 +187,7 @@ class ClassDefImpl : public DefinitionMixin<ClassDefMutable> virtual const ArgumentList &templateArguments() const; virtual FileDef *getFileDef() const; virtual const MemberDef *getMemberByName(const QCString &) const; - virtual bool isBaseClass(const ClassDef *bcd,bool followInstances,int level=0) const; + virtual int isBaseClass(const ClassDef *bcd,bool followInstances) const; virtual bool isSubClass(ClassDef *bcd,int level=0) const; virtual bool isAccessibleMember(const MemberDef *md) const; virtual const TemplateInstanceList &getTemplateInstances() const; @@ -439,8 +435,8 @@ class ClassDefAliasImpl : public DefinitionAliasMixin<ClassDef> { return getCdAlias()->getFileDef(); } virtual const MemberDef *getMemberByName(const QCString &s) const { return getCdAlias()->getMemberByName(s); } - virtual bool isBaseClass(const ClassDef *bcd,bool followInstances,int level=0) const - { return getCdAlias()->isBaseClass(bcd,followInstances,level); } + virtual int isBaseClass(const ClassDef *bcd,bool followInstances) const + { return getCdAlias()->isBaseClass(bcd,followInstances); } virtual bool isSubClass(ClassDef *bcd,int level=0) const { return getCdAlias()->isSubClass(bcd,level); } virtual bool isAccessibleMember(const MemberDef *md) const @@ -1278,7 +1274,7 @@ static void searchTemplateSpecs(/*in*/ const Definition *d, const ClassDef *cd=toClassDef(d); if (!name.isEmpty()) name+="::"; QCString clName = d->localName(); - if (/*clName.right(2)=="-g" ||*/ clName.right(2)=="-p") + if (clName.endsWith("-p")) { clName = clName.left(clName.length()-2); } @@ -3204,28 +3200,30 @@ void ClassDefImpl::setTemplateArguments(const ArgumentList &al) m_impl->tempArgs = al; } -/*! Returns \c TRUE iff this class or a class inheriting from this class - * is \e not defined in an external tag file. - */ -bool ClassDefImpl::hasNonReferenceSuperClass() const +static bool hasNonReferenceSuperClassRec(const ClassDef *cd,int level) { - bool found=!isReference() && isLinkableInProject() && !isHidden(); + bool found=!cd->isReference() && cd->isLinkableInProject() && !cd->isHidden(); if (found) { return TRUE; // we're done if this class is not a reference } - for (const auto &ibcd : m_impl->inheritedBy) + for (const auto &ibcd : cd->subClasses()) { - ClassDef *bcd=ibcd.classDef; + const ClassDef *bcd=ibcd.classDef; + if (level>256) + { + err("Possible recursive class relation while inside %s and looking for base class %s\n",qPrint(cd->name()),qPrint(bcd->name())); + return FALSE; + } // recurse into the super class branch - found = found || bcd->hasNonReferenceSuperClass(); + found = found || hasNonReferenceSuperClassRec(bcd,level+1); if (!found) { // look for template instances that might have non-reference super classes for (const auto &cil : bcd->getTemplateInstances()) { // recurse into the template instance branch - found = cil.classDef->hasNonReferenceSuperClass(); + found = hasNonReferenceSuperClassRec(cil.classDef,level+1); if (found) break; } } @@ -3237,6 +3235,14 @@ bool ClassDefImpl::hasNonReferenceSuperClass() const return found; } +/*! Returns \c TRUE iff this class or a class inheriting from this class + * is \e not defined in an external tag file. + */ +bool ClassDefImpl::hasNonReferenceSuperClass() const +{ + return hasNonReferenceSuperClassRec(this,0); +} + QCString ClassDefImpl::requiresClause() const { return m_impl->requiresClause; @@ -3359,26 +3365,40 @@ bool ClassDefImpl::hasDocumentation() const //---------------------------------------------------------------------- // recursive function: -// returns TRUE iff class definition 'bcd' represents an (in)direct base -// class of class definition 'cd'. +// returns the distance to the base class definition 'bcd' represents an (in)direct base +// class of class definition 'cd' or 0 if it does not. -bool ClassDefImpl::isBaseClass(const ClassDef *bcd, bool followInstances,int level) const +int ClassDefImpl::isBaseClass(const ClassDef *bcd, bool followInstances) const { - bool found=FALSE; + int distance=0; //printf("isBaseClass(cd=%s) looking for %s\n",qPrint(name()),qPrint(bcd->name())); - if (level>256) - { - err("Possible recursive class relation while inside %s and looking for base class %s\n",qPrint(name()),qPrint(bcd->name())); - return FALSE; - } for (const auto &bclass : baseClasses()) { const ClassDef *ccd = bclass.classDef; if (!followInstances && ccd->templateMaster()) ccd=ccd->templateMaster(); - found = (ccd==bcd) || ccd->isBaseClass(bcd,followInstances,level+1); - if (found) break; + if (ccd==bcd) + { + distance=1; + break; // no shorter path possible + } + else + { + int d = ccd->isBaseClass(bcd,followInstances); + if (d>256) + { + err("Possible recursive class relation while inside %s and looking for base class %s\n",qPrint(name()),qPrint(bcd->name())); + return 0; + } + else if (d>0) // path found + { + if (distance==0 || d+1<distance) // update if no path found yet or shorter path found + { + distance=d+1; + } + } + } } - return found; + return distance; } //---------------------------------------------------------------------- diff --git a/src/classdef.h b/src/classdef.h index bd1a160..f991791 100644 --- a/src/classdef.h +++ b/src/classdef.h @@ -216,16 +216,14 @@ class ClassDef : public Definition */ virtual FileDef *getFileDef() const = 0; - /** Returns the Java package this class is in or 0 if not applicable. - */ - + /** Returns the member with the given name */ virtual const MemberDef *getMemberByName(const QCString &) const = 0; /** Returns TRUE iff \a bcd is a direct or indirect base class of this * class. This function will recursively traverse all branches of the * inheritance tree. */ - virtual bool isBaseClass(const ClassDef *bcd,bool followInstances,int level=0) const = 0; + virtual int isBaseClass(const ClassDef *bcd,bool followInstances) const = 0; /** Returns TRUE iff \a bcd is a direct or indirect sub class of this * class. diff --git a/src/cmdmapper.cpp b/src/cmdmapper.cpp index a5caede..0f83334 100644 --- a/src/cmdmapper.cpp +++ b/src/cmdmapper.cpp @@ -34,21 +34,25 @@ CommandMap cmdMap[] = { "b", CMD_BOLD }, { "c", CMD_CODE }, { "cite", CMD_CITE }, + { "icode", CMD_ISTARTCODE }, { "code", CMD_STARTCODE }, { "copydoc", CMD_COPYDOC }, { "copybrief", CMD_COPYBRIEF }, { "copydetails", CMD_COPYDETAILS }, { "copyright", CMD_COPYRIGHT }, { "date", CMD_DATE }, + { "showdate", CMD_SHOWDATE }, { "dontinclude", CMD_DONTINCLUDE }, { "dotfile", CMD_DOTFILE }, { "e", CMD_EMPHASIS }, { "em", CMD_EMPHASIS }, + { "endicode", CMD_ENDICODE }, { "endcode", CMD_ENDCODE }, { "endhtmlonly", CMD_ENDHTMLONLY }, { "endlatexonly", CMD_ENDLATEXONLY }, { "endlink", CMD_ENDLINK }, { "endsecreflist", CMD_ENDSECREFLIST }, + { "endiverbatim", CMD_ENDIVERBATIM }, { "endverbatim", CMD_ENDVERBATIM }, { "endxmlonly", CMD_ENDXMLONLY }, { "exception", CMD_EXCEPTION }, @@ -99,6 +103,7 @@ CommandMap cmdMap[] = { "xrefitem", CMD_XREFITEM }, { "throw", CMD_EXCEPTION }, { "until", CMD_UNTIL }, + { "iverbatim", CMD_IVERBATIM }, { "verbatim", CMD_VERBATIM }, { "verbinclude", CMD_VERBINCLUDE }, { "version", CMD_VERSION }, @@ -151,6 +156,7 @@ CommandMap cmdMap[] = { "maninclude", CMD_MANINCLUDE }, { "xmlinclude", CMD_XMLINCLUDE }, { "iline", CMD_ILINE }, + { "ifile", CMD_IFILE }, { "iliteral", CMD_ILITERAL }, { "endiliteral", CMD_ENDILITERAL }, { 0, 0 }, diff --git a/src/cmdmapper.h b/src/cmdmapper.h index 619470a..b99dae9 100644 --- a/src/cmdmapper.h +++ b/src/cmdmapper.h @@ -145,6 +145,12 @@ enum CommandType CMD_ILINE = 116, CMD_ILITERAL = 117, CMD_ENDILITERAL = 118, + CMD_IFILE = 119, + CMD_SHOWDATE = 120, + CMD_ISTARTCODE = 121, + CMD_ENDICODE = 122, + CMD_IVERBATIM = 123, + CMD_ENDIVERBATIM = 124 }; enum HtmlTagType @@ -110,6 +110,7 @@ struct codeYY_state int yyLineNr = 0; //!< current line number yy_size_t yyColNr = 0; //!< current column number bool insideCodeLine = FALSE; + bool skipCodify = FALSE; //!< for CSharp files scoped namespace { bool exampleBlock = FALSE; QCString exampleName; @@ -278,7 +279,7 @@ Bopt {B}* BN [ \t\n\r] ID [$a-z_A-Z\x80-\xFF][$a-z_A-Z0-9\x80-\xFF]* SEP ("::"|"\\") -SCOPENAME ({SEP}{BN}*)?({ID}{BN}*{SEP}{BN}*)*("~"{BN}*)?{ID} +SCOPENAME ("::"{BN}*)?({ID}{BN}*{SEP}{BN}*)*("~"{BN}*)?{ID} TEMPLIST "<"[^\"\}\{\(\)\/\n\>]*">" SCOPETNAME (((({ID}{TEMPLIST}?){BN}*)?{SEP}{BN}*)*)((~{BN}*)?{ID}) SCOPEPREFIX ({ID}{TEMPLIST}?{BN}*{SEP}{BN}*)+ @@ -625,8 +626,8 @@ ENDQopt ("const"|"volatile"|"sealed"|"override")({BN}+("const"|"volatile"|"seale if (yyextra->sourceFileDef) { const FileDef *fd=yyextra->sourceFileDef; - yyextra->insideObjC = fd->name().lower().right(2)==".m" || - fd->name().lower().right(3)==".mm"; + yyextra->insideObjC = fd->name().lower().endsWith(".m") || + fd->name().lower().endsWith(".mm"); DBG_CTX((stderr,"insideObjC=%d\n",yyextra->insideObjC)); } else @@ -660,9 +661,18 @@ ENDQopt ("const"|"volatile"|"sealed"|"override")({BN}+("const"|"volatile"|"seale BEGIN(Body); } <ClassName,ClassVar>";" { - yyextra->code->codify(yytext); - yyextra->searchingForBody=FALSE; - BEGIN( Body ); + if (yyextra->lang==SrcLangExt_CSharp) + { + yyextra->code->codify(yytext); + yyextra->skipCodify = true; + unput('{'); + } + else + { + yyextra->code->codify(yytext); + yyextra->searchingForBody=FALSE; + BEGIN( Body ); + } } <ClassName,ClassVar>[*&^%]+ { yyextra->type=yyextra->curClassName; @@ -776,7 +786,8 @@ ENDQopt ("const"|"volatile"|"sealed"|"override")({BN}+("const"|"volatile"|"seale <Bases>^{Bopt}/"@"{ID} | // Objective-C interface <Bases,ClassName,ClassVar,CppCliTypeModifierFollowup>{B}*"{"{B}* { yyextra->theVarContext.pushScope(); - yyextra->code->codify(yytext); + if (!yyextra->skipCodify) yyextra->code->codify(yytext); + yyextra->skipCodify = false; if (YY_START==ClassVar && yyextra->curClassName.isEmpty()) { yyextra->curClassName = yyextra->name; @@ -1205,7 +1216,7 @@ ENDQopt ("const"|"volatile"|"sealed"|"override")({BN}+("const"|"volatile"|"seale yyextra->delimiter=yyextra->delimiter.left(yyextra->delimiter.length()-1); BEGIN( RawString ); } -<FuncCall,Body,MemberCall,MemberCall2,SkipInits,InlineInit>\" { +<FuncCall,Body,MemberCall,MemberCall2,SkipInits,InlineInit,ClassVar>\" { startFontClass(yyscanner,"stringliteral"); yyextra->code->codify(yytext); yyextra->lastStringContext=YY_START; @@ -2208,14 +2219,8 @@ static void addVariable(yyscan_t yyscanner,QCString type,QCString name) DBG_CTX((stderr,"VariableContext::addVariable(%s,%s)\n",qPrint(type),qPrint(name))); QCString ltype = type.simplifyWhiteSpace(); QCString lname = name.simplifyWhiteSpace(); - if (ltype.left(7)=="struct ") - { - ltype = ltype.right(ltype.length()-7); - } - else if (ltype.left(6)=="union ") - { - ltype = ltype.right(ltype.length()-6); - } + ltype.stripPrefix("struct "); + ltype.stripPrefix("union "); if (ltype.isEmpty() || lname.isEmpty()) return; ltype = substitute(ltype,".","::"); DBG_CTX((stderr,"** addVariable trying: type='%s' name='%s' currentDefinition=%s\n", @@ -2824,12 +2829,6 @@ static bool getLinkInScope(yyscan_t yyscanner, return TRUE; } } - else // found member, but it is not linkable, so make sure content inside is not assigned - // to the previous member, see bug762760 - { - DBG_CTX((stderr,"unlinkable member %s\n",qPrint(md->name()))); - yyextra->currentMemberDef = 0; - } } return FALSE; } diff --git a/src/commentcnv.l b/src/commentcnv.l index ae4c7d3..e0d3474 100644 --- a/src/commentcnv.l +++ b/src/commentcnv.l @@ -439,7 +439,7 @@ SLASHopt [/]* copyToOutput(yyscanner,yytext,(int)yyleng); yyextra->lastCommentContext = YY_START; yyextra->javaBlock=0; - yyextra->blockName=QCString(yytext).stripWhiteSpace().left(3); + yyextra->blockName=QCString(yytext).stripWhiteSpace().left(3); // take the ``` or ~~~ part BEGIN(VerbatimCode); } <CComment,ReadLine>[\\@]("dot"|"code"|"msc"|"startuml")/[^a-z_A-Z0-9] { /* start of a verbatim block */ @@ -663,6 +663,10 @@ SLASHopt [/]* BEGIN(Scan); } } +<CComment,CNComment>"/""/"+/"*/" { /* we are already in C-comment so not a start of a nested comment but + * just the end of the comment (the end part is handled later). */ + copyToOutput(yyscanner,yytext,(int)yyleng); + } <CComment,CNComment>"/"+"*" { /* nested C comment */ if (yyextra->lang==SrcLangExt_Python || yyextra->lang==SrcLangExt_Markdown) @@ -897,10 +901,10 @@ SLASHopt [/]* handleCondSectionId(yyscanner," "); // fake section id causing the section to be hidden unconditionally if (*yytext=='\n') { yyextra->lineNr++; copyToOutput(yyscanner,"\n",1);} } -<CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]* { // expand alias without arguments +<CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9-]* { // expand alias without arguments replaceAliases(yyscanner,QCString(yytext)); } -<CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9]*"{" { // expand alias with arguments +<CComment,ReadLine>[\\@][a-z_A-Z][a-z_A-Z0-9-]*"{" { // expand alias with arguments yyextra->lastBlockContext=YY_START; yyextra->blockCount=1; yyextra->aliasString=yytext; diff --git a/src/commentscan.l b/src/commentscan.l index 67756a4..fd2ae11 100644 --- a/src/commentscan.l +++ b/src/commentscan.l @@ -35,6 +35,8 @@ typedef yyguts_t *yyscan_t; #include <stack> #include <string> #include <mutex> +#include <functional> +#include <unordered_map> #include <stdio.h> #include <stdlib.h> @@ -42,6 +44,7 @@ typedef yyguts_t *yyscan_t; #include <ctype.h> #include "qcstring.h" +#include "fileinfo.h" #include "cite.h" #include "commentscan.h" #include "condparser.h" @@ -142,6 +145,8 @@ static bool handleParBlock(yyscan_t yyscanner,const QCString &, const StringVect static bool handleEndParBlock(yyscan_t yyscanner,const QCString &, const StringVector &); static bool handleParam(yyscan_t yyscanner,const QCString &, const StringVector &); static bool handleRetval(yyscan_t yyscanner,const QCString &, const StringVector &); +static bool handleFileInfo(yyscan_t yyscanner,const QCString &, const StringVector &); +static bool handleLineInfo(yyscan_t yyscanner,const QCString &, const StringVector &); #if USE_STATE2STRING static const char *stateToString(int state); @@ -183,11 +188,13 @@ static const std::map< std::string, DocCmdMap > docCmdMap = { "cite", { &handleCite, CommandSpacing::Inline }}, { "class", { &handleClass, CommandSpacing::Invisible }}, { "code", { &handleFormatBlock, CommandSpacing::Block }}, + { "icode", { &handleFormatBlock, CommandSpacing::Block }}, { "concept", { &handleConcept, CommandSpacing::Invisible }}, { "copybrief", { &handleCopyBrief, CommandSpacing::Invisible }}, { "copydetails", { &handleCopyDetails, CommandSpacing::Block }}, { "copydoc", { &handleCopyDoc, CommandSpacing::Block }}, { "copyright", { 0, CommandSpacing::Block }}, + { "showdate", { 0, CommandSpacing::Inline }}, { "date", { 0, CommandSpacing::Block }}, { "def", { &handleDef, CommandSpacing::Invisible }}, { "defgroup", { &handleDefGroup, CommandSpacing::Invisible }}, @@ -301,6 +308,7 @@ static const std::map< std::string, DocCmdMap > docCmdMap = { "until", { 0, CommandSpacing::Block }}, { "var", { &handleFn, CommandSpacing::Invisible }}, { "verbatim", { &handleFormatBlock, CommandSpacing::Block }}, + { "iverbatim", { &handleFormatBlock, CommandSpacing::Block }}, { "verbinclude", { 0, CommandSpacing::Inline }}, { "version", { 0, CommandSpacing::Block }}, { "warning", { 0, CommandSpacing::Block }}, @@ -309,6 +317,8 @@ static const std::map< std::string, DocCmdMap > docCmdMap = { "xmlonly", { &handleFormatBlock, CommandSpacing::Invisible }}, { "xrefitem", { &handleXRefItem, CommandSpacing::XRef }}, { "iliteral", { &handleFormatBlock, CommandSpacing::Inline }}, + { "fileinfo", { &handleFileInfo, CommandSpacing::Inline }}, + { "lineinfo", { &handleLineInfo, CommandSpacing::Inline }}, }; #define YY_NO_INPUT 1 @@ -454,6 +464,7 @@ static void handleGuard(yyscan_t yyscanner,const QCString &expr); static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size); static void addCite(yyscan_t yyscanner); static void addIline(yyscan_t yyscanner,int lineNr); +static void addIlineBreak(yyscan_t yyscanner,int lineNr); #define unput_string(yytext,yyleng) do { for (int i=(int)yyleng-1;i>=0;i--) unput(yytext[i]); } while(0) @@ -948,7 +959,7 @@ STopt [^\n@\\]* addOutput(yyscanner,yytext[0]); addOutput(yyscanner,yytext[2]); } -<Comment>".," { // . with comma such as "e.g.," +<Comment>"."[,:;] { // . with some puntuations such as "e.g.," or "e.g.:" addOutput(yyscanner,yytext); } <Comment>"...\\"[ \t] { // ellipsis with escaped space. @@ -1070,6 +1081,7 @@ STopt [^\n@\\]* yyextra->formulaNewLines++; yyextra->formulaText+=*yytext; yyextra->lineNr++; + addIline(yyscanner,yyextra->lineNr); } <ReadFormulaLong,ReadFormulaShort,ReadFormulaRound>. { // any other character yyextra->formulaText+=*yytext; @@ -1245,7 +1257,7 @@ STopt [^\n@\\]* //lastDefGroup.groupname = yytext; //lastDefGroup.pri = yyextra->current->groupingPri(); // the .html stuff is for Qt compatibility - if (yyextra->current->name.right(5)==".html") + if (yyextra->current->name.endsWith(".html")) { yyextra->current->name=yyextra->current->name.left(yyextra->current->name.length()-5); } @@ -1592,7 +1604,7 @@ STopt [^\n@\\]* /* ----- handle arguments of the preformatted block commands ------- */ -<FormatBlock>{CMD}("endverbatim"|"endiliteral"|"endlatexonly"|"endhtmlonly"|"endxmlonly"|"enddocbookonly"|"endrtfonly"|"endmanonly"|"enddot"|"endcode"|"endmsc")/{NW} { // possible ends +<FormatBlock>{CMD}("endverbatim"|"endiverbatim"|"endiliteral"|"endlatexonly"|"endhtmlonly"|"endxmlonly"|"enddocbookonly"|"endrtfonly"|"endmanonly"|"enddot"|"endcode"|"endicode"|"endmsc")/{NW} { // possible ends addOutput(yyscanner,yytext); if (&yytext[4]==yyextra->blockName) // found end of the block { @@ -1614,12 +1626,20 @@ STopt [^\n@\\]* addOutput(yyscanner,'\n'); } <FormatBlock>{CCS} { // start of a C-comment - if (!(yyextra->blockName=="code" || yyextra->blockName=="verbatim" || yyextra->blockName=="iliteral")) yyextra->commentCount++; + if (!(yyextra->blockName=="code" || yyextra->blockName=="verbatim" || + yyextra->blockName=="icode" || yyextra->blockName=="iverbatim"|| + yyextra->blockName=="iliteral" + ) + ) yyextra->commentCount++; addOutput(yyscanner,yytext); } <FormatBlock>{CCE} { // end of a C-comment addOutput(yyscanner,yytext); - if (!(yyextra->blockName=="code" || yyextra->blockName=="verbatim" || yyextra->blockName=="iliteral")) + if (!(yyextra->blockName=="code" || yyextra->blockName=="verbatim" || + yyextra->blockName=="icode" || yyextra->blockName=="iverbatim"|| + yyextra->blockName=="iliteral" + ) + ) { yyextra->commentCount--; if (yyextra->commentCount<0) @@ -1680,7 +1700,7 @@ STopt [^\n@\\]* if (*yytext=='\n') yyextra->lineNr++; //next line is commented out due to bug620924 //addOutput(yyscanner,'\n'); - addIline(yyscanner,yyextra->lineNr); + addIlineBreak(yyscanner,yyextra->lineNr); BEGIN( Comment ); } <GuardParam>{LC} { // line continuation @@ -1693,7 +1713,7 @@ STopt [^\n@\\]* <GuardParamEnd>{B}*{DOCNL} { lineCount(yyscanner); yyextra->spaceBeforeIf.resize(0); - addIline(yyscanner,yyextra->lineNr); + addIlineBreak(yyscanner,yyextra->lineNr); BEGIN(Comment); } <GuardParamEnd>{B}* { @@ -1702,12 +1722,12 @@ STopt [^\n@\\]* addOutput(yyscanner,yyextra->spaceBeforeIf); } yyextra->spaceBeforeIf.resize(0); - addIline(yyscanner,yyextra->lineNr); + addIlineBreak(yyscanner,yyextra->lineNr); BEGIN(Comment); } <GuardParamEnd>. { unput(*yytext); - addIline(yyscanner,yyextra->lineNr); + addIlineBreak(yyscanner,yyextra->lineNr); BEGIN(Comment); } @@ -1859,8 +1879,11 @@ STopt [^\n@\\]* /* ----- handle argument of noop command ------- */ <Noop>{DOCNL} { // end of argument - if (*yytext=='\n') yyextra->lineNr++; - addOutput(yyscanner,'\n'); + if (*yytext=='\n') + { + yyextra->lineNr++; + addOutput(yyscanner,'\n'); + } BEGIN( Comment ); } <Noop>. { // ignore other stuff @@ -2628,6 +2651,76 @@ static bool handleAddIndex(yyscan_t yyscanner,const QCString &, const StringVect return FALSE; } +static bool handleFileInfo(yyscan_t yyscanner,const QCString &, const StringVector &optList) +{ + using OutputWriter = std::function<void(yyscan_t,FileInfo &)>; + static std::unordered_map<std::string,OutputWriter> options = + { // name, writer + { "name", [](yyscan_t s,FileInfo &fi) { addOutput(s,fi.baseName()); } }, + { "extension", [](yyscan_t s,FileInfo &fi) { addOutput(s,fi.extension(true)); } }, + { "filename", [](yyscan_t s,FileInfo &fi) { addOutput(s,fi.fileName()); } }, + { "directory", [](yyscan_t s,FileInfo &fi) { addOutput(s,fi.dirPath()); } }, + { "full", [](yyscan_t s,FileInfo &fi) { addOutput(s,fi.absFilePath()); } }, + }; + + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + if (!yyextra->spaceBeforeCmd.isEmpty()) + { + addOutput(yyscanner,yyextra->spaceBeforeCmd); + yyextra->spaceBeforeCmd.resize(0); + } + bool first = true; + FileInfo fi(yyextra->fileName.str()); + for (const auto &opt_ : optList) + { + QCString optStripped = QCString(opt_).stripWhiteSpace(); + std::string opt = optStripped.lower().str(); + auto it = options.find(opt); + if (it != options.end()) + { + if (!first) + { + warn(yyextra->fileName,yyextra->lineNr,"Multiple options specified with \\fileinfo, discarding '%s'", qPrint(optStripped)); + } + else + { + it->second(yyscanner,fi); + } + first = false; + } + else + { + warn(yyextra->fileName,yyextra->lineNr,"Unknown option specified with \\fileinfo: '%s'", qPrint(optStripped)); + } + } + if (first) // no options specified + { + if (Config_getBool(FULL_PATH_NAMES)) + { + addOutput(yyscanner,stripFromPath(yyextra->fileName)); + } + else + { + addOutput(yyscanner,yyextra->fileName); + } + } + addOutput(yyscanner," "); + return FALSE; +} + +static bool handleLineInfo(yyscan_t yyscanner,const QCString &, const StringVector &) +{ + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + if (!yyextra->spaceBeforeCmd.isEmpty()) + { + addOutput(yyscanner,yyextra->spaceBeforeCmd); + yyextra->spaceBeforeCmd.resize(0); + } + addOutput(yyscanner,QCString().setNum(yyextra->lineNr)); + addOutput(yyscanner," "); + return FALSE; +} + static bool handleIf(yyscan_t yyscanner,const QCString &, const StringVector &) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; @@ -3464,6 +3557,13 @@ static inline void addOutput(yyscan_t yyscanner,char c) static void addIline(yyscan_t yyscanner,int lineNr) { + char cmd[20]; + sprintf(cmd,"\\iline %d ",lineNr); + addOutput(yyscanner, cmd); +} + +static void addIlineBreak(yyscan_t yyscanner,int lineNr) +{ char cmd[30]; sprintf(cmd,"\\iline %d \\ilinebr ",lineNr); addOutput(yyscanner, cmd); @@ -3477,8 +3577,16 @@ static void endBrief(yyscan_t yyscanner,bool addToOutput) // found some brief description and not just whitespace yyextra->briefEndsAtDot=FALSE; setOutput(yyscanner,OutputDoc); + addIline(yyscanner,yyextra->lineNr); if (addToOutput) addOutput(yyscanner,yytext); } + else + { + int saveLineNr = yyextra->lineNr; + lineCount(yyscanner); + yyextra->current->briefLine = yyextra->lineNr; + yyextra->lineNr = saveLineNr; + } } static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size) diff --git a/src/config.h b/src/config.h index 00b3b71..f754f9b 100644 --- a/src/config.h +++ b/src/config.h @@ -45,12 +45,13 @@ //#endif //! @} -enum class DoxyfileSettings { Full, Compressed, CompressedNoEnv }; class TextStream; /** \brief Public function to deal with the configuration file. */ namespace Config { + enum class CompareMode { Full, Compressed, CompressedNoEnv }; + /*! Initialize configuration variables to their default value */ void init(); @@ -63,7 +64,7 @@ namespace Config /*! Writes a the differences between the current configuration and the * template configuration to stream \a t. */ - void compareDoxyfile(TextStream &t, DoxyfileSettings diffList); + void compareDoxyfile(TextStream &t, CompareMode compareMode); /*! Writes a the used settings of the current configuration as XML format * to stream \a t. @@ -74,15 +75,15 @@ namespace Config * \returns TRUE if successful, FALSE if the file could not be * opened or read. */ - bool parse(const QCString &fileName,bool update=FALSE); + bool parse(const QCString &fileName,bool update=FALSE, CompareMode compareMode = CompareMode::Full); /*! Post processed the parsed data. Replaces raw string values by the actual values. * and replaces environment variables. * \param clearHeaderAndFooter set to TRUE when writing header and footer templates. - * \param compare signals if we in Doxyfile compare (`-x` or `-x_noenv`) mode are or not. - * Influences setting of the default value and replacement of environment variables. + * \param compareMode signals if we in Doxyfile compare (`-x` or `-x_noenv`) mode are or not. + * Influences setting of the default value and replacement of environment variables and CMake type replacement variables. */ - void postProcess(bool clearHeaderAndFooter, DoxyfileSettings compare = DoxyfileSettings::Full); + void postProcess(bool clearHeaderAndFooter, CompareMode compareMode = CompareMode::Full); /*! Check the validity of the parsed options and correct or warn the user where needed. * \param quiet setting for the QUIET option (can have been overruled by means of a command line option) diff --git a/src/config.xml b/src/config.xml index a10c1ac..296a62e 100644 --- a/src/config.xml +++ b/src/config.xml @@ -95,7 +95,7 @@ Use doxygen to compare the used configuration file with the template configurati doxygen -x [configFile] \endverbatim Use doxygen to compare the used configuration file with the template configuration file -without replacing the environment variables: +without replacing the environment variables or CMake type replacement variables: \verbatim doxygen -x_noenv [configFile] \endverbatim @@ -995,7 +995,7 @@ Go to the <a href="commands.html">next</a> section or return to the ]]> </docs> </option> - <option type='bool' id='CASE_SENSE_NAMES' defval='0' altdefval='Portable::fileSystemIsCaseSensitive()'> + <option type='enum' id='CASE_SENSE_NAMES' defval='SYSTEM'> <docs> <![CDATA[ With the correct setting of option \c CASE_SENSE_NAMES doxygen will better be able to match the @@ -1014,6 +1014,9 @@ Go to the <a href="commands.html">next</a> section or return to the whereas on Linux or other Unix flavors it should typically be set to \c YES. ]]> </docs> + <value name="SYSTEM" /> + <value name="NO"/> + <value name="YES" /> </option> <option type='bool' id='HIDE_SCOPE_NAMES' defval='0'> <docs> @@ -1455,6 +1458,22 @@ FILE_VERSION_FILTER = "cleartool desc -fmt \%Vn" Doxygen uses `libiconv` (or the `iconv` built into `libc`) for the transcoding. See <a href="https://www.gnu.org/software/libiconv/">the libiconv documentation</a> for the list of possible encodings. + + \sa \ref cfg_input_file_encoding "INPUT_FILE_ENCODING" +]]> + </docs> + </option> + <option type='list' id='INPUT_FILE_ENCODING' format='string'> + <docs> +<![CDATA[ + This tag can be used to specify the character encoding of the source files that doxygen + parses + The \c INPUT_FILE_ENCODING tag can be used to specify character encoding on a per file pattern + basis. Doxygen will compare the file name with each pattern and apply the + encoding instead of the default \ref cfg_input_encoding "INPUT_ENCODING") if there is a match. + The character encodings are a list of the form: pattern=encoding (like `*.php=ISO-8859-1`). + + See cfg_input_encoding "INPUT_ENCODING" for further information on supported encodings. ]]> </docs> </option> @@ -1628,6 +1647,9 @@ FILE_VERSION_FILTER = "cleartool desc -fmt \%Vn" <br>Note that the filter must not add or remove lines; it is applied before the code is scanned, but not when the output code is generated. If lines are added or removed, the anchors will not be placed correctly. + <br>Note that doxygen will use the data processed and written to standard output for further processing, + therefore nothing else, like debug statements or used commands (so in case of a Windows batch file + always use `@echo OFF`), should be written to standard output. <br>Note that for custom extensions or not directly supported extensions you also need to set \ref cfg_extension_mapping "EXTENSION_MAPPING" for the extension otherwise the files are not properly processed by doxygen.<br> @@ -1682,6 +1704,17 @@ FILE_VERSION_FILTER = "cleartool desc -fmt \%Vn" ]]> </docs> </option> + <option type='int' id='FORTRAN_COMMENT_AFTER' defval='72' minval='7' maxval='10000'> + <docs> +<![CDATA[ + The Fortran standard specifies that for fixed formatted Fortran code all characters + from position 72 are to be considered as comment. A common extension is to allow + longer lines before the automatic comment starts. + The setting \c FORTRAN_COMMENT_AFTER will also make it possible that longer lines can be + processed before the automatic comment starts. +]]> + </docs> + </option> </group> <group name='Source_Browser' docs='Configuration options related to source browsing'> <option type='bool' id='SOURCE_BROWSER' defval='0'> @@ -2107,6 +2140,21 @@ hr.footer { ]]> </docs> </option> + <option type='enum' id='HTML_COLORSTYLE' defval='AUTO_LIGHT' depends='GENERATE_HTML'> + <docs> +<![CDATA[ + The \c HTML_COLORSTYLE tag can be used to specify if the generated HTML output should be rendered with + a dark or light theme. Default setting \c AUTO_LIGHT enables light output unless the user preference is dark output. + Other options are \c DARK to always use dark mode, \c LIGHT to always use light mode, \c AUTO_DARK to default to dark + mode unless the user prefers light mode, and \c TOGGLE to let the user toggle between dark and light mode via a button. +]]> + </docs> + <value name="LIGHT" desc="Always generate light output."/> + <value name="DARK" desc="Always generate dark output."/> + <value name="AUTO_LIGHT" desc="Automatically set the mode according to the user preference, use light mode if no preference is set (the default)."/> + <value name="AUTO_DARK" desc="Automatically set the mode according to the user preference, use dark mode if no preference is set."/> + <value name="TOGGLE" desc="Allow to user to switch between light and dark mode via a button."/> + </option> <option type='int' id='HTML_COLORSTYLE_HUE' minval='0' maxval='359' defval='220' depends='GENERATE_HTML'> <docs> <![CDATA[ @@ -2539,17 +2587,6 @@ obfuscate email addresses. ]]> </docs> </option> - <option type='bool' id='FORMULA_TRANSPARENT' defval='1' depends='GENERATE_HTML'> - <docs> -<![CDATA[ - Use the \c FORMULA_TRANSPARENT tag to determine whether or not the images - generated for formulas are transparent PNGs. Transparent PNGs are - not supported properly for IE 6.0, but are supported on all modern browsers. - <br>Note that when changing this option you need to delete any `form_*.png` files - in the HTML output directory before the changes have effect. -]]> - </docs> - </option> <option type='string' id='FORMULA_MACROFILE' format='file' defval=''> <docs> <![CDATA[ @@ -2937,6 +2974,7 @@ doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty <dt><code>$papertype</code><dd>will be replaced by the paper type as set in \ref cfg_paper_type "PAPER_TYPE" and the word "paper" is directly appended to it to have a correct \f$\mbox{\LaTeX}\f$ paper type. + <dt><code>$langISO</code><dd>will be replaced by the ISO language name. <dt><code>$languagesupport</code><dd>will be replaced by an output language dependent setting of packages required for translating terms of the specified language. <dt><code>$latexfontenc</code><dd>will be replaced by an output language dependent setting @@ -3604,32 +3642,49 @@ to be found in the default search path. ]]> </docs> </option> - <option type='string' id='DOT_FONTNAME' format='string' defval='Helvetica' depends='HAVE_DOT'> + <option type='string' id='DOT_COMMON_ATTR' format='string' depends='HAVE_DOT' + defval='fontname=Helvetica,fontsize=10'> <docs> <![CDATA[ + \c DOT_COMMON_ATTR is common attributes for nodes, edges and labels of subgraphs. When you want a differently looking font in the dot files that doxygen generates - you can specify the font name - using \c DOT_FONTNAME. You need to make sure dot is able to find the font, - which can be done by putting it in a standard location or by setting the - \c DOTFONTPATH environment variable or by setting \ref cfg_dot_fontpath "DOT_FONTPATH" to the + you can specify fontname, fontcolor and fontsize attributes. + For details please see + <a href=https://graphviz.org/doc/info/attrs.html>Node, Edge and Graph Attributes specification</a> + You need to make sure dot is able to find the font, + which can be done by putting it in a standard location + or by setting the \c DOTFONTPATH environment variable or by + setting \ref cfg_dot_fontpath "DOT_FONTPATH" to the directory containing the font. + Default graphviz fontsize is 14. +]]> + </docs> + </option> + <option type='string' id='DOT_EDGE_ATTR' format='string' depends='HAVE_DOT' + defval='labelfontname=Helvetica,labelfontsize=10'> + <docs> +<![CDATA[ + \c DOT_EDGE_ATTR is concatenated with \ref cfg_dot_common_attr "DOT_COMMON_ATTR". + For elegant style you can add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. + <a href=https://graphviz.org/doc/info/arrows.html>Complete documentation about arrows shapes.</a> ]]> </docs> </option> - <option type='int' id='DOT_FONTSIZE' minval='4' maxval='24' defval='10' depends='HAVE_DOT'> + <option type='string' id='DOT_NODE_ATTR' format='string' depends='HAVE_DOT' + defval='shape=box,height=0.2,width=0.4'> <docs> <![CDATA[ - The \c DOT_FONTSIZE tag can be used to set the size (in points) of the font of dot graphs. + \c DOT_NODE_ATTR is concatenated with \ref cfg_dot_common_attr "DOT_COMMON_ATTR". + For view without boxes around nodes set 'shape=plain' or 'shape=plaintext' + <a href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a> ]]> </docs> </option> <option type='string' id='DOT_FONTPATH' format='dir' defval='' depends='HAVE_DOT'> <docs> <![CDATA[ - By default doxygen will tell \c dot to use the default font as specified with \ref cfg_dot_fontname "DOT_FONTNAME". - If you specify a - different font using \ref cfg_dot_fontname "DOT_FONTNAME" you can set the path where \c dot - can find it using this tag. + You can set the path where \c dot can find font specified with + fontname in \ref cfg_dot_common_attr "DOT_COMMON_ATTR" and others dot attributes. ]]> </docs> </option> @@ -3925,19 +3980,6 @@ UML notation for the relationships. ]]> </docs> </option> - <option type='bool' id='DOT_TRANSPARENT' defval='0' depends='HAVE_DOT'> - <docs> -<![CDATA[ - Set the \c DOT_TRANSPARENT tag to \c YES to generate images with a transparent - background. This is disabled by default, because dot on Windows does not - seem to support this out of the box. - <br> - Warning: Depending on the platform used, - enabling this option may lead to badly anti-aliased labels on the edges of - a graph (i.e. they become hard to read). -]]> - </docs> - </option> <option type='bool' id='DOT_MULTI_TARGETS' defval='0' depends='HAVE_DOT'> <docs> <![CDATA[ @@ -3998,5 +4040,9 @@ This setting is not only used for dot files but also for msc temporary files. <option type='obsolete' orgtype='bool' id='RTF_SOURCE_CODE'/> <option type='obsolete' orgtype='bool' id='LATEX_SOURCE_CODE'/> <option type='obsolete' orgtype='bool' id='CLASS_DIAGRAMS'/> + <option type='obsolete' orgtype='int' id='DOT_FONTSIZE'/> + <option type='obsolete' orgtype='string' id='DOT_FONTNAME'/> + <option type='obsolete' orgtype='bool' id='FORMULA_TRANSPARENT'/> + <option type='obsolete' orgtype='bool' id='DOT_TRANSPARENT'/> </group> </doxygenconfig> diff --git a/src/configgen.py b/src/configgen.py index 6a79811..910b6af 100755 --- a/src/configgen.py +++ b/src/configgen.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # python script to generate configoptions.cpp and config.doc from config.xml # # Copyright (C) 1997-2015 by Dimitri van Heesch. @@ -173,9 +173,9 @@ def prepCDocs(node): "default value", defval) elif (type == 'bool'): if (node.hasAttribute('altdefval')): - doc += "<br/>%s: %s." % ("The default value is", "system dependent") + doc += "<br/>%s: %s." % ("The default value is", "system dependent") else: - doc += "<br/>%s: %s." % ("The default value is", "YES" if (defval == "1") else "NO") + doc += "<br/>%s: %s." % ("The default value is", "YES" if (defval == "1") else "NO") elif (type == 'list'): if format == 'string': values = collectValues(node) diff --git a/src/configimpl.h b/src/configimpl.h index 9e737a7..e9fd2ad 100644 --- a/src/configimpl.h +++ b/src/configimpl.h @@ -27,6 +27,7 @@ #include "containers.h" #include "qcstring.h" +#include "config.h" class TextStream; @@ -77,9 +78,9 @@ class ConfigOption protected: virtual void writeTemplate(TextStream &t,bool sl,bool upd) = 0; - virtual void compareDoxyfile(TextStream &t) = 0; + virtual void compareDoxyfile(TextStream &t,Config::CompareMode compareMode) = 0; virtual void writeXMLDoxyfile(TextStream &t) = 0; - virtual void convertStrToVal() {} + virtual void convertStrToVal(Config::CompareMode compareMode) {} virtual void emptyValueToDefault() {} virtual void substEnvVars() = 0; virtual void init() {} @@ -87,7 +88,7 @@ class ConfigOption void writeBoolValue(TextStream &t,bool v,bool initSpace = true); void writeIntValue(TextStream &t,int i,bool initSpace = true); - void writeStringValue(TextStream &t,const QCString &s,bool initSpace = true); + void writeStringValue(TextStream &t,const QCString &s,bool initSpace = true,bool wasQuoted = false); void writeStringList(TextStream &t,const StringVector &l); QCString m_spaces; @@ -111,7 +112,7 @@ class ConfigInfo : public ConfigOption m_doc = doc; } void writeTemplate(TextStream &t, bool sl,bool); - void compareDoxyfile(TextStream &) {} + void compareDoxyfile(TextStream &,Config::CompareMode) {} void writeXMLDoxyfile(TextStream &) {} void substEnvVars() {} }; @@ -136,7 +137,7 @@ class ConfigList : public ConfigOption StringVector getDefault() { return m_defaultValue; } void emptyValueToDefault() { if (m_value.empty() && !m_defaultValue.empty()) m_value=m_defaultValue; }; void writeTemplate(TextStream &t,bool sl,bool); - void compareDoxyfile(TextStream &t); + void compareDoxyfile(TextStream &t,Config::CompareMode compareMode); void writeXMLDoxyfile(TextStream &t); void substEnvVars(); void init() { m_value = m_defaultValue; } @@ -165,8 +166,8 @@ class ConfigEnum : public ConfigOption QCString *valueRef() { return &m_value; } void substEnvVars(); void writeTemplate(TextStream &t,bool sl,bool); - void convertStrToVal(); - void compareDoxyfile(TextStream &t); + void convertStrToVal(Config::CompareMode compareMode); + void compareDoxyfile(TextStream &t,Config::CompareMode compareMode); void writeXMLDoxyfile(TextStream &t); void init() { m_value = m_defValue; } bool isDefault() { return m_value == m_defValue; } @@ -198,7 +199,7 @@ class ConfigString : public ConfigOption void setDefaultValue(const char *v) { m_defValue = v; } QCString *valueRef() { return &m_value; } void writeTemplate(TextStream &t,bool sl,bool); - void compareDoxyfile(TextStream &t); + void compareDoxyfile(TextStream &t,Config::CompareMode compareMode); void writeXMLDoxyfile(TextStream &t); void substEnvVars(); void init() { m_value = m_defValue; } @@ -230,10 +231,10 @@ class ConfigInt : public ConfigOption int *valueRef() { return &m_value; } int minVal() const { return m_minVal; } int maxVal() const { return m_maxVal; } - void convertStrToVal(); + void convertStrToVal(Config::CompareMode compareMode); void substEnvVars(); void writeTemplate(TextStream &t,bool sl,bool upd); - void compareDoxyfile(TextStream &t); + void compareDoxyfile(TextStream &t,Config::CompareMode compareMode); void writeXMLDoxyfile(TextStream &t); void init() { m_value = m_defValue; } bool isDefault() { return m_value == m_defValue; } @@ -260,11 +261,11 @@ class ConfigBool : public ConfigOption } QCString *valueStringRef() { return &m_valueString; } bool *valueRef() { return &m_value; } - void convertStrToVal(); + void convertStrToVal(Config::CompareMode compareMode); void substEnvVars(); void setValueString(const QCString &v) { m_valueString = v; } void writeTemplate(TextStream &t,bool sl,bool upd); - void compareDoxyfile(TextStream &t); + void compareDoxyfile(TextStream &t,Config::CompareMode compareMode); void writeXMLDoxyfile(TextStream &t); void init() { m_value = m_defValue; } bool isDefault() { return m_value == m_defValue; } @@ -282,7 +283,7 @@ class ConfigObsolete : public ConfigOption ConfigObsolete(const char *name,OptionType orgType) : ConfigOption(O_Obsolete), m_orgType(orgType) { m_name = name; } void writeTemplate(TextStream &,bool,bool); - void compareDoxyfile(TextStream &) {} + void compareDoxyfile(TextStream &,Config::CompareMode) {} void writeXMLDoxyfile(TextStream &) {} void substEnvVars() {} OptionType orgType() const { return m_orgType; } @@ -305,7 +306,7 @@ class ConfigDisabled : public ConfigOption ConfigDisabled(const char *name) : ConfigOption(O_Disabled) { m_name = name; } void writeTemplate(TextStream &,bool,bool); - void compareDoxyfile(TextStream &) {} + void compareDoxyfile(TextStream &,Config::CompareMode) {} void writeXMLDoxyfile(TextStream &) {} void substEnvVars() {} }; @@ -505,7 +506,7 @@ class ConfigImpl /*! Writes a the differences between the current configuration and the * template configuration to stream \a t. */ - void compareDoxyfile(TextStream &t); + void compareDoxyfile(TextStream &t,Config::CompareMode compareMode); /*! Writes a the used settings of the current configuration as XML format * to stream \a t. @@ -521,7 +522,7 @@ class ConfigImpl /*! Converts the string values read from the configuration file * to real values for non-string type options (like int, and bools) */ - void convertStrToVal(); + void convertStrToVal(Config::CompareMode compareMode); /*! Sets default value in case value is empty */ @@ -564,6 +565,12 @@ class ConfigImpl { m_userComment += u; } + /*! Append replacement string + */ + void appendStoreRepl(const QCString &u) + { + m_storeRepl += u; + } /*! Take the user start comment and reset it internally * \returns user start comment */ @@ -582,6 +589,15 @@ class ConfigImpl m_userComment.resize(0); return substitute(result,"\r",""); } + /*! Take the replacement string + * \returns the replacement string + */ + QCString takeStoreRepl() + { + QCString result=m_storeRepl; + m_storeRepl.resize(0); + return substitute(result,"\r",""); + } protected: @@ -602,6 +618,7 @@ class ConfigImpl static ConfigImpl *m_instance; QCString m_startComment; QCString m_userComment; + QCString m_storeRepl; bool m_initialized; QCString m_header; }; diff --git a/src/configimpl.l b/src/configimpl.l index 9975cd0..3ff0e6a 100644 --- a/src/configimpl.l +++ b/src/configimpl.l @@ -30,7 +30,9 @@ #include <algorithm> #include <fstream> #include <iostream> +#include <iomanip> +#include "config.h" #include "regex.h" #include "configimpl.h" #include "version.h" @@ -41,6 +43,7 @@ #include "fileinfo.h" #include "dir.h" #include "textstream.h" +#include "dotattributes.h" #define YY_NO_INPUT 1 #define YY_NO_UNISTD_H 1 @@ -84,6 +87,8 @@ static QCString configStringRecode( const QCString &fromEncoding, const QCString &toEncoding); +static bool containsEnvVar(QCString &str); + #define MAX_INCLUDE_DEPTH 10 #define YY_NEVER_INTERACTIVE 1 @@ -140,10 +145,10 @@ void ConfigOption::writeIntValue(TextStream &t,int i,bool initSpace) t << i; } -void ConfigOption::writeStringValue(TextStream &t,const QCString &s,bool initSpace) +void ConfigOption::writeStringValue(TextStream &t,const QCString &s,bool initSpace, bool wasQuoted) { char c; - bool needsEscaping=FALSE; + bool needsEscaping=wasQuoted; // convert the string back to it original g_encoding QCString se = configStringRecode(s,"UTF-8",m_encoding); if (se.isEmpty()) return; @@ -181,7 +186,12 @@ void ConfigOption::writeStringList(TextStream &t,const StringVector &l) QCString s=p.c_str(); if (!first) t << " "; - writeStringValue(t,s); + bool wasQuoted = ((s.at(0)=='"') && (s.at(s.length()-1)=='"')); + if (wasQuoted) + { + s = s.mid(1,s.length()-2); + } + writeStringValue(t,s,true,wasQuoted); first=FALSE; } } @@ -191,10 +201,14 @@ void ConfigOption::writeStringList(TextStream &t,const StringVector &l) ConfigImpl *ConfigImpl::m_instance = 0; -void ConfigInt::convertStrToVal() +void ConfigInt::convertStrToVal(Config::CompareMode compareMode) { if (!m_valueString.isEmpty()) { + if (compareMode == Config::CompareMode::CompressedNoEnv) + { + if (containsEnvVar(m_valueString)) return; + } bool ok; int val = m_valueString.toInt(&ok); if (!ok || val<m_minVal || val>m_maxVal) @@ -229,10 +243,14 @@ static bool convertStringToBool(const QCString &str,bool &isValid) return false; } -void ConfigBool::convertStrToVal() +void ConfigBool::convertStrToVal(Config::CompareMode compareMode) { if (!m_valueString.stripWhiteSpace().isEmpty()) { + if (compareMode == Config::CompareMode::CompressedNoEnv) + { + if (containsEnvVar(m_valueString)) return; + } bool isValid=false; bool b = convertStringToBool(m_valueString,isValid); if (isValid) @@ -247,13 +265,17 @@ void ConfigBool::convertStrToVal() } } -void ConfigEnum::convertStrToVal() +void ConfigEnum::convertStrToVal(Config::CompareMode compareMode) { if (m_value.isEmpty()) { m_value = m_defValue; return; } + if (compareMode == Config::CompareMode::CompressedNoEnv) + { + if (containsEnvVar(m_value)) return; + } QCString val = m_value.stripWhiteSpace().lower(); for (const auto &s : m_valueRange) { @@ -401,7 +423,7 @@ bool ConfigList::isDefault() return true; } -void ConfigList::compareDoxyfile(TextStream &t) +void ConfigList::compareDoxyfile(TextStream &t, Config::CompareMode) { if (!isDefault()) writeTemplate(t,TRUE,TRUE); } @@ -442,7 +464,7 @@ void ConfigEnum::writeTemplate(TextStream &t,bool sl,bool) t << "\n"; } -void ConfigEnum::compareDoxyfile(TextStream &t) +void ConfigEnum::compareDoxyfile(TextStream &t, Config::CompareMode) { if (!isDefault()) writeTemplate(t,TRUE,TRUE); } @@ -476,7 +498,7 @@ void ConfigString::writeTemplate(TextStream &t,bool sl,bool) t << "\n"; } -void ConfigString::compareDoxyfile(TextStream &t) +void ConfigString::compareDoxyfile(TextStream &t, Config::CompareMode) { if (!isDefault()) writeTemplate(t,TRUE,TRUE); } @@ -519,9 +541,9 @@ void ConfigInt::writeTemplate(TextStream &t,bool sl,bool upd) t << "\n"; } -void ConfigInt::compareDoxyfile(TextStream &t) +void ConfigInt::compareDoxyfile(TextStream &t,Config::CompareMode compareMode) { - if (!isDefault()) writeTemplate(t,TRUE,TRUE); + if (!isDefault() || ((compareMode == Config::CompareMode::CompressedNoEnv) && containsEnvVar(m_valueString))) writeTemplate(t,TRUE,TRUE); } void ConfigInt::writeXMLDoxyfile(TextStream &t) @@ -561,9 +583,9 @@ void ConfigBool::writeTemplate(TextStream &t,bool sl,bool upd) t << "\n"; } -void ConfigBool::compareDoxyfile(TextStream &t) +void ConfigBool::compareDoxyfile(TextStream &t,Config::CompareMode compareMode) { - if (!isDefault()) writeTemplate(t,TRUE,TRUE); + if (!isDefault() || ((compareMode == Config::CompareMode::CompressedNoEnv) && containsEnvVar(m_valueString))) writeTemplate(t,TRUE,TRUE); } void ConfigBool::writeXMLDoxyfile(TextStream &t) @@ -608,7 +630,10 @@ static std::vector< std::unique_ptr<ConfigFileState> > g_includeStack; static bool g_configUpdate = FALSE; static QCString g_encoding; static ConfigImpl *g_config; +static Config::CompareMode g_compareMode = Config::CompareMode::Full; +static QCString g_localStoreRepl; +#define unput_string(yytext,yyleng) do { for (int i=(int)yyleng-1;i>=0;i--) unput(yytext[i]); } while(0) /* ----------------------------------------------------------------- */ #undef YY_INPUT @@ -653,7 +678,8 @@ static QCString configStringRecode( void *cd = portable_iconv_open(outputEncoding.data(),inputEncoding.data()); if (cd==reinterpret_cast<void *>(-1)) { - config_term("Error: unsupported character conversion: '%s'->'%s'\n", + config_term("Error: unsupported character conversion: '%s'->'%s'\n" + "Check the 'DOXYFILE_ENCODING' setting in the config file!\n", qPrint(inputEncoding),qPrint(outputEncoding)); } size_t iLeft=inputSize; @@ -710,6 +736,14 @@ static QCString stripComment(const QCString &s) return s; } +static void processStoreRepl(QCString &storeReplStr) +{ + // strip leading and trailing whitespace + QCString s = stripComment(storeReplStr.stripWhiteSpace()); + // recode the string + storeReplStr=configStringRecode(s,g_encoding,"UTF-8"); +} + static void processString() { // strip leading and trailing whitespace @@ -778,21 +812,24 @@ static void processList() int l = s.length(); QCString elemStr; + bool wasQuote=false; // helper to push elemStr to the list and clear it - auto addElem = [&elemStr]() + auto addElem = [&elemStr,&wasQuote]() { if (!elemStr.isEmpty()) { QCString e = configStringRecode(elemStr,g_encoding,"UTF-8"); //printf("Processed list element '%s'\n",qPrint(e)); + if (wasQuote) e = "\""+e+"\""; + wasQuote = false; g_list->push_back(e.str()); elemStr=""; } }; bool needsSeparator=false; - int insideQuote=false; + bool insideQuote=false; bool warned=false; for (int i=0;i<l;i++) { @@ -818,6 +855,7 @@ static void processList() if (!insideQuote) { insideQuote=true; + wasQuote=true; } else // this quote ends an element { @@ -944,13 +982,16 @@ static void readIncludeFile(const QCString &incName) %} %option noyywrap -%option nounput + +REGEX_a [a-z_A-Z\x80-\xFF] +REGEX_w [a-z_A-Z0-9\x80-\xFF] %x Start %x SkipInvalid %x GetString %x GetStrList %x Include +%x StoreRepl %% @@ -1118,6 +1159,31 @@ static void readIncludeFile(const QCString &incName) <Start>"@INCLUDE_PATH"[ \t]*"=" { BEGIN(GetStrList); g_list=&g_includePathList; g_list->clear(); g_listStr=""; } /* include a g_config file */ <Start>"@INCLUDE"[ \t]*"=" { BEGIN(Include);} +<Start>"$("{REGEX_a}({REGEX_w}|[.-])*")" | // e.g. $(HOME) +<Start>"$("{REGEX_a}({REGEX_w}|[.-])*"("{REGEX_a}({REGEX_w}|[.-])*"))" { // e.g. $(PROGRAMFILES(X86)) + g_localStoreRepl = yytext; + if (g_compareMode == Config::CompareMode::CompressedNoEnv) // the x_noenv + { + BEGIN(StoreRepl); + } + else + { + substEnvVarsInString(g_localStoreRepl); + unput_string(g_localStoreRepl.data(),g_localStoreRepl.length()); + } + } +<Start>"@"{REGEX_a}{REGEX_w}*"@" { + if (g_compareMode == Config::CompareMode::CompressedNoEnv) // the x_noenv + { + g_localStoreRepl = yytext; + BEGIN(StoreRepl); + } + else + { + config_warn("ignoring unknown '%s' at line %d, file %s\n", + yytext,g_yyLineNr,qPrint(g_yyFileName)); + } + } <Include>([^ \"\t\r\n]+)|("\""[^\n\"]+"\"") { readIncludeFile(configStringRecode(yytext,g_encoding,"UTF-8")); BEGIN(Start); @@ -1146,6 +1212,25 @@ static void readIncludeFile(const QCString &incName) <Start>[a-z_A-Z0-9]+ { config_warn("ignoring unknown tag '%s' at line %d, file %s\n",yytext,g_yyLineNr,qPrint(g_yyFileName)); } /*-------------- GetString ---------------*/ +<StoreRepl>\n { + g_localStoreRepl += yytext; + processStoreRepl(g_localStoreRepl); + g_config->appendStoreRepl(g_localStoreRepl + "\n"); + g_localStoreRepl.resize(0); + g_yyLineNr++; // end of string + BEGIN(Start); + } +<StoreRepl>\\[ \r\t]*\n { g_yyLineNr++; // line continuation + g_localStoreRepl += yytext; + } +<StoreRepl>"\\" { // escape character + g_localStoreRepl += yytext; + } +<StoreRepl>[^\n\\]+ { // string part without escape characters + g_localStoreRepl += yytext; + } + /*-------------- GetString ---------------*/ + <GetString>\n { processString(); g_yyLineNr++; // end of string BEGIN(Start); @@ -1224,14 +1309,19 @@ void ConfigImpl::writeTemplate(TextStream &t,bool sl,bool upd) } } -void ConfigImpl::compareDoxyfile(TextStream &t) +void ConfigImpl::compareDoxyfile(TextStream &t,Config::CompareMode compareMode) { t << "# Difference with default Doxyfile " << getFullVersion(); t << "\n"; for (const auto &option : m_options) { option->m_userComment = ""; - option->compareDoxyfile(t); + option->compareDoxyfile(t,compareMode); + } + if (!m_storeRepl.isEmpty()) + { + t << "\n"; + t << takeStoreRepl() << "\n"; } } @@ -1246,11 +1336,11 @@ void ConfigImpl::writeXMLDoxyfile(TextStream &t) t << "</doxyfile>\n"; } -void ConfigImpl::convertStrToVal() +void ConfigImpl::convertStrToVal(Config::CompareMode compareMode) { for (const auto &option : m_options) { - option->convertStrToVal(); + option->convertStrToVal(compareMode); } } void ConfigImpl::emptyValueToDefault() @@ -1261,6 +1351,17 @@ void ConfigImpl::emptyValueToDefault() } } +static const reg::Ex reEnvVar(R"(\$\((\a[\w.-]*)\))"); // e.g. $(HOME) +static const reg::Ex reEnvVarExt(R"(\$\((\a[\w.-]*\(\a[\w.-]*\))\))"); // e.g. $(PROGRAMFILES(X86)) +static const reg::Ex reEnvVarCMake(R"(@\a\w*@)"); // CMake type replacement + +static bool containsEnvVar(QCString &str) +{ + reg::Match m; + std::string s = str.str(); + return reg::search(s,m,reEnvVar) || reg::search(s,m,reEnvVarExt) || reg::search(s,m,reEnvVarCMake); +} + static void substEnvVarsInString(QCString &str) { if (str.isEmpty()) return; @@ -1286,10 +1387,7 @@ static void substEnvVarsInString(QCString &str) return result; }; - // match e.g. re1=$(HOME) but also re2=$(PROGRAMFILES(X86)) - static const reg::Ex re1(R"(\$\((\a[\w.-]*)\))"); - static const reg::Ex re2(R"(\$\((\a[\w.-]*\(\a[\w.-]*\))\))"); - str = QCString(replace(replace(str.str(),re1),re2)).stripWhiteSpace(); + str = QCString(replace(replace(str.str(),reEnvVar),reEnvVarExt)).stripWhiteSpace(); } static void substEnvVarsInStrList(StringVector &sl) @@ -1298,7 +1396,15 @@ static void substEnvVarsInStrList(StringVector &sl) for (const auto &s : sl) { QCString result = s.c_str(); - bool wasQuoted = (result.find(' ')!=-1) || (result.find('\t')!=-1) || (result.find('"')!=-1); + bool wasQuoted = ((result.at(0)=='"') && (result.at(result.length()-1)=='"')); + if (wasQuoted) + { + result = result.mid(1,result.length()-2); + } + else + { + wasQuoted = (result.find(' ')!=-1) || (result.find('\t')!=-1) || (result.find('"')!=-1); + } // here we strip the quote again substEnvVarsInString(result); @@ -1518,7 +1624,6 @@ static bool checkFileName(const QCString &s,const char *optionName) return true; } -#include "config.h" void Config::init() { @@ -1583,6 +1688,33 @@ static void adjustStringSetting(const char *depOption, const char *optionName,co } } +static void adjustColorStyleSetting(const char *depOption) +{ + auto updateColorStyle = [&depOption](HTML_COLORSTYLE_t curStyle,HTML_COLORSTYLE_t newStyle) + { + err("When enabling '%s' the 'HTML_COLORSTYLE' option should be either 'LIGHT' or 'DARK' but has value '%s'. I'll adjust it for you to '%s'.\n", + depOption, + qPrint(HTML_COLORSTYLE_enum2str(curStyle)), + qPrint(HTML_COLORSTYLE_enum2str(newStyle))); + Config_updateEnum(HTML_COLORSTYLE,newStyle); + }; + auto colorStyle = Config_getEnum(HTML_COLORSTYLE); + switch (colorStyle) + { + case HTML_COLORSTYLE_t::LIGHT: + case HTML_COLORSTYLE_t::DARK: + // no adjustment needed + break; + case HTML_COLORSTYLE_t::AUTO_LIGHT: + case HTML_COLORSTYLE_t::TOGGLE: + updateColorStyle(colorStyle,HTML_COLORSTYLE_t::LIGHT); + break; + case HTML_COLORSTYLE_t::AUTO_DARK: + updateColorStyle(colorStyle,HTML_COLORSTYLE_t::DARK); + break; + } +} + void Config::checkAndCorrect(bool quiet, const bool check) { @@ -1776,8 +1908,8 @@ void Config::checkAndCorrect(bool quiet, const bool check) for (const auto &alias : aliasList) { // match aliases of the form re1='name=' and re2='name{2} =' - static const reg::Ex re1(R"(^\a\w*\s*=)"); - static const reg::Ex re2(R"(^\a\w*{\d+}\s*=)"); + static const reg::Ex re1(R"(^\a[\w-]*\s*=)"); + static const reg::Ex re2(R"(^\a[\w-]*{\d+}\s*=)"); if (!reg::search(alias,re1) && !reg::search(alias,re2)) { err("Illegal ALIASES format '%s'. Use \"name=value\" or \"name{n}=value\", where n is the number of arguments\n", @@ -1798,6 +1930,10 @@ void Config::checkAndCorrect(bool quiet, const bool check) checkList(Config_getList(FILTER_SOURCE_PATTERNS),"FILTER_SOURCE_PATTERNS",FALSE,FALSE); //------------------------ + // check INPUT_FILE_ENCODING + checkList(Config_getList(INPUT_FILE_ENCODING),"INPUT_FILE_ENCODING",TRUE,TRUE); + + //------------------------ // check TAGFILES checkList(Config_getList(TAGFILES),"TAGFILES",FALSE,TRUE); @@ -1826,6 +1962,7 @@ void Config::checkAndCorrect(bool quiet, const bool check) adjustBoolSetting( depOption, "HTML_DYNAMIC_MENUS", false ); adjustBoolSetting( depOption, "HTML_DYNAMIC_SECTIONS",false ); adjustStringSetting(depOption, "HTML_FILE_EXTENSION", ".html"); + adjustColorStyleSetting(depOption); } // check for settings that are inconsistent with having INLINE_GROUPED_CLASSES enabled @@ -1836,16 +1973,6 @@ void Config::checkAndCorrect(bool quiet, const bool check) } //------------------------ - // correct DOT_FONTNAME if needed - QCString dotFontName=Config_getString(DOT_FONTNAME); - if (dotFontName=="FreeSans" || dotFontName=="FreeSans.ttf") - { - warn_uncond("doxygen no longer ships with the FreeSans font.\n" - " You may want to clear or change DOT_FONTNAME.\n" - " Otherwise you run the risk that the wrong font is being used for dot generated graphs.\n"); - } - - //------------------------ // clip number of threads int dotNumThreads = Config_getInt(DOT_NUM_THREADS); if (dotNumThreads>32) @@ -2035,6 +2162,12 @@ void Config::checkAndCorrect(bool quiet, const bool check) } +static void updateAttribute(DotAttributes& attr, QCString name, ConfigObsolete* value) +{ + if (value==0 || value->valueStringRef()->isEmpty()) return; + attr.updateValue(name,*value->valueStringRef()); +} + void Config::updateObsolete() { //------------------------ @@ -2063,6 +2196,29 @@ void Config::updateObsolete() } } } + + auto fontname = dynamic_cast<ConfigObsolete*>(ConfigImpl::instance()->get("DOT_FONTNAME")); + auto fontsize = dynamic_cast<ConfigObsolete*>(ConfigImpl::instance()->get("DOT_FONTSIZE")); + + // correct DOT_FONTNAME if needed + if (fontname && + (*fontname->valueStringRef() == "FreeSans" + || *fontname->valueStringRef() == "FreeSans.ttf")) + warn_uncond("doxygen no longer ships with the FreeSans font.\n" + " You may want to clear or change DOT_FONTNAME.\n" + " Otherwise you run the risk that the wrong font is being used for dot generated graphs.\n"); + + QCString& commonAttrStr = *dynamic_cast<ConfigString*>(ConfigImpl::instance()->get("DOT_COMMON_ATTR"))->valueRef(); + DotAttributes commonAttr(commonAttrStr); + updateAttribute(commonAttr, "fontname", fontname); + updateAttribute(commonAttr, "fontsize", fontsize); + commonAttrStr = commonAttr.str(); + + QCString& edgeAttrStr = *dynamic_cast<ConfigString*>(ConfigImpl::instance()->get("DOT_EDGE_ATTR"))->valueRef(); + DotAttributes edgeAttr(edgeAttrStr); + updateAttribute(edgeAttr, "labelfontname", fontname); + updateAttribute(edgeAttr, "labelfontsize", fontsize); + edgeAttrStr = edgeAttr.str(); } void Config::writeTemplate(TextStream &t,bool shortList,bool update) @@ -2070,10 +2226,10 @@ void Config::writeTemplate(TextStream &t,bool shortList,bool update) ConfigImpl::instance()->writeTemplate(t,shortList,update); } -void Config::compareDoxyfile(TextStream &t,DoxyfileSettings diffList) +void Config::compareDoxyfile(TextStream &t,Config::CompareMode compareMode) { - postProcess(FALSE, diffList); - ConfigImpl::instance()->compareDoxyfile(t); + postProcess(FALSE, compareMode); + ConfigImpl::instance()->compareDoxyfile(t, compareMode); } void Config::writeXMLDoxyfile(TextStream &t) @@ -2081,8 +2237,9 @@ void Config::writeXMLDoxyfile(TextStream &t) ConfigImpl::instance()->writeXMLDoxyfile(t); } -bool Config::parse(const QCString &fileName,bool update) +bool Config::parse(const QCString &fileName,bool update, Config::CompareMode compareMode) { + g_compareMode = compareMode; bool parseRes = ConfigImpl::instance()->parse(fileName,update); if (!parseRes) return parseRes; @@ -2094,11 +2251,12 @@ bool Config::parse(const QCString &fileName,bool update) return parseRes; } -void Config::postProcess(bool clearHeaderAndFooter, DoxyfileSettings diffList) +void Config::postProcess(bool clearHeaderAndFooter, Config::CompareMode compareMode) { - if (diffList != DoxyfileSettings::CompressedNoEnv) ConfigImpl::instance()->substituteEnvironmentVars(); - if (diffList == DoxyfileSettings::Full)ConfigImpl::instance()->emptyValueToDefault(); - ConfigImpl::instance()->convertStrToVal(); + auto configInst = ConfigImpl::instance(); + if (compareMode != CompareMode::CompressedNoEnv) configInst->substituteEnvironmentVars(); + if (compareMode == CompareMode::Full) configInst->emptyValueToDefault(); + configInst->convertStrToVal(compareMode); // avoid bootstrapping issues when the g_config file already // refers to the files that we are supposed to parse. diff --git a/src/context.cpp b/src/context.cpp index 34c351e..346e28d 100644 --- a/src/context.cpp +++ b/src/context.cpp @@ -1034,7 +1034,7 @@ const PropertyMap<TranslateContext::Private> TranslateContext::Private::s_inst { { "classIndex", &Private::classIndex }, //%% string concepts { "concepts", &Private::concepts }, - //%% string conceptDefintion + //%% string conceptDefinition { "conceptDefinition", &Private::conceptDefinition }, //%% string namespaceIndex { "namespaceIndex", &Private::namespaceIndex }, @@ -7022,7 +7022,7 @@ class NavPathElemContext::Private } else if (type==Definition::TypeClass) { - if (text.right(2)=="-p") + if (text.endsWith("-p")) { text = text.left(text.length()-2); } diff --git a/src/datetime.cpp b/src/datetime.cpp new file mode 100644 index 0000000..b5ec3b3 --- /dev/null +++ b/src/datetime.cpp @@ -0,0 +1,231 @@ +/****************************************************************************** + * + * Copyright (C) 1997-2022 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +#include <cstdlib> +#include <chrono> +#include <memory> +#include <array> +#include <functional> + +#include "regex.h" +#include "datetime.h" +#include "config.h" +#include "portable.h" +#include "language.h" +#include "message.h" +#include "growbuf.h" + +std::tm getCurrentDateTime() +{ + QCString sourceDateEpoch = Portable::getenv("SOURCE_DATE_EPOCH"); + if (!sourceDateEpoch.isEmpty()) // see https://reproducible-builds.org/specs/source-date-epoch/ + { + bool ok; + uint64 epoch = sourceDateEpoch.toUInt64(&ok); + if (!ok) + { + static bool warnedOnce=FALSE; + if (!warnedOnce) + { + warn_uncond("Environment variable SOURCE_DATE_EPOCH does not contain a valid number; value is '%s'\n", + qPrint(sourceDateEpoch)); + warnedOnce=TRUE; + } + } + else // use given epoch value as current 'built' time + { + auto epoch_start = std::chrono::time_point<std::chrono::system_clock>{}; + auto epoch_seconds = std::chrono::seconds(epoch); + auto build_time = epoch_start + epoch_seconds; + std::time_t time = std::chrono::system_clock::to_time_t(build_time); + return *gmtime(&time); + } + } + + // return current local time + auto now = std::chrono::system_clock::now(); + std::time_t time = std::chrono::system_clock::to_time_t(now); + return *localtime(&time); +} + +QCString dateToString(bool includeTime) +{ + auto current = getCurrentDateTime(); + return theTranslator->trDateTime(current.tm_year + 1900, + current.tm_mon + 1, + current.tm_mday, + (current.tm_wday+6)%7+1, // map: Sun=0..Sat=6 to Mon=1..Sun=7 + current.tm_hour, + current.tm_min, + current.tm_sec, + includeTime); +} + +QCString yearToString() +{ + auto current = getCurrentDateTime(); + return QCString().setNum(current.tm_year+1900); +} + +struct SpecFormat +{ + const reg::Ex re; + int count; + int offset; + int format; +}; + +using TMFieldAssigner = std::function< void(std::tm &,int value) >; + +struct DateTimeField +{ + TMFieldAssigner assigner; + int minVal; + int maxVal; + const char *name; +}; + +static std::array<SpecFormat,5> g_specFormats +{{ + // regular expression, num values, offset, format bits + { std::string(R"((\d+)-(\d+)-(\d+)\s*(\d+):(\d+):(\d+))"), 6, 0, SF_Date|SF_Time|SF_Seconds }, // format 13-04-2015 12:34:56 + { std::string(R"((\d+)-(\d+)-(\d+)\s*(\d+):(\d+))"), 5, 0, SF_Date|SF_Time }, // format 13-04-2015 12:34 + { std::string(R"((\d+)-(\d+)-(\d+))"), 3, 0, SF_Date }, // format 13-04-2015 + { std::string(R"((\d+):(\d+):(\d+))"), 3, 3, SF_Time|SF_Seconds }, // format 12:34:56 + { std::string(R"((\d+):(\d+))"), 2, 3, SF_Time } // format 12:34 +}}; + +static std::array<DateTimeField,6> g_assignValues +{{ + // assigner, minVal, maxVal, name + { [](std::tm &tm,int value) { tm.tm_year = value-1900; }, 1900, 9999, "year" }, + { [](std::tm &tm,int value) { tm.tm_mon = value-1; }, 1, 12, "month" }, + { [](std::tm &tm,int value) { tm.tm_mday = value; }, 1, 31, "day" }, + { [](std::tm &tm,int value) { tm.tm_hour = value; }, 0, 23, "hour" }, + { [](std::tm &tm,int value) { tm.tm_min = value; }, 0, 59, "minute" }, + { [](std::tm &tm,int value) { tm.tm_sec = value; }, 0, 59, "second" } +}}; + +static void determine_weekday( std::tm& tm ) +{ + auto cpy = tm; + // there are some problems when the hr:min:sec are on 00:00:00 in determining the weekday + cpy.tm_hour = 12; + const auto as_time_t = std::mktime( &cpy ) ; + if (as_time_t != -1) + { + cpy = *std::localtime( &as_time_t ) ; + tm.tm_wday = cpy.tm_wday; + } +} + +QCString dateTimeFromString(const QCString &spec,std::tm &dt,int &format) +{ + // for an empty spec field return the current date and time + dt = getCurrentDateTime(); + if (spec.isEmpty()) + { + format = SF_Date | SF_Time | SF_Seconds; + return QCString(); + } + + // find a matching pattern + std::string s = spec.str(); + for (const auto &fmt : g_specFormats) + { + reg::Match m; + if (reg::match(s,m,fmt.re)) // match found + { + for (int i=0; i<fmt.count; i++) + { + int value = std::atoi(m[i+1].str().c_str()); + const DateTimeField &dtf = g_assignValues[i+fmt.offset]; + if (value<dtf.minVal || value>dtf.maxVal) // check if the value is in the expected range + { + return QCString().sprintf("value for %s is %d which is outside of the value range [%d..%d]", + dtf.name, value, dtf.minVal, dtf.maxVal); + } + dtf.assigner(dt,value); + } + format = fmt.format; + if (format&SF_Date) // if we have a date also determine the weekday + { + determine_weekday(dt); + } + return QCString(); + } + } + + // no matching pattern found + return "invalid or non representable date/time argument"; +} + +QCString formatDateTime(const QCString &format,const std::tm &dt,int &formatUsed) +{ + formatUsed = 0; + auto getYear = [](const std::tm &dat) { return dat.tm_year+1900; }; + auto getMonth = [](const std::tm &dat) { return dat.tm_mon+1; }; + auto getDay = [](const std::tm &dat) { return dat.tm_mday; }; + auto getDayOfWeek = [](const std::tm &dat) { return (dat.tm_wday+6)%7+1; }; + GrowBuf growBuf; + char c; + const char *p = format.data(); + const char *fmt_zero = "%02d"; + const char *fmt_nonzero = "%d"; + const char *fmt_selected = 0; + if (p==0) return QCString(); + while ((c=*p++)) + { + char nc = *p; + switch (c) + { + case '%': + fmt_selected = nc=='-' ? fmt_nonzero : fmt_zero; // %-H produces 1 and %H produces 01 + if (nc=='-') nc=*++p; // skip over - + switch (nc) + { + case '%': growBuf.addChar('%'); break; + case 'y': growBuf.addInt(fmt_selected,getYear(dt)%100); formatUsed|=SF_Date; break; + case 'Y': growBuf.addInt("%d",getYear(dt)); formatUsed|=SF_Date; break; + case 'm': growBuf.addInt(fmt_selected,getMonth(dt)); formatUsed|=SF_Date; break; + case 'b': growBuf.addStr(theTranslator->trMonth(getMonth(dt),false,false)); formatUsed|=SF_Date; break; + case 'B': growBuf.addStr(theTranslator->trMonth(getMonth(dt),false,true)); formatUsed|=SF_Date; break; + case 'd': growBuf.addInt(fmt_selected,getDay(dt)); formatUsed|=SF_Date; break; + case 'u': growBuf.addInt("%d",getDayOfWeek(dt)); /* Monday = 1 ... Sunday = 7 */ formatUsed|=SF_Date; break; + case 'w': growBuf.addInt("%d",getDayOfWeek(dt)%7); /* Sunday = 0 ... Saturday = 6 */ formatUsed|=SF_Date; break; + case 'a': growBuf.addStr(theTranslator->trDayOfWeek(getDayOfWeek(dt),false,false)); formatUsed|=SF_Date; break; + case 'A': growBuf.addStr(theTranslator->trDayOfWeek(getDayOfWeek(dt),false,true)); formatUsed|=SF_Date; break; + case 'H': growBuf.addInt(fmt_selected,dt.tm_hour); formatUsed|=SF_Time; break; + case 'I': growBuf.addInt(fmt_selected,dt.tm_hour%12); formatUsed|=SF_Time; break; + case 'p': growBuf.addStr(theTranslator->trDayPeriod(dt.tm_hour>=12?1:0)); formatUsed|=SF_Time; break; + case 'M': growBuf.addInt(fmt_selected,dt.tm_min); formatUsed|=SF_Time; break; + case 'S': growBuf.addInt(fmt_selected,dt.tm_sec); formatUsed|=SF_Seconds; break; + default: + growBuf.addChar(c); + if (*(p-1)=='-') growBuf.addChar('-'); + growBuf.addChar(nc); + break; + } + p++; + break; + default: + growBuf.addChar(c); + break; + } + } + growBuf.addChar(0); + return growBuf.get(); +} + diff --git a/src/datetime.h b/src/datetime.h new file mode 100644 index 0000000..746c1dc --- /dev/null +++ b/src/datetime.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * + * Copyright (C) 1997-2022 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +#ifndef DATETIME_H +#define DATETIME_H + +#include <ctime> +#include "qcstring.h" + +/** @brief Date and time related functions. */ + +constexpr int SF_Date = 1<<0; //!< a date is presenting in the format string +constexpr int SF_Time = 1<<1; //!< a time is presenting in the format string +constexpr int SF_Seconds = 1<<2; //!< the seconds are presenting in the format string +constexpr int SF_NumBits = 3; //!< number of bits in SF vector + +/** Helper function that returns the name related one of the SF bits. Used for generating warnings. + * @param[in] bitNumber bit value in range [0..SF_NumBits) for which to return the string value. + */ +constexpr const char *SF_bit2str(int bitNumber) +{ + constexpr const char *partNames[] = { "date", "time", "seconds" }; + return bitNumber>=0 && bitNumber<SF_NumBits ? partNames[bitNumber] : ""; +} + +/** Returns the filled in std::tm for a given string representing a date and/or time. + * + * @param[in] spec The string representation of the date and/or time + * Possible formats: + * - the empty string: the current date and time is returned + * - `YYYY-MM-DD HH:MM:SS`: the date and time are fully specified + * - `YYYY-MM-DD HH:MM`: the date and time without seconds + * - `YYYY-MM-DD`: the date without time + * - `HH:MM:SS`: the time with seconds but without date + * - `HH:MM`: the time without seconds and without date + * + * @param[out] dt The corresponding datetime value. + * @param[out] format The parts that have been found in spec; a bitwise or + * of `SF_Date`, `SF_Time` and `SF_Seconds`. + * @returns An empty string if the spec has a supported format, + * or an error message if the format is invalid. + */ +QCString dateTimeFromString(const QCString &spec,std::tm &dt,int &format); + + +/** Return a string representation for a given std::tm value that is formatted according to the + * pattern given by a format. + * @param[in] format the string used for format the date and time, e.g. `%Y-%m-%d` + * @param[in] dt the date and time value to fill in + * @param[out] formatUsed A bitwise OR of `SF_Date`, `SF_Time` and `SF_Seconds` representing the + * the types of markers found in the format string. + */ +QCString formatDateTime(const QCString &format,const std::tm &dt,int &formatUsed); + +/** Returns the filled in std::tm for the current date and time */ +std::tm getCurrentDateTime(); + +/** Returns the current year as a string */ +QCString yearToString(); + +#endif diff --git a/src/debug.cpp b/src/debug.cpp index 841bc46..fdf3138 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -36,7 +36,6 @@ static std::map< std::string, Debug::DebugMask > s_labels = { "classes", Debug::Classes }, { "commentcnv", Debug::CommentCnv }, { "commentscan", Debug::CommentScan }, - { "validate", Debug::Validate }, { "printtree", Debug::PrintTree }, { "time", Debug::Time }, { "extcmd", Debug::ExtCmd }, diff --git a/src/debug.h b/src/debug.h index e2f321e..ef5620b 100644 --- a/src/debug.h +++ b/src/debug.h @@ -30,7 +30,6 @@ class Debug Classes = 0x00000010, CommentCnv = 0x00000020, CommentScan = 0x00000040, - Validate = 0x00000080, PrintTree = 0x00000100, Time = 0x00000200, ExtCmd = 0x00000400, diff --git a/src/declinfo.l b/src/declinfo.l index 2e578b0..4a25ce6 100644 --- a/src/declinfo.l +++ b/src/declinfo.l @@ -127,6 +127,16 @@ ID ([$a-z_A-Z\x80-\xFF][$a-z_A-Z0-9\x80-\xFF]*)|(@[0-9]+) addTypeName(yyscanner); yyextra->name += removeRedundantWhiteSpace(QCString(yytext)); } +<Start>"anonymous_namespace{"[^}]+"}" { // anonymous namespace + if (!yyextra->scope.isEmpty()) + { + yyextra->scope+=QCString("::")+yytext; + } + else + { + yyextra->scope = yytext; + } + } <Start>([~!]{B}*)?{ID}/({B}*"["{B}*"]")* { // the []'s are for Java, // the / was add to deal with multi- // dimensional C++ arrays like A[][15] @@ -136,7 +146,7 @@ ID ([$a-z_A-Z\x80-\xFF][$a-z_A-Z0-9\x80-\xFF]*)|(@[0-9]+) yyextra->name += removeRedundantWhiteSpace(QCString(yytext)); } <Start>{B}*"::"{B}* { // found a yyextra->scope specifier - if (!yyextra->scope.isEmpty()) + if (!yyextra->scope.isEmpty() && !yyextra->scope.endsWith("::")) { yyextra->scope+="::"+yyextra->name; // add yyextra->name to yyextra->scope } @@ -347,28 +357,6 @@ void parseFuncDecl(const QCString &decl,const SrcLangExt lang,QCString &cl,QCStr yyextra->name=yyextra->name.left(nb); } -#if 0 - { - int l=yyextra->scope.length(); - int i=0; - int skipCount=0; - cl.resize(0); - ctl.resize(0); - for (i=0;i<l;i++) - { - char c=yyextra->scope.at(i); - if (c=='<') - skipCount++; - else if (c=='>') - skipCount--; - else if (skipCount==0) - cl+=c; - } - } - cl=stripTemplateSpecifiersFromScope(removeRedundantWhiteSpace(yyextra->scope),FALSE); - ctl.resize(0); -#endif - cl=yyextra->scope; n=removeRedundantWhiteSpace(yyextra->name); int il,ir; diff --git a/src/defargs.l b/src/defargs.l index 121b21b..e96664a 100644 --- a/src/defargs.l +++ b/src/defargs.l @@ -437,8 +437,8 @@ CPPC "/\/" // and need to correct it to avoid seeing a nameless parameter // "struct A" as a parameter with type "struct" and name "A". int sv=0; - if (a.type.left(6)=="const ") sv=6; - else if (a.type.left(9)=="volatile ") sv=9; + if (a.type.startsWith("const ")) sv=6; + else if (a.type.startsWith("volatile ")) sv=9; if (a.type.mid(sv)=="struct" || a.type.mid(sv)=="union" || diff --git a/src/define.h b/src/define.h index 6cbb636..0a0dc8f 100644 --- a/src/define.h +++ b/src/define.h @@ -42,6 +42,7 @@ class Define bool varArgs = FALSE; bool isPredefined = FALSE; bool nonRecursive = FALSE; + bool expandAsDefined = FALSE; }; /** List of all macro definitions */ diff --git a/src/definition.cpp b/src/definition.cpp index cd256bf..e8136a7 100644 --- a/src/definition.cpp +++ b/src/definition.cpp @@ -3,12 +3,12 @@ #include <iterator> #include <unordered_map> #include <string> - #include <ctype.h> -#include "md5.h" #include <stdio.h> #include <stdlib.h> #include <assert.h> + +#include "md5.h" #include "regex.h" #include "config.h" #include "definitionimpl.h" @@ -34,6 +34,8 @@ #include "bufstr.h" #include "reflist.h" #include "utf8.h" +#include "indexlist.h" +#include "fileinfo.h" //----------------------------------------------------------------------------------------- @@ -208,6 +210,7 @@ static void addToMap(const QCString &name,Definition *d) if (!vhdlOpt && index!=-1) symbolName=symbolName.mid(index+2); if (!symbolName.isEmpty()) { + //printf("adding symbol %s\n",qPrint(symbolName)); Doxygen::symbolMap->add(symbolName,d); d->_setSymbolName(symbolName); @@ -370,10 +373,10 @@ void DefinitionImpl::addSectionsToIndex() } QCString title = si->title(); if (title.isEmpty()) title = si->label(); - // determine if there is a next level inside this item + // determine if there is a next level inside this item, but be aware of the anchor and table section references. auto it_next = std::next(it); bool isDir = (it_next!=m_impl->sectionRefs.end()) ? - (static_cast<int>((*it_next)->type()) > nextLevel) : FALSE; + (isSection((*it_next)->type()) && static_cast<int>((*it_next)->type()) > nextLevel) : FALSE; Doxygen::indexList->addContentsItem(isDir,title, getReference(), m_impl->def->getOutputFileBase(), @@ -849,7 +852,7 @@ bool readCodeFragment(const QCString &fileName, Debug::print(Debug::FilterOutput,0,"-------------\n%s\n-------------\n",qPrint(result)); } } - result = transcodeCharacterStringToUTF8(result); + result = transcodeCharacterStringToUTF8(getEncoding(FileInfo(fileName.str())),result); if (!result.isEmpty() && result.at(result.length()-1)!='\n') result += "\n"; //printf("readCodeFragment(%d-%d)=%s\n",startLine,endLine,qPrint(result)); return found; @@ -1365,7 +1368,7 @@ QCString DefinitionImpl::navigationPathAsString() const else if (m_impl->def->definitionType()==Definition::TypeClass) { QCString name = locName; - if (name.right(2)=="-p" /*|| name.right(2)=="-g"*/) + if (name.endsWith("-p")) { name = name.left(name.length()-2); } @@ -1565,12 +1568,12 @@ QCString DefinitionImpl::documentation() const int DefinitionImpl::docLine() const { - return m_impl->details ? m_impl->details->line : 1; + return m_impl->details ? m_impl->details->line : m_impl->brief ? m_impl->brief->line : 1; } QCString DefinitionImpl::docFile() const { - return m_impl->details ? m_impl->details->file : QCString("<"+m_impl->name+">"); + return m_impl->details ? m_impl->details->file : m_impl->brief ? m_impl->brief->file : QCString("<"+m_impl->name+">"); } //---------------------------------------------------------------------------- diff --git a/src/diagram.cpp b/src/diagram.cpp index 6cfbb17..c6d18b5 100644 --- a/src/diagram.cpp +++ b/src/diagram.cpp @@ -26,7 +26,7 @@ #include "util.h" #include "doxygen.h" #include "portable.h" -#include "index.h" +#include "indexlist.h" #include "classlist.h" #include "textstream.h" @@ -216,7 +216,7 @@ static void writeBitmapBox(DiagramItem *di,Image *image, uint x,uint y,uint w,uint h,bool firstRow, bool hasDocs,bool children=FALSE) { - uchar colFill = hasDocs ? (firstRow ? 0 : 2) : 7; + uchar colFill = hasDocs ? (firstRow ? 8 : 2) : 7; uchar colBorder = (firstRow || !hasDocs) ? 1 : 3; uint l = Image::stringLength(di->label()); uint mask=virtToMask(di->virtualness()); @@ -287,7 +287,7 @@ QCString DiagramItem::label() const // we use classDef->name() here and not displayName() in order // to get the name used in the inheritance relation. QCString n = m_classDef->name(); - if (/*n.right(2)=="-g" ||*/ n.right(2)=="-p") + if (n.endsWith("-p")) { n = n.left(n.length()-2); } diff --git a/src/dir.cpp b/src/dir.cpp index 36c7051..4fc06ea 100644 --- a/src/dir.cpp +++ b/src/dir.cpp @@ -60,8 +60,9 @@ std::string DirEntry::path() const struct DirIterator::Private { Private() : it() {} - Private(const std::string &path) : it(path) {} + Private(const std::string &path) : it(path,ec) {} fs::directory_iterator it; + std::error_code ec; mutable DirEntry current; }; diff --git a/src/dispatcher.h b/src/dispatcher.h new file mode 100644 index 0000000..fc03598 --- /dev/null +++ b/src/dispatcher.h @@ -0,0 +1,101 @@ +/****************************************************************************** + * + * Copyright (C) 1997-2022 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +#ifndef DISPATCHER_H +#define DISPATCHER_H + +#include <cstddef> +#include <utility> +#include <functional> +#include <variant> +#include <type_traits> + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// idea based on https://mpark.github.io/programming/2019/01/22/variant-visitation-v2/ + +namespace detail +{ + +//! Represents an unreachable piece of code +#ifdef __GNUC__ // GCC 4.8+, Clang, Intel and other compilers compatible with GCC (-std=c++0x or above) +[[noreturn]] inline __attribute__((always_inline)) void unreachable() { __builtin_unreachable(); } +#elif defined(_MSC_VER) // MSVC +[[noreturn]] __forceinline void unreachable() { __assume(false); } +#else // ??? +#warning "no implementation of unreachable for this compiler!" +inline void unreachable() {} +#endif + +//! generic template declaration for the Dispatcher without implementation +template<bool valid> +struct Dispatcher; + +//! specialization for the invalid case, signaling the compiler that the path is unreachable +template<> +struct Dispatcher<false> +{ + //! case corresponding with the non-existing I'th type of variant V + template<template<class> class W, std::size_t I, class V, class... As> + static constexpr void case_(V &&, As &&...) { unreachable(); } +}; + +//! specialization for the valid case, where `case_` invokes a specific method. +template<> +struct Dispatcher<true> +{ + //! Invokes the method of a class `X` whose method pointer type is defined by `W<X>::method` + //! where `X` matches the I'th type of variant `V` on the object held by `v`. + template<template<class> class W, std::size_t I, class V, class... As> + static constexpr void case_(V &&v, As &&... args) { + using Alt = std::variant_alternative_t<I,std::decay_t<V>>; + std::invoke( W<Alt>::method, std::get<I>( std::forward<V>(v) ), std::forward<As>(args)... ); + } +}; + +} // namespace detail + +//! Invokes the method of a class `X` whose member pointer type is defined by `W<X>::method` +//! where `X` matches the type of the object held by `v` that is stored in a variant `V`. +//! \tparam W a template class where `W<X>::method` points to a method of class `X` one of the types of `V`. +//! \tparam V the type of the variant +//! \tparam As the parameter types used to invoke the method +//! \param v a object of the variant type for which to invoke the method +//! \param args the parameters to pass to the method +//! \note This implementation assumes a maximum of 10 types in the variant (easy to extend though by adding more cases). +template<template<class> class W,class V, class...As> +static constexpr void dispatch_call(V &&v,As &&... args) +{ + // size holds the number of type of variant `V` + constexpr std::size_t size = std::variant_size_v<std::decay_t<V>>; + // which on a worst case maximum number of types in `V`. + // fills in an unreachable branch for indices equal or higher than size. + switch (v.index()) + { +#define DISPATCH_AT(n) case n: detail::Dispatcher< ((n)<size) >::template case_<W,n>(v,std::forward<As>(args)...); break; + DISPATCH_AT(0) + DISPATCH_AT(1) + DISPATCH_AT(2) + DISPATCH_AT(3) + DISPATCH_AT(4) + DISPATCH_AT(5) + DISPATCH_AT(6) + DISPATCH_AT(7) + DISPATCH_AT(8) + DISPATCH_AT(9) +#undef DISPATCH_AT + } +} + +#endif // DISPATCHER_H diff --git a/src/docbookgen.cpp b/src/docbookgen.cpp index ac502a2..00a0e86 100644 --- a/src/docbookgen.cpp +++ b/src/docbookgen.cpp @@ -335,7 +335,7 @@ DB_GEN_C } pageName = fileName; relPath = relativePathToRoot(fileName); - if (fileName.right(4)!=".xml") fileName+=".xml"; + if (!fileName.endsWith(".xml")) fileName+=".xml"; startPlainFile(fileName); m_codeGen.setRelativePath(relPath); m_codeGen.setSourceFileName(stripPath(fileName)); @@ -761,12 +761,12 @@ DB_GEN_C void DocbookGenerator::writeNonBreakableSpace(int n) { DB_GEN_C - for (int i=0;i<n;i++) m_t << " "; + for (int i=0;i<n;i++) m_t << " "; } void DocbookGenerator::lineBreak(const QCString &) { DB_GEN_C - m_t << "\n"; + m_t << "<?linebreak?>"; } void DocbookGenerator::startTypewriter() { diff --git a/src/docbookvisitor.cpp b/src/docbookvisitor.cpp index 58bf25e..27d5bef 100644 --- a/src/docbookvisitor.cpp +++ b/src/docbookvisitor.cpp @@ -245,7 +245,7 @@ void DocbookDocVisitor::operator()(const DocLineBreak &) { DB_VIS_C if (m_hide) return; - m_t << "\n<literallayout> 
</literallayout>\n"; + m_t << "<?linebreak?>"; // gives nicer results but gives problems as it is not allowed in <pare> and also problems with dblatex // m_t << "\n" << "<sbr/>\n"; } @@ -306,20 +306,8 @@ DB_VIS_C case DocStyleChange::Ins: break; case DocStyleChange::Div: /* HTML only */ break; case DocStyleChange::Span: /* HTML only */ break; - case DocStyleChange::Details: /* emulation of the <details> tag */ - if (s.enable()) - { - m_t << "\n"; - m_t << "<para>"; - } - else - { - m_t << "</para>"; - m_t << "\n"; - } - break; case DocStyleChange::Summary: /* emulation of the <summary> tag inside a <details> tag */ - if (s.enable()) m_t << "<emphasis role=\"bold\">"; else m_t << "</emphasis>"; + if (s.enable()) m_t << "<para><emphasis role=\"bold\">"; else m_t << "</emphasis></para>"; break; } } @@ -1116,17 +1104,17 @@ DB_VIS_C } else if (opt.name=="class") { - if (opt.value.left(13)=="markdownTable") // handle markdown generated attributes + if (opt.value.startsWith("markdownTable")) // handle markdown generated attributes { - if (opt.value.right(5)=="Right") + if (opt.value.endsWith("Right")) { m_t << " align='right'"; } - else if (opt.value.right(4)=="Left") + else if (opt.value.endsWith("Left")) { m_t << " align='left'"; } - else if (opt.value.right(6)=="Center") + else if (opt.value.endsWith("Center")) { m_t << " align='center'"; } @@ -1184,6 +1172,17 @@ DB_VIS_C m_t << "</link>"; } +void DocbookDocVisitor::operator()(const DocHtmlDetails &d) +{ +DB_VIS_C + if (m_hide) return; + m_t << "\n"; + m_t << "<para>"; + visitChildren(d); + m_t << "</para>"; + m_t << "\n"; +} + void DocbookDocVisitor::operator()(const DocHtmlHeader &h) { DB_VIS_C diff --git a/src/docbookvisitor.h b/src/docbookvisitor.h index 1a05f00..15533ff 100644 --- a/src/docbookvisitor.h +++ b/src/docbookvisitor.h @@ -86,6 +86,7 @@ class DocbookDocVisitor : public DocVisitor void operator()(const DocHtmlCaption &); void operator()(const DocInternal &); void operator()(const DocHRef &); + void operator()(const DocHtmlDetails &); void operator()(const DocHtmlHeader &); void operator()(const DocImage &); void operator()(const DocDotFile &); diff --git a/src/docnode.cpp b/src/docnode.cpp index 0b9462c..538ac7a 100644 --- a/src/docnode.cpp +++ b/src/docnode.cpp @@ -31,6 +31,8 @@ #include "vhdldocgen.h" #include "doctokenizer.h" #include "plantuml.h" +#include "language.h" +#include "datetime.h" // debug off #define DBG(x) do {} while(0) @@ -96,7 +98,7 @@ static void unescapeCRef(QCString &s) static QCString stripKnownExtensions(const QCString &text) { QCString result=text; - if (result.right(4)==".tex") + if (result.endsWith(".tex")) { result=result.left(result.length()-4); } @@ -135,7 +137,6 @@ const char *DocStyleChange::styleString() const case DocStyleChange::Del: return "del"; case DocStyleChange::Underline: return "u"; case DocStyleChange::Ins: return "ins"; - case DocStyleChange::Details: return "details"; case DocStyleChange::Summary: return "summary"; } return "<invalid>"; @@ -499,12 +500,12 @@ bool DocXRefItem::parse(DocNodeVariant *thisVariant) DocFormula::DocFormula(DocParser *parser,DocNodeVariant *parent,int id) : DocNode(parser,parent), m_relPath(parser->context.relPath) { - QCString text = FormulaManager::instance().findFormula(id); - if (!text.isEmpty()) + const Formula *formula = FormulaManager::instance().findFormula(id); + if (formula && !formula->text().isEmpty()) { m_id = id; m_name.sprintf("form_%d",m_id); - m_text = text; + m_text = formula->text(); } else // wrong \_form#<n> command { @@ -1021,7 +1022,7 @@ bool DocDotFile::parse(DocNodeVariant *thisVariant) bool ambig; FileDef *fd = findFileDef(Doxygen::dotFileNameLinkedMap,p->name,ambig); - if (fd==0 && p->name.right(4)!=".dot") // try with .dot extension as well + if (fd==0 && !p->name.endsWith(".dot")) // try with .dot extension as well { fd = findFileDef(Doxygen::dotFileNameLinkedMap,p->name+".dot",ambig); } @@ -1059,7 +1060,7 @@ bool DocMscFile::parse(DocNodeVariant *thisVariant) bool ambig; FileDef *fd = findFileDef(Doxygen::mscFileNameLinkedMap,p->name,ambig); - if (fd==0 && p->name.right(4)!=".msc") // try with .msc extension as well + if (fd==0 && !p->name.endsWith(".msc")) // try with .msc extension as well { fd = findFileDef(Doxygen::mscFileNameLinkedMap,p->name+".msc",ambig); } @@ -1099,7 +1100,7 @@ bool DocDiaFile::parse(DocNodeVariant *thisVariant) bool ambig; FileDef *fd = findFileDef(Doxygen::diaFileNameLinkedMap,p->name,ambig); - if (fd==0 && p->name.right(4)!=".dia") // try with .dia extension as well + if (fd==0 && !p->name.endsWith(".dia")) // try with .dia extension as well { fd = findFileDef(Doxygen::diaFileNameLinkedMap,p->name+".dia",ambig); } @@ -1285,6 +1286,29 @@ endheader: } //--------------------------------------------------------------------------- +int DocHtmlDetails::parse(DocNodeVariant *thisVariant) +{ + DBG(("DocHtmlHtmlDetails::parse() start\n")); + int retval=0; + auto ns = AutoNodeStack(parser(),thisVariant); + + // parse one or more paragraphs + bool isFirst=TRUE; + DocPara *par=0; + do + { + auto vDocPara = children().append<DocPara>(parser(),thisVariant); + par = children().get_last<DocPara>(); + if (isFirst) { par->markFirst(); isFirst=FALSE; } + retval=par->parse(vDocPara); + } + while (retval==TK_NEWPARA); + if (par) par->markLast(); + + DBG(("DocHtmlHtmlDetails::parse() end retval=%s\n",DocTokenizer::retvalToString(retval))); + return (retval==RetVal_EndHtmlDetails) ? RetVal_OK : retval; +} + int DocHRef::parse(DocNodeVariant *thisVariant) { int retval=RetVal_OK; @@ -3177,9 +3201,70 @@ int DocPara::handleXRefItem(DocNodeVariant *thisVariant) return retval; } -void DocPara::handleIline(DocNodeVariant *) +void DocPara::handleShowDate(DocNodeVariant *thisVariant) { - parser()->tokenizer.setStateIline(); + DBG(("handleShowDate()\n")); + QCString fmt; + QCString date; + int tok=parser()->tokenizer.lex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after '\\%s' command", + qPrint("showdate")); + return; + } + parser()->tokenizer.setStateQuotedString(); + tok = parser()->tokenizer.lex(); + if (tok!=TK_WORD) + { + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <format> argument for command '\\showdate'"); + parser()->tokenizer.setStatePara(); + return; + } + fmt = parser()->context.token->name; + + parser()->tokenizer.setStateShowDate(); + tok = parser()->tokenizer.lex(); + + QCString specDate = parser()->context.token->name.stripWhiteSpace(); + if (!specDate.isEmpty() && tok!=TK_WORD) + { + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '\\showdate'"); + parser()->tokenizer.setStatePara(); + return; + } + + std::tm dat{}; + int specFormat=0; + QCString err = dateTimeFromString(specDate,dat,specFormat); + if (!err.isEmpty()) + { + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"invalid <date_time> argument for command '\\showdate': %s",qPrint(err)); + parser()->tokenizer.setStatePara(); + return; + } + + int usedFormat=0; + QCString dateTimeStr = formatDateTime(fmt,dat,usedFormat); + + // warn the user if the format contains markers that are not explicitly filled in + for (int i=0;i<SF_NumBits;i++) + { + int bitMask = 1<<i; + if ((usedFormat&bitMask) && !(specFormat&bitMask)) // a part was used in the format string but its value was not specified. + { + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"'\\showdate' <format> parameter '%s' has %s related markers which are not specified in the <date_time> parameter '%s'. Filling in the current value for %s instead.", + qPrint(fmt),SF_bit2str(i),qPrint(specDate),SF_bit2str(i)); + } + } + + children().append<DocWord>(parser(),thisVariant,dateTimeStr); + parser()->tokenizer.setStatePara(); +} +void DocPara::handleILine(DocNodeVariant *) +{ + DBG(("handleILine()\n")); + parser()->tokenizer.setStateILine(); int tok = parser()->tokenizer.lex(); if (tok!=TK_WORD) { @@ -3189,6 +3274,29 @@ void DocPara::handleIline(DocNodeVariant *) parser()->tokenizer.setStatePara(); } +void DocPara::handleIFile(DocNodeVariant *) +{ + DBG(("handleIFile()\n")); + int tok=parser()->tokenizer.lex(); + if (tok!=TK_WHITESPACE) + { + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"expected whitespace after \\ifile command"); + return; + } + parser()->tokenizer.setStateFile(); + tok=parser()->tokenizer.lex(); + parser()->tokenizer.setStatePara(); + if (tok!=TK_WORD) + { + warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"unexpected token %s as the argument of \\ifile", + DocTokenizer::tokToString(tok)); + return; + } + parser()->context.fileName = parser()->context.token->name; + parser()->tokenizer.setStatePara(); +} + + void DocPara::handleIncludeOperator(DocNodeVariant *thisVariant,const QCString &cmdName,DocIncOperator::Type t) { QCString saveCmdName = cmdName; @@ -3759,6 +3867,12 @@ int DocPara::handleCommand(DocNodeVariant *thisVariant,const QCString &cmdName, retval = RetVal_Paragraph; } break; + case CMD_ISTARTCODE: + { + parser()->tokenizer.setStateICode(); + retval = handleStartCode(thisVariant); + } + break; case CMD_STARTCODE: { parser()->tokenizer.setStateCode(); @@ -3866,9 +3980,17 @@ int DocPara::handleCommand(DocNodeVariant *thisVariant,const QCString &cmdName, parser()->tokenizer.setStatePara(); } break; + case CMD_IVERBATIM: case CMD_VERBATIM: { - parser()->tokenizer.setStateVerbatim(); + if (cmdId == CMD_VERBATIM) + { + parser()->tokenizer.setStateVerbatim(); + } + else + { + parser()->tokenizer.setStateIVerbatim(); + } retval = parser()->tokenizer.lex(); children().append<DocVerbatim>(parser(),thisVariant,parser()->context.context,parser()->context.token->verb,DocVerbatim::Verbatim,parser()->context.isExample,parser()->context.exampleName); if (retval==0) warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"verbatim section ended without end marker"); @@ -4027,6 +4149,7 @@ int DocPara::handleCommand(DocNodeVariant *thisVariant,const QCString &cmdName, case CMD_ENDPARBLOCK: retval=RetVal_EndParBlock; break; + case CMD_ENDICODE: case CMD_ENDCODE: case CMD_ENDHTMLONLY: case CMD_ENDMANONLY: @@ -4036,6 +4159,7 @@ int DocPara::handleCommand(DocNodeVariant *thisVariant,const QCString &cmdName, case CMD_ENDDBONLY: case CMD_ENDLINK: case CMD_ENDVERBATIM: + case CMD_ENDIVERBATIM: case CMD_ENDILITERAL: case CMD_ENDDOT: case CMD_ENDMSC: @@ -4216,8 +4340,14 @@ int DocPara::handleCommand(DocNodeVariant *thisVariant,const QCString &cmdName, case CMD_INHERITDOC: handleInheritDoc(thisVariant); break; + case CMD_SHOWDATE: + handleShowDate(thisVariant); + break; case CMD_ILINE: - handleIline(thisVariant); + handleILine(thisVariant); + break; + case CMD_IFILE: + handleIFile(thisVariant); break; default: // we should not get here! @@ -4256,7 +4386,8 @@ int DocPara::handleHtmlStartTag(DocNodeVariant *thisVariant,const QCString &tagN int retval=RetVal_OK; int tagId = Mappers::htmlTagMapper->map(tagName); if (parser()->context.token->emptyTag && !(tagId&XML_CmdMask) && - tagId!=HTML_UNKNOWN && tagId!=HTML_IMG && tagId!=HTML_BR && tagId!=HTML_HR && tagId!=HTML_P) + tagId!=HTML_UNKNOWN && tagId!=HTML_IMG && tagId!=HTML_BR && tagId!=HTML_HR && tagId!=HTML_P + && tagId!=HTML_DIV && tagId!=HTML_SPAN) { warn_doc_error(parser()->context.fileName,parser()->tokenizer.getLineNr(),"HTML tag ('<%s/>') may not use the 'empty tag' XHTML syntax.", qPrint(tagName)); @@ -4308,9 +4439,6 @@ int DocPara::handleHtmlStartTag(DocNodeVariant *thisVariant,const QCString &tagN case HTML_INS: if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant,children(),DocStyleChange::Ins,tagName,&parser()->context.token->attribs); break; - case HTML_DETAILS: - if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant,children(),DocStyleChange::Details,tagName,&parser()->context.token->attribs); - break; case HTML_CODE: if (parser()->context.token->emptyTag) break; if (/*getLanguageFromFileName(parser()->context.fileName)==SrcLangExt_CSharp ||*/ parser()->context.xmlComment) @@ -4329,10 +4457,12 @@ int DocPara::handleHtmlStartTag(DocNodeVariant *thisVariant,const QCString &tagN if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant,children(),DocStyleChange::Italic,tagName,&parser()->context.token->attribs); break; case HTML_DIV: - if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant,children(),DocStyleChange::Div,tagName,&parser()->context.token->attribs); + parser()->handleStyleEnter(thisVariant,children(),DocStyleChange::Div,tagName,&parser()->context.token->attribs); + if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant,children(),DocStyleChange::Div,tagName); break; case HTML_SPAN: - if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant,children(),DocStyleChange::Span,tagName,&parser()->context.token->attribs); + parser()->handleStyleEnter(thisVariant,children(),DocStyleChange::Span,tagName,&parser()->context.token->attribs); + if (parser()->context.token->emptyTag) parser()->handleStyleLeave(thisVariant,children(),DocStyleChange::Span,tagName); break; case HTML_SUB: if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant,children(),DocStyleChange::Subscript,tagName,&parser()->context.token->attribs); @@ -4426,6 +4556,14 @@ int DocPara::handleHtmlStartTag(DocNodeVariant *thisVariant,const QCString &tagN parser()->handleImg(thisVariant,children(),tagHtmlAttribs); } break; + case HTML_DETAILS: + if (!parser()->context.token->emptyTag) + { + auto vDocHtmlDetails = children().append<DocHtmlDetails>(parser(),thisVariant, + tagHtmlAttribs); + retval=children().get_last<DocHtmlDetails>()->parse(vDocHtmlDetails); + } + break; case HTML_BLOCKQUOTE: if (!parser()->context.token->emptyTag) { @@ -4435,11 +4573,14 @@ int DocPara::handleHtmlStartTag(DocNodeVariant *thisVariant,const QCString &tagN break; case XML_SUMMARY: - if (insideDetails(parser()->context.styleStack)) + if (insideDetails(thisVariant)) { - if (!parser()->context.token->emptyTag) parser()->handleStyleEnter(thisVariant,children(),DocStyleChange::Summary,tagName,&parser()->context.token->attribs); - break; + if (!parser()->context.token->emptyTag) + { + parser()->handleStyleEnter(thisVariant,children(),DocStyleChange::Summary,tagName,&parser()->context.token->attribs); + } } + break; case XML_REMARKS: case XML_EXAMPLE: parser()->context.xmlComment=TRUE; @@ -4708,6 +4849,9 @@ int DocPara::handleHtmlEndTag(DocNodeVariant *thisVariant,const QCString &tagNam // ignore </li> tags } break; + case HTML_DETAILS: + retval=RetVal_EndHtmlDetails; + break; case HTML_BLOCKQUOTE: retval=RetVal_EndBlockQuote; break; @@ -4729,9 +4873,6 @@ int DocPara::handleHtmlEndTag(DocNodeVariant *thisVariant,const QCString &tagNam case HTML_INS: parser()->handleStyleLeave(thisVariant,children(),DocStyleChange::Ins,tagName); break; - case HTML_DETAILS: - parser()->handleStyleLeave(thisVariant,children(),DocStyleChange::Details,tagName); - break; case HTML_CODE: parser()->handleStyleLeave(thisVariant,children(),DocStyleChange::Code,tagName); break; @@ -4827,11 +4968,11 @@ int DocPara::handleHtmlEndTag(DocNodeVariant *thisVariant,const QCString &tagNam //children().push_back(std::make_unique<DocStyleChange>(this,parser()->context.nodeStack.size(),DocStyleChange::Bold,FALSE)); break; case XML_SUMMARY: - if (insideDetails(parser()->context.styleStack)) + if (insideDetails(thisVariant)) { parser()->handleStyleLeave(thisVariant,children(),DocStyleChange::Summary,tagName); - break; } + break; case XML_REMARKS: case XML_PARA: case XML_VALUE: @@ -4911,7 +5052,7 @@ reparsetoken: // and whitespace after certain constructs !holds_one_of_alternatives<DocHtmlDescList, DocHtmlTable, DocHtmlList, DocSimpleSect, DocAutoList, DocSimpleList, DocHtmlHeader, DocHtmlBlockQuote, - DocParamSect, DocXRefItem>(children().back()) + DocParamSect, DocHtmlDetails, DocXRefItem>(children().back()) ) ) { @@ -4965,7 +5106,7 @@ reparsetoken: // so a new simple section will be started at this level. // This is the same as unputting the last read token and continuing. parser()->context.token->name = parser()->context.token->simpleSectName; - if (parser()->context.token->name.left(4)=="rcs:") // RCS section + if (parser()->context.token->name.startsWith("rcs:")) // RCS section { parser()->context.token->name = parser()->context.token->name.mid(4); parser()->context.token->text = parser()->context.token->simpleSectText; @@ -5063,7 +5204,7 @@ reparsetoken: // so a new simple section will be started at this level. // This is the same as unputting the last read token and continuing. parser()->context.token->name = parser()->context.token->simpleSectName; - if (parser()->context.token->name.left(4)=="rcs:") // RCS section + if (parser()->context.token->name.startsWith("rcs:")) // RCS section { parser()->context.token->name = parser()->context.token->name.mid(4); parser()->context.token->text = parser()->context.token->simpleSectText; @@ -5564,201 +5705,3 @@ void DocRoot::parse(DocNodeVariant *thisVariant) DBG(("DocRoot::parse() end\n")); } -static QCString extractCopyDocId(const char *data, uint &j, uint len) -{ - uint s=j; - int round=0; - bool insideDQuote=FALSE; - bool insideSQuote=FALSE; - bool found=FALSE; - while (j<len && !found) - { - if (!insideSQuote && !insideDQuote) - { - switch (data[j]) - { - case '(': round++; break; - case ')': round--; break; - case '"': insideDQuote=TRUE; break; - case '\'': insideSQuote=TRUE; break; - case ' ': // fall through - case '\t': // fall through - case '\n': - found=(round==0); - break; - } - } - else if (insideSQuote) // look for single quote end - { - if (data[j]=='\'' && (j==0 || data[j]!='\\')) - { - insideSQuote=FALSE; - } - } - else if (insideDQuote) // look for double quote end - { - if (data[j]=='"' && (j==0 || data[j]!='\\')) - { - insideDQuote=FALSE; - } - } - if (!found) j++; - } - if (qstrncmp(data+j," const",6)==0) - { - j+=6; - } - else if (qstrncmp(data+j," volatile",9)==0) - { - j+=9; - } - uint e=j; - if (j>0 && data[j-1]=='.') { e--; } // do not include punctuation added by Definition::_setBriefDescription() - QCString id(data+s,e-s); - //printf("extractCopyDocId='%s' input='%s'\n",qPrint(id),&data[s]); - return id; -} - -// macro to check if the input starts with a specific command. -// note that data[i] should point to the start of the command (\ or @ character) -// and the sizeof(str) returns the size of str including the '\0' terminator; -// a fact we abuse to skip over the start of the command character. -#define CHECK_FOR_COMMAND(str,action) \ - do if ((i+sizeof(str)<len) && qstrncmp(data+i+1,str,sizeof(str)-1)==0) \ - { j=i+sizeof(str); action; } while(0) - -static uint isCopyBriefOrDetailsCmd(const char *data, uint i,uint len,bool &brief) -{ - uint j=0; - if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command - { - CHECK_FOR_COMMAND("copybrief",brief=TRUE); // @copybrief or \copybrief - CHECK_FOR_COMMAND("copydetails",brief=FALSE); // @copydetails or \copydetails - } - return j; -} - -static uint isVerbatimSection(const char *data,uint i,uint len,QCString &endMarker) -{ - uint j=0; - if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command - { - CHECK_FOR_COMMAND("dot",endMarker="enddot"); - CHECK_FOR_COMMAND("code",endMarker="endcode"); - CHECK_FOR_COMMAND("msc",endMarker="endmsc"); - CHECK_FOR_COMMAND("verbatim",endMarker="endverbatim"); - CHECK_FOR_COMMAND("iliteral",endMarker="endiliteral"); - CHECK_FOR_COMMAND("latexonly",endMarker="endlatexonly"); - CHECK_FOR_COMMAND("htmlonly",endMarker="endhtmlonly"); - CHECK_FOR_COMMAND("xmlonly",endMarker="endxmlonly"); - CHECK_FOR_COMMAND("rtfonly",endMarker="endrtfonly"); - CHECK_FOR_COMMAND("manonly",endMarker="endmanonly"); - CHECK_FOR_COMMAND("docbookonly",endMarker="enddocbookonly"); - CHECK_FOR_COMMAND("startuml",endMarker="enduml"); - } - //printf("isVerbatimSection(%s)=%d)\n",qPrint(QCString(&data[i]).left(10)),j); - return j; -} - -static uint skipToEndMarker(const char *data,uint i,uint len,const QCString &endMarker) -{ - while (i<len) - { - if ((data[i]=='@' || data[i]=='\\') && // start of command character - (i==0 || (data[i-1]!='@' && data[i-1]!='\\'))) // that is not escaped - { - if (i+endMarker.length()+1<=len && qstrncmp(data+i+1,endMarker.data(),endMarker.length())==0) - { - return i+endMarker.length()+1; - } - } - i++; - } - // oops no endmarker found... - return i<len ? i+1 : len; -} - -QCString DocParser::processCopyDoc(const char *data,uint &len) -{ - //printf("processCopyDoc start '%s'\n",data); - GrowBuf buf; - uint i=0; - while (i<len) - { - char c = data[i]; - if (c=='@' || c=='\\') // look for a command - { - bool isBrief=TRUE; - uint j=isCopyBriefOrDetailsCmd(data,i,len,isBrief); - if (j>0) - { - // skip whitespace - while (j<len && (data[j]==' ' || data[j]=='\t')) j++; - // extract the argument - QCString id = extractCopyDocId(data,j,len); - const Definition *def = 0; - QCString doc,brief; - //printf("resolving docs='%s'\n",qPrint(id)); - if (findDocsForMemberOrCompound(id,&doc,&brief,&def)) - { - //printf("found it def=%p brief='%s' doc='%s' isBrief=%d\n",def,qPrint(brief),qPrint(doc),isBrief); - auto it = std::find(context.copyStack.begin(),context.copyStack.end(),def); - if (it==context.copyStack.end()) // definition not parsed earlier - { - context.copyStack.push_back(def); - if (isBrief) - { - uint l=static_cast<uint>(brief.length()); - buf.addStr(processCopyDoc(brief.data(),l)); - } - else - { - uint l=static_cast<uint>(doc.length()); - buf.addStr(processCopyDoc(doc.data(),l)); - } - context.copyStack.pop_back(); - } - else - { - warn_doc_error(context.fileName,tokenizer.getLineNr(), - "Found recursive @copy%s or @copydoc relation for argument '%s'.\n", - isBrief?"brief":"details",qPrint(id)); - } - } - else - { - warn_doc_error(context.fileName,tokenizer.getLineNr(), - "@copy%s or @copydoc target '%s' not found", isBrief?"brief":"details", - qPrint(id)); - } - // skip over command - i=j; - } - else - { - QCString endMarker; - uint k = isVerbatimSection(data,i,len,endMarker); - if (k>0) - { - uint orgPos = i; - i=skipToEndMarker(data,k,len,endMarker); - buf.addStr(data+orgPos,i-orgPos); - } - else - { - buf.addChar(c); - i++; - } - } - } - else // not a command, just copy - { - buf.addChar(c); - i++; - } - } - len = static_cast<uint>(buf.getPos()); - buf.addChar(0); - return buf.get(); -} - diff --git a/src/docnode.h b/src/docnode.h index 1db32dc..b280ed8 100644 --- a/src/docnode.h +++ b/src/docnode.h @@ -45,7 +45,8 @@ class DocParser; /* 35 */ DN(DocSecRefList) DN_SEP DN(DocInternal) DN_SEP DN(DocParBlock) DN_SEP DN(DocSimpleList) DN_SEP DN(DocHtmlList) DN_SEP \ /* 40 */ DN(DocSimpleSect) DN_SEP DN(DocSimpleSectSep) DN_SEP DN(DocParamSect) DN_SEP DN(DocPara) DN_SEP DN(DocParamList) DN_SEP \ /* 45 */ DN(DocSimpleListItem) DN_SEP DN(DocHtmlListItem) DN_SEP DN(DocHtmlDescData) DN_SEP DN(DocHtmlCell) DN_SEP DN(DocHtmlCaption) DN_SEP \ -/* 50 */ DN(DocHtmlRow) DN_SEP DN(DocHtmlTable) DN_SEP DN(DocHtmlBlockQuote) DN_SEP DN(DocText) DN_SEP DN(DocRoot) \ +/* 50 */ DN(DocHtmlRow) DN_SEP DN(DocHtmlTable) DN_SEP DN(DocHtmlBlockQuote) DN_SEP DN(DocText) DN_SEP DN(DocRoot) DN_SEP \ +/* 55 */ DN(DocHtmlDetails) \ // forward declarations #define DN(x) class x; @@ -55,13 +56,13 @@ DOC_NODES #undef DN_SEP // define a variant type -using DocNodeVariant = std::variant< #define DN(x) x #define DN_SEP , +using DocNodeVariant = std::variant< DOC_NODES +>; #undef DN #undef DN_SEP ->; // getter functions to return the name of a doc node type #define DN(x) constexpr const char *docNodeName(const x &n) { return #x; } @@ -271,7 +272,6 @@ class DocStyleChange : public DocNode Del = (1<<12), Ins = (1<<13), S = (1<<14), - Details = (1<<15), Summary = (1<<16), Cite = (1<<17) }; @@ -793,6 +793,19 @@ class DocHRef : public DocCompoundNode QCString m_file; }; +/** Node Html details */ +class DocHtmlDetails : public DocCompoundNode +{ + public: + DocHtmlDetails(DocParser *parser,DocNodeVariant *parent,const HtmlAttribList &attribs) : + DocCompoundNode(parser,parent), m_attribs(attribs) {} + const HtmlAttribList &attribs() const { return m_attribs; } + int parse(DocNodeVariant*); + + private: + HtmlAttribList m_attribs; +}; + /** Node Html heading */ class DocHtmlHeader : public DocCompoundNode { @@ -1029,7 +1042,9 @@ class DocPara : public DocCompoundNode void handleSection(DocNodeVariant *thisVariant,const QCString &cmdName); void handleInheritDoc(DocNodeVariant *thisVariant); void handleVhdlFlow(DocNodeVariant *thisVariant); - void handleIline(DocNodeVariant *thisVariant); + void handleILine(DocNodeVariant *thisVariant); + void handleIFile(DocNodeVariant *thisVariant); + void handleShowDate(DocNodeVariant *thisVariant); int handleStartCode(DocNodeVariant *thisVariant); int handleHtmlHeader(DocNodeVariant *thisVariant,const HtmlAttribList &tagHtmlAttribs,int level); @@ -1310,17 +1325,17 @@ inline T *DocNodeList::get_last() return std::get_if<T>(&back()); } -/// ---------------- Debug helpers ------------------------------- +// ---------------- Debug helpers ------------------------------- -inline const char *docNodeName(const DocNodeVariant &v) -{ #define DN(x) #x #define DN_SEP , +inline const char *docNodeName(const DocNodeVariant &v) +{ static const char *table[] = { DOC_NODES }; -#undef DN -#undef DN_SEP return table[v.index()]; } +#undef DN +#undef DN_SEP inline void dumpDocNodeSizes() { diff --git a/src/docparser.cpp b/src/docparser.cpp index fdb9179..88c97f0 100644 --- a/src/docparser.cpp +++ b/src/docparser.cpp @@ -36,6 +36,7 @@ #include "portable.h" #include "printdocvisitor.h" #include "util.h" +#include "indexlist.h" // debug off #define DBG(x) do {} while(0) @@ -179,7 +180,7 @@ QCString DocParser::findAndCopyImage(const QCString &fileName, DocImage::Type ty } if (type==DocImage::Latex && Config_getBool(USE_PDFLATEX) && - fd->name().right(4)==".eps" + fd->name().endsWith(".eps") ) { // we have an .eps image in pdflatex mode => convert it to a pdf. QCString outputDir = Config_getString(LATEX_OUTPUT); @@ -200,7 +201,7 @@ QCString DocParser::findAndCopyImage(const QCString &fileName, DocImage::Type ty else { result=fileName; - if (result.left(5)!="http:" && result.left(6)!="https:" && doWarn) + if (!result.startsWith("http:") && !result.startsWith("https:") && doWarn) { warn_doc_error(context.fileName,tokenizer.getLineNr(), "image file %s is not found in IMAGE_PATH: " @@ -245,7 +246,7 @@ void DocParser::checkArgumentName() if (lang==SrcLangExt_Fortran) argName=argName.lower(); argName=argName.stripWhiteSpace(); //printf("argName='%s' aName=%s\n",qPrint(argName),qPrint(aName)); - if (argName.right(3)=="...") argName=argName.left(argName.length()-3); + if (argName.endsWith("...")) argName=argName.left(argName.length()-3); if (aName==argName) { context.paramsFound.insert(aName.str()); @@ -322,7 +323,7 @@ void DocParser::checkUnOrMultipleDocumentedParams() if (lang==SrcLangExt_Fortran) argName = argName.lower(); argName=argName.stripWhiteSpace(); QCString aName = argName; - if (argName.right(3)=="...") argName=argName.left(argName.length()-3); + if (argName.endsWith("...")) argName=argName.left(argName.length()-3); if (lang==SrcLangExt_Python && (argName=="self" || argName=="cls")) { // allow undocumented self / cls parameter for Python @@ -370,6 +371,17 @@ void DocParser::checkUnOrMultipleDocumentedParams() qPrint(substitute(errMsg,"%","%%"))); } } + else + { + if (!context.paramsFound.size() && Config_getBool(WARN_IF_DOC_ERROR)) + { + warn_doc_error(context.memberDef->docFile(), + context.memberDef->docLine(), + "%s", + qPrint(context.memberDef->qualifiedName() + + " has @param documentation sections but no arguments")); + } + } } } @@ -853,7 +865,7 @@ void DocParser::handleLinkedWord(DocNodeVariant *parent,DocNodeList &children,bo } else // normal non-linkable word { - if (context.token->name.left(1)=="#" || context.token->name.left(2)=="::") + if (context.token->name.startsWith("#") || context.token->name.startsWith("::")) { warn_doc_error(context.fileName,tokenizer.getLineNr(),"explicit link request to '%s' could not be resolved",qPrint(name)); children.append<DocWord>(this,parent,context.token->name); @@ -962,6 +974,11 @@ void DocParser::defaultHandleTitleAndSize(const int cmd, DocNodeVariant *parent, // special case: no title, but we do have a size indicator break; } + else if (tok==TK_HTMLTAG) + { + tokenizer.unputString(context.token->name); + break; + } if (!defaultHandleToken(parent,tok,children)) { errorHandleDefaultToken(parent,tok,children,Mappers::cmdMapper->find(cmd)); @@ -974,7 +991,7 @@ void DocParser::defaultHandleTitleAndSize(const int cmd, DocNodeVariant *parent, } while (tok==TK_WHITESPACE || tok==TK_WORD) // there are values following the title { - if(tok == TK_WORD) + if (tok==TK_WORD) { if (context.token->name=="width=" || context.token->name=="height=") { @@ -992,6 +1009,7 @@ void DocParser::defaultHandleTitleAndSize(const int cmd, DocNodeVariant *parent, } else { + tokenizer.unputString(context.token->name); warn_doc_error(context.fileName,tokenizer.getLineNr(),"Unknown option '%s' after \\%s command, expected 'width' or 'height'", qPrint(context.token->name), qPrint(Mappers::cmdMapper->find(cmd))); break; @@ -999,6 +1017,17 @@ void DocParser::defaultHandleTitleAndSize(const int cmd, DocNodeVariant *parent, } tok=tokenizer.lex(); + // if we found something we did not expect, push it back to the stream + // so it can still be processed + if (tok==TK_COMMAND_AT || tok==TK_COMMAND_BS) + { + tokenizer.unputString(context.token->name); + tokenizer.unputString(tok==TK_COMMAND_AT ? "@" : "\\"); + } + else if (tok==TK_SYMBOL || tok==TK_HTMLTAG) + { + tokenizer.unputString(context.token->name); + } } tokenizer.setStatePara(); @@ -1394,16 +1423,6 @@ reparsetoken: handleStyleLeave(parent,children,DocStyleChange::Ins,tokenName); } break; - case HTML_DETAILS: - if (!context.token->endTag) - { - handleStyleEnter(parent,children,DocStyleChange::Details,tokenName,&context.token->attribs); - } - else - { - handleStyleLeave(parent,children,DocStyleChange::Details,tokenName); - } - break; case HTML_CODE: case XML_C: if (!context.token->endTag) @@ -1658,6 +1677,220 @@ void DocParser::readTextFileByName(const QCString &file,QCString &text) //--------------------------------------------------------------------------- +static QCString extractCopyDocId(const char *data, uint &j, uint len) +{ + uint s=j; + int round=0; + bool insideDQuote=FALSE; + bool insideSQuote=FALSE; + bool found=FALSE; + while (j<len && !found) + { + if (!insideSQuote && !insideDQuote) + { + switch (data[j]) + { + case '(': round++; break; + case ')': round--; break; + case '"': insideDQuote=TRUE; break; + case '\'': insideSQuote=TRUE; break; + case ' ': // fall through + case '\t': // fall through + case '\n': + found=(round==0); + break; + } + } + else if (insideSQuote) // look for single quote end + { + if (data[j]=='\'' && (j==0 || data[j]!='\\')) + { + insideSQuote=FALSE; + } + } + else if (insideDQuote) // look for double quote end + { + if (data[j]=='"' && (j==0 || data[j]!='\\')) + { + insideDQuote=FALSE; + } + } + if (!found) j++; + } + if (qstrncmp(data+j," const",6)==0) + { + j+=6; + } + else if (qstrncmp(data+j," volatile",9)==0) + { + j+=9; + } + uint e=j; + if (j>0 && data[j-1]=='.') { e--; } // do not include punctuation added by Definition::_setBriefDescription() + QCString id(data+s,e-s); + //printf("extractCopyDocId='%s' input='%s'\n",qPrint(id),&data[s]); + return id; +} + +// macro to check if the input starts with a specific command. +// note that data[i] should point to the start of the command (\ or @ character) +// and the sizeof(str) returns the size of str including the '\0' terminator; +// a fact we abuse to skip over the start of the command character. +#define CHECK_FOR_COMMAND(str,action) \ + do if ((i+sizeof(str)<len) && qstrncmp(data+i+1,str,sizeof(str)-1)==0) \ + { j=i+sizeof(str); action; } while(0) + +static uint isCopyBriefOrDetailsCmd(const char *data, uint i,uint len,bool &brief) +{ + uint j=0; + if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command + { + CHECK_FOR_COMMAND("copybrief",brief=TRUE); // @copybrief or \copybrief + CHECK_FOR_COMMAND("copydetails",brief=FALSE); // @copydetails or \copydetails + } + return j; +} + +static uint isVerbatimSection(const char *data,uint i,uint len,QCString &endMarker) +{ + uint j=0; + if (i==0 || (data[i-1]!='@' && data[i-1]!='\\')) // not an escaped command + { + CHECK_FOR_COMMAND("dot",endMarker="enddot"); + CHECK_FOR_COMMAND("icode",endMarker="endicode"); + CHECK_FOR_COMMAND("code",endMarker="endcode"); + CHECK_FOR_COMMAND("msc",endMarker="endmsc"); + CHECK_FOR_COMMAND("iverbatim",endMarker="endiverbatim"); + CHECK_FOR_COMMAND("verbatim",endMarker="endverbatim"); + CHECK_FOR_COMMAND("iliteral",endMarker="endiliteral"); + CHECK_FOR_COMMAND("latexonly",endMarker="endlatexonly"); + CHECK_FOR_COMMAND("htmlonly",endMarker="endhtmlonly"); + CHECK_FOR_COMMAND("xmlonly",endMarker="endxmlonly"); + CHECK_FOR_COMMAND("rtfonly",endMarker="endrtfonly"); + CHECK_FOR_COMMAND("manonly",endMarker="endmanonly"); + CHECK_FOR_COMMAND("docbookonly",endMarker="enddocbookonly"); + CHECK_FOR_COMMAND("startuml",endMarker="enduml"); + } + //printf("isVerbatimSection(%s)=%d)\n",qPrint(QCString(&data[i]).left(10)),j); + return j; +} + +static uint skipToEndMarker(const char *data,uint i,uint len,const QCString &endMarker) +{ + while (i<len) + { + if ((data[i]=='@' || data[i]=='\\') && // start of command character + (i==0 || (data[i-1]!='@' && data[i-1]!='\\'))) // that is not escaped + { + if (i+endMarker.length()+1<=len && qstrncmp(data+i+1,endMarker.data(),endMarker.length())==0) + { + return i+endMarker.length()+1; + } + } + i++; + } + // oops no endmarker found... + return i<len ? i+1 : len; +} + + +QCString DocParser::processCopyDoc(const char *data,uint &len) +{ + //printf("processCopyDoc start '%s'\n",data); + GrowBuf buf; + uint i=0; + int lineNr = tokenizer.getLineNr(); + while (i<len) + { + char c = data[i]; + if (c=='@' || c=='\\') // look for a command + { + bool isBrief=TRUE; + uint j=isCopyBriefOrDetailsCmd(data,i,len,isBrief); + if (j>0) + { + // skip whitespace + while (j<len && (data[j]==' ' || data[j]=='\t')) j++; + // extract the argument + QCString id = extractCopyDocId(data,j,len); + const Definition *def = 0; + QCString doc,brief; + //printf("resolving docs='%s'\n",qPrint(id)); + if (findDocsForMemberOrCompound(id,&doc,&brief,&def)) + { + //printf("found it def=%p brief='%s' doc='%s' isBrief=%d\n",def,qPrint(brief),qPrint(doc),isBrief); + auto it = std::find(context.copyStack.begin(),context.copyStack.end(),def); + if (it==context.copyStack.end()) // definition not parsed earlier + { + QCString orgFileName = context.fileName; + context.copyStack.push_back(def); + if (isBrief) + { + buf.addStr("\\ifile \""+QCString(def->briefFile())+"\" "); + buf.addStr("\\iline "+QCString().setNum(def->briefLine())+" "); + uint l=static_cast<uint>(brief.length()); + buf.addStr(processCopyDoc(brief.data(),l)); + } + else + { + buf.addStr("\\ifile \""+QCString(def->docFile())+"\" "); + buf.addStr("\\iline "+QCString().setNum(def->docLine())+" "); + uint l=static_cast<uint>(doc.length()); + buf.addStr(processCopyDoc(doc.data(),l)); + } + context.copyStack.pop_back(); + buf.addStr("\\ifile \""+context.fileName+"\" "); + buf.addStr("\\iline "+QCString().setNum(lineNr)+" "); + } + else + { + warn_doc_error(context.fileName,tokenizer.getLineNr(), + "Found recursive @copy%s or @copydoc relation for argument '%s'.\n", + isBrief?"brief":"details",qPrint(id)); + } + } + else + { + warn_doc_error(context.fileName,tokenizer.getLineNr(), + "@copy%s or @copydoc target '%s' not found", isBrief?"brief":"details", + qPrint(id)); + } + // skip over command + i=j; + } + else + { + QCString endMarker; + uint k = isVerbatimSection(data,i,len,endMarker); + if (k>0) + { + uint orgPos = i; + i=skipToEndMarker(data,k,len,endMarker); + buf.addStr(data+orgPos,i-orgPos); + // TODO: adjust lineNr + } + else + { + buf.addChar(c); + i++; + } + } + } + else // not a command, just copy + { + buf.addChar(c); + i++; + lineNr += (c=='\n') ? 1 : 0; + } + } + len = static_cast<uint>(buf.getPos()); + buf.addChar(0); + return buf.get(); +} + + +//--------------------------------------------------------------------------- + IDocNodeASTPtr validatingParseDoc(IDocParser &parserIntf, const QCString &fileName,int startLine, const Definition *ctx,const MemberDef *md, diff --git a/src/docparser.h b/src/docparser.h index 64db969..45923ba 100644 --- a/src/docparser.h +++ b/src/docparser.h @@ -98,5 +98,4 @@ void docFindSections(const QCString &input, const Definition *d, const QCString &fileName); - #endif diff --git a/src/docparser_p.h b/src/docparser_p.h index 3eca1ae..9e985f5 100644 --- a/src/docparser_p.h +++ b/src/docparser_p.h @@ -236,13 +236,14 @@ inline bool insideTable(const DocNodeVariant *n) //--------------------------------------------------------------------------- -inline bool insideDetails(DocStyleChangeStack styleStack) +inline bool insideDetails(const DocNodeVariant *n) { - for (auto i : styleStack) + while (n) { - if (std::get<DocStyleChange>(*i).style() == DocStyleChange::Details) return true; + if (std::holds_alternative<DocHtmlDetails>(*n)) return TRUE; + n=parent(n); } - return false; + return FALSE; } diff --git a/src/docsets.cpp b/src/docsets.cpp index bc8f7a9..f2bddc5 100644 --- a/src/docsets.cpp +++ b/src/docsets.cpp @@ -41,13 +41,9 @@ struct DocSets::Private }; -DocSets::DocSets() : p(std::make_unique<Private>()) -{ -} - -DocSets::~DocSets() -{ -} +DocSets::DocSets() : p(std::make_unique<Private>()) {} +DocSets::~DocSets() = default; +DocSets::DocSets(DocSets &&) = default; void DocSets::initialize() { @@ -311,7 +307,7 @@ void DocSets::addIndexItem(const Definition *context,const MemberDef *md, { if (md && (md->isObjCMethod() || md->isObjCProperty())) lang="occ"; // Objective C/C++ - else if (fd && fd->name().right(2).lower()==".c") + else if (fd && fd->name().lower().endsWith(".c")) lang="c"; // Plain C else if (cd==0 && nd==0) lang="c"; // Plain C symbol outside any class or namespace @@ -450,7 +446,7 @@ void DocSets::addIndexItem(const Definition *context,const MemberDef *md, else if (cd->compoundType()==ClassDef::Protocol) { type="intf"; - if (scope.right(2)=="-p") scope=scope.left(scope.length()-2); + if (scope.endsWith("-p")) scope=scope.left(scope.length()-2); } else if (cd->compoundType()==ClassDef::Interface) { @@ -494,7 +490,7 @@ void DocSets::writeToken(TextStream &t, t << " <Token>\n"; t << " <TokenIdentifier>\n"; QCString name = d->name(); - if (name.right(2)=="-p") name=name.left(name.length()-2); + if (name.endsWith("-p")) name=name.left(name.length()-2); t << " <Name>" << convertToXML(name) << "</Name>\n"; if (!lang.isEmpty()) { diff --git a/src/docsets.h b/src/docsets.h index 03146f1..0397a33 100644 --- a/src/docsets.h +++ b/src/docsets.h @@ -18,23 +18,26 @@ #include <memory> -#include "index.h" +#include "qcstring.h" class TextStream; class Definition; +class MemberDef; /** A class that generates docset files. * * These files can be used to create context help * for use within Apple's Xcode 3.0 development environment */ -class DocSets : public IndexIntf +class DocSets { public: DocSets(); - ~DocSets(); + virtual ~DocSets(); + DocSets(DocSets &&); + void initialize(); void finalize(); void incContentsDepth(); diff --git a/src/doctokenizer.h b/src/doctokenizer.h index 1c31920..8b61ec6 100644 --- a/src/doctokenizer.h +++ b/src/doctokenizer.h @@ -63,7 +63,8 @@ enum Tokens RetVal_EndBlockQuote = 0x10014, RetVal_CopyDoc = 0x10015, RetVal_EndInternal = 0x10016, - RetVal_EndParBlock = 0x10017 + RetVal_EndParBlock = 0x10017, + RetVal_EndHtmlDetails = 0x10018 }; #define TK_COMMAND_CHAR(token) ((token)==TK_COMMAND_AT ? "@" : "\\") @@ -141,10 +142,12 @@ class DocTokenizer void pushContext(); bool popContext(); int lex(); + void unputString(const QCString &tag); void setStatePara(); void setStateTitle(); void setStateTitleAttrValue(); void setStateCode(); + void setStateICode(); void setStateXmlCode(); void setStateHtmlOnly(); void setStateManOnly(); @@ -153,6 +156,7 @@ class DocTokenizer void setStateDbOnly(); void setStateRtfOnly(); void setStateVerbatim(); + void setStateIVerbatim(); void setStateILiteral(); void setStateILiteralOpt(); void setStateDot(); @@ -179,7 +183,9 @@ class DocTokenizer void setStateOptions(); void setStateBlock(); void setStateEmoji(); - void setStateIline(); + void setStateILine(); + void setStateQuotedString(); + void setStateShowDate(); private: struct Private; diff --git a/src/doctokenizer.l b/src/doctokenizer.l index 976f1dc..6f9f025 100644 --- a/src/doctokenizer.l +++ b/src/doctokenizer.l @@ -342,6 +342,8 @@ REFWORD_NOCV {FILEMASK}|{LABELID}|{REFWORD2_NOCV}|{REFWORD3}|{REFWORD4_NOCV} RCSID "$"("Author"|"Date"|"Header"|"Id"|"Locker"|"Log"|"Name"|"RCSfile"|"Revision"|"Source"|"State")":"[^:\n$][^\n$]*"$" LINENR {BLANK}*[1-9][0-9]* +SHOWDATE ([0-9]{4}"-"[0-9]{1,2}"-"[0-9]{1,2})?({WS}*[0-9]{1,2}":"[0-9]{1,2}(":"[0-9]{1,2})?)? + %option noyywrap %x St_Para @@ -352,7 +354,9 @@ LINENR {BLANK}*[1-9][0-9]* %x St_TitleA %x St_TitleV %x St_Code +%x St_iCode %x St_CodeOpt +%x St_iCodeOpt %x St_XmlCode %x St_HtmlOnly %x St_HtmlOnlyOption @@ -362,6 +366,7 @@ LINENR {BLANK}*[1-9][0-9]* %x St_XmlOnly %x St_DbOnly %x St_Verbatim +%x St_iVerbatim %x St_ILiteral %x St_ILiteralOpt %x St_Dot @@ -387,7 +392,8 @@ LINENR {BLANK}*[1-9][0-9]* %x St_Options %x St_Block %x St_Emoji -%x St_Iline +%x St_ILine +%x St_ShowDate %x St_Sections %s St_SecLabel1 @@ -395,6 +401,9 @@ LINENR {BLANK}*[1-9][0-9]* %s St_SecTitle %x St_SecSkip +%x St_QuotedString +%x St_QuotedContent + %% <St_Para>\r /* skip carriage return */ <St_Para>^{LISTITEM} { /* list item */ @@ -765,23 +774,39 @@ LINENR {BLANK}*[1-9][0-9]* yyextra->token->name = yyextra->token->name.mid(i+1,yyextra->token->name.length()-i-2); BEGIN(St_Code); } +<St_iCodeOpt>{BLANK}*"{"(".")?{LABELID}"}" { + yyextra->token->name = yytext; + int i=yyextra->token->name.find('{'); /* } to keep vi happy */ + yyextra->token->name = yyextra->token->name.mid(i+1,yyextra->token->name.length()-i-2); + BEGIN(St_iCode); + } <St_CodeOpt>"\\ilinebr" | <St_CodeOpt>\n | <St_CodeOpt>. { unput_string(yytext,yyleng); BEGIN(St_Code); } +<St_iCodeOpt>"\\ilinebr" | +<St_iCodeOpt>\n | +<St_iCodeOpt>. { + unput_string(yytext,yyleng); + BEGIN(St_iCode); + } <St_Code>{WS}*{CMD}"endcode" { lineCount(yytext,yyleng); return RetVal_OK; } +<St_iCode>{WS}*{CMD}"endicode" { + lineCount(yytext,yyleng); + return RetVal_OK; + } <St_XmlCode>{WS}*"</code>" { lineCount(yytext,yyleng); return RetVal_OK; } -<St_Code,St_XmlCode>[^\\@\n<]+ | -<St_Code,St_XmlCode>\n | -<St_Code,St_XmlCode>. { +<St_Code,St_iCode,St_XmlCode>[^\\@\n<]+ | +<St_Code,St_iCode,St_XmlCode>\n | +<St_Code,St_iCode,St_XmlCode>. { lineCount(yytext,yyleng); yyextra->token->verb+=yytext; } @@ -860,9 +885,13 @@ LINENR {BLANK}*[1-9][0-9]* yyextra->token->verb=yyextra->token->verb.mid(1,yyextra->token->verb.length()-2); return RetVal_OK; } -<St_Verbatim,St_ILiteral>[^\\@\n]+ | -<St_Verbatim,St_ILiteral>\n | -<St_Verbatim,St_ILiteral>. { /* Verbatim / javadac literal/code text */ +<St_iVerbatim>{CMD}"endiverbatim" { + yyextra->token->verb=stripEmptyLines(yyextra->token->verb); + return RetVal_OK; + } +<St_Verbatim,St_iVerbatim,St_ILiteral>[^\\@\n]+ | +<St_Verbatim,St_iVerbatim,St_ILiteral>\n | +<St_Verbatim,St_iVerbatim,St_ILiteral>. { /* Verbatim text */ lineCount(yytext,yyleng); yyextra->token->verb+=yytext; } @@ -955,7 +984,8 @@ LINENR {BLANK}*[1-9][0-9]* return TK_SYMBOL; } <St_TitleN>{HTMLTAG} { - lineCount(yytext,yyleng); + yyextra->token->name = yytext; + return TK_HTMLTAG; } <St_TitleN>\n { /* new line => end of title */ unput(*yytext); @@ -1237,8 +1267,39 @@ LINENR {BLANK}*[1-9][0-9]* unput(*yytext); return 0; } -<St_Iline>{LINENR}/[\n\.] | -<St_Iline>{LINENR}{BLANK} { +<St_QuotedString>"\"" { + yyextra->token->name=""; + BEGIN(St_QuotedContent); + } +<St_QuotedString>(\n|"\\ilinebr") { + unput_string(yytext,yyleng); + return 0; + } +<St_QuotedString>. { + unput(*yytext); + return 0; + } +<St_QuotedContent>"\"" { + return TK_WORD; + } +<St_QuotedContent>. { + yyextra->token->name+=yytext; + } +<St_ShowDate>{WS}+{SHOWDATE} { + lineCount(yytext,yyleng); + yyextra->token->name=yytext; + return TK_WORD; + } +<St_ShowDate>(\n|"\\ilinebr") { + unput_string(yytext,yyleng); + return 0; + } +<St_ShowDate>. { + unput(*yytext); + return 0; + } +<St_ILine>{LINENR}/[\n\.] | +<St_ILine>{LINENR}{BLANK} { bool ok = false; int nr = QCString(yytext).toInt(&ok); if (!ok) @@ -1251,7 +1312,7 @@ LINENR {BLANK}*[1-9][0-9]* } return TK_WORD; } -<St_Iline>. { +<St_ILine>. { return 0; } <St_File>{FILEMASK} { @@ -1343,6 +1404,10 @@ LINENR {BLANK}*[1-9][0-9]* yyextra->endMarker="endverbatim"; BEGIN(St_SecSkip); } +<St_Sections>{CMD}"iverbatim"/[^a-z_A-Z0-9] { + yyextra->endMarker="endiverbatim"; + BEGIN(St_SecSkip); + } <St_Sections>{CMD}"iliteral"/[^a-z_A-Z0-9] { yyextra->endMarker="endiliteral"; BEGIN(St_SecSkip); @@ -1387,6 +1452,10 @@ LINENR {BLANK}*[1-9][0-9]* yyextra->endMarker="endcode"; BEGIN(St_SecSkip); } +<St_Sections>{CMD}"icode"/[^a-z_A-Z0-9] { + yyextra->endMarker="endicode"; + BEGIN(St_SecSkip); + } <St_Sections>"<!--" { yyextra->endMarker="-->"; BEGIN(St_SecSkip); @@ -1425,7 +1494,7 @@ LINENR {BLANK}*[1-9][0-9]* lineCount(yytext,yyleng); yyextra->secTitle = yytext; yyextra->secTitle = yyextra->secTitle.stripWhiteSpace(); - if (yyextra->secTitle.right(8)=="\\ilinebr") + if (yyextra->secTitle.endsWith("\\ilinebr")) { yyextra->secTitle.left(yyextra->secTitle.length()-8); } @@ -1662,6 +1731,13 @@ int DocTokenizer::lex() return doctokenizerYYlex(p->yyscanner); } +void DocTokenizer::unputString(const QCString &tag) +{ + yyscan_t yyscanner = p->yyscanner; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + unput_string(tag.data(),tag.length()); +} + void DocTokenizer::findSections(const QCString &input,const Definition *d, const QCString &fileName) { @@ -1739,6 +1815,15 @@ void DocTokenizer::setStateCode() BEGIN(St_CodeOpt); } +void DocTokenizer::setStateICode() +{ + yyscan_t yyscanner = p->yyscanner; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + yyextra->token->verb=""; + yyextra->token->name=""; + BEGIN(St_iCodeOpt); +} + void DocTokenizer::setStateXmlCode() { yyscan_t yyscanner = p->yyscanner; @@ -1821,6 +1906,14 @@ void DocTokenizer::setStateVerbatim() BEGIN(St_Verbatim); } +void DocTokenizer::setStateIVerbatim() +{ + yyscan_t yyscanner = p->yyscanner; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + yyextra->token->verb=""; + BEGIN(St_iVerbatim); +} + void DocTokenizer::setStateDot() { yyscan_t yyscanner = p->yyscanner; @@ -1971,11 +2064,25 @@ void DocTokenizer::setStateEmoji() BEGIN(St_Emoji); } -void DocTokenizer::setStateIline() +void DocTokenizer::setStateILine() +{ + yyscan_t yyscanner = p->yyscanner; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + BEGIN(St_ILine); +} + +void DocTokenizer::setStateQuotedString() +{ + yyscan_t yyscanner = p->yyscanner; + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + BEGIN(St_QuotedString); +} + +void DocTokenizer::setStateShowDate() { yyscan_t yyscanner = p->yyscanner; struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; - BEGIN(St_Iline); + BEGIN(St_ShowDate); } void DocTokenizer::cleanup() diff --git a/src/dot.cpp b/src/dot.cpp index 1d44dd9..5f4c7df 100644 --- a/src/dot.cpp +++ b/src/dot.cpp @@ -28,7 +28,7 @@ #include "message.h" #include "doxygen.h" #include "language.h" -#include "index.h" +#include "indexlist.h" #include "dir.h" #define MAP_CMD "cmapx" diff --git a/src/dotattributes.h b/src/dotattributes.h new file mode 100644 index 0000000..2522db3 --- /dev/null +++ b/src/dotattributes.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * + * Copyright (C) 1997-2022 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +#ifndef DOTATTRIBUTES_H +#define DOTATTRIBUTES_H + +#include <map> +#include <string> + +#include "regex.h" +#include "qcstring.h" + +//! Class representing an attribute list of a dot graph object. +class DotAttributes +{ + public: + //! Creates an instance of a DotAttribute list given its initial string representation + DotAttributes(const QCString &input) : m_input(input) {} + + //! Return the string representation of the attribute list + QCString str() const { return m_input; } + + //! update a given attribute with a new value. + //! If the attribute is not found a new attribute will be appended. + void updateValue(const QCString &key,const QCString &value) + { + // look for key\s*= + const reg::Ex re = key.str()+R"(\s*=)"; + reg::Match match; + std::string s = m_input.str(); + if (reg::search(s,match,re)) // replace existing attribute + { + size_t len = s.length(); + size_t startPos = match.position()+match.length(); // position after = + size_t pos = startPos; + while (pos<len && qisspace(s[pos])) pos++; + if (pos<len && s[pos]=='"') // quoted value, search for end quote, ignoring escaped quotes + { + char pc=s[pos]; + pos++; // skip over start quote + while (pos<len && (s[pos]!='"' || (s[pos]=='"' && pc=='\\'))) pc=s[pos++]; + if (pos<len) pos++; // skip over end quote + } + else // unquoted value, search for attribute separator (space,comma, or semicolon) + { + while (pos<len && s[pos]!=',' && s[pos]!=';' && !qisspace(s[pos])) pos++; + } + // pos is now the position after the value, so replace the part between [start..pos) with the new value + m_input=m_input.left(startPos)+value.quoted()+m_input.mid(pos); + } + else // append new attribute + { + if (!m_input.isEmpty()) m_input+=","; + m_input+=key+"="+value.quoted(); + } + } + + private: + QCString m_input; +}; + +#endif // DOTATTRIBUTES_H diff --git a/src/dotdirdeps.cpp b/src/dotdirdeps.cpp index 4bc48fa..315416f 100644 --- a/src/dotdirdeps.cpp +++ b/src/dotdirdeps.cpp @@ -18,6 +18,7 @@ #include "doxygen.h" #include "config.h" #include "image.h" +#include "dotnode.h" #include <algorithm> #include <iterator> @@ -117,26 +118,30 @@ static const char* getDirectoryBorderColor(const DotDirProperty &property) /** Returns a DOT node style according to the directory properties. */ static std::string getDirectoryBorderStyle(const DotDirProperty &property) { - std::string style; - if (!property.isPeripheral) - { - style += "filled,"; - } + std::string style = "filled"; if (property.isOriginal) { - style += "bold,"; + style += ",bold"; } if (property.isIncomplete) { - style += "dashed,"; + style += ",dashed"; } else if (property.isTruncated && property.isOrphaned) { - style += "dashed,"; + style += ",dashed"; } return style; } +static TextStream &common_attributes(TextStream &t, const DirDef *const dir, const DotDirProperty &prop) +{ + return t << + "style=\"" << getDirectoryBorderStyle(prop) << "\", " + "URL=\"" << addHtmlExtensionIfMissing(dir->getOutputFileBase()) << "\"," + "tooltip=\"" << escapeTooltip(dir->briefDescriptionAsTooltip()) << "\""; +} + /** * Puts DOT code for drawing directory to stream and adds it to the list. * @param[in,out] t stream to which the DOT code is written to @@ -149,13 +154,11 @@ static void drawDirectory(TextStream &t, const DirDef *const directory, const Do DirDefMap &directoriesInGraph,int startLevel) { t << " " << directory->getOutputFileBase() << " [" - "shape=box, " - "label=\"" << directory->shortName() << "\", " - "style=\"" << getDirectoryBorderStyle(property) << "\", " + "label=\"" << DotNode::convertLabel(directory->shortName()) << "\", " "fillcolor=\"" << getDirectoryBackgroundColor(directory->level()-startLevel) << "\", " - "color=\"" << getDirectoryBorderColor(property) << "\", " - "URL=\"" << addHtmlExtensionIfMissing(directory->getOutputFileBase()) << "\"" - "];\n"; + "color=\"" << getDirectoryBorderColor(property) << "\", "; + common_attributes(t, directory, property) + << "];\n"; directoriesInGraph.insert(std::make_pair(directory->getOutputFileBase().str(), directory)); } @@ -178,21 +181,19 @@ static void drawClusterOpening(TextStream &outputStream, const DirDef *const dir " graph [ " "bgcolor=\"" << getDirectoryBackgroundColor(directory->level()-startLevel) << "\", " "pencolor=\"" << getDirectoryBorderColor(directoryProperty) << "\", " - "style=\"" << getDirectoryBorderStyle(directoryProperty) << "\", " "label=\""; if (isAncestor) { - outputStream << directory->shortName(); + outputStream << DotNode::convertLabel(directory->shortName()); } outputStream << "\", " - "fontname=\"" << Config_getString(DOT_FONTNAME) << "\", " - "fontsize=\"" << Config_getInt(DOT_FONTSIZE) << "\", " - "URL=\"" << addHtmlExtensionIfMissing(directory->getOutputFileBase()) << "\"" - "]\n"; + << Config_getString(DOT_COMMON_ATTR) << " "; + common_attributes(outputStream, directory, directoryProperty) + << "]\n"; if (!isAncestor) { outputStream << " " << directory->getOutputFileBase() << " [shape=plaintext, " - "label=\"" << directory->shortName() << "\"" + "label=\"" << DotNode::convertLabel(directory->shortName()) << "\"" "];\n"; directoriesInGraph.insert(std::make_pair(directory->getOutputFileBase().str(), directory)); } @@ -388,7 +389,7 @@ void writeDotDirDepGraph(TextStream &t,const DirDef *dd,bool linkRelations) { t << " headhref=\"" << addHtmlExtensionIfMissing(relationName) << "\""; } - t << "];\n"; + t << " color=\"steelblue1\" fontcolor=\"steelblue1\"];\n"; } } } diff --git a/src/dotfilepatcher.cpp b/src/dotfilepatcher.cpp index dc94299..5f14fe1 100644 --- a/src/dotfilepatcher.cpp +++ b/src/dotfilepatcher.cpp @@ -146,7 +146,7 @@ static QCString replaceRef(const QCString &buf,const QCString &relPath, QCString result; if (urlOnly) // for user defined dot graphs { - if (link.left(5)=="\\ref " || link.left(5)=="@ref ") // \ref url + if (link.startsWith("\\ref ") || link.startsWith("@ref ")) // \ref url { result=href+"=\""; // fake ref node to resolve the url @@ -230,7 +230,7 @@ bool DotFilePatcher::convertMapFile(TextStream &t,const QCString &mapName, while (getline(f,line)) // foreach line { QCString buf = line+'\n'; - if (buf.left(5)=="<area") + if (buf.startsWith("<area")) { QCString replBuf = replaceRef(buf,relPath,urlOnly,context); // strip id="..." from replBuf since the id's are not needed and not unique. @@ -255,7 +255,7 @@ DotFilePatcher::DotFilePatcher(const QCString &patchFile) bool DotFilePatcher::isSVGFile() const { - return m_patchFile.right(4)==".svg"; + return m_patchFile.endsWith(".svg"); } int DotFilePatcher::addMap(const QCString &mapFile,const QCString &relPath, @@ -296,7 +296,7 @@ bool DotFilePatcher::run() const { //printf("DotFilePatcher::run(): %s\n",qPrint(m_patchFile)); bool interactiveSVG_local = Config_getBool(INTERACTIVE_SVG); - bool isSVGFile = m_patchFile.right(4)==".svg"; + bool isSVGFile = m_patchFile.endsWith(".svg"); int graphId = -1; QCString relPath; if (isSVGFile) diff --git a/src/dotgraph.cpp b/src/dotgraph.cpp index 65ff967..9653055 100644 --- a/src/dotgraph.cpp +++ b/src/dotgraph.cpp @@ -15,10 +15,11 @@ #include <sstream> #include <mutex> +#include <regex> #include "config.h" #include "doxygen.h" -#include "index.h" +#include "indexlist.h" #include "md5.h" #include "message.h" #include "util.h" @@ -274,8 +275,6 @@ void DotGraph::generateCode(TextStream &t) void DotGraph::writeGraphHeader(TextStream &t,const QCString &title) { - int fontSize = Config_getInt(DOT_FONTSIZE); - QCString fontName = Config_getString(DOT_FONTNAME); t << "digraph "; if (title.isEmpty()) { @@ -292,16 +291,11 @@ void DotGraph::writeGraphHeader(TextStream &t,const QCString &title) t << " // INTERACTIVE_SVG=YES\n"; } t << " // LATEX_PDF_SIZE\n"; // write placeholder for LaTeX PDF bounding box size replacement - if (Config_getBool(DOT_TRANSPARENT)) - { - t << " bgcolor=\"transparent\";\n"; - } - t << " edge [fontname=\"" << fontName << "\"," - "fontsize=\"" << fontSize << "\"," - "labelfontname=\"" << fontName << "\"," - "labelfontsize=\"" << fontSize << "\"];\n"; - t << " node [fontname=\"" << fontName << "\"," - "fontsize=\"" << fontSize << "\",shape=record];\n"; + t << " bgcolor=\"transparent\";\n"; + QCString c = Config_getString(DOT_COMMON_ATTR); + if (!c.isEmpty()) c += ","; + t << " edge [" << c << Config_getString(DOT_EDGE_ATTR) << "];\n"; + t << " node [" << c << Config_getString(DOT_NODE_ATTR) << "];\n"; } void DotGraph::writeGraphFooter(TextStream &t) diff --git a/src/dotgraph.h b/src/dotgraph.h index 87d4165..417ebe3 100644 --- a/src/dotgraph.h +++ b/src/dotgraph.h @@ -17,6 +17,7 @@ #define DOTGRAPH_H #include <iostream> +#include <map> #include "qcstring.h" #include "dir.h" diff --git a/src/dotgroupcollaboration.cpp b/src/dotgroupcollaboration.cpp index b72b525..383926e 100644 --- a/src/dotgroupcollaboration.cpp +++ b/src/dotgroupcollaboration.cpp @@ -316,25 +316,6 @@ bool DotGroupCollaboration::isTrivial() const void DotGroupCollaboration::writeGraphHeader(TextStream &t,const QCString &title) const { - int fontSize = Config_getInt(DOT_FONTSIZE); - QCString fontName = Config_getString(DOT_FONTNAME); - t << "digraph "; - if (title.isEmpty()) - { - t << "\"Dot Graph\""; - } - else - { - t << "\"" << convertToXML(title) << "\""; - } - t << "\n"; - t << "{\n"; - if (Config_getBool(DOT_TRANSPARENT)) - { - t << " bgcolor=\"transparent\";\n"; - } - t << " edge [fontname=\"" << fontName << "\",fontsize=\"" << fontSize << "\"," - "labelfontname=\"" << fontName << "\",labelfontsize=\"" << fontSize << "\"];\n"; - t << " node [fontname=\"" << fontName << "\",fontsize=\"" << fontSize << "\",shape=box];\n"; + DotGraph::writeGraphHeader(t, title); t << " rankdir=LR;\n"; } diff --git a/src/dotlegendgraph.cpp b/src/dotlegendgraph.cpp index f512fa2..538efbe 100644 --- a/src/dotlegendgraph.cpp +++ b/src/dotlegendgraph.cpp @@ -22,6 +22,7 @@ #include "dot.h" #include "language.h" #include "dotfilepatcher.h" +#include "dotnode.h" void DotLegendGraph::writeGraph(const QCString &path) { @@ -43,27 +44,26 @@ QCString DotLegendGraph::getBaseName() const void DotLegendGraph::computeTheGraph() { - int fontSize = Config_getInt(DOT_FONTSIZE); - QCString fontName = Config_getString(DOT_FONTNAME); TextStream md5stream; writeGraphHeader(md5stream,theTranslator->trLegendTitle()); - md5stream << " Node9 [shape=\"box\",label=\"Inherited\",fontsize=\"" << fontSize << "\",height=0.2,width=0.4,fontname=\"" << fontName << "\",fillcolor=\"grey75\",style=\"filled\" fontcolor=\"black\"];\n"; - md5stream << " Node10 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << fontSize << "\",style=\"solid\",fontname=\"" << fontName << "\"];\n"; - md5stream << " Node10 [shape=\"box\",label=\"PublicBase\",fontsize=\"" << fontSize << "\",height=0.2,width=0.4,fontname=\"" << fontName << "\",color=\"black\"];\n"; - md5stream << " Node11 -> Node10 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << fontSize << "\",style=\"solid\",fontname=\"" << fontName << "\"];\n"; - md5stream << " Node11 [shape=\"box\",label=\"Truncated\",fontsize=\"" << fontSize << "\",height=0.2,width=0.4,fontname=\"" << fontName << "\",color=\"red\"];\n"; - md5stream << " Node13 -> Node9 [dir=\"back\",color=\"darkgreen\",fontsize=\"" << fontSize << "\",style=\"solid\",fontname=\"" << fontName << "\"];\n"; - md5stream << " Node13 [shape=\"box\",label=\"ProtectedBase\",fontsize=\"" << fontSize << "\",height=0.2,width=0.4,fontname=\"" << fontName << "\",color=\"black\"];\n"; - md5stream << " Node14 -> Node9 [dir=\"back\",color=\"firebrick4\",fontsize=\"" << fontSize << "\",style=\"solid\",fontname=\"" << fontName << "\"];\n"; - md5stream << " Node14 [shape=\"box\",label=\"PrivateBase\",fontsize=\"" << fontSize << "\",height=0.2,width=0.4,fontname=\"" << fontName << "\",color=\"black\"];\n"; - md5stream << " Node15 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << fontSize << "\",style=\"solid\",fontname=\"" << fontName << "\"];\n"; - md5stream << " Node15 [shape=\"box\",label=\"Undocumented\",fontsize=\"" << fontSize << "\",height=0.2,width=0.4,fontname=\"" << fontName << "\",color=\"grey75\"];\n"; - md5stream << " Node16 -> Node9 [dir=\"back\",color=\"midnightblue\",fontsize=\"" << fontSize << "\",style=\"solid\",fontname=\"" << fontName << "\"];\n"; - md5stream << " Node16 [shape=\"box\",label=\"Templ< int >\",fontsize=\"" << fontSize << "\",height=0.2,width=0.4,fontname=\"" << fontName << "\",color=\"black\"];\n"; - md5stream << " Node17 -> Node16 [dir=\"back\",color=\"orange\",fontsize=\"" << fontSize << "\",style=\"dashed\",label=\"< int >\",fontname=\"" << fontName << "\"];\n"; - md5stream << " Node17 [shape=\"box\",label=\"Templ< T >\",fontsize=\"" << fontSize << "\",height=0.2,width=0.4,fontname=\"" << fontName << "\",color=\"black\"];\n"; - md5stream << " Node18 -> Node9 [dir=\"back\",color=\"darkorchid3\",fontsize=\"" << fontSize << "\",style=\"dashed\",label=\"m_usedClass\",fontname=\"" << fontName << "\"];\n"; - md5stream << " Node18 [shape=\"box\",label=\"Used\",fontsize=\"" << fontSize << "\",height=0.2,width=0.4,fontname=\"" << fontName << "\",color=\"black\"];\n"; + + DotNode{9, "Inherited", "", "", TRUE}.writeBox(md5stream, CallGraph, GOF_BITMAP, false); + md5stream << " Node10 -> Node9 [dir=\"back\",color=\"steelblue1\",style=\"solid\"];\n"; + DotNode{10, "PublicBase", "", "url"}.markHasDocumentation().writeBox(md5stream, CallGraph, GOF_BITMAP, false); + md5stream << " Node11 -> Node10 [dir=\"back\",color=\"steelblue1\",style=\"solid\"];\n"; + DotNode{11, "Truncated", "", "url"}.markAsTruncated().markHasDocumentation().writeBox(md5stream, CallGraph, GOF_BITMAP, true); + md5stream << " Node13 -> Node9 [dir=\"back\",color=\"darkgreen\",style=\"solid\"];\n"; + md5stream << " Node13 [label=\"ProtectedBase\",color=\"gray40\",fillcolor=\"white\",style=\"filled\"];\n"; + md5stream << " Node14 -> Node9 [dir=\"back\",color=\"firebrick4\",style=\"solid\"];\n"; + md5stream << " Node14 [label=\"PrivateBase\",color=\"gray40\",fillcolor=\"white\",style=\"filled\"];\n"; + md5stream << " Node15 -> Node9 [dir=\"back\",color=\"steelblue1\",style=\"solid\"];\n"; + DotNode{15, "Undocumented", "", ""}.writeBox(md5stream, CallGraph, GOF_BITMAP, false); + md5stream << " Node16 -> Node9 [dir=\"back\",color=\"steelblue1\",style=\"solid\"];\n"; + md5stream << " Node16 [label=\"Templ\\< int \\>\",color=\"gray40\",fillcolor=\"white\",style=\"filled\"];\n"; + md5stream << " Node17 -> Node16 [dir=\"back\",color=\"orange\",style=\"dashed\",label=\"< int >\",];\n"; + md5stream << " Node17 [label=\"Templ\\< T \\>\",color=\"gray40\",fillcolor=\"white\",style=\"filled\"];\n"; + md5stream << " Node18 -> Node9 [dir=\"back\",color=\"darkorchid3\",style=\"dashed\",label=\"m_usedClass\",];\n"; + md5stream << " Node18 [label=\"Used\",color=\"gray40\",fillcolor=\"white\",style=\"filled\"];\n"; writeGraphFooter(md5stream); m_theGraph = md5stream.str(); } diff --git a/src/dotnode.cpp b/src/dotnode.cpp index beb0dc9..6dad107 100644 --- a/src/dotnode.cpp +++ b/src/dotnode.cpp @@ -34,7 +34,7 @@ struct EdgeProperties /*! mapping from protection levels to color names */ static const char *normalEdgeColorMap[] = { - "midnightblue", // Public + "steelblue1", // Public "darkgreen", // Protected "firebrick4", // Private "darkorchid3", // "use" relation @@ -61,7 +61,7 @@ static const char *normalEdgeStyleMap[] = static const char *umlEdgeColorMap[] = { - "midnightblue", // Public + "steelblue1", // Public "darkgreen", // Protected "firebrick4", // Private "grey25", // "use" relation @@ -96,7 +96,7 @@ static EdgeProperties umlEdgeProps = umlEdgeColorMap, umlArrowStyleMap, umlEdgeStyleMap }; -static QCString escapeTooltip(const QCString &tooltip) +QCString escapeTooltip(const QCString &tooltip) { if (tooltip.isEmpty()) return tooltip; QCString result; @@ -354,34 +354,9 @@ void DotNode::deleteNodes(DotNode *node) } } -void DotNode::writeBox(TextStream &t, - GraphType gt, - GraphOutputFormat /*format*/, - bool hasNonReachableChildren) const +void DotNode::writeLabel(TextStream &t, GraphType gt) const { - const char *labCol; - if (m_classDef) - { - if (m_classDef->hasDocumentation() && hasNonReachableChildren) - labCol = "red"; - else if (m_classDef->hasDocumentation() && !hasNonReachableChildren) - labCol = "black"; - else if (!m_classDef->hasDocumentation() && hasNonReachableChildren) - labCol = "orangered"; - else // (!m_classDef->hasDocumentation() && !hasNonReachableChildren) - { - labCol = "grey75"; - if (m_classDef->templateMaster() && m_classDef->templateMaster()->hasDocumentation()) - labCol = "black"; - } - } - else - { - labCol = m_url.isEmpty() ? "grey75" : // non link - (hasNonReachableChildren ? "red" : "black"); - } - t << " Node" << m_number << " [label=\""; - + t << "label="; if (m_classDef && Config_getBool(UML_LOOK) && (gt==Inheritance || gt==Collaboration)) { // add names shown as relations to a set, so we don't show @@ -407,7 +382,7 @@ void DotNode::writeBox(TextStream &t, } //printf("DotNode::writeBox for %s\n",qPrint(m_classDef->name())); - t << "{" << convertLabel(m_label) << "\\n"; + t << "\"{" << convertLabel(m_label) << "\\n"; auto dotUmlDetails = Config_getEnum(DOT_UML_DETAILS); if (dotUmlDetails!=DOT_UML_DETAILS_t::NONE) { @@ -450,47 +425,93 @@ void DotNode::writeBox(TextStream &t, } } } - t << "}"; + t << "}\""; + } + else if (Config_getString(DOT_NODE_ATTR).contains("shape=plain")) + { + if (m_isRoot) + t << "<<b>" << convertToXML(m_label) << "</b>>"; + else if (m_truncated == Truncated) + t << "<<i>" << convertToXML(m_label) << "</i>>"; + else + t << '"' << convertLabel(m_label) << '"'; } else // standard look { - t << convertLabel(m_label); + t << '"' << convertLabel(m_label) << '"'; } - t << "\",height=0.2,width=0.4"; - if (m_isRoot) +} + +void DotNode::writeUrl(TextStream &t) const +{ + if (m_url.isEmpty()) + return; + int tagPos = m_url.findRev('$'); + t << ",URL=\""; + QCString noTagURL = m_url; + if (tagPos!=-1) + { + t << m_url.left(tagPos); + noTagURL = m_url.mid(tagPos); + } + int anchorPos = noTagURL.findRev('#'); + if (anchorPos==-1) { - t << ",color=\"black\", fillcolor=\"grey75\", style=\"filled\", fontcolor=\"black\""; + t << addHtmlExtensionIfMissing(noTagURL) << "\""; } else { - t << ",color=\"" << labCol << "\""; - if (!Config_getBool(DOT_TRANSPARENT)) + t << addHtmlExtensionIfMissing(noTagURL.left(anchorPos)) + << noTagURL.right(noTagURL.length() - anchorPos) << "\""; + } +} + +void DotNode::writeBox(TextStream &t, + GraphType gt, + GraphOutputFormat /*format*/, + bool hasNonReachableChildren) const +{ + const char *labCol; + const char *fillCol = "white"; + if (m_classDef) + { + if (m_classDef->hasDocumentation() && hasNonReachableChildren) { - t << ", fillcolor=\"white\""; - t << ", style=\"filled\""; + labCol = "red"; + fillCol = "#FFF0F0"; } - if (!m_url.isEmpty()) + else if (m_classDef->hasDocumentation() && !hasNonReachableChildren) + labCol = "gray40"; + else if (!m_classDef->hasDocumentation() && hasNonReachableChildren) + labCol = "orangered"; + else // (!m_classDef->hasDocumentation() && !hasNonReachableChildren) { - int tagPos = m_url.findRev('$'); - t << ",URL=\""; - QCString noTagURL = m_url; - if (tagPos!=-1) - { - t << m_url.left(tagPos); - noTagURL = m_url.mid(tagPos); - } - int anchorPos = noTagURL.findRev('#'); - if (anchorPos==-1) - { - t << addHtmlExtensionIfMissing(noTagURL) << "\""; - } - else - { - t << addHtmlExtensionIfMissing(noTagURL.left(anchorPos)) - << noTagURL.right(noTagURL.length()-anchorPos) << "\""; - } + labCol = "grey75"; + if (m_classDef->templateMaster() && m_classDef->templateMaster()->hasDocumentation()) + labCol = "gray40"; } } + else + { + labCol = m_url.isEmpty() ? "grey60" : // non link + (hasNonReachableChildren ? "red" : "grey40"); + fillCol = m_url.isEmpty() ? "#E0E0E0" : + (hasNonReachableChildren ? "#FFF0F0" : "white"); + } + t << " Node" << m_number << " ["; + writeLabel(t,gt); + t << ",height=0.2,width=0.4"; + if (m_isRoot) + { + t << ",color=\"gray40\", fillcolor=\"grey60\", style=\"filled\", fontcolor=\"black\""; + } + else + { + t << ",color=\"" << labCol << "\""; + t << ", fillcolor=\"" << fillCol << "\""; + t << ", style=\"filled\""; + writeUrl(t); + } if (!m_tooltip.isEmpty()) { t << ",tooltip=\"" << escapeTooltip(m_tooltip) << "\""; @@ -527,8 +548,7 @@ void DotNode::writeArrow(TextStream &t, bool umlUseArrow = aStyle=="odiamond"; if (pointBack && !umlUseArrow) t << "dir=\"back\","; - t << "color=\"" << eProps->edgeColorMap[ei->color()] - << "\",fontsize=\"" << Config_getInt(DOT_FONTSIZE) << "\","; + t << "color=\"" << eProps->edgeColorMap[ei->color()] << "\","; t << "style=\"" << eProps->edgeStyleMap[ei->style()] << "\""; if (!ei->label().isEmpty()) { @@ -547,7 +567,6 @@ void DotNode::writeArrow(TextStream &t, t << ",arrowhead=\"" << eProps->arrowStyleMap[ei->color()] << "\""; } - if (format==GOF_BITMAP) t << ",fontname=\"" << Config_getString(DOT_FONTNAME) << "\""; t << "];\n"; } diff --git a/src/dotnode.h b/src/dotnode.h index c630951..ba6499b 100644 --- a/src/dotnode.h +++ b/src/dotnode.h @@ -81,6 +81,8 @@ class DotNode void writeXML(TextStream &t,bool isClassGraph) const; void writeDocbook(TextStream &t,bool isClassGraph) const; void writeDEF(TextStream &t) const; + void writeLabel(TextStream &t, GraphType gt) const; + void writeUrl(TextStream &t) const; void writeBox(TextStream &t,GraphType gt,GraphOutputFormat f, bool hasNonReachableChildren) const; void writeArrow(TextStream &t,GraphType gt,GraphOutputFormat f,const DotNode *cn, @@ -99,13 +101,13 @@ class DotNode void clearWriteFlag(); void renumberNodes(int &number); void markRenumbered() { m_renumbered = true; } - void markHasDocumentation() { m_hasDoc = true; } + DotNode& markHasDocumentation() { m_hasDoc = true; return *this;} void setSubgraphId(int id) { m_subgraphId = id; } void colorConnectedNodes(int curColor); void setDistance(int distance); void markAsVisible(bool b=TRUE) { m_visible=b; } - void markAsTruncated(bool b=TRUE) { m_truncated=b ? Truncated : Untruncated; } + DotNode& markAsTruncated(bool b=TRUE) { m_truncated=b ? Truncated : Untruncated; return *this;} const DotNodeRefVector &children() const { return m_children; } const DotNodeRefVector &parents() const { return m_parents; } const EdgeInfoVector &edgeInfo() const { return m_edgeInfo; } @@ -138,4 +140,6 @@ class DotNodeDeque : public std::deque<DotNode*> { }; +QCString escapeTooltip(const QCString &tooltip); + #endif diff --git a/src/dotrunner.cpp b/src/dotrunner.cpp index 04eb452..922c09f 100644 --- a/src/dotrunner.cpp +++ b/src/dotrunner.cpp @@ -14,6 +14,7 @@ */ #include <cassert> +#include <cmath> #include "dotrunner.h" #include "util.h" @@ -123,12 +124,15 @@ bool DotRunner::readBoundingBox(const QCString &fileName,int *width,int *height, if (p) // found PageBoundingBox or /MediaBox string { int x,y; + double w,h; fclose(f); - if (sscanf(p+bblen,"%d %d %d %d",&x,&y,width,height)!=4) + if (sscanf(p+bblen,"%d %d %lf %lf",&x,&y,&w,&h)!=4) { //printf("readBoundingBox sscanf fail\n"); return FALSE; } + *width = static_cast<int>(std::ceil(w)); + *height = static_cast<int>(std::ceil(h)); return TRUE; } } @@ -210,7 +214,7 @@ bool DotRunner::run() // As there should be only one pdf file be generated, we don't need code for regenerating multiple pdf files in one call for (auto& s : m_jobs) { - if (s.format.left(3)=="pdf") + if (s.format.startsWith("pdf")) { int width=0,height=0; if (!readBoundingBox(s.output,&width,&height,FALSE)) goto error; @@ -222,7 +226,7 @@ bool DotRunner::run() } } - if (s.format.left(3)=="png") + if (s.format.startsWith("png")) { checkPngResult(s.output); } diff --git a/src/doxygen.cpp b/src/doxygen.cpp index ab76f09..0c0bb31 100644 --- a/src/doxygen.cpp +++ b/src/doxygen.cpp @@ -31,6 +31,7 @@ #include "scanner.h" #include "entry.h" #include "index.h" +#include "indexlist.h" #include "message.h" #include "config.h" #include "util.h" @@ -153,7 +154,8 @@ bool Doxygen::parseSourcesNeeded = FALSE; SearchIndexIntf *Doxygen::searchIndex=0; SymbolMap<Definition>*Doxygen::symbolMap; ClangUsrMap *Doxygen::clangUsrMap = 0; -Cache<std::string,LookupInfo> *Doxygen::lookupCache; +Cache<std::string,LookupInfo> *Doxygen::typeLookupCache; +Cache<std::string,LookupInfo> *Doxygen::symbolLookupCache; DirLinkedMap *Doxygen::dirLinkedMap; DirRelationLinkedMap Doxygen::dirRelations; ParserManager *Doxygen::parserManager = 0; @@ -168,6 +170,7 @@ DefinesPerFileList Doxygen::macroDefinitions; bool Doxygen::clangAssistedParsing = FALSE; QCString Doxygen::verifiedDotPath; volatile bool Doxygen::terminating = false; +InputFileEncodingList Doxygen::inputFileEncodingList; // locally accessible globals static std::multimap< std::string, const Entry* > g_classEntries; @@ -715,6 +718,7 @@ QCString stripTemplateSpecifiers(const QCString &s) * full qualified name \a name. Creates an artificial scope if the scope is * not found and set the parent/child scope relation if the scope is found. */ +[[maybe_unused]] static Definition *buildScopeFromQualifiedName(const QCString &name_,SrcLangExt lang,const TagInfo *tagInfo) { QCString name = stripTemplateSpecifiers(name_); @@ -1488,7 +1492,7 @@ void distributeClassGroupRelations() static ClassDefMutable *createTagLessInstance(const ClassDef *rootCd,const ClassDef *templ,const QCString &fieldName) { QCString fullName = removeAnonymousScopes(templ->name()); - if (fullName.right(2)=="::") fullName=fullName.left(fullName.length()-2); + if (fullName.endsWith("::")) fullName=fullName.left(fullName.length()-2); fullName+="."+fieldName; //printf("** adding class %s based on %s\n",qPrint(fullName),qPrint(templ->name())); @@ -1860,7 +1864,7 @@ static void findUsingDirectives(const Entry *root) //printf("Found using directive %s at line %d of %s\n", // qPrint(root->name),root->startLine,qPrint(root->fileName)); QCString name=substitute(root->name,".","::"); - if (name.right(2)=="::") + if (name.endsWith("::")) { name=name.left(name.length()-2); } @@ -2406,10 +2410,8 @@ static MemberDef *addVariableToFile( { QCString ttype = type; ttype.stripPrefix("typedef "); - if (ttype.left(7)=="struct " || ttype.left(6)=="union ") + if (ttype.stripPrefix("struct ") || ttype.stripPrefix("union ")) { - ttype.stripPrefix("struct "); - ttype.stripPrefix("union "); static const reg::Ex re(R"(\a\w*)"); reg::Match match; std::string typ = ttype.str(); @@ -2682,7 +2684,7 @@ static bool isVarWithConstructor(const Entry *root) result=FALSE; goto done; } - else if ((fd != nullptr) && (fd->name().right(2)==".c" || fd->name().right(2)==".h")) + else if ((fd != nullptr) && (fd->name().endsWith(".c") || fd->name().endsWith(".h"))) { // inside a .c file result=FALSE; goto done; @@ -2702,7 +2704,6 @@ static bool isVarWithConstructor(const Entry *root) findAndRemoveWord(type,"static"); findAndRemoveWord(type,"volatile"); typePtrType = type.find('*')!=-1 || type.find('&')!=-1; - //if (type.left(6)=="const ") type=type.right(type.length()-6); if (!typePtrType) { typeIsClass = resolver.resolveClass(ctx,type)!=0; @@ -2856,16 +2857,27 @@ static void addVariable(const Entry *root,int isFuncPtr=-1) name=removeRedundantWhiteSpace(name); // find the scope of this variable - Entry *p = root->parent(); - while ((p->section & Entry::SCOPE_MASK)) + int index = computeQualifiedIndex(name); + if (index!=-1 && root->parent()->section==Entry::GROUPDOC_SEC && root->parent()->tagInfo()) + // grouped members are stored with full scope { - QCString scopeName = p->name; - if (!scopeName.isEmpty()) + buildScopeFromQualifiedName(name.left(index+2),root->lang,root->tagInfo()); + scope=name.left(index); + name=name.mid(index+2); + } + else + { + Entry *p = root->parent(); + while ((p->section & Entry::SCOPE_MASK)) { - scope.prepend(scopeName); - break; + QCString scopeName = p->name; + if (!scopeName.isEmpty()) + { + scope.prepend(scopeName); + break; + } + p=p->parent(); } - p=p->parent(); } MemberType mtype; @@ -2909,9 +2921,9 @@ static void addVariable(const Entry *root,int isFuncPtr=-1) if (type=="@") mtype=MemberType_EnumValue; - else if (type.left(8)=="typedef ") + else if (type.startsWith("typedef ")) mtype=MemberType_Typedef; - else if (type.left(7)=="friend ") + else if (type.startsWith("friend ")) mtype=MemberType_Friend; else if (root->mtype==Property) mtype=MemberType_Property; @@ -3244,7 +3256,7 @@ static void addMethodToClass(const Entry *root,ClassDefMutable *cd, QCString args = rargs; QCString name=removeRedundantWhiteSpace(rname); - if (name.left(2)=="::") name=name.right(name.length()-2); + name.stripPrefix("::"); MemberType mtype; if (isFriend) mtype=MemberType_Friend; @@ -3257,7 +3269,7 @@ static void addMethodToClass(const Entry *root,ClassDefMutable *cd, int i = -1; int j = -1; if ((fd==0 || fd->getLanguage()==SrcLangExt_Cpp) && - name.left(9)!="operator " && // not operator + !name.startsWith("operator ") && // not operator (i=name.find('<'))!=-1 && // containing < (j=name.find('>'))!=-1 && // or > (j!=i+2 || name.at(i+1)!='=') // but not the C++20 spaceship operator <=> @@ -3338,7 +3350,7 @@ static void addMethodToClass(const Entry *root,ClassDefMutable *cd, def=qualScope+scopeSeparator+name; //+optArgs; } } - if (def.left(7)=="friend ") def=def.right(def.length()-7); + def.stripPrefix("friend "); md->setDefinition(def); md->enableCallGraph(root->callGraph); md->enableCallerGraph(root->callerGraph); @@ -3421,6 +3433,10 @@ static void addGlobalFunction(const Entry *root,const QCString &rname,const QCSt nd = getResolvedNamespaceMutable(nscope); } } + else if (root->parent()->section==Entry::GROUPDOC_SEC && !scope.isEmpty()) + { + nd = getResolvedNamespaceMutable(sc); + } if (!scope.isEmpty()) { @@ -3448,7 +3464,7 @@ static void addGlobalFunction(const Entry *root,const QCString &rname,const QCSt " '%s' '%s'::'%s' '%s' proto=%d\n" " def='%s'\n", qPrint(root->type), - qPrint(root->parent()->name), + qPrint(scope), qPrint(rname), qPrint(root->args), root->proto, @@ -3459,10 +3475,6 @@ static void addGlobalFunction(const Entry *root,const QCString &rname,const QCSt md->enableCallerGraph(root->callerGraph); md->enableReferencedByRelation(root->referencedByRelation); md->enableReferencesRelation(root->referencesRelation); - //if (root->mGrpId!=-1) - //{ - // md->setMemberGroup(memberGroupDict[root->mGrpId]); - //} md->setRefItems(root->sli); if (nd && !nd->name().isEmpty() && nd->name().at(0)!='@') @@ -3521,7 +3533,19 @@ static void buildFunctionList(const Entry *root) QCString rname = removeRedundantWhiteSpace(root->name); //printf("rname=%s\n",qPrint(rname)); - QCString scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name); + QCString scope; + int index = computeQualifiedIndex(rname); + if (index!=-1 && root->parent()->section==Entry::GROUPDOC_SEC && root->parent()->tagInfo()) + // grouped members are stored with full scope + { + buildScopeFromQualifiedName(rname.left(index+2),root->lang,root->tagInfo()); + scope=rname.left(index); + rname=rname.mid(index+2); + } + else + { + scope=root->parent()->name; //stripAnonymousNamespaceScope(root->parent->name); + } if (!rname.isEmpty() && scope.find('@')==-1) { ClassDefMutable *cd=0; @@ -3580,7 +3604,7 @@ static void buildFunctionList(const Entry *root) ) && !isMember && (root->relates.isEmpty() || root->relatesType == Duplicate) && - root->type.left(7)!="extern " && root->type.left(8)!="typedef " + !root->type.startsWith("extern ") && !root->type.startsWith("typedef ") ) // no member => unrelated function { @@ -4210,7 +4234,7 @@ static void findUsedClassesForClass(const Entry *root, ClassDefMutable *usedCdm = toClassDefMutable(usedCd); if (usedCd==0 && !Config_getBool(HIDE_UNDOC_RELATIONS)) { - if (type.right(2)=="(*" || type.right(2)=="(^") // type is a function pointer + if (type.endsWith("(*") || type.endsWith("(^")) // type is a function pointer { type+=md->argsString(); } @@ -4498,7 +4522,7 @@ static bool findClassRelation( QCString biName=bi->name; bool explicitGlobalScope=FALSE; //printf("findClassRelation: biName='%s'\n",qPrint(biName)); - if (biName.left(2)=="::") // explicit global scope + if (biName.startsWith("::")) // explicit global scope { biName=biName.right(biName.length()-2); explicitGlobalScope=TRUE; @@ -4755,7 +4779,7 @@ static bool findClassRelation( } if (baseClass) { - if (biName.right(2)=="-p") + if (biName.endsWith("-p")) { biName="<"+biName.left(biName.length()-2)+">"; } @@ -4772,7 +4796,7 @@ static bool findClassRelation( baseClass->setOuterScope(scope); } - if (baseClassName.right(2)=="-p") + if (baseClassName.endsWith("-p")) { baseClass->setCompoundType(ClassDef::Protocol); } @@ -4937,7 +4961,7 @@ static void computeClassRelations() } size_t numMembers = cd ? cd->memberNameInfoLinkedMap().size() : 0; if ((cd==0 || (!cd->hasDocumentation() && !cd->isReference())) && numMembers>0 && - bName.right(2)!="::") + !bName.endsWith("::")) { if (!root->name.isEmpty() && root->name.find('@')==-1 && // normal name (guessSection(root->fileName)==Entry::HEADER_SEC || @@ -5581,8 +5605,8 @@ static QCString substituteTemplatesInString( QCString tdaName = tda->name; QCString tdaType = tda->type; int vc=0; - if (tdaType.left(6)=="class ") vc=6; - else if (tdaType.left(9)=="typename ") vc=9; + if (tdaType.startsWith("class ")) vc=6; + else if (tdaType.startsWith("typename ")) vc=9; if (vc>0) // convert type=="class T" to type=="class" name=="T" { tdaName = tdaType.mid(vc); @@ -6277,13 +6301,13 @@ static void findMember(const Entry *root, ).stripWhiteSpace(); //printf("funcDecl='%s'\n",qPrint(funcDecl)); - if (isFriend && funcDecl.left(6)=="class ") + if (isFriend && funcDecl.startsWith("class ")) { //printf("friend class\n"); funcDecl=funcDecl.right(funcDecl.length()-6); funcName = funcDecl; } - else if (isFriend && funcDecl.left(7)=="struct ") + else if (isFriend && funcDecl.startsWith("struct ")) { funcDecl=funcDecl.right(funcDecl.length()-7); funcName = funcDecl; @@ -6527,7 +6551,7 @@ static void findMember(const Entry *root, } } - if (funcName.left(9)=="operator ") // strip class scope from cast operator + if (funcName.startsWith("operator ")) // strip class scope from cast operator { funcName = substitute(funcName,className+"::",""); } @@ -6861,7 +6885,7 @@ static void filterMemberDocumentation(const Entry *root,const QCString &relates) //printf("Results type=%s,name=%s,args=%s\n",qPrint(type),qPrint(root->name),qPrint(args)); isFunc=FALSE; } - else if ((type.left(8)=="typedef " && args.find('(')!=-1)) + else if ((type.startsWith("typedef ") && args.find('(')!=-1)) // detect function types marked as functions { isFunc=FALSE; @@ -7067,7 +7091,10 @@ static void findEnums(const Entry *root) { scope=root->name.left(i); // extract scope name=root->name.right(root->name.length()-i-2); // extract name - if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope); + if ((cd=getClassMutable(scope))==0) + { + nd=toNamespaceDefMutable(buildScopeFromQualifiedName(root->name.left(i+2),root->lang,root->tagInfo())); + } } else // no scope, check the scope in which the docs where found { @@ -7234,7 +7261,10 @@ static void addEnumValuesToEnums(const Entry *root) { scope=root->name.left(i); // extract scope name=root->name.right(root->name.length()-i-2); // extract name - if ((cd=getClassMutable(scope))==0) nd=getResolvedNamespaceMutable(scope); + if ((cd=getClassMutable(scope))==0) + { + nd=toNamespaceDefMutable(buildScopeFromQualifiedName(root->name.left(i+2),root->lang,root->tagInfo())); + } } else // no scope, check the scope in which the docs where found { @@ -7300,14 +7330,12 @@ static void addEnumValuesToEnums(const Entry *root) // use raw pointer in this loop, since we modify mn and can then invalidate mdp. if (md && md->isEnumerate() && !root->children().empty()) { - //printf(" enum with %d children\n",root->children()->count()); + //printf(" enum with %zu children\n",root->children().size()); for (const auto &e : root->children()) { - SrcLangExt sle; - if ( - (sle=root->lang)==SrcLangExt_CSharp || - sle==SrcLangExt_Java || - sle==SrcLangExt_XML || + SrcLangExt sle = root->lang; + bool isJavaLike = sle==SrcLangExt_CSharp || sle==SrcLangExt_Java || sle==SrcLangExt_XML; + if ( isJavaLike || (root->spec&Entry::Strong) ) { @@ -7315,15 +7343,13 @@ static void addEnumValuesToEnums(const Entry *root) // values are only visible inside the enum scope, so we must create // them here and only add them to the enum //printf("md->qualifiedName()=%s e->name=%s tagInfo=%p name=%s\n", - // qPrint(md->qualifiedName()),qPrint(e->name),e->tagInfo,qPrint(e->name)); - QCString qualifiedName = substitute(root->name,"::","."); - if (!scope.isEmpty() && root->tagInfo()) + // qPrint(md->qualifiedName()),qPrint(e->name),(void*)e->tagInfo(),qPrint(e->name)); + QCString qualifiedName = root->name; + if (isJavaLike) { - qualifiedName=substitute(scope,"::",".")+"."+qualifiedName; + qualifiedName=substitute(qualifiedName,".","::"); } - if (substitute(md->qualifiedName(),"::",".")== // TODO: add function to get canonical representation - qualifiedName // enum value scope matches that of the enum - ) + if (md->qualifiedName()==qualifiedName) // enum value scope matches that of the enum { QCString fileName = e->fileName; if (fileName.isEmpty() && e->tagInfo()) @@ -8387,13 +8413,16 @@ static void computeTooltipTexts() // queue the work for (const auto &kv : *Doxygen::symbolMap) { - DefinitionMutable *dm = toDefinitionMutable(kv.second); - if (dm && !isSymbolHidden(toDefinition(dm)) && toDefinition(dm)->isLinkableInProject()) + for (const auto &def : kv.second) { - auto processTooltip = [dm]() { - dm->computeTooltip(); - }; - results.emplace_back(threadPool.queue(processTooltip)); + DefinitionMutable *dm = toDefinitionMutable(def); + if (dm && !isSymbolHidden(toDefinition(dm)) && toDefinition(dm)->isLinkableInProject()) + { + auto processTooltip = [dm]() { + dm->computeTooltip(); + }; + results.emplace_back(threadPool.queue(processTooltip)); + } } } // wait for the results @@ -8406,10 +8435,13 @@ static void computeTooltipTexts() { for (const auto &kv : *Doxygen::symbolMap) { - DefinitionMutable *dm = toDefinitionMutable(kv.second); - if (dm && !isSymbolHidden(toDefinition(dm)) && toDefinition(dm)->isLinkableInProject()) + for (const auto &def : kv.second) { - dm->computeTooltip(); + DefinitionMutable *dm = toDefinitionMutable(def); + if (dm && !isSymbolHidden(toDefinition(dm)) && toDefinition(dm)->isLinkableInProject()) + { + dm->computeTooltip(); + } } } } @@ -8800,17 +8832,17 @@ static void flushCachedTemplateRelations() // to this class. Optimization: only remove those classes that // have inheritance instances as direct or indirect sub classes. StringVector elementsToRemove; - for (const auto &ci : *Doxygen::lookupCache) + for (const auto &ci : *Doxygen::typeLookupCache) { const LookupInfo &li = ci.second; - if (li.classDef) + if (li.definition) { elementsToRemove.push_back(ci.first); } } for (const auto &k : elementsToRemove) { - Doxygen::lookupCache->remove(k); + Doxygen::typeLookupCache->remove(k); } // remove all cached typedef resolutions whose target is a @@ -8860,17 +8892,17 @@ static void flushUnresolvedRelations() // class C : public B::I {}; StringVector elementsToRemove; - for (const auto &ci : *Doxygen::lookupCache) + for (const auto &ci : *Doxygen::typeLookupCache) { const LookupInfo &li = ci.second; - if (li.classDef==0 && li.typeDef==0) + if (li.definition==0 && li.typeDef==0) { elementsToRemove.push_back(ci.first); } } for (const auto &k : elementsToRemove) { - Doxygen::lookupCache->remove(k); + Doxygen::typeLookupCache->remove(k); } // for each global function name @@ -9738,7 +9770,7 @@ static void generateConfigFile(const QCString &configFile,bool shortList, } } -static void compareDoxyfile(DoxyfileSettings diffList) +static void compareDoxyfile(Config::CompareMode diffList) { std::ofstream f; bool fileOpened=openOutputFile("-",f); @@ -9825,28 +9857,31 @@ static void copyStyleSheet() QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET); if (!htmlStyleSheet.isEmpty()) { - FileInfo fi(htmlStyleSheet.str()); - if (!fi.exists()) + if (!htmlStyleSheet.startsWith("http:") && !htmlStyleSheet.startsWith("https:")) { - err("Style sheet '%s' specified by HTML_STYLESHEET does not exist!\n",qPrint(htmlStyleSheet)); - htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default - } - else - { - QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName(); - copyFile(htmlStyleSheet,destFileName); + FileInfo fi(htmlStyleSheet.str()); + if (!fi.exists()) + { + err("Style sheet '%s' specified by HTML_STYLESHEET does not exist!\n",qPrint(htmlStyleSheet)); + htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default + } + else + { + QCString destFileName = Config_getString(HTML_OUTPUT)+"/"+fi.fileName(); + copyFile(htmlStyleSheet,destFileName); + } } } const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET); for (const auto &sheet : htmlExtraStyleSheet) { - std::string fileName = sheet; - if (!fileName.empty()) + QCString fileName(sheet); + if (!fileName.isEmpty() && !fileName.startsWith("http:") && !fileName.startsWith("https:")) { - FileInfo fi(fileName); + FileInfo fi(fileName.str()); if (!fi.exists()) { - err("Style sheet '%s' specified by HTML_EXTRA_STYLESHEET does not exist!\n",fileName.c_str()); + err("Style sheet '%s' specified by HTML_EXTRA_STYLESHEET does not exist!\n",qPrint(fileName)); } else if (fi.fileName()=="doxygen.css" || fi.fileName()=="tabs.css" || fi.fileName()=="navtree.css") { @@ -10308,7 +10343,7 @@ static std::string resolveSymlink(const std::string &path) { #if defined(_WIN32) // UNC path, skip server and share name - if (sepPos==0 && (result.left(2)=="//" || result.left(2)=="\\\\")) + if (sepPos==0 && (result.startsWith("//") || result.startsWith("\\\\"))) sepPos = result.find('/',2); if (sepPos!=-1) sepPos = result.find('/',sepPos+1); @@ -10670,7 +10705,10 @@ static void dumpSymbolMap() TextStream t(&f); for (const auto &kv : *Doxygen::symbolMap) { - dumpSymbol(t,kv.second); + for (const auto &def : kv.second) + { + dumpSymbol(t,def); + } } } } @@ -10745,7 +10783,7 @@ static void usage(const QCString &name,const QCString &versionString) msg("7) Use doxygen to compare the used configuration file with the template configuration file\n"); msg(" %s -x [configFile]\n\n",qPrint(name)); msg(" Use doxygen to compare the used configuration file with the template configuration file\n"); - msg(" without replacing the environment variables\n"); + msg(" without replacing the environment variables or CMake type replacement variables\n"); msg(" %s -x_noenv [configFile]\n\n",qPrint(name)); msg("8) Use doxygen to show a list of built-in emojis.\n"); msg(" %s -f emoji outputFileName\n\n",qPrint(name)); @@ -10932,7 +10970,7 @@ void readConfiguration(int argc, char **argv) QCString listName; bool genConfig=FALSE; bool shortList=FALSE; - DoxyfileSettings diffList=DoxyfileSettings::Full; + Config::CompareMode diffList=Config::CompareMode::Full; bool updateConfig=FALSE; int retVal; bool quiet = false; @@ -10977,8 +11015,8 @@ void readConfiguration(int argc, char **argv) } break; case 'x': - if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=DoxyfileSettings::CompressedNoEnv; - else if (!strcmp(argv[optInd]+1,"x")) diffList=DoxyfileSettings::Compressed; + if (!strcmp(argv[optInd]+1,"x_noenv")) diffList=Config::CompareMode::CompressedNoEnv; + else if (!strcmp(argv[optInd]+1,"x")) diffList=Config::CompareMode::Compressed; else { err("option should be \"-x\" or \"-x_noenv\", found: \"%s\".\n",argv[optInd]); @@ -11072,8 +11110,14 @@ void readConfiguration(int argc, char **argv) TextStream t(&f); RTFGenerator::writeStyleSheetFile(t); } + else + { + err("error opening RTF style sheet file %s!\n",argv[optInd+1]); + cleanUpDoxygen(); + exit(1); + } cleanUpDoxygen(); - exit(1); + exit(0); } else if (qstricmp(formatName.data(),"html")==0) { @@ -11296,14 +11340,14 @@ void readConfiguration(int argc, char **argv) exit(0); } - if (!Config::parse(configName,updateConfig)) + if (!Config::parse(configName,updateConfig,diffList)) { err("could not open or read configuration file %s!\n",qPrint(configName)); cleanUpDoxygen(); exit(1); } - if (diffList!=DoxyfileSettings::Full) + if (diffList!=Config::CompareMode::Full) { Config::updateObsolete(); compareDoxyfile(diffList); @@ -11395,6 +11439,54 @@ void adjustConfiguration() } } } + // create input file exncodings + + // check INPUT_ENCODING + void *cd = portable_iconv_open("UTF-8",Config_getString(INPUT_ENCODING).data()); + if (cd==reinterpret_cast<void *>(-1)) + { + term("unsupported character conversion: '%s'->'%s': %s\n" + "Check the 'INPUT_ENCODING' setting in the config file!\n", + qPrint(Config_getString(INPUT_ENCODING)),qPrint("UTF-8"),strerror(errno)); + } + else + { + portable_iconv_close(cd); + } + + // check and split INPUT_FILE_ENCODING + const StringVector &fileEncod = Config_getList(INPUT_FILE_ENCODING); + for (const auto &mapping : fileEncod) + { + QCString mapStr = mapping.c_str(); + int i=mapStr.find('='); + if (i==-1) + { + continue; + } + else + { + QCString pattern = mapStr.left(i).stripWhiteSpace().lower(); + QCString encoding = mapStr.mid(i+1).stripWhiteSpace().lower(); + if (pattern.isEmpty() || encoding.isEmpty()) + { + continue; + } + cd = portable_iconv_open("UTF-8",encoding.data()); + if (cd==reinterpret_cast<void *>(-1)) + { + term("unsupported character conversion: '%s'->'%s': %s\n" + "Check the 'INPUT_FILE_ENCODING' setting in the config file!\n", + qPrint(encoding),qPrint("UTF-8"),strerror(errno)); + } + else + { + portable_iconv_close(cd); + } + + Doxygen::inputFileEncodingList.push_back(InputFileEncoding(pattern, encoding)); + } + } // add predefined macro name to a dictionary const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED); @@ -11723,6 +11815,10 @@ void searchInputFiles() }); } } + if (Doxygen::inputNameLinkedMap->empty()) + { + warn_uncond("No files to be processed, please check your settings, in particular INPUT, FILE_PATTERNS, and RECURSIVE"); + } g_s.end(); } @@ -11782,7 +11878,8 @@ void parseInput() if (cacheSize<0) cacheSize=0; if (cacheSize>9) cacheSize=9; uint lookupSize = 65536 << cacheSize; - Doxygen::lookupCache = new Cache<std::string,LookupInfo>(lookupSize); + Doxygen::typeLookupCache = new Cache<std::string,LookupInfo>(lookupSize); + Doxygen::symbolLookupCache = new Cache<std::string,LookupInfo>(lookupSize); #ifdef HAS_SIGNALS signal(SIGINT, stopDoxygen); @@ -11933,24 +12030,19 @@ void parseInput() if (Config_getBool(GENERATE_HTML) && !Config_getBool(USE_MATHJAX)) { - FormulaManager::instance().readFormulas(Config_getString(HTML_OUTPUT)); + FormulaManager::instance().initFromRepository(Config_getString(HTML_OUTPUT)); } if (Config_getBool(GENERATE_RTF)) { - // in case GENERRATE_HTML is set we just have to compare, both repositories should be identical - FormulaManager::instance().readFormulas(Config_getString(RTF_OUTPUT), - Config_getBool(GENERATE_HTML) && - !Config_getBool(USE_MATHJAX)); + FormulaManager::instance().initFromRepository(Config_getString(RTF_OUTPUT)); } if (Config_getBool(GENERATE_DOCBOOK)) { - // in case GENERRATE_HTML is set we just have to compare, both repositories should be identical - FormulaManager::instance().readFormulas(Config_getString(DOCBOOK_OUTPUT), - (Config_getBool(GENERATE_HTML) && - !Config_getBool(USE_MATHJAX)) || - Config_getBool(GENERATE_RTF)); + FormulaManager::instance().initFromRepository(Config_getString(DOCBOOK_OUTPUT)); } + FormulaManager::instance().checkRepositories(); + /************************************************************************** * Handle Tag Files * **************************************************************************/ @@ -12029,7 +12121,7 @@ void parseInput() // calling buildClassList may result in cached relations that // become invalid after resolveClassNestingRelations(), that's why // we need to clear the cache here - Doxygen::lookupCache->clear(); + Doxygen::typeLookupCache->clear(); // we don't need the list of using declaration anymore g_usingDeclarations.clear(); @@ -12462,7 +12554,7 @@ void generateOutput() copyLogo(Config_getString(RTF_OUTPUT)); } - const FormulaManager &fm = FormulaManager::instance(); + FormulaManager &fm = FormulaManager::instance(); if (fm.hasFormulas() && generateHtml && !Config_getBool(USE_MATHJAX)) { @@ -12653,13 +12745,19 @@ void generateOutput() g_outputList->cleanup(); - int cacheParam; - msg("lookup cache used %zu/%zu hits=%" PRIu64 " misses=%" PRIu64 "\n", - Doxygen::lookupCache->size(), - Doxygen::lookupCache->capacity(), - Doxygen::lookupCache->hits(), - Doxygen::lookupCache->misses()); - cacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::lookupCache->misses()*2/3)); // part of the cache is flushed, hence the 2/3 correction factor + msg("type lookup cache used %zu/%zu hits=%" PRIu64 " misses=%" PRIu64 "\n", + Doxygen::typeLookupCache->size(), + Doxygen::typeLookupCache->capacity(), + Doxygen::typeLookupCache->hits(), + Doxygen::typeLookupCache->misses()); + msg("symbol lookup cache used %zu/%zu hits=%" PRIu64 " misses=%" PRIu64 "\n", + Doxygen::symbolLookupCache->size(), + Doxygen::symbolLookupCache->capacity(), + Doxygen::symbolLookupCache->hits(), + Doxygen::symbolLookupCache->misses()); + int typeCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::typeLookupCache->misses()*2/3)); // part of the cache is flushed, hence the 2/3 correction factor + int symbolCacheParam = computeIdealCacheParam(static_cast<size_t>(Doxygen::symbolLookupCache->misses())); + int cacheParam = std::max(typeCacheParam,symbolCacheParam); if (cacheParam>Config_getInt(LOOKUP_CACHE_SIZE)) { msg("Note: based on cache misses the ideal setting for LOOKUP_CACHE_SIZE is %d at the cost of higher memory usage.\n",cacheParam); diff --git a/src/doxygen.h b/src/doxygen.h index d0ecc1b..53d30ee 100644 --- a/src/doxygen.h +++ b/src/doxygen.h @@ -58,14 +58,24 @@ class NamespaceDefMutable; struct LookupInfo { LookupInfo() = default; - LookupInfo(const ClassDef *cd,const MemberDef *td,QCString ts,QCString rt) - : classDef(cd), typeDef(td), templSpec(ts),resolvedType(rt) {} - const ClassDef *classDef = 0; + LookupInfo(const Definition *d,const MemberDef *td,QCString ts,QCString rt) + : definition(d), typeDef(td), templSpec(ts),resolvedType(rt) {} + const Definition *definition = 0; const MemberDef *typeDef = 0; QCString templSpec; QCString resolvedType; }; +struct InputFileEncoding +{ + InputFileEncoding() {} + InputFileEncoding(const QCString &pat, const QCString &enc) : pattern(pat), encoding(enc) {} + QCString pattern; + QCString encoding; +}; + +using InputFileEncodingList = std::vector<InputFileEncoding>; + using ClangUsrMap = std::unordered_map<std::string,const Definition *>; /*! \brief This class serves as a namespace for global variables used by doxygen. @@ -105,7 +115,8 @@ class Doxygen static SearchIndexIntf *searchIndex; static SymbolMap<Definition> *symbolMap; static ClangUsrMap *clangUsrMap; - static Cache<std::string,LookupInfo> *lookupCache; + static Cache<std::string,LookupInfo> *typeLookupCache; + static Cache<std::string,LookupInfo> *symbolLookupCache; static DirLinkedMap *dirLinkedMap; static DirRelationLinkedMap dirRelations; static ParserManager *parserManager; @@ -119,6 +130,7 @@ class Doxygen static bool clangAssistedParsing; static QCString verifiedDotPath; static volatile bool terminating; + static InputFileEncodingList inputFileEncodingList; }; /** Deleter that only deletes an object if doxygen is not already terminating */ @@ -139,7 +151,6 @@ void adjustConfiguration(); void parseInput(); void generateOutput(); void readAliases(); -void readFormulaRepository(QCString dir, bool cmp = FALSE); void cleanUpDoxygen(); void readFileOrDirectory(const QCString &s, FileNameLinkedMap *fnDict, diff --git a/src/doxygen.md b/src/doxygen.md index de38efe..bc3c1be 100644 --- a/src/doxygen.md +++ b/src/doxygen.md @@ -1,6 +1,8 @@ -Doxygen Internals {#mainpage} +%Doxygen Internals {#mainpage} ================= +Generated on \showdate "%A, %B %-d, %Y at %-I:%M %p" + Introduction ============ @@ -22,10 +24,11 @@ option. The format of the configuration file (options and types) is defined by the file `config.xml`. As part of the build process, -the python script `configgen.py` will create a file `configoptions.cpp` -from this, which serves as the input for the configuration file parser -that is invoked using Config::parse(). The script `configgen.py` will also -create the documentation for the configuration items, creating the file +the python script `configgen.py` will create the files `configoptions.cpp`, +`configvalues.h` and `configvalues.cpp` from this, which serves as the input +for the configuration file parser that is invoked using Config::parse(). +The script `configgen.py` will also create the documentation for the +configuration items, creating the file `config.doc`. Gathering Input files @@ -37,12 +40,19 @@ searchInputFiles() and any tag files are read using readTagFile() Parsing Input files =================== -The function parseFiles() takes care of parsing all files. -It uses the ParserManager singleton factory to create a suitable parser object -for each file. Each parser implements the abstract interface ParserInterface. +The function parseFilesSingleThreading() takes care of parsing all files +(in case `NUM_PROC_THREADS!=1`, the function +parseFilesMultiThreading() is used instead). + +These functions use the ParserManager singleton factory to create a suitable parser object +for each file. Each parser implements two abstract interfaces: OutlineParserInterface +en CodeParserInterface. The OutlineParserInterface is used to collect information +about the symbols that can be documented but does not look into the body of functions. +The CodeParserInterface is used for syntax highlighting, but also to collect the symbol +references needed for cross reference relations. If the parser indicates it needs preprocessing -via ParserInterface::needsPreprocessing(), doxygen will call preprocessFile() +via OutlineParserInterface::needsPreprocessing(), doxygen will call Preprocessor::processFile() on the file. A second step is to convert multiline C++-style comments into C style comments @@ -54,31 +64,26 @@ aliases (ALIASES option) are resolved. The function that performs these now coupled to C/C++ code and does not work automatically for other languages! The third step is the actual language parsing and is done by calling -ParserInterface::parseInput() on the parser interface returned by +OutlineParserInterface::parseInput() on the parser interface returned by the ParserManager. The result of parsing is a tree of Entry objects. -These Entry objects are wrapped in a EntryNav object and stored on disk using -Entry::createNavigationIndex() on the root node of the tree. - Each Entry object roughly contains the raw data for a symbol and is later converted into a Definition object. When a parser finds a special comment block in the input, it will do a first -pass parsing via parseCommentBlock(). During this pass the comment block +pass parsing via CommentScanner::parseCommentBlock(). During this pass the comment block is split into multiple parts if needed. Some data that is later needed is extracted like section labels, xref items, and formulas. -Also Markdown markup is processed using processMarkdown() during this pass. +Also Markdown markup is processed via Markdown::process() during this pass. Resolving relations =================== -The Entry objects created and filled during parsing are stored on disk -(to keep memory needs low). The name, parent/child relation, and -location on disk of each Entry is stored as a tree of EntryNav nodes, which is -kept in memory. +The Entry objects created and filled during parsing and stored as a tree of Entry nodes, +which is kept in memory. -Doxygen does a number of tree walks over the EntryNav nodes in the tree to +%Doxygen does a number of tree walks over the Entry nodes in the tree to build up the data structures needed to produce the output. The resulting data structures are all children of the generic base class @@ -86,6 +91,7 @@ called Definition which holds all non-specific data for a symbol definition. Definition is an abstract base class. Concrete subclasses are - ClassDef: for storing class/struct/union related data +- ConceptDef: for storing C++20 concept definitions - NamespaceDef: for storing namespace related data - FileDef: for storing file related data - DirDef: for storing directory related data @@ -95,7 +101,8 @@ For doxygen specific concepts the following subclasses are available - PageDef: for storing page related data Finally the data for members of classes, namespaces, and files is stored in -the subclass MemberDef. +the subclass MemberDef. This class is used for functions, variables, enums, etc, as indicated by +MemberDef::memberType(). Producing debug output ====================== @@ -115,12 +122,13 @@ easy ways to get debug information. - set the item `Write used lex rules` to `Yes` - see to it that the `.l` file is newer than the corresponding `.cpp` file or remove the corresponding `.cpp` file + - when using `nmake` the same possibilities exist as described with "unices". - unices - global change<br> In the chapter "Doxygen's internals" a `perl` script is given to toggle the possibility of having the rules debug information. - command line change<br> - It is possible to the option `LEX="flex -d"` with the `make` command on the + It is possible to the option `LEX_FLAGS="-d"` with the `make` command on the command line. In this case the `.l` that are converted to the corresponding `.cpp` files during this `make` get the rules debug information.<br> To undo the rules debug information output just recompile the file with @@ -144,6 +152,9 @@ easy ways to get debug information. Shows the results of the preprocessing phase, i.e. results from include files, <tt>\#define</tt> statements etc., definitions in the doxygen configuration file like: `EXPAND_ONLY_PREDEF`, `PREDEFINED` and `MACRO_EXPANSION`. + - nolineno<br> + In case the line numbers in the results of the preprocessing phase are not wanted they + can be removed by means of this option (without `-d preprocessor` this option has no effect. - commentcnv<br> Shows the results of the comment conversion, the comment conversion does the following: @@ -172,13 +183,32 @@ easy ways to get debug information. Provide output of the `lex` files used. When a lexer is started and when a lexer ends the name of the `lex` file is given so it is possible to see in which lexer the problem occurs. This makes it easier to select the file to be compiled in `lex` debug mode. + - cite<br> + Retains the temporary files as created and used for the non LaTeX output results of the + generation of the bibliographical references. + - fortranfixed2free<br> + Shows the result ogf the conversion of Fortran fixed formatted files to Fortran free formatted + files as done by doxygen. + - plantuml<br> + Shows information about the plantuml process run and the used input / output filenames, the content of the + input file. + - rtf<br> + - Shows the original names and the anchors names (called bookmarks in RTF) where they are mapped to. + - At the end of the generation of the RTF files these files are merged into one large RTF file, with + this option the original files are retained. + - qhp<br> + - The qhp file is created with indentation for better readability (normally no indentation so the file is smaller). + - When the setting `QHG_LOCATION` is pointing to the `qhelpgenerator` besides generating the `qch` file + also some extra checks are done by means of the `-c` flag of the `qhelpgenerator`. + - tag<br> + Shows the results of reading the tag files. Producing output ================ TODO -Topics TODO +Documentation Topics TODO =========== - Grouping of files in Model / Parser / Generator categories - Index files based on IndexIntf diff --git a/src/eclipsehelp.cpp b/src/eclipsehelp.cpp index 53a370b..73eb405 100644 --- a/src/eclipsehelp.cpp +++ b/src/eclipsehelp.cpp @@ -12,47 +12,54 @@ * input used in their production; they are not affected by this license. * */ + +#include <fstream> + #include "eclipsehelp.h" #include "util.h" #include "config.h" #include "message.h" #include "doxygen.h" -EclipseHelp::EclipseHelp() : m_depth(0), m_endtag(FALSE), m_openTags(0) +struct EclipseHelp::Private { -} + int depth = 0; + bool endtag = false; + int openTags = 0; -EclipseHelp::~EclipseHelp() -{ -} + std::ofstream tocstream; + QCString pathprefix; -void EclipseHelp::indent() -{ - int i; - for (i=0; i<m_depth; i++) + /* -- formatting helpers */ + void indent() { - m_tocstream << " "; + for (int i=0; i<depth; i++) + { + tocstream << " "; + } } -} - -void EclipseHelp::closedTag() -{ - if (m_endtag) + void closedTag() { - m_tocstream << "/>\n"; - m_endtag = FALSE; + if (endtag) + { + tocstream << "/>\n"; + endtag = FALSE; + } } -} - -void EclipseHelp::openedTag() -{ - if (m_endtag) + void openedTag() { - m_tocstream << ">\n"; - m_endtag = FALSE; - ++m_openTags; + if (endtag) + { + tocstream << ">\n"; + endtag = FALSE; + ++openTags; + } } -} +}; + +EclipseHelp::EclipseHelp() : p(std::make_unique<Private>()) {} +EclipseHelp::~EclipseHelp() = default; +EclipseHelp::EclipseHelp(EclipseHelp&&) = default; /*! * \brief Initialize the Eclipse generator @@ -62,14 +69,10 @@ void EclipseHelp::openedTag() */ void EclipseHelp::initialize() { - // -- read path prefix from the configuration - //m_pathprefix = Config_getString(ECLIPSE_PATHPREFIX); - //if (m_pathprefix.isEmpty()) m_pathprefix = "html/"; - // -- open the contents file QCString name = Config_getString(HTML_OUTPUT) + "/toc.xml"; - m_tocstream.open(name.str(), std::ofstream::out | std::ofstream::binary); - if (!m_tocstream.is_open()) + p->tocstream.open(name.str(), std::ofstream::out | std::ofstream::binary); + if (!p->tocstream.is_open()) { term("Could not open file %s for writing\n", qPrint(name)); } @@ -80,10 +83,10 @@ void EclipseHelp::initialize() { title = "Doxygen generated documentation"; } - m_tocstream << "<toc label=\"" << convertToXML(title) - << "\" topic=\"" << convertToXML(m_pathprefix) + p->tocstream << "<toc label=\"" << convertToXML(title) + << "\" topic=\"" << convertToXML(p->pathprefix) << "index" << Doxygen::htmlFileExtension << "\">\n"; - ++ m_depth; + ++ p->depth; } /*! @@ -94,14 +97,14 @@ void EclipseHelp::initialize() */ void EclipseHelp::finalize() { - closedTag(); // -- close previous tag + p->closedTag(); // -- close previous tag // -- write ending tag - --m_depth; - m_tocstream << "</toc>\n"; + --p->depth; + p->tocstream << "</toc>\n"; // -- close the content file - m_tocstream.close(); + p->tocstream.close(); QCString name = Config_getString(HTML_OUTPUT) + "/plugin.xml"; std::ofstream t(name.str(),std::ofstream::out | std::ofstream::binary); @@ -122,8 +125,8 @@ void EclipseHelp::finalize() */ void EclipseHelp::incContentsDepth() { - openedTag(); - ++m_depth; + p->openedTag(); + ++p->depth; } /*! @@ -134,14 +137,14 @@ void EclipseHelp::incContentsDepth() void EclipseHelp::decContentsDepth() { // -- end of the opened topic - closedTag(); - --m_depth; + p->closedTag(); + --p->depth; - if (m_openTags==m_depth) + if (p->openTags==p->depth) { - --m_openTags; - indent(); - m_tocstream << "</topic>\n"; + --p->openTags; + p->indent(); + p->tocstream << "</topic>\n"; } } @@ -168,7 +171,7 @@ void EclipseHelp::addContentsItem( const Definition * /*def*/) { // -- write the topic tag - closedTag(); + p->closedTag(); if (!file.isEmpty()) { switch (file[0]) // check for special markers (user defined URLs) @@ -178,31 +181,31 @@ void EclipseHelp::addContentsItem( break; case '!': - indent(); - m_tocstream << "<topic label=\"" << convertToXML(name) << "\""; - m_tocstream << " href=\"" << convertToXML(m_pathprefix) << &file[1] << "\""; - m_endtag = TRUE; + p->indent(); + p->tocstream << "<topic label=\"" << convertToXML(name) << "\""; + p->tocstream << " href=\"" << convertToXML(p->pathprefix) << &file[1] << "\""; + p->endtag = TRUE; break; default: - indent(); - m_tocstream << "<topic label=\"" << convertToXML(name) << "\""; - m_tocstream << " href=\"" << convertToXML(m_pathprefix) + p->indent(); + p->tocstream << "<topic label=\"" << convertToXML(name) << "\""; + p->tocstream << " href=\"" << convertToXML(p->pathprefix) << addHtmlExtensionIfMissing(file); if (!anchor.isEmpty()) { - m_tocstream << "#" << anchor; + p->tocstream << "#" << anchor; } - m_tocstream << "\""; - m_endtag = TRUE; + p->tocstream << "\""; + p->endtag = TRUE; break; } } else { - indent(); - m_tocstream << "<topic label=\"" << convertToXML(name) << "\""; - m_endtag = TRUE; + p->indent(); + p->tocstream << "<topic label=\"" << convertToXML(name) << "\""; + p->endtag = TRUE; } } diff --git a/src/eclipsehelp.h b/src/eclipsehelp.h index 1ba4df4..68d023b 100644 --- a/src/eclipsehelp.h +++ b/src/eclipsehelp.h @@ -24,12 +24,13 @@ #ifndef ECLIPSEHELP_H #define ECLIPSEHELP_H -#include <fstream> +#include <memory> -#include "index.h" +#include "qcstring.h" /* -- forward declarations */ class Definition; +class MemberDef; /** Generator for Eclipse help files. * @@ -37,11 +38,12 @@ class Definition; * These files can be used to generate a help plugin readable * by the Eclipse IDE. */ -class EclipseHelp : public IndexIntf +class EclipseHelp { public: EclipseHelp(); virtual ~EclipseHelp(); + EclipseHelp(EclipseHelp &&); /* -- index interface */ virtual void initialize(); @@ -58,21 +60,8 @@ class EclipseHelp : public IndexIntf virtual void addStyleSheetFile(const QCString &name); private: - int m_depth; - bool m_endtag; - int m_openTags; - - std::ofstream m_tocstream; - QCString m_pathprefix; - - /* -- avoid copying */ - EclipseHelp(const EclipseHelp &); - EclipseHelp & operator = (const EclipseHelp &); - - /* -- formatting helpers */ - void indent(); - void closedTag(); - void openedTag(); + struct Private; + std::unique_ptr<Private> p; }; #endif /* ECLIPSEHELP_H */ diff --git a/src/filedef.cpp b/src/filedef.cpp index cb67f00..608310e 100644 --- a/src/filedef.cpp +++ b/src/filedef.cpp @@ -15,6 +15,8 @@ * */ +#include <unordered_set> + #include "memberlist.h" #include "classlist.h" #include "filedef.h" @@ -1510,11 +1512,11 @@ void FileDefImpl::combineUsingRelations() bool FileDefImpl::isDocumentationFile() const { - return name().right(4)==".doc" || - name().right(4)==".txt" || - name().right(4)==".dox" || - name().right(3)==".md" || - name().right(9)==".markdown" || + static const std::unordered_set<std::string> docExtensions = + { "doc", "txt", "dox", "md", "markdown" }; + + int lastDot = name().findRev('.'); + return (lastDot!=-1 && docExtensions.find(name().mid(lastDot+1).str())!=docExtensions.end()) || getLanguageFromFileName(getFileNameExtension(name())) == SrcLangExt_Markdown; } @@ -1522,7 +1524,7 @@ void FileDefImpl::acquireFileVersion() { QCString vercmd = Config_getString(FILE_VERSION_FILTER); if (!vercmd.isEmpty() && !m_filePath.isEmpty() && - m_filePath!="generated" && m_filePath!="graph_legend") + m_filePath!="generated" && m_filePath!="graph_legend.dox") { msg("Version of %s : ",qPrint(m_filePath)); QCString cmd = vercmd+" \""+m_filePath+"\""; diff --git a/src/filedef.h b/src/filedef.h index 8b90ecd..6d1d8b7 100644 --- a/src/filedef.h +++ b/src/filedef.h @@ -20,7 +20,6 @@ #include <set> -#include "index.h" #include "definition.h" #include "memberlist.h" #include "containers.h" diff --git a/src/fileinfo.cpp b/src/fileinfo.cpp index fdc6b6c..72b591e 100644 --- a/src/fileinfo.cpp +++ b/src/fileinfo.cpp @@ -77,7 +77,7 @@ bool FileInfo::isDir() const bool FileInfo::isSymLink() const { std::error_code ec; - fs::file_status status = fs::status(m_name,ec); + fs::file_status status = fs::symlink_status(m_name,ec); return !ec && fs::is_symlink(status); } diff --git a/src/filename.h b/src/filename.h index aae254f..3d8659c 100644 --- a/src/filename.h +++ b/src/filename.h @@ -20,8 +20,8 @@ #include <vector> #include "linkedmap.h" -#include "config.h" #include "utf8.h" +#include "util.h" class FileDef; @@ -59,7 +59,7 @@ class FileNameFn std::string searchKey(std::string input) const { std::string key = input; - if (!Config_getBool(CASE_SENSE_NAMES)) + if (!getCaseSenseNames()) { key = convertUTF8ToLower(key); } diff --git a/src/formula.cpp b/src/formula.cpp index 3217ae1..c39d325 100644 --- a/src/formula.cpp +++ b/src/formula.cpp @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright (C) 1997-2020 by Dimitri van Heesch. + * Copyright (C) 1997-2022 by Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby @@ -21,6 +21,8 @@ #include "image.h" #include "fileinfo.h" #include "dir.h" +#include "regex.h" +#include "linkedmap.h" #include <map> #include <vector> @@ -29,8 +31,8 @@ #include <fstream> // TODO: remove these dependencies -#include "doxygen.h" // for Doxygen::indexList -#include "index.h" // for Doxygen::indexList +#include "doxygen.h" // for Doxygen::indexList +#include "indexlist.h" // for Doxygen::indexList static int determineInkscapeVersion(Dir &thisDir); @@ -40,25 +42,13 @@ static int determineInkscapeVersion(Dir &thisDir); struct FormulaManager::Private { - void storeDisplaySize(int id,int w,int h) - { - displaySizeMap.insert(std::make_pair(id,DisplaySize(w,h))); - } - DisplaySize getDisplaySize(int id) - { - auto it = displaySizeMap.find(id); - if (it!=displaySizeMap.end()) - { - return it->second; - } - return DisplaySize(-1,-1); - } - StringVector formulas; - std::map<std::string,size_t> formulaMap; - std::map<int,DisplaySize> displaySizeMap; + LinkedMap<Formula> formulas; + std::map<int,Formula *> formulaIdMap; + bool repositoriesValid = true; + StringVector tempFiles; }; -FormulaManager::FormulaManager() : p(new Private) +FormulaManager::FormulaManager() : p(std::make_unique<Private>()) { } @@ -68,7 +58,7 @@ FormulaManager &FormulaManager::instance() return fm; } -void FormulaManager::readFormulas(const QCString &dir,bool doCompare) +void FormulaManager::initFromRepository(const QCString &dir) { std::ifstream f(dir.str()+"/formula.repository",std::ifstream::in); if (f.is_open()) @@ -94,62 +84,107 @@ void FormulaManager::readFormulas(const QCString &dir,bool doCompare) line += "\n" + readLine; } - // format: \_form#<digits>=<digits>x<digits>:formula - size_t hi=line.find('#'); - size_t ei=line.find('='); - size_t se=line.find(':'); // find name and text separator. - if (ei==std::string::npos || hi==std::string::npos || se==std::string::npos || hi>se || ei<hi || ei>se) + // new format: \_form#<digits>=<digits>x<digits>:formula + static const reg::Ex re_new(R"(\\_form#(\d+)=(\d+)x(\d+):)"); + // old format: \_form#<digits>:formula + static const reg::Ex re_old(R"(\\_form#(\d+):)"); + + reg::Match match; + int id = -1; + int width = -1; + int height = -1; + std::string text; + if (reg::search(line,match,re_new)) // try new format first + { + id = std::stoi(match[1].str()); + width = std::stoi(match[2].str()); + height = std::stoi(match[3].str()); + text = line.substr(match.position()+match.length()); + //printf("new format found id=%d width=%d height=%d text=%s\n",id,width,height,text.c_str()); + } + else if (reg::search(line,match,re_old)) // check for old format { - warn_uncond("%s/formula.repository is corrupted at line %d!\n",qPrint(dir),lineNr); + //id = std::stoi(match[1].str()); + //text = line.substr(match.position()+match.length()); + //printf("old format found id=%d text=%s\n",id,text.c_str()); + msg("old formula.repository format detected; forcing upgrade.\n"); + p->repositoriesValid = false; break; } - else + else // unexpected content { - std::string formName = line.substr(0,se); // '\_form#<digits>=<digits>x<digits>' part - std::string formText = line.substr(se+1); // 'formula' part - int w=-1,h=-1; - size_t xi=formName.find('x',ei); - if (xi!=std::string::npos) - { - w=std::stoi(formName.substr(ei+1,xi-ei-1)); // digits from '=<digits>x' part as int - h=std::stoi(formName.substr(xi+1)); // digits from 'x<digits>' part as int - } - formName = formName.substr(0,ei); // keep only the '\_form#<digits>' part - if (doCompare) - { - int formId = std::stoi(formName.substr(hi+1)); - std::string storedFormText = FormulaManager::instance().findFormula(formId); - if (storedFormText!=formText) - { - term("discrepancy between formula repositories! Remove " - "formula.repository and from_* files from output directories.\n"); - } - formulaCount++; - } - int id = addFormula(formText); - if (w!=-1 && h!=-1) + warn_uncond("%s/formula.repository contains invalid content at line %d: found: '%s'\n",qPrint(dir),lineNr,line.c_str()); + p->repositoriesValid = false; + break; + } + + auto it = p->formulaIdMap.find(id); + Formula *formula=0; + if (it!=p->formulaIdMap.end()) // formula already found in a repository for another output format + { + formula = it->second; + if (formula->text().str()!=text) // inconsistency between repositories detected { - p->storeDisplaySize(id,w,h); + msg("differences detected between formula.repository files; forcing upgrade.\n"); + p->repositoriesValid = false; + break; } + formulaCount++; + } + else // create new formula from cache + { + //printf("formula not found adding it under id=%d\n",id); + formula = p->formulas.add(text.c_str(),id,width,height); + p->formulaIdMap.insert(std::make_pair(id,formula)); + } + + if (formula) // if an entry in the repository exists also check if there is a generated image + { + QCString formImgName; + formImgName.sprintf("form_%d",formula->id()); + FileInfo fiPng((dir+"/"+formImgName+".png").str()); + FileInfo fiSvg((dir+"/"+formImgName+".svg").str()); + // mark formula as cached, so we do not need to regenerate the images + bool isCached = fiPng.exists() || fiSvg.exists(); + formula->setCached(isCached); + //printf("formula %d: cached=%d\n",formula->id(),isCached); + + FileInfo fiPngDark((dir+"/"+formImgName+"_dark.png").str()); + FileInfo fiSvgDark((dir+"/"+formImgName+"_dark.svg").str()); + bool isCachedDark = fiPngDark.exists() || fiSvgDark.exists(); + formula->setCachedDark(isCachedDark); + //printf("formula %d: cachedDark=%d\n",formula->id(),isCachedDark); } } - if (doCompare && formulaCount!=p->formulas.size()) + + // For the first repository all formulas should be new (e.g. formulaCount==0). + // For the other repositories the same number of formulas should be found + // (and number of formulas should be the same for all repositories, content is already check above) + if (formulaCount>0 && formulaCount!=p->formulas.size()) // inconsistency between repositories { - term("size discrepancy between formula repositories! Remove " - "formula.repository and from_* files from output directories.\n"); + msg("differences detected between formula.repository files; forcing upgrade.\n"); + p->repositoriesValid = false; } } + else // no repository found for an output format + { + p->repositoriesValid = false; + } } -void FormulaManager::generateImages(const QCString &path,Format format,HighDPI hd) const +void FormulaManager::checkRepositories() { - Dir d(path.str()); - // store the original directory - if (!d.exists()) + //printf("checkRepositories valid=%d\n",p->repositoriesValid); + if (!p->repositoriesValid) { - term("Output directory '%s' does not exist!\n",qPrint(path)); + clear(); // clear cached formulas, so the corresponding images and repository files + // are regenerated + p->repositoriesValid = true; } - std::string oldDir = Dir::currentDirPath(); +} + +void FormulaManager::createLatexFile(const QCString &fileName,Format format,Mode mode,IntVector &formulasToGenerate) +{ QCString macroFile = Config_getString(FORMULA_MACROFILE); QCString stripMacroFile; if (!macroFile.isEmpty()) @@ -159,12 +194,8 @@ void FormulaManager::generateImages(const QCString &path,Format format,HighDPI h stripMacroFile = fi.fileName(); } - // go to the html output directory (i.e. path) - Dir::setCurrent(d.absPath()); - Dir thisDir; // generate a latex file containing one formula per page. - QCString texName="_formulas.tex"; - IntVector formulasToGenerate; + QCString texName=fileName+".tex"; std::ofstream f(texName.str(),std::ofstream::out | std::ofstream::binary); if (f.is_open()) { @@ -173,6 +204,14 @@ void FormulaManager::generateImages(const QCString &path,Format format,HighDPI h t << "\\usepackage{ifthen}\n"; t << "\\usepackage{epsfig}\n"; // for those who want to include images t << "\\usepackage[utf8]{inputenc}\n"; // looks like some older distributions with newunicode package 1.1 need this option. + t << "\\usepackage{xcolor}\n"; + + if (mode==Mode::Dark) // invert page and text colors + { + t << "\\color{white}\n"; + t << "\\pagecolor{black}\n"; + } + writeExtraLatexPackages(t); writeLatexSpecialFormulaChars(t); if (!macroFile.isEmpty()) @@ -182,170 +221,330 @@ void FormulaManager::generateImages(const QCString &path,Format format,HighDPI h } t << "\\pagestyle{empty}\n"; t << "\\begin{document}\n"; - for (size_t i=0; i<p->formulas.size(); i++) + for (const auto &formula : p->formulas) { - QCString resultName; - resultName.sprintf("form_%d.%s",static_cast<int>(i),format==Format::Vector?"svg":"png"); - // only formulas for which no image exists are generated - FileInfo fi(resultName.str()); - if (!fi.exists()) + int id = formula->id(); + // only formulas for which no image is cached are generated + //printf("check formula %d: cached=%d cachedDark=%d\n",formula->id(),formula->isCached(),formula->isCachedDark()); + if ((mode==Mode::Light && !formula->isCached()) || + (mode==Mode::Dark && !formula->isCachedDark()) + ) { // we force a pagebreak after each formula - t << p->formulas[i].c_str() << "\n\\pagebreak\n\n"; - formulasToGenerate.push_back(static_cast<int>(i)); + t << formula->text() << "\n\\pagebreak\n\n"; + formulasToGenerate.push_back(id); } + QCString resultName; + resultName.sprintf("form_%d%s.%s",id, mode==Mode::Light?"":"_dark", format==Format::Vector?"svg":"png"); Doxygen::indexList->addImageFile(resultName); } t << "\\end{document}\n"; t.flush(); f.close(); } - if (!formulasToGenerate.empty()) // there are new formulas +} + +static bool createDVIFile(const QCString &fileName) +{ + QCString latexCmd = "latex"; + char args[4096]; + Portable::sysTimerStart(); + int rerunCount=1; + while (rerunCount<8) { - QCString latexCmd = "latex"; - char args[4096]; - Portable::sysTimerStart(); - int rerunCount=1; - while (rerunCount<8) + //printf("Running latex...\n"); + sprintf(args,"-interaction=batchmode %s >%s",qPrint(fileName),Portable::devNull()); + if ((Portable::system(latexCmd,args)!=0) || (Portable::system(latexCmd,args)!=0)) + { + err("Problems running latex. Check your installation or look " + "for typos in %s.tex and check %s.log!\n",qPrint(fileName),qPrint(fileName)); + Portable::sysTimerStop(); + return false; + } + // check the log file if we need to run latex again to resolve references + QCString logFile = fileToString(fileName+".log"); + if (logFile.isEmpty() || + (logFile.find("Rerun to get cross-references right")==-1 && logFile.find("Rerun LaTeX")==-1)) + { + break; + } + rerunCount++; + } + Portable::sysTimerStop(); + return true; +} + +static bool createPostscriptFile(const QCString &fileName,const QCString &formBase,int pageIndex) +{ + char args[4096]; + // run dvips to convert the page with number pageIndex to an + // postscript file. + sprintf(args,"-q -D 600 -n 1 -p %d -o %s_tmp.ps %s.dvi",pageIndex,qPrint(formBase),qPrint(fileName)); + Portable::sysTimerStart(); + if (Portable::system("dvips",args)!=0) + { + err("Problems running dvips. Check your installation!\n"); + Portable::sysTimerStop(); + return false; + } + Portable::sysTimerStop(); + return true; +} + +static bool createEPSbboxFile(const QCString &formBase) +{ + char args[4096]; + // extract the bounding box for the postscript file + sprintf(args,"-q -dBATCH -dNOPAUSE -P- -dNOSAFER -sDEVICE=bbox %s_tmp.ps 2>%s_tmp.epsi", + qPrint(formBase),qPrint(formBase)); + Portable::sysTimerStart(); + if (Portable::system(Portable::ghostScriptCommand(),args)!=0) + { + err("Problems running %s. Check your installation!\n",Portable::ghostScriptCommand()); + Portable::sysTimerStop(); + return false; + } + Portable::sysTimerStop(); + return true; +} + +static bool extractBoundingBox(const QCString &formBase, + int *x1,int *y1,int *x2,int *y2, + double *x1hi,double *y1hi,double *x2hi,double *y2hi) +{ + FileInfo fi((formBase+"_tmp.epsi").str()); + if (fi.exists()) + { + QCString eps = fileToString(formBase+"_tmp.epsi"); + int i = eps.find("%%BoundingBox:"); + if (i!=-1) + { + sscanf(eps.data()+i,"%%%%BoundingBox:%d %d %d %d",x1,y1,x2,y2); + } + else + { + err("Couldn't extract bounding box from %s_tmp.epsi",qPrint(formBase)); + return false; + } + i = eps.find("%%HiResBoundingBox:"); + if (i!=-1) { - //printf("Running latex...\n"); - sprintf(args,"-interaction=batchmode _formulas.tex >%s",Portable::devNull()); - if ((Portable::system(latexCmd,args)!=0) || (Portable::system(latexCmd,args)!=0)) + sscanf(eps.data()+i,"%%%%HiResBoundingBox:%lf %lf %lf %lf",x1hi,y1hi,x2hi,y2hi); + } + else + { + err("Couldn't extract high resolution bounding box from %s_tmp.epsi",qPrint(formBase)); + return false; + } + } + //printf("Bounding box [%d %d %d %d]\n",x1,y1,x2,y2); + return true; +} + +double FormulaManager::updateFormulaSize(int pageNum,int x1,int y1,int x2,int y2) +{ + double scaleFactor = 1.25; + int zoomFactor = Config_getInt(FORMULA_FONTSIZE); + if (zoomFactor<8 || zoomFactor>50) zoomFactor=10; + scaleFactor *= zoomFactor/10.0; + + auto it = p->formulaIdMap.find(pageNum); + if (it!=p->formulaIdMap.end()) + { + Formula *formula = it->second; + formula->setWidth(static_cast<int>((x2-x1)*scaleFactor+0.5)); + formula->setHeight(static_cast<int>((y2-y1)*scaleFactor+0.5)); + } + return scaleFactor; +} + +static bool createCroppedPDF(const QCString &formBase,int x1,int y1,int x2,int y2) +{ + char args[4096]; + // crop the image to its bounding box + sprintf(args,"-q -dBATCH -dNOPAUSE -P- -dNOSAFER -sDEVICE=pdfwrite" + " -o %s_tmp.pdf -c \"[/CropBox [%d %d %d %d] /PAGES pdfmark\" -f %s_tmp.ps", + qPrint(formBase),x1,y1,x2,y2,qPrint(formBase)); + Portable::sysTimerStart(); + if (Portable::system(Portable::ghostScriptCommand(),args)!=0) + { + err("Problems running %s. Check your installation!\n",Portable::ghostScriptCommand()); + Portable::sysTimerStop(); + return false; + } + Portable::sysTimerStop(); + return true; +} + +static bool createCroppedEPS(const QCString &formBase) +{ + char args[4096]; + // crop the image to its bounding box + sprintf(args,"-q -dBATCH -dNOPAUSE -P- -dNOSAFER -sDEVICE=eps2write" + " -o %s_tmp.eps -f %s_tmp.ps",qPrint(formBase),qPrint(formBase)); + Portable::sysTimerStart(); + if (Portable::system(Portable::ghostScriptCommand(),args)!=0) + { + err("Problems running %s. Check your installation!\n",Portable::ghostScriptCommand()); + Portable::sysTimerStop(); + return false; + } + return true; +} + +static bool createSVGFromPDF(const QCString &formBase,const QCString &outFile) +{ + char args[4096]; + sprintf(args,"%s_tmp.pdf %s",qPrint(formBase),qPrint(outFile)); + Portable::sysTimerStart(); + if (Portable::system("pdf2svg",args)!=0) + { + err("Problems running pdf2svg. Check your installation!\n"); + Portable::sysTimerStop(); + return false; + } + Portable::sysTimerStop(); + return true; +} + +static bool createSVGFromPDFviaInkscape(Dir &thisDir,const QCString &formBase,const QCString &outFile) +{ + char args[4096]; + int inkscapeVersion = determineInkscapeVersion(thisDir); + if (inkscapeVersion == -1) + { + err("Problems determining the version of inkscape. Check your installation!\n"); + return false; + } + else if (inkscapeVersion == 0) + { + sprintf(args,"-l %s -z %s_tmp.pdf 2>%s",qPrint(outFile),qPrint(formBase),Portable::devNull()); + } + else // inkscapeVersion >= 1 + { + sprintf(args,"--export-type=svg --export-filename=%s %s_tmp.pdf 2>%s",qPrint(outFile),qPrint(formBase),Portable::devNull()); + } + Portable::sysTimerStart(); + if (Portable::system("inkscape",args)!=0) + { + err("Problems running inkscape. Check your installation!\n"); + Portable::sysTimerStop(); + return false; + } + Portable::sysTimerStop(); + return true; +} + + +static bool updateEPSBoundingBox(const QCString &formBase, + int x1,int y1,int x2,int y2, + double x1hi,double y1hi,double x2hi,double y2hi) +{ + // read back %s_tmp.eps and replace + // bounding box values with x1,y1,x2,y2 and remove the HiResBoundingBox + std::ifstream epsIn(formBase.str()+"_tmp.eps",std::ifstream::in); + std::ofstream epsOut(formBase.str()+"_tmp_corr.eps",std::ofstream::out | std::ofstream::binary); + if (epsIn.is_open() && epsOut.is_open()) + { + std::string line; + while (getline(epsIn,line)) + { + if (line.rfind("%%BoundingBox",0)==0) { - err("Problems running latex. Check your installation or look " - "for typos in _formulas.tex and check _formulas.log!\n"); - Portable::sysTimerStop(); - Dir::setCurrent(oldDir); - return; + epsOut << "%%BoundingBox: " << x1 << " " << y1 << " " << x2 << " " << y2 << "\n"; } - // check the log file if we need to run latex again to resolve references - QCString logFile = fileToString("_formulas.log"); - if (logFile.isEmpty() || - (logFile.find("Rerun to get cross-references right")==-1 && logFile.find("Rerun LaTeX")==-1)) + else if (line.rfind("%%HiResBoundingBox",0)==0) { - break; + epsOut << "%%HiResBoundingBox: " << x1hi << " " << y1hi << " " << x2hi << " " << y2hi << "\n"; + } + else + { + epsOut << line << "\n"; } - rerunCount++; } + epsIn.close(); + epsOut.close(); + } + else + { + err("Problems correcting the eps files from %s_tmp.eps to %s_tmp_corr.eps\n", + qPrint(formBase),qPrint(formBase)); + return false; + } + return true; +} + +static bool createPNG(const QCString &formBase,const QCString &outFile,double scaleFactor) +{ + char args[4096]; + Portable::sysTimerStop(); + sprintf(args,"-q -dNOSAFER -dBATCH -dNOPAUSE -dEPSCrop -sDEVICE=pnggray -dGraphicsAlphaBits=4 -dTextAlphaBits=4 " + "-r%d -sOutputFile=%s %s_tmp_corr.eps",static_cast<int>(scaleFactor*72),qPrint(outFile),qPrint(formBase)); + Portable::sysTimerStart(); + if (Portable::system(Portable::ghostScriptCommand(),args)!=0) + { + err("Problems running %s. Check your installation!\n",Portable::ghostScriptCommand()); Portable::sysTimerStop(); + return false; + } + Portable::sysTimerStop(); + return true; +} + +void FormulaManager::createFormulasTexFile(Dir &thisDir,Format format,HighDPI hd,Mode mode) +{ + IntVector formulasToGenerate; + QCString formulaFileName = mode==Mode::Light ? "_formulas" : "_formulas_dark"; + createLatexFile(formulaFileName,format,mode,formulasToGenerate); + + if (!formulasToGenerate.empty()) // there are new formulas + { + if (!createDVIFile(formulaFileName)) return; + //printf("Running dvips...\n"); int pageIndex=1; for (int pageNum : formulasToGenerate) { - msg("Generating image form_%d.%s for formula\n",pageNum,(format==Format::Vector) ? "svg" : "png"); + QCString outputFile; + outputFile.sprintf("form_%d%s.%s",pageNum, mode==Mode::Light?"":"_dark", format==Format::Vector?"svg":"png"); + msg("Generating image %s for formula\n",qPrint(outputFile)); + QCString formBase; - formBase.sprintf("_form%d",pageNum); - // run dvips to convert the page with number pageIndex to an - // postscript file. - sprintf(args,"-q -D 600 -n 1 -p %d -o %s_tmp.ps _formulas.dvi", - pageIndex,qPrint(formBase)); - Portable::sysTimerStart(); - if (Portable::system("dvips",args)!=0) - { - err("Problems running dvips. Check your installation!\n"); - Portable::sysTimerStop(); - Dir::setCurrent(oldDir); - return; - } - Portable::sysTimerStop(); + formBase.sprintf("_form%d%s",pageNum,mode==Mode::Light?"":"_dark"); - // extract the bounding box for the postscript file - sprintf(args,"-q -dBATCH -dNOPAUSE -P- -dNOSAFER -sDEVICE=bbox %s_tmp.ps 2>%s_tmp.epsi", - qPrint(formBase),qPrint(formBase)); - Portable::sysTimerStart(); - if (Portable::system(Portable::ghostScriptCommand(),args)!=0) - { - err("Problems running %s. Check your installation!\n",Portable::ghostScriptCommand()); - Portable::sysTimerStop(); - Dir::setCurrent(oldDir); - return; - } - Portable::sysTimerStop(); + if (!createPostscriptFile(formulaFileName,formBase,pageIndex)) break; - // extract the bounding box info from the generate .epsi file int x1=0,y1=0,x2=0,y2=0; - FileInfo fi((formBase+"_tmp.epsi").str()); - if (fi.exists()) + double x1hi=0.0,y1hi=0.0,x2hi=0.0,y2hi=0.0; + if (mode==Mode::Light) { - QCString eps = fileToString(formBase+"_tmp.epsi"); - int i = eps.find("%%BoundingBox:"); - if (i!=-1) - { - sscanf(eps.data()+i,"%%%%BoundingBox:%d %d %d %d",&x1,&y1,&x2,&y2); - } - else - { - err("Couldn't extract bounding box from %s_tmp.epsi",qPrint(formBase)); - } + if (!createEPSbboxFile(formBase)) break; + // extract the bounding box info from the generated .epsi file + if (!extractBoundingBox(formBase,&x1,&y1,&x2,&y2,&x1hi,&y1hi,&x2hi,&y2hi)) break; + } + else // for dark images the bounding box is wrong (includes the black) so + // use the bounding box of the light image instead. + { + QCString formBaseLight; + formBaseLight.sprintf("_form%d",pageNum); + if (!extractBoundingBox(formBaseLight,&x1,&y1,&x2,&y2,&x1hi,&y1hi,&x2hi,&y2hi)) break; } - //printf("Bounding box [%d %d %d %d]\n",x1,y1,x2,y2); // convert the corrected EPS to a bitmap - double scaleFactor = 1.25; - int zoomFactor = Config_getInt(FORMULA_FONTSIZE); - if (zoomFactor<8 || zoomFactor>50) zoomFactor=10; - scaleFactor *= zoomFactor/10.0; - - int width = static_cast<int>((x2-x1)*scaleFactor+0.5); - int height = static_cast<int>((y2-y1)*scaleFactor+0.5); - p->storeDisplaySize(pageNum,width,height); + double scaleFactor = updateFormulaSize(pageNum,x1,y1,x2,y2); if (format==Format::Vector) { - // crop the image to its bounding box - sprintf(args,"-q -dBATCH -dNOPAUSE -P- -dNOSAFER -sDEVICE=pdfwrite" - " -o %s_tmp.pdf -c \"[/CropBox [%d %d %d %d] /PAGES pdfmark\" -f %s_tmp.ps", - qPrint(formBase),x1,y1,x2,y2,qPrint(formBase)); - Portable::sysTimerStart(); - if (Portable::system(Portable::ghostScriptCommand(),args)!=0) - { - err("Problems running %s. Check your installation!\n",Portable::ghostScriptCommand()); - Portable::sysTimerStop(); - Dir::setCurrent(oldDir); - return; - } - Portable::sysTimerStop(); + if (!createCroppedPDF(formBase,x1,y1,x2,y2)) break; // if we have pdf2svg available use it to create a SVG image if (Portable::checkForExecutable("pdf2svg")) { - sprintf(args,"%s_tmp.pdf form_%d.svg",qPrint(formBase),pageNum); - Portable::sysTimerStart(); - if (Portable::system("pdf2svg",args)!=0) - { - err("Problems running pdf2svg. Check your installation!\n"); - Portable::sysTimerStop(); - Dir::setCurrent(oldDir); - return; - } - Portable::sysTimerStop(); + createSVGFromPDF(formBase,outputFile); } else if (Portable::checkForExecutable("inkscape")) // alternative is to use inkscape { - int inkscapeVersion = determineInkscapeVersion(thisDir); - if (inkscapeVersion == -1) - { - err("Problems determining the version of inkscape. Check your installation!\n"); - Dir::setCurrent(oldDir); - return; - } - else if (inkscapeVersion == 0) - { - sprintf(args,"-l form_%d.svg -z %s_tmp.pdf 2>%s",pageNum,qPrint(formBase),Portable::devNull()); - } - else // inkscapeVersion >= 1 - { - sprintf(args,"--export-type=svg --export-filename=form_%d.svg %s_tmp.pdf 2>%s",pageNum,qPrint(formBase),Portable::devNull()); - } - Portable::sysTimerStart(); - if (Portable::system("inkscape",args)!=0) - { - err("Problems running inkscape. Check your installation!\n"); - Portable::sysTimerStop(); - Dir::setCurrent(oldDir); - return; - } - Portable::sysTimerStop(); + createSVGFromPDFviaInkscape(thisDir,formBase,outputFile); } else { @@ -353,56 +552,13 @@ void FormulaManager::generateImages(const QCString &path,Format format,HighDPI h return; } - if (RM_TMP_FILES) - { - thisDir.remove(formBase.str()+"_tmp.pdf"); - } + p->tempFiles.push_back(formBase.str()+"_tmp.pdf"); } else // format==Format::Bitmap { - // crop the image to its bounding box - sprintf(args,"-q -dBATCH -dNOPAUSE -P- -dNOSAFER -sDEVICE=eps2write" - " -o %s_tmp.eps -f %s_tmp.ps",qPrint(formBase),qPrint(formBase)); - Portable::sysTimerStart(); - if (Portable::system(Portable::ghostScriptCommand(),args)!=0) - { - err("Problems running %s. Check your installation!\n",Portable::ghostScriptCommand()); - Portable::sysTimerStop(); - Dir::setCurrent(oldDir); - return; - } + if (!createCroppedEPS(formBase)) break; - // read back %s_tmp.eps and replace - // bounding box values with x1,y1,x2,y2 and remove the HiResBoundingBox - std::ifstream epsIn(formBase.str()+"_tmp.eps",std::ifstream::in); - std::ofstream epsOut(formBase.str()+"_tmp_corr.eps",std::ofstream::out | std::ofstream::binary); - if (epsIn.is_open() && epsOut.is_open()) - { - std::string line; - while (getline(epsIn,line)) - { - if (line.rfind("%%BoundingBox",0)==0) - { - epsOut << "%%BoundingBox: " << x1 << " " << y1 << " " << x2 << " " << y2 << "\n"; - } - else if (line.rfind("%%HiResBoundingBox",0)==0) // skip this one - { - } - else - { - epsOut << line << "\n"; - } - } - epsIn.close(); - epsOut.close(); - } - else - { - err("Problems correcting the eps files from %s_tmp.eps to %s_tmp_corr.eps\n", - qPrint(formBase),qPrint(formBase)); - Dir::setCurrent(oldDir); - return; - } + if (!updateEPSBoundingBox(formBase,x1,y1,x2,y2,x1hi,y1hi,x2hi,y2hi)) break; if (hd==HighDPI::On) // for high DPI display it looks much better if the // image resolution is higher than the display resolution @@ -410,64 +566,80 @@ void FormulaManager::generateImages(const QCString &path,Format format,HighDPI h scaleFactor*=2; } - Portable::sysTimerStop(); - sprintf(args,"-q -dNOSAFER -dBATCH -dNOPAUSE -dEPSCrop -sDEVICE=pnggray -dGraphicsAlphaBits=4 -dTextAlphaBits=4 " - "-r%d -sOutputFile=form_%d.png %s_tmp_corr.eps",static_cast<int>(scaleFactor*72),pageNum,qPrint(formBase)); - Portable::sysTimerStart(); - if (Portable::system(Portable::ghostScriptCommand(),args)!=0) - { - err("Problems running %s. Check your installation!\n",Portable::ghostScriptCommand()); - Portable::sysTimerStop(); - Dir::setCurrent(oldDir); - return; - } - Portable::sysTimerStop(); + if (!createPNG(formBase,outputFile,scaleFactor)) break; - if (RM_TMP_FILES) - { - thisDir.remove(formBase.str()+"_tmp.eps"); - thisDir.remove(formBase.str()+"_tmp_corr.eps"); - } + p->tempFiles.push_back(formBase.str()+"_tmp.eps"); + p->tempFiles.push_back(formBase.str()+"_tmp_corr.eps"); } // remove intermediate image files - if (RM_TMP_FILES) + p->tempFiles.push_back(formBase.str()+"_tmp.ps"); + if (mode==Mode::Light) { - thisDir.remove(formBase.str()+"_tmp.ps"); - thisDir.remove(formBase.str()+"_tmp.epsi"); + p->tempFiles.push_back(formBase.str()+"_tmp.epsi"); } pageIndex++; } // remove intermediate files produced by latex - if (RM_TMP_FILES) - { - thisDir.remove("_formulas.dvi"); - thisDir.remove("_formulas.log"); // keep file in case of errors - thisDir.remove("_formulas.aux"); - } + p->tempFiles.push_back(formulaFileName.str()+".dvi"); + p->tempFiles.push_back(formulaFileName.str()+".log"); + p->tempFiles.push_back(formulaFileName.str()+".aux"); } // remove the latex file itself - if (RM_TMP_FILES) thisDir.remove("_formulas.tex"); + p->tempFiles.push_back(formulaFileName.str()+".tex"); // write/update the formula repository so we know what text the // generated images represent (we use this next time to avoid regeneration // of the images, and to avoid forcing the user to delete all images in order // to let a browser refresh the images). + std::ofstream f; f.open("formula.repository",std::ofstream::out | std::ofstream::binary); if (f.is_open()) { TextStream t(&f); - for (size_t i=0; i<p->formulas.size(); i++) + for (const auto &formula : p->formulas) { - DisplaySize size = p->getDisplaySize(static_cast<int>(i)); - t << "\\_form#" << i; - if (size.width!=-1 && size.height!=-1) + t << "\\_form#" << formula->id(); + if (formula->width()!=-1 && formula->height()!=-1) { - t << "=" << size.width << "x" << size.height; + t << "=" << formula->width() << "x" << formula->height(); } - t << ":" << p->formulas[i].c_str() << "\n"; + t << ":" << formula->text() << "\n"; } } +} + +void FormulaManager::generateImages(const QCString &path,Format format,HighDPI hd) +{ + Dir d(path.str()); + // store the original directory + if (!d.exists()) + { + term("Output directory '%s' does not exist!\n",qPrint(path)); + } + std::string oldDir = Dir::currentDirPath(); + + // go to the html output directory (i.e. path) + Dir::setCurrent(d.absPath()); + Dir thisDir; + + createFormulasTexFile(thisDir,format,hd,Mode::Light); + if (Config_getEnum(HTML_COLORSTYLE)!=HTML_COLORSTYLE_t::LIGHT) // all modes other than light need a dark version + { + // note that the dark version reuses the bounding box of the light version so it needs to be + // created after the light version. + createFormulasTexFile(thisDir,format,hd,Mode::Dark); + } + + // clean up temporary files + if (RM_TMP_FILES) + { + for (const auto &file : p->tempFiles) + { + thisDir.remove(file); + } + } + // reset the directory to the original location. Dir::setCurrent(oldDir); } @@ -475,40 +647,41 @@ void FormulaManager::generateImages(const QCString &path,Format format,HighDPI h void FormulaManager::clear() { p->formulas.clear(); - p->formulaMap.clear(); + p->formulaIdMap.clear(); } -int FormulaManager::addFormula(const std::string &formulaText) +int FormulaManager::addFormula(const std::string &formulaText,int width,int height) { - auto it = p->formulaMap.find(formulaText); - if (it!=p->formulaMap.end()) // already stored + Formula *formula = p->formulas.find(formulaText); + if (formula) // same formula already stored { - return static_cast<int>(it->second); + return formula->id(); } - // store new formula - size_t id = p->formulas.size(); - p->formulaMap.insert(std::make_pair(formulaText,id)); - p->formulas.push_back(formulaText); - return static_cast<int>(id); + // add new formula + int id = static_cast<int>(p->formulas.size()); + formula = p->formulas.add(formulaText.c_str(),id,width,height); + p->formulaIdMap.insert(std::make_pair(id,formula)); + return id; } -std::string FormulaManager::findFormula(int formulaId) const +const Formula *FormulaManager::findFormula(int formulaId) const { - if (formulaId>=0 && formulaId<static_cast<int>(p->formulas.size())) - { - return p->formulas[formulaId]; - } - return std::string(); + auto it = p->formulaIdMap.find(formulaId); + return it != p->formulaIdMap.end() ? it->second : nullptr; } -bool FormulaManager::hasFormulas() const +#if 0 +Formula *FormulaManager::findFormula(int formulaId) { - return !p->formulas.empty(); + auto it = p->formulaIdMap.find(formulaId); + return it != p->formulaIdMap.end() ? it->second : nullptr; } +#endif -FormulaManager::DisplaySize FormulaManager::displaySize(int formulaId) const + +bool FormulaManager::hasFormulas() const { - return p->getDisplaySize(formulaId); + return !p->formulas.empty(); } // helper function to detect and return the major version of inkscape. diff --git a/src/formula.h b/src/formula.h index 9baeb24..b5af7d0 100644 --- a/src/formula.h +++ b/src/formula.h @@ -1,8 +1,6 @@ /****************************************************************************** * - * - * - * Copyright (C) 1997-2015 by Dimitri van Heesch. + * Copyright (C) 1997-2022 by Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby @@ -22,28 +20,72 @@ #include <string> #include "qcstring.h" +#include "containers.h" + +class Dir; + +/** Class representing a LaTeX formula as found in the documentation */ +class Formula +{ + public: + Formula(const QCString &text,int id,int width=-1,int height=-1) + : m_text(text), m_id(id), m_width(width), m_height(height) {} + + int width() const { return m_width; } + int height() const { return m_height; } + int id() const { return m_id; } + QCString text() const { return m_text; } + bool isCached() const { return m_cached && !m_forceRegen; } + bool isCachedDark() const { return m_cached && !m_forceRegen; } + + private: + friend class FormulaManager; + void setWidth(int width) { m_width = width; } + void setHeight(int height) { m_height = height; } + void setCached(bool cached) { m_cached = cached; m_forceRegen = m_forceRegen || !cached; } + void setCachedDark(bool cached) { m_cachedDark = cached; m_forceRegen = m_forceRegen || !cached; } + + QCString m_text; + int m_id; + int m_width; + int m_height; + bool m_cached = false; + bool m_cachedDark = false; // dark version cached? + bool m_forceRegen = false; // true if there is an inconsistency in setCache calls +}; /*! Manager class to handle formulas */ class FormulaManager { public: - struct DisplaySize - { - DisplaySize(int w,int h) : width(w), height(h) {} - int width; - int height; - }; + static FormulaManager &instance(); + + //! @name repository functions + //! @{ + void initFromRepository(const QCString &dir); + void checkRepositories(); + //! @} + + //! @name formula functions + //! @{ + void clear(); + int addFormula(const std::string &formulaText,int width=-1,int height=-1); + const Formula *findFormula(int formulaId) const; + bool hasFormulas() const; + //! @} + + //! @name generator functions + //! @{ enum class Format { Bitmap, Vector }; enum class HighDPI { On, Off }; - static FormulaManager &instance(); - void readFormulas(const QCString &dir,bool doCompare=false); - void clear(); - int addFormula(const std::string &formulaText); - void generateImages(const QCString &outputDir,Format format,HighDPI hd = HighDPI::Off) const; - std::string findFormula(int formulaId) const; - bool hasFormulas() const; - DisplaySize displaySize(int formulaId) const; + void generateImages(const QCString &outputDir,Format format,HighDPI hd = HighDPI::Off); + //! @} + private: + enum class Mode { Dark, Light }; + void createFormulasTexFile(Dir &d,Format format,HighDPI hd,Mode mode); + void createLatexFile(const QCString &fileName,Format format,Mode mode,IntVector &formulasToGenerate); + double updateFormulaSize(int pageNum,int x1,int y1,int x2,int y2); FormulaManager(); struct Private; std::unique_ptr<Private> p; diff --git a/src/fortrancode.l b/src/fortrancode.l index 12ad042..58aa5d2 100644 --- a/src/fortrancode.l +++ b/src/fortrancode.l @@ -63,8 +63,6 @@ typedef yyguts_t *yyscan_t; #include "fortranscanner.h" #include "containers.h" -const int fixedCommentAfter = 72; - // Toggle for some debugging info //#define DBG_CTX(x) fprintf x #define DBG_CTX(x) do { } while(0) @@ -171,6 +169,8 @@ struct fortrancodeYY_state bool endComment = false; TooltipManager tooltipManager; + + int fixedCommentAfter = 72; }; #if USE_STATE2STRING @@ -211,6 +211,7 @@ static void addLocalVar(yyscan_t yyscanner,const QCString &varName); static MemberDef *getFortranDefs(yyscan_t yyscanner,const QCString &memberName, const QCString &moduleName, const UseMap &useMap); static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size); +static inline void pop_state(yyscan_t yyscanner); //------------------------------------------------------------------- @@ -376,7 +377,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") } <Use,UseOnly,Import>"\n" { unput(*yytext); - yy_pop_state(yyscanner); + pop_state(yyscanner); YY_FTN_RESET } <*>"import"{BS}/"\n" | @@ -437,7 +438,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") yyextra->currentModule = yyextra->currentModule.lower(); } generateLink(yyscanner,*yyextra->code,yytext); - yy_pop_state(yyscanner); + pop_state(yyscanner); } <ClassName>({ACCESS_SPEC}|ABSTRACT|EXTENDS)/[,:( ] { //| variable declaration startFontClass(yyscanner,"keyword"); @@ -445,7 +446,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") endFontClass(yyscanner); } <ClassName>\n { // interface may be without name - yy_pop_state(yyscanner); + pop_state(yyscanner); YY_FTN_REJECT; } <Start>^{BS}"end"({BS_}"enum").* { @@ -486,7 +487,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") } <Subprog,Subprogend>"\n" { codifyLines(yyscanner,yytext); yyextra->contLineNr++; - yy_pop_state(yyscanner); + pop_state(yyscanner); YY_FTN_RESET } <Start>"end"{BS}("block"{BS}"data"|{SUBPROG}|"module"|"program"|"enum"|"type"|"interface")?{BS} { // Fortran subroutine or function ends @@ -500,7 +501,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") } <Subprogend>{ID}/{BS}(\n|!|;) { generateLink(yyscanner,*yyextra->code,yytext); - yy_pop_state(yyscanner); + pop_state(yyscanner); } <Start>"end"{BS}("block"{BS}"data"|{SUBPROG}|"module"|"program"|"enum"|"type"|"interface"){BS}/(\n|!|;) { // Fortran subroutine or function ends //cout << "===> end function " << yytext << endl; @@ -572,7 +573,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") } <DeclarationBinding>{ID} { // Type bound procedure link generateLink(yyscanner,*yyextra->code, yytext); - yy_pop_state(yyscanner); + pop_state(yyscanner); } <Declaration>[(] { // start of array or type / class specification yyextra->bracketCount++; @@ -597,7 +598,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") yyextra->contLineNr++; codifyLines(yyscanner,yytext); yyextra->bracketCount = 0; - yy_pop_state(yyscanner); + pop_state(yyscanner); YY_FTN_RESET } <Declaration,DeclarationBinding>"\n" { // end declaration line (?) @@ -614,7 +615,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") if (!(yyextra->hasContLine && yyextra->hasContLine[yyextra->contLineNr - 1])) { yyextra->isExternal = false; - yy_pop_state(yyscanner); + pop_state(yyscanner); } YY_FTN_RESET } @@ -632,7 +633,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") yyextra->insideBody=TRUE; generateLink(yyscanner,*yyextra->code, yytext); yyextra->insideBody=FALSE; - yy_pop_state(yyscanner); + pop_state(yyscanner); } <Start>{ID}{BS}/"(" { // function call if (yyextra->isFixedForm && yy_my_start == 6) @@ -708,7 +709,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") } unput(*yytext); yyextra->contLineNr--; - yy_pop_state(yyscanner); + pop_state(yyscanner); YY_FTN_RESET } @@ -775,7 +776,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") startFontClass(yyscanner,"stringliteral"); codifyLines(yyscanner,yyextra->str); endFontClass(yyscanner); - yy_pop_state(yyscanner); + pop_state(yyscanner); } <String>. {yyextra->str+=yytext;} @@ -806,7 +807,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") <*>^{BS}"type"{BS}"=" { yyextra->code->codify(QCString(yytext)); } <*>[\x80-\xFF]* { // keep utf8 characters together... - if (yyextra->isFixedForm && yy_my_start > fixedCommentAfter) + if (yyextra->isFixedForm && yy_my_start > yyextra->fixedCommentAfter) { startFontClass(yyscanner,"comment"); codifyLines(yyscanner,yytext); @@ -817,7 +818,7 @@ LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}(,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})?") } } <*>. { - if (yyextra->isFixedForm && yy_my_start > fixedCommentAfter) + if (yyextra->isFixedForm && yy_my_start > yyextra->fixedCommentAfter) { //yy_push_state(YY_START,yyscanner); //BEGIN(DocBlock); @@ -1394,7 +1395,7 @@ static void checkContLines(yyscan_t yyscanner,const char *s) yyextra->hasContLine = (int *) malloc((numLines) * sizeof(int)); for (i = 0; i < numLines; i++) yyextra->hasContLine[i] = 0; - p = prepassFixedForm(s, yyextra->hasContLine); + p = prepassFixedForm(s, yyextra->hasContLine,yyextra->fixedCommentAfter); yyextra->hasContLine[0] = 0; } @@ -1481,6 +1482,7 @@ void FortranCodeParser::parseCode(CodeOutputInterface & codeOutIntf, if (yyextra->isFixedForm) { checkContLines(yyscanner,yyextra->inputString); + yyextra->fixedCommentAfter = Config_getInt(FORTRAN_COMMENT_AFTER); } yyextra->currentFontClass = 0; yyextra->insideCodeLine = FALSE; @@ -1538,6 +1540,14 @@ void FortranCodeParser::parseCode(CodeOutputInterface & codeOutIntf, printlex(yy_flex_debug, FALSE, __FILE__, fileDef ? qPrint(fileDef->fileName()): NULL); } +static inline void pop_state(yyscan_t yyscanner) +{ + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + if ( yyg->yy_start_stack_ptr <= 0 ) + warn(yyextra->fileName,yyextra->yyLineNr,"Unexpected statement '%s'",yytext ); + else + yy_pop_state(yyscanner); +} //--------------------------------------------------------- #if USE_STATE2STRING diff --git a/src/fortranscanner.h b/src/fortranscanner.h index bf286c3..34e8cdc 100644 --- a/src/fortranscanner.h +++ b/src/fortranscanner.h @@ -53,6 +53,6 @@ class FortranOutlineParserFixed : public FortranOutlineParser FortranOutlineParserFixed() : FortranOutlineParser(FortranFormat_Fixed) { } }; -const char* prepassFixedForm(const char* contents, int *hasContLine); +const char* prepassFixedForm(const char* contents, int *hasContLine, int fixedCommentAfter); #endif diff --git a/src/fortranscanner.l b/src/fortranscanner.l index 04421c5..58c774a 100755..100644 --- a/src/fortranscanner.l +++ b/src/fortranscanner.l @@ -74,7 +74,6 @@ typedef yyguts_t *yyscan_t; #include "debug.h" #include "markdown.h" -const int fixedCommentAfter = 72; // Toggle for some debugging info //#define DBG_CTX(x) fprintf x @@ -205,6 +204,10 @@ struct fortranscannerYY_state //! Holds program scope->symbol name->symbol modifiers. std::map<Entry*,std::map<std::string,SymbolModifiers> > modifiers; int anonCount = 0 ; + + int fixedCommentAfter = 72; + //! counter for the number of main programs in this file + int mainPrograms = 0; }; //----------------------------------------------------------------------------- @@ -225,6 +228,7 @@ static void addSubprogram(yyscan_t yyscanner,const QCString &text); static void addInterface(yyscan_t yyscanner,QCString name, InterfaceType type); static Argument *getParameter(yyscan_t yyscanner,const QCString &name); static void scanner_abort(yyscan_t yyscanner); +static inline void pop_state(yyscan_t yyscanner); static void startScope(yyscan_t yyscanner,Entry *scope); static bool endScope(yyscan_t yyscanner,Entry *scope, bool isGlobalRoot=FALSE); @@ -248,6 +252,8 @@ static inline const char *getLexerFILE() {return __FILE__;} #include "doxygen_lex.h" #define YY_USER_ACTION yyextra->colNr+=(int)yyleng; #define INVALID_ENTRY ((Entry*)0x8) + + //----------------------------------------------------------------------------- %} @@ -285,7 +291,7 @@ ATTR_SPEC (EXTERNAL|ALLOCATABLE|DIMENSION{ARGS}|{INTENT_SPEC}|INTRINSIC|OPTIONAL ACCESS_SPEC (PRIVATE|PUBLIC) LANGUAGE_BIND_SPEC BIND{BS}"("{BS}C{BS}((,{BS}NAME{BS}"="{BS}"\""(.*)"\""{BS})|(,{BS}NAME{BS}"="{BS}"'"(.*)"'"{BS}))?")" /* Assume that attribute statements are almost the same as attributes. */ -ATTR_STMT {ATTR_SPEC}|DIMENSION|{ACCESS_SPEC} +ATTR_STMT {ATTR_SPEC}|DIMENSION EXTERNAL_STMT (EXTERNAL) CONTAINS CONTAINS @@ -372,7 +378,7 @@ SCOPENAME ({ID}{BS}"::"{BS})* //printf("BUFFER:%s\n", (const char*)yyextra->inputStringPrepass); pushBuffer(yyscanner,yyextra->inputStringPrepass); yyextra->colNr = 0; - yy_pop_state(yyscanner); + pop_state(yyscanner); } else { // simple line @@ -413,7 +419,7 @@ SCOPENAME ({ID}{BS}"::"{BS})* { yyextra->initializer+=yytext; } - yy_pop_state(yyscanner); + pop_state(yyscanner); } <String>. { if (yy_top_state(yyscanner) == Initialization || yy_top_state(yyscanner) == ArrayInitializer) @@ -452,7 +458,7 @@ SCOPENAME ({ID}{BS}"::"{BS})* DBG_CTX((stderr,"start comment %d\n",yyextra->lineNr)); } } -<StrIgnore>.?/\n { yy_pop_state(yyscanner); // comment ends with endline character +<StrIgnore>.?/\n { pop_state(yyscanner); // comment ends with endline character DBG_CTX((stderr,"end comment %d %s\n",yyextra->lineNr,qPrint(yyextra->debugStr))); } // comment line ends <StrIgnore>. { yyextra->debugStr+=yytext; } @@ -476,7 +482,7 @@ SCOPENAME ({ID}{BS}"::"{BS})* yyextra->current->section=Entry::USINGDIR_SEC; yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current); yyextra->current->lang = SrcLangExt_Fortran; - yy_pop_state(yyscanner); + pop_state(yyscanner); } <Use>{ID}/, { yyextra->useModuleName=yytext; @@ -496,7 +502,7 @@ SCOPENAME ({ID}{BS}"::"{BS})* <Use,UseOnly>"\n" { yyextra->colNr -= 1; unput(*yytext); - yy_pop_state(yyscanner); + pop_state(yyscanner); } /* INTERFACE definitions */ @@ -537,25 +543,26 @@ SCOPENAME ({ID}{BS}"::"{BS})* yyterminate(); } yyextra->ifType = IF_NONE; - yy_pop_state(yyscanner); + pop_state(yyscanner); } <InterfaceBody>module{BS}procedure { yy_push_state(YY_START,yyscanner); BEGIN(ModuleProcedure); } -<ModuleProcedure>{ID} { if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC) +<ModuleProcedure>{ID} { QCString name = QCString(yytext).lower(); + if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC) { - addInterface(yyscanner,yytext, yyextra->ifType); + addInterface(yyscanner,name, yyextra->ifType); startScope(yyscanner,yyextra->last_entry.get()); } yyextra->current->section = Entry::FUNCTION_SEC ; - yyextra->current->name = yytext; + yyextra->current->name = name; yyextra->moduleProcedures.push_back(yyextra->current); addCurrentEntry(yyscanner,true); } <ModuleProcedure>"\n" { yyextra->colNr -= 1; unput(*yytext); - yy_pop_state(yyscanner); + pop_state(yyscanner); } <InterfaceBody>. {} @@ -593,7 +600,7 @@ SCOPENAME ({ID}{BS}"::"{BS})* //if (!endScope(yyscanner,yyextra->current_root)) // yyterminate(); yyextra->defaultProtection = Public; - yy_pop_state(yyscanner); + pop_state(yyscanner); } <Start,ModuleBody,ModuleBodyContains>"end"({BS}(module|program)({BS_}{ID})?)?{BS}/(\n|!|;) { // end module resolveModuleProcedures(yyscanner,yyextra->current_root); @@ -610,7 +617,7 @@ SCOPENAME ({ID}{BS}"::"{BS})* } else { - yy_pop_state(yyscanner); // cannot pop artrificial entry + pop_state(yyscanner); // cannot pop artrificial entry } } else @@ -630,12 +637,14 @@ SCOPENAME ({ID}{BS}"::"{BS})* /*------- access specification --------------------------------------------------------------------------*/ -<ModuleBody>private/{BS}(\n|"!") { yyextra->defaultProtection = Private; - yyextra->current->protection = yyextra->defaultProtection ; - } -<ModuleBody>public/{BS}(\n|"!") { yyextra->defaultProtection = Public; - yyextra->current->protection = yyextra->defaultProtection ; - } +<ModuleBody,TypedefBody,TypedefBodyContains>private/{BS}(\n|"!") { + yyextra->defaultProtection = Private; + yyextra->current->protection = yyextra->defaultProtection ; + } +<ModuleBody,TypedefBody,TypedefBodyContains>public/{BS}(\n|"!") { + yyextra->defaultProtection = Public; + yyextra->current->protection = yyextra->defaultProtection ; + } /*------- type definition -------------------------------------------------------------------------------*/ @@ -648,8 +657,8 @@ SCOPENAME ({ID}{BS}"::"{BS})* } yy_push_state(Typedef,yyscanner); - yyextra->current->protection = yyextra->defaultProtection; - yyextra->typeProtection = yyextra->defaultProtection; + yyextra->current->protection = Package; // invalid in Fortran, replaced below + yyextra->typeProtection = Public; yyextra->typeMode = true; } <Typedef>{ @@ -666,11 +675,9 @@ extends{ARGS} { } public { yyextra->current->protection = Public; - yyextra->typeProtection = Public; } private { yyextra->current->protection = Private; - yyextra->typeProtection = Private; } {LANGUAGE_BIND_SPEC} { /* ignored for now */ @@ -691,6 +698,20 @@ private { yyextra->current->name = yyextra->current_root->name + "::" + yyextra->current->name; } + // set modifiers to allow adjusting public/private in surrounding module scope + if( yyextra->current->protection == Package ) + { + yyextra->current->protection = yyextra->defaultProtection; + } + else if( yyextra->current->protection == Public ) + { + yyextra->modifiers[yyextra->current_root][yyextra->current->name.lower().str()] |= QCString("public"); + } + else if( yyextra->current->protection == Private ) + { + yyextra->modifiers[yyextra->current_root][yyextra->current->name.lower().str()] |= QCString("private"); + } + addCurrentEntry(yyscanner,true); startScope(yyscanner,yyextra->last_entry.get()); BEGIN(TypedefBody); @@ -745,7 +766,7 @@ private { yyterminate(); } yyextra->typeMode = false; - yy_pop_state(yyscanner); + pop_state(yyscanner); } ^{BS}"end"{BS}/(\n|!|;) { /* incorrect end type definition */ warn(yyextra->fileName,yyextra->lineNr, "Found 'END' instead of 'END TYPE'"); @@ -755,7 +776,7 @@ private { yyterminate(); } yyextra->typeMode = false; - yy_pop_state(yyscanner); + pop_state(yyscanner); } } @@ -780,7 +801,7 @@ private { } yyextra->subrCurrent.pop_back(); yyextra->vtype = V_IGNORE; - yy_pop_state(yyscanner) ; + pop_state(yyscanner) ; } <BlockData>{ {ID} { @@ -823,13 +844,30 @@ private { {ATTR_STMT}/{BS}"::" { /* attribute statement starts */ DBG_CTX((stderr,"5=========> Attribute statement: %s\n", yytext)); + if (YY_START == Start) + { + addModule(yyscanner); + yy_push_state(ModuleBody,yyscanner); //anon program + } QCString tmp = yytext; yyextra->currentModifiers |= tmp.stripWhiteSpace(); yyextra->argType=""; yy_push_state(YY_START,yyscanner); BEGIN( AttributeList ) ; } +"common" { + if (YY_START == Start) + { + addModule(yyscanner); + yy_push_state(ModuleBody,yyscanner); //anon program + } + } {ID} { + if (YY_START == Start) + { + addModule(yyscanner); + yy_push_state(ModuleBody,yyscanner); //anon program + } } ^{BS}"type"{BS_}"is"/{BT_} {} ^{BS}"type"{BS}"=" {} @@ -876,6 +914,21 @@ private { /* work around for bug in QCString.replace (QCString works) */ QCString name=yytext; name = name.lower(); + /* if variable/type/etc is part of a module, mod name is necessary for output */ + // get surrounding state + int currentState = YY_START; + yy_pop_state(yyscanner); + int outerState = YY_START; + yy_push_state(currentState,yyscanner); + if( outerState == Start || outerState == ModuleBody ) + { + if ((yyextra->current_root) && + (yyextra->current_root->section == Entry::CLASS_SEC + || yyextra->current_root->section == Entry::NAMESPACE_SEC)) + { + name = yyextra->current_root->name + "::" + name; + } + } /* remember attributes for the symbol */ yyextra->modifiers[yyextra->current_root][name.lower().str()] |= yyextra->currentModifiers; yyextra->argName= name; @@ -992,12 +1045,12 @@ private { BEGIN(Initialization); } <Variable>"\n" { yyextra->currentModifiers = SymbolModifiers(); - yy_pop_state(yyscanner); // end variable declaration list + pop_state(yyscanner); // end variable declaration list newLine(yyscanner); yyextra->docBlock.resize(0); } <Variable>";".*"\n" { yyextra->currentModifiers = SymbolModifiers(); - yy_pop_state(yyscanner); // end variable declaration list + pop_state(yyscanner); // end variable declaration list yyextra->docBlock.resize(0); yyextra->inputStringSemi = " \n"+QCString(yytext+1); yyextra->lineNr--; @@ -1036,7 +1089,7 @@ private { <Initialization>{COMMA} { if (yyextra->initializerScope == 0) { updateVariablePrepassComment(yyscanner,yyextra->colNr-(int)yyleng, yyextra->colNr); - yy_pop_state(yyscanner); // end initialization + pop_state(yyscanner); // end initialization if (yyextra->last_enum) { yyextra->last_enum->initializer.str(yyextra->initializer.str()); @@ -1052,7 +1105,7 @@ private { } } <Initialization>"\n"|"!" { //| - yy_pop_state(yyscanner); // end initialization + pop_state(yyscanner); // end initialization if (yyextra->last_enum) { yyextra->last_enum->initializer.str(yyextra->initializer.str()); @@ -1105,7 +1158,7 @@ private { yyterminate(); } yyextra->typeMode = false; - yy_pop_state(yyscanner); + pop_state(yyscanner); } /*------ fortran subroutine/function handling ------------------------------------------------------------*/ /* Start is initial condition */ @@ -1153,12 +1206,20 @@ private { <Subprog>{BS} { /* ignore white space */ } <Subprog>{ID} { yyextra->current->name = yytext; //cout << "1a==========> got " << yyextra->current->type << " " << yytext << " " << yyextra->lineNr << endl; - yyextra->modifiers[yyextra->current_root][yyextra->current->name.lower().str()].returnName = yyextra->current->name.lower(); + QCString returnName = yyextra->current->name.lower(); + /* if type is part of a module, mod name is necessary for output */ + if ((yyextra->current_root) && + (yyextra->current_root->section == Entry::CLASS_SEC || + yyextra->current_root->section == Entry::NAMESPACE_SEC)) + { + yyextra->current->name= yyextra->current_root->name + "::" + yyextra->current->name; + } + yyextra->modifiers[yyextra->current_root][yyextra->current->name.lower().str()].returnName = returnName; if (yyextra->ifType == IF_ABSTRACT || yyextra->ifType == IF_SPECIFIC) { yyextra->current_root->name = substitute( - yyextra->current_root->name, "$interface$", yytext); + yyextra->current_root->name, "$interface$", QCString(yytext).lower()); } BEGIN(Parameterlist); @@ -1273,7 +1334,7 @@ private { { subrHandleCommentBlockResult(yyscanner,yyextra->docBlock,TRUE); } - yy_pop_state(yyscanner); + pop_state(yyscanner); yyextra->docBlock.resize(0); } @@ -1301,7 +1362,7 @@ private { yyextra->colNr -= 1; unput(*yytext); handleCommentBlock(yyscanner,yyextra->docBlock,TRUE); - yy_pop_state(yyscanner); + pop_state(yyscanner); } /*-----Prototype parsing -------------------------------------------------------------------------*/ @@ -1516,7 +1577,7 @@ static void insertCharacter(char *contents, int length, int pos, char c) /* change yyextra->comments and bring line continuation character to previous line */ /* also used to set continuation marks in case of fortran code usage, done here as it is quite complicated code */ -const char* prepassFixedForm(const char* contents, int *hasContLine) +const char* prepassFixedForm(const char* contents, int *hasContLine,int fixedCommentAfter) { int column=0; int prevLineLength=0; @@ -1807,16 +1868,37 @@ static void copyEntry(std::shared_ptr<Entry> dest, const std::shared_ptr<Entry> void resolveModuleProcedures(yyscan_t yyscanner,Entry *current_root) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; - for (const auto &ce1 : yyextra->moduleProcedures) + if (yyextra->moduleProcedures.empty()) return; + + // build up map of available functions + std::map<std::string,std::shared_ptr<Entry>> procMap; { - // check all entries in this module - for (const auto &ce2 : current_root->children()) + for (const auto& cf: current_root->children()) { - if (ce1->name == ce2->name) + if (cf->section != Entry::FUNCTION_SEC) + continue; + + // remove scope from name + QCString name = cf->name; { - copyEntry(ce1, ce2); + int end = name.findRev(":"); + if (end != -1) + name.remove(0, end+1); } - } // for procedures in yyextra->current module + + procMap.insert(std::make_pair(name.str(), cf)); + } + } + + + // for all module procedures + for (const auto& ce1: yyextra->moduleProcedures) + { + if (procMap.find(ce1->name.str())!=procMap.end()) + { + std::shared_ptr<Entry> proc = procMap[ce1->name.str()]; + copyEntry(ce1, proc); + } } // for all interface module procedures yyextra->moduleProcedures.clear(); } @@ -2265,12 +2347,20 @@ static bool endScope(yyscan_t yyscanner,Entry *scope, bool isGlobalRoot) // iterate variables: get and apply yyextra->modifiers for (const auto &ce : scope->children()) { - if (ce->section != Entry::VARIABLE_SEC && ce->section != Entry::FUNCTION_SEC) + if (ce->section != Entry::VARIABLE_SEC && ce->section != Entry::FUNCTION_SEC && ce->section != Entry::CLASS_SEC && ce->section != Entry::FUNCTION_SEC) continue; //cout<<ce->name<<", "<<mdfsMap.contains(ce->name.lower())<<mdfsMap.count()<<endl; if (mdfsMap.find(ce->name.lower().str())!=mdfsMap.end()) applyModifiers(ce.get(), mdfsMap[ce->name.lower().str()]); + + // remove prefix for variable names + if (ce->section == Entry::VARIABLE_SEC || ce->section == Entry::FUNCTION_SEC) + { + int end = ce->name.findRev(":"); + if (end != -1) + ce->name.remove(0, end+1); + } } } @@ -2348,8 +2438,10 @@ static void addModule(yyscan_t yyscanner,const QCString &name, bool isModule) QCString fname = yyextra->fileName; int index = std::max(fname.findRev('/'), fname.findRev('\\')); fname = fname.right(fname.length()-index-1); + if (yyextra->mainPrograms) fname += "__" + QCString().setNum(yyextra->mainPrograms); + yyextra->mainPrograms++; fname = fname.prepend("__").append("__"); - yyextra->current->name = fname; + yyextra->current->name = substitute(fname, ".", "_"); } yyextra->current->type = "program"; yyextra->current->fileName = yyextra->fileName; @@ -2665,13 +2757,14 @@ static void parseMain(yyscan_t yyscanner, const QCString &fileName,const char *f if (yyextra->isFixedForm) { + yyextra->fixedCommentAfter = Config_getInt(FORTRAN_COMMENT_AFTER); msg("Prepassing fixed form of %s\n", qPrint(fileName)); //printf("---strlen=%d\n", strlen(fileBuf)); //clock_t start=clock(); //printf("Input fixed form string:\n%s\n", fileBuf); //printf("===========================\n"); - yyextra->inputString = prepassFixedForm(fileBuf, nullptr); + yyextra->inputString = prepassFixedForm(fileBuf, nullptr,yyextra->fixedCommentAfter); Debug::print(Debug::FortranFixed2Free,0,"======== Fixed to Free format =========\n---- Input fixed form string ------- \n%s\n", fileBuf); Debug::print(Debug::FortranFixed2Free,0,"---- Resulting free form string ------- \n%s\n", yyextra->inputString); //printf("Resulting free form string:\n%s\n", yyextra->inputString); @@ -2822,6 +2915,14 @@ static void scanner_abort(yyscan_t yyscanner) //exit(-1); } +static inline void pop_state(yyscan_t yyscanner) +{ + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + if ( yyg->yy_start_stack_ptr <= 0 ) + warn(yyextra->fileName,yyextra->lineNr,"Unexpected statement '%s'",yytext ); + else + yy_pop_state(yyscanner); +} //---------------------------------------------------------------------------- #include "fortranscanner.l.h" diff --git a/src/ftvhelp.cpp b/src/ftvhelp.cpp index 85291f5..85f3645 100644 --- a/src/ftvhelp.cpp +++ b/src/ftvhelp.cpp @@ -65,14 +65,19 @@ const char *JAVASCRIPT_LICENSE_TEXT = R"LIC(/* */ )LIC"; +struct FTVNode; +using FTVNodePtr = std::shared_ptr<FTVNode>; +using FTVNodeWeakPtr = std::weak_ptr<FTVNode>; +using FTVNodes = std::vector<FTVNodePtr>; + struct FTVNode { FTVNode(bool dir,const QCString &r,const QCString &f,const QCString &a, const QCString &n,bool sepIndex,bool navIndex,const Definition *df) - : isLast(TRUE), isDir(dir),ref(r),file(f),anchor(a),name(n), index(0), - parent(0), separateIndex(sepIndex), addToNavIndex(navIndex), + : isLast(TRUE), isDir(dir), ref(r), file(f), anchor(a), name(n), + separateIndex(sepIndex), addToNavIndex(navIndex), def(df) {} - ~FTVNode() { for (const auto &child : children) delete child; } + ~FTVNode() = default; int computeTreeDepth(int level) const; int numNodesAtLevel(int level,int maxLevel) const; bool isLast; @@ -81,9 +86,9 @@ struct FTVNode QCString file; QCString anchor; QCString name; - int index; - std::vector<FTVNode*> children; - FTVNode *parent; + int index = 0; + FTVNodes children; + FTVNodeWeakPtr parent; bool separateIndex; bool addToNavIndex; const Definition *def; @@ -119,31 +124,24 @@ int FTVNode::numNodesAtLevel(int level,int maxLevel) const //---------------------------------------------------------------------------- +struct FTVHelp::Private +{ + Private(bool TLI) : topLevelIndex(TLI) { indentNodes.resize(1); } + std::vector<FTVNodes> indentNodes; + int indent = 0; + bool topLevelIndex; + + void generateTree(TextStream &t,const FTVNodes &nl,int level,int maxLevel,int &index); + void generateLink(TextStream &t,const FTVNodePtr &n); +}; + /*! Constructs an ftv help object. * The object has to be \link initialize() initialized\endlink before it can * be used. */ -FTVHelp::FTVHelp(bool TLI) -{ - /* initial depth */ - m_indentNodes.resize(1); - m_indent=0; - m_topLevelIndex = TLI; -} - -/*! Destroys the ftv help object. */ -FTVHelp::~FTVHelp() -{ - for (auto &idx : m_indentNodes) - { - for (auto &n : idx) - { - delete n; - } - idx.clear(); - } - m_indentNodes.clear(); -} +FTVHelp::FTVHelp(bool TLI) : p(std::make_unique<Private>(TLI)) {} +FTVHelp::~FTVHelp() = default; +FTVHelp::FTVHelp(FTVHelp &&) = default; /*! This will create a folder tree view table of contents file (tree.js). * \sa finalize() @@ -167,9 +165,9 @@ void FTVHelp::finalize() */ void FTVHelp::incContentsDepth() { - //printf("%p: incContentsDepth() indent=%d\n",this,m_indent); - m_indent++; - m_indentNodes.resize(m_indent+1); + //printf("%p: incContentsDepth() indent=%d\n",this,p->indent); + p->indent++; + p->indentNodes.resize(p->indent+1); } /*! Decrease the level of the contents hierarchy. @@ -178,16 +176,16 @@ void FTVHelp::incContentsDepth() */ void FTVHelp::decContentsDepth() { - //printf("%p: decContentsDepth() indent=%d\n",this,m_indent); - ASSERT(m_indent>0); - if (m_indent>0) + //printf("%p: decContentsDepth() indent=%d\n",this,p->indent); + ASSERT(p->indent>0); + if (p->indent>0) { - m_indent--; - std::vector<FTVNode*> &nl = m_indentNodes[m_indent]; + p->indent--; + auto &nl = p->indentNodes[p->indent]; if (!nl.empty()) { - FTVNode *parent = nl.back(); - std::vector<FTVNode*> &children = m_indentNodes[m_indent+1]; + auto &parent = nl.back(); + auto &children = p->indentNodes[p->indent+1]; for (const auto &child : children) { parent->children.push_back(child); @@ -217,18 +215,18 @@ void FTVHelp::addContentsItem(bool isDir, const Definition *def ) { - //printf("%p: m_indent=%d addContentsItem(%s,%s,%s,%s)\n",this,m_indent,name,ref,file,anchor); - std::vector<FTVNode*> &nl = m_indentNodes[m_indent]; - FTVNode *newNode = new FTVNode(isDir,ref,file,anchor,name,separateIndex,addToNavIndex,def); + //printf("%p: p->indent=%d addContentsItem(%s,%s,%s,%s)\n",this,p->indent,name,ref,file,anchor); + auto &nl = p->indentNodes[p->indent]; if (!nl.empty()) { nl.back()->isLast=FALSE; } + auto newNode = std::make_shared<FTVNode>(isDir,ref,file,anchor,name,separateIndex,addToNavIndex,def); nl.push_back(newNode); newNode->index = static_cast<int>(nl.size()-1); - if (m_indent>0) + if (p->indent>0) { - std::vector<FTVNode*> &pnl = m_indentNodes[m_indent-1]; + auto &pnl = p->indentNodes[p->indent-1]; if (!pnl.empty()) { newNode->parent = pnl.back(); @@ -236,7 +234,7 @@ void FTVHelp::addContentsItem(bool isDir, } } -static QCString node2URL(const FTVNode *n,bool overruleFile=FALSE,bool srcLink=FALSE) +static QCString node2URL(const FTVNodePtr &n,bool overruleFile=FALSE,bool srcLink=FALSE) { QCString url = n->file; if (!url.isEmpty() && url.at(0)=='!') // relative URL @@ -268,22 +266,23 @@ static QCString node2URL(const FTVNode *n,bool overruleFile=FALSE,bool srcLink=F return url; } -QCString FTVHelp::generateIndentLabel(FTVNode *n,int level) +static QCString generateIndentLabel(const FTVNodePtr &n,int level) { QCString result; - if (n->parent) + auto parent = n->parent.lock(); + if (parent) { - result=generateIndentLabel(n->parent,level+1); + result=generateIndentLabel(parent,level+1); } result+=QCString().setNum(n->index)+"_"; return result; } -void FTVHelp::generateIndent(TextStream &t, FTVNode *n,bool opened) +static void generateIndent(TextStream &t, const FTVNodePtr &n,bool opened) { int indent=0; - FTVNode *p = n->parent; - while (p) { indent++; p=p->parent; } + auto parent = n->parent.lock(); + while (parent) { indent++; parent=parent->parent.lock(); } if (n->isDir) { QCString dir = opened ? "▼" : "►"; @@ -299,7 +298,7 @@ void FTVHelp::generateIndent(TextStream &t, FTVNode *n,bool opened) } } -void FTVHelp::generateLink(TextStream &t,FTVNode *n) +void FTVHelp::Private::generateLink(TextStream &t,const FTVNodePtr &n) { //printf("FTVHelp::generateLink(ref=%s,file=%s,anchor=%s\n", // qPrint(n->ref),qPrint(n->file),qPrint(n->anchor)); @@ -326,7 +325,7 @@ void FTVHelp::generateLink(TextStream &t,FTVNode *n) t << node2URL(n); if (!setTarget) { - if (m_topLevelIndex) + if (topLevelIndex) t << "\" target=\"basefrm\">"; else t << "\" target=\"_self\">"; @@ -387,13 +386,15 @@ static char compoundIcon(const ClassDef *cd) return icon; } -void FTVHelp::generateTree(TextStream &t, const std::vector<FTVNode*> &nl,int level,int maxLevel,int &index) +void FTVHelp::Private::generateTree(TextStream &t, const FTVNodes &nl,int level,int maxLevel,int &index) { for (const auto &n : nl) { t << "<tr id=\"row_" << generateIndentLabel(n,0) << "\""; if ((index&1)==0) // even row t << " class=\"even\""; + else + t << " class=\"odd\""; if (level>=maxLevel) // item invisible by default t << " style=\"display:none;\""; else // item visible by default @@ -522,26 +523,28 @@ class NavIndexEntryList : public std::vector<NavIndexEntry> { }; -static QCString pathToNode(const FTVNode *leaf,const FTVNode *n) +static QCString pathToNode(const FTVNodePtr &leaf,const FTVNodePtr &n) { QCString result; - if (n->parent) + auto parent = n->parent.lock(); + if (parent) { - result+=pathToNode(leaf,n->parent); + result+=pathToNode(leaf,parent); } result+=QCString().setNum(n->index); if (leaf!=n) result+=","; return result; } -static bool dupOfParent(const FTVNode *n) +static bool dupOfParent(const FTVNodePtr &n) { - if (n->parent==0) return FALSE; - if (n->file==n->parent->file) return TRUE; + auto parent = n->parent.lock(); + if (!parent) return FALSE; + if (n->file==parent->file) return TRUE; return FALSE; } -static void generateJSLink(TextStream &t,const FTVNode *n) +static void generateJSLink(TextStream &t,const FTVNodePtr &n) { if (n->file.isEmpty()) // no link { @@ -565,11 +568,12 @@ static QCString convertFileId2Var(const QCString &fileId) } static bool generateJSTree(NavIndexEntryList &navIndex,TextStream &t, - const std::vector<FTVNode*> &nl,int level,bool &first) + const FTVNodes &nl,int level,bool &first) { QCString htmlOutput = Config_getString(HTML_OUTPUT); QCString indentStr; indentStr.fill(' ',level*2); + bool found=FALSE; for (const auto &n : nl) { @@ -654,7 +658,7 @@ static bool generateJSTree(NavIndexEntryList &navIndex,TextStream &t, return found; } -static void generateJSNavTree(const std::vector<FTVNode*> &nodeList) +static void generateJSNavTree(const FTVNodes &nodeList) { QCString htmlOutput = Config_getString(HTML_OUTPUT); std::ofstream f(htmlOutput.str()+"/navtreedata.js",std::ofstream::out | std::ofstream::binary); @@ -769,9 +773,11 @@ void FTVHelp::generateTreeViewImages() QCString dname=Config_getString(HTML_OUTPUT); const ResourceMgr &rm = ResourceMgr::instance(); rm.copyResource("doc.luma",dname); + rm.copyResource("docd.luma",dname); rm.copyResource("folderopen.luma",dname); rm.copyResource("folderclosed.luma",dname); rm.copyResource("splitbar.lum",dname); + rm.copyResource("splitbard.lum",dname); } // new style scripts @@ -780,11 +786,26 @@ void FTVHelp::generateTreeViewScripts() QCString htmlOutput = Config_getString(HTML_OUTPUT); // generate navtree.js & navtreeindex.js - generateJSNavTree(m_indentNodes[0]); + generateJSNavTree(p->indentNodes[0]); // copy resize.js & navtree.css - ResourceMgr::instance().copyResource("resize.js",htmlOutput); - ResourceMgr::instance().copyResource("navtree.css",htmlOutput); + auto &mgr = ResourceMgr::instance(); + { + std::ofstream f(htmlOutput.str()+"/resize.js",std::ofstream::out | std::ofstream::binary); + if (f.is_open()) + { + TextStream t(&f); + t << substitute(mgr.getAsString("resize.js"), "$TREEVIEW_WIDTH", QCString().setNum(Config_getInt(TREEVIEW_WIDTH))); + } + } + { + std::ofstream f(htmlOutput.str()+"/navtree.css",std::ofstream::out | std::ofstream::binary); + if (f.is_open()) + { + TextStream t(&f); + t << HtmlGenerator::getNavTreeCss(); + } + } } // write tree inside page @@ -793,7 +814,7 @@ void FTVHelp::generateTreeViewInline(TextStream &t) int preferredNumEntries = Config_getInt(HTML_INDEX_NUM_ENTRIES); t << "<div class=\"directory\">\n"; int d=1, depth=1; - for (const auto &n : m_indentNodes[0]) + for (const auto &n : p->indentNodes[0]) { if (!n->children.empty()) { @@ -820,7 +841,7 @@ void FTVHelp::generateTreeViewInline(TextStream &t) for (int i=1;i<=depth;i++) { int num=0; - for (const auto &n : m_indentNodes[0]) + for (const auto &n : p->indentNodes[0]) { num+=n->numNodesAtLevel(0,i); } @@ -837,11 +858,11 @@ void FTVHelp::generateTreeViewInline(TextStream &t) } //printf("preferred depth=%d\n",preferredDepth); - if (!m_indentNodes[0].empty()) + if (!p->indentNodes[0].empty()) { t << "<table class=\"directory\">\n"; int index=0; - generateTree(t,m_indentNodes[0],0,preferredDepth,index); + p->generateTree(t,p->indentNodes[0],0,preferredDepth,index); t << "</table>\n"; } diff --git a/src/ftvhelp.h b/src/ftvhelp.h index 196b01c..e726065 100644 --- a/src/ftvhelp.h +++ b/src/ftvhelp.h @@ -24,20 +24,23 @@ #ifndef FTVHELP_H #define FTVHELP_H +#include <memory> #include <vector> -#include "index.h" +#include "qcstring.h" class Definition; +class MemberDef; class TextStream; -struct FTVNode; + /** A class that generates a dynamic tree view side panel. */ -class FTVHelp : public IndexIntf +class FTVHelp { public: FTVHelp(bool LTI); ~FTVHelp(); + FTVHelp(FTVHelp &&); void initialize(); void finalize(); void incContentsDepth(); @@ -59,13 +62,8 @@ class FTVHelp : public IndexIntf static void generateTreeViewImages(); void generateTreeViewScripts(); private: - void generateTree(TextStream &t,const std::vector<FTVNode*> &nl,int level,int maxLevel,int &index); - QCString generateIndentLabel(FTVNode *n,int level); - void generateIndent(TextStream &t,FTVNode *n,bool opened); - void generateLink(TextStream &t,FTVNode *n); - std::vector< std::vector<FTVNode*> > m_indentNodes; - int m_indent; - bool m_topLevelIndex; + struct Private; + std::unique_ptr<Private> p; }; extern const char *JAVASCRIPT_LICENSE_TEXT; diff --git a/src/groupdef.cpp b/src/groupdef.cpp index f9b3c71..c6d3fc0 100644 --- a/src/groupdef.cpp +++ b/src/groupdef.cpp @@ -707,7 +707,7 @@ void GroupDefImpl::writeTagFile(TextStream &tagFile) MemberList * ml = getMemberList(lmd->type); if (ml) { - ml->writeTagFile(tagFile); + ml->writeTagFile(tagFile,true); } } } @@ -716,7 +716,7 @@ void GroupDefImpl::writeTagFile(TextStream &tagFile) { for (const auto &mg : m_memberGroups) { - mg->writeTagFile(tagFile); + mg->writeTagFile(tagFile,true); } } break; diff --git a/src/growbuf.h b/src/growbuf.h index ba4c5fe..6581572 100644 --- a/src/growbuf.h +++ b/src/growbuf.h @@ -106,6 +106,11 @@ class GrowBuf m_pos+=l; } } + void addInt(const char *fmt,int value) { + char tmp[50]; + snprintf(tmp,50,fmt,value); + addStr(tmp); + } char *get() { return m_str; } const char *get() const { return m_str; } size_t getPos() const { return m_pos; } diff --git a/src/htmldocvisitor.cpp b/src/htmldocvisitor.cpp index 04598c2..c97a50e 100644 --- a/src/htmldocvisitor.cpp +++ b/src/htmldocvisitor.cpp @@ -34,6 +34,8 @@ #include "plantuml.h" #include "formula.h" #include "fileinfo.h" +#include "indexlist.h" +#include "growbuf.h" static const int NUM_HTML_LIST_TYPES = 4; static const char types[][NUM_HTML_LIST_TYPES] = {"1", "a", "i", "A"}; @@ -116,6 +118,7 @@ static bool mustBeOutsideParagraph(const DocNodeVariant &n) /* <hr> */ DocHorRuler, /* <blockquote> */ DocHtmlBlockQuote, /* \parblock */ DocParBlock, + /* <details> */ DocHtmlDetails, DocIncOperator >(n)) { return TRUE; @@ -522,9 +525,6 @@ void HtmlDocVisitor::operator()(const DocStyleChange &s) case DocStyleChange::Span: if (s.enable()) m_t << "<span" << htmlAttribsToString(s.attribs()) << ">"; else m_t << "</span>"; break; - case DocStyleChange::Details: - if (s.enable()) m_t << "<details" << htmlAttribsToString(s.attribs()) << ">\n"; else m_t << "</details>\n"; - break; case DocStyleChange::Summary: if (s.enable()) m_t << "<summary" << htmlAttribsToString(s.attribs()) << ">"; else m_t << "</summary>"; break; @@ -909,30 +909,77 @@ void HtmlDocVisitor::operator()(const DocFormula &f) } else { - m_t << "<img class=\"formula" - << (bDisplay ? "Dsp" : "Inl"); - m_t << "\" alt=\""; - filterQuotedCdataAttr(f.text()); - m_t << "\""; - m_t << " src=\"" << f.relPath() << f.name(); - if (Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg) - { - m_t << ".svg"; - } - else - { - m_t << ".png"; - } - FormulaManager::DisplaySize size = FormulaManager::instance().displaySize(f.id()); - if (size.width!=-1) - { - m_t << "\" width=\"" << size.width; - } - if (size.height!=-1) + const Formula *formula = FormulaManager::instance().findFormula(f.id()); + + enum class ImageType { Light, Dark }; + enum class Visibility { Always, Dark, Light, AutoDark, AutoLight }; + auto writeFormula = [&](ImageType imgType,Visibility visibility) -> QCString { + // see https://chipcullen.com/how-to-have-dark-mode-image-that-works-with-user-choice for the design idea + TextStream t; + QCString extension = Config_getEnum(HTML_FORMULA_FORMAT)==HTML_FORMULA_FORMAT_t::svg ? ".svg":".png" ; + if (visibility==Visibility::AutoDark || visibility==Visibility::AutoLight) + { + t << "<picture>"; + t << "<source srcset=\"" << f.relPath() << f.name(); + if (visibility==Visibility::AutoDark) + { + t << extension; + t << "\" media=\"(prefers-color-scheme: light)\""; + } + else // AutoLight + { + t << "_dark"; + t << extension; + t << "\" media=\"(prefers-color-scheme: dark)\""; + } + t << "/>"; + } + t << "<img class=\"formula"; + t << (bDisplay ? "Dsp" : "Inl"); + if (visibility==Visibility::Light) t << " light-mode-visible"; + else if (visibility==Visibility::Dark) t << " dark-mode-visible"; + t << "\" alt=\"" << filterQuotedCdataAttr(f.text()) << "\"" << " src=\"" << f.relPath() << f.name(); + if (imgType==ImageType::Dark) t << "_dark"; + t << extension; + if (formula && formula->width()!=-1) + { + t << "\" width=\""; + t << formula->width(); + } + if (formula && formula->height()!=-1) + { + t << "\" height=\""; + t << formula->height(); + } + t << "\"/>"; + if (visibility==Visibility::AutoDark || visibility==Visibility::AutoLight) + { + t << "</picture>"; + } + return QCString(t.str()); + }; + + auto colorStyle = Config_getEnum(HTML_COLORSTYLE); + switch(colorStyle) { - m_t << "\" height=\"" << size.height; + case HTML_COLORSTYLE_t::LIGHT: + m_t << writeFormula(ImageType::Light,Visibility::Always); + break; + case HTML_COLORSTYLE_t::DARK: + m_t << writeFormula(ImageType::Dark, Visibility::Always); + break; + case HTML_COLORSTYLE_t::AUTO_LIGHT: + m_t << writeFormula(ImageType::Light, Visibility::AutoLight); + break; + case HTML_COLORSTYLE_t::AUTO_DARK: + m_t << writeFormula(ImageType::Dark, Visibility::AutoDark); + break; + case HTML_COLORSTYLE_t::TOGGLE: + // write the image twice and use javascript (darkmode_toggle.js) to show only one of them + m_t << writeFormula(ImageType::Light,Visibility::Light); + m_t << writeFormula(ImageType::Dark, Visibility::Dark); + break; } - m_t << "\"/>"; } if (bDisplay) { @@ -1227,7 +1274,8 @@ static bool determineIfNeedsTag(const DocPara &p) DocSimpleSect, DocXRefItem, DocHtmlBlockQuote, - DocParBlock + DocParBlock, + DocHtmlDetails >(*p.parent())) { needsTag = TRUE; @@ -1566,7 +1614,7 @@ void HtmlDocVisitor::operator()(const DocInternal &i) void HtmlDocVisitor::operator()(const DocHRef &href) { if (m_hide) return; - if (href.url().left(7)=="mailto:") + if (href.url().startsWith("mailto:")) { writeObfuscatedMailAddress(href.url().mid(7)); } @@ -1580,6 +1628,16 @@ void HtmlDocVisitor::operator()(const DocHRef &href) m_t << "</a>"; } +void HtmlDocVisitor::operator()(const DocHtmlDetails &d) +{ + if (m_hide) return; + forceEndParagraph(d); + m_t << "<details " << htmlAttribsToString(d.attribs()) << ">\n"; + visitChildren(d); + m_t << "</details>\n"; + forceStartParagraph(d); +} + void HtmlDocVisitor::operator()(const DocHtmlHeader &header) { if (m_hide) return; @@ -1954,7 +2012,6 @@ void HtmlDocVisitor::operator()(const DocHtmlBlockQuote &b) { if (m_hide) return; forceEndParagraph(b); - QCString attrs = htmlAttribsToString(b.attribs()); m_t << "<blockquote class=\"doxtable\"" << htmlAttribsToString(b.attribs()) << ">\n"; visitChildren(b); m_t << "</blockquote>\n"; @@ -2032,9 +2089,10 @@ void HtmlDocVisitor::filter(const QCString &str, const bool retainNewline) /// Escape basic entities to produce a valid CDATA attribute value, /// assume that the outer quoting will be using the double quote " -void HtmlDocVisitor::filterQuotedCdataAttr(const QCString &str) +QCString HtmlDocVisitor::filterQuotedCdataAttr(const QCString &str) { - if (str.isEmpty()) return; + GrowBuf growBuf; + if (str.isEmpty()) return str; const char *p=str.data(); char c; while (*p) @@ -2042,30 +2100,41 @@ void HtmlDocVisitor::filterQuotedCdataAttr(const QCString &str) c=*p++; switch(c) { - case '&': m_t << "&"; break; - case '"': m_t << """; break; - case '<': m_t << "<"; break; - case '>': m_t << ">"; break; - case '\\': if ((*p == '(') || (*p == ')')) - m_t << "\\‍" << *p++; + case '&': growBuf.addStr("&"); break; + case '"': growBuf.addStr("""); break; + case '<': growBuf.addStr("<"); break; + case '>': growBuf.addStr(">"); break; + case '\\': + if ((*p == '(') || (*p == ')')) + { + growBuf.addStr("\\‍"); + growBuf.addChar(*p++); + } else - m_t << c; + { + growBuf.addChar(c); + } break; default: { uchar uc = static_cast<uchar>(c); if (uc<32 && !isspace(c)) // non-printable control characters { - m_t << "$" << hex[uc>>4] << hex[uc&0xF] << ";"; + growBuf.addStr("$"); + growBuf.addChar(hex[uc>>4]); + growBuf.addChar(hex[uc&0xF]); + growBuf.addStr(";"); } else { - m_t << c; + growBuf.addChar(c); } } break; } } + growBuf.addChar(0); + return growBuf.get(); } void HtmlDocVisitor::startLink(const QCString &ref,const QCString &file, diff --git a/src/htmldocvisitor.h b/src/htmldocvisitor.h index 36fb760..510f349 100644 --- a/src/htmldocvisitor.h +++ b/src/htmldocvisitor.h @@ -78,6 +78,7 @@ class HtmlDocVisitor : public DocVisitor void operator()(const DocHtmlCaption &); void operator()(const DocInternal &); void operator()(const DocHRef &); + void operator()(const DocHtmlDetails &); void operator()(const DocHtmlHeader &); void operator()(const DocImage &); void operator()(const DocDotFile &); @@ -115,7 +116,7 @@ class HtmlDocVisitor : public DocVisitor void writeObfuscatedMailAddress(const QCString &url); void filter(const QCString &str, const bool retainNewline = false); - void filterQuotedCdataAttr(const QCString &str); + QCString filterQuotedCdataAttr(const QCString &str); void startLink(const QCString &ref,const QCString &file, const QCString &relPath,const QCString &anchor, const QCString &tooltip = ""); diff --git a/src/htmlentity.cpp b/src/htmlentity.cpp index bcd1b25..16d4469 100644 --- a/src/htmlentity.cpp +++ b/src/htmlentity.cpp @@ -479,7 +479,7 @@ void HtmlEntityMapper::writeXMLSchema(TextStream &t) for (int i=0;i<g_numHtmlEntities - g_numberHtmlMappedCmds;i++) { QCString bareName = g_htmlEntities[i].xml; - if (!bareName.isEmpty() && bareName.at(0)=='<' && bareName.right(2)=="/>") + if (!bareName.isEmpty() && bareName.at(0)=='<' && bareName.endsWith("/>")) { bareName = bareName.mid(1,bareName.length()-3); // strip < and /> t << " <xsd:element name=\"" << bareName << "\" type=\"docEmptyType\" />\n"; diff --git a/src/htmlgen.cpp b/src/htmlgen.cpp index ea1f7e6..9f00196 100644 --- a/src/htmlgen.cpp +++ b/src/htmlgen.cpp @@ -54,6 +54,7 @@ #include "dir.h" #include "utf8.h" #include "textstream.h" +#include "indexlist.h" //#define DBG_HTML(x) x; #define DBG_HTML(x) @@ -70,11 +71,10 @@ static void writeClientSearchBox(TextStream &t,const QCString &relPath) { t << " <div id=\"MSearchBox\" class=\"MSearchBoxInactive\">\n"; t << " <span class=\"left\">\n"; - t << " <img id=\"MSearchSelect\" src=\"" << relPath << "search/mag_sel.svg\"\n"; - t << " onmouseover=\"return searchBox.OnSearchSelectShow()\"\n"; - t << " onmouseout=\"return searchBox.OnSearchSelectHide()\"\n"; - t << " alt=\"\"/>\n"; - t << " <input type=\"text\" id=\"MSearchField\" value=\"" + t << " <span id=\"MSearchSelect\" "; + t << " onmouseover=\"return searchBox.OnSearchSelectShow()\" "; + t << " onmouseout=\"return searchBox.OnSearchSelectHide()\"> </span>\n"; + t << " <input type=\"text\" id=\"MSearchField\" value=\"\" placeholder=\"" << theTranslator->trSearch() << "\" accesskey=\"S\"\n"; t << " onfocus=\"searchBox.OnSearchFieldFocus(true)\" \n"; t << " onblur=\"searchBox.OnSearchFieldFocus(false)\" \n"; @@ -103,10 +103,10 @@ static void writeServerSearchBox(TextStream &t,const QCString &relPath,bool high t << "search.php"; } t << "\" method=\"get\">\n"; - t << " <img id=\"MSearchSelect\" src=\"" << relPath << "search/mag.svg\" alt=\"\"/>\n"; + t << " <span id=\"MSearchSelectExt\"> </span>\n"; if (!highlightSearch) { - t << " <input type=\"text\" id=\"MSearchField\" name=\"query\" value=\"" + t << " <input type=\"text\" id=\"MSearchField\" name=\"query\" value=\"\" placeholder=\"" << theTranslator->trSearch() << "\" size=\"20\" accesskey=\"S\" \n"; t << " onfocus=\"searchBox.OnSearchFieldFocus(true)\" \n"; t << " onblur=\"searchBox.OnSearchFieldFocus(false)\"/>\n"; @@ -349,14 +349,17 @@ static QCString substituteHtmlKeywords(const QCString &str, } else { - FileInfo cssfi(cssFile.str()); - if (cssfi.exists()) + if (!cssFile.startsWith("http:") && !cssFile.startsWith("https:")) { - cssFile = cssfi.fileName(); - } - else - { - cssFile = "doxygen.css"; + FileInfo cssfi(cssFile.str()); + if (cssfi.exists()) + { + cssFile = cssfi.fileName(); + } + else + { + cssFile = "doxygen.css"; + } } } @@ -366,10 +369,18 @@ static QCString substituteHtmlKeywords(const QCString &str, { if (!fileName.empty()) { - FileInfo fi(fileName); - if (fi.exists()) + QCString htmlStyleSheet = fileName.c_str(); + if (htmlStyleSheet.startsWith("http:") || htmlStyleSheet.startsWith("https:")) { - extraCssText += "<link href=\"$relpath^"+stripPath(fileName.c_str())+"\" rel=\"stylesheet\" type=\"text/css\"/>\n"; + extraCssText += "<link href=\""+htmlStyleSheet+"\" rel=\"stylesheet\" type=\"text/css\"/>\n"; + } + else + { + FileInfo fi(fileName); + if (fi.exists()) + { + extraCssText += "<link href=\"$relpath^"+stripPath(fileName.c_str())+"\" rel=\"stylesheet\" type=\"text/css\"/>\n"; + } } } } @@ -443,7 +454,7 @@ static QCString substituteHtmlKeywords(const QCString &str, { auto mathJaxVersion = Config_getEnum(MATHJAX_VERSION); QCString path = Config_getString(MATHJAX_RELPATH); - if (path.isEmpty() || path.left(2)=="..") // relative path + if (path.isEmpty() || path.startsWith("..")) // relative path { path.prepend(relPath); } @@ -549,6 +560,12 @@ static QCString substituteHtmlKeywords(const QCString &str, } } + QCString darkModeJs; + if (Config_getEnum(HTML_COLORSTYLE)==HTML_COLORSTYLE_t::TOGGLE) + { + darkModeJs="<script type=\"text/javascript\" src=\"$relpath^darkmode_toggle.js\"></script>\n"; + } + // first substitute generic keywords QCString result = substituteKeywords(str,title, convertToHtml(Config_getString(PROJECT_NAME)), @@ -562,6 +579,7 @@ static QCString substituteHtmlKeywords(const QCString &str, result = substitute(result,"$searchbox",searchBox); result = substitute(result,"$search",searchCssJs); result = substitute(result,"$mathjax",mathJaxJs); + result = substitute(result,"$darkmode",darkModeJs); result = substitute(result,"$generatedby",generatedBy); result = substitute(result,"$extrastylesheet",extraCssText); result = substitute(result,"$relpath$",relPath); //<-- obsolete: for backwards compatibility only @@ -583,6 +601,72 @@ static QCString substituteHtmlKeywords(const QCString &str, return result; } +//--------------------------------------------------------------------------------------------- + +static StringUnorderedMap g_lightMap; +static StringUnorderedMap g_darkMap; + +static void fillColorStyleMap(const QCString &definitions,StringUnorderedMap &map) +{ + int p=0,i=0; + while ((i=definitions.find('\n',p))!=-1) + { + QCString line = definitions.mid(p,i-p); + if (line.startsWith("--")) + { + int separator = line.find(':'); + assert(separator!=-1); + std::string key = line.left(separator).str(); + int semi = line.find(';'); + assert(semi!=-1); + std::string value = line.mid(separator+1,semi-separator-1).stripWhiteSpace().str(); + map.insert(std::make_pair(key,value)); + //printf("var(%s)=%s\n",qPrint(key),qPrint(value)); + } + p=i+1; + } +} + +static QCString replaceVariables(const QCString &input) +{ + auto doReplacements = [&input](const StringUnorderedMap &mapping) -> QCString + { + GrowBuf output; + int p=0,i=0; + while ((i=input.find("var(",p))!=-1) + { + output.addStr(input.data()+p,i-p); + int j=input.find(")",i+4); + assert(j!=-1); + auto it = mapping.find(input.mid(i+4,j-i-4).str()); // find variable + assert(it!=mapping.end()); // should be found + output.addStr(it->second); // add it value + //printf("replace '%s' by '%s'\n",qPrint(input.mid(i+4,j-i-4)),qPrint(it->second)); + p=j+1; + } + output.addStr(input.data()+p,input.length()-p); + output.addChar(0); + return output.get(); + }; + + auto colorStyle = Config_getEnum(HTML_COLORSTYLE); + if (colorStyle==HTML_COLORSTYLE_t::LIGHT) + { + return doReplacements(g_lightMap); + } + else if (colorStyle==HTML_COLORSTYLE_t::DARK) + { + return doReplacements(g_darkMap); + } + else + { + return input; + } +} + +//---------------------------------------------------------------------------------------------- + + //-------------------------------------------------------------------------- HtmlCodeGenerator::HtmlCodeGenerator(TextStream &t) : m_t(t) @@ -941,7 +1025,6 @@ HtmlGenerator::~HtmlGenerator() //printf("HtmlGenerator::~HtmlGenerator()\n"); } - void HtmlGenerator::init() { QCString dname = Config_getString(HTML_OUTPUT); @@ -984,14 +1067,36 @@ void HtmlGenerator::init() createSubDirs(d); ResourceMgr &mgr = ResourceMgr::instance(); - if (Config_getBool(HTML_DYNAMIC_MENUS)) + auto colorStyle = Config_getEnum(HTML_COLORSTYLE); + if (colorStyle==HTML_COLORSTYLE_t::LIGHT) { - mgr.copyResourceAs("tabs.css",dname,"tabs.css"); + fillColorStyleMap(replaceColorMarkers(mgr.getAsString("lightmode_settings.css")),g_lightMap); } - else // stylesheet for the 'old' static tabs + else if (colorStyle==HTML_COLORSTYLE_t::DARK) { - mgr.copyResourceAs("fixed_tabs.css",dname,"tabs.css"); + fillColorStyleMap(replaceColorMarkers(mgr.getAsString("darkmode_settings.css")),g_darkMap); + } + + { + QCString tabsCss; + if (Config_getBool(HTML_DYNAMIC_MENUS)) + { + tabsCss = mgr.getAsString("tabs.css"); + } + else // stylesheet for the 'old' static tabs + { + tabsCss = mgr.getAsString("fixed_tabs.css"); + } + + std::ofstream f(dname.str()+"/tabs.css",std::ofstream::out | std::ofstream::binary); + if (f.is_open()) + { + TextStream t(&f); + t << replaceVariables(tabsCss); + } } + + mgr.copyResource("jquery.js",dname); if (Config_getBool(INTERACTIVE_SVG)) { @@ -1003,6 +1108,17 @@ void HtmlGenerator::init() mgr.copyResource("menu.js",dname); } + if (colorStyle==HTML_COLORSTYLE_t::TOGGLE) + { + //mgr.copyResource("darkmode_toggle.js",dname); + std::ofstream f(dname.str()+"/darkmode_toggle.js",std::ofstream::out | std::ofstream::binary); + if (f.is_open()) + { + TextStream t(&f); + t << replaceColorMarkers(mgr.getAsString("darkmode_toggle.js")); + } + } + { std::ofstream f(dname.str()+"/dynsections.js",std::ofstream::out | std::ofstream::binary); if (f.is_open()) @@ -1031,13 +1147,20 @@ void HtmlGenerator::writeTabData() QCString dname=Config_getString(HTML_OUTPUT); ResourceMgr &mgr = ResourceMgr::instance(); //writeColoredImgData(dname,colored_tab_data); - mgr.copyResource("tab_a.lum",dname); - mgr.copyResource("tab_b.lum",dname); - mgr.copyResource("tab_h.lum",dname); - mgr.copyResource("tab_s.lum",dname); - mgr.copyResource("nav_h.lum",dname); - mgr.copyResource("nav_f.lum",dname); - mgr.copyResource("bc_s.luma",dname); + mgr.copyResource("tab_a.lum",dname); // active, light mode + mgr.copyResource("tab_b.lum",dname); // normal, light mode + mgr.copyResource("tab_h.lum",dname); // highlight, light mode + mgr.copyResource("tab_s.lum",dname); // separator, light mode + mgr.copyResource("tab_ad.lum",dname); // active, dark mode + mgr.copyResource("tab_bd.lum",dname); // normal, dark mode + mgr.copyResource("tab_hd.lum",dname); // highlight, dark mode + mgr.copyResource("tab_sd.lum",dname); // separator, light mode + mgr.copyResource("nav_h.lum",dname); // header gradient, light mode + mgr.copyResource("nav_hd.lum",dname); // header gradient, dark mode + mgr.copyResource("nav_f.lum",dname); // member definition header, light mode + mgr.copyResource("nav_fd.lum",dname); // member definition header, dark mode + mgr.copyResource("bc_s.luma",dname); // breadcrumb separator, light mode + mgr.copyResource("bc_sd.luma",dname); // breadcrumb separator, dark mode mgr.copyResource("doxygen.svg",dname); Doxygen::indexList->addImageFile("doxygen.svg"); mgr.copyResource("closed.luma",dname); @@ -1058,28 +1181,23 @@ void HtmlGenerator::writeTabData() void HtmlGenerator::writeSearchData(const QCString &dname) { - bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH); + //bool serverBasedSearch = Config_getBool(SERVER_BASED_SEARCH); //writeImgData(dname,serverBasedSearch ? search_server_data : search_client_data); ResourceMgr &mgr = ResourceMgr::instance(); - mgr.copyResource("search_l.png",dname); - Doxygen::indexList->addImageFile("search/search_l.png"); - mgr.copyResource("search_m.png",dname); - Doxygen::indexList->addImageFile("search/search_m.png"); - mgr.copyResource("search_r.png",dname); - Doxygen::indexList->addImageFile("search/search_r.png"); - if (serverBasedSearch) - { - mgr.copyResource("mag.svg",dname); - Doxygen::indexList->addImageFile("search/mag.svg"); - } - else - { - mgr.copyResource("close.svg",dname); - Doxygen::indexList->addImageFile("search/close.svg"); - mgr.copyResource("mag_sel.svg",dname); - Doxygen::indexList->addImageFile("search/mag_sel.svg"); - } + // server side search resources + mgr.copyResource("mag.svg",dname); + mgr.copyResource("mag_d.svg",dname); + Doxygen::indexList->addImageFile("search/mag.svg"); + Doxygen::indexList->addImageFile("search/mag_d.svg"); + + // client side search resources + mgr.copyResource("close.svg",dname); + Doxygen::indexList->addImageFile("search/close.svg"); + mgr.copyResource("mag_sel.svg",dname); + mgr.copyResource("mag_seld.svg",dname); + Doxygen::indexList->addImageFile("search/mag_sel.svg"); + Doxygen::indexList->addImageFile("search/mag_seld.svg"); QCString searchDirName = dname; std::ofstream f(searchDirName.str()+"/search.css",std::ofstream::out | std::ofstream::binary); @@ -1087,35 +1205,89 @@ void HtmlGenerator::writeSearchData(const QCString &dname) { TextStream t(&f); QCString searchCss; + // the position of the search box depends on a number of settings. + // Insert the right piece of CSS code depending on which options are selected if (Config_getBool(DISABLE_INDEX)) { if (Config_getBool(GENERATE_TREEVIEW) && Config_getBool(FULL_SIDEBAR)) { - searchCss = mgr.getAsString("search_sidebar.css"); + searchCss = mgr.getAsString("search_sidebar.css"); // we have a full height side bar + } + else if (Config_getBool(HTML_COLORSTYLE)==HTML_COLORSTYLE_t::TOGGLE) + { + searchCss = mgr.getAsString("search_nomenu_toggle.css"); // we have no tabs but do have a darkmode button } else { - searchCss = mgr.getAsString("search_nomenu.css"); + searchCss = mgr.getAsString("search_nomenu.css"); // we have no tabs and no darkmode button } } else if (!Config_getBool(HTML_DYNAMIC_MENUS)) { - searchCss = mgr.getAsString("search_fixedtabs.css"); + searchCss = mgr.getAsString("search_fixedtabs.css"); // we have tabs, but they are static } else { - searchCss = mgr.getAsString("search.css"); + searchCss = mgr.getAsString("search.css"); // default case with a dynamic menu bar } + // and then add the option independent part of the styling searchCss += mgr.getAsString("search_common.css"); - searchCss = substitute(replaceColorMarkers(searchCss),"$doxygenversion",getDoxygenVersion()); - t << searchCss; + searchCss = substitute(searchCss,"$doxygenversion",getDoxygenVersion()); + t << replaceVariables(searchCss); Doxygen::indexList->addStyleSheetFile("search/search.css"); } } +static void writeDefaultStyleSheet(TextStream &t) +{ + t << "/* The standard CSS for doxygen " << getDoxygenVersion() << "*/\n\n"; + switch (Config_getEnum(HTML_COLORSTYLE)) + { + case HTML_COLORSTYLE_t::LIGHT: + case HTML_COLORSTYLE_t::DARK: + /* variables will be resolved while writing to the CSS file */ + break; + case HTML_COLORSTYLE_t::AUTO_LIGHT: + case HTML_COLORSTYLE_t::TOGGLE: + t << "html {\n"; + t << replaceColorMarkers(ResourceMgr::instance().getAsString("lightmode_settings.css")); + t << "}\n\n"; + break; + case HTML_COLORSTYLE_t::AUTO_DARK: + t << "html {\n"; + t << replaceColorMarkers(ResourceMgr::instance().getAsString("darkmode_settings.css")); + t << "}\n\n"; + break; + } + if (Config_getEnum(HTML_COLORSTYLE)==HTML_COLORSTYLE_t::AUTO_LIGHT) + { + t << "@media (prefers-color-scheme: dark) {\n"; + t << " html:not(.dark-mode) {\n"; + t << " color-scheme: dark;\n\n"; + t << replaceColorMarkers(ResourceMgr::instance().getAsString("darkmode_settings.css")); + t << "}}\n"; + } + else if (Config_getEnum(HTML_COLORSTYLE)==HTML_COLORSTYLE_t::AUTO_DARK) + { + t << "@media (prefers-color-scheme: light) {\n"; + t << " html:not(.light-mode) {\n"; + t << " color-scheme: light;\n\n"; + t << replaceColorMarkers(ResourceMgr::instance().getAsString("lightmode_settings.css")); + t << "}}\n"; + } + else if (Config_getEnum(HTML_COLORSTYLE)==HTML_COLORSTYLE_t::TOGGLE) + { + t << "html.dark-mode {\n"; + t << replaceColorMarkers(ResourceMgr::instance().getAsString("darkmode_settings.css")); + t << "}\n\n"; + } + + t << replaceVariables(ResourceMgr::instance().getAsString("doxygen.css")); +} + void HtmlGenerator::writeStyleSheetFile(TextStream &t) { - t << replaceColorMarkers(substitute(ResourceMgr::instance().getAsString("doxygen.css"),"$doxygenversion",getDoxygenVersion())); + writeDefaultStyleSheet(t); } void HtmlGenerator::writeHeaderFile(TextStream &t, const QCString & /*cssname*/) @@ -1153,18 +1325,16 @@ void HtmlGenerator::startFile(const QCString &name,const QCString &, m_t << "<!-- " << theTranslator->trGeneratedBy() << " Doxygen " << getDoxygenVersion() << " -->\n"; - //bool generateTreeView = Config_getBool(GENERATE_TREEVIEW); bool searchEngine = Config_getBool(SEARCHENGINE); if (searchEngine /*&& !generateTreeView*/) { m_t << "<script type=\"text/javascript\">\n"; m_t << "/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */\n"; m_t << "var searchBox = new SearchBox(\"searchBox\", \"" - << m_relPath<< "search\",'" << theTranslator->trSearch() << "','" << Doxygen::htmlFileExtension << "');\n"; + << m_relPath<< "search/\",'" << Doxygen::htmlFileExtension << "');\n"; m_t << "/* @license-end */\n"; m_t << "</script>\n"; } - //generateDynamicSections(t,relPath); m_sectionCount=0; } @@ -1183,10 +1353,17 @@ void HtmlGenerator::writeSearchInfo(TextStream &t,const QCString &) t << "\n"; t << "<!-- iframe showing the search results (closed by default) -->\n"; t << "<div id=\"MSearchResultsWindow\">\n"; - t << "<iframe src=\"javascript:void(0)\" frameborder=\"0\" \n"; - t << " name=\"MSearchResults\" id=\"MSearchResults\">\n"; - t << "</iframe>\n"; - t << "</div>\n"; + t << "<div id=\"MSearchResults\">\n"; + t << "<div class=\"SRPage\">\n"; + t << "<div id=\"SRIndex\">\n"; + t << "<div id=\"SRResults\"></div>\n"; // here the results will be inserted + t << "<div class=\"SRStatus\" id=\"Loading\">" << theTranslator->trLoading() << "</div>\n"; + t << "<div class=\"SRStatus\" id=\"Searching\">" << theTranslator->trSearching() << "</div>\n"; + t << "<div class=\"SRStatus\" id=\"NoMatches\">" << theTranslator->trNoMatches() << "</div>\n"; + t << "</div>\n"; // SRIndex + t << "</div>\n"; // SRPage + t << "</div>\n"; // MSearchResults + t << "</div>\n"; // MSearchResultsWindow t << "\n"; } } @@ -1261,33 +1438,31 @@ void HtmlGenerator::writeStyleInfo(int part) { //printf("write doxygen.css\n"); startPlainFile("doxygen.css"); - - // alternative, cooler looking titles - //t << "H1 { text-align: center; border-width: thin none thin none;\n"; - //t << " border-style : double; border-color : blue; padding-left : 1em; padding-right : 1em }\n"; - - m_t << replaceColorMarkers(substitute(ResourceMgr::instance().getAsString("doxygen.css"),"$doxygenversion",getDoxygenVersion())); + writeDefaultStyleSheet(m_t); endPlainFile(); Doxygen::indexList->addStyleSheetFile("doxygen.css"); } else // write user defined style sheet { - QCString cssname=Config_getString(HTML_STYLESHEET); - FileInfo cssfi(cssname.str()); - if (!cssfi.exists() || !cssfi.isFile() || !cssfi.isReadable()) - { - err("style sheet %s does not exist or is not readable!", qPrint(Config_getString(HTML_STYLESHEET))); - } - else + QCString cssName=Config_getString(HTML_STYLESHEET); + if (!cssName.startsWith("http:") && !cssName.startsWith("https:")) { - // convert style sheet to string - QCString fileStr = fileToString(cssname); - // write the string into the output dir - startPlainFile(cssfi.fileName().c_str()); - m_t << fileStr; - endPlainFile(); + FileInfo cssfi(cssName.str()); + if (!cssfi.exists() || !cssfi.isFile() || !cssfi.isReadable()) + { + err("style sheet %s does not exist or is not readable!", qPrint(Config_getString(HTML_STYLESHEET))); + } + else + { + // convert style sheet to string + QCString fileStr = fileToString(cssName); + // write the string into the output dir + startPlainFile(cssfi.fileName().c_str()); + m_t << fileStr; + endPlainFile(); + } + Doxygen::indexList->addStyleSheetFile(cssfi.fileName().c_str()); } - Doxygen::indexList->addStyleSheetFile(cssfi.fileName().c_str()); } const StringVector &extraCssFiles = Config_getList(HTML_EXTRA_STYLESHEET); for (const auto &fileName : extraCssFiles) @@ -1306,6 +1481,11 @@ void HtmlGenerator::writeStyleInfo(int part) Doxygen::indexList->addStyleSheetFile("dynsections.js"); + if (Config_getEnum(HTML_COLORSTYLE)==HTML_COLORSTYLE_t::TOGGLE) + { + Doxygen::indexList->addStyleSheetFile("darkmode_toggle.js"); + } + if (Config_getBool(INTERACTIVE_SVG)) { Doxygen::indexList->addStyleSheetFile("svgpan.js"); @@ -1851,24 +2031,22 @@ void HtmlGenerator::endIndexList() void HtmlGenerator::startIndexKey() { - // inserted 'class = ...', 02 jan 2002, jh - m_t << " <tr><td class=\"indexkey\">"; + //m_t << " <tr><td class=\"indexkey\">"; } void HtmlGenerator::endIndexKey() { - m_t << "</td>"; + //m_t << "</td>"; } void HtmlGenerator::startIndexValue(bool) { - // inserted 'class = ...', 02 jan 2002, jh - m_t << "<td class=\"indexvalue\">"; + //m_t << "<td class=\"indexvalue\">"; } void HtmlGenerator::endIndexValue(const QCString &,bool) { - m_t << "</td></tr>\n"; + //m_t << "</td></tr>\n"; } void HtmlGenerator::startMemberDocList() @@ -1887,7 +2065,7 @@ void HtmlGenerator::startMemberDoc( const QCString &clName, const QCString &memN { DBG_HTML(m_t << "<!-- startMemberDoc -->\n";) m_t << "\n<h2 class=\"memtitle\">" - << "<span class=\"permalink\"><a href=\"#" << anchor << "\">◆ </a></span>"; + << "<span class=\"permalink\"><a href=\"#" << anchor << "\">◆ </a></span>"; docify(title); if (memTotal>1) { @@ -2337,9 +2515,9 @@ static bool quickLinkVisible(LayoutNavEntry::Kind kind) case LayoutNavEntry::ClassIndex: return annotatedClasses>0; case LayoutNavEntry::ClassHierarchy: return hierarchyClasses>0; case LayoutNavEntry::ClassMembers: return documentedClassMembers[CMHL_All]>0; - case LayoutNavEntry::Files: return documentedFiles>0; - case LayoutNavEntry::FileList: return documentedFiles>0; - case LayoutNavEntry::FileGlobals: return documentedFileMembers[FMHL_All]>0; + case LayoutNavEntry::Files: return Config_getBool(SHOW_FILES) && documentedFiles>0; + case LayoutNavEntry::FileList: return Config_getBool(SHOW_FILES) && documentedFiles>0; + case LayoutNavEntry::FileGlobals: return Config_getBool(SHOW_FILES) && documentedFileMembers[FMHL_All]>0; case LayoutNavEntry::Examples: return !Doxygen::exampleLinkedMap->empty(); case LayoutNavEntry::Interfaces: return annotatedInterfaces>0; case LayoutNavEntry::InterfaceList: return annotatedInterfaces>0; @@ -2708,7 +2886,7 @@ void HtmlGenerator::writeSearchPage() t << "<script type=\"text/javascript\">\n"; t << "/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */\n"; t << "var searchBox = new SearchBox(\"searchBox\", \"" - << "search\",'" << theTranslator->trSearch() << "','" << Doxygen::htmlFileExtension << "');\n"; + << "search/\",'" << Doxygen::htmlFileExtension << "');\n"; t << "/* @license-end */\n"; t << "</script>\n"; if (!Config_getBool(DISABLE_INDEX)) @@ -2764,13 +2942,17 @@ void HtmlGenerator::writeExternalSearchPage() t << "<script type=\"text/javascript\">\n"; t << "/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */\n"; t << "var searchBox = new SearchBox(\"searchBox\", \"" - << "search\",'" << theTranslator->trSearch() << "','" << Doxygen::htmlFileExtension << "');\n"; + << "search/\",'" << Doxygen::htmlFileExtension << "');\n"; t << "/* @license-end */\n"; t << "</script>\n"; if (!Config_getBool(DISABLE_INDEX)) { writeDefaultQuickLinks(t,TRUE,HLI_Search,QCString(),QCString()); - t << " <input type=\"text\" id=\"MSearchField\" name=\"query\" value=\"\" size=\"20\" accesskey=\"S\" onfocus=\"searchBox.OnSearchFieldFocus(true)\" onblur=\"searchBox.OnSearchFieldFocus(false)\"/>\n"; + if (!Config_getBool(HTML_DYNAMIC_MENUS)) // for dynamic menus, menu.js creates this part + { + t << " <input type=\"text\" id=\"MSearchField\" name=\"query\" value=\"\" placeholder=\"" << theTranslator->trSearch() << + "\" size=\"20\" accesskey=\"S\" onfocus=\"searchBox.OnSearchFieldFocus(true)\" onblur=\"searchBox.OnSearchFieldFocus(false)\"/>\n"; + } t << " </form>\n"; t << " </div><div class=\"right\"></div>\n"; t << " </div>\n"; @@ -3087,3 +3269,10 @@ QCString HtmlGenerator::getMathJaxMacros() { return getConvertLatexMacro(); } + +QCString HtmlGenerator::getNavTreeCss() +{ + ResourceMgr &mgr = ResourceMgr::instance(); + return replaceVariables(mgr.getAsString("navtree.css")); +} + diff --git a/src/htmlgen.h b/src/htmlgen.h index fc2c8aa..6bb2744 100644 --- a/src/htmlgen.h +++ b/src/htmlgen.h @@ -84,6 +84,7 @@ class HtmlGenerator : public OutputGenerator static QCString writeLogoAsString(const QCString &path); static QCString writeSplitBarAsString(const QCString &name,const QCString &relpath); static QCString getMathJaxMacros(); + static QCString getNavTreeCss(); // ---- CodeOutputInterface void codify(const QCString &text) diff --git a/src/htmlhelp.cpp b/src/htmlhelp.cpp index 51cf757..847c740 100644 --- a/src/htmlhelp.cpp +++ b/src/htmlhelp.cpp @@ -316,6 +316,8 @@ class HtmlHelp::Private Private() : index(recoder) {} void createProjectFile(); std::ofstream cts,kts; + QCString prevFile; + QCString prevAnc; bool ctsItemPresent = false; int dc = 0; StringSet indexFiles; @@ -330,13 +332,9 @@ class HtmlHelp::Private * The object has to be \link initialize() initialized\endlink before it can * be used. */ -HtmlHelp::HtmlHelp() : p(std::make_unique<Private>()) -{ -} - -HtmlHelp::~HtmlHelp() -{ -} +HtmlHelp::HtmlHelp() : p(std::make_unique<Private>()) {} +HtmlHelp::~HtmlHelp() = default; +HtmlHelp::HtmlHelp(HtmlHelp &&) = default; /*! This will create a contents file (index.hhc) and a index file (index.hhk) * and write the header of those files. @@ -526,16 +524,6 @@ void HtmlHelp::addContentsItem(bool isDir, bool /* addToNavIndex */, const Definition * /* def */) { - bool binaryTOC = Config_getBool(BINARY_TOC); - // If we're using a binary toc then folders cannot have links. - // Tried this and I didn't see any problems, when not using - // the resetting of file and anchor the TOC works better - // (prev / next button) - //if(Config_getBool(BINARY_TOC) && isDir) - //{ - //file = 0; - //anchor = 0; - //} p->ctsItemPresent = true; int i; for (i=0;i<p->dc;i++) p->cts << " "; p->cts << "<LI><OBJECT type=\"text/sitemap\">"; @@ -552,13 +540,18 @@ void HtmlHelp::addContentsItem(bool isDir, } else { - if (!(binaryTOC && isDir)) + QCString currFile = addHtmlExtensionIfMissing(file); + QCString currAnc = anchor; + p->cts << "<param name=\"Local\" value=\""; + p->cts << currFile; + if (p->prevFile == currFile && p->prevAnc.isEmpty() && currAnc.isEmpty()) { - p->cts << "<param name=\"Local\" value=\""; - p->cts << addHtmlExtensionIfMissing(file); - if (!anchor.isEmpty()) p->cts << "#" << anchor; - p->cts << "\">"; + currAnc = "top"; } + if (!currAnc.isEmpty()) p->cts << "#" << currAnc; + p->cts << "\">"; + p->prevFile = currFile; + p->prevAnc = currAnc; } } p->cts << "<param name=\"ImageNumber\" value=\""; diff --git a/src/htmlhelp.h b/src/htmlhelp.h index a2f3683..28e2682 100644 --- a/src/htmlhelp.h +++ b/src/htmlhelp.h @@ -20,16 +20,17 @@ #define HTMLHELP_H #include <memory> -#include "index.h" +#include "qcstring.h" class Definition; +class MemberDef; /** A class that generated the HTML Help specific files. * * These files can be used with the Microsoft HTML Help workshop * to generate compressed HTML files (.chm). */ -class HtmlHelp : public IndexIntf +class HtmlHelp { /*! used in imageNumber param of HTMLHelp::addContentsItem() function to specify document icon in tree view. @@ -61,6 +62,7 @@ class HtmlHelp : public IndexIntf //static HtmlHelp *getInstance(); HtmlHelp(); ~HtmlHelp(); + HtmlHelp(HtmlHelp &&); void initialize(); void finalize(); void incContentsDepth(); diff --git a/src/image.cpp b/src/image.cpp index 06985df..2589646 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -158,54 +158,13 @@ static Color palette[] = { 0x00, 0x00, 0x00, 0xff }, { 0xff, 0xff, 0xc0, 0xff }, { 0x9f, 0x9f, 0x60, 0xff }, - { 0x90, 0x00, 0x00, 0xff }, - { 0x00, 0x90, 0x00, 0xff }, - { 0x00, 0x00, 0x90, 0xff }, - { 0xc0, 0xc0, 0xc0, 0xff } -}; - -// for alpha we use x^(1/1.3) -static Color palette2[] = -{ - { 0x00, 0x00, 0x00, 0x00 }, - { 0x00, 0x00, 0x00, 0x2e }, - { 0x00, 0x00, 0x00, 0x48 }, - { 0x00, 0x00, 0x00, 0x5d }, - { 0x00, 0x00, 0x00, 0x6f }, - { 0x00, 0x00, 0x00, 0x80 }, - { 0x00, 0x00, 0x00, 0x8f }, - { 0x00, 0x00, 0x00, 0x9e }, - { 0x00, 0x00, 0x00, 0xac }, - { 0x00, 0x00, 0x00, 0xb9 }, - { 0x00, 0x00, 0x00, 0xc5 }, - { 0x00, 0x00, 0x00, 0xd2 }, - { 0x00, 0x00, 0x00, 0xdd }, - { 0x00, 0x00, 0x00, 0xe9 }, - { 0x00, 0x00, 0x00, 0xf4 }, - { 0x00, 0x00, 0x00, 0xff } -}; - -static Color palette3[] = -{ - { 0xff, 0xff, 0xff, 0xff }, - { 0xe0, 0xe0, 0xe0, 0xff }, - { 0xd0, 0xd0, 0xd0, 0xff }, + { 0xa7, 0x38, 0x30, 0xff }, + { 0x29, 0x70, 0x18, 0xff }, + { 0x97, 0xCC, 0xE8, 0xff }, { 0xc0, 0xc0, 0xc0, 0xff }, - { 0xb0, 0xb0, 0xb0, 0xff }, - { 0xa0, 0xa0, 0xa0, 0xff }, - { 0x90, 0x90, 0x90, 0xff }, - { 0x80, 0x80, 0x80, 0xff }, - { 0x70, 0x70, 0x70, 0xff }, - { 0x60, 0x60, 0x60, 0xff }, - { 0x50, 0x50, 0x50, 0xff }, - { 0x40, 0x40, 0x40, 0xff }, - { 0x30, 0x30, 0x30, 0xff }, - { 0x20, 0x20, 0x20, 0xff }, - { 0x10, 0x10, 0x10, 0xff }, - { 0x00, 0x00, 0x00, 0xff } + { 0xff, 0xff, 0xff, 0xff } }; - Image::Image(uint w,uint h) { int hue = Config_getInt(HTML_COLORSTYLE_HUE); @@ -379,17 +338,14 @@ void Image::fillRect(uint x,uint y,uint width,uint height,uchar colIndex,uint ma setPixel(xp,yp,colIndex); } -bool Image::save(const QCString &fileName,int mode) +bool Image::save(const QCString &fileName) { - bool useTransparency = Config_getBool(FORMULA_TRANSPARENT); uchar* buffer; size_t bufferSize; LodePNG_Encoder encoder; LodePNG_Encoder_init(&encoder); - uint numCols = mode==0 ? 8 : 16; - Color *pPal = mode==0 ? palette : - useTransparency ? palette2 : - palette3 ; + uint numCols = 9; + Color *pPal = palette; uint i; for (i=0;i<numCols;i++,pPal++) { diff --git a/src/image.h b/src/image.h index be523c8..e024bb0 100644 --- a/src/image.h +++ b/src/image.h @@ -40,7 +40,7 @@ class Image void drawVertArrow(uint x,uint ys,uint ye,uchar colIndex,uint mask); void drawRect(uint x,uint y,uint width,uint height,uchar colIndex,uint mask); void fillRect(uint x,uint y,uint width,uint height,uchar colIndex,uint mask); - bool save(const QCString &fileName,int mode=0); + bool save(const QCString &fileName); friend uint stringLength(const QCString &s); uint width() const { return m_width; } uint height() const { return m_height; } diff --git a/src/index.cpp b/src/index.cpp index a0ad1b6..b5a9588 100644 --- a/src/index.cpp +++ b/src/index.cpp @@ -25,6 +25,7 @@ #include "message.h" #include "index.h" +#include "indexlist.h" #include "doxygen.h" #include "config.h" #include "filedef.h" @@ -4271,7 +4272,7 @@ static void writeConceptIndex(OutputList &ol) static void writeUserGroupStubPage(OutputList &ol,LayoutNavEntry *lne) { - if (lne->baseFile().left(9)=="usergroup") + if (lne->baseFile().startsWith("usergroup")) { ol.pushGeneratorState(); ol.disableAllBut(OutputGenerator::Html); @@ -4701,6 +4702,10 @@ static std::vector<bool> indexWritten; static void writeIndexHierarchyEntries(OutputList &ol,const LayoutNavEntryList &entries) { + auto isRef = [](const QCString &s) + { + return s.startsWith("@ref") || s.startsWith("\\ref"); + }; bool sliceOpt = Config_getBool(OPTIMIZE_OUTPUT_SLICE); for (const auto &lne : entries) { @@ -4933,8 +4938,8 @@ static void writeIndexHierarchyEntries(OutputList &ol,const LayoutNavEntryList & { url.prepend("^"); // prepend ^ to absolute URL } - bool isRef = lne->baseFile().left(4)=="@ref" || lne->baseFile().left(4)=="\\ref"; - Doxygen::indexList->addContentsItem(TRUE,lne->title(),QCString(),url,QCString(),FALSE,isRef || isRelative); + Doxygen::indexList->addContentsItem(TRUE,lne->title(),QCString(), + url,QCString(),FALSE,isRef(lne->baseFile()) || isRelative); } break; case LayoutNavEntry::UserGroup: @@ -4954,8 +4959,8 @@ static void writeIndexHierarchyEntries(OutputList &ol,const LayoutNavEntryList & { url.prepend("^"); // prepend ^ to absolute URL } - bool isRef = lne->baseFile().left(4)=="@ref" || lne->baseFile().left(4)=="\\ref"; - Doxygen::indexList->addContentsItem(TRUE,lne->title(),QCString(),url,QCString(),FALSE,isRef || isRelative); + Doxygen::indexList->addContentsItem(TRUE,lne->title(),QCString(), + url,QCString(),FALSE,isRef(lne->baseFile()) || isRelative); } } else diff --git a/src/index.h b/src/index.h index 30d6e5f..2b07c73 100644 --- a/src/index.h +++ b/src/index.h @@ -16,110 +16,13 @@ #ifndef INDEX_H #define INDEX_H -#include <utility> -#include <vector> -#include <memory> -#include <mutex> - #include "qcstring.h" class Definition; +class OutputList; class DefinitionMutable; class NamespaceDef; class MemberDef; -class OutputList; - -/** \brief Abstract interface for index generators. */ -class IndexIntf -{ - public: - virtual ~IndexIntf() = default; - virtual void initialize() = 0; - virtual void finalize() = 0; - virtual void incContentsDepth() = 0; - virtual void decContentsDepth() = 0; - virtual void addContentsItem(bool isDir, const QCString &name, const QCString &ref, - const QCString &file, const QCString &anchor, bool separateIndex, - bool addToNavIndex,const Definition *def) = 0; - virtual void addIndexItem(const Definition *context,const MemberDef *md, - const QCString §ionAnchor,const QCString &title) = 0; - virtual void addIndexFile(const QCString &name) = 0; - virtual void addImageFile(const QCString &name) = 0; - virtual void addStyleSheetFile(const QCString &name) = 0; -}; - -/** \brief A list of index interfaces. - * - * This class itself implements all methods of IndexIntf and - * just forwards the calls to all items in the list (composite design pattern). - */ -class IndexList : public IndexIntf -{ - private: - std::vector< std::unique_ptr<IndexIntf> > m_intfs; - - // For each index format we forward the method call. - // We use C++11 variadic templates and perfect forwarding to implement foreach() generically, - // and split the types of the methods from the arguments passed to allow implicit conversions. - template<class... Ts,class... As> - void foreach(void (IndexIntf::*methodPtr)(Ts...),As&&... args) - { - for (const auto &intf : m_intfs) - { - (intf.get()->*methodPtr)(std::forward<As>(args)...); - } - } - // For each version with locking - template<class... Ts,class... As> - void foreach_locked(void (IndexIntf::*methodPtr)(Ts...),As&&... args) - { - std::lock_guard<std::mutex> lock(m_mutex); - foreach(methodPtr,std::forward<As>(args)...); - } - - public: - /** Creates a list of indexes */ - IndexList() { m_enabled=TRUE; } - - /** Add an index generator to the list, using a syntax similar to std::make_unique<T>() */ - template<class T,class... As> - void addIndex(As&&... args) - { m_intfs.push_back(std::make_unique<T>(std::forward<As>(args)...)); } - - void disable() - { m_enabled = FALSE; } - void enable() - { m_enabled = TRUE; } - bool isEnabled() const - { return m_enabled; } - - // IndexIntf implementation - void initialize() - { foreach(&IndexIntf::initialize); } - void finalize() - { foreach(&IndexIntf::finalize); } - void incContentsDepth() - { if (m_enabled) foreach_locked(&IndexIntf::incContentsDepth); } - void decContentsDepth() - { if (m_enabled) foreach_locked(&IndexIntf::decContentsDepth); } - void addContentsItem(bool isDir, const QCString &name, const QCString &ref, - const QCString &file, const QCString &anchor,bool separateIndex=FALSE,bool addToNavIndex=FALSE, - const Definition *def=0) - { if (m_enabled) foreach_locked(&IndexIntf::addContentsItem,isDir,name,ref,file,anchor,separateIndex,addToNavIndex,def); } - void addIndexItem(const Definition *context,const MemberDef *md,const QCString §ionAnchor=QCString(),const QCString &title=QCString()) - { if (m_enabled) foreach_locked(&IndexIntf::addIndexItem,context,md,sectionAnchor,title); } - void addIndexFile(const QCString &name) - { if (m_enabled) foreach_locked(&IndexIntf::addIndexFile,name); } - void addImageFile(const QCString &name) - { if (m_enabled) foreach_locked(&IndexIntf::addImageFile,name); } - void addStyleSheetFile(const QCString &name) - { if (m_enabled) foreach_locked(&IndexIntf::addStyleSheetFile,name); } - - private: - bool m_enabled; - std::mutex m_mutex; -}; - enum IndexSections { diff --git a/src/indexlist.h b/src/indexlist.h new file mode 100644 index 0000000..50385db --- /dev/null +++ b/src/indexlist.h @@ -0,0 +1,134 @@ +/****************************************************************************** + * + * Copyright (C) 1997-2021 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + * + */ + +#ifndef INDEXLIST_H +#define INDEXLIST_H + +#include <utility> +#include <vector> +#include <memory> +#include <mutex> +#include <variant> + +#include "qcstring.h" +#include "dispatcher.h" + +#include "docsets.h" +#include "eclipsehelp.h" +#include "ftvhelp.h" +#include "htmlhelp.h" +#include "qhp.h" + +class Definition; +class MemberDef; +class OutputList; + +/** Namespace containing typed wrappers to refer to member functions for each method of the indices using a fixed `method` static variable. + * Based on this idea https://stackoverflow.com/a/71357544/784672 + */ +namespace IndexIntf +{ + template <class T> struct initialize { static constexpr auto method = &T::initialize; }; + template <class T> struct finalize { static constexpr auto method = &T::finalize; }; + template <class T> struct incContentsDepth { static constexpr auto method = &T::incContentsDepth; }; + template <class T> struct decContentsDepth { static constexpr auto method = &T::decContentsDepth; }; + template <class T> struct addContentsItem { static constexpr auto method = &T::addContentsItem; }; + template <class T> struct addIndexItem { static constexpr auto method = &T::addIndexItem; }; + template <class T> struct addIndexFile { static constexpr auto method = &T::addIndexFile; }; + template <class T> struct addImageFile { static constexpr auto method = &T::addImageFile; }; + template <class T> struct addStyleSheetFile { static constexpr auto method = &T::addStyleSheetFile; }; +} + +/** \brief A list of index interfaces. + * + * This class itself implements all methods of IndexIntf and + * just forwards the calls to all items in the list (composite design pattern). + */ +class IndexList +{ + using IndexVariant = std::variant<DocSets, EclipseHelp, FTVHelp, HtmlHelp, Qhp>; + + template<template <class> class IndexT, class... As> + void foreach(As&&... args) + { + for (auto &v : m_indices) + { + dispatch_call<IndexT>(v,std::forward<As>(args)...); + } + } + template<template <class> class IndexT, class... As> + void foreach_locked(As&&... args) + { + std::lock_guard<std::mutex> lock(m_mutex); + for (auto &v : m_indices) + { + dispatch_call<IndexT>(v,std::forward<As>(args)...); + } + } + + public: + /** disable the indices */ + void disable() + { m_enabled = FALSE; } + + /** enable the indices */ + void enable() + { m_enabled = TRUE; } + + /** returns true iff the indices are enabled */ + bool isEnabled() const + { return m_enabled; } + + /** Add an index generator to the list, using a syntax similar to std::make_unique<T>() */ + template<class T,class... As> + void addIndex(As&&... args) + { m_indices.push_back(std::move(T{std::forward<As>(args)...})); } + + void initialize() + { foreach<IndexIntf::initialize>(); } + + void finalize() + { foreach<IndexIntf::finalize>(); } + + void incContentsDepth() + { if (m_enabled) foreach_locked<IndexIntf::incContentsDepth>(); } + + void decContentsDepth() + { if (m_enabled) foreach_locked<IndexIntf::decContentsDepth>(); } + + void addContentsItem(bool isDir, const QCString &name, const QCString &ref, + const QCString &file, const QCString &anchor,bool separateIndex=FALSE,bool addToNavIndex=FALSE, + const Definition *def=0) + { if (m_enabled) foreach_locked<IndexIntf::addContentsItem>(isDir,name,ref,file,anchor,separateIndex,addToNavIndex,def); } + + void addIndexItem(const Definition *context,const MemberDef *md,const QCString §ionAnchor=QCString(),const QCString &title=QCString()) + { if (m_enabled) foreach_locked<IndexIntf::addIndexItem>(context,md,sectionAnchor,title); } + + void addIndexFile(const QCString &name) + { if (m_enabled) foreach_locked<IndexIntf::addIndexFile>(name); } + + void addImageFile(const QCString &name) + { if (m_enabled) foreach_locked<IndexIntf::addImageFile>(name); } + + void addStyleSheetFile(const QCString &name) + { if (m_enabled) foreach_locked<IndexIntf::addStyleSheetFile>(name); } + + private: + bool m_enabled = true; + std::mutex m_mutex; + std::vector<IndexVariant> m_indices; +}; + +#endif // INDEXLIST_H diff --git a/src/latexdocvisitor.cpp b/src/latexdocvisitor.cpp index 2b48c3c..feb4f93 100644 --- a/src/latexdocvisitor.cpp +++ b/src/latexdocvisitor.cpp @@ -378,11 +378,8 @@ void LatexDocVisitor::operator()(const DocStyleChange &s) break; case DocStyleChange::Div: /* HTML only */ break; case DocStyleChange::Span: /* HTML only */ break; - case DocStyleChange::Details: /* emulation of the <details> tag */ - if (!s.enable()) m_t << "\n\n"; - break; case DocStyleChange::Summary: /* emulation of the <summary> tag inside a <details> tag */ - if (s.enable()) m_t << "{\\bfseries{"; else m_t << "}}"; + if (s.enable()) m_t << "{\\bfseries{"; else m_t << "}}\\newline"; break; } } @@ -1412,6 +1409,14 @@ void LatexDocVisitor::operator()(const DocHRef &href) m_t << "}}"; } +void LatexDocVisitor::operator()(const DocHtmlDetails &d) +{ + if (m_hide) return; + m_t << "\n\n"; + visitChildren(d); + m_t << "\n\n"; +} + void LatexDocVisitor::operator()(const DocHtmlHeader &header) { if (m_hide) return; @@ -1426,7 +1431,7 @@ void LatexDocVisitor::operator()(const DocImage &img) { if (m_hide) return; QCString gfxName = img.name(); - if (gfxName.right(4)==".eps" || gfxName.right(4)==".pdf") + if (gfxName.endsWith(".eps") || gfxName.endsWith(".pdf")) { gfxName=gfxName.left(gfxName.length()-4); } diff --git a/src/latexdocvisitor.h b/src/latexdocvisitor.h index 8ce8524..fcd7f12 100644 --- a/src/latexdocvisitor.h +++ b/src/latexdocvisitor.h @@ -80,6 +80,7 @@ class LatexDocVisitor : public DocVisitor void operator()(const DocHtmlCell &); void operator()(const DocInternal &); void operator()(const DocHRef &); + void operator()(const DocHtmlDetails &); void operator()(const DocHtmlHeader &); void operator()(const DocImage &); void operator()(const DocDotFile &); diff --git a/src/latexgen.cpp b/src/latexgen.cpp index 33087a0..c5f436c 100644 --- a/src/latexgen.cpp +++ b/src/latexgen.cpp @@ -337,7 +337,7 @@ static void writeLatexMakefile() } t << "\techo \"Rerunning latex....\"\n" << "\t$(LATEX_CMD) $(MANUAL_FILE).tex\n" - << "\tlatex_count=%(LATEX_COUNT) ; \\\n" + << "\tlatex_count=$(LATEX_COUNT) ; \\\n" << "\twhile egrep -s 'Rerun (LaTeX|to get cross-references right|to get bibliographical references right)' $(MANUAL_FILE).log && [ $$latex_count -gt 0 ] ;\\\n" << "\t do \\\n" << "\t echo \"Rerunning latex....\" ;\\\n" @@ -565,7 +565,7 @@ void LatexGenerator::startFile(const QCString &name,const QCString &,const QCStr #endif QCString fileName=name; m_relPath = relativePathToRoot(fileName); - if (fileName.right(4)!=".tex" && fileName.right(4)!=".sty") fileName+=".tex"; + if (!fileName.endsWith(".tex") && !fileName.endsWith(".sty")) fileName+=".tex"; startPlainFile(fileName); m_codeGen.setRelativePath(m_relPath); m_codeGen.setSourceFileName(stripPath(fileName)); @@ -693,10 +693,12 @@ static QCString substituteLatexKeywords(const QCString &str, copyFile(formulaMacrofile,Config_getString(LATEX_OUTPUT) + "/" + stripMacroFile); } + QCString projectNumber = Config_getString(PROJECT_NUMBER); + // first substitute generic keywords QCString result = substituteKeywords(str,title, convertToLaTeX(Config_getString(PROJECT_NAME)), - convertToLaTeX(Config_getString(PROJECT_NUMBER)), + convertToLaTeX(projectNumber), convertToLaTeX(Config_getString(PROJECT_BRIEF))); // additional LaTeX only keywords @@ -726,6 +728,7 @@ static QCString substituteLatexKeywords(const QCString &str, result = selectBlock(result,"LATEX_BATCHMODE",latexBatchmode,OutputGenerator::Latex); result = selectBlock(result,"LATEX_FONTENC",!latexFontenc.isEmpty(),OutputGenerator::Latex); result = selectBlock(result,"FORMULA_MACROFILE",!formulaMacrofile.isEmpty(),OutputGenerator::Latex); + result = selectBlock(result,"PROJECT_NUMBER",!projectNumber.isEmpty(),OutputGenerator::Latex); result = removeEmptyLines(result); diff --git a/src/layout.cpp b/src/layout.cpp index ce7110b..ad8393a 100644 --- a/src/layout.cpp +++ b/src/layout.cpp @@ -139,11 +139,11 @@ QCString LayoutNavEntry::url() const { QCString url = baseFile().stripWhiteSpace(); if ((kind()!=LayoutNavEntry::User && kind()!=LayoutNavEntry::UserGroup) || - (kind()==LayoutNavEntry::UserGroup && url.left(9)=="usergroup")) + (kind()==LayoutNavEntry::UserGroup && url.startsWith("usergroup"))) { url = addHtmlExtensionIfMissing(url); } - else if (url.left(5)=="@ref " || url.left(5)=="\\ref ") + else if (url.startsWith("@ref ") || url.startsWith("\\ref ")) { const Definition *d = 0; QCString anchor; diff --git a/src/mandocvisitor.cpp b/src/mandocvisitor.cpp index 3995007..a1ae522 100644 --- a/src/mandocvisitor.cpp +++ b/src/mandocvisitor.cpp @@ -188,16 +188,8 @@ void ManDocVisitor::operator()(const DocStyleChange &s) break; case DocStyleChange::Div: /* HTML only */ break; case DocStyleChange::Span: /* HTML only */ break; - case DocStyleChange::Details: /* emulation of the <details> tag */ - if (!s.enable()) - { - if (!m_firstCol) m_t << "\n"; - m_t << ".PP\n"; - m_firstCol=TRUE; - } - break; case DocStyleChange::Summary: /* emulation of the <summary> tag inside a <details> tag */ - if (s.enable()) m_t << "\\fB"; else m_t << "\\fP"; + if (s.enable()) m_t << "\\fB"; else m_t << "\\fP\n.PP\n"; m_firstCol=FALSE; break; } @@ -784,6 +776,22 @@ void ManDocVisitor::operator()(const DocHRef &href) m_t << "\\fP"; } +void ManDocVisitor::operator()(const DocHtmlDetails &d) +{ + if (m_hide) return; + if (!m_firstCol) + { + m_t << "\n"; + m_t << ".PP\n"; + } + m_t << ".RS 4\n"; // TODO: add support for nested detailes sections + visitChildren(d); + if (!m_firstCol) m_t << "\n"; + m_t << ".RE\n"; + m_t << ".PP\n"; + m_firstCol=TRUE; +} + void ManDocVisitor::operator()(const DocHtmlHeader &header) { if (m_hide) return; diff --git a/src/mandocvisitor.h b/src/mandocvisitor.h index 96910bd..43fd19f 100644 --- a/src/mandocvisitor.h +++ b/src/mandocvisitor.h @@ -81,6 +81,7 @@ class ManDocVisitor : public DocVisitor void operator()(const DocHtmlCell &); void operator()(const DocInternal &); void operator()(const DocHRef &); + void operator()(const DocHtmlDetails &); void operator()(const DocHtmlHeader &); void operator()(const DocImage &); void operator()(const DocDotFile &); diff --git a/src/mangen.cpp b/src/mangen.cpp index 27484ce..7e69459 100644 --- a/src/mangen.cpp +++ b/src/mangen.cpp @@ -627,7 +627,7 @@ void ManGenerator::endSection(const QCString &,SectionType type) } else { - m_t << "\n"; + m_t << "\n.PP\n"; m_firstCol=TRUE; m_paragraph=FALSE; m_inHeader=FALSE; diff --git a/src/markdown.cpp b/src/markdown.cpp index 350863b..c2c0585 100644 --- a/src/markdown.cpp +++ b/src/markdown.cpp @@ -311,6 +311,8 @@ static QCString escapeSpecialChars(const QCString &s) case '\\': if (!insideQuote) { growBuf.addChar('\\'); } growBuf.addChar('\\'); break; case '@': if (!insideQuote) { growBuf.addChar('\\'); } growBuf.addChar('@'); break; case '#': if (!insideQuote) { growBuf.addChar('\\'); } growBuf.addChar('#'); break; + case '$': if (!insideQuote) { growBuf.addChar('\\'); } growBuf.addChar('$'); break; + case '&': if (!insideQuote) { growBuf.addChar('\\'); } growBuf.addChar('&'); break; default: growBuf.addChar(c); break; } pc=c; @@ -432,8 +434,10 @@ QCString Markdown::isBlockCommand(const char *data,int offset,int size) { { "dot", getEndBlock }, { "code", getEndCode }, + { "icode", getEndBlock }, { "msc", getEndBlock }, { "verbatim", getEndBlock }, + { "iverbatim", getEndBlock }, { "iliteral", getEndBlock }, { "latexonly", getEndBlock }, { "htmlonly", getEndBlock }, @@ -2765,7 +2769,7 @@ int Markdown::writeCodeBlock(const char *data,int size,int refIndent) int i=0,end; //printf("writeCodeBlock: data={%s}\n",qPrint(QCString(data).left(size))); // no need for \ilinebr here as the previous line was empty and was skipped - m_out.addStr("@verbatim\n"); + m_out.addStr("@iverbatim\n"); int emptyLines=0; while (i<size) { @@ -2799,7 +2803,7 @@ int Markdown::writeCodeBlock(const char *data,int size,int refIndent) break; } } - m_out.addStr("@endverbatim\\ilinebr "); + m_out.addStr("@endiverbatim\\ilinebr "); while (emptyLines>0) // write skipped empty lines { // add empty line @@ -2899,13 +2903,13 @@ void Markdown::writeFencedCodeBlock(const char *data,const char *lng, blockStart--; blockEnd--; } - m_out.addStr("@code"); + m_out.addStr("@icode"); if (!lang.isEmpty()) { m_out.addStr("{"+lang+"}"); } addStrEscapeUtf8Nbsp(data+blockStart,blockEnd-blockStart); - m_out.addStr("@endcode"); + m_out.addStr("@endicode"); } QCString Markdown::processQuotations(const QCString &s,int refIndent) diff --git a/src/memberdef.cpp b/src/memberdef.cpp index 9e116b9..5456d97 100644 --- a/src/memberdef.cpp +++ b/src/memberdef.cpp @@ -320,7 +320,7 @@ class MemberDefImpl : public DefinitionMixin<MemberDefMutable> virtual void writeMemberDocSimple(OutputList &ol,const Definition *container) const; virtual void writeEnumDeclaration(OutputList &typeDecl, const ClassDef *cd,const NamespaceDef *nd,const FileDef *fd,const GroupDef *gd) const; - virtual void writeTagFile(TextStream &) const; + virtual void writeTagFile(TextStream &,bool useQualifiedName) const; virtual void warnIfUndocumented() const; virtual void warnIfUndocumentedParams() const; virtual bool visibleInIndex() const; @@ -1353,7 +1353,7 @@ void MemberDefImpl::IMPL::init(Definition *d, hasDocumentedParams = FALSE; hasDocumentedReturnType = FALSE; docProvider = 0; - isDMember = d->getDefFileName().right(2).lower()==".d"; + isDMember = d->getDefFileName().lower().endsWith(".d"); } @@ -1841,8 +1841,6 @@ ClassDef *MemberDefImpl::getClassDefOfAnonymousType() const cname=getNamespaceDef()->name(); } QCString ltype(m_impl->type); - // strip 'static' keyword from ltype - //if (ltype.left(7)=="static ") ltype=ltype.right(ltype.length()-7); // strip 'friend' keyword from ltype ltype.stripPrefix("friend "); @@ -3708,7 +3706,7 @@ void MemberDefImpl::writeDocumentation(const MemberList *ml, static QCString simplifyTypeForTable(const QCString &s) { QCString ts=removeAnonymousScopes(s); - if (ts.right(2)=="::") ts = ts.left(ts.length()-2); + if (ts.endsWith("::")) ts = ts.left(ts.length()-2); static const reg::Ex re1(R"(\a\w*::)"); // non-template version static const reg::Ex re2(R"(\a\w*<[^>]*>::)"); // template version reg::Match match; @@ -3801,6 +3799,11 @@ void MemberDefImpl::writeMemberDocSimple(OutputList &ol, const Definition *conta { linkifyText(TextGeneratorOLImpl(ol),getOuterScope(),getBodyDef(),this,m_impl->bitfields); } + if (hasOneLineInitializer() && !isDefine()) + { + ol.writeString(" "); + linkifyText(TextGeneratorOLImpl(ol),getOuterScope(),getBodyDef(),this,m_impl->initializer.simplifyWhiteSpace()); + } ol.endInlineMemberName(); ol.startInlineMemberDoc(); @@ -4003,14 +4006,14 @@ void MemberDefImpl::warnIfUndocumentedParams() const bool isFortran = getLanguage()==SrcLangExt_Fortran; bool isFortranSubroutine = isFortran && returnType.find("subroutine")!=-1; - bool isVoidReturn = (returnType=="void") || (returnType.right(5)==" void"); + bool isVoidReturn = (returnType=="void") || (returnType.endsWith(" void")); if (!isVoidReturn && returnType == "auto") { const ArgumentList &defArgList=isDocsForDefinition() ? argumentList() : declArgumentList(); if (!defArgList.trailingReturnType().isEmpty()) { QCString strippedTrailingReturn = stripTrailingReturn(defArgList.trailingReturnType()); - isVoidReturn = (strippedTrailingReturn=="void") || (strippedTrailingReturn.right(5)==" void"); + isVoidReturn = (strippedTrailingReturn=="void") || (strippedTrailingReturn.endsWith(" void")); } } if (!Config_getBool(EXTRACT_ALL) && @@ -4182,7 +4185,7 @@ MemberDefMutable *MemberDefImpl::createTemplateInstanceMember( } QCString methodName=name(); - if (methodName.left(9)=="operator ") // conversion operator + if (methodName.startsWith("operator ")) // conversion operator { methodName=substituteTemplateArgumentsInString(methodName,formalArgs,actualArgs); } @@ -4325,7 +4328,7 @@ Specifier MemberDefImpl::virtualness(int count) const return v; } -void MemberDefImpl::writeTagFile(TextStream &tagFile) const +void MemberDefImpl::writeTagFile(TextStream &tagFile,bool useQualifiedName) const { if (!isLinkableInProject()) return; tagFile << " <member kind=\""; @@ -4370,7 +4373,7 @@ void MemberDefImpl::writeTagFile(TextStream &tagFile) const { tagFile << " <type>" << convertToXML(typeString()) << "</type>\n"; } - tagFile << " <name>" << convertToXML(name()) << "</name>\n"; + tagFile << " <name>" << convertToXML(useQualifiedName ? qualifiedName() : name()) << "</name>\n"; tagFile << " <anchorfile>" << addHtmlExtensionIfMissing(getOutputFileBase()) << "</anchorfile>\n"; tagFile << " <anchor>" << convertToXML(anchor()) << "</anchor>\n"; QCString idStr = id(); diff --git a/src/memberdef.h b/src/memberdef.h index 9b6e30d..d7e9751 100644 --- a/src/memberdef.h +++ b/src/memberdef.h @@ -417,7 +417,7 @@ class MemberDefMutable : public DefinitionMutable, public MemberDef bool inGroup,bool showEnumValues=FALSE,bool showInline=FALSE) const = 0; virtual void writeMemberDocSimple(OutputList &ol,const Definition *container) const = 0; - virtual void writeTagFile(TextStream &) const = 0; + virtual void writeTagFile(TextStream &,bool useQualifiedName) const = 0; virtual void writeLink(OutputList &ol, const ClassDef *cd,const NamespaceDef *nd,const FileDef *fd,const GroupDef *gd, bool onlyText=FALSE) const = 0; diff --git a/src/membergroup.cpp b/src/membergroup.cpp index 809308a..28521d0 100644 --- a/src/membergroup.cpp +++ b/src/membergroup.cpp @@ -270,9 +270,9 @@ void MemberGroup::setRefItems(const RefItemVector &sli) m_xrefListItems.insert(m_xrefListItems.end(), sli.cbegin(), sli.cend()); } -void MemberGroup::writeTagFile(TextStream &tagFile) +void MemberGroup::writeTagFile(TextStream &tagFile,bool qualifiedName) { - memberList->writeTagFile(tagFile); + memberList->writeTagFile(tagFile,qualifiedName); } //-------------------------------------------------------------------------- diff --git a/src/membergroup.h b/src/membergroup.h index 5d1c78e..5f51d09 100644 --- a/src/membergroup.h +++ b/src/membergroup.h @@ -59,7 +59,7 @@ class MemberGroup const Definition *container,bool showEnumValues,bool showInline) const; void writeDocumentationPage(OutputList &ol,const QCString &scopeName, const DefinitionMutable *container) const; - void writeTagFile(TextStream &); + void writeTagFile(TextStream &,bool qualifiedName=false); void addGroupedInheritedMembers(OutputList &ol,const ClassDef *cd, MemberListType lt, const ClassDef *inheritedFrom,const QCString &inheritId) const; diff --git a/src/memberlist.cpp b/src/memberlist.cpp index 4a062e5..8632ddf 100644 --- a/src/memberlist.cpp +++ b/src/memberlist.cpp @@ -937,7 +937,7 @@ QCString MemberList::listTypeAsString(MemberListType type) return ""; } -void MemberList::writeTagFile(TextStream &tagFile) +void MemberList::writeTagFile(TextStream &tagFile,bool useQualifiedName) { for (const auto &imd : m_members) { @@ -946,7 +946,7 @@ void MemberList::writeTagFile(TextStream &tagFile) { if (md->getLanguage()!=SrcLangExt_VHDL) { - md->writeTagFile(tagFile); + md->writeTagFile(tagFile,useQualifiedName); if (md->memberType()==MemberType_Enumeration && !md->isStrong()) { for (const auto &ivmd : md->enumFieldList()) @@ -954,7 +954,7 @@ void MemberList::writeTagFile(TextStream &tagFile) MemberDefMutable *vmd = toMemberDefMutable(ivmd); if (vmd) { - vmd->writeTagFile(tagFile); + vmd->writeTagFile(tagFile,useQualifiedName); } } } @@ -967,7 +967,7 @@ void MemberList::writeTagFile(TextStream &tagFile) } for (const auto &mg : m_memberGroupRefList) { - mg->writeTagFile(tagFile); + mg->writeTagFile(tagFile,useQualifiedName); } } diff --git a/src/memberlist.h b/src/memberlist.h index 2f207c6..2cf265c 100644 --- a/src/memberlist.h +++ b/src/memberlist.h @@ -109,7 +109,7 @@ class MemberList : public MemberVector void writeSimpleDocumentation(OutputList &ol,const Definition *container) const; void writeDocumentationPage(OutputList &ol, const QCString &scopeName, const DefinitionMutable *container) const; - void writeTagFile(TextStream &); + void writeTagFile(TextStream &,bool useQualifiedName=false); bool declVisible() const; void addMemberGroup(MemberGroup *mg); void addListReferences(Definition *def); diff --git a/src/msc.cpp b/src/msc.cpp index b05edfd..8a99e95 100644 --- a/src/msc.cpp +++ b/src/msc.cpp @@ -22,7 +22,7 @@ #include "docparser.h" #include "docnode.h" #include "doxygen.h" -#include "index.h" +#include "indexlist.h" #include "util.h" #include "mscgen_api.h" #include "dir.h" diff --git a/src/outputlist.h b/src/outputlist.h index ae143f8..7023019 100644 --- a/src/outputlist.h +++ b/src/outputlist.h @@ -20,7 +20,6 @@ #include <vector> #include <memory> -#include "index.h" // for IndexSections #include "outputgen.h" #include "searchindex.h" // for SIDataCollection #include "doxygen.h" diff --git a/src/perlmodgen.cpp b/src/perlmodgen.cpp index 8100d30..f413d1a 100644 --- a/src/perlmodgen.cpp +++ b/src/perlmodgen.cpp @@ -336,6 +336,7 @@ class PerlModDocVisitor : public DocVisitor void operator()(const DocHtmlCaption &); void operator()(const DocInternal &); void operator()(const DocHRef &); + void operator()(const DocHtmlDetails &); void operator()(const DocHtmlHeader &); void operator()(const DocImage &); void operator()(const DocDotFile &); @@ -625,9 +626,6 @@ void PerlModDocVisitor::operator()(const DocStyleChange &s) case DocStyleChange::Preformatted: style = "preformatted"; break; case DocStyleChange::Div: style = "div"; break; case DocStyleChange::Span: style = "span"; break; - case DocStyleChange::Details: /* emulation of the <details> tag */ - style = "details"; - break; case DocStyleChange::Summary: /* emulation of the <summary> tag inside a <details> tag */ style = "summary"; break; @@ -1033,6 +1031,15 @@ void PerlModDocVisitor::operator()(const DocHRef &href) #endif } +void PerlModDocVisitor::operator()(const DocHtmlDetails &details) +{ + openItem("details"); + openSubBlock("content"); + visitChildren(details); + closeSubBlock(); + closeItem(); +} + void PerlModDocVisitor::operator()(const DocHtmlHeader &header) { #if 0 @@ -1474,6 +1481,7 @@ void PerlModGenerator::generatePerlModForMember(const MemberDef *md,const Defini case MemberType_Dictionary: memType="dictionary"; break; } + bool isFortran = md->getLanguage()==SrcLangExt_Fortran; name = md->name(); if (md->isAnonymous()) name = "__unnamed" + name.right(name.length() - 1)+"__"; @@ -1517,7 +1525,9 @@ void PerlModGenerator::generatePerlModForMember(const MemberDef *md,const Defini if (defArg && !defArg->name.isEmpty() && defArg->name!=a.name) m_output.addFieldQuotedString("definition_name", defArg->name); - if (!a.type.isEmpty()) + if (isFortran && defArg && !defArg->type.isEmpty()) + m_output.addFieldQuotedString("type", defArg->type); + else if (!a.type.isEmpty()) m_output.addFieldQuotedString("type", a.type); if (!a.array.isEmpty()) diff --git a/src/plantuml.cpp b/src/plantuml.cpp index 33c7d1e..90dcec2 100644 --- a/src/plantuml.cpp +++ b/src/plantuml.cpp @@ -22,6 +22,7 @@ #include "debug.h" #include "fileinfo.h" #include "dir.h" +#include "indexlist.h" QCString PlantumlManager::writePlantUMLSource(const QCString &outDirArg,const QCString &fileName, const QCString &content,OutputFormat format, const QCString &engine, diff --git a/src/plantuml.h b/src/plantuml.h index e3809d7..7594339 100644 --- a/src/plantuml.h +++ b/src/plantuml.h @@ -49,16 +49,16 @@ class PlantumlManager void run(); /** Write a PlantUML compatible file. - * @param[in] outDir the output directory to write the file to. - * @param[in] fileName the name of the file. If empty a name will be chosen automatically. - * @param[in] content the contents of the PlantUML file. - * @param[in] format the image format to generate. - * @param[in] engine the plantuml engine to use. - * @param[in] srcFile the source file resulting in the write command. - * @param[in] srcLine the line number resulting in the write command. + * @param[in] outDirArg the output directory to write the file to. + * @param[in] fileName the name of the file. If empty a name will be chosen automatically. + * @param[in] content the contents of the PlantUML file. + * @param[in] format the image format to generate. + * @param[in] engine the plantuml engine to use. + * @param[in] srcFile the source file resulting in the write command. + * @param[in] srcLine the line number resulting in the write command. * @returns The name of the generated file. */ - QCString writePlantUMLSource(const QCString &outDir,const QCString &fileName, + QCString writePlantUMLSource(const QCString &outDirArg,const QCString &fileName, const QCString &content, OutputFormat format, const QCString &engine,const QCString &srcFile, int srcLine); diff --git a/src/post_lex.py b/src/post_lex.py new file mode 100644 index 0000000..459a1e8 --- /dev/null +++ b/src/post_lex.py @@ -0,0 +1,94 @@ +#!/usr/bin/python +# python script to correct the linenumbers due to inclusion of common parts in lex files +# +# Copyright (C) 1997-2022 by Dimitri van Heesch. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation under the terms of the GNU General Public License is hereby +# granted. No representations are made about the suitability of this software +# for any purpose. It is provided "as is" without express or implied warranty. +# See the GNU General Public License for more details. +# +# Documents produced by Doxygen are derivative works derived from the +# input used in their production; they are not affected by this license. +# +import sys +import os +import re + +def main(): + if len(sys.argv)!=6: + sys.exit('Usage: {0} <input_cpp_file> <output_cpp_file> <correction_file> <original_lex_file> <generated_lex_file>'.format(sys.argv[0])) + + inp_cpp_file, out_cpp_file, corr_cpp_file, org_lex, gen_lex = sys.argv[1:] + + quoted_inp_cpp_file = '"' + inp_cpp_file + '"' + quoted_gen_lex = '"' + gen_lex + '"' + + corr_list = [] + if (os.path.exists(corr_cpp_file)): + # read correction file as list of tuples + with open(corr_cpp_file,"r") as corr_file: + corr_list = [tuple(map(int, line.split(' '))) for line in corr_file] + + if (os.path.exists(inp_cpp_file)): + with open(out_cpp_file,"w") as out_file: + rule_correction = False + with open(inp_cpp_file) as in_file: + for line in in_file: + # helper function to find the correction needed for the given line number + def get_line_correction(line_no): + corr_cnt = 0 + for elem in corr_list: + if elem[0] <= line_no: + corr_cnt = elem[1] + return corr_cnt + + if re.search(r'^#line ', line): # statement added by C-preprocessing + inc_line, inc_file = line.split()[1:3] + if inc_file == quoted_gen_lex: + line_cnt = int(inc_line) + line_cnt -= get_line_correction(line_cnt) # adjust line number + out_file.write("#line {0} \"{1}\"\n".format(line_cnt,org_lex)) + else: + out_file.write(line) + elif re.search(r'^ /\* #line ', line): # statement added by lex-preprocessing + inc_line, inc_file = line.split()[2:4] # first token is `/*` part + out_file.write("#line {0} \"{1}\"\n".format(inc_line,inc_file)) + elif re.search(r'static .* yy_rule_linenum', line): # linenum table generated in debug mode (flex -d) + out_file.write(line) + rule_correction = True + # read next lines till } and correct numbers + # Assumption structure is like: + # static const flex_int16_t yy_rule_linenum[26] = + # { 0, + # 981, 985, 989, 995, 1093, 1151, 1153, 1154, 1179, 1182, + # 1195, 1198, 1201, 1207, 1211, 1214, 1217, 1223, 1226, 1228, + # 1230, 1235, 1236, 1237, 1238 + # } ; + elif rule_correction: + if re.search(r'{', line): # start of yy_rule_linenum array + out_file.write(line) + elif re.search(r'}', line): # end of yy_rule_linenum array + out_file.write(line) + rule_correction = False + else: # inside the table + out_file.write(" ") + out_list = [] + rule_list = line.replace(',',' ').split() + for rule_cnt in rule_list: + rule_num = int(rule_cnt) + rule_num -= get_line_correction(rule_num) + out_list.append("{0:5d}".format(rule_num)) + out_file.write(','.join(out_list)); + if re.search(r',$', line): # re-add trailing comma + out_file.write(",") + out_file.write("\n") + else: # normal line -> just copy + out_file.write(line) + else: # input file does not exist + sys.exit("Input cpp file '{0}' does not exist.".format(sys.argv[1])) + exit(1) + +if __name__ == '__main__': + main() @@ -476,7 +476,9 @@ WSopt [ \t\r]* BEGIN(CopyLine); } <Start>^{B}*[_A-Z][_A-Z0-9]+{B}*"("[^\(\)\n]*"("[^\)\n]*")"[^\)\n]*")"{B}*\n | // function list macro with one (...) argument, e.g. for K_GLOBAL_STATIC_WITH_ARGS -<Start>^{B}*[_A-Z][_A-Z0-9]+{B}*"("[^\)\n]*")"{B}*\n { // function like macro +<Start>^{B}*[_A-Z][_A-Z0-9]+{B}*"("[^\)\n]*")"{B}*\n | // function like macro +<Start>^{B}*[_A-Z][_A-Z0-9]+{B}*"("[^\(\)\n]*"("[^\)\n]*")"[^\)\n]*")"/{B}*("//"|"/\*") | // function list macro with one (...) argument followed by comment +<Start>^{B}*[_A-Z][_A-Z0-9]+{B}*"("[^\)\n]*")"/{B}*("//"|"/\*") { // function like macro followed by comment bool skipFuncMacros = Config_getBool(SKIP_FUNCTION_MACROS); QCString name(yytext); int pos = name.find('('); @@ -495,8 +497,12 @@ WSopt [ \t\r]* ) ) { - outputChar(yyscanner,'\n'); - yyextra->yyLineNr++; + // Only when ends on \n + if (yytext[yyleng-1] == '\n') + { + outputChar(yyscanner,'\n'); + yyextra->yyLineNr++; + } } else // don't skip { @@ -628,7 +634,6 @@ WSopt [ \t\r]* if ((yyextra->includeStack.empty() || yyextra->curlyCount>0) && yyextra->macroExpansion && (def=isDefined(yyscanner,yytext)) && - /*(def->isPredefined || macroIsAccessible(def)) && */ (!yyextra->expandOnlyPredef || def->isPredefined) ) { @@ -637,7 +642,9 @@ WSopt [ \t\r]* yyextra->defArgsStr=yytext; if (def->nargs==-1) // no function macro { - QCString result = def->isPredefined ? def->definition : expandMacro(yyscanner,yyextra->defArgsStr); + QCString result = def->isPredefined && !def->expandAsDefined ? + def->definition : + expandMacro(yyscanner,yyextra->defArgsStr); outputString(yyscanner,result); } else // zero or more arguments @@ -657,11 +664,12 @@ WSopt [ \t\r]* yyextra->macroExpansion && (def=isDefined(yyscanner,yytext)) && def->nargs==-1 && - /*(def->isPredefined || macroIsAccessible(def)) &&*/ (!yyextra->expandOnlyPredef || def->isPredefined) ) { - QCString result=def->isPredefined ? def->definition : expandMacro(yyscanner,yytext); + QCString result=def->isPredefined && !def->expandAsDefined ? + def->definition : + expandMacro(yyscanner,yytext); outputString(yyscanner,result); } else @@ -1395,7 +1403,7 @@ WSopt [ \t\r]* { if (yyextra->ccomment) { - outputArray(yyscanner,"/** ",4); + outputArray(yyscanner,"/** ",4); // */ } BEGIN(yyextra->condCtx); } @@ -2058,7 +2066,7 @@ static QCString extractTrailingComment(const QCString &s) { i++; } - // only /*!< or /**< are treated as a comment for the macro name, + // only /*!< ... */ or /**< ... */ are treated as a comment for the macro name, // otherwise the comment is treated as part of the macro definition return ((s[i+1]=='*' || s[i+1]=='!') && s[i+2]=='<') ? &s[i-1] : ""; } @@ -2557,6 +2565,7 @@ static int getNextId(const QCString &expr,int p,int *l) */ static bool expandExpression(yyscan_t yyscanner,QCString &expr,QCString *rest,int pos,int level) { + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; YY_EXTRA_TYPE state = preYYget_extra(yyscanner); //printf(">expandExpression(expr='%s',rest='%s',pos=%d,level=%d)\n",qPrint(expr),rest ? qPrint(*rest) : "", pos, level); if (expr.isEmpty()) @@ -2590,6 +2599,9 @@ static bool expandExpression(yyscan_t yyscanner,QCString &expr,QCString *rest,in if (state->expandedDict.find(macroName.str())==state->expandedDict.end()) // expand macro { Define *def=isDefined(yyscanner,macroName); + // In case EXPAND_ONLY_PREDEF is enabled prevent expansion unless the macro was explicitly + // predefined + if (yyextra->expandOnlyPredef && def && !def->isPredefined) def=nullptr; if (macroName=="defined") { //printf("found defined inside macro definition '%s'\n",qPrint(expr.right(expr.length()-p))); @@ -2642,10 +2654,10 @@ static bool expandExpression(yyscan_t yyscanner,QCString &expr,QCString *rest,in expanded = expandExpression(yyscanner,resultExpr,&restExpr,0,level+1); state->expandedDict.erase(toStdString(macroName)); } - else if (def && def->nonRecursive) - { - expanded = true; - } + else if (def && def->nonRecursive) + { + expanded = true; + } if (expanded) { expr=expr.left(p)+resultExpr+restExpr; @@ -2967,6 +2979,7 @@ static void addDefine(yyscan_t yyscanner) Doxygen::expandAsDefinedSet.find(def.name.str())!=Doxygen::expandAsDefinedSet.end()) { def.isPredefined=TRUE; + def.expandAsDefined=TRUE; } auto it = state->localDefines.find(def.name.str()); if (it!=state->localDefines.end()) // redefine @@ -3127,9 +3140,7 @@ static void readIncludeFile(yyscan_t yyscanner,const QCString &inc) { // extract include path+name QCString incFileName=inc.mid(s,i-s).stripWhiteSpace(); - - QCString dosExt = incFileName.right(4); - if (dosExt==".exe" || dosExt==".dll" || dosExt==".tlb") + if (incFileName.endsWith(".exe") || incFileName.endsWith(".dll") || incFileName.endsWith(".tlb")) { // skip imported binary files (e.g. M$ type libraries) return; diff --git a/src/pre_lex.py b/src/pre_lex.py new file mode 100644 index 0000000..3d3da43 --- /dev/null +++ b/src/pre_lex.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +# python script to include common parts in lex file +# +# Copyright (C) 1997-2022 by Dimitri van Heesch. +# +# Permission to use, copy, modify, and distribute this software and its +# documentation under the terms of the GNU General Public License is hereby +# granted. No representations are made about the suitability of this software +# for any purpose. It is provided "as is" without express or implied warranty. +# See the GNU General Public License for more details. +# +# Documents produced by Doxygen are derivative works derived from the +# input used in their production; they are not affected by this license. +# +import sys +import os +import re + +def main(): + if len(sys.argv)!=6: + sys.exit('Usage: {0} <input_lex_file> <output_lex_file> <correction_file> <dependency_lex_file> <include_path>'.format(sys.argv[0])) + + inp_lex_file, out_lex_file, corr_lex_file, dep_lex_file, inc_path = sys.argv[1:] + + out_cnt = rd_cnt = add_cnt = 0 + if (os.path.exists(inp_lex_file)): + with open(out_lex_file,"w") as out_file: + with open(corr_lex_file,"w") as corr_file: + first_corr = True + with open(dep_lex_file,"w") as dep_file: + dep_file.write("{0}:".format(out_lex_file)) + with open(inp_lex_file) as in_file: + for line in in_file: + if re.search(r'^%doxygen', line): # special include file marker + inc_file = inc_path + "/" + line.split()[1] + dep_file.write(" {0}".format(inc_file)) + first_line = True + with open(inc_file) as f_inc: + for inc_line in f_inc: + if first_line: + first_line = False + out_file.write(" /* #line 1 {0} */\n".format(inc_file)) + out_cnt += 1 + add_cnt += 1 + out_file.write(inc_line) + f_inc.close() + if not first_line: + out_file.write(" /* #line {0} {1} */\n".format(rd_cnt+2,inp_lex_file)) + out_cnt += 2 + add_cnt += 1 + if first_corr: + corr_file.write("{0} {1}\n".format(0,0)) + first_corr = False + corr_file.write("{0} {1}\n".format(out_cnt,add_cnt)) + else: + out_cnt += 1 + rd_cnt += 1 + out_file.write(line) + dep_file.write("\n") + else: # input file does not exist + sys.exit("Input lex file '{0}' does not exist.".format(sys.argv[1])) + +if __name__ == '__main__': + main() diff --git a/src/printdocvisitor.h b/src/printdocvisitor.h index e5e801b..6efb18a 100644 --- a/src/printdocvisitor.h +++ b/src/printdocvisitor.h @@ -147,18 +147,6 @@ class PrintDocVisitor case DocStyleChange::Span: if (s.enable()) printf("<span>"); else printf("</span>"); break; - case DocStyleChange::Details: - if (s.enable()) - { - indent_pre(); - printf("<details>\n"); - } - else - { - indent_post(); - printf("</details>\n"); - } - break; case DocStyleChange::Summary: if (s.enable()) { @@ -530,6 +518,19 @@ class PrintDocVisitor indent_post(); printf("</a>\n"); } + void operator()(const DocHtmlDetails &details) + { + indent_pre(); + printf("<details"); + for (const auto &opt : details.attribs()) + { + printf(" %s=\"%s\"",qPrint(opt.name),qPrint(opt.value)); + } + printf(">\n"); + visitChildren(details); + indent_post(); + printf("</details>\n"); + } void operator()(const DocHtmlHeader &header) { indent_pre(); diff --git a/src/pycode.l b/src/pycode.l index 8756325..8bcf6d9 100644 --- a/src/pycode.l +++ b/src/pycode.l @@ -143,11 +143,12 @@ static void generateClassOrGlobalLink(yyscan_t yyscanner, CodeOutputInterface &o static void generateFunctionLink(yyscan_t yyscanner, CodeOutputInterface &ol, const QCString &funcName); static bool findMemberLink(yyscan_t yyscanner, CodeOutputInterface &ol, - Definition *sym, const QCString &symName); + const Definition *sym, const QCString &symName); static void findMemberLink(yyscan_t yyscanner, CodeOutputInterface &ol, const QCString &symName); static void adjustScopesAndSuites(yyscan_t yyscanner,unsigned indentLength); static yy_size_t yyread(yyscan_t yyscanner,char *buf,yy_size_t max_size); +static inline void pop_state(yyscan_t yyscanner); #if 0 // TODO: call me to store local variables and get better syntax highlighting, see code.l static void addVariable(yyscan_t yyscanner, QCString type, QCString name); @@ -780,7 +781,7 @@ TARGET ({IDENTIFIER}|"("{TARGET_LIST}")"|"["{TARGET_LIST}"]"|{ATTRIBU endFontClass(yyscanner); } unput(*yytext); - yy_pop_state(yyscanner); + pop_state(yyscanner); } <*>{POUNDCOMMENT}.* { if (YY_START==SingleQuoteString || @@ -1483,7 +1484,7 @@ static void generateFunctionLink(yyscan_t yyscanner, static bool findMemberLink(yyscan_t yyscanner, CodeOutputInterface &ol, - Definition *sym, + const Definition *sym, const QCString &symName) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; @@ -1533,10 +1534,10 @@ static void findMemberLink(yyscan_t yyscanner, bool found = false; if (yyextra->currentDefinition) { - auto range = Doxygen::symbolMap->find(symName); - for (auto it = range.first; it!=range.second; ++it) + auto v = Doxygen::symbolMap->find(symName); + for (auto p : v) { - if (findMemberLink(yyscanner,ol,it->second,symName)) found = true; + if (findMemberLink(yyscanner,ol,p,symName)) found = true; } } //printf("sym %s not found\n",&yytext[5]); @@ -1664,6 +1665,15 @@ void PythonCodeParser::parseCode(CodeOutputInterface &codeOutIntf, printlex(yy_flex_debug, FALSE, __FILE__, fileDef ? qPrint(fileDef->fileName()): NULL); } +static inline void pop_state(yyscan_t yyscanner) +{ + struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; + if ( yyg->yy_start_stack_ptr <= 0 ) + warn(yyextra->fileName,yyextra->yyLineNr,"Unexpected statement '%s'",yytext ); + else + yy_pop_state(yyscanner); +} + #if USE_STATE2STRING #include "pycode.l.h" #endif diff --git a/src/pyscanner.l b/src/pyscanner.l index 7782125..8c87980 100644 --- a/src/pyscanner.l +++ b/src/pyscanner.l @@ -1187,6 +1187,10 @@ STARTDOCSYMS "##" yyextra->start_init = FALSE; yyextra->current->initializer << yytext; } + "\\\n" { + yyextra->current->initializer << yytext; + incLineNr(yyscanner); + } . { yyextra->start_init = FALSE; yyextra->current->initializer << *yytext; @@ -1284,8 +1288,8 @@ STARTDOCSYMS "##" if (!actualDoc.isEmpty()) { stripIndentation(actualDoc,yyextra->commentIndent); - actualDoc.prepend("\\verbatim\n"); - actualDoc.append("\\endverbatim "); + actualDoc.prepend("@iverbatim\n"); + actualDoc.append("@endiverbatim "); } } //printf("-------> yyextra->current=%p yyextra->bodyEntry=%p\n",yyextra->current,yyextra->bodyEntry); @@ -1299,8 +1303,8 @@ STARTDOCSYMS "##" if (!actualDoc.isEmpty()) { stripIndentation(actualDoc,yyextra->commentIndent); - actualDoc.prepend("\\verbatim\n"); - actualDoc.append("\\endverbatim "); + actualDoc.prepend("@iverbatim\n"); + actualDoc.append("@endiverbatim "); } } if (yyextra->moduleScope.startsWith("__") && yyextra->moduleScope.endsWith("__")) @@ -1534,7 +1538,7 @@ static void newVariable(yyscan_t yyscanner) static void newFunction(yyscan_t yyscanner) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; - if (yyextra->current->name.left(2)=="__" && yyextra->current->name.right(2)=="__") + if (yyextra->current->name.startsWith("__") && yyextra->current->name.endsWith("__")) { // special method name, see // http://docs.python.org/ref/specialnames.html diff --git a/src/qhp.cpp b/src/qhp.cpp index c093adb..eb6de0b 100644 --- a/src/qhp.cpp +++ b/src/qhp.cpp @@ -187,13 +187,9 @@ static QCString makeRef(const QCString & withoutExtension, const QCString & anch return result+"#"+anchor; } -Qhp::Qhp() : p(std::make_unique<Private>()) -{ -} - -Qhp::~Qhp() -{ -} +Qhp::Qhp() : p(std::make_unique<Private>()) {} +Qhp::~Qhp() = default; +Qhp::Qhp(Qhp &&) = default; void Qhp::initialize() { @@ -16,13 +16,17 @@ #include <memory> -#include "index.h" +#include "qcstring.h" -class Qhp : public IndexIntf +class Definition; +class MemberDef; + +class Qhp { public: Qhp(); ~Qhp(); + Qhp(Qhp &&); void initialize(); void finalize(); diff --git a/src/regex.h b/src/regex.h index 531092b..61e53ee 100644 --- a/src/regex.h +++ b/src/regex.h @@ -64,7 +64,7 @@ class Ex * - `\s` matches any whitespace as defined by `std::isspace()` * - `\d` matches any digit as defined by `std::digit()` * - `\a` matches any alphabetical characters, same as `[a-z_A-Z\x80-\xFF]` - * - `\w` matches any alpha numercial character, same as `[a-z_A-Z0-9\x80-\xFF]` + * - `\w` matches any alpha numerical character, same as `[a-z_A-Z0-9\x80-\xFF]` * - `\xHH` matches a hexadecimal character, e.g. `\xA0` matches character code 160. * * A character range can be used to match a character that falls inside a range diff --git a/src/res2cc_cmd.py b/src/res2cc_cmd.py index f6321f6..245c312 100755 --- a/src/res2cc_cmd.py +++ b/src/res2cc_cmd.py @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # Script that compiles a set of resources into a single C++ source file. The C++ file # offers an initResources() function, which registers the resources with the resource # manager (class ResourceMgr) @@ -20,106 +20,106 @@ from os.path import isfile, join, splitext import sys class File(object): - def __init__(self,directory,subdir,fileName,mode): - self.directory = directory - self.subdir = subdir - self.fileName = fileName - filePath = join(directory,subdir,fileName) - self.fileSize = stat(filePath).st_size - self.bareName = fileName.replace('.','_') - self.inputFile = open(filePath,mode) + def __init__(self,directory,subdir,fileName,mode): + self.directory = directory + self.subdir = subdir + self.fileName = fileName + filePath = join(directory,subdir,fileName) + self.fileSize = stat(filePath).st_size + self.bareName = fileName.replace('.','_') + self.inputFile = open(filePath,mode) - def formatByte(self,byte): - if isinstance(byte,int): - return "%02x" % byte - else: - return format(ord(byte),'02x') + def formatByte(self,byte): + if isinstance(byte,int): + return "%02x" % byte + else: + return format(ord(byte),'02x') - def writeBytes(self,data,outputFile): - bytes_per_line=16 - print("static const unsigned char %s_data[] = " % self.bareName,file=outputFile) - print("{",file=outputFile) - lines = [data[x:x+bytes_per_line] for x in range(0,len(data),bytes_per_line)] - linesAsString = ',\n '.join([', '.join(['0x'+self.formatByte(byte) for byte in line]) for line in lines]) - print(' %s' % linesAsString,file=outputFile) - print("};",file=outputFile) - print("const int %s_len = %d;\n" % (self.bareName,len(data)),file=outputFile) + def writeBytes(self,data,outputFile): + bytes_per_line=16 + print("static const unsigned char %s_data[] = " % self.bareName,file=outputFile) + print("{",file=outputFile) + lines = [data[x:x+bytes_per_line] for x in range(0,len(data),bytes_per_line)] + linesAsString = ',\n '.join([', '.join(['0x'+self.formatByte(byte) for byte in line]) for line in lines]) + print(' %s' % linesAsString,file=outputFile) + print("};",file=outputFile) + print("const int %s_len = %d;\n" % (self.bareName,len(data)),file=outputFile) - def convertToBytes(self,outputFile): - lines = [x for x in self.inputFile.readlines() if not x.startswith('#')] - w,h = (int(x) for x in lines[0].split()) - data = "".join(map(chr,[int(w>>8),int(w&0xFF),int(h>>8),int(h&0xFF)]+ - [int(x) for line in lines[1:] for x in line.split()])) - self.writeBytes(data,outputFile) + def convertToBytes(self,outputFile): + lines = [x for x in self.inputFile.readlines() if not x.startswith('#')] + w,h = (int(x) for x in lines[0].split()) + data = "".join(map(chr,[int(w>>8),int(w&0xFF),int(h>>8),int(h&0xFF)]+ + [int(x) for line in lines[1:] for x in line.split()])) + self.writeBytes(data,outputFile) - @staticmethod - def factory(directory,subdir,fname): - ext = splitext(fname)[1] - if ext=='.lum': return LumFile(directory,subdir,fname) - if ext=='.luma': return LumaFile(directory,subdir,fname) - if ext=='.css': return CSSFile(directory,subdir,fname) - if ext=='.svg': return SVGFile(directory,subdir,fname) - return VerbatimFile(directory,subdir,fname) + @staticmethod + def factory(directory,subdir,fname): + ext = splitext(fname)[1] + if ext=='.lum': return LumFile(directory,subdir,fname) + if ext=='.luma': return LumaFile(directory,subdir,fname) + if ext=='.css': return CSSFile(directory,subdir,fname) + if ext=='.svg': return SVGFile(directory,subdir,fname) + return VerbatimFile(directory,subdir,fname) class VerbatimFile(File): - def __init__(self,directory,subdir,fileName): - File.__init__(self,directory,subdir,fileName,"rb") - def writeContents(self,outputFile): - self.writeBytes(self.inputFile.read(),outputFile) - def writeDirEntry(self,outputFile): - print(" { \"%s\", \"%s\", %s_data, %s_len, Resource::Verbatim }," % (self.subdir,self.fileName,self.bareName,self.bareName), file=outputFile) + def __init__(self,directory,subdir,fileName): + File.__init__(self,directory,subdir,fileName,"rb") + def writeContents(self,outputFile): + self.writeBytes(self.inputFile.read(),outputFile) + def writeDirEntry(self,outputFile): + print(" { \"%s\", \"%s\", %s_data, %s_len, Resource::Verbatim }," % (self.subdir,self.fileName,self.bareName,self.bareName), file=outputFile) class CSSFile(File): - def __init__(self,directory,subdir,fileName): - File.__init__(self,directory,subdir,fileName,"r") - def writeContents(self,outputFile): - self.writeBytes(self.inputFile.read(),outputFile) - def writeDirEntry(self,outputFile): - print(" { \"%s\", \"%s\", %s_data, %s_len, Resource::CSS }," % (self.subdir,self.fileName,self.bareName,self.bareName), file=outputFile) + def __init__(self,directory,subdir,fileName): + File.__init__(self,directory,subdir,fileName,"r") + def writeContents(self,outputFile): + self.writeBytes(self.inputFile.read(),outputFile) + def writeDirEntry(self,outputFile): + print(" { \"%s\", \"%s\", %s_data, %s_len, Resource::CSS }," % (self.subdir,self.fileName,self.bareName,self.bareName), file=outputFile) class SVGFile(File): - def __init__(self,directory,subdir,fileName): - File.__init__(self,directory,subdir,fileName,"r") - def writeContents(self,outputFile): - self.writeBytes(self.inputFile.read(),outputFile) - def writeDirEntry(self,outputFile): - print(" { \"%s\", \"%s\", %s_data, %s_len, Resource::SVG }," % (self.subdir,self.fileName,self.bareName,self.bareName), file=outputFile) + def __init__(self,directory,subdir,fileName): + File.__init__(self,directory,subdir,fileName,"r") + def writeContents(self,outputFile): + self.writeBytes(self.inputFile.read(),outputFile) + def writeDirEntry(self,outputFile): + print(" { \"%s\", \"%s\", %s_data, %s_len, Resource::SVG }," % (self.subdir,self.fileName,self.bareName,self.bareName), file=outputFile) class LumFile(File): - def __init__(self,directory,subdir,fileName): - File.__init__(self,directory,subdir,fileName,"r") - def writeContents(self,outputFile): - self.convertToBytes(outputFile) - def writeDirEntry(self,outputFile): - print(" { \"%s\", \"%s\", %s_data, %s_len, Resource::Luminance }," % (self.subdir,self.fileName,self.bareName,self.bareName), file=outputFile) + def __init__(self,directory,subdir,fileName): + File.__init__(self,directory,subdir,fileName,"r") + def writeContents(self,outputFile): + self.convertToBytes(outputFile) + def writeDirEntry(self,outputFile): + print(" { \"%s\", \"%s\", %s_data, %s_len, Resource::Luminance }," % (self.subdir,self.fileName,self.bareName,self.bareName), file=outputFile) class LumaFile(File): - def __init__(self,directory,subdir,fileName): - File.__init__(self,directory,subdir,fileName,"r") - def writeContents(self,outputFile): - self.convertToBytes(outputFile) - def writeDirEntry(self,outputFile): - print(" { \"%s\", \"%s\", %s_data, %s_len, Resource::LumAlpha }," % (self.subdir,self.fileName,self.bareName,self.bareName), file=outputFile) + def __init__(self,directory,subdir,fileName): + File.__init__(self,directory,subdir,fileName,"r") + def writeContents(self,outputFile): + self.convertToBytes(outputFile) + def writeDirEntry(self,outputFile): + print(" { \"%s\", \"%s\", %s_data, %s_len, Resource::LumAlpha }," % (self.subdir,self.fileName,self.bareName,self.bareName), file=outputFile) def main(): - if len(sys.argv)<3: - sys.exit('Usage: %s directory output_file.cpp' % sys.argv[0]) - directory = sys.argv[1] - files = [] - for dirName, subdirList, fileList in walk(directory): - for fname in fileList: - subdir = dirName[len(directory)+1:] if dirName.startswith(directory) else dirName - if subdir: - files.append(File.factory(directory,subdir,fname)) - files.sort(key=lambda f: f.subdir + "/" + f.fileName) - outputFile = open(sys.argv[2],"w") - print("#include \"resourcemgr.h\"\n",file=outputFile) - for f in files: - f.writeContents(outputFile) - print("void initResources() { ResourceMgr::instance().registerResources({",file=outputFile) - for f in files: - f.writeDirEntry(outputFile) - print("});}",file=outputFile) + if len(sys.argv)<3: + sys.exit('Usage: %s directory output_file.cpp' % sys.argv[0]) + directory = sys.argv[1] + files = [] + for dirName, subdirList, fileList in walk(directory): + for fname in fileList: + subdir = dirName[len(directory)+1:] if dirName.startswith(directory) else dirName + if subdir: + files.append(File.factory(directory,subdir,fname)) + files.sort(key=lambda f: f.subdir + "/" + f.fileName) + outputFile = open(sys.argv[2],"w") + print("#include \"resourcemgr.h\"\n",file=outputFile) + for f in files: + f.writeContents(outputFile) + print("void initResources() { ResourceMgr::instance().registerResources({",file=outputFile) + for f in files: + f.writeDirEntry(outputFile) + print("});}",file=outputFile) if __name__ == '__main__': - main() + main() diff --git a/src/rtfdocvisitor.cpp b/src/rtfdocvisitor.cpp index 01a259b..1e2afaa 100644 --- a/src/rtfdocvisitor.cpp +++ b/src/rtfdocvisitor.cpp @@ -286,21 +286,8 @@ void RTFDocVisitor::operator()(const DocStyleChange &s) break; case DocStyleChange::Div: /* HTML only */ break; case DocStyleChange::Span: /* HTML only */ break; - case DocStyleChange::Details: /* emulation of the <details> tag */ - if (s.enable()) - { - m_t << "{\n"; - m_t << "\\par\n"; - } - else - { - m_t << "\\par"; - m_t << "}\n"; - } - m_lastIsPara=TRUE; - break; case DocStyleChange::Summary: /* emulation of the <summary> tag inside a <details> tag */ - if (s.enable()) m_t << "{\\b "; else m_t << "} "; + if (s.enable()) m_t << "{\\b "; else m_t << "}\\par "; break; } } @@ -1144,6 +1131,28 @@ void RTFDocVisitor::operator()(const DocHRef &href) m_lastIsPara=FALSE; } +void RTFDocVisitor::operator()(const DocHtmlDetails &d) +{ + if (m_hide) return; + //m_lastIsPara=TRUE; + //m_t << "{\n"; + //m_t << "\\par\n"; + //visitChildren(d); + //m_t << "\\par"; + //m_t << "}\n"; + //m_lastIsPara=TRUE; + DBG_RTF("{\\comment RTFDocVisitor::operator()(const DocHtmlDetails &)}\n"); + if (!m_lastIsPara) m_t << "\\par\n"; + m_t << "{"; // start desc + incIndentLevel(); + m_t << rtf_Style_Reset << getStyle("DescContinue"); + visitChildren(d); + if (!m_lastIsPara) m_t << "\\par\n"; + decIndentLevel(); + m_t << "}"; // end desc + m_lastIsPara=TRUE; +} + void RTFDocVisitor::operator()(const DocHtmlHeader &header) { if (m_hide) return; diff --git a/src/rtfdocvisitor.h b/src/rtfdocvisitor.h index fc3257c..fc1b539 100644 --- a/src/rtfdocvisitor.h +++ b/src/rtfdocvisitor.h @@ -80,6 +80,7 @@ class RTFDocVisitor : public DocVisitor void operator()(const DocHtmlCell &); void operator()(const DocInternal &); void operator()(const DocHRef &); + void operator()(const DocHtmlDetails &); void operator()(const DocHtmlHeader &); void operator()(const DocImage &); void operator()(const DocDotFile &); diff --git a/src/rtfgen.cpp b/src/rtfgen.cpp index a905fc0..882271d 100644 --- a/src/rtfgen.cpp +++ b/src/rtfgen.cpp @@ -17,8 +17,6 @@ * */ -#include <chrono> -#include <ctime> #include <stdlib.h> #include "rtfgen.h" @@ -48,17 +46,14 @@ #include "dir.h" #include "utf8.h" #include "debug.h" - +#include "datetime.h" //#define DBG_RTF(x) x; #define DBG_RTF(x) static QCString dateToRTFDateString() { - auto now = std::chrono::system_clock::now(); - auto time = std::chrono::system_clock::to_time_t(now); - auto tm = *localtime(&time); - + auto tm = getCurrentDateTime(); QCString result; result.sprintf("\\yr%d\\mo%d\\dy%d\\hr%d\\min%d\\sec%d", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday, @@ -374,7 +369,7 @@ void RTFGenerator::startFile(const QCString &name,const QCString &,const QCStrin QCString fileName=name; m_relPath = relativePathToRoot(fileName); - if (fileName.right(4)!=".rtf" ) fileName+=".rtf"; + if (!fileName.endsWith(".rtf")) fileName+=".rtf"; startPlainFile(fileName); setRelativePath(m_relPath); setSourceFileName(stripPath(fileName)); diff --git a/src/scan_states.py b/src/scan_states.py index e3924e0..ef159fe 100644 --- a/src/scan_states.py +++ b/src/scan_states.py @@ -1,5 +1,5 @@ -#!/usr/bin/python -# python script to generate an overview of the staes based on the input lex file. +#!/usr/bin/env python +# python script to generate an overview of the states based on the input lex file. # # Copyright (C) 1997-2019 by Dimitri van Heesch. # diff --git a/src/scanner.l b/src/scanner.l index 92387cc..980b8c8 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -202,7 +202,10 @@ struct scannerYY_state std::vector< std::pair<Entry*,std::shared_ptr<Entry> > > outerScopeEntries; QCString programStr; - ClangTUParser * clangParser = 0; + ClangTUParser *clangParser = 0; + + int fakeNS = 0; //<! number of file scoped namespaces in CSharp file + TextStream dummyTextStream; }; #if USE_STATE2STRING @@ -1098,6 +1101,10 @@ NONLopt [^\n]* if (yyextra->insideCli) yyextra->current->spec |= Entry::Initonly; lineCount(yyscanner); } +<FindMembers>{B}*"static"{BN}*/"{" { yyextra->current->type += " static "; + yyextra->current->stat = TRUE; + lineCount(yyscanner); + } <FindMembers>{B}*"static"{BN}+ { yyextra->current->type += " static "; yyextra->current->stat = TRUE; lineCount(yyscanner); @@ -2072,9 +2079,9 @@ NONLopt [^\n]* } } <HereDocEnd>. { } -<CopyHereDocEnd>^{ID} { // id at start of the line could mark the end of the block +<CopyHereDocEnd>^{Bopt}{ID} { // id at start of the line could mark the end of the block *yyextra->pCopyHereDocGString << yytext; - if (yyextra->delimiter==yytext) // it is the end marker + if (yyextra->delimiter==QCString(yytext).stripWhiteSpace()) // it is the end marker { BEGIN(yyextra->lastHereDocContext); } @@ -2089,7 +2096,7 @@ NONLopt [^\n]* <CopyHereDocEnd>. { *yyextra->pCopyHereDocGString << yytext; } -<FindMembers>"Q_OBJECT" { // Qt object macro +<FindMembers>"Q_OBJECT"|"Q_GADGET" { // Qt object / gadget macro } <FindMembers>"Q_PROPERTY" { // Qt property declaration yyextra->current->protection = Public ; // see bug734245 & bug735462 @@ -2361,12 +2368,12 @@ NONLopt [^\n]* yyextra->current->name = yytext; else yyextra->current->name += yytext; - if (yyextra->current->name.left(7)=="static ") + if (yyextra->current->name.startsWith("static ")) { yyextra->current->stat = TRUE; yyextra->current->name= yyextra->current->name.mid(7); } - else if (yyextra->current->name.left(7)=="inline ") + else if (yyextra->current->name.startsWith("inline ")) { if (yyextra->current->type.isEmpty()) { @@ -2378,7 +2385,7 @@ NONLopt [^\n]* } yyextra->current->name= yyextra->current->name.mid(7); } - else if (yyextra->current->name.left(10)=="constexpr ") + else if (yyextra->current->name.startsWith("constexpr ")) { if (yyextra->current->type.isEmpty()) { @@ -2390,7 +2397,7 @@ NONLopt [^\n]* } yyextra->current->name=yyextra->current->name.mid(10); } - else if (yyextra->current->name.left(6)=="const ") + else if (yyextra->current->name.startsWith("const ")) { if (yyextra->current->type.isEmpty()) { @@ -2402,7 +2409,7 @@ NONLopt [^\n]* } yyextra->current->name=yyextra->current->name.mid(6); } - else if (yyextra->current->name.left(9)=="volatile ") + else if (yyextra->current->name.startsWith("volatile ")) { if (yyextra->current->type.isEmpty()) { @@ -2414,7 +2421,7 @@ NONLopt [^\n]* } yyextra->current->name=yyextra->current->name.mid(9); } - else if (yyextra->current->name.left(8)=="typedef ") + else if (yyextra->current->name.startsWith("typedef ")) { if (yyextra->current->type.isEmpty()) { @@ -3268,6 +3275,15 @@ NONLopt [^\n]* yyextra->lastStringContext=YY_START; BEGIN(CopyGString); } +<GCopySquare>\' { + *yyextra->pCopySquareGString << *yytext; + if (yyextra->insidePHP) + { + yyextra->pCopyQuotedGString=yyextra->pCopySquareGString; + yyextra->lastStringContext=YY_START; + BEGIN(CopyPHPGString); + } + } <GCopySquare>"[" { *yyextra->pCopySquareGString << *yytext; yyextra->squareCount++; @@ -3304,7 +3320,7 @@ NONLopt [^\n]* *yyextra->pCopySquareGString << yytext; } } -<GCopySquare>[^"\[\]\n\/,]+ { +<GCopySquare>[^"'\[\]\n\/,]+ { *yyextra->pCopySquareGString << yytext; } <GCopySquare>. { @@ -3459,11 +3475,11 @@ NONLopt [^\n]* yyextra->current->bodyLine = yyextra->yyLineNr; yyextra->current->bodyColumn = yyextra->yyColNr; } - if ( yyextra->insidePHP && yyextra->current->type.left(3) == "var" ) + if ( yyextra->insidePHP && yyextra->current->type.startsWith("var")) { yyextra->current->type = yyextra->current->type.mid(3); } - if (yyextra->isTypedef && yyextra->current->type.left(8)!="typedef ") + if (yyextra->isTypedef && !yyextra->current->type.startsWith("typedef ")) { yyextra->current->type.prepend("typedef "); } @@ -4429,7 +4445,8 @@ NONLopt [^\n]* yyextra->current->type += *yytext; } <FindMembers>"("/{BN}*{ID}{BN}*"*"{BN}*{ID}*")"{BN}*"(" { // for catching typedef void (__stdcall *f)() like definitions - if (yyextra->current->type.left(7)=="typedef" && yyextra->current->bodyLine==-1) + if (yyextra->current->type.startsWith("typedef") && + yyextra->current->bodyLine==-1) // the bodyLine check is to prevent this guard to be true more than once { yyextra->current->bodyLine = yyextra->yyLineNr; @@ -5151,10 +5168,11 @@ NONLopt [^\n]* int te=yyextra->current->type.findRev('>'); // bug677315: A<int(void *, char *)> get(); is not a function pointer + bool startsWithTypedef = yyextra->current->type.startsWith("typedef "); bool isFunction = ti==-1 || // not a (...*...) pattern (ts!=-1 && ts<te && ts<ti && ti<te); // (...*...) is part of a template argument list - bool isVariable = (!yyextra->current->type.isEmpty() && - (!isFunction || yyextra->current->type.left(8)=="typedef ")); + bool isVariable = !yyextra->current->type.isEmpty() && + (!isFunction || startsWithTypedef); //printf("type=%s ts=%d te=%d ti=%d isFunction=%d\n", // qPrint(yyextra->current->type),ts,te,ti,isFunction); @@ -5167,7 +5185,7 @@ NONLopt [^\n]* if (isVariable) { //printf("Scanner.l: found in class variable: '%s' '%s' '%s'\n", qPrint(yyextra->current->type),qPrint(yyextra->current->name),qPrint(yyextra->current->args)); - if (yyextra->isTypedef && yyextra->current->type.left(8)!="typedef ") + if (yyextra->isTypedef && !startsWithTypedef) { yyextra->current->type.prepend("typedef "); } @@ -5185,7 +5203,7 @@ NONLopt [^\n]* //printf("Scanner.l: prototype? type='%s' name='%s' args='%s'\n",qPrint(yyextra->current->type),qPrint(yyextra->current->name),qPrint(yyextra->current->args)); if (isVariable) { - if (yyextra->isTypedef && yyextra->current->type.left(8)!="typedef ") + if (yyextra->isTypedef && !startsWithTypedef) { yyextra->current->type.prepend("typedef "); } @@ -5458,8 +5476,10 @@ NONLopt [^\n]* <SkipInits,SkipCurly,SkipCurlyCpp>@\" { if (!yyextra->insideCS) REJECT; // C# verbatim string + // we want to discard the string, due to reuse of states we need a dummy stream yyextra->lastSkipVerbStringContext=YY_START; - yyextra->pSkipVerbString=&yyextra->current->initializer; + yyextra->pSkipVerbString=&yyextra->dummyTextStream; + yyextra->dummyTextStream.clear(); // remove old data so it won't grow too much BEGIN(SkipVerbString); } <SkipInits,SkipCurly,SkipCurlyCpp>{CHARLIT} { @@ -5595,8 +5615,18 @@ NONLopt [^\n]* <ClassTemplSpec>. { yyextra->current->name += yytext; } -<CompoundName>{SCOPENAME}{BN}*";" { // forward declaration - if (!yyextra->current->tArgLists.empty()) +<CompoundName>{SCOPENAME}{BN}*";" { // forward declaration? + if (yyextra->insideCS && yyextra->current->type == "namespace") + { + // file scoped CSharp namespace + lineCount(yyscanner); + yyextra->current->name = yytext; + yyextra->current->name=yyextra->current->name.left(yyextra->current->name.length()-1).stripWhiteSpace(); + yyextra->fakeNS++; + unput('{'); // fake start of body + BEGIN( ClassVar ); + } + else if (!yyextra->current->tArgLists.empty()) { // found a forward template declaration, this has // a purpose of its own @@ -5634,19 +5664,22 @@ NONLopt [^\n]* yyextra->current_root->moveToSubEntryAndRefresh(yyextra->current); } - unput(';'); - yyextra->current->reset(); - initEntry(yyscanner); - if (yyextra->insideObjC) // see bug746361 - { - yyextra->language = yyextra->current->lang = SrcLangExt_Cpp; - yyextra->insideObjC = FALSE; - } - if (yyextra->isTypedef) // typedef of a class, put typedef keyword back + if (!(yyextra->insideCS && yyextra->current->type == "namespace")) { - yyextra->current->type.prepend("typedef"); + unput(';'); + yyextra->current->reset(); + initEntry(yyscanner); + if (yyextra->insideObjC) // see bug746361 + { + yyextra->language = yyextra->current->lang = SrcLangExt_Cpp; + yyextra->insideObjC = FALSE; + } + if (yyextra->isTypedef) // typedef of a class, put typedef keyword back + { + yyextra->current->type.prepend("typedef"); + } + BEGIN( FindMembers ); } - BEGIN( FindMembers ); } <CompoundName>{SCOPENAME}/{BN}*"(" { yyextra->current->name = yytext ; @@ -7052,6 +7085,18 @@ NONLopt [^\n]* yyextra->lastCContext = YY_START ; BEGIN( SkipCxxComment ) ; } +<<EOF>> { + if (yyextra->insideCS && yyextra->fakeNS) + { + yyextra->fakeNS--; + unput('}'); + BEGIN ( ReadNSBody); + } + else + { + yyterminate(); + } + } %% //---------------------------------------------------------------------------- @@ -7250,7 +7295,7 @@ static void prependScope(yyscan_t yyscanner) static bool checkForKnRstyleC(yyscan_t yyscanner) { struct yyguts_t *yyg = (struct yyguts_t*)yyscanner; - if (((QCString)yyextra->fileName).right(2).lower()!=".c") return FALSE; // must be a C file + if (!yyextra->fileName.lower().endsWith(".c")) return FALSE; // must be a C file if (yyextra->current->argList.empty()) return FALSE; // must have arguments for (const Argument &a : yyextra->current->argList) { @@ -7361,10 +7406,7 @@ static void addKnRArgInfo(yyscan_t yyscanner,const QCString &type,const QCString if (a.type==name) { a.type=type.stripWhiteSpace(); - if (a.type.left(9)=="register ") // strip keyword - { - a.type=a.type.mid(9); - } + a.type.stripPrefix("register "); a.name=name.stripWhiteSpace(); if (!brief.isEmpty() && !docs.isEmpty()) { diff --git a/src/searchindex.cpp b/src/searchindex.cpp index d0bfd49..789a8eb 100644 --- a/src/searchindex.cpp +++ b/src/searchindex.cpp @@ -49,40 +49,7 @@ const size_t numIndexEntries = 256*256; //-------------------------------------------------------------------- -struct URL -{ - URL(QCString n,QCString u) : name(n), url(u) {} - QCString name; - QCString url; -}; - -struct URLInfo -{ - URLInfo(int idx,int f) : urlIdx(idx), freq(f) {} - int urlIdx; - int freq; -}; - -class IndexWord -{ - public: - using URLInfoMap = std::unordered_map<int,URLInfo>; - IndexWord(QCString word); - void addUrlIndex(int,bool); - URLInfoMap urls() const { return m_urls; } - QCString word() const { return m_word; } - - private: - QCString m_word; - URLInfoMap m_urls; -}; - -IndexWord::IndexWord(QCString word) : m_word(word) -{ - //printf("IndexWord::IndexWord(%s)\n",word); -} - -void IndexWord::addUrlIndex(int idx,bool hiPriority) +void SearchIndex::IndexWord::addUrlIndex(int idx,bool hiPriority) { //printf("IndexWord::addUrlIndex(%d,%d)\n",idx,hiPriority); auto it = m_urls.find(idx); @@ -97,23 +64,7 @@ void IndexWord::addUrlIndex(int idx,bool hiPriority) //-------------------------------------------------------------------- -class SearchIndex : public SearchIndexIntf -{ - public: - SearchIndex(); - void setCurrentDoc(const Definition *ctx,const QCString &anchor,bool isSourceFile) override; - void addWord(const QCString &word,bool hiPriority) override; - void write(const QCString &file) override; - private: - void addWord(const QCString &word,bool hiPrio,bool recurse); - std::unordered_map<std::string,int> m_words; - std::vector< std::vector< IndexWord> > m_index; - std::unordered_map<std::string,int> m_url2IdMap; - std::map<int,URL> m_urls; - int m_urlIndex = -1; -}; - -SearchIndex::SearchIndex() : SearchIndexIntf(Internal) +SearchIndex::SearchIndex() { m_index.resize(numIndexEntries); } @@ -232,7 +183,7 @@ static int charsToIndex(const QCString &word) return c1*256+c2; } -void SearchIndex::addWord(const QCString &word,bool hiPriority,bool recurse) +void SearchIndex::addWordRec(const QCString &word,bool hiPriority,bool recurse) { if (word.isEmpty()) return; QCString wStr = QCString(word).lower(); @@ -254,7 +205,7 @@ void SearchIndex::addWord(const QCString &word,bool hiPriority,bool recurse) i=getPrefixIndex(word); if (i>0) { - addWord(word.data()+i,hiPriority,TRUE); + addWordRec(word.data()+i,hiPriority,TRUE); found=TRUE; } } @@ -269,14 +220,14 @@ void SearchIndex::addWord(const QCString &word,bool hiPriority,bool recurse) } if (word[i]!=0 && i>=1) { - addWord(word.data()+i+1,hiPriority,TRUE); + addWordRec(word.data()+i+1,hiPriority,TRUE); } } } void SearchIndex::addWord(const QCString &word,bool hiPriority) { - addWord(word,hiPriority,FALSE); + addWordRec(word,hiPriority,FALSE); } static void writeInt(std::ostream &f,size_t index) @@ -450,36 +401,7 @@ void SIDataCollection::transfer() //--------------------------------------------------------------------------- // the following part is for writing an external search index -struct SearchDocEntry -{ - QCString type; - QCString name; - QCString args; - QCString extId; - QCString url; - GrowBuf importantText; - GrowBuf normalText; -}; - -class SearchIndexExternal : public SearchIndexIntf -{ - struct Private; - public: - SearchIndexExternal(); - void setCurrentDoc(const Definition *ctx,const QCString &anchor,bool isSourceFile) override; - void addWord(const QCString &word,bool hiPriority) override; - void write(const QCString &file) override; - private: - std::unique_ptr<Private> p; -}; - -struct SearchIndexExternal::Private -{ - std::map<std::string,SearchDocEntry> docEntries; - SearchDocEntry *current = 0; -}; - -SearchIndexExternal::SearchIndexExternal() : SearchIndexIntf(External), p(std::make_unique<Private>()) +SearchIndexExternal::SearchIndexExternal() { } @@ -548,8 +470,8 @@ void SearchIndexExternal::setCurrentDoc(const Definition *ctx,const QCString &an if (!anchor.isEmpty()) url+=QCString("#")+anchor; QCString key = extId+";"+url; - auto it = p->docEntries.find(key.str()); - if (it == p->docEntries.end()) + auto it = m_docEntries.find(key.str()); + if (it == m_docEntries.end()) { SearchDocEntry e; e.type = isSourceFile ? QCString("source") : definitionToName(ctx); @@ -560,16 +482,16 @@ void SearchIndexExternal::setCurrentDoc(const Definition *ctx,const QCString &an } e.extId = extId; e.url = url; - it = p->docEntries.insert({key.str(),e}).first; + it = m_docEntries.insert({key.str(),e}).first; //printf("searchIndexExt %s : %s\n",qPrint(e->name),qPrint(e->url)); } - p->current = &it->second; + m_current = &it->second; } void SearchIndexExternal::addWord(const QCString &word,bool hiPriority) { - if (word.isEmpty() || !isId(word[0]) || p->current==0) return; - GrowBuf *pText = hiPriority ? &p->current->importantText : &p->current->normalText; + if (word.isEmpty() || !isId(word[0]) || m_current==0) return; + GrowBuf *pText = hiPriority ? &m_current->importantText : &m_current->normalText; if (pText->getPos()>0) pText->addChar(' '); pText->addStr(word); //printf("addWord %s\n",word); @@ -582,7 +504,7 @@ void SearchIndexExternal::write(const QCString &fileName) { t << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; t << "<add>\n"; - for (auto &kv : p->docEntries) + for (auto &kv : m_docEntries) { SearchDocEntry &doc = kv.second; doc.normalText.addChar(0); // make sure buffer ends with a 0 terminator @@ -620,14 +542,7 @@ void initSearchIndexer() bool externalSearch = Config_getBool(EXTERNAL_SEARCH); if (searchEngine && serverBasedSearch) { - if (externalSearch) // external tools produce search index and engine - { - Doxygen::searchIndex = new SearchIndexExternal; - } - else // doxygen produces search index and engine - { - Doxygen::searchIndex = new SearchIndex; - } + Doxygen::searchIndex = new SearchIndexIntf(externalSearch ? SearchIndexIntf::External : SearchIndexIntf::Internal); } else // no search engine or pure javascript based search function { diff --git a/src/searchindex.h b/src/searchindex.h index ad79858..428f36b 100644 --- a/src/searchindex.h +++ b/src/searchindex.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright (C) 1997-2020 by Dimitri van Heesch. + * Copyright (C) 1997-2022 by Dimitri van Heesch. * * Permission to use, copy, modify, and distribute this software and its * documentation under the terms of the GNU General Public License is hereby @@ -24,11 +24,15 @@ #include <memory> #include <vector> +#include <map> +#include <unordered_map> #include <string> #include <array> #include <variant> #include "qcstring.h" +#include "growbuf.h" +#include "dispatcher.h" class Definition; class SearchIndexIntf; @@ -81,18 +85,112 @@ class SIDataCollection //----------------------------- +/** Writes search index for doxygen provided server based search engine that uses PHP. */ +class SearchIndex +{ + struct URL + { + URL(QCString n,QCString u) : name(n), url(u) {} + QCString name; + QCString url; + }; + + struct URLInfo + { + URLInfo(int idx,int f) : urlIdx(idx), freq(f) {} + int urlIdx; + int freq; + }; + + class IndexWord + { + public: + using URLInfoMap = std::unordered_map<int,URLInfo>; + IndexWord(QCString word) : m_word(word) {} + void addUrlIndex(int,bool); + URLInfoMap urls() const { return m_urls; } + QCString word() const { return m_word; } + + private: + QCString m_word; + URLInfoMap m_urls; + }; + + public: + SearchIndex(); + void setCurrentDoc(const Definition *ctx,const QCString &anchor,bool isSourceFile); + void addWord(const QCString &word,bool hiPriority); + void write(const QCString &file); + private: + void addWordRec(const QCString &word,bool hiPrio,bool recurse); + std::unordered_map<std::string,int> m_words; + std::vector< std::vector< IndexWord> > m_index; + std::unordered_map<std::string,int> m_url2IdMap; + std::map<int,URL> m_urls; + int m_urlIndex = -1; +}; + +/** Writes search index that should be used with an externally provided search engine, + * e.g. doxyindexer and doxysearch.cgi. + */ +class SearchIndexExternal +{ + struct SearchDocEntry + { + QCString type; + QCString name; + QCString args; + QCString extId; + QCString url; + GrowBuf importantText; + GrowBuf normalText; + }; + + public: + SearchIndexExternal(); + void setCurrentDoc(const Definition *ctx,const QCString &anchor,bool isSourceFile); + void addWord(const QCString &word,bool hiPriority); + void write(const QCString &file); + private: + std::map<std::string,SearchDocEntry> m_docEntries; + SearchDocEntry *m_current = 0; +}; + +namespace SearchIndexMethods +{ + template <class T> struct setCurrentDoc { static constexpr auto method = &T::setCurrentDoc; }; + template <class T> struct addWord { static constexpr auto method = &T::addWord; }; + template <class T> struct write { static constexpr auto method = &T::write; }; +} + +/** Abstract proxy interface for non-javascript based search indices. + * It forwards calls to either SearchIndex or SearchIndexExternal depending + * on the Kind passed during construction. + */ class SearchIndexIntf { public: + using SearchIndexVariant = std::variant<SearchIndex,SearchIndexExternal>; enum Kind { Internal, External }; - SearchIndexIntf(Kind k) : m_kind(k) {} - virtual ~SearchIndexIntf() = default; - virtual void setCurrentDoc(const Definition *ctx,const QCString &anchor,bool isSourceFile) = 0; - virtual void addWord(const QCString &word,bool hiPriority) = 0; - virtual void write(const QCString &file) = 0; + SearchIndexIntf(Kind k) : m_kind(k), + m_variant(k==Internal ? SearchIndexVariant(SearchIndex()) : + SearchIndexVariant(SearchIndexExternal())) { } + void setCurrentDoc(const Definition *ctx,const QCString &anchor,bool isSourceFile) + { + dispatch_call<SearchIndexMethods::setCurrentDoc>(m_variant,ctx,anchor,isSourceFile); + } + void addWord(const QCString &word,bool hiPriority) + { + dispatch_call<SearchIndexMethods::addWord>(m_variant,word,hiPriority); + } + void write(const QCString &file) + { + dispatch_call<SearchIndexMethods::write>(m_variant,file); + } Kind kind() const { return m_kind; } private: Kind m_kind; + SearchIndexVariant m_variant; }; diff --git a/src/searchindex_js.cpp b/src/searchindex_js.cpp index ad1ef98..4486712 100644 --- a/src/searchindex_js.cpp +++ b/src/searchindex_js.cpp @@ -32,6 +32,7 @@ #include "version.h" #include "message.h" #include "resourcemgr.h" +#include "indexlist.h" QCString searchName(const Definition *d) { @@ -406,56 +407,11 @@ void writeJavaScriptSearchIndex() QCString baseName; baseName.sprintf("%s_%x",sii.name.data(),p); - QCString fileName = searchDirName + "/"+baseName+Doxygen::htmlFileExtension; QCString dataFileName = searchDirName + "/"+baseName+".js"; - std::ofstream t(fileName.str(), std::ofstream::out | std::ofstream::binary); std::ofstream ti(dataFileName.str(), std::ofstream::out | std::ofstream::binary); - if (t.is_open() && ti.is_open()) + if (ti.is_open()) { - { - t << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"" - " \"https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; - t << "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"; - t << "<head><title></title>\n"; - t << "<meta http-equiv=\"Content-Type\" content=\"text/xhtml;charset=UTF-8\"/>\n"; - t << "<meta name=\"generator\" content=\"Doxygen " << getDoxygenVersion() << "\"/>\n"; - t << "<link rel=\"stylesheet\" type=\"text/css\" href=\"search.css\"/>\n"; - t << "<script type=\"text/javascript\" src=\"" << baseName << ".js\"></script>\n"; - t << "<script type=\"text/javascript\" src=\"search.js\"></script>\n"; - t << "</head>\n"; - t << "<body class=\"SRPage\">\n"; - t << "<div id=\"SRIndex\">\n"; - t << "<div class=\"SRStatus\" id=\"Loading\">" << theTranslator->trLoading() << "</div>\n"; - t << "<div id=\"SRResults\"></div>\n"; // here the results will be inserted - t << "<script type=\"text/javascript\">\n"; - t << "/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */\n"; - t << "createResults();\n"; // this function will insert the results - t << "/* @license-end */\n"; - t << "</script>\n"; - t << "<div class=\"SRStatus\" id=\"Searching\">" - << theTranslator->trSearching() << "</div>\n"; - t << "<div class=\"SRStatus\" id=\"NoMatches\">" - << theTranslator->trNoMatches() << "</div>\n"; - - t << "<script type=\"text/javascript\">\n"; - t << "/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&dn=expat.txt MIT */\n"; - t << "document.getElementById(\"Loading\").style.display=\"none\";\n"; - t << "document.getElementById(\"NoMatches\").style.display=\"none\";\n"; - t << "var searchResults = new SearchResults(\"searchResults\");\n"; - t << "searchResults.Search();\n"; - t << "window.addEventListener(\"message\", function(event) {\n"; - t << " if (event.data == \"take_focus\") {\n"; - t << " var elem = searchResults.NavNext(0);\n"; - t << " if (elem) elem.focus();\n"; - t << " }\n"; - t << "});\n"; - t << "/* @license-end */\n"; - t << "</script>\n"; - t << "</div>\n"; // SRIndex - t << "</body>\n"; - t << "</html>\n"; - } ti << "var searchData=\n"; // format @@ -619,7 +575,7 @@ void writeJavaScriptSearchIndex() } else { - err("Failed to open file '%s' for writing...\n",qPrint(fileName)); + err("Failed to open file '%s' for writing...\n",qPrint(dataFileName)); } p++; } @@ -683,29 +639,6 @@ void writeJavaScriptSearchIndex() ResourceMgr::instance().copyResource("search.js",searchDirName); } - { - QCString noMatchesFileName =searchDirName+"/nomatches"+Doxygen::htmlFileExtension; - std::ofstream t(noMatchesFileName.str(), std::ofstream::out | std::ofstream::binary); - if (t.is_open()) - { - t << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" " - "\"https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; - t << "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"; - t << "<head><title></title>\n"; - t << "<meta http-equiv=\"Content-Type\" content=\"text/xhtml;charset=UTF-8\"/>\n"; - t << "<link rel=\"stylesheet\" type=\"text/css\" href=\"search.css\"/>\n"; - t << "<script type=\"text/javascript\" src=\"search.js\"></script>\n"; - t << "</head>\n"; - t << "<body class=\"SRPage\">\n"; - t << "<div id=\"SRIndex\">\n"; - t << "<div class=\"SRStatus\" id=\"NoMatches\">" - << theTranslator->trNoMatches() << "</div>\n"; - t << "</div>\n"; - t << "</body>\n"; - t << "</html>\n"; - } - } - Doxygen::indexList->addStyleSheetFile("search/search.js"); } diff --git a/src/symbolmap.h b/src/symbolmap.h index 63e1e3a..1c46e89 100644 --- a/src/symbolmap.h +++ b/src/symbolmap.h @@ -17,7 +17,7 @@ #define SYMBOLMAP_H #include <algorithm> -#include <map> +#include <unordered_map> #include <vector> #include <string> #include <utility> @@ -31,38 +31,54 @@ class SymbolMap { public: using Ptr = T *; - using Map = std::multimap<std::string,Ptr>; + using VectorPtr = std::vector<Ptr>; + using Map = std::unordered_map<std::string,VectorPtr>; using iterator = typename Map::iterator; using const_iterator = typename Map::const_iterator; //! Add a symbol \a def into the map under key \a name void add(const QCString &name,Ptr def) { - m_map.insert({name.str(),def}); + auto it = m_map.find(name.str()); + if (it!=m_map.end()) + { + it->second.push_back(def); + } + else + { + m_map.emplace(std::make_pair(name.str(),VectorPtr({def}))); + } } //! Remove a symbol \a def from the map that was stored under key \a name void remove(const QCString &name,Ptr def) { - auto range = find(name); - for (auto it=range.first; it!=range.second; ) + VectorPtr &v = find(name); + auto it = std::find(v.begin(),v.end(),def); + if (it!=v.end()) { - if (it->second==def) it = m_map.erase(it); else ++it; + v.erase(it); + if (v.empty()) + { + m_map.erase(name.str()); + } } } //! Find the list of symbols stored under key \a name //! Returns a pair of iterators pointing to the start and end of the range of matching symbols - std::pair<const_iterator,const_iterator> find(const QCString &name) const + const VectorPtr &find(const QCString &name) const { - return m_map.equal_range(name.str()); + auto it = m_map.find(name.str()); + return it==m_map.end() ? m_noMatch : it->second; } //! Find the list of symbols stored under key \a name //! Returns a pair of iterators pointing to the start and end of the range of matching symbols - std::pair<iterator,iterator> find(const QCString &name) + VectorPtr &find(const QCString &name) { - return m_map.equal_range(name.str()); + auto it = m_map.find(name.str()); + return it==m_map.end() ? m_noMatch : it->second; } iterator begin() { return m_map.begin(); } @@ -70,10 +86,10 @@ class SymbolMap const_iterator begin() const { return m_map.cbegin(); } const_iterator end() const { return m_map.cend(); } bool empty() const { return m_map.empty(); } - size_t size() const { return m_map.size(); } private: Map m_map; + VectorPtr m_noMatch; }; #endif diff --git a/src/symbolresolver.cpp b/src/symbolresolver.cpp index cf7fea1..9bc618e 100644 --- a/src/symbolresolver.cpp +++ b/src/symbolresolver.cpp @@ -37,7 +37,8 @@ class AccessStack /** Element in the stack. */ struct AccessElem { - AccessElem(const Definition *d,const FileDef *f,const Definition *i,QCString e = QCString()) : scope(d), fileScope(f), item(i), expScope(e) {} + AccessElem(const Definition *d,const FileDef *f,const Definition *i) : scope(d), fileScope(f), item(i) {} + AccessElem(const Definition *d,const FileDef *f,const Definition *i,const QCString &e) : scope(d), fileScope(f), item(i), expScope(e) {} const Definition *scope; const FileDef *fileScope; const Definition *item; @@ -103,9 +104,18 @@ struct SymbolResolver::Private const MemberDef *typeDef = 0; QCString templateSpec; - const ClassDef *getResolvedClassRec( + const ClassDef *getResolvedTypeRec( const Definition *scope, // in - const QCString &n, // in + const QCString &n, // in + const MemberDef **pTypeDef, // out + QCString *pTemplSpec, // out + QCString *pResolvedType); // out + // + const Definition *getResolvedSymbolRec( + const Definition *scope, // in + const QCString &n, // in + const QCString &args, // in + bool checkCV, // in const MemberDef **pTypeDef, // out QCString *pTemplSpec, // out QCString *pResolvedType); // out @@ -122,7 +132,7 @@ struct SymbolResolver::Private const QCString &explicitScopePart); private: - void getResolvedSymbol(const Definition *scope, // in + void getResolvedType( const Definition *scope, // in const Definition *d, // in const QCString &explicitScopePart, // in const std::unique_ptr<ArgumentList> &actTemplParams, // in @@ -131,6 +141,19 @@ struct SymbolResolver::Private const MemberDef *&bestTypedef, // out QCString &bestTemplSpec, // out QCString &bestResolvedType // out + ); + + void getResolvedSymbol(const Definition *scope, // in + const Definition *d, // in + const QCString &args, // in + bool checkCV, // in + const QCString &explicitScopePart, // in + const std::unique_ptr<ArgumentList> &actTemplParams, // in + int &minDistance, // inout + const Definition *&bestMatch, // out + const MemberDef *&bestTypedef, // out + QCString &bestTemplSpec, // out + QCString &bestResolvedType // out ); const ClassDef *newResolveTypedef( @@ -164,7 +187,7 @@ struct SymbolResolver::Private -const ClassDef *SymbolResolver::Private::getResolvedClassRec( +const ClassDef *SymbolResolver::Private::getResolvedTypeRec( const Definition *scope, const QCString &n, const MemberDef **pTypeDef, @@ -173,13 +196,10 @@ const ClassDef *SymbolResolver::Private::getResolvedClassRec( { if (n.isEmpty()) return 0; //static int level=0; - //fprintf(stderr,"%d [getResolvedClassRec(%s,%s)\n",level++,scope?qPrint(scope->name()):"<global>",n); - QCString name; + //printf("\n%d [getResolvedTypeRec(%s,%s)\n",level++,scope?qPrint(scope->name()):"<global>",qPrint(n)); QCString explicitScopePart; QCString strippedTemplateParams; - name=stripTemplateSpecifiersFromScope - (removeRedundantWhiteSpace(n),TRUE, - &strippedTemplateParams); + QCString name=stripTemplateSpecifiersFromScope(n,TRUE,&strippedTemplateParams); std::unique_ptr<ArgumentList> actTemplParams; if (!strippedTemplateParams.isEmpty()) // template part that was stripped { @@ -199,24 +219,17 @@ const ClassDef *SymbolResolver::Private::getResolvedClassRec( if (name.isEmpty()) { - //fprintf(stderr,"%d ] empty name\n",--level); + //printf("%d ] empty name\n",--level); return 0; // empty name } - //printf("Looking for symbol %s\n",qPrint(name)); - auto range = Doxygen::symbolMap->find(name); - // the -g (for C# generics) and -p (for ObjC protocols) are now already - // stripped from the key used in the symbolMap, so that is not needed here. - if (range.first==range.second) + //printf("Looking for type %s\n",qPrint(name)); + auto &range = Doxygen::symbolMap->find(name); + if (range.empty()) { - range = Doxygen::symbolMap->find(name+"-p"); - if (range.first==range.second) - { - //fprintf(stderr,"%d ] no such symbol!\n",--level); - return 0; - } + return 0; } - //printf("found symbol!\n"); + //printf("found type!\n"); bool hasUsingStatements = (m_fileScope && (!m_fileScope->getUsedNamespaces().empty() || @@ -235,7 +248,7 @@ const ClassDef *SymbolResolver::Private::getResolvedClassRec( int fileScopeLen = hasUsingStatements ? 1+m_fileScope->absFilePath().length() : 0; // below is a more efficient coding of - // QCString key=scope->name()+"+"+name+"+"+explicitScopePart; + // QCString key=scope->name()+"+"+name+"+"+explicitScopePart+args+typesOnly?'T':'F'; QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+1); char *pk=key.rawData(); qstrcpy(pk,scope->name().data()); *(pk+scopeNameLen-1)='+'; @@ -261,8 +274,8 @@ const ClassDef *SymbolResolver::Private::getResolvedClassRec( { std::lock_guard<std::mutex> lock(g_cacheMutex); - LookupInfo *pval = Doxygen::lookupCache->find(key.str()); - //printf("Searching for %s result=%p\n",qPrint(key),pval); + LookupInfo *pval = Doxygen::typeLookupCache->find(key.str()); + //printf("Searching for %s result=%p\n",qPrint(key),(void*)pval); if (pval) { //printf("LookupInfo %p %p '%s' %p\n", @@ -271,16 +284,16 @@ const ClassDef *SymbolResolver::Private::getResolvedClassRec( if (pTemplSpec) *pTemplSpec=pval->templSpec; if (pTypeDef) *pTypeDef=pval->typeDef; if (pResolvedType) *pResolvedType=pval->resolvedType; - //fprintf(stderr,"%d ] cachedMatch=%s\n",--level, - // pval->classDef?qPrint(pval->classDef->name()):"<none>"); + //printf("%d ] cachedMatch=%s\n",--level, + // pval->definition?qPrint(pval->definition->name()):"<none>"); //if (pTemplSpec) // printf("templSpec=%s\n",pTemplSpec->data()); - return pval->classDef; + return toClassDef(pval->definition); } else // not found yet; we already add a 0 to avoid the possibility of // endless recursion. { - Doxygen::lookupCache->insert(key.str(),LookupInfo()); + Doxygen::typeLookupCache->insert(key.str(),LookupInfo()); } } @@ -290,11 +303,184 @@ const ClassDef *SymbolResolver::Private::getResolvedClassRec( QCString bestResolvedType; int minDistance=10000; // init at "infinite" - for (auto it=range.first ; it!=range.second; ++it) + for (Definition *d : range) { - Definition *d = it->second; - getResolvedSymbol(scope,d,explicitScopePart,actTemplParams, + getResolvedType(scope,d,explicitScopePart,actTemplParams, + minDistance,bestMatch,bestTypedef,bestTemplSpec,bestResolvedType); + if (minDistance==0) break; // we can stop reaching if we already reached distance 0 + } + + if (pTypeDef) + { + *pTypeDef = bestTypedef; + } + if (pTemplSpec) + { + *pTemplSpec = bestTemplSpec; + } + if (pResolvedType) + { + *pResolvedType = bestResolvedType; + } + + //printf("getResolvedSymbolRec: bestMatch=%p pval->resolvedType=%s\n", + // bestMatch,qPrint(bestResolvedType)); + + { + // we need to insert the item in the cache again, as it could be removed in the meantime + std::lock_guard<std::mutex> lock(g_cacheMutex); + Doxygen::typeLookupCache->insert(key.str(), + LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType)); + } + //printf("%d ] bestMatch=%s distance=%d\n",--level, + // bestMatch?qPrint(bestMatch->name()):"<none>",minDistance); + //if (pTemplSpec) + // printf("templSpec=%s\n",pTemplSpec->data()); + return bestMatch; +} + +const Definition *SymbolResolver::Private::getResolvedSymbolRec( + const Definition *scope, + const QCString &n, + const QCString &args, + bool checkCV, + const MemberDef **pTypeDef, + QCString *pTemplSpec, + QCString *pResolvedType) +{ + if (n.isEmpty()) return 0; + //static int level=0; + //printf("\n%d [getResolvedSymbolRec(%s,%s)\n",level++,scope?qPrint(scope->name()):"<global>",qPrint(n)); + QCString explicitScopePart; + QCString strippedTemplateParams; + QCString name=stripTemplateSpecifiersFromScope(n,TRUE,&strippedTemplateParams); + std::unique_ptr<ArgumentList> actTemplParams; + if (!strippedTemplateParams.isEmpty()) // template part that was stripped + { + actTemplParams = stringToArgumentList(scope->getLanguage(),strippedTemplateParams); + } + + int qualifierIndex = computeQualifiedIndex(name); + //printf("name=%s qualifierIndex=%d\n",qPrint(name),qualifierIndex); + if (qualifierIndex!=-1) // qualified name + { + // split off the explicit scope part + explicitScopePart=name.left(qualifierIndex); + // todo: improve namespace alias substitution + replaceNamespaceAliases(explicitScopePart,explicitScopePart.length()); + name=name.mid(qualifierIndex+2); + } + + if (name.isEmpty()) + { + //printf("%d ] empty name\n",--level); + return 0; // empty name + } + + //printf("Looking for symbol %s\n",qPrint(name)); + auto &range = Doxygen::symbolMap->find(name); + if (range.empty()) + { + //printf("%d ] not symbols\n",--level); + return 0; + } + //printf("found symbol %zu times!\n",range.size()); + + bool hasUsingStatements = + (m_fileScope && (!m_fileScope->getUsedNamespaces().empty() || + !m_fileScope->getUsedClasses().empty()) + ); + //printf("hasUsingStatements=%d\n",hasUsingStatements); + // Since it is often the case that the same name is searched in the same + // scope over an over again (especially for the linked source code generation) + // we use a cache to collect previous results. This is possible since the + // result of a lookup is deterministic. As the key we use the concatenated + // scope, the name to search for and the explicit scope prefix. The speedup + // achieved by this simple cache can be enormous. + int scopeNameLen = scope->name().length()+1; + int nameLen = name.length()+1; + int explicitPartLen = explicitScopePart.length(); + int fileScopeLen = hasUsingStatements ? 1+m_fileScope->absFilePath().length() : 0; + int argsLen = args.length()+1; + + // below is a more efficient coding of + // QCString key=scope->name()+"+"+name+"+"+explicitScopePart+args+typesOnly?'T':'F'; + QCString key(scopeNameLen+nameLen+explicitPartLen+fileScopeLen+argsLen+1); + char *pk=key.rawData(); + qstrcpy(pk,scope->name().data()); *(pk+scopeNameLen-1)='+'; + pk+=scopeNameLen; + qstrcpy(pk,name.data()); *(pk+nameLen-1)='+'; + pk+=nameLen; + qstrcpy(pk,explicitScopePart.data()); + pk+=explicitPartLen; + + // if a file scope is given and it contains using statements we should + // also use the file part in the key (as a class name can be in + // two different namespaces and a using statement in a file can select + // one of them). + if (hasUsingStatements) + { + // below is a more efficient coding of + // key+="+"+m_fileScope->name(); + *pk++='+'; + qstrcpy(pk,m_fileScope->absFilePath().data()); + pk+=fileScopeLen-1; + } + if (argsLen>0) + { + qstrcpy(pk,args.data()); + pk+=argsLen-1; + } + *pk='\0'; + + { + std::lock_guard<std::mutex> lock(g_cacheMutex); + LookupInfo *pval = Doxygen::symbolLookupCache->find(key.str()); + //printf("Searching for %s result=%p\n",qPrint(key),(void*)pval); + if (pval) + { + //printf("LookupInfo %p %p '%s' %p\n", + // pval->classDef, pval->typeDef, qPrint(pval->templSpec), + // qPrint(pval->resolvedType)); + if (pTemplSpec) *pTemplSpec=pval->templSpec; + if (pTypeDef) *pTypeDef=pval->typeDef; + if (pResolvedType) *pResolvedType=pval->resolvedType; + //printf("%d ] cachedMatch=%s\n",--level, + // pval->definition?qPrint(pval->definition->name()):"<none>"); + //if (pTemplSpec) + // printf("templSpec=%s\n",pTemplSpec->data()); + return pval->definition; + } + else // not found yet; we already add a 0 to avoid the possibility of + // endless recursion. + { + Doxygen::symbolLookupCache->insert(key.str(),LookupInfo()); + } + } + + const Definition *bestMatch=0; + const MemberDef *bestTypedef=0; + QCString bestTemplSpec; + QCString bestResolvedType; + int minDistance=10000; // init at "infinite" + + for (Definition *d : range) + { + getResolvedSymbol(scope,d,args,checkCV,explicitScopePart,actTemplParams, + minDistance,bestMatch,bestTypedef,bestTemplSpec,bestResolvedType); + if (minDistance==0) break; // we can stop reaching if we already reached distance 0 + } + + // in case we are looking for e.g. func() and the real function is func(int x) we also + // accept func(), see example 036 in the test set. + if (bestMatch==0 && args=="()") + { + for (Definition *d : range) + { + getResolvedSymbol(scope,d,QCString(),false,explicitScopePart,actTemplParams, minDistance,bestMatch,bestTypedef,bestTemplSpec,bestResolvedType); + if (minDistance==0) break; // we can stop reaching if we already reached distance 0 + } } if (pTypeDef) @@ -310,23 +496,23 @@ const ClassDef *SymbolResolver::Private::getResolvedClassRec( *pResolvedType = bestResolvedType; } - //printf("getResolvedClassRec: bestMatch=%p pval->resolvedType=%s\n", + //printf("getResolvedSymbolRec: bestMatch=%p pval->resolvedType=%s\n", // bestMatch,qPrint(bestResolvedType)); { // we need to insert the item in the cache again, as it could be removed in the meantime std::lock_guard<std::mutex> lock(g_cacheMutex); - Doxygen::lookupCache->insert(key.str(), + Doxygen::symbolLookupCache->insert(key.str(), LookupInfo(bestMatch,bestTypedef,bestTemplSpec,bestResolvedType)); } - //fprintf(stderr,"%d ] bestMatch=%s distance=%d\n",--level, + //printf("%d ] bestMatch=%s distance=%d\n",--level, // bestMatch?qPrint(bestMatch->name()):"<none>",minDistance); //if (pTemplSpec) // printf("templSpec=%s\n",pTemplSpec->data()); return bestMatch; } -void SymbolResolver::Private::getResolvedSymbol( +void SymbolResolver::Private::getResolvedType( const Definition *scope, // in const Definition *d, // in const QCString &explicitScopePart, // in @@ -338,7 +524,7 @@ void SymbolResolver::Private::getResolvedSymbol( QCString &bestResolvedType // out ) { - //fprintf(stderr,"getResolvedSymbol(%s,%s)\n",qPrint(scope->name()),qPrint(d->qualifiedName())); + //fprintf(stderr,"getResolvedType(%s,%s)\n",qPrint(scope->name()),qPrint(d->qualifiedName())); // only look at classes and members that are enums or typedefs if (d->definitionType()==Definition::TypeClass || (d->definitionType()==Definition::TypeMember && @@ -485,6 +671,124 @@ void SymbolResolver::Private::getResolvedSymbol( //printf(" bestMatch=%p bestResolvedType=%s\n",bestMatch,qPrint(bestResolvedType)); } + +void SymbolResolver::Private::getResolvedSymbol( + const Definition *scope, // in + const Definition *d, // in + const QCString &args, // in + bool checkCV, // in + const QCString &explicitScopePart, // in + const std::unique_ptr<ArgumentList> &actTemplParams, // in + int &minDistance, // inout + const Definition *&bestMatch, // out + const MemberDef *&bestTypedef, // out + QCString &bestTemplSpec, // out + QCString &bestResolvedType // out + ) +{ + //fprintf(stderr,"getResolvedSymbol(%s,%s)\n",qPrint(scope->name()),qPrint(d->qualifiedName())); + // only look at classes and members that are enums or typedefs + VisitedNamespaces visitedNamespaces; + AccessStack accessStack; + // test accessibility of definition within scope. + int distance = isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope,d,explicitScopePart); + //fprintf(stderr," %s; distance %s (%p) is %d\n",qPrint(scope->name()),qPrint(d->name()),d,distance); + //printf("%s: distance=%d scope=%s explScope=%s\n",qPrint(d->name()),distance,qPrint(scope?scope->name():QCString()),qPrint(explicitScopePart)); + if (distance!=-1) // definition is accessible + { + // see if we are dealing with a class or a typedef + if (d->definitionType()==Definition::TypeClass) // d is a class + { + const ClassDef *cd = toClassDef(d); + //printf("cd=%s\n",qPrint(cd->name())); + if (!cd->isTemplateArgument()) // skip classes that + // are only there to + // represent a template + // argument + { + //printf("is not a templ arg\n"); + if (distance<minDistance) // found a definition that is "closer" + { + minDistance=distance; + bestMatch = d; + bestTypedef = 0; + bestTemplSpec.resize(0); + bestResolvedType = cd->qualifiedName(); + } + else if (distance==minDistance && + m_fileScope && bestMatch && + !m_fileScope->getUsedNamespaces().empty() && + d->getOuterScope()->definitionType()==Definition::TypeNamespace && + bestMatch->getOuterScope()==Doxygen::globalScope + ) + { + // in case the distance is equal it could be that a class X + // is defined in a namespace and in the global scope. When searched + // in the global scope the distance is 0 in both cases. We have + // to choose one of the definitions: we choose the one in the + // namespace if the fileScope imports namespaces and the definition + // found was in a namespace while the best match so far isn't. + // Just a non-perfect heuristic but it could help in some situations + // (kdecore code is an example). + minDistance=distance; + bestMatch = d; + bestTypedef = 0; + bestTemplSpec.resize(0); + bestResolvedType = cd->qualifiedName(); + } + } + else + { + //printf(" is a template argument!\n"); + } + } + else if (d->definitionType()==Definition::TypeMember) + { + const MemberDef *md = toMemberDef(d); + + bool match = true; + //printf("@@ checking %s\n",qPrint(md->name())); + if (md->isFunction() && !args.isEmpty()) + { + std::unique_ptr<ArgumentList> argList = stringToArgumentList(md->getLanguage(),args); + const ArgumentList &mdAl = md->argumentList(); + match = matchArguments2(md->getOuterScope(),md->getFileDef(),&mdAl, + scope, md->getFileDef(),argList.get(), + checkCV,md->getLanguage()); + //printf("@@ %s (%p): matching %s against %s -> %d\n",qPrint(md->name()),(void*)md,qPrint(args),qPrint(argListToString(mdAl)),match); + } + + //fprintf(stderr," member isTypedef()=%d\n",md->isTypedef()); + if (match && distance<minDistance) + { + minDistance=distance; + bestMatch = md; + bestTypedef = md; + bestTemplSpec = ""; + bestResolvedType = md->qualifiedName(); + } + } + else if ((d->definitionType()==Definition::TypeNamespace || + d->definitionType()==Definition::TypeFile)) + { + if (distance<minDistance) // found a definition that is "closer" + { + minDistance=distance; + bestMatch = d; + bestTypedef = 0; + bestTemplSpec.resize(0); + bestResolvedType.resize(0); + } + } + } // if definition accessible + else + { + //printf(" Not accessible!\n"); + } + //printf("bestMatch=%s bestResolvedType=%s\n",qPrint(bestMatch?bestMatch->name():"<none>"),qPrint(bestResolvedType)); +} + + const ClassDef *SymbolResolver::Private::newResolveTypedef( const Definition *scope, // in const MemberDef *md, // in @@ -540,7 +844,7 @@ const ClassDef *SymbolResolver::Private::newResolveTypedef( tl=type.length(); // length may have been changed while (sp<tl && type.at(sp)==' ') sp++; const MemberDef *memTypeDef = 0; - const ClassDef *result = getResolvedClassRec(md->getOuterScope(),type, + const ClassDef *result = getResolvedTypeRec(md->getOuterScope(),type, &memTypeDef,0,pResolvedType); // if type is a typedef then return what it resolves to. if (memTypeDef && memTypeDef->isTypedef()) @@ -562,7 +866,7 @@ const ClassDef *SymbolResolver::Private::newResolveTypedef( if (si==-1 && i!=-1) // typedef of a template => try the unspecialized version { if (pTemplSpec) *pTemplSpec = type.mid(i); - result = getResolvedClassRec(md->getOuterScope(),type.left(i),0,0,pResolvedType); + result = getResolvedTypeRec(md->getOuterScope(),type.left(i),0,0,pResolvedType); //printf("result=%p pRresolvedType=%s sp=%d ip=%d tl=%d\n", // result,pResolvedType?pResolvedType->data():"<none>",sp,ip,tl); } @@ -577,7 +881,7 @@ const ClassDef *SymbolResolver::Private::newResolveTypedef( { if (pTemplSpec) *pTemplSpec = type.mid(i); } - result = getResolvedClassRec(md->getOuterScope(), + result = getResolvedTypeRec(md->getOuterScope(), stripTemplateSpecifiersFromScope(type.left(i),FALSE),0,0,pResolvedType); } @@ -587,7 +891,7 @@ const ClassDef *SymbolResolver::Private::newResolveTypedef( done: if (pResolvedType) { - if (result) + if (result && result->definitionType()==Definition::TypeClass) { *pResolvedType = result->qualifiedName(); //printf("*pResolvedType=%s\n",pResolvedType->data()); @@ -623,6 +927,15 @@ done: return result; } +#if 0 +static bool isParentScope(const Definition *parent,const Definition *item) +{ + if (parent==item || item==0 || item==Doxygen::globalScope) return false; + if (parent==0 || parent==Doxygen::globalScope) return true; + return isParentScope(parent->getOuterScope(),item); +} +#endif + int SymbolResolver::Private::isAccessibleFromWithExpScope( VisitedNamespaces &visitedNamespaces, AccessStack &accessStack, @@ -643,15 +956,28 @@ int SymbolResolver::Private::isAccessibleFromWithExpScope( accessStack.push(scope,m_fileScope,item,explicitScopePart); - //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?qPrint(scope->name()):"<global>", - // item?qPrint(item->name()):"<none>", + //printf(" <isAccessibleFromWithExpScope(%s,%s,%s)\n",scope?qPrint(scope->name()):"<global>", + // item?qPrint(item->qualifiedName()):"<none>", // qPrint(explicitScopePart)); int result=0; // assume we found it const Definition *newScope = followPath(scope,explicitScopePart); if (newScope) // explicitScope is inside scope => newScope is the result { Definition *itemScope = item->getOuterScope(); + //printf(" scope traversal successful %s<->%s!\n",qPrint(itemScope->name()),qPrint(newScope->name())); + + bool nestedClassInsideBaseClass = + itemScope && + itemScope->definitionType()==Definition::TypeClass && + newScope->definitionType()==Definition::TypeClass && + (toClassDef(newScope))->isBaseClass(toClassDef(itemScope),TRUE); + + bool enumValueWithinEnum = + item->definitionType()==Definition::TypeMember && + toMemberDef(item)->isEnumValue() && + toMemberDef(item)->getEnumScope()==newScope; + //if (newScope && newScope->definitionType()==Definition::TypeClass) //{ // ClassDef *cd = (ClassDef *)newScope; @@ -659,13 +985,9 @@ int SymbolResolver::Private::isAccessibleFromWithExpScope( //} if (itemScope==newScope) // exact match of scopes => distance==0 { - //printf("> found it\n"); + //printf(" > found it\n"); } - else if (itemScope && newScope && - itemScope->definitionType()==Definition::TypeClass && - newScope->definitionType()==Definition::TypeClass && - (toClassDef(newScope))->isBaseClass(toClassDef(itemScope),TRUE,0) - ) + else if (nestedClassInsideBaseClass) { // inheritance is also ok. Example: looking for B::I, where // class A { public: class I {} }; @@ -679,6 +1001,10 @@ int SymbolResolver::Private::isAccessibleFromWithExpScope( //printf("scope(%s) is base class of newScope(%s)\n", // qPrint(scope->name()),qPrint(newScope->name())); } + else if (enumValueWithinEnum) + { + result=1; + } else { int i=-1; @@ -712,6 +1038,16 @@ int SymbolResolver::Private::isAccessibleFromWithExpScope( } } } +#if 0 // this caused problems resolving A::f() in the docs when there was a A::f(int) but also a + // global function f() that exactly matched the argument list. + else if (isParentScope(scope,newScope) && newScope->definitionType()==Definition::TypeClass) + { + // if we a look for a type B and have explicit scope A, then it is also fine if B + // is found at the global scope. + result = 1; + goto done; + } +#endif // repeat for the parent scope if (scope!=Doxygen::globalScope) { @@ -723,14 +1059,14 @@ int SymbolResolver::Private::isAccessibleFromWithExpScope( } else // failed to resolve explicitScope { - //printf(" failed to resolve: scope=%s\n",qPrint(scope->name())); + //printf(" failed to resolve explicitScope=%s: scope=%s\n",qPrint(explicitScopePart), qPrint(scope->name())); if (scope->definitionType()==Definition::TypeNamespace) { const NamespaceDef *nscope = toNamespaceDef(scope); StringUnorderedSet visited; if (accessibleViaUsingNamespace(visited,nscope->getUsedNamespaces(),item,explicitScopePart)) { - //printf("> found in used namespace\n"); + //printf(" > found in used namespace\n"); goto done; } } @@ -741,23 +1077,23 @@ int SymbolResolver::Private::isAccessibleFromWithExpScope( StringUnorderedSet visited; if (accessibleViaUsingNamespace(visited,m_fileScope->getUsedNamespaces(),item,explicitScopePart)) { - //printf("> found in used namespace\n"); + //printf(" > found in used namespace\n"); goto done; } } - //printf("> not found\n"); + //printf(" > not found\n"); result=-1; } else // continue by looking into the parent scope { int i=isAccessibleFromWithExpScope(visitedNamespaces,accessStack,scope->getOuterScope(),item,explicitScopePart); - //printf("> result=%d\n",i); + //printf(" > result=%d\n",i); result= (i==-1) ? -1 : i+2; } } done: - //printf(" > result=%d\n",result); + //printf(" > result=%d\n",result); accessStack.pop(); return result; } @@ -775,7 +1111,7 @@ const Definition *SymbolResolver::Private::followPath(const Definition *start,co // try to resolve the part if it is a typedef const MemberDef *memTypeDef=0; QCString qualScopePart = substTypedef(current,path.mid(is,l),&memTypeDef); - //printf(" qualScopePart=%s\n",qPrint(qualScopePart)); + //printf(" qualScopePart=%s\n",qPrint(qualScopePart)); if (memTypeDef) { const ClassDef *type = newResolveTypedef(m_fileScope,memTypeDef,0,0,0); @@ -790,6 +1126,50 @@ const Definition *SymbolResolver::Private::followPath(const Definition *start,co // qPrint(qualScopePart), // qPrint(current->name()), // next?qPrint(next->name()):"<null>"); + if (next==0) + { + next = current->findInnerCompound(qualScopePart+"-p"); + } + if (current->definitionType()==Definition::TypeClass) + { + const MemberDef *classMember = toClassDef(current)->getMemberByName(qualScopePart); + if (classMember && classMember->isEnumerate()) + { + next = classMember; + } + } + else if (current->definitionType()==Definition::TypeNamespace) + { + const MemberDef *namespaceMember = toNamespaceDef(current)->getMemberByName(qualScopePart); + if (namespaceMember && namespaceMember->isEnumerate()) + { + next = namespaceMember; + } + } + else if (current==Doxygen::globalScope || current->definitionType()==Definition::TypeFile) + { + auto &range = Doxygen::symbolMap->find(qualScopePart); + for (Definition *def : range) + { + const Definition *outerScope = def->getOuterScope(); + if ( + (outerScope==Doxygen::globalScope || // global scope or + (outerScope && // anonymous namespace in the global scope + outerScope->name().startsWith("anonymous_namespace{") && + outerScope->getOuterScope()==Doxygen::globalScope + ) + ) && + (def->definitionType()==Definition::TypeClass || + def->definitionType()==Definition::TypeMember || + def->definitionType()==Definition::TypeNamespace + ) + ) + { + next=def; + break; + } + } + } if (next==0) // failed to follow the path { //printf("==> next==0!\n"); @@ -809,7 +1189,7 @@ const Definition *SymbolResolver::Private::followPath(const Definition *start,co else // continue to follow scope { current = next; - //printf("==> current = %p\n",current); + //printf("==> current = %p\n",(void*)current); } ps=is+l; } @@ -896,29 +1276,82 @@ int SymbolResolver::Private::isAccessibleFrom(AccessStack &accessStack, int result=0; // assume we found it int i; - Definition *itemScope=item->getOuterScope(); + const Definition *itemScope=item->getOuterScope(); + bool itemIsMember = item->definitionType()==Definition::TypeMember; + bool itemIsClass = item->definitionType()==Definition::TypeClass; + + // if item is a global member and scope points to a specific file + // we adjust the scope so the file gets preference over members with the same name in + // other files. + if ((itemIsMember || itemIsClass) && + (itemScope==Doxygen::globalScope || // global + (itemScope && itemScope->name().startsWith("anonymous_namespace{")) // member of an anonymous namespace + ) && + scope->definitionType()==Definition::TypeFile) + { + if (itemIsMember) + { + itemScope = toMemberDef(item)->getFileDef(); + } + else if (itemIsClass) + { + itemScope = toClassDef(item)->getFileDef(); + } + //printf("adjust scope to %s\n",qPrint(itemScope?itemScope->name():QCString())); + } + bool memberAccessibleFromScope = - (item->definitionType()==Definition::TypeMember && // a member + (itemIsMember && // a member itemScope && itemScope->definitionType()==Definition::TypeClass && // of a class scope->definitionType()==Definition::TypeClass && // accessible (toClassDef(scope))->isAccessibleMember(toMemberDef(item)) // from scope ); bool nestedClassInsideBaseClass = - (item->definitionType()==Definition::TypeClass && // a nested class + (itemIsClass && // a nested class itemScope && itemScope->definitionType()==Definition::TypeClass && // inside a base scope->definitionType()==Definition::TypeClass && // class of scope (toClassDef(scope))->isBaseClass(toClassDef(itemScope),TRUE) ); + bool enumValueOfStrongEnum = + (itemIsMember && + toMemberDef(item)->isStrongEnumValue() && + scope->definitionType()==Definition::TypeMember && + toMemberDef(scope)->isEnumerate() && + scope==toMemberDef(item)->getEnumScope() + ); - if (itemScope==scope || memberAccessibleFromScope || nestedClassInsideBaseClass) + if (itemScope==scope || memberAccessibleFromScope || nestedClassInsideBaseClass || enumValueOfStrongEnum) { - //printf("> found it\n"); - if (nestedClassInsideBaseClass) result++; // penalty for base class to prevent + //printf("> found it memberAccessibleFromScope=%d nestedClassInsideBaseClass=%d enumValueOfStrongEnum=%d\n",memberAccessibleFromScope,nestedClassInsideBaseClass,enumValueOfStrongEnum); + int distanceToBase=0; + if (nestedClassInsideBaseClass) + { + result++; // penalty for base class to prevent // this is preferred over nested class in this class // see bug 686956 + } + else if (memberAccessibleFromScope && + itemScope && + itemScope->definitionType()==Definition::TypeClass && + scope->definitionType()==Definition::TypeClass && + (distanceToBase=toClassDef(scope)->isBaseClass(toClassDef(itemScope),TRUE))>0 + ) + { + result+=distanceToBase; // penalty if member is accessible via a base class + } } else if (scope==Doxygen::globalScope) { + if (itemScope && + itemScope->definitionType()==Definition::TypeNamespace && + toNamespaceDef(itemScope)->isAnonymous() && + itemScope->getOuterScope()==Doxygen::globalScope) + { // item is in an anonymous namespace in the global scope and we are + // looking in the global scope + //printf("> found in anonymous namespace\n"); + result++; + goto done; + } if (m_fileScope) { if (accessibleViaUsingClass(m_fileScope->getUsedClasses(),item)) @@ -956,7 +1389,19 @@ int SymbolResolver::Private::isAccessibleFrom(AccessStack &accessStack, } } // repeat for the parent scope - i=isAccessibleFrom(accessStack,scope->getOuterScope(),item); + const Definition *parentScope = scope->getOuterScope(); + if (parentScope==Doxygen::globalScope) + { + if (scope->definitionType()==Definition::TypeClass) + { + const FileDef *fd = toClassDef(scope)->getFileDef(); + if (fd) + { + parentScope = fd; + } + } + } + i=isAccessibleFrom(accessStack,parentScope,item); //printf("> result=%d\n",i); result= (i==-1) ? -1 : i+2; } @@ -972,16 +1417,15 @@ QCString SymbolResolver::Private::substTypedef( QCString result=name; if (name.isEmpty()) return result; - auto range = Doxygen::symbolMap->find(name); - if (range.first==range.second) + auto &range = Doxygen::symbolMap->find(name); + if (range.empty()) return result; // no matches MemberDef *bestMatch=0; int minDistance=10000; // init at "infinite" - for (auto it = range.first; it!=range.second; ++it) + for (Definition *d : range) { - Definition *d = it->second; // only look at members if (d->definitionType()==Definition::TypeMember) { @@ -1049,14 +1493,14 @@ const ClassDef *SymbolResolver::resolveClass(const Definition *scope, // qPrint(name), // mayBeUnlinkable // ); - const ClassDef *result; + const ClassDef *result=0; if (Config_getBool(OPTIMIZE_OUTPUT_VHDL)) { result = getClass(name); } else { - result = p->getResolvedClassRec(scope,name,&p->typeDef,&p->templateSpec,&p->resolvedType); + result = p->getResolvedTypeRec(scope,name,&p->typeDef,&p->templateSpec,&p->resolvedType); if (result==0) // for nested classes imported via tag files, the scope may not // present, so we check the class name directly as well. // See also bug701314 @@ -1077,6 +1521,18 @@ const ClassDef *SymbolResolver::resolveClass(const Definition *scope, return result; } +const Definition *SymbolResolver::resolveSymbol(const Definition *scope, + const QCString &name, + const QCString &args, + bool checkCV) +{ + p->reset(); + if (scope==0) scope=Doxygen::globalScope; + const Definition *result = p->getResolvedSymbolRec(scope,name,args,checkCV,&p->typeDef,&p->templateSpec,&p->resolvedType); + //printf("resolveSymbol(%s,%s,%s,%d)=%s\n",qPrint(scope?scope->name():QCString()),qPrint(name),qPrint(args),checkCV,qPrint(result?result->qualifiedName():QCString())); + return result; +} + int SymbolResolver::isAccessibleFrom(const Definition *scope,const Definition *item) { p->reset(); diff --git a/src/symbolresolver.h b/src/symbolresolver.h index 89c3e6d..32c6e21 100644 --- a/src/symbolresolver.h +++ b/src/symbolresolver.h @@ -60,6 +60,17 @@ class SymbolResolver return toClassDefMutable(resolveClass(scope,name,mayBeUnlinkable,mayBeHidden)); } + /** Find the symbool definition matching name within the scope set. + * @param scope The scope to search from. + * @param name The name of the symbol. + * @param args Argument list associated with the symbol (for functions) + * @param checkCV Check const/volatile qualifiers (for methods) + */ + const Definition *resolveSymbol(const Definition *scope, + const QCString &name, + const QCString &args=QCString(), + bool checkCV=false); + /** Checks if symbol \a item is accessible from within \a scope. * @returns -1 if \a item is not accessible or a number indicating how * many scope levels up the nearest match was found. diff --git a/src/tagreader.cpp b/src/tagreader.cpp index e83bcc2..97c8933 100644 --- a/src/tagreader.cpp +++ b/src/tagreader.cpp @@ -457,7 +457,7 @@ class TagFileParser case InMember: case InPackage: case InDir: - if (m_curString.right(10)=="autotoc_md") return; + if (m_curString.endsWith("autotoc_md")) return; break; default: warn("Unexpected tag 'docanchor' found"); diff --git a/src/template.cpp b/src/template.cpp index 1a483d0..f1c10ec 100755..100644 --- a/src/template.cpp +++ b/src/template.cpp @@ -1221,7 +1221,7 @@ class FilterRelative public: static TemplateVariant apply(const TemplateVariant &v,const TemplateVariant &) { - if (v.isValid() && v.isString() && v.toString().left(2)=="..") + if (v.isValid() && v.isString() && v.toString().startsWith("..")) { return TRUE; } @@ -3123,7 +3123,7 @@ class TemplateNodeIf : public TemplateNodeCreator<TemplateNodeIf> auto tok = parser->takeNextToken(); // elif 'nodes' - while (tok && tok->data.left(5)=="elif ") + while (tok && tok->data.startsWith("elif ")) { m_ifGuardedNodes.push_back(std::make_unique<GuardedNodes>()); auto &guardedNodes = m_ifGuardedNodes.back(); diff --git a/src/textdocvisitor.h b/src/textdocvisitor.h index 6cfed63..5360c35 100644 --- a/src/textdocvisitor.h +++ b/src/textdocvisitor.h @@ -85,6 +85,7 @@ class TextDocVisitor : public DocVisitor void operator()(const DocHtmlCaption &c) { visitChildren(c); } void operator()(const DocInternal &i) { visitChildren(i); } void operator()(const DocHRef &h) { visitChildren(h); } + void operator()(const DocHtmlDetails &d) { visitChildren(d); } void operator()(const DocHtmlHeader &h) { visitChildren(h); } void operator()(const DocImage &i) { visitChildren(i); } void operator()(const DocDotFile &df) { visitChildren(df); } diff --git a/src/translator.h b/src/translator.h index adb552f..a190bfa 100644 --- a/src/translator.h +++ b/src/translator.h @@ -606,6 +606,9 @@ class Translator virtual QCString trDateTime(int year,int month,int day,int dayOfWeek, int hour,int minutes,int seconds, bool includeTime) = 0; + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) = 0; + virtual QCString trMonth(int month, bool first_capital, bool full) = 0; + virtual QCString trDayPeriod(int period) = 0; ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_adapter.h b/src/translator_adapter.h index 241b9dd..c4c014d 100644 --- a/src/translator_adapter.h +++ b/src/translator_adapter.h @@ -324,6 +324,9 @@ class TranslatorAdapter_1_7_5 : public TranslatorAdapter_1_8_0 int hour,int minutes,int seconds, bool includeTime) { return english.trDateTime(year,month,day,dayOfWeek,hour,minutes,seconds,includeTime); } + virtual QCString trDayPeriod(int period) + { return english.trDayPeriod(period); } + }; /** Adapter class for languages that only contain translations up to diff --git a/src/translator_am.h b/src/translator_am.h index 5159493..43281f2 100644 --- a/src/translator_am.h +++ b/src/translator_am.h @@ -1,1794 +1,1811 @@ -/******************************************************************************
- *
- *
- *
- * Copyright (C) 1997-2015 by Dimitri van Heesch.
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation under the terms of the GNU General Public License is hereby
- * granted. No representations are made about the suitability of this software
- * for any purpose. It is provided "as is" without express or implied warranty.
- * See the GNU General Public License for more details.
- *
- * Documents produced by Doxygen are derivative works derived from the
- * input used in their production; they are not affected by this license.
- */
-
-/*
- * Translation by
- * Armen Tangamyan <armen.tangamyan@anu.edu.au>
- */
-
-#ifndef TRANSLATOR_AM_H
-#define TRANSLATOR_AM_H
-
-class TranslatorArmenian : public TranslatorAdapter_1_8_0
-{
- public:
- /*! Used for identification of the language. */
- virtual QCString idLanguage()
- { return "armenian"; }
-
- /* Used to get the command(s) for the language support. */
- virtual QCString latexLanguageSupportCommand()
- {
- return "\\usepackage[latin]{armtex}\n"
- "\\usepackage[armscii8]{inputenc}\n";
- }
- virtual QCString trISOLang()
- { return "hy"; }
- virtual QCString getLanguageString()
- {
- return "0x42b Armenian";
- }
-
- // --- Language translation methods -------------------
-
- /*! used in the compound documentation before a list of related functions. */
- virtual QCString trRelatedFunctions()
- { return "Դասին վերաբերվող ֆունկցիաներ"; }
-
- /*! subscript for the related functions. */
- virtual QCString trRelatedSubscript()
- { return "(Հաշվի առեք, որ սրանք անդամ ֆունկցիաներ չեն)"; }
-
- /*! header that is put before the detailed description of files, classes and namespaces. */
- virtual QCString trDetailedDescription()
- { return "Մանրամասն նկարագրություն"; }
-
- /*! header that is put before the list of typedefs. */
- virtual QCString trMemberTypedefDocumentation()
- { return "Անդամ տիպի սահմանումներ (typedef)"; }
-
- /*! header that is put before the list of enumerations. */
- virtual QCString trMemberEnumerationDocumentation()
- { return "Անդամ hամարակալումներ"; }
-
- /*! header that is put before the list of member functions. */
- virtual QCString trMemberFunctionDocumentation()
- { return "Անդամ ֆունկցիաներ"; }
-
- /*! header that is put before the list of member attributes. */
- virtual QCString trMemberDataDocumentation()
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- {
- return "Դաշտեր";
- }
- else
- {
- return "Անդամ տվյալներ";
- }
- }
-
- /*! this is the text of a link put after brief descriptions. */
- virtual QCString trMore()
- { return "Մանրամասն..."; }
-
- /*! put in the class documentation */
- /* Isn't used when optimization for C is on. */
- virtual QCString trListOfAllMembers()
- {
- return "Բոլոր անդամների ցուցակը";
- }
-
- /*! used as the title of the "list of all members" page of a class */
- /* Isn't used when optimization for C is on. */
- virtual QCString trMemberList()
- {
- return "Անդամների ցուցակ";
- }
-
- /*! this is the first part of a sentence that is followed by a class name */
- /* Isn't used when optimization for C is on. */
- virtual QCString trThisIsTheListOfAllMembers()
- { return "Սա դասի անդամների ամբողջական ցուցակն է "; }
-
- /*! this is the remainder of the sentence after the class name */
- /* Isn't used when optimization for C is on. */
- virtual QCString trIncludingInheritedMembers()
- { return ", ներառյալ բոլոր ժառանգված անդամները"; }
-
- /*! this is put at the author sections at the bottom of man pages.
- * parameter s is name of the project name.
- */
- virtual QCString trGeneratedAutomatically(const QCString &s)
- { QCString result="Ավտոմատ ստեղծված է ելքային կոդից, Doxygen-ի միջոցով, ";
- if (!s.isEmpty()) result+=s+" համար:";
- return result;
- }
-
- /*! put after an enum name in the list of all members */
- virtual QCString trEnumName()
- { return "համարակալման անուն"; }
-
- /*! put after an enum value in the list of all members */
- virtual QCString trEnumValue()
- { return "համարակալման արժեք"; }
-
- /*! put after an undocumented member in the list of all members */
- virtual QCString trDefinedIn()
- { return "սահմանված"; }
-
- // quick reference sections
-
- /*! This is put above each page as a link to the list of all groups of
- * compounds or files (see the \\group command).
- */
- virtual QCString trModules()
- { return "Մոդուլներ"; }
-
- /*! This is put above each page as a link to the class hierarchy */
- virtual QCString trClassHierarchy()
- { return "Դասերի հիերարխա"; }
-
- /*! This is put above each page as a link to the list of annotated classes */
- virtual QCString trCompoundList()
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- {
- return "Տվյալների կառուցվածք";
- }
- else
- {
- return "Դասերի ցուցակ";
- }
- }
-
- /*! This is put above each page as a link to the list of documented files */
- virtual QCString trFileList()
- { return "Ֆայլերի ցուցակ"; }
-
- /*! This is put above each page as a link to all members of compounds. */
- virtual QCString trCompoundMembers()
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- {
- return "Տվյալների դաշտեր";
- }
- else
- {
- return "Դասի անդամներ";
- }
- }
-
- /*! This is put above each page as a link to all members of files. */
- /*??*/
- virtual QCString trFileMembers()
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- {
- return "Գլոբալներ";
- }
- else
- {
- return "Ֆայլի անդամներ";
- }
- }
-
- /*! This is put above each page as a link to all related pages. */
- virtual QCString trRelatedPages()
- { return "Նմանատիպ էջեր"; }
-
- /*! This is put above each page as a link to all examples. */
- virtual QCString trExamples()
- { return "Օրինակներ"; }
-
- /*! This is put above each page as a link to the search engine. */
- virtual QCString trSearch()
- { return "Որոնում"; }
-
- /*! This is an introduction to the class hierarchy. */
- virtual QCString trClassHierarchyDescription()
- { return "Այս ժառանգման ցուցակը կոպտորեն է տեսակավորված, "
- "բայց ոչ ամբողջապես, այբբենական կարգով.";
- }
-
- /*! This is an introduction to the list with all files. */
- virtual QCString trFileListDescription(bool extractAll)
- {
- QCString result="Բոլոր ";
- if (!extractAll) result+="փաստագրված ";
- result+="ֆայլերի մի ցուցակ` կարճ բացատրություններով:";
- return result;
- }
-
- /*! This is an introduction to the annotated compound list. */
- virtual QCString trCompoundListDescription()
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- {
- return "Տվյալների կառուցվածքը` կարճ բացատրություններով.";
- }
- else
- {
- return "Դասերը, կառուցվածքները, միավորումները "
- "և ինտերֆեյսները` կարճ բացատրություններով.";
- }
- }
-
- /*! This is an introduction to the page with all class members. */
- virtual QCString trCompoundMembersDescription(bool extractAll)
- {
- QCString result="Բոլոր ";
- if(!extractAll) result+="փաստագրված ";
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- result+="կառուցվածքների և միավորումների դաշտերի ";
- else
- result+="դասի անդամների ";
- result+="ցուցակը`";
- result+=" հղումներով դեպի ";
- if(!extractAll)
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- result+="կառուցվածք/միավորում փաստագրությունները բոլոր անդամների համար.";
- else
- result+="դասի փաստագրությունը բոլոր անդամների համար.";
- }
- else
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- result += "կառուցվածքները/միավորումները, որոնց նրանք պատկանում են.";
- else
- result += "դասերը, որոնց նրանք պատկանում են.";
- }
- return result;
- }
-
- /*! This is an introduction to the page with all file members. */
- virtual QCString trFileMembersDescription(bool extractAll)
- {
- QCString result="Բոլոր ";
- if (!extractAll) result+="փաստագրված ";
-
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- {
- result+="ֆունկցիաների, փոփոխականների, մակրո-հայտարարությունների, "
- "համարակալումների և տիպի սահմանումների (typedef)";
- }
- else
- {
- result+="ֆայլի անդամների ";
- }
- result+="ցուցակը`";
- result+=" հղումներով դեպի ";
- if (extractAll)
- result+="ֆայլերը, որոնց նրանք պատկանում են.";
- else
- result+="փաստագրությունը.";
- return result;
- }
-
- /*! This is an introduction to the page with the list of all examples */
- virtual QCString trExamplesDescription()
- { return "Բոլոր օրինակների ցուցակը."; }
-
- /*! This is an introduction to the page with the list of related pages */
- virtual QCString trRelatedPagesDescription()
- { return "Բոլոր նմանատիպ փաստագրության էջերի ցուցակը."; }
-
- /*! This is an introduction to the page with the list of class/file groups */
- virtual QCString trModulesDescription()
- { return "Բոլոր մոդուլների ցուցակը."; }
-
- // index titles (the project name is prepended for these)
-
-
- /*! This is used in HTML as the title of index.html. */
- virtual QCString trDocumentation()
- { return " - Փաստագրություն"; }
-
- /*! This is used in LaTeX as the title of the chapter with the
- * index of all groups.
- */
- virtual QCString trModuleIndex()
- { return "Մոդուլներ"; }
-
- /*! This is used in LaTeX as the title of the chapter with the
- * class hierarchy.
- */
- virtual QCString trHierarchicalIndex()
- { return "Դասակարգումներ"; }
-
- /*! This is used in LaTeX as the title of the chapter with the
- * annotated compound index.
- */
- virtual QCString trCompoundIndex()
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- {
- return "Տվյալների կառուցվածք";
- }
- else
- {
- return "Դասեր";
- }
- }
-
- /*! This is used in LaTeX as the title of the chapter with the
- * list of all files.
- */
- virtual QCString trFileIndex()
- { return "Ֆայլեր"; }
-
- /*! This is used in LaTeX as the title of the chapter containing
- * the documentation of all groups.
- */
- virtual QCString trModuleDocumentation()
- { return "Մոդուլներ"; }
-
- /*! This is used in LaTeX as the title of the chapter containing
- * the documentation of all classes, structs and unions.
- */
- virtual QCString trClassDocumentation()
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- {
- return "Տվյալների կառուցվածքներ";
- }
- else if (Config_getBool(OPTIMIZE_OUTPUT_VHDL))
- {
- return trDesignUnitDocumentation();
- }
- else
- {
- return "Դասեր";
- }
- }
-
- /*! This is used in LaTeX as the title of the chapter containing
- * the documentation of all files.
- */
- virtual QCString trFileDocumentation()
- { return "Ֆայլեր"; }
-
- /*! This is used in LaTeX as the title of the chapter containing
- * the documentation of all examples.
- */
- virtual QCString trExampleDocumentation()
- { return "Օրինակներ"; }
-
- /*! This is used in LaTeX as the title of the document */
- virtual QCString trReferenceManual()
- { return "Հղումների ձեռնարկ"; }
-
- /*! This is used in the documentation of a file as a header before the
- * list of defines
- */
- virtual QCString trDefines()
- { return "Մակրոսներ"; }
-
- /*! This is used in the documentation of a file as a header before the
- * list of typedefs
- */
- virtual QCString trTypedefs()
- { return "Տիպի սահմանումներ (typedef)"; }
-
- /*! This is used in the documentation of a file as a header before the
- * list of enumerations
- */
- virtual QCString trEnumerations()
- { return "Համարակալումներ"; }
-
- /*! This is used in the documentation of a file as a header before the
- * list of (global) functions
- */
- virtual QCString trFunctions()
- { return "Ֆունկցիաներ"; }
-
- /*! This is used in the documentation of a file as a header before the
- * list of (global) variables
- */
- virtual QCString trVariables()
- { return "Փոփոխականներ"; }
-
- /*! This is used in the documentation of a file as a header before the
- * list of (global) variables
- */
- virtual QCString trEnumerationValues()
- { return "Հաշվիչ"; }
-
- /*! This is used in the documentation of a file before the list of
- * documentation blocks for defines
- */
- virtual QCString trDefineDocumentation()
- { return "Մակրոսներ"; }
-
- /*! This is used in the documentation of a file/namespace before the list
- * of documentation blocks for typedefs
- */
- virtual QCString trTypedefDocumentation()
- { return "Տիպի սահմանումներ (typedef)"; }
-
- /*! This is used in the documentation of a file/namespace before the list
- * of documentation blocks for enumeration types
- */
- virtual QCString trEnumerationTypeDocumentation()
- { return "Համարակալման տիպեր"; }
-
- /*! This is used in the documentation of a file/namespace before the list
- * of documentation blocks for functions
- */
- virtual QCString trFunctionDocumentation()
- { return "Ֆունկցիաներ"; }
-
- /*! This is used in the documentation of a file/namespace before the list
- * of documentation blocks for variables
- */
- virtual QCString trVariableDocumentation()
- { return "Փոփոխականներ"; }
-
- /*! This is used in the documentation of a file/namespace/group before
- * the list of links to documented compounds
- */
- virtual QCString trCompounds()
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- {
- return "Տվյալների կառուցվածք";
- }
- else
- {
- return "Դասեր";
- }
- }
-
- /*! This is used in the documentation of a group before the list of
- * links to documented files
- */
- /*! This is used in the standard footer of each page and indicates when
- * the page was generated
- */
- virtual QCString trGeneratedAt(const QCString &date,const QCString &projName)
- {
- QCString result=QCString("Ստեղծվել է ")+date;
- if (!projName.isEmpty()) result+=projName+" -ի համար,";
- result+=" հետևյալ համակարգով.";
- return result;
- }
-
- /*! this text is put before a class diagram */
- virtual QCString trClassDiagram(const QCString &clName)
- {
- return clName+QCString(" -ի ժառանգման գծագիրը.");
- }
-
- /*! this text is generated when the \\warning command is used. */
- virtual QCString trWarning()
- { return "Զգուշացում"; }
-
- /*! this text is generated when the \\version command is used. */
- virtual QCString trVersion()
- { return "Տարբերակ"; }
-
- /*! this text is generated when the \\date command is used. */
- virtual QCString trDate()
- { return "Տարեթիվ"; }
-
- /*! this text is generated when the \\return command is used. */
- virtual QCString trReturns()
- { return "Վերադարձնում է"; }
-
- /*! this text is generated when the \\sa command is used. */
- virtual QCString trSeeAlso()
- { return "Տեսեք նաև"; }
-
- /*! this text is generated when the \\param command is used. */
- virtual QCString trParameters()
- { return "Պարամետրեր"; }
-
- /*! this text is generated when the \\exception command is used. */
- virtual QCString trExceptions()
- { return "Բացառություններ"; }
-
- /*! this text is used in the title page of a LaTeX document. */
- virtual QCString trGeneratedBy()
- { return "Ստեղծված է հետևյալ համակարգի կողմից"; }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 0.49-990307
-//////////////////////////////////////////////////////////////////////////
-
- /*! used as the title of page containing all the index of all namespaces. */
- virtual QCString trNamespaceList()
- { return "Անունների տարածությունների ցուցակ"; }
-
- /*! used as an introduction to the namespace list */
- virtual QCString trNamespaceListDescription(bool extractAll)
- {
- QCString result="Բոլոր ";
- if (!extractAll) result+="փաստագրված ";
- result+="անունների տարածությունների ցուցակը` կարճ բացատրություններով.";
- return result;
- }
-
- /*! used in the class documentation as a header before the list of all
- * friends of a class
- */
- virtual QCString trFriends()
- { return "Ընկերներ"; }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 0.49-990405
-//////////////////////////////////////////////////////////////////////////
-
- /*! used in the class documentation as a header before the list of all
- * related classes
- */
- virtual QCString trRelatedFunctionDocumentation()
- { return "Դասի ընկերներ և կապված ֆունկցիաներ"; }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 0.49-990425
-//////////////////////////////////////////////////////////////////////////
-
- /*! used as the title of the HTML page of a class/struct/union */
- virtual QCString trCompoundReference(const QCString &clName,
- ClassDef::CompoundType compType,
- bool isTemplate)
- {
- QCString result=clName;
- if (isTemplate)
- {
- switch(compType)
- {
- case ClassDef::Class: result+=" Դասի"; break;
- case ClassDef::Struct: result+=" Կառուցվածքի"; break;
- case ClassDef::Union: result+=" Միավորման"; break;
- case ClassDef::Interface: result+=" Ինտերֆեյսի"; break;
- case ClassDef::Protocol: result+=" Արձանագրության"; break;
- case ClassDef::Category: result+=" Դասակարգման"; break;
- case ClassDef::Exception: result+=" Բացառության"; break;
- default: break;
- }
- result+=" Ձևանմուշներ";
- }
- else
- {
- switch(compType)
- {
- case ClassDef::Class: result+=" Դաս"; break;
- case ClassDef::Struct: result+=" Կառուցվածք"; break;
- case ClassDef::Union: result+=" Միավորում"; break;
- case ClassDef::Interface: result+=" Ինտերֆեյս"; break;
- case ClassDef::Protocol: result+=" Արձանագրություն"; break;
- case ClassDef::Category: result+=" Դասակարգում"; break;
- case ClassDef::Exception: result+=" Բացառություն"; break;
- default: break;
- }
- }
- return result;
- }
-
- /*! used as the title of the HTML page of a file */
- virtual QCString trFileReference(const QCString &fileName)
- {
- return fileName+QCString(" ֆայլեր");
- }
-
- /*! used as the title of the HTML page of a namespace */
- virtual QCString trNamespaceReference(const QCString &namespaceName)
- {
- QCString result=namespaceName;
- result+=" անունների տարածություններ";
- return result;
- }
-
- virtual QCString trPublicMembers()
- { return "Բաց անդամ ֆունկցիաներ"; }
- virtual QCString trPublicSlots()
- { return "Բաց սլոթեր"; }
- virtual QCString trSignals()
- { return "Ազդանշաններ"; }
- virtual QCString trStaticPublicMembers()
- { return "Բաց ստատիկ անդամ ֆունկցիաներ"; }
- virtual QCString trProtectedMembers()
- { return "Պաշտպանված անդամ ֆունկցիաներ"; }
- virtual QCString trProtectedSlots()
- { return "Պաշտպանված սլոթեր"; }
- virtual QCString trStaticProtectedMembers()
- { return "Պաշտպանված ստատիկ անդամ ֆունկցիաներ"; }
- virtual QCString trPrivateMembers()
- { return "Փակ ֆունկցիաներ"; }
- virtual QCString trPrivateSlots()
- { return "Փակ սլոթեր"; }
- virtual QCString trStaticPrivateMembers()
- { return "Փակ ստատիկ անդամ ֆունկցիաներ"; }
-
- /*! this function is used to produce a comma-separated list of items.
- * use generateMarker(i) to indicate where item i should be put.
- */
- virtual QCString trWriteList(int numEntries)
- {
- QCString result;
- int i;
- // the inherits list contain `numEntries' classes
- for (i=0;i<numEntries;i++)
- {
- // use generateMarker to generate placeholders for the class links!
- result+=generateMarker(i); // generate marker for entry i in the list
- // (order is left to right)
-
- if (i!=numEntries-1) // not the last entry, so we need a separator
- {
- if (i<numEntries-2) // not the fore last entry
- result+=", ";
- else // the fore last entry
- result+=" և ";
- }
- }
- return result;
- }
-
- /*! used in class documentation to produce a list of base classes,
- * if class diagrams are disabled.
- */
- virtual QCString trInheritsList(int numEntries)
- {
- return "Հենքային դասեր - "+trWriteList(numEntries)+":";
- }
-
- /*! used in class documentation to produce a list of super classes,
- * if class diagrams are disabled.
- */
- virtual QCString trInheritedByList(int numEntries)
- {
- return "Ժառանգորդ դասեր - "+trWriteList(numEntries)+":";
- }
-
- /*! used in member documentation blocks to produce a list of
- * members that are hidden by this one.
- */
- virtual QCString trReimplementedFromList(int numEntries)
- {
- return "Վերասահմանված ֆունկցիաներ - "+trWriteList(numEntries)+":";
- }
-
- /*! used in member documentation blocks to produce a list of
- * all member that overwrite the implementation of this member.
- */
- virtual QCString trReimplementedInList(int numEntries)
- {
- return "Վերասահմանված է "+trWriteList(numEntries)+" ում:";
- }
-
- /*! This is put above each page as a link to all members of namespaces. */
- virtual QCString trNamespaceMembers()
- { return "Անունների տարածության անդամներ"; }
-
- /*! This is an introduction to the page with all namespace members */
- virtual QCString trNamespaceMemberDescription(bool extractAll)
- {
- QCString result="Բոլոր ";
- if (!extractAll) result+="փաստագրված ";
- result+="անունների տարածության անդամների ցուցակը` "
- "հղումներով դեպի ";
- if (extractAll)
- result+="բոլոր անդամների անունների տարածության փաստագրությունը.";
- else
- result+="անունների տարածությունը, որին նրանք պատկանում են.";
- return result;
- }
-
- /*! This is used in LaTeX as the title of the chapter with the
- * index of all namespaces.
- */
- virtual QCString trNamespaceIndex()
- { return "Անունների տարածություններ"; }
-
- /*! This is used in LaTeX as the title of the chapter containing
- * the documentation of all namespaces.
- */
- virtual QCString trNamespaceDocumentation()
- { return "Անունների տարածություն"; }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 0.49-990522
-//////////////////////////////////////////////////////////////////////////
-
- /*! This is used in the documentation before the list of all
- * namespaces in a file.
- */
- virtual QCString trNamespaces()
- { return "Անունների տարածություններ"; }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 0.49-990728
-//////////////////////////////////////////////////////////////////////////
-
- /*! This is put at the bottom of a class documentation page and is
- * followed by a list of files that were used to generate the page.
- */
- virtual QCString trGeneratedFromFiles(ClassDef::CompoundType compType,
- bool single)
- {
- QCString result = "Այս ";
- switch(compType)
- {
- case ClassDef::Class: result+="դասի"; break;
- case ClassDef::Struct: result+="կառուցվածքի"; break;
- case ClassDef::Union: result+="միավորման"; break;
- case ClassDef::Interface: result+="ինտերֆեյսի"; break;
- case ClassDef::Protocol: result+="արձանագրության"; break;
- case ClassDef::Category: result+="դասակարգման"; break;
- case ClassDef::Exception: result+="բացառության"; break;
- default: break;
- }
- result+=" փաստագրությունը ստեղծվել է հետևյալ ֆայլ";
- if (single) result+="ից."; else result+="երից.";
- return result;
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 0.49-990901
-//////////////////////////////////////////////////////////////////////////
-
- /*! This is used as the heading text for the retval command. */
- virtual QCString trReturnValues()
- { return "Վերադարձվող արժեքներ"; }
-
- /*! This is in the (quick) index as a link to the main page (index.html)
- */
- virtual QCString trMainPage()
- { return "Գլխավոր էջ"; }
-
- /*! This is used in references to page that are put in the LaTeX
- * documentation. It should be an abbreviation of the word page.
- */
- virtual QCString trPageAbbreviation()
- { return "էջ:"; }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 0.49-991106
-//////////////////////////////////////////////////////////////////////////
-
- virtual QCString trDefinedAtLineInSourceFile()
- {
- return "Սահմանումը @1 ֆայլի @0 տողում է:";
- }
- virtual QCString trDefinedInSourceFile()
- {
- return "Սահմանումը @0 ֆայլում է:";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 0.49-991205
-//////////////////////////////////////////////////////////////////////////
-
- virtual QCString trDeprecated()
- {
- return "Հնացած է";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.0.0
-//////////////////////////////////////////////////////////////////////////
-
- /*! this text is put before a collaboration diagram */
- virtual QCString trCollaborationDiagram(const QCString &clName)
- {
- return clName+"-ի համագործակցությունների գծագիր.";
- }
- /*! this text is put before an include dependency graph */
- virtual QCString trInclDepGraph(const QCString &fName)
- {
- return fName+"-ի ներառումների կախվածությունների գծագիր.";
- }
- /*! header that is put before the list of constructor/destructors. */
- virtual QCString trConstructorDocumentation()
- {
- return "Կառուցիչներ";
- }
- /*! Used in the file documentation to point to the corresponding sources. */
- virtual QCString trGotoSourceCode()
- {
- return "Տե'ս այս ֆայլի ելքային կոդը";
- }
- /*! Used in the file sources to point to the corresponding documentation. */
- virtual QCString trGotoDocumentation()
- {
- return "Տե'ս այս ֆայլի փաստագրությունը:";
- }
- /*! Text for the \\pre command */
- virtual QCString trPrecondition()
- {
- return "Նախապայման";
- }
- /*! Text for the \\post command */
- virtual QCString trPostcondition()
- {
- return "Հետպայման";
- }
- /*! Text for the \\invariant command */
- virtual QCString trInvariant()
- {
- return "Անփոփոխ";
- }
- /*! Text shown before a multi-line variable/enum initialization */
- virtual QCString trInitialValue()
- {
- return "Նախնական արժեք";
- }
- /*! Text used the source code in the file index */
- virtual QCString trCode()
- {
- return "Ելքային կոդ";
- }
- virtual QCString trGraphicalHierarchy()
- {
- return "Գրաֆիկական դասերի հիերարխիա:";
- }
- virtual QCString trGotoGraphicalHierarchy()
- {
- return "Տե'ս դասերի գրաֆիկական հիերարխիան:";
- }
- virtual QCString trGotoTextualHierarchy()
- {
- return "Տե'ս դասերի տեքստային հիերարխիան:";
- }
- virtual QCString trPageIndex()
- {
- return "էջեր";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.1.0
-//////////////////////////////////////////////////////////////////////////
-
- virtual QCString trNote()
- {
- return "Նշում";
- }
- virtual QCString trPublicTypes()
- {
- return "Բաց տիպեր";
- }
- virtual QCString trPublicAttribs()
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- {
- return "Տվյալների դաշտեր";
- }
- else
- {
- return "Բաց ատրիբուտներ";
- }
- }
- virtual QCString trStaticPublicAttribs()
- {
- return "Բաց ստատիկ ատրիբուտներ";
- }
- virtual QCString trProtectedTypes()
- {
- return "Պաշտպանված տիպեր";
- }
- virtual QCString trProtectedAttribs()
- {
- return "Պաշտպանված ատրիբուտներ";
- }
- virtual QCString trStaticProtectedAttribs()
- {
- return "Պաշտպանված ստատիկ ատրիբուտներ";
- }
- virtual QCString trPrivateTypes()
- {
- return "Փակ տիպեր";
- }
- virtual QCString trPrivateAttribs()
- {
- return "Փակ ատրիբուտներ";
- }
- virtual QCString trStaticPrivateAttribs()
- {
- return "Փակ ստատիկ ատրիբուտներ";
- }
-
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.1.3
-//////////////////////////////////////////////////////////////////////////
-
- /*! Used as a marker that is put before a todo item */
- virtual QCString trTodo()
- /*??*/
- {
- return "Կատարման ենթակա";
- }
- /*! Used as the header of the todo list */
- virtual QCString trTodoList()
- /*??*/
- {
- return "Խնդիրների ցուցակ";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.1.4
-//////////////////////////////////////////////////////////////////////////
-
- virtual QCString trReferencedBy()
- {
- return "Օգտագործվում է հետևյալում - ";
- }
- virtual QCString trRemarks()
- {
- return "Դիտողություններ";
- }
- virtual QCString trAttention()
- {
- return "Ուշադրություն";
- }
- virtual QCString trInclByDepGraph()
- {
- return "Այս գրաֆը ցույց է տալիս, թե որ ֆայլերն են "
- "ուղղակի կամ անուղղակի ներառում տվյալ ֆայլը.";
- }
- virtual QCString trSince()
- /*??*/
- {
- return "Սկսած";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.1.5
-//////////////////////////////////////////////////////////////////////////
-
- /*! title of the graph legend page */
- virtual QCString trLegendTitle()
- {
- return "Լեգենդ";
- }
- /*! page explaining how the dot graph's should be interpreted
- * The %A in the text below are to prevent link to classes called "A".
- */
- virtual QCString trLegendDocs()
- {
- return
- "Այս էջը նկարագրում է, թե ինչպես մեկնաբանել doxygen-ի ստեղծած գրաֆները:<p>\n"
- "Դիտարկենք հետևյալ օրինակը.\n"
- "\\code\n"
- "/*! Կրճատման հետևանքով անտեսանելի դաս */\n"
- "class Invisible { };\n\n"
- "/*! Կրճատված դաս, ժառանգությունների հարաբերությունը փակ է */\n"
- "class Truncated : public Invisible { };\n\n"
- "/* Չփաստագրված դաս */\n"
- "class Undocumented { };\n\n"
- "/*! Բաց ժառանգում */\n"
- "class PublicBase : public Truncated { };\n\n"
- "/*! Դասի ձևաչափ */\n"
- "template<class T> class Templ {};\n\n"
- "/*! Պաշտպանված ժառանգում */\n"
- "class ProtectedBase { };\n\n"
- "/*! Փակ ժառանգում */\n"
- "class PrivateBase { };\n\n"
- "/*! Դաս, որը օգտագործվում է Inherited դասի կողմից */\n"
- "class Used { };\n\n"
- "/*! Դաս, որը ժառանգում է մի շարք այլ դասերից */\n"
- "class Inherited : public PublicBase,\n"
- " protected ProtectedBase,\n"
- " private PrivateBase,\n"
- " public Undocumented,\n"
- " public Templ<int>\n"
- "{\n"
- " private:\n"
- " Used *m_usedClass;\n"
- "};\n"
- "\\endcode\n"
- "Սրանով կստանանք հետևյալ գրաֆը."
- "<p><center><img src=\"graph_legend."+getDotImageExtension()+"\"></center>\n"
- "<p>\n"
- "Այս գրաֆի ուղղանկյունները ունեն հետևյալ իմաստը.\n"
- "<ul>\n"
- "<li>%A լցոնվաց մոխրագույն ուղղանկյունը ներկայացնում է այն դասը կամ կառուցվածքը, "
- "որի համար ստեղծվել է տվյալ գրաֆը:</li>\n"
- "<li>%A սև եզրերով ուղղանկյունը նշանակում է փաստագրված դաս կամ կարուցվածք:</li>\n"
- "<li>%A մոխրագույն եզրերով ուղղանկյունը նշանակում է չփաստագրված դաս կամ կառուցվածք:</li>\n"
- "<li>%A կարմիր եզրերով ուղղանկյունը նշանակում է դաս կամ կառուցվածք, որի համար\n"
- " ոչ բոլոր ժառանգում/պարունակում կապերն են ցուցադրված: Գրաֆը կրճատված է, "
- "եթե այն չի տեղավորվում նշված սահմաններում:</li>\n"
- "</ul>\n"
- "Սլաքները ունեն հետևյալ իմաստը.\n"
- "<ul>\n"
- "<li>%A մուգ կապույտ սլաքը օգտագործվում է երկու դասերի միջև բաց ժառանգում "
- "կապը ցուցադրելու համար:</li>\n"
- "<li>%A մուգ կանաչ սլաքը օգտագործվում է պաշտպանված ժառանգման համար:</li>\n"
- "<li>%A մուգ կարմիր սլաքը օգտագործվում է փակ ժառանգման համար:</li>\n"
- "<li>%A մանուշակագույն կետագիծ սլաքը օգտագորշվում է, եթե դասը պարունակվում է"
- "այլ դասում կամ օգտագորշվում է այլ դասի կողմից: Սլաքը պիտակավորվաշ է"
- "փոփոխական(ներ)ով, որի միջոցով մատնանշված դասը կամ կառուցվածքը հասանելի է:</li>\n"
- "<li>Դեզին կետագիծ սլաքը ցույց է տալիս ձևանմուշի օրինակի կապը այն ձևանմուշի հետ, "
- "որից այն իրականցվել է. Սլաքը պիտակավորված է օրինակի ձևանմուշային պարամետրերով:</li>\n"
- "</ul>\n";
- }
- /*! text for the link to the legend page */
- virtual QCString trLegend()
- {
- return "լեգենդ";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.2.0
-//////////////////////////////////////////////////////////////////////////
-
- /*! Used as a marker that is put before a test item */
- virtual QCString trTest()
- {
- return "Թեստ";
- }
- /*! Used as the header of the test list */
- virtual QCString trTestList()
- {
- return "Թեստերի ցուցակ";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.2.2
-//////////////////////////////////////////////////////////////////////////
-
- /*! Used as a section header for IDL properties */
- virtual QCString trProperties()
- {
- return "Հատկություններ";
- }
- /*! Used as a section header for IDL property documentation */
- virtual QCString trPropertyDocumentation()
- {
- return "Հատկություններ";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.2.4
-//////////////////////////////////////////////////////////////////////////
-
- /*! Used for Java classes in the summary section of Java packages */
- virtual QCString trClasses()
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- {
- return "Տվյալների կառուցվածք";
- }
- else
- {
- return "Դասեր";
- }
- }
- /*! Used as the title of a Java package */
- virtual QCString trPackage(const QCString &name)
- {
- return "Փաթեթ "+name;
- }
- /*! The description of the package index page */
- virtual QCString trPackageListDescription()
- {
- return "Բոլոր փաթեթները` կարճ բացատրություններով (եթե հասանելի են).";
- }
- /*! The link name in the Quick links header for each page */
- virtual QCString trPackages()
- {
- return "Փաթեթներ";
- }
- /*! Text shown before a multi-line define */
- virtual QCString trDefineValue()
- {
- return "Արժեքներ";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.2.5
-//////////////////////////////////////////////////////////////////////////
-
- /*! Used as a marker that is put before a \\bug item */
- virtual QCString trBug()
- {
- return "Սխալ";
- }
- /*! Used as the header of the bug list */
- virtual QCString trBugList()
- {
- return "Սխալների ցուցակ";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.2.6
-//////////////////////////////////////////////////////////////////////////
- /*! Used as ansicpg for RTF file */
- virtual QCString trRTFansicp()
- {
- return "armscii-8";
- }
- /*! Used as ansicpg for RTF fcharset */
- virtual QCString trRTFCharSet()
- {
- return "0";
- }
- /*! Used as header RTF general index */
- virtual QCString trRTFGeneralIndex()
- {
- return "Ցուցիչ";
- }
-
- /*! This is used for translation of the word that will possibly
- * be followed by a single name or by a list of names
- * of the category.
- */
- virtual QCString trClass(bool first_capital, bool singular)
- {
- if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C))
- {
- QCString result((first_capital ? "Տվյալների կառուցվածք" : "տվյալների կառուցվածք"));
- return result;
- }
- else
- {
- QCString result((first_capital ? "Դաս" : "դաս"));
- if(!singular) result+="եր";
- return result;
- }
- }
-
- /*! This is used for translation of the word that will possibly
- * be followed by a single name or by a list of names
- * of the category.
- */
- virtual QCString trFile(bool first_capital, bool singular)
- {
- QCString result((first_capital ? "Ֆայլ" : "ֆայլ"));
- if (!singular) result+="եր";
- return result;
- }
-
- /*! This is used for translation of the word that will possibly
- * be followed by a single name or by a list of names
- * of the category.
- */
- virtual QCString trNamespace(bool first_capital, bool singular)
- {
- QCString result((first_capital ? "Անունների տարածություն" : "անունների տարածություն"));
- if (!singular) result+="ներ";
- return result;
- }
-
- /*! This is used for translation of the word that will possibly
- * be followed by a single name or by a list of names
- * of the category.
- */
- virtual QCString trGroup(bool first_capital, bool singular)
- {
- QCString result((first_capital ? "Խ" : "խ"));
- result+=(singular ? "ումբ" : "մբեր");
- return result;
- }
-
- /*! This is used for translation of the word that will possibly
- * be followed by a single name or by a list of names
- * of the category.
- */
- virtual QCString trPage(bool first_capital, bool singular)
- {
- QCString result((first_capital ? "Էջ" : "էջ"));
- if (!singular) result+="եր";
- return result;
- }
-
- /*! This is used for translation of the word that will possibly
- * be followed by a single name or by a list of names
- * of the category.
- */
- virtual QCString trMember(bool first_capital, bool singular)
- {
- QCString result((first_capital ? "Անդամ" : "անդամ"));
- if (!singular) result+="ներ";
- return result;
- }
-
- /*! This is used for translation of the word that will possibly
- * be followed by a single name or by a list of names
- * of the category.
- */
- virtual QCString trGlobal(bool first_capital, bool singular)
- {
- QCString result((first_capital ? "Գլոբալ" : "գլոբալ"));
- if (!singular) result+="ներ";
- return result;
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.2.7
-//////////////////////////////////////////////////////////////////////////
-
- /*! This text is generated when the \\author command is used and
- * for the author section in man pages. */
- virtual QCString trAuthor(bool first_capital, bool singular)
- {
- QCString result((first_capital ? "Հեղինակ" : "հեղինակ"));
- if (!singular) result+="ներ";
- return result;
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.2.11
-//////////////////////////////////////////////////////////////////////////
-
- /*! This text is put before the list of members referenced by a member
- */
- virtual QCString trReferences()
- {
- return "Հղումներ - ";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.2.13
-//////////////////////////////////////////////////////////////////////////
-
- /*! used in member documentation blocks to produce a list of
- * members that are implemented by this one.
- */
- virtual QCString trImplementedFromList(int numEntries)
- {
- return "Իրագործում է հետևյալ դաս(եր)ի ֆունկցիաները - "+trWriteList(numEntries)+":";
- }
-
- /*! used in member documentation blocks to produce a list of
- * all members that implementation this member.
- */
- virtual QCString trImplementedInList(int numEntries)
- {
- return "Իրագործվում է հետևյալում - "+trWriteList(numEntries)+":";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.2.16
-//////////////////////////////////////////////////////////////////////////
-
- /*! used in RTF documentation as a heading for the Table
- * of Contents.
- */
- virtual QCString trRTFTableOfContents()
- {
- return "Բովանդակություն";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.2.17
-//////////////////////////////////////////////////////////////////////////
-
- /*! Used as the header of the list of item that have been
- * flagged deprecated
- */
- virtual QCString trDeprecatedList()
- {
- return "Հնացած սահմանումների ցուցակը";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.2.18
-//////////////////////////////////////////////////////////////////////////
-
- /*! Used as a header for declaration section of the events found in
- * a C# program
- */
- virtual QCString trEvents()
- {
- return "Պատահարներ";
- }
- /*! Header used for the documentation section of a class' events. */
- virtual QCString trEventDocumentation()
- {
- return "Պատահարների ցուցակը";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.3
-//////////////////////////////////////////////////////////////////////////
-
- /*! Used as a heading for a list of Java class types with package scope.
- */
- virtual QCString trPackageTypes()
- {
- return "Փաթեթի տիպեր";
- }
- /*! Used as a heading for a list of Java class functions with package
- * scope.
- */
- virtual QCString trPackageFunctions()
- {
- return "Փաթեթի ֆունկցիաներ";
- }
- virtual QCString trPackageMembers()
- {
- return "Փաթեթի անդամներ";
- }
- /*! Used as a heading for a list of static Java class functions with
- * package scope.
- */
- virtual QCString trStaticPackageFunctions()
- {
- return "Փաթեթի ստատիկ ֆունկցիաներ";
- }
- /*! Used as a heading for a list of Java class variables with package
- * scope.
- */
- virtual QCString trPackageAttribs()
- {
- return "Փաթեթի ատրիբուտներ";
- }
- /*! Used as a heading for a list of static Java class variables with
- * package scope.
- */
- virtual QCString trStaticPackageAttribs()
- {
- return "Փաթեթի ստատիկ ատրիբուտներ";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.3.1
-//////////////////////////////////////////////////////////////////////////
-
- /*! Used in the quick index of a class/file/namespace member list page
- * to link to the unfiltered list of all members.
- */
- virtual QCString trAll()
- {
- return "Բոլոր";
- }
- /*! Put in front of the call graph for a function. */
- virtual QCString trCallGraph()
- {
- return "Այս ֆունկցիայի կանչերի գրաֆը.";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.3.3
-//////////////////////////////////////////////////////////////////////////
-
- /*! This string is used as the title for the page listing the search
- * results.
- */
- virtual QCString trSearchResultsTitle()
- {
- return "Որոնման արդյունքները";
- }
- /*! This string is put just before listing the search results. The
- * text can be different depending on the number of documents found.
- * Inside the text you can put the special marker $num to insert
- * the number representing the actual number of search results.
- * The @a numDocuments parameter can be either 0, 1 or 2, where the
- * value 2 represents 2 or more matches. HTML markup is allowed inside
- * the returned string.
- */
- virtual QCString trSearchResults(int numDocuments)
- {
- if (numDocuments==0)
- {
- return "Ներեցեք, բայց Ձեր որոնումը արդյունք չտվեց:";
- }
- else if( numDocuments == 1 )
- {
- return "Հայտնաբերվել է 1 փաստաթուղթ:";
- }
- else
- {
- return "Հայտնաբերվել է <b>$num</b> փաստաթուղթ:"
- "Փաստաթղթերը դասակարգված են ըստ համապասխանության";
- }
- }
- /*! This string is put before the list of matched words, for each search
- * result. What follows is the list of words that matched the query.
- */
- virtual QCString trSearchMatches()
- {
- return "Որոնման արդյունքներ:";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.3.8
-//////////////////////////////////////////////////////////////////////////
-
- /*! This is used in HTML as the title of page with source code for file filename
- */
- virtual QCString trSourceFile(QCString& filename)
- {
- return "Ելակետային ֆայլ " + filename;
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.3.9
-//////////////////////////////////////////////////////////////////////////
-
- /*! This is used as the name of the chapter containing the directory
- * hierarchy.
- */
- virtual QCString trDirIndex()
- { return "Ֆայլադարանների հիերարխիա"; }
-
- /*! This is used as the name of the chapter containing the documentation
- * of the directories.
- */
- virtual QCString trDirDocumentation()
- { return "Ֆայլադարաններ"; }
-
- /*! This is used as the title of the directory index and also in the
- * Quick links of a HTML page, to link to the directory hierarchy.
- */
- virtual QCString trDirectories()
- { return "Ֆայլադրաններ"; }
-
- /*! This returns the title of a directory page. The name of the
- * directory is passed via \a dirName.
- */
- virtual QCString trDirReference(const QCString &dirName)
- { QCString result=dirName; result+=" Ֆայլադարան"; return result; }
-
- /*! This returns the word directory with or without starting capital
- * (\a first_capital) and in sigular or plural form (\a singular).
- */
- virtual QCString trDir(bool first_capital, bool singular)
- {
- QCString result((first_capital ? "Ֆայլադարան" : "ֆայլադարան"));
- if (!singular) result+="ներ";
- return result;
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.4.1
-//////////////////////////////////////////////////////////////////////////
-
- /*! This text is added to the documentation when the \\overload command
- * is used for a overloaded function.
- */
- virtual QCString trOverloadText()
- {
- return "Սա վերաբեռնված ֆունկցիա է` տրամադրված հարմարության համար: "
- "Այն տարբերվում է նախնականից միայն արգումնետներով:";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.4.6
-//////////////////////////////////////////////////////////////////////////
-
- /*! This is used to introduce a caller (or called-by) graph */
- virtual QCString trCallerGraph()
- {
- return "Այս ֆունկցիայի կանչերի գրաֆը.";
- }
-
- /*! This is used in the documentation of a file/namespace before the list
- * of documentation blocks for enumeration values
- */
- virtual QCString trEnumerationValueDocumentation()
- { return "Համարակալումներ"; }
-
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.5.4 (mainly for Fortran)
-//////////////////////////////////////////////////////////////////////////
- /*! header that is put before the list of member subprograms (Fortran). */
- virtual QCString trMemberFunctionDocumentationFortran()
- { return "Անդամ ֆունցիաներ/ենթածրագրեր"; }
-
- /*! This is put above each page as a link to the list of annotated data types (Fortran). */
- virtual QCString trCompoundListFortran()
- { return "Տվյալների տիպերի ցուցակը"; }
-
- /*! This is put above each page as a link to all members of compounds (Fortran). */
- virtual QCString trCompoundMembersFortran()
- { return "Տվյալների դաշտեր"; }
-
- /*! This is an introduction to the annotated compound list (Fortran). */
- virtual QCString trCompoundListDescriptionFortran()
- { return "Տվյալների տիպերը` կարճ բացատրություններով."; }
-
- /*! This is an introduction to the page with all data types (Fortran). */
- virtual QCString trCompoundMembersDescriptionFortran(bool extractAll)
- {
- QCString result="Բոլոր ";
- if (!extractAll)
- {
- result+="փաստագրված ";
- }
- result+="տվյալների տիպերի անդամների ցուցակը` հղումներով դեպի ";
- if (!extractAll)
- {
- result+="բոլոր անդամների տվյալների կառուցվածքի փաստագրությունը";
- }
- else
- {
- result+="տվյալների տիպերը, որոնց նրանք պատկանում են";
- }
- return result;
- }
-
- /*! This is used in LaTeX as the title of the chapter with the
- * annotated compound index (Fortran).
- */
- virtual QCString trCompoundIndexFortran()
- { return "Տվյալների տիպեր"; }
-
- /*! This is used in LaTeX as the title of the chapter containing
- * the documentation of all data types (Fortran).
- */
- virtual QCString trTypeDocumentation()
- { return "Տվյալների տիպեր"; }
-
- /*! This is used in the documentation of a file as a header before the
- * list of (global) subprograms (Fortran).
- */
- virtual QCString trSubprograms()
- { return "Ֆունկցիաներ/ենթածրագրեր"; }
-
- /*! This is used in the documentation of a file/namespace before the list
- * of documentation blocks for subprograms (Fortran)
- */
- virtual QCString trSubprogramDocumentation()
- { return "Ֆունկցիաներ/ենթածրագրեր"; }
-
- /*! This is used in the documentation of a file/namespace/group before
- * the list of links to documented compounds (Fortran)
- */
- virtual QCString trDataTypes()
- { return "Տվյալների տիպեր"; }
-
- /*! used as the title of page containing all the index of all modules (Fortran). */
- virtual QCString trModulesList()
- { return "Մոդուլների ցուցակ"; }
-
- /*! used as an introduction to the modules list (Fortran) */
- virtual QCString trModulesListDescription(bool extractAll)
- {
- QCString result="Բոլոր";
- if (!extractAll) result+="փաստագրված ";
- result+="մոդուլների ցուցակը` կարճ բացատրություններով.";
- return result;
- }
-
- /*! used as the title of the HTML page of a module/type (Fortran) */
- virtual QCString trCompoundReferenceFortran(const QCString &clName,
- ClassDef::CompoundType compType,
- bool isTemplate)
- {
- QCString result=clName;
- if (!isTemplate)
- {
- switch(compType)
- {
- case ClassDef::Class: result+=" Մոդուլ"; break;
- case ClassDef::Struct: result+=" Տիպ"; break;
- case ClassDef::Union: result+=" Միավորում"; break;
- case ClassDef::Interface: result+=" Ինտերֆեյս"; break;
- case ClassDef::Protocol: result+=" Արձանագրություն"; break;
- case ClassDef::Category: result+=" Դասակարգում"; break;
- case ClassDef::Exception: result+=" Բացառություն"; break;
- default: break;
- }
- }
- else
- {
- switch(compType)
- {
- case ClassDef::Class: result+=" Մոդուլի"; break;
- case ClassDef::Struct: result+=" Տիպի"; break;
- case ClassDef::Union: result+=" Միավորման"; break;
- case ClassDef::Interface: result+=" Ինտերֆեյսի"; break;
- case ClassDef::Protocol: result+=" Արձանագրության"; break;
- case ClassDef::Category: result+=" Դասակարգման"; break;
- case ClassDef::Exception: result+=" Բացառության"; break;
- default: break;
- }
- result+=" Ձևանմուշ";
- }
- return result;
- }
- /*! used as the title of the HTML page of a module (Fortran) */
- virtual QCString trModuleReference(const QCString &namespaceName)
- {
- return QCString("Մոդուլ ") + namespaceName;
- }
-
- /*! This is put above each page as a link to all members of modules. (Fortran) */
- virtual QCString trModulesMembers()
- { return "Մոդուլի անդամներ"; }
-
- /*! This is an introduction to the page with all modules members (Fortran) */
- virtual QCString trModulesMemberDescription(bool extractAll)
- {
- QCString result="Մոդուլի բոլոր ";
- if (!extractAll) result+="փաստագրված ";
- result+="անդամների ցուցակը` հղումներով դեպի ";
- if (extractAll)
- {
- result+="բոլոր անդամների փաստագրությունները.";
- }
- else
- {
- result+="մոդուլները, որոնց նրանք պատկանում են.";
- }
- return result;
- }
-
- /*! This is used in LaTeX as the title of the chapter with the
- * index of all modules (Fortran).
- */
- virtual QCString trModulesIndex()
- { return "Մոդուլներ"; }
-
- /*! This is used for translation of the word that will possibly
- * be followed by a single name or by a list of names
- * of the category.
- */
- virtual QCString trModule(bool first_capital, bool singular)
- {
- QCString result((first_capital ? "Մոդուլ" : "մոդուլ"));
- if (!singular) result+="ներ";
- return result;
- }
- /*! This is put at the bottom of a module documentation page and is
- * followed by a list of files that were used to generate the page.
- */
- virtual QCString trGeneratedFromFilesFortran(ClassDef::CompoundType compType,
- bool single)
- { // here s is one of " Module", " Struct" or " Union"
- // single is true implies a single file
- QCString result="Այս ";
- switch(compType)
- {
- case ClassDef::Class: result+="մոդուլի"; break;
- case ClassDef::Struct: result+="տիպի"; break;
- case ClassDef::Union: result+="միավորման"; break;
- case ClassDef::Interface: result+="ինտերֆեյսի"; break;
- case ClassDef::Protocol: result+="արձանագրության"; break;
- case ClassDef::Category: result+="դասակարգման"; break;
- case ClassDef::Exception: result+="բացառության"; break;
- default: break;
- }
- result+=" փաստագրությունը ստեղծվել է հետևալ ֆայլ";
- if (single) result+="ից."; else result+="երից.";
- return result;
- }
- /*! This is used for translation of the word that will possibly
- * be followed by a single name or by a list of names
- * of the category.
- */
- virtual QCString trType(bool first_capital, bool singular)
- {
- QCString result((first_capital ? "Տիպ" : "տիպ"));
- if (!singular) result+="եր";
- return result;
- }
- /*! This is used for translation of the word that will possibly
- * be followed by a single name or by a list of names
- * of the category.
- */
- virtual QCString trSubprogram(bool first_capital, bool singular)
- {
- QCString result((first_capital ? "Ե" : "ե"));
- if (singular) result+="նթածրագիր"; else result+="նթածրագրեր";
- return result;
- }
-
- /*! C# Type Constraint list */
- virtual QCString trTypeConstraints()
- {
- return "Տիպերի Սահմանափակումներ";
- }
-//////////////////////////////////////////////////////////////////////////
-// new since 1.6.0 (mainly for the new search engine)
-//////////////////////////////////////////////////////////////////////////
-
- /*! directory relation for \a name */
- virtual QCString trDirRelation(const QCString &name)
- {
- return QCString(name)+" Կապ";
- }
-
- /*! Loading message shown when loading search results */
- virtual QCString trLoading()
- {
- return "Բեռնում...";
- }
-
- /*! Label used for search results in the global namespace */
- virtual QCString trGlobalNamespace()
- {
- return "Գլոբալ անունների տարածություն";
- }
-
- /*! Message shown while searching */
- virtual QCString trSearching()
- {
- return "Որոնում...";
- }
-
- /*! Text shown when no search results are found */
- virtual QCString trNoMatches()
- {
- return "Անարդյունք";
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.6.3 (missing items for the directory pages)
-//////////////////////////////////////////////////////////////////////////
-
- /*! when clicking a directory dependency label, a page with a
- * table is shown. The heading for the first column mentions the
- * source file that has a relation to another file.
- */
- virtual QCString trFileIn(const QCString &name)
- {
- return "Ֆայլը " + name + " ում";
- }
-
- /*! when clicking a directory dependency label, a page with a
- * table is shown. The heading for the second column mentions the
- * destination file that is included.
- */
- virtual QCString trIncludesFileIn(const QCString &name)
- {
- return "Ներառում է ֆայլը " + name + " ում";
- }
-
- /** Compiles a date string.
- * @param year Year in 4 digits
- * @param month Month of the year: 1=January
- * @param day Day of the Month: 1..31
- * @param dayOfWeek Day of the week: 1=Monday..7=Sunday
- * @param hour Hour of the day: 0..23
- * @param minutes Minutes in the hour: 0..59
- * @param seconds Seconds within the minute: 0..59
- * @param includeTime Include time in the result string?
- */
- virtual QCString trDateTime(int year,int month,int day,int dayOfWeek,
- int hour,int minutes,int seconds,
- bool includeTime)
- {
- static const char *days[] = { "Երկուշաբթի,","Երեքշաբթի,","Չորեքշաբթի,","Հինգշաբթի,",
- "Ուրբաթ,","Շաբաթ,","Կիրակի," };
- static const char *months[] = { "Հունիսի","Փետրվարի","Մարտի","Ապրրիլի","Մայիսի","Հունիսի",
- "Հուլիսի","Օգոստոսի","Սեպտեմբերի","Հոկտեբմերի","Նոյեմբերի","Դեկտեմբերի" };
- QCString sdate;
- sdate.sprintf("%s %d %s %d",days[dayOfWeek-1],day,months[month-1],year);
- if (includeTime)
- {
- QCString stime;
- stime.sprintf(" %.2d:%.2d:%.2d ",hour,minutes,seconds);
- sdate+=stime;
- }
- return sdate;
- }
-
-//////////////////////////////////////////////////////////////////////////
-// new since 1.7.5
-//////////////////////////////////////////////////////////////////////////
-
- /*! Header for the page with bibliographic citations */
- virtual QCString trCiteReferences()
- { return "Գրականություն"; }
-
- /*! Text for copyright paragraph */
- virtual QCString trCopyright()
- { return "Հեղինակային իրավունք"; }
-
- /*! Header for the graph showing the directory dependencies */
- virtual QCString trDirDepGraph(const QCString &name)
- { return name + QCString("-ի ֆայլադարանների կախվածությունների գծագիր:"); }
-
-};
-#endif
+/****************************************************************************** + * + * + * + * Copyright (C) 1997-2015 by Dimitri van Heesch. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation under the terms of the GNU General Public License is hereby + * granted. No representations are made about the suitability of this software + * for any purpose. It is provided "as is" without express or implied warranty. + * See the GNU General Public License for more details. + * + * Documents produced by Doxygen are derivative works derived from the + * input used in their production; they are not affected by this license. + */ + +/* + * Translation by + * Armen Tangamyan <armen.tangamyan@anu.edu.au> + */ + +#ifndef TRANSLATOR_AM_H +#define TRANSLATOR_AM_H + +class TranslatorArmenian : public TranslatorAdapter_1_8_0 +{ + public: + /*! Used for identification of the language. */ + virtual QCString idLanguage() + { return "armenian"; } + + /* Used to get the command(s) for the language support. */ + virtual QCString latexLanguageSupportCommand() + { + return "\\usepackage[latin]{armtex}\n" + "\\usepackage[armscii8]{inputenc}\n"; + } + virtual QCString trISOLang() + { return "hy"; } + virtual QCString getLanguageString() + { + return "0x42b Armenian"; + } + + // --- Language translation methods ------------------- + + /*! used in the compound documentation before a list of related functions. */ + virtual QCString trRelatedFunctions() + { return "Դասին վերաբերվող ֆունկցիաներ"; } + + /*! subscript for the related functions. */ + virtual QCString trRelatedSubscript() + { return "(Հաշվի առեք, որ սրանք անդամ ֆունկցիաներ չեն)"; } + + /*! header that is put before the detailed description of files, classes and namespaces. */ + virtual QCString trDetailedDescription() + { return "Մանրամասն նկարագրություն"; } + + /*! header that is put before the list of typedefs. */ + virtual QCString trMemberTypedefDocumentation() + { return "Անդամ տիպի սահմանումներ (typedef)"; } + + /*! header that is put before the list of enumerations. */ + virtual QCString trMemberEnumerationDocumentation() + { return "Անդամ hամարակալումներ"; } + + /*! header that is put before the list of member functions. */ + virtual QCString trMemberFunctionDocumentation() + { return "Անդամ ֆունկցիաներ"; } + + /*! header that is put before the list of member attributes. */ + virtual QCString trMemberDataDocumentation() + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + { + return "Դաշտեր"; + } + else + { + return "Անդամ տվյալներ"; + } + } + + /*! this is the text of a link put after brief descriptions. */ + virtual QCString trMore() + { return "Մանրամասն..."; } + + /*! put in the class documentation */ + /* Isn't used when optimization for C is on. */ + virtual QCString trListOfAllMembers() + { + return "Բոլոր անդամների ցուցակը"; + } + + /*! used as the title of the "list of all members" page of a class */ + /* Isn't used when optimization for C is on. */ + virtual QCString trMemberList() + { + return "Անդամների ցուցակ"; + } + + /*! this is the first part of a sentence that is followed by a class name */ + /* Isn't used when optimization for C is on. */ + virtual QCString trThisIsTheListOfAllMembers() + { return "Սա դասի անդամների ամբողջական ցուցակն է "; } + + /*! this is the remainder of the sentence after the class name */ + /* Isn't used when optimization for C is on. */ + virtual QCString trIncludingInheritedMembers() + { return ", ներառյալ բոլոր ժառանգված անդամները"; } + + /*! this is put at the author sections at the bottom of man pages. + * parameter s is name of the project name. + */ + virtual QCString trGeneratedAutomatically(const QCString &s) + { QCString result="Ավտոմատ ստեղծված է ելքային կոդից, Doxygen-ի միջոցով, "; + if (!s.isEmpty()) result+=s+" համար:"; + return result; + } + + /*! put after an enum name in the list of all members */ + virtual QCString trEnumName() + { return "համարակալման անուն"; } + + /*! put after an enum value in the list of all members */ + virtual QCString trEnumValue() + { return "համարակալման արժեք"; } + + /*! put after an undocumented member in the list of all members */ + virtual QCString trDefinedIn() + { return "սահմանված"; } + + // quick reference sections + + /*! This is put above each page as a link to the list of all groups of + * compounds or files (see the \\group command). + */ + virtual QCString trModules() + { return "Մոդուլներ"; } + + /*! This is put above each page as a link to the class hierarchy */ + virtual QCString trClassHierarchy() + { return "Դասերի հիերարխա"; } + + /*! This is put above each page as a link to the list of annotated classes */ + virtual QCString trCompoundList() + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + { + return "Տվյալների կառուցվածք"; + } + else + { + return "Դասերի ցուցակ"; + } + } + + /*! This is put above each page as a link to the list of documented files */ + virtual QCString trFileList() + { return "Ֆայլերի ցուցակ"; } + + /*! This is put above each page as a link to all members of compounds. */ + virtual QCString trCompoundMembers() + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + { + return "Տվյալների դաշտեր"; + } + else + { + return "Դասի անդամներ"; + } + } + + /*! This is put above each page as a link to all members of files. */ + /*??*/ + virtual QCString trFileMembers() + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + { + return "Գլոբալներ"; + } + else + { + return "Ֆայլի անդամներ"; + } + } + + /*! This is put above each page as a link to all related pages. */ + virtual QCString trRelatedPages() + { return "Նմանատիպ էջեր"; } + + /*! This is put above each page as a link to all examples. */ + virtual QCString trExamples() + { return "Օրինակներ"; } + + /*! This is put above each page as a link to the search engine. */ + virtual QCString trSearch() + { return "Որոնում"; } + + /*! This is an introduction to the class hierarchy. */ + virtual QCString trClassHierarchyDescription() + { return "Այս ժառանգման ցուցակը կոպտորեն է տեսակավորված, " + "բայց ոչ ամբողջապես, այբբենական կարգով."; + } + + /*! This is an introduction to the list with all files. */ + virtual QCString trFileListDescription(bool extractAll) + { + QCString result="Բոլոր "; + if (!extractAll) result+="փաստագրված "; + result+="ֆայլերի մի ցուցակ` կարճ բացատրություններով:"; + return result; + } + + /*! This is an introduction to the annotated compound list. */ + virtual QCString trCompoundListDescription() + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + { + return "Տվյալների կառուցվածքը` կարճ բացատրություններով."; + } + else + { + return "Դասերը, կառուցվածքները, միավորումները " + "և ինտերֆեյսները` կարճ բացատրություններով."; + } + } + + /*! This is an introduction to the page with all class members. */ + virtual QCString trCompoundMembersDescription(bool extractAll) + { + QCString result="Բոլոր "; + if(!extractAll) result+="փաստագրված "; + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + result+="կառուցվածքների և միավորումների դաշտերի "; + else + result+="դասի անդամների "; + result+="ցուցակը`"; + result+=" հղումներով դեպի "; + if(!extractAll) + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + result+="կառուցվածք/միավորում փաստագրությունները բոլոր անդամների համար."; + else + result+="դասի փաստագրությունը բոլոր անդամների համար."; + } + else + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + result += "կառուցվածքները/միավորումները, որոնց նրանք պատկանում են."; + else + result += "դասերը, որոնց նրանք պատկանում են."; + } + return result; + } + + /*! This is an introduction to the page with all file members. */ + virtual QCString trFileMembersDescription(bool extractAll) + { + QCString result="Բոլոր "; + if (!extractAll) result+="փաստագրված "; + + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + { + result+="ֆունկցիաների, փոփոխականների, մակրո-հայտարարությունների, " + "համարակալումների և տիպի սահմանումների (typedef)"; + } + else + { + result+="ֆայլի անդամների "; + } + result+="ցուցակը`"; + result+=" հղումներով դեպի "; + if (extractAll) + result+="ֆայլերը, որոնց նրանք պատկանում են."; + else + result+="փաստագրությունը."; + return result; + } + + /*! This is an introduction to the page with the list of all examples */ + virtual QCString trExamplesDescription() + { return "Բոլոր օրինակների ցուցակը."; } + + /*! This is an introduction to the page with the list of related pages */ + virtual QCString trRelatedPagesDescription() + { return "Բոլոր նմանատիպ փաստագրության էջերի ցուցակը."; } + + /*! This is an introduction to the page with the list of class/file groups */ + virtual QCString trModulesDescription() + { return "Բոլոր մոդուլների ցուցակը."; } + + // index titles (the project name is prepended for these) + + + /*! This is used in HTML as the title of index.html. */ + virtual QCString trDocumentation() + { return " - Փաստագրություն"; } + + /*! This is used in LaTeX as the title of the chapter with the + * index of all groups. + */ + virtual QCString trModuleIndex() + { return "Մոդուլներ"; } + + /*! This is used in LaTeX as the title of the chapter with the + * class hierarchy. + */ + virtual QCString trHierarchicalIndex() + { return "Դասակարգումներ"; } + + /*! This is used in LaTeX as the title of the chapter with the + * annotated compound index. + */ + virtual QCString trCompoundIndex() + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + { + return "Տվյալների կառուցվածք"; + } + else + { + return "Դասեր"; + } + } + + /*! This is used in LaTeX as the title of the chapter with the + * list of all files. + */ + virtual QCString trFileIndex() + { return "Ֆայլեր"; } + + /*! This is used in LaTeX as the title of the chapter containing + * the documentation of all groups. + */ + virtual QCString trModuleDocumentation() + { return "Մոդուլներ"; } + + /*! This is used in LaTeX as the title of the chapter containing + * the documentation of all classes, structs and unions. + */ + virtual QCString trClassDocumentation() + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + { + return "Տվյալների կառուցվածքներ"; + } + else if (Config_getBool(OPTIMIZE_OUTPUT_VHDL)) + { + return trDesignUnitDocumentation(); + } + else + { + return "Դասեր"; + } + } + + /*! This is used in LaTeX as the title of the chapter containing + * the documentation of all files. + */ + virtual QCString trFileDocumentation() + { return "Ֆայլեր"; } + + /*! This is used in LaTeX as the title of the chapter containing + * the documentation of all examples. + */ + virtual QCString trExampleDocumentation() + { return "Օրինակներ"; } + + /*! This is used in LaTeX as the title of the document */ + virtual QCString trReferenceManual() + { return "Հղումների ձեռնարկ"; } + + /*! This is used in the documentation of a file as a header before the + * list of defines + */ + virtual QCString trDefines() + { return "Մակրոսներ"; } + + /*! This is used in the documentation of a file as a header before the + * list of typedefs + */ + virtual QCString trTypedefs() + { return "Տիպի սահմանումներ (typedef)"; } + + /*! This is used in the documentation of a file as a header before the + * list of enumerations + */ + virtual QCString trEnumerations() + { return "Համարակալումներ"; } + + /*! This is used in the documentation of a file as a header before the + * list of (global) functions + */ + virtual QCString trFunctions() + { return "Ֆունկցիաներ"; } + + /*! This is used in the documentation of a file as a header before the + * list of (global) variables + */ + virtual QCString trVariables() + { return "Փոփոխականներ"; } + + /*! This is used in the documentation of a file as a header before the + * list of (global) variables + */ + virtual QCString trEnumerationValues() + { return "Հաշվիչ"; } + + /*! This is used in the documentation of a file before the list of + * documentation blocks for defines + */ + virtual QCString trDefineDocumentation() + { return "Մակրոսներ"; } + + /*! This is used in the documentation of a file/namespace before the list + * of documentation blocks for typedefs + */ + virtual QCString trTypedefDocumentation() + { return "Տիպի սահմանումներ (typedef)"; } + + /*! This is used in the documentation of a file/namespace before the list + * of documentation blocks for enumeration types + */ + virtual QCString trEnumerationTypeDocumentation() + { return "Համարակալման տիպեր"; } + + /*! This is used in the documentation of a file/namespace before the list + * of documentation blocks for functions + */ + virtual QCString trFunctionDocumentation() + { return "Ֆունկցիաներ"; } + + /*! This is used in the documentation of a file/namespace before the list + * of documentation blocks for variables + */ + virtual QCString trVariableDocumentation() + { return "Փոփոխականներ"; } + + /*! This is used in the documentation of a file/namespace/group before + * the list of links to documented compounds + */ + virtual QCString trCompounds() + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + { + return "Տվյալների կառուցվածք"; + } + else + { + return "Դասեր"; + } + } + + /*! This is used in the documentation of a group before the list of + * links to documented files + */ + /*! This is used in the standard footer of each page and indicates when + * the page was generated + */ + virtual QCString trGeneratedAt(const QCString &date,const QCString &projName) + { + QCString result=QCString("Ստեղծվել է ")+date; + if (!projName.isEmpty()) result+=projName+" -ի համար,"; + result+=" հետևյալ համակարգով."; + return result; + } + + /*! this text is put before a class diagram */ + virtual QCString trClassDiagram(const QCString &clName) + { + return clName+QCString(" -ի ժառանգման գծագիրը."); + } + + /*! this text is generated when the \\warning command is used. */ + virtual QCString trWarning() + { return "Զգուշացում"; } + + /*! this text is generated when the \\version command is used. */ + virtual QCString trVersion() + { return "Տարբերակ"; } + + /*! this text is generated when the \\date command is used. */ + virtual QCString trDate() + { return "Տարեթիվ"; } + + /*! this text is generated when the \\return command is used. */ + virtual QCString trReturns() + { return "Վերադարձնում է"; } + + /*! this text is generated when the \\sa command is used. */ + virtual QCString trSeeAlso() + { return "Տեսեք նաև"; } + + /*! this text is generated when the \\param command is used. */ + virtual QCString trParameters() + { return "Պարամետրեր"; } + + /*! this text is generated when the \\exception command is used. */ + virtual QCString trExceptions() + { return "Բացառություններ"; } + + /*! this text is used in the title page of a LaTeX document. */ + virtual QCString trGeneratedBy() + { return "Ստեղծված է հետևյալ համակարգի կողմից"; } + +////////////////////////////////////////////////////////////////////////// +// new since 0.49-990307 +////////////////////////////////////////////////////////////////////////// + + /*! used as the title of page containing all the index of all namespaces. */ + virtual QCString trNamespaceList() + { return "Անունների տարածությունների ցուցակ"; } + + /*! used as an introduction to the namespace list */ + virtual QCString trNamespaceListDescription(bool extractAll) + { + QCString result="Բոլոր "; + if (!extractAll) result+="փաստագրված "; + result+="անունների տարածությունների ցուցակը` կարճ բացատրություններով."; + return result; + } + + /*! used in the class documentation as a header before the list of all + * friends of a class + */ + virtual QCString trFriends() + { return "Ընկերներ"; } + +////////////////////////////////////////////////////////////////////////// +// new since 0.49-990405 +////////////////////////////////////////////////////////////////////////// + + /*! used in the class documentation as a header before the list of all + * related classes + */ + virtual QCString trRelatedFunctionDocumentation() + { return "Դասի ընկերներ և կապված ֆունկցիաներ"; } + +////////////////////////////////////////////////////////////////////////// +// new since 0.49-990425 +////////////////////////////////////////////////////////////////////////// + + /*! used as the title of the HTML page of a class/struct/union */ + virtual QCString trCompoundReference(const QCString &clName, + ClassDef::CompoundType compType, + bool isTemplate) + { + QCString result=clName; + if (isTemplate) + { + switch(compType) + { + case ClassDef::Class: result+=" Դասի"; break; + case ClassDef::Struct: result+=" Կառուցվածքի"; break; + case ClassDef::Union: result+=" Միավորման"; break; + case ClassDef::Interface: result+=" Ինտերֆեյսի"; break; + case ClassDef::Protocol: result+=" Արձանագրության"; break; + case ClassDef::Category: result+=" Դասակարգման"; break; + case ClassDef::Exception: result+=" Բացառության"; break; + default: break; + } + result+=" Ձևանմուշներ"; + } + else + { + switch(compType) + { + case ClassDef::Class: result+=" Դաս"; break; + case ClassDef::Struct: result+=" Կառուցվածք"; break; + case ClassDef::Union: result+=" Միավորում"; break; + case ClassDef::Interface: result+=" Ինտերֆեյս"; break; + case ClassDef::Protocol: result+=" Արձանագրություն"; break; + case ClassDef::Category: result+=" Դասակարգում"; break; + case ClassDef::Exception: result+=" Բացառություն"; break; + default: break; + } + } + return result; + } + + /*! used as the title of the HTML page of a file */ + virtual QCString trFileReference(const QCString &fileName) + { + return fileName+QCString(" ֆայլեր"); + } + + /*! used as the title of the HTML page of a namespace */ + virtual QCString trNamespaceReference(const QCString &namespaceName) + { + QCString result=namespaceName; + result+=" անունների տարածություններ"; + return result; + } + + virtual QCString trPublicMembers() + { return "Բաց անդամ ֆունկցիաներ"; } + virtual QCString trPublicSlots() + { return "Բաց սլոթեր"; } + virtual QCString trSignals() + { return "Ազդանշաններ"; } + virtual QCString trStaticPublicMembers() + { return "Բաց ստատիկ անդամ ֆունկցիաներ"; } + virtual QCString trProtectedMembers() + { return "Պաշտպանված անդամ ֆունկցիաներ"; } + virtual QCString trProtectedSlots() + { return "Պաշտպանված սլոթեր"; } + virtual QCString trStaticProtectedMembers() + { return "Պաշտպանված ստատիկ անդամ ֆունկցիաներ"; } + virtual QCString trPrivateMembers() + { return "Փակ ֆունկցիաներ"; } + virtual QCString trPrivateSlots() + { return "Փակ սլոթեր"; } + virtual QCString trStaticPrivateMembers() + { return "Փակ ստատիկ անդամ ֆունկցիաներ"; } + + /*! this function is used to produce a comma-separated list of items. + * use generateMarker(i) to indicate where item i should be put. + */ + virtual QCString trWriteList(int numEntries) + { + QCString result; + int i; + // the inherits list contain `numEntries' classes + for (i=0;i<numEntries;i++) + { + // use generateMarker to generate placeholders for the class links! + result+=generateMarker(i); // generate marker for entry i in the list + // (order is left to right) + + if (i!=numEntries-1) // not the last entry, so we need a separator + { + if (i<numEntries-2) // not the fore last entry + result+=", "; + else // the fore last entry + result+=" և "; + } + } + return result; + } + + /*! used in class documentation to produce a list of base classes, + * if class diagrams are disabled. + */ + virtual QCString trInheritsList(int numEntries) + { + return "Հենքային դասեր - "+trWriteList(numEntries)+":"; + } + + /*! used in class documentation to produce a list of super classes, + * if class diagrams are disabled. + */ + virtual QCString trInheritedByList(int numEntries) + { + return "Ժառանգորդ դասեր - "+trWriteList(numEntries)+":"; + } + + /*! used in member documentation blocks to produce a list of + * members that are hidden by this one. + */ + virtual QCString trReimplementedFromList(int numEntries) + { + return "Վերասահմանված ֆունկցիաներ - "+trWriteList(numEntries)+":"; + } + + /*! used in member documentation blocks to produce a list of + * all member that overwrite the implementation of this member. + */ + virtual QCString trReimplementedInList(int numEntries) + { + return "Վերասահմանված է "+trWriteList(numEntries)+" ում:"; + } + + /*! This is put above each page as a link to all members of namespaces. */ + virtual QCString trNamespaceMembers() + { return "Անունների տարածության անդամներ"; } + + /*! This is an introduction to the page with all namespace members */ + virtual QCString trNamespaceMemberDescription(bool extractAll) + { + QCString result="Բոլոր "; + if (!extractAll) result+="փաստագրված "; + result+="անունների տարածության անդամների ցուցակը` " + "հղումներով դեպի "; + if (extractAll) + result+="բոլոր անդամների անունների տարածության փաստագրությունը."; + else + result+="անունների տարածությունը, որին նրանք պատկանում են."; + return result; + } + + /*! This is used in LaTeX as the title of the chapter with the + * index of all namespaces. + */ + virtual QCString trNamespaceIndex() + { return "Անունների տարածություններ"; } + + /*! This is used in LaTeX as the title of the chapter containing + * the documentation of all namespaces. + */ + virtual QCString trNamespaceDocumentation() + { return "Անունների տարածություն"; } + +////////////////////////////////////////////////////////////////////////// +// new since 0.49-990522 +////////////////////////////////////////////////////////////////////////// + + /*! This is used in the documentation before the list of all + * namespaces in a file. + */ + virtual QCString trNamespaces() + { return "Անունների տարածություններ"; } + +////////////////////////////////////////////////////////////////////////// +// new since 0.49-990728 +////////////////////////////////////////////////////////////////////////// + + /*! This is put at the bottom of a class documentation page and is + * followed by a list of files that were used to generate the page. + */ + virtual QCString trGeneratedFromFiles(ClassDef::CompoundType compType, + bool single) + { + QCString result = "Այս "; + switch(compType) + { + case ClassDef::Class: result+="դասի"; break; + case ClassDef::Struct: result+="կառուցվածքի"; break; + case ClassDef::Union: result+="միավորման"; break; + case ClassDef::Interface: result+="ինտերֆեյսի"; break; + case ClassDef::Protocol: result+="արձանագրության"; break; + case ClassDef::Category: result+="դասակարգման"; break; + case ClassDef::Exception: result+="բացառության"; break; + default: break; + } + result+=" փաստագրությունը ստեղծվել է հետևյալ ֆայլ"; + if (single) result+="ից."; else result+="երից."; + return result; + } + +////////////////////////////////////////////////////////////////////////// +// new since 0.49-990901 +////////////////////////////////////////////////////////////////////////// + + /*! This is used as the heading text for the retval command. */ + virtual QCString trReturnValues() + { return "Վերադարձվող արժեքներ"; } + + /*! This is in the (quick) index as a link to the main page (index.html) + */ + virtual QCString trMainPage() + { return "Գլխավոր էջ"; } + + /*! This is used in references to page that are put in the LaTeX + * documentation. It should be an abbreviation of the word page. + */ + virtual QCString trPageAbbreviation() + { return "էջ:"; } + +////////////////////////////////////////////////////////////////////////// +// new since 0.49-991106 +////////////////////////////////////////////////////////////////////////// + + virtual QCString trDefinedAtLineInSourceFile() + { + return "Սահմանումը @1 ֆայլի @0 տողում է:"; + } + virtual QCString trDefinedInSourceFile() + { + return "Սահմանումը @0 ֆայլում է:"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 0.49-991205 +////////////////////////////////////////////////////////////////////////// + + virtual QCString trDeprecated() + { + return "Հնացած է"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.0.0 +////////////////////////////////////////////////////////////////////////// + + /*! this text is put before a collaboration diagram */ + virtual QCString trCollaborationDiagram(const QCString &clName) + { + return clName+"-ի համագործակցությունների գծագիր."; + } + /*! this text is put before an include dependency graph */ + virtual QCString trInclDepGraph(const QCString &fName) + { + return fName+"-ի ներառումների կախվածությունների գծագիր."; + } + /*! header that is put before the list of constructor/destructors. */ + virtual QCString trConstructorDocumentation() + { + return "Կառուցիչներ"; + } + /*! Used in the file documentation to point to the corresponding sources. */ + virtual QCString trGotoSourceCode() + { + return "Տե'ս այս ֆայլի ելքային կոդը"; + } + /*! Used in the file sources to point to the corresponding documentation. */ + virtual QCString trGotoDocumentation() + { + return "Տե'ս այս ֆայլի փաստագրությունը:"; + } + /*! Text for the \\pre command */ + virtual QCString trPrecondition() + { + return "Նախապայման"; + } + /*! Text for the \\post command */ + virtual QCString trPostcondition() + { + return "Հետպայման"; + } + /*! Text for the \\invariant command */ + virtual QCString trInvariant() + { + return "Անփոփոխ"; + } + /*! Text shown before a multi-line variable/enum initialization */ + virtual QCString trInitialValue() + { + return "Նախնական արժեք"; + } + /*! Text used the source code in the file index */ + virtual QCString trCode() + { + return "Ելքային կոդ"; + } + virtual QCString trGraphicalHierarchy() + { + return "Գրաֆիկական դասերի հիերարխիա:"; + } + virtual QCString trGotoGraphicalHierarchy() + { + return "Տե'ս դասերի գրաֆիկական հիերարխիան:"; + } + virtual QCString trGotoTextualHierarchy() + { + return "Տե'ս դասերի տեքստային հիերարխիան:"; + } + virtual QCString trPageIndex() + { + return "էջեր"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.1.0 +////////////////////////////////////////////////////////////////////////// + + virtual QCString trNote() + { + return "Նշում"; + } + virtual QCString trPublicTypes() + { + return "Բաց տիպեր"; + } + virtual QCString trPublicAttribs() + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + { + return "Տվյալների դաշտեր"; + } + else + { + return "Բաց ատրիբուտներ"; + } + } + virtual QCString trStaticPublicAttribs() + { + return "Բաց ստատիկ ատրիբուտներ"; + } + virtual QCString trProtectedTypes() + { + return "Պաշտպանված տիպեր"; + } + virtual QCString trProtectedAttribs() + { + return "Պաշտպանված ատրիբուտներ"; + } + virtual QCString trStaticProtectedAttribs() + { + return "Պաշտպանված ստատիկ ատրիբուտներ"; + } + virtual QCString trPrivateTypes() + { + return "Փակ տիպեր"; + } + virtual QCString trPrivateAttribs() + { + return "Փակ ատրիբուտներ"; + } + virtual QCString trStaticPrivateAttribs() + { + return "Փակ ստատիկ ատրիբուտներ"; + } + + +////////////////////////////////////////////////////////////////////////// +// new since 1.1.3 +////////////////////////////////////////////////////////////////////////// + + /*! Used as a marker that is put before a todo item */ + virtual QCString trTodo() + /*??*/ + { + return "Կատարման ենթակա"; + } + /*! Used as the header of the todo list */ + virtual QCString trTodoList() + /*??*/ + { + return "Խնդիրների ցուցակ"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.1.4 +////////////////////////////////////////////////////////////////////////// + + virtual QCString trReferencedBy() + { + return "Օգտագործվում է հետևյալում - "; + } + virtual QCString trRemarks() + { + return "Դիտողություններ"; + } + virtual QCString trAttention() + { + return "Ուշադրություն"; + } + virtual QCString trInclByDepGraph() + { + return "Այս գրաֆը ցույց է տալիս, թե որ ֆայլերն են " + "ուղղակի կամ անուղղակի ներառում տվյալ ֆայլը."; + } + virtual QCString trSince() + /*??*/ + { + return "Սկսած"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.1.5 +////////////////////////////////////////////////////////////////////////// + + /*! title of the graph legend page */ + virtual QCString trLegendTitle() + { + return "Լեգենդ"; + } + /*! page explaining how the dot graph's should be interpreted + * The %A in the text below are to prevent link to classes called "A". + */ + virtual QCString trLegendDocs() + { + return + "Այս էջը նկարագրում է, թե ինչպես մեկնաբանել doxygen-ի ստեղծած գրաֆները:<p>\n" + "Դիտարկենք հետևյալ օրինակը.\n" + "\\code\n" + "/*! Կրճատման հետևանքով անտեսանելի դաս */\n" + "class Invisible { };\n\n" + "/*! Կրճատված դաս, ժառանգությունների հարաբերությունը փակ է */\n" + "class Truncated : public Invisible { };\n\n" + "/* Չփաստագրված դաս */\n" + "class Undocumented { };\n\n" + "/*! Բաց ժառանգում */\n" + "class PublicBase : public Truncated { };\n\n" + "/*! Դասի ձևաչափ */\n" + "template<class T> class Templ {};\n\n" + "/*! Պաշտպանված ժառանգում */\n" + "class ProtectedBase { };\n\n" + "/*! Փակ ժառանգում */\n" + "class PrivateBase { };\n\n" + "/*! Դաս, որը օգտագործվում է Inherited դասի կողմից */\n" + "class Used { };\n\n" + "/*! Դաս, որը ժառանգում է մի շարք այլ դասերից */\n" + "class Inherited : public PublicBase,\n" + " protected ProtectedBase,\n" + " private PrivateBase,\n" + " public Undocumented,\n" + " public Templ<int>\n" + "{\n" + " private:\n" + " Used *m_usedClass;\n" + "};\n" + "\\endcode\n" + "Սրանով կստանանք հետևյալ գրաֆը." + "<p><center><img src=\"graph_legend."+getDotImageExtension()+"\"></center>\n" + "<p>\n" + "Այս գրաֆի ուղղանկյունները ունեն հետևյալ իմաստը.\n" + "<ul>\n" + "<li>%A լցոնվաց մոխրագույն ուղղանկյունը ներկայացնում է այն դասը կամ կառուցվածքը, " + "որի համար ստեղծվել է տվյալ գրաֆը:</li>\n" + "<li>%A սև եզրերով ուղղանկյունը նշանակում է փաստագրված դաս կամ կարուցվածք:</li>\n" + "<li>%A մոխրագույն եզրերով ուղղանկյունը նշանակում է չփաստագրված դաս կամ կառուցվածք:</li>\n" + "<li>%A կարմիր եզրերով ուղղանկյունը նշանակում է դաս կամ կառուցվածք, որի համար\n" + " ոչ բոլոր ժառանգում/պարունակում կապերն են ցուցադրված: Գրաֆը կրճատված է, " + "եթե այն չի տեղավորվում նշված սահմաններում:</li>\n" + "</ul>\n" + "Սլաքները ունեն հետևյալ իմաստը.\n" + "<ul>\n" + "<li>%A մուգ կապույտ սլաքը օգտագործվում է երկու դասերի միջև բաց ժառանգում " + "կապը ցուցադրելու համար:</li>\n" + "<li>%A մուգ կանաչ սլաքը օգտագործվում է պաշտպանված ժառանգման համար:</li>\n" + "<li>%A մուգ կարմիր սլաքը օգտագործվում է փակ ժառանգման համար:</li>\n" + "<li>%A մանուշակագույն կետագիծ սլաքը օգտագորշվում է, եթե դասը պարունակվում է" + "այլ դասում կամ օգտագորշվում է այլ դասի կողմից: Սլաքը պիտակավորվաշ է" + "փոփոխական(ներ)ով, որի միջոցով մատնանշված դասը կամ կառուցվածքը հասանելի է:</li>\n" + "<li>Դեզին կետագիծ սլաքը ցույց է տալիս ձևանմուշի օրինակի կապը այն ձևանմուշի հետ, " + "որից այն իրականցվել է. Սլաքը պիտակավորված է օրինակի ձևանմուշային պարամետրերով:</li>\n" + "</ul>\n"; + } + /*! text for the link to the legend page */ + virtual QCString trLegend() + { + return "լեգենդ"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.2.0 +////////////////////////////////////////////////////////////////////////// + + /*! Used as a marker that is put before a test item */ + virtual QCString trTest() + { + return "Թեստ"; + } + /*! Used as the header of the test list */ + virtual QCString trTestList() + { + return "Թեստերի ցուցակ"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.2.2 +////////////////////////////////////////////////////////////////////////// + + /*! Used as a section header for IDL properties */ + virtual QCString trProperties() + { + return "Հատկություններ"; + } + /*! Used as a section header for IDL property documentation */ + virtual QCString trPropertyDocumentation() + { + return "Հատկություններ"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.2.4 +////////////////////////////////////////////////////////////////////////// + + /*! Used for Java classes in the summary section of Java packages */ + virtual QCString trClasses() + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + { + return "Տվյալների կառուցվածք"; + } + else + { + return "Դասեր"; + } + } + /*! Used as the title of a Java package */ + virtual QCString trPackage(const QCString &name) + { + return "Փաթեթ "+name; + } + /*! The description of the package index page */ + virtual QCString trPackageListDescription() + { + return "Բոլոր փաթեթները` կարճ բացատրություններով (եթե հասանելի են)."; + } + /*! The link name in the Quick links header for each page */ + virtual QCString trPackages() + { + return "Փաթեթներ"; + } + /*! Text shown before a multi-line define */ + virtual QCString trDefineValue() + { + return "Արժեքներ"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.2.5 +////////////////////////////////////////////////////////////////////////// + + /*! Used as a marker that is put before a \\bug item */ + virtual QCString trBug() + { + return "Սխալ"; + } + /*! Used as the header of the bug list */ + virtual QCString trBugList() + { + return "Սխալների ցուցակ"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.2.6 +////////////////////////////////////////////////////////////////////////// + /*! Used as ansicpg for RTF file */ + virtual QCString trRTFansicp() + { + return "armscii-8"; + } + /*! Used as ansicpg for RTF fcharset */ + virtual QCString trRTFCharSet() + { + return "0"; + } + /*! Used as header RTF general index */ + virtual QCString trRTFGeneralIndex() + { + return "Ցուցիչ"; + } + + /*! This is used for translation of the word that will possibly + * be followed by a single name or by a list of names + * of the category. + */ + virtual QCString trClass(bool first_capital, bool singular) + { + if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) + { + QCString result((first_capital ? "Տվյալների կառուցվածք" : "տվյալների կառուցվածք")); + return result; + } + else + { + QCString result((first_capital ? "Դաս" : "դաս")); + if(!singular) result+="եր"; + return result; + } + } + + /*! This is used for translation of the word that will possibly + * be followed by a single name or by a list of names + * of the category. + */ + virtual QCString trFile(bool first_capital, bool singular) + { + QCString result((first_capital ? "Ֆայլ" : "ֆայլ")); + if (!singular) result+="եր"; + return result; + } + + /*! This is used for translation of the word that will possibly + * be followed by a single name or by a list of names + * of the category. + */ + virtual QCString trNamespace(bool first_capital, bool singular) + { + QCString result((first_capital ? "Անունների տարածություն" : "անունների տարածություն")); + if (!singular) result+="ներ"; + return result; + } + + /*! This is used for translation of the word that will possibly + * be followed by a single name or by a list of names + * of the category. + */ + virtual QCString trGroup(bool first_capital, bool singular) + { + QCString result((first_capital ? "Խ" : "խ")); + result+=(singular ? "ումբ" : "մբեր"); + return result; + } + + /*! This is used for translation of the word that will possibly + * be followed by a single name or by a list of names + * of the category. + */ + virtual QCString trPage(bool first_capital, bool singular) + { + QCString result((first_capital ? "Էջ" : "էջ")); + if (!singular) result+="եր"; + return result; + } + + /*! This is used for translation of the word that will possibly + * be followed by a single name or by a list of names + * of the category. + */ + virtual QCString trMember(bool first_capital, bool singular) + { + QCString result((first_capital ? "Անդամ" : "անդամ")); + if (!singular) result+="ներ"; + return result; + } + + /*! This is used for translation of the word that will possibly + * be followed by a single name or by a list of names + * of the category. + */ + virtual QCString trGlobal(bool first_capital, bool singular) + { + QCString result((first_capital ? "Գլոբալ" : "գլոբալ")); + if (!singular) result+="ներ"; + return result; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.2.7 +////////////////////////////////////////////////////////////////////////// + + /*! This text is generated when the \\author command is used and + * for the author section in man pages. */ + virtual QCString trAuthor(bool first_capital, bool singular) + { + QCString result((first_capital ? "Հեղինակ" : "հեղինակ")); + if (!singular) result+="ներ"; + return result; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.2.11 +////////////////////////////////////////////////////////////////////////// + + /*! This text is put before the list of members referenced by a member + */ + virtual QCString trReferences() + { + return "Հղումներ - "; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.2.13 +////////////////////////////////////////////////////////////////////////// + + /*! used in member documentation blocks to produce a list of + * members that are implemented by this one. + */ + virtual QCString trImplementedFromList(int numEntries) + { + return "Իրագործում է հետևյալ դաս(եր)ի ֆունկցիաները - "+trWriteList(numEntries)+":"; + } + + /*! used in member documentation blocks to produce a list of + * all members that implementation this member. + */ + virtual QCString trImplementedInList(int numEntries) + { + return "Իրագործվում է հետևյալում - "+trWriteList(numEntries)+":"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.2.16 +////////////////////////////////////////////////////////////////////////// + + /*! used in RTF documentation as a heading for the Table + * of Contents. + */ + virtual QCString trRTFTableOfContents() + { + return "Բովանդակություն"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.2.17 +////////////////////////////////////////////////////////////////////////// + + /*! Used as the header of the list of item that have been + * flagged deprecated + */ + virtual QCString trDeprecatedList() + { + return "Հնացած սահմանումների ցուցակը"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.2.18 +////////////////////////////////////////////////////////////////////////// + + /*! Used as a header for declaration section of the events found in + * a C# program + */ + virtual QCString trEvents() + { + return "Պատահարներ"; + } + /*! Header used for the documentation section of a class' events. */ + virtual QCString trEventDocumentation() + { + return "Պատահարների ցուցակը"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.3 +////////////////////////////////////////////////////////////////////////// + + /*! Used as a heading for a list of Java class types with package scope. + */ + virtual QCString trPackageTypes() + { + return "Փաթեթի տիպեր"; + } + /*! Used as a heading for a list of Java class functions with package + * scope. + */ + virtual QCString trPackageFunctions() + { + return "Փաթեթի ֆունկցիաներ"; + } + virtual QCString trPackageMembers() + { + return "Փաթեթի անդամներ"; + } + /*! Used as a heading for a list of static Java class functions with + * package scope. + */ + virtual QCString trStaticPackageFunctions() + { + return "Փաթեթի ստատիկ ֆունկցիաներ"; + } + /*! Used as a heading for a list of Java class variables with package + * scope. + */ + virtual QCString trPackageAttribs() + { + return "Փաթեթի ատրիբուտներ"; + } + /*! Used as a heading for a list of static Java class variables with + * package scope. + */ + virtual QCString trStaticPackageAttribs() + { + return "Փաթեթի ստատիկ ատրիբուտներ"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.3.1 +////////////////////////////////////////////////////////////////////////// + + /*! Used in the quick index of a class/file/namespace member list page + * to link to the unfiltered list of all members. + */ + virtual QCString trAll() + { + return "Բոլոր"; + } + /*! Put in front of the call graph for a function. */ + virtual QCString trCallGraph() + { + return "Այս ֆունկցիայի կանչերի գրաֆը."; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.3.3 +////////////////////////////////////////////////////////////////////////// + + /*! This string is used as the title for the page listing the search + * results. + */ + virtual QCString trSearchResultsTitle() + { + return "Որոնման արդյունքները"; + } + /*! This string is put just before listing the search results. The + * text can be different depending on the number of documents found. + * Inside the text you can put the special marker $num to insert + * the number representing the actual number of search results. + * The @a numDocuments parameter can be either 0, 1 or 2, where the + * value 2 represents 2 or more matches. HTML markup is allowed inside + * the returned string. + */ + virtual QCString trSearchResults(int numDocuments) + { + if (numDocuments==0) + { + return "Ներեցեք, բայց Ձեր որոնումը արդյունք չտվեց:"; + } + else if( numDocuments == 1 ) + { + return "Հայտնաբերվել է 1 փաստաթուղթ:"; + } + else + { + return "Հայտնաբերվել է <b>$num</b> փաստաթուղթ:" + "Փաստաթղթերը դասակարգված են ըստ համապասխանության"; + } + } + /*! This string is put before the list of matched words, for each search + * result. What follows is the list of words that matched the query. + */ + virtual QCString trSearchMatches() + { + return "Որոնման արդյունքներ:"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.3.8 +////////////////////////////////////////////////////////////////////////// + + /*! This is used in HTML as the title of page with source code for file filename + */ + virtual QCString trSourceFile(QCString& filename) + { + return "Ելակետային ֆայլ " + filename; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.3.9 +////////////////////////////////////////////////////////////////////////// + + /*! This is used as the name of the chapter containing the directory + * hierarchy. + */ + virtual QCString trDirIndex() + { return "Ֆայլադարանների հիերարխիա"; } + + /*! This is used as the name of the chapter containing the documentation + * of the directories. + */ + virtual QCString trDirDocumentation() + { return "Ֆայլադարաններ"; } + + /*! This is used as the title of the directory index and also in the + * Quick links of a HTML page, to link to the directory hierarchy. + */ + virtual QCString trDirectories() + { return "Ֆայլադրաններ"; } + + /*! This returns the title of a directory page. The name of the + * directory is passed via \a dirName. + */ + virtual QCString trDirReference(const QCString &dirName) + { QCString result=dirName; result+=" Ֆայլադարան"; return result; } + + /*! This returns the word directory with or without starting capital + * (\a first_capital) and in sigular or plural form (\a singular). + */ + virtual QCString trDir(bool first_capital, bool singular) + { + QCString result((first_capital ? "Ֆայլադարան" : "ֆայլադարան")); + if (!singular) result+="ներ"; + return result; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.4.1 +////////////////////////////////////////////////////////////////////////// + + /*! This text is added to the documentation when the \\overload command + * is used for a overloaded function. + */ + virtual QCString trOverloadText() + { + return "Սա վերաբեռնված ֆունկցիա է` տրամադրված հարմարության համար: " + "Այն տարբերվում է նախնականից միայն արգումնետներով:"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.4.6 +////////////////////////////////////////////////////////////////////////// + + /*! This is used to introduce a caller (or called-by) graph */ + virtual QCString trCallerGraph() + { + return "Այս ֆունկցիայի կանչերի գրաֆը."; + } + + /*! This is used in the documentation of a file/namespace before the list + * of documentation blocks for enumeration values + */ + virtual QCString trEnumerationValueDocumentation() + { return "Համարակալումներ"; } + + +////////////////////////////////////////////////////////////////////////// +// new since 1.5.4 (mainly for Fortran) +////////////////////////////////////////////////////////////////////////// + /*! header that is put before the list of member subprograms (Fortran). */ + virtual QCString trMemberFunctionDocumentationFortran() + { return "Անդամ ֆունցիաներ/ենթածրագրեր"; } + + /*! This is put above each page as a link to the list of annotated data types (Fortran). */ + virtual QCString trCompoundListFortran() + { return "Տվյալների տիպերի ցուցակը"; } + + /*! This is put above each page as a link to all members of compounds (Fortran). */ + virtual QCString trCompoundMembersFortran() + { return "Տվյալների դաշտեր"; } + + /*! This is an introduction to the annotated compound list (Fortran). */ + virtual QCString trCompoundListDescriptionFortran() + { return "Տվյալների տիպերը` կարճ բացատրություններով."; } + + /*! This is an introduction to the page with all data types (Fortran). */ + virtual QCString trCompoundMembersDescriptionFortran(bool extractAll) + { + QCString result="Բոլոր "; + if (!extractAll) + { + result+="փաստագրված "; + } + result+="տվյալների տիպերի անդամների ցուցակը` հղումներով դեպի "; + if (!extractAll) + { + result+="բոլոր անդամների տվյալների կառուցվածքի փաստագրությունը"; + } + else + { + result+="տվյալների տիպերը, որոնց նրանք պատկանում են"; + } + return result; + } + + /*! This is used in LaTeX as the title of the chapter with the + * annotated compound index (Fortran). + */ + virtual QCString trCompoundIndexFortran() + { return "Տվյալների տիպեր"; } + + /*! This is used in LaTeX as the title of the chapter containing + * the documentation of all data types (Fortran). + */ + virtual QCString trTypeDocumentation() + { return "Տվյալների տիպեր"; } + + /*! This is used in the documentation of a file as a header before the + * list of (global) subprograms (Fortran). + */ + virtual QCString trSubprograms() + { return "Ֆունկցիաներ/ենթածրագրեր"; } + + /*! This is used in the documentation of a file/namespace before the list + * of documentation blocks for subprograms (Fortran) + */ + virtual QCString trSubprogramDocumentation() + { return "Ֆունկցիաներ/ենթածրագրեր"; } + + /*! This is used in the documentation of a file/namespace/group before + * the list of links to documented compounds (Fortran) + */ + virtual QCString trDataTypes() + { return "Տվյալների տիպեր"; } + + /*! used as the title of page containing all the index of all modules (Fortran). */ + virtual QCString trModulesList() + { return "Մոդուլների ցուցակ"; } + + /*! used as an introduction to the modules list (Fortran) */ + virtual QCString trModulesListDescription(bool extractAll) + { + QCString result="Բոլոր"; + if (!extractAll) result+="փաստագրված "; + result+="մոդուլների ցուցակը` կարճ բացատրություններով."; + return result; + } + + /*! used as the title of the HTML page of a module/type (Fortran) */ + virtual QCString trCompoundReferenceFortran(const QCString &clName, + ClassDef::CompoundType compType, + bool isTemplate) + { + QCString result=clName; + if (!isTemplate) + { + switch(compType) + { + case ClassDef::Class: result+=" Մոդուլ"; break; + case ClassDef::Struct: result+=" Տիպ"; break; + case ClassDef::Union: result+=" Միավորում"; break; + case ClassDef::Interface: result+=" Ինտերֆեյս"; break; + case ClassDef::Protocol: result+=" Արձանագրություն"; break; + case ClassDef::Category: result+=" Դասակարգում"; break; + case ClassDef::Exception: result+=" Բացառություն"; break; + default: break; + } + } + else + { + switch(compType) + { + case ClassDef::Class: result+=" Մոդուլի"; break; + case ClassDef::Struct: result+=" Տիպի"; break; + case ClassDef::Union: result+=" Միավորման"; break; + case ClassDef::Interface: result+=" Ինտերֆեյսի"; break; + case ClassDef::Protocol: result+=" Արձանագրության"; break; + case ClassDef::Category: result+=" Դասակարգման"; break; + case ClassDef::Exception: result+=" Բացառության"; break; + default: break; + } + result+=" Ձևանմուշ"; + } + return result; + } + /*! used as the title of the HTML page of a module (Fortran) */ + virtual QCString trModuleReference(const QCString &namespaceName) + { + return QCString("Մոդուլ ") + namespaceName; + } + + /*! This is put above each page as a link to all members of modules. (Fortran) */ + virtual QCString trModulesMembers() + { return "Մոդուլի անդամներ"; } + + /*! This is an introduction to the page with all modules members (Fortran) */ + virtual QCString trModulesMemberDescription(bool extractAll) + { + QCString result="Մոդուլի բոլոր "; + if (!extractAll) result+="փաստագրված "; + result+="անդամների ցուցակը` հղումներով դեպի "; + if (extractAll) + { + result+="բոլոր անդամների փաստագրությունները."; + } + else + { + result+="մոդուլները, որոնց նրանք պատկանում են."; + } + return result; + } + + /*! This is used in LaTeX as the title of the chapter with the + * index of all modules (Fortran). + */ + virtual QCString trModulesIndex() + { return "Մոդուլներ"; } + + /*! This is used for translation of the word that will possibly + * be followed by a single name or by a list of names + * of the category. + */ + virtual QCString trModule(bool first_capital, bool singular) + { + QCString result((first_capital ? "Մոդուլ" : "մոդուլ")); + if (!singular) result+="ներ"; + return result; + } + /*! This is put at the bottom of a module documentation page and is + * followed by a list of files that were used to generate the page. + */ + virtual QCString trGeneratedFromFilesFortran(ClassDef::CompoundType compType, + bool single) + { // here s is one of " Module", " Struct" or " Union" + // single is true implies a single file + QCString result="Այս "; + switch(compType) + { + case ClassDef::Class: result+="մոդուլի"; break; + case ClassDef::Struct: result+="տիպի"; break; + case ClassDef::Union: result+="միավորման"; break; + case ClassDef::Interface: result+="ինտերֆեյսի"; break; + case ClassDef::Protocol: result+="արձանագրության"; break; + case ClassDef::Category: result+="դասակարգման"; break; + case ClassDef::Exception: result+="բացառության"; break; + default: break; + } + result+=" փաստագրությունը ստեղծվել է հետևալ ֆայլ"; + if (single) result+="ից."; else result+="երից."; + return result; + } + /*! This is used for translation of the word that will possibly + * be followed by a single name or by a list of names + * of the category. + */ + virtual QCString trType(bool first_capital, bool singular) + { + QCString result((first_capital ? "Տիպ" : "տիպ")); + if (!singular) result+="եր"; + return result; + } + /*! This is used for translation of the word that will possibly + * be followed by a single name or by a list of names + * of the category. + */ + virtual QCString trSubprogram(bool first_capital, bool singular) + { + QCString result((first_capital ? "Ե" : "ե")); + if (singular) result+="նթածրագիր"; else result+="նթածրագրեր"; + return result; + } + + /*! C# Type Constraint list */ + virtual QCString trTypeConstraints() + { + return "Տիպերի Սահմանափակումներ"; + } +////////////////////////////////////////////////////////////////////////// +// new since 1.6.0 (mainly for the new search engine) +////////////////////////////////////////////////////////////////////////// + + /*! directory relation for \a name */ + virtual QCString trDirRelation(const QCString &name) + { + return QCString(name)+" Կապ"; + } + + /*! Loading message shown when loading search results */ + virtual QCString trLoading() + { + return "Բեռնում..."; + } + + /*! Label used for search results in the global namespace */ + virtual QCString trGlobalNamespace() + { + return "Գլոբալ անունների տարածություն"; + } + + /*! Message shown while searching */ + virtual QCString trSearching() + { + return "Որոնում..."; + } + + /*! Text shown when no search results are found */ + virtual QCString trNoMatches() + { + return "Անարդյունք"; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.6.3 (missing items for the directory pages) +////////////////////////////////////////////////////////////////////////// + + /*! when clicking a directory dependency label, a page with a + * table is shown. The heading for the first column mentions the + * source file that has a relation to another file. + */ + virtual QCString trFileIn(const QCString &name) + { + return "Ֆայլը " + name + " ում"; + } + + /*! when clicking a directory dependency label, a page with a + * table is shown. The heading for the second column mentions the + * destination file that is included. + */ + virtual QCString trIncludesFileIn(const QCString &name) + { + return "Ներառում է ֆայլը " + name + " ում"; + } + + /** Compiles a date string. + * @param year Year in 4 digits + * @param month Month of the year: 1=January + * @param day Day of the Month: 1..31 + * @param dayOfWeek Day of the week: 1=Monday..7=Sunday + * @param hour Hour of the day: 0..23 + * @param minutes Minutes in the hour: 0..59 + * @param seconds Seconds within the minute: 0..59 + * @param includeTime Include time in the result string? + */ + virtual QCString trDateTime(int year,int month,int day,int dayOfWeek, + int hour,int minutes,int seconds, + bool includeTime) + { + static const char *days[] = { "Երկուշաբթի,","Երեքշաբթի,","Չորեքշաբթի,","Հինգշաբթի,", + "Ուրբաթ,","Շաբաթ,","Կիրակի," }; + static const char *months[] = { "Հունիսի","Փետրվարի","Մարտի","Ապրրիլի","Մայիսի","Հունիսի", + "Հուլիսի","Օգոստոսի","Սեպտեմբերի","Հոկտեբմերի","Նոյեմբերի","Դեկտեմբերի" }; + QCString sdate; + sdate.sprintf("%s %d %s %d",days[dayOfWeek-1],day,months[month-1],year); + if (includeTime) + { + QCString stime; + stime.sprintf(" %.2d:%.2d:%.2d ",hour,minutes,seconds); + sdate+=stime; + } + return sdate; + } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "Երկ", "Երք", "Չրք", "Հնգ", "Ուր", "Շբթ", "Կիր" }; + static const char *days_full[] = { "Երկուշաբթի", "Երեքշաբթի", "Չորեքշաբթի", "Հինգշաբթի", "Ուրբաթ", "Շաբաթ", "Կիրակի" }; + return full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "Հնվ", "Փտվ", "Մրտ", "Ապր", "Մյս", "Հնս", "Հլս", "Օգս", "Սպտ", "Հկտ", "Նյմ", "Դկտ" }; + static const char *months_full[] = { "Հունվար", "Փետրվար", "Մարտ", "Ապրիլ", "Մայիս", "Հունիս", "Հուլիս", "Օգոստոս", "Սեպտեմբեր", "Հոկտեմբեր", "Նոյեմբեր", "Դեկտեմբեր" }; + return full? months_full[month-1] : months_short[month-1]; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } + +////////////////////////////////////////////////////////////////////////// +// new since 1.7.5 +////////////////////////////////////////////////////////////////////////// + + /*! Header for the page with bibliographic citations */ + virtual QCString trCiteReferences() + { return "Գրականություն"; } + + /*! Text for copyright paragraph */ + virtual QCString trCopyright() + { return "Հեղինակային իրավունք"; } + + /*! Header for the graph showing the directory dependencies */ + virtual QCString trDirDepGraph(const QCString &name) + { return name + QCString("-ի ֆայլադարանների կախվածությունների գծագիր:"); } + +}; +#endif diff --git a/src/translator_ar.h b/src/translator_ar.h index 6b6f1cc..38c0d5f 100644 --- a/src/translator_ar.h +++ b/src/translator_ar.h @@ -1509,6 +1509,23 @@ class TranslatorArabic : public TranslatorAdapter_1_4_6 "فقط في نوعية ال argument(s) التي تقبلها."; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "الإثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد" }; + static const char *days_full[] = { "الإثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد" }; + return full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو", "يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر" }; + static const char *months_full[] = { "يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو", "يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر" }; + return full? months_full[month-1] : months_short[month-1]; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "ص", "م" }; + return dayPeriod[period]; + } }; #endif diff --git a/src/translator_bg.h b/src/translator_bg.h index b9639b4..b38bfca 100644 --- a/src/translator_bg.h +++ b/src/translator_bg.h @@ -1831,6 +1831,23 @@ class TranslatorBulgarian : public TranslatorAdapter_1_9_4 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "пон", "вт", "ср", "четв", "пет", "съб", "нед" }; + static const char *days_full[] = { "понеделник", "вторник", "сряда", "четвъртък", "петък", "събота", "неделя" }; + return full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "яну", "фев", "мар", "апр", "май", "юни", "юли", "авг", "сеп", "окт", "ное", "дек" }; + static const char *months_full[] = { "януари", "февруари", "март", "април", "май", "юни", "юли", "август", "септември", "октомври", "ноември", "декември" }; + return full? months_full[month-1] : months_short[month-1]; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "пр.об.", "сл.об." }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_br.h b/src/translator_br.h index 2d7abcb..d7efd64 100644 --- a/src/translator_br.h +++ b/src/translator_br.h @@ -19,6 +19,8 @@ * Thanks to Jorge Ramos, Fernando Carijo and others for their contributions. * * History: + * 20220525: + * - Updated to 1.9.4; * 20211003: * - Updated to 1.9.3; * 20200112: @@ -54,7 +56,7 @@ #ifndef TRANSLATOR_BR_H #define TRANSLATOR_BR_H -class TranslatorBrazilian : public TranslatorAdapter_1_9_4 +class TranslatorBrazilian : public Translator { public: @@ -1897,6 +1899,27 @@ class TranslatorBrazilian : public TranslatorAdapter_1_9_4 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "seg", "ter", "qua", "qui", "sex", "sáb", "dom" }; + static const char *days_full[] = { "segunda-feira", "terça-feira", "quarta-feira", "quinta-feira", "sexta-feira", "sábado", "domingo" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez" }; + static const char *months_full[] = { "janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 @@ -2388,6 +2411,12 @@ class TranslatorBrazilian : public TranslatorAdapter_1_9_4 { return "Definição de conceito"; } + + ////////////////////////////////////////////////////////////////////////// + // new since 1.9.4 + ////////////////////////////////////////////////////////////////////////// + virtual QCString trPackageList() + { return "Lista de pacotes"; } }; #endif diff --git a/src/translator_ca.h b/src/translator_ca.h index 53dbffb..03a52b3 100644 --- a/src/translator_ca.h +++ b/src/translator_ca.h @@ -1819,6 +1819,27 @@ class TranslatorCatalan : public TranslatorAdapter_1_8_0 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "dl.", "dt.", "dc.", "dj.", "dv.", "ds.", "dg." }; + static const char *days_full[] = { "dilluns", "dimarts", "dimecres", "dijous", "divendres", "dissabte", "diumenge" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "gen.", "febr.", "març", "abr.", "maig", "juny", "jul.", "ag.", "set.", "oct.", "nov.", "des." }; + static const char *months_full[] = { "gener", "febrer", "març", "abril", "maig", "juny", "juliol", "agost", "setembre", "octubre", "novembre", "desembre" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "a.m.", "p.m." }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_cn.h b/src/translator_cn.h index 24aa3a1..3f673e3 100644 --- a/src/translator_cn.h +++ b/src/translator_cn.h @@ -1739,7 +1739,7 @@ class TranslatorChinese : public TranslatorAdapter_1_9_4 bool includeTime) { static const char *days[] = { "一","二","三","四","五","六","日" }; - static const char *months[] = { "一","二","三","四","五","六","七","八","九","十","十一","十二" }; + static const char *months[] = { "一","二","三","四","五","六","七","八","九","十","十一","十二" }; QCString sdate; @@ -1753,6 +1753,23 @@ class TranslatorChinese : public TranslatorAdapter_1_9_4 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "周一", "周二", "周三", "周四", "周五", "周六", "周日" }; + static const char *days_full[] = { "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日" }; + return full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月" }; + static const char *months_full[] = { "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" }; + return full? months_full[month-1] : months_short[month-1]; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "上午", "下午" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_cz.h b/src/translator_cz.h index ddd6033..497a729 100644 --- a/src/translator_cz.h +++ b/src/translator_cz.h @@ -20,8 +20,25 @@ // Updates: // -------- -// 2021/08/31 - Updated for 1.9.3 version by: <petyovsky@feec.vutbr.cz>. -// 2021/01/22 - Updated for 1.9.2 version by: <petyovsky@feec.vutbr.cz>. +// 2022/08/25 - Updated for "new since 1.9.4" by: <petyovsky@vut.cz> +// removed all implicit conversion from QCString to const char *, +// fixed issues: #7434, #8404, #9192, +// fixed typo and spacing in: `trLegendDocs()`, +// fixed plural in: `trTemplateParameters()`, +// better translation considering the context: `trMemberTypedefDocumentation()`, +// `trMemberEnumerationDocumentation()`, `trMemberFunctionDocumentation()`, +// `trMemberDataDocumentation()`, `trDefineDocumentation()`, `trDirDocumentation()`, +// `trTypeDocumentation()`. +// updated translation in context of non OO languages: `trHierarchicalIndex()` +// `trGotoGraphicalHierarchy()`, `trGotoTextualHierarchy()`, `trCompoundMembersFortran()`. +// updated translation in: `trNoMatches()`, trPackageAttribs(), trStaticPackageAttribs(). +// fixed wrong translation in: `trCompoundReferenceFortran()` of `case ClassDef::Class:`. +// unified translation string in: `trGeneratedFromFilesFortran` according to `trGeneratedFromFiles()`. +// fixed wrong wording in: `trCompoundReferenceSlice` of `case ClassDef::Interface:` when `isLocal==true`. +// minor spacing and comments changes for better convergence to the reference file: `translator_en.h`. +// all unclear or fuzzy czech translations marked with ???. +// 2021/08/31 - Updated for "new since 1.9.3" by: <petyovsky@vut.cz>. +// 2021/01/22 - Updated for "new since 1.9.2" by: <petyovsky@vut.cz>. // 2013/04/11 - Updates for "new since 1.8.4". // 2012/07/31 - Updates for "new since 1.8.2". // 2012/04/10 - Updates for "new since 1.8.0". @@ -87,14 +104,57 @@ // something else. It is difficult to find the general translation // for all kinds in the Czech language. -class TranslatorCzech : public TranslatorAdapter_1_9_4 +/*! + When defining a translator class for the new language, follow + the description in the documentation. One of the steps says + that you should copy the translator_en.h (this) file to your + translator_xx.h new file. Your new language should use the + Translator class as the base class. This means that you need to + implement exactly the same (pure virtual) methods as the + TranslatorEnglish does. Because of this, it is a good idea to + start with the copy of TranslatorEnglish and replace the strings + one by one. + + It is not necessary to include "translator.h" or + "translator_adapter.h" here. The files are included in the + language.cpp correctly. Not including any of the mentioned + files frees the maintainer from thinking about whether the + first, the second, or both files should be included or not, and + why. This holds namely for localized translators because their + base class is changed occasionally to adapter classes when the + Translator class changes the interface, or back to the + Translator class (by the local maintainer) when the localized + translator is made up-to-date again. +*/ +class TranslatorCzech : public Translator { public: + // --- Language control methods ------------------- + /*! Used for identification of the language. The identification + * should not be translated. It should be replaced by the name + * of the language in English using lower-case characters only + * (e.g. "czech", "japanese", "russian", etc.). It should be equal to + * the identification used in language.cpp. + */ virtual QCString idLanguage() { return "czech"; } + /*! Used to get the LaTeX command(s) for the language support. + * This method should return string with commands that switch + * LaTeX to the desired language. For example + * <pre>"\\usepackage[german]{babel}\n" + * </pre> + * or + * <pre>"\\usepackage{polski}\n" + * "\\usepackage[latin2]{inputenc}\n" + * "\\usepackage[T1]{fontenc}\n" + * </pre> + * + * The English LaTeX does not use such commands. Because of this + * the empty string is returned in this implementation. + */ virtual QCString latexLanguageSupportCommand() { return "\\usepackage[T2A]{fontenc}\n" @@ -111,11 +171,11 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 { return "cs"; } + virtual QCString getLanguageString() { return "0x405 Czech"; } - // --- Language translation methods ------------------- /*! used in the compound documentation before a list of related functions. */ @@ -132,26 +192,26 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! header that is put before the list of typedefs. */ virtual QCString trMemberTypedefDocumentation() - { return "Dokumentace k členským typům"; } + { return "Dokumentace členských typů"; } /*! header that is put before the list of enumerations. */ virtual QCString trMemberEnumerationDocumentation() - { return "Dokumentace k členským výčtům"; } + { return "Dokumentace členských výčtů"; } /*! header that is put before the list of member functions. */ virtual QCString trMemberFunctionDocumentation() - { return "Dokumentace k metodám"; } + { return "Dokumentace členských funkcí"; } /*! header that is put before the list of member attributes. */ virtual QCString trMemberDataDocumentation() { if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) { - return "Dokumentace k položkám"; + return "Dokumentace položek"; } else { - return "Dokumentace k datovým členům"; + return "Dokumentace datových členů"; } } @@ -179,10 +239,10 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 * parameter s is name of the project name. */ virtual QCString trGeneratedAutomatically(const QCString &s) - { QCString result("Vygenerováno automaticky programem Doxygen " - "ze zdrojových textů"); - if (!s.isEmpty()) result += QCString(" projektu ") + s; - result += "."; + { QCString result="Vygenerováno automaticky programem Doxygen " + "ze zdrojových textů"; + if (!s.isEmpty()) result+=" projektu "+s; + result+="."; return result; } @@ -295,40 +355,40 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) { return "Následující seznam obsahuje identifikace datových " - "struktur a jejich stručné popisy:"; + "struktur a jejich stručné popisy:"; } else if (Config_getBool(OPTIMIZE_OUTPUT_SLICE)) { return "Následující seznam obsahuje identifikace tříd. " - "V seznamu jsou uvedeny jejich stručné popisy:"; + "V seznamu jsou uvedeny jejich stručné popisy:"; } else { return "Následující seznam obsahuje především identifikace " - "tříd, ale nacházejí se zde i další netriviální prvky, " - "jako jsou struktury (struct), unie (union) a rozhraní " - "(interface). V seznamu jsou uvedeny jejich stručné " - "popisy:"; + "tříd, ale nacházejí se zde i další netriviální prvky, " + "jako jsou struktury (struct), unie (union) a rozhraní " + "(interface). V seznamu jsou uvedeny jejich stručné " + "popisy:"; } } /*! This is an introduction to the page with all class members. */ virtual QCString trCompoundMembersDescription(bool extractAll) { - QCString result= "Zde naleznete seznam všech "; + QCString result="Zde naleznete seznam všech "; if (!extractAll) { - result += "dokumentovaných "; + result+="dokumentovaných "; } if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) { - result += "položek struktur (struct) a unií (union) "; + result+="položek struktur (struct) a unií (union)"; } else { - result += "členů tříd "; + result+="členů tříd"; } - result += "s odkazy na "; + result+=" s odkazy na "; if (!extractAll) { if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) @@ -344,11 +404,11 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 { if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) { - result += "dokumentaci struktur/unií, ke kterým příslušejí:"; + result+="dokumentaci struktur/unií, ke kterým příslušejí:"; } else { - result += "dokumentaci tříd, ke kterým příslušejí:"; + result+="dokumentaci tříd, ke kterým příslušejí:"; } } return result; @@ -363,14 +423,14 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 if (Config_getBool(OPTIMIZE_OUTPUT_FOR_C)) { result+="funkcí, proměnných, maker, výčtů a definic typů (typedef) " - "s odkazy na "; + "s odkazy"; } else { result+="symbolů, které jsou definovány na úrovni svých souborů. " - "Pro každý symbol je uveden odkaz na "; + "Pro každý symbol je uveden odkaz"; } - + result+=" na "; if (extractAll) result+="soubory, ke kterým příslušejí:"; else @@ -406,7 +466,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 * class hierarchy. */ virtual QCString trHierarchicalIndex() - { return "Rejstřík hierarchie tříd"; } + { return "Rejstřík hierarchie"; } /*! This is used in LaTeX as the title of the chapter with the * annotated compound index. @@ -510,7 +570,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 * documentation blocks for defines */ virtual QCString trDefineDocumentation() - { return "Dokumentace k definicím maker"; } + { return "Dokumentace definic maker"; } /*! This is used in the documentation of a file/namespace before the list * of documentation blocks for typedefs @@ -557,7 +617,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 virtual QCString trGeneratedAt(const QCString &date,const QCString &projName) { QCString result="Vygenerováno dne: "+date; - if (!projName.isEmpty()) result += QCString(", pro projekt: ") + projName; + if (!projName.isEmpty()) result +=", pro projekt: "+projName; result+=", programem"; return result; } @@ -565,7 +625,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! this text is put before a class diagram */ virtual QCString trClassDiagram(const QCString &clName) { - return QCString("Diagram dědičnosti pro třídu ") + clName+":"; + return "Diagram dědičnosti pro třídu "+clName+":"; } /*! this text is generated when the \\warning command is used. */ @@ -642,17 +702,17 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 ClassDef::CompoundType compType, bool isTemplate) { - QCString result("Dokumentace "); + QCString result="Dokumentace "; if (isTemplate) result += "šablony "; switch(compType) { - case ClassDef::Class: result += "třídy "; break; - case ClassDef::Struct: result += "struktury "; break; - case ClassDef::Union: result += "unie "; break; - case ClassDef::Interface: result += "rozhraní "; break; - case ClassDef::Protocol: result += "protokolu "; break; - case ClassDef::Category: result += "kategorie "; break; - case ClassDef::Exception: result += "výjimky "; break; + case ClassDef::Class: result+="třídy "; break; + case ClassDef::Struct: result+="struktury "; break; + case ClassDef::Union: result+="unie "; break; + case ClassDef::Interface: result+="rozhraní "; break; + case ClassDef::Protocol: result+="protokolu "; break; + case ClassDef::Category: result+="kategorie "; break; + case ClassDef::Exception: result+="výjimky "; break; default: break; } result += clName; @@ -662,7 +722,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! used as the title of the HTML page of a file */ virtual QCString trFileReference(const QCString &fileName) { - QCString result("Dokumentace souboru "); + QCString result="Dokumentace souboru "; result+=fileName; return result; } @@ -670,7 +730,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! used as the title of the HTML page of a namespace */ virtual QCString trNamespaceReference(const QCString &namespaceName) { - QCString result("Dokumentace jmenného prostoru "); + QCString result="Dokumentace jmenného prostoru "; result+=namespaceName; return result; } @@ -729,7 +789,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 */ virtual QCString trInheritsList(int numEntries) { - QCString result("Dědí z "); + QCString result="Dědí z "; result += (numEntries == 1) ? "bázové třídy " : "bázových tříd "; result += trWriteList(numEntries) + "."; return result; @@ -740,7 +800,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 */ virtual QCString trInheritedByList(int numEntries) { - QCString result("Zděděna "); + QCString result="Zděděna "; result += (numEntries == 1) ? "třídou " : "třídami "; result += trWriteList(numEntries) + "."; return result; @@ -751,7 +811,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 */ virtual QCString trReimplementedFromList(int numEntries) { - QCString result("Reimplementuje stejnojmenný prvek z "); + QCString result="Reimplementuje stejnojmenný prvek z "; result += trWriteList(numEntries) + "."; return result; } @@ -813,10 +873,11 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 virtual QCString trGeneratedFromFiles(ClassDef::CompoundType compType, bool single) { // single is true implies a single file + bool vhdlOpt = Config_getBool(OPTIMIZE_OUTPUT_VHDL); QCString result="Dokumentace pro "; switch(compType) { - case ClassDef::Class: result+="tuto třídu"; break; + case ClassDef::Class: result+=vhdlOpt?"tuto návrhovou jednotku":"tuto třídu"; break; case ClassDef::Struct: result+="tuto strukturu"; break; case ClassDef::Union: result+="tuto unii"; break; case ClassDef::Interface: result+="toto rozhraní"; break; @@ -825,9 +886,9 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 case ClassDef::Exception: result+="tuto výjimku"; break; default: break; } - result+=" byla vygenerována z "; - if (single) result+="následujícího souboru:"; - else result+="následujících souborů:"; + result+=" byla vygenerována z následující"; + if (single) result+="ho souboru:"; + else result+="ch souborů:"; return result; } @@ -901,20 +962,22 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 { return "Zobrazit dokumentaci tohoto souboru."; } + + /*! ??? Jak to prelozit? Bylo by dobre, kdyby se ozval nekdo, kdo to pouziva.*/ /*! Text for the \\pre command */ virtual QCString trPrecondition() { - return "Precondition"; + return "Precondition"; // ??? } /*! Text for the \\post command */ virtual QCString trPostcondition() { - return "Postcondition"; + return "Postcondition"; // ??? } /*! Text for the \\invariant command */ virtual QCString trInvariant() { - return "Invariant"; + return "Invariant"; // ??? } /*! Text shown before a multi-line variable/enum initialization */ virtual QCString trInitialValue() @@ -932,11 +995,11 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 } virtual QCString trGotoGraphicalHierarchy() { - return "Zobrazit grafickou podobu hierarchie tříd"; + return "Zobrazit grafickou podobu hierarchie"; } virtual QCString trGotoTextualHierarchy() { - return "Zobrazit textovou podobu hierarchie tříd"; + return "Zobrazit textovou podobu hierarchie"; } virtual QCString trPageIndex() { @@ -1020,11 +1083,11 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 } virtual QCString trRemarks() { - return "Poznámky"; // ??? not checked in a context + return "Poznámky"; // ??? not checked in a context } virtual QCString trAttention() { - return "Upozornění"; // ??? not checked in a context + return "Upozornění"; // ??? not checked in a context } virtual QCString trInclByDepGraph() { @@ -1033,7 +1096,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 } virtual QCString trSince() { - return "Od"; // ??? not checked in a context + return "Od"; // ??? not checked in a context } ////////////////////////////////////////////////////////////////////////// @@ -1045,7 +1108,9 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 { return "Vysvětlivky ke grafu"; } - /*! page explaining how the dot graph's should be interpreted */ + /*! page explaining how the dot graph's should be interpreted + * The %A in the text below are to prevent link to classes called "A". + */ virtual QCString trLegendDocs() { return @@ -1106,7 +1171,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 "Šipky (tj. hrany grafu) mají následující význam:\n" "</p>\n" "<ul>\n" - "<li>Tmavě modrá šipka se používá pro označení vztahu veřejné " + "<li>Modrá šipka se používá pro označení vztahu veřejné " "dědičnosti (public) mezi dvěma třídami.</li>\n" "<li>Tmavě zelená šipka označuje vztah chráněné dědičnosti " "(protected).</li>\n" @@ -1114,13 +1179,13 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 "(private).</li>\n" "<li>Purpurová šipka kreslená čárkovaně se používá v případě, " "kdy je třída obsažena v jiné třídě,\n" - "nebo kdy je používána jinou třídou. Je označena identifikátorem " - "jedné nebo více proměných, přes které\n" + "nebo kdy je používána jinou třídou. Šipka je označena " + "identifikátorem jedné nebo více proměnných, přes které\n" "je třída nebo struktura zpřístupněna.</li>\n" "<li>Žlutá šipka kreslená čárkovaně vyjadřuje vztah mezi instancí šablony " "a šablonou třídy, na základě které byla\n" - "instance šablony vytvořena. V popisu šipky jsou uvedeny příslušné" - " parametry šablony.</li>\n" + "instance šablony vytvořena. V popisu šipky jsou uvedeny příslušné " + "parametry šablony.</li>\n" "</ul>\n"; } /*! text for the link to the legend page */ @@ -1156,7 +1221,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! Used as a section header for IDL property documentation */ virtual QCString trPropertyDocumentation() { - return "Dokumentace k vlastnosti"; + return "Dokumentace vlastností"; } ////////////////////////////////////////////////////////////////////////// // new since 1.2.4 @@ -1177,13 +1242,12 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! Used as the title of a Java package */ virtual QCString trPackage(const QCString &name) { - return QCString("Balík ") + name; + return "Balík "+name; } /*! The description of the package index page */ virtual QCString trPackageListDescription() { - return "Zde naleznete seznam balíků se stručným popisem " - "(pokud byl uveden):"; + return "Zde naleznete seznam balíků se stručným popisem (pokud byl uveden):"; } /*! The link name in the Quick links header for each page */ virtual QCString trPackages() @@ -1328,9 +1392,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 return result; } - /*! ??? Jak to prelozit? Bylo by dobre, kdyby se ozval nekdo, - * kdo to pouziva. - */ + /*! ??? Jak to prelozit? Bylo by dobre, kdyby se ozval nekdo, kdo to pouziva.*/ /*! This is used for translation of the word that will possibly * be followed by a single name or by a list of names * of the category. @@ -1379,7 +1441,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 } /*! used in member documentation blocks to produce a list of - * all members that implement this member. + * all members that implement this abstract member. */ virtual QCString trImplementedInList(int numEntries) { @@ -1446,7 +1508,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 } virtual QCString trPackageMembers() { - return "Členy v balíku"; + return "Členy v balíku"; // ??? not checked in a context } /*! Used as a heading for a list of static Java class functions with * package scope. @@ -1460,14 +1522,14 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 */ virtual QCString trPackageAttribs() { - return "Atributy balíku"; + return "Atributy v balíku"; // ??? not checked in a context } /*! Used as a heading for a list of static Java class variables with * package scope. */ virtual QCString trStaticPackageAttribs() { - return "Statické atributy balíku"; + return "Statické atributy v balíku"; // ??? not checked in a context } ////////////////////////////////////////////////////////////////////////// @@ -1539,7 +1601,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 */ virtual QCString trSourceFile(QCString& filename) { - return QCString("Zdrojový soubor ") + filename; + return "Zdrojový soubor " + filename; } ////////////////////////////////////////////////////////////////////////// @@ -1556,7 +1618,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 * of the directories. */ virtual QCString trDirDocumentation() - { return "Dokumentace k adresářům"; } + { return "Dokumentace adresářů"; } /*! This is used as the title of the directory index and also in the * Quick links of an HTML page, to link to the directory hierarchy. @@ -1568,11 +1630,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 * directory is passed via \a dirName. */ virtual QCString trDirReference(const QCString &dirName) - { - QCString result = "Reference k adresáři "; - result += dirName; - return result; - } + { QCString result="Reference k adresáři "; result+=dirName; return result; } /*! This returns the word directory with or without starting capital * (\a first_capital) and in singular or plural form (\a singular). @@ -1580,8 +1638,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 virtual QCString trDir(bool first_capital, bool singular) { QCString result((first_capital ? "Adresář" : "adresář")); - if (!singular) - result += "e"; + if (!singular) result+="e"; return result; } @@ -1629,7 +1686,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! This is put above each page as a link to all members of compounds (Fortran). */ virtual QCString trCompoundMembersFortran() - { return "Datová pole"; } + { return "Datové položky"; } /*! This is an introduction to the annotated compound list (Fortran). */ virtual QCString trCompoundListDescriptionFortran() @@ -1651,7 +1708,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 } else { - result+="příslušné datové typy:"; + result+="příslušné datové typy:"; } return result; } @@ -1666,7 +1723,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 * the documentation of all data types (Fortran). */ virtual QCString trTypeDocumentation() - { return "Dokumentace k datovým typům"; } + { return "Dokumentace datových typů"; } /*! This is used in the documentation of a file as a header before the * list of (global) subprograms (Fortran). @@ -1704,22 +1761,21 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 ClassDef::CompoundType compType, bool isTemplate) { - QCString result("Dokumentace "); + QCString result="Dokumentace "; if (isTemplate) result += "šablony "; switch(compType) { - case ClassDef::Class: result += "třídy "; break; - case ClassDef::Struct: result += "typu "; break; - case ClassDef::Union: result += "unie "; break; - case ClassDef::Interface: result += "rozhraní "; break; - case ClassDef::Protocol: result += "protokolu "; break; - case ClassDef::Category: result += "kategorie "; break; - case ClassDef::Exception: result += "výjimky "; break; + case ClassDef::Class: result+="modulu "; break; + case ClassDef::Struct: result+="typu "; break; + case ClassDef::Union: result+="unie "; break; + case ClassDef::Interface: result+="rozhraní "; break; + case ClassDef::Protocol: result+="protokolu "; break; + case ClassDef::Category: result+="kategorie "; break; + case ClassDef::Exception: result+="výjimky "; break; default: break; } - result += clName; + result+=clName; return result; - } /*! used as the title of the HTML page of a module (Fortran) */ virtual QCString trModuleReference(const QCString &namespaceName) @@ -1774,21 +1830,21 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 bool single) { // single is true implies a single file - QCString result="Dokumentace "; + QCString result="Dokumentace pro "; switch(compType) { - case ClassDef::Class: result+="k tomuto modulu"; break; - case ClassDef::Struct: result+="k tomuto typu"; break; - case ClassDef::Union: result+="k této unii"; break; - case ClassDef::Interface: result+="k tomuto rozhraní"; break; - case ClassDef::Protocol: result+="k tomuto protokolu"; break; - case ClassDef::Category: result+="k této kategorii"; break; - case ClassDef::Exception: result+="k této výjimce"; break; + case ClassDef::Class: result+="tento modul"; break; + case ClassDef::Struct: result+="tento typ"; break; + case ClassDef::Union: result+="tuto unii"; break; + case ClassDef::Interface: result+="toto rozhraní"; break; + case ClassDef::Protocol: result+="tento protokol"; break; + case ClassDef::Category: result+="tuto kategorii"; break; + case ClassDef::Exception: result+="tuto výjimku"; break; default: break; } - result+=" byla vygenerována z "; - if (single) result+="následujícího souboru:"; - else result+="následujících souborů:"; + result+=" byla vygenerována z následující"; + if (single) result+="ho souboru:"; + else result+="ch souborů:"; return result; } @@ -1827,7 +1883,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! directory relation for \a name */ virtual QCString trDirRelation(const QCString &name) { - return "Relace " + QCString(name); + return "Relace "+name; // ??? not checked in a context } /*! Loading message shown when loading search results */ @@ -1851,7 +1907,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! Text shown when no search results are found */ virtual QCString trNoMatches() { - return "Nic se nenašlo"; + return "Nebylo nic nalezeno"; } ////////////////////////////////////////////////////////////////////////// @@ -1902,6 +1958,27 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "po", "út", "st", "čt", "pá", "so", "ne" }; + static const char *days_full[] = { "pondělí", "úterý", "středa", "čtvrtek", "pátek", "sobota", "neděle" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "led", "úno", "bře", "dub", "kvě", "čvn", "čvc", "srp", "zář", "říj", "lis", "pro" }; + static const char *months_full[] = { "leden", "únor", "březen", "duben", "květen", "červen", "červenec", "srpen", "září", "říjen", "listopad", "prosinec" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "dop.", "odp." }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 @@ -1917,7 +1994,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! Header for the graph showing the directory dependencies */ virtual QCString trDirDepGraph(const QCString &name) - { return QCString("Graf závislosti na adresářích pro ")+name+":"; } + { return "Graf závislosti na adresářích pro "+name+":"; } // ??? not checked in a context ////////////////////////////////////////////////////////////////////////// // new since 1.8.0 @@ -1929,7 +2006,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! Section header for list of template parameters */ virtual QCString trTemplateParameters() - { return "Parametry šablon"; } + { return "Parametry šablony"; } /*! Used in dot graph when UML_LOOK is enabled and there are many fields */ virtual QCString trAndMore(const QCString &number) @@ -1937,21 +2014,21 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! Used file list for a Java enum */ virtual QCString trEnumGeneratedFromFiles(bool single) - { QCString result = "Dokumentace pro tento výčet byla vygenerována z "; + { QCString result = "Dokumentace pro tento výčet byla vygenerována z následující"; if (single) - result += "následujícího souboru:"; + result += "ho souboru:"; else - result += "následujících souborů:"; + result += "ch souborů:"; return result; } /*! Header of a Java enum page (Java enums are represented as classes). */ virtual QCString trEnumReference(const QCString &name) - { return "Reference k výčtu "+QCString(name); } + { return "Reference k výčtu "+name; } /*! Used for a section containing inherited members */ virtual QCString trInheritedFrom(const QCString &members,const QCString &what) - { return QCString(members)+" dědí se z "+what; } + { return members+" dědí se z "+what; } /*! Header of the sections with inherited members specific for the * base class(es) @@ -2034,39 +2111,39 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 virtual QCString trConstantGroupReference(const QCString &namespaceName) { QCString result="Konstantní skupiny z "; - result += namespaceName; + result+=namespaceName; return result; } /** UNO IDL service page title */ virtual QCString trServiceReference(const QCString &sName) { QCString result="Popis služby "; - result += sName; + result+=sName; return result; } /** UNO IDL singleton page title */ virtual QCString trSingletonReference(const QCString &sName) { QCString result="Popis singletonu "; - result += sName; + result+=sName; return result; } /** UNO IDL service page */ virtual QCString trServiceGeneratedFromFiles(bool single) { // single is true implies a single file - QCString result="Dokumentace k této službě byla vygenerována "; - if (single) result+="z následujícího souboru:"; - else result+="z následujících souborů:"; + QCString result="Dokumentace k této službě byla vygenerována z následující"; + if (single) result+="ho souboru:"; + else result+="ch souborů:"; return result; } /** UNO IDL singleton page */ virtual QCString trSingletonGeneratedFromFiles(bool single) { // single is true implies a single file - QCString result="Dokumentace k tomuto singletonu byla vygenerována "; - if (single) result+="z následujícího souboru:"; - else result+="z následujících souborů:"; + QCString result="Dokumentace k tomuto singletonu byla vygenerována z následující"; + if (single) result+="ho souboru:"; + else result+="ch souborů:"; return result; } @@ -2082,7 +2159,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 { return "Seznam návrhových jednotek"; } /** VHDL design unit members */ virtual QCString trDesignUnitMembers() - { return "Seznam členů návrhových jednotky"; } + { return "Seznam členů návrhových jednotek"; } /** VHDL design unit list description */ virtual QCString trDesignUnitListDescription() { @@ -2185,7 +2262,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 } } virtual QCString trCustomReference(const QCString &name) - { return "Dokumentace pro "+QCString(name); } + { return "Dokumentace pro "+name; } /* Slice */ virtual QCString trConstants() @@ -2286,23 +2363,24 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 } virtual QCString trCompoundReferenceSlice(const QCString &clName, ClassDef::CompoundType compType, bool isLocal) { - QCString result("Dokumentace "); + QCString result="Dokumentace"; if (isLocal) result+=" lokální"; switch(compType) { case ClassDef::Class: result+=" třídy "; break; case ClassDef::Struct: result+=" struktury "; break; case ClassDef::Union: result+=" unie "; break; - case ClassDef::Interface: result+=" rozhraní "; break; + case ClassDef::Interface: + if (isLocal) result+="ho"; + result+=" rozhraní "; break; case ClassDef::Protocol: - if (isLocal) result+="ho"; - result+=" protokolu "; - break; + if (isLocal) result+="ho"; + result+=" protokolu "; break; case ClassDef::Category: result+=" kategorie "; break; case ClassDef::Exception: result+=" vyjímky "; break; default: break; } - result += clName; + result+=clName; return result; } virtual QCString trOperations() @@ -2344,7 +2422,7 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 /*! used as the title of the HTML page of a C++20 concept page */ virtual QCString trConceptReference(const QCString &conceptName) { - QCString result("Dokumentace konceptu "); + QCString result="Dokumentace konceptu "; result+=conceptName; return result; } @@ -2375,6 +2453,13 @@ class TranslatorCzech : public TranslatorAdapter_1_9_4 { return "Definice konceptů"; } + +////////////////////////////////////////////////////////////////////////// +// new since 1.9.4 +////////////////////////////////////////////////////////////////////////// + + virtual QCString trPackageList() + { return "Seznam balíků"; } }; #endif // TRANSLATOR_CZ_H diff --git a/src/translator_de.h b/src/translator_de.h index c28385c..e6b7a4a 100644 --- a/src/translator_de.h +++ b/src/translator_de.h @@ -1176,7 +1176,7 @@ class TranslatorGerman : public TranslatorAdapter_1_8_15 "Die Pfeile bedeuten:\n" "</p>\n" "<ul>\n" - "<li>Ein dunkelblauer Pfeil stellt eine öffentliche Vererbungsbeziehung " + "<li>Ein blauer Pfeil stellt eine öffentliche Vererbungsbeziehung " "zwischen zwei Klassen dar.</li>\n" "<li>Ein dunkelgrüner Pfeil stellt geschützte Vererbung dar.</li>\n" "<li>Ein dunkelroter Pfeil stellt private Vererbung dar.</li>\n" @@ -1952,6 +1952,25 @@ class TranslatorGerman : public TranslatorAdapter_1_8_15 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So" }; + static const char *days_full[] = { "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "Jan", "Feb", "Mrz", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez" }; + static const char *months_full[] = { "Januar", "Februar", "März", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Dezember" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_dk.h b/src/translator_dk.h index cd348a8..afc3fce 100644 --- a/src/translator_dk.h +++ b/src/translator_dk.h @@ -1742,6 +1742,27 @@ class TranslatorDanish : public TranslatorAdapter_1_8_0 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "ma", "ti", "on", "to", "fr", "lø", "sø" }; + static const char *days_full[] = { "mandag", "tirsdag", "onsdag", "torsdag", "fredag", "lørdag", "søndag" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "jan", "feb", "mar", "apr", "maj", "jun", "jul", "aug", "sep", "okt", "nov", "dec" }; + static const char *months_full[] = { "januar", "februar", "marts", "april", "maj", "juni", "juli", "august", "september", "oktober", "november", "december" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_en.h b/src/translator_en.h index 236675b..f0ca0e7 100644 --- a/src/translator_en.h +++ b/src/translator_en.h @@ -1047,7 +1047,7 @@ class TranslatorEnglish : public Translator "The arrows have the following meaning:\n" "</p>\n" "<ul>\n" - "<li>%A dark blue arrow is used to visualize a public inheritance " + "<li>%A blue arrow is used to visualize a public inheritance " "relation between two classes.</li>\n" "<li>%A dark green arrow is used for protected inheritance.</li>\n" "<li>%A dark red arrow is used for private inheritance.</li>\n" @@ -1825,6 +1825,23 @@ class TranslatorEnglish : public Translator } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; + static const char *days_full[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" }; + return full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + static const char *months_full[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; + return full? months_full[month-1] : months_short[month-1]; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_eo.h b/src/translator_eo.h index d86ffaf..40992cc 100644 --- a/src/translator_eo.h +++ b/src/translator_eo.h @@ -1822,6 +1822,27 @@ class TranslatorEsperanto : public TranslatorAdapter_1_8_4 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "lu", "ma", "me", "ĵa", "ve", "sa", "di" }; + static const char *days_full[] = { "lundo", "mardo", "merkredo", "ĵaŭdo", "vendredo", "sabato", "dimanĉo" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "jan", "feb", "mar", "apr", "maj", "jun", "jul", "aŭg", "sep", "okt", "nov", "dec" }; + static const char *months_full[] = { "januaro", "februaro", "marto", "aprilo", "majo", "junio", "julio", "aŭgusto", "septembro", "oktobro", "novembro", "decembro" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "atm", "ptm" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_es.h b/src/translator_es.h index f7c22b7..4fd955e 100644 --- a/src/translator_es.h +++ b/src/translator_es.h @@ -34,7 +34,7 @@ * Updated to 1.8.4 by Bartomeu Creus Navarro (17-julio-2013) */ -class TranslatorSpanish : public TranslatorAdapter_1_8_15 +class TranslatorSpanish : public TranslatorAdapter_1_8_19 { public: @@ -1071,7 +1071,7 @@ class TranslatorSpanish : public TranslatorAdapter_1_8_15 "Las flechas tienen el siguiente significado:\n" "</p>\n" "<ul>\n" - "<li>Una flecha azul oscuro es usada para visualizar una relación herencia publica entre dos clases.\n" + "<li>Una azul oscuro es usada para visualizar una relación herencia publica entre dos clases.\n" "<li>Una flecha verde oscuro es usada para herencia protegida.\n" "<li>Una flecha rojo oscuro es usada para herencia privada.\n" "<li>Una flecha segmentada púrpura se usa si la clase es contenida o " @@ -1879,6 +1879,27 @@ class TranslatorSpanish : public TranslatorAdapter_1_8_15 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "lu.", "ma.", "mi.", "ju.", "vi.", "sá.", "do." }; + static const char *days_full[] = { "lunes", "martes", "miércoles", "jueves", "viernes", "sábado", "domingo" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "ene.", "feb.", "mar.", "abr.", "may.", "jun.", "jul.", "ago.", "sep.", "oct.", "nov.", "dic." }; + static const char *months_full[] = { "enero", "febrero", "marzo", "abril", "mayo", "junio", "julio", "agosto", "septiembre", "octubre", "noviembre", "diciembre" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "a. m.", "p. m." }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_fa.h b/src/translator_fa.h index b609623..a4c1c10 100644 --- a/src/translator_fa.h +++ b/src/translator_fa.h @@ -1773,6 +1773,25 @@ class TranslatorPersian : public TranslatorAdapter_1_7_5 } return convertDigitsToFarsi(sdate); } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "دوشنبه", "سه شنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه", "يكشنبه" }; + static const char *days_full[] = { "دوشنبه", "سه شنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه", "يكشنبه" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "ژانويه", "فوريه", "مارس", "آوريل", "مه", "ژوئن", "ژوئيه", "اوت", "سپتامبر", "اُكتبر", "نوامبر", "دسامبر" }; + static const char *months_full[] = { "ژانويه", "فوريه", "مارس", "آوريل", "مه", "ژوئن", "ژوئيه", "اوت", "سپتامبر", "اُكتبر", "نوامبر", "دسامبر" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "قبلازظهر", "بعدازظهر" }; + return dayPeriod[period]; + } }; diff --git a/src/translator_fi.h b/src/translator_fi.h index dd1d418..c86ef94 100644 --- a/src/translator_fi.h +++ b/src/translator_fi.h @@ -1840,6 +1840,27 @@ class TranslatorFinnish : public TranslatorAdapter_1_6_0 return "Tyyppien rajoitteet"; // "Type Constraints" } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "ma", "ti", "ke", "to", "pe", "la", "su" }; + static const char *days_full[] = { "maanantai", "tiistai", "keskiviikko", "torstai", "perjantai", "lauantai", "sunnuntai" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "tammi", "helmi", "maalis", "huhti", "touko", "kesä", "heinä", "elo", "syys", "loka", "marras", "joulu" }; + static const char *months_full[] = { "tammikuu", "helmikuu", "maaliskuu", "huhtikuu", "toukokuu", "kesäkuu", "heinäkuu", "elokuu", "syyskuu", "lokakuu", "marraskuu", "joulukuu" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "ap.", "ip." }; + return dayPeriod[period]; + } }; #endif diff --git a/src/translator_fr.h b/src/translator_fr.h index f50d5c9..a601b3d 100644 --- a/src/translator_fr.h +++ b/src/translator_fr.h @@ -103,7 +103,7 @@ // Translator class (by the local maintainer) when the localized // translator is made up-to-date again. -class TranslatorFrench : public TranslatorAdapter_1_8_15 +class TranslatorFrench : public TranslatorAdapter_1_8_19 { public: @@ -1107,7 +1107,7 @@ class TranslatorFrench : public TranslatorAdapter_1_8_15 "</ul>\n" "Les flèches ont la signification suivante :\n" "<ul>\n" - "<li>Une flèche bleu foncé est utilisée pour visualiser une relation d'héritage publique " + "<li>Une bleu foncé est utilisée pour visualiser une relation d'héritage publique " "entre deux classes.\n" "<li>Une flèche vert foncé est utilisée pour une relation d'héritage protégée.\n" "<li>Une flèche rouge foncé est utilisée pour une relation d'héritage privée.\n" @@ -1885,6 +1885,27 @@ class TranslatorFrench : public TranslatorAdapter_1_8_15 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "lun.", "mar.", "mer.", "jeu.", "ven.", "sam.", "dim." }; + static const char *days_full[] = { "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi", "dimanche" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "janv.", "févr.", "mars", "avr.", "mai", "juin", "juil.", "août", "sept.", "oct.", "nov.", "déc." }; + static const char *months_full[] = { "janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_gr.h b/src/translator_gr.h index 4f9e5ee..e429d37 100644 --- a/src/translator_gr.h +++ b/src/translator_gr.h @@ -1829,6 +1829,25 @@ class TranslatorGreek : public TranslatorAdapter_1_9_4 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ", "Κυρ" }; + static const char *days_full[] = { "Δευτέρα", "Τρίτη", "Τετάρτη", "Πέμπτη", "Παρασκευή", "Σάββατο", "Κυριακή" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "Ιαν", "Φεβ", "Μαρ", "Απρ", "Μαϊ", "Ιουν", "Ιουλ", "Αυγ", "Σεπ", "Οκτ", "Νοε", "Δεκ" }; + static const char *months_full[] = { "Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "π.μ.", "μ.μ." }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_hi.h b/src/translator_hi.h index fb50015..1197687 100644 --- a/src/translator_hi.h +++ b/src/translator_hi.h @@ -74,7 +74,7 @@ * instantiations = उदाहरणीकरणगण * interface = अंतराफलक * interfaces = अंतराफलकगण - * inherit = + * inherit = * inheritance = वरासत * inherited = वरासित * library = संग्रह @@ -1789,6 +1789,25 @@ class TranslatorHindi : public TranslatorAdapter_1_9_4 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "सोम.", "मंगल.", "बुध.", "गुरु.", "शुक्र.", "शनि.", "रवि." }; + static const char *days_full[] = { "सोमवार", "मंगलवार", "बुधवार", "गुरुवार", "शुक्रवार", "शनिवार", "रविवार" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "जनवरी", "फरवरी", "मार्च", "अप्रैल", "मई", "जून", "जुलाई", "अगस्त", "सितम्बर", "अक्तूबर", "नवम्बर", "दिसम्बर" }; + static const char *months_full[] = { "जनवरी", "फरवरी", "मार्च", "अप्रैल", "मई", "जून", "जुलाई", "अगस्त", "सितम्बर", "अक्तूबर", "नवम्बर", "दिसम्बर" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "am", "pm" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_hr.h b/src/translator_hr.h index 1329f1c..fc867b0 100644 --- a/src/translator_hr.h +++ b/src/translator_hr.h @@ -1512,6 +1512,27 @@ class TranslatorCroatian : public TranslatorAdapter_1_8_2 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "pon", "uto", "sri", "čet", "pet", "sub", "ned" }; + static const char *days_full[] = { "ponedjeljak", "utorak", "srijeda", "četvrtak", "petak", "subota", "nedjelja" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "sij", "vlj", "ožu", "tra", "svi", "lip", "srp", "kol", "ruj", "lis", "stu", "pro" }; + static const char *months_full[] = { "siječanj", "veljača", "ožujak", "travanj", "svibanj", "lipanj", "srpanj", "kolovoz", "rujan", "listopad", "studeni", "prosinac" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } + + ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 ////////////////////////////////////////////////////////////////////////// diff --git a/src/translator_hu.h b/src/translator_hu.h index ccf35db..d657832 100644 --- a/src/translator_hu.h +++ b/src/translator_hu.h @@ -1843,6 +1843,27 @@ class TranslatorHungarian : public TranslatorAdapter_1_8_15 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "H", "K", "Sze", "Cs", "P", "Szo", "V" }; + static const char *days_full[] = { "hétfő", "kedd", "szerda", "csütörtök", "péntek", "szombat", "vasárnap" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "jan.", "febr.", "márc.", "ápr.", "máj.", "jún.", "júl.", "aug.", "szept.", "okt.", "nov.", "dec." }; + static const char *months_full[] = { "január", "február", "március", "április", "május", "június", "július", "augusztus", "szeptember", "október", "november", "december" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "de.", "du." }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_id.h b/src/translator_id.h index 1e4052f..bd25c7a 100644 --- a/src/translator_id.h +++ b/src/translator_id.h @@ -1794,6 +1794,23 @@ class TranslatorIndonesian : public TranslatorAdapter_1_8_0 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "Sen", "Sel", "Rab", "Kam", "Jum", "Sab", "Min" }; + static const char *days_full[] = { "Senin", "Selasa", "Rabu", "Kamis", "Jumat", "Sabtu", "Minggu" }; + return full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "Jan", "Feb", "Mar", "Apr", "Mei", "Jun", "Jul", "Agu", "Sep", "Okt", "Nov", "Des" }; + static const char *months_full[] = { "Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember" }; + return full? months_full[month-1] : months_short[month-1]; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_it.h b/src/translator_it.h index 04ebbf5..eea9160 100644 --- a/src/translator_it.h +++ b/src/translator_it.h @@ -1795,6 +1795,27 @@ class TranslatorItalian : public TranslatorAdapter_1_8_15 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "lun", "mar", "mer", "gio", "ven", "sab", "dom" }; + static const char *days_full[] = { "lunedì", "martedì", "mercoledì", "giovedì", "venerdì", "sabato", "domenica" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "gen", "feb", "mar", "apr", "mag", "giu", "lug", "ago", "set", "ott", "nov", "dic" }; + static const char *months_full[] = { "gennaio", "febbraio", "marzo", "aprile", "maggio", "giugno", "luglio", "agosto", "settembre", "ottobre", "novembre", "dicembre" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_jp.h b/src/translator_jp.h index 4d5d1dd..38992c3 100644 --- a/src/translator_jp.h +++ b/src/translator_jp.h @@ -1814,6 +1814,25 @@ class TranslatorJapanese : public TranslatorAdapter_1_8_15 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "月", "火", "水", "木", "金", "土", "日" }; + static const char *days_full[] = { "月曜日", "火曜日", "水曜日", "木曜日", "金曜日", "土曜日", "日曜日" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" }; + static const char *months_full[] = { "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "午前", "午後" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_kr.h b/src/translator_kr.h index 43a2fbe..5635e97 100644 --- a/src/translator_kr.h +++ b/src/translator_kr.h @@ -1833,6 +1833,25 @@ class TranslatorKorean : public TranslatorAdapter_1_8_15 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "월", "화", "수", "목", "금", "토", "일" }; + static const char *days_full[] = { "월요일", "화요일", "수요일", "목요일", "금요일", "토요일", "일요일" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" }; + static const char *months_full[] = { "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "오전", "오후" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_lt.h b/src/translator_lt.h index 9f16716..e0c167e 100644 --- a/src/translator_lt.h +++ b/src/translator_lt.h @@ -1509,6 +1509,27 @@ class TranslatorLithuanian : public TranslatorAdapter_1_4_6 } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "pr", "an", "tr", "kt", "pn", "št", "sk" }; + static const char *days_full[] = { "pirmadienis", "antradienis", "trečiadienis", "ketvirtadienis", "penktadienis", "šeštadienis", "sekmadienis" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "saus.", "vas.", "kov.", "bal.", "geg.", "birž.", "liep.", "rugp.", "rugs.", "spal.", "lapkr.", "gruod." }; + static const char *months_full[] = { "sausis", "vasaris", "kovas", "balandis", "gegužė", "birželis", "liepa", "rugpjūtis", "rugsėjis", "spalis", "lapkritis", "gruodis" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "priešpiet", "popiet" }; + return dayPeriod[period]; + } }; #endif diff --git a/src/translator_lv.h b/src/translator_lv.h index c54fe7e..927351a 100644 --- a/src/translator_lv.h +++ b/src/translator_lv.h @@ -1822,6 +1822,27 @@ class TranslatorLatvian : public TranslatorAdapter_1_8_4 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "pirmd.", "otrd.", "trešd.", "ceturtd.", "piektd.", "sestd.", "svētd." }; + static const char *days_full[] = { "pirmdiena", "otrdiena", "trešdiena", "ceturtdiena", "piektdiena", "sestdiena", "svētdiena" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "janv.", "febr.", "marts", "apr.", "maijs", "jūn.", "jūl.", "aug.", "sept.", "okt.", "nov.", "dec." }; + static const char *months_full[] = { "janvāris", "februāris", "marts", "aprīlis", "maijs", "jūnijs", "jūlijs", "augusts", "septembris", "oktobris", "novembris", "decembris" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "priekšp.", "pēcp." }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_mk.h b/src/translator_mk.h index 0be70fc..c8e1217 100644 --- a/src/translator_mk.h +++ b/src/translator_mk.h @@ -1718,6 +1718,25 @@ class TranslatorMacedonian : public TranslatorAdapter_1_6_0 return "Ограничувања на Тип"; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "пон.", "вт.", "сре.", "чет.", "пет.", "саб.", "нед." }; + static const char *days_full[] = { "понеделник", "вторник", "среда", "четврток", "петок", "сабота", "недела" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "јан.", "фев.", "мар.", "апр.", "мај", "јун.", "јул.", "авг.", "септ.", "окт.", "ноем.", "дек." }; + static const char *months_full[] = { "јануари", "февруари", "март", "април", "мај", "јуни", "јули", "август", "септември", "октомври", "ноември", "декември" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "претпл.", "попл." }; + return dayPeriod[period]; + } }; #endif diff --git a/src/translator_nl.h b/src/translator_nl.h index 8b95637..e97a854 100644 --- a/src/translator_nl.h +++ b/src/translator_nl.h @@ -705,7 +705,7 @@ class TranslatorDutch : public Translator "</ul>\n" "De pijlen hebben de volgende betekenis:\n" "<ul>\n" - "<li>Een donkerblauwe pijl visualizeert een public inheritance " + "<li>Een blauwe pijl visualizeert een public inheritance " "relatie tussen twee klassen.\n" "<li>Een donkergroene pijl wordt gebruikt voor protected inheritance.\n" "<li>Een donkerrode pijl wordt gebruikt voor private inheritance.\n" @@ -1438,6 +1438,27 @@ class TranslatorDutch : public Translator } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "ma", "di", "wo", "do", "vr", "za", "zo" }; + static const char *days_full[] = { "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag", "zondag" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "jan", "feb", "mrt", "apr", "mei", "jun", "jul", "aug", "sep", "okt", "nov", "dec" }; + static const char *months_full[] = { "januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "a.m.", "p.m." }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_no.h b/src/translator_no.h index 916ba89..fc5ad63 100755..100644 --- a/src/translator_no.h +++ b/src/translator_no.h @@ -1520,6 +1520,27 @@ class TranslatorNorwegian : public TranslatorAdapter_1_4_6 "funksjonen ovenfor i argument(ene) den aksepterer."; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "må.", "ty.", "on.", "to.", "fr.", "la.", "sø." }; + static const char *days_full[] = { "måndag", "tysdag", "onsdag", "torsdag", "fredag", "laurdag", "søndag" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "jan", "feb", "mar", "apr", "mai", "jun", "jul", "aug", "sep", "okt", "nov", "des" }; + static const char *months_full[] = { "januar", "februar", "mars", "april", "mai", "juni", "juli", "august", "september", "oktober", "november", "desember" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "f.m.", "e.m." }; + return dayPeriod[period]; + } }; #endif diff --git a/src/translator_pl.h b/src/translator_pl.h index c8a215f..8309a7d 100644 --- a/src/translator_pl.h +++ b/src/translator_pl.h @@ -1768,6 +1768,27 @@ class TranslatorPolish : public TranslatorAdapter_1_8_2 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "pon.", "wt.", "śr.", "czw.", "pt.", "sob.", "niedz." }; + static const char *days_full[] = { "poniedziałek", "wtorek", "środa", "czwartek", "piątek", "sobota", "niedziela" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paź", "lis", "gru" }; + static const char *months_full[] = { "styczeń", "luty", "marzec", "kwiecień", "maj", "czerwiec", "lipiec", "sierpień", "wrzesień", "październik", "listopad", "grudzień" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_pt.h b/src/translator_pt.h index cad2a06..24f9083 100644 --- a/src/translator_pt.h +++ b/src/translator_pt.h @@ -26,6 +26,8 @@ * VERSION HISTORY * --------------- * History: + * 20220525: + * - Updated to 1.9.4; * 20211003: * - Updated to 1.9.3; * 20200112: @@ -65,7 +67,7 @@ #define TRANSLATOR_PT_H -class TranslatorPortuguese : public TranslatorAdapter_1_9_4 +class TranslatorPortuguese : public Translator { public: @@ -1841,6 +1843,27 @@ class TranslatorPortuguese : public TranslatorAdapter_1_9_4 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "seg", "ter", "qua", "qui", "sex", "sáb", "dom" }; + static const char *days_full[] = { "segunda-feira", "terça-feira", "quarta-feira", "quinta-feira", "sexta-feira", "sábado", "domingo" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "jan", "fev", "mar", "abr", "mai", "jun", "jul", "ago", "set", "out", "nov", "dez" }; + static const char *months_full[] = { "janeiro", "fevereiro", "março", "abril", "maio", "junho", "julho", "agosto", "setembro", "outubro", "novembro", "dezembro" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 @@ -2334,6 +2357,12 @@ class TranslatorPortuguese : public TranslatorAdapter_1_9_4 { return "Definição de conceito"; } + + ////////////////////////////////////////////////////////////////////////// + // new since 1.9.4 + ////////////////////////////////////////////////////////////////////////// + virtual QCString trPackageList() + { return "Lista de pacotes"; } }; #endif diff --git a/src/translator_ro.h b/src/translator_ro.h index 011bc29..7503381 100644 --- a/src/translator_ro.h +++ b/src/translator_ro.h @@ -1828,6 +1828,27 @@ class TranslatorRomanian : public TranslatorAdapter_1_8_15 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "lun.", "mar.", "mie.", "joi", "vin.", "sâm.", "dum." }; + static const char *days_full[] = { "luni", "mar?i", "miercuri", "joi", "vineri", "sâmbătă", "duminică" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "ian.", "feb.", "mar.", "apr.", "mai", "iun.", "iul.", "aug.", "sept.", "oct.", "nov.", "dec." }; + static const char *months_full[] = { "ianuarie", "februarie", "martie", "aprilie", "mai", "iunie", "iulie", "august", "septembrie", "octombrie", "noiembrie", "decembrie" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "a.m.", "p.m." }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_ru.h b/src/translator_ru.h index 0ae718c..3fd63b4 100644 --- a/src/translator_ru.h +++ b/src/translator_ru.h @@ -1790,6 +1790,25 @@ class TranslatorRussian : public TranslatorAdapter_1_8_15 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс" }; + static const char *days_full[] = { "понедельник", "вторник", "среда", "четверг", "пятница", "суббота", "воскресенье" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "янв", "фев", "мар", "апр", "май", "июн", "июл", "авг", "сен", "окт", "ноя", "дек" }; + static const char *months_full[] = { "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } /////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_sc.h b/src/translator_sc.h index 90eff44..e2a69f6 100644 --- a/src/translator_sc.h +++ b/src/translator_sc.h @@ -1762,6 +1762,25 @@ class TranslatorSerbianCyrillic : public TranslatorAdapter_1_6_0 return "Ограничења типова"; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "пон", "уто", "сри", "чет", "пет", "суб", "нед" }; + static const char *days_full[] = { "Понедељак", "Уторак", "Среда", "Четвртак", "Петак", "Субота", "Недеља" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "Јан", "Феб", "Мар", "Апр", "Мај", "Јун", "Јул", "Авг", "Сеп", "Окт", " новембар", "дец" }; + static const char *months_full[] = { "јануар", "фебруар", "март", "април", "мај", "јун", "јул", "август", "септембар", "октобар", " Новембар Децембар" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "пре подне", "по подне" }; + return dayPeriod[period]; + } }; #endif diff --git a/src/translator_si.h b/src/translator_si.h index 2440c1b..3fa3fb9 100644 --- a/src/translator_si.h +++ b/src/translator_si.h @@ -1163,6 +1163,27 @@ class TranslatorSlovene : public TranslatorAdapter_1_4_6 "samo v številu in/ali tipu formalnih argumentov."; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "pon.", "tor.", "sre.", "čet.", "pet.", "sob.", "ned." }; + static const char *days_full[] = { "ponedeljek", "torek", "sreda", "četrtek", "petek", "sobota", "nedelja" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "jan.", "feb.", "mar.", "apr.", "maj", "jun.", "jul.", "avg.", "sep.", "okt.", "nov.", "dec." }; + static const char *months_full[] = { "januar", "februar", "marec", "april", "maj", "junij", "julij", "avgust", "september", "oktober", "november", "december" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "dop.", "pop." }; + return dayPeriod[period]; + } }; #endif diff --git a/src/translator_sk.h b/src/translator_sk.h index a7a44b3..331f860 100644 --- a/src/translator_sk.h +++ b/src/translator_sk.h @@ -1778,6 +1778,27 @@ class TranslatorSlovak : public TranslatorAdapter_1_8_15 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "po", "ut", "st", "št", "pi", "so", "ne" }; + static const char *days_full[] = { "pondelok", "utorok", "streda", "štvrtok", "piatok", "sobota", "nedeľa" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "jan", "feb", "mar", "apr", "máj", "jún", "júl", "aug", "sep", "okt", "nov", "dec" }; + static const char *months_full[] = { "január", "február", "marec", "apríl", "máj", "jún", "júl", "august", "september", "október", "november", "december" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "AM", "PM" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_sr.h b/src/translator_sr.h index 2ef83c2..8d7ed4b 100644 --- a/src/translator_sr.h +++ b/src/translator_sr.h @@ -1720,6 +1720,27 @@ class TranslatorSerbian : public TranslatorAdapter_1_6_0 return "Interfejsi"; //!< Radna okruzenja. Ali to je dve reci. } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "pon", "uto", "sre", "čet", "pet", "sub", "ned" }; + static const char *days_full[] = { "ponedeljak", "utorak", "sreda", "četvrtak", "petak", "subota", "nedelja" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "jan", "feb", "mar", "apr", "maj", "jun", "jul", "avg", "sep", "okt", "nov", "dec" }; + static const char *months_full[] = { "januar", "februar", "mart", "april", "maj", "jun", "jul", "avgust", "septembar", "oktobar", "novembar", "decembar" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "pre podne", "po podne" }; + return dayPeriod[period]; + } }; #endif diff --git a/src/translator_sv.h b/src/translator_sv.h index f7a788c..e1f852b 100644 --- a/src/translator_sv.h +++ b/src/translator_sv.h @@ -1922,6 +1922,27 @@ class TranslatorSwedish : public TranslatorAdapter_1_9_4 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "mån", "tis", "ons", "tor", "fre", "lör", "sön" }; + static const char *days_full[] = { "måndag", "tisdag", "onsdag", "torsdag", "fredag", "lördag", "söndag" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "jan", "feb", "mar", "apr", "maj", "jun", "jul", "aug", "sep", "okt", "nov", "dec" }; + static const char *months_full[] = { "januari", "februari", "mars", "april", "maj", "juni", "juli", "augusti", "september", "oktober", "november", "december" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "fm", "em" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_tr.h b/src/translator_tr.h index 20a1eec..eb133ba 100644 --- a/src/translator_tr.h +++ b/src/translator_tr.h @@ -1808,6 +1808,25 @@ class TranslatorTurkish : public TranslatorAdapter_1_7_5 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "Pzt", "Sal", "Çar", "Per", "Cum", "Cmt", "Paz" }; + static const char *days_full[] = { "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi", "Pazar" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "Oca", "Şub", "Mar", "Nis", "May", "Haz", "Tem", "Ağu", "Eyl", "Eki", "Kas", "Ara" }; + static const char *months_full[] = { "Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "ÖÖ", "ÖS" }; + return dayPeriod[period]; + } }; diff --git a/src/translator_tw.h b/src/translator_tw.h index 0fb7b11..c2730b0 100644 --- a/src/translator_tw.h +++ b/src/translator_tw.h @@ -1797,6 +1797,25 @@ class TranslatorChinesetraditional : public TranslatorAdapter_1_8_15 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "周一", "周二", "周三", "周四", "周五", "周六", "周日" }; + static const char *days_full[] = { "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月" }; + static const char *months_full[] = { "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "上午", "下午" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_ua.h b/src/translator_ua.h index 746616a..7503363 100644 --- a/src/translator_ua.h +++ b/src/translator_ua.h @@ -1788,6 +1788,25 @@ class TranslatorUkrainian : public TranslatorAdapter_1_8_4 } return sdate; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Нд" }; + static const char *days_full[] = { "понеділок", "вівторок", "середа", "четвер", "п'ятниця", "субота", "неділя" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "Січ", "Лют", "Бер", "Кві", "Тра", "Чер", "Лип", "Сер", "Вер", "Жов", "Лис", "Гру" }; + static const char *months_full[] = { "січень", "лютий", "березень", "квітень", "травень", "червень", "липень", "серпень", "вересень", "жовтень", "листопад", "грудень" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "дп", "пп" }; + return dayPeriod[period]; + } ////////////////////////////////////////////////////////////////////////// // new since 1.7.5 diff --git a/src/translator_vi.h b/src/translator_vi.h index 7b3b5d8..b9d101c 100644 --- a/src/translator_vi.h +++ b/src/translator_vi.h @@ -1747,6 +1747,25 @@ class TranslatorVietnamese : public TranslatorAdapter_1_6_0 return "Ràng buộc của kiểu (Type)"; } + virtual QCString trDayOfWeek(int dayOfWeek, bool, bool full) + { + static const char *days_short[] = { "T2", "T3", "T4", "T5", "T6", "T7", "CN" }; + static const char *days_full[] = { "Thứ Hai", "Thứ Ba", "Thứ Tư", "Thứ Năm", "Thứ Sáu", "Thứ Bảy", "Chủ Nhật" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + return text; + } + virtual QCString trMonth(int month, bool, bool full) + { + static const char *months_short[] = { "Thg1", "Thg2", "Thg3", "Thg4", "Thg5", "Thg6", "Thg7", "Thg8", "Thg9", "Thg10", "Thg11", "Thg12" }; + static const char *months_full[] = { "Tháng Giêng", "Tháng Hai", "Tháng Ba", "Tháng Tư", "Tháng Năm", "Tháng Sáu", "Tháng Bảy", "Tháng Tám", "Tháng Chín", "Tháng Mười", "Tháng Mười Một", "Tháng Mười Hai" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "SA", "CH" }; + return dayPeriod[period]; + } }; #endif diff --git a/src/translator_za.h b/src/translator_za.h index ad5bc3c..b85a4de 100644 --- a/src/translator_za.h +++ b/src/translator_za.h @@ -1713,6 +1713,27 @@ class TranslatorAfrikaans : public TranslatorAdapter_1_6_0 return "Tipe Limiete"; } + virtual QCString trDayOfWeek(int dayOfWeek, bool first_capital, bool full) + { + static const char *days_short[] = { "Ma.", "Di.", "Wo.", "Do.", "Vr.", "Sa.", "So." }; + static const char *days_full[] = { "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrydag", "Saterdag", "Sondag" }; + QCString text = full? days_full[dayOfWeek-1] : days_short[dayOfWeek-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trMonth(int month, bool first_capital, bool full) + { + static const char *months_short[] = { "Jan.", "Feb.", "Mrt.", "Apr.", "Mei", "Jun.", "Jul.", "Aug.", "Sep.", "Okt.", "Nov.", "Des." }; + static const char *months_full[] = { "Januarie", "Februarie", "Maart", "April", "Mei", "Junie", "Julie", "Augustus", "September", "Oktober", "November", "Desember" }; + QCString text = full? months_full[month-1] : months_short[month-1]; + if (first_capital) return text.mid(0,1).upper()+text.mid(1); + else return text; + } + virtual QCString trDayPeriod(int period) + { + static const char *dayPeriod[] = { "vm.", "nm." }; + return dayPeriod[period]; + } }; #endif diff --git a/src/types.h b/src/types.h index 69d8f7e..e0fa9a5 100644 --- a/src/types.h +++ b/src/types.h @@ -300,6 +300,7 @@ enum FortranFormat FortranFormat_Fixed }; + class LocalToc { public: diff --git a/src/util.cpp b/src/util.cpp index f957181..12d13eb 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -73,6 +73,8 @@ #include "dir.h" #include "utf8.h" #include "textstream.h" +#include "indexlist.h" +#include "datetime.h" #define ENABLE_TRACINGSUPPORT 0 @@ -331,33 +333,31 @@ QCString stripFromIncludePath(const QCString &path) int guessSection(const QCString &name) { QCString n=name.lower(); - if (n.right(2)==".c" || // source - n.right(3)==".cc" || - n.right(4)==".cxx" || - n.right(4)==".cpp" || - n.right(4)==".c++" || - n.right(5)==".java" || - n.right(2)==".m" || - n.right(3)==".mm" || - n.right(3)==".ii" || // inline - n.right(4)==".ixx" || - n.right(4)==".ipp" || - n.right(4)==".i++" || - n.right(4)==".inl" || - n.right(4)==".xml" || - n.right(4)==".lex" || - n.right(4)==".sql" - ) return Entry::SOURCE_SEC; - if (n.right(2)==".h" || // header - n.right(3)==".hh" || - n.right(4)==".hxx" || - n.right(4)==".hpp" || - n.right(4)==".h++" || - n.right(4)==".idl" || - n.right(4)==".ddl" || - n.right(5)==".pidl" || - n.right(4)==".ice" - ) return Entry::HEADER_SEC; + static const std::unordered_set<std::string> sourceExt = { + "c","cc","cxx","cpp","c++", // C/C++ + "java", // Java + "cs", // C# + "m","mm", // Objective-C + "ii","ixx","ipp","i++","inl", // C/C++ inline + "xml","lex","sql" // others + }; + static const std::unordered_set<std::string> headerExt = { + "h", "hh", "hxx", "hpp", "h++" // C/C++ header + "idl", "ddl", "pidl", "ice" // IDL like + }; + int lastDot = n.findRev('.'); + if (lastDot!=-1) + { + QCString extension = n.mid(lastDot+1); // part after the last dot + if (sourceExt.find(extension.str())!=sourceExt.end()) + { + return Entry::SOURCE_SEC; + } + if (headerExt.find(extension.str())!=headerExt.end()) + { + return Entry::HEADER_SEC; + } + } return 0; } @@ -488,12 +488,6 @@ QCString resolveTypeDef(const Definition *context,const QCString &qualifiedName, } -int computeQualifiedIndex(const QCString &name) -{ - int i = name.find('<'); - return name.findRev("::",i==-1 ? name.length() : i); -} - //------------------------------------------------------------------------- //------------------------------------------------------------------------- //------------------------------------------------------------------------- @@ -896,7 +890,7 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, size_t strLen = txtStr.length(); if (strLen==0) return; - static const reg::Ex regExp(R"((::)?\a[\w~!\\.:$]*)"); + static const reg::Ex regExp(R"((::)?\a[\w~!\\.:$"]*)"); reg::Iterator it(txtStr,regExp); reg::Iterator end; @@ -926,6 +920,7 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, for (size_t i=index;i<newIndex;i++) { if (txtStr.at(i)=='"') insideString=!insideString; + if (txtStr.at(i)=='\\') i++; // skip next character it is escaped } //printf("floatingIndex=%d strlen=%d autoBreak=%d\n",floatingIndex,strLen,autoBreak); @@ -1067,7 +1062,7 @@ void linkifyText(const TextGeneratorIntf &out, const Definition *scope, if (md!=self && (self==0 || md->name()!=self->name())) // name check is needed for overloaded members, where getDefs just returns one { - /* in case of Fortran scop and the variable is a non Fortran variable: don't link, + /* in case of Fortran scope and the variable is a non Fortran variable: don't link, * see also getLink in fortrancode.l */ if (!(scope && (scope->getLanguage() == SrcLangExt_Fortran) && md->isVariable() && (md->getLanguage() != SrcLangExt_Fortran))) @@ -1339,20 +1334,18 @@ QCString getFileFilter(const QCString &name,bool isSourceCode) else { /* remove surrounding double quotes */ - if ((filterName.right(1) == "\"") && (filterName.left(1) == "\"")) + if (filterName.length()>=2 && filterName[0]=='"' && filterName[filterName.length()-1]=='"') { - filterName.remove(filterName.length() - 1, 1); - filterName.remove(0, 1); + filterName = filterName.mid(1,filterName.length()-2); } return filterName; } } -QCString transcodeCharacterStringToUTF8(const QCString &input) +QCString transcodeCharacterStringToUTF8(const QCString &inputEncoding, const QCString &input) { bool error=FALSE; - QCString inputEncoding = Config_getString(INPUT_ENCODING); const char *outputEncoding = "UTF-8"; if (inputEncoding.isEmpty() || qstricmp(inputEncoding,outputEncoding)==0) return input; int inputSize=input.length(); @@ -1384,8 +1377,8 @@ QCString transcodeCharacterStringToUTF8(const QCString &input) qPrint(inputEncoding),outputEncoding,qPrint(input)); error=TRUE; } + portable_iconv_close(cd); } - portable_iconv_close(cd); return error ? input : output; } @@ -1435,58 +1428,6 @@ QCString fileToString(const QCString &name,bool filter,bool isSourceCode) return ""; } -static std::tm getCurrentDateTime() -{ - QCString sourceDateEpoch = Portable::getenv("SOURCE_DATE_EPOCH"); - if (!sourceDateEpoch.isEmpty()) // see https://reproducible-builds.org/specs/source-date-epoch/ - { - bool ok; - uint64 epoch = sourceDateEpoch.toUInt64(&ok); - if (!ok) - { - static bool warnedOnce=FALSE; - if (!warnedOnce) - { - warn_uncond("Environment variable SOURCE_DATE_EPOCH does not contain a valid number; value is '%s'\n", - qPrint(sourceDateEpoch)); - warnedOnce=TRUE; - } - } - else // use given epoch value as current 'built' time - { - auto epoch_start = std::chrono::time_point<std::chrono::system_clock>{}; - auto epoch_seconds = std::chrono::seconds(epoch); - auto build_time = epoch_start + epoch_seconds; - std::time_t time = std::chrono::system_clock::to_time_t(build_time); - return *gmtime(&time); - } - } - - // return current local time - auto now = std::chrono::system_clock::now(); - std::time_t time = std::chrono::system_clock::to_time_t(now); - return *localtime(&time); -} - -QCString dateToString(bool includeTime) -{ - auto current = getCurrentDateTime(); - return theTranslator->trDateTime(current.tm_year + 1900, - current.tm_mon + 1, - current.tm_mday, - (current.tm_wday+6)%7+1, // map: Sun=0..Sat=6 to Mon=1..Sun=7 - current.tm_hour, - current.tm_min, - current.tm_sec, - includeTime); -} - -QCString yearToString() -{ - auto current = getCurrentDateTime(); - return QCString().setNum(current.tm_year+1900); -} - void trimBaseClassScope(const BaseClassList &bcl,QCString &s,int level=0) { //printf("trimBaseClassScope level=%d '%s'\n",level,qPrint(s)); @@ -2143,6 +2084,8 @@ void mergeArguments(ArgumentList &srcAl,ArgumentList &dstAl,bool forceNameOverwr } } +//--------------------------------------------------------------------------------------- + static void findMembersWithSpecificName(const MemberName *mn, const QCString &args, bool checkStatics, @@ -2185,6 +2128,51 @@ static void findMembersWithSpecificName(const MemberName *mn, } } +//--------------------------------------------------------------------------------------- + +bool getDefsNew(const QCString &scName, + const QCString &mbName, + const QCString &args, + const MemberDef *&md, + const ClassDef *&cd, + const FileDef *&fd, + const NamespaceDef *&nd, + const GroupDef *&gd, + bool forceEmptyScope, + const FileDef *currentFile, + bool checkCV + ) +{ + fd=0, md=0, cd=0, nd=0, gd=0; + if (mbName.isEmpty()) return false; + + //printf("@@ --- getDefsNew(%s,%s)-----------\n",qPrint(scName),qPrint(mbName)); + const Definition *scope = Doxygen::globalScope; + SymbolResolver resolver; + if (currentFile) resolver.setFileScope(currentFile); + if (!scName.isEmpty()) + { + scope = resolver.resolveSymbol(scope,scName); + } + if (scope==Doxygen::globalScope) + { + scope = currentFile; + } + //printf("@@ -> found scope scope=%s member=%s out=%s\n",qPrint(scName),qPrint(mbName),qPrint(scope?scope->name():"")); + // + const Definition *symbol = resolver.resolveSymbol(scope,mbName,args,checkCV); + //printf("@@ -> found symbol in=%s out=%s\n",qPrint(mbName),qPrint(symbol?symbol->qualifiedName():QCString())); + if (symbol && symbol->definitionType()==Definition::TypeMember) + { + md = toMemberDef(symbol); + cd = md->getClassDef(); + if (cd==0) nd = md->getNamespaceDef(); + if (cd==0 && nd==0) fd = md->getFileDef(); + gd = md->getGroupDef(); + } + return md!=0; +} + /*! * Searches for a member definition given its name 'memberName' as a string. * memberName may also include a (partial) scope to indicate the scope @@ -2207,7 +2195,7 @@ static void findMembersWithSpecificName(const MemberName *mn, * - if 'fd' is non zero, the member was found in the global namespace of * file fd. */ -bool getDefs(const QCString &scName, +bool getDefsOld(const QCString &scName, const QCString &mbName, const QCString &args, const MemberDef *&md, @@ -2221,16 +2209,23 @@ bool getDefs(const QCString &scName, ) { fd=0, md=0, cd=0, nd=0, gd=0; - if (mbName.isEmpty()) return FALSE; /* empty name => nothing to link */ + bool result = FALSE; + QCString scopeName; + QCString memberName; + QCString mName; + QCString mScope; + MemberName *mn = 0; + int is,im=0,pm=0; + + if (mbName.isEmpty()) goto exit; /* empty name => nothing to link */ - QCString scopeName=scName; - QCString memberName=mbName; - scopeName = substitute(scopeName,"\\","::"); // for PHP + scopeName = scName; + scopeName = substitute(scopeName,"\\","::"); // for PHP + memberName = mbName; memberName = substitute(memberName,"\\","::"); // for PHP //printf("Search for name=%s args=%s in scope=%s forceEmpty=%d\n", // qPrint(memberName),qPrint(args),qPrint(scopeName),forceEmptyScope); - int is,im=0,pm=0; // strip common part of the scope from the scopeName while ((is=scopeName.findRev("::"))!=-1 && (im=memberName.find("::",pm))!=-1 && @@ -2243,9 +2238,8 @@ bool getDefs(const QCString &scName, //printf("result after scope corrections scope=%s name=%s\n", // qPrint(scopeName), qPrint(memberName)); - QCString mName=memberName; - QCString mScope; - if (memberName.left(9)!="operator " && // treat operator conversion methods + mName=memberName; + if (!memberName.startsWith("operator ") && // treat operator conversion methods // as a special case (im=memberName.findRev("::"))!=-1 && im<static_cast<int>(memberName.length())-2 // not A:: @@ -2260,7 +2254,7 @@ bool getDefs(const QCString &scName, //printf("mScope='%s' mName='%s'\n",qPrint(mScope),qPrint(mName)); - MemberName *mn = Doxygen::memberNameLinkedMap->find(mName); + mn = Doxygen::memberNameLinkedMap->find(mName); //printf("mName=%s mn=%p\n",qPrint(mName),mn); if ((!forceEmptyScope || scopeName.isEmpty()) && // this was changed for bug638856, forceEmptyScope => empty scopeName @@ -2362,13 +2356,15 @@ bool getDefs(const QCString &scName, { md=0; // avoid returning things we cannot link to cd=0; - return FALSE; // match found, but was not linkable + result=FALSE; // match found, but was not linkable + goto exit; } else { gd=md->getGroupDef(); if (gd) cd=0; - return TRUE; /* found match */ + result=TRUE; /* found match */ + goto exit; } } } @@ -2383,13 +2379,15 @@ bool getDefs(const QCString &scName, { cd=tmd->getClassDef(); md=emd; - return TRUE; + result=TRUE; + goto exit; } else { cd=0; md=0; - return FALSE; + result=FALSE; + goto exit; } } } @@ -2453,7 +2451,8 @@ bool getDefs(const QCString &scName, { md = fuzzy_mmd; cd = fuzzy_mmd->getClassDef(); - return TRUE; + result=TRUE; + goto exit; } } @@ -2508,7 +2507,8 @@ bool getDefs(const QCString &scName, { md=0; cd=0; - return FALSE; + result=FALSE; + goto exit; } } else if (mmd->getOuterScope()==fnd /* && mmd->isLinkable() */ ) @@ -2554,13 +2554,15 @@ bool getDefs(const QCString &scName, { md=0; // avoid returning things we cannot link to nd=0; - return FALSE; // match found but not linkable + result=FALSE; // match found but not linkable + goto exit; } else { gd=md->resolveAlias()->getGroupDef(); if (gd && gd->isLinkable()) nd=0; else gd=0; - return TRUE; + result=TRUE; + goto exit; } } } @@ -2588,7 +2590,8 @@ bool getDefs(const QCString &scName, if (gd && gd->isLinkable()) fd=0; else gd=0; //printf("Found scoped enum %s fd=%p gd=%p\n", // qPrint(mmd->name()),fd,gd); - return TRUE; + result=TRUE; + goto exit; } } } @@ -2668,15 +2671,84 @@ bool getDefs(const QCString &scName, gd=md->getGroupDef(); //printf("fd=%p gd=%p gd->isLinkable()=%d\n",fd,gd,gd->isLinkable()); if (gd && gd->isLinkable()) fd=0; else gd=0; - return TRUE; + result=TRUE; + goto exit; } } } - // no nothing found - return FALSE; +exit: + return result; +} + +bool getDefs(const QCString &scName, + const QCString &mbName, + const QCString &args, + const MemberDef *&md, + const ClassDef *&cd, + const FileDef *&fd, + const NamespaceDef *&nd, + const GroupDef *&gd, + bool forceEmptyScope, + const FileDef *currentFile, + bool checkCV + ) +{ + if (false) // set this to true to try the old and new routine side-by-side and compare the results + { + printf("@@ ------ getDefsOld start\n"); + bool result = getDefsOld(scName,mbName,args,md,cd,fd,nd,gd,forceEmptyScope,currentFile,checkCV); + printf("@@ ------ getDefsOld end\n"); + const MemberDef *nmd = 0; + const ClassDef *ncd = 0; + const FileDef *nfd = 0; + const NamespaceDef *nnd = 0; + const GroupDef *ngd = 0; + printf("@@ ------ getDefsNew start\n"); + bool newResult = getDefsNew(scName,mbName,args, + nmd,ncd,nfd,nnd,ngd, + forceEmptyScope,currentFile,checkCV); + printf("@@ ------ getDefsNew end\n"); + if (result!=newResult || nmd!=md || ncd!=cd || nfd!=fd || nnd!=nd || ngd!=gd) + { + printf("@@ getDefsOld(scName=%s, mbName=%s, args=%s, forceEmptyScope=%d " + "currentFile=%s checkCV=%d)=%d md=%s (%p) cd=%s fd=%s nd=%s gd=%s\n", + qPrint(scName), qPrint(mbName), qPrint(args), + forceEmptyScope, qPrint(currentFile?currentFile->name():QCString()), + checkCV, + result, + qPrint(md?md->name():QCString()), + (void*)md, + qPrint(cd?cd->name():QCString()), + qPrint(fd?fd->name():QCString()), + qPrint(nd?nd->name():QCString()), + qPrint(gd?gd->name():QCString()) + ); + printf("@@ ------ getDefsOld start\n"); + printf("@@ getDefsNew(scName=%s, mbName=%s, args=%s, forceEmptyScope=%d " + "currentFile=%s checkCV=%d)=%d md=%s (%p) cd=%s fd=%s nd=%s gd=%s\n", + qPrint(scName), qPrint(mbName), qPrint(args), + forceEmptyScope, qPrint(currentFile?currentFile->name():QCString()), + checkCV, + newResult, + qPrint(nmd?nmd->name():QCString()), + (void*)nmd, + qPrint(ncd?ncd->name():QCString()), + qPrint(nfd?nfd->name():QCString()), + qPrint(nnd?nnd->name():QCString()), + qPrint(ngd?ngd->name():QCString()) + ); + } + return result; // use return newResult to use the result of the new routine + } + else // do one of the two getDefs routines (comment out the other one) + { + return getDefsNew(scName,mbName,args,md,cd,fd,nd,gd,forceEmptyScope,currentFile,checkCV); + //return getDefsOld(scName,mbName,args,md,cd,fd,nd,gd,forceEmptyScope,currentFile,checkCV); + } } + /*! * Searches for a scope definition given its name as a string via parameter * `scope`. @@ -2780,10 +2852,10 @@ bool resolveRef(/* in */ const QCString &scName, int bracePos=findParameterList(fullName); int endNamePos=bracePos!=-1 ? bracePos : fullName.length(); int scopePos=fullName.findRev("::",endNamePos); - bool explicitScope = fullName.left(2)=="::" && // ::scope or #scope - (scopePos>2 || // ::N::A - tsName.left(2)=="::" || // ::foo in local scope - scName==0 // #foo in global scope + bool explicitScope = fullName.startsWith("::") && // ::scope or #scope + (scopePos>2 || // ::N::A + tsName.startsWith("::") || // ::foo in local scope + scName==0 // #foo in global scope ); // default result values @@ -3346,6 +3418,7 @@ QCString substituteKeywords(const QCString &s,const QCString &title, result = substitute(result,"$projectnumber",projNum); result = substitute(result,"$projectbrief",projBrief); result = substitute(result,"$projectlogo",stripPath(Config_getString(PROJECT_LOGO))); + result = substitute(result,"$langISO",theTranslator->trISOLang()); return result; } @@ -3439,11 +3512,20 @@ int getUtf8Char(const char *input,char ids[MAX_UTF8_CHAR_SIZE],CaseModifier modi } #endif +bool getCaseSenseNames() +{ + auto caseSenseNames = Config_getEnum(CASE_SENSE_NAMES); + + if (caseSenseNames == CASE_SENSE_NAMES_t::YES) return true; + else if (caseSenseNames == CASE_SENSE_NAMES_t::NO) return false; + else return Portable::fileSystemIsCaseSensitive(); +} + // note that this function is not reentrant due to the use of static growBuf! QCString escapeCharsInString(const QCString &name,bool allowDots,bool allowUnderscore) { if (name.isEmpty()) return name; - bool caseSenseNames = Config_getBool(CASE_SENSE_NAMES); + bool caseSenseNames = getCaseSenseNames(); bool allowUnicodeNames = Config_getBool(ALLOW_UNICODE_NAMES); GrowBuf growBuf; signed char c; @@ -3525,7 +3607,7 @@ QCString escapeCharsInString(const QCString &name,bool allowDots,bool allowUnder QCString unescapeCharsInString(const QCString &s) { if (s.isEmpty()) return s; - bool caseSenseNames = Config_getBool(CASE_SENSE_NAMES); + bool caseSenseNames = getCaseSenseNames(); QCString result; const char *p = s.data(); if (p) @@ -3779,7 +3861,7 @@ done: } //printf("extractNamespace '%s' => '%s|%s'\n",qPrint(scopeName), // qPrint(className),qPrint(namespaceName)); - if (/*className.right(2)=="-g" ||*/ className.right(2)=="-p") + if (className.endsWith("-p")) { className = className.left(className.length()-2); } @@ -4358,7 +4440,7 @@ int extractClassNameFromType(const QCString &type,int &pos,QCString &name,QCStri if (lang == SrcLangExt_Fortran) { if (type[pos]==',') return -1; - if (QCString(type).left(4).lower()!="type") + if (!type.lower().startsWith("type")) { re = &re_fortran; } @@ -4525,17 +4607,17 @@ QCString substituteTemplateArgumentsInString( { actArg = *actIt; } - if (formArg.type.left(6)=="class " && formArg.name.isEmpty()) + if (formArg.type.startsWith("class ") && formArg.name.isEmpty()) { formArg.name = formArg.type.mid(6); formArg.type = "class"; } - if (formArg.type.left(9)=="typename " && formArg.name.isEmpty()) + if (formArg.type.startsWith("typename ") && formArg.name.isEmpty()) { formArg.name = formArg.type.mid(9); formArg.type = "typename"; } - if (formArg.type=="class" || formArg.type=="typename" || formArg.type.left(8)=="template") + if (formArg.type=="class" || formArg.type=="typename" || formArg.type.startsWith("template")) { //printf("n=%s formArg->type='%s' formArg->name='%s' formArg->defval='%s'\n", // qPrint(n),qPrint(formArg->type),qPrint(formArg->name),qPrint(formArg->defval)); @@ -4797,7 +4879,7 @@ PageDef *addRelatedPage(const QCString &name,const QCString &ptitle, if (newPage) // new page { QCString baseName=name; - if (baseName.right(4)==".tex") + if (baseName.endsWith(".tex")) baseName=baseName.left(baseName.length()-4); else if (baseName.right(Doxygen::htmlFileExtension.length())==Doxygen::htmlFileExtension) baseName=baseName.left(baseName.length()-Doxygen::htmlFileExtension.length()); @@ -5656,8 +5738,8 @@ static MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fil if (name.isEmpty()) return 0; // no name was given - auto range = Doxygen::symbolMap->find(name); - if (range.first==range.second) + auto &range = Doxygen::symbolMap->find(name); + if (range.empty()) return 0; // could not find any matching symbols // mostly copied from getResolvedClassRec() @@ -5674,9 +5756,8 @@ static MemberDef *getMemberFromSymbol(const Definition *scope,const FileDef *fil int minDistance = 10000; MemberDef *bestMatch = 0; - for (auto it=range.first; it!=range.second; ++it) + for (Definition *d : range) { - Definition *d = it->second; if (d->definitionType()==Definition::TypeMember) { SymbolResolver resolver(fileScope); @@ -5949,7 +6030,7 @@ static QCString escapeCommas(const QCString &s) static QCString expandAliasRec(StringUnorderedSet &aliasesProcessed,const QCString &s,bool allowRecursion) { QCString result; - static const reg::Ex re(R"([\\@](\a\w*))"); + static const reg::Ex re(R"([\\@](\a[\w-]*))"); std::string str = s.str(); reg::Match match; size_t p = 0; @@ -6010,6 +6091,13 @@ static QCString expandAliasRec(StringUnorderedSet &aliasesProcessed,const QCStri result+=s.right(s.length()-p); //printf("expandAliases '%s'->'%s'\n",s.data(),result.data()); + if (result == s) + { + std::string orgStr = s.str(); + int ridx = orgStr.rfind('-'); + if (ridx != -1) return expandAliasRec(aliasesProcessed,s.left(ridx),allowRecursion) + s.right(s.length() - ridx); + } + return result; } @@ -6250,7 +6338,7 @@ bool readInputFile(const QCString &fileName,BufStr &inBuf,bool filter,bool isSou { // do character transcoding if needed. transcodeCharacterBuffer(fileName,inBuf,inBuf.curPos(), - Config_getString(INPUT_ENCODING),"UTF-8"); + getEncoding(fi),"UTF-8"); } //inBuf.addChar('\n'); /* to prevent problems under Windows ? */ @@ -6290,13 +6378,15 @@ QCString filterTitle(const QCString &title) return QCString(tf); } -//---------------------------------------------------------------------------- -// returns TRUE if the name of the file represented by 'fi' matches -// one of the file patterns in the 'patList' list. +//--------------------------------------------------------------------------------------------------- -bool patternMatch(const FileInfo &fi,const StringVector &patList) +template<class PatternList, class PatternElem, typename PatternGet = QCString(*)(const PatternElem &)> +bool genericPatternMatch(const FileInfo &fi, + const PatternList &patList, + PatternElem &elem, + PatternGet getter) { - bool caseSenseNames = Config_getBool(CASE_SENSE_NAMES); + bool caseSenseNames = getCaseSenseNames(); bool found = FALSE; // For platforms where the file system is non case sensitive overrule the setting @@ -6311,8 +6401,9 @@ bool patternMatch(const FileInfo &fi,const StringVector &patList) std::string fp = fi.filePath(); std::string afp= fi.absFilePath(); - for (auto pattern: patList) + for (const auto &li : patList) { + std::string pattern = getter(li).str(); if (!pattern.empty()) { size_t i=pattern.find('='); @@ -6329,7 +6420,11 @@ bool patternMatch(const FileInfo &fi,const StringVector &patList) found = re.isValid() && (reg::match(fn,re) || (fn!=fp && reg::match(fp,re)) || (fn!=afp && fp!=afp && reg::match(afp,re))); - if (found) break; + if (found) + { + elem = li; + break; + } //printf("Matching '%s' against pattern '%s' found=%d\n", // qPrint(fi->fileName()),qPrint(pattern),found); } @@ -6338,6 +6433,31 @@ bool patternMatch(const FileInfo &fi,const StringVector &patList) return found; } +//---------------------------------------------------------------------------- +// returns TRUE if the name of the file represented by 'fi' matches +// one of the file patterns in the 'patList' list. + +bool patternMatch(const FileInfo &fi,const StringVector &patList) +{ + std::string elem; + auto getter = [](std::string s) { return QCString(s); }; + return genericPatternMatch(fi,patList,elem,getter); +} + +QCString getEncoding(const FileInfo &fi) +{ + InputFileEncoding elem; + auto getter = [](const InputFileEncoding &e) { return e.pattern; }; + if (genericPatternMatch(fi,Doxygen::inputFileEncodingList,elem,getter)) // check for file specific encoding + { + return elem.encoding; + } + else // fall back to default encoding + { + return Config_getString(INPUT_ENCODING); + } +} + QCString externalLinkTarget(const bool parent) { bool extLinksInWindow = Config_getBool(EXT_LINKS_IN_WINDOW); @@ -6578,12 +6698,12 @@ QCString getLanguageSpecificSeparator(SrcLangExt lang,bool classScope) /** Checks whether the given url starts with a supported protocol */ bool isURL(const QCString &url) { + static const std::unordered_set<std::string> schemes = { + "http", "https", "ftp", "ftps", "sftp", "file", "news", "irc", "ircs" + }; QCString loc_url = url.stripWhiteSpace(); - return loc_url.left(5)=="http:" || loc_url.left(6)=="https:" || - loc_url.left(4)=="ftp:" || loc_url.left(5)=="ftps:" || - loc_url.left(5)=="sftp:" || loc_url.left(5)=="file:" || - loc_url.left(5)=="news:" || loc_url.left(4)=="irc:" || - loc_url.left(5)=="ircs:"; + int colonPos = loc_url.find(':'); + return colonPos!=-1 && schemes.find(loc_url.left(colonPos).str())!=schemes.end(); } /** Corrects URL \a url according to the relative path \a relPath. * Returns the corrected URL. For absolute URLs no correction will be done. @@ -282,6 +282,8 @@ PageDef *addRelatedPage(const QCString &name, SrcLangExt lang=SrcLangExt_Unknown ); +bool getCaseSenseNames(); + QCString escapeCharsInString(const QCString &name,bool allowDots,bool allowUnderscore=FALSE); QCString unescapeCharsInString(const QCString &s); @@ -314,7 +316,20 @@ QCString stripExtension(const QCString &fName); void replaceNamespaceAliases(QCString &scope,int i); -int computeQualifiedIndex(const QCString &name); +//! Return the index of the last :: in the string \a name that is still before the first < +inline int computeQualifiedIndex(const QCString &name) +{ + int l = static_cast<int>(name.length()); + int lastSepPos = -1; + const char *p = name.data(); + for (int i=0;i<l-1;i++) + { + char c=*p++; + if (c==':' && *p==':') lastSepPos=i; + if (c=='<') break; + } + return lastSepPos; +} void addDirPrefix(QCString &fileName); @@ -342,7 +357,7 @@ bool checkIfTypedef(const Definition *scope,const FileDef *fileScope,const QCStr QCString parseCommentAsText(const Definition *scope,const MemberDef *member,const QCString &doc,const QCString &fileName,int lineNr); -QCString transcodeCharacterStringToUTF8(const QCString &input); +QCString transcodeCharacterStringToUTF8(const QCString &inputEncoding,const QCString &input); QCString recodeString(const QCString &str,const char *fromEncoding,const char *toEncoding); @@ -430,4 +445,6 @@ FortranFormat convertFileNameFortranParserCode(QCString fn); QCString integerToAlpha(int n, bool upper=true); QCString integerToRoman(int n, bool upper=true); +QCString getEncoding(const FileInfo &fi); + #endif diff --git a/src/vhdldocgen.cpp b/src/vhdldocgen.cpp index 59a24a7..cb06554 100644 --- a/src/vhdldocgen.cpp +++ b/src/vhdldocgen.cpp @@ -39,7 +39,6 @@ #include "util.h" #include "language.h" #include "commentscan.h" -#include "index.h" #include "definition.h" #include "searchindex.h" #include "outputlist.h" @@ -2558,7 +2557,7 @@ static void addInstance(ClassDefMutable* classEntity, ClassDefMutable* ar, // fprintf(stderr,"\naddInstance %s to %s %s %s\n",qPrint( classEntity->name()),qPrint(cd->name()),qPrint(ar->name()),cur->name); n1=classEntity->name(); - if (!cd->isBaseClass(classEntity, true, 0)) + if (!cd->isBaseClass(classEntity, true)) { cd->insertBaseClass(classEntity,n1,Public,Normal,QCString()); } @@ -2670,7 +2669,7 @@ bool VhdlDocGen::isSubClass(ClassDef* cd,ClassDef *scd, bool followInstances,int { err("Possible recursive class relation while inside %s and looking for %s\n",qPrint(cd->name()),qPrint(scd->name())); abort(); - return FALSE; + return false; } for (const auto &bcd :cd->subClasses()) @@ -2680,13 +2679,17 @@ bool VhdlDocGen::isSubClass(ClassDef* cd,ClassDef *scd, bool followInstances,int //printf("isSubClass() subclass %s\n",qPrint(ccd->name())); if (ccd==scd) { - found=TRUE; + found=true; } else { if (level <256) { - found=ccd->isBaseClass(scd,followInstances,level+1); + level = ccd->isBaseClass(scd,followInstances); + if (level>0) + { + found=true; + } } } } diff --git a/src/vhdljjparser.cpp b/src/vhdljjparser.cpp index bea252f..b9cc8a8 100644 --- a/src/vhdljjparser.cpp +++ b/src/vhdljjparser.cpp @@ -22,7 +22,6 @@ #include "util.h" #include "language.h" #include "commentscan.h" -#include "index.h" #include "definition.h" #include "searchindex.h" #include "outputlist.h" diff --git a/src/vhdljjparser.h b/src/vhdljjparser.h index 542f48d..542f48d 100755..100644 --- a/src/vhdljjparser.h +++ b/src/vhdljjparser.h diff --git a/src/xmldocvisitor.cpp b/src/xmldocvisitor.cpp index d3395db..9335f8d 100644 --- a/src/xmldocvisitor.cpp +++ b/src/xmldocvisitor.cpp @@ -294,9 +294,6 @@ void XmlDocVisitor::operator()(const DocStyleChange &s) break; case DocStyleChange::Div: /* HTML only */ break; case DocStyleChange::Span: /* HTML only */ break; - case DocStyleChange::Details: - if (s.enable()) m_t << "<details>"; else m_t << "</details>"; - break; case DocStyleChange::Summary: if (s.enable()) m_t << "<summary>"; else m_t << "</summary>"; break; @@ -834,17 +831,17 @@ void XmlDocVisitor::operator()(const DocHtmlCell &c) } else if (opt.name=="class") // handle markdown generated attributes { - if (opt.value.left(13)=="markdownTable") // handle markdown generated attributes + if (opt.value.startsWith("markdownTable")) // handle markdown generated attributes { - if (opt.value.right(5)=="Right") + if (opt.value.endsWith("Right")) { m_t << " align='right'"; } - else if (opt.value.right(4)=="Left") + else if (opt.value.endsWith("Left")) { m_t << " align='left'"; } - else if (opt.value.right(6)=="Center") + else if (opt.value.endsWith("Center")) { m_t << " align='center'"; } @@ -890,6 +887,14 @@ void XmlDocVisitor::operator()(const DocHRef &href) m_t << "</ulink>"; } +void XmlDocVisitor::operator()(const DocHtmlDetails &d) +{ + if (m_hide) return; + m_t << "<details>"; + visitChildren(d); + m_t << "</details>"; +} + void XmlDocVisitor::operator()(const DocHtmlHeader &header) { if (m_hide) return; diff --git a/src/xmldocvisitor.h b/src/xmldocvisitor.h index ce3b657..0670fe5 100644 --- a/src/xmldocvisitor.h +++ b/src/xmldocvisitor.h @@ -82,6 +82,7 @@ class XmlDocVisitor : public DocVisitor void operator()(const DocHtmlCaption &); void operator()(const DocInternal &); void operator()(const DocHRef &); + void operator()(const DocHtmlDetails &); void operator()(const DocHtmlHeader &); void operator()(const DocImage &); void operator()(const DocDotFile &); diff --git a/src/xmlgen.cpp b/src/xmlgen.cpp index 07b0d83..5594fcb 100644 --- a/src/xmlgen.cpp +++ b/src/xmlgen.cpp @@ -866,6 +866,7 @@ static void generateXMLForMember(const MemberDef *md,TextStream &ti,TextStream & { const ArgumentList &declAl = md->declArgumentList(); const ArgumentList &defAl = md->argumentList(); + bool isFortran = md->getLanguage()==SrcLangExt_Fortran; if (declAl.hasParameters()) { auto defIt = defAl.begin(); @@ -885,7 +886,13 @@ static void generateXMLForMember(const MemberDef *md,TextStream &ti,TextStream & writeXMLString(t,a.attrib); t << "</attributes>\n"; } - if (!a.type.isEmpty()) + if (isFortran && defArg && !defArg->type.isEmpty()) + { + t << " <type>"; + linkifyText(TextGeneratorXMLImpl(t),def,md->getBodyDef(),md,defArg->type); + t << "</type>\n"; + } + else if (!a.type.isEmpty()) { t << " <type>"; linkifyText(TextGeneratorXMLImpl(t),def,md->getBodyDef(),md,a.type); |