PageRenderTime 69ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/gpsman-6.4.1/gmsrc/gendials.tcl

#
TCL | 1720 lines | 1307 code | 130 blank | 283 comment | 306 complexity | 72ec04680a4c2356bee014397655da62 MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. #
  2. # gpsman --- GPS Manager: a manager for GPS receiver data
  3. #
  4. # Copyright (c) 1998-2009 Miguel Filgueiras mig@ncc.up.pt Universidade do Porto
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program.
  18. #
  19. # File: gendials.tcl
  20. # Last change: 27 December 2009
  21. #
  22. # Includes contributions by
  23. # - Brian Baulch (baulchb_AT_onthenet.com.au) marked "BSB contribution"
  24. # - Stefan Heinen (stefan.heinen_AT_djh-freeweb.de) marked "SH contribution"
  25. #
  26. # creating toplevels
  27. proc GMToplevel {w title geom trans prots binds} {
  28. # create a toplevel with given geometry
  29. # $w window path
  30. # $title if not empty title given as either "==TITLE" for TITLE,
  31. # or as an index in TXT array
  32. # $trans if not void is path of window of which $w is a transient
  33. # $prots list with in sequence a wm protocol and a command
  34. # $binds list with in sequence an event and a command
  35. # return $w
  36. global TXT
  37. if { $title != "" && ! [regsub {^==} $title "" title] } {
  38. set title $TXT($title)
  39. }
  40. toplevel $w
  41. if { $title != "" } { wm title $w "$title/GPSMan" }
  42. wm geometry $w $geom
  43. if { $trans != {} } { wm transient $w $trans }
  44. wm group $w .
  45. foreach {p c} $prots { wm protocol $w $p $c }
  46. foreach {e c} $binds { bind $w $e $c }
  47. return $w
  48. }
  49. # modal dialogs
  50. proc GMMessage {mess args} {
  51. # create modal dialog for displaying message
  52. # if $args=="wait" return only when user acknowledges message
  53. # except in command-line mode or if using the slow op window
  54. # single button: OK; binding: return
  55. global COLOUR EPOSX EPOSY TXT UNIX CMDLINE
  56. if { $CMDLINE } {
  57. puts stderr $mess
  58. flush stderr
  59. return
  60. }
  61. if { [winfo exists .slowop] } {
  62. SlowOpMessage $mess
  63. return
  64. }
  65. if { [winfo exists .mess] } {
  66. # add new message
  67. foreach s [pack slaves .mess.fr] {
  68. if { $s == ".mess.fr.ok" } { break }
  69. set last $s
  70. }
  71. if { ! [regexp {^\.mess\.fr\.text(.*)$} $last x n] } {
  72. BUG bad last message field in .mess
  73. return
  74. }
  75. if { $n == "" } {
  76. set n 1
  77. } else {
  78. if { $n == 4 } {
  79. .mess.fr.text4 configure -text $mess
  80. update idletasks
  81. return
  82. }
  83. incr n
  84. }
  85. label .mess.fr.text$n -text $mess
  86. pack .mess.fr.text$n -side top -pady 5 -before .mess.fr.ok
  87. update idletasks
  88. return
  89. }
  90. # this avoids bugs but may create havoc with grabs
  91. set gs [grab current]
  92. GMToplevel .mess message +$EPOSX+$EPOSY . \
  93. [list WM_DELETE_WINDOW [list DestroyRGrabs .mess $gs]] \
  94. [list <Key-Return> [list DestroyRGrabs .mess $gs]]
  95. if { ! $UNIX } {
  96. # SH contribution
  97. focus .mess
  98. }
  99. frame .mess.fr -borderwidth 5 -bg $COLOUR(messbg)
  100. label .mess.fr.title -text "!!!" -relief sunken
  101. label .mess.fr.text -text $mess
  102. button .mess.fr.ok -text $TXT(ok) -command [list DestroyRGrabs .mess $gs]
  103. pack .mess.fr -side top
  104. # changes in packing order must be reflected above when adding
  105. # new messages to existing window
  106. pack .mess.fr.title .mess.fr.text .mess.fr.ok -side top -pady 5
  107. RaiseWindow .mess
  108. update idletasks
  109. grab .mess
  110. if { $args == "wait" } {
  111. while 1 {
  112. after 500
  113. update
  114. if { ! [winfo exists .mess] } { return }
  115. }
  116. }
  117. return
  118. }
  119. proc GMConfirm {mess} {
  120. # create modal dialog for asking for confirmation
  121. # buttons: OK, Cancel; bindings: return, delete
  122. global GMResConf COLOUR EPOSX EPOSY TXT CMDLINE
  123. if { $CMDLINE } { return 1 }
  124. destroy .messres
  125. GMToplevel .messres message +$EPOSX+$EPOSY . \
  126. {WM_DELETE_WINDOW {set GMResConf 0}} \
  127. [list <Key-Return> {set GMResConf 1} \
  128. <Key-Delete> {set GMResConf 0}]
  129. frame .messres.fr -borderwidth 5 -bg $COLOUR(confbg)
  130. label .messres.fr.title -text "???" -relief sunken
  131. label .messres.fr.text -text $mess
  132. frame .messres.fr.bs
  133. button .messres.fr.bs.ok -text $TXT(ok) -command { set GMResConf 1 }
  134. button .messres.fr.bs.cancel -text $TXT(no) -command { set GMResConf 0 }
  135. pack .messres.fr.bs.ok .messres.fr.bs.cancel -side left -pady 5
  136. pack .messres.fr.title .messres.fr.text .messres.fr.bs -side top -pady 5
  137. pack .messres.fr -side top
  138. update idletasks
  139. set gs [grab current]
  140. grab .messres
  141. RaiseWindow .messres
  142. tkwait variable GMResConf
  143. DestroyRGrabs .messres $gs
  144. update idletasks
  145. return $GMResConf
  146. }
  147. proc GMSelect {mess blist vlist} {
  148. # create modal dialog for selecting values from $vlist under names in
  149. # $blist; if an element in $blist has the form @LIST then the
  150. # corresponding $vlist element is a list with one less element than LIST
  151. # menubuttons are created for each @LIST element whose label is the
  152. # first element of LIST, buttons being used for the other elements
  153. # bindings: return for first, delete for last element, or their
  154. # first elements in case of @LIST
  155. # (see proc GMChooseFrom for selection using a listbox)
  156. global GMResSel COLOUR EPOSX EPOSY TXT
  157. # assumes first and last elements of vlist are return values for
  158. # Return and Delete keys, respectively
  159. destroy .messres
  160. set e [lindex $blist 0]
  161. if { [string first "@" $e] == 0 } {
  162. set e [lindex [lindex $vlist 0] 0]
  163. } else { set e [lindex $vlist 0] }
  164. GMToplevel .messres selection +$EPOSX+$EPOSY . \
  165. [list WM_DELETE_WINDOW "set GMResSel [lindex $vlist 0]"] \
  166. [list <Key-Return> "set GMResSel $e"]
  167. frame .messres.fr -borderwidth 5 -bg $COLOUR(selbg)
  168. label .messres.fr.title -text "???" -relief sunken
  169. label .messres.fr.text -text $mess
  170. frame .messres.fr.frsel
  171. set max 4 ; set c 0 ; set r 0 ; set lval "" ; set menus 0
  172. foreach e $blist v $vlist {
  173. set b .messres.fr.frsel.b$r$c
  174. if { [regexp {^@(.+)$} $e x e] } {
  175. if { [llength $e] != [llength $v]+1 } {
  176. BUG GMSelect bad lengths of @LIST lists
  177. }
  178. incr menus
  179. set lval [lindex $v 0]
  180. menubutton $b -text [lindex $e 0] -menu $b.m
  181. menu $b.m
  182. foreach x [lreplace $e 0 0] y $v {
  183. $b.m add command -label $x -command "set GMResSel $y"
  184. }
  185. } else {
  186. set lval $v
  187. button $b -text $e -command "set GMResSel $v"
  188. }
  189. grid $b -column $c -row $r -sticky ew
  190. if { [incr c] >= $max } {
  191. set c 0 ; incr r
  192. }
  193. }
  194. bind .messres <Key-Delete> "set GMResSel $lval"
  195. pack .messres.fr -side top
  196. pack .messres.fr.title .messres.fr.text .messres.fr.frsel -side top -pady 5
  197. set gs [grab current]
  198. update idletasks
  199. grab .messres
  200. if { $menus } {
  201. Raise .messres
  202. } else { RaiseWindow .messres }
  203. tkwait variable GMResSel
  204. DestroyRGrabs .messres $gs
  205. update idletasks
  206. return $GMResSel
  207. }
  208. proc GMChooseFrom {how mess wd blist vlist args} {
  209. # create modal dialog for selecting elements from list $blist
  210. # with associated return values in $vlist
  211. # $how in {single, many, many_0} defines number of elements that can
  212. # be selected, many_0 meaning that 0 is an alternative
  213. # a listbox is used with width $wd
  214. # $args if present is a pair with $vars $descs, suitable for use
  215. # with proc GMSetupParams, so that parameters may be selected
  216. # buttons: OK, Cancel
  217. # bindings: return for commit, extended select mode on listbox,
  218. # make visible by initial char on listbox
  219. # return list with selected values upon normal termination, and
  220. # an empty list or -1 if $how==many_0
  221. # (see also proc GMSelect for selection of only one element with buttons)
  222. global GMResult DPOSX DPOSY COLOUR TXT UNIX
  223. set w .gmchoosefr
  224. if { [winfo exists $w] } { Raise $w ; bell ; return }
  225. GMToplevel $w "==$mess" +$DPOSX+$DPOSY . \
  226. {WM_DELETE_WINDOW {set GMResult cnc}} \
  227. [list <Key-Return> {set GMResult ok}]
  228. frame $w.fr -borderwidth 5 -bg $COLOUR(dialbg)
  229. label $w.fr.text -text $mess
  230. # adjust list height according to number of parameters
  231. set lh [expr 15-[llength [lindex $args 0]]]
  232. if { [set ll [llength $blist]] > $lh } {
  233. set ll $lh
  234. }
  235. frame $w.fr.frbx
  236. if { $how == "single" } {
  237. set mode single
  238. } else { set mode extended }
  239. listbox $w.fr.frbx.bx -height $ll -width $wd -relief flat \
  240. -selectmode $mode -yscrollcommand "$w.fr.frbx.bscr set" \
  241. -exportselection 0
  242. # SH contribution: no such bindings in non-unix systems
  243. if { $UNIX } {
  244. bind $w.fr.frbx.bx <Enter> { focus %W }
  245. bind $w.fr.frbx.bx <Leave> "focus $w.fr.frbx"
  246. }
  247. bind $w.fr.frbx.bx <Key> { ScrollListIndex %W %A }
  248. scrollbar $w.fr.frbx.bscr -command "$w.fr.frbx.bx yview"
  249. foreach i $blist { $w.fr.frbx.bx insert end $i }
  250. if { $ll == 1 } { $w.fr.frbx.bx selection set 0 }
  251. if { $args != "" } {
  252. set opts 1
  253. frame $w.fr.fopt
  254. foreach "menus es" \
  255. [GMSetupParams $w.fr.fopt [lindex $args 0] [lindex $args 1]] {}
  256. } else { set opts 0 ; set menus 0 }
  257. frame $w.fr.frbt
  258. button $w.fr.frbt.ok -text $TXT(ok) -command { set GMResult ok }
  259. button $w.fr.frbt.cnc -text $TXT(cancel) -command { set GMResult cnc }
  260. pack $w.fr.frbt.ok $w.fr.frbt.cnc -side left -pady 5
  261. pack $w.fr.frbx.bx $w.fr.frbx.bscr -side left -fill y
  262. if { $opts } {
  263. pack $w.fr.text $w.fr.frbx $w.fr.fopt $w.fr.frbt -side top -pady 5
  264. } else {
  265. pack $w.fr.text $w.fr.frbx $w.fr.frbt -side top -pady 5
  266. }
  267. pack $w.fr
  268. update idletasks
  269. set gs [grab current]
  270. grab $w
  271. if { $menus } {
  272. Raise .fdlg
  273. } else { RaiseWindow .fdlg }
  274. while 1 {
  275. tkwait variable GMResult
  276. switch $GMResult {
  277. "" { }
  278. cnc {
  279. if { $how == "many_0" } { set res -1 } else { set res "" }
  280. break
  281. }
  282. ok {
  283. set ss [$w.fr.frbx.bx curselection]
  284. if { $ss == "" && $how != "many_0" } {
  285. bell
  286. continue
  287. }
  288. set res ""
  289. foreach i $ss {
  290. lappend res [lindex $vlist $i]
  291. }
  292. if { $opts } {
  293. GMUseEntries $w.fr.fopt $es
  294. }
  295. break
  296. }
  297. }
  298. }
  299. DestroyRGrabs $w $gs
  300. update idletasks
  301. return $res
  302. }
  303. proc GMChooseParams {mess vars descs} {
  304. # create modal dialog for choosing parameters
  305. # $vars and $descs are as described in GMSetupParams
  306. # buttons: OK, Cancel
  307. # bindings: return for commit
  308. # return 0 if cancelled
  309. global GMResult DPOSX DPOSY COLOUR TXT
  310. set w .gmchooseprsr
  311. if { [winfo exists $w] } { Raise $w ; bell ; return }
  312. GMToplevel $w "==$mess" +$DPOSX+$DPOSY . \
  313. {WM_DELETE_WINDOW {set GMResult cnc}} \
  314. [list <Key-Return> {set GMResult ok}]
  315. frame $w.fr -borderwidth 5 -bg $COLOUR(dialbg)
  316. label $w.fr.text -text $mess
  317. frame $w.fr.fopt
  318. foreach "menus es" [GMSetupParams $w.fr.fopt $vars $descs] {}
  319. frame $w.fr.frbt
  320. button $w.fr.frbt.ok -text $TXT(ok) -command { set GMResult ok }
  321. button $w.fr.frbt.cnc -text $TXT(cancel) -command { set GMResult cnc }
  322. pack $w.fr.frbt.ok $w.fr.frbt.cnc -side left -pady 5
  323. pack $w.fr.text $w.fr.fopt $w.fr.frbt -side top -pady 5
  324. pack $w.fr
  325. update idletasks
  326. set gs [grab current]
  327. grab $w
  328. if { $menus } {
  329. Raise .fdlg
  330. } else { RaiseWindow .fdlg }
  331. while 1 {
  332. tkwait variable GMResult
  333. switch $GMResult {
  334. "" { }
  335. cnc {
  336. set res 0 ; break
  337. }
  338. ok {
  339. GMUseEntries $w.fr.fopt $es
  340. set res 1 ; break
  341. }
  342. }
  343. }
  344. DestroyRGrabs $w $gs
  345. update idletasks
  346. return $res
  347. }
  348. proc GMLogin {service} {
  349. # get or retrieve login information for accessing a given service
  350. # $service is a unique name for the service, needed for displaying
  351. # a message and indexing saved login information
  352. # save the login information for use in the current session if the user
  353. # asks for it
  354. # return list with user name and password or an empty list if cancelled
  355. global MESS TXT GMPInfo
  356. if { ! [catch {set up $GMPInfo($service)}] } { return $up }
  357. if { [GMChooseParams [format $MESS(loginto) $service] \
  358. {GMPInfo(__tmp,u) GMPInfo(__tmp,p) GMPInfo(__tmp,s)} \
  359. [list =$TXT(uname) =@$TXT(pword) @$TXT(remember)]] \
  360. == 0 } { return {} }
  361. set up [list $GMPInfo(__tmp,u) $GMPInfo(__tmp,p)]
  362. unset GMPInfo(__tmp,p)
  363. if { $GMPInfo(__tmp,s) } { set GMPInfo($service) $up }
  364. return $up
  365. }
  366. ##### information window
  367. proc DisplayInfo {mess args} {
  368. # display information on a dialog
  369. # the dialog is created if it not exists, otherwise the message
  370. # will be added to it
  371. # $args may be "" or "tabs" followed by tabs list (man 3tk text) in
  372. # which negative numbers are to be converted from chars to screen
  373. # distances
  374. global CMDLINE COLOUR EPOSX EPOSY TXT FixedFont DInfo
  375. if { $CMDLINE } { return }
  376. set frt .gminfo.fr.frt
  377. if { ! [winfo exists .gminfo] } {
  378. GMToplevel .gminfo info +$EPOSX+$EPOSY {} \
  379. {WM_DELETE_WINDOW {destroy .gminfo}} {}
  380. frame .gminfo.fr -borderwidth 5 -bg $COLOUR(messbg)
  381. label .gminfo.fr.title -text $TXT(info) -relief sunken
  382. frame $frt -relief flat -borderwidth 0
  383. text $frt.txt -width 80 -font $FixedFont -wrap word \
  384. -exportselection 1 -yscrollcommand "$frt.tscrl set"
  385. bind $frt.txt <space> "$frt.txt yview scroll 1 pages ; break"
  386. bind $frt.txt <Key-Delete> "$frt.txt yview scroll -1 pages ; break"
  387. bind $frt.txt <Any-Key> break
  388. bind $frt.txt <Button-2> break
  389. scrollbar $frt.tscrl -command "$frt.txt yview"
  390. set frb .gminfo.fr.frb
  391. frame $frb -relief flat -borderwidth 0
  392. button $frb.save -text $TXT(save) \
  393. -command "SaveDisplayInfo $frt.txt"
  394. button $frb.ok -text $TXT(ok) -command { destroy .gminfo }
  395. grid config $frt.txt -column 0 -row 1 -sticky nesw
  396. grid config $frt.tscrl -column 1 -row 1 -sticky nesw
  397. grid config $frb.save -column 0 -row 0
  398. grid config $frb.ok -column 1 -row 0
  399. pack .gminfo.fr.title $frt $frb -side top -pady 5
  400. pack .gminfo.fr
  401. # info on this window
  402. catch {unset DInfo}
  403. # to help setting tabs, make public the "ex" in pixels
  404. set x20 "xxxxxxxxxxxxxxxxxxxx"
  405. set DInfo(ex) [expr round([font measure $FixedFont $x20]/20.0)]
  406. # number of next free tag; tags will have names started by "itg"
  407. set DInfo(nxttag) 1
  408. }
  409. if { $args != "" } {
  410. set tags ""
  411. switch -- [lindex $args 0] {
  412. tabs {
  413. set tlst ""
  414. foreach x [lindex $args 1] {
  415. if { [regexp {^-([0-9]+)$} $x m n] } {
  416. # to pixels
  417. set x [expr $n*$DInfo(ex)]
  418. }
  419. lappend tlst $x
  420. }
  421. if { [catch {set tgname $DInfo($tlst)}] } {
  422. set tgname itg$DInfo(nxttag)
  423. incr DInfo(nxttag)
  424. $frt.txt tag configure $tgname -tabs $tlst
  425. set DInfo($tlst) $tgname
  426. }
  427. lappend tags $tgname
  428. }
  429. default {
  430. BUG bad args to DisplayInfo
  431. return
  432. }
  433. }
  434. $frt.txt insert end "$mess\n" $tags
  435. } else { $frt.txt insert end "$mess\n" }
  436. $frt.txt see end
  437. update idletasks
  438. return
  439. }
  440. proc SaveDisplayInfo {wtxt} {
  441. # save text in $wtxt text widget to file
  442. global TXT
  443. if { [set txt [$wtxt get 1.0 end]] == "" || \
  444. [set f [GMOpenFile $TXT(saveto) Info w]] == ".." } { return }
  445. puts $f $txt
  446. close $f
  447. return
  448. }
  449. ### dialog window for controlling slow operations
  450. proc SlowOpWindow {mess} {
  451. # create dialog for controlling slow operation
  452. # to be called by application before entering the main loop of the slow
  453. # operation
  454. # within the loop there should be calls to proc SlowOpAborted that
  455. # returns 1 if the operation is to be aborted, or updates the interface
  456. # and returns 0 otherwise
  457. # any call within the loop to GMMessage will be diverted to this dialog
  458. # after the main loop there should be a call to proc SlowOpFinish with
  459. # the unique identifier that is returned by proc SlowOpWindow
  460. # returns a unique identifier to be used when calling proc SlowOpWindow
  461. global SlowOp COLOUR MAPCOLOUR EPOSX EPOSY TXT CMDLINE USESLOWOPWINDOW \
  462. FixedFont
  463. if { $CMDLINE || ! $USESLOWOPWINDOW } { return }
  464. if { [winfo exists .slowop] } {
  465. set SlowOp(id) [clock seconds]
  466. set SlowOp(ids) [linsert $SlowOp(ids) 0 $SlowOp(id)]
  467. .slowop.fr.title configure -text $mess
  468. return $SlowOp(id)
  469. }
  470. set id [clock seconds]
  471. array set SlowOp [list aborting 0 id $id ids $id \
  472. status "$TXT(working)..." grabs [grab current]]
  473. # avoid completely covering other dialogs
  474. set pos [expr $EPOSX+150]
  475. GMToplevel .slowop opinprogr +$pos+$EPOSY {} \
  476. {WM_DELETE_WINDOW {set SlowOp(aborting) 1}} {}
  477. frame .slowop.fr -borderwidth 5 -bg $COLOUR(messbg)
  478. label .slowop.fr.title -text $mess -relief sunken
  479. set frs .slowop.fr.frs
  480. frame $frs -relief flat -borderwidth 0
  481. label $frs.st -textvariable SlowOp(status) -fg $MAPCOLOUR(trvwrnimportant)
  482. checkbutton $frs.light -disabledforeground $COLOUR(check) -state disabled
  483. set frt .slowop.fr.frt
  484. frame $frt -relief flat -borderwidth 0
  485. text $frt.txt -width 50 -font $FixedFont -wrap word \
  486. -yscrollcommand "$frt.tscrl set"
  487. bind $frt.txt <space> "$frt.txt yview scroll 1 pages ; break"
  488. bind $frt.txt <Key-Delete> "$frt.txt yview scroll -1 pages ; break"
  489. bind $frt.txt <Any-Key> break
  490. scrollbar $frt.tscrl -command "$frt.txt yview"
  491. set frb .slowop.fr.frb
  492. frame $frb -relief flat -borderwidth 0
  493. button $frb.abort -text $TXT(abort) -command SlowOpAbort
  494. button $frb.ok -text $TXT(ok) -state disabled \
  495. -command [list DestroyRGrabs .slowop $SlowOp(grabs)]
  496. pack $frs.st $frs.light -side left
  497. grid config $frt.txt -column 0 -row 1 -sticky nesw
  498. grid config $frt.tscrl -column 1 -row 1 -sticky nesw
  499. grid config $frb.abort -column 0 -row 0
  500. grid config $frb.ok -column 1 -row 0
  501. pack .slowop.fr.title $frs $frt $frb -side top -pady 5
  502. pack .slowop.fr
  503. update idletasks
  504. grab .slowop
  505. RaiseWindow .slowop
  506. return $id
  507. }
  508. proc SlowOpFinish {id mess} {
  509. # to be called by application when the operation ends (either normally
  510. # or not)
  511. # $id is unique identifier that was returned by proc SlowOpWindow
  512. # if $id is not in the $SlowOp(ids) stack the message is displayed
  513. # and nothing else happens
  514. # $mess will be displayed if not empty
  515. # the dialog window will be closed only when the stack of calls to
  516. # proc SlowOpWindow is empty
  517. # the dialog window is closed silently if there were no messages,
  518. # otherwise the Ok button is activated and the user must acknowledge it
  519. global SlowOp TXT
  520. if { ! [winfo exists .slowop] } {
  521. if { $mess != "" } { GMMessage $mess }
  522. return
  523. }
  524. if { $mess != "" } { SlowOpMessage $mess }
  525. if { [set ix [lsearch -exact $SlowOp(ids) $id]] == -1 || \
  526. [set SlowOp(ids) [lreplace $SlowOp(ids) 0 $ix]] != {} } {
  527. return
  528. }
  529. if { ! $SlowOp(aborting) } { set SlowOp(status) $TXT(errwarn) }
  530. set SlowOp(aborting) 0
  531. set txt .slowop.fr.frt.txt
  532. if { [$txt index end] == 2.0 } {
  533. DestroyRGrabs .slowop $SlowOp(grabs)
  534. return
  535. }
  536. set frb .slowop.fr.frb
  537. foreach b "abort ok" st "disabled normal" {
  538. $frb.$b configure -state $st
  539. }
  540. return
  541. }
  542. proc SlowOpAbort {} {
  543. # the user aborted the operation
  544. # not to be called directly from the application
  545. global SlowOp TXT
  546. set SlowOp(aborting) 1
  547. set SlowOp(status) $TXT(aborted)
  548. return
  549. }
  550. proc SlowOpMessage {mess} {
  551. # show message in slow operation dialog window
  552. # not to be called directly from the application
  553. set txt .slowop.fr.frt.txt
  554. $txt insert end "$mess\n"
  555. $txt see end
  556. update idletasks
  557. return
  558. }
  559. proc SlowOpAborted {} {
  560. # to be called by the application to test if the operation was aborted
  561. # if not a call to update is made to ensure that the window is usable
  562. # return 1 if yes
  563. global SlowOp TXT
  564. if { ! [winfo exists .slowop] } { return 0 }
  565. if { $SlowOp(aborting) } {
  566. set SlowOp(status) $TXT(aborted)
  567. return 1
  568. }
  569. set frs .slowop.fr.frs
  570. $frs.light toggle
  571. update
  572. return 0
  573. }
  574. ### opening files
  575. proc GMOpenFile {act wh mode} {
  576. # create modal dialog for selecting and opening a file
  577. # $act is string describing the action to do on the file
  578. # $wh in $filetypes (see proc GMStart, setup.tcl)
  579. # $mode in {r, w}
  580. # buttons: OK, Cancel
  581. # binding: return and double-left for commit, left-click for select
  582. global GMResult COLOUR DPOSX DPOSY LISTHEIGHT File TXT MESS UNIX
  583. if { [set f $File($wh)] == "" || [catch {cd [file dirname $f]}] } {
  584. set currfile ""
  585. } else { set currfile [file tail $f] }
  586. GMToplevel .fdlg file +$DPOSX+$DPOSY . \
  587. {WM_DELETE_WINDOW {set GMResult cnc}} \
  588. [list <Key-Return> {set GMResult ok}]
  589. frame .fdlg.fr -borderwidth 5 -bg $COLOUR(selbg)
  590. label .fdlg.fr.title -text [format $MESS(fileact) $act $TXT(name$wh)] \
  591. -relief sunken
  592. if { ! $UNIX } {
  593. menubutton .fdlg.fr.vols -text $TXT(volume) -menu .fdlg.fr.vols.m
  594. menu .fdlg.fr.vols.m
  595. bind .fdlg.fr.vols <Button-1> {
  596. FillMenuExec .fdlg.fr.vols.m {ChangeVolume .fdlg} file volume
  597. }
  598. }
  599. entry .fdlg.fr.wdir -width 30
  600. ShowTEdit .fdlg.fr.wdir [pwd] 0
  601. frame .fdlg.fr.frbx
  602. listbox .fdlg.fr.frbx.box -height $LISTHEIGHT -width 30 \
  603. -yscrollcommand ".fdlg.fr.frbx.bscr set" \
  604. -selectmode single -exportselection 1
  605. bind .fdlg.fr.frbx.box <Double-1> {
  606. global GMResult
  607. set GMResult [%W nearest %y]
  608. }
  609. bind .fdlg.fr.frbx.box <Button-1> {
  610. .fdlg.fr.fn delete 0 end
  611. .fdlg.fr.fn insert 0 [%W get [%W nearest %y]]
  612. }
  613. scrollbar .fdlg.fr.frbx.bscr -command ".fdlg.fr.frbx.box yview"
  614. FillDir .fdlg.fr.frbx.box
  615. entry .fdlg.fr.fn -width 30
  616. .fdlg.fr.fn insert 0 $currfile
  617. TextBindings .fdlg.fr.fn
  618. frame .fdlg.fr.bs
  619. button .fdlg.fr.bs.ok -text $TXT(ok) -command { set GMResult ok }
  620. button .fdlg.fr.bs.cnc -text $TXT(cancel) \
  621. -command { set GMResult cnc }
  622. pack .fdlg.fr.bs.ok .fdlg.fr.bs.cnc -side left -pady 5
  623. pack .fdlg.fr.frbx.box .fdlg.fr.frbx.bscr -side left -fill y
  624. if { $UNIX } {
  625. pack .fdlg.fr.title .fdlg.fr.wdir .fdlg.fr.frbx .fdlg.fr.fn \
  626. .fdlg.fr.bs -side top -pady 5
  627. } else {
  628. pack .fdlg.fr.title .fdlg.fr.vols .fdlg.fr.wdir .fdlg.fr.frbx \
  629. .fdlg.fr.fn .fdlg.fr.bs -side top -pady 5
  630. }
  631. pack .fdlg.fr -side top
  632. update idletasks
  633. set gs [grab current]
  634. grab .fdlg
  635. RaiseWindow .fdlg
  636. while 1 {
  637. tkwait variable GMResult
  638. switch $GMResult {
  639. "" { }
  640. cnc {
  641. set res ".."
  642. break
  643. }
  644. ok {
  645. set fn [.fdlg.fr.fn get]
  646. set f [GMCheckFile open $fn $mode]
  647. if { $f != ".." } {
  648. set File($wh) [file join [pwd] $fn]
  649. set res $f
  650. break
  651. }
  652. }
  653. 0 {
  654. cd ..
  655. ShowTEdit .fdlg.fr.wdir [pwd] 0
  656. .fdlg.fr.frbx.box delete 0 end ; FillDir .fdlg.fr.frbx.box
  657. .fdlg.fr.fn delete 0 end
  658. }
  659. default {
  660. set fn [.fdlg.fr.frbx.box get $GMResult]
  661. set f [GMCheckFile open $fn $mode]
  662. if { $f != ".." } {
  663. set File($wh) [file join [pwd] $fn]
  664. set res $f
  665. break
  666. }
  667. }
  668. }
  669. }
  670. DestroyRGrabs .fdlg $gs
  671. update idletasks
  672. return $res
  673. }
  674. proc GMOpenFileParms {act wh mode vars vals} {
  675. # create modal dialog for selecting and opening a file and parameters
  676. # see arguments of proc GMGetFileName
  677. set fname [GMGetFileName $act $wh $mode $vars $vals]
  678. if { $fname == ".." } { return ".." }
  679. return [open $fname $mode]
  680. }
  681. proc GMGetFileName {act wh mode vars vals} {
  682. # create modal dialog for selecting a file name and parameters
  683. # $act is string describing the action to do on the file
  684. # $wh in $filetypes (see proc GMStart, setup.tcl)
  685. # $mode in {r, w}
  686. # $vars is list of (global) vars to set
  687. # $vals is associated list of value descriptions (see proc GMSetupParams)
  688. # buttons: OK, Cancel
  689. # binding: return and double-left for commit, left-click for select
  690. global GMResult COLOUR DPOSX DPOSY LISTHEIGHT File TXT MESS UNIX
  691. if { [set f $File($wh)] == "" || [catch {cd [file dirname $f]}] } {
  692. set currfile ""
  693. } else { set currfile [file tail $f] }
  694. GMToplevel .fdlg file +$DPOSX+$DPOSY . \
  695. {WM_DELETE_WINDOW {set GMResult cnc}} \
  696. [list <Key-Return> {set GMResult ok}]
  697. frame .fdlg.fr -borderwidth 5 -bg $COLOUR(selbg)
  698. label .fdlg.fr.title -text [format $MESS(fileact) $act $TXT(name$wh)] \
  699. -relief sunken
  700. if { ! $UNIX } {
  701. menubutton .fdlg.fr.vols -text $TXT(volume) -menu .fdlg.fr.vols.m
  702. menu .fdlg.fr.vols.m
  703. bind .fdlg.fr.vols <Button-1> {
  704. FillMenuExec .fdlg.fr.vols.m {ChangeVolume .fdlg} file volume
  705. }
  706. }
  707. entry .fdlg.fr.wdir -width 30
  708. ShowTEdit .fdlg.fr.wdir [pwd] 0
  709. # adjust list height according to number of parameters
  710. set lh [expr $LISTHEIGHT-[llength $vars]]
  711. frame .fdlg.fr.frbx
  712. listbox .fdlg.fr.frbx.box -height $lh -width 30 \
  713. -yscrollcommand ".fdlg.fr.frbx.bscr set" \
  714. -selectmode single -exportselection 1
  715. bind .fdlg.fr.frbx.box <Double-1> {
  716. global GMResult
  717. set GMResult [%W nearest %y]
  718. }
  719. bind .fdlg.fr.frbx.box <Button-1> {
  720. .fdlg.fr.fn delete 0 end
  721. .fdlg.fr.fn insert 0 [%W get [%W nearest %y]]
  722. }
  723. scrollbar .fdlg.fr.frbx.bscr -command ".fdlg.fr.frbx.box yview"
  724. FillDir .fdlg.fr.frbx.box
  725. # BSB contribution: wheelmouse scrolling
  726. Mscroll .fdlg.fr.frbx.box
  727. entry .fdlg.fr.fn -width 30
  728. .fdlg.fr.fn insert 0 $currfile
  729. TextBindings .fdlg.fr.fn
  730. frame .fdlg.fr.fopt
  731. foreach "menus es" [GMSetupParams .fdlg.fr.fopt $vars $vals] {}
  732. frame .fdlg.fr.bs
  733. button .fdlg.fr.bs.ok -text $TXT(ok) -command { set GMResult ok }
  734. button .fdlg.fr.bs.cnc -text $TXT(cancel) \
  735. -command { set GMResult cnc }
  736. pack .fdlg.fr.bs.ok .fdlg.fr.bs.cnc -side left -pady 5
  737. pack .fdlg.fr.frbx.box .fdlg.fr.frbx.bscr -side left -fill y
  738. if { $UNIX } {
  739. pack .fdlg.fr.title .fdlg.fr.wdir .fdlg.fr.frbx .fdlg.fr.fn \
  740. .fdlg.fr.fopt .fdlg.fr.bs -side top -pady 5
  741. } else {
  742. pack .fdlg.fr.title .fdlg.fr.vols .fdlg.fr.wdir .fdlg.fr.frbx \
  743. .fdlg.fr.fn .fdlg.fr.fopt .fdlg.fr.bs -side top -pady 5
  744. }
  745. pack .fdlg.fr -side top
  746. update idletasks
  747. set gs [grab current]
  748. grab .fdlg
  749. if { $menus } {
  750. Raise .fdlg
  751. } else { RaiseWindow .fdlg }
  752. while 1 {
  753. tkwait variable GMResult
  754. switch $GMResult {
  755. "" { }
  756. cnc {
  757. set res ".." ; break
  758. }
  759. ok {
  760. set fn [.fdlg.fr.fn get]
  761. set f [GMCheckFile check $fn $mode]
  762. if { $f != ".." } {
  763. set File($wh) [file join [pwd] $fn]
  764. GMUseEntries .fdlg.fr.fopt $es
  765. set res $fn
  766. break
  767. }
  768. }
  769. 0 {
  770. cd ..
  771. ShowTEdit .fdlg.fr.wdir [pwd] 0
  772. .fdlg.fr.frbx.box delete 0 end ; FillDir .fdlg.fr.frbx.box
  773. .fdlg.fr.fn delete 0 end
  774. }
  775. default {
  776. set fn [.fdlg.fr.frbx.box get $GMResult]
  777. set f [GMCheckFile check $fn $mode]
  778. if { $f != ".." } {
  779. set File($wh) [file join [pwd] $fn]
  780. set res $fn
  781. break
  782. }
  783. }
  784. }
  785. }
  786. DestroyRGrabs .fdlg $gs
  787. update idletasks
  788. return $res
  789. }
  790. proc GMCheckFile {how f mode} {
  791. # check name of file $f and if ok either open it and return file descriptor
  792. # or return file name; otherwise return ".."
  793. # $how in {open check}
  794. # $mode in {r, w}
  795. global PERMS TXT MESS
  796. if { $f == "" } { bell ; return ".." }
  797. if { [file isdirectory $f] } {
  798. if { [file executable $f] } {
  799. cd $f
  800. ShowTEdit .fdlg.fr.wdir [pwd] 0
  801. .fdlg.fr.frbx.box delete 0 end
  802. FillDir .fdlg.fr.frbx.box
  803. .fdlg.fr.fn delete 0 end
  804. } else {
  805. bell
  806. }
  807. } elseif { $mode == "r" } {
  808. if { [file readable $f] } {
  809. switch $how {
  810. open { return [open $f r] }
  811. check { return $f }
  812. }
  813. } else {
  814. bell
  815. }
  816. } elseif { [file exists $f] } {
  817. if { [file writable $f] } {
  818. set m [GMSelect $MESS(filexists) \
  819. [list $TXT(ovwrt) $TXT(app) $TXT(cancel)] "w a 0"]
  820. if { $m != 0 } {
  821. switch $how {
  822. open { return [open $f $m $PERMS] }
  823. check { return $f }
  824. }
  825. }
  826. } else {
  827. bell
  828. }
  829. } elseif { [file writable [pwd]] } {
  830. switch $how {
  831. open { return [open $f $mode $PERMS] }
  832. check { return $f }
  833. }
  834. } else {
  835. bell
  836. }
  837. return ".."
  838. }
  839. proc ChangeVolume {w vol} {
  840. # file volume has changed $vol in file-selection dialog $w
  841. if { ! [file isdirectory $vol] } { bell ; return }
  842. cd $vol
  843. ShowTEdit .fdlg.fr.wdir [pwd] 0
  844. .fdlg.fr.frbx.box delete 0 end ; FillDir .fdlg.fr.frbx.box
  845. .fdlg.fr.fn delete 0 end
  846. return
  847. }
  848. ### font selection
  849. proc GMSelectFont {args} {
  850. # dialog for selecting a font
  851. # $args may contain the font description to return if the
  852. # default is selected; if empty "default" is returned
  853. # a font is defined by giving
  854. # one of [font families] and
  855. # the size in points or pixels, an integer > 0
  856. # the weight, one of {normal bold}
  857. # the slant, one of {roman italic}
  858. # whether to use underline
  859. # whether to use overstrike
  860. # return empty list if cancelled, "default" or the description
  861. # in $args, or list with family, size, and other style
  862. # indicators in {normal bold roman italic underline overstrike}
  863. # where size follows the Tk convention (negative if in pixels)
  864. global GMFtDial TXT MESS LISTHEIGHT EPOSX EPOSY COLOUR
  865. array set GMFtDial {
  866. size 12
  867. units points
  868. weight normal
  869. slant roman
  870. underline 0
  871. overstrike 0
  872. }
  873. if { [winfo exists .gmselfont] } { destroy .gmselfont }
  874. set w [GMToplevel .gmselfont selfont +$EPOSX+$EPOSY . \
  875. {WM_DELETE_WINDOW {set GMFtDial(act) cancel}} \
  876. [list <Key-Return> {set GMFtDial(act) ok}]]
  877. frame $w.fr -borderwidth 5 -bg $COLOUR(selbg)
  878. label $w.fr.tit -text $TXT(selfont)
  879. set frbx $w.fr.frbx
  880. frame $frbx
  881. listbox $frbx.box -height $LISTHEIGHT -width 40 -selectmode single \
  882. -yscrollcommand "$frbx.bscr set" -exportselection 1
  883. scrollbar $frbx.bscr -command "$frbx.box yview"
  884. grid $frbx.box -row 0 -column 0
  885. grid $frbx.bscr -row 0 -column 1 -sticky ns
  886. grid rowconfigure $frbx 0 -weight 1
  887. grid columnconfigure $frbx 0 -weight 1
  888. foreach fam [lsort -dictionary [font families]] {
  889. $frbx.box insert end $fam
  890. }
  891. frame $w.fr.frp
  892. set vars {} ; set descs {}
  893. foreach v {size units weight slant underline overstrike} {
  894. lappend vars GMFtDial($v)
  895. }
  896. set descs [list "=$TXT(size)" \
  897. "~$TXT(units)/[list points pixels]" \
  898. "~$TXT(weight)/[list normal bold]" \
  899. "~$TXT(slant)/[list roman italic]" \
  900. "@$TXT(underline)" "@$TXT(overstrike)"]
  901. set pes [lindex [GMSetupParams $w.fr.frp $vars $descs] 1]
  902. set frbs $w.fr.frbs
  903. frame $frbs
  904. foreach x {ok default cancel} {
  905. button $frbs.$x -text $TXT($x) -command "set GMFtDial(act) $x"
  906. pack $frbs.$x -side left
  907. }
  908. pack $w.fr.tit
  909. pack $frbs -side bottom -pady 5
  910. pack $w.fr.frp -side bottom -pady 5
  911. # must be the last one
  912. pack $w.fr.frbx -fill both -expand 1 -pady 5
  913. grid $w.fr
  914. grid rowconfigure $w.fr 0 -weight 1
  915. grid columnconfigure $w.fr 0 -weight 1
  916. grid rowconfigure $w 0 -weight 1
  917. grid columnconfigure $w 0 -weight 1
  918. update idletasks
  919. # cannot use RaiseWindow because of menus
  920. set grabs [grab current]
  921. grab $w
  922. while 1 {
  923. tkwait variable GMFtDial(act)
  924. switch $GMFtDial(act) {
  925. cancel {
  926. set res {} ; break
  927. }
  928. default {
  929. if { [set res [lindex $args 0]] == {} } {
  930. set res default
  931. }
  932. break
  933. }
  934. ok {
  935. if { [set ix [$frbx.box curselection]] == {} } {
  936. GMMessage $MESS(mustselftfam)
  937. continue
  938. }
  939. GMUseEntries $w.fr.frp $pes
  940. set n [string trim $GMFtDial(size)]
  941. if { ! [CheckNumber GMMessage $n] } { continue }
  942. if { $n < 1 } {
  943. GMMessage [format $MESS(xcantbey) $TXT(size) 0]
  944. continue
  945. }
  946. if { $GMFtDial(units) == "pixels" } {
  947. set n [expr -$n]
  948. }
  949. set res [list [$frbx.box get $ix]]
  950. lappend res $n
  951. foreach x {weight slant} { lappend res $GMFtDial($x) }
  952. foreach x {underline overstrike} {
  953. if { $GMFtDial($x) } { lappend res $x }
  954. }
  955. break
  956. }
  957. }
  958. }
  959. DestroyRGrabs $w $grabs
  960. destroy $w
  961. return $res
  962. }
  963. ### utilities for dealing with parameters in a dialog
  964. proc GMSetupParams {w vars descs} {
  965. # set-up widgets for setting parameters in a dialog
  966. # $w is window parent
  967. # $vars is list of (global) vars to set; they must have a value
  968. # except those associated to entries which will be initialised to ""
  969. # and to menubuttons that if undefined will be initialised to "";
  970. # array elements may also be given instead of normal variables but
  971. # the indices must be alphanumeric
  972. # $descs is associated list of value descriptions as:
  973. # @TEXT checkbutton with label TEXT, values 0 1
  974. # =@TEXT non-echo entry with label TEXT
  975. # =TEXT entry with label TEXT
  976. # !TEXT=MENUPROC/ARGS menubutton with label TEXT and menu filled by
  977. # proc MENUPROC; the arguments to the MENUPROC call are:
  978. # - the menu window
  979. # - the command to be associated with final entries, whose
  980. # arguments are the selected value and the menu window
  981. # - the elements of the list ARGS
  982. # |TEXT/LIST label TEXT and menubutton with text-variable for values
  983. # in LIST
  984. # +TEXT/LIST radiobuttons with possible values in LIST, label TEXT
  985. # /TEXT|LIST radiobuttons with possible values in LIST, label TEXT
  986. # ~TEXT/LIST radiobuttons with possible values in LIST but their
  987. # names are in the array TXT, label TEXT
  988. # LIST radiobutton with possible values in LIST
  989. # LISTs above cannot have repeated elements
  990. # return pair with flag set if there are menubuttons, and list of entries,
  991. # each as a triple, usually with path from $w to entry, the name of
  992. # global (array or normal) variable to be used in "global" and complete
  993. # name of variable to be used in "set"; for non-echo entries the
  994. # path is prefixed by a "@"; the list can be processed by proc GMUseEntries
  995. global COLOUR TXT NEEntry
  996. set i 0 ; set es "" ; set menus 0
  997. foreach v $vars os $descs {
  998. if { [regexp {^([^(]+)[(]([^)]+)[)]$} $v x vname el] } {
  999. set vid "${vname}___ARR_$el"
  1000. } else { set vid $v ; set vname $v }
  1001. global $vname
  1002. frame $w.f$i
  1003. switch -glob -- $os {
  1004. @* {
  1005. set os [string replace $os 0 0]
  1006. set cb $w.f$i.c$vid
  1007. checkbutton $cb -text $os -variable $v -anchor w \
  1008. -onvalue 1 -offvalue 0 -selectcolor $COLOUR(check)
  1009. if { [set $v] } {
  1010. $cb select
  1011. } else { $cb deselect }
  1012. pack $cb
  1013. }
  1014. =* {
  1015. if { [string index $os 1] == "@" } {
  1016. set z 1
  1017. } else { set z 0 }
  1018. set os [string replace $os 0 $z]
  1019. set wl [label $w.f$i.l$vid -text $os]
  1020. set ppath f$i.e$vid
  1021. set we [entry $w.f$i.e$vid -width 30]
  1022. TextBindings $we
  1023. if { $z } {
  1024. set NEEntry($we) ""
  1025. bind $we <Delete> "GMNEEntry $we _ BackSpace ; break"
  1026. bind $we <Any-Key> "GMNEEntry $we %A %K ; break"
  1027. set ppath "@$ppath"
  1028. }
  1029. if { [catch {set $v}] } {
  1030. set $v ""
  1031. } elseif { $z == 0 } { $we insert 0 [set $v] }
  1032. pack $wl $we -side left
  1033. lappend es [list $ppath $vname $v]
  1034. }
  1035. !* {
  1036. set menus 1
  1037. if { ! [regexp {^!([^=]+)=([^/]+)/(.*)$} $os \
  1038. m lab menuproc mpargs] } {
  1039. BUG Bad argument to GMSetupParams !
  1040. }
  1041. set mb $w.f$i.mb$vid
  1042. menubutton $mb -text $lab -relief raised \
  1043. -direction below -menu $mb.m
  1044. menu $mb.m
  1045. eval $menuproc $mb.m GMChangeParam $mpargs
  1046. if { [catch {set $v}] } {
  1047. set $v ""
  1048. }
  1049. set wl [label $w.f$i.l$vid -textvariable $v]
  1050. pack $mb $wl -side left
  1051. }
  1052. |* {
  1053. set menus 1
  1054. if { ! [regexp {^[|]([^/]+)/(.*)$} $os \
  1055. m lab lst] } {
  1056. BUG Bad argument to GMSetupParams |
  1057. }
  1058. set wl [label $w.f$i.t$vid -text $lab -width 16]
  1059. set mb $w.f$i.mb$vid
  1060. menubutton $mb -textvariable $v -relief raised \
  1061. -direction below -menu $mb.m
  1062. menu $mb.m
  1063. foreach x $lst {
  1064. $mb.m add command -label $x -command "set $v $x"
  1065. }
  1066. pack $wl $mb -side left
  1067. }
  1068. +* - /* - ~* {
  1069. set labval [string first "~" $os]
  1070. if { ! [regexp {^.([^/]+)/(.+)$} $os m lab lst] } {
  1071. BUG Bad argument to GMSetupParams +/~
  1072. continue
  1073. }
  1074. pack [label $w.f$i.l$vid -text $lab] -side left
  1075. set k 0
  1076. set wrb $w.f$i.r_${vid}_0
  1077. foreach o $lst {
  1078. if { $labval } {
  1079. set lv $o
  1080. } else { set lv $TXT($o) }
  1081. set rb $w.f$i.r_${vid}_$k
  1082. radiobutton $rb -text $lv -variable $v \
  1083. -value $o -anchor w -selectcolor $COLOUR(check)
  1084. pack $rb -side left -padx 2
  1085. if { [set $v] == $o } { set wrb $rb }
  1086. incr k
  1087. }
  1088. $wrb invoke
  1089. }
  1090. default {
  1091. set k 0
  1092. set wrb $w.f$i.rd_${vid}_0
  1093. foreach o $os {
  1094. set rb $w.f$i.rd_${vid}_$k
  1095. radiobutton $rb -text $o -variable $v \
  1096. -value $o -anchor w -selectcolor $COLOUR(check)
  1097. pack $rb -side left -padx 2
  1098. if { [set $v] == $o } { set wrb $rb }
  1099. incr k
  1100. }
  1101. $wrb invoke
  1102. }
  1103. }
  1104. pack $w.f$i -side top -fill x -expand 1
  1105. incr i
  1106. }
  1107. return [list $menus $es]
  1108. }
  1109. proc GMNEEntry {e char ksym} {
  1110. # keep track of characters typed in a non-echo entry $e
  1111. # current contents are kept on global NEEntry($e) that should be unset
  1112. # after use
  1113. global NEEntry PASSWDECHO
  1114. if { $PASSWDECHO == "none" } {
  1115. echo 0
  1116. $e delete 0 end
  1117. } else { set echo 1 }
  1118. if { $ksym == "BackSpace" } {
  1119. set NEEntry($e) [string replace $NEEntry($e) end end]
  1120. if { $echo } { $e delete 0 }
  1121. return
  1122. }
  1123. if { $ksym == $char || $ksym == "??" || [regexp {^[a-z]} $ksym] } {
  1124. append NEEntry($e) $char
  1125. if { $echo } { $e insert end $PASSWDECHO }
  1126. }
  1127. return
  1128. }
  1129. proc GMChangeParam {val varmenu args} {
  1130. # parameter value changed by a selection in a menu
  1131. # $varmenu is either the menu path assumed to have a single occurrence
  1132. # of .mbVARID. or has the form =VARID where VARID either is the name
  1133. # of the global simple variable to set, or has is the string
  1134. # concatenation of a global array identifier, "___ARR_" and
  1135. # an array index
  1136. # $args may be TXT to force value to be $TXT($val)
  1137. global TXT
  1138. if { ! [regexp {^=(.+)$} $varmenu x v] } {
  1139. regexp {\.mb([^.]+)\.} $varmenu x v
  1140. }
  1141. if { [regexp {^(.+)___ARR_(.+)$} $v x v ix] } {
  1142. global $v
  1143. append v "(" $ix ")"
  1144. } else { global $v }
  1145. if { $args == "TXT" } {
  1146. set val $TXT($val)
  1147. }
  1148. set $v $val
  1149. return
  1150. }
  1151. proc GMUseEntries {w es} {
  1152. # set global variables according to entries set-up by proc GMSetupParams
  1153. # $w is window parent
  1154. # $es is list of triples usually with path from $w to entry,
  1155. # name of global (array or normal) variable to be used in "global" and
  1156. # complete name of variable to be used in "set"; for non-echo entries
  1157. # the path is prefixed with a "@"
  1158. # current contents of non-echo entries are kept on global array NEEntry
  1159. # (see proc GMNEEntry) and corresponding elements are unset here
  1160. global NEEntry
  1161. foreach e $es {
  1162. global [lindex $e 1]
  1163. if { [string index [set ppath [lindex $e 0]] 0] == "@" } {
  1164. set ppath $w.[string replace $ppath 0 0]
  1165. set v $NEEntry($ppath)
  1166. unset NEEntry($ppath)
  1167. } else { set v [$w.$ppath get] }
  1168. set [lindex $e 2] $v
  1169. }
  1170. return
  1171. }
  1172. ### image listbox widget
  1173. proc ImageListbox {act path args} {
  1174. # implements a new widget whose model is a listbox but has entries
  1175. # with an image and possibly a text label
  1176. # $act is the action to perform and determines $args
  1177. # create SIZE WIDTH EHEIGHT SELECTMODE ?SCROLLBAR?
  1178. # insert INDEX IMAGE TEXT ?TAGS?; return either index or -1 if entry
  1179. # can not be inserted because IMAGE cannot be displayed and TEXT
  1180. # is empty
  1181. # delete INDEX ?INDEX?
  1182. # get INDEX ?INDEX? ; return list of texts in entries
  1183. # gettags INDEX ?INDEX? ; return list of tags in entries
  1184. # selclr INDEX ?INDEX? ; clear selected
  1185. # selset INDEX ?INDEX? ; set as selected (irrespective of SELECTMODE)
  1186. # cursel "" ; return list of indices of currently selected entries
  1187. # getsel "" ; return list of texts in currently selected entries
  1188. # getseltags "" ; return list of lists each with the tags in currently
  1189. # selected entries
  1190. # seldel "" ; delete selected entries
  1191. # index Y ; return index of entry at y-coordinate (inside listbox)
  1192. # destroyall "" ; destroy all image listboxes under window $path
  1193. # where
  1194. # SELECTMODE is one of {single, extended}
  1195. # EHEIGHT is the height for the entries in pixels (minimum used: 5)
  1196. # INDEX is either a numeric index from 0 or "end"
  1197. # TAGS is a list
  1198. # the widget should be packed or grided by caller after being created
  1199. # images that have more than EHEIGHT-4 in width or height are either
  1200. # truncated to that size if they are of type photo, or not displayed
  1201. # information related to these widgets is stored in global array GMIBox
  1202. # auxiliary images are created but never deleted; their names can be
  1203. # retrieved from GMIBox(img,*) entries
  1204. # bindings on entries:
  1205. # <Button-1> deselects everything, selects entry
  1206. # if SELECTMODE=="extended":
  1207. # <Control-Button-1> toggles selection state of entry
  1208. # <Shift-Button-1> selects range from last selected entry to entry
  1209. global GMIBox COLOUR
  1210. if { $act == "destroyall" } {
  1211. foreach n [array names GMIBox $path*,csize] {
  1212. regsub {,csize$} $n "" lbox
  1213. destroy $lbox
  1214. }
  1215. foreach n [array names GMIBox $path*] { unset GMIBox($n) }
  1216. return
  1217. }
  1218. if { [set nargs [llength $args]] != 0 } {
  1219. foreach "a1 a2 a3 a4 a5" $args { break }
  1220. }
  1221. if { $act != "create" } {
  1222. if { [catch {set csize $GMIBox($path,csize)}] } {
  1223. BUG trying to use non-existing ImageListbox
  1224. }
  1225. set end $csize
  1226. if { $end > 0 } { incr end -1 }
  1227. foreach x "sel eh ew mode" {
  1228. set $x $GMIBox($path,$x)
  1229. }
  1230. }
  1231. set res ""
  1232. switch $act {
  1233. create {
  1234. # SIZE WIDTH EHEIGHT MODE ?SCROLLBAR?
  1235. if { $nargs < 4 } { BUG missing args to ImageListbox create }
  1236. if { $a3 < 5 } { set a3 5 }
  1237. set height [expr $a1*$a3]
  1238. foreach x "csize sel eh ew mode anchor base height" \
  1239. v "0 {} $a3 $a2 $a4 {} 0 $height" {
  1240. set GMIBox($path,$x) $v
  1241. }
  1242. canvas $path -height $height -width $a2 -confine 1 \
  1243. -borderwidth 2 -relief sunken
  1244. if { $a5 != "" } {
  1245. $path configure -yscrollincrement $a3 \
  1246. -yscrollcommand "ImageListboxScroll $path $a5" \
  1247. -scrollregion "0 0 $a2 $height"
  1248. trace variable GMIBox($path,csize) w ImageListboxResize
  1249. }
  1250. $path bind entry <Shift-Button-1> \
  1251. "ImageListboxESButton $path %y ; break"
  1252. $path bind entry <Control-Button-1> \
  1253. "ImageListboxECButton $path %y ; break"
  1254. $path bind entry <Button-1> "ImageListboxEButton $path %y"
  1255. }
  1256. insert {
  1257. # INDEX IMAGE TEXT ?TAGS?
  1258. if { $nargs < 3 } { BUG missing args to ImageListbox insert }
  1259. set ih [expr $eh-2]
  1260. if { $a2 != "" && \
  1261. ([image width $a2] > $ih || [image height $a2] > $ih) } {
  1262. if { [image type $a2] != "photo" } {
  1263. if { $a3 == "" } { return -1 }
  1264. set a2 ""
  1265. } else {
  1266. if { [catch {set im $GMIBox(img,for,$a2)}] } {
  1267. set im [image create photo -width $ih -height $ih]
  1268. $im copy $a2 -from 0 0 $ih $ih
  1269. set GMIBox(img,for,$a2) $im
  1270. }
  1271. set a2 $im
  1272. }
  1273. }
  1274. if { $a1 != "end" } {
  1275. set na1 [ImageListboxIndices $path $end $a1]
  1276. if { $a1 > $na1 } {
  1277. # assuming given index must be an integer
  1278. set na1 $csize
  1279. }
  1280. # update selection
  1281. set s ""
  1282. foreach e $GMIBox($path,sel) {
  1283. if { $e >= $na1 } { incr e }
  1284. lappend s $e
  1285. }
  1286. set GMIBox($path,sel) $s
  1287. # move lower entries down
  1288. set y0 [expr $na1*$eh]
  1289. if { $csize > 0 && $na1 < $csize } {
  1290. foreach it [$path find withtag entry] {
  1291. if { [lindex [$path coords $it] 1] >= $y0 } {
  1292. $path move $it 0 $eh
  1293. }
  1294. }
  1295. }
  1296. } else { set y0 [expr $csize*$eh] }
  1297. $path create rectangle 1 [expr $y0+1] $ew [expr $y0+$eh] \
  1298. -fill $COLOUR(bg) -outline $COLOUR(bg) \
  1299. -tags [list txt entry bg "tgs=$a4"]
  1300. # texts are created even if empty so that they can be retrieved
  1301. $path create text [expr $eh+8] [expr $y0+$eh/2] -anchor w \
  1302. -text $a3 -fill $COLOUR(fg) \
  1303. -tags [list txt entry "txt=$a3"]
  1304. if { $a2 != "" } {
  1305. $path create image 5 [expr $y0+2] -anchor nw -image $a2 \
  1306. -tags "img entry"
  1307. }
  1308. incr GMIBox($path,csize)
  1309. update idletasks
  1310. }
  1311. delete {
  1312. # INDEX ?INDEX?
  1313. if { $nargs < 1 } { BUG missing args to ImageListbox delete }
  1314. foreach "a1 a2" [ImageListboxIndices $path $end [list $a1 $a2]] {}
  1315. if { $a2 == "" } { set a2 $a1 }
  1316. if { [set ndel [expr $a2-$a1+1]] == $csize } {
  1317. $path delete all
  1318. set GMIBox($path,sel) ""
  1319. } else {
  1320. # update selection
  1321. set s ""
  1322. foreach e $GMIBox($path,sel) {
  1323. if { $e < $a1 } {
  1324. lappend s $e
  1325. } elseif { $e > $a2 } {
  1326. lappend s [expr $e-$ndel]
  1327. }
  1328. }
  1329. set GMIBox($path,sel) $s
  1330. # move lower entries up
  1331. set y0 [expr $a1*$eh] ; set yn [expr ($a2+1)*$eh]
  1332. set dy [expr -$ndel*$eh]
  1333. foreach it [$path find withtag entry] {
  1334. if { [set y [lindex [$path coords $it] 1]] >= $y0 } {
  1335. if { $y >= $yn } {
  1336. $path move $it 0 $dy
  1337. } else { $path delete $it }
  1338. }
  1339. }
  1340. }
  1341. set GMIBox($path,csize) [expr $csize-$ndel]
  1342. update idletasks
  1343. }
  1344. get {
  1345. # INDEX ?INDEX?
  1346. if { $nargs < 1 } { BUG missing args to ImageListbox get }
  1347. foreach "a1 a2" [ImageListboxIndices $path $end [list $a1 $a2]] {}
  1348. if { $a2 == "" } { set a2 $a1 }
  1349. set y0 [expr $a1*$eh] ; set yn [expr ($a2+1)*$eh]
  1350. set r ""
  1351. foreach it [$path find withtag txt] {
  1352. if { [set y [lindex [$path coords $it] 1]] >= $y0 && \
  1353. $y < $yn } {
  1354. foreach t [$path gettags $it] {
  1355. if { [regsub {^txt=} $t "" tx] } {
  1356. lappend r [list [expr round($y)] $tx]
  1357. break
  1358. }
  1359. }
  1360. }
  1361. }
  1362. foreach p [lsort -integer -index 0 $r] {
  1363. lappend res [lindex $p 1]
  1364. }
  1365. }
  1366. gettags {
  1367. # INDEX ?INDEX?
  1368. if { $nargs < 1 } { BUG missing args to ImageListbox gettags }
  1369. foreach "a1 a2" [ImageListboxIndices $path $end [list $a1 $a2]] {}
  1370. if { $a2 == "" } { set a2 $a1 }
  1371. set y0 [expr $a1*$eh] ; set yn [expr ($a2+1)*$eh]
  1372. set r ""
  1373. foreach it [$path find withtag txt] {
  1374. if { [set y [lindex [$path coords $it] 1]] >= $y0 && \
  1375. $y < $yn } {
  1376. foreach t [$path gettags $it] {
  1377. if { [regsub {^tgs=} $t "" tgs] } {
  1378. lappend r [list [expr round($y)] $tgs]
  1379. break
  1380. }
  1381. }
  1382. }
  1383. }
  1384. foreach p [lsort -integer -index 0 $r] {
  1385. lappend res [lindex $p 1]
  1386. }
  1387. }
  1388. selset {
  1389. # INDEX ?INDEX?
  1390. # add to selection, irrespective of $mode
  1391. # keep selection list ordered
  1392. if { $nargs < 1 } { BUG missing args to ImageListbox selset }
  1393. foreach "a1 a2" [ImageListboxIndices $path $end [list $a1 $a2]] {}
  1394. if { $a2 == "" } { set a2 $a1 }
  1395. set y0 [expr $a1*$eh+2]
  1396. set s ""
  1397. foreach ix $sel {
  1398. if { $ix == $a1 } {
  1399. if { [incr a1] > $a2 } {
  1400. set a1 1e10
  1401. } else { set y0 [expr $y0+$eh] }
  1402. } else {
  1403. while { $a1 < $ix } {
  1404. ImageListboxSelect sel $path $y0
  1405. lappend s $a1
  1406. if { [incr a1] > $a2 } {
  1407. set a1 1e10
  1408. } else { set y0 [expr $y0+$eh] }
  1409. }
  1410. }
  1411. lappend s $ix
  1412. }
  1413. while { $a1 <= $a2 } {
  1414. ImageListboxSelect sel $path $y0
  1415. lappend s $a1
  1416. incr a1 ; set y0 [expr $y0+$eh]
  1417. }
  1418. set GMIBox($path,sel) $s
  1419. update idletasks
  1420. }
  1421. selclr {
  1422. # INDEX ?INDEX?
  1423. # keep selection list ordered
  1424. if { $nargs < 1 } { BUG missing args to ImageListbox selclr }
  1425. foreach "a1 a2" [ImageListboxIndices $path $end [list $a1 $a2]] {}
  1426. if { $a2 == "" } { set a2 $a1 }
  1427. set s ""
  1428. foreach ix $sel {
  1429. if { $ix >= $a1 && $ix <= $a2 } {
  1430. ImageListboxSelect clear $path [expr $ix*$eh+2]
  1431. } else { lappend s $ix }
  1432. }
  1433. set GMIBox($path,sel) $s
  1434. update idletasks
  1435. }
  1436. cursel {
  1437. set res $sel
  1438. }
  1439. getsel {
  1440. set dy [expr $eh-1]
  1441. foreach ix $sel {
  1442. set y0 [expr $ix*$eh]
  1443. foreach it [$path find overlapping 0 $y0 100 [expr $y0+$dy]] {
  1444. foreach t [$path gettags $it] {
  1445. if { [regsub {^txt=} $t "" tx] } {
  1446. lappend res $tx
  1447. break
  1448. }
  1449. }
  1450. }
  1451. }
  1452. }
  1453. getseltags {
  1454. foreach ix $sel {
  1455. set y0 [expr ($ix+0.5)*$eh]
  1456. foreach it [$path find overlapping 0 $y0 100 [expr $y0+4]] {
  1457. foreach t [$path gettags $it] {
  1458. if { [regsub {^tgs=} $t "" tgs] } {
  1459. lappend res $tgs
  1460. break
  1461. }
  1462. }
  1463. }
  1464. }
  1465. }
  1466. seldel {
  1467. set dy [expr -$eh]
  1468. foreach ix [lsort -integer -decreasing $sel] {
  1469. # move lower entries up
  1470. set y0 [expr $ix*$eh] ; set yn [expr $y0+$eh]
  1471. foreach it [$path find withtag entry] {
  1472. if { [set y [lindex [$path coords $it] 1]] >= $yn } {
  1473. $path move $it 0 $dy
  1474. } elseif { $y >= $y0 } { $path delete $it }
  1475. }
  1476. }
  1477. set GMIBox($path,csize) [expr $csize-[llength $sel]]
  1478. set GMIBox($path,sel) ""
  1479. update idletasks
  1480. }
  1481. index {
  1482. # Y (coordinates inside listbox)
  1483. if { $nargs < 1 } { BUG missing args to ImageListbox index }
  1484. set res [expr int($a1/$GMIBox($path,eh))+$GMIBox($path,base)]
  1485. if { $res > $end } { set res $end }
  1486. }
  1487. default { BUG calling ImageListbox with wrong action }
  1488. }
  1489. return $res
  1490. }
  1491. proc ImageListboxScroll {path scr pos0 posf} {
  1492. # scrolling image listbox
  1493. # $scr is scrollbar
  1494. # $pos0, $posf are the arguments to the scrolling command
  1495. # percentage of vertical dimension for top and bottom positions
  1496. global GMIBox
  1497. set s $GMIBox($path,csize)
  1498. set GMIBox($path,base) [expr round($s*$pos0)]
  1499. $scr set $pos0 $posf…

Large files files are truncated, but you can click here to view the full file