Blob Blame History Raw
/*
 * Copyright (C) 2008 by Sebastian Pipping.
 * Copyright (C) 2008 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.
 *
 * Sebastian Pipping <sebastian@pipping.org>
 */

#include "qhp.h"
#include "qhpxmlwriter.h"
#include "message.h"
#include "config.h"
#include "memberdef.h"
#include "groupdef.h"
#include "doxygen.h"
#include "filedef.h"

#include <qstringlist.h>
#include <string.h>
#include <qfile.h>

static QCString makeFileName(const char * withoutExtension)
{
  QCString result=withoutExtension;
  if (!result.isEmpty())
  {
    if (result.at(0)=='!') // relative URL -> strip marker
    {
      result=result.mid(1);
    }
    else // add specified HTML extension
    {
      result+=Doxygen::htmlFileExtension;
    }
  }
  return result;
}

static QCString makeRef(const char * withoutExtension, const char * anchor)
{
  //printf("QHP::makeRef(%s,%s)\n",withoutExtension,anchor);
  if (!withoutExtension) return QCString(); 
  QCString result = makeFileName(withoutExtension);
  if (!anchor) return result;
  return result+"#"+anchor;
}

Qhp::Qhp() : m_prevSectionLevel(0), m_sectionLevel(0), m_skipMainPageSection(FALSE)
{
  m_doc.setIndentLevel(0);
  m_toc.setIndentLevel(2);
  m_index.setIndentLevel(2);
  m_files.setIndentLevel(2);
}

Qhp::~Qhp()
{
  clearPrevSection();
}

void Qhp::initialize()
{
  /*
  <QtHelpProject version="1.0">
    <namespace>mycompany.com.myapplication.1_0</namespace>
    <virtualFolder>doc</virtualFolder>
    <customFilter name="My Application 1.0">
      <filterAttribute>myapp</filterAttribute>
      <filterAttribute>1.0</filterAttribute>
    </customFilter>
    <filterSection>
      <filterAttribute>myapp</filterAttribute>
      <filterAttribute>1.0</filterAttribute>
  ..
  */
  QCString nameSpace       = Config_getString(QHP_NAMESPACE);
  QCString virtualFolder   = Config_getString(QHP_VIRTUAL_FOLDER);

  m_doc.declaration("1.0", "UTF-8");

  const char * rootAttributes[] =
  { "version", "1.0", 0 };

  m_doc.open("QtHelpProject", rootAttributes);
  m_doc.openCloseContent("namespace", nameSpace);
  m_doc.openCloseContent("virtualFolder", virtualFolder);

  // Add custom filter
  QCString filterName = Config_getString(QHP_CUST_FILTER_NAME);
  if (!filterName.isEmpty())
  {
    const char * tagAttributes[] = 
    { "name", filterName, 0 };
    m_doc.open("customFilter", tagAttributes);

    QStringList customFilterAttributes = QStringList::split(QChar(' '), Config_getString(QHP_CUST_FILTER_ATTRS));
    for (int i = 0; i < (int)customFilterAttributes.count(); i++)
    {
      m_doc.openCloseContent("filterAttribute", customFilterAttributes[i].utf8());
    }
    m_doc.close("customFilter");
  }

  m_doc.open("filterSection");

  // Add section attributes
  QStringList sectionFilterAttributes = QStringList::split(QChar(' '),
      Config_getString(QHP_SECT_FILTER_ATTRS));
  if (!sectionFilterAttributes.contains(QString("doxygen")))
  {
    sectionFilterAttributes << "doxygen";
  }
  for (int i = 0; i < (int)sectionFilterAttributes.count(); i++)
  {
    m_doc.openCloseContent("filterAttribute", sectionFilterAttributes[i].utf8());
  }

  m_toc.open("toc");

  // Add extra root node
  QCString fullProjectname = getFullProjectName();
  QCString indexFile = "index"+Doxygen::htmlFileExtension;
  const char * const attributes[] =
  { "title", fullProjectname,
    "ref",   indexFile,
    NULL
  };
  m_toc.open("section", attributes);
  m_prevSectionTitle = getFullProjectName();
  m_prevSectionLevel = 1;
  m_sectionLevel = 1;

  m_index.open("keywords");
  m_files.open("files");
}

void Qhp::finalize()
{
  // Finish TOC
  handlePrevSection();
  for (int i = m_prevSectionLevel; i > 0; i--)
  {
    m_toc.close("section");
  }
  m_toc.close("toc");
  m_doc.insert(m_toc);

  // Finish index
  m_index.close("keywords");
  m_doc.insert(m_index);

  // Finish files
  m_files.close("files");
  m_doc.insert(m_files);

  m_doc.close("filterSection");
  m_doc.close("QtHelpProject");

  QCString fileName = Config_getString(HTML_OUTPUT) + "/" + getQhpFileName();
  QFile file(fileName);
  if (!file.open(IO_WriteOnly))
  {
    err("Could not open file %s for writing\n", fileName.data());
    exit(1);
  }
  m_doc.dumpTo(file);
}

void Qhp::incContentsDepth()
{
  m_sectionLevel++;
}

void Qhp::decContentsDepth()
{
  if (m_sectionLevel<=0 || (m_sectionLevel==1 && m_skipMainPageSection))
  {
    m_skipMainPageSection=FALSE;
    return;
  }
  m_sectionLevel--;
}

void Qhp::addContentsItem(bool /*isDir*/, const char * name, 
                          const char * /*ref*/, const char * file, 
                          const char *anchor, bool /* separateIndex */,
                          bool /* addToNavIndex */,
                          Definition * /*def*/)
{
  //printf("Qhp::addContentsItem(%s) %d\n",name,m_sectionLevel);
  // Backup difference before modification

  QCString f = file;
  if (!f.isEmpty() && f.at(0)=='^') return; // absolute URL not supported

  int diff = m_prevSectionLevel - m_sectionLevel;

  handlePrevSection();
  setPrevSection(name, f, anchor, m_sectionLevel);

  // Close sections as needed
  //printf("Qhp::addContentsItem() closing %d sections\n",diff);
  for (; diff > 0; diff--)
  {
    m_toc.close("section");
  }
}

void Qhp::addIndexItem(Definition *context,MemberDef *md,
                       const char *sectionAnchor,const char *word)
{
  (void)word;
  //printf("addIndexItem(%s %s %s\n",
  //       context?context->name().data():"<none>",
  //       md?md->name().data():"<none>",
  //       word);

  if (md) // member
  {
    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  = word ? QCString(word) : md->name();
    QCString contRef = separateMemberPages ? cfname : cfiname;
    QCString anchor  = sectionAnchor ? QCString(sectionAnchor) : md->anchor();

    QCString ref;

    // <keyword name="foo" id="MyApplication::foo" ref="doc.html#foo"/>
    ref = makeRef(contRef, anchor);
    QCString id = level1+"::"+level2;
    const char * attributes[] =
    {
      "name", level2,
      "id",   id,
      "ref",  ref,
      0
    };
    m_index.openClose("keyword", attributes);
  }
  else if (context) // container
  {
    // <keyword name="Foo" id="Foo" ref="doc.html#Foo"/>
    QCString contRef = context->getOutputFileBase();
    QCString level1  = word ? QCString(word) : context->name();
    QCString ref = makeRef(contRef,sectionAnchor);
    const char * attributes[] =
    {
      "name", level1,
      "id",   level1,
      "ref",  ref,
      0
    };
    m_index.openClose("keyword", attributes);
  }
}

void Qhp::addIndexFile(const char * name)
{
  addFile(name);
}

QCString Qhp::getQhpFileName()
{
  return "index.qhp";
}

QCString Qhp::getFullProjectName()
{
  QCString projectName = Config_getString(PROJECT_NAME);
  QCString versionText = Config_getString(PROJECT_NUMBER);
  if (projectName.isEmpty()) projectName="Root";
  return projectName + (versionText.isEmpty()
      ? QCString("")
      : QCString(" ") + versionText);
}

void Qhp::handlePrevSection()
{
  /*
  <toc>
    <section title="My Application Manual" ref="index.html">
      <section title="Chapter 1" ref="doc.html#chapter1"/>
      <section title="Chapter 2" ref="doc.html#chapter2"/>
      <section title="Chapter 3" ref="doc.html#chapter3"/>
    </section>
  </toc>
  */

  if (m_prevSectionTitle.isNull())
  {
    m_prevSectionTitle=" "; // should not happen...
  }

  // We skip "Main Page" as our extra root is pointing to that
  if (!((m_prevSectionLevel==1) && (m_prevSectionTitle==getFullProjectName())))
  {
    QCString finalRef = makeRef(m_prevSectionBaseName, m_prevSectionAnchor);

    const char * const attributes[] =
    { "title", m_prevSectionTitle,
      "ref",   finalRef,
      NULL
    };

    if (m_prevSectionLevel < m_sectionLevel)
    {
      // Section with children
      m_toc.open("section", attributes);
    }
    else
    {
      // Section without children
      m_toc.openClose("section", attributes);
    }
  }
  else
  {
    m_skipMainPageSection=TRUE;
  }

  clearPrevSection();
}

void Qhp::setPrevSection(const char * title, const char * basename, const char * anchor, int level)
{
  m_prevSectionTitle = title;
  m_prevSectionBaseName = basename;
  m_prevSectionAnchor = anchor;
  m_prevSectionLevel = level;
}

void Qhp::clearPrevSection()
{
  m_prevSectionTitle.resize(0);
  m_prevSectionBaseName.resize(0);
  m_prevSectionAnchor.resize(0);
}

void Qhp::addFile(const char * fileName)
{
  m_files.openCloseContent("file", fileName);
}

void Qhp::addImageFile(const char *fileName)
{
  addFile(fileName);
}

void Qhp::addStyleSheetFile(const char *fileName)
{
  addFile(fileName);
}