
/***************************************************************************
 *   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 <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#include "dbox.h"
#include "flex.h"
#include "werr.h"
#include "wimp.h"
#include "win.h"
#include "menu.h"
#include "msgs.h"

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

#ifdef MemCheck
#include "MemCheck:Flex.h"
#endif




extern int card_size(CARD *cptr);
extern FOLDREC *list_new_window(void);
extern FOLDREC *subset_new(char *name,FOLDREC *parent,int count);

extern wimp_menustr *category_make_menu(FOLDREC *fr,int control);
extern int category_menu_hit(int *hits, char **cat_name);
extern char *decode_date(int date,int timeflag);

extern FOLDREC cty_fr;
extern FOLDREC list_fr[N_LISTS];
extern BOX box_table[N_BOXES];
extern int timezone_offset;
extern OPTIONS options;
extern char chars_lc[256];
extern wimp_menustr *wmenu_subset_status;
extern FOLDREC *search_cancel_flag;
extern int  search_cancel_reason;

static int  next_subset = 1;
dbox dbox_subset = 0;

#define N_SUBSET_CATS  32
static int subset_n_cats = 0;
static short subset_cats[N_SUBSET_CATS];
static char *cats_set = NULL;     /* bit map of categories */
static int n_cats_set = 0;        /* size of category bit map */
static int  date_from;
static int  date_to;
static char date_from_buf[12];
static char date_to_buf[12];
static int  su_dialogue_type;
static int  search_options=NULL;
static int  subset_status_match = 0;

static char *subset_status_string[] = {
	"All","Unread","Read","Locked","Marked","Lock/Mark","Read/L/M","Replied","Outgoing"
};

int search_case_sensitive_open = 0;
int search_regex_flag_open = 0;
int search_regex_flag = 0;
char search_text_string[128] = {0};      /* for subset dialogue box */
char search_text_string_open[128] = {0};


/* icon numbers in subset dialogue box */
#define SU_NEW       1
#define SU_STRING    2
#define SU_AUTHOR    3
#define SU_TITLE     4
#define SU_COMMENT   5
#define SU_KEYS      6
#define SU_BODY      7
#define SU_HEADER    8
#define SU_CASE      9
#define SU_SELECTION 10
#define SU_MORE      11
#define SU_DATE_FROM 12
#define SU_DATE_TO   13
#define SU_KEEP     14
#define SU_REMOVE    15
#define SU_REGEX       16
#define SU_ADD_FROM  17
#define SU_NCHARS    18
#define SU_CATS      19
#define SU_SEARCH_ALL 20
#define SU_ALL       21
#define SU_ADD       22
#define SU_SIGNATURE 23
#define SU_STATUS    28



static void bit_set(char *bitmap, int index)
/************************************/
{
	bitmap[index >> 3] |= (1 << (index & 0x7));
}   /* end of bit_set */


static void bit_clear(char *bitmap, int index)
/*************************************/
{
	bitmap[index >> 3] &= ~(1 << (index & 0x7));
}   /* end of bit_clear */


static int bit_test(char *bitmap, int index)
/***********************************/
{
	return(bitmap[index >> 3] & (1 << (index & 0x7)));
}   /* end of bit_test */


static int bit_next(char *bitmap, int index, int length)
/***********************************************/
/* Find the next set bit after 'index'
   index = -1 to find the first bit */
{
	/* can be speeded up by reading a byte at a time */

	for(index= index+1; index<length; index++)
	{
		if(bit_test(bitmap,index))
			return(index);
	}
	return(-1);
}   /* end of bit_next */



static void bit_map_or(char *map1, char *map2, int length)
/*************************************************/
/* map1 = map1 OR map2,  length is in bits */
{
	int  i;

	length = (length-1) >> 3;

	for(i=0; i<=length; i++)
	{
		map1[i] |= map2[i];
	}
}   /* end of bit_map_or */


static void bit_map_clear(char *bitmap, int length)
/******************************************/
{
	int  i;

	length = (length-1) >> 3;

	for(i=0; i<=length; i++)
	{
		bitmap[i] = 0;
	}
}   /* end of bit_map_clear */



static char *bit_map_alloc(int length)
/*****************************/
{
	length = (length-1) >> 3;

	return(malloc(length+1));
}   /* end of bit_map_alloc */






static void set_category_bit_map(char *bitmap, int cat, int dependents)
/**************************************************************/
/* Set this category, and its dependents in the bit map */
{
	CAT *cptr;

	bit_set(bitmap,cat);
	if(dependents == 0)
		return;

	/* now look for any dependents */
	cptr = &cty_fr.cat_data[cat];
	cat = cptr->down;

	while(cat != 0)
	{
		set_category_bit_map(bitmap,cat,dependents);

		cptr = &cty_fr.cat_data[cat];
		cat = cptr->right;
	}

}   /* end of set_category_bit_map */



static int search_all(FOLDREC *fr, FOLDREC *new_fr, char *string,
					  int length_str, int case_sensitive, int regex_flag, int n_chars,
					  int search_fields, int selection, int status_match, unsigned int **ixlist)
/************************************************************************/
/* Search all articles in an index list for a text string */
/* n_chars, number of characters to search.  0=only search header */
/* Status_match: 0 all
          bit 0: unread, 1: marked, 2: locked, 3: replied, 4: outgoing, 7: read */

{
	int  ix;
	CARD *cptr;
	int  n;
	char *p;
	int  length;
	int  found;
	int  found_displ;
	int  count;
	int  cardsize;
	int  i;
	int  attach_start;
	short *catlist;
	int  text_start;
	clock_t  start_clock;
	TEXTR *t;
	char title[24];
	CARD_EXPANDED cardex;
	static TEXTR text_temp;
	static int text_temp_initialised=0;


	/* bits 0: unread, 1: marked, 2: locked, 3: replied, 7: read(not L/M) */
	static char status_types[] = {
		0x01, 0x01, 0x01, 0x01, 0x02, 0x04, 0x80, 0x00,
		0x09, 0x09, 0x00, 0x09, 0x0a, 0x0c, 0x88, 0x00,
	};

	count = 0;
	start_clock = clock();

#ifdef deleted
	if((string[0] == 0) && (subset_n_cats == 0) && (date_from==0) && (date_to==0))
	{
		werr(0,"No search condition is specified");
		return(-1);   /* no string specified */
	}
#endif

	if(text_temp_initialised == 0)
	{
		memset(&text_temp,0,sizeof(text_temp));
		text_temp_initialised = 1;
	}

	length = sizeof(CARD) + n_chars;
	t = &text_temp;
	text_temp.fr = fr;
	if(flex_alloc((flex_ptr)&text_temp.text_base,length+TEXT_EXTRA) == 0)
	{
		werr(0,"Failed to allocate memory for text");
		return(-1);
	}
	text_temp.text_buf_size = length+TEXT_EXTRA;

	/* make bitmap of catagories to search for */
	for(ix=0; ix<subset_n_cats; ix++)
	{
		set_category_bit_map(cats_set,subset_cats[ix],1);
	}

	if(regex_flag == -1)
	{
		/* compile the regex expression for use during the search */
		regex_flag = search_regex_compile(string,case_sensitive);
		if(regex_flag < 0)
			return(-1);
	}


	visdelay2_begin();
	set_lock_lists(4,fr);

	for(ix=0; ix<fr->n_entries; ix++)
	{
		if(selection)
		{
			/* only consider those which are selected */
			if((*ixlist)[ix] & IXLIST_MARKED)
			{
				(*ixlist)[ix] &= ~IXLIST_MARKED;   /* now clear the mark */
			}
			else
			{
				continue;   /* ignore this one, not selected */
			}
		}

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

		cptr = (CARD *)(((*ixlist)[ix] & IXLIST_MASK) << 2);

		if((date_from != 0) && ((cptr->date_box & DATE_MASK) < date_from))
			continue;

		if((date_to != 0) && ((cptr->date_box & DATE_MASK) >= date_to))
			continue;

		p = cptr->data;

		if(status_match)
		{
			if(((status_types[cptr->status & 0xf] & status_match) == 0) &&
					((cptr->status & status_match & 0x70) == 0))
				continue;
		}


		if(subset_n_cats > 0)
		{
			/* only proceed if any of the categories match */
			found = 0;
			catlist = (short *)(p + (cptr->n_refs & N_REFS_MASK)*sizeof(int));

			for(i=0; i<(cptr->n_cats & N_CATS_MASK); i++)
			{
				if(bit_test(cats_set,catlist[i]))
				{
					found = 1;
					break;
				}
			}
			if(found == 0)
				continue;    /* this item has none of the required categories */
		}

		found = 0;

		if((search_fields == 0) && (n_chars < 0))
		{
			found = 1;
		}
		else if((search_fields & 0xf) != 0)
		{
			if(search_fields & 1)
			{
				n = strlen(&cptr->data[cptr->d_author]);
				if(search_regex(string, length_str, &p, cptr->d_author, n, case_sensitive,regex_flag,NULL)>=0)
					found = 1;
			}
			if(search_fields & 2)
			{
				n = strlen(&cptr->data[cptr->d_title]);
				if(search_regex(string, length_str, &p, cptr->d_title, n, case_sensitive,regex_flag,NULL)>=0)
					found = 1;
			}
			if(search_fields & 4)
			{
				n = strlen(&cptr->data[cptr->d_comment]);
				if(search_regex(string, length_str, &p, cptr->d_comment, n, case_sensitive,regex_flag,NULL)>=0)
					found = 1;
			}
			if(search_fields & 8)
			{
				n = strlen(&cptr->data[cptr->d_keywords]);
				if(search_regex(string, length_str, &p, cptr->d_keywords, n, case_sensitive,regex_flag,NULL)>=0)
					found = 1;
			}
		}

		if((found == 0) && ((n_chars >= 0) || ((search_fields & 0x20) && (cptr->user & INET_HDR_MASK)) ))
		{
			/* not yet found, need to search text body or internet header */
			if((ix & 1) == 0)
				call_event_process(0);

			visdelay2_percent((ix*100)/fr->n_entries);

			/* look at article text */
			cardsize = card_size(cptr);
			length = cptr->alength & ART_LENGTH_MASK;

			length = length - cardsize;    /* length of text (internet header + text body) */
			if(length <= 0)
				continue;

			if(article_read(&text_temp, cptr->addr, cptr->date_box >> 26, cardsize, length, 1, 0, 1) < 0)
			{
				continue;   /* failed to read article */
			}

			length = text_temp.text_length;

			text_start = 0;  /* start of text body */
			if(cptr->user & INET_HDR_MASK)
			{
				p = text_temp.text_base;
				for(i=0; i<text_temp.text_length; i++)
				{
					if((p[i]=='\n') && (p[i+1]=='\n'))
					{
						text_start = i;
						break;
					}
				}
				if(text_start == 0)
				{
					/* no text body, probably a header-only article */
					text_start = text_temp.text_length;
				}
				length -= text_start;   /* length of just the text body */
			}

			if((n_chars > 0) && (length > n_chars))
			{
				/* max. length of search has been specified */
				length = n_chars;
			}

			if(n_chars < 0)
			{
				/* just search internet header */
				length = text_start;
				text_start = 0;
			}
			else if(search_fields & 0x20)
			{
				/* search internet header + text body */
				length += text_start;
				text_start = 0;
			}

			attach_start = -1;
			cardex.status = cptr->status & STATUS_MASK;
			cardex.status_other = cptr->status & 0x70;
			cardex.status_other2 = cptr->n_cats & 0xe0;
			cardex.flags = cptr->user & 0xe0;

			if(cptr->n_cats & STATUS_BIT_ATTACH)
			{
				text_interpret_article(&text_temp,&cardex,0);
				if(t->attachments > 0)
					attach_start = t->attach[0].displ;
				else
					attach_start = 0x7fffffff;

				/* restrict search to omit the attachments */
				if((text_start + length) > attach_start)
				{
					length = attach_start - text_start;
					if(length < 0)
						length = 0;
				}
			}

			if((found_displ = search_regex(string, length_str, &text_temp.text_base, text_start, length, case_sensitive,
										   regex_flag,NULL)) >= 0)
			{
				if(attach_start == -1)
				{
					/* not yet run text_interpret_article, do it now */
					text_interpret_article(&text_temp,&cardex,0);
					if(t->attachments > 0)
						attach_start = t->attach[0].displ;
					else
						attach_start = 0x7fffffff;
				}

				if(found_displ < attach_start)
				{
					if((found_displ < text_temp.sig_start) || (search_fields & 0x100))
						found = 1;
				}
			}
		}

		if(search_cancel_flag==NULL)
		{
			if(search_cancel_reason == 1)
				count = -2;   /* no results */

			/* clear the select flag on subsequent entries */
			for( ; ix < fr->n_entries; ix++)
			{
				(*ixlist)[ix] &= ~IXLIST_MARKED;   /* now clear the mark */
			}
			break;
		}

		if(found)
		{
			(*ixlist)[ix] |= IXLIST_MARKED;
			count++;

			sprintf(title,"%s   %d",new_fr->name,count);
			win_settitle(new_fr->window_buttons,title);
		}
	}
	if(text_temp.text_base != NULL)
		flex_free((flex_ptr)&text_temp.text_base);

	clear_lock_lists(4);
	visdelay2_end();


	if(search_cancel_reason == 0)
	{
		if(((clock() - start_clock)/CLOCKS_PER_SEC) > 3)
			announce_message(msgs_lookup("Y12"),1);  // search completed
	}

	return(count);
}   /* end of search_all */




static FOLDREC *subset_find_name(char *name)
/***********************************/
/* Find the list with the specified name */
{
	int  i;
	FOLDREC *fr;

	for(i=0; i<N_LISTS; i++)
	{
		fr = &list_fr[i];
		if(strcmp(fr->name,name)==0)
		{
			return(fr);
		}
	}
	return(NULL);
}   /* end of subset_find_name */








FOLDREC *subset_new_dialogue(FOLDREC *fr, int hide)
/*************************************************/
{
	char buf[20];

	sprintf(buf,"Subset%d",next_subset);

	/* don't show subset name dbox */
	next_subset++;
	return(subset_new(buf,fr,1));
}   /* end of subset_new_dialogue */



int subset_add_category(int action, int cat)
/******************************************/
/* Called from category module when click on category */
{
	char subset_cat_string[256];

	if(dbox_subset == 0)
		return(0);    /* subset dbox is not open */

	if(action == 1)
	{
		if(subset_n_cats < N_SUBSET_CATS)
			subset_cats[subset_n_cats++] = cat;
	}
	/* re-display the categories field */
	expand_cat_string(subset_cats, subset_n_cats, subset_cat_string, sizeof(subset_cat_string));
	dbox_setfield(dbox_subset,SU_CATS,subset_cat_string);
	dbox_showstatic(dbox_subset);
	return(1);
}   /* end of subset_add_category */




void subset_selected_source(int *hits)
/**********************************/
{
	subset_add_category(1,category_menu_hit(hits,NULL));
}   /* end of subset_selected_source */



void subset_selected_status(int *hits)
/************************************/
{
	subset_status_match = hits[0];
	dbox_setfield(dbox_subset,SU_STATUS,subset_status_string[subset_status_match]);
}   /* end of subset_selected_status */




void dbox_toggle(dbox d, int field)
/*********************************/
/* Toggle the state of a numeric field */
{
	int  i;

	i = dbox_getnumeric(d,field);
	dbox_setnumeric(d,field,i ^ 1);
}   /* end of dbox_toggle */




static BOOL subset_raw_handler(dbox d, void *event, void *handle)
/***************************************************************/
{
	wimp_mousestr *mouse;
	int bbits;
	wimp_eventstr *e = event;
	int  field;


	mouse = &e->data.but.m;
	field = mouse->i;

	switch(e->e)
	{
	case wimp_EKEY:
		switch(e->data.key.chcode)
		{
		case 1:   /* Ctrl-A */
			dbox_toggle(d,SU_AUTHOR);
			return(TRUE);

		case 2:   /* Ctrl-B */
			dbox_toggle(d,SU_BODY);
			return(TRUE);

		case 3:   /* Ctrl-C */
			dbox_toggle(d,SU_COMMENT);
			return(TRUE);

		case 9:   /* Ctrl-I */
			dbox_toggle(d,SU_HEADER);
			return(TRUE);

		case 11:   /* Ctrl-K */
			dbox_toggle(d,SU_KEYS);
			return(TRUE);

		case 18:   /* Ctrl-R */
			dbox_toggle(d,SU_REGEX);
			return(TRUE);

		case 19:   /* Ctrl-S */
			dbox_toggle(d,SU_CASE);
			return(TRUE);

		case 20:   /* Ctrl-T */
			dbox_toggle(d,SU_TITLE);
			return(TRUE);

		case '\r':
			break;
		}
		break;

	case wimp_EBUT:
		/* click on icon.  get icon number. */
		bbits = mouse->bbits;

		switch(field)
		{
		case SU_CATS:
			if(bbits & wimp_BMID)
			{
				dbox_menu(category_make_menu(&cty_fr,0),subset_selected_source,mouse);
				return(TRUE);
			}
			break;

		case SU_STATUS:
			dbox_menu(wmenu_subset_status,subset_selected_status,mouse);
			return(TRUE);

		case SU_SIGNATURE:
			/* also set the Body option if we set the Sigs option */
			if(dbox_getnumeric(d,SU_SIGNATURE)!=0)
			{
				dbox_setnumeric(d,SU_BODY,1);
			}
			break;
		}
		break;
	}
	return(help_handler(event,HELP_SUBSET));
}   /* end of subset_raw_handler */



static void subset_dialogue_handler(dbox d, void *handle)
/*******************************************************/
{
	int  ix;
	FOLDREC *search_fr;
	int  action;
	int  operation;  /* 1=keep, 2=remove, 3=add from */
	dbox dbox_subset1;
	static int  n_chars=0;
	int  case_sensitive;
	int  regex_flag;
	int  search_fields=0;
	int  status_match;
	FOLDREC *fr;
	int n_marked;
	int  ix2;
	unsigned int *ixlist;
	unsigned int *ixlist2;
	int  save_n_entries;
	int  num;
	int  level;
	int  length_str;
	int  su_all;
	int  su_selection;
	int  ixlist_sorted=0;
	char cats_string[128];
	char list_name[20];
	wimp_wstate wstate;
	wimp_wstate wstate1;

	/* translate from menu entry to status match map */
	static char status_match_map[] = {
		0x00, 0x01, 0x80, 0x04, 0x02, 0x06, 0x86, 0x08, 0x10
	};


	fr = (FOLDREC *)handle;

	if(dbox_subset == NULL)
	{
		if(search_cancel_flag != NULL)
		{
			/*         list_delete_window(search_cancel_flag); */
			search_cancel_flag = NULL;
			search_cancel_reason = 2;
		}
		return;
	}

	action = dbox_get(dbox_subset);
	if(action == 0)
		action = SU_NEW;  /* default */

	switch(action)
	{
	case SU_NEW:
	case SU_KEEP:
	case SU_REMOVE:
	case SU_ADD:
		break;

	case SU_MORE:
		dbox_subset1 = dbox_new("Search2");
		for(ix=3; ix<=10; ix++)
		{
			num = dbox_getnumeric(dbox_subset,ix);
			dbox_setnumeric(dbox_subset1,ix,num);
		}
		for(ix=SU_DATE_FROM; ix<=SU_DATE_TO; ix++)
		{
			dbox_getfield(dbox_subset,ix,cats_string,sizeof(cats_string));
			dbox_setfield(dbox_subset1,ix,cats_string);
		}
		dbox_getfield(dbox_subset,SU_STRING,cats_string,sizeof(cats_string));
		dbox_setfield(dbox_subset1,SU_STRING,cats_string);

		dbox_setfield(dbox_subset1,SU_STATUS,subset_status_string[subset_status_match]);

		if(dbox_getnumeric(dbox_subset,SU_SELECTION)==0)
			dbox_setnumeric(dbox_subset,SU_SEARCH_ALL,1);

		cats_string[0] = 0;
		su_dialogue_type = 1;

		wimp_get_wind_state(dbox_syshandle(dbox_subset),&wstate);
		dbox_dispose(&dbox_subset);
		dbox_subset = dbox_subset1;


		dbox_setfield(dbox_subset,SU_ADD_FROM,fr->parent->name);

		dbox_raw_eventhandler(dbox_subset,subset_raw_handler,NULL);
		dbox_eventhandler(dbox_subset, subset_dialogue_handler, (void *)fr);

		wstate.o.w = dbox_syshandle(dbox_subset);
		dbox_showstatic(dbox_subset);
		wimp_get_wind_state(dbox_syshandle(dbox_subset),&wstate1);

		wstate.o.box.y0 = wstate.o.box.y1 - (wstate1.o.box.y1 - wstate1.o.box.y0);
		wimp_open_wind(&wstate.o);
		set_input_focus(dbox_syshandle(dbox_subset),SU_STRING,strlen(search_text_string));
		return;

	default:
		dbox_dispose(&dbox_subset);
		dbox_subset = 0;

		set_focus(fr->window);
		return;
	}


	if(dbox_getnumeric(d,SU_AUTHOR)!=0)
		search_fields |= 1;
	if(dbox_getnumeric(d,SU_TITLE)!=0)
		search_fields |= 2;
	if(dbox_getnumeric(d,SU_COMMENT)!=0)
		search_fields |= 4;
	if(dbox_getnumeric(d,SU_KEYS)!=0)
		search_fields |= 8;
	if(dbox_getnumeric(d,SU_HEADER)!=0)
		search_fields |= 0x20;
	if(dbox_getnumeric(d,SU_SIGNATURE)!=0)
		search_fields |= 0x100;

	status_match = status_match_map[subset_status_match];

	case_sensitive = dbox_getnumeric(d,SU_CASE);
	if(case_sensitive != 0)
		search_fields |= 0x80;

	regex_flag = dbox_getnumeric(d,SU_REGEX);
	if(regex_flag) regex_flag = -1;

	search_regex_flag = regex_flag;

	if(regex_flag != 0)
		search_fields |= 0x10;

	if(dbox_getnumeric(d,SU_BODY))
	{
		if(su_dialogue_type == 1)
			n_chars = dbox_getnumeric(d,SU_NCHARS);
		else
			n_chars = 0;
		search_fields |= 0x40;
	}
	else
	{
		n_chars = -1;
	}

	search_options = search_fields;   /* remember search options */

	dbox_getfield(d,SU_STRING,search_text_string,sizeof(search_text_string)-1);

	if(su_dialogue_type == 1)
		dbox_getfield(d,SU_CATS,cats_string,sizeof(cats_string)-1);

	length_str = strlen(search_text_string);
	if((!case_sensitive) && (regex_flag == 0))
	{
		/* convert string to lower case */
		for(ix=0; ix<=length_str; ix++)
			search_text_string[ix] = chars_lc[search_text_string[ix]];
	}

	if(action != SU_REMOVE)
	{
		strcpy(search_text_string_open,search_text_string);
		search_regex_flag_open = regex_flag;
		search_case_sensitive_open = case_sensitive;
	}

	su_all = dbox_getnumeric(d,SU_ALL);
	su_selection = dbox_getnumeric(d,SU_SELECTION);
	operation = action;

	dbox_getfield(d,SU_DATE_FROM,date_from_buf,sizeof(date_from_buf));
	date_from = encode_date(date_from_buf,2);
	if(date_from != 0)
	{
		date_from = date_from - timezone_offset;
	}

	dbox_getfield(d,SU_DATE_TO,date_to_buf,sizeof(date_to_buf));
	date_to = encode_date(date_to_buf,2);
	if(date_to != 0)
	{
		date_to = date_to + (24*24) - timezone_offset;  /* add 24 hours to make To date inclusive */
	}


	dbox_dispose(&dbox_subset);
	dbox_subset = 0;

	if(operation == SU_ADD)
	{
		/* add from, find 'from' list */
		dbox_getfield(d,SU_ADD_FROM,list_name,sizeof(list_name)-1);
		search_fr = subset_find_name(list_name);
		if(search_fr == NULL)
		{
			werr(0,"Can't find list name '%s'",list_name);
			return;
		}
	}
	else if(operation == SU_NEW)
	{
		/* create new list */
		search_fr = fr;
		fr = subset_new_dialogue(fr,0);
		if(fr == NULL)
		{
			return;
		}
	}
	else
	{
		search_fr = fr;
	}

	/* unmark all indices */
	clear_ixlist(search_fr);
	save_n_entries = search_fr->n_entries;


	ixlist = search_fr->ixlist;

	if(su_all)
	{
		/* mark all records */
		for(ix=0; ix<fr->n_entries_tot; ix++)
		{
			ixlist[ix] |= IXLIST_MARKED;
		}
		n_marked = ix;
	}
	else if(su_selection)
	{
		/* mark all selected records */
		n_marked=0;
		for(ix=0; ix<search_fr->n_entries; ix++)
		{
			level = ((ixlist[ix] >> IXSEL) & IXSELB) - 1;   /* this item is selected */

			if(level >= 0)
			{
				/* mark this block as selected */
				do {
					ixlist[ix++] |= IXLIST_MARKED;
					n_marked++;
				} while((ix < search_fr->n_entries) && ( (ixlist[ix] >> IXLEV) > level));
				ix--;
			}
		}

		/* a search condition has been given, mark those which match the search conditions */

		if(n_chars >= 0)
		{
			article_make_index(search_fr,&ixlist);   /* sort by article file address */
			ixlist_sorted = 1;
		}

		search_cancel_flag = fr;
		search_cancel_reason = 0;
		num = search_all(search_fr,fr,search_text_string,length_str,case_sensitive,regex_flag,n_chars,search_fields,1,status_match,&ixlist);
		search_cancel_flag = NULL;

		/* has the search cleared the original selection and made a new one ? */
		if(num >= 0)
			n_marked = num;
		else
		{
			if((num < -1)  && ixlist_sorted)
				flex_free((flex_ptr)&ixlist);   /* temporary index sorted by article address */
			return;  /* cancelled */
		}

	}
	else
	{
		/* mark those which match the search conditions */
		search_fr->n_entries = search_fr->n_entries_tot;

		if(n_chars >= 0)
		{
			article_make_index(search_fr,&ixlist);   /* sort by article file address */
			ixlist_sorted = 1;
		}

		search_cancel_flag = fr;
		search_cancel_reason = 0;
		n_marked = search_all(search_fr,fr,search_text_string,length_str,case_sensitive,regex_flag,n_chars,search_fields,0,status_match,&ixlist);
		search_cancel_flag = NULL;

		if(n_marked < 0)
		{
			if((n_marked < -1) && ixlist_sorted)
				flex_free((flex_ptr)&ixlist);   /* temporary index sorted by article address */
			return;
		}
	}


	/* now perform the operation */
	switch(operation)
	{
	case SU_KEEP:   /* keep */
		num = n_marked;
		break;

	case SU_REMOVE:   /* remove */
		num = fr->n_entries - n_marked;
		break;

	case SU_ADD:   /* add from */
	case SU_NEW:   /* new */
		num = fr->n_entries + n_marked;
		break;
	}

	num += 8;  /* spare space in case we get n_marked wrong */

	ixlist2 = malloc(num * sizeof(int));
	if(ixlist2 == NULL)
	{
		malloc_err(51);
		return;
	}

	ix2 = 0;

	switch(operation)
	{
	case SU_KEEP:   /* keep */
		for(ix=0; ix<fr->n_entries; ix++)
		{
			if(ixlist[ix] & IXLIST_MARKED)
			{
				ixlist2[ix2++] = ixlist[ix];
			}
		}
		break;

	case SU_REMOVE:   /* remove */
		for(ix=0; ix<fr->n_entries; ix++)
		{
			if((ixlist[ix] & IXLIST_MARKED) == 0)
			{
				ixlist2[ix2++] = ixlist[ix];
			}
			else
			{
				if((ix < (fr->n_entries-1)) && (ixlist[ix] >> IXLEV) < (ixlist[ix+1] >> IXLEV))
				{
					/* propogate level information down */
					ixlist[ix+1] = (ixlist[ix+1] & ~IXLIST_LEVEL) | (ixlist[ix] & IXLIST_LEVEL);
				}
			}
		}
		break;

	case SU_ADD:   /* add from */
	case SU_NEW:   /* new */
		memcpy(ixlist2,fr->ixlist,fr->n_entries * sizeof(int));
		ix2 = fr->n_entries;

		for(ix=0; ix<search_fr->n_entries; ix++)
		{
			if(ixlist[ix] & IXLIST_MARKED)
			{
				ixlist2[ix2++] = ixlist[ix];
			}
		}
		break;
	}

	search_fr->n_entries = save_n_entries;

	if(ixlist_sorted)
		flex_free((flex_ptr)&ixlist);   /* temporary index sorted by article address */

	free(fr->ixlist);
	fr->ixlist = ixlist2;
	fr->n_entries = ix2;
	fr->n_entries_tot = ix2;
	fr->n_entries_max = num;

	if(operation == 3)
	{
		/* now sort the list and remove duplicates */
	}

	if((length_str > 0) && ((n_chars >= 0) || (search_fields != 0)))
		fr->search_text_result = 1;   /* this list is a search result */

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







void subset_dialogue(FOLDREC *fr, int type)
/*****************************************/
{
	FOLDREC *parent;

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

	if(dbox_subset == 0)
	{
		dbox_subset = dbox_new("Search1");
		subset_status_match = 0;

		/* set from saved search options */
		dbox_setnumeric(dbox_subset,SU_AUTHOR,search_options & 1);
		dbox_setnumeric(dbox_subset,SU_TITLE,search_options & 2);
		dbox_setnumeric(dbox_subset,SU_COMMENT,search_options & 4);
		dbox_setnumeric(dbox_subset,SU_KEYS,search_options & 8);
		dbox_setnumeric(dbox_subset,SU_REGEX,search_options & 0x10);
		dbox_setnumeric(dbox_subset,SU_HEADER,search_options & 0x20);
		dbox_setnumeric(dbox_subset,SU_BODY,search_options & 0x40);
		dbox_setnumeric(dbox_subset,SU_CASE,search_options & 0x80);
		dbox_setnumeric(dbox_subset,SU_SIGNATURE,search_options & 0x100);

		dbox_setfield(dbox_subset,SU_DATE_FROM,date_from_buf);
		dbox_setfield(dbox_subset,SU_DATE_TO,date_to_buf);
	}
	else
		reopen_window(dbox_syshandle(dbox_subset),-1);

	/* ensure that category bitmap is large enough */
	if(n_cats_set < cty_fr.max_cats)
	{
		if(cats_set != NULL)
			free(cats_set);
		cats_set = bit_map_alloc(cty_fr.max_cats);
	}
	if(cats_set != NULL)
	{
		n_cats_set = cty_fr.max_cats;
		bit_map_clear(cats_set,n_cats_set);
	}


	dbox_raw_eventhandler(dbox_subset,subset_raw_handler,NULL);
	dbox_eventhandler(dbox_subset, subset_dialogue_handler, (void *)fr);


	parent = fr->parent;
	subset_n_cats = 0;

	dbox_setfield(dbox_subset,SU_STRING,search_text_string);
	dbox_setnumeric(dbox_subset,SU_SEARCH_ALL,1);

	if(fr == &list_fr[0])
	{
		/* the main index, we can't alter this, only make a new subset */
		dbox_fadefield(dbox_subset,SU_KEEP);
		dbox_fadefield(dbox_subset,SU_REMOVE);
		dbox_fadefield(dbox_subset,SU_ADD);
		dbox_fadefield(dbox_subset,SU_ADD_FROM);
	}
	else
	{
		/* look whether any articles are selected in the list */
		if((fr != NULL) && (fr->n_selected > 0))
		{
			if((fr->n_selected > 1) || (fr->open_levels < fr->display_levels) ||
					(((fr->ixlist[fr->cursor_ref] >> IXSEL) & IXSELB) <= fr->display_levels))
			{
				dbox_setnumeric(dbox_subset,SU_SELECTION,1);

				if(su_dialogue_type == 1)
				{
					dbox_setnumeric(dbox_subset,SU_ALL,0);
					dbox_setnumeric(dbox_subset,SU_SEARCH_ALL,0);
				}
			}
		}
	}
	dbox_showstatic(dbox_subset);
	set_input_focus(dbox_syshandle(dbox_subset),SU_STRING,strlen(search_text_string));

}   /* end of subset_dialogue */




void subset_expand_level(FOLDREC *fr, int ref, int level)
/*******************************************************/
/* Create a subset from the specified selected item */
{
	int  ix;
	int  count=0;
	int  adjust=0;
	unsigned int *ixlist;
	unsigned int *ixlist2;
	FOLDREC *search_fr;

	ixlist = fr->ixlist;

	if(ref < 0)
	{
		/* an adjust-click, may be several groups selected */
		adjust = 1;

		for(ix=0; ix<fr->n_entries; ix++)
		{
			if((ixlist[ix] >> IXSEL) & IXSELB)
			{
				/* this entry is selected */
				ref = ix;
				for(ix=ref+1; ix<fr->n_entries; ix++)
				{
					if( (ixlist[ix] >> IXLEV) <= level)
					{
						break;
					}
				}
				count += (ix-ref);
				ix--;
			}
		}
	}
	else
	{
		for(ix=ref+1; ix<fr->n_entries; ix++)
		{
			if( (ixlist[ix] >> IXLEV) <= level)
				break;
		}
		count = (ix-ref);
	}


	ixlist2 = malloc(count * sizeof(int));
	if(ixlist2 == NULL)
	{
		malloc_err(52);
		return;
	}

	search_fr = fr;
	fr = subset_new_dialogue(fr,1);
	if(fr == NULL)
	{
		free(ixlist2);
		return;
	}


	free(fr->ixlist);
	fr->ixlist = ixlist2;

	if(adjust == 0)
	{
		for(ix=0; ix<count; ix++)
		{
			ixlist2[ix] = ixlist[ref+ix] & (IXLIST_LEVEL + IXLIST_MASK);
		}
	}
	else
	{
		count = 0;
		for(ix=0; ix<search_fr->n_entries; ix++)
		{
			if((ixlist[ix] >> IXSEL) & IXSELB)
			{
				/* this entry is selected */
				do {
					ixlist2[count++] = ixlist[ix++] & (IXLIST_LEVEL + IXLIST_MASK);
				} while((ix < search_fr->n_entries) && ((ixlist[ix] >> IXLEV) > level));
				ix--;
			}
		}
	}

	fr->n_entries = count;
	fr->n_entries_tot = count;
	fr->n_entries_max = count;

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



