annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/macosx.py @ 68:5028fdace37b

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 16:23:26 -0400
parents
children
rev   line source
jpayne@68 1 """
jpayne@68 2 A number of functions that enhance IDLE on macOS.
jpayne@68 3 """
jpayne@68 4 from os.path import expanduser
jpayne@68 5 import plistlib
jpayne@68 6 from sys import platform # Used in _init_tk_type, changed by test.
jpayne@68 7
jpayne@68 8 import tkinter
jpayne@68 9
jpayne@68 10
jpayne@68 11 ## Define functions that query the Mac graphics type.
jpayne@68 12 ## _tk_type and its initializer are private to this section.
jpayne@68 13
jpayne@68 14 _tk_type = None
jpayne@68 15
jpayne@68 16 def _init_tk_type():
jpayne@68 17 """
jpayne@68 18 Initializes OS X Tk variant values for
jpayne@68 19 isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
jpayne@68 20 """
jpayne@68 21 global _tk_type
jpayne@68 22 if platform == 'darwin':
jpayne@68 23 root = tkinter.Tk()
jpayne@68 24 ws = root.tk.call('tk', 'windowingsystem')
jpayne@68 25 if 'x11' in ws:
jpayne@68 26 _tk_type = "xquartz"
jpayne@68 27 elif 'aqua' not in ws:
jpayne@68 28 _tk_type = "other"
jpayne@68 29 elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
jpayne@68 30 _tk_type = "cocoa"
jpayne@68 31 else:
jpayne@68 32 _tk_type = "carbon"
jpayne@68 33 root.destroy()
jpayne@68 34 else:
jpayne@68 35 _tk_type = "other"
jpayne@68 36
jpayne@68 37 def isAquaTk():
jpayne@68 38 """
jpayne@68 39 Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
jpayne@68 40 """
jpayne@68 41 if not _tk_type:
jpayne@68 42 _init_tk_type()
jpayne@68 43 return _tk_type == "cocoa" or _tk_type == "carbon"
jpayne@68 44
jpayne@68 45 def isCarbonTk():
jpayne@68 46 """
jpayne@68 47 Returns True if IDLE is using a Carbon Aqua Tk (instead of the
jpayne@68 48 newer Cocoa Aqua Tk).
jpayne@68 49 """
jpayne@68 50 if not _tk_type:
jpayne@68 51 _init_tk_type()
jpayne@68 52 return _tk_type == "carbon"
jpayne@68 53
jpayne@68 54 def isCocoaTk():
jpayne@68 55 """
jpayne@68 56 Returns True if IDLE is using a Cocoa Aqua Tk.
jpayne@68 57 """
jpayne@68 58 if not _tk_type:
jpayne@68 59 _init_tk_type()
jpayne@68 60 return _tk_type == "cocoa"
jpayne@68 61
jpayne@68 62 def isXQuartz():
jpayne@68 63 """
jpayne@68 64 Returns True if IDLE is using an OS X X11 Tk.
jpayne@68 65 """
jpayne@68 66 if not _tk_type:
jpayne@68 67 _init_tk_type()
jpayne@68 68 return _tk_type == "xquartz"
jpayne@68 69
jpayne@68 70
jpayne@68 71 def tkVersionWarning(root):
jpayne@68 72 """
jpayne@68 73 Returns a string warning message if the Tk version in use appears to
jpayne@68 74 be one known to cause problems with IDLE.
jpayne@68 75 1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable.
jpayne@68 76 2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but
jpayne@68 77 can still crash unexpectedly.
jpayne@68 78 """
jpayne@68 79
jpayne@68 80 if isCocoaTk():
jpayne@68 81 patchlevel = root.tk.call('info', 'patchlevel')
jpayne@68 82 if patchlevel not in ('8.5.7', '8.5.9'):
jpayne@68 83 return False
jpayne@68 84 return ("WARNING: The version of Tcl/Tk ({0}) in use may"
jpayne@68 85 " be unstable.\n"
jpayne@68 86 "Visit http://www.python.org/download/mac/tcltk/"
jpayne@68 87 " for current information.".format(patchlevel))
jpayne@68 88 else:
jpayne@68 89 return False
jpayne@68 90
jpayne@68 91
jpayne@68 92 def readSystemPreferences():
jpayne@68 93 """
jpayne@68 94 Fetch the macOS system preferences.
jpayne@68 95 """
jpayne@68 96 if platform != 'darwin':
jpayne@68 97 return None
jpayne@68 98
jpayne@68 99 plist_path = expanduser('~/Library/Preferences/.GlobalPreferences.plist')
jpayne@68 100 try:
jpayne@68 101 with open(plist_path, 'rb') as plist_file:
jpayne@68 102 return plistlib.load(plist_file)
jpayne@68 103 except OSError:
jpayne@68 104 return None
jpayne@68 105
jpayne@68 106
jpayne@68 107 def preferTabsPreferenceWarning():
jpayne@68 108 """
jpayne@68 109 Warn if "Prefer tabs when opening documents" is set to "Always".
jpayne@68 110 """
jpayne@68 111 if platform != 'darwin':
jpayne@68 112 return None
jpayne@68 113
jpayne@68 114 prefs = readSystemPreferences()
jpayne@68 115 if prefs and prefs.get('AppleWindowTabbingMode') == 'always':
jpayne@68 116 return (
jpayne@68 117 'WARNING: The system preference "Prefer tabs when opening'
jpayne@68 118 ' documents" is set to "Always". This will cause various problems'
jpayne@68 119 ' with IDLE. For the best experience, change this setting when'
jpayne@68 120 ' running IDLE (via System Preferences -> Dock).'
jpayne@68 121 )
jpayne@68 122 return None
jpayne@68 123
jpayne@68 124
jpayne@68 125 ## Fix the menu and related functions.
jpayne@68 126
jpayne@68 127 def addOpenEventSupport(root, flist):
jpayne@68 128 """
jpayne@68 129 This ensures that the application will respond to open AppleEvents, which
jpayne@68 130 makes is feasible to use IDLE as the default application for python files.
jpayne@68 131 """
jpayne@68 132 def doOpenFile(*args):
jpayne@68 133 for fn in args:
jpayne@68 134 flist.open(fn)
jpayne@68 135
jpayne@68 136 # The command below is a hook in aquatk that is called whenever the app
jpayne@68 137 # receives a file open event. The callback can have multiple arguments,
jpayne@68 138 # one for every file that should be opened.
jpayne@68 139 root.createcommand("::tk::mac::OpenDocument", doOpenFile)
jpayne@68 140
jpayne@68 141 def hideTkConsole(root):
jpayne@68 142 try:
jpayne@68 143 root.tk.call('console', 'hide')
jpayne@68 144 except tkinter.TclError:
jpayne@68 145 # Some versions of the Tk framework don't have a console object
jpayne@68 146 pass
jpayne@68 147
jpayne@68 148 def overrideRootMenu(root, flist):
jpayne@68 149 """
jpayne@68 150 Replace the Tk root menu by something that is more appropriate for
jpayne@68 151 IDLE with an Aqua Tk.
jpayne@68 152 """
jpayne@68 153 # The menu that is attached to the Tk root (".") is also used by AquaTk for
jpayne@68 154 # all windows that don't specify a menu of their own. The default menubar
jpayne@68 155 # contains a number of menus, none of which are appropriate for IDLE. The
jpayne@68 156 # Most annoying of those is an 'About Tck/Tk...' menu in the application
jpayne@68 157 # menu.
jpayne@68 158 #
jpayne@68 159 # This function replaces the default menubar by a mostly empty one, it
jpayne@68 160 # should only contain the correct application menu and the window menu.
jpayne@68 161 #
jpayne@68 162 # Due to a (mis-)feature of TkAqua the user will also see an empty Help
jpayne@68 163 # menu.
jpayne@68 164 from tkinter import Menu
jpayne@68 165 from idlelib import mainmenu
jpayne@68 166 from idlelib import window
jpayne@68 167
jpayne@68 168 closeItem = mainmenu.menudefs[0][1][-2]
jpayne@68 169
jpayne@68 170 # Remove the last 3 items of the file menu: a separator, close window and
jpayne@68 171 # quit. Close window will be reinserted just above the save item, where
jpayne@68 172 # it should be according to the HIG. Quit is in the application menu.
jpayne@68 173 del mainmenu.menudefs[0][1][-3:]
jpayne@68 174 mainmenu.menudefs[0][1].insert(6, closeItem)
jpayne@68 175
jpayne@68 176 # Remove the 'About' entry from the help menu, it is in the application
jpayne@68 177 # menu
jpayne@68 178 del mainmenu.menudefs[-1][1][0:2]
jpayne@68 179 # Remove the 'Configure Idle' entry from the options menu, it is in the
jpayne@68 180 # application menu as 'Preferences'
jpayne@68 181 del mainmenu.menudefs[-3][1][0:2]
jpayne@68 182 menubar = Menu(root)
jpayne@68 183 root.configure(menu=menubar)
jpayne@68 184 menudict = {}
jpayne@68 185
jpayne@68 186 menudict['window'] = menu = Menu(menubar, name='window', tearoff=0)
jpayne@68 187 menubar.add_cascade(label='Window', menu=menu, underline=0)
jpayne@68 188
jpayne@68 189 def postwindowsmenu(menu=menu):
jpayne@68 190 end = menu.index('end')
jpayne@68 191 if end is None:
jpayne@68 192 end = -1
jpayne@68 193
jpayne@68 194 if end > 0:
jpayne@68 195 menu.delete(0, end)
jpayne@68 196 window.add_windows_to_menu(menu)
jpayne@68 197 window.register_callback(postwindowsmenu)
jpayne@68 198
jpayne@68 199 def about_dialog(event=None):
jpayne@68 200 "Handle Help 'About IDLE' event."
jpayne@68 201 # Synchronize with editor.EditorWindow.about_dialog.
jpayne@68 202 from idlelib import help_about
jpayne@68 203 help_about.AboutDialog(root)
jpayne@68 204
jpayne@68 205 def config_dialog(event=None):
jpayne@68 206 "Handle Options 'Configure IDLE' event."
jpayne@68 207 # Synchronize with editor.EditorWindow.config_dialog.
jpayne@68 208 from idlelib import configdialog
jpayne@68 209
jpayne@68 210 # Ensure that the root object has an instance_dict attribute,
jpayne@68 211 # mirrors code in EditorWindow (although that sets the attribute
jpayne@68 212 # on an EditorWindow instance that is then passed as the first
jpayne@68 213 # argument to ConfigDialog)
jpayne@68 214 root.instance_dict = flist.inversedict
jpayne@68 215 configdialog.ConfigDialog(root, 'Settings')
jpayne@68 216
jpayne@68 217 def help_dialog(event=None):
jpayne@68 218 "Handle Help 'IDLE Help' event."
jpayne@68 219 # Synchronize with editor.EditorWindow.help_dialog.
jpayne@68 220 from idlelib import help
jpayne@68 221 help.show_idlehelp(root)
jpayne@68 222
jpayne@68 223 root.bind('<<about-idle>>', about_dialog)
jpayne@68 224 root.bind('<<open-config-dialog>>', config_dialog)
jpayne@68 225 root.createcommand('::tk::mac::ShowPreferences', config_dialog)
jpayne@68 226 if flist:
jpayne@68 227 root.bind('<<close-all-windows>>', flist.close_all_callback)
jpayne@68 228
jpayne@68 229 # The binding above doesn't reliably work on all versions of Tk
jpayne@68 230 # on macOS. Adding command definition below does seem to do the
jpayne@68 231 # right thing for now.
jpayne@68 232 root.createcommand('exit', flist.close_all_callback)
jpayne@68 233
jpayne@68 234 if isCarbonTk():
jpayne@68 235 # for Carbon AquaTk, replace the default Tk apple menu
jpayne@68 236 menudict['application'] = menu = Menu(menubar, name='apple',
jpayne@68 237 tearoff=0)
jpayne@68 238 menubar.add_cascade(label='IDLE', menu=menu)
jpayne@68 239 mainmenu.menudefs.insert(0,
jpayne@68 240 ('application', [
jpayne@68 241 ('About IDLE', '<<about-idle>>'),
jpayne@68 242 None,
jpayne@68 243 ]))
jpayne@68 244 if isCocoaTk():
jpayne@68 245 # replace default About dialog with About IDLE one
jpayne@68 246 root.createcommand('tkAboutDialog', about_dialog)
jpayne@68 247 # replace default "Help" item in Help menu
jpayne@68 248 root.createcommand('::tk::mac::ShowHelp', help_dialog)
jpayne@68 249 # remove redundant "IDLE Help" from menu
jpayne@68 250 del mainmenu.menudefs[-1][1][0]
jpayne@68 251
jpayne@68 252 def fixb2context(root):
jpayne@68 253 '''Removed bad AquaTk Button-2 (right) and Paste bindings.
jpayne@68 254
jpayne@68 255 They prevent context menu access and seem to be gone in AquaTk8.6.
jpayne@68 256 See issue #24801.
jpayne@68 257 '''
jpayne@68 258 root.unbind_class('Text', '<B2>')
jpayne@68 259 root.unbind_class('Text', '<B2-Motion>')
jpayne@68 260 root.unbind_class('Text', '<<PasteSelection>>')
jpayne@68 261
jpayne@68 262 def setupApp(root, flist):
jpayne@68 263 """
jpayne@68 264 Perform initial OS X customizations if needed.
jpayne@68 265 Called from pyshell.main() after initial calls to Tk()
jpayne@68 266
jpayne@68 267 There are currently three major versions of Tk in use on OS X:
jpayne@68 268 1. Aqua Cocoa Tk (native default since OS X 10.6)
jpayne@68 269 2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
jpayne@68 270 3. X11 (supported by some third-party distributors, deprecated)
jpayne@68 271 There are various differences among the three that affect IDLE
jpayne@68 272 behavior, primarily with menus, mouse key events, and accelerators.
jpayne@68 273 Some one-time customizations are performed here.
jpayne@68 274 Others are dynamically tested throughout idlelib by calls to the
jpayne@68 275 isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
jpayne@68 276 are initialized here as well.
jpayne@68 277 """
jpayne@68 278 if isAquaTk():
jpayne@68 279 hideTkConsole(root)
jpayne@68 280 overrideRootMenu(root, flist)
jpayne@68 281 addOpenEventSupport(root, flist)
jpayne@68 282 fixb2context(root)
jpayne@68 283
jpayne@68 284
jpayne@68 285 if __name__ == '__main__':
jpayne@68 286 from unittest import main
jpayne@68 287 main('idlelib.idle_test.test_macosx', verbosity=2)