summaryrefslogtreecommitdiff
path: root/html.py
blob: 0cfc81a2db7242fe410b292f9decfdb2c4fd0c52 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of logilab-common.
#
# logilab-common is free software: you can redistribute it and/or modify it under
# the terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option) any
# later version.
#
# logilab-common 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 Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with logilab-common.  If not, see <http://www.gnu.org/licenses/>.
"""render a tree in HTML."""
__docformat__ = "restructuredtext en"

from warnings import warn
warn('lgc.html module is deprecated', DeprecationWarning, stacklevel=2)


def render_HTML_tree(tree, selected_node=None, render_node=None, caption=None):
    """
    Generate a pure HTML representation of a tree given as an instance
    of a logilab.common.tree.Node

    selected_node is the currently selected node (if any) which will
    have its surrounding <div> have id="selected" (which default
    to a bold border libe with the default CSS).

    render_node is a function that should take a Node content (Node.id)
    as parameter and should return a string (what will be displayed
    in the cell).

    Warning: proper rendering of the generated html code depends on html_tree.css
    """
    tree_depth = tree.depth_down()
    if render_node is None:
        render_node = str

    # helper function that build a matrix from the tree, like:
    # +------+-----------+-----------+
    # | root | child_1_1 | child_2_1 |
    # | root | child_1_1 | child_2_2 |
    # | root | child_1_2 |           |
    # | root | child_1_3 | child_2_3 |
    # | root | child_1_3 | child_2_4 |
    # +------+-----------+-----------+
    # from:
    # root -+- child_1_1 -+- child_2_1
    #       |             |
    #       |             +- child_2_2
    #       +- child_1_2
    #       |
    #       +- child1_3 -+- child_2_3
    #                    |
    #                    +- child_2_2
    def build_matrix(path, matrix):
        if path[-1].is_leaf():
            matrix.append(path[:])
        else:
            for child in path[-1].children:
                build_matrix(path[:] + [child], matrix)

    matrix = []
    build_matrix([tree], matrix)

    # make all lines in the matrix have the same number of columns
    for line in matrix:
        line.extend([None]*(tree_depth-len(line)))
    for i in range(len(matrix)-1, 0, -1):
        prev_line, line = matrix[i-1:i+1]
        for j in range(len(line)):
            if line[j] == prev_line[j]:
                line[j] = None

    # We build the matrix of link types (between 2 cells on a line of the matrix)
    # link types are :
    link_types = {(True,  True,  True ): 1, # T
                  (False, False, True ): 2, # |
                  (False, True,  True ): 3, # + (actually, vert. bar with horiz. bar on the right)
                  (False, True,  False): 4, # L
                  (True,  True,  False): 5, # -
                  }
    links = []
    for i, line in enumerate(matrix):
        links.append([])
        for j in range(tree_depth-1):
            cell_11 = line[j] is not None
            cell_12 = line[j+1] is not None
            cell_21 = line[j+1] is not None and line[j+1].next_sibling() is not None
            link_type = link_types.get((cell_11, cell_12, cell_21), 0)
            if link_type == 0 and i > 0 and links[i-1][j] in (1, 2, 3):
                link_type = 2
            links[-1].append(link_type)


    # We can now generate the HTML code for the <table>
    s = u'<table class="tree">\n'
    if caption:
        s += '<caption>%s</caption>\n' % caption

    for i, link_line in enumerate(links):
        line = matrix[i]

        s += '<tr>'
        for j, link_cell in enumerate(link_line):
            cell = line[j]
            if cell:
                if cell.id == selected_node:
                    s += '<td class="tree_cell" rowspan="2"><div class="selected tree_cell">%s</div></td>' % (render_node(cell.id))
                else:
                    s += '<td class="tree_cell" rowspan="2"><div class="tree_cell">%s</div></td>' % (render_node(cell.id))
            else:
                s += '<td rowspan="2">&nbsp;</td>'
            s += '<td class="tree_cell_%d_1">&nbsp;</td>' % link_cell
            s += '<td class="tree_cell_%d_2">&nbsp;</td>' % link_cell

        cell = line[-1]
        if cell:
            if cell.id == selected_node:
                s += '<td class="tree_cell" rowspan="2"><div class="selected tree_cell">%s</div></td>' % (render_node(cell.id))
            else:
                s += '<td class="tree_cell" rowspan="2"><div class="tree_cell">%s</div></td>' % (render_node(cell.id))
        else:
            s += '<td rowspan="2">&nbsp;</td>'

        s += '</tr>\n'
        if link_line:
            s += '<tr>'
            for j, link_cell in enumerate(link_line):
                s += '<td class="tree_cell_%d_3">&nbsp;</td>' % link_cell
                s += '<td class="tree_cell_%d_4">&nbsp;</td>' % link_cell
            s += '</tr>\n'

    s += '</table>'
    return s