[Python-checkins] r65542 - sandbox/trunk/ttk-gsoc/src/tabs_ttk_and_co.diff

guilherme.polo python-checkins at python.org
Tue Aug 5 04:02:53 CEST 2008


Author: guilherme.polo
Date: Tue Aug  5 04:02:52 2008
New Revision: 65542

Log:
Current patch for adding tabs support, ttk support and other things to the idlelib

Added:
   sandbox/trunk/ttk-gsoc/src/tabs_ttk_and_co.diff

Added: sandbox/trunk/ttk-gsoc/src/tabs_ttk_and_co.diff
==============================================================================
--- (empty file)
+++ sandbox/trunk/ttk-gsoc/src/tabs_ttk_and_co.diff	Tue Aug  5 04:02:52 2008
@@ -0,0 +1,8367 @@
+Index: ToolTip.py
+===================================================================
+--- ToolTip.py	(revision 63995)
++++ ToolTip.py	(revision 65541)
+@@ -1,89 +0,0 @@
+-# general purpose 'tooltip' routines - currently unused in idlefork
+-# (although the 'calltips' extension is partly based on this code)
+-# may be useful for some purposes in (or almost in ;) the current project scope
+-# Ideas gleaned from PySol
+-
+-from Tkinter import *
+-
+-class ToolTipBase:
+-
+-    def __init__(self, button):
+-        self.button = button
+-        self.tipwindow = None
+-        self.id = None
+-        self.x = self.y = 0
+-        self._id1 = self.button.bind("<Enter>", self.enter)
+-        self._id2 = self.button.bind("<Leave>", self.leave)
+-        self._id3 = self.button.bind("<ButtonPress>", self.leave)
+-
+-    def enter(self, event=None):
+-        self.schedule()
+-
+-    def leave(self, event=None):
+-        self.unschedule()
+-        self.hidetip()
+-
+-    def schedule(self):
+-        self.unschedule()
+-        self.id = self.button.after(1500, self.showtip)
+-
+-    def unschedule(self):
+-        id = self.id
+-        self.id = None
+-        if id:
+-            self.button.after_cancel(id)
+-
+-    def showtip(self):
+-        if self.tipwindow:
+-            return
+-        # The tip window must be completely outside the button;
+-        # otherwise when the mouse enters the tip window we get
+-        # a leave event and it disappears, and then we get an enter
+-        # event and it reappears, and so on forever :-(
+-        x = self.button.winfo_rootx() + 20
+-        y = self.button.winfo_rooty() + self.button.winfo_height() + 1
+-        self.tipwindow = tw = Toplevel(self.button)
+-        tw.wm_overrideredirect(1)
+-        tw.wm_geometry("+%d+%d" % (x, y))
+-        self.showcontents()
+-
+-    def showcontents(self, text="Your text here"):
+-        # Override this in derived class
+-        label = Label(self.tipwindow, text=text, justify=LEFT,
+-                      background="#ffffe0", relief=SOLID, borderwidth=1)
+-        label.pack()
+-
+-    def hidetip(self):
+-        tw = self.tipwindow
+-        self.tipwindow = None
+-        if tw:
+-            tw.destroy()
+-
+-class ToolTip(ToolTipBase):
+-    def __init__(self, button, text):
+-        ToolTipBase.__init__(self, button)
+-        self.text = text
+-    def showcontents(self):
+-        ToolTipBase.showcontents(self, self.text)
+-
+-class ListboxToolTip(ToolTipBase):
+-    def __init__(self, button, items):
+-        ToolTipBase.__init__(self, button)
+-        self.items = items
+-    def showcontents(self):
+-        listbox = Listbox(self.tipwindow, background="#ffffe0")
+-        listbox.pack()
+-        for item in self.items:
+-            listbox.insert(END, item)
+-
+-def main():
+-    # Test code
+-    root = Tk()
+-    b = Button(root, text="Hello", command=root.destroy)
+-    b.pack()
+-    root.update()
+-    tip = ListboxToolTip(b, ["Hello", "world"])
+-    root.mainloop()
+-
+-if __name__ == '__main__':
+-    main()
+Index: AutoCompleteWindow.py
+===================================================================
+--- AutoCompleteWindow.py	(revision 63995)
++++ AutoCompleteWindow.py	(revision 65541)
+@@ -1,10 +1,16 @@
+ """
+ An auto-completion window for IDLE, used by the AutoComplete extension
+ """
+-from Tkinter import *
++from Tkinter import Toplevel, Listbox, Scrollbar, TclError
++from Tkconstants import END, LEFT, RIGHT, BOTH, VERTICAL, Y
++
++import AutoComplete
+ from MultiCall import MC_SHIFT
+-import AutoComplete
++from configHandler import idleConf
+ 
++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'):
++    from ttk import Scrollbar
++
+ HIDE_VIRTUAL_EVENT_NAME = "<<autocompletewindow-hide>>"
+ HIDE_SEQUENCES = ("<FocusOut>", "<ButtonPress>")
+ KEYPRESS_VIRTUAL_EVENT_NAME = "<<autocompletewindow-keypress>>"
+Index: UndoDelegator.py
+===================================================================
+--- UndoDelegator.py	(revision 63995)
++++ UndoDelegator.py	(revision 65541)
+@@ -1,5 +1,5 @@
+ import string
+-from Tkinter import *
++
+ from Delegator import Delegator
+ 
+ #$ event <<redo>>
+@@ -74,7 +74,7 @@
+         if is_saved != self.was_saved:
+             self.was_saved = is_saved
+             if self.saved_change_hook:
+-                self.saved_change_hook()
++                self.saved_change_hook(None)
+ 
+     def insert(self, index, chars, tags=None):
+         self.addcmd(InsertCommand(index, chars, tags))
+@@ -336,6 +336,7 @@
+         return self.depth
+ 
+ def main():
++    from Tkinter import Tk, Text
+     from Percolator import Percolator
+     root = Tk()
+     root.wm_protocol("WM_DELETE_WINDOW", root.quit)
+Index: Bindings.py
+===================================================================
+--- Bindings.py	(revision 63995)
++++ Bindings.py	(revision 65541)
+@@ -6,15 +6,16 @@
+ makes it possible, for example, to define a Debug menu which is only present in
+ the PythonShell window, and a Format menu which is only present in the Editor
+ windows.
+-
+ """
+ import sys
++
+ from configHandler import idleConf
+ 
+ menudefs = [
+  # underscore prefixes character to underscore
+  ('file', [
+    ('_New Window', '<<open-new-window>>'),
++   ('New _Tab', '<<new-tab>>'),
+    ('_Open...', '<<open-window-from-file>>'),
+    ('Open _Module...', '<<open-module>>'),
+    ('Class _Browser', '<<open-class-browser>>'),
+@@ -26,6 +27,7 @@
+    None,
+    ('Prin_t Window', '<<print-window>>'),
+    None,
++   ('Close Tab', '<<close-tab>>'),
+    ('_Close', '<<close-window>>'),
+    ('E_xit', '<<close-all-windows>>'),
+   ]),
+@@ -80,7 +82,6 @@
+    ]),
+ ]
+ 
+-import sys
+ if sys.platform == 'darwin' and '.app' in sys.executable:
+     # Running as a proper MacOS application bundle. This block restructures
+     # the menus a little to make them conform better to the HIG.
+Index: AutoComplete.py
+===================================================================
+--- AutoComplete.py	(revision 63995)
++++ AutoComplete.py	(revision 65541)
+@@ -38,11 +38,11 @@
+     popupwait = idleConf.GetOption("extensions", "AutoComplete",
+                                    "popupwait", type="int", default=0)
+ 
+-    def __init__(self, editwin=None):
+-        self.editwin = editwin
+-        if editwin is None:  # subprocess and test
++    def __init__(self, editpage=None):
++        self.editpage = editpage
++        if editpage is None:  # subprocess and test
+             return
+-        self.text = editwin.text
++        self.text = editpage.text
+         self.autocompletewindow = None
+ 
+         # id of delayed call, and the index of the text insert when the delayed
+@@ -120,7 +120,7 @@
+             self.text.after_cancel(self._delayed_completion_id)
+             self._delayed_completion_id = None
+ 
+-        hp = HyperParser(self.editwin, "insert")
++        hp = HyperParser(self.editpage, "insert")
+         curline = self.text.get("insert linestart", "insert")
+         i = j = len(curline)
+         if hp.is_in_string() and (not mode or mode==COMPLETE_FILES):
+@@ -176,7 +176,7 @@
+         module may be inoperative if the module was not the last to run.
+         """
+         try:
+-            rpcclt = self.editwin.flist.pyshell.interp.rpcclt
++            rpcclt = self.editpage.editwin.flist.pyshell.interp.rpcclt
+         except:
+             rpcclt = None
+         if rpcclt:
+Index: configHandler.py
+===================================================================
+--- configHandler.py	(revision 63995)
++++ configHandler.py	(revision 65541)
+@@ -19,10 +19,10 @@
+ """
+ import os
+ import sys
+-import string
+-import macosxSupport
+ from ConfigParser import ConfigParser, NoOptionError, NoSectionError
+ 
++import macosxSupport
++
+ class InvalidConfigType(Exception): pass
+ class InvalidConfigSet(Exception): pass
+ class InvalidFgBg(Exception): pass
+@@ -295,7 +295,10 @@
+             back=themeDict['normal-background']
+         else:
+             back=themeDict[element+'-background']
+-        highlight={"foreground": fore,"background": back}
++        #bold = 0
++        #if  element + '-bold' in themeDict:
++        #    bold = themeDict[element + '-bold']
++        highlight={"foreground": fore, "background": back}#, "font": bold}
+         if not fgBg: #return dict of both colours
+             return highlight
+         else: #return specified colour only
+@@ -558,6 +561,10 @@
+         defined here.
+         """
+         keyBindings={
++            '<<close-tab>>': ['<Control-w>'],
++            '<<new-tab>>': ['<Control-t>'],
++            '<<next-tab>>': ['<Control-Next'],
++            '<<prev-tab>>': ['<Control-Prior'],
+             '<<copy>>': ['<Control-c>', '<Control-C>'],
+             '<<cut>>': ['<Control-x>', '<Control-X>'],
+             '<<paste>>': ['<Control-v>', '<Control-V>'],
+@@ -649,7 +656,7 @@
+                 menuItem='' #make these empty
+                 helpPath='' #so value won't be added to list
+             else: #config entry contains ';' as expected
+-                value=string.split(value,';')
++                value = value.split(';')
+                 menuItem=value[0].strip()
+                 helpPath=value[1].strip()
+             if menuItem and helpPath: #neither are empty strings
+Index: HyperParser.py
+===================================================================
+--- HyperParser.py	(revision 63995)
++++ HyperParser.py	(revision 65541)
+@@ -10,22 +10,22 @@
+ 
+ import string
+ import keyword
++
+ import PyParse
++from editorpage import index2line
+ 
+ class HyperParser:
+ 
+-    def __init__(self, editwin, index):
++    def __init__(self, editpage, index):
+         """Initialize the HyperParser to analyze the surroundings of the given
+         index.
+         """
+ 
+-        self.editwin = editwin
+-        self.text = text = editwin.text
++        self.editwin = editwin = editpage.editwin
++        self.text = text = editpage.text
+ 
+         parser = PyParse.Parser(editwin.indentwidth, editwin.tabwidth)
+ 
+-        def index2line(index):
+-            return int(float(index))
+         lno = index2line(text.index(index))
+ 
+         if not editwin.context_use_ps1:
+@@ -38,7 +38,7 @@
+                 # its status will be the same as the char before it, if should.
+                 parser.set_str(text.get(startatindex, stopatindex)+' \n')
+                 bod = parser.find_good_parse_start(
+-                          editwin._build_char_in_string_func(startatindex))
++                          editwin.build_char_in_string_func(startatindex))
+                 if bod is not None or startat == 1:
+                     break
+             parser.set_lo(bod or 0)
+Index: ColorDelegator.py
+===================================================================
+--- ColorDelegator.py	(revision 63995)
++++ ColorDelegator.py	(revision 65541)
+@@ -1,8 +1,8 @@
++import re
+ import time
+-import re
+ import keyword
+ import __builtin__
+-from Tkinter import *
++
+ from Delegator import Delegator
+ from configHandler import idleConf
+ 
+@@ -182,8 +182,11 @@
+             ok = False
+             while not ok:
+                 mark = next
+-                next = self.index(mark + "+%d lines linestart" %
+-                                         lines_to_get)
++                # XXX self could be None here in some cases.
++                #     I can reproduce it by clicking "Apply" then "Ok"
++                #     quickly in the config dialog (while editing a not so
++                #     small file).
++                next = self.index(mark + "+%d lines linestart" % lines_to_get)
+                 lines_to_get = min(lines_to_get * 2, 100)
+                 ok = "SYNC" in self.tag_names(next + "-1c")
+                 line = self.get(mark, next)
+@@ -248,6 +251,7 @@
+             self.tag_remove(tag, "1.0", "end")
+ 
+ def main():
++    from Tkinter import Tk, Text
+     from Percolator import Percolator
+     root = Tk()
+     root.wm_protocol("WM_DELETE_WINDOW", root.quit)
+Index: configSectionNameDialog.py
+===================================================================
+--- configSectionNameDialog.py	(revision 63995)
++++ configSectionNameDialog.py	(revision 65541)
+@@ -2,96 +2,111 @@
+ Dialog that allows user to specify a new config file section name.
+ Used to get new highlight theme and keybinding set names.
+ """
+-from Tkinter import *
++from Tkinter import Toplevel, Entry, Frame, Label, Button, StringVar
++from Tkconstants import TOP, BOTTOM, RIGHT, BOTH, SUNKEN, X
+ import tkMessageBox
+ 
++from configHandler import idleConf
++
++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int')
++if TTK:
++    from ttk import Entry, Frame, Label, Button
++
+ class GetCfgSectionNameDialog(Toplevel):
+-    def __init__(self,parent,title,message,usedNames):
++    def __init__(self, parent, title, message, usedNames):
+         """
+         message - string, informational message to display
+         usedNames - list, list of names already in use for validity check
+         """
+         Toplevel.__init__(self, parent)
+         self.configure(borderwidth=5)
+-        self.resizable(height=FALSE,width=FALSE)
++        self.resizable(height=False, width=False)
+         self.title(title)
+         self.transient(parent)
+         self.grab_set()
+         self.protocol("WM_DELETE_WINDOW", self.Cancel)
+         self.parent = parent
+-        self.message=message
+-        self.usedNames=usedNames
+-        self.result=''
++        self.message = message
++        self.usedNames = usedNames
++        self.result = ''
+         self.CreateWidgets()
+         self.withdraw() #hide while setting geometry
+         self.update_idletasks()
+-        #needs to be done here so that the winfo_reqwidth is valid
+-        self.messageInfo.config(width=self.frameMain.winfo_reqwidth())
+         self.geometry("+%d+%d" %
+-            ((parent.winfo_rootx()+((parent.winfo_width()/2)
+-                -(self.winfo_reqwidth()/2)),
+-              parent.winfo_rooty()+((parent.winfo_height()/2)
+-                -(self.winfo_reqheight()/2)) )) ) #centre dialog over parent
++            ((parent.winfo_rootx() + ((parent.winfo_width() / 2)
++                - (self.winfo_reqwidth() / 2)),
++              parent.winfo_rooty() + ((parent.winfo_height() / 2)
++                - (self.winfo_reqheight() / 2)) )) ) #centre dialog over parent
+         self.deiconify() #geometry set, unhide
+         self.wait_window()
+ 
+     def CreateWidgets(self):
+-        self.name=StringVar(self)
+-        self.fontSize=StringVar(self)
+-        self.frameMain = Frame(self,borderwidth=2,relief=SUNKEN)
+-        self.frameMain.pack(side=TOP,expand=TRUE,fill=BOTH)
+-        self.messageInfo=Message(self.frameMain,anchor=W,justify=LEFT,padx=5,pady=5,
+-                text=self.message)#,aspect=200)
+-        entryName=Entry(self.frameMain,textvariable=self.name,width=30)
++        self.name = StringVar(self)
++        self.fontSize = StringVar(self)
++        self.frameMain = Frame(self, borderwidth=2, relief=SUNKEN)
++        self.messageInfo = Label(self.frameMain, text=self.message)
++        entryName = Entry(self.frameMain, textvariable=self.name, width=30)
++        frameButtons = Frame(self)
++        self.buttonOk = Button(frameButtons, text='Ok', command=self.Ok)
++        self.buttonCancel = Button(frameButtons, text='Cancel',
++                command=self.Cancel)
++
+         entryName.focus_set()
+-        self.messageInfo.pack(padx=5,pady=5)#,expand=TRUE,fill=BOTH)
+-        entryName.pack(padx=5,pady=5)
+-        frameButtons=Frame(self)
+-        frameButtons.pack(side=BOTTOM,fill=X)
+-        self.buttonOk = Button(frameButtons,text='Ok',
+-                width=8,command=self.Ok)
+-        self.buttonOk.grid(row=0,column=0,padx=5,pady=5)
+-        self.buttonCancel = Button(frameButtons,text='Cancel',
+-                width=8,command=self.Cancel)
+-        self.buttonCancel.grid(row=0,column=1,padx=5,pady=5)
+ 
++        self.frameMain.pack(side=TOP, expand=True, fill=BOTH)
++        self.messageInfo.pack(padx=5, pady=5)
++        entryName.pack(padx=5, pady=5)
++        frameButtons.pack(side=BOTTOM, fill=X)
++        self.buttonOk.pack(padx=1, pady=5, side=RIGHT)
++        self.buttonCancel.pack(pady=5, padx=5, side=RIGHT)
++
++        if TTK:
++            self.messageInfo['padding'] = 5
++            frameButtons['style'] = 'RootColor.TFrame'
++        else:
++            self.messageInfo.configure(padx=5, pady=5)
++
+     def NameOk(self):
+         #simple validity check for a sensible
+         #ConfigParser file section name
+-        nameOk=1
+-        name=self.name.get()
++        nameOk = 1
++        name = self.name.get()
+         name.strip()
++
+         if not name: #no name specified
+             tkMessageBox.showerror(title='Name Error',
+-                    message='No name specified.', parent=self)
+-            nameOk=0
+-        elif len(name)>30: #name too long
++                message='No name specified.', parent=self)
++            nameOk = 0
++        elif len(name) > 30: #name too long
+             tkMessageBox.showerror(title='Name Error',
+-                    message='Name too long. It should be no more than '+
+-                    '30 characters.', parent=self)
++                message=('Name too long. It should be no more than '
++                         '30 characters.'), parent=self)
+             nameOk=0
+         elif name in self.usedNames:
+             tkMessageBox.showerror(title='Name Error',
+                     message='This name is already in use.', parent=self)
+             nameOk=0
++
+         return nameOk
+ 
+     def Ok(self, event=None):
+         if self.NameOk():
+-            self.result=self.name.get().strip()
++            self.result = self.name.get().strip()
+             self.destroy()
+ 
+     def Cancel(self, event=None):
+-        self.result=''
++        self.result = ''
+         self.destroy()
+ 
+ if __name__ == '__main__':
++    from Tkinter import Tk
+     #test the dialog
+-    root=Tk()
+     def run():
+-        keySeq=''
+-        dlg=GetCfgSectionNameDialog(root,'Get Name',
+-                'The information here should need to be word wrapped. Test.')
++        keySeq = ''
++        dlg = GetCfgSectionNameDialog(root, 'Get Name',
++            'The information here should need to be word wrapped. Test.', [])
+         print dlg.result
+-    Button(root,text='Dialog',command=run).pack()
++
++    root=Tk()
++    Button(root, text='Dialog', command=run).pack()
+     root.mainloop()
+Index: stylist.py
+===================================================================
+--- stylist.py	(revision 0)
++++ stylist.py	(revision 65541)
+@@ -0,0 +1,29 @@
++from configHandler import idleConf
++
++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int')
++
++class PoorManStyle(object):
++    def __init__(self, parent, styles=None, cfgstyles=None):
++        self.parent = parent
++        self.cfgstyles = cfgstyles
++        self.styles = styles
++
++    def configure(self, style, lookup=None, background=None):
++        if style not in self.cfgstyles: # passed wrong style probably
++            return
++
++        widget = getattr(self.parent, self.cfgstyles[style])
++        if lookup:
++            return widget.cget('bg')
++
++        widget.configure(bg=background)
++
++    def style_it(self, w, style):
++        if TTK:
++            w['style'] = style
++            return
++
++        if not style in self.styles: # may not need to be styled
++            return
++
++        w.configure(**self.styles[style])
+Index: ZoomHeight.py
+===================================================================
+--- ZoomHeight.py	(revision 63995)
++++ ZoomHeight.py	(revision 65541)
+@@ -12,11 +12,11 @@
+          ])
+     ]
+ 
+-    def __init__(self, editwin):
+-        self.editwin = editwin
++    def __init__(self, editpage):
++        self.editpage = editpage
+ 
+     def zoom_height_event(self, event):
+-        top = self.editwin.top
++        top = self.editpage.editwin.top
+         zoom_height(top)
+ 
+ def zoom_height(top):
+Index: PyShell.py
+===================================================================
+--- PyShell.py	(revision 63995)
++++ PyShell.py	(revision 65541)
+@@ -1,40 +1,50 @@
+ #! /usr/bin/env python
+-
+ import os
+-import os.path
++import re
+ import sys
++import time
++import types
+ import string
+ import getopt
+-import re
+ import socket
+-import time
++import linecache
+ import threading
+-import traceback
+-import types
+-import macosxSupport
+-
+-import linecache
+ from code import InteractiveInterpreter
+ 
+ try:
+-    from Tkinter import *
++    from Tkinter import Tk, Toplevel, TclError
++    from Tkconstants import END
+ except ImportError:
+     print>>sys.__stderr__, "** IDLE can't import Tkinter.  " \
+                            "Your Python may not be configured for Tk. **"
+     sys.exit(1)
++try:
++    from ttk import Style
++    TTK = 1
++except ImportError:
++    print >> sys.stderr, "** IDLE can't import ttk."
++    TTK = 0
++
+ import tkMessageBox
+ 
+-from EditorWindow import EditorWindow, fixwordbreaks
+-from FileList import FileList
+-from ColorDelegator import ColorDelegator
+-from UndoDelegator import UndoDelegator
+-from OutputWindow import OutputWindow
++import macosxSupport
+ from configHandler import idleConf
+-import idlever
+ 
++# store ttk availability
++idleConf.SetOption('main', 'General', 'use-ttk', str(TTK))
++idleConf.SaveUserCfgFiles()
++
+ import rpc
++import utils
++import idlever
+ import Debugger
++import IOBinding
+ import RemoteDebugger
++from FileList import FileList
++from OutputWindow import OutputWindow
++from EditorWindow import EditorWindow, fixwordbreaks
++from UndoDelegator import UndoDelegator
++from ColorDelegator import ColorDelegator
+ 
+ IDENTCHARS = string.ascii_letters + string.digits + "_"
+ LOCALHOST = '127.0.0.1'
+@@ -55,18 +65,20 @@
+ except ImportError:
+     pass
+ else:
+-    def idle_showwarning(message, category, filename, lineno):
++    def idle_showwarning(message, category, filename, lineno, line=None):
+         file = warning_stream
+         try:
+-            file.write(warnings.formatwarning(message, category, filename, lineno))
++            file.write(warnings.formatwarning(message, category, filename,
++                lineno, line))
+         except IOError:
+             pass  ## file (probably __stderr__) is invalid, warning dropped.
+     warnings.showwarning = idle_showwarning
+-    def idle_formatwarning(message, category, filename, lineno):
++    def idle_formatwarning(message, category, filename, lineno, line=None):
+         """Format warnings the IDLE way"""
+         s = "\nWarning (from warnings module):\n"
+         s += '  File \"%s\", line %s\n' % (filename, lineno)
+-        line = linecache.getline(filename, lineno).strip()
++        if line is None:
++            line = linecache.getline(filename, lineno).strip()
+         if line:
+             s += "    %s\n" % line
+         s += "%s: %s\n>>> " % (category.__name__, message)
+@@ -100,58 +112,67 @@
+ class PyShellEditorWindow(EditorWindow):
+     "Regular text edit window in IDLE, supports breakpoints"
+ 
++    rmenu_specs = [("Set Breakpoint", "<<set-breakpoint-here>>"),
++                   ("Clear Breakpoint", "<<clear-breakpoint-here>>")]
++
+     def __init__(self, *args):
+-        self.breakpoints = []
+         EditorWindow.__init__(self, *args)
+-        self.text.bind("<<set-breakpoint-here>>", self.set_breakpoint_here)
+-        self.text.bind("<<clear-breakpoint-here>>", self.clear_breakpoint_here)
+-        self.text.bind("<<open-python-shell>>", self.flist.open_shell)
+ 
+         self.breakpointPath = os.path.join(idleConf.GetUserCfgDir(),
+-                                           'breakpoints.lst')
++            'breakpoints.lst')
++        self.top.bind('<<tab-created>>', self.__configure_new_tab)
++        self.__configure_new_tab()
++
++    def __configure_new_tab(self, event=None):
++        page = self.text_notebook.last_page().editpage
++        page.breakpoints = []
++        text = page.text
++
++        text.bind("<<set-breakpoint-here>>",
++            utils.callback(self._set_breakpoint_here, text, page))
++        text.bind("<<clear-breakpoint-here>>",
++            utils.callback(self._clear_breakpoint_here, text, page))
++        text.bind("<<open-python-shell>>", self.flist.open_shell)
++
+         # whenever a file is changed, restore breakpoints
+-        if self.io.filename: self.restore_file_breaks()
+-        def filename_changed_hook(old_hook=self.io.filename_change_hook,
++        if page.io.filename:
++            self.restore_file_breaks(text, page)
++
++        def filename_changed_hook(old_hook=page.io.filename_change_hook,
+                                   self=self):
+-            self.restore_file_breaks()
++            self.restore_file_breaks(text, page)
+             old_hook()
+-        self.io.set_filename_change_hook(filename_changed_hook)
++        page.io.set_filename_change_hook(filename_changed_hook)
+ 
+-    rmenu_specs = [("Set Breakpoint", "<<set-breakpoint-here>>"),
+-                   ("Clear Breakpoint", "<<clear-breakpoint-here>>")]
+-
+-    def set_breakpoint(self, lineno):
+-        text = self.text
+-        filename = self.io.filename
++    def set_breakpoint(self, lineno, text, page):
++        filename = page.io.filename
+         text.tag_add("BREAK", "%d.0" % lineno, "%d.0" % (lineno+1))
+         try:
+-            i = self.breakpoints.index(lineno)
++            i = page.breakpoints.index(lineno)
+         except ValueError:  # only add if missing, i.e. do once
+-            self.breakpoints.append(lineno)
++            page.breakpoints.append(lineno)
+         try:    # update the subprocess debugger
+             debug = self.flist.pyshell.interp.debugger
+             debug.set_breakpoint_here(filename, lineno)
+         except: # but debugger may not be active right now....
+             pass
+ 
+-    def set_breakpoint_here(self, event=None):
+-        text = self.text
+-        filename = self.io.filename
++    def _set_breakpoint_here(self, event=None, text=None, page=None):
++        filename = page.io.filename
+         if not filename:
+             text.bell()
+             return
+         lineno = int(float(text.index("insert")))
+-        self.set_breakpoint(lineno)
++        self.set_breakpoint(lineno, text, page)
+ 
+-    def clear_breakpoint_here(self, event=None):
+-        text = self.text
+-        filename = self.io.filename
++    def _clear_breakpoint_here(self, event=None, text=None, page=None):
++        filename = page.io.filename
+         if not filename:
+             text.bell()
+             return
+         lineno = int(float(text.index("insert")))
+         try:
+-            self.breakpoints.remove(lineno)
++            page.breakpoints.remove(lineno)
+         except:
+             pass
+         text.tag_remove("BREAK", "insert linestart",\
+@@ -163,13 +184,17 @@
+             pass
+ 
+     def clear_file_breaks(self):
+-        if self.breakpoints:
+-            text = self.text
+-            filename = self.io.filename
++        for page in self.text_notebook.pages.itervalues():
++            page = page.editpage
++            text = page.text
++            filename = page.io.filename
++
++            if not page.breakpoints:
++                continue
+             if not filename:
+                 text.bell()
+-                return
+-            self.breakpoints = []
++                continue
++            page.breakpoints = []
+             text.tag_remove("BREAK", "1.0", END)
+             try:
+                 debug = self.flist.pyshell.interp.debugger
+@@ -177,7 +202,7 @@
+             except:
+                 pass
+ 
+-    def store_file_breaks(self):
++    def store_file_breaks(self, page):
+         "Save breakpoints when file is saved"
+         # XXX 13 Dec 2002 KBK Currently the file must be saved before it can
+         #     be run.  The breaks are saved at that time.  If we introduce
+@@ -200,8 +225,8 @@
+         #     debugger is loaded) is updated during the save, the visible
+         #     breaks stay synched with the subprocess even if one of these
+         #     unexpected breakpoint deletions occurs.
+-        breaks = self.breakpoints
+-        filename = self.io.filename
++        breaks = page.breakpoints
++        filename = page.io.filename
+         try:
+             lines = open(self.breakpointPath,"r").readlines()
+         except IOError:
+@@ -210,49 +235,42 @@
+         for line in lines:
+             if not line.startswith(filename + '='):
+                 new_file.write(line)
+-        self.update_breakpoints()
+-        breaks = self.breakpoints
++        self.update_breakpoints(page)
++        breaks = page.breakpoints
+         if breaks:
+             new_file.write(filename + '=' + str(breaks) + '\n')
+         new_file.close()
+ 
+-    def restore_file_breaks(self):
+-        self.text.update()   # this enables setting "BREAK" tags to be visible
+-        filename = self.io.filename
++    def restore_file_breaks(self, text, page):
++        text.update()   # this enables setting "BREAK" tags to be visible
++        filename = page.io.filename
+         if filename is None:
+             return
+         if os.path.isfile(self.breakpointPath):
+-            lines = open(self.breakpointPath,"r").readlines()
++            lines = open(self.breakpointPath, "r").readlines()
+             for line in lines:
+                 if line.startswith(filename + '='):
+                     breakpoint_linenumbers = eval(line[len(filename)+1:])
+                     for breakpoint_linenumber in breakpoint_linenumbers:
+-                        self.set_breakpoint(breakpoint_linenumber)
++                        self.set_breakpoint(breakpoint_linenumber, text, page)
+ 
+-    def update_breakpoints(self):
+-        "Retrieves all the breakpoints in the current window"
+-        text = self.text
++    def update_breakpoints(self, page):
++        "Retrieves all the breakpoints in the current page"
++        text = page.text
+         ranges = text.tag_ranges("BREAK")
+         linenumber_list = self.ranges_to_linenumbers(ranges)
+-        self.breakpoints = linenumber_list
++        page.breakpoints = linenumber_list
+ 
+     def ranges_to_linenumbers(self, ranges):
+         lines = []
+         for index in range(0, len(ranges), 2):
+-            lineno = int(float(ranges[index]))
+-            end = int(float(ranges[index+1]))
++            lineno = int(float(str(ranges[index])))
++            end = int(float(str(ranges[index+1])))
+             while lineno < end:
+                 lines.append(lineno)
+                 lineno += 1
+         return lines
+ 
+-# XXX 13 Dec 2002 KBK Not used currently
+-#    def saved_change_hook(self):
+-#        "Extend base method - clear breaks if module is modified"
+-#        if not self.get_saved():
+-#            self.clear_file_breaks()
+-#        EditorWindow.saved_change_hook(self)
+-
+     def _close(self):
+         "Extend base method - clear breaks when module is closed"
+         self.clear_file_breaks()
+@@ -589,7 +607,6 @@
+         self.save_warnings_filters = warnings.filters[:]
+         warnings.filterwarnings(action="error", category=SyntaxWarning)
+         if isinstance(source, types.UnicodeType):
+-            import IOBinding
+             try:
+                 source = source.encode(IOBinding.encoding)
+             except UnicodeError:
+@@ -814,30 +831,47 @@
+             flist = PyShellFileList(root)
+         #
+         OutputWindow.__init__(self, flist, None, None)
++        # remove things related to tabs. The window running the shell is
++        # considered special enough to have a single window for it.
++
++        # remove tabs area
++        if TTK:
++            self.text_notebook['style'] = 'PyShell.TNotebook'
++            style = Style(self.top)
++            style.layout('PyShell.TNotebook.Tab', [('null', '')])
++        else:
++            self.text_notebook._tab_set.grid_forget()
++
++        # remove commands related to tab
++        if 'file' in self.menudict:
++            menu = self.menudict['file']
++            curr_entry = None
++            i = 0
++            while True:
++                last_entry, curr_entry = curr_entry, menu.entryconfigure(i)
++                if last_entry == curr_entry:
++                    break
++
++                if 'label' in curr_entry and 'Tab' in curr_entry['label'][-1]:
++                    if 'New' in ' '.join(curr_entry['label'][-1]):
++                        menu.delete(i)
++                i += 1
++        self.text.unbind('<<new-tab>>')
++        #self.text.unbind('<<close-tab>>')
++
+         #
+-##        self.config(usetabs=1, indentwidth=8, context_use_ps1=1)
+         self.usetabs = True
+         # indentwidth must be 8 when using tabs.  See note in EditorWindow:
+         self.indentwidth = 8
+         self.context_use_ps1 = True
+         #
+-        text = self.text
+-        text.configure(wrap="char")
+-        text.bind("<<newline-and-indent>>", self.enter_callback)
+-        text.bind("<<plain-newline-and-indent>>", self.linefeed_callback)
+-        text.bind("<<interrupt-execution>>", self.cancel_callback)
+-        text.bind("<<end-of-file>>", self.eof_callback)
+-        text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
+-        text.bind("<<toggle-debugger>>", self.toggle_debugger)
+-        text.bind("<<toggle-jit-stack-viewer>>", self.toggle_jit_stack_viewer)
+-        if use_subprocess:
+-            text.bind("<<view-restart>>", self.view_restart_mark)
+-            text.bind("<<restart-shell>>", self.restart_shell)
+-        #
++        self.wtext = None
++        self.textpage = None
++        self.__configure_new_tab()
++
+         self.save_stdout = sys.stdout
+         self.save_stderr = sys.stderr
+         self.save_stdin = sys.stdin
+-        import IOBinding
+         self.stdout = PseudoFile(self, "stdout", IOBinding.encoding)
+         self.stderr = PseudoFile(self, "stderr", IOBinding.encoding)
+         self.console = PseudoFile(self, "console", IOBinding.encoding)
+@@ -845,11 +879,37 @@
+             sys.stdout = self.stdout
+             sys.stderr = self.stderr
+             sys.stdin = self
+-        #
+-        self.history = self.History(self.text)
+-        #
++
+         self.pollinterval = 50  # millisec
+ 
++        if TTK:
++            self.set_theme(Style(self.root))
++
++    def __configure_new_tab(self):
++        # select the last page (the one added)
++        page = self.text_notebook.last_page().editpage
++        self.wtext = text = page.text
++        self.textpage = page
++
++        text.configure(wrap="char")
++        text.bind("<<newline-and-indent>>",
++            utils.callback(self._enter_callback, text))
++        text.bind("<<plain-newline-and-indent>>",
++            utils.callback(self._linefeed_callback, text))
++        text.bind("<<interrupt-execution>>",
++            utils.callback(self._cancel_callback, text))
++        text.bind("<<end-of-file>>",
++            utils.callback(self._eof_callback, text))
++        text.bind("<<toggle-debugger>>", self._toggle_debugger)
++        text.bind("<<toggle-jit-stack-viewer>>", self._toggle_jit_stack_viewer)
++        text.bind("<<open-stack-viewer>>", self.open_stack_viewer)
++        if use_subprocess:
++            text.bind("<<view-restart>>",
++                utils.callback(self._view_restart_mark, text))
++            text.bind("<<restart-shell>>", self.restart_shell)
++
++        page.history = self.History(text)
++
+     def get_standard_extension_names(self):
+         return idleConf.GetExtensions(shell_only=True)
+ 
+@@ -866,11 +926,11 @@
+     def get_warning_stream(self):
+         return warning_stream
+ 
+-    def toggle_debugger(self, event=None):
++    def _toggle_debugger(self, event=None):
+         if self.executing:
+             tkMessageBox.showerror("Don't debug now",
+                 "You can only toggle the debugger when idle",
+-                master=self.text)
++                master=self.text_notebook)
+             self.set_debugger_indicator()
+             return "break"
+         else:
+@@ -884,7 +944,7 @@
+         db = self.interp.getdebugger()
+         self.setvar("<<toggle-debugger>>", not not db)
+ 
+-    def toggle_jit_stack_viewer(self, event=None):
++    def _toggle_jit_stack_viewer(self, event=None):
+         pass # All we need is the variable
+ 
+     def close_debugger(self):
+@@ -930,7 +990,7 @@
+                 "Kill?",
+                 "The program is still running!\n Do you want to kill it?",
+                 default="ok",
+-                parent=self.text)
++                parent=self.text_notebook)
+             if response is False:
+                 return "cancel"
+         if self.reading:
+@@ -938,7 +998,7 @@
+         self.canceled = True
+         self.closing = True
+         # Wait for poll_subprocess() rescheduling to stop
+-        self.text.after(2 * self.pollinterval, self.close2)
++        self.text_notebook.after(2 * self.pollinterval, self.close2)
+ 
+     def close2(self):
+         return EditorWindow.close(self)
+@@ -956,7 +1016,9 @@
+         self.interp = None
+         self.console = None
+         self.flist.pyshell = None
+-        self.history = None
++        for page in self.text_notebook.pages.itervalues():
++            page.editpage.history = None
++
+         EditorWindow._close(self)
+ 
+     def ispythonsource(self, filename):
+@@ -996,38 +1058,12 @@
+         Tkinter._default_root = None # 03Jan04 KBK What's this?
+         return True
+ 
+-    def readline(self):
+-        save = self.reading
+-        try:
+-            self.reading = 1
+-            self.top.mainloop()  # nested mainloop()
+-        finally:
+-            self.reading = save
+-        line = self.text.get("iomark", "end-1c")
+-        if len(line) == 0:  # may be EOF if we quit our mainloop with Ctrl-C
+-            line = "\n"
+-        if isinstance(line, unicode):
+-            import IOBinding
+-            try:
+-                line = line.encode(IOBinding.encoding)
+-            except UnicodeError:
+-                pass
+-        self.resetoutput()
+-        if self.canceled:
+-            self.canceled = 0
+-            if not use_subprocess:
+-                raise KeyboardInterrupt
+-        if self.endoffile:
+-            self.endoffile = 0
+-            line = ""
+-        return line
+-
+     def isatty(self):
+         return True
+ 
+-    def cancel_callback(self, event=None):
++    def _cancel_callback(self, event=None, text=None):
+         try:
+-            if self.text.compare("sel.first", "!=", "sel.last"):
++            if text.compare("sel.first", "!=", "sel.last"):
+                 return # Active selection -- always use default binding
+         except:
+             pass
+@@ -1047,11 +1083,11 @@
+             self.top.quit()  # exit the nested mainloop() in readline()
+         return "break"
+ 
+-    def eof_callback(self, event):
++    def _eof_callback(self, event, text):
+         if self.executing and not self.reading:
+             return # Let the default binding (delete next char) take over
+-        if not (self.text.compare("iomark", "==", "insert") and
+-                self.text.compare("insert", "==", "end-1c")):
++        if not (text.compare("iomark", "==", "insert") and
++                text.compare("insert", "==", "end-1c")):
+             return # Let the default binding (delete next char) take over
+         if not self.executing:
+             self.resetoutput()
+@@ -1062,24 +1098,24 @@
+             self.top.quit()
+         return "break"
+ 
+-    def linefeed_callback(self, event):
++    def _linefeed_callback(self, event, text):
+         # Insert a linefeed without entering anything (still autoindented)
+         if self.reading:
+-            self.text.insert("insert", "\n")
+-            self.text.see("insert")
++            text.insert("insert", "\n")
++            text.see("insert")
+         else:
+             self.newline_and_indent_event(event)
+         return "break"
+ 
+-    def enter_callback(self, event):
++    def _enter_callback(self, event, text):
+         if self.executing and not self.reading:
+             return # Let the default binding (insert '\n') take over
+         # If some text is selected, recall the selection
+         # (but only if this before the I/O mark)
+         try:
+-            sel = self.text.get("sel.first", "sel.last")
++            sel = text.get("sel.first", "sel.last")
+             if sel:
+-                if self.text.compare("sel.last", "<=", "iomark"):
++                if text.compare("sel.last", "<=", "iomark"):
+                     self.recall(sel, event)
+                     return "break"
+         except:
+@@ -1087,51 +1123,52 @@
+         # If we're strictly before the line containing iomark, recall
+         # the current line, less a leading prompt, less leading or
+         # trailing whitespace
+-        if self.text.compare("insert", "<", "iomark linestart"):
++        if text.compare("insert", "<", "iomark linestart"):
+             # Check if there's a relevant stdin range -- if so, use it
+-            prev = self.text.tag_prevrange("stdin", "insert")
+-            if prev and self.text.compare("insert", "<", prev[1]):
+-                self.recall(self.text.get(prev[0], prev[1]), event)
++            prev = text.tag_prevrange("stdin", "insert")
++            if prev and text.compare("insert", "<", prev[1]):
++                self.recall(text.get(prev[0], prev[1]), event)
+                 return "break"
+-            next = self.text.tag_nextrange("stdin", "insert")
+-            if next and self.text.compare("insert lineend", ">=", next[0]):
+-                self.recall(self.text.get(next[0], next[1]), event)
++            next = text.tag_nextrange("stdin", "insert")
++            if next and text.compare("insert lineend", ">=", next[0]):
++                self.recall(text.get(next[0], next[1]), event)
+                 return "break"
+             # No stdin mark -- just get the current line, less any prompt
+-            indices = self.text.tag_nextrange("console", "insert linestart")
++            indices = text.tag_nextrange("console", "insert linestart")
+             if indices and \
+-               self.text.compare(indices[0], "<=", "insert linestart"):
+-                self.recall(self.text.get(indices[1], "insert lineend"), event)
++               text.compare(indices[0], "<=", "insert linestart"):
++                self.recall(text.get(indices[1], "insert lineend"), event)
+             else:
+-                self.recall(self.text.get("insert linestart", "insert lineend"), event)
++                self.recall(text.get("insert linestart", "insert lineend"),
++                    event)
+             return "break"
+         # If we're between the beginning of the line and the iomark, i.e.
+         # in the prompt area, move to the end of the prompt
+-        if self.text.compare("insert", "<", "iomark"):
+-            self.text.mark_set("insert", "iomark")
++        if text.compare("insert", "<", "iomark"):
++            text.mark_set("insert", "iomark")
+         # If we're in the current input and there's only whitespace
+         # beyond the cursor, erase that whitespace first
+-        s = self.text.get("insert", "end-1c")
++        s = text.get("insert", "end-1c")
+         if s and not s.strip():
+-            self.text.delete("insert", "end-1c")
++            text.delete("insert", "end-1c")
+         # If we're in the current input before its last line,
+         # insert a newline right at the insert point
+-        if self.text.compare("insert", "<", "end-1c linestart"):
++        if text.compare("insert", "<", "end-1c linestart"):
+             self.newline_and_indent_event(event)
+             return "break"
+         # We're in the last line; append a newline and submit it
+-        self.text.mark_set("insert", "end-1c")
++        text.mark_set("insert", "end-1c")
+         if self.reading:
+-            self.text.insert("insert", "\n")
+-            self.text.see("insert")
++            text.insert("insert", "\n")
++            text.see("insert")
+         else:
+             self.newline_and_indent_event(event)
+-        self.text.tag_add("stdin", "iomark", "end-1c")
+-        self.text.update_idletasks()
++        text.tag_add("stdin", "iomark", "end-1c")
++        text.update_idletasks()
+         if self.reading:
+             self.top.quit() # Break out of recursive mainloop() in raw_input()
+         else:
+-            self.runit()
++            self._runit(text)
+         return "break"
+ 
+     def recall(self, s, event):
+@@ -1139,15 +1176,15 @@
+         s = re.sub(r'^\s*\n', '' , s)
+         s = re.sub(r'\n\s*$', '', s)
+         lines = s.split('\n')
+-        self.text.undo_block_start()
++        self.wtext.undo_block_start()
+         try:
+-            self.text.tag_remove("sel", "1.0", "end")
+-            self.text.mark_set("insert", "end-1c")
+-            prefix = self.text.get("insert linestart", "insert")
++            self.wtext.tag_remove("sel", "1.0", "end")
++            self.wtext.mark_set("insert", "end-1c")
++            prefix = self.wtext.get("insert linestart", "insert")
+             if prefix.rstrip().endswith(':'):
+                 self.newline_and_indent_event(event)
+-                prefix = self.text.get("insert linestart", "insert")
+-            self.text.insert("insert", lines[0].strip())
++                prefix = self.wtext.get("insert linestart", "insert")
++            self.wtext.insert("insert", lines[0].strip())
+             if len(lines) > 1:
+                 orig_base_indent = re.search(r'^([ \t]*)', lines[0]).group(0)
+                 new_base_indent  = re.search(r'^([ \t]*)', prefix).group(0)
+@@ -1155,13 +1192,13 @@
+                     if line.startswith(orig_base_indent):
+                         # replace orig base indentation with new indentation
+                         line = new_base_indent + line[len(orig_base_indent):]
+-                    self.text.insert('insert', '\n'+line.rstrip())
++                    self.wtext.insert('insert', '\n'+line.rstrip())
+         finally:
+-            self.text.see("insert")
+-            self.text.undo_block_stop()
++            self.wtext.see("insert")
++            self.wtext.undo_block_stop()
+ 
+-    def runit(self):
+-        line = self.text.get("iomark", "end-1c")
++    def _runit(self, text):
++        line = text.get("iomark", "end-1c")
+         # Strip off last newline and surrounding whitespace.
+         # (To allow you to hit return twice to end a statement.)
+         i = len(line)
+@@ -1179,18 +1216,18 @@
+             return self.interp.remote_stack_viewer()
+         try:
+             sys.last_traceback
+-        except:
++        except AttributeError:
+             tkMessageBox.showerror("No stack trace",
+                 "There is no stack trace yet.\n"
+                 "(sys.last_traceback is not defined)",
+-                master=self.text)
++                master=self.text_notebook)
+             return
+         from StackViewer import StackBrowser
+         sv = StackBrowser(self.root, self.flist)
+ 
+-    def view_restart_mark(self, event=None):
+-        self.text.see("iomark")
+-        self.text.see("restart")
++    def _view_restart_mark(self, event=None, text=None):
++        text.see("iomark")
++        text.see("restart")
+ 
+     def restart_shell(self, event=None):
+         self.interp.restart_subprocess()
+@@ -1202,25 +1239,30 @@
+         except:
+             s = ""
+         self.console.write(s)
+-        self.text.mark_set("insert", "end-1c")
++
++        curr_page = self.current_page
++        if not curr_page:
++            return
++
++        curr_page.text.mark_set("insert", "end-1c")
+         self.set_line_and_column()
+-        self.io.reset_undo()
++        curr_page.io.reset_undo()
+ 
+     def resetoutput(self):
+-        source = self.text.get("iomark", "end-1c")
+-        if self.history:
+-            self.history.history_store(source)
+-        if self.text.get("end-2c") != "\n":
+-            self.text.insert("end-1c", "\n")
+-        self.text.mark_set("iomark", "end-1c")
++        source = self.wtext.get("iomark", "end-1c")
++        if self.textpage.history:
++            self.textpage.history.history_store(source)
++        if self.wtext.get("end-2c") != "\n":
++            self.wtext.insert("end-1c", "\n")
++        self.wtext.mark_set("iomark", "end-1c")
+         self.set_line_and_column()
+         sys.stdout.softspace = 0
+ 
+     def write(self, s, tags=()):
+         try:
+-            self.text.mark_gravity("iomark", "right")
+-            OutputWindow.write(self, s, tags, "iomark")
+-            self.text.mark_gravity("iomark", "left")
++            self.wtext.mark_gravity("iomark", "right")
++            OutputWindow.write(self, s, tags, "iomark", self.wtext)
++            self.wtext.mark_gravity("iomark", "left")
+         except:
+             pass
+         if self.canceled:
+@@ -1381,6 +1423,19 @@
+     # start editor and/or shell windows:
+     root = Tk(className="Idle")
+ 
++    if TTK:
++        # create base styles used along idle files
++        style = Style()
++
++        rootbg = style.map('.', 'background')
++        fstyle = {'background': []}
++        for sspec in rootbg:
++            if 'active' in sspec[:-1]:
++                fstyle['background'].append(('!disabled', sspec[-1]))
++                break
++        style.map('RootColor.TFrame', **fstyle)
++        # end styles
++
+     fixwordbreaks(root)
+     root.withdraw()
+     flist = PyShellFileList(root)
+Index: ParenMatch.py
+===================================================================
+--- ParenMatch.py	(revision 63995)
++++ ParenMatch.py	(revision 65541)
+@@ -56,14 +56,13 @@
+     RESTORE_SEQUENCES = ("<KeyPress>", "<ButtonPress>",
+                          "<Key-Return>", "<Key-BackSpace>")
+ 
+-    def __init__(self, editwin):
+-        self.editwin = editwin
+-        self.text = editwin.text
++    def __init__(self, editpage):
++        self.editpage = editpage
++        self.text = editpage.text
+         # Bind the check-restore event to the function restore_event,
+         # so that we can then use activate_restore (which calls event_add)
+         # and deactivate_restore (which calls event_delete).
+-        editwin.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME,
+-                          self.restore_event)
++        editpage.text.bind(self.RESTORE_VIRTUAL_EVENT_NAME, self.restore_event)
+         self.counter = 0
+         self.is_restore_active = 0
+         self.set_style(self.STYLE)
+@@ -90,7 +89,7 @@
+             self.set_timeout = self.set_timeout_none
+ 
+     def flash_paren_event(self, event):
+-        indices = HyperParser(self.editwin, "insert").get_surrounding_brackets()
++        indices = HyperParser(self.editpage, "insert").get_surrounding_brackets()
+         if indices is None:
+             self.warn_mismatched()
+             return
+@@ -103,7 +102,7 @@
+         closer = self.text.get("insert-1c")
+         if closer not in _openers:
+             return
+-        hp = HyperParser(self.editwin, "insert-1c")
++        hp = HyperParser(self.editpage, "insert-1c")
+         if not hp.is_in_code():
+             return
+         indices = hp.get_surrounding_brackets(_openers[closer], True)
+@@ -159,14 +158,13 @@
+             if index != self.text.index("insert"):
+                 self.handle_restore_timer(c)
+             else:
+-                self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
+-        self.editwin.text_frame.after(CHECK_DELAY, callme, callme)
++                self.text.master.after(CHECK_DELAY, callme, callme)
++        self.text.master.after(CHECK_DELAY, callme, callme)
+ 
+     def set_timeout_last(self):
+         """The last highlight created will be removed after .5 sec"""
+         # associate a counter with an event; only disable the "paren"
+         # tag if the event is for the most recent timer.
+         self.counter += 1
+-        self.editwin.text_frame.after(self.FLASH_DELAY,
+-                                      lambda self=self, c=self.counter: \
+-                                      self.handle_restore_timer(c))
++        self.text.master.after(self.FLASH_DELAY,
++            lambda self=self, c=self.counter: self.handle_restore_timer(c))
+Index: config-keys.def
+===================================================================
+--- config-keys.def	(revision 63995)
++++ config-keys.def	(revision 65541)
+@@ -8,6 +8,10 @@
+ # configuration gui.
+ 
+ [IDLE Classic Windows]
++close-tab=<Control-Key-w> <Control-Key-W>
++new-tab=<Control-Key-t> <Control-Key-T>
++next-tab=<Control-Key-Next>
++prev-tab=<Control-Key-Prior>
+ copy=<Control-Key-c> <Control-Key-C>
+ cut=<Control-Key-x> <Control-Key-X>
+ paste=<Control-Key-v> <Control-Key-V>
+@@ -59,6 +63,10 @@
+ del-word-right=<Control-Key-Delete>
+ 
+ [IDLE Classic Unix]
++close-tab=<Alt-Key-w><Control-Key-w>
++new-tab=<Alt-Key-t><Control-Key-t>
++next-tab=<Control-Key-Next>
++prev-tab=<Control-Key-Prior>
+ copy=<Alt-Key-w> <Meta-Key-w>
+ cut=<Control-Key-w>
+ paste=<Control-Key-y>
+@@ -110,6 +118,10 @@
+ del-word-right=<Alt-Key-d>
+ 
+ [IDLE Classic Mac]
++close-tab=<Control-key-w>
++new-tab=<Control-Key-t>
++next-tab=<Control-Key-Next>
++prev-tab=<Control-Key-Prior>
+ copy=<Command-Key-c>
+ cut=<Command-Key-x>
+ paste=<Command-Key-v>
+@@ -161,6 +173,10 @@
+ del-word-right=<Control-Key-Delete>
+ 
+ [IDLE Classic OSX]
++close-tab=<Control-Key-w>
++new-tab = <Control-Key-t>
++next-tab=<Control-Key-Next>
++prev-tab=<Control-Key-Prior>
+ toggle-tabs = <Control-Key-t>
+ interrupt-execution = <Control-Key-c>
+ untabify-region = <Control-Key-6>
+Index: Debugger.py
+===================================================================
+--- Debugger.py	(revision 63995)
++++ Debugger.py	(revision 65541)
+@@ -1,11 +1,17 @@
+ import os
+ import bdb
+ import types
+-from Tkinter import *
++from Tkinter import Frame, Button, Entry, Checkbutton, Label, Scrollbar, \
++                    Canvas, BooleanVar
++from Tkconstants import W, LEFT, DISABLED, X, Y, BOTH, NW, GROOVE, RIGHT
++
++import macosxSupport
+ from WindowList import ListedToplevel
+ from ScrolledList import ScrolledList
+-import macosxSupport
++from configHandler import idleConf
+ 
++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'):
++    from ttk import Frame, Button, Checkbutton, Scrollbar
+ 
+ class Idb(bdb.Bdb):
+ 
+@@ -92,7 +98,7 @@
+         self.top.bind("<Escape>", self.close)
+         #
+         self.bframe = bframe = Frame(top)
+-        self.bframe.pack(anchor="w")
++        self.bframe.pack(anchor=W)
+         self.buttons = bl = []
+         #
+         self.bcont = b = Button(bframe, text="Go", command=self.cont)
+@@ -107,11 +113,11 @@
+         bl.append(b)
+         #
+         for b in bl:
+-            b.configure(state="disabled")
+-            b.pack(side="left")
++            b.configure(state=DISABLED)
++            b.pack(side=LEFT)
+         #
+         self.cframe = cframe = Frame(bframe)
+-        self.cframe.pack(side="left")
++        self.cframe.pack(side=LEFT)
+         #
+         if not self.vstack:
+             self.__class__.vstack = BooleanVar(top)
+@@ -136,18 +142,18 @@
+             text="Globals", command=self.show_globals, variable=self.vglobals)
+         self.bglobals.grid(row=1, column=1)
+         #
+-        self.status = Label(top, anchor="w")
+-        self.status.pack(anchor="w")
+-        self.error = Label(top, anchor="w")
+-        self.error.pack(anchor="w", fill="x")
++        self.status = Label(top, anchor=W)
++        self.status.pack(anchor=W)
++        self.error = Label(top, anchor=W)
++        self.error.pack(anchor=W, fill=X)
+         self.errorbg = self.error.cget("background")
+         #
+         self.fstack = Frame(top, height=1)
+-        self.fstack.pack(expand=1, fill="both")
++        self.fstack.pack(expand=1, fill=BOTH)
+         self.flocals = Frame(top)
+-        self.flocals.pack(expand=1, fill="both")
++        self.flocals.pack(expand=1, fill=BOTH)
+         self.fglobals = Frame(top, height=1)
+-        self.fglobals.pack(expand=1, fill="both")
++        self.fglobals.pack(expand=1, fill=BOTH)
+         #
+         if self.vstack.get():
+             self.show_stack()
+@@ -155,6 +161,7 @@
+             self.show_locals()
+         if self.vglobals.get():
+             self.show_globals()
++        #
+ 
+     def interaction(self, message, frame, info=None):
+         self.frame = frame
+@@ -313,12 +320,14 @@
+         "Load PyShellEditorWindow breakpoints into subprocess debugger"
+         pyshell_edit_windows = self.pyshell.flist.inversedict.keys()
+         for editwin in pyshell_edit_windows:
+-            filename = editwin.io.filename
+-            try:
+-                for lineno in editwin.breakpoints:
+-                    self.set_breakpoint_here(filename, lineno)
+-            except AttributeError:
+-                continue
++            for page in editwin.text_notebook.pages.itervalues():
++                editpage = page.editpage
++                filename = editpage.io.filename
++                try:
++                    for lineno in editpage.breakpoints:
++                        self.set_breakpoint_here(filename, lineno)
++                except AttributeError:
++                    continue
+ 
+ class StackViewer(ScrolledList):
+ 
+@@ -348,8 +357,7 @@
+             funcname = code.co_name
+             import linecache
+             sourceline = linecache.getline(filename, lineno)
+-            import string
+-            sourceline = string.strip(sourceline)
++            sourceline = sourceline.strip()
+             if funcname in ("?", "", None):
+                 item = "%s, line %d: %s" % (modname, lineno, sourceline)
+             else:
+@@ -413,24 +421,25 @@
+             height = 20*len(dict) # XXX 20 == observed height of Entry widget
+         self.master = master
+         self.title = title
++
+         import repr
+         self.repr = repr.Repr()
+         self.repr.maxstring = 60
+         self.repr.maxother = 60
+         self.frame = frame = Frame(master)
+-        self.frame.pack(expand=1, fill="both")
+-        self.label = Label(frame, text=title, borderwidth=2, relief="groove")
+-        self.label.pack(fill="x")
+-        self.vbar = vbar = Scrollbar(frame, name="vbar")
+-        vbar.pack(side="right", fill="y")
++        self.frame.pack(expand=1, fill=BOTH)
++        self.label = Label(frame, text=title, borderwidth=2, relief=GROOVE)
++        self.label.pack(fill=X)
++        vbar = Scrollbar(frame, name="vbar")
++        vbar.pack(side=RIGHT, fill=Y)
+         self.canvas = canvas = Canvas(frame,
+                                       height=min(300, max(40, height)),
+                                       scrollregion=(0, 0, width, height))
+-        canvas.pack(side="left", fill="both", expand=1)
++        canvas.pack(side=LEFT, fill=BOTH, expand=1)
+         vbar["command"] = canvas.yview
+         canvas["yscrollcommand"] = vbar.set
+         self.subframe = subframe = Frame(canvas)
+-        self.sfid = canvas.create_window(0, 0, window=subframe, anchor="nw")
++        self.sfid = canvas.create_window(0, 0, window=subframe, anchor=NW)
+         self.load_dict(dict)
+ 
+     dict = -1
+@@ -458,10 +467,10 @@
+                 if rpc_client:
+                     svalue = svalue[1:-1]
+                 l = Label(subframe, text=name)
+-                l.grid(row=row, column=0, sticky="nw")
++                l.grid(row=row, column=0, sticky=NW)
+                 l = Entry(subframe, width=0, borderwidth=0)
+                 l.insert(0, svalue)
+-                l.grid(row=row, column=1, sticky="nw")
++                l.grid(row=row, column=1, sticky=NW)
+                 row = row+1
+         self.dict = dict
+         # XXX Could we use a <Configure> callback for the following?
+Index: configDialog.py
+===================================================================
+--- configDialog.py	(revision 63995)
++++ configDialog.py	(revision 65541)
+@@ -7,19 +7,30 @@
+ 
+ Note that tab width in IDLE is currently fixed at eight due to Tk issues.
+ Refer to comments in EditorWindow autoindent code for details.
+-
+ """
+-from Tkinter import *
++from Tkinter import Toplevel, Frame, Button, Scale, Label, LabelFrame, Text, \
++                    Listbox, Scrollbar, Checkbutton, Radiobutton, Entry, \
++                    Checkbutton, StringVar, BooleanVar, IntVar
++from Tkconstants import LEFT, RIGHT, BOTTOM, TOP, BOTH, GROOVE, SOLID, NONE, \
++                        END, DISABLED, NSEW, Y, X, W, E, HORIZONTAL, NS, EW, \
++                        N, ANCHOR, NORMAL
+ import tkMessageBox, tkColorChooser, tkFont
+-import string
+ 
++from stylist import PoorManStyle
++from tabbedpages import get_tabbedpage
+ from configHandler import idleConf
++from keybindingDialog import GetKeysDialog
+ from dynOptionMenuWidget import DynOptionMenu
+-from tabbedpages import TabbedPageSet
+-from keybindingDialog import GetKeysDialog
++from configHelpSourceEdit import GetHelpSourceDialog
+ from configSectionNameDialog import GetCfgSectionNameDialog
+-from configHelpSourceEdit import GetHelpSourceDialog
+ 
++TabbedPageSet = get_tabbedpage()
++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int')
++if TTK:
++    from ttk import Frame, Button, Checkbutton, LabelFrame, LabeledScale, \
++                    Combobox, Checkbutton, Entry, Radiobutton, Scrollbar, \
++                    Label, Style
++
+ class ConfigDialog(Toplevel):
+ 
+     def __init__(self,parent,title):
+@@ -27,8 +38,8 @@
+         self.wm_withdraw()
+ 
+         self.configure(borderwidth=5)
+-        self.geometry("+%d+%d" % (parent.winfo_rootx()+20,
+-                parent.winfo_rooty()+30))
++        self.geometry("+%d+%d" % (parent.winfo_rootx() + 20,
++            parent.winfo_rooty() + 30))
+         #Theme Elements. Each theme element key is its display name.
+         #The first value of the tuple is the sample area tag name.
+         #The second value is the display name list sort index.
+@@ -47,8 +58,9 @@
+             'Shell Stderr Text':('stderr','12'),
+             }
+         self.ResetChangedItems() #load initial values in changed items dict
++        self.SetupStyles()
+         self.CreateWidgets()
+-        self.resizable(height=FALSE,width=FALSE)
++        self.resizable(height=False, width=False)
+         self.transient(parent)
+         self.grab_set()
+         self.protocol("WM_DELETE_WINDOW", self.Cancel)
+@@ -64,34 +76,54 @@
+         self.wm_deiconify()
+         self.wait_window()
+ 
++    def SetupStyles(self):
++        if TTK:
++            style = Style(self.master)
++            style.configure('S.TButton', padding=[6, 3])
++            style.configure('S2.TFrame', padding=2)
++            style.configure('Color.TFrame', background='blue')
++            self.ttkstyle = style
++            self.style = lambda w, style: w.configure(style=style)
++        else:
++            self.ttkstyle = PoorManStyle(self, styles={
++                'S.TButton': {'pady': 6, 'padx': 3},
++                'S2.TFrame': {'padx': 2, 'pady': 2}
++                }, cfgstyles={'Color.TFrame': 'frameColourSet'})
++            self.style = self.ttkstyle.style_it
++
+     def CreateWidgets(self):
+         self.tabPages = TabbedPageSet(self,
+-                page_names=['Fonts/Tabs','Highlighting','Keys','General'])
+-        frameActionButtons = Frame(self,pady=2)
++            page_names=['Fonts/Tabs','Highlighting','Keys','General'])
++        frameActionButtons = Frame(self)
+         #action buttons
+         self.buttonHelp = Button(frameActionButtons,text='Help',
+-                command=self.Help,takefocus=FALSE,
+-                padx=6,pady=3)
+-        self.buttonOk = Button(frameActionButtons,text='Ok',
+-                command=self.Ok,takefocus=FALSE,
+-                padx=6,pady=3)
+-        self.buttonApply = Button(frameActionButtons,text='Apply',
+-                command=self.Apply,takefocus=FALSE,
+-                padx=6,pady=3)
+-        self.buttonCancel = Button(frameActionButtons,text='Cancel',
+-                command=self.Cancel,takefocus=FALSE,
+-                padx=6,pady=3)
++                command=self.Help, takefocus=False)
++        self.buttonOk = Button(frameActionButtons, text='Ok',
++                command=self.Ok, takefocus=False)
++        self.buttonApply = Button(frameActionButtons, text='Apply',
++                command=self.Apply, takefocus=False)
++        self.buttonCancel = Button(frameActionButtons, text='Cancel',
++                command=self.Cancel, takefocus=False)
++
++        # Apply styles
++        s = self.style
++        s(frameActionButtons, 'RootColor.TFrame')
++        s(self.buttonHelp, 'S.TButton')
++        s(self.buttonOk, 'S.TButton')
++        s(self.buttonApply, 'S.TButton')
++        s(self.buttonCancel, 'S.TButton')
++
+         self.CreatePageFontTab()
+         self.CreatePageHighlight()
+         self.CreatePageKeys()
+         self.CreatePageGeneral()
+-        self.buttonHelp.pack(side=RIGHT,padx=5)
+-        self.buttonOk.pack(side=LEFT,padx=5)
+-        self.buttonApply.pack(side=LEFT,padx=5)
+-        self.buttonCancel.pack(side=LEFT,padx=5)
+-        frameActionButtons.pack(side=BOTTOM)
++        self.buttonHelp.pack(side=LEFT, pady=6)
++        self.buttonApply.pack(side=RIGHT, pady=6)
++        self.buttonOk.pack(side=RIGHT, padx=6, pady=6)
++        self.buttonCancel.pack(side=RIGHT, pady=6)
++        frameActionButtons.pack(side=BOTTOM, fill=X, expand=True)
+         Frame(self, height=2, borderwidth=0).pack(side=BOTTOM)
+-        self.tabPages.pack(side=TOP,expand=TRUE,fill=BOTH)
++        self.tabPages.pack(side=TOP,expand=True,fill=BOTH)
+ 
+     def CreatePageFontTab(self):
+         #tkVars
+@@ -113,8 +145,8 @@
+         frameFontParam=Frame(frameFont)
+         labelFontNameTitle=Label(frameFontName,justify=LEFT,
+                 text='Font Face :')
+-        self.listFontName=Listbox(frameFontName,height=5,takefocus=FALSE,
+-                exportselection=FALSE)
++        self.listFontName=Listbox(frameFontName,height=5,takefocus=False,
++                exportselection=False)
+         self.listFontName.bind('<ButtonRelease-1>',self.OnListFontButtonRelease)
+         scrollFont=Scrollbar(frameFontName)
+         scrollFont.config(command=self.listFontName.yview)
+@@ -127,122 +159,143 @@
+         frameFontSample=Frame(frameFont,relief=SOLID,borderwidth=1)
+         self.labelFontSample=Label(frameFontSample,
+                 text='AaBbCcDdEe\nFfGgHhIiJjK\n1234567890\n#:+=(){}[]',
+-                justify=LEFT,font=self.editFont)
++                justify=LEFT, font=self.editFont)
+         #frameIndent
+         frameIndentSize=Frame(frameIndent)
+         labelSpaceNumTitle=Label(frameIndentSize, justify=LEFT,
+                                  text='Python Standard: 4 Spaces!')
+-        self.scaleSpaceNum=Scale(frameIndentSize, variable=self.spaceNum,
+-                                 orient='horizontal',
+-                                 tickinterval=2, from_=2, to=16)
+         #widget packing
+         #body
+-        frameFont.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
++        frameFont.pack(side=LEFT,padx=5,pady=5,expand=True,fill=BOTH)
+         frameIndent.pack(side=LEFT,padx=5,pady=5,fill=Y)
+         #frameFont
+         frameFontName.pack(side=TOP,padx=5,pady=5,fill=X)
+         frameFontParam.pack(side=TOP,padx=5,pady=5,fill=X)
+         labelFontNameTitle.pack(side=TOP,anchor=W)
+-        self.listFontName.pack(side=LEFT,expand=TRUE,fill=X)
++        self.listFontName.pack(side=LEFT,expand=True,fill=X)
+         scrollFont.pack(side=LEFT,fill=Y)
+         labelFontSizeTitle.pack(side=LEFT,anchor=W)
+         self.optMenuFontSize.pack(side=LEFT,anchor=W)
+         checkFontBold.pack(side=LEFT,anchor=W,padx=20)
+-        frameFontSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
+-        self.labelFontSample.pack(expand=TRUE,fill=BOTH)
++        frameFontSample.pack(side=TOP,padx=5,pady=5,expand=True,fill=BOTH)
++        self.labelFontSample.pack(expand=1, fill=Y)
+         #frameIndent
+         frameIndentSize.pack(side=TOP,fill=X)
+-        labelSpaceNumTitle.pack(side=TOP,anchor=W,padx=5)
+-        self.scaleSpaceNum.pack(side=TOP,padx=5,fill=X)
++        labelSpaceNumTitle.pack(side=TOP, anchor=W, padx=5, pady=6)
++
++        if TTK:
++            self.scaleSpaceNum = LabeledScale(frameIndentSize, self.spaceNum,
++                from_=2, to=16, padding=2)
++        else:
++            self.scaleSpaceNum=Scale(frameIndentSize, variable=self.spaceNum,
++                orient='horizontal', from_=2, to=16, tickinterval=2)
++
++        self.scaleSpaceNum.pack(side=TOP, padx=5, fill=X)
++
+         return frame
+ 
+     def CreatePageHighlight(self):
+-        self.builtinTheme=StringVar(self)
+-        self.customTheme=StringVar(self)
+-        self.fgHilite=BooleanVar(self)
+-        self.colour=StringVar(self)
+-        self.fontName=StringVar(self)
+-        self.themeIsBuiltin=BooleanVar(self)
+-        self.highlightTarget=StringVar(self)
++        self.builtinTheme = StringVar(self)
++        self.customTheme = StringVar(self)
++        self.fgHilite = BooleanVar(self)
++        self.colour = StringVar(self)
++        self.fontName = StringVar(self)
++        self.themeIsBuiltin = BooleanVar(self)
++        self.highlightTarget = StringVar(self)
++        #self.themeFontBold = BooleanVar(self)
+         ##widget creation
+         #body frame
+-        frame=self.tabPages.pages['Highlighting'].frame
++        frame = self.tabPages.pages['Highlighting'].frame
+         #body section frames
+-        frameCustom=LabelFrame(frame,borderwidth=2,relief=GROOVE,
+-                               text=' Custom Highlighting ')
+-        frameTheme=LabelFrame(frame,borderwidth=2,relief=GROOVE,
+-                              text=' Highlighting Theme ')
++        frameCustom = LabelFrame(frame, borderwidth=2, relief=GROOVE,
++            text=' Custom Highlighting ')
++        frameTheme = LabelFrame(frame, borderwidth=2, relief=GROOVE,
++            text=' Highlighting Theme ')
+         #frameCustom
+-        self.textHighlightSample=Text(frameCustom,relief=SOLID,borderwidth=1,
+-            font=('courier',12,''),cursor='hand2',width=21,height=10,
+-            takefocus=FALSE,highlightthickness=0,wrap=NONE)
+-        text=self.textHighlightSample
+-        text.bind('<Double-Button-1>',lambda e: 'break')
+-        text.bind('<B1-Motion>',lambda e: 'break')
+-        textAndTags=(('#you can click here','comment'),('\n','normal'),
+-            ('#to choose items','comment'),('\n','normal'),('def','keyword'),
+-            (' ','normal'),('func','definition'),('(param):','normal'),
+-            ('\n  ','normal'),('"""string"""','string'),('\n  var0 = ','normal'),
+-            ("'string'",'string'),('\n  var1 = ','normal'),("'selected'",'hilite'),
+-            ('\n  var2 = ','normal'),("'found'",'hit'),
+-            ('\n  var3 = ','normal'),('list', 'builtin'), ('(','normal'),
+-            ('None', 'builtin'),(')\n\n','normal'),
+-            (' error ','error'),(' ','normal'),('cursor |','cursor'),
+-            ('\n ','normal'),('shell','console'),(' ','normal'),('stdout','stdout'),
+-            (' ','normal'),('stderr','stderr'),('\n','normal'))
++        self.textHighlightSample = Text(frameCustom, relief=SOLID,
++            borderwidth=1, font=('courier', 12, ''), cursor='hand2', width=21,
++            height=11, takefocus=False, highlightthickness=0, wrap=NONE)
++        text = self.textHighlightSample
++        text.bind('<Double-Button-1>', lambda e: 'break')
++        text.bind('<B1-Motion>', lambda e: 'break')
++        textAndTags = (
++            ('#you can click here','comment'), ('\n','normal'),
++            ('#to choose items', 'comment'), ('\n', 'normal'),
++            ('def', 'keyword'), (' ', 'normal'), ('func', 'definition'),
++            ('(param):', 'normal'), ('\n  ', 'normal'),
++            ('"""string"""', 'string'),
++            ('\n  var0 = ','normal'),  ("'string'", 'string'),
++            ('\n  var1 = ', 'normal'), ("'selected'", 'hilite'),
++            ('\n  var2 = ', 'normal'), ("'found'", 'hit'),
++            ('\n  var3 = ', 'normal'), ('list', 'builtin'), ('(', 'normal'),
++            ('None', 'builtin'), (')\n\n', 'normal'),
++            (' error ', 'error'), (' ', 'normal'), ('cursor |', 'cursor'),
++            ('\n ', 'normal'), ('shell', 'console'), (' ', 'normal'),
++            ('stdout', 'stdout'), (' ', 'normal'), ('stderr', 'stderr'),
++            ('\n', 'normal')
++        )
+         for txTa in textAndTags:
+-            text.insert(END,txTa[0],txTa[1])
++            text.insert(END, txTa[0], txTa[1])
+         for element in self.themeElements.keys():
+-            text.tag_bind(self.themeElements[element][0],'<ButtonPress-1>',
+-                lambda event,elem=element: event.widget.winfo_toplevel()
+-                .highlightTarget.set(elem))
++            text.tag_bind(self.themeElements[element][0], '<ButtonPress-1>',
++                lambda event, elem=element:
++                    event.widget.winfo_toplevel().highlightTarget.set(elem))
+         text.config(state=DISABLED)
+-        self.frameColourSet=Frame(frameCustom,relief=SOLID,borderwidth=1)
+-        frameFgBg=Frame(frameCustom)
+-        buttonSetColour=Button(self.frameColourSet,text='Choose Colour for :',
+-            command=self.GetColour,highlightthickness=0)
+-        self.optMenuHighlightTarget=DynOptionMenu(self.frameColourSet,
+-            self.highlightTarget,None,highlightthickness=0)#,command=self.SetHighlightTargetBinding
+-        self.radioFg=Radiobutton(frameFgBg,variable=self.fgHilite,
+-            value=1,text='Foreground',command=self.SetColourSampleBinding)
+-        self.radioBg=Radiobutton(frameFgBg,variable=self.fgHilite,
+-            value=0,text='Background',command=self.SetColourSampleBinding)
++
++        self.frameColourSet = Frame(frameCustom, relief=SOLID, borderwidth=1)
++        self.style(self.frameColourSet, 'Color.TFrame')
++
++        frameFgBg = Frame(frameCustom)
++        buttonSetColour = Button(self.frameColourSet,
++            text='Choose Colour for :', command=self.GetColour)
++        self.optMenuHighlightTarget = DynOptionMenu(self.frameColourSet,
++            self.highlightTarget, None)
++        #self.optBoldText = Checkbutton(self.frameColourSet, text="Bold",
++        #    variable=self.themeFontBold)
++        self.radioFg = Radiobutton(frameFgBg, variable=self.fgHilite,
++            value=1, text='Foreground', command=self.SetColourSampleBinding)
++        self.radioBg = Radiobutton(frameFgBg, variable=self.fgHilite,
++            value=0, text='Background', command=self.SetColourSampleBinding)
+         self.fgHilite.set(1)
+-        buttonSaveCustomTheme=Button(frameCustom,
+-            text='Save as New Custom Theme',command=self.SaveAsNewTheme)
++        buttonSaveCustomTheme = Button(frameCustom,
++            text='Save as New Custom Theme', command=self.SaveAsNewTheme)
+         #frameTheme
+-        labelTypeTitle=Label(frameTheme,text='Select : ')
+-        self.radioThemeBuiltin=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
+-            value=1,command=self.SetThemeType,text='a Built-in Theme')
+-        self.radioThemeCustom=Radiobutton(frameTheme,variable=self.themeIsBuiltin,
+-            value=0,command=self.SetThemeType,text='a Custom Theme')
+-        self.optMenuThemeBuiltin=DynOptionMenu(frameTheme,
+-            self.builtinTheme,None,command=None)
+-        self.optMenuThemeCustom=DynOptionMenu(frameTheme,
+-            self.customTheme,None,command=None)
+-        self.buttonDeleteCustomTheme=Button(frameTheme,text='Delete Custom Theme',
+-                command=self.DeleteCustomTheme)
++        labelTypeTitle = Label(frameTheme, text='Select : ')
++        self.radioThemeBuiltin = Radiobutton(frameTheme,
++            variable=self.themeIsBuiltin, value=1,
++            command=self.SetThemeType, text='a Built-in Theme')
++        self.radioThemeCustom = Radiobutton(frameTheme,
++            variable=self.themeIsBuiltin, value=0,
++            command=self.SetThemeType, text='a Custom Theme')
++        self.optMenuThemeBuiltin = DynOptionMenu(frameTheme,
++            self.builtinTheme, None, command=None)
++        self.optMenuThemeCustom = DynOptionMenu(frameTheme,
++            self.customTheme, None, command=None)
++        self.buttonDeleteCustomTheme = Button(frameTheme,
++            text='Delete Custom Theme', command=self.DeleteCustomTheme)
+         ##widget packing
+         #body
+-        frameCustom.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
+-        frameTheme.pack(side=LEFT,padx=5,pady=5,fill=Y)
++        frameCustom.pack(side=LEFT, padx=5, pady=5, expand=True, fill=BOTH)
++        frameTheme.pack(side=LEFT, padx=5, pady=5, fill=Y)
+         #frameCustom
+-        self.frameColourSet.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=X)
+-        frameFgBg.pack(side=TOP,padx=5,pady=0)
+-        self.textHighlightSample.pack(side=TOP,padx=5,pady=5,expand=TRUE,
++        self.frameColourSet.pack(side=TOP, padx=5, pady=5, expand=True, fill=X)
++        frameFgBg.pack(side=TOP, padx=5, pady=0)
++        self.textHighlightSample.pack(side=TOP, padx=5, pady=5, expand=True,
+             fill=BOTH)
+-        buttonSetColour.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=4)
+-        self.optMenuHighlightTarget.pack(side=TOP,expand=TRUE,fill=X,padx=8,pady=3)
+-        self.radioFg.pack(side=LEFT,anchor=E)
+-        self.radioBg.pack(side=RIGHT,anchor=W)
+-        buttonSaveCustomTheme.pack(side=BOTTOM,fill=X,padx=5,pady=5)
++        buttonSetColour.pack(side=TOP, expand=True, fill=X, padx=8, pady=4)
++        self.optMenuHighlightTarget.pack(side=LEFT, anchor=N, expand=True,
++            fill=X, padx=8, pady=3)
++        #self.optBoldText.pack(side=RIGHT, anchor=N, padx=8, pady=3)
++        self.radioFg.pack(side=LEFT, anchor=E)
++        self.radioBg.pack(side=RIGHT, anchor=W)
++        buttonSaveCustomTheme.pack(side=BOTTOM, fill=X, padx=5, pady=5)
+         #frameTheme
+-        labelTypeTitle.pack(side=TOP,anchor=W,padx=5,pady=5)
+-        self.radioThemeBuiltin.pack(side=TOP,anchor=W,padx=5)
+-        self.radioThemeCustom.pack(side=TOP,anchor=W,padx=5,pady=2)
+-        self.optMenuThemeBuiltin.pack(side=TOP,fill=X,padx=5,pady=5)
+-        self.optMenuThemeCustom.pack(side=TOP,fill=X,anchor=W,padx=5,pady=5)
+-        self.buttonDeleteCustomTheme.pack(side=TOP,fill=X,padx=5,pady=5)
++        labelTypeTitle.pack(side=TOP, anchor=W, padx=5, pady=5)
++        self.radioThemeBuiltin.pack(side=TOP, anchor=W, padx=5)
++        self.radioThemeCustom.pack(side=TOP, anchor=W, padx=5, pady=2)
++        self.optMenuThemeBuiltin.pack(side=TOP, fill=X, padx=5, pady=5)
++        self.optMenuThemeCustom.pack(side=TOP, fill=X, anchor=W, padx=5, pady=5)
++        self.buttonDeleteCustomTheme.pack(side=TOP, fill=X, padx=5, pady=5)
+         return frame
+ 
+     def CreatePageKeys(self):
+@@ -265,8 +318,8 @@
+         labelTargetTitle=Label(frameTarget,text='Action - Key(s)')
+         scrollTargetY=Scrollbar(frameTarget)
+         scrollTargetX=Scrollbar(frameTarget,orient=HORIZONTAL)
+-        self.listBindings=Listbox(frameTarget,takefocus=FALSE,
+-                exportselection=FALSE)
++        self.listBindings=Listbox(frameTarget,takefocus=False,
++                exportselection=False)
+         self.listBindings.bind('<ButtonRelease-1>',self.KeyBindingSelected)
+         scrollTargetY.config(command=self.listBindings.yview)
+         scrollTargetX.config(command=self.listBindings.xview)
+@@ -275,8 +328,11 @@
+         self.buttonNewKeys=Button(frameCustom,text='Get New Keys for Selection',
+             command=self.GetNewKeys,state=DISABLED)
+         #frameKeySets
+-        frames = [Frame(frameKeySets, padx=2, pady=2, borderwidth=0)
+-                  for i in range(2)]
++        frames = []
++        for i in range(2):
++            f = Frame(frameKeySets, borderwidth=0)
++            self.style(f, 'S2.TFrame')
++            frames.append(f)
+         self.radioKeysBuiltin=Radiobutton(frames[0],variable=self.keysAreBuiltin,
+             value=1,command=self.SetKeysType,text='Use a Built-in Key Set')
+         self.radioKeysCustom=Radiobutton(frames[0],variable=self.keysAreBuiltin,
+@@ -291,11 +347,11 @@
+                 text='Save as New Custom Key Set',command=self.SaveAsNewKeySet)
+         ##widget packing
+         #body
+-        frameCustom.pack(side=BOTTOM,padx=5,pady=5,expand=TRUE,fill=BOTH)
++        frameCustom.pack(side=BOTTOM,padx=5,pady=5,expand=True,fill=BOTH)
+         frameKeySets.pack(side=BOTTOM,padx=5,pady=5,fill=BOTH)
+         #frameCustom
+         self.buttonNewKeys.pack(side=BOTTOM,fill=X,padx=5,pady=5)
+-        frameTarget.pack(side=LEFT,padx=5,pady=5,expand=TRUE,fill=BOTH)
++        frameTarget.pack(side=LEFT,padx=5,pady=5,expand=True,fill=BOTH)
+         #frame target
+         frameTarget.columnconfigure(0,weight=1)
+         frameTarget.rowconfigure(1,weight=1)
+@@ -316,14 +372,16 @@
+ 
+     def CreatePageGeneral(self):
+         #tkVars
+-        self.winWidth=StringVar(self)
+-        self.winHeight=StringVar(self)
+-        self.paraWidth=StringVar(self)
+-        self.startupEdit=IntVar(self)
+-        self.autoSave=IntVar(self)
+-        self.encoding=StringVar(self)
+-        self.userHelpBrowser=BooleanVar(self)
+-        self.helpBrowser=StringVar(self)
++        self.winWidth = StringVar(self)
++        self.winHeight = StringVar(self)
++        self.paraWidth = StringVar(self)
++        self.startupEdit = IntVar(self)
++        self.autoSave = IntVar(self)
++        self.encoding = StringVar(self)
++        self.themename = StringVar(self)
++        self.fileintab = BooleanVar(self)
++        self.userHelpBrowser = BooleanVar(self)
++        self.helpBrowser = StringVar(self)
+         #widget creation
+         #body
+         frame=self.tabPages.pages['General'].frame
+@@ -335,6 +393,9 @@
+         frameWinSize=Frame(frame,borderwidth=2,relief=GROOVE)
+         frameParaSize=Frame(frame,borderwidth=2,relief=GROOVE)
+         frameEncoding=Frame(frame,borderwidth=2,relief=GROOVE)
++        frameModTab = Frame(frame, borderwidth=2, relief=GROOVE)
++        if TTK:
++            frameTheme = Frame(frame, borderwidth=2, relief=GROOVE)
+         frameHelp=LabelFrame(frame,borderwidth=2,relief=GROOVE,
+                              text=' Additional Help Sources ')
+         #frameRun
+@@ -371,12 +432,21 @@
+             value="utf-8",text="UTF-8")
+         radioEncNone=Radiobutton(frameEncoding,variable=self.encoding,
+             value="none",text="None")
++        # frameModTab
++        labelMTab = Label(frameModTab, text="Open files and modules in tabs")
++        checkMTab = Checkbutton(frameModTab, variable=self.fileintab,
++            text="Yes")
++        #frameTheme
++        if TTK:
++            labelTheme = Label(frameTheme, text="Display Theme")
++            comboTheme = Combobox(frameTheme, textvariable=self.themename,
++                values=self.ttkstyle.theme_names(), state='readonly')
+         #frameHelp
+         frameHelpList=Frame(frameHelp)
+         frameHelpListButtons=Frame(frameHelpList)
+         scrollHelpList=Scrollbar(frameHelpList)
+-        self.listHelp=Listbox(frameHelpList,height=5,takefocus=FALSE,
+-                exportselection=FALSE)
++        self.listHelp=Listbox(frameHelpList, height=4, takefocus=False,
++                exportselection=False)
+         scrollHelpList.config(command=self.listHelp.yview)
+         self.listHelp.config(yscrollcommand=scrollHelpList.set)
+         self.listHelp.bind('<ButtonRelease-1>',self.HelpSourceSelected)
+@@ -393,7 +463,10 @@
+         frameWinSize.pack(side=TOP,padx=5,pady=5,fill=X)
+         frameParaSize.pack(side=TOP,padx=5,pady=5,fill=X)
+         frameEncoding.pack(side=TOP,padx=5,pady=5,fill=X)
+-        frameHelp.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
++        frameModTab.pack(side=TOP, padx=5, pady=5, fill=X)
++        if TTK:
++            frameTheme.pack(side=TOP, padx=5, pady=5, fill=X)
++        frameHelp.pack(side=TOP,padx=5,pady=5,expand=True,fill=BOTH)
+         #frameRun
+         labelRunChoiceTitle.pack(side=LEFT,anchor=W,padx=5,pady=5)
+         radioStartupShell.pack(side=RIGHT,anchor=W,padx=5,pady=5)
+@@ -416,14 +489,22 @@
+         radioEncNone.pack(side=RIGHT,anchor=E,pady=5)
+         radioEncUTF8.pack(side=RIGHT,anchor=E,pady=5)
+         radioEncLocale.pack(side=RIGHT,anchor=E,pady=5)
++        #frameModTab
++        labelMTab.pack(side=LEFT, anchor=W, padx=5, pady=5)
++        checkMTab.pack(side=RIGHT, anchor=E, padx=5, pady=5)
++        #frameTheme
++        if TTK:
++            labelTheme.pack(side=LEFT, anchor=W, padx=5, pady=5)
++            comboTheme.pack(side=RIGHT, anchor=E, padx=5, pady=5)
+         #frameHelp
+         frameHelpListButtons.pack(side=RIGHT,padx=5,pady=5,fill=Y)
+-        frameHelpList.pack(side=TOP,padx=5,pady=5,expand=TRUE,fill=BOTH)
++        frameHelpList.pack(side=TOP,padx=5,pady=5,expand=True,fill=BOTH)
+         scrollHelpList.pack(side=RIGHT,anchor=W,fill=Y)
+-        self.listHelp.pack(side=LEFT,anchor=E,expand=TRUE,fill=BOTH)
++        self.listHelp.pack(side=LEFT,anchor=E,expand=True,fill=BOTH)
+         self.buttonHelpListEdit.pack(side=TOP,anchor=W,pady=5)
+         self.buttonHelpListAdd.pack(side=TOP,anchor=W)
+         self.buttonHelpListRemove.pack(side=TOP,anchor=W,pady=5)
++
+         return frame
+ 
+     def AttachVarCallbacks(self):
+@@ -432,6 +513,7 @@
+         self.fontBold.trace_variable('w',self.VarChanged_fontBold)
+         self.spaceNum.trace_variable('w',self.VarChanged_spaceNum)
+         self.colour.trace_variable('w',self.VarChanged_colour)
++        #self.themeFontBold.trace_variable('w', self.VarChanged_themeFontBold)
+         self.builtinTheme.trace_variable('w',self.VarChanged_builtinTheme)
+         self.customTheme.trace_variable('w',self.VarChanged_customTheme)
+         self.themeIsBuiltin.trace_variable('w',self.VarChanged_themeIsBuiltin)
+@@ -446,18 +528,20 @@
+         self.startupEdit.trace_variable('w',self.VarChanged_startupEdit)
+         self.autoSave.trace_variable('w',self.VarChanged_autoSave)
+         self.encoding.trace_variable('w',self.VarChanged_encoding)
++        self.themename.trace_variable('w', self.VarChanged_themename)
++        self.fileintab.trace_variable('w', self.VarChanged_fileintab)
+ 
+     def VarChanged_fontSize(self,*params):
+         value=self.fontSize.get()
+-        self.AddChangedItem('main','EditorWindow','font-size',value)
++        self.AddChangedItem('main','EditorPage','font-size',value)
+ 
+     def VarChanged_fontName(self,*params):
+         value=self.fontName.get()
+-        self.AddChangedItem('main','EditorWindow','font',value)
++        self.AddChangedItem('main','EditorPage','font',value)
+ 
+     def VarChanged_fontBold(self,*params):
+         value=self.fontBold.get()
+-        self.AddChangedItem('main','EditorWindow','font-bold',value)
++        self.AddChangedItem('main','EditorPage','font-bold',value)
+ 
+     def VarChanged_spaceNum(self,*params):
+         value=self.spaceNum.get()
+@@ -466,6 +550,9 @@
+     def VarChanged_colour(self,*params):
+         self.OnNewColourSet()
+ 
++    #def VarChanged_themeFontBold(self, *params):
++    #    self.OnBoldChanged()
++
+     def VarChanged_builtinTheme(self,*params):
+         value=self.builtinTheme.get()
+         self.AddChangedItem('main','Theme','name',value)
+@@ -521,11 +608,11 @@
+ 
+     def VarChanged_winWidth(self,*params):
+         value=self.winWidth.get()
+-        self.AddChangedItem('main','EditorWindow','width',value)
++        self.AddChangedItem('main', 'EditorWindow', 'width', value)
+ 
+     def VarChanged_winHeight(self,*params):
+         value=self.winHeight.get()
+-        self.AddChangedItem('main','EditorWindow','height',value)
++        self.AddChangedItem('main', 'EditorWindow', 'height', value)
+ 
+     def VarChanged_paraWidth(self,*params):
+         value=self.paraWidth.get()
+@@ -541,8 +628,16 @@
+ 
+     def VarChanged_encoding(self,*params):
+         value=self.encoding.get()
+-        self.AddChangedItem('main','EditorWindow','encoding',value)
++        self.AddChangedItem('main','EditorPage','encoding',value)
+ 
++    def VarChanged_themename(self, *params):
++        value = self.themename.get()
++        self.AddChangedItem('main', 'Theme', 'displaytheme', value)
++
++    def VarChanged_fileintab(self, *params):
++        value = self.fileintab.get()
++        self.AddChangedItem('main', 'EditorWindow', 'file-in-tab', value)
++
+     def ResetChangedItems(self):
+         #When any config item is changed in this dialog, an entry
+         #should be made in the relevant section (config type) of this
+@@ -552,10 +647,10 @@
+         self.changedItems={'main':{},'highlight':{},'keys':{},'extensions':{}}
+ 
+     def AddChangedItem(self,type,section,item,value):
+-        value=str(value) #make sure we use a string
++        value = str(value) #make sure we use a string
+         if not self.changedItems[type].has_key(section):
+-            self.changedItems[type][section]={}
+-        self.changedItems[type][section][item]=value
++            self.changedItems[type][section] = {}
++        self.changedItems[type][section][item] = value
+ 
+     def GetDefaultItems(self):
+         dItems={'main':{},'highlight':{},'keys':{},'extensions':{}}
+@@ -653,7 +748,7 @@
+         newKeys={}
+         for event in prevKeys.keys(): #add key set to changed items
+             eventName=event[2:-2] #trim off the angle brackets
+-            binding=string.join(prevKeys[event])
++            binding=' '.join(prevKeys[event])
+             newKeys[eventName]=binding
+         #handle any unsaved changes to prev key set
+         if prevKeySetName in self.changedItems['keys'].keys():
+@@ -680,7 +775,7 @@
+         bindNames.sort()
+         self.listBindings.delete(0,END)
+         for bindName in bindNames:
+-            key=string.join(keySet[bindName]) #make key(s) into a string
++            key=' '.join(keySet[bindName]) #make key(s) into a string
+             bindName=bindName[2:-2] #trim off the angle brackets
+             if keySetName in self.changedItems['keys'].keys():
+                 #handle any unsaved changes to this key set
+@@ -694,9 +789,9 @@
+ 
+     def DeleteCustomKeys(self):
+         keySetName=self.customKeys.get()
+-        if not tkMessageBox.askyesno('Delete Key Set','Are you sure you wish '+
+-                                     'to delete the key set %r ?' % (keySetName),
+-                                     parent=self):
++        if not tkMessageBox.askyesno("Delete Key Set",
++            "Are you sure you wish to delete the key set %r ?" % keySetName,
++            parent=self):
+             return
+         #remove key set from config
+         idleConf.userCfg['keys'].remove_section(keySetName)
+@@ -705,25 +800,26 @@
+         #write changes
+         idleConf.userCfg['keys'].Save()
+         #reload user key set list
+-        itemList=idleConf.GetSectionList('user','keys')
++        itemList = idleConf.GetSectionList('user', 'keys')
+         itemList.sort()
+         if not itemList:
+             self.radioKeysCustom.config(state=DISABLED)
+-            self.optMenuKeysCustom.SetMenu(itemList,'- no custom keys -')
++            self.optMenuKeysCustom.SetMenu(itemList, "- no custom keys -")
+         else:
+             self.optMenuKeysCustom.SetMenu(itemList,itemList[0])
+         #revert to default key set
+-        self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys','default'))
+-        self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys','name'))
++        self.keysAreBuiltin.set(idleConf.defaultCfg['main'].Get('Keys',
++            'default'))
++        self.builtinKeys.set(idleConf.defaultCfg['main'].Get('Keys', 'name'))
+         #user can't back out of these changes, they must be applied now
+         self.Apply()
+         self.SetKeysType()
+ 
+     def DeleteCustomTheme(self):
+-        themeName=self.customTheme.get()
+-        if not tkMessageBox.askyesno('Delete Theme','Are you sure you wish '+
+-                                     'to delete the theme %r ?' % (themeName,),
+-                                     parent=self):
++        themeName = self.customTheme.get()
++        if not tkMessageBox.askyesno("Delete Theme",
++            "Are you sure you wish to delete the theme %r ?" % themeName,
++            parent=self):
+             return
+         #remove theme from config
+         idleConf.userCfg['highlight'].remove_section(themeName)
+@@ -732,30 +828,31 @@
+         #write changes
+         idleConf.userCfg['highlight'].Save()
+         #reload user theme list
+-        itemList=idleConf.GetSectionList('user','highlight')
++        itemList = idleConf.GetSectionList('user', 'highlight')
+         itemList.sort()
+         if not itemList:
+             self.radioThemeCustom.config(state=DISABLED)
+-            self.optMenuThemeCustom.SetMenu(itemList,'- no custom themes -')
++            self.optMenuThemeCustom.SetMenu(itemList, "- no custom themes -")
+         else:
+-            self.optMenuThemeCustom.SetMenu(itemList,itemList[0])
++            self.optMenuThemeCustom.SetMenu(itemList, itemList[0])
+         #revert to default theme
+-        self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme','default'))
+-        self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme','name'))
++        self.themeIsBuiltin.set(idleConf.defaultCfg['main'].Get('Theme',
++            'default'))
++        self.builtinTheme.set(idleConf.defaultCfg['main'].Get('Theme', 'name'))
+         #user can't back out of these changes, they must be applied now
+         self.Apply()
+         self.SetThemeType()
+ 
+     def GetColour(self):
+         target=self.highlightTarget.get()
+-        prevColour=self.frameColourSet.cget('bg')
++        prevColour = self.ttkstyle.configure('Color.TFrame', 'background')
+         rgbTuplet, colourString = tkColorChooser.askcolor(parent=self,
+             title='Pick new colour for : '+target,initialcolor=prevColour)
+         if colourString and (colourString!=prevColour):
+             #user didn't cancel, and they chose a new colour
+             if self.themeIsBuiltin.get(): #current theme is a built-in
+-                message=('Your changes will be saved as a new Custom Theme. '+
+-                        'Enter a name for your new Custom Theme below.')
++                message=("Your changes will be saved as a new Custom Theme. "
++                         "Enter a name for your new Custom Theme below.")
+                 newTheme=self.GetNewThemeName(message)
+                 if not newTheme: #user cancelled custom theme creation
+                     return
+@@ -767,16 +864,33 @@
+ 
+     def OnNewColourSet(self):
+         newColour=self.colour.get()
+-        self.frameColourSet.config(bg=newColour)#set sample
++        self.ttkstyle.configure('Color.TFrame', background=newColour)
+         if self.fgHilite.get(): plane='foreground'
+         else: plane='background'
+         sampleElement=self.themeElements[self.highlightTarget.get()][0]
+-        self.textHighlightSample.tag_config(sampleElement, **{plane:newColour})
++        self.textHighlightSample.tag_config(sampleElement, **{plane: newColour})
+         theme=self.customTheme.get()
+         themeElement=sampleElement+'-'+plane
+         self.AddChangedItem('highlight',theme,themeElement,newColour)
+ 
++    #def OnBoldChanged(self):
++    #    bold = self.themeFontBold.get() and tkFont.BOLD or tkFont.NORMAL
++    #    sampleElement = self.themeElements[self.highlightTarget.get()][0]
++
++    #    #fontName = self.fontName.get()
++    #    #self.textHighlightSample.tag_config(sampleElement,
++    #    #    font=(fontName, self.fontSize.get(), bold))
++
++    #    self.textHighlightSample.tag_config(sampleElement,
++    #        font=('courier', 12, bold))
++
++    #    theme = self.customTheme.get()
++    #    themeElement = "%s-%s" % (sampleElement, 'bold')
++    #    self.AddChangedItem('highlight', theme, themeElement,
++    #        (bold == tkFont.BOLD) and 1 or 0)
++
+     def GetNewThemeName(self,message):
++        # XXX idle bug here
+         usedNames=(idleConf.GetSectionList('user','highlight')+
+                 idleConf.GetSectionList('default','highlight'))
+         newTheme=GetCfgSectionNameDialog(self,'New Custom Theme',
+@@ -835,37 +949,45 @@
+             self.radioFg.config(state=NORMAL)
+             self.radioBg.config(state=NORMAL)
+             self.fgHilite.set(1)
++        #tag = self.themeElements[self.highlightTarget.get()][0]
++        #font = self.textHighlightSample.tag_cget(tag, 'font')
++        #if font:
++        #    self.themeFontBold.set(font.split()[2] == tkFont.BOLD)
++        #else:
++        #    self.themeFontBold.set(0)
+         self.SetColourSample()
+ 
+     def SetColourSampleBinding(self,*args):
+         self.SetColourSample()
+ 
+     def SetColourSample(self):
+-        #set the colour smaple area
+-        tag=self.themeElements[self.highlightTarget.get()][0]
+-        if self.fgHilite.get(): plane='foreground'
+-        else: plane='background'
+-        colour=self.textHighlightSample.tag_cget(tag,plane)
+-        self.frameColourSet.config(bg=colour)
++        # set the colour sample area
++        tag = self.themeElements[self.highlightTarget.get()][0]
++        if self.fgHilite.get():
++            plane = 'foreground'
++        else:
++            plane = 'background'
++        colour = self.textHighlightSample.tag_cget(tag, plane)
++        self.ttkstyle.configure('Color.TFrame', background=colour)
+ 
+     def PaintThemeSample(self):
+         if self.themeIsBuiltin.get(): #a default theme
+-            theme=self.builtinTheme.get()
++            theme = self.builtinTheme.get()
+         else: #a user theme
+-            theme=self.customTheme.get()
++            theme = self.customTheme.get()
+         for elementTitle in self.themeElements.keys():
+-            element=self.themeElements[elementTitle][0]
+-            colours=idleConf.GetHighlight(theme,element)
+-            if element=='cursor': #cursor sample needs special painting
+-                colours['background']=idleConf.GetHighlight(theme,
+-                        'normal', fgBg='bg')
++            element = self.themeElements[elementTitle][0]
++            colours = idleConf.GetHighlight(theme, element)
++            if element == 'cursor': #cursor sample needs special painting
++                colours['background'] = idleConf.GetHighlight(theme,
++                    'normal', fgBg='bg')
+             #handle any unsaved changes to this theme
+             if theme in self.changedItems['highlight'].keys():
+-                themeDict=self.changedItems['highlight'][theme]
+-                if themeDict.has_key(element+'-foreground'):
+-                    colours['foreground']=themeDict[element+'-foreground']
+-                if themeDict.has_key(element+'-background'):
+-                    colours['background']=themeDict[element+'-background']
++                themeDict = self.changedItems['highlight'][theme]
++                if themeDict.has_key(element + '-foreground'):
++                    colours['foreground'] = themeDict[element + '-foreground']
++                if themeDict.has_key(element + '-background'):
++                    colours['background'] = themeDict[element + '-background']
+             self.textHighlightSample.tag_config(element, **colours)
+         self.SetColourSample()
+ 
+@@ -917,7 +1039,7 @@
+         self.changedItems['main']['HelpFiles'] = {}
+         for num in range(1,len(self.userHelpList)+1):
+             self.AddChangedItem('main','HelpFiles',str(num),
+-                    string.join(self.userHelpList[num-1][:2],';'))
++                ';'.join(self.userHelpList[num - 1][:2]))
+ 
+     def LoadFontCfg(self):
+         ##base editor font selection list
+@@ -925,7 +1047,7 @@
+         fonts.sort()
+         for font in fonts:
+             self.listFontName.insert(END,font)
+-        configuredFont=idleConf.GetOption('main','EditorWindow','font',
++        configuredFont=idleConf.GetOption('main','EditorPage','font',
+                 default='courier')
+         lc_configuredFont = configuredFont.lower()
+         self.fontName.set(lc_configuredFont)
+@@ -936,13 +1058,13 @@
+             self.listFontName.select_set(currentFontIndex)
+             self.listFontName.select_anchor(currentFontIndex)
+         ##font size dropdown
+-        fontSize=idleConf.GetOption('main','EditorWindow','font-size',
++        fontSize=idleConf.GetOption('main', 'EditorPage', 'font-size',
+                 default='10')
+         self.optMenuFontSize.SetMenu(('7','8','9','10','11','12','13','14',
+                 '16','18','20','22'),fontSize )
+         ##fontWeight
+-        self.fontBold.set(idleConf.GetOption('main','EditorWindow',
+-                'font-bold',default=0,type='bool'))
++        self.fontBold.set(idleConf.GetOption('main', 'EditorPage',
++                'font-bold', default=0, type='bool'))
+         ##font sample
+         self.SetFontSample()
+ 
+@@ -954,37 +1076,41 @@
+ 
+     def LoadThemeCfg(self):
+         ##current theme type radiobutton
+-        self.themeIsBuiltin.set(idleConf.GetOption('main','Theme','default',
+-            type='bool',default=1))
++        self.themeIsBuiltin.set(idleConf.GetOption('main', 'Theme', 'default',
++            type='bool', default=1))
+         ##currently set theme
+-        currentOption=idleConf.CurrentTheme()
++        currentOption = idleConf.CurrentTheme()
+         ##load available theme option menus
+         if self.themeIsBuiltin.get(): #default theme selected
+-            itemList=idleConf.GetSectionList('default','highlight')
++            itemList = idleConf.GetSectionList('default', 'highlight')
+             itemList.sort()
+-            self.optMenuThemeBuiltin.SetMenu(itemList,currentOption)
+-            itemList=idleConf.GetSectionList('user','highlight')
++            self.optMenuThemeBuiltin.SetMenu(itemList, currentOption)
++            itemList = idleConf.GetSectionList('user', 'highlight')
+             itemList.sort()
+             if not itemList:
+                 self.radioThemeCustom.config(state=DISABLED)
+-                self.customTheme.set('- no custom themes -')
++                self.customTheme.set("- no custom themes -")
+             else:
+                 self.optMenuThemeCustom.SetMenu(itemList,itemList[0])
+         else: #user theme selected
+-            itemList=idleConf.GetSectionList('user','highlight')
++            itemList = idleConf.GetSectionList('user', 'highlight')
+             itemList.sort()
+-            self.optMenuThemeCustom.SetMenu(itemList,currentOption)
+-            itemList=idleConf.GetSectionList('default','highlight')
++            self.optMenuThemeCustom.SetMenu(itemList, currentOption)
++            itemList = idleConf.GetSectionList('default', 'highlight')
+             itemList.sort()
+-            self.optMenuThemeBuiltin.SetMenu(itemList,itemList[0])
++            self.optMenuThemeBuiltin.SetMenu(itemList, itemList[0])
+         self.SetThemeType()
+         ##load theme element option menu
+-        themeNames=self.themeElements.keys()
++        themeNames = self.themeElements.keys()
+         themeNames.sort(self.__ThemeNameIndexCompare)
+-        self.optMenuHighlightTarget.SetMenu(themeNames,themeNames[0])
++        self.optMenuHighlightTarget.SetMenu(themeNames, themeNames[0])
+         self.PaintThemeSample()
+         self.SetHighlightTarget()
+ 
++        if TTK:
++            displaytheme = idleConf.GetOption('main', 'Theme', 'displaytheme')
++            self.themename.set(displaytheme)
++
+     def __ThemeNameIndexCompare(self,a,b):
+         if self.themeElements[a][1]<self.themeElements[b][1]: return -1
+         elif self.themeElements[a][1]==self.themeElements[b][1]: return 0
+@@ -1028,13 +1154,17 @@
+         self.autoSave.set(idleConf.GetOption('main', 'General', 'autosave',
+                                              default=0, type='bool'))
+         #initial window size
+-        self.winWidth.set(idleConf.GetOption('main','EditorWindow','width'))
+-        self.winHeight.set(idleConf.GetOption('main','EditorWindow','height'))
++        self.winWidth.set(idleConf.GetOption('main', 'EditorWindow', 'width'))
++        self.winHeight.set(idleConf.GetOption('main', 'EditorWindow', 'height'))
+         #initial paragraph reformat size
+-        self.paraWidth.set(idleConf.GetOption('main','FormatParagraph','paragraph'))
++        self.paraWidth.set(idleConf.GetOption('main','FormatParagraph',
++            'paragraph'))
+         # default source encoding
+-        self.encoding.set(idleConf.GetOption('main', 'EditorWindow',
+-                                             'encoding', default='none'))
++        self.encoding.set(idleConf.GetOption('main', 'EditorPage',
++            'encoding', default='none'))
++        # open files/modules in tabs
++        self.fileintab.set(idleConf.GetOption('main', 'EditorWindow',
++            'file-in-tab', default=1, type='bool'))
+         # additional help sources
+         self.userHelpList = idleConf.GetAllExtraHelpSourcesList()
+         for helpItem in self.userHelpList:
+@@ -1125,6 +1255,8 @@
+             instance.set_notabs_indentwidth()
+             instance.ApplyKeybindings()
+             instance.reset_help_menu_entries()
++            if TTK:
++                instance.set_theme(self.ttkstyle)
+ 
+     def Cancel(self):
+         self.destroy()
+@@ -1142,6 +1274,7 @@
+         pass
+ 
+ if __name__ == '__main__':
++    from Tkinter import Tk
+     #test the dialog
+     root=Tk()
+     Button(root,text='Dialog',
+Index: SearchEngine.py
+===================================================================
+--- SearchEngine.py	(revision 63995)
++++ SearchEngine.py	(revision 65541)
+@@ -1,5 +1,5 @@
+ import re
+-from Tkinter import *
++from Tkinter import StringVar, BooleanVar, TclError
+ import tkMessageBox
+ 
+ def get(root):
+Index: ReplaceDialog.py
+===================================================================
+--- ReplaceDialog.py	(revision 63995)
++++ ReplaceDialog.py	(revision 65541)
+@@ -1,4 +1,5 @@
+-from Tkinter import *
++from Tkinter import StringVar, TclError
++
+ import SearchEngine
+ from SearchDialogBase import SearchDialogBase
+ 
+@@ -11,9 +12,12 @@
+     dialog.open(text)
+ 
+ class ReplaceDialog(SearchDialogBase):
+-
+     title = "Replace Dialog"
+     icon = "Replace"
++    bottom_btns = [("Find", 'find_it'),
++                   ("Replace", 'replace_it'),
++                   ("Replace+Find", 'default_command', 1),
++                   ("Replace All", 'replace_all')]
+ 
+     def __init__(self, root, engine):
+         SearchDialogBase.__init__(self, root, engine)
+@@ -36,14 +40,10 @@
+ 
+     def create_entries(self):
+         SearchDialogBase.create_entries(self)
+-        self.replent = self.make_entry("Replace with:", self.replvar)
++        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)
+Index: ScriptBinding.py
+===================================================================
+--- ScriptBinding.py	(revision 63995)
++++ ScriptBinding.py	(revision 65541)
+@@ -46,8 +46,9 @@
+                  ('Check Module', '<<check-module>>'),
+                  ('Run Module', '<<run-module>>'), ]), ]
+ 
+-    def __init__(self, editwin):
+-        self.editwin = editwin
++    def __init__(self, editpage):
++        self.editpage = editpage
++        self.editwin = editpage.editwin
+         # Provide instance variables referenced by Debugger
+         # XXX This should be done differently
+         self.flist = self.editwin.flist
+@@ -70,7 +71,7 @@
+             msgtxt, (lineno, start) = msg
+             self.editwin.gotoline(lineno)
+             self.errorbox("Tabnanny Tokenizing Error",
+-                          "Token Error: %s" % msgtxt)
++                "Token Error: %s" % msgtxt)
+             return False
+         except tabnanny.NannyNag, nag:
+             # The error messages from tabnanny are too confusing...
+@@ -91,7 +92,7 @@
+             source = re.sub(r"\r", "\n", source)
+         if source and source[-1] != '\n':
+             source = source + '\n'
+-        text = self.editwin.text
++        text = self.editpage.text
+         text.tag_remove("ERROR", "1.0", "end")
+         try:
+             try:
+@@ -113,7 +114,7 @@
+             shell.set_warning_stream(saved_stream)
+ 
+     def colorize_syntax_error(self, msg, lineno, offset):
+-        text = self.editwin.text
++        text = self.editpage.text
+         pos = "0.0 + %d lines + %d chars" % (lineno-1, offset-1)
+         text.tag_add("ERROR", pos)
+         char = text.get(pos)
+@@ -175,20 +176,20 @@
+ 
+         If the user has configured IDLE for Autosave, the file will be
+         silently saved if it already exists and is dirty.
+-
+         """
+-        filename = self.editwin.io.filename
+-        if not self.editwin.get_saved():
++        page = self.editpage
++        filename = page.io.filename
++        if not page.get_saved():
+             autosave = idleConf.GetOption('main', 'General',
+                                           'autosave', type='bool')
+             if autosave and filename:
+-                self.editwin.io.save(None)
++                page.io.save(None)
+             else:
+                 reply = self.ask_save_dialog()
+-                self.editwin.text.focus_set()
++                page.text.focus_set()
+                 if reply == "ok":
+-                    self.editwin.io.save(None)
+-                    filename = self.editwin.io.filename
++                    page.io.save(None)
++                    filename = page.io.filename
+                 else:
+                     filename = None
+         return filename
+@@ -196,14 +197,12 @@
+     def ask_save_dialog(self):
+         msg = "Source Must Be Saved\n" + 5*' ' + "OK to Save?"
+         mb = tkMessageBox.Message(title="Save Before Run or Check",
+-                                  message=msg,
+-                                  icon=tkMessageBox.QUESTION,
+-                                  type=tkMessageBox.OKCANCEL,
+-                                  default=tkMessageBox.OK,
+-                                  master=self.editwin.text)
++            message=msg, icon=tkMessageBox.QUESTION,
++            type=tkMessageBox.OKCANCEL, default=tkMessageBox.OK,
++            master=self.editpage.text)
+         return mb.show()
+ 
+     def errorbox(self, title, message):
+         # XXX This should really be a function of EditorWindow...
+-        tkMessageBox.showerror(title, message, master=self.editwin.text)
+-        self.editwin.text.focus_set()
++        tkMessageBox.showerror(title, message, master=self.editpage.text)
++        self.editpage.text.focus_set()
+Index: tabbedpages.py
+===================================================================
+--- tabbedpages.py	(revision 63995)
++++ tabbedpages.py	(revision 65541)
+@@ -1,490 +1,12 @@
+-"""An implementation of tabbed pages using only standard Tkinter.
+-
+-Originally developed for use in IDLE. Based on tabpage.py.
+-
+-Classes exported:
+-TabbedPageSet -- A Tkinter implementation of a tabbed-page widget.
+-TabSet -- A widget containing tabs (buttons) in one or more rows.
+-
+-"""
+-from Tkinter import *
+-
++# Exceptions used on both versions of tabbedpages
+ class InvalidNameError(Exception): pass
+ class AlreadyExistsError(Exception): pass
+ 
++def get_tabbedpage():
++    """Returns the TabbedPageSet available for use."""
++    try:
++        from tabbedpages_new import TabbedPageSet
++    except ImportError:
++        from tabbedpages_old import TabbedPageSet
+ 
+-class TabSet(Frame):
+-    """A widget containing tabs (buttons) in one or more rows.
+-
+-    Only one tab may be selected at a time.
+-
+-    """
+-    def __init__(self, page_set, select_command,
+-                 tabs=None, n_rows=1, max_tabs_per_row=5,
+-                 expand_tabs=False, **kw):
+-        """Constructor arguments:
+-
+-        select_command -- A callable which will be called when a tab is
+-        selected. It is called with the name of the selected tab as an
+-        argument.
+-
+-        tabs -- A list of strings, the names of the tabs. Should be specified in
+-        the desired tab order. The first tab will be the default and first
+-        active tab. If tabs is None or empty, the TabSet will be initialized
+-        empty.
+-
+-        n_rows -- Number of rows of tabs to be shown. If n_rows <= 0 or is
+-        None, then the number of rows will be decided by TabSet. See
+-        _arrange_tabs() for details.
+-
+-        max_tabs_per_row -- Used for deciding how many rows of tabs are needed,
+-        when the number of rows is not constant. See _arrange_tabs() for
+-        details.
+-
+-        """
+-        Frame.__init__(self, page_set, **kw)
+-        self.select_command = select_command
+-        self.n_rows = n_rows
+-        self.max_tabs_per_row = max_tabs_per_row
+-        self.expand_tabs = expand_tabs
+-        self.page_set = page_set
+-
+-        self._tabs = {}
+-        self._tab2row = {}
+-        if tabs:
+-            self._tab_names = list(tabs)
+-        else:
+-            self._tab_names = []
+-        self._selected_tab = None
+-        self._tab_rows = []
+-
+-        self.padding_frame = Frame(self, height=2,
+-                                   borderwidth=0, relief=FLAT,
+-                                   background=self.cget('background'))
+-        self.padding_frame.pack(side=TOP, fill=X, expand=False)
+-
+-        self._arrange_tabs()
+-
+-    def add_tab(self, tab_name):
+-        """Add a new tab with the name given in tab_name."""
+-        if not tab_name:
+-            raise InvalidNameError("Invalid Tab name: '%s'" % tab_name)
+-        if tab_name in self._tab_names:
+-            raise AlreadyExistsError("Tab named '%s' already exists" %tab_name)
+-
+-        self._tab_names.append(tab_name)
+-        self._arrange_tabs()
+-
+-    def remove_tab(self, tab_name):
+-        """Remove the tab named <tab_name>"""
+-        if not tab_name in self._tab_names:
+-            raise KeyError("No such Tab: '%s" % page_name)
+-
+-        self._tab_names.remove(tab_name)
+-        self._arrange_tabs()
+-
+-    def set_selected_tab(self, tab_name):
+-        """Show the tab named <tab_name> as the selected one"""
+-        if tab_name == self._selected_tab:
+-            return
+-        if tab_name is not None and tab_name not in self._tabs:
+-            raise KeyError("No such Tab: '%s" % page_name)
+-
+-        # deselect the current selected tab
+-        if self._selected_tab is not None:
+-            self._tabs[self._selected_tab].set_normal()
+-        self._selected_tab = None
+-
+-        if tab_name is not None:
+-            # activate the tab named tab_name
+-            self._selected_tab = tab_name
+-            tab = self._tabs[tab_name]
+-            tab.set_selected()
+-            # move the tab row with the selected tab to the bottom
+-            tab_row = self._tab2row[tab]
+-            tab_row.pack_forget()
+-            tab_row.pack(side=TOP, fill=X, expand=0)
+-
+-    def _add_tab_row(self, tab_names, expand_tabs):
+-        if not tab_names:
+-            return
+-
+-        tab_row = Frame(self)
+-        tab_row.pack(side=TOP, fill=X, expand=0)
+-        self._tab_rows.append(tab_row)
+-
+-        for tab_name in tab_names:
+-            tab = TabSet.TabButton(tab_name, self.select_command,
+-                                   tab_row, self)
+-            if expand_tabs:
+-                tab.pack(side=LEFT, fill=X, expand=True)
+-            else:
+-                tab.pack(side=LEFT)
+-            self._tabs[tab_name] = tab
+-            self._tab2row[tab] = tab_row
+-
+-        # tab is the last one created in the above loop
+-        tab.is_last_in_row = True
+-
+-    def _reset_tab_rows(self):
+-        while self._tab_rows:
+-            tab_row = self._tab_rows.pop()
+-            tab_row.destroy()
+-        self._tab2row = {}
+-
+-    def _arrange_tabs(self):
+-        """
+-        Arrange the tabs in rows, in the order in which they were added.
+-
+-        If n_rows >= 1, this will be the number of rows used. Otherwise the
+-        number of rows will be calculated according to the number of tabs and
+-        max_tabs_per_row. In this case, the number of rows may change when
+-        adding/removing tabs.
+-
+-        """
+-        # remove all tabs and rows
+-        for tab_name in self._tabs.keys():
+-            self._tabs.pop(tab_name).destroy()
+-        self._reset_tab_rows()
+-
+-        if not self._tab_names:
+-            return
+-
+-        if self.n_rows is not None and self.n_rows > 0:
+-            n_rows = self.n_rows
+-        else:
+-            # calculate the required number of rows
+-            n_rows = (len(self._tab_names) - 1) // self.max_tabs_per_row + 1
+-
+-        # not expanding the tabs with more than one row is very ugly
+-        expand_tabs = self.expand_tabs or n_rows > 1
+-        i = 0 # index in self._tab_names
+-        for row_index in xrange(n_rows):
+-            # calculate required number of tabs in this row
+-            n_tabs = (len(self._tab_names) - i - 1) // (n_rows - row_index) + 1
+-            tab_names = self._tab_names[i:i + n_tabs]
+-            i += n_tabs
+-            self._add_tab_row(tab_names, expand_tabs)
+-
+-        # re-select selected tab so it is properly displayed
+-        selected = self._selected_tab
+-        self.set_selected_tab(None)
+-        if selected in self._tab_names:
+-            self.set_selected_tab(selected)
+-
+-    class TabButton(Frame):
+-        """A simple tab-like widget."""
+-
+-        bw = 2 # borderwidth
+-
+-        def __init__(self, name, select_command, tab_row, tab_set):
+-            """Constructor arguments:
+-
+-            name -- The tab's name, which will appear in its button.
+-
+-            select_command -- The command to be called upon selection of the
+-            tab. It is called with the tab's name as an argument.
+-
+-            """
+-            Frame.__init__(self, tab_row, borderwidth=self.bw, relief=RAISED)
+-
+-            self.name = name
+-            self.select_command = select_command
+-            self.tab_set = tab_set
+-            self.is_last_in_row = False
+-
+-            self.button = Radiobutton(
+-                self, text=name, command=self._select_event,
+-                padx=5, pady=1, takefocus=FALSE, indicatoron=FALSE,
+-                highlightthickness=0, selectcolor='', borderwidth=0)
+-            self.button.pack(side=LEFT, fill=X, expand=True)
+-
+-            self._init_masks()
+-            self.set_normal()
+-
+-        def _select_event(self, *args):
+-            """Event handler for tab selection.
+-
+-            With TabbedPageSet, this calls TabbedPageSet.change_page, so that
+-            selecting a tab changes the page.
+-
+-            Note that this does -not- call set_selected -- it will be called by
+-            TabSet.set_selected_tab, which should be called when whatever the
+-            tabs are related to changes.
+-
+-            """
+-            self.select_command(self.name)
+-            return
+-
+-        def set_selected(self):
+-            """Assume selected look"""
+-            self._place_masks(selected=True)
+-
+-        def set_normal(self):
+-            """Assume normal look"""
+-            self._place_masks(selected=False)
+-
+-        def _init_masks(self):
+-            page_set = self.tab_set.page_set
+-            background = page_set.pages_frame.cget('background')
+-            # mask replaces the middle of the border with the background color
+-            self.mask = Frame(page_set, borderwidth=0, relief=FLAT,
+-                              background=background)
+-            # mskl replaces the bottom-left corner of the border with a normal
+-            # left border
+-            self.mskl = Frame(page_set, borderwidth=0, relief=FLAT,
+-                              background=background)
+-            self.mskl.ml = Frame(self.mskl, borderwidth=self.bw,
+-                                 relief=RAISED)
+-            self.mskl.ml.place(x=0, y=-self.bw,
+-                               width=2*self.bw, height=self.bw*4)
+-            # mskr replaces the bottom-right corner of the border with a normal
+-            # right border
+-            self.mskr = Frame(page_set, borderwidth=0, relief=FLAT,
+-                              background=background)
+-            self.mskr.mr = Frame(self.mskr, borderwidth=self.bw,
+-                                 relief=RAISED)
+-
+-        def _place_masks(self, selected=False):
+-            height = self.bw
+-            if selected:
+-                height += self.bw
+-
+-            self.mask.place(in_=self,
+-                            relx=0.0, x=0,
+-                            rely=1.0, y=0,
+-                            relwidth=1.0, width=0,
+-                            relheight=0.0, height=height)
+-
+-            self.mskl.place(in_=self,
+-                            relx=0.0, x=-self.bw,
+-                            rely=1.0, y=0,
+-                            relwidth=0.0, width=self.bw,
+-                            relheight=0.0, height=height)
+-
+-            page_set = self.tab_set.page_set
+-            if selected and ((not self.is_last_in_row) or
+-                             (self.winfo_rootx() + self.winfo_width() <
+-                              page_set.winfo_rootx() + page_set.winfo_width())
+-                             ):
+-                # for a selected tab, if its rightmost edge isn't on the
+-                # rightmost edge of the page set, the right mask should be one
+-                # borderwidth shorter (vertically)
+-                height -= self.bw
+-
+-            self.mskr.place(in_=self,
+-                            relx=1.0, x=0,
+-                            rely=1.0, y=0,
+-                            relwidth=0.0, width=self.bw,
+-                            relheight=0.0, height=height)
+-
+-            self.mskr.mr.place(x=-self.bw, y=-self.bw,
+-                               width=2*self.bw, height=height + self.bw*2)
+-
+-            # finally, lower the tab set so that all of the frames we just
+-            # placed hide it
+-            self.tab_set.lower()
+-
+-class TabbedPageSet(Frame):
+-    """A Tkinter tabbed-pane widget.
+-
+-    Constains set of 'pages' (or 'panes') with tabs above for selecting which
+-    page is displayed. Only one page will be displayed at a time.
+-
+-    Pages may be accessed through the 'pages' attribute, which is a dictionary
+-    of pages, using the name given as the key. A page is an instance of a
+-    subclass of Tk's Frame widget.
+-
+-    The page widgets will be created (and destroyed when required) by the
+-    TabbedPageSet. Do not call the page's pack/place/grid/destroy methods.
+-
+-    Pages may be added or removed at any time using the add_page() and
+-    remove_page() methods.
+-
+-    """
+-    class Page(object):
+-        """Abstract base class for TabbedPageSet's pages.
+-
+-        Subclasses must override the _show() and _hide() methods.
+-
+-        """
+-        uses_grid = False
+-
+-        def __init__(self, page_set):
+-            self.frame = Frame(page_set, borderwidth=2, relief=RAISED)
+-
+-        def _show(self):
+-            raise NotImplementedError
+-
+-        def _hide(self):
+-            raise NotImplementedError
+-
+-    class PageRemove(Page):
+-        """Page class using the grid placement manager's "remove" mechanism."""
+-        uses_grid = True
+-
+-        def _show(self):
+-            self.frame.grid(row=0, column=0, sticky=NSEW)
+-
+-        def _hide(self):
+-            self.frame.grid_remove()
+-
+-    class PageLift(Page):
+-        """Page class using the grid placement manager's "lift" mechanism."""
+-        uses_grid = True
+-
+-        def __init__(self, page_set):
+-            super(TabbedPageSet.PageLift, self).__init__(page_set)
+-            self.frame.grid(row=0, column=0, sticky=NSEW)
+-            self.frame.lower()
+-
+-        def _show(self):
+-            self.frame.lift()
+-
+-        def _hide(self):
+-            self.frame.lower()
+-
+-    class PagePackForget(Page):
+-        """Page class using the pack placement manager's "forget" mechanism."""
+-        def _show(self):
+-            self.frame.pack(fill=BOTH, expand=True)
+-
+-        def _hide(self):
+-            self.frame.pack_forget()
+-
+-    def __init__(self, parent, page_names=None, page_class=PageLift,
+-                 n_rows=1, max_tabs_per_row=5, expand_tabs=False,
+-                 **kw):
+-        """Constructor arguments:
+-
+-        page_names -- A list of strings, each will be the dictionary key to a
+-        page's widget, and the name displayed on the page's tab. Should be
+-        specified in the desired page order. The first page will be the default
+-        and first active page. If page_names is None or empty, the
+-        TabbedPageSet will be initialized empty.
+-
+-        n_rows, max_tabs_per_row -- Parameters for the TabSet which will
+-        manage the tabs. See TabSet's docs for details.
+-
+-        page_class -- Pages can be shown/hidden using three mechanisms:
+-
+-        * PageLift - All pages will be rendered one on top of the other. When
+-          a page is selected, it will be brought to the top, thus hiding all
+-          other pages. Using this method, the TabbedPageSet will not be resized
+-          when pages are switched. (It may still be resized when pages are
+-          added/removed.)
+-
+-        * PageRemove - When a page is selected, the currently showing page is
+-          hidden, and the new page shown in its place. Using this method, the
+-          TabbedPageSet may resize when pages are changed.
+-
+-        * PagePackForget - This mechanism uses the pack placement manager.
+-          When a page is shown it is packed, and when it is hidden it is
+-          unpacked (i.e. pack_forget). This mechanism may also cause the
+-          TabbedPageSet to resize when the page is changed.
+-
+-        """
+-        Frame.__init__(self, parent, **kw)
+-
+-        self.page_class = page_class
+-        self.pages = {}
+-        self._pages_order = []
+-        self._current_page = None
+-        self._default_page = None
+-
+-        self.columnconfigure(0, weight=1)
+-        self.rowconfigure(1, weight=1)
+-
+-        self.pages_frame = Frame(self)
+-        self.pages_frame.grid(row=1, column=0, sticky=NSEW)
+-        if self.page_class.uses_grid:
+-            self.pages_frame.columnconfigure(0, weight=1)
+-            self.pages_frame.rowconfigure(0, weight=1)
+-
+-        # the order of the following commands is important
+-        self._tab_set = TabSet(self, self.change_page, n_rows=n_rows,
+-                               max_tabs_per_row=max_tabs_per_row,
+-                               expand_tabs=expand_tabs)
+-        if page_names:
+-            for name in page_names:
+-                self.add_page(name)
+-        self._tab_set.grid(row=0, column=0, sticky=NSEW)
+-
+-        self.change_page(self._default_page)
+-
+-    def add_page(self, page_name):
+-        """Add a new page with the name given in page_name."""
+-        if not page_name:
+-            raise InvalidNameError("Invalid TabPage name: '%s'" % page_name)
+-        if page_name in self.pages:
+-            raise AlreadyExistsError(
+-                "TabPage named '%s' already exists" % page_name)
+-
+-        self.pages[page_name] = self.page_class(self.pages_frame)
+-        self._pages_order.append(page_name)
+-        self._tab_set.add_tab(page_name)
+-
+-        if len(self.pages) == 1: # adding first page
+-            self._default_page = page_name
+-            self.change_page(page_name)
+-
+-    def remove_page(self, page_name):
+-        """Destroy the page whose name is given in page_name."""
+-        if not page_name in self.pages:
+-            raise KeyError("No such TabPage: '%s" % page_name)
+-
+-        self._pages_order.remove(page_name)
+-
+-        # handle removing last remaining, default, or currently shown page
+-        if len(self._pages_order) > 0:
+-            if page_name == self._default_page:
+-                # set a new default page
+-                self._default_page = self._pages_order[0]
+-        else:
+-            self._default_page = None
+-
+-        if page_name == self._current_page:
+-            self.change_page(self._default_page)
+-
+-        self._tab_set.remove_tab(page_name)
+-        page = self.pages.pop(page_name)
+-        page.frame.destroy()
+-
+-    def change_page(self, page_name):
+-        """Show the page whose name is given in page_name."""
+-        if self._current_page == page_name:
+-            return
+-        if page_name is not None and page_name not in self.pages:
+-            raise KeyError("No such TabPage: '%s'" % page_name)
+-
+-        if self._current_page is not None:
+-            self.pages[self._current_page]._hide()
+-        self._current_page = None
+-
+-        if page_name is not None:
+-            self._current_page = page_name
+-            self.pages[page_name]._show()
+-
+-        self._tab_set.set_selected_tab(page_name)
+-
+-if __name__ == '__main__':
+-    # test dialog
+-    root=Tk()
+-    tabPage=TabbedPageSet(root, page_names=['Foobar','Baz'], n_rows=0,
+-                          expand_tabs=False,
+-                          )
+-    tabPage.pack(side=TOP, expand=TRUE, fill=BOTH)
+-    Label(tabPage.pages['Foobar'].frame, text='Foo', pady=20).pack()
+-    Label(tabPage.pages['Foobar'].frame, text='Bar', pady=20).pack()
+-    Label(tabPage.pages['Baz'].frame, text='Baz').pack()
+-    entryPgName=Entry(root)
+-    buttonAdd=Button(root, text='Add Page',
+-            command=lambda:tabPage.add_page(entryPgName.get()))
+-    buttonRemove=Button(root, text='Remove Page',
+-            command=lambda:tabPage.remove_page(entryPgName.get()))
+-    labelPgName=Label(root, text='name of page to add/remove:')
+-    buttonAdd.pack(padx=5, pady=5)
+-    buttonRemove.pack(padx=5, pady=5)
+-    labelPgName.pack(padx=5)
+-    entryPgName.pack(padx=5)
+-    root.mainloop()
++    return TabbedPageSet
+Index: keybindingDialog.py
+===================================================================
+--- keybindingDialog.py	(revision 63995)
++++ keybindingDialog.py	(revision 65541)
+@@ -1,10 +1,18 @@
+ """
+ Dialog for building Tkinter accelerator key bindings
+ """
+-from Tkinter import *
++from Tkinter import Toplevel, Frame, Entry, Button, Checkbutton, Label, \
++                    Listbox, Scrollbar, StringVar
++from Tkconstants import TOP, BOTH, BOTTOM, X, NSEW, SUNKEN, LEFT, GROOVE, W, \
++                        END, EW, NS, SINGLE, VERTICAL, ANCHOR, MOVETO
+ import tkMessageBox
+ import string
+ 
++from idlelib.configHandler import idleConf
++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int')
++if TTK:
++    from ttk import Frame, Entry, Button, Checkbutton, Label, Scrollbar
++
+ class GetKeysDialog(Toplevel):
+     def __init__(self,parent,title,action,currentKeySequences):
+         """
+@@ -15,7 +23,7 @@
+         """
+         Toplevel.__init__(self, parent)
+         self.configure(borderwidth=5)
+-        self.resizable(height=FALSE,width=FALSE)
++        self.resizable(height=False, width=False)
+         self.title(title)
+         self.transient(parent)
+         self.grab_set()
+@@ -46,10 +54,10 @@
+         self.wait_window()
+ 
+     def CreateWidgets(self):
+-        frameMain = Frame(self,borderwidth=2,relief=SUNKEN)
+-        frameMain.pack(side=TOP,expand=TRUE,fill=BOTH)
++        frameMain = Frame(self, borderwidth=2, relief=SUNKEN)
++        frameMain.pack(side=TOP, expand=True, fill=BOTH)
+         frameButtons=Frame(self)
+-        frameButtons.pack(side=BOTTOM,fill=X)
++        frameButtons.pack(side=BOTTOM, fill=X)
+         self.buttonOK = Button(frameButtons,text='OK',
+                 width=8,command=self.OK)
+         self.buttonOK.grid(row=0,column=0,padx=5,pady=5)
+@@ -124,6 +132,9 @@
+                  "separated by a space, eg., <Alt-v> <Meta-v>." )
+         labelHelpAdvanced.grid(row=0,column=0,sticky=NSEW)
+ 
++        if TTK:
++            frameButtons['style'] = 'RootColor.TFrame'
++
+     def SetModifiersForPlatform(self):
+         """Determine list of names of key modifiers for this platform.
+ 
+@@ -163,7 +174,7 @@
+         if finalKey:
+             finalKey = self.TranslateKey(finalKey, modifiers)
+             keyList.append(finalKey)
+-        self.keyString.set('<' + string.join(keyList,'-') + '>')
++        self.keyString.set('<%s>' % '-'.join(keyList))
+ 
+     def GetModifiers(self):
+         modList = [variable.get() for variable in self.modifier_vars]
+@@ -258,6 +269,7 @@
+         return keysOK
+ 
+ if __name__ == '__main__':
++    from Tkinter import Tk
+     #test the dialog
+     root=Tk()
+     def run():
+Index: configHelpSourceEdit.py
+===================================================================
+--- configHelpSourceEdit.py	(revision 63995)
++++ configHelpSourceEdit.py	(revision 65541)
+@@ -1,12 +1,17 @@
+ "Dialog to specify or edit the parameters for a user configured help source."
+-
+ import os
+ import sys
+-
+-from Tkinter import *
++from Tkinter import Toplevel, Frame, Entry, Button, Label, StringVar
++from Tkconstants import GROOVE, LEFT, RIGHT, W, ACTIVE, X, BOTH, TOP, BOTTOM
+ import tkMessageBox
+ import tkFileDialog
+ 
++from configHandler import idleConf
++
++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int')
++if TTK:
++    from ttk import Frame, Entry, Button, Label
++
+ class GetHelpSourceDialog(Toplevel):
+     def __init__(self, parent, title, menuItem='', filePath=''):
+         """Get menu entry and url/ local file location for Additional Help
+@@ -18,13 +23,14 @@
+         """
+         Toplevel.__init__(self, parent)
+         self.configure(borderwidth=5)
+-        self.resizable(height=FALSE, width=FALSE)
++        self.resizable(height=False, width=False)
+         self.title(title)
+         self.transient(parent)
+         self.grab_set()
+         self.protocol("WM_DELETE_WINDOW", self.Cancel)
+         self.parent = parent
+         self.result = None
++
+         self.CreateWidgets()
+         self.menu.set(menuItem)
+         self.path.set(filePath)
+@@ -46,33 +52,36 @@
+         self.path = StringVar(self)
+         self.fontSize = StringVar(self)
+         self.frameMain = Frame(self, borderwidth=2, relief=GROOVE)
+-        self.frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
+         labelMenu = Label(self.frameMain, anchor=W, justify=LEFT,
+-                          text='Menu Item:')
+-        self.entryMenu = Entry(self.frameMain, textvariable=self.menu,
+-                               width=30)
+-        self.entryMenu.focus_set()
++            text='Menu Item:')
++        self.entryMenu = Entry(self.frameMain, textvariable=self.menu)
+         labelPath = Label(self.frameMain, anchor=W, justify=LEFT,
+-                          text='Help File Path: Enter URL or browse for file')
++            text='Help File Path: Enter URL or browse for file')
+         self.entryPath = Entry(self.frameMain, textvariable=self.path,
+-                               width=40)
++            width=30)
++        browseButton = Button(self.frameMain, text='Browse', width=8,
++            command=self.browseFile)
++        frameButtons = Frame(self)
++        self.buttonOk = Button(frameButtons, text='OK', width=8,
++            default=ACTIVE,  command=self.Ok)
++        self.buttonCancel = Button(frameButtons, text='Cancel', width=8,
++            command=self.Cancel)
++
+         self.entryMenu.focus_set()
++
++        self.frameMain.pack(side=TOP, expand=True, fill=BOTH)
+         labelMenu.pack(anchor=W, padx=5, pady=3)
+-        self.entryMenu.pack(anchor=W, padx=5, pady=3)
++        self.entryMenu.pack(anchor=W, padx=5, pady=3, fill=X)
+         labelPath.pack(anchor=W, padx=5, pady=3)
+-        self.entryPath.pack(anchor=W, padx=5, pady=3)
+-        browseButton = Button(self.frameMain, text='Browse', width=8,
+-                              command=self.browseFile)
+-        browseButton.pack(pady=3)
+-        frameButtons = Frame(self)
++        self.entryPath.pack(anchor=W, padx=5, pady=3, side=LEFT, fill=X)
++        browseButton.pack(pady=3, padx=5, side=RIGHT)
+         frameButtons.pack(side=BOTTOM, fill=X)
+-        self.buttonOk = Button(frameButtons, text='OK',
+-                               width=8, default=ACTIVE,  command=self.Ok)
+-        self.buttonOk.grid(row=0, column=0, padx=5,pady=5)
+-        self.buttonCancel = Button(frameButtons, text='Cancel',
+-                                   width=8, command=self.Cancel)
+-        self.buttonCancel.grid(row=0, column=1, padx=5, pady=5)
++        self.buttonOk.pack(pady=5, side=RIGHT)
++        self.buttonCancel.pack(padx=5, pady=5, side=RIGHT)
+ 
++        if TTK:
++            frameButtons['style'] = 'RootColor.TFrame'
++
+     def browseFile(self):
+         filetypes = [
+             ("HTML Files", "*.htm *.html", "TEXT"),
+@@ -159,6 +168,7 @@
+         self.destroy()
+ 
+ if __name__ == '__main__':
++    from Tkinter import Tk
+     #test the dialog
+     root = Tk()
+     def run():
+Index: WidgetRedirector.py
+===================================================================
+--- WidgetRedirector.py	(revision 63995)
++++ WidgetRedirector.py	(revision 65541)
+@@ -1,4 +1,4 @@
+-from Tkinter import *
++from Tkinter import TclError
+ 
+ class WidgetRedirector:
+ 
+@@ -105,6 +105,7 @@
+ 
+ 
+ def main():
++    from Tkinter import Tk, Text
+     root = Tk()
+     root.wm_protocol("WM_DELETE_WINDOW", root.quit)
+     text = Text()
+Index: GrepDialog.py
+===================================================================
+--- GrepDialog.py	(revision 63995)
++++ GrepDialog.py	(revision 65541)
+@@ -1,10 +1,15 @@
+ import os
+-import fnmatch
+ import sys
+-from Tkinter import *
++import fnmatch
++from Tkinter import StringVar, BooleanVar, Checkbutton
++
+ import SearchEngine
+ from SearchDialogBase import SearchDialogBase
++from configHandler import idleConf
+ 
++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'):
++    from ttk import Checkbutton
++
+ def grep(text, io=None, flist=None):
+     root = text._root()
+     engine = SearchEngine.get(root)
+@@ -15,10 +20,10 @@
+     dialog.open(text, searchphrase, io)
+ 
+ class GrepDialog(SearchDialogBase):
+-
+     title = "Find in Files Dialog"
+     icon = "Grep"
+     needwrapbutton = 0
++    bottom_btns = [("Search Files", 'default_command', 1)]
+ 
+     def __init__(self, root, engine, flist):
+         SearchDialogBase.__init__(self, root, engine)
+@@ -40,20 +45,18 @@
+ 
+     def create_entries(self):
+         SearchDialogBase.create_entries(self)
+-        self.globent = self.make_entry("In files:", self.globvar)
++        self.globent = self.make_entry("In files", self.globvar)
+ 
+     def create_other_buttons(self):
+         f = self.make_frame()
+ 
+-        btn = Checkbutton(f, anchor="w",
+-                variable=self.recvar,
++        btn = Checkbutton(f, variable=self.recvar,
+                 text="Recurse down subdirectories")
+         btn.pack(side="top", fill="both")
+-        btn.select()
++        btn.invoke()
+ 
+     def create_command_buttons(self):
+         SearchDialogBase.create_command_buttons(self)
+-        self.make_button("Search Files", self.default_command, 1)
+ 
+     def default_command(self, event=None):
+         prog = self.engine.getprog()
+@@ -126,8 +129,3 @@
+             for subdir in subdirs:
+                 list.extend(self.findfiles(subdir, base, rec))
+         return list
+-
+-    def close(self, event=None):
+-        if self.top:
+-            self.top.grab_release()
+-            self.top.withdraw()
+Index: FormatParagraph.py
+===================================================================
+--- FormatParagraph.py	(revision 63995)
++++ FormatParagraph.py	(revision 65541)
+@@ -25,28 +25,30 @@
+          ])
+     ]
+ 
+-    def __init__(self, editwin):
+-        self.editwin = editwin
++    def __init__(self, editpage):
++        self.editpage = editpage
+ 
+     def close(self):
+-        self.editwin = None
++        self.editpage = None
+ 
+     def format_paragraph_event(self, event):
+-        maxformatwidth = int(idleConf.GetOption('main','FormatParagraph','paragraph'))
+-        text = self.editwin.text
+-        first, last = self.editwin.get_selection_indices()
++        maxformatwidth = int(idleConf.GetOption('main', 'FormatParagraph',
++            'paragraph'))
++        text = self.editpage.text
++        first, last = self.editpage.get_selection_indices()
+         if first and last:
+             data = text.get(first, last)
+             comment_header = ''
+         else:
+-            first, last, comment_header, data = \
+-                    find_paragraph(text, text.index("insert"))
++            first, last, comment_header, data = find_paragraph(text,
++                text.index("insert"))
+         if comment_header:
+             # Reformat the comment lines - convert to text sans header.
+             lines = data.split("\n")
+             lines = map(lambda st, l=len(comment_header): st[l:], lines)
+             data = "\n".join(lines)
+-            # Reformat to maxformatwidth chars or a 20 char width, whichever is greater.
++            # Reformat to maxformatwidth chars or a 20 char width, whichever is
++            # greater.
+             format_width = max(maxformatwidth - len(comment_header), 20)
+             newdata = reformat_paragraph(data, format_width)
+             # re-split and re-insert the comment header.
+Index: EditorWindow.py
+===================================================================
+--- EditorWindow.py	(revision 63995)
++++ EditorWindow.py	(revision 65541)
+@@ -1,55 +1,54 @@
+-import sys
+ import os
+ import re
+-import imp
+-from itertools import count
+-from Tkinter import *
+-import tkSimpleDialog
++import sys
++import traceback
++import webbrowser
+ import tkMessageBox
+-from MultiCall import MultiCallCreator
++import tkSimpleDialog
++from Tkinter import Menu, Scrollbar, TclError, BooleanVar
++from Tkconstants import INSERT, END, RIGHT, BOTTOM, TOP, X, Y, BOTH
+ 
+-import webbrowser
+-import idlever
++import macosxSupport
++import Bindings
+ import WindowList
+-import SearchDialog
+-import GrepDialog
+-import ReplaceDialog
+-import PyParse
++from editorpage import EditorPage, classifyws, filename_to_unicode
++from tabbedpages import get_tabbedpage
+ from configHandler import idleConf
+-import aboutDialog, textView, configDialog
+-import macosxSupport
++from MultiStatusBar import MultiStatusBar
+ 
++TabbedPageSet = get_tabbedpage()
++
++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int')
++if TTK:
++    from ttk import Scrollbar
++
+ # The default tab setting for a Text widget, in average-width characters.
+ TK_TABWIDTH_DEFAULT = 8
+ 
+-def _find_module(fullname, path=None):
+-    """Version of imp.find_module() that handles hierarchical module names"""
+-
+-    file = None
+-    for tgt in fullname.split('.'):
+-        if file is not None:
+-            file.close()            # close intermediate files
+-        (file, filename, descr) = imp.find_module(tgt, path)
+-        if descr[2] == imp.PY_SOURCE:
+-            break                   # find but not load the source file
+-        module = imp.load_module(tgt, file, filename, descr)
+-        try:
+-            path = module.__path__
+-        except AttributeError:
+-            raise ImportError, 'No source for module ' + module.__name__
+-    return file, filename, descr
+-
+ class EditorWindow(object):
+-    from Percolator import Percolator
+-    from ColorDelegator import ColorDelegator
+-    from UndoDelegator import UndoDelegator
+-    from IOBinding import IOBinding, filesystemencoding, encoding
+-    import Bindings
+-    from Tkinter import Toplevel
+-    from MultiStatusBar import MultiStatusBar
++    from ColorDelegator import ColorDelegator # overridden by PyShell
++    from UndoDelegator import UndoDelegator   # overridden by PyShell
+ 
+     help_url = None
++    rmenu = None
++    rmenu_specs = [
++        # ("Label", "<<virtual-event>>"), ...
++        ("Close", "<<close-window>>"), # Example
++    ]
++    menu_specs = [
++        ("file", "_File"),
++        ("edit", "_Edit"),
++        ("format", "F_ormat"),
++        ("run", "_Run"),
++        ("options", "_Options"),
++        ("windows", "_Windows"),
++        ("help", "_Help"),
++    ]
+ 
++    if macosxSupport.runningAsOSXApp():
++        del menu_specs[-3]
++        menu_specs[-2] = ("windows", "_Window")
++
+     def __init__(self, flist=None, filename=None, key=None, root=None):
+         if EditorWindow.help_url is None:
+             dochome =  os.path.join(sys.prefix, 'Doc', 'index.html')
+@@ -81,7 +80,7 @@
+                     EditorWindow.help_url = 'file://' + EditorWindow.help_url
+             else:
+                 EditorWindow.help_url = "http://www.python.org/doc/current"
+-        currentTheme=idleConf.CurrentTheme()
++
+         self.flist = flist
+         root = root or flist.root
+         self.root = root
+@@ -90,94 +89,45 @@
+         except AttributeError:
+             sys.ps1 = '>>> '
+         self.menubar = Menu(root)
+-        self.top = top = WindowList.ListedToplevel(root, menu=self.menubar)
++        self.top = WindowList.ListedToplevel(root, menu=self.menubar)
+         if flist:
+             self.tkinter_vars = flist.vars
+-            #self.top.instance_dict makes flist.inversedict avalable to
+-            #configDialog.py so it can access all EditorWindow instaces
++            # self.top.instance_dict makes flist.inversedict avalable to
++            # configDialog.py so it can access all EditorWindow instaces
+             self.top.instance_dict = flist.inversedict
+         else:
+             self.tkinter_vars = {}  # keys: Tkinter event names
+                                     # values: Tkinter variable instances
+             self.top.instance_dict = {}
+         self.recent_files_path = os.path.join(idleConf.GetUserCfgDir(),
+-                'recent-files.lst')
+-        self.text_frame = text_frame = Frame(top)
+-        self.vbar = vbar = Scrollbar(text_frame, name='vbar')
+-        self.width = idleConf.GetOption('main','EditorWindow','width')
+-        self.text = text = MultiCallCreator(Text)(
+-                text_frame, name='text', padx=5, wrap='none',
+-                width=self.width,
+-                height=idleConf.GetOption('main','EditorWindow','height') )
+-        self.top.focused_widget = self.text
++            'recent-files.lst')
+ 
+-        self.createmenubar()
+-        self.apply_bindings()
+-
+-        self.top.protocol("WM_DELETE_WINDOW", self.close)
+-        self.top.bind("<<close-window>>", self.close_event)
+-        if macosxSupport.runningAsOSXApp():
+-            # Command-W on editorwindows doesn't work without this.
+-            text.bind('<<close-window>>', self.close_event)
+-        text.bind("<<cut>>", self.cut)
+-        text.bind("<<copy>>", self.copy)
+-        text.bind("<<paste>>", self.paste)
+-        text.bind("<<center-insert>>", self.center_insert_event)
+-        text.bind("<<help>>", self.help_dialog)
+-        text.bind("<<python-docs>>", self.python_docs)
+-        text.bind("<<about-idle>>", self.about_dialog)
+-        text.bind("<<open-config-dialog>>", self.config_dialog)
+-        text.bind("<<open-module>>", self.open_module)
+-        text.bind("<<do-nothing>>", lambda event: "break")
+-        text.bind("<<select-all>>", self.select_all)
+-        text.bind("<<remove-selection>>", self.remove_selection)
+-        text.bind("<<find>>", self.find_event)
+-        text.bind("<<find-again>>", self.find_again_event)
+-        text.bind("<<find-in-files>>", self.find_in_files_event)
+-        text.bind("<<find-selection>>", self.find_selection_event)
+-        text.bind("<<replace>>", self.replace_event)
+-        text.bind("<<goto-line>>", self.goto_line_event)
+-        text.bind("<3>", self.right_menu_event)
+-        text.bind("<<smart-backspace>>",self.smart_backspace_event)
+-        text.bind("<<newline-and-indent>>",self.newline_and_indent_event)
+-        text.bind("<<smart-indent>>",self.smart_indent_event)
+-        text.bind("<<indent-region>>",self.indent_region_event)
+-        text.bind("<<dedent-region>>",self.dedent_region_event)
+-        text.bind("<<comment-region>>",self.comment_region_event)
+-        text.bind("<<uncomment-region>>",self.uncomment_region_event)
+-        text.bind("<<tabify-region>>",self.tabify_region_event)
+-        text.bind("<<untabify-region>>",self.untabify_region_event)
+-        text.bind("<<toggle-tabs>>",self.toggle_tabs_event)
+-        text.bind("<<change-indentwidth>>",self.change_indentwidth_event)
+-        text.bind("<Left>", self.move_at_edge_if_selection(0))
+-        text.bind("<Right>", self.move_at_edge_if_selection(1))
+-        text.bind("<<del-word-left>>", self.del_word_left)
+-        text.bind("<<del-word-right>>", self.del_word_right)
+-        text.bind("<<beginning-of-line>>", self.home_callback)
+-
+         if flist:
+             flist.inversedict[self] = key
+             if key:
+                 flist.dict[key] = self
+-            text.bind("<<open-new-window>>", self.new_callback)
+-            text.bind("<<close-all-windows>>", self.flist.close_all_callback)
+-            text.bind("<<open-class-browser>>", self.open_class_browser)
+-            text.bind("<<open-path-browser>>", self.open_path_browser)
+ 
+-        self.set_status_bar()
+-        vbar['command'] = text.yview
+-        vbar.pack(side=RIGHT, fill=Y)
+-        text['yscrollcommand'] = vbar.set
+-        fontWeight = 'normal'
+-        if idleConf.GetOption('main', 'EditorWindow', 'font-bold', type='bool'):
+-            fontWeight='bold'
+-        text.config(font=(idleConf.GetOption('main', 'EditorWindow', 'font'),
+-                          idleConf.GetOption('main', 'EditorWindow', 'font-size'),
+-                          fontWeight))
+-        text_frame.pack(side=LEFT, fill=BOTH, expand=1)
+-        text.pack(side=TOP, fill=BOTH, expand=1)
+-        text.focus_set()
++        self.menudict = None
+ 
++        # create a Notebook where the text pages for this EditorWindow will
++        # reside
++        self.text_notebook = TabbedPageSet(self.top)
++        self.text_notebook.pack(fill=BOTH, expand=True)
++        self.text_notebook.bind('<<NotebookTabChanged>>', self._update_controls)
++        self.new_tab(filename=filename, load_ext=False)
++        self.text = self.current_page.text # XXX
++        self.top.focused_widget = self.text
++        self.top.bind('<<tab-closed>>', self._post_tab_close)
++
++        # The following "width" attribute is used by PyShell, so keep it here
++        self.width = idleConf.GetOption('main', 'EditorPage', 'width')
++
++        self.top.protocol("WM_DELETE_WINDOW", self.close)
++        self.top.bind("<<close-window>>", self.close_event)
++
++        self._create_statusbar()
++        self.top.after_idle(self.set_line_and_column)
++
+         # usetabs true  -> literal tab characters are used by indent and
+         #                  dedent cmds, possibly mixed with spaces if
+         #                  indentwidth is not a multiple of tabwidth,
+@@ -187,7 +137,8 @@
+         # Although use-spaces=0 can be configured manually in config-main.def,
+         # configuration of tabs v. spaces is not supported in the configuration
+         # dialog.  IDLE promotes the preferred Python indentation: use spaces!
+-        usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces', type='bool')
++        usespaces = idleConf.GetOption('main', 'Indent', 'use-spaces',
++            type='bool')
+         self.usetabs = not usespaces
+ 
+         # tabwidth is the display width of a literal tab character.
+@@ -214,37 +165,10 @@
+         # Making the initial values larger slows things down more often.
+         self.num_context_lines = 50, 500, 5000000
+ 
+-        self.per = per = self.Percolator(text)
+-
+-        self.undo = undo = self.UndoDelegator()
+-        per.insertfilter(undo)
+-        text.undo_block_start = undo.undo_block_start
+-        text.undo_block_stop = undo.undo_block_stop
+-        undo.set_saved_change_hook(self.saved_change_hook)
+-
+-        # IOBinding implements file I/O and printing functionality
+-        self.io = io = self.IOBinding(self)
+-        io.set_filename_change_hook(self.filename_change_hook)
+-
+-        # Create the recent files submenu
+-        self.recent_files_menu = Menu(self.menubar)
+-        self.menudict['file'].insert_cascade(3, label='Recent Files',
+-                                             underline=0,
+-                                             menu=self.recent_files_menu)
+-        self.update_recent_files_list()
+-
+-        self.color = None # initialized below in self.ResetColorizer
+-        if filename:
+-            if os.path.exists(filename) and not os.path.isdir(filename):
+-                io.loadfile(filename)
+-            else:
+-                io.set_filename(filename)
+-        self.ResetColorizer()
+-        self.saved_change_hook()
+-
+         self.set_indentation_params(self.ispythonsource(filename))
+ 
+-        self.load_extensions()
++        self.extensions = {}
++        self._load_extensions()
+ 
+         menu = self.menudict.get('windows')
+         if menu:
+@@ -262,121 +186,96 @@
+         self.askinteger = tkSimpleDialog.askinteger
+         self.showerror = tkMessageBox.showerror
+ 
+-    def _filename_to_unicode(self, filename):
+-        """convert filename to unicode in order to display it in Tk"""
+-        if isinstance(filename, unicode) or not filename:
+-            return filename
++    @property
++    def current_page(self):
++        """Return the active EditorPage in EditorWindow."""
++        curr_tab = self.text_notebook.select()
++        if not curr_tab:
++            return None
++
++        if TTK:
++            page = self.text_notebook.pages[self.text_notebook.tab(
++                curr_tab)['text']].editpage
+         else:
+-            try:
+-                return filename.decode(self.filesystemencoding)
+-            except UnicodeDecodeError:
+-                # XXX
+-                try:
+-                    return filename.decode(self.encoding)
+-                except UnicodeDecodeError:
+-                    # byte-to-byte conversion
+-                    return filename.decode('iso8859-1')
++            page = self.text_notebook.pages[curr_tab].editpage
++        return page
+ 
+-    def new_callback(self, event):
+-        dirname, basename = self.io.defaultfilename()
+-        self.flist.new(dirname)
+-        return "break"
++    def short_title(self):
++        # overriden by PyShell
++        self.current_page.short_title()
+ 
+-    def home_callback(self, event):
+-        if (event.state & 12) != 0 and event.keysym == "Home":
+-            # state&1==shift, state&4==control, state&8==alt
+-            return # <Modifier-Home>; fall back to class binding
++    def next_tab(self, event):
++        """Show next tab if not in the last tab already."""
++        index = self.text_notebook.index(self.text_notebook.select())
++        if index == len(self.text_notebook.tabs()) - 1:
++            return
++        self.text_notebook.select(index + 1)
+ 
+-        if self.text.index("iomark") and \
+-           self.text.compare("iomark", "<=", "insert lineend") and \
+-           self.text.compare("insert linestart", "<=", "iomark"):
+-            insertpt = int(self.text.index("iomark").split(".")[1])
+-        else:
+-            line = self.text.get("insert linestart", "insert lineend")
+-            for insertpt in xrange(len(line)):
+-                if line[insertpt] not in (' ','\t'):
+-                    break
+-            else:
+-                insertpt=len(line)
++    def prev_tab(self, event):
++        """Show the previous tab if not in the first tab already."""
++        index = self.text_notebook.index(self.text_notebook.select())
++        if index == 0:
++            return
++        self.text_notebook.select(index - 1)
+ 
+-        lineat = int(self.text.index("insert").split('.')[1])
++    def new_tab(self, event=None, filename=None, load_ext=True):
++        """Create a new EditorPage and insert it into the notebook."""
++        page_title = "#%d" % (len(self.text_notebook.pages) + 1)
++        page = self.text_notebook.add_page(page_title)
+ 
+-        if insertpt == lineat:
+-            insertpt = 0
++        vbar = Scrollbar(page.frame, name='vbar')
++        page.editpage = EditorPage(page.frame, self, title=page_title,
++            name='text', padx=5, wrap='none')
+ 
+-        dest = "insert linestart+"+str(insertpt)+"c"
++        firstpage = False # don't update window's title
++        if self.menudict is None:
++            # This EditorWindow is being created now, perform the following
++            # tasks before.
++            firstpage = True # will cause window's title to be updated
++            self.menudict = {}
++            self._createmenubar(page.editpage.text)
++            # Create the recent files submenu
++            self.recent_files_menu = Menu(self.menubar)
++            self.menudict['file'].insert_cascade(3, label='Recent Files',
++                underline=0, menu=self.recent_files_menu)
++            self.update_recent_files_list()
+ 
+-        if (event.state&1) == 0:
+-            # shift not pressed
+-            self.text.tag_remove("sel", "1.0", "end")
+-        else:
+-            if not self.text.index("sel.first"):
+-                self.text.mark_set("anchor","insert")
++        page.editpage.post_init(filename=filename, update_window_title=firstpage)
+ 
+-            first = self.text.index(dest)
+-            last = self.text.index("anchor")
++        text = page.editpage.text
++        vbar['command'] = text.yview
++        vbar.pack(side=RIGHT, fill=Y)
++        text['yscrollcommand'] = vbar.set
++        fontWeight = 'normal'
++        if idleConf.GetOption('main', 'EditorPage', 'font-bold', type='bool'):
++            fontWeight = 'bold'
++        text.config(font=(idleConf.GetOption('main', 'EditorPage', 'font'),
++            idleConf.GetOption('main', 'EditorPage', 'font-size'),
++            fontWeight))
++        text.pack(side=TOP, fill=BOTH, expand=1)
++        text.focus_set()
+ 
+-            if self.text.compare(first,">",last):
+-                first,last = last,first
++        self.apply_bindings(tab=page)
++        if load_ext:
++            self._load_extensions()
++        self.top.event_generate('<<tab-created>>')
++        return "break"
+ 
+-            self.text.tag_remove("sel", "1.0", "end")
+-            self.text.tag_add("sel", first, last)
+-
+-        self.text.mark_set("insert", dest)
+-        self.text.see("insert")
++    def new_callback(self, event, page):
++        dirname, basename = page.io.defaultfilename()
++        self.flist.new(dirname)
+         return "break"
+ 
+-    def set_status_bar(self):
+-        self.status_bar = self.MultiStatusBar(self.top)
+-        if macosxSupport.runningAsOSXApp():
+-            # Insert some padding to avoid obscuring some of the statusbar
+-            # by the resize widget.
+-            self.status_bar.set_label('_padding1', '    ', side=RIGHT)
+-        self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
+-        self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
+-        self.status_bar.pack(side=BOTTOM, fill=X)
+-        self.text.bind("<<set-line-and-column>>", self.set_line_and_column)
+-        self.text.event_add("<<set-line-and-column>>",
+-                            "<KeyRelease>", "<ButtonRelease>")
+-        self.text.after_idle(self.set_line_and_column)
+-
+     def set_line_and_column(self, event=None):
+-        line, column = self.text.index(INSERT).split('.')
++        # Used by PyShell too
++        curr_page = self.current_page
++        if not curr_page:
++            return
++
++        line, column = curr_page.text.index(INSERT).split('.')
+         self.status_bar.set_label('column', 'Col: %s' % column)
+         self.status_bar.set_label('line', 'Ln: %s' % line)
+ 
+-    menu_specs = [
+-        ("file", "_File"),
+-        ("edit", "_Edit"),
+-        ("format", "F_ormat"),
+-        ("run", "_Run"),
+-        ("options", "_Options"),
+-        ("windows", "_Windows"),
+-        ("help", "_Help"),
+-    ]
+-
+-    if macosxSupport.runningAsOSXApp():
+-        del menu_specs[-3]
+-        menu_specs[-2] = ("windows", "_Window")
+-
+-
+-    def createmenubar(self):
+-        mbar = self.menubar
+-        self.menudict = menudict = {}
+-        for name, label in self.menu_specs:
+-            underline, label = prepstr(label)
+-            menudict[name] = menu = Menu(mbar, name=name)
+-            mbar.add_cascade(label=label, menu=menu, underline=underline)
+-
+-        if sys.platform == 'darwin' and '.framework' in sys.executable:
+-            # Insert the application menu
+-            menudict['application'] = menu = Menu(mbar, name='apple')
+-            mbar.add_cascade(label='IDLE', menu=menu)
+-
+-        self.fill_menus()
+-        self.base_helpmenu_length = self.menudict['help'].index(END)
+-        self.reset_help_menu_entries()
+-
+     def postwindowsmenu(self):
+         # Only called when Windows menu exists
+         menu = self.menudict['windows']
+@@ -387,195 +286,27 @@
+             menu.delete(self.wmenu_end+1, end)
+         WindowList.add_windows_to_menu(menu)
+ 
+-    rmenu = None
++    def newline_and_indent_event(self, event):
++        """Call newline_and_indent_event on current EditorPage."""
++        self.current_page.newline_and_indent_event(event)
+ 
+-    def right_menu_event(self, event):
+-        self.text.tag_remove("sel", "1.0", "end")
+-        self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
+-        if not self.rmenu:
+-            self.make_rmenu()
+-        rmenu = self.rmenu
+-        self.event = event
+-        iswin = sys.platform[:3] == 'win'
+-        if iswin:
+-            self.text.config(cursor="arrow")
+-        rmenu.tk_popup(event.x_root, event.y_root)
+-        if iswin:
+-            self.text.config(cursor="ibeam")
++    def get_selection_indices(self):
++        """Call get_selection_indices on current EditorPage."""
++        return self.current_page.get_selection_indices()
+ 
+-    rmenu_specs = [
+-        # ("Label", "<<virtual-event>>"), ...
+-        ("Close", "<<close-window>>"), # Example
+-    ]
++    def build_char_in_string_func(self, startindex):
++        """Call build_char_in_string_func on current EditorPage."""
++        return self.current_page.build_char_in_string_func(startindex)
+ 
+-    def make_rmenu(self):
+-        rmenu = Menu(self.text, tearoff=0)
+-        for label, eventname in self.rmenu_specs:
+-            def command(text=self.text, eventname=eventname):
+-                text.event_generate(eventname)
+-            rmenu.add_command(label=label, command=command)
+-        self.rmenu = rmenu
+-
+-    def about_dialog(self, event=None):
+-        aboutDialog.AboutDialog(self.top,'About IDLE')
+-
+-    def config_dialog(self, event=None):
+-        configDialog.ConfigDialog(self.top,'Settings')
+-
+-    def help_dialog(self, event=None):
+-        fn=os.path.join(os.path.abspath(os.path.dirname(__file__)),'help.txt')
+-        textView.view_file(self.top,'Help',fn)
+-
+-    def python_docs(self, event=None):
+-        if sys.platform[:3] == 'win':
+-            os.startfile(self.help_url)
+-        else:
+-            webbrowser.open(self.help_url)
+-        return "break"
+-
+-    def cut(self,event):
+-        self.text.event_generate("<<Cut>>")
+-        return "break"
+-
+-    def copy(self,event):
+-        if not self.text.tag_ranges("sel"):
+-            # There is no selection, so do nothing and maybe interrupt.
+-            return
+-        self.text.event_generate("<<Copy>>")
+-        return "break"
+-
+-    def paste(self,event):
+-        self.text.event_generate("<<Paste>>")
+-        self.text.see("insert")
+-        return "break"
+-
+-    def select_all(self, event=None):
+-        self.text.tag_add("sel", "1.0", "end-1c")
+-        self.text.mark_set("insert", "1.0")
+-        self.text.see("insert")
+-        return "break"
+-
+-    def remove_selection(self, event=None):
+-        self.text.tag_remove("sel", "1.0", "end")
+-        self.text.see("insert")
+-
+-    def move_at_edge_if_selection(self, edge_index):
+-        """Cursor move begins at start or end of selection
+-
+-        When a left/right cursor key is pressed create and return to Tkinter a
+-        function which causes a cursor move from the associated edge of the
+-        selection.
+-
+-        """
+-        self_text_index = self.text.index
+-        self_text_mark_set = self.text.mark_set
+-        edges_table = ("sel.first+1c", "sel.last-1c")
+-        def move_at_edge(event):
+-            if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed
+-                try:
+-                    self_text_index("sel.first")
+-                    self_text_mark_set("insert", edges_table[edge_index])
+-                except TclError:
+-                    pass
+-        return move_at_edge
+-
+-    def del_word_left(self, event):
+-        self.text.event_generate('<Meta-Delete>')
+-        return "break"
+-
+-    def del_word_right(self, event):
+-        self.text.event_generate('<Meta-d>')
+-        return "break"
+-
+-    def find_event(self, event):
+-        SearchDialog.find(self.text)
+-        return "break"
+-
+-    def find_again_event(self, event):
+-        SearchDialog.find_again(self.text)
+-        return "break"
+-
+-    def find_selection_event(self, event):
+-        SearchDialog.find_selection(self.text)
+-        return "break"
+-
+-    def find_in_files_event(self, event):
+-        GrepDialog.grep(self.text, self.io, self.flist)
+-        return "break"
+-
+-    def replace_event(self, event):
+-        ReplaceDialog.replace(self.text)
+-        return "break"
+-
+-    def goto_line_event(self, event):
+-        text = self.text
+-        lineno = tkSimpleDialog.askinteger("Goto",
+-                "Go to line number:",parent=text)
+-        if lineno is None:
+-            return "break"
+-        if lineno <= 0:
+-            text.bell()
+-            return "break"
+-        text.mark_set("insert", "%d.0" % lineno)
+-        text.see("insert")
+-
+-    def open_module(self, event=None):
+-        # XXX Shouldn't this be in IOBinding or in FileList?
+-        try:
+-            name = self.text.get("sel.first", "sel.last")
+-        except TclError:
+-            name = ""
+-        else:
+-            name = name.strip()
+-        name = tkSimpleDialog.askstring("Module",
+-                 "Enter the name of a Python module\n"
+-                 "to search on sys.path and open:",
+-                 parent=self.text, initialvalue=name)
+-        if name:
+-            name = name.strip()
+-        if not name:
+-            return
+-        # XXX Ought to insert current file's directory in front of path
+-        try:
+-            (f, file, (suffix, mode, type)) = _find_module(name)
+-        except (NameError, ImportError), msg:
+-            tkMessageBox.showerror("Import error", str(msg), parent=self.text)
+-            return
+-        if type != imp.PY_SOURCE:
+-            tkMessageBox.showerror("Unsupported type",
+-                "%s is not a source module" % name, parent=self.text)
+-            return
+-        if f:
+-            f.close()
+-        if self.flist:
+-            self.flist.open(file)
+-        else:
+-            self.io.loadfile(file)
+-
+-    def open_class_browser(self, event=None):
+-        filename = self.io.filename
+-        if not filename:
+-            tkMessageBox.showerror(
+-                "No filename",
+-                "This buffer has no associated filename",
+-                master=self.text)
+-            self.text.focus_set()
+-            return None
+-        head, tail = os.path.split(filename)
+-        base, ext = os.path.splitext(tail)
+-        import ClassBrowser
+-        ClassBrowser.ClassBrowser(self.flist, base, [head])
+-
+-    def open_path_browser(self, event=None):
+-        import PathBrowser
+-        PathBrowser.PathBrowser(self.flist)
+-
+     def gotoline(self, lineno):
++        page = self.current_page
++        text = page.text
++
+         if lineno is not None and lineno > 0:
+-            self.text.mark_set("insert", "%d.0" % lineno)
+-            self.text.tag_remove("sel", "1.0", "end")
+-            self.text.tag_add("sel", "insert", "insert +1l")
+-            self.center()
++            text.mark_set("insert", "%d.0" % lineno)
++            text.tag_remove("sel", "1.0", "end")
++            text.tag_add("sel", "insert", "insert +1l")
++            page.center()
+ 
+     def ispythonsource(self, filename):
+         if not filename or os.path.isdir(filename):
+@@ -599,82 +330,59 @@
+     def set_close_hook(self, close_hook):
+         self.close_hook = close_hook
+ 
+-    def filename_change_hook(self):
+-        if self.flist:
+-            self.flist.filename_changed_edit(self)
+-        self.saved_change_hook()
+-        self.top.update_windowlist_registry(self)
+-        self.ResetColorizer()
++    def set_theme(self, ttkstyle):
++        # called from configDialog.py
++        ttkstyle.theme_use(idleConf.GetOption('main', 'Theme', 'displaytheme'))
+ 
+-    def _addcolorizer(self):
+-        if self.color:
+-            return
+-        if self.ispythonsource(self.io.filename):
+-            self.color = self.ColorDelegator()
+-        # can add more colorizers here...
+-        if self.color:
+-            self.per.removefilter(self.undo)
+-            self.per.insertfilter(self.color)
+-            self.per.insertfilter(self.undo)
+-
+-    def _rmcolorizer(self):
+-        if not self.color:
+-            return
+-        self.color.removecolors()
+-        self.per.removefilter(self.color)
+-        self.color = None
+-
+     def ResetColorizer(self):
+         "Update the colour theme"
+         # Called from self.filename_change_hook and from configDialog.py
+-        self._rmcolorizer()
+-        self._addcolorizer()
+-        theme = idleConf.GetOption('main','Theme','name')
+-        normal_colors = idleConf.GetHighlight(theme, 'normal')
+-        cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg')
+-        select_colors = idleConf.GetHighlight(theme, 'hilite')
+-        self.text.config(
+-            foreground=normal_colors['foreground'],
+-            background=normal_colors['background'],
+-            insertbackground=cursor_color,
+-            selectforeground=select_colors['foreground'],
+-            selectbackground=select_colors['background'],
+-            )
++        for page in self.text_notebook.pages.itervalues():
++            page.editpage.reset_colorizer()
+ 
+     def ResetFont(self):
+         "Update the text widgets' font if it is changed"
+         # Called from configDialog.py
+-        fontWeight='normal'
+-        if idleConf.GetOption('main','EditorWindow','font-bold',type='bool'):
+-            fontWeight='bold'
+-        self.text.config(font=(idleConf.GetOption('main','EditorWindow','font'),
+-                idleConf.GetOption('main','EditorWindow','font-size'),
++        fontWeight = 'normal'
++        if idleConf.GetOption('main', 'EditorPage', 'font-bold', type='bool'):
++            fontWeight = 'bold'
++
++        for page in self.text_notebook.pages.itervalues():
++            text = page.editpage.text
++            text.config(font=(idleConf.GetOption('main', 'EditorPage', 'font'),
++                idleConf.GetOption('main', 'EditorPage', 'font-size'),
+                 fontWeight))
+ 
+     def RemoveKeybindings(self):
+         "Remove the keybindings before they are changed."
+         # Called from configDialog.py
+-        self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
+-        for event, keylist in keydefs.items():
+-            self.text.event_delete(event, *keylist)
+-        for extensionName in self.get_standard_extension_names():
++        Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
++
++        for page in self.text_notebook.pages.itervalues():
++            text = page.editpage.text
++            for event, keylist in keydefs.items():
++                text.event_delete(event, *keylist)
++
++        for extensionName in self._get_standard_extension_names():
+             xkeydefs = idleConf.GetExtensionBindings(extensionName)
+             if xkeydefs:
+-                for event, keylist in xkeydefs.items():
+-                    self.text.event_delete(event, *keylist)
++                for page in self.text_notebook.pages.itervalues():
++                    text = page.editpage.text
++                    for event, keylist in xkeydefs.items():
++                        text.event_delete(event, *keylist)
+ 
+     def ApplyKeybindings(self):
+         "Update the keybindings after they are changed"
+         # Called from configDialog.py
+-        self.Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
++        Bindings.default_keydefs = keydefs = idleConf.GetCurrentKeySet()
+         self.apply_bindings()
+-        for extensionName in self.get_standard_extension_names():
++        for extensionName in self._get_standard_extension_names():
+             xkeydefs = idleConf.GetExtensionBindings(extensionName)
+             if xkeydefs:
+                 self.apply_bindings(xkeydefs)
+         #update menu accelerators
+         menuEventDict = {}
+-        for menu in self.Bindings.menudefs:
++        for menu in Bindings.menudefs:
+             menuEventDict[menu[0]] = {}
+             for item in menu[1]:
+                 if item:
+@@ -695,13 +403,6 @@
+                             accel = get_accelerator(keydefs, event)
+                             menu.entryconfig(index, accelerator=accel)
+ 
+-    def set_notabs_indentwidth(self):
+-        "Update the indentwidth if changed and not using tabs in this window"
+-        # Called from configDialog.py
+-        if not self.usetabs:
+-            self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces',
+-                                                  type='int')
+-
+     def reset_help_menu_entries(self):
+         "Update the additional help entries on the Help menu"
+         help_list = idleConf.GetAllExtraHelpSourcesList()
+@@ -719,19 +420,16 @@
+         # and update the menu dictionary
+         self.menudict['help'] = helpmenu
+ 
+-    def __extra_help_callback(self, helpfile):
+-        "Create a callback with the helpfile value frozen at definition time"
+-        def display_extra_help(helpfile=helpfile):
+-            if not helpfile.startswith(('www', 'http')):
+-                url = os.path.normpath(helpfile)
+-            if sys.platform[:3] == 'win':
+-                os.startfile(helpfile)
+-            else:
+-                webbrowser.open(helpfile)
+-        return display_extra_help
++    def set_notabs_indentwidth(self):
++        "Update the indentwidth if changed and not using tabs in this window"
++        # Called from configDialog.py
++        if not self.usetabs:
++            self.indentwidth = idleConf.GetOption('main', 'Indent','num-spaces',
++                                                  type='int')
+ 
+     def update_recent_files_list(self, new_file=None):
+         "Load and update the recent files list and menus"
++        # IOBinding calls this
+         rf_list = []
+         if os.path.exists(self.recent_files_path):
+             rf_list_file = open(self.recent_files_path,'r')
+@@ -761,83 +459,18 @@
+         for instance in self.top.instance_dict.keys():
+             menu = instance.recent_files_menu
+             menu.delete(1, END)  # clear, and rebuild:
+-            for i, file in zip(count(), rf_list):
++            for i, file in enumerate(rf_list):
+                 file_name = file[0:-1]  # zap \n
+                 # make unicode string to display non-ASCII chars correctly
+-                ufile_name = self._filename_to_unicode(file_name)
++                ufile_name = filename_to_unicode(file_name)
+                 callback = instance.__recent_file_callback(file_name)
+                 menu.add_command(label=ulchars[i] + " " + ufile_name,
+                                  command=callback,
+                                  underline=0)
+ 
+-    def __recent_file_callback(self, file_name):
+-        def open_recent_file(fn_closure=file_name):
+-            self.io.open(editFile=fn_closure)
+-        return open_recent_file
+-
+-    def saved_change_hook(self):
+-        short = self.short_title()
+-        long = self.long_title()
+-        if short and long:
+-            title = short + " - " + long
+-        elif short:
+-            title = short
+-        elif long:
+-            title = long
+-        else:
+-            title = "Untitled"
+-        icon = short or long or title
+-        if not self.get_saved():
+-            title = "*%s*" % title
+-            icon = "*%s" % icon
+-        self.top.wm_title(title)
+-        self.top.wm_iconname(icon)
+-
+     def get_saved(self):
+-        return self.undo.get_saved()
++        return self.current_page.undo.get_saved() # XXX check this!
+ 
+-    def set_saved(self, flag):
+-        self.undo.set_saved(flag)
+-
+-    def reset_undo(self):
+-        self.undo.reset_undo()
+-
+-    def short_title(self):
+-        filename = self.io.filename
+-        if filename:
+-            filename = os.path.basename(filename)
+-        # return unicode string to display non-ASCII chars correctly
+-        return self._filename_to_unicode(filename)
+-
+-    def long_title(self):
+-        # return unicode string to display non-ASCII chars correctly
+-        return self._filename_to_unicode(self.io.filename or "")
+-
+-    def center_insert_event(self, event):
+-        self.center()
+-
+-    def center(self, mark="insert"):
+-        text = self.text
+-        top, bot = self.getwindowlines()
+-        lineno = self.getlineno(mark)
+-        height = bot - top
+-        newtop = max(1, lineno - height//2)
+-        text.yview(float(newtop))
+-
+-    def getwindowlines(self):
+-        text = self.text
+-        top = self.getlineno("@0,0")
+-        bot = self.getlineno("@0,65535")
+-        if top == bot and text.winfo_height() == 1:
+-            # Geometry manager hasn't run yet
+-            height = int(text['height'])
+-            bot = top + height - 1
+-        return top, bot
+-
+-    def getlineno(self, mark="insert"):
+-        text = self.text
+-        return int(float(text.index(mark)))
+-
+     def get_geometry(self):
+         "Return (width, height, x, y)"
+         geom = self.top.wm_geometry()
+@@ -848,132 +481,49 @@
+     def close_event(self, event):
+         self.close()
+ 
+-    def maybesave(self):
+-        if self.io:
+-            if not self.get_saved():
+-                if self.top.state()!='normal':
+-                    self.top.deiconify()
+-                self.top.lower()
+-                self.top.lift()
+-            return self.io.maybesave()
+-
+     def close(self):
+-        reply = self.maybesave()
+-        if str(reply) != "cancel":
+-            self._close()
+-        return reply
++        to_check = self.text_notebook.pages.copy()
+ 
++        while to_check:
++            curr_tab = self.text_notebook.select()
++            if TTK:
++                page_name = self.text_notebook.tab(curr_tab)['text']
++            else:
++                page_name = curr_tab
++            page = to_check.pop(page_name)
++            editpage = page.editpage
++            reply = editpage.close_tab()
++            if reply == "cancel":
++                break
++
+     def _close(self):
+-        if self.io.filename:
+-            self.update_recent_files_list(new_file=self.io.filename)
+         WindowList.unregister_callback(self.postwindowsmenu)
+-        self.unload_extensions()
+-        self.io.close()
+-        self.io = None
+-        self.undo = None
+-        if self.color:
+-            self.color.close(False)
+-            self.color = None
+-        self.text = None
++        self._unload_extensions()
+         self.tkinter_vars = None
+-        self.per.close()
+-        self.per = None
++
++        for page in self.text_notebook.pages.itervalues():
++            page.editpage.close()
++
+         self.top.destroy()
+         if self.close_hook:
+             # unless override: unregister from flist, terminate if last window
+             self.close_hook()
+ 
+-    def load_extensions(self):
+-        self.extensions = {}
+-        self.load_standard_extensions()
+-
+-    def unload_extensions(self):
+-        for ins in self.extensions.values():
+-            if hasattr(ins, "close"):
+-                ins.close()
+-        self.extensions = {}
+-
+-    def load_standard_extensions(self):
+-        for name in self.get_standard_extension_names():
+-            try:
+-                self.load_extension(name)
+-            except:
+-                print "Failed to load extension", repr(name)
+-                import traceback
+-                traceback.print_exc()
+-
+-    def get_standard_extension_names(self):
+-        return idleConf.GetExtensions(editor_only=True)
+-
+-    def load_extension(self, name):
+-        try:
+-            mod = __import__(name, globals(), locals(), [])
+-        except ImportError:
+-            print "\nFailed to import extension: ", name
+-            return
+-        cls = getattr(mod, name)
+-        keydefs = idleConf.GetExtensionBindings(name)
+-        if hasattr(cls, "menudefs"):
+-            self.fill_menus(cls.menudefs, keydefs)
+-        ins = cls(self)
+-        self.extensions[name] = ins
+-        if keydefs:
+-            self.apply_bindings(keydefs)
+-            for vevent in keydefs.keys():
+-                methodname = vevent.replace("-", "_")
+-                while methodname[:1] == '<':
+-                    methodname = methodname[1:]
+-                while methodname[-1:] == '>':
+-                    methodname = methodname[:-1]
+-                methodname = methodname + "_event"
+-                if hasattr(ins, methodname):
+-                    self.text.bind(vevent, getattr(ins, methodname))
+-
+-    def apply_bindings(self, keydefs=None):
++    def apply_bindings(self, keydefs=None, tab=None):
+         if keydefs is None:
+-            keydefs = self.Bindings.default_keydefs
+-        text = self.text
+-        text.keydefs = keydefs
+-        for event, keylist in keydefs.items():
+-            if keylist:
+-                text.event_add(event, *keylist)
++            keydefs = Bindings.default_keydefs
+ 
+-    def fill_menus(self, menudefs=None, keydefs=None):
+-        """Add appropriate entries to the menus and submenus
++        if tab:
++            iter_over = [tab]
++        else:
++            iter_over = self.text_notebook.pages.itervalues()
+ 
+-        Menus that are absent or None in self.menudict are ignored.
+-        """
+-        if menudefs is None:
+-            menudefs = self.Bindings.menudefs
+-        if keydefs is None:
+-            keydefs = self.Bindings.default_keydefs
+-        menudict = self.menudict
+-        text = self.text
+-        for mname, entrylist in menudefs:
+-            menu = menudict.get(mname)
+-            if not menu:
+-                continue
+-            for entry in entrylist:
+-                if not entry:
+-                    menu.add_separator()
+-                else:
+-                    label, eventname = entry
+-                    checkbutton = (label[:1] == '!')
+-                    if checkbutton:
+-                        label = label[1:]
+-                    underline, label = prepstr(label)
+-                    accelerator = get_accelerator(keydefs, eventname)
+-                    def command(text=text, eventname=eventname):
+-                        text.event_generate(eventname)
+-                    if checkbutton:
+-                        var = self.get_var_obj(eventname, BooleanVar)
+-                        menu.add_checkbutton(label=label, underline=underline,
+-                            command=command, accelerator=accelerator,
+-                            variable=var)
+-                    else:
+-                        menu.add_command(label=label, underline=underline,
+-                                         command=command,
+-                                         accelerator=accelerator)
++        for page in iter_over:
++            text = page.editpage.text
++            text.keydefs = keydefs
++            for event, keylist in keydefs.items():
++                if keylist:
++                    text.event_add(event, *keylist)
+ 
+     def getvar(self, name):
+         var = self.get_var_obj(name)
+@@ -990,52 +540,25 @@
+         else:
+             raise NameError, name
+ 
+-    def get_var_obj(self, name, vartype=None):
++    def get_var_obj(self, name, vartype=None, text=None):
+         var = self.tkinter_vars.get(name)
+         if not var and vartype:
+             # create a Tkinter variable object with self.text as master:
+-            self.tkinter_vars[name] = var = vartype(self.text)
++            self.tkinter_vars[name] = var = vartype(text or self.text)
+         return var
+ 
+     # Tk implementations of "virtual text methods" -- each platform
+     # reusing IDLE's support code needs to define these for its GUI's
+     # flavor of widget.
+ 
+-    # Is character at text_index in a Python string?  Return 0 for
+-    # "guaranteed no", true for anything else.  This info is expensive
+-    # to compute ab initio, but is probably already known by the
+-    # platform's colorizer.
+-
+-    def is_char_in_string(self, text_index):
+-        if self.color:
+-            # Return true iff colorizer hasn't (re)gotten this far
+-            # yet, or the character is tagged as being in a string
+-            return self.text.tag_prevrange("TODO", text_index) or \
+-                   "STRING" in self.text.tag_names(text_index)
+-        else:
+-            # The colorizer is missing: assume the worst
+-            return 1
+-
+-    # If a selection is defined in the text widget, return (start,
+-    # end) as Tkinter text indices, otherwise return (None, None)
+-    def get_selection_indices(self):
+-        try:
+-            first = self.text.index("sel.first")
+-            last = self.text.index("sel.last")
+-            return first, last
+-        except TclError:
+-            return None, None
+-
+     # Return the text widget's current view of what a tab stop means
+     # (equivalent width in spaces).
+-
+-    def get_tabwidth(self):
++    def get_tabwidth(self): # XXX depends on self.text
+         current = self.text['tabs'] or TK_TABWIDTH_DEFAULT
+         return int(current)
+ 
+     # Set the text widget's current view of what a tab stop means.
+-
+-    def set_tabwidth(self, newtabwidth):
++    def set_tabwidth(self, newtabwidth): # XXX depends on self.text
+         text = self.text
+         if self.get_tabwidth() != newtabwidth:
+             pixels = text.tk.call("font", "measure", text["font"],
+@@ -1048,7 +571,6 @@
+     # indentwidth != tabwidth set usetabs false.
+     # In any case, adjust the Text widget's view of what a tab
+     # character means.
+-
+     def set_indentation_params(self, ispythonsource, guess=True):
+         if guess and ispythonsource:
+             i = self.guess_indent()
+@@ -1058,386 +580,187 @@
+                 self.usetabs = False
+         self.set_tabwidth(self.tabwidth)
+ 
+-    def smart_backspace_event(self, event):
+-        text = self.text
+-        first, last = self.get_selection_indices()
+-        if first and last:
+-            text.delete(first, last)
+-            text.mark_set("insert", first)
+-            return "break"
+-        # Delete whitespace left, until hitting a real char or closest
+-        # preceding virtual tab stop.
+-        chars = text.get("insert linestart", "insert")
+-        if chars == '':
+-            if text.compare("insert", ">", "1.0"):
+-                # easy: delete preceding newline
+-                text.delete("insert-1c")
+-            else:
+-                text.bell()     # at start of buffer
+-            return "break"
+-        if  chars[-1] not in " \t":
+-            # easy: delete preceding real char
+-            text.delete("insert-1c")
+-            return "break"
+-        # Ick.  It may require *inserting* spaces if we back up over a
+-        # tab character!  This is written to be clear, not fast.
+-        tabwidth = self.tabwidth
+-        have = len(chars.expandtabs(tabwidth))
+-        assert have > 0
+-        want = ((have - 1) // self.indentwidth) * self.indentwidth
+-        # Debug prompt is multilined....
+-        last_line_of_prompt = sys.ps1.split('\n')[-1]
+-        ncharsdeleted = 0
+-        while 1:
+-            if chars == last_line_of_prompt:
+-                break
+-            chars = chars[:-1]
+-            ncharsdeleted = ncharsdeleted + 1
+-            have = len(chars.expandtabs(tabwidth))
+-            if have <= want or chars[-1] not in " \t":
+-                break
+-        text.undo_block_start()
+-        text.delete("insert-%dc" % ncharsdeleted, "insert")
+-        if have < want:
+-            text.insert("insert", ' ' * (want - have))
+-        text.undo_block_stop()
+-        return "break"
++    # Guess indentwidth from text content.
++    # Return guessed indentwidth.  This should not be believed unless
++    # it's in a reasonable range (e.g., it will be 0 if no indented
++    # blocks are found).
++    def guess_indent(self): # XXX depends on self.text
++        opener, indented = IndentSearcher(self.text, self.tabwidth).run()
++        if opener and indented:
++            raw, indentsmall = classifyws(opener, self.tabwidth)
++            raw, indentlarge = classifyws(indented, self.tabwidth)
++        else:
++            indentsmall = indentlarge = 0
++        return indentlarge - indentsmall
+ 
+-    def smart_indent_event(self, event):
+-        # if intraline selection:
+-        #     delete it
+-        # elif multiline selection:
+-        #     do indent-region
+-        # else:
+-        #     indent one level
+-        text = self.text
+-        first, last = self.get_selection_indices()
+-        text.undo_block_start()
+-        try:
+-            if first and last:
+-                if index2line(first) != index2line(last):
+-                    return self.indent_region_event(event)
+-                text.delete(first, last)
+-                text.mark_set("insert", first)
+-            prefix = text.get("insert linestart", "insert")
+-            raw, effective = classifyws(prefix, self.tabwidth)
+-            if raw == len(prefix):
+-                # only whitespace to the left
+-                self.reindent_to(effective + self.indentwidth)
+-            else:
+-                # tab to the next 'stop' within or to right of line's text:
+-                if self.usetabs:
+-                    pad = '\t'
+-                else:
+-                    effective = len(prefix.expandtabs(self.tabwidth))
+-                    n = self.indentwidth
+-                    pad = ' ' * (n - effective % n)
+-                text.insert("insert", pad)
+-            text.see("insert")
+-            return "break"
+-        finally:
+-            text.undo_block_stop()
++    # Private methods/attributes
+ 
+-    def newline_and_indent_event(self, event):
+-        text = self.text
+-        first, last = self.get_selection_indices()
+-        text.undo_block_start()
+-        try:
+-            if first and last:
+-                text.delete(first, last)
+-                text.mark_set("insert", first)
+-            line = text.get("insert linestart", "insert")
+-            i, n = 0, len(line)
+-            while i < n and line[i] in " \t":
+-                i = i+1
+-            if i == n:
+-                # the cursor is in or at leading indentation in a continuation
+-                # line; just inject an empty line at the start
+-                text.insert("insert linestart", '\n')
+-                return "break"
+-            indent = line[:i]
+-            # strip whitespace before insert point unless it's in the prompt
+-            i = 0
+-            last_line_of_prompt = sys.ps1.split('\n')[-1]
+-            while line and line[-1] in " \t" and line != last_line_of_prompt:
+-                line = line[:-1]
+-                i = i+1
+-            if i:
+-                text.delete("insert - %d chars" % i, "insert")
+-            # strip whitespace after insert point
+-            while text.get("insert") in " \t":
+-                text.delete("insert")
+-            # start new line
+-            text.insert("insert", '\n')
++    # extensions won't have more than one instance per window
++    _unique_extensions = ['CodeContext', 'ScriptBinding', 'FormatParagraph']
+ 
+-            # adjust indentation for continuations and block
+-            # open/close first need to find the last stmt
+-            lno = index2line(text.index('insert'))
+-            y = PyParse.Parser(self.indentwidth, self.tabwidth)
+-            if not self.context_use_ps1:
+-                for context in self.num_context_lines:
+-                    startat = max(lno - context, 1)
+-                    startatindex = `startat` + ".0"
+-                    rawtext = text.get(startatindex, "insert")
+-                    y.set_str(rawtext)
+-                    bod = y.find_good_parse_start(
+-                              self.context_use_ps1,
+-                              self._build_char_in_string_func(startatindex))
+-                    if bod is not None or startat == 1:
+-                        break
+-                y.set_lo(bod or 0)
+-            else:
+-                r = text.tag_prevrange("console", "insert")
+-                if r:
+-                    startatindex = r[1]
+-                else:
+-                    startatindex = "1.0"
+-                rawtext = text.get(startatindex, "insert")
+-                y.set_str(rawtext)
+-                y.set_lo(0)
++    def _unload_extensions(self):
++        for ins in self.extensions.values():
++            if hasattr(ins, "close"):
++                ins.close()
++        self.extensions = {}
+ 
+-            c = y.get_continuation_type()
+-            if c != PyParse.C_NONE:
+-                # The current stmt hasn't ended yet.
+-                if c == PyParse.C_STRING_FIRST_LINE:
+-                    # after the first line of a string; do not indent at all
+-                    pass
+-                elif c == PyParse.C_STRING_NEXT_LINES:
+-                    # inside a string which started before this line;
+-                    # just mimic the current indent
+-                    text.insert("insert", indent)
+-                elif c == PyParse.C_BRACKET:
+-                    # line up with the first (if any) element of the
+-                    # last open bracket structure; else indent one
+-                    # level beyond the indent of the line with the
+-                    # last open bracket
+-                    self.reindent_to(y.compute_bracket_indent())
+-                elif c == PyParse.C_BACKSLASH:
+-                    # if more than one line in this stmt already, just
+-                    # mimic the current indent; else if initial line
+-                    # has a start on an assignment stmt, indent to
+-                    # beyond leftmost =; else to beyond first chunk of
+-                    # non-whitespace on initial line
+-                    if y.get_num_lines_in_stmt() > 1:
+-                        text.insert("insert", indent)
+-                    else:
+-                        self.reindent_to(y.compute_backslash_indent())
+-                else:
+-                    assert 0, "bogus continuation type %r" % (c,)
+-                return "break"
++    def _load_extension(self, name, tab):
++        ext_loaded = self.extensions.get(name, None)
+ 
+-            # This line starts a brand new stmt; indent relative to
+-            # indentation of initial line of closest preceding
+-            # interesting stmt.
+-            indent = y.get_base_indent_string()
+-            text.insert("insert", indent)
+-            if y.is_block_opener():
+-                self.smart_indent_event(event)
+-            elif indent and y.is_block_closer():
+-                self.smart_backspace_event(event)
+-            return "break"
+-        finally:
+-            text.see("insert")
+-            text.undo_block_stop()
++        try:
++            mod = __import__(name, globals(), locals(), [])
++        except ImportError:
++            print "\nFailed to import extension: ", name
++            return
+ 
+-    # Our editwin provides a is_char_in_string function that works
+-    # with a Tk text index, but PyParse only knows about offsets into
+-    # a string. This builds a function for PyParse that accepts an
+-    # offset.
++        keydefs = idleConf.GetExtensionBindings(name)
+ 
+-    def _build_char_in_string_func(self, startindex):
+-        def inner(offset, _startindex=startindex,
+-                  _icis=self.is_char_in_string):
+-            return _icis(_startindex + "+%dc" % offset)
+-        return inner
++        if name not in self._unique_extensions or not ext_loaded:
++            # create a new instance
++            cls = getattr(mod, name)
++            ins = cls(tab.editpage)
++            self.extensions.setdefault(name, []).append(ins)
++            if not ext_loaded:
++                # create new items in menu only if this is the first time this
++                # extension is being loaded in this window
++                if hasattr(cls, "menudefs"):
++                    self._fill_menus(cls.menudefs, keydefs)
++        elif name in self._unique_extensions and ext_loaded:
++            # get an existing instance
++            ins = self.extensions[name][0]
+ 
+-    def indent_region_event(self, event):
+-        head, tail, chars, lines = self.get_region()
+-        for pos in range(len(lines)):
+-            line = lines[pos]
+-            if line:
+-                raw, effective = classifyws(line, self.tabwidth)
+-                effective = effective + self.indentwidth
+-                lines[pos] = self._make_blanks(effective) + line[raw:]
+-        self.set_region(head, tail, chars, lines)
+-        return "break"
++        if keydefs:
++            self.apply_bindings(keydefs, tab)
++            for vevent in keydefs.keys():
++                methodname = vevent.replace("-", "_")
++                while methodname[:1] == '<':
++                    methodname = methodname[1:]
++                while methodname[-1:] == '>':
++                    methodname = methodname[:-1]
++                methodname = methodname + "_event"
++                if hasattr(ins, methodname):
++                    tab.editpage.text.bind(vevent, getattr(ins, methodname))
+ 
+-    def dedent_region_event(self, event):
+-        head, tail, chars, lines = self.get_region()
+-        for pos in range(len(lines)):
+-            line = lines[pos]
+-            if line:
+-                raw, effective = classifyws(line, self.tabwidth)
+-                effective = max(effective - self.indentwidth, 0)
+-                lines[pos] = self._make_blanks(effective) + line[raw:]
+-        self.set_region(head, tail, chars, lines)
+-        return "break"
++    def _load_extensions(self):
++        self._load_standard_extensions(self.text_notebook.last_page())
+ 
+-    def comment_region_event(self, event):
+-        head, tail, chars, lines = self.get_region()
+-        for pos in range(len(lines) - 1):
+-            line = lines[pos]
+-            lines[pos] = '##' + line
+-        self.set_region(head, tail, chars, lines)
++    def _load_standard_extensions(self, tab):
++        for name in self._get_standard_extension_names():
++            try:
++                self._load_extension(name, tab)
++            except:
++                print "Failed to load extension", repr(name)
++                traceback.print_exc()
+ 
+-    def uncomment_region_event(self, event):
+-        head, tail, chars, lines = self.get_region()
+-        for pos in range(len(lines)):
+-            line = lines[pos]
+-            if not line:
+-                continue
+-            if line[:2] == '##':
+-                line = line[2:]
+-            elif line[:1] == '#':
+-                line = line[1:]
+-            lines[pos] = line
+-        self.set_region(head, tail, chars, lines)
++    def _get_standard_extension_names(self):
++        return idleConf.GetExtensions(editor_only=True)
+ 
+-    def tabify_region_event(self, event):
+-        head, tail, chars, lines = self.get_region()
+-        tabwidth = self._asktabwidth()
+-        for pos in range(len(lines)):
+-            line = lines[pos]
+-            if line:
+-                raw, effective = classifyws(line, tabwidth)
+-                ntabs, nspaces = divmod(effective, tabwidth)
+-                lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
+-        self.set_region(head, tail, chars, lines)
++    def _post_tab_close(self, event):
++        if not self.current_page:
++            # no tabs now, close window
++            self._close()
++            return
+ 
+-    def untabify_region_event(self, event):
+-        head, tail, chars, lines = self.get_region()
+-        tabwidth = self._asktabwidth()
+-        for pos in range(len(lines)):
+-            lines[pos] = lines[pos].expandtabs(tabwidth)
+-        self.set_region(head, tail, chars, lines)
++    def _update_controls(self, event):
++        curr_page = self.current_page
++        if not curr_page:
++            return
+ 
+-    def toggle_tabs_event(self, event):
+-        if self.askyesno(
+-              "Toggle tabs",
+-              "Turn tabs " + ("on", "off")[self.usetabs] +
+-              "?\nIndent width " +
+-              ("will be", "remains at")[self.usetabs] + " 8." +
+-              "\n Note: a tab is always 8 columns",
+-              parent=self.text):
+-            self.usetabs = not self.usetabs
+-            # Try to prevent inconsistent indentation.
+-            # User must change indent width manually after using tabs.
+-            self.indentwidth = 8
+-        return "break"
++        self.text = curr_page.text
++        curr_page.saved_change_hook(True, False) # update window title
++        curr_page.text.focus_set()
++        self.set_line_and_column()
+ 
+-    # XXX this isn't bound to anything -- see tabwidth comments
+-##     def change_tabwidth_event(self, event):
+-##         new = self._asktabwidth()
+-##         if new != self.tabwidth:
+-##             self.tabwidth = new
+-##             self.set_indentation_params(0, guess=0)
+-##         return "break"
++        # update references in extensions that are loaded only once
++        for ext in self._unique_extensions:
++            if ext not in self.extensions:
++                continue
++            ext = self.extensions[ext][0]
++            ext.editpage = curr_page
+ 
+-    def change_indentwidth_event(self, event):
+-        new = self.askinteger(
+-                  "Indent width",
+-                  "New indent width (2-16)\n(Always use 8 when using tabs)",
+-                  parent=self.text,
+-                  initialvalue=self.indentwidth,
+-                  minvalue=2,
+-                  maxvalue=16)
+-        if new and new != self.indentwidth and not self.usetabs:
+-            self.indentwidth = new
+-        return "break"
++    def _create_statusbar(self):
++        self.status_bar = MultiStatusBar(self.top)
++        if macosxSupport.runningAsOSXApp():
++            # Insert some padding to avoid obscuring some of the statusbar
++            # by the resize widget.
++            self.status_bar.set_label('_padding1', '    ', side=RIGHT)
++        self.status_bar.set_label('column', 'Col: ?', side=RIGHT)
++        self.status_bar.set_label('line', 'Ln: ?', side=RIGHT)
++        self.status_bar.pack(side=BOTTOM, fill=X)
+ 
+-    def get_region(self):
+-        text = self.text
+-        first, last = self.get_selection_indices()
+-        if first and last:
+-            head = text.index(first + " linestart")
+-            tail = text.index(last + "-1c lineend +1c")
+-        else:
+-            head = text.index("insert linestart")
+-            tail = text.index("insert lineend +1c")
+-        chars = text.get(head, tail)
+-        lines = chars.split("\n")
+-        return head, tail, chars, lines
++    def _createmenubar(self, text):
++        mbar = self.menubar
++        menudict = self.menudict
++        for name, label in self.menu_specs:
++            underline, label = prepstr(label)
++            menudict[name] = menu = Menu(mbar, name=name, tearoff=0)
++            mbar.add_cascade(label=label, menu=menu, underline=underline)
+ 
+-    def set_region(self, head, tail, chars, lines):
+-        text = self.text
+-        newchars = "\n".join(lines)
+-        if newchars == chars:
+-            text.bell()
+-            return
+-        text.tag_remove("sel", "1.0", "end")
+-        text.mark_set("insert", head)
+-        text.undo_block_start()
+-        text.delete(head, tail)
+-        text.insert(head, newchars)
+-        text.undo_block_stop()
+-        text.tag_add("sel", head, "insert")
++        if sys.platform == 'darwin' and '.framework' in sys.executable:
++            # Insert the application menu
++            menudict['application'] = menu = Menu(mbar, name='apple')
++            mbar.add_cascade(label='IDLE', menu=menu)
+ 
+-    # Make string that displays as n leading blanks.
++        self._fill_menus(text=text)
++        self.base_helpmenu_length = self.menudict['help'].index(END)
++        self.reset_help_menu_entries()
+ 
+-    def _make_blanks(self, n):
+-        if self.usetabs:
+-            ntabs, nspaces = divmod(n, self.tabwidth)
+-            return '\t' * ntabs + ' ' * nspaces
+-        else:
+-            return ' ' * n
++    def _fill_menus(self, menudefs=None, keydefs=None, text=None):
++        """Add appropriate entries to the menus and submenus
+ 
+-    # Delete from beginning of line to insert point, then reinsert
+-    # column logical (meaning use tabs if appropriate) spaces.
++        Menus that are absent or None in self.menudict are ignored.
++        """
++        if menudefs is None:
++            menudefs = Bindings.menudefs
++        if keydefs is None:
++            keydefs = Bindings.default_keydefs
++        menudict = self.menudict
++        for mname, entrylist in menudefs:
++            menu = menudict.get(mname)
++            if not menu:
++                continue
++            for entry in entrylist:
++                if not entry:
++                    menu.add_separator()
++                else:
++                    label, eventname = entry
++                    checkbutton = (label[:1] == '!')
++                    if checkbutton:
++                        label = label[1:]
++                    underline, label = prepstr(label)
++                    accelerator = get_accelerator(keydefs, eventname)
++                    def command(eventname=eventname):
++                        self.text.event_generate(eventname)
++                    if checkbutton:
++                        var = self.get_var_obj(eventname, BooleanVar, text)
++                        menu.add_checkbutton(label=label, underline=underline,
++                            command=command, accelerator=accelerator,
++                            variable=var)
++                    else:
++                        menu.add_command(label=label, underline=underline,
++                            command=command, accelerator=accelerator)
+ 
+-    def reindent_to(self, column):
+-        text = self.text
+-        text.undo_block_start()
+-        if text.compare("insert linestart", "!=", "insert"):
+-            text.delete("insert linestart", "insert")
+-        if column:
+-            text.insert("insert", self._make_blanks(column))
+-        text.undo_block_stop()
++    def __recent_file_callback(self, file_name):
++        def open_recent_file(fn_closure=file_name):
++            self.current_page.io.open(editFile=fn_closure)
++        return open_recent_file
+ 
+-    def _asktabwidth(self):
+-        return self.askinteger(
+-            "Tab width",
+-            "Columns per tab? (2-16)",
+-            parent=self.text,
+-            initialvalue=self.indentwidth,
+-            minvalue=2,
+-            maxvalue=16) or self.tabwidth
++    def __extra_help_callback(self, helpfile):
++        "Create a callback with the helpfile value frozen at definition time"
++        def display_extra_help(helpfile=helpfile):
++            if not helpfile.startswith(('www', 'http')):
++                url = os.path.normpath(helpfile)
++            if sys.platform[:3] == 'win':
++                os.startfile(helpfile)
++            else:
++                webbrowser.open(helpfile)
++        return display_extra_help
+ 
+-    # Guess indentwidth from text content.
+-    # Return guessed indentwidth.  This should not be believed unless
+-    # it's in a reasonable range (e.g., it will be 0 if no indented
+-    # blocks are found).
+-
+-    def guess_indent(self):
+-        opener, indented = IndentSearcher(self.text, self.tabwidth).run()
+-        if opener and indented:
+-            raw, indentsmall = classifyws(opener, self.tabwidth)
+-            raw, indentlarge = classifyws(indented, self.tabwidth)
+-        else:
+-            indentsmall = indentlarge = 0
+-        return indentlarge - indentsmall
+-
+-# "line.col" -> line, as an int
+-def index2line(index):
+-    return int(float(index))
+-
+ # Look at the leading whitespace in s.
+ # Return pair (# of leading ws characters,
+ #              effective # of leading blanks after expanding
+ #              tabs to width tabwidth)
+ 
+-def classifyws(s, tabwidth):
+-    raw = effective = 0
+-    for ch in s:
+-        if ch == ' ':
+-            raw = raw + 1
+-            effective = effective + 1
+-        elif ch == '\t':
+-            raw = raw + 1
+-            effective = (effective // tabwidth + 1) * tabwidth
+-        else:
+-            break
+-    return raw, effective
+-
+ import tokenize
+ _tokenize = tokenize
+ del tokenize
+@@ -1534,6 +857,7 @@
+ 
+ 
+ def test():
++    from Tkinter import Tk
+     root = Tk()
+     fixwordbreaks(root)
+     root.withdraw()
+Index: help.txt
+===================================================================
+--- help.txt	(revision 63995)
++++ help.txt	(revision 65541)
+@@ -6,6 +6,7 @@
+ File Menu:
+ 
+ 	New Window       -- Create a new editing window
++    New Tab          -- Create a new editing tab
+ 	Open...          -- Open an existing file
+ 	Recent Files...  -- Open a list of recent files
+ 	Open Module...   -- Open an existing module (searches sys.path)
+@@ -23,6 +24,7 @@
+ 	---
+ 	Print Window     -- Print the current window
+ 	---
++    Close Tab        -- Close current tab (asks to save if unsaved)
+ 	Close            -- Close current window (asks to save if unsaved)
+ 	Exit             -- Close all windows, quit (asks to save if unsaved)
+ 
+Index: editorpage.py
+===================================================================
+--- editorpage.py	(revision 0)
++++ editorpage.py	(revision 65541)
+@@ -0,0 +1,925 @@
++import os
++import sys
++import imp
++import webbrowser
++import tkMessageBox
++import tkSimpleDialog
++from Tkinter import Text, Menu, TclError
++
++import utils
++import textView
++import aboutDialog
++import configDialog
++import macosxSupport
++import PyParse
++import IOBinding
++import GrepDialog
++import PathBrowser
++import ClassBrowser
++import SearchDialog
++import ReplaceDialog
++from configHandler import idleConf
++from MultiCall import MultiCallCreator
++from Percolator import Percolator
++
++def classifyws(s, tabwidth):
++    raw = effective = 0
++    for ch in s:
++        if ch == ' ':
++            raw = raw + 1
++            effective = effective + 1
++        elif ch == '\t':
++            raw = raw + 1
++            effective = (effective // tabwidth + 1) * tabwidth
++        else:
++            break
++    return raw, effective
++
++def index2line(index):
++    """"line.col" -> line, as an int"""
++    return int(float(index))
++
++def filename_to_unicode(filename):
++    """Convert filename to unicode in order to display it in Tk"""
++    if isinstance(filename, unicode) or not filename:
++        return filename
++    else:
++        try:
++            return filename.decode(IOBinding.filesystemencoding)
++        except UnicodeDecodeError:
++            # XXX
++            try:
++                return filename.decode(IOBinding.encoding)
++            except UnicodeDecodeError:
++                # byte-to-byte conversion
++                return filename.decode('iso8859-1')
++
++def _find_module(fullname, path=None):
++    """Version of imp.find_module() that handles hierarchical module names"""
++    file = None
++
++    for tgt in fullname.split('.'):
++        if file is not None:
++            file.close()            # close intermediate files
++        (file, filename, descr) = imp.find_module(tgt, path)
++        if descr[2] == imp.PY_SOURCE:
++            break                   # find but not load the source file
++        module = imp.load_module(tgt, file, filename, descr)
++        try:
++            path = module.__path__
++        except AttributeError:
++            raise ImportError('No source for module %s' % module.__name__)
++
++    return file, filename, descr
++
++class EditorPage(object):
++    def __init__(self, parent_frame, editwin, title=None, **kwargs):
++        self.editwin = editwin
++        self.title = title
++        self.tab_initialized = False
++        kwargs.setdefault('width', idleConf.GetOption('main', 'EditorPage',
++            'width'))
++        kwargs.setdefault('height', idleConf.GetOption('main', 'EditorPage',
++            'height'))
++
++        self.text = MultiCallCreator(Text)(parent_frame, **kwargs)
++        self.color = None # initialized in reset_colorizer
++        self.per = Percolator(self.text)
++        self.undo = self.editwin.UndoDelegator()
++        self.per.insertfilter(self.undo)
++        self.text.undo_block_start = self.undo.undo_block_start
++        self.text.undo_block_stop = self.undo.undo_block_stop
++        self.io = IOBinding.IOBinding(self)
++
++        self.undo.set_saved_change_hook(self.saved_change_hook)
++        self.io.set_filename_change_hook(self.filename_change_hook)
++        self.reset_colorizer()
++        self._setup_bindings()
++
++    def post_init(self, filename=None, update_window_title=False):
++        if filename:
++            if os.path.exists(filename) and not os.path.isdir(filename):
++                self.io.loadfile(filename)
++            else:
++                self.io.set_filename(filename)
++        self.saved_change_hook(update_window_title=update_window_title)
++        self.tab_initialized = True
++
++    def close_tab(self, event=None):
++        """Close current tab, if no more tabs present, close the window."""
++        if hasattr(self.editwin, 'interp'):
++            # this is a PyShell, don't ask to save
++            reply = 'yes'
++        else:
++            reply = str(self.maybesave())
++        if reply != "cancel":
++            if self.io.filename:
++                self.editwin.update_recent_files_list(new_file=self.io.filename)
++            self.close()
++            self.editwin.text_notebook.remove_page(self.title)
++            self.editwin.top.event_generate('<<tab-closed>>')
++
++        return reply
++
++    def close(self):
++        """Perform necessary cleanup for this page before closing it."""
++        self.io.close()
++        self.io = None
++
++        self.undo = None
++
++        if self.color:
++            self.color.close(False)
++            self.color = None
++
++        self.per.close()
++        self.per = None
++        self.text = None
++
++    # XXX (1) mark where these functions are used
++    def saved_change_hook(self,update_window_title=False,update_tab_title=True):
++        short = self.editwin.short_title()
++        long = self.long_title()
++
++        if short and long:
++            title = short + " - " + long
++            tabtitle = os.path.split(long)[-1]
++        elif short:
++            title = short
++            tabtitle = short
++        elif long:
++            title = long
++            tabtitle = os.path.split(long)[-1]
++        else:
++            title = tabtitle = "Untitled"
++        icon = short or long or title
++        if not self.get_saved():
++            title = "*%s*" % title
++            tabtitle = "*%s*" % tabtitle
++            icon = "*%s" % icon
++
++        if update_tab_title:
++            self.editwin.text_notebook.update_tabtitle(self, tabtitle)
++        if update_window_title or (
++           update_window_title is None and self.tab_initialized):
++            self.editwin.top.wm_title(title)
++            self.editwin.top.wm_iconname(icon)
++
++    def get_saved(self):
++        return self.undo.get_saved()
++
++    def set_saved(self, flag):
++        self.undo.set_saved(flag)
++
++    def filename_change_hook(self):
++        if self.editwin.flist:
++            self.editwin.flist.filename_changed_edit(self, self.editwin)
++        self.saved_change_hook(self.tab_initialized)
++        self.editwin.top.update_windowlist_registry(self.editwin)
++        self.reset_colorizer()
++
++    def reset_undo(self):
++        self.undo.reset_undo()
++
++    def reset_colorizer(self):
++        "Update the colour theme"
++        # Called from self.filename_change_hook and from configDialog.py
++        self.__rmcolorizer()
++        self.__addcolorizer()
++        theme = idleConf.GetOption('main','Theme','name')
++        normal_colors = idleConf.GetHighlight(theme, 'normal')
++        cursor_color = idleConf.GetHighlight(theme, 'cursor', fgBg='fg')
++        select_colors = idleConf.GetHighlight(theme, 'hilite')
++
++        self.text.config(
++            foreground=normal_colors['foreground'],
++            background=normal_colors['background'],
++            insertbackground=cursor_color,
++            selectforeground=select_colors['foreground'],
++            selectbackground=select_colors['background'])
++
++    def short_title(self):
++        filename = self.io.filename
++        if filename:
++            filename = os.path.basename(filename)
++        # return unicode string to display non-ASCII chars correctly
++        return filename_to_unicode(filename)
++
++    def long_title(self):
++        # return unicode string to display non-ASCII chars correctly
++        return filename_to_unicode(self.io.filename or "")
++
++    def maybesave(self):
++        if self.io:
++            if not self.get_saved():
++                if self.editwin.top.state()!= 'normal':
++                    self.editiwn.top.deiconify()
++                self.editwin.top.lower()
++                self.editwin.top.lift()
++            return self.io.maybesave()
++    # XXX (1) end
++
++    def center(self, mark="insert"):
++        # Used by EditorWindow.gotoline
++        text = self.text
++        top, bot = self._getwindowlines()
++        lineno = self._getlineno(mark)
++        height = bot - top
++        newtop = max(1, lineno - height//2)
++        text.yview(float(newtop))
++
++    def newline_and_indent_event(self, event):
++        # Used by EditorWindow.newline_and_indent_event which is used by PyShell
++        text = self.text
++        first, last = self.get_selection_indices()
++        text.undo_block_start()
++        try:
++            if first and last:
++                text.delete(first, last)
++                text.mark_set("insert", first)
++            line = text.get("insert linestart", "insert")
++            i, n = 0, len(line)
++            while i < n and line[i] in " \t":
++                i = i+1
++            if i == n:
++                # the cursor is in or at leading indentation in a continuation
++                # line; just inject an empty line at the start
++                text.insert("insert linestart", '\n')
++                return "break"
++            indent = line[:i]
++            # strip whitespace before insert point unless it's in the prompt
++            i = 0
++            last_line_of_prompt = sys.ps1.split('\n')[-1]
++            while line and line[-1] in " \t" and line != last_line_of_prompt:
++                line = line[:-1]
++                i = i+1
++            if i:
++                text.delete("insert - %d chars" % i, "insert")
++            # strip whitespace after insert point
++            while text.get("insert") in " \t":
++                text.delete("insert")
++            # start new line
++            text.insert("insert", '\n')
++
++            # adjust indentation for continuations and block
++            # open/close first need to find the last stmt
++            lno = index2line(text.index('insert'))
++            #print self.editwin.indentwidth, self.editwin.tabwidth
++            y = PyParse.Parser(self.editwin.indentwidth, self.editwin.tabwidth)
++            if not self.editwin.context_use_ps1:
++                for context in self.editwin.num_context_lines:
++                    startat = max(lno - context, 1)
++                    startatindex = `startat` + ".0"
++                    rawtext = text.get(startatindex, "insert")
++                    y.set_str(rawtext)
++                    bod = y.find_good_parse_start(
++                              self.editwin.context_use_ps1,
++                              self.build_char_in_string_func(startatindex))
++                    if bod is not None or startat == 1:
++                        break
++                y.set_lo(bod or 0)
++            else:
++                r = text.tag_prevrange("console", "insert")
++                if r:
++                    startatindex = r[1]
++                else:
++                    startatindex = "1.0"
++                rawtext = text.get(startatindex, "insert")
++                y.set_str(rawtext)
++                y.set_lo(0)
++
++            c = y.get_continuation_type()
++            if c != PyParse.C_NONE:
++                # The current stmt hasn't ended yet.
++                if c == PyParse.C_STRING_FIRST_LINE:
++                    # after the first line of a string; do not indent at all
++                    pass
++                elif c == PyParse.C_STRING_NEXT_LINES:
++                    # inside a string which started before this line;
++                    # just mimic the current indent
++                    text.insert("insert", indent)
++                elif c == PyParse.C_BRACKET:
++                    # line up with the first (if any) element of the
++                    # last open bracket structure; else indent one
++                    # level beyond the indent of the line with the
++                    # last open bracket
++                    self.__reindent_to(y.compute_bracket_indent())
++                elif c == PyParse.C_BACKSLASH:
++                    # if more than one line in this stmt already, just
++                    # mimic the current indent; else if initial line
++                    # has a start on an assignment stmt, indent to
++                    # beyond leftmost =; else to beyond first chunk of
++                    # non-whitespace on initial line
++                    if y.get_num_lines_in_stmt() > 1:
++                        text.insert("insert", indent)
++                    else:
++                        self.__reindent_to(y.compute_backslash_indent())
++                else:
++                    assert 0, "bogus continuation type %r" % (c,)
++                return "break"
++
++            # This line starts a brand new stmt; indent relative to
++            # indentation of initial line of closest preceding
++            # interesting stmt.
++            indent = y.get_base_indent_string()
++            text.insert("insert", indent)
++            if y.is_block_opener():
++                self._smart_indent_event(event)
++            elif indent and y.is_block_closer():
++                self._smart_backspace_event(event)
++            return "break"
++        finally:
++            text.see("insert")
++            text.undo_block_stop()
++
++    # If a selection is defined in the text widget, return (start,
++    # end) as Tkinter text indices, otherwise return (None, None)
++    def get_selection_indices(self):
++        # Used by EditorWindow.get_selection_indices which is used by
++        # FormatParagraph
++        try:
++            first = self.text.index("sel.first")
++            last = self.text.index("sel.last")
++            return first, last
++        except TclError:
++            return None, None
++
++    # Our editpage provides a __is_char_in_string function that works
++    # with a Tk text index, but PyParse only knows about offsets into
++    # a string. This builds a function for PyParse that accepts an
++    # offset.
++    def build_char_in_string_func(self, startindex):
++        # Used by EditorWindow.build_char_in_string_func which is used by
++        # HyperParser
++        def inner(offset, _startindex=startindex,
++                  _icis=self.__is_char_in_string):
++            return _icis(_startindex + "+%dc" % offset)
++        return inner
++
++    def _setup_bindings(self):
++        text = self.text
++        def bind_them(to_bind, prefix='_%s'):
++            for tb in to_bind:
++                prefix_size = tb.count('<')
++                method_name = tb[prefix_size:-prefix_size].replace('-', '_')
++                text.bind(tb, getattr(self, prefix % method_name.lower()))
++            
++        actions = ('<<help>>', '<<python-docs>>',
++            '<<about-idle>>', '<<open-config-dialog>>', '<<open-module>>',
++            '<<cut>>', '<<copy>>', '<<paste>>', '<<select-all>>',
++            '<<remove-selection>>', '<<del-word-left>>', '<<del-word-right>>',
++            '<<beginning-of-line>>')
++        events = ('<<find>>', '<<center-insert>>', '<<find-again>>',
++            '<<find-in-files>>', '<<find-selection>>', '<<replace>>',
++            '<<goto-line>>', '<<smart-backspace>>', '<<smart-indent>>',
++            '<<indent-region>>', '<<dedent-region>>', '<<comment-region>>',
++            '<<tabify-region>>', '<<untabify-region>>', '<<toggle-tabs>>',
++            '<<change-indentwidth>>')
++        parent_actions = ('<<new-tab>>', '<<next-tab>>', '<<prev-tab>>')
++
++        bind_them(actions)
++        bind_them(events, prefix="_%s_event")
++        for action in parent_actions:
++            prefix_size = action.count('<')
++            method_name = action[prefix_size:-prefix_size].replace('-', '_')
++            text.bind(action, getattr(self.editwin, method_name))
++
++        text.bind('<<close-tab>>', self.close_tab)
++        text.bind('<<newline-and-indent>>', self.newline_and_indent_event)
++        text.bind("<<do-nothing>>", lambda event: "break")
++        text.bind("<Left>", self._move_at_edge_if_selection(0))
++        text.bind("<Right>", self._move_at_edge_if_selection(1))
++        text.bind("<3>", self._right_menu)
++        text.bind('<<set-line-and-column>>', self.editwin.set_line_and_column)
++        text.event_add("<<set-line-and-column>>",
++                                    "<KeyRelease>", "<ButtonRelease>")
++
++        if self.editwin.flist:
++            text.bind("<<open-new-window>>",
++                utils.callback(self.editwin.new_callback, self))
++            text.bind("<<close-all-windows>>",
++                self.editwin.flist.close_all_callback)
++            text.bind("<<open-class-browser>>", self._open_class_browser)
++            text.bind("<<open-path-browser>>", self._open_path_browser)
++
++        if macosxSupport.runningAsOSXApp():
++            # Command-W on editorwindows doesn't work without this.
++            text.bind('<<close-window>>', self.editwin.close_event)
++
++    def _help(self, event=None):
++        fn = os.path.join(os.path.abspath(os.path.dirname(__file__)),
++            'help.txt')
++        textView.view_file(self.text, 'Help', fn)
++
++    def _python_docs(self, event=None):
++        if sys.platform[:3] == 'win':
++            os.startfile(self.editwin.help_url)
++        else:
++            webbrowser.open(self.editwin.help_url)
++        return "break"
++
++    def _about_idle(self, event=None):
++        aboutDialog.AboutDialog(self.text, 'About IDLE')
++
++    def _open_class_browser(self, event=None):
++        filename = self.io.filename
++        if not filename:
++            tkMessageBox.showerror("No filename",
++                "This buffer has no associated filename",
++                master=self.text)
++            self.text.focus_set()
++            return None
++        head, tail = os.path.split(filename)
++        base, ext = os.path.splitext(tail)
++        ClassBrowser.ClassBrowser(self.editwin.flist, base, [head])
++
++    def _open_path_browser(self, event=None):
++        PathBrowser.PathBrowser(self.editwin.flist)
++
++    def _open_config_dialog(self, event=None):
++        # When changing colors and saving it, it requires the attribute
++        # instance_dict making necessary to pass self.editwin.top as the
++        # parent
++        configDialog.ConfigDialog(self.editwin.top, 'Settings')
++
++    def _open_module(self, event=None):
++        try:
++            name = self.text.get("sel.first", "sel.last")
++        except TclError:
++            name = ""
++        else:
++            name = name.strip()
++
++        name = tkSimpleDialog.askstring("Module",
++            "Enter the name of a Python module\n"
++            "to search on sys.path and open:",
++            parent=self.text, initialvalue=name)
++
++        if name:
++            name = name.strip()
++        if not name:
++            return
++        # XXX Ought to insert current file's directory in front of path
++        try:
++            (f, file, (suffix, mode, type)) = _find_module(name)
++        except (NameError, ImportError), msg:
++            tkMessageBox.showerror("Import error", str(msg), parent=self.text)
++            return
++
++        if type != imp.PY_SOURCE:
++            tkMessageBox.showerror("Unsupported type",
++                "%s is not a source module" % name, parent=self.text)
++            return
++        if f:
++            f.close()
++        if self.editwin.flist:
++            if idleConf.GetOption('main', 'EditorWindow', 'file-in-tab',
++                default=1, type='bool'):
++                if hasattr(self.editwin, 'interp'):
++                    # PyShell window must open a module in a new window
++                    action = None
++                else:
++                    action = self.editwin.new_tab
++            else:
++                action = None
++            self.editwin.flist.open(file, action)
++        else:
++            self.io.loadfile(file)
++
++    def _find_event(self, event):
++        SearchDialog.find(self.text)
++        return "break"
++
++    def _find_again_event(self, event):
++        SearchDialog.find_again(self.text)
++        return "break"
++
++    def _find_selection_event(self, event):
++        SearchDialog.find_selection(self.text)
++        return "break"
++
++    def _find_in_files_event(self, event):
++        # XXX not expected to work correctly for now
++        GrepDialog.grep(self.text, self.editwin.io, self.editwin.flist)
++        return "break"
++
++    def _replace_event(self, event):
++        ReplaceDialog.replace(self.text)
++        return "break"
++
++    def _center_insert_event(self, event):
++        self.center()
++
++    def _getwindowlines(self):
++        text = self.text
++        top = self._getlineno("@0,0")
++        bot = self._getlineno("@0,65535")
++        if top == bot and text.winfo_height() == 1:
++            # Geometry manager hasn't run yet
++            height = int(text['height'])
++            bot = top + height - 1
++        return top, bot
++
++    def _getlineno(self, mark="insert"):
++        return int(float(self.text.index(mark)))
++
++    def _goto_line_event(self, event):
++        text = self.text
++        lineno = tkSimpleDialog.askinteger("Goto", "Go to line number:",
++            parent=text)
++
++        if lineno is None:
++            return "break"
++
++        if lineno <= 0:
++            text.bell()
++            return "break"
++
++        text.mark_set("insert", "%d.0" % lineno)
++        text.see("insert")
++
++    def _cut(self, event):
++        self.text.event_generate("<<Cut>>")
++        return "break"
++
++    def _copy(self, event):
++        if not self.text.tag_ranges("sel"):
++            # There is no selection, so do nothing and maybe interrupt.
++            return
++
++        self.text.event_generate("<<Copy>>")
++        return "break"
++
++    def _paste(self, event):
++        self.text.event_generate("<<Paste>>")
++        self.text.see("insert")
++        return "break"
++
++    def _select_all(self, event=None):
++        self.text.tag_add("sel", "1.0", "end-1c")
++        self.text.mark_set("insert", "1.0")
++        self.text.see("insert")
++        return "break"
++
++    def _remove_selection(self, event=None):
++        self.text.tag_remove("sel", "1.0", "end")
++        self.text.see("insert")
++
++    def _del_word_left(self, event):
++        self.text.event_generate('<Meta-Delete>')
++        return "break"
++
++    def _del_word_right(self, event):
++        self.text.event_generate('<Meta-d>')
++        return "break"
++
++    def _move_at_edge_if_selection(self, edge_index):
++        """Cursor move begins at start or end of selection
++        When a left/right cursor key is pressed create and return to Tkinter a
++        function which causes a cursor move from the associated edge of the
++        selection.
++        """
++        text_index = self.text.index
++        text_mark_set = self.text.mark_set
++        edges_table = ("sel.first+1c", "sel.last-1c")
++
++        def move_at_edge(event):
++            if (event.state & 5) == 0: # no shift(==1) or control(==4) pressed
++                try:
++                    text_index("sel.first")
++                    text_mark_set("insert", edges_table[edge_index])
++                except TclError:
++                    pass
++
++        return move_at_edge
++
++    def _beginning_of_line(self, event):
++        if (event.state & 12) != 0 and event.keysym == "Home":
++            # state&1==shift, state&4==control, state&8==alt
++            return # <Modifier-Home>; fall back to class binding
++
++        text = self.text
++
++        if text.index("iomark") and \
++           text.compare("iomark", "<=", "insert lineend") and \
++           text.compare("insert linestart", "<=", "iomark"):
++            insertpt = int(text.index("iomark").split(".")[1])
++        else:
++            line = text.get("insert linestart", "insert lineend")
++            for insertpt in xrange(len(line)):
++                if line[insertpt] not in (' ','\t'):
++                    break
++            else:
++                insertpt=len(line)
++
++        lineat = int(text.index("insert").split('.')[1])
++
++        if insertpt == lineat:
++            insertpt = 0
++
++        dest = "insert linestart+%sc" % str(insertpt)
++
++        if (event.state & 1) == 0:
++            # shift not pressed
++            text.tag_remove("sel", "1.0", "end")
++        else:
++            if not text.index("sel.first"):
++                text.mark_set("anchor", "insert")
++
++            first = text.index(dest)
++            last = text.index("anchor")
++
++            if text.compare(first, ">", last):
++                first, last = last, first
++
++            text.tag_remove("sel", "1.0", "end")
++            text.tag_add("sel", first, last)
++
++        text.mark_set("insert", dest)
++        text.see("insert")
++        return "break"
++
++    def _smart_backspace_event(self, event):
++        text = self.text
++        first, last = self.get_selection_indices()
++        if first and last:
++            text.delete(first, last)
++            text.mark_set("insert", first)
++            return "break"
++        # Delete whitespace left, until hitting a real char or closest
++        # preceding virtual tab stop.
++        chars = text.get("insert linestart", "insert")
++        if chars == '':
++            if text.compare("insert", ">", "1.0"):
++                # easy: delete preceding newline
++                text.delete("insert-1c")
++            else:
++                text.bell()     # at start of buffer
++            return "break"
++        if  chars[-1] not in " \t":
++            # easy: delete preceding real char
++            text.delete("insert-1c")
++            return "break"
++        # Ick.  It may require *inserting* spaces if we back up over a
++        # tab character!  This is written to be clear, not fast.
++        tabwidth = self.editwin.tabwidth
++        have = len(chars.expandtabs(tabwidth))
++        assert have > 0
++        want = ((have - 1) // 
++            self.editwin.indentwidth) * self.editwin.indentwidth
++        # Debug prompt is multilined....
++        last_line_of_prompt = sys.ps1.split('\n')[-1]
++        ncharsdeleted = 0
++        while 1:
++            if chars == last_line_of_prompt:
++                break
++            chars = chars[:-1]
++            ncharsdeleted = ncharsdeleted + 1
++            have = len(chars.expandtabs(tabwidth))
++            if have <= want or chars[-1] not in " \t":
++                break
++        text.undo_block_start()
++        text.delete("insert-%dc" % ncharsdeleted, "insert")
++        if have < want:
++            text.insert("insert", ' ' * (want - have))
++        text.undo_block_stop()
++        return "break"
++
++    def _smart_indent_event(self, event):
++        # if intraline selection:
++        #     delete it
++        # elif multiline selection:
++        #     do indent-region
++        # else:
++        #     indent one level
++        text = self.text
++        first, last = self.get_selection_indices()
++        text.undo_block_start()
++        try:
++            if first and last:
++                if index2line(first) != index2line(last):
++                    return self._indent_region_event(event)
++                text.delete(first, last)
++                text.mark_set("insert", first)
++            prefix = text.get("insert linestart", "insert")
++            raw, effective = classifyws(prefix, self.editwin.tabwidth)
++            if raw == len(prefix):
++                # only whitespace to the left
++                self.__reindent_to(effective + self.editwin.indentwidth)
++            else:
++                # tab to the next 'stop' within or to right of line's text:
++                if self.editwin.usetabs:
++                    pad = '\t'
++                else:
++                    effective = len(prefix.expandtabs(self.editwin.tabwidth))
++                    n = self.editwin.indentwidth
++                    pad = ' ' * (n - effective % n)
++                text.insert("insert", pad)
++            text.see("insert")
++            return "break"
++        finally:
++            text.undo_block_stop()
++
++    def _indent_region_event(self, event):
++        head, tail, chars, lines = self.__get_region()
++        for pos in range(len(lines)):
++            line = lines[pos]
++            if line:
++                raw, effective = classifyws(line, self.editwin.tabwidth)
++                effective = effective + self.editwin.indentwidth
++                lines[pos] = self.__make_blanks(effective) + line[raw:]
++        self.__set_region(head, tail, chars, lines)
++        return "break"
++
++    def _dedent_region_event(self, event):
++        head, tail, chars, lines = self.__get_region()
++        for pos in range(len(lines)):
++            line = lines[pos]
++            if line:
++                raw, effective = classifyws(line, self.editwin.tabwidth)
++                effective = max(effective - self.editwin.indentwidth, 0)
++                lines[pos] = self.__make_blanks(effective) + line[raw:]
++        self.__set_region(head, tail, chars, lines)
++        return "break"
++
++    def _comment_region_event(self, event):
++        head, tail, chars, lines = self.__get_region()
++        for pos in range(len(lines) - 1):
++            line = lines[pos]
++            lines[pos] = '##' + line
++        self.__set_region(head, tail, chars, lines)
++
++    def _uncomment_region_event(self, event):
++        head, tail, chars, lines = self.__get_region()
++        for pos in range(len(lines)):
++            line = lines[pos]
++            if not line:
++                continue
++            if line[:2] == '##':
++                line = line[2:]
++            elif line[:1] == '#':
++                line = line[1:]
++            lines[pos] = line
++        self.__set_region(head, tail, chars, lines)
++
++    def _tabify_region_event(self, event):
++        head, tail, chars, lines = self.__get_region()
++        tabwidth = self.__asktabwidth()
++        for pos in range(len(lines)):
++            line = lines[pos]
++            if line:
++                raw, effective = classifyws(line, tabwidth)
++                ntabs, nspaces = divmod(effective, tabwidth)
++                lines[pos] = '\t' * ntabs + ' ' * nspaces + line[raw:]
++        self.__set_region(head, tail, chars, lines)
++
++    def _untabify_region_event(self, event):
++        head, tail, chars, lines = self.__get_region()
++        tabwidth = self.__asktabwidth()
++        for pos in range(len(lines)):
++            lines[pos] = lines[pos].expandtabs(tabwidth)
++        self.__set_region(head, tail, chars, lines)
++
++    def _toggle_tabs_event(self, event):
++        if self.editwin.askyesno(
++              "Toggle tabs",
++              "Turn tabs " + ("on", "off")[self.editwin.usetabs] +
++              "?\nIndent width " +
++              ("will be", "remains at")[self.editwin.usetabs] + " 8." +
++              "\n Note: a tab is always 8 columns",
++              parent=self.text):
++            self.editwin.usetabs = not self.editwin.usetabs
++            # Try to prevent inconsistent indentation.
++            # User must change indent width manually after using tabs.
++            self.editwin.indentwidth = 8
++        return "break"
++
++    def _change_indentwidth_event(self, event):
++        new = self.editwin.askinteger(
++                  "Indent width",
++                  "New indent width (2-16)\n(Always use 8 when using tabs)",
++                  parent=self.text,
++                  initialvalue=self.editwin.indentwidth,
++                  minvalue=2,
++                  maxvalue=16)
++        if new and new != self.editwin.indentwidth and not self.editwin.usetabs:
++            self.editwin.indentwidth = new
++        return "break"
++
++    def _right_menu(self, event):
++        self.text.tag_remove("sel", "1.0", "end")
++        self.text.mark_set("insert", "@%d,%d" % (event.x, event.y))
++
++        if not self.editwin.rmenu:
++            self.__make_rmenu()
++
++        rmenu = self.editwin.rmenu
++        self.event = event
++        iswin = sys.platform[:3] == 'win'
++        if iswin:
++            self.text.config(cursor="arrow")
++        rmenu.tk_popup(event.x_root, event.y_root)
++        if iswin:
++            self.text.config(cursor="ibeam")
++
++    def __make_rmenu(self):
++        rmenu = Menu(self.text, tearoff=False)
++
++        for label, eventname in self.editwin.rmenu_specs:
++            def command(text=self.text, eventname=eventname):
++                text.event_generate(eventname)
++            rmenu.add_command(label=label, command=command)
++
++        self.editwin.rmenu = rmenu
++
++    def __rmcolorizer(self):
++        if not self.color:
++            return
++        self.color.removecolors()
++        self.per.removefilter(self.color)
++        self.color = None
++
++    def __addcolorizer(self):
++        if self.color:
++            return
++        if self.editwin.ispythonsource(self.io.filename):
++            self.color = self.editwin.ColorDelegator()
++
++        # can add more colorizers here...
++        if self.color:
++            self.per.removefilter(self.undo)
++            self.per.insertfilter(self.color)
++            self.per.insertfilter(self.undo)
++
++    def __asktabwidth(self):
++        return self.editwin.askinteger(
++            "Tab width",
++            "Columns per tab? (2-16)",
++            parent=self.text,
++            initialvalue=self.editwin.indentwidth,
++            minvalue=2,
++            maxvalue=16) or self.editwin.tabwidth
++
++    # Make string that displays as n leading blanks.
++    def __make_blanks(self, n):
++        if self.editwin.usetabs:
++            ntabs, nspaces = divmod(n, self.editwin.tabwidth)
++            return '\t' * ntabs + ' ' * nspaces
++        else:
++            return ' ' * n
++
++    def __get_region(self):
++        text = self.text
++        first, last = self.get_selection_indices()
++        if first and last:
++            head = text.index(first + " linestart")
++            tail = text.index(last + "-1c lineend +1c")
++        else:
++            head = text.index("insert linestart")
++            tail = text.index("insert lineend +1c")
++        chars = text.get(head, tail)
++        lines = chars.split("\n")
++        return head, tail, chars, lines
++
++    def __set_region(self, head, tail, chars, lines):
++        text = self.text
++        newchars = "\n".join(lines)
++        if newchars == chars:
++            text.bell()
++            return
++        text.tag_remove("sel", "1.0", "end")
++        text.mark_set("insert", head)
++        text.undo_block_start()
++        text.delete(head, tail)
++        text.insert(head, newchars)
++        text.undo_block_stop()
++        text.tag_add("sel", head, "insert")
++
++    # Delete from beginning of line to insert point, then reinsert
++    # column logical (meaning use tabs if appropriate) spaces.
++    def __reindent_to(self, column):
++        text = self.text
++        text.undo_block_start()
++        if text.compare("insert linestart", "!=", "insert"):
++            text.delete("insert linestart", "insert")
++        if column:
++            text.insert("insert", self.__make_blanks(column))
++        text.undo_block_stop()
++
++    # Tk implementations of "virtual text methods" -- each platform
++    # reusing IDLE's support code needs to define these for its GUI's
++    # flavor of widget.
++
++    # Is character at text_index in a Python string?  Return 0 for
++    # "guaranteed no", true for anything else.  This info is expensive
++    # to compute ab initio, but is probably already known by the
++    # platform's colorizer.
++    def __is_char_in_string(self, text_index):
++        if self.color:
++            # Return true iff colorizer hasn't (re)gotten this far
++            # yet, or the character is tagged as being in a string
++            return self.text.tag_prevrange("TODO", text_index) or \
++                   "STRING" in self.text.tag_names(text_index)
++        else:
++            # The colorizer is missing: assume the worst
++            return 1
+Index: OutputWindow.py
+===================================================================
+--- OutputWindow.py	(revision 63995)
++++ OutputWindow.py	(revision 65541)
+@@ -1,8 +1,9 @@
+-from Tkinter import *
+-from EditorWindow import EditorWindow
+ import re
+ import tkMessageBox
++
++import utils
+ import IOBinding
++from EditorWindow import EditorWindow
+ 
+ class OutputWindow(EditorWindow):
+ 
+@@ -14,8 +15,15 @@
+ 
+     def __init__(self, *args):
+         EditorWindow.__init__(self, *args)
+-        self.text.bind("<<goto-file-line>>", self.goto_file_line)
++        self.top.bind("<<tab-created>>", self.__configure_new_tab)
++        # configure the tab created in EditorWindow.__init__
++        self.__configure_new_tab() 
+ 
++    def __configure_new_tab(self, event=None):
++        page = self.text_notebook.last_page().editpage
++        page.text.bind("<<goto-file-line>>", utils.callback(self.goto_file_line,
++            page.text))
++
+     # Customize EditorWindow
+ 
+     def ispythonsource(self, filename):
+@@ -34,7 +42,8 @@
+ 
+     # Act as output file
+ 
+-    def write(self, s, tags=(), mark="insert"):
++    def write(self, s, tags=(), mark="insert", text=None):
++        assert text is not None
+         # Tk assumes that byte strings are Latin-1;
+         # we assume that they are in the locale's encoding
+         if isinstance(s, str):
+@@ -43,12 +52,12 @@
+             except UnicodeError:
+                 # some other encoding; let Tcl deal with it
+                 pass
+-        self.text.insert(mark, s, tags)
+-        self.text.see(mark)
+-        self.text.update()
++        text.insert(mark, s, tags)
++        text.see(mark)
++        text.update()
+ 
+     def writelines(self, l):
+-        map(self.write, l)
++        map(self.write, l, text=self.current_page.text)
+ 
+     def flush(self):
+         pass
+@@ -67,28 +76,26 @@
+ 
+     file_line_progs = None
+ 
+-    def goto_file_line(self, event=None):
++    def goto_file_line(self, event, text):
+         if self.file_line_progs is None:
+             l = []
+             for pat in self.file_line_pats:
+                 l.append(re.compile(pat, re.IGNORECASE))
+             self.file_line_progs = l
+-        # x, y = self.event.x, self.event.y
+-        # self.text.mark_set("insert", "@%d,%d" % (x, y))
+-        line = self.text.get("insert linestart", "insert lineend")
++        
++        line = text.get("insert linestart", "insert lineend")
+         result = self._file_line_helper(line)
+         if not result:
+             # Try the previous line.  This is handy e.g. in tracebacks,
+             # where you tend to right-click on the displayed source line
+-            line = self.text.get("insert -1line linestart",
+-                                 "insert -1line lineend")
++            line = text.get("insert -1line linestart", "insert -1line ineend")
+             result = self._file_line_helper(line)
+             if not result:
+                 tkMessageBox.showerror(
+                     "No special line",
+                     "The line you point at doesn't look like "
+                     "a valid file name followed by a line number.",
+-                    master=self.text)
++                    master=text)
+                 return
+         filename, lineno = result
+         edit = self.flist.open(filename)
+@@ -111,47 +118,3 @@
+             return filename, int(lineno)
+         except TypeError:
+             return None
+-
+-# These classes are currently not used but might come in handy
+-
+-class OnDemandOutputWindow:
+-
+-    tagdefs = {
+-        # XXX Should use IdlePrefs.ColorPrefs
+-        "stdout":  {"foreground": "blue"},
+-        "stderr":  {"foreground": "#007700"},
+-    }
+-
+-    def __init__(self, flist):
+-        self.flist = flist
+-        self.owin = None
+-
+-    def write(self, s, tags, mark):
+-        if not self.owin:
+-            self.setup()
+-        self.owin.write(s, tags, mark)
+-
+-    def setup(self):
+-        self.owin = owin = OutputWindow(self.flist)
+-        text = owin.text
+-        for tag, cnf in self.tagdefs.items():
+-            if cnf:
+-                text.tag_configure(tag, **cnf)
+-        text.tag_raise('sel')
+-        self.write = self.owin.write
+-
+-#class PseudoFile:
+-#
+-#      def __init__(self, owin, tags, mark="end"):
+-#          self.owin = owin
+-#          self.tags = tags
+-#          self.mark = mark
+-
+-#      def write(self, s):
+-#          self.owin.write(s, self.tags, self.mark)
+-
+-#      def writelines(self, l):
+-#          map(self.write, l)
+-
+-#      def flush(self):
+-#          pass
+Index: aboutDialog.py
+===================================================================
+--- aboutDialog.py	(revision 63995)
++++ aboutDialog.py	(revision 65541)
+@@ -1,13 +1,18 @@
+-"""About Dialog for IDLE
++"""About Dialog for IDLE"""
++import os
++import sys
++from Tkinter import Toplevel, Frame, Button, Label, TkVersion
++from Tkconstants import LEFT, NSEW, SUNKEN, EW, W, BOTH, TOP, BOTTOM
+ 
+-"""
+-
+-from Tkinter import *
+-import os
+-import os.path
++import idlever
+ import textView
+-import idlever
++from stylist import PoorManStyle
++from configHandler import idleConf
+ 
++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int')
++if TTK:
++    from ttk import Frame, Button, Label, Style
++
+ class AboutDialog(Toplevel):
+     """Modal about dialog for idle
+ 
+@@ -15,12 +20,15 @@
+     def __init__(self,parent,title):
+         Toplevel.__init__(self, parent)
+         self.configure(borderwidth=5)
++
+         self.geometry("+%d+%d" % (parent.winfo_rootx()+30,
+                                   parent.winfo_rooty()+30))
+         self.bg = "#707070"
+         self.fg = "#ffffff"
++ 
++        self.SetupStyles()
+         self.CreateWidgets()
+-        self.resizable(height=FALSE, width=FALSE)
++        self.resizable(height=False, width=False)
+         self.title(title)
+         self.transient(parent)
+         self.grab_set()
+@@ -31,40 +39,44 @@
+         self.bind('<Escape>',self.Ok) #dismiss dialog
+         self.wait_window()
+ 
++    def SetupStyles(self):
++        if TTK:
++            style = Style(self.master)
++            style.configure('Color.TLabel', foreground=self.fg,
++                            background=self.bg)
++            style.configure('Color.TFrame', background=self.bg)
++            self.ttkstyle = style
++            self.style = lambda w, style: w.configure(style=style)
++        else:
++            self.style = PoorManStyle(self,
++                styles={'Color.TLabel': {'fg': self.fg, 'bg': self.bg},
++                        'Color.TFrame': {'bg': self.bg}}).style_it
++
+     def CreateWidgets(self):
+         frameMain = Frame(self, borderwidth=2, relief=SUNKEN)
+         frameButtons = Frame(self)
+-        frameButtons.pack(side=BOTTOM, fill=X)
+-        frameMain.pack(side=TOP, expand=TRUE, fill=BOTH)
+-        self.buttonOk = Button(frameButtons, text='Close',
+-                               command=self.Ok)
+-        self.buttonOk.pack(padx=5, pady=5)
+-        #self.picture = Image('photo', data=self.pictureData)
+-        frameBg = Frame(frameMain, bg=self.bg)
+-        frameBg.pack(expand=TRUE, fill=BOTH)
+-        labelTitle = Label(frameBg, text='IDLE', fg=self.fg, bg=self.bg,
+-                           font=('courier', 24, 'bold'))
++        frameButtons.pack(side=BOTTOM, pady=3)
++        frameMain.pack(side=TOP, expand=True, fill=BOTH)
++        self.buttonOk = Button(frameButtons, text='Close', command=self.Ok)
++        self.buttonOk.pack()
++        frameBg = Frame(frameMain)
++        frameBg.pack(expand=True, fill=BOTH)
++        labelTitle = Label(frameBg, text='IDLE', font=('courier', 24, 'bold'))
+         labelTitle.grid(row=0, column=0, sticky=W, padx=10, pady=10)
+-        #labelPicture = Label(frameBg, text='[picture]')
+-        #image=self.picture, bg=self.bg)
+-        #labelPicture.grid(row=1, column=1, sticky=W, rowspan=2,
+-        #                  padx=0, pady=3)
+         byline = "Python's Integrated DeveLopment Environment" + 5*'\n'
+-        labelDesc = Label(frameBg, text=byline, justify=LEFT,
+-                          fg=self.fg, bg=self.bg)
++        labelDesc = Label(frameBg, text=byline, justify=LEFT)
+         labelDesc.grid(row=2, column=0, sticky=W, columnspan=3, padx=10, pady=5)
+         labelEmail = Label(frameBg, text='email:  idle-dev at python.org',
+-                           justify=LEFT, fg=self.fg, bg=self.bg)
++                           justify=LEFT)
+         labelEmail.grid(row=6, column=0, columnspan=2,
+                         sticky=W, padx=10, pady=0)
+         labelWWW = Label(frameBg, text='www:  http://www.python.org/idle/',
+-                         justify=LEFT, fg=self.fg, bg=self.bg)
++                         justify=LEFT)
+         labelWWW.grid(row=7, column=0, columnspan=2, sticky=W, padx=10, pady=0)
+-        Frame(frameBg, borderwidth=1, relief=SUNKEN,
+-              height=2, bg=self.bg).grid(row=8, column=0, sticky=EW,
+-                                         columnspan=3, padx=5, pady=5)
++        fbg = Frame(frameBg, borderwidth=1, relief=SUNKEN,  height=2)
++        fbg.grid(row=8, column=0, sticky=EW, columnspan=3, padx=5, pady=5)
+         labelPythonVer = Label(frameBg, text='Python version:  ' + \
+-                               sys.version.split()[0], fg=self.fg, bg=self.bg)
++                               sys.version.split()[0])
+         labelPythonVer.grid(row=9, column=0, sticky=W, padx=10, pady=0)
+         # handle weird tk version num in windoze python >= 1.6 (?!?)
+         tkVer = repr(TkVersion).split('.')
+@@ -72,44 +84,50 @@
+         if tkVer[len(tkVer)-1] == '':
+             tkVer[len(tkVer)-1] = '0'
+         tkVer = '.'.join(tkVer)
+-        labelTkVer = Label(frameBg, text='Tk version:  '+
+-                           tkVer, fg=self.fg, bg=self.bg)
++        labelTkVer = Label(frameBg, text='Tk version:  '+ tkVer)
+         labelTkVer.grid(row=9, column=1, sticky=W, padx=2, pady=0)
+-        py_button_f = Frame(frameBg, bg=self.bg)
++        py_button_f = Frame(frameBg)
+         py_button_f.grid(row=10, column=0, columnspan=2, sticky=NSEW)
+         buttonLicense = Button(py_button_f, text='License', width=8,
+-                               highlightbackground=self.bg,
+                                command=self.ShowLicense)
+         buttonLicense.pack(side=LEFT, padx=10, pady=10)
+         buttonCopyright = Button(py_button_f, text='Copyright', width=8,
+-                                 highlightbackground=self.bg,
+                                  command=self.ShowCopyright)
+         buttonCopyright.pack(side=LEFT, padx=10, pady=10)
+         buttonCredits = Button(py_button_f, text='Credits', width=8,
+-                               highlightbackground=self.bg,
+                                command=self.ShowPythonCredits)
+         buttonCredits.pack(side=LEFT, padx=10, pady=10)
+-        Frame(frameBg, borderwidth=1, relief=SUNKEN,
+-              height=2, bg=self.bg).grid(row=11, column=0, sticky=EW,
+-                                         columnspan=3, padx=5, pady=5)
+-        idle_v = Label(frameBg, text='IDLE version:   ' + idlever.IDLE_VERSION,
+-                       fg=self.fg, bg=self.bg)
++        fbg2 = Frame(frameBg, borderwidth=1, relief=SUNKEN, height=2)
++        fbg2.grid(row=11, column=0, sticky=EW, columnspan=3, padx=5, pady=5)
++        idle_v = Label(frameBg, text='IDLE version:   ' + idlever.IDLE_VERSION)
+         idle_v.grid(row=12, column=0, sticky=W, padx=10, pady=0)
+-        idle_button_f = Frame(frameBg, bg=self.bg)
++        idle_button_f = Frame(frameBg)
+         idle_button_f.grid(row=13, column=0, columnspan=3, sticky=NSEW)
+         idle_about_b = Button(idle_button_f, text='README', width=8,
+-                                highlightbackground=self.bg,
+                                 command=self.ShowIDLEAbout)
+         idle_about_b.pack(side=LEFT, padx=10, pady=10)
+         idle_news_b = Button(idle_button_f, text='NEWS', width=8,
+-                                highlightbackground=self.bg,
+                                 command=self.ShowIDLENEWS)
+         idle_news_b.pack(side=LEFT, padx=10, pady=10)
+         idle_credits_b = Button(idle_button_f, text='Credits', width=8,
+-                                highlightbackground=self.bg,
+                                 command=self.ShowIDLECredits)
+         idle_credits_b.pack(side=LEFT, padx=10, pady=10)
+ 
++        s = self.style
++        s(frameButtons, 'RootColor.TFrame')
++        s(frameBg, 'Color.TFrame')
++        s(labelTitle, 'Color.TLabel')
++        s(labelDesc, 'Color.TLabel')
++        s(labelEmail, 'Color.TLabel')
++        s(labelWWW, 'Color.TLabel')
++        s(fbg, 'Color.TFrame')
++        s(labelPythonVer, 'Color.TLabel')
++        s(labelTkVer, 'Color.TLabel')
++        s(py_button_f, 'Color.TFrame')
++        s(fbg2, 'Color.TFrame')
++        s(idle_v, 'Color.TLabel')
++        s(idle_button_f, 'Color.TFrame')
++
+     def ShowLicense(self):
+         self.display_printer_text('About - License', license)
+ 
+@@ -142,9 +160,9 @@
+ 
+ if __name__ == '__main__':
+     # test the dialog
++    from Tkinter import Tk
+     root = Tk()
+     def run():
+-        import aboutDialog
+-        aboutDialog.AboutDialog(root, 'About')
++        AboutDialog(root, 'About')
+     Button(root, text='Dialog', command=run).pack()
+     root.mainloop()
+Index: config-main.def
+===================================================================
+--- config-main.def	(revision 63995)
++++ config-main.def	(revision 65541)
+@@ -49,8 +49,14 @@
+ print-command-posix=lpr %s
+ print-command-win=start /min notepad /p %s
+ delete-exitfunc= 1
++use-ttk = 0
+ 
+ [EditorWindow]
++width = 80
++height = 40
++file-in-tab = 1
++
++[EditorPage]
+ width= 80
+ height= 40
+ font= courier
+@@ -68,6 +74,7 @@
+ [Theme]
+ default= 1
+ name= IDLE Classic
++displaytheme = default
+ 
+ [Keys]
+ default= 1
+Index: PathBrowser.py
+===================================================================
+--- PathBrowser.py	(revision 63995)
++++ PathBrowser.py	(revision 65541)
+@@ -15,25 +15,29 @@
+         self.top.wm_iconname("Path Browser")
+ 
+     def rootnode(self):
+-        return PathBrowserTreeItem()
++        return PathBrowserTreeItem(self.flist)
+ 
+ class PathBrowserTreeItem(TreeItem):
+ 
++    def __init__(self, flist):
++        self.flist = flist
++
+     def GetText(self):
+         return "sys.path"
+ 
+     def GetSubList(self):
+         sublist = []
+         for dir in sys.path:
+-            item = DirBrowserTreeItem(dir)
++            item = DirBrowserTreeItem(dir, flist=self.flist)
+             sublist.append(item)
+         return sublist
+ 
+ class DirBrowserTreeItem(TreeItem):
+ 
+-    def __init__(self, dir, packages=[]):
++    def __init__(self, dir, packages=[], flist=None):
+         self.dir = dir
+         self.packages = packages
++        self.flist = flist
+ 
+     def GetText(self):
+         if not self.packages:
+@@ -55,10 +59,11 @@
+         packages.sort()
+         sublist = []
+         for nn, name, file in packages:
+-            item = DirBrowserTreeItem(file, self.packages + [name])
++            item = DirBrowserTreeItem(file, self.packages + [name], self.flist)
+             sublist.append(item)
+         for nn, name in self.listmodules(names):
+-            item = ModuleBrowserTreeItem(os.path.join(self.dir, name))
++            item = ModuleBrowserTreeItem(os.path.join(self.dir, name), 
++                self.flist)
+             sublist.append(item)
+         return sublist
+ 
+Index: IOBinding.py
+===================================================================
+--- IOBinding.py	(revision 63995)
++++ IOBinding.py	(revision 65541)
+@@ -6,17 +6,20 @@
+ #     which will only understand the local convention.
+ 
+ import os
++import re
++import sys
+ import types
+-import sys
+ import codecs
+ import tempfile
+ import tkFileDialog
+ import tkMessageBox
+-import re
+-from Tkinter import *
++from Tkinter import Toplevel, Entry, Frame, Button, Label
++from Tkconstants import W, X, TOP, LEFT, BOTH
+ from SimpleDialog import SimpleDialog
+ 
+ from configHandler import idleConf
++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'):
++    from ttk import Entry, Frame, Button, Label
+ 
+ try:
+     from codecs import BOM_UTF8
+@@ -93,11 +96,11 @@
+         # For some reason, the text is not selectable anymore if the
+         # widget is disabled.
+         # l2['state'] = DISABLED
+-        l2.pack(side=TOP, anchor = W, fill=X)
++        l2.pack(side=TOP, anchor=W, fill=X)
+         l3 = Label(top, text="to your file\n"
+                    "Choose OK to save this file as %s\n"
+                    "Edit your general options to silence this warning" % enc)
+-        l3.pack(side=TOP, anchor = W)
++        l3.pack(side=TOP, anchor=W)
+ 
+         buttons = Frame(top)
+         buttons.pack(side=TOP, fill=X)
+@@ -142,10 +145,14 @@
+ 
+ 
+ class IOBinding:
++    filename_change_hook = None
++    filename = None
++    dirname = None
+ 
+-    def __init__(self, editwin):
+-        self.editwin = editwin
+-        self.text = editwin.text
++    def __init__(self, editpage):
++        self.editpage = editpage
++        self.editwin = editpage.editwin
++        self.text = editpage.text
+         self.__id_open = self.text.bind("<<open-window-from-file>>", self.open)
+         self.__id_save = self.text.bind("<<save-window>>", self.save)
+         self.__id_saveas = self.text.bind("<<save-window-as-file>>",
+@@ -163,27 +170,23 @@
+         self.text.unbind("<<save-copy-of-window-as-file>>", self.__id_savecopy)
+         self.text.unbind("<<print-window>>", self.__id_print)
+         # Break cycles
++        self.text = None
++        self.editpage = None
+         self.editwin = None
+-        self.text = None
+         self.filename_change_hook = None
+ 
+     def get_saved(self):
+-        return self.editwin.get_saved()
++        return self.editpage.get_saved()
+ 
+     def set_saved(self, flag):
+-        self.editwin.set_saved(flag)
++        self.editpage.set_saved(flag)
+ 
+     def reset_undo(self):
+-        self.editwin.reset_undo()
++        self.editpage.reset_undo()
+ 
+-    filename_change_hook = None
+-
+     def set_filename_change_hook(self, hook):
+         self.filename_change_hook = hook
+ 
+-    filename = None
+-    dirname = None
+-
+     def set_filename(self, filename):
+         if filename and os.path.isdir(filename):
+             self.filename = None
+@@ -200,21 +203,33 @@
+             if not editFile:
+                 filename = self.askopenfile()
+             else:
+-                filename=editFile
++                filename = editFile
+             if filename:
+                 # If the current window has no filename and hasn't been
+-                # modified, we replace its contents (no loss).  Otherwise
+-                # we open a new window.  But we won't replace the
+-                # shell window (which has an interp(reter) attribute), which
+-                # gets set to "not modified" at every new prompt.
++                # modified, we replace its contents (no loss). Otherwise
++                # we open a new window, or maybe open in a tab.
++                # But we won't replace the shell window (which has an
++                # interp(reter) attribute), which gets set to "not modified"
++                # at every new prompt.
+                 try:
+                     interp = self.editwin.interp
+                 except AttributeError:
+                     interp = None
++
+                 if not self.filename and self.get_saved() and not interp:
+                     self.editwin.flist.open(filename, self.loadfile)
+                 else:
+-                    self.editwin.flist.open(filename)
++                    if idleConf.GetOption('main', 'EditorWindow',
++                        'file-in-tab', default=1, type='bool'):
++                        if interp:
++                            # this is a PyShell, force file to be opened in a
++                            # new window
++                            action = None
++                        else:
++                            action = self.editwin.new_tab
++                    else:
++                        action = None
++                    self.editwin.flist.open(filename, action)
+             else:
+                 self.text.focus_set()
+             return "break"
+@@ -341,8 +356,8 @@
+             if self.writefile(self.filename):
+                 self.set_saved(1)
+                 try:
+-                    self.editwin.store_file_breaks()
+-                except AttributeError:  # may be a PyShell
++                    self.editwin.store_file_breaks(self.editpage)
++                except AttributeError:  # may not be a PyShell
+                     pass
+         self.text.focus_set()
+         return "break"
+@@ -354,7 +369,7 @@
+                 self.set_filename(filename)
+                 self.set_saved(1)
+                 try:
+-                    self.editwin.store_file_breaks()
++                    self.editwin.store_file_breaks(self.editpage)
+                 except AttributeError:
+                     pass
+         self.text.focus_set()
+@@ -381,8 +396,7 @@
+             f.close()
+             return True
+         except IOError, msg:
+-            tkMessageBox.showerror("I/O Error", str(msg),
+-                                   master=self.text)
++            tkMessageBox.showerror("I/O Error", str(msg), master=self.text)
+             return False
+ 
+     def encode(self, chars):
+@@ -429,8 +443,7 @@
+                 return BOM_UTF8 + chars.encode("utf-8")
+         # Nothing was declared, and we had not determined an encoding
+         # on loading. Recommend an encoding line.
+-        config_encoding = idleConf.GetOption("main","EditorWindow",
+-                                             "encoding")
++        config_encoding = idleConf.GetOption("main", "EditorPage", "encoding")
+         if config_encoding == 'utf-8':
+             # User has requested that we save files as UTF-8
+             return BOM_UTF8 + chars.encode("utf-8")
+@@ -563,6 +576,7 @@
+         self.editwin.update_recent_files_list(filename)
+ 
+ def test():
++    from Tkinter import Tk, Text
+     root = Tk()
+     class MyEditWin:
+         def __init__(self, text):
+Index: WindowList.py
+===================================================================
+--- WindowList.py	(revision 63995)
++++ WindowList.py	(revision 65541)
+@@ -1,4 +1,4 @@
+-from Tkinter import *
++from Tkinter import Toplevel, TclError
+ 
+ class WindowList:
+ 
+@@ -45,8 +45,8 @@
+             try:
+                 callback()
+             except:
+-                print "warning: callback failed in WindowList", \
+-                      sys.exc_type, ":", sys.exc_value
++                print ("warning: callback failed in WindowList",
++                    sys.exc_type, ":", sys.exc_value)
+ 
+ registry = WindowList()
+ 
+Index: ScrolledList.py
+===================================================================
+--- ScrolledList.py	(revision 63995)
++++ ScrolledList.py	(revision 65541)
+@@ -1,5 +1,9 @@
+-from Tkinter import *
++from Tkinter import Frame, Menu, Listbox, Scrollbar
++from idlelib.configHandler import idleConf
+ 
++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'):
++    from ttk import Frame, Scrollbar
++
+ class ScrolledList:
+ 
+     default = "(None)"
+@@ -120,6 +124,7 @@
+ 
+ 
+ def test():
++    from Tkinter import Tk
+     root = Tk()
+     root.protocol("WM_DELETE_WINDOW", root.destroy)
+     class MyScrolledList(ScrolledList):
+Index: ClassBrowser.py
+===================================================================
+--- ClassBrowser.py	(revision 63995)
++++ ClassBrowser.py	(revision 65541)
+@@ -14,7 +14,6 @@
+ import sys
+ import pyclbr
+ 
+-import PyShell
+ from WindowList import ListedToplevel
+ from TreeWidget import TreeNode, TreeItem, ScrolledCanvas
+ from configHandler import idleConf
+@@ -26,6 +25,7 @@
+         # XXX the code here is bogus!
+         self.name = name
+         self.file = os.path.join(path[0], self.name + ".py")
++        self.flist = None
+         self.init(flist)
+ 
+     def close(self, event=None):
+@@ -57,12 +57,13 @@
+         self.top.wm_iconname("Class Browser")
+ 
+     def rootnode(self):
+-        return ModuleBrowserTreeItem(self.file)
++        return ModuleBrowserTreeItem(self.file, self.flist)
+ 
+ class ModuleBrowserTreeItem(TreeItem):
+ 
+-    def __init__(self, file):
++    def __init__(self, file, flist):
+         self.file = file
++        self.flist = flist
+ 
+     def GetText(self):
+         return os.path.basename(self.file)
+@@ -73,7 +74,8 @@
+     def GetSubList(self):
+         sublist = []
+         for name in self.listclasses():
+-            item = ClassBrowserTreeItem(name, self.classes, self.file)
++            item = ClassBrowserTreeItem(name, self.classes, self.file, 
++                self.flist)
+             sublist.append(item)
+         return sublist
+ 
+@@ -82,7 +84,7 @@
+             return
+         if not os.path.exists(self.file):
+             return
+-        PyShell.flist.open(self.file)
++        self.flist.open(self.file)
+ 
+     def IsExpandable(self):
+         return os.path.normcase(self.file[-3:]) == ".py"
+@@ -122,10 +124,11 @@
+ 
+ class ClassBrowserTreeItem(TreeItem):
+ 
+-    def __init__(self, name, classes, file):
++    def __init__(self, name, classes, file, flist):
+         self.name = name
+         self.classes = classes
+         self.file = file
++        self.flist = flist
+         try:
+             self.cl = self.classes[self.name]
+         except (IndexError, KeyError):
+@@ -163,7 +166,7 @@
+     def OnDoubleClick(self):
+         if not os.path.exists(self.file):
+             return
+-        edit = PyShell.flist.open(self.file)
++        edit = self.flist.open(self.file)
+         if hasattr(self.cl, 'lineno'):
+             lineno = self.cl.lineno
+             edit.gotoline(lineno)
+@@ -199,10 +202,11 @@
+     def OnDoubleClick(self):
+         if not os.path.exists(self.file):
+             return
+-        edit = PyShell.flist.open(self.file)
++        edit = self.flist.open(self.file)
+         edit.gotoline(self.cl.methods[self.name])
+ 
+ def main():
++    import PyShell
+     try:
+         file = __file__
+     except NameError:
+Index: FileList.py
+===================================================================
+--- FileList.py	(revision 63995)
++++ FileList.py	(revision 65541)
+@@ -1,7 +1,15 @@
+ import os
+-from Tkinter import *
+ import tkMessageBox
+ 
++def _canonize(filename):
++    if not os.path.isabs(filename):
++        try:
++            pwd = os.getcwd()
++        except os.error:
++            pass
++        else:
++            filename = os.path.join(pwd, filename)
++    return os.path.normpath(filename)
+ 
+ class FileList:
+ 
+@@ -16,7 +24,7 @@
+ 
+     def open(self, filename, action=None):
+         assert filename
+-        filename = self.canonize(filename)
++        filename = _canonize(filename)
+         if os.path.isdir(filename):
+             # This can happen when bad filename is passed on command line:
+             tkMessageBox.showerror(
+@@ -29,9 +37,10 @@
+             edit = self.dict[key]
+             edit.top.wakeup()
+             return edit
++
+         if action:
+             # Don't create window, perform 'action', e.g. open in same window
+-            return action(filename)
++            return action(filename=filename)
+         else:
+             return self.EditorWindow(self, filename, key)
+ 
+@@ -62,20 +71,20 @@
+         if not self.inversedict:
+             self.root.quit()
+ 
+-    def filename_changed_edit(self, edit):
+-        edit.saved_change_hook()
++    def filename_changed_edit(self, page, editwin):
++        page.saved_change_hook(page.tab_initialized)
+         try:
+-            key = self.inversedict[edit]
++            key = self.inversedict[editwin]
+         except KeyError:
+             print "Don't know this EditorWindow object.  (rename)"
+             return
+-        filename = edit.io.filename
++        filename = page.io.filename
+         if not filename:
+             if key:
+                 del self.dict[key]
+-            self.inversedict[edit] = None
++            self.inversedict[editwin] = None
+             return
+-        filename = self.canonize(filename)
++        filename = _canonize(filename)
+         newkey = os.path.normcase(filename)
+         if newkey == key:
+             return
+@@ -86,28 +95,19 @@
+                 "Name Conflict",
+                 "You now have multiple edit windows open for %r" % (filename,),
+                 master=self.root)
+-        self.dict[newkey] = edit
+-        self.inversedict[edit] = newkey
++        self.dict[newkey] = editwin
++        self.inversedict[editwin] = newkey
+         if key:
+             try:
+                 del self.dict[key]
+             except KeyError:
+                 pass
+ 
+-    def canonize(self, filename):
+-        if not os.path.isabs(filename):
+-            try:
+-                pwd = os.getcwd()
+-            except os.error:
+-                pass
+-            else:
+-                filename = os.path.join(pwd, filename)
+-        return os.path.normpath(filename)
+ 
+-
+ def _test():
++    import sys
++    from Tkinter import Tk
+     from EditorWindow import fixwordbreaks
+-    import sys
+     root = Tk()
+     fixwordbreaks(root)
+     root.withdraw()
+Index: CallTips.py
+===================================================================
+--- CallTips.py	(revision 63995)
++++ CallTips.py	(revision 65541)
+@@ -22,12 +22,12 @@
+         ])
+     ]
+ 
+-    def __init__(self, editwin=None):
+-        if editwin is None:  # subprocess and test
+-            self.editwin = None
++    def __init__(self, editpage=None):
++        if editpage is None:  # subprocess and test
++            self.editpage = None
+             return
+-        self.editwin = editwin
+-        self.text = editwin.text
++        self.editpage = editpage
++        self.text = editpage.text
+         self.calltip = None
+         self._make_calltip_window = self._make_tk_calltip_window
+ 
+@@ -66,7 +66,7 @@
+     def open_calltip(self, evalfuncs):
+         self._remove_calltip_window()
+ 
+-        hp = HyperParser(self.editwin, "insert")
++        hp = HyperParser(self.editpage, "insert")
+         sur_paren = hp.get_surrounding_brackets('(')
+         if not sur_paren:
+             return
+@@ -95,7 +95,7 @@
+ 
+         """
+         try:
+-            rpcclt = self.editwin.flist.pyshell.interp.rpcclt
++            rpcclt = self.editpage.editwin.flist.pyshell.interp.rpcclt
+         except:
+             rpcclt = None
+         if rpcclt:
+Index: CodeContext.py
+===================================================================
+--- CodeContext.py	(revision 63995)
++++ CodeContext.py	(revision 65541)
+@@ -23,7 +23,7 @@
+ getspacesfirstword =\
+                    lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups()
+ 
+-class CodeContext:
++class CodeContext(object):
+     menudefs = [('options', [('!Code Conte_xt', '<<toggle-code-context>>')])]
+     context_depth = idleConf.GetOption("extensions", "CodeContext",
+                                        "numlines", type="int", default=3)
+@@ -31,10 +31,10 @@
+                                  "bgcolor", type="str", default="LightGray")
+     fgcolor = idleConf.GetOption("extensions", "CodeContext",
+                                  "fgcolor", type="str", default="Black")
+-    def __init__(self, editwin):
+-        self.editwin = editwin
+-        self.text = editwin.text
+-        self.textfont = self.text["font"]
++
++    def __init__(self, editpage):
++        self.editpage = editpage
++        self.editwin = editpage.editwin
+         self.label = None
+         # self.info is a list of (line number, indent level, line text, block
+         # keyword) tuples providing the block structure associated with
+@@ -43,14 +43,11 @@
+         # starts the toplevel 'block' of the module.
+         self.info = [(0, -1, "", False)]
+         self.topvisible = 1
+-        visible = idleConf.GetOption("extensions", "CodeContext",
+-                                     "visible", type="bool", default=False)
++        visible = idleConf.GetOption("extensions", "CodeContext", "visible",
++            type="bool", default=False)
+         if visible:
+             self.toggle_code_context_event()
+             self.editwin.setvar('<<toggle-code-context>>', True)
+-        # Start two update cycles, one for context lines, one for font changes.
+-        self.text.after(UPDATEINTERVAL, self.timer_event)
+-        self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
+ 
+     def toggle_code_context_event(self, event=None):
+         if not self.label:
+@@ -59,33 +56,27 @@
+             #
+             # All values are passed through int(str(<value>)), since some
+             # values may be pixel objects, which can't simply be added to ints.
+-            widgets = self.editwin.text, self.editwin.text_frame
+-            # Calculate the required vertical padding
+-            padx = 0
+-            for widget in widgets:
+-                padx += int(str( widget.pack_info()['padx'] ))
+-                padx += int(str( widget.cget('padx') ))
+-            # Calculate the required border width
+-            border = 0
+-            for widget in widgets:
+-                border += int(str( widget.cget('border') ))
++
++            # Calculate the required horizontal padding
++            padx = int(str(self.editwin.text_notebook.pack_info()['padx']))
++
+             self.label = Tkinter.Label(self.editwin.top,
+                                        text="\n" * (self.context_depth - 1),
+                                        anchor=W, justify=LEFT,
+                                        font=self.textfont,
+                                        bg=self.bgcolor, fg=self.fgcolor,
+                                        width=1, #don't request more than we get
+-                                       padx=padx, border=border,
++                                       padx=padx,
+                                        relief=SUNKEN)
+-            # Pack the label widget before and above the text_frame widget,
+-            # thus ensuring that it will appear directly above text_frame
++            # Pack the label widget before and above the text_notebook widget,
++            # thus ensuring that it will appear directly above text_notebook
+             self.label.pack(side=TOP, fill=X, expand=False,
+-                            before=self.editwin.text_frame)
++                before=self.editwin.text_notebook)
+         else:
+             self.label.destroy()
+             self.label = None
+         idleConf.SetOption("extensions", "CodeContext", "visible",
+-                           str(self.label is not None))
++            str(self.label is not None))
+         idleConf.SaveUserCfgFiles()
+ 
+     def get_line_info(self, linenum):
+@@ -132,15 +123,12 @@
+         return lines, lastindent
+ 
+     def update_code_context(self):
+-        """Update context information and lines visible in the context pane.
+-
+-        """
++        """Update context information and lines visible in the context pane."""
+         new_topvisible = int(self.text.index("@0,0").split('.')[0])
+         if self.topvisible == new_topvisible:      # haven't scrolled
+             return
+         if self.topvisible < new_topvisible:       # scroll down
+-            lines, lastindent = self.get_context(new_topvisible,
+-                                                 self.topvisible)
++            lines, lastindent = self.get_context(new_topvisible, self.topvisible)
+             # retain only context info applicable to the region
+             # between topvisible and new_topvisible:
+             while self.info[-1][1] >= lastindent:
+@@ -153,8 +141,7 @@
+                 stopindent = self.info[-1][1]
+                 del self.info[-1]
+             lines, lastindent = self.get_context(new_topvisible,
+-                                                 self.info[-1][0]+1,
+-                                                 stopindent)
++                self.info[-1][0] + 1, stopindent)
+         self.info.extend(lines)
+         self.topvisible = new_topvisible
+         # empty lines in context pane:
+@@ -174,3 +161,18 @@
+             self.textfont = newtextfont
+             self.label["font"] = self.textfont
+         self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
++
++    # Private methods
++
++    def _get_editpage(self):
++        return self._editpage
++
++    def _set_editpage(self, page):
++        self._editpage = page
++        self.text = page.text
++        self.textfont = self.text["font"]
++        # Start two update cycles, one for context lines, one for font changes.
++        self.text.after(UPDATEINTERVAL, self.timer_event)
++        self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
++
++    editpage = property(_get_editpage, _set_editpage)
+Index: textView.py
+===================================================================
+--- textView.py	(revision 63995)
++++ textView.py	(revision 65541)
+@@ -1,10 +1,16 @@
+-"""Simple text browser for IDLE
++"""Simple text browser for IDLE"""
+ 
+-"""
+-
+-from Tkinter import *
+ import tkMessageBox
++from Tkinter import Toplevel, Frame, Button, Scrollbar, Text
++from Tkconstants import DISABLED, SUNKEN, VERTICAL, WORD, RIGHT, Y, TOP, \
++                        LEFT, BOTH, BOTTOM
+ 
++from configHandler import idleConf
++
++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int')
++if TTK:
++    from ttk import Frame, Button, Scrollbar
++
+ class TextViewer(Toplevel):
+     """A simple text viewer dialog for IDLE
+ 
+@@ -40,19 +46,22 @@
+         frameText = Frame(self, relief=SUNKEN, height=700)
+         frameButtons = Frame(self)
+         self.buttonOk = Button(frameButtons, text='Close',
+-                               command=self.Ok, takefocus=FALSE)
++                               command=self.Ok, takefocus=False)
+         self.scrollbarView = Scrollbar(frameText, orient=VERTICAL,
+-                                       takefocus=FALSE, highlightthickness=0)
+-        self.textView = Text(frameText, wrap=WORD, highlightthickness=0,
+-                             fg=self.fg, bg=self.bg)
++                                       takefocus=False)
++        self.textView = Text(frameText, wrap=WORD, fg=self.fg, bg=self.bg,
++                             highlightthickness=0)
+         self.scrollbarView.config(command=self.textView.yview)
+         self.textView.config(yscrollcommand=self.scrollbarView.set)
+         self.buttonOk.pack()
+         self.scrollbarView.pack(side=RIGHT,fill=Y)
+-        self.textView.pack(side=LEFT,expand=TRUE,fill=BOTH)
+-        frameButtons.pack(side=BOTTOM,fill=X)
+-        frameText.pack(side=TOP,expand=TRUE,fill=BOTH)
++        self.textView.pack(side=LEFT,expand=True,fill=BOTH)
++        frameButtons.pack(side=BOTTOM)
++        frameText.pack(side=TOP,expand=True,fill=BOTH)
+ 
++        if TTK:
++            frameButtons['style'] = 'RootColor.TFrame'
++
+     def Ok(self, event=None):
+         self.destroy()
+ 
+@@ -68,7 +77,6 @@
+         else:
+             textFile = open(filename, 'r')
+     except IOError:
+-        import tkMessageBox
+         tkMessageBox.showerror(title='File Load Error',
+                                message='Unable to load file %r .' % filename,
+                                parent=parent)
+@@ -77,6 +85,7 @@
+ 
+ 
+ if __name__ == '__main__':
++    from Tkinter import Tk
+     #test the dialog
+     root=Tk()
+     root.title('textView test')
+Index: SearchDialogBase.py
+===================================================================
+--- SearchDialogBase.py	(revision 63995)
++++ SearchDialogBase.py	(revision 65541)
+@@ -1,35 +1,42 @@
+-from Tkinter import *
++from Tkinter import Toplevel, Frame, Label, Entry, Button, Checkbutton, \
++                    Radiobutton
+ 
++from configHandler import idleConf
++
++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'):
++    from ttk import Frame, Label, Entry, Button, Checkbutton, Radiobutton
++
+ class SearchDialogBase:
+ 
+     title = "Search Dialog"
+     icon = "Search"
+     needwrapbutton = 1
++    bottom_btns = None
+ 
+     def __init__(self, root, engine):
+         self.root = root
+         self.engine = engine
+-        self.top = None
++        self.ttop = None
+ 
+     def open(self, text, searchphrase=None):
+         self.text = text
+-        if not self.top:
++        if not self.ttop:
+             self.create_widgets()
+         else:
+-            self.top.deiconify()
+-            self.top.tkraise()
++            self.ttop.deiconify()
++            self.ttop.tkraise()
+         if searchphrase:
+-            self.ent.delete(0,"end")
+-            self.ent.insert("end",searchphrase)
++            self.ent.delete(0, "end")
++            self.ent.insert("end", searchphrase)
+         self.ent.focus_set()
+         self.ent.selection_range(0, "end")
+         self.ent.icursor(0)
+-        self.top.grab_set()
++        self.ttop.grab_set()
+ 
+     def close(self, event=None):
+-        if self.top:
+-            self.top.grab_release()
+-            self.top.withdraw()
++        if self.ttop:
++            self.ttop.grab_release()
++            self.ttop.withdraw()
+ 
+     def create_widgets(self):
+         top = Toplevel(self.root)
+@@ -38,103 +45,96 @@
+         top.protocol("WM_DELETE_WINDOW", self.close)
+         top.wm_title(self.title)
+         top.wm_iconname(self.icon)
+-        self.top = top
++        top.resizable(height=False, width=False)
++        self.ttop = top
++        self.top = Frame(top)
+ 
+         self.row = 0
+-        self.top.grid_columnconfigure(0, pad=2, weight=0)
+-        self.top.grid_columnconfigure(1, pad=2, minsize=100, weight=100)
++        self.top.grid(sticky='news')
+ 
+         self.create_entries()
+         self.create_option_buttons()
+         self.create_other_buttons()
+-        return self.create_command_buttons()
++        self.create_command_buttons()
+ 
++
+     def make_entry(self, label, var):
+         l = Label(self.top, text=label)
+-        l.grid(row=self.row, column=0, sticky="nw")
++        l.grid(row=self.row, column=0, sticky="ne", padx=6, pady=6)
+         e = Entry(self.top, textvariable=var, exportselection=0)
+-        e.grid(row=self.row, column=1, sticky="nwe")
++        e.grid(row=self.row, column=1, sticky="nwe", padx=6, pady=6)
+         self.row = self.row + 1
+         return e
+ 
+     def make_frame(self,labeltext=None):
+         if labeltext:
+             l = Label(self.top, text=labeltext)
+-            l.grid(row=self.row, column=0, sticky="nw")
++            l.grid(row=self.row, column=0, sticky="ne", padx=6, pady=6)
+         f = Frame(self.top)
+-        f.grid(row=self.row, column=1, columnspan=1, sticky="nwe")
++        f.grid(row=self.row, column=1, columnspan=1, sticky="nwe",
++               padx=6, pady=6 if labeltext else 0)
+         self.row = self.row + 1
+         return f
+ 
+-    def make_button(self, label, command, isdef=0):
+-        b = Button(self.buttonframe,
+-                   text=label, command=command,
+-                   default=isdef and "active" or "normal")
+-        cols,rows=self.buttonframe.grid_size()
+-        b.grid(pady=1,row=rows,column=0,sticky="ew")
+-        self.buttonframe.grid(rowspan=rows+1)
+-        return b
+-
+     def create_entries(self):
+-        self.ent = self.make_entry("Find:", self.engine.patvar)
++        self.ent = self.make_entry("Find", self.engine.patvar)
+ 
+     def create_option_buttons(self):
+         f = self.make_frame("Options")
+ 
+-        btn = Checkbutton(f, anchor="w",
+-                variable=self.engine.revar,
+-                text="Regular expression")
++        btn = Checkbutton(f, variable=self.engine.revar,
++                          text="Regular expression")
+         btn.pack(side="left", fill="both")
+         if self.engine.isre():
+-            btn.select()
++            btn.invoke()
+ 
+-        btn = Checkbutton(f, anchor="w",
+-                variable=self.engine.casevar,
+-                text="Match case")
++        btn = Checkbutton(f, variable=self.engine.casevar, text="Match case")
+         btn.pack(side="left", fill="both")
+         if self.engine.iscase():
+-            btn.select()
++            btn.invoke()
+ 
+-        btn = Checkbutton(f, anchor="w",
+-                variable=self.engine.wordvar,
+-                text="Whole word")
++        btn = Checkbutton(f, variable=self.engine.wordvar, text="Whole word")
+         btn.pack(side="left", fill="both")
+         if self.engine.isword():
+-            btn.select()
++            btn.invoke()
+ 
+         if self.needwrapbutton:
+-            btn = Checkbutton(f, anchor="w",
+-                    variable=self.engine.wrapvar,
+-                    text="Wrap around")
++            btn = Checkbutton(f, variable=self.engine.wrapvar,
++                              text="Wrap around")
+             btn.pack(side="left", fill="both")
+             if self.engine.iswrap():
+-                btn.select()
++                btn.invoke()
+ 
+     def create_other_buttons(self):
+         f = self.make_frame("Direction")
+ 
+-        #lbl = Label(f, text="Direction: ")
+-        #lbl.pack(side="left")
+-
+-        btn = Radiobutton(f, anchor="w",
+-                variable=self.engine.backvar, value=1,
+-                text="Up")
+-        btn.pack(side="left", fill="both")
++        btn = Radiobutton(f, variable=self.engine.backvar, value=1, text="Up")
++        btn.pack(side="left")
+         if self.engine.isback():
+-            btn.select()
++            btn.invoke()
+ 
+-        btn = Radiobutton(f, anchor="w",
+-                variable=self.engine.backvar, value=0,
+-                text="Down")
+-        btn.pack(side="left", fill="both")
++        btn = Radiobutton(f, variable=self.engine.backvar, value=0, text="Down")
++        btn.pack(side="left")
+         if not self.engine.isback():
+-            btn.select()
++            btn.invoke()
+ 
+     def create_command_buttons(self):
+-        #
+-        # place button frame on the right
+-        f = self.buttonframe = Frame(self.top)
+-        f.grid(row=0,column=2,padx=2,pady=2,ipadx=2,ipady=2)
++        self.bottom_btns = self.bottom_btns or []
++        f = Frame(self.top)
++        f.grid(row=self.row, column=0, columnspan=len(self.bottom_btns) + 1,
++               pady=6)
+ 
+-        b = self.make_button("close", self.close)
+-        b.lower()
++        column = 0
++        b = Button(f, text="Close", command=self.close)
++        b.grid(row=self.row, column=column, padx=6, pady=6)
++        column += 1
++
++        btns = {}
++        for tbtn in self.bottom_btns:
++            opts = {'text': tbtn[0], 'command': getattr(self, tbtn[1])}
++            if len(tbtn) == 3:
++                opts['default'] = tbtn[2] and 'active' or 'normal'
++
++            btns[opts['text']] = Button(f, **opts).grid(row=self.row, padx=6,
++                                                        pady=6, column=column)
++            column += 1
+Index: CallTipWindow.py
+===================================================================
+--- CallTipWindow.py	(revision 63995)
++++ CallTipWindow.py	(revision 65541)
+@@ -4,8 +4,14 @@
+ Used by the CallTips IDLE extension.
+ 
+ """
+-from Tkinter import *
++from Tkinter import Toplevel, Label, TclError
++from Tkconstants import SOLID, LEFT
+ 
++from configHandler import idleConf
++
++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'):
++    from ttk import Label
++
+ HIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-hide>>"
+ HIDE_SEQUENCES = ("<Key-Escape>", "<FocusOut>")
+ CHECKHIDE_VIRTUAL_EVENT_NAME = "<<calltipwindow-checkhide>>"
+@@ -142,6 +148,8 @@
+ #
+ class container: # Conceptually an editor_window
+     def __init__(self):
++        from Tkinter import Tk, Text
++        from Tkconstants import BOTH
+         root = Tk()
+         text = self.text = Text(root)
+         text.pack(side=LEFT, fill=BOTH, expand=1)
+@@ -163,6 +171,8 @@
+     def calltip_hide(self, event):
+         self.calltip.hidetip()
+ 
++# XXX Bugged test
++
+ def main():
+     # Test code
+     c=container()
+Index: SearchDialog.py
+===================================================================
+--- SearchDialog.py	(revision 63995)
++++ SearchDialog.py	(revision 65541)
+@@ -1,8 +1,8 @@
+-from Tkinter import *
++from Tkinter import TclError
++
+ import SearchEngine
+ from SearchDialogBase import SearchDialogBase
+ 
+-
+ def _setup(text):
+     root = text._root()
+     engine = SearchEngine.get(root)
+@@ -12,7 +12,7 @@
+ 
+ def find(text):
+     pat = text.get("sel.first", "sel.last")
+-    return _setup(text).open(text,pat)
++    return _setup(text).open(text, pat)
+ 
+ def find_again(text):
+     return _setup(text).find_again(text)
+@@ -21,10 +21,10 @@
+     return _setup(text).find_selection(text)
+ 
+ class SearchDialog(SearchDialogBase):
++    bottom_btns = [("Find", 'default_command', 1)]
+ 
+     def create_widgets(self):
+-        f = SearchDialogBase.create_widgets(self)
+-        self.make_button("Find", self.default_command, 1)
++        SearchDialogBase.create_widgets(self)
+ 
+     def default_command(self, event=None):
+         if not self.engine.getprog():
+Index: idlever.py
+===================================================================
+--- idlever.py	(revision 63995)
++++ idlever.py	(revision 65541)
+@@ -1 +1 @@
+-IDLE_VERSION = "2.6a3"
++IDLE_VERSION = "2.6a3-gpolo"
+Index: tabbedpages_old.py
+===================================================================
+--- tabbedpages_old.py	(revision 0)
++++ tabbedpages_old.py	(revision 65541)
+@@ -0,0 +1,546 @@
++"""An implementation of tabbed pages using only standard Tkinter.
++
++Originally developed for use in IDLE. Based on tabpage.py.
++
++Classes exported:
++TabbedPageSet -- A Tkinter implementation of a tabbed-page widget.
++TabSet -- A widget containing tabs (buttons) in one or more rows.
++
++"""
++from Tkinter import Frame, Radiobutton
++from Tkconstants import BOTH, TOP, X, RAISED, NSEW, FLAT, LEFT
++
++from tabbedpages import InvalidNameError, AlreadyExistsError
++
++
++class TabSet(Frame):
++    """A widget containing tabs (buttons) in one or more rows.
++
++    Only one tab may be selected at a time.
++
++    """
++    def __init__(self, page_set, select_command,
++                 tabs=None, n_rows=1, max_tabs_per_row=5,
++                 expand_tabs=False, **kw):
++        """Constructor arguments:
++
++        select_command -- A callable which will be called when a tab is
++        selected. It is called with the name of the selected tab as an
++        argument.
++
++        tabs -- A list of strings, the names of the tabs. Should be specified in
++        the desired tab order. The first tab will be the default and first
++        active tab. If tabs is None or empty, the TabSet will be initialized
++        empty.
++
++        n_rows -- Number of rows of tabs to be shown. If n_rows <= 0 or is
++        None, then the number of rows will be decided by TabSet. See
++        _arrange_tabs() for details.
++
++        max_tabs_per_row -- Used for deciding how many rows of tabs are needed,
++        when the number of rows is not constant. See _arrange_tabs() for
++        details.
++
++        """
++        Frame.__init__(self, page_set, **kw)
++        self.select_command = select_command
++        self.n_rows = n_rows
++        self.max_tabs_per_row = max_tabs_per_row
++        self.expand_tabs = expand_tabs
++        self.page_set = page_set
++
++        self._tabs = {}
++        self._tab2row = {}
++        if tabs:
++            self._tab_names = list(tabs)
++        else:
++            self._tab_names = []
++        self._selected_tab = None
++        self._tab_rows = []
++
++        self.padding_frame = Frame(self, height=2,
++                                   borderwidth=0, relief=FLAT,
++                                   background=self.cget('background'))
++        self.padding_frame.pack(side=TOP, fill=X, expand=False)
++
++        self._arrange_tabs()
++
++    def add_tab(self, tab_name):
++        """Add a new tab with the name given in tab_name."""
++        if not tab_name:
++            raise InvalidNameError("Invalid Tab name: '%s'" % tab_name)
++        if tab_name in self._tab_names:
++            raise AlreadyExistsError("Tab named '%s' already exists" %tab_name)
++
++        self._tab_names.append(tab_name)
++        self._arrange_tabs()
++
++    def remove_tab(self, tab_name):
++        """Remove the tab named <tab_name>"""
++        if not tab_name in self._tab_names:
++            raise KeyError("No such Tab: '%s" % tab_name)
++
++        self._tab_names.remove(tab_name)
++        self._arrange_tabs()
++
++    def set_selected_tab(self, tab_name):
++        """Show the tab named <tab_name> as the selected one"""
++        if tab_name == self._selected_tab:
++            return
++        if tab_name is not None and tab_name not in self._tabs:
++            raise KeyError("No such Tab: '%s" % tab_name)
++
++        # deselect the current selected tab
++        if self._selected_tab is not None:
++            self._tabs[self._selected_tab].set_normal()
++        self._selected_tab = None
++
++        if tab_name is not None:
++            # activate the tab named tab_name
++            self._selected_tab = tab_name
++            tab = self._tabs[tab_name]
++            tab.set_selected()
++            # move the tab row with the selected tab to the bottom
++            tab_row = self._tab2row[tab]
++            tab_row.pack_forget()
++            tab_row.pack(side=TOP, fill=X, expand=0)
++
++    def _add_tab_row(self, tab_names, expand_tabs):
++        if not tab_names:
++            return
++
++        tab_row = Frame(self)
++        tab_row.pack(side=TOP, fill=X, expand=0)
++        self._tab_rows.append(tab_row)
++
++        for tab_name in tab_names:
++            tab = TabSet.TabButton(tab_name, self.select_command,
++                                   tab_row, self)
++            if expand_tabs:
++                tab.pack(side=LEFT, fill=X, expand=True)
++            else:
++                tab.pack(side=LEFT)
++            self._tabs[tab_name] = tab
++            self._tab2row[tab] = tab_row
++
++        # tab is the last one created in the above loop
++        tab.is_last_in_row = True
++
++    def _reset_tab_rows(self):
++        while self._tab_rows:
++            tab_row = self._tab_rows.pop()
++            tab_row.destroy()
++        self._tab2row = {}
++
++    def _arrange_tabs(self):
++        """
++        Arrange the tabs in rows, in the order in which they were added.
++
++        If n_rows >= 1, this will be the number of rows used. Otherwise the
++        number of rows will be calculated according to the number of tabs and
++        max_tabs_per_row. In this case, the number of rows may change when
++        adding/removing tabs.
++
++        """
++        # remove all tabs and rows
++        while self._tabs:
++            self._tabs.popitem()[1].destroy()
++        self._reset_tab_rows()
++
++        if not self._tab_names:
++            return
++
++        if self.n_rows is not None and self.n_rows > 0:
++            n_rows = self.n_rows
++        else:
++            # calculate the required number of rows
++            n_rows = (len(self._tab_names) - 1) // self.max_tabs_per_row + 1
++
++        # not expanding the tabs with more than one row is very ugly
++        expand_tabs = self.expand_tabs or n_rows > 1
++        i = 0 # index in self._tab_names
++        for row_index in range(n_rows):
++            # calculate required number of tabs in this row
++            n_tabs = (len(self._tab_names) - i - 1) // (n_rows - row_index) + 1
++            tab_names = self._tab_names[i:i + n_tabs]
++            i += n_tabs
++            self._add_tab_row(tab_names, expand_tabs)
++
++        # re-select selected tab so it is properly displayed
++        selected = self._selected_tab
++        self.set_selected_tab(None)
++        if selected in self._tab_names:
++            self.set_selected_tab(selected)
++
++    class TabButton(Frame):
++        """A simple tab-like widget."""
++
++        bw = 2 # borderwidth
++
++        def __init__(self, name, select_command, tab_row, tab_set):
++            """Constructor arguments:
++
++            name -- The tab's name, which will appear in its button.
++
++            select_command -- The command to be called upon selection of the
++            tab. It is called with the tab's name as an argument.
++
++            """
++            Frame.__init__(self, tab_row, borderwidth=self.bw, relief=RAISED)
++
++            self.name = name
++            self.select_command = select_command
++            self.tab_set = tab_set
++            self.is_last_in_row = False
++
++            self.button = Radiobutton(
++                self, text=name, command=self._select_event,
++                padx=5, pady=1, takefocus=False, indicatoron=False,
++                highlightthickness=0, selectcolor='', borderwidth=0)
++            self.button.pack(side=LEFT, fill=X, expand=True)
++
++            self._init_masks()
++            self.set_normal()
++
++        def _select_event(self, *args):
++            """Event handler for tab selection.
++
++            With TabbedPageSet, this calls TabbedPageSet.change_page, so that
++            selecting a tab changes the page.
++
++            Note that this does -not- call set_selected -- it will be called by
++            TabSet.set_selected_tab, which should be called when whatever the
++            tabs are related to changes.
++
++            """
++            self.select_command(self.name)
++            return
++
++        def set_selected(self):
++            """Assume selected look"""
++            self._place_masks(selected=True)
++
++        def set_normal(self):
++            """Assume normal look"""
++            self._place_masks(selected=False)
++
++        def _init_masks(self):
++            page_set = self.tab_set.page_set
++            background = page_set.pages_frame.cget('background')
++            # mask replaces the middle of the border with the background color
++            self.mask = Frame(page_set, borderwidth=0, relief=FLAT,
++                              background=background)
++            # mskl replaces the bottom-left corner of the border with a normal
++            # left border
++            self.mskl = Frame(page_set, borderwidth=0, relief=FLAT,
++                              background=background)
++            self.mskl.ml = Frame(self.mskl, borderwidth=self.bw,
++                                 relief=RAISED)
++            self.mskl.ml.place(x=0, y=-self.bw,
++                               width=2*self.bw, height=self.bw*4)
++            # mskr replaces the bottom-right corner of the border with a normal
++            # right border
++            self.mskr = Frame(page_set, borderwidth=0, relief=FLAT,
++                              background=background)
++            self.mskr.mr = Frame(self.mskr, borderwidth=self.bw,
++                                 relief=RAISED)
++
++        def _place_masks(self, selected=False):
++            height = self.bw
++            if selected:
++                height += self.bw
++
++            self.mask.place(in_=self,
++                            relx=0.0, x=0,
++                            rely=1.0, y=0,
++                            relwidth=1.0, width=0,
++                            relheight=0.0, height=height)
++
++            self.mskl.place(in_=self,
++                            relx=0.0, x=-self.bw,
++                            rely=1.0, y=0,
++                            relwidth=0.0, width=self.bw,
++                            relheight=0.0, height=height)
++
++            page_set = self.tab_set.page_set
++            if selected and ((not self.is_last_in_row) or
++                             (self.winfo_rootx() + self.winfo_width() <
++                              page_set.winfo_rootx() + page_set.winfo_width())
++                             ):
++                # for a selected tab, if its rightmost edge isn't on the
++                # rightmost edge of the page set, the right mask should be one
++                # borderwidth shorter (vertically)
++                height -= self.bw
++
++            self.mskr.place(in_=self,
++                            relx=1.0, x=0,
++                            rely=1.0, y=0,
++                            relwidth=0.0, width=self.bw,
++                            relheight=0.0, height=height)
++
++            self.mskr.mr.place(x=-self.bw, y=-self.bw,
++                               width=2*self.bw, height=height + self.bw*2)
++
++            # finally, lower the tab set so that all of the frames we just
++            # placed hide it
++            self.tab_set.lower()
++
++class TabbedPageSet(Frame):
++    """A Tkinter tabbed-pane widget.
++
++    Constains set of 'pages' (or 'panes') with tabs above for selecting which
++    page is displayed. Only one page will be displayed at a time.
++
++    Pages may be accessed through the 'pages' attribute, which is a dictionary
++    of pages, using the name given as the key. A page is an instance of a
++    subclass of Tk's Frame widget.
++
++    The page widgets will be created (and destroyed when required) by the
++    TabbedPageSet. Do not call the page's pack/place/grid/destroy methods.
++
++    Pages may be added or removed at any time using the add_page() and
++    remove_page() methods.
++
++    """
++    class Page(object):
++        """Abstract base class for TabbedPageSet's pages.
++
++        Subclasses must override the _show() and _hide() methods.
++
++        """
++        uses_grid = False
++
++        def __init__(self, page_set):
++            self.frame = Frame(page_set, borderwidth=2, relief=RAISED)
++
++        def _show(self):
++            raise NotImplementedError
++
++        def _hide(self):
++            raise NotImplementedError
++
++    class PageRemove(Page):
++        """Page class using the grid placement manager's "remove" mechanism."""
++        uses_grid = True
++
++        def _show(self):
++            self.frame.grid(row=0, column=0, sticky=NSEW)
++
++        def _hide(self):
++            self.frame.grid_remove()
++
++    class PageLift(Page):
++        """Page class using the grid placement manager's "lift" mechanism."""
++        uses_grid = True
++
++        def __init__(self, page_set):
++            super(TabbedPageSet.PageLift, self).__init__(page_set)
++            self.frame.grid(row=0, column=0, sticky=NSEW)
++            self.frame.lower()
++
++        def _show(self):
++            self.frame.lift()
++
++        def _hide(self):
++            self.frame.lower()
++
++    class PagePackForget(Page):
++        """Page class using the pack placement manager's "forget" mechanism."""
++        def _show(self):
++            self.frame.pack(fill=BOTH, expand=True)
++
++        def _hide(self):
++            self.frame.pack_forget()
++
++    def __init__(self, parent, page_names=None, page_class=PageLift,
++                 n_rows=1, max_tabs_per_row=5, expand_tabs=False,
++                 **kw):
++        """Constructor arguments:
++
++        page_names -- A list of strings, each will be the dictionary key to a
++        page's widget, and the name displayed on the page's tab. Should be
++        specified in the desired page order. The first page will be the default
++        and first active page. If page_names is None or empty, the
++        TabbedPageSet will be initialized empty.
++
++        n_rows, max_tabs_per_row -- Parameters for the TabSet which will
++        manage the tabs. See TabSet's docs for details.
++
++        page_class -- Pages can be shown/hidden using three mechanisms:
++
++        * PageLift - All pages will be rendered one on top of the other. When
++          a page is selected, it will be brought to the top, thus hiding all
++          other pages. Using this method, the TabbedPageSet will not be resized
++          when pages are switched. (It may still be resized when pages are
++          added/removed.)
++
++        * PageRemove - When a page is selected, the currently showing page is
++          hidden, and the new page shown in its place. Using this method, the
++          TabbedPageSet may resize when pages are changed.
++
++        * PagePackForget - This mechanism uses the pack placement manager.
++          When a page is shown it is packed, and when it is hidden it is
++          unpacked (i.e. pack_forget). This mechanism may also cause the
++          TabbedPageSet to resize when the page is changed.
++
++        """
++        Frame.__init__(self, parent, **kw)
++
++        self.page_class = page_class
++        self.pages = {}
++        self._pages_order = []
++        self._current_page = None
++        self._default_page = None
++
++        self.columnconfigure(0, weight=1)
++        self.rowconfigure(1, weight=1)
++
++        self.pages_frame = Frame(self)
++        self.pages_frame.grid(row=1, column=0, sticky=NSEW)
++        if self.page_class.uses_grid:
++            self.pages_frame.columnconfigure(0, weight=1)
++            self.pages_frame.rowconfigure(0, weight=1)
++
++        # the order of the following commands is important
++        self._tab_set = TabSet(self, self.change_page, n_rows=n_rows,
++                               max_tabs_per_row=max_tabs_per_row,
++                               expand_tabs=expand_tabs)
++        if page_names:
++            for name in page_names:
++                self.add_page(name)
++        self._tab_set.grid(row=0, column=0, sticky=NSEW)
++
++        self.change_page(self._default_page)
++
++    def update_tabtitle(self, tab, newtitle):
++        """Update tab title to newtitle."""
++        currpage = self.pages[tab.title]
++        old = tab.title
++
++        # resolve title duplicate
++        if newtitle in self.pages:
++            count = 1
++            temptitle = newtitle
++            while temptitle in self.pages:
++                temptitle = "%s #%d" % (newtitle, count)
++                count += 1
++            newtitle = temptitle
++
++        tab.title = newtitle
++        # now update 1 million places.. yeh..
++        self.pages[newtitle] = self.pages.pop(old)
++        self._pages_order[self._pages_order.index(old)] = newtitle
++        self._tab_set._tab_names[self._tab_set._tab_names.index(old)] = newtitle
++        self._tab_set._tabs[newtitle] = self._tab_set._tabs.pop(old)
++        self._tab_set._tabs[newtitle].button['text'] = newtitle
++        if self._tab_set._selected_tab == old:
++            self._tab_set._selected_tab = newtitle
++        if self._current_page == old:
++            self._current_page = newtitle
++        if self._default_page == old:
++            self._default_page = newtitle
++
++    def add_page(self, page_name):
++        """Add a new page with the name given in page_name."""
++        if not page_name:
++            raise InvalidNameError("Invalid TabPage name: '%s'" % page_name)
++        if page_name in self.pages:
++            raise AlreadyExistsError(
++                "TabPage named '%s' already exists" % page_name)
++
++        self.pages[page_name] = self.page_class(self.pages_frame)
++        self._pages_order.append(page_name)
++        self._tab_set.add_tab(page_name)
++
++        if len(self.pages) == 1: # adding first page
++            self._default_page = page_name
++            self.change_page(page_name)
++
++        return self.pages[page_name]
++
++    def remove_page(self, page_name):
++        """Destroy the page whose name is given in page_name."""
++        if not page_name in self.pages:
++            raise KeyError("No such TabPage: '%s" % page_name)
++
++        self._pages_order.remove(page_name)
++        # handle removing last remaining, default, or currently shown page
++        if len(self._pages_order) > 0:
++            if page_name == self._default_page:
++                # set a new default page
++                self._default_page = self._pages_order[0]
++        else:
++            self._default_page = None
++
++        if page_name == self._current_page:
++            self.change_page(self._default_page)
++
++        self._tab_set.remove_tab(page_name)
++        page = self.pages.pop(page_name)
++        page.frame.destroy()
++
++    def change_page(self, page_name):
++        """Show the page whose name is given in page_name."""
++        if self._current_page == page_name:
++            return
++        if page_name is not None and page_name not in self.pages:
++            raise KeyError("No such TabPage: '%s'" % page_name)
++
++        if self._current_page is not None:
++            self.pages[self._current_page]._hide()
++        self._current_page = None
++
++        if page_name is not None:
++            self._current_page = page_name
++            self.pages[page_name]._show()
++
++        self._tab_set.set_selected_tab(page_name)
++        self.event_generate('<<NotebookTabChanged>>') # conform to ttk.Notebook
++
++    def last_page(self):
++        return self.pages[self._pages_order[-1]]
++
++    # Some methods to make this Notebook compatible with the ttk Notebook
++
++    def select(self, page_id=None):
++        """Return the name of the currently selected page, otherwise
++        selects page_id.
++        
++        page_id may be an integer or a page name."""
++        if page_id is None:
++            return self._current_page
++        elif isinstance(page_id, int):
++            self.change_page(self._pages_order[page_id])
++        elif isinstance(page_id, str):
++            self.change_page(page_id)
++
++    def index(self, page_name):
++        """Return the index of page_name."""
++        return self._pages_order.index(page_name)
++
++    def tabs(self):
++        """Return a list of page names."""
++        return self._pages_order
++
++if __name__ == '__main__':
++    from Tkinter import Tk, Label, Entry, Button
++    # test dialog
++    root=Tk()
++    tabPage=TabbedPageSet(root, page_names=['Foobar','Baz'], n_rows=0,
++                          expand_tabs=False,
++                          )
++    tabPage.pack(side=TOP, expand=True, fill=BOTH)
++    Label(tabPage.pages['Foobar'].frame, text='Foo', pady=20).pack()
++    Label(tabPage.pages['Foobar'].frame, text='Bar', pady=20).pack()
++    Label(tabPage.pages['Baz'].frame, text='Baz').pack()
++    entryPgName=Entry(root)
++    buttonAdd=Button(root, text='Add Page',
++            command=lambda:tabPage.add_page(entryPgName.get()))
++    buttonRemove=Button(root, text='Remove Page',
++            command=lambda:tabPage.remove_page(entryPgName.get()))
++    labelPgName=Label(root, text='name of page to add/remove:')
++    buttonAdd.pack(padx=5, pady=5)
++    buttonRemove.pack(padx=5, pady=5)
++    labelPgName.pack(padx=5)
++    entryPgName.pack(padx=5)
++    root.mainloop()
+Index: utils.py
+===================================================================
+--- utils.py	(revision 0)
++++ utils.py	(revision 65541)
+@@ -0,0 +1,4 @@
++def callback(func, *myargs):
++    def w(*args):
++        return func(*(args + myargs))
++    return w
+Index: TreeWidget.py
+===================================================================
+--- TreeWidget.py	(revision 63995)
++++ TreeWidget.py	(revision 65541)
+@@ -15,12 +15,16 @@
+ # - optimize tree redraw after expand of subnode
+ 
+ import os
+-from Tkinter import *
+-import imp
++from Tkinter import Tk, Label, Entry, Frame, Canvas, Scrollbar, PhotoImage
++from Tkconstants import ALL, END
+ 
+ import ZoomHeight
+ from configHandler import idleConf
+ 
++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int')
++if TTK:
++    from ttk import Label, Entry, Frame, Scrollbar
++
+ ICONDIR = "Icons"
+ 
+ # Look for Icons subdirectory in the same directory as this module
+@@ -248,7 +252,9 @@
+             label = self.label
+         except AttributeError:
+             # padding carefully selected (on Windows) to match Entry widget:
+-            self.label = Label(self.canvas, text=text, bd=0, padx=2, pady=2)
++            self.label = Label(self.canvas, text=text)
++            if not TTK:
++                self.label.configure(bd=0, padx=2, pady=2)
+         theme = idleConf.GetOption('main','Theme','name')
+         if self.selected:
+             self.label.configure(idleConf.GetHighlight(theme, 'hilite'))
+@@ -451,7 +457,10 @@
+ 
+ # Testing functions
+ 
++# XXX Can't run these tests
++
+ def test():
++    from Tkinter import Toplevel
+     import PyShell
+     root = Toplevel(PyShell.root)
+     root.configure(bd=0, bg="yellow")
+Index: run.py
+===================================================================
+--- run.py	(revision 63995)
++++ run.py	(revision 65541)
+@@ -24,11 +24,13 @@
+ except ImportError:
+     pass
+ else:
+-    def idle_formatwarning_subproc(message, category, filename, lineno):
++    def idle_formatwarning_subproc(message, category, filename, lineno,
++        line=None):
+         """Format warnings the IDLE way"""
+         s = "\nWarning (from warnings module):\n"
+         s += '  File \"%s\", line %s\n' % (filename, lineno)
+-        line = linecache.getline(filename, lineno).strip()
++        if line is None:
++            line = linecache.getline(filename, lineno).strip()
+         if line:
+             s += "    %s\n" % line
+         s += "%s: %s\n" % (category.__name__, message)
+Index: AutoExpand.py
+===================================================================
+--- AutoExpand.py	(revision 63995)
++++ AutoExpand.py	(revision 65541)
+@@ -15,8 +15,8 @@
+ 
+     wordchars = string.ascii_letters + string.digits + "_"
+ 
+-    def __init__(self, editwin):
+-        self.text = editwin.text
++    def __init__(self, editpage):
++        self.text = editpage.text
+         self.state = None
+ 
+     def expand_word_event(self, event):
+Index: Percolator.py
+===================================================================
+--- Percolator.py	(revision 63995)
++++ Percolator.py	(revision 65541)
+@@ -1,5 +1,5 @@
++from Delegator import Delegator
+ from WidgetRedirector import WidgetRedirector
+-from Delegator import Delegator
+ 
+ class Percolator:
+ 
+@@ -81,5 +81,5 @@
+     root.mainloop()
+ 
+ if __name__ == "__main__":
+-    from Tkinter import *
++    from Tkinter import Tk, Text
+     main()
+Index: tabbedpages_new.py
+===================================================================
+--- tabbedpages_new.py	(revision 0)
++++ tabbedpages_new.py	(revision 65541)
+@@ -0,0 +1,116 @@
++"""Classes exported:
++
++TabbedPageSet -- A custom ttk.Notebook used by IDLE.
++"""
++from ttk import Frame, Notebook
++
++from tabbedpages import InvalidNameError, AlreadyExistsError
++
++class FramePage(object):
++    def __init__(self, notebook):
++        self.frame = Frame(notebook)
++
++class TabbedPageSet(Notebook):
++    """
++    Pages may be accessed through the 'pages' attribute, which is a dictionary
++    of pages, using the name given as the key. A page is an instance of a
++    subclass of ttk's Frame widget.
++
++    Pages may be added or removed at any time using the add_page() and
++    remove_page() methods.
++    """
++
++    def __init__(self, master, page_names=None, **kw):
++        """Constructor arguments:
++
++        page_names -- A list of strings, each will be the dictionary key to a
++        page's widget, and the name displayed on the page's tab. Should be
++        specified in the desired page order. The first page will be the default
++        and first active page. If page_names is None or empty, the
++        TabbedPageSet will be initialized empty.
++        """
++        Notebook.__init__(self, master, **kw)
++
++        self.pages = {}
++        page_names = page_names or ()
++        for name in page_names:
++            self.add_page(name)
++
++    def update_tabtitle(self, tab, newtitle):
++        """Update tab title to newtitle."""
++        currpage = self.pages[tab.title].frame
++        old = tab.title
++
++        # resolve title duplicate
++        if newtitle in self.pages and currpage != self.pages[newtitle].frame:
++            # newtitle is already present, and the current tab is not the
++            # one who owns it
++            count = 1
++            temptitle = newtitle
++            while temptitle in self.pages:
++                if currpage == self.pages[temptitle].frame:
++                    break
++                temptitle = "%s #%d" % (newtitle, count)
++                count += 1
++            newtitle = temptitle
++
++        tab.title = newtitle
++        self.pages[newtitle] = self.pages.pop(old)
++        self.tab(currpage, text=newtitle)
++
++    def add_page(self, page_name):
++        """Add a new page with the name given in page_name."""
++        if not page_name:
++            raise InvalidNameError("Invalid TabPage name: '%s'" % page_name)
++        if page_name in self.pages:
++            raise AlreadyExistsError(
++                "TabPage named '%s' already exists" % page_name)
++
++        fpage = FramePage(self)
++        self.pages[page_name] = fpage
++        self.add(fpage.frame, text=page_name, padding=6)
++
++        # workaround for bug #1878298 at tktoolkit sf bug tracker
++        self.event_generate('<Expose>')
++
++        return fpage
++
++    def remove_page(self, page_name):
++        """Remove page_name from the notebook."""
++        if not page_name in self.pages:
++            raise KeyError("No such TabPage: '%s" % page_name)
++
++        self.forget(self.index(self.pages[page_name].frame))
++        del self.pages[page_name]
++
++        # workaround for bug #1878298 at tktoolkit sf bug tracker
++        self.event_generate('<Expose>')
++
++    def last_page(self):
++        """Return the last page in the notebook."""
++        return self.pages[self.tab(self.index('end') - 1)['text']]
++
++if __name__ == '__main__':
++    from Tkinter import Tk
++    from Tkconstants import TOP, BOTH
++    from ttk import Label, Entry, Button, Style
++    # test dialog
++    root=Tk()
++    style = Style()
++    style.configure('C.TLabel', padding=20)
++    tabPage=TabbedPageSet(root, page_names=['Foobar','Baz'])
++    tabPage.pack(side=TOP, expand=True, fill=BOTH)
++    Label(tabPage.pages['Foobar'].frame, text='Foo', style='C.TLabel').pack()
++    Label(tabPage.pages['Foobar'].frame, text='Bar', style='C.TLabel').pack()
++    Label(tabPage.pages['Baz'].frame, text='Baz').pack()
++    entryPgName=Entry(root)
++    buttonAdd=Button(root, text='Add Page',
++            command=lambda:tabPage.add_page(entryPgName.get()))
++    buttonRemove=Button(root, text='Remove Page',
++            command=lambda:tabPage.remove_page(entryPgName.get()))
++    labelPgName=Label(root, text='name of page to add/remove:')
++    buttonAdd.pack(padx=5, pady=5)
++    buttonRemove.pack(padx=5, pady=5)
++    labelPgName.pack(padx=5)
++    entryPgName.pack(padx=5)
++    root.mainloop()
+Index: dynOptionMenuWidget.py
+===================================================================
+--- dynOptionMenuWidget.py	(revision 63995)
++++ dynOptionMenuWidget.py	(revision 65541)
+@@ -2,34 +2,41 @@
+ OptionMenu widget modified to allow dynamic menu reconfiguration
+ and setting of highlightthickness
+ """
+-from Tkinter import OptionMenu
++from Tkinter import OptionMenu, Menu
+ from Tkinter import _setit
+ import copy
+ 
++from configHandler import idleConf
++TTK = idleConf.GetOption('main', 'General', 'use-ttk', type='int')
++if TTK:
++    from ttk import *
++
+ class DynOptionMenu(OptionMenu):
+-    """
+-    unlike OptionMenu, our kwargs can include highlightthickness
+-    """
++    """Unlike OptionMenu, our kwargs can include highlightthickness"""
+     def __init__(self, master, variable, value, *values, **kwargs):
+         #get a copy of kwargs before OptionMenu.__init__ munges them
+         kwargsCopy=copy.copy(kwargs)
+         if 'highlightthickness' in kwargs.keys():
+             del(kwargs['highlightthickness'])
++        self.command=kwargs.get('command')
++        self.variable=variable
++
+         OptionMenu.__init__(self, master, variable, value, *values, **kwargs)
+         self.config(highlightthickness=kwargsCopy.get('highlightthickness'))
+-        #self.menu=self['menu']
+-        self.variable=variable
+-        self.command=kwargs.get('command')
+ 
+-    def SetMenu(self,valueList,value=None):
++    def SetMenu(self, valueList, value=None):
+         """
+         clear and reload the menu with a new set of options.
+         valueList - list of new options
+         value - initial value to set the optionmenu's menubutton to
+         """
+-        self['menu'].delete(0,'end')
+-        for item in valueList:
+-            self['menu'].add_command(label=item,
++        if TTK:
++            self.set_menu(value, *valueList)
++        else:
++            menu = self['menu']
++            menu.delete(0,'end')
++            for item in valueList:
++                menu.add_command(label=item,
+                     command=_setit(self.variable,item,self.command))
+-        if value:
+-            self.variable.set(value)
++            if value:
++                self.variable.set(value)
+Index: extend.txt
+===================================================================
+--- extend.txt	(revision 63995)
++++ extend.txt	(revision 65541)
+@@ -81,3 +81,10 @@
+ 
+ For further information on binding refer to the Tkinter Resources web page at
+ python.org and to the Tk Command "bind" man page.
++
++
++Note
++----
++
++Given that this branch is using tabbed pages, don't expect the previous
++description to be correct related to the code present here.
+Index: MultiStatusBar.py
+===================================================================
+--- MultiStatusBar.py	(revision 63995)
++++ MultiStatusBar.py	(revision 65541)
+@@ -1,5 +1,11 @@
+-from Tkinter import *
++from Tkinter import Tk, Frame, Label
++from Tkconstants import LEFT, SUNKEN, W
+ 
++from configHandler import idleConf
++
++if idleConf.GetOption('main', 'General', 'use-ttk', type='int'):
++    from ttk import Frame, Label
++
+ class MultiStatusBar(Frame):
+ 
+     def __init__(self, master=None, **kw):
+@@ -10,7 +16,7 @@
+ 
+     def set_label(self, name, text='', side=LEFT):
+         if not self.labels.has_key(name):
+-            label = Label(self, bd=1, relief=SUNKEN, anchor=W)
++            label = Label(self, relief=SUNKEN, anchor=W)
+             label.pack(side=side)
+             self.labels[name] = label
+         else:
+@@ -18,6 +24,8 @@
+         label.config(text=text)
+ 
+ def _test():
++    from Tkinter import Text
++    from Tkconstants import TOP, BOTTOM, X
+     b = Frame()
+     c = Text(b)
+     c.pack(side=TOP)
+
+Property changes on: .
+___________________________________________________________________
+Name: svnmerge-integrated
+   + /python/trunk/Lib/idlelib:1-63994
+


More information about the Python-checkins mailing list