*****************************************************************************************************
; WP Line Manipulation Routines
; 
;    This file contains the routines used by WP to manipulate the line handles data structure for
; paragraphs. The paragraph and line handle data strucutures are described below:
;
; The paragraph data structure is an array from 1..W_LastP of paragraph info blocks:
;
;	W_pBlockHand	gequ 0  	: handle to the block that the par is in
;	W_pOffset		gequ 4  	: offset into this block that starts the par
;	W_pAttr	  	gequ 6  	: attribute word for the par
;	W_pRulerHand	gequ 8  	: handle to the ruler that governs this par
;	W_pPixels		gequ 12 	: number of pixels tall this par is
;	W_pLineHand	  	gequ 14 	: handle to the line struct for this par (Purgeable)
;	W_pLastLine	  	gequ 18 	: number of last line in the par+1, cr is 0
;	W_pBytes	 	gequ 20 	: equate for size of paragraph block.
;
; The linehandle will always have a header as follows
;
;	W_MaxLine		gequ 0
;	W_LineHeader	gequ 4
;
; The LineHandle in the paragraph record is an array from 0..W_lastline-1. Each record looks like:
;
;	W_lOffset		gequ 0
;	W_lFont	 	gequ 2
;	W_lSize	  	gequ 4
;	W_lStyle	 	gequ 5
;	W_lColor	 	gequ 6
;	W_lAscent		gequ 8
;	W_lDescent	   	gequ 10
;	W_lHeight		gequ 12
;	W_lLeading	   	gequ 14
;	lREcSize	   	gequ 16
;
*****************************************************************************************************

	load 	'macros.dump'
	include 	'driver.equ'
	include 	'wp.equ'

	IMPORT	D_BeachBall
	IMPORT	D_Deref
	IMPORT	D_GetStats
	IMPORT	D_GetWdth
	IMPORT	D_GrowLHandle
	IMPORT	D_MainZPage
	IMPORT	D_NeedHandle
	IMPORT	D_MustHandle
	IMPORT	D_MustGrowL
	IMPORT	D_NoAlert
	IMPORT	D_SelectFont
	IMPORT	D_UnLock

	IMPORT	W_DateStr
	IMPORT	W_DeepShit
	IMPORT	W_EndColor
	IMPORT	W_EndFont
	IMPORT	W_EndPtr
	IMPORT	W_EndStyle
	IMPORT	W_GetAddr
	IMPORT	W_GetLeft
	IMPORT	W_GetParRec
	IMPORT	W_Hand
	IMPORT	W_Indent
	IMPORT	W_JustBits
	IMPORT	W_JustExtra
	IMPORT	W_JustPtr
	IMPORT	W_JustSpaces
	IMPORT	W_LastP
	IMPORT	W_LeftMargin
	IMPORT	W_MaxAscent
	IMPORT	W_MaxDescent
	IMPORT	W_MaxLeading
	IMPORT	W_NumTabs
	IMPORT	W_PageStr
	IMPORT	W_ReadRuler
	IMPORT	W_RightMargin
	IMPORT	W_RulBits
	IMPORT	W_STColor
	IMPORT	W_STFont
	IMPORT	W_STLIne
	IMPORT	W_STPtr
	IMPORT	W_STStyle
	IMPORT	W_Tabs
	IMPORT	W_TimeStr

	ENTRY	W_CalcLineHeight
	ENTRY	W_CutLine
	ENTRY	W_GetLineRec
	ENTRY	W_UseTab


*****************************************************************************************************
; W_MakeLines ( Paragraph:word )
;
;   This routine is called to generate all the textual information for the given paragraph. This
; routine will return an error if the line handle could not be allocated ( either because there is
; is not enough memory or because more than 4k lines exit in the paragraph ).

W_MakeLines	PROC	EXPORT
	;Using 	wpglobals

	input 	par:w
	local 	ptr:l,LineHand:l,LinePtr:l,MaxLine:w
	local 	ParRec:l,LineRec:l,LineBeg:l,pixels:w
	error	err
	begin

; Get pointer to paragraph record and check if page break paragraph. If so no lines to make so just
; exit.

	stz	err
	rcall	W_GetParRec,in=(par:a),out=(ParRec:ax)
	cmpw 	[ParRec]:#W_pAttr,#W_PgBrk
	jeq 	exit

; Get ruler information for the paragraph.

	movelong	[ParRec]:#W_pRulerHand,ax
	jsl	W_ReadRuler

; Get line handle for paragraph. If no line handle exists then set paragraph line count to be
; initially 1.

	movelong 	[ParRec]:#W_pLineHand,LineHand
	cpzl 	LineHand	
	bne 	chknil
	moveword 	#1,[ParRec]:#W_pLastLine
	bra 	getlinehand

; If line handle has been purged then dispose of old handle and fall through to create new one.

chknil	cpzl 	[LineHand]	  
	jne 	NotPurge
	tool	_DisposeHandle,in=([ParRec]:#W_pLineHand:l)

; Ask for line handle large enough to hold all the lines in the paragraph. If we can't get it
; we're in trouble so use must handle. Note: Will fail if we have more than 4k lines in the
; paragraph. Line handle will be allocated as most purgeable status.

getlinehand	cmpw	[ParRec]:#W_pLastLine,#$0FFF
	bge	abort
	moveword	#1,>D_NoAlert
	moveword	[ParRec]:#W_pLastLine,a
	inc	a
	asl	a
	asl	a
	asl	a
	asl	a
	clc
	adc	#W_LineHeader
	ldx	#0
	call	D_MustHandle,in=(ax:l,#$300:w),out=(LineHand:l),err=err
	moveword	#0,>D_NoAlert
	bcc	gotline

; Really bad error has occurred. Just return with error -1 (handled) and hope we recover.

abort	moveword	#-1,err
	sta	W_DeepShit
	tool	_SysBeep
	movelong	#0,[ParRec]:#W_pLineHand
	IF (Developing) THEN
	setborder #3
	ENDIF
	brl	unlocktext

; Successfully got the line handle (whew!) so put it in the paragraph. Put line count into start
; of line handle.

gotline	movelong	LineHand,[ParRec]:#W_pLineHand
	movelong 	[LineHand],LinePtr
	moveword 	[ParRec]:#W_pLastLine,[LinePtr]

; Line handle exists. Loop through all lines getting line information as we go along. Lock down
; the line handle and the paragraph text block. Get the max line count. Get pointer to start of
; text block where paragraph font information is.

NotPurge	rcall	D_Deref,in=(LineHand:ax),out=(LinePtr:ax)
	rcall	D_Deref,in=([ParRec]:ax)
	moveword 	[LinePtr],MaxLine  
	rcall	W_GetAddr,in=(par:a,#0:x),out=(ptr:ax)

; Set up CutLine global variables.

	addlong 	#W_TextHeader,ptr,W_STPtr
	stz 	W_STLIne		
	moveword 	[ptr],W_STFont
	moveword 	[ptr]:#2,W_STStyle
	moveword 	[ptr]:#4,a
	and 	#$ff
	sta 	W_STColor
	stz 	pixels

; Call cutline to determine the end values for the line we specified above. The global variables for
; the end font/style/color/ptr and max ascent/descent/leading will be set. Get pointer to the lines
; record.

LineLoop	jsl 	W_CutLine
;	jsl	D_BeachBall
	rcall	W_GetLineRec,in=(LinePtr:ax,W_STLine:y),out=(LineRec:ax)

; Fill in line record information for current line.

	subword 	W_STPtr,ptr,[LineRec]:#W_lOffset  
	moveword 	W_STFont,[LineRec]:#W_lFont
	moveword 	W_STStyle,[LineRec]:#W_lStyle
	moveword 	W_STColor,[LineRec]:#W_lColor
	moveword 	W_MaxAscent,[LineRec]:#W_lAscent
	moveword 	W_MaxDescent,[LineRec]:#W_lDescent
	clc
	adc 	W_MaxAscent
	moveword 	a,[LineRec]:#W_lHeight
	moveword 	W_MaxLeading,[LineRec]:#W_lLeading

; Adjust current pixel position by the height of the line (takes into account line spacing).

	movelong 	LineRec,ax
	jsl 	W_CalcLineHeight
	addword 	a,pixels,pixels

; Setup values for start of next line to be same as end of last line.

	moveword 	W_EndFont,W_STFont
	moveword 	W_EndStyle,W_STStyle
	moveword 	W_EndColor,W_STColor
	movelong	W_EndPtr,LineBeg
	movelong	W_EndPtr,W_STPtr

; If carriage return then we are done getting lines for the paragraph so branch.

	lda 	[LineBeg]
	and 	#$ff
	cmp 	#cr
	beq 	DoneLoop

; Bump the line count. If we have not exceed the line count for the paragraph go back and process the
; next line.

	inc 	W_STLIne
	cmpw 	W_STLIne,MaxLine
	jcc 	LineLoop

; Paragraph has grown. We need to enlarge the line handle to hold new line record information. Try to
; grow line handle by enough room for 10 more lines (160 bytes). If we can't get it we are in real
; trouble. If we will exceed the 4k lines limit then abort.
	
	addword 	#10,MaxLine,MaxLine
	cmp	#$0FFF
	bge	goabort
	inc	a
	asl	a
	asl	a
	asl	a
	asl	a
	clc
	adc	#W_LineHeader
	ldx	#0

; Try to grow the line handle. If we succeed then save new line count in line handle and go back for
; next line. Else abort.

	call	D_MustGrowL,in=(ax:l,LineHand:l),out=(LinePtr:l),err=err
	bcs	goabort
	moveword 	MaxLine,[LinePtr]
	brl 	LineLoop
goabort	brl	abort

; Have successfully grown and filled line handle for the paragraph except for last line. Set offset
; for start of last line. Update the line count and pixel size for the paragraph.

DoneLoop	movelong 	LinePtr,ax	  	
	ldy 	W_STLIne
	iny
	jsl 	W_GetLineRec
	movelong 	ax,LineRec

	subword 	LineBeg,ptr,[LineRec]

	lda 	W_STLIne
	inc 	a
	moveword 	a,[ParRec]:#W_pLastLine
	moveword 	pixels,[ParRec]:#W_pPixels

; Unlock the line and paragraph handles.

	rcall	D_Unlock,in=(LineHand:ax)
unlocktext	rcall	D_Unlock,in=([ParRec]:ax)

exit	return

	ENDP

*****************************************************************************************************
; W_ValidLines ( Paragraph:word )
;
;   This routine is called to generate all the textual information for the given paragraph. If the
; paragraph is not a page break and its line handle has been purged then it will call MakeLines to
; do the actual work of rebuilding the line information for this paragraph.

W_ValidLines PROC	EXPORT
	;Using 	wpglobals

	input 	par:w
	local 	LineHand:l,parptr:l
	error	err
	begin

; Get pointer to paragraph and see if its a page break. If so then its valid so exit.

	stz	err
	rcall	W_GetParRec,in=(par:a),out=(parptr:ax)
	cmpw 	[parptr]:#W_pAttr,#W_PgBrk
	beq	exit

; Get line handle for paragraph. If no line handle exists or the handle has been purged then make
; new line information.

	movelong 	[parptr]:#W_pLineHand,LineHand
	cpzl 	LineHand		
	beq 	notvalid
	cpzl 	[LineHand]	  	
	bne 	exit

; Line information is no longer valid call make lines to update it.

notvalid	call	W_MakeLines,in=(par:w),err=err
exit	return

	ENDP

*****************************************************************************************************
; W_CutLine ( )
;
;   This routine is called to cut the line described in the following global variables:
;
;	W_STFont	  word	; starting font
;	W_STStyle	  byte	; starting style
;	W_STSize	  byte	; starting font size
;	W_STColor	  word	; starting text color
;	W_STPtr	  long	; pointer to start of line text
;	W_STLine	  word	; starting line
;
;   The following globals will have been set on return from this routine:
;
;	W_EndFont	  word	; ending font
;	W_EndStyle	  byte	; ending style
;	W_EndSize	  byte	; ending font size
;	W_EndColor	  word	; ending text color
;	W_EndPtr	  long	; pointer to end of line text
;	
;	W_MaxAscent	  word	; max ascent found in line
;	W_MaxDescent  word	; max descent found in line
;	W_MaxLeading  word	; max leading found in line
;
;	W_JustPtr	  long	; pointer used for justification
;	W_JustBits	  word	; 
;	W_JustSpaces  word	; number of spaces to use for justify
;	W_JustExtra	  long	; extra pixels to justify

W_CutLine	PROC	EXPORT
	;Using 	wpglobals
	
	local 	CurH:w,ChrPtr:l,LastH:w,lflag:w,lflag2:w,WasLastSp:w
	local 	tFont:w,tstyle:w,tcolor:w,StringPtr:l
	local 	tAscent:w,tDescent:w,tLeading:w
	begin

; Setup default values for tFont/tStyle/tColor & EndFont/EndStyle/EndColor. Then select the font.

	stz 	WasLastSp
	lda 	W_STFont
	sta 	tFont
	sta 	W_EndFont
	ldx 	W_STStyle
	stx 	tstyle
	stx 	W_EndStyle
	ldy 	W_STColor
	sty 	tcolor
	sty 	W_EndColor
	jsl 	D_SelectFont

; Get stats for current font.

	jsl 	D_GetStats
	sta 	W_MaxAscent
	stx 	W_MaxDescent
	sty 	W_MaxLeading

; Clear temp values for ascent, descent, and leading.

	stz 	tAscent
	stz 	tdescent
	stz 	tleading

; Set flags so first character will determine height.

	moveword 	#1,lflag	
	stz 	W_JustSpaces
	stz 	lflag2

; Setup initial values of various pointers to be same as start of text.

	movelong	W_StPtr,ax
	movelong	ax,ChrPtr
	movelong	ax,W_JustPtr
	movelong	ax,W_EndPtr

; Get current horizontal position for the line and set last position to it as well.

	lda 	W_STLIne
	jsl 	W_GetLeft
	sta 	CurH
	sta 	LastH

; All flags and variables now initialized. Loop through characters in line adjusting pointers and
; global variables as we cut the line. If character is not one of the supported characters then
; branch else if special character jump to handler.

ChrLoop	lda 	[ChrPtr]	 
	and 	#$ff
	cmp 	#SP
	beq	isspace
	bcs 	RegChr
	cmp	#CR+1
	bcc	isspecial
	brl	BadChar	
isspace	brl	HitSP
isspecial	asl 	a
	tax
	jmp 	(ChrTable,x)

; Bad character detected in WP; used to die now will treat character as a normal character. Set gray 
; border if developing to say we caught an old error and survived! Character will be changed to a
; DEL char.

BadChar	
	IF (Developing) THEN
	setborder #3
	ENDIF
	lda	#$FF
	sta	[ChrPtr]
	
; Regular character entered call check stuff to adjust values for ascent/descent/leading as needed.
; Make sure to preserve character in A-reg. Get width of character and adjust the current horizontal
; position.

RegChr	pha
	jsr 	CheckStuff
	pla
	jsl 	D_GetWdth
	clc
	adc 	CurH
	sta 	CurH

; Check if current horizontal has exceeded the right margin. If not bump the character pointer and
; go back for next character.

chkright	cmpw 	CurH,W_RightMargin
	bcs 	pastmarg
	incl 	ChrPtr
	bra 	ChrLoop

; We have gone past the right margin. Check to see if the current character is a space; branch if
; not. Else check if last character was a space ( ie two spaces in a row ); if not just set last
; space to where last horizontal was and branch back to the character loop.

pastmarg	lda 	[ChrPtr]
	and 	#$ff
	cmp 	#sp
	bne 	NoEndSpace
	lda 	WasLastSp
	bne 	WrapExSpace
	moveword 	LastH,WasLastSp
	brl 	ChrLoop

; Comes here if two spaces were kept past the margin. Dont count space length before doing a regular
; word wrap. ( Extra space was wrapped ).

WrapExSpace	moveword 	WasLastSp,LastH	
	bra 	RegWordWrap

; If start pointer is no longer same as end pointer then treat as regular word wrap.

NoEndSpace	cmpw 	W_STPtr,W_EndPtr
	bne 	RegWordWrap

; If no spaces on the line then use character that exceeded the right margin.	

	addlong 	#1,ChrPtr,W_EndPtr
	movelong 	W_EndPtr,W_JustPtr
	moveword 	CurH,LastH
	moveword 	tAscent,W_MaxAscent
	moveword 	tDescent,W_MaxDescent
	moveword 	tLeading,W_MaxLeading
	moveword 	tfont,W_EndFont
	moveword 	tstyle,W_EndStyle
	moveword 	tcolor,W_EndColor

; This is the regular word wrap exit. Just bits will get the number of pixels difference between
; the last seen horizontal position and the right margin. Check if we are in full justification
; mode. If so calculate the extra space that will be needed per blank to justify the line.

RegWordWrap	subword 	W_RightMargin,LastH,W_JustBits

	lda 	W_RulBits
	and 	#W_r_full
	beq 	killjust
	
; Check if single word on line ( just spaces <= 1 ). If so then zero extra justification space.

	stzl	W_JustExtra
	cmpw	W_JustSpaces,#2
	blt	exit

; At least two spaces exist on line so get justification amount needed for drawing line.

	spacelong
	pushword 	W_JustBits
	lda 	W_JustSpaces
	dec 	a
	pha
	_FixRatio
	pullLong 	W_JustExtra
	bra 	exit

; Move end pointer to just ptr for no justification.

killjust	movelong W_EndPtr,W_JustPtr
exit	return
;...................................................................................................;
; Check if ascent/descent/leading need to be changed. Iflag will be set when a change has been
; detected to font/style/size. Get the ascent/descent/size for the current font and adjust the
; running values if they are too small.

CheckStuff	ldy 	lflag
	beq 	NoHtChan
	stz 	lflag
	lda 	tfont
	ldx 	tstyle
	ldy 	tcolor
	jsl 	D_SelectFont
	jsl 	D_GetStats
	cmp 	tAscent
	bcc 	NotMaxA
	sta 	tAscent
NotMaxA	cpx 	tDescent
	bcc 	NotMaxD
	stx 	tDescent
NotMaxD	cpy 	tLeading
	bcc 	NoHtChan
	sty 	tLeading
NoHtChan	rts
;...................................................................................................;
; get new font and set both changed flags. bump character pointer by 3.

fChange	moveword	[ChrPtr]:#1,tFont
	lda	#3
	bra	bumpflags
;...................................................................................................;
; get new style and set both changed flags. bump character pointer by 2.

StyleChange	ldy 	#1
	lda 	[ChrPtr],y
	shortm
	sta 	tStyle
	longm
	bra	bump2
;...................................................................................................;
; get new size and set both changed flags. bump character pointer by 2.

SizeChange	ldy 	#1
	lda 	[ChrPtr],y
	shortm
	sta 	tStyle+1
	longm
bump2	lda	#2
	bra	bumpflags
;...................................................................................................;
; get new color and set something changed flag. bump character pointer by 2.

ColorChange	ldy 	#1
	lda 	[ChrPtr],y
	and 	#$ff
	sta 	tColor
	lda	#2
	bra	bumpflag2
;...................................................................................................;
; set change flags and bump character pointer by value in A-reg.

bumpflags	inc	lflag
bumpflag2	inc 	lflag2
	addwl	a,ChrPtr
	brl 	ChrLoop
;...................................................................................................;
; Tab character encountered. Bump current horizontal if not past right margin. Adjust justification
; values and cut line output variables.

HitTab	jsr 	CheckStuff

	addlong 	#1,ChrPtr,W_EndPtr
	movelong 	W_EndPtr,W_JustPtr

; Get position of next tab. If zero is returned then tab would go past the right margin and we should
; treat the tab as carriage return for a soft line wrap, otherwise adjust current horizontal position.

	call	W_UseTab,in=(tfont:w,tstyle:w,tcolor:w,CurH:w,ChrPtr:l),out=(a:w)
	jeq 	HitCr	
	sta 	CurH

; Tab found so clear number of justification spaces, and changed flag.

	stz 	W_JustSpaces
	stz 	lflag2

; Update the cut line out values. The current horizontal position is the last break position since
; we can break on tabs or spaces. If we were to break the max values for ascent/descent/leading
; would be the current running values and the font/style/color at the end of the line would be the
; current values.

	moveword 	CurH,LastH
	moveword 	tAscent,W_MaxAscent
	moveword 	tDescent,W_MaxDescent
	moveword 	tLeading,W_MaxLeading
	moveword 	tfont,W_EndFont
	moveword 	tstyle,W_EndStyle
	moveword 	tcolor,W_EndColor
	brl 	chkright
;...................................................................................................;
; Page/Date/Time width character encountered. Set pointer to string then do width.

PageWidth	movelong 	#W_PageStr,StringPtr
	bra 	DoWidth

DateWidth	movelong 	#W_DateStr,StringPtr
	bra 	DoWidth

TimeWidth	movelong 	#W_TimeStr,StringPtr

DoWidth	jsr 	CheckStuff
	tool	_StringWidth,in=(StringPtr:l),out=(a:w)
	addword 	a,CurH,CurH
	brl 	chkright
;...................................................................................................;
; Carriage return encountered. Set end pointer and justification pointer to location of carriage
; return.

HitCr	movelong	ChrPtr,ax
	movelong	ax,W_JustPtr
	movelong	ax,W_EndPtr

; Update ascent/descent/leading values if they have changed. Update ending font/style/color. Put
; difference between right margin and current horizontal position into just bits then exit. Note:
; last line of paragraphs is not justified ( this is what we want ).

	lda 	tascent
	ora 	tDescent
	ora 	tLeading
	beq 	MaxsOk
	moveword 	tAscent,W_MaxAscent
	moveword 	tDescent,W_MaxDescent
	moveword 	tLeading,W_MaxLeading
MaxsOk	moveword 	tfont,W_EndFont
	moveword 	tstyle,W_EndStyle
	moveword 	tcolor,W_EndColor
	subword 	W_RightMargin,CurH,W_JustBits
	brl 	exit
;...................................................................................................;
; Space detected. Increment the number of spaces count for justification. Point the end pointer just
; paste the space character. Set the last seen horizontal break location to the current horizontal
; position where the space was found.

HitSp	inc 	W_JustSpaces
	addlong 	#1,ChrPtr,W_EndPtr
	moveword 	CurH,LastH

; If Iflag2 has not been set then no changes have occurred yet so skip setting new values, else clear
; the flag and set the latest return values.

	lda 	lflag2
	beq 	SkipStuff	
	stz 	lflag2
	moveword 	tAscent,W_MaxAscent
	moveword 	tDescent,W_MaxDescent
	moveword 	tLeading,W_MaxLeading
	moveword 	tfont,W_EndFont
	moveword 	tstyle,W_EndStyle
	moveword 	tcolor,W_EndColor

; Handle space as regular character at this point.

SkipStuff	lda 	#sp
	brl 	RegChr
;...................................................................................................;
; Routines to handle the character found. Note uses BadChar if character is undefined and HitSp if
; character is a space.

ChrTable	DC.W	BadChar,fChange	;0,1
	DC.W	styleChange,SizeChange	;2,3
	DC.W	ColorChange,PageWidth	;4,5
	DC.W	DateWidth,TimeWidth	;6,7
	DC.W	BadChar,HitTab	;8,9
	DC.W	BadChar,BadChar	;a,b
	DC.W	BadChar,HitCR	;c,d

	ENDP



*****************************************************************************************************
; W_GetLRecPtr ( par:word, line:word ): LineRecPtr:ax
;
;    This routine will return a pointer to the record for the given line of the given paragraph. If
; unable to validate lines for the paragraph then return error #-1.

W_GetLRecPtr PROC	EXPORT
	input 	par:w,line:w
	local 	ParRec:l,Hand:l
	output 	LineRec:l
	error	err
	begin

	stzl	LineRec
	call	W_ValidLines,in=(par:w),err=err
	bcs	exit
	rcall	W_GetParRec,in=(par:a),out=(parrec:ax)
	movelong 	[ParRec]:#W_pLineHand,Hand

	ldy 	#2
	lda 	[Hand],y
	tax
	lda 	[Hand]
	ldy 	line
	jsl 	W_GetLineRec
	movelong 	ax,LineRec

exit	return

	ENDP

*****************************************************************************************************
; W_GetLineRec ( LinePtr:ax, Line:y ): LineRecPtr:ax
;
;    This routine will return a pointer in A,X to the record for the given line. LinePtr points to
; the start of the line handle to be used.

W_GetLineRec PROC	EXPORT

	pha
	tya
	asl 	a
	asl 	a
	asl 	a
	asl 	a
	adc 	#W_LineHeader
	clc
	adc 	1,s
	bcc 	exit
	inx
exit	ply
	rtl

	ENDP

*****************************************************************************************************
; W_UseTab ( CurFont:word, CurStyle:word, CurColor:word, CurH:word, CurPtr:long ):NextH:word
;
;    This routine will return horizontal position where placing text should continue given the
; current font/style/color horizontal position and a pointer to the text. If the tab places us past
; the right margin then return 0 for the next horizontal pixel position. CurH is where text up to
; CurPtr ends horizontally. CurPtr should point past the tab.
;
; CAUTION: 	This routine can change the current font
; NOTE:	This routine assumes that the text block for the paragraph has been locked down.

W_UseTab	PROC	EXPORT
	;Using 	wpglobals

	input 	CurFont:w,CurStyle:w,CurColor:w,CurH:w,CurPtr:l
	local 	xTab:w,CurTab:l,TextWidth:w,Room:w,StringPtr:l
	local 	FontChange:w,Indent:w
	output 	NextH:w
	begin

; Clear return value and font change flag.

	stz 	NextH
	stz 	fontChange
	jsr	selectfont

; Check if center or right justification if so branch and treat tab as just another space.

	lda 	W_RulBits
	and 	#W_r_full+W_r_left
	jeq 	MakeItSpace	  

; Get the number of tabs in ruler. If none then return with zero for horizontal, else get max tab
; offset.

	lda 	W_NumTabs
	jeq 	MakeCurH
	asl 	a
	asl 	a
	sta 	xTab

; Find tab in table if not found then return with past right margin (0) value for NextH.

	ldx 	#0
LookTab	cmpw 	CurH,W_Tabs:x
	bcc 	GetIndent
	inx
	inx
	inx
	inx
	cpx 	xTab
	bcc 	LookTab
	brl 	exit

; Determine whether to use indent margin or left margin as a possible tab stop.

GetIndent	moveword	W_Indent,Indent
	cmp	W_LeftMargin
	bge	FoundTab
	moveword	W_LeftMargin,Indent
	
; Found tab. If current horizontal position is less than indent margin and indent margin is less than
; the tab we found then use indent margin instead of tab, else use tab position for CurTab.

FoundTab	cmpw 	CurH,Indent
	bge 	NoUseIndent
	cmpw 	Indent,W_Tabs:x
	bge 	NoUseIndent
	moveword 	Indent,CurTab
	stz 	CurTab+2
	bra	GotCurTab
NoUseIndent	movelong 	W_Tabs:x,CurTab

; Check if found tab position will extend past the current right margin if so just return with 0 for
; NextH.

GotCurTab	cmpw 	CurTab,W_RightMargin
	jge 	exit
	
; Set NextH to found tab position. If tab is a left tab then we are done. If tab is decimal or right
; tab we still have some work to do.

	moveword 	CurTab,NextH
	lda 	CurTab+2
	jeq 	exit	   

; Point to next character. Determine how much room exists between the tab and the current horizontal
; position. Clear text width so far accumulated.

	incl 	CurPtr
	subword 	CurTab,CurH,Room
	stz 	TextWidth

; Main character loop. Check if decimal found (for decimal tabs). If not then branch to handle char.
; If period found and decimal tab then adjust for the tab and exit, else treat the period as a
; regular character.

ChrLoop	lda 	[CurPtr]
	and 	#$ff
	cmp 	#'.'
	bne 	noPer
	ldy 	CurTab+2
	bpl 	RegChr
	brl 	AdjustTab

; Check if character is valid, special character, or bad character and branch to the correct
; handler.

NoPer	cmp 	#sp
	bcs 	RegChr
	cmp	#cr+1
	bcs	badchar
UseTable	asl 	a
	tax
	jmp 	(ChrTable,x)

; Bad character detected in WP; used to die now will treat character as a normal character. Set gray 
; border if developing to say we caught an old error and survived! Character will be changed to a
; DEL char.

BadChar	
	IF (Developing) THEN
	setborder #3
	ENDIF
	lda	#$FF
	sta	[CurPtr]
	
; Regular character encountered. If font changes have been detected then select font before getting
; width of character. Get width of character and add it to the running text width. If width exceeds
; the room available then simply return with the tab position as found, else point to next character
; and go back for more.

RegChr	pha
	lda 	FontChange
	beq 	NoSelf
	jsr	selectfont
NoSelf	pla
	jsl 	D_GetWdth
	clc
	adc 	TextWidth
	sta 	TextWidth
GotTW	cmp 	Room
	bge 	MakeCurH
	incl 	CurPtr
	brl 	ChrLoop

; Tab character encountered. Make sure current font selected. If we ran out of room before the tap
; then return with NextH set to zero. Else return with NextH = CurH + (Room-TextWidth).

AdjustTab	jsr	selectfont
	lda 	Room
	sec
	sbc 	TextWidth
	bmi 	exit
	clc
	adc 	CurH
	sta 	NextH	
	bra 	exit

; Treat the tab as a space character. Get the width of a space character and set NextH accordingly,
; unless it will exceed the right margin in which case we should return zero.

MakeItSpace	lda 	#sp
	jsl 	D_GetWdth
	addword 	a,CurH,a
	cmp 	W_RightMargin
	bcs 	MakeCurH
	sta 	NextH
	bra 	exit

; Set return value to current horizontal value and return to caller.

MakeCurH	moveword 	CurH,NextH

exit	jsr	selectfont
	return
;...................................................................................................;
; get new font and bump character pointer by 3.

FChange	ldy 	#1
	lda 	[CurPtr],y
	sta 	CurFont
	lda	#3
	bra	bumpptr
;...................................................................................................;
; get new size and bump character pointer by 2.

SizeChange	ldy 	#1
	lda 	[CurPtr],y
	shortm
	sta 	CurStyle+1
	longm
	bra	bumpby2
;...................................................................................................;
; get new style and bump character pointer by 2.

StyleChange	ldy 	#1
	lda 	[CurPtr],y
	shortm
	sta 	CurStyle
	longm
	bra	bumpby2
;...................................................................................................;
; get new color and bump character pointer by 2.

ColorChange	ldy 	#1
	lda 	[CurPtr],y
	and 	#$ff
	sta 	CurColor
bumpby2	lda	#2
;...................................................................................................;

bumpptr	addwl	a,CurPtr
	moveword	#1,FontChange
	brl	ChrLoop
;...................................................................................................;
; Page/Date/Time width character encountered. Set pointer to string then do width.

PageWidth	movelong 	#W_PageStr,StringPtr
	bra 	DoWidth

DateWidth	movelong 	#W_DateStr,StringPtr
	bra 	DoWidth

TimeWidth	movelong 	#W_TimeStr,StringPtr

DoWidth	tool	_StringWidth,in=(StringPtr:l),out=(a:w)
	addword 	a,TextWidth,TextWidth
	brl 	GotTW
;...................................................................................................;
; Select the current font, style, and color.

selectfont	rcall	D_SelectFont,in=(CurFont:a,CurStyle:x,CurColor:y)
	rts
;...................................................................................................;

ChrTable	DC.W	BadChar,FChange	;0,1
	DC.W	StyleChange,SizeChange	;2,3
	DC.W	ColorChange,PageWidth	;4,5
	DC.W	DateWidth,TimeWidth	;6,7
	DC.W	BadChar,AdjustTab	;8,9
	DC.W	BadChar,BadChar	;a,b
	DC.W	BadChar,AdJustTab	;c,d
;...................................................................................................;

	ENDP

*****************************************************************************************************
; W_CalcLineHeight ( LineRecPtr:ax ): Height:a, Descent:x
;
;    This routine will return the height and descent for the given line. The routine will take into
; account the current line spacing.

W_CalcLineHeight PROC	EXPORT
	;Using 	wpglobals
	;Using 	D_GlobalData

ptr	equ 0
W_Leading	equ 4
W_HeightNL	equ 6
height	equ 8
descent	equ 10

	tay
	phd
	lda 	>D_MainZPage
	tcd
	sty 	ptr
	stx 	ptr+2

	moveword 	[ptr]:#W_lHeight,W_HeightNL
	addword 	[ptr]:#W_lLeading,W_HeightNL,height
	moveword 	[ptr]:#W_lDescent,descent

	lda 	W_RulBits
	and 	#W_r_single
	bne 	exit

	lda 	W_HeightNL
	lsr 	a
	addword 	a,height,height

	lda 	W_RulBits
	and 	#W_r_double
	bne 	exit

	lda 	W_HeightNL
	lsr 	a
	addword 	a,height,height

exit	lda 	height
	ldx 	descent
	pld
	rtl

	ENDP

*****************************************************************************************************
; W_FindFont ( par:word, line:word, offset:word ): font:long, color:word
;
;    This routine will return font and color for the given paragraph line and the given offset.
;
; NOTE: This routine will return an error if GetLRecPtr can not get a pointer to the line record.

W_FindFont	PROC	EXPORT
	;Using 	wpglobals

	input 	Par:w,line:w,offset:w
	local 	LineRec:l,StartPtr:l,MaxLook:w,ParRec:l
	output 	font:l,color:w
	error	err
	begin

; Get pointer to paragraph record.
	
	stz	err
	rcall	W_GetParRec,in=(par:a),out=(parrec:ax)

; If paragraph is a page break then look at next paragraph line 0 to get the font and color.

	cmpw 	[ParRec]:#W_pAttr,#W_PgBrk
	bne 	NotPB
	inc 	par
	stz 	line
	moveword 	#W_TextHeader,offset

; Get pointer to line record for the given paragraph line. From the line record extract the default
; font, style, and color to use for the line.

NotPB	call	W_GetLRecPtr,in=(par:w,line:w),out=(LineRec:l),err=err
	jcs	exit
	moveword 	[LineRec]:#W_lFont,font
	moveword 	[LineRec]:#W_lStyle,font+2	;STYLE
	moveword 	[LineRec]:#W_lColor,color

; Get pointer to start of line text.

	moveword 	[LineRec]:#W_lOffset,x
	stx 	MaxLook
	lda 	par
	jsl 	W_GetAddr
	movelong 	ax,StartPtr

; Determine maximum number of character we wish to look at by subtracting the given offset into the
; line from the starting offset of the line in the text block.

	subword 	offset,MaxLook,MaxLook

; Until we get to the point in the line that we want accumulate font/style/color changes.

	ldy 	#0
ChrLoop	cpy 	MaxLook
	jcs 	exit

	lda 	[StartPtr],y
	and 	#$ff
	iny
	cmp 	#sp
	bcs 	ChrLoop
	cmp	#cr+1
	bcs	BadChar

	asl 	a
	tax
	jmp 	(ChrTable,x)

; Bad character detected in WP; used to die now will treat character as a normal character. Set gray 
; border if developing to say we caught an old error and survived! Character will be changed to a
; DEL char.

BadChar	
	IF (Developing) THEN
	setborder #3
	ENDIF
	dey
	lda	#$FF
	sta	[StartPtr],y
	iny
	bra	Chrloop

; Font change encountered. Save new running font and point Y-reg past font info.

FChange	lda 	[StartPtr],y
	sta 	Font
	iny
	iny
	brl 	ChrLoop

; Size change encountered. Save new running size and point Y-reg past size info.

SizeChange	lda 	[StartPtr],y
	shortm
	sta 	Font+3
	longm
	iny
	brl 	ChrLoop

; Style change encountered. Save new running style and point Y-reg past style info.

StyleChange	lda 	[StartPtr],y
	shortm
	sta 	Font+2
	longm
	iny
	brl 	ChrLoop

; Color change encountered. Save new running color and point Y-reg past color info.

ColorChange	lda 	[StartPtr],y
	and 	#$ff
	sta 	color
	iny
	brl 	ChrLoop

exit	return
;...................................................................................................;

ChrTable	DC.W	BadChar,FChange	;0,1
	DC.W	StyleChange,SizeChange	;2,3
	DC.W	ColorChange,ChrLoop	;4,5
	DC.W	ChrLoop,ChrLoop	;6,7
	DC.W	BadChar,ChrLoop	;8,9
	DC.W	BadChar,BadChar	;a,b
	DC.W	BadChar,ChrLoop	;c,d
;...................................................................................................;

	ENDP

*****************************************************************************************************
; W_ReadFont ( font:long, color:word, StartPtr:long, EndPtr:long ): font:long, color:word
;
;    This routine will return font and color for the text between the two given pointers.

W_ReadFont	PROC	EXPORT

	input 	InFont:l,InCol:w,StartPtr:l,Ptr2:l
	local 	MaxLook:w
	output 	font:l,color:w
	begin

; Initialize starting font and color from inputs and determine maximum number of characters to be
; examined.

	movelong 	InFont,font
	moveword 	InCol,color
	subword 	Ptr2,StartPtr,MaxLook

; Until we get to the point in the line that we want accumulate font/style/color changes.

	ldy 	#0
ChrLoop	cpy 	MaxLook
	jcs 	exit

	lda 	[StartPtr],y
	and 	#$ff
	iny
	cmp 	#sp
	bcs 	ChrLoop
	cmp	#cr+1
	bcs	BadChar

	asl 	a
	tax
	jmp 	(ChrTable,x)

; Bad character detected in WP; used to die now will treat character as a normal character. Set gray 
; border if developing to say we caught an old error and survived! Character will be changed to a
; DEL char.

BadChar	
	IF (Developing) THEN
	setborder #3
	ENDIF
	dey
	lda	#$FF
	sta	[StartPtr],y
	iny
	bra	Chrloop

; Font change encountered. Save new running font and point Y-reg past font info.

FChange	lda 	[StartPtr],y
	sta 	Font
	iny
	iny
	brl 	ChrLoop

; Size change encountered. Save new running size and point Y-reg past size info.

SizeChange	lda 	[StartPtr],y
	shortm
	sta 	Font+3
	longm
	iny
	brl 	ChrLoop

; Style change encountered. Save new running style and point Y-reg past style info.

StyleChange	lda 	[StartPtr],y
	shortm
	sta 	Font+2
	longm
	iny
	brl 	ChrLoop

; Color change encountered. Save new running color and point Y-reg past color info.

ColorChange	lda 	[StartPtr],y
	and 	#$ff
	sta 	color
	iny
	brl 	ChrLoop

exit	return
;...................................................................................................;

ChrTable	DC.W	BadChar,FChange	;0,1
	DC.W	StyleChange,SizeChange	;2,3
	DC.W	ColorChange,ChrLoop	;4,5
	DC.W	ChrLoop,ChrLoop	;6,7
	DC.W	BadChar,ChrLoop	;8,9
	DC.W	BadChar,BadChar	;a,b
	DC.W	BadChar,ChrLoop	;c,d
;...................................................................................................;

	ENDP

*****************************************************************************************************
; W_UpdateLines ( par:word, line:word, amount:word )
;
;    This routine will add the given amount to the offset into the text block of every line after the
; given line. ( useful for inserting text on the line and keeping all the other lines correct ).
;
; Note: Can return an error if valid lines fails.

W_UpdateLines PROC	EXPORT

	input 	par:w,line:w,amount:w
	local 	ParRec:l,LineRec:l,LineHand:l,lines:w
	error	err
	begin

; Make sure line handle contains valid lines for the given paragraph. If error then abort and hope
; for the best.

	call	W_ValidLines,in=(par:w),err=err
	jcs	exit

; Get pointer to paragraph record. Get number of lines in the paragraph. Get pointer to the given
; line's record.

	rcall	W_GetParRec,in=(par:a),out=(ParRec:ax)
	moveword 	[ParRec]:#W_pLastLine,lines
	movelong 	[ParRec]:#W_pLineHand,LineHand
	movelong 	[LineHand],LineRec
	rcall	W_GetLineRec,in=(LineRec:ax,line:y),out=(LineRec:ax)

; Add amount to each line's offset after the given line.

	ldy 	#W_lOffset
Loop	inc 	line
	tya
	clc
	adc 	#W_lBytes
	tay
	addword 	Amount,[LineRec]:y,[LineRec]:y
	cmpw 	line,lines
	bcc 	Loop

exit	return

	ENDP

*****************************************************************************************************
; W_CutLine2 ( par:word, line:word )
;
;    This routine will setup the entry values to CutLine for the given line and paragraph and then
; will call CutLine to determine where the given paragraph line should be cut and what its end line
; attributes are. (see CutLine for more details)
;
; NOTE: This routine can return an error if the line handle does not exist for the paragraph.

W_CutLine2	PROC	EXPORT
	;Using 	wpglobals

	input 	par:w,line:w
	local 	ptr:l,LineHand:l,LineRecPtr:l
	error	err
	begin

; Get pointer to current paragraph record and put its ruler info in the ruler globals.

	moveword	#1,err
	rcall	W_GetParRec,in=(par:a),out=(ptr:ax)
	rcall	W_ReadRuler,in=([ptr]:#W_pRulerHand:ax)

; Make sure line handle exists else return with error set.

	movelong 	[ptr]:#W_pLineHand,LineHand
	cpzl	LineHand
	jeq	exit
	ldy	#2
	lda	[LineHand]
	ora	[LineHand],y
	jeq	exit
	stz	err
	
; Set starting font/style/color and starting ptr/line values for cutline to use. If line handle does
; not exist or has been purged then return with error set.

	rcall	W_GetLineRec,in=([LineHand]:ax,line:y),out=(LineRecPtr:ax)
	moveword 	[LineRecPtr]:#W_lFont,W_STFont
	moveword 	[LineRecPtr]:#W_lStyle,W_STStyle
	moveword 	[LineRecPtr]:#W_lColor,W_STColor
	moveword 	[LineRecPtr]:#W_lOffset,a
	tax
	lda 	par
	jsl 	W_GetAddr
	movelong 	ax,W_STPtr
	moveword 	line,W_STLIne

; Set just ptr and end ptr to start of next line.

	moveword 	[LineRecPtr]:#W_lOffset+W_lBytes,a
	tax
	lda 	par
	jsl 	W_GetAddr
	movelong 	ax,W_JustPtr
	movelong 	ax,W_EndPtr

; Cut line to do the actual work of determining where this paragraph line should be cut.

	jsl 	W_CutLine
exit	return

	ENDP

*****************************************************************************************************
; W_GetLineAddr ( par:word, line:word ) ptr:long
;
;    This routine will return a pointer to the start of the given paragraph line's text.
;
; Note: This routine can return an error if ValidLines fails.

W_GetLineAddr PROC	EXPORT

	input 	par:w,line:w
	local 	ptr:l,LineHand:l,LinePtr:l
	output 	LineAddr:l
	error	err
	begin

; Make sure paragraph lines are valid. If can't make lines valid we're in trouble error on up.

	call	W_ValidLines,in=(par:w),err=err
	jcs	exit

; Get pointer to start of line's text.

	rcall	W_GetParRec,in=(par:a),out=(ptr:ax)
	movelong 	[ptr]:#W_pLineHand,LineHand
	movelong 	[LineHand],LinePtr
	rcall	W_GetLineRec,in=(LinePtr:ax,Line:y),out=(ptr:ax)
	moveword 	[ptr]:#W_lOffset,x
	lda 	par
	jsl 	W_GetAddr
	movelong 	ax,LineAddr

exit	return

	ENDP

*****************************************************************************************************
; W_GetEndLine 	( par:word, line:word ): offset:word
; W_GetLineOffset 	( par:word, line:word ): offset:word
; W_GetBegLine	( par:word, line:word ): offset:word
;
;    The following shared code is used by the above three routines to return the requested offset
; into the given paragraph line's text.
;
; Note: These routine can return errors if the paragraph line handle could not be validated.

W_GetEndLine PROC	EXPORT
	EXPORT	W_GetLineOffset
	EXPORT	W_GetBegLine
	
; Get end by bumping line count first to next line.

	lda 	4,s
	inc 	a
	sta 	4,s

W_GetLineOffset	
W_GetBegLine

	input 	par:w,line:w
	local 	ptr:l,LineHand:l,LinePtr:l
	output 	offset:w
	error	err
	begin

; Make sure that valid line handle exists for this paragraph. If we can't make a valid line handle
; then we are in serious trouble pass the error on up.

	call	W_ValidLines,in=(par:w),err=err
	bcs	exit

; Get offset for the given line from its line record information.

	rcall	W_GetParRec,in=(par:a),out=(ptr:ax)
	movelong 	[ptr]:#W_pLineHand,LineHand
	movelong 	[LineHand],LinePtr
	rcall	W_GetLineRec,in=(LinePtr:ax,Line:y),out=(ptr:ax)
	moveword 	[ptr]:#W_lOffset,offset

exit	return

	ENDP

*****************************************************************************************************
; W_NextLine ( par:word, line:word ) par:word, line:word
;
;    This routine will return the paragraph and line for the line following the given paragraph line.
; An error will be returned if this is already the last line of the last paragraph.

W_NextLine	PROC	EXPORT
	;Using 	wpglobals

	input 	par:w,line:w
	output 	OPar:w,OLine:w
	local 	ParRec:l
	error 	err
	begin

; Bump line count. If it the given paragraph has enough lines we're done.

	stz 	err
	moveword 	par,OPar
	moveword 	line,OLine
	inc 	OLine
	rcall	W_GetParRec,in=(par:a),out=(ParRec:ax)
	cmpw 	Oline,[ParRec]:#W_pLastLine
	bcc 	exit

; Else bump the paragraph count. If we were already on the last paragraph then return error set.

	inc 	OPar
	stz 	OLine
	cmpw 	W_LastP,OPar
	bcs 	exit
	inc	err

exit	return

	ENDP
	END

