10a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoimport string 20a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom Tkinter import * 30a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 40a8c90248264a8b26970b4473770bcc3df8515fJosh Gaofrom idlelib.Delegator import Delegator 50a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 60a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ event <<redo>> 70a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ win <Control-y> 80a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ unix <Alt-z> 90a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ event <<undo>> 110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ win <Control-z> 120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ unix <Control-z> 130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ event <<dump-undo-state>> 150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ win <Control-backslash> 160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao#$ unix <Control-backslash> 170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 190a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass UndoDelegator(Delegator): 200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao max_undo = 1000 220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self): 240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Delegator.__init__(self) 250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.reset_undo() 260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def setdelegate(self, delegate): 280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.delegate is not None: 290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.unbind("<<undo>>") 300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.unbind("<<redo>>") 310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.unbind("<<dump-undo-state>>") 320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Delegator.setdelegate(self, delegate) 330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if delegate is not None: 340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.bind("<<undo>>", self.undo_event) 350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.bind("<<redo>>", self.redo_event) 360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.bind("<<dump-undo-state>>", self.dump_event) 370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def dump_event(self, event): 390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao from pprint import pprint 400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pprint(self.undolist[:self.pointer]) 410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print "pointer:", self.pointer, 420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print "saved:", self.saved, 430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print "can_merge:", self.can_merge, 440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao print "get_saved():", self.get_saved() 450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pprint(self.undolist[self.pointer:]) 460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return "break" 470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def reset_undo(self): 490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.was_saved = -1 500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.pointer = 0 510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.undolist = [] 520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.undoblock = 0 # or a CommandSequence instance 530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.set_saved(1) 540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def set_saved(self, flag): 560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if flag: 570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.saved = self.pointer 580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.saved = -1 600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.can_merge = False 610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.check_saved() 620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def get_saved(self): 640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.saved == self.pointer 650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao saved_change_hook = None 670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def set_saved_change_hook(self, hook): 690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.saved_change_hook = hook 700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao was_saved = -1 720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def check_saved(self): 740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao is_saved = self.get_saved() 750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if is_saved != self.was_saved: 760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.was_saved = is_saved 770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.saved_change_hook: 780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.saved_change_hook() 790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def insert(self, index, chars, tags=None): 810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.addcmd(InsertCommand(index, chars, tags)) 820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def delete(self, index1, index2=None): 840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.addcmd(DeleteCommand(index1, index2)) 850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Clients should call undo_block_start() and undo_block_stop() 870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # around a sequence of editing cmds to be treated as a unit by 880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # undo & redo. Nested matching calls are OK, and the inner calls 890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # then act like nops. OK too if no editing cmds, or only one 900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # editing cmd, is issued in between: if no cmds, the whole 910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # sequence has no effect; and if only one cmd, that cmd is entered 920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # directly into the undo list, as if undo_block_xxx hadn't been 930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # called. The intent of all that is to make this scheme easy 940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # to use: all the client has to worry about is making sure each 950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # _start() call is matched by a _stop() call. 960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def undo_block_start(self): 980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.undoblock == 0: 990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.undoblock = CommandSequence() 1000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.undoblock.bump_depth() 1010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def undo_block_stop(self): 1030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.undoblock.bump_depth(-1) == 0: 1040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cmd = self.undoblock 1050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.undoblock = 0 1060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if len(cmd) > 0: 1070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if len(cmd) == 1: 1080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # no need to wrap a single cmd 1090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cmd = cmd.getcmd(0) 1100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # this blk of cmds, or single cmd, has already 1110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # been done, so don't execute it again 1120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.addcmd(cmd, 0) 1130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def addcmd(self, cmd, execute=True): 1150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if execute: 1160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cmd.do(self.delegate) 1170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.undoblock != 0: 1180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.undoblock.append(cmd) 1190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return 1200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.can_merge and self.pointer > 0: 1210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao lastcmd = self.undolist[self.pointer-1] 1220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if lastcmd.merge(cmd): 1230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return 1240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.undolist[self.pointer:] = [cmd] 1250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.saved > self.pointer: 1260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.saved = -1 1270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.pointer = self.pointer + 1 1280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if len(self.undolist) > self.max_undo: 1290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ##print "truncating undo list" 1300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao del self.undolist[0] 1310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.pointer = self.pointer - 1 1320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.saved >= 0: 1330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.saved = self.saved - 1 1340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.can_merge = True 1350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.check_saved() 1360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def undo_event(self, event): 1380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.pointer == 0: 1390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.bell() 1400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return "break" 1410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cmd = self.undolist[self.pointer - 1] 1420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cmd.undo(self.delegate) 1430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.pointer = self.pointer - 1 1440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.can_merge = False 1450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.check_saved() 1460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return "break" 1470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def redo_event(self, event): 1490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.pointer >= len(self.undolist): 1500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.bell() 1510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return "break" 1520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cmd = self.undolist[self.pointer] 1530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cmd.redo(self.delegate) 1540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.pointer = self.pointer + 1 1550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.can_merge = False 1560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.check_saved() 1570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return "break" 1580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1600a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass Command: 1610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Base class for Undoable commands 1630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1640a8c90248264a8b26970b4473770bcc3df8515fJosh Gao tags = None 1650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, index1, index2, chars, tags=None): 1670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.marks_before = {} 1680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.marks_after = {} 1690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.index1 = index1 1700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.index2 = index2 1710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.chars = chars 1720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if tags: 1730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.tags = tags 1740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __repr__(self): 1760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao s = self.__class__.__name__ 1770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao t = (self.index1, self.index2, self.chars, self.tags) 1780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.tags is None: 1790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao t = t[:-1] 1800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return s + repr(t) 1810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def do(self, text): 1830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 1840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def redo(self, text): 1860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 1870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def undo(self, text): 1890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao pass 1900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def merge(self, cmd): 1920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return 0 1930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 1940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def save_marks(self, text): 1950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao marks = {} 1960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for name in text.mark_names(): 1970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if name != "insert" and name != "current": 1980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao marks[name] = text.index(name) 1990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return marks 2000a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def set_marks(self, text, marks): 2020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for name, index in marks.items(): 2030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.mark_set(name, index) 2040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2060a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass InsertCommand(Command): 2070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Undoable insert command 2090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, index1, chars, tags=None): 2110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Command.__init__(self, index1, None, chars, tags) 2120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def do(self, text): 2140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.marks_before = self.save_marks(text) 2150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.index1 = text.index(self.index1) 2160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if text.compare(self.index1, ">", "end-1c"): 2170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Insert before the final newline 2180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.index1 = text.index("end-1c") 2190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.insert(self.index1, self.chars, self.tags) 2200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.index2 = text.index("%s+%dc" % (self.index1, len(self.chars))) 2210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.marks_after = self.save_marks(text) 2220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ##sys.__stderr__.write("do: %s\n" % self) 2230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def redo(self, text): 2250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.mark_set('insert', self.index1) 2260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.insert(self.index1, self.chars, self.tags) 2270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.set_marks(text, self.marks_after) 2280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.see('insert') 2290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ##sys.__stderr__.write("redo: %s\n" % self) 2300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def undo(self, text): 2320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.mark_set('insert', self.index1) 2330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.delete(self.index1, self.index2) 2340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.set_marks(text, self.marks_before) 2350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.see('insert') 2360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ##sys.__stderr__.write("undo: %s\n" % self) 2370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def merge(self, cmd): 2390a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.__class__ is not cmd.__class__: 2400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return False 2410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.index2 != cmd.index1: 2420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return False 2430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.tags != cmd.tags: 2440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return False 2450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if len(cmd.chars) != 1: 2460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return False 2470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.chars and \ 2480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.classify(self.chars[-1]) != self.classify(cmd.chars): 2490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return False 2500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.index2 = cmd.index2 2510a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.chars = self.chars + cmd.chars 2520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return True 2530a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2540a8c90248264a8b26970b4473770bcc3df8515fJosh Gao alphanumeric = string.ascii_letters + string.digits + "_" 2550a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2560a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def classify(self, c): 2570a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if c in self.alphanumeric: 2580a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return "alphanumeric" 2590a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if c == "\n": 2600a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return "newline" 2610a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return "punctuation" 2620a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2630a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2640a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass DeleteCommand(Command): 2650a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2660a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Undoable delete command 2670a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2680a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self, index1, index2=None): 2690a8c90248264a8b26970b4473770bcc3df8515fJosh Gao Command.__init__(self, index1, index2, None, None) 2700a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2710a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def do(self, text): 2720a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.marks_before = self.save_marks(text) 2730a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.index1 = text.index(self.index1) 2740a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if self.index2: 2750a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.index2 = text.index(self.index2) 2760a8c90248264a8b26970b4473770bcc3df8515fJosh Gao else: 2770a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.index2 = text.index(self.index1 + " +1c") 2780a8c90248264a8b26970b4473770bcc3df8515fJosh Gao if text.compare(self.index2, ">", "end-1c"): 2790a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Don't delete the final newline 2800a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.index2 = text.index("end-1c") 2810a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.chars = text.get(self.index1, self.index2) 2820a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.delete(self.index1, self.index2) 2830a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.marks_after = self.save_marks(text) 2840a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ##sys.__stderr__.write("do: %s\n" % self) 2850a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2860a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def redo(self, text): 2870a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.mark_set('insert', self.index1) 2880a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.delete(self.index1, self.index2) 2890a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.set_marks(text, self.marks_after) 2900a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.see('insert') 2910a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ##sys.__stderr__.write("redo: %s\n" % self) 2920a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 2930a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def undo(self, text): 2940a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.mark_set('insert', self.index1) 2950a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.insert(self.index1, self.chars) 2960a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.set_marks(text, self.marks_before) 2970a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.see('insert') 2980a8c90248264a8b26970b4473770bcc3df8515fJosh Gao ##sys.__stderr__.write("undo: %s\n" % self) 2990a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3000a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoclass CommandSequence(Command): 3010a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3020a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # Wrapper for a sequence of undoable cmds to be undone/redone 3030a8c90248264a8b26970b4473770bcc3df8515fJosh Gao # as a unit 3040a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3050a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __init__(self): 3060a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.cmds = [] 3070a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.depth = 0 3080a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3090a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __repr__(self): 3100a8c90248264a8b26970b4473770bcc3df8515fJosh Gao s = self.__class__.__name__ 3110a8c90248264a8b26970b4473770bcc3df8515fJosh Gao strs = [] 3120a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for cmd in self.cmds: 3130a8c90248264a8b26970b4473770bcc3df8515fJosh Gao strs.append(" %r" % (cmd,)) 3140a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return s + "(\n" + ",\n".join(strs) + "\n)" 3150a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3160a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def __len__(self): 3170a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return len(self.cmds) 3180a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3190a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def append(self, cmd): 3200a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.cmds.append(cmd) 3210a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3220a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def getcmd(self, i): 3230a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.cmds[i] 3240a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3250a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def redo(self, text): 3260a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for cmd in self.cmds: 3270a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cmd.redo(text) 3280a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3290a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def undo(self, text): 3300a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cmds = self.cmds[:] 3310a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cmds.reverse() 3320a8c90248264a8b26970b4473770bcc3df8515fJosh Gao for cmd in cmds: 3330a8c90248264a8b26970b4473770bcc3df8515fJosh Gao cmd.undo(text) 3340a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3350a8c90248264a8b26970b4473770bcc3df8515fJosh Gao def bump_depth(self, incr=1): 3360a8c90248264a8b26970b4473770bcc3df8515fJosh Gao self.depth = self.depth + incr 3370a8c90248264a8b26970b4473770bcc3df8515fJosh Gao return self.depth 3380a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3390a8c90248264a8b26970b4473770bcc3df8515fJosh Gaodef main(): 3400a8c90248264a8b26970b4473770bcc3df8515fJosh Gao from idlelib.Percolator import Percolator 3410a8c90248264a8b26970b4473770bcc3df8515fJosh Gao root = Tk() 3420a8c90248264a8b26970b4473770bcc3df8515fJosh Gao root.wm_protocol("WM_DELETE_WINDOW", root.quit) 3430a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text = Text() 3440a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.pack() 3450a8c90248264a8b26970b4473770bcc3df8515fJosh Gao text.focus_set() 3460a8c90248264a8b26970b4473770bcc3df8515fJosh Gao p = Percolator(text) 3470a8c90248264a8b26970b4473770bcc3df8515fJosh Gao d = UndoDelegator() 3480a8c90248264a8b26970b4473770bcc3df8515fJosh Gao p.insertfilter(d) 3490a8c90248264a8b26970b4473770bcc3df8515fJosh Gao root.mainloop() 3500a8c90248264a8b26970b4473770bcc3df8515fJosh Gao 3510a8c90248264a8b26970b4473770bcc3df8515fJosh Gaoif __name__ == "__main__": 3520a8c90248264a8b26970b4473770bcc3df8515fJosh Gao main() 353