#============================================================================== # Contains private utility procedures for tablelist widgets. # # Structure of the module: # - Namespace initialization # - Private utility procedures # # Copyright (c) 2000-2006 Csaba Nemethi (E-mail: csaba.nemethi@t-online.de) #============================================================================== # # Namespace initialization # ======================== # namespace eval tablelist { # # Alignment -> anchor mapping # variable anchors array set anchors { left w right e center center } # # -> direction mapping # variable directions array set directions { up,increasing Up up,decreasing Dn down,increasing Dn down,decreasing Up } } # # Private utility procedures # ========================== # #------------------------------------------------------------------------------ # tablelist::rowIndex # # Checks the row index idx and returns either its numerical value or an error. # endIsSize must be a boolean value: if true, end refers to the number of items # in the tablelist, i.e., to the element just after the last one; if false, end # refers to 1 less than the number of items, i.e., to the last element in the # tablelist. #------------------------------------------------------------------------------ proc tablelist::rowIndex {win idx endIsSize} { upvar ::tablelist::ns${win}::data data set idxLen [string length $idx] if {[string first $idx "active"] == 0 && $idxLen >= 2} { return $data(activeRow) } elseif {[string first $idx "anchor"] == 0 && $idxLen >= 2} { return $data(anchorRow) } elseif {[string first $idx "end"] == 0} { if {$endIsSize} { return $data(itemCount) } else { return $data(lastRow) } } elseif {[string compare [string index $idx 0] "@"] == 0 && [catch {$data(body) index $idx}] == 0} { scan $idx "@%d,%d" x y incr x -[winfo x $data(body)] incr y -[winfo y $data(body)] set textIdx [$data(body) index @$x,$y] return [expr {int($textIdx) - 1}] } elseif {[string compare [string index $idx 0] "k"] == 0 && [set index [lsearch $data(itemList) "* $idx"]] >= 0} { return $index } elseif {[catch {format "%d" $idx} index] == 0} { return $index } else { for {set row 0} {$row < $data(itemCount)} {incr row} { set key [lindex [lindex $data(itemList) $row] end] set hasName [info exists data($key-name)] if {$hasName && [string compare $idx $data($key-name)] == 0 || !$hasName && [string compare $idx ""] == 0} { return $row } } return -code error \ "bad row index \"$idx\": must be active, anchor,\ end, @x,y, a number, a full key, or a name" } } #------------------------------------------------------------------------------ # tablelist::colIndex # # Checks the column index idx and returns either its numerical value or an # error. checkRange must be a boolean value: if true, it is additionally # checked whether the numerical value corresponding to idx is within the # allowed range. #------------------------------------------------------------------------------ proc tablelist::colIndex {win idx checkRange} { upvar ::tablelist::ns${win}::data data set idxLen [string length $idx] if {[string first $idx "active"] == 0 && $idxLen >= 2} { set index $data(activeCol) } elseif {[string first $idx "anchor"] == 0 && $idxLen >= 2} { set index $data(anchorCol) } elseif {[string first $idx "end"] == 0} { set index $data(lastCol) } elseif {[string compare [string index $idx 0] "@"] == 0 && [catch {$data(body) index $idx}] == 0} { scan $idx "@%d" x incr x -[winfo x $data(body)] set bodyWidth [winfo width $data(body)] if {$x >= $bodyWidth} { set x [expr {$bodyWidth - 1}] } elseif {$x < 0} { set x 0 } set x [expr {$x + [winfo rootx $data(body)]}] set lastVisibleCol -1 for {set col 0} {$col < $data(colCount)} {incr col} { if {$data($col-hide) || $data($col-elide)} { continue } set lastVisibleCol $col set w $data(hdrTxtFrLbl)$col set wX [winfo rootx $w] if {$x >= $wX && $x < $wX + [winfo width $w]} { return $col } } set index $lastVisibleCol } elseif {[catch {format "%d" $idx} index] != 0} { for {set col 0} {$col < $data(colCount)} {incr col} { set hasName [info exists data($col-name)] if {$hasName && [string compare $idx $data($col-name)] == 0 || !$hasName && [string compare $idx ""] == 0} { set index $col break } } if {$col == $data(colCount)} { return -code error \ "bad column index \"$idx\": must be active, anchor,\ end, @x,y, a number, or a name" } } if {$checkRange && ($index < 0 || $index > $data(lastCol))} { return -code error "column index \"$idx\" out of range" } else { return $index } } #------------------------------------------------------------------------------ # tablelist::cellIndex # # Checks the cell index idx and returns either its value in the form row,col or # an error. checkRange must be a boolean value: if true, it is additionally # checked whether the two numerical values corresponding to idx are within the # respective allowed ranges. #------------------------------------------------------------------------------ proc tablelist::cellIndex {win idx checkRange} { upvar ::tablelist::ns${win}::data data set idxLen [string length $idx] if {[string first $idx "active"] == 0 && $idxLen >= 2} { set row $data(activeRow) set col $data(activeCol) } elseif {[string first $idx "anchor"] == 0 && $idxLen >= 2} { set row $data(anchorRow) set col $data(anchorCol) } elseif {[string first $idx "end"] == 0} { set row [rowIndex $win $idx 0] set col [colIndex $win $idx 0] } elseif {[string compare [string index $idx 0] "@"] == 0} { if {[catch {rowIndex $win $idx 0} row] != 0 || [catch {colIndex $win $idx 0} col] != 0} { return -code error \ "bad cell index \"$idx\": must be active, anchor,\ end, @x,y, or row,col, where row must be active,\ anchor, end, a number, a full key, or a name, and\ col must be active, anchor, end, a number, or a name" } } else { set lst [split $idx ","] if {[llength $lst] != 2 || [catch {rowIndex $win [lindex $lst 0] 0} row] != 0 || [catch {colIndex $win [lindex $lst 1] 0} col] != 0} { return -code error \ "bad cell index \"$idx\": must be active, anchor,\ end, @x,y, or row,col, where row must be active,\ anchor, end, a number, a full key, or a name, and\ col must be active, anchor, end, a number, or a name" } } if {$checkRange && ($row < 0 || $row > $data(lastRow) || $col < 0 || $col > $data(lastCol))} { return -code error "cell index \"$idx\" out of range" } else { return $row,$col } } #------------------------------------------------------------------------------ # tablelist::adjustRowIndex # # Sets the row index specified by $rowName to the index of the nearest # (non-hidden) row. #------------------------------------------------------------------------------ proc tablelist::adjustRowIndex {win rowName {forceVisible 0}} { upvar ::tablelist::ns${win}::data data upvar $rowName row if {$row > $data(lastRow)} { set row $data(lastRow) } if {$row < 0} { set row 0 } if {$forceVisible} { set origRow $row for {} {$row < $data(itemCount)} {incr row} { set key [lindex [lindex $data(itemList) $row] end] if {![info exists data($key-hide)]} { return "" } } for {set row [expr {$origRow - 1}]} {$row >= 0} {incr row -1} { set key [lindex [lindex $data(itemList) $row] end] if {![info exists data($key-hide)]} { return "" } } set row 0 } } #------------------------------------------------------------------------------ # tablelist::adjustColIndex # # Sets the column index specified by $colName to the index of the nearest # (non-hidden) column. #------------------------------------------------------------------------------ proc tablelist::adjustColIndex {win colName {forceVisible 0}} { upvar ::tablelist::ns${win}::data data upvar $colName col if {$col > $data(lastCol)} { set col $data(lastCol) } if {$col < 0} { set col 0 } if {$forceVisible} { set origCol $col for {} {$col < $data(colCount)} {incr col} { if {!$data($col-hide)} { return "" } } for {set col [expr {$origCol - 1}]} {$col >= 0} {incr col -1} { if {!$data($col-hide)} { return "" } } set col 0 } } #------------------------------------------------------------------------------ # tablelist::findTabs # # Searches for the first and last occurrences of the tab character in the cell # range specified by firstCol and lastCol in the given line of the body text # child of the tablelist widget win. Assigns the index of the first tab to # $idx1Name and the index of the last tab to $idx2Name. It is assumed that # both columns are non-hidden (but there may be hidden ones between them). #------------------------------------------------------------------------------ proc tablelist::findTabs {win line firstCol lastCol idx1Name idx2Name} { variable canElide variable elide upvar ::tablelist::ns${win}::data data upvar $idx1Name idx1 $idx2Name idx2 set w $data(body) set endIdx $line.end set idx1 $line.0 for {set col 0} {$col < $firstCol} {incr col} { if {!$data($col-hide) || $canElide} { set idx1 [$w search $elide "\t" $idx1+1c $endIdx]+1c } } set idx1 [$w index $idx1] set idx2 $idx1 for {} {$col < $lastCol} {incr col} { if {!$data($col-hide) || $canElide} { set idx2 [$w search $elide "\t" $idx2+1c $endIdx]+1c } } set idx2 [$w search $elide "\t" $idx2+1c $endIdx] return "" } #------------------------------------------------------------------------------ # tablelist::sortStretchableColList # # Replaces the column indices different from end in the list of the stretchable # columns of the tablelist widget win with their numerical equivalents and # sorts the resulting list. #------------------------------------------------------------------------------ proc tablelist::sortStretchableColList win { upvar ::tablelist::ns${win}::data data if {[llength $data(-stretch)] == 0 || [string first $data(-stretch) "all"] == 0} { return "" } set containsEnd 0 foreach elem $data(-stretch) { if {[string first $elem "end"] == 0} { set containsEnd 1 } else { set tmp([colIndex $win $elem 0]) "" } } set data(-stretch) [lsort -integer [array names tmp]] if {$containsEnd} { lappend data(-stretch) end } } #------------------------------------------------------------------------------ # tablelist::deleteColData # # Cleans up the data associated with the col'th column of the tablelist widget # win. #------------------------------------------------------------------------------ proc tablelist::deleteColData {win col} { upvar ::tablelist::ns${win}::data data if {$data(editCol) == $col} { set data(editCol) -1 set data(editRow) -1 } # # Remove the elements with names of the form $col-* # if {[info exists data($col-redispId)]} { after cancel $data($col-redispId) } set w $data(body) foreach name [array names data $col-*] { unset data($name) } # # Remove the elements with names of the form k*-$col-* # foreach name [array names data k*-$col-*] { unset data($name) if {[string match "k*-$col-\[bf\]*" $name]} { incr data(tagRefCount) -1 } elseif {[string match "k*-$col-image" $name]} { incr data(imgCount) -1 } elseif {[string match "k*-$col-window" $name]} { incr data(winCount) -1 } } # # Remove col from the list of stretchable columns if explicitly specified # if {[string first $data(-stretch) "all"] != 0} { set stretchableCols {} foreach elem $data(-stretch) { if {[string first $elem "end"] == 0 || $elem != $col} { lappend stretchableCols $elem } } set data(-stretch) $stretchableCols } } #------------------------------------------------------------------------------ # tablelist::moveColData # # Moves the elements of oldArrName corresponding to oldCol to those of # newArrName corresponding to newCol. #------------------------------------------------------------------------------ proc tablelist::moveColData {win oldArrName newArrName imgArrName oldCol newCol} { upvar $oldArrName oldArr $newArrName newArr $imgArrName imgArr foreach specialCol {activeCol anchorCol editCol} { if {$oldArr($specialCol) == $oldCol} { set newArr($specialCol) $newCol } } if {$newCol < $newArr(colCount)} { foreach l [getSublabels $newArr(hdrTxtFrLbl)$newCol] { destroy $l } set newArr(fmtCmdFlagList) \ [lreplace $newArr(fmtCmdFlagList) $newCol $newCol 0] } # # Move the elements of oldArr with names of the form $oldCol-* # to those of newArr with names of the form $newCol-* # set w $newArr(body) foreach newName [array names newArr $newCol-*] { unset newArr($newName) } foreach oldName [array names oldArr $oldCol-*] { regsub "$oldCol-" $oldName "$newCol-" newName set newArr($newName) $oldArr($oldName) unset oldArr($oldName) set tail [lindex [split $newName "-"] 1] switch $tail { formatcommand { if {$newCol < $newArr(colCount)} { set newArr(fmtCmdFlagList) \ [lreplace $newArr(fmtCmdFlagList) $newCol $newCol 1] } } labelimage { set imgArr($newCol-$tail) $newArr($newName) unset newArr($newName) } } } # # Move the elements of oldArr with names of the form k*-$oldCol-* # to those of newArr with names of the form k*-$newCol-* # foreach newName [array names newArr k*-$newCol-*] { unset newArr($newName) } foreach oldName [array names oldArr k*-$oldCol-*] { regsub -- "-$oldCol-" $oldName "-$newCol-" newName set newArr($newName) $oldArr($oldName) unset oldArr($oldName) } # # Replace oldCol with newCol in the list of # stretchable columns if explicitly specified # if {[info exists oldArr(-stretch)] && [string first $oldArr(-stretch) "all"] != 0} { set stretchableCols {} foreach elem $oldArr(-stretch) { if {[string first $elem "end"] != 0 && $elem == $oldCol} { lappend stretchableCols $newCol } else { lappend stretchableCols $elem } } set newArr(-stretch) $stretchableCols } } #------------------------------------------------------------------------------ # tablelist::deleteColFromCellList # # Returns the list obtained from a given list of cell indices by removing the # elements whose column component equals a given column number. #------------------------------------------------------------------------------ proc tablelist::deleteColFromCellList {cellList col} { set newCellList {} foreach cellIdx $cellList { scan $cellIdx "%d,%d" cellRow cellCol if {$cellCol != $col} { lappend newCellList $cellIdx } } return $newCellList } #------------------------------------------------------------------------------ # tablelist::extractColFromCellList # # Returns the list of row indices obtained from those elements of a given list # of cell indices whose column component equals a given column number. #------------------------------------------------------------------------------ proc tablelist::extractColFromCellList {cellList col} { set rowList {} foreach cellIdx $cellList { scan $cellIdx "%d,%d" cellRow cellCol if {$cellCol == $col} { lappend rowList $cellRow } } return $rowList } #------------------------------------------------------------------------------ # tablelist::replaceColInCellList # # Returns the list obtained from a given list of cell indices by replacing the # occurrences of oldCol in the column components with newCol. #------------------------------------------------------------------------------ proc tablelist::replaceColInCellList {cellList oldCol newCol} { set cellList [deleteColFromCellList $cellList $newCol] set newCellList {} foreach cellIdx $cellList { scan $cellIdx "%d,%d" cellRow cellCol if {$cellCol == $oldCol} { lappend newCellList $cellRow,$newCol } else { lappend newCellList $cellIdx } } return $newCellList } #------------------------------------------------------------------------------ # tablelist::condUpdateListVar # # Updates the list variable of the tablelist widget win if present. #------------------------------------------------------------------------------ proc tablelist::condUpdateListVar win { upvar ::tablelist::ns${win}::data data if {$data(hasListVar)} { upvar #0 $data(-listvariable) var trace vdelete var wu $data(listVarTraceCmd) set var {} foreach item $data(itemList) { lappend var [lrange $item 0 $data(lastCol)] } trace variable var wu $data(listVarTraceCmd) } } #------------------------------------------------------------------------------ # tablelist::reconfigColLabels # # Reconfigures the labels of the col'th column of the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::reconfigColLabels {win imgArrName col} { variable usingTile upvar ::tablelist::ns${win}::data data upvar $imgArrName imgArr set optList {-labelalign -labelbackground -labelborderwidth -labelfont -labelforeground -labelpady -labelrelief} if {!$usingTile} { lappend optList -labelheight } foreach opt $optList { if {[info exists data($col$opt)]} { doColConfig $col $win $opt $data($col$opt) } else { doColConfig $col $win $opt "" } } if {[info exists imgArr($col-labelimage)]} { doColConfig $col $win -labelimage $imgArr($col-labelimage) } } #------------------------------------------------------------------------------ # tablelist::charsToPixels # # Returns the width in pixels of the string consisting of a given number of "0" # characters. #------------------------------------------------------------------------------ proc tablelist::charsToPixels {win font charCount} { ### set str [string repeat "0" $charCount] set str "" for {set n 0} {$n < $charCount} {incr n} { append str 0 } return [font measure $font -displayof $win $str] } #------------------------------------------------------------------------------ # tablelist::strRange # # Returns the largest initial (for alignment = left or center) or final (for # alignment = right) range of characters from str whose width, when displayed # in the given font, is no greater than pixels. #------------------------------------------------------------------------------ proc tablelist::strRange {win str font pixels alignment} { if {[font measure $font -displayof $win $str] <= $pixels} { return $str } set halfLen [expr {[string length $str] / 2}] if {$halfLen == 0} { return "" } if {[string compare $alignment "right"] == 0} { set rightStr [string range $str $halfLen end] set width [font measure $font -displayof $win $rightStr] if {$width == $pixels} { return $rightStr } elseif {$width > $pixels} { return [strRange $win $rightStr $font $pixels $alignment] } else { set str [string range $str 0 [expr {$halfLen - 1}]] return [strRange $win $str $font \ [expr {$pixels - $width}] $alignment]$rightStr } } else { set leftStr [string range $str 0 [expr {$halfLen - 1}]] set width [font measure $font -displayof $win $leftStr] if {$width == $pixels} { return $leftStr } elseif {$width > $pixels} { return [strRange $win $leftStr $font $pixels $alignment] } else { set str [string range $str $halfLen end] return $leftStr[strRange $win $str $font \ [expr {$pixels - $width}] $alignment] } } } #------------------------------------------------------------------------------ # tablelist::strRangeExt # # Invokes strRange with the given arguments and returns a string obtained by # appending (for alignment = left or center) or prepending (for alignment = # right) (part of) the snip string to (part of) its result. #------------------------------------------------------------------------------ proc tablelist::strRangeExt {win str font pixels alignment snipStr} { set subStr [strRange $win $str $font $pixels $alignment] set len [string length $subStr] if {$pixels < 0 || $len == [string length $str] || [string compare $snipStr ""] == 0} { return $subStr } if {[string compare $alignment "right"] == 0} { set extSubStr $snipStr$subStr while {[font measure $font -displayof $win $extSubStr] > $pixels} { if {$len > 0} { set subStr [string range $subStr 1 end] incr len -1 set extSubStr $snipStr$subStr } else { set extSubStr [string range $extSubStr 1 end] } } } else { set last [expr {$len - 1}] set extSubStr $subStr$snipStr while {[font measure $font -displayof $win $extSubStr] > $pixels} { if {$last >= 0} { incr last -1 set subStr [string range $subStr 0 $last] set extSubStr $subStr$snipStr } else { set extSubStr [string range $extSubStr 1 end] } } } return $extSubStr } #------------------------------------------------------------------------------ # tablelist::adjustItem # # Returns the list obtained by adjusting the list specified by item to the # length expLen. #------------------------------------------------------------------------------ proc tablelist::adjustItem {item expLen} { set len [llength $item] if {$len < $expLen} { for {set n $len} {$n < $expLen} {incr n} { lappend item "" } return $item } else { return [lrange $item 0 [expr {$expLen - 1}]] } } #------------------------------------------------------------------------------ # tablelist::formatItem # # Returns the list obtained by formatting the elements of the item argument. #------------------------------------------------------------------------------ proc tablelist::formatItem {win item} { upvar ::tablelist::ns${win}::data data set formattedItem {} set col 0 foreach text $item fmtCmdFlag $data(fmtCmdFlagList) { if {$fmtCmdFlag} { set text [uplevel #0 $data($col-formatcommand) [list $text]] } lappend formattedItem $text incr col } return $formattedItem } #------------------------------------------------------------------------------ # tablelist::hasChars # # Checks whether at least one element of the given list is a nonempty string. #------------------------------------------------------------------------------ proc tablelist::hasChars list { foreach str $list { if {[string compare $str ""] != 0} { return 1 } } return 0 } #------------------------------------------------------------------------------ # tablelist::getListWidth # # Returns the max. number of pixels that the elements of the given list would # use in the specified font when displayed in the window win. #------------------------------------------------------------------------------ proc tablelist::getListWidth {win list font} { set width 0 foreach str $list { set strWidth [font measure $font -displayof $win $str] if {$strWidth > $width} { set width $strWidth } } return $width } #------------------------------------------------------------------------------ # tablelist::joinList # # Returns the string formed by joining together with "\n" the strings obtained # by applying strRangeExt to the elements of the given list, with the specified # specified arguments. #------------------------------------------------------------------------------ proc tablelist::joinList {win list font pixels alignment snipStr} { set list2 {} foreach str $list { lappend list2 [strRangeExt $win $str $font $pixels $alignment $snipStr] } return [join $list2 "\n"] } #------------------------------------------------------------------------------ # tablelist::displayText # # Displays the given text in a message widget to be embedded into the specified # cell of the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::displayText {win key col text font alignment} { variable anchors upvar ::tablelist::ns${win}::data data set w $data(body).m$key,$col if {![winfo exists $w]} { # # Create a message widget and replace the binding tag Message with # $data(bodyTag) and TablelistBody in the list of its binding tags # message $w -background $data(-background) -borderwidth 0 \ -foreground $data(-foreground) -highlightthickness 0 \ -padx 0 -pady 0 -relief flat -takefocus 0 -width 1000000 bindtags $w [lreplace [bindtags $w] 1 1 $data(bodyTag) TablelistBody] } $w configure -anchor $anchors($alignment) -font $font \ -justify $alignment -text $text updateColorsWhenIdle $win return $w } #------------------------------------------------------------------------------ # tablelist::getAuxData # # Gets the name, type, and width of the image or window associated with the # specified cell of the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::getAuxData {win key col auxTypeName auxWidthName} { upvar ::tablelist::ns${win}::data data upvar $auxTypeName auxType $auxWidthName auxWidth if {[info exists data($key-$col-window)]} { set aux $data(body).f$key,$col set auxWidth $data($key-$col-reqWidth) set auxType 2 } elseif {[info exists data($key-$col-image)]} { set aux $data(body).l$key,$col set auxWidth [image width $data($key-$col-image)] set auxType 1 } else { set aux "" set auxWidth 0 set auxType 0 } return $aux } #------------------------------------------------------------------------------ # tablelist::adjustElem # # Prepares the text specified by $textName and the auxiliary object width # specified by $auxWidthName for insertion into a cell of the tablelist widget # win. #------------------------------------------------------------------------------ proc tablelist::adjustElem {win textName auxWidthName font pixels alignment snipStr} { upvar $textName text $auxWidthName auxWidth if {$pixels == 0} { ;# convention: dynamic width if {$auxWidth != 0 && [string compare $text ""] != 0} { if {[string compare $alignment "right"] == 0} { set text "$text " } else { set text " $text" } } } elseif {$auxWidth == 0} { ;# no image or window set text [strRangeExt $win $text $font $pixels $alignment $snipStr] } elseif {[string compare $text ""] == 0} { ;# aux. object w/o text if {$auxWidth > $pixels} { set auxWidth $pixels } } else { ;# both aux. object and text set gap [font measure $font -displayof $win " "] if {$auxWidth + $gap <= $pixels} { incr pixels -[expr {$auxWidth + $gap}] set text [strRangeExt $win $text $font $pixels $alignment $snipStr] if {[string compare $alignment "right"] == 0} { set text "$text " } else { set text " $text" } } elseif {$auxWidth <= $pixels} { set text "" ;# can't display the text } else { set auxWidth $pixels set text "" ;# can't display the text } } } #------------------------------------------------------------------------------ # tablelist::adjustMlElem # # Prepares the list specified by $listName and the auxiliary object width # specified by $auxWidthName for insertion into a multiline cell of the # tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::adjustMlElem {win listName auxWidthName font pixels alignment snipStr} { upvar $listName list $auxWidthName auxWidth set list2 {} if {$pixels == 0} { ;# convention: dynamic width if {$auxWidth != 0 && [hasChars $list]} { foreach str $list { if {[string compare $alignment "right"] == 0} { lappend list2 "$str " } else { lappend list2 " $str" } } set list $list2 } } elseif {$auxWidth == 0} { ;# no image or window foreach str $list { lappend list2 [strRangeExt $win $str $font \ $pixels $alignment $snipStr] } set list $list2 } elseif {![hasChars $list]} { ;# aux. object w/o text if {$auxWidth > $pixels} { set auxWidth $pixels } } else { ;# both aux. object and text set gap [font measure $font -displayof $win " "] if {$auxWidth + $gap <= $pixels} { incr pixels -[expr {$auxWidth + $gap}] foreach str $list { set str [strRangeExt $win $str $font \ $pixels $alignment $snipStr] if {[string compare $alignment "right"] == 0} { lappend list2 "$str " } else { lappend list2 " $str" } } set list $list2 } elseif {$auxWidth <= $pixels} { foreach str $list { lappend list2 "" } set list $list2 ;# can't display the text } else { set auxWidth $pixels foreach str $list { lappend list2 "" } set list $list2 ;# can't display the text } } } #------------------------------------------------------------------------------ # tablelist::getCellTextWidth # # Returns the number of pixels that the given text would use when displayed in # a cell of a dynamic-width column of the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::getCellTextWidth {win text auxWidth cellFont} { if {[string match "*\n*" $text]} { set list [split $text "\n"] if {$auxWidth != 0 && [hasChars $list]} { foreach str $list { lappend list2 " $str" } set list $list2 } return [getListWidth $win $list $cellFont] } else { if {$auxWidth != 0 && [string compare $text ""] != 0} { set text " $text" } return [font measure $cellFont -displayof $win $text] } } #------------------------------------------------------------------------------ # tablelist::createAuxObject # # Creates the specified auxiliary object (image or window) for insertion into # the given cell of the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::createAuxObject {win key row col aux auxType auxWidth} { if {[winfo exists $aux]} { return "" } upvar ::tablelist::ns${win}::data data if {$auxType == 1} { ;# image # # Create the label containing the cell's image and # replace the binding tag Label with $data(bodyTag) # and TablelistBody in the list of its binding tags # tk::label $aux -borderwidth 0 -height 0 -highlightthickness 0 \ -image $data($key-$col-image) -padx 0 -pady 0 \ -relief flat -takefocus 0 -width $auxWidth bindtags $aux [lreplace [bindtags $aux] 1 1 \ $data(bodyTag) TablelistBody] } elseif {$auxType == 2} { ;# window # # Create the frame and evaluate the script # that creates a child widget within the frame # tk::frame $aux -borderwidth 0 -class TablelistWindow -container 0 \ -height $data($key-$col-reqHeight) \ -highlightthickness 0 -relief flat \ -takefocus 0 -width $auxWidth uplevel #0 $data($key-$col-window) [list $win $row $col $aux.w] } } #------------------------------------------------------------------------------ # tablelist::insertElem # # Inserts the given text and auxiliary object (image or window) into the text # widget w, just before the character position specified by index. The object # will follow the text if alignment is "right", and will precede it otherwise. #------------------------------------------------------------------------------ proc tablelist::insertElem {w index text aux auxType alignment} { set index [$w index $index] if {$auxType == 0} { ;# no image or window $w insert $index $text } elseif {[string compare $alignment "right"] == 0} { if {$auxType == 2} { ;# window place $aux.w -relx 1.0 -anchor ne } $w window create $index -pady 1 -window $aux $w insert $index $text } else { if {$auxType == 2} { ;# window place $aux.w -relx 0.0 -anchor nw } $w insert $index $text $w window create $index -pady 1 -window $aux } } #------------------------------------------------------------------------------ # tablelist::insertMlElem # # Inserts the given message widget and auxiliary object (image or window) into # the text widget w, just before the character position specified by index. # The object will follow the message widget if alignment is "right", and will # precede it otherwise. #------------------------------------------------------------------------------ proc tablelist::insertMlElem {w index msgScript aux auxType alignment} { set index [$w index $index] if {$auxType == 0} { ;# no image or window $w window create $index -pady 1 -create $msgScript } elseif {[string compare $alignment "right"] == 0} { if {$auxType == 2} { ;# window place $aux.w -relx 1.0 -anchor ne } $w window create $index -pady 1 -window $aux $w window create $index -pady 1 -create $msgScript } else { if {$auxType == 2} { ;# window place $aux.w -relx 0.0 -anchor nw } $w window create $index -pady 1 -create $msgScript $w window create $index -pady 1 -window $aux } } #------------------------------------------------------------------------------ # tablelist::updateCell # # Updates the contents of the text widget w starting at index1 and ending just # before index2 by keeping the auxiliary object (image or window) (if any) and # replacing only the text between the two character positions. #------------------------------------------------------------------------------ proc tablelist::updateCell {w index1 index2 text aux auxType auxWidth alignment} { if {$auxType == 0} { ;# no image or window $w delete $index1 $index2 $w insert $index1 $text } else { # # Check whether the label containing an image or the frame containing # a window is mapped at the first or last position of the cell # if {[string compare [lindex [$w dump -window $index1] 1] $aux] == 0} { set auxFound 1 $w delete $index1+1c $index2 } elseif {[string compare [lindex [$w dump -window $index2-1c] 1] $aux] == 0} { set auxFound 1 $w delete $index1 $index2-1c } else { set auxFound 0 $w delete $index1 $index2 } if {$auxFound} { # # Adjust the aux. window's width and contents # $aux configure -width $auxWidth if {[string compare $alignment "right"] == 0} { if {$auxType == 2} { ;# window place $aux.w -relx 1.0 -anchor ne } $w insert $index1 $text } else { if {$auxType == 2} { ;# window place $aux.w -relx 0.0 -anchor nw } $w insert $index1+1c $text } } else { # # Insert the text and the aux. window # insertElem $w $index1 $text $aux $auxType $alignment } } } #------------------------------------------------------------------------------ # tablelist::updateMlCell # # Updates the contents of the text widget w starting at index1 and ending just # before index2 by keeping the auxiliary object (image or window) (if any) and # replacing only the multiline text between the two character positions. #------------------------------------------------------------------------------ proc tablelist::updateMlCell {w index1 index2 msgScript aux auxType auxWidth alignment} { if {$auxType == 0} { ;# no image or window set path [lindex [$w dump -window $index1] 1] if {[string compare $path ""] != 0 && [string compare [winfo class $path] "Message"] == 0} { eval $msgScript } else { $w delete $index1 $index2 $w window create $index1 -pady 1 -create $msgScript } } else { # # Check whether the label containing an image or the frame containing # a window is mapped at the first or last position of the cell # if {[string compare [lindex [$w dump -window $index1] 1] $aux] == 0} { set auxFound 1 if {[string compare $alignment "right"] == 0} { $w delete $index1+1c $index2 } } elseif {[string compare [lindex [$w dump -window $index2-1c] 1] $aux] == 0} { set auxFound 1 if {[string compare $alignment "right"] != 0} { $w delete $index1 $index2-1c } } else { set auxFound 0 $w delete $index1 $index2 } if {$auxFound} { # # Adjust the aux. window's width and contents # $aux configure -width $auxWidth if {[string compare $alignment "right"] == 0} { if {$auxType == 2} { ;# window place $aux.w -relx 1.0 -anchor ne } set path [lindex [$w dump -window $index1] 1] if {[string compare $path ""] != 0 && [string compare [winfo class $path] "Message"] == 0} { $w window configure $index1 -window [eval $msgScript] } else { $w window create $index1 -pady 1 -create $msgScript } } else { if {$auxType == 2} { ;# window place $aux.w -relx 0.0 -anchor nw } set path [lindex [$w dump -window $index1+1c] 1] if {[string compare $path ""] != 0 && [string compare [winfo class $path] "Message"] == 0} { $w window configure $index1+1c -window [eval $msgScript] } else { $w window create $index1+1c -pady 1 -create $msgScript } } } else { # # Insert the message and aux. windows # insertMlElem $w $index1 $msgScript $aux $auxType $alignment } } } #------------------------------------------------------------------------------ # tablelist::makeColFontAndTagLists # # Builds the lists data(colFontList) of the column fonts and data(colTagsList) # of the column tag names for the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::makeColFontAndTagLists win { upvar ::tablelist::ns${win}::data data set widgetFont $data(-font) set data(colFontList) {} set data(colTagsList) {} set data(hasColTags) 0 for {set col 0} {$col < $data(colCount)} {incr col} { set tagNames {} if {[info exists data($col-font)]} { lappend data(colFontList) $data($col-font) lappend tagNames col-font-$data($col-font) set data(hasColTags) 1 } else { lappend data(colFontList) $widgetFont } foreach opt {-background -foreground} { if {[info exists data($col$opt)]} { lappend tagNames col$opt-$data($col$opt) set data(hasColTags) 1 } } lappend data(colTagsList) $tagNames } } #------------------------------------------------------------------------------ # tablelist::makeSortAndArrowColLists # # Builds the lists data(sortColList) of the sort columns and data(arrowColList) # of the arrow columns for the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::makeSortAndArrowColLists win { upvar ::tablelist::ns${win}::data data set data(sortColList) {} set data(arrowColList) {} # # Build a list of {col sortRank} pairs and sort it based on sortRank # set pairList {} for {set col 0} {$col < $data(colCount)} {incr col} { if {$data($col-sortRank) > 0} { lappend pairList [list $col $data($col-sortRank)] } } set pairList [lsort -integer -index 1 $pairList] # # Build data(sortColList) and data(arrowColList), and update # the sort ranks to have values from 1 to [llength $pairList] # set sortRank 1 foreach pair $pairList { set col [lindex $pair 0] lappend data(sortColList) $col set data($col-sortRank) $sortRank if {$sortRank < 10 && $data(-showarrow) && $data($col-showarrow)} { lappend data(arrowColList) $col configCanvas $win $col raiseArrow $win $col } incr sortRank } } #------------------------------------------------------------------------------ # tablelist::setupColumns # # Updates the value of the -colums configuration option for the tablelist # widget win by using the width, title, and alignment specifications given in # the columns argument, and creates the corresponding label (and separator) # widgets if createLabels is true. #------------------------------------------------------------------------------ proc tablelist::setupColumns {win columns createLabels} { variable usingTile variable configSpecs variable configOpts variable alignments upvar ::tablelist::ns${win}::data data set argCount [llength $columns] set colConfigVals {} # # Check the syntax of columns before performing any changes # for {set n 0} {$n < $argCount} {incr n} { # # Get the column width # set width [lindex $columns $n] set width [format "%d" $width] ;# integer check with error message # # Get the column title # if {[incr n] == $argCount} { return -code error "column title missing" } set title [lindex $columns $n] # # Get the column alignment # set alignment left if {[incr n] < $argCount} { set next [lindex $columns $n] if {[catch {format "%d" $next}] == 0} { ;# integer check incr n -1 } else { set alignment [mwutil::fullOpt "alignment" $next $alignments] } } # # Append the properly formatted values of width, # title, and alignment to the list colConfigVals # lappend colConfigVals $width $title $alignment } # # Save the value of colConfigVals in data(-columns) # set data(-columns) $colConfigVals # # Delete the labels, canvases, and separators if requested # if {$createLabels} { foreach w [winfo children $data(hdrTxtFr)] { destroy $w } foreach w [winfo children $win] { if {[regexp {^sep[0-9]+$} [winfo name $w]]} { destroy $w } } set data(fmtCmdFlagList) {} } # # Build the list data(colList), and create # the labels and canvases if requested # set widgetFont $data(-font) set oldColCount $data(colCount) set data(colList) {} set data(colCount) 0 set data(lastCol) -1 set col 0 foreach {width title alignment} $data(-columns) { # # Append the width in pixels and the # alignment to the list data(colList) # if {$width > 0} { ;# convention: width in characters set pixels [charsToPixels $win $widgetFont $width] set data($col-lastStaticWidth) $pixels } elseif {$width < 0} { ;# convention: width in pixels set pixels [expr {(-1)*$width}] set data($col-lastStaticWidth) $pixels } else { ;# convention: dynamic width set pixels 0 } lappend data(colList) $pixels $alignment incr data(colCount) set data(lastCol) $col if {$createLabels} { set data($col-elide) 0 foreach {name val} {delta 0 lastStaticWidth 0 maxPixels 0 sortOrder "" sortRank 0 editable 0 editwindow entry hide 0 maxwidth 0 resizable 1 showarrow 1 sortmode ascii} { if {![info exists data($col-$name)]} { set data($col-$name) $val } } lappend data(fmtCmdFlagList) [info exists data($col-formatcommand)] # # Create the label # set w $data(hdrTxtFrLbl)$col if {$usingTile} { ttk::label $w -style TablelistHeader.TLabel -image "" \ -padding {1 1 1 1} -takefocus 0 -text "" \ -textvariable "" -underline -1 -wraplength 0 } else { tk::label $w -bitmap "" -highlightthickness 0 -image "" \ -takefocus 0 -text "" -textvariable "" \ -underline -1 -wraplength 0 } # # Apply to it the current configuration options # foreach opt $configOpts { set optGrp [lindex $configSpecs($opt) 2] if {[string compare $optGrp "l"] == 0} { set optTail [string range $opt 6 end] if {[info exists data($col$opt)]} { configLabel $w -$optTail $data($col$opt) } else { configLabel $w -$optTail $data($opt) } } elseif {[string compare $optGrp "c"] == 0} { configLabel $w $opt $data($opt) } } catch {configLabel $w -state $data(-state)} # # Replace the binding tag Label with TablelistLabel # in the list of binding tags of the label # bindtags $w [lreplace [bindtags $w] 1 1 TablelistLabel] # # Create a canvas containing the sort arrows # set w $data(hdrTxtFrCanv)$col canvas $w -borderwidth 0 -highlightthickness 0 \ -relief flat -takefocus 0 regexp {^(flat|sunken)([0-9]+)x([0-9]+)$} $data(-arrowstyle) \ dummy relief width height createArrows $w $width $height $relief # # Apply to it the current configuration options # foreach opt $configOpts { if {[string compare [lindex $configSpecs($opt) 2] "c"] == 0} { $w configure $opt $data($opt) } } # # Replace the binding tag Canvas with TablelistArrow # in the list of binding tags of the canvas # bindtags $w [lreplace [bindtags $w] 1 1 TablelistArrow] if {[info exists data($col-labelimage)]} { doColConfig $col $win -labelimage $data($col-labelimage) } } # # Configure the edit window if present # if {$col == $data(editCol) && [string compare [winfo class $data(bodyFrEd)] "Mentry"] != 0} { catch {$data(bodyFrEd) configure -justify $alignment} } incr col } # # Clean up the data associated with the deleted columns # for {set col $data(colCount)} {$col < $oldColCount} {incr col} { deleteColData $win $col } # # Create the separators if needed # if {$createLabels && $data(-showseparators)} { createSeps $win } } #------------------------------------------------------------------------------ # tablelist::createSeps # # Creates and manages the separator frames in the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::createSeps win { variable usingTile upvar ::tablelist::ns${win}::data data set x 1 if {$usingTile} { if {[string compare $tile::currentTheme "xpnative"] == 0 && $::tablelist::xpStyle} { set x 0 } elseif {[string compare $tile::currentTheme "tileqt"] == 0 && [string compare [string tolower $tile::theme::tileqt::theme] \ "qtcurve"] == 0} { set x 2 } } for {set col 0} {$col < $data(colCount)} {incr col} { # # Create the col'th separator frame and attach it # to the right edge of the col'th header label # set w $data(sep)$col if {$usingTile} { ttk::separator $w -style Seps$win.TSeparator \ -cursor $data(-cursor) -orient vertical \ -takefocus 0 } else { tk::frame $w -background $data(-background) -borderwidth 1 \ -container 0 -cursor $data(-cursor) \ -highlightthickness 0 -relief sunken \ -takefocus 0 -width 2 } place $w -in $data(hdrTxtFrLbl)$col -anchor ne -bordermode outside \ -relx 1.0 -x $x # # Replace the binding tag Frame with $data(bodyTag) and # TablelistBody in the list of binding tags of the frame # bindtags $w [lreplace [bindtags $w] 1 1 $data(bodyTag) TablelistBody] } adjustSepsWhenIdle $win } #------------------------------------------------------------------------------ # tablelist::adjustSepsWhenIdle # # Arranges for the height and vertical position of each separator frame in the # tablelist widget win to be adjusted at idle time. #------------------------------------------------------------------------------ proc tablelist::adjustSepsWhenIdle win { upvar ::tablelist::ns${win}::data data if {[info exists data(sepsId)]} { return "" } set data(sepsId) [after idle [list tablelist::adjustSeps $win]] } #------------------------------------------------------------------------------ # tablelist::adjustSeps # # Adjusts the height and vertical position of each separator frame in the # tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::adjustSeps win { variable usingTile upvar ::tablelist::ns${win}::data data if {[info exists data(sepsId)]} { after cancel $data(sepsId) unset data(sepsId) } # # Get the height to be applied to the separator frames # set w $data(body) set textIdx [$w index @0,[expr {[winfo height $w] - 1}]] set dlineinfo [$w dlineinfo $textIdx] if {$data(itemCount) == 0 || [string compare $dlineinfo ""] == 0} { set sepHeight 1 } else { foreach {x y width height baselinePos} $dlineinfo {} set sepHeight [expr {$y + $height}] } # # Set the height of the main separator frame (if any) and attach # the latter to the right edge of the last non-hidden title column # set startCol [expr {$data(-titlecolumns) - 1}] if {$startCol > $data(lastCol)} { set startCol $data(lastCol) } for {set col $startCol} {$col >= 0} {incr col -1} { if {!$data($col-hide)} { break } } set w $data(sep) if {$col < 0} { if {[winfo exists $w]} { place forget $w } } else { if {$usingTile && [string compare $tile::currentTheme "xpnative"] == 0 && $::tablelist::xpStyle} { set x 0 } else { set x 1 } place $w -in $data(hdrTxtFrLbl)$col -anchor ne -bordermode outside \ -height [expr {$sepHeight + [winfo height $data(hdr)] - 1}] \ -relx 1.0 -x $x -y 1 raise $w } # # Set the height and vertical position of each separator frame # if {!$usingTile && $data(-showlabels)} { incr sepHeight } foreach w [winfo children $win] { if {[regexp {^sep[0-9]+$} [winfo name $w]]} { if {$data(-showlabels)} { if {$usingTile} { place configure $w -height $sepHeight -rely 1.0 -y 0 } else { place configure $w -height $sepHeight -rely 1.0 -y -1 } } else { if {$usingTile} { place configure $w -height $sepHeight -rely 0.0 -y 1 } else { place configure $w -height $sepHeight -rely 0.0 -y 0 } } } } } #------------------------------------------------------------------------------ # tablelist::adjustColumns # # Applies some configuration options to the labels of the tablelist widget win, # places them in the header frame, computes and sets the tab stops for the body # text widget, and adjusts the width and height of the header frame. The # whichWidths argument specifies the dynamic-width columns or labels whose # widths are to be computed when performing these operations. The stretchCols # argument specifies whether to stretch the stretchable columns. #------------------------------------------------------------------------------ proc tablelist::adjustColumns {win whichWidths stretchCols} { variable canElide upvar ::tablelist::ns${win}::data data set compAllColWidths [expr {[string compare $whichWidths "allCols"] == 0}] set compAllLabelWidths \ [expr {[string compare $whichWidths "allLabels"] == 0}] # # Configure the labels, place them in the header frame, and compute # the positions of the tab stops to be set in the body text widget # set data(hdrPixels) 0 set tabs {} set col 0 set x 0 foreach {pixels alignment} $data(colList) { set w $data(hdrTxtFrLbl)$col if {$data($col-hide) && !$canElide} { place forget $w incr col continue } # # Adjust the col'th label # if {[info exists data($col-labelalign)]} { set labelAlignment $data($col-labelalign) } else { set labelAlignment $alignment } if {$pixels != 0} { ;# convention: static width incr pixels $data($col-delta) } adjustLabel $win $col $pixels $labelAlignment if {$pixels == 0} { ;# convention: dynamic width # # Compute the column or label width if requested # if {$compAllColWidths} { computeColWidth $win $col } elseif {$compAllLabelWidths} { computeLabelWidth $win $col } elseif {[lsearch -exact $whichWidths $col] >= 0} { computeColWidth $win $col } elseif {[lsearch -exact $whichWidths l$col] >= 0} { computeLabelWidth $win $col } set pixels $data($col-reqPixels) if {$data($col-maxPixels) > 0} { if {$pixels > $data($col-maxPixels)} { set pixels $data($col-maxPixels) incr pixels $data($col-delta) adjustLabel $win $col $pixels $labelAlignment } } else { incr pixels $data($col-delta) } } if {$col == $data(editCol) && ![string match "*Checkbutton" [winfo class $data(bodyFrEd)]]} { adjustEditWindow $win $pixels } set canvas $data(hdrTxtFrCanv)$col if {[lsearch -exact $data(arrowColList) $col] >= 0 && !$data($col-elide) && !$data($col-hide)} { # # Place the canvas to the left side of the label if the # latter is right-justified and to its right side otherwise # if {[string compare $labelAlignment "right"] == 0} { place $canvas -in $w -anchor w -bordermode outside \ -relx 0.0 -x $data(charWidth) -rely 0.499 -y -1 } else { place $canvas -in $w -anchor e -bordermode outside \ -relx 1.0 -x -$data(charWidth) -rely 0.499 -y -1 } raise $canvas } else { place forget $canvas } # # Place the label in the header frame # if {$data($col-elide) || $data($col-hide)} { foreach l [getSublabels $w] { place forget $l } place $w -x [expr {$x - 1}] -relheight 1.0 -width 1 lower $w } else { set labelPixels [expr {$pixels + 2*$data(charWidth)}] place $w -x $x -relheight 1.0 -width $labelPixels } # # Append a tab stop and the alignment to the tabs list # if {!$data($col-elide) && !$data($col-hide)} { incr x $data(charWidth) switch $alignment { left { lappend tabs $x left incr x $pixels } right { incr x $pixels lappend tabs $x right } center { lappend tabs [expr {$x + $pixels/2}] center incr x $pixels } } incr x $data(charWidth) lappend tabs $x left } incr col } place $data(hdrLbl) -x $x set data(hdrPixels) $x # # Apply the value of tabs to the body text widget # $data(body) configure -tabs $tabs # # Adjust the width and height of the frames data(hdrTxtFr) and data(hdr) # $data(hdrTxtFr) configure -width $data(hdrPixels) if {$data(-width) <= 0} { if {$stretchCols} { $data(hdr) configure -width $data(hdrPixels) $data(lb) configure -width \ [expr {$data(hdrPixels) / $data(charWidth)}] } } else { $data(hdr) configure -width 0 } adjustHeaderHeight $win # # Stretch the stretchable columns if requested, and update # the scrolled column offset and the horizontal scrollbar # if {$stretchCols} { stretchColumnsWhenIdle $win } if {![info exists data(x)]} { ;# no resize operation in progress updateScrlColOffset $win } updateHScrlbarWhenIdle $win } #------------------------------------------------------------------------------ # tablelist::adjustLabel # # Applies some configuration options to the col'th label of the tablelist # widget win as well as to the label's sublabels (if any), and places the # sublabels. #------------------------------------------------------------------------------ proc tablelist::adjustLabel {win col pixels alignment} { variable anchors variable usingTile upvar ::tablelist::ns${win}::data data # # Apply some configuration options to the label and its sublabels (if any) # set w $data(hdrTxtFrLbl)$col set anchor $anchors($alignment) set borderWidth [winfo pixels $w [$w cget -borderwidth]] if {$borderWidth < 0} { set borderWidth 0 } set padX [expr {$data(charWidth) - $borderWidth}] configLabel $w -anchor $anchor -justify $alignment -padx $padX if {[info exists data($col-labelimage)]} { set imageWidth [image width $data($col-labelimage)] $w-tl configure -anchor $anchor -justify $alignment } else { set imageWidth 0 } # # Make room for the canvas displaying an up- or down-arrow if needed # set title [lindex $data(-columns) [expr {3*$col + 1}]] set labelFont [$w cget -font] if {[lsearch -exact $data(arrowColList) $col] >= 0} { set spaceWidth [font measure $labelFont -displayof $w " "] set canvas $data(hdrTxtFrCanv)$col set canvasWidth $data(arrowWidth) if {[llength $data(arrowColList)] > 1} { incr canvasWidth 6 variable library $canvas itemconfigure sortRank \ -image sortRank$data($col-sortRank)$win } $canvas configure -width $canvasWidth set spaces " " set n 2 while {$n*$spaceWidth < $canvasWidth + $data(charWidth)} { append spaces " " incr n } set spacePixels [expr {$n * $spaceWidth}] } else { set spaces "" set spacePixels 0 } if {$pixels == 0} { ;# convention: dynamic width # # Set the label text # if {$imageWidth == 0} { ;# no image if {[string compare $title ""] == 0} { set text $spaces } else { set lines {} foreach line [split $title "\n"] { if {[string compare $alignment "right"] == 0} { lappend lines $spaces$line } else { lappend lines $line$spaces } } set text [join $lines "\n"] } $w configure -text $text } elseif {[string compare $title ""] == 0} { ;# image w/o text $w configure -text "" set text $spaces $w-tl configure -text $text $w-il configure -width $imageWidth } else { ;# both image and text $w configure -text "" set lines {} foreach line [split $title "\n"] { if {[string compare $alignment "right"] == 0} { lappend lines "$spaces$line " } else { lappend lines " $line$spaces" } } set text [join $lines "\n"] $w-tl configure -text $text $w-il configure -width $imageWidth } } else { # # Clip each line of title according to pixels and alignment # set lessPixels [expr {$pixels - $spacePixels}] if {$imageWidth == 0} { ;# no image if {[string compare $title ""] == 0} { set text $spaces } else { set lines {} foreach line [split $title "\n"] { set line [strRangeExt $win $line $labelFont \ $lessPixels $alignment $data(-snipstring)] if {[string compare $alignment "right"] == 0} { lappend lines $spaces$line } else { lappend lines $line$spaces } } set text [join $lines "\n"] } $w configure -text $text } elseif {[string compare $title ""] == 0} { ;# image w/o text $w configure -text "" if {$imageWidth + $spacePixels <= $pixels} { set text $spaces $w-tl configure -text $text $w-il configure -width $imageWidth } elseif {$spacePixels < $pixels} { set text $spaces $w-tl configure -text $text $w-il configure -width [expr {$pixels - $spacePixels}] } else { set imageWidth 0 ;# can't disp. the image set text "" } } else { ;# both image and text $w configure -text "" set gap [font measure $labelFont -displayof $win " "] if {$imageWidth + $gap + $spacePixels <= $pixels} { incr lessPixels -[expr {$imageWidth + $gap}] set lines {} foreach line [split $title "\n"] { set line [strRangeExt $win $line $labelFont \ $lessPixels $alignment $data(-snipstring)] if {[string compare $alignment "right"] == 0} { lappend lines "$spaces$line " } else { lappend lines " $line$spaces" } } set text [join $lines "\n"] $w-tl configure -text $text $w-il configure -width $imageWidth } elseif {$imageWidth + $spacePixels <= $pixels} { set text $spaces ;# can't display the orig. text $w-tl configure -text $text $w-il configure -width $imageWidth } elseif {$spacePixels < $pixels} { set text $spaces ;# can't display the orig. text $w-tl configure -text $text $w-il configure -width [expr {$pixels - $spacePixels}] } else { set imageWidth 0 ;# can't display the image set text "" ;# can't display the text } } } # # Place the label's sublabels (if any) # if {$imageWidth == 0} { if {[info exists data($col-labelimage)]} { place forget $w-il place forget $w-tl } } else { if {[string compare $text ""] == 0} { place forget $w-tl } set margin $data(charWidth) switch $alignment { left { place $w-il -in $w -anchor w -bordermode outside \ -relx 0.0 -x $margin -rely 0.499 if {[string compare $text ""] != 0} { if {$usingTile} { set padding [$w cget -padding] lset padding 0 [expr {$padX + [winfo reqwidth $w-il]}] $w configure -padding $padding -text $text } else { set textX [expr {$margin + [winfo reqwidth $w-il]}] place $w-tl -in $w -anchor w -bordermode outside \ -relx 0.0 -x $textX -rely 0.499 } } } right { place $w-il -in $w -anchor e -bordermode outside \ -relx 1.0 -x -$margin -rely 0.499 if {[string compare $text ""] != 0} { if {$usingTile} { set padding [$w cget -padding] lset padding 2 [expr {$padX + [winfo reqwidth $w-il]}] $w configure -padding $padding -text $text } else { set textX [expr {-$margin - [winfo reqwidth $w-il]}] place $w-tl -in $w -anchor e -bordermode outside \ -relx 1.0 -x $textX -rely 0.499 } } } center { if {[string compare $text ""] == 0} { place $w-il -in $w -anchor center -relx 0.5 -x 0 -rely 0.499 } else { set reqWidth [expr {[winfo reqwidth $w-il] + [winfo reqwidth $w-tl]}] set iX [expr {-$reqWidth/2}] place $w-il -in $w -anchor w -relx 0.5 -x $iX -rely 0.499 if {$usingTile} { set padding [$w cget -padding] lset padding 0 [expr {$padX + [winfo reqwidth $w-il]}] $w configure -padding $padding -text $text } else { set tX [expr {$reqWidth + $iX}] place $w-tl -in $w -anchor e -relx 0.5 -x $tX \ -rely 0.499 } } } } } } #------------------------------------------------------------------------------ # tablelist::computeColWidth # # Computes the width of the col'th column of the tablelist widget win to be just # large enough to hold all the elements of the column (including its label). #------------------------------------------------------------------------------ proc tablelist::computeColWidth {win col} { upvar ::tablelist::ns${win}::data data set fmtCmdFlag [info exists data($col-formatcommand)] set data($col-elemWidth) 0 set data($col-widestCount) 0 # # Column elements # foreach item $data(itemList) { if {$col >= [llength $item] - 1} { continue } set key [lindex $item end] if {[info exists data($key-hide)]} { continue } set text [lindex $item $col] if {$fmtCmdFlag} { set text [uplevel #0 $data($col-formatcommand) [list $text]] } set text [strToDispStr $text] getAuxData $win $key $col auxType auxWidth set cellFont [getCellFont $win $key $col] set textWidth [getCellTextWidth $win $text $auxWidth $cellFont] set elemWidth [expr {$auxWidth + $textWidth}] if {$elemWidth == $data($col-elemWidth)} { incr data($col-widestCount) } elseif {$elemWidth > $data($col-elemWidth)} { set data($col-elemWidth) $elemWidth set data($col-widestCount) 1 } } set data($col-reqPixels) $data($col-elemWidth) # # Column label # computeLabelWidth $win $col } #------------------------------------------------------------------------------ # tablelist::computeLabelWidth # # Computes the width of the col'th label of the tablelist widget win and # adjusts the column's width accordingly. #------------------------------------------------------------------------------ proc tablelist::computeLabelWidth {win col} { upvar ::tablelist::ns${win}::data data set w $data(hdrTxtFrLbl)$col if {[info exists data($col-labelimage)]} { set netLabelWidth \ [expr {[winfo reqwidth $w-il] + [winfo reqwidth $w-tl]}] } else { ;# no image set netLabelWidth [expr {[winfo reqwidth $w] - 2*$data(charWidth)}] } if {$netLabelWidth < $data($col-elemWidth)} { set data($col-reqPixels) $data($col-elemWidth) } else { set data($col-reqPixels) $netLabelWidth } } #------------------------------------------------------------------------------ # tablelist::adjustHeaderHeight # # Sets the height of the header frame of the tablelist widget win to the max. # height of its children. #------------------------------------------------------------------------------ proc tablelist::adjustHeaderHeight win { upvar ::tablelist::ns${win}::data data # # Compute the max. label height # set maxLabelHeight [winfo reqheight $data(hdrLbl)] for {set col 0} {$col < $data(colCount)} {incr col} { set w $data(hdrTxtFrLbl)$col if {[string compare [winfo manager $w] ""] == 0} { continue } set reqHeight [winfo reqheight $w] if {$reqHeight > $maxLabelHeight} { set maxLabelHeight $reqHeight } foreach l [getSublabels $w] { if {[string compare [winfo manager $l] ""] == 0} { continue } set borderWidth [winfo pixels $w [$w cget -borderwidth]] if {$borderWidth < 0} { set borderWidth 0 } set reqHeight [expr {[winfo reqheight $l] + 2*$borderWidth}] if {$reqHeight > $maxLabelHeight} { set maxLabelHeight $reqHeight } } } # # Set the height of the header frame, update # the colors, and adjust the separators # $data(hdrTxtFr) configure -height $maxLabelHeight if {$data(-showlabels)} { $data(hdr) configure -height $maxLabelHeight } else { $data(hdr) configure -height 1 } updateColorsWhenIdle $win adjustSepsWhenIdle $win } #------------------------------------------------------------------------------ # tablelist::stretchColumnsWhenIdle # # Arranges for the stretchable columns of the tablelist widget win to be # stretched at idle time. #------------------------------------------------------------------------------ proc tablelist::stretchColumnsWhenIdle win { upvar ::tablelist::ns${win}::data data if {[info exists data(stretchId)]} { return "" } set data(stretchId) [after idle [list tablelist::stretchColumns $win -1]] } #------------------------------------------------------------------------------ # tablelist::stretchColumns # # Stretches the stretchable columns to fill the tablelist window win # horizontally. The colOfFixedDelta argument specifies the column for which # the stretching is to be made using a precomputed amount of pixels. #------------------------------------------------------------------------------ proc tablelist::stretchColumns {win colOfFixedDelta} { upvar ::tablelist::ns${win}::data data if {[info exists data(stretchId)]} { after cancel $data(stretchId) unset data(stretchId) } set forceAdjust $data(forceAdjust) set data(forceAdjust) 0 if {$data(hdrPixels) == 0 || $data(-width) <= 0} { return "" } # # Get the list data(stretchableCols) of the # numerical indices of the stretchable columns # set data(stretchableCols) {} if {[string first $data(-stretch) "all"] == 0} { for {set col 0} {$col < $data(colCount)} {incr col} { lappend data(stretchableCols) $col } } else { foreach col $data(-stretch) { lappend data(stretchableCols) [colIndex $win $col 0] } } # # Compute the total number data(delta) of pixels by which the # columns are to be stretched and the total amount # data(stretchablePixels) of stretchable column widths in pixels # set data(delta) [winfo width $data(hdr)] set data(stretchablePixels) 0 set lastColToStretch -1 set col 0 foreach {pixels alignment} $data(colList) { if {$data($col-hide)} { incr col continue } if {$pixels == 0} { ;# convention: dynamic width set pixels $data($col-reqPixels) if {$data($col-maxPixels) > 0} { if {$pixels > $data($col-maxPixels)} { set pixels $data($col-maxPixels) } } } incr data(delta) -[expr {$pixels + 2*$data(charWidth)}] if {[lsearch -exact $data(stretchableCols) $col] >= 0} { incr data(stretchablePixels) $pixels set lastColToStretch $col } incr col } if {$data(delta) < 0} { set delta 0 } else { set delta $data(delta) } if {$data(stretchablePixels) == 0 && !$forceAdjust} { return "" } # # Distribute the value of delta to the stretchable # columns, proportionally to their widths in pixels # set rest $delta set col 0 foreach {pixels alignment} $data(colList) { if {$data($col-hide) || [lsearch -exact $data(stretchableCols) $col] < 0} { set data($col-delta) 0 } else { set oldDelta $data($col-delta) if {$pixels == 0} { ;# convention: dynamic width set dynamic 1 set pixels $data($col-reqPixels) if {$data($col-maxPixels) > 0} { if {$pixels > $data($col-maxPixels)} { set pixels $data($col-maxPixels) set dynamic 0 } } } else { set dynamic 0 } if {$data(stretchablePixels) == 0} { set data($col-delta) 0 } else { if {$col != $colOfFixedDelta} { set data($col-delta) \ [expr {$delta*$pixels/$data(stretchablePixels)}] } incr rest -$data($col-delta) } if {$col == $lastColToStretch} { incr data($col-delta) $rest } if {!$dynamic && $data($col-delta) != $oldDelta} { redisplayColWhenIdle $win $col } } incr col } # # Adjust the columns # adjustColumns $win {} 0 } #------------------------------------------------------------------------------ # tablelist::updateColorsWhenIdle # # Arranges for the background and foreground colors of the label and message # widgets containing the currently visible images and multiline elements of the # tablelist widget win to be updated at idle time. #------------------------------------------------------------------------------ proc tablelist::updateColorsWhenIdle win { upvar ::tablelist::ns${win}::data data if {[info exists data(colorId)]} { return "" } set data(colorId) [after idle [list tablelist::updateColors $win]] } #------------------------------------------------------------------------------ # tablelist::updateColors # # Updates the background and foreground colors of the label and message widgets # containing the currently visible images and multiline elements of the # tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::updateColors win { upvar ::tablelist::ns${win}::data data if {[info exists data(colorId)]} { after cancel $data(colorId) unset data(colorId) } set w $data(body) set topLeftIdx [$w index @0,0] set btmRightIdx "[$w index @0,[expr {[winfo height $w] - 1}]] lineend" foreach {dummy path textIdx} [$w dump -window $topLeftIdx $btmRightIdx] { if {[string compare $path ""] == 0} { continue } set class [winfo class $path] if {[string compare $class "Label"] != 0 && [string compare $class "Message"] != 0} { continue } set name [winfo name $path] foreach {key col} [split [string range $name 1 end] ","] {} set tagNames [$w tag names $textIdx] # # Set the widget's background and foreground # colors to those of the containing cell # if {$data(isDisabled)} { set bg $data(-background) set fg $data(-disabledforeground) } elseif {[lsearch -exact $tagNames select] < 0} { ;# not selected if {[info exists data($key-$col-background)]} { set bg $data($key-$col-background) } elseif {[info exists data($key-background)]} { set bg $data($key-background) } elseif {[lsearch -exact $tagNames stripe] < 0 || [string compare $data(-stripebackground) ""] == 0} { if {[info exists data($col-background)]} { set bg $data($col-background) } else { set bg $data(-background) } } else { set bg $data(-stripebackground) } if {[info exists data($key-$col-foreground)]} { set fg $data($key-$col-foreground) } elseif {[info exists data($key-foreground)]} { set fg $data($key-foreground) } elseif {[lsearch -exact $tagNames stripe] < 0 || [string compare $data(-stripeforeground) ""] == 0} { if {[info exists data($col-foreground)]} { set fg $data($col-foreground) } else { set fg $data(-foreground) } } else { set fg $data(-stripeforeground) } } else { ;# selected if {[info exists data($key-$col-selectbackground)]} { set bg $data($key-$col-selectbackground) } elseif {[info exists data($key-selectbackground)]} { set bg $data($key-selectbackground) } elseif {[info exists data($col-selectbackground)]} { set bg $data($col-selectbackground) } else { set bg $data(-selectbackground) } if {[info exists data($key-$col-selectforeground)]} { set fg $data($key-$col-selectforeground) } elseif {[info exists data($key-selectforeground)]} { set fg $data($key-selectforeground) } elseif {[info exists data($col-selectforeground)]} { set fg $data($col-selectforeground) } else { set fg $data(-selectforeground) } } if {[string compare [$path cget -background] $bg] != 0} { $path configure -background $bg } if {[string compare [$path cget -foreground] $fg] != 0} { $path configure -foreground $fg } } } #------------------------------------------------------------------------------ # tablelist::updateScrlColOffsetWhenIdle # # Arranges for the scrolled column offset of the tablelist widget win to be # updated at idle time. #------------------------------------------------------------------------------ proc tablelist::updateScrlColOffsetWhenIdle win { upvar ::tablelist::ns${win}::data data if {[info exists data(offsetId)]} { return "" } set data(offsetId) [after idle [list tablelist::updateScrlColOffset $win]] } #------------------------------------------------------------------------------ # tablelist::updateScrlColOffset # # Updates the scrolled column offset of the tablelist widget win to fit into # the allowed range. #------------------------------------------------------------------------------ proc tablelist::updateScrlColOffset win { upvar ::tablelist::ns${win}::data data if {[info exists data(offsetId)]} { after cancel $data(offsetId) unset data(offsetId) } set maxScrlColOffset [getMaxScrlColOffset $win] if {$data(scrlColOffset) > $maxScrlColOffset} { set data(scrlColOffset) $maxScrlColOffset adjustElidedTextWhenIdle $win } } #------------------------------------------------------------------------------ # tablelist::updateHScrlbarWhenIdle # # Arranges for the horizontal scrollbar associated with the tablelist widget # win to be updated at idle time. #------------------------------------------------------------------------------ proc tablelist::updateHScrlbarWhenIdle win { upvar ::tablelist::ns${win}::data data if {[info exists data(hScrlbarId)]} { return "" } set data(hScrlbarId) [after idle [list tablelist::updateHScrlbar $win]] } #------------------------------------------------------------------------------ # tablelist::updateHScrlbar # # Updates the horizontal scrollbar associated with the tablelist widget win by # invoking the command specified as the value of the -xscrollcommand option. #------------------------------------------------------------------------------ proc tablelist::updateHScrlbar win { upvar ::tablelist::ns${win}::data data if {[info exists data(hScrlbarId)]} { after cancel $data(hScrlbarId) unset data(hScrlbarId) } if {$data(-titlecolumns) > 0 && [string compare $data(-xscrollcommand) ""] != 0} { eval $data(-xscrollcommand) [xviewSubCmd $win {}] } } #------------------------------------------------------------------------------ # tablelist::updateVScrlbarWhenIdle # # Arranges for the vertical scrollbar associated with the tablelist widget win # to be updated at idle time. #------------------------------------------------------------------------------ proc tablelist::updateVScrlbarWhenIdle win { upvar ::tablelist::ns${win}::data data if {[info exists data(vScrlbarId)]} { return "" } set data(vScrlbarId) [after idle [list tablelist::updateVScrlbar $win]] } #------------------------------------------------------------------------------ # tablelist::updateVScrlbar # # Updates the vertical scrollbar associated with the tablelist widget win by # invoking the command specified as the value of the -yscrollcommand option. #------------------------------------------------------------------------------ proc tablelist::updateVScrlbar win { upvar ::tablelist::ns${win}::data data if {[info exists data(vScrlbarId)]} { after cancel $data(vScrlbarId) unset data(vScrlbarId) } if {[string compare $data(-yscrollcommand) ""] != 0} { eval $data(-yscrollcommand) [yviewSubCmd $win {}] } } #------------------------------------------------------------------------------ # tablelist::adjustElidedTextWhenIdle # # Arranges for the elided text ranges of the body text child of the tablelist # widget win to be updated at idle time. #------------------------------------------------------------------------------ proc tablelist::adjustElidedTextWhenIdle win { upvar ::tablelist::ns${win}::data data if {[info exists data(elidedId)]} { return "" } set data(elidedId) [after idle [list tablelist::adjustElidedText $win]] } #------------------------------------------------------------------------------ # tablelist::adjustElidedText # # Updates the elided text ranges of the body text child of the tablelist widget # win. #------------------------------------------------------------------------------ proc tablelist::adjustElidedText win { upvar ::tablelist::ns${win}::data data if {[info exists data(elidedId)]} { after cancel $data(elidedId) unset data(elidedId) } # # Remove the "hiddenCol" tag # set w $data(body) $w tag remove hiddenCol 1.0 end # # Add the "hiddenCol" tag to the contents of the hidden # columns from the top to the bottom window line # variable canElide if {$canElide && $data(hiddenColCount) > 0 && $data(itemCount) > 0} { set btmY [expr {[winfo height $w] - 1}] set topLine [expr {int([$w index @0,0])}] set btmLine [expr {int([$w index @0,$btmY])}] for {set line $topLine; set row [expr {$line - 1}]} \ {$line <= $btmLine} {set row $line; incr line} { set key [lindex [lindex $data(itemList) $row] end] if {[info exists data($key-hide)]} { continue } set textIdx1 $line.0 for {set col 0; set count 0} \ {$col < $data(colCount) && $count < $data(hiddenColCount)} \ {incr col} { set textIdx2 \ [$w search -elide "\t" $textIdx1+1c $line.end]+1c if {$data($col-hide)} { incr count $w tag add hiddenCol $textIdx1 $textIdx2 } set textIdx1 $textIdx2 } # # Update btmLine because it may # change due to the "hiddenCol" tag # set btmLine [expr {int([$w index @0,$btmY])}] } if {[lindex [$w yview] 1] == 1} { for {set line $btmLine; set row [expr {$line - 1}]} \ {$line >= $topLine} {set line $row; incr row -1} { set key [lindex [lindex $data(itemList) $row] end] if {[info exists data($key-hide)]} { continue } set textIdx1 $line.0 for {set col 0; set count 0} \ {$col < $data(colCount) && $count < $data(hiddenColCount)} \ {incr col} { set textIdx2 \ [$w search -elide "\t" $textIdx1+1c $line.end]+1c if {$data($col-hide)} { incr count $w tag add hiddenCol $textIdx1 $textIdx2 } set textIdx1 $textIdx2 } # # Update topLine because it may # change due to the "hiddenCol" tag # set topLine [expr {int([$w index @0,0])}] } } } if {$data(-titlecolumns) == 0} { return "" } # # Remove the "elidedCol" tag # $w tag remove elidedCol 1.0 end for {set col 0} {$col < $data(colCount)} {incr col} { set data($col-elide) 0 } if {$data(scrlColOffset) == 0} { adjustColumns $win {} 0 return "" } # # Find max. $data(scrlColOffset) non-hidden columns with indices >= # $data(-titlecolumns) and retain the first and last of these indices # set firstCol $data(-titlecolumns) while {$firstCol < $data(colCount) && $data($firstCol-hide)} { incr firstCol } if {$firstCol >= $data(colCount)} { return "" } set lastCol $firstCol set nonHiddenCount 1 while {$nonHiddenCount < $data(scrlColOffset) && $lastCol < $data(colCount)} { incr lastCol if {!$data($lastCol-hide)} { incr nonHiddenCount } } # # Add the "elidedCol" tag to the contents of these # columns from the top to the bottom window line # if {$data(itemCount) > 0} { set btmY [expr {[winfo height $w] - 1}] set topLine [expr {int([$w index @0,0])}] set btmLine [expr {int([$w index @0,$btmY])}] for {set line $topLine; set row [expr {$line - 1}]} \ {$line <= $btmLine} {set row $line; incr line} { set key [lindex [lindex $data(itemList) $row] end] if {![info exists data($key-hide)]} { findTabs $win $line $firstCol $lastCol tabIdx1 tabIdx2 $w tag add elidedCol $tabIdx1 $tabIdx2+1c } # # Update btmLine because it may # change due to the "elidedCol" tag # set btmLine [expr {int([$w index @0,$btmY])}] } if {[lindex [$w yview] 1] == 1} { for {set line $btmLine; set row [expr {$line - 1}]} \ {$line >= $topLine} {set line $row; incr row -1} { set key [lindex [lindex $data(itemList) $row] end] if {![info exists data($key-hide)]} { findTabs $win $line $firstCol $lastCol tabIdx1 tabIdx2 $w tag add elidedCol $tabIdx1 $tabIdx2+1c } # # Update topLine because it may # change due to the "elidedCol" tag # set topLine [expr {int([$w index @0,0])}] } } } # # Adjust the columns # for {set col $firstCol} {$col <= $lastCol} {incr col} { set data($col-elide) 1 } adjustColumns $win {} 0 } #------------------------------------------------------------------------------ # tablelist::redisplayWhenIdle # # Arranges for the items of the tablelist widget win to be redisplayed at idle # time. #------------------------------------------------------------------------------ proc tablelist::redisplayWhenIdle win { upvar ::tablelist::ns${win}::data data if {[info exists data(redispId)] || $data(itemCount) == 0} { return "" } set data(redispId) [after idle [list tablelist::redisplay $win]] # # Cancel the execution of all delayed redisplayCol commands # foreach name [array names data *-redispId] { after cancel $data($name) unset data($name) } } #------------------------------------------------------------------------------ # tablelist::redisplay # # Redisplays the items of the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::redisplay {win {getSelCells 1} {selCells {}}} { variable canElide upvar ::tablelist::ns${win}::data data if {[info exists data(redispId)]} { after cancel $data(redispId) unset data(redispId) } # # Save the indices of the selected cells # if {$getSelCells} { set selCells [curcellselectionSubCmd $win] } # # Save some data of the edit window if present # if {[set editCol $data(editCol)] >= 0} { set editRow $data(editRow) saveEditData $win } set w $data(body) set widgetFont $data(-font) set snipStr $data(-snipstring) set isSimple [expr {$data(tagRefCount) == 0 && $data(imgCount) == 0 && $data(winCount) == 0 && !$data(hasColTags)}] set newItemList {} set row 0 set line 1 foreach item $data(itemList) { # # Empty the line, clip the elements if necessary, # and insert them with the corresponding tags # $w delete $line.0 $line.end set keyIdx [expr {[llength $item] - 1}] set key [lindex $item end] set newItem {} set col 0 if {$isSimple} { set insertStr "" set multilineData {} foreach fmtCmdFlag $data(fmtCmdFlagList) \ {pixels alignment} $data(colList) { if {$col < $keyIdx} { set text [lindex $item $col] } else { set text "" } lappend newItem $text if {$data($col-hide) && !$canElide} { incr col continue } # # Clip the element if necessary # if {$fmtCmdFlag} { set text [uplevel #0 $data($col-formatcommand) [list $text]] } set text [strToDispStr $text] if {[string match "*\n*" $text]} { set multiline 1 set list [split $text "\n"] } else { set multiline 0 } if {$pixels == 0} { ;# convention: dynamic width if {$data($col-maxPixels) > 0} { if {$data($col-reqPixels) > $data($col-maxPixels)} { set pixels $data($col-maxPixels) } } } if {$pixels != 0} { incr pixels $data($col-delta) if {$multiline} { set text [joinList $win $list $widgetFont \ $pixels $alignment $snipStr] } else { set text [strRangeExt $win $text $widgetFont \ $pixels $alignment $snipStr] } } if {$multiline} { append insertStr "\t\t" lappend multilineData $col $text $alignment } else { append insertStr "\t$text\t" } incr col } # # Insert the item into the body text widget # $w insert $line.0 $insertStr # # Embed the message widgets displaying multiline elements # foreach {col text alignment} $multilineData { findTabs $win $line $col $col tabIdx1 tabIdx2 set msgScript [list ::tablelist::displayText $win $key \ $col $text $widgetFont $alignment] $w window create $tabIdx2 -pady 1 -create $msgScript } } else { array set itemData [array get data $key*-\[bf\]*] ;# for speed set rowTags {} foreach name [array names itemData $key-\[bf\]*] { set tail [lindex [split $name "-"] 1] lappend rowTags row-$tail-$itemData($name) } foreach colTags $data(colTagsList) \ fmtCmdFlag $data(fmtCmdFlagList) \ {pixels alignment} $data(colList) { if {$col < $keyIdx} { set text [lindex $item $col] } else { set text "" } lappend newItem $text if {$data($col-hide) && !$canElide} { incr col continue } # # Adjust the cell text and the image or window width # if {$fmtCmdFlag} { set text [uplevel #0 $data($col-formatcommand) [list $text]] } set text [strToDispStr $text] if {[string match "*\n*" $text]} { set multiline 1 set list [split $text "\n"] } else { set multiline 0 } set aux [getAuxData $win $key $col auxType auxWidth] set cellFont [getCellFont $win $key $col] if {$pixels == 0} { ;# convention: dynamic width if {$data($col-maxPixels) > 0} { if {$data($col-reqPixels) > $data($col-maxPixels)} { set pixels $data($col-maxPixels) } } } if {$pixels != 0} { incr pixels $data($col-delta) } if {$multiline} { adjustMlElem $win list auxWidth $cellFont \ $pixels $alignment $snipStr set msgScript [list ::tablelist::displayText $win $key \ $col [join $list "\n"] $cellFont $alignment] } else { adjustElem $win text auxWidth $cellFont \ $pixels $alignment $snipStr } # # Insert the text and the auxiliary object # set cellTags {} foreach name [array names itemData $key-$col-\[bf\]*] { set tail [lindex [split $name "-"] 2] lappend cellTags cell-$tail-$itemData($name) } set tagNames [concat $colTags $rowTags $cellTags] if {$auxType == 0} { if {$multiline} { $w insert $line.end "\t\t" $tagNames $w window create $line.end-1c -pady 1 -create $msgScript } else { $w insert $line.end "\t$text\t" $tagNames } } else { $w insert $line.end "\t\t" $tagNames createAuxObject $win $key $row $col $aux $auxType $auxWidth if {$multiline} { insertMlElem $w $line.end-1c $msgScript \ $aux $auxType $alignment } else { insertElem $w $line.end-1c $text \ $aux $auxType $alignment } } incr col } unset itemData } if {[info exists data($key-hide)]} { $w tag add hiddenRow $line.0 $line.end+1c } lappend newItem $key lappend newItemList $newItem set row $line incr line } set data(itemList) $newItemList # # Restore the stripes in the body text widget # makeStripes $win # # Select the cells that were selected before # foreach cellIdx $selCells { scan $cellIdx "%d,%d" row col if {$col < $data(colCount)} { cellselectionSubCmd $win set $row $col $row $col } } # # Restore the edit window if it was present before # if {$editCol >= 0} { editcellSubCmd $win $editRow $editCol 1 } # # Adjust the elided text # adjustElidedTextWhenIdle $win } #------------------------------------------------------------------------------ # tablelist::redisplayColWhenIdle # # Arranges for the elements of the col'th column of the tablelist widget win to # be redisplayed at idle time. #------------------------------------------------------------------------------ proc tablelist::redisplayColWhenIdle {win col} { upvar ::tablelist::ns${win}::data data if {[info exists data($col-redispId)] || [info exists data(redispId)] || $data(itemCount) == 0} { return "" } set data($col-redispId) \ [after idle [list tablelist::redisplayCol $win $col 0 end]] } #------------------------------------------------------------------------------ # tablelist::redisplayCol # # Redisplays the elements of the col'th column of the tablelist widget win, in # the range specified by first and last. #------------------------------------------------------------------------------ proc tablelist::redisplayCol {win col first last} { upvar ::tablelist::ns${win}::data data if {$first == 0 && [string first $last "end"] == 0 && [info exists data($col-redispId)]} { after cancel $data($col-redispId) unset data($col-redispId) } if {$data(itemCount) == 0 || $data($col-hide) || $first < 0} { return "" } if {[string first $last "end"] == 0} { set last $data(lastRow) } set snipStr $data(-snipstring) set fmtCmdFlag [info exists data($col-formatcommand)] set w $data(body) set pixels [lindex $data(colList) [expr {2*$col}]] if {$pixels == 0} { ;# convention: dynamic width if {$data($col-maxPixels) > 0} { if {$data($col-reqPixels) > $data($col-maxPixels)} { set pixels $data($col-maxPixels) } } } if {$pixels != 0} { incr pixels $data($col-delta) } set alignment [lindex $data(colList) [expr {2*$col + 1}]] for {set row $first; set line [expr {$first + 1}]} {$row <= $last} \ {set row $line; incr line} { if {$row == $data(editRow) && $col == $data(editCol)} { continue } # # Adjust the cell text and the image or window width # set item [lindex $data(itemList) $row] set text [lindex $item $col] if {$fmtCmdFlag} { set text [uplevel #0 $data($col-formatcommand) [list $text]] } set text [strToDispStr $text] if {[string match "*\n*" $text]} { set multiline 1 set list [split $text "\n"] } else { set multiline 0 } set key [lindex $item end] set aux [getAuxData $win $key $col auxType auxWidth] set cellFont [getCellFont $win $key $col] if {$multiline} { adjustMlElem $win list auxWidth $cellFont \ $pixels $alignment $snipStr set msgScript [list ::tablelist::displayText $win $key \ $col [join $list "\n"] $cellFont $alignment] } else { adjustElem $win text auxWidth $cellFont $pixels $alignment $snipStr } # # Update the text widget's contents between the two tabs # findTabs $win $line $col $col tabIdx1 tabIdx2 if {$multiline} { updateMlCell $w $tabIdx1+1c $tabIdx2 $msgScript \ $aux $auxType $auxWidth $alignment } else { updateCell $w $tabIdx1+1c $tabIdx2 $text \ $aux $auxType $auxWidth $alignment } } } #------------------------------------------------------------------------------ # tablelist::makeStripesWhenIdle # # Arranges for the stripes in the body of the tablelist widget win to be # redrawn at idle time. #------------------------------------------------------------------------------ proc tablelist::makeStripesWhenIdle win { upvar ::tablelist::ns${win}::data data if {[info exists data(stripesId)] || $data(itemCount) == 0} { return "" } set data(stripesId) [after idle [list tablelist::makeStripes $win]] } #------------------------------------------------------------------------------ # tablelist::makeStripes # # Redraws the stripes in the body of the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::makeStripes win { upvar ::tablelist::ns${win}::data data if {[info exists data(stripesId)]} { after cancel $data(stripesId) unset data(stripesId) } set w $data(body) $w tag remove stripe 1.0 end if {[string compare $data(-stripebackground) ""] != 0 || [string compare $data(-stripeforeground) ""] != 0} { set count 0 set inStripe 0 for {set row 0; set line 1} {$row < $data(itemCount)} \ {set row $line; incr line} { set key [lindex [lindex $data(itemList) $row] end] if {![info exists data($key-hide)]} { if {$inStripe} { $w tag add stripe $line.0 $line.end } if {[incr count] == $data(-stripeheight)} { set count 0 set inStripe [expr {!$inStripe}] } } } } updateColors $win } #------------------------------------------------------------------------------ # tablelist::synchronize # # This procedure is invoked either as an idle callback after the list variable # associated with the tablelist widget win was written, or directly, upon # execution of some widget commands. It makes sure that the content of the # widget is synchronized with the value of the list variable. #------------------------------------------------------------------------------ proc tablelist::synchronize win { upvar ::tablelist::ns${win}::data data # # Nothing to do if the list variable was not written # if {![info exists data(syncId)]} { return "" } # # Here we are in the case that the procedure was scheduled for # execution at idle time. However, it might have been invoked # directly, before the idle time occured; in this case we should # cancel the execution of the previously scheduled idle callback. # after cancel $data(syncId) ;# no harm if data(syncId) is no longer valid unset data(syncId) upvar #0 $data(-listvariable) var set newCount [llength $var] if {$newCount < $data(itemCount)} { # # Delete the items with indices >= newCount from the widget # set updateCount $newCount deleteRows $win $newCount $data(lastRow) 0 } elseif {$newCount > $data(itemCount)} { # # Insert the items of var with indices # >= data(itemCount) into the widget # set updateCount $data(itemCount) insertSubCmd $win $data(itemCount) [lrange $var $data(itemCount) end] 0 } else { set updateCount $newCount } # # Update the first updateCount items of the internal list # set itemsChanged 0 for {set row 0} {$row < $updateCount} {incr row} { set oldItem [lindex $data(itemList) $row] set newItem [adjustItem [lindex $var $row] $data(colCount)] lappend newItem [lindex $oldItem end] if {[string compare $oldItem $newItem] != 0} { set data(itemList) [lreplace $data(itemList) $row $row $newItem] set itemsChanged 1 } } # # If necessary, adjust the columns and make sure # that the items will be redisplayed at idle time # if {$itemsChanged} { adjustColumns $win allCols 1 redisplayWhenIdle $win } } #------------------------------------------------------------------------------ # tablelist::getSublabels # # Returns the list of the existing sublabels $w-il and $w-tl associated with # the label widget w. #------------------------------------------------------------------------------ proc tablelist::getSublabels w { set lst {} foreach lbl [list $w-il $w-tl] { if {[winfo exists $lbl]} { lappend lst $lbl } } return $lst } #------------------------------------------------------------------------------ # tablelist::configLabel # # This procedure configures the label widget w according to the options and # their values given in args. It is needed for label widgets with sublabels. #------------------------------------------------------------------------------ proc tablelist::configLabel {w args} { foreach {opt val} $args { switch -- $opt { -active { if {[string compare [winfo class $w] "TLabel"] == 0} { set state [expr {$val ? "active" : "!active"}] $w state $state if {$val} { variable themeDefaults set bg $themeDefaults(-labelactiveBg) } else { set bg [$w cget -background] } foreach l [getSublabels $w] { $l configure -background $bg } } else { set state [expr {$val ? "active" : "normal"}] catch { $w configure -state $state foreach l [getSublabels $w] { $l configure -state $state } } } regexp {^(.+)\.hdr\.t\.f\.l([0-9]+)$} $w dummy win col upvar ::tablelist::ns${win}::data data if {[lsearch -exact $data(arrowColList) $col] >= 0} { configCanvas $win $col } } -activebackground - -activeforeground - -disabledforeground { $w configure $opt $val foreach l [getSublabels $w] { $l configure $opt $val } } -background - -foreground - -font { if {[string compare [winfo class $w] "TLabel"] == 0 && [string compare $val ""] == 0} { variable themeDefaults set val $themeDefaults(-label[string range $opt 1 end]) } $w configure $opt $val foreach l [getSublabels $w] { $l configure $opt $val } } -padx { if {[string compare [winfo class $w] "TLabel"] == 0} { set padding [$w cget -padding] $w configure -padding \ [list $val [lindex $padding 1] $val [lindex $padding 3]] } else { $w configure $opt $val } } -pady { if {[string compare [winfo class $w] "TLabel"] == 0} { set val [winfo pixels $w $val] set padding [$w cget -padding] $w configure -padding \ [list [lindex $padding 0] $val [lindex $padding 2] $val] } else { $w configure $opt $val } } -pressed { if {[string compare [winfo class $w] "TLabel"] == 0} { set state [expr {$val ? "pressed" : "!pressed"}] $w state $state variable themeDefaults if {$val} { set bg $themeDefaults(-labelpressedBg) } else { set bg $themeDefaults(-labelactiveBg) } foreach l [getSublabels $w] { $l configure -background $bg } regexp {^(.+)\.hdr\.t\.f\.l([0-9]+)$} $w dummy win col upvar ::tablelist::ns${win}::data data if {[lsearch -exact $data(arrowColList) $col] >= 0} { configCanvas $win $col } } } -state { $w configure $opt $val if {[string compare [winfo class $w] "TLabel"] == 0} { if {[string compare $val "disabled"] == 0} { variable themeDefaults set bg $themeDefaults(-labeldisabledBg) } else { set bg [$w cget -background] } foreach l [getSublabels $w] { $l configure -background $bg } } else { foreach l [getSublabels $w] { $l configure $opt $val } } } default { $w configure $opt $val } } } } #------------------------------------------------------------------------------ # tablelist::createArrows # # Creates two arrows in the canvas w. #------------------------------------------------------------------------------ proc tablelist::createArrows {w width height relief} { variable library if {$height < 6} { set wHeight 6 set y 1 } else { set wHeight $height set y 0 } $w configure -width $width -height $wHeight # # Delete any existing arrow image items from # the canvas and the corresponding images # foreach shape {triangleUp darkLineUp lightLineUp triangleDn darkLineDn lightLineDn} { $w delete $shape catch {image delete $shape$w} } # # Create the arrow images and canvas image items # corresponding to the procedure's arguments # $relief${width}x${height}Arrows $w foreach shape {triangleUp darkLineUp lightLineUp triangleDn darkLineDn lightLineDn} { catch {$w create image 0 $y -anchor nw -image $shape$w -tags $shape} } # # Create the sort rank image item # $w delete sortRank set x [expr {$width + 2}] set y [expr {$wHeight - 6}] $w create image $x $y -anchor nw -tags sortRank } #------------------------------------------------------------------------------ # tablelist::configCanvas # # Sets the background color of the canvas displaying an up- or down-arrow for # the given column, and fills the two arrows contained in the canvas. #------------------------------------------------------------------------------ proc tablelist::configCanvas {win col} { upvar ::tablelist::ns${win}::data data set w $data(hdrTxtFrLbl)$col set labelBg [$w cget -background] set labelFg [$w cget -foreground] if {[string compare [winfo class $w] "TLabel"] == 0} { variable themeDefaults foreach state {disabled active pressed} { $w instate $state { set labelBg $themeDefaults(-label${state}Bg) set labelFg $themeDefaults(-label${state}Fg) } } } else { catch { set state [$w cget -state] variable winSys if {[string compare $state "disabled"] == 0} { set labelFg [$w cget -disabledforeground] } elseif {[string compare $state "active"] == 0 && [string compare $winSys "classic"] != 0 && [string compare $winSys "aqua"] != 0} { set labelBg [$w cget -activebackground] set labelFg [$w cget -activeforeground] } } } set w $data(hdrTxtFrCanv)$col $w configure -background $labelBg sortRank$data($col-sortRank)$win configure -foreground $labelFg if {$data(isDisabled)} { fillArrows $w $data(-arrowdisabledcolor) } else { fillArrows $w $data(-arrowcolor) } } #------------------------------------------------------------------------------ # tablelist::fillArrows # # Fills the two arrows contained in the canvas w with the given color, or with # the background color of the canvas if color is an empty string. Also fills # the arrow's borders with the corresponding 3-D shadow colors. #------------------------------------------------------------------------------ proc tablelist::fillArrows {w color} { set bgColor [$w cget -background] if {[string compare $color ""] == 0} { set color $bgColor } getShadows $w $color darkColor lightColor foreach dir {Up Dn} { triangle$dir$w configure -foreground $color -background $bgColor catch { darkLine$dir$w configure -foreground $darkColor lightLine$dir$w configure -foreground $lightColor } } } #------------------------------------------------------------------------------ # tablelist::getShadows # # Computes the shadow colors for a 3-D border from a given (background) color. # This is the Tcl-counterpart of the function TkpGetShadows() in the Tk # distribution file unix/tkUnix3d.c. #------------------------------------------------------------------------------ proc tablelist::getShadows {w color darkColorName lightColorName} { upvar $darkColorName darkColor $lightColorName lightColor set rgb [winfo rgb $w $color] foreach {r g b} $rgb {} set maxIntens [lindex [winfo rgb $w white] 0] # # Compute the dark shadow color # if {[string compare $::tk_patchLevel "8.3.1"] >= 0 && $r*0.5*$r + $g*1.0*$g + $b*0.28*$b < $maxIntens*0.05*$maxIntens} { # # The background is already very dark: make the dark # color a little lighter than the background by increasing # each color component 1/4th of the way to $maxIntens # foreach comp $rgb { lappend darkRGB [expr {($maxIntens + 3*$comp)/4}] } } else { # # Compute the dark color by cutting 40% from # each of the background color components. # foreach comp $rgb { lappend darkRGB [expr {60*$comp/100}] } } set darkColor [eval format "#%04x%04x%04x" $darkRGB] # # Compute the light shadow color # if {[string compare $::tk_patchLevel "8.3.1"] >= 0 && $g > $maxIntens*0.95} { # # The background is already very bright: make the # light color a little darker than the background # by reducing each color component by 10% # foreach comp $rgb { lappend lightRGB [expr {90*$comp/100}] } } else { # # Compute the light color by boosting each background # color component by 40% or half-way to white, whichever # is greater (the first approach works better for # unsaturated colors, the second for saturated ones) # foreach comp $rgb { set comp1 [expr {140*$comp/100}] if {$comp1 > $maxIntens} { set comp1 $maxIntens } set comp2 [expr {($maxIntens + $comp)/2}] lappend lightRGB [expr {($comp1 > $comp2) ? $comp1 : $comp2}] } } set lightColor [eval format "#%04x%04x%04x" $lightRGB] } #------------------------------------------------------------------------------ # tablelist::raiseArrow # # Raises one of the two arrows contained in the canvas associated with the # given column of the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::raiseArrow {win col} { variable directions upvar ::tablelist::ns${win}::data data set w $data(hdrTxtFrCanv)$col set dir $directions($data(-incrarrowtype),$data($col-sortOrder)) $w raise triangle$dir $w raise darkLine$dir $w raise lightLine$dir } #------------------------------------------------------------------------------ # tablelist::isHdrTxtFrXPosVisible # # Checks whether the given x position in the header text child of the tablelist # widget win is visible. #------------------------------------------------------------------------------ proc tablelist::isHdrTxtFrXPosVisible {win x} { upvar ::tablelist::ns${win}::data data foreach {fraction1 fraction2} [$data(hdrTxt) xview] {} return [expr {$x >= $fraction1 * $data(hdrPixels) && $x < $fraction2 * $data(hdrPixels)}] } #------------------------------------------------------------------------------ # tablelist::getColWidth # # Returns the displayed width of the specified column of the tablelist widget # win. #------------------------------------------------------------------------------ proc tablelist::getColWidth {win col} { upvar ::tablelist::ns${win}::data data set pixels [lindex $data(colList) [expr {2*$col}]] if {$pixels == 0} { ;# convention: dynamic width set pixels $data($col-reqPixels) if {$data($col-maxPixels) > 0} { if {$pixels > $data($col-maxPixels)} { set pixels $data($col-maxPixels) } } } return [expr {$pixels + $data($col-delta) + 2*$data(charWidth)}] } #------------------------------------------------------------------------------ # tablelist::getScrlContentWidth # # Returns the total width of the non-hidden scrollable columns of the tablelist # widget win, in the specified range. #------------------------------------------------------------------------------ proc tablelist::getScrlContentWidth {win scrlColOffset lastCol} { upvar ::tablelist::ns${win}::data data set scrlContentWidth 0 set nonHiddenCount 0 for {set col $data(-titlecolumns)} {$col <= $lastCol} {incr col} { if {!$data($col-hide) && [incr nonHiddenCount] > $scrlColOffset} { incr scrlContentWidth [getColWidth $win $col] } } return $scrlContentWidth } #------------------------------------------------------------------------------ # tablelist::getScrlWindowWidth # # Returns the number of pixels obtained by subtracting the widths of the non- # hidden title columns from the width of the header frame of the tablelist # widget win. #------------------------------------------------------------------------------ proc tablelist::getScrlWindowWidth win { upvar ::tablelist::ns${win}::data data set scrlWindowWidth [winfo width $data(hdr)] for {set col 0} {$col < $data(-titlecolumns) && $col < $data(colCount)} \ {incr col} { if {!$data($col-hide)} { incr scrlWindowWidth -[getColWidth $win $col] } } return $scrlWindowWidth } #------------------------------------------------------------------------------ # tablelist::getMaxScrlColOffset # # Returns the max. scrolled column offset of the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::getMaxScrlColOffset win { upvar ::tablelist::ns${win}::data data # # Get the number of non-hidden scrollable columns # set maxScrlColOffset 0 for {set col $data(-titlecolumns)} {$col < $data(colCount)} {incr col} { if {!$data($col-hide)} { incr maxScrlColOffset } } # # Decrement maxScrlColOffset while the total width of the # non-hidden scrollable columns starting with this offset # is less than the width of the window's scrollable part # set scrlWindowWidth [getScrlWindowWidth $win] if {$scrlWindowWidth > 0} { while {$maxScrlColOffset > 0} { incr maxScrlColOffset -1 set scrlContentWidth \ [getScrlContentWidth $win $maxScrlColOffset $data(lastCol)] if {$scrlContentWidth == $scrlWindowWidth} { break } elseif {$scrlContentWidth > $scrlWindowWidth} { incr maxScrlColOffset break } } } return $maxScrlColOffset } #------------------------------------------------------------------------------ # tablelist::changeScrlColOffset # # Changes the scrolled column offset of the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::changeScrlColOffset {win scrlColOffset} { upvar ::tablelist::ns${win}::data data # # Make sure the offset is non-negative and no # greater than the max. scrolled column offset # if {$scrlColOffset < 0} { set scrlColOffset 0 } else { set maxScrlColOffset [getMaxScrlColOffset $win] if {$scrlColOffset > $maxScrlColOffset} { set scrlColOffset $maxScrlColOffset } } # # Update data(scrlColOffset) and adjust the # elided text in the tablelist's body if necessary # if {$scrlColOffset != $data(scrlColOffset)} { set data(scrlColOffset) $scrlColOffset adjustElidedText $win } } #------------------------------------------------------------------------------ # tablelist::scrlXOffsetToColOffset # # Returns the scrolled column offset of the tablelist widget win, corresponding # to the desired x offset. #------------------------------------------------------------------------------ proc tablelist::scrlXOffsetToColOffset {win scrlXOffset} { upvar ::tablelist::ns${win}::data data set scrlColOffset 0 set scrlContentWidth 0 for {set col $data(-titlecolumns)} {$col < $data(colCount)} {incr col} { if {$data($col-hide)} { continue } incr scrlContentWidth [getColWidth $win $col] if {$scrlContentWidth > $scrlXOffset} { break } else { incr scrlColOffset } } return $scrlColOffset } #------------------------------------------------------------------------------ # tablelist::scrlColOffsetToXOffset # # Returns the x offset corresponding to the specified scrolled column offset of # the tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::scrlColOffsetToXOffset {win scrlColOffset} { upvar ::tablelist::ns${win}::data data set scrlXOffset 0 set nonHiddenCount 0 for {set col $data(-titlecolumns)} {$col < $data(colCount)} {incr col} { if {$data($col-hide)} { continue } if {[incr nonHiddenCount] > $scrlColOffset} { break } else { incr scrlXOffset [getColWidth $win $col] } } return $scrlXOffset } #------------------------------------------------------------------------------ # tablelist::getNonHiddenRowCount # # Returns the number of non-hidden rows of the tablelist widget win in the # specified range. #------------------------------------------------------------------------------ proc tablelist::getNonHiddenRowCount {win first last} { upvar ::tablelist::ns${win}::data data if {$data(hiddenRowCount) == 0} { return [expr {$last - $first + 1}] } else { set count 0 for {set row $first} {$row <= $last} {incr row} { set key [lindex [lindex $data(itemList) $row] end] if {![info exists data($key-hide)]} { incr count } } } return $count } #------------------------------------------------------------------------------ # tablelist::nonHiddenRowOffsetToRowIndex # # Returns the row index corresponding to the given non-hidden row offset in the # tablelist widget win. #------------------------------------------------------------------------------ proc tablelist::nonHiddenRowOffsetToRowIndex {win offset} { upvar ::tablelist::ns${win}::data data if {$data(hiddenRowCount) == 0} { return $offset } else { # # Rebuild the list data(nonHiddenRowList) of the row # indices indicating the non-hidden rows if needed # if {[lindex $data(nonHiddenRowList) 0] == -1} { set data(nonHiddenRowList) {} for {set row 0} {$row < $data(itemCount)} {incr row} { set key [lindex [lindex $data(itemList) $row] end] if {![info exists data($key-hide)]} { lappend data(nonHiddenRowList) $row } } } set nonHiddenCount [llength $data(nonHiddenRowList)] if {$nonHiddenCount == 0} { return 0 } else { if {$offset >= $nonHiddenCount} { set offset [expr {$nonHiddenCount - 1}] } if {$offset < 0} { set offset 0 } return [lindex $data(nonHiddenRowList) $offset] } } }