Balancing Open and Close Quotes in a TK text widget.

WJG (04/11/07) This week I've spent too many hours using OpenOffice correcting simple single and double quotes in page after page of text files created in my Tk based text editor. Pity I didn't sit down and complete the following proc and use it some time ago. If anyone has any suggestion please include them but please don't change the original code -just add comments to the end of the file.

 #---------------
 # balanceQuotes.tcl
 #---------------
 # William J Giddings
 # 04/11/07
 #---------------
 # Enable context sensitive quotations.
 # If the input quote mark is preceded by a printable character,
 # then closequote otherwise openquote!
 # 
 #---------------
 # Notes:
 # This is not "smart" and so doesn't attempt to balance quote marks.
 #---------------

 #!/bin/sh \
 exec tclsh  "$0" "$@"

 package require Tk

 #---------------
 # w    text widget into which the substition is to be made
 # c    character currently inserted
 #---------------
 proc balanceQuotes {w c} {

    #get the preceding character
    set idx [$w index insert]
    set str [$w get "$idx -1char" $idx]

    # if null is returned, it must be the start of a line
    # so make it whitespace
    if {$str == "" } {set str " "}
    
    # check for character types
    # the character class print is not used as <space> is printable!
    if { [string is wordchar $str] || [string is punct $str] } {       

        # check if the previous character is an open quote.
        # nested open quotations need to be allowed for
        if { $str == "\u2018" || $str == "\u201C" } {
            set i 0
        } else  {
            set i 1
        }

        if {$c == "apostrophe"} {
            if {$i} {
                $w insert insert \u2019    ;# single close
            } else  {
                $w insert insert \u2018    ;# single open
            }
        } else  {
            # must be quotedl
            if {$i} {
                $w insert insert \u201D    ;# double close
            } else  {
                $w insert insert \u201C    ;# double open
            }
        }

    } else  {
        if {$c == "apostrophe"} {
            $w insert insert \u2018    ;# single open
        } else  {
            # must be quotedl
            $w insert insert \u201C    ;# double open
        }
    }
 }

 #---------------
 # Demo
 #---------------
 set txt [text .txt -font {Sans 15}]
 pack $txt

 bind $txt <Key-apostrophe> { balanceQuotes %W %K ; break }
 bind $txt <Key-quotedbl> { balanceQuotes %W %K ; break }

MG gets an error about "apostrophe" not being a valid keysym; I think maybe it should be "quoteright"?

WJG (05/11/07) It all works fine on my machine.

MG That's weird; I thought the keysyms were the same across all platforms. What system are you using? (Mine is Tcl 8.4.9 / 8.5a1 on Win XP SP2)

WJG (06/11/07) Suse 10.1, Tcl 8.4.12. My code is fine. Does your system generate a "quoteright" keysym when the error occurs? I can't find that listed in my docs. Try the following binding on a text widget to check what keysym your system generates.

 console show
 set txt [text .txt]
 pack $txt
 bind $txt <Key> { puts %K }

MG Sorry; I answered this before, but it seems to have disappeared. Yeah, for me the apostrophe key generates a "quoteright" keysym, and that's what's listed in the keysym helpfile I have (the Win .chm from ActiveTcl 8.4.9).

LV Yea, there was data loss last week and my comments were lost as well. I ran the above code against the hummingbird Windows x server, and I got

' => apostrophe
` => grave

WJG (16/11/07) Naturally, just modify the codes to suit. I guess some platform checking could be put in there but perhaps this issue one that Tcl Core guys can resolve in order to maintain cross-platform compatibility.