1"""
2A number of functions that enhance IDLE on Mac OSX.
3"""
4import sys
5import Tkinter
6from os import path
7
8
9import warnings
10
11def runningAsOSXApp():
12    warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()",
13                        DeprecationWarning, stacklevel=2)
14    return isAquaTk()
15
16def isCarbonAquaTk(root):
17    warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()",
18                        DeprecationWarning, stacklevel=2)
19    return isCarbonTk()
20
21_tk_type = None
22
23def _initializeTkVariantTests(root):
24    """
25    Initializes OS X Tk variant values for
26    isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
27    """
28    global _tk_type
29    if sys.platform == 'darwin':
30        ws = root.tk.call('tk', 'windowingsystem')
31        if 'x11' in ws:
32            _tk_type = "xquartz"
33        elif 'aqua' not in ws:
34            _tk_type = "other"
35        elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
36            _tk_type = "cocoa"
37        else:
38            _tk_type = "carbon"
39    else:
40        _tk_type = "other"
41
42def isAquaTk():
43    """
44    Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
45    """
46    assert _tk_type is not None
47    return _tk_type == "cocoa" or _tk_type == "carbon"
48
49def isCarbonTk():
50    """
51    Returns True if IDLE is using a Carbon Aqua Tk (instead of the
52    newer Cocoa Aqua Tk).
53    """
54    assert _tk_type is not None
55    return _tk_type == "carbon"
56
57def isCocoaTk():
58    """
59    Returns True if IDLE is using a Cocoa Aqua Tk.
60    """
61    assert _tk_type is not None
62    return _tk_type == "cocoa"
63
64def isXQuartz():
65    """
66    Returns True if IDLE is using an OS X X11 Tk.
67    """
68    assert _tk_type is not None
69    return _tk_type == "xquartz"
70
71def tkVersionWarning(root):
72    """
73    Returns a string warning message if the Tk version in use appears to
74    be one known to cause problems with IDLE.
75    1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable.
76    2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but
77        can still crash unexpectedly.
78    """
79
80    if isCocoaTk():
81        patchlevel = root.tk.call('info', 'patchlevel')
82        if patchlevel not in ('8.5.7', '8.5.9'):
83            return False
84        return (r"WARNING: The version of Tcl/Tk ({0}) in use may"
85                r" be unstable.\n"
86                r"Visit http://www.python.org/download/mac/tcltk/"
87                r" for current information.".format(patchlevel))
88    else:
89        return False
90
91def addOpenEventSupport(root, flist):
92    """
93    This ensures that the application will respond to open AppleEvents, which
94    makes is feasible to use IDLE as the default application for python files.
95    """
96    def doOpenFile(*args):
97        for fn in args:
98            flist.open(fn)
99
100    # The command below is a hook in aquatk that is called whenever the app
101    # receives a file open event. The callback can have multiple arguments,
102    # one for every file that should be opened.
103    root.createcommand("::tk::mac::OpenDocument", doOpenFile)
104
105def hideTkConsole(root):
106    try:
107        root.tk.call('console', 'hide')
108    except Tkinter.TclError:
109        # Some versions of the Tk framework don't have a console object
110        pass
111
112def overrideRootMenu(root, flist):
113    """
114    Replace the Tk root menu by something that is more appropriate for
115    IDLE with an Aqua Tk.
116    """
117    # The menu that is attached to the Tk root (".") is also used by AquaTk for
118    # all windows that don't specify a menu of their own. The default menubar
119    # contains a number of menus, none of which are appropriate for IDLE. The
120    # Most annoying of those is an 'About Tck/Tk...' menu in the application
121    # menu.
122    #
123    # This function replaces the default menubar by a mostly empty one, it
124    # should only contain the correct application menu and the window menu.
125    #
126    # Due to a (mis-)feature of TkAqua the user will also see an empty Help
127    # menu.
128    from Tkinter import Menu
129    from idlelib import Bindings
130    from idlelib import WindowList
131
132    closeItem = Bindings.menudefs[0][1][-2]
133
134    # Remove the last 3 items of the file menu: a separator, close window and
135    # quit. Close window will be reinserted just above the save item, where
136    # it should be according to the HIG. Quit is in the application menu.
137    del Bindings.menudefs[0][1][-3:]
138    Bindings.menudefs[0][1].insert(6, closeItem)
139
140    # Remove the 'About' entry from the help menu, it is in the application
141    # menu
142    del Bindings.menudefs[-1][1][0:2]
143    # Remove the 'Configure Idle' entry from the options menu, it is in the
144    # application menu as 'Preferences'
145    del Bindings.menudefs[-2][1][0]
146    menubar = Menu(root)
147    root.configure(menu=menubar)
148    menudict = {}
149
150    menudict['windows'] = menu = Menu(menubar, name='windows', tearoff=0)
151    menubar.add_cascade(label='Window', menu=menu, underline=0)
152
153    def postwindowsmenu(menu=menu):
154        end = menu.index('end')
155        if end is None:
156            end = -1
157
158        if end > 0:
159            menu.delete(0, end)
160        WindowList.add_windows_to_menu(menu)
161    WindowList.register_callback(postwindowsmenu)
162
163    def about_dialog(event=None):
164        "Handle Help 'About IDLE' event."
165        # Synchronize with EditorWindow.EditorWindow.about_dialog.
166        from idlelib import aboutDialog
167        aboutDialog.AboutDialog(root, 'About IDLE')
168
169    def config_dialog(event=None):
170        "Handle Options 'Configure IDLE' event."
171        # Synchronize with EditorWindow.EditorWindow.config_dialog.
172        from idlelib import configDialog
173        root.instance_dict = flist.inversedict
174        configDialog.ConfigDialog(root, 'Settings')
175
176    def help_dialog(event=None):
177        "Handle Help 'IDLE Help' event."
178        # Synchronize with EditorWindow.EditorWindow.help_dialog.
179        from idlelib import help
180        help.show_idlehelp(root)
181
182    root.bind('<<about-idle>>', about_dialog)
183    root.bind('<<open-config-dialog>>', config_dialog)
184    root.createcommand('::tk::mac::ShowPreferences', config_dialog)
185    if flist:
186        root.bind('<<close-all-windows>>', flist.close_all_callback)
187
188        # The binding above doesn't reliably work on all versions of Tk
189        # on MacOSX. Adding command definition below does seem to do the
190        # right thing for now.
191        root.createcommand('exit', flist.close_all_callback)
192
193    if isCarbonTk():
194        # for Carbon AquaTk, replace the default Tk apple menu
195        menudict['application'] = menu = Menu(menubar, name='apple',
196                                              tearoff=0)
197        menubar.add_cascade(label='IDLE', menu=menu)
198        Bindings.menudefs.insert(0,
199            ('application', [
200                ('About IDLE', '<<about-idle>>'),
201                    None,
202                ]))
203        tkversion = root.tk.eval('info patchlevel')
204        if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
205            # for earlier AquaTk versions, supply a Preferences menu item
206            Bindings.menudefs[0][1].append(
207                    ('_Preferences....', '<<open-config-dialog>>'),
208                )
209    if isCocoaTk():
210        # replace default About dialog with About IDLE one
211        root.createcommand('tkAboutDialog', about_dialog)
212        # replace default "Help" item in Help menu
213        root.createcommand('::tk::mac::ShowHelp', help_dialog)
214        # remove redundant "IDLE Help" from menu
215        del Bindings.menudefs[-1][1][0]
216
217def setupApp(root, flist):
218    """
219    Perform initial OS X customizations if needed.
220    Called from PyShell.main() after initial calls to Tk()
221
222    There are currently three major versions of Tk in use on OS X:
223        1. Aqua Cocoa Tk (native default since OS X 10.6)
224        2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
225        3. X11 (supported by some third-party distributors, deprecated)
226    There are various differences among the three that affect IDLE
227    behavior, primarily with menus, mouse key events, and accelerators.
228    Some one-time customizations are performed here.
229    Others are dynamically tested throughout idlelib by calls to the
230    isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
231    are initialized here as well.
232    """
233    _initializeTkVariantTests(root)
234    if isAquaTk():
235        hideTkConsole(root)
236        overrideRootMenu(root, flist)
237        addOpenEventSupport(root, flist)
238