blob: 9d7563138898e769e2815aef63d1441900725355 [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
Ned Deilyb7601672014-03-27 20:49:14 -07008_tk_type = None
9
10def _initializeTkVariantTests(root):
11 """
12 Initializes OS X Tk variant values for
13 isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
14 """
15 global _tk_type
16 if sys.platform == 'darwin':
17 ws = root.tk.call('tk', 'windowingsystem')
18 if 'x11' in ws:
19 _tk_type = "xquartz"
20 elif 'aqua' not in ws:
21 _tk_type = "other"
22 elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
23 _tk_type = "cocoa"
24 else:
25 _tk_type = "carbon"
26 else:
27 _tk_type = "other"
28
29def isAquaTk():
30 """
31 Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
32 """
33 assert _tk_type is not None
34 return _tk_type == "cocoa" or _tk_type == "carbon"
35
36def isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +000037 """
38 Returns True if IDLE is using a Carbon Aqua Tk (instead of the
39 newer Cocoa Aqua Tk).
40 """
Ned Deilyb7601672014-03-27 20:49:14 -070041 assert _tk_type is not None
42 return _tk_type == "carbon"
43
44def isCocoaTk():
45 """
46 Returns True if IDLE is using a Cocoa Aqua Tk.
47 """
48 assert _tk_type is not None
49 return _tk_type == "cocoa"
50
51def isXQuartz():
52 """
53 Returns True if IDLE is using an OS X X11 Tk.
54 """
55 assert _tk_type is not None
56 return _tk_type == "xquartz"
Georg Brandlaedd2892010-12-19 10:10:32 +000057
Ned Deily4ce92b22011-01-15 04:37:12 +000058def tkVersionWarning(root):
59 """
60 Returns a string warning message if the Tk version in use appears to
Ned Deilyc556c642012-07-30 03:31:21 -070061 be one known to cause problems with IDLE.
62 1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable.
63 2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but
64 can still crash unexpectedly.
Ned Deily4ce92b22011-01-15 04:37:12 +000065 """
66
Ned Deilyb7601672014-03-27 20:49:14 -070067 if isCocoaTk():
Ned Deilyc556c642012-07-30 03:31:21 -070068 patchlevel = root.tk.call('info', 'patchlevel')
69 if patchlevel not in ('8.5.7', '8.5.9'):
70 return False
71 return (r"WARNING: The version of Tcl/Tk ({0}) in use may"
Ned Deily4ce92b22011-01-15 04:37:12 +000072 r" be unstable.\n"
73 r"Visit http://www.python.org/download/mac/tcltk/"
Ned Deilyc556c642012-07-30 03:31:21 -070074 r" for current information.".format(patchlevel))
Ned Deily4ce92b22011-01-15 04:37:12 +000075 else:
76 return False
77
Thomas Wouters0e3f5912006-08-11 14:57:12 +000078def addOpenEventSupport(root, flist):
79 """
Ezio Melotti13925002011-03-16 11:05:33 +020080 This ensures that the application will respond to open AppleEvents, which
81 makes is feasible to use IDLE as the default application for python files.
Thomas Wouters0e3f5912006-08-11 14:57:12 +000082 """
83 def doOpenFile(*args):
84 for fn in args:
85 flist.open(fn)
86
87 # The command below is a hook in aquatk that is called whenever the app
88 # receives a file open event. The callback can have multiple arguments,
89 # one for every file that should be opened.
90 root.createcommand("::tk::mac::OpenDocument", doOpenFile)
91
92def hideTkConsole(root):
Guido van Rossumb5a755e2007-07-18 18:15:48 +000093 try:
94 root.tk.call('console', 'hide')
Georg Brandl14fc4272008-05-17 18:39:55 +000095 except tkinter.TclError:
Guido van Rossumb5a755e2007-07-18 18:15:48 +000096 # Some versions of the Tk framework don't have a console object
97 pass
Thomas Wouters0e3f5912006-08-11 14:57:12 +000098
99def overrideRootMenu(root, flist):
100 """
Ned Deilyb7601672014-03-27 20:49:14 -0700101 Replace the Tk root menu by something that is more appropriate for
102 IDLE with an Aqua Tk.
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000103 """
104 # The menu that is attached to the Tk root (".") is also used by AquaTk for
105 # all windows that don't specify a menu of their own. The default menubar
106 # contains a number of menus, none of which are appropriate for IDLE. The
107 # Most annoying of those is an 'About Tck/Tk...' menu in the application
108 # menu.
109 #
110 # This function replaces the default menubar by a mostly empty one, it
111 # should only contain the correct application menu and the window menu.
112 #
113 # Due to a (mis-)feature of TkAqua the user will also see an empty Help
114 # menu.
Terry Jan Reedy038c16b2015-05-15 23:03:17 -0400115 from tkinter import Menu
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400116 from idlelib import mainmenu
117 from idlelib import windows
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000118
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400119 closeItem = mainmenu.menudefs[0][1][-2]
Ned Deilyb7601672014-03-27 20:49:14 -0700120
121 # Remove the last 3 items of the file menu: a separator, close window and
122 # quit. Close window will be reinserted just above the save item, where
123 # it should be according to the HIG. Quit is in the application menu.
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400124 del mainmenu.menudefs[0][1][-3:]
125 mainmenu.menudefs[0][1].insert(6, closeItem)
Ned Deilyb7601672014-03-27 20:49:14 -0700126
127 # Remove the 'About' entry from the help menu, it is in the application
128 # menu
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400129 del mainmenu.menudefs[-1][1][0:2]
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400130 # Remove the 'Configure Idle' entry from the options menu, it is in the
Ned Deilyb7601672014-03-27 20:49:14 -0700131 # application menu as 'Preferences'
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400132 del mainmenu.menudefs[-2][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000133 menubar = Menu(root)
134 root.configure(menu=menubar)
135 menudict = {}
136
Terry Jan Reedy30f1f672015-07-30 16:44:22 -0400137 menudict['windows'] = menu = Menu(menubar, name='windows', tearoff=0)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000138 menubar.add_cascade(label='Window', menu=menu, underline=0)
139
140 def postwindowsmenu(menu=menu):
141 end = menu.index('end')
142 if end is None:
143 end = -1
144
145 if end > 0:
146 menu.delete(0, end)
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400147 windows.add_windows_to_menu(menu)
Ned Deily622b2f62016-06-03 17:50:44 -0700148 windows.register_callback(postwindowsmenu)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000149
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000150 def about_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400151 "Handle Help 'About IDLE' event."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400152 # Synchronize with editor.EditorWindow.about_dialog.
153 from idlelib import help_about
154 help_about.AboutDialog(root, 'About IDLE')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000155
156 def config_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400157 "Handle Options 'Configure IDLE' event."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400158 # Synchronize with editor.EditorWindow.config_dialog.
159 from idlelib import configdialog
Ronald Oussoren220a9fb2009-05-26 18:41:00 +0000160
161 # Ensure that the root object has an instance_dict attribute,
162 # mirrors code in EditorWindow (although that sets the attribute
163 # on an EditorWindow instance that is then passed as the first
164 # argument to ConfigDialog)
165 root.instance_dict = flist.inversedict
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400166 configdialog.ConfigDialog(root, 'Settings')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000167
Georg Brandlaedd2892010-12-19 10:10:32 +0000168 def help_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400169 "Handle Help 'IDLE Help' event."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400170 # Synchronize with editor.EditorWindow.help_dialog.
Terry Jan Reedy5d46ab12015-09-20 19:57:13 -0400171 from idlelib import help
172 help.show_idlehelp(root)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000173
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000174 root.bind('<<about-idle>>', about_dialog)
175 root.bind('<<open-config-dialog>>', config_dialog)
Georg Brandlaedd2892010-12-19 10:10:32 +0000176 root.createcommand('::tk::mac::ShowPreferences', config_dialog)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000177 if flist:
178 root.bind('<<close-all-windows>>', flist.close_all_callback)
179
Ronald Oussoren10e05e12010-12-07 15:28:10 +0000180 # The binding above doesn't reliably work on all versions of Tk
181 # on MacOSX. Adding command definition below does seem to do the
182 # right thing for now.
183 root.createcommand('exit', flist.close_all_callback)
184
Ned Deilyb7601672014-03-27 20:49:14 -0700185 if isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000186 # for Carbon AquaTk, replace the default Tk apple menu
Terry Jan Reedy30f1f672015-07-30 16:44:22 -0400187 menudict['application'] = menu = Menu(menubar, name='apple',
188 tearoff=0)
Georg Brandlaedd2892010-12-19 10:10:32 +0000189 menubar.add_cascade(label='IDLE', menu=menu)
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400190 mainmenu.menudefs.insert(0,
Georg Brandlaedd2892010-12-19 10:10:32 +0000191 ('application', [
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000192 ('About IDLE', '<<about-idle>>'),
Georg Brandlaedd2892010-12-19 10:10:32 +0000193 None,
194 ]))
195 tkversion = root.tk.eval('info patchlevel')
196 if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
197 # for earlier AquaTk versions, supply a Preferences menu item
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400198 mainmenu.menudefs[0][1].append(
Georg Brandlaedd2892010-12-19 10:10:32 +0000199 ('_Preferences....', '<<open-config-dialog>>'),
200 )
Ned Deilyb7601672014-03-27 20:49:14 -0700201 if isCocoaTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000202 # replace default About dialog with About IDLE one
203 root.createcommand('tkAboutDialog', about_dialog)
204 # replace default "Help" item in Help menu
205 root.createcommand('::tk::mac::ShowHelp', help_dialog)
206 # remove redundant "IDLE Help" from menu
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400207 del mainmenu.menudefs[-1][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000208
Terry Jan Reedy24f3a182016-06-08 14:37:05 -0400209def fixb2context(root):
210 '''Removed bad AquaTk Button-2 (right) and Paste bindings.
211
212 They prevent context menu access and seem to be gone in AquaTk8.6.
213 See issue #24801.
214 '''
215 root.unbind_class('Text', '<B2>')
216 root.unbind_class('Text', '<B2-Motion>')
217 root.unbind_class('Text', '<<PasteSelection>>')
218
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)
Terry Jan Reedy24f3a182016-06-08 14:37:05 -0400240 fixb2context()