Home | History | Annotate | Download | only in idlelib
      1 """
      2 A number of functions that enhance IDLE on Mac OSX.
      3 """
      4 import sys
      5 import Tkinter
      6 from os import path
      7 
      8 
      9 import warnings
     10 
     11 def runningAsOSXApp():
     12     warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()",
     13                         DeprecationWarning, stacklevel=2)
     14     return isAquaTk()
     15 
     16 def isCarbonAquaTk(root):
     17     warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()",
     18                         DeprecationWarning, stacklevel=2)
     19     return isCarbonTk()
     20 
     21 _tk_type = None
     22 
     23 def _initializeTkVariantTests(root):
     24     """
     25     Initializes OS X Tk variant values for
     26     isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
     27     """
     28     global _tk_type
     29     if sys.platform == 'darwin':
     30         ws = root.tk.call('tk', 'windowingsystem')
     31         if 'x11' in ws:
     32             _tk_type = "xquartz"
     33         elif 'aqua' not in ws:
     34             _tk_type = "other"
     35         elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
     36             _tk_type = "cocoa"
     37         else:
     38             _tk_type = "carbon"
     39     else:
     40         _tk_type = "other"
     41 
     42 def isAquaTk():
     43     """
     44     Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
     45     """
     46     assert _tk_type is not None
     47     return _tk_type == "cocoa" or _tk_type == "carbon"
     48 
     49 def isCarbonTk():
     50     """
     51     Returns True if IDLE is using a Carbon Aqua Tk (instead of the
     52     newer Cocoa Aqua Tk).
     53     """
     54     assert _tk_type is not None
     55     return _tk_type == "carbon"
     56 
     57 def isCocoaTk():
     58     """
     59     Returns True if IDLE is using a Cocoa Aqua Tk.
     60     """
     61     assert _tk_type is not None
     62     return _tk_type == "cocoa"
     63 
     64 def isXQuartz():
     65     """
     66     Returns True if IDLE is using an OS X X11 Tk.
     67     """
     68     assert _tk_type is not None
     69     return _tk_type == "xquartz"
     70 
     71 def tkVersionWarning(root):
     72     """
     73     Returns a string warning message if the Tk version in use appears to
     74     be one known to cause problems with IDLE.
     75     1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable.
     76     2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but
     77         can still crash unexpectedly.
     78     """
     79 
     80     if isCocoaTk():
     81         patchlevel = root.tk.call('info', 'patchlevel')
     82         if patchlevel not in ('8.5.7', '8.5.9'):
     83             return False
     84         return (r"WARNING: The version of Tcl/Tk ({0}) in use may"
     85                 r" be unstable.\n"
     86                 r"Visit http://www.python.org/download/mac/tcltk/"
     87                 r" for current information.".format(patchlevel))
     88     else:
     89         return False
     90 
     91 def addOpenEventSupport(root, flist):
     92     """
     93     This ensures that the application will respond to open AppleEvents, which
     94     makes is feasible to use IDLE as the default application for python files.
     95     """
     96     def doOpenFile(*args):
     97         for fn in args:
     98             flist.open(fn)
     99 
    100     # The command below is a hook in aquatk that is called whenever the app
    101     # receives a file open event. The callback can have multiple arguments,
    102     # one for every file that should be opened.
    103     root.createcommand("::tk::mac::OpenDocument", doOpenFile)
    104 
    105 def hideTkConsole(root):
    106     try:
    107         root.tk.call('console', 'hide')
    108     except Tkinter.TclError:
    109         # Some versions of the Tk framework don't have a console object
    110         pass
    111 
    112 def overrideRootMenu(root, flist):
    113     """
    114     Replace the Tk root menu by something that is more appropriate for
    115     IDLE with an Aqua Tk.
    116     """
    117     # The menu that is attached to the Tk root (".") is also used by AquaTk for
    118     # all windows that don't specify a menu of their own. The default menubar
    119     # contains a number of menus, none of which are appropriate for IDLE. The
    120     # Most annoying of those is an 'About Tck/Tk...' menu in the application
    121     # menu.
    122     #
    123     # This function replaces the default menubar by a mostly empty one, it
    124     # should only contain the correct application menu and the window menu.
    125     #
    126     # Due to a (mis-)feature of TkAqua the user will also see an empty Help
    127     # menu.
    128     from Tkinter import Menu
    129     from idlelib import Bindings
    130     from idlelib import WindowList
    131 
    132     closeItem = Bindings.menudefs[0][1][-2]
    133 
    134     # Remove the last 3 items of the file menu: a separator, close window and
    135     # quit. Close window will be reinserted just above the save item, where
    136     # it should be according to the HIG. Quit is in the application menu.
    137     del Bindings.menudefs[0][1][-3:]
    138     Bindings.menudefs[0][1].insert(6, closeItem)
    139 
    140     # Remove the 'About' entry from the help menu, it is in the application
    141     # menu
    142     del Bindings.menudefs[-1][1][0:2]
    143     # Remove the 'Configure Idle' entry from the options menu, it is in the
    144     # application menu as 'Preferences'
    145     del Bindings.menudefs[-2][1][0]
    146     menubar = Menu(root)
    147     root.configure(menu=menubar)
    148     menudict = {}
    149 
    150     menudict['windows'] = menu = Menu(menubar, name='windows', tearoff=0)
    151     menubar.add_cascade(label='Window', menu=menu, underline=0)
    152 
    153     def postwindowsmenu(menu=menu):
    154         end = menu.index('end')
    155         if end is None:
    156             end = -1
    157 
    158         if end > 0:
    159             menu.delete(0, end)
    160         WindowList.add_windows_to_menu(menu)
    161     WindowList.register_callback(postwindowsmenu)
    162 
    163     def about_dialog(event=None):
    164         "Handle Help 'About IDLE' event."
    165         # Synchronize with EditorWindow.EditorWindow.about_dialog.
    166         from idlelib import aboutDialog
    167         aboutDialog.AboutDialog(root, 'About IDLE')
    168 
    169     def config_dialog(event=None):
    170         "Handle Options 'Configure IDLE' event."
    171         # Synchronize with EditorWindow.EditorWindow.config_dialog.
    172         from idlelib import configDialog
    173         root.instance_dict = flist.inversedict
    174         configDialog.ConfigDialog(root, 'Settings')
    175 
    176     def help_dialog(event=None):
    177         "Handle Help 'IDLE Help' event."
    178         # Synchronize with EditorWindow.EditorWindow.help_dialog.
    179         from idlelib import help
    180         help.show_idlehelp(root)
    181 
    182     root.bind('<<about-idle>>', about_dialog)
    183     root.bind('<<open-config-dialog>>', config_dialog)
    184     root.createcommand('::tk::mac::ShowPreferences', config_dialog)
    185     if flist:
    186         root.bind('<<close-all-windows>>', flist.close_all_callback)
    187 
    188         # The binding above doesn't reliably work on all versions of Tk
    189         # on MacOSX. Adding command definition below does seem to do the
    190         # right thing for now.
    191         root.createcommand('exit', flist.close_all_callback)
    192 
    193     if isCarbonTk():
    194         # for Carbon AquaTk, replace the default Tk apple menu
    195         menudict['application'] = menu = Menu(menubar, name='apple',
    196                                               tearoff=0)
    197         menubar.add_cascade(label='IDLE', menu=menu)
    198         Bindings.menudefs.insert(0,
    199             ('application', [
    200                 ('About IDLE', '<<about-idle>>'),
    201                     None,
    202                 ]))
    203         tkversion = root.tk.eval('info patchlevel')
    204         if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
    205             # for earlier AquaTk versions, supply a Preferences menu item
    206             Bindings.menudefs[0][1].append(
    207                     ('_Preferences....', '<<open-config-dialog>>'),
    208                 )
    209     if isCocoaTk():
    210         # replace default About dialog with About IDLE one
    211         root.createcommand('tkAboutDialog', about_dialog)
    212         # replace default "Help" item in Help menu
    213         root.createcommand('::tk::mac::ShowHelp', help_dialog)
    214         # remove redundant "IDLE Help" from menu
    215         del Bindings.menudefs[-1][1][0]
    216 
    217 def setupApp(root, flist):
    218     """
    219     Perform initial OS X customizations if needed.
    220     Called from PyShell.main() after initial calls to Tk()
    221 
    222     There are currently three major versions of Tk in use on OS X:
    223         1. Aqua Cocoa Tk (native default since OS X 10.6)
    224         2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
    225         3. X11 (supported by some third-party distributors, deprecated)
    226     There are various differences among the three that affect IDLE
    227     behavior, primarily with menus, mouse key events, and accelerators.
    228     Some one-time customizations are performed here.
    229     Others are dynamically tested throughout idlelib by calls to the
    230     isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
    231     are initialized here as well.
    232     """
    233     _initializeTkVariantTests(root)
    234     if isAquaTk():
    235         hideTkConsole(root)
    236         overrideRootMenu(root, flist)
    237         addOpenEventSupport(root, flist)
    238