/* trash.c
 *
 * A small utility for emulating a Mac style wastebasket - demonstrates the
 * use of the FilerAction commands as well as a std Acorn application
 * 
 * Program   QuickMode
 * Version   A 1.00
 * Author    Keith McAlpine
 * RISC User August 1993
 * Program   Subject to Copyright
 *           Not Public Domain
 * 
 */

#include "wimp.h"
#include "wimpt.h"
#include "win.h"
#include "event.h"
#include "baricon.h"
#include "res.h"
#include "resspr.h"
#include "menu.h"
#include "template.h"
#include "dbox.h"
#include "swis.h"
#include <stdlib.h>
#include <string.h>

/******************************* CONSTANTS ******************************/

/* Menu items */
#define kMenu_info     1
#define kMenu_show     2
#define kMenu_empty    3
#define kMenu_quit     4

#define kInfo_field    4       /* Info box field for the version string */


#define kFiler_Move    1       /* reason code for a move file operation */
#define kFiler_Delete  2       /* reason code for delete file operation */
#define kFileOp_Flags  0x04    /* code for ~V ~C F ~N ~R move options   */
#define kBinFull       FALSE   /* bin status flag                       */
#define kBinEmpty      TRUE    /* bin status flag                       */
#define kADFS_FileNo   8       /* the file handle set by Acorn for ADFS */
#define kDisplayBin    TRUE    /* constant to show the bin              */
#define kCloseBin      FALSE   /* constant to remove the bin            */


/******************************* GLOBAL DATA ******************************/

static char* kVersion_String = "1.00 (21 January 1993)"; /* app version    */
static char* kFull = "Full";                             /* full bin name  */
static char* kEmpty = "Empty";                           /* empty bin name */
static char* kBinDir = "<Trash$Dir>.TheBin";             /* bin dir. name  */

static menu        gMenu;           /* The top of the menu tree           */
static BOOL        gBinEmpty;       /* The state of the bin               */
static char*       gBin;            /* Ptr to current iconbar pic sprite  */
static char        gFileList[1024]; /* Buffer to hold file name info data */


/***************************** BIN ROUTINES ! *****************************/

void SetBinState(BOOL binEmpty)
{
  gBinEmpty = binEmpty;                 /* sort out our internal value    */
  gBin = (binEmpty ? kEmpty : kFull);   /* work out the bar sprite name   */
  baricon_newsprite(gBin);              /* get the iconbar sprite changed */
}


void OpenCloseBinDirectory(BOOL openIt)
{
  wimp_msgstr msg;

  /* set up the message header for a FilerOpenDir/CloseDir request */
  msg.hdr.size = 256;
  msg.hdr.your_ref = 0;
  msg.hdr.action = (openIt ? wimp_FilerOpenDir : wimp_FilerCloseDir);

  /* NB: 'device' is used as it has the same format as the Filer Dir
   *     message block */

  msg.data.device.major = kADFS_FileNo;      /* ADFS compatible only !! */
  msg.data.device.minor = 0;
  strcpy((char*) &msg.data.device.information, kBinDir);

  wimp_sendmessage(wimp_ESEND, &msg, 0);     /* get the message sent off */
}


void DeleteBin(void)
{
  int   filerTask;

  /* sort out the delete action & task */
  os_swi1r(XOS_Bit | Wimp_StartTask, (int) "Filer_Action", &filerTask);

  /* tell the Filer what the directory parent is */
  os_swi2(XOS_Bit | FilerAction_SendSelectedDirectory,
          filerTask, (int) kBinDir);

  /* pass all the files (from GetFiles) to the filer to delete */
  os_swi2(XOS_Bit | FilerAction_SendSelectedFile, filerTask,(int) gFileList);

  /* and start off the action */
  os_swi6(XOS_Bit | FilerAction_SendStartOperation,
          filerTask, kFiler_Delete, kFileOp_Flags, 0, 0, 0);

  /* Update our internal values */
  SetBinState(kBinEmpty);
  OpenCloseBinDirectory(kCloseBin);
}


void FileDropOnBin(wimp_eventstr* e, void* handle)
{
  handle = handle;   /* To stop a compiler warning */
  
  if (e->e == wimp_ESENDWANTACK)
  {
    if (e->data.msg.hdr.action == wimp_MDATALOAD)
    {
      /* a file has been dropped on us to delete */
      char  parentDir[230];
      char* fileToMove;
      char* truncate;
      int   filerTask;
      int   dirLength = strlen(kBinDir) + 1;

      /* copy the filename from the msg to our own buffer */
      strcpy((char*) &parentDir, e->data.msg.data.dataload.name);

      /* break off the leafname from the full pathname so that we have
       * dirname/0leafname/0 instead of dirname.leafname/0 */
      truncate = strrchr(parentDir, '.');
      fileToMove = (char*) (truncate + 1);
      truncate[0] = NULL;

      /* send out our reply acknowledging the message */
      e->data.msg.hdr.action = wimp_MDATALOADOK;
      e->data.msg.hdr.your_ref = e->data.msg.hdr.my_ref;
      wimp_sendmessage(wimp_EACK, &e->data.msg, e->data.msg.hdr.task);

      /* now it's time to send the FilerAction SWIs */
      os_swi1r(XOS_Bit | Wimp_StartTask, (int) "Filer_Action", &filerTask);

      /* r0 = returned filer task, r1 = ptr to dir/leaf name */
      os_swi2(XOS_Bit | FilerAction_SendSelectedDirectory,
              filerTask, (int) &parentDir);
      os_swi2(XOS_Bit | FilerAction_SendSelectedFile,
              filerTask, (int) fileToMove);

      /* now start off the Filer operation */
      os_swi6(XOS_Bit | FilerAction_SendStartOperation, filerTask,
              kFiler_Move, kFileOp_Flags, (int) kBinDir, dirLength, 0);

      /* finally set up our flags to show the bin is non-empty */
      SetBinState(kBinFull);
    }
  }
}


BOOL GetFiles(char* directory)
{
  os_regset regs;
  char*     posInList;
  int       n;

  regs.r[0] = 9;  /* code to read in just leafnames (NULL between each) */
  regs.r[1] = (int) directory;  /* directory to read from */
  regs.r[2] = (int) &gFileList; regs.r[3] = 255;
  regs.r[4] = 0; regs.r[5] = 254; regs.r[6] = 0;

  /* read in the directory information */
  os_swix(XOS_Bit | OS_GBPB, &regs);

  /* now change all the NULLs in the list into spaces */
  for (n = 1; n < regs.r[3] /* num. of files */ ; n++)
  {
    posInList = gFileList + strlen(gFileList);
    posInList[0] = ' ';  /* change the NULL to a space */
  }

  return (regs.r[3] == 0);   /* return TRUE if the bin is empty */
}


/***************************** EVENT HANDLERS *****************************/

/******* Handler called when the Trashcan iconbar icon is clicked on *******/

static void IconClick(wimp_i icon)
{
  icon = icon;  /* To stop a compiler warning */

  if (!gBinEmpty)
    OpenCloseBinDirectory(kDisplayBin);
}


/******************* Display the program info dialog box *******************/

static void ProgInfoBox(void)
{
  dbox  d;  /* Dialogue box handle */

  if (d = dbox_new("ProgInfo"), d != NULL)
  {
    /* Fill in the version number */
    dbox_setfield(d, kInfo_field, kVersion_String);

    /* Show, display, and dispose on the dialog */
    dbox_show(d);
    dbox_fillin(d);
    dbox_dispose(&d);
  }
}


/*** Event handler for the menu - maker routine to grey out when need to ***/

static menu MakeMenu(void* handle)
{
  handle = handle;  /* stop compiler warning */

  if (!event_is_menu_being_recreated())
  {
    /* Create the menu tree */
    if (gMenu != NULL) menu_dispose(&gMenu, TRUE);
    gMenu = menu_new("Trashcan", ">Info,Show Contents,Empty Bin,Quit");
  }

  /* set up the menu flags */
  menu_setflags(gMenu, kMenu_empty, 0, gBinEmpty);
  menu_setflags(gMenu, kMenu_show, 0, gBinEmpty);

  return gMenu;
}


static void MenuHit(void *handle, char *hit)
{
  handle = handle; /* stop compiler warning */

  /* Find which menu item was hit and take action as appropriate */
  switch (hit[0])
  {
    case kMenu_info:               
      ProgInfoBox();
      break;

    case kMenu_show:
      /* Show the bin contents (update our variables at the same time) */
      SetBinState(GetFiles(kBinDir));        /* check our internals! */
      if (!gBinEmpty)
        OpenCloseBinDirectory(kDisplayBin);  /* display the bin if full */
      break;

    case kMenu_empty:                        /* Empty the bin contents */
      SetBinState(GetFiles(kBinDir));        /* check our internals! */
      if (!gBinEmpty)
        DeleteBin();
      break;

    case kMenu_quit:                 /* Exit from the program. */
      win_register_event_handler(win_ICONBARLOAD,
                                 (win_event_handler) NULL, NULL);
      exit(0);
  }
}


/***************************** INITIALISATION *****************************/

/*--- Initialise the program, returning TRUE if it was all OK. ---*/
static BOOL Initialise(void)
{
  /* RISC_OSlib initialisation */

  wimpt_init("Trashcan");           /* Main Wimp initialisation */
  res_init("trash");                /* Resources */
  resspr_init();                    /* Application sprites */
  template_init();                  /* Templates */
  dbox_init();                      /* Dialogue boxes */

  /* Set up the icon on the icon bar, and declare its event handlers */
  gMenu = NULL;
  baricon_left(kEmpty, (int) resspr_area(), IconClick);
  SetBinState(GetFiles(kBinDir));  /* set our internal variables */

  if (!event_attachmenumaker(win_ICONBAR, MakeMenu, MenuHit, 0))
    return FALSE; /* Unable to attach menu */

  win_register_event_handler(win_ICONBARLOAD, FileDropOnBin, NULL);

  /* All went ok */
  return TRUE;
}

/****************************** MAIN PROGRAM *******************************/

int main()
{
  if (Initialise())
  {
    /* The main event loop */
    while (TRUE)
      event_process();
  }

  return 0;
}
