Issue #17654: Ensure IDLE menus are customized properly on OS X for
non-framework builds and for all variants of Tk.
diff --git a/Lib/idlelib/macosxSupport.py b/Lib/idlelib/macosxSupport.py
index af6652f..4f5259c 100644
--- a/Lib/idlelib/macosxSupport.py
+++ b/Lib/idlelib/macosxSupport.py
@@ -1,38 +1,72 @@
"""
-A number of function that enhance IDLE on MacOSX when it used as a normal
-GUI application (as opposed to an X11 application).
+A number of functions that enhance IDLE on Mac OSX.
"""
import sys
import Tkinter
from os import path
-_appbundle = None
+import warnings
def runningAsOSXApp():
- """
- Returns True if Python is running from within an app on OSX.
- If so, assume that Python was built with Aqua Tcl/Tk rather than
- X11 Tcl/Tk.
- """
- global _appbundle
- if _appbundle is None:
- _appbundle = (sys.platform == 'darwin' and '.app' in sys.executable)
- return _appbundle
-
-_carbonaquatk = None
+ warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()",
+ DeprecationWarning, stacklevel=2)
+ return isAquaTk()
def isCarbonAquaTk(root):
+ warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()",
+ DeprecationWarning, stacklevel=2)
+ return isCarbonTk()
+
+_tk_type = None
+
+def _initializeTkVariantTests(root):
+ """
+ Initializes OS X Tk variant values for
+ isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
+ """
+ global _tk_type
+ if sys.platform == 'darwin':
+ ws = root.tk.call('tk', 'windowingsystem')
+ if 'x11' in ws:
+ _tk_type = "xquartz"
+ elif 'aqua' not in ws:
+ _tk_type = "other"
+ elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
+ _tk_type = "cocoa"
+ else:
+ _tk_type = "carbon"
+ else:
+ _tk_type = "other"
+
+def isAquaTk():
+ """
+ Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
+ """
+ assert _tk_type is not None
+ return _tk_type == "cocoa" or _tk_type == "carbon"
+
+def isCarbonTk():
"""
Returns True if IDLE is using a Carbon Aqua Tk (instead of the
newer Cocoa Aqua Tk).
"""
- global _carbonaquatk
- if _carbonaquatk is None:
- _carbonaquatk = (runningAsOSXApp() and
- 'aqua' in root.tk.call('tk', 'windowingsystem') and
- 'AppKit' not in root.tk.call('winfo', 'server', '.'))
- return _carbonaquatk
+ assert _tk_type is not None
+ return _tk_type == "carbon"
+
+def isCocoaTk():
+ """
+ Returns True if IDLE is using a Cocoa Aqua Tk.
+ """
+ assert _tk_type is not None
+ return _tk_type == "cocoa"
+
+def isXQuartz():
+ """
+ Returns True if IDLE is using an OS X X11 Tk.
+ """
+ assert _tk_type is not None
+ return _tk_type == "xquartz"
def tkVersionWarning(root):
"""
@@ -43,8 +77,7 @@
can still crash unexpectedly.
"""
- if (runningAsOSXApp() and
- ('AppKit' in root.tk.call('winfo', 'server', '.')) ):
+ if isCocoaTk():
patchlevel = root.tk.call('info', 'patchlevel')
if patchlevel not in ('8.5.7', '8.5.9'):
return False
@@ -78,8 +111,8 @@
def overrideRootMenu(root, flist):
"""
- Replace the Tk root menu by something that's more appropriate for
- IDLE.
+ Replace the Tk root menu by something that is more appropriate for
+ IDLE with an Aqua Tk.
"""
# The menu that is attached to the Tk root (".") is also used by AquaTk for
# all windows that don't specify a menu of their own. The default menubar
@@ -98,6 +131,22 @@
from idlelib import WindowList
from idlelib.MultiCall import MultiCallCreator
+ closeItem = Bindings.menudefs[0][1][-2]
+
+ # Remove the last 3 items of the file menu: a separator, close window and
+ # quit. Close window will be reinserted just above the save item, where
+ # it should be according to the HIG. Quit is in the application menu.
+ del Bindings.menudefs[0][1][-3:]
+ Bindings.menudefs[0][1].insert(6, closeItem)
+
+ # Remove the 'About' entry from the help menu, it is in the application
+ # menu
+ del Bindings.menudefs[-1][1][0:2]
+
+ # Remove the 'Configure' entry from the options menu, it is in the
+ # application menu as 'Preferences'
+ del Bindings.menudefs[-2][1][0:2]
+
menubar = Menu(root)
root.configure(menu=menubar)
menudict = {}
@@ -140,7 +189,7 @@
# right thing for now.
root.createcommand('exit', flist.close_all_callback)
- if isCarbonAquaTk(root):
+ if isCarbonTk():
# for Carbon AquaTk, replace the default Tk apple menu
menudict['application'] = menu = Menu(menubar, name='apple')
menubar.add_cascade(label='IDLE', menu=menu)
@@ -155,8 +204,7 @@
Bindings.menudefs[0][1].append(
('_Preferences....', '<<open-config-dialog>>'),
)
- else:
- # assume Cocoa AquaTk
+ if isCocoaTk():
# replace default About dialog with About IDLE one
root.createcommand('tkAboutDialog', about_dialog)
# replace default "Help" item in Help menu
@@ -166,10 +214,22 @@
def setupApp(root, flist):
"""
- Perform setup for the OSX application bundle.
- """
- if not runningAsOSXApp(): return
+ Perform initial OS X customizations if needed.
+ Called from PyShell.main() after initial calls to Tk()
- hideTkConsole(root)
- overrideRootMenu(root, flist)
- addOpenEventSupport(root, flist)
+ There are currently three major versions of Tk in use on OS X:
+ 1. Aqua Cocoa Tk (native default since OS X 10.6)
+ 2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
+ 3. X11 (supported by some third-party distributors, deprecated)
+ There are various differences among the three that affect IDLE
+ behavior, primarily with menus, mouse key events, and accelerators.
+ Some one-time customizations are performed here.
+ Others are dynamically tested throughout idlelib by calls to the
+ isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
+ are initialized here as well.
+ """
+ _initializeTkVariantTests(root)
+ if isAquaTk():
+ hideTkConsole(root)
+ overrideRootMenu(root, flist)
+ addOpenEventSupport(root, flist)