PUBLIC	Extended_Character, Wrapmode

EXTRN	.CAPS:Near, .Error0:Near, .Msg:Near, .Makespace:near, CDELK:Near
EXTRN	.CLRMsg:Near, .Screen:NEAR, FMODELINE:Near, .InCH:NEAR
EXTRN	Sound:Near, .OUTSTR:NEAR

EXTRN	NoModeline:BYTE, LastLineUpdate:Near, LastLastLine:Word

;---------------------------------------------------------------------------
	page	,132
NAME ESCX

TITLE extended commands ESC-X

IF2
       %OUT *Pass 2*
ENDIF

INCLUDE        FDEF.DEF

Program        SEGMENT PARA    PUBLIC  'code'
       ASSUME  CS:Program, DS:Program, ES:Program, SS:Program

;-------------------------------------------------------------------------
CaseReplace	db	TRUE
QUERY		db	TRUE
Toggling	db	'[Toggling Wrap Mode]'
LastSearchStart	dw	100h
Confirmswitch	db	'Skip=<BS>   Replace=<SPACE>  Abort=<CTL-G>'
AbortedReplace	db	'*** User Abort ***'
ExitCharacter	db	CR
Cursor		equ	lowintensity*256+178


$Free	=	(Offset MXInvalid)

MXJTable	dw	$Free	; ^@
dw	$Free		; ^A
dw	$Free		; ^B
dw	$Free		; ^C
dw	$Free		; ^D
dw	$Free		; ^E
dw	$Free		; ^F
dw	$Free		; ^G
dw	$Free		; ^H
dw	$Free		; ^I
dw	$Free		; ^J
dw	$Free		; ^K
dw	$Free		; ^L
dw	$Free		; ^M
dw	$Free		; ^N
dw	$Free		; ^O
dw	$Free		; ^P
dw	$Free		; ^Q
dw	$Free		; ^R
dw	$Free		; ^S
dw	$Free		; ^T
dw	$Free		; ^U
dw	$Free		; ^V
dw	$Free		; ^W
dw	$Free		; ^X
dw	$Free		; ^Y
dw	$Free		; ^Z
dw	$Free		; ^[
dw	$Free		; ^\
dw	$Free		; ^]
dw	$Free		; ^^
dw	$Free		; ^_
dw	$Free		; ' '
dw	$Free		; !
dw	$Free		; "
dw	$Free		; #
dw	$Free		; $
dw	$Free		; %
dw	$Free		; &
dw	$Free		; `
dw	$Free		; (
dw	$Free		; )
dw	$Free		; *
dw	$Free		; +
dw	$Free		; `
dw	$Free		; -
dw	$Free		; .
dw	$Free		; \
dw	$Free		; 0
dw	$Free		; 1
dw	$Free		; 2
dw	$Free		; 3
dw	$Free		; 4
dw	$Free		; 5
dw	$Free		; 6
dw	$Free		; 7
dw	$Free		; 8
dw	$Free		; 9
dw	$Free		; :
dw	$Free		; ;
dw	$Free		; <
dw	$Free		; =
dw	$Free		; >
dw	$Free		; ?
dw	$Free		; @
dw	$Free		; A
dw	$Free		; B
dw	$Free		; C
dw	$Free		; D
dw	$Free		; E
dw	$Free		; F
dw	$Free		; G
dw	$Free		; H
dw	$Free		; I
dw	$Free		; J
dw	$Free		; K
dw	$Free		; L
dw	Offset ToggleLastLine		; M
dw	$Free		; N
dw	$Free		; O
dw	$Free		; P
dw	Offset QuerySearchReplace	; Q
dw	$Free		; R
dw	Offset SearchReplace		; S
dw	$Free		; T
dw	$Free		; U
dw	$Free		; V
dw	Offset ToggleWrapmode		; W
dw	$Free		; X
dw	$Free		; Y
dw	$Free		; Z
dw	$Free		; [
dw	$Free		; \
dw	$Free		; ]
dw	$Free		; ^
dw	$Free		; _
dw	$Free		; '
					; lowercase trapped

MXInvalidT0	db	'*** ESC-X '
MXInvalidT1	db	'? unknown ***'
$Error1         db      '*** FileStart ***'
$Error2         db      '*** FileEnd ***'


;---------------------------------------------------------------------------
Extended_Character	PROC	Near

	call	.CAPS

	cmp	AL, 'z'
	ja	MXInvalid
	
MxParse:xor	AH, AH			; clear insignificant
	add	AX, AX			; dw index
	mov	BX, AX			; get the index
	jmp	CS:MXJTable [BX]	; and jump

MXInvalid:
	mov	CS:MXInvalidT1, AL
        mov     SI, Offset MXInvalidT0
        mov     CX, 23
        jmp     .Error0

Extended_Character  ENDP

;---------------------------------------------------------------------------
ToggleWrapmode	PROC	Near

	xor	DS:BCB.EDMode, Wrapmode
	mov	SI, Offset Toggling
	mov	CX, 20
	jmp	.Msg

ToggleWrapmode	ENDP
;---------------------------------------------------------------------------


;---------------------------------------------------------------------------
; Search and Replace
Skip	equ	8
String1	db	80 dup ('?')
STRLEN1	dw	0
String2	db	80 dup ('?')
STRLEN2	dw	0
StringLength	equ	79
SeaMsg	db	'Replace string <CR>:'
QSeaMsg	db	'Query Replace string <CR>:'
RepMsg	db	'with string <CR>:'
DoneMsg	db	'[Done]'

;---------------------------------------------------------------------------
ToggleLastLine	PROC	NEAR
	xor	CS:Nomodeline, Toggle
	mov	SI, CS:LastLastLine
	call	LastLineupdate
	ret
ToggleLastLine	ENDP
;---------------------------------------------------------------------------
QuerySearchReplace	PROC	Near
	mov	CS:Query, TRUE

	call	.CLRMsg
	mov	SI, Offset QSeaMsg
	mov	CX, 26

	call	.SRPL
	ret
QuerySearchReplace	ENDP
;---------------------------------------------------------------------------
SearchReplace	PROC	NEAR
	mov	CS:Query, FALSE

	call	.CLRMsg
	mov	SI, Offset SeaMsg
	mov	CX, 20

	call	.SRPL
	ret
SearchReplace	ENDP

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.SRPL	PROC	NEAR

	call	.Msg

	mov	SI, CX			; now calculate the screen message
	add	SI, SI
	add	SI, StartofMsgField
	mov	DI, Offset String1	; load to string 1
	mov	DL, CR			; and end everything with a CR
	call	.GetMsg			; result in String1, CX: # of chars

	cmp	CX, 0
	jne	Continue1
	jmp	AbortReplace
Continue1:
	mov	CS:STRLEN1, CX
	cmp	CS:CaseReplace, TRUE
	jne	LeaveCase

	mov	SI, Offset String1
Changecase:
	mov	AL, CS:[SI]
	call	.CAPS
	mov	CS:[SI], AL
	inc	SI
	loop	Changecase
leavecase:
	call	.CLRMsg
	mov	SI, Offset RepMsg
	mov	CX, 17
	call	.Msg

	mov	SI, CX			; now calculate the screen message
	add	SI, SI
	add	SI, StartofMsgField
	mov	DI, Offset String2	; load to string 1
	mov	DL, CR			; and end everything with a CR
	call	.GetMsg			; result in String1, CX: # of chars

	push	DS:BCB.FCursor

	mov	CS:STRLEN2, CX
	mov	SI, DS:BCB.FCursor
	mov	CS:LastSearchStart, SI
SRLoop:	call	SearchforStr1
	cmp	SI, 0
	je	NotFound
	call	AskforAction
	cmp	AL, Skip
	je	SRLoop

	call	Replace1with2
	jmp	SRLoop
NotFound:
	pop	DS:BCB.FCursor
	call	.CLRMsg
	call	Sound
	mov	SI, Offset DoneMsg
	mov	CX, 6
	jmp	.Msg

.SRPL	ENDP
;---------------------------------------------------------------------------
SearchforStr1	PROC	NEAR
	mov	SI, CS:LastSearchStart
	mov	DX, CS:STRLEN1
	call	.Find
	mov	CS:LastSearchStart, SI
	inc	CS:LastSearchStart
	ret
SearchforStr1	ENDP
;---------------------------------------------------------------------------
AbortReplace:
	mov	SI, Offset AbortedReplace
	mov	CX, 18
	jmp	.Error0


AskforAction	PROC	NEAR
	cmp	CS:QUERY, TRUE
	je	AskComplicated
	push	SI
	mov	DS:BCB.FCursor, SI
	pop	SI
	mov	AL, ' '
	ret
AskComplicated:
				; here check whether question necessary
	push	SI
	add	SI, CS:STRLEN1
	mov	DS:BCB.FCursor, SI

	call	.Screen		; maybe put all into inverse

	push	DS:BCB.ScrCursor

	mov	SI, CS:STRLEN1
	sub	DS:BCB.FCursor, SI

	call	.Screen

	pop	SI		; get the start of the to be replaced word

	mov	BX, DS:BCB.ScrCursor
RedoPaint:
	cmp	BX, SI
	jae	FinishedPaint
	mov	Byte Ptr ES:[BX+1], modelinecolor
	inc	BX
	inc	BX
	jmp	RedoPaint

FinishedPaint:
	call	FModeline

	call	.CLRMsg
	mov	SI, Offset ConfirmSwitch
	mov	CX, 42
	call	.Msg

AA1:	call	.Inch
	jz	AA1

	cmp	AL, CTL_G
	je	Abortreplace

	cmp	AL, ' '
	je	OKret

	cmp	AL, Skip
	je	OKret

	call	Sound
	jmp	AA1

OKret:	pop	SI
	ret

AskforAction	ENDP

;---------------------------------------------------------------------------
Replace1with2	PROC	NEAR
	mov	AX, DS
	mov	ES, AX
	mov	CX, CS:StrLen1

	call	CDELK			; killed string 1

	mov	AX, CS
	mov	ES, AX
	push	SI
	mov	SI, Offset String2
	mov	CX, CS:StrLen2
	call	.OutStr
	pop	SI
	ret

Replace1with2	ENDP
;---------------------------------------------------------------------------
Wildcard	EQU	10	; ^J
.FIND	PROC	Near
; SI -> Start of Search	DX  -> # characters in Searchbuffer

	dec	SI
.FNew:  xor	BX, BX  ; last try unsuccessful, try one character further
	mov	DI, Offset String1
	inc	SI
.FMay:  cmp	SI, DS:BCB.FEnd  ; all characters so far match
	jae	.FErr
	mov	AH, CS:[DI+BX]
	cmp	AH, Wildcard
	je	.F1Mat
	mov	AL, DS:[SI+BX]
	call	.CAPS
	cmp	AL, AH
	jne	.FNew
.F1Mat: inc	BX		; one character matches
	cmp	BX, DX
	jb	.FMay

.FFound:mov	AH, 0
	ret
.FErr:  mov	AH, 0FFh
	sub	SI, SI
	ret

.Find	ENDP

;...........................................................................
; DI holds where the message shall go

.GetMsg	PROC	NEAR
	xor	BX, BX			; 0 characters received
	mov	CS:Exitcharacter, DL

.TextLoop:
	mov	AX, Cursor		; set the cursor
	mov	ES:[SI+BX], AX


.Text0:	call	.InCh
	jz	.Text0

	cmp	AL, CS:Exitcharacter	; if we have an end character
	je	.TextEnd
	cmp	AL, 21			; if we wish to retry = C-U
	je	.TextRedo
	cmp	AL, 7			; if we wish to quit = C-G
	je	.TextQuit
	cmp	AL, 8			; if we wish to backspace
	je	.TextDel

.TextChar:				; if we just wish to add a char
	cmp	BX, StringLength
	jae	FailLength

	mov	CS:[BX+DI], AL

	mov	AH, lowintensity	; put it to screen
	mov	ES:[BX+SI], AX

	inc	BX			; next character.
	inc	SI			; advance the screen by one extra.

	jmp	.TextLoop
FailLength:
	call	Sound
	jmp	.TextLoop
;...........................................................................
.TextRedo:	
	cmp	BX, 0				; are there 0 chars ?
	je	.TextLoop
	mov	AX, ' '+lowintensity*256	; no, then clear from scr
	mov	ES:[BX+SI], AX
	dec	BX				; one less char to clear
	dec	SI				; but adjust for scr *2
	jmp	.TextRedo

;...........................................................................
.TextQuit:
	call	.CLRMsg
	xor	BX, BX
	mov	SI, Offset AbortedReplace
	mov	CX, 18
	jmp	.Error0

;...........................................................................
.TextEnd:
	mov	CX, BX
	ret
;...........................................................................

.Textdel:
	cmp	BX, 0				; if there are 0 chars...
	je	.TextDelErr
	mov	AX, ' '+lowintensity*256	; clear the cursor
	mov	ES:[BX+SI], AX
	dec	SI				; adjust to point to
	dec	BX				; previous character
	jmp	.TextLoop

.TextDelErr:
	call	Sound
	jmp	.Text0				; do nothing
.GetMsg	ENDP



;---------------------------------------------------------------------------
	PROGRAM	ENDS
END
