From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

/*------------------------------------------------------------*/
/* filename - teditor2.cpp */
/* */
/* function(s) */
/* TEditor member functions */
/*------------------------------------------------------------*/
/*
* Turbo Vision - Version 2.0
*
* Copyright (c) 1994 by Borland International
* All Rights Reserved.
*
*/
#define Uses_TKeys
#define Uses_TEditor
#define Uses_TIndicator
#define Uses_TEvent
#define Uses_TScrollBar
#define Uses_TFindDialogRec
#define Uses_TReplaceDialogRec
#define Uses_TMenuItem
#define Uses_opstream
#define Uses_ipstream
#include <tvision/tv.h>
#if !defined( __STRING_H )
#include <string.h>
#endif // __STRING_H
#if !defined( __CTYPE_H )
#include <ctype.h>
#endif // __CTYPE_H
#if !defined( __DOS_H )
#include <dos.h>
#endif // __DOS_H
extern "C" {
int countLines( const char *buf, uint count );
uint scan( const char *block, uint size, const char *str );
uint iScan( const char *block, uint size, const char *str );
}
static int getCharType( char ch )
{
if( strchr("\t \0", ch) )
return 0;
if( strchr("\n\r", ch) )
return 1;
if( strchr("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", ch) )
return 2;
return 3;
}
static inline int isWordBoundary( char a, char b )
{
return getCharType(a) != getCharType(b);
}
static inline int isWordChar( int ch )
{
return strchr(" !\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~\0", ch) == 0;
}
#pragma warn -asc
#pragma warn .asc
void TEditor::detectEol()
{
for (uint p = 0; p < bufLen; ++p)
if (bufChar(p) == '\r')
{
if (p+1 < bufLen && bufChar(p+1) == '\n')
eolType = eolCrLf;
else
eolType = eolCr;
return;
}
else if (bufChar(p) == '\n')
{
eolType = eolLf;
return;
}
// Default to CRLF.
eolType = eolCrLf;
}
Boolean TEditor::hasSelection()
{
return Boolean(selStart != selEnd);
}
void TEditor::hideSelect()
{
selecting = False;
setSelect(curPtr, curPtr, False);
}
void TEditor::initBuffer()
{
buffer = new char[bufSize];
}
TMenuItem& TEditor::initContextMenu( TPoint )
// The TPoint argument is the future location of the context menu.
// You can get the text under it with 'getMousePtr'.
{
return
*new TMenuItem( "Cu~t~", cmCut, kbShiftDel, hcNoContext, "Shift-Del" ) +
*new TMenuItem( "~C~opy", cmCopy, kbCtrlIns, hcNoContext, "Ctrl-Ins" ) +
*new TMenuItem( "~P~aste", cmPaste, kbShiftIns, hcNoContext, "Shift-Ins" ) +
*new TMenuItem( "~U~ndo", cmUndo, kbCtrlU, hcNoContext, "Ctrl-U" );
}
uint TEditor::insertMultilineText( const char *text, uint length )
{
size_t i = 0, j = 0;
do {
if( text[i] == '\n' || text[i] == '\r' )
{
if( !insertText( &text[j], i - j, False ) ) return j;
if( !insertEOL( False ) ) return i;
if( i + 1 < length && text[i] == '\r' && text[i + 1] == '\n' )
++i;
j = i + 1;
}
} while( ++i < length );
if( !insertText( &text[j], i - j, False ) ) return j;
return i;
}
Boolean TEditor::insertBuffer( const char *p,
uint offset,
uint length,
Boolean allowUndo,
Boolean selectText
)
{
selecting = False;
uint selLen = selEnd - selStart;
if( selLen == 0 && length == 0 )
return True;
uint delLen = 0;
if( allowUndo == True )
{
if( curPtr == selStart )
delLen = selLen;
else
if( selLen > insCount )
delLen = selLen - insCount;
}
ulong newSize = ulong(bufLen + delCount - selLen + delLen) + length;
if( newSize > bufLen + delCount )
{
Boolean bufferText = Boolean( p >= buffer && p < buffer + bufLen );
if( bufferText )
p -= ptrdiff_t(buffer);
if( newSize > UINT_MAX-0x1Fl || setBufSize(uint(newSize)) == False )
{
editorDialog( edOutOfMemory );
selEnd = selStart;
return False;
}
if( bufferText )
p += ptrdiff_t(buffer);
}
uint selLines = countLines( &buffer[bufPtr(selStart)], selLen );
if( curPtr == selEnd )
{
if( allowUndo == True )
{
if( delLen > 0 )
memmove(
&buffer[curPtr + gapLen - delCount - delLen],
&buffer[selStart],
delLen
);
insCount -= selLen - delLen;
}
curPtr = selStart;
curPos.y -= selLines;
}
if( delta.y > curPos.y )
{
delta.y -= selLines;
if( delta.y < curPos.y )
delta.y = curPos.y;
}
if( length > 0 )
memmove(
&buffer[curPtr],
&p[offset],
length
);
uint lines = countLines( &buffer[curPtr], length );
curPtr += length;
curPos.y += lines;
drawLine = curPos.y;
drawPtr = lineStart(curPtr);
curPos.x = charPos(drawPtr, curPtr);
if( selectText == False )
selStart = curPtr;
selEnd = curPtr;
bufLen += length - selLen;
gapLen -= length - selLen;
if( allowUndo == True )
{
delCount += delLen;
insCount += length;
}
limit.y += lines - selLines;
delta.y = max(0, min(delta.y, limit.y - size.y));
if( isClipboard() == False )
modified = True;
setBufSize(bufLen + delCount);
if( selLines == 0 && lines == 0 )
update(ufLine);
else
update(ufView);
return True;
}
Boolean TEditor::insertEOL( Boolean selectText )
{
static const char * const eolBytes[] = { "\r\n", "\n", "\r" };
const char *eol = eolBytes[eolType];
return insertText( eol, strlen(eol), selectText );
}
Boolean TEditor::insertFrom( TEditor *editor )
{
return insertBuffer( editor->buffer,
editor->bufPtr(editor->selStart),
editor->selEnd - editor->selStart,
canUndo,
isClipboard()
);
}
Boolean TEditor::insertText( const void *text, uint length, Boolean selectText )
{
return insertBuffer( (const char *)text, 0, length, canUndo, selectText);
}
Boolean TEditor::isClipboard()
{
return Boolean(clipboard == this);
}
uint TEditor::lineMove( uint p, int count )
{
uint i = p;
p = lineStart(p);
int pos = charPos(p, i);
while( count != 0 )
{
i = p;
if( count < 0 )
{
p = prevLine(p);
count++;
}
else
{
p = nextLine(p);
count--;
}
}
if( p != i )
p = charPtr(p, pos);
return p;
}
void TEditor::lock()
{
lockCount++;
}
void TEditor::newLine()
{
uint p = lineStart(curPtr);
uint i = p;
while( i < curPtr &&
( (buffer[i] == ' ') || (buffer[i] == '\x9'))
)
i++;
insertEOL(False);
if( autoIndent == True )
insertText( &buffer[p], i - p, False);
}
uint TEditor::nextLine( uint p )
{
return nextChar(lineEnd(p));
}
uint TEditor::nextWord( uint p )
{
if (p < bufLen)
{
char a = bufChar(p);
char b;
do {
b = a;
p = nextChar(p);
} while( p < bufLen && !isWordBoundary((a = bufChar(p)), b) );
}
return p;
}
uint TEditor::prevLine( uint p )
{
return lineStart(prevChar(p));
}
uint TEditor::prevWord( uint p )
{
if (p > 0 && (p = prevChar(p), p > 0))
{
char a = bufChar(p);
char b;
do {
b = a;
p = prevChar(p);
a = bufChar(p);
} while( p > 0 && !isWordBoundary(a, b) );
if( isWordBoundary(a, b) )
p = nextChar(p);
}
return p;
}
uint TEditor::indentedLineStart( uint P )
{
uint startPtr = lineStart(P);
uint destPtr = startPtr;
char c;
while( destPtr < bufLen && ((c = bufChar(destPtr)) == ' ' || c == '\t') )
++destPtr;
return destPtr == P ? startPtr : destPtr;
}
void TEditor::replace()
{
TReplaceDialogRec replaceRec( findStr, replaceStr, editorFlags );
if( editorDialog( edReplace, &replaceRec ) != cmCancel )
{
strcpy( findStr, replaceRec.find );
strcpy( replaceStr, replaceRec.replace );
editorFlags = replaceRec.options | efDoReplace;
doSearchReplace();
}
}
void TEditor::scrollTo( int x, int y )
{
x = max(0, min(x, limit.x - size.x));
y = max(0, min(y, limit.y - size.y));
if( x != delta.x || y != delta.y )
{
delta.x = x;
delta.y = y;
update(ufView);
}
}
Boolean TEditor::search( const char *findStr, ushort opts )
{
uint pos = curPtr;
uint i;
do {
if( (opts & efCaseSensitive) != 0 )
i = scan( &buffer[bufPtr(pos)], bufLen - pos, findStr);
else
i = iScan( &buffer[bufPtr(pos)], bufLen - pos, findStr);
if( i != sfSearchFailed )
{
i += pos;
if( (opts & efWholeWordsOnly) == 0 ||
!(
( i != 0 && isWordChar(bufChar(i - 1)) != 0 ) ||
( i + strlen(findStr) != bufLen &&
isWordChar(bufChar(i + strlen(findStr)))
)
))
{
lock();
setSelect(i, i + strlen(findStr), False);
trackCursor(Boolean(!cursorVisible()));
unlock();
return True;
}
else
pos = i + 1;
}
} while( i != sfSearchFailed );
return False;
}
void TEditor::setBufLen( uint length )
{
bufLen = length;
gapLen = bufSize - length;
selStart = 0;
selEnd = 0;
curPtr = 0;
delta.x = 0;
delta.y = 0;
curPos = delta;
limit.x = maxLineLength;
limit.y = countLines( &buffer[gapLen], bufLen ) + 1;
drawLine = 0;
drawPtr = 0;
delCount = 0;
insCount = 0;
modified = False;
detectEol();
update(ufView);
}
Boolean TEditor::setBufSize( uint newSize )
{
return Boolean(newSize <= bufSize);
}
void TEditor::setCmdState( ushort command, Boolean enable )
{
TCommandSet s;
s += command;
if( enable == True && (state & sfActive) != 0 )
enableCommands(s);
else
disableCommands(s);
}
void TEditor::setCurPtr( uint p, uchar selectMode )
{
uint anchor;
if( (selectMode & smExtend) == 0 )
anchor = p;
else if( curPtr == selStart )
anchor = selEnd;
else
anchor = selStart;
if( p < anchor )
{
if( (selectMode & smDouble) != 0 )
{
p = prevWord(nextWord(p));
anchor = nextWord(prevWord(anchor));
}
else if( (selectMode & smTriple) != 0 )
{
p = prevLine(nextLine(p));
anchor = nextLine(prevLine(anchor));
}
setSelect(p, anchor, True);
}
else
{
if( (selectMode & smDouble) != 0 )
{
p = nextWord(p);
anchor = prevWord(nextWord(anchor));
}
else if( (selectMode & smTriple) != 0 )
{
p = nextLine(p);
anchor = prevLine(nextLine(anchor));
}
setSelect(anchor, p, False);
}
}
void TEditor::setSelect( uint newStart, uint newEnd, Boolean curStart )
{
uint p;
if( curStart != 0 )
p = newStart;
else
p = newEnd;
uchar flags = ufUpdate;
if( newStart != selStart || newEnd != selEnd )
if( newStart != newEnd || selStart != selEnd )
flags = ufView;
if( p != curPtr )
{
if( p > curPtr )
{
uint l = p - curPtr;
memmove( &buffer[curPtr], &buffer[curPtr + gapLen], l);
curPos.y += countLines(&buffer[curPtr], l);
curPtr = p;
}
else
{
uint l = curPtr - p;
curPtr = p;
curPos.y -= countLines(&buffer[curPtr], l);
memmove( &buffer[curPtr + gapLen], &buffer[curPtr], l);
}
delCount = 0;
insCount = 0;
setBufSize(bufLen);
}
drawLine = curPos.y;
drawPtr = lineStart(p);
curPos.x = charPos(drawPtr, p);
selStart = newStart;
selEnd = newEnd;
update(flags);
}
void TEditor::setState( ushort aState, Boolean enable )
{
TView::setState(aState, enable);
switch( aState )
{
case sfActive:
if( hScrollBar != 0 )
hScrollBar->setState(sfVisible, enable);
if( vScrollBar != 0 )
vScrollBar->setState(sfVisible, enable);
if( indicator != 0 )
indicator->setState(sfVisible, enable);
updateCommands();
break;
case sfExposed:
if( enable == True )
unlock();
}
}
void TEditor::startSelect()
{
hideSelect();
selecting = True;
}
void TEditor::toggleEncoding()
{
if( encoding == encDefault )
encoding = encSingleByte;
else
encoding = encDefault;
updateFlags |= ufView;
setSelect(selStart, selEnd, Boolean( curPtr < selEnd ));
}
void TEditor::toggleInsMode()
{
overwrite = Boolean(!overwrite);
setState(sfCursorIns, Boolean(!getState(sfCursorIns)));
}
void TEditor::trackCursor( Boolean center )
{
if( center == True )
scrollTo( curPos.x - size.x + 1, curPos.y - size.y / 2);
else
scrollTo( max(curPos.x - size.x + 1, min(delta.x, curPos.x)),
max(curPos.y - size.y + 1, min(delta.y, curPos.y)));
}
void TEditor::undo()
{
if( delCount != 0 || insCount != 0 )
{
selStart = curPtr - insCount;
selEnd = curPtr;
uint length = delCount;
delCount = 0;
insCount = 0;
insertBuffer(buffer, curPtr + gapLen - length, length, False, True);
}
}
void TEditor::unlock()
{
if( lockCount > 0 )
{
lockCount--;
if( lockCount == 0 )
doUpdate();
}
}
void TEditor::update( uchar aFlags )
{
updateFlags |= aFlags;
if( lockCount == 0 )
doUpdate();
}
void TEditor::updateCommands()
{
setCmdState( cmUndo, Boolean( delCount != 0 || insCount != 0 ) );
if( isClipboard() == False )
{
setCmdState(cmCut, hasSelection());
setCmdState(cmCopy, hasSelection());
setCmdState(cmPaste,
Boolean(clipboard == 0 || clipboard->hasSelection()) );
}
setCmdState(cmClear, hasSelection());
setCmdState(cmFind, True);
setCmdState(cmReplace, True);
setCmdState(cmSearchAgain, True);
}
Boolean TEditor::valid( ushort )
{
return isValid;
}
#if !defined(NO_STREAMABLE)
void TEditor::write( opstream& os )
{
TView::write( os );
os << hScrollBar << vScrollBar << indicator
<< bufSize << (uchar)canUndo << (uchar)eolType
<< (uchar)encoding;
}
void *TEditor::read( ipstream& is )
{
TView::read( is );
is >> hScrollBar >> vScrollBar >> indicator
>> bufSize;
uchar temp;
is >> temp; canUndo = Boolean(temp);
is >> temp; eolType = EolType(temp);
is >> temp; encoding = Encoding(temp);
selecting = False;
overwrite = False;
autoIndent = True;
lockCount = 0;
updateFlags = 0;
keyState = 0;
initBuffer();
if( buffer != 0 )
isValid = True;
else
{
isValid = False;
TEditor::editorDialog( edOutOfMemory, 0 );
bufSize = 0;
}
lockCount = 0;
lock();
setBufLen( 0 );
return this;
}
TStreamable *TEditor::build()
{
return new TEditor( streamableInit );
}
TEditor::TEditor( StreamableInit ) noexcept : TView( streamableInit )
{
}
#endif