Initial revision
diff --git a/Lib/idlelib/ReplaceDialog.py b/Lib/idlelib/ReplaceDialog.py
new file mode 100644
index 0000000..e29d4d6
--- /dev/null
+++ b/Lib/idlelib/ReplaceDialog.py
@@ -0,0 +1,172 @@
+import string
+import os
+import re
+import fnmatch
+from Tkinter import *
+import tkMessageBox
+import SearchEngine
+from SearchDialogBase import SearchDialogBase
+
+def replace(text):
+    root = text._root()
+    engine = SearchEngine.get(root)
+    if not hasattr(engine, "_replacedialog"):
+        engine._replacedialog = ReplaceDialog(root, engine)
+    dialog = engine._replacedialog
+    dialog.open(text)
+
+class ReplaceDialog(SearchDialogBase):
+
+    title = "Replace Dialog"
+    icon = "Replace"
+
+    def __init__(self, root, engine):
+        SearchDialogBase.__init__(self, root, engine)
+        self.replvar = StringVar(root)
+
+    def open(self, text):
+        SearchDialogBase.open(self, text)
+        try:
+            first = text.index("sel.first")
+        except TclError:
+            first = None
+        try:
+            last = text.index("sel.last")
+        except TclError:
+            last = None
+        first = first or text.index("insert")
+        last = last or first
+        self.show_hit(first, last)
+        self.ok = 1
+
+    def create_entries(self):
+        SearchDialogBase.create_entries(self)
+        self.replent = self.make_entry("Replace with:", self.replvar)
+
+    def create_command_buttons(self):
+        SearchDialogBase.create_command_buttons(self)
+        self.make_button("Find", self.find_it)
+        self.make_button("Replace", self.replace_it)
+        self.make_button("Replace+Find", self.default_command, 1)
+        self.make_button("Replace All", self.replace_all)
+
+    def find_it(self, event=None):
+        self.do_find(0)
+
+    def replace_it(self, event=None):
+        if self.do_find(self.ok):
+            self.do_replace()
+
+    def default_command(self, event=None):
+        if self.do_find(self.ok):
+            self.do_replace()
+            self.do_find(0)
+
+    def replace_all(self, event=None):
+        prog = self.engine.getprog()
+        if not prog:
+            return
+        repl = self.replvar.get()
+        text = self.text
+        res = self.engine.search_text(text, prog)
+        if not res:
+            text.bell()
+            return
+        text.tag_remove("sel", "1.0", "end")
+        text.tag_remove("hit", "1.0", "end")
+        line = res[0]
+        col = res[1].start()
+        if self.engine.iswrap():
+            line = 1
+            col = 0
+        ok = 1
+        first = last = None
+        # XXX ought to replace circular instead of top-to-bottom when wrapping
+        text.undo_block_start()
+        while 1:
+            res = self.engine.search_forward(text, prog, line, col, 0, ok)
+            if not res:
+                break
+            line, m = res
+            chars = text.get("%d.0" % line, "%d.0" % (line+1))
+            orig = m.group()
+            new = re.pcre_expand(m, repl)
+            i, j = m.span()
+            first = "%d.%d" % (line, i)
+            last = "%d.%d" % (line, j)
+            if new == orig:
+                text.mark_set("insert", last)
+            else:
+                text.mark_set("insert", first)
+                if first != last:
+                    text.delete(first, last)
+                if new:
+                    text.insert(first, new)
+            col = i + len(new)
+            ok = 0
+        text.undo_block_stop()
+        if first and last:
+            self.show_hit(first, last)
+        self.close()
+
+    def do_find(self, ok=0):
+        if not self.engine.getprog():
+            return 0
+        text = self.text
+        res = self.engine.search_text(text, None, ok)
+        if not res:
+            text.bell()
+            return 0
+        line, m = res
+        i, j = m.span()
+        first = "%d.%d" % (line, i)
+        last = "%d.%d" % (line, j)
+        self.show_hit(first, last)
+        self.ok = 1
+        return 1
+
+    def do_replace(self):
+        prog = self.engine.getprog()
+        if not prog:
+            return 0
+        text = self.text
+        try:
+            first = pos = text.index("sel.first")
+            last = text.index("sel.last")
+        except TclError:
+            pos = None
+        if not pos:
+            first = last = pos = text.index("insert")
+        line, col = SearchEngine.get_line_col(pos)
+        chars = text.get("%d.0" % line, "%d.0" % (line+1))
+        m = prog.match(chars, col)
+        if not prog:
+            return 0
+        new = re.pcre_expand(m, self.replvar.get())
+        text.mark_set("insert", first)
+        text.undo_block_start()
+        if m.group():
+            text.delete(first, last)
+        if new:
+            text.insert(first, new)
+        text.undo_block_stop()
+        self.show_hit(first, text.index("insert"))
+        self.ok = 0
+        return 1
+
+    def show_hit(self, first, last):
+        text = self.text
+        text.mark_set("insert", first)
+        text.tag_remove("sel", "1.0", "end")
+        text.tag_add("sel", first, last)
+        text.tag_remove("hit", "1.0", "end")
+        if first == last:
+            text.tag_add("hit", first)
+        else:
+            text.tag_add("hit", first, last)
+        text.see("insert")
+        text.update_idletasks()
+
+    def close(self, event=None):
+        SearchDialogBase.close(self, event)
+        self.text.tag_remove("hit", "1.0", "end")