/*  File: menu.c
 *  Author: Jean Thierry-Mieg (mieg@mrc-lmb.cam.ac.uk)
 *  Copyright (C) J Thierry-Mieg and R Durbin, 1992-1995
 * -------------------------------------------------------------------
 * This file is part of the ACEDB genome database package, written by
 * 	Richard Durbin (MRC LMB, UK) rd@mrc-lmb.cam.ac.uk, and
 *	Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * -------------------------------------------------------------------
 * This file is part of the ACEDB genome database package, written by
 * 	Richard Durbin (MRC LMB, UK) rd@mrc-lmb.cam.ac.uk, and
 *	Jean Thierry-Mieg (CRBM du CNRS, France) mieg@kaa.cnrs-mop.fr
 *
 * Description:
 * Exported functions: all in menu.h
 * HISTORY:
 * Last edited: Oct  8 15:02 1999 (fw)
 * * Jul  7 13:35 1999 (edgrif): Added menuSetLabel to change menu label.
 * * Jan 28 16:07 1999 (edgrif): small changes caused by menu.h/menu_.h
 *              changes for opaque type defs.
 * * Jan  9 22:52 1995 (rd): complete replacement for new menu package
 * * Jul 23 21:53 1992 (rd): big change because of new menus
     must have a separate MENUOPT* for each combination of entries
 * Created: Mon Jun 15 14:43:55 1992 (mieg)
 *-------------------------------------------------------------------
 */

/* $Id: menu.c,v 1.14 2002/02/22 18:00:21 srk Exp $ */
 
#include "regular.h"
#include "call.h"		/* for call() and callExists() */
#include "menu_.h"		/* private header for menu package */

/****************** MENU manipulation *******************/

MENU menuCreate (char *title)
{
  MENU menu = (MENU) messalloc (sizeof (MenuStruct)) ;
  menu->title = title ;
  menu->items = 0 ;

  return menu ;
}

MENU menuCopy (MENU oldMenu)
{
  MENU newMenu = (MENU) messalloc (sizeof (MenuStruct)) ;
  MENUITEM newItem, oldItem ;

  newMenu->title = oldMenu->title ;
  for (oldItem = oldMenu->items ; oldItem ; oldItem = oldItem->down)
    { newItem = menuCreateItem (oldItem->label, oldItem->func) ;
      *newItem = *oldItem ;
      menuAddItem (newMenu, newItem, 0) ;
    }

  return newMenu ;
}

void menuDestroy (MENU menu)
{
#ifdef NO_WE_CACHE_MENUS_IN_ASS_BELOW__DONT_DESTROY_HERE
  MENUITEM item = menu->items ;
  while (item)
    { MENUITEM next = item->down ;
      messfree (item) ;
      item = next ;
    }

  messfree (menu) ;
#endif
}

MENU menuInitialise (char *title, MENUSPEC *spec)
{
  MENU menu ;
  MENUITEM item ;
  static Associator ass = 0 ;
  static MENUSPEC *localSpec ;

  /* getMutex ("menuInitialise") ; since we handle a static ass */
  if (!ass)
    ass = assCreate () ;

  if (assFind (ass, spec, &menu))
    return menu ;

  if (!spec)
    spec = localSpec ;
  menu = menuCreate (title) ;
  while (spec->text)
    { item = menuCreateItem (spec->text, spec->f) ; 
      if (!*spec->text)	/* convenience - blank menu items are spacer */
	menuSetFlags (item, MENUFLAG_SPACER) ;
      if (!spec->f)		/* a submenu */
	{ localSpec = spec+1 ;
	  item->submenu = menuInitialise (spec->text, 0) ;
	  spec = localSpec ;
	}
      else 
	item->submenu = 0 ;
      menuAddItem (menu, item, 0) ;
      ++spec ;
    }
  localSpec = spec ;

  /* releaseMutex ("menuInitialise") ; */
  return menu ;
}

/******************* MENUITEM manipulation ******************/

MENUITEM menuCreateItem (char *label, MENUFUNCTION func)
{
  MENUITEM item = (MENUITEM) messalloc (sizeof (MenuItemStruct)) ;
  memset (item, 0, sizeof (MenuItemStruct)) ;
  item->label = label ;
  item->func = func ;
  return item ;
}

MENUITEM menuItem (MENU menu, char *label)
{
  MENUITEM item ;
  for (item = menu->items ; item && strcmp (label, item->label) ; item = item->down) ;
  return item ;
}

BOOL menuAddItem (MENU menu, MENUITEM item, char *beforeLabel)
{
  MENUITEM below, above = 0 ;

  if (!item) return FALSE ;

  if (beforeLabel)
    { for (below = menu->items ; 
	   below && strcmp (below->label, beforeLabel) ;
	   above = below, below = below->down) ;
      if (!below)
	return FALSE ;
    }
  else
    for (below = menu->items ; below ; above = below, below = below->down) ;

  if (above)
    { above->down = item ;
      item->up = above ;
    }
  else
    { menu->items = item ;
      item->up = 0 ;
    }

  item->down = below ;
  if (below)
    below->up = item ;

  return TRUE ;
}

BOOL menuDeleteItem (MENU menu, char *label)
{
  MENUITEM item ;

  if (!(item = menuItem (menu, label)))
    return FALSE ;

  if (item->up)
    item->up->down = item->down ;
  else
    menu->items = item->down ;

  if (item->down)
    item->down->up = item->up ;

  if (item->submenu)
    menuDestroy (item->submenu) ;

  messfree (item) ;

  return TRUE ;
}

BOOL menuSelectItem (MENUITEM item)
{
  BOOL changed = FALSE ;

  if (item->flags & (MENUFLAG_DISABLED | MENUFLAG_SPACER))
    return FALSE ;

  if (item->flags & MENUFLAG_TOGGLE)
    { 
      if (item->flags & MENUFLAG_TOGGLE_STATE)/* if flag is set */
	item->flags &= ~MENUFLAG_TOGGLE_STATE; /* unset flag */
      else
	item->flags |= MENUFLAG_TOGGLE_STATE; /* set flag */

      changed = TRUE ;
    }
  else if (!(item->flags & MENUFLAG_RADIO_STATE))
    { 
      MENUITEM first, last ;

      for (first = item ; 
	   first && !(first->flags & MENUFLAG_START_RADIO) ; 
	   first = first->up) ;
      for (last = item ; 
	   last && !(last->flags & MENUFLAG_END_RADIO) ; 
	   last = last->down) ;
      if (first && last)
	{ while (first != last)
	    { first->flags &= ~MENUFLAG_RADIO_STATE ;
	      first = first->down ;
	    }
	  item->flags |= MENUFLAG_RADIO_STATE ;
	  changed = TRUE ;
	}
    }

  /* Call user func with new menu flags and states, 
   * so the user has a chance to change them if the code requires so.
   * Lazy code relying on the implied logic of toggles and radios
   * need to do nothing to the menuitem-flags in the registered func */
  if (item->func)
    (*item->func)(item) ;
  else if (item->call)
    call (item->call, item) ;

  return changed ;
} /* menuSelectItem */

/***************** set item properties *******************/
BOOL menuSetLabel (MENUITEM item, char *label)
{ if (!item) return FALSE ;
  item->label = label ; return TRUE ;
}

BOOL menuSetCall (MENUITEM item, char *callName)
{ if (!item || !callExists (callName)) return FALSE ;
  item->call = callName ; return TRUE ;
}

BOOL menuSetFunc (MENUITEM item, MENUFUNCTION func)
{ if (!item) return FALSE ;
  item->func = func ; return TRUE ;
}

BOOL menuSetFlags (MENUITEM item, unsigned int flags)
{ if (!item) return FALSE ;
  item->flags |= flags ; return TRUE ;
}

BOOL menuUnsetFlags (MENUITEM item, unsigned int flags)
{ if (!item) return FALSE ;
  item->flags &= ~flags ; return TRUE ;
}

BOOL menuSetValue (MENUITEM item, int value)
{ if (!item) return FALSE ;
  item->value = value ; return TRUE ;
}

BOOL menuSetPtr (MENUITEM item, void *ptr)
{ if (!item) return FALSE ;
  item->ptr = ptr ; return TRUE ;
}

BOOL menuSetMenu (MENUITEM item, MENU menu)
{ if (!item || !menu) return FALSE ;
  item->submenu = menu ; return TRUE ;
}

/***************** get item properties *******************/

unsigned int menuGetFlags (MENUITEM item)
{ if (!item) return 0 ;
  return item->flags ;
}

int menuGetValue (MENUITEM item)
{ if (!item) return 0 ;
  return item->value ;
}

void* menuGetPtr (MENUITEM item)
{ if (!item) return 0 ;
  return item->ptr ;
}

/**************** utilities *******************/

void menuSuppress (MENU menu, char *string)
{
  BOOL inBlock = FALSE ;
  BOOL hide = FALSE ;
  MENUITEM item ;

  for (item = menu->items ; item ; item = item->down)
    { if (!inBlock && !strcmp (item->label, string))
	hide = TRUE ;
      if (hide)
	item->flags |= MENUFLAG_HIDE ;
      if (item->flags & MENUFLAG_SPACER)
	{ inBlock = FALSE ;
	  hide = FALSE ;
	}
      else
	inBlock = TRUE ;
    }
}

void menuRestore (MENU menu, char *string)
{
  BOOL inBlock = FALSE ;
  BOOL unHide = FALSE ;
  MENUITEM item ;

  for (item = menu->items ; item ; item = item->down)
    { if (!inBlock && !strcmp (item->label, string))
	unHide = TRUE ;
      if (unHide)
	item->flags &= ~MENUFLAG_HIDE ;
      if (item->flags & MENUFLAG_SPACER)
	{ inBlock = FALSE ;
	  unHide = FALSE ;
	}
      else
	inBlock = TRUE ;
    }
}

#if !defined(MACINTOSH)
void menuSpacer (void) { return ; }
#endif

/***************** end of file ****************/
