def __init__(self, maxsize=sys.maxint): """maxsize -- maximum size of undo list""" # stacks maintaining (undo,redo) pairs self._undo_actions = LinkedList() self._redo_actions = [] # grouping several actions into one self._group_counter = 0 self._pending_actions = [] # suppress undo/redo while counter > 0 self._suppress_counter = 0 # maximum size undo stack self._maxsize = maxsize self._in_progress = False
class UndoStack(object): """UndoStack for maintaining undo and redo actions""" def __init__(self, maxsize=sys.maxint): """maxsize -- maximum size of undo list""" # stacks maintaining (undo,redo) pairs self._undo_actions = LinkedList() self._redo_actions = [] # grouping several actions into one self._group_counter = 0 self._pending_actions = [] # suppress undo/redo while counter > 0 self._suppress_counter = 0 # maximum size undo stack self._maxsize = maxsize self._in_progress = False def do(self, action, undo, execute=True): """Perform action() (if execute=True) and place (action,undo) pair on stack""" if self._suppress_counter > 0: return if self._group_counter == 0: # grouping is not active, push action pair and clear redo stack self._undo_actions.append((action, undo)) self._redo_actions = [] # TODO: should stack be suppressed at this time? if execute: action() # maintain proper undo size while len(self._undo_actions) > self._maxsize: self._undo_actions.pop_front() else: # grouping is active, place action pair on pending stack self._pending_actions.append((action, undo)) self._redo_actions = [] if execute: action() def undo(self): """Undo last action on stack""" assert self._group_counter == 0 if len(self._undo_actions) > 0: action, undo = self._undo_actions.pop() self.suppress() self._in_progress = True undo() self._in_progress = False self.resume() self._redo_actions.append((action, undo)) def redo(self): """Redo last action on stack""" assert self._group_counter == 0 if len(self._redo_actions) > 0: action, undo = self._redo_actions.pop() self.suppress() self._in_progress = True action() self._in_progress = False self.resume() self._undo_actions.append((action, undo)) while len(self._undo_actions) > self._maxsize: self._undo_actions.pop_front() def begin_action(self): """Start grouping actions Can be called recursively. Must have corresponding end_action() call """ self._group_counter += 1 def end_action(self): """Stop grouping actions Can be called recursively. """ self._group_counter -= 1 assert self._group_counter >= 0 if self._group_counter == 0: if len(self._pending_actions) > 0: actions, undos = zip(*self._pending_actions) self._undo_actions.append( (cat_funcs(actions), cat_funcs(reversed(undos)))) self._pending_actions = [] while len(self._undo_actions) > self._maxsize: self._undo_actions.pop_front() def abort_action(self): """ Stop grouping actions and throw away actions collected so far """ self._group_counter = 0 self._pending_actions = [] def suppress(self): """Suppress pushing actions on stack Can be called recursively. Must have corresponding resume() call""" self._suppress_counter += 1 def resume(self): """Resume pushing actions on stack Can be called recursively. """ self._suppress_counter -= 1 assert self._suppress_counter >= 0 def is_suppressed(self): """Returns True if UndoStack is being suprressed""" return self._suppress_counter > 0 def reset(self): """Clear UndoStack of all actions""" self._undo_actions.clear() self._redo_actions = [] self._group_counter = 0 self._pending_actions = [] self._suppress_counter = 0 def is_in_progress(self): """Returns True if undo or redo is in progress""" return self._in_progress
class UndoStack (object): """UndoStack for maintaining undo and redo actions""" def __init__(self, maxsize=sys.maxint): """maxsize -- maximum size of undo list""" # stacks maintaining (undo,redo) pairs self._undo_actions = LinkedList() self._redo_actions = [] # grouping several actions into one self._group_counter = 0 self._pending_actions = [] # suppress undo/redo while counter > 0 self._suppress_counter = 0 # maximum size undo stack self._maxsize = maxsize self._in_progress = False def do(self, action, undo, execute=True): """Perform action() (if execute=True) and place (action,undo) pair on stack""" if self._suppress_counter > 0: return if self._group_counter == 0: # grouping is not active, push action pair and clear redo stack self._undo_actions.append((action, undo)) self._redo_actions = [] # TODO: should stack be suppressed at this time? if execute: action() # maintain proper undo size while len(self._undo_actions) > self._maxsize: self._undo_actions.pop_front() else: # grouping is active, place action pair on pending stack self._pending_actions.append((action, undo)) self._redo_actions = [] if execute: action() def undo(self): """Undo last action on stack""" assert self._group_counter == 0 if len(self._undo_actions) > 0: action, undo = self._undo_actions.pop() self.suppress() self._in_progress = True undo() self._in_progress = False self.resume() self._redo_actions.append((action, undo)) def redo(self): """Redo last action on stack""" assert self._group_counter == 0 if len(self._redo_actions) > 0: action, undo = self._redo_actions.pop() self.suppress() self._in_progress = True action() self._in_progress = False self.resume() self._undo_actions.append((action, undo)) while len(self._undo_actions) > self._maxsize: self._undo_actions.pop_front() def begin_action(self): """Start grouping actions Can be called recursively. Must have corresponding end_action() call """ self._group_counter += 1 def end_action(self): """Stop grouping actions Can be called recursively. """ self._group_counter -= 1 assert self._group_counter >= 0 if self._group_counter == 0: if len(self._pending_actions) > 0: actions, undos = zip(*self._pending_actions) self._undo_actions.append((cat_funcs(actions), cat_funcs(reversed(undos)))) self._pending_actions = [] while len(self._undo_actions) > self._maxsize: self._undo_actions.pop_front() def abort_action(self): """ Stop grouping actions and throw away actions collected so far """ self._group_counter = 0 self._pending_actions = [] def suppress(self): """Suppress pushing actions on stack Can be called recursively. Must have corresponding resume() call""" self._suppress_counter += 1 def resume(self): """Resume pushing actions on stack Can be called recursively. """ self._suppress_counter -= 1 assert self._suppress_counter >= 0 def is_suppressed(self): """Returns True if UndoStack is being suprressed""" return self._suppress_counter > 0 def reset(self): """Clear UndoStack of all actions""" self._undo_actions.clear() self._redo_actions = [] self._group_counter = 0 self._pending_actions = [] self._suppress_counter = 0 def is_in_progress(self): """Returns True if undo or redo is in progress""" return self._in_progress
def normalize_tags(contents, is_stable_tag=lambda tag: False): """Normalize open and close tags to ensure proper nesting This is especially useful for saving to HTML is_stable_tag -- a function that returns True iff a tag should not be touched (``stable''). NOTE: All iterators will be turned to None's. Since were are changing tag order, iterators no longer make sense NOTE: assumes we do not have any 'beginstr' and 'endstr' items """ open_stack = [] contents = PushIter(contents) for item in contents: kind, it, param = item if kind == "begin": if is_stable_tag(param): # if stable tag, skim ahead to see its closing tag stable_span = LinkedList() within_closes = set() for item2 in contents: stable_span.append(item2) if item2[0] == "end": # look at closing tags if item2[2] == param: # found matching stable close break else: # record tags that close within the stable_span within_closes.add(item2[2]) # push items back on contents stream for item2 in reversed(stable_span): contents.push(item2) # define tag classes # preopen = open_stack # preopen_inside = preopen's with a close in stable_span preopen_inside = [] for tag in open_stack: if tag in within_closes: preopen_inside.append(tag) # close preopen_inside for item2 in _normalize_close(open_stack, preopen_inside): yield item2 # yield stable open open_stack.append(param) yield ("begin", None, param) # reopen preopen_inside for tag in preopen_inside: open_stack.append(tag) yield ("begin", None, tag) else: # yield item unchanged open_stack.append(param) yield ("begin", None, param) elif kind == "end": for item2 in _normalize_close(open_stack, [param]): yield item2 else: yield (kind, None, param)