#include "snd.h"

#ifdef NEXT
  #include <sys/dir.h>
  #include <sys/dirent.h>
#else
  #include <dirent.h>
#endif

#define INIT_FILE_NAME "~/.snd"
#define BACKUP_FILE_NAME ".snd-backup"
#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"
#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 DEFAULT_SONOGRAM_COLOR 0     /* index into table of choices -- see snd.xchn.c and snd-xmenu.c */
#define DEFAULT_SPECTROGRAM_COLOR -1 /* -1 = just lines or index */
#define NGROUPS 6

/* 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 CLOSED_CTRLS_HEIGHT 22
  #define OPEN_CTRLS_HEIGHT 185
  #define ICON_TYPE 1
#endif
#if defined(LINUX) || defined(SCO5) || defined(UW2) || defined(SOLARIS)
  #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 CLOSED_CTRLS_HEIGHT 18
  #define OPEN_CTRLS_HEIGHT 155
  #define ICON_TYPE 2
#endif

/* ICON_TYPE 1 => use plain bitmap, 2 => use XPM bitmap, 0 => no icon */

#ifdef NEXT
  #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
#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 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 *use_schemes;
  char *button_font;
  char *bold_button_font;
  char *axis_label_font;
  char *axis_numbers_font;
  char *help_text_font;
  char *init_file_name;
  char *backup_file_name;
  char *eps_file_name;
  int default_output_type;
  int sonogram_color;
  int spectrogram_color;
  int overwrite_check;
  int ngroups;
  int auto_resize;
} 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},
  {"useSchemes","UseSchemes",XmRString,sizeof(char *),XtOffset(sndres *,use_schemes),XmRString,"none"},
  {"buttonFont","ButtonFont",XmRString,sizeof(char *),XtOffset(sndres *,button_font),XmRString,BUTTON_FONT},
  {"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},
  {"backupFile","BackupFile",XmRString,sizeof(char *),XtOffset(sndres *,backup_file_name),XmRString,BACKUP_FILE_NAME},
  {"epsFile","EpsFile",XmRString,sizeof(char *),XtOffset(sndres *,eps_file_name),XmRString,EPS_FILE_NAME},
  {"defaultOutputType","DefaultOutputType",XmRInt,sizeof(int),XtOffset(sndres *,default_output_type),XmRImmediate,(XtPointer)DEFAULT_OUTPUT_TYPE},
  {"sonogramColor","SonogramColor",XmRInt,sizeof(int),XtOffset(sndres *,sonogram_color),XmRImmediate,(XtPointer)DEFAULT_SONOGRAM_COLOR},
  {"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},
  {"groups","Groups",XmRInt,sizeof(int),XtOffset(sndres *,ngroups),XmRImmediate,(XtPointer)NGROUPS},
  {"autoResize","AutoResize",XmRInt,sizeof(int),XtOffset(sndres *,auto_resize),XmRImmediate,(XtPointer)AUTO_RESIZE_DEFAULT}
};

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 void Save_Yourself_Callback(Widget w,XtPointer clientData,XtPointer callData)
{
  task_manager *tm = (task_manager *)clientData;
  snd_state *ss;
  ss = tm->ss;
  save_snd_state(ss);
}

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;
  Widget menu;
  Atom wm_delete_window,wm_save_yourself;
  snd_info *sp;
  snd_state *ss;
  chan_info *cp;
  axis_info *ap;
  DIR *dp;
  int i,files,err;
  static int auto_open_ctr = 0;
  ss = tm->ss;
  switch (tm->slice)
    {
    case 0:
      menu = add_menu(ss);
      XtVaSetValues(menu,XmNpositionIndex,0,XmNpaneMinimum,MENU_HEIGHT,XmNpaneMaximum,MENU_HEIGHT,XmNmarginHeight,0,NULL);
      break;
    case 1:
      create_popup_menu(ss);
      break;
    case 2:
      intern_atoms(ss);
      break;
    case 3:
      InitializeDrop(ss);
      break;
    case 4:  
#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);
      wm_save_yourself = XmInternAtom(tm->dpy,"WM_SAVE_YOURSELF",FALSE);
      XmAddWMProtocols(tm->shell,&wm_save_yourself,1);
      XmAddWMProtocolCallback(tm->shell,wm_save_yourself,Save_Yourself_Callback,(XtPointer)tm); 
#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 5:
      make_graph_cursor(tm);
      break;
    case 6:
      err = CLM_connect(ss); /* this checks for pre-exisiting snd_snd_fifo so it must precede SND_connect */
      if (err == 0) SND_connect(ss);
      break;
    case 7:
      snd_add_locals(ss);
      break;
    case 8: 
      snd_load_init_file(ss);
      break;
    case 9: 
      if (auto_open_files > 0)
	{
	  if (auto_open_ctr == 0) remove_backup_file(ss);
	  if (auto_open_file_names[auto_open_ctr])
	    { /* wanted to use "-d" and "-i" (or "-s") but they're in use */
	      if (strcmp("-remote",auto_open_file_names[auto_open_ctr]) == 0) auto_open_ctr++; else
	      if (strcmp("-p",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)
		    {
		      /* grab session name -- if arg is "." grab latest on this directory */
		      auto_open_ctr++;
		      set_session_name(ss,auto_open_file_names[auto_open_ctr]);
		      auto_open_ctr++;
		    }
		  else
		    {
		      sp = snd_open_file(auto_open_file_names[auto_open_ctr],ss);
		      if (!sp) {tm->slice++; return(FALSE);} /* geez what could have gone wrong... */
		      auto_open_ctr++;
		      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 10: 
      /* this stupid thing (which I can't customize without major hassles) takes forever on large directories */
#ifndef LESSTIF_VERSION
      files = 0;
      if ((dp=opendir(".")) != NULL)
	{
	  while ((files < 400) && (readdir(dp) != NULL)) files++;
	}
      closedir(dp);
      if (files < 400) CreateOpenDialog(tm->shell,(XtPointer)ss);
#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 11:
      if (ss->window_width != 0) set_snd_window_width(ss,ss->window_width);
      if (ss->window_height != 0) set_snd_window_height(ss,ss->window_height);
      restore_state(ss,(ss->session_name) ? ss->session_name : ss->backup_file); /* could I trust C's || operator here? */
      break;
    case 12: 
      XtAppAddTimeOut((ss->sgx)->mainapp,(unsigned long)(CORRUPTION_TIME*1000),corruption_check,(XtPointer)ss);
      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 n;
  sndres snd_rs;
  state_context *sx;
  int scr;
  Colormap cmap;
  XColor tmp_color,ignore;
#if defined(NEXT)
  Widget toplevel;
#endif

  XtSetLanguageProc(NULL,NULL,NULL);

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

#if defined(SCO5) || defined(UW2) || defined(SOLARIS)
  /*
  ** 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 );
#endif

#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);
#endif

#if !defined(NEXT) && !defined(SCO5) && !defined(UW2) && !defined(SOLARIS)
  shell = XtVaOpenApplication(&app,"mainshell",NULL,0,&argc,argv,NULL,applicationShellWidgetClass,
			      XmNallowShellResize,AUTO_RESIZE_DEFAULT,
#ifdef LESSTIF_VERSION
			      XmNwidth,100,XmNheight,100,
#endif
			      NULL);
#endif

  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);
  state->using_schemes = (strcmp(snd_rs.use_schemes,"all") == 0);
  state->auto_resize = 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

  cmap=DefaultColormap(dpy,scr);

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

  sx->white = WhitePixel(dpy,scr);
  sx->black = BlackPixel(dpy,scr);
  state->place_scroll_size = 13;

  /* now try to get colors and fonts that are reasonable */

  if ((!XAllocNamedColor(dpy,cmap,snd_rs.lightest_color,&tmp_color,&ignore)) &&
      (!XAllocNamedColor(dpy,cmap,LIGHTEST_COLOR,&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)))
    {
      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)))
    {
      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)))
    {
      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)))
    {
      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->black;
    }
  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)))
    {
      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)))
    {
      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.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);

  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;
  state->backup_file = snd_rs.backup_file_name;
  state->eps_file = snd_rs.eps_file_name;
  state->default_output_type = snd_rs.default_output_type;
  state->sonogram_color = snd_rs.sonogram_color;
  state->spectrogram_color = snd_rs.spectrogram_color;
  state->ask_before_overwrite = snd_rs.overwrite_check;
  state->ngroups = snd_rs.ngroups;
  if (state->ngroups <= 0) state->ngroups = 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++;
  XtSetArg(args[n],XmNsashHeight,MAIN_SASH_SIZE); n++;
  XtSetArg(args[n],XmNsashWidth,MAIN_SASH_SIZE); n++;
  XtSetArg(args[n],XmNsashIndent,MAIN_SASH_INDENT); n++;
  sx->mainpane = XtCreateManagedWidget("mainpane",xmPanedWindowWidgetClass,shell,args,n);
#if ICON_TYPE
  SetupIcon(shell);
#endif

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

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

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


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

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;

  hlp = (help_win *)calloc(1,sizeof(help_win));
  titlestr = XmStringCreateLocalized(snd_string_Help);
  n=0;
  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;
  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);
  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);
    }
  ss->help_monolog = hlp;
}

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 (!(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);
  XmTextSetString(h->text,helpstr);
  if (!XtIsManaged(h->help)) XtManageChild(h->help);
  XmStringFree(xstr1);
}

void add_snd_help(snd_state *ss, char *str)
{
  int pos;
  help_win *h;
  if (!(ss->help_monolog)) create_help_monolog(ss); else raise_dialog(((help_win *)(ss->help_monolog))->help);
  h = (help_win *)(ss->help_monolog);
  pos = XmTextGetLastPosition(h->text);
  if (pos == 0) XmTextSetString(h->text,str);
  else XmTextInsert(h->text,pos,str);
  if (!XtIsManaged(h->help)) XtManageChild(h->help);
}

void ssnd_help(snd_state *ss, char *subject, ...)
{
  help_win *h;
  XmString xstr1;
  va_list ap;
  char *helpstr;
  int pos;
  va_start(ap,subject);
  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);
  pos = 0;
  while ((helpstr = va_arg(ap,char *)))
    {
      if (pos == 0) XmTextSetString(h->text,helpstr);
      else XmTextInsert(h->text,pos,helpstr);
      pos = XmTextGetLastPosition(h->text);
    }
  va_end(ap);
  if (!XtIsManaged(h->help)) XtManageChild(h->help);
  XmStringFree(xstr1);
}  

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);
  ss->window_width = width;
}

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

void sound_show_ctrls(snd_info *sp)
{
  XtUnmanageChild(snd_widget(sp,W_snd_ctrls));
  XtVaSetValues(snd_widget(sp,W_snd_ctrls),XmNpaneMaximum,OPEN_CTRLS_HEIGHT,NULL);
  XtVaSetValues(snd_widget(sp,W_snd_ctrls),XmNpaneMinimum,OPEN_CTRLS_HEIGHT,NULL);
  XtManageChild(snd_widget(sp,W_snd_ctrls));
  XtVaSetValues(snd_widget(sp,W_snd_ctrls),XmNpaneMaximum,1000,NULL);
  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,NULL);
  XtVaSetValues(snd_widget(sp,W_snd_ctrls),XmNpaneMinimum,CLOSED_CTRLS_HEIGHT,NULL);
  XtManageChild(snd_widget(sp,W_snd_ctrls));
  XtVaSetValues(snd_widget(sp,W_snd_ctrls),XmNpaneMaximum,1000,NULL);
  XtVaSetValues(snd_widget(sp,W_snd_ctrls),XmNpaneMinimum,1,NULL);
}

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
/* 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
/* 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

