namespace eval AdjustTabs { variable counter 0 } proc ::AdjustTabs::checkTabEvent {win} { set index [$win index insert] let {line col} [split $index .] set tabTag [getTabTagAt $win insert] set tabOccurs [winHasTabsAtLine $win $line] if {!$tabOccurs && ($tabTag eq "")} { # no tabs, nothing to do return } elseif {$tabTag eq ""} { # new tab set set tabTags [setTabTagAt $win $index] } elseif {!$tabOccurs} { # existing tab deleted set tabTags [removeTabTagAt $win $index] } else { # existing tab area changed set tabTags $tabTag } foreach tag $tabTags { set code [namespace code "adjustWinTagTabs $win $tag"] after cancel $code after idle $code } } # # ::AdjustTabs::removeTabTagAt # remove tags tab and tab[0-9]+ at $index # if inside, split area tabi to tabi + tabj # return name(s) of affected tags # proc ::AdjustTabs::removeTabTagAt {win index} { set thisTabTag [getTabTagAt $win $index] if {$thisTabTag eq ""} { return } set fromIndex [$win index "$index linestart"] set toIndex [$win index $fromIndex+1lines] $win tag remove tab $fromIndex $toIndex if {[$win compare $fromIndex > 1.0]} { set aboveTabTag [getTabTagAt $win $fromIndex-1lines] } else { set aboveTabTag "" } set belowTabTag [getTabTagAt $win $toIndex] if {$aboveTabTag eq "" && $belowTabTag eq ""} { # no neighbour tab area $win tag remove $thisTabTag $fromIndex $toIndex return } elseif {$belowTabTag eq ""} { # reduce tag area above $win tag remove $aboveTabTag $fromIndex $toIndex return $aboveTabTag } elseif {$aboveTabTag eq ""} { # reduce tag area below $win tag remove $belowTabTag $fromIndex $toIndex return $belowTabTag } else { # split tag area let {fromAround toAround} [$win tag prevrange $aboveTabTag $index] $win tag remove $aboveTabTag $fromIndex $toAround set newTabTag [genTagName $win] lappend result $newTabTag $win tag add $newTabTag $toIndex $toAround # return this value list $aboveTabTag $newTabTag } } # # ::AdjustTabs::setTabTagAt # set tag tab[0-9]+ at $index # return tag name # proc ::AdjustTabs::setTabTagAt {win index} { if {[getTabTagAt $win $index] ne ""} { return } set fromIndex [$win index "$index linestart"] set toIndex [$win index $fromIndex+1lines] $win tag add tab $fromIndex $toIndex if {[$win compare $fromIndex > 1.0]} { set aboveTabTag [getTabTagAt $win $fromIndex-1lines] } else { set aboveTabTag "" } set belowTabTag [getTabTagAt $win $toIndex] if {$aboveTabTag eq "" && $belowTabTag eq ""} { # no neighbour tab area set thisTabTag [genTagName $win] $win tag add $thisTabTag $fromIndex $toIndex return $thisTabTag } elseif {$belowTabTag eq ""} { # extend above tab area $win tag add $aboveTabTag $fromIndex $toIndex return $aboveTabTag } elseif {$aboveTabTag eq ""} { # extend below tab area $win tag add $belowTabTag $fromIndex $toIndex return $belowTabTag } else { # join tab area below and above let {belowFrom belowTo} [$win tag ranges $belowTabTag] $win tag remove $belowTabTag 1.0 end $win tag add $aboveTabTag $fromIndex $belowTo return $aboveTabTag } } # # ::AdjustTabs::initTabTags # remove all tags tab[0-9]+ # set tag tab on lines containing \t # set new tags tab[0-9]+ # return list of set tags tab[0-9]+ # proc ::AdjustTabs::initTabTags {win} { $win tag remove tab 1.0 end foreach tag [$win tag names] { if {[regexp {^tab[0-9]+$} $tag]} { $win tag remove $tag 1.0 end } } set from 1.0 set to 2.0 while {[$win compare $from < end]} { if {[$win search \t $from $to] ne ""} { $win tag add tab $from $to } set from [$win index $from+1lines] set to [$win index $to+1lines] } set result {} foreach {from to} [$win tag ranges tab] { set tabTag [genTagName $win] $win tag add $tabTag $from $to lappend result $tabTag after idle\ [namespace code\ [list atjustWinTagTabsBackground $win $tabTag]] } raise [winfo toplevel .] set result } # # ::AdjustTabs::genTagName # return tag name tab[0-9]+ which is new to $win # proc ::AdjustTabs::genTagName {win} { variable counter while {[lsearch [$win tag names] tab$counter] >= 0} { incr counter } return tab$counter } # # ::AdjustTabs::getTabTagAt # return first tag tab[0-9]+ occuring at $index # proc ::AdjustTabs::getTabTagAt {win index} { foreach tag [$win tag names $index] { if {[regexp {^tab[0-9]+$} $tag]} { return $tag } } } # # ::AdjustTabs::winHasTabsAtLine # return true if \t occurs in $line # proc ::AdjustTabs::winHasTabsAtLine {win line} { expr {[$win search \t $line.0 $line.end] ne ""} } # # ::AdjustTabs::adjustWinTabAt # adjust tab tag which occurs in $window at $index # proc ::AdjustTabs::adjustWinTabAt {window index} { set tabTag [getTabTagAt $window $index] adjustWinTagTabs $window $tabTag } # # ::AdjustTabs::atjustWinTagTabsBackground # # proc ::AdjustTabs::atjustWinTagTabsBackground {win tag} { if {![winfo exists $win] || [$win tag ranges $tag] eq ""} { return } while {true} { let {from to} [$win tag ranges $tag] let {fromLine fromCol} [split $from .] let {toLine toCol} [split $to .] set someLinesVisible 0 set allLinesVisible 1 foreach line [range $fromLine $toLine] { if {[$win dlineinfo $line.0] eq ""} { set allLinesVisible 0 } else { set someLinesVisible 1 } } if {$allLinesVisible} { adjustWinTagTabs $win $tag break } elseif {$someLinesVisible} { adjustWinTagTabs $win $tag sleep 200 } else { sleep 500 } } list tabstops of $tag in $win are adjusted } # # ::AdjustTabs::adjustWinTagTabs # adjust tabs in range of tag $tabTab # such that no cols overlap each other # proc ::AdjustTabs::adjustWinTagTabs {tabWindow tabTag {distance 12}} { set ranges [$tabWindow tag ranges $tabTag] if {$ranges eq {}} { return } set lineNrs {} foreach {from to} [$tabWindow tag ranges $tabTag] { set startIndex [$tabWindow index "$from linestart"] let {startLine startCol} [split $startIndex .] while {[$tabWindow compare $startLine.0 < $to]} { lappend lineNrs $startLine incr startLine } } if {[llength $lineNrs] <= 1} { $tabWindow tag configure $tabTag -tabs {} return } set cols "" foreach nr $lineNrs { set tabs [numOfTabsAtLine $tabWindow $nr] if {$cols eq ""} { set cols $tabs } elseif {($tabs ne "" && $tabs > $cols)} { set cols $tabs } } if {$cols eq ""} { return } set colWidths {} for {set i 0} {$i < $cols} {incr i} { set currentWidth [winTagColWidth $tabWindow $tabTag $i] if {$currentWidth eq ""} { break } incr currentWidth $distance lappend colWidths $currentWidth } set tabstops {} set tabstop 0 foreach colWidth $colWidths { lappend tabstops [incr tabstop $colWidth] } $tabWindow tag configure $tabTag -tabs $tabstops } # # ::AdjustTabs::winTagColWidth # return really needed width inside tag $tabTag on col $colNr # proc ::AdjustTabs::winTagColWidth {tabWindow tabTag colNr} { set result "" foreach {from to} [$tabWindow tag ranges $tabTag] { set startIndex [$tabWindow index "$from linestart"] let {startLine startCol} [split $startIndex .] while {[$tabWindow compare $startLine.0 < $to]} { set colWidth [calcLineColWidth $tabWindow $startLine $colNr] if {$colWidth ne ""} { if {$result eq "" || $colWidth > $result} { set result $colWidth } } incr startLine } } set result } # # ::AdjustTabs::calcLineColWidth # return really needed width in line $line at tabstop $colNr # proc ::AdjustTabs::calcLineColWidth {window line colNr} { if {$colNr == 0} { let {x y w h} [$window bbox $line.0] if {![info exists x]} { return } set leftEdge $x } else { set leftEdge [calcTabRightEdge $window $line [expr {$colNr-1}]] } if {$leftEdge eq ""} { return } set rightEdge [calcTabLeftEdge $window $line $colNr] if {$rightEdge ne ""} { expr {$rightEdge - $leftEdge} } } # # ::AdjustTabs::calcTabLeftEdge # return left edge of tab space in text window # proc ::AdjustTabs::calcTabLeftEdge {window line tabNr} { set index [calcTabIndex $window $line $tabNr] if {$index ne ""} { let {x y w h} [$window bbox $index] if {[info exists x]} { set x } } elseif {[numOfTabsAtLine $window $line] == $tabNr} { let {x y w h} [$window bbox $line.end] if {[info exists x]} { set x } } } # # ::AdjustTabs::calcTabRightEdge # return right edge of tab space in text window # proc ::AdjustTabs::calcTabRightEdge {window line tabNr} { set index [calcTabIndex $window $line $tabNr] if {$index ne ""} { let {x y w h} [$window bbox $index] if {[info exists x]} { expr {$x + $w} } } } # # ::AdjustTabs::calcTabIndex # return widget index of tab $tabNr in line $line # proc ::AdjustTabs::calcTabIndex {window line tabNr {start 0}} { set result\ [$window search \t "$line.0+$start chars" $line.0+1lines] if {$tabNr == 0 || $result eq ""} { set result } else { let {row col} [split $result .] calcTabIndex $window $line [expr {$tabNr-1}] [incr col] } } # # ::AdjustTabs::numOfTabsAtLine # return number of tabs contained in line # proc ::AdjustTabs::numOfTabsAtLine {window line} { set str1 [$window get $line.0 $line.end] set str2 [string map [list \t {}] $str1] set l1 [string length $str1] set l2 [string length $str2] expr {$l1 - $l2} } namespace eval AdjustTabs { namespace export\ checkTabEvent\ initTabTags } namespace import -force ::AdjustTabs::*