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