Text Widget Newline Wrapping

Vaclav Snajdr posted to comp.lang.tcl:

Hi, to fill out correctly formulars with fixed number of lines and character per line I want use the widget text. I declare text .t -font 10x20 -width 50 -height 5 so the number of characters per line is controlled (skip after 50 to the next line) but not the number of lines not (more than 5 lines are editable).

Question: there is an another atribute for it or must it be controlled by a method? Thanks

GPS: You could try overloading insert and delete to only allow line index insertion below 4.0...

GPS: Oct 14, 2003 - NEW AND IMPROVED The original version of the code on this page became broken somehow, so I rewrote it.

 proc split.string.every {s n} {
  set r [list]
  set sLen [string length $s]
  for {set i 0} {$i < $sLen} {incr i $n} {
   lappend r [string range $s $i [expr {$i + $n - 1}]]
  return $r

 set ::wrapLen 20

 proc newline.wrapping.text.instance {win args} {
  set subCmd [lindex $args 0]

  if {"insert" == $subCmd} {
   set i [_$win index [lindex $args 1]]
   set charCount [lindex [split $i "."] 1]
   set txt [lindex $args 2]

   if {([string length $txt] + $charCount) > $::wrapLen} {
     set segEnd [expr {$::wrapLen - $charCount}]
     uplevel [list _$win insert $i [string range $txt 0 $segEnd]\n]
     foreach s [split.string.every \
       [string range $txt [expr {$segEnd + 1}] end] $::wrapLen] {
       uplevel [list _$win insert end $s\n]
   } else {
    uplevel [linsert $args 0 _$win]
  } else {
   uplevel [linsert $args 0 _$win]

 proc newline.wrapping.text {win args} {
  text $win
  rename $win _$win 
  interp alias {} $win {} newline.wrapping.text.instance $win

  $win insert end "This is a long line that should wrap into multiple lines."
  return $win

 pack [newline.wrapping.text .t]

More advanced wrapping is left as an exercise for the reader.

GPS answered a different question in c.l.t: how does one calculate the number of displayed lines in a text widget, as distinguished from the number of logical (\n-separated) lines:

    set start [.t index @0,0]
    set end [.t index @0,[winfo height .t]]  
    expr $end - $start + 1 ...

This returns the number of logical (\n-separated) lines currently displayed, which is not the same as the number of display lines on screen (which is very hard to calculate if font sizes differ). But, with tip 155:

    expr [.t count -displaylines @0,0 @0,[winfo height .t]] + 1

does the job.