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)