#include "snd.h"

#ifndef VMS
#if defined(NEXT) || defined(HAVE_SYS_DIR_H)
  #include <sys/dir.h>
  #include <sys/dirent.h>
#else
  #include <dirent.h>
#endif
#else
#include "vms_dirent.h"
#endif

#ifndef VMS
#define INIT_FILE_NAME "~/.snd"
#else
#define INIT_FILE_NAME "sys$login:.snd"
#endif
#define EPS_FILE_NAME "snd.eps"
#define FALLBACK_FONT "fixed"

/* our default basic colors (resource file can override these): */
#define LIGHTEST_COLOR   "ivory1"
#define BASIC_COLOR      "ivory2"
#define DARK_COLOR       "ivory3"
#define DARKEST_COLOR    "ivory4"
#define TEXT_COLOR       "lightsteelblue1"
#define LIGHT_BLUE_COLOR "AliceBlue"
#ifdef SGI
#define CURSOR_COLOR     "cyan"
#else
#define CURSOR_COLOR     "red"
#endif
#define SELECTION_COLOR  "ivory4"
#define MIXER_COLOR      "lightgreen"
#define MIXER_HIGH_COLOR "green2"
#define GROUP_COLOR      "red"
#define YELLOW_COLOR     "yellow"
#define DEFAULT_SPECTROGRAM_COLOR -1 /* -1 = just lines or index */
#define MIXER_GROUPS 6

#if HAVE_XmHTML
/* XmHTML defaults */
#define HTML_WIDTH 600
#define HTML_HEIGHT 400
#define HTML_DIR "."
#define HTML_FONT_SIZE_LIST "14,10,24,24,18,14,12"
#define HTML_FIXED_FONT_SIZE_LIST "14,10"
#endif

/* we assume later that we can always find these fonts (if resource file gives bogus entry, we fall back on these) */
#ifdef SGI
  #define BUTTON_FONT "-*-times-medium-r-*-*-14-*-*-*-*-*-iso8859-1"
  #define BOLD_BUTTON_FONT "-*-times-bold-r-*-*-14-*-*-*-*-*-iso8859-1"
  #define AXIS_LABEL_FONT "-*-times-medium-r-normal-*-20-*-*-*-*-*-iso8859-1"
  #define AXIS_NUMBERS_FONT "-*-courier-medium-r-normal-*-14-*-*-*-*-*-iso8859-1"
  #define HELP_TEXT_FONT "9x15"
  #define ICON_TYPE 1
#else
#if defined(LINUX) || defined(SCO5) || defined(UW2) || defined(SOLARIS) || defined(HPUX) || defined(ALPHA)
  #define BUTTON_FONT "-*-times-medium-r-*-*-12-*-*-*-*-*-iso8859-1"
  #define BOLD_BUTTON_FONT "-*-times-bold-r-*-*-12-*-*-*-*-*-iso8859-1"
  #define AXIS_LABEL_FONT "-*-times-medium-r-normal-*-16-*-*-*-*-*-iso8859-1"
  #define AXIS_NUMBERS_FONT "-*-courier-medium-r-normal-*-12-*-*-*-*-*-iso8859-1"
  #define HELP_TEXT_FONT "8x13"
  #define ICON_TYPE 2
#else
/* ICON_TYPE 1 => use plain bitmap, 2 => use XPM bitmap, 0 => no icon */
  #define BUTTON_FONT "-*-times-medium-r-*-*-14-*-*-*-*-*-iso8859-1"
  #define BOLD_BUTTON_FONT "-*-times-bold-r-*-*-14-*-*-*-*-*-iso8859-1"
  #define AXIS_LABEL_FONT "-*-times-medium-r-normal-*-20-*-*-*-*-*-iso8859-1"
  #define AXIS_NUMBERS_FONT "-*-courier-medium-r-normal-*-14-*-*-*-*-*-iso8859-1"
  #define HELP_TEXT_FONT "9x15"
  #define CLOSED_CTRLS_HEIGHT 22
  #define OPEN_CTRLS_HEIGHT 185
  #define ICON_TYPE 0
#endif
#endif

#define CHANNEL_MIN_HEIGHT 150  /* open size (set to 5 immediately thereafter) */
                                /* paned window default setup is not very smart, so we force these to be this size to begin with */
                                /* this number is only a first approximation -- we try not to expand below the screen */
                                /* if too small (i.e. 100), the scrollbars are sometimes messed up on the initial layout */

#define MAIN_SASH_SIZE 14
#define MAIN_SASH_INDENT -6
#define AUTO_RESIZE_DEFAULT 1

/* /usr/lib/X11/rgb.txt, /usr/lib/X11/fonts/Type1/fonts.dir, /usr/lib/X11/fonts/misc */
/* schemes are in /usr/lib/X11/schemes */

#define HELP_ROWS 12
#define HELP_COLUMNS 56
/* these set the initial size of the (non XmHTML) help dialog text area */

typedef struct {
  char *lightest_color;
  char *basic_color;
  char *dark_color;
  char *darkest_color;
  char *text_color;
  char *cursor_color;
  char *selection_color;
  char *mixer_color;
  char *mixer_high_color;
  char *group_color;
  char *yellow_color;
  char *light_blue_color;
  char *use_schemes;
  char *button_font;
  char *listener_font;
  char *bold_button_font;
  char *axis_label_font;
  char *axis_numbers_font;
  char *help_text_font;
  char *init_file_name;
  char *eps_file_name;
  int def_output_type;
  int spectrogram_color;
  int overwrite_check;
  int ngroups;
  int auto_resize;
  int group_out_chans;
  int horizontal_panes;
#if HAVE_XmHTML
  int html_width;
  int html_height;
  char *html_dir;
  char *html_font_size_list;
  char *html_fixed_font_size_list;
#endif
} sndres;

static XtResource resources[] = {
  {"lightestcolor","Lightestcolor",XmRString,sizeof(char *),XtOffset(sndres *,lightest_color),XmRString,LIGHTEST_COLOR},
  {"basiccolor","Basiccolor",XmRString,sizeof(char *),XtOffset(sndres *,basic_color),XmRString,BASIC_COLOR},
  {"darkcolor","Darkcolor",XmRString,sizeof(char *),XtOffset(sndres *,dark_color),XmRString,DARK_COLOR},
  {"darkestcolor","Darkestcolor",XmRString,sizeof(char *),XtOffset(sndres *,darkest_color),XmRString,DARKEST_COLOR},
  {"textcolor","Textcolor",XmRString,sizeof(char *),XtOffset(sndres *,text_color),XmRString,TEXT_COLOR},
  {"cursorcolor","Cursorcolor",XmRString,sizeof(char *),XtOffset(sndres *,cursor_color),XmRString,CURSOR_COLOR},
  {"selectioncolor","Selectioncolor",XmRString,sizeof(char *),XtOffset(sndres *,selection_color),XmRString,SELECTION_COLOR},
  {"mixercolor","Mixercolor",XmRString,sizeof(char *),XtOffset(sndres *,mixer_color),XmRString,MIXER_COLOR},
  {"mixerhighcolor","MixerHighcolor",XmRString,sizeof(char *),XtOffset(sndres *,mixer_high_color),XmRString,MIXER_HIGH_COLOR},
  {"groupcolor","Groupcolor",XmRString,sizeof(char *),XtOffset(sndres *,group_color),XmRString,GROUP_COLOR},
  {"yellowcolor","Yellowcolor",XmRString,sizeof(char *),XtOffset(sndres *,yellow_color),XmRString,YELLOW_COLOR},
  {"lightbluecolor","LightBluecolor",XmRString,sizeof(char *),XtOffset(sndres *,light_blue_color),XmRString,LIGHT_BLUE_COLOR},
  {"useSchemes","UseSchemes",XmRString,sizeof(char *),XtOffset(sndres *,use_schemes),XmRString,"none"},
  {"buttonFont","ButtonFont",XmRString,sizeof(char *),XtOffset(sndres *,button_font),XmRString,BUTTON_FONT},
  {"listenerFont","ListenerFont",XmRString,sizeof(char *),XtOffset(sndres *,listener_font),XmRString,NULL},
  {"boldbuttonFont","BoldbuttonFont",XmRString,sizeof(char *),XtOffset(sndres *,bold_button_font),XmRString,BOLD_BUTTON_FONT},
  {"axisLabelFont","AxisLabelFont",XmRString,sizeof(char *),XtOffset(sndres *,axis_label_font),XmRString,AXIS_LABEL_FONT},
  {"axisNumbersFont","AxisNumbersFont",XmRString,sizeof(char *),XtOffset(sndres *,axis_numbers_font),XmRString,AXIS_NUMBERS_FONT},
  {"helpTextFont","HelpTextFont",XmRString,sizeof(char *),XtOffset(sndres *,help_text_font),XmRString,HELP_TEXT_FONT},
  {"initFile","InitFile",XmRString,sizeof(char *),XtOffset(sndres *,init_file_name),XmRString,INIT_FILE_NAME},
  {"epsFile","EpsFile",XmRString,sizeof(char *),XtOffset(sndres *,eps_file_name),XmRString,EPS_FILE_NAME},
  {"defaultOutputType","DefaultOutputType",XmRInt,sizeof(int),XtOffset(sndres *,def_output_type),XmRImmediate,(XtPointer)DEFAULT_OUTPUT_TYPE},
  {"spectrogramColor","SpectrogramColor",XmRInt,sizeof(int),XtOffset(sndres *,spectrogram_color),XmRImmediate,(XtPointer)DEFAULT_SPECTROGRAM_COLOR},
  {"overwriteCheck","OverwriteCheck",XmRInt,sizeof(int),XtOffset(sndres *,overwrite_check),XmRImmediate,(XtPointer)0},
  {"mixerGroups","MixerGroups",XmRInt,sizeof(int),XtOffset(sndres *,ngroups),XmRImmediate,(XtPointer)MIXER_GROUPS},
  {"autoResize","AutoResize",XmRInt,sizeof(int),XtOffset(sndres *,auto_resize),XmRImmediate,(XtPointer)AUTO_RESIZE_DEFAULT},
  {"groupOutChans","GroupOutChans",XmRInt,sizeof(int),XtOffset(sndres *,group_out_chans),XmRImmediate,(XtPointer)4},
  {"horizontalPanes","HorizontalPanes",XmRInt,sizeof(int),XtOffset(sndres *,horizontal_panes),XmRImmediate,(XtPointer)SOUNDS_VERTICAL},
#if HAVE_XmHTML
  {"htmlWidth","HtmlWidth",XmRInt,sizeof(int),XtOffset(sndres *,html_width),XmRImmediate,(XtPointer)HTML_WIDTH},
  {"htmlHeight","HtmlHeight",XmRInt,sizeof(int),XtOffset(sndres *,html_height),XmRImmediate,(XtPointer)HTML_HEIGHT},
  {"htmldir","HtmlDir",XmRString,sizeof(char *),XtOffset(sndres *,html_dir),XmRString,HTML_DIR},
  {"htmlfontsizelist","Htmlfontsizelist",XmRString,sizeof(char *),XtOffset(sndres *,html_font_size_list),XmRString,HTML_FONT_SIZE_LIST},
  {"htmlfixedfontsizelist","Htmlfixedfontsizelist",XmRString,sizeof(char *),XtOffset(sndres *,html_fixed_font_size_list),XmRString,HTML_FIXED_FONT_SIZE_LIST}
#endif
};

static task_manager *make_task_manager(snd_state *ss, Widget shell, Display *dpy)
{
  task_manager *tm;
  tm = (task_manager *)calloc(1,sizeof(task_manager));
  tm->slice = 0;
  tm->ss = ss;
  tm->shell = shell;
  tm->dpy = dpy;
  return(tm);
}

static void FinalCleanupCB(Widget w,XtPointer clientData,XtPointer callData)
{
  snd_exit_cleanly((snd_state *)clientData);
}

static int snd_not_current(snd_info *sp, void *dat)
{
  /* check for change in update status */
  int needs_update;
  needs_update = (file_write_date(sp->fullname) != sp->write_date);
  if (needs_update != sp->need_update)
    {
      snd_file_bomb_icon(sp,needs_update);
      sp->need_update = needs_update;
    }
  return(0);
}

static void corruption_check(XtPointer clientData, XtIntervalId *id)
{
  snd_state *ss = (snd_state *)clientData;
  if ((!(play_in_progress())) && (!(record_in_progress())))
    {
      map_over_sounds(ss,snd_not_current,NULL);
    }
  XtAppAddTimeOut((ss->sgx)->mainapp,(unsigned long)(CORRUPTION_TIME*1000),(XtTimerCallbackProc)corruption_check,clientData);
}

#if UNSET_TRANSIENT
void add_dialog(snd_state *ss, Widget dialog)
{
  state_context *sx;
  int i;
  sx = ss->sgx;
  if (sx->dialog_list_size == 0)
    {
      sx->dialog_list_size = 8;
      sx->dialogs = (Widget *)calloc(sx->dialog_list_size,sizeof(Widget));
      sx->ndialogs = 0;
    }
  else
    {
      if (sx->ndialogs == sx->dialog_list_size)
	{
	  sx->dialog_list_size *= 2;
	  sx->dialogs = (Widget *)realloc(sx->dialogs,sx->dialog_list_size * sizeof(Widget));
	  for (i=sx->ndialogs;i<sx->dialog_list_size;i++) sx->dialogs[i] = NULL;
	}
    }
  sx->dialogs[sx->ndialogs] = dialog;
  sx->ndialogs++;
}

static void minify_maxify_window(Widget w,XtPointer clientData,XEvent *event,Boolean *cont) 
{
  XMapEvent *ev = (XMapEvent *)event;
  snd_state *ss = (snd_state *)clientData;
  state_context *sx;
  int i;
  /* ev->type can be several things, but the one's we care about here are MapNotify and UnmapNotify */
  /* The UNSET_TRANSIENT flag makes the convenience dialogs into "windows" in X-jargon, so when the */
  /* main window is minimized (iconified), other active dialogs aren't also closed unless we mess */
  /* with them explicitly here.  We keep a list of created dialogs, and depend on XtIsManaged to tell */
  /* us which ones need to be unmanaged. */
  if (ev->type == UnmapNotify)
    {
      sx = ss->sgx;
      if (sx->dialog_list_size > 0)
	{
	  for (i=0;i<sx->ndialogs;i++)
	    {
	      if (sx->dialogs[i])
		{
		  if (XtIsManaged(sx->dialogs[i])) XtUnmanageChild(sx->dialogs[i]);
		}
	    }
	}
    }
}
#endif

static char **auto_open_file_names = NULL;
static int auto_open_files = 0;

static Boolean startup_funcs(XtPointer clientData)
{
  task_manager *tm = (task_manager *)clientData;
  Atom wm_delete_window;
  snd_info *sp;
  snd_state *ss;
  chan_info *cp;
  axis_info *ap;
  DIR *dp;
  int i,files;
  static int auto_open_ctr = 0;
  ss = tm->ss;
  switch (tm->slice)
    {
    case 0:
      create_popup_menu(ss);
      break;
    case 1:
      intern_atoms(ss);
      break;
    case 2:
      InitializeDrop(ss);
      break;
    case 3:  
#ifndef NEXT
      /* trap outer-level Close for cleanup check */
      wm_delete_window = XmInternAtom(tm->dpy,"WM_DELETE_WINDOW",FALSE);
      XmAddWMProtocolCallback(tm->shell,wm_delete_window,FinalCleanupCB,(XtPointer)ss);
#endif
#if UNSET_TRANSIENT
      /* if dialogs are not transient, we need to close/reopen them ourselves when the user
       * either iconifys or restores the program
       */
      XtAddEventHandler(tm->shell,StructureNotifyMask,FALSE,minify_maxify_window,(XtPointer)ss);
#endif
      break;
    case 4:
      make_graph_cursor(tm);
      break;
    case 5:
      CLM_connect(ss); 
      break;
    case 6: 
      snd_load_init_file(ss);
      break;
    case 7: 
      if (auto_open_files > 0)
	{
	  if (auto_open_file_names[auto_open_ctr])
	    { /* wanted to use "-d" and "-i" (or "-s") but they're in use */
	      if ((strcmp("-h",auto_open_file_names[auto_open_ctr]) == 0) ||
		  (strcmp("-horizontal",auto_open_file_names[auto_open_ctr]) == 0))
		auto_open_ctr++; 
	      else
		{
		  if (strcmp("--version",auto_open_file_names[auto_open_ctr]) == 0)
		    {
		      fprintf(stdout,"Snd %s %s\n",SND_RPM_VERSION,SND_VERSION);
		      exit(0);
		    }
		  else
		    {
		      if ((strcmp("-p",auto_open_file_names[auto_open_ctr]) == 0) ||
			  (strcmp("-preload",auto_open_file_names[auto_open_ctr]) == 0))
			{
			  /* preload sound files in dir (can be ., should be unquoted) */
			  auto_open_ctr++;
			  add_directory_to_prevlist(ss,auto_open_file_names[auto_open_ctr]);
			  auto_open_ctr++;
			}
		      else
			{
			  if ((strcmp("-l",auto_open_file_names[auto_open_ctr]) == 0) ||
			      (strcmp("-load",auto_open_file_names[auto_open_ctr]) == 0))
			    {
			      /* grab session name -- if arg is "." grab latest on this directory */
			      auto_open_ctr++;
			      snd_load_file(ss,auto_open_file_names[auto_open_ctr]);
			      auto_open_ctr++;
			    }
			  else
			    {
			      if (strcmp("--help",auto_open_file_names[auto_open_ctr]) == 0)
				{
				  fprintf(stdout,"Snd is a sound editor.  Execute it and peruse the 'help' menu for details.\n");
				  exit(0);
				}
			      else
				{
				  sp = snd_open_file(auto_open_file_names[auto_open_ctr],ss);
				  auto_open_ctr++;
				  if (!sp) {tm->slice++; return(FALSE);} /* geez what could have gone wrong... */
				  if (auto_open_ctr < auto_open_files) 
				    {
				      if (strcmp("-s",auto_open_file_names[auto_open_ctr]) == 0)
					{
					  /* start up info follows -- [sx,sy,zx,zy] ... (per chan) */
					  auto_open_ctr++;
					  for (i=0;i<sp->nchans;i++)
					    {
					      cp = sp->chans[i];
					      ap = cp->axis;
					      sscanf(auto_open_file_names[auto_open_ctr],"%f,%f,%f,%f",&ap->sx,&ap->sy,&ap->zx,&ap->zy);
					      set_xy_bounds(cp,ap);
					      auto_open_ctr++;
					    }}}}}}}}}
	  if (auto_open_ctr < auto_open_files) return(FALSE); /* i.e. come back to this branch */
	}
      break;
    case 8: 
      /* this stupid thing (which I can't customize without major hassles) takes forever on large directories */
#ifndef LESSTIF_VERSION
#ifndef VMS
      files = 0;
      if ((dp=opendir(".")) != NULL)
	{
	  while ((files < 400) && (readdir(dp) != NULL)) files++;
	}
      closedir(dp);
      if (files < 400) CreateOpenDialog(tm->shell,(XtPointer)ss);
#endif
#endif
      /* in Lesstif, we can't set up the dialog in advance in the background
       * because it requires that the dialog be activated ("managed") before
       * various things are done to it as it is initialized.  But that requires
       * the dumb thing to popup unexpectedly.
       */
      break;
    case 9:
      if (window_width(ss) != 0) set_snd_window_width(ss,window_width(ss));
      if (window_height(ss) != 0) set_snd_window_height(ss,window_height(ss));
      if (window_x(ss) != -1) set_window_x(ss,window_x(ss));
      if (window_y(ss) != -1) set_window_y(ss,window_y(ss));
      break;
    case 10: 
      XtAppAddTimeOut((ss->sgx)->mainapp,(unsigned long)(CORRUPTION_TIME*1000),corruption_check,(XtPointer)ss);
      break;
    case 11: 
      if (dont_start(ss)) exit(1);
      return(TRUE); 
      break;
    }
  tm->slice++;
  return(FALSE);
}

#if ICON_TYPE
static void SetupIcon(Widget shell);
#endif

void snd_doit(snd_state *state,int argc, char **argv)
{
  XtAppContext app;     
  Widget shell;
  Display *dpy;
  Arg args[32];
  int i,n,err;
  sndres snd_rs;
  state_context *sx;
  int scr;
  Colormap cmap;
  XColor tmp_color,ignore;
#if defined(NEXT)
  Widget toplevel;
#endif
  Widget menu;

  XtSetLanguageProc(NULL,NULL,NULL);

  state->ctrls_height = CLOSED_CTRLS_HEIGHT;
  state->channel_min_height = CHANNEL_MIN_HEIGHT;

#if defined(SCO5) || defined(UW2) || defined(SOLARIS) || defined(HPUX) || defined(ALPHA) || defined(VMS)
  /*
  ** Use of the lower level Xt routines does not work.
  */
  XtSetArg(args[0],XtNwidth,640);
  XtSetArg(args[1],XtNheight,256);
  shell = XtAppInitialize( &app, "Snd", NULL, 0, &argc, argv, NULL, args, 2 );
#else

 #if defined(NEXT)
  toplevel = XtInitialize(argv[0],"Snd",NULL,0,&argc,argv);
  app = XtCreateApplicationContext();
  n=0;
  XtSetArg(args[n],XmNallowShellResize,TRUE); n++;
  shell = XtCreateApplicationShell("mainshell",shellWidgetClass,args,n);
 #else

  shell = XtVaOpenApplication(&app,"mainshell",NULL,0,&argc,argv,NULL,applicationShellWidgetClass,
			      XmNallowShellResize,AUTO_RESIZE_DEFAULT,
  #ifdef LESSTIF_VERSION
			      XmNwidth,250,XmNheight,100,
  #endif
			      NULL);
 #endif
#endif

  /* if user has *keyboardFocusPolicy: Pointer in .Xdefaults, it can screw up Snd's attempt to
   * keep the channel graphics window as the active widget in case of keyboard input.  So,
   * we try to force Snd's focus policy to be XmEXPLICIT
   */
  XtVaGetValues(shell,XmNkeyboardFocusPolicy,&err,NULL);
  if (err != XmEXPLICIT)
    XtVaSetValues(shell,XmNkeyboardFocusPolicy,XmEXPLICIT,NULL);

  auto_open_files = argc-1;
  if (argc > 1) auto_open_file_names = (char **)(argv+1);

  dpy=XtDisplay(shell);
  scr=DefaultScreen(dpy);
  XtGetApplicationResources(shell,&snd_rs,resources,XtNumber(resources),NULL,0);

  set_sound_style(state,snd_rs.horizontal_panes);
  if (sound_style(state) == SOUNDS_VERTICAL)
    {
      for (i=1;i<argc;i++)
	{
	  if (strcmp(argv[i],"-h") == 0)
	    {
	      set_sound_style(state,1);
	      break;
	    }
	}
    }

#if HAVE_XmHTML
  set_html_width(state,snd_rs.html_width);
  set_html_height(state,snd_rs.html_height);
  set_html_dir(state,snd_rs.html_dir);
  set_html_font_size_list(state,snd_rs.html_font_size_list);
  set_html_fixed_font_size_list(state,snd_rs.html_fixed_font_size_list);
#endif

#ifdef SGI
  state->using_schemes = (strcmp(snd_rs.use_schemes,"all") == 0);
#else
  state->using_schemes = 0;
#endif 

  set_auto_resize(state,snd_rs.auto_resize);
  state->sgx = (state_context *)calloc(1,sizeof(state_context));
  sx = state->sgx;
#if UNSET_TRANSIENT
  sx->dialog_list_size = 0;
#endif

#if 0
  fprintf(stderr,"Version: %d, Revision: %d, Release: %d (%d), Motif: %d %d %d\n",
	XProtocolVersion(dpy),XProtocolRevision(dpy),VendorRelease(dpy),ServerVendor(dpy),
	XmVERSION,XmREVISION,XmUPDATE_LEVEL);
#endif
#if 0
  fprintf(stderr,"Lesstif %d.%d\n",LESSTIF_VERSION,LESSTIF_REVISION);
#endif

  cmap=DefaultColormap(dpy,scr);

  sx->mainapp = app;
  sx->mainshell = shell;
  sx->mdpy = dpy;

  sx->white = WhitePixel(dpy,scr);
  sx->black = BlackPixel(dpy,scr);
#ifndef LESSTIF_VERSION
  state->place_scroll_size = 13;
#else
  state->place_scroll_size = 16;
#endif

  /* now try to get colors and fonts that are reasonable */
  /* the gray shades are an attempt to get around Netscape which hogs all the colors */

  if ((!XAllocNamedColor(dpy,cmap,snd_rs.lightest_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,LIGHTEST_COLOR,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,"gray90",&tmp_color,&ignore)))
    {
      fprintf(stderr,snd_string_no_color_use_white,snd_rs.lightest_color);
      sx->high = sx->white;
    }
  else sx->high = tmp_color.pixel;
  if ((!XAllocNamedColor(dpy,cmap,snd_rs.basic_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,BASIC_COLOR,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,"light gray",&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,"gray80",&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,"gray",&tmp_color,&ignore)))
    {
      fprintf(stderr,snd_string_no_color_use_white,snd_rs.basic_color);
      sx->main = sx->white;
    }
  else sx->main = tmp_color.pixel;
  if ((!XAllocNamedColor(dpy,cmap,snd_rs.dark_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,DARK_COLOR,&tmp_color,&ignore)) && 
      (!XAllocNamedColor(dpy,cmap,"gray60",&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,"gray",&tmp_color,&ignore)))
    {
      fprintf(stderr,snd_string_no_color_use_black,snd_rs.dark_color);
      sx->scale = sx->black;
    }
  else sx->scale = tmp_color.pixel;
  if ((!XAllocNamedColor(dpy,cmap,snd_rs.darkest_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,DARKEST_COLOR,&tmp_color,&ignore)) && 
      (!XAllocNamedColor(dpy,cmap,"dark gray",&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,"gray20",&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,"gray",&tmp_color,&ignore)))
    {
      fprintf(stderr,snd_string_no_color_use_black,snd_rs.darkest_color);
      sx->zoom = sx->black;
    }
  else sx->zoom = tmp_color.pixel;
  if ((!XAllocNamedColor(dpy,cmap,snd_rs.text_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,TEXT_COLOR,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,"blue",&tmp_color,&ignore)))
    {
      fprintf(stderr,snd_string_no_color_use_white,snd_rs.text_color);
      sx->text = sx->white;
    }
  else sx->text = tmp_color.pixel;
  if ((!XAllocNamedColor(dpy,cmap,snd_rs.cursor_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,CURSOR_COLOR,&tmp_color,&ignore)))
    {
      fprintf(stderr,snd_string_no_color_use_black,snd_rs.cursor_color);
      sx->cursor = sx->white;
      /* actually nothing makes any sense here -- we're doing XOR on the colormap index??? is that ever correct? */
    }
  else sx->cursor = tmp_color.pixel;
  if ((!XAllocNamedColor(dpy,cmap,snd_rs.selection_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,SELECTION_COLOR,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,"gray80",&tmp_color,&ignore)))
    {
      fprintf(stderr,snd_string_no_color_use_black,snd_rs.selection_color);
      sx->gray = sx->black;
    }
  else sx->gray = tmp_color.pixel;
  if ((!XAllocNamedColor(dpy,cmap,snd_rs.mixer_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,MIXER_COLOR,&tmp_color,&ignore)))
    {
      fprintf(stderr,snd_string_no_color_use_white,snd_rs.mixer_color);
      sx->mixpix = sx->white;
    }
  else sx->mixpix = tmp_color.pixel;
  if ((!XAllocNamedColor(dpy,cmap,snd_rs.mixer_high_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,MIXER_HIGH_COLOR,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,MIXER_COLOR,&tmp_color,&ignore)))
    {
      fprintf(stderr,snd_string_no_color_use_white,snd_rs.mixer_high_color);
      sx->mixhi = sx->white;
    }
  else sx->mixhi = tmp_color.pixel;
  if ((!XAllocNamedColor(dpy,cmap,snd_rs.yellow_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,YELLOW_COLOR,&tmp_color,&ignore)))
    {
      fprintf(stderr,snd_string_no_color_use_white,snd_rs.yellow_color);
      sx->yellow = sx->white;
    }
  else sx->yellow = tmp_color.pixel;
  if ((!XAllocNamedColor(dpy,cmap,snd_rs.light_blue_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,LIGHT_BLUE_COLOR,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,"gray90",&tmp_color,&ignore)))
    {
      fprintf(stderr,snd_string_no_color_use_white,snd_rs.light_blue_color);
      sx->txt = sx->white;
    }
  else sx->txt = tmp_color.pixel;
  if ((!XAllocNamedColor(dpy,cmap,snd_rs.group_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,GROUP_COLOR,&tmp_color,&ignore)))
    {
      fprintf(stderr,snd_string_no_color_use_black,snd_rs.group_color);
      sx->red = sx->black;
    }
  else sx->red = tmp_color.pixel;

  sx->button_fontstruct = XLoadQueryFont(dpy,snd_rs.button_font);
  if (!(sx->button_fontstruct)) 
    {
      fprintf(stderr,snd_string_cant_find_font,snd_rs.button_font);
      sx->button_fontstruct = XLoadQueryFont(dpy,BUTTON_FONT);
      if (!(sx->button_fontstruct)) 
	{
	  sx->button_fontstruct = XLoadQueryFont(dpy,FALLBACK_FONT);
	  if (!(sx->button_fontstruct)) fprintf(stderr,snd_string_serious_font_trouble,FALLBACK_FONT);
	}
    }
  sx->button_fontlist = XmFontListCreate(sx->button_fontstruct,"buttonset");
  XmFontListEntryCreate("button_font",XmFONT_IS_FONT,sx->button_fontstruct);

  if (snd_rs.listener_font)
    {
      sx->listener_fontstruct = XLoadQueryFont(dpy,snd_rs.listener_font);
      if (!(sx->listener_fontstruct)) 
	sx->listener_fontlist = NULL;
      else
	{
	  sx->listener_fontlist = XmFontListCreate(sx->listener_fontstruct,"listenerset");
	  XmFontListEntryCreate("listener_font",XmFONT_IS_FONT,sx->listener_fontstruct);
	}
    }
  else sx->listener_fontlist = NULL;

  sx->bold_button_fontstruct = XLoadQueryFont(dpy,snd_rs.bold_button_font);
  if (!(sx->bold_button_fontstruct)) 
    {
      fprintf(stderr,snd_string_cant_find_font,snd_rs.bold_button_font);
      sx->bold_button_fontstruct = XLoadQueryFont(dpy,BOLD_BUTTON_FONT);
      if (!(sx->bold_button_fontstruct)) 
	{
	  sx->bold_button_fontstruct = XLoadQueryFont(dpy,FALLBACK_FONT);
	  if (!(sx->bold_button_fontstruct)) fprintf(stderr,snd_string_serious_font_trouble,FALLBACK_FONT);
	}
    }
  sx->bold_button_fontlist = XmFontListCreate(sx->bold_button_fontstruct,"boldbuttonset");
  XmFontListEntryCreate("bold_button_font",XmFONT_IS_FONT,sx->bold_button_fontstruct);

  sx->axis_label_fontstruct = XLoadQueryFont(dpy,snd_rs.axis_label_font);
  if (!(sx->axis_label_fontstruct)) 
    {
      fprintf(stderr,snd_string_cant_find_font,snd_rs.axis_label_font);
      sx->axis_label_fontstruct = XLoadQueryFont(dpy,AXIS_LABEL_FONT);
      if (!(sx->axis_label_fontstruct)) 
	{
	  sx->axis_label_fontstruct = XLoadQueryFont(dpy,FALLBACK_FONT);
	  if (!(sx->axis_label_fontstruct)) fprintf(stderr,snd_string_serious_font_trouble,FALLBACK_FONT);
	}
    }
  sx->axis_numbers_fontstruct = XLoadQueryFont(dpy,snd_rs.axis_numbers_font);
  if (!(sx->axis_numbers_fontstruct)) 
    {
      fprintf(stderr,snd_string_cant_find_font,snd_rs.axis_numbers_font);
      sx->axis_numbers_fontstruct = XLoadQueryFont(dpy,AXIS_NUMBERS_FONT);
      if (!(sx->axis_numbers_fontstruct)) 
	{
	  sx->axis_numbers_fontstruct = XLoadQueryFont(dpy,FALLBACK_FONT);
	  if (!(sx->axis_numbers_fontstruct)) fprintf(stderr,snd_string_serious_font_trouble,FALLBACK_FONT);
	}
    }
  sx->help_text_fontstruct = XLoadQueryFont(dpy,snd_rs.help_text_font);
  if (!(sx->help_text_fontstruct)) 
    {
      fprintf(stderr,snd_string_cant_find_font,snd_rs.help_text_font);
      sx->help_text_fontstruct = XLoadQueryFont(dpy,HELP_TEXT_FONT);
      if (!(sx->help_text_fontstruct)) 
	{
	  sx->help_text_fontstruct = XLoadQueryFont(dpy,FALLBACK_FONT);
	  if (!(sx->help_text_fontstruct)) fprintf(stderr,snd_string_serious_font_trouble,FALLBACK_FONT);
	}
    }
  sx->help_text_fontlist = XmFontListCreate(sx->help_text_fontstruct,"helptextset");
  XmFontListEntryCreate("help_text_font",XmFONT_IS_FONT,sx->help_text_fontstruct);

  if (!(state->using_schemes)) XtVaSetValues(shell,XmNbackground,sx->main,NULL);
  state->init_file = snd_rs.init_file_name; /* doesn't make any sense to pass this out to the user -- what can he do? */
  set_eps_file(state,snd_rs.eps_file_name);
  set_default_output_type(state,snd_rs.def_output_type);
  set_spectro_color(state,snd_rs.spectrogram_color);
  set_ask_before_overwrite(state,snd_rs.overwrite_check);
  set_mixer_group_max_out_chans(state,snd_rs.group_out_chans);
  set_mixer_groups(state,snd_rs.ngroups);
  if (mixer_groups(state) <= 0) set_mixer_groups(state,1);

  n=0;
  if (!(state->using_schemes)) n = background_main_color(args,n,(snd_state *)state);
  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_FORM); n++;
  XtSetArg(args[n],XmNallowResize,TRUE); n++;
#if PANED_WINDOWS_BUGGY
  XtSetArg(args[n],XmNnoResize,FALSE); n++;
  XtSetArg(args[n],XmNresizePolicy,XmRESIZE_GROW); n++;
#endif
  sx->mainpane = XtCreateManagedWidget("mainpane",xmFormWidgetClass,shell,args,n);

  menu = add_menu(state);

  n=0;
  if (!(state->using_schemes)) n = background_main_color(args,n,(snd_state *)state);
  XtSetArg(args[n],XmNtopAttachment,XmATTACH_WIDGET); n++;
  XtSetArg(args[n],XmNtopWidget,menu); n++;
  XtSetArg(args[n],XmNbottomAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNleftAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
  XtSetArg(args[n],XmNallowResize,TRUE); n++;
  XtSetArg(args[n],XmNsashHeight,MAIN_SASH_SIZE); n++;
  XtSetArg(args[n],XmNsashWidth,MAIN_SASH_SIZE); n++;
  if (sound_style(state) == SOUNDS_HORIZONTAL) {XtSetArg(args[n],XmNorientation,XmHORIZONTAL); n++;}
  XtSetArg(args[n],XmNsashIndent,MAIN_SASH_INDENT); n++;
#if (!(PANED_WINDOWS_BUGGY))
  sx->soundpane = XtCreateManagedWidget("soundpane",xmPanedWindowWidgetClass,sx->mainpane,args,n);
#else
  sx->soundpane = XtCreateManagedWidget("soundpane",xmRowColumnWidgetClass,sx->mainpane,args,n);
#endif
  /* might want a second layer here so that the listener is underneath the horizontally placed sounds */

#if ICON_TYPE
  SetupIcon(shell);
#endif

  XtRealizeWidget(shell);
  if (auto_resize(state) != AUTO_RESIZE_DEFAULT) XtVaSetValues(shell,XmNallowShellResize,auto_resize(state),NULL);

  XtAppAddWorkProc(app,startup_funcs,make_task_manager(state,shell,dpy));
  XtAppMainLoop(app);
}

void reflect_resize(snd_state *ss)
{
  XtVaSetValues(main_SHELL(ss),XmNallowShellResize,auto_resize(ss),NULL);
}


/* ---------------- HELP MONOLOG ---------------- */

#if HAVE_XmHTML

/* TODO: select text in HTML widget (for fft peaks primarily)
 *       perhaps divide up snd.html (size makes it hard to scroll)
 *       snd.html dir as resource, or ask if not found (need gif files too)
 *       widget size (resize if not snd.html?)
 *       font faces/size-lists as resources
 */

#include <XmHTML/XmHTML.h>
/* CFLAGS = -g -Wall -DLINUX -DUSR_LIB_OSS=1 -DHAVE_GUILE=1 -DHAVE_XmHTML=1 -I/home/bil/test/XmHTML-1.1.4/include */
/* LIBS = /home/bil/test/XmHTML-1.1.4/src/libXmHTML.a -L/usr/X11R6/lib -lMrm -lXp -lXm -lXpm -lXmu -lXt -lXext -lX11 /usr/local/lib/libguile.a -lm -ldl */

static char *Load_HTML_File(char *filename)
{ /* from XmHTML/examples/example_1.c */
  FILE *file;
  int size;
  char *content;
  if ((file = fopen(filename, "r")) == NULL)
    {
      perror(filename);
      return(NULL);
    }
  fseek(file, 0, SEEK_END);
  size = ftell(file);
  rewind(file);
  if ((content = malloc(size)) == NULL) return(NULL);
  if((fread(content, 1, size, file)) != size)
    fprintf(stderr,"Warning: did not read entire file!\n");
  fclose(file);
  return(content);
}

static void anchorCB(Widget widget, XtPointer client_data, XmHTMLAnchorCallbackStruct *cbs)
{
  cbs->doit = True;
  cbs->visited = True;
}

static char *snd_html = NULL;
static int snd_html_loaded = 0;
#endif


typedef struct {Widget help; Widget text;} help_win;
static char help_window_label[64];

static void help_help_callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
	   "Help",
"You can get help within Snd either from\n\
the Help Menu items, or by clicking on\n\
some portion of the Snd display while the\n\
cursor is '?'.  See Click for Help in the\n\
Help Menu.\n\
");
}

static void create_help_monolog(snd_state *ss)
{
  /* create scrollable but not editable text window */
  help_win *hlp;
  Arg args[20];
  int n;
  XmString titlestr;
#if HAVE_XmHTML
  Widget ww;
#endif

  hlp = (help_win *)calloc(1,sizeof(help_win));
  titlestr = XmStringCreateLocalized(snd_string_Help);
  n=0;
  if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
  XtSetArg(args[n],XmNdialogTitle,titlestr); n++;
  /* this window should be resizable by the user (i.e. have the resize bars), but not resize itself */
  XtSetArg(args[n],XmNresizePolicy,XmRESIZE_GROW); n++;
#if RESIZE_DIALOG
  XtSetArg(args[n],XmNnoResize,FALSE); n++;
#endif
#if UNSET_TRANSIENT
  XtSetArg(args[n],XmNtransient,FALSE); n++;
#endif
  hlp->help = XmCreateMessageDialog(main_PANE(ss),"snd-help",args,n);
#if UNSET_TRANSIENT
  add_dialog(ss,hlp->help);
#endif

  XtUnmanageChild(XmMessageBoxGetChild(hlp->help,XmDIALOG_SYMBOL_LABEL));
  XtUnmanageChild(XmMessageBoxGetChild(hlp->help,XmDIALOG_CANCEL_BUTTON));
  XtAddCallback(hlp->help,XmNhelpCallback,help_help_callback,ss);
  XmStringFree(titlestr);

  n=0;
#if HAVE_XmHTML
  XtSetArg(args[n],XmNwidth,html_width(ss)); n++;
  XtSetArg(args[n],XmNheight,html_height(ss)); n++;
  XtSetArg(args[n],XmNfontSizeList,html_font_size_list(ss)); n++;
  XtSetArg(args[n],XmNfontSizeFixedList,html_fixed_font_size_list(ss)); n++;
  hlp->text = XtCreateManagedWidget("html",xmHTMLWidgetClass,hlp->help,args,n);
  XtAddCallback(hlp->text,XmNactivateCallback,(XtCallbackProc)anchorCB, NULL);
#else
  XtSetArg(args[n],XmNeditMode,XmMULTI_LINE_EDIT); n++;
  XtSetArg(args[n],XmNeditable,FALSE); n++;
  XtSetArg(args[n],XmNcolumns,HELP_COLUMNS); n++;
  XtSetArg(args[n],XmNrows,HELP_ROWS); n++;
  XtSetArg(args[n],XmNfontList,help_text_FONT(ss)); n++;
  XtSetArg(args[n],XmNforeground,(ss->sgx)->black); n++; /* needed if color allocation fails completely */
  XtSetArg(args[n],XmNbackground,(ss->sgx)->white); n++;
  hlp->text = XmCreateScrolledText(hlp->help,"help-text",args,n);
  XtManageChild(hlp->text);
#endif

#if MANAGE_DIALOG
  XtManageChild(hlp->help);
#endif

  if (!(ss->using_schemes))
    {
      map_over_children(hlp->help,set_main_color_of_widget,(void *)ss);
      XtVaSetValues(hlp->text,XmNbackground,(ss->sgx)->white,NULL);
      XtVaSetValues(XmMessageBoxGetChild(hlp->help,XmDIALOG_OK_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
      XtVaSetValues(XmMessageBoxGetChild(hlp->help,XmDIALOG_HELP_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
#if HAVE_XmHTML
      XtVaGetValues(hlp->text,XmNworkWindow,&ww,NULL);
      XtVaSetValues(ww,XmNbackground,(ss->sgx)->white,NULL);
#endif
    }
  ss->help_monolog = hlp;
}

#if HAVE_XmHTML
static void load_snd_html_and_jump(snd_state *ss, help_win *hlp,char *anchor)
{
  char *buf;
  if (snd_html == NULL) 
    {
      if (html_dir(ss) && (*(html_dir(ss))))
	{
	  buf = (char *)calloc(256,sizeof(char));
	  sprintf(buf,"%s/snd.html",html_dir(ss));
	  snd_html = Load_HTML_File(buf);
	  free(buf);
	}
      else snd_html = Load_HTML_File("snd.html");
    }
  if (snd_html_loaded == 0) 
    {
      XmHTMLTextSetString(hlp->text,snd_html);
      snd_html_loaded = 1;
    }
  XmHTMLAnchorScrollToName(hlp->text,anchor);
}
#endif

void snd_help(snd_state *ss, char *subject, char *helpstr)
{
  /* place help string in scrollable help window */
  /* if window is already active, add this help at the top and reposition */
  help_win *h;
  XmString xstr1;
#if HAVE_XmHTML
  char *newhelp;
#endif
  if (!(ss->help_monolog)) create_help_monolog(ss); else raise_dialog(((help_win *)(ss->help_monolog))->help);
  h = (help_win *)(ss->help_monolog);
  sprintf(help_window_label,"%s help",subject);
  xstr1 = XmStringCreate(help_window_label,XmFONTLIST_DEFAULT_TAG);
  XtVaSetValues(h->help,XmNmessageString,xstr1,NULL);
#if HAVE_XmHTML
  if (helpstr[0] == '#')
    load_snd_html_and_jump(ss,h,helpstr);
  else
    {
      newhelp = (char *)calloc(strlen(helpstr) + 32,sizeof(char));
      sprintf(newhelp,"<html><body>%s</body></html>",helpstr);
      XmHTMLTextSetString(h->text,newhelp);
      snd_html_loaded = 0;
      free(newhelp);
    }
#else
  XmTextSetString(h->text,helpstr);
#endif
  if (!XtIsManaged(h->help)) XtManageChild(h->help);
  XmStringFree(xstr1);
}

void ssnd_help(snd_state *ss, char *subject, ...)
{
  va_list ap;
  char *helpstr,*newstr;
  int len,size;
  va_start(ap,subject);
  size = 1024;
  newstr = (char *)calloc(size,sizeof(char));
  len = 0;
  while ((helpstr = va_arg(ap,char *)))
    {
      len += strlen(helpstr);
      if (len >= size)
	{
	  size = len+1024;
	  newstr = (char *)realloc(newstr,size * sizeof(char));
	}
      strcat(newstr,helpstr);
    }
  va_end(ap);
  snd_help(ss,subject,newstr);
  free(newstr);
}  

int snd_window_width(snd_state *ss)
{
  Dimension width;
  XtVaGetValues(main_SHELL(ss),XmNwidth,&width,NULL);
  return(width);
}

int snd_window_height(snd_state *ss)
{
  Dimension height;
  XtVaGetValues(main_SHELL(ss),XmNheight,&height,NULL);
  return(height);
}

void set_snd_window_width(snd_state *ss,int width)
{
  if (any_selected_sound(ss)) XtVaSetValues(main_SHELL(ss),XmNwidth,(Dimension)width,NULL);
  set_window_width(ss,width);
}

void set_snd_window_height(snd_state *ss,int height)
{
  if (any_selected_sound(ss)) XtVaSetValues(main_SHELL(ss),XmNheight,(Dimension)height,NULL);
  set_window_height(ss,height);
  normalize_all_sounds(ss);
}

void set_window_y(snd_state *ss, int y)
{
  XtVaSetValues(main_SHELL(ss),XmNy,y,NULL);
}

void set_window_x(snd_state *ss, int x)
{
  XtVaSetValues(main_SHELL(ss),XmNx,x,NULL);
}

void sound_show_ctrls(snd_info *sp)
{
  XtUnmanageChild(snd_widget(sp,W_snd_ctrls));
  XtVaSetValues(snd_widget(sp,W_snd_ctrls),XmNpaneMinimum,OPEN_CTRLS_HEIGHT,XmNpaneMaximum,OPEN_CTRLS_HEIGHT,NULL);
  XtManageChild(snd_widget(sp,W_snd_ctrls));
  XtVaSetValues(snd_widget(sp,W_snd_ctrls),XmNpaneMinimum,1,NULL);
}

void sound_hide_ctrls(snd_info *sp)
{
  XtUnmanageChild(snd_widget(sp,W_snd_ctrls));
  XtVaSetValues(snd_widget(sp,W_snd_ctrls),XmNpaneMaximum,CLOSED_CTRLS_HEIGHT,XmNpaneMinimum,CLOSED_CTRLS_HEIGHT,NULL);
  XtManageChild(snd_widget(sp,W_snd_ctrls));
  XtVaSetValues(snd_widget(sp,W_snd_ctrls),XmNpaneMaximum,OPEN_CTRLS_HEIGHT,XmNpaneMinimum,1,NULL);
}

int control_panel_open(snd_info *sp)
{
  Dimension hgt;
  XtVaGetValues(snd_widget(sp,W_snd_ctrls),XmNheight,&hgt,NULL);
  return(hgt > CLOSED_CTRLS_HEIGHT);
}

void show_controls(snd_state *ss)
{
  snd_info *sp;
  int i;
  ss->ctrls_height = OPEN_CTRLS_HEIGHT;
  set_view_ctrls_label(snd_string_Hide_controls);
  for (i=0;i<ss->max_sounds;i++)
    {
      sp = ss->sounds[i];
      if ((sp) && (sp->inuse)) sound_show_ctrls(sp);
    }
}

void hide_controls(snd_state *ss)
{
  snd_info *sp;
  int i;
  ss->ctrls_height = CLOSED_CTRLS_HEIGHT;
  set_view_ctrls_label(snd_string_Show_controls);
  for (i=0;i<ss->max_sounds;i++)
    {
      sp = ss->sounds[i];
      if ((sp) && (sp->inuse)) sound_hide_ctrls(sp);
    }
}

#if (ICON_TYPE == 1) || ((ICON_TYPE == 2) && (!HAVE_XPM))
/* use plain 48x48 bitmap */
#define snd_width 48
#define snd_height 48
static unsigned char snd_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0xf0, 0x01, 0x00,
   0x32, 0x00, 0x00, 0x40, 0x00, 0x00, 0x32, 0x0c, 0x00, 0x40, 0x00, 0x00,
   0x32, 0x0f, 0x10, 0x40, 0x00, 0x00, 0xb2, 0x19, 0x38, 0x40, 0x00, 0x00,
   0xb0, 0x10, 0x68, 0x40, 0x00, 0x00, 0xb0, 0x30, 0x88, 0x40, 0x00, 0x00,
   0xb0, 0x20, 0x88, 0x40, 0x00, 0x00, 0xf0, 0xe0, 0x89, 0x41, 0x00, 0x00,
   0x30, 0x00, 0x09, 0x43, 0x3c, 0x00, 0x30, 0x00, 0x0f, 0x4e, 0xe6, 0x00,
   0x30, 0x00, 0x00, 0xf8, 0x83, 0x7f, 0x30, 0x00, 0x00, 0x40, 0x00, 0xf8,
   0x30, 0x00, 0x00, 0x40, 0x00, 0x0e, 0x30, 0x00, 0x07, 0xe0, 0x01, 0x03,
   0x30, 0x80, 0x09, 0x50, 0x03, 0x01, 0x70, 0xc0, 0x08, 0x58, 0x82, 0x01,
   0xb0, 0x60, 0x18, 0x44, 0xc6, 0x00, 0xb0, 0x31, 0x10, 0x44, 0x7c, 0x00,
   0x30, 0x13, 0x10, 0x44, 0x00, 0x00, 0x30, 0x1e, 0x30, 0x44, 0x00, 0x00,
   0x30, 0x00, 0x60, 0x42, 0x00, 0x00, 0x30, 0x00, 0xc0, 0xc3, 0x00, 0x00,
   0x37, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x35, 0x00, 0x00, 0xc0, 0x03, 0x00,
   0x35, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x35, 0x00, 0x00, 0xc0, 0x00, 0x00,
   0xf7, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x7f,
   0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x77, 0x17, 0x00, 0xde, 0x03,
   0x50, 0x55, 0x11, 0x00, 0xd6, 0x02, 0x50, 0x57, 0x13, 0x00, 0xd6, 0x02,
   0x50, 0x55, 0x11, 0x00, 0xd6, 0x02, 0x70, 0x77, 0x17, 0x00, 0xde, 0x03,
   0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x77,
   0x08, 0x00, 0x80, 0x0d, 0x00, 0x54, 0xf8, 0xff, 0xff, 0xff, 0xff, 0x77,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0xf8, 0xff, 0xff, 0xff, 0xff, 0x77, 0x88, 0x0d, 0x00, 0x00, 0x00, 0x54,
   0xf8, 0xff, 0xff, 0xff, 0xff, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

static void SetupIcon(Widget shell)
{
  Display *dpy;
  Window root;
  Pixmap bitmap;
  dpy = XtDisplay (shell);
  root = DefaultRootWindow(dpy);
  bitmap = XCreateBitmapFromData(dpy,root,snd_bits,snd_width,snd_height);
  XtVaSetValues(shell,XmNiconPixmap,bitmap,NULL);
}
#endif

#if (ICON_TYPE == 2) && (HAVE_XPM)
/* use XPM-style 48x48 bitmap */
#include <X11/xpm.h>
static char *snd_bits[] = {
"48 48 9 1",
". c white m white",
"B c black m black",
"l c ivory1 m white s lightestcolor",
"a c ivory2 m white s basiccolor",
"d c ivory3 m black s darkcolor",
"X c ivory4 m black s darkestcolor",
"b c lightsteelblue1 m white s textcolor",
"r c red m black s cursorcolor",
"g c lightgreen m black s mixercolor",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"aaXXda.........................................a",
"aaXXda..BB........................rrrrrrr......a",
"aaXXda..BB...........................r.........a",
"aaXXda..BB...........................r.........a",
"aaXXda..BB...........................r.........a",
"aaXXda..BB...........................r.........a",
"aaXXda..BB........BBB................r.........a",
"aaXXda..BB......B.....B..............r.........a",
"aadBda..BB....B........B.............r.........a",
"aalBda..BB..B...........B............r.........a",
"aalBda..BB.B.............B...........r.........a",
"aalBda..BBB..............B...........r.........a",
"aaXXda..BB...............B...........r.........a",
"aaXXda..BB................B..........r.........a",
"aaXXda..BB................B..........r.........a",
"aaXXda..BB.................B.........r.........a",
"aaXXda..BB.................B.........r........Ba",
"aaXXda..BB..................B........r.......B.a",
"aaXXda..BB...................B.......r......B..a",
"aaXXda..BB.....................B.....r.....B...a",
"aaXXda..BB......................B....r...B.....a",
"aaXXda..BB.......................B...r.B.......a",
"aaXXda..BB.........................B.r.........a",
"aaXXda..BB...........................rr........a",
"aaXXda..BB...........................rrr.......a",
"aaaaaa..BB...........................rrrr......a",
"aaaaaa..BB...........................rrr.......a",
"aa.lda..BB...........................rr........a",
"aa.lda..BB...........................r.........a",
"aa.lda..BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.a",
"aaddda..BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB.a",
"aaaaaa.........................................a",
"aaaaaa.........................................a",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"aBbbaaaddddd....ddddddddddddddddddddddddddddddda",
"adbbaaadddddllllddddddddddddddddddddddddddddddda",
"adbbaaadddddllllddddddddddddddddddddddddddddddda",
"adbbaaaXXXXXXXXXXXXXXXXXXXXddddXXXXXXXXXXXXXXXXa",
"aaaaaaaXXXXXXXXXXXXXXXXXXXXddddXXXXXXXXXXXXXXXXa",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaggg",
"aaaaBBBlBBBlBBBlBBBlBaaaaaaaaaaaaaaa....a....ggg",
"aaaaBlBlBlBlBlBlBlllBaaaaaaaaaaaaaaallldallldggg",
"aaaaBlBlBBBlBlBlBBllBaaaaaaaaaaaaaaallldallldaaa",
"aaaaBlBlBlBlBlBlBlllBaaaaaaaaaaaaaaaddddaddddaaa",
"aaaaBBBlBBBlBBBlBBBlBaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"};

static void SetupIcon(Widget shell)
{
  Display *dpy;
  Window root;
  int status,scr;
  Pixmap pix,mask;
  XpmAttributes attributes;
  dpy = XtDisplay(shell);
  root = DefaultRootWindow(dpy);
  scr = DefaultScreen(dpy);
  XtVaGetValues(shell,XmNdepth,&attributes.depth,XmNcolormap,&attributes.colormap,NULL);
  attributes.visual = DefaultVisual(dpy,scr);
  attributes.valuemask = XpmDepth | XpmColormap | XpmVisual;
  status = XpmCreatePixmapFromData(dpy,root,snd_bits,&pix,&mask,&attributes);
  if (mask) XFreePixmap(dpy,mask);
  XtVaSetValues(shell,XmNiconPixmap,pix,NULL);
}
#endif


static Widget session_dialog = NULL;
static Widget session_text = NULL;
static Widget session_loadB = NULL;
static Widget session_error = NULL;
static char session_string[128];

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

static void Options_Session_Load_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  int err;
  char *name;
  XmString s1;
  name = XmTextGetString(session_text);
  err = snd_load_file((snd_state *)clientData,name);
  if (err == -1)
    {
      sprintf(session_string,"%s: %s",name,strerror(errno));
      s1 = XmStringCreate(session_string,XmFONTLIST_DEFAULT_TAG);
      XtVaSetValues(session_error,XmNlabelString,s1,NULL);
      XmStringFree(s1);
    }
  else XtUnmanageChild(session_dialog);
}

static void Options_Session_Save_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss = (snd_state *)clientData;
  state_context *sgx;
  XmAnyCallbackStruct *cb = (XmAnyCallbackStruct *)callData;
  sgx = ss->sgx;
  if (cb->event != sgx->text_activate_event)
    {
      save_state((snd_state *)clientData,XmTextGetString(session_text));
      XtUnmanageChild(session_dialog);
    }
}

static void Options_Session_Help_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_help((snd_state *)clientData,
	   "Sessions",
"You can save the current state of Snd by giving a\n\
file name and clicking 'save'.  You can start Snd\n\
in this state by invoking it with this file name.\n\
This 'session file' is lisp, just like a customization\n\
file (e.g. '.snd').  To load such a file after\n\
startup, use the command load: M-x (load name) or\n\
click 'load' in this dialog.\n\
");
}

void Options_Session_Callback(Widget w,XtPointer clientData,XtPointer callData) 
{
  snd_state *ss = (snd_state *)clientData;
  Arg args[20];
  int n;
  XmString xmstr1,titlestr;
  Widget rc,dl;
  if (!session_dialog) 
    {
      n=0;
      if (!(ss->using_schemes)) n = background_main_color(args,n,ss);
      xmstr1=XmStringCreateLocalized(snd_string_Save);
      titlestr=XmStringCreateLocalized(snd_string_Session);
      XtSetArg(args[n],XmNokLabelString,xmstr1); 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
      session_dialog = XmCreateMessageDialog(main_PANE(ss),snd_string_session,args,n);

      XmStringFree(xmstr1);
      XmStringFree(titlestr);
      XtUnmanageChild(XmMessageBoxGetChild(session_dialog,XmDIALOG_SYMBOL_LABEL));
      XtAddCallback(session_dialog,XmNhelpCallback,Options_Session_Help_Callback,ss);
      XtAddCallback(session_dialog,XmNokCallback,Options_Session_Save_Callback,ss);
      XtAddCallback(session_dialog,XmNcancelCallback,Options_Session_Cancel_Callback,ss);

      n=0;
      session_loadB = XtCreateManagedWidget(snd_string_Load,xmPushButtonWidgetClass,session_dialog,args,n);
      XtAddCallback(session_loadB,XmNactivateCallback,Options_Session_Load_Callback,ss);
  
      n=0;
      rc = XtCreateManagedWidget("session-form",xmFormWidgetClass,session_dialog,args,n);

      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_session_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++;
      if (session_file(ss)) {XtSetArg(args[n],XmNvalue,session_file(ss)); n++;}
      session_text = sndCreateTextFieldWidget(ss,"text",rc,args,n,NOT_ACTIVATABLE);

      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,session_text); n++;
      XtSetArg(args[n],XmNrightAttachment,XmATTACH_FORM); n++;
      session_error = XtCreateManagedWidget("",xmLabelWidgetClass,rc,args,n);

#if MANAGE_DIALOG
      XtManageChild(session_dialog);
#endif
      if (!(ss->using_schemes)) 
	{
	  map_over_children(session_dialog,set_main_color_of_widget,(void *)clientData);
	  XtVaSetValues(XmMessageBoxGetChild(session_dialog,XmDIALOG_OK_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	  XtVaSetValues(XmMessageBoxGetChild(session_dialog,XmDIALOG_CANCEL_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	  XtVaSetValues(XmMessageBoxGetChild(session_dialog,XmDIALOG_HELP_BUTTON),XmNarmColor,(ss->sgx)->text,NULL);
	  XtVaSetValues(session_loadB,XmNarmColor,(ss->sgx)->text,NULL);
	}
    }
  else raise_dialog(session_dialog);
  XtVaSetValues(session_error,XmNlabelString,NULL,NULL);
  if (!XtIsManaged(session_dialog)) XtManageChild(session_dialog);
}

