This file is indexed.

/usr/lib/python3/dist-packages/aeidon/agents/register.py is in python3-aeidon 0.24.3-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# -*- coding: utf-8 -*-

# Copyright (C) 2005-2007,2009 Osmo Salomaa
#
# This file is part of Gaupol.
#
# Gaupol 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 3 of the License, or (at your option) any later
# version.
#
# Gaupol 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
# Gaupol. If not, see <http://www.gnu.org/licenses/>.

"""
Managing revertable actions.

To hook a method up with the undo/redo system, the following need to be done:

(1) The last argument to the method must be a keyword argument `register` with
    a default value of -1. This argument indicates which of doing, undoing or
    redoing is in process.

(2) At the end of the method, :meth:`register_action` needs to be called with
    an instance of :class:`aeidon.RevertableAction`. Calling this method takes
    care of the management of undo and redo stacks.

(3) The method should be marked with the :func:`aeidon.revertable` decorator.
    This decorator takes care of emitting an action signal once the method has
    been run, cuts the undo and redo stacks if needed and defaults the
    `register` keyword argument to :attr:`aeidon.registers.DO`.

Each method marked as revertable should match exactly one action in the undo
and redo stacks. Hence, if a method calls other revertable methods, the
resulting action needs to be grouped as one using :meth:`group_actions`.

If a revertable method needs to be performed without the possibility of
reverting, the `register` keyword argument should be given a value of ``None``.
This way it will not be in any way processed by the undo/redo system.
"""

import aeidon


class RegisterAgent(aeidon.Delegate, metaclass=aeidon.Contractual):

    """
    Managing revertable actions.

    :ivar _do_description: Original description of the action
    """

    def __init__(self, master):
        """Initialize a :class:`RegisterAgent` object."""
        aeidon.Delegate.__init__(self, master)
        self._do_description = None
        aeidon.util.connect(self, self, "notify::undo_limit")

    def _break_action_group_require(self, stack, index=0):
        assert 0 <= index < len(stack)

    def _break_action_group(self, stack, index=0):
        """
        Break the action group in `stack` into individual actions.

        Return the amount of actions broken into.
        """
        action_group = stack.pop(index)
        for action in reversed(action_group.actions):
            stack.insert(index, action)
        return len(action_group.actions)

    def _get_destination_stack(self, register):
        """Return the stack where the registered action will be placed."""
        if register.shift == 1:
            return self.undoables
        if register.shift == -1:
            return self.redoables
        raise ValueError("Invalid register: {}"
                         .format(repr(register)))

    def _get_source_stack(self, register):
        """Return the stack where the action to register is taken from."""
        if register.shift == 1:
            return self.redoables
        if register.shift == -1:
            return self.undoables
        raise ValueError("Invalid register: {}"
                         .format(repr(register)))

    def _on_notify_undo_limit(self, *args):
        """Cut reversion stacks if limit set."""
        if self.undo_limit is not None:
            self.cut_reversion_stacks()

    def _revert_multiple_require(self, count, register):
        stack = self._get_source_stack(register)
        assert len(stack) >= count

    def _revert_multiple(self, count, register):
        """Revert multiple actions."""
        self.block(register.signal)
        stack = self._get_source_stack(register)
        for i in range(count):
            part_count = 1
            if isinstance(stack[0], aeidon.RevertableActionGroup):
                description = stack[0].description
                part_count = self._break_action_group(stack)
            for j in range(part_count):
                self._do_description = stack[0].description
                stack.pop(0).revert()
            if part_count > 1:
                self.group_actions(register, part_count, description)
        self.unblock(register.signal)
        self.cut_reversion_stacks()
        self.emit_action_signal(register)

    def _shift_changed_value(self, action, shift):
        """Shift the values of changed attributes."""
        if aeidon.documents.MAIN in action.docs:
            self.main_changed += shift
        if tuple(action.docs) == (aeidon.documents.TRAN,):
            # Make translation document active.
            if self.tran_changed is None:
                self.tran_changed = 0
        if aeidon.documents.TRAN in action.docs:
            if self.tran_changed is not None:
                self.tran_changed += shift

    @aeidon.deco.export
    def can_redo(self):
        """Return ``True`` if one or more actions can be redone."""
        return bool(self.redoables)

    @aeidon.deco.export
    def can_undo(self):
        """Return ``True`` if one or more actions can be undone."""
        return bool(self.undoables)

    @aeidon.deco.export
    def cut_reversion_stacks(self):
        """Cut undo and redo stacks to their maximum lengths."""
        if self.undo_limit is not None:
            del self.redoables[self.undo_limit:]
            del self.undoables[self.undo_limit:]

    def emit_action_signal_require(self, register):
        if  register is not None:
            assert self._get_destination_stack(register)

    @aeidon.deco.export
    def emit_action_signal(self, register):
        """Emit an action signal for the most recent registered action."""
        if register is not None:
            self.emit(register.signal,
                      self._get_destination_stack(register)[0])

    def group_actions_require(self, register, count, description):
        if register is not None:
            assert len(self._get_destination_stack(register)) >= count

    def group_actions_ensure(self, value, register, count, description):
        if register is not None:
            assert isinstance(self._get_destination_stack(register)[0],
                              aeidon.RevertableActionGroup)

    @aeidon.deco.export
    def group_actions(self, register, count, description):
        """Group the registered actions as one item in the stack."""
        if register is None: return
        action_group = aeidon.RevertableActionGroup()
        action_group.actions = []
        action_group.description = description
        stack = self._get_destination_stack(register)
        for i in range(count):
            action = stack.pop(0)
            if isinstance(action, aeidon.RevertableActionGroup):
                action_group.actions.extend(action.actions)
            else: # Single action
                action_group.actions.append(action)
        stack.insert(0, action_group)

    def redo_require(self, count=1):
        assert len(self.redoables) >= count

    @aeidon.deco.export
    def redo(self, count=1):
        """Redo `count` amount of actions from the redoable stack."""
        group = aeidon.RevertableActionGroup
        if (count > 1) or isinstance(self.redoables[0], group):
            return self._revert_multiple(count, aeidon.registers.REDO)
        self._do_description = self.redoables[0].description
        self.redoables.pop(0).revert()

    @aeidon.deco.export
    def register_action(self, action):
        """Register `action` as done, undone or redone."""
        if action.register == aeidon.registers.DO:
            self.undoables.insert(0, action)
            self.redoables = []
            self._shift_changed_value(action, action.register.shift)
        if action.register == aeidon.registers.UNDO:
            self.redoables.insert(0, action)
            action.description = self._do_description
            self._shift_changed_value(action, action.register.shift)
        if action.register == aeidon.registers.REDO:
            self.undoables.insert(0, action)
            action.description = self._do_description
            self._shift_changed_value(action, action.register.shift)

    def set_action_description_require(self, register, description):
        if register is not None:
            assert self._get_destination_stack(register)

    @aeidon.deco.export
    def set_action_description(self, register, description):
        """Set the description of the most recent registered action."""
        if register is None: return
        stack = self._get_destination_stack(register)
        stack[0].description = description

    def undo_require(self, count=1):
        assert len(self.undoables) >= count

    @aeidon.deco.export
    def undo(self, count=1):
        """Undo `count` amount of actions from the undoable stack."""
        group = aeidon.RevertableActionGroup
        if (count > 1) or isinstance(self.undoables[0], group):
            return self._revert_multiple(count, aeidon.registers.UNDO)
        self._do_description = self.undoables[0].description
        self.undoables.pop(0).revert()