blob: 9a9f74ca77fe537be6c322a2073235cc0697e7af [file] [log] [blame]
Georg Brandl6634bf22008-05-20 07:13:37 +00001from Tkinter import *
Florent Xiclunad630c042010-04-02 07:24:52 +00002
3from idlelib import SearchEngine
4from idlelib.SearchDialogBase import SearchDialogBase
Andrew Svetlov8ba844a2012-08-04 21:45:23 +03005import re
6
David Scherer7aced172000-08-15 01:13:23 +00007
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
Andrew Svetlov8ba844a2012-08-04 21:45:23 +030016
David Scherer7aced172000-08-15 01:13:23 +000017class 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)
Terry Jan Reedye2c409f2014-07-13 17:27:21 -040043 self.replent = self.make_entry("Replace with:", self.replvar)[0]
David Scherer7aced172000-08-15 01:13:23 +000044
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):
Andrew Svetlov8ba844a2012-08-04 21:45:23 +030061 if self.do_replace(): # Only find next match if replace succeeded.
62 # A bad re can cause a 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
David Scherer7aced172000-08-15 01:13:23 +000077
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()
Andrew Svetlov8ba844a2012-08-04 21:45:23 +0300106 new = self._replace_expand(m, repl)
107 if new is None:
108 break
David Scherer7aced172000-08-15 01:13:23 +0000109 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)
Benjamin Peterson167a96c2013-04-03 22:35:12 -0400125 self.close()
David Scherer7aced172000-08-15 01:13:23 +0000126
127 def do_find(self, ok=0):
128 if not self.engine.getprog():
Kurt B. Kaiserce86b102002-09-18 02:56:10 +0000129 return False
David Scherer7aced172000-08-15 01:13:23 +0000130 text = self.text
131 res = self.engine.search_text(text, None, ok)
132 if not res:
133 text.bell()
Kurt B. Kaiserce86b102002-09-18 02:56:10 +0000134 return False
David Scherer7aced172000-08-15 01:13:23 +0000135 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
Kurt B. Kaiserce86b102002-09-18 02:56:10 +0000141 return True
David Scherer7aced172000-08-15 01:13:23 +0000142
143 def do_replace(self):
144 prog = self.engine.getprog()
145 if not prog:
Kurt B. Kaiserce86b102002-09-18 02:56:10 +0000146 return False
David Scherer7aced172000-08-15 01:13:23 +0000147 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:
Kurt B. Kaiserce86b102002-09-18 02:56:10 +0000159 return False
Andrew Svetlov8ba844a2012-08-04 21:45:23 +0300160 new = self._replace_expand(m, self.replvar.get())
161 if new is None:
162 return False
David Scherer7aced172000-08-15 01:13:23 +0000163 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
Kurt B. Kaiserce86b102002-09-18 02:56:10 +0000172 return True
David Scherer7aced172000-08-15 01:13:23 +0000173
David Scherer7aced172000-08-15 01:13:23 +0000174 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")
Terry Jan Reedyd0d4f2d2014-05-27 03:30:44 -0400190
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)