blob: 65bd68885ac2bcb1db93a464ae721d2dc67faf5a [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.
Georg Brandl14fc4272008-05-17 18:39:55 +0000126 from tkinter import Menu, Text, Text
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000127 from idlelib.EditorWindow import prepstr, get_accelerator
128 from idlelib import Bindings
129 from idlelib import WindowList
130 from idlelib.MultiCall import MultiCallCreator
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000131
Ned Deilyb7601672014-03-27 20:49:14 -0700132 closeItem = Bindings.menudefs[0][1][-2]
133
134 # Remove the last 3 items of the file menu: a separator, close window and
135 # quit. Close window will be reinserted just above the save item, where
136 # it should be according to the HIG. Quit is in the application menu.
137 del Bindings.menudefs[0][1][-3:]
138 Bindings.menudefs[0][1].insert(6, closeItem)
139
140 # Remove the 'About' entry from the help menu, it is in the application
141 # menu
142 del Bindings.menudefs[-1][1][0:2]
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400143 # Remove the 'Configure Idle' entry from the options menu, it is in the
Ned Deilyb7601672014-03-27 20:49:14 -0700144 # application menu as 'Preferences'
Terry Jan Reedya9421fb2014-10-22 20:15:18 -0400145 del Bindings.menudefs[-2][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000146 menubar = Menu(root)
147 root.configure(menu=menubar)
148 menudict = {}
149
150 menudict['windows'] = menu = Menu(menubar, name='windows')
151 menubar.add_cascade(label='Window', menu=menu, underline=0)
152
153 def postwindowsmenu(menu=menu):
154 end = menu.index('end')
155 if end is None:
156 end = -1
157
158 if end > 0:
159 menu.delete(0, end)
160 WindowList.add_windows_to_menu(menu)
161 WindowList.register_callback(postwindowsmenu)
162
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000163 def about_dialog(event=None):
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000164 from idlelib import aboutDialog
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000165 aboutDialog.AboutDialog(root, 'About IDLE')
166
167 def config_dialog(event=None):
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000168 from idlelib import configDialog
Ronald Oussoren220a9fb2009-05-26 18:41:00 +0000169
170 # Ensure that the root object has an instance_dict attribute,
171 # mirrors code in EditorWindow (although that sets the attribute
172 # on an EditorWindow instance that is then passed as the first
173 # argument to ConfigDialog)
174 root.instance_dict = flist.inversedict
Benjamin Petersonfa0d7032009-06-01 22:42:33 +0000175 root.instance_dict = flist.inversedict
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000176 configDialog.ConfigDialog(root, 'Settings')
177
Georg Brandlaedd2892010-12-19 10:10:32 +0000178 def help_dialog(event=None):
179 from idlelib import textView
180 fn = path.join(path.abspath(path.dirname(__file__)), 'help.txt')
181 textView.view_file(root, 'Help', fn)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000182
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000183 root.bind('<<about-idle>>', about_dialog)
184 root.bind('<<open-config-dialog>>', config_dialog)
Georg Brandlaedd2892010-12-19 10:10:32 +0000185 root.createcommand('::tk::mac::ShowPreferences', config_dialog)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000186 if flist:
187 root.bind('<<close-all-windows>>', flist.close_all_callback)
188
Ronald Oussoren10e05e12010-12-07 15:28:10 +0000189 # The binding above doesn't reliably work on all versions of Tk
190 # on MacOSX. Adding command definition below does seem to do the
191 # right thing for now.
192 root.createcommand('exit', flist.close_all_callback)
193
Ned Deilyb7601672014-03-27 20:49:14 -0700194 if isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000195 # for Carbon AquaTk, replace the default Tk apple menu
196 menudict['application'] = menu = Menu(menubar, name='apple')
197 menubar.add_cascade(label='IDLE', menu=menu)
198 Bindings.menudefs.insert(0,
199 ('application', [
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000200 ('About IDLE', '<<about-idle>>'),
Georg Brandlaedd2892010-12-19 10:10:32 +0000201 None,
202 ]))
203 tkversion = root.tk.eval('info patchlevel')
204 if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
205 # for earlier AquaTk versions, supply a Preferences menu item
206 Bindings.menudefs[0][1].append(
207 ('_Preferences....', '<<open-config-dialog>>'),
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
215 del Bindings.menudefs[-1][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000216
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000217def setupApp(root, flist):
218 """
Ned Deilyb7601672014-03-27 20:49:14 -0700219 Perform initial OS X customizations if needed.
220 Called from PyShell.main() after initial calls to Tk()
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000221
Ned Deilyb7601672014-03-27 20:49:14 -0700222 There are currently three major versions of Tk in use on OS X:
223 1. Aqua Cocoa Tk (native default since OS X 10.6)
224 2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
225 3. X11 (supported by some third-party distributors, deprecated)
226 There are various differences among the three that affect IDLE
227 behavior, primarily with menus, mouse key events, and accelerators.
228 Some one-time customizations are performed here.
229 Others are dynamically tested throughout idlelib by calls to the
230 isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
231 are initialized here as well.
232 """
233 _initializeTkVariantTests(root)
234 if isAquaTk():
235 hideTkConsole(root)
236 overrideRootMenu(root, flist)
237 addOpenEventSupport(root, flist)