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

/*------------------------------------------------------------*/
/* filename - teditor1.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_opstream
#define Uses_ipstream
#define Uses_TText
#define Uses_TClipboard
#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
#if !defined( __MALLOC_H )
#include <malloc.h>
#endif // __MALLOC_H
#if !defined( __STDLIB_H )
#include <stdlib.h>
#endif // __STDLIB_H
#ifndef __BORLANDC__
#define register
#endif
const ushort firstKeys[] =
{
41,
kbCtrlA, cmSelectAll,
kbCtrlC, cmPageDown,
kbCtrlD, cmCharRight,
kbCtrlE, cmLineUp,
kbCtrlF, cmWordRight,
kbCtrlG, cmDelChar,
kbCtrlH, cmBackSpace,
kbCtrlK, 0xFF02,
kbCtrlL, cmSearchAgain,
kbCtrlM, cmNewLine,
kbCtrlO, cmIndentMode,
kbCtrlP, cmEncoding,
kbCtrlQ, 0xFF01,
kbCtrlR, cmPageUp,
kbCtrlS, cmCharLeft,
kbCtrlT, cmDelWord,
kbCtrlU, cmUndo,
kbCtrlV, cmInsMode,
kbCtrlX, cmLineDown,
kbCtrlY, cmDelLine,
kbLeft, cmCharLeft,
kbRight, cmCharRight,
kbAltBack, cmDelWordLeft,
kbCtrlBack, cmDelWordLeft,
kbCtrlDel, cmDelWord,
kbCtrlLeft, cmWordLeft,
kbCtrlRight, cmWordRight,
kbHome, cmLineStart,
kbEnd, cmLineEnd,
kbUp, cmLineUp,
kbDown, cmLineDown,
kbPgUp, cmPageUp,
kbPgDn, cmPageDown,
kbCtrlHome, cmTextStart,
kbCtrlEnd, cmTextEnd,
kbIns, cmInsMode,
kbDel, cmDelChar,
kbShiftIns, cmPaste,
kbShiftDel, cmCut,
kbCtrlIns, cmCopy,
kbCtrlDel, cmClear
};
const ushort quickKeys[] =
{ 8,
'A', cmReplace,
'C', cmTextEnd,
'D', cmLineEnd,
'F', cmFind,
'H', cmDelStart,
'R', cmTextStart,
'S', cmLineStart,
'Y', cmDelEnd
};
const ushort blockKeys[] =
{ 5,
'B', cmStartSelect,
'C', cmPaste,
'H', cmHideSelect,
'K', cmCopy,
'Y', cmCut
};
const ushort *keyMap[] = { firstKeys, quickKeys, blockKeys };
ushort defEditorDialog( int, ... );
#pragma warn -asc
ushort scanKeyMap( const void *keyMap, ushort keyCode )
{
#if !defined(__FLAT__)
asm {
PUSH DS
LDS SI,keyMap
MOV DX,keyCode
CLD
LODSW
MOV CX,AX
}
__1:
asm {
LODSW
MOV BX,AX
LODSW
CMP BL,DL
JNE __3
OR BH,BH
JE __4
CMP BH,DH
JE __4
}
__3:
asm {
LOOP __1
XOR AX,AX
}
__4:
asm POP DS
return _AX;
#else
register ushort *kM = (ushort *)keyMap;
uchar codeLow = keyCode & 0xff;
uchar codeHi = keyCode >> 8;
int n;
for (n = *kM++; n--; kM++)
{
uchar mapLow = *kM & 0xff;
uchar mapHi = *kM >> 8;
kM++;
ushort command = *kM;
if ((mapLow == codeLow) && ((mapHi == 0) || (mapHi == codeHi)))
return command;
};
return 0;
#endif
}
#pragma warn .asc
#define cpEditor "\x06\x07"
TEditor::TEditor( const TRect& bounds,
TScrollBar *aHScrollBar,
TScrollBar *aVScrollBar,
TIndicator *aIndicator,
uint aBufSize ) noexcept :
TView( bounds ),
hScrollBar( aHScrollBar ),
vScrollBar( aVScrollBar ),
indicator( aIndicator ),
bufSize( aBufSize ),
canUndo( True ),
selecting( False ),
overwrite( False ),
autoIndent( True ) ,
encoding( encDefault ),
lockCount( 0 ),
updateFlags( 0 ),
keyState( 0 )
{
growMode = gfGrowHiX | gfGrowHiY;
options |= ofSelectable;
eventMask = evMouseDown | evKeyDown | evCommand | evBroadcast;
showCursor();
initBuffer();
if( buffer != 0 )
isValid = True;
else
{
editorDialog( edOutOfMemory );
bufSize = 0;
isValid = False;
}
setBufLen(0);
}
TEditor::~TEditor()
{
}
void TEditor::shutDown()
{
doneBuffer();
TView::shutDown();
}
void TEditor::changeBounds( const TRect& bounds )
{
setBounds(bounds);
delta.x = max(0, min(delta.x, limit.x - size.x));
delta.y = max(0, min(delta.y, limit.y - size.y));
update(ufView);
}
TStringView TEditor::bufChars( uint P )
{
static thread_local char buf[maxCharSize];
if (encoding == encSingleByte)
{
buf[0] = bufChar(P);
return TStringView(buf, 1);
}
else
{
int len = min(max(max(curPtr, bufLen) - P, 1), sizeof(buf));
for (int i = 0; i < len; ++i)
buf[i] = bufChar(P + i);
return TStringView(buf, len);
}
}
TStringView TEditor::prevBufChars( uint P )
{
static thread_local char buf[maxCharSize];
if (encoding == encSingleByte)
{
buf[0] = bufChar(P - 1);
return TStringView(buf, 1);
}
else
{
int len = min(max(P, 1), sizeof(buf));
for (int i = 0; i < len; ++i)
buf[i] = bufChar(P - len + i);
return TStringView(buf, len);
}
}
void TEditor::nextChar( TStringView s, uint &p, uint &width )
{
if (encoding == encSingleByte || s.size() == 0)
{
++p;
++width;
}
else
{
size_t p_ = 0, w_ = 0;
TText::next(s, p_, w_);
p += p_; width += w_;
}
}
Boolean TEditor::formatCell( TSpan<TScreenCell> cells, uint &width,
TStringView text, uint &p, TColorAttr color )
{
size_t p_ = 0, w_ = width;
if (TText::drawOne(cells, w_, text, p_, color))
{
p += p_; width = w_;
return True;
}
return False;
}
int TEditor::charPos( uint p, uint target )
{
uint pos = 0;
while( p < target )
{
TStringView chars = bufChars(p);
if( chars[0] == '\x9' )
pos |= 7;
nextChar(chars, p, pos);
}
return pos;
}
uint TEditor::charPtr( uint p, int target )
{
uint pos = 0;
uint lastP = p;
char c;
TStringView chars;
while( (int) pos < target && p < bufLen &&
(c = (chars = bufChars(p))[0]) != '\r' && c != '\n' )
{
lastP = p;
if( c == '\x09' )
pos |= 7;
nextChar(chars, p, pos);
}
if( (int) pos > target)
p = lastP;
return p;
}
Boolean TEditor::clipCopy()
{
Boolean res = False;
if( clipboard != this )
{
if( clipboard != 0 )
res = clipboard->insertFrom(this);
else
{
TClipboard::setText( TStringView( buffer + bufPtr(selStart),
selEnd - selStart ) );
res = True;
}
selecting = False;
update(ufUpdate);
}
return res;
}
void TEditor::clipCut()
{
if( clipCopy() == True )
deleteSelect();
}
void TEditor::clipPaste()
{
if( clipboard != this )
{
if( clipboard != 0 )
insertFrom(clipboard);
else
TClipboard::requestText();
}
}
void TEditor::convertEvent( TEvent& event )
{
if( event.what == evKeyDown )
{
if( (event.keyDown.controlKeyState & kbShift) != 0 &&
event.keyDown.charScan.scanCode >= 0x47 &&
event.keyDown.charScan.scanCode <= 0x51
)
event.keyDown.charScan.charCode = 0;
ushort key = event.keyDown.keyCode;
if( keyState != 0 )
{
if( (key & 0xFF) >= 0x01 && (key & 0xFF) <= 0x1A )
key += 0x40;
if( (key & 0xFF) >= 0x61 && (key & 0xFF) <= 0x7A )
key -= 0x20;
}
key = scanKeyMap(keyMap[keyState], key);
keyState = 0;
if( key != 0 )
{
if( (key & 0xFF00) == 0xFF00 )
{
keyState = (key & 0xFF);
clearEvent(event);
}
else
{
event.what = evCommand;
event.message.command = key;
}
}
}
}
Boolean TEditor::cursorVisible()
{
return Boolean((curPos.y >= delta.y) && (curPos.y < delta.y + size.y));
}
void TEditor::deleteRange( uint startPtr,
uint endPtr,
Boolean delSelect
)
{
if( hasSelection() == True && delSelect == True )
deleteSelect();
else
{
setSelect(curPtr, endPtr, True);
deleteSelect();
setSelect(startPtr, curPtr, False);
deleteSelect();
}
}
void TEditor::deleteSelect()
{
insertText( 0, 0, False );
}
void TEditor::doneBuffer()
{
delete[] buffer;
}
void TEditor::doSearchReplace()
{
int i;
do {
i = cmCancel;
if( search(findStr, editorFlags) == False )
{
if( (editorFlags & (efReplaceAll | efDoReplace)) !=
(efReplaceAll | efDoReplace) )
editorDialog( edSearchFailed );
}
else
if( (editorFlags & efDoReplace) != 0 )
{
i = cmYes;
if( (editorFlags & efPromptOnReplace) != 0 )
{
TPoint c = makeGlobal( cursor );
i = editorDialog( edReplacePrompt, &c );
}
if( i == cmYes )
{
lock();
insertText( replaceStr, strlen(replaceStr), False);
trackCursor(False);
unlock();
}
}
} while( i != cmCancel && (editorFlags & efReplaceAll) != 0 );
}
void TEditor::doUpdate()
{
if( updateFlags != 0 )
{
setCursor(curPos.x - delta.x, curPos.y - delta.y);
if( (updateFlags & ufView) != 0 )
drawView();
else
if( (updateFlags & ufLine) != 0 )
drawLines( curPos.y-delta.y, 1, lineStart(curPtr) );
if( hScrollBar != 0 )
hScrollBar->setParams(delta.x, 0, limit.x - size.x, size.x / 2, 1);
if( vScrollBar != 0 )
vScrollBar->setParams(delta.y, 0, limit.y - size.y, size.y - 1, 1);
if( indicator != 0 )
indicator->setValue(curPos, modified);
if( (state & sfActive) != 0 )
updateCommands();
updateFlags = 0;
}
}
void TEditor::draw()
{
if( drawLine != delta.y )
{
drawPtr = lineMove( drawPtr, delta.y - drawLine );
drawLine = delta.y;
}
drawLines( 0, size.y, drawPtr );
}
void TEditor::drawLines( int y, int count, uint linePtr )
{
TAttrPair color = getColor(0x0201);
#ifndef __FLAT__
TScreenCell b[maxLineLength];
#else
TScreenCell *b = (TScreenCell*) alloca(sizeof(TScreenCell)*(delta.x+size.x));
#endif
while( count-- > 0 )
{
formatLine( b, linePtr, delta.x+size.x, color );
writeBuf(0, y, size.x, 1, &b[delta.x]);
linePtr = nextLine(linePtr);
y++;
}
}
void TEditor::find()
{
TFindDialogRec findRec( findStr, editorFlags );
if( editorDialog( edFind, &findRec ) != cmCancel )
{
strcpy( findStr, findRec.find );
editorFlags = findRec.options & ~efDoReplace;
doSearchReplace();
}
}
uint TEditor::getMousePtr( TPoint m )
{
TPoint mouse = makeLocal( m );
mouse.x = max(0, min(mouse.x, size.x - 1));
mouse.y = max(0, min(mouse.y, size.y - 1));
return charPtr(lineMove(drawPtr, mouse.y + delta.y - drawLine),
mouse.x + delta.x);
}
TPalette& TEditor::getPalette() const
{
static TPalette palette( cpEditor, sizeof( cpEditor )-1 );
return palette;
}
void TEditor::checkScrollBar( const TEvent& event,
TScrollBar *p,
int& d
)
{
if( (event.message.infoPtr == p) && (p->value != d) )
{
d = p->value;
update( ufView );
}
}
void TEditor::handleEvent( TEvent& event )
{
TView::handleEvent( event );
Boolean centerCursor = Boolean(!cursorVisible());
uchar selectMode = 0;
if( selecting == True ||
(event.what & evMouse && (event.mouse.controlKeyState & kbShift) != 0) ||
(event.what & evKeyboard && (event.keyDown.controlKeyState & kbShift ) != 0)
)
selectMode = smExtend;
convertEvent( event );
switch( event.what )
{
case evMouseDown:
if( event.mouse.buttons & mbRightButton )
{
TMenuItem &menu = initContextMenu( event.mouse.where );
popupMenu( event.mouse.where, menu, owner );
break;
}
if( event.mouse.buttons & mbMiddleButton )
{
TPoint lastMouse = makeLocal( event.mouse.where );
while( mouseEvent(event, evMouse) )
{
TPoint mouse = makeLocal( event.mouse.where );
TPoint d = delta + (lastMouse - mouse);
scrollTo(d.x, d.y);
lastMouse = mouse;
}
break;
}
if( event.mouse.eventFlags & meDoubleClick )
selectMode |= smDouble;
else if( event.mouse.eventFlags & meTripleClick )
selectMode |= smTriple;
do {
lock();
if( event.what == evMouseAuto )
{
TPoint mouse = makeLocal( event.mouse.where );
TPoint d = delta;
if( mouse.x < 0 )
d.x--;
if( mouse.x >= size.x )
d.x++;
if( mouse.y < 0 )
d.y--;
if( mouse.y >= size.y )
d.y++;
scrollTo(d.x, d.y);
}
else if( event.what == evMouseWheel )
{
TEvent ev = event;
vScrollBar->handleEvent(ev);
hScrollBar->handleEvent(ev);
}
setCurPtr(getMousePtr(event.mouse.where), selectMode);
selectMode |= smExtend;
unlock();
} while( mouseEvent(event, evMouseMove + evMouseAuto + evMouseWheel) );
break;
case evKeyDown:
if( ( encoding != encSingleByte && event.keyDown.textLength > 0 ) ||
event.keyDown.charScan.charCode == 9 ||
( event.keyDown.charScan.charCode >= 32 && event.keyDown.charScan.charCode < 255 )
)
{
lock();
if( event.keyDown.controlKeyState & kbPaste )
{
char buf[512];
size_t length;
while( textEvent( event, TSpan<char>(buf, sizeof(buf)), length ) )
insertMultilineText( buf, (uint) length );
}
else
{
if( overwrite == True && hasSelection() == False )
if( curPtr != lineEnd(curPtr) )
selEnd = nextChar(curPtr);
if( encoding != encSingleByte && event.keyDown.textLength > 0 )
insertText( event.keyDown.text, event.keyDown.textLength, False );
else
insertText( &event.keyDown.charScan.charCode, 1, False );
}
trackCursor(centerCursor);
unlock();
}
else
return;
break;
case evCommand:
switch( event.message.command )
{
case cmFind:
find();
break;
case cmReplace:
replace();
break;
case cmSearchAgain:
doSearchReplace();
break;
case cmEncoding:
toggleEncoding();
break;
default:
lock();
switch( event.message.command )
{
case cmCut:
clipCut();
break;
case cmCopy:
clipCopy();
break;
case cmPaste:
clipPaste();
break;
case cmUndo:
undo();
break;
case cmClear:
deleteSelect();
break;
case cmCharLeft:
setCurPtr(prevChar(curPtr), selectMode);
break;
case cmCharRight:
setCurPtr(nextChar(curPtr), selectMode);
break;
case cmWordLeft:
setCurPtr(prevWord(curPtr), selectMode);
break;
case cmWordRight:
setCurPtr(nextWord(curPtr), selectMode);
break;
case cmLineStart:
setCurPtr(autoIndent ? indentedLineStart(curPtr) : lineStart(curPtr), selectMode);
break;
case cmLineEnd:
setCurPtr(lineEnd(curPtr), selectMode);
break;
case cmLineUp:
setCurPtr(lineMove(curPtr, -1), selectMode);
break;
case cmLineDown:
setCurPtr(lineMove(curPtr, 1), selectMode);
break;
case cmPageUp:
setCurPtr(lineMove(curPtr, -(size.y-1)), selectMode);
break;
case cmPageDown:
setCurPtr(lineMove(curPtr, size.y-1), selectMode);
break;
case cmTextStart:
setCurPtr(0, selectMode);
break;
case cmTextEnd:
setCurPtr(bufLen, selectMode);
break;
case cmNewLine:
newLine();
break;
case cmBackSpace:
deleteRange(prevChar(curPtr), curPtr, True);
break;
case cmDelChar:
deleteRange(curPtr, nextChar(curPtr), True);
break;
case cmDelWord:
deleteRange(curPtr, nextWord(curPtr), False);
break;
case cmDelWordLeft:
deleteRange(prevWord(curPtr), curPtr, False);
break;
case cmDelStart:
deleteRange(lineStart(curPtr), curPtr, False);
break;
case cmDelEnd:
deleteRange(curPtr, lineEnd(curPtr), False);
break;
case cmDelLine:
deleteRange(lineStart(curPtr), nextLine(curPtr), False);
break;
case cmInsMode:
toggleInsMode();
break;
case cmStartSelect:
startSelect();
break;
case cmHideSelect:
hideSelect();
break;
case cmIndentMode:
autoIndent = Boolean(!autoIndent);
break;
case cmSelectAll:
setCurPtr(0, selectMode);
selectMode |= smExtend;
setCurPtr(bufLen, selectMode);
break;
default:
unlock();
return;
}
trackCursor(centerCursor);
unlock();
break;
}
break;
case evBroadcast:
switch( event.message.command )
{
case cmScrollBarChanged:
if ((event.message.infoPtr == hScrollBar) ||
(event.message.infoPtr == vScrollBar))
{
checkScrollBar( event, hScrollBar, delta.x );
checkScrollBar( event, vScrollBar, delta.y );
}
else
return;
break;
default:
return;
}
break;
}
clearEvent(event);
}