00001 /****************************************************************************** 00002 * 00003 * $Id: htmlhelp.cpp,v 1.8 2001/03/19 19:27:40 root Exp $ 00004 * 00005 * Copyright (C) 1997-2008 by Dimitri van Heesch. 00006 * 00007 * Permission to use, copy, modify, and distribute this software and its 00008 * documentation under the terms of the GNU General Public License is hereby 00009 * granted. No representations are made about the suitability of this software 00010 * for any purpose. It is provided "as is" without express or implied warranty. 00011 * See the GNU General Public License for more details. 00012 * 00013 * Documents produced by Doxygen are derivative works derived from the 00014 * input used in their production; they are not affected by this license. 00015 * 00016 * The code is this file is largely based on a contribution from 00017 * Harm van der Heijden <H.v.d.Heijden@phys.tue.nl> 00018 * Please send thanks to him and bug reports to me :-) 00019 */ 00020 00021 #include <stdio.h> 00022 #include <stdlib.h> 00023 #include <qlist.h> 00024 #include <qdict.h> 00025 00026 #include "htmlhelp.h" 00027 #include "config.h" 00028 #include "message.h" 00029 #include "doxygen.h" 00030 #include "language.h" 00031 00032 //---------------------------------------------------------------------------- 00033 00034 struct IndexField 00035 { 00036 QCString name; 00037 QCString url; 00038 QCString anchor; 00039 bool link; 00040 bool reversed; 00041 }; 00042 00043 class IndexFieldList : public QList<IndexField> 00044 { 00045 public: 00046 int compareItems(GCI item1, GCI item2) 00047 { 00048 return stricmp(((IndexField *)item1)->name,((IndexField *)item2)->name); 00049 } 00050 ~IndexFieldList() {} 00051 }; 00052 00053 class IndexFieldListIterator : public QListIterator<IndexField> 00054 { 00055 public: 00056 IndexFieldListIterator( const IndexFieldList &list) : 00057 QListIterator<IndexField>(list) {} 00058 }; 00059 00060 class IndexFieldDict : public QDict<IndexField> 00061 { 00062 public: 00063 IndexFieldDict(int size) : QDict<IndexField>(size) {} 00064 ~IndexFieldDict() {} 00065 }; 00066 00070 class HtmlHelpIndex 00071 { 00072 public: 00073 HtmlHelpIndex(); 00074 ~HtmlHelpIndex(); 00075 void addItem(const char *first,const char *second, 00076 const char *url, const char *anchor, 00077 bool hasLink,bool reversed); 00078 void writeFields(QTextStream &t); 00079 private: 00080 IndexFieldList *list; 00081 IndexFieldDict *dict; 00082 }; 00083 00085 HtmlHelpIndex::HtmlHelpIndex() 00086 { 00087 list = new IndexFieldList; 00088 dict = new IndexFieldDict(10007); 00089 list->setAutoDelete(TRUE); 00090 } 00091 00093 HtmlHelpIndex::~HtmlHelpIndex() 00094 { 00095 delete list; 00096 delete dict; 00097 } 00098 00112 void HtmlHelpIndex::addItem(const char *level1,const char *level2, 00113 const char *url,const char *anchor,bool hasLink, 00114 bool reversed) 00115 { 00116 QCString key = level1; 00117 if (level2) key+= (QCString)"?" + level2; 00118 if (dict->find(key)==0) // new key 00119 { 00120 //printf(">>>>>>>>> HtmlHelpIndex::addItem(%s,%s,%s,%s)\n", 00121 // level1,level2,url,anchor); 00122 IndexField *f = new IndexField; 00123 f->name = key; 00124 f->url = url; 00125 f->anchor = anchor; 00126 f->link = hasLink; 00127 f->reversed = reversed; 00128 list->inSort(f); 00129 dict->insert(key,f); 00130 } 00131 } 00132 00159 void HtmlHelpIndex::writeFields(QTextStream &t) 00160 { 00161 IndexFieldListIterator ifli(*list); 00162 IndexField *f; 00163 QCString lastLevel1; 00164 bool level2Started=FALSE; 00165 for (;(f=ifli.current());++ifli) 00166 { 00167 QCString level1,level2; 00168 int i; 00169 if ((i=f->name.find('?'))!=-1) 00170 { 00171 level1 = f->name.left(i); 00172 level2 = f->name.right(f->name.length()-i-1); 00173 } 00174 else 00175 { 00176 level1 = f->name.copy(); 00177 } 00178 00179 if (level1!=lastLevel1) 00180 { // finish old list at level 2 00181 if (level2Started) t << " </UL>" << endl; 00182 level2Started=FALSE; 00183 00184 // <Antony> 00185 // Added this code so that an item with only one subitem is written 00186 // without any subitem. 00187 // For example: 00188 // a1, b1 -> will create only a1, not separate subitem for b1 00189 // a2, b2 00190 // a2, b3 00191 QCString nextLevel1; 00192 IndexField* fnext = ++ifli; 00193 if (fnext) 00194 { 00195 nextLevel1 = fnext->name.left(fnext->name.find('?')); 00196 --ifli; 00197 } 00198 if (level1 != nextLevel1) 00199 { 00200 level2 = ""; 00201 } 00202 // </Antony> 00203 00204 if (level2.isEmpty()) 00205 { 00206 t << " <LI><OBJECT type=\"text/sitemap\">"; 00207 t << "<param name=\"Local\" value=\"" << f->url << Doxygen::htmlFileExtension; 00208 if (!f->anchor.isEmpty() && f->reversed) t << "#" << f->anchor; 00209 t << "\">"; 00210 t << "<param name=\"Name\" value=\"" << level1 << "\">" 00211 "</OBJECT>\n"; 00212 } 00213 else 00214 { 00215 if (f->link) 00216 { 00217 t << " <LI><OBJECT type=\"text/sitemap\">"; 00218 t << "<param name=\"Local\" value=\"" << f->url << Doxygen::htmlFileExtension; 00219 if (!f->anchor.isEmpty() && f->reversed) t << "#" << f->anchor; 00220 t << "\">"; 00221 t << "<param name=\"Name\" value=\"" << level1 << "\">" 00222 "</OBJECT>\n"; 00223 } 00224 else 00225 { 00226 t << " <LI><OBJECT type=\"text/sitemap\">"; 00227 t << "<param name=\"See Also\" value=\"" << level1 << "\">"; 00228 t << "<param name=\"Name\" value=\"" << level1 << "\">" 00229 "</OBJECT>\n"; 00230 } 00231 } 00232 } 00233 if (!level2Started && !level2.isEmpty()) 00234 { // start new list at level 2 00235 t << " <UL>" << endl; 00236 level2Started=TRUE; 00237 } 00238 else if (level2Started && level2.isEmpty()) 00239 { // end list at level 2 00240 t << " </UL>" << endl; 00241 level2Started=FALSE; 00242 } 00243 if (level2Started) 00244 { 00245 t << " <LI><OBJECT type=\"text/sitemap\">"; 00246 t << "<param name=\"Local\" value=\"" << f->url << Doxygen::htmlFileExtension; 00247 if (!f->anchor.isEmpty()) t << "#" << f->anchor; 00248 t << "\">"; 00249 t << "<param name=\"Name\" value=\"" << level2 << "\">" 00250 "</OBJECT>\n"; 00251 } 00252 lastLevel1 = level1.copy(); 00253 } 00254 if (level2Started) t << " </UL>" << endl; 00255 } 00256 00257 //---------------------------------------------------------------------------- 00258 00259 HtmlHelp *HtmlHelp::theInstance = 0; 00260 00265 HtmlHelp::HtmlHelp() : indexFileDict(1009) 00266 { 00267 /* initial depth */ 00268 dc = 0; 00269 cf = kf = 0; 00270 index = new HtmlHelpIndex; 00271 } 00272 00273 #if 0 00274 00276 HtmlHelp *HtmlHelp::getInstance() 00277 { 00278 if (theInstance==0) theInstance = new HtmlHelp; 00279 return theInstance; 00280 } 00281 #endif 00282 00283 static QDict<QCString> s_languageDict; 00284 00290 void HtmlHelp::initialize() 00291 { 00292 /* open the contents file */ 00293 QCString fName = Config_getString("HTML_OUTPUT") + "/index.hhc"; 00294 cf = new QFile(fName); 00295 if (!cf->open(IO_WriteOnly)) 00296 { 00297 err("Could not open file %s for writing\n",fName.data()); 00298 exit(1); 00299 } 00300 /* Write the header of the contents file */ 00301 cts.setDevice(cf); 00302 cts.setEncoding(QTextStream::UnicodeUTF8); 00303 cts << "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n" 00304 "<HTML><HEAD></HEAD><BODY>\n" 00305 "<OBJECT type=\"text/site properties\">\n" 00306 "<param name=\"FrameName\" value=\"right\">\n" 00307 "</OBJECT>\n" 00308 "<UL>\n"; 00309 00310 /* open the contents file */ 00311 fName = Config_getString("HTML_OUTPUT") + "/index.hhk"; 00312 kf = new QFile(fName); 00313 if (!kf->open(IO_WriteOnly)) 00314 { 00315 err("Could not open file %s for writing\n",fName.data()); 00316 exit(1); 00317 } 00318 /* Write the header of the contents file */ 00319 kts.setDevice(kf); 00320 kts.setEncoding(QTextStream::UnicodeUTF8); 00321 kts << "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n" 00322 "<HTML><HEAD></HEAD><BODY>\n" 00323 "<OBJECT type=\"text/site properties\">\n" 00324 "<param name=\"FrameName\" value=\"right\">\n" 00325 "</OBJECT>\n" 00326 "<UL>\n"; 00327 00328 /* language codes for Html help 00329 0x405 Czech 00330 0x406 Danish 00331 0x413 Dutch 00332 0xC09 English (Australia) 00333 0x809 English (Britain) 00334 0x1009 English (Canada) 00335 0x1809 English (Ireland) 00336 0x1409 English (New Zealand) 00337 0x1C09 English (South Africa) 00338 0x409 English (United States) 00339 0x40B Finnish 00340 0x40C French 00341 0x407 German 00342 0x408 Greece 00343 0x40E Hungarian 00344 0x410 Italian 00345 0x814 Norwegian 00346 0x415 Polish 00347 0x816 Portuguese(Portugal) 00348 0x416 Portuguese(Brazil) 00349 0x419 Russian 00350 0x80A Spanish(Mexico) 00351 0xC0A Spanish(Modern Sort) 00352 0x40A Spanish(Traditional Sort) 00353 0x41D Swedish 00354 0x41F Turkey 00355 0x411 Japanese 00356 0x412 Korean 00357 0x804 Chinese (PRC) 00358 0x404 Chinese (Taiwan) 00359 */ 00360 s_languageDict.setAutoDelete(TRUE); 00361 s_languageDict.clear(); 00362 s_languageDict.insert("czech", new QCString("0x405 Czech")); 00363 s_languageDict.insert("danish", new QCString("0x406 Danish")); 00364 s_languageDict.insert("dutch", new QCString("0x413 Dutch")); 00365 s_languageDict.insert("finnish", new QCString("0x40B Finnish")); 00366 s_languageDict.insert("french", new QCString("0x40C French")); 00367 s_languageDict.insert("german", new QCString("0x407 German")); 00368 s_languageDict.insert("greece", new QCString("0x408 Greece")); 00369 s_languageDict.insert("hungarian", new QCString("0x40E Hungarian")); 00370 s_languageDict.insert("italian", new QCString("0x410 Italian")); 00371 s_languageDict.insert("norwegian", new QCString("0x814 Norwegian")); 00372 s_languageDict.insert("polish", new QCString("0x415 Polish")); 00373 s_languageDict.insert("portugese", new QCString("0x816 Portuguese(Portugal)")); 00374 s_languageDict.insert("brazil", new QCString("0x416 Portuguese(Brazil)")); 00375 s_languageDict.insert("russian", new QCString("0x419 Russian")); 00376 s_languageDict.insert("spanish", new QCString("0x40A Spannish(Traditional Sort)")); 00377 s_languageDict.insert("swedish", new QCString("0x41D Swedish")); 00378 s_languageDict.insert("turkey", new QCString("0x41F Turkey")); 00379 s_languageDict.insert("japanese", new QCString("0x411 Japanese")); 00380 s_languageDict.insert("japanese-en", new QCString("0x411 Japanese")); 00381 s_languageDict.insert("korean", new QCString("0x412 Korean")); 00382 s_languageDict.insert("chinese", new QCString("0x804 Chinese (PRC)")); 00383 s_languageDict.insert("chinese-traditional", new QCString("0x404 Chinese (Taiwan)")); 00384 } 00385 00386 00387 static QCString getLanguageString() 00388 { 00389 if (!theTranslator->idLanguage().isEmpty()) 00390 { 00391 QCString *s = s_languageDict[theTranslator->idLanguage()]; 00392 if (s) 00393 { 00394 return *s; 00395 } 00396 } 00397 // default language 00398 return "0x409 English (United States)"; 00399 } 00400 00401 00402 00403 void HtmlHelp::createProjectFile() 00404 { 00405 /* Write the project file */ 00406 QCString fName = Config_getString("HTML_OUTPUT") + "/index.hhp"; 00407 QFile f(fName); 00408 if (f.open(IO_WriteOnly)) 00409 { 00410 QTextStream t(&f); 00411 #if QT_VERSION >= 200 00412 t.setEncoding(QTextStream::UnicodeUTF8); 00413 #endif 00414 00415 00416 00417 QCString indexName="index"+Doxygen::htmlFileExtension; 00418 if (Config_getBool("GENERATE_TREEVIEW")) indexName="main"+Doxygen::htmlFileExtension; 00419 t << "[OPTIONS]\n"; 00420 if (!Config_getString("CHM_FILE").isEmpty()) 00421 { 00422 t << "Compiled file=" << Config_getString("CHM_FILE") << "\n"; 00423 } 00424 t << "Compatibility=1.1\n" 00425 "Full-text search=Yes\n" 00426 "Contents file=index.hhc\n" 00427 "Default Window=main\n" 00428 "Default topic=" << indexName << "\n" 00429 "Index file=index.hhk\n" 00430 "Language=" << getLanguageString() << endl; 00431 if (Config_getBool("BINARY_TOC")) t << "Binary TOC=YES\n"; 00432 if (Config_getBool("GENERATE_CHI")) t << "Create CHI file=YES\n"; 00433 t << "Title=" << Config_getString("PROJECT_NAME") << endl << endl; 00434 00435 t << "[WINDOWS]" << endl; 00436 t << "main=\"" << Config_getString("PROJECT_NAME") << "\",\"index.hhc\"," 00437 "\"index.hhk\",\"" << indexName << "\",\"" << 00438 indexName << "\",,,,,0x23520,,0x387e,,,,,,,,0" << endl << endl; 00439 00440 t << "[FILES]" << endl; 00441 char *s = indexFiles.first(); 00442 while (s) 00443 { 00444 t << s << endl; 00445 s = indexFiles.next(); 00446 } 00447 t << "tabs.css" << endl; 00448 t << "tab_b.gif" << endl; 00449 t << "tab_l.gif" << endl; 00450 t << "tab_r.gif" << endl; 00451 if (Config_getBool("HTML_DYNAMIC_SECTIONS")) 00452 { 00453 t << "open.gif" << endl; 00454 t << "closed.gif" << endl; 00455 } 00456 f.close(); 00457 } 00458 else 00459 { 00460 err("Could not open file %s for writing\n",fName.data()); 00461 } 00462 } 00463 00464 void HtmlHelp::addIndexFile(const char *s) 00465 { 00466 if (indexFileDict.find(s)==0) 00467 { 00468 indexFiles.append(s); 00469 indexFileDict.insert(s,(void *)0x8); 00470 } 00471 } 00472 00477 void HtmlHelp::finalize() 00478 { 00479 // end the contents file 00480 cts << "</UL>\n"; 00481 cts << "</BODY>\n"; 00482 cts << "</HTML>\n"; 00483 cts.unsetDevice(); 00484 cf->close(); 00485 delete cf; 00486 00487 index->writeFields(kts); 00488 00489 // end the index file 00490 kts << "</UL>\n"; 00491 kts << "</BODY>\n"; 00492 kts << "</HTML>\n"; 00493 kts.unsetDevice(); 00494 kf->close(); 00495 delete kf; 00496 00497 createProjectFile(); 00498 s_languageDict.clear(); 00499 } 00500 00505 void HtmlHelp::incContentsDepth() 00506 { 00507 int i; for (i=0;i<dc+1;i++) cts << " "; 00508 cts << "<UL>\n"; 00509 ++dc; 00510 } 00511 00516 void HtmlHelp::decContentsDepth() 00517 { 00518 int i; for (i=0;i<dc;i++) cts << " "; 00519 cts << "</UL>\n"; 00520 --dc; 00521 } 00522 00530 void HtmlHelp::addContentsItem(bool isDir, 00531 const char *name, 00532 const char * /*ref*/, 00533 const char *file, 00534 const char *anchor) 00535 { 00536 // If we're using a binary toc then folders cannot have links. 00537 if(Config_getBool("BINARY_TOC") && isDir) 00538 { 00539 file = 0; 00540 anchor = 0; 00541 } 00542 00543 int i; for (i=0;i<dc;i++) cts << " "; 00544 cts << "<LI><OBJECT type=\"text/sitemap\">"; 00545 cts << "<param name=\"Name\" value=\"" << name << "\">"; 00546 if (file) // made file optional param - KPW 00547 { 00548 cts << "<param name=\"Local\" value=\"" << file << Doxygen::htmlFileExtension; 00549 if (anchor) cts << "#" << anchor; 00550 cts << "\">"; 00551 } 00552 cts << "<param name=\"ImageNumber\" value=\""; 00553 if (isDir) // added - KPW 00554 { 00555 cts << (int)BOOK_CLOSED ; 00556 } 00557 else 00558 { 00559 cts << (int)TEXT; 00560 } 00561 cts << "\">"; 00562 cts << "</OBJECT>\n"; 00563 } 00564 00574 void HtmlHelp::addIndexItem(const char *level1, const char *level2, 00575 const char *contRef, const char *memRef, 00576 const char *anchor,const MemberDef *md) 00577 { 00578 (void)md; 00579 index->addItem(level1,level2,contRef,anchor,TRUE,FALSE); 00580 index->addItem(level2,level1,memRef,anchor,TRUE,TRUE); 00581 } 00582