blob: 58137492f924a2f947d30c4ae58134303f96250d [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
148 menudict['windows'] = menu = Menu(menubar, name='windows')
149 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):
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000162 from idlelib import aboutDialog
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000163 aboutDialog.AboutDialog(root, 'About IDLE')
164
165 def config_dialog(event=None):
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000166 from idlelib import configDialog
Ronald Oussoren220a9fb2009-05-26 18:41:00 +0000167
168 # Ensure that the root object has an instance_dict attribute,
169 # mirrors code in EditorWindow (although that sets the attribute
170 # on an EditorWindow instance that is then passed as the first
171 # argument to ConfigDialog)
172 root.instance_dict = flist.inversedict
Benjamin Petersonfa0d7032009-06-01 22:42:33 +0000173 root.instance_dict = flist.inversedict
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000174 configDialog.ConfigDialog(root, 'Settings')
175
Georg Brandlaedd2892010-12-19 10:10:32 +0000176 def help_dialog(event=None):
177 from idlelib import textView
178 fn = path.join(path.abspath(path.dirname(__file__)), 'help.txt')
179 textView.view_file(root, 'Help', fn)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000180
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000181 root.bind('<<about-idle>>', about_dialog)
182 root.bind('<<open-config-dialog>>', config_dialog)
Georg Brandlaedd2892010-12-19 10:10:32 +0000183 root.createcommand('::tk::mac::ShowPreferences', config_dialog)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000184 if flist:
185 root.bind('<<close-all-windows>>', flist.close_all_callback)
186
Ronald Oussoren10e05e12010-12-07 15:28:10 +0000187 # The binding above doesn't reliably work on all versions of Tk
188 # on MacOSX. Adding command definition below does seem to do the
189 # right thing for now.
190 root.createcommand('exit', flist.close_all_callback)
191
Ned Deilyb7601672014-03-27 20:49:14 -0700192 if isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000193 # for Carbon AquaTk, replace the default Tk apple menu
194 menudict['application'] = menu = Menu(menubar, name='apple')
195 menubar.add_cascade(label='IDLE', menu=menu)
196 Bindings.menudefs.insert(0,
197 ('application', [
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000198 ('About IDLE', '<<about-idle>>'),
Georg Brandlaedd2892010-12-19 10:10:32 +0000199 None,
200 ]))
201 tkversion = root.tk.eval('info patchlevel')
202 if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
203 # for earlier AquaTk versions, supply a Preferences menu item
204 Bindings.menudefs[0][1].append(
205 ('_Preferences....', '<<open-config-dialog>>'),
206 )
Ned Deilyb7601672014-03-27 20:49:14 -0700207 if isCocoaTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000208 # replace default About dialog with About IDLE one
209 root.createcommand('tkAboutDialog', about_dialog)
210 # replace default "Help" item in Help menu
211 root.createcommand('::tk::mac::ShowHelp', help_dialog)
212 # remove redundant "IDLE Help" from menu
213 del Bindings.menudefs[-1][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000214
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000215def setupApp(root, flist):
216 """
Ned Deilyb7601672014-03-27 20:49:14 -0700217 Perform initial OS X customizations if needed.
218 Called from PyShell.main() after initial calls to Tk()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000219
Ned Deilyb7601672014-03-27 20:49:14 -0700220 There are currently three major versions of Tk in use on OS X:
221 1. Aqua Cocoa Tk (native default since OS X 10.6)
222 2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
223 3. X11 (supported by some third-party distributors, deprecated)
224 There are various differences among the three that affect IDLE
225 behavior, primarily with menus, mouse key events, and accelerators.
226 Some one-time customizations are performed here.
227 Others are dynamically tested throughout idlelib by calls to the
228 isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
229 are initialized here as well.
230 """
231 _initializeTkVariantTests(root)
232 if isAquaTk():
233 hideTkConsole(root)
234 overrideRootMenu(root, flist)
235 addOpenEventSupport(root, flist)