static char rcsid[] = "@(#)$Id: limit.c,v 1.45 2006/04/09 07:37:18 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.45 $   $State: Exp $
 *
 *  Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI> 
 *                           (was hurtta+elm@ozone.FMI.FI)
 ******************************************************************************
 *  The Elm Mail System 
 *
 *			Copyright (c) 1988-1992 USENET Community Trust
 *			Copyright (c) 1986,1987 Dave Taylor
 *****************************************************************************/

/** This stuff is inspired by MH and dmail and is used to 'select'
    a subset of the existing mail in the folder based on one of a
    number of criteria.  The basic tricks are pretty easy - we have
    as status of VISIBLE associated with each header stored in the
    (er) mind of the computer (!) and simply modify the commands to
    check that flag...the global variable `selected' is set to the
    number of messages currently selected, or ZERO if no select.
**/

#include "def_elm.h"
#include "s_elm.h"
#include "s_aliases.h"

DEBUG_VAR(Debug,__FILE__,"mbox");

static unsigned char *s2us P_((char *str));
static unsigned char *s2us(str) 
     char *str;
{
    return (unsigned char *)str;
}


static int SelectTaggedMessages P_((struct menu_common *menu,
				    int *vector));
static int SelectTaggedMessages(menu, vector)
     struct menu_common *menu;
     int *vector;
{
    int iindex, count = 0;
    int mc = mcommon_get_count(menu);


    for (iindex = 0; iindex < mc; iindex++) {
	if (mcommon_ison_status(menu,iindex, status_basic,TAGGED)) {
	    vector[count++] = iindex;
	}
    }
    return(count);
}

static int SelectFlaggedMessages P_((struct menu_common *menu,
				    int *vector));
static int SelectFlaggedMessages(menu, vector)
     struct menu_common *menu;
     int *vector;
{
    int iindex, count = 0;
    int mc = mcommon_get_count(menu);


    for (iindex = 0; iindex < mc; iindex++) {
	if (mcommon_ison_status(menu,iindex, status_1,S1_FLAGGED)) {
	    vector[count++] = iindex;
	}
    }
    return(count);
}

void limit(menu, page,LOC)
     struct menu_common *menu;
     struct menu_context  *page;
     struct screen_parts *LOC;
{
    /** if we changed selection criteria = need redraw 
	   use menu_trigger_redraw(page)
    */
	
    int  last_selected, all;
    struct string *criteria = NULL;
    int li,co;
    int delay_redraw = 0;

    menu_get_sizes(LOC->prompt_page,&li,&co);
	
    last_selected = mcommon_get_selected(menu);

    all = 0;

    if (mcommon_get_selected(menu)) {
	int c;

    redraw0:
	menu_PutLineX(LOC->prompt_page,
		      1, 0, 
		      CATGETS(elm_msg_cat, ElmSet, ElmLimitAlreadyHave,
			      "Already have selection criteria - add more? (%c/%c) %c%c"),
		      *def_ans_yes, *def_ans_no, *def_ans_no, BACKSPACE);
	c = menu_ReadCh(LOC->prompt_page,
			REDRAW_MARK|READCH_sig_char);

	if (TERMCH_interrupt_char == c) {
	    /* Ctrl-C */

	    menu_ClearScreen(LOC->prompt_page);   
	    if (delay_redraw)
		menu_trigger_redraw(page);
	    	    
	    return;
	}

	if (REDRAW_MARK == c) {
	    menu_ClearScreen(page);   /* Clear possible redraw mark */
	    
	    /* Call refresh routines of children */
	    menu_redraw_children(page);
	    
	    if (menu_need_redraw(LOC->prompt_page))		    
		menu_ClearScreen(LOC->prompt_page);   /* Clear redraw mark from prompt_area*/
	    
	    /* NOTICE: using menu_trigger_redraw(page) on here
	       may cause redraw loop!
	    */
	    delay_redraw++;
	    goto redraw0;
	}

	if (
#ifdef ASCII_CTYPE
	    isascii(c) &&
#endif
	    tolower((unsigned char)c) == *def_ans_yes) {

	    menu_Write_to_screen(LOC->prompt_page,
				 CATGETS(elm_msg_cat, ElmSet, ElmYesWord, "Yes."));

	    menu_PutLineX(LOC->prompt_page,
			  0, co-30, 
			  CATGETS(elm_msg_cat, ElmSet, ElmLimitAdding,
				  "Adding criteria..."));
	  } else {
	      menu_Write_to_screen(LOC->prompt_page,
				   CATGETS(elm_msg_cat, ElmSet, ElmNoWord, "No."));

	      menu_PutLineX(LOC->prompt_page,
			    0, co-30, 
			    CATGETS(elm_msg_cat, ElmSet, ElmLimitChanging,
				    "Change criteria..."));

	      mcommon_set_selected(menu,0);  
	      
	      menu_trigger_redraw(LOC->title_page);
	      menu_trigger_redraw(LOC->header_page);
	  }
    }



    while(1) {
	int line;
	int code;
	int f = 0;

	/* CLear previous prompt */
	menu_MoveCursor(LOC->prompt_page,1,0); 
	menu_CleartoEOLN(LOC->prompt_page);


    redraw:
	/* FIXME --optionally_enter*  should use prompt_area */
	line = menu_GetAbsLine(LOC->prompt_page,1);	
	
	code = optionally_enter2(page,
				 &criteria,line,0,
				 OE_REDRAW_MARK|OE_ALLOW_MIMEENC|f|
				 OE_SIG_CHAR /* Ctrl-C */,
				 CATGETS(elm_msg_cat, ElmSet, 
					 ElmLimitEnterCriteria,
					 "Enter criteria or '?' for help: "));
	if (REDRAW_MARK == code) {
	    menu_ClearScreen(page);   /* Clear possible redraw mark */
	    
	    /* Call refresh routines of children */
	    menu_redraw_children(page);
	    
	    if (menu_need_redraw(LOC->prompt_page))		    
		menu_ClearScreen(LOC->prompt_page);   /* Clear redraw mark from prompt_area*/
	    
	    /* NOTICE: using menu_trigger_redraw(page) on here
	       may cause redraw loop!
	    */
	    delay_redraw++;
	    goto redraw;
	}	    

	if (0 != code) {
				   
	    if (criteria)
		free_string(& criteria);

	    menu_trigger_redraw(LOC->title_page);
	    menu_trigger_redraw(LOC->header_page);

	    if (delay_redraw)
		menu_trigger_redraw(page);
		
	    return;

	}		   

	
	clear_error();
	
	if (!criteria || string_len(criteria) == 0) {
	    /* no change */
	    
	    if (criteria)
		free_string(& criteria);
	    
	    mcommon_set_selected(menu,last_selected);
	    menu_trigger_redraw(LOC->title_page);
	    menu_trigger_redraw(LOC->header_page);

	    if (last_selected)
		lib_error(CATGETS(elm_msg_cat, ElmSet, 
				  ElmLimitReturnToLastSelection,
				  "Returned to last selection."));


	    if (delay_redraw)
		menu_trigger_redraw(page);
	    return;	
	}

	if (string_matches_ascii(criteria,s2us("all"))) {
	    all++;

	    mcommon_set_selected(menu,0);
	} else if (string_matches_ascii(criteria,s2us("?"))) {
	    mcommon_limit_print_help(menu,last_selected);
	    continue;
	} else {
	    int X = 0;
	    int i,c;
	    int len = string_len(criteria);
	    uint16 lastsep = 0;
	    int count;

	    while (X <len) {
		uint16  sep;
		struct string * part = NULL;
		int   reslen = 0;
		int   * selarr = 0;

	    
		if (!get_word_from_string(criteria,&part,&X, 
					  GWF_lowercase | GWF_trim_space,
					  /* ASCII assumed */
					  s2us("&|"),&sep)) {

		    if (part)
			free_string(&part);
		    break;
		}

		if (string_matches_ascii(part, s2us("tagged"))) {
		    int n = mcommon_get_count(menu);

		    if (n < 1)
			goto fail;

		    selarr = safe_malloc(n * sizeof(selarr[0]));
		    reslen = SelectTaggedMessages(menu,selarr);
		} else if (string_matches_ascii(part, s2us("flagged"))) {
		    int n = mcommon_get_count(menu);

		    if (n < 1)
			goto fail;

		    selarr = safe_malloc(n * sizeof(selarr[0]));
		    reslen = SelectFlaggedMessages(menu,selarr);

		} else 		
		    reslen = mcommon_limit_helper(menu,part,&selarr,
						  page);

		switch (lastsep) {
		    int additional_criteria;
		    int i,c;
		case 0:   /* First selection */
		    additional_criteria = mcommon_get_selected(menu);

		    if (!additional_criteria) {
			c = mcommon_get_count(menu);

			for (i = 0; i < c; i++)
			    mcommon_clearf_status(menu,i,status_basic,VISIBLE);
			goto action_or;
		    }
		    goto action_and;

		    /* and */
		action_and:
		case 0x0026 /* & */:   
		    /* !!!   ASCII assumed on get_word_from_string */
		    c = mcommon_get_count(menu);

		    for (i = 0; i < c; i++) {
			int match = 0;
			int j;

			for (j = 0; j < reslen; j++)
			    if (i == selarr[j]) {
				match++;
				break;
			    }
			
			if (!match) 
			    mcommon_clearf_status(menu,i,status_basic,VISIBLE);
		    }

		    break;

		    /* or */
		action_or:
		case 0x007C /* | */:               
		    /* !!!   ASCII assumed on get_word_from_string() */
		    /* also | do not exists on iso 646 sets ... */
		    for (i = 0; i < reslen; i++) {
			int x = selarr[i];
			
			mcommon_setf_status(menu,x,status_basic,VISIBLE);
		    }

		    break;
		default:
		    panic("LIMIT PANIC",__FILE__,__LINE__,"limit",
			  "bad separator",0);

		}
		X++;

	    fail:
		if (part)
		    free_string(&part);
		if (selarr)
		    free(selarr);

		lastsep = sep;
	    }

	    count = 0;
	    c = mcommon_get_count(menu);

	    for (i = 0; i < c; i++)
		if (mcommon_ison_status(menu,i,status_basic,VISIBLE))
		    count++;
	    
	    mcommon_set_selected(menu,count);
	}

	if (all && last_selected)
	    lib_error(CATGETS(elm_msg_cat, ElmSet, ElmLimitReturnToUnlimited,
			      "Returned to unlimited display."));
	else {

	    mcommon_limit_print_result(menu);

	}
       
	/* we need a redraw if there had been a selection or there is now. */
	if (last_selected || mcommon_get_selected(menu)) {

	    /* if current message won't be on new display, 
	       go to first message */	    

	    int current = mcommon_get_current(menu);

	    if (mcommon_get_selected(menu) &&
		mcommon_isoff_status(menu,current-1,status_basic,
				     VISIBLE)) {
		
		current = visible_to_index(1,menu)+1;
		mcommon_set_current(menu,current);
		copy_current(menu,LOC->header_page);
	    }
	    get_page(menu, LOC->header_page);
	 
	    menu_trigger_redraw(LOC->title_page);
	    menu_trigger_redraw(LOC->header_page);
	} 
	break;
    }

    if (delay_redraw)
	menu_trigger_redraw(page);
    else
	menu_trigger_redraw(LOC->prompt_page);

    if (criteria)
	free_string(& criteria);
}


int next_message(iindex, skipdel, menu)
     int iindex, skipdel;
     struct menu_common *menu;
     
{
    /** Given 'iindex', this routine will return the actual iindex into the
	array of the NEXT message, or '-1' iindex is the last.
	If skipdel, return the iindex for the NEXT undeleted message.
	If selected, return the iindex for the NEXT message marked VISIBLE.
    **/
    
    int remember_for_debug, stat;
    int mc       = mcommon_get_count(menu);
    int selected = mcommon_get_selected(menu);

    if (iindex < 0) return(-1);	/* invalid argument value! */
    
    remember_for_debug = iindex;

   

    for(iindex++; iindex < mc; iindex++) {

	if ((mcommon_ison_status(menu,iindex,
				 status_basic,VISIBLE) || !selected)
	    && (mcommon_isoff_status(menu,iindex,
				     status_basic,DELETED) || (!skipdel))) {
	    DPRINT(Debug,9,(&Debug,  
			    "[Next%s%s: given %d returning %d]\n", 
			    (skipdel ? " undeleted" : ""),
			    (selected ? " visible" : ""),
			    remember_for_debug+1, iindex+1));
	    return(iindex);
	}
    }
    return(-1);
}

int prev_message(iindex, skipdel, menu)
     int iindex, skipdel;
     struct menu_common *menu;
{
    /** Like next_message, but the PREVIOUS message. **/
    
    int remember_for_debug, stat;    
    int mc       = mcommon_get_count(menu);
    int selected = mcommon_get_selected(menu);
    
    if (iindex >= mc) 
	return(-1);	/* invalid argument value! */
    
    remember_for_debug = iindex;
    
    for(iindex--; iindex >= 0; iindex--) {
		
	if ((mcommon_ison_status(menu,iindex,
				 status_basic,VISIBLE) || (!selected))
	    && (mcommon_isoff_status(menu,iindex,
				     status_basic,DELETED) || (!skipdel))) {
	    DPRINT(Debug,4,(&Debug, 
			    "[Previous%s%s: given %d returning %d]\n", 
			    (skipdel  ? " undeleted" : ""),
			    (selected ? " visible" : ""),
			    remember_for_debug+1, iindex+1));
	    return(iindex);
	}
    }
    return(-1);
}

int compute_visible(message,menu)
     int message;
     struct menu_common *menu;
{

    /** return the 'virtual' iindex of the specified message in the
	set of messages - that is, if we have the 25th message as
	the current one, but it's #2 based on our limit criteria,
	this routine, given 25, will return 2.
    **/

    int iindex, count = 0;

    if (! mcommon_get_selected(menu)) 
	return(message);

    if (message < 1) message = 1;	/* normalize */

    for (iindex = 0; iindex < message; iindex++) {
	
	DPRINT(Debug,4,(&Debug, 
			"[compute-visible: displayed message %d is actually %d]\n",
			count, message));
	
	if (mcommon_ison_status(menu,iindex,status_basic,VISIBLE))
	    count++;
    }
    
    DPRINT(Debug,4,(&Debug, 
		    "[compute-visible: displayed message %d is actually %d]\n",
		    count, message));
    
    return(count);
}

int visible_to_index(message,menu)
     int message;
     struct menu_common *menu;
{
    /** Given a 'virtual' iindex, return a real one.  This is the
	flip-side of the routine above, and returns (message_count+1)
	if it cannot map the virtual iindex requested (too big) 
    **/
    
    int iindex = 0, count = 0;
    int mc = mcommon_get_count(menu);
    
    for (iindex = 0; iindex < mc; iindex++) {
	
	if (mcommon_ison_status(menu,iindex,status_basic,VISIBLE))
	    count++;
	if (count == message) {
	    DPRINT(Debug,4,(&Debug, 			       
			    "visible-to-index: (up) index %d is displayed as %d\n",
			    message, iindex));
	    return(iindex);
	}
    }
    
    DPRINT(Debug,4,(&Debug,  "index %d is NOT displayed!\n", message));
	
    return mc+1;
}

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1