Version 64 of notebook

Updated 2023-05-15 21:56:56 by et4

There is a variety of "notebook" widgets available for Tcl/Tk.

The term "notebook" can mean many things, but in TCL world it particularly refer to a kind of widget. A notebook widget has a row of tabs, like a card index. When one of the tabs is clicked, it is "raised", and the page associated with the tab is displayed. This type of widget is very popular in GUI applications (e.g. the tabbed web browser Firefox).

The notebook widget shipped with TK since 8.5 is ttk::notebook. Besides that, many other GUI packages also offer notebook widgets. Please read below for details.

There is also a Personal Wiki application called Notebook: see Notebook App.


http://incrtcl.sourceforge.net/iwidgets/iwidgets/tabnotebook.gif

Docs can be found at http://incrtcl.sourceforge.net/iwidgets/iwidgets/tabnotebook.html and http://purl.org/tcl/home/man/iwidgets3.0/tabnotebook.n.html

  • The IWidgets command "notebook" is slightly different:

http://incrtcl.sourceforge.net/iwidgets/iwidgets/notebook.gif

See docs at http://incrtcl.sourceforge.net/iwidgets/iwidgets/notebook.html and http://purl.org/tcl/home/man/iwidgets3.0/notebook.n.html

http://classytcl.sourceforge.net/screenshots/notebook.gif

  • MegaWidget package, by Jeff Hobbs contains one. Jeff reports on the MegaWidget package page that this package is not yet 100% updated for changes in Tcl/Tk 8.4.
  • The Tkcon application, also by Jeff Hobbs, uses a simple but effective notebook format for displaying multiple consoles. The notebook is not provided as standalone code: you will need to extract it from the Tkcon source yourself.
  • obTcl for Tcl 7.[456] has one
  • rnotebook has one in pure Tcl/Tk with with full resizability

http://web.archive.org/web/20060826205454/daniel.roche.free.fr/rnotebook/rnotebook.gif

A recent article on comp.lang.tcl gave a comparison by the author, who was looking for some specific features. Sure wish it was easier to reference Google group articles http://groups.google.com/groups?q=tabbed+notebook+comp.lang.tcl&hl=en&lr=&ie=UTF-8&scoring=d&selm=cj07uf%244p8%241%40wagner.wagner.home&rnum=1


Rohan Pall Wow, this page is popular ;) So here's my two cents: I keep examples of each little Tcl/Tk component. This way I don't forget how to do something. Here is my BWidgets NoteBook example. If I remember correctly, I think I learnt this from Bryan Oakley in a comp.lang.tcl newsgroup posting.

BWidget is an excellent package.

The code has been tested with BWidget 1.4.1 on

  • Windows98 the first release, not second edition
  • a custom version of Linux (means I don't remember what I did to it ;)
  package require BWidget

  set nb [NoteBook .nb -side top]
  $nb insert 0 foo -text "foo"
  $nb insert 1 bar -text "bar"

  set pane [$nb getframe foo]
  label $pane.hello -text "hello world"
  pack $pane.hello -fill both -expand 1

  set pane [$nb getframe bar]
  button $pane.fizz -text testing
  pack $pane.fizz -fill both -expand true

  pack $nb -fill both -expand 1
  $nb raise foo

Michael Jacobson Oct 8, 2002 ~ I was trying to get the BWidget NoteBook to work with a popup menu displayed on the active tab when you right clicked on the tab. I thought I would document how to do it below.

http://web.archive.org/web/20070208094238/mywebpages.comcast.net/jakeforce/bwidget_notebook.jpg

 package require BWidget

 ## create a notebook with 2 text panes
 NoteBook .n
 .n insert 0 text1 -text Text1
 .n insert 1 text2 -text Text2
 foreach panel {text1 text2} {
        set pane [.n getframe $panel]
        text $pane.t
        pack $pane.t -fill both -expand 1
 }
 pack .n
 .n raise text1

 ## make a popup menu for the tabs (just add commands)
 menu .popup -tearoff 0 -activeborderwidth 0
 .popup add command -label "mess 1" -command [list puts "in mess 1"]
 .popup add command -label "mess 2" -command [list puts "in mess 2"]
 .popup add separator
 .popup add command -label "mess n" -command [list puts "in mess n"] 

 ## bind right mouse button to the popup menus
 .n bindtabs <Button-3> [list popup .popup %X %Y]

 proc popup {win X Y pane} {
        # check to see if current click is on the top tab
        if {[string equal [.n raise] $pane]} {
                tk_popup $win $X $Y
        }
 }

The Tile notebook provides a <<NotebookTabChanged>> virtual event, to be followed typically by [pathname index current] to determine the specific tab selected.

HaO 2015-06-30: One may also use the managed widget path [pathname select]:

pack [::ttk::notebook .n] -fill both -expand true
foreach tabs {Edit Check Close} {
    ::ttk::entry .n.[string tolower $tabs]
    .n add .n.[string tolower $tabs] -text $tabs
}
# bind after tabs are created to avoid calls in creation
bind .n <<NotebookTabChanged>> tabChanged
proc tabChanged {} {
    if {[.n select] eq ".n.edit"} {
        # If edit page raised, always clear the entry widget
        .n.edit delete 0 end
        .n.edit selection clear
    }
}

Do any notebook implementations allow interaction with sub-elements of the tabs? For example the "Eclipse" IDE organizes its editing tabs with a grey X which turns red when the mouse moves over it, and on which you can click to kill that tab. Alternatively, Firefox has a red X on the far right hand side of the tab area, but separate from the tabs themselves. You can click on that to close the frontmost tab. (escargo 29 Nov 2006 - I believe this was true for Firefox up to version 1.5, but changed in version 2.0.)

It appears as if the Tile notebook doesn't support either of these approaches.

It also does not handle what happens when there is not enough room for all the tabs Firefox allows multiple rows or scrolling buttons (BLT tab widget allows dragging of the tabs with the middle button).

Would it not be possible to change the style for individual tabs rather than for the notebook as a whole ?

2011/05/22 Aud – Actually, I think it might be possible to code something like this. If you modify the Notebook.Tab layout to include a 'close button' element, then you can modify the code binded to <Button-1> on the TNotebook class to detect a click on the close element with the help of %W identify %x %y. This wasn't immediately obvious to me, but when I went snooping around the binds for a few Ttk classes, I discovered a gem that made it click, (try info body ttk::scrollbar::Press in wish :). Of course, I haven't tested this yet, though I really hope it works.

Aud So I was too curious to just leave it. Turns out it works. :) Here's an example:

set image [image create photo -format gif -data {R0lGODlhBwAHAIABAP8AAP///
        yH5BAEKAAEALAAAAAAHAAcAAAIMBIKmsWrIXnLxuDMLADs=}]
ttk::style element create close_button image $image -height 14 -width 14 -sticky e

ttk::style layout TNotebook.Tab {Notebook.tab -sticky nswe -children {
        Notebook.padding -expand 1 -sticky nswe -children {Notebook.label
        -expand 1 -sticky nesw -side left close_button -side right}}}

ttk::notebook .test
ttk::frame .test.frame_a
ttk::frame .test.frame_b
.test add .test.frame_a -text "Frame A"
.test add .test.frame_b -text "Frame B"
pack .test

set script {
        if {[%W identify %x %y] == "close_button"} {
                puts "Hurrah! Close tab [%W index @%x,%y] plz."
                break
        }
}

bind TNotebook <Button-1> "$script\;[bind TNotebook <Button-1>]"

See also A tiny notebook


LV So, does anyone have an example of the Tile notebook widget? In particular, something that runs a command when the tab is selected.

ET Here is small BWidget notebook program that was a response to a post on clt. It is an example where tabs have a close box right side image, a popup menu for the tabs (with close and move tab commands), and uses the mouse wheel to scroll between tabs (put the mouse pointer over any tab and use the wheel). The close1 image now has an alpha channel, hence the larger size. It makes the area around the X transparent, so it shows the canvas color behind it (image built using gimp tool). This code needs BWidget 1.9.13 or later (circa 2018).

Here's a screenshot:

NoteBook

package require Tk  
package require BWidget 1.9.13 ;# for right side images in tabs
wm geom . 877x161+92+213
catch {console show ; console eval {wm geom . 69x24+15+6}}

# normal and active, must be same size right side images

#here's with an alpha channel
set close1 [image create photo -data {

iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAACpF6WWAAAAw3pUWHRSYXcgcHJvZmlsZSB0eXBl
IGV4aWYAAHjabVDbEcMgDPtnio6AkQEzDmnSu27Q8WtiJxeaKof8EKcYh+3zfoXHQCIOnKuUVkpU
cOOWuiYSDX1niryzFdk1mvvhFJK2oBFWSvH7R59OAwtds3wxkqcLyyw0dn/5MUoWMCYa+epGzY2Q
TCA36PasWJrU6xOWLc4QO2EQyzz2ra66vTXrf5DSBkJUBtgGwDgI6JpkZULWi6RfB6Mqq+xmupB/
ezoQvuQ5WRQoZReIAAABhWlDQ1BJQ0MgcHJvZmlsZQAAeJx9kT1Iw0AcxV9TS4tUCtpBxCFDdbKL
iohTqWIRLJS2QqsOJpd+QZOGJMXFUXAtOPixWHVwcdbVwVUQBD9AXF2cFF2kxP81hRYxHhz34929
x907QGhWmWr2xQBVs4x0Ii7m8qui/xU+hDCIAOYkZurJzGIWruPrHh6+3kV5lvu5P8eAUjAZ4BGJ
Y0w3LOIN4plNS+e8TxxmZUkhPieeMOiCxI9clx1+41xqs8Azw0Y2PU8cJhZLPSz3MCsbKvE0cURR
NcoXcg4rnLc4q9U669yTvzBY0FYyXKc5igSWkEQKImTUUUEVFqK0aqSYSNN+3MU/0vanyCWTqwJG
jgXUoEJq+8H/4He3ZnFq0kkKxgHfi21/jAH+XaDVsO3vY9tunQDeZ+BK6/prTWD2k/RGV4scAaFt
4OK6q8l7wOUOMPykS4bUlrw0hWIReD+jb8oDQ7dA/5rTW2cfpw9AlrpavgEODoHxEmWvu7w70Nvb
v2c6/f0AvtdyxZ81q2EAAA12aVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVn
aW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5z
Ong9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRm
OlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1u
cyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0
cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMu
YWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpkYz0iaHR0
cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3
LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYv
MS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1w
TU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOmZjMmE1ODYxLTk3OWQtNGVhYS05MzZkLTVm
OWExODgzZGMzZSIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyYTliNzc5YS00MGRkLTQ5
OTUtYmY1ZS0xOGE3MTM4ZmQzZDQiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRp
ZDpmZmIyZjhkYy04OTJlLTRhMzYtYTdhMS04MmNmYjdlYmNhMTQiCiAgIGRjOkZvcm1hdD0iaW1h
Z2UvcG5nIgogICBHSU1QOkFQST0iMi4wIgogICBHSU1QOlBsYXRmb3JtPSJXaW5kb3dzIgogICBH
SU1QOlRpbWVTdGFtcD0iMTY4NDE3NjQ0MzE5NzY1OCIKICAgR0lNUDpWZXJzaW9uPSIyLjEwLjM0
IgogICB0aWZmOk9yaWVudGF0aW9uPSIxIgogICB4bXA6Q3JlYXRvclRvb2w9IkdJTVAgMi4xMCIK
ICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMzowNToxNVQxMTo0NzoyMy0wNzowMCIKICAgeG1wOk1v
ZGlmeURhdGU9IjIwMjM6MDU6MTVUMTE6NDc6MjMtMDc6MDAiPgogICA8eG1wTU06SGlzdG9yeT4K
ICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAg
ICAgc3RFdnQ6Y2hhbmdlZD0iLyIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDphZTVk
NjllNS1kMDkxLTQ3ZmUtYWJhZi00ZDM3NjNkY2UwZmYiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdl
bnQ9IkdpbXAgMi4xMCAoV2luZG93cykiCiAgICAgIHN0RXZ0OndoZW49IjIwMjMtMDUtMTVUMTE6
NDc6MjMiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogIDwvcmRmOkRlc2Ny
aXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAog
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
CiAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+fzzjKwAAAAZi
S0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB+cFDxIvF9l71AgAAAE5
SURBVDjLrdQtjoNAHAXwCQcYMwIzYgymAhIaTEUPwMeRMAhCgkFgsBhMBQZBNqmpwGAxFSAwcIq3
YrPZlNB2O/D8/JKX/OcRXddRVRXIDun7HoZhgNR1DcbYZnieZ800TZxOpx+nrmtQSlGWpTQshIBt
24/vr9erFNz3PY7HI1zXXX93u91AKcXlcoFU5WdpmgaUUhRFAanKr2DG2FP4beVXsKqqyPMcUpWf
pW1bMMYe4I8qv4OjKIJlWfA8b5ePQqqqgqIosCxrH3CaprNpmrBtG4wxZFm2HRZCwHEcEEJI13VQ
VRVpmkJ6HNbOpus6cM6RJAm2jcMi9/v9c/g/Z/MLx3GMbeOwyDAM4JwjDENsG4dFxnH84pwjCAJs
G4cVWAjxB0uPwwqsaRp83wcxDEN+HFbgw+GAb9Ni+U6zJj5PAAAAAElFTkSuQmCC

}]

set close2 [image create photo -data {

iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAIAAAAmdTLBAAAABGdBTUEAAK/INwWK6QAAABl0RVh0
U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAGmSURBVHjaYtDT09u6desPDg5S0b179/T1
9QECiGHnzp3CwsKkGvHy5UsjIyMrKyuAAGIAcoBG8PHxbdiwgUjNb968UVBQ8PLy+vHjB0AAMUCE
9u3bR6QRQGcbGxv7+Pi8f/8eyAUIIAa4xOHDh4FGrFmzhhhnw0UAAogBWfr48eNAI5YvX07Q2XBB
gABiQFMENAIYnJhGoDkbjgACiAHTHqAR4uLiixYtwuNsOAIIIAasTj1z5gzQFRAjsDobjgACiAFX
UEGM6OjoMDU19fX1RXM2HAEEECPIDBxg79q1QJ1Abx++fBmXGoAAwmn/ixcvgH4GOhvoipkzZ+JS
BhBADHiiytvbG8i+evUqMDinTJmCVSVAADEQTGEQI2RkZCZMmICpGCCAGAimMAi6desWViMAAoiB
YApDM6K7uxtZECCAGAimMGR0//59oBFtbW1wEYAAYiCYwtDQ48ePgUY0NTVBuAABxEDQ2ViNAKqH
GAEQQIxAZ4eGhkpKSi5evJhTUpKBOPD69m0nJ6eIiAiAAGIElmHc3Nz7zp1jIBEAjXB3dwcIMAA7
8dZ08a6UVAAAAABJRU5ErkJggg==

}]


proc main {} {
    global nb close1 close2
    set ::Widget::_theme 1 ;# undocumented feature ??, will use ttk::frame for tab container frame
    
    set nb [NoteBook .nb -side top -bd 1  -font {{consolas bold italic} 13} -homogeneous 0 -arcradius 8 -bg grey85]
    catch {bind ${nb}.c <MouseWheel> [list wheel $nb %D]  
           bind    ${nb}.c <Button-4>         [list wheel $nb 1]  
           bind    ${nb}.c <Button-5>         [list wheel $nb -1] 
    } ;# if this fails, well no mouse wheel support bind to .c canvas with tabs
    pack $nb -fill both -expand 1
        
    foreach tab {notebook tiletable dragdrop2 ver extra1 extra2} { ;# generate 6 tabs, a text and 5 buttons
        $nb insert end $tab -text "   $tab.tcl  " -rimage $close1 -ractiveimage $close2 \
                -rimagecmd [list close $nb] -activeforeground red -createcmd [list created $tab] -raisecmd [list raised $nb $tab]
    }
    
    set pane [$nb getframe notebook] ;# this one gets a text widget in the frame
    text $pane.hello  -font {{comic sans MS} 20}
    $pane.hello insert end  "hello notebook\nhere is some text"
    pack $pane.hello  -fill both -side top -expand 1
    
    foreach {tab fill} {tiletable both   dragdrop2 y   ver x   extra1 none   extra2 both} { ;# rest are just some buttons
        pack [ttk::button [$nb getframe $tab].foo -text "$tab-button" \
             -command [list puts "$tab button pushed"]]  -fill $fill -expand true 
    }
    $nb raise notebook
    
    #       make a popup menu for the tabs
    menu .popup -tearoff 0 -activeborderwidth 0
    .popup add command -label "close tab"           -command [list close $nb] 
    .popup add separator 
    .popup add command -label "move tab to front"   -command [list moveit $nb 0] 
    .popup add command -label "move tab to end"     -command [list moveit $nb end] 
    
    #       bind right mouse button to the popup menus
    $nb bindtabs <Button-3> [list popup $nb .popup %X %Y]
    
    return
}

proc wheel {args} {
    lassign $args nb dir
    set cur [$nb raise]
    set index [$nb index $cur]
    if { $dir >= 0 } {
        if { [incr index -1] >= 0 } {
            $nb raise [$nb pages $index]
            $nb see [$nb  raise]
        }
    } else {
        if { [incr index] < [llength [$nb pages]] } {
            $nb raise [$nb pages $index]
            $nb see [$nb  raise]
        }
    }
}
proc created {args} {
#   puts "created args= |$args| " ;# first time (only) it's raised
}
proc raised {pathname args} {
#   global nb
#   puts "raised args= |$args| "
    if [catch {
        foreach page [$pathname pages] {
            if { [$pathname raise] eq $page } {
#               puts "page= |$page| active"
                $pathname itemconfigure $page -background white
            } else {
#               puts "page= |$page| "
                $pathname itemconfigure $page -background grey85
            }
        }
    } err_code] {
        puts $err_code
    }
}


proc popup {pathname win X Y pane} {
# also raise on right click     
    $pathname raise $pane
    tk_popup $win $X $Y
}
proc close {pathname args} {
    $pathname delete [$pathname raise]  ;# current pane
    $pathname raise [$pathname pages 0] ;# activate and see first one
    catch {$pathname see [$pathname pages 0]} ;# This will fail if no tabs left
    
}
proc moveit {pathname where args} {
    set pane [$pathname raise] ;# current pane
    $pathname move $pane $where
    $pathname see $pane
    
}

main