/*
 * $Source: $
 * $Revision: $
 * $Date: $
 * $State: $
 * $Author: $
 *
 *
 * $Log: $
 * Revision 3.1  3/10/92 wade
 * changed flag variable to atoi(flag)
 *
 * Revision 3.1  4/23/92 wade
 * Added list_scroll_page_up()
 *       list_scroll_page_down()
 *		 list_scroll_home()
 *		 list_scroll_end()
 * 	     list_up(), list_down()
 *
 */

/*  
 *	Copyright (c) 1991 by the Massachusetts Institute of Technology,
 *	For copying and distribution information, see the file
 *	"mit-copyright.h".
 *
 *  listMgr.c  - WADE 8/23/89
 *		All the list manager toolbox routines are located in this file.
 *		These routines all begin with a L (as in capital L).
 *
 *	
 *	list_init
 *	list_bind
 *	list_configure
 *	list_draw_off
 *	list_draw_on
 *	list_draw
 *	list_size
 *	list_click
 *	list_vscroll
 *	list_unselect
 *	list_get_buffer
 *	list_last_click_buffer
 *	list_get_list_select
 *	list_end
 *	list_select
 *	list_get_vscroll
 *	list_get_size
 *	list_remove_hilight
 *  list_scroll_page_up
 *  list_scroll_page_down
 *	list_scroll_home
 *  list_scroll_end
 *  list_down
 *  list_up
 */
#include <Limits.h>
#include <Types.h>
#include <QuickDraw.h>
#include <Fonts.h>
#include <Events.h>
#include <Controls.h>
#include <Windows.h>
#include <Menus.h>
#include <TextEdit.h>
#include <Dialogs.h>
#include <Desk.h>
#include <Scrap.h>
#include <ToolUtils.h>
#include <Memory.h>
#include <SegLoad.h>
#include <Files.h>
#include <OSUtils.h>
#include <Traps.h>
#include <Lists.h>
#include <string.h>
#include "pips.h"
#include <resources.h>
#include "prototype.h"
#include <stdio.h>
#include "mit-copyright.h"

extern int			gListSize;
extern ListHandle 	glH;
extern WindowPtr	gListWindow;

/*     
 *
 * 	list_init:
 * 
 *	Setup glH (pointer to list object) with one cell per row.
 */
void
list_Init(window)
WindowPtr	window;
{
	Rect		dataBounds, myRect;
	Point		cSize;
	
	myRect = window -> portRect;
	/* myRect.top = myRect.top + 16; test */
	myRect.right -= SCROLL_BAR_PIXELS;
	myRect.bottom -= SCROLL_BAR_PIXELS;
	
	/* this determines list matrix, which is 0,1 starting at 0,0 */
	dataBounds.left = 0;
	dataBounds.top = 0;
	dataBounds.right = 1;
	dataBounds.bottom = 0;
	
	cSize.v = 0;
	cSize.h = MAX_CELL_SIZE; 
	
	glH = LNew( &myRect, &dataBounds, cSize, 400, window, true, true, false, true);
	
	gListSize = 0;
}

/*     
 *
 * 	list_bind:
 * 
 *	Place value in buffer parameter into cell pointed to by cell_ptr.
 *
 */
Boolean
list_Bind( buffer, length, curr_ptr)
char   *buffer;
int     length;
int     curr_ptr;
{
	Point	cell;
	
	/* let's get out of here if we have reached the limit of the list size */
	/* assume each cell has the same length. */
	if (( curr_ptr > gListSize ) || 
		( ((**glH).cellArray[curr_ptr] + length) > 32767 )) {
		/* ((( curr_ptr * length) + length) > 32767 )) { */
		
		/* put up an error message */
		return false;
	}
	
	list_Draw_Off();	/* don't draw new cell value until list_Draw call 	*/
						/* LSetCell: place data value into cell				*/
						
	/*  create a new cell if it does not alreay exist */
	/*  remember, gListSize is the actual number of cells, where curr_ptr is one 
		less than gListSize as it uses the range of [0:n]. */
	if (curr_ptr == gListSize) {
		LAddRow( 1 , gListSize, glH );
		gListSize = gListSize + 1;
	}
	
	cell.v = curr_ptr;  /* this field is the list row index */
	cell.h = 0;
			
	LSetCell( buffer, length , cell, glH );  
	
	list_Draw_On();	
	
	return true;
}

/*     
 *
 * 	list_configure
 * 
 *	After creating a new list, this routine is called to remove any cells that
 *	were not used, ie. the new list is smaller than the old one.
 *
 */
void
list_Configure( curr_size,menu )
int 	curr_size;
Boolean	menu;
{
	list_Draw_Off();
	
	/* make current size the same range as gListSize, which is [1:n] */
	curr_size++;
	
	if ( gListSize > curr_size ) {
		LDelRow( ( gListSize - curr_size ), curr_size , glH );
		gListSize = curr_size ;
	}
	
	/* this next command may be temporary */
	/* let's delete the first list item as it now gets displayed in the banner */
	if (menu) {
		LDelRow(1,0,glH);
		gListSize--;
	}
	
	list_Draw_On();
}

/*     
 *
 * 	list_draw_off
 * 
 *	Turn drawing of the list off.
 *
 */
void
list_Draw_Off()
{
	LDoDraw(false, glH);
}

/*     
 *
 * 	list_draw_on:
 * 
 *	Turn drawing of the list on.
 *
 */
void
list_Draw_On()
{

	LDoDraw(true, glH);

}

/*     
 *
 * 	list_draw
 * 
 *	Draw the list within the list window.
 *
 */
void
list_Draw(window)
WindowPtr	window;
{
	LUpdate( window -> clipRgn, glH ); 
	
}

/*     
 *
 * list_size
 * 
 * Change the list size when the window changes size (grow or zoom routines).
 *
 */
void
list_Size(window)
WindowPtr	window;
{
	Rect		myRect;
	
	myRect = window -> portRect;
	myRect.right -= SCROLL_BAR_PIXELS;
	myRect.bottom -= SCROLL_BAR_PIXELS;
	
	LSize ( myRect.right - myRect.left, myRect.bottom - myRect.top, glH );
}

/*     
 *
 * list_click
 * 
 * Highlight the cell clicked or perform scrolling if user clicked in the scroll bar.
 * Return "true" if the user double clicked on a list item, else return "false".
 *
 */
Boolean
list_click(mouse,modifiers)
Point	mouse;
short	modifiers;
{
	Boolean		doubleClick;
	
	doubleClick = LClick( mouse, modifiers, glH );
	
	return doubleClick;
}

/*     
 *
 * 	list_last_click_buffer
 * 
 *	Returns the entire buffer in the cell of the last clicked cell
 *	Returns true if everything is ok.
 *
 */
Boolean
list_last_click_buffer( buffer )
char	*buffer;

{
	Cell 	cell;
	short	max_size = 255;
	
	/* cell = LLastClick( glH ); */
	cell.h = 0;
	cell.v = 0;
	
	if ( LGetSelect(true, &cell, glH) && cell.v >= 0 ) {
		LGetCell( buffer, &max_size, cell, glH );
		buffer[max_size] = '\0';
		return true;
	}
	else
		return false;
}

/*     
 *
 * 	list_get_buffer
 * 
 *	Like list_last_click_buffer, but it returns the entire buffer
 *  of the specified cell (item).
 *	Returns true if everything is ok.
 *
 */
int
list_get_buffer( buffer, item )
char	*buffer;
short	item;

{
	Cell 	cell;
	short	max_size = 255;
	
	/* if the cell is not in our boundry (using 1:n range), stop function */
	if (item >= list_get_size())
		return false;
		
	cell.h = 0;
	cell.v = item;
	
	LGetCell( buffer, &max_size, cell, glH );
	buffer[max_size] = '\0';
	
	return true;
	
}

/*     
 *
 * 	list_unselect
 * 
 *	deselect all highlighted cells.
 *
 */
void
list_unselect()
{
	Cell 			cell;
	
	/* I can not remember why I wrote this, so I am commenting 
	   it out.  - wade 4/24/92 

	list_Draw_Off();
	
	/*
	/* get last cell clicked 
	cell = LLastClick( glH );
	
	/* deselect active cell 
	if ( cell.v >= 0 ) {
		/* change value in list record for last cell clicked 
		cell.v = -1;
		cell.h = -1;
		(**glH).lastClick = cell;
	}
	
	list_Draw_On();
	*/
	
	/* remove highlighting for all cell */
	cell.v = 0;
	cell.h = 0;
	while( LGetSelect( true, &cell, glH )) 
		LSetSelect(false, cell, glH);
	
}

/*     
 *
 * 	list_vscroll
 * 
 *	set the list verticle scrolling control to the beginning of the list.
 *
 */
void
list_vscroll()
{
	Cell	cell;
	
	list_Draw_Off();
	
	/* this is a kluge, but don't know how else to to it */
	cell.v = 0;
	cell.h = 0;
	LSetSelect( true, cell, glH );
	
	LAutoScroll( glH );
	
	LSetSelect( false, cell, glH );
	list_Draw_On();
}

/*     
 *
 * 	list_get_list_select
 * 
 *	return the first selected list item.
 *
 */
Boolean
list_get_list_select(item)
short	*item;
{
	Cell	cell;
	Boolean	item_selected;
	
	list_Draw_Off();
	
	cell.v = 0;
	cell.h = 0;
	item_selected = LGetSelect(true,&cell,glH );
	
	list_Draw_On();
	
	*item = cell.v;
	return item_selected;
}

/*     
 *
 * 	list_end
 * 
 *	return the last cell allocated in the list.
 *
 */
short
list_end()
{
	return (**glH).dataBounds.bottom;
}

/*     
 *
 * 	list_select
 * 
 *	Highlight the selected cell and make sure it is visible in the window.
 *
 */
void
list_select(vert)
short	vert;
{
	Cell	cell;
	
	cell.v = vert;
	cell.h = 0;
	LSetSelect(true,cell,glH);
	LAutoScroll( glH );
}

/*     
 *
 * 	list_get_vscroll
 * 
 *	Return the handle to the vertical scroll control for hide/show operations.
 *
 */
ControlHandle
list_get_vscroll()
{
	return (**glH).vScroll;
}

/*     
 *
 * 	list_click_loop
 * 
 *	Store or retrieve the click_loop procedure
 *
 */
void
list_click_loop(mode,function)
int   	mode;
ProcPtr *function;
{
	
	switch(mode) {
		case store:
			(**glH).lClikLoop = *function;
			break;
		case retrieve:
			*function = (**glH).lClikLoop;
			break;
	}
}

/*     
 *
 * 	list_clickloop
 * 
 *	do custom clickloop procedure for the reorder function
 *
 */
pascal Boolean
list_clickloop()
{
	Point	curr_mouse;
	Point	theCell;
	Boolean	rc;
	short	anchor;
	Str255	list_string;
	Str255	flag;
	int		flag_int;
	
	/* Find out which cell the mouse down event is currently in */
    GetMouse(&curr_mouse);
	theCell.h = 0;
	rc = list_position(curr_mouse,&theCell.v);
	
	/* continue if we have a mouse click in the portRect */
	if (rc) {
		/* now get the initial cell clicked with a mouse down */
		rc = data_anchor_cell(retrieve,&anchor);
		
		/* determine if we have mouse movement */
		if (rc && anchor != theCell.v) {
		
			wind_set_cursor(128);	/* change to reorder cursor */
			
			/* have we alreay drawn the frame?  check the flag */
			rc = list_get_buffer(list_string,anchor);
			if (rc) rc = get_field(list_string,FLAGS,flag);
			if (rc) if (!(atoi(flag) & FRAME)) {
				/* draw a frame around the anchor cell */
				flag_int = atoi(flag) | FRAME;
				sprintf(flag,"%d",flag_int);
				rc = util_put_field(list_string,strlen(list_string),FLAGS,flag);
				if (rc) list_Replace( list_string, strlen(list_string), anchor);
			}
			
			/* make sure that the cell is highlighted in either the top or bottom portion */
			rc = list_get_buffer(list_string,theCell.v);
			if (rc) rc = get_field(list_string,FLAGS,flag);
			if (rc) if (!((atoi(flag) & SEL_HIGH) || (atoi(flag) & SEL_LOW)))  {
				/* draw a frame around the anchor cell */
				if (theCell.v < anchor) {
					flag_int = atoi(flag) | SEL_HIGH;
					sprintf(flag,"%d",flag_int);
				}
				else {
					flag_int = atoi(flag) | SEL_LOW;
					sprintf(flag,"%d",flag_int);
				}
				rc = util_put_field(list_string,strlen(list_string),FLAGS,flag);
				if (rc) list_Replace( list_string, strlen(list_string), theCell.v); 
			}
		}
	}
	else
		wind_set_cursor(0);		/* turn cursor back to an arrow */

    return(true);
}

/*     
 *
 * 	list_position
 * 
 *	Given a window verticle coordinate, return the cell containing this position.
 *
 */
Boolean
list_position(mouse,position)
Point	mouse;
short	*position;
{
	Rect	rectsize;
	Point	theCell;
	int		list_begin;
	int		list_bottom;
	
	/* let's get out of here if user clicked in the scroll region */
	rectsize.right = gListWindow -> portRect.right - SCROLL_BAR_PIXELS;
	rectsize.bottom = gListWindow -> portRect.bottom - SCROLL_BAR_PIXELS;
	if (mouse.v > rectsize.bottom || mouse.h > rectsize.right) 
		return false;
	
	list_bottom = list_end() - 1;		/* get end of list */
	theCell.h = 0;
	theCell.v = 0;
	LRect(&rectsize, theCell, glH); /* get first cell size */
	for(list_begin=0; list_begin <= list_bottom, mouse.v > rectsize.bottom; ) {
		theCell.v = ++list_begin;
		LRect(&rectsize, theCell, glH); /* get cell size */
	}
	
	if (list_begin > list_bottom)
		return false;
	else {
		*position = list_begin;
		return true;
	}
}

/*     
 *
 * 	list_Replace:
 * 
 *	replace contents of an existing cell.
 *
 */
list_Replace( buffer, length, cell_v)
char   *buffer;
int     length;
short   cell_v;
{

	Point	cell;
	
	cell.h = 0;
	cell.v =  cell_v;
	LSetCell( buffer, length , cell, glH );
}

/*     
 *
 * 	list_get_size:
 * 
 *	return the size of the list using [1:n] range.
 *
 */
short
list_get_size()
{
	return ((**glH).dataBounds.bottom - (**glH).dataBounds.top);
	
}

/*     
 *
 * 	list_remove_hilight:
 * 
 *	remove the hilight bit from every cell in the list.
 *
 */
void
list_remove_hilight()
{
	Boolean			rc;
	Str255			list_string;
	Str255			flag;
	short			items;
	int				flag_int;

	/* get rid of the hilighting criteria */
	for(items=list_get_size(); items > 0; items--) {			
		rc = list_get_buffer(list_string,items);
		if (rc) rc = get_field(list_string,FLAGS,flag);
		if (rc) {
			/* null out the frame and hilite bits */
			flag_int = atoi(flag) & ~FRAME & ~SEL_HIGH & ~SEL_LOW;
			sprintf(flag,"%d",flag_int);
		}
		if (rc) rc = util_put_field(list_string,strlen(list_string),FLAGS,flag);
		if (rc) list_Replace( list_string, strlen(list_string), items);
	}
}

/*     
 *
 * 	list_DeActive:
 * 
 *	do the right thing with the list selection when an
 *  activate or deactivate event occurs.
 */
void
list_DeActive(Boolean mode)
{
	LActivate(mode, glH);
}

/*     
 *
 * 	list_scroll_page_up:
 * 
 *	scroll the list window up one page
 */
void
list_scroll_page_up()
{
     short dest;

	 dest = (**glH).visible.top - ((**glH).visible.bottom -
					    (**glH).visible.top);
	 if (dest < (**glH).dataBounds.top)
	 	dest = (**glH).dataBounds.top;
	 LScroll(0, dest - (**glH).visible.top, glH);
}

/*     
 *
 * 	list_scroll_page_down:
 * 
 *	scroll the list window down one page
 */
void
list_scroll_page_down()
{
	short dest;
	
	dest = (**glH).visible.bottom + ((**glH).visible.bottom -
					    (**glH).visible.top);
	if (dest > (**glH).dataBounds.bottom+1)
		dest = (**glH).dataBounds.bottom+1;
	LScroll(0, dest - (**glH).visible.bottom, glH);
}
     
/*     
 *
 * 	list_scroll_home:
 * 
 *	scroll the list window to the top
 */
void
list_scroll_home()
{
	  LScroll(0, (**glH).dataBounds.top - (**glH).visible.top, glH);
}

/*     
 *
 * 	list_scroll_end:
 * 
 *	scroll the list window to the end
 */
 void
list_scroll_end()
{
	  LScroll(0, (**glH).dataBounds.bottom +1 - (**glH).visible.bottom, glH);
}

/*     
 *
 * 	list_up:
 * 
 *	up arrow, scroll one list item up
 */
 void
list_up()
{
	Cell	cell;
	
	cell.h = 0;
	cell.v = 0;
	  
	if (LGetSelect(true, &cell, glH) && cell.v > 0) {
		  
		  cell.v--;
		  LSetSelect(true, cell, glH);
		  
		  if (cell.v < (**glH).visible.top || cell.v >= (**glH).visible.bottom)
				LScroll(0, cell.v - (**glH).visible.top, glH);
				
		  cell.v++;
		  while (LGetSelect(true, &cell, glH)) {
		  	LSetSelect(false, cell, glH);
		  	cell.v++;
		  }
	}
}

/*     
 *
 * 	list_down:
 * 
 *	down arrow, scroll one list item down
 */
 void
list_down()
{
	Cell	cell;
	short	limit;
	
     cell.h = 0;
	 cell.v = 0;
	  
     while (LGetSelect(true, &cell, glH))
	       cell.v++;
		   
	  if (cell.v < (**glH).dataBounds.bottom) {
	       LSetSelect(true, cell, glH);
	       if (cell.v >= (**glH).visible.bottom || cell.v < (**glH).visible.top)
	            LScroll(0, cell.v - (**glH).visible.bottom + 1, glH);
		    
			limit = cell.v;
		    cell.v = 0;
		    while (LGetSelect(true, &cell, glH) && cell.v < limit) {
		         LSetSelect(false, cell, glH);
			 cell.v++;
		    }
	  }
}