#include "snd.h"

#ifdef VMS
#define lstat stat
#endif
/* a bit of a grab-bag -- nominally file-related */

static char file_string[256];

static void color_file_selection_box(Widget w, snd_state *ss)
{
  Widget wtmp,ftmp;
  if (!(ss->using_schemes)) 	
    {
      map_over_children(w,set_main_color_of_widget,(void *)ss);
      XtVaSetValues(XmFileSelectionBoxGetChild(w,XmDIALOG_DIR_LIST),XmNbackground,(ss->sgx)->white,NULL);
      XtVaSetValues(XmFileSelectionBoxGetChild(w,XmDIALOG_LIST),XmNbackground,(ss->sgx)->white,NULL);
#ifndef VMS
#ifndef LESSTIF_VERSION
      XtVaSetValues(XtNameToWidget(w,"Apply"),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XtNameToWidget(w,"Cancel"),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XtNameToWidget(w,"Help"),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XtNameToWidget(w,"OK"),XmNarmColor,(ss->sgx)->text,NULL);
      wtmp = XtNameToWidget(w,"Text");
      ftmp = XtNameToWidget(w,"FilterText");
#else
      XtVaSetValues(XmFileSelectionBoxGetChild(w,XmDIALOG_APPLY_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XmFileSelectionBoxGetChild(w,XmDIALOG_CANCEL_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XmFileSelectionBoxGetChild(w,XmDIALOG_HELP_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XmFileSelectionBoxGetChild(w,XmDIALOG_OK_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      wtmp = XmFileSelectionBoxGetChild(w,XmDIALOG_TEXT);
      ftmp = XmFileSelectionBoxGetChild(w,XmDIALOG_FILTER_TEXT);	
#endif
#else
      XtVaSetValues(XmFileSelectionBoxGetChild(w,XmDIALOG_APPLY_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XmFileSelectionBoxGetChild(w,XmDIALOG_CANCEL_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XmFileSelectionBoxGetChild(w,XmDIALOG_HELP_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XmFileSelectionBoxGetChild(w,XmDIALOG_OK_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      wtmp = XmFileSelectionBoxGetChild(w,XmDIALOG_TEXT);
      ftmp = XmFileSelectionBoxGetChild(w,XmDIALOG_FILTER_TEXT);	
#endif
      XtAddCallback(wtmp,XmNfocusCallback,textfield_focus_Callback,ss);
      XtAddCallback(wtmp,XmNlosingFocusCallback,textfield_unfocus_Callback,ss);
      XtAddCallback(ftmp,XmNfocusCallback,textfield_focus_Callback,ss);
      XtAddCallback(ftmp,XmNlosingFocusCallback,textfield_unfocus_Callback,ss);
    }
}

static int is_directory(char *filename)
{
  struct stat statbuf;
  if (lstat(filename,&statbuf) >= 0) return(S_ISDIR(statbuf.st_mode));
  return(0);
}

#if 0
static int is_regular_file(char *filename)
{
  struct stat statbuf;
  if ((lstat(fullpath,&statbuf) >= 0) && (S_ISDIR(statbuf.st_mode) == 0))
    return((statbuf.st_mode & S_IFMT) == S_IFREG);
  return(0);
}
#endif

static void File_Open_OK_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_info *sp;
  snd_state *ss = (snd_state *)clientData;
  XmFileSelectionBoxCallbackStruct *cbs = (XmFileSelectionBoxCallbackStruct *) callData;
  char *fileName;
  XtUnmanageChild(w);
  XmStringGetLtoR (cbs->value,XmFONTLIST_DEFAULT_TAG,&fileName);
  /* this can be a directory name if the user clicked 'ok' when he meant 'cancel' */
  if (!(is_directory(fileName)))
    {
      sp = snd_open_file(fileName,ss);
      if (sp) select_channel(sp,0);           /* add_sound_window (snd-xsnd.c) will report reason for error, if any */
    }
  else
    {
      sprintf(file_string,snd_string_is_a_directory,fileName);
      snd_printf(ss,file_string);
    }
}

static void File_Open_Help_Callback (Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
       "File Open",
"If you click the 'Sound Files Only'\n\
button, only those files in the current\n\
directory that look vaguely like sound\n\
files will be displayed.\n\
");
}

static void File_Open_Cancel_Callback (Widget w,XtPointer clientData,XtPointer callData) 
{
  XtUnmanageChild (w);
}

static Widget open_dialog = NULL; 
static XmSearchProc default_search_proc;

static int string_compare(const void *ss1, const void *ss2)
{
  return(strcmp((*((char **)ss1)),(*((char **)ss2)))); /* sweet baby jesus... ain't C grand? */
}

static void just_sounds_help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
	   "Sound Files Only",
"If you click the 'Sound Files Only'\n\
button, only those files in the current\n\
directory that look vaguely like sound\n\
files will be displayed.  The decision\n\
is based on the file's extension.\n\
");
}

static char *fullpathname = NULL;
static int file_SB_list_needs_update = 0; /* browser list needs update (new 'filter' or whatever) */
static int new_file_written = 0;          /* sound file list needs update because we wrote a new file */

void alert_new_file(void) {new_file_written = 1;}

static void sound_file_search(Widget FSB_w, XmFileSelectionBoxCallbackStruct *callData)
{
  /* generate list of sound files, set XmNfileListItems, XmNfileListItemCount, XmNlistUpdated
   * the latter if new file list generated -- if no files, XmNfileListItems NULL, Count 0
   * can also set XmNdirSpec to full file spec of dir.  The callbackstruct is:
   *    typedef struct
   *    {
   *      int      reason;         Why called
   *      XEvent   * event;  
   *      XmString value;          current value of XmNdirSpec
   *      int      length;         number of bytes in value
   *      XmString mask;           current value of XmNdirMask
   *      int      mask_length;    number of bytes in mask
   *      XmString dir;            current base directory
   *      int      dir_length;     number of bytes in dir
   *      XmString pattern;        current search pattern
   *      int      pattern_length; number of bytes in pattern
   *    } XmFileSelectionBoxCallbackStruct;
   *
   * proc should stick to XmNfileTypeMask (type unsigned char): 
   *   XmFILE_REGULAR -- regular files
   *   XmFILE_DIRECTORY -- directories
   *   XmFILE_ANY_TYPE 
   *
   * the pattern (file name mask) only matters if the filter button is hit, 
   * it appears to be "*" until the filter is invoked.
   */
  char *pattern,*our_dir,*sp,*sn;
  static char *save_dir = NULL;
  static char *last_dir = NULL;
  static dir *sound_files,*current_files;
  static char *last_pattern = NULL;
  dir *cdp;
  XmFileSelectionBoxCallbackStruct *data = (XmFileSelectionBoxCallbackStruct *)callData;
  XmString *names = NULL;
  int i,filter_callback,need_update;

  XmStringGetLtoR (data->pattern,XmFONTLIST_DEFAULT_TAG,&pattern);
  XmStringGetLtoR (data->dir,XmFONTLIST_DEFAULT_TAG,&our_dir);

  if (!fullpathname) fullpathname = (char *)calloc(FILENAME_MAX,sizeof(char));
  filter_callback = (strcmp(pattern,"*") != 0);
  need_update = file_SB_list_needs_update;
  file_SB_list_needs_update = 0;
  if (!filter_callback)
    {
      if ((!last_dir) || (strcmp(our_dir,last_dir) != 0) || (new_file_written))
	{
	  if (current_files) current_files = free_dir(current_files);
	  if (last_dir) {free(last_dir); last_dir = NULL;}
	  last_dir = (char *)calloc(snd_strlen(our_dir)+1,sizeof(char));
	  strcpy(last_dir,our_dir);
	  strcpy(fullpathname,our_dir);
	  save_dir = (char *)(fullpathname+snd_strlen(our_dir));
	  sound_files = find_sound_files_in_dir(our_dir);
	  need_update = 1;
	}
      if (last_pattern)
	{
	  free(last_pattern);
	  last_pattern = NULL;
	}
      cdp = sound_files;
    }
  else 
    {
      if ((!last_pattern) || (strcmp(pattern,last_pattern) != 0) || (new_file_written))
	  {
	    if (last_pattern) {free(last_pattern); last_pattern = NULL;}
	    last_pattern = (char *)calloc(snd_strlen(pattern)+1,sizeof(char));
	    strcpy(last_pattern,pattern);
	    if (current_files)  current_files = free_dir(current_files);
	    if ((sound_files) && (sound_files->len > 0)) current_files = filter_sound_files(sound_files,pattern);
	    need_update = 1;
	  }
      cdp = current_files;
    }  
  new_file_written = 0;
  if (need_update)
    {
      if ((cdp) && (cdp->len > 0))
	{
	  qsort((void *)(cdp->files),cdp->len,sizeof(char *),string_compare);
	  names = (XmString *)calloc(cdp->len,sizeof(XmString));
	  for (i=0;i<cdp->len;i++) 
	    {
	      for (sp=save_dir,sn=cdp->files[i];((*sp)=(*sn)) != '\0';sp++,sn++) ;
	      names[i] = XmStringCreate(fullpathname,XmFONTLIST_DEFAULT_TAG);
	    }
	}
      else names=NULL;
      if (cdp) XtVaSetValues(FSB_w,XmNfileListItems,names,XmNfileListItemCount,cdp->len,XmNlistUpdated,TRUE,NULL);
      if (names)
	{
	  for (i=0;i<cdp->len;i++) if (names[i]) XmStringFree(names[i]);
	  free(names);
	}
    }
}

static void force_directory_reread(void)
{
  XmString dirmask;
  XtVaGetValues(open_dialog,XmNdirMask,&dirmask,NULL);
  XmFileSelectionDoSearch(open_dialog,dirmask);
  XmStringFree(dirmask);
}

static void just_sounds_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  XmToggleButtonCallbackStruct *cb = (XmToggleButtonCallbackStruct *)callData;
  if (cb->set)
    XtVaSetValues(open_dialog,XmNfileSearchProc,sound_file_search,NULL);
  else XtVaSetValues(open_dialog,XmNfileSearchProc,default_search_proc,NULL);
  file_SB_list_needs_update = 1;
  force_directory_reread();
}

static Widget just_sounds_button = NULL;
static int just_sounds_state = FALSE;

void toggle_just_sounds(int n)
{
  if (just_sounds_button)
    XmToggleButtonSetState(just_sounds_button,n,TRUE);
  just_sounds_state = n;
}


Boolean CreateOpenDialog(Widget w,XtPointer clientData)
{
  /* file selection dialog box with added "Just Sound Files" toggle button */
  Arg args[20];
  int n;
  XmString s1;
  snd_state *ss = (snd_state *)clientData;
  n=0;
  if (!open_dialog)
    {
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      if (just_sounds_state)
	{
	  XtSetArg(args[n],XmNfileSearchProc,sound_file_search); n++;
	}
      s1 = XmStringCreate(snd_string_open_p,XmFONTLIST_DEFAULT_TAG);
      XtSetArg(args[n],XmNselectionLabelString,s1); n++;
      open_dialog = XmCreateFileSelectionDialog(w,snd_string_File,args,n);
      XmStringFree(s1);
#ifdef LESSTIF_VERSION
      XtManageChild(open_dialog);
#endif
      just_sounds_button = XtVaCreateManagedWidget(snd_string_Sound_Files_Only,xmToggleButtonWidgetClass,open_dialog,
						   XmNset,just_sounds_state,
						   XmNalignment,XmALIGNMENT_BEGINNING,
						   NULL);
      color_file_selection_box(open_dialog,ss);
      if (!(ss->using_schemes)) 
	{
	  XtVaSetValues(just_sounds_button,XmNselectColor,(ss->sgx)->text,NULL);
#if 0
	  XtVaSetValues(XtNameToWidget(open_dialog,"DirList"),XmNbackground,(ss->sgx)->white,NULL);
	  XtVaSetValues(XtNameToWidget(open_dialog,"ItemsList"),XmNbackground,(ss->sgx)->white,NULL);
	  fprintf(stderr,"Dirlist: %s, List: %s ",
		  XtName(XmFileSelectionBoxGetChild(open_dialog,XmDIALOG_DIR_LIST)),
		  XtName(XmFileSelectionBoxGetChild(open_dialog,XmDIALOG_LIST)));
#endif
	  /* on both the SGI and in Linux, this reports the names "DirList" and "ItemsList" but they are not accesible via XtNameToWidget?!? */
	}
      XtVaGetValues(open_dialog,XmNfileSearchProc,&default_search_proc,NULL);
      XtAddCallback(open_dialog,XmNokCallback,File_Open_OK_Callback,clientData);
      XtAddCallback(open_dialog,XmNcancelCallback,File_Open_Cancel_Callback,clientData);
      XtAddCallback(open_dialog,XmNhelpCallback,File_Open_Help_Callback,clientData);
      XtAddCallback(just_sounds_button,XmNvalueChangedCallback,just_sounds_Callback,NULL);
      XtAddCallback(just_sounds_button,XmNhelpCallback,just_sounds_help_Callback,clientData);
    }
  return(TRUE);
}

void snd_open_file_dialog(Widget w, snd_state *ss)
{
  finish_keyboard_selection();
  if (!open_dialog) CreateOpenDialog(w,ss);
  if (new_file_written) 
    {
      force_directory_reread();
      new_file_written = 0;
    }
  if (!(XtIsManaged(open_dialog))) XtManageChild(open_dialog);
}


/* -------- save as dialog (file and edit menus) -------- */
/* 
 * changed 19-June-97 to simply save the current state under the new name and return
 * to the current state/file (different from emacs) -- this keeps mix console intact
 * across backups and so on, and seems more useful to me than switching to the new file.
 */

typedef struct {
  Widget dialog;
  Widget header_list; 
  Widget data_list;
  Widget text;
  Widget srtext;
  Widget comtext;
  snd_state *state;
  int type;
  int header_choice,data_choice;
} save_as_info;

static save_as_info *save_as_dialog = NULL;

#define FILE_SAVE_AS 1
#define EDIT_SAVE_AS 2

#define NUM_HEADER_TYPES 5
#define NUM_NEXT_FORMATS 8
#define NUM_IRCAM_FORMATS 5
#define NUM_WAVE_FORMATS 5
#define NUM_AIFF_FORMATS 6

static char *next_data_formats[NUM_NEXT_FORMATS] = {"short","mulaw",  "signed byte  ","float","long","alaw","24 bit int","double"};
static char *ircam_data_formats[NUM_IRCAM_FORMATS] = {"short","mulaw","float        ","long","alaw"};
static char *wave_data_formats[NUM_WAVE_FORMATS] = {"mulaw","alaw",   "unsigned byte","short","long"};
static char *aiff_data_formats[NUM_AIFF_FORMATS] = {"short","mulaw",  "signed byte  ","long","alaw","24 bit int"};

static int next_dfs[NUM_NEXT_FORMATS] = {snd_16_linear,snd_8_mulaw,snd_8_linear,snd_32_float,snd_32_linear,snd_8_alaw,snd_24_linear,snd_64_double};
static int ircam_dfs[NUM_IRCAM_FORMATS] = {snd_16_linear,snd_8_mulaw,snd_32_float,snd_32_linear,snd_8_alaw};
static int wave_dfs[NUM_WAVE_FORMATS] = {snd_8_mulaw,snd_8_alaw,snd_8_unsigned,snd_16_linear_little_endian,snd_32_linear_little_endian};
static int aiff_dfs[NUM_AIFF_FORMATS] = {snd_16_linear,snd_8_mulaw,snd_8_linear,snd_32_linear,snd_8_alaw,snd_24_linear};

#define NUM_DATA_FORMATS 17
static char *data_formats[NUM_DATA_FORMATS] = {
  "16 bit big-endian int",
  "8 bit mulaw",
  "8 bit signed int",
  "32 bit big-endian float",
  "32 bit big-endian int",
  "8 bit alaw",
  "8 bit unsigned int",
  "24 bit big-endian int",
  "64 bit big-endian double",
  "16 bit little-endian int",
  "32 bit little-endian int",
  "32 bit little-endian float",
  "64 bit little-endian double",
  "16 bit big-endian unsigned int",
  "16 bit little-endian unsigned int",
  "24 bit little-endian int",
  "32 bit vax float"};

/* must parallel sndlib.h definitions */

static int get_header_type_from_position(int position)
{
  switch (position)
    {
    case 1: return(NeXT_sound_file); break;
    case 2: return(AIFF_sound_file); break;
    case 3: return(RIFF_sound_file); break;
    case 4: return(IRCAM_sound_file); break;
    case 5: return(raw_sound_file); break;
    }
  return(raw_sound_file);
}

static int get_data_format_from_header_and_position(int header, int position)
{
  switch (header)
    {
    case NeXT_sound_file: return(next_dfs[position-1]); break;
    case AIFF_sound_file: return(aiff_dfs[position-1]); break;
    case RIFF_sound_file: return(wave_dfs[position-1]); break;
    case IRCAM_sound_file: return(ircam_dfs[position-1]); break;
    case raw_sound_file: return(position); break;
    }
  return(position);
}

typedef struct {
  snd_info *sp;
  char *fullname;
  int edits;
} same_name_info;

static int check_for_same_name(snd_info *sp1, void *ur_info)
{
  int i;
  chan_info *cp;
  same_name_info *info = (same_name_info *)ur_info;
  if ((sp1) && (strcmp(sp1->fullname,info->fullname) == 0))
    {
      
      info->sp = sp1;
      for (i=0;i<sp1->nchans;i++) 
	{
	  cp = sp1->chans[i];
	  if (info->edits < cp->edit_ctr) info->edits = cp->edit_ctr;
	}
      return(1); /* stop immediately and deal with this one */
    }
  return(0);
}

void read_file_data_choices(Widget stext, Widget ctext, Widget hlist, Widget dlist, int *srate, int *chans, int *type, int *format)
{
  char *str;
  int n;
#if HAVE_SELECTED_POSITION_COUNT
  unsigned int *ns;
#else
  int *ns;
#endif
  if (stext) {str = XmTextGetString(stext); if (str) {sscanf(str,"%d",&n); (*srate) = n;}}
  if (ctext) {str = XmTextGetString(ctext); if (str) {sscanf(str,"%d",&n); (*chans) = n;}}
#if HAVE_SELECTED_POSITION_COUNT
  if (hlist)
    {
      XtVaGetValues(hlist,XmNselectedPositionCount,&n,NULL);
      if (n>0)
	{
	  XtVaGetValues(hlist,XmNselectedPositions,&ns,NULL);
	  (*type) = get_header_type_from_position(ns[0]);
	}
    }
  if (dlist)
    {
      XtVaGetValues(dlist,XmNselectedPositionCount,&n,NULL);
      if (n>0)
	{
	  XtVaGetValues(dlist,XmNselectedPositions,&ns,NULL);
	  (*format) = get_data_format_from_header_and_position((*type),ns[0]);
	}
    }
#else
  if (hlist)
    {
      XmListGetSelectedPos(hlist,&ns,&n);
      if (n>0)
	(*type) = get_header_type_from_position(ns[0]);
    }
  if (dlist)
    {
      XmListGetSelectedPos(dlist,&ns,&n);
      if (n>0)
	(*format) = get_data_format_from_header_and_position((*type),ns[0]);
    }
#endif
}

static void save_as_ok_callback(Widget w,XtPointer clientData,XtPointer callData)
{ /* called from both save as options */
  save_as_info *fd = (save_as_info *)clientData;
  XmAnyCallbackStruct *cb = (XmAnyCallbackStruct *)callData;
  Widget text;
  char *str,*str1,*fullname,*comment;
  same_name_info *collision = NULL;
  snd_info *sp;
  int result,fil;
  snd_state *ss;
  int type,format,srate;
  ss = fd->state;
  if (cb->event == ((ss->sgx)->text_activate_event)) return; /* <cr> in one of text fields */
  str = XmTextGetString(fd->srtext);
  sscanf(str,"%d",&srate);
  comment = copy_string(XmTextGetString(fd->comtext));
  type = get_header_type_from_position(fd->header_choice);
  format = get_data_format_from_header_and_position(type,fd->data_choice);
  text = fd->text;
  str = XmTextGetString(text);
  sp = any_selected_sound(ss);
  if ((str) && (*str))
    {
      if (sp) clear_minibuffer(sp);
      alert_new_file();
      /* now check in-core files -- need to close any of same name -- if edited what to do? */
      str1 = copy_string(str);
      fullname = copy_string(complete_filename(str1));
      free(str1);
      if (ask_before_overwrite(ss))
	{
	  fil = open(fullname,O_RDONLY,0);
	  if (fil != -1) 
	    {
	      close(fil);
	      sprintf(file_string,snd_string_exists_overwrite,str);
	      if (!(snd_yes_or_no_p(ss,file_string))) {free(fullname); return;}
	    }
	}
      collision = (same_name_info *)calloc(1,sizeof(same_name_info));
      collision->fullname = fullname;
      collision->edits = 0;
      collision->sp = NULL;
      map_over_sounds(ss,check_for_same_name,(void *)collision);
      if (collision->sp)
	{
	  /* if no edits, we'll just close, overwrite, reopen */
	  /* if edits, we need to ask luser what to do */
	  /* we don't need to check for overwrites at this point */
	  if (collision->edits > 0)
	    {
	      sprintf(file_string,snd_string_has_unsaved_edits,str,str);
	      if (!(snd_yes_or_no_p(ss,file_string))) {free(fullname); free(collision); return;}
	    }
	  snd_close_file(collision->sp,ss);
	}
      if (fd->type == FILE_SAVE_AS)
	{
	  result = save_edits_2(sp,str,type,format,srate,comment); /* save_edits_2 called only here (and in clm_save_as, snd-clm.c) */
	  XmTextSetString(text,NULL);
	  if (result != snd_no_error)
	    {
	      sprintf(file_string,"%s: %s (%s)",sp->fullname,strerror(errno),snd_error_name(result));
	      report_in_minibuffer(sp,file_string);
	    }
	}
      else /* EDIT_SAVE_AS */
	{
	  save_selection(ss,str,type,format,srate,comment);
	}
      if (collision->sp) snd_open_file(fullname,ss);
      free(fullname);
      free(collision);
    }
  else if (sp) report_in_minibuffer(sp,snd_string_not_saved_no_name_given);
  XtUnmanageChild(fd->dialog);
} 

static void save_as_help_callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
	   "Save As",
"You can save the current state of a file or region\n\
under a different file name using the Save\n\
As option.  The output header type, data format,\n\
and sampling rate can also be set.  The data formats\n\
are big-endian where relevant except for 'wave'\n\
output.  If a file by the chosen name already exists\n\
it is silently overwritten, unless that file is\n\
already open in Snd and has edits.  In that case,\n\
you'll be asked what to do.  If you want to be warned\n\
whenever a file is about to be overwritten by this\n\
option, set the resource overwriteCheck to 1.\n\
");
}

static void save_as_cancel_callback(Widget w,XtPointer clientData,XtPointer callData)
{ 
  save_as_info *fd = (save_as_info *)clientData;
  XtUnmanageChild(fd->dialog);
} 

void load_header_and_data_lists(Widget hlist, Widget dlist, int type, int format, int *hpos, int *dpos)
{
  int dformats = 0,header_pos = 0,data_pos = 0,i;
  char **fl = NULL;
  XmString *strs;
  switch (type)
    {
    case AIFF_sound_file: 
      dformats = NUM_AIFF_FORMATS; 
      fl = aiff_data_formats; 
      header_pos = 2; 
      data_pos = 1;
      for (i=0;i<NUM_AIFF_FORMATS;i++) if (format == aiff_dfs[i]) {data_pos = i+1; break;}
      break;
    case RIFF_sound_file: 
      dformats = NUM_WAVE_FORMATS; 
      fl = wave_data_formats; 
      header_pos = 3;
      data_pos = 4;
      for (i=0;i<NUM_WAVE_FORMATS;i++) if (format == wave_dfs[i]) {data_pos = i+1; break;}
      break;
    case IRCAM_sound_file: 
      dformats =NUM_IRCAM_FORMATS; 
      fl = ircam_data_formats; 
      header_pos = 4; 
      data_pos = 1;
      for (i=0;i<NUM_IRCAM_FORMATS;i++) if (format == ircam_dfs[i]) {data_pos = i+1; break;}
      break;
    case NeXT_sound_file:
      dformats = NUM_NEXT_FORMATS; 
      fl = next_data_formats; 
      header_pos = 1; 
      data_pos = 1;
      for (i=0;i<NUM_NEXT_FORMATS;i++) if (format == next_dfs[i]) {data_pos = i+1; break;}
      break;
    case raw_sound_file:
      dformats = NUM_DATA_FORMATS; 
      fl = data_formats; 
      header_pos = 5; 
      data_pos = format;
      break;
    }
  XmListSelectPos(hlist,header_pos,FALSE);
  strs = (XmString *)calloc(dformats,sizeof(XmString)); 
  for (i=0;i<dformats;i++) strs[i] = XmStringCreate(fl[i],XmFONTLIST_DEFAULT_TAG);
  XtVaSetValues(dlist,XmNitems,strs,XmNitemCount,dformats,NULL);
  for (i=0;i<dformats;i++) XmStringFree(strs[i]);
  free(strs);
  XmListSelectPos(dlist,data_pos,FALSE);
  (*hpos) = header_pos;
  (*dpos) = data_pos;
}

static void Save_as_Header_type_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  /* set fdw->header_choice, if needed reload data_formats window */
  int pos;
  file_info *hdr;
  XmListCallbackStruct *cbs = (XmListCallbackStruct *)callData;
  save_as_info *fdw = (save_as_info *)clientData;
  pos = cbs->item_position;
  if (fdw->header_choice != pos)
    {
      hdr = (file_info *)calloc(1,sizeof(file_info));
      hdr->comment = NULL;
      switch (pos)
	{
	case 1: hdr->type = NeXT_sound_file; hdr->format = snd_16_linear; break;
	case 2: hdr->type = AIFF_sound_file; hdr->format = snd_16_linear; break;
	case 3: hdr->type = RIFF_sound_file; hdr->format = snd_16_linear_little_endian; break;
	case 4: hdr->type = IRCAM_sound_file; hdr->format = snd_16_linear; break;
	case 5: hdr->type = raw_sound_file; hdr->format = snd_16_linear; break;
	}
      load_header_and_data_lists(fdw->header_list,fdw->data_list,hdr->type,hdr->format,&(fdw->header_choice),&(fdw->data_choice));
      free(hdr);
    }
}

static void Save_as_Data_format_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  XmListCallbackStruct *cbs = (XmListCallbackStruct *)callData;
  save_as_info *fdw = (save_as_info *)clientData;
  fdw->data_choice = cbs->item_position;
}

static void file_header_help_callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
	   "File Header Type",
"This list shows the output header types that\n\
Snd is willing to write.\n\
");
}

static void file_data_help_callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
	   "File Data Format",
"This list shows the data format choices\n\
available with the currently selected header\n\
choice.  'Short' means 16-bit two's complement\n\
integer.  'Mulaw' and 'Alaw' are common 8-bit\n\
compression schemes. 'Long' is 32-bit integer.\n\
'Float' is 32-bit float.  In each case, the\n\
decision as to byte order ('endianess') depends\n\
on the header type.\n\
");
}

static void file_data_location_help_callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
	   "File Data Location",
"If you know the data location (in bytes)\n\
you can set it in this field.\n\
");
}

static void file_srate_help_callback(Widget w,XtPointer clientData,XtPointer callData)
{
  snd_help((snd_state *)clientData,
	   "File Srate",
"This field sets the nominal file sampling\n\
rate.\n\
");
} 

static void file_chans_help_callback(Widget w,XtPointer clientData,XtPointer callData)
{
  snd_help((snd_state *)clientData,
	   "File Srate",
"This field sets the number of channels\n\
in the output file.\n\
");
} 

static void file_comment_label_help_callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss = (snd_state *)clientData;
  snd_help(ss,
	   "Output File Comment",
"This optional field provides any comments\n\
that you want saved in the output file's\n\
header.\n\
");	   
}

Widget *sndCreateFileDataForm(snd_state *ss, Widget parent, char *name, Arg *args, int n, int with_chan, int with_comment, int header_type, int data_format, int with_loc)
{
  Widget *arr;
  XmString *strs;
  int dformats = 0;
  char **formats = NULL;
  int *dfs = NULL;
  int i,header_pos= 0,data_pos = 0;
  switch (header_type)
    {
    case NeXT_sound_file: dformats = NUM_NEXT_FORMATS; formats = next_data_formats; dfs = next_dfs; header_pos = 1; break;
    case AIFF_sound_file: dformats = NUM_AIFF_FORMATS; formats = aiff_data_formats; header_pos = 2; dfs = aiff_dfs; break;
    case RIFF_sound_file: dformats = NUM_WAVE_FORMATS; formats = wave_data_formats; header_pos = 3; dfs = wave_dfs; break;
    case IRCAM_sound_file: dformats = NUM_IRCAM_FORMATS; formats = ircam_data_formats; header_pos = 4; dfs = ircam_dfs; break;
    case raw_sound_file: dformats = NUM_DATA_FORMATS; formats = data_formats; header_pos = 5; dfs = NULL; data_pos = data_format; break;
    }
  if (header_type != raw_sound_file)
    {
      data_pos = 1;
      for (i=0;i<dformats;i++) if (data_format == dfs[i]) {data_pos = i+1; break;}
    }

  arr = (Widget *)calloc(FILE_DATA_WIDGETS,sizeof(Widget));

  if (with_comment)
    {
      n=0;
      arr[fdata_out_form] = XtCreateManagedWidget(name,xmFormWidgetClass,parent,args,n);
    }
  else arr[fdata_out_form] = parent;

  n=0;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  if (with_comment)
    {
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
    }
  else
    {
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
    }
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  arr[fdata_form] = XtCreateManagedWidget(name,xmFormWidgetClass,arr[fdata_out_form],args,n);

  n=0;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNorientation,XmVERTICAL); n++;
  XtSetArg(args[n],XmNwidth,5); n++;
  XtSetArg(args[n],XmNseparatorType,XmNO_LINE); n++;
  arr[fdata_sep1] = XtCreateManagedWidget("sep1",xmSeparatorWidgetClass,arr[fdata_form],args,n);

  n=0;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,arr[fdata_sep1]); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  arr[fdata_hlab] = XtCreateManagedWidget(snd_string_header,xmLabelWidgetClass,arr[fdata_form],args,n);
  XtAddCallback(arr[fdata_hlab],XmNhelpCallback,file_header_help_callback,ss);

  n=0;
#ifdef LESSTIF_VERSION
  if (!(ss->using_schemes)) n = background_white_color(args,n,ss);
#endif
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,arr[fdata_hlab]); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,arr[fdata_sep1]); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_OPPOSITE_WIDGET); n++;
  XtSetArg(args[n],XmNrightWidget,arr[fdata_hlab]); n++;
  XtSetArg(args[n],XmNlistMarginWidth,1); n++;
  /* what is selected depends on current type */
  strs = (XmString *)calloc(NUM_HEADER_TYPES,sizeof(XmString)); 
  strs[0] = XmStringCreate("Sun ",XmFONTLIST_DEFAULT_TAG);
  strs[1] = XmStringCreate("AIFF ",XmFONTLIST_DEFAULT_TAG);
  strs[2] = XmStringCreate("Wave ",XmFONTLIST_DEFAULT_TAG);
  strs[3] = XmStringCreate("IRCAM",XmFONTLIST_DEFAULT_TAG);
  strs[4] = XmStringCreate("raw",XmFONTLIST_DEFAULT_TAG);
  XtSetArg(args[n],XmNitems,strs); n++;
  XtSetArg(args[n],XmNitemCount,NUM_HEADER_TYPES); n++;
  XtSetArg(args[n],XmNvisibleItemCount,NUM_HEADER_TYPES); n++;
  arr[fdata_hlist] = XmCreateScrolledList(arr[fdata_form],"header type",args,n);
  XtAddCallback(arr[fdata_hlist],XmNhelpCallback,file_header_help_callback,ss);
  XtManageChild(arr[fdata_hlist]);
  for (i=0;i<NUM_HEADER_TYPES;i++) XmStringFree(strs[i]);
  free(strs);

  n=0;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,arr[fdata_hlab]); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNorientation,XmVERTICAL); n++;
  XtSetArg(args[n],XmNwidth,15); n++;
  XtSetArg(args[n],XmNseparatorType,XmNO_LINE); n++;
  arr[fdata_sep2] = XtCreateManagedWidget("sep2",xmSeparatorWidgetClass,arr[fdata_form],args,n);

  n=0;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,arr[fdata_sep2]); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  arr[fdata_dlab] = XtCreateManagedWidget(snd_string_data,xmLabelWidgetClass,arr[fdata_form],args,n);
  XtAddCallback(arr[fdata_dlab],XmNhelpCallback,file_data_help_callback,ss);

  n=0;
#ifdef LESSTIF_VERSION
  if (!(ss->using_schemes)) n = background_white_color(args,n,ss);
#endif
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,arr[fdata_dlab]); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,arr[fdata_sep2]); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  arr[fdata_dlist] = XmCreateScrolledList(arr[fdata_form],"data format",args,n);
  /* what is displayed and selected depends on current type */
  XtAddCallback(arr[fdata_dlist],XmNhelpCallback,file_data_help_callback,ss);

  XmListSelectPos(arr[fdata_hlist],header_pos,FALSE);
  strs = (XmString *)calloc(dformats,sizeof(XmString)); 
  for (i=0;i<dformats;i++) strs[i] = XmStringCreate(formats[i],XmFONTLIST_DEFAULT_TAG);
  XtVaSetValues(arr[fdata_dlist],XmNitems,strs,XmNitemCount,dformats,NULL);
  for (i=0;i<dformats;i++) XmStringFree(strs[i]);
  free(strs);
  XmListSelectPos(arr[fdata_dlist],data_pos,FALSE);
  XtManageChild(arr[fdata_dlist]);

  n=0;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
#ifndef LESSTIF_VERSION
  XtSetArg(args[n],XmNleftWidget,arr[fdata_dlist]); n++;
#else
  XtSetArg(args[n],XmNleftWidget,XtParent(arr[fdata_dlist])); n++;
#endif
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNorientation,XmVERTICAL); n++;
  XtSetArg(args[n],XmNwidth,15); n++;
  XtSetArg(args[n],XmNseparatorType,XmNO_LINE); n++;
  arr[fdata_sep3] = XtCreateManagedWidget("sep3",xmSeparatorWidgetClass,arr[fdata_form],args,n);

  n=0;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,arr[fdata_sep3]); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  arr[fdata_slab] = XtCreateManagedWidget(snd_string_srate_p,xmLabelWidgetClass,arr[fdata_form],args,n);
  XtAddCallback(arr[fdata_slab],XmNhelpCallback,file_srate_help_callback,ss);

  n=0;
  XtSetArg(args[n],XmNcolumns,6); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,arr[fdata_slab]); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,arr[fdata_sep3]); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNalignment,XmALIGNMENT_BEGINNING); n++;	
  arr[fdata_stext] = sndCreateTextFieldWidget(ss,"text",arr[fdata_form],args,n,NOT_ACTIVATABLE);
  XtAddCallback(arr[fdata_stext],XmNhelpCallback,file_srate_help_callback,ss);

  if (with_chan)
    {
      n=0;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNtopWidget,arr[fdata_stext]); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNleftWidget,arr[fdata_sep3]); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
      arr[fdata_clab] = XtCreateManagedWidget(snd_string_chans_p,xmLabelWidgetClass,arr[fdata_form],args,n);
      XtAddCallback(arr[fdata_clab],XmNhelpCallback,file_chans_help_callback,ss);

      n=0;
      XtSetArg(args[n],XmNcolumns,6); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNtopWidget,arr[fdata_clab]); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNleftWidget,arr[fdata_sep3]); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNalignment,XmALIGNMENT_BEGINNING); n++;	
      arr[fdata_ctext] = sndCreateTextFieldWidget(ss,"text",arr[fdata_form],args,n,NOT_ACTIVATABLE);
      XtAddCallback(arr[fdata_ctext],XmNhelpCallback,file_chans_help_callback,ss);

      if (with_loc)
	{
	  n=0;
	  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
	  XtSetArg(args[n],XmNtopWidget,arr[fdata_ctext]); n++;
	  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
	  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
	  XtSetArg(args[n],XmNleftWidget,arr[fdata_sep3]); n++;
	  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
	  arr[fdata_loclab] = XtCreateManagedWidget(snd_string_location_p,xmLabelWidgetClass,arr[fdata_form],args,n);
	  XtAddCallback(arr[fdata_loclab],XmNhelpCallback,file_data_location_help_callback,ss);

	  n=0;
	  XtSetArg(args[n],XmNcolumns,6); n++;
	  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
	  XtSetArg(args[n],XmNtopWidget,arr[fdata_loclab]); n++;
	  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
	  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
	  XtSetArg(args[n],XmNleftWidget,arr[fdata_sep3]); n++;
	  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
	  XtSetArg(args[n],XmNalignment,XmALIGNMENT_BEGINNING); n++;	
	  arr[fdata_loctext] = sndCreateTextFieldWidget(ss,"text",arr[fdata_form],args,n,NOT_ACTIVATABLE);
	  XtAddCallback(arr[fdata_loctext],XmNhelpCallback,file_data_location_help_callback,ss);
	}
    }
  if (with_comment)
    {
      n=0;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNtopWidget,arr[fdata_form]); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
      XtSetArg(args[n],XmNheight,5); n++;
      XtSetArg(args[n],XmNseparatorType,XmNO_LINE); n++;
      arr[fdata_sep4] = XtCreateManagedWidget("sep4",xmSeparatorWidgetClass,arr[fdata_out_form],args,n);

      n=0;
      /* if (!(ss->using_schemes)) n = background_main_color(args,n,ss);       */
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNtopWidget,arr[fdata_sep4]); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
      arr[fdata_comment_label] = XtCreateManagedWidget(snd_string_comment_p,xmLabelWidgetClass,arr[fdata_out_form],args,n);
      XtAddCallback(arr[fdata_comment_label],XmNhelpCallback,file_comment_label_help_callback,ss);

      n=0;
      /* if (!(ss->using_schemes)) n = background_main_color(args,n,ss); */
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_OPPOSITE_WIDGET); n++;
      XtSetArg(args[n],XmNtopWidget,arr[fdata_comment_label]); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNleftWidget,arr[fdata_comment_label]); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrows,2); n++;
#if HAVE_OSS
      XtSetArg(args[n],XmNcolumns,40); n++;
      /* this pushes the right boundary over a ways -- otherwise the button holder box takes up all available space */
#endif
      arr[fdata_comment_text] = sndCreateTextWidget(ss,"comment",arr[fdata_out_form],args,n);

      XtAddCallback(arr[fdata_comment_text],XmNhelpCallback,file_comment_label_help_callback,ss);
    }

  return(arr);
}

static void make_save_as_dialog(Widget w,save_as_info *fdw, char *name, file_info *hdr)
{
  Arg args[32];
  int n;
  Widget *wids;
  snd_state *ss;
  XmString xmstr1,xmstr2,s1;
  ss=fdw->state;

  n=0;
  if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
  s1 = XmStringCreate(snd_string_save_as_p,XmFONTLIST_DEFAULT_TAG);
  XtSetArg(args[n],XmNselectionLabelString,s1); n++;
  xmstr1=XmStringCreateLocalized(snd_string_Save);
  XtSetArg(args[n],XmNokLabelString,xmstr1); n++;
  sprintf(file_string,snd_string_saving,name);
  xmstr2 = XmStringCreateLocalized(file_string);
  XtSetArg(args[n],XmNdialogTitle,xmstr2); n++;
#if RESIZE_DIALOG
  XtSetArg(args[n],XmNresizePolicy,XmRESIZE_GROW); n++;
  XtSetArg(args[n],XmNnoResize,FALSE); n++;
#endif
  XtSetArg(args[n],XmNautoUnmanage,FALSE); n++;
  XtSetArg(args[n],XmNchildPlacement,XmPLACE_ABOVE_SELECTION); n++;
  XtSetArg(args[n],XmNallowOverlap,FALSE); n++;
  fdw->dialog = XmCreateFileSelectionDialog(w,"save-as",args,n);
  XtManageChild(fdw->dialog);
  XmStringFree(s1);
  XmStringFree(xmstr1);
  XmStringFree(xmstr2);
#ifndef LESSTIF_VERSION
  fdw->text = XtNameToWidget(fdw->dialog,"Text");
#else
  fdw->text = XmFileSelectionBoxGetChild(fdw->dialog,XmDIALOG_TEXT);
#endif

  XtAddCallback(fdw->dialog,XmNhelpCallback,save_as_help_callback,ss);
  XtAddCallback(fdw->dialog,XmNcancelCallback,save_as_cancel_callback,fdw);
  XtAddCallback(fdw->dialog,XmNokCallback,save_as_ok_callback,fdw);

  n=0;
#ifdef LESSTIF_VERSION
  XtSetArg(args[n],XmNheight,100); n++;
#endif
  wids = sndCreateFileDataForm(ss,fdw->dialog,"data-form",args,n,FALSE,TRUE,NeXT_sound_file,snd_16_linear,FALSE);
  fdw->header_list = wids[fdata_hlist];
  fdw->comtext = wids[fdata_comment_text];
  XtAddCallback(fdw->header_list,XmNbrowseSelectionCallback,Save_as_Header_type_Callback,fdw);
  fdw->data_list = wids[fdata_dlist];
  XtAddCallback(fdw->data_list,XmNbrowseSelectionCallback,Save_as_Data_format_Callback,fdw);
  fdw->srtext = wids[fdata_stext];
  sprintf(file_string,"%d",(hdr) ? hdr->srate : region_srate(0));
  XmTextSetString(fdw->srtext,file_string);
  load_header_and_data_lists(fdw->header_list,fdw->data_list,
			     (hdr) ? hdr->type : NeXT_sound_file,(hdr) ? hdr->format : snd_16_linear,
			     &(fdw->header_choice),&(fdw->data_choice));
  color_file_selection_box(fdw->dialog,ss);
  if (!(ss->using_schemes))	
    {
      XtVaSetValues(fdw->data_list,XmNbackground,(ss->sgx)->white,NULL);
      XtVaSetValues(fdw->header_list,XmNbackground,(ss->sgx)->white,NULL);
    }
}

void snd_save_as_dialog(Widget w, snd_state *ss)
{
  /* save old as new, close old, open new */
  snd_info *sp;
  XmString xmstr2;
  file_info *hdr;
  finish_keyboard_selection();
  sp = any_selected_sound(ss);
  if (!save_as_dialog)
    {
      save_as_dialog = (save_as_info *)calloc(1,sizeof(save_as_info));
      save_as_dialog->state = ss;
      make_save_as_dialog(w,save_as_dialog,sp->shortname,sp->hdr);
    }
  else
    {
      sprintf(file_string,snd_string_saving,sp->shortname);
      xmstr2 = XmStringCreateLocalized(file_string);
      XtVaSetValues(save_as_dialog->dialog,XmNtitleString,xmstr2,NULL);
      XmStringFree(xmstr2);
      hdr = sp->hdr;
      load_header_and_data_lists(save_as_dialog->header_list,save_as_dialog->data_list,
				 hdr->type,hdr->format,
				 &(save_as_dialog->header_choice),&(save_as_dialog->data_choice));
    }
  save_as_dialog->type = FILE_SAVE_AS;
  if (!XtIsManaged(save_as_dialog->dialog)) XtManageChild(save_as_dialog->dialog);
}


/* -------- files browser -------- */
/*
 * the region and file browsers share much widgetry -- they are supposed to look the same
 */

typedef struct {
  Widget rw,nm,pl,sv;
  int pos;
  snd_state *ss;
} regrow;

typedef struct {
  Widget ww;
  Widget list;
  Widget plw;
  Widget svw;
  Widget dbline;
} ww_info;

typedef struct {
  Widget dialog;
  Widget curlst;
  Widget prevlst;
  Widget curww; /* work area of scrolled window */
  Widget prevww;
  int selected_file;
  snd_state *state;
} fb_info;

static fb_info *fbd = NULL;

#define MAX_FILE_LIST_SIZE 256
static char *curnames[MAX_FILE_LIST_SIZE];
static char *prevnames[MAX_FILE_LIST_SIZE];
static char *prevfullnames[MAX_FILE_LIST_SIZE];
static int a_big_star[MAX_FILE_LIST_SIZE];
static int curfile_end = 0;
static int prevfile_end = -1;
static int max_curfile_end = 0;
static int max_prevfile_end = -1;

static void make_curfiles_list (snd_state *ss);
static void make_prevfiles_list (snd_state *ss);  /* these are needed as local forward declarations */

static regrow *cur_name_row[MAX_FILE_LIST_SIZE];
static regrow *prev_name_row[MAX_FILE_LIST_SIZE];

static ww_info *make_title_row(snd_state *ss, Widget formw, char *first_str, char *second_str, char *main_str, int pad)
{
  int n;
  Arg args[32];
  Widget plw,svw,rlw,sep1;
  ww_info *wwi;

  wwi = (ww_info *)calloc(1,sizeof(ww_info));
  
  n=0;
  if (!(ss->using_schemes)) n = background_high_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNalignment,XmALIGNMENT_CENTER); n++;	
  rlw = XtCreateManagedWidget(main_str,xmLabelWidgetClass,formw,args,n);
      
  n=0;
  if (!(ss->using_schemes)) n = background_white_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,rlw); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
  XtSetArg(args[n],XmNseparatorType,XmDOUBLE_LINE); n++;
  sep1 = XtCreateManagedWidget("sep",xmSeparatorWidgetClass,formw,args,n);
  wwi->dbline = sep1;

  n=0;
  if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
  if (pad == 2)
    {
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_POSITION); n++;
      XtSetArg(args[n],XmNleftPosition,5); n++;
    }
  else
    {
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
    }
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,sep1); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNfontList,button_FONT(ss)); n++;
  svw = XtCreateManagedWidget(first_str,xmLabelWidgetClass,formw,args,n);
  make_button_label(svw,first_str);
  wwi->svw = svw;

  n=0;
  if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,svw); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,sep1); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNfontList,button_FONT(ss)); n++;
  plw = XtCreateManagedWidget(second_str,xmLabelWidgetClass,formw,args,n);
  make_button_label(plw,second_str);
  wwi->plw = plw;
  
  n=0;
  if (pad == 2) /* pad on left */
    {
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_POSITION); n++;
      XtSetArg(args[n],XmNleftPosition,5); n++;
    }
  else
    {
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
    }
  if (pad == 1) /* pad on right */
    {
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_POSITION); n++;
      XtSetArg(args[n],XmNrightPosition,95); n++;
    }
  else
    {
      if (pad == 2)
	{
	  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
	}
      else
	{
	  XtSetArg(args[n],XmNrightAttachment,XmATTACH_POSITION); n++;
	  XtSetArg(args[n],XmNrightPosition,70); n++;
	}
    }
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,svw); n++;
  if (pad == 0)
    {
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_POSITION); n++;
      XtSetArg(args[n],XmNbottomPosition,40); n++;
    }
  else
    {
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
    }
  XtSetArg(args[n],XmNscrollingPolicy,XmAUTOMATIC); n++;
  XtSetArg(args[n],XmNscrollBarDisplayPolicy,XmSTATIC); n++;
  wwi->list = XmCreateScrolledWindow(formw,"reglist",args,n);

  n=0;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
  wwi->ww = XtCreateManagedWidget("ww",xmFormWidgetClass,wwi->list,args,n);
  XtVaSetValues(wwi->list,XmNworkWindow,wwi->ww,NULL);
  
  return(wwi);
}

static regrow *make_regrow(snd_state *ss, Widget ww, Widget last_row, 
			   XtCallbackProc first_callback, XtCallbackProc second_callback, XtCallbackProc third_callback)
{
  int n;
  Arg args[32];
  regrow *r;
  XmString s1;
  s1 = XmStringCreateLtoR("",XmFONTLIST_DEFAULT_TAG);
  r = (regrow *)calloc(1,sizeof(regrow));

  n=0;
  if (!(ss->using_schemes)) n = background_high_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,(last_row) ? XmATTACH_WIDGET : XmATTACH_FORM); n++;
  if (last_row) {XtSetArg(args[n],XmNtopWidget,last_row); n++;}
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
#if defined(LINUX) || defined(SCO5) || defined(UW2) || defined(SUN)
  XtSetArg(args[n],XmNheight,17); n++; 
#else
  XtSetArg(args[n],XmNheight,20); n++; 
#endif
  r->rw = XtCreateWidget("rw",xmFormWidgetClass,ww,args,n);

  n=0;
  if (!(ss->using_schemes)) n = background_high_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
  if (!(ss->using_schemes)) {XtSetArg(args[n],XmNselectColor,(ss->sgx)->text); n++;}
  XtSetArg(args[n],XmNlabelString,s1); n++;
  XtSetArg(args[n],XmNvalueChangedCallback,make_callback_list(first_callback,(XtPointer)r)); n++;
#if NEED_TOGGLE_SIZE
  XtSetArg(args[n],XmNindicatorSize,TOGGLE_SIZE); n++;
#endif
  r->sv = XtCreateManagedWidget("sv",xmToggleButtonWidgetClass,r->rw,args,n);
#if OVERRIDE_TOGGLE
  override_toggle_translation(r->sv);
#endif

  n=0;
  if (!(ss->using_schemes)) n = background_high_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,r->sv); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
  if (!(ss->using_schemes)) {XtSetArg(args[n],XmNselectColor,(ss->sgx)->text); n++;}
  XtSetArg(args[n],XmNlabelString,s1); n++;
  XtSetArg(args[n],XmNvalueChangedCallback,make_callback_list(second_callback,(XtPointer)r)); n++;
#if NEED_TOGGLE_SIZE
  XtSetArg(args[n],XmNindicatorSize,TOGGLE_SIZE); n++;
#endif
  r->pl = XtCreateManagedWidget("pl",xmToggleButtonWidgetClass,r->rw,args,n);
#if OVERRIDE_TOGGLE
  override_toggle_translation(r->pl);
#endif

  n=0;
  if (!(ss->using_schemes)) n = background_high_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,r->pl); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNshadowThickness,0); n++;
  XtSetArg(args[n],XmNhighlightThickness,0); n++;
  XtSetArg(args[n],XmNalignment,XmALIGNMENT_BEGINNING); n++;
  XtSetArg(args[n],XmNfillOnArm,FALSE); n++;
  XtSetArg(args[n],XmNfontList,bold_button_FONT(ss)); n++;
  XtSetArg(args[n],XmNrecomputeSize,FALSE); n++;
  XtSetArg(args[n],XmNwidth,300); n++;
  XtSetArg(args[n],XmNactivateCallback,make_callback_list(third_callback,(XtPointer)r)); n++;
  r->nm = XtCreateManagedWidget("nm",xmPushButtonWidgetClass,r->rw,args,n);
  XmStringFree(s1);
  return(r);
}

static void make_row_name(regrow *r, char *name, int big_star)
{
  if (big_star)
    {
      sprintf(file_string,"%s*",name);
      make_bold_button_label(r->nm,file_string);
    }
  else make_bold_button_label(r->nm,name);
}

static void fill_in_row(regrow *r, char *name, int big_star)
{
  make_row_name(r,name,big_star);
  XmToggleButtonSetState(r->sv,FALSE,FALSE);
  XmToggleButtonSetState(r->pl,FALSE,FALSE);
  XtManageChild(r->rw);
}

static int find_curfile_regrow(char *shortname)
{
  int i;
  for (i=0;i<curfile_end;i++)
    {
      if (strcmp(curnames[i],shortname) == 0) return(i);
    }
  return(-1);
}

static int find_prevfile_regrow(char *shortname)
{
  int i;
  for (i=0;i<=prevfile_end;i++)
    {
      if (strcmp(prevnames[i],shortname) == 0) return(i);
    }
  return(-1);
}

void save_prevlist(FILE *fd)
{
  int i;
  for (i=0;i<=prevfile_end;i++)
    fprintf(fd,"(preload-file \"%s\")\n",prevfullnames[i]);
}

static void file_uncurlist(char *filename)
{
  int i,j;
  i = find_curfile_regrow(filename);
  if (i != -1)
    {
      free(curnames[i]);
      curnames[i] = NULL;
      for (j=i;j<curfile_end-1;j++) 
	{
	  curnames[j] = curnames[j+1];
	  a_big_star[j] = a_big_star[j+1];
	}
      curfile_end--;
    }
}

static void file_unprevlist(char *filename)
{
  int i,j;
  i = find_prevfile_regrow(filename);
  if (i != -1)
    {
      free(prevnames[i]);
      prevnames[i] = NULL;
      free(prevfullnames[i]);
      prevfullnames[i] = NULL;
      for (j=i;j<prevfile_end;j++) {prevnames[j] = prevnames[j+1]; prevfullnames[j] = prevfullnames[j+1];}
      prevfile_end--;
    }
}

static void clear_prevlist(void)
{
  int i;
  for (i=0;i<=prevfile_end;i++)
    {
      if (prevnames[i]) 
	{
	  free(prevnames[i]); prevnames[i]=NULL;
	  free(prevfullnames[i]); prevfullnames[i]=NULL;
	}
    }
  prevfile_end = -1;
}

static void update_prevlist(void)
{
  /* here we need the file's full name */
  int i,j,fd;
  for (i=0;i<=prevfile_end;i++)
    {
      if (prevnames[i]) 
	{
	  fd = open(prevfullnames[i],O_RDONLY,0);
	  if (fd == -1) 
	    {
	      free(prevnames[i]); prevnames[i]=NULL;
	      free(prevfullnames[i]); prevfullnames[i]=NULL;
	    }
	  else close(fd);
	}
    }
  for (i=0,j=0;i<=prevfile_end;i++)
    {
      if (prevnames[i])
	{
	  if (i != j) {prevnames[j] = prevnames[i]; prevfullnames[j] = prevfullnames[i];}
	  j++;
	}
    }
  prevfile_end = j-1;
}

static void file_curlist(char *filename)
{
  if (curfile_end == MAX_FILE_LIST_SIZE) 
    {
      curfile_end--;
      if (curnames[curfile_end]) free(curnames[curfile_end]);
    }
  curnames[curfile_end] = copy_string(filename);
  a_big_star[curfile_end] = 0;
  curfile_end++;
  if (max_curfile_end < curfile_end) max_curfile_end = curfile_end;
}

static void file_prevlist(char *filename, char *fullname)
{
  int k;
  prevfile_end++;
  if (prevfile_end == MAX_FILE_LIST_SIZE) 
    {
      prevfile_end--;
      if (prevnames[prevfile_end]) {free(prevnames[prevfile_end]); free(prevfullnames[prevfile_end]);}
    }
  for (k=prevfile_end;k>0;k--) {prevnames[k]=prevnames[k-1]; prevfullnames[k]=prevfullnames[k-1];}
  prevnames[0] = copy_string(filename);
  prevfullnames[0] = copy_string(fullname);
  if (max_prevfile_end < prevfile_end) max_prevfile_end = prevfile_end;
}

static void add_files_to_prevlist(snd_state *ss, char **shortnames, char **longnames, int len)
{
  int i,k;
  for (i=0,k=prevfile_end+1;((i<len) && (k < MAX_FILE_LIST_SIZE));i++,k++)
    {
      prevnames[k] = copy_string(shortnames[i]);
      prevfullnames[k] = copy_string(longnames[i]);
    }
  prevfile_end = k-1;
  max_prevfile_end = prevfile_end;
  if ((fbd) && (XtIsManaged(fbd->dialog))) make_prevfiles_list(ss);
}

void add_directory_to_prevlist(snd_state *ss, char *dirname)
{
  dir *sound_files = NULL;
  char *fullpathname = NULL;
  char **fullnames;
  int i,end;
  sound_files = find_sound_files_in_dir(dirname);
  if ((sound_files) && (sound_files->len > 0))
    {
      fullpathname = (char *)calloc(FILENAME_MAX,sizeof(char));
      strcpy(fullpathname,dirname);
#ifndef VMS
      if (dirname[strlen(dirname)-1] != '/') strcat(fullpathname,"/");
#endif
      end = strlen(fullpathname);
      fullnames = (char **)calloc(sound_files->len,sizeof(char *));
      for (i=0;i<sound_files->len;i++) 
	{
	  fullnames[i] = copy_string(strcat(fullpathname,sound_files->files[i]));
	  fullpathname[end] = '\0';
	}
      add_files_to_prevlist(ss,sound_files->files,fullnames,sound_files->len);
      for (i=0;i<sound_files->len;i++) {free(fullnames[i]); fullnames[i]=NULL;}
      free(fullnames);
      free_dir(sound_files);
      free(fullpathname);
    }
  if ((fbd) && (XtIsManaged(fbd->dialog))) make_prevfiles_list(ss);
}

void remember_me(snd_state *ss, char *shortname,char *fullname)
{
  file_prevlist(shortname,fullname);
  file_uncurlist(shortname);
  if ((fbd) && (XtIsManaged(fbd->dialog)))
    {
      make_curfiles_list(ss);
      make_prevfiles_list(ss);
    }
}

void greet_me(snd_state *ss, char *shortname)
{
  file_curlist(shortname);
  file_unprevlist(shortname);
  if ((fbd) && (XtIsManaged(fbd->dialog)))
    {
      make_curfiles_list(ss);
      make_prevfiles_list(ss);
    }
}

void make_a_big_star_outa_me(snd_state *ss, char *shortname, int big_star)
{
  int i;
  regrow *r;
  i = find_curfile_regrow(shortname);
  if ((i != -1) && (a_big_star[i] != big_star))
    {
      if ((fbd) && (XtIsManaged(fbd->dialog)))
	{
	  r = cur_name_row[i];
	  make_row_name(r,curnames[i],big_star);
	}
      a_big_star[i] = big_star;
    }
}

static void View_Files_Help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
       "File Browser",
"This dialog provides two lists, one of\n\
the currently active files in Snd, and\n\
the other of previously active files.\n\
The save button saves current edits, the\n\
play button plays the file, and the\n\
unlist button removes a file from the\n\
previous files list.  If a file is deleted\n\
while Snd is running, it will not notice\n\
its deletion automatically.  To update the\n\
previous files list to account for such\n\
actions, click on the 'update' button.\n\
To reopen a previous file, simply select\n\
that file from the previous files list.\n\
To remove all files from the previous files\n\
list, click the 'clear' button.  To select\n\
one of the current files in Snd, opening\n\
its associated windows, select it in the\n\
current files list.\n\
\n\
To preload all the sound files in a directory\n\
into the previous files list, use either the\n\
command (snd-preload-directory dir), as in\n\
\n\
  M-x (snd-preload-directory \"/usr/people/bil/hdr)\"\n\
\n\
or give the directory name to the -p flag\n\
when starting Snd:\n\
\n\
  snd -p . oboe.snd\n\
\n\
To preload a specific file,\n\
  (snd-preload-file <name>)\n\
");	   
}

static void View_Files_Dismiss_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  fb_info *fd = (fb_info *)clientData;
  XtUnmanageChild(fd->dialog);
}

static void View_Files_Clear_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  /* clear previous files list and associated widget list */
  clear_prevlist();
  make_prevfiles_list((snd_state *)clientData);
}

static void View_Files_Update_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  /* run through previous files list looking for any that have been deleted behind our back */
  update_prevlist();
  make_prevfiles_list((snd_state *)clientData);
}

static void View_CurFiles_Save_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  regrow *r = (regrow *)clientData;
  snd_info *sp;
  sp = find_sound(r->ss,curnames[r->pos]);
  if (sp)
    {
      finish_keyboard_selection();
      save_edits(sp,NULL);
    }
  XmToggleButtonSetState(r->sv,FALSE,FALSE);
}

void set_file_browser_play_button(char *name, int state)
{
  int i,list;
  regrow *r;
  list = 0;
  if ((fbd) && (XtIsManaged(fbd->dialog)))
    {
      i=find_curfile_regrow(name); 
      if (i != -1) list = 1; else i=find_prevfile_regrow(name);
      if (i != -1)
	{
	  if (list) r=cur_name_row[i]; else r=prev_name_row[i];
	  XmToggleButtonSetState(r->pl,state,FALSE);
	}
    }
}

static void View_CurFiles_Play_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  regrow *r = (regrow *)clientData;
  XmToggleButtonCallbackStruct *cb = (XmToggleButtonCallbackStruct *)callData;
  snd_info *sp;
  sp = find_sound(r->ss,curnames[r->pos]);
  if (sp)
    {
      if (sp->playing) stop_playing(sp->playing);
      if (cb->set)
	{
	  start_playing(sp,0);
	  set_play_button(sp,1);
	}
      else set_play_button(sp,0);
    }
}

static void curfile_unhighlight(snd_state *ss)
{
  regrow *r;
  if ((fbd) && (XtIsManaged(fbd->dialog)))
    {
      if ((!(ss->using_schemes)) && (fbd->selected_file != -1))
	{
	  r = cur_name_row[fbd->selected_file];
	  XtVaSetValues(r->rw,XmNbackground,(ss->sgx)->high,NULL);
	  XtVaSetValues(r->nm,XmNbackground,(ss->sgx)->high,NULL);
	  fbd->selected_file = -1;
	}
    }
}

static void curfile_highlight(snd_state *ss, int i)
{
  regrow *r;
  if ((fbd) && (XtIsManaged(fbd->dialog)))
    {
      if (!(ss->using_schemes)) 
	{
	  if (fbd->selected_file != -1) curfile_unhighlight(ss);
	  r = cur_name_row[i];
	  XtVaSetValues(r->rw,XmNbackground,(ss->sgx)->zoom,NULL);
	  XtVaSetValues(r->nm,XmNbackground,(ss->sgx)->zoom,NULL);
	  fbd->selected_file = i;
	}
    }
}

static void View_CurFiles_Select_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  regrow *r = (regrow *)clientData;
  snd_info *sp,*osp;
  snd_state *ss;
  ss = r->ss;
  curfile_highlight(ss,r->pos);
  sp = find_sound(ss,curnames[r->pos]);
  osp = any_selected_sound(ss);
  if (sp != osp)
    {
      select_channel(sp,0);
      normalize_sound(ss,sp,osp,sp->chans[0]);
      goto_graph(sp->chans[0]);
    }
}

static void View_PrevFiles_Unlist_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  regrow *r = (regrow *)clientData;
  file_unprevlist(prevnames[r->pos]);
  make_prevfiles_list(r->ss);
}

static void View_PrevFiles_Play_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  /* open and play -- close at end or when button off toggled */
  regrow *r = (regrow *)clientData;
  XmToggleButtonCallbackStruct *cb = (XmToggleButtonCallbackStruct *)callData;
  static snd_info *play_sp;
  int play;
  play = cb->set;
  if (play)
    {
      if (play_sp)
	{
	  if (play_sp->playing) {XmToggleButtonSetState(w,FALSE,FALSE); return;} /* can't play two of these at once */
	  if (strcmp(play_sp->shortname,prevnames[r->pos]) != 0)
	    {
	      free_snd_info(play_sp);
	      play_sp = NULL;
	    }
	}
      if (!play_sp) play_sp = make_sound_readable(r->ss,prevfullnames[r->pos],FALSE);
      if (play_sp)
	{
	  play_sp->shortname = prevnames[r->pos];
	  play_sp->fullname = NULL;
	  start_playing(play_sp,0);
	}
      else
	{ /* can't find or setup file */
	  XmToggleButtonSetState(w,FALSE,FALSE);
	  play = 0;
	}
    }
  else
    { /* play toggled off */
      if ((play_sp) && (play_sp->playing)) {stop_playing(play_sp->playing); play_sp->playing = 0;}
    }
}

static void View_PrevFiles_Select_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  /* open and set as selected */
  regrow *r = (regrow *)clientData;
  snd_info *sp;
  sp = snd_open_file(prevfullnames[r->pos],r->ss);
  if (sp) select_channel(sp,0); 
}

void highlight_selected_sound(snd_state *ss)
{
  snd_info *sp;
  int i;
  sp = selected_sound(ss);
  if (sp)
    {
      i = find_curfile_regrow(sp->shortname);
      if (i != -1) curfile_highlight(ss,i); else curfile_unhighlight(ss);
    }
  else curfile_unhighlight(ss);
}

static void make_curfiles_list (snd_state *ss)
{
  int i;
  Widget last_row = NULL;
  regrow *r;
  for (i=0;i<curfile_end;i++)
    {
      if (!(r = cur_name_row[i]))
	{
	  r = make_regrow(ss,fbd->curww,last_row,View_CurFiles_Save_Callback,View_CurFiles_Play_Callback,View_CurFiles_Select_Callback);
	  cur_name_row[i] = r;
	  r->pos = i;
	  r->ss = ss;
	}
      fill_in_row(r,curnames[i],a_big_star[i]);
      last_row = r->rw;
    }
  for (i=curfile_end;i<max_curfile_end;i++)
    {
      if ((r = cur_name_row[i]))
	{
	  if (XtIsManaged(r->rw)) XtUnmanageChild(r->rw);
	}
    }
  max_curfile_end = curfile_end;
  highlight_selected_sound(ss);
  XtManageChild(fbd->curlst);
}

static void make_prevfiles_list (snd_state *ss)
{
  int i;
  Widget last_row = NULL;
  regrow *r;
  for (i=0;i<=prevfile_end;i++)
    {
      if (!((r = prev_name_row[i])))
	{
	  r = make_regrow(ss,fbd->prevww,last_row,View_PrevFiles_Unlist_Callback,View_PrevFiles_Play_Callback,View_PrevFiles_Select_Callback);
	  prev_name_row[i] = r;
	  r->pos = i;
	  r->ss = ss;
	}
      fill_in_row(r,prevnames[i],0);
      last_row = r->rw;
    }
  for (i=prevfile_end+1;i<=max_prevfile_end;i++)
    {
      if ((r = prev_name_row[i]))
	{
	  if (XtIsManaged(r->rw)) XtUnmanageChild(r->rw);
	}
    }
  max_prevfile_end = prevfile_end;
  XtManageChild(fbd->prevlst);
}


/* play open unlist for prevfile, play save select for curfile, preload process for prevfile (snd-clm) */

void View_Files_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  /* fire up a dialog window with a list of currently open files, 
   * currently selected file also selected in list --
   * if user selects one (browse mode), so does Snd (via normalize_sound etc)
   * use snd_info label as is (short-form with '*' etc) -- snd_widget(sp,W_snd_name)
   * secondary list of previously edited files (if still in existence) --
   * click here re-opens the file.  (The overall form is similar to the regions browser).
   * The previous files list requires that we keep such a list as we go along, on the
   * off-chance this browser will be fired up.  (Such files may be subsequently moved or deleted).
   */
  snd_state *ss = (snd_state *)clientData;
  int n,i;
  Arg args[20];
  ww_info *wwl;
  regrow *r;
  XmString xdismiss,xhelp,xclear,titlestr;
  Widget mainform,curform,prevform,updateB,sep;
  if (!fbd)
    {
      fbd = (fb_info *)calloc(1,sizeof(fb_info));
      fbd->state = ss;
      fbd->selected_file = -1;
      for (i=0;i<MAX_FILE_LIST_SIZE;i++) /* paranoia */
	{ 
	  cur_name_row[i] = NULL;
	  prev_name_row[i] = NULL;
	}
      xdismiss = XmStringCreate(snd_string_Dismiss,XmFONTLIST_DEFAULT_TAG);
      xhelp = XmStringCreate(snd_string_Help,XmFONTLIST_DEFAULT_TAG);
      xclear = XmStringCreate(snd_string_Clear,XmFONTLIST_DEFAULT_TAG);
      titlestr = XmStringCreateLocalized(snd_string_Files);
      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNcancelLabelString,xclear); n++;
      XtSetArg(args[n],XmNhelpLabelString,xhelp); n++;
      XtSetArg(args[n],XmNokLabelString,xdismiss); n++;
      XtSetArg(args[n],XmNautoUnmanage,FALSE); n++;
      XtSetArg(args[n],XmNdialogTitle,titlestr); n++;
#if RESIZE_DIALOG
      XtSetArg(args[n],XmNresizePolicy,XmRESIZE_GROW); n++;
      XtSetArg(args[n],XmNnoResize,FALSE); n++;
#endif
#if UNSET_TRANSIENT
      XtSetArg(args[n],XmNtransient,FALSE); n++;
#endif

      fbd->dialog = XmCreateTemplateDialog(main_SHELL(ss),snd_string_File_Browser,args,n);
#if UNSET_TRANSIENT
      add_dialog(ss,fbd->dialog);
#endif

      XtAddCallback(fbd->dialog,XmNcancelCallback,View_Files_Clear_Callback,ss);
      XtAddCallback(fbd->dialog,XmNhelpCallback,View_Files_Help_Callback,ss);
      XtAddCallback(fbd->dialog,XmNokCallback,View_Files_Dismiss_Callback,fbd);
      XmStringFree(xhelp);
      XmStringFree(xdismiss);
      XmStringFree(xclear);
      XmStringFree(titlestr);

      if (!(ss->using_schemes))
	{
	  XtVaSetValues(XmMessageBoxGetChild(fbd->dialog,XmDIALOG_OK_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	  XtVaSetValues(XmMessageBoxGetChild(fbd->dialog,XmDIALOG_CANCEL_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	  XtVaSetValues(XmMessageBoxGetChild(fbd->dialog,XmDIALOG_HELP_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	}

      n=0;
      if (!(ss->using_schemes)) 
	{
	  n = background_main_color(args,n,ss);
	  XtSetArg(args[n],XmNarmColor,(ss->sgx)->text); n++;
	}
      updateB = XtCreateManagedWidget(snd_string_Update,xmPushButtonWidgetClass,fbd->dialog,args,n);
      XtAddCallback(updateB,XmNactivateCallback,View_Files_Update_Callback,ss);

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNbottomWidget,XmMessageBoxGetChild(fbd->dialog,XmDIALOG_SEPARATOR)); n++;
      mainform = XtCreateManagedWidget("formd",xmFormWidgetClass,fbd->dialog,args,n);

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_POSITION); n++;
      XtSetArg(args[n],XmNrightPosition,49); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
      curform = XtCreateManagedWidget("curform",xmFormWidgetClass,mainform,args,n);

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNleftWidget,curform); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNorientation,XmVERTICAL); n++;
      XtSetArg(args[n],XmNseparatorType,XmSHADOW_ETCHED_IN); n++;
      sep = XtCreateManagedWidget("sep",xmSeparatorWidgetClass,mainform,args,n);

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNleftWidget,sep); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
      prevform = XtCreateManagedWidget("prevform",xmFormWidgetClass,mainform,args,n);


      /* current files section: save play current files | files */
      wwl = make_title_row(ss,curform,snd_string_save,snd_string_play,snd_string_current_files,1);
      fbd->curww = wwl->ww;
      fbd->curlst = wwl->list;
      if (!(ss->using_schemes)) map_over_children(fbd->curlst,set_main_color_of_widget,(void *)clientData);
      free(wwl); 
      wwl=NULL;
      if (curfile_end == 0) /* apparently we need at least one row to get Motif to allocate the outer widgets correctly */
	{
	  r = make_regrow(ss,fbd->curww,NULL,View_CurFiles_Save_Callback,View_CurFiles_Play_Callback,View_CurFiles_Select_Callback);
	  cur_name_row[0] = r;
	  r->pos = 0;
	  r->ss = ss;
	}

      /* previous files section: unlist play previous files | files */
      wwl = make_title_row(ss,prevform,snd_string_unlist,snd_string_play,snd_string_previous_files,2);
      fbd->prevww = wwl->ww;
      fbd->prevlst = wwl->list;
      if (!(ss->using_schemes)) map_over_children(fbd->prevlst,set_main_color_of_widget,(void *)clientData);
      free(wwl); 
      wwl=NULL;
      if (prevfile_end == -1)
	{
	  r = make_regrow(ss,fbd->prevww,NULL,View_PrevFiles_Unlist_Callback,View_PrevFiles_Play_Callback,View_PrevFiles_Select_Callback);
	  prev_name_row[0] = r;
	  r->pos = 0;
	  r->ss = ss;
	}
    }
  else raise_dialog(fbd->dialog);
  make_curfiles_list(ss);
  make_prevfiles_list(ss);
  if (!XtIsManaged(fbd->dialog)) XtManageChild(fbd->dialog);
  highlight_selected_sound(ss);
}


/* -------- edit find -------- */

typedef struct {
  int size; 
  int window; 
  float beta; 
  Widget dialog;
  Widget list; 
  Widget scale; 
  Widget cancelB;
  snd_state *state;
  int type;
} dialog_info;

static void edit_find_cancel_callback(Widget w,XtPointer clientData,XtPointer callData)
{ /* "Done" */
  dialog_info *fd = (dialog_info *)clientData;
  snd_state *ss;
  ss = fd->state;
  if (ss->checking_explicitly)
    ss->stopped_explicitly = 1;
  else XtUnmanageChild(fd->dialog);
} 

static void edit_find_help_callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
	   "Global Find",
"This search travels through all the current channels\n\
in parallel until a match is found.  The expression\n\
uses a C-like syntax.  y>.1, for example, searches\n\
until a sample is found greater than .1.\n\
");
} 

static void edit_find_ok_callback(Widget w,XtPointer clientData,XtPointer callData)
{ /* "Find" is the label here */
  dialog_info *fd = (dialog_info *)clientData;
  Widget text;
  char *str;
  XmString s1;
  snd_state *ss;
  text = fd->list;
  ss = fd->state;
  str = XmTextGetString(text);
  if ((str) && (*str))
    {
      if (ss->search_tree) free_sop(ss->search_tree);
      if (ss->search_expr) free(ss->search_expr);
      ss->search_expr = str;
      ss->search_tree = sop_parse(str);
      if (!ss->search_tree)
	{
	  sprintf(file_string,"%s: %s",str,sop_parse_error_name());
	  make_button_label(fd->scale,file_string);
	}
      else 
	{
	  sprintf(file_string,snd_string_find_s,str);
	  make_button_label(fd->scale,file_string);
	}
      XmTextSetString(text,NULL);
    }
  if (ss->search_tree) 
    {
      s1 = XmStringCreate(snd_string_Stop,XmFONTLIST_DEFAULT_TAG);
      XtVaSetValues(fd->cancelB,XmNlabelString,s1,NULL);
      XmStringFree(s1);
      str = global_search(ss);
      s1 = XmStringCreate(snd_string_Dismiss,XmFONTLIST_DEFAULT_TAG);
      XtVaSetValues(fd->cancelB,XmNlabelString,s1,NULL);
      XmStringFree(s1);
      if ((str) && (*str)) make_button_label(fd->scale,str);
    }
} 

static void make_edit_find_dialog(Widget w,dialog_info *fdw)
{
  Arg args[20];
  int n;
  Widget df,dl,rc,ds;
  XmString xmstr1,xmstr2,titlestr;
  snd_state *ss;
  ss = fdw->state;

  n=0;
  if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
  xmstr1=XmStringCreateLocalized(snd_string_Find);
  xmstr2=XmStringCreateLocalized(snd_string_Dismiss);
  titlestr=XmStringCreateLocalized(snd_string_Find);
  XtSetArg(args[n],XmNokLabelString,xmstr1); n++;
  XtSetArg(args[n],XmNcancelLabelString,xmstr2); n++;
  XtSetArg(args[n],XmNautoUnmanage,FALSE); n++;
  XtSetArg(args[n],XmNdialogTitle,titlestr); n++;
#if RESIZE_DIALOG
  XtSetArg(args[n],XmNresizePolicy,XmRESIZE_GROW); n++;
  XtSetArg(args[n],XmNnoResize,FALSE); n++;
#endif
#if UNSET_TRANSIENT
  XtSetArg(args[n],XmNtransient,FALSE); n++;
#endif
  fdw->dialog = XmCreateMessageDialog(w,snd_string_find,args,n);
#if UNSET_TRANSIENT
  add_dialog(fdw->state,fdw->dialog);
#endif

  XmStringFree(xmstr1);
  XmStringFree(xmstr2);
  XmStringFree(titlestr);

  XtUnmanageChild(XmMessageBoxGetChild(fdw->dialog,XmDIALOG_SYMBOL_LABEL));
  XtUnmanageChild(XmMessageBoxGetChild(fdw->dialog,XmDIALOG_MESSAGE_LABEL));

  XtAddCallback(fdw->dialog,XmNhelpCallback,edit_find_help_callback,fdw->state);
  XtAddCallback(fdw->dialog,XmNcancelCallback,edit_find_cancel_callback,fdw);
  XtAddCallback(fdw->dialog,XmNokCallback,edit_find_ok_callback,fdw);

  rc = XtCreateManagedWidget("row",xmFormWidgetClass,fdw->dialog,NULL,0);

  n=0;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  dl = XtCreateManagedWidget(snd_string_find_p,xmLabelWidgetClass,rc,args,n);

  n=0;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,dl); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  df = sndCreateTextFieldWidget(fdw->state,"text",rc,args,n,ACTIVATABLE);
  fdw->list = df;

  n=0;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,df); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  ds = XtCreateManagedWidget(snd_string_global_search,xmLabelWidgetClass,rc,args,n);
  fdw->scale = ds;
}

void Edit_Find_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  static dialog_info *fdw;
  snd_state *ss = (snd_state *)clientData;
  finish_keyboard_selection();
  if (!fdw) 
    {
      fdw = (dialog_info *)calloc(1,sizeof(dialog_info));
      fdw->state = ss;
      make_edit_find_dialog(w,fdw);
#if MANAGE_DIALOG
      XtManageChild(fdw->dialog);
#endif
      if (!(ss->using_schemes)) 
	{
	  map_over_children(fdw->dialog,set_main_color_of_widget,(void *)clientData);
	  XtVaSetValues(XmMessageBoxGetChild(fdw->dialog,XmDIALOG_OK_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	  XtVaSetValues(XmMessageBoxGetChild(fdw->dialog,XmDIALOG_CANCEL_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	  XtVaSetValues(XmMessageBoxGetChild(fdw->dialog,XmDIALOG_HELP_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	}
      fdw->cancelB = XmMessageBoxGetChild(fdw->dialog,XmDIALOG_CANCEL_BUTTON);
    }
  else raise_dialog(fdw->dialog);
  if (!XtIsManaged(fdw->dialog)) XtManageChild(fdw->dialog);
}


/* -------- region browser -------- */

static regrow *region_rows[REGIONS];
static snd_info *reg_sp = NULL;
static dialog_info *reg_fd = NULL;
static int current_region = -1;
static Widget selectw,reg_srtxt,reg_lentxt,reg_chntxt,reg_maxtxt;

static void make_region_element(region_state *rs, int i)
{
  regrow *r;
  r = region_rows[i];
  make_bold_button_label(r->nm,rs->name[i]);
  XmToggleButtonSetState(r->sv,rs->save[i],FALSE);
  XmToggleButtonSetState(r->pl,FALSE,FALSE);
  XtManageChild(r->rw);
}

static void unhighlight_region(snd_state *ss)
{
  regrow *oldr;
  if (current_region != -1)
    {
      oldr = region_rows[current_region];
      if (!(ss->using_schemes)) 
	{
	  XtVaSetValues(oldr->rw,XmNbackground,(ss->sgx)->high,NULL);
	  XtVaSetValues(oldr->nm,XmNbackground,(ss->sgx)->high,NULL);
	}
    }
}

static void unmake_region_labels (void)
{
  make_bold_button_label(reg_srtxt,snd_string_srate_p);
  make_bold_button_label(reg_chntxt,snd_string_chans_p);
  make_bold_button_label(reg_lentxt,snd_string_length_p);
  make_bold_button_label(reg_maxtxt,snd_string_maxamp_p);
}

static void highlight_region(snd_state *ss)
{
  regrow *oldr;
  if (current_region != -1)
    {
      oldr = region_rows[current_region];
      if (!(ss->using_schemes)) 
	{
	  XtVaSetValues(oldr->rw,XmNbackground,(ss->sgx)->zoom,NULL);
	  XtVaSetValues(oldr->nm,XmNbackground,(ss->sgx)->zoom,NULL);
	}
    }
}

static void make_region_labels(file_info *hdr)
{
  sprintf(file_string,snd_string_srate,hdr->srate);
  make_bold_button_label(reg_srtxt,file_string);
  sprintf(file_string,snd_string_chans,hdr->chans);
  make_bold_button_label(reg_chntxt,file_string);
  sprintf(file_string,snd_string_length,(float)(hdr->samples)/(float)(hdr->chans * hdr->srate));
  make_bold_button_label(reg_lentxt,file_string);
  sprintf(file_string,snd_string_maxamp,region_maxamp(current_region));
  make_bold_button_label(reg_maxtxt,file_string);
}

void update_region_browser(int grf_too)
{
  int i,len;
  region_state *rs;
  snd_state *ss;
  chan_info *cp;
  rs = region_report();
  len = rs->len;
  for (i=0;i<len;i++) make_region_element(rs,i);
  for (i=len;i<REGIONS;i++) XtUnmanageChild(region_rows[i]->rw);
  free_region_state(rs);
  if (len == 0) return;
  XtManageChild(reg_fd->list);
  ss = reg_fd->state;
  if (grf_too)
    {
      unhighlight_region(ss);
      current_region = 0;
      highlight_region(ss);
      goto_window(region_rows[0]->nm);
      cp = reg_sp->chans[0];
      if (cp) 
	{
	  cp->chan = 0;
	  XtSetSensitive(chan_widget(cp,W_chn_f),FALSE);
	  XtSetSensitive(chan_widget(cp,W_chn_w),(region_chans(0) > 1));
	  if (region_ok(0)) 
	    {
	      reg_sp->hdr = fixup_region_data(cp,0,0);
	      make_region_labels(reg_sp->hdr);
	      update_graph(cp,ss);
	    }
	  else unmake_region_labels();
	}
    }
}

static void region_ok_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  dialog_info *fd = (dialog_info *)clientData;
  XtUnmanageChild(fd->dialog);
}

int region_browser_is_active(void)
{
  return((reg_fd) && (XtIsRealized(reg_fd->dialog)));
}

static void region_resize_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  update_graph((chan_info *)clientData,NULL);
}

static int region_dialog_active(void);

void delete_region_and_update_browser(snd_state *ss, int n)
{
  int act;
  act = delete_region(n);
  if (region_dialog_active())
    {
      if (act != -1)
	{
	  current_region = 0;
	  highlight_region(ss);
	  goto_window(region_rows[0]->nm);
	}
      else 
	{
	  unhighlight_region(ss);
	  current_region = -1;
	}
      update_region_browser(1);
    }
}

static void region_delete_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss = (snd_state *)clientData;
  if (current_region != -1)
    delete_region_and_update_browser(ss,current_region);
}

static void region_help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  dialog_info *fd = (dialog_info *)clientData;
#if HAVE_XmHTML
  snd_help(fd->state,snd_string_Region_Browser,"#regionbrowser");
#else
  snd_help(fd->state,snd_string_Region_Browser,get_region_browser_help());
#endif
}

void select_region_and_update_browser(snd_state *ss, int n)
{
  deactivate_selection();  /* just in case there's a region being highlighted */
  if (region_dialog_active())
    {
      unhighlight_region(ss);
      select_region(n);
      current_region = 0;
      highlight_region(ss);
      goto_window(region_rows[0]->nm);
      XtSetSensitive(selectw,FALSE);
      update_region_browser(0);
    }
}

static void region_select_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss = (snd_state *)clientData;
  if (current_region != -1)
    select_region_and_update_browser(ss,current_region);
}

static void region_up_arrow_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  chan_info *cp;
  cp = reg_sp->chans[0];
  if (cp->chan > 0)
    {
      cp->chan--;
      XtSetSensitive(chan_widget(cp,W_chn_f),(cp->chan > 0));
      XtSetSensitive(chan_widget(cp,W_chn_w),TRUE);
      fixup_region_data(cp,cp->chan,current_region);
      update_graph(cp,NULL);
    }
}

static void region_down_arrow_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  chan_info *cp;
  cp = reg_sp->chans[0];
  if ((cp->chan+1) < region_chans(current_region))
    {
      cp->chan++;
      XtSetSensitive(chan_widget(cp,W_chn_f),TRUE);
      XtSetSensitive(chan_widget(cp,W_chn_w),(region_chans(current_region) > (cp->chan+1)));
      fixup_region_data(cp,cp->chan,current_region);
      update_graph(cp,NULL);
    }
}

static void region_focus_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss;
  chan_info *cp;
  regrow *r = (regrow *)clientData;
  ss = r->ss;
  unhighlight_region(ss);
  current_region=r->pos;
  cp = reg_sp->chans[0];
  cp->chan  = 0;
  highlight_region(ss);
  XtSetSensitive(chan_widget(cp,W_chn_f),FALSE);
  XtSetSensitive(chan_widget(cp,W_chn_w),(region_chans(current_region) > 1));
  XtSetSensitive(selectw,(current_region != 0));
  reg_sp->hdr = fixup_region_data(cp,0,current_region);
  make_region_labels(reg_sp->hdr);
  update_graph(cp,NULL);
}


void reflect_play_region_stop(region_info *r)
{
  regrow *rg;
  rg = (r->rg);
  if (rg) XmToggleButtonSetState(rg->pl,FALSE,FALSE);
}

static void region_play_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  regrow *r = (regrow *)clientData;
  play_region(r->ss,r->pos,(void *)r);
}

static void region_save_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  regrow *r = (regrow *)clientData;
  XmToggleButtonCallbackStruct *cb = (XmToggleButtonCallbackStruct *)callData;
  protect_region(r->pos,cb->set);
}

void set_region_protect(int reg, int protect)
{
  regrow *r;
  protect_region(reg,protect);
  r = region_rows[reg];
  if ((r) && (r->sv)) XmToggleButtonSetState(r->sv,protect,FALSE);
}

static void region_print_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss = (snd_state *)clientData;
  region_print(eps_file(ss),"region",reg_sp->chans[0]);
}

static void make_region_dialog(Widget w, dialog_info *fd)
{
  int n,i;
  Arg args[32];
  Widget formw,last_row,ww,sep,infosep,prtb;
  XmString xok,xdelete,xhelp,titlestr;
  regrow *r;
  snd_state *ss;
  chan_info *cp;
  file_info *hdr;
  ww_info *wwl;

  ss = fd->state;
  reg_fd = fd;

  xok = XmStringCreate(snd_string_Dismiss,XmFONTLIST_DEFAULT_TAG);
  xhelp = XmStringCreate(snd_string_Help,XmFONTLIST_DEFAULT_TAG);
  xdelete = XmStringCreate(snd_string_Delete,XmFONTLIST_DEFAULT_TAG);
  titlestr = XmStringCreateLocalized(snd_string_Regions);

  n=0;
  if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
  XtSetArg(args[n],XmNcancelLabelString,xdelete); n++;
  XtSetArg(args[n],XmNhelpLabelString,xhelp); n++;
  XtSetArg(args[n],XmNokLabelString,xok); n++;
  XtSetArg(args[n],XmNautoUnmanage,FALSE); n++;
  XtSetArg(args[n],XmNdialogTitle,titlestr); n++;
#if RESIZE_DIALOG
  XtSetArg(args[n],XmNresizePolicy,XmRESIZE_GROW); n++;
  XtSetArg(args[n],XmNnoResize,FALSE); n++;
#endif
#if UNSET_TRANSIENT
  XtSetArg(args[n],XmNtransient,FALSE); n++;
#endif
  fd->dialog = XmCreateTemplateDialog(w,snd_string_Regions,args,n);
#if UNSET_TRANSIENT
  add_dialog(ss,fd->dialog);
#endif

  XtAddCallback(fd->dialog,XmNokCallback,region_ok_Callback,fd);
  XtAddCallback(fd->dialog,XmNcancelCallback,region_delete_Callback,ss);
  XtAddCallback(fd->dialog,XmNhelpCallback,region_help_Callback,fd);
  XmStringFree(xhelp);
  XmStringFree(xok);
  XmStringFree(xdelete);
  XmStringFree(titlestr);

  if (!(ss->using_schemes))
    {
      XtVaSetValues(XmMessageBoxGetChild(fd->dialog,XmDIALOG_OK_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XmMessageBoxGetChild(fd->dialog,XmDIALOG_CANCEL_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XmMessageBoxGetChild(fd->dialog,XmDIALOG_HELP_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
    }

  n=0;
  if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
  selectw = XtCreateManagedWidget(snd_string_Select,xmPushButtonWidgetClass,fd->dialog,args,n);
  XtAddCallback(selectw,XmNactivateCallback,region_select_Callback,ss);
  XtSetSensitive(selectw,FALSE);

  n=0;
  if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNbottomWidget,XmMessageBoxGetChild(fd->dialog,XmDIALOG_SEPARATOR)); n++;
  formw = XtCreateManagedWidget("formw",xmFormWidgetClass,fd->dialog,args,n);

  wwl = make_title_row(ss,formw,snd_string_save,snd_string_play,snd_string_regions,0);
  ww = wwl->ww;
  fd->list = wwl->list;
  if (!(ss->using_schemes)) map_over_children(fd->list,set_main_color_of_widget,(void *)ss);
  last_row = NULL;
  
  for (i=0;i<REGIONS;i++)
    {
      r = make_regrow(ss,ww,last_row,region_save_Callback,region_play_Callback,region_focus_Callback);
      region_rows[i] = r;
      r->pos = i;
      r->ss = ss;
      last_row = r->rw;
    }

  update_region_browser(0);

  n=0;
  if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,fd->list); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
  XtSetArg(args[n],XmNseparatorType,XmSHADOW_ETCHED_IN); n++;
  XtSetArg(args[n],XmNwidth,5); n++;
  sep = XtCreateManagedWidget("sep",xmSeparatorWidgetClass,formw,args,n);


  n=0;
  if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,fd->list); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,wwl->dbline); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNbottomWidget,sep); n++;
  XtSetArg(args[n],XmNorientation,XmVERTICAL); n++;
  XtSetArg(args[n],XmNseparatorType,XmSHADOW_ETCHED_IN); n++;
  infosep = XtCreateManagedWidget("infosep",xmSeparatorWidgetClass,formw,args,n);

  n=0;
  if (!(ss->using_schemes)) n = background_high_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,infosep); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,wwl->dbline); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNfontList,bold_button_FONT(ss)); n++;
  XtSetArg(args[n],XmNalignment,XmALIGNMENT_BEGINNING); n++;
  reg_srtxt = XtCreateManagedWidget(snd_string_srate_p,xmLabelWidgetClass,formw,args,n);

  n=0;
  if (!(ss->using_schemes)) n = background_high_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,infosep); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,reg_srtxt); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNfontList,bold_button_FONT(ss)); n++;
  XtSetArg(args[n],XmNalignment,XmALIGNMENT_BEGINNING); n++;
  reg_chntxt = XtCreateManagedWidget(snd_string_chans_p,xmLabelWidgetClass,formw,args,n);

  n=0;
  if (!(ss->using_schemes)) n = background_high_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,infosep); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,reg_chntxt); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNfontList,bold_button_FONT(ss)); n++;
  XtSetArg(args[n],XmNalignment,XmALIGNMENT_BEGINNING); n++;
  reg_lentxt = XtCreateManagedWidget(snd_string_length_p,xmLabelWidgetClass,formw,args,n);

  n=0;
  if (!(ss->using_schemes)) n = background_high_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,infosep); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,reg_lentxt); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNfontList,bold_button_FONT(ss)); n++;
  XtSetArg(args[n],XmNalignment,XmALIGNMENT_BEGINNING); n++;
  reg_maxtxt = XtCreateManagedWidget(snd_string_maxamp_p,xmLabelWidgetClass,formw,args,n);

  n=0;
  if (!(ss->using_schemes)) 
    {
      n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNarmColor,(ss->sgx)->text); n++;
    }
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,infosep); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,reg_maxtxt); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNbottomWidget,sep); n++;
  XtSetArg(args[n],XmNfontList,bold_button_FONT(ss)); n++;
  XtSetArg(args[n],XmNalignment,XmALIGNMENT_CENTER); n++;
  prtb = XtCreateManagedWidget(snd_string_print,xmPushButtonWidgetClass,formw,args,n);
  XtAddCallback(prtb,XmNactivateCallback,region_print_Callback,(XtPointer)ss);

  n=0;
  if (!(ss->using_schemes)) n = background_white_color(args,n,ss);
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,sep); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
  fd->scale = XtCreateManagedWidget("grf",xmFormWidgetClass,formw,args,n);

  free(wwl); 
  wwl=NULL;

  XtManageChild(fd->dialog);

  if (!reg_sp) 
    { /* just a place holder, I think -- see make_region_readable in snd-clip.c */
      reg_sp = (snd_info *)calloc(1,sizeof(snd_info));
      reg_sp->s_type = make_snd_pointer_type(SND_INFO);
      reg_sp->nchans = 1;
      reg_sp->chans = (chan_info **)calloc(1,sizeof(chan_info *));
      reg_sp->sx_scroll_max = 100;
      reg_sp->hdr = (file_info *)calloc(1,sizeof(file_info));
      hdr = reg_sp->hdr;
      hdr->s_type = make_snd_pointer_type(FILE_INFO);
      hdr->samples = region_len(0)-1;
      hdr->srate = region_srate(0);
      hdr->comment = NULL;
      hdr->chans = 1;
      current_region = 0;
    }

  add_channel_window(reg_sp,0,ss,0,0,fd->scale,WITH_ARROWS);
  cp = reg_sp->chans[0];
  cp->hookable = 0;
  if (!(ss->using_schemes)) XtVaSetValues(region_rows[0]->nm,XmNbackground,(ss->sgx)->white,NULL);

  XtAddCallback(chan_widget(cp,W_chn_graph),XmNresizeCallback,region_resize_Callback,(XtPointer)cp);
  XtAddCallback(chan_widget(cp,W_chn_graph),XmNexposeCallback,region_resize_Callback,(XtPointer)cp);

  /* chn_f is up arrow, chn_w is down arrow */
  XtAddCallback(chan_widget(cp,W_chn_f),XmNactivateCallback,region_up_arrow_Callback,(XtPointer)ss);
  XtAddCallback(chan_widget(cp,W_chn_w),XmNactivateCallback,region_down_arrow_Callback,(XtPointer)ss);
  XtSetSensitive(chan_widget(cp,W_chn_f),FALSE);
  if (region_chans(0) > 1) XtSetSensitive(chan_widget(cp,W_chn_w),TRUE);
  cp->chan = 0;
  reg_sp->hdr = fixup_region_data(cp,0,0);
  make_region_labels(reg_sp->hdr);
  highlight_region(ss);
  update_graph(cp,NULL);
}

static dialog_info *region_fd = NULL;

void View_Region_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  /* put up scrollable dialog describing/playing/editing the region list */
  snd_state *ss = (snd_state *)clientData;
  if (region_fd == NULL)
    {
      region_fd = (dialog_info *)calloc(1,sizeof(dialog_info));
      region_fd->size = 0;
      region_fd->state = ss;
      make_region_dialog(w,region_fd);
    }
  else raise_dialog(region_fd->dialog);
  if (!XtIsManaged(region_fd->dialog)) {current_region = 0; XtManageChild(region_fd->dialog);}
}

static int region_dialog_active(void) {return(region_fd != NULL);}

int region_dialog_is_active(void)
{
  return((region_fd) && (region_fd->dialog) && (XtIsManaged(region_fd->dialog)));
}


/* -------------------------------- Raw Data Dialog -------------------------------- */
/*
 * used also by New File menu option
 */

static Widget raw_data_dialog = NULL;
static Widget raw_srate_text,raw_chans_text,raw_location_text;
static int raw_data_location = 0;

static int new_ctr = 0;

static void cancel_new_file(snd_state *ss)
{
  if (ss->pending_new)
    {
      remove(ss->pending_new);
      ss->pending_new = NULL;
      new_ctr--;
    }
}

static snd_info *finish_new_file(snd_state *ss)
{
  snd_info *sp;
  int chan,size;
  unsigned char* buf;
  if (output_type_and_format_ok(raw_type(ss),raw_format(ss)))
    {
      snd_write_header(ss,ss->pending_new,raw_type(ss),raw_srate(ss),raw_chans(ss),28,raw_chans(ss)*2,raw_format(ss),NULL,0);
      chan = snd_reopen_write(ss,ss->pending_new);
      lseek(chan,c_snd_header_data_location(),0L);
      size = raw_chans(ss) * c_snd_bytes(raw_format(ss),2);
      buf = (unsigned char *)calloc(size,sizeof(unsigned char));
      write(chan,buf,size);
      close(chan);
      free(buf);
      sp = snd_open_file(ss->pending_new,ss);
      ss->pending_new = NULL;
      return(sp);
    }
  else 
    {
      sprintf(file_string,snd_string_cant_write_type_with_format,
	      (raw_type(ss) == AIFF_sound_file) ? "an" : "a",
	      sound_type_name(raw_type(ss)),
	      data_formats[raw_format(ss)-1]);
      snd_printf(ss,file_string);
      cancel_new_file(ss);
      return(NULL);
    }
}

static void raw_data_ok_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss = (snd_state *)clientData;
  state_context *sgx;
  snd_info *sp;
  char *str;
  int tmp;
  XmAnyCallbackStruct *cb = (XmAnyCallbackStruct *)callData;
  sgx = ss->sgx;
  if (cb->event != sgx->text_activate_event)
    {
      str = XmTextGetString(raw_srate_text);
      if ((str) && (*str)) sscanf(str,"%d",&tmp);
      set_raw_srate(ss,tmp);
      str = XmTextGetString(raw_chans_text);
      if ((str) && (*str)) sscanf(str,"%d",&tmp);
      set_raw_chans(ss,tmp);
      str = XmTextGetString(raw_location_text);
      if ((str) && (*str)) sscanf(str,"%d",&raw_data_location);
      if (ss->pending_new)
	sp = finish_new_file(ss);
      else 
	{
	  override_sound_header(ss->pending_open, raw_srate(ss), raw_chans(ss), raw_format(ss), raw_sound_file, raw_data_location, 
			    c_snd_samples(raw_format(ss),c_true_file_length() - raw_data_location));
	  sp = snd_open_file(ss->pending_open,ss);
	}
      if (sp) select_channel(sp,0);
      reflect_raw_open_in_menu();
      XtUnmanageChild(w);
    }
}

static void raw_data_help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
       snd_string_Raw_Data,
"To display and edit sound data, Snd needs\n\
to know how the data's sampling rate, number\n\
of channels, and numerical format.  This dialog\n\
gives you a chance to set those fields.\n\
To make the current settings the default\n\
for any future headerless files, click the\n\
'Default' button.\n\
");
}

static void raw_data_cancel_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss = (snd_state *)clientData;
  ss->pending_open = NULL;
  if (ss->pending_new) cancel_new_file(ss);
      reflect_raw_open_in_menu();
  XtUnmanageChild(raw_data_dialog);
}

static void raw_data_default_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss = (snd_state *)clientData;
  set_use_raw_defaults(ss,1);
  raw_data_ok_Callback(w,clientData,callData);
}

static void raw_data_browse_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss = (snd_state *)clientData;
  XmListCallbackStruct *cbs = (XmListCallbackStruct *)callData;
  set_raw_format(ss,cbs->item_position);
}


static char dfs_str[8];
static char dfc_str[4];

static void make_raw_data_dialog(char *filename, snd_state *ss)
{
  XmString *formats;
  XmString xstr1,xstr2,xstr3,xstr4,titlestr;
  int i,n;
  Arg args[20];
  Widget lst,defw,dls,dfs,dfc,rform,dlab,dloc,dloclab,chnlab;
  n=0;
  xstr1 = XmStringCreate(snd_string_Cancel,XmFONTLIST_DEFAULT_TAG); /* needed by template dialog */
  xstr2 = XmStringCreate(snd_string_Help,XmFONTLIST_DEFAULT_TAG);
  xstr3 = XmStringCreate(snd_string_Ok,XmFONTLIST_DEFAULT_TAG);
  titlestr = XmStringCreateLocalized(snd_string_No_Header_on_File);
  sprintf(file_string,snd_string_No_header_found_for,filename_without_home_directory(filename));
  xstr4 = XmStringCreate(file_string,XmFONTLIST_DEFAULT_TAG);
  if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
  XtSetArg(args[n],XmNcancelLabelString,xstr1); n++;
  XtSetArg(args[n],XmNhelpLabelString,xstr2); n++;
  XtSetArg(args[n],XmNokLabelString,xstr3); n++;
  XtSetArg(args[n],XmNmessageString,xstr4); n++;
  XtSetArg(args[n],XmNdialogTitle,titlestr); n++;
  XtSetArg(args[n],XmNautoUnmanage,FALSE); n++;
#if RESIZE_DIALOG
  XtSetArg(args[n],XmNresizePolicy,XmRESIZE_GROW); n++;
  XtSetArg(args[n],XmNnoResize,FALSE); n++;
#endif
#if UNSET_TRANSIENT
  XtSetArg(args[n],XmNtransient,FALSE); n++;
#endif
  raw_data_dialog = XmCreateTemplateDialog(main_SHELL(ss),snd_string_raw_data,args,n);
#if UNSET_TRANSIENT
  add_dialog(ss,raw_data_dialog);
#endif

  XtAddCallback(raw_data_dialog,XmNcancelCallback,raw_data_cancel_Callback,ss);
  XtAddCallback(raw_data_dialog,XmNhelpCallback,raw_data_help_Callback,ss);
  XtAddCallback(raw_data_dialog,XmNokCallback,raw_data_ok_Callback,ss);
  XmStringFree(xstr1);
  XmStringFree(xstr2);
  XmStringFree(xstr3);
  XmStringFree(xstr4);
  XmStringFree(titlestr);

  n=0;
  if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
  defw = XtCreateManagedWidget(snd_string_Default,xmPushButtonWidgetClass,raw_data_dialog,args,n);
  XtAddCallback(defw,XmNactivateCallback,raw_data_default_Callback,ss);

  rform = XtCreateManagedWidget("sretc",xmFormWidgetClass,raw_data_dialog,NULL,0);

  n=0;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNmarginTop,6); n++;
  dls = XtCreateManagedWidget(snd_string_srate_p,xmLabelWidgetClass,rform,args,n);

  n=0;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,dls); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNcolumns,6); n++;
  XtSetArg(args[n],XmNresizeWidth,FALSE); n++;
  dfs = sndCreateTextFieldWidget(ss,"text",rform,args,n,NOT_ACTIVATABLE);
  if (raw_srate(ss) < 100000) sprintf(dfs_str," %d",raw_srate(ss)); else sprintf(dfs_str,"%d",raw_srate(ss));
  XmTextSetString(dfs,dfs_str);

  n=0;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,dfs); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNalignment,XmALIGNMENT_END); n++;	
  XtSetArg(args[n],XmNmarginTop,6); n++;
  chnlab = XtCreateManagedWidget(snd_string_chans_p,xmLabelWidgetClass,rform,args,n);

  n=0;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,chnlab); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNcolumns,3); n++;
  XtSetArg(args[n],XmNresizeWidth,FALSE); n++;
  dfc = sndCreateTextFieldWidget(ss,"text",rform,args,n,NOT_ACTIVATABLE);
  if (raw_chans(ss) < 10) sprintf(dfc_str,"  %d",raw_chans(ss)); else sprintf(dfc_str," %d",raw_chans(ss));
  XmTextSetString(dfc,dfc_str);
  
  n=0;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,dfs); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNalignment,XmALIGNMENT_END); n++;
  XtSetArg(args[n],XmNmarginTop,6); n++;
  dloclab = XtCreateManagedWidget(snd_string_data_location_p,xmLabelWidgetClass,rform,args,n);
  
  n=0;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNleftWidget,dloclab); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,dfs); n++;
  XtSetArg(args[n],XmNcolumns,8); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  dloc = sndCreateTextFieldWidget(ss,"text",rform,args,n,NOT_ACTIVATABLE);
  XmTextSetString(dloc,"0");

  n=0;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,dloc); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
  dlab = XtCreateManagedWidget(snd_string_data_format_p,xmLabelWidgetClass,rform,args,n);

  n=0;
#ifdef LESSTIF_VERSION
  if (!(ss->using_schemes)) n = background_white_color(args,n,ss);
#endif
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,dlab); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  formats = (XmString *)calloc(NUM_DATA_FORMATS,sizeof(XmString));
  for (i=0;i<NUM_DATA_FORMATS;i++) formats[i] = XmStringCreate(data_formats[i],XmFONTLIST_DEFAULT_TAG);
  lst = XmCreateScrolledList(rform,"raw-data-format-list",args,n);
  XtVaSetValues(lst,XmNitems,formats,XmNitemCount,NUM_DATA_FORMATS,XmNvisibleItemCount,6,NULL);
  XtManageChild(lst); 
  XmListSelectPos(lst,raw_format(ss),FALSE);
  for (i=0;i<NUM_DATA_FORMATS;i++) XmStringFree(formats[i]);
  free(formats);
  XtAddCallback(lst,XmNbrowseSelectionCallback,raw_data_browse_Callback,ss);

#if MANAGE_DIALOG
  XtManageChild(raw_data_dialog);
#endif

  if (!(ss->using_schemes)) 
    {
      map_over_children(rform,set_main_color_of_widget,(void *)ss);
      XtVaSetValues(lst,XmNbackground,(ss->sgx)->white,NULL);
      XtVaSetValues(XmMessageBoxGetChild(raw_data_dialog,XmDIALOG_OK_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XmMessageBoxGetChild(raw_data_dialog,XmDIALOG_CANCEL_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XmMessageBoxGetChild(raw_data_dialog,XmDIALOG_HELP_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(defw,XmNselectColor,(ss->sgx)->text,NULL);
    }

  raw_srate_text = dfs;
  raw_chans_text = dfc;
  raw_location_text = dloc;
}

file_info *get_file_info(char *filename, snd_state *ss)
{
  /* put up dialog for srate, chans, data format */
  XmString xstr;
  if ((ss->pending_open) || (use_raw_defaults(ss)))
    {
      /* choices already made, so just send back a header that reflects those choices */
      return(make_file_info_1(filename,ss));
    }
  if (!raw_data_dialog) 
    make_raw_data_dialog(filename,ss);
  else
    {
      /* set filename in label */
      sprintf(file_string,snd_string_No_header_found_for,filename_without_home_directory(filename));
      xstr = XmStringCreate(file_string,XmFONTLIST_DEFAULT_TAG);
      XtVaSetValues(raw_data_dialog,XmNmessageString,xstr,NULL);
      XmStringFree(xstr);
      raise_dialog(raw_data_dialog);
    }
  if (!XtIsManaged(raw_data_dialog)) XtManageChild(raw_data_dialog);
  if (!(ss->pending_new))
    {
      snd_IO_error = snd_pending_open;
      ss->pending_open = filename;
    }
  /* we don't want to lock out the main program (or the dialog help window), but
   * it would be very confusing to have two file openings in progress.
   */
  reflect_raw_pending_in_menu();
  return(NULL);
}

static int swap_int (int n)
{
  int o;
  unsigned char *inp,*outp; 
  inp=(unsigned char *)&n; 
  outp=(unsigned char *)&o;
  outp[0]=inp[3]; outp[1]=inp[2]; outp[2]=inp[1]; outp[3]=inp[0];
  return(o);
}

static short swap_short (short n)
{
  short o;
  unsigned char *inp,*outp; 
  inp=(unsigned char *)&n; 
  outp=(unsigned char *)&o;
  outp[0]=inp[1]; outp[1]=inp[0]; 
  return(o);
}

file_info *get_reasonable_file_info(char *filename, snd_state *ss, file_info *hdr)
{
  XmString xstr;
  char *reason_str,*tmp_str;
  int ns,better_srate = 0,better_chans = 0;
  reason_str = (char *)calloc(1024,sizeof(char));
  tmp_str = (char *)calloc(64,sizeof(char));
  /* try to provide some notion of what might be the intended header (currently limited to byte-order mistakes) */
  sprintf(reason_str,"srate: %d",hdr->srate);
  ns = swap_int(hdr->srate);
  if ((ns<4000) || (ns>100000)) ns = swap_short(hdr->srate);
  if ((ns>4000) && (ns<100000))
    {
      better_srate = ns;
      sprintf(tmp_str," (swapped: %d)",ns);
      strcat(reason_str,tmp_str);
    }
  sprintf(tmp_str,"\nchans: %d",hdr->chans);
  strcat(reason_str,tmp_str);
  ns = swap_int(hdr->chans);
  if ((ns<0) || (ns>8)) ns=swap_short(hdr->chans);
  if ((ns>0) && (ns <= 8))
    {
      better_chans = ns;
      sprintf(tmp_str," (swapped: %d)",ns);
      strcat(reason_str,tmp_str);
    }
  sprintf(tmp_str,"\nlength: %.3f (%d samples, %d bytes total)",
	  (float)(hdr->samples)/(float)(hdr->chans * hdr->srate),
	  hdr->samples,
	  c_true_file_length());
  strcat(reason_str,tmp_str);
  ns = swap_int(hdr->samples);
  if (ns < c_true_file_length())
    {
      sprintf(tmp_str,"\n  (swapped: %d",ns);
      strcat(reason_str,tmp_str);
      if ((better_chans) && (better_srate))
	{
	  sprintf(tmp_str,", swapped length: %.3f / sample-size-in-bytes)",(float)ns/(float)(better_chans * better_srate));
	  strcat(reason_str,tmp_str);
	}
      else strcat(reason_str,")");
    }
  sprintf(tmp_str,"\ndata location: %d",hdr->data_location);
  strcat(reason_str,tmp_str);
  ns = swap_int(hdr->data_location);
  if ((ns>0) && (ns<=1024)) 
    {
      sprintf(tmp_str," (swapped: %d)",ns);
      strcat(reason_str,tmp_str);
    }
  sprintf(tmp_str,"\ntype: %s",sound_type_name(hdr->type));
  strcat(reason_str,tmp_str);
  sprintf(tmp_str,"\nformat: %s\n",sound_format_name(hdr->format));
  strcat(reason_str,tmp_str);
  hdr->type = raw_sound_file;
  snd_help(ss,"Current header values",reason_str);
  sprintf(file_string,snd_string_Bogus_header_found_for,filename_without_home_directory(filename));
  xstr = XmStringCreate(file_string,XmFONTLIST_DEFAULT_TAG);
  if (!raw_data_dialog) make_raw_data_dialog(filename,ss);
  XtVaSetValues(raw_data_dialog,XmNmessageString,xstr,NULL);
  XmStringFree(xstr);
  raise_dialog(raw_data_dialog);
  if (!XtIsManaged(raw_data_dialog)) XtManageChild(raw_data_dialog);
  snd_IO_error = snd_pending_open;
  ss->pending_open = filename;
  reflect_raw_pending_in_menu();
  free(tmp_str);
  free(reason_str);
  return(NULL);
}

/* new file needs raw data dialog to get file description */

static char new_file_name[MAX_FILE_NAME];

snd_info *snd_new_file(snd_state *ss, char *newname)
{
  file_info *hdr;
  finish_keyboard_selection();
  if (!newname) sprintf(new_file_name,"new-%d.snd",new_ctr++); else strcpy(new_file_name,newname);
  snd_create(ss,new_file_name); /* needs to exist for get_file_info */
  ss->pending_new = new_file_name;
  hdr = get_file_info(new_file_name,ss);
  if (hdr) return(finish_new_file(ss));
  return(NULL);
}




/* -------- color browser -------- */

typedef struct {
  Widget dialog;
  Widget list; 
  Widget scale; 
  Widget invert;
  Widget cutoff;
  snd_state *state;
} color_chooser_info;

static color_chooser_info *ccd = NULL;

static void Invert_Color_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  snd_state *ss;
  color_chooser_info *cd = (color_chooser_info *)clientData;
  XmToggleButtonCallbackStruct *cb = (XmToggleButtonCallbackStruct *)callData;
  ss = cd->state;
  in_set_color_inverted(ss,cb->set);
  map_over_chans(ss,update_graph,NULL);
}

void set_color_inverted(snd_state *ss, int val)
{
  in_set_color_inverted(ss,val);
  if (ccd) XmToggleButtonSetState(ccd->invert,val,FALSE);
  if (!(ss->graph_hook_active)) map_over_chans(ss,update_graph,NULL);
}

static void Scale_Color_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  snd_state *ss;
  float val;
  int scale_val;
  XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *)callData;
  color_chooser_info *cd = (color_chooser_info *)clientData;
  ss = cd->state;
  scale_val = cbs->value;
  if (scale_val <= 50) 
    val = (float)(scale_val+1)/51.0;
  else val = 1.0 + (float)(scale_val-50)*20.0;
  in_set_color_scale(ss,val);
  map_over_chans(ss,update_graph,NULL);
}

static void reflect_color_scale(float val)
{
  if (val<=1.0) 
    XmScaleSetValue(ccd->scale,(int)(val*51.0 - 1));
  else XmScaleSetValue(ccd->scale,(int)((val-1.0)/20.0 + 50.0));
}

void set_color_scale(snd_state *ss, float val)
{
  in_set_color_scale(ss,val);
  if (ccd) reflect_color_scale(color_scale(ss));
  if (!(ss->graph_hook_active)) map_over_chans(ss,update_graph,NULL);
}

static void List_Color_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  snd_state *ss;
  XmListCallbackStruct *cbs = (XmListCallbackStruct *)callData;
  color_chooser_info *cd = (color_chooser_info *)clientData;
  ss = cd->state;
  in_set_spectro_color(ss,(cbs->item_position - 1));
  map_over_chans(ss,update_graph,NULL);
}

void set_spectro_color(snd_state *ss, int val)
{
  in_set_spectro_color(ss,val);
  if (ccd) XmListSelectPos(ccd->list,val+1,FALSE);
  if (!(ss->graph_hook_active)) map_over_chans(ss,update_graph,NULL);
}

static void Cutoff_Color_Callback(Widget w,XtPointer clientData,XtPointer callData) /* cutoff point */
{
  /* cutoff point for color chooser */
  snd_state *ss;
  XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *)callData;
  color_chooser_info *cd = (color_chooser_info *)clientData;
  ss = cd->state;
  in_set_color_cutoff(ss,(float)(cbs->value)/1000.0);
  map_over_chans(ss,update_graph,NULL);
}

void set_color_cutoff(snd_state *ss, float val)
{
  in_set_color_cutoff(ss,val);
  if (ccd) XmScaleSetValue(ccd->cutoff,(int)(val*1000.0));
  if (!(ss->graph_hook_active)) map_over_chans(ss,update_graph,NULL);
}


static void Dismiss_Color_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  color_chooser_info *cd = (color_chooser_info *)clientData;
  XtUnmanageChild(cd->dialog);
}

static void Help_Color_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  snd_state *ss;
  color_chooser_info *cd = (color_chooser_info *)clientData;
  ss = cd->state;
  snd_help(ss,
       "View Color",
"This dialog sets the colormap and associated\n\
variables used during sonogram, spectrogram,\n\
and perhaps wavogram display. The cutoff scale refers\n\
to the minimum data value to be displayed.\n\
");	   
}

#define NUM_COLORMAPS 9
static char *colormaps[] = {"gray","hsv","hot","cool","bone","copper","pink","jet","prism"};
/* I tried a scrolled window with each colormap name in an appropriate color, but it looked kinda dumb */

void View_Color_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  Arg args[32];
  int n,i;
  XmString xhelp,xdismiss,xcutoff,xinvert,titlestr;
  XmString *cmaps;
  Widget mainform,list_label,light_label,sep,sep1;
  snd_state *ss = (snd_state *)clientData;
  if (!ccd)
    {
      /* create color chooser dialog window */
      ccd = (color_chooser_info *)calloc(1,sizeof(color_chooser_info));
      ccd->state = ss;

      xdismiss = XmStringCreate(snd_string_Dismiss,XmFONTLIST_DEFAULT_TAG); /* needed by template dialog */
      xhelp = XmStringCreate(snd_string_Help,XmFONTLIST_DEFAULT_TAG);
      titlestr = XmStringCreateLocalized(snd_string_Color_Editor);
      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNcancelLabelString,xdismiss); n++;
      XtSetArg(args[n],XmNhelpLabelString,xhelp); n++;
      XtSetArg(args[n],XmNautoUnmanage,FALSE); n++;
      XtSetArg(args[n],XmNdialogTitle,titlestr); n++;
#if RESIZE_DIALOG
      XtSetArg(args[n],XmNresizePolicy,XmRESIZE_GROW); n++;
      XtSetArg(args[n],XmNnoResize,FALSE); n++;
#endif
#if UNSET_TRANSIENT
      XtSetArg(args[n],XmNtransient,FALSE); n++;
#endif
      ccd->dialog = XmCreateTemplateDialog(main_SHELL(ss),snd_string_Color,args,n);
#if UNSET_TRANSIENT
      add_dialog(ss,ccd->dialog);
#endif

      XtAddCallback(ccd->dialog,XmNcancelCallback,Dismiss_Color_Callback,ccd);
      XtAddCallback(ccd->dialog,XmNhelpCallback,Help_Color_Callback,ccd);
      XmStringFree(xhelp);
      XmStringFree(xdismiss);
      XmStringFree(titlestr);

      if (!(ss->using_schemes))
	{
	  XtVaSetValues(XmMessageBoxGetChild(ccd->dialog,XmDIALOG_CANCEL_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	  XtVaSetValues(XmMessageBoxGetChild(ccd->dialog,XmDIALOG_HELP_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	}

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNbottomWidget,XmMessageBoxGetChild(ccd->dialog,XmDIALOG_SEPARATOR)); n++;
      mainform = XtCreateManagedWidget("formd",xmFormWidgetClass,ccd->dialog,args,n);

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
      list_label = XtCreateManagedWidget(snd_string_colormap,xmLabelWidgetClass,mainform,args,n);
      
      n=0;
      cmaps = (XmString *)calloc(NUM_COLORMAPS,sizeof(XmString));
      for (i=0;i<NUM_COLORMAPS;i++) cmaps[i] = XmStringCreate(colormaps[i],XmFONTLIST_DEFAULT_TAG);
#ifdef LESSTIF_VERSION
      if (!(ss->using_schemes)) n = background_white_color(args,n,ss);
#else
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
#endif
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNtopWidget,list_label); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNlistMarginWidth,3); n++;
      ccd->list = XmCreateScrolledList(mainform,"colormap-list",args,n);
      if (!(ss->using_schemes)) XtVaSetValues(ccd->list,XmNbackground,(ss->sgx)->white,NULL);
      XtVaSetValues(ccd->list,XmNitems,cmaps,XmNitemCount,NUM_COLORMAPS,XmNvisibleItemCount,6,NULL);
      XtAddCallback(ccd->list,XmNbrowseSelectionCallback,List_Color_Callback,ccd);
      for (i=0;i<NUM_COLORMAPS;i++) XmStringFree(cmaps[i]);
      free(cmaps);
      XtManageChild(ccd->list);

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_WIDGET); n++;
#ifdef LESSTIF_VERSION
      XtSetArg(args[n],XmNrightWidget,XtParent(ccd->list)); n++;
#else
      XtSetArg(args[n],XmNrightWidget,ccd->list); n++;
#endif
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
#ifdef LESSTIF_VERSION
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
#else
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNbottomWidget,XmMessageBoxGetChild(ccd->dialog,XmDIALOG_SEPARATOR)); n++;
#endif
      XtSetArg(args[n],XmNseparatorType,XmNO_LINE); n++;
      XtSetArg(args[n],XmNorientation,XmVERTICAL); n++;
      XtSetArg(args[n],XmNwidth,10); n++;
      sep = XtCreateManagedWidget("sep",xmSeparatorWidgetClass,mainform,args,n);

      /* this horizontal separator exists solely to keep the "light" label from clobbering the "dark" label! */
      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNseparatorType,XmNO_LINE); n++;
      XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
      XtSetArg(args[n],XmNwidth,250); n++;
      XtSetArg(args[n],XmNheight,10); n++;
      sep1 = XtCreateManagedWidget("sep1",xmSeparatorWidgetClass,mainform,args,n);

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNrightWidget,sep); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNtopWidget,sep1); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
      XtSetArg(args[n],XmNshowValue,TRUE); n++;
      XtSetArg(args[n],XmNvalue,50); n++;
      ccd->scale = XtCreateManagedWidget("ccdscl",xmScaleWidgetClass,mainform,args,n);
      XtAddCallback(ccd->scale,XmNvalueChangedCallback,Scale_Color_Callback,ccd);
      XtAddCallback(ccd->scale,XmNdragCallback,Scale_Color_Callback,ccd);

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNtopWidget,ccd->scale); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
      light_label = XtCreateManagedWidget(snd_string_light,xmLabelWidgetClass,mainform,args,n);

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNrightWidget,sep); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNtopWidget,ccd->scale); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
      XtCreateManagedWidget(snd_string_dark,xmLabelWidgetClass,mainform,args,n);

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNrightWidget,sep); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNtopWidget,light_label); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
      XtSetArg(args[n],XmNshowValue,TRUE); n++;
      XtSetArg(args[n],XmNmaximum,250); n++;
      XtSetArg(args[n],XmNdecimalPoints,3); n++;
      xcutoff = XmStringCreate(snd_string_cutoff,XmFONTLIST_DEFAULT_TAG);
      XtSetArg(args[n],XmNtitleString,xcutoff); n++;
      XtSetArg(args[n],XmNvalue,(int)(color_cutoff(ss) * 1000)); n++;
      ccd->cutoff = XtCreateManagedWidget("cutoff",xmScaleWidgetClass,mainform,args,n);
      XtAddCallback(ccd->cutoff,XmNvalueChangedCallback,Cutoff_Color_Callback,ccd);
      XtAddCallback(ccd->cutoff,XmNdragCallback,Cutoff_Color_Callback,ccd);
      XmStringFree(xcutoff);

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      if (!(ss->using_schemes)) {XtSetArg(args[n],XmNselectColor,(ss->sgx)->text); n++;}
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNtopWidget,ccd->cutoff); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_NONE); n++;
      XtSetArg(args[n],XmNset,color_inverted(ss)); n++;
      xinvert = XmStringCreate(snd_string_invert,XmFONTLIST_DEFAULT_TAG);
      XtSetArg(args[n],XmNlabelString,xinvert); n++;
      ccd->invert = XtCreateManagedWidget(snd_string_invert,xmToggleButtonWidgetClass,mainform,args,n);
      XtAddCallback(ccd->invert,XmNvalueChangedCallback,Invert_Color_Callback,ccd);
      XmStringFree(xinvert);
#if OVERRIDE_TOGGLE
      override_toggle_translation(ccd->invert);
#endif
      if (color_scale(ss) != 1.0) reflect_color_scale(color_scale(ss));
    }
  else raise_dialog(ccd->dialog);
  if (!XtIsManaged(ccd->dialog)) XtManageChild(ccd->dialog);
}

int color_dialog_is_active(void)
{
  return((ccd) && (ccd->dialog) && (XtIsManaged(ccd->dialog)));
}


/* -------- orientation browser -------- */

typedef struct {
  Widget dialog;
  Widget ax,ay,az,sx,sy,sz,hop,cut; 
  snd_state *state;
} orientation_info;

static orientation_info *oid = NULL;

static void AX_Orientation_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss;
  XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *)callData;
  orientation_info *od = (orientation_info *)clientData;
  ss = od->state;
  in_set_spectro_x_angle(ss,(float)(cbs->value));
  map_over_chans(ss,update_graph,NULL);
}

void set_spectro_x_angle(snd_state *ss, float val)
{
  in_set_spectro_x_angle(ss,val);
  if (oid) XmScaleSetValue(oid->ax,(int)val);
  if (!(ss->graph_hook_active)) map_over_chans(ss,update_graph,NULL);
}

static void AX_Help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,"x angle slider","This slider causes the graph to rotate\naround the x axis.\n");
}

static void AY_Orientation_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss;
  XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *)callData;
  orientation_info *od = (orientation_info *)clientData;
  ss = od->state;
  in_set_spectro_y_angle(ss,(float)(cbs->value));
  map_over_chans(ss,update_graph,NULL);
}

void set_spectro_y_angle(snd_state *ss, float val)
{
  in_set_spectro_y_angle(ss,val);
  if (oid) XmScaleSetValue(oid->ay,(int)val);
  if (!(ss->graph_hook_active)) map_over_chans(ss,update_graph,NULL);
}

static void AY_Help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,"y angle slider","This slider causes the graph to rotate\naround the y axis.\n");
}

static void AZ_Orientation_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss;
  XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *)callData;
  orientation_info *od = (orientation_info *)clientData;
  ss = od->state;
  in_set_spectro_z_angle(ss,(float)(cbs->value));
  map_over_chans(ss,update_graph,NULL);
}

void set_spectro_z_angle(snd_state *ss, float val)
{
  in_set_spectro_z_angle(ss,val);
  if (oid) XmScaleSetValue(oid->az,(int)val);
  if (!(ss->graph_hook_active)) map_over_chans(ss,update_graph,NULL);
}

static void AZ_Help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,"z angle slider","This slider causes the graph to rotate\naround the z axis.\n");
}

static void SX_Orientation_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss;
  XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *)callData;
  orientation_info *od = (orientation_info *)clientData;
  ss = od->state;
  in_set_spectro_x_scale(ss,(float)(cbs->value)*0.02);
  map_over_chans(ss,update_graph,NULL);
}

void set_spectro_x_scale(snd_state *ss, float val)
{
  in_set_spectro_x_scale(ss,val);
  if (oid) XmScaleSetValue(oid->sx,(int)(val*50));
  if (!(ss->graph_hook_active)) map_over_chans(ss,update_graph,NULL);
}

static void SX_Help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,"x scale slider","This slider causes the graph to expand or\ncontract along the x axis.\n");
}

static void SY_Orientation_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss;
  XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *)callData;
  orientation_info *od = (orientation_info *)clientData;
  ss = od->state;
  in_set_spectro_y_scale(ss,(float)(cbs->value)*0.02);
  map_over_chans(ss,update_graph,NULL);
}

void set_spectro_y_scale(snd_state *ss, float val)
{
  in_set_spectro_y_scale(ss,val);
  if (oid) XmScaleSetValue(oid->sy,(int)(val*50));
  if (!(ss->graph_hook_active)) map_over_chans(ss,update_graph,NULL);
}

static void SY_Help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,"y scale slider","This slider causes the graph to expand or\ncontract along the y axis.\n");
}

static void SZ_Orientation_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss;
  XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *)callData;
  orientation_info *od = (orientation_info *)clientData;
  ss = od->state;
  in_set_spectro_z_scale(ss,(float)(cbs->value)*0.01);
  map_over_chans(ss,update_graph,NULL);
}

void set_spectro_z_scale(snd_state *ss, float val)
{
  in_set_spectro_z_scale(ss,val);
  if (oid) XmScaleSetValue(oid->sz,(int)(val*100));
  if (!(ss->graph_hook_active)) map_over_chans(ss,update_graph,NULL);
}

static void SZ_Help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,"z scale slider","This slider causes the graph to expand or\ncontract along the z axis.\n");
}

static void Hop_Orientation_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss;
  XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *)callData;
  orientation_info *od = (orientation_info *)clientData;
  ss = od->state;
  if (cbs->value <= 0) 
    in_set_spectro_hop(ss,1);
  else in_set_spectro_hop(ss,cbs->value);
  map_over_chans(ss,update_graph,NULL);
}

void set_spectro_hop(snd_state *ss, int val)
{
  if (val>0)
    {
      in_set_spectro_hop(ss,val);
      if (oid) XmScaleSetValue(oid->hop,val);
      if (!(ss->graph_hook_active)) map_over_chans(ss,update_graph,NULL);
    }
}

static void Hop_Help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,"hop slider","This slider changes the hop size.\n");
}

static void Cut_Orientation_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  /* y axis limit */
  snd_state *ss;
  XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct *)callData;
  orientation_info *od = (orientation_info *)clientData;
  ss = od->state;
  set_spectro_cutoff_and_redisplay(ss,(float)(cbs->value)*0.01); /* calls in_set... */
} 

void set_spectro_cutoff(snd_state *ss, float val)
{
  in_set_spectro_cutoff(ss,val);
  if (oid) XmScaleSetValue(oid->cut,(int)(val*100));
  if (!(ss->graph_hook_active)) map_over_chans(ss,update_graph,NULL);
}

static void Cut_Help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,"% of spectrum slider","This slider determines how much of\nthe spectrum is displayed\n");
}

static void Help_Orientation_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
       "View Orientation",
"This dialog sets the rotation and scaling\n\
variables used during sonogram, spectrogram,\n\
and wavogram display.\n\
");	   
}

static void Dismiss_Orientation_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  orientation_info *od = (orientation_info *)clientData;
  XtUnmanageChild(od->dialog);
}

static int fixup_angle(float ang)
{
  int na;
  na = ang;
  na = na%360;
  if (na < 0) na+=360;
  return(na);
}

void reflect_spectro(snd_state *ss)
{
  /* set color/orientaton widget values */
  if (ccd) 
    {
      XmToggleButtonSetState(ccd->invert,color_inverted(ss),FALSE);
      XtVaSetValues(ccd->cutoff,XmNvalue,(int)((color_cutoff(ss))*1000),NULL);
      reflect_color_scale(color_scale(ss));
    }
  if (oid) 
    {
      XtVaSetValues(oid->ax,XmNvalue,fixup_angle(spectro_x_angle(ss)),NULL);
      XtVaSetValues(oid->ay,XmNvalue,fixup_angle(spectro_y_angle(ss)),NULL);
      XtVaSetValues(oid->az,XmNvalue,fixup_angle(spectro_z_angle(ss)),NULL);
      XtVaSetValues(oid->sx,XmNvalue,(int)(spectro_x_scale(ss) * 50),NULL);
      XtVaSetValues(oid->sy,XmNvalue,(int)(spectro_y_scale(ss) * 50),NULL);
      XtVaSetValues(oid->sz,XmNvalue,(int)(spectro_z_scale(ss) * 100),NULL);
      XtVaSetValues(oid->hop,XmNvalue,(spectro_hop(ss) > 100) ? 100 : (spectro_hop(ss)),NULL);
      XtVaSetValues(oid->cut,XmNvalue,(int)(spectro_cutoff(ss) * 100),NULL);
    }
}

static void Reset_Orientation_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss;
  orientation_info *od = (orientation_info *)clientData;
  /* put everything back the way it was at the start */
  ss = od->state;
  reset_spectro(ss);
  reflect_spectro(ss);
  map_over_chans(ss,update_graph,NULL);
}

void View_Orientation_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  snd_state *ss = (snd_state *)clientData;
  Widget mainform,rightbox,leftbox;
  XmString xdismiss,xhelp,xstr,xreset,titlestr;
  int n;
  Arg args[20];
  if (!oid)
    {
      /* create orientation window */
      oid = (orientation_info *)calloc(1,sizeof(orientation_info));
      oid->state = ss;

      xdismiss = XmStringCreate(snd_string_Dismiss,XmFONTLIST_DEFAULT_TAG); /* needed by template dialog */
      xhelp = XmStringCreate(snd_string_Help,XmFONTLIST_DEFAULT_TAG);
      xreset = XmStringCreate(snd_string_Reset,XmFONTLIST_DEFAULT_TAG);
      titlestr = XmStringCreateLocalized(snd_string_FFT_Orientation);
      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNcancelLabelString,xdismiss); n++;
      XtSetArg(args[n],XmNhelpLabelString,xhelp); n++;
      XtSetArg(args[n],XmNokLabelString,xreset); n++;
      XtSetArg(args[n],XmNautoUnmanage,FALSE); n++;
      XtSetArg(args[n],XmNdialogTitle,titlestr); n++;
#if RESIZE_DIALOG
      XtSetArg(args[n],XmNresizePolicy,XmRESIZE_GROW); n++;
      XtSetArg(args[n],XmNnoResize,FALSE); n++;
#endif
#if UNSET_TRANSIENT
      XtSetArg(args[n],XmNtransient,FALSE); n++;
#endif
      oid->dialog = XmCreateTemplateDialog(main_SHELL(ss),snd_string_Orientation,args,n);
#if UNSET_TRANSIENT
      add_dialog(ss,oid->dialog);
#endif

      XtAddCallback(oid->dialog,XmNcancelCallback,Dismiss_Orientation_Callback,oid);
      XtAddCallback(oid->dialog,XmNhelpCallback,Help_Orientation_Callback,ss);
      XtAddCallback(oid->dialog,XmNokCallback,Reset_Orientation_Callback,oid);
      XmStringFree(xhelp);
      XmStringFree(xdismiss);
      XmStringFree(titlestr);
      XmStringFree(xreset);

      if (!(ss->using_schemes))
	{
	  XtVaSetValues(XmMessageBoxGetChild(oid->dialog,XmDIALOG_OK_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	  XtVaSetValues(XmMessageBoxGetChild(oid->dialog,XmDIALOG_CANCEL_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	  XtVaSetValues(XmMessageBoxGetChild(oid->dialog,XmDIALOG_HELP_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	}

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNbottomWidget,XmMessageBoxGetChild(oid->dialog,XmDIALOG_SEPARATOR)); n++;
      mainform = XtCreateManagedWidget("formd",xmFormWidgetClass,oid->dialog,args,n);
      
      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_POSITION); n++;
      XtSetArg(args[n],XmNrightPosition,50); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNorientation,XmVERTICAL); n++;
      leftbox = XtCreateManagedWidget("leftb",xmRowColumnWidgetClass,mainform,args,n);
      
      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNleftAttachment,XmATTACH_WIDGET); n++;
      XtSetArg(args[n],XmNleftWidget,leftbox); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNtopAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
      XtSetArg(args[n],XmNorientation,XmVERTICAL); n++;
      rightbox = XtCreateManagedWidget("rightb",xmRowColumnWidgetClass,mainform,args,n);
      
      /* left box */
      n=0;
      xstr = XmStringCreate(snd_string_x_angle,XmFONTLIST_DEFAULT_TAG);
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
      XtSetArg(args[n],XmNshowValue,TRUE); n++;
      XtSetArg(args[n],XmNvalue,fixup_angle(spectro_x_angle(ss))); n++;
      XtSetArg(args[n],XmNmaximum,360); n++;
      XtSetArg(args[n],XmNtitleString,xstr); n++;
      oid->ax = XtCreateManagedWidget("ax",xmScaleWidgetClass,leftbox,args,n);
      XtAddCallback(oid->ax,XmNvalueChangedCallback,AX_Orientation_Callback,oid);
      XtAddCallback(oid->ax,XmNdragCallback,AX_Orientation_Callback,oid);
      XtAddCallback(oid->ax,XmNhelpCallback,AX_Help_Callback,ss);
      XmStringFree(xstr);

      n=0;
      xstr = XmStringCreate(snd_string_y_angle,XmFONTLIST_DEFAULT_TAG);
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
      XtSetArg(args[n],XmNshowValue,TRUE); n++;
      XtSetArg(args[n],XmNvalue,fixup_angle(spectro_y_angle(ss))); n++;
      XtSetArg(args[n],XmNmaximum,360); n++;
      XtSetArg(args[n],XmNtitleString,xstr); n++;
      oid->ay = XtCreateManagedWidget("ay",xmScaleWidgetClass,leftbox,args,n);
      XtAddCallback(oid->ay,XmNvalueChangedCallback,AY_Orientation_Callback,oid);
      XtAddCallback(oid->ay,XmNdragCallback,AY_Orientation_Callback,oid);
      XtAddCallback(oid->ay,XmNhelpCallback,AY_Help_Callback,ss);
      XmStringFree(xstr);

      n=0;
      xstr = XmStringCreate(snd_string_z_angle,XmFONTLIST_DEFAULT_TAG);
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
      XtSetArg(args[n],XmNshowValue,TRUE); n++;
      XtSetArg(args[n],XmNvalue,fixup_angle(spectro_z_angle(ss))); n++;
      XtSetArg(args[n],XmNmaximum,360); n++;
      XtSetArg(args[n],XmNtitleString,xstr); n++;
      oid->az = XtCreateManagedWidget("az",xmScaleWidgetClass,leftbox,args,n);
      XtAddCallback(oid->az,XmNvalueChangedCallback,AZ_Orientation_Callback,oid);
      XtAddCallback(oid->az,XmNdragCallback,AZ_Orientation_Callback,oid);
      XtAddCallback(oid->az,XmNhelpCallback,AZ_Help_Callback,ss);
      XmStringFree(xstr);

      n=0;
      xstr = XmStringCreate(snd_string_hop,XmFONTLIST_DEFAULT_TAG);
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
      XtSetArg(args[n],XmNshowValue,TRUE); n++;
      XtSetArg(args[n],XmNvalue,(spectro_hop(ss) > 100) ? 100 : (spectro_hop(ss))); n++;
      XtSetArg(args[n],XmNmaximum,100); n++;
      XtSetArg(args[n],XmNtitleString,xstr); n++;
      oid->hop = XtCreateManagedWidget("hop",xmScaleWidgetClass,leftbox,args,n);
      XtAddCallback(oid->hop,XmNvalueChangedCallback,Hop_Orientation_Callback,oid);
      XtAddCallback(oid->hop,XmNdragCallback,Hop_Orientation_Callback,oid);
      XtAddCallback(oid->hop,XmNhelpCallback,Hop_Help_Callback,ss);
      XmStringFree(xstr);

      /* right box */
      n=0;
      xstr = XmStringCreate(snd_string_x_scale,XmFONTLIST_DEFAULT_TAG);
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
      XtSetArg(args[n],XmNshowValue,TRUE); n++;
      XtSetArg(args[n],XmNvalue,(int)(spectro_x_scale(ss) * 50)); n++;
      XtSetArg(args[n],XmNtitleString,xstr); n++;
      oid->sx = XtCreateManagedWidget("xs",xmScaleWidgetClass,rightbox,args,n);
      XtAddCallback(oid->sx,XmNvalueChangedCallback,SX_Orientation_Callback,oid);
      XtAddCallback(oid->sx,XmNdragCallback,SX_Orientation_Callback,oid);
      XtAddCallback(oid->sx,XmNhelpCallback,SX_Help_Callback,ss);
      XmStringFree(xstr);

      n=0;
      xstr = XmStringCreate(snd_string_y_scale,XmFONTLIST_DEFAULT_TAG);
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
      XtSetArg(args[n],XmNshowValue,TRUE); n++;
      XtSetArg(args[n],XmNvalue,(int)(spectro_y_scale(ss) * 50)); n++;
      XtSetArg(args[n],XmNtitleString,xstr); n++;
      oid->sy = XtCreateManagedWidget("ys",xmScaleWidgetClass,rightbox,args,n);
      XtAddCallback(oid->sy,XmNvalueChangedCallback,SY_Orientation_Callback,oid);
      XtAddCallback(oid->sy,XmNdragCallback,SY_Orientation_Callback,oid);
      XtAddCallback(oid->sy,XmNhelpCallback,SY_Help_Callback,ss);
      XmStringFree(xstr);

      n=0;
      xstr = XmStringCreate(snd_string_z_scale,XmFONTLIST_DEFAULT_TAG);
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
      XtSetArg(args[n],XmNshowValue,TRUE); n++;
      XtSetArg(args[n],XmNvalue,(int)(spectro_z_scale(ss) * 100)); n++;
      XtSetArg(args[n],XmNtitleString,xstr); n++;
      oid->sz = XtCreateManagedWidget("zs",xmScaleWidgetClass,rightbox,args,n);
      XtAddCallback(oid->sz,XmNvalueChangedCallback,SZ_Orientation_Callback,oid);
      XtAddCallback(oid->sz,XmNdragCallback,SZ_Orientation_Callback,oid);
      XtAddCallback(oid->sz,XmNhelpCallback,SZ_Help_Callback,ss);
      XmStringFree(xstr);

      n=0;
      xstr = XmStringCreate(snd_string_percent_of_spectrum,XmFONTLIST_DEFAULT_TAG);
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;
      XtSetArg(args[n],XmNshowValue,TRUE); n++;
      XtSetArg(args[n],XmNvalue,(int)(spectro_cutoff(ss) * 100)); n++;
      XtSetArg(args[n],XmNtitleString,xstr); n++;
      oid->cut = XtCreateManagedWidget("cut",xmScaleWidgetClass,rightbox,args,n);
      XtAddCallback(oid->cut,XmNvalueChangedCallback,Cut_Orientation_Callback,oid);
      XtAddCallback(oid->cut,XmNdragCallback,Cut_Orientation_Callback,oid);
      XtAddCallback(oid->cut,XmNhelpCallback,Cut_Help_Callback,ss);
      XmStringFree(xstr);
    }
  else raise_dialog(oid->dialog);
  if (!XtIsManaged(oid->dialog)) XtManageChild(oid->dialog);
}

int orientation_dialog_is_active(void)
{
  return((oid) && (oid->dialog) && (XtIsManaged(oid->dialog)));
}

void start_color_dialog(snd_state *ss, int width, int height)
{
  View_Color_Callback(NULL,(XtPointer)ss,NULL);
  if (width != 0) XtVaSetValues(ccd->dialog,XmNwidth,(Dimension)width,XmNheight,(Dimension)height,NULL);
}

void start_orientation_dialog(snd_state *ss, int width, int height)
{
  View_Orientation_Callback(NULL,(XtPointer)ss,NULL);
  if (width != 0) XtVaSetValues(oid->dialog,XmNwidth,(Dimension)width,XmNheight,(Dimension)height,NULL);
}

#if HAVE_GUILE
  #define SETF "set!"
#else
  #define SETF "setf"
#endif

void start_file_dialog(snd_state *ss, int width, int height)
{
  View_Files_Callback(NULL,(XtPointer)ss,NULL);
  if (width > 0) XtVaSetValues(fbd->dialog,XmNwidth,(Dimension)width,XmNheight,(Dimension)height,NULL);
}

int file_dialog_is_active(void)
{
  return((fbd) && (fbd->dialog) && (XtIsManaged(fbd->dialog)));
}


static Widget file_mix_dialog = NULL;
static Widget file_mix_name = NULL;

static void file_mix_help_callback(Widget w,XtPointer clientData,XtPointer callData)
{
  snd_help((snd_state *)clientData,
	   "File Mix",
"The file you specify to the mix file prompt\n\
will be mixed into the current active sound at\n\
the current cursor location of the active channel.\n\
The equivalent keyboard command is C-x C-q.\n\
");
}

static void file_mix_cancel_callback(Widget w,XtPointer clientData,XtPointer callData)
{
  XtUnmanageChild(w);
}

static void file_mix_ok_callback(Widget w,XtPointer clientData,XtPointer callData)
{
  XtUnmanageChild(w);
  mix_complete_file(any_selected_sound((snd_state *)clientData),XmTextGetString(file_mix_name),"File: mix");
}

void File_Mix_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  Arg args[20];
  int n;
  XmString s1;
  snd_state *ss = (snd_state *)clientData;
  if (!file_mix_dialog)
    {
      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
#if RESIZE_DIALOG
      XtSetArg(args[n],XmNresizePolicy,XmRESIZE_GROW); n++;
      XtSetArg(args[n],XmNnoResize,FALSE); n++;
#endif
      s1 = XmStringCreate(snd_string_mix_in_p,XmFONTLIST_DEFAULT_TAG);
      XtSetArg(args[n],XmNselectionLabelString,s1); n++;
      file_mix_dialog = XmCreateFileSelectionDialog(w,snd_string_mix_file_p,args,n);
      XmStringFree(s1);
      XtAddCallback(file_mix_dialog,XmNhelpCallback,file_mix_help_callback,ss);
      XtAddCallback(file_mix_dialog,XmNcancelCallback,file_mix_cancel_callback,ss);
      XtAddCallback(file_mix_dialog,XmNokCallback,file_mix_ok_callback,ss);
      XtManageChild(file_mix_dialog);
      color_file_selection_box(file_mix_dialog,ss);
#ifndef LESSTIF_VERSION
      file_mix_name = XtNameToWidget(file_mix_dialog,"Text");
#else
      file_mix_name = XmFileSelectionBoxGetChild(file_mix_dialog,XmDIALOG_TEXT);
#endif

    }
  if (!XtIsManaged(file_mix_dialog)) XtManageChild(file_mix_dialog);
}

void snd_edit_save_as_dialog(Widget w, snd_state *ss)
{
  XmString xmstr2;
  finish_keyboard_selection();
  if (!save_as_dialog)
    {
      save_as_dialog = (save_as_info *)calloc(1,sizeof(save_as_info));
      save_as_dialog->state = ss;
      make_save_as_dialog(w,save_as_dialog,snd_string_current_selection,NULL);
    }
  else
    {
      sprintf(file_string,snd_string_saving,snd_string_current_selection);
      xmstr2 = XmStringCreateLocalized(file_string);
      XtVaSetValues(save_as_dialog->dialog,XmNmessageString,xmstr2,NULL);
      XmStringFree(xmstr2);
      load_header_and_data_lists(save_as_dialog->header_list,save_as_dialog->data_list,
				 0,1,
				 &(save_as_dialog->header_choice),&(save_as_dialog->data_choice));
    }
  save_as_dialog->type = EDIT_SAVE_AS;
  if (!XtIsManaged(save_as_dialog->dialog)) XtManageChild(save_as_dialog->dialog);
}


/* ---------------- EDIT_HEADER ---------------- */

static Widget edit_header_dialog = NULL;
static char edit_string[128];
static Widget *edit_header_data;

static void edit_header_help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
       snd_string_Edit_Header,
"This dialog edits the header of a sound file.\n\
No change is made to the actual sound data; the\n\
new header is blindly written, any unsaved edits\n\
are ignored. If you specify 'raw' as the type,\n\
any existing header is removed.  This dialog is\n\
aimed at adding or removing an entire header, \n\
or editing the header comments; anything else\n\
is obviously dangerous.\n\
");
}

static void edit_header_cancel_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  XtUnmanageChild(edit_header_dialog);
}

#define RIPPLE_SIZE 65536
/* needs to be big enough to accomodate any newly added header or header comments */

static void edit_header_ok_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  unsigned char *ripple0,*ripple1,*zerobuf;
  int fd,err,chans,srate,loc,comlen,type,format,bytes0,bytes1,curloc,readloc,writeloc,curbytes,totalbytes;
  char *comment,*str;
  snd_state *ss;
  file_info *hdr;
  snd_info *sp = (snd_info *)clientData;
  XmAnyCallbackStruct *cb = (XmAnyCallbackStruct *)callData;
  ss = sp->state;
  if (cb->event == ((ss->sgx)->text_activate_event)) return; /* <cr> in one of text fields */
  if (!(sp->read_only))
    {
      err = access(sp->fullname,W_OK);
      if (err == 0)
	{
	  fd = open(sp->fullname,O_RDWR,0);
	  if (fd != -1)
	    {
	      hdr = sp->hdr;
	      ripple0 = (unsigned char *)calloc(RIPPLE_SIZE,sizeof(unsigned char));
	      ripple1 = (unsigned char *)calloc(RIPPLE_SIZE,sizeof(unsigned char));
	      lseek(fd,hdr->data_location,0L);
	      bytes0 = read(fd,ripple0,RIPPLE_SIZE);
	      if (bytes0 == RIPPLE_SIZE) bytes1 = read(fd,ripple1,RIPPLE_SIZE); else bytes1 = -1;
	      srate = hdr->srate;
	      chans = hdr->chans;
	      type = hdr->type;
	      format = hdr->format;
	      read_file_data_choices(edit_header_data[fdata_stext], edit_header_data[fdata_ctext], 
				     edit_header_data[fdata_hlist], edit_header_data[fdata_dlist], 
				     &srate, &chans, &type, &format);
	      str = XmTextGetString(edit_header_data[fdata_loctext]); 
	      if (str) sscanf(str,"%d",&loc); else loc = hdr->data_location;
	      comment = XmTextGetString(edit_header_data[fdata_comment_text]);
	      comlen = snd_strlen(comment);
	      curloc = 0;
	      curbytes = 0;
	      totalbytes = hdr->samples * c_snd_datum_size(hdr->format);
	      lseek(fd,0,0L);
	      if (type != raw_sound_file)
		{
		  c_write_header_with_fd(fd,type,srate,chans,loc,hdr->samples,format,comment,comlen);
		  curloc = c_snd_header_data_location();
		  if ((loc != curloc) && (loc != hdr->data_location)) /* user changed it */
		    {
		      /* pad if possible ? */
		      if (loc > curloc)
			{
			  zerobuf = (unsigned char *)calloc(loc-curloc,sizeof(unsigned char));
			  write(fd,zerobuf,loc-curloc);
			  free(zerobuf);
			  curloc = loc;
			}
		    }
		}
	      readloc = RIPPLE_SIZE * 2;
	      writeloc = curloc;
	      if (writeloc > readloc) fprintf(stderr,"uh oh");
	      snd_printf(ss,"rippling...");
	      while (bytes0 > 0)
		{
		  write(fd,ripple0,bytes0);
		  curbytes += bytes0;
		  writeloc += RIPPLE_SIZE;
		  if (bytes1 > 0)
		    {
		      lseek(fd,readloc,0);
		      readloc += RIPPLE_SIZE;
		      bytes0 = read(fd,ripple0,RIPPLE_SIZE);
		      lseek(fd,writeloc,0);
		      writeloc += RIPPLE_SIZE;
		      write(fd,ripple1,bytes1);
		      curbytes += bytes1;
		      if (bytes0 > 0)
			{
			  lseek(fd,readloc,0);
			  readloc += RIPPLE_SIZE;
			  bytes1 = read(fd,ripple1,RIPPLE_SIZE);
			}
		    }
		  if (curbytes > totalbytes) break; /* ?? this should not happen */
		}
	      close(fd);
	      clear_minibuffer(sp);
	      snd_file_bomb_icon(sp,TRUE);
	      free(ripple0);
	      free(ripple1);
	      free(comment);
	      free(str);
	    }
	  else 
	    {
	      sprintf(edit_string,snd_string_cant_open,sp->shortname);
	      snd_printf(ss,edit_string);
	    }
	}
      else 
	{
	  sprintf(edit_string,snd_string_cant_write,sp->shortname);
	  snd_printf(ss,edit_string);
	}
    }
  else
    {
      sprintf(edit_string,snd_string_is_write_protected,sp->shortname);
      snd_printf(ss,edit_string);
    }
  XtUnmanageChild(edit_header_dialog);
}

static int current_type_pos,current_format_pos;

static void edit_header_type_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  int pos;
  file_info *hdr;
  XmListCallbackStruct *cbs = (XmListCallbackStruct *)callData;
  pos = cbs->item_position;
  if (current_type_pos != pos)
    {
      hdr = (file_info *)calloc(1,sizeof(file_info));
      hdr->comment = NULL;
      switch (pos)
	{
	case 1: hdr->type = NeXT_sound_file; hdr->format = snd_16_linear; break;
	case 2: hdr->type = AIFF_sound_file; hdr->format = snd_16_linear; break;
	case 3: hdr->type = RIFF_sound_file; hdr->format = snd_16_linear_little_endian; break;
	case 4: hdr->type = IRCAM_sound_file; hdr->format = snd_16_linear; break;
	case 5: hdr->type = raw_sound_file; hdr->format = snd_16_linear; break;
	}
      load_header_and_data_lists(edit_header_data[fdata_hlist], edit_header_data[fdata_dlist],hdr->type,hdr->format,&current_type_pos,&current_format_pos);
      free(hdr);
    }
}

static void edit_header_format_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  XmListCallbackStruct *cbs = (XmListCallbackStruct *)callData;
  current_format_pos = cbs->item_position;
}

void edit_header(snd_info *sp)
{
  /* like display-info, but writable.
   * need fields for srate, channels, type, format, data location, comment
   * if any are changed, need save button, cancel button, dismiss (leave unsaved but pending), reflect (change Snd display, not file)
   * this means the Snd-effective header is separate from the in-file header even across saves??
   */
  XmString xstr1,xstr2,xstr3,xstr4,titlestr;
  int n;
  Arg args[20];
  snd_state *ss;
  file_info *hdr;
  if (!sp) return;
  ss = sp->state;
  hdr = sp->hdr;

  if (!edit_header_dialog)
    {
      n=0;
      xstr1 = XmStringCreate(snd_string_Cancel,XmFONTLIST_DEFAULT_TAG); /* needed by template dialog */
      xstr2 = XmStringCreate(snd_string_Help,XmFONTLIST_DEFAULT_TAG);
      xstr3 = XmStringCreate(snd_string_Save,XmFONTLIST_DEFAULT_TAG);
      titlestr = XmStringCreateLocalized(snd_string_Edit_Header);
      sprintf(edit_string,snd_string_Edit_header_of,sp->shortname);
      xstr4 = XmStringCreate(edit_string,XmFONTLIST_DEFAULT_TAG);
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      XtSetArg(args[n],XmNcancelLabelString,xstr1); n++;
      XtSetArg(args[n],XmNhelpLabelString,xstr2); n++;
      XtSetArg(args[n],XmNokLabelString,xstr3); n++;
      XtSetArg(args[n],XmNmessageString,xstr4); n++;
      XtSetArg(args[n],XmNdialogTitle,titlestr); n++;
      XtSetArg(args[n],XmNautoUnmanage,FALSE); n++;
#if RESIZE_DIALOG
      XtSetArg(args[n],XmNresizePolicy,XmRESIZE_GROW); n++;
      XtSetArg(args[n],XmNnoResize,FALSE); n++;
#endif
#if UNSET_TRANSIENT
      XtSetArg(args[n],XmNtransient,FALSE); n++;
#endif
      edit_header_dialog = XmCreateTemplateDialog(main_SHELL(ss),snd_string_Edit_Header,args,n);
#if UNSET_TRANSIENT
      add_dialog(ss,edit_header_dialog);
#endif
      XtAddCallback(edit_header_dialog,XmNcancelCallback,edit_header_cancel_Callback,ss);
      XtAddCallback(edit_header_dialog,XmNhelpCallback,edit_header_help_Callback,ss);
      XtAddCallback(edit_header_dialog,XmNokCallback,edit_header_ok_Callback,sp);
      XmStringFree(xstr1);
      XmStringFree(xstr2);
      XmStringFree(xstr3);
      XmStringFree(xstr4);
      XmStringFree(titlestr);

      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      edit_header_data = sndCreateFileDataForm(ss,edit_header_dialog,snd_string_Edit_Header,args,n,TRUE,TRUE,hdr->type,hdr->format,TRUE);
      load_header_and_data_lists(edit_header_data[fdata_hlist],edit_header_data[fdata_dlist],hdr->type,hdr->format,&current_type_pos,&current_format_pos);
      XtVaSetValues(edit_header_data[fdata_comment_text],XmNvalue,(hdr->comment) ? (hdr->comment) : "",NULL);
      sprintf(edit_string,"%d",hdr->data_location);
      XtVaSetValues(edit_header_data[fdata_loctext],XmNvalue,edit_string,NULL);
      sprintf(edit_string,"%d",hdr->srate);
      XtVaSetValues(edit_header_data[fdata_stext],XmNvalue,edit_string,NULL);
      sprintf(edit_string,"%d",hdr->chans);
      XtVaSetValues(edit_header_data[fdata_ctext],XmNvalue,edit_string,NULL);

      XtAddCallback(edit_header_data[fdata_hlist],XmNbrowseSelectionCallback,edit_header_type_Callback,NULL);
      XtAddCallback(edit_header_data[fdata_dlist],XmNbrowseSelectionCallback,edit_header_format_Callback,NULL);

#if MANAGE_DIALOG
      XtManageChild(edit_header_dialog);
#endif
      if (!(ss->using_schemes)) 
	{
	  XtVaSetValues(XmMessageBoxGetChild(edit_header_dialog,XmDIALOG_OK_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	  XtVaSetValues(XmMessageBoxGetChild(edit_header_dialog,XmDIALOG_CANCEL_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	  XtVaSetValues(XmMessageBoxGetChild(edit_header_dialog,XmDIALOG_HELP_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	}
      map_over_children(edit_header_dialog,set_main_color_of_widget,ss);
      XtVaSetValues(edit_header_data[fdata_hlist],XmNbackground,(ss->sgx)->white,NULL);
      XtVaSetValues(edit_header_data[fdata_dlist],XmNbackground,(ss->sgx)->white,NULL);
    }
  else raise_dialog(edit_header_dialog);
  if (!(XtIsManaged(edit_header_dialog))) XtManageChild(edit_header_dialog);
}
