1 /******************************************************************************
3 * $Id: searchindex.cpp,v 1.7 2001/03/19 19:27:41 root Exp $
5 * Copyright (C) 1997-2012 by Dimitri van Heesch.
7 * Permission to use, copy, modify, and distribute this software and its
8 * documentation under the terms of the GNU General Public License is hereby
9 * granted. No representations are made about the suitability of this software
10 * for any purpose. It is provided "as is" without express or implied warranty.
11 * See the GNU General Public License for more details.
13 * Documents produced by Doxygen are derivative works derived from the
14 * input used in their production; they are not affected by this license.
25 #include "searchindex.h"
35 // file format: (all multi-byte values are stored in big endian format)
37 // 256*256*4 byte index (4 bytes)
38 // for each index entry: a zero terminated list of words
39 // for each word: a \0 terminated string + 4 byte offset to the stats info
40 // padding bytes to align at 4 byte boundary
41 // for each word: the number of urls (4 bytes)
42 // + for each url containing the word 8 bytes statistics
43 // (4 bytes index to url string + 4 bytes frequency counter)
44 // for each url: a \0 terminated string
46 const int numIndexEntries = 256*256;
48 //--------------------------------------------------------------------
50 IndexWord::IndexWord(const char *word) : m_word(word), m_urls(17)
52 m_urls.setAutoDelete(TRUE);
53 //printf("IndexWord::IndexWord(%s)\n",word);
56 void IndexWord::addUrlIndex(int idx,bool hiPriority)
58 //printf("IndexWord::addUrlIndex(%d,%d)\n",idx,hiPriority);
59 URLInfo *ui = m_urls.find(idx);
62 //printf("URLInfo::URLInfo(%d)\n",idx);
63 ui=new URLInfo(idx,0);
64 m_urls.insert(idx,ui);
67 if (hiPriority) ui->freq|=1; // mark as high priority document
70 //--------------------------------------------------------------------
72 SearchIndex::SearchIndex() : SearchIndexIntf(Internal),
73 m_words(328829), m_index(numIndexEntries), m_urlIndex(-1)
76 m_words.setAutoDelete(TRUE);
77 m_urls.setAutoDelete(TRUE);
78 m_index.setAutoDelete(TRUE);
79 for (i=0;i<numIndexEntries;i++) m_index.insert(i,new QList<IndexWord>);
82 void SearchIndex::setCurrentDoc(Definition *ctx,const char *anchor,bool isSourceFile)
85 assert(!isSourceFile || ctx->definitionType()==Definition::TypeFile);
86 //printf("SearchIndex::setCurrentDoc(%s,%s,%s)\n",name,baseName,anchor);
87 QCString url=isSourceFile ? ((FileDef*)ctx)->getSourceFileBase() : ctx->getOutputFileBase();
88 url+=Config_getString("HTML_FILE_EXTENSION");
89 if (anchor) url+=QCString("#")+anchor;
91 QCString name=ctx->qualifiedName();
92 if (ctx->definitionType()==Definition::TypeMember)
94 MemberDef *md = (MemberDef *)ctx;
95 name.prepend((md->getLanguage()==SrcLangExt_Fortran ?
96 theTranslator->trSubprogram(TRUE,TRUE) :
97 theTranslator->trMember(TRUE,TRUE))+" ");
101 SrcLangExt lang = ctx->getLanguage();
102 QCString sep = getLanguageSpecificSeparator(lang);
105 name = substitute(name,"::",sep);
107 switch (ctx->definitionType())
109 case Definition::TypePage:
111 PageDef *pd = (PageDef *)ctx;
112 if (!pd->title().isEmpty())
114 name = theTranslator->trPage(TRUE,TRUE)+" "+pd->title();
118 name = theTranslator->trPage(TRUE,TRUE)+" "+pd->name();
122 case Definition::TypeClass:
124 ClassDef *cd = (ClassDef *)ctx;
125 name.prepend(cd->compoundTypeString()+" ");
128 case Definition::TypeNamespace:
130 if (lang==SrcLangExt_Java || lang==SrcLangExt_CSharp)
132 name = theTranslator->trPackage(name);
134 else if (lang==SrcLangExt_Fortran)
136 name.prepend(theTranslator->trModule(TRUE,TRUE)+" ");
140 name.prepend(theTranslator->trNamespace(TRUE,TRUE)+" ");
144 case Definition::TypeGroup:
146 GroupDef *gd = (GroupDef *)ctx;
147 if (gd->groupTitle())
149 name = theTranslator->trGroup(TRUE,TRUE)+" "+gd->groupTitle();
153 name.prepend(theTranslator->trGroup(TRUE,TRUE)+" ");
162 m_urls.insert(m_urlIndex,new URL(name,url));
165 static int charsToIndex(const char *word)
167 if (word==0) return -1;
169 // Fast string hashing algorithm
170 //register ushort h=0;
171 //const char *k = word;
172 //ushort mask=0xfc00;
175 // h = (h&mask)^(h<<6)^(*k++);
179 // Simple hashing that allows for substring searching
181 if (c1==0) return -1;
183 if (c2==0) return -1;
187 void SearchIndex::addWord(const char *word,bool hiPriority,bool recurse)
189 static QRegExp nextPart("[_a-z:][A-Z]");
190 //printf("SearchIndex::addWord(%s,%d)\n",word,hiPriority);
191 if (word==0 || word[0]=='\0') return;
192 QCString wStr = QCString(word).lower();
193 IndexWord *w = m_words[wStr];
196 int idx=charsToIndex(wStr);
198 w = new IndexWord(wStr);
199 //fprintf(stderr,"addWord(%s) at index %d\n",word,idx);
200 m_index[idx]->append(w);
201 m_words.insert(wStr,w);
203 w->addUrlIndex(m_urlIndex,hiPriority);
206 if (!recurse) // the first time we check if we can strip the prefix
208 i=getPrefixIndex(word);
211 addWord(word+i,hiPriority,TRUE);
215 if (!found) // no prefix stripped
217 if ((i=nextPart.match(word))>=1)
219 addWord(word+i+1,hiPriority,TRUE);
224 void SearchIndex::addWord(const char *word,bool hiPriority)
226 addWord(word,hiPriority,FALSE);
229 static void writeInt(QFile &f,int index)
231 f.putch(((uint)index)>>24);
232 f.putch((((uint)index)>>16)&0xff);
233 f.putch((((uint)index)>>8)&0xff);
234 f.putch(((uint)index)&0xff);
237 static void writeString(QFile &f,const char *s)
240 while (*p) f.putch(*p++);
244 void SearchIndex::write(const char *fileName)
247 int size=4; // for the header
248 size+=4*numIndexEntries; // for the index
249 int wordsOffset = size;
250 // first pass: compute the size of the wordlist
251 for (i=0;i<numIndexEntries;i++)
253 QList<IndexWord> *wlist = m_index[i];
254 if (!wlist->isEmpty())
256 QListIterator<IndexWord> iwi(*wlist);
258 for (iwi.toFirst();(iw=iwi.current());++iwi)
260 int ws = iw->word().length()+1;
261 size+=ws+4; // word + url info list offset
263 size+=1; // zero list terminator
267 // second pass: compute the offsets in the index
268 int indexOffsets[numIndexEntries];
269 int offset=wordsOffset;
270 for (i=0;i<numIndexEntries;i++)
272 QList<IndexWord> *wlist = m_index[i];
273 if (!wlist->isEmpty())
275 indexOffsets[i]=offset;
276 QListIterator<IndexWord> iwi(*wlist);
278 for (iwi.toFirst();(iw=iwi.current());++iwi)
280 offset+= iw->word().length()+1;
281 offset+=4; // word + offset to url info array
283 offset+=1; // zero list terminator
291 size = (size+3)&~3; // round up to 4 byte boundary
292 padding = size - padding;
294 //int statsOffset = size;
295 QDictIterator<IndexWord> wdi(m_words);
297 int *wordStatOffsets = new int[m_words.count()];
301 // third pass: compute offset to stats info for each word
302 for (i=0;i<numIndexEntries;i++)
304 QList<IndexWord> *wlist = m_index[i];
305 if (!wlist->isEmpty())
307 QListIterator<IndexWord> iwi(*wlist);
309 for (iwi.toFirst();(iw=iwi.current());++iwi)
311 //printf("wordStatOffsets[%d]=%d\n",count,size);
312 wordStatOffsets[count++] = size;
313 size+=4+iw->urls().count()*8; // count + (url_index,freq) per url
317 int *urlOffsets = new int[m_urls.count()];
318 //int urlsOffset = size;
319 QIntDictIterator<URL> udi(m_urls);
321 for (udi.toFirst();(url=udi.current());++udi)
323 urlOffsets[udi.currentKey()]=size;
324 size+=url->name.length()+1+
327 //printf("Total size %x bytes (word=%x stats=%x urls=%x)\n",size,wordsOffset,statsOffset,urlsOffset);
329 if (f.open(IO_WriteOnly))
332 f.putch('D'); f.putch('O'); f.putch('X'); f.putch('S');
334 for (i=0;i<numIndexEntries;i++)
336 writeInt(f,indexOffsets[i]);
340 for (i=0;i<numIndexEntries;i++)
342 QList<IndexWord> *wlist = m_index[i];
343 if (!wlist->isEmpty())
345 QListIterator<IndexWord> iwi(*wlist);
347 for (iwi.toFirst();(iw=iwi.current());++iwi)
349 writeString(f,iw->word());
350 writeInt(f,wordStatOffsets[count++]);
355 // write extra padding bytes
356 for (i=0;i<padding;i++) f.putch(0);
357 // write word statistics
358 for (i=0;i<numIndexEntries;i++)
360 QList<IndexWord> *wlist = m_index[i];
361 if (!wlist->isEmpty())
363 QListIterator<IndexWord> iwi(*wlist);
365 for (iwi.toFirst();(iw=iwi.current());++iwi)
367 int numUrls = iw->urls().count();
369 QIntDictIterator<URLInfo> uli(iw->urls());
371 for (uli.toFirst();(ui=uli.current());++uli)
373 writeInt(f,urlOffsets[ui->urlIdx]);
374 writeInt(f,ui->freq);
380 QIntDictIterator<URL> udi(m_urls);
382 for (udi.toFirst();(url=udi.current());++udi)
384 writeString(f,url->name);
385 writeString(f,url->url);
390 delete[] wordStatOffsets;
394 //---------------------------------------------------------------------------
395 // the following part is for writing an external search index
397 struct SearchDocEntry
403 GrowBuf importantText;
407 struct SearchIndexExternal::Private
409 Private() : docEntries(257) {}
414 SDict<SearchDocEntry> docEntries;
415 SearchDocEntry *current;
418 SearchIndexExternal::SearchIndexExternal() : SearchIndexIntf(External)
420 p = new SearchIndexExternal::Private;
421 p->docEntries.setAutoDelete(TRUE);
423 //p->f.setName(fileName);
424 //p->openOk = p->f.open(IO_WriteOnly);
427 // p->t.setDevice(&p->f);
428 // p->t << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
429 // p->t << "<add>" << endl;
430 // p->insideDoc=FALSE;
434 SearchIndexExternal::~SearchIndexExternal()
440 // p->t << " </doc>" << endl;
442 // p->t << "</add>" << endl;
449 static QCString definitionToName(Definition *ctx)
451 if (ctx->definitionType()==Definition::TypeMember)
453 MemberDef *md = (MemberDef*)ctx;
454 if (md->isFunction())
456 else if (md->isSlot())
458 else if (md->isSignal())
460 else if (md->isVariable())
462 else if (md->isTypedef())
464 else if (md->isEnumerate())
466 else if (md->isEnumValue())
468 else if (md->isProperty())
470 else if (md->isEvent())
472 else if (md->isRelated() || md->isForeign())
474 else if (md->isFriend())
476 else if (md->isDefine())
481 switch(ctx->definitionType())
483 case Definition::TypeClass:
484 return ((ClassDef*)ctx)->compoundTypeString();
485 case Definition::TypeFile:
487 case Definition::TypeNamespace:
489 case Definition::TypeGroup:
491 case Definition::TypePackage:
493 case Definition::TypePage:
495 case Definition::TypeDir:
504 void SearchIndexExternal::setCurrentDoc(Definition *ctx,const char *anchor,bool isSourceFile)
508 SearchDocEntry *e = new SearchDocEntry;
509 e->type = definitionToName(ctx);
510 e->name = ctx->qualifiedName();
511 e->tag = stripPath(Config_getString("GENERATE_TAGFILE"));
512 QCString baseName = isSourceFile ? ((FileDef*)ctx)->getSourceFileBase() : ctx->getOutputFileBase();
513 e->url = baseName + Doxygen::htmlFileExtension;
514 if (anchor) e->url+=QCString("#")+anchor;
516 p->docEntries.append(e->url,e);
519 // p->t << " </doc>" << endl;
521 //p->t << " <doc>" << endl;
522 //QCString baseName = isSourceFile ? ((FileDef*)ctx)->getSourceFileBase() : ctx->getOutputFileBase();
523 //p->t << " <field name=\"type\">" << definitionToName(ctx) << "</field>" << endl;
524 //p->t << " <field name=\"name\">" << convertToXML(ctx->qualifiedName()) << "</field>" << endl;
525 //p->t << " <field name=\"tag\">" << convertToXML(stripPath(Config_getString("GENERATE_TAGFILE"))) << "</field>" << endl;
526 //p->t << " <field name=\"url\">" << baseName << Doxygen::htmlFileExtension;
527 //if (anchor) p->t << "#" << anchor;
528 //p->t << "</field>" << endl;
533 void SearchIndexExternal::addWord(const char *word,bool hiPriority)
535 if (word==0 || !isId(*word) || p->current==0) return;
536 GrowBuf *pText = hiPriority ? &p->current->importantText : &p->current->normalText;
537 if (pText->getPos()>0) pText->addChar(' ');
541 // p->t << " <field name=\"text";
542 // if (hiPriority) p->t << "\" boost=\"yes";
544 // p->t << convertToXML(word);
545 // p->t << "</field>" << endl;
549 void SearchIndexExternal::write(const char *fileName)
552 if (f.open(IO_WriteOnly))
555 t << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << endl;
556 t << "<add>" << endl;
557 SDict<SearchDocEntry>::Iterator it(p->docEntries);
559 for (it.toFirst();(doc=it.current());++it)
561 doc->normalText.addChar(0); // make sure buffer ends with a 0 terminator
562 doc->importantText.addChar(0); // make sure buffer ends with a 0 terminator
563 t << " <doc>" << endl;
564 t << " <field name=\"type\">" << doc->type << "</field>" << endl;
565 t << " <field name=\"name\">" << convertToXML(doc->name) << "</field>" << endl;
566 t << " <field name=\"tag\">" << convertToXML(doc->tag) << "</field>" << endl;
567 t << " <field name=\"url\">" << doc->url << "</field>" << endl;
568 t << " <field name=\"keywords\">" << convertToXML(doc->importantText.get()) << "</field>" << endl;
569 t << " <field name=\"text\">" << convertToXML(doc->normalText.get()) << "</field>" << endl;
570 t << " </doc>" << endl;
572 t << "</add>" << endl;
576 err("Failed to open file %s for writing!\n",fileName);
580 //---------------------------------------------------------------------------
581 // the following part is for the javascript based search engine
583 #include "memberdef.h"
584 #include "namespacedef.h"
586 #include "classdef.h"
588 #include "language.h"
592 static const char search_script[]=
593 #include "search_js.h"
596 #define MEMBER_INDEX_ENTRIES 256
598 #define SEARCH_INDEX_ALL 0
599 #define SEARCH_INDEX_CLASSES 1
600 #define SEARCH_INDEX_NAMESPACES 2
601 #define SEARCH_INDEX_FILES 3
602 #define SEARCH_INDEX_FUNCTIONS 4
603 #define SEARCH_INDEX_VARIABLES 5
604 #define SEARCH_INDEX_TYPEDEFS 6
605 #define SEARCH_INDEX_ENUMS 7
606 #define SEARCH_INDEX_ENUMVALUES 8
607 #define SEARCH_INDEX_PROPERTIES 9
608 #define SEARCH_INDEX_EVENTS 10
609 #define SEARCH_INDEX_RELATED 11
610 #define SEARCH_INDEX_DEFINES 12
611 #define SEARCH_INDEX_GROUPS 13
612 #define SEARCH_INDEX_PAGES 14
613 #define NUM_SEARCH_INDICES 15
615 class SearchIndexList : public SDict< QList<Definition> >
618 SearchIndexList(int size=17) : SDict< QList<Definition> >(size,FALSE)
622 ~SearchIndexList() {}
623 void append(Definition *d)
625 QList<Definition> *l = find(d->name());
628 l=new QList<Definition>;
629 SDict< QList<Definition> >::append(d->name(),l);
633 int compareItems(GCI item1, GCI item2)
635 QList<Definition> *md1=(QList<Definition> *)item1;
636 QList<Definition> *md2=(QList<Definition> *)item2;
637 QCString n1 = md1->first()->localName();
638 QCString n2 = md2->first()->localName();
639 return stricmp(n1.data(),n2.data());
643 static void addMemberToSearchIndex(
644 SearchIndexList symbols[NUM_SEARCH_INDICES][MEMBER_INDEX_ENTRIES],
645 int symbolCount[NUM_SEARCH_INDICES],
648 static bool hideFriendCompounds = Config_getBool("HIDE_FRIEND_COMPOUNDS");
649 bool isLinkable = md->isLinkable();
656 ((cd=md->getClassDef()) && cd->isLinkable() && cd->templateMaster()==0) ||
657 ((gd=md->getGroupDef()) && gd->isLinkable())
661 QCString n = md->name();
662 uchar charCode = (uchar)n.at(0);
663 uint letter = charCode<128 ? tolower(charCode) : charCode;
666 bool isFriendToHide = hideFriendCompounds &&
667 (QCString(md->typeString())=="friend class" ||
668 QCString(md->typeString())=="friend struct" ||
669 QCString(md->typeString())=="friend union");
670 if (!(md->isFriend() && isFriendToHide))
672 symbols[SEARCH_INDEX_ALL][letter].append(md);
673 symbolCount[SEARCH_INDEX_ALL]++;
675 if (md->isFunction() || md->isSlot() || md->isSignal())
677 symbols[SEARCH_INDEX_FUNCTIONS][letter].append(md);
678 symbolCount[SEARCH_INDEX_FUNCTIONS]++;
680 else if (md->isVariable())
682 symbols[SEARCH_INDEX_VARIABLES][letter].append(md);
683 symbolCount[SEARCH_INDEX_VARIABLES]++;
685 else if (md->isTypedef())
687 symbols[SEARCH_INDEX_TYPEDEFS][letter].append(md);
688 symbolCount[SEARCH_INDEX_TYPEDEFS]++;
690 else if (md->isEnumerate())
692 symbols[SEARCH_INDEX_ENUMS][letter].append(md);
693 symbolCount[SEARCH_INDEX_ENUMS]++;
695 else if (md->isEnumValue())
697 symbols[SEARCH_INDEX_ENUMVALUES][letter].append(md);
698 symbolCount[SEARCH_INDEX_ENUMVALUES]++;
700 else if (md->isProperty())
702 symbols[SEARCH_INDEX_PROPERTIES][letter].append(md);
703 symbolCount[SEARCH_INDEX_PROPERTIES]++;
705 else if (md->isEvent())
707 symbols[SEARCH_INDEX_EVENTS][letter].append(md);
708 symbolCount[SEARCH_INDEX_EVENTS]++;
710 else if (md->isRelated() || md->isForeign() ||
711 (md->isFriend() && !isFriendToHide))
713 symbols[SEARCH_INDEX_RELATED][letter].append(md);
714 symbolCount[SEARCH_INDEX_RELATED]++;
718 else if (isLinkable &&
719 (((nd=md->getNamespaceDef()) && nd->isLinkable()) ||
720 ((fd=md->getFileDef()) && fd->isLinkable())
724 QCString n = md->name();
725 uchar charCode = (uchar)n.at(0);
726 uint letter = charCode<128 ? tolower(charCode) : charCode;
729 symbols[SEARCH_INDEX_ALL][letter].append(md);
730 symbolCount[SEARCH_INDEX_ALL]++;
732 if (md->isFunction())
734 symbols[SEARCH_INDEX_FUNCTIONS][letter].append(md);
735 symbolCount[SEARCH_INDEX_FUNCTIONS]++;
737 else if (md->isVariable())
739 symbols[SEARCH_INDEX_VARIABLES][letter].append(md);
740 symbolCount[SEARCH_INDEX_VARIABLES]++;
742 else if (md->isTypedef())
744 symbols[SEARCH_INDEX_TYPEDEFS][letter].append(md);
745 symbolCount[SEARCH_INDEX_TYPEDEFS]++;
747 else if (md->isEnumerate())
749 symbols[SEARCH_INDEX_ENUMS][letter].append(md);
750 symbolCount[SEARCH_INDEX_ENUMS]++;
752 else if (md->isEnumValue())
754 symbols[SEARCH_INDEX_ENUMVALUES][letter].append(md);
755 symbolCount[SEARCH_INDEX_ENUMVALUES]++;
757 else if (md->isDefine())
759 symbols[SEARCH_INDEX_DEFINES][letter].append(md);
760 symbolCount[SEARCH_INDEX_DEFINES]++;
766 static QCString searchId(const QCString &s)
771 for (i=0;i<s.length();i++)
774 if ((c>='0' && c<='9') || (c>='A' && c<='Z') || (c>='a' && c<='z'))
776 result+=(char)tolower(c);
781 sprintf(val,"_%02x",(uchar)c);
788 static int g_searchIndexCount[NUM_SEARCH_INDICES];
789 static SearchIndexList g_searchIndexSymbols[NUM_SEARCH_INDICES][MEMBER_INDEX_ENTRIES];
790 static const char *g_searchIndexName[NUM_SEARCH_INDICES] =
810 class SearchIndexCategoryMapping
813 SearchIndexCategoryMapping()
815 categoryLabel[SEARCH_INDEX_ALL] = theTranslator->trAll();
816 categoryLabel[SEARCH_INDEX_CLASSES] = theTranslator->trClasses();
817 categoryLabel[SEARCH_INDEX_NAMESPACES] = theTranslator->trNamespace(TRUE,FALSE);
818 categoryLabel[SEARCH_INDEX_FILES] = theTranslator->trFile(TRUE,FALSE);
819 categoryLabel[SEARCH_INDEX_FUNCTIONS] = theTranslator->trFunctions();
820 categoryLabel[SEARCH_INDEX_VARIABLES] = theTranslator->trVariables();
821 categoryLabel[SEARCH_INDEX_TYPEDEFS] = theTranslator->trTypedefs();
822 categoryLabel[SEARCH_INDEX_ENUMS] = theTranslator->trEnumerations();
823 categoryLabel[SEARCH_INDEX_ENUMVALUES] = theTranslator->trEnumerationValues();
824 categoryLabel[SEARCH_INDEX_PROPERTIES] = theTranslator->trProperties();
825 categoryLabel[SEARCH_INDEX_EVENTS] = theTranslator->trEvents();
826 categoryLabel[SEARCH_INDEX_RELATED] = theTranslator->trFriends();
827 categoryLabel[SEARCH_INDEX_DEFINES] = theTranslator->trDefines();
828 categoryLabel[SEARCH_INDEX_GROUPS] = theTranslator->trGroup(TRUE,FALSE);
829 categoryLabel[SEARCH_INDEX_PAGES] = theTranslator->trPage(TRUE,FALSE);
831 QCString categoryLabel[NUM_SEARCH_INDICES];
834 void writeJavascriptSearchIndex()
836 if (!Config_getBool("GENERATE_HTML")) return;
839 ClassSDict::Iterator cli(*Doxygen::classSDict);
841 for (;(cd=cli.current());++cli)
843 uchar charCode = (uchar)cd->localName().at(0);
844 uint letter = charCode<128 ? tolower(charCode) : charCode;
845 if (cd->isLinkable() && isId(letter))
847 g_searchIndexSymbols[SEARCH_INDEX_ALL][letter].append(cd);
848 g_searchIndexSymbols[SEARCH_INDEX_CLASSES][letter].append(cd);
849 g_searchIndexCount[SEARCH_INDEX_ALL]++;
850 g_searchIndexCount[SEARCH_INDEX_CLASSES]++;
855 NamespaceSDict::Iterator nli(*Doxygen::namespaceSDict);
857 for (;(nd=nli.current());++nli)
859 uchar charCode = (uchar)nd->name().at(0);
860 uint letter = charCode<128 ? tolower(charCode) : charCode;
861 if (nd->isLinkable() && isId(letter))
863 g_searchIndexSymbols[SEARCH_INDEX_ALL][letter].append(nd);
864 g_searchIndexSymbols[SEARCH_INDEX_NAMESPACES][letter].append(nd);
865 g_searchIndexCount[SEARCH_INDEX_ALL]++;
866 g_searchIndexCount[SEARCH_INDEX_NAMESPACES]++;
871 FileNameListIterator fnli(*Doxygen::inputNameList);
873 for (;(fn=fnli.current());++fnli)
875 FileNameIterator fni(*fn);
877 for (;(fd=fni.current());++fni)
879 uchar charCode = (uchar)fd->name().at(0);
880 uint letter = charCode<128 ? tolower(charCode) : charCode;
881 if (fd->isLinkable() && isId(letter))
883 g_searchIndexSymbols[SEARCH_INDEX_ALL][letter].append(fd);
884 g_searchIndexSymbols[SEARCH_INDEX_FILES][letter].append(fd);
885 g_searchIndexCount[SEARCH_INDEX_ALL]++;
886 g_searchIndexCount[SEARCH_INDEX_FILES]++;
891 // index class members
893 MemberNameSDict::Iterator mnli(*Doxygen::memberNameSDict);
895 // for each member name
896 for (mnli.toFirst();(mn=mnli.current());++mnli)
899 MemberNameIterator mni(*mn);
900 // for each member definition
901 for (mni.toFirst();(md=mni.current());++mni)
903 addMemberToSearchIndex(g_searchIndexSymbols,g_searchIndexCount,md);
908 // index file/namespace members
910 MemberNameSDict::Iterator fnli(*Doxygen::functionNameSDict);
912 // for each member name
913 for (fnli.toFirst();(mn=fnli.current());++fnli)
916 MemberNameIterator mni(*mn);
917 // for each member definition
918 for (mni.toFirst();(md=mni.current());++mni)
920 addMemberToSearchIndex(g_searchIndexSymbols,g_searchIndexCount,md);
926 GroupSDict::Iterator gli(*Doxygen::groupSDict);
928 for (gli.toFirst();(gd=gli.current());++gli)
930 if (gd->isLinkable())
932 QCString title = gd->groupTitle();
933 if (!title.isEmpty()) // TODO: able searching for all word in the title
935 uchar charCode = title.at(0);
936 uint letter = charCode<128 ? tolower(charCode) : charCode;
939 g_searchIndexSymbols[SEARCH_INDEX_ALL][letter].append(gd);
940 g_searchIndexSymbols[SEARCH_INDEX_GROUPS][letter].append(gd);
941 g_searchIndexCount[SEARCH_INDEX_ALL]++;
942 g_searchIndexCount[SEARCH_INDEX_GROUPS]++;
949 PageSDict::Iterator pdi(*Doxygen::pageSDict);
951 for (pdi.toFirst();(pd=pdi.current());++pdi)
953 if (pd->isLinkable())
955 QCString title = pd->title();
956 if (!title.isEmpty())
958 uchar charCode = title.at(0);
959 uint letter = charCode<128 ? tolower(charCode) : charCode;
962 g_searchIndexSymbols[SEARCH_INDEX_ALL][letter].append(pd);
963 g_searchIndexSymbols[SEARCH_INDEX_PAGES][letter].append(pd);
964 g_searchIndexCount[SEARCH_INDEX_ALL]++;
965 g_searchIndexCount[SEARCH_INDEX_PAGES]++;
970 if (Doxygen::mainPage)
972 QCString title = Doxygen::mainPage->title();
973 if (!title.isEmpty())
975 uchar charCode = title.at(0);
976 uint letter = charCode<128 ? tolower(charCode) : charCode;
979 g_searchIndexSymbols[SEARCH_INDEX_ALL][letter].append(Doxygen::mainPage);
980 g_searchIndexSymbols[SEARCH_INDEX_PAGES][letter].append(Doxygen::mainPage);
981 g_searchIndexCount[SEARCH_INDEX_ALL]++;
982 g_searchIndexCount[SEARCH_INDEX_PAGES]++;
989 for (i=0;i<NUM_SEARCH_INDICES;i++)
991 for (p=0;p<MEMBER_INDEX_ENTRIES;p++)
993 if (g_searchIndexSymbols[i][p].count()>0)
995 g_searchIndexSymbols[i][p].sort();
1000 // write index files
1001 QCString searchDirName = Config_getString("HTML_OUTPUT")+"/search";
1003 for (i=0;i<NUM_SEARCH_INDICES;i++)
1005 for (p=0;p<MEMBER_INDEX_ENTRIES;p++)
1007 if (g_searchIndexSymbols[i][p].count()>0)
1010 baseName.sprintf("%s_%02x",g_searchIndexName[i],p);
1012 QCString fileName = searchDirName + "/"+baseName+".html";
1013 QCString dataFileName = searchDirName + "/"+baseName+".js";
1015 QFile outFile(fileName);
1016 QFile dataOutFile(dataFileName);
1017 if (outFile.open(IO_WriteOnly) && dataOutFile.open(IO_WriteOnly))
1020 FTextStream t(&outFile);
1022 t << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\""
1023 " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" << endl;
1024 t << "<html><head><title></title>" << endl;
1025 t << "<meta http-equiv=\"Content-Type\" content=\"text/xhtml;charset=UTF-8\"/>" << endl;
1026 t << "<link rel=\"stylesheet\" type=\"text/css\" href=\"search.css\"/>" << endl;
1027 t << "<script type=\"text/javascript\" src=\"" << baseName << ".js\"></script>" << endl;
1028 t << "<script type=\"text/javascript\" src=\"search.js\"></script>" << endl;
1029 t << "</head>" << endl;
1030 t << "<body class=\"SRPage\">" << endl;
1031 t << "<div id=\"SRIndex\">" << endl;
1032 t << "<div class=\"SRStatus\" id=\"Loading\">" << theTranslator->trLoading() << "</div>" << endl;
1033 t << "<div id=\"SRResults\"></div>" << endl; // here the results will be inserted
1034 t << "<script type=\"text/javascript\"><!--" << endl;
1035 t << "createResults();" << endl; // this function will insert the results
1036 t << "--></script>" << endl;
1037 t << "<div class=\"SRStatus\" id=\"Searching\">"
1038 << theTranslator->trSearching() << "</div>" << endl;
1039 t << "<div class=\"SRStatus\" id=\"NoMatches\">"
1040 << theTranslator->trNoMatches() << "</div>" << endl;
1042 t << "<script type=\"text/javascript\"><!--" << endl;
1043 t << "document.getElementById(\"Loading\").style.display=\"none\";" << endl;
1044 t << "document.getElementById(\"NoMatches\").style.display=\"none\";" << endl;
1045 t << "var searchResults = new SearchResults(\"searchResults\");" << endl;
1046 t << "searchResults.Search();" << endl;
1047 t << "--></script>" << endl;
1048 t << "</div>" << endl; // SRIndex
1049 t << "</body>" << endl;
1050 t << "</html>" << endl;
1052 FTextStream ti(&dataOutFile);
1054 ti << "var searchData=" << endl;
1056 // searchData[] = array of items
1057 // searchData[x][0] = id
1058 // searchData[x][1] = [ name + child1 + child2 + .. ]
1059 // searchData[x][1][0] = name as shown
1060 // searchData[x][1][y+1] = info for child y
1061 // searchData[x][1][y+1][0] = url
1062 // searchData[x][1][y+1][1] = 1 => target="_parent"
1063 // searchData[x][1][y+1][2] = scope
1066 bool firstEntry=TRUE;
1068 SDict<QList<Definition> >::Iterator li(g_searchIndexSymbols[i][p]);
1069 QList<Definition> *dl;
1071 for (li.toFirst();(dl=li.current());++li)
1073 Definition *d = dl->first();
1074 QCString id = d->localName();
1082 QCString dispName = d->localName();
1083 if (d->definitionType()==Definition::TypeGroup)
1085 dispName = ((GroupDef*)d)->groupTitle();
1087 else if (d->definitionType()==Definition::TypePage)
1089 dispName = ((PageDef*)d)->title();
1091 ti << " ['" << searchId(dispName) << "',['"
1092 << convertToXML(dispName) << "',[";
1094 if (dl->count()==1) // item with a unique name
1097 bool isMemberDef = d->definitionType()==Definition::TypeMember;
1098 if (isMemberDef) md = (MemberDef*)d;
1099 QCString anchor = d->anchor();
1101 ti << "'" << externalRef("../",d->getReference(),TRUE)
1102 << d->getOutputFileBase() << Doxygen::htmlFileExtension;
1103 if (!anchor.isEmpty())
1105 ti << "#" << anchor;
1109 static bool extLinksInWindow = Config_getBool("EXT_LINKS_IN_WINDOW");
1110 if (!extLinksInWindow || d->getReference().isEmpty())
1119 if (d->getOuterScope()!=Doxygen::globalScope)
1121 ti << "'" << convertToXML(d->getOuterScope()->name()) << "'";
1125 FileDef *fd = md->getBodyDef();
1126 if (fd==0) fd = md->getFileDef();
1129 ti << "'" << convertToXML(fd->localName()) << "'";
1138 else // multiple items with the same name
1140 QListIterator<Definition> di(*dl);
1141 bool overloadedFunction = FALSE;
1142 Definition *prevScope = 0;
1144 for (di.toFirst();(d=di.current());)
1147 Definition *scope = d->getOuterScope();
1148 Definition *next = di.current();
1149 Definition *nextScope = 0;
1151 bool isMemberDef = d->definitionType()==Definition::TypeMember;
1152 if (isMemberDef) md = (MemberDef*)d;
1153 if (next) nextScope = next->getOuterScope();
1154 QCString anchor = d->anchor();
1160 ti << "'" << externalRef("../",d->getReference(),TRUE)
1161 << d->getOutputFileBase() << Doxygen::htmlFileExtension;
1162 if (!anchor.isEmpty())
1164 ti << "#" << anchor;
1168 static bool extLinksInWindow = Config_getBool("EXT_LINKS_IN_WINDOW");
1169 if (!extLinksInWindow || d->getReference().isEmpty())
1178 overloadedFunction = ((prevScope!=0 && scope==prevScope) ||
1179 (scope && scope==nextScope)
1181 (md->isFunction() || md->isSlot());
1183 if (md) prefix=convertToXML(md->localName());
1184 if (overloadedFunction) // overloaded member function
1186 prefix+=convertToXML(md->argsString());
1187 // show argument list to disambiguate overloaded functions
1189 else if (md) // unique member function
1191 prefix+="()"; // only to show it is a function
1194 if (d->definitionType()==Definition::TypeClass)
1196 name = convertToXML(((ClassDef*)d)->displayName());
1199 else if (d->definitionType()==Definition::TypeNamespace)
1201 name = convertToXML(((NamespaceDef*)d)->displayName());
1204 else if (scope==0 || scope==Doxygen::globalScope) // in global scope
1208 FileDef *fd = md->getBodyDef();
1209 if (fd==0) fd = md->getFileDef();
1212 if (!prefix.isEmpty()) prefix+=": ";
1213 name = prefix + convertToXML(fd->localName());
1218 else if (md && (md->getClassDef() || md->getNamespaceDef()))
1219 // member in class or namespace scope
1221 SrcLangExt lang = md->getLanguage();
1222 name = convertToXML(d->getOuterScope()->qualifiedName())
1223 + getLanguageSpecificSeparator(lang) + prefix;
1226 else if (scope) // some thing else? -> show scope
1228 name = prefix + convertToXML(scope->name());
1231 if (!found) // fallback
1233 name = prefix + "("+theTranslator->trGlobalNamespace()+")";
1236 ti << "'" << name << "'";
1257 err("Failed to open file '%s' for writing...\n",fileName.data());
1264 QFile f(searchDirName+"/search.js");
1265 if (f.open(IO_WriteOnly))
1268 t << "// Search script generated by doxygen" << endl;
1269 t << "// Copyright (C) 2009 by Dimitri van Heesch." << endl << endl;
1270 t << "// The code in this file is loosly based on main.js, part of Natural Docs," << endl;
1271 t << "// which is Copyright (C) 2003-2008 Greg Valure" << endl;
1272 t << "// Natural Docs is licensed under the GPL." << endl << endl;
1273 t << "var indexSectionsWithContent =" << endl;
1277 for (i=0;i<NUM_SEARCH_INDICES;i++)
1279 if (g_searchIndexCount[i]>0)
1281 if (!first) t << "," << endl;
1282 t << " " << j << ": \"";
1283 for (p=0;p<MEMBER_INDEX_ENTRIES;p++)
1285 t << (g_searchIndexSymbols[i][p].count()>0 ? "1" : "0");
1292 if (!first) t << "\n";
1293 t << "};" << endl << endl;
1294 t << "var indexSectionNames =" << endl;
1298 for (i=0;i<NUM_SEARCH_INDICES;i++)
1300 if (g_searchIndexCount[i]>0)
1302 if (!first) t << "," << endl;
1303 t << " " << j << ": \"" << g_searchIndexName[i] << "\"";
1308 if (!first) t << "\n";
1309 t << "};" << endl << endl;
1314 QFile f(searchDirName+"/nomatches.html");
1315 if (f.open(IO_WriteOnly))
1318 t << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
1319 "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" << endl;
1320 t << "<html><head><title></title>" << endl;
1321 t << "<meta http-equiv=\"Content-Type\" content=\"text/xhtml;charset=UTF-8\"/>" << endl;
1322 t << "<link rel=\"stylesheet\" type=\"text/css\" href=\"search.css\"/>" << endl;
1323 t << "<script type=\"text/javascript\" src=\"search.js\"></script>" << endl;
1324 t << "</head>" << endl;
1325 t << "<body class=\"SRPage\">" << endl;
1326 t << "<div id=\"SRIndex\">" << endl;
1327 t << "<div class=\"SRStatus\" id=\"NoMatches\">"
1328 << theTranslator->trNoMatches() << "</div>" << endl;
1329 t << "</div>" << endl;
1330 t << "</body>" << endl;
1331 t << "</html>" << endl;
1334 Doxygen::indexList.addStyleSheetFile("search/search.js");
1337 void writeSearchCategories(FTextStream &t)
1339 static SearchIndexCategoryMapping map;
1341 for (i=0;i<NUM_SEARCH_INDICES;i++)
1343 if (g_searchIndexCount[i]>0)
1345 t << "<a class=\"SelectItem\" href=\"javascript:void(0)\" "
1346 << "onclick=\"searchBox.OnSelectItem(" << j << ")\">"
1347 << "<span class=\"SelectionMark\"> </span>"
1348 << convertToXML(map.categoryLabel[i])
1355 //---------------------------------------------------------------------------------------------
1357 void initSearchIndexer()
1359 static bool searchEngine = Config_getBool("SEARCHENGINE");
1360 static bool serverBasedSearch = Config_getBool("SERVER_BASED_SEARCH");
1361 if (searchEngine && serverBasedSearch)
1363 //Doxygen::searchIndex = new SearchIndexExternal;
1364 Doxygen::searchIndex = new SearchIndex;
1366 else // no search engine or pure javascript based search function
1368 Doxygen::searchIndex = 0;
1372 void finializeSearchIndexer()
1374 delete Doxygen::searchIndex;