2000-07-04 20:36 UTC+0100 Victor Szakats <info@szelvesz.hu>
This commit is contained in:
@@ -1,8 +1,13 @@
|
||||
2000-07-24 13:59 GMT+3 Alexander Kresin <alex@belacy.belgorod.su>
|
||||
2000-07-04 20:36 UTC+0100 Victor Szakats <info@szelvesz.hu>
|
||||
|
||||
* source/rtl/teditor.prg
|
||||
* Formatting.
|
||||
|
||||
2000-07-04 13:59 GMT+3 Alexander Kresin <alex@belacy.belgorod.su>
|
||||
* contrib/rdd_ads/adsfunc.c
|
||||
* added full set of encryption/decryption functions
|
||||
|
||||
2000-07-24 13:59 GMT+3 Alexander Kresin <alex@belacy.belgorod.su>
|
||||
2000-07-04 13:59 GMT+3 Alexander Kresin <alex@belacy.belgorod.su>
|
||||
* include/hbapirdd.h
|
||||
* uncommented relational methods in functions table
|
||||
* source/rdd/dbcmd.c
|
||||
|
||||
@@ -44,46 +44,46 @@
|
||||
|
||||
CLASS TEditor
|
||||
|
||||
DATA cFile INIT "" // name of file being edited
|
||||
|
||||
DATA aText INIT {} // array with lines of text being edited
|
||||
DATA naTextLen INIT 0 // number of lines of text inside aText.
|
||||
|
||||
DATA nTop // boundaries of editor window, without box around
|
||||
DATA nLeft
|
||||
DATA nBottom
|
||||
DATA nRight
|
||||
|
||||
DATA nFirstCol INIT 1 // FirstCol/Row of current text visible inside editor window
|
||||
DATA nFirstRow INIT 1
|
||||
DATA nRow INIT 1 // Cursor position inside aText (nRow) and inside current line of text (nCol)
|
||||
DATA nCol INIT 1
|
||||
DATA nNumCols INIT 1 // How many columns / rows can be displayed inside editor window
|
||||
DATA nNumRows INIT 1
|
||||
DATA cFile INIT "" // name of file being edited
|
||||
|
||||
DATA aText INIT {} // array with lines of text being edited
|
||||
DATA naTextLen INIT 0 // number of lines of text inside aText.
|
||||
|
||||
DATA nTop // boundaries of editor window, without box around
|
||||
DATA nLeft
|
||||
DATA nBottom
|
||||
DATA nRight
|
||||
|
||||
DATA nFirstCol INIT 1 // FirstCol/Row of current text visible inside editor window
|
||||
DATA nFirstRow INIT 1
|
||||
DATA nRow INIT 1 // Cursor position inside aText (nRow) and inside current line of text (nCol)
|
||||
DATA nCol INIT 1
|
||||
DATA nNumCols INIT 1 // How many columns / rows can be displayed inside editor window
|
||||
DATA nNumRows INIT 1
|
||||
|
||||
DATA lInsert INIT .F. // Is editor in Insert mode or in Overstrike one?
|
||||
DATA nTabWidth INIT 8 // Size of Tab chars
|
||||
DATA nTabWidth INIT 8 // Size of Tab chars
|
||||
DATA lEditAllow INIT .T. // Are changes to text allowed?
|
||||
DATA lSaved INIT .F. // True if user exited editor with K_CTRL_W
|
||||
DATA lWordWrap INIT .F. // True if word wrapping is active
|
||||
DATA nWordWrapCol INIT 0 // At which column word wrapping occurs
|
||||
DATA nWordWrapCol INIT 0 // At which column word wrapping occurs
|
||||
|
||||
|
||||
METHOD New(cString, nTop, nLeft, nBottom,; // Converts a string to an array of strings splitting input string at EOL boundaries
|
||||
nRight, lEditMode, cUdF, nLineLength,;
|
||||
nTabSize)
|
||||
METHOD New( cString, nTop, nLeft, nBottom,; // Converts a string to an array of strings splitting input string at EOL boundaries
|
||||
nRight, lEditMode, cUdF, nLineLength,;
|
||||
nTabSize )
|
||||
|
||||
METHOD LoadFile(cFileName) // Load cFileName into active editor
|
||||
METHOD LoadFile( cFileName ) // Load cFileName into active editor
|
||||
METHOD SaveFile() // Save active file (not for MemoEdit() emulation)
|
||||
|
||||
METHOD AddLine(cLine, lSoftCR) // Add a new Line of text at end of current text
|
||||
METHOD InsertLine(cLine, lSoftCR, nRow) // Insert a line of text at a defined row
|
||||
METHOD RemoveLine(nRow) // Remove a line of text
|
||||
METHOD AddLine( cLine, lSoftCR ) // Add a new Line of text at end of current text
|
||||
METHOD InsertLine( cLine, lSoftCR, nRow ) // Insert a line of text at a defined row
|
||||
METHOD RemoveLine( nRow ) // Remove a line of text
|
||||
|
||||
METHOD GetLine(nRow) // Return line n of text
|
||||
METHOD LineLen(nRow) INLINE Len(::aText[nRow]:cText) // Return text length of line n
|
||||
METHOD SplitLine(nRow) // If a line of text is longer than nWordWrapCol divides it into multiple lines
|
||||
METHOD GotoLine(nRow) // Put line nRow at cursor position
|
||||
METHOD GetLine( nRow ) // Return line n of text
|
||||
METHOD LineLen( nRow ) INLINE Len( ::aText[ nRow ]:cText ) // Return text length of line n
|
||||
METHOD SplitLine( nRow ) // If a line of text is longer than nWordWrapCol divides it into multiple lines
|
||||
METHOD GotoLine( nRow ) // Put line nRow at cursor position
|
||||
|
||||
METHOD GetText() // Returns aText as a string (for MemoEdit())
|
||||
|
||||
@@ -91,27 +91,27 @@ CLASS TEditor
|
||||
METHOD RefreshLine() // Redraw a line
|
||||
METHOD RefreshColumn() // Redraw a column of text
|
||||
|
||||
METHOD MoveCursor(nKey) // Move cursor inside text / window (needs a movement key)
|
||||
METHOD InsertState(lInsState) // Changes lInsert value and insertion / overstrike mode of editor
|
||||
METHOD Edit(nPassedKey) // Handles input (can receive a key in which case handles only this key and then exits)
|
||||
METHOD MoveCursor( nKey ) // Move cursor inside text / window (needs a movement key)
|
||||
METHOD InsertState( lInsState ) // Changes lInsert value and insertion / overstrike mode of editor
|
||||
METHOD Edit( nPassedKey ) // Handles input (can receive a key in which case handles only this key and then exits)
|
||||
|
||||
ENDCLASS
|
||||
|
||||
|
||||
// Returns EOL char (be it either CR or LF or both)
|
||||
STATIC function WhichEOL(cString)
|
||||
STATIC function WhichEOL( cString )
|
||||
|
||||
local nCRPos := At(Chr(13), cString)
|
||||
local nLFPos := At(Chr(10), cString)
|
||||
LOCAL nCRPos := At( Chr( 13 ), cString )
|
||||
LOCAL nLFPos := At( Chr( 10 ), cString )
|
||||
|
||||
if nCRPos > 0 .AND. nLFPos == 0
|
||||
return Chr(13)
|
||||
return Chr( 13 )
|
||||
|
||||
elseif nCRPos == 0 .AND. nLFPos > 0
|
||||
return Chr(10)
|
||||
return Chr( 10 )
|
||||
|
||||
elseif nCRPos > 0 .AND. nLFPos == nCRPos + 1
|
||||
return Chr(13) + Chr(10)
|
||||
return Chr( 13 ) + Chr( 10 )
|
||||
|
||||
endif
|
||||
|
||||
@@ -119,62 +119,70 @@ return HB_OSNewLine()
|
||||
|
||||
|
||||
// Converts a string to an array of strings splitting input string at EOL boundaries
|
||||
STATIC function Text2Array(cString, nWordWrapCol)
|
||||
STATIC function Text2Array( cString, nWordWrapCol )
|
||||
|
||||
LOCAL cLine, nTokNum, aArray, cEOL, nEOLLen, nRetLen, ncSLen
|
||||
LOCAL nFirstSpace, cSplittedLine, nTokPos := 0
|
||||
LOCAL cLine
|
||||
LOCAL nTokNum
|
||||
LOCAL aArray
|
||||
LOCAL cEOL
|
||||
LOCAL nEOLLen
|
||||
LOCAL nRetLen
|
||||
LOCAL ncSLen
|
||||
LOCAL nFirstSpace
|
||||
LOCAL cSplittedLine
|
||||
LOCAL nTokPos := 0
|
||||
|
||||
nTokNum := 1
|
||||
aArray := {}
|
||||
|
||||
cEOL := WhichEOL(cString)
|
||||
nEOLLen := Len(cEOL)
|
||||
cEOL := WhichEOL( cString )
|
||||
nEOLLen := Len( cEOL )
|
||||
|
||||
nRetLen := 0
|
||||
ncSLen := Len(cString)
|
||||
ncSLen := Len( cString )
|
||||
|
||||
while nRetLen < ncSLen
|
||||
/* TOFIX: Note that __StrToken is not able to cope with delimiters longer than one char */
|
||||
// Dos - OS/2 - Windows have CRLF as EOL
|
||||
if nEOLLen > 1
|
||||
cLine := StrTran(__StrTkPtr(@cString, @nTokPos, cEOL), SubStr(cEOL, 2), "")
|
||||
cLine := StrTran( __StrTkPtr( @cString, @nTokPos, cEOL ), SubStr( cEOL, 2 ), "" )
|
||||
else
|
||||
cLine := __StrTkPtr(@cString, @nTokPos, cEOL)
|
||||
cLine := __StrTkPtr( @cString, @nTokPos, cEOL )
|
||||
endif
|
||||
nRetLen += Len(cLine) + nEOLLen
|
||||
nRetLen += Len( cLine ) + nEOLLen
|
||||
|
||||
if !nWordWrapCol == NIL .AND. Len(cLine) > nWordWrapCol
|
||||
if nWordWrapCol != NIL .AND. Len( cLine ) > nWordWrapCol
|
||||
|
||||
while !Empty(cLine)
|
||||
while !Empty( cLine )
|
||||
|
||||
// Split line at nWordWrapCol boundary
|
||||
if Len(cLine) > nWordWrapCol
|
||||
if Len( cLine ) > nWordWrapCol
|
||||
|
||||
nFirstSpace := nWordWrapCol
|
||||
while SubStr(cLine, --nFirstSpace, 1) <> " " .AND. nFirstSpace > 1
|
||||
while SubStr( cLine, --nFirstSpace, 1 ) <> " " .AND. nFirstSpace > 1
|
||||
enddo
|
||||
|
||||
if nFirstSpace > 1
|
||||
cSplittedLine := Left(cLine, nFirstSpace)
|
||||
cSplittedLine := Left( cLine, nFirstSpace )
|
||||
else
|
||||
cSplittedLine := Left(cLine, nWordWrapCol)
|
||||
cSplittedLine := Left( cLine, nWordWrapCol )
|
||||
endif
|
||||
|
||||
AAdd(aArray, TTextLine():New(cSplittedLine, .T.))
|
||||
AAdd( aArray, TTextLine():New( cSplittedLine, .T. ) )
|
||||
|
||||
else
|
||||
|
||||
// remainder of line is shorter than split point
|
||||
cSplittedLine := cLine
|
||||
AAdd(aArray, TTextLine():New(cSplittedLine, .F.))
|
||||
AAdd( aArray, TTextLine():New( cSplittedLine, .F. ) )
|
||||
|
||||
endif
|
||||
|
||||
cLine := Right(cLine, Len(cLine) - Len(cSplittedLine))
|
||||
cLine := Right( cLine, Len( cLine ) - Len( cSplittedLine ) )
|
||||
enddo
|
||||
|
||||
else
|
||||
AAdd(aArray, TTextLine():New(cLine, .F.))
|
||||
AAdd( aArray, TTextLine():New( cLine, .F. ) )
|
||||
|
||||
endif
|
||||
|
||||
@@ -190,21 +198,21 @@ METHOD GetText() CLASS TEditor
|
||||
LOCAL cEOL := HB_OSNewLine()
|
||||
|
||||
if ::lWordWrap
|
||||
AEval(::aText, {|cItem| cString += cItem:cText + iif(cItem:lSoftCR, "", cEOL) })
|
||||
AEval( ::aText, {| cItem | cString += cItem:cText + iif( cItem:lSoftCR, "", cEOL ) } )
|
||||
else
|
||||
AEval(::aText, {|cItem| cString += cItem:cText + cEOL })
|
||||
AEval( ::aText, {| cItem | cString += cItem:cText + cEOL } )
|
||||
endif
|
||||
|
||||
return cString
|
||||
|
||||
|
||||
METHOD New(cString, nTop, nLeft, nBottom, nRight, lEditMode, cUdF, nLineLength, nTabSize) CLASS TEditor
|
||||
METHOD New( cString, nTop, nLeft, nBottom, nRight, lEditMode, cUdF, nLineLength, nTabSize ) CLASS TEditor
|
||||
|
||||
::aText := Text2Array(cString, nLineLength)
|
||||
::naTextLen := Len(::aText)
|
||||
::aText := Text2Array( cString, nLineLength )
|
||||
::naTextLen := Len( ::aText )
|
||||
|
||||
if ::naTextLen == 0
|
||||
AAdd(::aText, TTextLine():New())
|
||||
AAdd( ::aText, TTextLine():New() )
|
||||
::naTextLen++
|
||||
endif
|
||||
|
||||
@@ -218,41 +226,41 @@ METHOD New(cString, nTop, nLeft, nBottom, nRight, lEditMode, cUdF, nLineLength,
|
||||
::nNumCols := nRight - nLeft + 1
|
||||
::nNumRows := nBottom - nTop + 1
|
||||
|
||||
if !lEditMode == NIL
|
||||
if lEditMode != NIL
|
||||
::lEditAllow := lEditMode
|
||||
endif
|
||||
|
||||
// set correct insert state
|
||||
if ::lEditAllow
|
||||
::InsertState(::lInsert)
|
||||
::InsertState( ::lInsert )
|
||||
endif
|
||||
|
||||
// is word wrap required?
|
||||
if !nLineLength == NIL
|
||||
if nLineLength != NIL
|
||||
::lWordWrap := .T.
|
||||
::nWordWrapCol := nLineLength
|
||||
endif
|
||||
|
||||
// how many spaces for each tab?
|
||||
if !nTabSize == NIL
|
||||
if nTabSize != NIL
|
||||
::nTabWidth := nTabSize
|
||||
endif
|
||||
|
||||
// Empty area of screen which will hold editor window
|
||||
Scroll(nTop, nLeft, nBottom, nRight)
|
||||
Scroll( nTop, nLeft, nBottom, nRight )
|
||||
|
||||
// Set cursor upper left corner
|
||||
SetPos(::nTop, ::nLeft)
|
||||
SetPos( ::nTop, ::nLeft )
|
||||
|
||||
return Self
|
||||
|
||||
|
||||
METHOD LoadFile(cFileName) CLASS TEditor
|
||||
METHOD LoadFile( cFileName ) CLASS TEditor
|
||||
|
||||
local cString
|
||||
LOCAL cString
|
||||
|
||||
::cFile := cFileName
|
||||
cString := MemoRead(cFileName)
|
||||
cString := MemoRead( cFileName )
|
||||
|
||||
return Self
|
||||
|
||||
@@ -263,38 +271,38 @@ return Self
|
||||
|
||||
|
||||
// Add a new Line of text at end of current text
|
||||
METHOD AddLine(cLine, lSoftCR) CLASS TEditor
|
||||
METHOD AddLine( cLine, lSoftCR ) CLASS TEditor
|
||||
|
||||
AAdd(::aText, TTextLine():New(cLine, lSoftCR))
|
||||
AAdd( ::aText, TTextLine():New( cLine, lSoftCR ) )
|
||||
::naTextLen++
|
||||
|
||||
return Self
|
||||
|
||||
|
||||
// Insert a line of text at a defined row
|
||||
METHOD InsertLine(cLine, lSoftCR, nRow) CLASS TEditor
|
||||
METHOD InsertLine( cLine, lSoftCR, nRow ) CLASS TEditor
|
||||
|
||||
::AddLine()
|
||||
AIns(::aText, nRow)
|
||||
::aText[nRow] := TTextLine():New(cLine, lSoftCR)
|
||||
AIns( ::aText, nRow )
|
||||
::aText[ nRow ] := TTextLine():New( cLine, lSoftCR )
|
||||
|
||||
return Self
|
||||
|
||||
|
||||
// Remove a line of text
|
||||
METHOD RemoveLine(nRow) CLASS TEditor
|
||||
METHOD RemoveLine( nRow ) CLASS TEditor
|
||||
|
||||
ADel(::aText, nRow)
|
||||
ASize(::aText, --::naTextLen)
|
||||
ADel( ::aText, nRow )
|
||||
ASize( ::aText, --::naTextLen )
|
||||
|
||||
return Self
|
||||
|
||||
|
||||
// Return line n of text
|
||||
METHOD GetLine(nRow) CLASS TEditor
|
||||
METHOD GetLine( nRow ) CLASS TEditor
|
||||
|
||||
if nRow <= ::naTextLen .AND. nRow > 0
|
||||
return ::aText[nRow]:cText
|
||||
return ::aText[ nRow ]:cText
|
||||
else
|
||||
return ""
|
||||
endif
|
||||
@@ -302,18 +310,18 @@ METHOD GetLine(nRow) CLASS TEditor
|
||||
return Self
|
||||
|
||||
|
||||
METHOD GotoLine(nRow) CLASS TEditor
|
||||
METHOD GotoLine( nRow ) CLASS TEditor
|
||||
|
||||
if nRow <= ::naTextLen .AND. nRow > 0
|
||||
|
||||
// I need to move cursor if is past requested line number and if requested line is
|
||||
// inside first screen of text otherwise ::nFirstRow would be wrong
|
||||
if nRow < ::nNumRows .AND. (::nTop + nRow) < Row()
|
||||
SetPos(::nTop + nRow, Col())
|
||||
if nRow < ::nNumRows .AND. ( ::nTop + nRow ) < Row()
|
||||
SetPos( ::nTop + nRow, Col() )
|
||||
endif
|
||||
|
||||
::nRow := nRow
|
||||
::nFirstRow := Max(1, nRow - (Row() - ::nTop))
|
||||
::nFirstRow := Max( 1, nRow - ( Row() - ::nTop ) )
|
||||
|
||||
::RefreshWindow()
|
||||
endif
|
||||
@@ -321,85 +329,92 @@ METHOD GotoLine(nRow) CLASS TEditor
|
||||
return Self
|
||||
|
||||
// Rebuild a long line from multiple short ones (wrapped at soft CR)
|
||||
STATIC function GetParagraph(oSelf, nRow)
|
||||
STATIC function GetParagraph( oSelf, nRow )
|
||||
|
||||
local cLine := ""
|
||||
LOCAL cLine := ""
|
||||
|
||||
while oSelf:aText[nRow]:lSoftCR
|
||||
cLine += oSelf:aText[nRow]:cText
|
||||
while oSelf:aText[ nRow ]:lSoftCR
|
||||
cLine += oSelf:aText[ nRow ]:cText
|
||||
// I don't need to increment nRow since I'm removing lines, ie line n is
|
||||
// a different line each time I add it to cLine
|
||||
oSelf:RemoveLine(nRow)
|
||||
oSelf:RemoveLine( nRow )
|
||||
enddo
|
||||
|
||||
// Last line, or only one line
|
||||
cLine += oSelf:aText[nRow]:cText
|
||||
oSelf:RemoveLine(nRow)
|
||||
cLine += oSelf:aText[ nRow ]:cText
|
||||
oSelf:RemoveLine( nRow )
|
||||
|
||||
return cLine
|
||||
|
||||
|
||||
// If a line of text is longer than nWordWrapCol divides it into multiple lines,
|
||||
// Used during text editing to reflow a paragraph
|
||||
METHOD SplitLine(nRow) CLASS TEditor
|
||||
METHOD SplitLine( nRow ) CLASS TEditor
|
||||
|
||||
local nFirstSpace, cLine, cSplittedLine, nStartRow
|
||||
local nOCol, nORow, lMoveToNextLine, nPosInWord, nI
|
||||
LOCAL nFirstSpace
|
||||
LOCAL cLine
|
||||
LOCAL cSplittedLine
|
||||
LOCAL nStartRow
|
||||
LOCAL nOCol
|
||||
LOCAL nORow
|
||||
LOCAL lMoveToNextLine
|
||||
LOCAL nPosInWord
|
||||
LOCAL nI
|
||||
|
||||
// Do something only if Word Wrapping is on
|
||||
if ::lWordWrap .AND. (::LineLen(nRow) > ::nWordWrapCol)
|
||||
if ::lWordWrap .AND. ( ::LineLen( nRow ) > ::nWordWrapCol )
|
||||
|
||||
nOCol := Col()
|
||||
nORow := Row()
|
||||
|
||||
// Move cursor to next line if you will move the word which I'm over to next line
|
||||
// ie, since word wrapping happens at spaces if first space is behind cursor
|
||||
lMoveToNextLine := Rat(" ", RTrim(::GetLine(nRow))) < ::nCol
|
||||
nPosInWord := Len(::GetLine(nRow)) - ::nCol
|
||||
lMoveToNextLine := RAt( " ", RTrim( ::GetLine( nRow ) ) ) < ::nCol
|
||||
nPosInWord := Len( ::GetLine( nRow ) ) - ::nCol
|
||||
|
||||
nStartRow := nRow
|
||||
cLine := GetParagraph(Self, nRow)
|
||||
cLine := GetParagraph( Self, nRow )
|
||||
|
||||
while !Empty(cLine)
|
||||
while !Empty( cLine )
|
||||
|
||||
if Len(cLine) > ::nWordWrapCol
|
||||
if Len( cLine ) > ::nWordWrapCol
|
||||
nFirstSpace := ::nWordWrapCol
|
||||
|
||||
// Split line at fist space before current position
|
||||
while SubStr(cLine, --nFirstSpace, 1) <> " " .AND. nFirstSpace > 1
|
||||
while SubStr( cLine, --nFirstSpace, 1 ) <> " " .AND. nFirstSpace > 1
|
||||
enddo
|
||||
|
||||
// If there is a space before beginning of line split there
|
||||
if nFirstSpace > 1
|
||||
cSplittedLine := Left(cLine, nFirstSpace)
|
||||
cSplittedLine := Left( cLine, nFirstSpace )
|
||||
else
|
||||
// else split at current cursor position
|
||||
cSplittedLine := Left(cLine, ::nCol - 1)
|
||||
cSplittedLine := Left( cLine, ::nCol - 1 )
|
||||
endif
|
||||
|
||||
::InsertLine(cSplittedLine, .T., nStartRow++)
|
||||
::InsertLine( cSplittedLine, .T., nStartRow++ )
|
||||
|
||||
else
|
||||
// remainder of line
|
||||
cSplittedLine := cLine
|
||||
::InsertLine(cSplittedLine, .F., nStartRow++)
|
||||
::InsertLine( cSplittedLine, .F., nStartRow++ )
|
||||
endif
|
||||
|
||||
cLine := Right(cLine, Len(cLine) - Len(cSplittedLine))
|
||||
cLine := Right( cLine, Len( cLine ) - Len( cSplittedLine ) )
|
||||
enddo
|
||||
|
||||
if lMoveToNextLine
|
||||
::MoveCursor(K_DOWN)
|
||||
::MoveCursor(K_HOME)
|
||||
::MoVeCursor(K_CTRL_RIGHT)
|
||||
::MoveCursor( K_DOWN )
|
||||
::MoveCursor( K_HOME )
|
||||
::MoveCursor( K_CTRL_RIGHT )
|
||||
if nPosInWord > 0
|
||||
// from 0 since I have to take into account previous K_CTRL_RIGHT which moves me past end of word
|
||||
for nI := 0 to nPosInWord
|
||||
::MoveCursor(K_LEFT)
|
||||
::MoveCursor( K_LEFT )
|
||||
next
|
||||
endif
|
||||
else
|
||||
SetPos(nORow, nOCol)
|
||||
SetPos( nORow, nOCol )
|
||||
endif
|
||||
::RefreshWindow()
|
||||
endif
|
||||
@@ -410,23 +425,26 @@ return Self
|
||||
// Redraws a screenfull of text
|
||||
METHOD RefreshWindow() CLASS TEditor
|
||||
|
||||
LOCAL i, nOCol, nORow, nOCur
|
||||
LOCAL i
|
||||
LOCAL nOCol
|
||||
LOCAL nORow
|
||||
LOCAL nOCur
|
||||
|
||||
nOCol := Col()
|
||||
nORow := Row()
|
||||
nOCur := SetCursor(SC_NONE)
|
||||
nOCur := SetCursor( SC_NONE )
|
||||
|
||||
for i := 0 to Min(::nNumRows - 1, ::naTextLen - 1)
|
||||
DispOutAt(::nTop + i, ::nLeft, PadR(SubStr(::GetLine(::nFirstRow + i), ::nFirstCol, ::nNumCols), ::nNumCols, " "))
|
||||
for i := 0 to Min( ::nNumRows - 1, ::naTextLen - 1 )
|
||||
DispOutAt( ::nTop + i, ::nLeft, PadR( SubStr( ::GetLine( ::nFirstRow + i ), ::nFirstCol, ::nNumCols ), ::nNumCols, " " ) )
|
||||
next
|
||||
|
||||
// Clear rest of editor window (needed when deleting lines of text)
|
||||
if ::naTextLen < ::nNumRows
|
||||
Scroll(::nTop + ::naTextLen, ::nLeft, ::nBottom, ::nRight)
|
||||
Scroll( ::nTop + ::naTextLen, ::nLeft, ::nBottom, ::nRight )
|
||||
endif
|
||||
|
||||
SetCursor(nOCur)
|
||||
SetPos(nORow, nOCol)
|
||||
SetCursor( nOCur )
|
||||
SetPos( nORow, nOCol )
|
||||
|
||||
return Self
|
||||
|
||||
@@ -434,14 +452,15 @@ return Self
|
||||
// Redraws current screen line
|
||||
METHOD RefreshLine() CLASS TEditor
|
||||
|
||||
LOCAL nOCol, nORow
|
||||
LOCAL nOCol
|
||||
LOCAL nORow
|
||||
|
||||
nOCol := Col()
|
||||
nORow := Row()
|
||||
|
||||
DispOutAt(Row(), ::nLeft, PadR(SubStr(::GetLine(::nRow), ::nFirstCol, ::nNumCols), ::nNumCols, " "))
|
||||
DispOutAt( Row(), ::nLeft, PadR( SubStr( ::GetLine( ::nRow ), ::nFirstCol, ::nNumCols ), ::nNumCols, " " ) )
|
||||
|
||||
SetPos(nORow, nOCol)
|
||||
SetPos( nORow, nOCol )
|
||||
|
||||
return Self
|
||||
|
||||
@@ -449,38 +468,41 @@ return Self
|
||||
// Refreshes only one screen column of text (for Left() and Right() movements)
|
||||
METHOD RefreshColumn() CLASS TEditor
|
||||
|
||||
LOCAL i, nOCol, nORow, nOCur
|
||||
LOCAL i
|
||||
LOCAL nOCol
|
||||
LOCAL nORow
|
||||
LOCAL nOCur
|
||||
|
||||
nOCol := Col()
|
||||
nORow := Row()
|
||||
nOCur := SetCursor(SC_NONE)
|
||||
nOCur := SetCursor( SC_NONE )
|
||||
|
||||
for i := 0 to Min(::nNumRows - 1, ::naTextLen - 1)
|
||||
DispOutAt(::nTop + i, nOCol, SubStr(::GetLine(::nFirstRow + i), ::nCol, 1))
|
||||
for i := 0 to Min( ::nNumRows - 1, ::naTextLen - 1 )
|
||||
DispOutAt( ::nTop + i, nOCol, SubStr( ::GetLine( ::nFirstRow + i ), ::nCol, 1 ) )
|
||||
next
|
||||
|
||||
SetCursor(nOCur)
|
||||
SetPos(nORow, nOCol)
|
||||
SetCursor( nOCur )
|
||||
SetPos( nORow, nOCol )
|
||||
|
||||
return Self
|
||||
|
||||
|
||||
// Handles cursor movements inside text array
|
||||
METHOD MoveCursor(nKey) CLASS TEditor
|
||||
METHOD MoveCursor( nKey ) CLASS TEditor
|
||||
|
||||
LOCAL lMoveKey := .T.
|
||||
|
||||
do case
|
||||
case (nKey == K_DOWN)
|
||||
case nKey == K_DOWN
|
||||
if !::lEditAllow
|
||||
while Row() < ::nBottom .AND. ::nRow < ::naTextLen
|
||||
::nRow++
|
||||
SetPos(Row() + 1, Col())
|
||||
SetPos( Row() + 1, Col() )
|
||||
enddo
|
||||
endif
|
||||
if Row() == ::nBottom
|
||||
if ::nRow < ::naTextLen
|
||||
Scroll(::nTop, ::nLeft, ::nBottom, ::nRight, 1)
|
||||
Scroll( ::nTop, ::nLeft, ::nBottom, ::nRight, 1 )
|
||||
::nFirstRow++
|
||||
::nRow++
|
||||
::RefreshLine()
|
||||
@@ -488,53 +510,53 @@ METHOD MoveCursor(nKey) CLASS TEditor
|
||||
else
|
||||
if ::nRow < ::naTextLen
|
||||
::nRow++
|
||||
SetPos(Row() + 1, Col())
|
||||
SetPos( Row() + 1, Col() )
|
||||
endif
|
||||
endif
|
||||
|
||||
case (nKey == K_PGDN)
|
||||
case nKey == K_PGDN
|
||||
if ::nRow + ::nNumRows < ::naTextLen
|
||||
::nRow += ::nNumRows
|
||||
::nFirstRow += ::nNumRows
|
||||
if ::nFirstRow + ::nNumRows > ::naTextLen
|
||||
::nFirstRow -= ((::nFirstRow + ::nNumRows) - ::naTextLen) + 1
|
||||
::nFirstRow -= ( ( ::nFirstRow + ::nNumRows ) - ::naTextLen ) + 1
|
||||
endif
|
||||
else
|
||||
::nFirstRow := Max(::naTextLen - ::nNumRows + 1, 1)
|
||||
::nFirstRow := Max( ::naTextLen - ::nNumRows + 1, 1 )
|
||||
::nRow := ::naTextLen
|
||||
SetPos(Min(::nTop + ::naTextLen - 1, ::nBottom), Col())
|
||||
SetPos( Min( ::nTop + ::naTextLen - 1, ::nBottom ), Col() )
|
||||
endif
|
||||
::RefreshWindow()
|
||||
|
||||
case (nKey == K_CTRL_PGDN)
|
||||
case nKey == K_CTRL_PGDN
|
||||
::nRow := ::naTextLen
|
||||
::nCol := Max(::LineLen(::nRow), 1)
|
||||
::nFirstRow := Max(::naTextLen - ::nNumRows + 1, 1)
|
||||
::nFirstCol := Max(::nCol - ::nNumCols + 1, 1)
|
||||
SetPos(Min(::nBottom, ::naTextLen), Min(::nLeft + ::nCol - 1, ::nRight))
|
||||
::nCol := Max( ::LineLen( ::nRow ), 1 )
|
||||
::nFirstRow := Max( ::naTextLen - ::nNumRows + 1, 1 )
|
||||
::nFirstCol := Max( ::nCol - ::nNumCols + 1, 1 )
|
||||
SetPos( Min( ::nBottom, ::naTextLen ), Min( ::nLeft + ::nCol - 1, ::nRight ) )
|
||||
::RefreshWindow()
|
||||
|
||||
case (nKey == K_UP)
|
||||
if !::lEditAllow
|
||||
case nKey == K_UP
|
||||
if ! ::lEditAllow
|
||||
while Row() > ::nTop .AND. ::nRow > 1
|
||||
::nRow--
|
||||
SetPos(Row() -1, Col())
|
||||
SetPos( Row() - 1, Col() )
|
||||
enddo
|
||||
endif
|
||||
if Row() == ::nTop
|
||||
if ::nRow > 1
|
||||
Scroll(::nTop, ::nLeft, ::nBottom, ::nRight, -1)
|
||||
Scroll( ::nTop, ::nLeft, ::nBottom, ::nRight, -1 )
|
||||
::nFirstRow--
|
||||
::nRow--
|
||||
::RefreshLine()
|
||||
endif
|
||||
else
|
||||
::nRow--
|
||||
SetPos(Row() - 1, Col())
|
||||
SetPos( Row() - 1, Col() )
|
||||
endif
|
||||
|
||||
case (nKey == K_PGUP)
|
||||
if (::nRow - ::nNumRows) > 1
|
||||
case nKey == K_PGUP
|
||||
if ( ::nRow - ::nNumRows ) > 1
|
||||
::nRow -= ::nNumRows
|
||||
::nFirstRow -= ::nNumRows
|
||||
if ::nFirstRow < 1
|
||||
@@ -543,89 +565,89 @@ METHOD MoveCursor(nKey) CLASS TEditor
|
||||
else
|
||||
::nFirstRow := 1
|
||||
::nRow := 1
|
||||
SetPos(::nTop, Col())
|
||||
SetPos( ::nTop, Col() )
|
||||
endif
|
||||
::RefreshWindow()
|
||||
|
||||
case (nKey == K_CTRL_PGUP)
|
||||
case nKey == K_CTRL_PGUP
|
||||
::nRow := 1
|
||||
::nCol := 1
|
||||
::nFirstCol := 1
|
||||
::nFirstRow := 1
|
||||
SetPos(::nTop, ::nLeft)
|
||||
SetPos( ::nTop, ::nLeft )
|
||||
::RefreshWindow()
|
||||
|
||||
case (nKey == K_RIGHT)
|
||||
case nKey == K_RIGHT
|
||||
if Col() == ::nRight
|
||||
if ::nCol <= iif(::lWordWrap, ::nWordWrapCol, ::LineLen(::nRow))
|
||||
Scroll(::nTop, ::nLeft, ::nBottom, ::nRight,, 1)
|
||||
if ::nCol <= iif( ::lWordWrap, ::nWordWrapCol, ::LineLen( ::nRow ) )
|
||||
Scroll( ::nTop, ::nLeft, ::nBottom, ::nRight,, 1 )
|
||||
::nFirstCol++
|
||||
::nCol++
|
||||
::RefreshColumn()
|
||||
endif
|
||||
else
|
||||
::nCol++
|
||||
SetPos(Row(), Col() + 1)
|
||||
SetPos( Row(), Col() + 1 )
|
||||
endif
|
||||
|
||||
case (nKey == K_CTRL_RIGHT)
|
||||
case nKey == K_CTRL_RIGHT
|
||||
// NOTE: should be faster without call to ::GetLine()
|
||||
while ::nCol <= iif(::lWordWrap, Min(::nWordWrapCol, ::LineLen(::nRow)), ::LineLen(::nRow)) .AND. SubStr(::aText[::nRow]:cText, ::nCol, 1) <> " "
|
||||
::MoveCursor(K_RIGHT)
|
||||
while ::nCol <= iif( ::lWordWrap, Min( ::nWordWrapCol, ::LineLen( ::nRow ) ), ::LineLen( ::nRow ) ) .AND. SubStr( ::aText[ ::nRow ]:cText, ::nCol, 1 ) <> " "
|
||||
::MoveCursor( K_RIGHT )
|
||||
enddo
|
||||
while ::nCol <= iif(::lWordWrap, Min(::nWordWrapCol, ::LineLen(::nRow)), ::LineLen(::nRow)) .AND. SubStr(::aText[::nRow]:cText, ::nCol, 1) == " "
|
||||
::MoveCursor(K_RIGHT)
|
||||
while ::nCol <= iif( ::lWordWrap, Min( ::nWordWrapCol, ::LineLen( ::nRow ) ), ::LineLen( ::nRow ) ) .AND. SubStr( ::aText[ ::nRow ]:cText, ::nCol, 1 ) == " "
|
||||
::MoveCursor( K_RIGHT )
|
||||
enddo
|
||||
|
||||
case (nKey == K_LEFT)
|
||||
case nKey == K_LEFT
|
||||
if Col() == ::nLeft
|
||||
if ::nCol > 1
|
||||
Scroll(::nTop, ::nLeft, ::nBottom, ::nRight,, -1)
|
||||
Scroll( ::nTop, ::nLeft, ::nBottom, ::nRight,, -1 )
|
||||
::nFirstCol--
|
||||
::nCol--
|
||||
::RefreshColumn()
|
||||
endif
|
||||
else
|
||||
::nCol--
|
||||
SetPos(Row(), Col() - 1)
|
||||
SetPos( Row(), Col() - 1 )
|
||||
endif
|
||||
|
||||
case (nKey == K_CTRL_LEFT)
|
||||
while ::nCol > 1 .AND. SubStr(::aText[::nRow]:cText, ::nCol, 1) <> " "
|
||||
::MoveCursor(K_LEFT)
|
||||
case nKey == K_CTRL_LEFT
|
||||
while ::nCol > 1 .AND. SubStr( ::aText[ ::nRow ]:cText, ::nCol, 1 ) <> " "
|
||||
::MoveCursor( K_LEFT )
|
||||
enddo
|
||||
while ::nCol > 1 .AND. SubStr(::aText[::nRow]:cText, ::nCol, 1) == " "
|
||||
::MoveCursor(K_LEFT)
|
||||
while ::nCol > 1 .AND. SubStr( ::aText[ ::nRow ]:cText, ::nCol, 1 ) == " "
|
||||
::MoveCursor( K_LEFT )
|
||||
enddo
|
||||
|
||||
case (nKey == K_HOME)
|
||||
case nKey == K_HOME
|
||||
::nCol := 1
|
||||
::nFirstCol := 1
|
||||
SetPos(Row(), ::nLeft)
|
||||
SetPos( Row(), ::nLeft )
|
||||
::RefreshWindow()
|
||||
|
||||
case (nKey == K_CTRL_HOME)
|
||||
case nKey == K_CTRL_HOME
|
||||
::nCol := 1
|
||||
::nFirstCol := 1
|
||||
::nRow -= (Row() - ::nTop)
|
||||
SetPos(::nTop, ::nLeft)
|
||||
::nRow -= ( Row() - ::nTop )
|
||||
SetPos( ::nTop, ::nLeft )
|
||||
::RefreshWindow()
|
||||
|
||||
case (nKey == K_END)
|
||||
case nKey == K_END
|
||||
// Empty lines have 0 len
|
||||
::nCol := Max(::LineLen(::nRow), 1)
|
||||
::nFirstCol := Max(::nCol - ::nNumCols + 1, 1)
|
||||
SetPos(Row(), Min(::nLeft + ::nCol - 1, ::nRight))
|
||||
::nCol := Max( ::LineLen( ::nRow ), 1 )
|
||||
::nFirstCol := Max( ::nCol - ::nNumCols + 1, 1 )
|
||||
SetPos( Row(), Min( ::nLeft + ::nCol - 1, ::nRight ) )
|
||||
::RefreshWindow()
|
||||
|
||||
case (nKey == K_CTRL_END)
|
||||
::nRow += (::nBottom - Row())
|
||||
case nKey == K_CTRL_END
|
||||
::nRow += ::nBottom - Row()
|
||||
if ::nRow > ::naTextLen
|
||||
::nRow := ::naTextLen
|
||||
endif
|
||||
::nCol := Max(::LineLen(::nRow), 1)
|
||||
::nFirstCol := Max(::nCol - ::nNumCols + 1, 1)
|
||||
SetPos(Min(::nBottom, ::naTextLen), Min(::nLeft + ::nCol - 1, ::nRight))
|
||||
::nCol := Max( ::LineLen( ::nRow ), 1 )
|
||||
::nFirstCol := Max( ::nCol - ::nNumCols + 1, 1 )
|
||||
SetPos( Min( ::nBottom, ::naTextLen ), Min( ::nLeft + ::nCol - 1, ::nRight ) )
|
||||
::RefreshWindow()
|
||||
|
||||
otherwise
|
||||
@@ -637,35 +659,37 @@ return lMoveKey
|
||||
|
||||
|
||||
// Changes lInsert value and insertion / overstrike mode of editor
|
||||
METHOD InsertState(lInsState) CLASS TEditor
|
||||
METHOD InsertState( lInsState ) CLASS TEditor
|
||||
|
||||
::lInsert := lInsState
|
||||
SET(_SET_INSERT, lInsState)
|
||||
SET( _SET_INSERT, lInsState )
|
||||
|
||||
return Self
|
||||
|
||||
|
||||
// if editing isn't allowed we enter this loop which
|
||||
// handles only movement keys and discards all the others
|
||||
STATIC procedure BrowseText(oSelf)
|
||||
STATIC procedure BrowseText( oSelf )
|
||||
|
||||
LOCAL nKey
|
||||
|
||||
while (nKey := InKey(0)) <> K_ESC
|
||||
oSelf:MoveCursor(nKey)
|
||||
while ( nKey := InKey( 0 ) ) <> K_ESC
|
||||
oSelf:MoveCursor( nKey )
|
||||
enddo
|
||||
|
||||
return
|
||||
|
||||
|
||||
// Edits text
|
||||
METHOD Edit(nPassedKey) CLASS TEditor
|
||||
METHOD Edit( nPassedKey ) CLASS TEditor
|
||||
|
||||
LOCAL i, nKey, lOldInsert
|
||||
LOCAL i
|
||||
LOCAL nKey
|
||||
LOCAL lOldInsert
|
||||
LOCAL lKeepGoing := .T.
|
||||
|
||||
if !::lEditAllow
|
||||
BrowseText(Self)
|
||||
if ! ::lEditAllow
|
||||
BrowseText( Self )
|
||||
|
||||
else
|
||||
|
||||
@@ -673,102 +697,102 @@ METHOD Edit(nPassedKey) CLASS TEditor
|
||||
|
||||
// If I've been called with a key already preset, evaluate this key and then exit
|
||||
if nPassedKey == NIL
|
||||
nKey := InKey(0)
|
||||
nKey := InKey( 0 )
|
||||
else
|
||||
lKeepGoing := .F.
|
||||
nKey := nPassedKey
|
||||
endif
|
||||
|
||||
do case
|
||||
case (nKey >= 32 .AND. nKey <= 255)
|
||||
case nKey >= 32 .AND. nKey <= 255
|
||||
// If I'm past EOL I need to add as much spaces as I need to reach ::nCol
|
||||
if ::nCol > ::LineLen(::nRow)
|
||||
::aText[::nRow]:cText += Space(::nCol - ::LineLen(::nRow))
|
||||
if ::nCol > ::LineLen( ::nRow )
|
||||
::aText[ ::nRow ]:cText += Space( ::nCol - ::LineLen( ::nRow ) )
|
||||
endif
|
||||
// insert char if in insert mode or at end of current line
|
||||
if ::lInsert .OR. (::nCol > ::LineLen(::nRow))
|
||||
::aText[::nRow]:cText := Stuff(::aText[::nRow]:cText, ::nCol, 0, Chr(nKey))
|
||||
if ::lInsert .OR. ( ::nCol > ::LineLen( ::nRow ) )
|
||||
::aText[ ::nRow ]:cText := Stuff( ::aText[ ::nRow ]:cText, ::nCol, 0, Chr( nKey ) )
|
||||
else
|
||||
::aText[::nRow]:cText := Stuff(::aText[::nRow]:cText, ::nCol, 1, Chr(nKey))
|
||||
::aText[ ::nRow ]:cText := Stuff( ::aText[ ::nRow ]:cText, ::nCol, 1, Chr( nKey ) )
|
||||
endif
|
||||
::MoveCursor(K_RIGHT)
|
||||
::MoveCursor( K_RIGHT )
|
||||
::RefreshLine()
|
||||
::SplitLine(::nRow)
|
||||
::SplitLine( ::nRow )
|
||||
|
||||
case (nKey == K_RETURN)
|
||||
case nKey == K_RETURN
|
||||
if ::lInsert .OR. ::nRow == ::naTextLen
|
||||
if ::LineLen(::nRow) > 0
|
||||
if ::LineLen( ::nRow ) > 0
|
||||
// Split current line at cursor position
|
||||
::InsertLine(Right(::aText[::nRow]:cText, ::LineLen(::nRow) - ::nCol + 1), ::aText[::nRow]:lSoftCR, ::nRow + 1)
|
||||
::aText[::nRow]:cText := Left(::aText[::nRow]:cText, ::nCol - 1)
|
||||
::InsertLine( Right( ::aText[ ::nRow ]:cText, ::LineLen( ::nRow ) - ::nCol + 1 ), ::aText[ ::nRow ]:lSoftCR, ::nRow + 1 )
|
||||
::aText[ ::nRow ]:cText := Left( ::aText[ ::nRow ]:cText, ::nCol - 1 )
|
||||
if ::lWordWrap
|
||||
::aText[::nRow]:lSoftCR := .F.
|
||||
::aText[ ::nRow ]:lSoftCR := .F.
|
||||
endif
|
||||
else
|
||||
::InsertLine("", .F., ::nRow + 1)
|
||||
::InsertLine( "", .F., ::nRow + 1 )
|
||||
endif
|
||||
endif
|
||||
::MoveCursor(K_DOWN)
|
||||
::MoveCursor(K_HOME)
|
||||
::MoveCursor( K_DOWN )
|
||||
::MoveCursor( K_HOME )
|
||||
|
||||
case (nKey == K_INS)
|
||||
::InsertState(!::lInsert)
|
||||
case nKey == K_INS
|
||||
::InsertState( !::lInsert )
|
||||
|
||||
case (nKey == K_DEL)
|
||||
::aText[::nRow]:cText := Stuff(::aText[::nRow]:cText, ::nCol, 1, "")
|
||||
case nKey == K_DEL
|
||||
::aText[ ::nRow ]:cText := Stuff( ::aText[ ::nRow ]:cText, ::nCol, 1, "" )
|
||||
::RefreshLine()
|
||||
if ::LineLen(::nRow) == 0
|
||||
::Edit(K_CTRL_Y)
|
||||
if ::LineLen( ::nRow ) == 0
|
||||
::Edit( K_CTRL_Y )
|
||||
endif
|
||||
|
||||
case (nKey == K_TAB)
|
||||
case nKey == K_TAB
|
||||
// insert char if in insert mode or at end of current line
|
||||
if ::lInsert .OR. (::nCol == ::LineLen(::nRow))
|
||||
::aText[::nRow]:cText := Stuff(::aText[::nRow]:cText, ::nCol, 0, Space(::nTabWidth))
|
||||
if ::lInsert .OR. ( ::nCol == ::LineLen( ::nRow ) )
|
||||
::aText[ ::nRow ]:cText := Stuff( ::aText[ ::nRow ]:cText, ::nCol, 0, Space( ::nTabWidth ) )
|
||||
endif
|
||||
for i := 1 to ::nTabWidth
|
||||
::MoveCursor(K_RIGHT)
|
||||
::MoveCursor( K_RIGHT )
|
||||
next
|
||||
::RefreshLine()
|
||||
|
||||
case (nKey == K_BS)
|
||||
case nKey == K_BS
|
||||
// delete previous character
|
||||
::aText[::nRow]:cText := Stuff(::aText[::nRow]:cText, --::nCol, 1, "")
|
||||
::aText[ ::nRow ]:cText := Stuff( ::aText[ ::nRow ]:cText, --::nCol, 1, "" )
|
||||
// correct column position for next call to MoveCursor()
|
||||
::nCol++
|
||||
::MoveCursor(K_LEFT)
|
||||
::MoveCursor( K_LEFT )
|
||||
::RefreshLine()
|
||||
|
||||
case (nKey == K_CTRL_Y)
|
||||
case nKey == K_CTRL_Y
|
||||
if ::naTextLen > 1
|
||||
::RemoveLine(::nRow)
|
||||
::RemoveLine( ::nRow )
|
||||
// if we have less lines of text than our current position, up one line
|
||||
if ::nRow > ::naTextLen
|
||||
::nRow := Max(::nRow - 1, 1)
|
||||
::nRow := Max( ::nRow - 1, 1 )
|
||||
// if our position on screen exceeds text length, up one row
|
||||
if (::nFirstRow + ::nNumRows - 1) > ::naTextLen
|
||||
SetPos(Max(Row() -1, ::nTop), Col())
|
||||
if ( ::nFirstRow + ::nNumRows - 1 ) > ::naTextLen
|
||||
SetPos( Max( Row() - 1, ::nTop ), Col() )
|
||||
endif
|
||||
// if first line of displayed text is less than length of text
|
||||
if ::nFirstRow > ::naTextLen
|
||||
::nFirstRow := Max(::nFirstRow - 1, 1)
|
||||
::nFirstRow := Max( ::nFirstRow - 1, 1 )
|
||||
endif
|
||||
endif
|
||||
::RefreshWindow()
|
||||
else
|
||||
::aText[::nRow]:cText := ""
|
||||
::aText[ ::nRow ]:cText := ""
|
||||
::RefreshLine()
|
||||
endif
|
||||
|
||||
case (::MoveCursor(nKey))
|
||||
case ::MoveCursor( nKey )
|
||||
// if it's a movement key ::MoveCursor() handles it
|
||||
|
||||
case (nKey == K_ALT_W)
|
||||
case nKey == K_ALT_W
|
||||
/* TOFIX: Not clipper compatible */
|
||||
::lSaved := .T.
|
||||
lKeepGoing := .F.
|
||||
|
||||
case (nKey == K_ESC)
|
||||
case nKey == K_ESC
|
||||
lKeepGoing := .F.
|
||||
|
||||
otherwise
|
||||
|
||||
Reference in New Issue
Block a user