naar deel (1) De formule hierboven (Newton binomium) werd ingetikt in enkele seconden. Alle tekst is te editen, ook -undo- werkt. Deze wiskunde editor staat op de volgende pijlers: DavArrayButton Eigen component met menuknoppen in rijen en kolommen. Gebruikt voor menu keuzes. Beschrijving [ hier ] XBitmap Eigen unit/Class. Uitbreiding van TBitmap met clipping rectangle, verbeterde floodfill, verbeterde stretchrect, stippellijnen met grotere pendikten, lijnen met pijlpunten. Beschrijving [ hier ] Xfont Eigen Class met schaalbare fonts (3 stuks), opgebouwd uit ASCII en Griekse letters, aangevuld met meetkundige tekens. Beschrijving [ hier ] Xtree Eigen unit/Class met boomstructuur voor text editing. Voorziet ook in het UNDO mechanisme. Beschrijving [ hier ] Data Flows Er zijn 3 XBitmaps van elk 760 x 1080 pixels in 32bit kleuren formaat.
map2 - map1 + tekst map3 - map2 + tijdelijke tekeningen tijdens constructie Tekst wordt gewist door een gedeelte van map1 naar map2 te schrijven. Tekst wordt toegevoegd in map2. Map3 bevat tekeningen tijdens de constructie. Grafische elementen worden dan gewist door een gedeelte van map2 naar map3 te kopiëren. Het resultaat wordt pas zichtbaar na kopiëren van map3 naar de paintbox. De hoogte van de paintbox is minder dan van de bitmaps, een scrollbar bepaalt welk deel van map3 in de paintbox wordt afgebeeld. Bij elke XBitmap hoort een Trect (rect1,rect2,rect3). Zowel de XFont als de XBitmap class hebben de property modrect. Dat is het rechthoekige deel van de bitmap waarin de laatste wijziging plaatsvond. Deze rechthoeken moeten in rect1,rect2,rect3 worden ingevoerd voor de overdracht tussen de XBitmaps. Als een tekening uit meerdere elementen (lijnen, cirkels) bestaat dan moeten de modrects gecombineerd worden. Alleen gewijzigde delen worden gekopieerd. Bewerkingen met rectangles Unirect(r1,r2) : rechthoek r2 wordt toegevoegd aan r1. Interrect(r1,r2) : r1 wordt beperkt tot overlapping van r1 en r2. Packrect(x1,y1,x2,y2 : smallInt) : Trect : maak Trect uit x,y waarden. Bij rect1,rect2,rect3 horen ook de variabelen map1full,map2full,map3full : Boolean. Deze vlaggen zijn false als de rectangles geen informatie bevatten. In dat geval is een unirect(r1,r2) gelijk aan r1 := r2. Alle variabelen voor de coördinaten zijn van het type smallInt. Dat maakt ook coördinaten mogelijk die buiten de bitmap vallen. Het maximale aantal pagina's is 30. De y-coördinaat (gedeeld door 1080) bepaalt de pagina. Het menu systeem Hierboven staat het hoofdmenu. Een enumerated variabele (dat is een volgnummer met een naam) houdt bij welke menuknop actief is. type TMainbutton = (mbDoc,mbPage,mbEdit,mbFrame,mbText,mbGraph,mbSymbol,mbGeo, mbInfo,mbhelp,mbOff); .... var mainmenubutten : TMainbutton;Activeren van een mainbutton opent weer submenu's en toont property buttons.
type TFontProp = record nr : byte; //font number 1..3 height : byte; base : byte; style : byte; color1 : DWORD; //foreground color2 : DWORD; //background end; ...... var fontprops : TFontProp; fontDef : array[1..maxFontDef] of TFontProp; fontdefcount : byte; //top of arrayProperty selectie xxx komt terecht in variable xxxProp. De tekstelementen bevatten een index naar de tabel waarin de properties zijn opgeborgen. Voor het font is dat array fontDEF, in het algemeen xxxDEF. Tekst Element[element nr].p1 is in geval van lijnen, macro's en characters een index naar de font tabel. Zo zijn er vele property variabelen, al of niet met DEF tabellen. Deze properties blijven in de tabellen staan, ook als de elementen worden verwijderd. Bijschrijven van een nieuwe propertie in een tabel doen procedures met de naam registerXXX(....) Control flow Keyboard en muis genereren events.Menuvariabelen zoals mainmenubutton sturen met case-statements die events door naar procedures voor het tekenen of wijzigen van grafische symbolen of tekstverwerking. procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin case key of VK_Next : begin nextpage; key := 0; end; VK_Prior : begin previouspage; key := 0; end; end; if key <> 0 then begin keyEvent := kbDown; keyword := key; keyShift := shift; case mainmenubutton of mbText : textKeyEvent;//--> text control unit mbEdit : case editbutton of ebGraph : ; //--> graph edit unit ebText : texteditKeyboardEvent; //--> textedit unit; end; end;//case key := 0; end;//if end;Hierboven is te zien dat de code voor het editen van grafische elementen nog moet komen. Dit is een eenvoudig voorbeeld van de structuur. Andere, soortgelijke, procedures bestaan uit veel meer code. Tekstverwerking Alle tekstelementen (letters, macro's, lijnen) zijn van:type Telementtype = (elFree,elblock,elLine,elChar,elMacro,elYTab, elReserved); TElSet = set of TElementType; TElement = record elType : TelementType; elCode : byte; p1 : byte; p2 : byte; p3 : smallInt; x : smallInt; //position relative to parent y : smallInt; //.. width : smallInt; height : smallInt; end; ..... var element : array[1..maxelement] of TElement; //frame,line,macro,charDe samenhang tussen deze elementen staat in: type TLinks = record parent : dword; child : dword; next : dword; previous : dword; end; ..... var Links : array[1..maxelement] of TLinks;De Element en Link arrays liggen dus precies naast elkaar. Het Link array is niet rechtstreeks te lezen of te wijzigen: dat kan alleen via de aanroep van procedures en functies. Ook het UNDO systeem is hieraan gekoppeld. In tegenstelling tot de properties worden entries in Elements[ ] gewist zodat ze weer voor hergebruik beschikbaar komen. Alleen de XTree unit kan dat en die stelt hiervan de textcontrol_unit op de hoogte. XTree wijzigt het Element[ ] array niet omdat Xtree zich niet bemoeit met het soort element. Deleten van een element koppelt het alleen los van de Tree. UNDO voegt het weer in. Tekst controle De textcontrol_unit is hier verantwoordelijk voor.Een eerste vereiste is cursor controle. Cursorbeweging binnen lijnen: Dat is de afhandeling van events van de linker- en rechter- cursortoetsen. Binnen een lijn kunnen tekens van verschillend font (kleur, steil,grootte) broederlijk naast elkaar staan. Daarom bevat de cursor property een veld posR:Boolean dat aangeeft of de cursor links dan wel rechts van het element staat. De cursor past zijn grootte aan op het element en nieuw ingevoerde tekst heeft ook die properties. Bij posR = true zal - een left event de cursor links van het element plaatsen - een right event de cursor rechts van het volgende element plaatsen - bij het laatste element van de lijn de parent aanroepen Bij posR = false zal - een left event de cursor links van het vorige element plaatsen - een right event de cursor rechts van het element plaatsen - bij het eerste element van de lijn de parent aanroepen De aangeroepen parent kan van het type elBock of elMacro zijn. In het geval van een macro moet per soort worden bepaald wat de cursor doet. Meestal zal naar een line element (child) van de macro worden verwezen. En dan er er nog cursor Up- en Down events. Bij lijnen binnen macro's wordt de (parent) macro aangeroepen. In elk geval verlaat de cursor de line. Voor cursorcontrole heeft elke macro zijn eigen procedure waar de events binnenkomen. Daarbij zijn er nog algemene procedures per type macro: met 1, 2 of 3 (line) children. type TCursordirection = (cdLeft,cdRight,cdUp,cdDown); TCursorProc = (cpKB,cpM2P,cpM2C); //message to Parent / Child TCursorNavigation = record cdir : TCursordirection; cproc : TCursorProc; destEL : dword; //destination element srcEL : dword; //source element end; ... var cuNAV : Tcursornavigation;Variable cuNAV bepaalt de route van de cursor. Hier een stukje van de procedure die het text event ontvangt .... case keyEvent of kbDown : begin case keyword of VK_LEFT : begin cuNAV.cdir := cdLeft; cuNavigate; end; VK_RIGHT : begin cuNav.cdir := cdRight; cuNavigate; end; VK_UP : begin cuNav.cdir := cdUP; cuNavigate; end; VK_DOWN : begin cuNav.cdir := cdDown; cuNavigate; end; ....leidt de cursor naar het type element.... procedure CuNavigate; //switch to element type //use cuNAV var mac : TTextbutton; framecode : TFrameButton; el : dword; m23 : boolean; begin el := cuNAV.destEL; case element[el].elType of elChar : NavChar; elLine : Navline; elMacro : begin mac := TTextbutton(element[el].elCode); m23 := (textprops[mac].code and 1) = 1; case mac of txtPower, txtIndex, txtParenth : NavMacro1; txtLimit, txtPowInd, txtFraction, txtOver : NAVmacro2; txtRoot : if m23 then NAVroot else NAVmacro1; txtPowerLine : NAVpowerLine; txtIndexLine : NAVIndexLine; txtPowIndLine : NAVpowIndLine; txtSymbol : if m23 then NAVmacro3 else NAVmacro1; txtVector : NAVmacroVector; end;//case end; ....In geval van een macro met één line: procedure NavMacro1; //single line macros begin case cuNAV.cproc of cpKB : begin case cuNAV.cdir of cdLeft : if textcursor.posR then gotoChild else gotoParent; cdRight : if textcursor.posR = false then gotochild else gotoParent; cdUp, cdDown : gotoParent; end; end; cpM2P : begin case cuNAV.cdir of cdLeft : begin textcursor.posR := false; setcursorOnElement(cuNAV.destEL); end; cdRight : begin textcursor.posR := true; setcursorOnElement(cuNAV.destEL); end; cdUp, cdDown : gotoParent; end; end; cpM2C : begin case cuNAV.cdir of cdLeft : begin textcursor.posR := false; gotochild; end; cdRight : begin textcursor.posR := false; gotoChild; end; cdUp, cdDown : gotoXmatchChildN(1); end; end; end; end;Procedure gotoXmatchChild plaatst de cursor zo mogelijk op het child element met dezelfde X positie, gaat dus vertikaal omhoog en omlaag. De procedure GotoChild ziet er zo uit: procedure GotoChild; begin with cuNAV do begin srcEL := destEL; cproc := cpM2C; getChild(destEL,srcEL); end; cuNavigate; end;cpM2C betekent cursor procedure Message to Child. Er is ook een cpM2P, message to parent. Een element weet zo of het door parent of child werd aangeroepen. Tekstverwerking Drie units houden zich met de tekst bezig:
- text paint : tekst en macro's - text calculation Voor elk soort element (elBlock,elMacro,elLine,elChar) zijn er drie specifieke procedures
paint : tekenen het element recalculate : berekent de hoogte,breedte en positie van de children Het voordeel van deze abstracte aanpak is dat met elementen kan worden gesleept zonder ons om de inhoud te bekommeren. De (x,y) positie van children is altijd relatief t.o.v. de parent. Als een element is gewijzigd door een verwijdering of toevoeging van een element dan wordt de parent in kennis gesteld. Die herberekend zijn afmetingen, zet de children op hun plek. Bij veranderde afmetingen wordt weer de parent aangeroepen. Het laatste herberekende element wordt opnieuw getekend. De kern is deze procedure : procedure processELchange(el : dword); //element reports change to it's parent el //el can be macro,line,column (not char or block) //purpose is //1. to call for recalculation of parents //2. set area to be erased //3. set element that needs repainting var oldw,oldh : smallInt; Done : boolean; r1 : Trect; begin eraseflag := false; spaceflag := false; repeat repaintELnr := el; oldw := element[el].width; oldh := element[el].height; r1 := getElementRect(pageNr,el); updateRect(eraseflag,eraseRect,r1); recalculateElement(el); if (oldw <> element[el].width) or (oldh <> element[el].height) then begin getParent(el,el); Done := element[el].elType = elBlock; end else Done := true; until Done; end;oldw : oude breedte van element oldh : oude hoogte eraseRect : te wissen rectangle van oude element getElementRect : berekent de rectangle van het element relatief t.o.v. de pagina updateRect : voegt de rectangles samen recalculateElement( ): naar groot case statement om juiste element te kiezen. Element Insert of Delete procedures zijn dus onafhankelijk van het type element. Hiermee besluit ik deel twee van de math editor beschrijving. Ik hoop de structuur enigszins te hebben weergegeven. Als afsluiter een afbeelding van een vector inproduct, wederom ingetikt in enkele seconden: |
||||||||