/*
    ####             #    #     # #
    #   #            #    #       #          The FreeWare C library for
    #   #  ##   ###  #  # #     # ###             RISC OS machines
    #   # #  # #     # #  #     # #  #   ___________________________________
    #   # ####  ###  ##   #     # #  #
    #   # #        # # #  #     # #  #    Please refer to the accompanying
    ####   ### ####  #  # ##### # ###    documentation for conditions of use
    ________________________________________________________________________

    File:    Menu.NewMenu.c
    Author:  Copyright  1993 Shaun Blackmore and Jason Williams,
                        1994 Tony Houghton
                        1995 Neil Tarrant
    Version: 1.05 (16 May 1995)
    Purpose: Creates a new menu given a textual description string
    Mods:    24 Sep 1994  -  TH  -  Can now cope with indirected menu titles in
                                    >= RISC OS 3.1
             16 May 1995  - Neil Tarrant - malloc(1+titlelen).
*/


#include <stdlib.h>
#include <string.h>

#include "Desk.Core.h"
#include "Desk.Error.h"
#include "Desk.Event.h"
#include "Desk.Screen.h"
#include "Desk.Wimp.h"
#include "Desk.Menu.h"
#include "Desk.DeskMem.h"

#include "MenuDefs.h"


/* Description format:
 *      opt   :- " "  no special options (i.e. leading spaces are skipped)
 *               "!"  ticked
 *               "~"  shaded
 *               ">"  has submenu/subwindow
 *               "-"  dotted
 *      name  :- any character except ", " and "|"
 *      entry :- {opt}* name
 *      sep   :- ", "  to separate normal items
 *               "|"  to separate items and place a dotted line between them
 *      descr :- entry {sep entry}*
 *
 *      EXAMPLE:
 *        ">Info, Create, Quit"
 *        "!Ticked, ~Disabled|>Submenu"
 */


extern void Desk_Menu__CountItems(const char *description, int *numitems, int *menuwidth)
/*  For internal use - counts the number of items in a menu description
 *  string, and also returns the widest menu item (in terms of characters)
 */
{
  const char *s;
  int  width = 0, maxwidth = 0, count = 0;
  Desk_bool Desk_in_option = Desk_bool_TRUE;

  for (s = description; *s; s++)
  {
    if (*s == ', ' || *s == '|')
    {
     if (width > maxwidth)
       maxwidth = width;
     count++;
     width = 0;
     Desk_in_option = Desk_bool_TRUE;
    }
    else
    {
      if (Desk_in_option)
      {
        if(*s != '!' && *s != '>' && *s != '~' && *s != ' ')
        {
          Desk_in_option = Desk_bool_FALSE;
          width++;
        }
      }
      else
        width++;
    }
  }

  *numitems = count + 1;                    /* Count last item and its width */

  if (width > maxwidth)
    maxwidth=width;

  *menuwidth = maxwidth + 1;       /* Add room for a gap/sublink arrows, etc */
}


extern Desk_bool Desk_Menu__Create(Desk_menu_item *item, const char *description, int numitems)
{
  Desk_menu_item *it;
  const char *s;
  char *t;
  int  i, index;
  Desk_bool foundtext;

  s = description;
  for (i = 0; i < numitems; i++)
  {
    it = &item[i];
    it->menuflags.value = 0;
    it->submenu.value   = NULL;
    it->iconflags.value = Desk_icon_TEXT | Desk_icon_FILLED;
    it->iconflags.data.foreground = 7;

    foundtext = Desk_bool_FALSE;
    do
    {
      switch(*s++)
      {
        case '!':
          it->menuflags.data.ticked = Desk_bool_TRUE;
          break;

        case '~':
          it->iconflags.data.shaded = Desk_bool_TRUE;
          break;

        case '>':
          it->menuflags.data.notifysub = Desk_bool_TRUE;  /* Ask for sublink warnings */
          it->submenu.value = 1;                /* This is sublink item '1' */
          break;

        case ' ':  /* No option */
          break;

        default:   /* Any other == start of menu item text, so don't lose it */
          s--;
          foundtext = Desk_bool_TRUE;
          break;
      }
    } while (!foundtext);

    for (index = 0; ; index++)
      if (s[index] == 0 || s[index] == '|' || s[index] == ', ')
        break;

    if (index <= Desk_wimp_MAXNAME)
      t = it->icondata.text;            /* Copy text directly into menu item */
    else
    {
      /* Copy text into indirected menu item */
      it->iconflags.data.indirected = Desk_bool_TRUE;
      it->icondata.indirecttext.buffer = Desk_DeskMem_XMalloc(index + 1);
      if (it->icondata.indirecttext.buffer == NULL)
        return(Desk_Error_OutOfMemory(Desk_bool_FALSE, "menus"));

      it->icondata.indirecttext.bufflen = index + 1;
      it->icondata.indirecttext.validstring = (char *) -1;

      t = it->icondata.indirecttext.buffer;
    }

    while (*s != 0 && *s != ', ' && *s != '|')
      *t++ = *s++;

    if (index != Desk_wimp_MAXNAME)     /* Only terminate if not exactly 12 chars */
      *t = 0;

    if (*s++ == '|')                        /* Step over separator...        */
      it->menuflags.data.dotted = Desk_bool_TRUE;     /* ...setting 'dotted' if needed */
  }

  return(Desk_bool_TRUE);
}



extern Desk_menu_ptr Desk_Menu_New(const char *title, const char *description)
{
  Desk_menu_ptr   menu;
  Desk_menu_item  *item;
  int        numitems, maxwidth;
  int        titlelen;
  int        trunclen;

  /*  Copy the string. If it's less than 12 characters we need it to be
   *  zero-terminated. We can let it go to 12 chars without any terminator
   *  though, so we don't need to bother terminating after the strncpy.
   */

  Desk_Menu__CountItems(description, &numitems, &maxwidth);

  titlelen = strlen(title);

  /* Make sure menu is wide enough for truncated title  */
  trunclen = titlelen > Desk_wimp_MAXNAME ? Desk_wimp_MAXNAME : titlelen;
  if (trunclen > maxwidth)
    maxwidth = trunclen;

  menu = (Desk_menu_ptr) Desk_DeskMem_XMalloc(sizeof(Desk_menu_block) + (numitems * sizeof(Desk_menu_item)));

  if (menu == NULL)
    return((Desk_menu_ptr) Desk_Error_OutOfMemory(Desk_bool_FALSE, "menus"));

  item = (Desk_menu_item *) &(menu[1]);

  strncpy(menu->title, title, Desk_wimp_MAXNAME);

  menu->titlefore = 7;
  menu->titleback = 2;
  menu->workfore  = 7;
  menu->workback  = 0;
  menu->width     = maxwidth * 16;
  menu->height    = 44;
  menu->gap       = 0;

  if (!Desk_Menu__Create(item, description, numitems))
    return((Desk_menu_ptr) NULL);
  item[numitems - 1].menuflags.data.last = Desk_bool_TRUE;

  /* If title > 12 chars and we've got RISC OS 3 make it indirected */
  if (titlelen > Desk_wimp_MAXNAME && Desk_Event_wimpversion >= 310)
  {
    char *titledata;
    Desk_icon_data *titleicon;
    Desk_menu_item *firstitem;

    /* Set up data as for indirected text icon */
    if (titledata = Desk_DeskMem_XMalloc(1+titlelen), !titledata)	/* +1 courtesy of Neil Tarrant */
      return (menu);
    strcpy(titledata, title);
    titleicon = (Desk_icon_data *) &menu->title;
    titleicon->indirecttext.buffer = titledata;
    titleicon->indirecttext.validstring = (char *) -1;
    titleicon->indirecttext.bufflen = titlelen;

    /* Set flag in first item to inicate indirected title */
    firstitem = (Desk_menu_item *) ((int) menu + sizeof(Desk_menu_block));
    firstitem->menuflags.data.indtitle = 1;

    /* Make sure it is wide enough for indirected title  */
    if (titlelen > maxwidth)
      maxwidth = strlen(title);
  }
  return(menu);
}

