blob: 67069fa0f393b526855c3ee5e8b9b05c6532e060 [file] [log] [blame]
Thomas Wouters0e3f5912006-08-11 14:57:12 +00001"""
2A number of function that enhance IDLE on MacOSX when it used as a normal
3GUI application (as opposed to an X11 application).
4"""
5import sys
Georg Brandl14fc4272008-05-17 18:39:55 +00006import tkinter
Georg Brandlaedd2892010-12-19 10:10:32 +00007from os import path
Thomas Wouters0e3f5912006-08-11 14:57:12 +00008
Ronald Oussoren6f6c5622009-12-24 14:03:19 +00009
10_appbundle = None
11
Thomas Wouters0e3f5912006-08-11 14:57:12 +000012def runningAsOSXApp():
Ronald Oussoren827822e2009-02-12 15:01:44 +000013 """
14 Returns True if Python is running from within an app on OSX.
Ned Deily6aaa03a2012-08-17 13:22:30 -070015 If so, the various OS X customizations will be triggered later (menu
16 fixup, et al). (Originally, this test was supposed to condition
17 behavior on whether IDLE was running under Aqua Tk rather than
18 under X11 Tk but that does not work since a framework build
19 could be linked with X11. For several releases, this test actually
20 differentiates between whether IDLE is running from a framework or
21 not. As a future enhancement, it should be considered whether there
22 should be a difference based on framework and any needed X11 adaptions
23 should be made dependent on a new function that actually tests for X11.)
Ronald Oussoren827822e2009-02-12 15:01:44 +000024 """
Ronald Oussoren6f6c5622009-12-24 14:03:19 +000025 global _appbundle
26 if _appbundle is None:
Ned Deily6aaa03a2012-08-17 13:22:30 -070027 _appbundle = sys.platform == 'darwin'
28 if _appbundle:
29 import sysconfig
30 _appbundle = bool(sysconfig.get_config_var('PYTHONFRAMEWORK'))
Ronald Oussoren6f6c5622009-12-24 14:03:19 +000031 return _appbundle
Thomas Wouters0e3f5912006-08-11 14:57:12 +000032
Georg Brandlaedd2892010-12-19 10:10:32 +000033_carbonaquatk = None
34
35def isCarbonAquaTk(root):
36 """
37 Returns True if IDLE is using a Carbon Aqua Tk (instead of the
38 newer Cocoa Aqua Tk).
39 """
40 global _carbonaquatk
41 if _carbonaquatk is None:
42 _carbonaquatk = (runningAsOSXApp() and
43 'aqua' in root.tk.call('tk', 'windowingsystem') and
44 'AppKit' not in root.tk.call('winfo', 'server', '.'))
45 return _carbonaquatk
46
Ned Deily4ce92b22011-01-15 04:37:12 +000047def tkVersionWarning(root):
48 """
49 Returns a string warning message if the Tk version in use appears to
Ned Deilyc556c642012-07-30 03:31:21 -070050 be one known to cause problems with IDLE.
51 1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable.
52 2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but
53 can still crash unexpectedly.
Ned Deily4ce92b22011-01-15 04:37:12 +000054 """
55
56 if (runningAsOSXApp() and
Ned Deilyc556c642012-07-30 03:31:21 -070057 ('AppKit' in root.tk.call('winfo', 'server', '.')) ):
58 patchlevel = root.tk.call('info', 'patchlevel')
59 if patchlevel not in ('8.5.7', '8.5.9'):
60 return False
61 return (r"WARNING: The version of Tcl/Tk ({0}) in use may"
Ned Deily4ce92b22011-01-15 04:37:12 +000062 r" be unstable.\n"
63 r"Visit http://www.python.org/download/mac/tcltk/"
Ned Deilyc556c642012-07-30 03:31:21 -070064 r" for current information.".format(patchlevel))
Ned Deily4ce92b22011-01-15 04:37:12 +000065 else:
66 return False
67
Thomas Wouters0e3f5912006-08-11 14:57:12 +000068def addOpenEventSupport(root, flist):
69 """
Ezio Melotti13925002011-03-16 11:05:33 +020070 This ensures that the application will respond to open AppleEvents, which
71 makes is feasible to use IDLE as the default application for python files.
Thomas Wouters0e3f5912006-08-11 14:57:12 +000072 """
73 def doOpenFile(*args):
74 for fn in args:
75 flist.open(fn)
76
77 # The command below is a hook in aquatk that is called whenever the app
78 # receives a file open event. The callback can have multiple arguments,
79 # one for every file that should be opened.
80 root.createcommand("::tk::mac::OpenDocument", doOpenFile)
81
82def hideTkConsole(root):
Guido van Rossumb5a755e2007-07-18 18:15:48 +000083 try:
84 root.tk.call('console', 'hide')
Georg Brandl14fc4272008-05-17 18:39:55 +000085 except tkinter.TclError:
Guido van Rossumb5a755e2007-07-18 18:15:48 +000086 # Some versions of the Tk framework don't have a console object
87 pass
Thomas Wouters0e3f5912006-08-11 14:57:12 +000088
89def overrideRootMenu(root, flist):
90 """
91 Replace the Tk root menu by something that's more appropriate for
92 IDLE.
93 """
94 # The menu that is attached to the Tk root (".") is also used by AquaTk for
95 # all windows that don't specify a menu of their own. The default menubar
96 # contains a number of menus, none of which are appropriate for IDLE. The
97 # Most annoying of those is an 'About Tck/Tk...' menu in the application
98 # menu.
99 #
100 # This function replaces the default menubar by a mostly empty one, it
101 # should only contain the correct application menu and the window menu.
102 #
103 # Due to a (mis-)feature of TkAqua the user will also see an empty Help
104 # menu.
Georg Brandl14fc4272008-05-17 18:39:55 +0000105 from tkinter import Menu, Text, Text
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000106 from idlelib.EditorWindow import prepstr, get_accelerator
107 from idlelib import Bindings
108 from idlelib import WindowList
109 from idlelib.MultiCall import MultiCallCreator
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000110
111 menubar = Menu(root)
112 root.configure(menu=menubar)
113 menudict = {}
114
115 menudict['windows'] = menu = Menu(menubar, name='windows')
116 menubar.add_cascade(label='Window', menu=menu, underline=0)
117
118 def postwindowsmenu(menu=menu):
119 end = menu.index('end')
120 if end is None:
121 end = -1
122
123 if end > 0:
124 menu.delete(0, end)
125 WindowList.add_windows_to_menu(menu)
126 WindowList.register_callback(postwindowsmenu)
127
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000128 def about_dialog(event=None):
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000129 from idlelib import aboutDialog
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000130 aboutDialog.AboutDialog(root, 'About IDLE')
131
132 def config_dialog(event=None):
Kurt B. Kaiser2d7f6a02007-08-22 23:01:33 +0000133 from idlelib import configDialog
Ronald Oussoren220a9fb2009-05-26 18:41:00 +0000134
135 # Ensure that the root object has an instance_dict attribute,
136 # mirrors code in EditorWindow (although that sets the attribute
137 # on an EditorWindow instance that is then passed as the first
138 # argument to ConfigDialog)
139 root.instance_dict = flist.inversedict
Benjamin Petersonfa0d7032009-06-01 22:42:33 +0000140 root.instance_dict = flist.inversedict
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000141 configDialog.ConfigDialog(root, 'Settings')
142
Georg Brandlaedd2892010-12-19 10:10:32 +0000143 def help_dialog(event=None):
144 from idlelib import textView
145 fn = path.join(path.abspath(path.dirname(__file__)), 'help.txt')
146 textView.view_file(root, 'Help', fn)
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000147
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000148 root.bind('<<about-idle>>', about_dialog)
149 root.bind('<<open-config-dialog>>', config_dialog)
Georg Brandlaedd2892010-12-19 10:10:32 +0000150 root.createcommand('::tk::mac::ShowPreferences', config_dialog)
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000151 if flist:
152 root.bind('<<close-all-windows>>', flist.close_all_callback)
153
Ronald Oussoren10e05e12010-12-07 15:28:10 +0000154 # The binding above doesn't reliably work on all versions of Tk
155 # on MacOSX. Adding command definition below does seem to do the
156 # right thing for now.
157 root.createcommand('exit', flist.close_all_callback)
158
Georg Brandlaedd2892010-12-19 10:10:32 +0000159 if isCarbonAquaTk(root):
160 # for Carbon AquaTk, replace the default Tk apple menu
161 menudict['application'] = menu = Menu(menubar, name='apple')
162 menubar.add_cascade(label='IDLE', menu=menu)
163 Bindings.menudefs.insert(0,
164 ('application', [
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000165 ('About IDLE', '<<about-idle>>'),
Georg Brandlaedd2892010-12-19 10:10:32 +0000166 None,
167 ]))
168 tkversion = root.tk.eval('info patchlevel')
169 if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
170 # for earlier AquaTk versions, supply a Preferences menu item
171 Bindings.menudefs[0][1].append(
172 ('_Preferences....', '<<open-config-dialog>>'),
173 )
Guido van Rossumb5a755e2007-07-18 18:15:48 +0000174 else:
Georg Brandlaedd2892010-12-19 10:10:32 +0000175 # assume Cocoa AquaTk
176 # replace default About dialog with About IDLE one
177 root.createcommand('tkAboutDialog', about_dialog)
178 # replace default "Help" item in Help menu
179 root.createcommand('::tk::mac::ShowHelp', help_dialog)
180 # remove redundant "IDLE Help" from menu
181 del Bindings.menudefs[-1][1][0]
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000182
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000183def setupApp(root, flist):
184 """
185 Perform setup for the OSX application bundle.
186 """
187 if not runningAsOSXApp(): return
188
189 hideTkConsole(root)
190 overrideRootMenu(root, flist)
191 addOpenEventSupport(root, flist)