chiark / gitweb /
doxygen: new @m_footernavigation command.
authorVladimír Vondruš <mosra@centrum.cz>
Thu, 14 Dec 2017 23:12:33 +0000 (00:12 +0100)
committerVladimír Vondruš <mosra@centrum.cz>
Fri, 15 Dec 2017 01:20:26 +0000 (02:20 +0100)
doc/doxygen.rst
doxygen/dox2html5.py
doxygen/templates/base.html
doxygen/templates/page.html
doxygen/test/contents_custom/Doxyfile
doxygen/test/contents_custom/input.dox
doxygen/test/contents_custom/math.html
doxygen/test/contents_custom/subpage1.html [new file with mode: 0644]
doxygen/test/contents_custom/subpage2.html [new file with mode: 0644]
doxygen/test/test_contents.py

index 0ac782909a51d58a3ae220ac63a7b6849b4333ac..66f9fa51670989f40fc9bd9740214727daf4979d 100644 (file)
@@ -568,8 +568,8 @@ aliases in the original ``Doxyfile``:
     Doxygen with :gh:`doxygen/doxygen#623` applied, otherwise the codes will be
     present in the rendered output in their raw form.
 
-`Custom styling`_
------------------
+`Theme-specific commands`_
+--------------------------
 
 It's possible to insert custom m.css classes into the Doxygen output. Add the
 following to your ``Doxyfile-mcss``:
@@ -581,7 +581,8 @@ following to your ``Doxyfile-mcss``:
         "m_enddiv=@xmlonly</mcss:div>@endxmlonly" \
         "m_span{1}=@xmlonly<mcss:span xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" mcss:class=\"\1\">@endxmlonly" \
         "m_endspan=@xmlonly</mcss:span>@endxmlonly" \
-        "m_class{1}=@xmlonly<mcss:class xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" mcss:class=\"\1\" />@endxmlonly"
+        "m_class{1}=@xmlonly<mcss:class xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" mcss:class=\"\1\" />@endxmlonly" \
+        "m_footernavigation=@xmlonly<mcss:footernavigation xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" />@endxmlonly"
 
 If you need backwards compatibility with stock Doxygen HTML output, just make
 the aliases empty in your original ``Doxyfile``. Note that you can rename the
@@ -594,7 +595,8 @@ aliases however you want to fit your naming scheme.
         "m_enddiv=" \
         "m_span{1}=" \
         "m_endspan=" \
-        "m_class{1}="
+        "m_class{1}=" \
+        "m_footernavigation="
 
 With ``@m_div`` and ``@m_span`` it's possible to wrap individual paragraphs or
 inline text in :html:`<div>` / :html:`<span>` and add CSS classes to them.
@@ -645,6 +647,12 @@ formula. Example usage:
 
     See the red :math-danger:`\Sigma` character.
 
+The ``@m_footernavigation`` command is similar to ``@tableofcontents``, but
+across pages --- if a page is a subpage of some other page and this command is
+present in page detailed description, it will cause the footer of the rendered
+page to contain a link to previous, parent and next page according to defined
+page order.
+
 `Customizing the template`_
 ===========================
 
@@ -764,7 +772,9 @@ Property                                Description
 :py:`compound.has_template_details`     If there is a detailed documentation
                                         of template parameters
 :py:`compound.sections`                 Sections of detailed description. See
-                                        `Section properties`_ for details.
+                                        `Navigation properties`_ for details.
+:py:`compound.footer_navigation`        Footer navigation of a page. See
+                                        `Navigation properties`_ for details.
 :py:`compound.brief`                    Brief description. Can be empty. [1]_
 :py:`compound.description`              Detailed description. Can be empty. [2]_
 :py:`compound.dirs`                     List of directories in this compound.
@@ -858,8 +868,8 @@ Property                                Description
                                         result will be saved
 ======================================= =======================================
 
-`Section properties`_
-`````````````````````
+`Navigation properties`_
+````````````````````````
 
 The :py:`compound.sections` property defines a Table of Contents for given
 detailed description. It's a list of :py:`(id, title, children)` tuples, where
@@ -868,6 +878,13 @@ a recursive list of nested sections. If the list is empty, given detailed
 description either has no sections or the TOC was not explicitly requested via
 ``@tableofcontents`` in case of pages.
 
+The :py:`compound.footer_navigation` property defines footer navigation
+requested by the ``@m_footernavigation`` `theme-specific command <#theme-specific-commands>`_.
+If available, it's a tuple of :py:`(prev, up, next)` where each item is a tuple
+of :py:`(url, title)` for a page that's either previous in the defined order,
+one level up or next. For starting/ending page the :py:`prev`/:py:`next` is
+:py:`None`.
+
 `Directory properties`_
 ```````````````````````
 
index 261a244246634d1a668a036b8c9c0054258d7adf..9887dc2e0855b7f7f9caa553b93a7f7d284296cb 100755 (executable)
@@ -141,6 +141,7 @@ def parse_desc_internal(state: State, element: ET.Element, immediate_parent: ET.
     out.params = {}
     out.return_value = None
     out.add_css_class = None
+    out.footer_navigation = False
 
     # DOXYGEN <PARA> PATCHING 1/4
     #
@@ -412,6 +413,9 @@ def parse_desc_internal(state: State, element: ET.Element, immediate_parent: ET.
             # resetting here explicitly.
             add_css_class = parsed.add_css_class
 
+            # Bubble up also the footer navigation
+            if parsed.footer_navigation: out.footer_navigation = True
+
             # Assert we didn't miss anything important
             assert not parsed.section
 
@@ -601,6 +605,10 @@ def parse_desc_internal(state: State, element: ET.Element, immediate_parent: ET.
             else:
                 add_inline_css_class = i.attrib['{http://mcss.mosra.cz/doxygen/}class']
 
+        # Enabling footer navigation in a page
+        elif i.tag == '{http://mcss.mosra.cz/doxygen/}footernavigation':
+            out.footer_navigation = True
+
         # Either block or inline
         elif i.tag == 'programlisting':
             assert element.tag == 'para' # is inside a paragraph :/
@@ -856,7 +864,7 @@ def parse_toplevel_desc(state: State, element: ET.Element):
     assert not parsed.return_value
     if parsed.params:
         logging.warning("Use @tparam instead of @param for documenting class templates, @param is ignored")
-    return (parsed.parsed, parsed.templates, parsed.section[2] if parsed.section else '')
+    return (parsed.parsed, parsed.templates, parsed.section[2] if parsed.section else '', parsed.footer_navigation)
 
 def parse_typedef_desc(state: State, element: ET.Element):
     # Verify that we didn't ignore any important info by accident
@@ -1224,7 +1232,8 @@ def parse_xml(state: State, xml: str):
     compound.has_template_details = False
     compound.templates = None
     compound.brief = parse_desc(state, compounddef.find('briefdescription'))
-    compound.description, templates, compound.sections = parse_toplevel_desc(state, compounddef.find('detaileddescription'))
+    compound.description, templates, compound.sections, footer_navigation = parse_toplevel_desc(state, compounddef.find('detaileddescription'))
+    compound.footer_navigation = None
     compound.dirs = []
     compound.files = []
     compound.namespaces = []
@@ -1271,6 +1280,30 @@ def parse_xml(state: State, xml: str):
         if compounddef.find('tableofcontents') is None:
             compound.sections = []
 
+        # Enable footer navigation, if requested
+        if footer_navigation:
+            up = state.compounds[compound.id].parent
+
+            # Go through all parent children and
+            if up:
+                up = state.compounds[up]
+
+                prev = None
+                next = None
+                prev_child = None
+                for child in up.children:
+                    if child == compound.id:
+                        if prev_child: prev = state.compounds[prev_child]
+                    elif prev_child == compound.id:
+                        next = state.compounds[child]
+                        break
+
+                    prev_child = child
+
+                compound.footer_navigation = ((prev.url, prev.name) if prev else None,
+                                              (up.url, up.name),
+                                              (next.url, next.name) if next else None)
+
         if compound.brief:
             # Remove duplicated brief in pages. Doxygen sometimes adds a period
             # at the end, try without it also.
index d7d632fab2048beeba12cc2e87083944bfbbabed..161c17cad5343a630c136e5bc5fbe174d7df7ce2 100644 (file)
@@ -6,6 +6,8 @@
   {% for css in HTML_EXTRA_STYLESHEET %}
   <link rel="stylesheet" href="{{ css|basename_or_url|e }}" />
   {% endfor %}
+  {% block header_links %}
+  {% endblock %}
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <meta name="theme-color" content="{{ M_THEME_COLOR }}" />
 </head>
index fc227a0f6a322428b6f1ee7b39330a7971d92480..a4c7ebc08cc5dedd77c8996015ab8e000f4933ce 100644 (file)
@@ -3,6 +3,15 @@
 
 {% block title %}{% if 1 in compound.breadcrumb or compound.breadcrumb[-1][0] != PROJECT_NAME %}{% set j = joiner(' &raquo; ') %}{% for name, _ in compound.breadcrumb %}{{ j() }}{{ name }}{% endfor %} | {{ super() }}{% else %}{{ super() }}{% endif %}{% endblock %}
 
+{% block header_links %}
+{% if compound.footer_navigation and compound.footer_navigation[0] %}
+  <link rel="prev" href="{{ compound.footer_navigation[0][0] }}" />
+{% endif %}
+{% if compound.footer_navigation and compound.footer_navigation[2] %}
+  <link rel="next" href="{{ compound.footer_navigation[2][0] }}" />
+{% endif %}
+{% endblock %}
+
 {% block main %}
         <h1>
           {% for name, target in compound.breadcrumb[:-1] %}
           </ul>
         </div>
         {% endif %}
-{% if compound.description %}
+        {% if compound.description %}
 {{ compound.description }}
-{% endif %}
+        {% endif %}
+        {% if compound.footer_navigation %}
+        <div class="m-note m-dim m-thin m-text-center">{% if compound.footer_navigation[0] %}<a href="{{ compound.footer_navigation[0][0] }}" class="m-dox">&laquo; {{ compound.footer_navigation[0][1] }}</a> | {% endif %}<a href="{{ compound.footer_navigation[1][0] }}" class="m-dox">{{ compound.footer_navigation[1][1] }}</a>{% if compound.footer_navigation[2] %} | <a href="{{ compound.footer_navigation[2][0] }}" class="m-dox">{{ compound.footer_navigation[2][1] }} &raquo;</a>{% endif %}</div>
+        {% endif %}
 {% endblock %}
index a902bd3f37433c7d5e95fb0a88a7d0d1798c90d6..afc65d510e33a64c943fc1a2d8af6103675f3fd4 100644 (file)
@@ -12,4 +12,5 @@ ALIASES = \
     "m_enddiv=@xmlonly</mcss:div>@endxmlonly" \
     "m_span{1}=@xmlonly<mcss:span xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" mcss:class=\"\1\">@endxmlonly" \
     "m_endspan=@xmlonly</mcss:span>@endxmlonly" \
-    "m_class{1}=@xmlonly<mcss:class xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" mcss:class=\"\1\" />@endxmlonly"
+    "m_class{1}=@xmlonly<mcss:class xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" mcss:class=\"\1\" />@endxmlonly" \
+    "m_footernavigation=@xmlonly<mcss:footernavigation xmlns:mcss=\"http://mcss.mosra.cz/doxygen/\" />@endxmlonly"
index 6c9951f67164c5f2ac8d8270597da2ebec78b2ab..2ef19b7c695b5b05c415d89410f7f86e201325a3 100644 (file)
@@ -48,6 +48,8 @@ An unstyled list:
 
 /** @page math Math
 
+@m_footernavigation
+
 A green formula:
 
 @m_class{m-success}
@@ -58,4 +60,17 @@ A green formula:
 
 A yellow @m_class{m-warning} @f$ \Sigma @f$ inline formula.
 
+- @subpage subpage1 First subpage
+- @subpage subpage2 Second subpage
+
+*/
+
+/** @page subpage1 First subpage
+
+@m_footernavigation
+*/
+
+/** @page subpage2 Second subpage
+
+@m_footernavigation
 */
index 7dcbc588c36ebe20480a1c49512c095c45dc0fa7..3f064b6bdbac1e903b33e9877a75f01c4954cef7 100644 (file)
@@ -61,7 +61,7 @@ $ \Sigma $
 <g id='eq2-page1'>
 <use x='0' xlink:href='#eq2-g0-6' y='0'/>
 </g>
-</svg> inline formula.</p>
+</svg> inline formula.</p><ul><li><a href="subpage1.html" class="m-dox">First subpage</a> First subpage</li><li><a href="subpage2.html" class="m-dox">Second subpage</a> Second subpage</li></ul>
       </div>
     </div>
   </div>
@@ -76,4 +76,4 @@ $ \Sigma $
   </div>
 </nav></footer>
 </body>
-</html>
\ No newline at end of file
+</html>
diff --git a/doxygen/test/contents_custom/subpage1.html b/doxygen/test/contents_custom/subpage1.html
new file mode 100644 (file)
index 0000000..a41c7cf
--- /dev/null
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>Math &raquo; First subpage | My Project</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+  <link rel="stylesheet" href="m-dark+doxygen.compiled.css" />
+  <link rel="next" href="subpage2.html" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta name="theme-color" content="#22272e" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
+      <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+      <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+      <div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
+        <div class="m-row">
+          <ol class="m-col-t-6 m-col-m-none">
+            <li><a href="pages.html" id="m-navbar-current">Pages</a></li>
+            <li><a href="namespaces.html">Namespaces</a></li>
+          </ol>
+          <ol class="m-col-t-6 m-col-m-none" start="3">
+            <li><a href="annotated.html">Classes</a></li>
+            <li><a href="files.html">Files</a></li>
+          </ol>
+        </div>
+      </div>
+    </div>
+  </div>
+</nav></header>
+<main><article>
+  <div class="m-container m-container-inflatable">
+    <div class="m-row">
+      <div class="m-col-l-10 m-push-l-1">
+        <h1>
+          <span class="m-breadcrumb"><a href="math.html">Math</a> &raquo;</span>
+          First subpage
+        </h1>
+        <div class="m-note m-dim m-thin m-text-center"><a href="math.html" class="m-dox">Math</a> | <a href="subpage2.html" class="m-dox">Second subpage &raquo;</a></div>
+      </div>
+    </div>
+  </div>
+</article></main>
+<footer><nav>
+  <div class="m-container">
+    <div class="m-row">
+      <div class="m-col-l-10 m-push-l-1">
+        <p>My Project. Created by <a href="http://doxygen.org/">Doxygen</a> and <a href="http://mcss.mosra.cz/">m.css</a>.</p>
+      </div>
+    </div>
+  </div>
+</nav></footer>
+</body>
+</html>
diff --git a/doxygen/test/contents_custom/subpage2.html b/doxygen/test/contents_custom/subpage2.html
new file mode 100644 (file)
index 0000000..163982d
--- /dev/null
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>Math &raquo; Second subpage | My Project</title>
+  <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400i,600,600i%7CSource+Code+Pro:400,400i,600" />
+  <link rel="stylesheet" href="m-dark+doxygen.compiled.css" />
+  <link rel="prev" href="subpage1.html" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <meta name="theme-color" content="#22272e" />
+</head>
+<body>
+<header><nav id="navigation">
+  <div class="m-container">
+    <div class="m-row">
+      <a href="index.html" id="m-navbar-brand" class="m-col-t-9 m-col-m-none m-left-m">My Project</a>
+      <a id="m-navbar-show" href="#navigation" title="Show navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+      <a id="m-navbar-hide" href="#" title="Hide navigation" class="m-col-t-3 m-hide-m m-text-right"></a>
+      <div id="m-navbar-collapse" class="m-col-t-12 m-show-m m-col-m-none m-right-m">
+        <div class="m-row">
+          <ol class="m-col-t-6 m-col-m-none">
+            <li><a href="pages.html" id="m-navbar-current">Pages</a></li>
+            <li><a href="namespaces.html">Namespaces</a></li>
+          </ol>
+          <ol class="m-col-t-6 m-col-m-none" start="3">
+            <li><a href="annotated.html">Classes</a></li>
+            <li><a href="files.html">Files</a></li>
+          </ol>
+        </div>
+      </div>
+    </div>
+  </div>
+</nav></header>
+<main><article>
+  <div class="m-container m-container-inflatable">
+    <div class="m-row">
+      <div class="m-col-l-10 m-push-l-1">
+        <h1>
+          <span class="m-breadcrumb"><a href="math.html">Math</a> &raquo;</span>
+          Second subpage
+        </h1>
+        <div class="m-note m-dim m-thin m-text-center"><a href="subpage1.html" class="m-dox">&laquo; First subpage</a> | <a href="math.html" class="m-dox">Math</a></div>
+      </div>
+    </div>
+  </div>
+</article></main>
+<footer><nav>
+  <div class="m-container">
+    <div class="m-row">
+      <div class="m-col-l-10 m-push-l-1">
+        <p>My Project. Created by <a href="http://doxygen.org/">Doxygen</a> and <a href="http://mcss.mosra.cz/">m.css</a>.</p>
+      </div>
+    </div>
+  </div>
+</nav></footer>
+</body>
+</html>
index 704b295fa1fa97383f491a079dd9bbe936c037a3..8c5f4059a115526f949e60cd0d4c010944b250ab 100644 (file)
@@ -139,3 +139,8 @@ class Custom(IntegrationTestCase):
     def test_math(self):
         self.run_dox2html5(wildcard='math.xml')
         self.assertEqual(*self.actual_expected_contents('math.html'))
+
+    def test_footer_navigation(self):
+        self.run_dox2html5(wildcard='subpage*.xml')
+        self.assertEqual(*self.actual_expected_contents('subpage1.html'))
+        self.assertEqual(*self.actual_expected_contents('subpage2.html'))