#!/usr/local/bin/python
# Copyright (C) 1999, Michael P. Reilly,  All rights reserved
#

"""Classes to represent entries in a Tkinter.Menu.

These classes are to ease the problem of knowing the index of a particular
entry in a menu when calling Menu methods.

The MenuCascade class creates a new Menu instance, setting it in the
"submenu" attribute.

Limitations:

These are not Widget subclasses; all subclasses of Widget must be real
Tk widgets, these are virtual Tk widgets.  For this reason, do not call
the destroy() method.

The order of the menu entries is crucial, the instances create entries
at the end of the menu, then used the index of the final (the current)
entry in future method calls.  For this reason, do not insert entries
and do not delete entries once the Menu has been populated - this will
corrupt the saved indices in the instances.

Example:
  menu = Menu(mbutton)
  open = MenuCommand(menu, label='Open...', command=self.openfile_dialog)
  save = MenuCommand(menu, label='Save', command=self.savefile_default)
  saveas = MenuCommand(menu, label='Save...', command=self.savefile_dialog)
  MenuSeparator(menu)
  MenuCommand(menu, label='Quit', command=self.quitapp)
  save.config(state=DISABLED)  # "grey-out" the menu option
  print save['label']
"""

import Tkinter, Tkconstants

class MenuEntry:
  """Base class for menu entries."""
  def __init__(self, master, cnf={}, **kw):
    self.master = master
    apply(master.add, (self.type,), kw)
    self.index = master.index(Tkinter.AtEnd())
    # should this be done?
    self.master.children[self.index] = self
  # this should ONLY be called by self.master.destroy
  def destroy(self):
    self.master.delete(self.index)
  def activate(self):
    self.master.activate(self.index)
  def cget(self, option):
    return self.master.entrycget(self.index, option)
  def configure(self, cnf=None, **kw):
    return apply(self.master.entryconfigure, (self.index, cnf), kw)
  config = configure
  def invoke(self):
    return self.master.invoke(self.index)
  def yposition(self):
    return self.master.yposition(self.index)
  def __getitem__(self, name):
    return self.cget(name)
  def __setitem__(self, name, value):
    return self.configure(cnf={name: value})

class MenuCascade(MenuEntry):
  """Create a cascading menu entry.
submenu contains a new menu object bound to this entry."""
  type = Tkconstants.CASCADE
  def __init__(self, master, **kw):
    apply(MenuEntry.__init__, (self, master), kw)
    self.submenu = Tkinter.Menu(master)
    self.configure(menu=self.submenu)
  def destroy(self):
    self.submenu.destroy()
    del self.submenu
    MenuEntry.destroy()
  def __getitem__(self, key):
    if key == 'menu':
      try:
        return self.submenu
      except AttributeError:
        raise KeyError, key
    else
      return MenuEntry.__getitem__(self, key)

class MenuCommand(MenuEntry):
  """Create a menu button entry."""
  type = Tkconstants.COMMAND
class MenuCheckbutton(MenuEntry):
  """Create a checkbox menu entry.
Note: may require a Tkinter.Variable."""
  type = Tkconstants.CHECKBUTTON
class MenuRadiobutton(MenuEntry):
  """Create a radiobutton menu entry.
Note: should require a Tkinter.Variable."""
  type = Tkconstants.RADIOBUTTON
class MenuSeparator(MenuEntry):
  """Create a menu separator, usually just a line across the menu."""
  type = Tkconstants.SEPARATOR

def test():
  mbar = Tkinter.Frame(None)
  filebutton = Tkinter.Menubutton(mbar, text='File')
  menu = Tkinter.Menu(filebutton)
  filebutton['menu'] = menu
  MenuCommand(menu, label='Open...')
  save = MenuCommand(menu, label='Save')
  MenuCommand(menu, label='Save As')
  MenuSeparator(menu)
  MenuCommand(menu, label='Quit', command=mbar.quit)
  save.config(state=Tkinter.DISABLED)
  filebutton.pack(side=Tkinter.LEFT)
  editbutton = Tkinter.Menubutton(mbar, text='Edit')
  menu = Tkinter.Menu(editbutton)
  editbutton['menu'] = menu
  casc = MenuCascade(menu, label='Toaster')
  MenuCheckbutton(casc['menu'], label='On')
  MenuCheckbutton(casc['menu'], label='Off')
  editbutton.pack(side=Tkinter.LEFT)
  mbar.pack()
  mbar.mainloop()

if __name__ == '__main__':
  test()
