# ---------------------------------------------------------------------------- # buttonbox.tcl # This file is part of Unifix BWidget Toolkit # $Id: buttonbox.tcl,v 1.15 2009/11/01 20:20:50 oberdorfer Exp $ # ---------------------------------------------------------------------------- # Index of commands: # - ButtonBox::create # - ButtonBox::configure # - ButtonBox::cget # - ButtonBox::add # - ButtonBox::itemconfigure # - ButtonBox::itemcget # - ButtonBox::setfocus # - ButtonBox::invoke # - ButtonBox::index # - ButtonBox::_destroy # - ButtonBox::_themechanged # ---------------------------------------------------------------------------- namespace eval ButtonBox { Widget::define ButtonBox buttonbox Button Widget::declare ButtonBox { {-background Color "SystemWindowFrame" 0} {-orient Enum horizontal 1 {horizontal vertical}} {-state Enum "normal" 0 {normal disabled}} {-homogeneous Boolean 1 1} {-spacing Int 10 0 "%d >= 0"} {-padx TkResource "" 0 button} {-pady TkResource "" 0 button} {-default Int -1 0 "%d >= -1"} {-bg Synonym -background} {-style String "" 0} } if { ![BWidget::using ttk] } { Widget::addmap ButtonBox "" :cmd {-background {}} } if { [BWidget::using ttk] } { Widget::addmap Button "" :cmd {-style {}} } Widget::addmap ButtonBox "" :cmd {-background {}} bind ButtonBox [list ButtonBox::_destroy %W] if {[lsearch [bindtags .] ButtonBoxThemeChanged] < 0} { bindtags . [linsert [bindtags .] 1 ButtonBoxThemeChanged] } } # ---------------------------------------------------------------------------- # Command ButtonBox::create # ---------------------------------------------------------------------------- proc ButtonBox::create { path args } { Widget::init ButtonBox $path $args variable $path upvar 0 $path data eval [list frame $path] [Widget::subcget $path :cmd] \ [list -class ButtonBox -takefocus 0 -highlightthickness 0] # For 8.4+ we don't want to inherit the padding catch {$path configure -padx 0 -pady 0} set data(max) 0 set data(nbuttons) 0 set data(buttons) [list] set data(default) [Widget::getoption $path -default] bind ButtonBoxThemeChanged <> \ "+ [namespace current]::_themechanged $path" return [Widget::create ButtonBox $path] } # ---------------------------------------------------------------------------- # Command ButtonBox::configure # ---------------------------------------------------------------------------- proc ButtonBox::configure { path args } { variable $path upvar 0 $path data set res [Widget::configure $path $args] if { [Widget::hasChanged $path -default val] } { if { $data(default) != -1 && $val != -1 } { set but $path.b$data(default) if { [winfo exists $but] } { $but configure -default normal } set but $path.b$val if { [winfo exists $but] } { $but configure -default active } set data(default) $val } else { Widget::setoption $path -default $data(default) } } if {[Widget::hasChanged $path -state val]} { foreach i $data(buttons) { $path.b$i configure -state $val } } return $res } # ---------------------------------------------------------------------------- # Command ButtonBox::cget # ---------------------------------------------------------------------------- proc ButtonBox::cget { path option } { return [Widget::cget $path $option] } # ---------------------------------------------------------------------------- # Command ButtonBox::add # ---------------------------------------------------------------------------- proc ButtonBox::add { path args } { return [eval [linsert $args 0 insert $path end]] } proc ButtonBox::insert { path idx args } { variable $path upvar 0 $path data set but $path.b$data(nbuttons) set spacing [Widget::getoption $path -spacing] ## Save the current spacing setting for this button. Buttons ## appended to the end of the box have their spacing applied ## to their left while all other have their spacing applied ## to their right. if {$idx == "end"} { set data(spacing,$data(nbuttons)) [list left $spacing] lappend data(buttons) $data(nbuttons) } else { set data(spacing,$data(nbuttons)) [list right $spacing] set data(buttons) [linsert $data(buttons) $idx $data(nbuttons)] } if { $data(nbuttons) == $data(default) } { set style active } elseif { $data(default) == -1 } { set style disabled } else { set style normal } array set flags $args set tags "" if { [info exists flags(-tags)] } { set tags $flags(-tags) unset flags(-tags) set args [array get flags] } eval [list Button::create $but \ -background [Widget::getoption $path -background]\ -padx [Widget::getoption $path -padx] \ -pady [Widget::getoption $path -pady]] \ $args [list -default $style] # a button box button - by default - is flat! if { [BWidget::using ttk] } { $but configure -style [Button::getSlimButtonStyle] } # ericm@scriptics.com: set up tags, just like the menu items foreach tag $tags { lappend data(tags,$tag) $but if { ![info exists data(tagstate,$tag)] } { set data(tagstate,$tag) 0 } } set data(buttontags,$but) $tags # ericm@scriptics.com _redraw $path incr data(nbuttons) return $but } proc ButtonBox::delete { path idx } { variable $path upvar 0 $path data set i [lindex $data(buttons) $idx] set data(buttons) [lreplace $data(buttons) $idx $idx] destroy $path.b$i } # ButtonBox::setbuttonstate -- # # Set the state of a given button tag. If this makes any buttons # enable-able (ie, all of their tags are TRUE), enable them. # # Arguments: # path the button box widget name # tag the tag to modify # state the new state of $tag (0 or 1) # # Results: # None. proc ButtonBox::setbuttonstate {path tag state} { variable $path upvar 0 $path data # First see if this is a real tag if { [info exists data(tagstate,$tag)] } { set data(tagstate,$tag) $state foreach but $data(tags,$tag) { set expression "1" foreach buttontag $data(buttontags,$but) { append expression " && $data(tagstate,$buttontag)" } if { [expr $expression] } { set state normal } else { set state disabled } $but configure -state $state } } return } # ButtonBox::getbuttonstate -- # # Retrieve the state of a given button tag. # # Arguments: # path the button box widget name # tag the tag to modify # # Results: # None. proc ButtonBox::getbuttonstate {path tag} { variable $path upvar 0 $path data # First see if this is a real tag if { [info exists data(tagstate,$tag)] } { return $data(tagstate,$tag) } else { error "unknown tag $tag" } } # ---------------------------------------------------------------------------- # Command ButtonBox::itemconfigure # ---------------------------------------------------------------------------- proc ButtonBox::itemconfigure { path index args } { if { [set idx [lsearch $args -default]] != -1 } { set args [lreplace $args $idx [expr {$idx+1}]] } return [eval [list Button::configure $path.b[index $path $index]] $args] } # ---------------------------------------------------------------------------- # Command ButtonBox::itemcget # ---------------------------------------------------------------------------- proc ButtonBox::itemcget { path index option } { return [Button::cget $path.b[index $path $index] $option] } # ---------------------------------------------------------------------------- # Command ButtonBox::setfocus # ---------------------------------------------------------------------------- proc ButtonBox::setfocus { path index } { set but $path.b[index $path $index] if { [winfo exists $but] } { focus $but } } # ---------------------------------------------------------------------------- # Command ButtonBox::invoke # ---------------------------------------------------------------------------- proc ButtonBox::invoke { path index } { set but $path.b[index $path $index] if { [winfo exists $but] } { Button::invoke $but } } # ---------------------------------------------------------------------------- # Command ButtonBox::index # ---------------------------------------------------------------------------- proc ButtonBox::index { path index } { variable $path upvar 0 $path data set n [expr {$data(nbuttons) - 1}] if {[string equal $index "default"]} { set res [Widget::getoption $path -default] } elseif {$index == "end" || $index == "last"} { set res $n } elseif {![string is integer -strict $index]} { ## It's not an integer. Search the text of each button ## in the box and return the index that matches. foreach i $data(buttons) { set w $path.b$i lappend text [$w cget -text] lappend names [$w cget -name] } set res [lsearch -exact [concat $names $text] $index] } else { set res $index if {$index > $n} { set res $n } } return $res } # ButtonBox::gettags -- # # Return a list of all the tags on all the buttons in a buttonbox. # # Arguments: # path the buttonbox to query. # # Results: # taglist a list of tags on the buttons in the buttonbox proc ButtonBox::gettags {path} { upvar ::ButtonBox::$path data set taglist {} foreach tag [array names data "tags,*"] { lappend taglist [string range $tag 5 end] } return $taglist } # ---------------------------------------------------------------------------- # Command ButtonBox::_redraw # ---------------------------------------------------------------------------- proc ButtonBox::_redraw { path } { variable $path upvar 0 $path data Widget::getVariable $path buttons # For tk >= 8.4, -uniform gridding option is used. # Otherwise, there is the constraint, that button size may not change after # creation. set uniformAvailable [expr {0 <= [package vcompare [info patchlevel] 8.4.0]}] ## We re-grid the buttons from left-to-right. As we go through ## each button, we check its spacing and which direction the ## spacing applies to. Once spacing has been applied to an index, ## it is not changed. This means spacing takes precedence from ## left-to-right. set idx 0 set idxs [list] foreach i $data(buttons) { set dir [lindex $data(spacing,$i) 0] set spacing [lindex $data(spacing,$i) 1] set but $path.b$i if {[string equal [Widget::getoption $path -orient] "horizontal"]} { grid $but -column $idx -row 0 -sticky nsew if { [Widget::getoption $path -homogeneous] } { if {$uniformAvailable} { grid columnconfigure $path $idx -uniform koen -weight 1 } else { set req [winfo reqwidth $but] if { $req > $data(max) } { grid columnconfigure $path [expr {2*$i}] -minsize $req set data(max) $req } grid columnconfigure $path $idx -weight 1 } } else { grid columnconfigure $path $idx -weight 0 } set col [expr {$idx - 1}] if {[string equal $dir "right"]} { set col [expr {$idx + 1}] } if {$col > 0 && [lsearch $idxs $col] < 0} { lappend idxs $col grid columnconfigure $path $col -minsize $spacing } } else { grid $but -column 0 -row $idx -sticky nsew grid rowconfigure $path $idx -weight 0 set row [expr {$idx - 1}] if {[string equal $dir "right"]} { set row [expr {$idx + 1}] } if {$row > 0 && [lsearch $idxs $row] < 0} { lappend idxs $row grid rowconfigure $path $row -minsize $spacing } } incr idx 2 } if {!$uniformAvailable} { # Now that the maximum size has been calculated, go back through # and correctly set the size for homogeneous horizontal buttons. if { [string equal [Widget::getoption $path -orient] "horizontal"] && [Widget::getoption $path -homogeneous] } { set idx 0 foreach i $data(buttons) { grid columnconfigure $path $idx -minsize $data(max) incr idx 2 } } } } # ---------------------------------------------------------------------------- # Command ButtonBox::_destroy # ---------------------------------------------------------------------------- proc ButtonBox::_destroy { path } { variable $path upvar 0 $path data Widget::destroy $path unset data } # ---------------------------------------------------------------------------- # Command ButtonBox::_themechanged # ---------------------------------------------------------------------------- proc ButtonBox::_themechanged { path } { if { ![winfo exists $path] } { return } BWidget::set_themedefaults $path configure -background $BWidget::colors(SystemWindowFrame) }