blob: b96bae1d55086bb53e5d381b34ce1dd4586a69a1 [file] [log] [blame]
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001"""
Ned Deilyb7601672014-03-27 20:49:14 -07002A number of functions that enhance IDLE on Mac OSX.
Thomas Wouters0e3f5912006-08-11 14:57:12 +00003"""
4import sys
Georg Brandl14fc4272008-05-17 18:39:55 +00005import tkinter
Georg Brandlaedd2892010-12-19 10:10:32 +00006from os import path
Ned Deilyb7601672014-03-27 20:49:14 -07007import warnings
Ronald Oussoren6f6c5622009-12-24 14:03:19 +00008
Thomas Wouters0e3f5912006-08-11 14:57:12 +00009def runningAsOSXApp():
Ned Deilyb7601672014-03-27 20:49:14 -070010 warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()",
11 DeprecationWarning, stacklevel=2)
12 return isAquaTk()
Georg Brandlaedd2892010-12-19 10:10:32 +000013
14def isCarbonAquaTk(root):
Ned Deilyb7601672014-03-27 20:49:14 -070015 warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()",
16 DeprecationWarning, stacklevel=2)
17 return isCarbonTk()
18
19_tk_type = None
20
21def _initializeTkVariantTests(root):
22 """
23 Initializes OS X Tk variant values for
24 isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
25 """
26 global _tk_type
27 if sys.platform == 'darwin':
28 ws = root.tk.call('tk', 'windowingsystem')
29 if 'x11' in ws:
30 _tk_type = "xquartz"
31 elif 'aqua' not in ws:
32 _tk_type = "other"
33 elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
34 _tk_type = "cocoa"
35 else:
36 _tk_type = "carbon"
37 else:
38 _tk_type = "other"
39
40def isAquaTk():
41 """
42 Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
43 """
44 assert _tk_type is not None
45 return _tk_type == "cocoa" or _tk_type == "carbon"
46
47def isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +000048 """
49 Returns True if IDLE is using a Carbon Aqua Tk (instead of the
50 newer Cocoa Aqua Tk).
51 """
Ned Deilyb7601672014-03-27 20:49:14 -070052 assert _tk_type is not None
53 return _tk_type == "carbon"
54
55def isCocoaTk():
56 """
57 Returns True if IDLE is using a Cocoa Aqua Tk.
58 """
59 assert _tk_type is not None
60 return _tk_type == "cocoa"
61
62def isXQuartz():
63 """
64 Returns True if IDLE is using an OS X X11 Tk.
65 """
66 assert _tk_type is not None
67 return _tk_type == "xquartz"
Georg Brandlaedd2892010-12-19 10:10:32 +000068
Ned Deily4ce92b22011-01-15 04:37:12 +000069def tkVersionWarning(root):
70 """
71 Returns a string warning message if the Tk version in use appears to
Ned Deilyc556c642012-07-30 03:31:21 -070072 be one known to cause problems with IDLE.
73 1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable.
74 2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but
75 can still crash unexpectedly.
Ned Deily4ce92b22011-01-15 04:37:12 +000076 """
77
Ned Deilyb7601672014-03-27 20:49:14 -070078 if isCocoaTk():
Ned Deilyc556c642012-07-30 03:31:21 -070079 patchlevel = root.tk.call('info', 'patchlevel')
80 if patchlevel not in ('8.5.7', '8.5.9'):
81 return False
82 return (r"WARNING: The version of Tcl/Tk ({0}) in use may"
Ned Deily4ce92b22011-01-15 04:37:12 +000083 r" be unstable.\n"
84 r"Visit http://www.python.org/download/mac/tcltk/"
Ned Deilyc556c642012-07-30 03:31:21 -070085 r" for current information.".format(patchlevel))
Ned Deily4ce92b22011-01-15 04:37:12 +000086 else:
87 return False
88
Thomas Wouters0e3f5912006-08-11 14:57:12 +000089def addOpenEventSupport(root, flist):
90 """
Ezio Melotti13925002011-03-16 11:05:33 +020091 This ensures that the application will respond to open AppleEvents, which
92 makes is feasible to use IDLE as the default application for python files.
Thomas Wouters0e3f5912006-08-11 14:57:12 +000093 """
94 def doOpenFile(*args):
95 for fn in args:
96 flist.open(fn)
97
98 # The command below is a hook in aquatk that is called whenever the app
99 # receives a file open event. The callback can have multiple arguments,
100 # one for every file that should be opened.
101 root.createcommand("::tk::mac::OpenDocument", doOpenFile)
102
103def hideTkConsole(root):
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000104 try:
105 root.tk.call('console', 'hide')
Georg Brandl14fc4272008-05-17 18:39:55 +0000106 except tkinter.TclError:
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000107 # Some versions of the Tk framework don't have a console object
108 pass
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000109
110def overrideRootMenu(root, flist):
111 """
Ned Deilyb7601672014-03-27 20:49:14 -0700112 Replace the Tk root menu by something that is more appropriate for
113 IDLE with an Aqua Tk.
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000114 """
115 # The menu that is attached to the Tk root (".") is also used by AquaTk for
116 # all windows that don't specify a menu of their own. The default menubar
117 # contains a number of menus, none of which are appropriate for IDLE. The
118 # Most annoying of those is an 'About Tck/Tk...' menu in the application
119 # menu.
120 #
121 # This function replaces the default menubar by a mostly empty one, it
122 # should only contain the correct application menu and the window menu.
123 #
124 # Due to a (mis-)feature of TkAqua the user will also see an empty Help
125 # menu.
Terry Jan Reedy038c16b2015-05-15 23:03:17 -0400126 from tkinter import Menu
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000127 from idlelib import Bindings
128 from idlelib import WindowList
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000129
Ned Deilyb7601672014-03-27 20:49:14 -0700130 closeItem = Bindings.menudefs[0][1][-2]
131
132 # Remove the last 3 items of the file menu: a separator, close window and
133 # quit. Close window will be reinserted just above the save item, where
134 # it should be according to the HIG. Quit is in the application menu.
135 del Bindings.menudefs[0][1][-3:]
136 Bindings.menudefs[0][1].insert(6, closeItem)
137
138 # Remove the 'About' entry from the help menu, it is in the application
139 # menu
140 del Bindings.menudefs[-1][1][0:2]
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400141 # Remove the 'Configure Idle' entry from the options menu, it is in the
Ned Deilyb7601672014-03-27 20:49:14 -0700142 # application menu as 'Preferences'
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400143 del Bindings.menudefs[-2][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000144 menubar = Menu(root)
145 root.configure(menu=menubar)
146 menudict = {}
147
Terry Jan Reedy30f1f672015-07-30 16:44:22 -0400148 menudict['windows'] = menu = Menu(menubar, name='windows', tearoff=0)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000149 menubar.add_cascade(label='Window', menu=menu, underline=0)
150
151 def postwindowsmenu(menu=menu):
152 end = menu.index('end')
153 if end is None:
154 end = -1
155
156 if end > 0:
157 menu.delete(0, end)
158 WindowList.add_windows_to_menu(menu)
159 WindowList.register_callback(postwindowsmenu)
160
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000161 def about_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400162 "Handle Help 'About IDLE' event."
163 # Synchronize with EditorWindow.EditorWindow.about_dialog.
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000164 from idlelib import aboutDialog
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000165 aboutDialog.AboutDialog(root, 'About IDLE')
166
167 def config_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400168 "Handle Options 'Configure IDLE' event."
169 # Synchronize with EditorWindow.EditorWindow.config_dialog.
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000170 from idlelib import configDialog
Ronald Oussoren220a9fb2009-05-26 18:41:00 +0000171
172 # Ensure that the root object has an instance_dict attribute,
173 # mirrors code in EditorWindow (although that sets the attribute
174 # on an EditorWindow instance that is then passed as the first
175 # argument to ConfigDialog)
176 root.instance_dict = flist.inversedict
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000177 configDialog.ConfigDialog(root, 'Settings')
178
Georg Brandlaedd2892010-12-19 10:10:32 +0000179 def help_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400180 "Handle Help 'IDLE Help' event."
181 # Synchronize with EditorWindow.EditorWindow.help_dialog.
Terry Jan Reedy5d46ab12015-09-20 19:57:13 -0400182 from idlelib import help
183 help.show_idlehelp(root)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000184
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000185 root.bind('<<about-idle>>', about_dialog)
186 root.bind('<<open-config-dialog>>', config_dialog)
Georg Brandlaedd2892010-12-19 10:10:32 +0000187 root.createcommand('::tk::mac::ShowPreferences', config_dialog)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000188 if flist:
189 root.bind('<<close-all-windows>>', flist.close_all_callback)
190
Ronald Oussoren10e05e12010-12-07 15:28:10 +0000191 # The binding above doesn't reliably work on all versions of Tk
192 # on MacOSX. Adding command definition below does seem to do the
193 # right thing for now.
194 root.createcommand('exit', flist.close_all_callback)
195
Ned Deilyb7601672014-03-27 20:49:14 -0700196 if isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000197 # for Carbon AquaTk, replace the default Tk apple menu
Terry Jan Reedy30f1f672015-07-30 16:44:22 -0400198 menudict['application'] = menu = Menu(menubar, name='apple',
199 tearoff=0)
Georg Brandlaedd2892010-12-19 10:10:32 +0000200 menubar.add_cascade(label='IDLE', menu=menu)
201 Bindings.menudefs.insert(0,
202 ('application', [
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000203 ('About IDLE', '<<about-idle>>'),
Georg Brandlaedd2892010-12-19 10:10:32 +0000204 None,
205 ]))
206 tkversion = root.tk.eval('info patchlevel')
207 if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
208 # for earlier AquaTk versions, supply a Preferences menu item
209 Bindings.menudefs[0][1].append(
210 ('_Preferences....', '<<open-config-dialog>>'),
211 )
Ned Deilyb7601672014-03-27 20:49:14 -0700212 if isCocoaTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000213 # replace default About dialog with About IDLE one
214 root.createcommand('tkAboutDialog', about_dialog)
215 # replace default "Help" item in Help menu
216 root.createcommand('::tk::mac::ShowHelp', help_dialog)
217 # remove redundant "IDLE Help" from menu
218 del Bindings.menudefs[-1][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000219
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000220def setupApp(root, flist):
221 """
Ned Deilyb7601672014-03-27 20:49:14 -0700222 Perform initial OS X customizations if needed.
223 Called from PyShell.main() after initial calls to Tk()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000224
Ned Deilyb7601672014-03-27 20:49:14 -0700225 There are currently three major versions of Tk in use on OS X:
226 1. Aqua Cocoa Tk (native default since OS X 10.6)
227 2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
228 3. X11 (supported by some third-party distributors, deprecated)
229 There are various differences among the three that affect IDLE
230 behavior, primarily with menus, mouse key events, and accelerators.
231 Some one-time customizations are performed here.
232 Others are dynamically tested throughout idlelib by calls to the
233 isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
234 are initialized here as well.
235 """
236 _initializeTkVariantTests(root)
237 if isAquaTk():
238 hideTkConsole(root)
239 overrideRootMenu(root, flist)
240 addOpenEventSupport(root, flist)