1from Tkinter import * 2 3from idlelib import SearchEngine 4from idlelib.SearchDialogBase import SearchDialogBase 5import re 6 7 8def replace(text): 9 root = text._root() 10 engine = SearchEngine.get(root) 11 if not hasattr(engine, "_replacedialog"): 12 engine._replacedialog = ReplaceDialog(root, engine) 13 dialog = engine._replacedialog 14 dialog.open(text) 15 16 17class ReplaceDialog(SearchDialogBase): 18 19 title = "Replace Dialog" 20 icon = "Replace" 21 22 def __init__(self, root, engine): 23 SearchDialogBase.__init__(self, root, engine) 24 self.replvar = StringVar(root) 25 26 def open(self, text): 27 SearchDialogBase.open(self, text) 28 try: 29 first = text.index("sel.first") 30 except TclError: 31 first = None 32 try: 33 last = text.index("sel.last") 34 except TclError: 35 last = None 36 first = first or text.index("insert") 37 last = last or first 38 self.show_hit(first, last) 39 self.ok = 1 40 41 def create_entries(self): 42 SearchDialogBase.create_entries(self) 43 self.replent = self.make_entry("Replace with:", self.replvar)[0] 44 45 def create_command_buttons(self): 46 SearchDialogBase.create_command_buttons(self) 47 self.make_button("Find", self.find_it) 48 self.make_button("Replace", self.replace_it) 49 self.make_button("Replace+Find", self.default_command, 1) 50 self.make_button("Replace All", self.replace_all) 51 52 def find_it(self, event=None): 53 self.do_find(0) 54 55 def replace_it(self, event=None): 56 if self.do_find(self.ok): 57 self.do_replace() 58 59 def default_command(self, event=None): 60 if self.do_find(self.ok): 61 if self.do_replace(): # Only find next match if replace succeeded. 62 # A bad re can cause it to fail. 63 self.do_find(0) 64 65 def _replace_expand(self, m, repl): 66 """ Helper function for expanding a regular expression 67 in the replace field, if needed. """ 68 if self.engine.isre(): 69 try: 70 new = m.expand(repl) 71 except re.error: 72 self.engine.report_error(repl, 'Invalid Replace Expression') 73 new = None 74 else: 75 new = repl 76 return new 77 78 def replace_all(self, event=None): 79 prog = self.engine.getprog() 80 if not prog: 81 return 82 repl = self.replvar.get() 83 text = self.text 84 res = self.engine.search_text(text, prog) 85 if not res: 86 text.bell() 87 return 88 text.tag_remove("sel", "1.0", "end") 89 text.tag_remove("hit", "1.0", "end") 90 line = res[0] 91 col = res[1].start() 92 if self.engine.iswrap(): 93 line = 1 94 col = 0 95 ok = 1 96 first = last = None 97 # XXX ought to replace circular instead of top-to-bottom when wrapping 98 text.undo_block_start() 99 while 1: 100 res = self.engine.search_forward(text, prog, line, col, 0, ok) 101 if not res: 102 break 103 line, m = res 104 chars = text.get("%d.0" % line, "%d.0" % (line+1)) 105 orig = m.group() 106 new = self._replace_expand(m, repl) 107 if new is None: 108 break 109 i, j = m.span() 110 first = "%d.%d" % (line, i) 111 last = "%d.%d" % (line, j) 112 if new == orig: 113 text.mark_set("insert", last) 114 else: 115 text.mark_set("insert", first) 116 if first != last: 117 text.delete(first, last) 118 if new: 119 text.insert(first, new) 120 col = i + len(new) 121 ok = 0 122 text.undo_block_stop() 123 if first and last: 124 self.show_hit(first, last) 125 self.close() 126 127 def do_find(self, ok=0): 128 if not self.engine.getprog(): 129 return False 130 text = self.text 131 res = self.engine.search_text(text, None, ok) 132 if not res: 133 text.bell() 134 return False 135 line, m = res 136 i, j = m.span() 137 first = "%d.%d" % (line, i) 138 last = "%d.%d" % (line, j) 139 self.show_hit(first, last) 140 self.ok = 1 141 return True 142 143 def do_replace(self): 144 prog = self.engine.getprog() 145 if not prog: 146 return False 147 text = self.text 148 try: 149 first = pos = text.index("sel.first") 150 last = text.index("sel.last") 151 except TclError: 152 pos = None 153 if not pos: 154 first = last = pos = text.index("insert") 155 line, col = SearchEngine.get_line_col(pos) 156 chars = text.get("%d.0" % line, "%d.0" % (line+1)) 157 m = prog.match(chars, col) 158 if not prog: 159 return False 160 new = self._replace_expand(m, self.replvar.get()) 161 if new is None: 162 return False 163 text.mark_set("insert", first) 164 text.undo_block_start() 165 if m.group(): 166 text.delete(first, last) 167 if new: 168 text.insert(first, new) 169 text.undo_block_stop() 170 self.show_hit(first, text.index("insert")) 171 self.ok = 0 172 return True 173 174 def show_hit(self, first, last): 175 text = self.text 176 text.mark_set("insert", first) 177 text.tag_remove("sel", "1.0", "end") 178 text.tag_add("sel", first, last) 179 text.tag_remove("hit", "1.0", "end") 180 if first == last: 181 text.tag_add("hit", first) 182 else: 183 text.tag_add("hit", first, last) 184 text.see("insert") 185 text.update_idletasks() 186 187 def close(self, event=None): 188 SearchDialogBase.close(self, event) 189 self.text.tag_remove("hit", "1.0", "end") 190 191def _replace_dialog(parent): 192 root = Tk() 193 root.title("Test ReplaceDialog") 194 width, height, x, y = list(map(int, re.split('[x+]', parent.geometry()))) 195 root.geometry("+%d+%d"%(x, y + 150)) 196 197 # mock undo delegator methods 198 def undo_block_start(): 199 pass 200 201 def undo_block_stop(): 202 pass 203 204 text = Text(root) 205 text.undo_block_start = undo_block_start 206 text.undo_block_stop = undo_block_stop 207 text.pack() 208 text.insert("insert","This is a sample string.\n"*10) 209 210 def show_replace(): 211 text.tag_add(SEL, "1.0", END) 212 replace(text) 213 text.tag_remove(SEL, "1.0", END) 214 215 button = Button(root, text="Replace", command=show_replace) 216 button.pack() 217 218if __name__ == '__main__': 219 from idlelib.idle_test.htest import run 220 run(_replace_dialog) 221