
/***************************************************************************
 *   Copyright (C) 1997 to 2004 by Jonathan Duddington                     *
 *   email: jonsd@users.sourceforge.net                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write see:                           *
 *               <http://www.gnu.org/licenses/>.                           *
 ***************************************************************************/

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

#include "menu.h"
#include "event.h"
#include "template.h"
#include "win.h"
#include "wimp.h"
#include "dbox.h"
#include "werr.h"
#include "wimpt.h"
#include "os.h"
#include "bbc.h"
#include "xferrecv.h"
#include "akbd.h"

#include "narc.h"
#include "hdrs.h"

extern char *status_sprites;


extern void addrlist_display_window(BLIST *b,wimp_redrawstr *r,int line,int end_line,int x,int y);
extern void addrlist_click_window(BLIST *b,int linenum,int bbits);
extern void addrlist_key_window(BLIST *b,wimp_eventdata *d);
extern void addrlist_click_buttons(BLIST *b, wimp_eventdata *d);
extern void addrlist_import(char *fname,int filetype);

extern void kill_display_window(BLIST *b,wimp_redrawstr *r,int line,int end_line,int x,int y);
extern void kill_click_window(BLIST *b,int linenum,int bbits);
extern void kill_key_window(BLIST *b,wimp_eventdata *d);
extern void kill_click_buttons(BLIST *b, wimp_eventdata *d);

extern void newsg_display_window(BLIST *b,wimp_redrawstr *r,int line,int end_line,int x,int y);
extern void newsg_click_buttons(BLIST *b, wimp_eventdata *d);
extern void newsg_click_window(BLIST *b,int linenum,int bbits);
extern void newsg_key_window(BLIST *b,wimp_eventdata *d);


extern void postedlist_alarm_handler(int called_at, void *handle);
extern void postedlist_click_window(BLIST *b,int linenum,int bbits);
extern void postedlist_key_window(BLIST *b,wimp_eventdata *d);
extern void postedlist_display_window(BLIST *b,wimp_redrawstr *r,int line,int end_line,int x,int y);
extern void postedlist_click_buttons(BLIST *b, wimp_eventdata *d);


extern OPTIONS options;
extern CARDFILE_REC cardfile[N_CARDFILES];
extern int cardfile_changed[N_BOXES_EXTERN+1];

extern int y_screen_dim;   /* screen height for current mode */
extern int status_sprite_offset;
extern int scroll_bar_width;
extern int scroll_bar_height;
extern char mbox_list[];
extern int options_save_flag;
extern int null_event_map;

extern FOLDREC list_fr[N_LISTS];
extern int lists_line_height;
extern int lists_font_offset;
extern int standard_font;
extern int scale_x, scale_y;   /* font scale factors */

menu menu_boxes_names=0;
wimp_menustr *wmenu_boxes_names=NULL;
DISPLAY display_tab[N_DISPLAY_TAB];

extern char *path_choices;
extern TEXTR *text_view;
extern dbox d_spell;
static BLIST *blist_reg[BLIST_MAX+1];  /* list of registered blists */
menu blist_current_menu = NULL;
int blist_current_menu_line = 0;
int blist_current_menu_blist = 0;

static int  n_boxes_list = 0;
static int  n_boxes_list2 = 0;
int  boxes_hidden = 1;
char boxes_list[N_BOXES];   /* the order in which they appear on screen & menus */
static char boxes_list2[N_BOXES];   /* screen order, not including "hideen" boxes */

int box_number;
static menu menu_boxlist;
static menu menu_boxnews;

static int newsg_menu_type;
static int newsg_menu_extra;

int box_guard = 0x1234;
BOX box_table[N_BOXES];
char box_selected[N_BOXES];
char box_password_ok[N_BOXES];

char boxes_unread[N_BOXES];  /* highest priority status for this box */
int  boxes_count[N_BOXES];
int  boxes_unread_count[N_BOXES];

static int  boxlist_changed = 0;
static int box_selected_by_menu = -1;
int mbox_selected = 0;
int password_entered=0;

static int drag_last_line=0;
static int drag_direction=0;

/* type, x, y, title, butbar_height, width, n_entries */
BLIST boxlist = {BLIST_BOXES,8,1500,NULL,80,582,0,HELP_BOXLIST};
BLIST mboxlist = {BLIST_MBOXES,8,350,NULL,80,800,N_MAIL_BOX,HELP_USERLIST};
BLIST newsglist = {BLIST_NEWSG,100,500,NULL,80,800,0,HELP_NGLIST};
BLIST mlistlist = {BLIST_MLIST,100,500,NULL,80,800,0,HELP_NGLIST};
BLIST pgplist = {BLIST_PGP,100,500,NULL,80,820,0,HELP_PGP};
BLIST postedlist = {BLIST_POSTED,100,800,0,80,960,0,HELP_POSTLIST};

#ifdef deleted
static char *menustr_boxlist = "Write mail ^W,Write news,Select all ^A,Open selection,Edit details of box,New box,Debatch mail,Debatch news,Show outgoing";
#endif

static char *menustr_boxlist = ">Info ^I,Select all ^A,Open selection,Mail & News,Edit details of box,New box,Close external box,Compact external box";
static char *menustr_boxnews = "Write mail ^W,Write news,Debatch mail,Debatch news,Show outgoing";

static char *menustr_mboxlist = "Edit user,Add user";

static char *bin_box_name[] = {
	"Bin","(none)","(log box)","(discard)"
};   /* 4 values only */



int select_user_flag = 0;







int category_lookup2(FOLDREC *fr, char **parts, CAT *cptr)
/********************************************************/
/* Match parts of a category 'path' with the dependents of a category */
{
	int  cat;

	cat = cptr->catnum;

	while(*parts != NULL)
	{
		cat = cptr->down;

		while(cat > 0)
		{
			cptr = &fr->cat_data[cat];
			if(strcmp_lc(*parts,&fr->cat_chars[cptr->name])==0)
			{
				break;
			}

			cat = cptr->right;
		}
		if(cat == 0)
			return(0);   /* not found */

		parts++;
	}

	return(cat);
}   /* end of category_lookup2 */




int category_lookup(FOLDREC *fr,const char *name, int check)
/*****************************************************/
/* Category name to number.  Can have preceding 'path'
   eg. one/two/three
   Only least sig. parts of path are needed if not ambiguous
   'check' = 1 to fail ambiguous names
*/
{
	char *p;
	int  cat;
	CAT *cptr;
	int  n_parts;
	int  found;
	char *parts[16];
	char name_lc[200];

	while(*name == '/') name++;   /* skip leading '/' */

	/* convert to lower case and split into parts, separated by '/' */
	p = name_lc;
	parts[0] = p;
	n_parts = 1;
	while((p[0] = tolower(*name++)) > ' ')
	{
		if(p[0] == '/')
		{
			p[0] = 0;    /* string terminator */
			parts[n_parts++] = p+1;
		}
		p++;
	}
	*p = 0;   /* string terminator (writing back to original copy of article though) */
	parts[n_parts] = NULL;

	found = 0;

	/* lookup 1st part */
	for(cptr = &fr->cat_data[1]; cptr < &fr->cat_data[fr->max_cats]; cptr++)
	{
		if(strcmp_lc(parts[0],&fr->cat_chars[cptr->name]) == 0)
		{
			cat = category_lookup2(fr,&parts[1],cptr);   /* check subsequent parts */
			if(cat > 0)
			{
				/* subsequent parts match OK */
				if((found > 0) && (check != 0))
					return(-1);    /* already found a match, so it's ambiguous */

				found = cat;
			}
		}
	}
	return(found);
}   /* end of category_lookup */




char *category_name(FOLDREC *fr,int cat)
/**************************************/
/* Translate category number to name */
{
	int  i;
	static char buf[12];

	if(cat==0)
		return("");

	if((cat >= fr->max_cats) || (fr->cat_data[cat].catnum == 0))
	{
		sprintf(buf,"*%d",cat);
		return(buf);
	}

	i = fr->cat_data[cat].name;
	return(&fr->cat_chars[i]);
}   /* end of category_name */












int box_password_check(int box, int ask)
/**************************************/
/* Returns:  0 failed,  1 OK,  2 special password to gain entry to edit dbox */
{
	FILE *f=NULL;
	BOX *b;
	ART_FILE_HEADER header;


	if((box > BOX_BIN) || (box_password_ok[box] == 1))
		return(1);

	b = &box_table[box];

	if(box_to_cf(box)==0)
	{
		/* don't check with password copy in article file for external
		   boxes because they can open ino different box numbers */
		f = article_fopen(0,box & 0x1f,"r");
	}

	if(f == NULL)
	{
		header.box_password[box >> 5] = b->box_password;
	}
	else
	{
		fread(&header,sizeof(header),1,f);
		fclose(f);
	}

	if((b->box_password == 0) && (header.box_password[box >> 5] == 0))
	{
		/* no password as been set */
		box_password_ok[box] = 1;
		return(1);
	}

	if(ask==0)
		return(0);

	return(box_password_ask(box,&header));
}   /* end of box_password_check */





void redraw_boxes_list(int box)
/**************************/
{
	int  line;

	if(box >= 0)
	{
		for(line=0; line<boxlist.n_entries; line++)
		{
			if(boxes_list2[line] == box)
			{
				redraw_blist_line(&boxlist,line);
				return;
			}
		}
	}

	redraw_blist(&boxlist);
}   /* end of redraw_boxes_list*/




void set_window_position(wimp_w w, int ix, int offset)
/****************************************************/
{
	wimp_wstate wstate;

	wimp_get_wind_state(w,&wstate);
	options.position_x[ix] = wstate.o.box.x0;
	options.position_y[ix] = wstate.o.box.y1+offset;
	options.position_y0[ix] = wstate.o.box.y0;
	if(ix < 4)
		options.position_width[ix] = wstate.o.box.x1 - wstate.o.box.x0;

}   /* set_window_position */





void set_window_positions()
/*************************/
{
	int  ix;
	TEXTR *t;
	for(ix=1; ix<=BLIST_MAX; ix++)
	{
		if(blist_reg[ix]->open)
			set_window_position(blist_reg[ix]->w_buttons,ix+4,0);
	}

	set_window_position(text_view->w_text,0,text_view->button_bar_height);

	/* spell window */
	if(d_spell != NULL)
		set_window_position(dbox_syshandle(d_spell),4,0);

	/* find write mail/news record */
	t = reply_data_find(0);
	if(t != NULL)
	{
		set_window_position(t->w_text,1,0);
	}
	else if(options.position_y[1] == 0)
	{
		options.position_y[1] = 8000;  /* not set, top of screen */
	}

	/* article list */
	for(ix=1; ix<N_LISTS; ix++)
	{
		if(list_fr[ix].window != NULL)
		{
			set_window_position(list_fr[ix].window,3,0);
			break;
		}
	}

	options_save();
}   /* end of set_window_positions */





void blist_adjust_selected_count(BLIST *b, int selected)
/******************************************************/
{
	if(selected)
		b->n_selected--;
	else
		b->n_selected++;
}   /* end of blist_adjust_selected_count */






char *get_box_name(int boxno)
/***************************/
{
	if(boxno == BOX_BIN)
		return("Bin");

	if((boxno < 0) || (boxno > BOX_BIN))
		return("");

	return(box_table[boxno].name);
}   /* end of get_box_name */




char *get_box_name2(int box, int type)
/************************************/
{
	if(box == BOX_BIN)
		return(bin_box_name[type]);
	else
		return(get_box_name(box));
}   /* end of get_box_name2 */





int get_box_number(char *name)
/****************************/
{
	int  i;

	if(name[0] == 0)
		return(-1);

	for(i=0; i<BOX_BIN; i++)
	{
		if(strcmp_lc(box_table[i].name,name)==0)
			return(i);
	}
	return(-1);
}   /* end of get_box_number */




void boxlist_set_positions()
/**************************/
{
	int  i;

	for(i=0; i<(boxlist.n_entries-1); i++)
	{
		if(box_table[boxes_list[i]].position != i)
		{
			boxlist_changed = 0x6398;
			box_table[boxes_list[i]].position = i;
		}
	}
}   /* end of boxlist_set_positions */





void make_boxes_list2()
/*********************/
/* Make list of boxes which are not "hidden" */
{
	int  i;
	int  j;
	int  box;

	if(boxes_hidden == 0)
	{
		memcpy(boxes_list2,boxes_list,sizeof(boxes_list));
		n_boxes_list2 = n_boxes_list;
		return;
	}

	for(i=0, j=0; i<boxlist.n_entries; i++)
	{
		/* never hide external boxes */
		box = boxes_list[i];
		if((box_table[box].hide_box == 0) || ((box>=BOX_EXTERN) && (box != BOX_BIN)) ||
				((box_table[box].hide_box == 2) && (boxes_unread_count[box] > 0)) ||
				((box_table[box].hide_box == 3) && (boxes_count[box] > 0)))

		{
			boxes_list2[j++] = box;
		}
	}
	n_boxes_list2 = j;
	boxlist.n_entries = n_boxes_list2;
}   /* end of make_boxes_list2 */








void make_boxes_list()
/********************/
{
	int  box;
	int  posn;
	int  min;
	int  min_pos;
	char box_done[N_BOXES];

	memset(box_done,0,sizeof(box_done));

	for(posn=0; posn<N_BOXES; posn++)
	{
		min = 255;

		for(box=0; box<BOX_BIN; box++)
		{
			if(box_table[box].name[0] != 0)
			{
				if((box_table[box].position <= min) && (box_done[box]==0))
				{
					min = box_table[box].position;
					min_pos = box;
				}
			}
		}
		if(min < 255)
		{
			boxes_list[posn] = min_pos;
			box_done[min_pos] = 1;
		}
		else
		{
			break;   /* dealt with all the boxes */
		}
	}

	n_boxes_list = posn;
	boxes_list[n_boxes_list] = BOX_BIN;  /* add BIN to the end */

	boxlist.n_entries = n_boxes_list+1;

	boxlist_set_positions();

	make_boxes_list2();
}   /* end of make_boxes_list */





void boxlist_recalc()
/*******************/
/* Find the unread status of the boxes */
{
	int  status;
	int  status2;
	int  box;
	CARD *cptr;
	unsigned int *ixlist;
	int  ix;
	FOLDREC *fr;

	fr = &list_fr[0];

	for(ix=0; ix<N_BOXES; ix++)
	{
		boxes_unread[ix] = 0x7f;
		boxes_count[ix] = 0;
		boxes_unread_count[ix]=0;
	}

	ixlist = fr->ixlist;
	for(ix=0; ix<fr->n_entries; ix++)
	{
		cptr = (CARD *)((ixlist[ix] & IXLIST_MASK) << 2);
		box = cptr->date_box >> 26;


		if((status2 = (cptr->status & STATUS_MASK2)) == STATUS_FETCHING)
			status = STATUS_READ;
		else
			status = status2 & STATUS_MASK;

		if(status < boxes_unread[box])
		{
			boxes_unread[box] = status;
		}
		if(status2 != STATUS_HIDDEN)
		{
			if(status <= STATUS_UNREAD)
			{
				boxes_unread_count[box]++;
			}
			boxes_count[box]++;
		}
	}
}   /* end of boxlist_recalc */





void set_boxlist_extent(int open)
/*******************************/
{
	if(open & 2)
	{
		/* calculate totals, do this first then remake list of non-hidden boxes */
		boxlist_recalc();
		make_boxes_list();

#ifdef deleted
		if(open & 1)
			blist_set_extent(&boxlist,open & ~2);   /* open boxlist window */
		else
			redraw_boxes_list(-1);
#endif

		blist_set_extent(&boxlist,open & ~2);

	}
	else
	{
		blist_set_extent(&boxlist,open);

	}

	if(open & 0x20)
		postedlist_alarm_handler(0,NULL);
}   /* end of set_boxlist_extent */





int box_lookup_number(int number)
/****************************/
/* Look up a number in the boxes list */
{
	return(boxes_list[number]);
}   /* end of box_lookup_number */





void save_boxes_file(void)
/************************/
{
	FILE *f;
	int  version;
	int  box;
	char fname[256];

	if(box_guard != 0x1234)
	{
		werr(0,"Warning: Data Corruption 1");
		return;
	}
	sprintf(fname,"%s.boxes",path_choices);
	f = fopen_werr(fname,"w",NULL);

	if(f==NULL)
		return;


	version = 0x81;
	fwrite(&version,1,sizeof(int),f);

	/* save up to but not including external boxes, plus BIN box */
	fwrite(box_table,sizeof(box_table[0]),BOX_EXTERN,f);
	fwrite(&box_table[BOX_BIN],sizeof(box_table[0]),1,f);
	fclose(f);

	/* save any external boxes */
	for(box=BOX_EXTERN; box<BOX_BIN; box++)
	{
	}

	boxlist_changed = 0;
}   /* end of save_boxes_file */



int load_boxes_file()
/********************/
{
	FILE *f;
	int version;
	char fname[256];

	if(boxlist_changed == 0x6398)
		save_boxes_file();

	sprintf(fname,"%s.boxes",path_choices);
	f = fopen_werr(fname,"r",NULL);

	if(f==NULL)
	{
		return(-1);
	}

	fread(&version,1,sizeof(int),f);
	if((version < 0x81) || (version > 0xff))
	{
		rewind(f);
		fread(box_table,sizeof(box_table[0]),BOX_EXTERN,f);
		fclose(f);
		convert_boxes3();
		save_boxes_file();
		load_boxes_file();
		return(0);
	}

	fread(box_table,sizeof(box_table[0]),BOX_EXTERN,f);
	if(!feof(f))
		fread(&box_table[BOX_BIN],sizeof(box_table[0]),1,f);
	fclose(f);

	box_table[BOX_BIN].archive_to = 255;

	make_boxes_list();
	return(0);
}   /* end of load_boxes_file */



void make_boxes_menu(int control, menu *menuptr)
/**********************************************/
/* Control:
   bits 0-7   (box number)
   bit  8     don't allow the box specified by bits 0-7
   bit  9-10  replace BIN by (none) or (log box)
   bit 11     include external boxes
   bits 12-13 title
*/
{
	int  i;
	int  count=0;
	int  ignore=-1;
	char *p;
	int  colour;
	char ignore_tab[N_BOXES];

	static wimp_colour[] = {7,11,14,9,13,8,11,6,15};

	static char *title[] = {
		"Boxes","Move to Box","Copy to Box"
	};

	if(control & 0x100)
		ignore = control & 0xff;

	if(menuptr == NULL)
		menuptr = &menu_boxes_names;

	memset(ignore_tab,0,sizeof(ignore_tab));
	make_boxes_list();

	if(*menuptr != 0)
	{
		menu_dispose(menuptr,0);
	}

	*menuptr = menu_new(title[(control >> 12) & 3],"");

	for(i=0; i<=n_boxes_list; i++)
	{
		if((i<n_boxes_list) && (boxes_list[i]>=BOX_EXTERN) && ((control & 0x800)==0))
			ignore_tab[i] = 1;   /* don't include external boxes */

		p = get_box_name(boxes_list[i]);

		if((control & 0x600) && (i==(n_boxes_list)))
			p = bin_box_name[(control >> 9)];

		if(p[0] > ' ')
		{
			if(ignore == boxes_list[i])
				ignore_tab[i] = 1;
			if(*p=='!') p++;    /* this char would tick the menu */

			menu_extend(*menuptr,p);
		}
		else
		{
			menu_extend(*menuptr,"\240");  /* hard space */
			ignore_tab[i]=1;
		}
	}
	count = i;

	if(n_boxes_list<1)
	{
		menu_extend(*menuptr,"0");
	}

	for(i=0; i<count; i++)
	{
		colour = box_table[boxes_list[i]].colour;

		if(ignore_tab[i])
			menu_setflags(*menuptr,i+1,0,1);   /* fade entry */
		else if(colour!=0)
			menu_setcolour(*menuptr,i+1,wimp_colour[colour]);
	}

	wmenu_boxes_names = (wimp_menustr *)menu_syshandle(*menuptr);

}   /* end of make_boxes_menu */





void box_set_postedlist_icon(int waiting, int held)
/*************************************************/
/* Set the appropriate sprite to show any waiting or held messages */
{
	int  flags=0;
	wimp_icon *icon_ptr;
	char buf[16];

	if(waiting)
		flags = 1;
	if(held)
		flags |= 2;

	icon_ptr =  ((wimp_icon *)(boxlist.button_template + 1)) + 2;
	sprintf(buf,"Snxoutg%d;R5,3\r",flags);
	memcpy(icon_ptr->data.indirecttext.validstring,buf,14);
	wimp_set_icon_state(boxlist.w_buttons,2,wimp_INDIRECT,wimp_INDIRECT);
}   /* end of box_set_postedlist_icon */





int boxlist_adjust_count(int box, int adjust, int status)
/*******************************************************/
{
	int changed1= -1;
	int changed2= -1;

	if((status & STATUS_MASK) <= STATUS_UNREAD)
	{
		if(boxes_unread_count[box] == 0)
			changed1 = 2;

		boxes_unread_count[box] += adjust;

		if(boxes_unread_count[box] == 0)
			changed1 = 2;
	}

	if(boxes_count[box]==0)
		changed2 = 3;

	boxes_count[box] += adjust;

	if(boxes_count[box]==0)
	{
		changed2 = 3;
		boxes_unread[box] = 0x7f;  /* box empty, no status icon */
	}

	redraw_boxes_list(box);

	if((changed1 == box_table[box].hide_box) || (changed2 == box_table[box].hide_box))
		return(2);   /* recalc and redraw */
	else
		return(1);

	/* could look for special cases where we know there's no change in Box status */
}   /* end of boxlist_adjust_count */




int boxes_unread_inc(int box,int count)
/*************************************/
/* Returns 1= read/unread status of this box has changed and boxes may become hidden/unhidden */
{
	int  before;

	before = boxes_unread_count[box];

	boxes_unread_count[box] += count;

	if(boxes_unread_count[box] < 0)
	{
		boxes_unread_count[box] = 0;
	}

	if(before > 0)
	{
		if(boxes_unread_count[box] == 0)
			boxes_unread[box] = STATUS_READ;
	}
	else
	{
		if(count > 0)
			boxes_unread[box] = STATUS_UNREAD;
	}


	if((before==0) || (boxes_unread_count[box]==0))
	{
		if(box_table[box].hide_box > 1)
			return(2); /* boxes may have appeared/been hidden */
		return(1);    /* recalc counts but only redraw for this one box */
	}
	return(0);
}   /* end of boxes_unread_inc */




void subset_box_delete(char *name, int box_no)
/********************************************/
/* Delete any existing subset for this for */
{
	int  i;
	FOLDREC *fr;

	for(i=1; i<N_LISTS; i++)
	{
		fr = &list_fr[i];
		if((fr->window != NULL) && (fr->box_expansion == (box_no+1)) && (strcmp(fr->name,name)==0))
		{
			list_delete_window(fr);
		}
	}
}   /* end of subset_box_delete */




void set_user_from_box(int box)
/*****************************/
/* look for a user whose mail_in box this is */
{
	int  i;

	for(i=0; i<N_MAIL_BOX; i++)
	{
		if(options.mailbox[i].box == box)
		{
			user_select(NULL,i,0);
			mbox_selected = i;
			redraw_blist(&mboxlist);
			break;
		}
	}
}   /* end of set_user_from_box */




static void subset_box(char *box_select)
/**************************************/
/* Create subset consisting or one or more boxes */
{
	int  ix;
	int  n_entries;
	unsigned int *ixlist;
	CARD *cptr;
	int n_marked;
	int  ix2;
	int  box;
	int  also_box;
	int  also_source;
	FOLDREC *fr;
	unsigned int *ixlist2;
	int  num;
	int  box_no;
	int  sort_on;
	int  single_box;
	DISPLAY *display;
	FOLDREC *search_fr;
	char *name;
	char box_map[64];

	search_fr = &list_fr[0];

	memset(box_map,0,sizeof(box_map));
	single_box = -1;
	for(ix=0; ix<=BOX_BIN; ix++)
	{
		if(box_select[ix])
		{
			if(box_password_check(ix,1) != 1)
				return;

			box_map[box_no = ix] = 1;

			if(single_box == -1)
				single_box = box_no;
			else
				single_box = -2;   /* more than one box */
		}
	}
	if(single_box >= 0)
	{
		/*      set_user_from_box(single_box); */
	}

	ixlist = search_fr->ixlist;
	n_entries = search_fr->n_entries;

	if(single_box >= 0)
	{
		n_marked = box_selector_1(single_box,ixlist,n_entries);  /* assembler */
	}
	else
	{
		/* unmark all indices */
		n_marked = 0;

		clear_ixlist(search_fr);
		for(ix=0; ix<n_entries; ix++)
		{
			cptr = (CARD *)((ixlist[ix] & IXLIST_MASK) << 2);

			if(((cptr->status & STATUS_MASK2) != STATUS_HIDDEN) && (box_map[cptr->date_box >> 26]))
			{
				ixlist[ix] |= IXLIST_MARKED;
				n_marked++;
			}
		}
	}

	/* any external boxes with  "also open box/source"  set ? */
	for(box=BOX_EXTERN; box<BOX_BIN; box++)
	{
		if((box_table[box].name[0] == 0) || (box_select[box]==0))
			continue;


		also_box = box_table[box].expire_to;
		also_source = box_table[box].expiry_flags;

		if((also_box == BOX_BIN) && (also_source == 0))
		{
			/* neither also-Box or also-Source are set */
			continue;
		}

		/* select any articles with matching also-Box or the the also-Source,
		   or if both are specified, then matching both */
		for(ix=0; ix<n_entries; ix++)
		{
			cptr = (CARD *)((ixlist[ix] & IXLIST_MASK) << 2);
			if(((also_box == BOX_BIN) || ((cptr->date_box >> 26) == also_box))
					&& ((also_source == 0) || (cptr->source == also_source)))
			{
				if((ixlist[ix] & IXLIST_MARKED)==0)
				{
					ixlist[ix] |= IXLIST_MARKED;
					n_marked++;
				}
			}
		}
	}

	sort_on = -1;
	num = 0;
	for(ix=0; ix<N_BOXES; ix++)
	{
		if(box_map[ix])
		{
			num++;
			box_no = ix;

			if(sort_on == -1)
				sort_on = box_table[ix].sort_on;
			else if(sort_on != box_table[ix].sort_on)
				sort_on = -2;   /* boxes with different sort types */
		}
	}
	if((num == 0) && (box_map[BOX_BIN]))
	{
		box_no = BOX_BIN;
		num++;
	}

	display = search_fr->display;
	if(sort_on >= 0)
	{
		/* all the selected boxes have the same sort type. Use it */
		search_fr->display = &display_tab[sort_on];
	}

	if(num == 1)
	{
		name = get_box_name(box_no);

		subset_box_delete(name, box_no);  /* delete any existing subset with the same box name */

		fr = subset_new(name,search_fr,n_marked);   /* single box */
		if(fr == NULL)
			return;

		fr->box_expansion = box_no + 1;
		fr->box = (char)-1;
		fr->hide_read = 0;
		if(box_no < N_BOXES)
		{
			fr->box = box_no;
			fr->hide_read = box_table[box_no].hide_read;
		}
	}
	else
	{
		fr = subset_new("Article List",search_fr,n_marked);
		if(fr == NULL)
			return;
		fr->box = (char)-1;
	}
	set_icon_state(fr->window_buttons,6,fr->hide_read ^ 1);

	search_fr->display = display;

	if(fr == NULL)
	{
		return;
	}

	ixlist2 = fr->ixlist;
	ix2 = 0;

	for(ix=0; ix<search_fr->n_entries; ix++)
	{
		if(ixlist[ix] & IXLIST_MARKED)
		{
			cptr = (CARD *)((ixlist[ix] & IXLIST_MASK) << 2);

			if(ix2 >= n_marked)
				break;

			ixlist2[ix2++] = ixlist[ix];
		}
	}

	fr->n_entries = ix2;
	fr->n_entries_tot = ix2;
	fr->n_entries_max = n_marked;

	list_sort(fr,1);
}   /* end of subset_box */



void boxlist_toggle_hidden()
/**************************/
{
	boxes_hidden ^= 1;
	make_boxes_list();
	set_icon_state(boxlist.w_buttons,5,boxes_hidden ^ 1);
	set_boxlist_extent(0x43);   /* open boxes list */
}   /* end of boxlist_toggle_hidden */



void blist_close(BLIST *b)
/************************/
{
	b->open = 0;
	wimp_close_wind(b->w_list);
	wimp_close_wind(b->w_buttons);
}   /* end of blist_close */



void boxlist_click_buttons(BLIST *b, wimp_eventdata *d)
/*****************************************************/
/* Click on the box-list button bar */
{
	int  icon;
	int  click;
	int  i;
	int  c;
	int  user;
	static int debatching=0;  /* to suppress double-click */

	icon = d->but.m.i;
	click = d->but.m.bbits;

	switch(icon)
	{
	case 5:   /* toggle hidden boxes */
		boxlist_toggle_hidden();
		break;

	case 1:   /* fetch news/mail */
		if(debatching == 0)
		{
			debatching = 1;

			if(dbox_persist())
				read_raw_news(1,2);   /* mail only */
			else
				read_raw_news(3,2);   /* mail and news */
		}
		debatching = 0;
		break;

	case 2:   /* list of article waiting to be sent */
		if(dbox_persist())
			blist_close(&postedlist);
		else
			posted_open_list();
		break;

	case 3:   /* open address book */
		addrlist_open(1);
		break;

	case 4:
		/* is there a selected box? */
		user = mbox_list[0]+1;

		for(i=0; i<BOX_BIN; i++)
		{
			if(box_selected[i] && ((c = box_table[i].default_user)!=0))
			{
				user = c;
				break;
			}
		}
		if(dbox_persist())
			new_reply(NULL,NULL,"",NULL,user,NULL);
		else
			new_reply(NULL,NULL,NULL,NULL,user,NULL);
		break;
	}
}   /* end of boxlist_click_buttons */






int box_save_external(int box, int articles)
/**************************************/
/* Returns number of articles */
{
	int  cf;
	FILE *f;
	char buf[256];

	cf = box_to_cf(box);

	sprintf(buf,"%s.box",cardfile[cf].path);

	/* not fopen_werr in case it's readonly */
	if((f = fopen(buf,"w")) != NULL)
	{
		fwrite(&box_table[box],sizeof(BOX),1,f);
		fclose(f);
	}

	if(articles)
	{
		return(cardfile_save(cf,0));
	}
	return(0);
}   /* end of box_save_external */






void box_close_external(int box)
/*****************************/
{
	int  cf;

	cf = box_to_cf(box);

	if(cardfile_changed[cf])
	{
		box_save_external(box,1);
	}
	cardfile[box_to_cf(box)].path[0] = 0;

	/* remove from the boxes list */
	box_selected[box] = 0;
	box_table[box].name[0] = 0;
	make_boxes_menu(0,NULL);
	set_boxlist_extent(3);

	box_password_ok[box] = 0;

	cardfile_free(cf);
}   /* end of box_close_external */



void box_close_all_external()
/************************/
{
	int  cf;

	for(cf=1; cf<N_BOXES_EXTERN; cf++)
	{
		if(cardfile[cf].path[0] != 0)
			box_close_external(cf_to_box(cf));
	}
}   /* end of box_close_all-external */




void boxlist_select_all()
/***********************/
{
	int  i;

	memset(box_selected,0,sizeof(box_selected));
	for(i=0; i<boxlist.n_entries; i++)
	{
		box_selected[boxes_list2[i]] = 1;
	}
	boxlist.n_selected = boxlist.n_entries;
}   /* end of boxlist_select_all */




void boxlist_select(int linenum, int selected)
/**********************************************/
{
	if(selected==2)
		box_selected[boxes_list2[linenum]] ^= 1;
	else
		box_selected[boxes_list2[linenum]] = selected;
	redraw_blist_line(&boxlist,linenum);
}   /* end of boxlist_select */




void boxlist_menu_proc(void *handle, char *hits)
/**********************************************/
{
	int  box;
	int  cf;
	int  ix;
	int  count=0;
	BLIST *b;

	b = (BLIST *)handle;

	for(ix=0; ix<=BOX_BIN; ix++)
	{
		if(box_selected[ix])
		{
			box = ix;
			cf = box_to_cf(box);
			count++;
		}
	}

	switch(hits[0])
	{
	case 1:    /* box info */
		if(hits[1])
		{
			if((count==1) && (cf > 0))
			{
				artfile_statistics(cf);
			}
			else
			{
				artfile_statistics(-1);
			}
			return;
		}
		break;;

	case 2:    /* select all */
		boxlist_select_all();
		redraw_blist(b);
		return;       /* don't de-select menu-click line */

	case 3:      /* open selection */
		subset_box(box_selected);
		break;

	case 4:      /* mail and news */
		switch(hits[1])
		{
		case 1:      /* open Write Mail window */
			new_reply(NULL,NULL,NULL,NULL,0,NULL);
			break;

		case 2:     /* open Write News window */
			new_reply(NULL,NULL,"",NULL,0,NULL);
			break;

		case 3:      /* fetch raw mail */
			read_raw_news(1,2);
			break;

		case 4:      /* fetch raw news */
			read_raw_news(2,2);
			break;

		case 5:      /* show outgoing messages */
			posted_open_list();
			break;
		}
		break;

	case 5:      /* edit box */
		boxes_edit(box);
		break;

	case 6:      /* add new box */
		box_new();
		break;

	case 7:    /* close external */
		for(ix=BOX_EXTERN; ix<BOX_BIN; ix++)
		{
			if(box_selected[ix])
				box_close_external(ix);
		}
		break;

	case 8:   /* compact external */
		for(ix=BOX_EXTERN; ix<BOX_BIN; ix++)
		{
			if(box_selected[ix])
				article_files_repair(box_to_cf(ix),0);
		}
		break;
	}
	if(blist_current_menu == menu_boxlist)
	{
		boxlist_select(blist_current_menu_line,0);
	}
}   /* end of boxlist_menu_proc */




menu boxlist_menu_maker(void *handle)
/***********************************/
{
	int  ix;
	int  fade;
	int  count=0;
	int  count_ext=0;
	int  linenum;

	blist_current_menu = NULL;

	for(ix=0; ix<N_BOXES - 1; ix++)
	{
		if(box_selected[ix])
		{
			count++;

			if((ix>=BOX_EXTERN && ix<BOX_BIN))
				count_ext++;
		}
	}

	box_selected_by_menu = -1;

	if(count == 0)
	{
		/* no items selected, menu button selects one */

		if((linenum = list_get_linenum(boxlist.w_list)) < boxlist.n_entries)
		{
			box_selected[box_selected_by_menu = boxes_list2[linenum]] = 1;
			if(box_to_cf(box_selected_by_menu) > 0)
				count_ext=1;
			count = 1;
			redraw_blist_line(&boxlist,linenum);

			blist_current_menu = menu_boxlist;
			blist_current_menu_line = linenum;
			blist_current_menu_blist = BLIST_BOXES;
		}
	}

	if(count > 0)
		fade=0;
	else
		fade=1;
	menu_setflags(menu_boxlist,3,0,fade);

	if(count==1)
		fade=0;
	else
		fade=1;
	menu_setflags(menu_boxlist,5,0,fade);

	if(count_ext>0)
		fade=0;
	else
		fade=1;
	menu_setflags(menu_boxlist,7,0,fade);
	menu_setflags(menu_boxlist,8,0,fade);

	return(menu_boxlist);
}   /* end of boxlist_menu_maker */





void boxlist_click_window(BLIST *b,int linenum,int bbits)
/*******************************************************/
{
	int  i;
	int  box;

	box = boxes_list2[linenum];


	switch(bbits)
	{
	case wimp_BCLICKLEFT:    /* select */
		for(i=0; i<N_BOXES; i++)
		{
			if(box_selected[i] != 0)
				redraw_boxes_list(i);
		}
		memset(box_selected,0,sizeof(box_selected));
		box_selected[box] = 1;
		b->n_selected = 1;
		redraw_boxes_list(box);
		break;

	case wimp_BCLICKRIGHT:   /* adjust */
		blist_adjust_selected_count(b,box_selected[box]);
		box_selected[box] ^= 1;   /* toggle */
		redraw_boxes_list(box);
		break;

	case wimp_BLEFT:   /* double click */
		subset_box(box_selected);
		memset(box_selected,0,sizeof(box_selected));
		redraw_boxes_list(box);
		break;

	case wimp_BRIGHT:        /* adjust, 2nd click */
		if(linenum >= BOX_BIN)
		{
			beep();
		}
		else
		{
			memset(box_selected,0,sizeof(box_selected));
			b->n_selected = 0;
			boxes_edit(box);
			redraw_boxes_list(-1);
		}
		break;
	}
}   /* end of boxlist_click_window */




void boxlist_key_window(BLIST *b,wimp_eventdata *d)
/*************************************************/
{
	int  c;
	int  ix;
	int  count;
	int  cf;

	switch(c = d->key.chcode)
	{

	case KEY_SH_UP:
		if(akbd_pollsh())
			boxlist_shift(b,-1);
		break;

	case KEY_SH_DOWN:
		if(akbd_pollsh())
			boxlist_shift(b,1);
		break;

	case 1:   /* CTRL-A, select all */
		boxlist_select_all();
		redraw_blist(b);
		break;

	case 9:   /* CTRL-I, info */
		count = 0;
		for(ix=0; ix<=BOX_BIN; ix++)
		{
			if(box_selected[ix])
			{
				cf = box_to_cf(ix);
				count++;
			}
		}
		if((count==1) && (cf > 0))
		{
			artfile_statistics(cf);
		}
		else
		{
			artfile_statistics(-1);
		}
		break;

	case 23:   /* CTRL-W */
		new_reply(NULL,NULL,NULL,NULL,0,NULL);
		break;

	case 26:   /* CTRL-Z */
		memset(box_selected,0,sizeof(box_selected));
		b->n_selected = 0;
		redraw_blist(b);
		break;

	case 27:   /* TESTING ONLY */
		break;

	case F_KEY+7:
		addrlist_open(1);
		break;

	default:
		wimp_processkey(c);
		break;
	}
}   /* end of boxlist_key_window */




void print_justify(int x, int y, int width, int value)
/****************************************************/
{
	int  n_chars;
	char buf[16];
	os_regset regs;

	sprintf(buf,"%d",value);

	regs.r[0] = standard_font;
	regs.r[1] = (int)buf;
	regs.r[2] = 0x20180;   /* bits 7, 8, 17 */
	regs.r[3] = width * scale_x;
	regs.r[4] = lists_line_height * scale_y;
	regs.r[7] = strlen(buf);
	os_swi(0x400a1+os_X,&regs);     /* Font_ScanString */

	n_chars = (char *)regs.r[1] - buf;

	if(width > 0)
	{
		x = x + width  - (regs.r[3] / scale_x);
	}

	regs.r[1] = (int)buf;
	regs.r[2] = 0x190;    /* bit 4, bit 7, bit 8 */
	regs.r[3] = x;
	regs.r[4] = y;
	regs.r[7] = n_chars;
	os_swi(0x40086+os_X,&regs);   /* Font_Paint */
}   /* end of print_justify */




void boxlist_display_window(BLIST *b,wimp_redrawstr *r,int line,int end_line,int x,int y)
/***************************************************************************************/
{
	int  n_chars;
	int  box;
	char *name;
	int  colr;
	int  x1;
	int  y_status;
	char count_string[16];
	os_regset regs;

	for(line=line; line<end_line; line++)
	{
		if(line >= b->n_entries)
			break;

		box = boxes_list2[line];
		name = get_box_name(box);

		colr = box_table[box].colour;
		list_set_colour(colr, box_selected[box] ^ 1,x,y);

		if(box_table[box].indent)
			x1 = 80;
		else
			x1 = 48;

		if(standard_font >= 0)
		{
			n_chars = strlen(name);

			regs.r[0] = standard_font;
			regs.r[1] = (int)name;
			regs.r[2] = 0x190;    /* bit 4, bit 7, bit 8 */
			regs.r[3] = x+x1;
			regs.r[4] = y;
			regs.r[7] = n_chars;
			os_swi(0x40086+os_X,&regs);    /* Font_Paint */
		}
		else
		{
			bbc_move(x+x1, y-4);
			printf("%s",name);
		}


		if((boxes_count[box] > 0) || (name[0] > ' '))
		{
			if(standard_font >= 0)
			{
				print_justify(x+388,y,92,boxes_unread_count[box]);
				print_justify(x+480,y,92,boxes_count[box]);
			}
			else
			{
				bbc_move(x+404, y-4);
				sprintf(count_string,"%5d",boxes_unread_count[box]);
				printf(count_string);
				bbc_move(x+488, y-4);
				sprintf(count_string,"%5d",boxes_count[box]);
				printf(count_string);
			}
		}

		y_status = y - r->box.y1 + r->scy + status_sprite_offset;
		if((box >= BOX_EXTERN) && (box < BOX_BIN))
		{
			/* external box, always display, special status icon */
			display_icon(0,2,y_status-2,3);
		}
		else if(boxes_unread[box] != 0x7f)
		{
			display_icon(boxes_unread[box],2,y_status,2);
		}

		y -= lists_line_height;
	}
}   /* boxlist_display_window */





/******************************************************************/
/*                    MAIL BOXES LIST                             */
/******************************************************************/


void mbox_open(int open)
/**********************/
{
	blist_set_extent(&mboxlist,open);
}



static void mboxlist_display_window(BLIST *b,wimp_redrawstr *r,int line,int end_line,int x,int y)
/***********************************************************************************************/
{
	int  i;
	char *user_id;
	OPTIONS_MAILBOX *mb;
	char buf[128];

	for(line=line; line<end_line; line++)
	{
		if(line >= b->n_entries)
			break;

		mb = &options.mailbox[mbox_list[line]];

		if(mbox_list[line] == mbox_selected)
			list_set_colour(0, 0,x,y);
		else
			list_set_colour(0, 1,x,y);

		if(options.users_menu)
			user_id = mb->email_addr;
		else
			user_id = get_user_id(mb);

		if(standard_font >= 0)
		{
			paint_string(user_id,x+12,y,382);
			paint_string(mb->full_name,x+410,y,1024);

		}
		else
		{
			strncpy(buf,user_id,27);
			buf[27] = 0;

			bbc_move(x+12, y-4);
			printf("%s",user_id);
			i = strlen(user_id)*16 + 28;
			if(i < 410)
				i = 410;
			bbc_move(x+i, y-4);
			printf("%s",mb->full_name);
		}

		y -= lists_line_height;
	}
}   /* mboxlist_display_window */




static void mboxlist_init()
/*************************/
{
	menu m;

	m = menu_new("Users",menustr_mboxlist);
	event_attachmenu(mboxlist.w_list,m,mboxlist_menu_proc,(void *)NULL);
}   /* end of mboxlist */



/******************************************************************/
/*                          NEWSGROUPS                            */
/******************************************************************/


extern NEWSG **ng_index;
extern int  n_ngroups;



void blist_save_data(BLIST *b,char *fname)
/****************************************/
{
	FILE *f;
	int  ix;
	char *p;
	char buf[160];

	sprintf(buf,"%s.%s",path_choices,fname);
	f = fopen_werr(buf,"w",NULL);
	if(f == NULL)
		return;

	fwrite(&(b->n_entries),sizeof(int),1,f);
	for(ix=0; ix<b->n_entries; ix++)
	{
		p = b->index_ptr[ix];
		fwrite(p,p[0],1,f);
	}
	fclose(f);
}   /* end of blist_save_data */




wimp_menustr *newsg_make_menu(int type)
/*************************************/
/* Type=0  newsgroups,  1=mailing lists
       bits 8-15  start with an extra entry: 1=Poster
*/
{
	int  i;
	NEWSG *ng;
	char *title;
	int  count;

	static menu menu_newsg=NULL;

	newsg_menu_extra = type >> 8;
	newsg_menu_type = type & 0xff;

	if(newsg_menu_type == 0)
		title = "NewsGroups";
	else
		title = "Mailing Lists";

	if(menu_newsg != NULL)
		menu_dispose(&menu_newsg,0);

	menu_newsg = menu_new(title,"");

	if(newsg_menu_extra == 1)
	{
		menu_extend(menu_newsg,"poster");
	}

	count = 0;
	for(i=0; i<n_ngroups; i++)
	{
		ng = ng_index[i];
		if((ng->maillist==newsg_menu_type) && (ng->active))
		{
			menu_extend(menu_newsg,expand_source(ng->source,3));
			count++;
		}
	}
	if(count==0)
		menu_extend(menu_newsg,"none");   /* ensure the menu is not empty - causes crash */

	return((wimp_menustr *)menu_syshandle(menu_newsg));

}   /* end of newsg_make_menu */



char *newsg_menu_name(int hit, NEWSG **ng_out)
/********************************************/
/* Get the newsgroup name that has been selected from the newsgroups menu */
{
	int  i;
	NEWSG *ng;

	if(ng_out != NULL)
		*ng_out = NULL;

	if(newsg_menu_extra)
	{
		if(--hit < 0)
		{
			return("poster");
		}
	}


	for(i=0; i<n_ngroups; i++)
	{
		ng = ng_index[i];

		if((ng->maillist==newsg_menu_type) && (ng->active))
		{
			if(--hit < 0)
			{
				if(ng_out != NULL)
					*ng_out = ng;

				if(memcmp(ng->name,"mail.",5)==0)
					return(&ng->name[5]);
				else
					return(ng->name);
			}
		}
	}
	return("");
}   /* end of newsg_menu_name */




NEWSG *newsg_lookup(char *name)
/*****************************/
{
	int i;

	for(i=0; i<n_ngroups; i++)
	{
		if(strcmp_lc(name,ng_index[i]->name)==0)
			return(ng_index[i]);
	}
	return(NULL);
}   /* end of newsg_lookup */



NEWSG *newsg_lookup2(char *name)
/******************************/
/* Match start of string with newsgroup name */
{
	int i;

	while(isspace(name[0]))
		name++;

	for(i=0; i<n_ngroups; i++)
	{
		if(memcmp_lc2(name,ng_index[i]->name,strlen(ng_index[i]->name))==0)
			return(ng_index[i]);
	}
	return(NULL);
}   /* end of newsg_lookup */




int source_to_user(int source)
/****************************/
{
	int  i;

	for(i=0; i<n_ngroups; i++)
	{
		if(ng_index[i]->source == source)
			return(ng_index[i]->default_user);
	}
	return(0);
}   /* end of source_to_user */



void newsg_display_window(BLIST *b,wimp_redrawstr *r,int line,int end_line,int x,int y)
/*************************************************************************************/
{
	os_regset regs;
	NEWSG *ng;
	int  start_ng;
	int  margin;
	int  y_top;
	char expiry[8];
	char name[150];
	int sprite_iconblk[12];

	y_top = r->box.y1 - r->scy + 36 - lists_font_offset;

	if(b == &newsglist)
	{
		start_ng = 0;
		margin = 64;
	}
	else
	{
		start_ng = newsglist.n_entries;
		margin = 0;
	}

	for(line=line; line<end_line; line++)
	{
		if(line >= b->n_entries)
			break;

		ng = ng_index[line + start_ng];


		if(b == &mlistlist)
			strcpy_printable(name,expand_source(ng->source,3),sizeof(name));  /* omit mail. */
		else
			strcpy_printable(name,ng->name,sizeof(name));

		if(ng->selected)
			list_set_colour(0, 0,x,y);
		else
			list_set_colour(0, 1,x,y);

		sprintf(expiry,"%3d",ng->expiry);

		if(standard_font >= 0)
		{
			regs.r[0] = standard_font;
			regs.r[2] = 0x190;    /* bit 4, bit 7, bit 8 */
			regs.r[4] = y;

			if(margin != 0)
			{
				if(ng->ng_flags & NG_HEADFETCH)
				{
					regs.r[1] = (int)"H";
					regs.r[3] = x+40;
					regs.r[7] = 1;
					os_swi(0x40086,&regs);
				}
			}

			if(ng->expiry > 0)
			{
				regs.r[1] = (int)expiry;
				regs.r[3] = x+margin;
				regs.r[7] = strlen(expiry);
				os_swi(0x40086,&regs);
			}
			regs.r[1] = (int)name;
			regs.r[3] = x+margin+60;
			regs.r[7] = strlen(name);
			os_swi(0x40086,&regs);
		}
		else
		{
			if(margin != 0)
			{
				if(ng->ng_flags & NG_HEADFETCH)
				{
					bbc_move(x+40,y-4);
					putchar('H');
				}
			}

			if(ng->expiry > 0)
			{
				bbc_move(x+margin,y-4);
				printf(expiry);
			}

			bbc_move(x+margin+60, y-4);
			printf("%s",name);
		}

		if(margin != 0)
		{
			if(ng->active)
			{
				strcpy((char *)&sprite_iconblk[8],"\200");   /* tick sprite */
				sprite_iconblk[0] = 0;
				sprite_iconblk[1] = y-y_top;
				sprite_iconblk[2] = 0+40;
				sprite_iconblk[3] = y-y_top+36;
				if(ng->selected)
					sprite_iconblk[4] = 0x20011a;
				else
					sprite_iconblk[4] = 0x11a;  /* bits 1, 3, 4, 8 */
				sprite_iconblk[5] = (int)&sprite_iconblk[8];
				sprite_iconblk[6] = 1;   /* wimp sprites */
				sprite_iconblk[7] = 1;  /* length of sprite name */

				wimp_ploticon((wimp_icon *)&sprite_iconblk);
			}
		}

		y -= lists_line_height;
	}
}   /* newsg_display_window */






void newsg_open(int type)
/***********************/
/* 0=newsgroups  1=mailing lists */
{
	newsg_load(0);    /* to make sure we have up-to-date article serial numbers */

	if(type==0)
		blist_set_extent(&newsglist,3);
	else
		blist_set_extent(&mlistlist,3);

}   /* end of newsg_open */





/******************************************************************/
/*          GENERAL  "LIST WITH BUTTON BAR"  ROUTINES             */
/******************************************************************/



void redraw_blist(BLIST *b)
/*************************/
{
	wimp_redrawstr r;
	memcpy(&r,&b->workarea,sizeof(r));


	wimp_force_redraw(&r);
}   /* end of redraw_blist */




void redraw_blist_line(BLIST *b,int line)
/***************************************/
{
	wimp_redrawstr r;
	memcpy(&r,&b->workarea,sizeof(r));

	/* give workarea coordinates of line to be redrawn */
	r.box.y0 = b->workarea.box.y1 - ((line+1)*lists_line_height);
	r.box.y1 = b->workarea.box.y1 - (line*lists_line_height)+2;
	wimp_force_redraw(&r);
}   /* end of redraw_list_line */





void blist_open_window(BLIST *b, wimp_openstr *o)
/***********************************************/
{
	int max_y;
	int  dy;
	wimp_openstr but;
	wimp_wstate wstatet;


	/* make sure there's room for button bar above the boxlist window */
	max_y = y_screen_dim - (b->butbar_height + scroll_bar_height - 2);

	if(o->box.y1 > max_y)
		o->box.y1 = max_y;
	if(o->box.y0 < scroll_bar_height)
		o->box.y0 = scroll_bar_height;

#ifdef deleted
	dy = (o->box.y1 - o->box.y0) - max_y;
	if(dy > 0)
	{
		/* restrict size of window */
		o->box.y0 += dy;
	}

	max_y = y_screen_dim - (b->butbar_height + scroll_bar_height - 2);
	if(o->box.y1 > max_y)
	{
		/* too near top of screen, allow space for button bar  */
		dy = o->box.y1 - max_y;
		o->box.y1 -= dy;
		o->box.y0 -= dy;
	}
#endif

	b->open = 1;
	wimp_open_wind(o);     /* open boxlist window */

	if(b->w_buttons)
	{
		wimp_get_wind_state(b->w_list,&wstatet);

		but.box.x0 = wstatet.o.box.x0;
		but.box.x1 = wstatet.o.box.x1 + scroll_bar_width;
		but.box.y0 = wstatet.o.box.y0;
		but.box.y1 = wstatet.o.box.y1 + b->butbar_height;
		but.x = 0;
		but.y = 0;
		but.w = b->w_buttons;
		but.behind = b->w_list;
		wimp_open_wind(&but);    /* open button bar and title bar */
	}

}   /* end of blist_open_window */





void blist_select(BLIST *b, int line, int selected)
/*************************************************/
{
	switch(b->type)
	{
	case BLIST_BOXES:
		boxlist_select(line,selected);
		break;
	case BLIST_MBOXES:
		break;
	case BLIST_POSTED:
		postedlist_select(line,selected);
		break;
	case BLIST_ADDR:
		addrlist_select(line,selected);
		break;
	case BLIST_NEWSG:
		newsg_select(&newsglist,line,selected);
		break;
	case BLIST_MLIST:
		newsg_select(&mlistlist,line,selected);
		break;
	case BLIST_KILL:
		filter_select(line,selected);
		break;
	}
}   /* end of blist_select */







void blist_drag_continue(BLIST *b, int open, wimp_wstate *wstate,
						 wimp_wstate *wstate1, wimp_mousestr *m)
/***************************************************************/
{
	int  i;
	int  y;
	int  linenum;
	int  start=0;
	int  end=0;

	if(b==NULL)
		return;

	/* any scrolling to be done */
	if(open)
	{
		blist_open_window(b,&wstate->o);
	}

	/* find line number from workarea coords */
	i = wstate->o.box.y1 - m->y;   /* displacement down the window */
	y = i - wstate->o.y;        /* work area coordinate */
	linenum = y/lists_line_height;

	if(linenum < 0)
		linenum = 0;
	if(linenum >= b->n_entries)
		linenum = b->n_entries-1;

	if(linenum == drag_last_line)
		return;

	if(linenum > drag_last_line)
	{
		start = drag_last_line + 1;
		if(drag_direction < 0)
			start = drag_last_line;
		end = linenum;
		drag_direction = 1;
	}
	else
	{
		start = linenum;
		end = drag_last_line - 1;
		if(drag_direction > 0)
			end = drag_last_line;
		drag_direction = -1;
	}

	for(i=start; i<=end; i++)
	{
		blist_select(b,i,2);   /* invert */
	}
	drag_last_line = linenum;
}   /* end of blist_drag_continue */





void blist_set_extent(BLIST *b,int open)
/**************************************/
/* open:  bit 0   open list window
          bit 1   recalculate data
          bit 2   don't bring to front
          bit 3   open at pointer
          bit 4   get input focus
          bit 5   open posted list
          bit 6   full size
*/
{
	int size;
	int re_open = 0;
	wimp_wstate wstate;
	wimp_redrawstr w_workarea;
	wimp_mousestr m;

	size = b->n_entries * lists_line_height;
	if(size < 124)
		size = 124;

	/*   if(-size > b->workarea.box.y0) */
	{
		/* workarea is now smaller, redraw the larger area */
		redraw_blist(b);
	}

	if(size != b->workarea.box.y0)
		re_open = 1;

	wimp_get_wind_state(b->w_list,&wstate);
	if((wstate.flags & wimp_WFULL) && (wstate.flags & wimp_WOPEN))
	{
		/* was open fully, set full size option */
		open |= 0x40;
	}

	b->workarea.box.y1 = 0;
	b->workarea.box.y0 = -size;
	b->workarea.box.x0 = 0;
	b->workarea.box.x1 = b->width;
	b->workarea.w = b->w_list;
	wimp_set_extent(&b->workarea);

	memcpy(&w_workarea,&b->workarea,sizeof(w_workarea));
	w_workarea.box.y0 = -(size  + b->butbar_height);
	w_workarea.box.x1 = b->width+scroll_bar_width;
	w_workarea.w = b->w_buttons;
	wimp_set_extent(&w_workarea);


	if(open & 2)
	{
		switch(b->type)
		{
		case BLIST_BOXES:
			boxlist_recalc();
			re_open = 1;
			break;
		}
		redraw_blist(b);    /* force redraw */
	}

	if(open & 1)
	{
		wimp_get_wind_state(b->w_list,&wstate);

		if(open & 0x40)
		{
			/* open full size */
			wstate.o.box.y0 = wstate.o.box.y1 - size;
		}
		if(open & 8)
		{
			/* open at pointer position */
			wimp_get_point_info(&m);
			wstate.o.box.x0 = m.x - 20;
			wstate.o.box.x1 = wstate.o.box.x0 + b->width;
			wstate.o.box.y1 = m.y + 16;
		}

		/*      wstate.o.box.y0 = wstate.o.box.y1 - size; */

		if((open & 4) == 0)
			wstate.o.behind = -1;

		blist_open_window(b,&wstate.o);
		re_open=0;

	}

	if(re_open)
	{
		wimp_get_wind_state(b->w_list,&wstate);
		if(open & 0x40)
		{
			/* open full size */
			wstate.o.box.y0 = wstate.o.box.y1 - size;
		}
		if(wstate.flags & wimp_WOPEN)
			blist_open_window(b,&wstate.o);
	}

	if(open & 0x10)
	{
		set_focus(b->w_list);
	}
}   /* end of blist_set_extent */



void call_key_window(BLIST *b,wimp_eventdata *d)
/**********************************************/
{
	switch(b->type)
	{
	case BLIST_BOXES:
		boxlist_key_window(b,d);
		break;

	case BLIST_MBOXES:
		mboxlist_key_window(b,d);
		break;

	case BLIST_POSTED:
		postedlist_key_window(b,d);
		break;

	case BLIST_NEWSG:
	case BLIST_MLIST:
		newsg_key_window(b,d);
		break;

	case BLIST_KILL:
		kill_key_window(b,d);
		break;

	case BLIST_ADDR:
		addrlist_key_window(b,d);
		break;

	case BLIST_PGP:
		pgp_key_window(b,d);
		break;
	}
}   /* end of call_key_window */




void blist_window_handler(wimp_eventstr *e, void *handle)
/*******************************************************/
{
	int  more;
	wimp_redrawstr r;
	BLIST *b;
	int  type;
	int  linenum;
	int  i;
	int  bbits;
	int  c;
	wimp_eventdata *d;
	wimp_wstate wstate;

	int  min_y, max_y;
	int  line;
	int  x;
	int  y;
	int  end_line;

	int filetype;
	char *filename;


	b = (BLIST *)handle;
	type = b->type;

	d = &e->data;

	switch (e->e)
	{
	case wimp_ENULL:
		if(null_event_map & 2)
			drag_continue();
		break;

	case wimp_EUSERDRAG:
		null_events(2,0);     /* turn off for drag */
		break;

	case wimp_EOPEN:
		blist_open_window(b,&e->data.o);
		break;

	case wimp_ECLOSE:  /* Pass on close request */
		b->open = 0;
		wimp_close_wind(e->data.o.w);

		break;

	case wimp_ESCROLL:
		scroll_window(&e->data);
		break;

	case wimp_EPTRLEAVE:
		visdelay2_leave();
		break;

	case wimp_EPTRENTER:
		visdelay2_enter();
		break;



	case wimp_EREDRAW:
		r.w = e->data.o.w;
		wimp_redraw_wind(&r, &more);

		while(more)
		{
			min_y = (r.box.y1 - r.g.y1) - r.scy;
			max_y = (r.box.y1 - r.g.y0) - r.scy;

			line = min_y / lists_line_height;
			end_line = (max_y / lists_line_height) + 1;

			x = r.box.x0 - r.scx + 2;
			y = r.box.y1 - r.scy - (line * lists_line_height);
			y -= lists_font_offset;

			switch(type)
			{
			case BLIST_BOXES:
				boxlist_display_window(b,&r,line,end_line,x,y);
				break;

			case BLIST_MBOXES:
				mboxlist_display_window(b,&r,line,end_line,x,y);
				break;

			case BLIST_POSTED:
				postedlist_display_window(b,&r,line,end_line,x,y);
				break;

			case BLIST_NEWSG:
			case BLIST_MLIST:
				newsg_display_window(b,&r,line,end_line,x,y);
				break;

			case BLIST_KILL:
				kill_display_window(b,&r,line,end_line,x,y);
				break;

			case BLIST_ADDR:
				addrlist_display_window(b,&r,line,end_line,x,y);
				break;

			case BLIST_PGP:
				pgp_display_window(b,&r,line,end_line,x,y);
				break;
			}
			wimp_get_rectangle(&r, &more);
		}
		break;


	case wimp_EBUT:
		wimp_get_wind_state(b->w_list,&wstate);
		i = wstate.o.box.y1 - d->but.m.y;   /* displacement down the window */
		y = i - wstate.o.y;        /* work area coordinate */

		/* find article from workarea coords */
		linenum = y/lists_line_height;

		set_focus(d->but.m.w);
		if(linenum >= b->n_entries)
			break;

		bbits = d->but.m.bbits;

		if((bbits==wimp_BDRAGRIGHT) || (bbits==wimp_BDRAGLEFT))
		{
			if(akbd_pollsh() || akbd_pollctl())
				drag_start(&d->but.m,4,(void *)b,b->w_list);
			break;
		}
		drag_last_line = linenum;
		drag_direction = 0;

		switch(type)
		{
		case BLIST_BOXES:
			boxlist_click_window(b,linenum,bbits);
			break;

		case BLIST_MBOXES:
			mboxlist_click_window(b,linenum,bbits);
			break;

		case BLIST_POSTED:
			postedlist_click_window(b,linenum,bbits);
			break;

		case BLIST_NEWSG:
		case BLIST_MLIST:
			newsg_click_window(b,linenum,bbits);
			break;

		case BLIST_KILL:
			kill_click_window(b,linenum,bbits);
			break;

		case BLIST_ADDR:
			addrlist_click_window(b,linenum,bbits);
			break;

		case BLIST_PGP:
			pgp_click_window(b,linenum,bbits);
			break;
		}
		break;

	case wimp_EKEY:
		c = d->key.chcode;
		switch(c)
		{
		case F_KEY_CTRL+2:
			b->open = 0;
			wimp_close_wind(b->w_list);
			wimp_close_wind(b->w_buttons);
			break;

		case PAGE_UP:
			if(akbd_pollsh())
				call_key_window(b,d);
			else
				scroll_page(b->w_list,1);
			break;

		case PAGE_DOWN:
			if(akbd_pollsh())
				call_key_window(b,d);
			else
				scroll_page(b->w_list,-1);
			break;

		case KEY_CT_DOWN:
			scroll_to_top(b->w_list,1);
			break;

		case KEY_CT_UP:
		case KEY_HOME:
			scroll_to_top(b->w_list,-1);
			break;

		default:
			call_key_window(b,d);
			break;
		}
		break;

	case wimp_ESEND:
	case wimp_ESENDWANTACK:
		switch(e->data.msg.hdr.action)
		{
		case wimp_MDATASAVE:     /* import data */
			xferrecv_wimpscrap(e);
			break;

		case wimp_MDATALOAD:     /* insert data */
			filetype = xferrecv_checkinsert(&filename);

			if(type == BLIST_ADDR)
			{
				addrlist_import(filename,filetype);
			}
			else if(type == BLIST_PGP)
			{
				pgp_import(filename,filetype);
			}
			else if(type == BLIST_BOXES)
			{
				boxlist_import(filename,filetype);
			}
			xferrecv_insertfileok();
			break;

		case wimp_MHELPREQUEST:
			help_handler(e,b->help_type);
			break;
		}
		break;

	default:   /* Ignore any other event */
		break;
	}
}   /* end of blist_window_handler */





void blist_menu_deleted(int menuptr)
/**********************************/
{
	int *i;

	i = (int *)blist_current_menu;

	if(menuptr == *i)
	{
		/* unselect from the corresponding blist if this item was
		   automatically selected by a menu-click */

		switch(blist_current_menu_blist)
		{
		case BLIST_BOXES:
			boxlist_select(blist_current_menu_line,0);
			break;
		case BLIST_MBOXES:
			break;
		case BLIST_POSTED:
			break;
		case BLIST_ADDR:
			addrlist_select(blist_current_menu_line,0);
			break;
		case BLIST_NEWSG:
			newsg_select(&newsglist,blist_current_menu_line,0);
			break;
		case BLIST_MLIST:
			newsg_select(&mlistlist,blist_current_menu_line,0);
			break;
		case BLIST_KILL:
			filter_select(blist_current_menu_line,0);
			break;
		default:
			list_deselect((FOLDREC *)blist_current_menu_blist,blist_current_menu_line);
			break;
		}
	}


	blist_current_menu = NULL;
}   /* end of menu_deleted */






void blist_open_buttons(BLIST *b,wimp_openstr *o, wimp_w window)
/**************************************************************/
/* Open the button bar window, as specified.  Also open the
    list window */

/* NOTE: Can use this for folding list window, but that doesn't have BLIST *b */
{
	int behind;
	int  top_window;
	wimp_w this_w;

	wimp_wstate wstate;
	wimp_wstate wstate_t;

	b->open = 1;

#ifdef deleted
	if(o->box.y1 > (y_screen_dim - scroll_bar_height + 2))
		o->box.y1 = y_screen_dim - scroll_bar_height + 2;
#endif

	this_w = o->w;
	wimp_get_wind_state(window,&wstate_t);

	behind = top_window = o->behind;

	if(behind <= -2)
	{
		if(behind == -3)
			wimp_open_wind(o);

		wstate_t.o.behind = behind;
		wimp_open_wind(&wstate_t.o);

		/* put to back */
		if(behind == -2)
			wimp_open_wind(o);
		return;
	}


	if((behind == -1) && (wstate_t.o.behind == -1))
		o->behind = window;

	behind = o->behind;
	wimp_open_wind(o);   /* open button bar & title bar */

	wimp_get_wind_state(this_w,&wstate);

	wstate_t.o.box.x0 = wstate.o.box.x0;
	wstate_t.o.box.x1 = wstate.o.box.x1 - scroll_bar_width;
	wstate_t.o.box.y1 = wstate.o.box.y1 - b->butbar_height;
	wstate_t.o.box.y0 = wstate.o.box.y0;

	if(behind == -1)
		wstate_t.o.behind = -1;
	else
		wstate_t.o.behind = top_window;   /* Added 28.6.99 */
	wimp_open_wind(&wstate_t.o);    /* open list window */

	wstate.o.behind = window;
	wimp_open_wind(&wstate.o);   /* open the button window again to ensure it's behind the list window */

}   /* end of blist_open_buttons */




void blist_buttons_handler(wimp_eventstr *e, void *handle)
/********************************************************/
{
	BLIST *b;
	wimp_eventdata *d;
	int  icon;
	int  click;

	b = (BLIST *)handle;
	d = &e->data;

	switch (e->e)
	{
	case wimp_EOPEN:
		blist_open_buttons(b,&e->data.o,b->w_list);
		break;

	case wimp_ECLOSE:  /* Pass on close request */
		b->open = 0;

		if(b->type == BLIST_BOXES)
		{
			/* revoke all passwords */
			memset(box_password_ok,0,sizeof(box_password_ok));
			pgp_clear_password();
		}

		wimp_close_wind(b->w_list);   /* close list window also */
		wimpt_noerr(wimp_close_wind(e->data.o.w));
		break;

	case wimp_EPTRLEAVE:
		visdelay2_leave();
		break;

	case wimp_EPTRENTER:
		visdelay2_enter();
		break;

	case wimp_EBUT:
		icon = d->but.m.i;
		click = d->but.m.bbits;

		switch(b->type)
		{
		case BLIST_BOXES:
			boxlist_click_buttons(b,d);
			break;

		case BLIST_MBOXES:
			mboxlist_click_buttons(b,d);
			break;

		case BLIST_POSTED:
			postedlist_click_buttons(b,d);
			break;

		case BLIST_NEWSG:
		case BLIST_MLIST:
			newsg_click_buttons(b,d);
			break;

		case BLIST_KILL:
			kill_click_buttons(b,d);
			break;

		case BLIST_ADDR:
			addrlist_click_buttons(b,d);
			break;

		case BLIST_PGP:
			pgp_click_buttons(b,d);
			break;

		}

	default:   /* Ignore any other event */
		help_handler(e,b->help_type);
		break;
	}
}   /* end of blist_buttons_handler */




void blist_register(BLIST *b,int type,char *templ)
/*******************************************/
{
	int  y;
	wimp_wind *window_template;
	/*   template *t; */
	int  back_colour=0;


	blist_reg[type] = b;

	if(options.position_y[type+4] > 0)
	{
		b->x = options.position_x[type+4];
		b->y = options.position_y[type+4];
		b->y0 = options.position_y0[type+4];
	}

	b->max_entries = 0;
	b->data_ptr = NULL;

	b->type = type;
	if(options.colours[COLR_LISTS] != 0)
		back_colour = 1;

	if(templ)
	{
		window_template = template_syshandle("List");
		window_template->box.x0 = b->x;
		window_template->box.x1 = b->x + b->width;
		y = b->y;
		if(y > y_screen_dim)
			y = y_screen_dim;
		window_template->box.y1 = y - b->butbar_height;

		if(options.restrict_height==2)
			window_template->box.y0 = b->y0;
		if((options.restrict_height==1) || preserve_iconbar())
			window_template->box.y0 = 160;  /* don't cover icon bar */

		window_template->colours[wimp_WCWKAREABACK] = back_colour;
		wimp_create_wind(window_template ,&b->w_list);
	}
	else
	{
#ifdef deleted
		/* list without a button bar */
		t = template_copy(template_find("Window"));
		t->window.box.x0 = b->x;
		t->window.box.y1 = b->y;
		window_template->colours[wimp_WCWKAREABACK] = back_colour;
		wimp_create_wind(&t->window,&b->w_list);
		win_settitle(b->w_list,b->title);
#endif
	}


	win_register_event_handler(b->w_list,blist_window_handler,(void *)b);

	if(templ)
	{
		/* create button bar */
		window_template = template_syshandle(templ);
		b->button_template = window_template;
		window_template->box.x0 = b->x;
		window_template->box.x1 = b->x + b->width;
		window_template->box.y1 = y;
		if(options.restrict_height==2)
			window_template->box.y0 = b->y0;
		wimp_create_wind(window_template,&b->w_buttons);
		win_register_event_handler(b->w_buttons,blist_buttons_handler,(void *)b);
	}
	else
	{
		b->w_buttons = NULL;
	}
}   /* end of blist_register */



void box_bin_hide()
/****************/
/* Set all BIN articles to HIDDEN */
{
	int  ix;
	CARD *cptr;
	unsigned int *ixlist;
	FOLDREC *fr;


	fr = &list_fr[0];
	ixlist = fr->ixlist;
	for(ix=0; ix<fr->n_entries; ix++)
	{
		cptr = (CARD *)((ixlist[ix] & IXLIST_MASK) << 2);
		if((cptr->date_box >> 26) == BOX_BIN)
			cptr->status = cptr->status & ~STATUS_MASK2 | STATUS_HIDDEN;
	}
}   /* end of box_bin_hide */




void blist_exit()
/***************/
{
	if(password_entered)
	{
		/* set all BIN articles to HIDDEN */
		box_bin_hide();
	}

	if(boxlist_changed==0x6398)
		save_boxes_file();

	if(options_save_flag==1)
		options_save();
}   /* end of blist_exit */




void boxlist_click_open(int full_size)
/************************************/
{
	if(select_user_flag==1)
		mbox_open(0x41);   /* full size */
	else
	{
		set_boxlist_extent(full_size ? 0x63 : 0x23);   /* open boxes list */
		select_user_flag = 0;
	}
}   /* end of boxlist_click_open */




void init_blist()
/***************/
{
	/* window for list of boxes */
	if(options.box_list_y != 0)
	{
		boxlist.x = options.box_list_x;
		boxlist.y = options.box_list_y;
	}
	blist_register(&boxlist,BLIST_BOXES,"BoxBar");   /* BoxBar */
	menu_boxlist = menu_new("Boxes",menustr_boxlist);
	menu_boxnews = menu_new("Mail+News",menustr_boxnews);
	menu_submenu(menu_boxlist,4,menu_boxnews);

	event_attachmenumaker(boxlist.w_list,boxlist_menu_maker,boxlist_menu_proc,(void *)&boxlist);


	memset(box_table,0,sizeof(box_table));
	if(load_boxes_file() < 0)
		exit(4);

	make_boxes_menu(0,NULL);

	memset(box_password_ok,0,sizeof(box_password_ok));

	/* window for list of mail boxes */
	blist_register(&mboxlist,BLIST_MBOXES,"MBoxBar");   /* MBoxBar */
	mboxlist_init();


	if((options.initial_select_user) && (mboxlist.n_entries > 1))
		select_user_flag = 1;

	blist_register(&pgplist,BLIST_PGP,"PGPBar");


	postedlist_init();
}   /* end of init_blist */


