"""
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 types
import Tkinter
import Pmw

TRUE  = 1
FALSE = 0

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)

	# Get default colors.
	# We don't quite capture the Windows look yet, which has
	# "rounded" edges, using two different shades on each side.
	self.FACE = interior.cget('background')
	face_rgb = self.winfo_rgb(self.FACE)
	bri = Pmw.Color.rgb2brightness(face_rgb) / 65536.0
	hibri = min(1.0, bri + 0.2)
	self.HILIGHT = Pmw.Color.changebrightness(self, self.FACE, hibri)
	lobri = max(0.0, bri - 0.35)
	self.SHADOW = Pmw.Color.changebrightness(self, '#000', lobri)

	self._nbframe = self.createcomponent(
	    'nbframe', 
	    (), None,
	    Tkinter.Canvas, (self.component('hull'),),
	    bd=0,
	    height=0,
	    width=0,
	    highlightcolor=self.FACE # hide the black border
	    )

	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, w=1, h=1):
        if e:
            w,h = e.width, e.height
	self._makereqsize(w,h)
	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

	# Clean up drawing the tab outlines.
	# It will require more work to make the active tab
	# a little taller than the others.  We'll need to
	# reserve additional space at the top of the widget.
	l = x+d;     li = x+2*d
	r = x+3*d+w; ri = x+2*d+w-1
	ti = 3*d

	lightcoords   = (
	    l, h,
	    l, ti,
	    li, d,
	    ri, d
	    )
	shadowcoords = (
	    ri, d,
	    r, ti,
	    r, h-d+1
	    )
	lightkw = {'fill': self.HILIGHT, 'width': d, 'tags': pagename+'_light'}
	shadowkw = {'fill': self.SHADOW, 'width': d, 'tags': pagename+'_dark'}

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

	tabcanvas.create_line(
	    x+d,  h,
	    x+4*d+w, h,
	    fill=self.HILIGHT,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=self.HILIGHT,
	    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=self.HILIGHT,
	    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=self.SHADOW,
	    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,w=1,h=1):
	self.update_idletasks()
	tabcanvas = self.component('nbframe')

	ipadx = self['ipadx']
	ipady = self['ipady']

	manager = self.winfo_manager()
	if manager == '':
	    return
	mgr_info = getattr(self, manager + '_info')()
	try:
	    mgr_ipadx = int(mgr_info['ipadx'])
	    mgr_ipady = int(mgr_info['ipady'])
	except KeyError:
	    # NEED FIX for place mgr
	    raise ValueError, \
		'Sorry, resizing works only with grid and pack mgrs'

	highlightthickness = \
	    string.atoi(tabcanvas.cget('highlightthickness')) + \
	    string.atoi(self.cget('hull_highlightthickness'))
	reqw = w - 2*(ipadx + mgr_ipadx + highlightthickness)
	reqh = h - 2*(ipady + mgr_ipady + highlightthickness) - self._tabheight

	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*ipadx])
	self._nbheight = self._tabheight + 2*ipady + self._pageheight
	self._nbcenterx = self._nbwidth/2
	self._nbcentery = self._tabheight + 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) == types.StringType:
	    pagename = pagenameOrIndex
	else:
	    if len(self._pages) <= pagenameOrIndex:
		return
	    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=self.HILIGHT)
	    tabcanvas.lower( self._currentpage+'_topline' )
	    self._pagedict[self._currentpage]._lower()

	tabcanvas.itemconfigure( pagename+'_bottom', fill=self.FACE )
	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,
	    width=self._pagewidth,
	    height=self._pageheight,
	    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')