Updated 2017-10-03 09:15:22 by JOB

JOB 17-02-11

Yes, Tkhtml 3.0 is still alive !

Beside some other packages, you need the html3widget - A TclOO Tkhtml 3.0 megawidget - example of how to render html+css which is a megawidget implementation based on Tkhtml 3.0.

Attributes  edit

name
HelpViewer Application
The complete package can be downloaded from here
http://www.johann-oberdorfer.eu/blog/2017/04/10/17-10-04_helpviewer/
latest release
3.0.2
release time
2017-04
contact
JOB

Recent changes:

Here is the code (although I would suggest to use the link stated above, where the source most likely is up-to-date):

  • helpviewer.tcl
# -------------------------------------------------------------------------
# helpviewer.tcl
# -------------------------------------------------------------------------
# (c) 2016, Johann Oberdorfer - Engineering Support | CAD | Software
#     johann.oberdorfer [at] googlemail.com
#     www.johann-oberdorfer.eu
# -------------------------------------------------------------------------
# This source file is distributed under the BSD license.
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#   See the BSD License for more details.
# 
# Credits:
#  This software related on the the helpviewer application originally
#  created by Ramon Ribó (RAMSAN) ramsan@cimne.upc.es.
#  (http://gid.cimne.upc.es/ramsan) for his version of the 
#  Thank you.
#
# -------------------------------------------------------------------------
# -------------------------------------------------------------------------

package provide helpviewer 3.0


# lappend auto_path [file dirname [info script]]

set dir [file dirname [info script]]
lappend auto_path [file join $dir "hvimages"]

# requiring exactly 2.0 to avoid getting the one from Activestate
package require -exact Tkhtml 3.0

package require -exact BWidget 1.9.10
package require BWidget_patch
        Widget::theme 1

package require fileutil
package require hvimages
package require html3widget


# package is optional
catch {        package require Pan }


if { [info command tkTabToWindow] == "" } {
        proc tkTabToWindow {w} {
                focus $w
                after 100 {
                        set w [focus]
                        if {[string equal [winfo class $w] Entry]} {
                                $w selection range 0 end
                                $w icursor end
                        }
                }
        }
}

# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------

namespace eval HelpViewer {
        
        variable HelpBaseDir
        variable LastFileList
        
        # These images are used in place of GIFs or of form elements
        #
        if { [lsearch [image names] smgray] == -1 } {
                
                image create photo smgray -data {
                        R0lGODdhOAAYAPAAALi4uAAAACwAAAAAOAAYAAACI4SPqcvtD6OctNqLs968+w+G4kiW5omm
                        6sq27gvH8kzX9m0VADv/
                }
                image create photo nogifbig -data {
                        R0lGODdhJAAkAPEAAACQkADQ0PgAAAAAACwAAAAAJAAkAAACmISPqcsQD6OcdJqKM71PeK15
                        AsSJH0iZY1CqqKSurfsGsex08XuTuU7L9HywHWZILAaVJssvgoREk5PolFo1XrHZ29IZ8oo0
                        HKEYVDYbyc/jFhz2otvdcyZdF68qeKh2DZd3AtS0QWcDSDgWKJXY+MXS9qY4+JA2+Vho+YPp
                        FzSjiTIEWslDQ1rDhPOY2sXVOgeb2kBbu1AAADv/
                }
                image create photo nogifsm -data {
                        R0lGODdhEAAQAPEAAACQkADQ0PgAAAAAACwAAAAAEAAQAAACNISPacHtD4IQz80QJ60as25d
                        3idKZdR0IIOm2ta0Lhw/Lz2S1JqvK8ozbTKlEIVYceWSjwIAO///
                }
        }
        
        if { [lsearch [image names] appbook16] == -1 } {
                
                image create photo appbook16 -data {
                        R0lGODlhEAAQAIQAAPwCBAQCBDyKhDSChGSinFSWlEySjCx+fHSqrGSipESO
                        jCR6dKTGxISytIy6vFSalBxydAQeHHyurAxubARmZCR+fBx2dDyKjPz+/MzK
                        zLTS1IyOjAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAVkICCOZGmK
                        QXCWqTCoa0oUxnDAZIrsSaEMCxwgwGggHI3E47eA4AKRogQxcy0mFFhgEW3M
                        CoOKBZsdUrhFxSUMyT7P3bAlhcnk4BoHvb4RBuABGHwpJn+BGX1CLAGJKzmK
                        jpF+IQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0K
                        qSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpo
                        dHRwOi8vd3d3LmRldmVsY29yLmNvbQA7
                }
                image create photo appbookopen16 -data {
                        R0lGODlhEAAQAIUAAPwCBAQCBExCNGSenHRmVCwqJPTq1GxeTHRqXPz+/Dwy
                        JPTq3Ny+lOzexPzy5HRuVFSWlNzClPTexIR2ZOzevPz29AxqbPz6/IR+ZDyK
                        jPTy5IyCZPz27ESOjJySfDSGhPTm1PTizJSKdDSChNzWxMS2nIR6ZKyijNzO
                        rOzWtIx+bLSifNTGrMy6lIx+ZCRWRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAae
                        QEAAQCwWBYJiYEAoGAFIw0E5QCScAIVikUgQqNargtFwdB9KSDhxiEjMiUlg
                        HlB3E48IpdKdLCxzEAQJFxUTblwJGH9zGQgVGhUbbhxdG4wBHQQaCwaTb10e
                        mB8EBiAhInp8CSKYIw8kDRSfDiUmJ4xCIxMoKSoRJRMrJyy5uhMtLisTLCQk
                        C8bHGBMj1daARgEjLyN03kPZc09FfkEAIf5oQ3JlYXRlZCBieSBCTVBUb0dJ
                        RiBQcm8gdmVyc2lvbiAyLjUNCqkgRGV2ZWxDb3IgMTk5NywxOTk4LiBBbGwg
                        cmlnaHRzIHJlc2VydmVkLg0KaHR0cDovL3d3dy5kZXZlbGNvci5jb20AOw==
                }
                image create photo filedocument16 -data {
                        R0lGODlhEAAQAIUAAPwCBFxaXNze3Ly2rJSWjPz+/Ozq7GxqbJyanPT29HRy
                        dMzOzDQyNIyKjERCROTi3Pz69PTy7Pzy7PTu5Ozm3LyqlJyWlJSSjJSOhOzi
                        1LyulPz27PTq3PTm1OzezLyqjIyKhJSKfOzaxPz29OzizLyidIyGdIyCdOTO
                        pLymhOzavOTStMTCtMS+rMS6pMSynMSulLyedAAAAAAAAAAAAAAAAAAAAAAA
                        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAaQ
                        QIAQECgajcNkQMBkDgKEQFK4LFgLhkMBIVUKroWEYlEgMLxbBKLQUBwc52Hg
                        AQ4LBo049atWQyIPA3pEdFcQEhMUFYNVagQWFxgZGoxfYRsTHB0eH5UJCJAY
                        ICEinUoPIxIcHCQkIiIllQYEGCEhJicoKYwPmiQeKisrKLFKLCwtLi8wHyUl
                        MYwM0tPUDH5BACH+aENyZWF0ZWQgYnkgQk1QVG9HSUYgUHJvIHZlcnNpb24g
                        Mi41DQqpIERldmVsQ29yIDE5OTcsMTk5OC4gQWxsIHJpZ2h0cyByZXNlcnZl
                        ZC4NCmh0dHA6Ly93d3cuZGV2ZWxjb3IuY29tADs=
                }
                image create photo viewmag16 -data {
                        R0lGODlhEAAQAIUAAPwCBCQmJDw+PAwODAQCBMza3NTm5MTW1HyChOTy9Mzq
                        7Kze5Kzm7OT29Oz6/Nzy9Lzu7JTW3GTCzLza3NTy9Nz29Ize7HTGzHzK1AwK
                        DMTq7Kzq9JTi7HTW5HzGzMzu9KzS1IzW5Iza5FTK1ESyvLTa3HTK1GzGzGzG
                        1DyqtIzK1AT+/AQGBATCxHRydMTCxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                        AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAQABAAAAZ8
                        QIAQEBAMhkikgFAwHAiC5FCASCQUCwYiKiU0HA9IRAIhSAcTSuXBsFwwk0wy
                        YNBANpyOxPMxIzMgCyEiHSMkGCV+SAQQJicoJCllUgBUECEeKhAIBCuUSxMK
                        IFArBIpJBCxmLQQuL6eUAFCusJSzr7GLArS5Q7O1tmZ+QQAh/mhDcmVhdGVk
                        IGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3
                        LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVs
                        Y29yLmNvbQA7
                }
                
        }
}


proc HelpViewer::GiveLastFile { w } {
        variable LastFileList
        set retval ""
        catch {
                set w [winfo toplevel $w]
                set retval $LastFileList($w)
        }
        return $retval
}
proc HelpViewer::EnterLastFile { w file } {
        variable LastFileList
        set w [winfo toplevel $w]
        set LastFileList($w) $file
}


proc HelpViewer::LoadRef { w new { enterinhistory 1 } } {
        global tcl_platform
        
        if { [regexp {http:/localhost/cgi-bin/man/man2html\?(\w+)\+(\w+)} $new {} sec word] } {
                SearchManHelpFor $w $word $sec
                return
        } elseif { $new != "" && [regexp {[a-zA-Z]+[a-zA-Z]:.*} $new] } {

                regexp {http:/.*} $new url
                
                if { [regexp {:/[^/]} $url] } {
                        regsub {:/} $url {://} url
                }
                
                if { $tcl_platform(platform) != "windows"} {
                        set comm [auto_execok konqueror]
                        if { $comm == "" } {
                                set comm [auto_execok netscape]
                        }
                        if { $comm == "" } {
                                tk_messageBox -icon warning -message \
                                                "Check url: $url in your web browser." -type ok
                        } else {
                                exec $comm $url &
                        }
                } else {

                        global env
                        if {[file exists [file join $env(ProgramFiles) "internet explorer" iexplore.exe]]} {
                                exec [file join $env(ProgramFiles) "internet explorer" iexplore.exe] $url &
                        } else  {
                                tk_messageBox -icon warning -message \
                                                "Check url: $url in your web browser." -type ok
                        }
                }
                return
        }
        
        if {$new!=""} {
                set LastFile [GiveLastFile $w]
                if { [string match \#* [file tail $new]] } {
                        set new $LastFile[file tail $new]
                }
                set pattern $LastFile#
                set len [string length $pattern]
                incr len -1
                if {[string range $new 0 $len]==$pattern} {
                        incr len
                        $w yview [string range $new $len end]
                        if { $enterinhistory } {
                                History::Add $new
                        }
                } elseif { [regexp {(.*)\#(.*)} $new {} file tag] } {
                        LoadFile $w $file $enterinhistory $tag
                } else {
                        LoadFile $w $new $enterinhistory
                }
        }
}

proc HelpViewer::Load { w } {
        global lastDir

        set filetypes {
                {{Html Files} {.html .htm}}
                {{All Files} *}
        }
        set f [tk_getOpenFile -initialdir $lastDir -filetypes $filetypes]
        
        if {$f!=""} {
                set lastDir [file dirname $f]
                LoadFile $w $f
        }
}

# Clear the screen.
#
proc HelpViewer::ClearHtmlWidget { } {
        variable hwidget

        # hiding the search widget automatically
        # clears all search tags in the html widget as well
        $hwidget hideSearchWidget
        $hwidget reset
}


proc HelpViewer::ReadFile {name} {
        #
        # read html file and try to detect charset
        # return value is a string holding all the html stuff
        #
        if { [file dirname $name] == "." } {
                set name [file tail $name]
        }
        if {[catch {open $name r} fp]} {
                tk_messageBox -icon error -message $fp -type ok
                return {}
        } else {
                fconfigure $fp -translation binary
                set r [read $fp [file size $name]]
                close $fp
                
                # attempt to detect charset="UTF-8" !

                if { [regexp {(?i)<meta\s+[^>]*charset=utf-8[^>]*>} $r] ||
                         [string first "charset=utf-8" [string tolower $r]] != -1 ||
                         [string first "charset=\"utf-8\"" [string tolower $r]] != -1  } {

                        set fp [open $name r]
                        fconfigure $fp -encoding utf-8
                        set r [read $fp]
                        close $fp
                }

                return $r
        }
}

# Load a file into the HTML widget
#
proc HelpViewer::LoadFile {hwidget name { enterinhistory 1 } { tag "" } } {
        variable HelpBaseDir
        global HelpPriv

        if { $name == "" } { return }
        
        if { [file isdir $name] } {
                # name is a directory
                set HelpBaseDir $name
        
                set files [glob -nocomplain -dir $name *]
                set ipos [lsearch -regexp $files {(?i)(index|contents|_toc)\.(htm|html)}]
                if { $ipos != -1 } {
                        set name [lindex $files $ipos]
                } else {
                        return
                }
        } else {
                # name refers to a file
                set HelpBaseDir [file dirname $name]
        }

        set html [ReadFile $name]

        if {$html == ""} {
                return
        }

        ClearHtmlWidget
        EnterLastFile $hwidget $name
        
        if { $enterinhistory } {
                if { $tag == "" } {
                        History::Add $name
                } else {
                        History::Add $name#$tag
                }
        }
        
        $hwidget setbasedir $HelpBaseDir
        $hwidget parse -final $html

        if { $tag != "" } {
                update idletasks
                $hwidget yview $tag
        }

        TryToSelect $name
        variable SearchPos
        set SearchPos ""
}


# Refresh the current file.
#
proc HelpViewer::Refresh {hwidget args} {
        set LastFile [GiveLastFile $hwidget]
        if {![info exists LastFile] || ![winfo exists $hwidget] } return
        LoadFile $hwidget $LastFile 0
}

proc HelpViewer::ResolveUri { args } {
        return [file join [file dirname [lindex $args 0]] [lindex $args 1]]
}


proc HelpViewer::FillDir { tree node } {
        variable HelpBaseDir
        
        if { $node == "root" } {
                set dir $HelpBaseDir
        } else {
                set dir [lindex [$tree itemcget $node -data] 1]
        }
        
        set idxfolder 0
        set files ""
        foreach i [glob -nocomplain -dir $dir *] {
                lappend files [file tail $i]
        }
        foreach i [lsort -dictionary $files] {
                set fullpath [file join $dir $i]
                regsub {^[0-9]+} $i {} name
                regsub -all {\s} $fullpath _ item
                if { [file isdir $fullpath] } {

                        # exclude special directories ...
                        if { [string equal -nocase $i "images"] } { continue }
                        if { [string equal -nocase $i "externals"] } { continue }
                        if { [string equal -nocase $i "lib"] } { continue }

                        $tree insert $idxfolder $node $item \
                                        -image appbook16 -text $name \
                                        -data [list folder $fullpath] -drawcross allways
                        incr idxfolder
                } elseif { [string match .htm* [file ext $i]] } {
                        set name [file root $i]
                        $tree insert end $node $item \
                                        -image filedocument16 -text $name \
                                        -data [list file $fullpath]
                }
        }
}

proc HelpViewer::moddir { idx tree node } {
        variable HelpBaseDir
        
        if { $idx && [$tree itemcget $node -drawcross] == "allways" } {
                FillDir $tree $node
                $tree itemconfigure $node -drawcross auto
                
                if { [llength [$tree nodes $node]] } {
                        $tree itemconfigure $node -image appbookopen16
                } else {
                        $tree itemconfigure $node -image appbook16
                }
        } else {
                if { [lindex [$tree itemcget $node -data] 0] == "folder" } {
                        switch $idx {
                                0 { set img appbook16 }
                                1 { set img appbookopen16 }
                        }
                        $tree itemconfigure $node -image $img
                }
        }
}


proc HelpViewer::KeyPress { a } {
        variable tree
        variable searchstring
        
        set node [$tree selection get]
        if { [llength $node] != 1 } { return }
        
        append searchstring $a
        after 300 [list set HelpViewer::searchstring ""]
        
        if { [$tree itemcget $node -open] == 1 && [llength [$tree nodes $node]] > 0 } {
                set parent $node
                set after 1
        } else {
                set parent [$tree parent $node]
                set after 0
        }
        
        foreach i [$tree nodes $parent] {
                if { !$after } {
                        if { $i == $node } {
                                if { [string length $HelpViewer::searchstring] > 1 } {
                                        set after 2
                                } else {
                                        set after 1
                                }
                        }
                }
                if { $after == 2 && [string match -nocase $HelpViewer::searchstring* \
                                        [$tree itemcget $i -text]] } {
                        $tree selection clear
                        $tree selection set $i
                        $tree see $i
                        return
                }
                if { $after == 1 } { set after 2 }
        }
        foreach i [$tree nodes [$tree parent $node]] {
                if { $i == $node } { return }
                if { [string match -nocase $HelpViewer::searchstring* [$tree itemcget $i -text]] } {
                        $tree selection clear
                        $tree selection set $i
                        $tree see $i
                        return
                }
        }
}

proc HelpViewer::Select { tree num node } {
        variable hwidget
        
        if { $node == "" } {

                set node [$tree selection get]
                if { [llength $node] != 1 } { return }

        } elseif { ![$tree exists $node] } {
                return
        }
        
        if { $num >= 1 } {
                if { [$tree itemcget $node -open] == 0 } {
                        $tree itemconfigure $node -open 1
                        set idx 1
                } else {
                        $tree itemconfigure $node -open 0
                        set idx 0
                }
                moddir $idx $tree $node
                if { $num == 1 && $idx == 0 } {
                        return
                }

                # set selection and ...
                # hans: bring current selection onto the screen

                $tree selection set $node
                $tree see $node

                if { [llength [$tree selection get]] == 1 } {
                        set data [$tree itemcget [$tree selection get] -data]
                        if { $num >= 1 && $num <= 2 } {
                                LoadFile $hwidget [lindex $data 1]
                        }
                }
                return
        }
}


proc HelpViewer::TryToSelect { name } {
        variable HelpBaseDir
        variable tree
        
        set nameL [file split $name]
        
        set level [llength [file split $HelpBaseDir]]
        set node root
        while 1 {
                set found 0
                foreach i [$tree nodes $node] {
                        if { [lindex [$tree itemcget $i -data] 1] == [eval file join [lrange $nameL 0 $level]] } {
                                set found 1
                                break
                        }
                }
                if { !$found } { return }
                if { [lindex [$tree itemcget $i -data] 0] == "folder" } {
                        if { [$tree itemcget $i -open] == 0 } {
                                $tree itemconfigure $i -open 1
                        }
                        moddir 1 $tree $i
                }
                
                if { $level == [llength $nameL]-1 } {
                        Select $tree 3 $i
                        return
                }
                set node $i
                incr level
        }
}


proc HelpViewer::CenterWindow {w wparent} {

    wm withdraw $w
    update idletasks

    set top [winfo toplevel [winfo parent $w]]
        set width [winfo reqwidth $w]
        set height [winfo reqheight $w]

        if { [wm state $top] == "withdrawn" } {
            set x [expr [winfo screenwidth $top]/2-$width/2]
            set y [expr [winfo screenheight $top]/2-$height/2]
        } else {
            set x [expr [winfo x $top]+[winfo width $top]/2-$width/2]
            set y [expr [winfo y $top]+[winfo height $top]/2-$height/2]
        }
        if { $x < 0 } { set x 0 }
        if { $y < 0 } { set y 0 }

        wm geom $w ${width}x${height}+${x}+$y
    wm deiconify $w
    update idletasks
    wm geom $w [wm geom $w]

    focus $w
}


proc HelpViewer::ExitCmd {{wbase ""}} {
        variable callbackcmd

        if {$callbackcmd != ""} {
                catch {uplevel $callbackcmd}
        }
        
        if {$wbase != "" && [winfo exists $wbase]} {
                destroy $wbase
        } else {
                exit 0
        }
}


proc HelpViewer::getTreeWidget {} {
        variable tree
        return $tree
}


proc HelpViewer::getTreeRootName {} {
        return "root"
}


proc HelpViewer::getHelpViewerScale {} {
        variable hwidget
        return [$hwidget fontScaleCmd "getscale"]
}

proc HelpViewer::setHelpViewerScale {scale} {
        variable hwidget
        $hwidget setscale $scale
}


# -----------------------------------------------------------------------------
# main dialog
# -----------------------------------------------------------------------------

proc HelpViewer::HelpWindow { file_or_dir {base .help} {geom ""} {title ""} {cmd ""} } {
        variable HelpBaseDir
        variable hwidget
        variable tree
        variable searchlistbox1
        variable searchlistbox2
        variable notebook
        variable callbackcmd

        global lastDir tcl_platform argv0

        set callbackcmd $cmd
        
        if { $tcl_platform(platform) != "windows" } {
                option add *Scrollbar*Width 10
                option add *Scrollbar*BorderWidth 1
                option add *Button*BorderWidth 1
        }
        option add *Menu*TearOff 0


        catch { destroy $base }
        
        if { $title == "" } { set title Help }
        
        # could be either a directory or a file reference:
        # ------------------------------------------------
        # set file_or_dir [file normalize $file_or_dir]
        
        if { [file isdirectory $file_or_dir] } {
                set HelpBaseDir $file_or_dir
        } else {
                set HelpBaseDir [file dirname $file_or_dir]
        }
        # -----------------------------------------------
        # puts $HelpBaseDir

        if { [info procs InitWindow] != "" } {
                InitWindow $base $title PostHelpViewerWindowGeom
        } else {
                toplevel $base
                wm title $base $title
        }

        wm withdraw $base
        wm protocol $base WM_DELETE_WINDOW "[namespace current]::ExitCmd $base"

        #set pw [PanedWindow $base.pw \
        #                        -activator button \
        #                        -side top \
        #                        -pad 5]

        set pw [ttk::panedwindow $base.pw \
                                -orient "horizontal"]
                                
        $pw add [set pane1 [ttk::frame $base.pane1]] ;# -weight 5
        $pw add [set pane2 [ttk::frame $base.pane2]] ;# -weight 95

        NoteBook $pane1.nb \
                -homogeneous 1 \
                -bd 1 \
                -internalborderwidth 3 \
                -bg "#EDF3FE" \
                -activebackground "#EDF3FE" \
                -disabledforeground "#EDF3FE"

        pack $pane1.nb -side top -fill both -expand true
                        
        set notebook $pane1.nb
        
        set f1 [$pane1.nb insert end tree -text "Contents" -image appbook16]
        
        set sw [ScrolledWindow $f1.lf -relief flat -borderwidth 0]
        pack $sw -fill both -expand yes
        
        set tree [Tree $sw.tree -bg white\
                                -width 15 \
                                -relief flat \
                                -borderwidth 0 \
                                -highlightthickness 0 \
                                -redraw 1 \
                                -deltay 18 \
                                -bg "#EDF3FE" \
                                -opencmd   "HelpViewer::moddir 1 $sw.tree" \
                                -closecmd  "HelpViewer::moddir 0 $sw.tree"]

        $sw setwidget $tree
        
        # font metrics:
        set font [option get $tree font Font]

        if { $font == "" } {
                set font "TkDefaultFont"
        }

        catch {
                $tree configure -deltay [expr [font metrics $font -linespace]]
        }

        if { $::tcl_platform(platform) != "windows" } {
                $tree configure \
                        -selectbackground "#48c96f" \
                        -selectforeground white
        }
        $pane1.nb itemconfigure tree -raisecmd "focus $tree"

        if {[string equal "unix" $::tcl_platform(platform)]} {
                bind $tree.c <4> { %W yview scroll -5 units }
                bind $tree.c <5> { %W yview scroll 5 units }
        }

        $tree bindText  <ButtonPress-1>         "HelpViewer::Select $tree 1"
        $tree bindText  <Double-ButtonPress-1>  "HelpViewer::Select $tree 2"
        $tree bindImage <ButtonPress-1>         "HelpViewer::Select $tree 1"
        $tree bindImage <Double-ButtonPress-1>  "HelpViewer::Select $tree 2"
        $tree bindText  <Control-ButtonPress-1> "HelpViewer::Select $tree 3"
        $tree bindImage <Control-ButtonPress-1> "HelpViewer::Select $tree 3"
        $tree bindText  <Shift-ButtonPress-1>   "HelpViewer::Select $tree 4"
        $tree bindImage <Shift-ButtonPress-1>   "HelpViewer::Select $tree 4"

        # dirty trick
        foreach i [bind $tree.c] {
                bind $tree.c $i "+[list after idle [list HelpViewer::Select $tree 0 {}]]"
        }
        bind $tree.c <Return> "HelpViewer::Select $tree 1 {}"
        bind $tree.c <KeyPress> "if \[string is wordchar -strict {%A}\] {HelpViewer::KeyPress %A}"
        bind $tree.c <Alt-KeyPress-Left> ""
        bind $tree.c <Alt-KeyPress-Right> ""
        bind $tree.c <Alt-KeyPress> { break }

        
        set f2 [$pane1.nb insert end search -text "Search" -image viewmag16]
        
        set fs [ttk::frame $f2.search]
        pack $fs -side top -fill x
        
        label $fs.l1 -text "S:" -bg "#EDF3FE"
        entry $fs.e1 -textvariable HelpViewer::searchstring -bg "#EDF3FE"

        pack $fs.l1 -side left
        pack $fs.e1 -side right -fill x -expand true
        
        $pane1.nb itemconfigure search -raisecmd "tkTabToWindow $fs.e1"

        bind $fs.e1 <Return> "focus $f2.lf1.lb; HelpViewer::SearchInAllHelp"
        
        set sw [ScrolledWindow $f2.lf1 -relief sunken]
        pack $sw -fill both -expand yes
        
        set searchlistbox1 \
                        [listbox $f2.lf1.lb \
                                -listvar HelpViewer::SearchFound \
                                -bg "#EDF3FE" \
                                -exportselection 0]

        $f2.lf1 setwidget $searchlistbox1

        bind $searchlistbox1 <FocusIn> "if { \[%W curselection\] == {} } { %W selection set 0 }"
        bind $searchlistbox1 <ButtonPress-1> "focus $f2.lf2.lb; HelpViewer::SearchInAllHelpL1; HelpViewer::SearchCmd"
        bind $searchlistbox1 <Return> "focus $f2.lf2.lb; HelpViewer::SearchInAllHelpL1; HelpViewer::SearchCmd"
        
        set sw [ScrolledWindow $f2.lf2 -relief sunken]
        pack $sw -fill both -expand yes
        
        set searchlistbox2 \
                        [listbox $f2.lf2.lb \
                                -listvar HelpViewer::SearchFound2 \
                                -bg #EDF3FE \
                                -exportselection 0]

        $f2.lf2 setwidget $searchlistbox2
        
        bind $searchlistbox2 <FocusIn> "if { \[%W curselection\] == {} } { %W selection set 0 }"
        bind $searchlistbox2 <ButtonPress-1> "HelpViewer::SearchInAllHelpL2; HelpViewer::SearchCmd"
        bind $searchlistbox2 <Return> "HelpViewer::SearchInAllHelpL2; HelpViewer::SearchCmd"
        
        set HelpViewer::SearchFound ""
        set HelpViewer::SearchFound2 ""
        
        $pane1.nb compute_size
        $pane1.nb raise tree
        
        # set pane2 [$pw add -weight $weight2]
        
        set sw [ScrolledWindow $pane2.lf -relief sunken -borderwidth 0]
        pack $sw -fill both -expand yes

        # -----------------------------------------------------------------------------
        # create html3widget
        # -----------------------------------------------------------------------------
        
        html3widget::html3widget $sw.h \
                -width 550 \
                -height 500

        set hwidget $sw.h
        $sw setwidget $hwidget

        
        bind [$hwidget get_htmlwidget] <1> \
                "+[namespace current]::HrefBinding $hwidget %x %y"

        frame $base.buts -bg grey93
        
        Button $base.buts.b1 \
                        -image $::hv_images(draw-arrow-back) \
                        -command "History::GoBackward $hwidget" \
                        -relief link \
                        -helptext "Go Backward..."

        Button $base.buts.b2 \
                        -image $::hv_images(draw-arrow-forward) \
                        -command "History::GoForward $hwidget" \
                        -relief link \
                        -helptext "Go Forward..."

        menubutton $base.buts.b3 -text "More..." -bg grey93 -fg DarkGrey -relief flat \
                        -menu $base.buts.b3.m -activebackground grey93
        
        menu $base.buts.b3.m
        $base.buts.b3.m add command \
                        -label "Home" -acc "" \
                        -command "History::GoHome $hwidget" \
                        -image $::hv_images(go-home-4) \
                        -compound left

        $base.buts.b3.m add command \
                        -label "Previus" -acc "Alt-Left" \
                        -command "History::GoBackward $hwidget" \
                        -image $::hv_images(draw-arrow-back) \
                        -compound left

        $base.buts.b3.m add command \
                        -label "Next" -acc "Alt-Right" \
                        -command "History::GoForward $hwidget" \
                        -image $::hv_images(draw-arrow-forward) \
                        -compound left

        $base.buts.b3.m add separator
        $base.buts.b3.m add command \
                        -label "Search in page..." -acc "Ctrl+F" \
                        -command "focus $hwidget; HelpViewer::SearchWindow" \
                        -image $::hv_images(system-search) \
                        -compound left

        $base.buts.b3.m add command \
                        -label "Close Search Dialog" -acc "" \
                        -command "$hwidget hideSearchWidget" \
                        -image $::hv_images(emblem-unreadable) \
                        -compound left

        $base.buts.b3.m add separator

        $base.buts.b3.m add command \
                        -label "  Increase Viewer Font / Zoom +" -acc "Ctrl+Plus" \
                        -command "$hwidget fontScaleCmd plus" \
                        -compound left

        $base.buts.b3.m add command \
                        -label "  Decrease Viewer Font / Zoom -" -acc "Ctrl+Minus" \
                        -command "$hwidget fontScaleCmd minus" \
                        -compound left

        Button $base.buts.b4 \
                        -image $::hv_images(emblem-unreadable) \
                        -command "[namespace current]::ExitCmd $base" \
                        -relief link \
                        -helptext "Close the Dialog"

        pack $base.buts.b1 $base.buts.b2 $base.buts.b3 -side left
        pack $base.buts.b4 -side right

        pack $base.buts -side top -fill x
        pack $pw -side bottom -fill both -expand true
        
        # This procedure is called when the user selects the File/Open
        # menu option.
        #
        set lastDir [pwd]
        
        if { [package provide Pan] != "" } {
                pan bind $tree.c
                
                # option -cursor is not implemented in html & html3widget...
                pan bind [winfo parent [$hwidget get_htmlwidget]]
        }

        bind [$hwidget get_htmlwidget] <3> \
                        [list tk_popup $base.buts.b3.m %X %Y]


        focus $hwidget
        bind [$hwidget get_htmlwidget] <Prior> {%W yview scroll -1 pages}
        bind [$hwidget get_htmlwidget] <Next>  {%W yview scroll 1 pages}
        bind [$hwidget get_htmlwidget] <Home>  {%W yview moveto 0}
        bind [$hwidget get_htmlwidget] <End>   {%W yview moveto 1}
        
        bind [winfo toplevel $hwidget] <Alt-Left> "History::GoBackward $hwidget; break"
        bind [winfo toplevel $hwidget] <Alt-Right> "History::GoForward $hwidget; break"
        
        bind [winfo toplevel $hwidget] <F3> "focus $hwidget; HelpViewer::Search ; break"
        bind [winfo toplevel $hwidget] <Control-f> "focus $hwidget; HelpViewer::SearchWindow; break"

        bind [winfo toplevel $hwidget] <Alt-KeyPress-c> [list $notebook raise tree]
        bind [winfo toplevel $hwidget] <Alt-KeyPress-i> [list $notebook raise search]
        bind [winfo toplevel $hwidget] <Alt-KeyPress-s> [list $notebook raise search]
        bind [winfo toplevel $hwidget] <Control-KeyPress-i> [list $notebook raise search]
        bind [winfo toplevel $hwidget] <Control-KeyPress-s> [list $notebook raise search]

        
        FillDir $tree root

        # if an arguent was specified, read it into the HTML widget.
        if {$file_or_dir != ""} {
                LoadFile $hwidget $file_or_dir
        }

        if { $geom == "" } {
                set x [expr [winfo screenwidth $base]/2-400]
                set y [expr [winfo screenheight $base]/2-300]
                wm geom $base 650x400+${x}+$y

        } else {
                wm geom $base $geom
        }
        
        update idletasks
        wm deiconify $base

        return $hwidget
}

  • helpviewer_bindings.tcl

bind all <MouseWheel> {
        set w %W
        while { $w != [winfo toplevel $w] } {
                catch {
                        set ycomm [$w cget -yscrollcommand]
                        if { $ycomm != "" } {
                                $w yview scroll [expr int(-1*%D/36)] units
                                break
                        }
                }
                set w [winfo parent $w]
        }
}

  • helpviewer_cmd.tcl
# -------------------------------------------------------------------------
# helpviewer_cmd.tcl
# -------------------------------------------------------------------------
# (c) 2016, Johann Oberdorfer - Engineering Support | CAD | Software
#     johann.oberdorfer [at] googlemail.com
#     www.johann-oberdorfer.eu
# -------------------------------------------------------------------------
# This source file is distributed under the BSD license.
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#   See the BSD License for more details.
# 
# -------------------------------------------------------------------------
# Revision history:
# 17-01-21: Hans, Initial release
# -------------------------------------------------------------------------
# Purpose:
#   Implements required callback commands for the tkhtml 3.0 widget
# -------------------------------------------------------------------------

package require http

# to do:
# support for:
#   + hyperlinks within the same page ?
#   + search highligt (see tkhtml PDF documentation)
#   + frame frameset support


namespace eval HelpViewer {}


# This procedure is called when the user clicks on a hyperlink.
# See the "bind $base.h.h.x" below for the binding that invokes this
# procedure
#
proc HelpViewer::HrefBinding {hwidget x y} {
        variable HelpBaseDir
        
        set node_data [$hwidget node -index $x $y]

        if { [llength $node_data] >= 2 } {
                set node [lindex $node_data 0]
        } else {
                set node $node_data
        }

        # parent node is an <A> tag (maybe?)
        if { [catch {set node [$node parent]} ] == 0 } {
        
                if {[$node tag] == "a"} {
                        set href [string trim [$node attr -default "" href]]

                        if {$href ne "" && $href ne "#"} {
                                set fname [file join $HelpBaseDir $href]

                                # follow the link, if the file exists
                                if {[file exists $fname] } {
                                        LoadRef $hwidget $fname 0
                                } else {
                                        # LoadRef $hwidget $node 0
                                }
                        }
                }
        }
}

  • helpviewer_history.tcl
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------


namespace eval History {
        variable list ""
        variable pos
        variable menu
        
        proc Add { name } {
                variable list
                variable pos
                variable menu
                
                lappend list $name
                set pos [expr [llength $list]-1]
                if { $pos == 0 } {
                        if { [info exists menu] && [winfo exists $menu] } {
                                $menu entryconf Backward -state disabled
                        }
                } else {
                        if { [info exists menu] && [winfo exists $menu] } {
                                $menu entryconf Backward -state normal
                        }
                }
                if { [info exists menu] && [winfo exists $menu] } {
                        $menu entryconf Forward -state disabled
                }
        }

        proc GoHome { w } {
                variable list
                variable pos
                variable menu
                
                set pos 0
                if { [info exists menu] && [winfo exists $menu] } {
                        $menu entryconf Backward -state disabled
                }
                if { [info exists menu] && [winfo exists $menu] } {
                        $menu entryconf Forward -state normal
                }
                # HelpViewer::LoadRef $w [lindex $list $pos] 0
                HelpViewer::LoadRef $w [file join $HelpViewer::HelpBaseDir "index.html"] 0
        }

        proc GoBackward { w } {
                variable list
                variable pos
                variable menu
                
                incr pos -1
                if { $pos == 0 } {
                        if { [info exists menu] && [winfo exists $menu] } {
                                $menu entryconf Backward -state disabled
                        }
                }
                if { [info exists menu] && [winfo exists $menu] } {
                        $menu entryconf Forward -state normal
                }
                HelpViewer::LoadRef $w [lindex $list $pos] 0
        }

        proc GoForward { w } {
                variable list
                variable pos
                variable menu
                
                incr pos 1
                if { $pos == [expr [llength $list]-1] } {
                        if { [info exists menu] && [winfo exists $menu] } {
                                $menu entryconf Forward -state disabled
                        }
                }
                if { [info exists menu] && [winfo exists $menu] } {
                        $menu entryconf Backward -state normal
                }
                HelpViewer::LoadRef $w [lindex $list $pos] 0
        }
}

  • helpviewer_search.tcl
# -------------------------------------------------------------------------
# helpviewer_search.tcl
# -------------------------------------------------------------------------
# (c) 2016, Johann Oberdorfer - Engineering Support | CAD | Software
#     johann.oberdorfer [at] googlemail.com
#     www.johann-oberdorfer.eu
# -------------------------------------------------------------------------
# This source file is distributed under the BSD license.
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#   See the BSD License for more details.
# 
# Credits:
#  This software related on the the helpviewer application originally
#  created by Ramon Ribó (RAMSAN) ramsan@cimne.upc.es.
#  (http://gid.cimne.upc.es/ramsan) for his version of the 
#  Thank you.
#
# -------------------------------------------------------------------------


namespace eval HelpViewer {}

proc HelpViewer::IsWordGood { word otherwords } {
        variable Index
        variable IndexFilesTitles
        
        if { $otherwords == "" } { return 1 }
        
        if { ![info exists Index($word)] } { return 0 }
        
        foreach i $Index($word) {
                set file [lindex [lindex $IndexFilesTitles $i] 0]
                if { [HasFileTheWord $file $otherwords] } { return 1 }
        }
        return 0
}

proc HelpViewer::HasFileTheWord { fname otherwords } {
        variable HelpBaseDir
        variable Index
        variable IndexFilesTitles
        variable FindWordInFileCache
        
        set fullfile [file join $HelpBaseDir $fname]
        
        foreach word $otherwords {
                if { [info exists FindWordInFileCache($fname,$word)] } {

                        if { !$FindWordInFileCache($fname,$word) } {
                                return 0
                        }
                        continue
                }

                set fp [open $fullfile "r"]
                set aa [read $fp]
                close $fp

                if { [string match -nocase *$word* $aa] } {
                        set FindWordInFileCache($fname,$word) 1
                } else {
                        set FindWordInFileCache($fname,$word) 0
                        return 0
                }
        }
        return 1
}


proc HelpViewer::GiveManHelpNames { word } {
        
        if { [auto_execok man2html] == "" } { return "" }
        
        set err [catch { exec man -aw $word } file]
        if { $err } { return "" }
        
        set words ""
        foreach i [split $file \n] {
                set ext [string trimleft [file ext $i] .]
                if { $ext == "gz" } { set ext [string trimleft [file ext [file root $i]] .] }
                if { [lsearch $words "$word (man $ext)"] == -1 } {
                        lappend words "$word (man $ext)"
                }
        }
        return $words
}

proc HelpViewer::WaitState { what } {
        variable tree
        variable hwidget

        # set parent [winfo parent $hwidget]
        
        switch $what {
                1 {
                        $tree configure -cursor watch
                        # $parent configure -cursor watch
                }
                0 {
                        $tree configure -cursor ""
                        # $parent configure -cursor ""
                }
        }
        update
}


proc HelpViewer::CreateIndex {} {
        variable HelpBaseDir
        variable Index
        variable IndexFilesTitles
        variable progressbar
        variable progressbarStop
        variable hwidget
        
        if { [array exists Index] } { return }
        if { [file exists [file join $HelpBaseDir wordindex]] } {
                set fin [open [file join $HelpBaseDir wordindex] r]
                foreach "IndexFilesTitles aa" [read $fin] break
                array set Index $aa
                close $fin
                return
        }
        
        WaitState 1
        
        ProgressDlg $hwidget.prdg -textvariable HelpViewer::progressbarT -variable \
                        HelpViewer::progressbar -title "Creating search index" \
                        -troughcolor \#48c96f -stop Stop -command "set HelpViewer::progressbarStop 1"
        
        set progressbar 0
        set progressbarStop 0
        
        catch { unset Index }
        
        set files [::fileutil::findByPattern $HelpBaseDir "*.htm *.html"]
        
        set len [llength [file split $HelpBaseDir]]
        set ipos 0
        set numfiles [llength $files]
        
        set IndexFilesTitles ""
        
        foreach i $files {
                set HelpViewer::progressbar [expr int($ipos*50/$numfiles)]
                set HelpViewer::progressbarT $HelpViewer::progressbar%
                if { $HelpViewer::progressbarStop } {
                        destroy .prdg
                        return
                }
                
                set fin [open $i r]
                set aa [read $fin]
                
                set file [eval file join [lrange [file split $i] $len end]]
                set title ""
                regexp {(?i)<title>(.*?)</title>} $aa {} title
                if { $title == "" } {
                        regexp {(?i)<h([1234])>(.*?)</h\1>} $aa {} {} title
                }
                lappend IndexFilesTitles [list $file $title]
                set IndexPos [expr [llength $IndexFilesTitles]-1]
                
                foreach j [regexp -inline -all -- {-?\w{3,}} $aa] {
                        if { [string is integer $j] || [string length $j] > 25 || [regexp {_[0-9]+$} $j] } {
                                continue
                        }
                        lappend Index([string tolower $j]) $IndexPos
                }
                close $fin
                incr ipos
        }
        
        proc IndexesSortCommand { e1 e2 } {
                upvar freqs freqsL
                if { $freqsL($e1) > $freqsL($e2) } { return -1 }
                if { $freqsL($e1) < $freqsL($e2) } { return 1 }
                return 0
        }
        
        set names [array names Index]
        set len [llength $names]
        set ipos 0
        foreach i $names {
                set HelpViewer::progressbar [expr 50+int($ipos*50/$len)]
                set HelpViewer::progressbarT $HelpViewer::progressbar%
                if { $HelpViewer::progressbarStop } {
                        destroy .prdg
                        return
                }
                foreach j $Index($i) {
                        set title [lindex [lindex $IndexFilesTitles $j] 1]
                        if { [string match -nocase *$i* $title] } {
                                set icr 10
                        } else { set icr 1 }
                        if { ![info exists freqs($j)] } {
                                set freqs($j) $icr
                        } else { incr freqs($j) $icr }
                }
                #          if { $i == "variable" } {
                #              puts "-----variable-----"
                #              foreach j $Index($i) {
                #                  puts [lindex $IndexFilesTitles $j]-----$j
                #              }
                #              parray freqs
                #          }
                set Index($i) [lrange [lsort -command HelpViewer::IndexesSortCommand [array names freqs]] \
                                0 4]
                
                #          if { $i == "variable" } {
                #              puts "-----variable-----"
                #              foreach j [lsort -command HelpViewer::IndexesSortCommand [array names freqs]] {
                #                  puts [lindex $IndexFilesTitles $j]-----$j
                #              }
                #          }
                unset freqs
                incr ipos
        }
        
        set HelpViewer::progressbar 100
        set HelpViewer::progressbarT $HelpViewer::progressbar%
        destroy $hwidget.prdg
        set fout [open [file join $HelpBaseDir wordindex] w]
        puts -nonewline $fout [list $IndexFilesTitles [array get Index]]
        close $fout
        WaitState 0
}


proc HelpViewer::SearchInAllHelp {} {
        variable Index
        variable searchlistbox1
        
        set word [string tolower $HelpViewer::searchstring]
        CreateIndex
        
        set HelpViewer::SearchFound ""
        set HelpViewer::SearchFound2 ""
        
        if { [string trim $word] == "" } { return }
        
        set words [regexp -all -inline {\S+} $word]
        if { [llength $words] > 1 } {
                set word [lindex $words 0]
                set otherwords [lrange $words 1 end]
        } else { set otherwords "" }
        
        set ipos 0
        set iposgood -1
        foreach i [array names Index *$word*] {
                if { ![IsWordGood $i $otherwords] } { continue }
                
                lappend HelpViewer::SearchFound $i
                if { [string equal $word [lindex $i 0]] } { set iposgood $ipos }
                incr ipos
        }
        if { $iposgood == -1 && [llength [GiveManHelpNames $HelpViewer::searchstring]] > 0 } {
                lappend HelpViewer::SearchFound $HelpViewer::searchstring
                set iposgood $ipos
        }
        
        if { $iposgood >= 0 } {
                $searchlistbox1 selection clear 0 end
                $searchlistbox1 selection set $iposgood
                $searchlistbox1 see $iposgood
                SearchInAllHelpL1
        }
}

proc HelpViewer::SearchInAllHelpL1 {} {
        variable Index
        variable IndexFilesTitles
        variable SearchFound2
        variable SearchFound2data
        variable searchlistbox1
        variable searchlistbox2
        
        set SearchFound2 ""
        set SearchFound2data ""
        
        set sels [$searchlistbox1 curselection]
        if { $sels == "" } {
                # bell
                return
        }
        
        set words [regexp -all -inline {\S+} $HelpViewer::searchstring]
        if { [llength $words] > 1 } {
                set otherwords [lrange $words 1 end]
        } else { set otherwords "" }
        
        set ipos 0
        set iposgood -1
        set iposgoodW -1
        foreach i $sels {
                set word [$searchlistbox1 get $i]
                if { [info exists Index($word)] } {
                        foreach i $Index($word) {
                                foreach "file title" [lindex $IndexFilesTitles $i] break
                                
                                if { ![HasFileTheWord $file $otherwords] } { continue }
                                
                                if { [lsearch $HelpViewer::SearchFound2 $title] != -1 } { continue }
                                
                                lappend SearchFound2 $title
                                lappend SearchFound2data $i
                                if { [string match -nocase *$word* $title] } {
                                        set W 1
                                        foreach i $otherwords {
                                                if { [string match -nocase *$i* $title] } { incr W }
                                        }
                                        if { [string match -nocase *$HelpViewer::searchstring* $title] } { incr W }
                                        if { [string equal -nocase $HelpViewer::searchstring $title] } { incr W }
                                        
                                        if { $W > $iposgoodW } {
                                                set iposgood $ipos
                                                set iposgoodW $W
                                        }
                                }
                                incr ipos
                        }
                }
                foreach i [GiveManHelpNames $word] {
                        lappend SearchFound2 $i
                        if { $iposgood == -1 } {
                                set iposgood $ipos
                        } else { set iposgood -2 }
                        incr ipos
                }
        }
        if { $iposgood < 0 && $ipos > 0 } { set iposgood 0 }
        if { $iposgood >= 0 } {
                focus $searchlistbox2
                $searchlistbox2 selection clear 0 end
                $searchlistbox2 selection set $iposgood
                $searchlistbox2 see $iposgood
                SearchInAllHelpL2
        }
}

proc HelpViewer::SearchInAllHelpL2 {} {
        variable HelpBaseDir
        variable SearchFound2data
        variable IndexFilesTitles
        variable SearchFound2
        variable hwidget
        variable searchlistbox2
        
        set sels [$searchlistbox2 curselection]
        if { [llength $sels] != 1 } {
                # bell
                return
        }
        if { [regexp {(.*)\(man (.*)\)} [lindex $SearchFound2 $sels]] } {
                # -removed- SearchManHelpFor $hwidget [lindex $SearchFound2 $sels]
        } else {
                set i [lindex $SearchFound2data $sels]
                set file [file join $HelpBaseDir [lindex [lindex $IndexFilesTitles $i] 0]]

                LoadFile $hwidget $file 1

                $hwidget showSearchWidget
                
                # without delay, html widget 'll potentially crash!
                after idle "$hwidget setsearchstring $HelpViewer::searchstring"
        }
}


proc HelpViewer::Search {} {
        variable hwidget
        
        if { ![info exists ::HelpViewer::searchstring] } {
        
                set msg "Before using 'Continue search', use 'Search'"
                append msg [winfo toplevel $hwidget]

                tk_messageBox \
                        -icon warning -message $msg \
                        -type ok
                return
        }
        if { $HelpViewer::searchstring == "" } {
                return
        }

        $hwidget showSearchWidget

        # handing over the search command to the
        # html3widget (which has a build in search capability)
        # note: without delay, html widget 'll potentially crash!
        after idle "$hwidget setsearchstring $HelpViewer::searchstring"
        
}


proc HelpViewer::SearchCmd {} {
        #
        # perform search as well on the html viewer widget content
        # search up to the 1st occurance of the search string
        # so that the string item is highlighted and immediately shown
        # on the screen...
        #
        variable searchstring
        
        if {$searchstring == ""} {
                return
        }

        # html widget needs some time to establish screen...
        # catch maybe not really required, just in case...
        after 400 "catch {[namespace current]::Search}"
}


proc HelpViewer::SearchWindow {} {
        variable hwidget
        $hwidget showSearchWidget
}

  • hvimages.tcl
# ImageLib.tcl ---
# Automatically created by: CreateImageLib.tcl

set images(emblem-unreadable) [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBI
WXMAAABIAAAASABGyWs+AAAACXZwQWcAAAAWAAAAFgDcxelYAAADlElEQVQ4y9WUTWwbRRTH/961
x+D9aDmWjxskNE1UEEgcGojghtRISImEBKUF6sbBadU7Rv5SnKByQGrJVzFJDCrqwQfUcuCAaEr4
uCDamOTSCy1ULYVmv7xe787a+zjURkmJ2yIkJEZ60szoP7958968B/xXI5tN9adzbz//b7XCxkU6
lxoUwuJyWAx/lc6lBu8EvWdtOpcaLEzmPKfukOM4VJjMeZ0OtLV2zSa7ZnfWpnOpofx4mup1h4Ig
oCAIyLTMLQ+0obqhEececc7JtquUH09TNvvOC5tCIVBIBwAiACBwn0OSJBxOHmVRxspteDqXGowy
Vk6+dYTFYhKqNQsE+uvSIEQPtOehjZ5EGSsfTh5lkiTD4x6ijMG2bUzNHOc+56UIYweSo0dYTJJQ
tS3IkgKnVsP03AnOuT+czxTO3jHOhmmQ3/Cpalvk+5x0Q6OJd/Ok6Rp53KWb2u/kei5p2jqNT2a9
uyV6M9zQifucDFMnz3MpCAJyPZf+WL9Brle/K1S8feP80vKl/uf2rPx44YehJ3Y/Kcbuj6Fas+DU
a/BcF4qkwnEczMx+wLnf+fnCVpv5TOGsz3lpeuYEIhGGIAhuGQVgLIrZ2Sk0fL/UMaZbedwOB2Ns
YnRkTBSEEFyvDiICEUEURfT29uHixQt9/QN7Vs4vLV+6J3D7d4wcSjJJlmDZJmRZhapsgyiIMC0d
iqKgp6dX/KmyMtQJ/reSjkYi5UPxUSZJMZiWATmmoGpZOPbeBCzLgqpsg2asQ5IkvLb/dcbCkfJW
CRRvh8YPJpgsy7BsE4qkwjRNzM9/yBu+P1+prPR1dz8uqooK3VyHqqjo6uoW19ZWh54d6N/kudCC
DoUF8UwiMcZkRYFVNSHHFOiGgYWFIucNfzibKYzwhj9c+niB64YOVdkOTb8JRVGwb99+JgrCmY4l
3QyaICJIMRm6oWNxsch103g1nymcAyDnM4VzVrX6yqlTn3BN06Aq2xEEAZqNJoDNJS0CwNLS1788
/cxTldXVyks9PbtE7nEsLhb9a9dvHDz+/tSXABiA+wCI333z/eWdu3auXbny895HH+sSPdfF6dOf
8mrNfnly/NhnwK3mEQIQBhAFEE0k43sf3LGjBAC/Xr36ZnFu4QsAfsuaLUciAMLxxBsvPvLQw/MA
cO236wfmpoufA/Ba1mg3oUgLHk6MxQfIp8bJkx99C6DREvobEv5PtP+j8Sc0NB687S7VUwAAACV0
RVh0Y3JlYXRlLWRhdGUAMjAwOS0xMS0xNVQyMzowNDowOS0wNzowMOh51zsAAAAldEVYdGRhdGU6
Y3JlYXRlADIwMTAtMDEtMTFUMDk6MDk6MjYtMDc6MDCVrk2EAAAAJXRFWHRkYXRlOm1vZGlmeQAy
MDEwLTAxLTExVDA5OjA5OjI2LTA3OjAw5PP1OAAAADR0RVh0TGljZW5zZQBodHRwOi8vY3JlYXRp
dmVjb21tb25zLm9yZy9saWNlbnNlcy9HUEwvMi4wL2xqBqgAAAAldEVYdG1vZGlmeS1kYXRlADIw
MDktMTEtMTVUMjM6MDQ6MDktMDc6MDC3yKEPAAAAF3RFWHRTb3VyY2UAR05PTUUgSWNvbiBUaGVt
ZcH5JmkAAAAgdEVYdFNvdXJjZV9VUkwAaHR0cDovL2FydC5nbm9tZS5vcmcvMuSReQAAAABJRU5E
rkJggg==
}]
set images(system-search) [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBI
WXMAAA3XAAAN1wFCKJt4AAAACXZwQWcAAAAWAAAAFgDcxelYAAAEVElEQVQ4y7WT209UVxTGv3Xm
DsMwcxhgRIyDVkGMZYy1aRWiVaJtkJvpQ31pbN/atA99aI1RmxBNGPGlf0ANSU1No6maam1VRlCw
iSi1tNXCjBEGGS7OgbngzDlnzpy9++BgjC3oiyv5spK9d35r5dtrEeccryKEV0IFYPyfM2rvONJI
oPcA1HNAJGAcoFEOdnz/Vwd/ehkwPWuF3+93QdC/q6yq2rVxw0aIohtWqxWKLCMWn8WNG714MDJy
nTj7bN++Q3+9FNjv93sFA+9uam72rqhYhfN9w/HuwWn9XnjWsdYrJt+pKTXs2lzpDIcf4Oy5s5Jm
0H0HvjgQebHHBv2b1tbdXrGojLUcupC8GUrO7qqrShz9dNtY7fqV0Yu/Rx81H7yQKBQ9rKmxyW3Q
8ENbW5txIbARANqPtddVLK9oLi9fjtavL87t2fH6RP36pUVmo2DSdKa57JZM5TKX8mPfg+G9Hd1V
5w43OFavXl07HAzuAXBiwY6Js899Ph86f74TXVrqnN5U7bEDHAycA2CCQFmDAFbkzIszMjw8Fbgb
q6nxgYjvWNQKAlYaTUZ03XnE3qgskzWdZRVNl1WNxTXGEpzzpJbVZ6YTmdHSYuf4L7cnsjabDZxh
66JWcGAZYzrGpZRotZpG5EzWxMmgMQgZ4iyjM6j992O3NT2bsedbDAP3I9uzWQ2qmsl70RwrRqMJ
Ze78mVAkwVctdSSJSIaeVTKMRW8FZ/qDE6k0BIiJOWWZaDfNJpPJEjWjPlwczBGQ0/Leel+J4cLt
iEMwGXvFAlsildbGY3I2TAJ/LBBcnME6NR2r2FlTDFVVoanK2OLjJuDXoaF/8FFDjZvrWvnoRKwk
MpuaiymqDAACezLs4QnpNUHX1n/csK7kZn8/VEX9ftGOxcLiM6FQ6Lfq6pFNJ/Zvs3/o79429She
XFLkuGUvsFji8bQrKiXfthr5lhMHttuj0UkMDg5mhoaDsRdu3rFjbR5OloGW5pYyr3clzvcFZ3v+
nNbvjs441y53xbf6PKbGzZXO4eA9dHZ2Ih6PQ5KktMVkazh9+nTPgmAAOHr08DpGwrcrvBVv1tbW
weUUYbXZoMgypBkJPT3duN7bq2QzGWvVmjUoK1uCrq5AStHSTadOnrm6IHg+2juO7CZOn0BAOThK
VVVNyLIaUNT0pUuX++J2u+1cc+O7eUs8HpjMZgQCgZSsplpPnTxzZVHw00ui+Q+mnAAA1Rs2vCU6
HBd31m8p8HiWwGKxoKurKyWrqaedC8+BDERkJiIbEeUDcORUCMA5r3sDA0NTk9H3L1/peTw5NQlV
VVFXV5cvwHiciIxEREIOSERkAmAGYMnJBiAPQD6AglwBJwAXgML7Q38HxyKRDy5fuTYXHgtDkqLQ
s7rT7XbbAJjnN48vIJazQH/m3Xxm4VDoDzCh5WrPjQ6bxVwuCPhSkiQdgP4fj+mJscIzMjyXkSuo
P5fnxTnn+BdfFBTdhrqWWgAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxMC0wMS0xMVQwOToxMzowMy0w
NzowMFMfKlcAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTAtMDEtMTFUMDk6MTM6MDMtMDc6MDAiQpLr
AAAANHRFWHRMaWNlbnNlAGh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL0dQTC8y
LjAvbGoGqAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAATdEVYdFNvdXJj
ZQBHTk9NRS1Db2xvcnOqmUTiAAAAMXRFWHRTb3VyY2VfVVJMAGh0dHA6Ly9jb2RlLmdvb2dsZS5j
b20vcC9nbm9tZS1jb2xvcnMvUB216wAAAABJRU5ErkJggg==
}]
set images(go-home-4) [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBI
WXMAAA3XAAAN1wFCKJt4AAAACXZwQWcAAAAWAAAAFgDcxelYAAADKElEQVQ4y6WUwWsUVxzHP+9l
ZuKu2e6YTVrXUncpSA0VqUhqwEsPbVok6MIiCW1K/oI9yUip2H9gKKiRerJNaQ4prTAb8OLZg4ce
apraQ0PrIWyCSXZjm3XX7My8HmZ2nDVuEugPHjPzfu/3+f3e9/3eCPZhs5AFKuHnqUn4dT9xe0FP
z4Lasiy1ZVlqFlSYaFcTe0GBXwqW1THv2DbAkUlY6RYr9wN9ev9+NF+dnydMVNmtcrkXtPnwIX2Z
TId/P3CxG9RdWkJqGjKfb2+fgmWxXi4jhaD//PmusshuUFWpIFOpCPrH2DmWP53AsW0GLlzAVWrX
ykUMOgn8ULAsVK0WOA8dwrFt6p9/xtrxd3jj9cMcq22yfPkyBctitVzG6FJ5HKwKlgWNBmxvQzqN
Y9s8ty7xz5tHGBp6l2QiQbPZJLmyyuOpqQDuOBhSRvDJkKmF0APtBKrRQPT349g26msbmcnwXi5P
LpcL/Aq23zqKfvcuc8UiE6USq46z4/C08JmMtAmhPd/dxtV0zp4ZwTTNKEApBYBpmmQePGBuZISJ
Umnvdrt252e8mW9paRrDw+/T19eH53kdo9Vq4XkevZkMo48eMTc9HW8A2aHxvZMnp54sLMycWfqT
L698saOCmze+wfd9rl2fRokelNtEaAdQ3nO+unqVcirFwIkTpU8WF2/GpWB0YeH7i+PFmXP9wWUY
GjqOEAIhgtye56GUQglJT34Ud2kekfsI9fgeSinK40XyR9++xeLiq6Voa2gYBrquo2kauq7j+34E
r2xsAfDbX+t4nh/F2LatXqWxFgdLKZFSYshlNFHF931838fF4Pe/NwB4UnuGK3qjGILuetFuQA9w
MA5uS+CTpdEMoNlslg/ODvNx4iBSHeZDqbO2QhycIbggrhZm0LqBoZdEAnzfB+BpbR2xudEhX7Va
jbr15T4WbVkMw8A0TdLpNAJJwBcMDg5Sr9cZGxvb0THJZHQN6oBqA0UoRerieLHK/7CffryjA268
9LYcveEBvBbqNQAkQp8Xrt0K31vAJvAvsAY8i63p+B/Hk8jwqcd2RSxQhcMLh+Il+w9pZlYolc7x
lgAAACV0RVh0Y3JlYXRlLWRhdGUAMjAwOS0xMS0xMFQxOTozODoyMC0wNzowMIB9jfEAAAAldEVY
dGRhdGU6Y3JlYXRlADIwMTAtMDItMjBUMjM6MjY6MTUtMDc6MDAGO1yBAAAAJXRFWHRkYXRlOm1v
ZGlmeQAyMDEwLTAxLTExVDA4OjU2OjMwLTA3OjAwFkPu1gAAADJ0RVh0TGljZW5zZQBodHRwOi8v
ZW4ud2lraXBlZGlhLm9yZy93aWtpL1B1YmxpY19kb21haW4//erPAAAAJXRFWHRtb2RpZnktZGF0
ZQAyMDA5LTExLTEwVDE5OjM4OjIwLTA3OjAw38z7xQAAABl0RVh0U291cmNlAFRhbmdvIEljb24g
TGlicmFyeVTP7YIAAAA6dEVYdFNvdXJjZV9VUkwAaHR0cDovL3RhbmdvLmZyZWVkZXNrdG9wLm9y
Zy9UYW5nb19JY29uX0xpYnJhcnm8yK3WAAAAAElFTkSuQmCC
}]
set images(draw-arrow-back) [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAFzUkdC
AK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dE
AAAAAAAA+UO7fwAAAAlwSFlzAAAEwgAABMIBvM+QGAAAAAl2cEFnAAAAFgAAABYA3MXpWAAAAoZJ
REFUOMvFlE1PE1EUht/7MXPpQCgk7FyRuGBhDIk7ygaBIAlYQ0lMZIFsXJu45i/oDzAp6AIChWKR
poLQBKQkYqOucIk/gDCQEGYa597jojAWqB8NTXw3dzLnnmfec+fcA/wvTU5Oh89EVD9wsfg1trv7
+VOx+OVpLXn8T8FC4WPP6am/3dbWesf3Sy/qAs7nt+56np9vb79B0Wgz8/1STZVWBedyG72e5290
dNykSCTCOOcola4JXl7O9vq+t97ZeYscJ8IYY+CcoxbHi4uLYJUvUqlMrzF6vaenmyKRCNNag4hg
WRILC29JKbtiP7vQJWUDDFLKl4nE/SfhxpmZVJ/W+v3Q0AA5jsOMMdBaQ2sNYwwsywKRARHBGIIQ
Ak1NTSH44OAAWmusruYxPv6ISQBIJl/3e97p2sjIA2ppaWbGGBhjwBgDYwzGGARBcAY1UEqhsbER
RAQiwsnJCYIggJQy/BdyampaGIO1RCJO0WgzAwDOeQjlnENrHa6O48C2bQDlCxMEAUqlEoQQEIID
KB8Pn5h4rIUoJ1WKMQYhBKSUkFJCCHEBer7H87zQCOccQohfXWFZVvfKyjt2eOhWvbNCCNi2HSad
uz0/Ms75GfwSeGzsYUEpGcvl1pjrHtFl55Xr5YoaGhqglIJSKqys3DMVmptb6jLmR2FgoJ9aW1su
xJLJV6SUYtU+UCnbtp6Pjo48u1L20lKma34+Ta7rGqrQ7Oz8P4+2dDpdPZDJZLrS6TfkukchPJVK
1zQzq86KeDy+Y1kqtrn5gR0fHxMASClq4f5+ug0PD+4oZcW2trbZ/v53EkKauoABYHDw3o6U8vbe
3rdZy5J9NVn+m7LZbF1519ZPBq0o6tkbPl8AAAAldEVYdGNyZWF0ZS1kYXRlADIwMDktMTEtMTVU
MTc6MDM6MDUtMDc6MDBx3el7AAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDEwLTAxLTExVDA5OjI1OjI2
LTA3OjAw1/4UywAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxMC0wMS0xMVQwOToyNToyNi0wNzowMKaj
rHcAAABndEVYdExpY2Vuc2UAaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnkt
c2EvMy4wLyBvciBodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9MR1BMLzIuMS9b
jzxjAAAAJXRFWHRtb2RpZnktZGF0ZQAyMDA5LTA2LTI0VDEwOjM2OjQ3LTA2OjAwVRY2/AAAABN0
RVh0U291cmNlAE94eWdlbiBJY29uc+wYrugAAAAndEVYdFNvdXJjZV9VUkwAaHR0cDovL3d3dy5v
eHlnZW4taWNvbnMub3JnL+83qssAAAAASUVORK5CYII=
}]
set images(dialog-close) [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABmJLR0QAAAAAAAD5Q7t/AAAACXBI
WXMAAA3XAAAN1wFCKJt4AAAACXZwQWcAAAAWAAAAFgDcxelYAAADzklEQVQ4y7WVPU8bWRSG3zvj
Gdt3bMM62J5gZ+NAA6JIEykSCRnbVDRUW/AHtt9i22i77fIX0i7FVtNsEwWPSIpIaYIUFCmKQgJs
GBuDPR4PHs/92MI2C9gpc6Wj+dB7nnvmPfdoiJQSP2IpP4QKIHbzRb1ajWmdzjYARDMzW5WdHTYt
sV6txjTP24aUU3XKTTE5O7MT+fxGolDYUM7O7Hq1OnVzpd22E6a5kSgUNsgUnXIN2mrZqUKhUqpW
aWl9nRrz8xXSal1LGusM06zcqdVoaX2dpkxzQkfGzdtZWfnbyOU2fq7VKDs/B1EUqLkcjl6+vPCO
j+tKPr8JAGg07FSpVCnVaknebAJSQs1mcfjiReA3Gv9U37//5brHQgBCQEQR5Dg4R3FtLSkdx+oe
HdkAkC6VrNLaWpKdnED2+yCaBjEYQHI+ZNy0gpjmVsd1nUPHCdRMBlAUCN8Hc12UVldpulh8ki4W
n5RWVylzXQjfBxQFSjqNw52doOO6DjHNrQkrAKBOSCxaWLBThYJ159EjKtptIIpAdB1qNgsA4Ofn
kGEIoutQZmfx9fXrwHddR/v0abMiJZsKHsPDcnkIf/iQCs+DjCIQTQOA4b2uDyt98ybwXdeJHxxc
g04Fj+EXd+/amUJhrfjgQYq32wDnw4RYDMrMDI7fvvU9191NfvkyAZ06IOMlGYNgDHwwAI+i/8FC
QA4GEIxBMva99OlW+Ldv28atW9b8/ftUBAHAOYiqDjfkHCQWA0km8e+7d0Gv1XJS375NVK3chHbz
eTuRyVj5pSUaeR54vw8hJaSmQWoahJRgFxdgnof88jJNZDJWN5+364TEplZcJyTmzc3ZidlZy1xe
pnJ0NhVVBUkk0PjwwQeA/NJSSobh0AZVBYnHcbK/H/TbbSdzenpZ+WXF7Wx2WzcMa+7ePcp8H6zf
hxQCHMDx3l7QbTZ3u83m7vHeXsClhBACPAzBul3kFhaobhhWO5vdnmieEAKcMfAwhBQChBBIAM2P
H4O+5zk/dTqbAHDOuX2yv2/lFhepHDUUnEMwBjF+vmnFaTptxw3DypXLlBCC5sFB0Pd9Z67bvfzE
sS6RSlm5cpkCQOPz5yDs9a7pJiavSakdNwwLAC56vVd/BsHWHiAAkPFJXAHUp5T+lTSMxwAw6PWc
uSCYPnmEEB2AvgjQP+Lx5wJQfg/D304BdWSZOoILADwLiGfx+DMFEE/D8NevQABgAGAgpWRXwSoA
HUB8dNUBaFdCvVI1BxBdiQGA/igiKaUg3/uZEkLICERGp+fSiishAEBOgfwHrlwiLshhIVcAAAAl
dEVYdGRhdGU6Y3JlYXRlADIwMTAtMDItMTdUMDc6MDM6MzMtMDc6MDAWPxGJAAAAJXRFWHRkYXRl
Om1vZGlmeQAyMDEwLTAxLTExVDA5OjEzOjAxLTA3OjAwtd2DwgAAADR0RVh0TGljZW5zZQBodHRw
Oi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9HUEwvMi4wL2xqBqgAAAAZdEVYdFNvZnR3
YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAE3RFWHRTb3VyY2UAR05PTUUtQ29sb3JzqplE4gAA
ADF0RVh0U291cmNlX1VSTABodHRwOi8vY29kZS5nb29nbGUuY29tL3AvZ25vbWUtY29sb3JzL1Ad
tesAAAAASUVORK5CYII=
}]
set images(draw-arrow-forward) [image create photo -data {
iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAFzUkdC
AK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dE
AAAAAAAA+UO7fwAAAAlwSFlzAAAEwgAABMIBvM+QGAAAAAl2cEFnAAAAFgAAABYA3MXpWAAAAmdJ
REFUOMvFlM9LVFEUx7/n/qKZYTQCpYWbFu4DN26aAjFREReO4UJI3LVo2x/RskU7ESGVGt8QY+YY
uZgeGjhQbbSl/Qk2KO/n3NNC32OMycYy+sLj3gvf8znn3nPfBf63mPnfwev1L4/39j7V6/XPhd95
xWXAQeA/y+ezA74f1HZ2Pg5fIThAJnONentvsOeF77a33ftXBA5BRDBGU1/fTQ4Cf6tafd8WLhzH
6Rjs+z6MMVBKIZvNUH//LY6ieKtS2Rz52UsAsLZWeR5F8SPAXoAlhGHAY2PDFEURhBDo6elBGEa8
sbFFAEaLxcnqOfDS0goXi5Ngtji9Vdx2ZGaEYQRjNLq7u9OUYRiy41SIGaOzsw+q6Rl7ng+lJKxl
WGvRbFo0m810jOPm2bqJXC6LfD4Pay2YGcwMpRQNDg5wEHibCwsvRluax9DawBgDrTWUUtBaQ0qZ
fkSEXC4HY0wKtfb06I6Pj9HV1UVDQ/cYsG8XFxeMAgClBIgIWus0IKkmmUspIcRpHUQEZgYRpTuR
Up75BOL4eqQAQAiVBgghUlNrVQksbQ4RACCOY0gp0Wg02HV3SWtzd25uis8qlufMyTxJ1KokUZJE
CAHP89l1d8kYXZiZmXYBQAFAJpN5urLy8slFd5iZEQQBz88/pFbwyckJ12ouGaML09NTbhqwvPym
4x9kdfUVt+ro6MiWSmV2nMqdjiHtVCqVW6Dfbbn8miuV9bZQdRlw0otGo8G12gfS2hQmJsbcvwZL
qYLDw29mf/+AlDKF8fER91feS71uSqmRg4Ovq0LI2xdB/1jr65tXzuxYPwBSLVS8t8W5kAAAACV0
RVh0Y3JlYXRlLWRhdGUAMjAwOS0xMS0xNVQxNzowMzowNS0wNzowMHHd6XsAAAAldEVYdGRhdGU6
Y3JlYXRlADIwMTAtMDEtMTFUMDk6MjU6MzUtMDc6MDAqvA7IAAAAJXRFWHRkYXRlOm1vZGlmeQAy
MDEwLTAxLTExVDA5OjI1OjM1LTA3OjAwW+G2dAAAAGd0RVh0TGljZW5zZQBodHRwOi8vY3JlYXRp
dmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1zYS8zLjAvIG9yIGh0dHA6Ly9jcmVhdGl2ZWNvbW1v
bnMub3JnL2xpY2Vuc2VzL0xHUEwvMi4xL1uPPGMAAAAldEVYdG1vZGlmeS1kYXRlADIwMDktMDYt
MjRUMTA6MzY6NDctMDY6MDBVFjb8AAAAE3RFWHRTb3VyY2UAT3h5Z2VuIEljb25z7Biu6AAAACd0
RVh0U291cmNlX1VSTABodHRwOi8vd3d3Lm94eWdlbi1pY29ucy5vcmcv7zeqywAAAABJRU5ErkJg
gg==
}]

  • Demo:
# do something like:

# where to find the required library packages...
set auto_path [linsert $auto_path 0 [file join $::starkit::topdir "lib"]]

package require Tk
wm withdraw .

package require tile
# package require Img
package require http

# requiring exactly 3.0 preferable a download from Activestate
package require -exact Tkhtml 3.0

# same is required for BWidget ...
package require -exact BWidget 1.9.10
package require BWidget_patch
Widget::theme 1

package require helpviewer 3.0


# ------------------------
# start the help viewer...
# ------------------------

set help_dir "where_to_find_the_html_documentation"

HelpViewer::HelpWindow $help_dir