diff --git a/CHANGES b/CHANGES index d89886868..cd459a15c 100644 --- a/CHANGES +++ b/CHANGES @@ -52,6 +52,13 @@ New features added - The new ``html_show_sourcelink`` config value can be used to switch off the links to the reST sources in the sidebar. + - The default value for ``htmlhelp_basename`` is now the project + title, cleaned up as a filename. + +* Builders: + + - New builder for Qt help collections, by Antonio Valentino. + - The new ``html_link_suffix`` config value can be used to select the suffix of generated links between HTML files. diff --git a/doc/Makefile b/doc/Makefile index 7cb1ceb7e..6da1654bb 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -11,7 +11,7 @@ PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d _build/doctrees $(PAPEROPT_$(PAPER)) \ $(SPHINXOPTS) . -.PHONY: help clean html web htmlhelp latex changes linkcheck +.PHONY: help clean html pickle htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @@ -48,6 +48,16 @@ htmlhelp: @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in _build/htmlhelp." +qthelp: + mkdir -p _build/qthelp _build/doctrees + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) _build/qthelp + @echo + @echo "Build finished; now you can run qcollectiongenerator with the" \ + ".qhcp project file in build/qthelp." + @echo "# qcollectiongenerator _build/qthelp/Sphinx.qhcp" + @echo "To view the help collection:" + @echo "# assistant -collectionFile _build/qthelp/Sphinx.qhc" + latex: mkdir -p _build/latex _build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex diff --git a/sphinx/builders/__init__.py b/sphinx/builders/__init__.py index 07b1657a4..52b4b0ac2 100644 --- a/sphinx/builders/__init__.py +++ b/sphinx/builders/__init__.py @@ -338,6 +338,7 @@ BUILTIN_BUILDERS = { 'json': ('html', 'JSONHTMLBuilder'), 'web': ('html', 'PickleHTMLBuilder'), 'htmlhelp': ('htmlhelp', 'HTMLHelpBuilder'), + 'qthelp': ('qthelp', 'QtHelpBuilder'), 'latex': ('latex', 'LaTeXBuilder'), 'text': ('text', 'TextBuilder'), 'changes': ('changes', 'ChangesBuilder'), diff --git a/sphinx/builders/html.py b/sphinx/builders/html.py index 86d3f0b77..4ce30ad8b 100644 --- a/sphinx/builders/html.py +++ b/sphinx/builders/html.py @@ -55,6 +55,7 @@ class StandaloneHTMLBuilder(Builder): 'image/jpeg'] searchindex_filename = 'searchindex.js' add_permalinks = True + embedded = False # for things like HTML help or Qt help: suppresses sidebar # This is a class attribute because it is mutated by Sphinx.add_javascript. script_files = ['_static/jquery.js', '_static/doctools.js'] @@ -136,6 +137,7 @@ class StandaloneHTMLBuilder(Builder): rellinks.append(('modindex', _('Global Module Index'), 'M', _('modules'))) self.globalcontext = dict( + embedded = self.embedded, project = self.config.project, release = self.config.release, version = self.config.version, diff --git a/sphinx/builders/htmlhelp.py b/sphinx/builders/htmlhelp.py index 68d3de288..bb8166aca 100644 --- a/sphinx/builders/htmlhelp.py +++ b/sphinx/builders/htmlhelp.py @@ -133,6 +133,8 @@ class HTMLHelpBuilder(StandaloneHTMLBuilder): # don't add links add_permalinks = False + # don't add sidebar etc. + embedded = True def init(self): StandaloneHTMLBuilder.init(self) diff --git a/sphinx/builders/qthelp.py b/sphinx/builders/qthelp.py new file mode 100644 index 000000000..3ad331e48 --- /dev/null +++ b/sphinx/builders/qthelp.py @@ -0,0 +1,261 @@ +# -*- coding: utf-8 -*- +""" + sphinx.builders.qthelp + ~~~~~~~~~~~~~~~~~~~~~~ + + Build input files for the Qt collection generator. + + :copyright: 2008 by Antonio Valentino. + :license: BSD, see LICENSE for details. +""" + +import os +import re +import cgi +from os import path + +from docutils import nodes + +from sphinx import addnodes +from sphinx.builders.html import StandaloneHTMLBuilder + +_idpattern = re.compile('(?P.+) (\((?P<id>[\w\.]+)( (?P<descr>\w+))?\))$') + + +# Qt Help Collection Project (.qhcp). +# Is the input file for the help collection generator. +# It contains references to compressed help files which should be +# included in the collection. +# It may contain various other information for customizing Qt Assistant. +collection_template = '''\ +<?xml version="1.0" encoding="utf-8" ?> +<QHelpCollectionProject version="1.0"> + <docFiles> + <generate> + <file> + <input>%(outname)s.qhp</input> + <output>%(outname)s.qch</output> + </file> + </generate> + <register> + <file>%(outname)s.qch</file> + </register> + </docFiles> +</QHelpCollectionProject> +''' + +# Qt Help Project (.qhp) +# This is the input file for the help generator. +# It contains the table of contents, indices and references to the +# actual documentation files (*.html). +# In addition it defines a unique namespace for the documentation. +project_template = '''\ +<?xml version="1.0" encoding="UTF-8"?> +<QtHelpProject version="1.0"> + <namespace>%(outname)s.org.%(outname)s.%(nversion)s</namespace> + <virtualFolder>doc</virtualFolder> + <customFilter name="%(project)s %(version)s"> + <filterAttribute>%(outname)s</filterAttribute> + <filterAttribute>%(version)s</filterAttribute> + </customFilter> + <filterSection> + <filterAttribute>%(outname)s</filterAttribute> + <filterAttribute>%(version)s</filterAttribute> + <toc> + <section title="%(title)s" ref="%(masterdoc)s.html"> +%(sections)s + </section> + </toc> + <keywords> +%(keywords)s + </keywords> + <files> +%(files)s + </files> + </filterSection> +</QtHelpProject> +''' + +section_template = '<section title="%(title)s" ref="%(ref)s"/>' +file_template = ' '*12 + '<file>%(filename)s</file>' + + +class QtHelpBuilder(StandaloneHTMLBuilder): + """ + Builder that also outputs Qt help project, contents and index files. + """ + name = 'qthelp' + + # don't copy the reST source + copysource = False + supported_image_types = ['image/svg+xml', 'image/png', 'image/gif', + 'image/jpeg'] + + # don't add links + add_permalinks = False + # don't add sidebar etc. + embedded = True + + def init(self): + StandaloneHTMLBuilder.init(self) + # the output files for HTML help must be .html only + self.out_suffix = '.html' + #self.config.html_style = 'traditional.css' + + def handle_finish(self): + self.build_qhcp(self.outdir, self.config.qthelp_basename) + self.build_qhp(self.outdir, self.config.qthelp_basename) + + def build_qhcp(self, outdir, outname): + self.info('writing collection project file...') + f = open(path.join(outdir, outname+'.qhcp'), 'w') + try: + f.write(collection_template % {'outname': outname}) + finally: + f.close() + + def build_qhp(self, outdir, outname): + self.info('writing project file...') + + # sections + tocdoc = self.env.get_and_resolve_doctree(self.config.master_doc, self, + prune_toctrees=False) + istoctree = lambda node: ( + isinstance(node, addnodes.compact_paragraph) + and node.has_key('toctree')) + sections = [] + for node in tocdoc.traverse(istoctree): + sections.extend(self.write_toc(node)) + + if self.config.html_use_modindex: + item = section_template % {'title': _('Global Module Index'), + 'ref': 'modindex.html'} + sections.append(' '*4*4 + item) + sections = '\n'.join(sections) + + # keywords + keywords = [] + index = self.env.create_index(self) + for (key, group) in index: + for title, (refs, subitems) in group: + keywords.extend(self.build_keywords(title, refs, subitems)) + keywords = '\n'.join(keywords) + + # files + if not outdir.endswith(os.sep): + outdir += os.sep + olen = len(outdir) + projectfiles = [] + for root, dirs, files in os.walk(outdir): + staticdir = (root == path.join(outdir, '_static')) + for fn in files: + if (staticdir and not fn.endswith('.js')) or fn.endswith('.html'): + filename = path.join(root, fn)[olen:] + #filename = filename.replace(os.sep, '\\') # XXX + projectfiles.append(file_template % {'filename': filename}) + projectfiles = '\n'.join(projectfiles) + + # write the project file + f = open(path.join(outdir, outname+'.qhp'), 'w') + try: + nversion = self.config.version.replace('.', '_') + nversion = nversion.replace(' ', '_') + f.write(project_template % {'outname': outname, + 'title': self.config.html_title, + 'version': self.config.version, + 'project': self.config.project, + 'nversion': nversion, + 'masterdoc': self.config.master_doc, + 'sections': sections, + 'keywords': keywords, + 'files': projectfiles}) + finally: + f.close() + + def isdocnode(self, node): + if not isinstance(node, nodes.list_item): + return False + if len(node.children) != 2: + return False + if not isinstance(node.children[0], addnodes.compact_paragraph): + return False + if not isinstance(node.children[0][0], nodes.reference): + return False + if not isinstance(node.children[1], nodes.bullet_list): + return False + return True + + def write_toc(self, node, indentlevel=4): + parts = [] + if self.isdocnode(node): + refnode = node.children[0][0] + link = refnode['refuri'] + title = cgi.escape(refnode.astext()).replace('"','"') + item = '<section title="%(title)s" ref="%(ref)s">' % { + 'title': title, + 'ref': link} + parts.append(' '*4*indentlevel + item) + for subnode in node.children[1]: + parts.extend(self.write_toc(subnode, indentlevel+1)) + parts.append(' '*4*indentlevel + '</section>') + elif isinstance(node, nodes.list_item): + for subnode in node: + parts.extend(self.write_toc(subnode, indentlevel)) + elif isinstance(node, nodes.reference): + link = node['refuri'] + title = cgi.escape(node.astext()).replace('"','"') + item = section_template % {'title': title, 'ref': link} + item = ' '*4*indentlevel + item.encode('ascii', 'xmlcharrefreplace') + parts.append(item.encode('ascii', 'xmlcharrefreplace')) + elif isinstance(node, nodes.bullet_list): + for subnode in node: + parts.extend(self.write_toc(subnode, indentlevel)) + elif isinstance(node, addnodes.compact_paragraph): + for subnode in node: + parts.extend(self.write_toc(subnode, indentlevel)) + + return parts + + def keyword_item(self, name, ref): + matchobj = _idpattern.match(name) + if matchobj: + groupdict = matchobj.groupdict() + shortname = groupdict['title'] + id = groupdict.get('id') +# descr = groupdict.get('descr') + if shortname.endswith('()'): + shortname = shortname[:-2] + id = '%s.%s' % (id, shortname) + else: + id = descr = None + + if id: + item = ' '*12 + '<keyword name="%s" id="%s" ref="%s"/>' % ( + name, id, ref) + else: + item = ' '*12 + '<keyword name="%s" ref="%s"/>' % (name, ref) + item.encode('ascii', 'xmlcharrefreplace') + return item + + def build_keywords(self, title, refs, subitems): + keywords = [] + + title = cgi.escape(title) +# if len(refs) == 0: # XXX +# write_param('See Also', title) + if len(refs) == 1: + keywords.append(self.keyword_item(title, refs[0])) + elif len(refs) > 1: + for i, ref in enumerate(refs): # XXX +# item = (' '*12 + +# '<keyword name="%s [%d]" ref="%s"/>' % ( +# title, i, ref)) +# item.encode('ascii', 'xmlcharrefreplace') +# keywords.append(item) + keywords.append(self.keyword_item(title, ref)) + + if subitems: + for subitem in subitems: + keywords.extend(self.build_keywords(subitem[0], subitem[1], [])) + + return keywords diff --git a/sphinx/config.py b/sphinx/config.py index 7eb2fb410..4dce42d6a 100644 --- a/sphinx/config.py +++ b/sphinx/config.py @@ -12,6 +12,8 @@ import os from os import path +from sphinx.util import make_filename + class Config(object): """Configuration file abstraction.""" @@ -77,7 +79,10 @@ class Config(object): html_context = ({}, False), # HTML help only options - htmlhelp_basename = ('pydoc', False), + htmlhelp_basename = (lambda self: make_filename(self.project), False), + + # Qt help only options + qthelp_basename = (lambda self: make_filename(self.project), False), # LaTeX options latex_documents = ([], False), diff --git a/sphinx/quickstart.py b/sphinx/quickstart.py index 8c5ffa3db..ab0a20943 100644 --- a/sphinx/quickstart.py +++ b/sphinx/quickstart.py @@ -257,7 +257,7 @@ PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d %(rbuilddir)s/doctrees $(PAPEROPT_$(PAPER)) \ $(SPHINXOPTS) %(rsrcdir)s -.PHONY: help clean html web pickle htmlhelp latex changes linkcheck +.PHONY: help clean html pickle json htmlhelp qthelp latex changes linkcheck help: \t@echo "Please use \\`make <target>' where <target> is one of" @@ -265,6 +265,7 @@ help: \t@echo " pickle to make pickle files" \t@echo " json to make JSON files" \t@echo " htmlhelp to make HTML files and a HTML help project" +\t@echo " qthelp to make HTML files and a qthelp project" \t@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" \t@echo " changes to make an overview over all changed/added/deprecated items" \t@echo " linkcheck to check all external links for integrity" @@ -284,8 +285,6 @@ pickle: \t@echo \t@echo "Build finished; now you can process the pickle files." -web: pickle - json: \tmkdir -p %(rbuilddir)s/json %(rbuilddir)s/doctrees \t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) %(rbuilddir)s/json @@ -299,6 +298,16 @@ htmlhelp: \t@echo "Build finished; now you can run HTML Help Workshop with the" \\ \t ".hhp project file in %(rbuilddir)s/htmlhelp." +qthelp: +\tmkdir -p %(rbuilddir)s/qthelp %(rbuilddir)s/doctrees +\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) %(rbuilddir)s/qthelp +\t@echo +\t@echo "Build finished; now you can run "qcollectiongenerator" with the" \\ +\t ".qhcp project file in %(rbuilddir)s/qthelp, like this:" +\t@echo "# qcollectiongenerator %(rbuilddir)s/qthelp/Sphinx.qhcp" +\t@echo "To view the help file:" +\t@echo "# assistant -collectionFile %(rbuilddir)s/qthelp/%(project)s.qhc" + latex: \tmkdir -p %(rbuilddir)s/latex %(rbuilddir)s/doctrees \t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) %(rbuilddir)s/latex diff --git a/sphinx/templates/layout.html b/sphinx/templates/layout.html index 510fef7b5..bf203a603 100644 --- a/sphinx/templates/layout.html +++ b/sphinx/templates/layout.html @@ -25,7 +25,7 @@ </div> {%- endmacro %} {%- macro sidebar() %} - {%- if builder != 'htmlhelp' %} + {%- if not embedded %} <div class="sphinxsidebar"> <div class="sphinxsidebarwrapper"> {%- block sidebarlogo %} @@ -82,13 +82,15 @@ <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> {{ metatags }} - {%- if builder != 'htmlhelp' %} + {%- if not embedded %} {%- set titlesuffix = " — "|safe + docstitle|e %} + {%- else %} + {%- set titlesuffix = "" %} {%- endif %} <title>{{ title|striptags }}{{ titlesuffix }} - {%- if builder != 'htmlhelp' %} + {%- if not embedded %}