1#!/usr/bin/wish -f
2#
3# TiMidity++ -- MIDI to WAVE converter and player
4# Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
5# Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20#
21# TkMidity -- Tcl/Tk Interface for TiMidity
22#	written by Takashi IWAI
23#
24# Tk control panel routine
25#
26
27#----------------
28# initialize global variables
29#
30proc InitGlobal {} {
31
32    global Stat tk_patchLevel
33
34    if [catch {expr $Stat(new_tcltk) == 0 || $Stat(new_tcltk) == 1}] {
35	set Stat(new_tcltk) 0
36	if [regexp "(\[0-9\]+\.\[0-9\]+)" $tk_patchLevel cur] {
37	    if {$cur >= 4.0} {
38		set Stat(new_tcltk) 1
39	    }
40	}
41    }
42
43    # time table and volume
44    set Stat(MaxSecs) 0
45    set Stat(LastSec) 0
46    set Stat(TotalTimeStr) "/ 0:00"
47
48    # message lines
49    set Stat(CurMsgs) 0
50    set Stat(MaxMsgs) 500
51
52    # current status
53    set Stat(Playing) 0
54    set Stat(Paused) 0
55    set Stat(Blinking) 0
56
57    # MIDI file list
58    set Stat(CurIdx) -1
59    set Stat(MaxFiles) 0
60    set Stat(FileList) {}
61    set Stat(ShuffleList) {}
62    set Stat(CurFile) "--------"
63    set Stat(NextFile) "--------"
64
65    global Config
66    # playing mode
67    set Config(Tracing) 0
68    set Config(RepeatPlay) 0
69    set Config(ShufflePlay) 0
70    set Config(AutoStart) 0
71    set Config(AutoExit) 0
72    set Config(ConfirmExit) 1
73
74    # display configuration
75    set Config(Disp:file) 1
76    set Config(Disp:time) 1
77    set Config(Disp:text) 1
78    set Config(Disp:volume) 1
79    set Config(Disp:button) 1
80    set Config(Disp:trace) 1
81
82    set Config(CurFileMode) 0
83
84    # current volume
85    set Config(CurVol) 75
86
87    wm title . "TkMidity"
88    wm iconname . "TkMidity"
89    global bitmap_path
90    wm iconbitmap . "timidity"
91}
92
93
94#----------------
95# read a message from stdin
96#
97proc HandleInput {} {
98    global Stat Config
99
100    set mlist [gets stdin]
101    set msg [lindex $mlist 0]
102
103    if {$msg == "RSET"} {
104	if {$Config(Tracing)} {
105	    TraceReset
106	}
107    } elseif {$msg == "TIME"} {
108	# set total time
109	set csecs [expr [lindex $mlist 1] / 100]
110	set mins [expr $csecs / 60]
111	set secs [expr $csecs % 60]
112	set Stat(TotalTimeStr) [format "/ %d:%02d" $mins $secs]
113	set Stat(MaxSecs) $csecs
114	set tics [expr $csecs / 8]
115	set tics [expr (($tics + 4) / 5) * 5]
116	.body.time.scale configure -tickinterval $tics -to $csecs
117	SetTime 0
118
119    } elseif {$msg == "MVOL"} {
120	# set master volume
121	SetVolume [lindex $mlist 1]
122
123    } elseif {$msg == "FILE"} {
124	# set playing file
125	set Stat(CurFile) [retrieve-filename [lindex $mlist 1]]
126	wm title . "TkMidity: $Stat(CurFile)"
127	wm iconname . "TkMidity: $Stat(CurFile)"
128	if {$Config(CurFileMode) == 0} {
129	    .body.curfile configure -text "$Stat(CurFile) / 00:00"
130	} else {
131	    .body.curfile configure -text "$Stat(CurFile) / -$Stat(TotalTimeStr)"
132	}
133	AppendMsg "------"
134
135    } elseif {$msg == "LIST"} {
136	# set file list
137	.body.file.list delete 0 end
138	set Stat(MaxFiles) [lindex $mlist 1]
139	set Stat(FileList) {}
140	for {set i 0} {$i < $Stat(MaxFiles)} {incr i} {
141	    set file [gets stdin]
142	    .body.file.list insert end $file
143	    lappend Stat(FileList) $file
144	}
145
146	set Stat(CurIdx) -1
147	SelectNumber
148
149    } elseif {$msg == "ALST"} {
150	# append file list
151	set i $Stat(MaxFiles)
152	set Stat(MaxFiles) [expr $i + [lindex $mlist 1]]
153	for {} {$i < $Stat(MaxFiles)} {incr i} {
154	    set file [gets stdin]
155	    .body.file.list insert end $file
156	    if {$Config(ShufflePlay)} {
157		lappend Stat(ShuffleList) [llength $Stat(FileList)]
158	    }
159	    lappend Stat(FileList) $file
160	}
161	.body.file.list select set $Stat(CurIdx)
162
163    } elseif {$msg == "ERRR"} {
164	# delete file list
165	.body.file.list delete $Stat(CurIdx)
166	set Stat(MaxFiles) [expr $Stat(MaxFiles) - 1]
167	set tmplist {}
168	for {set i 0} {$i < $Stat(CurIdx)} {incr i} {
169	    lappend tmplist [lindex $Stat(FileList) $i]
170	}
171	for {set i $Stat(CurIdx)} {$i < $Stat(MaxFiles)} {incr i} {
172	    lappend tmplist [lindex $Stat(FileList) [expr $i + 1]]
173	}
174	set Stat(FileList) {}
175	for {set i 0} {$i < $Stat(MaxFiles)} {incr i} {
176	    lappend Stat(FileList) [lindex $tmplist $i]
177	}
178	if {$Stat(CurIdx) >= $Stat(MaxFiles)} {
179	    if {$Config(RepeatPlay)} {
180		set Stat(CurIdx) 0
181	    } elseif {$Config(AutoExit)} {
182		QuitCmd
183	    } else {
184		StopCmd
185	    }
186	}
187	SelectNumber
188
189    } elseif {$msg == "PREV"} {
190	# previous file
191	set Stat(CurIdx) [expr $Stat(CurIdx) - 1]
192	if {$Stat(CurIdx) < 0} {set Stat(CurIdx) 0}
193	SelectNumber
194
195    } elseif {$msg == "NEXT"||$msg == "TEND"} {
196	# next file
197	incr Stat(CurIdx)
198	if {$Stat(CurIdx) >= $Stat(MaxFiles)} {
199	    if {$Config(RepeatPlay)} {
200		set Stat(CurIdx) 0
201	    } elseif {$Config(AutoExit)} {
202		QuitCmd
203	    } else {
204		StopCmd
205	    }
206	}
207	SelectNumber
208
209    } elseif {$msg == "CMSG"} {
210	# put message
211	set type [lindex $mlist 1]
212	set str [gets stdin]
213	AppendMsg $str
214
215    } elseif {$msg == "LYRC"} {
216	# put message
217	set type [lindex $mlist 1]
218	set str [gets stdin]
219	.body.text.buf insert end $str
220
221    } elseif {$msg == "CERR"} {
222	error [format "%s: %s" $Stat(NextFile) [gets stdin]]
223	WriteMsg "NEXT"
224
225    } elseif {$msg == "QUIT"} {
226	# quit
227	exit
228    } elseif {$msg == "RSTA"} {
229	# restart file
230	SelectNumber
231    }
232}
233
234
235#----------------
236# make shuffled list
237#
238proc MakeShuffleList {} {
239    global Stat
240    set Stat(ShuffleList) {}
241    for {set i 0} {$i < $Stat(MaxFiles)} {incr i} {
242	lappend Stat(ShuffleList) $i
243    }
244    set len [expr $Stat(MaxFiles) - 1]
245    while {$len > 0} {
246	set pos [my-random [expr $len + 1]]
247	set tmp [lindex $Stat(ShuffleList) $pos]
248	set Stat(ShuffleList) [lreplace $Stat(ShuffleList) $pos $pos \
249					[lindex $Stat(ShuffleList) $len]]
250	set Stat(ShuffleList) [lreplace $Stat(ShuffleList) $len $len $tmp]
251	set len [expr $len - 1]
252    }
253}
254
255#
256# append a string to message buffer
257#
258proc AppendMsg {str} {
259    global Stat
260
261    incr Stat(CurMsgs)
262    if {$Stat(CurMsgs) >= $Stat(MaxMsgs)} { ClearMsg }
263    .body.text.buf insert end $str\n
264    .body.text.buf yview -pickplace end
265}
266
267#
268# clear message buffer
269#
270proc ClearMsg {} {
271    global Stat
272    .body.text.buf delete 0.0 end
273    .body.text.buf yview 0
274    set Stat(CurMsgs) 0
275}
276
277
278#----------------
279# select the file in listbox and load it
280#
281proc SelectNumber {} {
282    global Stat Config
283    if {$Stat(new_tcltk)} {
284	.body.file.list select clear 0 end
285    } else {
286	.body.file.list select clear
287    }
288    set idx -1
289    if {$Stat(CurIdx) >= 0 && $Stat(CurIdx) < [llength $Stat(FileList)]} {
290	if {$Config(ShufflePlay)} {
291	    if {$Stat(ShuffleList) == {}} {
292		MakeShuffleList
293	    }
294	    set idx [lindex $Stat(ShuffleList) $Stat(CurIdx)]
295	} else {
296	    set idx $Stat(CurIdx)
297	}
298	set thefile [lindex $Stat(FileList) $idx]
299	set Stat(NextFile) $thefile
300    }
301
302    if {$idx >= 0} {
303	if {$Stat(new_tcltk)} {
304#	    .body.file.list select set $idx
305	} else {
306#	    .body.file.list select from $idx
307#	    .body.file.list select to $idx
308	}
309	.body.curfile configure -text\
310		"Playing: [lindex $Stat(FileList) $idx]"
311	LoadCmd $idx
312	set Stat(Playing) 1
313    } else {
314	SetTime 0
315	.body.curfile configure -text "-------- / 00:00"
316	set Stat(Playing) 0
317	set Stat(Paused) 0
318    }
319    DispButtonPlay
320}
321
322
323#
324# update current time
325#
326proc SetTime {val} {
327    global Stat Config
328    if {$Stat(CurIdx) == -1} {
329	return
330    }
331    set Stat(LastSec) $val
332    if {$Config(CurFileMode) == 0} {
333	set curt [sec2time $val]
334	.body.curfile configure\
335		-text "$Stat(CurFile) / $curt"
336    } else {
337	set curt [sec2time [expr $val - $Stat(MaxSecs)]]
338	.body.curfile configure\
339		-text "$Stat(CurFile) / $curt"
340    }
341    set curt [sec2time $val]
342    .body.time.label configure\
343	    -text "$curt / $Stat(TotalTimeStr)"
344    .body.time.scale set $val
345    DispButtonPlay
346}
347
348
349#
350# colorize buttons with each state
351#
352proc DispButtonPlay {} {
353    global Stat
354    if {$Stat(Playing)} {
355	if {$Stat(Blinking)} {
356	    set color green
357	    set Stat(Blinking) 0
358	} else {
359	    set color red
360	    set Stat(Blinking) 1
361	}
362	.body.button.play configure -fg $color -activeforeground $color
363    } else {
364	.body.button.play configure -fg black -activeforeground black
365    }
366
367    if {$Stat(Playing) && $Stat(Paused)} {
368	.body.button.pause configure -fg red -activeforeground red
369    } else {
370	.body.button.pause configure -fg black -activeforeground black
371    }
372}
373
374
375#
376# update current volume
377#
378proc SetVolume {val} {
379    global Config
380    set Config(CurVol) $val
381    .body.volume.scale set $val
382}
383
384
385#----------------
386# write message
387# messages are: PREV, NEXT, QUIT, FWRD, BACK, RSET, STOP
388#	LOAD\n<filename>, JUMP <time>, VOLM <volume>
389#
390
391proc WriteMsg {str} {
392    puts stdout $str
393    flush stdout
394}
395
396
397#----------------
398# callback commands
399#
400
401#
402# jump to specified time
403#
404proc JumpCmd {val} {
405    global Stat
406    if {$val != $Stat(LastSec)} {
407	WriteMsg [format "JUMP %d" [expr $val * 100]]
408    }
409}
410
411#
412# change volume amplitude
413#
414proc VolumeCmd {val {force 0}} {
415    global Config
416    if {$val < 0} {set val 0}
417    if {$val > 200} {set val 200}
418    if {$force != 0 || $val != $Config(CurVol)} {
419	WriteMsg [format "VOLM %d" $val]
420    }
421}
422
423#
424# load the specified file
425#
426proc LoadCmd {idx} {
427    global Stat Config
428    WriteMsg "LOAD"
429    WriteMsg [lindex $Stat(FileList) $idx]
430    AppendMsg ""
431}
432
433#
434# play the first file
435#
436proc PlayCmd {} {
437    global Stat
438    if {$Stat(Playing) == 0} {
439	WriteMsg "NEXT"
440    }
441}
442
443proc PauseCheck {} {
444    global Stat
445    if {$Stat(Paused)} {
446	set Stat(Paused) 0
447    }
448    DispButtonPlay
449}
450
451#
452# pause music
453#
454proc PauseCmd {} {
455    global Stat
456    if {$Stat(Playing)} {
457	if {$Stat(Paused)} {
458	    set Stat(Paused) 0
459	} else {
460	    set Stat(Paused) 1
461	}
462	DispButtonPlay
463	WriteMsg "STOP"
464    }
465}
466
467#
468# stop playing
469#
470proc StopCmd {} {
471    global Stat Config
472    if {$Stat(Playing)} {
473	WriteMsg "QUIT"
474	WriteMsg "XTND"
475	set Stat(CurIdx) -1
476	SelectNumber
477	if {$Config(Tracing)} {
478	    TraceReset
479	}
480	wm title . "TkMidity"
481	wm iconname . "TkMidity"
482    }
483}
484
485#
486# quit TkMidity
487#
488proc QuitCmd {} {
489    global Config Stat
490    if {$Config(AutoExit) || !$Config(ConfirmExit)} {
491	WriteMsg "QUIT"
492	WriteMsg "ZAPP"
493	return
494    }
495    set oldpause $Stat(Paused)
496    if {!$oldpause} {PauseCmd}
497    if {[question "Really Quit TkMidity?" 0]} {
498	WriteMsg "QUIT"
499	WriteMsg "ZAPP"
500	return
501    }
502    if {!$oldpause} {PauseCmd}
503}
504
505#
506# play previous file
507#
508proc PrevCmd {} {
509    global Stat Config
510    if {$Stat(Playing)} {
511	WriteMsg "PREV"
512	PauseCheck
513    }
514    VolumeCmd $Config(CurVol) 1
515}
516
517#
518# play next file
519#
520proc NextCmd {} {
521    global Stat Config
522    if {$Stat(Playing)} {
523	WriteMsg "NEXT"
524	PauseCheck
525    }
526    VolumeCmd $Config(CurVol) 1
527}
528
529#
530# forward/backward 2 secs
531#
532proc ForwardCmd {} {
533    global Stat
534    if {$Stat(Playing)} {
535	WriteMsg "FWRD"
536	PauseCheck
537    }
538}
539
540proc BackwardCmd {} {
541    global Stat
542    if {$Stat(Playing)} {
543	WriteMsg "BACK"
544	PauseCheck
545    }
546}
547
548
549#
550# volume up/down
551#
552proc VolUpCmd {} {
553    global Stat Config
554    if {$Stat(Playing)} {
555	VolumeCmd [expr $Config(CurVol) + 5]
556    }
557}
558
559proc VolDownCmd {} {
560    global Stat Config
561    if {$Stat(Playing)} {
562	VolumeCmd [expr $Config(CurVol) - 5]
563    }
564}
565
566#----------------
567# display configured tables
568#
569proc DispTables {} {
570    global Config
571    set allitems {file time text volume button trace}
572
573    foreach i $allitems {
574	pack forget .body.$i
575	if {$Config(Disp:$i)} {
576	    pack .body.$i -side top -fill x
577	}
578    }
579}
580
581#
582# save configuration and playing mode
583#
584proc SaveConfig {} {
585    global Config ConfigFile
586    set fd [open $ConfigFile w]
587    if {$fd != ""} {
588	puts $fd "global Config"
589	foreach i [array names Config] {
590	    puts $fd "set Config($i) $Config($i)"
591	}
592	close $fd
593    }
594}
595
596#
597# load configuration file
598#
599proc LoadConfig {} {
600    global ConfigFile Stat
601    catch {source $ConfigFile}
602}
603
604#
605# from command line
606#
607proc InitCmdLine {argc argv} {
608    global Config
609    set Config(Disp:trace) 0
610    for {set i 0} {$i < $argc} {incr i} {
611	if {[lindex $argv $i] == "-mode"} {
612	    incr i
613	    set mode [lindex $argv $i]
614	    if {$mode == "trace"} {
615		set Config(Tracing) 1
616		set Config(Disp:trace) 1
617	    } elseif {$mode == "shuffle"} {
618		set Config(ShufflePlay) 1
619	    } elseif {$mode == "normal"} {
620		set Config(ShufflePlay) 0
621	    } elseif {$mode == "autostart"} {
622		set Config(AutoStart) 1
623	    } elseif {$mode == "autoexit"} {
624		set Config(AutoExit) 1
625	    } elseif {$mode == "repeat"} {
626		set Config(RepeatPlay) 1
627	    }
628	}
629    }
630}
631
632
633#
634# selection callback of the playing file from listbox
635#
636proc SelectList {lw pos} {
637    global Config Stat
638    set idx [$lw nearest $pos]
639    if {$idx >= 0 && $idx < $Stat(MaxFiles)} {
640	if {$Config(ShufflePlay)} {
641	    set found [lsearch -exact $Stat(ShuffleList) $idx]
642	    if {$found != -1} {
643		set Stat(CurIdx) $found
644	    }
645	} else {
646	    set Stat(CurIdx) $idx
647	}
648	set Stat(Playing) 1
649	PauseCheck
650	SelectNumber
651    }
652}
653
654
655#
656#
657#
658proc OpenFiles {} {
659    global Stat
660    set files [filebrowser .browser "" "*.{mid*,kar,lzh,zip}"]
661    if {$files != ""} {
662	WriteMsg "XPND"
663	WriteMsg $files
664    }
665}
666
667#
668#
669#
670proc CloseFiles {} {
671    global Stat
672    if {[question "Really Clear List?" 0]} {
673	StopCmd
674	.body.file.list delete 0 end
675	set Stat(MaxFiles) 0
676	set Stat(FileList) {}
677	set Stat(ShuffleList) {}
678    }
679}
680
681proc ToggleCurFileMode {} {
682    global Config Stat
683    if {$Config(CurFileMode) == 0} {
684	set Config(CurFileMode) 1
685    } else {
686	set Config(CurFileMode) 0
687    }
688    SetTime $Stat(LastSec)
689}
690
691#----------------
692# create main window
693#
694
695proc CreateWindow {} {
696    global Config Stat
697
698    # menu bar
699    frame .menu -relief raised -bd 1
700    pack .menu -side top -expand 1 -fill x
701
702    # File menu
703    menubutton .menu.file -text "File" -menu .menu.file.m\
704	    -underline 0
705    menu .menu.file.m
706    .menu.file.m add command -label "Open Files" -underline 0\
707	    -command OpenFiles
708    .menu.file.m add command -label "Clear List" -underline 0\
709	    -command CloseFiles
710    .menu.file.m add command -label "Save Config" -underline 0\
711	    -command SaveConfig
712    .menu.file.m add command -label "About" -underline 0\
713	    -command {
714	information "TkMidity -- TiMidty Tcl/Tk Version\n  written by T.IWAI"
715    }
716    .menu.file.m add command -label "Quit" -underline 0\
717	    -command QuitCmd
718
719    # Mode menu
720    menubutton .menu.mode -text "Mode" -menu .menu.mode.m\
721	    -underline 0
722    menu .menu.mode.m
723    .menu.mode.m add check -label "Repeat" -underline 0\
724	    -variable Config(RepeatPlay)
725    .menu.mode.m add check -label "Shuffle" -underline 0\
726	    -variable Config(ShufflePlay) -command {
727	if {$Config(ShufflePlay)} {
728	    MakeShuffleList
729	} else {
730	    set Stat(ShuffleList) {}
731	}
732    }
733    .menu.mode.m add check -label "Auto Start" -underline 5\
734	    -variable Config(AutoStart)
735    .menu.mode.m add check -label "Auto Exit" -underline 5\
736	    -variable Config(AutoExit)
737    .menu.mode.m add check -label "Confirm Quit" -underline 0\
738	    -variable Config(ConfirmExit)
739
740    # Display menu
741    menubutton .menu.disp -text "Display" -menu .menu.disp.m\
742	    -underline 0
743    menu .menu.disp.m
744    .menu.disp.m add check -label "File List" -underline 0\
745	    -variable Config(Disp:file) -command "DispTables"
746    .menu.disp.m add check -label "Time" -underline 0\
747	    -variable Config(Disp:time) -command "DispTables"
748    .menu.disp.m add check -label "Messages" -underline 0\
749	    -variable Config(Disp:text) -command "DispTables"
750    .menu.disp.m add check -label "Volume" -underline 0\
751	    -variable Config(Disp:volume) -command "DispTables"
752    .menu.disp.m add check -label "Buttons" -underline 0\
753	    -variable Config(Disp:button) -command "DispTables"
754    if {$Config(Tracing)} {
755	.menu.disp.m add check -label "Trace" -underline 1\
756		-variable Config(Disp:trace) -command "DispTables"
757    }
758
759    pack .menu.file .menu.mode .menu.disp -side left
760
761    # display body
762    if {$Stat(new_tcltk)} {
763	frame .body -relief flat
764    } else {
765	frame .body -relief raised -bd 1
766    }
767    pack .body -side top -expand 1 -fill both
768
769    # current playing file
770    button .body.curfile -text "-------- / 00:00" -relief ridge\
771	    -command "ToggleCurFileMode"
772    pack .body.curfile -side top -expand 1 -fill x
773
774    # playing files list
775    frame .body.file -relief raised -bd 1
776    scrollbar .body.file.bar -relief sunken\
777	    -command ".body.file.list yview"
778    pack .body.file.bar -side right -fill y
779    if {$Stat(new_tcltk)} {
780	listbox .body.file.list -width 36 -height 10 -relief sunken -bd 2\
781		-yscroll ".body.file.bar set"
782    } else {
783	listbox .body.file.list -geometry 36x10 -relief sunken -bd 2\
784		-yscroll ".body.file.bar set"
785    }
786    bind .body.file.list <Button-1> {SelectList %W %y}
787
788    pack .body.file.list -side top -expand 1 -fill both
789
790    # time label and scale
791    frame .body.time -relief raised -bd 1
792    label .body.time.label -text "0:00 / 0:00"
793    pack .body.time.label -side top
794    scale .body.time.scale -orient horizontal -length 280\
795	    -from 0 -to 100 -tickinterval 10
796    bind .body.time.scale <ButtonRelease-1> {JumpCmd [%W get]}
797    pack .body.time.scale -side bottom -expand 1 -fill x
798
799    # text browser
800    frame .body.text -relief raised -bd 1
801    scrollbar .body.text.bar -relief sunken\
802	    -command ".body.text.buf yview"
803    pack .body.text.bar -side right -fill y
804    text .body.text.buf -width 36 -height 12 -relief sunken -bd 2\
805	    -wrap word -yscroll ".body.text.bar set"
806    bind .body.text.buf <Button-1> { }
807    bind .body.text.buf <Any-Key> { }
808    pack .body.text.buf -side top -expand 1 -fill both
809    button .body.text.clear -text "Clear"\
810	    -command ClearMsg
811    pack .body.text.clear -side bottom
812
813    # volume label and scale
814    frame .body.volume -relief raised -bd 1
815    label .body.volume.label -text "Volume:"
816    pack .body.volume.label -side top
817    scale .body.volume.scale -orient horizontal -length 280\
818	    -from 0 -to 200 -tickinterval 25\
819	    -showvalue true -label "Volume"\
820	    -command VolumeCmd
821    pack .body.volume.scale -side bottom -expand 1 -fill x
822    .body.volume.scale set $Config(CurVol)
823
824    # buttons
825    global bitmap_path
826    frame .body.button -relief raised -bd 1
827    pack .body.button -side top -expand 1 -fill x
828    button .body.button.play -bitmap "play" -command "PlayCmd"
829    button .body.button.stop -bitmap "stop" -command "StopCmd"
830    button .body.button.prev -bitmap "prev" -command "PrevCmd"
831    button .body.button.back -bitmap "back" -command "BackwardCmd"
832    button .body.button.fwrd -bitmap "fwrd" -command "ForwardCmd"
833    button .body.button.next -bitmap "next" -command "NextCmd"
834    button .body.button.pause -bitmap "pause" -command "PauseCmd"
835    button .body.button.quit -bitmap "quit" -command "QuitCmd"
836    pack .body.button.play .body.button.pause\
837	    .body.button.prev .body.button.back\
838	    .body.button.stop\
839	    .body.button.fwrd .body.button.next\
840	    .body.button.quit\
841	    -side left -ipadx 4 -pady 2 -fill x
842
843    if {$Config(Tracing)} {
844	# trace display
845	TraceCreate
846    }
847    TraceUpdate
848
849    # pack all items
850    DispTables
851
852    focus .
853    tk_menuBar .menu .menu.file .menu.mode .menu.disp
854    bind . <Key-Right> "ForwardCmd"
855    bind . <Key-n> "NextCmd"
856    bind . <Key-Left> "BackwardCmd"
857    bind . <Key-p> "PrevCmd"
858    bind . <Key-s> "PauseCmd"
859    bind . <Key-Down> "VolDownCmd"
860    bind . <Key-v> "VolDownCmd"
861    bind . <Key-Up> "VolUpCmd"
862    bind . <Key-V> "VolUpCmd"
863    bind . <Key-space> "PauseCmd"
864    bind . <Return> "PlayCmd"
865    bind . <Key-c> "StopCmd"
866    bind . <Key-q> "QuitCmd"
867
868    VolumeCmd $Config(CurVol) 1
869}
870