From e15730ef43bbb6d80846591fb031b88e3135a9de Mon Sep 17 00:00:00 2001 From: Maurilio Longo Date: Fri, 5 May 2000 08:51:09 +0000 Subject: [PATCH] 20000505-10:47 GMT+2 Maurilio Longo --- harbour/ChangeLog | 5 + harbour/source/rtl/teditor.prg | 549 --------------------------------- 2 files changed, 5 insertions(+), 549 deletions(-) diff --git a/harbour/ChangeLog b/harbour/ChangeLog index 858e6704f0..97392481f9 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -1,3 +1,8 @@ +20000505-10:47 GMT+2 Maurilio Longo + + * source/rtl/teditor.prg + ! cvs completely broke source file adding Chr(13) chars allover + 20000504-23:00 GMT-8 Ron Pinkas * source/compiler/cmdcheck.c diff --git a/harbour/source/rtl/teditor.prg b/harbour/source/rtl/teditor.prg index 08adf56986..35138016be 100644 --- a/harbour/source/rtl/teditor.prg +++ b/harbour/source/rtl/teditor.prg @@ -1,1100 +1,551 @@ /* - * $Id$ - */ - - /* - * Harbour Project source code: - * Editor Class (base for Memoedit(), debugger, etc.) - * - * Copyright 2000 Maurilio Longo - * www - http://www.harbour-project.org - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version, with one exception: - * - * The exception is that if you link the Harbour Runtime Library (HRL) - * and/or the Harbour Virtual Machine (HVM) with other files to produce - * an executable, this does not by itself cause the resulting executable - * to be covered by the GNU General Public License. Your use of that - * executable is in no way restricted on account of linking the HRL - * and/or HVM code into it. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA (or visit - * their web site at http://www.gnu.org/). - * - */ - - /* TODO: Very minimal and little tested. To finish and refine */ - - #include "hbclass.ch" - #include "error.ch" - #include "fileio.ch" - #include "inkey.ch" - #include "setcurs.ch" - - 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 lInsert INIT .F. // Is editor in Insert mode or in Overstrike one? - 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 - - METHOD New(cString, nTop, nLeft, nBottom, nRight, lEditMode, cUdF, nLineLength, nTabSize) - METHOD GetText() // Returns aText as a string (for MemoEdit()) - METHOD RefreshWindow() - METHOD RefreshLine() - METHOD RefreshColumn() - METHOD MoveCursor(nKey) - METHOD InsertState(lInsState) // Changes lInsert value and insertion / overstrike mode of editor - METHOD Edit(nPassedKey) - - ENDCLASS - - /* - METHOD New(cFile, nTop, nLeft, nBottom, nRight) CLASS TEditor - - LOCAL oFile := TFileRead():New(cFile) - - oFile:Open() - if oFile:Error() - Alert(oFile:ErrorMsg("FileRead: ")) - else - while oFile:MoreToRead() - AAdd(::aText, oFile:ReadLine()) - end while - oFile:Close() - - return Self - */ - - - STATIC function Text2Array(cString) - - LOCAL cLine, i, nLastEOL, aArray - - nLastEOL := 1 - aArray := {} - - while nLastEOL > 0 - cLine := Left(cString, (nLastEOL := at(HB_OSNewLine(), cString)) - 1) - cLine := StrTran(cLine, HB_OSNewLine(), "") - if nLastEOL > 0 - AAdd(aArray, cLine) - nLastEOL += Len(HB_OSNewLine()) - cString := SubStr(cString, nLastEOL) - endif - enddo - - return aArray - - - METHOD GetText() CLASS TEditor - - LOCAL cString := "" - - AEval(::aText, {|cItem| cString += cItem + HB_OSNewLine() }) - - return cString - - - METHOD New(cString, nTop, nLeft, nBottom, nRight, lEditMode, cUdF, nLineLength, nTabSize) CLASS TEditor - - ::aText := Text2Array(cString) - ::naTextLen := Len(::aText) - - // editor window boundaries - ::nTop := nTop - ::nLeft := nLeft - ::nBottom := nBottom - ::nRight := nRight - - // How many cols and rows are available - ::nNumCols := nRight - nLeft + 1 - ::nNumRows := nBottom - nTop + 1 - - if !lEditMode == NIL - ::lEditAllow := lEditMode - endif - - // set correct insert state - if ::lEditAllow - ::InsertState(::lInsert) - endif - - // is word wrap required? - if !nLineLength == NIL - ::lWordWrap := .T. - ::nWordWrapCol := nLineLength - endif - - // how many spaces for each tab? - if !nTabSize == NIL - ::nTabWidth := nTabSize - endif - - // Empty area of screen which will hold editor window - Scroll(nTop, nLeft, nBottom, nRight) - - // Set cursor upper left corner - SetPos(::nTop, ::nLeft) - - return Self - - - // Redraws a screenfull of text (or part of it if nFromRow is not nil) - METHOD RefreshWindow() CLASS TEditor - - LOCAL i, nOCol, nORow - - nOCol := Col() - nORow := Row() - - for i := 0 to Min(::nNumRows - 1, ::naTextLen - 1) - DispOutAt(::nTop + i, ::nLeft, PadR(SubStr(::aText[::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) - endif - - SetPos(nORow, nOCol) - - return Self - - - // Redraws current screen line - METHOD RefreshLine() CLASS TEditor - - LOCAL nOCol, nORow - - nOCol := Col() - nORow := Row() - - DispOutAt(Row(), ::nLeft, PadR(SubStr(::aText[::nRow], ::nFirstCol, ::nNumCols), ::nNumCols, " ")) - - SetPos(nORow, nOCol) - - return Self - - - // Refreshes only one screen column of text (for Left() and Right() movements) - METHOD RefreshColumn() CLASS TEditor - - LOCAL i, nOCol, nORow - - nOCol := Col() - nORow := Row() - - for i := 0 to Min(::nNumRows - 1, ::naTextLen - 1) - DispOutAt(::nTop + i, nOCol, SubStr(::aText[::nFirstRow + i], ::nCol, 1)) - next - - SetPos(nORow, nOCol) - - return Self - - - // Handles cursor movements inside text array - METHOD MoveCursor(nKey) CLASS TEditor - - LOCAL lMoveKey := .T. - - do case - case (nKey == K_DOWN) - if !::lEditAllow - while Row() < ::nBottom .AND. ::nRow < ::naTextLen - ::nRow++ - SetPos(Row() + 1, Col()) - enddo - endif - if Row() == ::nBottom - if ::nRow < ::naTextLen - Scroll(::nTop, ::nLeft, ::nBottom, ::nRight, 1) - ::nFirstRow++ - ::nRow++ - ::RefreshLine() - endif - else - if ::nRow < ::naTextLen - ::nRow++ - SetPos(Row() + 1, Col()) - endif - endif - - case (nKey == K_PGDN) - if ::nRow + ::nNumRows < ::naTextLen - ::nRow += ::nNumRows - ::nFirstRow += ::nNumRows - if ::nFirstRow + ::nNumRows > ::naTextLen - ::nFirstRow -= ((::nFirstRow + ::nNumRows) - ::naTextLen) + 1 - endif - else - ::nFirstRow := Max(::naTextLen - ::nNumRows + 1, 1) - ::nRow := ::naTextLen - SetPos(Min(::nTop + ::naTextLen - 1, ::nBottom), Col()) - endif - ::RefreshWindow() - - case (nKey == K_CTRL_PGDN) - ::nRow := ::naTextLen - ::nCol := Max(Len(::aText[::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 - while Row() > ::nTop .AND. ::nRow > 1 - ::nRow-- - SetPos(Row() -1, Col()) - enddo - endif - if Row() == ::nTop - if ::nRow > 1 - Scroll(::nTop, ::nLeft, ::nBottom, ::nRight, -1) - ::nFirstRow-- - ::nRow-- - ::RefreshLine() - endif - else - ::nRow-- - SetPos(Row() - 1, Col()) - endif - - case (nKey == K_PGUP) - if (::nRow - ::nNumRows) > 1 - ::nRow -= ::nNumRows - ::nFirstRow -= ::nNumRows - if ::nFirstRow < 1 - ::nFirstRow := 1 - endif - else - ::nFirstRow := 1 - ::nRow := 1 - SetPos(::nTop, Col()) - endif - ::RefreshWindow() - - case (nKey == K_CTRL_PGUP) - ::nRow := 1 - ::nCol := 1 - ::nFirstCol := 1 - ::nFirstRow := 1 - SetPos(::nTop, ::nLeft) - ::RefreshWindow() - - case (nKey == K_RIGHT) - if Col() == ::nRight - if ::nCol <= iif(::lWordWrap, ::nWordWrapCol, Len(::aText[::nRow])) - Scroll(::nTop, ::nLeft, ::nBottom, ::nRight,, 1) - ::nFirstCol++ - ::nCol++ - ::RefreshColumn() - endif - else - ::nCol++ - SetPos(Row(), Col() + 1) - endif - - case (nKey == K_CTRL_RIGHT) - while ::nCol <= Len(::aText[::nRow]) .AND. SubStr(::aText[::nRow], ::nCol, 1) <> " " - ::MoveCursor(K_RIGHT) - enddo - while ::nCol <= Len(::aText[::nRow]) .AND. SubStr(::aText[::nRow], ::nCol, 1) == " " - ::MoveCursor(K_RIGHT) - enddo - - case (nKey == K_LEFT) - if Col() == ::nLeft - if ::nCol > 1 - Scroll(::nTop, ::nLeft, ::nBottom, ::nRight,, -1) - ::nFirstCol-- - ::nCol-- - ::RefreshColumn() - endif - else - ::nCol-- - SetPos(Row(), Col() - 1) - endif - - case (nKey == K_CTRL_LEFT) - while ::nCol > 1 .AND. SubStr(::aText[::nRow], ::nCol, 1) <> " " - ::MoveCursor(K_LEFT) - enddo - while ::nCol > 1 .AND. SubStr(::aText[::nRow], ::nCol, 1) == " " - ::MoveCursor(K_LEFT) - enddo - - case (nKey == K_HOME) - ::nCol := 1 - ::nFirstCol := 1 - SetPos(Row(), ::nLeft) - ::RefreshWindow() - - case (nKey == K_CTRL_HOME) - ::nCol := 1 - ::nFirstCol := 1 - ::nRow -= (Row() - ::nTop) - SetPos(::nTop, ::nLeft) - ::RefreshWindow() - - case (nKey == K_END) - // Empty lines have 0 len - ::nCol := Max(Len(::aText[::nRow]), 1) - ::nFirstCol := Max(::nCol - ::nNumCols + 1, 1) - SetPos(Row(), Min(::nLeft + ::nCol - 1, ::nRight)) - ::RefreshWindow() - - case (nKey == K_CTRL_END) - ::nRow += (::nBottom - Row()) - if ::nRow > ::naTextLen - ::nRow := ::naTextLen - endif - ::nCol := Max(Len(::aText[::nRow]), 1) - ::nFirstCol := Max(::nCol - ::nNumCols + 1, 1) - SetPos(Min(::nBottom, ::naTextLen), Min(::nLeft + ::nCol - 1, ::nRight)) - ::RefreshWindow() - - otherwise - lMoveKey := .F. - - endcase - - return lMoveKey - - - // Changes lInsert value and insertion / overstrike mode of editor - METHOD InsertState(lInsState) CLASS TEditor - - ::lInsert := 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) - - LOCAL nKey - - while (nKey := InKey(0)) <> K_ESC - oSelf:MoveCursor(nKey) - enddo - - return - - - // Edits text - METHOD Edit(nPassedKey) CLASS TEditor - - LOCAL i, nKey, lOldInsert - LOCAL lKeepGoing := .T. - - if !::lEditAllow - BrowseText(Self) - - else - - while lKeepGoing - - // If I've been called with a key already preset, evaluate this key and then exit - if nPassedKey == NIL - nKey := InKey(0) - else - lKeepGoing := .F. - nKey := nPassedKey - endif - - do case - 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 > Len(::aText[::nRow]) - ::aText[::nRow] += Space(::nCol - Len(::aText[::nRow])) - endif - // insert char if in insert mode or at end of current line - if ::lInsert .OR. (::nCol > Len(::aText[::nRow])) - ::aText[::nRow] := Stuff(::aText[::nRow], ::nCol, 0, Chr(nKey)) - else - ::aText[::nRow] := Stuff(::aText[::nRow], ::nCol, 1, Chr(nKey)) - endif - ::MoveCursor(K_RIGHT) - ::RefreshLine() - - // if we need to word wrap a word we simulate a word left + return - if ::lWordWrap .AND. (::nCol > ::nWordWrapCol) - ::MoveCursor(K_CTRL_LEFT) - ::MoveCursor(K_CTRL_RIGHT) - lOldInsert := ::lInsert - ::lInsert := .T. - ::Edit(K_RETURN) - ::lInsert := lOldInsert - ::MoveCursor(K_CTRL_RIGHT) - endif - - case (nKey == K_RETURN) - if ::lInsert .OR. ::nRow == ::naTextLen - AAdd(::aText, "") - if ::nRow <= ::naTextLen - AIns(::aText, ::nRow + 1) - if Len(::aText[::nRow]) > 0 - // Split current line at cursor position - ::aText[::nRow + 1] := Right(::aText[::nRow], Len(::aText[::nRow]) - ::nCol + 1) - ::aText[::nRow] := Left(::aText[::nRow], ::nCol - 1) - else - ::aText[::nRow + 1] := "" - endif - endif - // I increment naTextLen only here because now there is a "real" line of text, before - // this point we have only added some "space" to split current line - ::naTextLen++ - endif - ::MoveCursor(K_DOWN) - ::MoveCursor(K_HOME) - - case (nKey == K_INS) - ::InsertState(!::lInsert) - - case (nKey == K_DEL) - ::aText[::nRow] := Stuff(::aText[::nRow], ::nCol, 1, "") - ::RefreshLine() - if Len(::aText[::nRow]) == 0 - ::Edit(K_CTRL_Y) - endif - - case (nKey == K_TAB) - // insert char if in insert mode or at end of current line - if ::lInsert .OR. (::nCol == Len(::aText[::nRow])) - ::aText[::nRow] := Stuff(::aText[::nRow], ::nCol, 0, Space(::nTabWidth)) - endif - for i := 1 to ::nTabWidth - ::MoveCursor(K_RIGHT) - next - ::RefreshLine() - - case (nKey == K_BS) - // delete previous character - ::aText[::nRow] := Stuff(::aText[::nRow], --::nCol, 1, "") - // correct column position for next call to MoveCursor() - ::nCol++ - ::MoveCursor(K_LEFT) - ::RefreshLine() - - case (nKey == K_CTRL_Y) - if ::naTextLen > 1 - // delete current line of text - ADel(::aText, ::nRow) - ASize(::aText, --::naTextLen) - // if we now have less than a screen full of text, adjust nFirstRow position - if ::nFirstRow + ::nNumRows > ::naTextLen - ::nFirstRow := Max(::nFirstRow - 1, 1) - // if we have less lines of text than our current position on scree, up one line - if ::nRow > ::naTextLen - ::nRow := Max(::nRow - 1, 1) - SetPos(Max(Row() -1, ::nTop), Col()) - endif - endif - ::RefreshWindow() - else - ::aText[::nRow] := "" - ::RefreshLine() - endif - - case (::MoveCursor(nKey)) - // if it's a movement key ::MoveCursor() handles it - - case (nKey == K_ALT_W) - /* TOFIX: Not clipper compatible */ - ::lSaved := .T. - lKeepGoing := .F. - - case (nKey == K_ESC) - lKeepGoing := .F. - - otherwise - /* TODO: Here we should call an user defined function (to match memoedit() behaviour) */ - endcase - enddo - endif - - return Self