/************************************************************
**
** Application: Snapper
**
** Title:       c.config
**
*************************************************************/

/*
*
* Copyright (c) 2017, David Pilling and Chris Johnson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*   * Neither the name of the copyright holder nor the names of their
*     contributors may be used to endorse or promote products derived
*     from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/

/* Include files */

#include "h.includes"


/* from TaskLib */

#include "TaskLib:h.wimpt"
#include "TaskLib:h.flex"
#include "TaskLib:h.sprite"
#include "TaskLib:h.transform"
#include "TaskLib:h.bbc"
#include "TaskLib:h.xhelp"
#include "TaskLib:h.alloc"
#include "TaskLib:h.task"
#include "TaskLib:h.poll"
#include "TaskLib:h.temp"
#include "TaskLib:h.scrap"
#include "TaskLib:h.fsx"
#include "TaskLib:h.etc"
#include "TaskLib:h.xx2con"
#include "TaskLib:h.swis"
#include "TaskLib:h.save"
#include "TaskLib:h.bf"
#include "TaskLib:h.colour"
#include "TaskLib:h.key"
#include "TaskLib:h.pane"
#include "TaskLib:h.font"
#include "TaskLib:h.mlo"
#include "TaskLib:h.timex"


/* from application */

#include "h.constants"
#include "h.reslink"
#include "h.str"
#include "h.area"
#include "h.main"
#include "h.convert"
#include "h.config"



#define FILEPATH_SIZE 256

#define SWINDOW   0
#define SCONTENTS 1
#define SAREA     2
#define SSCREEN   3

/* height of control window in small state */
#define SMALL_HEIGHT 132

static int smode;

static int sptrmove;
static int sptrshow;

#define SNOFILL 16

static int sfill;
static int sdelaysnap;
static int sdelaytime;

/* bits for key down */
#define LALT  0x1
#define RALT  0x2
#define LCTRL 0x4
#define RCTRL 0x8
#define LSHFT 0x10
#define RSHFT 0x20
#define LLOGO 0x40
#define RLOGO 0x80

static int hotkey;

static int chandle;       /* control window handle */
static int shandle;       /* saveas window handle */
static int choicehandle;  /* extra settings window handle */
static int choice_firstopen = 1;
static int choice_is_open = 0;
static win_position choice_pos;


/* Save methods */
/* indexes for the save methods */
#define SAVEMETHOD_SAVEAS    0
#define SAVEMETHOD_SAVEPATH  1
#define SAVEMETHOD_CLIPBOARD 2
#define SAVEMETHOD_FILERRUN  3

static int  save_method;
static char savepath[256];


/* Output formats */
#define SPAREA_OFFSET 48
#define DRAWHDR 64
#define DRAWW 16

#define SAVEOUTPUTOPTS 4        /* no of filetype buttons in saveas window */

static saveoptionstr saveopts[4];

static int saveopt = 0;

static int fname_set;

static int output_format = 1;
static int clipboard_type = 0;
static int output_type = 0;
static int output_length;
static char * output_buffer;

/* Clipboard stuff */
#define CLIPBOARD_CLAIM 1<<2

static int we_own_clipboard = 0;


static int jpeg_quality = 75;

static int wx0;
static int wx1;
static int wy0;
static int wy1;

static int first_open = 1;

static int config_icon;       /* store the icon destination of a file drag */

/* Icon numbers in snapper control window */
#define S_SCREEN   13
#define S_AREA     4
#define S_WINDOW   5
#define S_CONTENTS 11
#define S_SNAP     3
#define S_MOVE     14
#define S_SHOW     2
#define S_FILL     27
#define S_FILLMENU 26
#define S_SAVEPATH 18
#define S_SAVEMENU 19
#define S_LALT     22
#define S_RALT     23
#define S_LCTRL    20
#define S_RCTRL    21
#define S_LSHFT    10
#define S_RSHFT    12
#define S_LLOGO    35
#define S_RLOGO    36
#define S_WINTOP   28
#define S_DESTINATION 29
#define S_DESTMENU 30
#define S_OUTPUT   31
#define S_OUTPUTMENU 32
#define S_MORECHOICE 33
#define S_HIDECONTROL 37
#define S_DELAYSNAP 38
#define S_DELAYTIME 39
#define S_DELAYDOWN 40
#define S_DELAYUP   41

/* icon numbers in 'more choices' */
#define C_AUTOOPEN 0
#define C_DESKTOPFONT 1
#define C_JPEG_QUALITY 3
#define C_JPEG_QUALITY_DOWN 4
#define C_JPEG_QUALITY_UP 5
#define C_ADD_EXT         6
#define C_OLD_FORMAT      8
#define C_CONV_64_32     10
#define C_OPEN_CONTROL   11
#define C_RESET_SUFFIX   12

static char fn_buf[256];
static char last_fn[256];
static char leaf_buf[24];
static int count[4];
static int wintotop;
int add_ftype_ext = 0;
static int old_format_spr = 0;
static int convert_4_32 = 1;
static int convert_64_32 = 1;

static int hide_control_win = 0;       /* Hide control window during full screen grab */
static int cwindow_behind;             /* position of cwindow in stack */

int set_open_control_at_start;
int set_reset_suffix_at_start = 1;

static char * save_buf;
static sprite_area * sparea;

/* BPP mapping for different sprite types - define only once here */
// now in tasklib
//int bppmap[17]={0,1,2,4,8,16,32,32,24,24,16,0,0,0,0,0,16};



spr_info sp_info;

/* size of memory claimed for snap sprite - now made global */
static int size;

static char * save_destination[] = { "Use save as",
                                     "Use save path",
                                     "Use clipboard",
                                     "Filer_run"
                                   };


static char * save_format[] = { "Sprite",
                                "Draw",
                                "PNG",
                                "JPEG"
                              };

/* For reading/writing config etc in Choices */
static char * cwritepath = "<Choices$Write>.CJohnson.Snapper.";
static char * creadpath = "Choices:CJohnson.Snapper.";
static char * apppath = "<Snapper$Dir>.Resources.";
static char *write_chsysvar = "Choices$Write";
static char *read_chsysvar = "Choices$Path";

#define CHSAVE TRUE
#define CHLOAD FALSE

/*

   NB set graphics window


   PRM V1 p 774


   OS_SpriteOp 16
                  r0=16
                  r1=spa
                  r2=name
                  r3=1 palette
                  r4=xlo
                  r5=ylo
                  r6=xhi
                  r7=yhi

    out
                  r2=address of sprite


   PRM V1 p 764

   OS_SpriteOp 2
                  r0=2
                  r2=path
                  r3=1 palette


 */



/************************************************************/

#define SPELLKEYS 0x44F01

static int getkeys(void)
{
 os_error * err;
 os_regset rx;

 rx.r[0]=0;

 err=os_swix(SPELLKEYS,&rx);

 if(err) return(0);
 else    return(rx.r[0]);
}





/************************************************************/

static os_error * convert_to_ro35 ( void )

{
  newsprite_header * sph;
  char * pCimdata;
  int * pIimdata;
  char * next;
  int * pInext;
  char val;
  int iVal;
  int xdpi, ydpi, type;

  sparea = (sprite_area *)(save_buf + SPAREA_OFFSET);
  sph=(newsprite_header *)(sparea->sproff+(char*)sparea);

  /* Take a copy of type5 values to insert into type35 later */
  xdpi = 180 >> sph->selector.type5.xeigen;
  ydpi = 180 >> sph->selector.type5.yeigen;
  type = sph->selector.type5.type;

  switch (sp_info.type)
  {
    case 5:
      /* 16bpp 32k colour mode - R, G, B each 5 bits */
      /* assuming 16bits are 0bbbbbgggggrrrrr so the
         32-bit word is (in 3.5 non-swapped order)
         0bbbbbgggggrrrrr0bbbbbgggggrrrrr
      */
      if (sp_info.colour_order == 1)
      {
        /* swap the r/b colours */
        pInext = (int *)((char*)sparea + sph->next);
        pIimdata = (int*)(sph->image+(char*)sph);
        while (pIimdata < pInext)
        {
          iVal = (*pIimdata) & 0x3E003E0;
          iVal |= ((*pIimdata) & 0x1F001F) << 10;
          iVal |= ((*pIimdata) & 0x7C007C00) >> 10;
          *pIimdata = iVal;
          pIimdata++;
        }
      }
      break;

    case 6:
      /* 32 bpp - R, G, B each 8 bits */
      /* The four bytes of data are in the order r/b:g:b/r:0 in memory */
      if (sp_info.colour_order == 1)
      {
        /* swap the r/b colours */
        next = (char*)sparea + sph->next;
        pCimdata = (char*)(sph->image+(char*)sph);
        while (pCimdata < next)
        {
          val = *(pCimdata);
          *(pCimdata) = *(pCimdata + 2);
          *(pCimdata + 2) = val;
          pCimdata += 4;
        }

      }
      break;

    case 10:
      /* 16bpp 64k colour mode - R 5 bits, G 6 bits, B 5 bits */
      /* Only ROS 5.2+ and Select 6 */
      /* assuming 16bits are bbbbbggggggrrrrr so the
         32-bit word is (in 3.5 non-swapped order)
         bbbbbggggggrrrrrbbbbbggggggrrrrr
      */
      if (sp_info.colour_order == 1)
      {
        pInext = (int *)((char*)sparea + sph->next);
        pIimdata = (int*)(sph->image+(char*)sph);
        while (pIimdata < pInext)
        {
          iVal = (*pIimdata) & 0x7E007E0;
          iVal |= ((*pIimdata) & 0x1F001F) << 11;
          iVal |= ((*pIimdata) & 0xF800F800) >> 11;
          *pIimdata = iVal;
          pIimdata++;
        }
      }
      /* Now convert to 32k sprite if required */
      if (convert_64_32 == 1)
      {
        pIimdata = (int*)(sph->image+(char*)sph);
        while (pIimdata < pInext)
        {
          iVal = ((*pIimdata) & 0x1F001F) ;
          iVal |= ((*pIimdata) & 0xFFC0FFC0) >> 1;
          *pIimdata = iVal;
          pIimdata++;
        }
        /* We have created a type 5 sprite */
        type = 5;
      }
      break;

    case 16:
      /* New colour mode - 4096 colours */
      /* assuming 16bits are 0000bbbbggggrrrr */
      /* 32-bit word is 0000bbbbggggrrrr0000bbbbggggrrrr */
      /* Swap r/b and set mode word for colour order accordingly */
      pInext = (int *)((char*)sparea + sph->next);
      if (sp_info.colour_order == 1)
      {
        pIimdata = (int*)(sph->image+(char*)sph);
        while (pIimdata < pInext)
        {
          iVal = (*pIimdata) & 0xF000F0;
          iVal |= ((*pIimdata) & 0xF000F) << 8;
          iVal |= ((*pIimdata) & 0xF000F00) >> 8;
          *pIimdata = iVal;
          pIimdata++;
        }
      }
      /* Now convert to 32k type 5 sprite */
      pIimdata = (int*)(sph->image+(char*)sph);
      while (pIimdata < pInext)
      {
        iVal = ((*pIimdata) & 0xF000F) << 1;
        iVal |= ((*pIimdata) & 0xF000F0) << 2;
        iVal |= ((*pIimdata) & 0xF000F00) << 3;
        *pIimdata = iVal;
        pIimdata++;
      }
      /* We have created a type 5 sprite */
      type = 5;
      break;

    default:
      return (NULL);
  }


  /* now rewrite the mode word */
  sph->selector.type.xdpi = xdpi;
  sph->selector.type.ydpi = ydpi;
  sph->selector.type.type = type;

  return (NULL);
}







/************************************************************/

static os_error * writepointer(int x,int y)
{
 os_error         * err = NULL;
 os_regset          rx;
 int                ptr;
 char             * p;
 int                acx;
 int                acy;
 int                width;
 int                height;
 newsprite_header * sph;
 int                xpix;
 int                ypix;
 int                spbpp;
 int                bitwidth;
 int                spbitmask;
 int              * idata;
 int                shift;
 int                pshift;
 int                pbyte;
 int                i;
 int                j;
 int                bits;
 int                xs;
 int                bitword[4];
 unsigned int       w;


 if(x<0 || y<0) return (NULL);
 x/=cvdu.deltax;         /* current mode */
 y/=cvdu.deltay;

 rx.r[0]=1;
 err=os_swix(SPELLKEYS,&rx);

 if(!err)
 {
  /* r0=pointer number 1-4 */
  /* r1=pointer to buffer  */

  ptr=rx.r[0]-1;
  if(ptr>=0 && ptr<4)
  {
   p=(char*)rx.r[1];
   p+=(32*8+4)*ptr;

   width=*p++;
   height=*p++;
   acx=*p++;
   acy=*p++;

   sparea = (sprite_area *)(save_buf + SPAREA_OFFSET);
   sph=(newsprite_header *)(sparea->sproff+(char*)sparea);
   /*  if(sph->selector.type.type) */ /* it is a new sprite */
   /* Is it one we can deal with? */

   /*
      In order to refuse gracefully to deal with new sprite types, the
      check below has been added. It can be updated if we understand any
      newer types.
   */
   if(sp_info.type > 16)
   {
     err = generror (256,"Snapper cannot deal with this type of sprite");
     return (err);
   }
   getbpp(sph->selector.mode.mode, NULL, &spbitmask, &spbpp);
//////   if (sp_info.format != SPRITE_FORMAT_ROS_50) spbpp=(1<<spbpp);

   bitwidth=32*(sph->width+1)-sph->lbit-(31-sph->rbit);

   xpix=bitwidth/spbpp;
   ypix=sph->height+1;

   x-=acx;
   y-=acy;

   /* have to read mouse colours by some means */

   for(i = 1; i < 4; i++)
   {
    rx.r[0]=i;                       /* logical colour */
    rx.r[1]=25;                      /* read pointer colour */
    os_swix(OS_ReadPalette,&rx);
    w=rx.r[2];                       /* setting of first (flashing) colour */
                        /* second flashing colour is in R3 (not used here) */

    /*
      Changed by cj to include sprite type 10 (64k colour)
      w contains returned data (R2) as follows
      Bits 0-6:   16 for colours 0-7, 17,18 for flashing colours 8 -15
      Bit 7:      supremacy bit;
      Bits 8-15:  amount of red
      Bits 16-23: amount of green
      Bits 24-31: amount of blue
    */
    switch (sp_info.type)
    {
      case 5:
        /* 16bpp 32k colour mode - R, G, B each 5 bits */
        /* assuming 16bits are 0bbbbbgggggrrrrr */
        if (sp_info.colour_order == 0)
          bitword[i] = ((w & 0xF800) >> 11) | ((w & 0xF80000) >> 14) |
                                              ((w & 0xF8000000) >> 17) ;
        else
          bitword[i] = ((w & 0xF800)>>1) | ((w & 0xF80000) >> 14) |
                                              ((w & 0xF8000000) >> 27) ;
        break;

      case 6:
        /* 32 bpp - R, G, B each 8 bits */
        if (sp_info.colour_order == 0)
        {
        /* just shift into lower 24 bits -> 00000000bbbbbbbbggggggggrrrrrrrr */
        bitword[i] = (w >> 8) ;
        }
        else
        {
          bitword[i] = ((w & 0xFF00) << 8) | ((w & 0xFF0000) >> 8) |
                                              ((w & 0xFF000000) >> 24) ;
        }
        break;

      case 10:
        /* 16bpp 64k colour mode - R 5 bits, G 6 bits, B 5 bits */
        /* assuming 16bits are bbbbbggggggrrrrr */
        if (sp_info.colour_order == 0)
          bitword[i] = ((w & 0xF800) >> 11) | ((w & 0xFC0000) >> 13) |
                                              ((w & 0xF8000000) >> 16) ;
        else
          bitword[i] = ((w & 0xF800)) | ((w & 0xFC0000) >> 13) |
                                              ((w & 0xF8000000) >> 27) ;
        break;

      case 16:
        /* New colour mode - 4096 colours */
        /* assuming 16bits are 0000bbbbggggrrrr */
        if (sp_info.colour_order == 0)
          bitword[i] = ((w & 0xF000) >> 12) | ((w & 0xF00000) >> 16) |
                                              ((w & 0xF0000000) >> 20) ;
        else
          bitword[i] = ((w & 0xF000) >> 4) | ((w & 0xF00000) >> 16) |
                                              ((w & 0xF0000000) >> 28) ;

      default:
        bitword[i]=getcolnumber(w);
    }
   }

   /* we set idata to the word nearest the pixel we want */

   pshift=0;
   pbyte=*p++;

   for(j=0;j<height;j++)
   {
    y++;

    xs=FLOOR(x,0);

    idata=(int*)(sph->image+(char*)sph);
    idata+=(xs*spbpp+sph->lbit)/32;
    idata+=y*(sph->width+1);
    shift=(xs*spbpp+sph->lbit)%32;

    for(i=0;i<(width*4);i++)
    {
     if(pshift>=8)
     {
      pbyte=*p++;
      pshift=0;
     }
     bits=(pbyte>>pshift)&0x3;
     pshift+=2;

     if((x+i)<xpix && (x+i)>=0 && y<ypix)
     {                    /* not transparent, map to bits for this mode */
      if(bits)
      {
       *idata&=~(spbitmask<<shift);
       *idata|=bitword[bits]<<shift;
      }

      shift+=spbpp;
      if(shift>=32)
      {
       idata++;
       shift=0;
      }
     }
    }
   }
  }
 }
 return (err);
}



/************************************************************/

static os_error * updatemodule(void)
{
  char string[256];

////////
#if 0
  sprintf(string, "Snapper_Key %d",
             ((smode == SSCREEN) && (save_method == SAVEMETHOD_SAVEPATH))?hotkey:0);
#endif
////////
  sprintf(string, "Snapper_Key %d",
             ((smode == SSCREEN) && (!chandle))?hotkey:0);

  os_cli(string);

  return(NULL);
}




/************************************************************/

static void setwindowcoords(void)
{
 windowstr window;

 if(chandle)
 {
  getw(chandle,&window);

  wx0=window.x0;
  wx1=window.x1;
  wy0=window.y0;
  wy1=window.y1;
 }
}



/************************************************************/

static void savezero(int handle)
{
 windowstr window;

 getw(handle,&window);
 if(!(window.wflags & 0x10000))
 {
   sfree((flex_ptr)&save_buf);
   remzeroevent(savezero, handle);
 }
}



/************************************************************/

static os_error * get_sprite_size (sprite_area *area, sprite_info *resultinfo )
{
  os_error * err;
  os_regset r;
  int ad;

  ad = (int) area;
  r.r[0] = 40 + 512;
  r.r[1] = ad;                           /* address of sprite area */
  r.r[2] = ad + 16;                      /* address of first (only) sprite */
  err = os_swix(OS_SpriteOp, &r);
  resultinfo->width = r.r[3] ;
  resultinfo->height = r.r[4] ;
  resultinfo->mask = r.r[5] ;
  resultinfo->mode = r.r[6] ;
  return(err);
}





/************************************************************/

/* The sprite area is offset 48 bytes (SPAREA_OFFSET) into the buffer.
 * The draw header (40 bytes) and sprite object header (24 bytes)
 * are constructed at the start of the buffer. This means the
 * first 16 bytes of the sprite area are overwritten when the draw file
 * is constructed. These four words correspond to the sprite area
 * control block, which are not required in the draw file. The sprite
 * itself then follows immediately on from the draw file + sprite object
 * headers.
*/

static os_error * convert_to_draw(void)
{

  int          firstoff;
  int          firstfree;
  int          spritelen;
  int        * buff;

  int         h;
  int         w;
  int         xhi;
  int         xlo;
  int         yhi;
  int         ylo;
  int         mode;
  int         dx;
  int         dy;
  sprite_info sinfo;


  buff = ((int*)(sparea));
  firstoff = *(buff + 2);
  firstfree = *(buff + 3);

  /* now get the sprite details and convert to draw units etc */
  get_sprite_size(((sprite_area *)(save_buf + SPAREA_OFFSET)), &sinfo);

  w = sinfo.width;
  h = sinfo.height;
  mode = sinfo.mode;

  getdeltas(mode, &dx, &dy);

  xhi = w * 256 * dx;           /* draw units */
  yhi = h * 256 * dy;
  xlo = ylo = 0;

  /* can now trash first 16 bytes of sprite area */

  spritelen = *(buff + 4);

  /* now construct the draw file header */
  /* note that 'text' is entered as int words */
  buff = (int*)save_buf;

  *(buff) = 0x77617244;         /* "Draw" */
  *(buff + 1) = 201;
  *(buff + 2) = 0;

  *(buff + 3) = 0x70616E53;     /* "Snap" */
  *(buff + 4) = 0x20726570;     /* "per " */
  *(buff + 5) = 0x20202020;     /* "    " */

  /* bounding box of draw file */
  *(buff + 6) = xlo;
  *(buff + 7) = ylo;
  *(buff + 8) = xhi;
  *(buff + 9) = yhi;

  *(buff + 10) = 5;
  /* draw object should be rounded up to word boundary */
  /* add on the object header size to sprite size rounded up */
  *(buff + 11) = ((spritelen + 3) & ~3) + 24;

  /* bounding box of sprite object */
  *(buff + 12) = xlo;
  *(buff + 13) = ylo;
  *(buff + 14) = xhi;
  *(buff + 15) = yhi;

  output_type = DRAW;
  output_buffer = save_buf;

  /* draw file length is sprite obj length + draw header length */
  output_length = *(buff + 11) + 40;
  return (NULL);
}




/************************************************************/

static os_error * save(char * name, int type)
{
 os_error * err;
 char *p;
 int len;

 err = NULL;

 switch (saveopt)
 {
   case OUTPUT_SPRITE:
     output_type = SPRITE;
     if ((sp_info.format == SPRITE_FORMAT_ROS_50) && old_format_spr) convert_to_ro35 ();
     sparea = (sprite_area *)(save_buf + SPAREA_OFFSET);
     err = bf_saveblock(name, type, ((char*)sparea)+4, sparea->freeoff-4);
     break;

   case OUTPUT_DRAW:
     if ((sp_info.format == SPRITE_FORMAT_ROS_50) && old_format_spr) convert_to_ro35 ();
     err = convert_to_draw();
     if (!err) err = bf_saveblock (name, type, save_buf, output_length);
     break;

   case OUTPUT_PNG:
     output_type = PNG;
     sparea = (sprite_area *)(save_buf + SPAREA_OFFSET);
     len = sparea->freeoff - 4;
     err = convert_spr_to_png (name, save_buf + SPAREA_OFFSET + sizeof(int), size, len);
     break;

    case OUTPUT_JPEG:
     output_type = JPEG;
     sparea = (sprite_area *)(save_buf + SPAREA_OFFSET);
     len = sparea->freeoff - 4;
     err = convert_spr_to_jpeg (name, save_buf + SPAREA_OFFSET + sizeof(int), size,
                                len, jpeg_quality);
     break;

 }
 p = strrchr(name,'.');
 if(p)
 {
   strcpy(last_fn, name);
   count[smode]++;
 }
 sfree((flex_ptr)&save_buf);
 return(err);
}



/************************************************************/

static os_error * save_cb (char * name, int type)
{
  os_error * err;

  err = NULL;
  switch (type)
  {
    case SPRITE:
      if ((sp_info.colour_order == 1) && old_format_spr) convert_to_ro35 ();
      sparea = (sprite_area *)(save_buf + SPAREA_OFFSET);
      err=bf_saveblock (name, type, ((char*)sparea)+4, sparea->freeoff-4);
      break;

    case DRAW:
      if ((sp_info.colour_order == 1) && old_format_spr) convert_to_ro35 ();
      err = convert_to_draw();
      err=bf_saveblock (name, type, save_buf, output_length);
      break;
  }
  return (err);
}




/************************************************************/

static os_error * saveinfo(int * len)
{
  switch (output_format)
  {
    case OUTPUT_SPRITE:
      sparea = (sprite_area *)(save_buf + SPAREA_OFFSET);
      *len=sparea->freeoff-4;
      break;

    case OUTPUT_DRAW:
      *len = output_length;
      break;

    default:
      sparea = (sprite_area *)(save_buf + SPAREA_OFFSET);
      *len=sparea->freeoff-4;
      break;
  }
  return(NULL);
}







/************************************************************/

static void  shiftline(int * wdata,int xpix,int bpp,int rshift)
{
 unsigned int w;
 unsigned int r;
 int          x;
 int        * rdata;
 int          wshift;

 rdata=wdata;
 r=*rdata++;
 w=0;
 wshift=0;

 for(x=0;x<xpix;x++)
 {
  w|=(r>>rshift)<<wshift;
  rshift+=bpp;
  if(rshift>=32)
  {
   rshift=0;
   r=*rdata++;
  }
  wshift+=bpp;
  if(wshift>=32)
  {
   *wdata++=w;
   wshift=0;
   w=0;
  }
 }
 if(wshift) *wdata=w;
}




/************************************************************/

/*
    Changed by cj to include sprite type 10 (64k colour)
    Note - type 9 is a JPEG and as such is not relevant to the current
    version of this application.

    RISC OS 5 new sprite types up to 16 now done
*/

/*

  RISC OS 3.5 sprite types

  Type  Description         NColour value  ModeFlags value  Log2BPP value
  0     Invalid (used to identify
        mode numbers)             -               -              -
  1     1bpp palletised           1               0              0
  2     2bpp palletised           3               0              1
  3     4bpp palletised           15              0              2
  4     8bpp palletised           63              0              3
  5     16bpp 1:5:5:5 TBGR        65535           0              4
  6     32bpp 8:8:8:8 TBGR        -1              0              5
  7     32bpp CMYK                -1              &1000          5
  8     24bpp                     16777215        0              6
  9     JPEG data                 -               -              -
  10    16bpp 5:6:5 TBGR          65535           &80            4
  11-14  Reserved                 -               -              -
  15  Invalid (used to identify
      RISC OS 5 sprite mode words)  -             -              -

  Note that for sprite type 4, the values returned by the kernel are for
  a sprite with a 64 entry palette instead of a full 256 entry palette.

  RISC OS 5 sprite types

  Types 0-15 are as above.

  Type  Description         NColour value       Log2BPP value
  16    16bpp 4:4:4:4         4095                4
  17    4:2:0 YCbCr           420                 7
  18    4:2:2 YCbCr           422                 7
  19-127  Reserved            -                   -

  When reading the mode flags, bits 8-15 of the sprite mode word
  directly specify bits 8-15 of the mode flags. The other flags (e.g.
  bit 7) are as specified in the tables above.

*/


static os_error * tidysprite(sprite_area * sparea)
{
 os_error         * err;
 int                y;
 newsprite_header * sph;
 int                xpix;
 int                ypix;
 int                bitwidth;
 int                bpp;
 int                bitmask;
 int              * idata;
 int              * r;
 int              * w;


 err=NULL;


 sph=(newsprite_header*)((char*)sparea+sparea->sproff);

 if (sp_info.type)
 {
   /*
      In order to refuse gracefully to deal with new sprite types, the
      check below has been added. It can be updated if we understand any
      newer types.
   */
   if(sp_info.type > 16)
   {
     err = generror (256,"Snapper cannot deal with this type of sprite");
////     main_issuewarning (WUNKSPR);
     return (err);
   }

  bpp=bppmap[sp_info.type];
  bitwidth=32*(sph->width+1)-(31-sph->rbit);
  xpix=bitwidth/bpp;
  ypix=sph->height+1;
 }
 else
 {
  getbpp(sph->selector.mode.mode, NULL,&bitmask,&bpp);
//////  bpp=(1<<bpp);
  bitwidth=32*(sph->width+1)-sph->lbit-(31-sph->rbit);
  xpix=bitwidth/bpp;
  ypix=sph->height+1;
 }


 if(sph->lbit)
 {
  idata=(int*)((char*)sph+sph->image);

  for(y=0;y<ypix;y++)
  {
   shiftline(idata,xpix,bpp,sph->lbit);
   idata=(int*)((char*)idata+(sph->width+1)*4);
  }
 }


 sph->rbit-=sph->lbit;
 sph->lbit=0;

 if(sph->rbit<0)
 {

  w=(int*)((char*)sph+sph->image);
  r=(int*)((char*)sph+sph->image);

  for(y=0;y<ypix;y++)
  {
   memcpy(w,r,sph->width*4);
   w=(int*)((char*)w+sph->width*4);
   r=(int*)((char*)r+(sph->width+1)*4);
  }

  sparea->freeoff-=ypix*4;

  sph->width-=1;
  sph->rbit&=0x1F;
  sph->next-=ypix*4;

 }

 return(err);
}





/************************************************************/

static int set_output_type (int type_idx)
{
  int type = SPRITE;

  switch (type_idx)
  {
    case OUTPUT_SPRITE:
      type = SPRITE;
      break;

    case OUTPUT_DRAW:
      type = DRAW;
      break;

    case OUTPUT_PNG:
      type = PNG;
      break;

    case OUTPUT_JPEG:
      type = JPEG;
      break;
  }
  return (type);
}



/************************************************************/

static void make_valid_filename(char *name, int type)
{
  int suffix;
  char newname[256];
  os_regset r;
  os_error * err = NULL;

  suffix = count[smode];

  while (!err)
  {
    sprintf(newname, "%s%d", name, suffix);
    if (add_ftype_ext)
    {
      /* add required ext */
      switch (type)
      {
        case PNG:
          strcat (newname, "/png");
          break;

        case JPEG:
          strcat (newname, "/jpg");
          break;
      }
    }

    r.r[0] = 17;
    r.r[1] = (int)newname;
    err = os_swix(OS_File, &r);
    if (r.r[0] == 0)
    {
      /* file not found - name ok to use */
      break;
    }
    suffix++;
  }

  count[smode] = suffix;

  strcpy(name, newname);

  fname_set = 1;

  return;
}







/************************************************************/

static os_error * savearea(void)
{
 os_error * err;
 windowstr window;
 mousestr  mouse;
 int       x0;
 int       x1;
 int       y0;
 int       y1;
 int       bpp;
 int       size;
 int       i;
 os_regset rx;

 wimp_wstate ws;
 wimp_wstate wsc;
 int     bhandle = -1;
 int     putwinback=0;
 char  * p;

 wimp_msgstr msg;

 char tbuf[256];
 int flags;
 newsprite_header * sph;

 int newmonotonic;

 err=getpointer(&mouse);
 if(err) return(err);

 /* Have we set a delay before snapping? */
 /* This seems to be the place to introduce the delay */

 if (sdelaysnap)
 {
   newmonotonic = monotonic () + (100 * sdelaytime);
   while (monotonic() < newmonotonic)
   {
     poll();
   }
   /* Now check that window (if specified) is still open, if not abort */
   if ((smode==SWINDOW) || (smode==SCONTENTS))
   {
     err = wimp_get_wind_state( mouse.handle, &wsc);
     if (err) return (err);
     if ((wsc.flags & wimp_WOPEN) != wimp_WOPEN) return (err);
   }
 }




 if(smode==SCONTENTS)
 {
  err=getw(mouse.handle,&window);
  x0=window.x0;
  x1=window.x1-cvdu.deltax;
  y0=window.y0;
  y1=window.y1-cvdu.deltay;
  strcpy(leaf_buf,"content");             /*cj*/
 }
 else
 if(smode==SWINDOW)
 {
  err=geto(mouse.handle,&window);
  x0=window.x0;
  x1=window.x1-cvdu.deltax;
  y0=window.y0;
  y1=window.y1-cvdu.deltay;
  strcpy(leaf_buf,"window");             /*cj*/
 }
 else
 if(smode==SSCREEN)
 {
  x0=0;
  x1=cvdu.screenx-cvdu.deltax;
  y0=0;
  y1=cvdu.screeny-cvdu.deltay;
  strcpy(leaf_buf,"scrn");             /*cj*/
 }
 else
 if(smode==SAREA)
 {
  x0=areax0+cvdu.deltax;
  x1=areax1-2*cvdu.deltax;
  y0=areay0+cvdu.deltay;
  y1=areay1-2*cvdu.deltay;
  strcpy(leaf_buf,"area");             /*cj*/
 }
 else return(err);

 x0=FLOOR(x0,0);
 y0=FLOOR(y0,0);
 x1=ROOF(x1,(cvdu.screenx-cvdu.deltax));
 y1=ROOF(y1,cvdu.screeny);


 if(sfill!=SNOFILL && smode==SAREA)
 {
  report(openfillwindow(sfill));
  for(i=0;i<5;i++) poll();
 }

  /* cj added - hide control window during full screen grab */
 if ((smode == SSCREEN) && hide_control_win)
 {
   err = wimp_get_wind_state( chandle, &wsc);
   cwindow_behind = wsc.o.behind;
   wsc.o.behind = 0xfffffffd;
   wimp_open_wind ( (wimp_openstr*)&wsc );

   /* Need to do a few polls so wimp can do the redraws etc */
   for(i=0;i<5;i++) poll();
 }

 /* cj added - bring target window to front before grabbing */
 if (((smode==SWINDOW) || (smode==SCONTENTS)) && wintotop)
 {
   err=wimp_get_wind_state(mouse.handle,&ws);
   bhandle=ws.o.behind;
   if (bhandle!=-1)
   {
     putwinback=1;
     ws.o.behind=-1;

     /* send open window request message to window owner */
     err = wimp_sendmessage(wimp_EOPEN, (wimp_msgstr*)&ws.o, mouse.handle);

     /* Need to do a few polls so wimp can do the redraws etc */
     for(i=0;i<5;i++) poll();
   }
 }
 /* cj end */


 if(sptrmove)
 {
  setmouse(0,0);
  for(i=0;i<5;i++) poll();
  setmouse(mouse.x,mouse.y);
 }

 if(sfill!=SNOFILL && smode==SAREA)
 {
  closefillwindow();
 }

  /* cj added - put target window back where it was */
  /* These following wimp actions will not actually occur until we
  ** start doing wimp polls again (after screen save has occurred) */

 if (putwinback)
 {
   ws.o.behind=bhandle;
   /* send open window request message to window owner */
   err = wimp_sendmessage(wimp_EOPEN, (wimp_msgstr*)&ws.o, mouse.handle);
 }
 /* cj end */

  /* cj added - restore control window during full screen grab */
 if ((smode == SSCREEN) && hide_control_win)
 {
   wsc.o.behind = cwindow_behind;
   wimp_open_wind ( (wimp_openstr*)&wsc );
 }
 /* cj end */

 if(!err)  /* allocate a chunk big enough to hold the sprite */
 {
  bpp=(1<<cvdu.ln2bpp);

  size=((x1+cvdu.deltax-1)/cvdu.deltax-x0/cvdu.deltax+1)*bpp;
  size=((size+31+32)&(~0x1F))/8;
  /* +32 to allow for landing on word boundaries lbit,rbit etc. */

  size*=(y1+cvdu.deltay-1)/cvdu.deltay-y0/cvdu.deltay+1;

  size+=2048;        /* at least 256*4*2 bytes for palette */

  size+=sizeof(sprite_header);
  size+=sizeof(sprite_area);
////  size += DRAWHDR;
  size+=256;         /* for luck */


////  if(!sparea) err=flex_alloc((flex_ptr)&sparea,size);
  if(!save_buf) err=salloc((flex_ptr)&save_buf, size + SPAREA_OFFSET);
  else        err=srealloc((flex_ptr)&save_buf, size + SPAREA_OFFSET);
////  else        err=flex_extend((flex_ptr)&sparea,size);
  if(!err)
  {
   sparea = (sprite_area *)(save_buf + SPAREA_OFFSET);
   sprite_area_initialise(sparea, size);


   rx.r[0]=16|256;
   rx.r[1]=(int)sparea;
   rx.r[2]=(int)"snap";
   rx.r[3]=(cvdu.ln2bpp<4)?1:0;
   rx.r[4]=x0;
   rx.r[5]=y0;
   rx.r[6]=x1;
   rx.r[7]=y1;

   err = os_swix(OS_SpriteOp,&rx);


 /* Find the sprite type - may be 3.5 or 5 version type */
 sph=(newsprite_header*)((char*)sparea+sparea->sproff);

 if ( (sph->selector.type5.reserved3 == 15)
      && (sph->selector.type5.reserved2 == 0)
      && (sph->selector.type5.reserved1 == 0)
      && (sph->selector.type5.new == 1))
 {
   /* Riscos 5 type sprite */
   sp_info.format = SPRITE_FORMAT_ROS_50;
   sp_info.type = sph->selector.type5.type;
   flags = sph->selector.type5.flags >> 4;
   if ((flags & 3) != 0) return NULL;
   if ((flags & 4) == 4) sp_info.colour_order = 1;
   else sp_info.colour_order = 0;
 }
 else
 {
   if(sph->selector.type.type)
   {
     /* risc os 3.5 sprite */
     sp_info.format = SPRITE_FORMAT_ROS_35;
     sp_info.type = sph->selector.type.type;
     sp_info.colour_order = 0;
   }
   else
   {
     /* old sprite */
     sp_info.format = SPRITE_FORMAT_MODE_NUM;
     sp_info.type = 0;
     sp_info.colour_order = 0;
   }
 }


   if (!err) tidysprite(sparea);

   if(sptrshow) err = writepointer(mouse.x-x0,y1-mouse.y);


   if(!err)
   {
     /* Now we need to write out the file */
     switch (save_method)
     {
       case SAVEMETHOD_SAVEPATH:
       case SAVEMETHOD_FILERRUN:
         output_type = set_output_type (output_format);
         /* save direct to configured directory without a save dialogue */
         strcpy(fn_buf,savepath);
         /* cope with situation that savepath is null but save to path
            is ticked - will now end up with just the leaf and save will
            be into current dir, usually the $ root */
         if (fn_buf[0]!='\0') strcat(fn_buf,".");
         strcat(fn_buf,leaf_buf);
         make_valid_filename(fn_buf, output_type);

         switch (output_format)
         {
           case OUTPUT_SPRITE:
             if ((sp_info.format == SPRITE_FORMAT_ROS_50) && old_format_spr)
               convert_to_ro35 ();
             err=fs_saveblock(fn_buf, SPRITE, ((char*)sparea) + 4, sparea->freeoff - 4);
             break;

           case OUTPUT_DRAW:
             if ((sp_info.format == SPRITE_FORMAT_ROS_50) && old_format_spr)
               convert_to_ro35 ();
             err = convert_to_draw();
             err=fs_saveblock(fn_buf, DRAW, output_buffer, output_length);
             break;

           case OUTPUT_PNG:
             err = convert_spr_to_png (fn_buf,
                                       save_buf + SPAREA_OFFSET + sizeof(int),
                                       size,
                                       sparea->freeoff - 4);
             break;

           case OUTPUT_JPEG:
             err = convert_spr_to_jpeg (fn_buf,
                                       save_buf + SPAREA_OFFSET + sizeof(int),
                                       size,
                                       sparea->freeoff - 4, jpeg_quality);
             break;

         }


         if(!err)
         {
           count[smode]++;
           sfree((flex_ptr)&save_buf);
           if (save_method == SAVEMETHOD_FILERRUN)
           {
             sprintf (tbuf, "Filer_Run %s", fn_buf);
             os_cli (tbuf);
           }
           return(err);
         }
         break;

       case SAVEMETHOD_CLIPBOARD:

         switch (output_format)
         {
           case OUTPUT_DRAW:
             err = convert_to_draw();
             break;

           case OUTPUT_SPRITE:
           default:
             output_type = SPRITE;
             output_buffer = ((char*)sparea) + 4;
             output_length = sparea->freeoff - 4;
             break;

         }

         /* we need to claim the clipboard and preserve the snap */
         if (!we_own_clipboard)
         {
           /* we need to send a message to claim it */
           msg.hdr.size = 24;
           msg.hdr.your_ref = 0;
           msg.hdr.action = wimp_MCLAIMENTITY;
           msg.data.words[0] = CLIPBOARD_CLAIM;
           wimp_sendmessage (wimp_ESEND, &msg, 0);

           we_own_clipboard = 1;
         }
         break;

       case SAVEMETHOD_SAVEAS:
       default:
         /* raise a saveas dialogue */
         p=fs_leaf(last_fn);
         if(p)
         {
          *p=0;
         }
         strcpy(fn_buf,last_fn);
         strcat(fn_buf,leaf_buf);
         make_valid_filename(fn_buf, output_type);
         setsaveoptions(SAVEOUTPUTOPTS, saveopts, &saveopt);
         err=setsave(fn_buf, output_type, save, &shandle);

         if(!err)
         {
           setsave2(NULL,saveinfo);

           /* set window title */
           wos_windsettitle ( shandle, "Save as" );
           /* Open SaveAs at the pointer when the Snap button is pressed */
           if (!err) err=wimp_create_menu((wimp_menustr *)shandle,mouse.x+16,mouse.y+16);

           addzeroevent(savezero,shandle);
         }
         break;

     }


   }

   if(err) sfree((flex_ptr)&save_buf);
  }
 }
 return(err);
}



/************************************************************/

static os_error * setsavepath(void)
{
 os_error * err;
 char       temp[512];

 sprintf(temp,"set SnapperFile$Dir %s",savepath);

 err=os_cli(temp);

 return(err);
}


/************************************************************/

static os_error * pathsave(char * name,int type)
{
 char * p;

 strcpy(savepath,name);

 p=fs_leaf(savepath);

 if(p)
 {
  if(p>savepath && (*(p-1))=='.') p--;
  *p=0;
 }

 if(chandle) wimp_set_icon_state(chandle,18,(wimp_iconflags)0,(wimp_iconflags)0);

 setsavepath();

 return(NULL);

 USE(type);
}


/*
cj commented


static os_error * opensavepath(void)
{
 os_error * err;

 err=opensave(savepath,SPRITE,pathsave);

 return(err);
}

*/


/* cj added */
/*
  The original opensavepath() function (above) has been replaced by
  the two functions below. The original TASKLIB function always opened
  the Saveas dialogue in the middle of the screen. The modified version
  now opens the saveas dialogue at the pointer so only a small mouse
  movement is required to start the drag.
*/

static os_error *menuwindow_atpointer(int handle)
{
 os_error * err;
 mousestr   mouse;

 err=getpointer(&mouse);
 if (!err) err =wimp_create_menu((wimp_menustr *)handle, mouse.x-48, mouse.y);

 return(err);
}


/************************************************************/

/*
  The following is a bit hacky. To set the path for automatic saves,
  a 'Save as' dialogue is raised and the sprite icon must be dragged into
  the destination directory. In the original program, the save as was a
  standard one, with a SPRITE icon, a writable field and an OK button. The
  last two items are not needed in this context and can cause confusion.
  We have some options:

  - Use a different dialogue with just an icon. Then we have the problem
    of adding (perhaps a lot) of code to handle the save protocol itself,
    or at least tie into the default TASKLIB handlers in some way, which
    use the default 'Save as' template.

  - Shade the unwanted icons. Looks a bit messy

  - Delete the unwanted icons. Less messy in one sense, but a lot of
    space now in the dialogue.

  - Set the window extent of the 'Save as' dialogue so that it no longer
    shows the two redundant icons. This is the option chosen here. It
    works OK because the 'Save as' dialogue is always recreated from the
    template file whenever it is needed. The sprite icon type is also
    set to DIRECTORY to be more consistent with the action.
*/

static os_error * opensavepath(void)
{
 os_error * err;
 int        handle;
 wimp_redrawstr redrw;

 err=setsave(savepath, DIRECTORY, pathsave, &handle);

 if(!err)
 {
   redrw.w = handle;
   redrw.box.x0 = 0;
   redrw.box.y0 = -(54 << mode.yeig);
   redrw.box.x1 = 88 << mode.xeig;
   redrw.box.y1 = 0;
   wimp_set_extent ( &redrw );
   /* set window title */
   wos_windsettitle ( handle, "Set path" );

   /* delete the 'OK' icon */
   wimp_delete_icon ( handle, 2 );
   err=menuwindow_atpointer(handle);
 }

 return(err);
}




/************************************************************/

static void keyzero(int handle)
{
        int keypress;
 static int lastkey;
 static int recurse;

 if(recurse) return;
 recurse=1;

 keypress=getkeys();

 if(keypress==hotkey && hotkey && keypress!=lastkey)
 {
////  if(!((save_method == SAVEMETHOD_SAVEPATH) && (smode==SSCREEN))) report(savearea());
    report(savearea());

 }

 lastkey=keypress;
 recurse=0;

 USE(handle);
}





static int modeicon[]=
{
  S_WINDOW,  /* SWINDOW   0 */
  S_CONTENTS, /* SCONTENTS 1 */
  S_AREA,  /* SAREA       */
  S_SCREEN, /* SSCREEN     */
};


/************************************************************/

static void setmode(int newmode)
{
 if(newmode==SAREA) openarea();
 else
 {
   if(aopen)  closearea();   /* cj change */
 }

 smode=newmode;

 updatemodule();
}


/*****************************************************************************/

#define BSHIFT 28
#define FSHIFT 24



static int uncolour[16];


/************************************************************/

static int brightness(int c1)
{
 return((c1>>8 & 255) + 2 * (c1>>16 & 255) + (c1>>24 & 255));
}



static void encodepal(void)
{
 os_regset  rx;
 os_error * err;
 int        wpal[20];
 int        bright[16];
 int        c;
 int        d;
 int        e;
 int        br;
 int        maxd;
 int        maxe;

 maxd=0; /* for compiler */

 rx.r[1]=(int)wpal;

 err=os_swix(Wimp_ReadPalette,&rx);

 for(c=0;c<16;c++) bright[c]=brightness(wpal[c]);

 for(c=0;c<16;c++)
 {
  br=bright[c];
  maxe=-1;
  for(d=0;d<16;d++)
  {
   e=bright[d]-br;
   if(e<0) e=-e;
   if(e>maxe)
   {
    maxe=e;
    maxd=d;
   }
  }
  uncolour[c]=maxd;
 }
}




/************************************************************/

static os_error * showfill(int handle)
{
 char * p;

 iconaddr(handle, S_FILL, &p);

 if(sfill==SNOFILL)
 {
  strcpy(p,"Trans");
  wimp_set_icon_state(handle, S_FILL, (wimp_iconflags)(WIMPWHITE<<BSHIFT),
                                        (wimp_iconflags)(0xFu<<BSHIFT));
 }
 else
 {
  *p=0;
  wimp_set_icon_state(handle, S_FILL, (wimp_iconflags)(sfill<<BSHIFT),
                                        (wimp_iconflags)(0xFu<<BSHIFT));
 }

 return(NULL);
}


/************************************************************/

static os_error * filldecode(int * menu)
{
 int c;

 c=menu[0];
 if(c>=0 && c<=SNOFILL)
 {
  sfill=c;
  showfill(chandle);
 }

 return(NULL);
}


/************************************************************/

static void setmenucolour(int * menuhandle,int item,int colour)
{
 int c3;
 c3=uncolour[colour];
 *(menuhandle+(7+2+6*item))=((*(menuhandle+(7+2+6*item)))&(~(0xFFu<<FSHIFT)))|
                                          (colour<<BSHIFT)|(c3<<FSHIFT);
}


/************************************************************/

static os_error * openfill(int handle)
{
 int i;

 encodepal();

 for(i=0;i<16;i++) setmenucolour(menus[MFILL].menuptr,i,i);

 setmenufns(MFILL,filldecode,0);

 return(openupmenu(MFILL));

 USE(handle);
}




/************************************************************/

static void shadeoutputicons (void)
{
   if ((save_method == SAVEMETHOD_SAVEAS) || (save_method == SAVEMETHOD_CLIPBOARD))
   {
     shadeicon (chandle, S_SAVEPATH);
     shadeicon (chandle, S_SAVEMENU);
     shadeicon (chandle, S_OUTPUT);
     shadeicon (chandle, S_OUTPUTMENU);
   }
   else
   {
     unshadeicon (chandle, S_SAVEPATH);
     unshadeicon (chandle, S_SAVEMENU);
     unshadeicon (chandle, S_OUTPUT);
     unshadeicon (chandle, S_OUTPUTMENU);
   }
   return;
}




/************************************************************/

static os_error * destmenu_decode (int * menu)
{
  int c;
  os_error      * err;
  wimp_icon       isblock;

  c = menu[0];
  if (c >= 0)
  {
    /* There has been a choice */
    save_method = c;
    err = setindirect (chandle, S_DESTINATION, sizeof (save_destination[c]),
                                               save_destination[c]);
    if (!err) err = wimp_get_icon_info(chandle, S_DESTINATION, &isblock);
    if (!err) err = refreshicon(chandle, &isblock);
    shadeoutputicons();
    updatemodule ();
  }
  return(NULL);
}



/************************************************************/

static os_error * opendestmenu (int handle)
{
  setmenufns (MDEST, destmenu_decode, 0);
  return (openupmenu (MDEST));

 USE(handle);
}




/************************************************************/

static os_error * outputmenu_decode (int * menu)
{
  int c;
  os_error      * err;
  wimp_icon       isblock;

  c = menu[0];
  if (c >= 0)
  {
    /* There has been a choice */
    output_format = c;
    err = setindirect (chandle, S_OUTPUT, sizeof (save_format[c]),
                                               save_format[c]);
    if (!err) err = wimp_get_icon_info(chandle, S_OUTPUT, &isblock);
    if (!err) err = refreshicon(chandle, &isblock);
  }
  return(NULL);
}



/************************************************************/

static os_error * openoutputmenu (int handle)
{
  setmenufns (MCHOICE, outputmenu_decode, 0);
  return (openupmenu (MCHOICE));

  USE(handle);
}


/************************************************************/

static os_error *write_ind_icon_string (int win, int ic, char *string)
{
  os_error   *err;
  wimp_icon   iconinfo;

  err = wimp_get_icon_info (win, ic, &iconinfo);

  if (!err)
  {
    if (iconinfo.flags & wimp_INDIRECT)
    {
      strcpy (iconinfo.data.indirecttext.buffer, string);
      err = wimp_set_icon_state (win, ic, (wimp_iconflags) 0, (wimp_iconflags) 0);
    }
  }

  return (err);
}


/************************************************************/

static os_error *write_ind_icon_int_value (int win, int ic, int val)
{
  char        buffer[16];

  sprintf (buffer, "%d", val);

  return (write_ind_icon_string (win, ic, buffer));
}





/************************************************************/

static os_error * choice_icon(int handle, int userhandle, wimp_mousestr * m)
{
  os_error * err;
  font fh;
  int        inc = 1;

  err=NULL;

  switch(m->i)
  {
    case C_JPEG_QUALITY_DOWN:
      /* simply invert inc and use the same code as for up bump */
      inc = -inc;
    case C_JPEG_QUALITY_UP:
      if (m->bbits == 1)
        inc = -inc;			/* adjust button */

      if (inc > 0)
      {
        if (jpeg_quality < 95) inc *= 5;
      }
      else
      {
        if (jpeg_quality < 96) inc *= 5;
      }
      jpeg_quality += inc;
      jpeg_quality = CUT (jpeg_quality, 20, 100);
      write_ind_icon_int_value (handle, C_JPEG_QUALITY, jpeg_quality);
      break;

    case C_AUTOOPEN:
          selectst(handle, C_AUTOOPEN, autocoord ^= 1);
          if (autocoord)
          {
            if((smode==SAREA) && aopen && (!coordhandle))
            {
              coord_open ();
            }
          }
      break;

    case C_ADD_EXT:
      selectst(handle, C_ADD_EXT, add_ftype_ext ^= 1);
      break;

    case C_OLD_FORMAT:
      selectst(handle, C_OLD_FORMAT, old_format_spr ^= 1);
      break;

    case C_CONV_64_32:
      selectst(handle, C_CONV_64_32, convert_64_32 ^= 1);
      break;

    case C_DESKTOPFONT:
      selectst(handle, C_DESKTOPFONT, UseDesktopFont ^= 1);
      /* now we need to reset the font handling */
      if (UseDesktopFont)
      {
        /* we were not using it before */
        fh = font_getdesktopfonthandle();
        if (fh)
        {
          /* we will use the desktop font */
          /* first lose the default font we were using */
          font_lose(fonthandle);
          /* now set up for use of desktop font */
          err = font_setdesktopfont(fh);
        }
        /* else do nothing */
      }
      else
      {
        /* we want to stop using the desktop font */
        /* no need to lose any fonts */
        err = font_setdefaultfont (&fonthandle);
      }

      if(!err) err = set_top_bar_height ();
      if(!err) force_resize_topbar();

      break;

    case C_OPEN_CONTROL:
      selectst(handle, C_OPEN_CONTROL, set_open_control_at_start ^= 1);
      break;

    case C_RESET_SUFFIX:
      selectst(handle, C_RESET_SUFFIX, set_reset_suffix_at_start ^= 1);
      break;

  }

  return(err);

  USE(userhandle);
}




/************************************************************/

static os_error * choice_close(int w, int userhandle)
{
  os_error * err;

  wos_savewindposition (choicehandle, &choice_pos);

  remcloseevent(choice_close, choicehandle, 0);
  remclickevent(choice_icon, choicehandle, 0);
  remhelpevent (NULL, choicehandle, TCHOICE);

  err = wimp_close_wind (choicehandle);
  choicehandle = NULL;

  return(err);

  USE(userhandle);
  USE(w);
}




/************************************************************/

#define WIN_OFFSET 36

os_error * choice_open (void)
{
 os_error * err;

 if(choicehandle)
 {
   err = forward(choicehandle, 0, NULL);
 }
 else
 {
   err = createwindow(TCHOICE, &choicehandle);
   if (!err)
   {
     addcloseevent(choice_close, choicehandle, 0);
     addclickevent(choice_icon, choicehandle, 0);
     addhelpevent(NULL, choicehandle, TCHOICE);

     selectst(choicehandle, C_AUTOOPEN, autocoord);
     selectst(choicehandle, C_ADD_EXT, add_ftype_ext);
     selectst(choicehandle, C_OLD_FORMAT, old_format_spr);
     selectst(choicehandle, C_CONV_64_32, convert_64_32);

     selectst(choicehandle, C_DESKTOPFONT, UseDesktopFont);
     selectst(choicehandle, C_OPEN_CONTROL, set_open_control_at_start);
     selectst(choicehandle, C_RESET_SUFFIX, set_reset_suffix_at_start);


     /* early versions of RISC OS don't support desktop font */
     if (wimpversion < 350) shadeicon(choicehandle, C_DESKTOPFONT);

     write_ind_icon_int_value (choicehandle, C_JPEG_QUALITY, jpeg_quality);


      /* open window at centre */
      if (choice_pos.reopen)
      {
	open (choicehandle, choice_pos.x0, choice_pos.y0,
	      choice_pos.x1, choice_pos.y1, 0, 0, -1);
      }
      else
      {
	popup (choicehandle, 0);
	choice_pos.reopen = 1;
      }

   }
 }
  return(err);
}


/*****************************************************************************/



static os_error * configicon(int handle,int userhandle,wimp_mousestr * m)
{
 os_error * err;
  int inc = 1;      /* bump icons change delay time in 1 sec increments */

 err=NULL;

 switch(m->i)
 {
  case  S_SNAP:
         /* if(smode==SSCREEN || smode==SAREA)*/ err=savearea();
          break;


  case  S_WINDOW:
          if(smode!=SWINDOW)
          {
           deselect(handle,modeicon[smode]);
           setmode(SWINDOW);
           select(handle,modeicon[smode]);
          }
          break;

  case  S_CONTENTS:
          if(smode!=SCONTENTS)
          {
           deselect(handle,modeicon[smode]);
           setmode(SCONTENTS);
           select(handle,modeicon[smode]);
          }
          break;

  case  S_AREA:
          if(smode!=SAREA)
          {
           deselect(handle,modeicon[smode]);
           setmode(SAREA);
           select(handle,modeicon[smode]);
          }
          break;

  case  S_SCREEN:
          if(smode!=SSCREEN)
          {
           deselect(handle,modeicon[smode]);
           setmode(SSCREEN);
           select(handle,modeicon[smode]);
          }
          break;

  case  S_LSHFT:
          selectst(handle, S_LSHFT, (hotkey^=LSHFT)&LSHFT);
          updatemodule();
          break;

  case  S_RSHFT:
          selectst(handle, S_RSHFT, (hotkey^=RSHFT)&RSHFT);
          updatemodule();
          break;

  case  S_LCTRL:
          selectst(handle, S_LCTRL, (hotkey^=LCTRL)&LCTRL);
          updatemodule();
          break;

  case  S_RCTRL:
          selectst(handle, S_RCTRL, (hotkey^=RCTRL)&RCTRL);
          updatemodule();
          break;

  case  S_LALT:
          selectst(handle, S_LALT, (hotkey^=LALT)&LALT);
          updatemodule();
          break;

  case  S_RALT:
          selectst(handle, S_RALT, (hotkey^=RALT)&RALT);
          updatemodule();
          break;

  case  S_LLOGO:
          selectst(handle, S_LLOGO, (hotkey ^= LLOGO) & LLOGO);
          updatemodule();
          break;

  case  S_RLOGO:
          selectst(handle, S_RLOGO, (hotkey ^= RLOGO) & RLOGO);
          updatemodule();
          break;

  case  S_FILLMENU:
          err=openfill(handle);
          break;

  case  S_SAVEMENU:
          opensavepath();
          break;

  case S_DESTMENU:
    err = opendestmenu (handle);
    break;

  case S_OUTPUTMENU:
    err = openoutputmenu (handle);
    break;

  case  S_MOVE:
          selectst(handle, S_MOVE, sptrmove^=1);
          break;

  case  S_SHOW:
          selectst(handle, S_SHOW, sptrshow^=1);
          break;

  case S_WINTOP:
    selectst(handle, S_WINTOP, wintotop^=1);
    break;

  case S_HIDECONTROL:
    selectst(handle, S_HIDECONTROL, hide_control_win ^= 1);
    break;

  case S_DELAYSNAP:
    selectst(handle, S_DELAYSNAP, sdelaysnap^=1);
    break;

  case S_MORECHOICE:
    err = choice_open();
    break;

    case S_DELAYDOWN:
      /* simply invert inc and use the same code as for up bump */
      inc = -inc;
    case S_DELAYUP:
      if (m->bbits == 1)
        inc = -inc;			/* adjust button */

      sdelaytime += inc;
      sdelaytime = CUT (sdelaytime, 4, 10);
      write_ind_icon_int_value (handle, S_DELAYTIME, sdelaytime);
      break;


 }

 return(err);

 USE(handle);
 USE(userhandle);
}



/*****************************************************************************/

static os_error * configkey(int handle,int userhandle,int icon,int * key)
{
 int ch;

 ch=*key;

 if(icon==18 && ch==RETURN) setsavepath();

 return(NULL);

 USE(handle);
 USE(userhandle);
 USE(icon);
}





/*****************************************************************************/

static os_error *config_loadfiletype (int type, mousestr * m, int userhandle, int *method)
{
  config_icon = m->icon;

  /* Allow any filetype or dir */
  if (type <= DIR)
  {
    if (*method == 0) *method = 1;
  }

  return (NULL);

  USE (m);
  USE (userhandle);

}





/*****************************************************************************/

static os_error *config_loadfiles (char *name, int type, int userhandle, int xvolatile)
{
  char * s;


  if (config_icon == S_SAVEPATH)
  {
    if (type == DIR)
    {
      strcpy (savepath, name);
      wimp_set_icon_state ( chandle,
                            S_SAVEPATH,
                            (wimp_iconflags)0,
                            (wimp_iconflags)0);
    }
    else
    {
      s = strrchr(name, '.');
      if (s)
      {
        *s = 0x00;
        strcpy (savepath, name);
        wimp_set_icon_state ( chandle,
                              S_SAVEPATH,
                              (wimp_iconflags)0,
                              (wimp_iconflags)0);
      }

    }
  }

  return (NULL);

  USE (type);
  USE (userhandle);
  USE (xvolatile);
}





/*****************************************************************************/

static os_error * configclose(int w,int userhandle)
{
 os_error * err;

 setwindowcoords();
 setsavepath();

 remkeyevent(configkey,chandle,0);
 remclickevent(configicon,chandle,0);
 remcloseevent(configclose,chandle,0);
 remzeroevent(keyzero,0);
  remdataload (chandle, 0, config_loadfiles);
  remdataloadtype (chandle, 0, config_loadfiletype);
 remhelpevent (NULL, chandle, TCONFIG);

 err=closedown(&chandle);

 closearea();
 if (choicehandle) choice_close (0, 0);

 updatemodule();

 return(err);

 USE(userhandle);
 USE(w);
}





/*****************************************************************************/

os_error * configure(void)
{
  os_error * err;
  os_regset r;
  wimp_winfo result;
  int max_wid_t;
  int max_ht_t;
  int vis_ht_t;


  if(chandle) err = forward (chandle,0,NULL);
  else
  {
    err = createwindow (TCONFIG,&chandle);
    if(!err)
    {
      addkeyevent (configkey,chandle,0);
      addclickevent (configicon,chandle,0);
      addcloseevent (configclose,chandle,0);
      addzeroevent (keyzero,0);
      addhelpevent (NULL, chandle, TCONFIG);
      /* A drag of a dir to the save path field */
      adddataload (chandle, 0, config_loadfiles);
      adddataloadtype (chandle, 0, config_loadfiletype);

      select (chandle,modeicon[smode]);

      selectst (chandle, S_LSHFT, hotkey & LSHFT);
      selectst (chandle, S_RSHFT, hotkey & RSHFT);
      selectst (chandle, S_LCTRL, hotkey & LCTRL);
      selectst (chandle, S_RCTRL, hotkey & RCTRL);
      selectst (chandle, S_LALT, hotkey & LALT);
      selectst (chandle, S_RALT, hotkey & RALT);
      selectst (chandle, S_LLOGO, hotkey & LLOGO);
      selectst (chandle, S_RLOGO, hotkey & RLOGO);

      setindirect (chandle, S_DESTINATION, sizeof (save_destination[save_method]),
                                           save_destination[save_method]);
      setindirect (chandle, S_SAVEPATH, sizeof(savepath), savepath);
      setindirect (chandle, S_OUTPUT, sizeof (save_format[output_format]),
                                           save_format[output_format]);
      shadeoutputicons ();

      selectst (chandle, S_MOVE, sptrmove);
      selectst (chandle, S_SHOW, sptrshow);
      selectst (chandle, S_WINTOP, wintotop);    /* CJ */
      selectst (chandle, S_HIDECONTROL, hide_control_win);    /* CJ */
      selectst (chandle, S_DELAYSNAP, sdelaysnap);    /* CJ */

      showfill (chandle);

      write_ind_icon_int_value (chandle, S_DELAYTIME, sdelaytime);

      if (first_open)
      {
        /* Before opening the window for the first time, ensure the saved
        ** config window details match the current template definition */
        result.w = chandle;
        r.r[1] = 1 + (int)(&result);
        err = os_swix (Wimp_GetWindowInfo, &r);
        if (!err)
        {
          /* Find max dims of window */
          max_wid_t = result.info.ex.x1 - result.info.ex.x0;
          max_ht_t = result.info.ex.y1 - result.info.ex.y0;
          /* Set wx1 */
          wx1 = wx0 + max_wid_t;
          /* Find visible height - template should have been saved
          ** with window contracted */
          vis_ht_t = result.info.box.y1 - result.info.box.y0;

          if (max_ht_t > (2 * vis_ht_t))
            wy1 = wy0 + SMALL_HEIGHT;      /* open at small size */
          else wy1 = wy0 + max_ht_t;       /* open at full size */

        }
        first_open = 0;            /* reset flag */
      }

      open (chandle,wx0,wy0,wx1,wy1,0,0,-1);

      setmode (smode);
    }
  }
  return (err);
}




/*****************************************************************************/

static contokstr modetoks[]=
{
 "Window",SWINDOW,
 "Contents",SCONTENTS,
 "Area",SAREA,
 "Screen",SSCREEN,
 NULL,0
};



/*****************************************************************************/

static contokstr spathtoks[]=
{
 "", sizeof(savepath),
 NULL,0
};




/*****************************************************************************/

/* LOAD/SAVE CONFIGURATION FILES */

static void make_choices_path (BOOL save, char *path, char *filename)

{
  os_error * err;
  int type;

  if (save)
  {
    if (os_read_var_size (write_chsysvar) != 0)
    {
      /* use the appropriate choices path to save config file */
      sprintf (path, "%s%s", cwritepath, filename);
      /* ensure all directories exist */
      fs_create_dir_chain (path);
    }
    else
    {
      /* Look inside app directory */
      sprintf (path, "%s%s", apppath, filename);
    }
  }
  else
  {
    if (os_read_var_size (read_chsysvar) != 0)
    {
      /* use the appropriate choices path to save config file */
      sprintf (path, "%s%s", creadpath, filename);
      /* does the file in choices exist? */
      err = fs_exists (path, &type);
      if (!type || err)
      {
        /* drop back to use the app path */
        sprintf (path, "%s%s", apppath, filename);
      }
    }
    else
    {
      /* Look inside app directory */
      sprintf (path, "%s%s", apppath, filename);
    }
  }


  return;
}





static contag contable[] =
{
 "Mode",         CONTOK,&smode,    modetoks,NULL,
 "ShowPtr",      CONINT,&sptrshow, NULL,NULL,
 "MovePtr",      CONINT,&sptrmove, NULL,NULL,
 "Trigger",      CONINT,&hotkey,   NULL,NULL,
 "SavePath",     CONSTR,savepath,  spathtoks,NULL,
 "WindowX0",     CONINT,&wx0,      NULL,NULL,
 "WindowY0",     CONINT,&wy0,      NULL,NULL,
 "WindowX1",     CONINT,&wx1,      NULL,NULL,
 "WindowY1",     CONINT,&wy1,      NULL,NULL,
 "SaveMethod",   CONINT,&save_method, NULL,NULL,
 "Fill",         CONINT,&sfill,    NULL,NULL,
 "AutoCoord",    CONINT,&autocoord,NULL,NULL,
 "SnapX0",       CONINT,&areax0,   NULL,NULL,
 "SnapY0",       CONINT,&areay0,   NULL,NULL,
 "SnapX1",       CONINT,&areax1,   NULL,NULL,
 "SnapY1",       CONINT,&areay1,   NULL,NULL,
 "WindowToTop",  CONINT,&wintotop, NULL,NULL,
 "HideControl",  CONINT,&hide_control_win, NULL,NULL,
 "UseDesktopFont", CONINT,&UseDesktopFont, NULL,NULL,
 "OutputFormat", CONINT, &output_format, NULL, NULL,
 "JPEGQuality",  CONINT, &jpeg_quality, NULL, NULL,
 "AddFileExt",   CONINT, &add_ftype_ext, NULL, NULL,
 "OldFormatSpr", CONINT, &old_format_spr, NULL, NULL,
 "Convert64KTo32K", CONINT, &convert_64_32, NULL, NULL,
 "SnapDelayTime", CONINT, &sdelaytime, NULL, NULL,
 "OpenControl", CONINT, &set_open_control_at_start, NULL, NULL,
 "ResetSuffix", CONINT, &set_reset_suffix_at_start, NULL, NULL,

 NULL,0,NULL,NULL,NULL
};


static conlink configlink=
{
 "Snapper",
 NULL,
 NULL
};



/************************************************************/

os_error * configsave(void)
{
  os_error * err;
  char   *spath;
  char *leaf = "Config";

  err = FLEXALLOC(spath, FILEPATH_SIZE);
  if (!err)
  {
    make_choices_path (CHSAVE, spath, leaf);
    setwindowcoords();
    setsavepath();
    saveconfig(spath);
    FLEXFREE(spath);
  }
  return(NULL);
}



/**************************************************************
**                                                            *
** Functions for the loading and saving of the current values *
** of the suffixes used to create unique filenames            *
**                                                            *
**************************************************************/


#define  SLEN 32

/************************************************************/

void config_save_suffixes (void)

{
  os_error  *err;
  FILE   *handle;
  char   *path;
  char *leaf = "Suffix";
  int i;


  err = FLEXALLOC(path, FILEPATH_SIZE);
  if (!err)
  {
    make_choices_path (CHSAVE, path, leaf);
    handle = fopen (path, "w");

    if (handle)
    {
      for (i = 0; i < 4; i++)
      {
        fprintf(handle, "%d\n", count[i]);
      }
      fclose (handle);
    }

    FLEXFREE(path);
  }

  return;
}




/************************************************************/

static void load_suffixes (void)

{
  os_error  *err;
  FILE   *handle;
  char   *path;
  char *leaf = "Suffix";
  char sfx[SLEN];
  int i;

  err = FLEXALLOC(path, FILEPATH_SIZE);
  if (!err)
  {
    make_choices_path (CHLOAD, path, leaf);
    handle = fopen (path, "r");
    if (handle)
    {
      for (i = 0; i < 4; i++)
      {
        fgets (sfx, SLEN, handle);
        count[i] = atoi (sfx);
      }
      fclose (handle);
    }
    FLEXFREE(path);
  }

  return;
}


/************************************************************/

static void init_config_vars(void)
{
  strcpy(savepath,"<Snapper$Dir>.^");
  sfill=16;        /* Trans */
  hotkey=5;        /* left ctrl + left Alt */
  sptrmove=1;
  sdelaysnap = 0;
  wx0=636;
  wy0=174;
  wx1=1204;
  wy1=302;
  UseDesktopFont = 0;
  output_format = 0;
  jpeg_quality = 75;
  sdelaytime = 10;
  set_open_control_at_start = 0;
  set_reset_suffix_at_start = 1;
  /* areax0 etc are set by compiler */
  /* others are initialised to 0 by being declared static */
  return;
}



/************************************************************/

static os_error * saveoptall(int handle, int opt, int * type, char * name)
{
  char *p, *q;

  if (fname_set)
  {
    strcpy (name, fn_buf);
    fname_set = 0;
  }

  if (add_ftype_ext)
  {
    /* remove existing ext */
     p = fs_leaf (name);
    /* remove any existing extension */
    q = strrchr (p, '/');
    if (q) *q = 0;
  }

  saveopt = opt;

  switch (saveopt)
  {
    case OUTPUT_SPRITE:
      *type = SPRITE;
      break;

    case OUTPUT_DRAW:
      *type = DRAW;
      break;

    case OUTPUT_PNG:
      *type = PNG;
      if (add_ftype_ext) strcat (name, "/png");
      break;

    case OUTPUT_JPEG:
      *type = JPEG;
      if (add_ftype_ext) strcat (name, "/jpg");
      break;

  }

  return(NULL);

  USE(handle);
}





/************************************************************/

static void init_save_options (void)
{
  saveopts[0].name = "Sprite";
  saveopts[1].name = "Draw";
  saveopts[2].name = "PNG";
  saveopts[3].name = "JPEG";
  saveopts[0].fn = saveoptall;
  saveopts[1].fn = saveoptall;
  saveopts[2].fn = saveoptall;
  saveopts[3].fn = saveoptall;

  return;
}




/************************************************************/

os_error * configinit(void)
{
  os_error * err;
  char   *spath;

  /* we need to allow for the config file being absent. If it is then some
     variables will not be initialised to sensible values */
  init_config_vars();

  err=addcontable(contable,&configlink);
  err = FLEXALLOC(spath, FILEPATH_SIZE);
  if (!err)
  {
    make_choices_path (CHLOAD, spath, "Config");
    if(!err)
      err=loadconfig(spath);
    FLEXFREE(spath);
  }

  setsavepath();
  updatemodule();

  init_save_options();

  if (!set_reset_suffix_at_start)
  {
    load_suffixes ();
  }

  return(NULL);
}




/*******************************************************************/
/********************* Clipboard related routines ******************/
/*******************************************************************/



os_error * claim_entity (wimp_msgstr * msg, int * ack)
{
  /* Ignore it if it is our own message being returned? */
  if (msg->hdr.task != taskhandle)
  {
    if ((msg->data.words[0] & CLIPBOARD_CLAIM) == CLIPBOARD_CLAIM)
    {
      if (we_own_clipboard)
      {
        if(save_buf) sfree((flex_ptr)&save_buf);
        we_own_clipboard = 0;
      }
    }
  }

  return (NULL);

  USE (ack);
}



/************************************************************/

os_error * data_request (wimp_msgstr * msg, int * ack)
{
  os_error * err;

  int i;
  int send = 0;

  err = NULL;
  if (msg->data.words[4] & CLIPBOARD_CLAIM)
  {
    /* set up the send */
    if (we_own_clipboard)
    {

      i = 5;     /* start of the filetype data */
      /* max data size corresponds to words[59] so add a check to prevent
       * us checking beyond the end of the message block in case there has
       * been any corruption, or a -1 is never found
      */
      while ((msg->data.words[i] != -1) && (i < 60))
      {
        if ((msg->data.words[i] == SPRITE) || (msg->data.words[i] == 0))
        {
          clipboard_type = SPRITE;
          send = 1;
          break;
        }
        else
        {
          if (msg->data.words[i] == DRAW)
          {
            clipboard_type = DRAW;
            send = 1;
            break;
          }
        }
        i++;
      }


      if (send)
      {
        err = setsave0 (clipboard_type, save_cb);
        if (!err) err = setsave2 (NULL, saveinfo);
        if (!err) sendsave3 (clipboard_type, leaf_buf, msg);
      }

    }
  }
  return (err);

  USE (ack);
}









/*************  End of c.config  ***********************/


