chiark / gitweb /
doxygen: support arbitrary HTML for navbar links.
authorVladimír Vondruš <mosra@centrum.cz>
Thu, 11 Oct 2018 10:49:32 +0000 (12:49 +0200)
committerVladimír Vondruš <mosra@centrum.cz>
Thu, 11 Oct 2018 11:30:28 +0000 (13:30 +0200)
doc/doxygen.rst
doxygen/dox2html5.py
doxygen/templates/base.html
doxygen/test/layout_navbar_html/Doxyfile [new file with mode: 0644]
doxygen/test/layout_navbar_html/index.html [new file with mode: 0644]
doxygen/test/layout_navbar_html/indexpage.xml [new file with mode: 0644]
doxygen/test/test_layout.py

index b2193e429e83e6bb69610f41150b41fa3ea54a90..a1a6381c917a217b1b55f67d9c05758e644bb5a8 100644 (file)
@@ -468,6 +468,19 @@ This will put links to namespaces Foo, Bar and Utils as a sub-items of a
 top-level *Namespaces* item and links to two subdirectories as sub-items of the
 *Files* item.
 
+For custom links in the navbar it's possible to use HTML code directly, both
+for a top-level item or in a submenu. The item is taken as everything from the
+initial :html:`<a` to the first closing :html:`</a>`. In the following snippet,
+there are two top-level items, first linking to the page index and having a
+submenu linking to an e-mail address and a ``fine-print`` page and the second
+linking to a GitHub project page:
+
+.. code:: ini
+
+    M_LINKS_NAVBAR2 = \
+        "pages <a href=\"mailto:mosra@centrum.cz\">Contact</a> fine-print" \
+        "<a href=\"https://github.com/mosra/m.css\">GitHub</a>"
+
 `Search options`_
 -----------------
 
@@ -1220,11 +1233,13 @@ the `Configuration`_ table. Most values are provided as-is depending on their
 type, so either strings, booleans, or lists of strings. The exceptions are:
 
 -   The :py:`M_LINKS_NAVBAR1` and :py:`M_LINKS_NAVBAR2` are processed to tuples
-    in a form :py:`(title, url, id, sub)` where :py:`title` is link title,
-    :py:`url` is link URL, :py:`id` is compound ID (to use for highlighting
-    active menu item) and :py:`sub` is a list optionally containing sub-menu
-    items. The sub-menu items are in a similarly formed tuple,
-    :py:`(title, url, id)`.
+    in a form :py:`(html, title, url, id, sub)` where either :py:`html` is a
+    full HTML code for the link and :py:`title`, :py:`url` :py:`id` is empty;
+    or :py:`html` is :py:`None`, :py:`title` and :py:`url` is a link title and
+    URL and :py:`id` is compound ID (to use for highlighting active menu item).
+    The last item, :py:`sub` is a list optionally containing sub-menu items.
+    The sub-menu items are in a similarly formed tuple,
+    :py:`(html, title, url, id)`.
 -   The :py:`M_FAVICON` is converted to a tuple of :py:`(url, type)` where
     :py:`url` is the favicon URL and :py:`type` is favicon MIME type to
     populate the ``type`` attribute of :html:`<link rel="favicon" />`.
index 7d0abde887c8843eb7c66187fd5483fdafd5cf2c..e53bf8fbb68f7cab7cda0e19072102bcb3a81b83 100755 (executable)
@@ -1889,36 +1889,58 @@ def postprocess_state(state: State):
         # Other compounds are not in any index pages or breadcrumb, so leaf
         # name not needed
 
-    # Assign names and URLs to menu items
+    # Assign names and URLs to menu items. The link can be either a predefined
+    # keyword from the below list, a Doxygen symbol, or a HTML code. The
+    # template then gets a tuple of (HTML code, title, URL) and either puts
+    # in the HTML code verbatim (if it's not empty) or creates a link from the
+    # title and URL.
     predefined = {
-        'pages': ("Pages", 'pages.html'),
-        'namespaces': ("Namespaces", 'namespaces.html'),
-        'modules': ("Modules", 'modules.html'),
-        'annotated': ("Classes", 'annotated.html'),
-        'files': ("Files", 'files.html')
+        'pages': (None, "Pages", 'pages.html'),
+        'namespaces': (None, "Namespaces", 'namespaces.html'),
+        'modules': (None, "Modules", 'modules.html'),
+        'annotated': (None, "Classes", 'annotated.html'),
+        'files': (None, "Files", 'files.html')
     }
+    def extract_link(link):
+        # If this is a HTML code, return it verbatim
+        if link.startswith('<a'):
+            return link, None, None
 
-    def find(id):
         # If predefined, return those
-        if id in predefined:
-            return predefined[id]
+        if link in predefined:
+            return predefined[link]
 
         # Otherwise search in symbols
-        found = state.compounds[id]
-        return found.name, found.url
-
+        found = state.compounds[link]
+        return None, found.name, found.url
     i: str
     for var in 'M_LINKS_NAVBAR1', 'M_LINKS_NAVBAR2':
         navbar_links = []
         for i in state.doxyfile[var]:
-            links = i.split()
-            assert len(links)
+            # Split the line into links. It's either single-word keywords or
+            # HTML <a> elements. If it looks like a HTML, take everything until
+            # the closing </a>, otherwise take everything until the next
+            # whitespace.
+            links = []
+            while i:
+                if i.startswith('<a'):
+                    end = i.index('</a>') + 4
+                    links += [i[0:end]]
+                    i = i[end:]
+                else:
+                    firstAndRest = i.split(None, 1)
+                    if len(firstAndRest):
+                        links += [firstAndRest[0]]
+                        if len(firstAndRest) == 1:
+                            break;
+                    i = firstAndRest[1]
+
             sublinks = []
             for sublink in links[1:]:
-                title, url = find(sublink)
-                sublinks += [(title, url, sublink)]
-            title, url = find(links[0])
-            navbar_links += [(title, url, links[0], sublinks)]
+                html, title, url = extract_link(sublink)
+                sublinks += [(html, title, url, sublink)]
+            html, title, url = extract_link(links[0])
+            navbar_links += [(html, title, url, links[0], sublinks)]
 
         state.doxyfile[var] = navbar_links
 
index 205cbc33251983178448df209d264d622dd4713d..db26ec0a79e2d60f4599025a29bb0aa65be3facc 100644 (file)
       <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="{% if M_LINKS_NAVBAR2 %}m-col-t-6{% else %}m-col-t-12{% endif %} m-col-m-none">
-            {% for title, link, id, sub in M_LINKS_NAVBAR1 %}
+            {% for html, title, link, id, sub in M_LINKS_NAVBAR1 %}
             {% if not sub %}
-            <li><a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a></li>
+            <li>{% if html %}{{ html }}{% else %}<a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a>{% endif %}</li>
             {% else %}
             <li>
+              {% if html %}
+              {{ html }}
+              {% else %}
               <a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a>
+              {% endif %}
               <ol>
-                {% for title, link, id in sub %}
-                <li><a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a></li>
+                {% for html, title, link, id in sub %}
+                <li>{% if html %}{{ html }}{% else %}<a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a>{% endif %}</li>
                 {% endfor %}
               </ol>
             </li>
           {% if M_LINKS_NAVBAR2 or not M_SEARCH_DISABLED %}
           {% set start = M_LINKS_NAVBAR1|length + 1 %}
           <ol class="m-col-t-6 m-col-m-none" start="{{ start }}">
-            {% for title, link, id, sub in M_LINKS_NAVBAR2 %}
+            {% for html, title, link, id, sub in M_LINKS_NAVBAR2 %}
             {% if not sub %}
-            <li><a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a></li>
+            <li>{% if html %}{{ html }}{% else %}<a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a>{% endif %}</li>
             {% else %}
             <li>
+              {% if html %}
+              {{ html }}
+              {% else %}
               <a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a>
+              {% endif %}
               <ol>
-                {% for title, link, id in sub %}
-                <li><a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a></li>
+                {% for html, title, link, id in sub %}
+                <li>{% if html %}{{ html }}{% else %}<a href="{{ link }}"{% if (compound and compound.id == id) or navbar_current == id %} id="m-navbar-current"{% endif %}>{{ title }}</a>{% endif %}</li>
                 {% endfor %}
               </ol>
             </li>
diff --git a/doxygen/test/layout_navbar_html/Doxyfile b/doxygen/test/layout_navbar_html/Doxyfile
new file mode 100644 (file)
index 0000000..124959d
--- /dev/null
@@ -0,0 +1,12 @@
+XML_OUTPUT              =
+
+##! M_LINKS_NAVBAR1 = \
+##!     "<a href=\"javascript:alert('hello!');\">Say hello</a> annotated" \
+##!     "files"
+##! M_LINKS_NAVBAR2 = \
+##!     "pages <a href=\"mailto:mosra@centrum.cz\">This is an e-mail</a> namespaces" \
+##!     "<a href=\"https://github.com/mosra/m.css\">GitHub</a>"
+##! M_PAGE_FINE_PRINT   =
+##! M_THEME_COLOR       =
+##! M_FAVICON           =
+##! M_SEARCH_DISABLED   = YES
diff --git a/doxygen/test/layout_navbar_html/index.html b/doxygen/test/layout_navbar_html/index.html
new file mode 100644 (file)
index 0000000..12cf07e
--- /dev/null
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8" />
+  <title>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" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+</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-8 m-col-m-none m-left-m">My Project</a>
+      <div class="m-col-t-4 m-hide-m m-text-right m-nopadr">
+        <a id="m-navbar-show" href="#navigation" title="Show navigation"></a>
+        <a id="m-navbar-hide" href="#" title="Hide navigation"></a>
+      </div>
+      <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="javascript:alert('hello!');">Say hello</a>
+              <ol>
+                <li><a href="annotated.html">Classes</a></li>
+              </ol>
+            </li>
+            <li><a href="files.html">Files</a></li>
+          </ol>
+          <ol class="m-col-t-6 m-col-m-none" start="3">
+            <li>
+              <a href="pages.html">Pages</a>
+              <ol>
+                <li><a href="mailto:mosra@centrum.cz">This is an e-mail</a></li>
+                <li><a href="namespaces.html">Namespaces</a></li>
+              </ol>
+            </li>
+            <li><a href="https://github.com/mosra/m.css">GitHub</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>
+          My Project
+        </h1>
+      </div>
+    </div>
+  </div>
+</article></main>
+</body>
+</html>
\ No newline at end of file
diff --git a/doxygen/test/layout_navbar_html/indexpage.xml b/doxygen/test/layout_navbar_html/indexpage.xml
new file mode 100644 (file)
index 0000000..fa7a2a8
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version='1.0' encoding='UTF-8' standalone='no'?>
+<doxygen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="compound.xsd" version="1.8.14">
+  <compounddef id="indexpage" kind="page">
+    <compoundname>index</compoundname>
+    <title>My Project</title>
+    <briefdescription>
+    </briefdescription>
+    <detaileddescription>
+    </detaileddescription>
+  </compounddef>
+</doxygen>
index 709d60250cac8e63e2140ccef1c9aaf886238a62..5d108db279e7173357dcb7131956b85199508276 100644 (file)
@@ -67,6 +67,14 @@ class NavbarSingleColumn(BaseTestCase):
         self.run_dox2html5(wildcard='indexpage.xml')
         self.assertEqual(*self.actual_expected_contents('index.html'))
 
+class NavbarHtml(BaseTestCase):
+    def __init__(self, *args, **kwargs):
+        super().__init__(__file__, 'navbar_html', *args, **kwargs)
+
+    def test(self):
+        self.run_dox2html5(wildcard='indexpage.xml')
+        self.assertEqual(*self.actual_expected_contents('index.html'))
+
 class SearchBinary(BaseTestCase):
     def __init__(self, *args, **kwargs):
         super().__init__(__file__, 'search_binary', *args, **kwargs)