Imported Upstream version 1.8.15
[platform/upstream/doxygen.git] / src / htmlhelp.cpp
1 /******************************************************************************
2  *
3  * 
4  *
5  * Copyright (C) 1997-2015 by Dimitri van Heesch.
6  *
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.
12  *
13  * Documents produced by Doxygen are derivative works derived from the
14  * input used in their production; they are not affected by this license.
15  *
16  * The original version of this file is largely based on a contribution from
17  * Harm van der Heijden.
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <qlist.h>
23 #include <qdict.h>
24 #include <qregexp.h>
25 #include <qfile.h>
26
27 #include "qtextcodec.h"
28 #include "sortdict.h"
29 #include "htmlhelp.h"
30 #include "config.h"
31 #include "message.h"
32 #include "doxygen.h"
33 #include "language.h"
34 #include "portable.h"
35 #include "groupdef.h"
36 #include "memberdef.h"
37 #include "filedef.h"
38 #include "util.h"
39
40 //----------------------------------------------------------------------------
41
42 /** Class representing a field in the HTML help index. */
43 struct IndexField
44 {
45   QCString name;
46   QCString url;
47   QCString anchor;
48   bool     link;
49   bool     reversed;
50 };
51
52 /** Sorted dictionary of IndexField objects. */
53 class IndexFieldSDict : public SDict<IndexField>
54 {
55   public:
56     IndexFieldSDict() : SDict<IndexField>(17) {}
57    ~IndexFieldSDict() {}
58  private:
59     int compareValues(const IndexField *item1, const IndexField *item2) const
60     {
61       return qstricmp(item1->name,item2->name);
62     }
63 };
64
65 /** A helper class for HtmlHelp that manages a two level index in 
66  *  alphabetical order.
67  */
68 class HtmlHelpIndex
69 {
70   public:
71     HtmlHelpIndex(HtmlHelp *help);
72    ~HtmlHelpIndex();
73     void addItem(const char *first,const char *second, 
74                  const char *url, const char *anchor,
75                  bool hasLink,bool reversed);
76     void writeFields(FTextStream &t);
77   private:
78     IndexFieldSDict *dict;   
79     HtmlHelp *m_help;
80 };
81
82 /*! Constructs a new HtmlHelp index */
83 HtmlHelpIndex::HtmlHelpIndex(HtmlHelp *help) : m_help(help)
84 {
85   dict = new IndexFieldSDict;
86   dict->setAutoDelete(TRUE);
87 }
88
89 /*! Destroys the HtmlHelp index */
90 HtmlHelpIndex::~HtmlHelpIndex()
91 {
92   delete dict;
93 }
94
95 /*! Stores an item in the index if it is not already present. 
96  *  Items are stored in alphetical order, by sorting on the
97  *  concatenation of \a level1 and \a level2 (if present).
98  *
99  *  \param level1 the string at level 1 in the index.
100  *  \param level2 the string at level 2 in the index (or 0 if not applicable).
101  *  \param url the url of the documentation (without .html extension).
102  *  \param anchor the anchor of the documentation within the page.
103  *  \param hasLink if true, the url (without anchor) can be used in the 
104  *         level1 item, when writing the header of a list of level2 items.
105  *  \param reversed TRUE if level1 is the member name and level2 the compound
106  *         name.
107  */
108 void HtmlHelpIndex::addItem(const char *level1,const char *level2,
109                        const char *url,const char *anchor,bool hasLink,
110                        bool reversed)
111 {
112   QCString key = level1; 
113   if (level2) key+= (QCString)"?" + level2;
114   if (key.find(QRegExp("@[0-9]+"))!=-1) // skip anonymous stuff
115   {
116     return;
117   }
118   if (dict->find(key+anchor)==0) // new key
119   {
120     //printf(">>>>>>>>> HtmlHelpIndex::addItem(%s,%s,%s,%s)\n",
121     //      level1,level2,url,anchor);
122     IndexField *f = new IndexField;
123     f->name     = key;
124     f->url      = url;
125     f->anchor   = anchor;
126     f->link     = hasLink;
127     f->reversed = reversed;
128     dict->append(key+anchor,f);
129   }
130 }
131
132 static QCString field2URL(const IndexField *f,bool checkReversed)
133 {
134   QCString result = f->url + Doxygen::htmlFileExtension;
135   if (!f->anchor.isEmpty() && (!checkReversed || f->reversed)) 
136   {
137     // HTML Help needs colons in link anchors to be escaped in the .hhk file.
138     result+="#"+substitute(f->anchor,":","%3A");
139   }
140   return result;
141 }
142
143 /*! Writes the sorted list of index items into a html like list.
144  *
145  *  An list of calls with <code>name = level1,level2</code> as follows:
146  *  <pre>
147  *    a1,b1
148  *    a1,b2
149  *    a2,b1
150  *    a2,b2
151  *    a3
152  *    a4,b1
153  *  </pre>
154  *
155  *  Will result in the following list:
156  *
157  *  <pre>
158  *    a1       -> link to url if hasLink==TRUE
159  *      b1     -> link to url#anchor
160  *      b2     -> link to url#anchor
161  *    a2       -> link to url if hasLink==TRUE
162  *      b1     -> link to url#anchor
163  *      b2     -> link to url#anchor
164  *    a3       -> link to url if hasLink==TRUE
165  *    a4       -> link to url if hasLink==TRUE
166  *      b1     -> link to url#anchor 
167  *  </pre>
168  */
169 void HtmlHelpIndex::writeFields(FTextStream &t)
170 {
171   dict->sort();
172   IndexFieldSDict::Iterator ifli(*dict);
173   IndexField *f;
174   QCString lastLevel1;
175   bool level2Started=FALSE;
176   for (;(f=ifli.current());++ifli)
177   {
178     QCString level1,level2;
179     int i;
180     if ((i=f->name.find('?'))!=-1)
181     {
182       level1 = f->name.left(i);
183       level2 = f->name.right(f->name.length()-i-1); 
184     }
185     else
186     {
187       level1  = f->name.copy();
188     }
189
190     //if (level1!=lastLevel1)
191     { // finish old list at level 2
192       if (level2Started) t << "  </UL>" << endl;
193       level2Started=FALSE;
194
195       // <Antony>
196       // Added this code so that an item with only one subitem is written
197       // without any subitem.
198       // For example:
199       //   a1, b1 -> will create only a1, not separate subitem for b1
200       //   a2, b2
201       //   a2, b3
202       QCString nextLevel1;
203       IndexField* fnext = ++ifli;
204       if (fnext)
205       {
206         nextLevel1 = fnext->name.left(fnext->name.find('?'));
207         --ifli;
208       }
209       if (level1 != nextLevel1)
210       {
211         level2 = "";
212       }
213       // </Antony>
214
215       if (level2.isEmpty())
216       {
217         t << "  <LI><OBJECT type=\"text/sitemap\">";
218         t << "<param name=\"Local\" value=\"" << field2URL(f,FALSE);
219         t << "\">";
220         t << "<param name=\"Name\" value=\"" << m_help->recode(level1) << "\">"
221            "</OBJECT>\n";
222       }
223       else
224       {
225         if (f->link)
226         {
227           t << "  <LI><OBJECT type=\"text/sitemap\">";
228           t << "<param name=\"Local\" value=\"" << field2URL(f,TRUE);
229           t << "\">";
230           t << "<param name=\"Name\" value=\"" << m_help->recode(level1) << "\">"
231                "</OBJECT>\n";
232         }
233         else
234         {
235           t << "  <LI><OBJECT type=\"text/sitemap\">";
236           t << "<param name=\"See Also\" value=\"" << m_help->recode(level1) << "\">";
237           t << "<param name=\"Name\" value=\"" << m_help->recode(level1) << "\">"
238                "</OBJECT>\n";
239         }
240       }
241     }
242     if (!level2Started && !level2.isEmpty())
243     { // start new list at level 2
244       t << "  <UL>" << endl;
245       level2Started=TRUE;
246     }
247     else if (level2Started && level2.isEmpty())
248     { // end list at level 2
249       t << "  </UL>" << endl;
250       level2Started=FALSE;
251     }
252     if (level2Started)
253     {
254       t << "    <LI><OBJECT type=\"text/sitemap\">";
255       t << "<param name=\"Local\" value=\"" << field2URL(f,FALSE);
256       t << "\">";
257       t << "<param name=\"Name\" value=\"" << m_help->recode(level2) << "\">"
258          "</OBJECT>\n";
259     }
260     lastLevel1 = level1.copy();
261   } 
262   if (level2Started) t << "  </UL>" << endl;
263 }
264
265 //----------------------------------------------------------------------------
266
267 HtmlHelp *HtmlHelp::theInstance = 0;
268
269 /*! Constructs an html object. 
270  *  The object has to be \link initialize() initialized\endlink before it can 
271  *  be used.
272  */
273 HtmlHelp::HtmlHelp() : indexFileDict(1009)
274 {
275   /* initial depth */
276   dc = 0;
277   cf = kf = 0;
278   index = new HtmlHelpIndex(this);
279   m_fromUtf8 = (void *)(-1);
280 }
281
282 HtmlHelp::~HtmlHelp()
283 {
284   if (m_fromUtf8!=(void *)(-1))   portable_iconv_close(m_fromUtf8);
285   delete index;
286 }
287 #if 0
288 /*! return a reference to the one and only instance of this class. 
289  */
290 HtmlHelp *HtmlHelp::getInstance()
291 {
292   if (theInstance==0) theInstance = new HtmlHelp;
293   return theInstance;
294 }
295 #endif
296
297 static QDict<QCString> s_languageDict;
298
299 /*! This will create a contents file (index.hhc) and a index file (index.hhk)
300  *  and write the header of those files. 
301  *  It also creates a project file (index.hhp)
302  *  \sa finalize()
303  */
304 void HtmlHelp::initialize()
305 {
306   const char *str = Config_getString(CHM_INDEX_ENCODING);
307   if (!str) str = "CP1250"; // use safe and likely default
308   m_fromUtf8 = portable_iconv_open(str,"UTF-8"); 
309   if (m_fromUtf8==(void *)(-1))
310   {
311     err("unsupported character conversion for CHM_INDEX_ENCODING: '%s'->'UTF-8'\n", str);
312     exit(1);
313   }
314
315   /* open the contents file */
316   QCString fName = Config_getString(HTML_OUTPUT) + "/index.hhc";
317   cf = new QFile(fName);
318   if (!cf->open(IO_WriteOnly))
319   {
320     err("Could not open file %s for writing\n",fName.data());
321     exit(1);
322   }
323   /* Write the header of the contents file */
324   cts.setDevice(cf);
325   cts << "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n"
326          "<HTML><HEAD></HEAD><BODY>\n"
327          "<OBJECT type=\"text/site properties\">\n"
328          "<param name=\"FrameName\" value=\"right\">\n"
329          "</OBJECT>\n"
330          "<UL>\n";
331   
332   /* open the contents file */
333   fName = Config_getString(HTML_OUTPUT) + "/index.hhk";
334   kf = new QFile(fName);
335   if (!kf->open(IO_WriteOnly))
336   {
337     err("Could not open file %s for writing\n",fName.data());
338     exit(1);
339   }
340   /* Write the header of the contents file */
341   kts.setDevice(kf);
342   kts << "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n"
343          "<HTML><HEAD></HEAD><BODY>\n"
344          "<OBJECT type=\"text/site properties\">\n"
345          "<param name=\"FrameName\" value=\"right\">\n"
346          "</OBJECT>\n"
347          "<UL>\n";
348
349   /* language codes for Html help
350      0x405 Czech
351      0x406 Danish
352      0x413 Dutch
353      0xC09 English (Australia)
354      0x809 English (Britain)
355      0x1009 English (Canada)
356      0x1809 English (Ireland)
357      0x1409 English (New Zealand)
358      0x1C09 English (South Africa)
359      0x409 English (United States)
360      0x40B Finnish
361      0x40C French
362      0x407 German
363      0x408 Greece
364      0x40E Hungarian
365      0x410 Italian
366      0x814 Norwegian
367      0x415 Polish
368      0x816 Portuguese(Portugal)
369      0x416 Portuguese(Brazil)
370      0x419 Russian
371      0x80A Spanish(Mexico)
372      0xC0A Spanish(Modern Sort)
373      0x40A Spanish(Traditional Sort)
374      0x41D Swedish
375      0x41F Turkey
376      0x411 Japanese
377      0x412 Korean
378      0x804 Chinese (PRC)
379      0x404 Chinese (Taiwan)
380
381      New LCIDs:
382          0x421 Indonesian
383          0x41A Croatian
384          0x418 Romanian
385          0x424 Slovenian
386          0x41B Slovak
387          0x422 Ukrainian
388          0x81A Serbian (Serbia, Latin)
389          0x403 Catalan
390          0x426 Latvian
391          0x427 Lithuanian
392          0x436 Afrikaans
393          0x42A Vietnamese
394          0x429 Persian (Iran)
395          0xC01 Arabic (Egypt) - I don't know which version of arabic is used inside translator_ar.h ,
396      so I have chosen Egypt at random
397
398   */
399   s_languageDict.setAutoDelete(TRUE);
400   s_languageDict.clear();
401   s_languageDict.insert("czech",       new QCString("0x405 Czech"));
402   s_languageDict.insert("danish",      new QCString("0x406 Danish"));
403   s_languageDict.insert("dutch",       new QCString("0x413 Dutch"));
404   s_languageDict.insert("finnish",     new QCString("0x40B Finnish"));
405   s_languageDict.insert("french",      new QCString("0x40C French"));
406   s_languageDict.insert("german",      new QCString("0x407 German"));
407   s_languageDict.insert("greek",       new QCString("0x408 Greece"));
408   s_languageDict.insert("hungarian",   new QCString("0x40E Hungarian"));
409   s_languageDict.insert("italian",     new QCString("0x410 Italian"));
410   s_languageDict.insert("norwegian",   new QCString("0x814 Norwegian"));
411   s_languageDict.insert("polish",      new QCString("0x415 Polish"));
412   s_languageDict.insert("portuguese",  new QCString("0x816 Portuguese(Portugal)"));
413   s_languageDict.insert("brazilian",   new QCString("0x416 Portuguese(Brazil)"));
414   s_languageDict.insert("russian",     new QCString("0x419 Russian"));
415   s_languageDict.insert("spanish",     new QCString("0x40A Spanish(Traditional Sort)"));
416   s_languageDict.insert("swedish",     new QCString("0x41D Swedish"));
417   s_languageDict.insert("turkish",     new QCString("0x41F Turkey"));
418   s_languageDict.insert("japanese",    new QCString("0x411 Japanese"));
419   s_languageDict.insert("japanese-en", new QCString("0x411 Japanese"));
420   s_languageDict.insert("korean",      new QCString("0x412 Korean"));
421   s_languageDict.insert("korean-en",   new QCString("0x412 Korean"));
422   s_languageDict.insert("chinese",     new QCString("0x804 Chinese (PRC)"));
423   s_languageDict.insert("chinese-traditional", new QCString("0x404 Chinese (Taiwan)"));
424
425   // new LCIDs
426   s_languageDict.insert("indonesian",  new QCString("0x412 Indonesian"));
427   s_languageDict.insert("croatian",    new QCString("0x41A Croatian"));
428   s_languageDict.insert("romanian",    new QCString("0x418 Romanian"));
429   s_languageDict.insert("slovene",     new QCString("0x424 Slovenian"));
430   s_languageDict.insert("slovak",      new QCString("0x41B Slovak"));
431   s_languageDict.insert("ukrainian",   new QCString("0x422 Ukrainian"));
432   s_languageDict.insert("serbian",     new QCString("0x81A Serbian (Serbia, Latin)"));
433   s_languageDict.insert("catalan",     new QCString("0x403 Catalan"));
434   s_languageDict.insert("lithuanian",  new QCString("0x427 Lithuanian"));
435   s_languageDict.insert("afrikaans",   new QCString("0x436 Afrikaans"));
436   s_languageDict.insert("vietnamese",  new QCString("0x42A Vietnamese"));
437   s_languageDict.insert("persian",     new QCString("0x429 Persian (Iran)"));
438   s_languageDict.insert("arabic",      new QCString("0xC01 Arabic (Egypt)"));
439   s_languageDict.insert("latvian",     new QCString("0x426 Latvian"));
440   s_languageDict.insert("macedonian",  new QCString("0x042f Macedonian (Former Yugoslav Republic of Macedonia)"));
441   s_languageDict.insert("armenian",    new QCString("0x42b Armenian"));
442   //Code for Esperanto should be as shown below but the htmlhelp compiler 1.3 does not support this
443   // (and no newer version is available).
444   //So do a fallback to the default language (see getLanguageString())
445   //s_languageDict.insert("esperanto",   new QCString("0x48f Esperanto"));
446   s_languageDict.insert("serbian-cyrillic", new QCString("0xC1A Serbian (Serbia, Cyrillic)"));
447 }
448
449
450 QCString HtmlHelp::getLanguageString()
451 {
452   if (!theTranslator->idLanguage().isEmpty())
453   {
454     QCString *s = s_languageDict[theTranslator->idLanguage()];
455     if (s)
456     {
457       return *s;
458     }
459   }
460   // default language
461   return "0x409 English (United States)";
462 }
463   
464
465
466 void HtmlHelp::createProjectFile()
467 {
468   /* Write the project file */
469   QCString fName = Config_getString(HTML_OUTPUT) + "/index.hhp";
470   QFile f(fName);
471   if (f.open(IO_WriteOnly))
472   {
473     FTextStream t(&f);
474     
475     QCString indexName="index"+Doxygen::htmlFileExtension;
476     t << "[OPTIONS]\n";
477     if (!Config_getString(CHM_FILE).isEmpty())
478     {
479       t << "Compiled file=" << Config_getString(CHM_FILE) << "\n";
480     }
481     t << "Compatibility=1.1\n"
482          "Full-text search=Yes\n"
483          "Contents file=index.hhc\n"
484          "Default Window=main\n"
485          "Default topic=" << indexName << "\n"
486          "Index file=index.hhk\n"
487          "Language=" << getLanguageString() << endl;
488     if (Config_getBool(BINARY_TOC)) t << "Binary TOC=YES\n";
489     if (Config_getBool(GENERATE_CHI)) t << "Create CHI file=YES\n";
490     t << "Title=" << recode(Config_getString(PROJECT_NAME)) << endl << endl;
491     
492     t << "[WINDOWS]" << endl;
493
494     // NOTE: the 0x10387e number is a set of bits specifying the buttons
495     //       which should appear in the CHM viewer; that specific value
496     //       means "show all buttons including the font-size one";
497     //       the font-size one is not normally settable by the HTML Help Workshop
498     //       utility but the way to set it is described here:
499     //          http://support.microsoft.com/?scid=kb%3Ben-us%3B240062&x=17&y=18
500     // NOTE: the 0x70387e number in addition to the above the Next and Prev button
501     //       are shown. They can only be shown in case of a binary toc.
502     //          dee http://www.mif2go.com/xhtml/htmlhelp_0016_943addingtabsandtoolbarbuttonstohtmlhelp.htm#Rz108x95873
503     //       Value has been taken from htmlhelp.h file of the HTML Help Workshop
504     if (Config_getBool(BINARY_TOC))
505     {
506       t << "main=\"" << recode(Config_getString(PROJECT_NAME)) << "\",\"index.hhc\","
507          "\"index.hhk\",\"" << indexName << "\",\"" << 
508          indexName << "\",,,,,0x23520,,0x70387e,,,,,,,,0" << endl << endl;
509     }
510     else
511     {
512       t << "main=\"" << recode(Config_getString(PROJECT_NAME)) << "\",\"index.hhc\","
513          "\"index.hhk\",\"" << indexName << "\",\"" << 
514          indexName << "\",,,,,0x23520,,0x10387e,,,,,,,,0" << endl << endl;
515     }
516     
517     t << "[FILES]" << endl;
518     char *s = indexFiles.first();
519     while (s)
520     {
521       t << s << endl;
522       s = indexFiles.next();
523     }
524     uint i;
525     for (i=0;i<imageFiles.count();i++)
526     {
527       t << imageFiles.at(i) << endl;
528     }
529     f.close();
530   }
531   else
532   {
533     err("Could not open file %s for writing\n",fName.data());
534   }
535 }
536
537 void HtmlHelp::addIndexFile(const char *s)
538 {
539   if (indexFileDict.find(s)==0)
540   {
541     indexFiles.append(s);
542     indexFileDict.insert(s,(void *)0x8);
543   }
544 }
545
546 /*! Finalizes the HTML help. This will finish and close the
547  *  contents file (index.hhc) and the index file (index.hhk).
548  *  \sa initialize()
549  */
550 void HtmlHelp::finalize()
551 {
552   // end the contents file
553   cts << "</UL>\n";
554   cts << "</BODY>\n";
555   cts << "</HTML>\n";
556   cts.unsetDevice();
557   cf->close();
558   delete cf;
559   
560   index->writeFields(kts);
561   
562   // end the index file
563   kts << "</UL>\n";
564   kts << "</BODY>\n";
565   kts << "</HTML>\n";
566   kts.unsetDevice();
567   kf->close();
568   delete kf;
569
570   createProjectFile();
571   s_languageDict.clear();
572 }
573
574 /*! Increase the level of the contents hierarchy. 
575  *  This will start a new unnumbered HTML list in contents file.
576  *  \sa decContentsDepth()
577  */
578 void HtmlHelp::incContentsDepth()
579 {
580   int i; for (i=0;i<dc+1;i++) cts << "  ";
581   cts << "<UL>\n";
582   ++dc;
583 }
584
585 /*! Decrease the level of the contents hierarchy.
586  *  This will end the unnumber HTML list.
587  *  \sa incContentsDepth()
588  */
589 void HtmlHelp::decContentsDepth()
590 {
591   int i; for (i=0;i<dc;i++) cts << "  ";
592   cts << "</UL>\n";
593   --dc;
594 }
595
596 QCString HtmlHelp::recode(const QCString &s) 
597 {
598   int iSize        = s.length();
599   int oSize        = iSize*4+1;
600   QCString output(oSize);
601   size_t iLeft     = iSize;
602   size_t oLeft     = oSize;
603   char *iPtr       = s.rawData();
604   char *oPtr       = output.rawData();
605   if (!portable_iconv(m_fromUtf8,&iPtr,&iLeft,&oPtr,&oLeft))
606   {
607     oSize -= (int)oLeft;
608     output.resize(oSize+1);
609     output.at(oSize)='\0';
610     return output;
611   }
612   else
613   {
614     return s;
615   }
616 }
617
618 /*! Add an list item to the contents file.
619  *  \param isDir boolean indicating if this is a dir or file entry
620  *  \param name the name of the item.
621  *  \param ref  the URL of to the item.
622  *  \param file the file in which the item is defined.
623  *  \param anchor the anchor of the item.
624  *  \param separateIndex not used.
625  *  \param addToNavIndex not used.
626  *  \param def not used.
627  */
628 void HtmlHelp::addContentsItem(bool isDir,
629                                const char *name,
630                                const char * /*ref*/, 
631                                const char *file,
632                                const char *anchor,
633                                bool /* separateIndex */,
634                                bool /* addToNavIndex */,
635                                Definition * /* def */)
636 {
637   // If we're using a binary toc then folders cannot have links. 
638   // Tried this and I didn't see any problems, when not using
639   // the resetting of file and anchor the TOC works better
640   // (prev / next button)
641   //if(Config_getBool(BINARY_TOC) && isDir) 
642   //{
643     //file = 0;
644     //anchor = 0;
645   //}
646   int i; for (i=0;i<dc;i++) cts << "  ";
647   cts << "<LI><OBJECT type=\"text/sitemap\">";
648   cts << "<param name=\"Name\" value=\"" << convertToHtml(recode(name),TRUE) << "\">";
649   if (file)      // made file optional param - KPW
650   {
651     if (file && (file[0]=='!' || file[0]=='^')) // special markers for user defined URLs
652     {
653       cts << "<param name=\"";
654       if (file[0]=='^') cts << "URL"; else cts << "Local";
655       cts << "\" value=\"";
656       cts << &file[1];
657     }
658     else
659     {
660       cts << "<param name=\"Local\" value=\"";
661       cts << file << Doxygen::htmlFileExtension;
662       if (anchor) cts << "#" << anchor;  
663     }
664     cts << "\">";
665   }
666   cts << "<param name=\"ImageNumber\" value=\"";
667   if (isDir)  // added - KPW
668   {
669     cts << (int)BOOK_CLOSED ;
670   }
671   else
672   {
673     cts << (int)TEXT;
674   }
675   cts << "\">";
676   cts << "</OBJECT>\n";
677 }
678
679
680 void HtmlHelp::addIndexItem(Definition *context,MemberDef *md,
681                             const char *sectionAnchor,const char *word)
682 {
683   if (md)
684   {
685     static bool separateMemberPages = Config_getBool(SEPARATE_MEMBER_PAGES);
686     if (context==0) // global member
687     {
688       if (md->getGroupDef())
689         context = md->getGroupDef();
690       else if (md->getFileDef())
691         context = md->getFileDef();
692     }
693     if (context==0) return; // should not happen
694
695     QCString cfname  = md->getOutputFileBase();
696     QCString cfiname = context->getOutputFileBase();
697     QCString level1  = context->name();
698     QCString level2  = md->name();
699     QCString contRef = separateMemberPages ? cfname : cfiname;
700     QCString memRef  = cfname;
701     QCString anchor  = sectionAnchor ? QCString(sectionAnchor) : md->anchor();
702     index->addItem(level1,level2,contRef,anchor,TRUE,FALSE);
703     index->addItem(level2,level1,memRef,anchor,TRUE,TRUE);
704   }
705   else if (context)
706   {
707     QCString level1  = word ? QCString(word) : context->name();
708     index->addItem(level1,0,context->getOutputFileBase(),sectionAnchor,TRUE,FALSE);
709   }
710 }
711
712 void HtmlHelp::addImageFile(const char *fileName)
713 {
714   if (!imageFiles.contains(fileName)) imageFiles.append(fileName);
715 }
716