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

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.10 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI>
 *****************************************************************************/

#include "def_screen.h"

DEBUG_VAR(Debug,__FILE__,"screen");

struct menu_context ROOT_MENU = {
    MENU_CONTEXT_magic,

    &CURSES_ROUTINES,   /* menu_draw_routine */
    NULL, NULL,         /* subpages, container */

    0,0,        /* lines, columns  */
    0,0,        /* changed, redraw */

    0,          /* modes */
    0,0,        /* row,col */

    NULL,       /* dummy */

    NULL        /* prev */,
    NULL        /* next */
};


struct menu_context COOKED_MENU = {
    MENU_CONTEXT_magic,

    &CURSES_ROUTINES,   /* menu_draw_routine */
    NULL, NULL,         /* subpages, container */

    0,0,        /* lines, columns  */
    0,0,        /* changed, redraw */

    0,          /* modes */
    0,0,        /* row,col */

    NULL,       /* dummy */

    NULL        /* prev */,
    NULL        /* next */
};


struct menu_context  * default_context = &COOKED_MENU;

struct menu_context  * new_menu_type(T)
     struct  menu_draw_routine *T;
{
    struct menu_context  *ret = safe_malloc(sizeof (*ret));

    if (MENU_DRAW_magic != T->magic)
	panic("SCREEN PANIC",__FILE__,__LINE__,"new_menu_type",
	      "Bad routine magic number",0);
	
    /* bzero is defined hdrs/defs.h */
    bzero((void *)ret,sizeof (*ret));

    ret->magic      = MENU_CONTEXT_magic;
    ret->routine    = T;

    ret->subpages   = NULL;
    ret->container  = NULL;

    ret->lines      = 0;
    ret->columns    = 0;
    ret->changed    = 0;
    ret->redraw     = 0;

    ret->modes      = 0;
    ret->row        = 0;
    ret->col        = 0;

    ret->u.dummy    = NULL;

    T->wra_init(ret);

    return ret;
}

struct menu_context  * new_menu_context()
{
    struct menu_context  *ret =  new_menu_type(&CURSES_ROUTINES);

    /* Take data from root menu */

    ret->lines      = ROOT_MENU.lines;
    ret->columns    = ROOT_MENU.columns;
    ret->changed    = ROOT_MENU.changed;
    ret->redraw     = ROOT_MENU.redraw;

    ret->modes      = ROOT_MENU.modes;
    ret->row        = ROOT_MENU.row;
    ret->col        = ROOT_MENU.col;

    /* chain to list */

    ret->next_menu       = ROOT_MENU.next_menu;
    ROOT_MENU.next_menu  = ret;
    ret->prev_menu       = &ROOT_MENU;

    if (ret->next_menu) 
	ret->next_menu->prev_menu = ret;

    if (default_context == &COOKED_MENU) {
	DPRINT(Debug,1, (&Debug, 
			 "new_menu_context: On COOKED MENU, not changing default context\n"));
    } else {
	default_context = ret;

	DPRINT(Debug,9, (&Debug, 
			 "new_menu_context: default context to %p\n",
			 default_context));
	
	Syncronize(default_context);
    }

    return ret;
}

void  erase_menu_context(ptr)
     struct menu_context  **ptr;
{
    struct  menu_subpages *list, *next = NULL;

    if ((*ptr)->magic != MENU_CONTEXT_magic)
	panic("SCREEN PANIC",__FILE__,__LINE__,"erase_menu_context",
	      "Bad type magic number",0);
    
    if (&ROOT_MENU == *ptr)
	panic("SCREEN PANIC",__FILE__,__LINE__,"erase_menu_context",
	      "attempt to erase root menu",0);

    if (&COOKED_MENU == *ptr)
	panic("SCREEN PANIC",__FILE__,__LINE__,"erase_menu_context",
	      "attempt to erase cooked menu",0);
	
    if (MENU_DRAW_magic != (*ptr)->routine->magic) 
	panic("SCREEN PANIC",__FILE__,__LINE__,"erase_menu_context",
	      "Bad routine magic number",0);

    EndSyncronize(*ptr);

    if ((*ptr)->container)
	detach_subpage(*ptr);

    if ((*ptr)->subpages) {
	DPRINT(Debug,1, (&Debug, 
			 "erase_menu_context: %p: WARNING: subpages not erased / detached\n",
			 *ptr));

	for (list = (*ptr)->subpages; list; list = next) {
	    if (MENU_CONTAINER_magic != list->magic)
		panic("SCREEN PANIC",__FILE__,__LINE__,"erase_menu_context",
		      "Bad subpage type magic number",0);
	    if (list->parent != *ptr) 
		panic("SCREEN PANIC",__FILE__,__LINE__,"erase_menu_context",
		      "Bad parent pointer on subpage",0);
	    next = list->next;
	    
	    if (list->subpage) {
		if (list != list->subpage->container)
		    panic("SCREEN PANIC",__FILE__,__LINE__,"erase_menu_context",
			  "Bad container pointer on subpage",0);
		
		/* Reset congtainer pointer so that erase menu
		   content do not call detach_subpage()
		*/
		list->subpage->container = NULL;

		erase_menu_context( & (list->subpage) );
	    }
	    
	    list->prev = NULL;
	    list->next = NULL;
	    list->magic = 0;    /* Invalidate */
	    free(list);
	}

	(*ptr)->subpages = NULL;
    }

    if (default_context == (*ptr)) {
	default_context = (*ptr)->prev_menu;

	DPRINT(Debug,9, (&Debug, 
			 "erase_menu_context: default context to %p\n",
			 default_context));

    }

    if (!default_context)
	panic("SCREEN PANIC",__FILE__,__LINE__,"erase_menu_context",
	      "no default content",0);

    Syncronize(default_context);
	
    if ((*ptr)->next_menu)
	(*ptr)->next_menu->prev_menu   = (*ptr)->prev_menu;
    if ((*ptr)->prev_menu)
	(*ptr)->prev_menu->next_menu   = (*ptr)->next_menu;

    (*ptr)->routine->wra_free(*ptr);

    if ((*ptr)->u.dummy) {
	DPRINT(Debug,1, (&Debug, 
			 "erase_menu_context: Data not free'ed\n"));
    }


    (*ptr)->magic = 0;         /* Invalidate */
    free(*ptr);
    *ptr = NULL;
}

void attach_subpage(parent,subpage, first_line, last_line)  
     struct menu_context *parent;
     struct menu_context *subpage;
     /* absolute screen cordinates */
     int first_line, last_line;
{
    struct menu_subpages   * X;

    if (MENU_CONTEXT_magic != parent->magic)
	panic("SCREEN PANIC",__FILE__,__LINE__,"attach_subpage",
	      "Bad parent type magic number",0);

    if (MENU_CONTEXT_magic != subpage->magic)
	panic("SCREEN PANIC",__FILE__,__LINE__,"attach_subpage",
	      "Bad subpage type magic number",0);
    
    X = safe_malloc(sizeof (*X));

    /* bzero is defined hdrs/defs.h */
    bzero((void *)X,sizeof (*X));

    X->magic = MENU_CONTAINER_magic;
    X->location.first_line = first_line;
    X->location.last_line  = last_line;
    X->location.gone_out   = 0;

    subpage->lines     = last_line - first_line + 1;
    subpage->columns   = parent->columns;
    subpage->redraw    = 1;
    subpage->container = X;

    X->subpage         = subpage;
    X->parent          = parent;
    X->next            = parent->subpages;
    X->prev            = NULL;
    if (parent->subpages) {
	if (parent->subpages->prev) 
	    panic("SCREEN PANIC",__FILE__,__LINE__,"attach_subpage",
		  "parent's subpage list do not point to first item",0);

	parent->subpages->prev = X;
    }
    parent->subpages   = X;

}


extern void detach_subpage(subpage)
     struct menu_context *subpage;
{
    if (MENU_CONTEXT_magic != subpage->magic)
	panic("SCREEN PANIC",__FILE__,__LINE__,"detach_subpage",
	      "Bad subpage type magic number",0);
    
    if (MENU_CONTAINER_magic != subpage->container->magic)
	panic("SCREEN PANIC",__FILE__,__LINE__,"detach_subpage",
	      "Bad container type magic number",0);

    if (MENU_CONTEXT_magic != subpage->container->parent->magic)
	panic("SCREEN PANIC",__FILE__,__LINE__,"detach_subpage",
	      "Bad parent type magic number",0);
    if (subpage->container->subpage != subpage)
	panic("SCREEN PANIC",__FILE__,__LINE__,"detach_subpage",
	      "Bad subpage of container",0);
	
	
    /* Remove from list */
    if (subpage->container->prev) {
	if (subpage->container != subpage->container->prev->next)
	    panic("SCREEN PANIC",__FILE__,__LINE__,"detach_subpage",
		  "Bad prev of container",0);

	subpage->container->prev->next = subpage->container->next;
    } else {
	if (subpage->container != subpage->container->parent->subpages)
	    panic("SCREEN PANIC",__FILE__,__LINE__,"detach_subpage",
		  "Bad subpages of parent",0);
	    	
	subpage->container->parent->subpages = subpage->container->next;
    }

    if (subpage->container->next) {
	if (subpage->container != subpage->container->next->prev)
	    panic("SCREEN PANIC",__FILE__,__LINE__,"detach_subpage",
		  "Bad next of container",0);
	    
	subpage->container->next->prev = subpage->container->prev;
    }

    subpage->container->subpage = NULL;
    subpage->container->magic   = 0;   /* Invalidate */
    free(subpage->container);
    subpage->container = NULL;
}

static struct menu_context * get_root_menu P_((struct menu_context *ptr,
					       int *pending_redraw));
static struct menu_context * get_root_menu(ptr,pending_redraw)
     struct menu_context *ptr;
     int *pending_redraw;
{
    struct menu_context *pptr = ptr;

    while (pptr->container) {
	if (MENU_CONTAINER_magic != pptr->container->magic)
	    panic("SCREEN PANIC",__FILE__,__LINE__,"get_root_menu",
		  "Bad subpage container type magic number",0);
	
	pptr = pptr->container->parent;

	if (pptr->redraw)
	    (*pending_redraw) ++;

	if (pptr->magic != MENU_CONTEXT_magic)
	    panic("SCREEN PANIC",__FILE__,__LINE__,"get_root_menu",
		  "Bad parent type magic number",0);
    }

    if (pptr != default_context) {
	DPRINT(Debug,7, (&Debug, 
			 "get_root_menu(%p): root %p is not default context %p\n",
			 ptr,pptr,default_context));
    }

    return pptr;
}


int   menu_resized(ptr)
     struct menu_context *ptr;
{
    if (ptr->magic != MENU_CONTEXT_magic)
	panic("SCREEN PANIC",__FILE__,__LINE__,"menu_resized",
	      "Bad type magic number",0);

    check_changes();

   
    if (ptr->changed) {
	struct  menu_subpages *list, *next = NULL;

	if (MENU_DRAW_magic != ptr->routine->magic) 
	    panic("SCREEN PANIC",__FILE__,__LINE__,"menu_resized",
		  "Bad routine magic number",0);


	ptr->routine->wra_resized(ptr);
	
	/* Turn changed flag for subpages ... */

	for (list = ptr->subpages; list; list = list->next) {
	    if (MENU_CONTAINER_magic != list->magic)
		panic("SCREEN PANIC",__FILE__,__LINE__,"menu_resized",
		      "Bad subpage container type magic number",0);
	    if (list->parent != ptr) 
		panic("SCREEN PANIC",__FILE__,__LINE__,"menu_resized",
		      "Bad parent pointer on subpage",0);

	    if (list->subpage) {
		if (MENU_CONTEXT_magic != list->subpage->magic)
		    panic("SCREEN PANIC",__FILE__,__LINE__,"menu_resized",
			  "Bad subpage type magic number",0);
		list->subpage->changed = 1;
	    }
	}

	return 1;
    }

    return 0;
}

void  menu_get_sizes(ptr,lines,columns)
     struct menu_context *ptr;
     int *lines; 
     int *columns;
{
    if (ptr->magic != MENU_CONTEXT_magic)
	panic("SCREEN PANIC",__FILE__,__LINE__,"menu_get_sizes",
	      "Bad type magic number",0);

    *lines    = ptr->lines;
    *columns  = ptr->columns;
}


int   menu_need_redraw(ptr) 
     struct menu_context *ptr;
{
    struct menu_context *pptr;
    int pending_redraw = 0;

    if (ptr->magic != MENU_CONTEXT_magic)
	panic("SCREEN PANIC",__FILE__,__LINE__,"menu_need_redraw",
	      "Bad type magic number",0);

    check_changes();

    /* Check parents */
    pptr = get_root_menu(ptr,&pending_redraw);

    if (pending_redraw) {
	DPRINT(Debug,7, (&Debug, 
			 "menu_need_redraw(%p): pending redraw on parent\n",
			 ptr));
	ptr->redraw = 1;
    }	    

    if (pptr->routine != &CURSES_ROUTINES) {
	DPRINT(Debug,1, (&Debug, 
			 "menu_need_redraw(%p): ERROR -- parent %p not main manu (not connected) -- quiting%s\n",
			 ptr,pptr,
			 ptr->redraw ? " (returning redraw)": ""));

	return ptr->redraw;
    }

    if (pptr != default_context) {
	DPRINT(Debug,9, (&Debug, 
			 "menu_need_redraw: default context to %p (redraw needed)\n",
			 pptr));
	default_context = pptr;
	Syncronize(default_context);
	pptr->redraw = 1;
	ptr->redraw  = 1;
    }

    if (ptr->redraw) {
	struct  menu_subpages *list;
	int r;

	if (MENU_DRAW_magic != ptr->routine->magic) 
	    panic("SCREEN PANIC",__FILE__,__LINE__,"menu_need_redraw",
		  "Bad routine magic number",0);


	/* Reset ptr->redraw ....  */
	ptr->routine->wra_reset_redraw(ptr);


	/* Turn redraw flag for subpages ... */

	for (list = ptr->subpages; list; list = list->next) {
	    if (MENU_CONTAINER_magic != list->magic)
		panic("SCREEN PANIC",__FILE__,__LINE__,"menu_need_redraw",
		      "Bad subpage container type magic number",0);
	    if (list->parent != ptr) 
		panic("SCREEN PANIC",__FILE__,__LINE__,"menu_need_redraw",
		      "Bad parent pointer on subpage",0);

	    if (list->subpage) 
		menu_trigger_redraw(list->subpage);
	}


	r = ptr->routine->wra_do_redraw(ptr);

	if (r) {
	    DPRINT(Debug,7, (&Debug, 
			     "menu_need_redraw(%p): redraw done\n", ptr));
	} else {
	    DPRINT(Debug,7, (&Debug, 
			     "menu_need_redraw(%p): returning redraw\n",
			     ptr));
	    return 1;
	}
    }

    return 0;
}

/* Call refresh routines of children */
void menu_redraw_children(ptr)
     struct menu_context *ptr;
{
    struct  menu_subpages *list;

    if (ptr->magic != MENU_CONTEXT_magic)
	panic("SCREEN PANIC",__FILE__,__LINE__,"menu_redraw_children",
	      "Bad type magic number",0);
    
    for (list = ptr->subpages; list; list = list->next) {
	if (MENU_CONTAINER_magic != list->magic)
	    panic("SCREEN PANIC",__FILE__,__LINE__,"menu_redraw_children",
		  "Bad subpage container type magic number",0);

	if (list->parent != ptr) 
	    panic("SCREEN PANIC",__FILE__,__LINE__,"menu_redraw_children",
		  "Bad parent pointer on subpage",0);
	
	if (list->subpage) {
	    struct menu_context *ptr1 = list->subpage;
	    int r;
	    
	    if (ptr1->magic != MENU_CONTEXT_magic)
		panic("SCREEN PANIC",__FILE__,__LINE__,"menu_redraw_children",
		      "Bad type magic number",0);
	    
	    
	    if (MENU_DRAW_magic != ptr1->routine->magic) 
		panic("SCREEN PANIC",__FILE__,__LINE__,"menu_redraw_children",
		      "Bad routine magic number",0);
	    
	    r = ptr1->routine->wra_do_redraw(ptr1);
	    
	    if (r) {
		DPRINT(Debug,7, (&Debug, 
				 "menu_redraw_children(%p): %p: redraw done\n",
				 ptr,ptr1));
	    } else
		ptr1->redraw = 1;		    
	}
    }    
}


void menu_trigger_redraw(ptr) 
     struct menu_context *ptr;
{
    struct menu_context *pptr;
    int pending_redraw = 0;
    int r;

    if (ptr->magic != MENU_CONTEXT_magic)
	panic("SCREEN PANIC",__FILE__,__LINE__,"menu_trigger_redraw",
	      "Bad type magic number",0);
    DPRINT(Debug,7, (&Debug, 
		     "menu_trigger_redraw(%p): triggering redraw\n",
		     ptr));

    if (MENU_DRAW_magic != ptr->routine->magic) 
	panic("SCREEN PANIC",__FILE__,__LINE__,"menu_trigger_redraw",
	      "Bad routine magic number",0);

    /* Check parents */

    pptr = get_root_menu(ptr,&pending_redraw);

    if (pptr == default_context) {
	/* Is active */

	if (pending_redraw) {
		DPRINT(Debug,7, (&Debug, 
				 "menu_trigger_redraw(%p): pending redraw on parent\n",
				 ptr));
	    ptr->redraw = 1;
	} else {

	    r = ptr->routine->wra_do_redraw(ptr);
	    
	    if (r) {
		DPRINT(Debug,7, (&Debug, 
				 "menu_trigger_redraw(%p): redraw done\n",
				 ptr));
	    } else
		ptr->redraw = 1;
	}
    } else {
	DPRINT(Debug,7, (&Debug, 
			 "menu_trigger_redraw(%p): parent %p not active\n",
			 ptr,pptr));
	ptr->redraw = 1;
    }
}


static VOLATILE int resize_screen = 0;	/** SIGWINCH occured?   **/
static VOLATILE int redraw_screen = 0;	/** Elm suspended?   **/


/* called from signal handler */
void menu_context_resize() 
{

    resize_screen = 1;
    redraw_screen = 1;

    ROOT_MENU.changed = 1;   /* Indicate that ROOT_MENU is invalid
				not really needed */
}

/* called from signal handler */
void menu_context_redraw() 
{

    redraw_screen = 1;
    ROOT_MENU.redraw = 1;     /* not really needed */
}

void set_root_menu(lines,columns)
     int lines; 
     int columns;
{
    int size = 0;

    if (lines != ROOT_MENU.lines &&
	-1 != lines) {
	ROOT_MENU.lines   = lines;
	COOKED_MENU.lines = lines;

	size ++;
    }

    
    if (columns != ROOT_MENU.columns &&
	-1 != columns) {
	ROOT_MENU.columns    = columns;
	COOKED_MENU.columns  = columns;

	size++;
    }

    if (size) {
	struct menu_context *ptr;

	for (ptr = ROOT_MENU.next_menu; ptr; ptr = ptr->next_menu) {

	    if (ptr->magic != MENU_CONTEXT_magic)
		panic("SCREEN PANIC",__FILE__,__LINE__,"set_root_menu",
		      "Bad type magic number",0);

	    ptr->changed = 1;
	}    
    }

}

void check_changes()
{

    if (resize_screen || redraw_screen) {
	int size = resize_screen;
	int draw = redraw_screen;

	struct menu_context *ptr;

	int lines, columns;

	resize_screen = 0;
	redraw_screen = 0;
	
	/* Real lines .. not lines-1 !!! */
	cur_ScreenSize(&lines,&columns);

	if (lines > 0 && columns > 0) {

	    /* Check that is size changed when
	       Elm ME+ was suspended
	    */
	    if (lines   != ROOT_MENU.lines ||
		columns != ROOT_MENU.columns)
		size++;
	    
	    ROOT_MENU.lines   = lines;
	    ROOT_MENU.columns = columns;
	    ROOT_MENU.changed = 0;       /* Values valid */
	    
	    COOKED_MENU.lines   = lines;
	    COOKED_MENU.columns = columns;

	} else {
	    	DPRINT(Debug,1, (&Debug, 
				 "check_changes: cur_ScreenSize returned bad sizes: lines=%d columns=%d\n",
				 lines,columns));
	}

	if (size && default_context == &COOKED_MENU)
	    COOKED_MENU.changed = 1;

	ROOT_MENU.redraw  = draw;    /* ? ? ? */

	if (draw && default_context == &COOKED_MENU)
	    COOKED_MENU.redraw = 1;

	for (ptr = ROOT_MENU.next_menu; ptr; ptr = ptr->next_menu) {

	    if (ptr->magic != MENU_CONTEXT_magic)
		panic("SCREEN PANIC",__FILE__,__LINE__,"check_changes",
		      "Bad type magic number",0);

	    if (size)
		ptr->changed = 1;
	    if (draw)
		ptr->redraw  = 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