blob: a2260af6e753a38e865509b475bcf3e4ecf5046f [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"""
Terry Jan Reedy2518fa82016-06-12 15:49:20 -04004from sys import platform # Used in _init_tk_type, changed by test.
Ronald Oussoren6f6c5622009-12-24 14:03:19 +00005
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -04006import tkinter
7
Terry Jan Reedy2518fa82016-06-12 15:49:20 -04008
9## Define functions that query the Mac graphics type.
10## _tk_type and its initializer are private to this section.
11
Ned Deilyb7601672014-03-27 20:49:14 -070012_tk_type = None
13
Terry Jan Reedy2518fa82016-06-12 15:49:20 -040014def _init_tk_type():
Ned Deilyb7601672014-03-27 20:49:14 -070015 """
16 Initializes OS X Tk variant values for
17 isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
18 """
19 global _tk_type
Terry Jan Reedy2518fa82016-06-12 15:49:20 -040020 if platform == 'darwin':
21 root = tkinter.Tk()
Ned Deilyb7601672014-03-27 20:49:14 -070022 ws = root.tk.call('tk', 'windowingsystem')
23 if 'x11' in ws:
24 _tk_type = "xquartz"
25 elif 'aqua' not in ws:
26 _tk_type = "other"
27 elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
28 _tk_type = "cocoa"
29 else:
30 _tk_type = "carbon"
Terry Jan Reedy2518fa82016-06-12 15:49:20 -040031 root.destroy()
Ned Deilyb7601672014-03-27 20:49:14 -070032 else:
33 _tk_type = "other"
34
35def isAquaTk():
36 """
37 Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
38 """
Terry Jan Reedyfb51e652016-06-08 18:09:22 -040039 if not _tk_type:
40 _init_tk_type()
Ned Deilyb7601672014-03-27 20:49:14 -070041 return _tk_type == "cocoa" or _tk_type == "carbon"
42
43def isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +000044 """
45 Returns True if IDLE is using a Carbon Aqua Tk (instead of the
46 newer Cocoa Aqua Tk).
47 """
Terry Jan Reedyfb51e652016-06-08 18:09:22 -040048 if not _tk_type:
49 _init_tk_type()
Ned Deilyb7601672014-03-27 20:49:14 -070050 return _tk_type == "carbon"
51
52def isCocoaTk():
53 """
54 Returns True if IDLE is using a Cocoa Aqua Tk.
55 """
Terry Jan Reedyfb51e652016-06-08 18:09:22 -040056 if not _tk_type:
57 _init_tk_type()
Ned Deilyb7601672014-03-27 20:49:14 -070058 return _tk_type == "cocoa"
59
60def isXQuartz():
61 """
62 Returns True if IDLE is using an OS X X11 Tk.
63 """
Terry Jan Reedyfb51e652016-06-08 18:09:22 -040064 if not _tk_type:
65 _init_tk_type()
Ned Deilyb7601672014-03-27 20:49:14 -070066 return _tk_type == "xquartz"
Georg Brandlaedd2892010-12-19 10:10:32 +000067
Terry Jan Reedy2518fa82016-06-12 15:49:20 -040068
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
Terry Jan Reedy2518fa82016-06-12 15:49:20 -040089
90## Fix the menu and related functions.
91
Thomas Wouters0e3f5912006-08-11 14:57:12 +000092def addOpenEventSupport(root, flist):
93 """
Ezio Melotti13925002011-03-16 11:05:33 +020094 This ensures that the application will respond to open AppleEvents, which
95 makes is feasible to use IDLE as the default application for python files.
Thomas Wouters0e3f5912006-08-11 14:57:12 +000096 """
97 def doOpenFile(*args):
98 for fn in args:
99 flist.open(fn)
100
101 # The command below is a hook in aquatk that is called whenever the app
102 # receives a file open event. The callback can have multiple arguments,
103 # one for every file that should be opened.
104 root.createcommand("::tk::mac::OpenDocument", doOpenFile)
105
106def hideTkConsole(root):
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000107 try:
108 root.tk.call('console', 'hide')
Georg Brandl14fc4272008-05-17 18:39:55 +0000109 except tkinter.TclError:
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000110 # Some versions of the Tk framework don't have a console object
111 pass
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000112
113def overrideRootMenu(root, flist):
114 """
Ned Deilyb7601672014-03-27 20:49:14 -0700115 Replace the Tk root menu by something that is more appropriate for
116 IDLE with an Aqua Tk.
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000117 """
118 # The menu that is attached to the Tk root (".") is also used by AquaTk for
119 # all windows that don't specify a menu of their own. The default menubar
120 # contains a number of menus, none of which are appropriate for IDLE. The
121 # Most annoying of those is an 'About Tck/Tk...' menu in the application
122 # menu.
123 #
124 # This function replaces the default menubar by a mostly empty one, it
125 # should only contain the correct application menu and the window menu.
126 #
127 # Due to a (mis-)feature of TkAqua the user will also see an empty Help
128 # menu.
Terry Jan Reedy038c16b2015-05-15 23:03:17 -0400129 from tkinter import Menu
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400130 from idlelib import mainmenu
Terry Jan Reedya361e892018-06-20 21:25:59 -0400131 from idlelib import window
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000132
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400133 closeItem = mainmenu.menudefs[0][1][-2]
Ned Deilyb7601672014-03-27 20:49:14 -0700134
135 # Remove the last 3 items of the file menu: a separator, close window and
136 # quit. Close window will be reinserted just above the save item, where
137 # it should be according to the HIG. Quit is in the application menu.
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400138 del mainmenu.menudefs[0][1][-3:]
139 mainmenu.menudefs[0][1].insert(6, closeItem)
Ned Deilyb7601672014-03-27 20:49:14 -0700140
141 # Remove the 'About' entry from the help menu, it is in the application
142 # menu
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400143 del mainmenu.menudefs[-1][1][0:2]
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400144 # Remove the 'Configure Idle' entry from the options menu, it is in the
Ned Deilyb7601672014-03-27 20:49:14 -0700145 # application menu as 'Preferences'
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400146 del mainmenu.menudefs[-2][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000147 menubar = Menu(root)
148 root.configure(menu=menubar)
149 menudict = {}
150
Terry Jan Reedy33c74202018-06-20 22:49:55 -0400151 menudict['window'] = menu = Menu(menubar, name='window', tearoff=0)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000152 menubar.add_cascade(label='Window', menu=menu, underline=0)
153
154 def postwindowsmenu(menu=menu):
155 end = menu.index('end')
156 if end is None:
157 end = -1
158
159 if end > 0:
160 menu.delete(0, end)
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400161 windows.add_windows_to_menu(menu)
Ned Deily622b2f62016-06-03 17:50:44 -0700162 windows.register_callback(postwindowsmenu)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000163
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000164 def about_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400165 "Handle Help 'About IDLE' event."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400166 # Synchronize with editor.EditorWindow.about_dialog.
167 from idlelib import help_about
csabella18ede062017-06-23 20:00:58 -0400168 help_about.AboutDialog(root)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000169
170 def config_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400171 "Handle Options 'Configure IDLE' event."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400172 # Synchronize with editor.EditorWindow.config_dialog.
173 from idlelib import configdialog
Ronald Oussoren220a9fb2009-05-26 18:41:00 +0000174
175 # Ensure that the root object has an instance_dict attribute,
176 # mirrors code in EditorWindow (although that sets the attribute
177 # on an EditorWindow instance that is then passed as the first
178 # argument to ConfigDialog)
179 root.instance_dict = flist.inversedict
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400180 configdialog.ConfigDialog(root, 'Settings')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000181
Georg Brandlaedd2892010-12-19 10:10:32 +0000182 def help_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400183 "Handle Help 'IDLE Help' event."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400184 # Synchronize with editor.EditorWindow.help_dialog.
Terry Jan Reedy5d46ab12015-09-20 19:57:13 -0400185 from idlelib import help
186 help.show_idlehelp(root)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000187
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000188 root.bind('<<about-idle>>', about_dialog)
189 root.bind('<<open-config-dialog>>', config_dialog)
Georg Brandlaedd2892010-12-19 10:10:32 +0000190 root.createcommand('::tk::mac::ShowPreferences', config_dialog)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000191 if flist:
192 root.bind('<<close-all-windows>>', flist.close_all_callback)
193
Ronald Oussoren10e05e12010-12-07 15:28:10 +0000194 # The binding above doesn't reliably work on all versions of Tk
195 # on MacOSX. Adding command definition below does seem to do the
196 # right thing for now.
197 root.createcommand('exit', flist.close_all_callback)
198
Ned Deilyb7601672014-03-27 20:49:14 -0700199 if isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000200 # for Carbon AquaTk, replace the default Tk apple menu
Terry Jan Reedy30f1f672015-07-30 16:44:22 -0400201 menudict['application'] = menu = Menu(menubar, name='apple',
202 tearoff=0)
Georg Brandlaedd2892010-12-19 10:10:32 +0000203 menubar.add_cascade(label='IDLE', menu=menu)
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400204 mainmenu.menudefs.insert(0,
Georg Brandlaedd2892010-12-19 10:10:32 +0000205 ('application', [
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000206 ('About IDLE', '<<about-idle>>'),
Georg Brandlaedd2892010-12-19 10:10:32 +0000207 None,
208 ]))
Ned Deilyb7601672014-03-27 20:49:14 -0700209 if isCocoaTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000210 # 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
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400215 del mainmenu.menudefs[-1][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000216
Terry Jan Reedy24f3a182016-06-08 14:37:05 -0400217def fixb2context(root):
218 '''Removed bad AquaTk Button-2 (right) and Paste bindings.
219
220 They prevent context menu access and seem to be gone in AquaTk8.6.
221 See issue #24801.
222 '''
223 root.unbind_class('Text', '<B2>')
224 root.unbind_class('Text', '<B2-Motion>')
225 root.unbind_class('Text', '<<PasteSelection>>')
226
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000227def setupApp(root, flist):
228 """
Ned Deilyb7601672014-03-27 20:49:14 -0700229 Perform initial OS X customizations if needed.
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400230 Called from pyshell.main() after initial calls to Tk()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000231
Ned Deilyb7601672014-03-27 20:49:14 -0700232 There are currently three major versions of Tk in use on OS X:
233 1. Aqua Cocoa Tk (native default since OS X 10.6)
234 2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
235 3. X11 (supported by some third-party distributors, deprecated)
236 There are various differences among the three that affect IDLE
237 behavior, primarily with menus, mouse key events, and accelerators.
238 Some one-time customizations are performed here.
239 Others are dynamically tested throughout idlelib by calls to the
240 isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
241 are initialized here as well.
242 """
Ned Deilyb7601672014-03-27 20:49:14 -0700243 if isAquaTk():
244 hideTkConsole(root)
245 overrideRootMenu(root, flist)
246 addOpenEventSupport(root, flist)
Ned Deily139fb7c2016-06-11 02:57:56 -0400247 fixb2context(root)
Terry Jan Reedy2518fa82016-06-12 15:49:20 -0400248
249
250if __name__ == '__main__':
251 from unittest import main
252 main('idlelib.idle_test.test_macosx', verbosity=2)