Although this editor sort of works, its performance could be improved because it has to redraw the edit window whenever text may have moved between lines. Any suggestions on how to make the text structure less reliant on refreshes would be most welcome.
Code:
' a stab at a Text Eitor using PBCC GRAPHIC WINDOW ' V 0.2 - subclassing the graphic window for mouse control ' V 0.3 - cursor navigation ' V 0.4 - charcter insert, delete, backspace ' ' Chris Holbrook Oct 2008 ' #COMPILE EXE #DIM ALL #INCLUDE "WIN32API.INC" %TRUE = 1 %FALSE = 0 %KEY_LEFT = 19200 %KEY_RIGHT = 19712 %KEY_HOME = 18776 %KEY_END = 20224 %KEY_DEL = 21248 %KEY_UP = 18432 %KEY_DOWN = 20480 %KEY_BSP = 8 %KEY_ESC = 27 %KEY_CTRLF = 6 %KEY_CTRLA = 1 GLOBAL GrDialogProc AS LONG GLOBAL GrStaticProc AS LONG GLOBAL gMouseX AS LONG,gMouseY AS LONG ' Mouse x and y GLOBAL gLbDOWN AS LONG,gRBDOWN AS LONG ' Left and right mouse button GLOBAL gMouseMoved AS LONG ' Detect mouse movements '-------------------------------------------------------------------------------- FUNCTION GrDlgProc(BYVAL hWnd AS DWORD, BYVAL wMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG FUNCTION = CallWindowProc(GrDialogProc, hWnd, wMsg, wParam, lParam) END FUNCTION '-------------------------------------------------------------------------------- FUNCTION GrProc(BYVAL hWnd AS DWORD, BYVAL wMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG LOCAL p AS pointapi SELECT CASE wMsg CASE %WM_MOUSEMOVE gMouseMoved = %TRUE gMouseX = LO(WORD,lParam) gMouseY = HI(WORD,lParam) ' Current Mouse X and Y Position in the graphic window CASE %WM_LBUTTONDOWN gLBDOWN = 1 EXIT FUNCTION ' Left button pressed CASE %WM_RBUTTONDOWN gRBDOWN = 1 EXIT FUNCTION ' Right button pressed END SELECT FUNCTION = CallWindowProc(GrStaticProc, hWnd, wMsg, wParam, lParam) END FUNCTION '-------------------------------------------------------------------- ' edit text %fgcolor = %BLACK %bgcolor = %WHITE %cursorbgcolor = %CYAN %hmargin = 2 %vmargin = 2 %MAXLINES = 99 %MINHEIGHT = 5 %MINWIDTH = 20 FUNCTION edittext ( hGW AS DWORD, BYREF stext AS STRING, maxrct AS QUAD, minx AS LONG, miny AS LONG,term AS STRING) AS QUAD LOCAL ll() AS BYTE PTR LOCAL cpl, i, lpd, lline, endoftext, maxright, maxbottom, pxcwide, pxchigh AS LONG LOCAL rw, cl AS LONG ' row, col for generating edit window, not for editing! LOCAL row, col AS LONG ' for keeping edit location within window LOCAL pbwd, p, pe, pf AS BYTE PTR DIM ll(0 TO %MAXLINES) AS LOCAL BYTE PTR ' array of start of line offsets LOCAL r, lastrect AS rect LOCAL skey, s AS STRING STATIC SERRWTOOLONG AS STRING LOCAL l, movemode, ltc AS LONG LOCAL pw AS WORD PTR 'LOCAL lastcursor as quad ' text offset, x & Y of last cursor pos 'local cursorpos AS DWORD 'X & Y of current cursor pos LOCAL cursoroffset AS LONG ' the offset in the string of the current (cursor) character) ' it is not changed by resizing the edit window LOCAL lastrow, lastcol AS LONG ' the row and column before edit window resizing LOCAL top AS LONG ' the line number of the top of the edit window LOCAL resized AS LONG ' boolean to indicate that the control has been resized LOCAL whitespace AS LONG ' bool LOCAL freshen AS LONG ' bool used to control redisplay of edit window after ll array invalidated SERRWTOOLONG = "<word too long>" ' extract bounding max rect from parameter r.nleft = LO(WORD, LO(DWORD,maxrct)) r.ntop = HI(WORD, LO(DWORD,maxrct)) r.nright = LO(WORD, HI(DWORD,maxrct)) r.nbottom = HI(WORD, HI(DWORD,maxrct)) ' set font GRAPHIC FONT "Courier New", 10 'get pixel sizes of characters GRAPHIC CHR SIZE TO pxcwide, pxchigh lastrect = r freshen = %TRUE DO IF freshen THEN freshen = %FALSE GOSUB again END IF ' if display has been resized then convert prev. text offset into a cursor position ' delete last cursor and display current cursor ' mCursorClr (col, row) lastcol = col lastrow = row GOSUB clrcursor IF resized THEN GOSUB getrowcolfroffset 'calculate current position (row, col) from cursoroffset (position in text string) lastcol = col lastrow = row GOSUB clrcursor END IF GOSUB shewcursor resized = %FALSE WHILE skey = "" GRAPHIC INKEY$ TO skey IF skey = "" THEN SLEEP 1 IF gLBDOWN THEN movemode = %TRUE gLBdown = 0 END IF IF movemode THEN IF gLBDOWN = %FALSE THEN ' moveto event has occurred movemode = %FALSE IF gMouseY < MINX THEN ITERATE IF gMouseX < MINY THEN ITERATE r.nright = gMouseX r.nbottom = gMouseY resized = %TRUE freshen = %true: GOSUB again END IF END IF END IF WEND SELECT CASE LEN(skey) CASE 1 : lTC = ASC(skey) CASE 2 : pw = STRPTR(skey): lTC = @pw CASE 4 : ' unexpected mouse response END SELECT skey = "" SELECT CASE AS LONG lTC CASE %KEY_UP GOSUB clrcursor 'are we at the first row? IF row = 0 THEN ITERATE DECR row IF row < top THEN DECR top ' move edit window up a line freshen = %true EXIT SELECT ' display the new window END IF ' is the current position off the end of the line? WHILE ll(row) + col >= ll(row+1) DECR col IF col = 0 THEN ITERATE WEND GOSUB setoffset CASE %KEY_DOWN ' are we at the end of the array? IF row >= %MAXLINES -1 THEN ITERATE ' are we at the end of the data? IF (ll(row + 2) = 0) THEN ITERATE GOSUB clrcursor INCR row ' is the current position off the end of the line? WHILE ll(row) + col >= ll(row+1) DECR col IF col < 0 THEN ITERATE WEND GOSUB setoffset ' cursor at end of edit window IF row >= (top + lpd) THEN INCR top freshen = %true EXIT SELECT END IF CASE %KEY_CtrlF ' to start of next word GOSUB clrcursor p = cursoroffset + STRPTR(stext) WHILE p < endoftext IF @p < 33 THEN whitespace = %true ELSE IF whitespace = %true THEN' we have gone thru whitespace to get to a "printable" char whitespace = %false cursoroffset = p - STRPTR(stext) 'lastrow = row: lastcol = col GOSUB getrowcolfroffset IF row > (lpd + top -1) THEN INCR top freshen = %true EXIT SELECT END IF EXIT LOOP END IF END IF INCR p WEND CASE %KEY_CtrlA ' to start of next word GOSUB clrcursor p = cursoroffset + STRPTR(stext) WHILE p >= STRPTR(stext) IF @p < 33 THEN whitespace = %true ELSE IF whitespace = %true THEN' we have gone thru whitespace to get to a "printable" char whitespace = %false ' we have arrived at the trailing char if the target word ' so wind back to the leading char WHILE (@p > 32) DECR p IF p < STRPTR(stext) THEN EXIT LOOP ' start of string WEND INCR p cursoroffset = p - STRPTR(stext) 'lastrow = row: lastcol = col GOSUB getrowcolfroffset IF row < top THEN DECR top freshen = %true END IF EXIT LOOP END IF END IF DECR p WEND CASE %KEY_RIGHT GOSUB clrcursor INCR cursoroffset ' are we at the end of the text? IF cursoroffset = endoftext - STRPTR(stext) THEN stext = stext + " " INCR endoftext INCR ll(row + 1) GOSUB getrowcolfroffset freshen = %true ELSE 'are we at the right hand end of the row? IF col = ll(row + 1) - ll(row) THEN GOSUB getrowcolfroffset IF row >= (lpd + top) THEN INCR top freshen = %true EXIT SELECT END IF ELSE INCR col END IF END IF CASE %KEY_LEFT ' are we at extreme left or the row? GOSUB clrcursor IF col = 0 THEN IF row = 0 THEN ITERATE ELSE DECR cursoroffset GOSUB getrowcolfroffset END IF ELSE DECR cursoroffset DECR col END IF IF row < top THEN DECR top CASE %KEY_DEL ' delete a character stext = LEFT$(stext, cursoroffset) + MID$(stext, cursoroffset + 2) freshen = %true CASE %KEY_BSP ' destructive backspace stext = LEFT$(stext, cursoroffset -1) + MID$(stext, cursoroffset + 1) DECR col DECR cursoroffset GOSUB getrowcolfroffset freshen = %true CASE %KEY_ESC FUNCTION = MAK(QUAD,MAK(DWORD,&H1B,0),0) EXIT FUNCTION CASE 32 TO 126 stext = LEFT$(stext, cursoroffset) + CHR$(lTC) + MID$(stext, cursoroffset + 1) INCR cursoroffset INCR col freshen = %true CASE 13 stext = LEFT$(stext, cursoroffset) + $CRLF + MID$(stext, cursoroffset + 1) cursoroffset = cursoroffset + 2 col = 0 INCR row freshen = %true CASE ELSE freshen = %true END SELECT LOOP EXIT FUNCTION ' should never get here! ''''''''''''''''''''''''''''''''''''''''''''''''' again: 'get no of characters per line cpl = (r.nright - r.nleft - 2*%hmargin ) \pxcwide 'get no of lines in display lpd = (r.nbottom - r.ntop - 2 * %vmargin ) \pxchigh GRAPHIC COLOR %fgcolor, %bgcolor REDIM ll(0 TO %MAXLINES) AS LOCAL BYTE PTR GRAPHIC BOX ( lastrect.nleft, lastrect.ntop) - (lastrect.nright, lastrect.nbottom),0,%WHITE,%WHITE,0 GRAPHIC BOX (r.nleft,r.ntop) - (r.nright, r.nbottom), 0,0,%WHITE,0 ' split text into lines until text or exhausted or > maxlines endoftext = STRPTR(stext) + LEN(stext) p = STRPTR(stext) rw = 0: cl = 0 pe = p ' DO ll(rw) = pe p = pe ' substring to consider is bounded by p and pe ' it is the max possible line length (cpl=chars per line) pe = p + cpl -1 IF pe > endoftext THEN pe = endoftext INCR rw EXIT LOOP END IF ' look forward for first line break pf = p WHILE pf < pe IF @pf = 13 THEN pe = pf ' leave CR on the end of the line EXIT LOOP ' exit this WHILE loop END IF INCR pf WEND ' if no line break, look backwards for first word break IF @pe <> 13 THEN ' no line break detected WHILE @pe <> 32 IF pe = p THEN ' word exceeds line pe = p + cpl EXIT LOOP ' exit this WHILE loop END IF DECR pe WEND END IF WHILE @pe < 33 INCR pe ' leave LF, CR or control chars on end of line IF pe > p + cpl THEN EXIT LOOP WEND INCR rw IF rw > %maxlines THEN EXIT LOOP LOOP ' at this point the row variable points to the last row used +1 ll(rw) = pe ' set limiting size into pointer array ' display lines in text box FOR rw = top TO top + lpd -1 IF ll(rw + 1) = 0 THEN EXIT FOR pbwd = ll(rw) lline = ll(rw +1) - pbwd s = STRING$(cpl, 0) movememory(BYVAL STRPTR(s), @pbwd, lline) GRAPHIC SET POS ( r.nleft + 2 , r.ntop + 2 + ((rw - top) * pxchigh)) REPLACE $NUL WITH $SPC IN s GRAPHIC PRINT s NEXT pbwd = STRPTR(stext) FOR i = 0 TO %MAXLINES IF ll(i) = 0 THEN EXIT FOR ll(i) = ll(i) - pbwd NEXT maxright = 375 maxbottom = 315 skey = "" lastrect = r ' save box rectangle in case it is changed RETURN ''''''''''''''''''''''''''''''''''''''''''''''''' 'calculate current position (row, col) from cursoroffset (position in text string) getrowcolfroffset: FOR i = 0 TO %MAXLINES IF ll(i) > cursoroffset THEN row = i - 1 col = cursoroffset - ll(row) EXIT FOR END IF NEXT RETURN setoffset: cursoroffset = ll(row) + col clrcursor: ' if the cursor position is in view, clear it IF lastrow <= (top + lpd) THEN IF lastrow >= top THEN IF ( lastcol <= cpl) THEN GRAPHIC SET POS ( r.nleft + %hmargin + pxcwide*lastcol , r.ntop + %vmargin + pxchigh * (lastrow-top)) GRAPHIC COLOR %fgcolor, %bgcolor GRAPHIC PRINT MID$(stext, ll(lastrow) + lastcol + 1,1) END IF END IF END IF RETURN ''''''''''''''''''''''''''''''''''''''''''''''''' shewcursor: ' if the cursor position is in view, highlight it IF row <= (top + lpd) THEN IF row >= top THEN IF ( col <= cpl) THEN IF (col > -1) THEN GRAPHIC SET POS ( r.nleft + %hmargin + pxcwide*col , r.ntop + %vmargin + pxchigh * (row-top)) GRAPHIC COLOR %fgcolor, %cursorbgcolor GRAPHIC PRINT MID$(stext, ll(row) + col + 1,1) END IF END IF END IF END IF RETURN ''''''''''''''''''''''''''''''''''''''''''''''''' END FUNCTION '------------------------------------------------- FUNCTION PBMAIN () AS LONG LOCAL hGW AS DWORD LOCAL qrect AS QUAD LOCAL qtc AS QUAD LOCAL s AS STRING LOCAL hstatic AS DWORD GRAPHIC WINDOW "text editor", 100, 100, 450, 450 TO hGW hStatic = GetWindow(hGW, %GW_CHILD) ' Retrieve static handle of graphic window GrStaticProc = SetWindowLong(hStatic, %GWL_WNDPROC, CODEPTR(GrProc)) ' Subclasses Graphic control GRAPHIC ATTACH hGW, 0 GRAPHIC COLOR %BLACK, %WHITE s = "Titiro e Kawana--a whakarongo mai hoki. " + $CRLF + "Ko te wahi e nohoia na e koe, ko " + _ "te wahi tena i noho ai to matou tupuna a Hinemoa, i tona Kauanga mai. " + _ "Kia kauwhautia atu iana, e ahau, ki a koe. Na, ko Rangiuru, te matua " + _ "wahine o Tutanekai, ko Whakaue-Kaipapa tana tane tupu; he tane tahae a " + _ "Tuwharetoa. Ko ana tamariki matamua tokotoru, ko o ratou ingoa, ko " + _ "Tawakeheimoa, ko Ngararanui, ko Tuteaiti; muri iho o te tokotoru, ka " + _ "moea tahaetia a Rangiuru e Tuwharetoa."+$CRLF + "I haere manuhiri mai a " + _ ' "Tuwharetoa, nana a Tutanekai, he poriro ia, otira i moea ano e Whakaue a " + _ "Rangiuru; ka hapu ano, he tamaiti, ko Kopako tona ingoa; ka hapu ano " + _ "hoki ia i muri iho i a Kopako, he tamahine, Ko Tupa tona ingoa, ko te " + _ "whakapakanga ia o nga tamariki a Whakaue." GRAPHIC SET POS ( 20, 340) GRAPHIC PRINT "Left-click to plant the bottom right corner of the text, ESC to exit" GRAPHIC SET POS ( 20, 355) GRAPHIC PRINT "word-wise navigation: Ctrl-A, Ctrl-F" qrect = MAK(QUAD,MAK(DWORD,20,20),MAK(DWORD,400,300)) qtc = edittext ( hGW, s, qrect, 100, 100, $ESC + $NUL) END FUNCTION
Comment