/* Program   Tree
             Displays the disc contents in the form of a directory tree
 * Version   A 1.11
 * Author    Andrew Cumming
 * RISC User December 1992
 * Program   Subject to Copyright
 *           Not Public Domain
 */

#include "wimp.h"
#include "wimpt.h"
#include "win.h"
#include "werr.h"
#include "event.h"
#include "baricon.h"
#include "res.h"
#include "resspr.h"
#include "menu.h"
#include "template.h"
#include "dbox.h"
#include "flex.h"
#include "heap.h"
#include "os.h"
#include "visdelay.h"

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


/*------------------------------- definitions ---------------------------*/

/* Menu items */
#define BAR_MENU_info  1
#define BAR_MENU_readtree 2
#define BAR_MENU_savesettings 3
#define BAR_MENU_quit  4

#define TREE_MENU_options 1
#define TREE_MENU_select 2
#define TREE_MENU_view 3
#define TREE_MENU_tree 4
#define TREE_MENU_update 5
#define TREE_MENU_openroot 6

#define TREE_MENU_tree_exbranch 1
#define TREE_MENU_tree_clbranch 3
#define TREE_MENU_tree_exlevel 2
#define TREE_MENU_tree_clall 5
#define TREE_MENU_tree_exall 4

#define TREE_MENU_select_open 1
#define TREE_MENU_select_run 2

/* dbox fields */
#define info_FIELD 4

/* SWIs */

#define XOS_SWINumberFromString  0x20039
#define XWimp_SpriteOp  0x600e9   /* 0x400e9 | 0x20000 */
#define OS_ConvertHex4  0xd2
#define XOS_FSControl   0x20029
#define OS_FSControl    0x29
#define OS_GBPB         0xc
#define XOS_GBPB        0x2000c
#define XOS_Find        0x2000d
#define OS_Find         0xd
#define XOS_File        0x20008
#define OS_File         0x8
#define Wimp_StartTask  0x400de

/* misc */

#define XSTEP 56
#define YSTEP 48
#define DIRECTORY 0x1000
#define APPLICATION 0x2000


/*----------------------------- some prototypes -------------------------*/

static void set_new_extent(void);
static void do_redraw(void);
static void open_tree_window(void);

/*------------------------------- global data ---------------------------*/

static struct object
{
  char name[11];
  int filetype;
  int depth;
  char *path;
  BOOL go_down;
  struct object *next;
  struct object *up;
  struct object *down;
};

static struct object *SELECTED = NULL;   /* currently selected object */
static struct object *root = NULL;


/* Menu and window handles */

static menu bar_menu;
static menu tree_menu;
static menu tree_tree_menu;
static menu root_menu;
static menu ADFS_menu;
static menu SCSI_menu;
static menu IDE_menu;
static menu select_menu;

static wimp_w tree_win_handle;


/* Flags */

static int view_files = 0;
static int view_apps = 1;
static int view_app_contents = 0;
static BOOL view_all_files = TRUE;

static int save_on_exit;
static int auto_opendir = 0;
static int auto_exbranch = 0;
static int expand_on_entry = 0;

static BOOL DATA_LOADED = FALSE;
static BOOL window_open = FALSE;
static BOOL down_FLAG = FALSE;


/* Misc. */

static int SCSI_default_drive;
static int SCSI_drives;
static int IDE_default_drive;
static int IDE_drives;
static int ADFS_default_drive;
static int ADFS_hard_drives;
static int ADFS_floppies;
static char SNAME[15];
static char M[256];
static char *DIR = "small_dir";
static char *APP = "small_app";
static char *FIL = "small_xxx";
static char *BRANCH = "tree_branch";
static char DEVICE[256] = "";
static char ROOTSTR[256] = "";
static char FILETYPES[256] = "Obey,FFF";
static char WILDNAME[11] = "*";
static char WILDAPP[11] = "*";
static char *ADFS_string = "ADFS::0";
static char *SCSI_string = "SCSI::0";
static char *IDE_string = "IDEFS::0";
static char *ADFS_name = "ADFS";
static char *SCSIFS_name = "SCSIFS";
static char *IDEFS_name = "IDEFS";
static char *RAMFS_name = "RAM";
static char *version_string = "1.11 (10-Oct-92)";
static int ADFS_Drives;
static int SCSIFS_Drives;
static int IDEFS_Drives;

/*--------------------------- struct bits -------------------------------*/

static struct object *get_list(char *dir, struct object *up, int depth)
{
  int n = 0;
  char ndir[256];
  struct object *p, *first, *previous = NULL;
  os_regset r;

  r.r[0] = 10;
  r.r[1] = (int) dir;
  r.r[2] = (int) M;
  r.r[5] = 256;
  r.r[6] = NULL;

  while (n != -1)
  {
    r.r[3] = 1;
    r.r[4] = n;
    if (wimpt_complain(os_swix(XOS_GBPB, &r)) != NULL) return NULL;

    if (n == 0 && r.r[3] == 0) { DATA_LOADED=TRUE; return (NULL); }

    n = r.r[4];

    if (r.r[3] == 1)
    {
      p = (struct object *) heap_alloc(sizeof(struct object));
      strcpy(p->name, &M[20]);
      p->depth = depth;
      if (M[3] == 0xff && (M[2] & 0xf0)==0xf0)
        p->filetype = ((M[2] & 0xf)<<8) | M[1];
      else p->filetype = 0xffff;

      if (previous == NULL) first = p;
      else previous->next = p;
      previous = p;

      if (M[16] == 2)
      {
        strcpy(ndir,dir);
        strcat(ndir,".");
        strcat(ndir,p->name);

        if (p->name[0] == '!') p->filetype = APPLICATION;
        else p->filetype = DIRECTORY;

        p->path = (char *) heap_alloc(strlen(ndir)+1);
        strcpy(p->path, ndir);

        if (expand_on_entry == 1) p->go_down = TRUE;
        else p->go_down = FALSE;

        p->down = get_list(ndir, p, depth+1);
      }
      else
      {
        p->down = NULL;
        p->path = NULL;
      }
      p->up = up;
    }
  }
  p->next = NULL;

  DATA_LOADED = TRUE;
  return (first);
}


static void free_list(struct object *p)
{
  struct object *q;

  while (p != NULL)
  {
    q = p;
    if (p->down != NULL) free_list(p->down);
    p = p->next;
    heap_free((void *) q);
  }

  DATA_LOADED = FALSE;
}


static BOOL should_show_filetype(int filetype)
{
  os_regset r;
  BOOL yes = FALSE;
  int count = 0;

  while (FILETYPES[count] != NULL)
  {
    if (FILETYPES[count] == ',') FILETYPES[count] = 13;
    count++;
  }

  if (count == 0) return FALSE;

  count = 0;
  while (count < 256)
  {
    r.r[0] = 31;
    r.r[1] = (int) &FILETYPES[count];
    os_swi(OS_FSControl, &r);

    if (r.r[2] == filetype) { yes = TRUE; break; }

    while (FILETYPES[count] != NULL && FILETYPES[count] != 13) count++;
    if (FILETYPES[count] == NULL) break;
    count++;
  }

  count = 0;
  while (FILETYPES[count] != NULL)
  {
    if (FILETYPES[count] == 13) FILETYPES[count] = ',';
    count++;
  }

  return (yes);
}


static BOOL same(char *n, char *w)       /* does name at n match with
                                            wildcarded name at w */
{
  int wc=0, nc=0;

  while (n[nc] != NULL && w[wc] != NULL)
  {
    switch(w[wc])
    {
      case '*':
        wc++;
        if (w[wc] == NULL) return TRUE;
        while (n[nc] != NULL && n[nc] != w[wc]) nc++;
        if (n[nc] == NULL) return FALSE;
        break;

      case '#':
        wc++; nc++;
        break;

      default:
        if (tolower((int) w[wc]) != tolower((int) n[nc])) return FALSE;
        nc++; wc++;
        break;
    }
  }
  if (w[wc] == NULL && n[nc] == NULL) return TRUE;
  return FALSE;
}


static struct object *raw_get_nextpointer(struct object *p)
{
  if (p == NULL) return (NULL);

  if (p->filetype == DIRECTORY &&
      p->down != NULL &&
      p->go_down == TRUE)     { down_FLAG = TRUE;
                                return (p->down); }

  if (p->filetype == APPLICATION &&
      p->down != NULL &&
      view_app_contents == 1 &&
      p->go_down == TRUE)       { down_FLAG = TRUE;
                                  return (p->down); }

  if (p->next != NULL) return (p->next);
  while (p->up != NULL)
  {
    p = p->up;
    down_FLAG = FALSE;
    if (p->next != NULL) return (p->next);
  }
  return (NULL);
}


static struct object *get_nextpointer(struct object *p)
{
  down_FLAG = FALSE;
  while (p != NULL)
  {
    if ((p = raw_get_nextpointer(p)) != NULL)
    {
      switch (p->filetype)
      {
        case DIRECTORY:
          return (p);
          break;

        case APPLICATION:
          if (view_apps == 1)
          {
            if (same(p->name, WILDAPP)) return (p);
          }
          break;

        default:
          if (view_files == 1)
          {
            if (same(p->name, WILDNAME))
            {
              if (view_all_files) return (p);
              else { if (should_show_filetype(p->filetype)) return (p); }
            }
          }
          break;
      }
    }
  }
  return (NULL);
}


static struct object *get_pointer_number(int n)
{
  int count = 0;
  struct object *p = root;

  if (n < 1) return NULL;

  while (count != n)
  {
    p = get_nextpointer(p);
    count ++;
    if (p == NULL) break;
  }

  return (p);
}


static void do_branch(struct object *p, BOOL go)
{
  while (p != NULL)
  {
    p->go_down = go;
    if (p->down != NULL) do_branch(p->down, go);
    p = p->next;
  }
}


/*----------------------------- disc bits -------------------------------*/

static void do_dir(struct object *p, BOOL open)
{
  char string[256];

  if (open) strcpy(string, "Filer_OpenDir ");
  else strcpy(string, "Filer_CloseDir ");
  if (p->up != NULL) strcat(string, (p->up)->path);
  else strcat(string, ROOTSTR);
  strcat(string, ".");
  strcat(string, p->name);

  os_cli(string);
}


static void run_object(struct object *p)
{
  os_regset r;

  char string[256];
  if (p->up != NULL) strcpy(string, (p->up)->path);
  else strcpy(string, ROOTSTR);
  strcat(string, ".");
  strcat(string, p->name);

  r.r[0] = (int) string;
  os_swi(Wimp_StartTask, &r);
}


static BOOL nice_path(char *s)
{
  os_regset r;

  if (s[0] == NULL)
  {
    werr(NULL, "Please enter a path to use as the root of the tree.");
    return FALSE;
  }

  r.r[0] = 17;
  r.r[1] = (int) s;
  if (wimpt_complain(os_swix(XOS_File, &r)) != 0) return FALSE;
  if (r.r[0] == 1)
  {
    werr(NULL, "The path '%s' points to a file.", DEVICE);
    return FALSE;
  }
  if (r.r[0] == 0)
  {
    werr(NULL, "The path '%s' is not valid.", DEVICE);
    return FALSE;
  }
  return TRUE;
}

static BOOL file_system_present(char *name)
{
  os_regset r;

  r.r[0] = 13;
  r.r[1] = (int) name;
  r.r[2] = 0;

  os_swi(OS_FSControl, &r);

  return (r.r[2] != 0);
}


/* not needed!

static int file_system_number(char *name)
{
  os_regset r;

  r.r[0] = 13;
  r.r[1] = (int) name;
  r.r[2] = 1;

  os_swi(OS_FSControl, &r);

  if (r.r[2] == 0) return (0);
  else return(r.r[1]);
} 
*/


static BOOL set_root(int drive, char *s)
{
  os_regset r;

  if (s == IDE_string) s[7] = 48+drive;
  else s[6] = 48+drive;
  strcpy(M, s);
  strcat(M, ".$");
  r.r[0] = 0;
  r.r[1] = (int) M;

  if (s != ADFS_string)
  {
    if (wimpt_complain(os_swix(XOS_FSControl, &r)) != NULL) return FALSE;
  }
  else
  {
    if (os_swix(XOS_FSControl, &r) != NULL)
      { werr(NULL,"Drive %d is empty",drive); return FALSE; }
  }

  r.r[0] = 5;
  r.r[2] = (int) M;
  os_swi(OS_GBPB, &r);

  strcpy(ROOTSTR, s);
  if (s == IDE_string) ROOTSTR[7] = 0;
  else ROOTSTR[6] = 0;
  M[(int)M[0] + 1] = NULL;

/*  if (strcmp(&M[1], "\"Unset\"") == 0)
  {
    werr(NULL, "Drive %d is empty", drive);
    return FALSE;
  }
*/

  strcat(ROOTSTR, &M[1]);
  strcat(ROOTSTR, ".$");
  return TRUE;
}


/*-------------------------------- settings -----------------------------*/

static void do_gbpb(int *a, os_regset *r)
{
  r->r[2] = (int) a;
  r->r[3] = 4;
  os_swi(OS_GBPB, r);
}


static void do_settings(BOOL SAVE)
{
  int file_handle;
  os_regset r;
  os_error *e;

  if (SAVE) r.r[0] = 0x8f;                 /* Open file */
  else r.r[0] = 0x4f;
  r.r[1] = (int) "<Tree$Dir>.Settings";
  r.r[2] = NULL;
  e = os_swix(XOS_Find, &r);

  if (e != NULL)
  {
    if (SAVE) wimpt_complain(e);
    else werr(NULL, "Tree could not open its settings file : default settings will be used.");
    return;
  }

  file_handle = r.r[0];

  if (SAVE) r.r[0] = 2;        /* these are preserved through OS_GBPB */
  else r.r[0] = 4;
  r.r[1] = file_handle;  

  do_gbpb(&view_files, &r);        /* ints */
  do_gbpb(&view_apps, &r);
  do_gbpb(&view_app_contents, &r);
  do_gbpb(&save_on_exit, &r);
  do_gbpb(&auto_opendir, &r);
  do_gbpb(&auto_exbranch, &r);
  do_gbpb(&expand_on_entry, &r);

  r.r[2] = (int) &view_all_files;     /* BOOL */
  r.r[3] = sizeof(BOOL);
  os_swi(OS_GBPB, &r);

  r.r[2] = (int) FILETYPES;    /* strings */
  r.r[3] = 256;
  os_swi(OS_GBPB, &r);

  r.r[2] = (int) DEVICE;
  r.r[3] = 256;
  os_swi(OS_GBPB, &r);

  r.r[2] = (int) WILDNAME;
  r.r[3] = 11;
  os_swi(OS_GBPB, &r);

  r.r[2] = (int) WILDAPP;
  r.r[3] = 11;
  os_swi(OS_GBPB, &r);

  r.r[0] = 0;
  os_swi(OS_Find, &r);    /* Close file */
}


/*-------------------------- dialogue boxes -----------------------------*/

static BOOL view_dbox_handler(dbox d, void *event, void *handle)
{
  wimp_eventstr *e = (wimp_eventstr *) event;
  wimp_i icon;

  handle = handle;

  if (e->e == wimp_EBUT)
  {
    icon = e->data.but.m.i;

    switch (icon)
    {
      case 3:
      case 4:
        if (e->data.but.m.bbits == wimp_BRIGHT)
        {
          if (dbox_getnumeric(d, icon) == 0)
          {
            dbox_setnumeric(d, icon, TRUE);
            return (TRUE);
          }
        }
        break;

      case 7:
        if (dbox_getnumeric(d, 7) == 0)
        {
          dbox_fadefield(d,3);
          dbox_fadefield(d,4);
          dbox_fadefield(d,5);
          dbox_fadefield(d,10);
        }
        else
        {
          dbox_unfadefield(d,3);
          dbox_unfadefield(d,4);
          dbox_unfadefield(d,5);
          dbox_unfadefield(d,10);
        }
        return (TRUE);

      case 1:
        if (dbox_getnumeric(d, 1) == 0)
        {
          dbox_fadefield(d,12);
          dbox_setnumeric(d, 2, 0);
        }
        else
        {
          dbox_unfadefield(d,12);
        }
        return (TRUE);

      case 2:
        if (dbox_getnumeric(d, 2) == 1 && dbox_getnumeric(d,1) == 0)
        {
          dbox_setnumeric(d, 2, 0);
          return TRUE;
        }
    }
  }
  return (FALSE);
}


static void do_view_dbox(void)
{
  dbox d;
  dbox_field response;

  if (d = dbox_new("view"), d!=NULL)
  {
    dbox_setnumeric(d, 7, view_files);
    dbox_setnumeric(d, 1, view_apps);
    if (view_apps == 0) dbox_fadefield(d,12);
    dbox_setnumeric(d, 2, view_app_contents);
    if (view_all_files) dbox_setnumeric(d, 3, 1);
    else dbox_setnumeric(d, 4, 1);
    if (view_files == 0)
    {
      dbox_fadefield(d,3);
      dbox_fadefield(d,4);
      dbox_fadefield(d,5);
      dbox_fadefield(d,10);
    }
    dbox_setfield(d,5,FILETYPES);
    dbox_setfield(d,10,WILDNAME);
    dbox_setfield(d,12,WILDAPP);

    dbox_raw_eventhandler(d, view_dbox_handler, 0);

    dbox_show(d);
    response = dbox_fillin(d);

    if (response == 0)
    {
      view_files = dbox_getnumeric(d,7);
      view_apps = dbox_getnumeric(d,1);
      view_app_contents = dbox_getnumeric(d,2);
      if (dbox_getnumeric(d, 3) == 1) view_all_files = TRUE;
      else view_all_files = FALSE;
      dbox_getfield(d, 5, FILETYPES, 256);
      dbox_getfield(d, 10, WILDNAME, 11);
      dbox_getfield(d, 12, WILDAPP, 11);
    }
    dbox_dispose(&d);

    if (response == 0) { set_new_extent();
                         do_redraw();
                       }
  }
}


static void info_about_program(void)
{
  dbox d;

  if (d = dbox_new("progInfo"), d != NULL)
  {
    dbox_setfield(d, info_FIELD, version_string);
    dbox_show(d);
    dbox_fillin(d);
    dbox_dispose(&d);
  }
}


static void do_options_dbox(void)
{
  dbox d;
  dbox_field response;

  if (d = dbox_new("options"), d != NULL)
  {
    dbox_setnumeric(d, 2, auto_opendir);
    dbox_setnumeric(d, 3, auto_exbranch);
    dbox_setnumeric(d, 4, save_on_exit);
    dbox_setnumeric(d, 5, expand_on_entry);

    dbox_show(d);
    response = dbox_fillin(d);
    if (response == 0)
    {
      auto_opendir = dbox_getnumeric(d, 2);
      auto_exbranch = dbox_getnumeric(d, 3);
      save_on_exit = dbox_getnumeric(d, 4);
      expand_on_entry = dbox_getnumeric(d, 5);
    }

    dbox_dispose(&d);
  }
}



/*---------------------------------- menus ------------------------------*/

static void bar_menu_proc(void *handle, char *hit)
{
  handle = handle;

  switch (hit[0])
  {
    case BAR_MENU_info:
      info_about_program();
      break;

    case BAR_MENU_readtree:
      if (hit[1] == 0)     /* if scuzzy present use that
                              else IDE else ADFS...*/
      {
        if (SCSI_drives>0)
        {
          if (!set_root(SCSI_default_drive, SCSI_string)) break;
        }
        else
        {
          if (IDE_drives>0)
          {
            if (!set_root(IDE_default_drive, IDE_string)) break;
          }
          else
          {
            if (!set_root(ADFS_default_drive, ADFS_string)) break;
          }
        }
      }
      if (hit[1] == 1)      /* own path */
      {
        if (nice_path(DEVICE)) strcpy(ROOTSTR, DEVICE);
        else break;
      }
      if (hit[1] == 2)      /* ram disc */
      {
        if (file_system_present(RAMFS_name)) strcpy(ROOTSTR,"RAM:$");
        else { werr(NULL,"There is no RAM disc!"); break; }
      }
      if (hit[1] == 3)     /* adfs drive */
      {
        if (hit[2] == 0)
        {
          if (!set_root(ADFS_default_drive, ADFS_string)) break;
        }
        if (hit[2] > 0)
        {
          if (hit[2] <= (ADFS_floppies))  /* floppy */
          {
            if (!set_root(hit[2]-1, ADFS_string)) break;
          }
          else      /* hard */
          {
            if (!set_root(hit[2]-(1+ADFS_floppies)+4, ADFS_string)) break;
          }
        }
      }
      if (hit[1] == 4)       /* scuzzy drive */
      {
        if (hit[2] == 0)
        {
          if (!set_root(SCSI_default_drive, SCSI_string)) break;
        }
        if (hit[2] > 0)
        {
          if (!set_root(hit[2]+3, SCSI_string)) break;
        }
      }
      if (hit[1] == 5)       /* IDE drive */
      {
        if (hit[2] == 0)
        {
          if (!set_root(IDE_default_drive, IDE_string)) break;
        }
        if (hit[2] > 0)
        {
          if (!set_root(hit[2]+3, IDE_string)) break;
        }
      }
      visdelay_begin();
      if (DATA_LOADED) free_list(root->next);
      root->next = get_list(ROOTSTR,NULL,0);
      visdelay_end();
      if (DATA_LOADED)
      {
        open_tree_window();
        do_redraw();
      }
      else
      {
        if (window_open)
        {
          window_open = FALSE;
          wimpt_noerr(wimp_close_wind(tree_win_handle));
        }
      }
      break;

    case BAR_MENU_savesettings:
      do_settings(TRUE);
      break;

    case BAR_MENU_quit:
      visdelay_begin();
      if (save_on_exit) do_settings(TRUE);
      if (DATA_LOADED) free_list(root->next);
      visdelay_end();
      exit(0);
  }
}


static void tree_menu_proc(void *handle, char *hit)
{
  handle = handle;

  switch (hit[0])
  {
    case TREE_MENU_openroot:
      {
        char string[256];
        strcpy(string, "Filer_OpenDir ");
        strcat(string, ROOTSTR);
        os_cli(string);
      }
      break;
    
    case TREE_MENU_update:
      visdelay_begin();
      free_list(root->next);
      root->next = get_list(ROOTSTR,NULL,0);
      visdelay_end();
      if (DATA_LOADED)
      {
        set_new_extent();
        do_redraw();
      }
      else
      {
        werr(NULL, "Tree was unable to read tree contents");
        window_open = FALSE;
        wimpt_noerr(wimp_close_wind(tree_win_handle));
      }
      break;

    case TREE_MENU_options:
      do_options_dbox();
      break;

    case TREE_MENU_select:
      switch (hit[1])
      {
        case TREE_MENU_select_open:
          do_dir(SELECTED, TRUE);
          break;

        case TREE_MENU_select_run:
          run_object(SELECTED);
          break;
      }
      break;

    case TREE_MENU_view:
      do_view_dbox();
      break;

    case TREE_MENU_tree:
    {
      switch (hit[1])
      {
        case TREE_MENU_tree_exbranch:
          SELECTED->go_down = TRUE;
          do_branch(SELECTED->down, TRUE);
          menu_setflags(tree_tree_menu, TREE_MENU_tree_exbranch, 0, 1);
          menu_setflags(tree_tree_menu, TREE_MENU_tree_exlevel, 0, 1);
          menu_setflags(tree_tree_menu, TREE_MENU_tree_clbranch, 0, 0);
          break;

        case TREE_MENU_tree_clbranch:
          SELECTED->go_down = FALSE;
          do_branch(SELECTED->down, FALSE);
          menu_setflags(tree_tree_menu, TREE_MENU_tree_clbranch, 0, 1);
          menu_setflags(tree_tree_menu, TREE_MENU_tree_exlevel, 0, 0);
          menu_setflags(tree_tree_menu, TREE_MENU_tree_exbranch, 0, 0);
          break;

        case TREE_MENU_tree_exall:
          do_branch(root->next, TRUE);
          break;

        case TREE_MENU_tree_exlevel:
          SELECTED->go_down = TRUE;
          menu_setflags(tree_tree_menu, TREE_MENU_tree_clbranch, 0, 0);
          menu_setflags(tree_tree_menu, TREE_MENU_tree_exlevel, 0, 1);
          break;

        case TREE_MENU_tree_clall:
          do_branch(root->next, FALSE);
          break;
      }
    }
    if (hit[1]>0)
    {
      set_new_extent();
      do_redraw();
    }
    break;
  }
}


/*--------------------------- window redraw -----------------------------*/

static BOOL should_print(int X1, int X2, int depth)
{
  int x1, x2;
  x1 = depth*XSTEP;
  x2 = depth*XSTEP + 268;

  if (x2 < X1) return (FALSE);
  if (x1 > X2) return (FALSE);
  return(TRUE);
}


static BOOL sprite_exists(char *name)
{
  os_regset r;
  r.r[0] = 40;
  r.r[2] = (int) name;
  if (os_swix(XWimp_SpriteOp, &r) != NULL) return (FALSE);
  else return (TRUE);
}


static void set_name_and_flags(wimp_icon *i, struct object *p)
{
  i->flags = wimp_ISPRITE | wimp_INDIRECT;

  switch (p->filetype)
  {
    case DIRECTORY:
      i->data.indirectsprite.name = DIR;
      break;

    case APPLICATION:
      strcpy(SNAME, "sm");
      strcat(SNAME, p->name);
      if (sprite_exists(SNAME)) i->data.indirectsprite.name = SNAME;
      else
      {
        strcpy(SNAME, p->name);
        if (sprite_exists(SNAME))
        {
          i->data.indirectsprite.name = SNAME;
          i->flags = i->flags | wimp_IHALVESPRITE;
        }
        else i->data.indirectsprite.name = APP;
      }
      break;

    default:
    {
      os_regset r;
      if (p->filetype == 0xffff) i->data.indirectsprite.name = FIL;
      else
      {
        strcpy(SNAME, "small_");
        r.r[0] = p->filetype;
        r.r[1] = (int) &SNAME[5];
        r.r[2] = 5;
        os_swi(OS_ConvertHex4, &r);
        SNAME[5] = '_';
        if (sprite_exists(SNAME)) i->data.indirectsprite.name = SNAME;
        else
        {
          strcpy(SNAME, "file_");
          r.r[0] = p->filetype;
          r.r[1] = (int) &SNAME[4];
          r.r[2] = 5;
          os_swi(OS_ConvertHex4, &r);
          SNAME[4] = '_';
          if (sprite_exists(SNAME))
          {
            i->data.indirectsprite.name = SNAME;
            i->flags = i->flags | wimp_IHALVESPRITE;
          }
          else i->data.indirectsprite.name = FIL;
        }
      }
    }
  }
}


static void lets_do_redraw(wimp_eventstr *e)
{
  wimp_redrawstr r;
  BOOL more;
  int first, last, count, x;
  struct object *p;

  r.w = e->data.o.w;
  wimp_redraw_wind(&r,&more);
  while (more)
  {
    first = ((r.g.y1-(r.box.y1-r.scy)-(YSTEP/2))/(-YSTEP))-1;
    if (first<1) first = 1;
    last = (r.g.y0-(r.box.y1-r.scy)+(YSTEP/2))/(-YSTEP);
    last++;

    p = get_nextpointer(root);
    for (count = 1; count<first; count++) p = get_nextpointer(p);
      
    for (x=first; x<=last; x++)
    {
      if (should_print(r.g.x0-(r.box.x0-r.scx), r.g.x1-(r.box.x0-r.scx), p->depth))
      {
        wimp_icon i;
        os_regset rg;
        i.box.x0 = (p->depth)*XSTEP + 48;
        i.box.y1 = x*(-YSTEP) + 12;
        i.box.y0 = i.box.y1 - 40;
        i.box.x1 = i.box.x0 + 40;
        set_name_and_flags(&i, p);
        i.data.indirectsprite.spritearea = (void *) 1;
        i.data.indirectsprite.nameisname = TRUE;
        wimp_ploticon(&i);

        if (down_FLAG)
        {
          i.box.x0 = (p->depth)*XSTEP +4;
          i.box.x1 = i.box.x0 + 40;
          i.box.y1 = (x)*(-YSTEP) + 20;
          i.box.y0 = i.box.y1 - 40;
          i.flags = wimp_ISPRITE | wimp_INDIRECT;
          i.data.indirectsprite.name = BRANCH;
          i.data.indirectsprite.spritearea = (void *) 1;
          i.data.indirectsprite.nameisname = TRUE;
          wimp_ploticon(&i);
        }
        
        rg.r[0] = 4;
        rg.r[1] = ((p->depth)*XSTEP + 100)+(r.box.x0-r.scx);
        rg.r[2] = ((-YSTEP)*x)+(r.box.y1-r.scy);
        os_swi(69, &rg);
        if (p == SELECTED) wimp_setcolour(11);
        else wimp_setcolour(7);
        rg.r[0] = (int) p->name;
        rg.r[1] = strlen(p->name);
        os_swi(70, &rg);
      }
      p = get_nextpointer(p);
      if (p == NULL) break;
    }
    wimp_get_rectangle(&r, &more);
  }
}


/*---------------------------------- window -----------------------------*/

static void do_redraw(void)
{
  wimp_wstate w;

  wimp_get_wind_state(tree_win_handle, &w);
  w.o.w = -1;
  wimp_force_redraw((wimp_redrawstr *) &w.o);
}


static void set_new_extent(void)
{
  int count = 1, x, y, maxdepth = 0;
  struct object *p = get_nextpointer(root);
  wimp_wstate r;

  while ((p = get_nextpointer(p)) != NULL)
  {
    count++;
    if (p->depth > maxdepth) maxdepth = p->depth;
  }
  x = maxdepth*XSTEP + 300;
  y = count*YSTEP + 100;
                 /* don't set new extent to less than window size */
  wimp_get_wind_state(tree_win_handle, &r);
  if (x < (r.o.box.x1 - r.o.box.x0)) x = (r.o.box.x1 - r.o.box.x0);
  if (y < (r.o.box.y1 - r.o.box.y0)) y = (r.o.box.y1 - r.o.box.y0);

  r.o.box.x0 = 0;
  r.o.box.x1 = x;
  r.o.box.y0 = -y;
  r.o.box.y1 = 0;

  wimp_set_extent((wimp_redrawstr *) &r.o);
}


static void open_tree_window(void)
{
  wimp_wstate state;
  char string[256];

  strcpy(string, "Tree '");
  strcat(string, ROOTSTR);
  strcat(string, "'");
  win_settitle(tree_win_handle, string);

  if (wimpt_complain(wimp_get_wind_state(tree_win_handle, &state)) == 0)
    {
      set_new_extent();

      state.o.behind = -1;
      wimpt_noerr(wimp_open_wind(&state.o));
  
      window_open = TRUE;   
    }
}


static BOOL is_key_pressed(int key)
{
  os_byte(121, &key, NULL);
  if (key == 0xff) return TRUE;
  else return FALSE;
}


static void click_click(wimp_eventstr *e)
{
  struct object *old = SELECTED;
  BOOL shift, ctrl, alt;

  wimp_wstate w;
  wimpt_noerr(wimp_get_wind_state(tree_win_handle, &w));
  SELECTED = get_pointer_number(-((e->data.but.m.y) - (w.o.box.y1) + (w.o.y))/YSTEP);

  if (SELECTED == NULL)
  {
    menu_setflags(tree_menu, TREE_MENU_select, 0, 1);
    menu_setflags(tree_tree_menu, TREE_MENU_tree_exbranch, 0, 1);
    menu_setflags(tree_tree_menu, TREE_MENU_tree_exlevel, 0, 1);
    menu_setflags(tree_tree_menu, TREE_MENU_tree_clbranch, 0, 1);

    if (old->filetype == APPLICATION ||
        old->filetype == DIRECTORY)
    {
      if (auto_opendir == 1) do_dir(old, FALSE);
      if (auto_exbranch == 1 && old->go_down == TRUE)
      {
        old->go_down = FALSE;
        do_branch(old->down, FALSE);
      }
    }
    do_redraw();
    return;
  }

  shift = is_key_pressed(0x80);
  ctrl = is_key_pressed(0x81);
  alt = is_key_pressed(0x82);
    
  switch(e->data.but.m.bbits & 0xfff)
  {
    case wimp_BCLICKRIGHT:
      if (SELECTED == old)
      {
        menu_setflags(tree_menu, TREE_MENU_select, 0, 1);
        menu_setflags(tree_tree_menu, TREE_MENU_tree_exbranch, 0, 1);
        menu_setflags(tree_tree_menu, TREE_MENU_tree_exlevel, 0,1);
        menu_setflags(tree_tree_menu, TREE_MENU_tree_clbranch, 0, 1);

        if (SELECTED->filetype == APPLICATION ||
            SELECTED->filetype == DIRECTORY)
        {
          if (auto_opendir == 1) do_dir(SELECTED, FALSE);
          if (auto_exbranch == 1 && SELECTED->go_down == TRUE)
          {
            SELECTED->go_down = FALSE;
            do_branch(SELECTED->down, FALSE);
          }
        }
              
        SELECTED = NULL;
        do_redraw();
        break;
      }          /* if SELECTED != old then carry on and treat as a
                          select click */

    case wimp_BCLICKLEFT:
      if (SELECTED == old) break;

      /* Set menu flags as appropriate */
      if (old == NULL) menu_setflags(tree_menu, TREE_MENU_select, 0, 0);
      if (SELECTED->filetype == APPLICATION |
          SELECTED->filetype == DIRECTORY)
      {
        menu_setflags(select_menu, TREE_MENU_select_open, 0, 0);
        if (SELECTED->go_down)
        {
          menu_setflags(tree_tree_menu, TREE_MENU_tree_exbranch, 0, 1);
          menu_setflags(tree_tree_menu, TREE_MENU_tree_exlevel, 0, 1);
          menu_setflags(tree_tree_menu, TREE_MENU_tree_clbranch, 0, 0);
        }
        else
        {
          menu_setflags(tree_tree_menu, TREE_MENU_tree_exbranch, 0, 0);
          menu_setflags(tree_tree_menu, TREE_MENU_tree_exlevel, 0, 0);
          menu_setflags(tree_tree_menu, TREE_MENU_tree_clbranch, 0, 1);
        }
      }
      else menu_setflags(select_menu, TREE_MENU_select_open, 0, 1);

      if (SELECTED->filetype == DIRECTORY)
        menu_setflags(select_menu, TREE_MENU_select_run, 0, 1);
      else
        menu_setflags(select_menu, TREE_MENU_select_run, 0, 0);

      /* carry out automatic action, if any */
      if (auto_opendir == 1)
      {
        if (SELECTED->filetype == APPLICATION ||
            SELECTED->filetype == DIRECTORY)
        {
          if (old != NULL) do_dir(old, FALSE);
          do_dir(SELECTED, TRUE);
        }
      }

      if (auto_exbranch == 1)
      {
        if (SELECTED->filetype == APPLICATION ||
            SELECTED->filetype == DIRECTORY)
        {
          if (!SELECTED->go_down)
          {
            SELECTED->go_down = TRUE;
            do_branch(SELECTED->down, TRUE);
            set_new_extent();
            menu_setflags(tree_tree_menu, TREE_MENU_tree_exbranch,0,1);
            menu_setflags(tree_tree_menu, TREE_MENU_tree_exlevel,0,1);
            menu_setflags(tree_tree_menu, TREE_MENU_tree_clbranch,0,0);
          }
        }
      }
      do_redraw();
      break;

    case wimp_BLEFT:  /* double click */
    case wimp_BRIGHT:
      switch(SELECTED->filetype)
      {
        case DIRECTORY:
          if (!shift && !ctrl && !alt)
          {
            do_dir(SELECTED, TRUE);
            if ((e->data.but.m.bbits & 0xfff) == wimp_BRIGHT)
            {
              wimpt_noerr(wimp_close_wind(tree_win_handle));
              window_open = FALSE;
            }
          }
          if (alt && SELECTED->go_down == FALSE)
          {
            SELECTED->go_down = TRUE;
            do_branch(SELECTED->down, TRUE);
            set_new_extent();
          }
          if (ctrl && SELECTED->go_down == TRUE)
          {
            SELECTED->go_down = FALSE;
            do_branch(SELECTED->down, FALSE);
            set_new_extent();
          }
          if (shift && SELECTED->go_down == FALSE)
          {
            SELECTED->go_down = TRUE;
            set_new_extent();
          }
          break;

        case APPLICATION:
          if (!shift && !alt && !ctrl)
          {
            run_object(SELECTED);
            if ((e->data.but.m.bbits & 0xfff) == wimp_BRIGHT)
            {
              wimpt_noerr(wimp_close_wind(tree_win_handle));
              window_open = FALSE;
            }
          }
          if (shift) do_dir(SELECTED, TRUE);
          if (alt && SELECTED->go_down == FALSE)
          {
            SELECTED->go_down = TRUE;
            do_branch(SELECTED->down, TRUE);
            set_new_extent();
          }
          if (ctrl && SELECTED->go_down == TRUE)
          {
            SELECTED->go_down = FALSE;
            do_branch(SELECTED->down, FALSE);
            set_new_extent();
          }
          break;

        default:
          run_object(SELECTED);
          break;
      }
      SELECTED = NULL;
      menu_setflags(tree_menu, TREE_MENU_select, 0, 1);
      menu_setflags(tree_tree_menu, TREE_MENU_tree_exbranch, 0, 1);
      menu_setflags(tree_tree_menu, TREE_MENU_tree_exlevel, 0, 1);
      menu_setflags(tree_tree_menu, TREE_MENU_tree_clbranch, 0, 1);
      do_redraw();
      break;
  }
}
      

static void tree_win_handler(wimp_eventstr *e, void *handle)
{
  handle = handle;

  switch (e->e)
  {
    case wimp_EREDRAW:
      lets_do_redraw(e);
      break;

    case wimp_EOPEN:
      wimpt_noerr(wimp_open_wind(&e->data.o));
      break;

    case wimp_ECLOSE:
      wimpt_noerr(wimp_close_wind(e->data.o.w));
      window_open = FALSE;
      break;

    case wimp_EBUT:
      click_click(e);
      break;
  }
}


/*---------------------------------- icon bar ---------------------------*/

static void iconbar_click(wimp_i icon)
{
  icon = icon;

  if (!window_open)
  {
    if (!DATA_LOADED)
    {
      visdelay_begin();
      if (SCSI_drives>0)
      {
        if (set_root(SCSI_default_drive, SCSI_string))
          root->next = get_list(ROOTSTR,NULL,0);
      }
      else
      {
        if (IDE_drives>0)
        {
          if (set_root(IDE_default_drive, IDE_string))
            root->next = get_list(ROOTSTR,NULL,0);
        }
        else
        {
          if (set_root(ADFS_default_drive, ADFS_string))
            root->next = get_list(ROOTSTR,NULL,0);
        }
      }
      visdelay_end();
    }
    if (DATA_LOADED) open_tree_window();
  }
}


/*------------------------------ initialise --------------------------------*/

static BOOL create_window(char *name, wimp_w *handle)
{
  wimp_wind *window;

  window = template_syshandle(name);
  if (window == 0) return FALSE;

  return (wimpt_complain(wimp_create_wind(window, handle)) == 0);
}


static BOOL menu_init(void)
{
  /* Icon bar menu */

  char string[256];
  char buffer[4];
  os_regset r;
  int x;

  buffer[0] = ':';
  buffer[2] = ',';
  buffer[3] = 0;

  bar_menu = menu_new("Tree", ">Info...,Read tree,Save settings,Quit");
  if (bar_menu == NULL) return FALSE;

                                             /* make 'root' submenu */
  root_menu = menu_new("Root","DUMMY,RAMFS,ADFS,SCSI,IDE");
  if (root_menu == NULL) return FALSE;

                                               /* IDE menu */
  if (file_system_present(IDEFS_name))
  {
    string[0] = 0;

    os_swi(IDEFS_Drives, &r);
    IDE_default_drive = r.r[0];
    IDE_drives = r.r[2];

    if (IDE_drives>0)
    {
      for (x=0; x<IDE_drives; x++)
      {
        buffer[1] = 52+x;
        strcat(string, buffer);                                                       }

      string[IDE_drives*3-1] = 0;
      IDE_menu = menu_new("IDE drives",string);
      if (IDE_menu == NULL) return FALSE;
      menu_submenu(root_menu, 5, IDE_menu);
    }
    else
    {
      menu_setflags(root_menu,5,0,1);
    }
  }
  else
  {
    IDE_drives = 0;
    IDE_default_drive = 0;
    menu_setflags(root_menu,5,0,1);
  }

                                  /* scuzzy menu */

  if (file_system_present(SCSIFS_name))
  {
    string[0] = 0;

    os_swi(SCSIFS_Drives, &r);
    SCSI_default_drive = r.r[0];
    SCSI_drives = r.r[2];

    if (SCSI_drives>0)
    {
      for (x=0; x<SCSI_drives; x++)
      {
        buffer[1] = 52+x;
        strcat(string, buffer);                                                       }

      string[SCSI_drives*3-1] = 0;
      SCSI_menu = menu_new("SCSI drives",string);
      if (SCSI_menu == NULL) return FALSE;
      menu_submenu(root_menu, 4, SCSI_menu);
    }
    else
    {
      menu_setflags(root_menu,4,0,1);
    }
  }
  else
  {
    SCSI_drives = 0;
    SCSI_default_drive = 0;
    menu_setflags(root_menu,4,0,1);
  }

                                     /* adfs submenu */
  string[0] = 0;

  os_swi(ADFS_Drives, &r);
  ADFS_default_drive = r.r[0];
  ADFS_floppies = r.r[1];
  ADFS_hard_drives = r.r[2];

  if (ADFS_floppies == 0 && ADFS_hard_drives)
  {
    menu_setflags(root_menu,3,0,1);
  }
  else
  {
    for (x=0; x<ADFS_floppies; x++)
    {
      buffer[1] = 48+x;
      strcat(string, buffer);
    }
    for (x=0; x<ADFS_hard_drives; x++)
    {
      buffer[1] = 52+x;
      strcat(string, buffer);
    }

    string[(ADFS_hard_drives+ADFS_floppies)*3-1] = 0;
    ADFS_menu = menu_new("ADFS drives",string);
    if (ADFS_menu == NULL) return FALSE;
    menu_submenu(root_menu, 3, ADFS_menu);
  }

  menu_submenu(bar_menu, 2, root_menu);
  menu_make_writeable(root_menu, 1, DEVICE, 256, NULL);


  /* Tree window menu */

  tree_menu = menu_new("Tree", ">Options...,~Select,>View...,Tree,Update,Open root");
  if (tree_menu == NULL) return FALSE;

  tree_tree_menu = menu_new("Tree", "~Expand branch,~Expand next level,~Close branch|Expand all,Close all");
  if (tree_tree_menu == NULL) return FALSE;

  select_menu = menu_new("Select","Open,Run");
  if (select_menu == NULL) return FALSE;

  menu_submenu(tree_menu, 4, tree_tree_menu);
  menu_submenu(tree_menu, 2, select_menu);

  return TRUE;  /* Everything a-ok, red leader */
}


static int get_swi_number(char *FSname)
{
  char buffer[20];
  os_regset r;

  strcpy(buffer,FSname);
  strcat(buffer,"_Drives");

  r.r[1] = (int) buffer;

  os_swix(XOS_SWINumberFromString,&r);

  return (r.r[0]);
}


static BOOL initialise(void)
{
  wimpt_init("Tree");
  res_init("Tree");
  resspr_init();
  template_init();
  dbox_init();
  flex_init();
  heap_init(TRUE);  /* TRUE => reduce heap size after
                       heap_free() if possible */
  visdelay_init();

  ADFS_Drives = get_swi_number(ADFS_name);  /* get xxx_Drives swi number */
  SCSIFS_Drives = get_swi_number(SCSIFS_name);
  IDEFS_Drives = get_swi_number(IDEFS_name);

  if (!menu_init()) return FALSE;

  baricon("!tree", (int)resspr_area(), iconbar_click);
  if (!event_attachmenu(win_ICONBAR, bar_menu, bar_menu_proc, 0))
    return FALSE;

  if (!create_window("tree", &tree_win_handle))
    return FALSE;
  win_register_event_handler(tree_win_handle, tree_win_handler, 0);
  if (!event_attachmenu(tree_win_handle, tree_menu, tree_menu_proc, 0))
    return FALSE;

  root = (struct object *) heap_alloc(sizeof(struct object));

  do_settings(FALSE);       /* Load settings */

  return TRUE;   /* Everything's ok */
}


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

int main()
{
  if (initialise())
    while (TRUE) event_process();
  return 0;
}
