"""Basic regular expression demostration facility (Perl style syntax)."""

from Tkinter import *
import re

class ReDemo:

   def __init__(self, master):
       self.master = master

       self.promptdisplay = Label(self.master, anchor=W,
               text="Enter a Perl-style regular expression:")
       self.promptdisplay.pack(side=TOP, fill=X)

       self.regexdisplay = Entry(self.master)
       self.regexdisplay.pack(fill=X)
       self.regexdisplay.focus_set()

       self.addoptions()

       self.statusdisplay = Label(self.master, text="", anchor=W)
       self.statusdisplay.pack(side=TOP, fill=X)

       self.labeldisplay = Label(self.master, anchor=W,
               text="Enter a string to search:")
       self.labeldisplay.pack(fill=X)
       self.labeldisplay.pack(fill=X)

       self.showframe = Frame(master)
       self.showframe.pack(fill=X, anchor=W)

       self.showvar = StringVar(master)
       self.showvar.set("first")

       self.showfirstradio = Radiobutton(self.showframe,
                                        text="Highlight first match",
                                         variable=self.showvar,
                                         value="first",
                                         command=self.recompile)
       self.showfirstradio.pack(side=LEFT)

       self.showallradio = Radiobutton(self.showframe,
                                       text="Highlight all matches",
                                       variable=self.showvar,
                                       value="all",
                                       command=self.recompile)
       self.showallradio.pack(side=LEFT)

       self.stringdisplay = Text(self.master, width=60, height=4)
       self.stringdisplay.pack(fill=BOTH, expand=1)
       self.stringdisplay.tag_configure("hit", background="yellow")

       self.grouplabel = Label(self.master, text="Groups:", anchor=W)
       self.grouplabel.pack(fill=X)

       self.grouplist = Listbox(self.master)
       self.grouplist.pack(expand=1, fill=BOTH)

       self.regexdisplay.bind('<Key>', self.recompile)
       self.stringdisplay.bind('<Key>', self.reevaluate)

       self.compiled = None
       self.recompile()

       btags = self.regexdisplay.bindtags()
       self.regexdisplay.bindtags(btags[1:] + btags[:1])

       btags = self.stringdisplay.bindtags()
       self.stringdisplay.bindtags(btags[1:] + btags[:1])

   def addoptions(self):
       self.frames = []
       self.boxes = []
       self.vars = []
       for name in ('IGNORECASE',
                    'LOCALE',
                    'MULTILINE',
                    'DOTALL',
                    'VERBOSE'):
           if len(self.boxes) % 3 == 0:
               frame = Frame(self.master)
               frame.pack(fill=X)
               self.frames.append(frame)
           val = getattr(re, name)
           var = IntVar()
           box = Checkbutton(frame,
                   variable=var, text=name,
                   offvalue=0, onvalue=val,
                   command=self.recompile)
           box.pack(side=LEFT)
           self.boxes.append(box)
           self.vars.append(var)

   def getflags(self):
       flags = 0
       for var in self.vars:
           flags = flags | var.get()
       flags = flags
       return flags

   def recompile(self, event=None):
       try:
           self.compiled = re.compile(self.regexdisplay.get(),
                                      self.getflags())
           bg = self.promptdisplay['background']
           self.statusdisplay.config(text="", background=bg)
       except re.error, msg:
           self.compiled = None
           self.statusdisplay.config(
                   text="re.error: %s" % str(msg),
                   background="red")
       self.reevaluate()

   def reevaluate(self, event=None):
       try:
           self.stringdisplay.tag_remove("hit", "1.0", END)
       except TclError:
           pass
       try:
           self.stringdisplay.tag_remove("hit0", "1.0", END)
       except TclError:
           pass
       self.grouplist.delete(0, END)
       if not self.compiled:
           return
       self.stringdisplay.tag_configure("hit", background="yellow")
       self.stringdisplay.tag_configure("hit0", background="orange")
       text = self.stringdisplay.get("1.0", END)
       last = 0
       nmatches = 0
       while last <= len(text):
           m = self.compiled.search(text, last)
           if m is None:
               break
           first, last = m.span()
           if last == first:
               last = first+1
               tag = "hit0"
           else:
               tag = "hit"
           pfirst = "1.0 + %d chars" % first
           plast = "1.0 + %d chars" % last
           self.stringdisplay.tag_add(tag, pfirst, plast)
           if nmatches == 0:
               self.stringdisplay.yview_pickplace(pfirst)
               groups = list(m.groups())
               groups.insert(0, m.group())
               for i in range(len(groups)):
                   g = "%2d: %r" % (i, groups[i])
                   self.grouplist.insert(END, g)
           nmatches = nmatches + 1
           if self.showvar.get() == "first":
               break

       if nmatches == 0:
           self.statusdisplay.config(text="(no match)",
                                     background="yellow")
       else:
           self.statusdisplay.config(text="")


# Main function, run when invoked as a stand-alone Python program.

def main():
   root = Tk()
   demo = ReDemo(root)
   root.protocol('WM_DELETE_WINDOW', root.quit)
   root.mainloop()

if __name__ == '__main__':
   main()