# -*- coding: iso8859-1 -*-
"""
= TracNav: The navigation bar for Trac =

This macro implements a fully customizable navigation bar for the Trac
wiki engine. The contents of the navigation bar is a wiki page itself
and can be edited like any other wiki page through the web
interface. The navigation bar supports hierarchical ordering of
topics. The design of TracNav mimics the design of the TracGuideToc
that was originally supplied with Trac. The drawback of TracGuideToc
is that it is not customizable without editing its source code and
that it does not support hierarchical ordering.


== Installation ==

To install TracNav, you must place the file {{{TracNav.py}}} somewhere
in your {{{wiki-macros/}}} directory of your Trac
installation. Additionally, you have to append the following lines to
the file {{{htdocs/css/wiki.css}}} that can also be found in your Trac
directoryhierarchy.

{{{
/* Styles for TracNav */
.wiki-toc.trac-nav li { margin: 0; padding: 0; }
.wiki-toc.trac-nav li li { margin: 0; padding: 0 1em; }
.wiki-toc.trac-nav .edit { border:0; position:absolute; top:0; right:5px; }
.wiki-toc.trac-nav .edit a { color:blue; border-color:blue; }
}}}

The lines above define the styles for displaying the navigation
bar. These styles build upon the styles for !TracGuideToc that come
with your Trac distribution. If you just install the macro but miss to
extend the style file, TracNav will work but look somewhat strange.


== Usage ==

To use TracNav, you have to create an index page for your site and
call the TracNav macro on each page, where the navigation bar should
be displayed. The index page is a regular wiki page. The page with the
table of contents must include an unordered list of links that should
be displayed in the navigation bar.

To display the navigation bar on a page, you must call the TracNav
macro on that page an pass the name of your table of contents as
argument.


== Author and license ==

Copyright 2005 Bernhard Haumacher (haui at haumacher.de)

{{{
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
}}}

== Additional information and a life example ==

Please visit: http://svn.ipd.uka.de/trac/javaparty/wiki/TracNav
"""

import re
import sys

listRule = re.compile(r"""^(?P<indent> +)\* +(?:(?P<wikilink>\[wiki:(?P<link>(&#34;([^&#34;]*)&#34;|'([^']*)')|([^ \]]+)) +(?P<label>[^\]]*)\])|(?P<text>.*))""", re.M)

def getToc(env, db, name):
    cursor = db.cursor()
    cursor.execute('SELECT text FROM wiki WHERE name=%s ORDER BY version DESC LIMIT 1', name)
    row = cursor.fetchone()
    if not row:
        return None

    tocText = row[0]

    # env.log.debug(tocText)

    stack = [(1, [])]
    nextPos = 0
    while 1:
        match = listRule.search(tocText, nextPos)
        if not match:
            env.log.debug("No more matches")
            break

        indent = len(match.group('indent'))
        if match.group('wikilink'):
            link = match.group('link')
            label = match.group('label')
        else:
            link = None
            label = match.group('text')

        if link == None:
            env.log.debug(label + " ---")
        else:
            env.log.debug(label + ": " + link)

        (lastIndent, list) = stack[len(stack) - 1]

        if indent > lastIndent:
            stack.append((indent, [(link, label, None)]))
        elif indent == lastIndent:
            list.append((link, label, None))
        else:
            while indent < lastIndent:
                (_, list) = stack.pop()
                (lastIndent, topList) = stack[len(stack) - 1]
                (topLink, topLabel, _) = topList[len(topList) - 1]
                topList[len(topList) - 1] = (topLink, topLabel, list)

            (lastIndent, list) = stack[len(stack) - 1]
            list.append((link, label, None))
                
        nextPos = match.end()

    while len(stack) > 1:
        (_, list) = stack.pop()
        (_, topList) = stack[len(stack) - 1]
        (topLink, topLabel, _) = topList[len(topList) - 1]
        topList[len(topList) - 1] = (topLink, topLabel, list)

    (_, list) = stack.pop()
    return list


def execute(hdf, args, env):
    curpage =  '%s' % hdf.getValue('args.page', '')
    name = args
    if not name:
        name = 'TOC'

    db = env.get_db_cnx()
    toc = getToc(env, db, name)
    if not toc:
        msg = ''
        msg += '<div class="system-message"><strong>Error: Table of contents does not exist.'
        if (hdf.getValue('trac.acl.WIKI_MODIFY', '')):
            msg += ' Click here to <a href="%s?edit=yes">edit</a>.' % env.href.wiki(name)
        msg += '</strong></div>'
        return msg

    html = ''
    html += '<div class="wiki-toc trac-nav">'
    if (hdf.getValue('trac.acl.WIKI_MODIFY', '')):
        html += '<div class="edit"><a href="%s?edit=yes">edit</a></div>' % env.href.wiki(name)
    (found, filtered) = filter(curpage, toc, 0)
    if found:
        html += display(env, curpage, filtered)
    else:
        html += display(env, curpage, toc)
    html += '</div>'
    return html


def filter(curpage, toc, level):
    found = 0
    result = []
    for name, title, sub in toc:
        if sub == None:
            if name == curpage:
                found = 1
            result.append((name, title, None))
        else:
            (subfound, subtoc) = filter(curpage, sub, level + 1)
            if subfound or (name == None):
                found = 1
                if level == 0 and name != None:
                    prepended = [(name, title, subtoc)]
                    prepended.extend(result)
                    result = prepended
                else:
                    result.append((name, title, subtoc))
            else:
                result.append((name, title, []))
    return (found, result)


def display(env, curpage, toc):
    html = '<ul>'
    for name, title, sub in toc:
        if sub == None:
            if name == curpage:
                cls = ' class="active"'
            else:
                cls = ''
            html += '<li%s>' % ( cls )
            if name == None:
                html += title
            else:
                html += '<a href="%s">%s</a>' % (env.href.wiki(name), title)
            html += '</li>'
        else:
            html += '<li>'
            if name == None or len(sub) > 0:
                html += '<h4>%s</h4>' % title
            else:
                html += '<h4><a href="%s">%s...</a></h4>' % (env.href.wiki(name), title)
            html += display(env, curpage, sub)
            html += '</li>'
    html += '</ul>'
    return html


