blob: b6488f84ca1eed3d68a2412a800920191f2fd95a [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]
143
144 # Remove the 'Configure' entry from the options menu, it is in the
145 # application menu as 'Preferences'
146 del Bindings.menudefs[-2][1][0:2]
147
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000148 menubar = Menu(root)
149 root.configure(menu=menubar)
150 menudict = {}
151
152 menudict['windows'] = menu = Menu(menubar, name='windows')
153 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)
162 WindowList.add_windows_to_menu(menu)
163 WindowList.register_callback(postwindowsmenu)
164
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000165 def about_dialog(event=None):
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000166 from idlelib import aboutDialog
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000167 aboutDialog.AboutDialog(root, 'About IDLE')
168
169 def config_dialog(event=None):
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000170 from idlelib import configDialog
Ronald Oussoren220a9fb2009-05-26 18:41:00 +0000171
172 # Ensure that the root object has an instance_dict attribute,
173 # mirrors code in EditorWindow (although that sets the attribute
174 # on an EditorWindow instance that is then passed as the first
175 # argument to ConfigDialog)
176 root.instance_dict = flist.inversedict
Benjamin Petersonfa0d7032009-06-01 22:42:33 +0000177 root.instance_dict = flist.inversedict
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000178 configDialog.ConfigDialog(root, 'Settings')
179
Georg Brandlaedd2892010-12-19 10:10:32 +0000180 def help_dialog(event=None):
181 from idlelib import textView
182 fn = path.join(path.abspath(path.dirname(__file__)), 'help.txt')
183 textView.view_file(root, 'Help', fn)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000184
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000185 root.bind('<<about-idle>>', about_dialog)
186 root.bind('<<open-config-dialog>>', config_dialog)
Georg Brandlaedd2892010-12-19 10:10:32 +0000187 root.createcommand('::tk::mac::ShowPreferences', config_dialog)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000188 if flist:
189 root.bind('<<close-all-windows>>', flist.close_all_callback)
190
Ronald Oussoren10e05e12010-12-07 15:28:10 +0000191 # The binding above doesn't reliably work on all versions of Tk
192 # on MacOSX. Adding command definition below does seem to do the
193 # right thing for now.
194 root.createcommand('exit', flist.close_all_callback)
195
Ned Deilyb7601672014-03-27 20:49:14 -0700196 if isCarbonTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000197 # for Carbon AquaTk, replace the default Tk apple menu
198 menudict['application'] = menu = Menu(menubar, name='apple')
199 menubar.add_cascade(label='IDLE', menu=menu)
200 Bindings.menudefs.insert(0,
201 ('application', [
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000202 ('About IDLE', '<<about-idle>>'),
Georg Brandlaedd2892010-12-19 10:10:32 +0000203 None,
204 ]))
205 tkversion = root.tk.eval('info patchlevel')
206 if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
207 # for earlier AquaTk versions, supply a Preferences menu item
208 Bindings.menudefs[0][1].append(
209 ('_Preferences....', '<<open-config-dialog>>'),
210 )
Ned Deilyb7601672014-03-27 20:49:14 -0700211 if isCocoaTk():
Georg Brandlaedd2892010-12-19 10:10:32 +0000212 # replace default About dialog with About IDLE one
213 root.createcommand('tkAboutDialog', about_dialog)
214 # replace default "Help" item in Help menu
215 root.createcommand('::tk::mac::ShowHelp', help_dialog)
216 # remove redundant "IDLE Help" from menu
217 del Bindings.menudefs[-1][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000218
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.
222 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)