{ Simple text editor 2009 March 5 started; basic screen drawing; cursor movement 6 InsertChar, InsertCR, DeleteLeft/Right 6 filename edit field 6 file load, save June 9 Sphinxified 30 Page up/down, minor cursor movement improvement 2010 January 29 Bug: Was dropping last character when reading a file that didn't end with CR/LF. Fixed. ENTER(CR) Carriage return BKSP Delete to the left DEL Delete to the right ?, ?, ?, ? Cursor movement HOME Start of line CTRL-HOME Start of file END End of line CTRL-END End of file CTRL-O Open a file CTRL-S Save to a file CTRL-Q Quit to Sphinx PGUP Page up PGDN Page down to do: block select, cut/copy/paste, find/replace, undo/redo } obj kb: "isxkb" sxtv: "isxtv" term: "tvtexted" sd: "sxfile" var byte x[10] pub Main | e if e := \Try if e > 0 sxtv.str( e ) else sxtv.dec( e ) sxtv.out( 13 ) term.stop sxtv.Enable pub Try | i, key, refresh, blink, t, done sxtv.Disable term.start( sxtv.GetBasepin ) Init Args done~ blink~ t := cnt repeat refresh~ repeat while key := kb.key refresh~~ blink~ case key $20..$7f: InsertChar( key ) CR: InsertCR BKSP: DeleteLeft DEL: DeleteRight LARROW: MoveCursorLeft RARROW: MoveCursorRight UARROW: MoveCursorUp( false ) DARROW: MoveCursorDown( false ) HOME: MoveToStartOfLine CTRL+HOME: MoveToTop END: MoveToEndOfLine CTRL+END: MoveToBottom CTRL+"o": FileOp( OPEN, FALSE ) CTRL+"s": FileOp( SAVE, FALSE ) CTRL+"q": FileOp( SAVE, FALSE ) done~~ PGUP: PageCursorUp PGDN: PageCursorDown other: refresh~ if cnt - t > 0 refresh~~ if refresh RefreshScreen( blink ^= 1 ) t := cnt + clkfreq/10 until done term.stop sxtv.Enable sd.Close sd.Open( string("sphinx.bin"), "R" ) sd.Execute( 0 ) pri Args if sd.Open( string("args.d8a"), "R" ) == 0 if sd.ReadByte sd.ReadString( @filename, FILENAMEBUFFERSIZE-1 ) sd.Close FileOp( OPEN, true ) sd.Close con VISIBLELINES = 13 VISIBLECOLUMNS = 40 con CR = $0d LARROW = $c0 RARROW = $c1 UARROW = $c2 DARROW = $c3 HOME = $c4 END = $c5 PGUP = $c6 PGDN = $c7 BKSP = $c8 DEL = $c9 ESC = $cb CTRL = $0200 CTRL_ALT_DEL = $06c9 con BUFFERSIZE = 20000 var long nLines long firstVisibleLine long firstVisibleColumn long bytesUsed long cursorLine long cursorColumn long desiredColumn long cp long currentLineLength long topPtr ' points to start of first visible line pri InsertChar( ch ) | n if cursorLine < nLines if bytesUsed + 1 => BUFFERSIZE return else ' starting new line at end of file if bytesUsed + 2 => BUFFERSIZE return buffer[bytesUsed++]~ ' terminating null for the new line ++nLines n := bytesUsed - (cp - @buffer) bytemove( cp+1, cp, n ) byte[cp] := ch ++bytesUsed ++currentLineLength MoveCursorRight pri InsertCR | n if cursorLine < nLines if bytesUsed + 1 => BUFFERSIZE return else ' starting new line at end of file if bytesUsed + 2 => BUFFERSIZE return buffer[bytesUsed++]~ ' terminating null for the new line ++nLines n := bytesUsed - (cp - @buffer) bytemove( cp+1, cp, n ) byte[cp]~ ++bytesUsed ++nLines currentLineLength := cursorColumn MoveCursorRight pri DeleteLeft if cursorLine or cursorColumn MoveCursorLeft DeleteRight pri DeleteRight | n if cursorLine == nLines ' can't delete if we're on the phantom line return if byte[cp] == 0 and cursorLine == nLines - 1 and cursorColumn return ' can't delete if we're at the end of the last real line ' unless we're at the start (start = end => empty line ' which we can delete, making this line the new phantom line) n := bytesUsed - (cp - @buffer) - 1 ifnot byte[cp] --nLines bytemove( cp, cp+1, n ) --bytesUsed repeat while byte[--cp] currentLineLength := strsize( ++cp ) cp += cursorColumn AdjustVisibleColumn pri MoveCursorLeft if cursorColumn desiredColumn := --cursorColumn --cp AdjustVisibleColumn else ifnot cursorLine return MoveCursorUp( true ) pri MoveCursorRight if cursorColumn < currentLineLength desiredColumn := ++cursorColumn ++cp AdjustVisibleColumn else if cursorLine == nLines return MoveCursorDown( true ) pri MoveCursorDown( f ) ' if f, also move cursor to start of line if cursorLine == nLines return if ++cursorLine - firstVisibleLine => VISIBLELINES ++firstVisibleLine repeat while byte[topPtr++] MoveCpToNextLine if f cursorColumn~ desiredColumn~ elseif cursorLine < nLines cursorColumn := currentLineLength <# desiredColumn else cursorColumn~ currentLineLength~ cp += cursorColumn AdjustVisibleColumn pri PageCursorDown if firstVisibleLine + VISIBLELINES - 1 => nLines return repeat VISIBLELINES - 1 repeat while byte[topPtr++] ++firstVisibleLine MoveCursorDown( false ) pri MoveCursorUp( f ) ' if f, also move cursor to end of line ifnot cursorLine return if --cursorLine < firstVisibleLine --firstVisibleLine repeat while byte[--topPtr] repeat while byte[--topPtr] ++topPtr MoveCpToPreviousLine if f cursorColumn := desiredColumn := currentLineLength else cursorColumn := currentLineLength <# desiredColumn cp += cursorColumn AdjustVisibleColumn pri PageCursorUp | n n := firstVisibleLine <# VISIBLELINES - 1 repeat n repeat 2 repeat while byte[--topPtr] ++topPtr --firstVisibleLine MoveCursorUp( false ) pri MoveCpToNextLine repeat while byte[cp++] currentLineLength := strsize( cp ) pri MoveCpToPreviousLine repeat while byte[--cp] repeat while byte[--cp] ++cp currentLineLength := strsize( cp ) pri MoveToStartOfLine cp -= cursorColumn cursorColumn~ desiredColumn~ AdjustVisibleColumn pri MoveToEndOfLine cp -= cursorColumn cursorColumn := desiredColumn := currentLineLength cp += currentLineLength AdjustVisibleColumn pri MoveToTop cp := topPtr := @buffer[1] currentLineLength := strsize( cp ) cursorLine~ cursorColumn~ desiredColumn~ firstVisibleLine~ firstVisibleColumn~ pri MoveToBottom ' cheesy implementation MoveToTop if nLines repeat nLines-1 MoveCursorDown( false ) MoveToEndOfLine pri AdjustVisibleColumn firstVisibleColumn~ firstVisibleColumn #>= cursorColumn - VISIBLECOLUMNS + 1 var byte buffer[BUFFERSIZE] { buffer contains nLines null-terminated strings, with sentinel nulls at the beginning and end: +-+---------+-+--------+-+- ---------+-+-+ ¦0¦ line0 0¦ line1 0¦ ... lineN-1 0¦0¦ +-+---------+-+--------+-+- ---------+-+-+ } pri Init nLines~ firstVisibleLine~ firstVisibleColumn~ bytesUsed := 2 buffer[0]~ buffer[1]~ cursorLine~ cursorColumn~ desiredColumn~ currentLineLength~ cp := topPtr := @buffer[1] eolLength := 2 eol[0] := $0d eol[1] := $0a pri RefreshScreen( drawCursor ) | p, len, m, n, l, beyondEnd p := topPtr beyondEnd~ repeat l from 0 to VISIBLELINES-1 term.setXY( 0, l ) if firstVisibleLine + l => nLines beyondEnd~~ len~ else len := strsize( p ) if len > firstVisibleColumn m := (len - firstVisibleColumn) <# VISIBLECOLUMNS p += firstVisibleColumn repeat m-1 term.out( byte[p++] ) term.printNoAdvance( byte[p++] ) term.setX( m ) n := VISIBLECOLUMNS - m if n repeat n-1 term.out( " " ) term.printNoAdvance( " " ) else repeat VISIBLECOLUMNS-1 term.out( " " ) term.printNoAdvance( " " ) ifnot beyondEnd repeat while byte[p++] if drawCursor term.setXY( cursorColumn-firstVisibleColumn, cursorLine-firstVisibleLine ) term.printNoAdvance( "_" ) con #0, OPEN, SAVE ' File ops ' File errors #1, FILE_TOO_BIG, CARD_FULL_MAYBE FILENAMEBUFFERSIZE = 13 ' 8.3 + null pri FileOp( op, t ) | e, i if e := \_FileOp( op, t ) RefreshScreen( false ) ' cursor off term.setXY( 0, VISIBLELINES-1 ) term.out( $0c ) ' set color term.out( 1 ) repeat VISIBLECOLUMNS-1 term.out( " " ) term.printNoAdvance( " " ) term.setX( 0 ) case e FILE_TOO_BIG: term.str( string("File too large") ) CARD_FULL_MAYBE: term.str( string("SD card full?") ) -13: term.str( string("Unable to mount SD card") ) other: term.str( string("Error code ") ) term.dec( e ) kb.getkey term.out( $0c ) ' set color term.out( 0 ) pri _FileOp( op, t ) | r case op OPEN: if t ReadFile elseif EditFilename ReadFile SAVE: WriteFile pri ReadFile | r, p, pEnd sd.Close if r := sd.Open( @filename, "R" ) <> 0 abort r Init r := sd.Read( @buffer[1], BUFFERSIZE-2 ) if r < 0 abort r p := @buffer[1] pEnd := @buffer[r+1] repeat while p <> pEnd if byte[p] == $0d or byte[p] == $0a ' change CR, LF, or CRLF if byte[p] == $0d and byte[p+1] == $0a ' eolLength := 2 ' bytemove( p, p + 1, pEnd - p - 1 ) ' --pEnd ' else ' eolLength := 1 ' byte[p]~ ' to null ++nLines ++p if byte[pEnd-1] ' if byte[pEnd-1] isn't null, that means that the file didn't byte[pEnd++]~ ' end with CR, LF, or CRLF, so we have to fake it. (fixed 2010/01/29 mp) ++nLines byte[pEnd]~ ' add final null bytesUsed := pEnd + 1 - @buffer currentLineLength := strsize( topPtr ) if r == BUFFERSIZE-2 abort FILE_TOO_BIG ' technically we don't know that it's too big at this point, but it's a good guess. if r := sd.Close abort r pri WriteFile | p, len, r ifnot EditFilename return sd.Close if r := sd.Open( @filename, "W" ) abort r p := @buffer[1] repeat nLines len := strsize( p ) r := sd.Write( p, len ) if r < 0 abort r if r <> len ' Does this ever happen? abort CARD_FULL_MAYBE r := sd.Write( @eol, eolLength ) if r < 0 abort r if r <> eolLength ' Does this ever happen? abort CARD_FULL_MAYBE p += len + 1 if (r := sd.Close) < 0 abort r var byte filename[FILENAMEBUFFERSIZE] long filenameCursorColumn long filenameLength byte eolLength byte eol[2] con EDITOFFSET = 10 ' filename edit field starts at column 10 pri EditFilename | t, key, blink, refresh RefreshScreen( false ) ' cursor off term.setXY( 0, VISIBLELINES-1 ) term.out( $0c ) ' set color term.out( 2 ) repeat VISIBLECOLUMNS-1 term.out( " " ) term.printNoAdvance( " " ) term.setX( 0 ) term.str( string("Filename: ") ) filenameCursorColumn := filenameLength := strsize( @filename ) blink~ t := cnt repeat refresh~ repeat while key := kb.key refresh~~ blink~ case key $20..$7f: FilenameInsertChar( key ) CR: term.out( $0c ) ' set color term.out( 0 ) ' back to normal return true BKSP: if filenameCursorColumn filenameCursorColumn := (filenameCursorColumn - 1) #> 0 FilenameDeleteRight DEL: FilenameDeleteRight LARROW: filenameCursorColumn := (filenameCursorColumn - 1) #> 0 RARROW: filenameCursorColumn := (filenameCursorColumn + 1) <# filenameLength ESC: term.out( $0c ) ' set color term.out( 0 ) ' back to normal return false other: refresh~ if cnt - t > 0 refresh~~ if refresh RefreshFilename( blink ^= 1 ) t := cnt + clkfreq/10 pri RefreshFilename( drawCursor ) | i term.setXY( EDITOFFSET, VISIBLELINES-1 ) repeat i from 0 to FILENAMEBUFFERSIZE-1 if drawCursor and i == filenameCursorColumn term.out( "_" ) else if i < filenameLength term.out( filename[i] ) else term.out( " " ) pri FilenameInsertChar( ch ) | n if filenameLength => FILENAMEBUFFERSIZE-1 return n := filenameLength - filenameCursorColumn + 1 bytemove( @filename[filenameCursorColumn+1], @filename[filenameCursorColumn], n ) ++filenameLength filename[filenameCursorColumn++] := ch pri FilenameDeleteRight | n ifnot filenameLength return ifnot n := filenameLength - filenameCursorColumn return bytemove( @filename[filenameCursorColumn], @filename[filenameCursorColumn+1], n ) --filenameLength {{ Copyright (c) 2009 Michael Park +------------------------------------------------------------------------------------------------------------------------------+ | TERMS OF USE: MIT License | +------------------------------------------------------------------------------------------------------------------------------+ |Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation | |files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, | |modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software| |is furnished to do so, subject to the following conditions: | | | |The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.| | | |THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | +------------------------------------------------------------------------------------------------------------------------------+ }}