#
# Widget implementing functionality similar to that of OptionMenu widget
#
# The widget defines two components: label and menu, menu being Tkinter's 
# OptionMenu
#
# The public methods are:
#
# get()         - returns the current selection
# index(name)   - returns the integer index of the item 'name' 
# setitems(items, active = None)
#               - set's the list of items displayed, selects the item indexed
#               by 'active' as active (calls the callback too). Otherwise,
#               first item in the list is selected.
# select(index) - selects the item indexed by index. If command option is given,
#               the command is called
#
# If an option 'variable' is given, the current value of the menu will be
# available via the variable.
#
# Author Roman Sulzhyk
#
import Tkinter, Pmw

import string

# Dummy callback
class _dummy:
    def __init__(self, f, pars):
        self.f, self.pars = f, pars

    def __call__(self): 
        apply ( self.f, (self.pars, ) )

class OptionMenu(Pmw.MegaWidget):
    def __init__(self, parent = None, **kw):

	# Define the megawidget options.
	INITOPT = Pmw.INITOPT
	optiondefs = (
	    ('labelmargin',        0,          INITOPT),
	    ('labelpos',           None,       INITOPT),
	    ('selectioncommand',   '',         None),
            ('items',              [],         INITOPT ),
            ('variable',           Tkinter.StringVar(''),       INITOPT ),
            ('command',            None,       None ),
            ('direction',          'flush',    INITOPT ),
	)
	self.defineoptions(kw, optiondefs)

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

        # Check that the direction is valid
        dirs = [ 'left', 'right', 'above', 'below', 'flush' ]

        if self['direction'] not in dirs:
           raise ValueError, 'direction is %s, should be one of : %s' % \
                 ( self['direction'], string.join ( dirs, ', ' ))

        # A nuisance inherited from OptionMenu
        if not len(self['items']):
            raise ValueError, 'items must be a non-empty list'

	interior = self.interior()

        # Create OptionMenu
	self._optionmenu = self.createcomponent('menu',
		(), None,
		Tkinter.OptionMenu, (interior, self['variable'],
                                     self['items'][0], self['items'][1:]),
                )
        self._optionmenu.configure ( direction = self['direction'],)
	self._optionmenu.grid ( row = 2, column = 2, sticky='nsew')
	interior.grid_columnconfigure ( 2, weight = 1)
	interior.grid_rowconfigure ( 2, weight = 1)

        # Create the label.
        self.createlabel(interior)

        # Re-set the items to register proper callback
        self.setitems ( self['items'] )

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


    #======================================================================

    # Public methods
    def get ( self ):
        "Return the name of the menu item currently selected"

        return self['variable'].get()

    def setitems ( self, items, active = None ):
        "Set the list of items to be displayed"

        m = self._optionmenu['menu']

        m.delete ( 0, 'end' )

        for i in items:
            m.add_command ( label = i, command = _dummy ( self._callback, i ))

        if active:
           self.select(active)
        else:
           self.select(items[0])
 
    def index ( self, name ):
        "Return integer index of item with name 'name'"

        return self._optionmenu['menu'].index(name)

    def select ( self, index ):
        "Select the item with index"

        ind = self.index ( index )

        val = self._optionmenu['menu'].entrycget(ind, 'label')

        self['variable'].set(val)

        # Note that the callback is called:
        if callable ( self['command'] ):
            apply ( self['command'], (val, ) )

    #======================================================================

    # Private methods
    def _callback ( self, tag ):
        "Called when user selects an item"

        self['variable'].set(tag)

        if callable ( self['command'] ):
            apply ( self['command'], (tag, ) )