Commit fb3cec77 authored by Ned Deily's avatar Ned Deily

Issue #17654: Ensure IDLE menus are customized properly on OS X for

non-framework builds and for all variants of Tk.
parent 99313194
...@@ -8,9 +8,14 @@ the PythonShell window, and a Format menu which is only present in the Editor ...@@ -8,9 +8,14 @@ the PythonShell window, and a Format menu which is only present in the Editor
windows. windows.
""" """
import sys
from idlelib.configHandler import idleConf from idlelib.configHandler import idleConf
from idlelib import macosxSupport
# Warning: menudefs is altered in macosxSupport.overrideRootMenu()
# after it is determined that an OS X Aqua Tk is in use,
# which cannot be done until after Tk() is first called.
# Do not alter the 'file', 'options', or 'help' cascades here
# without altering overrideRootMenu() as well.
# TODO: Make this more robust
menudefs = [ menudefs = [
# underscore prefixes character to underscore # underscore prefixes character to underscore
...@@ -81,27 +86,4 @@ menudefs = [ ...@@ -81,27 +86,4 @@ menudefs = [
]), ]),
] ]
if macosxSupport.runningAsOSXApp():
# Running as a proper MacOS application bundle. This block restructures
# the menus a little to make them conform better to the HIG.
quitItem = menudefs[0][1][-1]
closeItem = 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 menudefs[0][1][-3:]
menudefs[0][1].insert(6, closeItem)
# Remove the 'About' entry from the help menu, it is in the application
# menu
del menudefs[-1][1][0:2]
# Remove the 'Configure' entry from the options menu, it is in the
# application menu as 'Preferences'
del menudefs[-2][1][0:2]
default_keydefs = idleConf.GetCurrentKeySet() default_keydefs = idleConf.GetCurrentKeySet()
del sys
...@@ -322,7 +322,7 @@ class Debugger: ...@@ -322,7 +322,7 @@ class Debugger:
class StackViewer(ScrolledList): class StackViewer(ScrolledList):
def __init__(self, master, flist, gui): def __init__(self, master, flist, gui):
if macosxSupport.runningAsOSXApp(): if macosxSupport.isAquaTk():
# At least on with the stock AquaTk version on OSX 10.4 you'll # At least on with the stock AquaTk version on OSX 10.4 you'll
# get an shaking GUI that eventually kills IDLE if the width # get an shaking GUI that eventually kills IDLE if the width
# argument is specified. # argument is specified.
......
...@@ -109,8 +109,8 @@ class EditorWindow(object): ...@@ -109,8 +109,8 @@ class EditorWindow(object):
'Python%s.chm' % _sphinx_version()) 'Python%s.chm' % _sphinx_version())
if os.path.isfile(chmfile): if os.path.isfile(chmfile):
dochome = chmfile dochome = chmfile
elif macosxSupport.runningAsOSXApp(): elif sys.platform == 'darwin':
# documentation is stored inside the python framework # documentation may be stored inside a python framework
dochome = os.path.join(sys.base_prefix, dochome = os.path.join(sys.base_prefix,
'Resources/English.lproj/Documentation/index.html') 'Resources/English.lproj/Documentation/index.html')
dochome = os.path.normpath(dochome) dochome = os.path.normpath(dochome)
...@@ -166,7 +166,7 @@ class EditorWindow(object): ...@@ -166,7 +166,7 @@ class EditorWindow(object):
self.top.protocol("WM_DELETE_WINDOW", self.close) self.top.protocol("WM_DELETE_WINDOW", self.close)
self.top.bind("<<close-window>>", self.close_event) self.top.bind("<<close-window>>", self.close_event)
if macosxSupport.runningAsOSXApp(): if macosxSupport.isAquaTk():
# Command-W on editorwindows doesn't work without this. # Command-W on editorwindows doesn't work without this.
text.bind('<<close-window>>', self.close_event) text.bind('<<close-window>>', self.close_event)
# Some OS X systems have only one mouse button, # Some OS X systems have only one mouse button,
...@@ -409,7 +409,7 @@ class EditorWindow(object): ...@@ -409,7 +409,7 @@ class EditorWindow(object):
def set_status_bar(self): def set_status_bar(self):
self.status_bar = self.MultiStatusBar(self.top) self.status_bar = self.MultiStatusBar(self.top)
if macosxSupport.runningAsOSXApp(): if sys.platform == "darwin":
# Insert some padding to avoid obscuring some of the statusbar # Insert some padding to avoid obscuring some of the statusbar
# by the resize widget. # by the resize widget.
self.status_bar.set_label('_padding1', ' ', side=RIGHT) self.status_bar.set_label('_padding1', ' ', side=RIGHT)
...@@ -436,7 +436,7 @@ class EditorWindow(object): ...@@ -436,7 +436,7 @@ class EditorWindow(object):
("help", "_Help"), ("help", "_Help"),
] ]
if macosxSupport.runningAsOSXApp(): if sys.platform == "darwin":
menu_specs[-2] = ("windows", "_Window") menu_specs[-2] = ("windows", "_Window")
...@@ -447,7 +447,7 @@ class EditorWindow(object): ...@@ -447,7 +447,7 @@ class EditorWindow(object):
underline, label = prepstr(label) underline, label = prepstr(label)
menudict[name] = menu = Menu(mbar, name=name) menudict[name] = menu = Menu(mbar, name=name)
mbar.add_cascade(label=label, menu=menu, underline=underline) mbar.add_cascade(label=label, menu=menu, underline=underline)
if macosxSupport.isCarbonAquaTk(self.root): if macosxSupport.isCarbonTk():
# Insert the application menu # Insert the application menu
menudict['application'] = menu = Menu(mbar, name='apple') menudict['application'] = menu = Menu(mbar, name='apple')
mbar.add_cascade(label='IDLE', menu=menu) mbar.add_cascade(label='IDLE', menu=menu)
...@@ -1673,7 +1673,7 @@ def get_accelerator(keydefs, eventname): ...@@ -1673,7 +1673,7 @@ def get_accelerator(keydefs, eventname):
keylist = keydefs.get(eventname) keylist = keydefs.get(eventname)
# issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5 # issue10940: temporary workaround to prevent hang with OS X Cocoa Tk 8.5
# if not keylist: # if not keylist:
if (not keylist) or (macosxSupport.runningAsOSXApp() and eventname in { if (not keylist) or (macosxSupport.isCocoaTk() and eventname in {
"<<open-module>>", "<<open-module>>",
"<<goto-line>>", "<<goto-line>>",
"<<change-indentwidth>>"}): "<<change-indentwidth>>"}):
......
...@@ -32,7 +32,6 @@ Each function will be called at most once for each event. ...@@ -32,7 +32,6 @@ Each function will be called at most once for each event.
import sys import sys
import re import re
import tkinter import tkinter
from idlelib import macosxSupport
# the event type constants, which define the meaning of mc_type # the event type constants, which define the meaning of mc_type
MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3; MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3;
...@@ -45,7 +44,7 @@ MC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5 ...@@ -45,7 +44,7 @@ MC_SHIFT = 1<<0; MC_CONTROL = 1<<2; MC_ALT = 1<<3; MC_META = 1<<5
MC_OPTION = 1<<6; MC_COMMAND = 1<<7 MC_OPTION = 1<<6; MC_COMMAND = 1<<7
# define the list of modifiers, to be used in complex event types. # define the list of modifiers, to be used in complex event types.
if macosxSupport.runningAsOSXApp(): if sys.platform == "darwin":
_modifiers = (("Shift",), ("Control",), ("Option",), ("Command",)) _modifiers = (("Shift",), ("Control",), ("Option",), ("Command",))
_modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND) _modifier_masks = (MC_SHIFT, MC_CONTROL, MC_OPTION, MC_COMMAND)
else: else:
......
...@@ -844,7 +844,7 @@ class PyShell(OutputWindow): ...@@ -844,7 +844,7 @@ class PyShell(OutputWindow):
("help", "_Help"), ("help", "_Help"),
] ]
if macosxSupport.runningAsOSXApp(): if sys.platform == "darwin":
menu_specs[-2] = ("windows", "_Window") menu_specs[-2] = ("windows", "_Window")
...@@ -1560,7 +1560,7 @@ def main(): ...@@ -1560,7 +1560,7 @@ def main():
shell = flist.open_shell() shell = flist.open_shell()
if not shell: if not shell:
return # couldn't open shell return # couldn't open shell
if macosxSupport.runningAsOSXApp() and flist.dict: if macosxSupport.isAquaTk() and flist.dict:
# On OSX: when the user has double-clicked on a file that causes # On OSX: when the user has double-clicked on a file that causes
# IDLE to be launched the shell window will open just in front of # IDLE to be launched the shell window will open just in front of
# the file she wants to see. Lower the interpreter window when # the file she wants to see. Lower the interpreter window when
......
...@@ -53,7 +53,7 @@ class ScriptBinding: ...@@ -53,7 +53,7 @@ class ScriptBinding:
self.flist = self.editwin.flist self.flist = self.editwin.flist
self.root = self.editwin.root self.root = self.editwin.root
if macosxSupport.runningAsOSXApp(): if macosxSupport.isCocoaTk():
self.editwin.text_frame.bind('<<run-module-event-2>>', self._run_module_event) self.editwin.text_frame.bind('<<run-module-event-2>>', self._run_module_event)
def check_module_event(self, event): def check_module_event(self, event):
...@@ -114,7 +114,7 @@ class ScriptBinding: ...@@ -114,7 +114,7 @@ class ScriptBinding:
shell.set_warning_stream(saved_stream) shell.set_warning_stream(saved_stream)
def run_module_event(self, event): def run_module_event(self, event):
if macosxSupport.runningAsOSXApp(): if macosxSupport.isCocoaTk():
# Tk-Cocoa in MacOSX is broken until at least # Tk-Cocoa in MacOSX is broken until at least
# Tk 8.5.9, and without this rather # Tk 8.5.9, and without this rather
# crude workaround IDLE would hang when a user # crude workaround IDLE would hang when a user
......
...@@ -32,7 +32,7 @@ def zoom_height(top): ...@@ -32,7 +32,7 @@ def zoom_height(top):
newy = 0 newy = 0
newheight = newheight - 72 newheight = newheight - 72
elif macosxSupport.runningAsOSXApp(): elif macosxSupport.isAquaTk():
# The '88' below is a magic number that avoids placing the bottom # The '88' below is a magic number that avoids placing the bottom
# of the window below the panel on my machine. I don't know how # of the window below the panel on my machine. I don't know how
# to calculate the correct value for this with tkinter. # to calculate the correct value for this with tkinter.
......
...@@ -74,7 +74,7 @@ class ConfigDialog(Toplevel): ...@@ -74,7 +74,7 @@ class ConfigDialog(Toplevel):
frameActionButtons = Frame(self,pady=2) frameActionButtons = Frame(self,pady=2)
#action buttons #action buttons
if macosxSupport.runningAsOSXApp(): if macosxSupport.isAquaTk():
# Surpress the padx and pady arguments when # Surpress the padx and pady arguments when
# running as IDLE.app, otherwise the text # running as IDLE.app, otherwise the text
# on these buttons will not be readable. # on these buttons will not be readable.
......
...@@ -20,7 +20,6 @@ configuration problem notification and resolution. ...@@ -20,7 +20,6 @@ configuration problem notification and resolution.
import os import os
import sys import sys
from idlelib import macosxSupport
from configparser import ConfigParser, NoOptionError, NoSectionError from configparser import ConfigParser, NoOptionError, NoSectionError
class InvalidConfigType(Exception): pass class InvalidConfigType(Exception): pass
...@@ -527,10 +526,13 @@ class IdleConf: ...@@ -527,10 +526,13 @@ class IdleConf:
def GetCurrentKeySet(self): def GetCurrentKeySet(self):
result = self.GetKeySet(self.CurrentKeys()) result = self.GetKeySet(self.CurrentKeys())
if macosxSupport.runningAsOSXApp(): if sys.platform == "darwin":
# We're using AquaTk, replace all keybingings that use the # OS X Tk variants do not support the "Alt" keyboard modifier.
# Alt key by ones that use the Option key because the former # So replace all keybingings that use "Alt" with ones that
# don't work reliably. # use the "Option" keyboard modifier.
# TO DO: the "Option" modifier does not work properly for
# Cocoa Tk and XQuartz Tk so we should not use it
# in default OS X KeySets.
for k, v in result.items(): for k, v in result.items():
v2 = [ x.replace('<Alt-', '<Option-') for x in v ] v2 = [ x.replace('<Alt-', '<Option-') for x in v ]
if v != v2: if v != v2:
......
...@@ -4,7 +4,7 @@ Dialog for building Tkinter accelerator key bindings ...@@ -4,7 +4,7 @@ Dialog for building Tkinter accelerator key bindings
from tkinter import * from tkinter import *
import tkinter.messagebox as tkMessageBox import tkinter.messagebox as tkMessageBox
import string import string
from idlelib import macosxSupport import sys
class GetKeysDialog(Toplevel): class GetKeysDialog(Toplevel):
def __init__(self,parent,title,action,currentKeySequences): def __init__(self,parent,title,action,currentKeySequences):
...@@ -133,8 +133,7 @@ class GetKeysDialog(Toplevel): ...@@ -133,8 +133,7 @@ class GetKeysDialog(Toplevel):
order is also important: key binding equality depends on it, so order is also important: key binding equality depends on it, so
config-keys.def must use the same ordering. config-keys.def must use the same ordering.
""" """
import sys if sys.platform == "darwin":
if macosxSupport.runningAsOSXApp():
self.modifiers = ['Shift', 'Control', 'Option', 'Command'] self.modifiers = ['Shift', 'Control', 'Option', 'Command']
else: else:
self.modifiers = ['Control', 'Alt', 'Shift'] self.modifiers = ['Control', 'Alt', 'Shift']
......
""" """
A number of function that enhance IDLE on MacOSX when it used as a normal A number of functions that enhance IDLE on Mac OSX.
GUI application (as opposed to an X11 application).
""" """
import sys import sys
import tkinter import tkinter
from os import path from os import path
import warnings
def runningAsOSXApp():
warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()",
DeprecationWarning, stacklevel=2)
return isAquaTk()
_appbundle = None def isCarbonAquaTk(root):
warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()",
DeprecationWarning, stacklevel=2)
return isCarbonTk()
def runningAsOSXApp(): _tk_type = None
def _initializeTkVariantTests(root):
"""
Initializes OS X Tk variant values for
isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
""" """
Returns True if Python is running from within an app on OSX. global _tk_type
If so, the various OS X customizations will be triggered later (menu if sys.platform == 'darwin':
fixup, et al). (Originally, this test was supposed to condition ws = root.tk.call('tk', 'windowingsystem')
behavior on whether IDLE was running under Aqua Tk rather than if 'x11' in ws:
under X11 Tk but that does not work since a framework build _tk_type = "xquartz"
could be linked with X11. For several releases, this test actually elif 'aqua' not in ws:
differentiates between whether IDLE is running from a framework or _tk_type = "other"
not. As a future enhancement, it should be considered whether there elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
should be a difference based on framework and any needed X11 adaptions _tk_type = "cocoa"
should be made dependent on a new function that actually tests for X11.) else:
""" _tk_type = "carbon"
global _appbundle else:
if _appbundle is None: _tk_type = "other"
_appbundle = sys.platform == 'darwin'
if _appbundle:
import sysconfig
_appbundle = bool(sysconfig.get_config_var('PYTHONFRAMEWORK'))
return _appbundle
_carbonaquatk = None
def isCarbonAquaTk(root): 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 Returns True if IDLE is using a Carbon Aqua Tk (instead of the
newer Cocoa Aqua Tk). newer Cocoa Aqua Tk).
""" """
global _carbonaquatk assert _tk_type is not None
if _carbonaquatk is None: return _tk_type == "carbon"
_carbonaquatk = (runningAsOSXApp() and
'aqua' in root.tk.call('tk', 'windowingsystem') and def isCocoaTk():
'AppKit' not in root.tk.call('winfo', 'server', '.')) """
return _carbonaquatk 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): def tkVersionWarning(root):
""" """
...@@ -53,8 +75,7 @@ def tkVersionWarning(root): ...@@ -53,8 +75,7 @@ def tkVersionWarning(root):
can still crash unexpectedly. can still crash unexpectedly.
""" """
if (runningAsOSXApp() and if isCocoaTk():
('AppKit' in root.tk.call('winfo', 'server', '.')) ):
patchlevel = root.tk.call('info', 'patchlevel') patchlevel = root.tk.call('info', 'patchlevel')
if patchlevel not in ('8.5.7', '8.5.9'): if patchlevel not in ('8.5.7', '8.5.9'):
return False return False
...@@ -88,8 +109,8 @@ def hideTkConsole(root): ...@@ -88,8 +109,8 @@ def hideTkConsole(root):
def overrideRootMenu(root, flist): def overrideRootMenu(root, flist):
""" """
Replace the Tk root menu by something that's more appropriate for Replace the Tk root menu by something that is more appropriate for
IDLE. IDLE with an Aqua Tk.
""" """
# The menu that is attached to the Tk root (".") is also used by AquaTk for # 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 # all windows that don't specify a menu of their own. The default menubar
...@@ -108,6 +129,22 @@ def overrideRootMenu(root, flist): ...@@ -108,6 +129,22 @@ def overrideRootMenu(root, flist):
from idlelib import WindowList from idlelib import WindowList
from idlelib.MultiCall import MultiCallCreator 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) menubar = Menu(root)
root.configure(menu=menubar) root.configure(menu=menubar)
menudict = {} menudict = {}
...@@ -156,7 +193,7 @@ def overrideRootMenu(root, flist): ...@@ -156,7 +193,7 @@ def overrideRootMenu(root, flist):
# right thing for now. # right thing for now.
root.createcommand('exit', flist.close_all_callback) root.createcommand('exit', flist.close_all_callback)
if isCarbonAquaTk(root): if isCarbonTk():
# for Carbon AquaTk, replace the default Tk apple menu # for Carbon AquaTk, replace the default Tk apple menu
menudict['application'] = menu = Menu(menubar, name='apple') menudict['application'] = menu = Menu(menubar, name='apple')
menubar.add_cascade(label='IDLE', menu=menu) menubar.add_cascade(label='IDLE', menu=menu)
...@@ -171,8 +208,7 @@ def overrideRootMenu(root, flist): ...@@ -171,8 +208,7 @@ def overrideRootMenu(root, flist):
Bindings.menudefs[0][1].append( Bindings.menudefs[0][1].append(
('_Preferences....', '<<open-config-dialog>>'), ('_Preferences....', '<<open-config-dialog>>'),
) )
else: if isCocoaTk():
# assume Cocoa AquaTk
# replace default About dialog with About IDLE one # replace default About dialog with About IDLE one
root.createcommand('tkAboutDialog', about_dialog) root.createcommand('tkAboutDialog', about_dialog)
# replace default "Help" item in Help menu # replace default "Help" item in Help menu
...@@ -182,10 +218,22 @@ def overrideRootMenu(root, flist): ...@@ -182,10 +218,22 @@ def overrideRootMenu(root, flist):
def setupApp(root, flist): def setupApp(root, flist):
""" """
Perform setup for the OSX application bundle. Perform initial OS X customizations if needed.
Called from PyShell.main() after initial calls to Tk()
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.
""" """
if not runningAsOSXApp(): return _initializeTkVariantTests(root)
if isAquaTk():
hideTkConsole(root) hideTkConsole(root)
overrideRootMenu(root, flist) overrideRootMenu(root, flist)
addOpenEventSupport(root, flist) addOpenEventSupport(root, flist)
...@@ -94,6 +94,12 @@ Library ...@@ -94,6 +94,12 @@ Library
(Original patches by Hirokazu Yamamoto and Amaury Forgeot d'Arc, with (Original patches by Hirokazu Yamamoto and Amaury Forgeot d'Arc, with
suggested wording by David Gutteridge) suggested wording by David Gutteridge)
IDLE
----
- Issue #17654: Ensure IDLE menus are customized properly on OS X for
non-framework builds and for all variants of Tk.
Documentation Documentation
------------- -------------
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment