/* AmiMenus.c - Low Level Menu Objects for Amiga                       */
/* Copyright (c) 1990 by J.K. Lindsey                                  */
/* Additions to XLISP-STAT 2.1 Copyright (c) 1990, by Luke Tierney     */
/* Additions to Xlisp 2.1, Copyright (c) 1989 by David Michael Betz    */
/* You may give out copies of this software; for conditions see the    */
/* file COPYING included with this distribution.                       */

#include <proto/intuition.h>
#include <stdlib.h>
#include "autil2.h"
#include "xlisp.h"
#include "osdef.h"
#include "xlproto.h"
#include "xlsproto.h"
#include "iviewproto.h"
#include "Stproto.h"
#include "xlsvar.h"
#include "amivar.h"

char *get_item_string(LVAL);

#define get_menu_id(m) ((int) getfixnum(slot_value(m, s_id)))

/***********************************************************************/
/**                                                                   **/
/**                        Support Function                           **/
/**                                                                   **/
/***********************************************************************/

static LVAL GetMenuList(void){
   return(slot_value(getvalue(s_menu_proto),s_menu_list));}

/* find the position of the item in the menu */
static int get_item_position(LVAL menu,LVAL item){
   int i;
   LVAL items;
   for(items=slot_value(menu,s_items),i=0;consp(items)&&car(items)!=item;
   i++,items=cdr(items));
   if(item!=car(items))xlfail("item not in the menu");
   return(i);}

/***********************************************************************/
/**                                                                   **/
/**                            Menu Functions                         **/
/**                                                                   **/
/***********************************************************************/

int StMObInstalled(LVAL m){
  return(StMObAllocated(m));}

/* find menu object with given hardware address */
LVAL get_menu_by_hardware(IVIEW_MENU m){
   LVAL menu=0,next;
   for(next=GetMenuList();menu==0&&consp(next);next=cdr(next)){
      if(StMObAllocated(car(next))&&m==get_menu_address(car(next)))menu=car(next);}
   if(!menu)xlfail("can't find menu with this ID");
   return(menu);}

/* find lisp menu with a specified Amiga address */
/*static LVAL get_menu_by_id(IVIEW_MENU m){
   return(get_menu_by_hardware(m));} not used */

/* allocate an Amiga internal menu */
static int id_in_use(int id){
   LVAL next;
   for(next=GetMenuList();consp(next);next=cdr(next)){
      if(id==get_menu_id(car(next)))return(TRUE);}
   return(FALSE);}

static unique_id(void){
   static int id=2000;
   if(id>32000)id=2000;
   id++;
   while(id_in_use(id))id++;
   return(id);}

void StMObAllocateMach(LVAL menu){
   int MenuID,i;
   struct SuperMenu *tmptr,*this;
   tmptr=calloc(1,sizeof(struct SuperMenu));
   if(!tmptr)xlfail("menu allocation failed");
   this=Menu_Ptr;
   i=0;
   if(this){            /* find last menu on list */
      i++;
      while(this->Next){
         i++;
         this=this->Next;}
      this->Next=tmptr;}
   else Menu_Ptr=tmptr;
   if(i>30){
      free(tmptr);
      xlfail("no menu space left");}
   tmptr->MenuNum=i;
   tmptr->Title=getstring(slot_value(menu,s_title));
   tmptr->Next=0;
   tmptr->Items=0;
   set_menu_address(tmptr,menu);
   MenuID=unique_id();
   set_slot_value(menu,s_id,cvfixnum((FIXTYPE)MenuID));}

/* dispose of an Amiga internal menu */
void StMObDisposeMach(LVAL menu){
   int i;
   struct SuperMenu *theMenu,*this,*temp;
   struct SuperMenuItem *tmptr,*ptr;
   if(StMObAllocated(menu)){
      theMenu=get_menu_address(menu);
      this=Menu_Ptr;
      i=0;
      if(this==theMenu){
         temp=this;
         Menu_Ptr=this=this->Next;}
      else {
         while(this->Next&&this->Next!=theMenu){
            i++;
            this=this->Next;}
         if(!this->Next)xlfail("menu not found");
         temp=this->Next;
         this=this->Next=this->Next->Next;}
      while(this){
         this->MenuNum=++i;
         this=this->Next;}
      ptr=temp->Items;
      while(ptr){
         tmptr=ptr;
         ptr=ptr->Next;
         free(tmptr);}
      free(temp);
      StMObInstall(0);}}

/* add items to an Amiga internal menu */
void StMObAppendItems(LVAL menu,LVAL items){
   LVAL item,key;
   int i;
   struct SuperMenu *theMenu;
   struct SuperMenuItem *tmptr,*this,*next;
   if(StMObAllocated(menu)){
      theMenu=get_menu_address(menu);
      i=llength(slot_value(menu,s_items))-llength(items);
      if(i<0)xlfail("append list should not exceed item list");
      i=0;
      tmptr=this=0;
      next=theMenu->Items;
      while(next){
         i++;         /* i is number of items and number for next item */
         this=next;
         next=next->Next;}
      for(;consp(items);items=cdr(items),i++){
         item=car(items);
         tmptr=calloc(1,sizeof(struct SuperMenuItem));
         if(!tmptr)xlfail("menu item allocation failed");
         tmptr->ItemNum=i;
         tmptr->Text=get_item_string(item);
         if(charp(key=slot_value(item,s_key)))tmptr->CmndChar=getchcode(key);
         if(slot_value(item,s_enabled))tmptr->Enabled=1;
         else tmptr->Enabled=0;
         if(this)this->Next=tmptr;
         else theMenu->Items=tmptr;
         this=tmptr;}
      if(tmptr)tmptr->Next=0;
      StMObInstall(menu);}}

/* remove item from an Amiga menu */
void StMObDeleteItem(LVAL menu,LVAL item){
   int i;
   struct SuperMenu *theMenu;
   struct SuperMenuItem *this,*temp;
   if(StMObAllocated(menu)){
      theMenu=get_menu_address(menu);
      i=get_item_position(menu,item);
      this=theMenu->Items;
      if(i){
         while(this->Next->ItemNum!=i)this=this->Next;
         temp=this->Next;
         this=this->Next=this->Next->Next;}
      else {
         temp=this;
         this=theMenu->Items=this->Next;}
      while(this){
         this->ItemNum=i--;
         this=this->Next;}
      free(temp);
      StMObInstall(menu);}}

/* install an Amiga menu */
void StMObInstall(LVAL menu){
   struct Window *w;
   struct SuperMenu *mptr;
   struct SuperMenuItem *iptr;
   int i,j;
   if(!Menu_Ptr)return;
   if(menu&&!StMObAllocated(menu))StMObAllocate(menu);
   if(!(mymenu=BuildMenuStripL(Menu_Ptr,8)))xlfail("menu build failed");
   w=screen->FirstWindow;
   while(w){
      ClearMenuStrip(w);
      SetMenuStrip(w,mymenu);
      mptr=Menu_Ptr;
      i=0;
      while(mptr){
         iptr=mptr->Items;
         j=0;
         while(iptr){
            if(!iptr->Enabled)OffMenu(w,i+(j<<5));
            j++;
            iptr=iptr->Next;}
         i++;
         mptr=mptr->Next;}
      w=w->NextWindow;}}

/* remove an Amiga menu */
void StMObRemove(LVAL menu){
   if(StMObAllocated(menu))StMObDispose(menu);}

/* enable or disable an Amiga menu */
void StMObEnable(LVAL menu,int enable){
   struct SuperMenu *theMenu,*this;
   struct Window *w;
   short i;
   if(StMObAllocated(menu)){
      theMenu=get_menu_address(menu);
      this=Menu_Ptr;
      i=0;
      while(this->Next&&this!=theMenu){
         i++;
         this=this->Next;}
      if(!this->Next)return;
      w=screen->FirstWindow;
      while(w){
         if(enable)OnMenu(w,i);
         else OffMenu(w,i);
         w=w->NextWindow;}}
   set_slot_value(menu,s_enabled,(enable)?s_true:NIL);}

int StMObPopup(LVAL menu,int left,int top,LVAL window){
   return(0);}

/***********************************************************************/
/**                                                                   **/
/**                         Menu Item Functions                       **/
/**                                                                   **/
/***********************************************************************/

/* Get a string for use by AppendMenu. Style info is not encoded. */
static char *get_item_string(LVAL item){
   LVAL title;/*,key,mark,enabled;
   static char *s;*/
   if(!menu_item_p(item))xlerror("not a menu item", item);
   title=slot_value(item,s_title);
   if(!stringp(title))xlerror("title is not a string", title);
/*   key=slot_value(item, s_key);
   mark=slot_value(item, s_mark);
   enabled=slot_value(item, s_enabled);
   s=buf;
   if(!enabled)s+=sprintf(s,"(");
   if(charp(key))s+=sprintf(s,"/%c",getchcode(key));
   if(mark==s_true)s+=sprintf(s,"!%c",0x12);
   else if(charp(mark))s+=sprintf(s,"!%c",getchcode(key));
   sprintf(s,"%s",getstring(title));*/
   return((char *)getstring(title));}

/* adjust internal implementation of allocated menu to new instance value */ 
void StMObSetItemProp(LVAL item,int which){
   struct SuperMenu *theMenu,*mptr;
   struct SuperMenuItem *iptr;
   LVAL menu;
   int j;
   menu=slot_value(item,s_menu);
   if(menu&&StMObAllocated(menu)){
      theMenu=get_menu_address(menu);
      mptr=Menu_Ptr;
      while(mptr&&mptr!=theMenu){
         mptr=mptr->Next;}
      if(!mptr)xlfail("menu not found");
      j=get_item_position(menu,item);
      iptr=theMenu->Items;
      if(j)while(iptr&&iptr->ItemNum!=j)iptr=iptr->Next;
      if(!iptr)xlfail("menu item not found");
printf("which=%c\n",which);
      switch(which){
         case 'T': {
            LVAL title=slot_value(item,s_title);
            if(!stringp(title))xlerror("title is not a string",title);
            iptr->Text=getstring(title);
            break;}
         case 'K': {
            LVAL key=slot_value(item,s_key);
            if(charp(key))iptr->CmndChar=getchcode(key);
            break;}
         case 'M': {
            LVAL mark=slot_value(item,s_mark);
            if(mark==s_true)iptr->SetFlags|=CHECKIT|MENUTOGGLE|CHECKED;
            else iptr->SetFlags&=~CHECKIT&~MENUTOGGLE&~CHECKED;
            break;}
         case 'S':
         case 'A': break;
         case 'E': {
            if(slot_value(item,s_enabled))iptr->Enabled=1;
            else iptr->Enabled=0;
            break;}
         default: xlfail("unknown item instance variable");}
      StMObInstall(menu);}}

/* about alert for the compiler */
LVAL xsabout_xlisp_stat(void){
   struct IntuiText text[]={{1,2,JAM2,20,5,0,"Lattice C, V5.05",0},
                            {0,1,JAM2,5,4,0,"OK",0}};
   AutoRequest(window,&text[0],0,&text[1],0,0,200,50);
   return(NIL);}
