#!/usr/bin/env wish

# WISH CD-Writer 2009
# A simple CD-R and CD-RW writer written in Tcl/Tk
# by David McClamrock <mcclamrock@locl.net>
# with help from *Practical Programming in Tcl and Tk*
# by Brent Welch, Ken Jones, and Jeffrey Hobbs

# Copyright  2002-2008 David H. McClamrock
# Freely available under Maximum Use License for Everyone
# You should have received a copy of this license with this program.
# If you didn't, e-mail the author to get one.

##################################


### INITIALIZATION ###

# WISH applications require at least Tcl and Tk 8.5:

set tclo [package vcompare [package require Tcl] 8.5]
set tko [package vcompare [package require Tk] 8.5]
if {$tclo < 0 || $tko < 0} {
	tk_messageBox -message "This program requires Tcl and Tk 8.5 or greater" -type ok
	exit
}
package require Ttk

# Default settings:

set topdir /usr/local
set docdir [file join $topdir doc wishes]
set libdir [file join $topdir lib wishes]
set helpfile [file join $docdir cdhelp_link.txt] ; # User Help Guide
set licfile [file join $docdir mule_license.txt] ; # License
set version "2009"

# WISH CD-Writer requires the following additional programs:
# "mkisofs" to create CD images
# "cdrecord" to write data CDs or blank CD-RWs
# "cdrdao" to write audio CDs
# "timidity" (see Audio CD section below), if
# WAV files are to be produced from MIDI files

# Procedure to find out whether executable program is in system's PATH or not:

proc inpath {prog} {
	global env
	set exok 0
	set envlist [split $env(PATH) :]
	foreach direc $envlist {
		if {[file executable [file join $direc $prog]]} {
			set exok 1
			break
		}
	}
	return $exok
}

set needlist [list mkisofs cdrecord cdrdao]
set lacklist [list]
foreach prog $needlist {
	if {[inpath $prog] == 0} {
		lappend lacklist $prog
	}
	if {[llength $lacklist] > 0} {
		set lackmess "The following programs do not appear to be in\
			your system's PATH:\n$lacklist\nPlease make sure all\
			programs listed here are in your system's PATH before\
			proceeding; otherwise, WISH CD-Writer will not work!"
		tk_messageBox -message $lackmess -type ok
	}
}

# Where program listings and configuration files go
# (Replace old "~/wishes," if any, with new "~/.wishes")

set wishdir [file join $env(HOME) .wishes]
set oldwishdir [file join $env(HOME) wishes]
if {[file exists $wishdir] == 0} {
	if {[file exists $oldwishdir] && [file type $oldwishdir] eq "directory"} {
		file rename $oldwishdir $wishdir
		file link $oldwishdir $wishdir
	} else {
		file mkdir $wishdir
	}
}

# Directory for color schemes from WISH Color Picker Plus:
set colordir [file join $wishdir colorschemes]

# Subdirectory for CD content lists:
set contidir [file join $wishdir cdcontent]
if {[file isdirectory $contidir] == 0} {
	file mkdir $contidir
}

# Subdirectory for CD images:
set imagedir [file join $wishdir cdimage]
if {[file isdirectory $imagedir] == 0} {
	file mkdir $imagedir
}

# One or more features may work only on unix platforms (including Linux), 
# so identify the platform:
set platforms [split [array get tcl_platform]]
if {"unix" in $platforms} {
	set platform unix
}

# WISH CD-Writer will be most seriously displeased
# if it doesn't get to use Unix-type file names:
set platform [split [array get tcl_platform]]
if {[lsearch $platform unix] == -1} {
	tk_messageBox -message "Sorry, the present version of WISH CD-Writer\
		will work only on Unix-type operating systems" -type ok
	exit
}

# Default settings that may be changed by previously saved ones
# in "$specfile" (see below):
set nowdir [pwd] ; # Starting directory for CD content list
set speed 4
set typewrite newdat ; # New data, copy data, re-use data, audio, or blank
set contents [list]; # CD content lists
set imago [file join $imagedir IMAGE01] ; # CD image file
set calcsize 1 ; # Calculate image size first
set symlinks 1 ; # Follow symbolic links
set rock "\-R" ; # Rock Ridge (Unix) extensions
set joliet "\-J" ; # Joliet (Windows) extensions
set burnfree "driveropts=burnfree" ; # Buffer underrun protection
set dumbo "" ; # Don't do dummy write
set dumpimage 0 ; # Don't automatically delete CD image after writing
set booty 0 ; # Presume copied CD is to be non-bootable
set reader_mnt "/mnt/cdrom" ; # Directory filename of CD reader
set reader_dev "" ; # Device name of CD reader
set writer_dev "" ; # Device name of CD writer (on Linux 2.6)
set driver "generic-mmc" ; # Generic driver for CD-RW drive
set subdirs 1 ; # Include subdirectories
set showhid 0 ; # Don't show hidden files
set whatgo init ; # Nothing is going on yet
set writdat 0 ; # Don't write data yet
set coloron 0 ; # WISH Color Picker Plus not yet loaded
set helpon 0 ; # WISH User Help not yet loaded either

# Substitute saved settings (if any) for defaults:
set specfile [file join $wishdir cdspecs.tcl]
if {[file exists $specfile]} {
	source $specfile
}

# Procedure to save configuration and variable settings:

proc savefig {} {
	global speed contents imago calcsize symlinks \
		rock joliet burnfree dumbo dumpimage booty reader_mnt \
		reader_dev writer_dev driver subdirs specfile showhid \
		helpfile typewrite current_scheme
	set varbs "set speed \"$speed\"\
		\nset typewrite \"$typewrite\"\
		\nset contents \"$contents\"\
		\nset imago \"$imago\"\
		\nset calcsize \"$calcsize\"\
		\nset symlinks \"$symlinks\"\
		\nset rock \"$rock\"\
		\nset joliet \"$joliet\"\
		\nset burnfree \"$burnfree\"\
		\nset dumbo \"$dumbo\"\
		\nset dumpimage \"$dumpimage\"\
		\nset booty \"$booty\"\
		\nset reader_mnt \"$reader_mnt\"\
		\nset reader_dev \"$reader_dev\"\
		\nset writer_dev \"$writer_dev\"\
		\nset driver \"$driver\"\
		\nset subdirs \"$subdirs\"\
		\nset showhid \"$showhid\"\
		\nset helpfile \"$helpfile\"\
		\nset current_scheme \"$current_scheme\""
	set fileid [open $specfile "w"]
	puts -nonewline $fileid $varbs
	close $fileid
}

# Initialize lists of widgets for color display
# (not all may be used by all programs):

set buttlist [list] ; # Buttons
set texlist [list] ; # Text widgets
set entlist [list] ; # Entry widgets
set lublist [list] ; # Listboxes
set spinlist [list] ; # Spinboxes
set winlist [list] ; # Widgets to get window background color when disabled
set headlist [list] ; # Emphasized labels
set lightlist [list] ; # Light labels
set checklist [list] ; # Checkbuttons and radiobuttons

# Integer range generator for "foreach"
# (to do a "for" loop without ugly, awkward "for" code):

proc range {start cutoff finish {step 1}} {
	# If "start" and "finish" aren't integers, do nothing:
	if {[string is integer -strict $start] == 0 || [string is\
		integer -strict $finish] == 0} {
		error "range: Range must contain two integers"
	}
			
	# "Step" has to be an integer too, and
	# no infinite loops that go nowhere are allowed:
	if {$step == 0 || [string is integer -strict $step] == 0} {
		error "range: Step must be an integer other than zero"
	}
	
	# Does the range include the last number?
	switch $cutoff {
		"to" {set inclu 1}
		"no" {set inclu 0}
		default {
			error "range: Use \"to\" for an inclusive range,\
			or \"no\" for a noninclusive range"
		}
	}
		
	# Is the range ascending or descending (or neither)?
	set ascendo [expr $finish - $start]
	if {$ascendo > -1} {
		set up 1
	} else {
		set up 0
	}
	
	# If range is descending and step is positive but doesn't have a "+" sign,
	# change step to negative:
	if {$up == 0 && $step > 0 && [string first "+" $start] != 0} {
		set step [expr $step * -1]
	}
	
	set ranger [list] ; # Initialize list variable for generated range
	switch "$up $inclu" {
		"1 1" {set op "<=" ; # Ascending, inclusive range}
		"1 0" {set op "<" ; # Ascending, noninclusive range}
		"0 1" {set op ">=" ; # Descending, inclusive range}
		"0 0" {set op ">" ; # Descending, noninclusive range}
	}
	
	# Generate a list containing the specified range of integers:
	for {set i $start} "\$i $op $finish" {incr i $step} {
		lappend ranger $i
	}
	return $ranger
}


### GUI ELEMENTS : MAIN WINDOW ###

wm title . "WISH CD-Writer"
set fontnow "-*-helvetica-medium-r-normal--12-*-*-*-*-*-*"

# Top row of widgets:

frame .newfr1
radiobutton .newdata -pady 2 -text "New Data CD" -variable typewrite \
	-value newdat -command newdata
radiobutton .copydata -pady 2 -text "Copy Data CD" -variable typewrite \
	-value copydat -command copydata
radiobutton .reuse -pady 2 -text "Re-use CD Content" -variable typewrite \
	-value reuse -command suite_cheroux
radiobutton .newaudio -pady 2 -text "New Audio CD" -variable typewrite \
	-value audio -command audiobox
radiobutton .blank -pady 2 -text "Blank" -variable typewrite \
	-value blank -command blankout
pack .newdata .copydata .reuse .newaudio .blank -in .newfr1 \
	-side left -fill both -expand 1
grid .newfr1 -row 0 -column 0 -columnspan 3 -sticky news
foreach radiobutt [list .newdata .copydata .reuse .newaudio .blank] {
	lappend checklist $radiobutt
}

# Always-visible bottom widgets:

label .putout -text "P  R  O  G  R  A  M     O  U  T  P  U  T" -pady 2
lappend headlist .putout

frame .newfr2
text .textout -width 80 -height 18 -wrap word
.textout configure -font $fontnow -setgrid 1
ttk::scrollbar .rollout -command ".textout yview"
.textout configure -yscrollcommand ".rollout set"
.textout insert end "READY TO RUN\n"
pack .textout .rollout -in .newfr2 -side left -expand 1 -fill both
lappend texlist .textout

frame .newfr3
button .create -text "Create CD Image"
button .write -text "Write Image to CD" -command {
	savefig
	writedata
}
button .blankcd -text "Blank CD-RW" -command blankcd
button .display -text "Display" -command colodisp
button .help -text "HELP" -command cdhelp
button .exit -text "Exit" -command exit
pack .create .write .blankcd .display .help .exit -in .newfr3 \
	-side left -expand 1 -fill both
foreach butt [list .create .write .blankcd .display .help .exit] {
	lappend buttlist $butt
}
	
grid .putout -row 9 -column 0 -columnspan 3 -sticky news
grid .newfr2 -row 10 -column 0 -columnspan 3 -sticky news
grid .newfr3 -row 11 -column 0 -columnspan 3 -sticky news

# Multiple-use temporary widgets:

label .imago -text "CD Image File: " -pady 2
entry .imgname -width 48 -textvariable imago
button .pickimg -text "Pick" -padx 12 -command pickimage -pady 2
lappend entlist .imgname
lappend buttlist .pickimg

frame .fr5
checkbutton .rock -text "Unix (Rock Ridge) extensions?" \
	-variable rock -onvalue "\-R" -offvalue "" -pady 1
checkbutton .joliet -text "Add Windows (Joliet) extensions?" \
	-variable joliet -onvalue "\-J" -offvalue "" -pady 1
pack .rock .joliet -in .fr5 -side left -expand 1 -fill both
lappend checklist .rock .joliet

frame .fr7
label .speedy -text "Speed: "
spinbox .speedup -width 3 -textvariable speed -from 1 -to 128
label .writer -text "CD Writer Device: " -pady 1
entry .dev -width 20 -textvariable writer_dev
checkbutton .burn -text "Buffer underrun protection?" \
	-variable burnfree -onvalue "driveropts=burnfree" \
	-offvalue "" -pady 1
pack .speedy .speedup .writer .dev .burn -in .fr7 \
	-side left -expand 1 -fill both
lappend entlist .speedup .dev
lappend checklist .burn
lappend spinlist .speedup
	
frame .fr8
checkbutton .dumbo -text "Dummy (test) write only?" \
	-variable dumbo -onvalue "\-dummy" -offvalue "" -pady 1
checkbutton .dump -text "Delete CD image after writing?" \
	-variable dumpimage -pady 1
pack .dumbo .dump -in .fr8 -side left -expand 1 -fill both
lappend checklist .dumbo .dump

# "New Data CD" single-use widgets:

# Row 1:
label .creacid -pady 2 \
	-text "C  r  e  a  t  e     N  e  w     C  D     I  m  a  g  e  :"
lappend headlist .creacid

# Row 2:
frame .new
frame .new.cidfr
frame .new.conifr
label .new.selc -text "Select" -padx 4 -pady 0
label .new.cid -text "CD" -padx 4 -pady 0
label .new.conto -text "Content" -padx 4 -pady 0
label .new.listo -text "List:" -padx 4 -pady 0
label .new.blank -pady 0
pack .new.selc .new.cid .new.conto .new.listo .new.blank \
	-in .new.cidfr -side top -expand 1 -fill both
listbox .new.conames -width 56 -height 4 \
	-selectmode single -listvariable contents
ttk::scrollbar .new.conex -orient horizontal -command ".new.conames xview"
pack .new.conames .new.conex -in .new.conifr \
		-side top -expand 1 -fill both
ttk::scrollbar .new.cony -command ".new.conames yview"
.new.conames configure -yscrollcommand ".new.cony set" \
	-xscrollcommand ".new.conex set"
pack .new.cidfr .new.conifr .new.cony -in .new \
	-side left -expand 1 -fill both
lappend lublist .new.conames

frame .addfr
button .addfr.addcon -text "Add" -pady 0 -command "newcont Add"
button .addfr.editcon -text "Edit" -pady 0 -command {
	getready
	newcont Edit
}
button .addfr.delcon -text "Delete" -pady 0 -command delconlist
pack .addfr.addcon .addfr.editcon .addfr.delcon -in .addfr \
	-side top -expand 1 -fill both
foreach butt [list .addfr.addcon .addfr.editcon .addfr.delcon] {
	lappend buttlist $butt
}

# Row 4:
frame .newfr4
checkbutton .follow -text "Follow symbolic links?" \
	-variable symlinks -pady 1
checkbutton .calc -text "Calculate image size first?" \
	-variable calcsize -pady 1
checkbutton .subdir -text "Include subdirectories?" \
	-variable subdirs -pady 1
pack .follow .calc .subdir -in .newfr4 -side left -expand 1 -fill both
foreach checkbutt [list .follow .calc .subdir] {
	lappend checklist $checkbutt
}

# Row 6:
label .writecd -pady 2 \
	-text "W  r  i  t  e     N  e  w     D  a  t  a     C  D  :"
lappend headlist .writecd
	
# "Copy Data CD" single-use widgets:

frame .copyfr1
label .copycrea -pady 2 -text "C r e a t e   C o p i e d   C D   I m a g e :"
radiobutton .copyboot -pady 2 \
	-text "Bootable" -variable booty -value 1 -command {
	.copyready delete 0 end
	.copyready insert 0 $reader_dev
}
radiobutton .copynoboot -pady 2 \
	-text "Non-bootable" -variable booty -value 0 -command {
	.copyready delete 0 end
	.copyready insert 0 $reader_mnt
}
pack .copycrea .copyboot .copynoboot -in .copyfr1 \
	-side left -expand 1 -fill both
	
label .copyreader -text "CD Reader:"
entry .copyready -width 44
button .copypickread -text "Pick" -padx 12	-command pickreader

label .copywrite -pady 2 \
	-text "W r i t e   C o p i e d   D a t a   C D :"
lappend checklist .copyboot .copynoboot
lappend entlist .copyready
lappend buttlist .copypickread
lappend headlist .copycrea .copywrite .copyboot .copynoboot
	
# "Re-use Data CD Image" single-use widgets:

label .rewrite -pady 2 \
	-text "W  r  i  t  e     D  a  t  a    C  D  :"
lappend headlist .rewrite

frame .suite
button .suite.data -text "Re-use Data CD Image" -command {
	set whatgo oldburn
	redata
}
button .suite.audio -text "Re-use Audio CD TOC" -command {
	set whatgo audio
	audiotoc
}
button .suite.can -text "Cancel" -command {
	set typewrite newdat
	wigswitch
}
pack .suite.data .suite.audio .suite.can -in .suite \
	-side left -expand 1 -fill both
foreach butt [list .suite.data .suite.audio .suite.can] {
	lappend buttlist $butt
}

# Procedure to clear out irrelevant temporary widgets and audio boxes:

proc clearout {} {
	foreach w $::clearwidgets {
		catch {grid remove $w}
	}
	catch {destroy .audi ; destroy .sub}
}

# Possibly irrelevant widgets:

set clearwidgets [list .creacid .new .imago .imgname .pickimg .newfr4 \
	.fr5 .writecd .fr7 .fr8 .copyfr1 .copyreader .copyready .copypickread \
	.copywrite .suite .rewrite .addfr]

# Procedure to show "New Data CD" widgets:

proc newdata {} {
	global butpad speed contents imago calcsize symlinks \
		rock joliet burnfree dumbo dumpimage booty reader_mnt \
		reader_dev writer_dev driver subdirs specfile speed \
		whatgo
		
	# No other widgets allowed:
	clearout
	
	# New CD is to be burned:
	set whatgo newburn
		
	# Row 1:
	grid .creacid -row 1 -column 0 -columnspan 3 -sticky news
		
	# Row 2:
	grid .new -row 2 -column 0 -columnspan 2 -sticky news
	grid .addfr -row 2 -column 2 -sticky news
	
	# Row 3:
	grid .imago -row 3 -column 0 -sticky news
	grid .imgname -row 3 -column 1 -sticky news
	.imgname selection range 0 end
	grid .pickimg -row 3 -column 2 -sticky news
		
	# Row 4:
	grid .newfr4 -row 4 -column 0 -columnspan 3 -sticky news
	
	# Row 5:
	grid .fr5 -row 5 -column 0 -columnspan 3 -sticky news
	
	# Row 6:
	grid .writecd -row 6 -column 0 -columnspan 3 -sticky news
	
	# Row 7:
	grid .fr7 -row 7 -column 0 -columnspan 3 -sticky news
	
	# Row 8:
	grid .fr8 -row 8 -column 0 -columnspan 3 -sticky news
	
	# Bottom:
	.create configure -command {
		savefig
		createimage
	}
	.create configure -state normal
	foreach butt [list .write .blankcd] {
		$butt configure -state disabled
	}
}

# Procedure to show "Copy Data CD" widgets:

proc copydata {} {
	global butpad speed contents imago calcsize symlinks \
		rock joliet burnfree dumbo dumpimage booty reader_mnt \
		reader_dev writer_dev driver subdirs specfile \
		speed whatgo
		
	# No other widgets allowed:
	clearout
	
	# New CD is to be burned:
	set whatgo newburn
	
	# Row 1:
	grid .copyfr1 -row 1 -column 0 -columnspan 3 -sticky news
	
	# Row 2:
	grid .copyreader -row 2 -column 0 -sticky news
	grid .copyready -row 2 -column 1 -sticky news
	.copyready delete 0 end
	if {$booty == 1} {
		.copyready insert 0 $reader_dev
	} else {
		.copyready insert 0 $reader_mnt
	}
	grid .copypickread -row 2 -column 2 -sticky news
		
	# Row 3:
	grid .imago -row 3 -column 0 -sticky news
	grid .imgname -row 3 -column 1 -sticky news
	.imgname selection range 0 end
	grid .pickimg -row 3 -column 2 -sticky news
	
	# Row 4:
	grid .fr5 -row 4 -column 0 -columnspan 3 -sticky news
	
	# Row 5:
	grid .copywrite -row 5 -column 0 -columnspan 3 -sticky news
	
	# Row 6:
	grid .fr7 -row 6 -column 0 -columnspan 3 -sticky news
	
	# Row 7:
	grid .fr8 -row 8 -column 0 -columnspan 3 -sticky news
	
	# Bottom:
	.create configure -command {
		if {$booty == 1} {
			set reader_dev [.copytop.ready get]
		} else {
			set reader_mnt [.copytop.ready get]
		}
		makeimage
	}
	.create configure -state normal
	foreach butt [list .write .blankcd] {
		$butt configure -state disabled
	}
}

# Procedure to show selector buttons for re-using CD content:

proc suite_cheroux {} {
	global typewrite
	clearout
	foreach w [list .create .write .blankcd] {
		$w configure -state disabled
	}
	grid .suite -row 1 -column 0 -columnspan 3 -sticky news
}

# Procedure to show "Re-use Data CD Image" widgets:

proc redata {} {
	global imago burnfree dumbo dumpimage writer_dev \
		headback speed whatgo
	
	# No other widgets allowed:
	clearout
	
	# Old CD image is to be re-used:
	set whatgo oldburn
	
	# Row 1:		
	grid .rewrite -row 1 -column 0 -columnspan 3 -sticky news
	
	# Row 2:
	grid .imago -row 2 -column 0 -sticky news
	grid .imgname -row 2 -column 1 -sticky news
	grid .pickimg -row 2 -column 2 -sticky news
	
	# Row 3:
	grid .fr7 -row 3 -column 0 -columnspan 3 -sticky news
	
	# Row 4:
	grid .fr8 -row 4 -column 0 -columnspan 3 -sticky news
	
	# Bottom:
	.write configure -state normal
	foreach butt [list .create .blankcd] {
		$butt configure -state disabled
	}
}

# Procedure to show nothing to do but blank a CD-RW:

proc blankout {} {
	global whatgo
	clearout
	set whatgo blank
	.blankcd configure -state normal
	foreach butt [list .create .write] {
		$butt configure -state disabled
	}
}

# Procedure to show the right widgets for the selected CD-writing type:

proc wigswitch {} {
	global typewrite whatgo
	switch $typewrite {
		newdat {
			set whatgo newburn
			newdata
		}
		copydat {
			set whatgo newburn
			copydata
		}
		reuse {suite_cheroux}
		audio {
			set whatgo audio
			audiobox
		}
		blank {
			set whatgo blank
			blankout
		}
	}
}
wigswitch


### COLOR DISPLAY ###

# Procedure to set up GUI box for configuring color display:

proc colodisp {} {
	global color red green blue whatfig whatbutt colorlist colordir \
		winback winfore selback selfore buttback buttfore textback \
		textfore headback headfore lightback lightfore coloron wishdir \
		libdir current_scheme bogomips
	if {$coloron == 0} {
		source [file join $libdir wishcolorplus.tcl]
		set coloron 1
	}
	wishcolorplus ; # This does all the work--from WISH Color Picker Plus
	wm title .colo "WISH CD-Writer : WISH Color Picker Plus"
}


### USER HELP ###

# Use WISH User Help for user help guide:

# Procedure for setting up user help display:

proc cdhelp {} {
	global helpon libdir
	if {$helpon == 0} {
		source [file join $libdir wishuhelp.tcl]
		set helpon 1
	}
	uhelp ; # Set up user help window--from WISH User Help
	wm title .uhelp "WISH CD-Writer - User Help"
	set linkup [open $::helpfile r]
	set helpcontents [read $linkup]
	close $linkup
	.uhelp.tx insert 1.0 $helpcontents
	helplink .uhelp.tx; # Show links in text--from WISH User Help
	.uhelp.tx mark set insert 1.0
	.uhelp.tx configure -state disabled
}


### PROCEDURES INVOKED FROM MAIN WINDOW (EXCEPT AUDIO) ###

# Procedure to pick image filename:

proc pickimage {} {
	global imago exclulist
	set oldimago $imago
	set imgdir [tk_chooseDirectory]
	if {[file isdirectory $imgdir]} {
		set imago "$imgdir/IMAGE01"
		if {$oldimago in $exclulist} {
			lset exclulist [lsearch $exclulist $oldimago] $imago
		} else {
			lappend exclulist $imago
		}
		set exclulist [lsort -dictionary $exclulist]
	} else {
		tk_messageBox -message "If you want a name for your CD\
			image file, please select a directory for the file!"
	}
}

# Procedure to pick CD reader:

proc pickreader {} {
	global reader_mnt reader_dev booty
	set newreader [tk_getOpenFile]
	if {[file isdirectory $newreader]} {
		set reader_mnt $newreader
	} else {
		tk_messageBox -message "Please insert CD to be copied\
			in reader drive and specify correct reader drive!"
	}
	.copytop.ready delete 0 end
	if { $booty == 1 } {
		.copyready insert 0 $reader_dev
	} else {
		.copyready insert 0 $reader_mnt
	}
}

# Procedure to calculate directory size:
	
proc calcsize {dir} {
	global subdirs symlinks showhid exclulist toto
	set dirlist [glob -nocomplain -directory $dir *]
	if {$showhid == 1} {
		set hiddens [glob -nocomplain -types hidden -directory $dir *]
		foreach hid $hiddens {
			lappend dirlist $hid
		}
	}
	foreach fil $dirlist {
		if {$fil in $exclulist} {continue}
		set filtil [file tail $fil]
		switch $filtil {
			".." -
			"." {continue}
			default {
				if {$symlinks == 1 || [file type $fil] ne "link"} {
					set toto [expr {$toto + [file size $fil]}]
				}
				if {$subdirs == 1 && [file isdirectory $fil]} {
				 	calcsize $fil
				}
			}
		}
	}
}

# Procedure to disable widgets that shouldn't be used for a while:

proc nogui {} {
	foreach w [list .newdata .copydata .reuse .newaudio .blank \
		.create .write .blankcd .exit] {
		$w configure -state disabled	
	}
}

# Procedure to re-enable widgets when their time has come:

proc regui {whatfor} {
	foreach w [list .newdata .copydata .reuse .newaudio .blank .exit] {
		$w configure -state normal
	}
	switch $whatfor {
		newburn {
			foreach w [list .create .write] {
				$w configure -state normal
			}
		}
		oldburn {
			.write configure -state normal
		}
		blank {
			.blankcd configure -state normal
		}
		default {
			# Don't do anything
		}
	}
}

# Procedures to create CD image:

proc createimage {} {
	global inclulist exclulist finalist finalex calcsize imago whatgo
	set contlist [.new.conames curselection]
	if {[llength $contlist] < 1} {
		tk_messageBox -message "If you want to create a CD image,\
			please select a CD content list" -type ok
		return
	}
	if {$imago == ""} {
		tk_messageBox -message "Please select a directory for\
			the CD image" -type ok
		return
	}
	getready
	set finalist [list]
	foreach inclu $inclulist {
		if {[file readable $inclu]} {
			lappend finalist $inclu
			if {[file isdirectory $inclu]} {
				hackunread $inclu
			}
		} else {
			set eekplus [expr {[string first "=" $inclu] + 1}]
			set rem [string range $inclu $eekplus end]
			if {[file readable $rem]} {
				lappend finalist $inclu
				if {[file isdirectory $rem]} {
					hackunread $rem
				}
			}
		}
	}
	set finalex ""
	if {$imago ni $exclulist} {
		lappend exclulist $imago
	}
	foreach ex $exclulist {
		if {$ex != ""} {
			append finalex "-x $ex "
		}
	}
	if {$calcsize == 0} {
		makeimage
	} else {
		set imageorno [sizenow final]
		if {$imageorno eq "yes"} {
			makeimage
		}
	}
}

proc makeimage {} {
	global command booty symlinks rock joliet reader_mnt reader_dev \
		imago finalist finalex typewrite
	if {$typewrite eq "copydat"} {
		if {$booty} {
			set command "dd if=$reader_dev of=$imago"
		} else {
			set command "mkisofs $rock $joliet -o $imago $reader_mnt"
		}
	} else {
		if {$symlinks} {
			set follow "-f"
		} else {
			set follow ""
		}
		set command "mkisofs $follow $rock $joliet -graft-points \
			$finalex -o $imago $finalist"
	}
	.textout insert end "Creating CD image...\n"
	Pipe $command
}

# Procedure to write data CD:

proc writedata {} {
	global speed imago dumbo burnfree writer_dev whatgo writdat
	set writdat 1
	set command "cdrecord -v $dumbo dev=$writer_dev $burnfree\
		speed=$speed $imago"
	.textout insert end "Trying to write CD . . . hope this works . . .\n"
	Pipe $command
}

# Procedure to blank CD:

proc blankcd {} {
	global speed writer_dev
	set command "cdrecord -v blank=all speed=$speed dev=$writer_dev"
	.textout insert end "Blanking a CD should take several minutes.\
		If this doesn't take several minutes, try again!\n"
	Pipe $command
}

# Procedure to create and initialize the slave interpreter:

proc checkserv {} {
	if {[interp exists servus] == 0} {
		interp create servus
		interp alias servus puts {} PutsAlias servus
	}
}

# Procedure to execute command and pipe output for display:

proc Pipe {command} {
	global output
	nogui
	.textout mark set insert end
	if {[catch {open "|$command |& cat"} output]} {
		.textout insert insert $output error
		.textout insert insert \n
		.textout insert insert $prompt prompt
		.textout see insert
		.textout mark set limit insert
		set command ""
	} else {
		fconfigure $output -buffering none -blocking 0
		fileevent $output readable Log	
	}
}

# Procedure to display piped output:

proc Log {} {
	global output dumpimage imago typewrite whatgo writdat
	if {[eof $output]} {
		catch {close $output}
		if {[.textout compare insert != "insert linestart"]} {
			.textout insert insert \n
		}
		.textout insert insert "READY TO RUN\n"
		regui $whatgo
		.textout see insert
		if {$writdat == 1} {
			if {$dumpimage == 1 && [file exists $imago] && \
				[regexp {newdat|copydat|reuse} $typewrite]} {
				file delete $imago
			}
			set writdat 0
		}
		return
	} else {
		set putout [read $output]
		if {[string first "\b" $putout] != -1} {
			set putout [string map -nocase {\b ""} $putout]
		}
		.textout insert insert $putout output
		.textout see insert
	}
}

# Procedure to use alias to put stdout and stderr into the text widget:

proc PutsAlias {slave args} {
	if {[llength $args] > 3} {
		error "invalid arguments"
	}
	set newline "\n"
	if {[string match "-nonewline" [lindex $args 0]]} {
		set newline ""
		set args [lreplace $args 0 0]
	}
	if {[llength $args] == 1} {
		set chan stdout
		set string [lindex $args 0]$newline
	} else {
		set chan [lindex $args 0]
		set string [lindex $args 1]$newline
	}
	if [regexp (stdout|stderr) $chan] {
		.textout mark gravity limit right
		.textout insert limit $string output
		.textout see limit
		.textout mark gravity limit left
	} else {
		puts -nonewline $chan $string
	}
}


### GUI ELEMENTS: DATA CD CONTENT LIST EDITING BOX

# Procedure to get ready to edit CD content list or create CD image:

proc getready {} {
	global inclulist exclulist getnom
	set getsel [.new.conames curselection]
	set getnom [.new.conames get $getsel]
	set slash [string first "/" $getnom]
	set getlist [string range $getnom $slash end]
	source $getlist
}

# Procedure to set up GUI box to add or edit data CD content list:

set exclulist [list]

proc newcont {whattodo} {
	global nowdir subdirs inclulist exclulist whatcont doit \
		listline getnom imago showhid
	toplevel .cont
	wm title .cont "$whattodo Data CD Content List"
	set butpad 3
	set doit $whattodo
	if {$imago ni $exclulist} {
		lappend exclulist $imago
	}
	set whatcont "\[Top Level\]"
	cd
	frame .cont.top
	label .cont.condir -text "CD Content List: "
	entry .cont.content -bg $::textback -fg $::textfore -width 64
	button .cont.set -text "Set Name" -bg $::buttback -fg $::buttfore \
		-command setname
	button .cont.change -text "Change Name" -bg $::buttback -fg $::buttfore \
		-command changename
	pack .cont.condir .cont.content .cont.set .cont.change -in .cont.top \
		-side left -expand 1 -fill both
	grid .cont.top -row 0 -column 0 -columnspan 7 -sticky news
	grid [label .cont.sel -bg $::lightback -fg $::lightfore -pady 6 \
		-text "Select content from : $nowdir "] \
		-row 1 -column 0 -columnspan 7 -sticky news
	grid [label .cont.dir -text "Directory" -pady 4 -relief raised] \
		-row 2 -column 0 -columnspan 2 -sticky news
	grid [label .cont.fil -text "File" -pady 4 -relief raised] \
		-row 2 -column 2 -columnspan 2 -sticky news
	frame .cont.butfr
	button .cont.addcon -text "Add Content" -command addcon
	button .cont.rename -text "Rename Content" -command renabox
	button .cont.delcon -text "Delete Item(s)" \
		-activebackground red -command delitems
	button .cont.new -text "New CD Dir" -command addnew
	button .cont.exclude -text "Exclude" -command excludo
	button .cont.showcon -text "Show Content" \
		-command showcont
	button .cont.showex -text "Show Exclusions" \
		-command showexclu
	checkbutton .cont.showhid -pady 4 -text "Show Hidden" \
		-relief raised -variable showhid	-command {after 10 view_dir}
	button .cont.calc -text "Calculate Size" \
 		-command "sizenow prelim"
	button .cont.desel -text "Deselect All" -command "selection clear"
	button .cont.done -text "DONE" -command getdone
	button .cont.cancel -text "Cancel" -command "destroy .cont"
	foreach butt [list .cont.addcon .cont.rename .cont.delcon .cont.new \
		.cont.exclude .cont.showcon .cont.showex .cont.showhid .cont.calc \
		.cont.desel .cont.done .cont.cancel] {
		$butt configure -bg $::buttback -fg $::buttfore	
	}
	.cont.showhid configure -selectcolor $::textback
	pack .cont.addcon .cont.rename .cont.delcon .cont.new .cont.exclude \
		.cont.showcon .cont.showex .cont.showhid .cont.calc .cont.desel \
		.cont.done .cont.cancel -in .cont.butfr \
		-side top -expand 1 -fill both
	grid .cont.butfr -row 2 -column 4 -rowspan 3 -sticky news
	grid [label .cont.cdcon -text "CD Content" -pady 4 -relief raised] \
		-row 2 -column 5 -columnspan 2 -sticky news
	frame .cont.frbutts
	button .cont.topo -text "Top" -command {chug_now top}
	button .cont.home -text "Home" -command {chug_now home}
	button .cont.up -text "Up" -command {chug_now up}
	foreach butt [list .cont.topo .cont.home .cont.up] {
		$butt configure -bg $::buttback -fg $::buttfore
		pack $butt -in .cont.frbutts -side left -expand 1 -fill both
	}
	grid .cont.frbutts -row 3 -column 0 -sticky news
	frame .cont.frdir
	listbox .cont.dirlist -bg $::textback -fg $::textfore -width 22 \
		-height 16 -border 1 -selectmode extended
	bind .cont.dirlist <Double-Button-1> {
		set listline [.cont.dirlist curselection]
		chug_now $listline
	}
	bind .cont.dirlist <Button-3> {
		selection clear
		set clixel [.cont.dirlist nearest %y]
		.cont.dirlist selection set $clixel
		set listline [.cont.dirlist curselection]
		chug_now $listline
	}
	ttk::scrollbar .cont.scrox -orient horizontal \
		-command ".cont.dirlist xview"
	pack .cont.dirlist .cont.scrox -in .cont.frdir \
		-side top -expand 1 -fill both
	ttk::scrollbar .cont.scroy -command ".cont.dirlist yview"
	.cont.dirlist configure -xscrollcommand ".cont.scrox set" \
		-yscrollcommand ".cont.scroy set"
	
	grid .cont.frdir -row 4 -column 0 -sticky news
	grid .cont.scroy -row 3 -column 1 -rowspan 2 -sticky news
	frame .cont.frfil
	listbox .cont.filist -bg $::textback -fg $::textfore -width 22 \
		-height 18 -border 1 -selectmode extended
	ttk::scrollbar .cont.frox -orient horizontal \
		-command ".cont.filist xview"
	.cont.filist configure -xscrollcommand ".cont.frox set"
	pack .cont.filist .cont.frox -in .cont.frfil \
		-side top -expand 1 -fill both
	ttk::scrollbar .cont.froy -command ".cont.filist yview"
	.cont.filist configure -xscrollcommand ".cont.frox set" \
		-yscrollcommand ".cont.froy set"
	grid .cont.frfil -row 3 -column 2 -rowspan 2 -sticky news
	grid .cont.froy -row 3 -column 3 -rowspan 2 -sticky news
	frame .cont.frcon
	listbox .cont.conlist -bg $::textback -fg $::textfore -width 42 \
		-height 18 -border 1 -selectmode extended -listvariable inclulist
	listbox .cont.exlist -bg $::textfore -fg $::textback -width 42 -height 17 \
		-border 1 -selectmode extended -listvariable exclulist
	if {$whattodo eq "Edit"} {
		.cont.content insert 0 $getnom
		.cont.content configure -state readonly
		foreach w [list .cont.set .cont.change] {
			$w configure -state disabled
		}
	} else {
		set inclulist [list]
		set exclulist [list $imago]
		.cont.conlist insert 0 "\[Top Level\]"
		.cont.conlist itemconfigure 0 -background "#A0FFA0"
	}
	bind .cont.conlist <Double-Button-1> {
		set listline [.cont.conlist curselection]
		chug_cd $listline
	}
	bind .cont.conlist <Button-3> {
		selection clear
		set clixel [.cont.conlist nearest %y]
		.cont.conlist selection set $clixel
		set listline [.cont.conlist curselection]
		chug_cd $listline
	}
	ttk::scrollbar .cont.clox -orient horizontal \
		-command ".cont.conlist xview"
	ttk::scrollbar .cont.exx -orient horizontal \
		-command ".cont.exlist xview"
	pack .cont.conlist .cont.clox -in .cont.frcon \
		-side top -expand 1 -fill both
	ttk::scrollbar .cont.cloy -command ".cont.conlist yview"
	ttk::scrollbar .cont.exy -command ".cont.exlist yview"
	.cont.conlist configure -xscrollcommand ".cont.clox set" \
		-yscrollcommand ".cont.cloy set"
	.cont.exlist configure -xscrollcommand ".cont.exx set" \
		-yscrollcommand ".cont.exy set"
	grid .cont.frcon -row 3 -column 5 -rowspan 2 -sticky news
	grid .cont.cloy -row 3 -column 6 -rowspan 2 -sticky news
	grid [label .cont.addtocd -bg $::lightback -fg $::lightfore -pady 6 \
		-text "Add content to CD directory : $whatcont"] \
		-row 5 -column 0 -columnspan 7 -sticky news
	view_dir
	focus .cont.content
}

# Procedure to set name of new CD content list:

proc setname {} {
	global contidir
	hackname
	set newname [string trim [.cont.content get]]
	if {$newname == ""} {
		tk_messageBox -message "Your CD content list must\
			have a name!" -type ok
		return
	}
	set newlow [string map "{ } _" [string tolower $newname]]
	.cont.content delete 0 end
	.cont.content insert 0 "$newname : [file join $contidir $newlow.tcl]"
	.cont.content configure -state readonly
}

# Procedure to change name of new CD content list:

proc changename {} {
	.cont.content configure -state normal
	hackname
	.cont.content selection range 0 end
}

# Procedure to get rid of old CD content filename:

proc hackname {} {
	set conlist [.cont.content get]
	set colon [string first ":" $conlist]
	set slash [string first "/" $conlist]
	if {$colon > -1 || $slash > -1} {
		if {$colon < $slash} {
			.cont.content delete [expr {$colon-1}] end
		} else {
			.cont.content delete [expr {$slash-1}] end
		}
	}
}

# Procedure to delete CD content list:
proc delconlist {} {
	set deldir [.new.conames curselection]
	if {[llength $deldir] < 1} {
		tk_messageBox -message "If you want to delete a CD content\
			list, please select it" -type ok
		return
	}
	set usher [tk_messageBox -message "Are you sure you want to\
		delete this CD content list?" -type yesno]
	if {$usher eq "yes"} {
		.new.conames delete $deldir
	}
}

# Procedure to change source directory:

proc chug_now {how} {
	global nowdir
	switch $how {
		"top" {
			cd "/"
			set nowdir [pwd]
		}
		"home" {
			cd
			set nowdir [pwd]
		}
		"up" {
			cd ".."
			set nowdir [pwd]
		}
		default {
			set nowdir [file join $nowdir [.cont.dirlist get $how]]
			cd $nowdir
		}
	}
	.cont.sel configure -text "Select content from $nowdir"
	view_dir
}

# Procedure to view lists of directories and files:

proc view_dir {} {
	global nowdir showhid

	# Clear listboxes:
	.cont.dirlist delete 0 end
	.cont.filist delete 0 end

	# Display lists of directories and files:
	set visibles [glob -nocomplain -directory $nowdir *]
	if {$showhid == 1} {
		set hiddens [glob -nocomplain -types hidden -directory $nowdir *]
	}
	if {[llength $visibles] > 0} {
		set visibles [lsort -dictionary $visibles]
		foreach filename $visibles {
			if {[file isdirectory $filename]} {
				.cont.dirlist insert end [file tail $filename]
			} else {
				.cont.filist insert end [file tail $filename]
            }
        }
	}
	if {$showhid == 1 && [llength $hiddens] > 0} {
		set hiddens [lsort -dictionary $hiddens]
		foreach filename $hiddens {
			if {[file isdirectory $filename]} {
				.cont.dirlist insert end [file tail $filename]
			} else {
				.cont.filist insert end [file tail $filename]
            }
        }
	}
}

# Procedure to change CD content directory:

proc chug_cd {listline} {
	global whatcont inclulist
	set newdir [.cont.conlist get $listline]
	foreach line [range 0 no [.cont.conlist index end]] {
		.cont.conlist itemconfigure $line -background "#FFFFF0"
	}
	if {[string first "=" $newdir] < 0 && [string first "/" $newdir] != 0} {
		.cont.conlist itemconfigure $listline -background "#A0FFA0"
		selection clear
		set whatcont $newdir
		.cont.addtocd configure -text "Add content to\
			CD directory : $whatcont"
	} else {
		tk_messageBox -message "Sorry, WISH CD-Writer can modify CD content\
			only in a CD directory you created, or in the \"Top Level\"\
			CD directory" -type ok
	}
}

# Procedure to identify content to be added or excluded from CD:

proc gripcont {} {
	set griplist [list]
	set farx 0
	set gripnum [.cont.dirlist curselection]
	if {[llength $gripnum] > 0} {
		foreach num $gripnum {
 			lappend griplist [.cont.dirlist get $num]
		}
	} else {
		set gripnum [.cont.filist curselection]
		if {[llength $gripnum] > 0} {
			foreach num $gripnum {
				lappend griplist [.cont.filist get $num]
			}
		}
	}
	selection clear
	return $griplist
}

# Procedure to add existing directories & files to CD content:

proc addcon {} {
	global nowdir whatcont inclulist exclulist imago
	set addlist [gripcont]
	if {[llength $addlist] < 1} {
		tk_messageBox -message "Please select one or more\
			directories or files to add to CD content" -type ok
		return
	}
	if {[.cont.cdcon cget -text] eq "Exclude from CD"} {
		set inclorno [tk_messageBox -message "Include content in\
			CD directory \"$whatcont\"?" -type yesno]
		if {$inclorno eq "no"} {
			showcont
			return
		}
	}
	foreach add $addlist {
		if {$whatcont eq "\[Top Level\]"} {
			set linker $add
		} else {
			set linker [file join $whatcont $add]
		}
		set targer [file join $nowdir $add]
		if {$targer eq $imago} {
			tk_messageBox -message "Your CD image file must be excluded\
				from the list of its own contents!" -type ok
			continue
		}
		foreach exnum [range 0 no [llength $exclulist]] {
			set item [lindex $exclulist $exnum]
			if {[string match $item* $targer]} {
				set ritex 0
				set cantboth [tk_messageBox -message "You cannot both\
					include and exclude	$targer. Do you wish to include it?"\
					-type yesno]
				if {$cantboth eq "yes"} {
					.cont.exlist delete $exnum
					break
				} else {
					set ritex 1
					continue
				}
			}	
		}
		if {[info exists ritex] == 0 || $ritex == 0} {
			lappend inclulist "$linker=$targer"
		}
	}
	.cont.conlist delete 0
	set inclulist [lsort -dictionary $inclulist]
	.cont.conlist insert 0 "\[Top Level\]"
	selection clear
	showcont
}

# Procedure to set up GUI box for renaming CD content:

proc renabox {} {
	global whatcont renlist biglist renleng reno rennum mergeok
	set renlist [list]
	set biglist [list]
	set reno 0
	set mergeok no
	set rennum [.cont.conlist curselection]
	if {[llength $rennum] > 0} {
		foreach num $rennum {
			set bigum [.cont.conlist get $num]
			set lilum [split $bigum "="]
			lappend renlist [lindex $lilum 0]
			if {[llength $lilum] > 1} {
				lappend biglist "=[lindex $lilum end]"
			} else {
				lappend biglist ""
			}
		}
		set renleng [llength $renlist]
	} else {
		tk_messageBox -message "Please select one or more\
			items to rename" -type ok
		return
	}
	toplevel .ren
	wm title .ren "Rename"
	frame .ren.fr0
	label .ren.rename -text "Rename: "
	label .ren.oldname -text [lindex $renlist 0]
	pack .ren.rename .ren.oldname -in .ren.fr0 -side left -expand 1 -fill both
	grid .ren.fr0 -sticky news
	frame .ren.fr1
	label .ren.as -text "As: "
	entry .ren.ent -bg white -width 36
	pack .ren.as .ren.ent -in .ren.fr1 -side left -expand 1 -fill both
	grid .ren.fr1 -sticky news
	frame .ren.fr2
	button .ren.doit -text "Rename" -default active -command renacon
	button .ren.skipone -text "Skip One" -default normal -command skipdown
	button .ren.skipall -text "Skip All" -default normal -command {
		destroy .ren
		view_cdcont
	}
	pack .ren.doit .ren.skipone .ren.skipall -in .ren.fr2 \
		-side left -expand 1 -fill both
	grid .ren.fr2 -sticky news
	bind .ren <Key-Return> renacon
	focus .ren.ent
}

# Procedure to rename CD content:

proc renacon {} {
	global whatcont renlist biglist inclulist renleng reno rennum mergeok
	set oldum [.ren.oldname cget -text]
	set newum [.ren.ent get]
	if {$newum ne ""} {
		lset inclulist [lindex $rennum $reno] "$newum[lindex $biglist $reno]"
		.cont.conlist delete 0
		set inclulist [lsort -dictionary $inclulist]
		.cont.conlist insert 0 "\[Top Level\]"
		skipdown
	} else {
		if {$mergeok ne "yes"} {
			set mergeok [tk_messageBox -message "Content from any file or\
				directory not preceded by a name and an equals sign (=)\
				will be merged into the Top Level CD directory, not placed\
				into a separate	CD directory. Subdirectories of such a\
				directory will remain intact as subdirectories of the\
				Top Level CD directory. OK?" -type yesno]
		}
		if {$mergeok eq "yes"} {
			lset inclulist [lindex $rennum $reno] [string trimleft \
				[lindex $biglist $reno] "="]
			.cont.conlist delete 0
			set inclulist [lsort -dictionary $inclulist]
			.cont.conlist insert 0 "\[Top Level\]"
			skipdown
		}
	}
	
}

# Procedure to go to next item in "rename" list:

proc skipdown {} {
	global reno renleng renlist
	.ren.ent delete 0 end
	incr reno
	if {$reno < $renleng} {
		.ren.oldname configure -text [lindex $renlist $reno]
		focus .ren.ent
	} else {
		destroy .ren
		selection clear
	}
}

# Procedure to delete CD content or exclusions:

proc delitems {} {
	global whatcont imago
	if {[.cont.cdcon cget -text] eq "CD Content"} {
		set delta ".cont.conlist"
	} else {
		set delta ".cont.exlist"
	}
	set delnum [$delta curselection]
	set delleng [expr [llength $delnum] -1]
	foreach deli [range $delleng to 0] {
		set deldex [lindex $delnum $deli]
		set delft [$delta get $deldex]
		if {$delft eq $imago} {
			tk_messageBox -message "Your CD image file must be excluded\
				from the list of its own contents!" -type ok
		} elseif {$delft eq "\[Top Level\]"} {
			tk_messageBox -message "The \[Top Level\] directory cannot\
				be deleted" -type ok
		} else {
			$delta delete $deldex
		}
	}
}

# Procedure to set up GUI box for adding new directory to CD content list:

proc addnew {} {
	global whatcont
	toplevel .addo
	wm title .addo "Add New Dir"
	grid [label .addo.nom -text "New Directory Name:"] -sticky news
	grid [entry .addo.ent -bg white -width 32] -sticky news
	frame .addo.fr
	button .addo.add -text "Add" -default active -command getadd
	button .addo.done -text "Done" -default normal -command {
		getadd
		destroy .addo
	}
	button .addo.cancel -text "Cancel" -default normal \
		-command "destroy .addo"
	pack .addo.add .addo.done .addo.cancel -in .addo.fr \
		-side left -expand 1 -fill both
	grid .addo.fr -sticky news
	bind .addo <Key-Return> getadd
	focus .addo.ent
}

# Procedure to get directory name and add it:

proc getadd {} {
	global whatcont inclulist
	set getit [.addo.ent get]
	if {$getit ne ""} {
		if {[string first "=" $getit] != -1 || [string \
			first "/" $getit] == 0} {
			tk_messageBox -message "Sorry, you can't add a CD directory\
				with a slash (/) at the beginning or an equals sign (=)\
				anywhere	in the name. If you did, WISH CD-Writer would\
				think you didn't add it." -type ok
			return
		}
		if {$whatcont eq "\[Top Level\]"} {
			lappend inclulist $getit
		} else {
			lappend inclulist [file join $whatcont $getit]
		}
		.cont.conlist delete 0
		set inclulist [lsort -dictionary $inclulist]
		.cont.conlist insert 0 "\[Top Level\]"
		.cont.conlist see [lsearch $inclulist $getit]
		.addo.ent delete 0 end
		focus .addo.ent
	}
}

# Procedure to exclude content from CD:

proc excludo {} {
	global nowdir exclulist inclulist
	set exlist [gripcont]
	if {[llength $exlist] < 1} {
		tk_messageBox -message "Please select one or more\
			directories or files to exclude from CD content" -type ok
		return
	}
	set showex 0
	foreach ex $exlist {
		set bigex [file join $nowdir $ex]
		set hackinc 0
		set hackrej 0
		set cantboth no
		set incluleng [expr {[llength $inclulist] -1}]
		foreach inc [range $incluleng to 1] {
			set item [lindex $inclulist $inc]
			set aequum [string first "=" $item]
			if {$aequum != -1} {
				set item [string range $item [expr {$aequum + 1}] end]
			}
			if {[string match $bigex* $item]} {
				if {$hackinc == 0} {
					set cantboth [tk_messageBox -message "If you exclude\
						$bigex, you cannot include any content from it. \
						Do you	wish to exclude $bigex?"	-type yesno]
					if {$cantboth eq "no"} {
						set hackrej 1
						break
					} else {
						set hackinc 1
					}
				}
				if {$hackinc == 1} {
					.cont.conlist delete $inc
				}
			}
		}
		if {$hackrej == 0} {
			lappend exclulist $bigex
			set showex 1
		}
	}
	set exclulist [lsort -dictionary $exclulist]
	selection clear
	if {$showex == 1} {
		showexclu
	}
}

# Procedure to show content:

proc showcont {} {
	foreach w [list .cont.exlist .cont.exx] {pack forget $w}
	foreach w [list .cont.conlist .cont.clox] {
		pack $w -in .cont.frcon -side top -expand 1 -fill both
	}
	grid forget .cont.exy
	grid .cont.cloy -row 3 -column 6 -sticky news
	.cont.cdcon configure -text "CD Content"
}

# Procedure to show excluded items:

proc showexclu {} {
	foreach w [list .cont.conlist .cont.clox] {pack forget $w}
	foreach w [list .cont.exlist .cont.exx] {
		pack $w -in .cont.frcon -side top -expand 1 -fill both
	}
	grid forget .cont.cloy
	grid .cont.exy -row 3 -column 6 -sticky news	
	.cont.cdcon configure -text "Exclude from CD"
}



# Procedure to show current size of CD content directory:

proc sizenow {hownow} {
	global subdirs symlinks showhid inclulist toto
	set toto 0
	foreach item $inclulist {
		set aequum [string first "=" $item]
		set rootum [string first "/" $item]
		if { $aequum == -1 && $rootum != 0} {
			continue
		}
		set cont [string range $item [expr {$aequum + 1}] end]
		if {[file isdirectory $cont]} {
			calcsize $cont
		} elseif {[file exists $cont]} {
			set toto [expr $toto + [file size $cont]]
		}
	}	
	if {$subdirs == 1} {
		set subway "with"
	} else {
		set subway "without"
	}
	if {$symlinks == 1} {
		set linkway "with"
	} else {
		set linkway "without"
	}
	if {$showhid == 1} {
		set hidway "with"
	} else {
		set hidway "without"
	}
	set calcmess "Approximately [expr $toto/1024/1024]\
		megabytes\n($toto bytes) in CD content:\n$subway\
		subdirectories;\n$linkway symbolic links;\n$hidway\
		unspecified hidden files."
	if {$hownow eq "prelim"} {
		tk_messageBox -message $calcmess -type ok
	} else {
		set imageorno [tk_messageBox -message "$calcmess Create CD image?" \
			-type yesno]
		return $imageorno
	}
}

# Procedure to finish adding or editing CD content list:

proc getdone {} {
	global inclulist exclulist contents
	set contnom [.cont.content get]
	set concoloon [string first ":" $contnom]
	set conslash [string first "/" $contnom]
	if {$concoloon == -1 || $conslash == -1} {
		setname
		return
	}
	set filnom [string range $contnom $conslash end]
	set comnom [string trim [string range $contnom 0 [expr {$concoloon-1}]]]
	set doonfil "# WISH CD-Writer : CD content list \
		\n# $comnom\n\nset inclulist \[list $inclulist\] \
		\nset exclulist \[list $exclulist\]"
	set filid [open $filnom "w"]
	puts -nonewline $filid $doonfil
	close $filid
	destroy .cont
	set nomline [lsearch $contents $contnom]
	if {$nomline == -1} {
		lappend contents $contnom
		set contents [lsort -dictionary $contents]
	}
	.new.conames selection set [lsearch $contents $contnom]
}

# Procedure to hack out unreadable files from list for CD image:

proc hackunread {dir} {
	global exclulist showhid
	set dirlist [glob -nocomplain -directory $dir *]
	if {$showhid == 1} {
		set hiddens [glob -nocomplain -types hidden -directory $dir *]
		foreach hid $hiddens {
			lappend dirlist $hid
		}
	}
	foreach fil $dirlist {
		if {[file readable $fil] < 1} {
			lappend exclulist $fil
		} elseif {[file isdirectory $fil]} {
			hackunread $fil
		}
	}
}


### GUI ELEMENTS: AUDIO CD CONTENT LIST EDITING BOX ###

set picklist(soundboxes) [list] ; # List of selected items
set picklist(trackboxes) [list] ; # Same, for track list
set thisline(soundboxes) "" ; # Active line
set thisline(trackboxes) "" ; # Same, for track list
set selback cyan ; # Selection background
set mubs 0 ; # Megabytes
set bubs 0 ; # Bytes

# Procedure to set up GUI box for selecting sound files
# to turn into audio tracks:

proc audiobox {} {
	global selback picklist thisline soundboxes trackboxes \
		trackents mubs bubs boxnom env whatgo
	clearout
	set whatgo audio
	foreach w [list .create .write .blankcd] {
		$w configure -state disabled
	}
	toplevel .audi
	wm title .audi "Sound Files for Audio Tracks"
	cd
	grid [label .audi.sound -bg $::headback -fg $::headfore -pady 6 -text \
		"S E L E C T   S O U N D   F I L E S  in  [pwd] :"] \
		-row 0 -column 0 -columnspan 10 -sticky news
	grid [label .audi.sndir -text "Directory" -relief raised] \
		-row 1 -column 0 -columnspan 2 -sticky news
	grid [label .audi.sndfil -text "File" -relief raised] \
		-row 1 -column 2 -columnspan 2 -sticky news
	grid [label .audi.sndsiz -text "Size" -relief raised] \
		-row 1 -column 4 -columnspan 2 -sticky news
	grid [button .audi.mid -text "Make WAV from MIDI (one at a time)" \
		-command midwav] -row 1 -column 6 -columnspan 4 -sticky news
	frame .audi.frdir
	listbox .audi.soundir -bg $::textback -fg $::textfore -width 20 \
		-height 9 -border 1 -selectmode single
	.audi.soundir insert 0 "\[Top\]"
	.audi.soundir insert 1 "\[Up One Level\]"
	bind .audi.soundir <Double-Button-1> {
		set listline [.audi.soundir curselection]
		change_dir $listline
	}
	bind .audi.soundir <Button-3> {
		selection clear
		set clixel [.audi.soundir nearest %y]
		.audi.soundir selection set $clixel
		set listline [.audi.soundir curselection]
		change_dir $listline
	}
	ttk::scrollbar .audi.recrox -orient horizontal \
		-command ".audi.soundir xview"
	pack .audi.soundir .audi.recrox -in .audi.frdir \
		-side top -expand 1 -fill both
	ttk::scrollbar .audi.recroy -command ".audi.soundir yview"
	.audi.soundir configure -xscrollcommand ".audi.recrox set" \
		-yscrollcommand ".audi.recroy set"
	grid .audi.frdir -row 2 -column 0 -rowspan 6 -sticky news
	grid .audi.recroy -row 2 -column 1 -rowspan 6 -sticky news
	frame .audi.frfil
	listbox .audi.soundfile -bg $::textback -fg $::textfore -width 20 \
		-height 9 -border 1 -selectmode extended
	ttk::scrollbar .audi.filrox -orient horizontal \
		-command ".audi.soundfile xview"
	.audi.soundfile configure -xscrollcommand ".audi.filrox set"
	pack .audi.soundfile .audi.filrox -in .audi.frfil \
		-side top -expand 1 -fill both
	grid .audi.frfil -row 2 -column 2 -rowspan 6 -columnspan 2 -sticky news
	frame .audi.frsiz
	listbox .audi.soundsize -bg $::textback -fg $::textfore -width 12 \
		-height 9 -border 1 -selectmode extended
	ttk::scrollbar .audi.sizrox -orient horizontal \
		-command ".audi.soundsize xview"
	.audi.soundsize configure -xscrollcommand ".audi.sizrox set"
	pack .audi.soundsize .audi.sizrox -in .audi.frsiz \
		-side top -expand 1 -fill both
	set soundboxes [list .audi.soundfile .audi.soundsize]
	ttk::scrollbar .audi.sizroy -command [list rollon $soundboxes]
	foreach bojo $soundboxes {
		$bojo configure -yscrollcommand ".audi.sizroy set"
		bind $bojo <Button-1> {
			set boxnom soundboxes
			set boxlist $soundboxes
			set thisline($boxnom) [%W nearest %y]
			pickline $thisline($boxnom)
		}
		bind $bojo <Control-Button-1> {
			set boxnom soundboxes
			set boxlist $soundboxes
			set thisline($boxnom) [%W nearest %y]
			pickadd $thisline($boxnom)
		}
		bind $bojo <Shift-Button-1> {
			set boxnom soundboxes
			set boxlist $soundboxes
			set lastline $thisline($boxnom)
			set thisline($boxnom) [%W nearest %y]
			pickmore on $lastline $thisline($boxnom)
		}
		bind $bojo <Control-Button-3> {
			set boxnom soundboxes
			set boxlist $soundboxes
			set noline [%W nearest %y]
			deselrow $noline
		}
		bind $bojo <Shift-Button-3> {
			set boxnom soundboxes
			set boxlist $soundboxes
			set lastline $thisline($boxnom)
			set thisline($boxnom) [%W nearest %y]
			pickmore off $lastline $thisline($boxnom)
		}
		bind $bojo <Double-Button-1> addfiles
	}
	grid .audi.frsiz -row 2 -column 4 -rowspan 6 -sticky news
	grid .audi.sizroy -row 2 -column 5 -rowspan 6 -sticky news
	grid [button .audi.add -text "Add to Track List (double-click sound\
		file)" -command addfiles] \
		-row 2 -column 6 -columnspan 4 -sticky news
	grid [button .audi.del -text "Delete from Track List" \
		-command delfiles] -row 3 -column 6 -columnspan 4 -sticky news
	grid [button .audi.edit -text "Edit Track Listing (right-click; Enter\
		when done)" -command {
		set boxnom trackboxes
		set thisline($boxnom) ""
		foreach box $trackboxes {
			set tracklines [$box curselection]
			if { $tracklines != "" } {
				set thisline($boxnom) [lindex $tracklines 0]
				break
			}
		}
		if { $thisline($boxnom) != "" } {
			editline $thisline($boxnom)
		} else {
			tk_messageBox -message "Please select a track listing\
				to edit!" -type okcancel
		}
	}] -row 4 -column 6 -columnspan 4 -sticky news
	grid [button .audi.desel -text "Deselect All" \
		-command deselrows] -row 5 -column 6 -columnspan 4 -sticky news
	grid [button .audi.updatoa -text "Update Size (when \"READY TO RUN\")" \
		-command updatoa] \
		-row 6 -column 6 -columnspan 4 -sticky news
	grid [button .audi.done -text "D  O  N  E" \
		-command maketoc] -row 7 -column 6 -columnspan 2 -sticky news
	grid [button .audi.cancel -text "Cancel"	-command {destroy .audi}] \
		-row 7 -column 8 -columnspan 2 -sticky news
	foreach butt [list .audi.mid .audi.add .audi.del .audi.edit .audi.desel \
		.audi.updatoa .audi.done .audi.cancel] {
		$butt configure -bg $::buttback -fg $::buttfore
	}
	set tracpad 5
	grid [label .audi.track -text "T R A C K   L I S T :  Total size \
		$bubs bytes (approx. $mubs MB)" -bg $::headback \
		-fg $::headfore -pady $tracpad] \
		-row 8 -column 0 -columnspan 10 -sticky news
	grid [label .audi.titletrack -text "Title" \
		-relief raised -pady $tracpad] \
		-row 9 -column 0 -columnspan 2 -sticky news
	grid [label .audi.filetrack -text "File" \
		-relief raised -pady $tracpad] \
		-row 9 -column 2 -columnspan 2 -sticky news
	grid [label .audi.sizetrack -text "Size" \
		-relief raised -pady $tracpad] \
		-row 9 -column 4 -columnspan 2 -sticky news
	grid [label .audi.perftrack -text "Performer" \
		-relief raised -pady $tracpad] \
		-row 9 -column 6 -columnspan 2 -sticky news
	grid [label .audi.comptrack -text "Composer" \
		-relief raised -pady $tracpad] \
		-row 9 -column 8 -columnspan 2 -sticky news
	grid [entry .audi.titlent -bg $::textback -fg $::textfore -width 25 \
		-border 1] -row 10 -column 0 -columnspan 2 -sticky news
	grid [entry .audi.filent -bg $::lightback -fg $::lightfore -width 20] \
		-row 10 -column 2 -columnspan 2 -sticky news
	grid [entry .audi.sizent -bg $::lightback -fg $::lightfore -width 12] \
		-row 10 -column 4 -columnspan 2 -sticky news
	grid [entry .audi.perfent -bg $::textback -fg $::textfore -width 25 \
		-border 1] -row 10 -column 6 -columnspan 2 -sticky news
	grid [entry .audi.compent -bg $::textback -fg $::textfore -width 25 \
		-border 1] -row 10 -column 8 -columnspan 2 -sticky news
	set trackents [list .audi.titlent .audi.filent .audi.sizent \
		.audi.perfent .audi.compent]
	frame .audi.frtitle
	listbox .audi.tracrec -bg $::textback -fg $::textfore -width 25 \
		-height 10 -border 1 -selectmode extended
	ttk::scrollbar .audi.tracrox -orient horizontal \
		-command ".audi.tracrec xview"
	.audi.tracrec configure -xscrollcommand ".audi.tracrox set"
	pack .audi.tracrec .audi.tracrox -in .audi.frtitle \
		-side top -expand 1 -fill both
	grid .audi.frtitle -row 11 -column 0 -columnspan 2 -sticky news
	frame .audi.frile
	listbox .audi.tracfile -bg $::textback -fg $::textfore -width 20 \
		-height 10 -border 1 -selectmode extended
	ttk::scrollbar .audi.tracfox -orient horizontal \
		-command ".audi.tracfile xview"
	.audi.tracfile configure -xscrollcommand ".audi.tracfox set"
	pack .audi.tracfile .audi.tracfox -in .audi.frile \
		-side top -expand 1 -fill both
	grid .audi.frile -row 11 -column 2 -columnspan 2 -sticky news
	frame .audi.frize
	listbox .audi.tracsize -bg $::textback -fg $::textfore -width 12 \
		-height 10 -border 1 -selectmode extended
	ttk::scrollbar .audi.tracsox -orient horizontal \
		-command ".audi.tracsize xview"
	.audi.tracsize configure -xscrollcommand ".audi.tracsox set"
	pack .audi.tracsize .audi.tracsox -in .audi.frize \
		-side top -expand 1 -fill both
	grid .audi.frize -row 11 -column 4 -columnspan 2 -sticky news
	frame .audi.fripe
	listbox .audi.tracperf -bg $::textback -fg $::textfore -width 25 \
		-height 10 -border 1 -selectmode extended
	ttk::scrollbar .audi.tracpox -orient horizontal \
		-command ".audi.tracperf xview"
	.audi.tracperf configure -xscrollcommand ".audi.tracpox set"
	pack .audi.tracperf .audi.tracpox -in .audi.fripe \
		-side top -expand 1 -fill both
	grid .audi.fripe -row 11 -column 6 -columnspan 2 -sticky news
	frame .audi.frice
	listbox .audi.traccomp -bg $::textback -fg $::textfore -width 25 \
		-height 10 -border 1 -selectmode extended
	ttk::scrollbar .audi.traccox -orient horizontal \
		-command ".audi.traccomp xview"
	.audi.traccomp configure -xscrollcommand ".audi.traccox set"
	pack .audi.traccomp .audi.traccox -in .audi.frice \
		-side top -expand 1 -fill both
	set trackboxes [list .audi.tracrec \
		.audi.tracfile .audi.tracsize .audi.tracperf .audi.traccomp]
	ttk::scrollbar .audi.traccoy -command [list rollon $trackboxes]
	foreach bojo $trackboxes {
		$bojo configure -yscrollcommand ".audi.traccoy set" \
			-selectbackground cyan
		bind $bojo <Button-1> {
			set boxnom trackboxes
			set boxlist $trackboxes
			set thisline($boxnom) [%W nearest %y]
			pickline $thisline($boxnom)
		}
		bind $bojo <Control-Button-1> {
			set boxnom trackboxes
			set boxlist $trackboxes
			set thisline($boxnom) [%W nearest %y]
			pickadd $thisline($boxnom)
		}
		bind $bojo <Shift-Button-1> {
			set boxnom trackboxes
			set boxlist $trackboxes
			set lastline $thisline($boxnom)
			set thisline($boxnom) [%W nearest %y]
			pickmore on $lastline $thisline($boxnom)
		}
		bind $bojo <Control-Button-3> {
			set boxnom trackboxes
			set boxlist $trackboxes
			set noline [%W nearest %y]
			deselrow $noline
		}
		bind $bojo <Shift-Button-3> {
			set boxnom trackboxes
			set boxlist $trackboxes
			set lastline $thisline($boxnom)
			set thisline($boxnom) [%W nearest %y]
			pickmore off $lastline $thisline($boxnom)
		}
		bind $bojo <Button-3> {
			set boxnom trackboxes
			set boxlist $trackboxes
			set thisline($boxnom) [%W nearest %y]
			pickline $thisline($boxnom)
			editline $thisline($boxnom)
		}
		bind $bojo <Double-Button-1> {
			set boxnom trackboxes
			set boxlist $trackboxes
			set thisline($boxnom) [%W nearest %y]
			pickline $thisline($boxnom)
			editline $thisline($boxnom)
		}
	}
	grid .audi.frice -row 11 -column 8 -sticky news
	grid .audi.traccoy -row 11 -column 9 -sticky news
	frame .audi.tice
	label .audi.titulo -text "A U D I O   C D   T I T L E : " \
		-bg $::lightback -fg $::lightfore -pady 6
	entry .audi.title -bg $::textback -fg $::textfore -width 60 \
		-font "-*-helvetica-bold-r-normal--14-*-*-*-*-*-*"
	pack .audi.titulo .audi.title -in .audi.tice \
		-side left -expand 1 -fill both
	grid .audi.tice -row 12 -column 0 -columnspan 10 -sticky news
	bind .audi <Key-Return> donedit
	read_dir
}

lappend buttlist .audi.mid .audi.add .audi.del .audi.edit .audi.desel \
	.audi.updatoa .audi.done .audi.cancel
lappend lublist .audi.soundir .audi.soundfile .audi.soundsize \
	.audi.tracrec .audi.tracfile .audi.tracsize .audi.tracperf .audi.traccomp
lappend entlist .audi.titlent .audi.perfent .audi.compent .audi.title
lappend headlist .audi.sound .audi.track
lappend lightlist .audi.filent .audi.sizent .audi.titulo

# Procedure to get scrollbar to make all listboxes scroll together:

proc rollon {boxes args} {
	foreach box $boxes {
		eval {$box yview} $args
	}
}

# Procedure to fill lists with filenames:

proc read_dir {} {

	# Clear listboxes:
	.audi.soundir delete 2 end
	.audi.soundfile delete 0 end
	.audi.soundsize delete 0 end

	# Display lists of directories and files:
	set unsorted [glob -nocomplain *]
	if {$unsorted != "" } {
		set files [lsort $unsorted]
		foreach filename $files {
			if { [file isdirectory $filename] != 0 } {
				.audi.soundir insert end "$filename"
			} else {
				.audi.soundfile insert end "$filename"
				set sz [file size $filename]
				.audi.soundsize insert end $sz
            }
        }
	}
}

# Procedure to change directory:

proc change_dir { listline } {
	set olddir [pwd]
	switch $listline {
		0 { cd /}
		1 { cd .. }
		default {
			set newdir [.audi.soundir get $listline]
			cd $olddir/$newdir
		}
	}
	.audi.sound configure -text \
		"S E L E C T   S O U N D   F I L E S  in  [pwd] :"
	read_dir
}

# Procedure to highlight single row only:

proc pickline {line} {
	global picklist boxlist boxnom thisline selback
	selection clear
	if { [info exists picklist($boxnom)] } {
		foreach pick $picklist($boxnom) {
			foreach bojo $boxlist {
				set bojend [$bojo index end]
				if { [expr $bojend - $thisline($boxnom)] > 0 } {
					$bojo itemconfigure $thisline($boxnom) -background {}
				}
			}
		}
	}
	set picklist($boxnom) ""
	set thisline($boxnom) $line
	foreach bojo $boxlist {
		set bojend [$bojo index end]
		if { [expr $bojend - $thisline($boxnom)] > 0 } {
			$bojo itemconfigure $thisline($boxnom) -background $selback
		}
	}
	lappend picklist($boxnom) $thisline($boxnom)
}

# Procedure to highlight additional row:

proc pickadd {line} {
	global thisline picklist selback boxlist boxnom
	set thisline($boxnom) $line
	foreach bojo $boxlist {
		set bojend [$bojo index end]
		if { [expr $bojend - $thisline($boxnom)] > 0 } {
			$bojo itemconfigure $thisline($boxnom) -background $selback
		}
	}
	lappend picklist($boxnom) $thisline($boxnom)
	set picklist($boxnom) [lsort -integer $picklist($boxnom)]
}

# Procedure to highlight (or un-highlight) multiple rows:

proc pickmore {onoff oldline newline} {
	global picklist thisline selback boxlist boxnom
	if { $onoff == "on" } {
		set pickback $selback
	} else {
		set pickback {}
	}
	if { $newline > $oldline } {
		for { set b [expr $oldline+1] } { $b <= $newline } { incr b } {
			foreach bojo $boxlist {
				set bojend [$bojo index end]
				if { [expr $bojend - $thisline($boxnom)] > 0 } {
					$bojo itemconfigure $b	-background $pickback
					if { $onoff == "off" } {
						$bojo selection clear $b
					}
				}
			}
			if { $onoff == "on" } {
				lappend picklist($boxnom) $b
			} else {
				set whackoe [lsearch $picklist($boxnom) $b]
				set picklist($boxnom) [lreplace $picklist($boxnom) \
					$whackoe $whackoe]
			}
		}
	} else {
		for { set b $newline } { $b < $oldline } { incr b } {
			foreach bojo $boxlist {
				set bojend [$bojo index end]
				if { [expr $bojend - $thisline($boxnom)] > 0 } {
					$bojo itemconfigure $b	-background $pickback
					if { $onoff == "off" } {
						$bojo selection clear $b
					}
				}
			}
			if { $onoff == "on" } {
				lappend picklist($boxnom) $b
			} else {
				set whackoe [lsearch $picklist($boxnom) $b]
				set picklist($boxnom) [lreplace $picklist($boxnom) \
					$whackoe $whackoe]
			}
		}
	}
	set picklist($boxnom) [lsort -integer $picklist($boxnom)]
	set thisline($boxnom) $newline
}

# Procedure to deselect single row:

proc deselrow {line} {
	global picklist thisline boxlist boxnom
	set thisline($boxnom) $line
	foreach bojo $boxlist {
		set bojend [$bojo index end]
		if { [expr $bojend - $thisline($boxnom)] > 0 } {
			$bojo selection clear $thisline($boxnom)
			$bojo itemconfigure $thisline($boxnom) -background {}
		}
	}
	set wherein [lsearch $picklist($boxnom) $thisline($boxnom)]
	set picklist($boxnom) [lreplace $picklist($boxnom) $wherein $wherein]
	# clearentry
}

# Procedure to deselect multiple rows:

proc deselrows {} {
	global boxlist boxnom picklist thisline trackents
	set allboxes [list .audi.soundfile .audi.soundsize .audi.tracrec \
		.audi.tracfile .audi.tracsize .audi.tracperf .audi.traccomp]
	selection clear
	foreach bojo $allboxes {
		for {set b 0} {$b < [$bojo index end]} {incr b} {
			$bojo itemconfigure $b -background ""
		}
	}
	.audi.filent configure -state normal
	.audi.sizent configure -state normal
	foreach ent $trackents {
		$ent delete 0 end
	}
}

# Procedure to produce WAV files from MIDI files:

proc midwav {} {
	set soundlines [.audi.soundfile curselection]
	if { $soundlines == "" } {
		set soundlines [.audi.soundsize curselection]
	}
	if { $soundlines == "" } {
		tk_messageBox -message "Please select a MIDI file\
			to convert to WAV format (the original MIDI file will\
			remain unchanged)!" -type ok
	} else {
		set filin [lindex $soundlines 0]
		set fil [.audi.soundfile get $filin]
		if { [inpath timidity] } {
			set stoporgo [tk_messageBox -message "WISH	CD-Writer\
				will	use TiMidity to produce a WAV file\
				from this MIDI file. Please be patient; this may\
				take some time. The words \"READY TO RUN\" will\
				appear at the bottom of the program output when\
				the job is finished." -type okcancel]
			if { $stoporgo == "ok" && [file extension $fil] == ".mid" } {
				set filpars [string trimright $fil "mid"]
				set filpars [string trimright $filpars .]
				set filroot [pwd]/$filpars
				set command "timidity -s 44100Hz -Ow\
					-o $filroot.wav $filroot.mid"
				.textout insert end "Producing $filpars.wav\
					from	$fil...\n"
				deselrows
				.audi.tracfile insert end "$filroot.wav"
				.audi.tracfile xview moveto 1
				Pipe $command
			}
		} else {
			tk_messageBox -message "WISH	CD-Writer\
				would use TiMidity (if available) to produce\
				a WAV file from this MIDI file. However, it does not\
				appear that TiMidity is available on this system."\
				-type ok
		}
	}
}

# Procedure to add files to track list:

proc addfiles {} {
	global soundboxes bubs mubs
	set anyraws 0
	set soundlines [.audi.soundfile curselection]
	if { $soundlines == "" } {
		set soundlines [.audi.soundsize curselection]
	}
	if { $soundlines == "" } {
		tk_messageBox -message "Please select one or more sound files\
			to add to the track list!" -type ok
		return
	} else {
		foreach line $soundlines {
			set fil [.audi.soundfile get $line]
			set filex [file extension $fil]
			if { $filex != ".wav" } {
				set anyraws 1
				break
			}
		}
	}
	set stoporgo "go"
	if { $anyraws == 1 } {
		set ifnot "If your answer is NO, please convert to\
			WAV or \"raw audio\" format	before proceeding!"
		if { [llength $soundlines] == 1 } {
			set stoporgo [tk_messageBox -message "Is this a WAV\
			or \"raw audio\" file? $ifnot" -type yesno]
		} else { set stoporgo [tk_messageBox -message "Are these\
			all WAV or \"raw audio\" files? $ifnot" -type yesno]
		}
	}
	if { $stoporgo == "go" || $stoporgo == "yes" || $stoporgo == "ok" }	{
		foreach line $soundlines {
			set fil [.audi.soundfile get $line]
			set dirfil "[pwd]/$fil"
			lappend tracklines $dirfil
		}
		foreach line $tracklines {
			.audi.tracfile insert end $line
		}
		deselrows
		.audi.tracfile xview moveto 1
	}
}

# Procedure to delete files from track list:

proc delfiles {} {
	global soundboxes trackboxes
	foreach box $trackboxes {
		set tracklines [$box curselection]
		if { $tracklines != "" } {
			set lasttrack [llength $tracklines]
			break
		}
	}
	if { $tracklines == "" } { return }
	for {set d [expr $lasttrack-1]} {$d >= 0} {incr d -1} {
		foreach box $trackboxes {
			$box delete [lindex $tracklines $d]
		}
	}
}

# Procedures to edit track listing:

proc editline {line} {
	global trackents trackboxes edlin
	set edlin $line
	for { set b 0 } { $b < [llength $trackents] } { incr b } {
		set entity [lindex $trackents $b]
		set lenity [lindex $trackboxes $b]
		if { $edlin < [$lenity index end] } {
			set editee [$lenity get $edlin]
			$entity delete 0 end
			$entity insert 0 $editee
		}
	}
	.audi.filent xview end
	.audi.filent configure -state readonly
	.audi.sizent configure -state readonly
}

proc donedit {} {
	global edlin trackents trackboxes
	.audi.filent configure -state normal
	.audi.sizent configure -state normal
	for { set b 0 } { $b < [llength $trackents] } { incr b } {
		set entity [lindex $trackents $b]
		set lenity [lindex $trackboxes $b]
		set entry($b) [$entity get]
		$lenity delete $edlin
		$lenity insert $edlin $entry($b)
		$entity delete 0 end
	}
	foreach bojo $trackboxes {
		if { $edlin < [$bojo index end] } {
			$bojo itemconfigure $edlin -background ""
		}
	}
	set edlin ""
}

# Procedure to update track listing size:

proc updatoa {} {
	global mubs bubs
	set mubs 0
	set bubs 0
	for { set f 0 } { $f < [.audi.tracfile index end] } { incr f } {
		set fil [.audi.tracfile get $f]
		set siz [file size $fil]
		set bubs [expr $bubs + $siz]
		.audi.tracsize delete $f
		.audi.tracsize insert $f $siz
	}
	set lubs [expr $bubs/1024]
	set mubs [expr $lubs/1024]
	.audi.track configure -text "T R A C K   L I S T :  \
		Total size $bubs bytes (approx. $mubs MB)"
}

# Procedure to create TOC file:

proc maketoc {} {
	subnotepad ""
	set audiotitle [.audi.title get]
	.sub.text insert 1.0 "CD_DA\
		\nCD_TEXT \{\
		\n\tLANGUAGE_MAP \{\
		\n\t\t0 : EN\
		\n\t\}\
		\n\
		\n\tLANGUAGE 0 \{\
		\n\t\tTITLE \"$audiotitle\"\
		\n\t\}\
		\n\}"
	for { set f 0 } { $f < [.audi.tracfile index end] } { incr f } {
		.sub.text insert end "\n\nTRACK AUDIO\
			\nCD_TEXT \{\
			\n\tLANGUAGE 0 \{"
		set FILE [.audi.tracfile get $f]
		set optlist [list TITLE PERFORMER COMPOSER]
		set audilist [list .audi.tracrec .audi.tracperf .audi.traccomp]
		for { set t 0 } { $t < 3 } { incr t } {
			set opto [lindex $optlist $t]
			set audo [lindex $audilist $t]
			if { $f < [$audo index end] } {
				set anyopt [$audo get $f]
				if { $anyopt != "" } {
					.sub.text insert end "\n\t\t$opto \"$anyopt\""
				}
			}
		}
		.sub.text insert end "\n\t\}\
			\n\}\
			\nPREGAP 0:2:0\
			\nFILE \"$FILE\" 0"
	}
}

# Procedure to display and edit TOC file:

proc subnotepad {filename} {
	global wr exmess exans newfil curfil tocfil env \
		driver writer_dev contidir
	toplevel .sub
	wm title .sub "wish subnotepad"
	set exmess "Save changes?"
	set exans "" ; # Answer to "exmess" question
	set curfil "" ; # Name of open file
	set tocfil [file join $contidir toc_1.txt]

	# Make the text area, scrollbars, and entry bar:
	set wr word
	set fonto -*-courier-medium-r-normal--14-*-*-*-*-*-*
	grid [text .sub.text -width 65 -height 20 -fg $::textfore \
		-bg $::textback -wrap $wr -font $fonto -setgrid 1 -undo 1 \
		-tabs {36 72 108 144 180 216 252 288}] \
		-row 0 -column 0 -sticky news
	grid [ttk::scrollbar .sub.y -command ".sub.text yview"] \
		-row 0 -column 1 -sticky news
	grid [ttk::scrollbar .sub.x -command ".sub.text xview" \
		-orient horizontal] \
		-row 1 -column 0 -columnspan 2 -sticky news
	.sub.text configure -xscrollcommand ".sub.x set" \
		-yscrollcommand ".sub.y set"
	grid rowconfigure .sub 0 -weight 1
	grid columnconfigure .sub 0 -weight 1
	frame .sub.fr
	label .sub.toc -text "TOC File: "
	entry .sub.ent -width 45 -bg $::textback -fg $::textfore \
		-textvariable tocfil
	label .sub.driv -text "Driver:"
	entry .sub.ver -bg $::textback -fg $::textfore -textvariable driver
	pack .sub.toc .sub.ent .sub.driv .sub.ver -in .sub.fr \
		-side left -expand 1 -fill both
	grid .sub.fr -row 2 -column 0 -columnspan 2 -sticky news
	frame .sub.fr2
	label .sub.writter -text "CD Writer Device: "
	entry .sub.writent -bg $::textback -fg $::textfore \
		-textvariable writer_dev
	button .sub.pick -text "Pick" -command {
		set trytoc [tk_getOpenFile]
		if { $trytoc != "" } {
			set tocfil $trytoc
		}
	}
	button .sub.sim -text "Simulate" -command simaudio
	button .sub.write -text "Write Audio CD" -command writeaudio
	button .sub.cancel -text "Cancel" -command {
		readytogo
		if { $exans != "cancel" } {
			destroy .sub
		}
	}
	foreach butt [list .sub.pick .sub.sim .sub.write .sub.cancel] {
		$butt configure -bg $::buttback -fg $::buttfore
	}
	pack .sub.writter .sub.writent .sub.pick .sub.sim .sub.write \
		.sub.cancel -in .sub.fr2 -side left -expand 1 -fill both
	grid .sub.fr2 -row 3 -column 0 -columnspan 2 -sticky news
	focus .sub.text

	# Procedure to get ready to remove old contents from text area:
	proc readytogo {} {
		global exmess exans
		set exans ""
		if { [.sub.text edit modified] } {
			set exans [tk_messageBox -message $exmess \
				-title "Save changes?" -type yesnocancel -icon question]
			if { $exans == yes } {
			file_save
			}
		}
	}

	# Procedure to remove old contents from text area:
	proc outwithold {} {
		global curfil
		set curfil ""
		.sub.text delete 1.0 end
		.sub.text edit reset
		.sub.text edit modified 0
		wm title .sub "wish supernotepad"
	}

	# Procedure to change display on title bar:

	proc wmti {chug} {
		global curfil
		if { $chug == "save" } {
			if { [info exists curfil] < 1 || $curfil == "" } {
				wm title .sub "wish subnotepad : save changes?"
			} else {
				wm title .sub "wish subnotepad : $curfil (save changes?)"
			}
		} else {
			if { [info exists curfil] < 1 || $curfil == "" } {
				wm title .sub "wish subnotepad"
			} else {
				wm title .sub "wish subnotepad : $curfil"
			}
			bind .sub <Key> { after 100 saveup }
			bind .sub <Button-2> { after 100 saveup }
		}
	}

	# Procedure to get saved changes recognized at once:

	proc saveup {} {
		if { [.sub.text edit modified] } {
			bind .sub <Key> {}
			bind .sub <Button-2 {}
			wmti save
		}
	}

	# Procedure to put contents of new file into text area:
	proc inwithnew {} {
		global newfil curfil
		if { $newfil != "" } {
			set star [open $newfil "r"]
			set filcon [read $star]
			close $star
			set filcon [string trimright $filcon]
			.sub.text insert insert $filcon
			set curfil $newfil
			.sub.text edit reset
			.sub.text edit modified 0
			wmti unchug
		}
	}


	# Create main menu

	menu .sub.filmen -tearoff 0

	# File menu

	menu .sub.filmen.fil -tearoff 0
	.sub.filmen add cascade -label "File" -underline 0 -menu .sub.filmen.fil

	# File -- New

	.sub.filmen.fil add command -label "New" -underline 0 -command file_new
	
	proc file_new {} {
		global exans newfil
		readytogo
		if { $exans != "cancel" } {
			outwithold
			.sub.text edit separator
			wmti unchug
		}
	}

	# File -- Open

	.sub.filmen.fil add command -label "Open" -underline 0 -command file_open

	proc file_open {} {
		global exans newfil curfil
		readytogo
		if { $exans != "cancel" } {
			set newfil [tk_getOpenFile]
			if { $newfil != "" } {
				if { [isBinary $newfil] } {
					tk_messageBox -icon error -message "Sorry, WISH\
						Supernotepad can't handle that type of file!"
					return
				}
				outwithold
				inwithnew
				.sub.text mark set insert 1.0
			}
		}
	}

	# Procedure to determine whether file is binary or text
	# (Thanks to an anonymous contributor to
	# The Tcl'ers Wiki <http://mini.net/tcl/0>)!
	proc isBinary { filename } {
		if { [ catch {
			set bin_rx {[\x00-\x08\x0b\x0e-\x1f]}
			set fid [ ::open $filename r ]
			fconfigure $fid -translation binary
			fconfigure $fid -buffersize 1024
			fconfigure $fid -buffering full
			set test [ read $fid 1024 ]
			::close $fid
			if { [ regexp $bin_rx $test ] } {
				set bool 1
			} else {
				set bool 0
			}
		} err ] } {	catch { ::close $fid }
			return -code error "isBinary: $err"
		}
		return $bool
	}
	
	# File -- Insert
	
	.sub.filmen.fil add command -label "Insert File" -underline 0 \
		-command file_insert -accelerator Shift+F1

	proc file_insert {} {
		global exans
		set adfil [tk_getOpenFile]
		if { $adfil != "" } {
			set star [open $adfil "r"]
			set filcon [read $star]
			close $star
			set filcon [string trimright $filcon]
			.sub.text insert insert $filcon
			.sub.text edit separator
			.sub.text see insert
			wmti save
		}
	}
	
	bind .sub <Shift-F1> file_insert

	.sub.filmen.fil add separator

	# File -- Save

	.sub.filmen.fil add command -label "Save" -underline 0 \
		-command file_save -accelerator Ctrl+s

	proc file_save {} {
		global curfil
		set filcon [.sub.text get 1.0 {end -1c}]
		set texto [string trimright $filcon]
		if { $curfil != "" } {
			set fileid [open $curfil "w"]
			puts -nonewline $fileid $texto
			close $fileid
			.sub.text edit reset
			.sub.text edit modified 0
			wmti unchug
		} else { file_saveas }
	}	

	bind .sub <Control-s> file_save
	
	# File -- Save As

	.sub.filmen.fil add command -label "Save As" -underline 5 \
		-command "file_saveas"

	proc file_saveas {} {
		global curfil newfil
		set filcon [.sub.text get 1.0 {end -1c}]
		set texto [string trimright $filcon]
		set filto [tk_getSaveFile]
		if { $filto != "" } {
			set fileid [open $filto "w"]
			puts -nonewline $fileid $texto
			close $fileid
			set curfil $filto
			.sub.text edit reset
			.sub.text edit modified 0
			wmti unchug
		}
	}

	.sub.filmen.fil add separator

	# File -- Exit

	.sub.filmen.fil add command -label "Exit" -underline 1 -command {
		readytogo
		if { $exans != "cancel" } {
			exit
		}
	}

	# Edit menu
	# using built-in procedures tk_textCut, tk_textCopy, tk_textPaste

	menu .sub.filmen.edit -tearoff 0
	.sub.filmen add cascade -label "Edit" -underline 0 -menu .sub.filmen.edit

	# Edit -- Cut

	.sub.filmen.edit add command -label "Cut" -underline 2 \
		-command cut_text -accelerator Ctrl+x
	# binding <Control-x> is built-in

	proc cut_text {} {
		tk_textCut .sub.text
		.sub.text edit separator
	}

	# Edit -- Copy

	.sub.filmen.edit add command -label "Copy" -underline 0 \
		-command copy_text -accelerator Ctrl+c
	# binding <Control-c> is built-in

	proc copy_text {} {
		tk_textCopy .sub.text
		.sub.text edit separator
	}

	# Edit -- Paste

	.sub.filmen.edit add command -label "Paste" -underline 0 \
		-command paste_text -accelerator Ctrl+g

	proc paste_text {} {
		tk_textPaste .sub.text
		.sub.text edit separator
		.sub.text see insert
	}

	bind .sub <Control-g> paste_text
	# This binding isn't built in.
	# <Control-v> didn't work quite right--I don't know why.

	# Edit -- Delete

	.sub.filmen.edit add command -label "Delete" -underline 0 \
		-command ".sub.text delete sel.first sel.last" -accelerator Del

	proc delete_text {} {
		.sub.text delete sel.first sel.last
		.sub.text edit separator
	}

	.sub.filmen.edit add separator

	# Edit -- Undo

	.sub.filmen.edit add command -label "Undo" -underline 0 -command {
		catch {	.sub.text edit undo }
	} -accelerator Ctrl+z
	# Binding Ctrl+z is built in

	# Edit -- Redo

	.sub.filmen.edit add command -label "Redo" -underline 0 \
		-command { catch {.sub.text edit redo} } -accelerator Ctrl+r
	bind .sub <Control-r> { catch {.sub.text edit redo} }
	bind .sub <space> {.sub.text edit separator}
	bind .sub <BackSpace> {.sub.text edit separator}

	.sub.filmen.edit add separator

	# Edit -- Select All

	.sub.filmen.edit add command -label "Select all" -underline 7 \
		-command ".sub.text tag add sel 1.0 end" -accelerator Ctrl+/
	# binding <Control-/> is built-in

	# Edit -- Word Wrap

	.sub.filmen.edit add checkbutton -variable wr \
		-label "Word wrap" -onvalue word -offvalue none \
		-underline 0 -selectcolor #ff0000 -command wraponoroff

	proc wraponoroff {} {
		global wr
		if { $wr == "none" } {
			.sub.text configure -wrap none
		} else {
			.sub.text configure -wrap word
		}
	}


	# Make the menu visible:

	.sub configure -menu .sub.filmen
	
	# Get ready to see some changes:
	
	bind .sub <Key> { after 100 saveup }
	bind .sub <Button-2> { after 100 saveup }
}

lappend buttlist .sub.pick .sub.sim .sub.write .sub.cancel
lappend texlist .sub.text
lappend entlist .sub.ent .sub.ver .sub.writent

# Procedure to simulate audio CD writing:

proc simaudio {} {
	global curfil tocfil driver bus targ lun speed writer_dev
	set curfil $tocfil
	file_save
	set command "cdrdao simulate --driver $driver --device $writer_dev \
		--speed $speed -v 2 $tocfil"
	Pipe $command
}

# Procedure to write audio CD for real:

proc writeaudio {} {
	global curfil tocfil driver bus targ lun speed writer_dev
	set curfil $tocfil
	file_save
	set command "cdrdao write --driver $driver --device $writer_dev \
		--speed $speed -v 2 $tocfil"
	Pipe $command
}

# Procedure for re-using audio CD TOC file:

proc audiotoc {} {
	global tocfil whatgo
	clearout
	set whatgo audio
	foreach w [list .create .write .blankcd] {
		$w configure -state disabled
	}
	if { [info exists tocfil] && [file exists $tocfil] } {
		subnotepad $tocfil
	} else {
		subnotepad ""
		set okorno [tk_messageBox -message "Please select a TOC file\
			for your audio CD!" -type okcancel]
		if { $okorno == "ok" } {
			file_open
		} else {
			destroy .sub
		}
	}
}

# Load most recently used color scheme, if specified in configuration
# file; if not, load "AntiqueBisque" color scheme as default;
# if not that either, complain:

if {[info exists current_scheme]} {
	source [file join $colordir $current_scheme.tcl]
} elseif {[file readable [file join $colordir AntiqueBisque.tcl]]} {
	source [file join $colordir AntiqueBisque.tcl]
} else {
	tk_messageBox -message "Current color scheme file not found\
	in $colordir" -type ok
}

