- Add a button (though only programatically, at present)
 - Delete a button (just drag it off the toolbar while customizing, or selected Delete from the context menu)
 - Start "Customizing" by right-clicking the toolbar and selecting it. You can stop customizing in the same way
 - Select whether or not a button "begins a group", by adding a separator before it (from context menu)
 - Change how a button is displayed (toolbar's default style, text only, image only, or both, also from context menu)
 - Reset a button (to toolbar's default display style/default text/default icon). There's currently no way to alter the icon/text used, except from the console, so resetting doesn't do a lot.
 
- Ability to add buttons properly, by dragging them onto the toolbar
 - Ability to move buttons, by dragging them along the toolbar
 - Ability to alter buttons more - their name, icon, more...?
 - Probably more I haven't thought of, yet.
 
The code:}
 namespace eval ::toolbar {}
 array set ::toolbar::functions {
            0,icon ::img::new
            0,text "New Document"
            0,cmd  "do_new_document"
            1,icon ::img::open
            1,text "Open Document"
            1,cmd  "do_open_document"
            2,icon ::img::save
            2,text "Save Document"
            2,cmd  "do_save"
            3,icon ::img::save
            3,text "Save As..."
            3,cmd  "do_save_as"
            4,icon ::img::print
            4,text "Print"
            4,cmd  "do_print"
 };# array set ::toolbar::functions
 proc ::toolbar::startCustomize {tb} {
   bind ToolbarButton <Enter> break
   bind ToolbarButton <Leave> break
   bind ToolbarButton <ButtonPress-1> {::toolbar::select %W ; break}
   bind ToolbarButton <B1-Motion> {::toolbar::drag %W %X %Y; break} ;# to allow moving/deleting buttons by dragging
   bind ToolbarButton <ButtonRelease-1> {::toolbar::dragRelease %W %X %Y ; break} ;# as above
   bind ToolbarButton <Key-space> {::toolbar::select %W ; break}
   bind ToolbarButton <3> {::toolbar::select %W ; ::toolbar::showOptions %W %X %Y ; break}
   bind Toolbar <3> {::toolbar::showToolbarOptions %W %X %Y 1; break}
 };# tb / startCustomize
 proc ::toolbar::endCustomize {tb} {
   bind ToolbarButton <Enter> continue
   bind ToolbarButton <Leave> continue
   bind ToolbarButton <ButtonPress-1> continue
   bind ToolbarButton <ButtonRelease-1> continue
   bind ToolbarButton <Key-space> continue
   bind ToolbarButton <3> {::toolbar::showToolbarOptions [winfo parent %W] %X %Y 0; break}
   bind Toolbar <3> {::toolbar::showToolbarOptions %W %X %Y 0; break}
   if { [info exists ::toolbar::this($tb,selected)] && [winfo exists $::toolbar::this($tb,selected)] } {
        ::toolbar::deselect $tb
      }
 };# tb / endCustomize
 proc ::toolbar::resetButton {btn} {
   variable functions
   set tb [winfo parent $btn]
   if { [winfo class $tb] != "Toolbar" } {
        return;
      }
   upvar 0 ::toolbar::this local
   set func $local($tb,func,$btn)
   set local($tb,text,$btn) $functions($func,text)
   set local($tb,icon,$btn) $functions($func,icon)
   ::toolbar::setCompound $btn default
 };# ::toolbar::resetButton
 proc ::toolbar::drag {w x y} {
   set container [winfo containing $x $y]
   set tb [winfo parent $w]
   if { $container != $tb && ![string match "${tb}.*" $container] } {
        # it's being dragged off the toolbar
        $w configure -relief ridge
      } else {
        $w configure -relief solid
      }
 };# tb / drag
 proc ::toolbar::dragRelease {w x y} {
   set container [winfo containing $x $y]
   set tb [winfo parent $w]
   if { $container != $tb && ![string match "${tb}.*" $container] } {
        # it's been dragged off the toolbar - delete it!
        ::toolbar::delete $w
        return;
      }
   #abc check if it's been moved, and where it should go to
 };# tb / dragRelease
 proc ::toolbar::delete {w} {
   upvar 0 ::toolbar::this local
   set tb [winfo parent $w]
   set pos [lsearch -exact $local($tb,bar) $w]
   set local($tb,bar) [lreplace $local($tb,bar) $pos $pos]
   destroy $w
   catch {destroy $local($tb,beginWidget,$w)}
   array unset local $tb,*,$w)
 };# tb / delete
 proc ::toolbar::toggleBegin {w} {
   variable counter
   set tb [winfo parent $w]
   upvar 0 ::toolbar::this local
   if { [info exists local($tb,beginWidget,$w)] } {
        destroy $local($tb,beginWidget,$w)
        unset local($tb,beginWidget,$w)
        set local($tb,beginBool,$w) 0
      } else {
        set begin [frame $tb.[incr counter($tb)] -width 2 -borderwidth 1 -relief ridge -bg grey65]
        pack $begin -before $w -padx 5 -side left -pady 1 -fill y
        set local($tb,beginWidget,$w) $begin
        set local($tb,beginBool,$w) 1
      }
 };# tb / toggleBegin
 proc ::toolbar::showToolbarOptions {tb x y customizing} {
   if { [winfo class $tb] != "Toolbar" } {
        return;
      }
   #abc show right-click menu with "Customize" option to start customizing!
   set w .toolbarOptionsMenu
   catch {destroy $w}
   menu $w -tearoff 0
   if { $customizing } {
        $w add command -label "Stop Customizing" -underline 0 -command [list ::toolbar::endCustomize $tb]
      } else {
        $w add command -label "Customize..." -underline 0 -command [list ::toolbar::startCustomize $tb]
      }
   $w post $x $y
 };# tb / showToolbarOptions
 proc ::toolbar::showOptions {btn x y} {
   if { [lsearch [bindtags $btn] "ToolbarButton"] < 0 } {
        return;
      }
   #abc do stuff!
   set tb [winfo parent $btn]
   set w .toolbarButtonOptions
   catch {destroy $w}
   #toplevel $w
   #wm withdraw .
   #wm overrideredirect $w 1
   #wm title $w "Toolbar Customization"
   #bind $w <FocusOut> {if { [winfo toplevel %W] == %W } {destroy %W}}
   menu $w -tearoff 0
   $w add command -label "Reset" -underline 0 -command [list ::toolbar::resetButton $btn]
   $w add command -label "Delete" -underline 0 -command [list ::toolbar::delete $btn]
   $w add separator
   $w add checkbutton -label "Begin a group?" -variable ::toolbar::this($tb,beginBool,$btn) \
                      -command [list ::toolbar::toggleBegin $btn]
   $w add separator
   $w add radiobutton -label "Default Style" -variable ::toolbar::this($tb,compound,$btn) \
                      -value "default" -command [list ::toolbar::setCompound $btn var]
   $w add radiobutton -label "Text Only" -variable ::toolbar::this($tb,compound,$btn) \
                      -value "text" -command [list ::toolbar::setCompound $btn var]
   $w add radiobutton -label "Image Only" -variable ::toolbar::this($tb,compound,$btn) \
                      -value "image" -command [list ::toolbar::setCompound $btn var]
   $w add radiobutton -label "Image and Text" -variable ::toolbar::this($tb,compound,$btn) \
                      -value "both" -command [list ::toolbar::setCompound $btn var]
   $w post $x $y
   #wm geography $w $x $y
   #wm deiconify $w
 };# tb / showOptions
 proc ::toolbar::select {w} {
   set parent [winfo parent $w]
   ::toolbar::deselect $parent
   $w configure -border 2 -relief solid
   set ::toolbar::this($parent,selected) $w
 };# tb / select
 proc ::toolbar::deselect {tb} {
   upvar 0 ::toolbar::this local
   if { [info exists local($tb,selected)] && [winfo exists $local($tb,selected)] } {
        set w $local($tb,selected)
        $w configure -border $local($tb,border) -relief $local($tb,relief)
        set local($tb,selected) ""
      }
 };# tb / deselect
 proc ::toolbar::toolbar {w args} {
   variable counter
   if { [winfo exists $w] } {
        set par [winfo parent $w]
        set len [string length $par]
        if { $len > 1 } {
             incr len
           }
        set this [string range $w $len end]
        error "window name \"$this\" already exists in parent"
      }
   set ::toolbar::this($w,relief) flat
   set ::toolbar::this($w,border) 2
   set ::toolbar::this($w,compound) image
   set ::toolbar::this($w,overrelief) raised
   set ::toolbar::this($w,bar) [list]
   set options [list]
   foreach {name value} $args {
         if { $name == "-buttonrelief" } {
              set ::toolbar::this($w,relief) $value
            } elseif { $name == "-buttonoverrelief" } {
              set ::toolbar::this($w,overrelief) $value
            } elseif { $name == "-buttonborder" } {
              set ::toolbar::this($w,border) $value
            } elseif { $name == "-buttoncompound" } {
              set ::toolbar::this($w,compound) $value
            } else {
              lappend options $name $value
            }
        }
   set counter($w) 0
   set frame [eval ::frame $w -class Toolbar $options -padx 3]
   bindtags $frame [linsert [bindtags $frame] 1 "Toolbar"]
   ::toolbar::endCustomize $frame ;# setup default bindings
   return $frame;
 };# tb / toolbar
 proc ::toolbar::button {tb func {pos "end"}} {
   variable functions
   variable this
   variable counter
   if { [winfo class $tb] != "Toolbar" } {
        error "window \"$tb\" is not a toolbar widget"
      }
   if { ![info exists functions($func,cmd)] } {
        error "invalid toolbar function \"$func\""
      }
   set button $tb.[incr counter($tb)]
   ::button $button -relief $this($tb,relief) -overrelief $this($tb,overrelief) -border $this($tb,border) \
                                  -command $functions($func,cmd)
   upvar 0 ::toolbar::this local
   #($tb,bar) bar
   set local($tb,bar) [linsert $local($tb,bar) $pos $button]
   set pos [lsearch -exact $local($tb,bar) $button]
   if { $pos == "0" } {
        pack $button -side left -padx 1 -pady 1 -anchor nw
      } else {
        pack $button -side left -padx 1 -pady 1 -anchor nw -after [lindex $local($tb,bar) [expr {$pos-1}]]
      }
   bindtags $button [linsert [bindtags $button] 0 ToolbarButton]
   ::toolbar::balloon $button
   set local($tb,func,$button) $func
   set local($tb,text,$button) $functions($func,text)
   set local($tb,icon,$button) $functions($func,icon)
   set local($tb,beginBool,$button) 0
   ::toolbar::setCompound $button default
   return $button;
 };# tb / button
 proc ::toolbar::setCompound {w {compound default}} {
   upvar 0 ::toolbar::this local
   variable functions
   set tb [winfo parent $w]
   set func $local($tb,func,$w)
   if { $compound == "var" } {
        # use the var setting for this button
        set compound $local($tb,compound,$w)
      }
   if { $compound != "text" && $compound != "image" && $compound != "both" && $compound != "default" } {
        set compound $local($tb,compound) ;# bad value, so we use the toolbar default
      }
   if { $compound == "default" } {
        set compoundDisp "default"
        set compound $local($tb,compound)
      } else {
        set compoundDisp $compound
      }
   if { $compound == "text" || $compound == "both" } {
        if { $local($tb,text,$w) == "" } {
             if { $functions($func,text) == "" && $compound == "text" } {
                  set text "Function $func"
                } else {
                  set text $functions($func,text)
                }
           } else {
             set text $local($tb,text,$w)
           }
       } else {
         set text ""
       }
   set image "" ; set text ""
   if { $compound == "image" || $compound == "both" } {
        if { ![catch {image type $local($tb,icon,$w)}] } {
             # use button-specific image
             set image $local($tb,icon,$w)
           } elseif { ![catch {image type $functions($func,icon)}] } {
             # use function-specific image
             set image $functions($func,icon)
           } else {
             # fall back to just text
             set compound "text"
           }
      }
   if { $compound == "text" || $compound == "both" } {
        if { $local($tb,text,$w) != "" } {
             # use button-specific text
             set text $local($tb,text,$w)
           } elseif { $functions($func,text) != "" } {
             # use function-specific text
             set text $functions($func,text)
           } else {
             # if we're on compound == text (not both), use default text
             if { $compound == "text" } {
                  set text "Function $func"
                }
           }
       }
   if { $image == "" || $text == "" } {
        set compound "none"
      } else {
        set compound "left"
        set text " $text" ;# add a single space before text, for a better appearance.
      }
   $w configure -image $image -text $text -compound $compound
   set local($tb,compound,$w) $compoundDisp
 };# tb / setCompound
 proc ::toolbar::balloon {w} {
     bind $w <Any-Enter> "after 450 [list ::toolbar::balloonShow %W]"
     bind $w <Any-Leave> [list destroy %W.balloon]
 };# tb / balloon
 proc ::toolbar::balloonShow {w} {
   if { [eval winfo containing  [winfo pointerxy .]] != $w } {
        return;
      }
   set tb [winfo parent $w]
   set text $::toolbar::this($tb,text,$w)
   set top $w.balloon
   catch {destroy $top}
   toplevel $top
   wm title $top $text
   $top configure -bd 1 -bg black
   wm overrideredirect $top 1
   pack [message $top.txt -aspect 10000 -bg lightyellow \
         -font {"" 8} -text $text -padx 1 -pady 0]
   bind $top <ButtonPress-1> {catch {destroy [winfo toplevel %W]}}
   set wmx [winfo pointerx $w]
   set wmy [expr [winfo rooty $w]+[winfo height $w]]
   if {[expr $wmy+([winfo reqheight $top.txt]*2)]>[winfo screenheight $top]} {
       incr wmy -[expr [winfo reqheight $top.txt]*2]
      }
   if {[expr $wmx+([winfo reqwidth $top.txt]+5)]>[winfo screenwidth $top]} {
       incr wmx -[expr [winfo reqwidth $top.txt]*2]
       set wmx [expr [winfo screenwidth $top]-[winfo reqwidth $top.txt]-7]
      }
   wm geometry $top \
      [winfo reqwidth $top.txt]x[winfo reqheight $top.txt]+$wmx+$wmy
   raise $top
 };# tb / balloonShow
 namespace eval ::img {}
 image create photo ::img::new -data {
    R0lGODlhEAAQAIUAAPwCBFxaXNze3Ly2rJyanPz+/Ozq7GxqbPz6/GxubNTK
    xDQyNIyKhHRydERCROTi3PT29Pz29Pzy7PTq3My2pPzu5PTi1NS+rPTq5PTe
    zMyynPTm1Pz69OzWvMyqjPTu5PTm3OzOtOzGrMSehNTCtNS+tAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAZ/
    QAAgQCwWhUhhQMBkDgKEQFIpKFgLhgMiOl1eC4iEYrtIer+MxsFRRgYe3wLk
    MWC0qXE5/T6sfiMSExR8Z1YRFRMWF4RwYIcYFhkahH6AGBuRk2YCCBwSFZgd
    HR6UgB8gkR0hpJsSGCAZoiEiI4QKtyQlFBQeHrVmC8HCw21+QQAh/mhDcmVh
    dGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAx
    OTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRl
    dmVsY29yLmNvbQA7
 }
 image create photo ::img::open -data {
    R0lGODlhEAAQAIUAAPwCBAQCBOSmZPzSnPzChPzGhPyuZEwyHExOTFROTFxa
    VFRSTMSGTPT29Ozu7Nze3NTS1MzKzMTGxLy6vLS2tLSytDQyNOTm5OTi5Ly+
    vKyqrKSmpIyOjLR+RNTW1MzOzJyenGxqZBweHKSinJSWlExKTMTCxKyurGxu
    bBQSFAwKDJyanERCRERGRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaR
    QIBwGCgGhkhkEWA8HpNPojFJFU6ryitTiw0IBgRBkxsYFAiGtDodDZwPCERC
    EV8sEk0CI9FoOB4BEBESExQVFgEEBw8PFxcYEBIZGhscCEwdCxAPGA8eHxkU
    GyAhIkwHEREQqxEZExUjJCVWCBAZJhEmGRUnoygpQioZGxsnxsQrHByzQiJx
    z3EsLSwWpkJ+QQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9u
    IDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2
    ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7
 }
 image create photo ::img::save -data {
    R0lGODlhEAAQAIUAAPwCBAQCBFRSVMTCxKyurPz+/JSWlFRWVJyenKSipJSS
    lOzu7ISChISGhIyOjHR2dJyanIyKjHx6fMzOzGRiZAQGBFxeXGRmZHRydGxq
    bAwODOTm5ExOTERGRExKTHx+fGxubNza3Dw+PDQ2NAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaA
    QIAQECgOj0jBgFAoBpBHpaFAbRqRh0F1a30ClAhuNZHwZhViqgFhJizSjIZX
    QCAoHOKHYw5xRBiAElQTFAoVQgINFBYXGBkZFxYHGRqIDBQbmRwdHgKeH2Yg
    HpmkIR0HAhFeTqSZIhwCFIdIrBsjAgcPXlBERZ4Gu7xCRZVDfkEAIf5oQ3Jl
    YXRlZCBieSBCTVBUb0dJRiBQcm8gdmVyc2lvbiAyLjUNCqkgRGV2ZWxDb3Ig
    MTk5NywxOTk4LiBBbGwgcmlnaHRzIHJlc2VydmVkLg0KaHR0cDovL3d3dy5k
    ZXZlbGNvci5jb20AOw==
 }
 image create photo ::img::print -data {
    R0lGODlhEAAQAIUAAPwCBFRKNAQCBPz+/MTCxExKLPTq5Pz29Pz6/OzezPT2
    9PTu7PTy7NzClOzm1PTu5LSabJyanPTm3FxaXOzCjOTKrOzi1OzaxOTSvJye
    nGRmZLyyTKSipDQyNERCROTi5Hx+fMzKzJSSlIyOjISChLS2tAT+BDw6PAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaY
    QIBwKAwIBMTkMDAYEApIpVBgOCAOg4RRGlAoEAuGIdGITgWOq4LxcCQgZkEk
    IHksHgYJOR6ZQCgVFhYJFxgTBVMZihoCfxUYDWUbUBGKGREcjBoQEB2TAB4C
    Ax+Vl5WMhyACHiEhH6IfIiMktCQgE0cZJQStr6O2t6EARxO6vK6iEx4dZsMC
    xbsmBB4nzUTEutVSSUdmfkEAIf5oQ3JlYXRlZCBieSBCTVBUb0dJRiBQcm8g
    dmVyc2lvbiAyLjUNCqkgRGV2ZWxDb3IgMTk5NywxOTk4LiBBbGwgcmlnaHRz
    IHJlc2VydmVkLg0KaHR0cDovL3d3dy5kZXZlbGNvci5jb20AOw==
 }
 catch {console show}
 ###### TEST #######
 pack [set toolbar [toolbar::toolbar .tb]] -side top -fill x -anchor nw
 toolbar::button $toolbar 0
 toolbar::button $toolbar 1
 ::toolbar::toggleBegin [toolbar::button $toolbar 2]
 toolbar::button $toolbar 4
 pack [frame .btm] -side top -expand 1 -fill both
 pack [text .btm.txt -yscrollcommand ".btm.sb set" -wrap word] -side left -expand 1 -fill both
 pack [scrollbar .btm.sb -command ".btm.txt yview"] -side left -fill y
 catch {wm state . zoomed}if 0 {[Category Widget] | toolbar}
