Changeset 95:1f15abe2f51a

Show
Ignore:
Timestamp:
01/21/09 07:18:37 (19 months ago)
Author:
Ga?tan de Menten <ged@…>
Branch:
default
Message:

- add support for column looping over several rows (rowspan > 1)
- factorized a method to wrap the nodes between two existing nodes in a new

node (removing the boundary nodes in the process)

- the factorization also fixed a small bug which led to bad column width when

repeating several columns

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • relatorio/templates/opendocument.py

    r94 r95  
    121121 
    122122 
     123def wrap_nodes_between(first, last, new_parent): 
     124    old_parent = first.getparent() 
     125    for node in first.itersiblings(): 
     126        if node is last: 
     127            break 
     128        # appending a node to a new parent also 
     129        # remove it from its previous parent 
     130        new_parent.append(node) 
     131    old_parent.replace(first, new_parent) 
     132    old_parent.remove(last) 
     133 
     134 
    123135class Template(MarkupTemplate): 
    124136 
     
    262274        table_row_tag = '{%s}table-row' % table_namespace 
    263275        table_cell_tag = '{%s}table-cell' % table_namespace 
     276        table_cov_cell_tag = '{%s}covered-table-cell' % table_namespace 
    264277        table_num_col_attr = '{%s}number-columns-repeated' % table_namespace 
    265278        table_name_attr = '{%s}name' % table_namespace 
     279        table_rowspan_attr = '{%s}number-rows-spanned' % table_namespace 
    266280 
    267281        office_name = '{%s}value' % self.namespaces['office'] 
     
    298312                        break 
    299313                    except ValueError: 
     314                        # c_ancestors.index(node) raise ValueError if node is 
     315                        # not a child of c_ancestors 
    300316                        pass 
    301317                    o_ancestors.append(node) 
     
    327343                    #    the first cell node after the opening (cell node) 
    328344                    #    ancestor 
    329                     enclosed_cell = outermost_o_ancestor.itersiblings().next() 
     345                    enclosed_cell = outermost_o_ancestor.getnext() 
    330346                    assert enclosed_cell.tag == table_cell_tag 
    331347                    attr_value = "__inc_col_count(%d)" % loop_id 
     
    343359                    # find the position in the row of the cells holding the 
    344360                    # <for> and </for> instructions 
     361                    #XXX: count cells only instead of * 
    345362                    position_xpath_expr = 'count(preceding-sibling::*)' 
    346363                    opening_pos = \ 
     
    351368                                                   namespaces=self.namespaces)) 
    352369 
    353                     # check if this table was already processed 
     370                    # check whether or not the opening tag spans several rows 
     371                    o_node_attrs = outermost_o_ancestor.attrib 
     372                    if table_rowspan_attr in o_node_attrs: 
     373                        rows_spanned = int(o_node_attrs[table_rowspan_attr]) 
     374                        # if so, we need to replace the corresponding cell on 
     375                        # the next line (a covered-table-cell) by a duplicate 
     376                        # py:for node, delete the covered-table-cell 
     377                        # corresponding to the /for, and move all cells between 
     378                        # them into the py:for node 
     379                        row_node = outermost_o_ancestor.getparent() 
     380                        assert row_node.tag == table_row_tag 
     381                        next_rows = row_node.itersiblings(table_row_tag) 
     382                        for row_idx in range(rows_spanned-1): 
     383                            next_row_node = next_rows.next() 
     384                            first = next_row_node[opening_pos] 
     385                            last = next_row_node[closing_pos] 
     386                            assert first.tag == table_cov_cell_tag 
     387                            assert last.tag == table_cov_cell_tag 
     388 
     389                            # execute moves (add a <py:for> node around 
     390                            # the column definitions nodes) 
     391                            tag = '{%s}%s' % (py_namespace, directive) 
     392                            for_node = EtreeElement(tag, 
     393                                                    attrib={attr: a_val}, 
     394                                                    nsmap=self.namespaces) 
     395                            wrap_nodes_between(first, last, for_node) 
     396 
     397                    # check if this table's headers were already processed 
    354398                    repeat_node = table_node.find(repeat_tag) 
    355399                    if repeat_node is not None: 
     
    391435                        # need to be moved to inside the "repeat" tag (which 
    392436                        # is to be created later). 
    393                         to_remove = [] 
    394                         to_move = [] 
    395                         coldefs = table_node.iterchildren(table_col_tag) 
    396                         for idx, tag in enumerate(coldefs): 
    397                             if idx in (opening_pos, closing_pos): 
    398                                 to_remove.append(tag) 
    399                             if opening_pos < idx < closing_pos: 
    400                                 to_move.append(tag) 
    401                             elif idx > closing_pos: 
    402                                 break 
    403                         assert len(to_remove) == 2, "failed %d %d" \ 
    404                                % (opening_pos, closing_pos) 
    405                         assert to_move 
    406  
    407                         # execute deletes 
    408                         for node in to_remove: 
    409                             table_node.remove(node) 
     437                        coldefs = list(table_node.iterchildren(table_col_tag)) 
     438                        first = table_node[opening_pos] 
     439                        last = table_node[closing_pos] 
    410440 
    411441                        # execute moves (add a <relatorio:repeat> node around 
    412442                        # the column definitions nodes) 
    413                         o_pos, c_pos = str(opening_pos), str(closing_pos) 
    414                         repeat_node = EtreeElement(repeat_tag, 
    415                                                    attrib={ 
    416                                                        "opening": o_pos, 
    417                                                        "closing": c_pos, 
    418                                                        "table": table_name}, 
     443                        attribs = { 
     444                           "opening": str(opening_pos), 
     445                           "closing": str(closing_pos), 
     446                           "table": table_name 
     447                        } 
     448                        repeat_node = EtreeElement(repeat_tag, attrib=attribs, 
    419449                                                   nsmap=self.namespaces) 
    420  
    421                         for node in to_move[1:]: 
    422                             table_node.remove(node) 
    423                             repeat_node.append(node) 
    424                         table_node.replace(to_move[0], repeat_node) 
    425                         repeat_node.append(to_move[0]) 
     450                        wrap_nodes_between(first, last, repeat_node) 
    426451 
    427452                # - we create a <py:xxx> node 
     
    431456                                           nsmap=self.namespaces) 
    432457 
    433                 # - we add all the nodes between the opening and closing 
    434                 #   statements to this new node 
    435                 for node in outermost_o_ancestor.itersiblings(): 
    436                     if node is outermost_c_ancestor: 
    437                         break 
    438                     genshi_node.append(node) 
    439  
     458                # - we move all the nodes between the opening and closing 
     459                #   statements to this new node (append also removes from old 
     460                #   parent) 
    440461                # - we replace the opening statement by the <py:xxx> node 
    441                 ancestor.replace(outermost_o_ancestor, genshi_node) 
    442  
    443462                # - we delete the closing statement (and its ancestors) 
    444                 ancestor.remove(outermost_c_ancestor) 
     463                wrap_nodes_between(outermost_o_ancestor, outermost_c_ancestor, 
     464                                   genshi_node) 
    445465            else: 
    446466                # It's not a genshi statement it's a python expression