"""
I really should define a NBFrame widget according to the Tix specifications.
Thus, I could refer to the Tix documentation.
"""

import sys
import string
import Tkinter
import Pmw

TRUE  = 1
FALSE = 0

DARKGRAY  = 'gray60'
LIGHTGRAY = 'gray82'

class NoteBookPage( Pmw.MegaWidget ):
    def __init__(self, notebook, pagename, **kw):
	self.notebook = notebook
	self.pagename = pagename

        # Define the megawidget options.
	INITOPT = Pmw.INITOPT
        optiondefs = (
	    ('anchor',        'c',       INITOPT),
	    #('bitmap',        None,      INITOPT),
	    ('balloonHelp',    '',      None),
	    ('statusHelp',  None,      None),
	    ('createcmd',     None,      None),
	    #('image',         None,      INITOPT),
	    ('justify',       None,      INITOPT),
	    ('label',         "",        INITOPT),
	    ('raisecmd',      None,      None),
	    ('lowercmd',      None,      None),
	    ('state',         'normal',  self._setstate),
	    ('underline',     0,         INITOPT),
	    #('wraplength',    0,         INITOPT)
	    )
        self.defineoptions(kw, optiondefs)

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

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

	l = Tkinter.Label(self.notebook.component('nbframe'))
	font = l.cget('font')
	l.destroy()
	del l

	self._button = self.createcomponent(
	    'button',
	    (), None,
	    Tkinter.Button, (self.notebook.interior(),),
	    bd=0, relief='flat',
	    text=self['label'],
	    font=font,
	    underline=self['underline'],
	    command=self.lift
	    )

	if not self.notebook['balloon'] is None:
	    self.notebook['balloon'].bind(
		self.component('button'), 
		self['balloonHelp'], 
		self['statusHelp']
		)
						    
        # Check keywords and initialise options.
        self.initialiseoptions(NoteBookPage)
	
	self._iscreated = FALSE

    def _setstate(self):
	self.component('button').configure(state=self['state'])

    def lift(self):
	if not self._iscreated:
	    if not self['createcmd'] is None: self['createcmd']()
	    self._iscreated = TRUE
	if not self['raisecmd'] is None: self['raisecmd']()

	if self.cget('state') == 'normal':
	    self.notebook.lift(self.pagename)

    def _lower(self):
	"""
	Should no be called directly: top is lowered when other is raised !
	Whereas the page.lift() calls the notebook.lift(), page._lower() is
	called *from* notebook.lift(). Asymmetric indeed, but don't forget 
	that this is caused by the restriction that only one page may be
	raised. 
	The reason for having this page._lower() method is to call the 
	lowercmd if defined, just as the page.lift() method calls the 
	raisecmd.
	"""
	if not self['lowercmd'] is None: self['lowercmd']
	
	

    def req_size(self):
	self.update_idletasks()
	return self.winfo_reqwidth(), self.winfo_reqheight()

Pmw.forwardmethods(NoteBookPage, Tkinter.Frame, '_hull')
	
	    
class NoteBookR( Pmw.MegaWidget ):
    def __init__(self, parent = None, **kw):

        # Define the megawidget options.
	INITOPT = Pmw.INITOPT
        optiondefs = (
	    #('dynamicgeometry', FALSE,  INITOPT ),# deplorable option!
	    ('balloon',       None,      None),
	    ('ipadx',           4,      INITOPT),
	    ('ipady',           4,      INITOPT)
	    )
        self.defineoptions(kw, optiondefs)

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

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

	self._nbframe = self.createcomponent(
	    'nbframe', 
	    (), None,
	    Tkinter.Canvas, (self.component('hull'),),
	    bd=0,
	    height=0,
	    width=0
	    )

	self._nbframe.pack(side='top',padx=0,pady=0)
						    
        # Check keywords and initialise options.
        self.initialiseoptions(NoteBookR)

	self._pages       = []        # [name1, name2, ...]
	self._pagedict    = {}        # { name: NoteBookPageObject}
	self._currentpage = None      # name (!) of currently raised page
	self._indexpage   = -1        # index of currently raised page
	
	# Variables used for sizing the Canvas and painting the tabs.
	self._tabheight   = 0
	self._tabwidth    = 0
	self._borderwidth = 2
	self._pagewidth   = 0
	self._pageheight  = 0
	self._nbwidth     = 2*self._borderwidth
	self._nbheight    = 2*self._borderwidth
	self._nbcenterx   = 0
	self._nbcentery   = 0
	self._tablineitems = ['_light','_dark', '_topline','_button','_bottom']
	
	# XXX Necessary, but clumsy to look at: really should be the 
	# Toplevel configure at which the method should be called, that is,
	# before other widgets are already displayed.
	self.bind('<Configure>', self.initialise)

    def initialise(self,e=None):
	self._makereqsize()
	self._drawborder()
	
	self.lift(0)
	
    def interior(self):
        return self._nbframe

    def _drawtab(self,newpage):
	tabcanvas = self.component('nbframe')
	d = self._borderwidth
	b = newpage.component('button')
	w = b.winfo_reqwidth()

	if self._tabwidth == 0: 
	    x = self._tabwidth
	    self._tabwidth = 0
	else:
	    x = self._tabwidth

	pagename = newpage.pagename

	tabcanvas.create_window(
	    x+2*d,d+1,window=b,anchor='nw',tags=pagename+'_button')

	self._tabwidth = self._tabwidth + 3*d + w
	oldwidth = string.atoi(tabcanvas.cget('width'))
	if oldwidth < self._tabwidth:	    
	    tabcanvas.configure(width=self._tabwidth)

	h = self._tabheight

	lightcoords   = (
	    x+d, h,
	    x+d, 3*d,
	    x+2*d-1, d,
	    x+2*d+w-1, d, 
	    x+2*d+w+-2+2*d, d+2*d
	    )
	shadowcoords = (
	    x+3*d+w, d+2*d+1,
	    x+3*d+w, h-d+1
	    )
	toplinecoords = (
	    x, h-2*d,
	    x, d+2*d-1,
	    x+2*d-1, d-1,
	    x+d+w+1, d-1,
	    x+3*d+w, d+2*d,
	    x+3*d+w, h-d+1
	    )
	lightkw = {'fill': 'white', 'width': d, 'tags': pagename+'_light'}
	shadowkw = {'fill': DARKGRAY, 'width': d, 'tags': pagename+'_dark'}
	toplinekw = {'fill': DARKGRAY, 'width': 1,'tags': pagename+'_topline'}

	apply( tabcanvas.create_line, lightcoords, lightkw )
	apply( tabcanvas.create_line, shadowcoords, shadowkw )
	apply( tabcanvas.create_line, toplinecoords, toplinekw )
	
	tabcanvas.lower( pagename+'_topline' )

	tabcanvas.create_line(
	    x+d,  h,
	    x+4*d+w, h,
	    fill='white',width=d,tags=pagename+'_bottom'
	    )

    def _drawborder(self):
	tabcanvas = self.component('nbframe')
	tw = string.atoi(tabcanvas.cget('width'))
	th = string.atoi(tabcanvas.cget('height'))
	d = self._borderwidth
	tabcanvas.delete('border')
	tabcanvas.create_line(
	    d,self._tabheight-2*d,
	    d,self._tabheight+2*self['ipady']+self._pageheight,
	    fill='white',
	    width=self._borderwidth,
	    tags='border borderlight'
	    )	   
	#print self._nbwidth - self._tabwidth
	#print self._nbwidth - self._pagewidth
	tabcanvas.create_line(
	    self._tabwidth, self._tabheight,
	    self._nbwidth, self._tabheight,
	    fill='white',
	    width=self._borderwidth,
	    tags='border borderlight'
	    )
	tabcanvas.create_line(
	    d, self._tabheight+2*self['ipady']+self._pageheight,
	    self._nbwidth, self._tabheight+2*self['ipady']+self._pageheight,
	    self._nbwidth, self._tabheight-d,
	    fill=DARKGRAY,
	    width=self._borderwidth,
	    tags='border bordershadow'
	    )	    
		
    def add(self,pagename,**kw):
	if self._pagedict.has_key(pagename):
	    msg = "Attempt to create a second tab with name '%s'." % pagename
	    raise ValueError, msg
	newpage = apply( NoteBookPage, (self, pagename), kw )
	setattr(self,pagename,newpage)

	tabcanvas = self.component('nbframe')

	d = self._borderwidth
	# Initialization:
	if not self._tabheight:
	    b = newpage.component('button')
	    self._tabheight = b.winfo_reqheight() + 2*d
	    tabcanvas.configure(height=self._tabheight)
	    INITIALIZE = TRUE
	else:
	    INITIALIZE = FALSE

	self._drawtab(newpage)

	self._pages.append( (pagename,newpage) )
	self._pagedict[pagename] = newpage
	
	if INITIALIZE:
	    self.lift(pagename)
  		
    def _makereqsize(self):
	self.update_idletasks()
	tabcanvas = self.component('nbframe')
	reqw = 1
	reqh = 1
	for page in self._pagedict.values():
	    w, h = page.req_size()
	    reqw = max([reqw,w])
	    reqh = max([reqh,h])

	self._pagewidth  = reqw
	self._pageheight = reqh
	self._nbwidth = max([self._tabwidth,self._pagewidth+2*self['ipadx']])
	self._nbheight = self._tabheight + 2*self['ipady'] + self._pageheight
	self._nbcenterx = self._nbwidth/2
	self._nbcentery = self._tabheight + self['ipady'] + self._pageheight/2

	tabcanvas.configure( width=self._nbwidth, height=self._nbheight )

    def _undrawtab(self,delpage):
	tabcanvas = self.component('nbframe')
	d = self._borderwidth
	b = delpage.component('button')
	w = b.winfo_reqwidth()
	x = self._tabwidth
	h = self._tabheight

	pagename = delpage.pagename

	for item in self._tablineitems:
	    tabcanvas.delete(pagename+item)
	i = 0
	for name in map( lambda x: x[0], self._pages):
	    i = i+1
	    if name == pagename: break
	for name  in map( lambda x: x[0], self._pages[i:]):
	    for item in self._tablineitems:
		tabcanvas.move(name+item,-(w+4*d),0)

	if self._pagewidth < self._tabwidth:
	    tabcanvas.configure(width=self._tabwidth)

    def tkdelete(self,pagename):
	delpage = self._pagedict[pagename]

	ip = self._indexpage
	if self.raised() == pagename:
	    if self._indexpage < len(self._pages) - 1:
		self.lift( self._pages[self._indexpage + 1][0] )
	    elif self._indexpage > 0:
		self.lift( self._pages[self._indexpage - 1][0] )
	    else:
		self._indexpage = -1
		self._currentpage = None
			    
	b = delpage.component('button')
	w = b.winfo_reqwidth()
	d = self._borderwidth
	tabcanvas = self.component('nbframe')
	self._tabwidth = self._tabwidth - w - 4*d

	self._undrawtab(delpage)

	delpage.destroy()
	delattr(self,pagename)	

	del self._pagedict[pagename]
	self._pages = self._pages[:ip] + self._pages[ip+1:]
	
    def pagecget(self,pagename,option):
	return self._pagedict[pagename].cget(option)
    def pageconfigure(self,pagename,**kw):
	return apply( self._pagedict[pagename].configure, (), kw )
    def pages(self):
	return self._pagedict.keys()
    def lift(self,pagenameOrIndex):
	if type(pagenameOrIndex) == type(''):
	    pagename = pagenameOrIndex
	else:
	    pagename = self._pages[pagenameOrIndex][0]

	tabcanvas = self.component('nbframe')

	# deal with the present top page
	if not self._currentpage is None:
	    tabcanvas.itemconfigure( self._currentpage+'_bottom', fill='white')
	    tabcanvas.lower( self._currentpage+'_topline' )
	    self._pagedict[self._currentpage]._lower()

	tabcanvas.itemconfigure( pagename+'_bottom', fill=LIGHTGRAY )
	tabcanvas.lift( pagename+'_topline' )
	self._currentpage = pagename
	self._indexpage = map( lambda x: x[0], self._pages ).index(pagename)
	p = self._pagedict[pagename]

	tabcanvas.delete('pageframe')
	tabcanvas.create_window(
	    self._nbcenterx,
	    self._nbcentery,
	    window=p,
	    anchor='c',
	    tags='pageframe'
	    )
    tkraise = lift
    def raised(self):
	return self._currentpage

    def page(self,pagename):
	return self._pagedict[pagename]

Pmw.forwardmethods(NoteBookR, Tkinter.Frame, '_hull')