To start with i buy the results of all these variables. I Triedit To See What VALUES I GOT. You Should Do The Same. Type Slowly IN:
1234567890
and see how the results are reflected in the Edit control as you do so Then experiment -. try adding stuff to the ends of lines, and in the beginning of the line, and middle of lines You may have to refer back to the Code. To Work Out Which Number Repesents Which variable.
Okay, Now Using The Variables We Have, Lets Try Selecting The Text of The Current Line, and Display It in A New Edit Control (Edit2).
Add The Following Code to See What Happens (Don't Forget To Add The Second Edit Control and make it it as wide as possible):
Myre.selstart: = beginselstart; myre.sellength: = endselstart - beginselstart; edit2.text: = myre.seltext; end;
Run the program and try it out.
OOPS - That Doesn't Work - The text remains selected and the Original Cursor Position is Lost. We need to reset selstart and sellth before we finish in the [onchange] Event. So let's address:
Myre.SELSTART: = WasselStart; // back to were we started myre.sellength: = 0; // Nothing SELECTED END;
While Playing with text in The Edit Control I Discovered Sometting Weird.
If You Typed [1] Then
But there..............
I made a mistake. It appears that RichEdit.Text can tell you where the beginning and end of line is. Why? Because you can access the
7. Okay Implement: Part 2 - Change The Format
After the line edit2.text: = myre.seltext, but before the "resetting" Part, lets put some logic in To Turn Lines Red WHEN THEY ARE LONGER THAN CERTAIN Length:
IF (Myre.Sellength> 10) Then Myre.selattributes.color: = CLRED;
You'll notice two things if you test this out First -.. It does work Second however, is that if you type a line> 10 characters, press return and type one character - its in Red This is because it inherits the Attributes. Of The Preceding Text. Just Like if you have bold on in A Word Processor, It Doesn't Reset if you press Return. so lets change the line to include an else site:
Else myre.selattributes.color: = CLBLACK;
That seems to work - except when you press return in the middle of a> 10 character line you have already typed (which is already Red) to leave a stump <10 characters on the line above -. It remains red This is because the code leaves you on the next line, and SelStart refers to this new line, not the previous one In our eventual code, we'll have to take care to ensure this does not happen -. we have to catch this situation and deal with it . IT WONT Be The Only Situation I'm Sure .... PS: There Will Be a Number of Situation We're We'll Have To Be Careful. Can you think of any now? Try Putting a Lot of Text in The Control (or manipulate a loaded file) and selecting some and using the inherit Drag and Drop (move your Mouse over some selected text, press and hold down the Left MouseButton and then drag away) to move some text. This only Triggers one OnChange Event WE May Also Be Moving Multiple Lines Along The Way. In The Future We'll Have To Put in Some Code To Detect this Happy, And ensure the [OnChange] event can deal with the need to reformat in two different locations. That means thinking in the back of the head about how in the future we may have to deal with this kind of situation, and ensure our code to deal with The Simple Situation Can Be Adapted - IE Be "Versatile".
8. Basically It All Seems to Kind-of Work .. Can't We do Some Real Programming Now?
Okay, Okay. Butfirst We Have A Problem. Actually a Rather Big Problem. The problem is pascon. Why?
First: It Returns RTF Code. Problem: We can't Use RTF Code.
SECOND: ITS Designed to Work An Entire Stream, And The Give It Back to US Again As A Whole. Problem: We Actually Want Greater Control Over It Than This All or Nothing "Approach.oop to the Rescue
Wension, And NEEDS to Be Applied in Another Situation Were It Has To Do A Similar, But Subtly Different Job - You Have Two Choices:
Copy The Function, AND RE-WRITE IT IT (E.G Use Pas2rtf, And The Write A RTFCodes2rtfcontrol Procedure).
Modern languages however give you an option: OOP it "Objectify" it This is more than just deriving something from an existing object It is in a sense programming in a "state of mind" Controls should be created so they can be.... used in a variety of situations -.. father than situation specific in this case all PasCon can deal with is tokenising the input stream and returning code RTF text What we really need to do is divide it into two entitites We need to separate the [. Parsing / recognoded the token and tokentype] from the [eNCode it in → → r r
So Lets Start With ConvertReadstream, Editing It So It Looks Sometying Like this:
function TPasConversion.ConvertReadStream: Integer; begin FOutBuffSize: = size 3; ReAllocMem (FOutBuff, FOutBuffSize); FTokenState: = tsUnknown; FComment: = csNo; FBuffPos: = 0; FReadBuff: = Memory; {Write leading RTF} WriteToBuffer ( ' {/ rtf1 / ansi / deff0 / deftab720 '); WriteFontTable; WriteColorTable; WriteToBuffer (' / deflang1033 / pard / plain / f2 / fs20 '); Result: = Read (FReadBuff ^, Size); if Result> 0 then begin FReadBuff [Result]: = # 0; Run: = freadbuff; while run ^ <> # 0 do beg: = gettoken (run, ftokenstate, tokenstr); scanforrtf; setf; WriteTobuffer (prefix tokenstr postfix); end; { WriteTobuffer (# 13 # 10 '/ par} {' # 13 # 10); end; clear; setpointer (foutbuff, fbuffpos-1); end; {converestStream} The code for ConvertReadstream is now NOW Much Smaller, And Also Easier To Understand. We can TA ke all the code that used to be in ConvertReadStream that did the tokenizing and create a new subroutine -. the GetToken function that just does the recognizing and labelling of the individual tokens In the process we also loose a huge number of repeated lines of code, AS Well as a Number of Sub-Routines Such as HandleborCom and HandLestring.
// // My Get Token routine // function TPasConversion.GetToken (Run: PChar; var aTokenState: TTokenState; var aTokenStr: string): PChar; begin aTokenState: = tsUnknown; aTokenStr: = ''; TokenPtr: = Run; / / MARK WERE We Started Case Run ^
# 13:
Begin
AtokenState: = TSCRLF; Inc (Run, 2);
# 1 .. # 9, # 11, # 12, # 14 .. # 32:
Begin
While run ^ in [# 1 .. # 9, # 11, # 12, # 14 .. # 32] do inc (run); atokenstate: = TSSPACE
END;
'A' .. 'z', 'a' .. 'z', '_':
Begin
AtokenState: = TsiDentifier; Inc (run); while run ^ in ['a'. 'Z', 'a' .. 'Z', '0' .. '9', '_'] do inc (RUN ); Tokenlen: = Run - tokenptr; setString (atokenstr, tokenptr, tokenlen);
IF iskeyword (atokenstr) THEN BEGIN
IF isdirective (atoKenstr) THEN ATOKENSTATE: = TSDIRECTIVE ELSE ATOKENSTATE: = Tskeyword;
END;
END;
'0' .. '9':
Begin
INC; atokenstate: = TSNumber; while run ^ in ['0' .. '9', '.', 'e', 'e'] do inc (RUN);
END;
'{':
Begin
FComment: = csbor; atokenState: = TsComment; While NOT ((Run = # 0)) DO INC (RUN); Inc (RUN);
END;
'!', '",'% ',' & ',' ('..' / ',': '..' @ ',' ['..' ^ ','` ',' ~ ' :
Begin
AtokenState: = tsunknown; while run ^ in ['!', '",'% ',' & ',' ('..' / ',': '..' @ ',' ['..' ^ ',' `',' ~ '] Do Begin
Case run ^
'/': If (Run 1) ^ = '/' Then Begin
IF (atokenstate = tsunknown) THEN BEGIN
While (Run ^ <> # 13) AND (Run ^ <> # 0) Do INC (RUN); fcomment: = CSSLASHES; atokenState: = TSComment; Break;
end
Else
Begin
ATOKENSTATE: = TSSYMBOL; BREAK;
END;
END;
'(': If (Run 1) ^ = '*' Then Begin
IF (atokenstate = tsunknown) THEN BEGINWHILE (RUN ^ <> # 0) AND NOT ((Run = ')') AND ((Run - 1) ^ = '*')) DO INC (RUN);
FComment: = csansi; atokenstate: = TSComment
Inc (RUN); Break;
end
Else
Begin
ATOKENSTATE: = TSSYMBOL; BREAK;
END;
END;
END;
AtokenState: = TSSYMBOL; Inc (RUN);
END;
IF atokenstate = Tsunknown kiln = tssymbol;
END;
# 39:
Begin
AtokenState: = TSSTRING; fcomment: = CSNO;
Repeat
Case Run ^ OF # 0, # 10, # 13: raise exception.create ('invalid string');
INC; RUN;
Until Run ^ = # 39;
INC; RUN;
END;
'#':
Begin
AtokenState: = TSSTRING; while run ^ in ['#', '0' .. '9'] do inc (RUN);
END;
'$':
Begin
FTOKENSTATE: = TSNumber; While Run ^ in ['$', '0' .. '9', 'a' .. 'f', 'a' .. 'f'] do inc (RUN);
END;
Else
IF Run ^ <> # 0 THEN INC (RUN);
End; tokenlen: = Run - tokenptr; setString (atokenstr, tokenptr, tokenlen); result: = run end; {convertreadstream}