//
// Copyright (c) 1997 Jean-Marc Zucconi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer
//    in this position and unchanged.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. The name of the author may not be used to endorse or promote products
//    derived from this software without specific prior written permission
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/rtprio.h>
#include <sys/errno.h>

#include <c++tk.h>

#include "cd-write.h"

static struct dev_struct dev[] = {
    {"cd0", 0},
    {"cd1", 0},
    {"cd2", 0},
    {"cd3", 0},
    {0, 0}
};
static struct worm_op *ops;

static char device[40];

static ButtonBox bbox, bx, bb;
static Label label;
static ExFileSelectBox2 B;
static ScrolledHList hlist;
static Button bsel;
static int speed;
static int speeds[3] = {1, 2, 4}; 
static Radiobutton r[8], rb;
static OptionMenu cdtype;
char *cdtype_txt[4] = {"CD-DA", "CD-ROM", "{CD-ROM XA/mode 1}", 
		    "{CD-ROM XA/mode 2}"};
static int cd_type;
static int disabled = 1;
static int writing = 0;
static char filename[1024];
static int track, total_size, selected_item;
static int dummy, error;
static int button;

static int disk_at_once;

static void
ret (int argc, char **argv)
{
    if (!writing) {
	TkDestroy (".write");
	if (*TkWinfo ("exists", ".cue") == '1')
	    kill_cue ();
	delete_cue_sheet ();
    }
}
static void
setbutton (int argc, char **argv)
{
    button = argv[0][0] - '0';
}
static int
open_close_tray ()
{
    int r;
    FD fd;
    char d[20];

    if (*TkWinfo ("exists", ".tray") == '1')
	return 0;

    TkToplevel (".tray -class Dialog");
    TkWm ("title .tray", "{Open/Close tray}");
    TkLabel (".tray.msg -wraplength 4i -justify center -text {\n\
 It is advised to open and close the tray if you already did a dummy write,\
 in order to avoid an End Of Medium error. \
 If you just inserted a blank in the drive click on the \"Continue\" button.\
 Click on the \"Open/CLose\" button to open and close the tray.\n\
}");
    button = -1;
    TkPack (".tray.msg -side top -padx 1c -pady 1c");
    TkFrame (".tray.b");
    Button b = TkButton (".tray.b.cancel", "-text Cancel");
    b.command (setbutton, "1");
    b = TkButton (".tray.b.continue", "-text Continue");
    b.command (setbutton, "2");
    b = TkButton (".tray.b.open", "-text {Open/CLose}");
    b.command (setbutton, "3");
    TkPack (".tray.b.open .tray.b.continue .tray.b.cancel", "-side left -expand 1");
    TkPack (".tray.b", "-side top -padx 10 -pady 10 -fill both"); 
    
    TkWait (&button);
    switch (button) {
    case 1:
	r = 0;
	break;
    case 2:
	r = 1;
	break;
    case 3:	
	strcpy (d, device);
	fd = cam_open_device (d, O_RDWR);
	if (open_tray (fd)) {
	    r = 0;
	    CLOSE (fd);
	    break;
	}
	rezero_unit (fd);
	if (close_tray (fd)) {
	    CLOSE (fd);
	    r = 0;
	    break;
	}
	CLOSE (fd);
    }
    TkDestroy (".tray");
    return r;
}
static void
cue_edit (int argc, char **argv)
{
    int q;
    static int s[100];
    char x[20], *p;

    if (*TkWinfo ("exists", ".cue") == '1')
	return;

    for (q = 0; q < 100; q++) {
	sprintf (x, "info exist %d", q);
	if (*hlist.subwidget ("hlist", x) == '0')
	    break;
	sprintf (x, "info data %d", q);
	p = hlist.subwidget ("hlist", x);
	s[q] = atoi (p);
    }
    s[q] = 0;
    edit_cue_sheet (s);
}
static void
do_write (int argc, char **argv)
{
    char *p;
    char x[80];
    char name[1024], dir[1024];
    int q, type, size, file;
    cuesheet *c, *cuelist;
    FD fd;

    if (!disk_at_once) {
	p = cdtype.subwidget ("menubutton configure", "-text");
	q = 0;
	while (*p) {
	    if (*p++ == ' ')
		q++;
	    if (q == 4)
		break;
	}
	for (q = 0; q < 4; q++)
	    if (strcmp (p, cdtype_txt[q]) == 0) {
		cd_type = q;
		break;
	    }
	if (q == 4) {
	    Error ("bad CD type ???");
	    return;
	}
    } else {
	int s[100];
	for (q = 0; q < 100; q++) {
	    sprintf (x, "info exist %d", q);
	    if (*hlist.subwidget ("hlist", x) == '0')
		break;
	    sprintf (x, "info data %d", q);
	    p = hlist.subwidget ("hlist", x);
	    s[q] = atoi (p);
	}
	s[q] = 0;
	c = cuelist = check_cue_sheet (s);
	cd_type = 0;
    }
    set_params (device, speed, dummy, ops, cd_type, disk_at_once, cuelist);

    file = 0;
    for (q = 0; q < 100; q++) {
	sprintf (x, "info exist %d", q);
	if (*hlist.subwidget ("hlist", x) == '0')
	    break;
	sprintf (x, "item cget %d 0 -image", q);
	p = hlist.subwidget ("hlist", x);
	if (strcmp (p, "note_p") == 0)
	    type = T_AUDIO|T_PREEMP;
	else if (strcmp (p, "note") == 0)
	    type = T_AUDIO;
	else
	    type = T_DATA;
	sprintf (x, "item cget %d 1 -text", q);
	p = hlist.subwidget ("hlist", x);
	strcpy (name, p);
	//sprintf (x, "item cget %d 2 -text", q);
	//p = hlist.subwidget ("hlist", x);
	sprintf (x, "item cget %d 3 -text", q);
	p = hlist.subwidget ("hlist", x);
	strcpy (dir, p+1);
	*strrchr (dir, ')') = 0;
	sprintf (x, "info data %d", q);
	p = hlist.subwidget ("hlist", x);
	size = atoi (p);
	if (disk_at_once) {
	    int n = 2352*(c->pregap->m*60*75+c->pregap->s*75+c->pregap->f);
	    add_file (file++, 0, 0, n, type|T_PREGAP);
	    c->preemp = type & T_PREEMP ? 1 : 0; 
	    c = c->next;
	}
	add_file (file++, name, dir, size, type);
	p = 0;
    }
    if (!open_close_tray ())
	return;
    bx.subwidget ("ok", "configure -state disabled");
    if (disk_at_once)
	bx.subwidget ("cue", "configure -state disabled");
    start_info ();
    struct rtprio rtp = {RTP_PRIO_REALTIME, 5}, rtpold;
    rtprio (RTP_LOOKUP, 0, &rtpold);
    if (rtprio (RTP_SET, 0, &rtp) < 0)
	Info ("\nCan't change the process priority");
    else {
	w_active |= WRITE_ACTIVE;
	p = worm ();
	w_active &= ~WRITE_ACTIVE;
	if (p) {
	    Info ("\n\n", 2);
	    Info (p);
	} else 
	    Info ("No errors");
	rtprio (RTP_SET, 0, &rtpold);
    }
    end_info ();
    bx.subwidget ("ok", "configure -state normal");
    if (disk_at_once)
	bx.subwidget ("cue", "configure -state normal");

    strcpy (x, device);
    fd = cam_open_device (x, O_RDWR);
    prevent_removable (fd, 0);
    CLOSE (fd);
}
static void
swap (int i)
{
    // i <-> i+1
    char image[20], x1[30], x2[30], s[1280];
    char *p;
    // save image i
    sprintf (x1, "hlist item cget %d", i);
    strcpy (image, hlist.subwidget (x1, "0 -image"));
    // put image i+1 in i
    sprintf (x2, "hlist item cget %d", i+1);
    sprintf (s, "hlist item configure %d 0 -itemtype imagetext -image", i);
    hlist.subwidget (s, hlist.subwidget (x2, "0 -image"));
    // put image in i+1
    sprintf (s, "hlist item configure %d 0 -itemtype imagetext -image", i+1);
    hlist.subwidget (s, image); 

    // col1 
    p = strdup (hlist.subwidget (x1, "1 -text"));
    sprintf (s, "item create %d 1 -itemtype text -text {%s}", i,  
	     hlist.subwidget (x2, "1 -text"));
    hlist.subwidget ("hlist", s); 
    sprintf (s, "item create %d 1 -itemtype text -text {%s}", i+1, p);  
    hlist.subwidget ("hlist", s); 
    free (p);

    // col2 
    p = strdup (hlist.subwidget (x1, "2 -text"));
    sprintf (s, "item create %d 2 -itemtype text -text {%s}", i,  
	     hlist.subwidget (x2, "2 -text"));
    hlist.subwidget ("hlist", s); 
    sprintf (s, "item create %d 2 -itemtype text -text {%s}", i+1, p);  
    hlist.subwidget ("hlist", s); 
    free (p);

    // col3
    p = strdup (hlist.subwidget (x1, "1 -text"));
    sprintf (s, "item create %d 3 -itemtype text -text {%s}", i,  
	     hlist.subwidget (x2, "3 -text"));
    hlist.subwidget ("hlist", s); 
    sprintf (s, "item create %d 3 -itemtype text -text {%s}", i+1, p);  
    hlist.subwidget ("hlist", s); 
    free (p);

    // data
    sprintf (x1, "info data %d", i);
    sprintf (x2, "info data %d", i+1);
    strcpy (image, hlist.subwidget ("hlist", x1));
    sprintf (s, "entryconfigure %d -data %s", i, hlist.subwidget ("hlist", x2)); 
    hlist.subwidget ("hlist", s); 
    sprintf (s, "entryconfigure %d -data %s", i+1, image); 
    hlist.subwidget ("hlist", s); 
}
static void
check_types ()
{
    char *p, x[30];
    int q, type = 0;

    bx.subwidget ("ok", "configure -state disabled");
    if (disk_at_once)
	bx.subwidget ("cue", "configure -state disabled");
    if (!track)
	return;
    for (q = 0; q < 100; q++) {
	sprintf (x, "info exist %d", q);
	if (*hlist.subwidget ("hlist", x) == '0')
	    break;
	sprintf (x, "item cget %d 0 -image", q);
	p = hlist.subwidget ("hlist", x);
	if (*p == 'q')
	    return;
	if (*p == 'd')
	    type = 1;
    }
    bx.subwidget ("ok", "configure -state normal");
    if (disk_at_once)
	bx.subwidget ("cue", "configure -state normal");
    if (q < 100)
	bsel.configure ("-state normal");
    if (!disk_at_once)
	cdtype.subwidget ("menubutton configure -text", cdtype_txt[type]);
}
static void
set_bb (int argc, char **argv)
{
    char s[100];

    if (strcmp (*argv, "note") == 0) {
	sprintf (s, "item configure %d 0 -itemtype imagetext -image note", 
		 selected_item);
	hlist.subwidget ("hlist", s); 
	check_types ();
	return;
    }
    if (strcmp (*argv, "pre") == 0) {
	sprintf (s, "item configure %d 0 -itemtype imagetext -image note_p", 
		 selected_item);
	hlist.subwidget ("hlist", s); 
	check_types ();
	return;
    }
    if (strcmp (*argv, "data") == 0) {
	sprintf (s, "item configure %d 0 -itemtype imagetext -image data", 
		 selected_item);
	hlist.subwidget ("hlist", s); 
	check_types ();
	return;
    }
    if (strcmp (*argv, "del") == 0) {
	// move entries
	int i;
	for (i = selected_item+1; i < track; i++)
	    swap (i-1);
	track--;
	// update size
	sprintf (s, "info data %d", track);
	total_size -= atoi (hlist.subwidget ("hlist", s));
	sprintf (s, "header create 2 -itemtype text -text {  %3dM}", total_size >>20);
	hlist.subwidget ("hlist", s);
	
	sprintf (s, "delete entry %d", track);
	hlist.subwidget ("hlist", s); 
	hlist.subwidget ("hlist", "selection clear"); 
	bb.subwidget ("note", "configure -state disabled");
	bb.subwidget ("pre", "configure -state disabled");
	if (!disk_at_once)
	    bb.subwidget ("data", "configure -state disabled");
	bb.subwidget ("del", "configure -state disabled");
	bb.subwidget ("up", "configure -state disabled");
	bb.subwidget ("down", "configure -state disabled");
	if (!track) {
	    bx.subwidget ("ok", "configure -state disabled");
	    if (disk_at_once)
		bx.subwidget ("cue", "configure -state disabled");
	}
	check_types ();
	return;
    }
    if (strcmp (*argv, "up") == 0 && selected_item) {
	selected_item--;
	swap (selected_item);
	hlist.subwidget ("hlist", "selection clear"); 
	sprintf (s, "selection set %d", selected_item);
	hlist.subwidget ("hlist", s);
	sprintf (s, "see %d", selected_item);
	hlist.subwidget ("hlist", s);
	bb.subwidget ("up", "configure -state normal");
	bb.subwidget ("down", "configure -state normal");
	if (selected_item == 0)
	    bb.subwidget ("up", "configure -state disabled");
	return;
    }
    if ((strcmp (*argv, "down") == 0) && (selected_item < (track-1))) {
	swap (selected_item);
	selected_item++;
	hlist.subwidget ("hlist", "selection clear"); 
	sprintf (s, "selection set %d", selected_item);
	hlist.subwidget ("hlist", s);
	sprintf (s, "see %d", selected_item);
	hlist.subwidget ("hlist", s);
	bb.subwidget ("up", "configure -state normal");
	bb.subwidget ("down", "configure -state normal");
	if (selected_item == track-1)
	    bb.subwidget ("down", "configure -state disabled");
	return;
    }
}
static void
hlistcmd (int argc, char **argv)
{
    int n = atoi (argv[0]);
    bb.subwidget ("note", "configure -state normal");
    bb.subwidget ("pre", "configure -state normal");
    if (!disk_at_once)
	bb.subwidget ("data", "configure -state normal");
    bb.subwidget ("del", "configure -state normal");
    if (n)
	bb.subwidget ("up", "configure -state normal");
    if (n < track-1)
	bb.subwidget ("down", "configure -state normal");
    selected_item = n;
}

static void
filesel (int argc, char **argv)
{
    char *p;
    char *type;
    int size;
    char s[1200];
    struct stat st;
    FILE *f;

    if (*TkWinfo ("exists", ".cue") == '1')
	kill_cue ();
    if (**argv == '/') 
	strcpy (filename, *argv);
    else {
	strcpy (s, B.configure ("-directory"));
	p = s;
	while (*p) {
	    if (*p++ == ' ')
		size++;
	    if (size == 4)
		break;
	}
	strcpy (filename, p);
	strcat (filename, "/");
	strcat (filename, B.subwidget ("filelist subwidget listbox get", argv[0]));
    }
    f = fopen (filename, "r");
    if (!f) {
	sprintf (s, "can't open file\n(%s)", filename);
	Error (s);
	return;
    }
    p = strrchr (filename, '.');
    if (!p)
	p = "?";
    if (strcmp (p, ".audio") == 0) 
	type = "note";
    else if (strcmp (p, ".audio+") == 0) 
	type = "note_p";
    else if (!disk_at_once && strcmp (p, ".data") == 0)
	type = "data";
    else {
	if (disk_at_once || fseek (f, 32768, SEEK_SET) < 0)
	    type = "qm";
	else {
	    fread (s, 1, 10, f);
	    s[7] = 0;
	    if (strcmp (s, "\001CD001\001") == 0)
		type = "data";
	    else 
		type = "qm";
	}
    }
    fclose (f);
    if (stat (filename, &st) < 0) {
	Error ("can't stat file");
	error = 1;
	return;
    }
    size = st.st_size;
    p = strrchr (filename, '/');
    *p++ = 0;
    sprintf (s, "add %d -itemtype imagetext -image %s -data %d", track, type, size);
    hlist.subwidget ("hlist", s);
    sprintf (s, "item create %d 1 -itemtype text -text %s", track, p);
    hlist.subwidget ("hlist", s);
    if (size < 1024*1024) 
	sprintf (s, "item create %d 2 -itemtype text -text {  %3dK}", track, size >>10);
    else
	sprintf (s, "item create %d 2 -itemtype text -text {  %3dM}", track, size >>20);
    hlist.subwidget ("hlist", s);
    sprintf (s, "item create %d 3 -itemtype text -text (%s)", track, filename);
    hlist.subwidget ("hlist", s);
    sprintf (s, "see %d", track);
    hlist.subwidget ("hlist", s);
    *--p = '/';
    track++;
    total_size += size;
    sprintf (s, "header create 2 -itemtype text -text {  %3dM}", total_size >>20);
    hlist.subwidget ("hlist", s);
    if (track == 99)
	bsel.configure ("-state disabled");
    check_types ();
}
static void
browsecmd (int argc, char **argv)
{
    if (!disabled) 
	bsel.configure ("-state", "normal");
}
static void
mfilesel (int argc, char **argv)
{
    char *p, *s, *q, *a[2];
    if (*TkWinfo ("exists", ".cue") == '1')
	kill_cue ();
    p = s = strdup (B.subwidget ("filelist subwidget listbox", "curselection"));
    error = 0;
    while ((q = strtok (p, " \t"))) {
	p = 0;
	a[0] = q;
	a[1] = 0;
	filesel (1, a);
	if (error)
	    break;
    }
    free (s);
}
static void
dummy_toggle (int argc, char **argv)
{
    if (dummy) {
	dummy = 0;
	rb.deselect ();
    } else {
	dummy = 1;
	rb.select ();
    }
}
static void
set_button (int argc, char **argv)
{
    char s[200];
    int i;
    struct dev_info *inf;
    int idev = **argv - '0';
    FD fd;

    disabled = 1;
    track = 0;
    total_size = 0;
    bb.subwidget ("note", "configure -state disabled");
    bb.subwidget ("pre", "configure -state disabled");
    if (!disk_at_once)
	bb.subwidget ("data", "configure -state disabled");
    bb.subwidget ("del", "configure -state disabled");
    bb.subwidget ("up", "configure -state disabled");
    bb.subwidget ("down", "configure -state disabled");
    if (*hlist.subwidget ("hlist", "header exist 2") == '1')
	hlist.subwidget ("hlist", "header delete 2");
    bsel.configure ("-state", "disabled");
    B.subwidget ("filelist", "configure -state disabled");
    hlist.subwidget ("hlist", "delete all");
    sprintf(device, "/dev/r%s.ctl", dev[idev].name);
#ifndef REALLY_DUMMY
    fd = cam_open_device (device, O_RDWR);
    rezero_unit (fd);
    inf = inquiry (fd);
    if (!inf) {
	CLOSE (fd);
	hlist.subwidget ("hlist", "add 0 -itemtype text -text {INQUIRE command failed}");
	return;
    }
    sprintf (s, "-text {%s             %s             %s}", inf->vendor, inf->product, inf->revision);
#else
    strcpy (s, " -text {testing}");
#endif
    label.configure (s);
#ifndef REALLY_DUMMY
    i = test_unit_ready (fd);
    if (i < 0) {
	hlist.subwidget ("hlist", "add 0 -itemtype text -text {TEST UNIT READY command failed}");
	CLOSE (fd);
	return;
    }
    ops = worm_cap(inf->vendor, inf->product, inf->revision);
    CLOSE (fd);
#else
    ops= worm_cap("PHILIPS", "CDD2600", "1.07");
#endif
    if (!ops) {
	hlist.subwidget ("hlist", "add 0 -itemtype text -text {Unknown device}");
	return;
    }
    int *sp = ops->speeds ();
    speed = ops->default_speed ();
    for (i = 0; i < 3; i++)
	r[i].configure ("-state disabled");
    for (i = 0; sp[i]; i++) {
	int n ;
	for (n = 0; n < 3; n++)
	    if (speeds[n] == sp[i])
		break;
	r[n].configure ("-state normal");
	if (sp[i] == speed)
	    r[i].invoke ();
    }
    B.subwidget ("filelist", "configure -state normal");
    disabled = 0;
    strcpy (device, dev[idev].name);
}

void 
write_cd (int argc, char **argv)
{
    int i;
    char s[180], x[4];
    
    if (*TkWinfo ("exists", ".write") == '1')
	return;
    
    if (strcmp (argv[0], "DAO") == 0)
	disk_at_once = 1;
    else
	disk_at_once = 0;
    
    check_dev (dev);
#ifdef REALLY_DUMMY
    dev[2].there = 1;
#endif
    dummy = 0;
    TkToplevel (".write");
    bbox = tixButtonBox (".write.b -orientation horizontal");
    x[0] = 'b'; x[2] = 0;
    for (i = 0; dev[i].name; i++) {
	sprintf (s, "-text %s -state %s", 
		 dev[i].name, dev[i].there ? "normal" : "disabled");
	x[1] = i + '0';
	bbox.add (x, s);
	bbox.subwidget (x)->command (set_button, x+1);
    }

    TkPack (".write.b", "-side top -pady 2 -fill x -expand 1");

    label = TkLabel (".write.label -text { } -bd 2 -width 25 -height 1 -relief sunken");
    TkPack (".write.label", "-side top -pady 8 -fill x -expand 1");
    

    TkFrame (".write.f");
    B = tixExFileSelectBox2 (".write.f.sel", "-options {filelist.listbox.selectMode extended}");
    B.command (filesel);
    B.browsecmd (browsecmd);
    B.subwidget ("filelist", "configure -state disabled");
    bsel = TkButton (".write.f.bs", "-text {Select >>>} -state disabled");
    bsel.command (mfilesel);
    TkFrame (".write.f.f");
    hlist = tixScrolledHList (".write.f.f.l", "-relief flat -options {\n\
                    hlist.columns 4\n\
                    hlist.gap 0\n\
                    hlist.header  true\n\
                    hlist.selectMode single\n\
                    hlist.width 40 hlist.height 15\n\
             }");
    hlist.subwidget ("hlist")->browsecmd (hlistcmd); 
    bb = tixButtonBox (".write.f.f.b", "-relief flat -orientation horizontal -padx 0");
    bb.add ("note", "-state disabled -image note");
    bb.subwidget ("note")->command (set_bb, "note");
    bb.add ("pre", "-state disabled -image note_p");
    bb.subwidget ("pre")->command (set_bb, "pre");
    if (!disk_at_once) {
	bb.add ("data", "-state disabled  -image data");
	bb.subwidget ("data")->command (set_bb, "data");
    }
    bb.add ("del", "-state disabled -text remove");
    bb.subwidget ("del")->command (set_bb, "del");
    bb.add ("up", "-state disabled -text {move up}");
    bb.subwidget ("up")->command (set_bb, "up");
    bb.add ("down", "-state disabled -text {move down}");
    bb.subwidget ("down")->command (set_bb, "down");
    TkPack (".write.f.f.l .write.f.f.b", "-side top -fill x -expand 1");
    TkPack (".write.f.sel", "-side left -fill both -expand 1");
    TkPack (".write.f.bs", "-side left  -anchor c");
    TkPack (".write.f.f", "-side left -fill both -padx 8 -expand 1");

    TkPack (".write.f", "-side top");

    TkFrame (".write.sep", "-borderwidth 2 -height 6 -relief sunken");
    TkPack (".write.sep", "-side top -fill x");

    TkFrame (".write.rb");
    rb = TkRadiobutton (".write.rb.d", "-text {dummy write} -relief flat -value 0");
    rb.command (dummy_toggle);
    TkPack (".write.rb.d", "-side left -padx 20m");
    TkLabel (".write.rb.l", "-text {write speed:}");
    TkPack (".write.rb.l", "-side left -padx 4m -anchor w");
    speed = 0;
    for (i = 0; i < 3; i++) {
	sprintf (s, ".write.rb.b%d -text %dx -relief flat -value %d \
                   -state disabled", speeds[i], speeds[i], speeds[i]);
	r[i] = TkRadiobutton (s);
	r[i].variable (&speed);
	*strchr (s, ' ') = 0;
	TkPack (s, "-side left -padx 2m");
    }
    if (!disk_at_once) {
	cdtype = tixOptionMenu (".write.rb.y", "-label {CD type: } -options {label.anchor e}");
	for (i = 0; i < 4; i++) 
	    cdtype.add ("command", cdtype_txt[i]);
	TkPack (".write.rb.y", "-side left -padx 20m");
    }
	
    TkPack (".write.rb", "-side top -pady 15 -fill x");

    bx = tixButtonBox (".write.b2 -orientation horizontal");
    bx.add ("cancel", "-text Cancel");
    if (disk_at_once) {
	bx.add ("cue", "-text  {Edit cue sheet} -state disabled");
	bx.subwidget ("cue")->command (cue_edit);
    }
    bx.add ("ok", "-text  Write -state disabled");
    bx.subwidget ("ok")->command (do_write);
    bx.subwidget ("cancel")->command (ret);
    TkPack (".write.b2", "-side top -fill x");
}
