blob: 6991eb1c1e6b2de186e6f2ea1f30b1185b057469 [file] [log] [blame]
#
# Hyperhelp
# ----------------------------------------------------------------------
# Implements a help facility using html formatted hypertext files.
#
# ----------------------------------------------------------------------
# AUTHOR: Kris Raney EMAIL: kraney@spd.dsccc.com
#
# @(#) $Id: hyperhelp.itk,v 1.5 2002/03/16 05:26:19 mgbacke Exp $
# ----------------------------------------------------------------------
# Copyright (c) 1996 DSC Technologies Corporation
# ======================================================================
# Permission to use, copy, modify, distribute and license this software
# and its documentation for any purpose, and without fee or written
# agreement with DSC, is hereby granted, provided that the above copyright
# notice appears in all copies and that both the copyright notice and
# warranty disclaimer below appear in supporting documentation, and that
# the names of DSC Technologies Corporation or DSC Communications
# Corporation not be used in advertising or publicity pertaining to the
# software without specific, written prior permission.
#
# DSC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
# ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND NON-
# INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE
# AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. IN NO EVENT SHALL
# DSC BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
# ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTUOUS ACTION,
# ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
# SOFTWARE.
# ======================================================================
#
# Acknowledgements:
#
# Special thanks go to Sam Shen(SLShen@lbl.gov), as this code is based on his
# help.tcl code from tk inspect.
#
# Default resources.
#
option add *Hyperhelp.width 575 widgetDefault
option add *Hyperhelp.height 450 widgetDefault
option add *Hyperhelp.modality none widgetDefault
option add *Hyperhelp.vscrollMode static widgetDefault
option add *Hyperhelp.hscrollMode static widgetDefault
option add *Hyperhelp.maxHistory 20 widgetDefault
#
# Usual options.
#
itk::usual Hyperhelp {
keep -activebackground -activerelief -background -borderwidth -cursor \
-foreground -highlightcolor -highlightthickness \
-selectbackground -selectborderwidth -selectforeground \
-textbackground
}
# ------------------------------------------------------------------
# HYPERHELP
# ------------------------------------------------------------------
itcl::class iwidgets::Hyperhelp {
inherit iwidgets::Shell
constructor {args} {}
itk_option define -topics topics Topics {}
itk_option define -helpdir helpdir Directory .
itk_option define -title title Title "Help"
itk_option define -closecmd closeCmd CloseCmd {}
itk_option define -maxhistory maxHistory MaxHistory 20
public variable beforelink {}
public variable afterlink {}
public method showtopic {topic}
public method followlink {link}
public method forward {}
public method back {}
public method updatefeedback {n}
protected method _readtopic {file {anchorpoint {}}}
protected method _pageforward {}
protected method _pageback {}
protected method _lineforward {}
protected method _lineback {}
protected method _fill_go_menu {}
protected variable _history {} ;# History list of viewed pages
protected variable _history_ndx -1 ;# current position in history list
protected variable _history_len 0 ;# length of history list
protected variable _histdir -1 ;# direction in history we just came
;# from
protected variable _len 0 ;# length of text to be rendered
protected variable _file {} ;# current topic
private variable _remaining 0 ;# remaining text to be rendered
private variable _rendering 0 ;# flag - in process of rendering
}
#
# Provide a lowercased access method for the Scrolledlistbox class.
#
proc ::iwidgets::hyperhelp {pathName args} {
uplevel ::iwidgets::Hyperhelp $pathName $args
}
# ------------------------------------------------------------------
# CONSTRUCTOR
# ------------------------------------------------------------------
itcl::body iwidgets::Hyperhelp::constructor {args} {
itk_option remove iwidgets::Shell::padx iwidgets::Shell::pady
#
# Create a pulldown menu
#
itk_component add -private menubar {
frame $itk_interior.menu -relief raised -bd 2
} {
keep -background -cursor
}
pack $itk_component(menubar) -side top -fill x
itk_component add -private topicmb {
menubutton $itk_component(menubar).topicmb -text "Topics" \
-menu $itk_component(menubar).topicmb.topicmenu \
-underline 0 -padx 8 -pady 2
} {
keep -background -cursor -font -foreground \
-activebackground -activeforeground
}
pack $itk_component(topicmb) -side left
itk_component add -private topicmenu {
menu $itk_component(topicmb).topicmenu -tearoff no
} {
keep -background -cursor -font -foreground \
-activebackground -activeforeground
}
itk_component add -private navmb {
menubutton $itk_component(menubar).navmb -text "Navigate" \
-menu $itk_component(menubar).navmb.navmenu \
-underline 0 -padx 8 -pady 2
} {
keep -background -cursor -font -foreground \
-activebackground -activeforeground
}
pack $itk_component(navmb) -side left
itk_component add -private navmenu {
menu $itk_component(navmb).navmenu -tearoff no
} {
keep -background -cursor -font -foreground \
-activebackground -activeforeground
}
set m $itk_component(navmenu)
$m add command -label "Forward" -underline 0 -state disabled \
-command [itcl::code $this forward] -accelerator f
$m add command -label "Back" -underline 0 -state disabled \
-command [itcl::code $this back] -accelerator b
$m add cascade -label "Go" -underline 0 -menu $m.go
itk_component add -private navgo {
menu $itk_component(navmenu).go -postcommand [itcl::code $this _fill_go_menu]
} {
keep -background -cursor -font -foreground \
-activebackground -activeforeground
}
#
# Create a scrolledhtml object to display help pages
#
itk_component add scrtxt {
iwidgets::scrolledhtml $itk_interior.scrtxt \
-linkcommand "$this followlink" -feedback "$this updatefeedback"
} {
keep -hscrollmode -vscrollmode -background -textbackground \
-fontname -fontsize -fixedfont -link \
-linkhighlight -borderwidth -cursor -sbwidth -scrollmargin \
-width -height -foreground -highlightcolor -visibleitems \
-highlightthickness -padx -pady -activerelief \
-relief -selectbackground -selectborderwidth \
-selectforeground -setgrid -wrap -unknownimage
}
pack $itk_component(scrtxt) -fill both -expand yes
#
# Bind shortcut keys
#
bind $itk_component(hull) <Key-f> [itcl::code $this forward]
bind $itk_component(hull) <Key-b> [itcl::code $this back]
bind $itk_component(hull) <Alt-Right> [itcl::code $this forward]
bind $itk_component(hull) <Alt-Left> [itcl::code $this back]
bind $itk_component(hull) <Key-space> [itcl::code $this _pageforward]
bind $itk_component(hull) <Key-Next> [itcl::code $this _pageforward]
bind $itk_component(hull) <Key-BackSpace> [itcl::code $this _pageback]
bind $itk_component(hull) <Key-Prior> [itcl::code $this _pageback]
bind $itk_component(hull) <Key-Delete> [itcl::code $this _pageback]
bind $itk_component(hull) <Key-Down> [itcl::code $this _lineforward]
bind $itk_component(hull) <Key-Up> [itcl::code $this _lineback]
wm title $itk_component(hull) "Help"
eval itk_initialize $args
if {[lsearch -exact $args -closecmd] == -1} {
configure -closecmd [itcl::code $this deactivate]
}
}
# ------------------------------------------------------------------
# OPTIONS
# ------------------------------------------------------------------
# ------------------------------------------------------------------
# OPTION: -topics
#
# Specifies the topics to display on the menu. For each topic, there should
# be a file named <helpdir>/<topic>.html
# ------------------------------------------------------------------
itcl::configbody iwidgets::Hyperhelp::topics {
set m $itk_component(topicmenu)
$m delete 0 last
foreach topic $itk_option(-topics) {
if {[lindex $topic 1] == {} } {
$m add radiobutton -variable topic \
-value $topic \
-label $topic \
-command [list $this showtopic $topic]
} else {
if {[string index [file dirname [lindex $topic 1]] 0] != "/" && \
[string index [file dirname [lindex $topic 1]] 0] != "~"} {
set link $itk_option(-helpdir)/[lindex $topic 1]
} else {
set link [lindex $topic 1]
}
$m add radiobutton -variable topic \
-value [lindex $topic 0] \
-label [lindex $topic 0] \
-command [list $this followlink $link]
}
}
$m add separator
$m add command -label "Close Help" -underline 0 \
-command $itk_option(-closecmd)
}
# ------------------------------------------------------------------
# OPTION: -title
#
# Specify the window title.
# ------------------------------------------------------------------
itcl::configbody iwidgets::Hyperhelp::title {
wm title $itk_component(hull) $itk_option(-title)
}
# ------------------------------------------------------------------
# OPTION: -helpdir
#
# Set location of help files
# ------------------------------------------------------------------
itcl::configbody iwidgets::Hyperhelp::helpdir {
if {[file pathtype $itk_option(-helpdir)] == "relative"} {
configure -helpdir [file join [pwd] $itk_option(-helpdir)]
} else {
set _history {}
set _history_len 0
set _history_ndx -1
$itk_component(navmenu) entryconfig 0 -state disabled
$itk_component(navmenu) entryconfig 1 -state disabled
configure -topics $itk_option(-topics)
}
}
# ------------------------------------------------------------------
# OPTION: -closecmd
#
# Specify the command to execute when close is selected from the menu
# ------------------------------------------------------------------
itcl::configbody iwidgets::Hyperhelp::closecmd {
$itk_component(topicmenu) entryconfigure last -command $itk_option(-closecmd)
}
# ------------------------------------------------------------------
# METHODS
# ------------------------------------------------------------------
# ------------------------------------------------------------------
# METHOD: showtopic topic
#
# render text of help topic <topic>. The text is expected to be found in
# <helpdir>/<topic>.html
# ------------------------------------------------------------------
itcl::body iwidgets::Hyperhelp::showtopic {topic} {
if ![regexp {(.*)#(.*)} $topic dummy topicname anchorpart] {
set topicname $topic
set anchorpart {}
}
if {$topicname == ""} {
set topicname $_file
set filepath $_file
} else {
set filepath $itk_option(-helpdir)/$topicname.html
}
if {[incr _history_ndx] < $itk_option(-maxhistory)} {
set _history [lrange $_history 0 [expr {$_history_ndx - 1}]]
set _history_len [expr {$_history_ndx + 1}]
} else {
incr _history_ndx -1
set _history [lrange $_history 1 $_history_ndx]
set _history_len [expr {$_history_ndx + 1}]
}
lappend _history [list $topicname $filepath $anchorpart]
_readtopic $filepath $anchorpart
}
# ------------------------------------------------------------------
# METHOD: followlink link
#
# Callback for click on a link. Shows new topic.
# ------------------------------------------------------------------
itcl::body iwidgets::Hyperhelp::followlink {link} {
if {[string compare $beforelink ""] != 0} {
eval $beforelink $link
}
if ![regexp {(.*)#(.*)} $link dummy filepart anchorpart] {
set filepart $link
set anchorpart {}
}
if {$filepart != "" && [string index [file dirname $filepart] 0] != "/" && \
[string index [file dirname $filepart] 0] != "~"} {
set filepart [$itk_component(scrtxt) pwd]/$filepart
set hfile $filepart
} else {
set hfile $_file
}
incr _history_ndx
set _history [lrange $_history 0 [expr {$_history_ndx - 1}]]
set _history_len [expr {$_history_ndx + 1}]
lappend _history [list [file rootname [file tail $hfile]] $hfile $anchorpart]
set ret [_readtopic $filepart $anchorpart]
if {[string compare $afterlink ""] != 0} {
eval $afterlink $link
}
return $ret
}
# ------------------------------------------------------------------
# METHOD: forward
#
# Show topic one forward in history list
# ------------------------------------------------------------------
itcl::body iwidgets::Hyperhelp::forward {} {
if {$_rendering || ($_history_ndx+1) >= $_history_len} return
incr _history_ndx
eval _readtopic [lrange [lindex $_history $_history_ndx] 1 end]
}
# ------------------------------------------------------------------
# METHOD: back
#
# Show topic one back in history list
# ------------------------------------------------------------------
itcl::body iwidgets::Hyperhelp::back {} {
if {$_rendering || $_history_ndx <= 0} return
incr _history_ndx -1
set _histdir 1
eval _readtopic [lrange [lindex $_history $_history_ndx] 1 end]
}
# ------------------------------------------------------------------
# METHOD: updatefeedback remaining
#
# Callback from text to update feedback widget
# ------------------------------------------------------------------
itcl::body iwidgets::Hyperhelp::updatefeedback {n} {
if {($_remaining - $n) > .1*$_len} {
[$itk_interior.feedbackshell childsite].helpfeedback step [expr {$_remaining - $n}]
update idletasks
set _remaining $n
}
}
# ------------------------------------------------------------------
# PRIVATE METHOD: _readtopic
#
# Read in file, render it in text area, and jump to anchorpoint
# ------------------------------------------------------------------
itcl::body iwidgets::Hyperhelp::_readtopic {file {anchorpoint {}}} {
if {$file != ""} {
if {[string compare $file $_file] != 0} {
if {[catch {set f [open $file r]} err]} {
incr _history_ndx $_histdir
set _history_len [expr {$_history_ndx + 1}]
set _histdir -1
set m $itk_component(navmenu)
if {($_history_ndx+1) < $_history_len} {
$m entryconfig 0 -state normal
} else {
$m entryconfig 0 -state disabled
}
if {$_history_ndx > 0} {
$m entryconfig 1 -state normal
} else {
$m entryconfig 1 -state disabled
}
return
}
set _file $file
set txt [read $f]
iwidgets::shell $itk_interior.feedbackshell -title \
"Rendering HTML" -padx 1 -pady 1
iwidgets::Feedback [$itk_interior.feedbackshell \
childsite].helpfeedback \
-steps [set _len [string length $txt]] \
-labeltext "Rendering HTML" -labelpos n
pack [$itk_interior.feedbackshell childsite].helpfeedback
$itk_interior.feedbackshell center $itk_interior
$itk_interior.feedbackshell activate
set _remaining $_len
set _rendering 1
if {[catch {$itk_component(scrtxt) render $txt [file dirname \
$file]} err]} {
if [regexp "</pre>" $err] {
$itk_component(scrtxt) render "<tt>$err</tt>"
} else {
$itk_component(scrtxt) render "<pre>$err</pre>"
}
}
wm title $itk_component(hull) "Help: $file"
itcl::delete object [$itk_interior.feedbackshell \
childsite].helpfeedback
itcl::delete object $itk_interior.feedbackshell
set _rendering 0
}
}
set m $itk_component(navmenu)
if {($_history_ndx+1) < $_history_len} {
$m entryconfig 0 -state normal
} else {
$m entryconfig 0 -state disabled
}
if {$_history_ndx > 0} {
$m entryconfig 1 -state normal
} else {
$m entryconfig 1 -state disabled
}
if {$anchorpoint != {}} {
$itk_component(scrtxt) import -link #$anchorpoint
} else {
$itk_component(scrtxt) import -link #
}
set _histdir -1
}
# ------------------------------------------------------------------
# PRIVATE METHOD: _fill_go_menu
#
# update go submenu with current history
# ------------------------------------------------------------------
itcl::body iwidgets::Hyperhelp::_fill_go_menu {} {
set m $itk_component(navgo)
catch {$m delete 0 last}
for {set i [expr {$_history_len - 1}]} {$i >= 0} {incr i -1} {
set topic [lindex [lindex $_history $i] 0]
set filepath [lindex [lindex $_history $i] 1]
set anchor [lindex [lindex $_history $i] 2]
$m add command -label $topic \
-command [list $this followlink $filepath#$anchor]
}
}
# ------------------------------------------------------------------
# PRIVATE METHOD: _pageforward
#
# Callback for page forward shortcut key
# ------------------------------------------------------------------
itcl::body iwidgets::Hyperhelp::_pageforward {} {
$itk_component(scrtxt) yview scroll 1 pages
}
# ------------------------------------------------------------------
# PRIVATE METHOD: _pageback
#
# Callback for page back shortcut key
# ------------------------------------------------------------------
itcl::body iwidgets::Hyperhelp::_pageback {} {
$itk_component(scrtxt) yview scroll -1 pages
}
# ------------------------------------------------------------------
# PRIVATE METHOD: _lineforward
#
# Callback for line forward shortcut key
# ------------------------------------------------------------------
itcl::body iwidgets::Hyperhelp::_lineforward {} {
$itk_component(scrtxt) yview scroll 1 units
}
# ------------------------------------------------------------------
# PRIVATE METHOD: _lineback
#
# Callback for line back shortcut key
# ------------------------------------------------------------------
itcl::body iwidgets::Hyperhelp::_lineback {} {
$itk_component(scrtxt) yview scroll -1 units
}