/* *****                                                            *****
** *****           Program created by Ken Farinsky, 1986.           *****
** *****                                                            *****
**
** All material contained herein may be used in any way desired.
**                    >>>>> i.e.  PUBLIC DOMAIN. <<<<<
** Not limited to any single bulletin board - can be freely transferred.
**
** Original distribution through:
**     Slipped Disk, Inc.
**     Madison Heights,  MI  48071
**     (313) 583-9803
**
** Distributed in the hopes of increasing the level of understanding of the
** Amiga personal computer.  Even the best computer can only succeed with
** the proper software support.
*/
#include <exec/types.h>
#include <intuition/intuition.h>
#include <functions.h>
#include <stdio.h>
#include <exec/memory.h>
#include "view.h"

extern struct Library	   *DiskfontBase ;

extern struct Gadget		my_gad ;
extern struct Menu			menu_header ;
extern SHORT				nums_on ;
extern SHORT 				tab_width ;
extern struct Menu			font_menu ;

struct TextFont		   *textfont ;
struct TextAttr			textattr ;

struct	big_MenuItem
	{
	struct MenuItem		menu ;
	struct TextAttr	   *text ;
	} ;

/*----------------------------------------------------------------------
** process_window() - I'd rather not talk about a 400 line function as
** I consider it bad programming practice.  Things just got a bit out of
** hand and I was too lazy to change it.
**
** this routine is the main loop which processes the intuition messages.
** return from this routine indicate that the user is done with the file.
** if the program returns a file name, then process_window() is again
** called with the new file as data.
*/
UBYTE				*process_window( The_Window, data, memory_key)
struct Window	    *The_Window ;
UBYTE			   **data ;
struct Remember		**memory_key ;
{
struct IntuiMessage	   *The_Msg ;
struct Gadget		   *gad_addr ;
struct big_MenuItem	   *tmp_item ;

ULONG		sig ;
ULONG		class ;
USHORT		code ;
UBYTE	  **top ;
SHORT		tot_recs ;
SHORT		win_size ;
SHORT		cur_offset ;
SHORT		cur_indent ;
void		error() ;
void		font_load() ;
void 		install_data() ;
void 		update_prop() ;
void 		process_gadgetup() ;
void 		process_gadgetdown() ;
void		draw_box() ;
void		expand_text() ;
SHORT		count_records() ;
UBYTE	   *file_request() ;
UBYTE	   *new_file ;
long		font_height ;
long		font_baseline ;
short		x ;
short		y ;
short		firstY ;
short		distance ;
short		oldx ;
short		oldy ;
long		mousemove ;
long		box_drawn ;
short		shifted ;
short		ctrl ;
UBYTE		line_buffer[201] ;

/* set up the defaults. */
font_height = textfont->tf_YSize ;
font_baseline = textfont->tf_Baseline ;
top = &data[0] ;
cur_offset = 0 ;
cur_indent = 0 ;
win_size = (The_Window->GZZHeight)/font_height ;
tot_recs = count_records( data) ;
/* for dragging lines around on the screen. */
mousemove = FALSE ;
box_drawn = FALSE ;
/* these are for processing the arrow keys.  I should use the values in 
** qual, but I wrote this before I knew how.
*/
shifted = 0 ;
ctrl = 0 ;

SetDrMd( The_Window->RPort, JAM2) ;
SetAPen( The_Window->RPort, BLKP) ;
SetBPen( The_Window->RPort, WHTP) ;

/* set up the side prop gadget.  the bottom prop gadget never changes,
** it is only changed by the user.  the side prop must be updated for
** each change of the screen (font, line number, etc.).
*/
update_prop( The_Window, &my_gad, tot_recs, win_size, cur_offset) ;
install_data( The_Window, top, win_size, cur_offset, cur_indent) ;

/* goes forever...there are some explicit returns in this loop
** which end the processing of the window.
*/
for (;;)
	{
	sig = Wait( 1L << The_Window->UserPort->mp_SigBit) ;

	while ( NULL != (
			The_Msg = (struct IntuiMessage *)GetMsg( The_Window->UserPort)))
		{
		class = The_Msg->Class ;
		code  = The_Msg->Code ;
		gad_addr = (struct Gadget *)(The_Msg->IAddress) ;
		x = The_Msg->MouseX ;
		y = The_Msg->MouseY ;
		ReplyMsg( The_Msg) ;

		/* y is used to position a drag box.  it has to be kept within
		** the limits of the data on the screen.  this processing does
		** that.
		*/
		y -= The_Window->BorderTop ;
		y -= font_height >> 1 ;

		if ( y < 0)
			y = 0 ;
		if ( y > (The_Window->GZZHeight - font_height - 1))
			y = The_Window->GZZHeight - font_height - 1 ;

		/* now process the message */
		switch ( class)
			{
			case RAWKEY:
				/* process the arrow keys the hard way..use the qualifier
				** next time to tell if shifted or ctrl key...
				*/
				if ( code == 0x63)
					ctrl = 1 ;
				else if (code == 0xe3)
					ctrl = 0 ;
				else if ( (code == 0xe0) || (code == 0xe1) || (code == 0xe2))
					shifted = !shifted ;
				else if ( (code == 0x60) || (code == 0x61) || (code == 0x62))
					shifted = !shifted ;
				else if (code == 0x4d) /* down arrow on keyboard */
					{
					if (ctrl)
						{ /* ctrl-down: go to the bottom of the file */
						cur_offset = tot_recs - win_size ;
						if ( cur_offset < 0)
							cur_offset = 0 ;
						top = &data[cur_offset] ;
						update_prop(The_Window,&my_gad,
									tot_recs,win_size,cur_offset) ;
						install_data( The_Window, top, win_size,
										cur_offset, cur_indent) ;
						}
					else if (shifted)
						{ /* shift-down: page down */
						cur_offset += win_size ;
						if ( ( cur_offset + win_size) > tot_recs)
							cur_offset = tot_recs - win_size ;
						if ( cur_offset < 0)
							cur_offset = 0 ;
						top = &data[cur_offset] ;
						update_prop(The_Window,&my_gad,
									tot_recs,win_size,cur_offset) ;
						install_data( The_Window, top, win_size, 
										cur_offset, cur_indent) ;
						}
					else if ( ( cur_offset + win_size) < tot_recs)
						{ /* down: line down and line to display */
						update_prop( The_Window, &my_gad, tot_recs,
									win_size, ++cur_offset) ;
						ScrollRaster(The_Window->RPort,0L,font_height,
							0L,0L,(long)The_Window->GZZWidth,
							(long)The_Window->GZZHeight) ;
						SetAPen(The_Window->RPort,WHTP) ;
						RectFill(The_Window->RPort,0L,
								(long)(font_height*(win_size-1)),
								(long)The_Window->GZZWidth,
								(long)The_Window->GZZHeight);
						SetAPen(The_Window->RPort,BLKP) ;

						Move(The_Window->RPort, 0L,
							(long)(font_height*(win_size-1)+font_baseline)) ;
						expand_text( line_buffer, *(top+win_size+1),
									200, cur_offset+win_size);
						Text(The_Window->RPort,line_buffer,
								(long)strlen(line_buffer)) ;

						/* more to display (partial line?) */
						if ( ( cur_offset + win_size) < tot_recs)
							{
							Move(The_Window->RPort, 0L,
								(long)(font_height*(win_size)+font_baseline)) ;
							expand_text(line_buffer,*(top+win_size+2),200,
										cur_offset+win_size+1);
							Text(The_Window->RPort,line_buffer,
									(long)strlen(line_buffer)) ;
							}
						top++ ;
						}
					}
				else if (code == 0x4c) /* up arrow on keyboard */
					{
					if (ctrl)
						{ /* ctrl-up: go to the top of the file */
						cur_offset = 0 ;
						top = &data[cur_offset] ;
						update_prop(The_Window,&my_gad,
									tot_recs,win_size,cur_offset) ;
						install_data( The_Window, top, win_size, 
										cur_offset, cur_indent) ;
						}
					else if (shifted)
						{ /* shift-up: page up */
						cur_offset -= win_size ;
						if ( cur_offset < 0)
							cur_offset = 0 ;
						top = &data[cur_offset] ;
						update_prop(The_Window,&my_gad,
									tot_recs,win_size,cur_offset) ;
						install_data( The_Window, top, win_size, 
										cur_offset, cur_indent) ;
						}
					else if ( cur_offset > 0)
						{ /* up: line up */
						update_prop( The_Window, &my_gad,
									tot_recs, win_size, --cur_offset) ;
						ScrollRaster(The_Window->RPort,0L,-font_height,
							0L,0L,(long)The_Window->GZZWidth,
							(long)The_Window->GZZHeight) ;
						SetAPen(The_Window->RPort,WHTP) ;
						RectFill(The_Window->RPort, 0L,0L,
								(long)The_Window->GZZWidth,font_height-1L) ;
						SetAPen(The_Window->RPort,BLKP) ;
						Move(The_Window->RPort, 0L, font_baseline) ;
						expand_text( line_buffer, *top, 200, cur_offset+1) ;
						Text(The_Window->RPort, line_buffer,
								(long)strlen(line_buffer)) ;
						top-- ;
						}
					}
				break ;
			case MOUSEMOVE:
				/* mouse only moves when the user is dragging on the
				** file display.  note that it happened and process the
				** move when out of the while loop (no more messages).
				*/
				mousemove = TRUE ;
				break ;
			case MOUSEBUTTONS:
				/* if the user lets go of the mouse button while the box
				** is drawn, then the mouse has been dragged on the screen.
				** erase the box, and compute the distance moved.
				** the file then may be redisplayed after a check for the
				** top and bottom past the limits of the screen.
				*/
				if ((code == SELECTUP) && (box_drawn == TRUE))
					{
					draw_box( oldx, oldy, The_Window, font_height) ;
					box_drawn = FALSE ;
					mousemove = FALSE ;
					distance = firstY - y ;
					if (distance < 0)
						distance = (distance - (font_height>>1))/font_height ;
					else
						distance = (distance + (font_height>>1))/font_height ;
					cur_offset += distance ;
					if ( ( cur_offset + win_size) > tot_recs)
						cur_offset = tot_recs - win_size ;
					if ( cur_offset < 0)
						cur_offset = 0 ;
					top = &data[cur_offset] ;
					update_prop(The_Window,&my_gad,
								tot_recs,win_size,cur_offset) ;
					install_data( The_Window, top, win_size, 
									cur_offset, cur_indent) ;
					}
				break ;
			case CLOSEWINDOW:
				/* return a null file...no more to do */
				return( (UBYTE *)"") ;
				break ;
			case GADGETUP:
				if (gad_addr->GadgetID == 70)
					{
					/* de-select on the display of the file.  get the info
					** required to reposition the screen (the user has just
					** dragged on the screen and released the button over
					** the display.  see SELECTUP, where the user releases
					** the button elsewhere).
					*/
					draw_box( oldx, oldy, The_Window, font_height) ;
					box_drawn = FALSE ;
					mousemove = FALSE ;
					distance = firstY - y ;
					if (distance < 0)
						distance = (distance - (font_height>>1))/font_height ;
					else
						distance = (distance + (font_height>>1))/font_height ;
					cur_offset += distance ;
					if ( ( cur_offset + win_size) > tot_recs)
						cur_offset = tot_recs - win_size ;
					if ( cur_offset < 0)
						cur_offset = 0 ;
					top = &data[cur_offset] ;
					update_prop(The_Window,&my_gad,
								tot_recs,win_size,cur_offset) ;
					install_data( The_Window, top, win_size, 
									cur_offset, cur_indent) ;
					}
				else
					process_gadgetup(The_Window, gad_addr, &top, tot_recs,
									win_size, &cur_offset, data, &cur_indent);
				break ;
			case GADGETDOWN:
				if (gad_addr->GadgetID == 70)
					{
					/* if click on the display of the file, note where
					** and draw a box which will display until the user
					** releases it.
					*/
					box_drawn = TRUE ;
					draw_box( x, y, The_Window, font_height) ;
					oldx = x ;
					oldy = y ;
					firstY = y ;
					}
				else
					process_gadgetdown(The_Window, gad_addr, &top, tot_recs,
									win_size, &cur_offset) ;
				break ;
			case NEWSIZE:
				/* calc info related to size and update the display
				** and the side prop gadget.
				*/
				win_size = (The_Window->GZZHeight)/font_height ;
				if ( ( cur_offset + win_size) > tot_recs)
					cur_offset = tot_recs - win_size ;
				if ( cur_offset < 0)
					cur_offset = 0 ;
				top = &data[cur_offset] ;
				update_prop(The_Window,&my_gad,tot_recs,win_size,cur_offset) ;
				install_data( The_Window, top, win_size, 
								cur_offset, cur_indent) ;
				break ;
			case MENUPICK:
				if ( MENUNUM(code) == 0)
					{
					if ( ITEMNUM(code) == 0)
						{
						/* open file.  return the name of the selected
						** file to the calling routine.
						*/
						new_file = file_request( The_Window) ;
						if ( *new_file != '\0')
							return( new_file) ;
						}
					else if ( ITEMNUM(code) == 1)
						/* quit, return no file name */
						return( (UBYTE *)"") ;
					else if ( ITEMNUM(code) == 2)
						/* credits - guess who */
						error(" Original program created by Ken Farinsky.",
							  " first distributed through:",
							  "    'Slipped Disk' - Madison Heights, MI") ;
					}
				else if ( MENUNUM(code) == 1)
					{
					if ( ITEMNUM(code) == 0)
						/* numbers on */
						nums_on = TRUE ;
					else if ( ITEMNUM(code) == 1)
						/* numbers off */
						nums_on = FALSE ;
					else if ( ITEMNUM(code) == 2)
						{
						/* tab width, 4 or 8 only */
						if ( SUBNUM(code) == 0)
							tab_width = 4 ;
						else if ( SUBNUM(code) == 1)
							tab_width = 8 ;
						}
					}
				else if ( MENUNUM(code) == 2)
					{
					if ( DiskfontBase == NULL)
						{
						/* if diskfontbase is null, then the fonts are not
						** loaded yet... load the font and attatch
						** beginning at font_menu.
						** hook into window menu...
						*/
						if (NULL != (DiskfontBase =
										OpenLibrary("diskfont.library", 0)))
							font_load(The_Window, &font_menu, memory_key);
						}
					else
						{
						/* fonts loaded and choose new font.  get the
						** TextAttr (see big_MenuItem), and open the new
						** font.
						*/
						if ( NULL != textfont)
							CloseFont( textfont) ;
						/* Diskfont library loaded, open disk font */
						tmp_item = (struct big_MenuItem *)
									ItemAddress( &menu_header, (long)code) ;
						if (NULL != (textfont = OpenDiskFont(tmp_item->text)))
							SetFont( The_Window->RPort, textfont) ;
						}
					if ( NULL == textfont)
						{
						/* if no font, default to topaz 9 */
						textattr.ta_Name = (STRPTR)"topaz.font" ;
						textattr.ta_YSize = 9 ;
						if (NULL != ( textfont = OpenFont( &textattr)))
							SetFont( The_Window->RPort, textfont) ;
						}
					/* get the font info, update the side prop to
					** reflect the new font, and redisplay the screen.
					*/
					font_height = textfont->tf_YSize ;
					font_baseline = textfont->tf_Baseline ;
					win_size = (The_Window->GZZHeight)/font_height ;
					if ( ( cur_offset + win_size) > tot_recs)
						cur_offset = tot_recs - win_size ;
					if ( cur_offset < 0)
						cur_offset = 0 ;
					top = &data[cur_offset] ;
					update_prop(The_Window,&my_gad,tot_recs,
								win_size,cur_offset) ;
					}
				install_data( The_Window, top, win_size, 
								cur_offset, cur_indent) ;
				break ;
			default:
				break ;
			}
		}
	if (mousemove == TRUE)
		{
		/* if the mouse moved, move the box. */
		mousemove = FALSE ;

		draw_box( oldx, oldy, The_Window, font_height) ;
		draw_box( x, y, The_Window, font_height) ;
		box_drawn = TRUE ;
		oldx = x ;
		oldy = y ;
		}
	}
}

/*----------------------------------------------------------------------
** draw_box() - draws a complemented box around the line picked with the
** mouse at the mouse position.  used for dragging lines up and down on
** the screen to reposition the file.
*/
void draw_box( x, y, win, box_height)
short			x ;
short			y ;
struct Window  *win ;
long			box_height ;
{
SetDrMd( win->RPort, COMPLEMENT) ;

Move(win->RPort, (long)0, (long)y) ;
Draw(win->RPort, (long)(win->GZZWidth-3), (long)y) ;
Draw(win->RPort, (long)(win->GZZWidth-3), (long)(y+box_height)) ;
Draw(win->RPort, (long)0, (long)(y+box_height)) ;
Draw(win->RPort, (long)0, (long)y) ;

SetDrMd( win->RPort, JAM2) ;
}
