In the previous post, methods clone_node
and indent_node
were described. Next method we are going to describe is dedent_node
or moving node left.
Method dedent_node
Method dedent_node(pos)
moves node at the given position to left, i.e. to its grand-parent just after its current parent. This operation can change size of model by deleting some positions.
Here is the image showing dedent_node
in action. We are using outline from our last experiment.
![
Here is the definition of this method:
def dedent_node(self, pos):
'''Moves node left'''
(positions, nodes, attrs, levels, expanded, marked) = self.data
# this node
i = positions.index(pos)
if levels[i] == 1:
# can't move left
return
gnx = nodes[i]
# parent node
pi = up_level_index(levels, i)
pp = positions[pi]
pgnx = nodes[pi]
psz = attrs[pgnx].size
h, b, mps, chn, sz0 = attrs[gnx]
# grandparent node
gpi = up_level_index(levels, pi)
gpp = positions[pi]
gpgnx = nodes[gpi]
di0 = i - gpi
di1 = di0 + sz0
di2 = pi - gpi
di3 = di2 + psz
def movedata(j, ar):
ar[j+di0: j+di3] = ar[j+di1:j+di3] + ar[j+di0:j+di1]
def decrease_levels(j):
a = j + di0
b = j + di1
levels[a:b] = [x-1 for x in levels[a:b]]
donepos = []
for gxi in gnx_iter(nodes, gpgnx, attrs[gpgnx].size):
donepos.append(positions[gxi + di2])
decrease_levels(gxi)
if di1 != di3:
# this node is not the last child of its parent
# we need to move data to the end of parent block
movedata(gxi, positions)
movedata(gxi, nodes)
movedata(gxi, levels)
for pxi in gnx_iter(nodes, pgnx, psz-sz0):
if positions[pxi] not in donepos:
a = pxi + di0 - di2
b = a + sz0
del positions[a:b]
del nodes[a:b]
del levels[a:b]
update_size(attrs, pgnx, -sz0)
update_size(attrs, gpgnx, sz0)
# replace parent with grandparent
mps[mps.index(pgnx)] = gpgnx
self._update_children(pgnx)
self._update_children(gpgnx)
First lines 7-9 prevents dedenting top level nodes (at level 1). Then we collect necessary data about this node, its parent and grand parent node (lines 11-24). Then we calculate the distances of data blocks in lines (26-29). The data block [di0:di1]
relative to grand-parent index represents the node being moved. The data block [di2:di3]
represents parent node.
Then we define two helper functions: movedata
and decrease_levels
. The movedata
method moves block of this node at the end of its current parent node. For example if we look at the left side of the above picture and let’s consider moving node at position P9 left. We would have the following situation (see lines 26-29):
i, pi, gpi = 9, 8, 0
sz0, psz = 4, 11
di0 = i - gpi = 9
di1 = di0 + sz0 = 13
di2 = pi - gpi = 8
di3 = di2 + psz = 19
In this case there is only one grand parent at index 0 (root node). We would then be moving blocks of data as follows (see line 33):
Look at the above image, left side to see which nodes are moved where.
The other helper function decrease_levels
decreses levels of this node and its subtree by one.
If di1 == di3
(end of this node is equal to the end of its parent, i.e. this node is the last child of its parent), we don’t need to move data blocks because they are already where they need to be (see lines 44-46).
Then we visit every occurrence of grand parent node (for loop lines 40-49) and each time we decrease levels, then if necessary move this node at the end of its parent. Each position of parent node is marked as done, so that we don’t process it again.
In the loop (lines 51-57) we are visiting every occurrence of parent node and if it is not already processed we delete child node that was dedented.
Then we update sizes of parent and grand parent ancestors nodes (lines 62-63). Then we change link to parent node to point to the grand parent node in line 63.
Finally in lines 65-66 we update lists of children for the parent node and grand parent node.