e7c8d1083ea061e64bdac761ddcf58db1d5a489d
[platform/upstream/doxygen.git] / src / qhp.cpp
1 /*
2  * Copyright (C) 2008 by Sebastian Pipping.
3  * Copyright (C) 2008 Dimitri van Heesch.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby 
7  * granted. No representations are made about the suitability of this software 
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  * Sebastian Pipping <sebastian@pipping.org>
15  */
16
17 #include "qhp.h"
18 #include "qhpxmlwriter.h"
19 #include "message.h"
20 #include "config.h"
21 #include "memberdef.h"
22 #include "groupdef.h"
23 #include "doxygen.h"
24 #include "filedef.h"
25
26 #include <qstringlist.h>
27 #include <string.h>
28 #include <qfile.h>
29
30 static QCString makeFileName(const char * withoutExtension)
31 {
32   QCString result=withoutExtension;
33   if (!result.isEmpty())
34   {
35     if (result.at(0)=='!') // relative URL -> strip marker
36     {
37       result=result.mid(1);
38     }
39     else // add specified HTML extension
40     {
41       result+=Doxygen::htmlFileExtension;
42     }
43   }
44   return result;
45 }
46
47 static QCString makeRef(const char * withoutExtension, const char * anchor)
48 {
49   //printf("QHP::makeRef(%s,%s)\n",withoutExtension,anchor);
50   if (!withoutExtension) return QCString(); 
51   QCString result = makeFileName(withoutExtension);
52   if (!anchor) return result;
53   return result+"#"+anchor;
54 }
55
56 Qhp::Qhp() : m_prevSectionLevel(0), m_sectionLevel(0), m_skipMainPageSection(FALSE)
57 {
58   m_doc.setIndentLevel(0);
59   m_toc.setIndentLevel(2);
60   m_index.setIndentLevel(2);
61   m_files.setIndentLevel(2);
62 }
63
64 Qhp::~Qhp()
65 {
66   clearPrevSection();
67 }
68
69 void Qhp::initialize()
70 {
71   /*
72   <QtHelpProject version="1.0">
73     <namespace>mycompany.com.myapplication.1_0</namespace>
74     <virtualFolder>doc</virtualFolder>
75     <customFilter name="My Application 1.0">
76       <filterAttribute>myapp</filterAttribute>
77       <filterAttribute>1.0</filterAttribute>
78     </customFilter>
79     <filterSection>
80       <filterAttribute>myapp</filterAttribute>
81       <filterAttribute>1.0</filterAttribute>
82   ..
83   */
84   QCString nameSpace       = Config_getString(QHP_NAMESPACE);
85   QCString virtualFolder   = Config_getString(QHP_VIRTUAL_FOLDER);
86
87   m_doc.declaration("1.0", "UTF-8");
88
89   const char * rootAttributes[] =
90   { "version", "1.0", 0 };
91
92   m_doc.open("QtHelpProject", rootAttributes);
93   m_doc.openCloseContent("namespace", nameSpace);
94   m_doc.openCloseContent("virtualFolder", virtualFolder);
95
96   // Add custom filter
97   QCString filterName = Config_getString(QHP_CUST_FILTER_NAME);
98   if (!filterName.isEmpty())
99   {
100     const char * tagAttributes[] = 
101     { "name", filterName, 0 };
102     m_doc.open("customFilter", tagAttributes);
103
104     QStringList customFilterAttributes = QStringList::split(QChar(' '), Config_getString(QHP_CUST_FILTER_ATTRS));
105     for (int i = 0; i < (int)customFilterAttributes.count(); i++)
106     {
107       m_doc.openCloseContent("filterAttribute", customFilterAttributes[i].utf8());
108     }
109     m_doc.close("customFilter");
110   }
111
112   m_doc.open("filterSection");
113
114   // Add section attributes
115   QStringList sectionFilterAttributes = QStringList::split(QChar(' '),
116       Config_getString(QHP_SECT_FILTER_ATTRS));
117   if (!sectionFilterAttributes.contains(QString("doxygen")))
118   {
119     sectionFilterAttributes << "doxygen";
120   }
121   for (int i = 0; i < (int)sectionFilterAttributes.count(); i++)
122   {
123     m_doc.openCloseContent("filterAttribute", sectionFilterAttributes[i].utf8());
124   }
125
126   m_toc.open("toc");
127
128   // Add extra root node
129   QCString fullProjectname = getFullProjectName();
130   QCString indexFile = "index"+Doxygen::htmlFileExtension;
131   const char * const attributes[] =
132   { "title", fullProjectname,
133     "ref",   indexFile,
134     NULL
135   };
136   m_toc.open("section", attributes);
137   m_prevSectionTitle = getFullProjectName();
138   m_prevSectionLevel = 1;
139   m_sectionLevel = 1;
140
141   m_index.open("keywords");
142   m_files.open("files");
143 }
144
145 void Qhp::finalize()
146 {
147   // Finish TOC
148   handlePrevSection();
149   for (int i = m_prevSectionLevel; i > 0; i--)
150   {
151     m_toc.close("section");
152   }
153   m_toc.close("toc");
154   m_doc.insert(m_toc);
155
156   // Finish index
157   m_index.close("keywords");
158   m_doc.insert(m_index);
159
160   // Finish files
161   m_files.close("files");
162   m_doc.insert(m_files);
163
164   m_doc.close("filterSection");
165   m_doc.close("QtHelpProject");
166
167   QCString fileName = Config_getString(HTML_OUTPUT) + "/" + getQhpFileName();
168   QFile file(fileName);
169   if (!file.open(IO_WriteOnly))
170   {
171     err("Could not open file %s for writing\n", fileName.data());
172     exit(1);
173   }
174   m_doc.dumpTo(file);
175 }
176
177 void Qhp::incContentsDepth()
178 {
179   m_sectionLevel++;
180 }
181
182 void Qhp::decContentsDepth()
183 {
184   if (m_sectionLevel<=0 || (m_sectionLevel==1 && m_skipMainPageSection))
185   {
186     m_skipMainPageSection=FALSE;
187     return;
188   }
189   m_sectionLevel--;
190 }
191
192 void Qhp::addContentsItem(bool /*isDir*/, const char * name, 
193                           const char * /*ref*/, const char * file, 
194                           const char *anchor, bool /* separateIndex */,
195                           bool /* addToNavIndex */,
196                           Definition * /*def*/)
197 {
198   //printf("Qhp::addContentsItem(%s) %d\n",name,m_sectionLevel);
199   // Backup difference before modification
200
201   QCString f = file;
202   if (!f.isEmpty() && f.at(0)=='^') return; // absolute URL not supported
203
204   int diff = m_prevSectionLevel - m_sectionLevel;
205
206   handlePrevSection();
207   setPrevSection(name, f, anchor, m_sectionLevel);
208
209   // Close sections as needed
210   //printf("Qhp::addContentsItem() closing %d sections\n",diff);
211   for (; diff > 0; diff--)
212   {
213     m_toc.close("section");
214   }
215 }
216
217 void Qhp::addIndexItem(Definition *context,MemberDef *md,
218                        const char *sectionAnchor,const char *word)
219 {
220   (void)word;
221   //printf("addIndexItem(%s %s %s\n",
222   //       context?context->name().data():"<none>",
223   //       md?md->name().data():"<none>",
224   //       word);
225
226   if (md) // member
227   {
228     static bool separateMemberPages = Config_getBool(SEPARATE_MEMBER_PAGES);
229     if (context==0) // global member
230     {
231       if (md->getGroupDef())
232         context = md->getGroupDef();
233       else if (md->getFileDef())
234         context = md->getFileDef();
235     }
236     if (context==0) return; // should not happen
237     QCString cfname  = md->getOutputFileBase();
238     QCString cfiname = context->getOutputFileBase();
239     QCString level1  = context->name();
240     QCString level2  = word ? QCString(word) : md->name();
241     QCString contRef = separateMemberPages ? cfname : cfiname;
242     QCString anchor  = sectionAnchor ? QCString(sectionAnchor) : md->anchor();
243
244     QCString ref;
245
246     // <keyword name="foo" id="MyApplication::foo" ref="doc.html#foo"/>
247     ref = makeRef(contRef, anchor);
248     QCString id = level1+"::"+level2;
249     const char * attributes[] =
250     {
251       "name", level2,
252       "id",   id,
253       "ref",  ref,
254       0
255     };
256     m_index.openClose("keyword", attributes);
257   }
258   else if (context) // container
259   {
260     // <keyword name="Foo" id="Foo" ref="doc.html#Foo"/>
261     QCString contRef = context->getOutputFileBase();
262     QCString level1  = word ? QCString(word) : context->name();
263     QCString ref = makeRef(contRef,sectionAnchor);
264     const char * attributes[] =
265     {
266       "name", level1,
267       "id",   level1,
268       "ref",  ref,
269       0
270     };
271     m_index.openClose("keyword", attributes);
272   }
273 }
274
275 void Qhp::addIndexFile(const char * name)
276 {
277   addFile(name);
278 }
279
280 QCString Qhp::getQhpFileName()
281 {
282   return "index.qhp";
283 }
284
285 QCString Qhp::getFullProjectName()
286 {
287   QCString projectName = Config_getString(PROJECT_NAME);
288   QCString versionText = Config_getString(PROJECT_NUMBER);
289   if (projectName.isEmpty()) projectName="Root";
290   return projectName + (versionText.isEmpty()
291       ? QCString("")
292       : QCString(" ") + versionText);
293 }
294
295 void Qhp::handlePrevSection()
296 {
297   /*
298   <toc>
299     <section title="My Application Manual" ref="index.html">
300       <section title="Chapter 1" ref="doc.html#chapter1"/>
301       <section title="Chapter 2" ref="doc.html#chapter2"/>
302       <section title="Chapter 3" ref="doc.html#chapter3"/>
303     </section>
304   </toc>
305   */
306
307   if (m_prevSectionTitle.isNull())
308   {
309     m_prevSectionTitle=" "; // should not happen...
310   }
311
312   // We skip "Main Page" as our extra root is pointing to that
313   if (!((m_prevSectionLevel==1) && (m_prevSectionTitle==getFullProjectName())))
314   {
315     QCString finalRef = makeRef(m_prevSectionBaseName, m_prevSectionAnchor);
316
317     const char * const attributes[] =
318     { "title", m_prevSectionTitle,
319       "ref",   finalRef,
320       NULL
321     };
322
323     if (m_prevSectionLevel < m_sectionLevel)
324     {
325       // Section with children
326       m_toc.open("section", attributes);
327     }
328     else
329     {
330       // Section without children
331       m_toc.openClose("section", attributes);
332     }
333   }
334   else
335   {
336     m_skipMainPageSection=TRUE;
337   }
338
339   clearPrevSection();
340 }
341
342 void Qhp::setPrevSection(const char * title, const char * basename, const char * anchor, int level)
343 {
344   m_prevSectionTitle = title;
345   m_prevSectionBaseName = basename;
346   m_prevSectionAnchor = anchor;
347   m_prevSectionLevel = level;
348 }
349
350 void Qhp::clearPrevSection()
351 {
352   m_prevSectionTitle.resize(0);
353   m_prevSectionBaseName.resize(0);
354   m_prevSectionAnchor.resize(0);
355 }
356
357 void Qhp::addFile(const char * fileName)
358 {
359   m_files.openCloseContent("file", fileName);
360 }
361
362 void Qhp::addImageFile(const char *fileName)
363 {
364   addFile(fileName);
365 }
366
367 void Qhp::addStyleSheetFile(const char *fileName)
368 {
369   addFile(fileName);
370 }
371