/*
 * DLibrary, an example program using the Acorn Toolbox and TPlusLib.
 * (c) Tony Howat / 20-20 Software, and RISC User 1997
 *
 * Version : 1.05
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "kernel.h"
#include "swis.h"

#include "event.h"
#include "toolbox.h"
#include "gadgets.h"
#include "wimplib.h"
#include "window.h"
#include "proginfo.h"
#include "fileinfo.h"
#include "saveas.h"
#include "menu.h"
#include "TPlusLib:drawwin.h"
#include "TPlusLib:flex.h"
#include "tpluslib:tplus.h"
#include "tpluslib:panes.h"
#include "tpluslib:alarm.h"
#include "TPluslib:duptask.h"
#include "addwin.h"

/* user messages */
#define MAIN_HIDDEN          1
#define MAIN_OPEN            2
#define MENU_QUIT            3
#define MENU_DELETE          4
#define DISPLAY_MENU_OPENING 5
#define QUICKMENU_OPENING    6
#define QUICKMENU_CLICK      7

/* components in main window */
#define CDESCRIPTION  4
#define CFILENAME     2
#define CTIME         6
#define CLAST         2
#define CNEXT         1
#define CNUMBER       0
#define CPOPUPFILE    8
#define CPOPUPDESC    9

/* components in display menu */
#define CDABOUT       3
#define CDSAVE        0
#define CDDELETE      2

/* raises any error x */
#define are(x) if(x) { raise_error(x); exit(0); }

#define FID   77        /* number of files to put in a directory */
#define FIDSQ (FID*FID) /* number of files in a directory squared */

#define VERSION "1.05"

/* structure for each record in the catalogue file */
typedef struct {
  char filename[255];
  char description[255];
  int time[2];
  int  number;
} catentry;

MessagesFD mfd; /* messages file */

static int message_list [] = {0};
static int event_list   [] = {0};
static IdBlock       id_blk;
static WimpPollBlock poll_block;

/* draw_length = length of currently loaded draw file
 * drawdata    = pointer to block of memory containing currently
 *               loaded draw file
 * cat_entries = number of drawings currently in the catalogue
 * current_pic = the number of the picture in the catalogue currently
 *               being displayed
 * pane, mainw, ctrlwin
 *             = object ids of the pane window (ie the one with the
 *               drawfile in it, the main window (the host window for
 *               the pane) and the control toolbar.
 * ccsl_filemenu, ccsl_descmenu
 *             = set to 1 if the catalogue has changed since the last
 *               time the file or description pop up menus were
 *               displayed. If they haven't changed we don't need to
 *               regenerate them.
 */
static int draw_length=0,current_pic=-1,*drawdata=NULL, cat_entries=0;
static int ccsl_filemenu=1,ccsl_descmenu=1;
static ObjectId pane=NULL, mainw=NULL, ctrlwin=NULL;
static catentry *cat=NULL;

/* -- find the size of a file using OS_File 17 ---
 * -- returns 0 if a file doesn't exist        --- */

int file_size(char *file)
{
  _kernel_swi_regs r;
  _kernel_oserror *e;

  r.r[0] = 17;
  r.r[1] = (int)file;
  if((e=_kernel_swi(OS_File, &r, &r))!=NULL) {
    raise_error(e);
    return 0;
  }

  if(r.r[0]==0) /* file doesn't exist */
    return 0;
  else
    return r.r[4]; /* return length */
}

/* --- loads the catalogue file into memory --- */

void cat_load(void)
{
  int cat_length;
  _kernel_swi_regs r;

  /* allocate memory for and load in the catalogue file */
  cat_length=file_size("<DLibrary$Dir>.Catalogue");
  if(flex_alloc((flex_ptr)&cat, cat_length) == FALSE) {
    werr(TRUE, msgs_lookup("faca:No room, failed to allocate %iK for catalogue area"),cat_length/1024);
    return;
  }

  /* load it in using OS_File 16 */
  r.r[0] = 16;
  r.r[1] = (int)"<DLibrary$Dir>.Catalogue";
  r.r[2] = (int)cat;
  r.r[3] = 0;
  if(_kernel_swi(OS_File, &r, &r)) {
    flex_free((flex_ptr)&cat);
    return;
  }

  /* update cat_entries with the number of entries in the newly loaded
   * catalogue */
  cat_entries=cat_length/sizeof(catentry);
  /* if there are any entries in the newly loaded catalogue we should
   * start at the first one, otherwise we can go to the default picture
   */
  if(cat_entries)
    current_pic=1;
}

/* --- save the catalogue file  --- */

void cat_save(void)
{
  _kernel_swi_regs r;
  _kernel_oserror *e;

  /* save using os_file 10 */
  r.r[0] = 10;
  r.r[1] = (int)"<DLibrary$Dir>.Catalogue";
  r.r[2] = 0xffd; /* data */
  r.r[4] = (int)cat;  /* start address */
  r.r[5] = (int)cat+flex_size((flex_ptr)&cat); /* end address */
  if((e=_kernel_swi(OS_File, &r, &r))!=NULL) {
    raise_error(e);
    return;
  }
}

/* --- check that a directory exists, if it doesn't create it. if ---
 * --- something gets in the way complain.                        --- */

int checkdirexists(char *path)
{
  _kernel_swi_regs r;
  _kernel_oserror *e;

  /* check to see if something of this name already exists */
  r.r[0] = 13;
  r.r[1] = (int)path;
  if((e=_kernel_swi(OS_File, &r, &r))!=NULL) {
    raise_error(e);
    return 1;
  }

  switch(r.r[0]) 
  {
    case 0: /* no directory of that name has been found, create it */
    {
      _kernel_swi_regs r;
      _kernel_oserror *e;
    
      r.r[0] = 8;
      r.r[1] = (int)path;
      r.r[4] = 0;
      if((e=_kernel_swi(OS_File, &r, &r))!=NULL) {
        raise_error(e);
        return 1;
      }
      return 0;
    }
    case 2:  /* existing directory found, so use it */
      return 0;
    default: /* image or file found in place, complain */
      werr(FALSE,msgs_lookup("nmfda:%s is a file or image which should not be there"),path);
      return 1;
  }
}


/* --- Good-bye! --- */

int wimp_quit (WimpMessage *message, void *h) {
  /* nothing to tidy up */
  exit(0);
  return(0); /* Keep the compiler happy */
}

/* --- Click on "Quit" in the menu --- */

int menu_quit (int event_code, ToolboxEvent *event_block,
                   IdBlock *id_blk, void *h)
{
  /* nothing to tidy up */
  exit(0);
  return(0); /* Keep the compiler happy */
}

/* --- our main window is hidden free up diagram memory --- */

int main_hidden (int event_code, ToolboxEvent *event_block,
                   IdBlock *id_blk, void *h)
{
  flex_free((flex_ptr)&drawdata); /* free up flex memory for diagram */
  drawwin_deregister(pane);       /* deregister pane from drawwin */
  drawdata=NULL; /* clear old drawdata pointer */
  draw_length=0; /* we have no drawing loaded, so length = 0 */
  mainw=NULL;
  return 0;
}

/* return contents of address, that may not be word aligned */

static unsigned int contents_of(int *a)
{
   unsigned char *ad = (unsigned char *) a;
   unsigned int v = (((*(ad+3)) <<24) + 
          ((*(ad+2)) <<16) + 
          ((*(ad+1)) <<8 ) + *ad);

   return v;
}

#define SQUASH  0x48535153                /* SQSH */

/* --- load and show a new picture --- */
void update_pic_display(void)
{
  _kernel_swi_regs r;
  char file[80]="<DLibrary$Dir>.Picture";
  char timestr[80];

  /* generate a pathname from the number */
  if(current_pic!=-1)
    sprintf(file,"<DLibrary$Dir>.Files.%x.%x.%x",
                 cat[current_pic-1].number/FIDSQ,
                 (cat[current_pic-1].number%FIDSQ)/FID,
                 cat[current_pic-1].number);
 
  /* get rid of previous drawing (if it was loaded at all) */
  if(drawdata!=NULL)
  {
    flex_free((flex_ptr)&drawdata);
    drawwin_deregister(pane);
  }

  /* allocate memory for and load in the drawing */
  draw_length=file_size(file);
  if(flex_alloc((flex_ptr)&drawdata, draw_length) == FALSE) {
    werr(0, msgs_lookup("nmfda:No room, failed to allocate %iK for draw area"),draw_length/1024);
    return;
  }

  r.r[0] = 16;
  r.r[1] = (int)file;
  r.r[2] = (int)drawdata;
  r.r[3] = 0;
  if(_kernel_swi(OS_File, &r, &r)) {
    flex_free((flex_ptr)&drawdata);
    return;
  }

  if(contents_of(drawdata) == SQUASH) {
    /* it's squashed */
    _kernel_oserror *e;
    char *wkspc=0;
    int ss,q;
    int *s;

    /* note that drawdata is of type int, and so adding one to a pointer
     * actually increases it by sizeof(int), which is 4. Forgetting this
     * simple fact caused me no end of bother.
     */
    q = contents_of(drawdata+1);        /* size */

    if(flex_alloc((flex_ptr)&s, draw_length) == FALSE) {
      werr(0, msgs_lookup("nmfda:No room, failed to allocate %iK for draw area"),draw_length/1024);
      return;
    }

    memcpy(s,drawdata,draw_length); /* move the squashed data to a temp */
    flex_free((flex_ptr)&drawdata);

    if(flex_alloc((flex_ptr)&drawdata, q) == FALSE) {
      werr(0, msgs_lookup("nmfda:No room, failed to allocate %iK for draw area"),draw_length/1024);
      return;
    }

    r.r[0]=8;
    r.r[1]=draw_length-20;

    if (_kernel_swi(Squash_Decompress, &r, &r))
    {
      flex_free((flex_ptr)&drawdata);
      flex_free((flex_ptr)&s); 
      werr(0, msgs_lookup("sa:Squash error"));
      return;
    }

    ss=r.r[0];
    wkspc = malloc(ss);
    if(!wkspc)
    {
      werr(FALSE,"Failed to allocate %iK squash workspace",ss/1024);
      return;
    }

    r.r[0]=4;
    r.r[1]=(int)wkspc;
    r.r[2]=(int)(s+5);
    r.r[3]=draw_length-20;
    r.r[4]=(int)drawdata;
    r.r[5]=q;

    if ((!wkspc) || ((e=_kernel_swi(Squash_Decompress, &r, &r))!=NULL)) {
       flex_free((flex_ptr)&drawdata);
       flex_free((flex_ptr)&s); 
       if (wkspc) free (wkspc);
       if(e)
         raise_error(e);
       else
         werr(0, msgs_lookup("sa:Squash error"));
       return;
    }
    free(wkspc);

    draw_length=q;
    flex_free((flex_ptr)&s);
  }

  /* register the drawing to our pane window */
  drawwin_register(DRAWWIN_SCALE_PIC_BESTFIT,pane,(void **)&drawdata,draw_length,95);

  /* update the icons in the main window */
  if(current_pic!=-1)
  {
    /* convert risc-os 5 byte time into text */
    r.r[0]=(int)cat[current_pic-1].time;
    r.r[1]=(int)timestr;
    r.r[2]=80;
    _kernel_swi(OS_ConvertStandardDateAndTime, &r, &r);
    displayfield_set_value(0,mainw,CFILENAME,cat[current_pic-1].filename);
    displayfield_set_value(0,mainw,CTIME,timestr);
    /* if it's blank update_controls will fill in the description with
     * Drawing <n> later
     */
    displayfield_set_value(0,mainw,CDESCRIPTION,cat[current_pic-1].description);
  } else {
    displayfield_set_value(0,mainw,CFILENAME,msgs_lookup("none:None"));
    displayfield_set_value(0,mainw,CTIME,"");
    displayfield_set_value(0,mainw,CDESCRIPTION,"");
  }
}

/* --- fill in the current picture etc on the toolbar and grey out ---
 * --- the arrow gadgets if necessary                              --- */
void update_controls(void)
{
   char buffer[40];

   if(cat_entries>0)
     sprintf(buffer,msgs_lookup("nofn:%i of %i"),current_pic,cat_entries);
   else
     sprintf(buffer,msgs_lookup("empty:Empty"));

   fade_gadget(ctrlwin,CLAST,(cat_entries==0 || current_pic==1));
   fade_gadget(ctrlwin,CNEXT,(cat_entries==0 || current_pic==cat_entries));
   displayfield_set_value(0,ctrlwin,CNUMBER,buffer);

   /* also fade out the pop-up buttons if no drawings are in the catalogue */
   fade_gadget(mainw,CPOPUPFILE,(cat_entries==0));
   fade_gadget(mainw,CPOPUPDESC,(cat_entries==0));

   /* if the description for the drawing is blank fill it in with Drawing
    * <number>. This code is here so when a drawing is deleted if the
    * drawing number is affected it is updated.
    */
   if(current_pic!=-1)
   {
     if(!strcmp(cat[current_pic-1].description,""))
     {
       char desc[80];
       sprintf(desc,msgs_lookup("dwg:Drawing %i"),current_pic);
       displayfield_set_value(0,mainw,CDESCRIPTION,desc);
     }
   }
}


/* --- our main window is hidden free up diagram memory --- */

int change_drawing (int event_code, ToolboxEvent *event_block,
                    IdBlock *id_blk, void *h)
{
  AdjusterClickedEvent *ace=(AdjusterClickedEvent *)event_block;

  if(ace->direction)
   ++current_pic;
  else
   --current_pic;

  update_pic_display();
  update_controls();

  return 0;
}

/* type definition for standard squash file header, see RISC OS 3
 * PRM 4-493
 */

typedef struct
{
  int id;              /* SQSH */
  unsigned int length;
  unsigned int load;
  unsigned int exec;
  int reserved;        /* Should be zero */
} squash_header;


/* --- add a drawfile (loaded into memory, pointed to by *data and ---
 * --- of length length(!), with the source filename and           ---
 * --- description supplied.                                       --- */

void lib_add_file(void *data, int length, char *file, char *desc)
{
  char pathname[255];
  FILE *out;
  int num=0, size,sqsize;
  _kernel_swi_regs r;
  int *squasheddata;
  int *wrkspc;
  squash_header squashhead;

  /* fill in the squash file header for the file to be added */
  r.r[0]=17;                    /* read catalogue info for an object */
  r.r[1]=(int)file;             /* pointer to filename */
  _kernel_swi(OS_File, &r, &r);
  squashhead.id=SQUASH;         /* predefined constant "SQSH" */
  squashhead.reserved=0;        /* must be zero */
  squashhead.length=r.r[4];
  squashhead.load=r.r[2];
  squashhead.exec=r.r[3];

  r.r[0] = 8; /* find size of workspace and output space required*/
  r.r[1] = length;
  _kernel_swi(Squash_Compress, &r, &r);

  sqsize=r.r[1];

  if(flex_alloc((flex_ptr)&squasheddata, sqsize) == FALSE) {
    werr(0, msgs_lookup("nmfds:No room, failed to allocate %iK for squashed data area"),sqsize/1024);
    return;
  }

  wrkspc=(int *)malloc(r.r[0]);
  if(!wrkspc)
  {
    werr(FALSE,"Failed to allocate %iK squash workspace",r.r[0]/1024);
    return;
  }

  _kernel_swi(Hourglass_On,NULL,NULL);

  r.r[0] = 0;
  r.r[1] = (int)wrkspc;
  r.r[2] = (int)data;
  r.r[3] = length;
  r.r[4] = (int)squasheddata;
  r.r[5] = sqsize;
  if(_kernel_swi(Squash_Compress, &r, &r))
  {
    werr(FALSE,msgs_lookup("sa:Squash error"));
    free(wrkspc);
    flex_free((flex_ptr)&squasheddata);
    _kernel_swi(Hourglass_Off,NULL,NULL);
    return;
  }

  free(wrkspc);

  /* find a number to use - ie the largest one currently in use +1 */
  if(cat_entries>0)
  {
    int n=0;

    while(n<cat_entries)
    {
      if(cat[n].number>=num)
        num=cat[n].number+1;
      ++n;
    }
  }

  sprintf(pathname,"<DLibrary$Dir>.Files.%x",num/FIDSQ);
  if(checkdirexists(pathname))
  {
    werr(FALSE,msgs_lookup("dne:Drawing not added."),pathname,file);
    return;
  }
  sprintf(pathname,"<DLibrary$Dir>.Files.%x.%x",num/FIDSQ,(num%FIDSQ)/FID);
  if(checkdirexists(pathname))
  {
    werr(FALSE,msgs_lookup("dne:Drawing not added."),pathname,file);
    return;
  }

  sprintf(pathname,"<DLibrary$Dir>.Files.%x.%x.%x",num/FIDSQ,
          (num%FIDSQ)/FID,num);
  if((out=fopen(pathname,"wb"))==NULL)
  {
    werr(FALSE,msgs_lookup("fowc:Failed to open \"%s\" whilst copying \"%s\" to the library, drawing not added."),pathname,file);
    return;
  }

  /* write out the squash file header */
  fwrite(&squashhead,sizeof(squash_header),1,out);
  /* write out the draw data */
  fwrite(squasheddata,r.r[4]-(int)squasheddata,1,out); 
  fclose(out);
  /* set its type */
  r.r[0]=18;    /* set type reason code */
  r.r[1]=(int)pathname;
  r.r[2]=0xfca; /* the squash file type */
  _kernel_swi(OS_File, &r, &r);

  flex_free((flex_ptr)&squasheddata); /* free space for squashed data */

  /* --- add the drawing to the catalogue --- */

  if(cat==NULL) /* null pointer indicates no memory has been allocated */
  {
    if(flex_alloc((flex_ptr)&cat, sizeof(catentry)) == FALSE)
      werr(TRUE,msgs_lookup("famfe:Failed to allocate memory for entry"));
    size=sizeof(catentry);
  } else {
    /* how big should our flex block be to include this? */
    size=flex_size((flex_ptr)&cat)+sizeof(catentry);
    /* resize it */
    if(flex_extend((flex_ptr)&cat,size)==0) {
      werr(FALSE,msgs_lookup("cneca:Could not extend catalogue area, drawing not added."));
      return;
    }
  }

  /* clear the memory first */
  memset(&cat[cat_entries],'\0',sizeof(catentry));
  strcpy(cat[cat_entries].filename,file);

  strcpy(cat[cat_entries].description,desc);
  cat[cat_entries].number=num;

  /* fill in the time */
  *cat[cat_entries].time=3; /* 3 is needed in the block pointed to by
                             * R1 because we are calling OS_Word 14,3 -
                             * see the PRM for details */
  r.r[0] = 14;
  r.r[1] = (int)cat[cat_entries].time;
  _kernel_swi(OS_Word, &r, &r);

  ++cat_entries; /* we've now got one more drawing available */
  cat_save(); /* make sure catalogue on disc is up to date */

  if(cat_entries==1)
  {
    current_pic=1;
    update_pic_display();
  }

  update_controls();

  /* catalogue has changed, we need to generate new menus next time they
   * are needed */
  ccsl_filemenu=1;
  ccsl_descmenu=1;
}


/* --- called when main window should be opened --- */

int main_open (int event_code, ToolboxEvent *event_block,
               IdBlock *id_blk, void *h)
{
  /* if the window is already open bring it to the front */
  if(mainw) {
    window_front(mainw); /* tpluslib call */
    window_front(pane);  /* the pane window should still be on top */
    return 0;
  }

  /* create our main and pane windows, giving us ObjectIds for each */
  are(toolbox_create_object(0,"main",&mainw));
  are(toolbox_create_object(0,"pane",&pane));
  are(toolbox_create_object(0,"Toolbar",&ctrlwin));
  event_register_toolbox_handler(ctrlwin, Adjuster_Clicked, change_drawing, NULL);
  /* attach our toolbar */
  window_set_tool_bars(15,mainw,0,0,ctrlwin,0);
  /* open the main window */
  are(window_open_centre(0,mainw,0,0));
  toolbox_show_object(0,ctrlwin,0,0,mainw,0);
  /* finally register the pane with our library, it will be opened for
   * us */
  are(pane_register(mainw,0x1,pane));
  /* load the picture and display it, and update the controls */
  update_pic_display();
  update_controls();
  return 0;
}

/* --- the toolbox wants us to report an error --- */

int error_handler (int event_code, ToolboxEvent *event_block,
                   IdBlock *id_blk, void *h)
{
  raise_error ((_kernel_oserror *)event_block->data.bytes);
  return 1;
}

/* --- a file has been dragged somewhere --- */

int file_loader(WimpMessage *message, void *handle)
{
  WimpMessage msg;
  int winh,paneh;

  window_get_wimp_handle(0, mainw, &winh);
  window_get_wimp_handle(0, pane, &paneh);

  if (message->data.data_load.destination_window==winh ||
      message->data.data_load.destination_window==paneh)
  {
     /* check drag is draw file */
     if(message->data.data_load.file_type == 0xaff)
       add_open(message->data.data_load.leaf_name);
     else {
       werr(FALSE,msgs_lookup("icoudf:I can only use draw files"));
       return 0;
     }
  } else {
    return 0;
  }

  /* ack message */
  msg = *message;
  msg.hdr.your_ref = msg.hdr.my_ref;

  if (message->hdr.action_code == Wimp_MDataLoad)
      msg.hdr.action_code = Wimp_MDataLoadAck;
  wimp_send_message(Wimp_EUserMessage, &msg, msg.hdr.sender,0,0);

  return 1;
}

/* --- remove the catalogue entry and draw file for the currently ---
 * --- loaded file                                                --- */

int delete_drawing(int event_code, ToolboxEvent  *event, IdBlock *id_block,
                   void  *handle)
{
  char file[80]="";

  if(current_pic==-1)
  {
    werr(FALSE,msgs_lookup("lie:You can't delete this drawing,"
                           " the library is empty"));
    return 1;
  }

  sprintf(file,"<DLibrary$Dir>.Files.%x.%x.%x",
               cat[current_pic-1].number/FIDSQ,
               (cat[current_pic-1].number%FIDSQ)/FID,
               cat[current_pic-1].number);

  remove(file);
  /* shrink the flex block to get rid of deleted entry */
  flex_midextend((flex_ptr)&cat,(current_pic)*sizeof(catentry),
                 -sizeof(catentry));

  cat_save(); /* make sure catalogue on disc is up to date */

  --cat_entries;

  /* if the library is now empty we should go back to our default
   * drawing */
  if(current_pic==1 && cat_entries==0)
    current_pic=-1;
  /* if we were displaying the last picture in the library, and it
   * has just been deleted, go to the one before it */
  if(current_pic>cat_entries)
    current_pic=cat_entries;

  update_pic_display();
  update_controls();

  /* catalogue has changed, we need to generate new menus next time they
   * are needed */
  ccsl_filemenu=1;
  ccsl_descmenu=1;

  return 1;
}

/* just fill in the version number when our program info box is displayed */

int proginfo_show(int event_code, ToolboxEvent  *event, IdBlock *id_block,
                   void  *handle)
{
  proginfo_set_version(0,id_block->self_id,"V" VERSION " ("__DATE__")");
  return 1;
}

/* fill in the fileinfo dialogue */

int fileinfo_show(int event_code, ToolboxEvent  *event, IdBlock *id_block,
                   void  *handle)
{
  fileinfo_set_modified(0,id_block->self_id,0);
  fileinfo_set_file_name(0,id_block->self_id,cat[current_pic-1].filename);
  fileinfo_set_file_size(0,id_block->self_id,draw_length);
  fileinfo_set_date(0,id_block->self_id,cat[current_pic-1].time);
  return 1;
}

/* return the filename of a risc-os pathname */
static char *getleafname(char *pnpath)
{
  char *pnlast;

  while (*pnpath != '\0') {
    ++pnpath;
    if (*pnpath == '.')
      pnlast = pnpath;
  }

  return pnlast+1;
}

/* set up the saveas dialogue and point it at the correct area of RAM */

int saveas_show(int event_code, ToolboxEvent  *event, IdBlock *id_block,
                   void  *handle)
{
  saveas_set_file_name(0,id_block->self_id,
                       getleafname(cat[current_pic-1].filename));
  saveas_set_file_type(0,id_block->self_id,0xaff);
  saveas_set_file_size(0,id_block->self_id,draw_length);
  saveas_set_data_address(0,id_block->self_id,drawdata,draw_length,
                          drawdata,draw_length);
  return 1;
}

/* fade or unfade the display menu items depending on if there are
 * actually any pictures in the library */

int menu_show(int event_code, ToolboxEvent  *event, IdBlock *id_block,
                   void  *handle)
{
  menu_set_fade(0,id_block->self_id,CDABOUT,(current_pic==-1));
  menu_set_fade(0,id_block->self_id,CDSAVE,(current_pic==-1));
  menu_set_fade(0,id_block->self_id,CDDELETE,(current_pic==-1));
  return 1;
}

/* this routine handles everything to do with the pop up menu directory
 * on the main dialler window
 */

int quickmenu_handler (int event_code, ToolboxEvent *event_block,
                       IdBlock *id_blk, void *h)
{    
  switch(event_code)
  {
    case QUICKMENU_OPENING: /* the quick menu is about to open */
    {
      ComponentId n=0;
      char desc[80];

      /* check to see if we actually need to create a new menu */
      if((!ccsl_filemenu) && id_blk->parent_component==CPOPUPFILE)
        return 1;
      if((!ccsl_descmenu) && id_blk->parent_component==CPOPUPDESC)
        return 1;
    
      /* if we do, set ccsl_xmenu to indicate that the menu is up to date */
      if(id_blk->parent_component==CPOPUPFILE)
        ccsl_filemenu=0;  
      if(id_blk->parent_component==CPOPUPDESC)
        ccsl_descmenu=0;

      _kernel_swi(Hourglass_On,NULL,NULL);

      /* clear any existing entries */
      while(!menu_remove_entry(0,id_blk->self_id,n))
        ++n;
    
      n=0;
    
      /* loop through all catalogue entries */
      while (n!=cat_entries)
      {
        ComponentId new;
        /* MenuTemplateEntry is a structure used to define menu entries */
        MenuTemplateEntry menuentry={0};
    
        /* this means we can use the component numbers directly as record
         * numbers for cat[] */
        menuentry.component_id=n;
        /* set the text to the description or the filename, this depends on
         * parent_component (which will contain the ComponentId of the
         * pop up gadget which was clicked */
        if(id_blk->parent_component==CPOPUPFILE) {
          menuentry.text=cat[n].filename;
          menuentry.max_text=strlen(cat[n].filename)+1;
        }
        if(id_blk->parent_component==CPOPUPDESC) {
          if(strcmp(cat[n].description,"")) {
            menuentry.text=cat[n].description;
            menuentry.max_text=strlen(cat[n].description)+1;
          } else {
            sprintf(desc,msgs_lookup("dwg:Drawing %i"),n+1);
            menuentry.text=desc;
            menuentry.max_text=strlen(desc)+1;
          }
        }
        /* all entries have same click event, we distinguish between entries
         * by their component id */
        menuentry.click_event=QUICKMENU_CLICK;
        /* add it */
        menu_add_entry(0,id_blk->self_id,-2,(char*)&menuentry,&new);
        n++;
      }

      _kernel_swi(Hourglass_Off,NULL,NULL);

      break;
    }
    case QUICKMENU_CLICK: /* an entry in the menu has been clicked */
    {
      current_pic=id_blk->self_component+1;
      update_pic_display();
      update_controls();
      break;
    }
    default:
      break;
  }

  return 1;
}

/* --- Initialise toolbox resources --- */
static void initialise (void)
{
  _kernel_oserror *e;

  if ((e = toolbox_initialise (0, 310, message_list, event_list,
       "<DLibrary$Dir>", &mfd, &id_blk, NULL, NULL, NULL)) != NULL) {
     raise_error (e);
     exit (1); /* Fatal */
  }

  event_initialise (&id_blk); /* this leaves us our id */

  /* initialise the flex memory manager */
  flex_init();

  /* initialise pane library (actually just initialises alarm) */
  pane_init();

  /* register our handlers for custom toolbox events */
  event_register_toolbox_handler(-1, MAIN_HIDDEN, main_hidden, NULL);
  event_register_toolbox_handler(-1, MAIN_OPEN, main_open, NULL);
  event_register_toolbox_handler(-1, MENU_QUIT, menu_quit, NULL);
  event_register_toolbox_handler(-1, MENU_DELETE, delete_drawing, NULL);

  /* pick up Wimp_MDataLoad events so we can detect file drags to
   * the main window */
  event_register_message_handler(Wimp_MDataLoad,file_loader,0);

  /* these two are self explanatory */
  event_register_toolbox_handler(-1, Toolbox_Error, error_handler, NULL); 
  event_register_message_handler(Wimp_MQuit, wimp_quit, NULL);

  /* register a handler so we can fill in the version in the proginfo box
   * before it is shown */
  event_register_toolbox_handler(-1, ProgInfo_AboutToBeShown,proginfo_show,0);

  /* register a handler so we can fill the fileinfo box before it is
   * shown */
  event_register_toolbox_handler(-1, FileInfo_AboutToBeShown,fileinfo_show,0);

  /* register a handler to update the pointers to the data for the saveas
   * whenever the saveas window is displayed */
  event_register_toolbox_handler(-1, SaveAs_AboutToBeShown,saveas_show,0);

  /* register a handler so we can grey out the options of the display
   * menu when they are not needed */
  event_register_toolbox_handler(-1, DISPLAY_MENU_OPENING,menu_show,0);

  /* these register the handlers for the pop up menu in the main
   * window */
  event_register_toolbox_handler(-1, QUICKMENU_OPENING, \
                                 quickmenu_handler, NULL);
  event_register_toolbox_handler(-1, QUICKMENU_CLICK, \
                                 quickmenu_handler, NULL);
}


/* --- main routine --- */

int main (void)
{
   int event_code;

   initialise();
   if(find_duplicatetask()>=1)
     werr(TRUE,"%s is already running",msgs_lookup("_TaskName"));

   cat_load();

   /* pane library uses alarm, so we need to use alarm's poll code */
   while (1)
     alarm_event_poll(&event_code, &poll_block, NULL);
}
