Load, save cut and paste tag formatted text widget contents.
Available at:
http://www1.clearlight.com/~oakley/tcl/ttd/index.html
Archived at: [L1 ]
APN 2016-10-29 The above link is broken so below is a copy of some version of that code.
# Tcl/Tk text dump # # Copyright (c) 1999, Bryan Douglas Oakley # All Rights Reserved. # # This code is provide as-is, with no warranty expressed or implied. Use # at your own risk. # # package provide ttd 1.0 namespace eval ::ttd { # this is the public interface namespace export get insert variable code variable ttdVersion {} variable taglist variable safeInterpreter } proc ::ttd::get {args} { set argc [llength $args] if {$argc == 0} { error "wrong \# args: must be ::ttd::get pathName ?index1? ?index2?" } set w [lindex $args 0] if {[winfo class $w] != "Text"} { error "\"$w\" is not a text widget" } if {$argc == 1} { set index1 "1.0" # one might think we want "end -1c" here, but if we do that # we end up losing some tagoff directives. We'll remove the # trailing space later. set index2 "end" } elseif {$argc == 2} { set index1 [lindex $args 1] set index2 "[$w index {$index1 + 1c}]" } else { set index1 [lindex $args 1] set index2 [lindex $args 2] } set tagData {} set imageData {} set header "# -*- tcl -*-\n#\n\n" set version [list ttd.version 1.0] set result [list ] # we use these arrays to keep track of actual images, tags # and windows (though, not really windows...) catch {unset tags} catch {unset images} catch {unset windows} foreach {key value index} [$w dump $index1 $index2] { switch -exact -- $key { tagon { lappend result [list ttd.tagon $value] if {![info exists tags($value)]} { # we need to steal all of the configuration data set tagname $value set tags($tagname) {} foreach item [$w tag configure $tagname] { set value [lindex $item 4] if {[string length $value] > 0} { set option [lindex $item 0] lappend tags($tagname) $option $value } } } } tagoff { lappend result [list ttd.tagoff $value] } text { lappend result [list ttd.text $value] } mark { # bah! marks aren't interesting, are they? # lappend result [list ttd.mark $value] } image { # $value is an internal identifier. We need the actual # image name so we can grab its data... set imagename [$w image cget $value -image] set image [list ttd.image] # this gets all of the options for this image # at this location (such as -align, etc) foreach item [$w image configure $value] { set value [lindex $item 4] if {[string length $value] != 0} { set option [lindex $item 0] lappend image $option $value } } lappend result $image # if we don't yet have a definition for this # image, grab it if {[string length $imagename] > 0 \ && ![info exists images($imagename)]} { # we need to steal all of the configuration data set images($imagename) $imagename foreach item [$imagename configure] { set value [lindex $item 4] if {[string length $imagename] > 0} { set option [lindex $item 0] lappend images($imagename) $option $value } } } } window { set window [list ttd.window $value] foreach item [$w window configure $index] { set value [lindex $item 4] if {[string length $value] != 0} { set option [lindex $item 0] lappend window $option $value } } lappend result $window } } } # process tags in priority order; ignore tags that aren't used set tagData {} foreach tag [$w tag names] { if {[info exists tags($tag)]} { lappend tagData [concat ttd.tagdef $tag $tags($tag)] } } set imageData {} foreach image [array names images] { lappend imageData [concat ttd.imgdef $images($image)] } # remove the trailing newline that the text widget added # for us set result [lreplace $result end end] set tmp $header append tmp "$version\n\n" append tmp "[join $tagData \n]\n\n" append tmp "[join $imageData \n]\n\n" append tmp "[join $result \n]\n" return $tmp } proc ::ttd::insert {w ttd} { variable ttdVersion {} variable taglist variable safeInterpreter variable ttdCode # create a safe interpreter, if we haven't already done so catch {interp delete $safeInterpreter } set safeInterpreter [interp create -safe] # we want the widget command to be available to the # safe interpreter. Also, the text may include embedded # images, so we need the image command available as well. interp alias $safeInterpreter masterTextWidget {} $w interp alias $safeInterpreter image {} image # interp alias $safeInterpreter puts {} puts # this defines the commands we use to parse the data $safeInterpreter eval $ttdCode # this processes the data. Alert the user if there was # a problem. if {[catch {$safeInterpreter eval $ttd} error]} { set message "Error opening file:\n\n$error" tk_messageBox -icon info \ -message $message \ -title "Bad file" \ -type ok } # and clean up after ourselves interp delete $safeInterpreter } # this code defines the commands which are embedded in the ttd # data. It should only executed in a safe interpreter. set ::ttd::ttdCode { set taglist "" set command "" set ttdVersion "" proc ttd.version {version} { global ttdVersion set ttdVersion $version } proc ttd.window {args} { # not supported yet error "embedded windows aren't supported in this version" } proc ttd.image {args} { global taglist set index [masterTextWidget index insert] eval masterTextWidget image create $index $args # we want the current tags associated with the image... # (I wonder why I can't supply tags at the time I create # the image, like I can when I insert text?) foreach tag $taglist { masterTextWidget tag add $tag $index } } proc ttd.imgdef {name args} { eval image create photo $name $args } proc ttd.tagdef {name args} { eval masterTextWidget tag configure $name $args } proc ttd.text {string} { global taglist masterTextWidget insert insert $string $taglist } proc ttd.tagon {tag} { global taglist # I'm confused by this, but we need to keep track of our # tags in reverse order. set taglist [concat $tag $taglist] } proc ttd.tagoff {tag} { global taglist set i [lsearch -exact $taglist $tag] if {$i >= 0} { set taglist [lreplace $taglist $i $i] } masterTextWidget tag remove $tag insert } proc ttd.mark {name} { masterTextWidget mark set $name [masterTextWidget index insert] } }