/*
 * $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()
 *
 * Revision 3.3 7/15/92 wade
 * Changed list_draw() - Added DrawControls
 * Changed list_Init() - cell width is based on text font
 * Changed list_Size() - cell width is based on text fontÄ
 * Added list_new_size()
 *
 * Revision 4.2a3 6/8/94 - edward
 * Included StandardFile
 *
 * Revision 4.2a3 6/17/94 - edward
 * Include stdlib.h
 *
 * Revision 4.2a4 7/5/94 - edward
 * Porting to MW C/C++ 68K.  The headers are only included if we're not using MW
 */

/*  
 *	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
 */

// 7/5/94 edward - If we're compiling with MW all of these headers are precompiled in TIFast*
#ifndef __MWERKS__

#include <Types.h>
#include <Memory.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 <SegLoad.h>
#include <Files.h>
#include <OSUtils.h>
#include <Traps.h>
#include <Lists.h>
#include <string.h>
#include <stdlib.h>
#include "pips.h"
#include <resources.h>
#include <StandardFile.h>
#ifdef TE32K
#include "TE32K.h"
#endif
#include "documentRec.h"
#include "prototype.h"
#include <stdio.h>

#endif

#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(WindowPtr window)
{
	Rect		dataBounds, myRect;
	Point		cSize;
	FontInfo	fInfo;
	
	/* 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; this is the default size */
	GetFontInfo(&fInfo);
	cSize.v = fInfo.ascent + fInfo.descent + fInfo.leading;
	cSize.h = MAX_CELL_SIZE; 
	
	myRect = window -> portRect;
	myRect.top = myRect.top + cSize.v;
	myRect.right -= SCROLL_BAR_PIXELS;
	myRect.bottom -= SCROLL_BAR_PIXELS;
	
	glH = LNew( &myRect, &dataBounds, cSize, 400, window, true, true, false, true);
	
	/* {set if only one selection at a time} */
	(**glH).selFlags = (**glH).selFlags | lOnlyOne;
	
	gListSize = 0;
}

/*     
 *
 * 	list_bind:
 * 
 *	Place value in buffer parameter into cell pointed to by cell_ptr.
 *
 */
#define LIST_DATA_SIZE 32000
Boolean list_Bind(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 */
	/* watch out for cellArray[curr_ptr] being a negative value, which will*/
	/* happen if it overflows the 32k size. */
	if (( curr_ptr > gListSize ) || 
		( ((**glH).cellArray[curr_ptr] + length) > LIST_DATA_SIZE )) {
		/* ((( 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(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(void)
{
	LDoDraw(false, glH);
}

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

	LDoDraw(true, glH);

}

/*     
 *
 * 	list_draw
 * 
 *	Draw the list within the list window.
 *
 */
void list_Draw(WindowPtr window)
{	
	Rect		myRect;
	
	UpdtControl(window, window -> clipRgn);
	 
    PenNormal();
    MoveTo(0, (**glH).cellSize.v - 1);
	myRect = window -> portRect;
    LineTo(myRect.right - myRect.left - SCROLL_BAR_PIXELS, (**glH).cellSize.v - 1);

	LUpdate( window -> clipRgn, glH ); 
	
}

/*     
 *
 * list_size
 * 
 * Change the list size when the window changes size (grow or zoom routines).
 *
 */
void list_Size(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).cellSize.v, 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(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(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(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(void)
{
	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(void)
{
	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(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(void)
{
	return (**glH).dataBounds.bottom;
}

/*     
 *
 * 	list_select
 * 
 *	Highlight the selected cell and make sure it is visible in the window.
 *
 */
void list_select(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(void)
{
	return (**glH).vScroll;
}

/*     
 *
 * 	list_click_loop
 * 
 *	Store or retrieve the click_loop procedure
 *
 */
 /* TechInfo 4.2a3 - update to use the Universal Headers.  Now takes a UPP instead of */
 /* a ProcPtr.  The field lClikLoop also changed to lClickLoop */
void list_click_loop(int mode, ListClickLoopUPP *function)
{
	
	switch(mode)
	{
		case store:
			(**glH).lClickLoop = *function;
			break;
		case retrieve:
			*function = (**glH).lClickLoop;
			break;
	}
}

/*     
 *
 * 	list_clickloop
 * 
 *	do custom clickloop procedure for the reorder function
 *
 */
pascal Boolean list_clickloop(void)
{
	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(Point mouse, short *position)
{
	Rect	rectsize;
	Point	theCell;
	short	list_begin;
	short	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.
 *
 */
void list_Replace(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(void)
{
	return ((**glH).dataBounds.bottom - (**glH).dataBounds.top);
	
}

/*     
 *
 * 	list_remove_hilight:
 * 
 *	remove the hilight bit from every cell in the list.
 *
 */
void list_remove_hilight(void)
{
	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(void)
{
     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(void)
{
	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(void)
{
	  LScroll(0, (**glH).dataBounds.top - (**glH).visible.top, glH);
}

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

/*     
 *
 * 	list_up:
 * 
 *	up arrow, scroll one list item up
 */
void list_up(void)
{
	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(void)
{
	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++;
		    }
	  }
}

/*     
 *
 * 	list_new_size:
 * 
 *	The window font size changed, change info in the listRecord.
 */
void list_new_size(WindowPtr window)
{
	FontInfo	  fInfo;
	Point		  cSize;
	Rect		  myRect;
	ControlHandle control;
	int			  refCon;
	
	GetFontInfo(&fInfo);
	cSize.v = fInfo.ascent + fInfo.descent + fInfo.leading;
	cSize.h = MAX_CELL_SIZE; 
	
	/* set the cell size field - but don't draw the contents yet */
	list_Draw_Off();
	LCellSize(cSize, glH);
	list_Draw_On();
	
	/* these values were originally set in the ldef init section */
	/* we need to revise them */
	(*glH)->indent.h = fInfo.widMax / 3;
	(*glH)->indent.v = fInfo.ascent;
	
	/* we need to change the rView to accomondate the new header size */
	myRect = window -> portRect;
	(**glH).rView.top = myRect.top + cSize.v;
		
	/*  we need to change the size of the controls in the heading */
	control = ((WindowPeek) window)->controlList;
	while(control) {
		refCon = GetCRefCon (control);
		switch(refCon) {
			case TITLE_CTRL:
			case DATE_CTRL:
			case SOURCE_CTRL:
				if (refCon == DATE_CTRL || refCon == SOURCE_CTRL)
					MoveControl(control,COLUMN2_START,0);
				else
					if (refCon == TITLE_CTRL)
						MoveControl(control,COLUMN1_START,0);
				SizeControl(control,StringWidth((**control).contrlTitle)+2,fInfo.ascent + fInfo.descent);
				break;
			defalut:
				/* we don't need to worry about controls not in the heading */
				break;
		}
		control = (**control).nextControl;
	}
	
	/* we need to change the scroll bars */
	list_Draw_Off();
	list_Size(window);
	list_Draw_On();
}