import types
import Tkinter
import Pmw

class OptionMenu(Pmw.MegaWidget):

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

        # Define the megawidget options.
        INITOPT = Pmw.INITOPT
        optiondefs = (
            ('command',        None,       None),
            ('items',          (),         INITOPT),
            ('initialitem',    None,       INITOPT),
            ('labelmargin',    0,          INITOPT),
            ('labelpos',       None,       INITOPT),
            ('sticky',         'ew',       INITOPT),
        )
        self.defineoptions(kw, optiondefs)

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

        # Create the components.
        interior = self.interior()

        self._menubutton = self.createcomponent('menubutton',
                                                (), None,
                                                Tkinter.Menubutton, (interior,),
                                                borderwidth = 2,
                                                indicatoron = 1,
                                                relief = 'raised',
                                                anchor = 'c',
                                                highlightthickness = 2,
                                                direction = 'flush',
                                                takefocus = 1,
                                                )
        self._menubutton.grid(column = 2, row = 2, sticky = self['sticky'])

        self._menu = self.createcomponent('menu',
                                          (), None,
                                          Tkinter.Menu, (self._menubutton,),
                                          tearoff=0
                                          )
        self._menubutton.configure(menu = self._menu)

        interior.grid_columnconfigure(2, weight = 1)
        interior.grid_rowconfigure(2, weight = 1)

        # Create the label.
        self.createlabel(interior)

        # Add the items specified by the initialisation option.
        self._itemList = []
        self.setitems(self['items'], self['initialitem'])

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

    def setitems(self, items, index = None):

        # Clean up old items and callback commands.
        for oldIndex in range(len(self._itemList)):
            tclCommandName = str(self._menu.entrycget(oldIndex, 'command'))
            if tclCommandName != '':
                self._menu.deletecommand(tclCommandName)
        self._menu.delete(0, 'end')
        self._itemList = list(items)

        # Set the items in the menu component.
        for item in items:
            self._menu.add_command(label = item,
                                   command = lambda self = self, item = item: self._invoke(item))

        # Set the currently selected value.
        if index is None:
            var = str(self._menubutton.cget('textvariable'))
            if var != '':
                # None means do not change text variable.
                return
            if len(items) == 0:
                text = ''
            elif str(self._menubutton.cget('text')) in items:
                # Do not change selection if it is still valid
                return
            else:
                text = items[0]
        else:
            index = self.index(index)
            text = self._itemList[index]

        self.setvalue(text)

    def getcurselection(self):
        var = str(self._menubutton.cget('textvariable'))
        if var == '':
            return str(self._menubutton.cget('text'))
        else:
            return self._menu.tk.globalgetvar(var)

    def getvalue(self):
        return self.getcurselection()

    def setvalue(self, text):
        var = str(self._menubutton.cget('textvariable'))
        if var == '':
            self._menubutton.configure(text = text)
        else:
            self._menu.tk.globalsetvar(var, text)

    def index(self, index):
        listLength = len(self._itemList)
        if type(index) == types.IntType:
            if index < listLength:
                return index
            else:
                raise ValueError, 'index "%s" is out of range' % index
        elif index is Pmw.END:
            if listLength > 0:
                return listLength - 1
            else:
                raise ValueError, 'OptionMenu has no items'
        else:
            if index is Pmw.SELECT:
                if listLength > 0:
                    index = self.getcurselection()
                else:
                    raise ValueError, 'OptionMenu has no items'
            if index in self._itemList:
                return self._itemList.index(index)
            raise ValueError, \
                  'bad index "%s": must be a ' \
                  'name, a number, Pmw.END or Pmw.SELECT' % (index,)

    def invoke(self, index = Pmw.SELECT):
        index = self.index(index)
        text = self._itemList[index]

        return self._invoke(text)

    def _invoke(self, text):
        self.setvalue(text)

        command = self['command']
        if callable(command):
            return command(text)
