blob: 1e16f2c586f32d29aebc76734bbcac4059b2138c [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
Ned Deilyb7601672014-03-27 20:49:14 -07006import warnings
Ronald Oussoren6f6c5622009-12-24 14:03:19 +00007
Thomas Wouters0e3f5912006-08-11 14:57:12 +00008def runningAsOSXApp():
Ned Deilyb7601672014-03-27 20:49:14 -07009 warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()",
10 DeprecationWarning, stacklevel=2)
11 return isAquaTk()
Georg Brandlaedd2892010-12-19 10:10:32 +000012
13def isCarbonAquaTk(root):
Ned Deilyb7601672014-03-27 20:49:14 -070014 warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()",
15 DeprecationWarning, stacklevel=2)
16 return isCarbonTk()
17
18_tk_type = None
19
20def _initializeTkVariantTests(root):
21 """
22 Initializes OS X Tk variant values for
23 isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
24 """
25 global _tk_type
26 if sys.platform == 'darwin':
27 ws = root.tk.call('tk', 'windowingsystem')
28 if 'x11' in ws:
29 _tk_type = "xquartz"
30 elif 'aqua' not in ws:
31 _tk_type = "other"
32 elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
33 _tk_type = "cocoa"
34 else:
35 _tk_type = "carbon"
36 else:
37 _tk_type = "other"
38
39def isAquaTk():
40 """
41 Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
42 """
43 assert _tk_type is not None
44 return _tk_type == "cocoa" or _tk_type == "carbon"
45
46def isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +000047 """
48 Returns True if IDLE is using a Carbon Aqua Tk (instead of the
49 newer Cocoa Aqua Tk).
50 """
Ned Deilyb7601672014-03-27 20:49:14 -070051 assert _tk_type is not None
52 return _tk_type == "carbon"
53
54def isCocoaTk():
55 """
56 Returns True if IDLE is using a Cocoa Aqua Tk.
57 """
58 assert _tk_type is not None
59 return _tk_type == "cocoa"
60
61def isXQuartz():
62 """
63 Returns True if IDLE is using an OS X X11 Tk.
64 """
65 assert _tk_type is not None
66 return _tk_type == "xquartz"
Georg Brandlaedd2892010-12-19 10:10:32 +000067
Ned Deily4ce92b22011-01-15 04:37:12 +000068def tkVersionWarning(root):
69 """
70 Returns a string warning message if the Tk version in use appears to
Ned Deilyc556c642012-07-30 03:31:21 -070071 be one known to cause problems with IDLE.
72 1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable.
73 2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but
74 can still crash unexpectedly.
Ned Deily4ce92b22011-01-15 04:37:12 +000075 """
76
Ned Deilyb7601672014-03-27 20:49:14 -070077 if isCocoaTk():
Ned Deilyc556c642012-07-30 03:31:21 -070078 patchlevel = root.tk.call('info', 'patchlevel')
79 if patchlevel not in ('8.5.7', '8.5.9'):
80 return False
81 return (r"WARNING: The version of Tcl/Tk ({0}) in use may"
Ned Deily4ce92b22011-01-15 04:37:12 +000082 r" be unstable.\n"
83 r"Visit http://www.python.org/download/mac/tcltk/"
Ned Deilyc556c642012-07-30 03:31:21 -070084 r" for current information.".format(patchlevel))
Ned Deily4ce92b22011-01-15 04:37:12 +000085 else:
86 return False
87
Thomas Wouters0e3f5912006-08-11 14:57:12 +000088def addOpenEventSupport(root, flist):
89 """
Ezio Melotti13925002011-03-16 11:05:33 +020090 This ensures that the application will respond to open AppleEvents, which
91 makes is feasible to use IDLE as the default application for python files.
Thomas Wouters0e3f5912006-08-11 14:57:12 +000092 """
93 def doOpenFile(*args):
94 for fn in args:
95 flist.open(fn)
96
97 # The command below is a hook in aquatk that is called whenever the app
98 # receives a file open event. The callback can have multiple arguments,
99 # one for every file that should be opened.
100 root.createcommand("::tk::mac::OpenDocument", doOpenFile)
101
102def hideTkConsole(root):
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000103 try:
104 root.tk.call('console', 'hide')
Georg Brandl14fc4272008-05-17 18:39:55 +0000105 except tkinter.TclError:
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000106 # Some versions of the Tk framework don't have a console object
107 pass
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000108
109def overrideRootMenu(root, flist):
110 """
Ned Deilyb7601672014-03-27 20:49:14 -0700111 Replace the Tk root menu by something that is more appropriate for
112 IDLE with an Aqua Tk.
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000113 """
114 # The menu that is attached to the Tk root (".") is also used by AquaTk for
115 # all windows that don't specify a menu of their own. The default menubar
116 # contains a number of menus, none of which are appropriate for IDLE. The
117 # Most annoying of those is an 'About Tck/Tk...' menu in the application
118 # menu.
119 #
120 # This function replaces the default menubar by a mostly empty one, it
121 # should only contain the correct application menu and the window menu.
122 #
123 # Due to a (mis-)feature of TkAqua the user will also see an empty Help
124 # menu.
Terry Jan Reedy038c16b2015-05-15 23:03:17 -0400125 from tkinter import Menu
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400126 from idlelib import mainmenu
127 from idlelib import windows
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000128
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400129 closeItem = mainmenu.menudefs[0][1][-2]
Ned Deilyb7601672014-03-27 20:49:14 -0700130
131 # Remove the last 3 items of the file menu: a separator, close window and
132 # quit. Close window will be reinserted just above the save item, where
133 # it should be according to the HIG. Quit is in the application menu.
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400134 del mainmenu.menudefs[0][1][-3:]
135 mainmenu.menudefs[0][1].insert(6, closeItem)
Ned Deilyb7601672014-03-27 20:49:14 -0700136
137 # Remove the 'About' entry from the help menu, it is in the application
138 # menu
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400139 del mainmenu.menudefs[-1][1][0:2]
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400140 # Remove the 'Configure Idle' entry from the options menu, it is in the
Ned Deilyb7601672014-03-27 20:49:14 -0700141 # application menu as 'Preferences'
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400142 del mainmenu.menudefs[-2][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000143 menubar = Menu(root)
144 root.configure(menu=menubar)
145 menudict = {}
146
Terry Jan Reedy30f1f672015-07-30 16:44:22 -0400147 menudict['windows'] = menu = Menu(menubar, name='windows', tearoff=0)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000148 menubar.add_cascade(label='Window', menu=menu, underline=0)
149
150 def postwindowsmenu(menu=menu):
151 end = menu.index('end')
152 if end is None:
153 end = -1
154
155 if end > 0:
156 menu.delete(0, end)
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400157 windows.add_windows_to_menu(menu)
158 Windows.register_callback(postwindowsmenu)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000159
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000160 def about_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400161 "Handle Help 'About IDLE' event."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400162 # Synchronize with editor.EditorWindow.about_dialog.
163 from idlelib import help_about
164 help_about.AboutDialog(root, 'About IDLE')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000165
166 def config_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400167 "Handle Options 'Configure IDLE' event."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400168 # Synchronize with editor.EditorWindow.config_dialog.
169 from idlelib import configdialog
Ronald Oussoren220a9fb2009-05-26 18:41:00 +0000170
171 # Ensure that the root object has an instance_dict attribute,
172 # mirrors code in EditorWindow (although that sets the attribute
173 # on an EditorWindow instance that is then passed as the first
174 # argument to ConfigDialog)
175 root.instance_dict = flist.inversedict
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400176 configdialog.ConfigDialog(root, 'Settings')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000177
Georg Brandlaedd2892010-12-19 10:10:32 +0000178 def help_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400179 "Handle Help 'IDLE Help' event."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400180 # Synchronize with editor.EditorWindow.help_dialog.
Terry Jan Reedy5d46ab12015-09-20 19:57:13 -0400181 from idlelib import help
182 help.show_idlehelp(root)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000183
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000184 root.bind('<<about-idle>>', about_dialog)
185 root.bind('<<open-config-dialog>>', config_dialog)
Georg Brandlaedd2892010-12-19 10:10:32 +0000186 root.createcommand('::tk::mac::ShowPreferences', config_dialog)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000187 if flist:
188 root.bind('<<close-all-windows>>', flist.close_all_callback)
189
Ronald Oussoren10e05e12010-12-07 15:28:10 +0000190 # The binding above doesn't reliably work on all versions of Tk
191 # on MacOSX. Adding command definition below does seem to do the
192 # right thing for now.
193 root.createcommand('exit', flist.close_all_callback)
194
Ned Deilyb7601672014-03-27 20:49:14 -0700195 if isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000196 # for Carbon AquaTk, replace the default Tk apple menu
Terry Jan Reedy30f1f672015-07-30 16:44:22 -0400197 menudict['application'] = menu = Menu(menubar, name='apple',
198 tearoff=0)
Georg Brandlaedd2892010-12-19 10:10:32 +0000199 menubar.add_cascade(label='IDLE', menu=menu)
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400200 mainmenu.menudefs.insert(0,
Georg Brandlaedd2892010-12-19 10:10:32 +0000201 ('application', [
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000202 ('About IDLE', '<<about-idle>>'),
Georg Brandlaedd2892010-12-19 10:10:32 +0000203 None,
204 ]))
205 tkversion = root.tk.eval('info patchlevel')
206 if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
207 # for earlier AquaTk versions, supply a Preferences menu item
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400208 mainmenu.menudefs[0][1].append(
Georg Brandlaedd2892010-12-19 10:10:32 +0000209 ('_Preferences....', '<<open-config-dialog>>'),
210 )
Ned Deilyb7601672014-03-27 20:49:14 -0700211 if isCocoaTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000212 # replace default About dialog with About IDLE one
213 root.createcommand('tkAboutDialog', about_dialog)
214 # replace default "Help" item in Help menu
215 root.createcommand('::tk::mac::ShowHelp', help_dialog)
216 # remove redundant "IDLE Help" from menu
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400217 del mainmenu.menudefs[-1][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000218
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000219def setupApp(root, flist):
220 """
Ned Deilyb7601672014-03-27 20:49:14 -0700221 Perform initial OS X customizations if needed.
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400222 Called from pyshell.main() after initial calls to Tk()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000223
Ned Deilyb7601672014-03-27 20:49:14 -0700224 There are currently three major versions of Tk in use on OS X:
225 1. Aqua Cocoa Tk (native default since OS X 10.6)
226 2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
227 3. X11 (supported by some third-party distributors, deprecated)
228 There are various differences among the three that affect IDLE
229 behavior, primarily with menus, mouse key events, and accelerators.
230 Some one-time customizations are performed here.
231 Others are dynamically tested throughout idlelib by calls to the
232 isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
233 are initialized here as well.
234 """
235 _initializeTkVariantTests(root)
236 if isAquaTk():
237 hideTkConsole(root)
238 overrideRootMenu(root, flist)
239 addOpenEventSupport(root, flist)