# Manager widget for menus.

import string
import types
import Tkinter
import Pmw

def _findHotkey(hotkeys, name, traverseSpec):

    lowerName = string.lower(name)

    if traverseSpec is not None:
	if type(traverseSpec) == types.StringType:
	    lowerLetter = string.lower(traverseSpec)
	    if traverseSpec in name and lowerLetter not in hotkeys:
		hotkeys.append(lowerLetter)
		return string.index(name, traverseSpec), traverseSpec
	elif type(traverseSpec) == types.IntType:
	    if traverseSpec < len(name):
		hotkeys.append(lowerName[traverseSpec])
		return traverseSpec, lowerName[traverseSpec]

    for letter_index in range(len(name)):
	letter = lowerName[letter_index]
	if letter in (string.digits + string.letters) and letter not in hotkeys:
	    hotkeys.append(letter)
	    return letter_index, letter

    return None, None

class MenuBar(Pmw.MegaWidget):

    def __init__(self, parent = None, **kw):

	# Define the megawidget options.
	INITOPT = Pmw.INITOPT
	optiondefs = (
	    ('balloon',     None,       None),
	    ('padx',        0,          INITOPT),
	    ('hotkeys',     1,          INITOPT),
	)
	self.defineoptions(kw, optiondefs)

	# Initialise the base class (after defining the options).
	Pmw.MegaWidget.__init__(self, parent)

	# Initialise instance variables.
	self._menuHelpDict = {}

	# Map from a menu name to a tuple of information about the
	# menu's hotkeys.  The first item in the tuple is a list of
	# hotkeys of the items in the menu.  The second item is the
	# parent menu (required because of cascaded menus).  The third
	# item is the hotkey of this menu in it parent.  The last two
	# are used when menus are deleted.  Includes information for
	# the toplevel menubuttons as item None.
	self._menuHotkeys = {None : ([], None, None)}

	# Check keywords and initialise options.
	self.initialiseoptions(MenuBar)

    def deletemenuitems(self, menuName, start='0', end=None):
        if (menuName + '-menu') in self.components():
	    self.component(menuName + '-menu').delete(start, end)
	if self._menuHelpDict.has_key(menuName + '-menu'):
	    if end is None:
		del self._menuHelpDict[menuName + '-menu'][start]
	    else:
		self._menuHelpDict[menuName + '-menu'][start:end+1] = []

    def deletemenu(self, menuName):
	"""Delete should be called for cascaded menus before main menus.
	"""
        if (menuName + '-menu') in self.components():
	    self.destroycomponent(menuName + '-menu')
        if (menuName + '-button') in self.components():
	    self.destroycomponent(menuName + '-button')
	if self._menuHelpDict.has_key(menuName + '-menu'):
	    del self._menuHelpDict[menuName + '-menu']
	if self._menuHotkeys.has_key(menuName):
	    parent = self._menuHotkeys[menuName][1]
	    hotkey = self._menuHotkeys[menuName][2]
	    hotkeyList = self._menuHotkeys[parent][0]
	    if hotkey in hotkeyList:
		hotkeyList.remove(hotkey)
	    del self._menuHotkeys[menuName]

    def disableall(self):
	for item in self.components():
	    if len(item) > 6 and item[-6:] == 'button':
	        self.component(item).configure(state='disabled')

    def enableall(self):
	for item in self.components():
	    if len(item) > 6 and item[-6:] == 'button':
	        self.component(item).configure(state='normal')

    def addcascademenu(self, menuName, submenu, help='',
	    traverseSpec=None, **kw):
	if (submenu + '-menu') in self.components():
	    raise ValueError, 'submenu "%s" already exists' % submenu

	parentmenu_w = self.component(menuName + '-menu')
	submenu_w = self.createcomponent(submenu + '-menu',
	        (), 'Menu',
		Tkinter.Menu,(parentmenu_w,), tearoff=0)
	self._menuHelpDict[submenu] = []

	kw['menu'] = submenu_w
	if not kw.has_key('label'):
	    kw['label'] = submenu

	if self['hotkeys']:
	    hotkey = None
	    if not kw.has_key('underline'):
		hotkeyList = self._menuHotkeys[menuName][0]
		underline, hotkey = \
		    _findHotkey(hotkeyList, kw['label'], traverseSpec)
		if underline is not None:
		    kw['underline'] = underline

	    self._menuHotkeys[submenu] = ([], menuName, hotkey)

	self._menuHelpDict[menuName].append(help)
	apply(parentmenu_w.add_cascade, (), kw)

        # Need to put this binding after the class bindings so that
        # submenu_w.index() does not lag behind.
        _bindtag = 'PmwMenuBar' + str(self) + submenu
        self.bind_class(_bindtag, '<Motion>',
            lambda event=None, self=self, menuName=submenu:
                    self._menuHelp(menuName))
        submenu_w.bindtags(submenu_w.bindtags() + (_bindtag,))
        submenu_w.bind('<Leave>', self._resetHelpmessage)

    def addmenu(self, menuName, balloonHelp, statusHelp=None,
	    side='left', traverseSpec=None, **kw):
	if (menuName + '-button') in self.components():
	    raise ValueError, 'menu "%s" already exists' % menuName
	if not kw.has_key('text'):
	    kw['text'] = menuName

	if self['hotkeys']:
	    hotkey = None
	    if not kw.has_key('underline'):
		hotkeyList = self._menuHotkeys[None][0]
		underline, hotkey = \
		    _findHotkey(hotkeyList, kw['text'], traverseSpec)
		if underline is not None:
		    kw['underline'] = underline
	    self._menuHotkeys[menuName] = ([], None, hotkey)

	button = apply(self.createcomponent, (menuName + '-button',
		(), 'Button',
		Tkinter.Menubutton, (self.interior(),)), kw)
	button.pack(side=side, padx = self['padx'])
	balloon = self['balloon']
	if balloon is not None:
	    balloon.bind(button, balloonHelp, statusHelp)

	menu = self.createcomponent(menuName + '-menu',
		(), 'Menu',
		Tkinter.Menu, (button,), tearoff=0)
	button.configure(menu = menu)
	self._menuHelpDict[menuName] = []

	# Need to put this binding after the class bindings so that
	# menu.index() does not lag behind.
	_bindtag = 'PmwMenuBar' + str(self) + menuName
	self.bind_class(_bindtag, '<Motion>',
	    lambda event=None, self=self, menuName=menuName:
		    self._menuHelp(menuName))
	menu.bindtags(menu.bindtags() + (_bindtag,))
	menu.bind('<Leave>', self._resetHelpmessage)

	return button

    def addmenuitem(self, menuName, type, help='', traverseSpec=None, **kw):
	menu = self.component(menuName + '-menu')
	if (self['hotkeys'] and type != 'separator' and
		not kw.has_key('underline') and kw.has_key('label')):
	    hotkeyList = self._menuHotkeys[menuName][0]
	    underline, hotkey = \
		_findHotkey(hotkeyList, kw['label'], traverseSpec)
	    if underline is not None:
		kw['underline'] = underline

	if type == 'command':
	    command = menu.add_command
	elif type == 'separator':
	    command = menu.add_separator
	elif type == 'checkbutton':
	    command = menu.add_checkbutton
	elif type == 'radiobutton':
	    command = menu.add_radiobutton
	elif type == 'cascade':
	    command = menu.add_cascade
	else:
	    raise ValueError, 'unknown menuitem type "%s"' % type

	self._menuHelpDict[menuName].append(help)
	apply(command, (), kw)

    def _menuHelp(self, menuName):
	menu = self.component(menuName + '-menu')
        index = menu.index('active')
	if index is None:
	    self._resetHelpmessage()
	else:
	    balloon = self['balloon']
	    if balloon is not None:
		help = self._menuHelpDict[menuName][index]
		balloon.showstatus(help)

    def _resetHelpmessage(self, event=None):
	balloon = self['balloon']
	if balloon is not None:
	    balloon.clearstatus()