---- [http://mini.net/files/htext.jpg] set docu(htext) { [Richard Suchenwirth] 2001-07-20 - Here's an update to a simple hypertext system that you might use for online help. It exports a single command: htext::htext (widget) ?title? brings up a toplevel showing the specified page (or an alphabetic index of titles, if not specified). Thus you can use it for context-sensitive help. You create help pages by just assigning to the global [[::docu]] array. Links are displayed underlined and blue (or purple if they have been visited before), and change the cursor to a pointing hand. Clicking on a link of course brings up that page. In addition, you get "Index", "Search" (case-insensitive regexp in titles and full text), "History", and "Back" links at the bottom of pages. In a nutshell, you get a tiny browser, an information server, and a search engine ;-) See also [[htext format]]. } set {docu(htext format)} { The [htext] hypertext pages stored in the [[::docu]] array are in a subset of Wiki format: * indented lines come in fixed font without evaluation; * blank lines break paragraphs * all lines without leading blanks are displayed without explicit linebreak (but possibly word-wrapped) * a link is the title of another page in brackets (see examples at end). } set docu(::docu) { This global array is used for storing [htext] pages. The advantage is that source files can be documented just by assigning to ::docu fields, without creating a dependency on [htext]. After creating a [htext] widget, all ''docu'' documentation is instantly available. If you wish to have spaces in title, brace the whole thing: set {docu(An example)} {...} } package require msgcat namespace eval htext { namespace export htext variable history {} seen {} proc htext {w args} { variable historyLabel variable searchLabel variable indexLabel variable backLabel if ![winfo exists $w] { wm title [toplevel $w] Help text $w.t -border 5 -relief flat -wrap word \ -state disabled -font {Times 9} pack $w.t -fill both -expand 1 set w $w.t } if ![info exists historyLabel] { set historyLabel [msgcat::mc "History"] set searchLabel [msgcat::mc "Search"] set indexLabel [msgcat::mc "Index"] set backLabel [msgcat::mc "Back"] } $w tag config centered -justify center $w tag config link -foreground blue -underline 1 $w tag config seen -foreground purple4 -underline 1 $w tag bind link "$w config -cursor hand2" $w tag bind link "$w config -cursor {}" $w tag bind link <1> "[namespace current]::click $w %x %y" $w tag config hdr -font {Times 16} $w tag config fix -font {Courier 9} raise $w if ![llength [array names ::docu $args]] {set args Index} show $w $args } proc click {w x y} { variable historyLabel variable searchLabel variable indexLabel variable backLabel set range [$w tag prevrange link [$w index @$x,$y]] set link [eval $w get $range] if {[string equal $link $historyLabel]} { set link History } elseif {[string equal $link $searchLabel]} { set link Search } elseif {[string equal $link $indexLabel]} { set link Index } elseif {[string equal $link $backLabel]} { set link Back } if [llength $range] {show $w $link} } proc back w { variable history set l [llength $history] set last [lindex $history [expr $l-2]] set history [lrange $history 0 [expr $l-3]] show $w $last } proc listpage {w list} { foreach i $list {$w insert end \n; showlink $w $i} } proc search w { $w insert end "\n" {} [msgcat::mc "Search phrase:"] {} " " {} entry $w.e -textvar [namespace current]::search $w window create end -window $w.e focus $w.e $w.e select range 0 end bind $w.e "htext::dosearch $w" button $w.b -text [msgcat::mc "Search!"] -command "htext::dosearch $w" -pady 0 $w window create end -window $w.b } proc dosearch w { variable search $w config -state normal $w insert end "\n\n" {} [msgcat::mc "Search results for '%s':" $search] {} \n {} foreach i [lsort [array names ::docu]] { if [regexp -nocase $search $i] { $w insert end \n; showlink $w $i ;# found in title } elseif [regexp -nocase -indices -- $search $::docu($i) pos] { regsub -all \n [string range $::docu($i) \ [expr [lindex $pos 0]-20] [expr [lindex $pos 1]+20]] \ " " context $w insert end \n showlink $w $i $w insert end " - ...$context..." } } $w config -state disabled } proc showlink {w link} { if {[regexp "^image\\://(.*)\$" $link "" imgname]} { set end0 [$w index end] $w insert end "\n" $w image create end -image $imgname $w insert end "\n" $w tag add centered $end0 end-1c return } variable seen set tag link if {[lsearch -exact $seen $link]>-1} { lappend tag seen } else {lappend seen $link} if {[string equal $link History]} { set link $historyLabel } elseif {[string equal $link Search]} { set link $searchLabel } elseif {[string equal $link Index]} { set link $indexLabel } elseif {[string equal $link Back]} { set link $backLabel } $w insert end $link $tag } proc show {w title} { variable historyLabel variable searchLabel variable indexLabel variable backLabel variable history $w config -state normal $w delete 1.0 end $w insert end $title hdr \n switch -- $title { Back {back $w; return} History {listpage $w $history} Index {listpage $w [lsort -dic [array names ::docu]]} Search {search $w} default { if {![info exists ::docu($title)]} { $w insert end [msgcat::mc "This page was referenced but not written yet."] } else { set var 1 foreach i [split $::docu($title) \n] { if [regexp {^[ \t]+} $i] { if $var {$w insert end \n\n; set var 0} $w insert end $i\n fix continue } set i [string trim $i] if ![string length $i] {$w insert end \n\n; continue} if !$var {$w insert end \n} set var 1 while {[regexp {([^[]*)[[]([^]]+)[]](.*)} $i \ -> before link after]} { $w insert end "$before " {} showlink $w $link set i $after } $w insert end "$i " } } } } $w insert end \n------\n {} $indexLabel link " - " {} $searchLabel link if [llength $history] { $w insert end " - " {} $historyLabel link " - " {} $backLabel link } $w insert end \n lappend history $title $w config -state disabled } } ;# end namespace htext if {[file tail [info script]]==[file tail $argv0]} { htext::htext .h htext wm withdraw . } ---- The following procedure will load all files that end with one of the extensions .hlp, .htp or .txt and populate the docu array used by the help system above. I use it to keep all the files describing my applications in a single directory. While this has the drawback of separating the help text from the applications themselves, it allows for better and easier editing from your favorite editor. [EF] proc loadhelp { dir { f_ptn {*.{hlp,htp,txt}}} } { global docu foreach fname [glob -nocomplain -tails -directory $dir $f_ptn] { if { [catch {open [file join $dir $fname]} fd] == 0 } { set idx [file rootname $fname] set docu(${idx}) [read $fd] close $fd } } } ---- Implemented with Tk's versatile [text] widget. ---- [Wojciech Kocjan] I have put an image support - putting [[image://anyphotoexistingintk]] shows an image. I have also converted htext package to use msgcat. This is very useful for people like me who would like to supply documentation that looks Polish. ---- Here's how to plug htext into the menu of a [console] (tested on both Win XP and [eTcl]/[PocketPC]): console eval {.menubar.help add command -label Help \ -command {consoleinterp eval {htext::htext .h}}} ---- See also [htext as eTcl plugin] ---- [Category Widget]