2264 lines
57 KiB
C
2264 lines
57 KiB
C
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
document.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the code which deals with the underlying document
|
|
file.
|
|
|
|
Author:
|
|
|
|
Jim Schaad (jimsch)
|
|
Griffith Wm. Kadnier (v-griffk) 1-Nov-1992
|
|
|
|
Environment:
|
|
|
|
Win32 - User
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#define ROREGION(d,X,Y) ((d)->RORegionSet && ((Y)<(d)->RoY2 || ((Y)==(d)->RoY2 && (X)<(d)->RoX2)))
|
|
|
|
|
|
//Prototypes
|
|
|
|
void NEAR PASCAL ReadOnlyBeep (void);
|
|
LPBLOCKDEF NEAR PASCAL FreeBlock (int doc, LPBLOCKDEF pCurBlock);
|
|
void NEAR PASCAL ModifiedDoc (int doc);
|
|
BOOL NEAR PASCAL FindInsertSpace (int doc, LPBLOCKDEF *pCurBlock, LPLINEREC *pCurLine, LPSTR pBlockEnd, int sizeExpansion);
|
|
BOOL NEAR PASCAL CancelDelete (int doc);
|
|
BOOL NEAR PASCAL PartialLineDelete (int doc, LPBLOCKDEF *pCurBlock, LPLINEREC *pCurLine, int *totalSize, int col1, int col2, BOOL moveToNextLine);
|
|
BOOL NEAR PASCAL FullLineDelete (int doc, LPBLOCKDEF *pCurBlock, LPLINEREC *pCurLine, int *totalSize);
|
|
BOOL NEAR PASCAL JoinLines (int doc, LPBLOCKDEF pCurBlock, LPLINEREC pCurLine, int *totalSize, int lineNb, int col1);
|
|
|
|
|
|
/*
|
|
Document Structure in memory
|
|
---------
|
|
(Use TABULATION = 3 if drawing looks bad)
|
|
- Blocks are allocated using malloc
|
|
|
|
Line status (tags,
|
|
----------> error, breakpoint, <-
|
|
| in C comment, ...) |
|
|
| |
|
|
| Previous line | ---------> Previous line
|
|
| --------> length (in previous | | length
|
|
| | Block for Line 0) | |
|
|
| | | |
|
|
| | ------> Line Length | | -------> Line length
|
|
| | | | | |
|
|
| | | --> Chars | | | | ----> Chars
|
|
| | | | | | | | |
|
|
| | | | v | | | |
|
|
| | | | | ... | | | |
|
|
| | | | | |_|_|_|_________| |_|_|...__| |_|_|_|____| <-Unused->| | | |
|
|
| | | | | Line 0 Block n Line...1 Line m Block n space | | | |
|
|
__| |_|_|_|_________________________...____________________________| |_|_|__
|
|
|
|
| | | | Block n ^ |
|
|
| | | | | |
|
|
v | | ---> Offset of Last line in block --- v
|
|
| |
|
|
Block | | Block
|
|
n - 1 | -----> Address of Block n + 1 (0 if last block) n + 1
|
|
|
|
|
|
|
|
-------> Address of Block n - 1 (0 if first block)
|
|
|
|
*/
|
|
|
|
static BOOL fReadOnlyErrorStatus = FALSE;
|
|
static BOOL fReadOnlyBeepEnabled = TRUE;
|
|
|
|
|
|
void NEAR PASCAL
|
|
ReadOnlyBeep(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Report a readonly error condition; set flag and
|
|
beep if the beep flag is set.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
fReadOnlyErrorStatus = TRUE;
|
|
if (fReadOnlyBeepEnabled) {
|
|
MessageBeep(0);
|
|
}
|
|
}
|
|
|
|
void FAR PASCAL
|
|
EnableReadOnlyBeep(
|
|
BOOL f
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set or clear the beep flag. Always clears the error status flag.
|
|
|
|
Arguments:
|
|
|
|
f - supplies new flag value
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
fReadOnlyBeepEnabled = (f != 0);
|
|
fReadOnlyErrorStatus = FALSE;
|
|
}
|
|
|
|
BOOL FAR PASCAL
|
|
QueryReadOnlyError(
|
|
void
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Examine the read only error status flag. Status flag is cleared
|
|
after examining the value.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Value of readonly error status - TRUE or FALSE.
|
|
|
|
--*/
|
|
{
|
|
BOOL f = fReadOnlyErrorStatus;
|
|
fReadOnlyErrorStatus = FALSE;
|
|
return f;
|
|
}
|
|
|
|
LPSTR FAR PASCAL
|
|
DocAlloc(
|
|
WORD bytes
|
|
)
|
|
{
|
|
PSTR psz = (PSTR) malloc(bytes);
|
|
if (psz) {
|
|
ZeroMemory(psz, bytes);
|
|
}
|
|
return psz;
|
|
}
|
|
|
|
BOOL FAR PASCAL
|
|
DocFree(
|
|
LPVOID lpv
|
|
)
|
|
{
|
|
free(lpv);
|
|
return TRUE;
|
|
}
|
|
|
|
//Allocates and initialize a block, update doc pointers
|
|
BOOL FAR PASCAL
|
|
AllocateBlock(
|
|
LPBLOCKDEF pPrevBlock,
|
|
LPBLOCKDEF pNextBlock,
|
|
LPBLOCKDEF *pNewBlock
|
|
)
|
|
{
|
|
//Allocate memory
|
|
*pNewBlock = (LPBLOCKDEF)DocAlloc(sizeof(BLOCKDEF));
|
|
if (*pNewBlock == NULL) {
|
|
return ErrorBox(SYS_Allocate_Memory);
|
|
}
|
|
|
|
//Do initialization work
|
|
(*pNewBlock)->NextBlock = pNextBlock;
|
|
(*pNewBlock)->PrevBlock = pPrevBlock;
|
|
(*pNewBlock)->LastLineOffset = 0;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//Free current block, link previous and next blocks, update doc pointers
|
|
LPBLOCKDEF NEAR PASCAL
|
|
FreeBlock(
|
|
int doc,
|
|
LPBLOCKDEF pCurBlock
|
|
)
|
|
{
|
|
LPBLOCKDEF nextBlock = pCurBlock->NextBlock;
|
|
|
|
//Link previous block to next block
|
|
if (pCurBlock->PrevBlock == NULL) {
|
|
Docs[doc].FirstBlock = nextBlock;
|
|
} else {
|
|
pCurBlock->PrevBlock->NextBlock = nextBlock;
|
|
}
|
|
if (nextBlock == NULL) {
|
|
Docs[doc].LastBlock = pCurBlock->PrevBlock;
|
|
} else {
|
|
pCurBlock->NextBlock->PrevBlock = pCurBlock->PrevBlock;
|
|
}
|
|
|
|
//Release memory
|
|
if (!DocFree((LPSTR)pCurBlock)) {
|
|
InternalErrorBox(SYS_Free_Memory);
|
|
}
|
|
|
|
return nextBlock;
|
|
}
|
|
|
|
/*** InitializeDocument
|
|
|
|
** Synopsis:
|
|
** void = InitializeDocument(void)
|
|
|
|
** Entry:
|
|
** none
|
|
|
|
** Returns:
|
|
** Nothing
|
|
|
|
** Description:
|
|
** Initialize global variables used by the module
|
|
*/
|
|
|
|
void
|
|
InitializeDocument(
|
|
void
|
|
)
|
|
{
|
|
UINT i;
|
|
for (i = 0; i < MAX_DOCUMENTS; i++) {
|
|
Docs[i].FirstView = -1;
|
|
}
|
|
for (i = 0; i < MAX_VIEWS; i++) {
|
|
Views[i].Doc = -1;
|
|
}
|
|
} /* InitializeDocument() */
|
|
|
|
/*** FirstLine
|
|
|
|
** Synopsis:
|
|
** bool = FirstLine(doc, pl, lineNb, pb)
|
|
|
|
** Entry:
|
|
** doc - Current document index
|
|
** pl - Place where the line is to be stored
|
|
** lineNb - pointer to line to be searched (incremented after search)
|
|
** pb - pointer to block pointer to be returned
|
|
|
|
** Returns:
|
|
|
|
** Description:
|
|
|
|
*/
|
|
|
|
BOOL FAR PASCAL
|
|
FirstLine (
|
|
int doc,
|
|
LPLINEREC *pl,
|
|
long *lineNb,
|
|
LPBLOCKDEF *pb
|
|
)
|
|
{
|
|
LPLINEREC stop;
|
|
int l;
|
|
LPDOCREC d = &Docs[doc];
|
|
|
|
// kcarlos
|
|
// BUGBUG
|
|
// This simple solves the symptom, but not the cause.
|
|
// Assert(*lineNb < d->NbLines);
|
|
if (*lineNb >= d->NbLines) {
|
|
*lineNb = d->NbLines - 1;
|
|
}
|
|
|
|
//Exit if line was the last loaded
|
|
if (*lineNb == d->CurrentLine) {
|
|
*pb = d->CurrentBlock;
|
|
*pl = (LPLINEREC)((LPSTR)(*pb)->Data + d->CurrentLineOffset);
|
|
(*lineNb)++;
|
|
|
|
//Expand line
|
|
ExpandTabs(pl);
|
|
pcl = *pl;
|
|
pcb = *pb;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//First evaluate from which block do we start
|
|
if (*lineNb <= (d->CurrentLine >> 1)) {
|
|
|
|
//Go up from first line
|
|
*pb = d->FirstBlock;
|
|
|
|
l = *lineNb;
|
|
*pl = (LPLINEREC)((*pb)->Data);
|
|
stop = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
|
|
|
|
while (l--) {
|
|
|
|
//Get next line
|
|
if (*pl < stop) {
|
|
|
|
//We are below the last line of the block,
|
|
// get just the next line
|
|
*pl = (LPLINEREC) ((LPSTR)*pl + (*pl)->Length);
|
|
|
|
} else {
|
|
|
|
//We are at the last line of the block. Load the next block
|
|
//and initialize line
|
|
Assert(*pl == stop);
|
|
Assert((*pb)->NextBlock != NULL);
|
|
*pb = (*pb)->NextBlock;
|
|
*pl = (LPLINEREC)((*pb)->Data);
|
|
stop = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
|
|
}
|
|
}
|
|
} else if (*lineNb <= d->CurrentLine) {
|
|
|
|
//Go down from current line
|
|
*pb = d->CurrentBlock;
|
|
|
|
l = d->CurrentLine - *lineNb;
|
|
*pl = (LPLINEREC)((*pb)->Data + d->CurrentLineOffset);
|
|
stop = (LPLINEREC)((*pb)->Data);
|
|
|
|
while (l--) {
|
|
if (*pl > stop) {
|
|
*pl = (LPLINEREC) ((LPSTR)*pl - (*pl)->PrevLength);
|
|
} else {
|
|
Assert(*pl == stop);
|
|
Assert((*pb)->PrevBlock != NULL);
|
|
*pb = (*pb)->PrevBlock;
|
|
*pl = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
|
|
stop = (LPLINEREC)((*pb)->Data);
|
|
}
|
|
}
|
|
// Kim Peterson
|
|
// BUGBUG: Original expression without parens was always true
|
|
// may be related to previous BUG
|
|
} else if (*lineNb <= (d->CurrentLine + ((d->NbLines-d->CurrentLine) >> 1))) {
|
|
|
|
//Go up from current line
|
|
*pb = d->CurrentBlock;
|
|
|
|
l = *lineNb - d->CurrentLine;
|
|
*pl = (LPLINEREC)((*pb)->Data + d->CurrentLineOffset);
|
|
stop = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
|
|
|
|
while (l--) {
|
|
if (*pl < stop) {
|
|
*pl = (LPLINEREC) ((LPSTR)*pl + (*pl)->Length);
|
|
} else {
|
|
Assert(*pl == stop);
|
|
Assert((*pb)->NextBlock != NULL);
|
|
*pb = (*pb)->NextBlock;
|
|
*pl = (LPLINEREC)((*pb)->Data);
|
|
stop = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
// Go down from last line
|
|
*pb = d->LastBlock;
|
|
l = d->NbLines - *lineNb -1;
|
|
*pl = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
|
|
stop = (LPLINEREC)((*pb)->Data);
|
|
|
|
while (l--) {
|
|
if (*pl > stop) {
|
|
*pl = (LPLINEREC) ((LPSTR)*pl - (*pl)->PrevLength);
|
|
} else {
|
|
Assert(*pl == stop);
|
|
Assert((*pb)->PrevBlock != NULL);
|
|
*pb = (*pb)->PrevBlock;
|
|
*pl = (LPLINEREC)((*pb)->Data + (*pb)->LastLineOffset);
|
|
stop = (LPLINEREC)((*pb)->Data);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Increment line count
|
|
(*lineNb)++;
|
|
|
|
//Expand line
|
|
ExpandTabs(pl);
|
|
pcl = *pl;
|
|
pcb = *pb;
|
|
|
|
return TRUE;
|
|
} /* FirstLine() */
|
|
|
|
/*** NextLine
|
|
|
|
** Synopsis:
|
|
** bool = NextLine(doc, pl, lineno, bl, pb)
|
|
|
|
** Entry:
|
|
|
|
** Returns:
|
|
|
|
** Description:
|
|
|
|
*/
|
|
|
|
BOOL FAR PASCAL
|
|
NextLine(
|
|
int doc,
|
|
LPLINEREC *pl,
|
|
long *lineNb,
|
|
LPBLOCKDEF *pb
|
|
)
|
|
{
|
|
LPDOCREC d = &Docs[doc];
|
|
|
|
if (*lineNb >= d->NbLines) {
|
|
*lineNb = LAST_LINE;
|
|
return TRUE;
|
|
}
|
|
Assert(*lineNb >= 0);
|
|
Assert(((LPSTR)*pl - (LPSTR)(*pb)->Data) <= (*pb)->LastLineOffset);
|
|
|
|
//Return next line
|
|
if ((LPSTR)*pl == (LPSTR)((*pb)->Data + (*pb)->LastLineOffset)) {
|
|
|
|
//We are at the last line of the block. Load next block
|
|
|
|
Assert((*pb)->NextBlock != NULL);
|
|
*pb = (*pb)->NextBlock;
|
|
|
|
//First line of next block
|
|
|
|
*pl = (LPLINEREC)((*pb)->Data);
|
|
|
|
AssertAligned( *pl );
|
|
}
|
|
else {
|
|
int i;
|
|
|
|
//Get pointer to next line
|
|
|
|
i = (*pl)->Length;
|
|
#ifdef ALIGN
|
|
i = (i + 3) & ~3;
|
|
#endif
|
|
|
|
*pl = (LPLINEREC)((LPSTR)*pl + i);
|
|
|
|
AssertAligned( *pl );
|
|
}
|
|
|
|
(*lineNb)++;
|
|
|
|
//Expand line
|
|
ExpandTabs(pl);
|
|
pcl = *pl;
|
|
pcb = *pb;
|
|
|
|
return TRUE;
|
|
} /* NextLine() */
|
|
|
|
/*** PreviousLine
|
|
|
|
*/
|
|
|
|
BOOL FAR PASCAL
|
|
PreviousLine(
|
|
int doc,
|
|
LPLINEREC *pl,
|
|
long lineNb,
|
|
LPBLOCKDEF *pb
|
|
)
|
|
{
|
|
LPDOCREC d = &Docs[doc];
|
|
|
|
if (lineNb <= 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
Assert(lineNb < d->NbLines);
|
|
Assert(((LPSTR)(*pl)-(LPSTR)(*pb)->Data) <= (*pb)->LastLineOffset);
|
|
|
|
//Return previous line
|
|
if ((LPSTR)(*pl) == (LPSTR)((*pb)->Data)) {
|
|
|
|
//We are at the first line of the block. Load previous block
|
|
Assert((*pb)->PrevBlock != NULL);
|
|
*pb = (*pb)->PrevBlock;
|
|
|
|
//Last line of previous block
|
|
*pl = (LPLINEREC)((LPSTR)(*pb)->Data + (*pb)->LastLineOffset);
|
|
} else {
|
|
|
|
//Get pointer to previous line
|
|
*pl = (LPLINEREC)((LPSTR)(*pl) - (*pl)->PrevLength);
|
|
}
|
|
|
|
//Expand line
|
|
ExpandTabs(pl);
|
|
pcl = *pl;
|
|
pcb = *pb;
|
|
|
|
return TRUE;
|
|
} /* PreviousLine() */
|
|
|
|
/*** CloseLine
|
|
|
|
** Synopsis:
|
|
** void = CloseLine()
|
|
|
|
** Entry:
|
|
|
|
** Returns:
|
|
|
|
** Description:
|
|
|
|
*/
|
|
|
|
void FAR PASCAL
|
|
CloseLine (
|
|
int doc,
|
|
LPLINEREC *pl,
|
|
long lineNb,
|
|
LPBLOCKDEF *pb
|
|
)
|
|
{
|
|
LPDOCREC d = &Docs[doc];
|
|
|
|
//Save current line infos
|
|
|
|
d->CurrentBlock = *pb;
|
|
d->CurrentLine = lineNb - 1;
|
|
d->CurrentLineOffset = (int) ((LPSTR)(*pl) - (LPSTR)(*pb)->Data);
|
|
|
|
return;
|
|
} /* CloseLine() */
|
|
|
|
/*** FirstNonBlank
|
|
|
|
*/
|
|
|
|
int FAR PASCAL
|
|
FirstNonBlank(
|
|
int doc,
|
|
long line
|
|
)
|
|
{
|
|
LPLINEREC pl;
|
|
LPBLOCKDEF pb;
|
|
register int x;
|
|
|
|
if (!FirstLine(doc, &pl, &line, &pb)) {
|
|
return FALSE;
|
|
}
|
|
CloseLine(doc, &pl, line, &pb);
|
|
|
|
x = 0;
|
|
while (x < elLen && el[x] == ' ') {
|
|
x++;
|
|
}
|
|
if (x >= (elLen)) {
|
|
x = 0;
|
|
}
|
|
|
|
return x;
|
|
} /* FirstNonBlank() */
|
|
|
|
void FAR PASCAL
|
|
CheckSyntax(
|
|
int doc
|
|
)
|
|
{
|
|
LPDOCREC d = &Docs[doc];
|
|
LPBLOCKDEF pCurBlock;
|
|
LPLINEREC pCurLine;
|
|
char prevCh;
|
|
char ch;
|
|
int i;
|
|
long line = d->lineTop;
|
|
int savedLineBottom;
|
|
int curStatus, prevStatus;
|
|
int curLevel, prevLevel;
|
|
|
|
if (d->lineBottom >= d->NbLines) {
|
|
d->lineBottom--;
|
|
}
|
|
Assert(d->lineBottom < d->NbLines);
|
|
savedLineBottom = d->lineBottom;
|
|
|
|
//Load previous line to get previous line status
|
|
|
|
if (line > 0) {
|
|
line--;
|
|
if (!FirstLine(doc, &pCurLine, &line, &pCurBlock)) {
|
|
return;
|
|
}
|
|
|
|
//Compute previous line comment level and status
|
|
|
|
prevStatus = pCurLine->Status & COMMENT_LINE;
|
|
if (prevStatus == COMMENT_LINE) {
|
|
prevLevel = 1;
|
|
} else {
|
|
|
|
for (i = 0, prevCh = ' ', prevLevel = 0;
|
|
i < pCurLine->Length - LHD; i++) {
|
|
if (IsDBCSLeadByte(pCurLine->Text[i]) && pCurLine->Text[i+1]) {
|
|
prevCh = ' ';
|
|
i++;
|
|
continue;
|
|
}
|
|
ch = pCurLine->Text[i];
|
|
if (prevCh == '/' && ch == '*') {
|
|
prevLevel++;
|
|
}
|
|
if (prevCh == '*' && ch == '/') {
|
|
prevLevel = -1;
|
|
}
|
|
prevCh = ch;
|
|
}
|
|
}
|
|
if (!NextLine(doc, &pCurLine, &line, &pCurBlock)) {
|
|
return;
|
|
}
|
|
|
|
} else {
|
|
Assert(line == 0);
|
|
prevStatus = prevLevel = 0;
|
|
if (!FirstLine(doc, &pCurLine, &line, &pCurBlock)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Scan now every line between lineTop and lineBottom and possibly
|
|
//extend lineBottom if needed
|
|
|
|
while (TRUE) {
|
|
|
|
//Compute current line comment level and status
|
|
|
|
curStatus = pCurLine->Status & COMMENT_LINE;
|
|
for (i = 0, prevCh = ' ', curLevel = 0; i < pCurLine->Length - LHD;
|
|
i++) {
|
|
if (IsDBCSLeadByte(pCurLine->Text[i]) && pCurLine->Text[i+1]) {
|
|
prevCh = ' ';
|
|
i++;
|
|
continue;
|
|
}
|
|
ch = pCurLine->Text[i];
|
|
|
|
if (prevCh == '/' && ch == '*') {
|
|
if (i <= 1 || pCurLine->Text[i-2] != '/') {
|
|
curLevel++;
|
|
}
|
|
}
|
|
|
|
if (prevCh == '*' && ch == '/') {
|
|
curLevel = -1;
|
|
}
|
|
prevCh = ch;
|
|
}
|
|
|
|
//Impossible combination
|
|
|
|
Assert(prevStatus == 0 || prevLevel >= 0);
|
|
|
|
//Set or reset multiline status
|
|
|
|
if (prevStatus == 0 && prevLevel <= 0) {
|
|
if (curLevel > 0) {
|
|
SET(pCurLine->Status, COMMENT_LINE);
|
|
} else {
|
|
RESET(pCurLine->Status, COMMENT_LINE);
|
|
}
|
|
|
|
} else if (curLevel >= 0) {
|
|
|
|
SET(pCurLine->Status, COMMENT_LINE);
|
|
|
|
} else {
|
|
|
|
RESET(pCurLine->Status, COMMENT_LINE);
|
|
|
|
}
|
|
|
|
//Current line info is now previous
|
|
|
|
prevStatus = pCurLine->Status & COMMENT_LINE;
|
|
|
|
//Check stop conditions
|
|
|
|
if (line > d->lineBottom) {
|
|
if (curStatus == prevStatus) {
|
|
break;
|
|
} else {
|
|
d->lineBottom++;
|
|
}
|
|
}
|
|
|
|
//Current level is previous one
|
|
|
|
prevLevel = curLevel;
|
|
|
|
//Stop if at end of doc
|
|
|
|
if (line >= d->NbLines) {
|
|
break;
|
|
}
|
|
|
|
//Return next line
|
|
|
|
if ((LPSTR)pCurLine ==
|
|
(LPSTR)(pCurBlock->Data + pCurBlock->LastLineOffset)) {
|
|
pCurBlock = pCurBlock->NextBlock;
|
|
pCurLine = (LPLINEREC)(pCurBlock->Data);
|
|
}
|
|
else {
|
|
pCurLine = (LPLINEREC)((LPSTR)pCurLine + pCurLine->Length);
|
|
}
|
|
line++;
|
|
|
|
}
|
|
|
|
if (!syntaxColors) {
|
|
d->lineBottom = savedLineBottom;
|
|
}
|
|
|
|
CloseLine(doc, &pCurLine, line, &pCurBlock);
|
|
|
|
return;
|
|
} /* CheckSyntax() */
|
|
|
|
/*** ModifiedDoc
|
|
|
|
** Synopsis:
|
|
** void = ModifiedDoc(doc)
|
|
|
|
** Entry:
|
|
** doc - index of document which has been modified
|
|
|
|
** Returns:
|
|
** Nothing:
|
|
|
|
** Description:
|
|
** Marks the document as having been modified and updates the
|
|
** title if it has not previously been modified
|
|
|
|
*/
|
|
|
|
void NEAR PASCAL
|
|
ModifiedDoc(
|
|
int doc
|
|
)
|
|
{
|
|
|
|
//Document is modified
|
|
if (!Docs[doc].ismodified) {
|
|
Docs[doc].ismodified = TRUE;
|
|
RefreshWindowsTitle(doc);
|
|
}
|
|
return;
|
|
} /* ModifiedDoc() */
|
|
|
|
void FAR PASCAL
|
|
ExpandTabs(
|
|
LPLINEREC *pl
|
|
)
|
|
{
|
|
register int i = 0;
|
|
register int j = 0;
|
|
int len = (*pl)->Length - LHD;
|
|
LPSTR pc = (*pl)->Text;
|
|
|
|
memset(el, ' ', MAX_USER_LINE * 2);
|
|
while (i < len) {
|
|
if (IsDBCSLeadByte((BYTE)pc[i])) {
|
|
el[j++] = pc[i++];
|
|
el[j++] = pc[i++];
|
|
} else
|
|
if (pc[i] == TAB) {
|
|
j += g_contGlobalPreferences_WkSp.m_nTabSize - (j % g_contGlobalPreferences_WkSp.m_nTabSize);
|
|
i++;
|
|
}
|
|
else {
|
|
el[j++] = pc[i++];
|
|
}
|
|
}
|
|
|
|
Assert(j <= MAX_USER_LINE);
|
|
elLen = j;
|
|
}
|
|
|
|
int FAR PASCAL
|
|
AlignToTabs(
|
|
int editCol,
|
|
int len,
|
|
LPSTR pc
|
|
)
|
|
{
|
|
register int col, realCol;
|
|
|
|
col = realCol = 0;
|
|
while (realCol < editCol) {
|
|
|
|
if (col >= len) {
|
|
col += (editCol - realCol);
|
|
break;
|
|
}
|
|
if (IsDBCSLeadByte(pc[col])) {
|
|
realCol += 2;
|
|
} else if (pc[col] == TAB) {
|
|
realCol += g_contGlobalPreferences_WkSp.m_nTabSize - (realCol % g_contGlobalPreferences_WkSp.m_nTabSize);
|
|
} else {
|
|
realCol++;
|
|
}
|
|
|
|
if (realCol <= editCol) {
|
|
if (IsDBCSLeadByte(pc[col])) {
|
|
col += 2;
|
|
} else {
|
|
col++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return col;
|
|
} /* AlignToTabs() */
|
|
|
|
BOOL NEAR PASCAL
|
|
FindInsertSpace(
|
|
int doc,
|
|
LPBLOCKDEF *pCurBlock,
|
|
LPLINEREC *pCurLine,
|
|
LPSTR pBlockEnd,
|
|
int sizeExpansion
|
|
)
|
|
{
|
|
LPBLOCKDEF pNextBlock;
|
|
LPLINEREC pNextLineEnd, pTmpLine = *pCurLine;
|
|
int moveSize;
|
|
BOOL newBlockCreated = FALSE;
|
|
int curLineOffset;
|
|
|
|
//Check not to move first line of current block in next block
|
|
|
|
if ((LPSTR)*pCurLine - (LPSTR)(*pCurBlock)->Data == 0) {
|
|
pTmpLine = (LPLINEREC)((LPSTR) pTmpLine + pTmpLine->Length);
|
|
}
|
|
|
|
moveSize = (int) (pBlockEnd - (LPSTR)pTmpLine);
|
|
|
|
if ((*pCurBlock)->NextBlock == NULL) {
|
|
|
|
//Create a new block
|
|
|
|
if (!AllocateBlock(*pCurBlock, NULL, &pNextBlock)) {
|
|
return FALSE;
|
|
}
|
|
newBlockCreated = TRUE;
|
|
Docs[doc].LastBlock = pNextBlock;
|
|
|
|
pNextLineEnd = (LPLINEREC)pNextBlock->Data;
|
|
}
|
|
else {
|
|
|
|
//Before allocating a new block, look into next block
|
|
//to see if we have space to insert chars and to move in
|
|
//current line and lines after
|
|
|
|
pNextBlock = (*pCurBlock)->NextBlock;
|
|
|
|
pNextLineEnd = (LPLINEREC)(pNextBlock->Data
|
|
+ pNextBlock->LastLineOffset);
|
|
pNextLineEnd = (LPLINEREC)((LPSTR)pNextLineEnd
|
|
+ pNextLineEnd->Length);
|
|
|
|
if (moveSize + sizeExpansion > (int)(BLOCK_SIZE
|
|
- ((LPSTR)pNextLineEnd - (LPSTR)pNextBlock->Data))) {
|
|
|
|
LPBLOCKDEF pNewBlock;
|
|
|
|
//We have not enough space in next block, so insert
|
|
//a new created block
|
|
|
|
if (!AllocateBlock(*pCurBlock, pNextBlock, &pNewBlock)) {
|
|
return FALSE;
|
|
}
|
|
|
|
newBlockCreated = TRUE;
|
|
pNextBlock->PrevBlock = pNewBlock;
|
|
|
|
//Next block is new block
|
|
|
|
pNextBlock = pNewBlock;
|
|
pNextLineEnd = (LPLINEREC)pNextBlock->Data;
|
|
}
|
|
}
|
|
|
|
curLineOffset = (int) ((LPSTR)pTmpLine - (LPSTR)(*pCurBlock)->Data);
|
|
|
|
//Link current block to next
|
|
(*pCurBlock)->NextBlock = pNextBlock;
|
|
|
|
//Shift right next blocks chars
|
|
|
|
memmove(pNextBlock->Data + moveSize,
|
|
pNextBlock->Data,
|
|
(size_t) ((LPSTR)pNextLineEnd - (LPSTR)pNextBlock->Data));
|
|
|
|
//And copy chars from current block to next block
|
|
|
|
memmove(pNextBlock->Data, (LPSTR)pTmpLine, moveSize);
|
|
|
|
Assert(moveSize >= LHD);
|
|
|
|
//Rework pointers
|
|
|
|
if (newBlockCreated) {
|
|
pNextBlock->LastLineOffset = (*pCurBlock)->LastLineOffset -
|
|
curLineOffset;
|
|
} else {
|
|
pNextBlock->LastLineOffset += moveSize;
|
|
}
|
|
|
|
|
|
(*pCurBlock)->LastLineOffset = curLineOffset - pTmpLine->PrevLength;
|
|
|
|
//Current block is next block if current line is not block first line
|
|
|
|
if ((LPSTR)*pCurLine != (LPSTR)(*pCurBlock)->Data) {
|
|
|
|
//Cur line is now next block first line
|
|
|
|
*pCurLine = (LPLINEREC)pNextBlock->Data;
|
|
|
|
//Next block becomes current block
|
|
|
|
*pCurBlock = pNextBlock;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
} /* FindInsertSpace() */
|
|
|
|
//When your file has an Undo buffer, be sure to set the undo/redo
|
|
//engine to UNDO before inserting chars, otherwise they will be stored
|
|
//in the REDO buffer
|
|
|
|
BOOL FAR PASCAL
|
|
InsertBlock(
|
|
int doc,
|
|
int col,
|
|
long line,
|
|
int size,
|
|
LPSTR buf
|
|
)
|
|
{
|
|
LPLINEREC pCurLine = NULL, pLastLine = NULL, pNextLine = NULL;
|
|
LPBLOCKDEF pCurBlock, pNextBlock;
|
|
LPSTR charsBegin = NULL;
|
|
LPSTR charsEnd = NULL;
|
|
LPSTR pBlockEnd = NULL;
|
|
LPSTR start = NULL;
|
|
int sizeExpansion, newLineLen, charsSize;
|
|
int extraBlanks, lineLen;
|
|
int nextLineLength;
|
|
int freeBlockSize;
|
|
BOOL addOneLine;
|
|
LPDOCREC d = &Docs[doc];
|
|
UNDOREDOREC pUndoRedo;
|
|
int startLine = line;
|
|
BOOL firstLine = TRUE;
|
|
int nbLines = d->NbLines;
|
|
char lineBuf[MAX_LINE_SIZE];
|
|
int expandedLen;
|
|
|
|
//Beep and exit if file is ReadOnly
|
|
|
|
if (d->readOnly || ROREGION(d,col,line)) {
|
|
ReadOnlyBeep();
|
|
return FALSE;
|
|
} else {
|
|
// Clear error status
|
|
QueryReadOnlyError();
|
|
}
|
|
|
|
if (size == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
editorIsCritical = TRUE;
|
|
Assert(line < d->NbLines);
|
|
col = min(col, MAX_USER_LINE);
|
|
|
|
d->lineTop = d->lineBottom = line;
|
|
|
|
//Get information from line where we insert
|
|
|
|
if (!FirstLine(doc, &pCurLine, &line, &pCurBlock)) {
|
|
goto errRet;
|
|
}
|
|
|
|
//Align column respecting tabs
|
|
|
|
if (playingRecords) {
|
|
expandedLen = ConvertPosX(col);
|
|
} else {
|
|
expandedLen = col;
|
|
col = AlignToTabs(expandedLen, pCurLine->Length - LHD,
|
|
(LPSTR)pCurLine->Text);
|
|
}
|
|
Assert(col >= 0);
|
|
|
|
//Open an edit record
|
|
|
|
if (!OpenRec(doc, DELETESTREAM, col, line - 1)) {
|
|
goto errRet;
|
|
}
|
|
|
|
//Initialize rec buffer ptr if we will read chars from undo/redo record
|
|
|
|
if (buf) {
|
|
charsBegin = charsEnd = buf;
|
|
} else if (d->recType == REC_UNDO) {
|
|
pUndoRedo = d->redo;
|
|
} else if (d->recType == REC_REDO) {
|
|
pUndoRedo = d->undo;
|
|
} else {
|
|
Assert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
//Insert until buffer empty
|
|
|
|
while (size) {
|
|
|
|
if (buf != NULL) {
|
|
|
|
//Extract chars from supplied buf until EOF, EOL or end of buffer
|
|
|
|
while (size && *charsEnd != CR) {
|
|
if (*charsEnd == TAB) {
|
|
expandedLen += g_contGlobalPreferences_WkSp.m_nTabSize - (expandedLen % g_contGlobalPreferences_WkSp.m_nTabSize);
|
|
} else {
|
|
expandedLen++;
|
|
}
|
|
charsEnd++;
|
|
size--;
|
|
}
|
|
} else {
|
|
|
|
//Extract from undo/redo records buffer a line, or the text if
|
|
//no CR+LF found (no overflow protection needed, by definition
|
|
//lines in undo/redo buf are never beyond maximum)
|
|
|
|
charsBegin = (LPSTR)lineBuf;
|
|
ReadLineFromBuf(&pUndoRedo, lineBuf, &size, &expandedLen,&charsEnd);
|
|
}
|
|
|
|
if (size == 0) {
|
|
addOneLine = FALSE;
|
|
} else if ( (addOneLine = (*charsEnd == CR)) &&
|
|
d->NbLines >= MAX_LINE_NUMBER) {
|
|
ErrorBox(ERR_Too_Many_Lines);
|
|
goto errRet;
|
|
}
|
|
|
|
lineLen = pCurLine->Length - LHD;
|
|
|
|
//We are in a line, check if we are after last char
|
|
|
|
extraBlanks = max (col - lineLen, 0);
|
|
|
|
//Compute new line len whether chars end with a CR or not
|
|
|
|
newLineLen =
|
|
sizeExpansion =
|
|
charsSize = (int) (charsEnd - charsBegin);
|
|
if (addOneLine) {
|
|
newLineLen += col + LHD;
|
|
sizeExpansion += LHD;
|
|
|
|
//Adjust startline for breakpoints and error handling
|
|
|
|
if (firstLine && col > 0) {
|
|
startLine++;
|
|
}
|
|
} else {
|
|
|
|
int newCol = col;
|
|
|
|
newLineLen += (pCurLine->Length + extraBlanks);
|
|
|
|
//Now see if what we insert in current line will not expand
|
|
//line len beyond maximum
|
|
|
|
if (extraBlanks == 0) {
|
|
|
|
while (newCol < lineLen) {
|
|
if (pCurLine->Text[newCol] == TAB) {
|
|
expandedLen += g_contGlobalPreferences_WkSp.m_nTabSize - (expandedLen % g_contGlobalPreferences_WkSp.m_nTabSize);
|
|
} else {
|
|
expandedLen++;
|
|
}
|
|
newCol++;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//Check new line length
|
|
|
|
if (expandedLen > MAX_USER_LINE) {
|
|
ErrorBox(ERR_Line_Too_Long);
|
|
goto errRet;
|
|
}
|
|
|
|
firstLine = FALSE;
|
|
|
|
//Possibly add extraBlanks to buffer size expansion
|
|
|
|
sizeExpansion += extraBlanks;
|
|
|
|
//Sorry but recursivity eats stack
|
|
|
|
reDoIt:
|
|
|
|
//Keep a pointer on last line of block
|
|
|
|
pLastLine = (LPLINEREC)((LPSTR)pCurBlock->Data +
|
|
pCurBlock->LastLineOffset);
|
|
|
|
//Check block size
|
|
|
|
freeBlockSize = BLOCK_SIZE - pCurBlock->LastLineOffset -
|
|
pLastLine->Length;
|
|
|
|
//Compute very end of block
|
|
|
|
pBlockEnd = (LPSTR)pLastLine + pLastLine->Length;
|
|
Assert(pBlockEnd <= (LPSTR)(pCurBlock)->Data + BLOCK_SIZE);
|
|
|
|
//Do we have room in current block
|
|
|
|
if (freeBlockSize >= sizeExpansion) {
|
|
|
|
//We have space to insert chars in current block
|
|
|
|
//If we add a line, first update next line length
|
|
|
|
if (addOneLine) {
|
|
nextLineLength = pCurLine->Length - (BYTE)newLineLen
|
|
+ (BYTE)sizeExpansion;
|
|
pNextLine = (LPLINEREC)((LPSTR)pCurLine + pCurLine->Length);
|
|
|
|
//Write future line length in next line prev length field,
|
|
//if line is the last of block, load next block
|
|
|
|
if (((LPSTR)pNextLine - (LPSTR)pCurBlock->Data)
|
|
<= pCurBlock->LastLineOffset) {
|
|
pNextLine->PrevLength = (BYTE)nextLineLength;
|
|
} else {
|
|
|
|
//Write header only if there is a line after
|
|
|
|
pNextBlock = pCurBlock->NextBlock;
|
|
if (pNextBlock != NULL) {
|
|
|
|
//Set next line pointer in first line of Next Block
|
|
|
|
pNextLine = (LPLINEREC)pNextBlock->Data;
|
|
pNextLine->PrevLength = (BYTE)nextLineLength;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Compute the position where to insert
|
|
|
|
start = (LPSTR)(pCurLine) + min(col + LHD, (int)pCurLine->Length);
|
|
|
|
//Shift right part
|
|
|
|
memmove(start + sizeExpansion, start, (size_t) (pBlockEnd - start));
|
|
|
|
//First insert the extraBlanks if any
|
|
|
|
memset(start, ' ', extraBlanks);
|
|
start += extraBlanks;
|
|
|
|
//Then insert chars
|
|
|
|
memmove(start, charsBegin, charsSize);
|
|
|
|
//Possibly adjust block last Line Offset
|
|
// - We inserted before last line, propagate line extension
|
|
// - We inserted at last line, propagate line extension
|
|
// only if a new line is inserted
|
|
|
|
if (pCurLine < pLastLine) {
|
|
pCurBlock->LastLineOffset +=sizeExpansion;
|
|
} else if (pCurLine == pLastLine) {
|
|
if (addOneLine) {
|
|
pCurBlock->LastLineOffset +=newLineLen;
|
|
}
|
|
} else {
|
|
Assert(FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
pNextLine = (LPLINEREC)((LPSTR)pCurLine + newLineLen);
|
|
Assert((LPSTR)pNextLine - (LPSTR)pCurBlock->Data <= BLOCK_SIZE);
|
|
|
|
//Write next line header, in block or in next block
|
|
|
|
if (((LPSTR)pCurLine - (LPSTR)pCurBlock->Data)
|
|
== pCurBlock->LastLineOffset) {
|
|
|
|
//Write header only if there is a line after
|
|
|
|
pNextBlock = pCurBlock->NextBlock;
|
|
if (pNextBlock != NULL) {
|
|
|
|
//Set next line pointer to first line of Next Block
|
|
|
|
pNextLine = (LPLINEREC)pNextBlock->Data;
|
|
pNextLine->PrevLength = (BYTE)newLineLen;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
//Not at last line of block, Set next line previous line
|
|
|
|
pNextLine->PrevLength = (BYTE)newLineLen;
|
|
|
|
//If we insert a new line : Change next line length and
|
|
//set current line status to 0 if it's a brand new line
|
|
|
|
if (addOneLine) {
|
|
pNextLine->Length = (BYTE)nextLineLength;
|
|
if (col == 0) {
|
|
|
|
//Propagate status to next line
|
|
|
|
pNextLine->Status = pCurLine->Status;
|
|
pCurLine->Status = 0;
|
|
} else {
|
|
|
|
//Set next line status to 0
|
|
|
|
AssertAligned(pNextLine);
|
|
pNextLine->Status = 0;
|
|
}
|
|
}
|
|
|
|
//Set cur line length
|
|
|
|
pCurLine->Length = (BYTE)newLineLen;
|
|
|
|
} else {
|
|
|
|
if (!FindInsertSpace(doc, &pCurBlock, &pCurLine, pBlockEnd,
|
|
sizeExpansion)) {
|
|
goto errRet;
|
|
}
|
|
|
|
//Insert chars in next block
|
|
|
|
goto reDoIt;
|
|
|
|
}
|
|
|
|
if (addOneLine) {
|
|
charsEnd += 2;
|
|
size -=2;
|
|
d->lineBottom++;
|
|
d->NbLines++;
|
|
expandedLen = 0;
|
|
}
|
|
|
|
charsBegin = charsEnd;
|
|
col = 0;
|
|
if (size || addOneLine) {
|
|
pCurLine = pNextLine;
|
|
}
|
|
|
|
}
|
|
|
|
//Close undo/redo record
|
|
|
|
CloseRec(doc, max(0, (int)((start - (LPSTR)pCurLine->Text) + charsSize)),
|
|
d->lineBottom, TRUE);
|
|
|
|
//Inform breakpoints and errors handlers of what we did in the text
|
|
//(only if it's a real insertion)
|
|
|
|
if (d->recType != REC_STOPPED && d->NbLines > nbLines) {
|
|
AdjustDebugLines(doc, startLine, d->NbLines - nbLines, TRUE);
|
|
}
|
|
|
|
CloseLine(doc, &pCurLine, d->lineBottom + 1, &pCurBlock);
|
|
|
|
//Document is modified
|
|
|
|
ModifiedDoc(doc);
|
|
|
|
//If there was nothing to record, tells the system that now we have
|
|
//something in buffer
|
|
|
|
if (d->playCount == REC_CANNOTUNDO) {
|
|
d->playCount = 0;
|
|
}
|
|
|
|
if (d->language != NO_LANGUAGE) {
|
|
CheckSyntax(doc);
|
|
}
|
|
|
|
editorIsCritical = FALSE;
|
|
|
|
return TRUE;
|
|
|
|
errRet: {
|
|
|
|
//Cancel recording
|
|
|
|
CloseRec(doc, 0, 0, FALSE);
|
|
|
|
//Try to resynchronize system on first line of document
|
|
|
|
pCurLine = (LPLINEREC)d->FirstBlock->Data;
|
|
CloseLine(doc, &pCurLine, 1, &d->FirstBlock);
|
|
|
|
editorIsCritical = FALSE;
|
|
return FALSE;
|
|
}
|
|
|
|
} /* InsertBlock() */
|
|
|
|
BOOL NEAR PASCAL
|
|
CancelDelete(
|
|
int doc
|
|
)
|
|
{
|
|
|
|
if (QuestionBox(ERR_UndoRedoBufferTooSmall, MB_YESNO) == IDYES) {
|
|
|
|
//Cancel this record and stop recording for this action
|
|
CloseRec(doc, 0, 0, FALSE);
|
|
Docs[doc].recType = REC_HADOVERFLOW;
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//Delete characters on a single line, pointers are unaffected
|
|
BOOL NEAR PASCAL
|
|
PartialLineDelete(
|
|
int doc,
|
|
LPBLOCKDEF *pCurBlock,
|
|
LPLINEREC *pCurLine,
|
|
int * totalSize,
|
|
int col1,
|
|
int col2,
|
|
BOOL moveToNextLine
|
|
)
|
|
{
|
|
int moveSize;
|
|
LPLINEREC pNextLine = NULL, pLastLine;
|
|
LPBLOCKDEF pNextBlock = *pCurBlock;
|
|
|
|
//Adjust col1 and col2 inside the line
|
|
col1 = min(col1, (*pCurLine)->Length - LHD);
|
|
col2 = min(col2, (*pCurLine)->Length - LHD);
|
|
moveSize = col2 - col1;
|
|
|
|
//Send deleted chars to record buffer and roll back
|
|
//if buffer full
|
|
if (!AppendToRec(doc, (*pCurLine)->Text + col1, moveSize, FALSE, totalSize))
|
|
if (CancelDelete(doc))
|
|
return FALSE;
|
|
|
|
pLastLine = (LPLINEREC)((*pCurBlock)->Data + (*pCurBlock)->LastLineOffset);
|
|
|
|
//Move chars
|
|
if (moveSize > 0) {
|
|
memmove((LPSTR)(*pCurLine)->Text + col1,
|
|
(LPSTR)(*pCurLine)->Text + col2,
|
|
(size_t) ((LPSTR)pLastLine + pLastLine->Length
|
|
- ((LPSTR)(*pCurLine)->Text + col2)));
|
|
}
|
|
|
|
//Update current line length
|
|
(*pCurLine)->Length -= (BYTE) moveSize;
|
|
|
|
//Update last line offset and next line previous length (in current
|
|
//or in next block)
|
|
if ((*pCurLine) == pLastLine) {
|
|
|
|
//Next line is in next block
|
|
pNextBlock = (*pCurBlock)->NextBlock;
|
|
if (pNextBlock != NULL) {
|
|
|
|
//Set next line pointer to first line of Next Block and
|
|
//update previous length
|
|
pNextLine = (LPLINEREC)pNextBlock->Data;
|
|
pNextLine->PrevLength -= (BYTE) moveSize;
|
|
|
|
}
|
|
|
|
}
|
|
else {
|
|
|
|
(*pCurBlock)->LastLineOffset -= moveSize;
|
|
pNextLine = (LPLINEREC)((LPSTR)*pCurLine + (*pCurLine)->Length);
|
|
pNextLine->PrevLength -= (BYTE) moveSize;
|
|
}
|
|
|
|
if (moveToNextLine) {
|
|
*pCurLine = pNextLine;
|
|
*pCurBlock = pNextBlock;
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
//Deletes the pCurLine in the pCurBlock and adjust them to the
|
|
//next line, if the line was the last line of the block, the block
|
|
//is deleted
|
|
BOOL NEAR PASCAL
|
|
FullLineDelete(
|
|
int doc,
|
|
LPBLOCKDEF *pCurBlock,
|
|
LPLINEREC *pCurLine,
|
|
int *totalSize
|
|
)
|
|
{
|
|
LPLINEREC pLastLine;
|
|
BYTE prevLineLen = (*pCurLine)->PrevLength;
|
|
|
|
//First send deleted chars to record buffer
|
|
if (!AppendToRec(doc, (*pCurLine)->Text, (*pCurLine)->Length - LHD, TRUE,
|
|
totalSize)) {
|
|
if (CancelDelete(doc)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//Compute lastLine and nextLine in block
|
|
pLastLine = (LPLINEREC)((*pCurBlock)->Data + (*pCurBlock)->LastLineOffset);
|
|
if (*pCurLine == pLastLine) {
|
|
|
|
//If line is also the first line of block, delete the block
|
|
if (*pCurLine == (LPLINEREC)(*pCurBlock)->Data) {
|
|
|
|
Assert((*pCurBlock)->LastLineOffset == 0);
|
|
|
|
//Document may be empty, reinitialize first block
|
|
if ((*pCurBlock)->PrevBlock == NULL &&
|
|
(*pCurBlock)->NextBlock == NULL) {
|
|
(*pCurLine)->Length = LHD;
|
|
(*pCurLine)->Status = 0;
|
|
(*pCurBlock)->LastLineOffset = 0;
|
|
Docs[doc].NbLines = 1;
|
|
Assert((*pCurLine)->PrevLength == 0);
|
|
Assert(Docs[doc].FirstBlock == *pCurBlock);
|
|
Assert(Docs[doc].LastBlock == *pCurBlock);
|
|
return TRUE;
|
|
} else {
|
|
|
|
//Delete block, and set current block to next block
|
|
*pCurBlock = FreeBlock(doc, *pCurBlock);
|
|
}
|
|
|
|
} else {
|
|
|
|
//Adjust the current block last offset
|
|
(*pCurBlock)->LastLineOffset -= prevLineLen;
|
|
Assert((*pCurBlock)->LastLineOffset >= 0);
|
|
|
|
//Set curBlock to next block
|
|
*pCurBlock = (*pCurBlock)->NextBlock;
|
|
|
|
}
|
|
|
|
//Set next line to first line of block
|
|
if (*pCurBlock != NULL) {
|
|
*pCurLine = (LPLINEREC)(*pCurBlock)->Data;
|
|
}
|
|
|
|
} else {
|
|
|
|
LPLINEREC pNextLine = (LPLINEREC)((LPSTR)*pCurLine +
|
|
(*pCurLine)->Length);
|
|
|
|
//Adjust block last offset
|
|
(*pCurBlock)->LastLineOffset -= (*pCurLine)->Length;
|
|
Assert((*pCurBlock)->LastLineOffset >= 0);
|
|
|
|
//Now shift left rest of block chars
|
|
memmove(*pCurLine,
|
|
(LPSTR)pNextLine,
|
|
(size_t) ((LPSTR)pLastLine + pLastLine->Length - (LPSTR)pNextLine));
|
|
}
|
|
|
|
//If next line exist, change it's header, otherwise resynchronise
|
|
if (*pCurBlock != NULL) {
|
|
(*pCurLine)->PrevLength = prevLineLen;
|
|
} else {
|
|
*pCurBlock = Docs[doc].LastBlock;
|
|
*pCurLine = (LPLINEREC)((*pCurBlock)->Data +
|
|
(*pCurBlock)->LastLineOffset);
|
|
}
|
|
|
|
Docs[doc].NbLines--;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//Join the current line to the preceding, pad blanks if necessary
|
|
BOOL NEAR PASCAL
|
|
JoinLines(
|
|
int doc,
|
|
LPBLOCKDEF pCurBlock,
|
|
LPLINEREC pCurLine,
|
|
int *totalSize,
|
|
int lineNb,
|
|
int col1
|
|
)
|
|
{
|
|
LPDOCREC d = &Docs[doc];
|
|
WORD undoRedoState = d->recType;
|
|
int limit;
|
|
LPLINEREC pPrevLine;
|
|
char tmpLine[MAX_USER_LINE + 2]; //2 = Possible CR+LF
|
|
WORD status;
|
|
BOOL language = d->language;
|
|
|
|
//First compose the resulting new line
|
|
if (pCurLine == (LPLINEREC)pCurBlock->Data) {
|
|
pCurBlock = pCurBlock->PrevBlock;
|
|
Assert(pCurBlock != NULL);
|
|
pPrevLine = (LPLINEREC)((LPSTR)pCurBlock->Data +
|
|
pCurBlock->LastLineOffset);
|
|
} else {
|
|
pPrevLine = (LPLINEREC)((LPSTR)pCurLine - pCurLine->PrevLength);
|
|
}
|
|
|
|
//Save portion of first line and save status (unless all first line
|
|
//is deleted)
|
|
if (col1 >0) {
|
|
status = pPrevLine->Status;
|
|
} else {
|
|
status = 0;
|
|
}
|
|
|
|
limit = min(col1, pPrevLine->Length - LHD);
|
|
memmove((LPSTR)tmpLine, (LPSTR)pPrevLine->Text, limit);
|
|
|
|
//We may have now to pad line with blanks
|
|
for(; limit < MAX_USER_LINE && limit < col1; limit++) {
|
|
tmpLine[limit] = ' ';
|
|
}
|
|
|
|
//Append now current line
|
|
if (limit + pCurLine->Length - LHD <= MAX_USER_LINE) {
|
|
memmove((LPSTR)tmpLine + limit, (LPSTR)pCurLine->Text,
|
|
pCurLine->Length - LHD);
|
|
limit += pCurLine->Length - LHD;
|
|
}
|
|
else {
|
|
ErrorBox(ERR_Line_Too_Long);
|
|
return FALSE;
|
|
}
|
|
|
|
//Check line overflow
|
|
if (limit >= MAX_USER_LINE) {
|
|
ErrorBox(ERR_Line_Too_Long);
|
|
return FALSE;
|
|
}
|
|
|
|
//Append a CR+LF
|
|
memmove(tmpLine + limit, CrLf, 2);
|
|
limit += 2;
|
|
|
|
//Insert the new one
|
|
CloseLine(doc, &pPrevLine, lineNb--, &pCurBlock);
|
|
|
|
//Stop Recording
|
|
d->recType = REC_STOPPED;
|
|
|
|
d->language = NO_LANGUAGE; //To avoid syntax check
|
|
if (!InsertBlock(doc, 0, lineNb++, limit, (LPSTR)tmpLine)) {
|
|
return FALSE;
|
|
}
|
|
d->language = (WORD) language;
|
|
|
|
//Resynchronize to next line
|
|
pCurBlock = d->CurrentBlock;
|
|
pCurLine = (LPLINEREC)((LPSTR)pCurBlock->Data + d->CurrentLineOffset);
|
|
pCurLine->Status = 0;
|
|
|
|
//Write saved status in previous line (the one just inserted)
|
|
if (pCurLine == (LPLINEREC)pCurBlock->Data) {
|
|
|
|
LPBLOCKDEF pPrevBlock = pCurBlock->PrevBlock;
|
|
if (pPrevBlock != NULL) {
|
|
((LPLINEREC)((LPSTR)pPrevBlock->Data + pPrevBlock->LastLineOffset)) ->Status = status;
|
|
}
|
|
} else {
|
|
((LPLINEREC)((LPSTR)pCurLine - pCurLine->PrevLength))->Status = status;
|
|
}
|
|
|
|
//Delete now the 2 lines
|
|
if (!FullLineDelete(doc, &pCurBlock, &pCurLine, totalSize)) {
|
|
return FALSE;
|
|
}
|
|
if (!FullLineDelete(doc, &pCurBlock, &pCurLine, totalSize)) {
|
|
return FALSE;
|
|
}
|
|
d->recType = undoRedoState;
|
|
CloseLine(doc, &pCurLine, min(d->NbLines, lineNb + 1), &pCurBlock);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//When your file has an Undo buffer, be sure to set the undo/redo
|
|
//engine to UNDO before deleting chars, otherwise they will be stored
|
|
//in the REDO buffer
|
|
BOOL FAR PASCAL
|
|
DeleteBlock(
|
|
int doc,
|
|
int col1,
|
|
long line1,
|
|
int col2,
|
|
long line2
|
|
)
|
|
{
|
|
LPLINEREC pCurLine;
|
|
LPBLOCKDEF pCurBlock;
|
|
int curLine = line1, startLine = line1;
|
|
int editCol1, editCol2;
|
|
LPDOCREC d = &Docs[doc];
|
|
int totalSize = 0;
|
|
WORD undoRedoState;
|
|
int totalNbLines = d->NbLines;
|
|
|
|
//Beep and exit if file is ReadOnly
|
|
if (d->readOnly || ROREGION(d,col1,line1)) {
|
|
ReadOnlyBeep();
|
|
return FALSE;
|
|
} else {
|
|
// Clear error status
|
|
QueryReadOnlyError();
|
|
}
|
|
|
|
//Verifications and adjustements
|
|
Assert(line1 < totalNbLines);
|
|
Assert(line2 <= totalNbLines);
|
|
Assert(line2 >= line1);
|
|
editCol1 = min(col1, MAX_USER_LINE);
|
|
editCol2 = min(col2, MAX_USER_LINE + 1);
|
|
d->lineTop = d->lineBottom = line1;
|
|
|
|
editorIsCritical = TRUE;
|
|
|
|
//Get information about line we start delete
|
|
if (!FirstLine (doc, &pCurLine, &line1, &pCurBlock)) {
|
|
//Try to resynchronize system on first line of document
|
|
pCurLine = (LPLINEREC)(d->FirstBlock->Data);
|
|
CloseLine(doc, &pCurLine, 1, &d->FirstBlock);
|
|
ClearSelection(curView);
|
|
editorIsCritical = FALSE;
|
|
return FALSE;
|
|
}
|
|
line1--;
|
|
|
|
//Remember record action
|
|
undoRedoState = d->recType;
|
|
|
|
//Align column respecting tabs
|
|
if (playingRecords) {
|
|
col1 = editCol1;
|
|
} else {
|
|
col1 = AlignToTabs(editCol1, pCurLine->Length - LHD, (LPSTR)pCurLine->Text);
|
|
}
|
|
|
|
//Open an edit record, if buffer full, ask the user for action
|
|
if (!OpenRec(doc, INSERTSTREAM, col1, curLine)) {
|
|
if (CancelDelete(doc)) {
|
|
goto rollBack;
|
|
}
|
|
}
|
|
|
|
//Full line(s) delete
|
|
if (col1 == 0 && col2 == 0) {
|
|
|
|
for (curLine = line1; curLine < line2; curLine++) {
|
|
if (!FullLineDelete(doc, &pCurBlock, &pCurLine, &totalSize)) {
|
|
goto rollBack;
|
|
}
|
|
}
|
|
CloseLine(doc, &pCurLine, line1+1, &pCurBlock);
|
|
|
|
} else if (line1 == line2) {
|
|
|
|
if (playingRecords) {
|
|
col2 = editCol2;
|
|
} else {
|
|
col2 = AlignToTabs(editCol2, pCurLine->Length - LHD, (LPSTR)pCurLine->Text);
|
|
}
|
|
|
|
//The 2 columns could be in the same tab
|
|
if (col1 == col2) {
|
|
col2++;
|
|
}
|
|
|
|
if (!PartialLineDelete(doc, &pCurBlock, &pCurLine, &totalSize, col1, col2, FALSE)) {
|
|
goto rollBack;
|
|
}
|
|
CloseLine(doc, &pCurLine, line1+1, &pCurBlock);
|
|
|
|
} else {
|
|
|
|
//Tricky case, deletion spreads over several lines
|
|
Assert(curLine < line2);
|
|
|
|
//Delete the right part of first line
|
|
if (!PartialLineDelete(doc,
|
|
&pCurBlock,
|
|
&pCurLine,
|
|
&totalSize,
|
|
col1,
|
|
pCurLine->Length - LHD,
|
|
TRUE)) {
|
|
goto rollBack;
|
|
}
|
|
|
|
//Insert a fake CrLf in Undo buffer,
|
|
if (!AppendToRec(doc, (LPSTR)NULL, 0, TRUE, &totalSize)) {
|
|
if (CancelDelete(doc)) {
|
|
goto rollBack;
|
|
}
|
|
}
|
|
|
|
//Delete lines in between
|
|
for (curLine = line1+1; curLine < line2; curLine++) {
|
|
if (!FullLineDelete(doc, &pCurBlock, &pCurLine, &totalSize)) {
|
|
goto rollBack;
|
|
}
|
|
}
|
|
|
|
if (playingRecords) {
|
|
col2 = editCol2;
|
|
} else {
|
|
col2 = AlignToTabs(editCol2, pCurLine->Length - LHD, (LPSTR)pCurLine->Text);
|
|
}
|
|
|
|
//Delete the left part of last line
|
|
if (!PartialLineDelete(doc,
|
|
&pCurBlock,
|
|
&pCurLine,
|
|
&totalSize,
|
|
0,
|
|
col2,
|
|
FALSE)) {
|
|
goto rollBack;
|
|
}
|
|
|
|
//Rebuild resulting line
|
|
if (!JoinLines(doc, pCurBlock, pCurLine, &totalSize, line1+1, col1)) {
|
|
goto rollBack;
|
|
}
|
|
|
|
//Inform breakpoints and errors handler
|
|
if (col1 > 0) {
|
|
startLine++;
|
|
}
|
|
}
|
|
|
|
//Document is modified
|
|
ModifiedDoc(doc);
|
|
|
|
//Inform breakpoints handler of what we did in the text
|
|
if (d->NbLines < totalNbLines) {
|
|
AdjustDebugLines(doc, startLine, totalNbLines - d->NbLines, FALSE);
|
|
}
|
|
|
|
//If the recording was canceled, this close will do nothing
|
|
CloseRec(doc, 0, 0, TRUE);
|
|
|
|
//If there was nothing to record, tells the system that now we have
|
|
//something in buffer, if we had an overflow, restart Undo/Redo
|
|
if (d->recType != REC_HADOVERFLOW) {
|
|
d->recType = undoRedoState;
|
|
} else {
|
|
d->playCount = REC_CANNOTUNDO;
|
|
}
|
|
|
|
if (d->language != NO_LANGUAGE) {
|
|
CheckSyntax(doc);
|
|
}
|
|
|
|
editorIsCritical = FALSE;
|
|
return TRUE;
|
|
|
|
|
|
//Record buffer full
|
|
rollBack: {
|
|
|
|
CloseRec(doc, 0, 0, TRUE);
|
|
|
|
//Document is modified
|
|
ModifiedDoc(doc);
|
|
|
|
//Try to resynchronize system on first line of document
|
|
pCurBlock = d->FirstBlock;
|
|
pCurLine = (LPLINEREC)pCurBlock->Data;
|
|
|
|
CloseLine(doc, &pCurLine, 1, &pCurBlock);
|
|
ClearSelection(curView);
|
|
editorIsCritical = FALSE;
|
|
|
|
//Play the recorded information
|
|
PlayRec(doc, d->recType, FALSE, FALSE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL FAR PASCAL
|
|
DeleteAll(
|
|
int doc
|
|
)
|
|
{
|
|
return DeleteBlock(doc, 0, 0, MAX_USER_LINE + 1, Docs[doc].NbLines - 1);
|
|
}
|
|
|
|
BOOL FAR PASCAL
|
|
ReplaceCharInBlock(
|
|
int doc,
|
|
int x,
|
|
long y,
|
|
int ch
|
|
)
|
|
{
|
|
LPLINEREC pCurLine;
|
|
LPBLOCKDEF pCurBlock;
|
|
LPDOCREC d = &Docs[doc];
|
|
int realX;
|
|
|
|
//Beep and exit if file is ReadOnly
|
|
if (d->readOnly || ROREGION(d,x,y)) {
|
|
ReadOnlyBeep();
|
|
return TRUE;
|
|
} else {
|
|
// Clear error status
|
|
QueryReadOnlyError();
|
|
}
|
|
|
|
//Get information from line we replace char
|
|
if (!FirstLine (doc, &pCurLine, &y, &pCurBlock))
|
|
return FALSE;
|
|
CloseLine(doc, &pCurLine, y, &pCurBlock);
|
|
|
|
if (playingRecords) {
|
|
realX = x;
|
|
} else {
|
|
realX = AlignToTabs(x, pCurLine->Length - LHD, (LPSTR)pCurLine->Text);
|
|
}
|
|
|
|
if (realX >= pCurLine->Length - LHD) {
|
|
|
|
//Character position is after line last char, insert char
|
|
return InsertBlock(doc, x, y - 1, 1, (LPSTR)&ch);
|
|
|
|
} else {
|
|
|
|
d->lineTop = d->lineBottom = y - 1;
|
|
|
|
//It's a real replace, open an edit record, append char and
|
|
//close record
|
|
if (!OpenRec(doc, REPLACECHAR, realX, y - 1)) {
|
|
return FALSE;
|
|
}
|
|
if (!AppendToRec(doc, (LPSTR)pCurLine->Text + realX, 1, FALSE, NULL)) {
|
|
return FALSE;
|
|
}
|
|
CloseRec(doc, 0, 0, TRUE);
|
|
|
|
//Character is inside line, just replace it
|
|
pCurLine->Text[realX] = (char)ch;
|
|
|
|
//Document is modified
|
|
ModifiedDoc(doc);
|
|
|
|
if (d->language != NO_LANGUAGE) {
|
|
CheckSyntax(doc);
|
|
}
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
ULONG FAR PASCAL CountCharsInDocument(
|
|
int doc)
|
|
{
|
|
LPLINEREC pl;
|
|
LPBLOCKDEF pb;
|
|
long y;
|
|
ULONG nbChars;
|
|
|
|
y = 0;
|
|
//Load first line of document
|
|
if (!FirstLine (doc, &pl, &y, &pb)) {
|
|
Assert(FALSE);
|
|
return 0L;
|
|
}
|
|
|
|
//Count chars in first line
|
|
nbChars = pl->Length - LHD + 2; //2 = CR+LF
|
|
|
|
//Count chars in middle lines
|
|
while (y < Docs[doc].NbLines) {
|
|
|
|
//Load next line of document
|
|
if (!NextLine (doc, &pl, &y, &pb)) {
|
|
Assert(FALSE);
|
|
return 0;
|
|
}
|
|
|
|
//Count chars in current line
|
|
nbChars += pl->Length - LHD + 2; //2 = CR+LF
|
|
|
|
}
|
|
|
|
//Refresh pointers
|
|
CloseLine (doc, &pl, y, &pb);
|
|
|
|
return nbChars;
|
|
|
|
}
|
|
|
|
//Scan all document and try to compress data by moving lines and
|
|
//filling blocks
|
|
BOOL FAR PASCAL
|
|
CompactDocument(
|
|
int doc
|
|
)
|
|
{
|
|
LPLINEREC pCurLine, pNextLine;
|
|
LPBLOCKDEF pCurBlock, pNextBlock;
|
|
int freeBlockSize;
|
|
BOOL more;
|
|
LPDOCREC d = &Docs[doc];
|
|
long spaceRecovered = 0;
|
|
|
|
//Load last line of first block
|
|
pCurBlock = d->FirstBlock;
|
|
pNextBlock = pCurBlock->NextBlock;
|
|
|
|
while (pNextBlock != NULL) {
|
|
|
|
pNextLine = (LPLINEREC)(pNextBlock->Data);
|
|
|
|
more = TRUE;
|
|
while (more) {
|
|
|
|
pCurLine = (LPLINEREC)(pCurBlock->Data + pCurBlock->LastLineOffset);
|
|
freeBlockSize = BLOCK_SIZE - pCurBlock->LastLineOffset -
|
|
pCurLine->Length;
|
|
|
|
//Have we space to move next line in current block ?
|
|
if (freeBlockSize < (int)pNextLine->Length) {
|
|
|
|
more = FALSE;
|
|
|
|
} else {
|
|
|
|
//Move first line from next block in current block
|
|
pCurBlock->LastLineOffset += pCurLine->Length;
|
|
memmove((LPSTR)pCurLine + pCurLine->Length,
|
|
pNextLine,
|
|
pNextLine->Length);
|
|
|
|
//Remove line from next block
|
|
pNextBlock->LastLineOffset -= pNextLine->Length;
|
|
spaceRecovered += pNextLine->Length;
|
|
memmove((LPSTR)pNextBlock->Data,
|
|
(LPSTR)pNextLine + pNextLine->Length,
|
|
BLOCK_SIZE - (size_t) (((LPSTR)pNextLine + pNextLine->Length -
|
|
(LPSTR)pNextBlock->Data)));
|
|
|
|
//Free next block if empty
|
|
if (pNextBlock->LastLineOffset < 0) {
|
|
pNextBlock = FreeBlock(doc, pNextBlock);
|
|
if (pNextBlock == NULL) {
|
|
more = FALSE;
|
|
} else {
|
|
pNextLine = (LPLINEREC)(pNextBlock->Data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Next block becomes current block
|
|
pCurBlock = pCurBlock->NextBlock;
|
|
if (pCurBlock != NULL) {
|
|
pNextBlock = pCurBlock->NextBlock;
|
|
}
|
|
}
|
|
|
|
//Resynchronize to first line of document
|
|
d->CurrentLine = 0;
|
|
d->CurrentBlock = d->FirstBlock;
|
|
d->CurrentLineOffset = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL FAR PASCAL
|
|
CheckDocument(
|
|
int doc
|
|
)
|
|
{
|
|
LPLINEREC pl;
|
|
LPBLOCKDEF pb, previousBlock;
|
|
BOOL curBlockFound = FALSE, lastBlockFound = FALSE;
|
|
BOOL curLineFound = FALSE;
|
|
BYTE curLen;
|
|
int nbLines = 0;
|
|
LPDOCREC d = &Docs[doc];
|
|
int nbBl = 0;
|
|
DWORD dataSize = 0;
|
|
DWORD totalSize;
|
|
|
|
pb = d->FirstBlock;
|
|
Assert(pb != NULL);
|
|
previousBlock = NULL;
|
|
curLen = 0;
|
|
|
|
while (pb != NULL) {
|
|
|
|
if (pb == d->CurrentBlock) {
|
|
curBlockFound = TRUE;
|
|
}
|
|
if (pb == d->LastBlock) {
|
|
lastBlockFound = TRUE;
|
|
}
|
|
if (pb->PrevBlock != previousBlock) {
|
|
return AuxPrintf(1, "ChkDoc:Prev block pointer (Line %i)", nbLines);
|
|
}
|
|
|
|
if (pb->LastLineOffset > BLOCK_SIZE - LHD) {
|
|
return AuxPrintf(1, "ChkDoc:Line last offset above (Line %i)",
|
|
nbLines);
|
|
}
|
|
if (pb->LastLineOffset < 0) {
|
|
return AuxPrintf(1, "ChkDoc:Line last offset below (Line %i)",
|
|
nbLines);
|
|
}
|
|
|
|
pl = (LPLINEREC)(pb)->Data;
|
|
|
|
while (pl <= (LPLINEREC)(pb->Data + pb->LastLineOffset)) {
|
|
|
|
if (d->CurrentBlock == pb) {
|
|
if (d->CurrentLineOffset == ((LPSTR)pl - (LPSTR)pb->Data)) {
|
|
curLineFound = TRUE;
|
|
if (nbLines != d->CurrentLine) {
|
|
return AuxPrintf(1, "ChkDoc:Bad current line (Line %i)",
|
|
nbLines);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pl->PrevLength != curLen) {
|
|
return AuxPrintf(1, "ChkDoc:Bad prev length (Line %i)",
|
|
nbLines);
|
|
}
|
|
curLen = pl->Length;
|
|
dataSize += curLen;
|
|
if (curLen < LHD) {
|
|
return AuxPrintf(1, "ChkDoc:Bad length (Line %i)", nbLines);
|
|
}
|
|
if ((LPSTR)pl + curLen > (LPSTR)pb->Data + BLOCK_SIZE) {
|
|
return AuxPrintf(1, "ChkDoc:Line above block (Line %i)",
|
|
nbLines);
|
|
}
|
|
pl = (LPLINEREC) ((LPSTR)pl + curLen);
|
|
nbLines++;
|
|
}
|
|
|
|
previousBlock = pb;
|
|
pb = pb->NextBlock;
|
|
if (pb == NULL) {
|
|
if (d->LastBlock != previousBlock) {
|
|
return AuxPrintf(1, "ChkDoc: Bad last block (Line %i)",
|
|
nbLines);
|
|
}
|
|
}
|
|
|
|
nbBl++;
|
|
}
|
|
|
|
totalSize = (DWORD)nbBl * sizeof(BLOCKDEF);
|
|
Assert(totalSize);
|
|
AuxPrintf(1,
|
|
"Blocks = %i, Total Size = %lu, Data Size = %lu, Efficiency = %lu\\%",
|
|
nbBl, totalSize, dataSize, (100 * dataSize) / totalSize);
|
|
if (!curBlockFound) {
|
|
return AuxPrintf(1, "ChkDoc:Lost cur block (Line %i)", nbLines);
|
|
}
|
|
if (!lastBlockFound) {
|
|
return AuxPrintf(1, "ChkDoc:Lost last block (Line %i)", nbLines);
|
|
}
|
|
if (!curLineFound){
|
|
return AuxPrintf(1, "ChkDoc:Lost cur line (Line %i)", nbLines);
|
|
}
|
|
if (nbLines != d->NbLines) {
|
|
return AuxPrintf(1,
|
|
"ChkDoc:Wrong total of lines (Doc = %i, Count = %i)",
|
|
d->NbLines, nbLines);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void FAR PASCAL
|
|
DumpDocument(
|
|
int doc
|
|
)
|
|
{
|
|
LPLINEREC pl;
|
|
LPBLOCKDEF pb;
|
|
int line = 0;
|
|
char s[10], t[10];
|
|
LPDOCREC d = &Docs[doc];
|
|
int i;
|
|
|
|
pb = d->FirstBlock;
|
|
while (pb != NULL) {
|
|
_itoa(HIWORD((DWORD_PTR)pb), t, 16);
|
|
while ((int)strlen(t) <= 4) {
|
|
strcat(t, " ");
|
|
}
|
|
strcat(t, ":");
|
|
pl = (LPLINEREC)(pb)->Data;
|
|
while (pl <= (LPLINEREC)(pb->Data + pb->LastLineOffset)) {
|
|
|
|
s[0]='\0';
|
|
if (d->CurrentBlock == pb) {
|
|
if (d->CurrentLineOffset == ((LPSTR)pl - (LPSTR)pb->Data)) {
|
|
_itoa((int)d->CurrentLine, s, 10);
|
|
}
|
|
}
|
|
|
|
while ((int)strlen(s) <= 2) {
|
|
strcat(s, " ");
|
|
}
|
|
|
|
strncpy((LPSTR)szTmp, pl->Text, pl->Length - LHD);
|
|
for (i = 0; i < (int)strlen(szTmp); i++) {
|
|
if(IsDBCSLeadByte((BYTE)szTmp[i])) {
|
|
i++;
|
|
}
|
|
if (szTmp[i] == TAB) {
|
|
szTmp[i] = 126;
|
|
}
|
|
}
|
|
szTmp[min(200, pl->Length - LHD)] = '\0';
|
|
AuxPrintf(1, "%s%3u [%3i,%3i,%3i] %s '%s'",
|
|
(LPSTR)t, line++, pl->Status, pl->PrevLength,
|
|
pl->Length, (LPSTR)s, (LPSTR)szTmp);
|
|
strcpy(t," :");
|
|
pl = (LPLINEREC) ((LPSTR)pl + pl->Length);
|
|
}
|
|
pb = pb->NextBlock;
|
|
}
|
|
|
|
AuxPrintf(1, "");
|
|
}
|
|
|
|
BOOL FAR PASCAL
|
|
DestroyDocument(
|
|
int doc
|
|
)
|
|
{
|
|
LPBLOCKDEF pb;
|
|
LPDOCREC d = &Docs[doc];
|
|
|
|
//Destroy undo and possibly redo buffers
|
|
DestroyRecBuf(doc, REC_UNDO);
|
|
if (d->playCount != 0 && d->playCount != REC_CANNOTUNDO) {
|
|
DestroyRecBuf(doc, REC_REDO);
|
|
}
|
|
|
|
//And blocks of memory allocated for it
|
|
while (d->FirstBlock != NULL) {
|
|
|
|
pb = d->FirstBlock;
|
|
|
|
Assert(pb->NextBlock != NULL || pb == d->LastBlock);
|
|
|
|
d->FirstBlock = pb->NextBlock;
|
|
if (!DocFree((LPSTR)pb)) {
|
|
InternalErrorBox(SYS_Free_Memory);
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//Retrieve chars on a line
|
|
BOOL FAR PASCAL
|
|
GetTextAtLine(
|
|
int doc,
|
|
long line,
|
|
int col1,
|
|
int col2,
|
|
LPSTR text
|
|
)
|
|
{
|
|
LPLINEREC pl;
|
|
LPBLOCKDEF pb;
|
|
|
|
if (!FirstLine (doc, &pl, &line, &pb))
|
|
return FALSE;
|
|
CloseLine(doc, &pl, line, &pb);
|
|
|
|
col2 = min(col2, elLen);
|
|
col1 = min(col1, col2);
|
|
memmove(text, el + col1, col2 - col1);
|
|
text[col2 - col1] = '\0';
|
|
return TRUE;
|
|
}
|
|
|