blob: 7d5de691329cddb59229ddc831944323dfe2c184 [file] [log] [blame]
Guido van Rossum504b0bf1999-01-02 21:28:54 +00001import string
2import os
3import re
4import fnmatch
5from Tkinter import *
6import tkMessageBox
7import SearchEngine
8from SearchDialogBase import SearchDialogBase
Guido van Rossum77f6ccd2002-07-24 01:49:16 +00009import sre_parse
Guido van Rossum504b0bf1999-01-02 21:28:54 +000010
11def replace(text):
12 root = text._root()
13 engine = SearchEngine.get(root)
14 if not hasattr(engine, "_replacedialog"):
15 engine._replacedialog = ReplaceDialog(root, engine)
16 dialog = engine._replacedialog
17 dialog.open(text)
18
19class ReplaceDialog(SearchDialogBase):
20
21 title = "Replace Dialog"
22 icon = "Replace"
23
24 def __init__(self, root, engine):
25 SearchDialogBase.__init__(self, root, engine)
26 self.replvar = StringVar(root)
27
28 def open(self, text):
29 SearchDialogBase.open(self, text)
30 try:
31 first = text.index("sel.first")
32 except TclError:
33 first = None
34 try:
35 last = text.index("sel.last")
36 except TclError:
37 last = None
38 first = first or text.index("insert")
39 last = last or first
40 self.show_hit(first, last)
41 self.ok = 1
42
43 def create_entries(self):
44 SearchDialogBase.create_entries(self)
45 self.replent = self.make_entry("Replace with:", self.replvar)
46
47 def create_command_buttons(self):
48 SearchDialogBase.create_command_buttons(self)
49 self.make_button("Find", self.find_it)
50 self.make_button("Replace", self.replace_it)
51 self.make_button("Replace+Find", self.default_command, 1)
52 self.make_button("Replace All", self.replace_all)
53
54 def find_it(self, event=None):
55 self.do_find(0)
56
57 def replace_it(self, event=None):
58 if self.do_find(self.ok):
59 self.do_replace()
60
61 def default_command(self, event=None):
62 if self.do_find(self.ok):
63 self.do_replace()
64 self.do_find(0)
65
66 def replace_all(self, event=None):
67 prog = self.engine.getprog()
68 if not prog:
69 return
70 repl = self.replvar.get()
71 text = self.text
72 res = self.engine.search_text(text, prog)
73 if not res:
74 text.bell()
75 return
76 text.tag_remove("sel", "1.0", "end")
77 text.tag_remove("hit", "1.0", "end")
78 line = res[0]
79 col = res[1].start()
80 if self.engine.iswrap():
81 line = 1
82 col = 0
83 ok = 1
84 first = last = None
85 # XXX ought to replace circular instead of top-to-bottom when wrapping
Guido van Rossumab6a08a1999-06-08 13:06:07 +000086 text.undo_block_start()
Guido van Rossum504b0bf1999-01-02 21:28:54 +000087 while 1:
88 res = self.engine.search_forward(text, prog, line, col, 0, ok)
89 if not res:
90 break
91 line, m = res
92 chars = text.get("%d.0" % line, "%d.0" % (line+1))
93 orig = m.group()
Guido van Rossumf8d07132000-09-19 20:51:17 +000094 new = self._expand(m, repl)
Guido van Rossum504b0bf1999-01-02 21:28:54 +000095 i, j = m.span()
96 first = "%d.%d" % (line, i)
97 last = "%d.%d" % (line, j)
98 if new == orig:
99 text.mark_set("insert", last)
100 else:
101 text.mark_set("insert", first)
102 if first != last:
103 text.delete(first, last)
104 if new:
105 text.insert(first, new)
106 col = i + len(new)
107 ok = 0
Guido van Rossumab6a08a1999-06-08 13:06:07 +0000108 text.undo_block_stop()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000109 if first and last:
110 self.show_hit(first, last)
111 self.close()
112
113 def do_find(self, ok=0):
114 if not self.engine.getprog():
Tim Petersbc0e9102002-04-04 22:55:58 +0000115 return False
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000116 text = self.text
117 res = self.engine.search_text(text, None, ok)
118 if not res:
119 text.bell()
Tim Petersbc0e9102002-04-04 22:55:58 +0000120 return False
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000121 line, m = res
122 i, j = m.span()
123 first = "%d.%d" % (line, i)
124 last = "%d.%d" % (line, j)
125 self.show_hit(first, last)
126 self.ok = 1
Tim Petersbc0e9102002-04-04 22:55:58 +0000127 return True
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000128
129 def do_replace(self):
130 prog = self.engine.getprog()
131 if not prog:
Tim Petersbc0e9102002-04-04 22:55:58 +0000132 return False
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000133 text = self.text
134 try:
135 first = pos = text.index("sel.first")
136 last = text.index("sel.last")
137 except TclError:
138 pos = None
139 if not pos:
140 first = last = pos = text.index("insert")
141 line, col = SearchEngine.get_line_col(pos)
142 chars = text.get("%d.0" % line, "%d.0" % (line+1))
143 m = prog.match(chars, col)
144 if not prog:
Tim Petersbc0e9102002-04-04 22:55:58 +0000145 return False
Guido van Rossumf8d07132000-09-19 20:51:17 +0000146 new = self._expand(m, self.replvar.get())
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000147 text.mark_set("insert", first)
Guido van Rossum9745f5a1999-06-08 12:54:56 +0000148 text.undo_block_start()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000149 if m.group():
150 text.delete(first, last)
151 if new:
152 text.insert(first, new)
Guido van Rossum9745f5a1999-06-08 12:54:56 +0000153 text.undo_block_stop()
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000154 self.show_hit(first, text.index("insert"))
155 self.ok = 0
Tim Petersbc0e9102002-04-04 22:55:58 +0000156 return True
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000157
Guido van Rossumf8d07132000-09-19 20:51:17 +0000158 def _expand(self, m, template):
159 # XXX This code depends on internals of the regular expression
160 # engine! There's no standard API to do a substitution when you
161 # have already found the match. One should be added.
Guido van Rossum77f6ccd2002-07-24 01:49:16 +0000162 # XXX This parses the template over and over...
163 ptemplate = sre_parse.parse_template(template, m.re)
164 return sre_parse.expand_template(ptemplate, m)
Guido van Rossumf8d07132000-09-19 20:51:17 +0000165
Guido van Rossum504b0bf1999-01-02 21:28:54 +0000166 def show_hit(self, first, last):
167 text = self.text
168 text.mark_set("insert", first)
169 text.tag_remove("sel", "1.0", "end")
170 text.tag_add("sel", first, last)
171 text.tag_remove("hit", "1.0", "end")
172 if first == last:
173 text.tag_add("hit", first)
174 else:
175 text.tag_add("hit", first, last)
176 text.see("insert")
177 text.update_idletasks()
178
179 def close(self, event=None):
180 SearchDialogBase.close(self, event)
181 self.text.tag_remove("hit", "1.0", "end")