package require Metawidget # name: Toolbar # args: args: option-value pairs # creates inner widgets and bindings. initializes variables. metawidget create Toolbar { # padding of 2 pixel between outer frame and the buttons pack [frame $this.frm -border 2 -relief flat] -side top -fill both -expand 1 # popup menu for right mouse key menu $this.menu -tearoff 0 $this.menu add command -label Top -command "$this config -side top" $this.menu add command -label Bottom -command "$this config -side bottom" $this.menu add command -label Left -command "$this config -side left" $this.menu add command -label Right -command "$this config -side right" # this is for X11 only. it's a noop on Windows and Mac bind $this.menu "$this.menu unpost" # new class bindings make Toolbar draggable bind Toolbar$this <1> "$this _click" bind Toolbar$this "$this _drag %X %Y" bind Toolbar$this <3> "$this.menu post %X %Y; focus $this.menu" # buttons get a nice roll-over effect bind Toolbutton$this {%W config -relief raised} bind Toolbutton$this {%W config -relief flat} my wParent [winfo parent $this] # set options by calling their member functions -side top -state normal } {} -default frm -border 1 -relief raised # name: _click # args: - # called when clicked. stores the coordinates of the parent window. metawidget proc Toolbar _click {} { my iX1 [expr [winfo rootx [my wParent]]+20] my iY1 [expr [winfo rooty [my wParent]]+20] my iX2 [expr [my iX1]+[winfo width [my wParent]]-40] my iY2 [expr [my iY1]+[winfo height [my wParent]]-40] } # name: _drag # args: iPx, iPy: mouse coordinates # called when dragged. detects if the mouse is outside of any of the four # sides of the parent window and places the Toolbar accordingly. metawidget proc Toolbar _drag { iPx iPy } { if { $iPx < [my iX1] && [my -side] != "left" } { -side left } elseif { $iPx > [my iX2] && [my -side] != "right" } { -side right } elseif { $iPy < [my iY1] && [my -side] != "top" } { -side top } elseif { $iPy > [my iY2] && [my -side] != "bottom" } { -side bottom } } # name: _checkItem # args: sTag: item tag # throws an exception if the given tag does not exists. tests on # existence of a private variable. metawidget proc Toolbar _checkItem { sTag } { if { ! [myinfo exists _$sTag.wWidget] } { error "Toolbar $this: Item $sTag does not exist." } } # name: _redraw # args: - # redraws the Toolbar. computes the values for the -fill and -side options # and the first child of the parent. packs the Toolbar before that child. metawidget proc Toolbar _redraw {} { set sFill [mkw.decode [my -side] "top x bottom x left y right y"] set sInner [mkw.decode [my -side] "top left bottom left left top right top"] set sFirst [lindex [pack slaves [my wParent]] 0] # pack Toolbar *before* first child (if any) if { $sFirst == {} } { pack $this -side [my -side] -fill $sFill } elseif { [lsearch [bindtags $sFirst] "Statusbar"] != -1 } { pack $this -side [my -side] -fill $sFill -after $sFirst } else { pack $this -side [my -side] -fill $sFill -before $sFirst } # pack the buttons inside the Toolbar. foreach sWidget [winfo children $this.frm] { if { [winfo class $sWidget] == "Frame" } { # special treatment for separator frames if { $sFill == "x" } { pack $sWidget -side $sInner -fill y -padx 4 -pady 1 } else { pack $sWidget -side $sInner -fill x -padx 1 -pady 4 } } else { # all other buttons pack $sWidget -side $sInner -fill both } } } # name: _bindTag # args: sWhat: operation (extend or shrink) # wWidget: widget to modify # sBindTag: bindtag to add or remove # adds or removes a bindtag to a widget. metawidget proc Toolbar _bindTag { sWhat wWidget sBindTag } { bindtags $wWidget [mkw.l$sWhat [bindtags $wWidget] $sBindTag] } # name: _checkButton # args: sTag: item tag # iState: optional new state (boolean) # handles checkbuttons. makes them look "checked" (=sunken) respectively # normal again. metawidget proc Toolbar _checkButton { sTag {iState {}} } { # either toggle state or set new state from iState if { $iState == {} } { my _$sTag.iState [expr ! [my _$sTag.iState]] } else { my _$sTag.iState [mkw.complete $iState {on off yes no 1 0}] } # change relief and add resp. remove bind tag for roll-over effect if { [my _$sTag.iState] } { [my _$sTag.wWidget] config -relief sunken _bindTag shrink [my _$sTag.wWidget] Toolbutton$this } else { [my _$sTag.wWidget] config -relief flat _bindTag extend [my _$sTag.wWidget] Toolbutton$this } } # name: _radioButton # args: sTag: item tag # handles radiobuttons. sets all buttons of a group to normal state and # then makes the clicked one look checked. metawidget proc Toolbar _radioButton { sTag } { set wButton $this.frm._$sTag # reset all radiobuttons of the group foreach sGroupTag [my __[my _$sTag.sGroup]] { $this.frm._$sGroupTag config -relief flat _bindTag extend $this.frm._$sGroupTag Toolbutton$this } # then check the activated button [my _$sTag.wWidget] config -relief sunken _bindTag shrink [my _$sTag.wWidget] Toolbutton$this } # name: -side # args: sSide: side to place Toolbar on # option member. repositions the Toolbar. metawidget proc Toolbar -side { sSide } { my -side [mkw.complete $sSide {top bottom left right}] _redraw } # name: -state # args: sState: new state for the Toolbar # option member. lets a Toolbar disappear or disables dragging. metawidget proc Toolbar -state { sState } { my -state [mkw.complete $sState {normal fixed withdrawn}] switch [my -state] { normal { _bindTag extend $this.frm Toolbar$this -side [my -side] } fixed { _bindTag shrink $this.frm Toolbar$this -side [my -side] } withdrawn { pack forget $this } } } # name: add # args: sType: type of new object # sTag: unique item tag # args: option-value pairs # adds a new button, checkbutton, radiobutton or separator. metawidget proc Toolbar add { sType sTag args } { if { [myinfo exists _$sTag.wWidget] } { error "Toolbar $this: Item $sTag already exists." } set sType [mkw.complete $sType {button checkbutton radiobutton separator}] set wWidget $this.frm._$sTag # store item attributes in private variables my _$sTag.sTag $sTag my _$sTag.sType $sType my _$sTag.wWidget $wWidget my _$sTag.iState 0 if { $sType == "separator" } { frame $wWidget -border 1 -relief sunken -width 2 -height 2 _bindTag extend $wWidget Toolbar$this } else { button $wWidget -border 1 -relief flat -padx 0 -pady 0 -takefocus 0 _bindTag extend $wWidget Toolbutton$this if { $sType == "checkbutton" } { bind $wWidget <1> "$this _checkButton $sTag" } elseif { $sType == "radiobutton" } { bind $wWidget <1> "$this _radioButton $sTag" # extract -group option and append it to a list set args [mkw.options $args * {-group *}] if { ! [info exists -group] } { set -group default } my _$sTag.sGroup ${-group} if { [myinfo exists __${-group}] } { my __${-group} [linsert [my __${-group}] end $sTag] } else { my __${-group} $sTag } } } eval itemconf {$sTag} $args if { [my -state] != "withdrawn" } { _redraw } return $wWidget } # name: delete # args: args: list of item tags # deletes Toolbar objects. metawidget proc Toolbar delete { args } { foreach sTag $args { if { [my _$sTag.sType] == "radiobutton" } { my __[my _$sTag.sGroup] [mkw.lshrink [my __[my _$sTag.sGroup]] $sTag] } destroy [my _$sTag.wWidget] unmy _$sTag.sTag _$sTag.sType _$sTag.wWidget _$sTag.iState _$sTag.sGroup } _redraw } # name: itemconf # args: sTag: item tag # args: option-value pairs # configures a Toolbar object. metawidget proc Toolbar itemconf { sTag args } { _checkItem $sTag eval {$this.frm._$sTag} config $args } # name: itemcget # args: sTag: item tag # sOption: option to query # returns an option value of a Toolbar object. metawidget proc Toolbar itemcget { sTag sOption } { _checkItem $sTag $this.frm._$sTag cget $sOption } # name: set # args: sTag: item tag # iState: new state for checkbuttons # similar to invoke, but the button's command is not invoked, # just the toolbutton is set. metawidget proc Toolbar set_ { sTag {iState {}} } { _checkItem $sTag if { [my _$sTag.sType] == "checkbutton" } { _checkButton $sTag $iState } elseif { [my _$sTag.sType] == "radiobutton" } { _radioButton $sTag } } # name: get # args: sTag: item tag # returns the state of a button. always 0 for regular buttons. # 0 or 1 for check- and radiobuttons. metawidget proc Toolbar get { sTag } { _checkItem $sTag return [my _$sTag.iState] } # name: invoke # args: sTag: item tag # iState: new state for checkbuttons # invokes a button. if iState is specified, it sets a checkbutton to the # corresponding value. does not apply for buttons and radiobuttons, since # there state is always known. metawidget proc Toolbar invoke { sTag {iState {}} } { _checkItem $sTag set_ $sTag $iState $this.frm._$sTag invoke } # name: menu # args: args: arguments for the regular menu command. # simply passes all arguments to the popup menu. metawidget proc Toolbar menu_ { args } { eval $this.menu $args } # name: names # args: - # returns the tags of all toolbar buttons metawidget proc Toolbar names { {sPattern *} } { set lTags {} foreach sVar [myinfo vars _*.sTag] { if { ! [string match $sPattern [my $sVar]] } continue lappend lTags [my $sVar] } return $lTags } metawidget command Toolbar _click _click metawidget command Toolbar _drag _drag metawidget command Toolbar _checkButton _checkButton metawidget command Toolbar _radioButton _radioButton metawidget command Toolbar add add metawidget command Toolbar delete delete metawidget command Toolbar itemconf itemconf metawidget command Toolbar itemcget itemcget metawidget command Toolbar set set_ metawidget command Toolbar get get metawidget command Toolbar invoke invoke metawidget command Toolbar menu menu_ metawidget command Toolbar names names metawidget option Toolbar -side -side metawidget option Toolbar -state -state proc test {} { wm minsize . 320 250 image create photo p1 -file ./demos/images/Notebook.gif image create photo p2 -file ./demos/images/Compass.gif pack [frame .work -bg gray50] -fill both -expand 1 grid [frame .test] -in .work pack [text .test.text -width 40 -height 10 -wrap word] toolbar .test.tbar -state fixed -side bottom -relief sunken .test.text insert end "Above are regular buttons, checkbuttons and radiobuttons." .test.text insert end "The Toolbars can be dragged to the four window sides." .test.text insert end "The right mouse button will invoke a menu.\n" .test.tbar add button b1 -image p1 -command {.test.text delete 1.0 end } .test.tbar add check b2 -image p2 -command { if { [.test.tbar get b2] } { .test.text config -bg black -fg white } else { .test.text config -bg white -fg black } } toolbar .tbar1 .tbar1 add button button1 -image p2 -command {.test.text insert end "button1 clicked\n"; .test.text see end } .tbar1 add button button2 -image p2 -command {.test.text insert end "button2 clicked\n"; .test.text see end } .tbar1 add separator s1 .tbar1 add checkbutton check1 -image p1 -command {.test.text insert end "check1 clicked\n"; .test.text see end } .tbar1 add checkbutton check2 -image p1 -command {.test.text insert end "check2 clicked\n"; .test.text see end } .tbar1 add separator s2 .tbar1 add radiobutton radio1 -image p1 -command {.test.text insert end "radio1 clicked\n"; .test.text see end } .tbar1 add radiobutton radio2 -image p2 -command {.test.text insert end "radio2 clicked\n"; .test.text see end } .tbar1 add radiobutton radio3 -image p1 -command {.test.text insert end "radio3 clicked\n"; .test.text see end } .tbar1 add radiobutton radio4 -image p2 -command {.test.text insert end "radio4 clicked\n"; .test.text see end } toolbar .t2 -side bottom .t2 add button b1 -image p1 -bg red .t2 add button b2 -image p2 -bg green .t2 add button b3 -image p1 -bg yellow .t2 add button b4 -image p2 -bg blue .t2 add button b5 -image p1 -bg white .t2 add button b6 -image p2 -bg black } #test