
/***************************************************************************
 *   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 "win.h"
#include "event.h"
#include "wimp.h"
#include "dbox.h"
#include "werr.h"
#include "os.h"
#include "bbc.h"
#include "xfersend.h"
#include "flex.h"

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



extern char *path_choices;
extern wimp_menustr *wmenu_boxes_names;
extern int dbox_menu_field;
extern menu menu_colours;
extern int lists_line_height;
extern int standard_font;
extern OPTIONS options;
extern FOLDREC list_fr[N_LISTS];
extern int scale_x, scale_y;

extern menu blist_current_menu;
extern int blist_current_menu_line;
extern int blist_current_menu_blist;

extern char mbox_list[];

ADDR_BOOK *a_edit;


/******************************************************************/
/*                       ADDRESS BOOK                             */
/******************************************************************/


#define ADDR_FIXED 16   /* length length part of addr record */
#define ADDR_EXTRA 50

static void set_folder(int fldr);


static BLIST blist_addr = {BLIST_ADDR,100,500,NULL,80,1200,0,HELP_ADDRLIST};
static char *menustr_addrlist = "Selection,Select all ^A,Folder,Sort,>Export,Find  F4,Set article list colours,Initially open";
static char *menustr_addrlist_select = "Edit,Move,Copy,Clear ^Z,Delete ^X";
static char *menustr_addrlist_sort = "Alias,Name,Last name,Address,Colour,Colr+Last name";
static char *menustr_addrlist_fldractn = "New folder,Rename,Delete";

static menu menu_addrlist;
static menu menu_selection;
static menu menu_fldractn;
static menu menu_folders=NULL;
static menu menu_folders2=NULL;
static wimp_menustr *wmenu_folders2=NULL;
static char menu_folder_new[24];
static char menu_folder_rename[24];

static dbox dbox_addr=0;
static addr_number=0;

extern ADDR_BOOK **addr_list;
extern int n_addr_list;

static char *addr_list_data = NULL;
static int max_addr_list;
static int addr_list_sort_type;

TEXTR *addr_insert_textr=NULL;
dbox addr_insert_dbox = NULL;
static int addr_insert_field = 0;
static char addr_filename[128] = "Addresses";

char addr_colr_trans[16] = {0,1,2,3,4,5,6,7,6,5,4,1,0,0,0,0};


#define N_FOLDERS  32

int folder_base=0;
static int current_folder=0;
static int current_folder_num = 0;
static int initial_open_folder = 0;

static int changed_folder;
static int changed_user;

static char folder_nums[N_FOLDERS];  /* back from fixed number to folder index */

typedef struct {
	char folder_number;
	char folder_sorting;
	char *folder_name;
} FOLDER;

int n_folders = 0;
FOLDER folder[N_FOLDERS];











static int folder_sorter(const void *p1, const void *p2)
/******************************************************/
{
	FOLDER *a1;
	FOLDER *a2;

	a1 = (FOLDER *)p1;
	a2 = (FOLDER *)p2;

	return(strcmp(a1->folder_name,a2->folder_name));
}   /* end of folder_sorter */



static void sort_folders()
/************************/
{
	int  ix;

	/* don't move the first entry - the default folder */
	qsort(&folder[1],n_folders-1,sizeof(FOLDER),folder_sorter);

	memset(folder_nums,sizeof(folder_nums),0);
	for(ix=0; ix<n_folders; ix++)
		folder_nums[folder[ix].folder_number] = ix;
}   /* end of sort_folders */



static int addrlist_sorter(const void *p1, const void *p2)
/********************************************************/
{
	int  i;
	ADDR_BOOK *a1;
	ADDR_BOOK *a2;
	int  ix1, ix2;

	a1 = *(ADDR_BOOK **)p1;
	a2 = *(ADDR_BOOK **)p2;


	switch(addr_list_sort_type)
	{
	case 0:    /* sort by folder */
		i = a1->folder - a2->folder;
		if(i != 0)
			return(i);
		return(a1-a2);


	case 5:    /* sort on colour and name */
		i =  - a1->colr + a2->colr;
		if(i != 0)
			return(i);
	case 1:    /* sort on alias */
		i = strcmp_lc(&a1->data[a1->alias],&a2->data[a2->alias]);
		if(i != 0)
			return(i);
		/* else drop through to sort on name */
	case 2:    /* sort on name */
		return(strcmp_lc(a1->data,a2->data));


	case 6:   /* sort on colour and last name */
		i =  - a1->colr + a2->colr;
		if(i != 0)
			return(i);
		/* drop through to sort on last name */
	case 3:   /* sort on last name */
		ix1 = strlen(a1->data);
		while(--ix1 > 0)
		{
			if(a1->data[ix1] <= ' ')
			{
				ix1++;
				break;
			}
		}

		ix2 = strlen(a2->data);
		while(--ix2 > 0)
		{
			if(a2->data[ix2] <= ' ')
			{
				ix2++;
				break;
			}
		}
		i = strcmp_lc(&a1->data[ix1],&a2->data[ix2]);
		if(i == 0)
		{
			i = strcmp_lc(a1->data,a2->data);
		}
		return(i);

	case 4:    /* sort on url */
		return(strcmp_lc(&a1->data[a1->offset],&a2->data[a2->offset]));
	}

	return(0);
}   /* end of addrlist_sorter */



static void sort_addr_list()
/**************************/
{
	if(current_folder==n_folders)
		addr_list_sort_type = folder[0].folder_sorting;
	else
		addr_list_sort_type = folder[current_folder].folder_sorting;

	qsortG(&addr_list[folder_base],blist_addr.n_entries,sizeof(void *),addrlist_sorter);
}   /* end of sort_addr_list */





static void load_addr_file()
/**************************/
{
	FILE *f;
	int  ix;
	ADDR_BOOK *a;
	int  length;
	int  len;
	char *p;
	int  word;
	int  n_temp;
	FOLDER *fldr;
	char *name;
	ADDR_BOOK_OLD *a_old;
	ADDR_BOOK2 *a_new;
	char buf[256];

	sprintf(buf,"%s.addresses",path_choices);
	f = fopen(buf,"r");

	n_addr_list = 0;
	max_addr_list = 0;

	if(f!=NULL)
	{
		if(addr_list_data != NULL)
			free(addr_list_data);

		length = get_filelength(buf);

		if((fgets(buf,sizeof(buf),f) != NULL) && (memcmp(buf,"$nitems ",8)==0))
		{
			/* new format */
			n_temp = atoi(&buf[8]);
			n_folders = 0;

			if((addr_list_data = malloc(length)) == NULL)
			{
				malloc_err(70);
				fclose(f);
				return;
			}

			for(;;)
			{
				if(fgets(buf,sizeof(buf),f) == NULL)
					break;


				if(memcmp(buf,"$folder ",8)==0)
				{
					buf[strlen(buf)-1]=0;  /* strip trailing NL */
					fldr = &folder[n_folders++];
					fldr->folder_number = atoi(&buf[8]);
					fldr->folder_sorting = atoi(&buf[11]);
					name = &buf[17];
					p = malloc(strlen(name)+1);
					strcpy(p,name);
					fldr->folder_name = p;
				}
				else if(memcmp(buf,"$open",5)==0)
				{
					initial_open_folder = atoi(&buf[6]);
				}
				else if(memcmp(buf,"$data",5)==0)
				{
					fread(addr_list_data,1,length,f);
					n_addr_list = n_temp;
					break;
				}
			}
			sort_folders();

			max_addr_list = n_addr_list + ADDR_EXTRA;

			addr_list = malloc(max_addr_list * sizeof(ADDR_BOOK *));
			if(addr_list == NULL)
			{
				malloc_err(71);
				n_addr_list = 0;
				return;
			}

			p = addr_list_data;

			for(ix=0; ix<n_addr_list; ix++)
			{
				a = (ADDR_BOOK *)p;
				a->selected = 0;

				addr_list[ix] = a;

				len = a->length;
				if(len > 0x200)
				{
					/* corrupt length, due to not being on 2-byte boundary */
					len = len & 0xff;
				}
				p += (len+1);   /* including a NL separator */
			}
		}
		else if(length > 4)
		{
			/* old format */
			rewind(f);

			fread(&word,1,sizeof(word),f);
			n_addr_list = word & 0xffffff;
			folder[0].folder_sorting = word >> 24;

			a_old = (ADDR_BOOK_OLD *)buf;
			if((a_new = (ADDR_BOOK2 *)malloc(sizeof(ADDR_BOOK2))) == NULL)
				return;

			max_addr_list = n_addr_list + ADDR_EXTRA;

			addr_list = malloc(max_addr_list * sizeof(ADDR_BOOK *));
			if(addr_list == NULL)
			{
				malloc_err(71);
				n_addr_list = 0;
				return;
			}

			for(ix=0; ix<n_addr_list; ix++)
			{
				len = fgetc(f);
				if(feof(f))
					break;

				ungetc(len,f);
				fread(a_old,1,len,f);

				memcpy(&a_new->offset,&a_old->offset,5);
				memset(&a_new->folder,0,9);
				memcpy(a_new->data,a_old->data,len-6);
				a_new->length = len+10;
				a_new->sig = a_new->comment = (a_new->length - ADDR_FIXED - 1);

				a = (ADDR_BOOK *)malloc(a_new->length);
				if(a == NULL)
				{
					malloc_err(74);
					return;
				}
				memcpy(a,a_new,a_new->length);

				addr_list[ix] = a;
			}
			n_addr_list = ix;
		}
		fclose(f);
	}
	if(n_folders == 0)
		n_folders = 1;


	blist_addr.n_entries = n_addr_list;
	current_folder = folder_nums[initial_open_folder];
}   /* end of load_addr_file */



#ifdef deleted
static void save_addr_file1()
/***************************/
{
	/* Pluto V2 version */
	FILE *f;
	int  ix;
	int word;
	char buf[256];

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

	word = n_addr_list + (addr_list_sort_type << 24);
	fwrite(&word,sizeof(n_addr_list),1,f);
	for(ix=0; ix<n_addr_list; ix++)
	{
		fwrite(addr_list[ix],addr_list[ix]->length,1,f);
	}
	fclose(f);
}   /* end of save_addr_file1 */
#endif



void save_addr_file()
/*******************/
/* New Format for Pluto V3 */
{
	FILE *f;
	int ix;
	int len;
	int len2;
	ADDR_BOOK *a;
	char buf[256];

#ifdef deleted
	/* do checks on address data */
	for(ix=0; ix<n_addr_list; ix++)
	{
		if(addr_list[ix]->length > sizeof(ADDR_BOOK2))
		{
			werr(0,"Error in address book");
		}
	}
#endif

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

	fprintf(f,"$nitems %d\n",n_addr_list);
	fprintf(f,"$open %d\n",initial_open_folder);
	for(ix=0; ix<n_folders; ix++)
	{
		fprintf(f,"$folder %2d %2d %2d %s\n",folder[ix].folder_number,folder[ix].folder_sorting,0,folder[ix].folder_name);
	}
	fprintf(f,"$data\n");

	for(ix=0; ix<n_addr_list; ix++)
	{
		a = addr_list[ix];

		len2 = len = (ADDR_FIXED + a->comment + strlen(&a->data[a->comment]) + 1);
		if((len & 1)==0) len2++;   /* ensure length is odd */
		fwrite(&len2,2,1,f);          /* write out length */

		/* length must be odd, so with the added NL it's even - each
		   address book record must start on a two-byte boundary */

		fwrite(&a->offset,len-2,1,f);

		if((len & 1)==0)
		{
			fputc(0,f);   /* padding if needed */
		}
		fputc('\n',f);   /* separator to aid manual reading of address file */
	}
	fclose(f);
}   /* end of save_addr_file */





static void redraw_addr(int ix)
/*****************************/
{
	int line;

	line = ix - folder_base;
	if(line >= 0)
		redraw_blist_line(&blist_addr,line);
}   /* end of redraw_addr */



static void addrlist_clear()
/**************************/
{
	int  i;

	for(i=0; i<n_addr_list; i++)
	{
		if(addr_list[i]->selected)
		{
			addr_list[i]->selected = 0;
			redraw_addr(i);
		}
	}
}   /* end of addrlist_clear */







static wimp_menustr *make_folders_menu()
/**************************************/
{
	int  i;

	if(menu_folders != NULL)
		menu_dispose(&menu_folders,0);
	if(menu_folders2 != NULL)
		menu_dispose(&menu_folders2,0);

	menu_folders = menu_new("Folders","");
	menu_folders2 = menu_new("Folders","");

	for(i=0; i<n_folders; i++)
	{
		menu_extend(menu_folders,folder[i].folder_name);
		menu_extend(menu_folders2,folder[i].folder_name);
	}
	menu_extend(menu_folders2,"(ALL)");

	wmenu_folders2 = (wimp_menustr *)menu_syshandle(menu_folders2);
	return((wimp_menustr *)menu_syshandle(menu_folders));
}   /* end of name_folders_menu */





static void set_folder(int fldr)
/******************************/
{
	int  i;
	int  len;
	int  count;
	int  fld;
	int  base_set=0;
	char *name;
	wimp_icon *icon;

	if(fldr != current_folder)
		addrlist_clear();

	current_folder = fldr;

	if(fldr==n_folders)
	{
		current_folder_num = 0;
		name = "(ALL)";
	}
	else
	{
		current_folder_num = folder[fldr].folder_number;
		name = folder[fldr].folder_name;
	}

	icon = ((wimp_icon *)(blist_addr.button_template + 1)) + 2;  /* icon #2 */
	len = icon->data.indirecttext.bufflen;
	for(i=0; i<len; i++)
	{
		if((icon->data.indirecttext.buffer[i] = name[i])==0)
			break;
	}
	icon->data.indirecttext.buffer[len-1]=0;
	wimp_set_icon_state(blist_addr.w_buttons,2,0,0);  /* prod to redraw icon */

	/* adjust size of window to this folder */

	/* ensure it's sorted by folder */
	addr_list_sort_type = 0;    /* sort by folder */
	qsortG(addr_list,n_addr_list,sizeof(void *),addrlist_sorter);


	count = 0;
	folder_base = 0;
	for(i=0; i<n_addr_list; i++)
	{
		if((current_folder==n_folders) ||((fld = addr_list[i]->folder) == current_folder_num))
		{
			count++;

			if(!base_set)
			{
				folder_base = i;
				base_set = 1;
			}
		}
		else if(folder_nums[fld] == 0)
		{
			/* invalid folder number (or the default), set to default (zero) */
			addr_list[i]->folder = 0;
		}
	}
	blist_addr.n_entries = count;
	blist_set_extent(&blist_addr,4);
	sort_addr_list();
}   /* end of set_folder */




static void addrlist_delete(int addr_number)
/******************************************/
{
	int  i;

	if(addr_number >= n_addr_list)
		return;

	n_addr_list--;

	/* move subsequent entries down */
	for(i=addr_number; i<(n_addr_list); i++)
	{
		addr_list[i] = addr_list[i+1];
	}
}   /* end of addrlist_delete */





static void addrlist_delete2()
/****************************/
{
	int  count;
	int  i;

	count = 0;
	for(i=0; i<n_addr_list; i++)
	{
		if(addr_list[i]->selected)
		{
			count++;
			addrlist_delete(i);
			i--;    /* because they've moved down */
		}
	}
	if(count > 0)
		save_addr_file();
	else
		beep();

	set_folder(current_folder);
	blist_set_extent(&blist_addr,3);
}   /* end of addrlist_delete2 */




static int addrlist_check_unique(ADDR_BOOK2 *addr, int ignore)
/************************************************************/
/* ignore = -1
   ignore = -2  suppress error message
   ignore >= 0  ignore this address book entry (index) */
{
	char *alias;
	ADDR_BOOK *a;
	int  ix;
	char *message;
	char *name;
	char *fldr;
	int  reported=0;
	static char *button = "Continue";
	char buf[128];

	alias = &addr->data[addr->alias];

	if(ignore == -2)
	{
		for(ix=0; ix<n_addr_list; ix++)
		{
			if(strcmp_lc(addr->data,addr_list[ix]->data)==0)
			{
				a = addr_list[ix];
				if(strcmp_lc(&addr->data[addr->offset],&a->data[a->offset])==0)
				{
					/* match in name and URL. ignore */
					return(0);
				}
			}
		}
	}

	if((a = addr_lookup_url2(&addr->data[addr->offset],ignore)) != NULL)
	{
		fldr = folder[folder_nums[a->folder]].folder_name;

		/* compare Names */
		if(strcmp_lc(a->data,addr->data)==0)
		{
			if(ignore != -2)
			{
				sprintf(buf,"'%s <%s>' is already in address book '%s'",addr->data,&addr->data[addr->offset],fldr);
				if(query(buf,button)==0)
					return(0);
			}
			else
			{
				return(0);
			}
		}
		else
		{
			sprintf(buf,"%s '<%s>' is already in address book '%s' for '%s'",addr->data,&addr->data[addr->offset],fldr,a->data);
			if(query(buf,button)==0)
				return(0);
		}

		reported=1;
	}

	ix = -1;
	name="";
	if( a != NULL)
		name = a->data;

	if(alias[0] != 0)
	{
		if((ix = addr_list_lookup_name(alias,1,ignore,0)) >= 0)
		{
			message = "Alias";
			name = &a->data[a->alias];
		}
	}

	if(ix == -1)
	{
		if((ix = addr_list_lookup_name(addr->data,0,ignore,0)) >= 0)
			message = "Name";
	}

	if(ix >= 0)
	{
		a = addr_list[ix];

		if((strcmp_lc(&addr->data[addr->offset],&a->data[a->offset])==0) && (ignore == -2))
		{
			return(0);
		}

		if(reported==0)
		{
			fldr = folder[folder_nums[a->folder]].folder_name;

			sprintf(buf,"%s '%s' is already in address book '%s' as '%s'  Continue?",
					message,a->data,fldr,&a->data[a->offset]);
			if(query(buf,button)==0)
				return(0);
		}
	}
	return(-1);
}   /* end of addrlist_check_unique */




void addrlist_add_entry(ADDR_BOOK2 *addr, int list)
/*************************************************/
{
	ADDR_BOOK *a;
	int ignore = -1;

	if((addr->data[0] == 0) && (&addr->data[addr->alias] == 0))
	{
		beep();   /* no name or alias */
		return;
	}

	if(list)
		ignore = -2;

	if(addrlist_check_unique(addr,ignore) == 0)
		return;

	if(n_addr_list >= max_addr_list)
	{
		save_addr_file();
		load_addr_file();   /* allocate more space */
	}

	a = calloc(addr->length,1);
	if(a == NULL)
	{
		malloc_err(73);
		return;
	}

	addr_number = n_addr_list;
	addr_list[n_addr_list++] = a;
//   blist_addr.n_entries = n_addr_list;
	memcpy(addr_list[addr_number],addr,addr->length);
}   /* end of addrlist_add_entry */




static void addr_user_selected(int *hits)
/***************************************/
{
	if(hits[0] == 0)
		changed_user = 0;
	else
		changed_user = mbox_list[hits[0]-1]+1;
	dbox_setfield(dbox_addr,22,get_user_id3(changed_user));
}   /* end of addr_user_selected */



static void addr_sig_selected(int *hits)
/**************************************/
{
	char selected_sig[32];

	get_sig_name(hits[0],selected_sig);
	dbox_setfield(dbox_addr,23,selected_sig);
}   /* end of addr_sig_selected */



static void addr_folder_selected(int *hits)
/*****************************************/
{
	changed_folder = folder[hits[0]].folder_number;
	dbox_setfield(dbox_addr,25,folder[folder_nums[changed_folder]].folder_name);
}   /* end of addr_folder_selected */



static BOOL dbox_addr_raw_handler(dbox d, void *event, void *handle)
/******************************************************************/
{
	int bbits;
	wimp_mousestr *mouse;
	wimp_eventstr *e = event;
	ADDR_BOOK *a;
	char *p;
	char buf[128];
	char signame[20];

	a = (ADDR_BOOK *)handle;

	switch(e->e)
	{
	case wimp_EBUT:
		/* click on icon.  get icon number. */
		mouse = &e->data.but.m;
		bbits = mouse->bbits;

		switch(dbox_menu_field = mouse->i)
		{
		case 15:
		case 16:
		case 20:   /* comment fields */
			if(bbits & 0x1)   /* adjust click */
			{
				dbox_getfield(d,dbox_menu_field,buf,sizeof(buf));
				if((p=strstr(buf,"http://")) != NULL)
				{
					pass_url_to_browser(p);
				}
				return(TRUE);
			}
			break;

		case 22:   /* user menu */
			if(bbits & 7)
			{
				/* choose user_id */
				dbox_menu(make_users_menu(1),addr_user_selected,mouse);
			}
			return(TRUE);

		case 23:   /* signature menu, not for adjust */
			if(bbits & 6)
			{
				dbox_menu(sigs_make_menu(0,"(none)"),addr_sig_selected,mouse);
			}
			else
			{
				/* adjust-click, show the sig file */
				dbox_getfield(d,23,signame,sizeof(buf));
				sprintf(buf,"%s.Signatures.%s",path_choices,signame);
				if(signame[0] != 0)
					dataopen(buf,0xfff);
			}
			return(TRUE);

		case 25:    /* folder */
			dbox_menu(make_folders_menu(),addr_folder_selected,mouse);
			return(TRUE);
		}
	}
	return(help_handler(event,HELP_ADDRBOOK));
}   /* end of dbox_addr_raw_handler */




static void dbox_addr_handler_many(dbox d, void *handle)
/******************************************************/
{
	ADDR_BOOK *a;
	int  action;
	int  colr=0;
	int  flags;
	int  open=0;
	int  i;

	a = (ADDR_BOOK *)handle;

	action = dbox_get(d);

	switch(action)
	{
	case 0:   /* save */
		/* encrypt, sign, colour article list, replace with name */
		flags = (dbox_getnumeric(d,5) << 1) | dbox_getnumeric(d,6) |
				(dbox_getnumeric(d,18) << 2) | (dbox_getnumeric(d,19) << 3) |
				(dbox_getnumeric(d,17) << 4);

		for(i=0; i<8; i++)
		{
			if(dbox_getnumeric(d,7+i))
				colr = i;
		}

		for(i=0; i<n_addr_list; i++)
		{
			if(addr_list[i]->selected)
			{
				addr_list[i]->flags = flags;
				addr_list[i]->colr = colr;

				if(changed_folder > 0)
				{
					addr_list[i]->folder = changed_folder;
				}
				if(changed_user > 0)
				{
					addr_list[i]->user = changed_user;
				}
			}
		}

		set_folder(current_folder);
		save_addr_file();
		break;


	case 2:   /* delete address entry */
		break;

	}

	if(open)
		blist_set_extent(&blist_addr,2);


	if(dbox_persist() && (action == 0))
		return;

	dbox_hide(d);
}  /* end of dbox_addr_handler_many */




static void dbox_addr_handler(dbox d, void *handle)
/*************************************************/
{
	ADDR_BOOK *a;
	int  action;
	int colr;
	int  open=0;
	int  i;
	int  moved;
	char buf[244];
	char buf2[81];
	char buf3[81];
	ADDR_BOOK2 addr_book;

	a = (ADDR_BOOK *)handle;

	action = dbox_get(d);

	switch(action)
	{
	case 0:   /* save */
		memset(&addr_book,0,sizeof(addr_book));
		addr_book.folder = changed_folder;
		addr_book.user = changed_user;

		moved = 0;
		if(changed_folder != a_edit->folder)
		{
			moved = 1;
		}

		dbox_getfield(d,3,addr_book.data,sizeof(addr_book.data));   /* name */
		addr_book.alias = strlen(addr_book.data) + 1;

		dbox_getfield(d,1,buf,sizeof(buf));       /* alias */
		strcpy(&addr_book.data[addr_book.alias],buf);
		addr_book.offset = addr_book.alias + strlen(buf) + 1;

		dbox_getfield(d,4,buf,sizeof(buf));       /* url */
		strcpy(&addr_book.data[addr_book.offset],buf);
		addr_book.sig = addr_book.offset + strlen(buf) + 1;

		if((buf[0]==0) || isspace(buf[0]))
		{
			werr(0,"Email field is blank");
			break;
		}

		dbox_getfield(d,23,buf,sizeof(buf));      /* signature */
		strcpy(&addr_book.data[addr_book.sig],buf);
		i = addr_book.sig + strlen(buf) + 1;
		addr_book.comment = i;

		if(i > 255)
		{
			werr(0,"Too much data in this address record");
			break;    /* offset too large for 8 bit field */
		}

		dbox_getfield(d,15,buf,81);     /* comment */
		dbox_getfield(d,16,buf2,81);    /* comment, line 2 */
		dbox_getfield(d,20,buf3,81);    /* comment, line 3 */
		sprintf(buf,"%s\t%s\t%s",buf,buf2,buf3);
		strcpy(&addr_book.data[addr_book.comment],buf);
		addr_book.length = ADDR_FIXED + addr_book.comment + strlen(buf) + 1;

		/* encrypt, sign, colour article list, replace with name */
		addr_book.flags = (dbox_getnumeric(d,5) << 1) | dbox_getnumeric(d,6) |
						  (dbox_getnumeric(d,18) << 2) | (dbox_getnumeric(d,19) << 3) |
						  (dbox_getnumeric(d,17) << 4);

		addr_book.wordwrap = dbox_getnumeric(d,24);

		for(colr=0; colr<8; colr++)
		{
			if(dbox_getnumeric(d,7+colr))
				addr_book.colr =  colr;
		}

		if(addr_number >= 0)
		{
			/* update this entry */
			if(addrlist_check_unique(&addr_book,addr_number)==0)
				break;

			if(addr_book.length > addr_list[addr_number]->length)
			{
				a = malloc(addr_book.length);
				if(a == NULL)
				{
					malloc_err(72);
					break;
				}
				addr_list[addr_number] = a;
			}
			memcpy(addr_list[addr_number],&addr_book,addr_book.length);
		}
		else
		{
			/* add new entry */
			moved = 1;
			addrlist_add_entry(&addr_book,0);
		}

		if(moved)
			set_folder(current_folder);

		save_addr_file();
		open = 1;
		break;

	case 2:   /* delete address entry */
		if(addr_number >= 0)
		{
			addrlist_delete(addr_number);
			set_folder(current_folder);
			save_addr_file();
			open = 1;
		}
		break;

	}

	if(open)
		blist_set_extent(&blist_addr,2);


	if(dbox_persist() && (action == 0))
		return;

	dbox_hide(d);
}   /* end of dbox_addr_handler */



static void addrlist_edit(ADDR_BOOK *a, int many)
/***********************************************/
{
	int  i;
	char *p;
	char *p2;
	char buf[244];

	static char fade_fields[] = {1,2,3,4,15,16,20,23,0};
	static char clear_fields[] = {1,3,4,15,16,20,22,23,25,0};

	a_edit = a;

	if(many <= 1)
		dbox_eventhandler(dbox_addr, dbox_addr_handler, (void *)a);
	else
		dbox_eventhandler(dbox_addr, dbox_addr_handler_many, (void *)a);

	dbox_raw_eventhandler(dbox_addr,dbox_addr_raw_handler,(void *)a);

	for(i=1; i<=4; i++)
		dbox_unfadefield(dbox_addr,i);

	for(i=5; i<=14; i++)
		dbox_setnumeric(dbox_addr,i,0);  /* de-select the rest */

	if(a != NULL)
	{
		changed_folder = a->folder;
		changed_user = a->user;

		dbox_setfield(dbox_addr,1,&a->data[a->alias]);
		dbox_setfield(dbox_addr,3,a->data);
		dbox_setfield(dbox_addr,4,&a->data[a->offset]);
		dbox_setfield(dbox_addr,23,&a->data[a->sig]);

		if(a->user == 0)
			dbox_setfield(dbox_addr,22,"(none)");
		else
			dbox_setfield(dbox_addr,22,options.mailbox[a->user-1].email_addr);

		strcpy(buf,&a->data[a->comment]);
		dbox_setfield(dbox_addr,16,"");
		dbox_setfield(dbox_addr,20,"");

		if((p = strchr(buf,'\t')) != NULL)  /* split comment field at NL */
		{
			*p++ = 0;

			if((p2 = strchr(p,'\t')) != NULL)
			{
				*p2++ = 0;
				dbox_setfield(dbox_addr,20,p2);
			}
			dbox_setfield(dbox_addr,16,p);
		}
		dbox_setfield(dbox_addr,15,buf);

		dbox_setnumeric(dbox_addr,7+a->colr,1);

		dbox_setnumeric(dbox_addr,5,a->flags & 2);    /* encrypt */
		dbox_setnumeric(dbox_addr,6,a->flags & 1);    /* clearsign */
		dbox_setnumeric(dbox_addr,18,a->flags & 4);   /* colour article list */
		dbox_setnumeric(dbox_addr,19,a->flags & 8);   /* replace with name */
		dbox_setnumeric(dbox_addr,17,a->flags & 0x10); /* lock log copy */

		if(a->wordwrap == 0)
			dbox_setfield(dbox_addr,25,"");
		else
			dbox_setnumeric(dbox_addr,24,a->wordwrap);

		dbox_setfield(dbox_addr,25,folder[folder_nums[a->folder]].folder_name);
	}
	else
	{
		p = clear_fields;
		while(*p != 0)
			dbox_setfield(dbox_addr,*p++,"");

		changed_user = 0;

		if(many==0)
		{
			changed_folder = folder[current_folder].folder_number;
			dbox_setfield(dbox_addr,25,folder[current_folder].folder_name);
		}

		if(many > 1)
		{
			dbox_setfield(dbox_addr,3,"Changes apply to all selected entries");
			p = fade_fields;
			while(*p != 0)
				dbox_fadefield(dbox_addr,*p++);
		}
		else
		{
			dbox_setfield(dbox_addr,3,"");
			dbox_setnumeric(dbox_addr,7,1);   /* default colr 0 */
		}
	}
	dbox_hide(dbox_addr);
	dbox_showstatic(dbox_addr);
}   /* end of addrlist_edit */




static void addrlist_edit2()
/**************************/
/* Apply properties to all selected entries */
{
	int  i;
	int  found = -1;
	int count=0;
	ADDR_BOOK *a;

	for(i=0; i<n_addr_list; i++)
	{
		if(addr_list[i]->selected)
		{
			a = addr_list[i];
			found = i;
			count++;
		}
	}

	if(count == 0)
	{
		beep();
		return;
	}

	if(count == 1)
	{
		addr_number = found;
		addrlist_edit(a,1);
	}
	else
	{
		/* more than selected item. Only allow change to properties which
		   can apply to all */
		addrlist_edit(NULL,2);
	}

}   /* end of addrlist_edit2 */


static void selected_folder(int *hits)
/************************************/
{
	set_folder(hits[0]);
}   /* end of selected_folder */




static menu addrlist_menu_maker(void *handle)
/*******************************************/
{
	int  ix;
	int  count=0;
	int  fade =0;

	/* are there any selected items in this list */
	for(ix=0; ix<n_addr_list; ix++)
	{
		if(addr_list[ix]->selected)
		{
			count = 1;
			break;
		}
	}

	blist_current_menu = NULL;
	if(count == 0)
	{
		/* no items selected, menu button selects one */
		/* get line number of last muse click */
		if((blist_current_menu_line = list_get_linenum(blist_addr.w_list)) < blist_addr.n_entries)
		{
			addr_list[blist_current_menu_line+folder_base]->selected = 1;
			redraw_blist_line(&blist_addr,blist_current_menu_line);

			blist_current_menu = menu_addrlist;
			blist_current_menu_blist = BLIST_ADDR;
			count = 1;
		}
	}

	menu_folder_new[0]=0;
	strncpy0(menu_folder_rename,folder[current_folder].folder_name,sizeof(menu_folder_rename));

	/* set list of folders */
	menu_submenu(menu_addrlist,1,0);
	menu_submenu(menu_selection,2,0);

	make_folders_menu();
	menu_submenu(menu_selection,2,menu_folders);
	menu_submenu(menu_selection,3,menu_folders);
	menu_submenu(menu_addrlist,1,menu_selection);
	menu_submenu(menu_addrlist,8,menu_folders);
	if(count==0)
		fade=1;
	menu_setflags(menu_addrlist,1,0,fade);
	return(menu_addrlist);
}   /* end of addrlist_menu_maker */




void addrlist_select(int line, int selected)
/**************************************/
{
	if((line < 0) || (line >= blist_addr.n_entries))
		return;

	if(selected == 2)
		addr_list[line+folder_base]->selected ^= 1;
	else
		addr_list[line+folder_base]->selected = selected;
	redraw_blist_line(&blist_addr,line);
}   /* end of addrlist_select */



static void addrlist_move(int value, int copy)
/********************************************/
{
	int  count;
	int  i;
	ADDR_BOOK *a;
	ADDR_BOOK *a2;

	count = 0;
	for(i=0; i<n_addr_list; i++)
	{
		a = addr_list[i];

		if(a->selected)
		{
			count++;

			if(copy)
			{
				if((a2 = malloc(a->length)) == NULL)
				{
					malloc_err(75);
					break;
				}
				else
				{
					memcpy(a2,a,a->length);
					a2->selected = 0;
					a2->folder = value;

					if(n_addr_list >= max_addr_list)
					{
						save_addr_file();
						load_addr_file();   /* allocate more space */
					}

					addr_list[n_addr_list++] = a2;
				}
			}
			else
			{
				/* set folder */
				a->folder = value;
			}
		}
	}
	if(count > 0)
	{
		set_folder(current_folder);
		save_addr_file();
	}
	else
		beep();

	addrlist_clear();
	blist_set_extent(&blist_addr,3);
}   /* end of addrlist_move */





static BOOL addrlist_export_saver(char *filename, void *handle)
/*************************************************************/
{
	FILE *f;
	int  ix;
	ADDR_BOOK *a;
	int  count=0;

	f = fopen(filename,"w");
	if(f == NULL)
	{
		werr(0,"Can't write to '%s'",filename);
		return(FALSE);
	}

	for(ix=0; ix<n_addr_list; ix++)
	{
		a = addr_list[ix];

		if(a->selected)
		{
			if(strchr(a->data,',')!=NULL)
				fprintf(f,"\"%s\"",a->data);
			else
				fprintf(f,"%s",a->data);
			fprintf(f,", %s, %s\n",&a->data[a->offset],&a->data[a->alias]);

			count++;
		}
	}
	fclose(f);
	set_filetype(filename,0xdfe);

	if(count==0)
		werr(0,"No items are selected");
	return(TRUE);
}   /* end of addrlist_export */





static BOOL addrlist_export_handler(dbox dbox_save, void *event, void *handle)
/*************************************************************************/
{
	wimp_eventstr *e;

	e = (wimp_eventstr *)event;

	/* look for mouse drag event */
	switch (e->e)
	{
	case wimp_EBUT:
		if(e->data.but.m.bbits & 0x50)
		{
			dbox_getfield(dbox_save,2,addr_filename,sizeof(addr_filename));
			xfersend(0xfff,addr_filename,0,addrlist_export_saver,NULL,0,e,NULL);
			return(TRUE);
		}
	}
	return(FALSE);
}   /* end of addrlist_export_handler */




static void addrlist_export(int from_menu)
/****************************************/
{
	dbox dbox_save;

	dbox_save = dbox_new("SaveAs");
	dbox_raw_eventhandler(dbox_save,addrlist_export_handler,NULL);
	dbox_setfield(dbox_save,2,addr_filename);

	set_filetype_icon(dbox_save,3,0xdfe);

	for(;;)
	{
		dbox_show(dbox_save);

		if(!from_menu)
			set_dbox_at_pointer(dbox_save);


		if(dbox_fillin(dbox_save)==0)
		{
			dbox_getfield(dbox_save,2,addr_filename,sizeof(addr_filename));
			addrlist_export_saver(addr_filename,NULL);
			if(dbox_persist()) continue;
		}
		break;
	}
	dbox_dispose(&dbox_save);
}   /* end of addrlist_export */




static void addrlist_search(int reset)
/************************************/
{
	int  ix;
	int  len;
	int  length;
	char *p;
	dbox d;
	ADDR_BOOK *a;
	static int  case_sensitive;
	static int  regex;
	static int  start=0;
	static char string[80];

	if(start >= n_addr_list)
		start = 0;

	if(reset)
	{
		start = 0;
		d = dbox_new("Find");
		dbox_setfield(d,2,string);
		dbox_show(d);
		if(dbox_fillin(d) < 0)
		{
			dbox_dispose(&d);
			return;
		}
		dbox_getfield(d,2,string,sizeof(string));
		if((case_sensitive = dbox_getnumeric(d,3)) == 0)
			strcpy_lc(string,string);

		if(dbox_getnumeric(d,4))
			regex = -1;
		else
			regex = 0;

		dbox_dispose(&d);
	}

	addrlist_clear();

	len = strlen(string);
	for(ix=start; ix<n_addr_list; ix++)
	{
		p = addr_list[ix]->data;
		length = addr_list[ix]->length - ADDR_FIXED;

		if(search_regex(string,len,&p,0,length,case_sensitive,regex,NULL)>=0)
			break;
	}

	if(ix < n_addr_list)
	{
		/* found a match */
		a = addr_list[ix];
		set_folder(folder_nums[a->folder]);  /* select the folder which contains this entry */
		a->selected = 1;
		redraw_addr(ix);

		start = ix+1;
		scroll_to_y2(blist_addr.w_list,-ix*lists_line_height,-(ix+2)*lists_line_height);
		set_focus(blist_addr.w_list);
	}
	else
	{
		beep();
		start = 0;
	}
}   /* end of addrlist_search */



static void addrlist_search_letter(int letter)
/********************************************/
{
	int  ix;
	int  ix1;
	char *p;

	addrlist_clear();

	for(ix=0; ix<n_addr_list; ix++)
	{
		p = addr_list[ix]->data;

		if(addr_list_sort_type == 1)
		{
			p = &addr_list[ix]->data[addr_list[ix]->alias];
		}
		else if(addr_list_sort_type == 3)
		{
			ix1 = strlen(p);
			while(--ix1 > 0)
			{
				if(addr_list[ix]->data[ix1] <= ' ')
				{
					ix1++;
					break;
				}
			}
			p = &p[ix1];
		}
		else if(addr_list_sort_type == 4)
		{
			p = &addr_list[ix]->data[addr_list[ix]->offset];
		}

		if(tolower(p[0]) >= letter)
			break;
	}
	if(ix < n_addr_list)
	{
		addr_list[ix]->selected = 1;
		redraw_addr(ix);

		scroll_to_y2(blist_addr.w_list,-ix*lists_line_height,-(ix+2)*lists_line_height);
	}
	else
	{
		beep();
	}
}   /* end of addrlist_search_letter */




static void addrlist_update_lists(int action)
/*******************************************/
{
	FOLDREC *fr;
	CARD *cptr;
	ADDR_BOOK *a;
	int  ix;
	char *p;
	char *p2;
	char *p3;
	unsigned int *ixlist;
	int  entry;
	int  colour;
	int  found;
	CARD_EXPANDED cardex;
	char buf[128];

	if(check_lock_lists(0xffff) != 0)
		return;

	set_lock_lists(3,NULL);
	fr = &list_fr[0];

	visdelay2_begin();

	/* sort by file address */
	if(article_make_index(&list_fr[0],&ixlist)==NULL)
		return;

	for(ix=0; ix<fr->n_entries; ix++)
	{
		cptr = (CARD *)((ixlist[ix] & IXLIST_MASK) << 2);
		p = &cptr->data[cptr->d_author];

		if((ix & 0x1f) == 0)
			call_event_process(0);

		/* extract URL from address */
		buf[0]=0;
		p2 = strchr(p,'<');
		if(p2 != NULL)
		{
			p2++;
			if((p3 = strchr(p2,'>')) != NULL)
			{
				memcpy(buf,p2,p3-p2);
				buf[p3-p2]=0;
			}
		}

		colour = 0;  /* default is black */
		found = 0;

		for(entry=0; entry<n_addr_list; entry++)
		{
			a = addr_list[entry];

			if((strcmp_lc(buf,&a->data[a->offset])==0) || (strcmp_lc(p,a->data) == 0))
			{
				found = 1;

				/* match on name or address */
				unpack_card(cptr,&cardex);

				if(a->flags & 0x4)
					colour = addr_colr_trans[a->colr & 0xf];
				else
					colour = 0;

				if(a->flags & 0x8)
				{
					if(strcmp(cardex.author,a->data) != 0)
					{
						/* replace address by name */
						strcpy(cardex.author,a->data);
						found = 2;
					}
				}
				else
				{
					/* should we replace Name by Address if "replace" option is not set,
					   problem is we've lost the original name, unless we look in the
					   internet header */
				}

				if(found == 2)
				{
					/* change the Author field, and perhaps the colour, cardsize may change */
					/* change of just the colour is dealt with below */
					cardex.colour = colour;
					cardfile_update(fr,NULL,&cardex,0);
					visdelay2_percent((ix*100)/fr->n_entries);
				}
				break;
			}
		}

		if((found < 2) && (((cptr->alength >> 24) & 0x7) != colour))
		{
			cptr->alength = (cptr->alength & ~COLR_MASK) | (colour << 24);
			article_update(fr,cptr,NULL,0,0);
			visdelay2_percent((ix*100)/fr->n_entries);
		}
	}

	visdelay2_end();
	clear_lock_lists(3);
	flex_free((flex_ptr)&ixlist);
}   /* end of addrlist_update_lists */




static void folder_menu_proc(int action)
/**************************************/
{
	int  ix;
	char *p;
	FOLDER *fldr;
	char buf[256];

	fldr = &folder[current_folder];

	switch(action)
	{
	case 1:    /* New folder */
		/* find free fixed folder number */
		if(n_folders >= (N_FOLDERS-1))
		{
			werr(0,"No more folders available");
			return;
		}
		fldr = &folder[n_folders++];

		for(ix=1; ix<N_FOLDERS; ix++)
		{
			if(folder_nums[ix]==0)
			{
				fldr->folder_number = ix;
				break;
			}
		}

		fldr->folder_sorting = folder[0].folder_sorting;

		if((p = (char *)malloc(strlen(menu_folder_new)+1)) != NULL)
		{
			strcpy(p,menu_folder_new);
			fldr->folder_name = p;
		}
		set_folder(n_folders-1);
		sort_folders();
		break;

	case 2:    /* Rename folder */
		if((p = (char *)malloc(strlen(menu_folder_rename)+1)) != NULL)
		{
			strcpy(p,menu_folder_rename);
			free(fldr->folder_name);
			fldr->folder_name = p;
		}
		set_folder(current_folder);  /* to update the display */
		sort_folders();
		break;

	case 3:    /* Delete folder */
		if(current_folder==0)
		{
			werr(0,"Can't delete default folder '%s'",fldr->folder_name);
			return;
		}
		sprintf(buf,"Delete '%s' and move its contents to '%s'?",fldr->folder_name,folder[0].folder_name);
		if(query(buf,"DELETE")==0)
			return;

		for(ix=0; ix<n_addr_list; ix++)
		{
			if(addr_list[ix]->folder == current_folder_num)
			{
				addr_list[ix]->folder = 0;
			}
		}
		if(fldr->folder_number == initial_open_folder)
			initial_open_folder = 0;

		folder_nums[fldr->folder_number] = 0;
		free(fldr->folder_name);

		/* move others down to close the gap */
		for(ix=current_folder+1; ix<n_folders; ix++)
		{
			memcpy(&folder[ix-1],&folder[ix],sizeof(FOLDER));
		}
		n_folders--;

		set_folder(0);
		break;
	}
	make_folders_menu();
	save_addr_file();
}   /* end of folder_menu_proc */



static void addrlist_menu_proc(void *handle, char *hits)
/******************************************************/
{
	int  i;

	switch(hits[0])
	{
	case 1:   /* selection */
		switch(hits[1])
		{
		case 1:
			addrlist_edit2();
			break;

		case 2:   /* move to folder */
			addrlist_move(folder[hits[2]-1].folder_number,0);
			break;

		case 3:   /* copy to folder */
			addrlist_move(folder[hits[2]-1].folder_number,1);
			break;

		case 4:
			addrlist_clear();
			break;

		case 5:
			addrlist_delete2();
			break;
		}
		break;

	case 2:   /* select all */
		for(i=0; i<blist_addr.n_entries; i++)
			addr_list[folder_base+i]->selected = 1;
		redraw_blist(&blist_addr);
		return;        /* don'tdeselect line selected by menu-click */

	case 3:  /* Folder */
		folder_menu_proc(hits[1]);
		break;

	case 4:   /* sort */
		if(current_folder==n_folders)
			folder[0].folder_sorting = hits[1];
		else
			folder[current_folder].folder_sorting = hits[1];
		sort_addr_list();
		redraw_blist(&blist_addr);
		save_addr_file();
		addrlist_clear();
		return;

	case 5:   /* export */
		if(hits[1])
		{
			addrlist_export(1);
			return;
		}
		break;

	case 6:  /* Find */
		addrlist_search(1);
		break;

	case 7:
		addrlist_update_lists(hits[1]);
		break;

	case 8:
		initial_open_folder = folder[hits[1]-1].folder_number;
		save_addr_file();
		break;
	}
	if(blist_current_menu == menu_addrlist)
	{
		addrlist_select(blist_current_menu_line,0);
	}
}   /* end of addrlist_menu_proc */






void addrlist_click_buttons(BLIST *b, wimp_eventdata *d)
/*****************************************************/
/* Click on the address-list button bar */
{
	int  icon;
	int  click;
	wimp_mousestr *mouse;

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

	switch(icon)
	{
	case 3:   /* add */
		addr_number = -1;
		addrlist_edit(NULL,0);
		break;

	case 1:   /* delete */
		addrlist_delete2();
		break;

	case 4:   /* select folder */
		dbox_menu(wmenu_folders2,selected_folder,mouse);
		break;

	case 5:   /* edit */
		addrlist_edit2();
		break;
	}
}   /* end of addrlist_click_buttons */





static void addrlist_add_to_field2(dbox d,int field,ADDR_BOOK *a)
/***************************************************************/
{
	char *addr;
	char *param;
	ARTICLE_OUT *art_out;
	char buf[128];

	addr = &a->data[a->offset];
	if((param = strchr(addr,'?')) != NULL)
	{
		/* the address url contains parameters */
		art_out = calloc(1,sizeof(ARTICLE_OUT));
		get_url_parameters(art_out,addr,param,NULL);
		if(art_out->subject[0] != 0)
			dbox_setfield(d,8,art_out->subject);   /* Subject field */

		reply_add_to_field(d,field,art_out->subject);
		free(art_out);
		return;
	}

	if((options.addr_expand == 0) && (akbd_pollsh() == 0))
	{
		if(a->data[0] != 0)
		{
			if(addr_list_lookup_count(a->data,NULL,NULL) == 1)
			{
				addr = a->data;   /* use Name if present and not ambiguous */
			}
			else
			{
				/* ambiguous, use name and address */
				sprintf(buf,"%s <%s>",a->data,addr);
				addr = buf;
			}
		}
	}
	reply_add_to_field(d,field,addr);

	/* if a TEXTR was specified, set the user and signature if specified
	   for this address book entry */
	if(addr_insert_textr != NULL)
	{
		reply_addrbook_sig(addr_insert_textr,a);
	}
}   /* end of addrlist_add_to_field2 */




void addrlist_click_window(BLIST *b,int linenum,int bbits)
/********************************************************/
{
	int  close=0;
	char *name;
	char *addr;
	TEXTR *t;
	ADDR_BOOK *a;

	linenum += folder_base;

	a = addr_list[linenum];
	name = a->data;
	addr = &a->data[a->offset];

	if(addr_insert_dbox)
	{
		switch(bbits)
		{
		case wimp_BCLICKLEFT:    /* select */
			close = 1;
		case wimp_BCLICKRIGHT:   /* adjust */
			/* append the address to the specified dbox field */
			addrlist_add_to_field2(addr_insert_dbox,addr_insert_field,addr_list[linenum]);

			if(close)
			{
				wimp_close_wind(b->w_buttons);
				wimp_close_wind(b->w_list);
			}
			break;
		}
		return;
	}

	switch(bbits)
	{
	case wimp_BCLICKLEFT:    /* select */
		addrlist_clear();
		addr_list[linenum]->selected = 1;
		redraw_addr(linenum);
		break;

	case wimp_BCLICKRIGHT:   /* adjust */
		addr_list[linenum]->selected ^= 1;   /* toggle */
		redraw_addr(linenum);
		break;

	case wimp_BLEFT:    /*select double click, add to To field */
		addr_list[linenum]->selected = 0;
		redraw_addr(linenum);

		t = reply_data_find(3);   /* only one and mail */
		if(t != NULL)
			addrlist_add_to_field2(t->dbox_card,9,a);
		else
		{
			if((strchr(addr,'?') != NULL) || (addr_list_lookup_count(name,NULL,NULL) != 1))
				t = new_reply(NULL,addr,NULL,NULL,0,NULL);  /* contains ?Subject= or similar, or ambiguous name */
			else
				t = new_reply(NULL,name,NULL,NULL,0,NULL);
		}
		reply_addrbook_sig(t,a);
		break;

	case wimp_BRIGHT:   /* adjust double click, edit address list entry */
		addrlist_clear();
		addr_number = linenum;
		addrlist_edit(addr_list[addr_number],1);
		break;
	}
}   /* end of addrlist_click_window */




void addrlist_key_window(BLIST *b,wimp_eventdata *d)
/**************************************************/
{
	int  c;
	int  i;

	switch(c = d->key.chcode)
	{
	case F_KEY+4:
		addrlist_search(1);
		break;

	case F_KEY_SHIFT+4:
		addrlist_search(0);
		break;

	case 01:   /* CTRL-A, select all */
		for(i=0; i<n_addr_list; i++)
			addr_list[i]->selected = 1;
		redraw_blist(b);
		break;

	case 24:   /* CTRL-X */
		addrlist_delete2();
		break;

	case 26:   /* CTRL-Z */
		addrlist_clear();
		break;

	default:
		if(isalpha(c))
		{
			addrlist_search_letter(tolower(c));
		}
		else
		{
			wimp_processkey(c);
		}
		break;
	}
}   /* end of addrlist_key_window */





void addrlist_open(int at_pointer)
/*****************************/
{
	if(at_pointer)
		at_pointer = 8;

	addr_insert_dbox = NULL;
	blist_set_extent(&blist_addr,3+at_pointer+0x10);
	set_folder(current_folder);
}   /* end of addr_open */



void addrlist_import(char *fname, int filetype)
/*********************************************/
{
	FILE *f;
	char *url;
	char *name;
	char *alias;
	char *p;
	int  c;
	int  marcel=0;
	char buf[256];
	ADDR_BOOK2 addr;

	strcpy(buf,fname);   /* save across wimp poll */

	if(query("Import file into Address Book ?",NULL)==0)
		return;

	f = fopen(buf,"r");
	if(f==NULL)
		return;

	fgets(buf,sizeof(buf),f);
	if(memcmp(buf,"# MarcelRC:aliases",18)==0)
		marcel = 1;

	rewind(f);
	while(!feof(f))
	{
		alias = NULL;

		if(fgets(buf,sizeof(buf),f) == NULL)
			break;

		if(filetype == FILETYPE_HTML)
		{
			/* Marcel Hotlist */

			if(memcmp(buf,"<li><a href=\"mailto:",20)!=0)
				continue;

			url = &buf[20];
			name = strchr(url,'"');
			if(name == NULL)
				continue;

			*name = 0;
			name += 2;
			p = strchr(name,'<');
			if(p == NULL)
				continue;

			*p = 0;
		}
		if(marcel==1)
		{
			if(memcmp(buf,"USER=",5) != 0)
				continue;

			if((url = strstr(buf,"ADDR=")) == NULL)
				continue;
			url += 6;
			p = strchr(url,'"');
			*p = 0;

			if((name = strstr(buf,"NAME=")) == NULL)
				continue;
			name += 6;
			p = strchr(name,'"');
			*p = 0;

			if((alias = strstr(buf,"USER=")) == NULL)
				continue;
			alias += 6;
			p = strchr(alias,'"');
			*p = 0;
		}
		else
		{
			url = strpbrk(buf,",\t");
			if(url == NULL)
				continue;
			name = buf;

			buf[strlen(buf)-1] = 0;
			*url++ = 0;

		}

		while(((c = *url) <= ' ') && (c != 0))  url++;  /* skip leading blanks */
		memset(&addr,0,sizeof(addr));
		strcpy(addr.data,name);

		/* Is this alias (if specified) or name (if not) already in the address book ? */
		if(alias != NULL)
		{
			if(addr_list_lookup_name(alias,1,-1,0) >= 0)
				continue;
		}
		else
		{
			if(addr_list_lookup_name(name,0,-1,0) >= 0)
				continue;
		}

		if(alias == NULL)
			alias = "";

		addr.alias = strlen(name) + 1;
		strcpy(&addr.data[addr.alias],alias);

		addr.sig = addr.alias + strlen(alias) + 1;
		addr.offset = addr.sig + 1;
		strcpy(&addr.data[addr.offset],url);

		addr.comment = (addr.offset + strlen(url) + 1);
		addr.length = addr.comment + ADDR_FIXED + 1;

		addrlist_add_entry(&addr,1);
	}
	fclose(f);

	save_addr_file();
	set_folder(current_folder);
	blist_set_extent(&blist_addr,3);
}   /* end of addrlist_import */





void addrlist_add_email(char *address, int control)
/*************************************************/
/* Add a standard email address to the address list */
/* Control 0=single entry, 1=start list, 2=list entry, 3=end list */
{
	char *p;
	int  i;
	int  c;
	char *name_start;
	char buf[80];
	char url[80];
	char *name = "";
	char *old_url;
	ADDR_BOOK *a;

	ADDR_BOOK2 addr;

	if(control==1)
	{
		addrlist_clear();   /* clear any selection */
	}

	if(address != NULL)
	{
		if(strchr(address,'@') == NULL)
		{
			if(control == 0)
				werr(0,"'%s' is not an email address, no @ symbol",address);
			return;
		}

		while((*address <= ' ') && (*address != 0))
			address++;   /* skip leading blanks */

		p=buf;
		for(i=0; i<sizeof(buf); i++)
		{
			if((c = address[i]) != '"')
				*p++ = c;
			if(c == 0)
				break;
		}
		buf[sizeof(buf)-1]=0;

		p = reply_extract_email_addr(buf,1);
		strcpy(url,p);

		name_start = buf;
		if(*name_start == '<') name_start++;
		p = strpbrk(name_start,"<@");
		if((p != NULL) && (p != buf))
		{
			while((--p > name_start) && (*p <= ' '));   /* strip trailing spaces */
			p[1] = 0;
			name = name_start;
		}

		memset(&addr,0,sizeof(addr));
		strcpy(addr.data,name);
		addr.alias = strlen(name)+1;
		addr.sig = addr.alias+1;
		addr.offset = addr.sig+2;
		strcpy(&addr.data[addr.offset],url);
		addr.comment = addr.offset + strlen(url) + 1;
		addr.length = ADDR_FIXED + addr.comment+1;

		if(name[0]==0)
			return;   /* no name given */

		addr_number = addr_list_lookup(name);
		if(addr_number >= 0)
		{
			a = addr_list[addr_number];
			old_url = &a->data[a->offset];

			if(strcmp(old_url,url)!=0)
			{
				if(control == 0)
				{
					sprintf(url,"'%s' is already in the Address Book as %s",name,addr_lookup(name));
					if(query(url,NULL)!=0)
					{
						addr_number = -1;
						addrlist_edit((ADDR_BOOK *)&addr,1);
					}
					return;
				}
				return;
			}
			/* else overwrite with new address */
		}

		if(control==0)
		{
			addrlist_edit((ADDR_BOOK *)&addr,1);
		}
		else
		{
			addr.selected = 1;
			addrlist_add_entry(&addr,1);
		}
	}

	if(control==3)
	{
		/* end of list */
		addrlist_edit2();    /* edit all selected entries */
	}
}   /* end of addrlist_add_email */




void address_insert(TEXTR *t, dbox d, int field)
/**********************************************/
{
	addrlist_clear();
	addrlist_open(1);
	addr_insert_textr = t;
	addr_insert_dbox = d;
	addr_insert_field = field;
}  /* end of address_insert */






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

void init_addr(void)
/******************/
{
	menu m2;
	menu m_fldrnew;
	menu m_fldrrename;

	blist_register(&blist_addr,BLIST_ADDR,"AddrBar");
	menu_addrlist = menu_new("Address Book",menustr_addrlist);
	menu_selection = menu_new("Selection",menustr_addrlist_select);
	menu_fldractn = menu_new("Folder",menustr_addrlist_fldractn);

	m_fldrnew = menu_new("New Folder","x");
	m_fldrrename = menu_new("Rename","x");
	menu_make_writeable(m_fldrnew,1,menu_folder_new,sizeof(menu_folder_new),"");
	menu_make_writeable(m_fldrrename,1,menu_folder_rename,sizeof(menu_folder_rename),"");
	menu_submenu(menu_fldractn,1,m_fldrnew);
	menu_submenu(menu_fldractn,2,m_fldrrename);

	/*   menu_submenu(menu_selection,4,menu_colours); */
	menu_submenu(menu_addrlist,1,menu_selection);
	menu_submenu(menu_addrlist,3,menu_fldractn);

	m2 = menu_new("Sort",menustr_addrlist_sort);
	menu_submenu(menu_addrlist,4,m2);

	event_attachmenumaker(blist_addr.w_list,addrlist_menu_maker,addrlist_menu_proc,(void *)NULL);

	folder[0].folder_name = "Default";
	folder[0].folder_sorting = 0;
	folder[0].folder_number = 0;
	n_folders = 1;

	load_addr_file();

	dbox_addr = dbox_new("Addr");
	make_folders_menu();


}   /* end of init_addr */
