#######################################################################
#  File:  PmwNoteBook.py
#  Author: Joseph A. Saltiel - jsaltiel@lucent.com
#  References:  Pmw.0.4
#  Modules:  Tkinter, Pmw, string
#  PmwNoteBook -  This is a NoteBook Mega Widget made for Python.
#               It is based on the framework and essentially is
#               an extension of PMW (Python Mega Widgets).  It 
#               allows the user to create a notebook, add and del
#               pages.  A page is a frame and the user can grab 
#               and put additional widgets on to it.
#
#  Copyright (C) 1997 by Lucent Technologies, Inc. & Joseph Saltiel
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
# 
#######################################################################

import Tkinter
import Pmw
import string

##################################################################
# Class:  NoteBook
# Description:  This defines the notebook widget.  This includes 
#               its subcomponents and attributes.
##################################################################
class NoteBookS(Pmw.MegaWidget):
    def __init__(self, parent = None, **kw):
	
	# Define the megawidget options.
	INITOPT = Pmw.INITOPT
	optiondefs = (
	    ('tabColor', 'blue', None),
	    ('canvasColor', 'white', None),
	    ('activeColor', 'red', None),
	    ('deactiveColor', 'grey', None),
	    ('shadeColor', '#666666', None),
	    ('textColor', 'black', None),
	    ('textFont', '-*-helvetica-bold-r-normal--10-*-*-*-*-*', self._setFontLength),
	    ('longX', 30, INITOPT),
	    ('shortX', 7, INITOPT),
	    ('longY', 35, INITOPT),
	    ('shortY', 7, INITOPT),
	    ('offsetY', 5, INITOPT),
	    ('canvasHeight', 250, self._adjustHeight),
	    ('canvasWidth', 400, self._adjustWidth),
	    ('tabHeight', 40, INITOPT),
	)
	self.defineoptions(kw, optiondefs)

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

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

	# Create the canvas widget.
	self._mainCanvas = self.createcomponent('mainCanvas',
		(), None,
		Tkinter.Canvas, (interior,),
		bg=self['canvasColor'], height=self['canvasHeight'],
		width=self['canvasWidth'], highlightthickness=0,
		bd=2, relief='raised')
	self._mainCanvas.grid(sticky = 'news', ipadx=2, ipady=2)

	# Create the tab canvas widget.
	self._tabCanvas = self.createcomponent('tabCanvas',
		(), None,
		Tkinter.Canvas, (self._mainCanvas,),
		bg=self['tabColor'], height=self['tabHeight'],
		highlightthickness=0)
	self._tabCanvas.grid(row=0, column=0, sticky = 'news')

	# Create the container canvas widget.
	self._containerCanvas = self.createcomponent('containerCanvas',
		(), None,
		Tkinter.Canvas, (self._mainCanvas,),
		bg=self['activeColor'], highlightthickness=0, 
	        height=self['canvasHeight'] - self['offsetY'] - self['longY'],
	        width=self['canvasWidth'])
	self._containerCanvas.grid(row=1, column=0, sticky='news')

	# Initialise instance variables.
	self._tabOrder = []
	self._pages = {}
	self._containers = {}
	self._tabNames = {}
	self._shade = {}
	self._activePage = None
	self._numofPages = 0
	self._fontLength = 10
	self._offsetX = 10
	self._lineBorder = self._tabCanvas.create_line(0, self['tabHeight']-1, self._tabCanvas.winfo_reqwidth(), self['tabHeight']-1, fill=self['shadeColor'])
	self._mutex = 1

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


#**********************************************************************

#          CONFIGURATION METHODS 

#**********************************************************************


##################################################################
# Method: _setFontLength
# Description: This sets the font length.  The font length is based
#              on the font type used.  This determines how many
#              characters may fit on a tab.
##################################################################
    def _setFontLength(self):
	try:
	    length = string.atoi(string.splitfields(self['textFont'], '-')[7])
	    self._fontLength = length
	except:
	    message = 'Invalid Font specified. The 8th field, point size, must be specified'
	    raise ValueError, message

##################################################################
# Method: _tabSelect
# Description: When a tab is selected, we raise it and lower the 
#              the other tabs.
##################################################################
    def _tabSelect(self, event=None):
	if self._mutex:
	    for item in self._containers.keys():
		self._containers[item].grid_forget()
	    for item in self._pages.keys():
		if ((event==None) and (self._activePage != item)) or ((event) and (event.widget.gettags(self._pages[item])) == () and (event.widget.gettags(self._tabNames[item])) == ()):
		    self._tabCanvas.itemconfigure(self._pages[item], fill=self['deactiveColor'], outline=self['deactiveColor'])
		    self._tabCanvas.lower(self._pages[item])
		else:
		    self.raisePage(item, select=None)

##################################################################
# Method: _adjustHeight
# Description: When the user changes the notebook height, we must make
#              sure that information gets propageted to the proper components.
##################################################################
    def _adjustHeight(self):
	frameHeight = self['canvasHeight'] - self['offsetY'] - self['longY']
	self._mainCanvas.configure(height=self['canvasHeight'])
	self._mainCanvas.grid_rowconfigure(1, minsize=frameHeight)
	self._containerCanvas.grid_rowconfigure(0, minsize=frameHeight)

##################################################################
# Method: _adjustWidth
# Description: When the user changes the notebook width, we must make
#              sure that information gets propageted to the proper components.
##################################################################
    def _adjustWidth(self):
	self._mainCanvas.configure(width=self['canvasWidth'])
	self._containerCanvas.grid_columnconfigure(0, minsize=self['canvasWidth'])

#**********************************************************************

#          PRIVATE METHODS 

#**********************************************************************

##################################################################
# Method: _getOffset
# Description: This determines where to draw a tab name on the tab
##################################################################
    def _getOffset(self, name):
	length = len(name)
	if length > (self['longX'] / self._fontLength):
	    width = length * self._fontLength
	else:
	    width = self['longX']
	coords = (self._offsetX, self['longY']+self['offsetY'], self._offsetX, self['shortY']+self['offsetY'], self['shortX'] + self._offsetX, self['offsetY'], width + self._offsetX - self['shortX'], self['offsetY'], width+self._offsetX, self['shortY']+self['offsetY'], self._offsetX+width, self['offsetY']+self['longY'])
	startX = self._offsetX+width/2
	startY = self['offsetY']+self['longY']/2
	self._offsetX = self._offsetX+width+4
	return coords, startX, startY

##################################################################
# Method: _drawTab
# Description: This draws the tab on the tab Canvas
##################################################################
    def _drawTab(self, name):
	self._tabSelect()
	coords, startX, startY = self._getOffset(name)
	node = self._tabCanvas.create_polygon(coords, fill=self['activeColor'], outline=self['shadeColor'])
	pageText = self._tabCanvas.create_text(startX, startY, text=name, fill=self['textColor'], font=self['textFont'])
	self._lineBorder = self._tabCanvas.create_line(0, self['tabHeight']-1, self._tabCanvas.winfo_width(), self['tabHeight']-1, fill=self['shadeColor'])
	self._tabCanvas.tag_bind(pageText, "", self._tabSelect)
	self._tabCanvas.tag_bind(node, "", self._tabSelect)
	self._pages[name] = node
	self._tabNames[name] = pageText 


#**********************************************************************

#          PUBLIC METHODS

#**********************************************************************


##################################################################
# Method: addPage
# Description: The user calls this to add a page/tab
##################################################################
    def addPage(self, name):
	if self._tabNames.has_key(name):
	    message = 'Tab name already exists'
	    raise NameError, message
	self._activePage = name
	self._drawTab(name)
	frame = Tkinter.Frame(self._containerCanvas, bg=self['activeColor'],
	        height=self['canvasHeight'] - self['offsetY'] - self['longY'])
	frame.grid(row=0, column=0, sticky='news')
	self._numofPages = self._numofPages + 1
	self._tabOrder.append(name)
	self._containers[name] = frame

##################################################################
# Method: delPage
# Description: The user calls this to delete a page/tab
##################################################################
    def delPage (self, name):
	if not(self._tabNames.has_key(name)):
	    message = 'That tab name does not exist'
	    raise KeyError, message
	for item in self._tabOrder:
	    self._tabCanvas.delete(self._pages[item], self._tabNames[item])
	    self._tabCanvas.dtag(self._pages[item], self._tabNames[item])
	self._tabOrder.remove(name)
	self._containers[name].destroy()
	del self._pages[name]
	del self._tabNames[name]
	del self._containers[name]
	self._numofPages = self._numofPages - 1
	self._offsetX = 10
	for each in self._tabOrder:
	    self._drawTab(each)
	if self._numofPages > 0:
	    self.raisePage(each)

##################################################################
# Method: getPage
# Description: Given a name, returns that frame(page) to the user
##################################################################
    def getPage(self, name):
	if self._containers.has_key(name):
	    return self._containers[name]
	else:
	    message = 'That page does not exist'
	    raise KeyError, message
    
##################################################################
# Method: pages
# Description: Returns a list of frames(pages) on the current notebook
##################################################################
    def pages(self):
	lst = []
	for item in self._containers.keys():
	    lst.append(self._containers[item])
	return lst

##################################################################
# Method: raisePage
# Description: Raises the given tab name
##################################################################
    def raisePage(self, name, select=1):
	if select:
	    if not(self._pages.has_key(name)):
		message = 'That page does not exist'
		raise KeyError, message
	    self._tabSelect()
	self._activePage = name
	self._containers[name].grid(row=0, column=0, sticky='news')
	self._tabCanvas.itemconfigure(self._pages[name], fill=self['activeColor'], outline=self['shadeColor'])
	self._tabCanvas.lift(self._pages[name])
	self._tabCanvas.lift(self._tabNames[name])	

##################################################################
# Method: raised
# Description: Returns the name of the currently raised tab
##################################################################
    def raised(self):
	return self._activePage

##################################################################
# Method: pageNames
# Description: Returns a list of the all the page names
##################################################################
    def pageNames(self):
	return self._pages.keys()

##################################################################
# Method: unBind
# Description: Unbind the notebooks bindings
##################################################################
    def unBind(self):
	self._mutex = 0

##################################################################
# Method: reBind
# Description: Re bind the notebooks bindings
##################################################################
    def reBind(self):
	self._mutex = 1