annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/idlelib/macosx.py @ 69:33d812a61356

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