# Based on iwidgets2.2.0/buttonbox.itk code.

import Tkinter
import Pmw

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

	# Define the megawidget options.
	INITOPT = Pmw.INITOPT
	optiondefs = (
	    ('labelmargin',       0,              INITOPT),
	    ('labelpos',          None,           INITOPT),
	    ('orient',            'horizontal',   INITOPT),
	    ('padx',              8,              INITOPT),
	    ('pady',              8,              INITOPT),
	    ('ringborderwidth',   1,              INITOPT),
	    ('ringpadx',          1,              INITOPT),
	    ('ringpady',          1,              INITOPT),
	)
	self.defineoptions(kw, optiondefs)

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

	# Create the components.
	interior = self.interior()
	if self['labelpos'] is None:
	    self._buttonBoxFrame = self._hull
	    columnOrRow = 0
	else:
	    self._buttonBoxFrame = self.createcomponent('frame',
		    (), None,
		    Tkinter.Frame, (interior,))
	    self._buttonBoxFrame.grid(column=2, row=2, sticky='nsew')
	    columnOrRow = 2

	    self.createlabel(interior)

	if self['orient'] == 'horizontal':
	    interior.grid_columnconfigure(columnOrRow, weight = 1)
	else:
	    interior.grid_rowconfigure(columnOrRow, weight = 1)

	# Initialise instance variables.

	# List of tuples describing the buttons:
	#   - name
	#   - button widget
	#   - surrounding frame widget
	self._buttonList = []

	# The index of the default button.
	self._defaultButton = None

	self._timerId = None

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

    def destroy(self):
	if self._timerId:
	    self.after_cancel(self._timerId)
	    self._timerId = None
	Pmw.MegaWidget.destroy(self)

    def numbuttons(self):
        return len(self._buttonList)

    def index(self, index, forInsert = 0):
	listLength = len(self._buttonList)
	if type(index) == type(1):
	    if forInsert and index <= listLength:
		return index
	    elif not forInsert and index < listLength:
		return index
	    else:
		raise ValueError, 'index "%s" is out of range' % index
	elif index == 'end':
	    if forInsert:
		return listLength
	    elif listLength > 0:
		return listLength - 1
	    else:
		raise ValueError, 'ButtonBox has no buttons'
	elif index == 'default':
	    if self._defaultButton is not None:
		return self._defaultButton
	    raise ValueError, 'ButtonBox has no default'
	else:
	    for count in range(listLength):
		name = self._buttonList[count][0]
		if index == name:
		    return count
	    validValues = 'number, end, default, or a name'
	    raise ValueError, \
		'bad index "%s": must be %s' % (index, validValues)

    def insert(self, name, before = 0, **kw):
	if name in self.components():
	    raise ValueError, 'button "%s" already exists' % name
	frame = Tkinter.Frame(self._buttonBoxFrame,
		borderwidth = self['ringborderwidth'], relief = 'flat')
	if not kw.has_key('text'):
	    kw['text'] = name
	button = apply(self.createcomponent, (name,
		(), 'Button',
		Tkinter.Button, (frame,)), kw)
	button.pack(padx = self['ringpadx'], pady = self['ringpady'],
		fill = 'both', expand = 1)

	index = self.index(before, 1)
	horizontal = self['orient'] == 'horizontal'
	numButtons = len(self._buttonList)

	# Shift buttons up one position.
	for i in range(numButtons - 1, index - 1, -1):
	    widget = self._buttonList[i][2]
	    pos = i * 2 + 3
	    if horizontal:
		widget.grid(column = pos, row = 0)
	    else:
		widget.grid(column = 0, row = pos)

	# Display the new button.
	if horizontal:
	    frame.grid(column = index * 2 + 1, row = 0, sticky = 'ew',
		    padx = self['padx'], pady = self['pady'])
	    self._buttonBoxFrame.grid_columnconfigure(numButtons * 2 + 2, weight = 1)
	else:
	    frame.grid(column = 0, row = index * 2 + 1, sticky = 'ew',
		    padx = self['padx'], pady = self['pady'])
	    self._buttonBoxFrame.grid_rowconfigure(numButtons * 2 + 2, weight = 1)
	self._buttonList.insert(index, (name, button, frame))

	return button

    def add(self, name, **kw):
        return apply(self.insert, (name, len(self._buttonList)), kw)

    def delete(self, index):
        index = self.index(index)
	(name, widget, frame) = self._buttonList[index]
	frame.grid_forget()
	self.destroycomponent(name)

	numButtons = len(self._buttonList)

	# Shift buttons down one position.
	horizontal = self['orient'] == 'horizontal'
	for i in range(index + 1, numButtons):
	    frame = self._buttonList[i][2]
	    pos = i * 2 - 1
	    if horizontal:
		frame.grid(column = pos, row = 0)
	    else:
		frame.grid(column = 0, row = pos)

	if horizontal:
	    self._buttonBoxFrame.grid_columnconfigure(numButtons * 2 - 1,
		    minsize = 0)
	    self._buttonBoxFrame.grid_columnconfigure(numButtons * 2, weight = 0)
	else:
	    self._buttonBoxFrame.grid_rowconfigure(numButtons * 2, weight = 0)
	del self._buttonList[index]

    def setdefault(self, index):
	# Turn off the default ring around the current default button.
	if self._defaultButton is not None:
	    frame = self._buttonList[self._defaultButton][2]
	    frame.configure(relief = 'flat')
	    self._defaultButton = None

	# Turn on the default ring around the new default button.
	if index is not None:
	    index = self.index(index)
	    self._defaultButton = index
	    frame = self._buttonList[index][2]
	    frame.configure(relief = 'sunken')

    def invoke(self, index = 'default', noFlash = 0):
	# Invoke the callback associated with the *index* button.  If
	# *noFlash* is not set, flash the button to indicate to the
	# user that something happened.

	button = self._buttonList[self.index(index)][1]
	if not noFlash:
	    state = button.cget('state')
	    relief = button.cget('relief')
	    button.configure(state = 'active', relief = 'sunken')
	    self.update_idletasks()
	    self.after(100)
	    button.configure(state = state, relief = relief)
	return button.invoke()

    def alignbuttons(self, when = 'later'):
	if when == 'later':
	    if not self._timerId:
		self._timerId = self.after_idle(self.alignbuttons, 'now')
	    return
	self.update_idletasks()
	self._timerId = None

	if self['orient'] == 'horizontal':
	    # Determine the width of the maximum length button.
	    max = 0
	    for index in range(len(self._buttonList)):
		width = self._buttonBoxFrame.grid_bbox(index * 2 + 1, 0)[2]
		if width > max:
		    max = width

	    # Set the widths of all the buttons to be the same.
	    for index in range(len(self._buttonList)):
		self._buttonBoxFrame.grid_columnconfigure(index * 2 + 1,
			minsize = max)
	else:
	    # Determine the width of the maximum length button.
	    max = 0
	    for index in range(len(self._buttonList)):
		width = self._buttonBoxFrame.grid_bbox(0, index * 2 + 1)[2]
		if width > max:
		    max = width

	    # Set the width of all the buttons to be the same.
	    self._buttonBoxFrame.grid_columnconfigure(0, minsize = max)