PageRenderTime 61ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/amsn-0.98.9/utils/macosx/snack2.2/snack.tcl

#
TCL | 1348 lines | 1033 code | 195 blank | 120 comment | 180 complexity | 538599c1ccaa5d545458ab8616edd0fa MD5 | raw file
Possible License(s): GPL-2.0, AGPL-3.0, LGPL-2.1
  1. #
  2. # Copyright (C) 1997-99 Kare Sjolander <kare@speech.kth.se>
  3. #
  4. # This file is part of the Snack sound extension for Tcl/Tk.
  5. # The latest version can be found at http://www.speech.kth.se/snack/
  6. #
  7. # This program is free software; you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation; either version 2 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # This program is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with this program; if not, write to the Free Software
  19. # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. #
  21. package provide snack 2.2
  22. # Set playback latency according to the environment variable PLAYLATENCY
  23. if {$::tcl_platform(platform) == "unix"} {
  24. if {[info exists env(PLAYLATENCY)] && $env(PLAYLATENCY) > 0} {
  25. snack::audio playLatency $env(PLAYLATENCY)
  26. }
  27. }
  28. namespace eval snack {
  29. namespace export gainBox get* add* menu* frequencyAxis timeAxis \
  30. createIcons mixerDialog sound audio mixer debug
  31. #
  32. # Gain control dialog
  33. #
  34. proc gainBox flags {
  35. variable gainbox
  36. catch {destroy .snackGainBox}
  37. toplevel .snackGainBox
  38. wm title .snackGainBox {Gain Control Panel}
  39. if {[string match *p* $flags]} {
  40. set gainbox(play) [snack::audio play_gain]
  41. pack [scale .snackGainBox.s -label {Play volume} -orient horiz \
  42. -variable snack::gainbox(play) \
  43. -command {snack::audio play_gain} \
  44. -length 200]
  45. }
  46. if {[snack::mixer inputs] != ""} {
  47. if {[string match *r* $flags]} {
  48. set gainbox(rec) [snack::audio record_gain]
  49. pack [scale .snackGainBox.s2 -label {Record gain} \
  50. -orient horiz \
  51. -variable snack::gainbox(rec) \
  52. -command {snack::audio record_gain} \
  53. -length 200]
  54. }
  55. }
  56. pack [button .snackGainBox.exitB -text Close -command {destroy .snackGainBox}]
  57. }
  58. #
  59. # Snack mixer dialog
  60. #
  61. proc flipScaleValue {scaleVar var args} {
  62. set $var [expr 100-[set $scaleVar]]
  63. }
  64. proc mixerDialog {} {
  65. set wi .snackMixerDialog
  66. catch {destroy $wi}
  67. toplevel $wi
  68. wm title $wi "Mixer"
  69. # pack [frame $wi.f0]
  70. # label $wi.f0.l -text "Mixer device:"
  71. # set outDevList [snack::mixer devices]
  72. # eval tk_optionMenu $wi.f0.om mixerDev $outDevList
  73. # pack $wi.f0.l $wi.f0.om -side left
  74. pack [frame $wi.f] -expand yes -fill both
  75. foreach line [snack::mixer lines] {
  76. pack [frame $wi.f.g$line -bd 1 -relief solid] -side left \
  77. -expand yes -fill both
  78. pack [label $wi.f.g$line.l -text $line]
  79. if {[snack::mixer channels $line] == "Mono"} {
  80. snack::mixer volume $line snack::v(r$line)
  81. } else {
  82. snack::mixer volume $line snack::v(l$line) snack::v(r$line)
  83. if {[info exists tile::version]} {
  84. pack [ttk::scale $wi.f.g$line.e -from 0 -to 100 -show no -orient vertical \
  85. -var snack::v(lI$line) -command [namespace code [list flipScaleValue ::snack::v(lI$line) ::snack::v(l$line)]]] -side left -expand yes -fill y
  86. set snack::v(lI$line) [expr 100-[lindex [snack::mixer volume $line] end]]
  87. $wi.f.g$line.e set $snack::v(lI$line)
  88. } else {
  89. pack [scale $wi.f.g$line.e -from 100 -to 0 -show no -orient vertical \
  90. -var snack::v(l$line)] -side left -expand yes -fill both
  91. }
  92. }
  93. if {[info exists tile::version]} {
  94. pack [ttk::scale $wi.f.g$line.s -from 0 -to 100 -show no -orient vertical \
  95. -var snack::v(rI$line) -command [namespace code [list flipScaleValue ::snack::v(rI$line) ::snack::v(r$line)]]] -expand yes -fill y
  96. set snack::v(rI$line) [expr 100-[lindex [snack::mixer volume $line] end]]
  97. $wi.f.g$line.s set $snack::v(rI$line)
  98. } else {
  99. pack [scale $wi.f.g$line.s -from 100 -to 0 -show no -orient vertical \
  100. -var snack::v(r$line)] -expand yes -fill both
  101. }
  102. }
  103. pack [frame $wi.f.f2] -side left
  104. if {[snack::mixer inputs] != ""} {
  105. pack [label $wi.f.f2.li -text "Input jacks:"]
  106. foreach jack [snack::mixer inputs] {
  107. snack::mixer input $jack [namespace current]::v(in$jack)
  108. pack [checkbutton $wi.f.f2.b$jack -text $jack \
  109. -variable [namespace current]::v(in$jack)] \
  110. -anchor w
  111. }
  112. }
  113. if {[snack::mixer outputs] != ""} {
  114. pack [label $wi.f.f2.lo -text "Output jacks:"]
  115. foreach jack [snack::mixer outputs] {
  116. snack::mixer output $jack [namespace current]::v(out$jack)
  117. pack [checkbutton $wi.f.f2.b$jack -text $jack \
  118. -variable [namespace current]::v(out$jack)] \
  119. -anchor w
  120. }
  121. }
  122. pack [button $wi.b1 -text Close -command "destroy $wi"]
  123. }
  124. #
  125. # Snack filename dialog
  126. #
  127. proc getOpenFile {args} {
  128. upvar #0 __snack_args data
  129. set specs {
  130. {-title "" "" "Open file"}
  131. {-initialdir "" "" "."}
  132. {-initialfile "" "" ""}
  133. {-multiple "" "" 0}
  134. {-format "" "" "none"}
  135. }
  136. tclParseConfigSpec __snack_args $specs "" $args
  137. if {$data(-format) == "none"} {
  138. if {$data(-initialfile) != ""} {
  139. set data(-format) [ext2fmt [file extension $data(-initialfile)]]
  140. } else {
  141. set data(-format) WAV
  142. }
  143. }
  144. if {$data(-format) == ""} {
  145. set data(-format) RAW
  146. }
  147. set data(-format) [string toupper $data(-format)]
  148. if {$data(-initialdir) == ""} {
  149. set data(-initialdir) "."
  150. }
  151. if {[string match Darwin $::tcl_platform(os)]} {
  152. return [tk_getOpenFile -title $data(-title) \
  153. -multiple $data(-multiple) \
  154. -filetypes [loadTypes $data(-format)] \
  155. -defaultextension [fmt2ext $data(-format)] \
  156. -initialdir $data(-initialdir)]
  157. }
  158. # Later Tcl's allow multiple files returned as a list
  159. if {$::tcl_version <= 8.3} {
  160. set res [tk_getOpenFile -title $data(-title) \
  161. -filetypes [loadTypes $data(-format)] \
  162. -defaultextension [fmt2ext $data(-format)] \
  163. -initialdir $data(-initialdir) \
  164. -initialfile $data(-initialfile)]
  165. } else {
  166. set res [tk_getOpenFile -title $data(-title) \
  167. -multiple $data(-multiple) \
  168. -filetypes [loadTypes $data(-format)] \
  169. -defaultextension [fmt2ext $data(-format)] \
  170. -initialdir $data(-initialdir) \
  171. -initialfile $data(-initialfile)]
  172. }
  173. return $res
  174. }
  175. set loadTypes ""
  176. proc addLoadTypes {typelist fmtlist} {
  177. variable loadTypes
  178. variable filebox
  179. set loadTypes $typelist
  180. set i 9 ; # Needs updating when adding new formats
  181. foreach fmt $fmtlist {
  182. set filebox(l$fmt) $i
  183. incr i
  184. }
  185. }
  186. proc loadTypes fmt {
  187. variable loadTypes
  188. variable filebox
  189. if {$::tcl_platform(platform) == "windows"} {
  190. set l [concat {{{MS Wav Files} {.wav}} {{Smp Files} {.smp}} {{Snd Files} {.snd}} {{AU Files} {.au}} {{AIFF Files} {.aif}} {{AIFF Files} {.aiff}} {{Waves Files} {.sd}} {{MP3 Files} {.mp3}} {{CSL Files} {.nsp}}} $loadTypes {{{All Files} * }}]
  191. } else {
  192. set l [concat {{{MS Wav Files} {.wav .WAV}} {{Smp Files} {.smp .SMP}} {{Snd Files} {.snd .SND}} {{AU Files} {.au .AU}} {{AIFF Files} {.aif .AIF}} {{AIFF Files} {.aiff .AIFF}} {{Waves Files} {.sd .SD}} {{MP3 Files} {.mp3 .MP3}} {{CSL Files} {.nsp .NSP}}} $loadTypes {{{All Files} * }}]
  193. }
  194. return [swapListElem $l $filebox(l$fmt)]
  195. }
  196. variable filebox
  197. set filebox(RAW) .raw
  198. set filebox(SMP) .smp
  199. set filebox(AU) .au
  200. set filebox(WAV) .wav
  201. set filebox(SD) .sd
  202. set filebox(SND) .snd
  203. set filebox(AIFF) .aif
  204. set filebox(MP3) .mp3
  205. set filebox(CSL) .nsp
  206. set filebox(lWAV) 0
  207. set filebox(lSMP) 1
  208. set filebox(lSND) 2
  209. set filebox(lAU) 3
  210. set filebox(lAIFF) 4
  211. # skip 2 because of aif and aiff
  212. set filebox(lSD) 6
  213. set filebox(lMP3) 7
  214. set filebox(lCSL) 8
  215. set filebox(lRAW) end
  216. # Do not forget to update indexes
  217. set filebox(sWAV) 0
  218. set filebox(sSMP) 1
  219. set filebox(sSND) 2
  220. set filebox(sAU) 3
  221. set filebox(sAIFF) 4
  222. # skip 2 because of aif and aiff
  223. set filebox(sCSL) 6
  224. set filebox(sRAW) end
  225. proc fmt2ext fmt {
  226. variable filebox
  227. return $filebox($fmt)
  228. }
  229. proc addExtTypes extlist {
  230. variable filebox
  231. foreach pair $extlist {
  232. set filebox([lindex $pair 0]) [lindex $pair 1]
  233. }
  234. }
  235. proc getSaveFile args {
  236. upvar #0 __snack_args data
  237. set specs {
  238. {-title "" "" "Save file"}
  239. {-initialdir "" "" "."}
  240. {-initialfile "" "" ""}
  241. {-format "" "" "none"}
  242. }
  243. tclParseConfigSpec __snack_args $specs "" $args
  244. if {$data(-format) == "none"} {
  245. if {$data(-initialfile) != ""} {
  246. set data(-format) [ext2fmt [file extension $data(-initialfile)]]
  247. } else {
  248. set data(-format) WAV
  249. }
  250. }
  251. if {$data(-format) == ""} {
  252. set data(-format) RAW
  253. }
  254. set data(-format) [string toupper $data(-format)]
  255. if {$data(-initialdir) == ""} {
  256. set data(-initialdir) "."
  257. }
  258. if {[string match macintosh $::tcl_platform(platform)]} {
  259. set tmp [tk_getSaveFile -title $data(-title) \
  260. -initialdir $data(-initialdir) -initialfile $data(-initialfile)]
  261. if {[string compare [file ext $tmp] ""] == 0} {
  262. append tmp [fmt2ext $data(-format)]
  263. }
  264. return $tmp
  265. } else {
  266. return [tk_getSaveFile -title $data(-title) \
  267. -filetypes [saveTypes $data(-format)] \
  268. -defaultextension [fmt2ext $data(-format)] \
  269. -initialdir $data(-initialdir) -initialfile $data(-initialfile)]
  270. }
  271. }
  272. set saveTypes ""
  273. proc addSaveTypes {typelist fmtlist} {
  274. variable saveTypes
  275. variable filebox
  276. set saveTypes $typelist
  277. set j 7 ; # Needs updating when adding new formats
  278. foreach fmt $fmtlist {
  279. set filebox(s$fmt) $j
  280. incr j
  281. }
  282. }
  283. proc saveTypes fmt {
  284. variable saveTypes
  285. variable filebox
  286. if {[info exists filebox(s$fmt)] == 0} {
  287. set fmt RAW
  288. }
  289. if {$::tcl_platform(platform) == "windows"} {
  290. set l [concat {{{MS Wav Files} {.wav}} {{Smp Files} {.smp}} {{Snd Files} {.snd}} {{AU Files} {.au}} {{AIFF Files} {.aif}} {{AIFF Files} {.aiff}} {{CSL Files} {.nsp}}} $saveTypes {{{All Files} * }}]
  291. } else {
  292. set l [concat {{{MS Wav Files} {.wav .WAV}} {{Smp Files} {.smp .SMP}} {{Snd Files} {.snd .SND}} {{AU Files} {.au .AU}} {{AIFF Files} {.aif .AIF}} {{AIFF Files} {.aiff .AIFF}} {{CSL Files} {.nsp .NSP}}} $saveTypes {{{All Files} * }}]
  293. }
  294. return [swapListElem $l $filebox(s$fmt)]
  295. }
  296. proc swapListElem {l n} {
  297. set tmp [lindex $l $n]
  298. set l [lreplace $l $n $n]
  299. return [linsert $l 0 $tmp]
  300. }
  301. set filebox(.wav) WAV
  302. set filebox(.smp) SMP
  303. set filebox(.au) AU
  304. set filebox(.raw) RAW
  305. set filebox(.snd) SND
  306. set filebox(.sd) SD
  307. set filebox(.aif) AIFF
  308. set filebox(.aiff) AIFF
  309. set filebox(.mp3) MP3
  310. set filebox(.nsp) CSL
  311. set filebox() WAV
  312. proc ext2fmt ext {
  313. variable filebox
  314. return $filebox($ext)
  315. }
  316. #
  317. # Menus
  318. #
  319. proc menuInit { {m .menubar} } {
  320. variable menu
  321. menu $m
  322. [winfo parent $m] configure -menu $m
  323. set menu(menubar) $m
  324. set menu(uid) 0
  325. }
  326. proc menuPane {label {u 0} {postcommand ""}} {
  327. variable menu
  328. if [info exists menu(menu,$label)] {
  329. error "Menu $label already defined"
  330. }
  331. if {$label == "Help"} {
  332. set name $menu(menubar).help
  333. } else {
  334. set name $menu(menubar).mb$menu(uid)
  335. }
  336. set m [menu $name -tearoff 1 -postcommand $postcommand]
  337. $menu(menubar) add cascade -label $label -menu $name -underline $u
  338. incr menu(uid)
  339. set menu(menu,$label) $m
  340. return $m
  341. }
  342. proc menuDelete {menuName label} {
  343. variable menu
  344. set m [menuGet $menuName]
  345. if [catch {$m index $label} index] {
  346. error "$label not in menu $menuName"
  347. }
  348. [menuGet $menuName] delete $index
  349. }
  350. proc menuDeleteByIndex {menuName index} {
  351. [menuGet $menuName] delete $index
  352. }
  353. proc menuGet menuName {
  354. variable menu
  355. if [catch {set menu(menu,$menuName)} m] {
  356. return -code error "No such menu: $menuName"
  357. }
  358. return $m
  359. }
  360. proc menuCommand {menuName label command} {
  361. [menuGet $menuName] add command -label $label -command $command
  362. }
  363. proc menuCheck {menuName label var {command {}} } {
  364. variable menu
  365. [menuGet $menuName] add check -label $label -command $command \
  366. -variable $var
  367. }
  368. proc menuRadio {menuName label var {val {}} {command {}} } {
  369. variable menu
  370. if {[string length $val] == 0} {
  371. set val $label
  372. }
  373. [menuGet $menuName] add radio -label $label -command $command \
  374. -value $val -variable $var
  375. }
  376. proc menuSeparator menuName {
  377. variable menu
  378. [menuGet $menuName] add separator
  379. }
  380. proc menuCascade {menuName label} {
  381. variable menu
  382. set m [menuGet $menuName]
  383. if [info exists menu(menu,$label)] {
  384. error "Menu $label already defined"
  385. }
  386. set sub $m.sub$menu(uid)
  387. incr menu(uid)
  388. menu $sub -tearoff 0
  389. $m add cascade -label $label -menu $sub
  390. set menu(menu,$label) $sub
  391. return $sub
  392. }
  393. proc menuBind {what char menuName label} {
  394. variable menu
  395. set m [menuGet $menuName]
  396. if [catch {$m index $label} index] {
  397. error "$label not in menu $menuName"
  398. }
  399. set command [$m entrycget $index -command]
  400. if {$::tcl_platform(platform) == "unix"} {
  401. bind $what <Alt-$char> $command
  402. $m entryconfigure $index -accelerator Alt-$char
  403. } else {
  404. bind $what <Control-$char> $command
  405. set char [string toupper $char]
  406. $m entryconfigure $index -accelerator Ctrl-$char
  407. }
  408. }
  409. proc menuEntryOff {menuName label} {
  410. variable menu
  411. set m [menuGet $menuName]
  412. if [catch {$m index $label} index] {
  413. error "$label not in menu $menuName"
  414. }
  415. $m entryconfigure $index -state disabled
  416. }
  417. proc menuEntryOn {menuName label} {
  418. variable menu
  419. set m [menuGet $menuName]
  420. if [catch {$m index $label} index] {
  421. error "$label not in menu $menuName"
  422. }
  423. $m entryconfigure $index -state normal
  424. }
  425. #
  426. # Vertical frequency axis
  427. #
  428. proc frequencyAxis {canvas x y width height args} {
  429. array set a [list \
  430. -tags snack_y_axis \
  431. -font {Helvetica 8} \
  432. -topfr 8000 \
  433. -fill black \
  434. -draw0 0
  435. ]
  436. if {[string match unix $::tcl_platform(platform)] } {
  437. set a(-font) {Helvetica 10}
  438. }
  439. array set a $args
  440. if {$height <= 0} return
  441. set ticklist [list 10 20 50 100 200 500 1000 2000 5000 \
  442. 10000 20000 50000 100000 200000 500000 1000000]
  443. set npt 10
  444. set dy [expr {double($height * $npt) / $a(-topfr)}]
  445. while {$dy < [font metrics $a(-font) -linespace]} {
  446. foreach elem $ticklist {
  447. if {$elem <= $npt} {
  448. continue
  449. }
  450. set npt $elem
  451. break
  452. }
  453. set dy [expr {double($height * $npt) / $a(-topfr)}]
  454. }
  455. if {$npt < 1000} {
  456. set hztext Hz
  457. } else {
  458. set hztext kHz
  459. }
  460. if $a(-draw0) {
  461. set i0 0
  462. set j0 0
  463. } else {
  464. set i0 $dy
  465. set j0 1
  466. }
  467. for {set i $i0; set j $j0} {$i < $height} {set i [expr {$i+$dy}]; incr j} {
  468. set yc [expr {$height + $y - $i}]
  469. if {$npt < 1000} {
  470. set t [expr {$j * $npt}]
  471. } else {
  472. set t [expr {$j * $npt / 1000}]
  473. }
  474. if {$yc > [expr {8 + $y}]} {
  475. if {[expr {$yc - [font metrics $a(-font) -ascent]}] > \
  476. [expr {$y + [font metrics $a(-font) -linespace]}] ||
  477. [font measure $a(-font) $hztext] < \
  478. [expr {$width - 8 - [font measure $a(-font) $t]}]} {
  479. $canvas create text [expr {$x +$width - 8}] [expr {$yc-2}]\
  480. -text $t -fill $a(-fill)\
  481. -font $a(-font) -anchor e -tags $a(-tags)
  482. }
  483. $canvas create line [expr {$x + $width - 5}] $yc \
  484. [expr {$x + $width}]\
  485. $yc -tags $a(-tags) -fill $a(-fill)
  486. }
  487. }
  488. $canvas create text [expr {$x + 2}] [expr {$y + 1}] -text $hztext \
  489. -font $a(-font) -anchor nw -tags $a(-tags) -fill $a(-fill)
  490. return $npt
  491. }
  492. #
  493. # Horizontal time axis
  494. #
  495. proc timeAxis {canvas ox oy width height pps args} {
  496. array set a [list \
  497. -tags snack_t_axis \
  498. -font {Helvetica 8} \
  499. -starttime 0.0 \
  500. -fill black \
  501. -format time \
  502. -draw0 0 \
  503. -drawvisible 0
  504. ]
  505. if {[string match unix $::tcl_platform(platform)] } {
  506. set a(-font) {Helvetica 10}
  507. }
  508. array set a $args
  509. if {$pps <= 0.004} return
  510. switch -- $a(-format) {
  511. time -
  512. seconds {
  513. set deltalist [list .0001 .0002 .0005 .001 .002 .005 \
  514. .01 .02 .05 .1 .2 .5 1 2 5 \
  515. 10 20 30 60 120 240 360 600 900 1800 3600 7200 14400]
  516. }
  517. "PAL frames" {
  518. set deltalist [list .04 .08 .4 .8 2 4 \
  519. 10 20 50 100 200 500 1000 2000 5000 10000 20000]
  520. }
  521. "NTSC frames" {
  522. set deltalist [list .03333333333334 .0666666666667 \
  523. .3333333333334 .666666666667 1 2 4 \
  524. 10 20 50 100 200 500 1000 2000 5000 10000 20000]
  525. }
  526. "10ms frames" {
  527. set deltalist [list .01 .02 .05 .1 .2 .5 1 2 5 \
  528. 10 20 50 100 200 500 1000 2000 5000 10000 20000]
  529. }
  530. }
  531. set majTickH [expr {$height - [font metrics $a(-font) -linespace]}]
  532. set minTickH [expr {$majTickH / 2}]
  533. # Create a typical time label
  534. set maxtime [expr {double($width) / $pps + $a(-starttime)}]
  535. if {$maxtime < 60} {
  536. set wtime 00
  537. } elseif {$maxtime < 3600} {
  538. set wtime 00:00
  539. } else {
  540. set wtime 00:00:00
  541. }
  542. if {$pps > 50} {
  543. append wtime .0
  544. } elseif {$pps > 500} {
  545. append wtime .00
  546. } elseif {$pps > 5000} {
  547. append wtime .000
  548. } elseif {$pps > 50000} {
  549. append wtime .0000
  550. }
  551. # Compute the distance in pixels (and time) between tick marks
  552. set dx [expr {10+[font measure $a(-font) $wtime]}]
  553. set dt [expr {double($dx) / $pps}]
  554. foreach elem $deltalist {
  555. if {$elem <= $dt} {
  556. continue
  557. }
  558. set dt $elem
  559. break
  560. }
  561. set dx [expr {$pps * $dt}]
  562. if {$dt < 0.00099} {
  563. set ndec 4
  564. } elseif {$dt < 0.0099} {
  565. set ndec 3
  566. } elseif {$dt < 0.099} {
  567. set ndec 2
  568. } else {
  569. set ndec 1
  570. }
  571. if {$a(-starttime) > 0.0} {
  572. set ft [expr {(int($a(-starttime) / $dt) + 1) * $dt}]
  573. set fx [expr {$pps * ($ft - $a(-starttime))}]
  574. } else {
  575. set ft 0
  576. set fx 0.0
  577. }
  578. set lx [expr {($ox + $width) * [lindex [$canvas xview] 0] - 50}]
  579. set rx [expr {($ox + $width) * [lindex [$canvas xview] 1] + 50}]
  580. set jinit 0
  581. if {$a(-drawvisible)} {
  582. set jinit [expr {int($lx/$dx)}]
  583. set fx [expr {$fx + $jinit * $dx}]
  584. }
  585. for {set x $fx;set j $jinit} {$x < $width} \
  586. {set x [expr {$x+$dx}];incr j} {
  587. if {$a(-drawvisible) && $x < $lx} continue
  588. if {$a(-drawvisible) && $x > $rx} break
  589. switch -- $a(-format) {
  590. time {
  591. set t [expr {$j * $dt + $ft}]
  592. if {$maxtime < 60} {
  593. set tmp [expr {int($t)}]
  594. } elseif {$maxtime < 3600} {
  595. set tmp x[clock format [expr {int($t)}] -format "%M:%S" -gmt 1]
  596. regsub x0 $tmp "" tmp
  597. regsub x $tmp "" tmp
  598. } else {
  599. set tmp [clock format [expr {int($t)}] -format "%H:%M:%S" -gmt 1]
  600. }
  601. if {$dt < 1.0} {
  602. set t $tmp[string trimleft [format "%.${ndec}f" \
  603. [expr {($t-int($t))}]] 0]
  604. } else {
  605. set t $tmp
  606. }
  607. }
  608. "PAL frames" {
  609. set t [expr {int($j * $dt * 25.0 + $ft)}]
  610. }
  611. "NTSC frames" {
  612. set t [expr {int($j * $dt * 30.0 + $ft)}]
  613. }
  614. "10ms frames" {
  615. set t [expr {int($j * $dt * 100.0 + $ft)}]
  616. }
  617. seconds {
  618. set t [expr {double($j * $dt * 1.0 + $ft)}]
  619. }
  620. }
  621. if {$a(-draw0) == 1 || $j > 0 || $a(-starttime) > 0.0} {
  622. $canvas create text [expr {$ox+$x}] [expr {$oy+$height}] \
  623. -text $t -font $a(-font) -anchor s -tags $a(-tags) \
  624. -fill $a(-fill)
  625. }
  626. $canvas create line [expr {$ox+$x}] $oy [expr {$ox+$x}] \
  627. [expr {$oy+$majTickH}] -tags $a(-tags) -fill $a(-fill)
  628. if {[string match *5 $dt] || [string match 5* $dt]} {
  629. set nt 5
  630. } else {
  631. set nt 2
  632. }
  633. for {set k 1} {$k < $nt} {incr k} {
  634. set xc [expr {$k * $dx / $nt}]
  635. $canvas create line [expr {$ox+$x+$xc}] $oy \
  636. [expr {$ox+$x+$xc}] [expr {$oy+$minTickH}]\
  637. -tags $a(-tags) -fill $a(-fill)
  638. }
  639. }
  640. }
  641. #
  642. # Snack icons
  643. #
  644. variable icon
  645. set icon(new) R0lGODlhEAAQALMAAAAAAMbGxv///////////////////////////////////////////////////////yH5BAEAAAEALAAAAAAQABAAAAQwMMhJ6wQ4YyuB+OBmeeDnAWNpZhWpmu0bxrKAUu57X7VNy7tOLxjIqYiapIjDbDYjADs=
  646. set icon(open) R0lGODlhEAAQALMAAAAAAISEAMbGxv//AP///////////////////////////////////////////////yH5BAEAAAIALAAAAAAQABAAAAQ4UMhJq6Ug3wpm7xsHZqBFCsBADGTLrbCqllIaxzSKt3wmA4GgUPhZAYfDEQuZ9ByZAVqPF6paLxEAOw==
  647. set icon(save) R0lGODlhEAAQALMAAAAAAISEAMbGxv///////////////////////////////////////////////////yH5BAEAAAIALAAAAAAQABAAAAQ3UMhJqwQ4a30DsJfwiR4oYt1oASWpVuwYm7NLt6y3YQHe/8CfrLfL+HQcGwmZSXWYKOWpmDSBIgA7
  648. set icon(print) R0lGODlhEAAQALMAAAAAAISEhMbGxv//AP///////////////////////////////////////////////yH5BAEAAAIALAAAAAAQABAAAAQ5UMhJqwU450u67wCnAURYkZ9nUuRYbhKalkJoj1pdYxar40ATrxIoxn6WgTLGC4500J6N5Vz1roIIADs=
  649. # set icon(open) R0lGODlhFAATAOMAAAAAAFeEAKj/AYQAV5o2AP8BqP9bAQBXhC8AhJmZmWZmZszMzAGo/1sB/////9zc3CH5BAEAAAsALAAAAAAUABMAQARFcMlJq13ANc03uGAoTp+kACWpAUjruum4nAqI3hdOZVtz/zoS6/WKyY7I4wlnPKIqgB7waet1VqHoiliE+riw3PSXlEUAADs=
  650. # set icon(save) R0lGODlhFAATAOMAAAAAAAAAhAAA/wCEAACZmQD/AAD//4QAAISEAJmZmWZmZszMzP8AAP//AP///9zc3CH5BAEAAAsALAAAAAAUABMAQARBcMlJq5VACGDzvkAojiGocZWHUiopflcsL2p32lqu3+lJYrCZcCh0GVeTWi+Y5LGczY0RCtxZkVUXEEvzjbbEWQQAOw==
  651. # set icon(print) R0lGODlhFAATAOMAAAAAAAAAhAAA/wCEAACZmQD/AAD//4QAAISEAJmZmWZmZszMzP8AAP//AP///9zc3CH5BAEAAAsALAAAAAAUABMAQARHcMlJq53A6b2BEIAFjGQZXlTGdZX3vTAInmiNqqtGY3Ev76bgCGQrGo8toS3DdIycNWZTupMITbPUtfQBznyz6sLl84iRlAgAOw==
  652. # set icon(cut) R0lGODlhFAATAOMAAAAAAAAAhAAA/wCEAACZmQD/AAD//4QAAISEAJmZmWZmZszMzP8AAP//AP///9zc3CH5BAEAAAsALAAAAAAUABMAQAQ3cMlJq71LAYUvANPXVVsGjpImfiW6nK87aS8nS+x9gvvt/xgYzLUaEkVAI0r1ao1WMWSn1wNeIgA7
  653. # set icon(copy) R0lGODlhFAATAOMAAAAAAAAAhAAA/wCEAACZmQD/AAD//4QAAISEAJmZmWZmZszMzP8AAP//AP///9zc3CH5BAEAAAsALAAAAAAUABMAQARFcMlJq5XAZSB0FqBwjSTmnF45ASzbbZojqrTJyqgMjDAXwzNSaAiqGY+UVsuYQRGDluap49RcpLjcNJqjaqEXbxdJLkUAADs=
  654. # set icon(paste) R0lGODlhFAATAOMAAAAAAFeEAKj/AYQAV5o2AP8BqP9bAQBXhC8AhJmZmWZmZszMzAGo/1sB/////9zc3CH5BAEAAAsALAAAAAAUABMAQARTcMlJq11A6c01uFXjAGNJNpMCrKvEroqVcSJ5NjgK7tWsUr5PryNyGB04GdHE1PGe0OjrGcR8qkPPCwsk5nLCLu1oFCUnPk2RfHSqXms2cvetJyMAOw==
  655. # set icon(undo) R0lGODlhFAATAOMAAAAAAAAAhAAA/wCEAACZmQD/AAD//4QAAISEAJmZmWZmZszMzP8AAP//AP///9zc3CH5BAEAAAsALAAAAAAUABMAQAQ7cMlJq6UKALmpvmCIaWQJZqXidWJboWr1XSgpszTu7nyv1IBYyCSBgWyWjHAUnE2cnBKyGDxNo72sKwIAOw==
  656. set icon(cut) R0lGODlhEAAQALMAAAAAAAAAhMbGxv///////////////////////////////////////////////////yH5BAEAAAIALAAAAAAQABAAAAQvUMhJqwUTW6pF314GZhjwgeXImSrXTgEQvMIc3ONtS7PV77XNL0isDGs9YZKmigAAOw==
  657. set icon(copy) R0lGODlhEAAQALMAAAAAAAAAhMbGxv///////////////////////////////////////////////////yH5BAEAAAIALAAAAAAQABAAAAQ+UMhJqwA4WwqGH9gmdV8HiKYZrCz3ecG7TikWf3EwvkOM9a0a4MbTkXCgTMeoHPJgG5+yF31SLazsTMTtViIAOw==
  658. set icon(paste) R0lGODlhEAAQALMAAAAAAAAAhISEAISEhMbGxv//AP///////////////////////////////////////yH5BAEAAAQALAAAAAAQABAAAARMkMhJqwUYWJlxKZ3GCYMAgCdQDqLKXmUrGGE2vIRK7usu94GgMNDqDQKGZDI4AiqXhkDOiMxEhQCeAPlUEqm0UDTX4XbHlaFaumlHAAA7
  659. set icon(undo) R0lGODlhEAAQALMAAAAAhMbGxv///////////////////////////////////////////////////////yH5BAEAAAEALAAAAAAQABAAAAQgMMhJq704622BB93kUSAJlhUafJj6qaLJklxc33iuXxEAOw==
  660. set icon(redo) R0lGODlhFAATAKEAAMzMzGZmZgAAAAAAACH5BAEAAAAALAAAAAAUABMAAAI4hI+py+0fhBQhPDCztCzSkzWS4nFJZCLTMqrGxgrJBistmKUHqmo3jvBMdC9Z73MBEZPMpvOpKAAAOw==
  661. set icon(gain) R0lGODlhFAATAOMAAAAAAFpaWjMzZjMAmZlmmapV/729vY+Pj5mZ/+/v78zM/wAAAAAAAAAAAAAAAAAAACH5BAEAAAUALAAAAAAUABMAAARnsMhJqwU4a32T/6AHdF8WjhUAAoa6kqwhtyW8uUlG4Tl2DqoJjzUcIAIeyZAmAiBwyhUNADQCAsHCUoVBKBTERLQ0RRiftLGoPGgDk1qpC+N2qXPM5lscL/lAAj5CIYQ5gShaN4oVEQA7
  662. set icon(zoom) R0lGODlhFAATAMIAAAAAAF9fXwAA/8zM/8zMzP///wAAAAAAACH5BAEAAAQALAAAAAAUABMAAAM/SLrc/jBKGYAFYapaes0U0I0VIIkjaUZo2q1Q68IP5r5UcFtgbL8YTOhS+mgWFcFAeCQEBMre8WlpLqrWrCYBADs=
  663. set icon(zoomIn) R0lGODlhFAATAMIAAMzMzF9fXwAAAP///wAA/8zM/wAAAAAAACH5BAEAAAAALAAAAAAUABMAAANBCLrc/jBKGYQVYao6es2U0FlDJUjimFbocF1u+5JnhKldHAUB7mKom+oTupiImo2AUAAmAQECE/SMWp6LK3arSQAAOw==
  664. set icon(zoomOut) R0lGODlhFAATAMIAAMzMzF9fXwAAAP///wAA/8zM/wAAAAAAACH5BAEAAAAALAAAAAAUABMAAANCCLrc/jBKGYQVYao6es2U0I2VIIkjaUbidQ0r1LrtGaRj/AQ3boEyTA6DCV1KH82iQigUlYAAoQlUSi3QBTbL1SQAADs=
  665. set icon(play) R0lGODlhFQAVAKEAANnZ2QAAAP///////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgAAACwAAAAAFQAVAAACJISPqcvtD10IUc1Zz7157+h5Txg2pMicmESCqLt2VEbX9o1XBQA7
  666. set icon(pause) R0lGODlhFQAVAKEAANnZ2QAAAP///////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgAAACwAAAAAFQAVAAACLISPqcvtD12Y09DKbrC3aU55HfBlY7mUqKKO6emycGjSa9LSrx1H/g8MCiMFADs=
  667. set icon(stop) R0lGODlhFQAVAKEAANnZ2QAAAP///////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgAAACwAAAAAFQAVAAACJISPqcvtD12YtM5mc8C68n4xIPWBZXdqabZarSeOW0TX9o3bBQA7
  668. set icon(record) R0lGODlhFQAVAKEAANnZ2f8AAP///////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgAAACwAAAAAFQAVAAACJoSPqcvtDyMINMhZM8zcuq41ICeOVWl6S0p95pNu4BVe9o3n+lIAADs=
  669. proc createIcons {} {
  670. variable icon
  671. image create photo snackOpen -data $icon(open)
  672. image create photo snackSave -data $icon(save)
  673. image create photo snackPrint -data $icon(print)
  674. image create photo snackCut -data $icon(cut)
  675. image create photo snackCopy -data $icon(copy)
  676. image create photo snackPaste -data $icon(paste)
  677. image create photo snackUndo -data $icon(undo)
  678. image create photo snackRedo -data $icon(redo)
  679. image create photo snackGain -data $icon(gain)
  680. image create photo snackZoom -data $icon(zoom)
  681. image create photo snackZoomIn -data $icon(zoomIn)
  682. image create photo snackZoomOut -data $icon(zoomOut)
  683. image create photo snackPlay -data $icon(play)
  684. image create photo snackPause -data $icon(pause)
  685. image create photo snackStop -data $icon(stop)
  686. image create photo snackRecord -data $icon(record)
  687. }
  688. #
  689. # Support routines for shape files
  690. #
  691. proc deleteInvalidShapeFile {fileName} {
  692. if {$fileName == ""} return
  693. if ![file exists $fileName] return
  694. set shapeName ""
  695. if [file exists [file rootname $fileName].shape] {
  696. set shapeName [file rootname $fileName].shape
  697. }
  698. if [file exists [file rootname [file tail $fileName]].shape] {
  699. set shapeName [file rootname [file tail $fileName]].shape
  700. }
  701. if {$shapeName != ""} {
  702. set fileTime [file mtime $fileName]
  703. set shapeTime [file mtime $shapeName]
  704. if {$fileTime > $shapeTime} {
  705. # Delete shape file if older than sound file
  706. file delete -force $shapeName
  707. } else {
  708. set s [snack::sound]
  709. $s config -file $fileName
  710. set soundSize [expr {200 * [$s length -unit seconds] * \
  711. [$s cget -channels]}]
  712. set shapeSize [file size $shapeName]
  713. if {[expr {$soundSize*0.95}] > $shapeSize || \
  714. [expr {$soundSize*1.05}] < $shapeSize} {
  715. # Delete shape file with incorrect size
  716. file delete -force $shapeName
  717. }
  718. $s destroy
  719. }
  720. }
  721. }
  722. proc makeShapeFileDeleteable {fileName} {
  723. if {$::tcl_platform(platform) == "unix"} {
  724. if [file exists [file rootname $fileName].shape] {
  725. set shapeName [file rootname $fileName].shape
  726. catch {file attributes $shapeName -permissions 0777}
  727. }
  728. if [file exists [file rootname [file tail $fileName]].shape] {
  729. set shapeName [file rootname [file tail $fileName]].shape
  730. catch {file attributes $shapeName -permissions 0777}
  731. }
  732. }
  733. }
  734. #
  735. # Snack default progress callback
  736. #
  737. proc progressCallback {message fraction} {
  738. set w .snackProgressDialog
  739. # if {$fraction == 0.0} return
  740. if {$fraction == 1.0} {
  741. # Task is finished close dialog
  742. destroy $w
  743. return
  744. }
  745. if {![winfo exists $w]} {
  746. # Open progress dialog if not currently shown
  747. toplevel $w
  748. pack [label $w.l]
  749. pack [canvas $w.c -width 200 -height 20 -relief sunken \
  750. -borderwidth 2]
  751. $w.c create rect 0 0 0 20 -fill black -tags bar
  752. pack [button $w.b -text Stop -command "destroy $w.b"]
  753. wm title $w "Please wait..."
  754. wm transient $w .
  755. wm withdraw $w
  756. set x [expr {[winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \
  757. - [winfo vrootx [winfo parent $w]]}]
  758. set y [expr {[winfo screenheight $w]/2 - [winfo reqheight $w]/2 \
  759. - [winfo vrooty [winfo parent $w]]}]
  760. wm geom $w +$x+$y
  761. wm deiconify $w
  762. update idletasks
  763. } elseif {![winfo exists $w.b]} {
  764. # User hit Stop button, close dialog
  765. destroy $w
  766. return -code error
  767. }
  768. switch -- $message {
  769. "Converting rate" {
  770. set message "Converting sample rate..."
  771. }
  772. "Converting encoding" {
  773. set message "Converting sample encoding format..."
  774. }
  775. "Converting channels" {
  776. set message "Converting number of channels..."
  777. }
  778. "Computing pitch" {
  779. set message "Computing pitch..."
  780. }
  781. "Reading sound" {
  782. set message "Reading sound..."
  783. }
  784. "Writing sound" {
  785. set message "Writing sound..."
  786. }
  787. "Computing waveform" {
  788. set message "Waveform is being precomputed and\
  789. stored on disk..."
  790. }
  791. "Reversing sound" {
  792. set message "Reversing sound..."
  793. }
  794. "Filtering sound" {
  795. set message "Filtering sound..."
  796. }
  797. }
  798. $w.l configure -text $message
  799. $w.c coords bar 0 0 [expr {$fraction * 200}] 20
  800. update
  801. }
  802. #
  803. # Convenience function to create dialog boxes, derived from tk_messageBox
  804. #
  805. proc makeDialogBox {toplevel args} {
  806. variable tkPriv
  807. set w tkPrivMsgBox
  808. upvar #0 $w data
  809. #
  810. # The default value of the title is space (" ") not the empty string
  811. # because for some window managers, a
  812. # wm title .foo ""
  813. # causes the window title to be "foo" instead of the empty string.
  814. #
  815. set specs {
  816. {-default "" "" ""}
  817. {-message "" "" ""}
  818. {-parent "" "" .}
  819. {-title "" "" " "}
  820. {-type "" "" "okcancel"}
  821. }
  822. tclParseConfigSpec $w $specs "" $args
  823. if {![winfo exists $data(-parent)]} {
  824. error "bad window path name \"$data(-parent)\""
  825. }
  826. switch -- $data(-type) {
  827. abortretryignore {
  828. set buttons {
  829. {abort -width 6 -text Abort -under 0}
  830. {retry -width 6 -text Retry -under 0}
  831. {ignore -width 6 -text Ignore -under 0}
  832. }
  833. }
  834. ok {
  835. set buttons {
  836. {ok -width 6 -text OK -under 0}
  837. }
  838. if {![string compare $data(-default) ""]} {
  839. set data(-default) "ok"
  840. }
  841. }
  842. okcancel {
  843. set buttons {
  844. {ok -width 6 -text OK -under 0}
  845. {cancel -width 6 -text Cancel -under 0}
  846. }
  847. }
  848. retrycancel {
  849. set buttons {
  850. {retry -width 6 -text Retry -under 0}
  851. {cancel -width 6 -text Cancel -under 0}
  852. }
  853. }
  854. yesno {
  855. set buttons {
  856. {yes -width 6 -text Yes -under 0}
  857. {no -width 6 -text No -under 0}
  858. }
  859. }
  860. yesnocancel {
  861. set buttons {
  862. {yes -width 6 -text Yes -under 0}
  863. {no -width 6 -text No -under 0}
  864. {cancel -width 6 -text Cancel -under 0}
  865. }
  866. }
  867. default {
  868. error "bad -type value \"$data(-type)\": must be abortretryignore, ok, okcancel, retrycancel, yesno, or yesnocancel"
  869. }
  870. }
  871. if {[string compare $data(-default) ""]} {
  872. set valid 0
  873. foreach btn $buttons {
  874. if {![string compare [lindex $btn 0] $data(-default)]} {
  875. set valid 1
  876. break
  877. }
  878. }
  879. if {!$valid} {
  880. error "invalid default button \"$data(-default)\""
  881. }
  882. }
  883. # 2. Set the dialog to be a child window of $parent
  884. #
  885. #
  886. if {[string compare $data(-parent) .]} {
  887. set w $data(-parent)$toplevel
  888. } else {
  889. set w $toplevel
  890. }
  891. # 3. Create the top-level window and divide it into top
  892. # and bottom parts.
  893. # catch {destroy $w}
  894. # toplevel $w -class Dialog
  895. wm title $w $data(-title)
  896. wm iconname $w Dialog
  897. wm protocol $w WM_DELETE_WINDOW { }
  898. # Message boxes should be transient with respect to their parent so that
  899. # they always stay on top of the parent window. But some window managers
  900. # will simply create the child window as withdrawn if the parent is not
  901. # viewable (because it is withdrawn or iconified). This is not good for
  902. # "grab"bed windows. So only make the message box transient if the parent
  903. # is viewable.
  904. #
  905. if { [winfo viewable [winfo toplevel $data(-parent)]] } {
  906. wm transient $w $data(-parent)
  907. }
  908. if {![string compare $::tcl_platform(platform) "macintosh"]} {
  909. unsupported1 style $w dBoxProc
  910. }
  911. frame $w.bot
  912. pack $w.bot -side bottom -fill both
  913. if {[string compare $::tcl_platform(platform) "macintosh"]} {
  914. $w.bot configure -relief raised -bd 1
  915. }
  916. # 4. Fill the top part with bitmap and message (use the option
  917. # database for -wraplength and -font so that they can be
  918. # overridden by the caller).
  919. option add *Dialog.msg.wrapLength 3i widgetDefault
  920. if {![string compare $::tcl_platform(platform) "macintosh"]} {
  921. option add *Dialog.msg.font system widgetDefault
  922. } else {
  923. option add *Dialog.msg.font {Times 18} widgetDefault
  924. }
  925. # 5. Create a row of buttons at the bottom of the dialog.
  926. set i 0
  927. foreach but $buttons {
  928. set name [lindex $but 0]
  929. set opts [lrange $but 1 end]
  930. if {![llength $opts]} {
  931. # Capitalize the first letter of $name
  932. set capName [string toupper \
  933. [string index $name 0]][string range $name 1 end]
  934. set opts [list -text $capName]
  935. }
  936. eval button [list $w.$name] $opts [list -command \
  937. [list set [namespace current]::tkPriv(button) $name]]
  938. if {![string compare $name $data(-default)]} {
  939. $w.$name configure -default active
  940. }
  941. pack $w.$name -in $w.bot -side left -expand 1 -padx 3m -pady 2m
  942. # create the binding for the key accelerator, based on the underline
  943. #
  944. set underIdx [$w.$name cget -under]
  945. if {$underIdx >= 0} {
  946. set key [string index [$w.$name cget -text] $underIdx]
  947. bind $w <Alt-[string tolower $key]> [list $w.$name invoke]
  948. bind $w <Alt-[string toupper $key]> [list $w.$name invoke]
  949. }
  950. incr i
  951. }
  952. if {[string compare {} $data(-default)]} {
  953. bind $w <FocusIn> {
  954. if {0 == [string compare Button [winfo class %W]]} {
  955. %W configure -default active
  956. }
  957. }
  958. bind $w <FocusOut> {
  959. if {0 == [string compare Button [winfo class %W]]} {
  960. %W configure -default normal
  961. }
  962. }
  963. }
  964. # 6. Create a binding for <Return> on the dialog
  965. bind $w <Return> {
  966. if {0 == [string compare Button [winfo class %W]]} {
  967. if {$::tcl_version <= 8.3} {
  968. tkButtonInvoke %W
  969. } else {
  970. tk::ButtonInvoke %W
  971. }
  972. }
  973. }
  974. # 7. Withdraw the window, then update all the geometry information
  975. # so we know how big it wants to be, then center the window in the
  976. # display and de-iconify it.
  977. wm withdraw $w
  978. update idletasks
  979. set x [expr {[winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \
  980. - [winfo vrootx [winfo parent $w]]}]
  981. set y [expr {[winfo screenheight $w]/2 - [winfo reqheight $w]/2 \
  982. - [winfo vrooty [winfo parent $w]]}]
  983. wm geom $w +$x+$y
  984. wm deiconify $w
  985. # 8. Set a grab and claim the focus too.
  986. set oldFocus [focus]
  987. set oldGrab [grab current $w]
  988. if {[string compare $oldGrab ""]} {
  989. set grabStatus [grab status $oldGrab]
  990. }
  991. grab $w
  992. if {[string compare $data(-default) ""]} {
  993. focus $w.$data(-default)
  994. } else {
  995. focus $w
  996. }
  997. # 9. Wait for the user to respond, then restore the focus and
  998. # return the index of the selected button. Restore the focus
  999. # before deleting the window, since otherwise the window manager
  1000. # may take the focus away so we can't redirect it. Finally,
  1001. # restore any grab that was in effect.
  1002. tkwait variable [namespace current]::tkPriv(button)
  1003. catch {focus $oldFocus}
  1004. destroy $w
  1005. if {[string compare $oldGrab ""]} {
  1006. if {![string compare $grabStatus "global"]} {
  1007. grab -global $oldGrab
  1008. } else {
  1009. grab $oldGrab
  1010. }
  1011. }
  1012. return $tkPriv(button)
  1013. }
  1014. #
  1015. # Snack level meter implemented as minimal mega widget
  1016. #
  1017. proc levelMeter {w args} {
  1018. array set a [list \
  1019. -oncolor red \
  1020. -offcolor grey10 \
  1021. -background black \
  1022. -width 6 \
  1023. -length 80 \
  1024. -level 0.0 \
  1025. -orient horizontal \
  1026. -type log \
  1027. ]
  1028. array set a $args
  1029. # Widget specific storage
  1030. namespace eval [namespace current]::$w {
  1031. variable levelmeter
  1032. }
  1033. upvar [namespace current]::${w}::levelmeter lm
  1034. set lm(level) 0
  1035. set lm(orient) $a(-orient)
  1036. set lm(oncolor) $a(-oncolor)
  1037. set lm(offcolor) $a(-offcolor)
  1038. set lm(bg) $a(-background)
  1039. set lm(type) $a(-type)
  1040. if {[string match horiz* $lm(orient)]} {
  1041. set lm(height) $a(-width)
  1042. set lm(width) $a(-length)
  1043. } else {
  1044. set lm(height) $a(-length)
  1045. set lm(width) $a(-width)
  1046. }
  1047. set lm(maxtime) [clock seconds]
  1048. set lm(maxlevel) 0.0
  1049. proc drawLevelMeter {w} {
  1050. upvar [namespace current]::${w}::levelmeter lm
  1051. set c ${w}_levelMeter
  1052. $c configure -width $lm(width) -height $lm(height)
  1053. $c delete all
  1054. $c create rectangle 0 0 $lm(width) $lm(height) \
  1055. -fill $lm(oncolor) -outline ""
  1056. $c create rectangle 0 0 0 0 -outline "" -fill $lm(offcolor) \
  1057. -tag mask1
  1058. $c create rectangle 0 0 0 0 -outline "" -fill $lm(offcolor) \
  1059. -tag mask2
  1060. $c create rectangle 0 0 [expr $lm(width)-1] [expr $lm(height)-1] \
  1061. -outline $lm(bg)
  1062. if {[string match horiz* $lm(orient)]} {
  1063. $c coords mask1 [expr {$lm(level)*$lm(width)}] 0 \
  1064. $lm(width) $lm(height)
  1065. $c coords mask2 [expr {$lm(level)*$lm(width)}] 0 \
  1066. $lm(width) $lm(height)
  1067. for {set x 5} {$x < $lm(width)} {incr x 5} {
  1068. $c create line $x 0 $x [expr $lm(width)-1] -fill black \
  1069. -width 2
  1070. }
  1071. } else {
  1072. $c coords mask1 0 0 $lm(width) \
  1073. [expr {$lm(height)-$lm(level)*$lm(height)}]
  1074. $c coords mask2 0 0 $lm(width) \
  1075. [expr {$lm(height)-$lm(level)*$lm(height)}]
  1076. for {set y 5} {$y < $lm(height)} {incr y 5} {
  1077. $c create line 0 [expr $lm(height)-$y] \
  1078. [expr $lm(width)-1] [expr $lm(height)-$y] \
  1079. -fill black -width 2
  1080. }
  1081. }
  1082. }
  1083. proc levelMeterHandler {w cmd args} {
  1084. upvar [namespace current]::${w}::levelmeter lm
  1085. if {[string match conf* $cmd]} {
  1086. switch -- [lindex $args 0] {
  1087. -level {
  1088. set arg [lindex $args 1]
  1089. if {$arg < 1} { set arg 1 }
  1090. if {$lm(type)=="linear"} {
  1091. set lm(level) [expr {$arg/32760.0}]
  1092. } else {
  1093. set lm(level) [expr {log($arg)/10.3972}]
  1094. }
  1095. if {[clock seconds] - $lm(maxtime) > 2} {
  1096. set lm(maxtime) [clock seconds]
  1097. set lm(maxlevel) 0.0
  1098. }
  1099. if {$lm(level) > $lm(maxlevel)} {
  1100. set lm(maxlevel) $lm(level)
  1101. }
  1102. if {[string match horiz* $lm(orient)]} {
  1103. set l1 [expr {5*int($lm(level)*$lm(width)/5)}]
  1104. set l2 [expr {5*int($lm(maxlevel)*$lm(width)/5)}]
  1105. ${w}_levelMeter coords mask1 $l2 0 \
  1106. $lm(width) $lm(height)
  1107. ${w}_levelMeter coords mask2 [expr {$l2-5}] 0 \
  1108. $l1 $lm(height)
  1109. } else {
  1110. set l1 [expr {5*int($lm(level)*$lm(height)/5)}]
  1111. set l2 [expr {5*int($lm(maxlevel)*$lm(height)/5)}]
  1112. ${w}_levelMeter coords mask1 0 0 $lm(width) \
  1113. [expr {$lm(height)-$l2}]
  1114. ${w}_levelMeter coords mask2 0 [expr {$lm(height)-$l2+5}] \
  1115. $lm(width) [expr {$lm(height)-$l1}]
  1116. }
  1117. }
  1118. -length {
  1119. if {[string match horiz* $lm(orient)]} {
  1120. set lm(width) [lindex $args 1]
  1121. } else {
  1122. set lm(height) [lindex $args 1]
  1123. }
  1124. drawLevelMeter $w
  1125. }
  1126. -width {
  1127. if {[string match horiz* $lm(orient)]} {
  1128. set lm(height) [lindex $args 1]
  1129. } else {
  1130. set lm(width) [lindex $args 1]
  1131. }
  1132. drawLevelMeter $w
  1133. }
  1134. default {
  1135. error "unknown option \"[lindex $args 0]\""
  1136. }
  1137. }
  1138. } else {
  1139. error "bad option \"$cmd\": must be configure"
  1140. }
  1141. }
  1142. # Create a canvas where the widget is to be rendered
  1143. canvas $w -highlightthickness 0
  1144. # Replave the canvas widget command
  1145. rename $w ${w}_levelMeter
  1146. # Draw level meter
  1147. drawLevelMeter $w
  1148. # Create level meter widget command
  1149. proc ::$w {cmd args} \
  1150. "return \[eval snack::levelMeterHandler $w \$cmd \$args\]"
  1151. return $w
  1152. }
  1153. }