#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);
}