1""" 2A number of functions that enhance IDLE on Mac OSX. 3""" 4import sys 5import Tkinter 6from os import path 7 8 9import warnings 10 11def runningAsOSXApp(): 12 warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()", 13 DeprecationWarning, stacklevel=2) 14 return isAquaTk() 15 16def isCarbonAquaTk(root): 17 warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()", 18 DeprecationWarning, stacklevel=2) 19 return isCarbonTk() 20 21_tk_type = None 22 23def _initializeTkVariantTests(root): 24 """ 25 Initializes OS X Tk variant values for 26 isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz(). 27 """ 28 global _tk_type 29 if sys.platform == 'darwin': 30 ws = root.tk.call('tk', 'windowingsystem') 31 if 'x11' in ws: 32 _tk_type = "xquartz" 33 elif 'aqua' not in ws: 34 _tk_type = "other" 35 elif 'AppKit' in root.tk.call('winfo', 'server', '.'): 36 _tk_type = "cocoa" 37 else: 38 _tk_type = "carbon" 39 else: 40 _tk_type = "other" 41 42def isAquaTk(): 43 """ 44 Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon). 45 """ 46 assert _tk_type is not None 47 return _tk_type == "cocoa" or _tk_type == "carbon" 48 49def isCarbonTk(): 50 """ 51 Returns True if IDLE is using a Carbon Aqua Tk (instead of the 52 newer Cocoa Aqua Tk). 53 """ 54 assert _tk_type is not None 55 return _tk_type == "carbon" 56 57def isCocoaTk(): 58 """ 59 Returns True if IDLE is using a Cocoa Aqua Tk. 60 """ 61 assert _tk_type is not None 62 return _tk_type == "cocoa" 63 64def isXQuartz(): 65 """ 66 Returns True if IDLE is using an OS X X11 Tk. 67 """ 68 assert _tk_type is not None 69 return _tk_type == "xquartz" 70 71def tkVersionWarning(root): 72 """ 73 Returns a string warning message if the Tk version in use appears to 74 be one known to cause problems with IDLE. 75 1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable. 76 2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but 77 can still crash unexpectedly. 78 """ 79 80 if isCocoaTk(): 81 patchlevel = root.tk.call('info', 'patchlevel') 82 if patchlevel not in ('8.5.7', '8.5.9'): 83 return False 84 return (r"WARNING: The version of Tcl/Tk ({0}) in use may" 85 r" be unstable.\n" 86 r"Visit http://www.python.org/download/mac/tcltk/" 87 r" for current information.".format(patchlevel)) 88 else: 89 return False 90 91def addOpenEventSupport(root, flist): 92 """ 93 This ensures that the application will respond to open AppleEvents, which 94 makes is feasible to use IDLE as the default application for python files. 95 """ 96 def doOpenFile(*args): 97 for fn in args: 98 flist.open(fn) 99 100 # The command below is a hook in aquatk that is called whenever the app 101 # receives a file open event. The callback can have multiple arguments, 102 # one for every file that should be opened. 103 root.createcommand("::tk::mac::OpenDocument", doOpenFile) 104 105def hideTkConsole(root): 106 try: 107 root.tk.call('console', 'hide') 108 except Tkinter.TclError: 109 # Some versions of the Tk framework don't have a console object 110 pass 111 112def overrideRootMenu(root, flist): 113 """ 114 Replace the Tk root menu by something that is more appropriate for 115 IDLE with an Aqua Tk. 116 """ 117 # The menu that is attached to the Tk root (".") is also used by AquaTk for 118 # all windows that don't specify a menu of their own. The default menubar 119 # contains a number of menus, none of which are appropriate for IDLE. The 120 # Most annoying of those is an 'About Tck/Tk...' menu in the application 121 # menu. 122 # 123 # This function replaces the default menubar by a mostly empty one, it 124 # should only contain the correct application menu and the window menu. 125 # 126 # Due to a (mis-)feature of TkAqua the user will also see an empty Help 127 # menu. 128 from Tkinter import Menu 129 from idlelib import Bindings 130 from idlelib import WindowList 131 132 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 # Remove the 'Configure Idle' entry from the options menu, it is in the 144 # application menu as 'Preferences' 145 del Bindings.menudefs[-2][1][0] 146 menubar = Menu(root) 147 root.configure(menu=menubar) 148 menudict = {} 149 150 menudict['windows'] = menu = Menu(menubar, name='windows', tearoff=0) 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 163 def about_dialog(event=None): 164 "Handle Help 'About IDLE' event." 165 # Synchronize with EditorWindow.EditorWindow.about_dialog. 166 from idlelib import aboutDialog 167 aboutDialog.AboutDialog(root, 'About IDLE') 168 169 def config_dialog(event=None): 170 "Handle Options 'Configure IDLE' event." 171 # Synchronize with EditorWindow.EditorWindow.config_dialog. 172 from idlelib import configDialog 173 root.instance_dict = flist.inversedict 174 configDialog.ConfigDialog(root, 'Settings') 175 176 def help_dialog(event=None): 177 "Handle Help 'IDLE Help' event." 178 # Synchronize with EditorWindow.EditorWindow.help_dialog. 179 from idlelib import help 180 help.show_idlehelp(root) 181 182 root.bind('<<about-idle>>', about_dialog) 183 root.bind('<<open-config-dialog>>', config_dialog) 184 root.createcommand('::tk::mac::ShowPreferences', config_dialog) 185 if flist: 186 root.bind('<<close-all-windows>>', flist.close_all_callback) 187 188 # The binding above doesn't reliably work on all versions of Tk 189 # on MacOSX. Adding command definition below does seem to do the 190 # right thing for now. 191 root.createcommand('exit', flist.close_all_callback) 192 193 if isCarbonTk(): 194 # for Carbon AquaTk, replace the default Tk apple menu 195 menudict['application'] = menu = Menu(menubar, name='apple', 196 tearoff=0) 197 menubar.add_cascade(label='IDLE', menu=menu) 198 Bindings.menudefs.insert(0, 199 ('application', [ 200 ('About IDLE', '<<about-idle>>'), 201 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 ) 209 if isCocoaTk(): 210 # 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] 216 217def setupApp(root, flist): 218 """ 219 Perform initial OS X customizations if needed. 220 Called from PyShell.main() after initial calls to Tk() 221 222 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) 238