blob: c225dd9e1a115dac1d1ce968d8b9e35fa279a202 [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.
Ned Deilyb7601672014-03-27 20:49:14 -07005import warnings
Ronald Oussoren6f6c5622009-12-24 14:03:19 +00006
Terry Jan Reedybfbaa6b2016-08-31 00:50:55 -04007import tkinter
8
Terry Jan Reedy2518fa82016-06-12 15:49:20 -04009
10## Define functions that query the Mac graphics type.
11## _tk_type and its initializer are private to this section.
12
Ned Deilyb7601672014-03-27 20:49:14 -070013_tk_type = None
14
Terry Jan Reedy2518fa82016-06-12 15:49:20 -040015def _init_tk_type():
Ned Deilyb7601672014-03-27 20:49:14 -070016 """
17 Initializes OS X Tk variant values for
18 isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
19 """
20 global _tk_type
Terry Jan Reedy2518fa82016-06-12 15:49:20 -040021 if platform == 'darwin':
22 root = tkinter.Tk()
Ned Deilyb7601672014-03-27 20:49:14 -070023 ws = root.tk.call('tk', 'windowingsystem')
24 if 'x11' in ws:
25 _tk_type = "xquartz"
26 elif 'aqua' not in ws:
27 _tk_type = "other"
28 elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
29 _tk_type = "cocoa"
30 else:
31 _tk_type = "carbon"
Terry Jan Reedy2518fa82016-06-12 15:49:20 -040032 root.destroy()
Ned Deilyb7601672014-03-27 20:49:14 -070033 else:
34 _tk_type = "other"
35
36def isAquaTk():
37 """
38 Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
39 """
Terry Jan Reedyfb51e652016-06-08 18:09:22 -040040 if not _tk_type:
41 _init_tk_type()
Ned Deilyb7601672014-03-27 20:49:14 -070042 return _tk_type == "cocoa" or _tk_type == "carbon"
43
44def isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +000045 """
46 Returns True if IDLE is using a Carbon Aqua Tk (instead of the
47 newer Cocoa Aqua Tk).
48 """
Terry Jan Reedyfb51e652016-06-08 18:09:22 -040049 if not _tk_type:
50 _init_tk_type()
Ned Deilyb7601672014-03-27 20:49:14 -070051 return _tk_type == "carbon"
52
53def isCocoaTk():
54 """
55 Returns True if IDLE is using a Cocoa Aqua Tk.
56 """
Terry Jan Reedyfb51e652016-06-08 18:09:22 -040057 if not _tk_type:
58 _init_tk_type()
Ned Deilyb7601672014-03-27 20:49:14 -070059 return _tk_type == "cocoa"
60
61def isXQuartz():
62 """
63 Returns True if IDLE is using an OS X X11 Tk.
64 """
Terry Jan Reedyfb51e652016-06-08 18:09:22 -040065 if not _tk_type:
66 _init_tk_type()
Ned Deilyb7601672014-03-27 20:49:14 -070067 return _tk_type == "xquartz"
Georg Brandlaedd2892010-12-19 10:10:32 +000068
Terry Jan Reedy2518fa82016-06-12 15:49:20 -040069
Ned Deily4ce92b22011-01-15 04:37:12 +000070def tkVersionWarning(root):
71 """
72 Returns a string warning message if the Tk version in use appears to
Ned Deilyc556c642012-07-30 03:31:21 -070073 be one known to cause problems with IDLE.
74 1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable.
75 2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but
76 can still crash unexpectedly.
Ned Deily4ce92b22011-01-15 04:37:12 +000077 """
78
Ned Deilyb7601672014-03-27 20:49:14 -070079 if isCocoaTk():
Ned Deilyc556c642012-07-30 03:31:21 -070080 patchlevel = root.tk.call('info', 'patchlevel')
81 if patchlevel not in ('8.5.7', '8.5.9'):
82 return False
83 return (r"WARNING: The version of Tcl/Tk ({0}) in use may"
Ned Deily4ce92b22011-01-15 04:37:12 +000084 r" be unstable.\n"
85 r"Visit http://www.python.org/download/mac/tcltk/"
Ned Deilyc556c642012-07-30 03:31:21 -070086 r" for current information.".format(patchlevel))
Ned Deily4ce92b22011-01-15 04:37:12 +000087 else:
88 return False
89
Terry Jan Reedy2518fa82016-06-12 15:49:20 -040090
91## Fix the menu and related functions.
92
Thomas Wouters0e3f5912006-08-11 14:57:12 +000093def addOpenEventSupport(root, flist):
94 """
Ezio Melotti13925002011-03-16 11:05:33 +020095 This ensures that the application will respond to open AppleEvents, which
96 makes is feasible to use IDLE as the default application for python files.
Thomas Wouters0e3f5912006-08-11 14:57:12 +000097 """
98 def doOpenFile(*args):
99 for fn in args:
100 flist.open(fn)
101
102 # The command below is a hook in aquatk that is called whenever the app
103 # receives a file open event. The callback can have multiple arguments,
104 # one for every file that should be opened.
105 root.createcommand("::tk::mac::OpenDocument", doOpenFile)
106
107def hideTkConsole(root):
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000108 try:
109 root.tk.call('console', 'hide')
Georg Brandl14fc4272008-05-17 18:39:55 +0000110 except tkinter.TclError:
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000111 # Some versions of the Tk framework don't have a console object
112 pass
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000113
114def overrideRootMenu(root, flist):
115 """
Ned Deilyb7601672014-03-27 20:49:14 -0700116 Replace the Tk root menu by something that is more appropriate for
117 IDLE with an Aqua Tk.
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000118 """
119 # The menu that is attached to the Tk root (".") is also used by AquaTk for
120 # all windows that don't specify a menu of their own. The default menubar
121 # contains a number of menus, none of which are appropriate for IDLE. The
122 # Most annoying of those is an 'About Tck/Tk...' menu in the application
123 # menu.
124 #
125 # This function replaces the default menubar by a mostly empty one, it
126 # should only contain the correct application menu and the window menu.
127 #
128 # Due to a (mis-)feature of TkAqua the user will also see an empty Help
129 # menu.
Terry Jan Reedy038c16b2015-05-15 23:03:17 -0400130 from tkinter import Menu
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400131 from idlelib import mainmenu
132 from idlelib import windows
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000133
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400134 closeItem = mainmenu.menudefs[0][1][-2]
Ned Deilyb7601672014-03-27 20:49:14 -0700135
136 # Remove the last 3 items of the file menu: a separator, close window and
137 # quit. Close window will be reinserted just above the save item, where
138 # it should be according to the HIG. Quit is in the application menu.
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400139 del mainmenu.menudefs[0][1][-3:]
140 mainmenu.menudefs[0][1].insert(6, closeItem)
Ned Deilyb7601672014-03-27 20:49:14 -0700141
142 # Remove the 'About' entry from the help menu, it is in the application
143 # menu
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400144 del mainmenu.menudefs[-1][1][0:2]
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400145 # Remove the 'Configure Idle' entry from the options menu, it is in the
Ned Deilyb7601672014-03-27 20:49:14 -0700146 # application menu as 'Preferences'
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400147 del mainmenu.menudefs[-2][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000148 menubar = Menu(root)
149 root.configure(menu=menubar)
150 menudict = {}
151
Terry Jan Reedy30f1f672015-07-30 16:44:22 -0400152 menudict['windows'] = menu = Menu(menubar, name='windows', tearoff=0)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000153 menubar.add_cascade(label='Window', menu=menu, underline=0)
154
155 def postwindowsmenu(menu=menu):
156 end = menu.index('end')
157 if end is None:
158 end = -1
159
160 if end > 0:
161 menu.delete(0, end)
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400162 windows.add_windows_to_menu(menu)
Ned Deily622b2f62016-06-03 17:50:44 -0700163 windows.register_callback(postwindowsmenu)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000164
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000165 def about_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400166 "Handle Help 'About IDLE' event."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400167 # Synchronize with editor.EditorWindow.about_dialog.
168 from idlelib import help_about
169 help_about.AboutDialog(root, 'About IDLE')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000170
171 def config_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400172 "Handle Options 'Configure IDLE' event."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400173 # Synchronize with editor.EditorWindow.config_dialog.
174 from idlelib import configdialog
Ronald Oussoren220a9fb2009-05-26 18:41:00 +0000175
176 # Ensure that the root object has an instance_dict attribute,
177 # mirrors code in EditorWindow (although that sets the attribute
178 # on an EditorWindow instance that is then passed as the first
179 # argument to ConfigDialog)
180 root.instance_dict = flist.inversedict
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400181 configdialog.ConfigDialog(root, 'Settings')
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000182
Georg Brandlaedd2892010-12-19 10:10:32 +0000183 def help_dialog(event=None):
Terry Jan Reedyb50c6372015-09-20 22:55:39 -0400184 "Handle Help 'IDLE Help' event."
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400185 # Synchronize with editor.EditorWindow.help_dialog.
Terry Jan Reedy5d46ab12015-09-20 19:57:13 -0400186 from idlelib import help
187 help.show_idlehelp(root)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000188
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000189 root.bind('<<about-idle>>', about_dialog)
190 root.bind('<<open-config-dialog>>', config_dialog)
Georg Brandlaedd2892010-12-19 10:10:32 +0000191 root.createcommand('::tk::mac::ShowPreferences', config_dialog)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000192 if flist:
193 root.bind('<<close-all-windows>>', flist.close_all_callback)
194
Ronald Oussoren10e05e12010-12-07 15:28:10 +0000195 # The binding above doesn't reliably work on all versions of Tk
196 # on MacOSX. Adding command definition below does seem to do the
197 # right thing for now.
198 root.createcommand('exit', flist.close_all_callback)
199
Ned Deilyb7601672014-03-27 20:49:14 -0700200 if isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000201 # for Carbon AquaTk, replace the default Tk apple menu
Terry Jan Reedy30f1f672015-07-30 16:44:22 -0400202 menudict['application'] = menu = Menu(menubar, name='apple',
203 tearoff=0)
Georg Brandlaedd2892010-12-19 10:10:32 +0000204 menubar.add_cascade(label='IDLE', menu=menu)
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400205 mainmenu.menudefs.insert(0,
Georg Brandlaedd2892010-12-19 10:10:32 +0000206 ('application', [
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000207 ('About IDLE', '<<about-idle>>'),
Georg Brandlaedd2892010-12-19 10:10:32 +0000208 None,
209 ]))
Ned Deilyb7601672014-03-27 20:49:14 -0700210 if isCocoaTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000211 # replace default About dialog with About IDLE one
212 root.createcommand('tkAboutDialog', about_dialog)
213 # replace default "Help" item in Help menu
214 root.createcommand('::tk::mac::ShowHelp', help_dialog)
215 # remove redundant "IDLE Help" from menu
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400216 del mainmenu.menudefs[-1][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000217
Terry Jan Reedy24f3a182016-06-08 14:37:05 -0400218def fixb2context(root):
219 '''Removed bad AquaTk Button-2 (right) and Paste bindings.
220
221 They prevent context menu access and seem to be gone in AquaTk8.6.
222 See issue #24801.
223 '''
224 root.unbind_class('Text', '<B2>')
225 root.unbind_class('Text', '<B2-Motion>')
226 root.unbind_class('Text', '<<PasteSelection>>')
227
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000228def setupApp(root, flist):
229 """
Ned Deilyb7601672014-03-27 20:49:14 -0700230 Perform initial OS X customizations if needed.
Terry Jan Reedy6fa5bdc2016-05-28 13:22:31 -0400231 Called from pyshell.main() after initial calls to Tk()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000232
Ned Deilyb7601672014-03-27 20:49:14 -0700233 There are currently three major versions of Tk in use on OS X:
234 1. Aqua Cocoa Tk (native default since OS X 10.6)
235 2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
236 3. X11 (supported by some third-party distributors, deprecated)
237 There are various differences among the three that affect IDLE
238 behavior, primarily with menus, mouse key events, and accelerators.
239 Some one-time customizations are performed here.
240 Others are dynamically tested throughout idlelib by calls to the
241 isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
242 are initialized here as well.
243 """
Ned Deilyb7601672014-03-27 20:49:14 -0700244 if isAquaTk():
245 hideTkConsole(root)
246 overrideRootMenu(root, flist)
247 addOpenEventSupport(root, flist)
Ned Deily139fb7c2016-06-11 02:57:56 -0400248 fixb2context(root)
Terry Jan Reedy2518fa82016-06-12 15:49:20 -0400249
250
251if __name__ == '__main__':
252 from unittest import main
253 main('idlelib.idle_test.test_macosx', verbosity=2)