macosxSupport.py revision fa002d4cdd3a44a07252857ec7a55ed27fb15648
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        from idlelib import aboutDialog
165        aboutDialog.AboutDialog(root, 'About IDLE')
166
167    def config_dialog(event=None):
168        from idlelib import configDialog
169        root.instance_dict = flist.inversedict
170        configDialog.ConfigDialog(root, 'Settings')
171
172    def help_dialog(event=None):
173        from idlelib import textView
174        fn = path.join(path.abspath(path.dirname(__file__)), 'help.txt')
175        textView.view_file(root, 'Help', fn)
176
177    root.bind('<<about-idle>>', about_dialog)
178    root.bind('<<open-config-dialog>>', config_dialog)
179    root.createcommand('::tk::mac::ShowPreferences', config_dialog)
180    if flist:
181        root.bind('<<close-all-windows>>', flist.close_all_callback)
182
183        # The binding above doesn't reliably work on all versions of Tk
184        # on MacOSX. Adding command definition below does seem to do the
185        # right thing for now.
186        root.createcommand('exit', flist.close_all_callback)
187
188    if isCarbonTk():
189        # for Carbon AquaTk, replace the default Tk apple menu
190        menudict['application'] = menu = Menu(menubar, name='apple',
191                                              tearoff=0)
192        menubar.add_cascade(label='IDLE', menu=menu)
193        Bindings.menudefs.insert(0,
194            ('application', [
195                ('About IDLE', '<<about-idle>>'),
196                    None,
197                ]))
198        tkversion = root.tk.eval('info patchlevel')
199        if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
200            # for earlier AquaTk versions, supply a Preferences menu item
201            Bindings.menudefs[0][1].append(
202                    ('_Preferences....', '<<open-config-dialog>>'),
203                )
204    if isCocoaTk():
205        # replace default About dialog with About IDLE one
206        root.createcommand('tkAboutDialog', about_dialog)
207        # replace default "Help" item in Help menu
208        root.createcommand('::tk::mac::ShowHelp', help_dialog)
209        # remove redundant "IDLE Help" from menu
210        del Bindings.menudefs[-1][1][0]
211
212def setupApp(root, flist):
213    """
214    Perform initial OS X customizations if needed.
215    Called from PyShell.main() after initial calls to Tk()
216
217    There are currently three major versions of Tk in use on OS X:
218        1. Aqua Cocoa Tk (native default since OS X 10.6)
219        2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
220        3. X11 (supported by some third-party distributors, deprecated)
221    There are various differences among the three that affect IDLE
222    behavior, primarily with menus, mouse key events, and accelerators.
223    Some one-time customizations are performed here.
224    Others are dynamically tested throughout idlelib by calls to the
225    isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
226    are initialized here as well.
227    """
228    _initializeTkVariantTests(root)
229    if isAquaTk():
230        hideTkConsole(root)
231        overrideRootMenu(root, flist)
232        addOpenEventSupport(root, flist)
233