Orb/Doxygen/src/htmlhelp.cpp
changeset 0 42188c7ea2d9
child 4 468f4c8d3d5b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orb/Doxygen/src/htmlhelp.cpp	Thu Jan 21 17:29:01 2010 +0000
@@ -0,0 +1,679 @@
+/******************************************************************************
+ *
+ * 
+ *
+ * Copyright (C) 1997-2008 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.
+ *
+ * The code is this file is largely based on a contribution from
+ * Harm van der Heijden <H.v.d.Heijden@phys.tue.nl>
+ * Please send thanks to him and bug reports to me :-)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <qlist.h>
+#include <qdict.h>
+#include <qregexp.h>
+#include "qtextcodec.h"
+
+#include "htmlhelp.h"
+#include "config.h"
+#include "message.h"
+#include "doxygen.h"
+#include "language.h"
+#include "portable.h"
+
+//----------------------------------------------------------------------------
+
+struct IndexField
+{
+  QCString name;
+  QCString url;
+  QCString anchor;
+  bool     link;
+  bool     reversed;
+};
+
+class IndexFieldList : public QList<IndexField>
+{
+  public:
+    int compareItems(GCI item1, GCI item2)
+    {
+      return stricmp(((IndexField *)item1)->name,((IndexField *)item2)->name);
+    }
+   ~IndexFieldList() {}
+};
+
+class IndexFieldListIterator : public QListIterator<IndexField>
+{
+  public:
+    IndexFieldListIterator( const IndexFieldList &list) :
+       QListIterator<IndexField>(list) {}
+};
+
+class IndexFieldDict : public QDict<IndexField>
+{
+  public:
+    IndexFieldDict(int size) : QDict<IndexField>(size) {}
+   ~IndexFieldDict() {}
+};
+
+/*! A helper class for HtmlHelp that manages a two level index in 
+ * alphabetical order 
+ */
+class HtmlHelpIndex
+{
+  public:
+    HtmlHelpIndex();
+   ~HtmlHelpIndex();
+    void addItem(const char *first,const char *second, 
+                 const char *url, const char *anchor,
+                 bool hasLink,bool reversed);
+    void writeFields(QTextStream &t);
+  private:
+    IndexFieldList *list;
+    IndexFieldDict *dict;   
+};
+
+/*! Constructs a new HtmlHelp index */
+HtmlHelpIndex::HtmlHelpIndex()
+{
+  list = new IndexFieldList;
+  dict = new IndexFieldDict(10007);
+  list->setAutoDelete(TRUE);
+}
+
+/*! Destroys the HtmlHelp index */
+HtmlHelpIndex::~HtmlHelpIndex()
+{
+  delete list;
+  delete dict;
+}
+
+/*! Stores an item in the index if it is not already present. 
+ *  Items are stored in alphetical order, by sorting on the
+ *  concatenation of \a level1 and \a level2 (if present).
+ *
+ *  \param level1 the string at level 1 in the index.
+ *  \param level2 the string at level 2 in the index (or 0 if not applicable).
+ *  \param url the url of the documentation (without .html extension).
+ *  \param anchor the anchor of the documentation within the page.
+ *  \param hasLink if true, the url (without anchor) can be used in the 
+ *         level1 item, when writing the header of a list of level2 items.
+ *  \param reversed TRUE if level1 is the member name and level2 the compound
+ *         name.
+ */
+void HtmlHelpIndex::addItem(const char *level1,const char *level2,
+                       const char *url,const char *anchor,bool hasLink,
+                       bool reversed)
+{
+  QCString key = level1; 
+  if (level2) key+= (QCString)"?" + level2;
+  if (key.find(QRegExp("@[0-9]+"))!=-1) // skip anonymous stuff
+  {
+    return;
+  }
+  if (dict->find(key)==0) // new key
+  {
+    //printf(">>>>>>>>> HtmlHelpIndex::addItem(%s,%s,%s,%s)\n",
+    //      level1,level2,url,anchor);
+    IndexField *f = new IndexField;
+    f->name     = key;
+    f->url      = url;
+    f->anchor   = anchor;
+    f->link     = hasLink;
+    f->reversed = reversed;
+    list->inSort(f);
+    dict->insert(key,f);
+  }
+}
+
+/*! Writes the sorted list of index items into a html like list.
+ *
+ *  An list of calls with <code>name = level1,level2</code> as follows:
+ *  <pre>
+ *    a1,b1
+ *    a1,b2
+ *    a2,b1
+ *    a2,b2
+ *    a3
+ *    a4,b1
+ *  </pre>
+ *
+ *  Will result in the following list:
+ *
+ *  <pre>
+ *    a1       -> link to url if hasLink==TRUE
+ *      b1     -> link to url#anchor
+ *      b2     -> link to url#anchor
+ *    a2       -> link to url if hasLink==TRUE
+ *      b1     -> link to url#anchor
+ *      b2     -> link to url#anchor
+ *    a3       -> link to url if hasLink==TRUE
+ *    a4       -> link to url if hasLink==TRUE
+ *      b1     -> link to url#anchor 
+ *  </pre>
+ */
+void HtmlHelpIndex::writeFields(QTextStream &t)
+{
+  IndexFieldListIterator ifli(*list);
+  IndexField *f;
+  QCString lastLevel1;
+  bool level2Started=FALSE;
+  for (;(f=ifli.current());++ifli)
+  {
+    QCString level1,level2;
+    int i;
+    if ((i=f->name.find('?'))!=-1)
+    {
+      level1 = f->name.left(i);
+      level2 = f->name.right(f->name.length()-i-1); 
+    }
+    else
+    {
+      level1  = f->name.copy();
+    }
+
+    if (level1!=lastLevel1)
+    { // finish old list at level 2
+      if (level2Started) t << "  </UL>" << endl;
+      level2Started=FALSE;
+    
+      // <Antony>
+      // Added this code so that an item with only one subitem is written
+      // without any subitem.
+      // For example:
+      //   a1, b1 -> will create only a1, not separate subitem for b1
+      //   a2, b2
+      //   a2, b3
+      QCString nextLevel1;
+      IndexField* fnext = ++ifli;
+      if (fnext)
+      {
+        nextLevel1 = fnext->name.left(fnext->name.find('?'));
+        --ifli;
+      }
+      if (level1 != nextLevel1)
+      {
+        level2 = "";
+      }
+      // </Antony>
+
+      if (level2.isEmpty())
+      {
+        t << "  <LI><OBJECT type=\"text/sitemap\">";
+        t << "<param name=\"Local\" value=\"" << f->url << Doxygen::htmlFileExtension;
+        if (!f->anchor.isEmpty() && f->reversed) t << "#" << f->anchor;  
+        t << "\">";
+        t << "<param name=\"Name\" value=\"" << level1 << "\">"
+           "</OBJECT>\n";
+      }
+      else
+      {
+        if (f->link)
+        {
+          t << "  <LI><OBJECT type=\"text/sitemap\">";
+          t << "<param name=\"Local\" value=\"" << f->url << Doxygen::htmlFileExtension;
+          if (!f->anchor.isEmpty() && f->reversed) t << "#" << f->anchor;  
+          t << "\">";
+          t << "<param name=\"Name\" value=\"" << level1 << "\">"
+               "</OBJECT>\n";
+        }
+        else
+        {
+          t << "  <LI><OBJECT type=\"text/sitemap\">";
+          t << "<param name=\"See Also\" value=\"" << level1 << "\">";
+          t << "<param name=\"Name\" value=\"" << level1 << "\">"
+               "</OBJECT>\n";
+        }
+      }
+    }
+    if (!level2Started && !level2.isEmpty())
+    { // start new list at level 2
+      t << "  <UL>" << endl;
+      level2Started=TRUE;
+    }
+    else if (level2Started && level2.isEmpty())
+    { // end list at level 2
+      t << "  </UL>" << endl;
+      level2Started=FALSE;
+    }
+    if (level2Started)
+    {
+      t << "    <LI><OBJECT type=\"text/sitemap\">";
+      t << "<param name=\"Local\" value=\"" << f->url << Doxygen::htmlFileExtension;
+      if (!f->anchor.isEmpty()) t << "#" << f->anchor;  
+      t << "\">";
+      t << "<param name=\"Name\" value=\"" << level2 << "\">"
+         "</OBJECT>\n";
+    }
+    lastLevel1 = level1.copy();
+  } 
+  if (level2Started) t << "  </UL>" << endl;
+}
+
+//----------------------------------------------------------------------------
+
+HtmlHelp *HtmlHelp::theInstance = 0;
+
+/*! Constructs an html object. 
+ *  The object has to be \link initialize() initialized\endlink before it can 
+ *  be used.
+ */
+HtmlHelp::HtmlHelp() : indexFileDict(1009)
+{
+  /* initial depth */
+  dc = 0;
+  cf = kf = 0;
+  index = new HtmlHelpIndex;
+  m_fromUtf8 = (void *)(-1);
+}
+
+HtmlHelp::~HtmlHelp()
+{
+  if (m_fromUtf8!=(void *)(-1))   portable_iconv_close(m_fromUtf8);
+}
+#if 0
+/*! return a reference to the one and only instance of this class. 
+ */
+HtmlHelp *HtmlHelp::getInstance()
+{
+  if (theInstance==0) theInstance = new HtmlHelp;
+  return theInstance;
+}
+#endif
+
+static QDict<QCString> s_languageDict;
+
+/*! This will create a contents file (index.hhc) and a index file (index.hhk)
+ *  and write the header of those files. 
+ *  It also creates a project file (index.hhp)
+ *  \sa finalize()
+ */
+void HtmlHelp::initialize()
+{
+  const char *str = Config_getString("CHM_INDEX_ENCODING");
+  if (!str) str = "CP1250"; // use safe and likely default
+  m_fromUtf8 = portable_iconv_open(str,"UTF-8"); 
+  if (m_fromUtf8==(void *)(-1))
+  {
+    err("Error: unsupported character conversion for CHM_INDEX_ENCODING: '%s'->'UTF-8'\n", str);
+    exit(1);
+  }
+
+  /* open the contents file */
+  QCString fName = Config_getString("HTML_OUTPUT") + "/index.hhc";
+  cf = new QFile(fName);
+  if (!cf->open(IO_WriteOnly))
+  {
+    err("Could not open file %s for writing\n",fName.data());
+    exit(1);
+  }
+  /* Write the header of the contents file */
+  cts.setDevice(cf);
+  cts.setEncoding(QTextStream::Latin1);
+  cts << "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n"
+         "<HTML><HEAD></HEAD><BODY>\n"
+         "<OBJECT type=\"text/site properties\">\n"
+         "<param name=\"FrameName\" value=\"right\">\n"
+         "</OBJECT>\n"
+         "<UL>\n";
+  
+  /* open the contents file */
+  fName = Config_getString("HTML_OUTPUT") + "/index.hhk";
+  kf = new QFile(fName);
+  if (!kf->open(IO_WriteOnly))
+  {
+    err("Could not open file %s for writing\n",fName.data());
+    exit(1);
+  }
+  /* Write the header of the contents file */
+  kts.setDevice(kf);
+  kts.setEncoding(QTextStream::Latin1);
+  kts << "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n"
+         "<HTML><HEAD></HEAD><BODY>\n"
+         "<OBJECT type=\"text/site properties\">\n"
+         "<param name=\"FrameName\" value=\"right\">\n"
+         "</OBJECT>\n"
+         "<UL>\n";
+
+  /* language codes for Html help
+     0x405 Czech
+     0x406 Danish
+     0x413 Dutch
+     0xC09 English (Australia)
+     0x809 English (Britain)
+     0x1009 English (Canada)
+     0x1809 English (Ireland)
+     0x1409 English (New Zealand)
+     0x1C09 English (South Africa)
+     0x409 English (United States)
+     0x40B Finnish
+     0x40C French
+     0x407 German
+     0x408 Greece
+     0x40E Hungarian
+     0x410 Italian
+     0x814 Norwegian
+     0x415 Polish
+     0x816 Portuguese(Portugal)
+     0x416 Portuguese(Brazil)
+     0x419 Russian
+     0x80A Spanish(Mexico)
+     0xC0A Spanish(Modern Sort)
+     0x40A Spanish(Traditional Sort)
+     0x41D Swedish
+     0x41F Turkey
+     0x411 Japanese
+     0x412 Korean
+     0x804 Chinese (PRC)
+     0x404 Chinese (Taiwan)
+
+     New LCIDs:
+	 0x421 Indonesian
+	 0x41A Croatian
+	 0x418 Romanian
+	 0x424 Slovenian
+	 0x41B Slovak
+	 0x422 Ukrainian
+	 0x81A Serbian (Serbia, Latin)
+	 0x403 Catalan
+	 0x427 Lithuanian
+	 0x436 Afrikaans
+	 0x42A Vietnamese
+	 0x429 Persian (Iran)
+	 0xC01 Arabic (Egypt) - I don't know which version of arabic is used inside translator_ar.h ,
+     so I have chosen Egypt at random
+
+  */
+  s_languageDict.setAutoDelete(TRUE);
+  s_languageDict.clear();
+  s_languageDict.insert("czech",       new QCString("0x405 Czech"));
+  s_languageDict.insert("danish",      new QCString("0x406 Danish"));
+  s_languageDict.insert("dutch",       new QCString("0x413 Dutch"));
+  s_languageDict.insert("finnish",     new QCString("0x40B Finnish"));
+  s_languageDict.insert("french",      new QCString("0x40C French"));
+  s_languageDict.insert("german",      new QCString("0x407 German"));
+  s_languageDict.insert("greek",       new QCString("0x408 Greece"));
+  s_languageDict.insert("hungarian",   new QCString("0x40E Hungarian"));
+  s_languageDict.insert("italian",     new QCString("0x410 Italian"));
+  s_languageDict.insert("norwegian",   new QCString("0x814 Norwegian"));
+  s_languageDict.insert("polish",      new QCString("0x415 Polish"));
+  s_languageDict.insert("portuguese",  new QCString("0x816 Portuguese(Portugal)"));
+  s_languageDict.insert("brazil",      new QCString("0x416 Portuguese(Brazil)"));
+  s_languageDict.insert("russian",     new QCString("0x419 Russian"));
+  s_languageDict.insert("spanish",     new QCString("0x40A Spanish(Traditional Sort)"));
+  s_languageDict.insert("swedish",     new QCString("0x41D Swedish"));
+  s_languageDict.insert("turkish",     new QCString("0x41F Turkey"));
+  s_languageDict.insert("japanese",    new QCString("0x411 Japanese"));
+  s_languageDict.insert("japanese-en", new QCString("0x411 Japanese"));
+  s_languageDict.insert("korean",      new QCString("0x412 Korean"));
+  s_languageDict.insert("korean-en",   new QCString("0x412 Korean"));
+  s_languageDict.insert("chinese",     new QCString("0x804 Chinese (PRC)"));
+  s_languageDict.insert("chinese-traditional", new QCString("0x404 Chinese (Taiwan)"));
+
+  // new LCIDs
+  s_languageDict.insert("indonesian",  new QCString("0x412 Indonesian"));
+  s_languageDict.insert("croatian",    new QCString("0x41A Croatian"));
+  s_languageDict.insert("romanian",    new QCString("0x418 Romanian"));
+  s_languageDict.insert("slovene",     new QCString("0x424 Slovenian"));
+  s_languageDict.insert("slovak",      new QCString("0x41B Slovak"));
+  s_languageDict.insert("ukrainian",   new QCString("0x422 Ukrainian"));
+  s_languageDict.insert("serbian",     new QCString("0x81A Serbian (Serbia, Latin)"));
+  s_languageDict.insert("catalan",     new QCString("0x403 Catalan"));
+  s_languageDict.insert("lithuanian",  new QCString("0x427 Lithuanian"));
+  s_languageDict.insert("afrikaans",   new QCString("0x436 Afrikaans"));
+  s_languageDict.insert("vietnamese",  new QCString("0x42A Vietnamese"));
+  s_languageDict.insert("persian",     new QCString("0x429 Persian (Iran)"));
+  s_languageDict.insert("arabic",      new QCString("0xC01 Arabic (Egypt)"));
+}
+
+
+static QCString getLanguageString()
+{
+  if (!theTranslator->idLanguage().isEmpty())
+  {
+    QCString *s = s_languageDict[theTranslator->idLanguage()];
+    if (s)
+    {
+      return *s;
+    }
+  }
+  // default language
+  return "0x409 English (United States)";
+}
+  
+
+
+void HtmlHelp::createProjectFile()
+{
+  /* Write the project file */
+  QCString fName = Config_getString("HTML_OUTPUT") + "/index.hhp";
+  QFile f(fName);
+  if (f.open(IO_WriteOnly))
+  {
+    QTextStream t(&f);
+#if QT_VERSION >= 200
+    t.setEncoding(QTextStream::Latin1);
+#endif
+
+   
+    
+    QCString indexName="index"+Doxygen::htmlFileExtension;
+    if (Config_getBool("GENERATE_TREEVIEW")) indexName="main"+Doxygen::htmlFileExtension;
+    t << "[OPTIONS]\n";
+    if (!Config_getString("CHM_FILE").isEmpty())
+    {
+      t << "Compiled file=" << Config_getString("CHM_FILE") << "\n";
+    }
+    t << "Compatibility=1.1\n"
+         "Full-text search=Yes\n"
+         "Contents file=index.hhc\n"
+         "Default Window=main\n"
+         "Default topic=" << indexName << "\n"
+         "Index file=index.hhk\n"
+         "Language=" << getLanguageString() << endl;
+    if (Config_getBool("BINARY_TOC")) t << "Binary TOC=YES\n";
+    if (Config_getBool("GENERATE_CHI")) t << "Create CHI file=YES\n";
+    t << "Title=" << recode(Config_getString("PROJECT_NAME")) << endl << endl;
+    
+    t << "[WINDOWS]" << endl;
+
+    // NOTE: the 0x10387e number is a set of bits specifying the buttons
+    //       which should appear in the CHM viewer; that specific value
+    //       means "show all buttons including the font-size one";
+    //       the font-size one is not normally settable by the HTML Help Workshop
+    //       utility but the way to set it is described here:
+    //          http://support.microsoft.com/?scid=kb%3Ben-us%3B240062&x=17&y=18
+    t << "main=\"" << recode(Config_getString("PROJECT_NAME")) << "\",\"index.hhc\","
+         "\"index.hhk\",\"" << indexName << "\",\"" << 
+         indexName << "\",,,,,0x23520,,0x10387e,,,,,,,,0" << endl << endl;
+    
+    t << "[FILES]" << endl;
+    char *s = indexFiles.first();
+    while (s)
+    {
+      t << s << endl;
+      s = indexFiles.next();
+    }
+    t << "tabs.css" << endl;
+    t << "tab_b.gif" << endl;
+    t << "tab_l.gif" << endl;
+    t << "tab_r.gif" << endl;
+    if (Config_getBool("HTML_DYNAMIC_SECTIONS"))
+    {
+      t << "open.gif" << endl;
+      t << "closed.gif" << endl;
+    }
+    f.close();
+  }
+  else
+  {
+    err("Could not open file %s for writing\n",fName.data());
+  }
+}
+
+void HtmlHelp::addIndexFile(const char *s)
+{
+  if (indexFileDict.find(s)==0)
+  {
+    indexFiles.append(s);
+    indexFileDict.insert(s,(void *)0x8);
+  }
+}
+
+/*! Finalizes the HTML help. This will finish and close the
+ *  contents file (index.hhc) and the index file (index.hhk).
+ *  \sa initialize()
+ */
+void HtmlHelp::finalize()
+{
+  // end the contents file
+  cts << "</UL>\n";
+  cts << "</BODY>\n";
+  cts << "</HTML>\n";
+  cts.unsetDevice();
+  cf->close();
+  delete cf;
+  
+  index->writeFields(kts);
+  
+  // end the index file
+  kts << "</UL>\n";
+  kts << "</BODY>\n";
+  kts << "</HTML>\n";
+  kts.unsetDevice();
+  kf->close();
+  delete kf;
+
+  createProjectFile();
+  s_languageDict.clear();
+}
+
+/*! Increase the level of the contents hierarchy. 
+ *  This will start a new unnumbered HTML list in contents file.
+ *  \sa decContentsDepth()
+ */
+void HtmlHelp::incContentsDepth()
+{
+  int i; for (i=0;i<dc+1;i++) cts << "  ";
+  cts << "<UL>\n";
+  ++dc;
+}
+
+/*! Decrease the level of the contents hierarchy.
+ *  This will end the unnumber HTML list.
+ *  \sa incContentsDepth()
+ */
+void HtmlHelp::decContentsDepth()
+{
+  int i; for (i=0;i<dc;i++) cts << "  ";
+  cts << "</UL>\n";
+  --dc;
+}
+
+QCString HtmlHelp::recode(const QCString &s) 
+{
+  int iSize        = s.length();
+  int oSize        = iSize*4+1;
+  QCString output(oSize);
+  size_t iLeft     = iSize;
+  size_t oLeft     = oSize;
+  const char *iPtr = s.data();
+  char *oPtr       = output.data();
+  if (!portable_iconv(m_fromUtf8,&iPtr,&iLeft,&oPtr,&oLeft))
+  {
+    oSize -= oLeft;
+    output.resize(oSize+1);
+    output.at(oSize)='\0';
+    return output;
+  }
+  else
+  {
+    return s;
+  }
+}
+
+/*! Add an list item to the contents file.
+ *  \param isDir boolean indicating if this is a dir or file entry
+ *  \param name the name of the item.
+ *  \param ref  the URL of to the item.
+ *  \param file the file in which the item is defined.
+ *  \param anchor the anchor of the item.
+ */
+void HtmlHelp::addContentsItem(bool isDir,
+                               const char *name,
+                               const char * /*ref*/, 
+                               const char *file,
+                               const char *anchor)
+{
+  // If we're using a binary toc then folders cannot have links. 
+  if(Config_getBool("BINARY_TOC") && isDir) 
+  {
+    file = 0;
+    anchor = 0;
+  }
+  int i; for (i=0;i<dc;i++) cts << "  ";
+  cts << "<LI><OBJECT type=\"text/sitemap\">";
+  cts << "<param name=\"Name\" value=\"" << recode(name) << "\">";
+  if (file)      // made file optional param - KPW
+  {
+    cts << "<param name=\"Local\" value=\"" << file << Doxygen::htmlFileExtension;
+    if (anchor) cts << "#" << anchor;  
+    cts << "\">";
+  }
+  cts << "<param name=\"ImageNumber\" value=\"";
+  if (isDir)  // added - KPW
+  {
+    cts << (int)BOOK_CLOSED ;
+  }
+  else
+  {
+    cts << (int)TEXT;
+  }
+  cts << "\">";
+  cts << "</OBJECT>\n";
+}
+
+
+void HtmlHelp::addIndexItem(Definition *context,MemberDef *md,
+                            const char *anc,const char *word)
+{
+  if (md)
+  {
+    static bool separateMemberPages = Config_getBool("SEPARATE_MEMBER_PAGES");
+    if (context==0) // global member
+    {
+      if (md->getGroupDef())
+        context = md->getGroupDef();
+      else if (md->getFileDef())
+        context = md->getFileDef();
+    }
+    if (context==0) return; // should not happen
+
+    QCString cfname  = md->getOutputFileBase();
+    QCString cfiname = context->getOutputFileBase();
+    QCString level1  = context->name();
+    QCString level2  = md->name();
+    QCString contRef = separateMemberPages ? cfname : cfiname;
+    QCString memRef  = cfname;
+    QCString anchor  = anc;
+    index->addItem(level1,level2,contRef,anchor,TRUE,FALSE);
+    index->addItem(level2,level1,memRef,anchor,TRUE,TRUE);
+  }
+  else if (context)
+  {
+    QCString level1  = word ? QCString(word) : context->name();
+    index->addItem(level1,0,context->getOutputFileBase(),anc,TRUE,FALSE);
+  }
+}
+