html_library.tcl 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418
  1. # Simple HTML display library by Stephen Uhler (stephen.uhler@sun.com)
  2. # Copyright (c) 1995 by Sun Microsystems
  3. # Version 0.3 Fri Sep 1 10:47:17 PDT 1995
  4. #
  5. # See the file "license.terms" for information on usage and redistribution
  6. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  7. #
  8. # To use this package, create a text widget (say, .text)
  9. # and set a variable full of html, (say $html), and issue:
  10. # HMinit_win .text
  11. # HMparse_html $html "HMrender .text"
  12. # You also need to supply the routine:
  13. # proc HMlink_callback {win href} { ...}
  14. # win: The name of the text widget
  15. # href The name of the link
  16. # which will be called anytime the user "clicks" on a link.
  17. # The supplied version just prints the link to stdout.
  18. # In addition, if you wish to use embedded images, you will need to write
  19. # proc HMset_image {handle src}
  20. # handle an arbitrary handle (not really)
  21. # src The name of the image
  22. # Which calls
  23. # HMgot_image $handle $image
  24. # with the TK image.
  25. #
  26. # To return a "used" text widget to its initialized state, call:
  27. # HMreset_win .text
  28. # See "sample.tcl" for sample usage
  29. ##################################################################
  30. ############################################
  31. # mapping of html tags to text tag properties
  32. # properties beginning with "T" map directly to text tags
  33. # These are Defined in HTML 2.0
  34. array set HMtag_map {
  35. b {weight bold}
  36. blockquote {style i indent 1 Trindent rindent}
  37. bq {style i indent 1 Trindent rindent}
  38. cite {style i}
  39. code {family courier}
  40. dfn {style i}
  41. dir {indent 1}
  42. dl {indent 1}
  43. em {style i}
  44. h1 {size 24 weight bold}
  45. h2 {size 22}
  46. h3 {size 20}
  47. h4 {size 18}
  48. h5 {size 16}
  49. h6 {style i}
  50. i {style i}
  51. kbd {family courier weight bold}
  52. menu {indent 1}
  53. ol {indent 1}
  54. pre {fill 0 family courier Tnowrap nowrap}
  55. samp {family courier}
  56. strong {weight bold}
  57. tt {family courier}
  58. u {Tunderline underline}
  59. ul {indent 1}
  60. var {style i}
  61. }
  62. # These are in common(?) use, but not defined in html2.0
  63. array set HMtag_map {
  64. center {Tcenter center}
  65. strike {Tstrike strike}
  66. u {Tunderline underline}
  67. }
  68. # initial values
  69. set HMtag_map(hmstart) {
  70. family times weight medium style r size 14
  71. Tcenter "" Tlink "" Tnowrap "" Tunderline "" list list
  72. fill 1 indent "" counter 0 adjust 0
  73. }
  74. # html tags that insert white space
  75. array set HMinsert_map {
  76. blockquote "\n\n" /blockquote "\n"
  77. br "\n"
  78. dd "\n" /dd "\n"
  79. dl "\n" /dl "\n"
  80. dt "\n"
  81. form "\n" /form "\n"
  82. h1 "\n\n" /h1 "\n"
  83. h2 "\n\n" /h2 "\n"
  84. h3 "\n\n" /h3 "\n"
  85. h4 "\n" /h4 "\n"
  86. h5 "\n" /h5 "\n"
  87. h6 "\n" /h6 "\n"
  88. li "\n"
  89. /dir "\n"
  90. /ul "\n"
  91. /ol "\n"
  92. /menu "\n"
  93. p "\n\n"
  94. pre "\n" /pre "\n"
  95. }
  96. # tags that are list elements, that support "compact" rendering
  97. array set HMlist_elements {
  98. ol 1 ul 1 menu 1 dl 1 dir 1
  99. }
  100. ############################################
  101. # initialize the window and stack state
  102. proc HMinit_win {win} {
  103. upvar #0 HM$win var
  104. HMinit_state $win
  105. $win tag configure underline -underline 1
  106. $win tag configure center -justify center
  107. $win tag configure nowrap -wrap none
  108. $win tag configure rindent -rmargin $var(S_tab)c
  109. $win tag configure strike -overstrike 1
  110. $win tag configure mark -foreground red ;# list markers
  111. $win tag configure list -spacing1 3p -spacing3 3p ;# regular lists
  112. $win tag configure compact -spacing1 0p ;# compact lists
  113. $win tag configure link -borderwidth 2 -foreground blue ;# hypertext links
  114. HMset_indent $win $var(S_tab)
  115. $win configure -wrap word
  116. # configure the text insertion point
  117. $win mark set $var(S_insert) 1.0
  118. # for horizontal rules
  119. $win tag configure thin -font [HMx_font times 2 medium r]
  120. $win tag configure hr -relief sunken -borderwidth 2 -wrap none \
  121. -tabs [winfo width $win]
  122. bind $win <Configure> {
  123. %W tag configure hr -tabs %w
  124. %W tag configure last -spacing3 %h
  125. }
  126. # generic link enter callback
  127. $win tag bind link <1> "HMlink_hit $win %x %y"
  128. }
  129. # set the indent spacing (in cm) for lists
  130. # TK uses a "weird" tabbing model that causes \t to insert a single
  131. # space if the current line position is past the tab setting
  132. proc HMset_indent {win cm} {
  133. set tabs [expr $cm / 2.0]
  134. $win configure -tabs ${tabs}c
  135. foreach i {1 2 3 4 5 6 7 8 9} {
  136. set tab [expr $i * $cm]
  137. $win tag configure indent$i -lmargin1 ${tab}c -lmargin2 ${tab}c \
  138. -tabs "[expr $tab + $tabs]c [expr $tab + 2*$tabs]c"
  139. }
  140. }
  141. # reset the state of window - get ready for the next page
  142. # remove all but the font tags, and remove all form state
  143. proc HMreset_win {win} {
  144. upvar #0 HM$win var
  145. regsub -all { +[^L ][^ ]*} " [$win tag names] " {} tags
  146. catch "$win tag delete $tags"
  147. eval $win mark unset [$win mark names]
  148. $win delete 0.0 end
  149. $win tag configure hr -tabs [winfo width $win]
  150. # configure the text insertion point
  151. $win mark set $var(S_insert) 1.0
  152. # remove form state. If any check/radio buttons still exists,
  153. # their variables will be magically re-created, and never get
  154. # cleaned up.
  155. catch unset [info globals HM$win.form*]
  156. HMinit_state $win
  157. return HM$win
  158. }
  159. # initialize the window's state array
  160. # Parameters beginning with S_ are NOT reset
  161. # adjust_size: global font size adjuster
  162. # unknown: character to use for unknown entities
  163. # tab: tab stop (in cm)
  164. # stop: enabled to stop processing
  165. # update: how many tags between update calls
  166. # tags: number of tags processed so far
  167. # symbols: Symbols to use on un-ordered lists
  168. proc HMinit_state {win} {
  169. upvar #0 HM$win var
  170. array set tmp [array get var S_*]
  171. catch {unset var}
  172. array set var {
  173. stop 0
  174. tags 0
  175. fill 0
  176. list list
  177. S_adjust_size 0
  178. S_tab 1.0
  179. S_unknown \xb7
  180. S_update 10
  181. S_symbols O*=+-o\xd7\xb0>:\xb7
  182. S_insert Insert
  183. }
  184. array set var [array get tmp]
  185. }
  186. # alter the parameters of the text state
  187. # this allows an application to over-ride the default settings
  188. # it is called as: HMset_state -param value -param value ...
  189. array set HMparam_map {
  190. -update S_update
  191. -tab S_tab
  192. -unknown S_unknown
  193. -stop S_stop
  194. -size S_adjust_size
  195. -symbols S_symbols
  196. -insert S_insert
  197. }
  198. proc HMset_state {win args} {
  199. upvar #0 HM$win var
  200. global HMparam_map
  201. set bad 0
  202. if {[catch {array set params $args}]} {return 0}
  203. foreach i [array names params] {
  204. incr bad [catch {set var($HMparam_map($i)) $params($i)}]
  205. }
  206. return [expr $bad == 0]
  207. }
  208. ############################################
  209. # manage the display of html
  210. # HMrender gets called for every html tag
  211. # win: The name of the text widget to render into
  212. # tag: The html tag (in arbitrary case)
  213. # not: a "/" or the empty string
  214. # param: The un-interpreted parameter list
  215. # text: The plain text until the next html tag
  216. proc HMrender {win tag not param text} {
  217. upvar #0 HM$win var
  218. if {$var(stop)} return
  219. global HMtag_map HMinsert_map HMlist_elements
  220. set tag [string tolower $tag]
  221. set text [HMmap_esc $text]
  222. # manage compact rendering of lists
  223. if {[info exists HMlist_elements($tag)]} {
  224. set list "list [expr {[HMextract_param $param compact] ? "compact" : "list"}]"
  225. } else {
  226. set list ""
  227. }
  228. # Allow text to be diverted to a different window (for tables)
  229. # this is not currently used
  230. if {[info exists var(divert)]} {
  231. set win $var(divert)
  232. upvar #0 HM$win var
  233. }
  234. # adjust (push or pop) tag state
  235. catch {HMstack $win $not "$HMtag_map($tag) $list"}
  236. # insert white space (with current font)
  237. # adding white space can get a bit tricky. This isn't quite right
  238. set bad [catch {$win insert $var(S_insert) $HMinsert_map($not$tag) "space $var(font)"}]
  239. if {!$bad && [lindex $var(fill) end]} {
  240. set text [string trimleft $text]
  241. }
  242. # to fill or not to fill
  243. if {[lindex $var(fill) end]} {
  244. set text [HMzap_white $text]
  245. }
  246. # generic mark hook
  247. catch {HMmark $not$tag $win $param text} err
  248. # do any special tag processing
  249. catch {HMtag_$not$tag $win $param text} msg
  250. # add the text with proper tags
  251. set tags [HMcurrent_tags $win]
  252. $win insert $var(S_insert) $text $tags
  253. # We need to do an update every so often to insure interactive response.
  254. # This can cause us to re-enter the event loop, and cause recursive
  255. # invocations of HMrender, so we need to be careful.
  256. if {!([incr var(tags)] % $var(S_update))} {
  257. update
  258. }
  259. }
  260. # html tags requiring special processing
  261. # Procs of the form HMtag_<tag> or HMtag_</tag> get called just before
  262. # the text for this tag is displayed. These procs are called inside a
  263. # "catch" so it is OK to fail.
  264. # win: The name of the text widget to render into
  265. # param: The un-interpreted parameter list
  266. # text: A pass-by-reference name of the plain text until the next html tag
  267. # Tag commands may change this to affect what text will be inserted
  268. # next.
  269. # A pair of pseudo tags are added automatically as the 1st and last html
  270. # tags in the document. The default is <HMstart> and </HMstart>.
  271. # Append enough blank space at the end of the text widget while
  272. # rendering so HMgoto can place the target near the top of the page,
  273. # then remove the extra space when done rendering.
  274. proc HMtag_hmstart {win param text} {
  275. upvar #0 HM$win var
  276. $win mark gravity $var(S_insert) left
  277. $win insert end "\n " last
  278. $win mark gravity $var(S_insert) right
  279. }
  280. proc HMtag_/hmstart {win param text} {
  281. $win delete last.first end
  282. }
  283. # put the document title in the window banner, and remove the title text
  284. # from the document
  285. proc HMtag_title {win param text} {
  286. upvar $text data
  287. wm title [winfo toplevel $win] $data
  288. set data ""
  289. }
  290. proc HMtag_hr {win param text} {
  291. upvar #0 HM$win var
  292. $win insert $var(S_insert) "\n" space "\n" thin "\t" "thin hr" "\n" thin
  293. }
  294. # list element tags
  295. proc HMtag_ol {win param text} {
  296. upvar #0 HM$win var
  297. set var(count$var(level)) 0
  298. }
  299. proc HMtag_ul {win param text} {
  300. upvar #0 HM$win var
  301. catch {unset var(count$var(level))}
  302. }
  303. proc HMtag_menu {win param text} {
  304. upvar #0 HM$win var
  305. set var(menu) ->
  306. set var(compact) 1
  307. }
  308. proc HMtag_/menu {win param text} {
  309. upvar #0 HM$win var
  310. catch {unset var(menu)}
  311. catch {unset var(compact)}
  312. }
  313. proc HMtag_dt {win param text} {
  314. upvar #0 HM$win var
  315. upvar $text data
  316. set level $var(level)
  317. incr level -1
  318. $win insert $var(S_insert) "$data" \
  319. "hi [lindex $var(list) end] indent$level $var(font)"
  320. set data {}
  321. }
  322. proc HMtag_li {win param text} {
  323. upvar #0 HM$win var
  324. set level $var(level)
  325. incr level -1
  326. set x [string index $var(S_symbols)+-+-+-+-" $level]
  327. catch {set x [incr var(count$level)]}
  328. catch {set x $var(menu)}
  329. $win insert $var(S_insert) \t$x\t "mark [lindex $var(list) end] indent$level $var(font)"
  330. }
  331. # Manage hypertext "anchor" links. A link can be either a source (href)
  332. # a destination (name) or both. If its a source, register it via a callback,
  333. # and set its default behavior. If its a destination, check to see if we need
  334. # to go there now, as a result of a previous HMgoto request. If so, schedule
  335. # it to happen with the closing </a> tag, so we can highlight the text up to
  336. # the </a>.
  337. proc HMtag_a {win param text} {
  338. upvar #0 HM$win var
  339. # a source
  340. if {[HMextract_param $param href]} {
  341. set var(Tref) [list L:$href]
  342. HMstack $win "" "Tlink link"
  343. HMlink_setup $win $href
  344. }
  345. # a destination
  346. if {[HMextract_param $param name]} {
  347. set var(Tname) [list N:$name]
  348. HMstack $win "" "Tanchor anchor"
  349. $win mark set N:$name "$var(S_insert) - 1 chars"
  350. $win mark gravity N:$name left
  351. if {[info exists var(goto)] && $var(goto) == $name} {
  352. unset var(goto)
  353. set var(going) $name
  354. }
  355. }
  356. }
  357. # The application should call here with the fragment name
  358. # to cause the display to go to this spot.
  359. # If the target exists, go there (and do the callback),
  360. # otherwise schedule the goto to happen when we see the reference.
  361. proc HMgoto {win where {callback HMwent_to}} {
  362. upvar #0 HM$win var
  363. if {[regexp N:$where [$win mark names]]} {
  364. $win see N:$where
  365. update
  366. eval $callback $win [list $where]
  367. return 1
  368. } else {
  369. set var(goto) $where
  370. return 0
  371. }
  372. }
  373. # We actually got to the spot, so highlight it!
  374. # This should/could be replaced by the application
  375. # We'll flash it orange a couple of times.
  376. proc HMwent_to {win where {count 0} {color orange}} {
  377. upvar #0 HM$win var
  378. if {$count > 5} return
  379. catch {$win tag configure N:$where -foreground $color}
  380. update
  381. after 200 [list HMwent_to $win $where [incr count] \
  382. [expr {$color=="orange" ? "" : "orange"}]]
  383. }
  384. proc HMtag_/a {win param text} {
  385. upvar #0 HM$win var
  386. if {[info exists var(Tref)]} {
  387. unset var(Tref)
  388. HMstack $win / "Tlink link"
  389. }
  390. # goto this link, then invoke the call-back.
  391. if {[info exists var(going)]} {
  392. $win yview N:$var(going)
  393. update
  394. HMwent_to $win $var(going)
  395. unset var(going)
  396. }
  397. if {[info exists var(Tname)]} {
  398. unset var(Tname)
  399. HMstack $win / "Tanchor anchor"
  400. }
  401. }
  402. # Inline Images
  403. # This interface is subject to change
  404. # Most of the work is getting around a limitation of TK that prevents
  405. # setting the size of a label to a widthxheight in pixels
  406. #
  407. # Images have the following parameters:
  408. # align: top,middle,bottom
  409. # alt: alternate text
  410. # ismap: A clickable image map
  411. # src: The URL link
  412. # Netscape supports (and so do we)
  413. # width: A width hint (in pixels)
  414. # height: A height hint (in pixels)
  415. # border: The size of the window border
  416. proc HMtag_img {win param text} {
  417. upvar #0 HM$win var
  418. # get alignment
  419. array set align_map {top top middle center bottom bottom}
  420. set align bottom ;# The spec isn't clear what the default should be
  421. HMextract_param $param align
  422. catch {set align $align_map([string tolower $align])}
  423. # get alternate text
  424. set alt "<image>"
  425. HMextract_param $param alt
  426. set alt [HMmap_esc $alt]
  427. # get the border width
  428. set border 1
  429. HMextract_param $param border
  430. # see if we have an image size hint
  431. # If so, make a frame the "hint" size to put the label in
  432. # otherwise just make the label
  433. set item $win.$var(tags)
  434. # catch {destroy $item}
  435. if {[HMextract_param $param width] && [HMextract_param $param height]} {
  436. frame $item -width $width -height $height
  437. pack propagate $item 0
  438. set label $item.label
  439. label $label
  440. pack $label -expand 1 -fill both
  441. } else {
  442. set label $item
  443. label $label
  444. }
  445. $label configure -relief ridge -fg orange -text $alt
  446. catch {$label configure -bd $border}
  447. $win window create $var(S_insert) -align $align -window $item -pady 2 -padx 2
  448. # add in all the current tags (this is overkill)
  449. set tags [HMcurrent_tags $win]
  450. foreach tag $tags {
  451. $win tag add $tag $item
  452. }
  453. # set imagemap callbacks
  454. if {[HMextract_param $param ismap]} {
  455. # regsub -all {[^L]*L:([^ ]*).*} $tags {\1} link
  456. set link [lindex $tags [lsearch -glob $tags L:*]]
  457. regsub L: $link {} link
  458. global HMevents
  459. regsub -all {%} $link {%%} link2
  460. foreach i [array names HMevents] {
  461. bind $label <$i> "catch \{%W configure $HMevents($i)\}"
  462. }
  463. bind $label <1> "+HMlink_callback $win $link2?%x,%y"
  464. }
  465. # now callback to the application
  466. set src ""
  467. HMextract_param $param src
  468. HMset_image $win $label $src
  469. return $label ;# used by the forms package for input_image types
  470. }
  471. # The app needs to supply one of these
  472. proc HMset_image {win handle src} {
  473. HMgot_image $handle "can't get\n$src"
  474. }
  475. # When the image is available, the application should call back here.
  476. # If we have the image, put it in the label, otherwise display the error
  477. # message. If we don't get a callback, the "alt" text remains.
  478. # if we have a clickable image, arrange for a callback
  479. proc HMgot_image {win image_error} {
  480. # if we're in a frame turn on geometry propogation
  481. if {[winfo name $win] == "label"} {
  482. pack propagate [winfo parent $win] 1
  483. }
  484. if {[catch {$win configure -image $image_error}]} {
  485. $win configure -image {}
  486. $win configure -text $image_error
  487. }
  488. }
  489. # Sample hypertext link callback routine - should be replaced by app
  490. # This proc is called once for each <A> tag.
  491. # Applications can overwrite this procedure, as required, or
  492. # replace the HMevents array
  493. # win: The name of the text widget to render into
  494. # href: The HREF link for this <a> tag.
  495. array set HMevents {
  496. Enter {-borderwidth 2 -relief raised }
  497. Leave {-borderwidth 2 -relief flat }
  498. 1 {-borderwidth 2 -relief sunken}
  499. ButtonRelease-1 {-borderwidth 2 -relief raised}
  500. }
  501. # We need to escape any %'s in the href tag name so the bind command
  502. # doesn't try to substitute them.
  503. proc HMlink_setup {win href} {
  504. global HMevents
  505. regsub -all {%} $href {%%} href2
  506. foreach i [array names HMevents] {
  507. eval {$win tag bind L:$href <$i>} \
  508. \{$win tag configure \{L:$href2\} $HMevents($i)\}
  509. }
  510. }
  511. # generic link-hit callback
  512. # This gets called upon button hits on hypertext links
  513. # Applications are expected to supply ther own HMlink_callback routine
  514. # win: The name of the text widget to render into
  515. # x,y: The cursor position at the "click"
  516. proc HMlink_hit {win x y} {
  517. set tags [$win tag names @$x,$y]
  518. set link [lindex $tags [lsearch -glob $tags L:*]]
  519. # regsub -all {[^L]*L:([^ ]*).*} $tags {\1} link
  520. regsub L: $link {} link
  521. HMlink_callback $win $link
  522. }
  523. # replace this!
  524. # win: The name of the text widget to render into
  525. # href: The HREF link for this <a> tag.
  526. proc HMlink_callback {win href} {
  527. puts "Got hit on $win, link $href"
  528. }
  529. # extract a value from parameter list (this needs a re-do)
  530. # returns "1" if the keyword is found, "0" otherwise
  531. # param: A parameter list. It should alredy have been processed to
  532. # remove any entity references
  533. # key: The parameter name
  534. # val: The variable to put the value into (use key as default)
  535. proc HMextract_param {param key {val ""}} {
  536. if {$val == ""} {
  537. upvar $key result
  538. } else {
  539. upvar $val result
  540. }
  541. set ws " \n\r"
  542. # look for name=value combinations. Either (') or (") are valid delimeters
  543. if {
  544. [regsub -nocase [format {.*%s[%s]*=[%s]*"([^"]*).*} $key $ws $ws] $param {\1} value] ||
  545. [regsub -nocase [format {.*%s[%s]*=[%s]*'([^']*).*} $key $ws $ws] $param {\1} value] ||
  546. [regsub -nocase [format {.*%s[%s]*=[%s]*([^%s]+).*} $key $ws $ws $ws] $param {\1} value] } {
  547. set result $value
  548. return 1
  549. }
  550. # now look for valueless names
  551. # I should strip out name=value pairs, so we don't end up with "name"
  552. # inside the "value" part of some other key word - some day
  553. set bad \[^a-zA-Z\]+
  554. if {[regexp -nocase "$bad$key$bad" -$param-]} {
  555. return 1
  556. } else {
  557. return 0
  558. }
  559. }
  560. # These next two routines manage the display state of the page.
  561. # Push or pop tags to/from stack.
  562. # Each orthogonal text property has its own stack, stored as a list.
  563. # The current (most recent) tag is the last item on the list.
  564. # Push is {} for pushing and {/} for popping
  565. proc HMstack {win push list} {
  566. upvar #0 HM$win var
  567. array set tags $list
  568. if {$push == ""} {
  569. foreach tag [array names tags] {
  570. lappend var($tag) $tags($tag)
  571. }
  572. } else {
  573. foreach tag [array names tags] {
  574. # set cnt [regsub { *[^ ]+$} $var($tag) {} var($tag)]
  575. set var($tag) [lreplace $var($tag) end end]
  576. }
  577. }
  578. }
  579. # extract set of current text tags
  580. # tags starting with T map directly to text tags, all others are
  581. # handled specially. There is an application callback, HMset_font
  582. # to allow the application to do font error handling
  583. proc HMcurrent_tags {win} {
  584. upvar #0 HM$win var
  585. set font font
  586. foreach i {family size weight style} {
  587. set $i [lindex $var($i) end]
  588. append font :[set $i]
  589. }
  590. set xfont [HMx_font $family $size $weight $style $var(S_adjust_size)]
  591. HMset_font $win $font $xfont
  592. set indent [llength $var(indent)]
  593. incr indent -1
  594. lappend tags $font indent$indent
  595. foreach tag [array names var T*] {
  596. lappend tags [lindex $var($tag) end] ;# test
  597. }
  598. set var(font) $font
  599. set var(xfont) [$win tag cget $font -font]
  600. set var(level) $indent
  601. return $tags
  602. }
  603. # allow the application to do do better font management
  604. # by overriding this procedure
  605. proc HMset_font {win tag font} {
  606. catch {$win tag configure $tag -font $font} msg
  607. }
  608. # generate an X font name
  609. proc HMx_font {family size weight style {adjust_size 0}} {
  610. catch {incr size $adjust_size}
  611. return "-*-$family-$weight-$style-normal-*-*-${size}0-*-*-*-*-*-*"
  612. }
  613. # Optimize HMrender (hee hee)
  614. # This is experimental
  615. proc HMoptimize {} {
  616. regsub -all "\n\[ \]*#\[^\n\]*" [info body HMrender] {} body
  617. regsub -all ";\[ \]*#\[^\n]*" $body {} body
  618. regsub -all "\n\n+" $body \n body
  619. proc HMrender {win tag not param text} $body
  620. }
  621. ############################################
  622. # Turn HTML into TCL commands
  623. # html A string containing an html document
  624. # cmd A command to run for each html tag found
  625. # start The name of the dummy html start/stop tags
  626. proc HMparse_html {html {cmd HMtest_parse} {start hmstart}} {
  627. regsub -all \{ $html {\&ob;} html
  628. regsub -all \} $html {\&cb;} html
  629. set w " \t\r\n" ;# white space
  630. proc HMcl x {return "\[$x\]"}
  631. set exp <(/?)([HMcl ^$w>]+)[HMcl $w]*([HMcl ^>]*)>
  632. set sub "\}\n$cmd {\\2} {\\1} {\\3} \{"
  633. regsub -all $exp $html $sub html
  634. eval "$cmd {$start} {} {} \{ $html \}"
  635. eval "$cmd {$start} / {} {}"
  636. }
  637. proc HMtest_parse {command tag slash text_after_tag} {
  638. puts "==> $command $tag $slash $text_after_tag"
  639. }
  640. # Convert multiple white space into a single space
  641. proc HMzap_white {data} {
  642. regsub -all "\[ \t\r\n\]+" $data " " data
  643. return $data
  644. }
  645. # find HTML escape characters of the form &xxx;
  646. proc HMmap_esc {text} {
  647. if {![regexp & $text]} {return $text}
  648. regsub -all {([][$\\])} $text {\\\1} new
  649. regsub -all {&#([0-9][0-9]?[0-9]?);?} \
  650. $new {[format %c [scan \1 %d tmp;set tmp]]} new
  651. regsub -all {&([a-zA-Z]+);?} $new {[HMdo_map \1]} new
  652. return [subst $new]
  653. }
  654. # convert an HTML escape sequence into character
  655. proc HMdo_map {text {unknown ?}} {
  656. global HMesc_map
  657. set result $unknown
  658. catch {set result $HMesc_map($text)}
  659. return $result
  660. }
  661. # table of escape characters (ISO latin-1 esc's are in a different table)
  662. array set HMesc_map {
  663. lt < gt > amp & quot \" copy \xa9
  664. reg \xae ob \x7b cb \x7d nbsp \xa0
  665. }
  666. #############################################################
  667. # ISO Latin-1 escape codes
  668. array set HMesc_map {
  669. nbsp \xa0 iexcl \xa1 cent \xa2 pound \xa3 curren \xa4
  670. yen \xa5 brvbar \xa6 sect \xa7 uml \xa8 copy \xa9
  671. ordf \xaa laquo \xab not \xac shy \xad reg \xae
  672. hibar \xaf deg \xb0 plusmn \xb1 sup2 \xb2 sup3 \xb3
  673. acute \xb4 micro \xb5 para \xb6 middot \xb7 cedil \xb8
  674. sup1 \xb9 ordm \xba raquo \xbb frac14 \xbc frac12 \xbd
  675. frac34 \xbe iquest \xbf Agrave \xc0 Aacute \xc1 Acirc \xc2
  676. Atilde \xc3 Auml \xc4 Aring \xc5 AElig \xc6 Ccedil \xc7
  677. Egrave \xc8 Eacute \xc9 Ecirc \xca Euml \xcb Igrave \xcc
  678. Iacute \xcd Icirc \xce Iuml \xcf ETH \xd0 Ntilde \xd1
  679. Ograve \xd2 Oacute \xd3 Ocirc \xd4 Otilde \xd5 Ouml \xd6
  680. times \xd7 Oslash \xd8 Ugrave \xd9 Uacute \xda Ucirc \xdb
  681. Uuml \xdc Yacute \xdd THORN \xde szlig \xdf agrave \xe0
  682. aacute \xe1 acirc \xe2 atilde \xe3 auml \xe4 aring \xe5
  683. aelig \xe6 ccedil \xe7 egrave \xe8 eacute \xe9 ecirc \xea
  684. euml \xeb igrave \xec iacute \xed icirc \xee iuml \xef
  685. eth \xf0 ntilde \xf1 ograve \xf2 oacute \xf3 ocirc \xf4
  686. otilde \xf5 ouml \xf6 divide \xf7 oslash \xf8 ugrave \xf9
  687. uacute \xfa ucirc \xfb uuml \xfc yacute \xfd thorn \xfe
  688. yuml \xff
  689. }
  690. ##########################################################
  691. # html forms management commands
  692. # As each form element is located, it is created and rendered. Additional
  693. # state is stored in a form specific global variable to be processed at
  694. # the end of the form, including the "reset" and "submit" options.
  695. # Remember, there can be multiple forms existing on multiple pages. When
  696. # HTML tables are added, a single form could be spread out over multiple
  697. # text widgets, which makes it impractical to hang the form state off the
  698. # HM$win structure. We don't need to check for the existance of required
  699. # parameters, we just "fail" and get caught in HMrender
  700. # This causes line breaks to be preserved in the inital values
  701. # of text areas
  702. array set HMtag_map {
  703. textarea {fill 0}
  704. }
  705. ##########################################################
  706. # html isindex tag. Although not strictly forms, they're close enough
  707. # to be in this file
  708. # is-index forms
  709. # make a frame with a label, entry, and submit button
  710. proc HMtag_isindex {win param text} {
  711. upvar #0 HM$win var
  712. set item $win.$var(tags)
  713. if {[winfo exists $item]} {
  714. destroy $item
  715. }
  716. frame $item -relief ridge -bd 3
  717. set prompt "Enter search keywords here"
  718. HMextract_param $param prompt
  719. label $item.label -text [HMmap_esc $prompt] -font $var(xfont)
  720. entry $item.entry
  721. bind $item.entry <Return> "$item.submit invoke"
  722. button $item.submit -text search -font $var(xfont) -command \
  723. [format {HMsubmit_index %s {%s} [HMmap_reply [%s get]]} \
  724. $win $param $item.entry]
  725. pack $item.label -side top
  726. pack $item.entry $item.submit -side left
  727. # insert window into text widget
  728. $win insert $var(S_insert) \n isindex
  729. HMwin_install $win $item
  730. $win insert $var(S_insert) \n isindex
  731. bind $item <Visibility> {focus %W.entry}
  732. }
  733. # This is called when the isindex form is submitted.
  734. # The default version calls HMlink_callback. Isindex tags should either
  735. # be deprecated, or fully supported (e.g. they need an href parameter)
  736. proc HMsubmit_index {win param text} {
  737. HMlink_callback $win ?$text
  738. }
  739. # initialize form state. All of the state for this form is kept
  740. # in a global array whose name is stored in the form_id field of
  741. # the main window array.
  742. # Parameters: ACTION, METHOD, ENCTYPE
  743. proc HMtag_form {win param text} {
  744. upvar #0 HM$win var
  745. # create a global array for the form
  746. set id HM$win.form$var(tags)
  747. upvar #0 $id form
  748. # missing /form tag, simulate it
  749. if {[info exists var(form_id)]} {
  750. puts "Missing end-form tag !!!! $var(form_id)"
  751. HMtag_/form $win {} {}
  752. }
  753. catch {unset form}
  754. set var(form_id) $id
  755. set form(param) $param ;# form initial parameter list
  756. set form(reset) "" ;# command to reset the form
  757. set form(reset_button) "" ;# list of all reset buttons
  758. set form(submit) "" ;# command to submit the form
  759. set form(submit_button) "" ;# list of all submit buttons
  760. }
  761. # Where we're done try to get all of the state into the widgets so
  762. # we can free up the form structure here. Unfortunately, we can't!
  763. proc HMtag_/form {win param text} {
  764. upvar #0 HM$win var
  765. upvar #0 $var(form_id) form
  766. # make submit button entries for all radio buttons
  767. foreach name [array names form radio_*] {
  768. regsub radio_ $name {} name
  769. lappend form(submit) [list $name \$form(radio_$name)]
  770. }
  771. # process the reset button(s)
  772. foreach item $form(reset_button) {
  773. $item configure -command $form(reset)
  774. }
  775. # no submit button - add one
  776. if {$form(submit_button) == ""} {
  777. HMinput_submit $win {}
  778. }
  779. # process the "submit" command(s)
  780. # each submit button could have its own name,value pair
  781. foreach item $form(submit_button) {
  782. set submit $form(submit)
  783. catch {lappend submit $form(submit_$item)}
  784. $item configure -command \
  785. [list HMsubmit_button $win $var(form_id) $form(param) \
  786. $submit]
  787. }
  788. # unset all unused fields here
  789. unset form(reset) form(submit) form(reset_button) form(submit_button)
  790. unset var(form_id)
  791. }
  792. ###################################################################
  793. # handle form input items
  794. # each item type is handled in a separate procedure
  795. # Each "type" procedure needs to:
  796. # - create the window
  797. # - initialize it
  798. # - add the "submit" and "reset" commands onto the proper Q's
  799. # "submit" is subst'd
  800. # "reset" is eval'd
  801. proc HMtag_input {win param text} {
  802. upvar #0 HM$win var
  803. set type text ;# the default
  804. HMextract_param $param type
  805. set type [string tolower $type]
  806. if {[catch {HMinput_$type $win $param} err]} {
  807. puts stderr $err
  808. }
  809. }
  810. # input type=text
  811. # parameters NAME (reqd), MAXLENGTH, SIZE, VALUE
  812. proc HMinput_text {win param {show {}}} {
  813. upvar #0 HM$win var
  814. upvar #0 $var(form_id) form
  815. # make the entry
  816. HMextract_param $param name ;# required
  817. set item $win.input_text,$var(tags)
  818. set size 20; HMextract_param $param size
  819. set maxlength 0; HMextract_param $param maxlength
  820. entry $item -width $size -show $show
  821. # set the initial value
  822. set value ""; HMextract_param $param value
  823. $item insert 0 $value
  824. # insert the entry
  825. HMwin_install $win $item
  826. # set the "reset" and "submit" commands
  827. append form(reset) ";$item delete 0 end;$item insert 0 [list $value]"
  828. lappend form(submit) [list $name "\[$item get]"]
  829. # handle the maximum length (broken - no way to cleanup bindtags state)
  830. if {$maxlength} {
  831. bindtags $item "[bindtags $item] max$maxlength"
  832. bind max$maxlength <KeyPress> "%W delete $maxlength end"
  833. }
  834. }
  835. # password fields - same as text, only don't show data
  836. # parameters NAME (reqd), MAXLENGTH, SIZE, VALUE
  837. proc HMinput_password {win param} {
  838. HMinput_text $win $param *
  839. }
  840. # checkbuttons are missing a "get" option, so we must use a global
  841. # variable to store the value.
  842. # Parameters NAME, VALUE, (reqd), CHECKED
  843. proc HMinput_checkbox {win param} {
  844. upvar #0 HM$win var
  845. upvar #0 $var(form_id) form
  846. HMextract_param $param name
  847. HMextract_param $param value
  848. # Set the global variable, don't use the "form" alias as it is not
  849. # defined in the global scope of the button
  850. set variable $var(form_id)(check_$var(tags))
  851. set item $win.input_checkbutton,$var(tags)
  852. checkbutton $item -variable $variable -off {} -on $value -text " "
  853. if {[HMextract_param $param checked]} {
  854. $item select
  855. append form(reset) ";$item select"
  856. } else {
  857. append form(reset) ";$item deselect"
  858. }
  859. HMwin_install $win $item
  860. lappend form(submit) [list $name \$form(check_$var(tags))]
  861. }
  862. # radio buttons. These are like check buttons, but only one can be selected
  863. proc HMinput_radio {win param} {
  864. upvar #0 HM$win var
  865. upvar #0 $var(form_id) form
  866. HMextract_param $param name
  867. HMextract_param $param value
  868. set first [expr ![info exists form(radio_$name)]]
  869. set variable $var(form_id)(radio_$name)
  870. set variable $var(form_id)(radio_$name)
  871. set item $win.input_radiobutton,$var(tags)
  872. radiobutton $item -variable $variable -value $value -text " "
  873. HMwin_install $win $item
  874. if {$first || [HMextract_param $param checked]} {
  875. $item select
  876. append form(reset) ";$item select"
  877. } else {
  878. append form(reset) ";$item deselect"
  879. }
  880. # do the "submit" actions in /form so we only end up with 1 per button grouping
  881. # contributing to the submission
  882. }
  883. # hidden fields, just append to the "submit" data
  884. # params: NAME, VALUE (reqd)
  885. proc HMinput_hidden {win param} {
  886. upvar #0 HM$win var
  887. upvar #0 $var(form_id) form
  888. HMextract_param $param name
  889. HMextract_param $param value
  890. lappend form(submit) [list $name $value]
  891. }
  892. # handle input images. The spec isn't very clear on these, so I'm not
  893. # sure its quite right
  894. # Use std image tag, only set up our own callbacks
  895. # (e.g. make sure ismap isn't set)
  896. # params: NAME, SRC (reqd) ALIGN
  897. proc HMinput_image {win param} {
  898. upvar #0 HM$win var
  899. upvar #0 $var(form_id) form
  900. HMextract_param $param name
  901. set name ;# barf if no name is specified
  902. set item [HMtag_img $win $param {}]
  903. $item configure -relief raised -bd 2 -bg blue
  904. # make a dummy "submit" button, and invoke it to send the form.
  905. # We have to get the %x,%y in the value somehow, so calculate it during
  906. # binding, and save it in the form array for later processing
  907. set submit $win.dummy_submit,$var(tags)
  908. if {[winfo exists $submit]} {
  909. destroy $submit
  910. }
  911. button $submit -takefocus 0;# this never gets mapped!
  912. lappend form(submit_button) $submit
  913. set form(submit_$submit) [list $name $name.\$form(X).\$form(Y)]
  914. $item configure -takefocus 1
  915. bind $item <FocusIn> "catch \{$win see $item\}"
  916. bind $item <1> "$item configure -relief sunken"
  917. bind $item <Return> "
  918. set $var(form_id)(X) 0
  919. set $var(form_id)(Y) 0
  920. $submit invoke
  921. "
  922. bind $item <ButtonRelease-1> "
  923. set $var(form_id)(X) %x
  924. set $var(form_id)(Y) %y
  925. $item configure -relief raised
  926. $submit invoke
  927. "
  928. }
  929. # Set up the reset button. Wait for the /form to attach
  930. # the -command option. There could be more that 1 reset button
  931. # params VALUE
  932. proc HMinput_reset {win param} {
  933. upvar #0 HM$win var
  934. upvar #0 $var(form_id) form
  935. set value reset
  936. HMextract_param $param value
  937. set item $win.input_reset,$var(tags)
  938. button $item -text [HMmap_esc $value]
  939. HMwin_install $win $item
  940. lappend form(reset_button) $item
  941. }
  942. # Set up the submit button. Wait for the /form to attach
  943. # the -command option. There could be more that 1 submit button
  944. # params: NAME, VALUE
  945. proc HMinput_submit {win param} {
  946. upvar #0 HM$win var
  947. upvar #0 $var(form_id) form
  948. HMextract_param $param name
  949. set value submit
  950. HMextract_param $param value
  951. set item $win.input_submit,$var(tags)
  952. button $item -text [HMmap_esc $value] -fg blue
  953. HMwin_install $win $item
  954. lappend form(submit_button) $item
  955. # need to tie the "name=value" to this button
  956. # save the pair and do it when we finish the submit button
  957. catch {set form(submit_$item) [list $name $value]}
  958. }
  959. #########################################################################
  960. # selection items
  961. # They all go into a list box. We don't what to do with the listbox until
  962. # we know how many items end up in it. Gather up the data for the "options"
  963. # and finish up in the /select tag
  964. # params: NAME (reqd), MULTIPLE, SIZE
  965. proc HMtag_select {win param text} {
  966. upvar #0 HM$win var
  967. upvar #0 $var(form_id) form
  968. HMextract_param $param name
  969. set size 5; HMextract_param $param size
  970. set form(select_size) $size
  971. set form(select_name) $name
  972. set form(select_values) "" ;# list of values to submit
  973. if {[HMextract_param $param multiple]} {
  974. set mode multiple
  975. } else {
  976. set mode single
  977. }
  978. set item $win.select,$var(tags)
  979. frame $item
  980. set form(select_frame) $item
  981. listbox $item.list -selectmode $mode -width 0 -exportselection 0
  982. HMwin_install $win $item
  983. }
  984. # select options
  985. # The values returned in the query may be different from those
  986. # displayed in the listbox, so we need to keep a separate list of
  987. # query values.
  988. # form(select_default) - contains the default query value
  989. # form(select_frame) - name of the listbox's containing frame
  990. # form(select_values) - list of query values
  991. # params: VALUE, SELECTED
  992. proc HMtag_option {win param text} {
  993. upvar #0 HM$win var
  994. upvar #0 $var(form_id) form
  995. upvar $text data
  996. set frame $form(select_frame)
  997. # set default option (or options)
  998. if {[HMextract_param $param selected]} {
  999. lappend form(select_default) [$form(select_frame).list size]
  1000. }
  1001. set value [string trimright $data " \n"]
  1002. $frame.list insert end $value
  1003. HMextract_param $param value
  1004. lappend form(select_values) $value
  1005. set data ""
  1006. }
  1007. # do most of the work here!
  1008. # if SIZE>1, make the listbox. Otherwise make a "drop-down"
  1009. # listbox with a label in it
  1010. # If the # of items > size, add a scroll bar
  1011. # This should probably be broken up into callbacks to make it
  1012. # easier to override the "look".
  1013. proc HMtag_/select {win param text} {
  1014. upvar #0 HM$win var
  1015. upvar #0 $var(form_id) form
  1016. set frame $form(select_frame)
  1017. set size $form(select_size)
  1018. set items [$frame.list size]
  1019. # set the defaults and reset button
  1020. append form(reset) ";$frame.list selection clear 0 $items"
  1021. if {[info exists form(select_default)]} {
  1022. foreach i $form(select_default) {
  1023. $frame.list selection set $i
  1024. append form(reset) ";$frame.list selection set $i"
  1025. }
  1026. } else {
  1027. $frame.list selection set 0
  1028. append form(reset) ";$frame.list selection set 0"
  1029. }
  1030. # set up the submit button. This is the general case. For single
  1031. # selections we could be smarter
  1032. for {set i 0} {$i < $size} {incr i} {
  1033. set value [format {[expr {[%s selection includes %s] ? {%s} : {}}]} \
  1034. $frame.list $i [lindex $form(select_values) $i]]
  1035. lappend form(submit) [list $form(select_name) $value]
  1036. }
  1037. # show the listbox - no scroll bar
  1038. if {$size > 1 && $items <= $size} {
  1039. $frame.list configure -height $items
  1040. pack $frame.list
  1041. # Listbox with scrollbar
  1042. } elseif {$size > 1} {
  1043. scrollbar $frame.scroll -command "$frame.list yview" \
  1044. -orient v -takefocus 0
  1045. $frame.list configure -height $size \
  1046. -yscrollcommand "$frame.scroll set"
  1047. pack $frame.list $frame.scroll -side right -fill y
  1048. # This is a joke!
  1049. } else {
  1050. scrollbar $frame.scroll -command "$frame.list yview" \
  1051. -orient h -takefocus 0
  1052. $frame.list configure -height 1 \
  1053. -yscrollcommand "$frame.scroll set"
  1054. pack $frame.list $frame.scroll -side top -fill x
  1055. }
  1056. # cleanup
  1057. foreach i [array names form select_*] {
  1058. unset form($i)
  1059. }
  1060. }
  1061. # do a text area (multi-line text)
  1062. # params: COLS, NAME, ROWS (all reqd, but default rows and cols anyway)
  1063. proc HMtag_textarea {win param text} {
  1064. upvar #0 HM$win var
  1065. upvar #0 $var(form_id) form
  1066. upvar $text data
  1067. set rows 5; HMextract_param $param rows
  1068. set cols 30; HMextract_param $param cols
  1069. HMextract_param $param name
  1070. set item $win.textarea,$var(tags)
  1071. frame $item
  1072. text $item.text -width $cols -height $rows -wrap none \
  1073. -yscrollcommand "$item.scroll set" -padx 3 -pady 3
  1074. scrollbar $item.scroll -command "$item.text yview" -orient v
  1075. $item.text insert 1.0 $data
  1076. HMwin_install $win $item
  1077. pack $item.text $item.scroll -side right -fill y
  1078. lappend form(submit) [list $name "\[$item.text get 0.0 end]"]
  1079. append form(reset) ";$item.text delete 1.0 end; \
  1080. $item.text insert 1.0 [list $data]"
  1081. set data ""
  1082. }
  1083. # procedure to install windows into the text widget
  1084. # - win: name of the text widget
  1085. # - item: name of widget to install
  1086. proc HMwin_install {win item} {
  1087. upvar #0 HM$win var
  1088. $win window create $var(S_insert) -window $item -align bottom
  1089. $win tag add indent$var(level) $item
  1090. set focus [expr {[winfo class $item] != "Frame"}]
  1091. $item configure -takefocus $focus
  1092. bind $item <FocusIn> "$win see $item"
  1093. }
  1094. #####################################################################
  1095. # Assemble and submit the query
  1096. # each list element in "stuff" is a name/value pair
  1097. # - The names are the NAME parameters of the various fields
  1098. # - The values get run through "subst" to extract the values
  1099. # - We do the user callback with the list of name value pairs
  1100. proc HMsubmit_button {win form_id param stuff} {
  1101. upvar #0 HM$win var
  1102. upvar #0 $form_id form
  1103. set query ""
  1104. foreach pair $stuff {
  1105. set value [subst [lindex $pair 1]]
  1106. if {$value != ""} {
  1107. set item [lindex $pair 0]
  1108. lappend query $item $value
  1109. }
  1110. }
  1111. # this is the user callback.
  1112. HMsubmit_form $win $param $query
  1113. }
  1114. # sample user callback for form submission
  1115. # should be replaced by the application
  1116. # Sample version generates a string suitable for http
  1117. proc HMsubmit_form {win param query} {
  1118. set result ""
  1119. set sep ""
  1120. foreach i $query {
  1121. append result $sep [HMmap_reply $i]
  1122. if {$sep != "="} {set sep =} {set sep &}
  1123. }
  1124. puts $result
  1125. }
  1126. # do x-www-urlencoded character mapping
  1127. # The spec says: "non-alphanumeric characters are replaced by '%HH'"
  1128. set HMalphanumeric a-zA-Z0-9 ;# definition of alphanumeric character class
  1129. for {set i 1} {$i <= 256} {incr i} {
  1130. set c [format %c $i]
  1131. if {![string match \[$HMalphanumeric\] $c]} {
  1132. set HMform_map($c) %[format %.2x $i]
  1133. }
  1134. }
  1135. # These are handled specially
  1136. array set HMform_map {
  1137. " " + \n %0d%0a
  1138. }
  1139. # 1 leave alphanumerics characters alone
  1140. # 2 Convert every other character to an array lookup
  1141. # 3 Escape constructs that are "special" to the tcl parser
  1142. # 4 "subst" the result, doing all the array substitutions
  1143. proc HMmap_reply {string} {
  1144. global HMform_map HMalphanumeric
  1145. regsub -all \[^$HMalphanumeric\] $string {$HMform_map(&)} string
  1146. regsub -all \n $string {\\n} string
  1147. regsub -all \t $string {\\t} string
  1148. regsub -all {[][{})\\]\)} $string {\\&} string
  1149. return [subst $string]
  1150. }
  1151. # convert a x-www-urlencoded string int a a list of name/value pairs
  1152. # 1 convert a=b&c=d... to {a} {b} {c} {d}...
  1153. # 2, convert + to " "
  1154. # 3, convert %xx to char equiv
  1155. proc HMcgiDecode {data} {
  1156. set data [split $data "&="]
  1157. foreach i $data {
  1158. lappend result [cgiMap $i]
  1159. }
  1160. return $result
  1161. }
  1162. proc HMcgiMap {data} {
  1163. regsub -all {\+} $data " " data
  1164. if {[regexp % $data]} {
  1165. regsub -all {([][$\\])} $data {\\\1} data
  1166. regsub -all {%([0-9a-fA-F][0-9a-fA-F])} $data {[format %c 0x\1]} data
  1167. return [subst $data]
  1168. } else {
  1169. return $data
  1170. }
  1171. }
  1172. # There is a bug in the tcl library focus routines that prevents focus
  1173. # from every reaching an un-viewable window. Use our *own*
  1174. # version of the library routine, until the bug is fixed, make sure we
  1175. # over-ride the library version, and not the otherway around
  1176. auto_load tkFocusOK
  1177. proc tkFocusOK w {
  1178. set code [catch {$w cget -takefocus} value]
  1179. if {($code == 0) && ($value != "")} {
  1180. if {$value == 0} {
  1181. return 0
  1182. } elseif {$value == 1} {
  1183. return 1
  1184. } else {
  1185. set value [uplevel #0 $value $w]
  1186. if {$value != ""} {
  1187. return $value
  1188. }
  1189. }
  1190. }
  1191. set code [catch {$w cget -state} value]
  1192. if {($code == 0) && ($value == "disabled")} {
  1193. return 0
  1194. }
  1195. regexp Key|Focus "[bind $w] [bind [winfo class $w]]"
  1196. }