static char rcsid[] = "@(#)$Id: builtin++.c,v 1.30 2006/04/09 07:37:17 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.30 $   $State: Exp $
 *
 *  Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI> 
 *                           (was hurtta+elm@ozone.FMI.FI)
 *
 *  Initially written by: Michael Elkins <elkins@aero.org>, 1995
 *****************************************************************************/

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

DEBUG_VAR(Debug,__FILE__,"ui");

#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif         
extern int tabspacing;

static void builtin_help P_((void));
static void builtin_help () 
{
    /* A help screen for the pager below. */
    int LINES, COLUMNS;
    struct menu_context *page = new_menu_context();

redraw:

    if (menu_resized(page) ||
	menu_need_redraw(page))
	/* Nothing */;
        
    menu_get_sizes(page, &LINES, &COLUMNS);   

    menu_ClearScreen(page);

    StartInverse();
    Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpTitle,
			     "Help for builtin++"));
  EndInverse();
  NewLine ();
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpKeyA,
			   "Key\t\tAction"));
  NewLine ();
  Write_to_screen (FRM("---\t\t------"));
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpSpace,
			   "<SPACE>, +\tNext page."));
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpMinus,
			   "-\t\tPrevious page."));
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpReturn,
			   "<RETURN>\tNext line."));
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpq,
			   "q, x, i\t\tReturn to the index menu."));
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpDiv,
			   "/\t\tSearch for pattern in message."));
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpArr,
			   "^\t\tFirst page."));
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpG,
			   "G\t\tLast page."));
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpCtrlL,
			   "^L\t\tRefresh display."));
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpCtrlP,
			   "^P\t\tUp one line."));
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpCtrlP,
			   "^D\t\tDown one-half page."));
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpCtrlU,
			   "^U\t\tUp one-half page."));
  NewLine ();
  Write_to_screen (CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpQuestion,
			   "?\t\tThis help screen."));
  NewLine ();

  menu_PutLineX (page,
		 LINES-1, 0, 
		 CATGETS(elm_msg_cat, ElmSet, ElmBuiltinHelpPressRet,
			 "Press any key to return..."));
  if (REDRAW_MARK == menu_ReadCh(page,REDRAW_MARK))
      goto redraw;


  erase_menu_context(&page);
  return;
}

static int search_it P_((struct stringbuffer * bout, int start_idx,
			 struct string * search_pattern));

static int search_it(bout,start_idx,search_pattern)
     struct stringbuffer * bout; 
     int start_idx;
     struct string * search_pattern;
{
    int ret = 0;
    int idx = start_idx;

    while (idx < linecount_stringbuffer(bout) && !ret) {
	struct string * buffer = get_line_from_stringbuffer(bout,idx);

	if (find_pattern_from_string(buffer,search_pattern,1))
	    ret = idx;
	
	idx++;
	free_string(&buffer);
    }
    DPRINT(Debug,3,
	   (&Debug,"Builtin pager: Search from line %d %s (%d)\n",
	    start_idx, 
	    ret ? "SUCCEED" : "FAILED",
	    ret));
    return ret;
}

int builtinplusplus (bout,pager_page)
     struct stringbuffer * bout;
     struct pager_page * pager_page;
{   
    int idx = 0;
    int is_end = 0;

    /* Current line for outputting and position on it ... */
    struct string * buffer = NULL;
    int                ret = '\0';
    int X = 0;
    struct string * search_pattern = NULL;        /* Last seach string */
   
    clear_error();
    
    for (;;) {
	int idx_top;             /* line number of top of display  */
	int ch;                  /* command key read */
	int search_matches = 0;  /* First idx on page where search matches */
	int lines;               /* Number of visible lines printed
				    so far */
	int FF_seen;            /* If page is breaken because of FF */
	int LINES, COLUMNS;

 new_page:
    resize_mark:
	idx_top = idx;       /* line number of top of display  */
	lines = 0;           /* Number of visible lines printed
				so far */
        FF_seen = 0;

	menu_ClearScreen(pager_page->root);

    cr_restart:                  /* so that we can handle CR specially --
				    ie. don't reset page variables 
				 */

	menu_get_sizes(pager_page->root, &LINES, &COLUMNS);

    

	DPRINT(Debug,5,(&Debug, "builtin++ top  idx = %d\n",idx));

	while ((idx < linecount_stringbuffer(bout)) &&
	       lines < LINES-1 && !FF_seen) {
	    
	    int chars = 0; /* Number of characters on current line */
	    int mayclear = 1;
	    int buffer_len = -1;
	    int visible_len;
	

	    if (menu_resized(pager_page->root)) {
		menu_get_sizes(pager_page->root, &LINES, &COLUMNS);

		if (buffer)
		    free_string(&buffer);
	    
		idx= idx_top;
		goto new_page;
	    }

	    if (menu_need_redraw(pager_page->root)) {

		if (buffer)
		    free_string(&buffer);

		idx= idx_top;
		goto new_page;

	    }
    
	    DPRINT(Debug,9,(&Debug, 
			    "builtin++ before line  idx = %d X=%d\n",
			    idx,X));

	    if (!buffer || X >= (buffer_len = string_len(buffer))) {
		/* caller of get_line_from_stringbuffer is expected to 
		   free_string () resulting buffer */

		if (buffer) {
		    if (X >= buffer_len)
			idx++;
		    if (idx >= linecount_stringbuffer(bout))
			break;
		    free_string (&buffer);
		}
		X = 0;

		/* There is no pending data to output, so we need to grab 
		 * another line.
		 */

		buffer = get_line_from_stringbuffer(bout,idx);		
		buffer_len = string_len(buffer);
	    }

	    /* This is the part of the code that actually displays on 
	     * the screen 
	     */
	    chars = 0;
	    while (X < buffer_len) {
		uint16 ch;
		int len = 0;   /* Length for next clip ... */
		struct string * data;

		/* If no space for next character */
		if (chars >= COLUMNS)
		    break;
		
		ch = give_unicode_from_string(buffer,X);
		if (ch <= 31) {
		    
		    /* Make sure that carbage from previous
		       output is not left */
		    if (mayclear) {
			CleartoEOLN();
			mayclear = 0;
		    }

		    switch(ch) {
		    case 0x0009:  /* HT */
			Writechar('\t');
			chars = ((chars / get_tabspacing() ) +1) * 
			    get_tabspacing(); 
			break;
		    case 0x000C:   /* FF */
		    case 0x000B:   /* VT */
			FF_seen = 1;
			/* FALLTHRU */
		    case 0x000A:   /* LF */
			X++;
			goto out;

		    default:
			/* This won't fit on the line, so just skip it 
			   for now */
			if (chars == COLUMNS - 1)
			    break;
			/* Output character set is assumed to be ASCII
			   compatible on here: */
			StartBold();
			Writechar('^');
			Writechar(ch + 64);
			EndBold();
			chars += 2;
			break;
		    }

		    X++;
		    continue;
		}

		DPRINT(Debug,9,(&Debug, 
				"builtin++ after ctrl X=%d (chars = %d)\n",
				X,chars));
	
		/* calculate clip len ... */
		while (X + len     < buffer_len &&
		       chars + len < COLUMNS) {
		    ch = give_unicode_from_string(buffer,X+len);
		    if (ch <= 31)
			break;
		    len++;
		}
		
		DPRINT(Debug,9,(&Debug, 
				"builtin++ after text  X=%d ( len = %d)\n",
				X,len));
		
		if (len < 1)
		    continue;    /* next character was also control char */

		/* clip_from_string updates X */
		data = curses_printable_clip(buffer,&X,len,&visible_len,
					     COLUMNS - chars);

		if (data) {

		    chars += visible_len;   
		    
		    DPRINT(Debug,9,(&Debug, 
				    "builtin++ after clip  idx = %d X=%d (len = %d, chars=%d  visible_len=%d)\n",
				    idx,X,len,chars, visible_len));
		    
		    if (search_matches &&
			search_matches == idx)
			StartBold();
		    Write_to_screen(FRM("%S"),data);
		    if (search_matches &&
			search_matches == idx)
			EndBold();		
		    free_string(&data);
		    
		    if (visible_len < 1) {
			DPRINT(Debug,9,(&Debug, 
					"builtin++ --- No space for next character on line\n"));
			goto out;
		    }

		} else {
		    /* Treat next character as control character */

		    /* Output character set is assumed to be ASCII
		       compatible on here: */
		    StartBold();
		    Writechar('?');		    
		    EndBold();

		    chars += 1;
		    X++;
		}
	    }

	    /* Make sure that carbage from previous
	       output is not left */
	    if (mayclear) {
		CleartoEOLN();
		mayclear = 0;
	    }


		 out:
	    NewLine();
	    lines++;
	}

	CleartoEOS();
	if (lines < LINES-1 && FF_seen) {
	    StartBold();
	    PutLineX (lines, 0, 
		      CATGETS(elm_msg_cat, ElmSet, ElmBuiltinNewPage,
			      "~ (new page) "));
	    EndBold();  CleartoEOLN(); 
	    NewLine();
	    lines++;
	}
	while (lines < LINES-1) {
	    Writechar('~'); NewLine();
	    CleartoEOLN();
	    lines++;
	}

    reprompt:
	StartBold();
	
	if (idx >= linecount_stringbuffer(bout)) {
	    menu_PutLineX (pager_page->root,LINES-1, 0, 
			   CATGETS(elm_msg_cat, ElmSet, ElmBuiltinCommandi,
				   "Command ('i' to return to index):"));
	    is_end = 1;
	}
	else {
	    int relative;
	    int ul = give_dt_enumerate_as_int(&user_level);

	    if (linecount_stringbuffer(bout) < 1)
		relative = 100;
	    else
		relative = 100 * idx / linecount_stringbuffer(bout);
	    
	    if (ul == 0)
		menu_PutLineX(pager_page->root,LINES-1, 0, 
			      CATGETS(elm_msg_cat, ElmSet, ElmBuiltInMore0,
				      "MORE (line %d-%d/%d, you've seen %d%%). Press <space> for more:"), 
			      idx_top+1,idx,linecount_stringbuffer(bout),
			      relative);
	    else
		menu_PutLineX(pager_page->root,
			      LINES-1, 0, 
			      CATGETS(elm_msg_cat, ElmSet, ElmBuiltInMore,
				      "MORE (line %d-%d/%d, you've seen %d%%):"), 
			      idx_top+1,idx,linecount_stringbuffer(bout),
			      relative);
	    is_end = 0;
	}
	EndBold();
	CleartoEOS();
    read_key:
	ch = menu_ReadCh(pager_page->root, REDRAW_MARK|READCH_CURSOR|READCH_resize|
			 READCH_sig_char);

	if (ch != '/') {
	    if (search_pattern) {
		free_string(&search_pattern);
	    }
	}

	switch (ch) {
	case TERMCH_interrupt_char:
	    ret = 0;
	    goto finish;

	case RESIZE_MARK:
	    DPRINT(Debug,4, (&Debug, "    ... resizing\n"));
	    idx = idx_top;
	    goto resize_mark;

	case ' ':
	case '+':
	case PAGEDOWN_MARK:
	    if (is_end) {
		ret = ' ';
		goto finish;
	    }
	break;
	case HOME_MARK:
	case '^':      
	    idx = 0;
	    /* Avoid incrementing of idx: */
	    if (buffer)    
		free_string (&buffer);
	    break;
	case 'G':
	    /* Not completely correct if multiline lines */
	    idx = linecount_stringbuffer(bout)-LINES;
	    if (idx < 0)
		idx = 0;
	    lines = 0;
	    /* Avoid incrementing of idx: */
	    if (buffer)    
		free_string (&buffer);
	    menu_ClearScreen(pager_page->root);
	    break;
	case FIND_MARK:
	case '/':
	    ClearLine (LINES-1);
	    {
		int code;
		
		code = optionally_enter2(pager_page->root,
					 &search_pattern,
					 LINES-1, 0, 
					 OE_REDRAW_MARK|
					 OE_SIG_CHAR /* Ctrl-C */, 
					 CATGETS(elm_msg_cat, ElmSet, 
						 ElmBuiltinSearch,
						 "Search: "));

		if (REDRAW_MARK == code)
		    goto redraw;
		if (-1 == code) /* Ctrl-C */
		    goto reprompt;
		if (code != 0)
		    goto quit;
		if (search_pattern && string_len(search_pattern)) {
		    int start_idx;
		    /* Continue search from previous match */
		    if (!search_matches)
			start_idx = idx_top;
		    else if (search_matches < linecount_stringbuffer(bout))
			start_idx = search_matches+1;
		    else
			start_idx = idx_top;
		    search_matches = search_it(bout,start_idx,
					       search_pattern);
		    if (search_matches) {
			if (search_matches > idx_top && 
			    search_matches < idx_top + LINES -4)
			    idx = idx_top;
			else
			    idx = search_matches - LINES / 2;
			if (idx < 0)
			    idx = 0;
			/* Avoid incrementing of idx: */
			if (buffer)    
			    free_string (&buffer);
			goto new_page; /* and draw match */
		    } else {
			int relative;
			int ul = give_dt_enumerate_as_int(&user_level);

			if (linecount_stringbuffer(bout) < 1)
			    relative = 100;
			else
			    relative = 100 * idx / linecount_stringbuffer(bout);
			
			ClearLine (LINES-1);
			StartBold();
			if (ul == 0)
			    PutLineX (LINES-1, 0, 
				      CATGETS(elm_msg_cat, ElmSet, ElmBuiltInNotFound0,
					      "NOT FOUND! (line %d-%d/%d, you've seen %d%%). Press <space> for more:"), 
				      idx_top+1,idx,linecount_stringbuffer(bout),
				      relative);
		    else
			PutLineX (LINES-1, 0, 
				  CATGETS(elm_msg_cat, ElmSet, 
					  ElmBuiltInNotFound,
					  "NOT FOUND! (line %d-%d/%d, you've seen %d%%):"), 
				  idx_top+1,idx,linecount_stringbuffer(bout),
				  relative);		    
			EndBold();
			goto read_key;
		    }
		} 
		goto reprompt;
	    }	    
	case PAGEUP_MARK:
	case '-':
	    idx = idx_top - LINES;
	    if (idx < 0)
		idx = 0;
	    if (buffer)
		free_string(&buffer);
	    break;
	case ctrl('P'):
	    idx = idx_top -1;
	    if (idx < 0)
		idx = 0;
	    if (buffer)
		free_string(&buffer);
	    break;
	case ctrl('D'):
	    if (is_end) 
		goto reprompt; /* Can't go down */
	    lines = LINES / 2;
	    CarriageReturn();

	    CleartoEOLN();	    
	    idx_top += LINES / 2;
	    goto cr_restart; /* Hanlde Ctrl-D specially */
	    	   	    
	case ctrl('U'):
	    idx = idx_top - LINES / 2;
	    if (idx < 0)
		idx = 0;
	    if (buffer)
		free_string(&buffer);
	    break;
	case HELP_MARK:
	case '?':
	    builtin_help ();
	    /* FALLTHRU */
	redraw:
	case REDRAW_MARK:
	case ctrl('L'): 
	    idx = idx_top;
	    if (buffer)
		free_string(&buffer);
	    break;
	case '\n':
	    if (FF_seen)
		break;   /* New page */
	    {
		int x, y;
		
		GetXYLocation (&x, &y);
		ClearLine (x);
	    }
	    lines = LINES - 2;
	    idx_top++;
	    goto cr_restart; /* Hanlde CR specially */
	quit:
	case EOF:
	default:
	    ret =  (ch == 'q' || ch == 'x' ? 0 : ch);
	    goto finish;
	}
    }

 finish:

    if (buffer)
	free_string (&buffer);
    DPRINT(Debug,9,(&Debug, 
		    "builtin++ returning %c\n",ret));
    return ret;
}

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


syntax highlighted by Code2HTML, v. 0.9.1