IDE.MainForm.pas 310 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309
  1. unit IDE.MainForm;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Compiler form
  8. }
  9. {x$DEFINE STATICCOMPILER}
  10. { For debugging purposes, remove the 'x' to have it link the compiler code into
  11. this program and not depend on ISCmplr.dll. You will also need to add the
  12. ..\Components and Src folders to the Delphi Compiler Search path in the project
  13. options. Also see ISCC's STATICCOMPILER and Compiler.Compile's STATICPREPROC. }
  14. {$IFDEF STATICCOMPILER}
  15. {$R ..\Res\ISCmplr.images.res}
  16. {$ENDIF}
  17. interface
  18. uses
  19. Windows, Messages, SysUtils, Classes, Contnrs, Graphics, Controls, Forms, Dialogs, CommDlg,
  20. Generics.Collections, UIStateForm, StdCtrls, ExtCtrls, Menus, Buttons, ComCtrls, CommCtrl,
  21. ScintInt, ScintEdit, IDE.ScintStylerInnoSetup, NewTabSet, ModernColors, IDE.IDEScintEdit,
  22. Shared.DebugStruct, Shared.CompilerInt.Struct, NewUxTheme, ImageList, ImgList, ToolWin, IDE.HelperFunc,
  23. VirtualImageList, BaseImageCollection, BitmapButton;
  24. const
  25. WM_StartCommandLineCompile = WM_USER + $1000;
  26. WM_StartCommandLineWizard = WM_USER + $1001;
  27. WM_StartNormally = WM_USER + $1002;
  28. type
  29. PDebugEntryArray = ^TDebugEntryArray;
  30. TDebugEntryArray = array[0..0] of TDebugEntry;
  31. PVariableDebugEntryArray = ^TVariableDebugEntryArray;
  32. TVariableDebugEntryArray = array[0..0] of TVariableDebugEntry;
  33. TStepMode = (smRun, smStepInto, smStepOver, smStepOut, smRunToCursor);
  34. TDebugTarget = (dtSetup, dtUninstall);
  35. const
  36. DebugTargetStrings: array[TDebugTarget] of String = ('Setup', 'Uninstall');
  37. type
  38. TStatusMessageKind = (smkStartEnd, smkNormal, smkWarning, smkError);
  39. TIncludedFile = class
  40. Filename: String;
  41. CompilerFileIndex: Integer;
  42. LastWriteTime: TFileTime;
  43. HasLastWriteTime: Boolean;
  44. Memo: TIDEScintFileEdit;
  45. end;
  46. TIncludedFiles = TObjectList<TIncludedFile>;
  47. TFindResult = class
  48. Filename: String;
  49. Line, LineStartPos: Integer;
  50. Range: TScintRange;
  51. PrefixStringLength: Integer;
  52. end;
  53. TFindResults = TObjectList<TFindResult>;
  54. TMenuBitmaps = TDictionary<TMenuItem, HBITMAP>;
  55. TKeyMappedMenus = TDictionary<TShortCut, TToolButton>;
  56. TCallTipState = record
  57. StartCallTipWord: Integer;
  58. FunctionDefinition: AnsiString;
  59. BraceCount: Integer;
  60. LastPosCallTip: Integer;
  61. ClassOrRecordMember: Boolean;
  62. CurrentCallTipWord: String;
  63. CurrentCallTip: Integer;
  64. MaxCallTips: Integer;
  65. end;
  66. TUpdatePanelMessage = class
  67. Msg, ConfigIdent: String;
  68. ConfigValue: Integer;
  69. Color: TColor;
  70. HasLink: Boolean;
  71. constructor Create(const AMsg, AConfigIdent: String; const AConfigValue: Integer; const AColor: TColor; const AHasLink: Boolean);
  72. end;
  73. TUpdatePanelMessages = TObjectList<TUpdatePanelMessage>;
  74. TMainForm = class(TUIStateForm)
  75. MainMenu1: TMainMenu;
  76. FMenu: TMenuItem;
  77. FNewMainFile: TMenuItem;
  78. FOpenMainFile: TMenuItem;
  79. FSave: TMenuItem;
  80. FSaveMainFileAs: TMenuItem;
  81. N1: TMenuItem;
  82. BCompile: TMenuItem;
  83. N2: TMenuItem;
  84. FExit: TMenuItem;
  85. EMenu: TMenuItem;
  86. EUndo: TMenuItem;
  87. N3: TMenuItem;
  88. ECut: TMenuItem;
  89. ECopy: TMenuItem;
  90. EPaste: TMenuItem;
  91. EDelete: TMenuItem;
  92. N4: TMenuItem;
  93. ESelectAll: TMenuItem;
  94. VMenu: TMenuItem;
  95. EFind: TMenuItem;
  96. EFindNext: TMenuItem;
  97. EReplace: TMenuItem;
  98. HMenu: TMenuItem;
  99. HDoc: TMenuItem;
  100. HAbout: TMenuItem;
  101. FRecent: TMenuItem;
  102. FClearRecent: TMenuItem;
  103. N6: TMenuItem;
  104. VCompilerOutput: TMenuItem;
  105. FindDialog: TFindDialog;
  106. ReplaceDialog: TReplaceDialog;
  107. StatusPanel: TPanel;
  108. CompilerOutputList: TListBox;
  109. SplitPanel: TPanel;
  110. HWebsite: TMenuItem;
  111. VToolbar: TMenuItem;
  112. N7: TMenuItem;
  113. TOptions: TMenuItem;
  114. HFaq: TMenuItem;
  115. StatusBar: TStatusBar;
  116. BodyPanel: TPanel;
  117. VStatusBar: TMenuItem;
  118. ERedo: TMenuItem;
  119. RMenu: TMenuItem;
  120. RStepInto: TMenuItem;
  121. RStepOver: TMenuItem;
  122. N5: TMenuItem;
  123. RRun: TMenuItem;
  124. RRunToCursor: TMenuItem;
  125. N10: TMenuItem;
  126. REvaluate: TMenuItem;
  127. CheckIfRunningTimer: TTimer;
  128. RPause: TMenuItem;
  129. RParameters: TMenuItem;
  130. OutputListPopupMenu: TMenuItem;
  131. POutputListCopy: TMenuItem;
  132. HISPPSep: TMenuItem;
  133. N12: TMenuItem;
  134. BStopCompile: TMenuItem;
  135. HISPPDoc: TMenuItem;
  136. N13: TMenuItem;
  137. EGoto: TMenuItem;
  138. RTerminate: TMenuItem;
  139. BMenu: TMenuItem;
  140. BLowPriority: TMenuItem;
  141. HPurchase: TMenuItem;
  142. HRegister: TMenuItem;
  143. HUnregister: TMenuItem;
  144. HDonate: TMenuItem;
  145. N14: TMenuItem;
  146. N15: TMenuItem;
  147. RTargetSetup: TMenuItem;
  148. RTargetUninstall: TMenuItem;
  149. OutputTabSet: TNewTabSet;
  150. DebugOutputList: TListBox;
  151. VDebugOutput: TMenuItem;
  152. VHide: TMenuItem;
  153. N11: TMenuItem;
  154. TMenu: TMenuItem;
  155. TAddRemovePrograms: TMenuItem;
  156. RToggleBreakPoint: TMenuItem;
  157. RDeleteBreakPoints: TMenuItem;
  158. HWhatsNew: TMenuItem;
  159. TGenerateGUID: TMenuItem;
  160. TSignTools: TMenuItem;
  161. N16: TMenuItem;
  162. HExamples: TMenuItem;
  163. N17: TMenuItem;
  164. BOpenOutputFolder: TMenuItem;
  165. N8: TMenuItem;
  166. VZoom: TMenuItem;
  167. VZoomIn: TMenuItem;
  168. VZoomOut: TMenuItem;
  169. N9: TMenuItem;
  170. VZoomReset: TMenuItem;
  171. N18: TMenuItem;
  172. N19: TMenuItem;
  173. FSaveEncoding: TMenuItem;
  174. FSaveEncodingAuto: TMenuItem;
  175. FSaveEncodingUTF8WithBOM: TMenuItem;
  176. ToolBar: TToolBar;
  177. BackNavButton: TToolButton;
  178. ForwardNavButton: TToolButton;
  179. ToolButton1: TToolButton;
  180. NewMainFileButton: TToolButton;
  181. OpenMainFileButton: TToolButton;
  182. SaveButton: TToolButton;
  183. ToolButton2: TToolButton;
  184. CompileButton: TToolButton;
  185. StopCompileButton: TToolButton;
  186. ToolButton3: TToolButton;
  187. RunButton: TToolButton;
  188. PauseButton: TToolButton;
  189. ToolButton4: TToolButton;
  190. TargetSetupButton: TToolButton;
  191. TargetUninstallButton: TToolButton;
  192. ToolButton5: TToolButton;
  193. HelpButton: TToolButton;
  194. Bevel1: TBevel;
  195. TerminateButton: TToolButton;
  196. ThemedToolbarVirtualImageList: TVirtualImageList;
  197. LightToolbarVirtualImageList: TVirtualImageList;
  198. POutputListSelectAll: TMenuItem;
  199. DebugCallStackList: TListBox;
  200. VDebugCallStack: TMenuItem;
  201. TMsgBoxDesigner: TMenuItem;
  202. TRegistryDesigner: TMenuItem;
  203. ToolBarPanel: TPanel;
  204. HMailingList: TMenuItem;
  205. MemosTabSet: TNewTabSet; { First tab is the main memo, last tab is the preprocessor output memo }
  206. FSaveAll: TMenuItem;
  207. RStepOut: TMenuItem;
  208. VNextTab: TMenuItem;
  209. VPreviousTab: TMenuItem;
  210. N20: TMenuItem;
  211. HShortcutsDoc: TMenuItem;
  212. HRegExDoc: TMenuItem;
  213. N21: TMenuItem;
  214. EFindPrevious: TMenuItem;
  215. FindResultsList: TListBox;
  216. VFindResults: TMenuItem;
  217. EFindInFiles: TMenuItem;
  218. FindInFilesDialog: TFindDialog;
  219. FPrint: TMenuItem;
  220. N22: TMenuItem;
  221. PrintDialog: TPrintDialog;
  222. FSaveEncodingUTF8WithoutBOM: TMenuItem;
  223. TFilesDesigner: TMenuItem;
  224. VCloseCurrentTab: TMenuItem;
  225. VReopenTab: TMenuItem;
  226. VReopenTabs: TMenuItem;
  227. MemosTabSetPopupMenu: TMenuItem;
  228. VCloseCurrentTab2: TMenuItem;
  229. VReopenTab2: TMenuItem;
  230. VReopenTabs2: TMenuItem;
  231. NavPopupMenu: TMenuItem;
  232. N23: TMenuItem;
  233. ThemedMarkersAndACVirtualImageList: TVirtualImageList;
  234. ESelectNextOccurrence: TMenuItem;
  235. ESelectAllOccurrences: TMenuItem;
  236. BreakPointsPopupMenu: TMenuItem;
  237. RToggleBreakPoint2: TMenuItem;
  238. RDeleteBreakPoints2: TMenuItem;
  239. N24: TMenuItem;
  240. VWordWrap: TMenuItem;
  241. N25: TMenuItem;
  242. ESelectAllFindMatches: TMenuItem;
  243. EToggleLinesComment: TMenuItem;
  244. EBraceMatch: TMenuItem;
  245. EFoldLine: TMenuItem;
  246. EUnfoldLine: TMenuItem;
  247. EFindRegEx: TMenuItem;
  248. UpdatePanel: TPanel;
  249. UpdateLinkLabel: TLinkLabel;
  250. UpdatePanelCloseBitBtn: TBitmapButton;
  251. UpdatePanelDonateBitBtn: TBitmapButton;
  252. procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  253. procedure FExitClick(Sender: TObject);
  254. procedure FOpenMainFileClick(Sender: TObject);
  255. procedure EUndoClick(Sender: TObject);
  256. procedure EMenuClick(Sender: TObject);
  257. procedure ECutClick(Sender: TObject);
  258. procedure ECopyClick(Sender: TObject);
  259. procedure EPasteClick(Sender: TObject);
  260. procedure EDeleteClick(Sender: TObject);
  261. procedure FSaveClick(Sender: TObject);
  262. procedure ESelectAllClick(Sender: TObject);
  263. procedure FNewMainFileClick(Sender: TObject);
  264. procedure FNewMainFileUserWizardClick(Sender: TObject);
  265. procedure HDocClick(Sender: TObject);
  266. procedure BCompileClick(Sender: TObject);
  267. procedure FMenuClick(Sender: TObject);
  268. procedure FMRUClick(Sender: TObject);
  269. procedure VCompilerOutputClick(Sender: TObject);
  270. procedure HAboutClick(Sender: TObject);
  271. procedure EFindClick(Sender: TObject);
  272. procedure FindDialogFind(Sender: TObject);
  273. procedure EReplaceClick(Sender: TObject);
  274. procedure ReplaceDialogReplace(Sender: TObject);
  275. procedure EFindNextOrPreviousClick(Sender: TObject);
  276. procedure SplitPanelMouseMove(Sender: TObject; Shift: TShiftState; X,
  277. Y: Integer);
  278. procedure VMenuClick(Sender: TObject);
  279. procedure HWebsiteClick(Sender: TObject);
  280. procedure VToolbarClick(Sender: TObject);
  281. procedure TOptionsClick(Sender: TObject);
  282. procedure HFaqClick(Sender: TObject);
  283. procedure HISPPDocClick(Sender: TObject);
  284. procedure VStatusBarClick(Sender: TObject);
  285. procedure ERedoClick(Sender: TObject);
  286. procedure StatusBarResize(Sender: TObject);
  287. procedure RStepIntoClick(Sender: TObject);
  288. procedure RStepOverClick(Sender: TObject);
  289. procedure RRunToCursorClick(Sender: TObject);
  290. procedure RRunClick(Sender: TObject);
  291. procedure REvaluateClick(Sender: TObject);
  292. procedure CheckIfRunningTimerTimer(Sender: TObject);
  293. procedure RPauseClick(Sender: TObject);
  294. procedure RParametersClick(Sender: TObject);
  295. procedure POutputListCopyClick(Sender: TObject);
  296. procedure BStopCompileClick(Sender: TObject);
  297. procedure EGotoClick(Sender: TObject);
  298. procedure RTerminateClick(Sender: TObject);
  299. procedure BMenuClick(Sender: TObject);
  300. procedure BLowPriorityClick(Sender: TObject);
  301. procedure StatusBarDrawPanel(StatusBar: TStatusBar;
  302. Panel: TStatusPanel; const Rect: TRect);
  303. procedure HPurchaseClick(Sender: TObject);
  304. procedure HRegisterClick(Sender: TObject);
  305. procedure HUnregisterClick(Sender: TObject);
  306. procedure HDonateClick(Sender: TObject);
  307. procedure RTargetClick(Sender: TObject);
  308. procedure DebugOutputListDrawItem(Control: TWinControl; Index: Integer;
  309. Rect: TRect; State: TOwnerDrawState);
  310. procedure OutputTabSetClick(Sender: TObject);
  311. procedure VHideClick(Sender: TObject);
  312. procedure VDebugOutputClick(Sender: TObject);
  313. procedure FormResize(Sender: TObject);
  314. procedure TAddRemoveProgramsClick(Sender: TObject);
  315. procedure RToggleBreakPointClick(Sender: TObject);
  316. procedure RDeleteBreakPointsClick(Sender: TObject);
  317. procedure HWhatsNewClick(Sender: TObject);
  318. procedure TGenerateGUIDClick(Sender: TObject);
  319. procedure TSignToolsClick(Sender: TObject);
  320. procedure HExamplesClick(Sender: TObject);
  321. procedure BOpenOutputFolderClick(Sender: TObject);
  322. procedure FormKeyDown(Sender: TObject; var Key: Word;
  323. Shift: TShiftState);
  324. procedure VZoomInClick(Sender: TObject);
  325. procedure VZoomOutClick(Sender: TObject);
  326. procedure VZoomResetClick(Sender: TObject);
  327. procedure FSaveEncodingItemClick(Sender: TObject);
  328. procedure CompilerOutputListDrawItem(Control: TWinControl; Index: Integer;
  329. Rect: TRect; State: TOwnerDrawState);
  330. procedure FormAfterMonitorDpiChanged(Sender: TObject; OldDPI,
  331. NewDPI: Integer);
  332. procedure POutputListSelectAllClick(Sender: TObject);
  333. procedure DebugCallStackListDrawItem(Control: TWinControl; Index: Integer; Rect: TRect;
  334. State: TOwnerDrawState);
  335. procedure VDebugCallStackClick(Sender: TObject);
  336. procedure HMailingListClick(Sender: TObject);
  337. procedure TMsgBoxDesignerClick(Sender: TObject);
  338. procedure TRegistryDesignerClick(Sender: TObject);
  339. procedure MemosTabSetClick(Sender: TObject);
  340. procedure FSaveAllClick(Sender: TObject);
  341. procedure RStepOutClick(Sender: TObject);
  342. procedure TMenuClick(Sender: TObject);
  343. procedure VNextTabClick(Sender: TObject);
  344. procedure VPreviousTabClick(Sender: TObject);
  345. procedure HShortcutsDocClick(Sender: TObject);
  346. procedure HRegExDocClick(Sender: TObject);
  347. procedure VFindResultsClick(Sender: TObject);
  348. procedure EFindInFilesClick(Sender: TObject);
  349. procedure FindInFilesDialogFind(Sender: TObject);
  350. procedure FindResultsListDrawItem(Control: TWinControl; Index: Integer; Rect: TRect;
  351. State: TOwnerDrawState);
  352. procedure FindResultsListDblClick(Sender: TObject);
  353. procedure FPrintClick(Sender: TObject);
  354. procedure TFilesDesignerClick(Sender: TObject);
  355. procedure VCloseCurrentTabClick(Sender: TObject);
  356. procedure VReopenTabsClick(Sender: TObject);
  357. procedure MemosTabSetPopupMenuClick(Sender: TObject);
  358. procedure MemosTabSetOnCloseButtonClick(Sender: TObject; Index: Integer);
  359. procedure StatusBarClick(Sender: TObject);
  360. procedure SimpleMenuClick(Sender: TObject);
  361. procedure OutputListKeyDown(Sender: TObject; var Key: Word;
  362. Shift: TShiftState);
  363. procedure RMenuClick(Sender: TObject);
  364. procedure BackNavButtonClick(Sender: TObject);
  365. procedure ForwardNavButtonClick(Sender: TObject);
  366. procedure NavPopupMenuClick(Sender: TObject);
  367. procedure ESelectNextOccurrenceClick(Sender: TObject);
  368. procedure ESelectAllOccurrencesClick(Sender: TObject);
  369. procedure BreakPointsPopupMenuClick(Sender: TObject);
  370. procedure FClearRecentClick(Sender: TObject);
  371. procedure VWordWrapClick(Sender: TObject);
  372. procedure ESelectAllFindMatchesClick(Sender: TObject);
  373. procedure EToggleLinesCommentClick(Sender: TObject);
  374. procedure EBraceMatchClick(Sender: TObject);
  375. procedure EFoldOrUnfoldLineClick(Sender: TObject);
  376. procedure EFindRegExClick(Sender: TObject);
  377. procedure UpdateLinkLabelLinkClick(Sender: TObject; const Link: string;
  378. LinkType: TSysLinkType);
  379. procedure UpdatePanelCloseBitBtnPaint(Sender: TObject; Canvas: TCanvas; var ARect: TRect);
  380. procedure UpdatePanelCloseBitBtnClick(Sender: TObject);
  381. procedure UpdatePanelDonateBitBtnClick(Sender: TObject);
  382. procedure HMenuClick(Sender: TObject);
  383. private
  384. { Private declarations }
  385. FMemos: TList<TIDEScintEdit>; { FMemos[0] is the main memo and FMemos[1] the preprocessor output memo - also see MemosTabSet comment above }
  386. FMainMemo: TIDEScintFileEdit; { Doesn't change }
  387. FPreprocessorOutputMemo: TIDEScintEdit; { Doesn't change and is the only memo which isnt a TIDEScint*File*Edit}
  388. FFileMemos: TList<TIDEScintFileEdit>; { All memos except FPreprocessorOutputMemo, including those without a tab }
  389. FHiddenFiles: TStringList; { List of files which *do* use a memo but are hidden by the user and have no tab }
  390. FActiveMemo: TIDEScintEdit; { Changes depending on user input }
  391. FErrorMemo, FStepMemo: TIDEScintFileEdit; { These change depending on user input }
  392. FMemosStyler: TInnoSetupStyler; { Single styler for all memos }
  393. FCompilerVersion: PCompilerVersionInfo;
  394. FMRUMainFilesMenuItems: array[0..MRUListMaxCount-1] of TMenuItem;
  395. FMRUMainFilesList: TStringList;
  396. FMRUParametersList: TStringList;
  397. FOptions: record
  398. ShowStartupForm: Boolean;
  399. UseWizard: Boolean;
  400. Autosave: Boolean;
  401. Autoreload: Boolean;
  402. MakeBackups: Boolean;
  403. FullPathInTitleBar: Boolean;
  404. UndoAfterSave: Boolean;
  405. UndoAfterReload: Boolean;
  406. PauseOnDebuggerExceptions: Boolean;
  407. RunAsDifferentUser: Boolean;
  408. AutoAutoComplete: Boolean;
  409. AutoCallTips: Boolean;
  410. UseSyntaxHighlighting: Boolean;
  411. ColorizeCompilerOutput: Boolean;
  412. UnderlineErrors: Boolean;
  413. HighlightWordAtCursorOccurrences: Boolean;
  414. HighlightSelTextOccurrences: Boolean;
  415. CursorPastEOL: Boolean;
  416. TabWidth: Integer;
  417. UseTabCharacter: Boolean;
  418. ShowWhiteSpace: Boolean;
  419. UseFolding: Boolean;
  420. FindRegEx: Boolean;
  421. WordWrap: Boolean;
  422. AutoIndent: Boolean;
  423. IndentationGuides: Boolean;
  424. LowPriorityDuringCompile: Boolean;
  425. GutterLineNumbers: Boolean;
  426. KeyMappingType: TKeyMappingType;
  427. MemoKeyMappingType: TIDEScintKeyMappingType;
  428. ThemeType: TThemeType;
  429. ShowPreprocessorOutput: Boolean;
  430. OpenIncludedFiles: Boolean;
  431. ShowCaretPosition: Boolean;
  432. end;
  433. FOptionsLoaded: Boolean;
  434. FTheme: TTheme;
  435. FSignTools: TStringList;
  436. FFindResults: TFindResults;
  437. FCompiling: Boolean;
  438. FCompileWantAbort: Boolean;
  439. FBecameIdle: Boolean;
  440. FModifiedAnySinceLastCompile, FModifiedAnySinceLastCompileAndGo: Boolean;
  441. FDebugEntries: PDebugEntryArray;
  442. FDebugEntriesCount: Integer;
  443. FVariableDebugEntries: PVariableDebugEntryArray;
  444. FVariableDebugEntriesCount: Integer;
  445. FCompiledCodeText: AnsiString;
  446. FCompiledCodeDebugInfo: AnsiString;
  447. FDebugClientWnd: HWND;
  448. FProcessHandle, FDebugClientProcessHandle: THandle;
  449. FDebugTarget: TDebugTarget;
  450. FCompiledExe, FUninstExe, FTempDir: String;
  451. FPreprocessorOutput: String;
  452. FIncludedFiles: TIncludedFiles;
  453. FDebugging: Boolean;
  454. FStepMode: TStepMode;
  455. FPaused, FPausedAtCodeLine: Boolean;
  456. FRunToCursorPoint: TDebugEntry;
  457. FReplyString: String;
  458. FDebuggerException: String;
  459. FRunParameters: String;
  460. FLastFindOptions: TFindOptions;
  461. FLastFindRegEx: Boolean;
  462. FLastFindText: String;
  463. FLastReplaceText: String;
  464. FLastEvaluateConstantText: String;
  465. FSavePriorityClass: DWORD;
  466. FBuildAnimationFrame: Cardinal;
  467. FLastAnimationTick: DWORD;
  468. FProgress, FProgressMax: Cardinal;
  469. FTaskbarProgressValue: Cardinal;
  470. FProgressThemeData: HTHEME;
  471. FMenuThemeData: HTHEME;
  472. FToolbarThemeData: HTHEME;
  473. FStatusBarThemeData: HTHEME;
  474. FMenuDarkBackgroundBrush: TBrush;
  475. FMenuDarkHotOrSelectedBrush: TBrush;
  476. FDebugLogListTimestampsWidth: Integer;
  477. FOnPendingSquiggly: Boolean;
  478. FPendingSquigglyCaretPos: Integer;
  479. FCallStackCount: Cardinal;
  480. FDevMode, FDevNames: HGLOBAL;
  481. FMenuImageList: TVirtualImageList;
  482. FMenuBitmaps: TMenuBitmaps;
  483. FMenuBitmapsSize: TSize;
  484. FMenuBitmapsSourceImageCollection: TCustomImageCollection;
  485. FSynchingZoom: Boolean;
  486. FNavStacks: TIDEScintEditNavStacks;
  487. FCurrentNavItem: TIDEScintEditNavItem;
  488. FKeyMappedMenus: TKeyMappedMenus;
  489. FBackNavButtonShortCut, FForwardNavButtonShortCut: TShortCut;
  490. FBackNavButtonShortCut2, FForwardNavButtonShortCut2: TShortCut;
  491. FIgnoreTabSetClick: Boolean;
  492. FFirstTabSelectShortCut, FLastTabSelectShortCut: TShortCut;
  493. FCompileShortCut2: TShortCut;
  494. FCallTipState: TCallTipState;
  495. FUpdatePanelMessages: TUpdatePanelMessages;
  496. FBuildImageList: TImageList;
  497. FHighContrastActive: Boolean;
  498. FDonateImageMenuItem: TMenuItem;
  499. function AnyMemoHasBreakPoint: Boolean;
  500. class procedure AppOnException(Sender: TObject; E: Exception);
  501. procedure AppOnActivate(Sender: TObject);
  502. class procedure AppOnGetActiveFormHandle(var AHandle: HWND);
  503. procedure AppOnIdle(Sender: TObject; var Done: Boolean);
  504. function AskToDetachDebugger: Boolean;
  505. procedure BringToForeground;
  506. procedure BuildAndSaveBreakPointLines(const AMemo: TIDEScintFileEdit);
  507. procedure BuildAndSaveKnownIncludedAndHiddenFiles;
  508. procedure CheckIfTerminated;
  509. procedure ClearMRUMainFilesList;
  510. procedure CloseTab(const TabIndex: Integer);
  511. procedure CompileFile(AFilename: String; const ReadFromFile: Boolean);
  512. procedure CompileIfNecessary;
  513. function ConfirmCloseFile(const PromptToSave: Boolean): Boolean;
  514. procedure DebuggingStopped(const WaitForTermination: Boolean);
  515. procedure DebugLogMessage(const S: String);
  516. procedure DebugShowCallStack(const CallStack: String; const CallStackCount: Cardinal);
  517. function DestroyLineState(const AMemo: TIDEScintFileEdit): Boolean;
  518. procedure DestroyDebugInfo;
  519. procedure DetachDebugger;
  520. function EvaluateConstant(const S: String; out Output: String): Integer;
  521. function EvaluateVariableEntry(const DebugEntry: PVariableDebugEntry;
  522. out Output: String): Integer;
  523. procedure FindNext(const ReverseDirection: Boolean);
  524. function FindSetupDirectiveValue(const DirectiveName,
  525. DefaultValue: String): String; overload;
  526. function FindSetupDirectiveValue(const DirectiveName: String;
  527. DefaultValue: Boolean): Boolean; overload;
  528. function FromCurrentPPI(const XY: Integer): Integer;
  529. function GetBorderStyle: TFormBorderStyle;
  530. procedure Go(AStepMode: TStepMode);
  531. procedure HideError;
  532. procedure InitializeFindText(Dlg: TFindDialog);
  533. function InitializeFileMemo(const Memo: TIDEScintFileEdit; const PopupMenu: TPopupMenu): TIDEScintFileEdit;
  534. function InitializeMainMemo(const Memo: TIDEScintFileEdit; const PopupMenu: TPopupMenu): TIDEScintFileEdit;
  535. function InitializeMemoBase(const Memo: TIDEScintEdit; const PopupMenu: TPopupMenu): TIDEScintEdit;
  536. function InitializeNonFileMemo(const Memo: TIDEScintEdit; const PopupMenu: TPopupMenu): TIDEScintEdit;
  537. function InitiateAutoCompleteOrCallTipAllowedAtPos(const AMemo: TIDEScintEdit;
  538. const WordStartLinePos, PositionBeforeWordStartPos: Integer): Boolean;
  539. procedure InitiateAutoComplete(const Key: AnsiChar);
  540. procedure UpdateCallTipFunctionDefinition(const Pos: Integer = -1);
  541. procedure InitiateCallTip(const Key: AnsiChar);
  542. procedure ContinueCallTip;
  543. procedure InvalidateStatusPanel(const Index: Integer);
  544. procedure LoadBreakPointLinesAndUpdateLineMarkers(const AMemo: TIDEScintFileEdit);
  545. procedure LoadKnownIncludedAndHiddenFilesAndUpdateMemos(const AFilename: String);
  546. procedure MemoCallTipArrowClick(Sender: TObject; const Up: Boolean);
  547. procedure MemoChange(Sender: TObject; const Info: TScintEditChangeInfo);
  548. procedure MemoCharAdded(Sender: TObject; Ch: AnsiChar);
  549. procedure MainMemoDropFiles(Sender: TObject; X, Y: Integer; AFiles: TStrings);
  550. procedure MemoHintShow(Sender: TObject; var Info: TScintHintInfo);
  551. procedure MemoKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  552. procedure MemoKeyPress(Sender: TObject; var Key: Char);
  553. procedure MemoLinesDeleted(Memo: TIDEScintFileEdit; FirstLine, Count, FirstAffectedLine: Integer);
  554. procedure MemoLinesInserted(Memo: TIDEScintFileEdit; FirstLine, Count: integer);
  555. procedure MemoMarginClick(Sender: TObject; MarginNumber: Integer;
  556. Line: Integer);
  557. procedure MemoMarginRightClick(Sender: TObject; MarginNumber: Integer;
  558. Line: Integer);
  559. procedure MemoModifiedChange(Sender: TObject);
  560. function MemoToTabIndex(const AMemo: TIDEScintEdit): Integer;
  561. procedure MemoUpdateUI(Sender: TObject; Updated: TScintEditUpdates);
  562. procedure MemoZoom(Sender: TObject);
  563. function MultipleSelectionPasteFromClipboard(const AMemo: TIDESCintEdit): Boolean;
  564. procedure UpdateReopenTabMenu(const Menu: TMenuItem);
  565. procedure ModifyMRUMainFilesList(const AFilename: String; const AddNewItem: Boolean);
  566. procedure ModifyMRUParametersList(const AParameter: String; const AddNewItem: Boolean);
  567. procedure MoveCaretAndActivateMemo(AMemo: TIDEScintEdit; const LineNumberOrPosition: Integer;
  568. const AlwaysResetColumnEvenIfOnRequestedLineAlready: Boolean;
  569. const IsPosition: Boolean = False; const PositionVirtualSpace: Integer = 0);
  570. procedure NavItemClick(Sender: TObject);
  571. procedure NewMainFile(const IsReload: Boolean = False);
  572. procedure NewMainFileUsingWizard;
  573. procedure OpenFile(AMemo: TIDEScintFileEdit; AFilename: String; const MainMemoAddToRecentDocs: Boolean;
  574. const IsReload: Boolean = False);
  575. procedure OpenMRUMainFile(const AFilename: String);
  576. procedure ParseDebugInfo(DebugInfo: Pointer);
  577. procedure ReadMRUMainFilesList;
  578. procedure ReadMRUParametersList;
  579. procedure RemoveMemoFromNav(const AMemo: TIDEScintEdit);
  580. procedure RemoveMemoBadLinesFromNav(const AMemo: TIDEScintEdit);
  581. procedure ReopenTabClick(Sender: TObject);
  582. procedure ReopenTabOrTabs(const HiddenFileIndex: Integer; const Activate: Boolean);
  583. procedure ResetAllMemosLineState;
  584. procedure StartProcess;
  585. function SaveFile(const AMemo: TIDEScintFileEdit; const SaveAs: Boolean): Boolean;
  586. procedure SetBorderStyle(Value: TFormBorderStyle);
  587. procedure SetErrorLine(const AMemo: TIDEScintFileEdit; const ALine: Integer);
  588. procedure SetStatusPanelVisible(const AVisible: Boolean);
  589. procedure SetStepLine(const AMemo: TIDEScintFileEdit; ALine: Integer);
  590. procedure ShowOpenMainFileDialog(const Examples: Boolean);
  591. procedure StatusBarCanvasDrawPanel(Canvas: TCanvas;
  592. Panel: TStatusPanel; const Rect: TRect);
  593. procedure StatusMessage(const Kind: TStatusMessageKind; const S: String);
  594. function StoreAndTestLastFindOptions(Sender: TObject): Boolean;
  595. function TestLastFindOptions: Boolean;
  596. procedure SyncEditorOptions;
  597. function TabIndexToMemo(const ATabIndex, AMaxTabIndex: Integer): TIDEScintEdit;
  598. function ToCurrentPPI(const XY: Integer): Integer;
  599. procedure ToggleBreakPoint(Line: Integer);
  600. procedure UpdateAllMemoLineMarkers(const AMemo: TIDEScintFileEdit);
  601. procedure UpdateAllMemosLineMarkers;
  602. procedure UpdateBevel1Visibility;
  603. procedure UpdateCaption;
  604. procedure UpdateCaretPosPanelAndBackNavStack;
  605. procedure UpdateCompileStatusPanels(const AProgress, AProgressMax: Cardinal;
  606. const ASecondsRemaining: Integer; const ABytesCompressedPerSecond: Cardinal);
  607. procedure UpdateEditModePanel;
  608. procedure UpdateFindRegExUI;
  609. procedure UpdateFindResult(const FindResult: TFindResult; const ItemIndex: Integer;
  610. const NewLine, NewLineStartPos: Integer);
  611. procedure UpdatePreprocMemos(const DontUpdateRelatedVisibilty: Boolean = False);
  612. procedure UpdateLineMarkers(const AMemo: TIDEScintFileEdit; const Line: Integer);
  613. procedure UpdateImages;
  614. procedure UpdateMarginsAndAutoCompleteIcons;
  615. procedure UpdateMarginsAndSquigglyAndCaretWidths;
  616. procedure UpdateMemosTabSetVisibility;
  617. procedure UpdateMenuBitmapsIfNeeded;
  618. procedure UpdateModifiedPanel;
  619. procedure UpdateNavButtons;
  620. procedure UpdateNewMainFileButtons;
  621. procedure UpdateOccurrenceIndicators(const AMemo: TIDEScintEdit);
  622. procedure UpdateOutputTabSetListsItemHeightAndDebugTimeWidth;
  623. procedure UpdateRunMenu;
  624. procedure UpdateSaveMenuItemAndButton;
  625. procedure UpdateTargetMenu;
  626. procedure UpdateUpdatePanel;
  627. procedure UpdateKeyMapping;
  628. procedure UpdateTheme;
  629. procedure UpdateThemeData(const Open: Boolean);
  630. procedure ApplyMenuBitmaps(const ParentMenuItem: TMenuItem);
  631. procedure UpdateStatusPanelHeight(H: Integer);
  632. procedure WMAppCommand(var Message: TMessage); message WM_APPCOMMAND;
  633. procedure WMCopyData(var Message: TWMCopyData); message WM_COPYDATA;
  634. procedure WMDebuggerHello(var Message: TMessage); message WM_Debugger_Hello;
  635. procedure WMDebuggerGoodbye(var Message: TMessage); message WM_Debugger_Goodbye;
  636. procedure WMDebuggerQueryVersion(var Message: TMessage); message WM_Debugger_QueryVersion;
  637. procedure GetMemoAndDebugEntryFromMessage(Kind, Index: Integer; var Memo: TIDEScintFileEdit;
  638. var DebugEntry: PDebugEntry);
  639. procedure DebuggerStepped(var Message: TMessage; const Intermediate: Boolean);
  640. procedure WMDebuggerStepped(var Message: TMessage); message WM_Debugger_Stepped;
  641. procedure WMDebuggerSteppedIntermediate(var Message: TMessage); message WM_Debugger_SteppedIntermediate;
  642. procedure WMDebuggerException(var Message: TMessage); message WM_Debugger_Exception;
  643. procedure WMDebuggerSetForegroundWindow(var Message: TMessage); message WM_Debugger_SetForegroundWindow;
  644. procedure WMDebuggerCallStackCount(var Message: TMessage); message WM_Debugger_CallStackCount;
  645. procedure WMStartCommandLineCompile(var Message: TMessage); message WM_StartCommandLineCompile;
  646. procedure WMStartCommandLineWizard(var Message: TMessage); message WM_StartCommandLineWizard;
  647. procedure WMStartNormally(var Message: TMessage); message WM_StartNormally;
  648. procedure WMDPIChanged(var Message: TMessage); message WM_DPICHANGED;
  649. procedure WMSettingChange(var Message: TMessage); message WM_SETTINGCHANGE;
  650. procedure WMSysColorChange(var Message: TMessage); message WM_SYSCOLORCHANGE;
  651. procedure WMThemeChanged(var Message: TMessage); message WM_THEMECHANGED;
  652. procedure WMUAHDrawMenu(var Message: TMessage); message WM_UAHDRAWMENU;
  653. procedure WMUAHDrawMenuItem(var Message: TMessage); message WM_UAHDRAWMENUITEM;
  654. procedure UAHDrawMenuBottomLine;
  655. procedure WMNCActivate(var Message: TMessage); message WM_NCACTIVATE;
  656. procedure WMNCPaint(var Message: TMessage); message WM_NCPAINT;
  657. protected
  658. procedure WndProc(var Message: TMessage); override;
  659. public
  660. { Public declarations }
  661. constructor Create(AOwner: TComponent); override;
  662. destructor Destroy; override;
  663. function IsShortCut(var Message: TWMKey): Boolean; override;
  664. published
  665. property BorderStyle: TFormBorderStyle read GetBorderStyle write SetBorderStyle;
  666. end;
  667. var
  668. MainForm: TMainForm;
  669. CommandLineFilename, CommandLineWizardName: String;
  670. CommandLineCompile: Boolean;
  671. CommandLineWizard: Boolean;
  672. implementation
  673. uses
  674. ActiveX, Clipbrd, ShellApi, ShlObj, IniFiles, Registry, Consts, Types, UITypes, Themes, DateUtils,
  675. Math, StrUtils, WideStrUtils, TypInfo,
  676. PathFunc, TaskbarProgressFunc, NewUxTheme.TmSchema, BrowseFunc,
  677. Shared.CommonFunc.Vcl, Shared.CommonFunc, Shared.FileClass,
  678. IDE.Messages, IDE.HtmlHelpFunc, IDE.ImagesModule,
  679. {$IFDEF STATICCOMPILER} Compiler.Compile, {$ENDIF}
  680. IDE.OptionsForm, IDE.StartupForm, IDE.Wizard.WizardForm, IDE.SignToolsForm,
  681. Shared.ConfigIniFile, Shared.SignToolsFunc, IDE.InputQueryComboForm, IDE.MsgBoxDesignerForm,
  682. IDE.FilesDesignerForm, IDE.RegistryDesignerForm, IDE.Wizard.WizardFormRegistryHelper,
  683. Shared.CompilerInt, Shared.LicenseFunc, IDE.LicenseKeyForm;
  684. {$R *.DFM}
  685. const
  686. { Memos }
  687. MaxMemos = 22; { Includes the main and preprocessor output memos }
  688. FirstIncludedFilesMemoIndex = 1; { This is an index into FFileMemos }
  689. { Status bar panel indexes }
  690. spCaretPos = 0;
  691. spModified = 1;
  692. spEditMode = 2;
  693. spFindRegEx = 3;
  694. spHiddenFilesCount = 4;
  695. spCompileIcon = 5;
  696. spCompileProgress = 6;
  697. spExtraStatus = 7;
  698. { Output tab set indexes }
  699. tiCompilerOutput = 0;
  700. tiDebugOutput = 1;
  701. tiDebugCallStack = 2;
  702. tiFindResults = 3;
  703. LineStateGrowAmount = 4000;
  704. { TUpdatePanelMessage }
  705. constructor TUpdatePanelMessage.Create(const AMsg, AConfigIdent: String;
  706. const AConfigValue: Integer; const AColor: TColor; const AHasLink: Boolean);
  707. begin
  708. Msg := AMsg;
  709. ConfigIdent := AConfigIdent;
  710. ConfigValue := AConfigValue;
  711. Color := AColor;
  712. HasLink := AHasLink;
  713. end;
  714. { TMainFormPopupMenu }
  715. type
  716. TMainFormPopupMenu = class(TPopupMenu)
  717. private
  718. FParentMenuItem: TMenuItem;
  719. public
  720. constructor Create(const AOwner: TComponent; const ParentMenuItem: TMenuItem); reintroduce; virtual;
  721. procedure Popup(X, Y: Integer); override;
  722. end;
  723. constructor TMainFormPopupMenu.Create(const AOwner: TComponent; const ParentMenuItem: TMenuItem);
  724. begin
  725. inherited Create(AOwner);
  726. FParentMenuItem := ParentMenuItem;
  727. end;
  728. procedure TMainFormPopupMenu.Popup(X, Y: Integer);
  729. var
  730. Form: TMainForm;
  731. begin
  732. { Show the existing main menu's submenu }
  733. Form := Owner as TMainForm;
  734. var OldVisible := FParentMenuItem.Visible; { See ApplyMenuBitmaps }
  735. FParentMenuItem.Visible := True;
  736. try
  737. TrackPopupMenu(FParentMenuItem.Handle, TPM_RIGHTBUTTON, X, Y, 0, Form.Handle, nil);
  738. finally
  739. FParentMenuItem.Visible := OldVisible;
  740. end;
  741. end;
  742. { TMainForm }
  743. function TMainForm.InitializeMemoBase(const Memo: TIDEScintEdit; const PopupMenu: TPopupMenu): TIDEScintEdit;
  744. begin
  745. Memo.Align := alClient;
  746. Memo.Font.Name := GetPreferredMemoFont; { Default font only, see ReadConfig }
  747. Memo.Font.Size := 10;
  748. Memo.ShowHint := True;
  749. Memo.Styler := FMemosStyler;
  750. Memo.PopupMenu := PopupMenu;
  751. Memo.OnCallTipArrowClick := MemoCallTipArrowClick;
  752. Memo.OnChange := MemoChange;
  753. Memo.OnCharAdded := MemoCharAdded;
  754. Memo.OnHintShow := MemoHintShow;
  755. Memo.OnKeyDown := MemoKeyDown;
  756. Memo.OnKeyPress := MemoKeyPress;
  757. Memo.OnMarginClick := MemoMarginClick;
  758. Memo.OnMarginRightClick := MemoMarginRightClick;
  759. Memo.OnModifiedChange := MemoModifiedChange;
  760. Memo.OnUpdateUI := MemoUpdateUI;
  761. Memo.OnZoom := MemoZoom;
  762. Memo.Parent := BodyPanel;
  763. Memo.SetAutoCompleteSeparators(InnoSetupStylerWordListSeparator, InnoSetupStylerWordListTypeSeparator);
  764. Memo.SetWordChars(Memo.GetDefaultWordChars+'#{}[]');
  765. Memo.Theme := FTheme;
  766. Memo.StyleName := 'Windows';
  767. Memo.Visible := False;
  768. Result := Memo;
  769. end;
  770. function TMainForm.InitializeFileMemo(const Memo: TIDEScintFileEdit; const PopupMenu: TPopupMenu): TIDEScintFileEdit;
  771. begin
  772. InitializeMemoBase(Memo, PopupMenu);
  773. Memo.ChangeHistory := schMarkers;
  774. Memo.CompilerFileIndex := UnknownCompilerFileIndex;
  775. Memo.ErrorLine := -1;
  776. Memo.StepLine := -1;
  777. Result := Memo;
  778. end;
  779. function TMainForm.InitializeMainMemo(const Memo: TIDEScintFileEdit; const PopupMenu: TPopupMenu): TIDEScintFileEdit;
  780. begin
  781. InitializeFileMemo(Memo, PopupMenu);
  782. Memo.AcceptDroppedFiles := True;
  783. Memo.CompilerFileIndex := -1;
  784. Memo.OnDropFiles := MainMemoDropFiles;
  785. Memo.Used := True;
  786. Result := Memo;
  787. end;
  788. function TMainForm.InitializeNonFileMemo(const Memo: TIDEScintEdit; const PopupMenu: TPopupMenu): TIDEScintEdit;
  789. begin
  790. InitializeMemoBase(Memo, PopupMenu);
  791. Memo.ReadOnly := True;
  792. Result := Memo;
  793. end;
  794. constructor TMainForm.Create(AOwner: TComponent);
  795. procedure CheckUpdatePanelMessage(const Ini: TConfigIniFile; const ConfigIdent: String;
  796. const ConfigValueDefault, ConfigValueMinimum, ConfigValueNew: Integer; const Msg: String; const Color: TColor;
  797. const HasLink: Boolean); overload;
  798. begin
  799. var ConfigValue := Ini.ReadInteger('UpdatePanel', ConfigIdent, ConfigValueDefault); { Also see HUnregisterClick }
  800. if ConfigValue < ConfigValueMinimum then
  801. FUpdatePanelMessages.Add(TUpdatePanelMessage.Create(Msg, ConfigIdent, ConfigValueNew, Color,
  802. HasLink));
  803. end;
  804. procedure CheckUpdatePanelMessage(const Ini: TConfigIniFile; const ConfigIdent: String;
  805. const ConfigValueDefault, ConfigValueExpected: Integer; const Msg: String; const Color: TColor;
  806. const HasLink: Boolean); overload;
  807. begin
  808. CheckUpdatePanelMessage(Ini, ConfigIdent, ConfigValueDefault, ConfigValueExpected, ConfigValueExpected,
  809. Msg, Color, HasLink);
  810. end;
  811. procedure ReadAndApplyConfig;
  812. var
  813. Ini: TConfigIniFile;
  814. WindowPlacement: TWindowPlacement;
  815. I: Integer;
  816. Memo: TIDEScintEdit;
  817. begin
  818. Ini := TConfigIniFile.Create;
  819. try
  820. { Menu check boxes state }
  821. ToolbarPanel.Visible := Ini.ReadBool('Options', 'ShowToolbar', True);
  822. StatusBar.Visible := Ini.ReadBool('Options', 'ShowStatusBar', True);
  823. FOptions.LowPriorityDuringCompile := Ini.ReadBool('Options', 'LowPriorityDuringCompile', False);
  824. { Configuration options - does not read ThemeType, see ReadAndUpdateTheme instead }
  825. FOptions.ShowStartupForm := Ini.ReadBool('Options', 'ShowStartupForm', True);
  826. FOptions.UseWizard := Ini.ReadBool('Options', 'UseWizard', True);
  827. FOptions.Autosave := Ini.ReadBool('Options', 'Autosave', False);
  828. FOptions.Autoreload := Ini.ReadBool('Options', 'Autoreload', True);
  829. FOptions.MakeBackups := Ini.ReadBool('Options', 'MakeBackups', False);
  830. FOptions.FullPathInTitleBar := Ini.ReadBool('Options', 'FullPathInTitleBar', False);
  831. FOptions.UndoAfterSave := Ini.ReadBool('Options', 'UndoAfterSave', True);
  832. FOptions.UndoAfterReload := Ini.ReadBool('Options', 'UndoAfterReload', True);
  833. FOptions.PauseOnDebuggerExceptions := Ini.ReadBool('Options', 'PauseOnDebuggerExceptions', True);
  834. FOptions.RunAsDifferentUser := Ini.ReadBool('Options', 'RunAsDifferentUser', False);
  835. FOptions.AutoAutoComplete := Ini.ReadBool('Options', 'AutoComplete', True);
  836. FOptions.AutoCallTips := Ini.ReadBool('Options', 'AutoCallTips', True);
  837. FOptions.UseSyntaxHighlighting := Ini.ReadBool('Options', 'UseSynHigh', True);
  838. FOptions.ColorizeCompilerOutput := Ini.ReadBool('Options', 'ColorizeCompilerOutput', True);
  839. FOptions.UnderlineErrors := Ini.ReadBool('Options', 'UnderlineErrors', True);
  840. FOptions.HighlightWordAtCursorOccurrences := Ini.ReadBool('Options', 'HighlightWordAtCursorOccurrences', False);
  841. FOptions.HighlightSelTextOccurrences := Ini.ReadBool('Options', 'HighlightSelTextOccurrences', True);
  842. FOptions.CursorPastEOL := Ini.ReadBool('Options', 'EditorCursorPastEOL', False);
  843. FOptions.TabWidth := Ini.ReadInteger('Options', 'TabWidth', 2);
  844. FOptions.UseTabCharacter := Ini.ReadBool('Options', 'UseTabCharacter', False);
  845. FOptions.ShowWhiteSpace := Ini.ReadBool('Options', 'ShowWhiteSpace', False);
  846. FOptions.UseFolding := Ini.ReadBool('Options', 'UseFolding', True);
  847. FOptions.FindRegEx := Ini.ReadBool('Options', 'FindRegEx', False);
  848. FOptions.WordWrap := Ini.ReadBool('Options', 'WordWrap', False);
  849. FOptions.AutoIndent := Ini.ReadBool('Options', 'AutoIndent', True);
  850. FOptions.IndentationGuides := Ini.ReadBool('Options', 'IndentationGuides', True);
  851. FOptions.GutterLineNumbers := Ini.ReadBool('Options', 'GutterLineNumbers', False);
  852. FOptions.ShowPreprocessorOutput := Ini.ReadBool('Options', 'ShowPreprocessorOutput', True);
  853. FOptions.OpenIncludedFiles := Ini.ReadBool('Options', 'OpenIncludedFiles', True);
  854. I := Ini.ReadInteger('Options', 'KeyMappingType', Ord(GetDefaultKeyMappingType));
  855. if (I >= 0) and (I <= Ord(High(TKeyMappingType))) then
  856. FOptions.KeyMappingType := TKeyMappingType(I);
  857. I := Ini.ReadInteger('Options', 'MemoKeyMappingType', Ord(GetDefaultMemoKeyMappingType));
  858. if (I >= 0) and (I <= Ord(High(TIDEScintKeyMappingType))) then
  859. FOptions.MemoKeyMappingType := TIDEScintKeyMappingType(I);
  860. FMainMemo.Font.Name := Ini.ReadString('Options', 'EditorFontName', FMainMemo.Font.Name);
  861. FMainMemo.Font.Size := Ini.ReadInteger('Options', 'EditorFontSize', 10);
  862. FMainMemo.Font.Charset := Ini.ReadInteger('Options', 'EditorFontCharset', FMainMemo.Font.Charset);
  863. FMainMemo.Zoom := Ini.ReadInteger('Options', 'Zoom', 0); { MemoZoom will zoom the other memos }
  864. for Memo in FMemos do
  865. if Memo <> FMainMemo then
  866. Memo.Font := FMainMemo.Font;
  867. { UpdatePanel visibility }
  868. const BannerGreen = $ABE3AB; { MGreen with HSL lightness changed from 40% to 78% }
  869. const BannerBlue = $FFD399; { MBlue with HSL lightness changed from 42% to 80% }
  870. const BannerOrange = $9EB8F0; {MOrange with HSL lightness changed from 63% to 78% }
  871. const BannerRed = $BBB5EE; {MRed with HSL lightness changed from 58% to 82% }
  872. CheckUpdatePanelMessage(Ini, 'KnownVersion', 0, Integer(FCompilerVersion.BinVersion),
  873. 'Your version of Inno Setup has been updated! <a id="hwhatsnew">See what''s new</a>.',
  874. BannerGreen, True);
  875. CheckUpdatePanelMessage(Ini, 'VSCodeMemoKeyMap', 0, 1,
  876. 'VS Code-style editor shortcuts added! Use the <a id="toptions-vscode">Editor Keys option</a> in Options dialog.',
  877. BannerBlue, True);
  878. const LicenseState = GetLicenseState;
  879. if LicenseState = lsExpiredButUpdated then begin
  880. { Complain twice per day }
  881. const CurrentHourAsInt = FormatDateTime('yyyymmddhh', Now).ToInteger;
  882. const WarnAgainHourAsInt = FormatDateTime('yyyymmddhh', IncHour(Now, 12)).ToInteger;
  883. const Msg = 'Running a version released after your update entitlement ended. <a id="hpurchase">Renew license</a>, <a id="hunregister">remove key</a>, or <a id="fexit">exit</a>.';
  884. CheckUpdatePanelMessage(Ini, 'Purchase.ExpiredButUpdated', 0, CurrentHourAsInt, WarnAgainHourAsInt, { Also see UpdateUpdatePanel }
  885. Msg, BannerRed, True);
  886. end else if LicenseState in [lsExpiring, lsExpired] then begin
  887. { Warn about expiry, once per week }
  888. const CurrentDateAsInt = FormatDateTime('yyyymmdd', Date).ToInteger;
  889. const WarnAgainDateAsInt = FormatDateTime('yyyymmdd', IncDay(Date, 7)).ToInteger;
  890. const Msg = IfThen(LicenseState = lsExpiring,
  891. 'Your update entitlement is ending soon. Please <a id="hpurchase">renew your license</a>. Thanks!',
  892. 'Your update entitlement has ended. Please <a id="hpurchase">renew your license</a>. Thanks!');
  893. CheckUpdatePanelMessage(Ini, 'Purchase.Renew', 0, CurrentDateAsInt, WarnAgainDateAsInt, { Also see UpdateUpdatePanel }
  894. Msg, BannerOrange, True);
  895. end else if LicenseState = lsNotLicensed then begin
  896. { Ask about current commercial use, once per month }
  897. const CurrentDateAsInt = FormatDateTime('yyyymmdd', Date).ToInteger;
  898. const AskAgainDateAsInt = FormatDateTime('yyyymmdd', IncDay(IncMonth(Date, 6), -1)).ToInteger; { Also see HUnregisterClick }
  899. CheckUpdatePanelMessage(Ini, 'Purchase', 0, CurrentDateAsInt, AskAgainDateAsInt, { Also see UpdateUpdatePanel and HUnregisterClick }
  900. 'Using Inno Setup commercially? Please <a id="hpurchase">purchase a license</a>. Thanks!',
  901. BannerBlue, True);
  902. end;
  903. UpdateUpdatePanel;
  904. { Debug options }
  905. FOptions.ShowCaretPosition := Ini.ReadBool('Options', 'ShowCaretPosition', False);
  906. if FOptions.ShowCaretPosition then begin
  907. StatusBar.Panels[spCaretPos].Width := MulDiv(StatusBar.Panels[spCaretPos].Width, 7, 2);
  908. StatusBar.Panels[spCaretPos].Alignment := taLeftJustify;
  909. end;
  910. SyncEditorOptions;
  911. UpdateNewMainFileButtons;
  912. UpdateKeyMapping;
  913. UpdateFindRegExUI;
  914. { Window state }
  915. WindowPlacement.length := SizeOf(WindowPlacement);
  916. GetWindowPlacement(Handle, @WindowPlacement);
  917. WindowPlacement.showCmd := SW_HIDE; { the form isn't Visible yet }
  918. WindowPlacement.rcNormalPosition.Left := Ini.ReadInteger('State',
  919. 'WindowLeft', WindowPlacement.rcNormalPosition.Left);
  920. WindowPlacement.rcNormalPosition.Top := Ini.ReadInteger('State',
  921. 'WindowTop', WindowPlacement.rcNormalPosition.Top);
  922. WindowPlacement.rcNormalPosition.Right := Ini.ReadInteger('State',
  923. 'WindowRight', WindowPlacement.rcNormalPosition.Left + Width);
  924. WindowPlacement.rcNormalPosition.Bottom := Ini.ReadInteger('State',
  925. 'WindowBottom', WindowPlacement.rcNormalPosition.Top + Height);
  926. SetWindowPlacement(Handle, @WindowPlacement);
  927. { Note: Must set WindowState *after* calling SetWindowPlacement, since
  928. TCustomForm.WMSize resets WindowState }
  929. if Ini.ReadBool('State', 'WindowMaximized', False) then
  930. WindowState := wsMaximized;
  931. { Note: Don't call UpdateStatusPanelHeight here since it clips to the
  932. current form height, which hasn't been finalized yet }
  933. { StatusPanel height }
  934. StatusPanel.Height := ToCurrentPPI(Ini.ReadInteger('State', 'StatusPanelHeight',
  935. (10 * FromCurrentPPI(DebugOutputList.ItemHeight) + 4) + FromCurrentPPI(OutputTabSet.Height)));
  936. finally
  937. Ini.Free;
  938. end;
  939. FOptionsLoaded := True;
  940. end;
  941. procedure ReadAndApplyTheme;
  942. begin
  943. const Ini = TConfigIniFile.Create;
  944. try
  945. const I = Ini.ReadInteger('Options', 'ThemeType', Ord(GetDefaultThemeType));
  946. if (I >= 0) and (I <= Ord(High(TThemeType))) then
  947. FOptions.ThemeType := TThemeType(I);
  948. finally
  949. Ini.Free
  950. end;
  951. UpdateTheme;
  952. end;
  953. var
  954. I: Integer;
  955. NewItem: TMenuItem;
  956. PopupMenu: TPopupMenu;
  957. Memo: TIDEScintEdit;
  958. begin
  959. inherited;
  960. {$IFNDEF STATICCOMPILER}
  961. FCompilerVersion := ISDllGetVersion;
  962. {$ELSE}
  963. FCompilerVersion := ISGetVersion;
  964. {$ENDIF}
  965. FModifiedAnySinceLastCompile := True;
  966. InitFormFont(Self);
  967. FHighContrastActive := HighContrastActive; { Just checking once at startup }
  968. if FHighContrastActive then begin
  969. { If UseVisualStyle is False (LWS_USEVISUALSTYLE is off) the regular text of the label does not
  970. follow any high contrast theme but stays black instead, which is likely to be invisible.
  971. Setting it to True makes all text (regular and link) to get the COLOR_HOTLIGHT color. }
  972. UpdateLinkLabel.UseVisualStyle := True;
  973. { COLOR_WINDOW is documented as the associated background color of COLOR_HOTLIGHT }
  974. UpdatePanel.Color := GetSysColor(COLOR_WINDOW);
  975. end;
  976. { For some reason, if AutoScroll=False is set on the form Delphi ignores the
  977. 'poDefault' Position setting }
  978. AutoScroll := False;
  979. { Append the shortcut key text to the Edit items. Don't actually set the
  980. ShortCut property because we don't want the key combinations having an
  981. effect when Memo doesn't have the focus. }
  982. SetFakeShortCut(EUndo, Ord('Z'), [ssCtrl]);
  983. SetFakeShortCut(ERedo, Ord('Y'), [ssCtrl]);
  984. SetFakeShortCut(ECut, Ord('X'), [ssCtrl]);
  985. SetFakeShortCut(ECopy, Ord('C'), [ssCtrl]);
  986. SetFakeShortCut(EPaste, Ord('V'), [ssCtrl]);
  987. SetFakeShortCut(ESelectAll, Ord('A'), [ssCtrl]);
  988. SetFakeShortCut(EDelete, VK_DELETE, []);
  989. SetFakeShortCutText(VZoomIn, SmkcCtrl + 'Num +'); { These zoom shortcuts are handled by Scintilla and only support the active memo, unlike the menu items which work on all memos }
  990. SetFakeShortCutText(VZoomOut, SmkcCtrl + 'Num -');
  991. SetFakeShortCutText(VZoomReset, SmkcCtrl + 'Num /');
  992. { Use fake Esc shortcut for Stop Compile so it doesn't conflict with the
  993. editor's autocompletion list }
  994. SetFakeShortCut(BStopCompile, VK_ESCAPE, []);
  995. { Use fake Ctrl+F4 shortcut for VCloseCurrentTab2 because VCloseCurrentTab
  996. already has the real one }
  997. SetFakeShortCut(VCloseCurrentTab2, VK_F4, [ssCtrl]);
  998. { Use fake Ctrl+C and Ctrl+A shortcuts for OutputListPopupMenu's items so they
  999. don't conflict with the editor which also uses fake shortcuts for these }
  1000. SetFakeShortCut(POutputListCopy, Ord('C'), [ssCtrl]);
  1001. SetFakeShortCut(POutputListSelectAll, Ord('A'), [ssCtrl]);
  1002. { Set real shortcut on TOptions which can't be set at design time }
  1003. TOptions.ShortCut := ShortCut(VK_OEM_COMMA, [ssCtrl]);
  1004. PopupMenu := TMainFormPopupMenu.Create(Self, EMenu);
  1005. FMemosStyler := TInnoSetupStyler.Create(Self);
  1006. FMemosStyler.ISPPInstalled := ISPPInstalled;
  1007. FTheme := TTheme.Create;
  1008. InitFormThemeInit(FTheme);
  1009. MemosTabSet.Theme := FTheme;
  1010. OutputTabSet.Theme := FTheme;
  1011. ToolBarPanel.ParentBackground := False;
  1012. UpdatePanel.ParentBackground := False;
  1013. UpdatePanelDonateBitBtn.Hint := RemoveAccelChar(HDonate.Caption);
  1014. UpdateImages;
  1015. FMemos := TList<TIDEScintEdit>.Create;
  1016. FMainMemo := InitializeMainMemo(TIDEScintFileEdit.Create(Self), PopupMenu);
  1017. FMemos.Add(FMainMemo);
  1018. FPreprocessorOutputMemo := InitializeNonFileMemo(TIDEScintEdit.Create(Self), PopupMenu);
  1019. FMemos.Add(FPreprocessorOutputMemo);
  1020. for I := FMemos.Count to MaxMemos-1 do
  1021. FMemos.Add(InitializeFileMemo(TIDEScintFileEdit.Create(Self), PopupMenu));
  1022. FFileMemos := TList<TIDEScintFileEdit>.Create;
  1023. for Memo in FMemos do
  1024. if Memo is TIDEScintFileEdit then
  1025. FFileMemos.Add(TIDEScintFileEdit(Memo));
  1026. FHiddenFiles := TStringList.Create(dupError, True, True);
  1027. FActiveMemo := FMainMemo;
  1028. FActiveMemo.Visible := True;
  1029. ActiveControl := FActiveMemo;
  1030. FErrorMemo := FMainMemo;
  1031. FStepMemo := FMainMemo;
  1032. UpdateMarginsAndSquigglyAndCaretWidths;
  1033. FMemosStyler.Theme := FTheme;
  1034. MemosTabSet.PopupMenu := TMainFormPopupMenu.Create(Self, MemosTabSetPopupMenu);
  1035. FFirstTabSelectShortCut := ShortCut(Ord('1'), [ssCtrl]);
  1036. FLastTabSelectShortCut := ShortCut(Ord('9'), [ssCtrl]);
  1037. FNavStacks := TIDEScintEditNavStacks.Create;
  1038. UpdateNavButtons;
  1039. FCurrentNavItem.Invalidate;
  1040. BackNavButton.Style := tbsDropDown;
  1041. BackNavButton.DropdownMenu := TMainFormPopupMenu.Create(Self, NavPopupMenu);
  1042. PopupMenu := TMainFormPopupMenu.Create(Self, OutputListPopupMenu);
  1043. CompilerOutputList.PopupMenu := PopupMenu;
  1044. DebugOutputList.PopupMenu := PopupMenu;
  1045. DebugCallStackList.PopupMenu := PopupMenu;
  1046. FindResultsList.PopupMenu := PopupMenu;
  1047. UpdateOutputTabSetListsItemHeightAndDebugTimeWidth;
  1048. Application.HintShortPause := 0;
  1049. Application.OnException := AppOnException;
  1050. Application.OnActivate := AppOnActivate;
  1051. Application.OnIdle := AppOnIdle;
  1052. FMRUMainFilesList := TStringList.Create;
  1053. for I := 0 to High(FMRUMainFilesMenuItems) do begin
  1054. NewItem := TMenuItem.Create(Self);
  1055. NewItem.OnClick := FMRUClick;
  1056. FRecent.Insert(I, NewItem);
  1057. FMRUMainFilesMenuItems[I] := NewItem;
  1058. end;
  1059. FMRUParametersList := TStringList.Create;
  1060. FSignTools := TStringList.Create;
  1061. FFindResults := TFindResults.Create;
  1062. FIncludedFiles := TIncludedFiles.Create;
  1063. UpdatePreprocMemos;
  1064. FDebugTarget := dtSetup;
  1065. UpdateTargetMenu;
  1066. ReadLicense;
  1067. UpdateCaption;
  1068. FMenuDarkBackgroundBrush := TBrush.Create;
  1069. FMenuDarkHotOrSelectedBrush := TBrush.Create;
  1070. LightToolbarVirtualImageList.AutoFill := True;
  1071. ThemedMarkersAndACVirtualImageList.AutoFill := True;
  1072. UpdateThemeData(True);
  1073. FMenuBitmaps := TMenuBitmaps.Create;
  1074. FMenuBitmapsSize.cx := 0;
  1075. FMenuBitmapsSize.cy := 0;
  1076. FKeyMappedMenus := TKeyMappedMenus.Create;
  1077. FCallTipState.MaxCallTips := 1; { Just like SciTE 5.50 }
  1078. FUpdatePanelMessages := TUpdatePanelMessages.Create;
  1079. if CommandLineCompile then begin
  1080. ReadAndApplyTheme;
  1081. ReadSignTools(FSignTools);
  1082. PostMessage(Handle, WM_StartCommandLineCompile, 0, 0)
  1083. end else if CommandLineWizard then begin
  1084. { Stop Delphi from showing the compiler form }
  1085. Application.ShowMainForm := False;
  1086. { Show wizard form later }
  1087. ReadAndApplyTheme;
  1088. PostMessage(Handle, WM_StartCommandLineWizard, 0, 0);
  1089. end else begin
  1090. ReadAndApplyConfig;
  1091. ReadAndApplyTheme;
  1092. ReadSignTools(FSignTools);
  1093. PostMessage(Handle, WM_StartNormally, 0, 0);
  1094. end;
  1095. end;
  1096. destructor TMainForm.Destroy;
  1097. procedure SaveConfig;
  1098. var
  1099. Ini: TConfigIniFile;
  1100. WindowPlacement: TWindowPlacement;
  1101. begin
  1102. Ini := TConfigIniFile.Create;
  1103. try
  1104. { Theme state - can change without opening the options }
  1105. Ini.WriteInteger('Options', 'ThemeType', Ord(FOptions.ThemeType)); { Also see TOptionsClick }
  1106. { Menu check boxes state }
  1107. Ini.WriteBool('Options', 'ShowToolbar', ToolbarPanel.Visible);
  1108. Ini.WriteBool('Options', 'ShowStatusBar', StatusBar.Visible);
  1109. Ini.WriteBool('Options', 'LowPriorityDuringCompile', FOptions.LowPriorityDuringCompile);
  1110. { Window state }
  1111. WindowPlacement.length := SizeOf(WindowPlacement);
  1112. GetWindowPlacement(Handle, @WindowPlacement);
  1113. Ini.WriteInteger('State', 'WindowLeft', WindowPlacement.rcNormalPosition.Left);
  1114. Ini.WriteInteger('State', 'WindowTop', WindowPlacement.rcNormalPosition.Top);
  1115. Ini.WriteInteger('State', 'WindowRight', WindowPlacement.rcNormalPosition.Right);
  1116. Ini.WriteInteger('State', 'WindowBottom', WindowPlacement.rcNormalPosition.Bottom);
  1117. { The GetWindowPlacement docs claim that "flags" is always zero.
  1118. Fortunately, that's wrong. WPF_RESTORETOMAXIMIZED is set when the
  1119. window is either currently maximized, or currently minimized from a
  1120. previous maximized state. }
  1121. Ini.WriteBool('State', 'WindowMaximized', WindowPlacement.flags and WPF_RESTORETOMAXIMIZED <> 0);
  1122. Ini.WriteInteger('State', 'StatusPanelHeight', FromCurrentPPI(StatusPanel.Height));
  1123. { Zoom state }
  1124. Ini.WriteInteger('Options', 'Zoom', FMainMemo.Zoom); { Only saves the main memo's zoom }
  1125. finally
  1126. Ini.Free;
  1127. end;
  1128. end;
  1129. begin
  1130. UpdateThemeData(False);
  1131. Application.OnActivate := nil;
  1132. Application.OnIdle := nil;
  1133. if FOptionsLoaded and not (CommandLineCompile or CommandLineWizard) then
  1134. SaveConfig;
  1135. if FDevMode <> 0 then
  1136. GlobalFree(FDevMode);
  1137. if FDevNames <> 0 then
  1138. GlobalFree(FDevNames);
  1139. FUpdatePanelMessages.Free;
  1140. FNavStacks.Free;
  1141. FKeyMappedMenus.Free;
  1142. FMenuBitmaps.Free;
  1143. FMenuDarkBackgroundBrush.Free;
  1144. FMenuDarkHotOrSelectedBrush.Free;
  1145. FTheme.Free;
  1146. DestroyDebugInfo;
  1147. FIncludedFiles.Free;
  1148. FFindResults.Free;
  1149. FSignTools.Free;
  1150. FMRUParametersList.Free;
  1151. FMRUMainFilesList.Free;
  1152. FFileMemos.Free;
  1153. FHiddenFiles.Free;
  1154. FMemos.Free;
  1155. inherited;
  1156. end;
  1157. function TMainForm.GetBorderStyle: TFormBorderStyle;
  1158. begin
  1159. Result := inherited BorderStyle;
  1160. end;
  1161. procedure TMainForm.SetBorderStyle(Value: TFormBorderStyle);
  1162. begin
  1163. { Hack: To stop the Delphi IDE from adding Explicit* properties to the .dfm
  1164. file every time the unit is saved, we set BorderStyle=bsNone on the form.
  1165. At run-time, ignore that setting so that BorderStyle stays at the default
  1166. value, bsSizeable.
  1167. It would be simpler to change BorderStyle from bsNone to bsSizeable in the
  1168. form's constructor, but it doesn't quite work: when a form's handle is
  1169. created while BorderStyle=bsNone, Position=poDefault behaves like
  1170. poDefaultPosOnly (see TCustomForm.CreateParams). }
  1171. if Value <> bsNone then
  1172. inherited BorderStyle := Value;
  1173. end;
  1174. class procedure TMainForm.AppOnException(Sender: TObject; E: Exception);
  1175. begin
  1176. AppMessageBox(PChar(AddPeriod(E.Message)), SCompilerFormCaption,
  1177. MB_OK or MB_ICONSTOP);
  1178. end;
  1179. class procedure TMainForm.AppOnGetActiveFormHandle(var AHandle: HWND);
  1180. begin
  1181. { As of Delphi 11.3, the default code in TApplication.GetActiveFormHandle
  1182. (which runs after this handler) calls GetActiveWindow, and if that returns
  1183. 0, it calls GetLastActivePopup(Application.Handle).
  1184. The problem is that when the application isn't in the foreground,
  1185. GetActiveWindow returns 0, and when MainFormOnTaskBar=True, the
  1186. GetLastActivePopup call normally just returns Application.Handle (since
  1187. there are no popups owned by the application window).
  1188. So if the application calls Application.MessageBox while it isn't in the
  1189. foreground, that message box will be owned by Application.Handle, not by
  1190. the last-active window as it should be. That can lead to the message box
  1191. falling behind the main form in z-order.
  1192. To rectify that, when no window is active and MainFormOnTaskBar=True, we
  1193. fall back to returning the handle of the main form's last active popup,
  1194. which is the window that would be activated if the main form's taskbar
  1195. button were clicked. (If Application.Handle is active, we treat that the
  1196. same as no active window because Application.Handle shouldn't be the owner
  1197. of any windows when MainFormOnTaskBar=True.)
  1198. If there is no assigned main form or if MainFormOnTaskBar=False, then we
  1199. fall back to the default handling. }
  1200. if Application.MainFormOnTaskBar then begin
  1201. AHandle := GetActiveWindow;
  1202. if ((AHandle = 0) or (AHandle = Application.Handle)) and
  1203. Assigned(Application.MainForm) and
  1204. Application.MainForm.HandleAllocated then
  1205. AHandle := GetLastActivePopup(Application.MainFormHandle);
  1206. end;
  1207. end;
  1208. procedure TMainForm.FormAfterMonitorDpiChanged(Sender: TObject; OldDPI,
  1209. NewDPI: Integer);
  1210. begin
  1211. UpdateImages;
  1212. UpdateMarginsAndAutoCompleteIcons;
  1213. UpdateMarginsAndSquigglyAndCaretWidths;
  1214. UpdateOutputTabSetListsItemHeightAndDebugTimeWidth;
  1215. UpdateStatusPanelHeight(StatusPanel.Height);
  1216. end;
  1217. procedure TMainForm.FormCloseQuery(Sender: TObject;
  1218. var CanClose: Boolean);
  1219. begin
  1220. if IsWindowEnabled(Handle) then
  1221. CanClose := ConfirmCloseFile(True)
  1222. else
  1223. { CloseQuery is also called by the VCL when a WM_QUERYENDSESSION message
  1224. is received. Don't display message box if a modal dialog is already
  1225. displayed. }
  1226. CanClose := False;
  1227. end;
  1228. procedure TMainForm.FormKeyDown(Sender: TObject; var Key: Word;
  1229. Shift: TShiftState);
  1230. procedure AddControlToArray(const ControlToAdd: TWinControl; var Controls: TArray<TWinControl>;
  1231. var NControls: Integer);
  1232. begin
  1233. Inc(NControls);
  1234. SetLength(Controls, NControls);
  1235. Controls[NControls-1] := ControlToAdd;
  1236. end;
  1237. begin
  1238. var AShortCut := ShortCut(Key, Shift);
  1239. if (AShortCut = VK_ESCAPE) and BStopCompile.Enabled then begin
  1240. Key := 0; { Intentionally only done when BStopCompile is enabled to allow the memo to process it instead }
  1241. BStopCompileClick(Self)
  1242. end else if (AShortCut = FBackNavButtonShortCut) or
  1243. ((FBackNavButtonShortCut2 <> 0) and (AShortCut = FBackNavButtonShortCut2)) then begin
  1244. Key := 0;
  1245. if BackNavButton.Enabled then
  1246. BackNavButtonClick(Self);
  1247. end else if (AShortCut = FForwardNavButtonShortCut) or
  1248. ((FForwardNavButtonShortCut2 <> 0) and (AShortCut = FForwardNavButtonShortCut2)) then begin
  1249. Key := 0;
  1250. if ForwardNavButton.Enabled then
  1251. ForwardNavButtonClick(Self);
  1252. end else if (AShortCut >= FFirstTabSelectShortCut) and (AShortCut <= FLastTabSelectShortCut) then begin
  1253. Key := 0;
  1254. if MemosTabSet.Visible then begin
  1255. var TabIndex := AShortCut - FFirstTabSelectShortCut;
  1256. if TabIndex < 8 then begin
  1257. if TabIndex < MemosTabSet.Tabs.Count then
  1258. MemosTabSet.TabIndex := TabIndex;
  1259. end else { Ctrl+9 = Select last tab }
  1260. MemosTabSet.TabIndex := MemosTabSet.Tabs.Count-1;
  1261. end;
  1262. end else if AShortCut = FCompileShortCut2 then begin
  1263. Key := 0;
  1264. if BCompile.Enabled then
  1265. BCompileClick(Self);
  1266. end else if (Key = VK_F6) and not (ssAlt in Shift) then begin
  1267. { Move focus between the active memo, the active bottom pane, and the active banner }
  1268. Key := 0;
  1269. { First get the list of controls to toggle between }
  1270. var Controls: TArray<TWinControl> := [FActiveMemo];
  1271. var NControls := Length(Controls);
  1272. if StatusPanel.Visible then begin
  1273. var ControlToAdd: TWinControl := nil;
  1274. case OutputTabSet.TabIndex of
  1275. tiCompilerOutput: ControlToAdd := CompilerOutputList;
  1276. tiDebugOutput: ControlToAdd := DebugOutputList;
  1277. tiDebugCallStack: ControlToAdd := DebugCallStackList;
  1278. tiFindResults: ControlToAdd := FindResultsList;
  1279. end;
  1280. if ControlToAdd <> nil then
  1281. AddControlToArray(ControlToAdd, Controls, NControls);
  1282. end;
  1283. if UpdatePanel.Visible then begin
  1284. if FUpdatePanelMessages[UpdateLinkLabel.Tag].HasLink then
  1285. AddControlToArray(UpdateLinkLabel, Controls, NControls);
  1286. AddControlToArray(UpdatePanelDonateBitBtn, Controls, NControls);
  1287. AddControlToArray(UpdatePanelCloseBitBtn, Controls, NControls);
  1288. end;
  1289. { Now move focus to next }
  1290. if NControls > 1 then begin
  1291. for var I := 0 to NControls-1 do begin
  1292. if ActiveControl = Controls[I] then begin
  1293. if I = NControls-1 then
  1294. ActiveControl := Controls[0]
  1295. else
  1296. ActiveControl := Controls[I+1];
  1297. Exit;
  1298. end;
  1299. end;
  1300. end;
  1301. { Didn't move }
  1302. if ActiveControl <> FActiveMemo then
  1303. ActiveControl := FActiveMemo;
  1304. end;
  1305. end;
  1306. procedure TMainForm.MemoKeyDown(Sender: TObject; var Key: Word;
  1307. Shift: TShiftState);
  1308. procedure SimplifySelection(const AMemo: TIDEScintEdit);
  1309. begin
  1310. { The built in Esc (SCI_CANCEL) simply drops all additional selections
  1311. and does not empty the main selection, It doesn't matter if Esc is
  1312. pressed once or twice. Implement our own behaviour, same as VSCode.
  1313. Also see https://github.com/microsoft/vscode/issues/118835. }
  1314. if AMemo.SelectionCount > 1 then
  1315. AMemo.RemoveAdditionalSelections
  1316. else if not AMemo.SelEmpty then
  1317. AMemo.SetEmptySelection;
  1318. AMemo.ScrollCaretIntoView;
  1319. end;
  1320. procedure AddCursor(const AMemo: TIDEScintEdit; const Up: Boolean);
  1321. begin
  1322. { Does not try to keep the main selection. }
  1323. var Selections: TScintCaretAndAnchorList := nil;
  1324. var VirtualSpaces: TScintCaretAndAnchorList := nil;
  1325. try
  1326. Selections := TScintCaretAndAnchorList.Create;
  1327. VirtualSpaces := TScintCaretAndAnchorList.Create;
  1328. { Get all the virtual spaces as well before we start doing modifications }
  1329. AMemo.GetSelections(Selections, VirtualSpaces);
  1330. for var I := 0 to Selections.Count-1 do begin
  1331. var Selection := Selections[I];
  1332. var LineCaret := AMemo.GetLineFromPosition(Selection.CaretPos);
  1333. var LineAnchor := AMemo.GetLineFromPosition(Selection.AnchorPos);
  1334. if LineCaret = LineAnchor then begin
  1335. { Add selection with same caret and anchor offsets one line up or down. }
  1336. var OtherLine := LineCaret + IfThen(Up, -1, 1);;
  1337. if (OtherLine < 0) or (OtherLine >= AMemo.Lines.Count) then
  1338. Continue { Already at the top or bottom, can't add }
  1339. else begin
  1340. var LineStartPos := AMemo.GetPositionFromLine(LineCaret);
  1341. var CaretCharacterCount := AMemo.GetCharacterCount(LineStartPos, Selection.CaretPos) + VirtualSpaces[I].CaretPos;
  1342. var AnchorCharacterCount := AMemo.GetCharacterCount(LineStartPos, Selection.AnchorPos) + VirtualSpaces[I].AnchorPos;
  1343. var OtherLineStart := AMemo.GetPositionFromLine(OtherLine);
  1344. var MaxCharacterCount := AMemo.GetCharacterCount(OtherLineStart, AMemo.GetLineEndPosition(OtherLine));
  1345. var NewCaretCharacterCount := CaretCharacterCount;
  1346. var NewCaretVirtualSpace := 0;
  1347. var NewAnchorCharacterCount := AnchorCharacterCount;
  1348. var NewAnchorVirtualSpace := 0;
  1349. if NewCaretCharacterCount > MaxCharacterCount then begin
  1350. NewCaretVirtualSpace := NewCaretCharacterCount - MaxCharacterCount;
  1351. NewCaretCharacterCount := MaxCharacterCount;
  1352. end;
  1353. if NewAnchorCharacterCount > MaxCharacterCount then begin
  1354. NewAnchorVirtualSpace := NewAnchorCharacterCount - MaxCharacterCount;
  1355. NewAnchorCharacterCount := MaxCharacterCount;
  1356. end;
  1357. var NewSelection: TScintCaretAndAnchor;
  1358. NewSelection.CaretPos := AMemo.GetPositionRelative(OtherLineStart, NewCaretCharacterCount);
  1359. NewSelection.AnchorPos := AMemo.GetPositionRelative(OtherLineStart, NewAnchorCharacterCount);
  1360. { AddSelection trims selections except for the main selection so
  1361. we need to check that ourselves unfortunately. Not doing a check
  1362. gives a problem when you AddCursor two times starting with an
  1363. empty single selection. The result will be 4 cursors, with 2 of
  1364. them in the same place. The check below fixes this but not
  1365. other cases when there's only partial overlap and Scintilla still
  1366. behaves weird. The check also doesn't handle virtual space which
  1367. is why we ultimately don't set virtual space: it leads to duplicate
  1368. selections. }
  1369. var MainSelection := AMemo.Selection;
  1370. if not NewSelection.Range.Within(AMemo.Selection) then begin
  1371. AMemo.AddSelection(NewSelection.CaretPos, NewSelection.AnchorPos);
  1372. { if svsUserAccessible in FActiveMemo.VirtualSpaceOptions then begin
  1373. var MainSel := AMemo.MainSelection;
  1374. AMemo.SelectionCaretVirtualSpace[MainSel] := NewCaretVirtualSpace;
  1375. AMemo.SelectionAnchorVirtualSpace[MainSel] := NewAnchorVirtualSpace;
  1376. end; }
  1377. end;
  1378. end;
  1379. end else begin
  1380. { Extend multiline selection up or down. This is not the same as
  1381. LineExtendUp/Down because those can shrink instead of extend. }
  1382. var CaretBeforeAnchor := Selection.CaretPos < Selection.AnchorPos;
  1383. var Down := not Up;
  1384. var LineStartOrEnd, StartOrEndPos, VirtualSpace: Integer;
  1385. { Does it start (when going up) or end (when going down) at the caret or the anchor? }
  1386. if (Up and CaretBeforeAnchor) or (Down and not CaretBeforeAnchor) then begin
  1387. LineStartOrEnd := LineCaret;
  1388. StartOrEndPos := Selection.CaretPos;
  1389. VirtualSpace := VirtualSpaces[I].CaretPos;
  1390. end else begin
  1391. LineStartOrEnd := LineAnchor;
  1392. StartOrEndPos := Selection.AnchorPos;
  1393. VirtualSpace := VirtualSpaces[I].AnchorPos;
  1394. end;
  1395. var NewStartOrEndPos: Integer;
  1396. var NewVirtualSpace := 0;
  1397. { Go up or down one line or to the start or end of the document }
  1398. if (Up and (LineStartOrEnd > 0)) or (Down and (LineStartOrEnd < AMemo.Lines.Count-1)) then begin
  1399. var CharacterCount := AMemo.GetCharacterCount(AMemo.GetPositionFromLine(LineStartOrEnd), StartOrEndPos) + VirtualSpace;
  1400. var OtherLine := LineStartOrEnd + IfThen(Up, -1, 1);
  1401. var OtherLineStart := AMemo.GetPositionFromLine(OtherLine);
  1402. var MaxCharacterCount := AMemo.GetCharacterCount(OtherLineStart, AMemo.GetLineEndPosition(OtherLine));
  1403. var NewCharacterCount := CharacterCount;
  1404. if NewCharacterCount > MaxCharacterCount then begin
  1405. NewVirtualSpace := NewCharacterCount - MaxCharacterCount;
  1406. NewCharacterCount := MaxCharacterCount;
  1407. end;
  1408. NewStartOrEndPos := AMemo.GetPositionRelative(OtherLineStart, NewCharacterCount);
  1409. end else
  1410. NewStartOrEndPos := IfThen(Up, 0, AMemo.GetPositionFromLine(AMemo.Lines.Count));
  1411. { Move the caret or the anchor up or down to extend the selection }
  1412. if (Up and CaretBeforeAnchor) or (Down and not CaretBeforeAnchor) then begin
  1413. AMemo.SelectionCaretPosition[I] := NewStartOrEndPos;
  1414. if svsUserAccessible in FActiveMemo.VirtualSpaceOptions then
  1415. AMemo.SelectionCaretVirtualSpace[I] := NewVirtualSpace;
  1416. end else begin
  1417. AMemo.SelectionAnchorPosition[I] := NewStartOrEndPos;
  1418. if svsUserAccessible in FActiveMemo.VirtualSpaceOptions then
  1419. AMemo.SelectionAnchorVirtualSpace[I] := NewVirtualSpace;
  1420. end;
  1421. end;
  1422. end;
  1423. finally
  1424. VirtualSpaces.Free;
  1425. Selections.Free;
  1426. end;
  1427. end;
  1428. procedure AddCursorsToLineEnds(const AMemo: TIDEScintEdit);
  1429. begin
  1430. { Does not try to keep the main selection. Otherwise behaves the same as
  1431. observed in Visual Studio Code, see comments. }
  1432. var Selections: TScintCaretAndAnchorList := nil;
  1433. var VirtualSpaces: TScintCaretAndAnchorList := nil;
  1434. try
  1435. Selections := TScintCaretAndAnchorList.Create;
  1436. VirtualSpaces := TScintCaretAndAnchorList.Create;
  1437. AMemo.GetSelections(Selections, VirtualSpaces);
  1438. { First remove all empty selections }
  1439. for var I := Selections.Count-1 downto 0 do begin
  1440. var Selection := Selections[I];
  1441. var VirtualSpace := VirtualSpaces[I];
  1442. if (Selection.CaretPos + VirtualSpace.CaretPos) =
  1443. (Selection.AnchorPos + VirtualSpace.AnchorPos) then begin
  1444. Selections.Delete(I);
  1445. VirtualSpaces.Delete(I);
  1446. end;
  1447. end;
  1448. { If all selections were empty do nothing }
  1449. if Selections.Count = 0 then
  1450. Exit;
  1451. { Handle non empty selections }
  1452. for var I := Selections.Count-1 downto 0 do begin
  1453. var Selection := Selections[I];
  1454. var Line1 := AMemo.GetLineFromPosition(Selection.CaretPos);
  1455. var Line2 := AMemo.GetLineFromPosition(Selection.AnchorPos);
  1456. var SelSingleLine := Line1 = Line2;
  1457. if SelSingleLine then begin
  1458. { Single line selections are updated into empty selection at end of selection }
  1459. var VirtualSpace := VirtualSpaces[I];
  1460. if Selection.CaretPos + VirtualSpace.CaretPos > Selection.AnchorPos + VirtualSpace.AnchorPos then begin
  1461. Selection.AnchorPos := Selection.CaretPos;
  1462. VirtualSpace.AnchorPos := VirtualSpace.CaretPos;
  1463. end else begin
  1464. Selection.CaretPos := Selection.AnchorPos;
  1465. VirtualSpace.CaretPos := VirtualSpace.AnchorPos;
  1466. end;
  1467. Selections[I] := Selection;
  1468. VirtualSpaces[I] := VirtualSpace;
  1469. end else begin
  1470. { Multiline selections are replaced by empty selections at each end of line }
  1471. if Line1 > Line2 then begin
  1472. var TmpLine := Line1;
  1473. Line1 := Line2;
  1474. Line2 := TmpLine;
  1475. end;
  1476. { Ignore last line if the selection doesn't really select anything on that line }
  1477. if Selection.Range.EndPos = AMemo.GetPositionFromLine(Line2) then
  1478. Dec(Line2);
  1479. for var Line := Line1 to Line2 do begin
  1480. Selection.CaretPos := AMemo.GetLineEndPosition(Line);
  1481. Selection.AnchorPos := Selection.CaretPos;
  1482. Selections.Add(Selection);
  1483. VirtualSpaces.Add(TScintCaretAndAnchor.Create(0, 0));
  1484. end;
  1485. Selections.Delete(I);
  1486. VirtualSpaces.Delete(I);
  1487. end;
  1488. end;
  1489. { Send updated selections to memo }
  1490. for var I := 0 to Selections.Count-1 do begin
  1491. var Selection := Selections[I];
  1492. var VirtualSpace := VirtualSpaces[I];
  1493. if I = 0 then
  1494. AMemo.SetSingleSelection(Selection.CaretPos, Selection.AnchorPos)
  1495. else
  1496. AMemo.AddSelection(Selection.CaretPos, Selection.AnchorPos);
  1497. AMemo.SelectionCaretVirtualSpace[I] := VirtualSpaces[I].CaretPos;
  1498. AMemo.SelectionAnchorVirtualSpace[I] := VirtualSpaces[I].AnchorPos;
  1499. end;
  1500. finally
  1501. VirtualSpaces.Free;
  1502. Selections.Free;
  1503. end;
  1504. end;
  1505. begin
  1506. if (Key in [VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN, VK_HOME, VK_END]) then begin
  1507. var Memo := Sender as TIDEScintEdit;
  1508. { Make sure we don't break the special rectangular select shortcuts }
  1509. if Shift * [ssShift, ssAlt, ssCtrl] <> Memo.GetRectExtendShiftState(True) then begin
  1510. if Memo.SelectionMode in [ssmRectangular, ssmThinRectangular] then begin
  1511. { Allow left/right/etc. navigation with rectangular selection, see
  1512. https://sourceforge.net/p/scintilla/feature-requests/1275/ and
  1513. https://sourceforge.net/p/scintilla/bugs/2412/#cb37
  1514. Notepad++ calls this "Enable Column Selection to Multi-editing" which
  1515. is on by default and in VSCode and VS it's also on by default. }
  1516. Memo.SelectionMode := ssmStream;
  1517. end;
  1518. end;
  1519. { Key is not cleared to allow Scintilla to do the actual handling }
  1520. end;
  1521. if Key = VK_F1 then begin
  1522. Key := 0;
  1523. var HelpFile := GetHelpFile;
  1524. if Assigned(HtmlHelp) then begin
  1525. HtmlHelp(GetDesktopWindow, PChar(HelpFile), HH_DISPLAY_TOPIC, 0);
  1526. var S := FActiveMemo.WordAtCaret;
  1527. if S <> '' then begin
  1528. var KLink: THH_AKLINK;
  1529. FillChar(KLink, SizeOf(KLink), 0);
  1530. KLink.cbStruct := SizeOf(KLink);
  1531. KLink.pszKeywords := PChar(S);
  1532. KLink.fIndexOnFail := True;
  1533. HtmlHelp(GetDesktopWindow, PChar(HelpFile), HH_KEYWORD_LOOKUP, DWORD(@KLink));
  1534. end;
  1535. end;
  1536. end else if ((Key = Ord('V')) or (Key = VK_INSERT)) and (Shift * [ssShift, ssAlt, ssCtrl] = [ssCtrl]) then begin
  1537. if FActiveMemo.CanPaste then
  1538. if MultipleSelectionPasteFromClipboard(FActiveMemo) then
  1539. Key := 0;
  1540. end else if (Key = VK_SPACE) and (Shift * [ssShift, ssAlt, ssCtrl] = [ssShift, ssCtrl]) then begin
  1541. Key := 0;
  1542. { Based on SciTE 5.50's SciTEBase::MenuCommand IDM_SHOWCALLTIP }
  1543. if FActiveMemo.CallTipActive then begin
  1544. FCallTipState.CurrentCallTip := IfThen(FCallTipState.CurrentCallTip + 1 = FCallTipState.MaxCallTips, 0, FCallTipState.CurrentCallTip + 1);
  1545. UpdateCallTipFunctionDefinition;
  1546. end else begin
  1547. FCallTipState.BraceCount := 1; { Missing in SciTE, see https://sourceforge.net/p/scintilla/bugs/2446/ }
  1548. InitiateCallTip(#0);
  1549. end;
  1550. end else begin
  1551. var AShortCut := ShortCut(Key, Shift);
  1552. { Check if the memo keymap wants us to handle the shortcut but first check
  1553. the menu keymap didn't already claim the same shortcut. Other shortcuts
  1554. (which are always same and not set by the menu keymap) are assumed to
  1555. never conflict. }
  1556. if not FKeyMappedMenus.ContainsKey(AShortCut) then begin
  1557. var ComplexCommand := FActiveMemo.GetComplexCommand(AShortCut);
  1558. if ComplexCommand <> ccNone then begin
  1559. if Key <> VK_ESCAPE then { Allow Scintilla to see Esc }
  1560. Key := 0;
  1561. case ComplexCommand of
  1562. ccSelectNextOccurrence:
  1563. ESelectNextOccurrenceClick(Self);
  1564. ccSelectAllOccurrences:
  1565. ESelectAllOccurrencesClick(Self);
  1566. ccSelectAllFindMatches:
  1567. ESelectAllFindMatchesClick(Self);
  1568. ccFoldLine:
  1569. EFoldOrUnfoldLineClick(EFoldLine);
  1570. ccUnfoldLine:
  1571. EFoldOrUnfoldLineClick(EUnfoldLine);
  1572. ccSimplifySelection:
  1573. SimplifySelection(FActiveMemo);
  1574. ccToggleLinesComment:
  1575. EToggleLinesCommentClick(Self); //GetCompexCommand already checked ReadOnly for us
  1576. ccAddCursorUp, ccAddCursorDown:
  1577. AddCursor(FActiveMemo, ComplexCommand = ccAddCursorUp);
  1578. ccBraceMatch:
  1579. EBraceMatchClick(Self);
  1580. ccAddCursorsToLineEnds:
  1581. AddCursorsToLineEnds(FActiveMemo);
  1582. else
  1583. raise Exception.Create('Unknown ComplexCommand');
  1584. end;
  1585. end;
  1586. end;
  1587. end;
  1588. end;
  1589. procedure TMainForm.MemoKeyPress(Sender: TObject; var Key: Char);
  1590. begin
  1591. if ((Key = #9) or (Key = ' ')) and (GetKeyState(VK_CONTROL) < 0) then begin
  1592. { About #9, as Wikipedia explains: "The most known and common tab is a
  1593. horizontal tabulation <..> and may be referred to as Ctrl+I." Ctrl+I is
  1594. (just like in Visual Studio Code) our alternative code completion character
  1595. because Ctrl+Space is used by the Chinese IME and Alt+Right is used for the
  1596. forward button. So that's why we handle #9 here. Doesn't mean Ctrl+Tab
  1597. doesn't work: it doesnt trigger KeyPress, even if it wasn't a menu
  1598. shortcut for Next Tab (which it is). }
  1599. InitiateAutoComplete(#0);
  1600. Key := #0;
  1601. end else if (Key <= #31) or (Key = #127) then begin
  1602. { Prevent "control characters" from being entered in text. Don't need to be
  1603. concerned about #9 or #10 or #13 etc here. Based on Notepad++'s WM_CHAR
  1604. handling in ScintillaEditView.cpp.
  1605. Also don't need to be concerned about shortcuts like Ctrl+Shift+- which
  1606. equals #31. }
  1607. Key := #0
  1608. end;
  1609. end;
  1610. procedure TMainForm.FormResize(Sender: TObject);
  1611. begin
  1612. { Make sure the status panel's height is decreased if necessary in response
  1613. to the form's height decreasing }
  1614. if StatusPanel.Visible then
  1615. UpdateStatusPanelHeight(StatusPanel.Height);
  1616. end;
  1617. procedure TMainForm.WndProc(var Message: TMessage);
  1618. begin
  1619. { Without this, the status bar's owner drawn panels sometimes get corrupted and show
  1620. menu items instead. See:
  1621. http://groups.google.com/group/borland.public.delphi.vcl.components.using/browse_thread/thread/e4cb6c3444c70714 }
  1622. with Message do
  1623. case Msg of
  1624. WM_DRAWITEM:
  1625. with PDrawItemStruct(Message.LParam)^ do
  1626. if (CtlType = ODT_MENU) and not IsMenu(hwndItem) then
  1627. CtlType := ODT_STATIC;
  1628. end;
  1629. inherited
  1630. end;
  1631. function TMainForm.IsShortCut(var Message: TWMKey): Boolean;
  1632. begin
  1633. { Key messages are forwarded by the VCL to the main form for ShortCut
  1634. processing. In Delphi 5+, however, this happens even when a TFindDialog
  1635. is active, causing Ctrl+V/Esc/etc. to be intercepted by the main form.
  1636. Work around this by always returning False when not Active. }
  1637. if Active then
  1638. Result := inherited IsShortCut(Message)
  1639. else
  1640. Result := False;
  1641. end;
  1642. procedure TMainForm.UpdateCaption;
  1643. var
  1644. NewCaption: String;
  1645. begin
  1646. if FMainMemo.Filename = '' then
  1647. NewCaption := GetFileTitle(FMainMemo.Filename)
  1648. else begin
  1649. if FOptions.FullPathInTitleBar then
  1650. NewCaption := FMainMemo.Filename
  1651. else
  1652. NewCaption := GetDisplayFilename(FMainMemo.Filename);
  1653. end;
  1654. NewCaption := NewCaption + ' - ' + SCompilerFormCaption + ' ' +
  1655. String(FCompilerVersion.Version) + ' - ' + GetLicenseeDescription;
  1656. if FCompiling then
  1657. NewCaption := NewCaption + ' [Compiling]'
  1658. else if FDebugging then begin
  1659. if not FPaused then
  1660. NewCaption := NewCaption + ' [Running]'
  1661. else
  1662. NewCaption := NewCaption + ' [Paused]';
  1663. end;
  1664. Caption := NewCaption;
  1665. if not CommandLineWizard then
  1666. Application.Title := NewCaption;
  1667. end;
  1668. procedure TMainForm.UpdateNewMainFileButtons;
  1669. begin
  1670. if FOptions.UseWizard then begin
  1671. FNewMainFile.Caption := '&New...';
  1672. FNewMainFile.OnClick := FNewMainFileUserWizardClick;
  1673. NewMainFileButton.OnClick := FNewMainFileUserWizardClick;
  1674. end else begin
  1675. FNewMainFile.Caption := '&New';
  1676. FNewMainFile.OnClick := FNewMainFileClick;
  1677. NewMainFileButton.OnClick := FNewMainFileClick;
  1678. end;
  1679. end;
  1680. procedure TMainForm.NewMainFile(const IsReload: Boolean);
  1681. var
  1682. Memo: TIDEScintFileEdit;
  1683. begin
  1684. HideError;
  1685. FUninstExe := '';
  1686. if FDebugTarget <> dtSetup then begin
  1687. FDebugTarget := dtSetup;
  1688. UpdateTargetMenu;
  1689. end;
  1690. FHiddenFiles.Clear;
  1691. InvalidateStatusPanel(spHiddenFilesCount);
  1692. for Memo in FFileMemos do
  1693. if Memo.Used then
  1694. Memo.BreakPoints.Clear;
  1695. DestroyDebugInfo;
  1696. FMainMemo.Filename := '';
  1697. UpdateCaption;
  1698. FMainMemo.SaveEncoding := seUTF8WithoutBOM;
  1699. if not IsReload then
  1700. FMainMemo.Lines.Clear;
  1701. FModifiedAnySinceLastCompile := True;
  1702. FPreprocessorOutput := '';
  1703. FIncludedFiles.Clear;
  1704. UpdatePreprocMemos(IsReload);
  1705. if not IsReload then
  1706. FMainMemo.ClearUndo;
  1707. FNavStacks.Clear;
  1708. UpdateNavButtons;
  1709. FCurrentNavItem.Invalidate;
  1710. end;
  1711. { Breakpoints are preserved on a per-file basis }
  1712. procedure TMainForm.LoadBreakPointLinesAndUpdateLineMarkers(const AMemo: TIDEScintFileEdit);
  1713. begin
  1714. if AMemo.BreakPoints.Count <> 0 then
  1715. raise Exception.Create('AMemo.BreakPoints.Count <> 0'); { NewMainFile or OpenFile should have cleared these }
  1716. try
  1717. var HadSkippedBreakPoint := False;
  1718. var Strings := TStringList.Create;
  1719. try
  1720. LoadBreakPointLines(AMemo.FileName, Strings);
  1721. for var LineAsString in Strings do begin
  1722. var Line := LineAsString.ToInteger;
  1723. if Line < AMemo.Lines.Count then
  1724. AMemo.BreakPoints.Add(Line)
  1725. else
  1726. HadSkippedBreakPoint := True;
  1727. end;
  1728. finally
  1729. Strings.Free;
  1730. end;
  1731. for var Line in AMemo.BreakPoints do
  1732. UpdateLineMarkers(AMemo, Line);
  1733. { If there were breakpoints beyond the end of file get rid of them so they
  1734. don't magically reappear on a reload of an externally edited and grown
  1735. file }
  1736. if HadSkippedBreakPoint then
  1737. BuildAndSaveBreakPointLines(AMemo);
  1738. except
  1739. { Ignore any exceptions }
  1740. end;
  1741. end;
  1742. procedure TMainForm.BuildAndSaveBreakPointLines(const AMemo: TIDEScintFileEdit);
  1743. begin
  1744. try
  1745. if AMemo.FileName <> '' then begin
  1746. var Strings := TStringList.Create;
  1747. try
  1748. for var Line in AMemo.BreakPoints do
  1749. Strings.Add(Line.ToString);
  1750. SaveBreakPointLines(AMemo.FileName, Strings);
  1751. finally
  1752. Strings.Free;
  1753. end;
  1754. end;
  1755. except
  1756. { Handle exceptions locally; failure to save the breakpoint lines list should not be
  1757. a fatal error }
  1758. Application.HandleException(Self);
  1759. end;
  1760. end;
  1761. { Known included and hidden files are preserved on a per-main-file basis }
  1762. procedure TMainForm.LoadKnownIncludedAndHiddenFilesAndUpdateMemos;
  1763. begin
  1764. if FIncludedFiles.Count <> 0 then
  1765. raise Exception.Create('FIncludedFiles.Count <> 0'); { NewMainFile should have cleared these }
  1766. try
  1767. if AFilename <> '' then begin
  1768. var Strings := TStringList.Create;
  1769. try
  1770. LoadKnownIncludedAndHiddenFiles(FMainMemo.FileName, Strings, FHiddenFiles);
  1771. if Strings.Count > 0 then begin
  1772. try
  1773. for var Filename in Strings do begin
  1774. var IncludedFile := TIncludedFile.Create;
  1775. IncludedFile.Filename := Filename;
  1776. IncludedFile.CompilerFileIndex := UnknownCompilerFileIndex;
  1777. IncludedFile.HasLastWriteTime := GetLastWriteTimeOfFile(IncludedFile.Filename,
  1778. @IncludedFile.LastWriteTime);
  1779. FIncludedFiles.Add(IncludedFile);
  1780. end;
  1781. finally
  1782. UpdatePreprocMemos;
  1783. end;
  1784. end;
  1785. finally
  1786. Strings.Free;
  1787. end;
  1788. end;
  1789. except
  1790. { Ignore any exceptions }
  1791. end;
  1792. end;
  1793. procedure TMainForm.BuildAndSaveKnownIncludedAndHiddenFiles;
  1794. begin
  1795. try
  1796. if FMainMemo.FileName <> '' then begin
  1797. var Strings := TStringList.Create;
  1798. try
  1799. for var IncludedFile in FIncludedFiles do
  1800. Strings.Add(IncludedFile.Filename);
  1801. SaveKnownIncludedAndHiddenFiles(FMainMemo.FileName, Strings, FHiddenFiles);
  1802. finally
  1803. Strings.Free;
  1804. end;
  1805. end;
  1806. except
  1807. { Handle exceptions locally; failure to save the includes list should not be
  1808. a fatal error }
  1809. Application.HandleException(Self);
  1810. end;
  1811. end;
  1812. procedure TMainForm.NewMainFileUsingWizard;
  1813. var
  1814. WizardForm: TWizardForm;
  1815. SaveEnabled: Boolean;
  1816. begin
  1817. WizardForm := TWizardForm.Create(Application);
  1818. try
  1819. SaveEnabled := Enabled;
  1820. if CommandLineWizard then begin
  1821. WizardForm.WizardName := CommandLineWizardName;
  1822. { Must disable MainForm even though it isn't shown, otherwise
  1823. menu keyboard shortcuts (such as Ctrl+O) still work }
  1824. Enabled := False;
  1825. end;
  1826. try
  1827. if WizardForm.ShowModal <> mrOk then
  1828. Exit;
  1829. finally
  1830. Enabled := SaveEnabled;
  1831. end;
  1832. if CommandLineWizard then begin
  1833. SaveTextToFile(CommandLineFileName, WizardForm.ResultScript, seUTF8WithoutBOM);
  1834. end else begin
  1835. NewMainFile;
  1836. FMainMemo.Lines.Text := WizardForm.ResultScript;
  1837. FMainMemo.ClearUndo;
  1838. if WizardForm.Result = wrComplete then begin
  1839. FMainMemo.ForceModifiedState;
  1840. if MsgBox('Would you like to compile the new script now?', SCompilerFormCaption, mbConfirmation, MB_YESNO) = IDYES then
  1841. BCompileClick(Self);
  1842. end;
  1843. end;
  1844. finally
  1845. WizardForm.Free;
  1846. end;
  1847. end;
  1848. procedure TMainForm.OpenFile(AMemo: TIDEScintFileEdit; AFilename: String;
  1849. const MainMemoAddToRecentDocs, IsReload: Boolean);
  1850. function GetStreamSaveEncoding(const Stream: TStream): TSaveEncoding;
  1851. var
  1852. Buf: array[0..2] of Byte;
  1853. begin
  1854. Result := seAuto;
  1855. var StreamSize := Stream.Size;
  1856. var CappedSize: Integer;
  1857. if StreamSize > High(Integer) then
  1858. CappedSize := High(Integer)
  1859. else
  1860. CappedSize := Integer(StreamSize);
  1861. if (CappedSize >= SizeOf(Buf)) and (Stream.Read(Buf, SizeOf(Buf)) = SizeOf(Buf)) and
  1862. (Buf[0] = $EF) and (Buf[1] = $BB) and (Buf[2] = $BF) then
  1863. Result := seUTF8WithBOM
  1864. else begin
  1865. Stream.Seek(0, soFromBeginning);
  1866. var S: AnsiString;
  1867. SetLength(S, CappedSize);
  1868. SetLength(S, Stream.Read(S[1], CappedSize));
  1869. if DetectUTF8Encoding(S) in [etUSASCII, etUTF8] then
  1870. Result := seUTF8WithoutBOM;
  1871. end;
  1872. end;
  1873. function GetEncoding(const SaveEncoding: TSaveEncoding): TEncoding;
  1874. begin
  1875. if SaveEncoding in [seUTF8WithBOM, seUTF8WithoutBOM] then
  1876. Result := TEncoding.UTF8
  1877. else
  1878. Result := nil;
  1879. end;
  1880. { Same as TStrings.LoadFromStream, except that it returns the loaded string }
  1881. function LoadFromStream(const Stream: TStream; const Encoding: TEncoding): String;
  1882. begin
  1883. const Size = Stream.Size - Stream.Position;
  1884. var Buffer: TBytes;
  1885. SetLength(Buffer, Size);
  1886. Stream.Read(Buffer, 0, Size);
  1887. var BufferEncoding := Encoding;
  1888. const PreambleSize = TEncoding.GetBufferEncoding(Buffer, BufferEncoding, TEncoding.Default);
  1889. Result := BufferEncoding.GetString(Buffer, PreambleSize, Length(Buffer) - PreambleSize);
  1890. end;
  1891. type
  1892. TFilePosition = record
  1893. Selection: TScintCaretAndAnchor;
  1894. ScrollPosition: Integer;
  1895. end;
  1896. { See SciTEBase::CheckReload }
  1897. function GetFilePosition(const AMemo: TScintEdit): TFilePosition;
  1898. begin
  1899. Result.Selection.CaretPos := AMemo.CaretPosition;
  1900. Result.Selection.AnchorPos := AMemo.AnchorPosition;
  1901. Result.ScrollPosition := AMemo.GetDocLineFromVisibleLine(AMemo.TopLine);
  1902. end;
  1903. { See SciTEBase::CheckReload }
  1904. procedure DisplayAround(const AMemo: TScintEdit; const FilePosition: TFilePosition);
  1905. begin
  1906. AMemo.Call(SCI_SETSEL, FilePosition.Selection.AnchorPos, FilePosition.Selection.CaretPos);
  1907. const CurTop = AMemo.TopLine;
  1908. const LineTop = AMemo.GetVisibleLineFromDocLine(FilePosition.ScrollPosition);
  1909. AMemo.Call(SCI_LINESCROLL, 0, LineTop - CurTop);
  1910. AMemo.ChooseCaretX;
  1911. end;
  1912. var
  1913. Stream: TFileStream;
  1914. begin
  1915. AMemo.OpeningFile := True;
  1916. try
  1917. AFilename := PathExpand(AFilename);
  1918. const NameChange = PathCompare(AMemo.Filename, AFilename) <> 0;
  1919. const FilePosition = GetFilePosition(AMemo);
  1920. if IsReload then
  1921. AMemo.BeginUndoAction;
  1922. Stream := TFileStream.Create(AFilename, fmOpenRead or fmShareDenyNone);
  1923. try
  1924. if AMemo = FMainMemo then
  1925. NewMainFile(IsReload)
  1926. else begin
  1927. AMemo.BreakPoints.Clear;
  1928. if DestroyLineState(AMemo) then
  1929. UpdateAllMemoLineMarkers(AMemo);
  1930. if NameChange then { Also see below the other case which needs to be done after load }
  1931. RemoveMemoFromNav(AMemo);
  1932. end;
  1933. GetFileTime(Stream.Handle, nil, nil, @AMemo.FileLastWriteTime);
  1934. AMemo.SaveEncoding := GetStreamSaveEncoding(Stream);
  1935. Stream.Seek(0, soFromBeginning);
  1936. const TextStr = LoadFromStream(Stream, GetEncoding(AMemo.SaveEncoding));
  1937. if IsReload and (AMemo.ChangeHistory <> schDisabled) then begin
  1938. { Workaround to minimize change history on reload }
  1939. AMemo.Call(SCI_TARGETWHOLEDOCUMENT, 0, 0);
  1940. const RawTextStr = AMemo.ConvertStringToRawString(TextStr);
  1941. AMemo.Call(SCI_REPLACETARGETMINIMAL, Length(RawTextStr), RawTextStr);
  1942. end else
  1943. AMemo.Lines.Text := TextStr;
  1944. if (AMemo <> FMainMemo) and not NameChange then
  1945. RemoveMemoBadLinesFromNav(AMemo);
  1946. finally
  1947. Stream.Free;
  1948. if IsReload then
  1949. AMemo.EndUndoAction;
  1950. end;
  1951. if IsReload then begin
  1952. DisplayAround(AMemo, FilePosition);
  1953. AMemo.SetSavePoint;
  1954. end else
  1955. AMemo.ClearUndo;
  1956. if AMemo = FMainMemo then begin
  1957. AMemo.Filename := AFilename;
  1958. UpdateCaption;
  1959. ModifyMRUMainFilesList(AFilename, True);
  1960. if MainMemoAddToRecentDocs then
  1961. AddFileToRecentDocs(AFilename);
  1962. LoadKnownIncludedAndHiddenFilesAndUpdateMemos(AFilename);
  1963. InvalidateStatusPanel(spHiddenFilesCount);
  1964. end;
  1965. LoadBreakPointLinesAndUpdateLineMarkers(AMemo);
  1966. finally
  1967. AMemo.OpeningFile := False;
  1968. end;
  1969. end;
  1970. procedure TMainForm.OpenMRUMainFile(const AFilename: String);
  1971. { Same as OpenFile, but offers to remove the file from the MRU list if it
  1972. cannot be opened }
  1973. begin
  1974. try
  1975. OpenFile(FMainMemo, AFilename, True);
  1976. except
  1977. Application.HandleException(Self);
  1978. if MsgBoxFmt('There was an error opening the file. Remove it from the list?',
  1979. [AFilename], SCompilerFormCaption, mbError, MB_YESNO) = IDYES then begin
  1980. ModifyMRUMainFilesList(AFilename, False);
  1981. DeleteBreakPointLines(AFilename);
  1982. DeleteKnownIncludedAndHiddenFiles(AFilename);
  1983. end;
  1984. end;
  1985. end;
  1986. function TMainForm.SaveFile(const AMemo: TIDEScintFileEdit; const SaveAs: Boolean): Boolean;
  1987. procedure SaveMemoTo(const FN: String);
  1988. var
  1989. TempFN, BackupFN: String;
  1990. Buf: array[0..4095] of Char;
  1991. begin
  1992. { Save to a temporary file; don't overwrite existing files in place. This
  1993. way, if the system crashes or the disk runs out of space during the save,
  1994. the existing file will still be intact. }
  1995. if GetTempFileName(PChar(PathExtractDir(FN)), 'iss', 0, Buf) = 0 then
  1996. raise Exception.CreateFmt('Error creating file (code %d). Could not save file',
  1997. [GetLastError]);
  1998. TempFN := Buf;
  1999. try
  2000. SaveTextToFile(TempFN, AMemo.Lines.Text, AMemo.SaveEncoding);
  2001. { Back up existing file if needed }
  2002. if FOptions.MakeBackups and NewFileExists(FN) then begin
  2003. BackupFN := PathChangeExt(FN, '.~is');
  2004. DeleteFile(BackupFN);
  2005. if not RenameFile(FN, BackupFN) then
  2006. raise Exception.Create('Error creating backup file. Could not save file');
  2007. end;
  2008. { Delete existing file }
  2009. if not DeleteFile(FN) and (GetLastError <> ERROR_FILE_NOT_FOUND) then
  2010. raise Exception.CreateFmt('Error removing existing file (code %d). Could not save file',
  2011. [GetLastError]);
  2012. except
  2013. DeleteFile(TempFN);
  2014. raise;
  2015. end;
  2016. { Rename temporary file.
  2017. Note: This is outside the try..except because we already deleted the
  2018. existing file, and don't want the temp file also deleted in the unlikely
  2019. event that the rename fails. }
  2020. if not RenameFile(TempFN, FN) then
  2021. raise Exception.CreateFmt('Error renaming temporary file (code %d). Could not save file',
  2022. [GetLastError]);
  2023. GetLastWriteTimeOfFile(FN, @AMemo.FileLastWriteTime);
  2024. end;
  2025. var
  2026. FN: String;
  2027. begin
  2028. Result := False;
  2029. var OldName := AMemo.Filename;
  2030. if SaveAs or (AMemo.Filename = '') then begin
  2031. if AMemo <> FMainMemo then
  2032. raise Exception.Create('Internal error: AMemo <> FMainMemo');
  2033. FN := AMemo.Filename;
  2034. if not NewGetSaveFileName('', FN, '', SCompilerOpenFilter, 'iss', Handle) then Exit;
  2035. FN := PathExpand(FN);
  2036. SaveMemoTo(FN);
  2037. AMemo.Filename := FN;
  2038. UpdateCaption;
  2039. end else
  2040. SaveMemoTo(AMemo.Filename);
  2041. AMemo.SetSavePoint;
  2042. if not FOptions.UndoAfterSave then
  2043. AMemo.ClearUndo(False);
  2044. Result := True;
  2045. if AMemo = FMainMemo then begin
  2046. ModifyMRUMainFilesList(AMemo.Filename, True);
  2047. if PathCompare(AMemo.Filename, OldName) <> 0 then begin
  2048. if OldName <> '' then begin
  2049. DeleteBreakPointLines(OldName);
  2050. DeleteKnownIncludedAndHiddenFiles(OldName);
  2051. end;
  2052. BuildAndSaveBreakPointLines(AMemo);
  2053. BuildAndSaveKnownIncludedAndHiddenFiles;
  2054. end;
  2055. end;
  2056. end;
  2057. function TMainForm.ConfirmCloseFile(const PromptToSave: Boolean): Boolean;
  2058. function PromptToSaveMemo(const AMemo: TIDEScintFileEdit): Boolean;
  2059. var
  2060. FileTitle: String;
  2061. begin
  2062. Result := True;
  2063. if AMemo.Modified then begin
  2064. FileTitle := GetFileTitle(AMemo.Filename);
  2065. case MsgBox('The text in the ' + FileTitle + ' file has changed.'#13#10#13#10 +
  2066. 'Do you want to save the changes?', SCompilerFormCaption, mbError,
  2067. MB_YESNOCANCEL) of
  2068. IDYES: Result := SaveFile(AMemo, False);
  2069. IDNO: ;
  2070. else
  2071. Result := False;
  2072. end;
  2073. end;
  2074. end;
  2075. var
  2076. Memo: TIDEScintFileEdit;
  2077. begin
  2078. if FCompiling then begin
  2079. MsgBox('Please stop the compile process before performing this command.',
  2080. SCompilerFormCaption, mbError, MB_OK);
  2081. Result := False;
  2082. Exit;
  2083. end;
  2084. if FDebugging and not AskToDetachDebugger then begin
  2085. Result := False;
  2086. Exit;
  2087. end;
  2088. Result := True;
  2089. if PromptToSave then begin
  2090. for Memo in FFileMemos do begin
  2091. if Memo.Used then begin
  2092. Result := PromptToSaveMemo(Memo);
  2093. if not Result then
  2094. Exit;
  2095. end;
  2096. end;
  2097. end;
  2098. end;
  2099. procedure TMainForm.ClearMRUMainFilesList;
  2100. begin
  2101. try
  2102. ClearMRUList(FMRUMainFilesList, 'ScriptFileHistoryNew');
  2103. except
  2104. { Ignore any exceptions. }
  2105. end;
  2106. end;
  2107. procedure TMainForm.ReadMRUMainFilesList;
  2108. begin
  2109. try
  2110. ReadMRUList(FMRUMainFilesList, 'ScriptFileHistoryNew', 'History');
  2111. except
  2112. { Ignore any exceptions. }
  2113. end;
  2114. end;
  2115. procedure TMainForm.ModifyMRUMainFilesList(const AFilename: String;
  2116. const AddNewItem: Boolean);
  2117. begin
  2118. { Load most recent items first, just in case they've changed }
  2119. try
  2120. ReadMRUMainFilesList;
  2121. except
  2122. { Ignore any exceptions. }
  2123. end;
  2124. try
  2125. ModifyMRUList(FMRUMainFilesList, 'ScriptFileHistoryNew', 'History', AFileName, AddNewItem, @PathCompare);
  2126. except
  2127. { Handle exceptions locally; failure to save the MRU list should not be
  2128. a fatal error. }
  2129. Application.HandleException(Self);
  2130. end;
  2131. end;
  2132. procedure TMainForm.ReadMRUParametersList;
  2133. begin
  2134. try
  2135. ReadMRUList(FMRUParametersList, 'ParametersHistory', 'History');
  2136. except
  2137. { Ignore any exceptions. }
  2138. end;
  2139. end;
  2140. procedure TMainForm.ModifyMRUParametersList(const AParameter: String;
  2141. const AddNewItem: Boolean);
  2142. begin
  2143. { Load most recent items first, just in case they've changed }
  2144. try
  2145. ReadMRUParametersList;
  2146. except
  2147. { Ignore any exceptions. }
  2148. end;
  2149. try
  2150. ModifyMRUList(FMRUParametersList, 'ParametersHistory', 'History', AParameter, AddNewItem, @CompareText);
  2151. except
  2152. { Handle exceptions locally; failure to save the MRU list should not be
  2153. a fatal error. }
  2154. Application.HandleException(Self);
  2155. end;
  2156. end;
  2157. procedure TMainForm.StatusMessage(const Kind: TStatusMessageKind; const S: String);
  2158. begin
  2159. AddLines(CompilerOutputList, S, TObject(Kind), False, alpNone, 0);
  2160. CompilerOutputList.Update;
  2161. end;
  2162. procedure TMainForm.DebugLogMessage(const S: String);
  2163. begin
  2164. AddLines(DebugOutputList, S, nil, True, alpTimestamp, FDebugLogListTimestampsWidth);
  2165. DebugOutputList.Update;
  2166. end;
  2167. procedure TMainForm.DebugShowCallStack(const CallStack: String; const CallStackCount: Cardinal);
  2168. begin
  2169. DebugCallStackList.Clear;
  2170. AddLines(DebugCallStackList, CallStack, nil, True, alpCountdown, FCallStackCount-1);
  2171. DebugCallStackList.Items.Insert(0, '*** [Code] Call Stack');
  2172. DebugCallStackList.Update;
  2173. end;
  2174. type
  2175. PAppData = ^TAppData;
  2176. TAppData = record
  2177. Form: TMainForm;
  2178. Filename: String;
  2179. Lines: TStringList;
  2180. CurLineNumber: Integer;
  2181. CurLine: String;
  2182. OutputExe: String;
  2183. DebugInfo: Pointer;
  2184. ErrorMsg: String;
  2185. ErrorFilename: String;
  2186. ErrorLine: Integer;
  2187. Aborted: Boolean;
  2188. end;
  2189. function CompilerCallbackProc(Code: Integer; var Data: TCompilerCallbackData;
  2190. AppData: Longint): Integer; stdcall;
  2191. procedure DecodeIncludedFilenames(P: PChar; const IncludedFiles: TIncludedFiles);
  2192. var
  2193. IncludedFile: TIncludedFile;
  2194. I: Integer;
  2195. begin
  2196. IncludedFiles.Clear;
  2197. if P = nil then
  2198. Exit;
  2199. I := 0;
  2200. while P^ <> #0 do begin
  2201. if not IsISPPBuiltins(P) then begin
  2202. IncludedFile := TIncludedFile.Create;
  2203. IncludedFile.Filename := GetCleanFileNameOfFile(P);
  2204. IncludedFile.CompilerFileIndex := I;
  2205. IncludedFile.HasLastWriteTime := GetLastWriteTimeOfFile(IncludedFile.Filename,
  2206. @IncludedFile.LastWriteTime);
  2207. IncludedFiles.Add(IncludedFile);
  2208. end;
  2209. Inc(P, StrLen(P) + 1);
  2210. Inc(I);
  2211. end;
  2212. end;
  2213. procedure CleanHiddenFiles(const IncludedFiles: TIncludedFiles; const HiddenFiles: TStringList);
  2214. var
  2215. HiddenFileIncluded: array of Boolean;
  2216. begin
  2217. if HiddenFiles.Count > 0 then begin
  2218. { Clean previously hidden files which are no longer included }
  2219. if IncludedFiles.Count > 0 then begin
  2220. SetLength(HiddenFileIncluded, HiddenFiles.Count);
  2221. for var I := 0 to HiddenFiles.Count-1 do
  2222. HiddenFileIncluded[I] := False;
  2223. for var I := 0 to IncludedFiles.Count-1 do begin
  2224. var IncludedFile := IncludedFiles[I];
  2225. var HiddenFileIndex := HiddenFiles.IndexOf(IncludedFile.Filename);
  2226. if HiddenFileIndex <> -1 then
  2227. HiddenFileIncluded[HiddenFileIndex] := True;
  2228. end;
  2229. for var I := HiddenFiles.Count-1 downto 0 do
  2230. if not HiddenFileIncluded[I] then
  2231. HiddenFiles.Delete(I);
  2232. end else
  2233. HiddenFiles.Clear;
  2234. end;
  2235. end;
  2236. begin
  2237. Result := iscrSuccess;
  2238. with PAppData(AppData)^ do
  2239. case Code of
  2240. iscbReadScript:
  2241. begin
  2242. if Data.Reset then
  2243. CurLineNumber := 0;
  2244. if CurLineNumber < Lines.Count then begin
  2245. CurLine := Lines[CurLineNumber];
  2246. Data.LineRead := PChar(CurLine);
  2247. Inc(CurLineNumber);
  2248. end;
  2249. end;
  2250. iscbNotifyStatus:
  2251. if Data.Warning then
  2252. Form.StatusMessage(smkWarning, Data.StatusMsg)
  2253. else
  2254. Form.StatusMessage(smkNormal, Data.StatusMsg);
  2255. iscbNotifyIdle:
  2256. begin
  2257. Form.UpdateCompileStatusPanels(Data.CompressProgress,
  2258. Data.CompressProgressMax, Data.SecondsRemaining,
  2259. Data.BytesCompressedPerSecond);
  2260. { We have to use HandleMessage instead of ProcessMessages so that
  2261. Application.Idle is called. Otherwise, Flat TSpeedButton's don't
  2262. react to the mouse being moved over them.
  2263. Unfortunately, HandleMessage by default calls WaitMessage. To avoid
  2264. this we have an Application.OnIdle handler which sets Done to False
  2265. while compiling is in progress - see AppOnIdle.
  2266. The GetQueueStatus check below is just an optimization; calling
  2267. HandleMessage when there are no messages to process wastes CPU. }
  2268. if GetQueueStatus(QS_ALLINPUT) <> 0 then begin
  2269. Form.FBecameIdle := False;
  2270. repeat
  2271. Application.HandleMessage;
  2272. { AppOnIdle sets FBecameIdle to True when it's called, which
  2273. indicates HandleMessage didn't find any message to process }
  2274. until Form.FBecameIdle;
  2275. end;
  2276. if Form.FCompileWantAbort then
  2277. Result := iscrRequestAbort;
  2278. end;
  2279. iscbNotifyPreproc:
  2280. begin
  2281. Form.FPreprocessorOutput := TrimRight(Data.PreprocessedScript);
  2282. DecodeIncludedFilenames(Data.IncludedFilenames, Form.FIncludedFiles); { Also stores last write time }
  2283. CleanHiddenFiles(Form.FIncludedFiles, Form.FHiddenFiles);
  2284. Form.InvalidateStatusPanel(spHiddenFilesCount);
  2285. Form.BuildAndSaveKnownIncludedAndHiddenFiles;
  2286. end;
  2287. iscbNotifySuccess:
  2288. begin
  2289. OutputExe := Data.OutputExeFilename;
  2290. if Form.FCompilerVersion.BinVersion >= $3000001 then begin
  2291. DebugInfo := AllocMem(Data.DebugInfoSize);
  2292. Move(Data.DebugInfo^, DebugInfo^, Data.DebugInfoSize);
  2293. end else
  2294. DebugInfo := nil;
  2295. end;
  2296. iscbNotifyError:
  2297. begin
  2298. if Assigned(Data.ErrorMsg) then
  2299. ErrorMsg := Data.ErrorMsg
  2300. else
  2301. Aborted := True;
  2302. ErrorFilename := Data.ErrorFilename;
  2303. ErrorLine := Data.ErrorLine;
  2304. end;
  2305. end;
  2306. end;
  2307. procedure TMainForm.CompileFile(AFilename: String; const ReadFromFile: Boolean);
  2308. function GetMemoFromErrorFilename(const ErrorFilename: String): TIDEScintFileEdit;
  2309. var
  2310. Memo: TIDEScintFileEdit;
  2311. begin
  2312. if ErrorFilename = '' then
  2313. Result := FMainMemo
  2314. else begin
  2315. if FOptions.OpenIncludedFiles then begin
  2316. for Memo in FFileMemos do begin
  2317. if Memo.Used and (PathCompare(Memo.Filename, ErrorFilename) = 0) then begin
  2318. Result := Memo;
  2319. Exit;
  2320. end;
  2321. end;
  2322. end;
  2323. Result := nil;
  2324. end;
  2325. end;
  2326. var
  2327. SourcePath, S, Options: String;
  2328. Params: TCompileScriptParamsEx;
  2329. AppData: TAppData;
  2330. StartTime, ElapsedTime, ElapsedSeconds: DWORD;
  2331. I: Integer;
  2332. Memo: TIDEScintFileEdit;
  2333. OldActiveMemo: TIDEScintEdit;
  2334. begin
  2335. if FCompiling then begin
  2336. { Shouldn't get here, but just in case... }
  2337. MsgBox('A compile is already in progress.', SCompilerFormCaption, mbError, MB_OK);
  2338. Abort;
  2339. end;
  2340. if not ReadFromFile then begin
  2341. if FOptions.OpenIncludedFiles then begin
  2342. { Included files must always be saved since they're not read from the editor by the compiler }
  2343. for Memo in FFileMemos do begin
  2344. if (Memo <> FMainMemo) and Memo.Used and Memo.Modified then begin
  2345. if FOptions.Autosave then begin
  2346. if not SaveFile(Memo, False) then
  2347. Abort;
  2348. end else begin
  2349. case MsgBox('The text in the ' + Memo.Filename + ' file has changed and must be saved before compiling.'#13#10#13#10 +
  2350. 'Save the changes and continue?', SCompilerFormCaption, mbError,
  2351. MB_YESNO) of
  2352. IDYES:
  2353. if not SaveFile(Memo, False) then
  2354. Abort;
  2355. else
  2356. Abort;
  2357. end;
  2358. end;
  2359. end;
  2360. end;
  2361. end;
  2362. { Save main file if requested }
  2363. if FOptions.Autosave and FMainMemo.Modified then begin
  2364. if not SaveFile(FMainMemo, False) then
  2365. Abort;
  2366. end else if FMainMemo.Filename = '' then begin
  2367. case MsgBox('Would you like to save the script before compiling?' +
  2368. SNewLine2 + 'If you answer No, the compiled installation will be ' +
  2369. 'placed under your My Documents folder by default.',
  2370. SCompilerFormCaption, mbConfirmation, MB_YESNOCANCEL) of
  2371. IDYES:
  2372. if not SaveFile(FMainMemo, False) then
  2373. Abort;
  2374. IDNO: ;
  2375. else
  2376. Abort;
  2377. end;
  2378. end;
  2379. AFilename := FMainMemo.Filename;
  2380. end; {else: Command line compile, AFilename already set. }
  2381. DestroyDebugInfo;
  2382. OldActiveMemo := FActiveMemo;
  2383. AppData.Lines := TStringList.Create;
  2384. try
  2385. FBuildAnimationFrame := 0;
  2386. FProgress := 0;
  2387. FProgressMax := 0;
  2388. FTaskbarProgressValue := 0;
  2389. FActiveMemo.CancelAutoCompleteAndCallTip;
  2390. FActiveMemo.Cursor := crAppStart;
  2391. FActiveMemo.SetCursorID(999); { hack to keep it from overriding Cursor }
  2392. CompilerOutputList.Cursor := crAppStart;
  2393. for Memo in FFileMemos do
  2394. Memo.ReadOnly := True;
  2395. UpdateEditModePanel;
  2396. HideError;
  2397. CompilerOutputList.Clear;
  2398. SendMessage(CompilerOutputList.Handle, LB_SETHORIZONTALEXTENT, 0, 0);
  2399. DebugOutputList.Clear;
  2400. SendMessage(DebugOutputList.Handle, LB_SETHORIZONTALEXTENT, 0, 0);
  2401. DebugCallStackList.Clear;
  2402. SendMessage(DebugCallStackList.Handle, LB_SETHORIZONTALEXTENT, 0, 0);
  2403. OutputTabSet.TabIndex := tiCompilerOutput;
  2404. SetStatusPanelVisible(True);
  2405. SourcePath := GetSourcePath(AFilename);
  2406. FillChar(Params, SizeOf(Params), 0);
  2407. Params.Size := SizeOf(Params);
  2408. Params.CompilerPath := nil;
  2409. Params.SourcePath := PChar(SourcePath);
  2410. Params.CallbackProc := CompilerCallbackProc;
  2411. Pointer(Params.AppData) := @AppData;
  2412. Options := '';
  2413. for I := 0 to FSignTools.Count-1 do
  2414. Options := Options + AddSignToolParam(FSignTools[I]);
  2415. Params.Options := PChar(Options);
  2416. AppData.Form := Self;
  2417. AppData.CurLineNumber := 0;
  2418. AppData.Aborted := False;
  2419. I := ReadScriptLines(AppData.Lines, ReadFromFile, AFilename, FMainMemo);
  2420. if I <> -1 then begin
  2421. if not ReadFromFile then begin
  2422. MoveCaretAndActivateMemo(FMainMemo, I, False);
  2423. SetErrorLine(FMainMemo, I);
  2424. end;
  2425. raise Exception.CreateFmt(SCompilerIllegalNullChar, [I + 1]);
  2426. end;
  2427. StartTime := GetTickCount;
  2428. StatusMessage(smkStartEnd, Format(SCompilerStatusStarting, [TimeToStr(Time)]));
  2429. StatusMessage(smkStartEnd, '');
  2430. FCompiling := True;
  2431. FCompileWantAbort := False;
  2432. UpdateRunMenu;
  2433. UpdateCaption;
  2434. SetLowPriority(FOptions.LowPriorityDuringCompile, FSavePriorityClass);
  2435. AppData.Filename := AFilename;
  2436. {$IFNDEF STATICCOMPILER}
  2437. if ISDllCompileScript(Params) <> isceNoError then begin
  2438. {$ELSE}
  2439. if ISCompileScript(Params, False) <> isceNoError then begin
  2440. {$ENDIF}
  2441. StatusMessage(smkError, SCompilerStatusErrorAborted);
  2442. if not ReadFromFile and (AppData.ErrorLine > 0) then begin
  2443. Memo := GetMemoFromErrorFilename(AppData.ErrorFilename);
  2444. if Memo <> nil then begin
  2445. { Move the caret to the line number the error occurred on }
  2446. MoveCaretAndActivateMemo(Memo, AppData.ErrorLine - 1, False);
  2447. SetErrorLine(Memo, AppData.ErrorLine - 1);
  2448. end;
  2449. end;
  2450. if not AppData.Aborted then begin
  2451. S := '';
  2452. if AppData.ErrorFilename <> '' then
  2453. S := 'File: ' + AppData.ErrorFilename + SNewLine2;
  2454. if AppData.ErrorLine > 0 then
  2455. S := S + Format('Line %d:' + SNewLine, [AppData.ErrorLine]);
  2456. S := S + AppData.ErrorMsg;
  2457. SetAppTaskbarProgressState(tpsError);
  2458. MsgBox(S, 'Compiler Error', mbCriticalError, MB_OK)
  2459. end;
  2460. Abort;
  2461. end;
  2462. ElapsedTime := GetTickCount - StartTime;
  2463. ElapsedSeconds := ElapsedTime div 1000;
  2464. StatusMessage(smkStartEnd, Format(SCompilerStatusFinished, [TimeToStr(Time),
  2465. Format('%.2u%s%.2u%s%.3u', [ElapsedSeconds div 60, FormatSettings.TimeSeparator,
  2466. ElapsedSeconds mod 60, FormatSettings.DecimalSeparator, ElapsedTime mod 1000])]));
  2467. finally
  2468. AppData.Lines.Free;
  2469. FCompiling := False;
  2470. SetLowPriority(False, FSavePriorityClass);
  2471. OldActiveMemo.Cursor := crDefault;
  2472. OldActiveMemo.SetCursorID(SC_CURSORNORMAL);
  2473. CompilerOutputList.Cursor := crDefault;
  2474. for Memo in FFileMemos do
  2475. Memo.ReadOnly := False;
  2476. UpdateEditModePanel;
  2477. UpdateRunMenu;
  2478. UpdateCaption;
  2479. UpdatePreprocMemos;
  2480. if AppData.DebugInfo <> nil then begin
  2481. ParseDebugInfo(AppData.DebugInfo); { Must be called after UpdateIncludedFilesMemos }
  2482. FreeMem(AppData.DebugInfo);
  2483. end;
  2484. InvalidateStatusPanel(spCompileIcon);
  2485. InvalidateStatusPanel(spCompileProgress);
  2486. SetAppTaskbarProgressState(tpsNoProgress);
  2487. StatusBar.Panels[spExtraStatus].Text := '';
  2488. end;
  2489. FCompiledExe := AppData.OutputExe;
  2490. FModifiedAnySinceLastCompile := False;
  2491. FModifiedAnySinceLastCompileAndGo := False;
  2492. end;
  2493. procedure TMainForm.SyncEditorOptions;
  2494. const
  2495. SquigglyStyles: array[Boolean] of Integer = (INDIC_HIDDEN, INDIC_SQUIGGLE);
  2496. WhiteSpaceStyles: array[Boolean] of Integer = (SCWS_INVISIBLE, SCWS_VISIBLEALWAYS);
  2497. var
  2498. Memo: TIDEScintEdit;
  2499. begin
  2500. for Memo in FMemos do begin
  2501. Memo.UseStyleAttributes := FOptions.UseSyntaxHighlighting;
  2502. Memo.Call(SCI_INDICSETSTYLE, minSquiggly, SquigglyStyles[FOptions.UnderlineErrors]);
  2503. Memo.Call(SCI_SETVIEWWS, WhiteSpaceStyles[FOptions.ShowWhiteSpace], 0);
  2504. if FOptions.CursorPastEOL then
  2505. Memo.VirtualSpaceOptions := [svsRectangularSelection, svsUserAccessible, svsNoWrapLineStart]
  2506. else
  2507. Memo.VirtualSpaceOptions := [];
  2508. Memo.FillSelectionToEdge := FOptions.CursorPastEOL;
  2509. Memo.TabWidth := FOptions.TabWidth;
  2510. Memo.UseTabCharacter := FOptions.UseTabCharacter;
  2511. Memo.KeyMappingType := FOptions.MemoKeyMappingType;
  2512. if Memo = FMainMemo then begin
  2513. SetFakeShortCut(ESelectNextOccurrence, FMainMemo.GetComplexCommandShortCut(ccSelectNextOccurrence));
  2514. SetFakeShortCut(ESelectAllOccurrences, FMainMemo.GetComplexCommandShortCut(ccSelectAllOccurrences));
  2515. SetFakeShortCut(ESelectAllFindMatches, FMainMemo.GetComplexCommandShortCut(ccSelectAllFindMatches));
  2516. SetFakeShortCut(EFoldLine, FMainMemo.GetComplexCommandShortCut(ccFoldLine));
  2517. SetFakeShortCut(EUnfoldLine, FMainMemo.GetComplexCommandShortCut(ccUnfoldLine));
  2518. SetFakeShortCut(EToggleLinesComment, FMainMemo.GetComplexCommandShortCut(ccToggleLinesComment));
  2519. SetFakeShortCut(EBraceMatch, FMainMemo.GetComplexCommandShortCut(ccBraceMatch));
  2520. end;
  2521. Memo.UseFolding := FOptions.UseFolding;
  2522. Memo.WordWrap := FOptions.WordWrap;
  2523. if FOptions.IndentationGuides then
  2524. Memo.IndentationGuides := sigLookBoth
  2525. else
  2526. Memo.IndentationGuides := sigNone;
  2527. Memo.LineNumbers := FOptions.GutterLineNumbers;
  2528. end;
  2529. end;
  2530. procedure TMainForm.FMenuClick(Sender: TObject);
  2531. var
  2532. I: Integer;
  2533. begin
  2534. FSaveMainFileAs.Enabled := FActiveMemo = FMainMemo;
  2535. FSaveEncoding.Enabled := FSave.Enabled; { FSave.Enabled is kept up-to-date by UpdateSaveMenuItemAndButton }
  2536. FSaveEncodingAuto.Checked := FSaveEncoding.Enabled and ((FActiveMemo as TIDEScintFileEdit).SaveEncoding = seAuto);
  2537. FSaveEncodingUTF8WithBOM.Checked := FSaveEncoding.Enabled and ((FActiveMemo as TIDEScintFileEdit).SaveEncoding = seUTF8WithBOM);
  2538. FSaveEncodingUTF8WithoutBOM.Checked := FSaveEncoding.Enabled and ((FActiveMemo as TIDEScintFileEdit).SaveEncoding = seUTF8WithoutBOM);
  2539. FSaveAll.Visible := FOptions.OpenIncludedFiles;
  2540. ReadMRUMainFilesList;
  2541. FRecent.Visible := FMRUMainFilesList.Count <> 0;
  2542. for I := 0 to High(FMRUMainFilesMenuItems) do
  2543. with FMRUMainFilesMenuItems[I] do begin
  2544. if I < FMRUMainFilesList.Count then begin
  2545. Visible := True;
  2546. Caption := '&' + IntToStr((I+1) mod 10) + ' ' + DoubleAmp(FMRUMainFilesList[I]);
  2547. end
  2548. else
  2549. Visible := False;
  2550. end;
  2551. ApplyMenuBitmaps(Sender as TMenuItem);
  2552. end;
  2553. procedure TMainForm.FNewMainFileClick(Sender: TObject);
  2554. begin
  2555. if ConfirmCloseFile(True) then
  2556. NewMainFile;
  2557. end;
  2558. procedure TMainForm.FNewMainFileUserWizardClick(Sender: TObject);
  2559. begin
  2560. if ConfirmCloseFile(True) then
  2561. NewMainFileUsingWizard;
  2562. end;
  2563. procedure TMainForm.ShowOpenMainFileDialog(const Examples: Boolean);
  2564. var
  2565. InitialDir, FileName: String;
  2566. begin
  2567. if Examples then begin
  2568. InitialDir := PathExtractPath(NewParamStr(0)) + 'Examples';
  2569. Filename := PathExtractPath(NewParamStr(0)) + 'Examples\Example1.iss';
  2570. end
  2571. else begin
  2572. InitialDir := PathExtractDir(FMainMemo.Filename);
  2573. Filename := '';
  2574. end;
  2575. if ConfirmCloseFile(True) then
  2576. if NewGetOpenFileName('', FileName, InitialDir, SCompilerOpenFilter, 'iss', Handle) then
  2577. OpenFile(FMainMemo, Filename, False);
  2578. end;
  2579. procedure TMainForm.FOpenMainFileClick(Sender: TObject);
  2580. begin
  2581. ShowOpenMainFileDialog(False);
  2582. end;
  2583. procedure TMainForm.FSaveClick(Sender: TObject);
  2584. begin
  2585. SaveFile((FActiveMemo as TIDEScintFileEdit), Sender = FSaveMainFileAs);
  2586. end;
  2587. procedure TMainForm.FSaveEncodingItemClick(Sender: TObject);
  2588. begin
  2589. var Memo := (FActiveMemo as TIDEScintFileEdit);
  2590. var OldSaveEncoding := Memo.SaveEncoding;
  2591. if Sender = FSaveEncodingUTF8WithBOM then
  2592. Memo.SaveEncoding := seUTF8WithBOM
  2593. else if Sender = FSaveEncodingUTF8WithoutBOM then
  2594. Memo.SaveEncoding := seUTF8WithoutBOM
  2595. else
  2596. Memo.SaveEncoding := seAuto;
  2597. if Memo.SaveEncoding <> OldSaveEncoding then
  2598. Memo.ForceModifiedState;
  2599. end;
  2600. procedure TMainForm.FSaveAllClick(Sender: TObject);
  2601. var
  2602. Memo: TIDEScintFileEdit;
  2603. begin
  2604. for Memo in FFileMemos do
  2605. if Memo.Used and Memo.Modified then
  2606. SaveFile(Memo, False);
  2607. end;
  2608. procedure TMainForm.FPrintClick(Sender: TObject);
  2609. procedure SetupNonDarkPrintStyler(var PrintStyler: TInnoSetupStyler; var PrintTheme: TTheme;
  2610. var OldStyler: TScintCustomStyler; var OldTheme: TTheme);
  2611. begin
  2612. { Not the most pretty code, would ideally make a copy of FActiveMemo and print that instead or
  2613. somehow convince Scintilla to use different print styles but don't know of a good way to do
  2614. either. Using SC_PRINT_COLOURONWHITE doesn't help, this gives white on white in dark mode. }
  2615. PrintStyler := TInnoSetupStyler.Create(nil);
  2616. PrintTheme := TTheme.Create;
  2617. PrintStyler.ISPPInstalled := ISPPInstalled;
  2618. PrintStyler.Theme := PrintTheme;
  2619. if not FTheme.Dark then
  2620. PrintTheme.Typ := FTheme.Typ
  2621. else
  2622. PrintTheme.Typ := ttModernLight;
  2623. OldStyler := FActiveMemo.Styler;
  2624. OldTheme := FActiveMemo.Theme;
  2625. FActiveMemo.Styler := PrintStyler;
  2626. FActiveMemo.Theme := PrintTheme;
  2627. FActiveMemo.UpdateThemeColorsAndStyleAttributes;
  2628. end;
  2629. procedure DeinitPrintStyler(const PrintStyler: TInnoSetupStyler; const PrintTheme: TTheme;
  2630. const OldStyler: TScintCustomStyler; const OldTheme: TTheme);
  2631. begin
  2632. if (OldStyler <> nil) or (OldTheme <> nil) then begin
  2633. if OldStyler <> nil then
  2634. FActiveMemo.Styler := OldStyler;
  2635. if OldTheme <> nil then
  2636. FActiveMemo.Theme := OldTheme;
  2637. FActiveMemo.UpdateThemeColorsAndStyleAttributes;
  2638. end;
  2639. if PrintTheme <> FTheme then
  2640. PrintTheme.Free;
  2641. PrintStyler.Free;
  2642. end;
  2643. var
  2644. PrintStyler: TInnoSetupStyler;
  2645. OldStyler: TScintCustomStyler;
  2646. PrintTheme, OldTheme: TTheme;
  2647. PrintMemo: TIDEScintEdit;
  2648. HeaderMemo: TIDEScintFileEdit;
  2649. FileTitle, S: String;
  2650. pdlg: TPrintDlg;
  2651. hdc: Windows.HDC;
  2652. rectMargins, rectPhysMargins, rectSetup, rcw: TRect;
  2653. ptPage, ptDpi: TPoint;
  2654. headerLineHeight, footerLineHeight: Integer;
  2655. fontHeader, fontFooter: HFONT;
  2656. tm: TTextMetric;
  2657. di: TDocInfo;
  2658. lengthDoc, lengthDocMax, lengthPrinted: Integer;
  2659. frPrint: TScintRangeToFormat;
  2660. pageNum: Integer;
  2661. printPage: Boolean;
  2662. ta: UINT;
  2663. sHeader, sFooter: String;
  2664. pen, penOld: HPEN;
  2665. begin
  2666. if FActiveMemo is TIDEScintFileEdit then
  2667. HeaderMemo := TIDEScintFileEdit(FActiveMemo)
  2668. else
  2669. HeaderMemo := FMainMemo;
  2670. sHeader := HeaderMemo.Filename;
  2671. FileTitle := GetFileTitle(HeaderMemo.Filename);
  2672. if HeaderMemo <> FActiveMemo then begin
  2673. S := ' - ' + MemosTabSet.Tabs[MemoToTabIndex(FActiveMemo)];
  2674. sHeader := Format('%s %s', [sHeader, S]);
  2675. FileTitle := Format('%s %s', [FileTitle, S]);
  2676. end;
  2677. sHeader := Format('%s - %s', [sHeader, DateTimeToStr(Now())]);
  2678. { Based on SciTE 5.50's SciTEWin::Print }
  2679. ZeroMemory(@pdlg, SizeOf(pdlg));
  2680. pdlg.lStructSize := SizeOf(pdlg);
  2681. pdlg.hwndOwner := Handle;
  2682. pdlg.hInstance := hInstance;
  2683. pdlg.Flags := PD_USEDEVMODECOPIES or PD_ALLPAGES or PD_RETURNDC;
  2684. pdlg.nFromPage := 1;
  2685. pdlg.nToPage := 1;
  2686. pdlg.nMinPage := 1;
  2687. pdlg.nMaxPage := $ffff; // We do not know how many pages in the document until the printer is selected and the paper size is known.
  2688. pdlg.nCopies := 1;
  2689. pdlg.hDC := 0;
  2690. pdlg.hDevMode := FDevMode;
  2691. pdlg.hDevNames := FDevNames;
  2692. // See if a range has been selected
  2693. var rangeSelection := FActiveMemo.Selection;
  2694. if rangeSelection.StartPos = rangeSelection.EndPos then
  2695. pdlg.Flags := pdlg.Flags or PD_NOSELECTION
  2696. else
  2697. pdlg.Flags := pdlg.Flags or PD_SELECTION;
  2698. if not PrintDlg(pdlg) then
  2699. Exit;
  2700. PrintStyler := nil;
  2701. PrintTheme := nil;
  2702. OldStyler := nil;
  2703. OldTheme := nil;
  2704. try
  2705. if FTheme.Dark then
  2706. SetupNonDarkPrintStyler(PrintStyler, PrintTheme, OldStyler, OldTheme)
  2707. else
  2708. PrintTheme := FTheme;
  2709. FDevMode := pdlg.hDevMode;
  2710. FDevNames := pdlg.hDevNames;
  2711. hdc := pdlg.hDC;
  2712. // Get printer resolution
  2713. ptDpi.x := GetDeviceCaps(hdc, LOGPIXELSX); // dpi in X direction
  2714. ptDpi.y := GetDeviceCaps(hdc, LOGPIXELSY); // dpi in Y direction
  2715. // Start by getting the physical page size (in device units).
  2716. ptPage.x := GetDeviceCaps(hdc, PHYSICALWIDTH); // device units
  2717. ptPage.y := GetDeviceCaps(hdc, PHYSICALHEIGHT); // device units
  2718. // Get the dimensions of the unprintable
  2719. // part of the page (in device units).
  2720. rectPhysMargins.left := GetDeviceCaps(hdc, PHYSICALOFFSETX);
  2721. rectPhysMargins.top := GetDeviceCaps(hdc, PHYSICALOFFSETY);
  2722. // To get the right and lower unprintable area,
  2723. // we take the entire width and height of the paper and
  2724. // subtract everything else.
  2725. rectPhysMargins.right := ptPage.x // total paper width
  2726. - GetDeviceCaps(hdc, HORZRES) // printable width
  2727. - rectPhysMargins.left; // left unprintable margin
  2728. rectPhysMargins.bottom := ptPage.y // total paper height
  2729. - GetDeviceCaps(hdc, VERTRES) // printable height
  2730. - rectPhysMargins.top; // right unprintable margin
  2731. // At this point, rectPhysMargins contains the widths of the
  2732. // unprintable regions on all four sides of the page in device units.
  2733. (*
  2734. // Take in account the page setup given by the user (if one value is not null)
  2735. if (pagesetupMargin.left != 0 || pagesetupMargin.right != 0 ||
  2736. pagesetupMargin.top != 0 || pagesetupMargin.bottom != 0) {
  2737. GUI::Rectangle rectSetup;
  2738. // Convert the hundredths of millimeters (HiMetric) or
  2739. // thousandths of inches (HiEnglish) margin values
  2740. // from the Page Setup dialog to device units.
  2741. // (There are 2540 hundredths of a mm in an inch.)
  2742. TCHAR localeInfo[3];
  2743. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IMEASURE, localeInfo, 3);
  2744. if (localeInfo[0] == '0') { // Metric system. '1' is US System *)
  2745. rectSetup.left := MulDiv(500 {pagesetupMargin.left}, ptDpi.x, 2540);
  2746. rectSetup.top := MulDiv(500 {pagesetupMargin.top}, ptDpi.y, 2540);
  2747. rectSetup.right := MulDiv(500 {pagesetupMargin.right}, ptDpi.x, 2540);
  2748. rectSetup.bottom := MulDiv(500 {pagesetupMargin.bottom}, ptDpi.y, 2540);
  2749. (* } else {
  2750. rectSetup.left = MulDiv(pagesetupMargin.left, ptDpi.x, 1000);
  2751. rectSetup.top = MulDiv(pagesetupMargin.top, ptDpi.y, 1000);
  2752. rectSetup.right = MulDiv(pagesetupMargin.right, ptDpi.x, 1000);
  2753. rectSetup.bottom = MulDiv(pagesetupMargin.bottom, ptDpi.y, 1000);
  2754. } *)
  2755. // Don't reduce margins below the minimum printable area
  2756. rectMargins.left := Max(rectPhysMargins.left, rectSetup.left);
  2757. rectMargins.top := Max(rectPhysMargins.top, rectSetup.top);
  2758. rectMargins.right := Max(rectPhysMargins.right, rectSetup.right);
  2759. rectMargins.bottom := Max(rectPhysMargins.bottom, rectSetup.bottom);
  2760. (*
  2761. } else {
  2762. rectMargins := rectPhysMargins;
  2763. }
  2764. *)
  2765. // rectMargins now contains the values used to shrink the printable
  2766. // area of the page.
  2767. // Convert device coordinates into logical coordinates
  2768. DPtoLP(hdc, rectMargins, 2);
  2769. DPtoLP(hdc, rectPhysMargins, 2);
  2770. // Convert page size to logical units and we're done!
  2771. DPtoLP(hdc, ptPage, 1);
  2772. headerLineHeight := MulDiv(9, ptDpi.y, 72);
  2773. fontHeader := CreateFont(headerLineHeight, 0, 0, 0, FW_REGULAR, 1, 0, 0, 0, 0, 0, 0, 0, PChar(FActiveMemo.Font.Name));
  2774. SelectObject(hdc, fontHeader);
  2775. GetTextMetrics(hdc, &tm);
  2776. headerLineHeight := tm.tmHeight + tm.tmExternalLeading;
  2777. footerLineHeight := MulDiv(9, ptDpi.y, 72);
  2778. fontFooter := CreateFont(footerLineHeight, 0, 0, 0, FW_REGULAR, 0, 0, 0, 0, 0, 0, 0, 0, PChar(FActiveMemo.Font.Name));
  2779. SelectObject(hdc, fontFooter);
  2780. GetTextMetrics(hdc, &tm);
  2781. footerLineHeight := tm.tmHeight + tm.tmExternalLeading;
  2782. ZeroMemory(@di, SizeOf(di));
  2783. di.cbSize := SizeOf(di);
  2784. di.lpszDocName := PChar(FileTitle);
  2785. di.lpszOutput := nil;
  2786. di.lpszDatatype := nil;
  2787. di.fwType := 0;
  2788. if StartDoc(hdc, &di) < 0 then begin
  2789. DeleteDC(hdc);
  2790. DeleteObject(fontHeader);
  2791. DeleteObject(fontFooter);
  2792. MsgBox('Can not start printer document.', SCompilerFormCaption, mbError, MB_OK);
  2793. Exit;
  2794. end;
  2795. lengthDocMax := FActiveMemo.GetRawTextLength;
  2796. // PD_SELECTION -> requested to print selection.
  2797. lengthDoc := IfThen((pdlg.Flags and PD_SELECTION) <> 0, rangeSelection.EndPos, lengthDocMax);
  2798. lengthPrinted := IfThen((pdlg.Flags and PD_SELECTION) <> 0, rangeSelection.StartPos, 0);
  2799. // We must subtract the physical margins from the printable area
  2800. frPrint.hdc := hdc;
  2801. frPrint.hdcTarget := hdc;
  2802. frPrint.rc.left := rectMargins.left - rectPhysMargins.left;
  2803. frPrint.rc.top := rectMargins.top - rectPhysMargins.top;
  2804. frPrint.rc.right := ptPage.x - rectMargins.right - rectPhysMargins.left;
  2805. frPrint.rc.bottom := ptPage.y - rectMargins.bottom - rectPhysMargins.top;
  2806. frPrint.rcPage.left := 0;
  2807. frPrint.rcPage.top := 0;
  2808. frPrint.rcPage.right := ptPage.x - rectPhysMargins.left - rectPhysMargins.right - 1;
  2809. frPrint.rcPage.bottom := ptPage.y - rectPhysMargins.top - rectPhysMargins.bottom - 1;
  2810. frPrint.rc.top := frPrint.rc.top + headerLineHeight + headerLineHeight div 2;
  2811. frPrint.rc.bottom := frPrint.rc.bottom - (footerLineHeight + footerLineHeight div 2);
  2812. // Print each page
  2813. pageNum := 1;
  2814. while lengthPrinted < lengthDoc do begin
  2815. printPage := ((pdlg.Flags and PD_PAGENUMS) = 0) or
  2816. ((pageNum >= pdlg.nFromPage) and (pageNum <= pdlg.nToPage));
  2817. sFooter := Format('- %d -', [pageNum]);
  2818. if printPage then begin
  2819. StartPage(hdc);
  2820. SetTextColor(hdc, PrintTheme.Colors[tcFore]);
  2821. SetBkColor(hdc, PrintTheme.Colors[tcBack]);
  2822. SelectObject(hdc, fontHeader);
  2823. ta := SetTextAlign(hdc, TA_BOTTOM);
  2824. rcw := Rect(frPrint.rc.left, frPrint.rc.top - headerLineHeight - headerLineHeight div 2,
  2825. frPrint.rc.right, frPrint.rc.top - headerLineHeight div 2);
  2826. rcw.bottom := rcw.top + headerLineHeight;
  2827. ExtTextOut(hdc, frPrint.rc.left + 5, frPrint.rc.top - headerLineHeight div 2,
  2828. ETO_OPAQUE, rcw, sHeader, Length(sHeader), nil);
  2829. SetTextAlign(hdc, ta);
  2830. pen := CreatePen(0, 1, GetTextColor(hdc));
  2831. penOld := SelectObject(hdc, pen);
  2832. MoveToEx(hdc, frPrint.rc.left, frPrint.rc.top - headerLineHeight div 4, nil);
  2833. LineTo(hdc, frPrint.rc.right, frPrint.rc.top - headerLineHeight div 4);
  2834. SelectObject(hdc, penOld);
  2835. DeleteObject(pen);
  2836. end;
  2837. frPrint.chrg.StartPos := lengthPrinted;
  2838. frPrint.chrg.EndPos := lengthDoc;
  2839. lengthPrinted := FActiveMemo.FormatRange(printPage, @frPrint);
  2840. if printPage then begin
  2841. SetTextColor(hdc, PrintTheme.Colors[tcFore]);
  2842. SetBkColor(hdc, PrintTheme.Colors[tcBack]);
  2843. SelectObject(hdc, fontFooter);
  2844. ta := SetTextAlign(hdc, TA_TOP);
  2845. rcw := Rect(frPrint.rc.left, frPrint.rc.bottom + footerLineHeight div 2,
  2846. frPrint.rc.right, frPrint.rc.bottom + footerLineHeight + footerLineHeight div 2);
  2847. ExtTextOut(hdc, frPrint.rc.left + 5, frPrint.rc.bottom + footerLineHeight div 2,
  2848. ETO_OPAQUE, rcw, sFooter, Length(sFooter), nil);
  2849. SetTextAlign(hdc, ta);
  2850. pen := CreatePen(0, 1, GetTextColor(hdc));
  2851. penOld := SelectObject(hdc, pen);
  2852. MoveToEx(hdc, frPrint.rc.left, frPrint.rc.bottom + footerLineHeight div 4, nil);
  2853. LineTo(hdc, frPrint.rc.right, frPrint.rc.bottom + footerLineHeight div 4);
  2854. SelectObject(hdc, penOld);
  2855. DeleteObject(pen);
  2856. EndPage(hdc);
  2857. end;
  2858. Inc(pageNum);
  2859. if ((pdlg.Flags and PD_PAGENUMS) <> 0) and (pageNum > pdlg.nToPage) then
  2860. Break;
  2861. end;
  2862. FActiveMemo.FormatRange(False, nil);
  2863. EndDoc(hdc);
  2864. DeleteDC(hdc);
  2865. DeleteObject(fontHeader);
  2866. DeleteObject(fontFooter);
  2867. finally
  2868. DeinitPrintStyler(PrintStyler, PrintTheme, OldStyler, OldTheme);
  2869. end;
  2870. end;
  2871. procedure TMainForm.FClearRecentClick(Sender: TObject);
  2872. begin
  2873. if MsgBox('Are you sure you want to clear the list of recently opened files?',
  2874. SCompilerFormCaption, mbConfirmation, MB_YESNO or MB_DEFBUTTON2) <> IDNO then
  2875. ClearMRUMainFilesList;
  2876. end;
  2877. procedure TMainForm.FMRUClick(Sender: TObject);
  2878. var
  2879. I: Integer;
  2880. begin
  2881. if ConfirmCloseFile(True) then
  2882. for I := 0 to High(FMRUMainFilesMenuItems) do
  2883. if FMRUMainFilesMenuItems[I] = Sender then begin
  2884. OpenMRUMainFile(FMRUMainFilesList[I]);
  2885. Break;
  2886. end;
  2887. end;
  2888. procedure TMainForm.FExitClick(Sender: TObject);
  2889. begin
  2890. Close;
  2891. end;
  2892. procedure TMainForm.EMenuClick(Sender: TObject);
  2893. var
  2894. MemoHasFocus, MemoIsReadOnly: Boolean;
  2895. begin
  2896. MemoHasFocus := FActiveMemo.Focused;
  2897. MemoIsReadOnly := FActiveMemo.ReadOnly;
  2898. EUndo.Enabled := MemoHasFocus and FActiveMemo.CanUndo;
  2899. ERedo.Enabled := MemoHasFocus and FActiveMemo.CanRedo;
  2900. ECut.Enabled := MemoHasFocus and not MemoIsReadOnly and not FActiveMemo.SelEmpty;
  2901. ECopy.Enabled := MemoHasFocus and not FActiveMemo.SelEmpty;
  2902. EPaste.Enabled := MemoHasFocus and FActiveMemo.CanPaste;
  2903. EDelete.Enabled := MemoHasFocus and not FActiveMemo.SelEmpty;
  2904. ESelectAll.Enabled := MemoHasFocus;
  2905. ESelectNextOccurrence.Enabled := MemoHasFocus;
  2906. ESelectAllOccurrences.Enabled := MemoHasFocus;
  2907. ESelectAllFindMatches.Enabled := MemoHasFocus and (FLastFindText <> '');
  2908. EFind.Enabled := MemoHasFocus;
  2909. EFindNext.Enabled := MemoHasFocus;
  2910. EFindPrevious.Enabled := MemoHasFocus;
  2911. EReplace.Enabled := MemoHasFocus and not MemoIsReadOnly;
  2912. EFindRegEx.Checked := FOptions.FindRegEx;
  2913. EFoldLine.Visible := FOptions.UseFolding;
  2914. EFoldLine.Enabled := MemoHasFocus;
  2915. EUnfoldLine.Visible := EFoldLine.Visible;
  2916. EUnfoldLine.Enabled := EFoldLine.Enabled;
  2917. EGoto.Enabled := MemoHasFocus;
  2918. EToggleLinesComment.Enabled := not MemoIsReadOnly;
  2919. EBraceMatch.Enabled := MemoHasFocus;
  2920. ApplyMenuBitmaps(Sender as TMenuItem);
  2921. end;
  2922. procedure TMainForm.EUndoClick(Sender: TObject);
  2923. begin
  2924. FActiveMemo.Undo;
  2925. end;
  2926. procedure TMainForm.ERedoClick(Sender: TObject);
  2927. begin
  2928. FActiveMemo.Redo;
  2929. end;
  2930. procedure TMainForm.ECutClick(Sender: TObject);
  2931. begin
  2932. FActiveMemo.CutToClipboard;
  2933. end;
  2934. procedure TMainForm.ECopyClick(Sender: TObject);
  2935. begin
  2936. FActiveMemo.CopyToClipboard;
  2937. end;
  2938. function TMainForm.MultipleSelectionPasteFromClipboard(const AMemo: TIDEScintEdit): Boolean;
  2939. begin
  2940. { Scintilla doesn't yet properly support multiple selection paste. Handle it
  2941. here, just like VS and VSCode do: if there's multiple selections and the paste
  2942. text has the same amount of lines then paste 1 line per selection. Do this even
  2943. if the paste text is marked as rectangular. Otherwise (so no match between
  2944. the selection count and the line count) paste all lines into each selection.
  2945. For the latter we don't need handling here: this is Scintilla's default
  2946. behaviour if SC_MULTIPASTE_EACH is on. }
  2947. Result := False;
  2948. var SelectionCount := AMemo.SelectionCount;
  2949. if SelectionCount > 1 then begin
  2950. var PasteLines := Clipboard.AsText.Replace(#13#10, #13).Split([#13, #10]);
  2951. if SelectionCount = Length(PasteLines) then begin
  2952. AMemo.BeginUndoAction;
  2953. try
  2954. for var I := 0 to SelectionCount-1 do begin
  2955. var StartPos := AMemo.SelectionStartPosition[I]; { Can't use AMemo.GetSelections because each paste can update other selections }
  2956. var EndPos := AMemo.SelectionEndPosition[I];
  2957. AMemo.ReplaceTextRange(StartPos, EndPos, PasteLines[I], srmMinimal);
  2958. { Update the selection to an empty selection at the end of the inserted
  2959. text, just like ReplaceMainSelText }
  2960. var Pos := AMemo.Target.EndPos; { ReplaceTextRange updates the target }
  2961. AMemo.SelectionCaretPosition[I] := Pos;
  2962. AMemo.SelectionAnchorPosition[I] := Pos;
  2963. end;
  2964. { Be like SCI_PASTE }
  2965. AMemo.ChooseCaretX;
  2966. AMemo.ScrollCaretIntoView;
  2967. finally
  2968. AMemo.EndUndoAction;
  2969. end;
  2970. Result := True;
  2971. end;
  2972. end;
  2973. end;
  2974. procedure TMainForm.EPasteClick(Sender: TObject);
  2975. begin
  2976. if not MultipleSelectionPasteFromClipboard(FActiveMemo) then
  2977. FActiveMemo.PasteFromClipboard;
  2978. end;
  2979. procedure TMainForm.EDeleteClick(Sender: TObject);
  2980. begin
  2981. FActiveMemo.ClearSelection;
  2982. end;
  2983. procedure TMainForm.ESelectAllClick(Sender: TObject);
  2984. begin
  2985. FActiveMemo.SelectAll;
  2986. end;
  2987. procedure TMainForm.ESelectAllOccurrencesClick(Sender: TObject);
  2988. begin
  2989. { Might be called even if ESelectAllOccurrences.Enabled would be False in EMenuClick }
  2990. if FActiveMemo.SelEmpty then begin
  2991. { If the selection is empty then SelectAllOccurrences will actually just select
  2992. the word at caret which is not what we want, so preselect this word ourselves }
  2993. var Range := FActiveMemo.WordAtCaretRange;
  2994. if Range.StartPos <> Range.EndPos then
  2995. FActiveMemo.SetSingleSelection(Range.EndPos, Range.StartPos);
  2996. end;
  2997. FActiveMemo.SelectAllOccurrences([sfoMatchCase]);
  2998. end;
  2999. procedure TMainForm.ESelectNextOccurrenceClick(Sender: TObject);
  3000. begin
  3001. { Might be called even if ESelectNextOccurrence.Enabled would be False in EMenuClick }
  3002. FActiveMemo.SelectNextOccurrence([sfoMatchCase]);
  3003. end;
  3004. procedure TMainForm.EToggleLinesCommentClick(Sender: TObject);
  3005. begin
  3006. var AMemo := FActiveMemo;
  3007. { Based on SciTE 5.50's SciTEBase::StartBlockComment - only toggles comments
  3008. for the main selection }
  3009. var Selection := AMemo.Selection;
  3010. var CaretPosition := AMemo.CaretPosition;
  3011. // checking if caret is located in _beginning_ of selected block
  3012. var MoveCaret := CaretPosition < Selection.EndPos;
  3013. var SelStartLine := AMemo.GetLineFromPosition(Selection.StartPos);
  3014. var SelEndLine := AMemo.GetLineFromPosition(Selection.EndPos);
  3015. var Lines := SelEndLine - SelStartLine;
  3016. var FirstSelLineStart := AMemo.GetPositionFromLine(SelStartLine);
  3017. // "caret return" is part of the last selected line
  3018. if (Lines > 0) and (Selection.EndPos = AMemo.GetPositionFromLine(SelEndLine)) then
  3019. Dec(SelEndLine);
  3020. { We rely on the styler to identify [Code] section lines, but we
  3021. may be searching into areas that haven't been styled yet }
  3022. AMemo.StyleNeeded(Selection.EndPos);
  3023. AMemo.BeginUndoAction;
  3024. try
  3025. var LastLongCommentLength := 0;
  3026. for var I := SelStartLine to SelEndLine do begin
  3027. var LineIndent := AMemo.GetLineIndentPosition(I);
  3028. var LineEnd := AMemo.GetLineEndPosition(I);
  3029. var LineBuf := AMemo.GetTextRange(LineIndent, LineEnd);
  3030. // empty lines are not commented
  3031. if LineBuf = '' then
  3032. Continue;
  3033. var Comment: String;
  3034. if LineBuf.StartsWith('//') or
  3035. (FMemosStyler.GetSectionFromLineState(AMemo.Lines.State[I]) = scCode) then
  3036. Comment := '//'
  3037. else
  3038. Comment := ';';
  3039. var LongComment := Comment + ' ';
  3040. LastLongCommentLength := Length(LongComment);
  3041. if LineBuf.StartsWith(Comment) then begin
  3042. var CommentLength := Length(Comment);
  3043. if LineBuf.StartsWith(LongComment) then begin
  3044. // Removing comment with space after it.
  3045. CommentLength := Length(LongComment);
  3046. end;
  3047. AMemo.Selection := TScintRange.Create(LineIndent, LineIndent + CommentLength);
  3048. AMemo.SelText := '';
  3049. if I = SelStartLine then // is this the first selected line?
  3050. Dec(Selection.StartPos, CommentLength);
  3051. Dec(Selection.EndPos, CommentLength); // every iteration
  3052. Continue;
  3053. end;
  3054. if I = SelStartLine then // is this the first selected line?
  3055. Inc(Selection.StartPos, Length(LongComment));
  3056. Inc(Selection.EndPos, Length(LongComment)); // every iteration
  3057. AMemo.Call(SCI_INSERTTEXT, LineIndent, AMemo.ConvertStringToRawString(LongComment));
  3058. end;
  3059. // after uncommenting selection may promote itself to the lines
  3060. // before the first initially selected line;
  3061. // another problem - if only comment symbol was selected;
  3062. if Selection.StartPos < FirstSelLineStart then begin
  3063. if Selection.StartPos >= Selection.EndPos - (LastLongCommentLength - 1) then
  3064. Selection.EndPos := FirstSelLineStart;
  3065. Selection.StartPos := FirstSelLineStart;
  3066. end;
  3067. if MoveCaret then begin
  3068. // moving caret to the beginning of selected block
  3069. AMemo.CaretPosition := Selection.EndPos;
  3070. AMemo.CaretPositionWithSelectFromAnchor := Selection.StartPos;
  3071. end else
  3072. AMemo.Selection := Selection;
  3073. finally
  3074. AMemo.EndUndoAction;
  3075. end;
  3076. end;
  3077. procedure TMainForm.EBraceMatchClick(Sender: TObject);
  3078. begin
  3079. var AMemo := FActiveMemo;
  3080. var Selections: TScintCaretAndAnchorList := nil;
  3081. var VirtualSpaces: TScintCaretAndAnchorList := nil;
  3082. try
  3083. Selections := TScintCaretAndAnchorList.Create;
  3084. VirtualSpaces := TScintCaretAndAnchorList.Create;
  3085. AMemo.GetSelections(Selections, VirtualSpaces);
  3086. for var I := 0 to Selections.Count-1 do begin
  3087. if VirtualSpaces[I].CaretPos = 0 then begin
  3088. var Pos := Selections[I].CaretPos;
  3089. var MatchPos := AMemo.GetPositionOfMatchingBrace(Pos);
  3090. if MatchPos = -1 then begin
  3091. Pos := AMemo.GetPositionBefore(Pos);
  3092. MatchPos := AMemo.GetPositionOfMatchingBrace(Pos)
  3093. end;
  3094. if MatchPos <> -1 then begin
  3095. AMemo.SelectionCaretPosition[I] := MatchPos;
  3096. AMemo.SelectionAnchorPosition[I] := MatchPos;
  3097. if I = 0 then
  3098. AMemo.ScrollCaretIntoView;
  3099. end;
  3100. end;
  3101. end;
  3102. finally
  3103. VirtualSpaces.Free;
  3104. Selections.Free;
  3105. end;
  3106. end;
  3107. procedure TMainForm.ESelectAllFindMatchesClick(Sender: TObject);
  3108. begin
  3109. { Might be called even if ESelectAllFindMatches.Enabled would be False in EMenuClick }
  3110. if FLastFindText <> '' then begin
  3111. var StartPos := 0;
  3112. var EndPos := FActiveMemo.RawTextLength;
  3113. var FoundRange: TScintRange;
  3114. var ClosestSelection := -1;
  3115. var ClosestSelectionDistance := 0; { Silence compiler }
  3116. var CaretPos := FActiveMemo.CaretPosition;
  3117. while (StartPos < EndPos) and
  3118. FActiveMemo.FindText(StartPos, EndPos, FLastFindText,
  3119. FindOptionsToSearchOptions(FLastFindOptions, FLastFindRegEx), FoundRange) do begin
  3120. if StartPos = 0 then
  3121. FActiveMemo.SetSingleSelection(FoundRange.EndPos, FoundRange.StartPos)
  3122. else
  3123. FActiveMemo.AddSelection(FoundRange.EndPos, FoundRange.StartPos);
  3124. var Distance := Abs(CaretPos-FoundRange.EndPos);
  3125. if (ClosestSelection = -1) or (Distance < ClosestSelectionDistance) then begin
  3126. ClosestSelection := FActiveMemo.SelectionCount-1;
  3127. ClosestSelectionDistance := Distance;
  3128. end;
  3129. StartPos := FoundRange.EndPos;
  3130. end;
  3131. if ClosestSelection <> -1 then begin
  3132. FActiveMemo.MainSelection := ClosestSelection;
  3133. FActiveMemo.ScrollCaretIntoView;
  3134. end;
  3135. end;
  3136. end;
  3137. procedure TMainForm.VMenuClick(Sender: TObject);
  3138. begin
  3139. VZoomIn.Enabled := (FActiveMemo.Zoom < 20);
  3140. VZoomOut.Enabled := (FActiveMemo.Zoom > -10);
  3141. VZoomReset.Enabled := (FActiveMemo.Zoom <> 0);
  3142. VToolbar.Checked := ToolbarPanel.Visible;
  3143. VStatusBar.Checked := StatusBar.Visible;
  3144. VNextTab.Enabled := MemosTabSet.Visible and (MemosTabSet.Tabs.Count > 1);
  3145. VPreviousTab.Enabled := VNextTab.Enabled;
  3146. VCloseCurrentTab.Enabled := MemosTabSet.Visible and (FActiveMemo <> FMainMemo) and (FActiveMemo <> FPreprocessorOutputMemo);
  3147. VReopenTab.Visible := MemosTabSet.Visible and (FHiddenFiles.Count > 0);
  3148. if VReopenTab.Visible then
  3149. UpdateReopenTabMenu(VReopenTab);
  3150. VReopenTabs.Visible := VReopenTab.Visible;
  3151. VHide.Checked := not StatusPanel.Visible;
  3152. VCompilerOutput.Checked := StatusPanel.Visible and (OutputTabSet.TabIndex = tiCompilerOutput);
  3153. VDebugOutput.Checked := StatusPanel.Visible and (OutputTabSet.TabIndex = tiDebugOutput);
  3154. VDebugCallStack.Checked := StatusPanel.Visible and (OutputTabSet.TabIndex = tiDebugCallStack);
  3155. VFindResults.Checked := StatusPanel.Visible and (OutputTabSet.TabIndex = tiFindResults);
  3156. VWordWrap.Checked := FOptions.WordWrap;
  3157. ApplyMenuBitmaps(Sender as TMenuItem);
  3158. end;
  3159. procedure TMainForm.VNextTabClick(Sender: TObject);
  3160. var
  3161. NewTabIndex: Integer;
  3162. begin
  3163. NewTabIndex := MemosTabSet.TabIndex+1;
  3164. if NewTabIndex >= MemosTabSet.Tabs.Count then
  3165. NewTabIndex := 0;
  3166. MemosTabSet.TabIndex := NewTabIndex;
  3167. end;
  3168. procedure TMainForm.VPreviousTabClick(Sender: TObject);
  3169. var
  3170. NewTabIndex: Integer;
  3171. begin
  3172. NewTabIndex := MemosTabSet.TabIndex-1;
  3173. if NewTabIndex < 0 then
  3174. NewTabIndex := MemosTabSet.Tabs.Count-1;
  3175. MemosTabSet.TabIndex := NewTabIndex;
  3176. end;
  3177. procedure TMainForm.CloseTab(const TabIndex: Integer);
  3178. begin
  3179. var Memo := TabIndexToMemo(TabIndex, MemosTabSet.Tabs.Count-1);
  3180. var MemoWasActiveMemo := Memo = FActiveMemo;
  3181. MemosTabSet.Tabs.Delete(TabIndex); { This will not change MemosTabset.TabIndex }
  3182. MemosTabSet.Hints.Delete(TabIndex);
  3183. MemosTabSet.CloseButtons.Delete(TabIndex);
  3184. FHiddenFiles.Add((Memo as TIDEScintFileEdit).Filename);
  3185. InvalidateStatusPanel(spHiddenFilesCount);
  3186. BuildAndSaveKnownIncludedAndHiddenFiles;
  3187. { Because MemosTabSet.Tabs and FHiddenFiles have both been updated now,
  3188. hereafter setting TabIndex will not select the memo we're closing
  3189. even if it's not hidden yet because TabIndexToMemo as called by
  3190. MemosTabSetClick will skip it }
  3191. if MemoWasActiveMemo then begin
  3192. { Select next tab, except when we're already at the end. Avoiding flicker by
  3193. doing this before hiding old active memo. We do this in a dirty way by
  3194. clicking two tabs while making sure TabSetClick doesn't see the first
  3195. 'fake' one. }
  3196. FIgnoreTabSetClick := True;
  3197. try
  3198. VNextTabClick(Self);
  3199. finally
  3200. FIgnoreTabSetClick := False;
  3201. end;
  3202. VPreviousTabClick(Self);
  3203. Memo.CancelAutoCompleteAndCallTip;
  3204. Memo.Visible := False;
  3205. end else if TabIndex < MemosTabset.TabIndex then
  3206. MemosTabSet.TabIndex := MemosTabset.TabIndex-1; { Reselect old selected tab }
  3207. end;
  3208. procedure TMainForm.VCloseCurrentTabClick(Sender: TObject);
  3209. begin
  3210. CloseTab(MemosTabSet.TabIndex);
  3211. end;
  3212. procedure TMainForm.ReopenTabOrTabs(const HiddenFileIndex: Integer;
  3213. const Activate: Boolean);
  3214. begin
  3215. var ReopenFilename: String;
  3216. if HiddenFileIndex >= 0 then begin
  3217. ReopenFilename := FHiddenFiles[HiddenFileIndex];
  3218. FHiddenFiles.Delete(HiddenFileIndex);
  3219. end else begin
  3220. ReopenFilename := FHiddenFiles[0];
  3221. FHiddenFiles.Clear;
  3222. end;
  3223. InvalidateStatusPanel(spHiddenFilesCount);
  3224. UpdatePreprocMemos;
  3225. BuildAndSaveKnownIncludedAndHiddenFiles;
  3226. { Activate the memo if requested }
  3227. if Activate then begin
  3228. for var Memo in FFileMemos do begin
  3229. if Memo.Used and (PathCompare(Memo.Filename, ReopenFilename) = 0) then begin
  3230. MemosTabSet.TabIndex := MemoToTabIndex(memo);
  3231. Break;
  3232. end;
  3233. end
  3234. end;
  3235. end;
  3236. procedure TMainForm.ReopenTabClick(Sender: TObject);
  3237. begin
  3238. ReopenTabOrTabs((Sender as TMenuItem).Tag, True);
  3239. end;
  3240. procedure TMainForm.VReopenTabsClick(Sender: TObject);
  3241. begin
  3242. ReopenTabOrTabs(-1, True);
  3243. end;
  3244. procedure TMainForm.VZoomInClick(Sender: TObject);
  3245. begin
  3246. FActiveMemo.ZoomIn; { MemoZoom will zoom the other memos }
  3247. end;
  3248. procedure TMainForm.VZoomOutClick(Sender: TObject);
  3249. begin
  3250. FActiveMemo.ZoomOut;
  3251. end;
  3252. procedure TMainForm.VZoomResetClick(Sender: TObject);
  3253. begin
  3254. FActiveMemo.Zoom := 0;
  3255. end;
  3256. procedure TMainForm.VToolbarClick(Sender: TObject);
  3257. begin
  3258. ToolbarPanel.Visible := not ToolbarPanel.Visible;
  3259. end;
  3260. procedure TMainForm.VStatusBarClick(Sender: TObject);
  3261. begin
  3262. StatusBar.Visible := not StatusBar.Visible;
  3263. end;
  3264. procedure TMainForm.VWordWrapClick(Sender: TObject);
  3265. begin
  3266. FOptions.WordWrap := not FOptions.WordWrap;
  3267. SyncEditorOptions;
  3268. var Ini := TConfigIniFile.Create;
  3269. try
  3270. Ini.WriteBool('Options', 'WordWrap', FOptions.WordWrap);
  3271. finally
  3272. Ini.Free;
  3273. end;
  3274. end;
  3275. procedure TMainForm.SetStatusPanelVisible(const AVisible: Boolean);
  3276. var
  3277. CaretWasInView: Boolean;
  3278. begin
  3279. if StatusPanel.Visible <> AVisible then begin
  3280. CaretWasInView := FActiveMemo.IsPositionInViewVertically(FActiveMemo.CaretPosition);
  3281. if AVisible then begin
  3282. { Ensure the status panel height isn't out of range before showing }
  3283. UpdateStatusPanelHeight(StatusPanel.Height);
  3284. SplitPanel.Top := ClientHeight;
  3285. StatusPanel.Top := ClientHeight;
  3286. end
  3287. else begin
  3288. if StatusPanel.ContainsControl(ActiveControl) then
  3289. ActiveControl := FActiveMemo;
  3290. end;
  3291. SplitPanel.Visible := AVisible;
  3292. StatusPanel.Visible := AVisible;
  3293. if AVisible and CaretWasInView then begin
  3294. { If the caret was in view, make sure it still is }
  3295. FActiveMemo.ScrollCaretIntoView;
  3296. end;
  3297. end;
  3298. end;
  3299. procedure TMainForm.VHideClick(Sender: TObject);
  3300. begin
  3301. SetStatusPanelVisible(False);
  3302. end;
  3303. procedure TMainForm.VCompilerOutputClick(Sender: TObject);
  3304. begin
  3305. OutputTabSet.TabIndex := tiCompilerOutput;
  3306. SetStatusPanelVisible(True);
  3307. end;
  3308. procedure TMainForm.VDebugOutputClick(Sender: TObject);
  3309. begin
  3310. OutputTabSet.TabIndex := tiDebugOutput;
  3311. SetStatusPanelVisible(True);
  3312. end;
  3313. procedure TMainForm.VDebugCallStackClick(Sender: TObject);
  3314. begin
  3315. OutputTabSet.TabIndex := tiDebugCallStack;
  3316. SetStatusPanelVisible(True);
  3317. end;
  3318. procedure TMainForm.VFindResultsClick(Sender: TObject);
  3319. begin
  3320. OutputTabSet.TabIndex := tiFindResults;
  3321. SetStatusPanelVisible(True);
  3322. end;
  3323. procedure TMainForm.BMenuClick(Sender: TObject);
  3324. begin
  3325. BLowPriority.Checked := FOptions.LowPriorityDuringCompile;
  3326. BOpenOutputFolder.Enabled := (FCompiledExe <> '');
  3327. ApplyMenuBitmaps(Sender as TMenuItem);
  3328. end;
  3329. procedure TMainForm.BCompileClick(Sender: TObject);
  3330. begin
  3331. CompileFile('', False);
  3332. end;
  3333. procedure TMainForm.BStopCompileClick(Sender: TObject);
  3334. begin
  3335. SetAppTaskbarProgressState(tpsPaused);
  3336. try
  3337. if MsgBox('Are you sure you want to abort the compile?', SCompilerFormCaption,
  3338. mbConfirmation, MB_YESNO or MB_DEFBUTTON2) <> IDNO then
  3339. FCompileWantAbort := True;
  3340. finally
  3341. SetAppTaskbarProgressState(tpsNormal);
  3342. end;
  3343. end;
  3344. procedure TMainForm.BLowPriorityClick(Sender: TObject);
  3345. begin
  3346. FOptions.LowPriorityDuringCompile := not FOptions.LowPriorityDuringCompile;
  3347. { If a compile is already in progress, change the priority now }
  3348. if FCompiling then
  3349. SetLowPriority(FOptions.LowPriorityDuringCompile, FSavePriorityClass);
  3350. end;
  3351. procedure TMainForm.BOpenOutputFolderClick(Sender: TObject);
  3352. begin
  3353. LaunchFileOrURL(AddBackslash(GetSystemWinDir) + 'explorer.exe',
  3354. Format('/select,"%s"', [FCompiledExe]));
  3355. end;
  3356. procedure TMainForm.HMenuClick(Sender: TObject);
  3357. begin
  3358. HUnregister.Visible := IsLicensed;
  3359. HDonate.Visible := not HUnregister.Visible;
  3360. ApplyMenuBitmaps(Sender as TMenuItem);
  3361. end;
  3362. procedure TMainForm.HPurchaseClick(Sender: TObject);
  3363. begin
  3364. if IsLicensed then
  3365. if MsgBox('Do you want to copy your current license key to the clipboard before opening our order page? You will need it to be able to renew it.',
  3366. SCompilerFormCaption, mbConfirmation, MB_YESNO) = IDYES then
  3367. ClipBoard.AsText := GetChunkedLicenseKey;
  3368. LaunchFileOrURL('https://jrsoftware.org/isorder.php');
  3369. end;
  3370. procedure TMainForm.HRegisterClick(Sender: TObject);
  3371. begin
  3372. const LicenseKeyForm = TLicenseKeyForm.Create(Application);
  3373. try
  3374. if LicenseKeyForm.ShowModal = mrOk then begin
  3375. WriteLicense;
  3376. UpdateCaption;
  3377. MsgBox('New commercial license key has been registered:' + SNewLine2 +
  3378. GetLicenseDescription('', SNewLine2) + SNewLine2 +
  3379. 'Thanks for your support!', SCompilerFormCaption, mbInformation, MB_OK);
  3380. end;
  3381. finally
  3382. LicenseKeyForm.Free;
  3383. end;
  3384. end;
  3385. procedure TMainForm.HUnregisterClick(Sender: TObject);
  3386. begin
  3387. if MsgBox('Are you sure you want to remove your commercial license key and revert to non-commercial use only?',
  3388. SCompilerFormCaption, mbConfirmation, MB_YESNO or MB_DEFBUTTON2) <> IDNO then begin
  3389. RemoveLicense;
  3390. UpdateCaption;
  3391. const Ini = TConfigIniFile.Create;
  3392. try
  3393. const AskAgainDateAsInt = FormatDateTime('yyyymmdd', IncDay(IncMonth(Date, 6), -1)).ToInteger;
  3394. Ini.WriteInteger('UpdatePanel', 'Purchase', AskAgainDateAsInt);
  3395. finally
  3396. Ini.Free;
  3397. end;
  3398. MsgBox('Commercial license key has been removed.', SCompilerFormCaption, mbInformation, MB_OK);
  3399. end;
  3400. end;
  3401. procedure TMainForm.HDonateClick(Sender: TObject);
  3402. begin
  3403. OpenDonateSite;
  3404. end;
  3405. procedure TMainForm.HShortcutsDocClick(Sender: TObject);
  3406. begin
  3407. if Assigned(HtmlHelp) then
  3408. HtmlHelp(GetDesktopWindow, PChar(GetHelpFile), HH_DISPLAY_TOPIC, Cardinal(PChar('topic_compformshortcuts.htm')));
  3409. end;
  3410. procedure TMainForm.HRegExDocClick(Sender: TObject);
  3411. begin
  3412. if Assigned(HtmlHelp) then
  3413. HtmlHelp(GetDesktopWindow, PChar(GetHelpFile), HH_DISPLAY_TOPIC, Cardinal(PChar('topic_compformregex.htm')));
  3414. end;
  3415. procedure TMainForm.HDocClick(Sender: TObject);
  3416. begin
  3417. if Assigned(HtmlHelp) then
  3418. HtmlHelp(GetDesktopWindow, PChar(GetHelpFile), HH_DISPLAY_TOPIC, 0);
  3419. end;
  3420. procedure TMainForm.HExamplesClick(Sender: TObject);
  3421. begin
  3422. LaunchFileOrURL(PathExtractPath(NewParamStr(0)) + 'Examples');
  3423. end;
  3424. procedure TMainForm.HFaqClick(Sender: TObject);
  3425. begin
  3426. LaunchFileOrURL(PathExtractPath(NewParamStr(0)) + 'isfaq.url');
  3427. end;
  3428. procedure TMainForm.HWhatsNewClick(Sender: TObject);
  3429. begin
  3430. LaunchFileOrURL(PathExtractPath(NewParamStr(0)) + {$IFDEF DEBUG} '..\..\' + {$ENDIF} 'whatsnew.htm');
  3431. end;
  3432. procedure TMainForm.HWebsiteClick(Sender: TObject);
  3433. begin
  3434. LaunchFileOrURL('https://jrsoftware.org/isinfo.php');
  3435. end;
  3436. procedure TMainForm.HMailingListClick(Sender: TObject);
  3437. begin
  3438. OpenMailingListSite;
  3439. end;
  3440. procedure TMainForm.HISPPDocClick(Sender: TObject);
  3441. begin
  3442. if Assigned(HtmlHelp) then
  3443. HtmlHelp(GetDesktopWindow, PChar(GetHelpFile), HH_DISPLAY_TOPIC, Cardinal(PChar('topic_isppoverview.htm')));
  3444. end;
  3445. procedure TMainForm.HAboutClick(Sender: TObject);
  3446. var
  3447. S: String;
  3448. begin
  3449. { Removing the About box or modifying any existing text inside it is a
  3450. violation of the Inno Setup license agreement; see LICENSE.TXT.
  3451. However, adding additional lines to the About box is permitted, as long as
  3452. they are placed below the original copyright notice. }
  3453. S := FCompilerVersion.Title + ' Compiler version ' +
  3454. String(FCompilerVersion.Version) + SNewLine;
  3455. if FCompilerVersion.Title <> 'Inno Setup' then
  3456. S := S + (SNewLine + 'Based on Inno Setup' + SNewLine);
  3457. S := S + ('Copyright (C) 1997-2025 Jordan Russell' + SNewLine +
  3458. 'Portions Copyright (C) 2000-2025 Martijn Laan' + SNewLine +
  3459. 'All rights reserved.' + SNewLine2 +
  3460. 'Inno Setup home page:' + SNewLine +
  3461. 'https://www.innosetup.com/' + SNewLine2 +
  3462. 'RemObjects Pascal Script home page:' + SNewLine +
  3463. 'https://www.remobjects.com/ps' + SNewLine2 +
  3464. 'Refer to LICENSE.TXT for conditions of distribution and use.');
  3465. S := S + SNewLine2 + GetLicenseDescription('Registered commercial license:' + SNewLine, SNewLine);
  3466. MsgBox(S, 'About ' + FCompilerVersion.Title, mbInformation, MB_OK);
  3467. end;
  3468. procedure TMainForm.WMStartCommandLineCompile(var Message: TMessage);
  3469. var
  3470. Code: Integer;
  3471. begin
  3472. UpdateStatusPanelHeight(ClientHeight);
  3473. Code := 0;
  3474. try
  3475. try
  3476. CompileFile(CommandLineFilename, True);
  3477. except
  3478. Code := 2;
  3479. Application.HandleException(Self);
  3480. end;
  3481. finally
  3482. Halt(Code);
  3483. end;
  3484. end;
  3485. procedure TMainForm.WMStartCommandLineWizard(var Message: TMessage);
  3486. var
  3487. Code: Integer;
  3488. begin
  3489. Code := 0;
  3490. try
  3491. try
  3492. NewMainFileUsingWizard;
  3493. except
  3494. Code := 2;
  3495. Application.HandleException(Self);
  3496. end;
  3497. finally
  3498. Halt(Code);
  3499. end;
  3500. end;
  3501. procedure TMainForm.WMStartNormally(var Message: TMessage);
  3502. procedure ShowStartupForm;
  3503. var
  3504. StartupForm: TStartupForm;
  3505. Ini: TConfigIniFile;
  3506. begin
  3507. ReadMRUMainFilesList;
  3508. StartupForm := TStartupForm.Create(Application);
  3509. try
  3510. StartupForm.MRUFilesList := FMRUMainFilesList;
  3511. StartupForm.StartupCheck.Checked := not FOptions.ShowStartupForm;
  3512. if StartupForm.ShowModal = mrOK then begin
  3513. if FOptions.ShowStartupForm <> not StartupForm.StartupCheck.Checked then begin
  3514. FOptions.ShowStartupForm := not StartupForm.StartupCheck.Checked;
  3515. Ini := TConfigIniFile.Create;
  3516. try
  3517. Ini.WriteBool('Options', 'ShowStartupForm', FOptions.ShowStartupForm);
  3518. finally
  3519. Ini.Free;
  3520. end;
  3521. end;
  3522. case StartupForm.Result of
  3523. srEmpty:
  3524. FNewMainFileClick(Self);
  3525. srWizard:
  3526. FNewMainFileUserWizardClick(Self);
  3527. srOpenFile:
  3528. if ConfirmCloseFile(True) then
  3529. OpenMRUMainFile(StartupForm.ResultMainFileName);
  3530. srOpenDialog:
  3531. ShowOpenMainFileDialog(False);
  3532. srOpenDialogExamples:
  3533. ShowOpenMainFileDialog(True);
  3534. end;
  3535. end;
  3536. finally
  3537. StartupForm.Free;
  3538. end;
  3539. end;
  3540. begin
  3541. if CommandLineFilename = '' then begin
  3542. if FOptions.ShowStartupForm then
  3543. ShowStartupForm;
  3544. end else
  3545. OpenFile(FMainMemo, CommandLineFilename, False);
  3546. end;
  3547. procedure TMainForm.WMSysColorChange(var Message: TMessage);
  3548. begin
  3549. inherited;
  3550. for var Memo in FMemos do
  3551. Memo.SysColorChange(Message);
  3552. end;
  3553. procedure TMainForm.UpdateReopenTabMenu(const Menu: TMenuItem);
  3554. begin
  3555. Menu.Clear;
  3556. for var I := 0 to FHiddenFiles.Count-1 do begin
  3557. var MenuItem := TMenuItem.Create(Menu);
  3558. MenuItem.Caption := '&' + IntToStr((I+1) mod 10) + ' ' + DoubleAmp(PathExtractName(FHiddenFiles[I]));
  3559. MenuItem.Tag := I;
  3560. MenuItem.OnClick := ReopenTabClick;
  3561. Menu.Add(MenuItem);
  3562. end;
  3563. end;
  3564. procedure TMainForm.MemosTabSetPopupMenuClick(Sender: TObject);
  3565. begin
  3566. { Main and preprocessor memos can't be hidden }
  3567. VCloseCurrentTab2.Enabled := (FActiveMemo <> FMainMemo) and (FActiveMemo <> FPreprocessorOutputMemo);
  3568. VReopenTab2.Visible := FHiddenFiles.Count > 0;
  3569. if VReopenTab2.Visible then
  3570. UpdateReopenTabMenu(VReopenTab2);
  3571. VReopenTabs2.Visible := VReopenTab2.Visible;
  3572. ApplyMenuBitmaps(Sender as TMenuItem)
  3573. end;
  3574. procedure TMainForm.MemosTabSetClick(Sender: TObject);
  3575. begin
  3576. if FIgnoreTabSetClick then
  3577. Exit;
  3578. var NewActiveMemo := TabIndexToMemo(MemosTabSet.TabIndex, MemosTabSet.Tabs.Count-1);
  3579. if NewActiveMemo <> FActiveMemo then begin
  3580. { Avoiding flicker by showing new before hiding old }
  3581. NewActiveMemo.Visible := True;
  3582. var OldActiveMemo := FActiveMemo;
  3583. FActiveMemo := NewActiveMemo;
  3584. ActiveControl := NewActiveMemo;
  3585. OldActiveMemo.CancelAutoCompleteAndCallTip;
  3586. OldActiveMemo.Visible := False;
  3587. UpdateSaveMenuItemAndButton;
  3588. UpdateRunMenu;
  3589. UpdateCaretPosPanelAndBackNavStack;
  3590. UpdateEditModePanel;
  3591. UpdateModifiedPanel;
  3592. end;
  3593. end;
  3594. procedure TMainForm.MemosTabSetOnCloseButtonClick(Sender: TObject; Index: Integer);
  3595. begin
  3596. CloseTab(Index);
  3597. end;
  3598. procedure TMainForm.InitializeFindText(Dlg: TFindDialog);
  3599. var
  3600. S: String;
  3601. begin
  3602. S := FActiveMemo.MainSelText;
  3603. if (S <> '') and (Pos(#13, S) = 0) and (Pos(#10, S) = 0) then
  3604. Dlg.FindText := S
  3605. else
  3606. Dlg.FindText := FLastFindText;
  3607. end;
  3608. const
  3609. OldFindReplaceWndProcProp = 'OldFindReplaceWndProc';
  3610. function FindReplaceWndProc(Wnd: HWND; Msg: Cardinal; WParam: WPARAM; LParam: LPARAM): LRESULT; stdcall;
  3611. function CallDefWndProc: LRESULT;
  3612. begin
  3613. Result := CallWindowProc(Pointer(GetProp(Wnd, OldFindReplaceWndProcProp)), Wnd,
  3614. Msg, WParam, LParam);
  3615. end;
  3616. begin
  3617. case Msg of
  3618. WM_MENUCHAR:
  3619. if LoWord(wParam) = VK_RETURN then begin
  3620. var hwndCtl := GetDlgItem(Wnd, idOk);
  3621. if (hWndCtl <> 0) and IsWindowEnabled(hWndCtl) then
  3622. PostMessage(Wnd, WM_COMMAND, MakeWParam(idOk, BN_CLICKED), Windows.LPARAM(hWndCtl));
  3623. end;
  3624. WM_NCDESTROY:
  3625. begin
  3626. Result := CallDefWndProc;
  3627. RemoveProp(Wnd, OldFindReplaceWndProcProp);
  3628. Exit;
  3629. end;
  3630. end;
  3631. Result := CallDefWndProc;
  3632. end;
  3633. procedure ExecuteFindDialogAllowingAltEnter(const FindDialog: TFindDialog);
  3634. begin
  3635. var DoHook := FindDialog.Handle = 0;
  3636. FindDialog.Execute;
  3637. if DoHook then begin
  3638. SetProp(FindDialog.Handle, OldFindReplaceWndProcProp, GetWindowLong(FindDialog.Handle, GWL_WNDPROC));
  3639. SetWindowLong(FindDialog.Handle, GWL_WNDPROC, IntPtr(@FindReplaceWndProc));
  3640. end;
  3641. end;
  3642. procedure TMainForm.EFindClick(Sender: TObject);
  3643. begin
  3644. ReplaceDialog.CloseDialog;
  3645. if FindDialog.Handle = 0 then
  3646. InitializeFindText(FindDialog);
  3647. if (Sender = EFind) or (Sender = EFindNext) then
  3648. FindDialog.Options := FindDialog.Options + [frDown]
  3649. else
  3650. FindDialog.Options := FindDialog.Options - [frDown];
  3651. ExecuteFindDialogAllowingAltEnter(FindDialog);
  3652. end;
  3653. procedure TMainForm.EFindInFilesClick(Sender: TObject);
  3654. begin
  3655. InitializeFindText(FindInFilesDialog);
  3656. FindInFilesDialog.Execute;
  3657. end;
  3658. procedure TMainForm.EFindNextOrPreviousClick(Sender: TObject);
  3659. begin
  3660. if FLastFindText = '' then
  3661. EFindClick(Sender)
  3662. else begin
  3663. if Sender = EFindNext then
  3664. FLastFindOptions := FLastFindOptions + [frDown]
  3665. else
  3666. FLastFindOptions := FLastFindOptions - [frDown];
  3667. FLastFindRegEx := FOptions.FindRegEx;
  3668. if not TestLastFindOptions then
  3669. Exit;
  3670. FindNext(False);
  3671. end;
  3672. end;
  3673. procedure TMainForm.FindNext(const ReverseDirection: Boolean);
  3674. var
  3675. StartPos, EndPos: Integer;
  3676. Range: TScintRange;
  3677. begin
  3678. var Down := frDown in FLastFindOptions;
  3679. if ReverseDirection then
  3680. Down := not Down;
  3681. if Down then begin
  3682. StartPos := FActiveMemo.Selection.EndPos;
  3683. EndPos := FActiveMemo.RawTextLength;
  3684. end
  3685. else begin
  3686. StartPos := FActiveMemo.Selection.StartPos;
  3687. EndPos := 0;
  3688. end;
  3689. if FActiveMemo.FindText(StartPos, EndPos, FLastFindText,
  3690. FindOptionsToSearchOptions(FLastFindOptions, FLastFindRegEx), Range) then
  3691. FActiveMemo.SelectAndEnsureVisible(Range)
  3692. else
  3693. MsgBoxFmt('Cannot find "%s"', [FLastFindText], SCompilerFormCaption,
  3694. mbInformation, MB_OK);
  3695. end;
  3696. function TMainForm.StoreAndTestLastFindOptions(Sender: TObject): Boolean;
  3697. begin
  3698. { TReplaceDialog is a subclass of TFindDialog must check for TReplaceDialog first }
  3699. if Sender is TReplaceDialog then begin
  3700. with Sender as TReplaceDialog do begin
  3701. FLastFindOptions := Options;
  3702. FLastFindText := FindText;
  3703. end;
  3704. end else begin
  3705. with Sender as TFindDialog do begin
  3706. FLastFindOptions := Options;
  3707. FLastFindText := FindText;
  3708. end;
  3709. end;
  3710. FLastFindRegEx := FOptions.FindRegEx;
  3711. Result := TestLastFindOptions;
  3712. end;
  3713. function TMainForm.TestLastFindOptions;
  3714. begin
  3715. if FLastFindRegEx then begin
  3716. Result := FActiveMemo.TestRegularExpression(FLastFindText);
  3717. if not Result then
  3718. MsgBoxFmt('Invalid regular expression "%s"', [FLastFindText], SCompilerFormCaption,
  3719. mbError, MB_OK);
  3720. end else
  3721. Result := True;
  3722. end;
  3723. procedure TMainForm.FindDialogFind(Sender: TObject);
  3724. begin
  3725. { This event handler is shared between FindDialog & ReplaceDialog }
  3726. if not StoreAndTestLastFindOptions(Sender) then
  3727. Exit;
  3728. if GetKeyState(VK_MENU) < 0 then begin
  3729. { Alt+Enter was used to close the dialog }
  3730. (Sender as TFindDialog).CloseDialog;
  3731. ESelectAllFindMatchesClick(Self); { Uses the copy made above }
  3732. end else
  3733. FindNext(GetKeyState(VK_SHIFT) < 0);
  3734. end;
  3735. procedure TMainForm.FindInFilesDialogFind(Sender: TObject);
  3736. begin
  3737. if not StoreAndTestLastFindOptions(Sender) then
  3738. Exit;
  3739. FindResultsList.Clear;
  3740. SendMessage(FindResultsList.Handle, LB_SETHORIZONTALEXTENT, 0, 0);
  3741. FFindResults.Clear;
  3742. var Hits := 0;
  3743. var Files := 0;
  3744. for var Memo in FFileMemos do begin
  3745. if Memo.Used then begin
  3746. var StartPos := 0;
  3747. var EndPos := Memo.RawTextLength;
  3748. var FileHits := 0;
  3749. var Range: TScintRange;
  3750. while (StartPos < EndPos) and
  3751. Memo.FindText(StartPos, EndPos, FLastFindText,
  3752. FindOptionsToSearchOptions(FLastFindOptions, FLastFindRegEx), Range) do begin
  3753. { Also see UpdateFindResult }
  3754. var Line := Memo.GetLineFromPosition(Range.StartPos);
  3755. var Prefix := Format(' Line %d: ', [Line+1]);
  3756. var FindResult := TFindResult.Create;
  3757. FindResult.Filename := Memo.Filename;
  3758. FindResult.Line := Line;
  3759. FindResult.LineStartPos := Memo.GetPositionFromLine(Line);
  3760. FindResult.Range := Range;
  3761. FindResult.PrefixStringLength := Length(Prefix);
  3762. FFindResults.Add(FindResult);
  3763. FindResultsList.Items.AddObject(Prefix + Memo.Lines[Line], FindResult);
  3764. Inc(FileHits);
  3765. StartPos := Range.EndPos;
  3766. end;
  3767. Inc(Files);
  3768. if FileHits > 0 then begin
  3769. Inc(Hits, FileHits);
  3770. FindResultsList.Items.Insert(FindResultsList.Count-FileHits, Format('%s (%d hits):', [Memo.Filename, FileHits]));
  3771. end;
  3772. end;
  3773. end;
  3774. FindResultsList.Items.Insert(0, Format('Find "%s" (%d hits in %d files)', [FindInFilesDialog.FindText, Hits, Files]));
  3775. FindInFilesDialog.CloseDialog;
  3776. OutputTabSet.TabIndex := tiFindResults;
  3777. SetStatusPanelVisible(True);
  3778. end;
  3779. function TMainForm.FindSetupDirectiveValue(const DirectiveName,
  3780. DefaultValue: String): String;
  3781. begin
  3782. Result := DefaultValue;
  3783. var Memo := FMainMemo; { This function only searches the main file }
  3784. var StartPos := 0;
  3785. var EndPos := Memo.RawTextLength;
  3786. var Range: TScintRange;
  3787. { We rely on the styler to identify [Setup] section lines, but we
  3788. may be searching into areas that haven't been styled yet }
  3789. Memo.StyleNeeded(EndPos);
  3790. while (StartPos < EndPos) and
  3791. Memo.FindText(StartPos, EndPos, DirectiveName, [sfoWholeWord], Range) do begin
  3792. var Line := Memo.GetLineFromPosition(Range.StartPos);
  3793. if FMemosStyler.GetSectionFromLineState(Memo.Lines.State[Line]) = scSetup then begin
  3794. var LineValue := Memo.Lines[Line].Trim; { LineValue can't be empty }
  3795. if LineValue[1] <> ';' then begin
  3796. var LineParts := LineValue.Split(['=']);
  3797. if (Length(LineParts) = 2) and SameText(LineParts[0].Trim, DirectiveName) then begin
  3798. Result := LineParts[1].Trim;
  3799. { If Result is surrounded in quotes, remove them, just like TSetupCompiler.SeparateDirective }
  3800. if (Length(Result) >= 2) and
  3801. (Result[1] = '"') and (Result[Length(Result)] = '"') then
  3802. Result := Copy(Result, 2, Length(Result)-2);
  3803. Exit; { Compiler doesn't allow a directive to be specified twice so we can exit now }
  3804. end;
  3805. end;
  3806. end;
  3807. StartPos := Range.EndPos;
  3808. end;
  3809. end;
  3810. function TMainForm.FindSetupDirectiveValue(const DirectiveName: String;
  3811. DefaultValue: Boolean): Boolean;
  3812. begin
  3813. var Value := FindSetupDirectiveValue(DirectiveName, IfThen(DefaultValue, '1', '0'));
  3814. if not TryStrToBoolean(Value, Result) then
  3815. Result := DefaultValue;
  3816. end;
  3817. procedure TMainForm.EReplaceClick(Sender: TObject);
  3818. begin
  3819. FindDialog.CloseDialog;
  3820. if ReplaceDialog.Handle = 0 then begin
  3821. InitializeFindText(ReplaceDialog);
  3822. ReplaceDialog.ReplaceText := FLastReplaceText;
  3823. end;
  3824. ExecuteFindDialogAllowingAltEnter(ReplaceDialog);
  3825. end;
  3826. procedure TMainForm.ReplaceDialogReplace(Sender: TObject);
  3827. begin
  3828. if not StoreAndTestLastFindOptions(Sender) then
  3829. Exit;
  3830. FLastReplaceText := ReplaceDialog.ReplaceText;
  3831. var ReplaceMode := RegExToReplaceMode(FLastFindRegEx);
  3832. if frReplaceAll in FLastFindOptions then begin
  3833. var ReplaceCount := 0;
  3834. FActiveMemo.BeginUndoAction;
  3835. try
  3836. var Pos := 0;
  3837. var Range: TScintRange;
  3838. while FActiveMemo.FindText(Pos, FActiveMemo.RawTextLength, FLastFindText,
  3839. FindOptionsToSearchOptions(FLastFindOptions, FLastFindRegEx), Range) do begin
  3840. var NewRange := FActiveMemo.ReplaceTextRange(Range.StartPos, Range.EndPos, FLastReplaceText, ReplaceMode);
  3841. Pos := NewRange.EndPos;
  3842. Inc(ReplaceCount);
  3843. end;
  3844. finally
  3845. FActiveMemo.EndUndoAction;
  3846. end;
  3847. if ReplaceCount = 0 then
  3848. MsgBoxFmt('Cannot find "%s"', [FLastFindText], SCompilerFormCaption,
  3849. mbInformation, MB_OK)
  3850. else
  3851. MsgBoxFmt('%d occurrence(s) replaced.', [ReplaceCount], SCompilerFormCaption,
  3852. mbInformation, MB_OK);
  3853. end
  3854. else begin
  3855. if FActiveMemo.MainSelTextEquals(FLastFindText, FindOptionsToSearchOptions(frMatchCase in FLastFindOptions, FLastFindRegEx)) then begin
  3856. { Note: the MainSelTextEquals above performs a search so the replacement
  3857. below is safe even if the user just enabled regex }
  3858. FActiveMemo.ReplaceMainSelText(FLastReplaceText, ReplaceMode);
  3859. end;
  3860. FindNext(GetKeyState(VK_SHIFT) < 0);
  3861. end;
  3862. end;
  3863. procedure TMainForm.EFindRegExClick(Sender: TObject);
  3864. begin
  3865. { If EFindRegEx uses Alt+R as the shortcut just like VSCode then also handle it like VSCode:
  3866. when the memo does not have the focus open the Run menu (also Alt+R) instead }
  3867. if not FActiveMemo.Focused and (EFindRegEx.ShortCut = ShortCut(Ord('R'), [ssAlt])) then
  3868. SendMessage(Handle, WM_SYSCOMMAND, SC_KEYMENU, Ord('r'))
  3869. else begin
  3870. FOptions.FindRegEx := not FOptions.FindRegEx;
  3871. UpdateFindRegExUI;
  3872. var Ini := TConfigIniFile.Create;
  3873. try
  3874. Ini.WriteBool('Options', 'FindRegEx', FOptions.FindRegEx);
  3875. finally
  3876. Ini.Free;
  3877. end;
  3878. end;
  3879. end;
  3880. procedure TMainForm.EFoldOrUnfoldLineClick(Sender: TObject);
  3881. begin
  3882. FActiveMemo.FoldLine(FActiveMemo.CaretLine, Sender = EFoldLine);
  3883. end;
  3884. procedure TMainForm.UpdateStatusPanelHeight(H: Integer);
  3885. var
  3886. MinHeight, MaxHeight: Integer;
  3887. begin
  3888. MinHeight := (3 * DebugOutputList.ItemHeight + ToCurrentPPI(4)) + OutputTabSet.Height;
  3889. MaxHeight := BodyPanel.ClientHeight - ToCurrentPPI(48) - SplitPanel.Height;
  3890. if H > MaxHeight then H := MaxHeight;
  3891. if H < MinHeight then H := MinHeight;
  3892. StatusPanel.Height := H;
  3893. end;
  3894. procedure TMainForm.UpdateOccurrenceIndicators(const AMemo: TIDEScintEdit);
  3895. procedure FindTextAndAddRanges(const AMemo: TIDEScintEdit;
  3896. const TextToFind: TScintRawString; const Options: TScintFindOptions;
  3897. const Selections, IndicatorRanges: TScintRangeList);
  3898. begin
  3899. if TScintEdit.RawStringIsBlank(TextToFind) then
  3900. Exit;
  3901. var StartPos := 0;
  3902. var EndPos := AMemo.RawTextLength;
  3903. var FoundRange: TScintRange;
  3904. while (StartPos < EndPos) and
  3905. AMemo.FindRawText(StartPos, EndPos, TextToFind, Options, FoundRange) do begin
  3906. StartPos := FoundRange.EndPos;
  3907. { Don't add indicators on lines which have a line marker }
  3908. var Line := AMemo.GetLineFromPosition(FoundRange.StartPos);
  3909. var Markers := AMemo.GetMarkers(Line);
  3910. if Markers * [mlmError, mlmBreakpointBad, mlmStep] <> [] then
  3911. Continue;
  3912. { Add indicator while making sure it does not overlap any regular selection
  3913. styling for either the main selection or any additional selection. Does
  3914. not account for an indicator overlapping more than 1 selection. }
  3915. var OverlappingSelection: TScintRange;
  3916. if Selections.Overlaps(FoundRange, OverlappingSelection) then begin
  3917. if FoundRange.StartPos < OverlappingSelection.StartPos then
  3918. IndicatorRanges.Add(TScintRange.Create(FoundRange.StartPos, OverlappingSelection.StartPos));
  3919. if FoundRange.EndPos > OverlappingSelection.EndPos then
  3920. IndicatorRanges.Add(TScintRange.Create(OverlappingSelection.EndPos, FoundRange.EndPos));
  3921. end else
  3922. IndicatorRanges.Add(FoundRange);
  3923. end;
  3924. end;
  3925. function HighlightAtCursorAllowed(const Word: TScintRawString): Boolean;
  3926. begin
  3927. const Section = FMemosStyler.GetSectionFromLineState(FActiveMemo.Lines.State[FActiveMemo.CaretLine]);
  3928. Result := FMemosStyler.HighlightAtCursorAllowed(Section, FActiveMemo.ConvertRawStringToString(Word));
  3929. end;
  3930. begin
  3931. { Add occurrence indicators for the word at cursor if there's any and the
  3932. main selection is within this word. On top of those add occurrence indicators
  3933. for the main selected text if there's any. Don't do anything if the main
  3934. selection is not single line. All of these things are just like VSCode. }
  3935. var MainSelection: TScintRange;
  3936. var MainSelNotEmpty := AMemo.SelNotEmpty(MainSelection);
  3937. var MainSelSingleLine := AMemo.GetLineFromPosition(MainSelection.StartPos) =
  3938. AMemo.GetLineFromPosition(MainSelection.EndPos);
  3939. var IndicatorRanges: TScintRangeList := nil;
  3940. var Selections: TScintRangeList := nil;
  3941. try
  3942. IndicatorRanges := TScintRangeList.Create;
  3943. Selections := TScintRangeList.Create;
  3944. if FOptions.HighlightWordAtCursorOccurrences and (AMemo.CaretVirtualSpace = 0) and MainSelSingleLine then begin
  3945. var Word := AMemo.WordAtCaretRange;
  3946. if (Word.StartPos <> Word.EndPos) and MainSelection.Within(Word) then begin
  3947. var TextToIndicate := AMemo.GetRawTextRange(Word.StartPos, Word.EndPos);
  3948. if HighlightAtCursorAllowed(TextToIndicate) then begin
  3949. AMemo.GetSelections(Selections); { Gets any additional selections as well }
  3950. FindTextAndAddRanges(AMemo, TextToIndicate, [sfoMatchCase, sfoWholeWord], Selections, IndicatorRanges);
  3951. end;
  3952. end;
  3953. end;
  3954. AMemo.UpdateIndicators(IndicatorRanges, minWordAtCursorOccurrence);
  3955. IndicatorRanges.Clear;
  3956. if FOptions.HighlightSelTextOccurrences and MainSelNotEmpty and MainSelSingleLine then begin
  3957. var TextToIndicate := AMemo.RawMainSelText;
  3958. if Selections.Count = 0 then { If 0 then we didn't already call GetSelections above}
  3959. AMemo.GetSelections(Selections);
  3960. FindTextAndAddRanges(AMemo, TextToIndicate, [], Selections, IndicatorRanges);
  3961. end;
  3962. AMemo.UpdateIndicators(IndicatorRanges, minSelTextOccurrence);
  3963. finally
  3964. Selections.Free;
  3965. IndicatorRanges.Free;
  3966. end;
  3967. end;
  3968. procedure TMainForm.UpdateImages;
  3969. { Should be called at startup and after DPI changes }
  3970. begin
  3971. var WH := MulDiv(16, CurrentPPI, 96);
  3972. var Images := ImagesModule.LightToolBarImageCollection;
  3973. var Image := Images.GetSourceImage(Images.GetIndexByName('heart-filled'), WH, WH);
  3974. UpdatePanelDonateBitBtn.Graphic := Image;
  3975. end;
  3976. procedure TMainForm.UpdateOutputTabSetListsItemHeightAndDebugTimeWidth;
  3977. { Should be called at startup and after DPI changes }
  3978. begin
  3979. CompilerOutputList.Canvas.Font.Assign(CompilerOutputList.Font);
  3980. CompilerOutputList.ItemHeight := CompilerOutputList.Canvas.TextHeight('0') + 1;
  3981. DebugOutputList.Canvas.Font.Assign(DebugOutputList.Font);
  3982. FDebugLogListTimestampsWidth := DebugOutputList.Canvas.TextWidth(Format('[00%s00%s00%s000] ', [FormatSettings.TimeSeparator, FormatSettings.TimeSeparator, FormatSettings.DecimalSeparator]));
  3983. DebugOutputList.ItemHeight := DebugOutputList.Canvas.TextHeight('0') + 1;
  3984. DebugCallStackList.Canvas.Font.Assign(DebugCallStackList.Font);
  3985. DebugCallStackList.ItemHeight := DebugCallStackList.Canvas.TextHeight('0') + 1;
  3986. FindResultsList.Canvas.Font.Assign(FindResultsList.Font);
  3987. FindResultsList.ItemHeight := FindResultsList.Canvas.TextHeight('0') + 1;
  3988. end;
  3989. type
  3990. TBitmapWithBits = class
  3991. Handle: HBITMAP;
  3992. pvBits: Pointer;
  3993. destructor Destroy; override;
  3994. end;
  3995. destructor TBitmapWithBits.Destroy;
  3996. begin
  3997. if Handle <> 0 then
  3998. DeleteObject(Handle);
  3999. inherited;
  4000. end;
  4001. procedure TMainForm.UpdateMarginsAndAutoCompleteIcons;
  4002. { Should be called at startup and after theme and DPI changes }
  4003. type
  4004. TMarkerOrACBitmaps = TObjectDictionary<Integer, TBitmapWithBits>;
  4005. procedure SwapRedBlue(const pvBits: PByte; Width, Height: Integer);
  4006. begin
  4007. var pvPixel := pvBits;
  4008. var pvMax := pvBits + 4*Width*Height;
  4009. while pvPixel < pvMax do begin
  4010. var Tmp := PByte(pvPixel)^;
  4011. PByte(pvPixel)^ := PByte(pvPixel + 2)^;
  4012. PByte(pvPixel + 2)^ := Tmp;
  4013. Inc(pvPixel, 4);
  4014. end;
  4015. end;
  4016. procedure AddMarkerOrACBitmap(const MarkerOrACBitmaps: TMarkerOrACBitmaps; const DC: HDC; const BitmapInfo: TBitmapInfo;
  4017. const MarkerNumberOrACType: Integer; const BkBrush: TBrush; const ImageList: TVirtualImageList; const ImageName: String);
  4018. begin
  4019. { Prepare a bitmap and select it }
  4020. var pvBits: Pointer;
  4021. var Bitmap := CreateDIBSection(DC, BitmapInfo, DIB_RGB_COLORS, pvBits, 0, 0);
  4022. var OldBitmap := SelectObject(DC, Bitmap);
  4023. { Fill the entire bitmap to avoid any alpha so we don't have to worry about
  4024. whether will be premultiplied or not (it was in tests) when Scintilla wants
  4025. it without premultiplication }
  4026. var Width := BitmapInfo.bmiHeader.biWidth;
  4027. var Height := Abs(BitmapInfo.bmiHeader.biHeight);
  4028. var Rect := TRect.Create(0, 0, Width, Height);
  4029. FillRect(DC, Rect, BkBrush.Handle);
  4030. { Draw the image - the result will be in pvBits }
  4031. if ImageList_Draw(ImageList.Handle, ImageList.GetIndexByName(ImageName), DC, 0, 0, ILD_TRANSPARENT) then begin
  4032. SwapRedBlue(pvBits, Width, Height); { Change pvBits from BGRA to RGBA like Scintilla wants }
  4033. var Bitmap2 := TBitmapWithBits.Create;
  4034. Bitmap2.Handle := Bitmap;
  4035. Bitmap2.pvBits := pvBits;
  4036. MarkerOrACBitmaps.Add(MarkerNumberOrACType, Bitmap2);
  4037. end else begin
  4038. SelectObject(DC, OldBitmap);
  4039. DeleteObject(Bitmap);
  4040. end;
  4041. end;
  4042. type
  4043. TMarkerNumberOrACType = TPair<Integer, String>;
  4044. function NNT(const MarkerNumberOrACType: Integer; const Name: String): TMarkerNumberOrACType;
  4045. begin
  4046. Result := TMarkerNumberOrACType.Create(MarkerNumberOrACType, Name); { This is a record so no need to free }
  4047. end;
  4048. begin
  4049. var ImageList := ThemedMarkersAndACVirtualImageList;
  4050. var DC := CreateCompatibleDC(0);
  4051. if DC <> 0 then begin
  4052. try
  4053. var MarkerBitmaps: TMarkerOrACBitmaps := nil;
  4054. var MarkerBkBrush: TBrush := nil;
  4055. var AutoCompleteBitmaps: TMarkerOrACBitmaps := nil;
  4056. var AutoCompleteBkBrush: TBrush := nil;
  4057. try
  4058. var BitmapInfo := CreateBitmapInfo(ImageList.Width, -ImageList.Height, 32); { This is a record so no need to free }
  4059. MarkerBitmaps := TMarkerOrACBitmaps.Create([doOwnsValues]);
  4060. MarkerBkBrush := TBrush.Create;
  4061. MarkerBkBrush.Color := FTheme.Colors[tcMarginBack];
  4062. var NamedMarkers := [
  4063. NNT(mmiHasEntry, 'markers\debug-stop-filled'),
  4064. NNT(mmiEntryProcessed, 'markers\debug-stop-filled_2'),
  4065. NNT(mmiBreakpoint, 'markers\debug-breakpoint-filled'),
  4066. NNT(mmiBreakpointBad, 'markers\debug-breakpoint-filled-cancel-2'),
  4067. NNT(mmiBreakpointGood, 'markers\debug-breakpoint-filled-ok-2'),
  4068. NNT(mmiStep, 'markers\symbol-arrow-right'),
  4069. NNT(mmiBreakpointStep, 'markers\debug-breakpoint-filled-ok2-symbol-arrow-right'),
  4070. NNT(SC_MARKNUM_FOLDER, 'markers\symbol-add'),
  4071. NNT(SC_MARKNUM_FOLDEROPEN, 'markers\symbol-remove')];
  4072. for var NamedMarker in NamedMarkers do
  4073. AddMarkerOrAcBitmap(MarkerBitmaps, DC, BitmapInfo, NamedMarker.Key, MarkerBkBrush, ImageList, NamedMarker.Value);
  4074. AutoCompleteBitmaps := TMarkerOrACBitmaps.Create([doOwnsValues]);
  4075. AutoCompleteBkBrush := TBrush.Create;
  4076. AutoCompleteBkBrush.Color := FTheme.Colors[tcIntelliBack];
  4077. var NamedTypes := [
  4078. NNT(awtSection, 'ac\structure-filled'),
  4079. NNT(awtParameter, 'ac\xml-filled'),
  4080. NNT(awtDirective, 'ac\xml-filled'),
  4081. NNT(awtFlag, 'ac\values'),
  4082. NNT(awtPreprocessorDirective, 'ac\symbol-hashtag'),
  4083. NNT(awtConstant, 'ac\constant-filled_2'),
  4084. NNT(awtScriptFunction, 'ac\method-filled'),
  4085. NNT(awtScriptType, 'ac\types'),
  4086. NNT(awtScriptVariable, 'ac\variables'),
  4087. NNT(awtScriptConstant, 'ac\constant-filled'),
  4088. NNT(awtScriptInterface, 'ac\interface-filled'),
  4089. NNT(awtScriptProperty, 'ac\properties-filled'),
  4090. NNT(awtScriptEvent, 'ac\event-filled'),
  4091. NNT(awtScriptKeyword, 'ac\list'),
  4092. NNT(awtScriptEnumValue, 'ac\constant-filled')];
  4093. for var NamedType in NamedTypes do
  4094. AddMarkerOrAcBitmap(AutoCompleteBitmaps, DC, BitmapInfo, NamedType.Key, AutoCompleteBkBrush, ImageList, NamedType.Value);
  4095. for var Memo in FMemos do begin
  4096. Memo.Call(SCI_RGBAIMAGESETWIDTH, ImageList.Width, 0);
  4097. Memo.Call(SCI_RGBAIMAGESETHEIGHT, ImageList.Height, 0);
  4098. for var MarkerBitmap in MarkerBitmaps do
  4099. Memo.Call(SCI_MARKERDEFINERGBAIMAGE, MarkerBitmap.Key, LPARAM(MarkerBitmap.Value.pvBits));
  4100. for var AutoCompleteBitmap in AutoCompleteBitmaps do
  4101. Memo.Call(SCI_REGISTERRGBAIMAGE, AutoCompleteBitmap.Key, LPARAM(AutoCompleteBitmap.Value.pvBits));
  4102. end;
  4103. finally
  4104. AutoCompleteBkBrush.Free;
  4105. AutoCompleteBitmaps.Free;
  4106. MarkerBkBrush.Free;
  4107. MarkerBitmaps.Free;
  4108. end;
  4109. finally
  4110. DeleteDC(DC);
  4111. end;
  4112. end;
  4113. end;
  4114. procedure TMainForm.UpdateMarginsAndSquigglyAndCaretWidths;
  4115. { Update the width of our two margins. Note: the width of the line numbers
  4116. margin is fully handled by TScintEdit. Should be called at startup and after
  4117. DPI change. }
  4118. begin
  4119. var IconMarkersWidth := ToCurrentPPI(18); { 3 pixel margin on both sides of the icon }
  4120. var BaseChangeHistoryWidth := ToCurrentPPI(6); { 6 = 2 pixel bar with 2 pixel margin on both sides because: "SC_MARK_BAR ... takes ... 1/3 of the margin width" }
  4121. var FolderMarkersWidth := ToCurrentPPI(14); { 1 pixel margin on boths side of the icon }
  4122. var LeftBlankMarginWidth := ToCurrentPPI(2); { 2 pixel margin between gutter and the main text }
  4123. var SquigglyWidth := ToCurrentPPI(100); { 100 = 1 pixel }
  4124. var CaretWidth := ToCurrentPPI(2);
  4125. var WhiteSpaceSize := CaretWidth;
  4126. for var Memo in FMemos do
  4127. Memo.UpdateWidthsAndSizes(IconMarkersWidth, BaseChangeHistoryWidth, FolderMarkersWidth,
  4128. LeftBlankMarginWidth, 0, SquigglyWidth, CaretWidth, WhiteSpaceSize);
  4129. end;
  4130. procedure TMainForm.SplitPanelMouseMove(Sender: TObject;
  4131. Shift: TShiftState; X, Y: Integer);
  4132. begin
  4133. if (ssLeft in Shift) and StatusPanel.Visible then begin
  4134. UpdateStatusPanelHeight(BodyPanel.ClientToScreen(Point(0, 0)).Y -
  4135. SplitPanel.ClientToScreen(Point(0, Y)).Y +
  4136. BodyPanel.ClientHeight - (SplitPanel.Height div 2));
  4137. end;
  4138. end;
  4139. procedure TMainForm.SimpleMenuClick(Sender: TObject);
  4140. begin
  4141. ApplyMenuBitmaps(Sender as TMenuItem);
  4142. end;
  4143. procedure TMainForm.TMenuClick(Sender: TObject);
  4144. var
  4145. MemoIsReadOnly: Boolean;
  4146. begin
  4147. MemoIsReadOnly := FActiveMemo.ReadOnly;
  4148. TGenerateGUID.Enabled := not MemoIsReadOnly;
  4149. TMsgBoxDesigner.Enabled := not MemoIsReadOnly;
  4150. TFilesDesigner.Enabled := not MemoIsReadOnly;
  4151. TRegistryDesigner.Enabled := not MemoIsReadOnly;
  4152. ApplyMenuBitmaps(Sender as TMenuItem);
  4153. end;
  4154. procedure TMainForm.TAddRemoveProgramsClick(Sender: TObject);
  4155. begin
  4156. StartAddRemovePrograms;
  4157. end;
  4158. procedure TMainForm.TGenerateGUIDClick(Sender: TObject);
  4159. begin
  4160. if MsgBox('The generated GUID will be inserted into the editor at the cursor position. Continue?',
  4161. SCompilerFormCaption, mbConfirmation, MB_YESNO) = IDYES then
  4162. FActiveMemo.MainSelText := GenerateGuid;
  4163. end;
  4164. procedure TMainForm.TMsgBoxDesignerClick(Sender: TObject);
  4165. begin
  4166. if (FMemosStyler.GetSectionFromLineState(FActiveMemo.Lines.State[FActiveMemo.CaretLine]) <> scCode) and
  4167. (MsgBox('The generated Pascal script will be inserted into the editor at the cursor position, but the cursor is not in the [Code] section. Continue anyway?',
  4168. SCompilerFormCaption, mbConfirmation, MB_YESNO) = IDNO) then
  4169. Exit;
  4170. var MsgBoxForm := TMsgBoxDesignerForm.Create(Application);
  4171. try
  4172. if MsgBoxForm.ShowModal = mrOk then
  4173. FActiveMemo.MainSelText := MsgBoxForm.GetText(FOptions.TabWidth, FOptions.UseTabCharacter);
  4174. finally
  4175. MsgBoxForm.Free;
  4176. end;
  4177. end;
  4178. procedure TMainForm.TRegistryDesignerClick(Sender: TObject);
  4179. begin
  4180. var RegistryDesignerForm := TRegistryDesignerForm.Create(Application);
  4181. try
  4182. var PrivilegesRequired := FindSetupDirectiveValue('PrivilegesRequired', 'admin');
  4183. var PrivilegesRequiredOverridesAllowed := FindSetupDirectiveValue('PrivilegesRequiredOverridesAllowed', '');
  4184. if PrivilegesRequiredOverridesAllowed = '' then begin
  4185. if SameText(PrivilegesRequired, 'admin') then
  4186. RegistryDesignerForm.PrivilegesRequired := prAdmin
  4187. else
  4188. RegistryDesignerForm.PrivilegesRequired := prLowest
  4189. end else
  4190. RegistryDesignerForm.PrivilegesRequired := prDynamic;
  4191. if RegistryDesignerForm.ShowModal = mrOk then
  4192. begin
  4193. FActiveMemo.CaretColumn := 0;
  4194. var Text := RegistryDesignerForm.Text;
  4195. if FMemosStyler.GetSectionFromLineState(FActiveMemo.Lines.State[FActiveMemo.CaretLine]) <> scRegistry then
  4196. Text := '[Registry]' + SNewLine + Text;
  4197. FActiveMemo.MainSelText := Text;
  4198. end;
  4199. finally
  4200. RegistryDesignerForm.Free;
  4201. end;
  4202. end;
  4203. procedure TMainForm.TFilesDesignerClick(Sender: TObject);
  4204. begin
  4205. var FilesDesignerForm := TFilesDesignerForm.Create(Application);
  4206. try
  4207. FilesDesignerForm.CreateAppDir := FindSetupDirectiveValue('CreateAppDir', True);
  4208. if FilesDesignerForm.ShowModal = mrOk then begin
  4209. FActiveMemo.CaretColumn := 0;
  4210. var Text := FilesDesignerForm.Text;
  4211. if FMemosStyler.GetSectionFromLineState(FActiveMemo.Lines.State[FActiveMemo.CaretLine]) <> scFiles then
  4212. Text := '[Files]' + SNewLine + Text;
  4213. FActiveMemo.MainSelText := Text;
  4214. end;
  4215. finally
  4216. FilesDesignerForm.Free;
  4217. end;
  4218. end;
  4219. procedure TMainForm.TSignToolsClick(Sender: TObject);
  4220. var
  4221. SignToolsForm: TSignToolsForm;
  4222. Ini: TConfigIniFile;
  4223. I: Integer;
  4224. begin
  4225. SignToolsForm := TSignToolsForm.Create(Application);
  4226. try
  4227. SignToolsForm.SignTools := FSignTools;
  4228. if SignToolsForm.ShowModal <> mrOK then
  4229. Exit;
  4230. FSignTools.Assign(SignToolsForm.SignTools);
  4231. { Save new options }
  4232. Ini := TConfigIniFile.Create;
  4233. try
  4234. Ini.EraseSection('SignTools');
  4235. for I := 0 to FSignTools.Count-1 do
  4236. Ini.WriteString('SignTools', 'SignTool' + IntToStr(I), FSignTools[I]);
  4237. finally
  4238. Ini.Free;
  4239. end;
  4240. finally
  4241. SignToolsForm.Free;
  4242. end;
  4243. end;
  4244. procedure TMainForm.TOptionsClick(Sender: TObject);
  4245. var
  4246. OptionsForm: TOptionsForm;
  4247. Ini: TConfigIniFile;
  4248. Memo: TIDEScintEdit;
  4249. begin
  4250. OptionsForm := TOptionsForm.Create(Application);
  4251. try
  4252. OptionsForm.StartupCheck.Checked := FOptions.ShowStartupForm;
  4253. OptionsForm.WizardCheck.Checked := FOptions.UseWizard;
  4254. OptionsForm.AutosaveCheck.Checked := FOptions.Autosave;
  4255. OptionsForm.AutoreloadCheck.Checked := FOptions.Autoreload;
  4256. OptionsForm.BackupCheck.Checked := FOptions.MakeBackups;
  4257. OptionsForm.FullPathCheck.Checked := FOptions.FullPathInTitleBar;
  4258. OptionsForm.UndoAfterSaveCheck.Checked := FOptions.UndoAfterSave;
  4259. OptionsForm.UndoAfterReloadCheck.Checked := FOptions.UndoAfterReload;
  4260. OptionsForm.PauseOnDebuggerExceptionsCheck.Checked := FOptions.PauseOnDebuggerExceptions;
  4261. OptionsForm.RunAsDifferentUserCheck.Checked := FOptions.RunAsDifferentUser;
  4262. OptionsForm.AutoAutoCompleteCheck.Checked := FOptions.AutoAutoComplete;
  4263. OptionsForm.UseSynHighCheck.Checked := FOptions.UseSyntaxHighlighting;
  4264. OptionsForm.ColorizeCompilerOutputCheck.Checked := FOptions.ColorizeCompilerOutput;
  4265. OptionsForm.UnderlineErrorsCheck.Checked := FOptions.UnderlineErrors;
  4266. OptionsForm.CursorPastEOLCheck.Checked := FOptions.CursorPastEOL;
  4267. OptionsForm.TabWidthEdit.Text := IntToStr(FOptions.TabWidth);
  4268. OptionsForm.UseTabCharacterCheck.Checked := FOptions.UseTabCharacter;
  4269. OptionsForm.ShowWhiteSpaceCheck.Checked := FOptions.ShowWhiteSpace;
  4270. OptionsForm.UseFoldingCheck.Checked := FOptions.UseFolding;
  4271. OptionsForm.AutoIndentCheck.Checked := FOptions.AutoIndent;
  4272. OptionsForm.IndentationGuidesCheck.Checked := FOptions.IndentationGuides;
  4273. OptionsForm.GutterLineNumbersCheck.Checked := FOptions.GutterLineNumbers;
  4274. OptionsForm.ShowPreprocessorOutputCheck.Checked := FOptions.ShowPreprocessorOutput;
  4275. OptionsForm.OpenIncludedFilesCheck.Checked := FOptions.OpenIncludedFiles;
  4276. OptionsForm.KeyMappingComboBox.ItemIndex := Ord(FOptions.KeyMappingType);
  4277. OptionsForm.MemoKeyMappingComboBox.ItemIndex := Ord(FOptions.MemoKeyMappingType);
  4278. OptionsForm.ThemeComboBox.ItemIndex := Ord(FOptions.ThemeType);
  4279. OptionsForm.FontPanel.Font.Assign(FMainMemo.Font);
  4280. OptionsForm.FontPanel.ParentBackground := False;
  4281. OptionsForm.FontPanel.Color := FMainMemo.Color;
  4282. OptionsForm.HighlightWordAtCursorOccurrencesCheck.Checked := FOptions.HighlightWordAtCursorOccurrences;
  4283. OptionsForm.HighlightSelTextOccurrencesCheck.Checked := FOptions.HighlightSelTextOccurrences;
  4284. if OptionsForm.ShowModal <> mrOK then
  4285. Exit;
  4286. FOptions.ShowStartupForm := OptionsForm.StartupCheck.Checked;
  4287. FOptions.UseWizard := OptionsForm.WizardCheck.Checked;
  4288. FOptions.Autosave := OptionsForm.AutosaveCheck.Checked;
  4289. FOptions.Autoreload := OptionsForm.AutoreloadCheck.Checked;
  4290. FOptions.MakeBackups := OptionsForm.BackupCheck.Checked;
  4291. FOptions.FullPathInTitleBar := OptionsForm.FullPathCheck.Checked;
  4292. FOptions.UndoAfterSave := OptionsForm.UndoAfterSaveCheck.Checked;
  4293. FOptions.UndoAfterReload := OptionsForm.UndoAfterReloadCheck.Checked;
  4294. FOptions.PauseOnDebuggerExceptions := OptionsForm.PauseOnDebuggerExceptionsCheck.Checked;
  4295. FOptions.RunAsDifferentUser := OptionsForm.RunAsDifferentUserCheck.Checked;
  4296. FOptions.AutoAutoComplete := OptionsForm.AutoAutoCompleteCheck.Checked;
  4297. FOptions.UseSyntaxHighlighting := OptionsForm.UseSynHighCheck.Checked;
  4298. FOptions.ColorizeCompilerOutput := OptionsForm.ColorizeCompilerOutputCheck.Checked;
  4299. FOptions.UnderlineErrors := OptionsForm.UnderlineErrorsCheck.Checked;
  4300. FOptions.CursorPastEOL := OptionsForm.CursorPastEOLCheck.Checked;
  4301. FOptions.TabWidth := StrToInt(OptionsForm.TabWidthEdit.Text);
  4302. FOptions.UseTabCharacter := OptionsForm.UseTabCharacterCheck.Checked;
  4303. FOptions.ShowWhiteSpace := OptionsForm.ShowWhiteSpaceCheck.Checked;
  4304. FOptions.UseFolding := OptionsForm.UseFoldingCheck.Checked;
  4305. FOptions.AutoIndent := OptionsForm.AutoIndentCheck.Checked;
  4306. FOptions.IndentationGuides := OptionsForm.IndentationGuidesCheck.Checked;
  4307. FOptions.GutterLineNumbers := OptionsForm.GutterLineNumbersCheck.Checked;
  4308. FOptions.ShowPreprocessorOutput := OptionsForm.ShowPreprocessorOutputCheck.Checked;
  4309. FOptions.OpenIncludedFiles := OptionsForm.OpenIncludedFilesCheck.Checked;
  4310. FOptions.KeyMappingType := TKeyMappingType(OptionsForm.KeyMappingComboBox.ItemIndex);
  4311. FOptions.MemoKeyMappingType := TIDEScintKeyMappingType(OptionsForm.MemoKeyMappingComboBox.ItemIndex);
  4312. FOptions.ThemeType := TThemeType(OptionsForm.ThemeComboBox.ItemIndex);
  4313. FOptions.HighlightWordAtCursorOccurrences := OptionsForm.HighlightWordAtCursorOccurrencesCheck.Checked;
  4314. FOptions.HighlightSelTextOccurrences := OptionsForm.HighlightSelTextOccurrencesCheck.Checked;
  4315. UpdateCaption;
  4316. UpdatePreprocMemos;
  4317. InvalidateStatusPanel(spHiddenFilesCount);
  4318. for Memo in FMemos do begin
  4319. { Move caret to start of line to ensure it doesn't end up in the middle
  4320. of a double-byte character if the code page changes from SBCS to DBCS }
  4321. Memo.CaretLine := Memo.CaretLine;
  4322. Memo.Font.Assign(OptionsForm.FontPanel.Font);
  4323. end;
  4324. SyncEditorOptions;
  4325. UpdateMarginsAndSquigglyAndCaretWidths;
  4326. UpdateNewMainFileButtons;
  4327. UpdateOccurrenceIndicators(FActiveMemo);
  4328. UpdateKeyMapping;
  4329. UpdateTheme;
  4330. { Save new options }
  4331. Ini := TConfigIniFile.Create;
  4332. try
  4333. Ini.WriteBool('Options', 'ShowStartupForm', FOptions.ShowStartupForm);
  4334. Ini.WriteBool('Options', 'UseWizard', FOptions.UseWizard);
  4335. Ini.WriteBool('Options', 'Autosave', FOptions.Autosave);
  4336. Ini.WriteBool('Options', 'Autoreload', FOptions.Autoreload);
  4337. Ini.WriteBool('Options', 'MakeBackups', FOptions.MakeBackups);
  4338. Ini.WriteBool('Options', 'FullPathInTitleBar', FOptions.FullPathInTitleBar);
  4339. Ini.WriteBool('Options', 'UndoAfterSave', FOptions.UndoAfterSave);
  4340. Ini.WriteBool('Options', 'UndoAfterReload', FOptions.UndoAfterReload);
  4341. Ini.WriteBool('Options', 'PauseOnDebuggerExceptions', FOptions.PauseOnDebuggerExceptions);
  4342. Ini.WriteBool('Options', 'RunAsDifferentUser', FOptions.RunAsDifferentUser);
  4343. Ini.WriteBool('Options', 'AutoComplete', FOptions.AutoAutoComplete);
  4344. Ini.WriteBool('Options', 'AutoCallTips', FOptions.AutoCallTips);
  4345. Ini.WriteBool('Options', 'UseSynHigh', FOptions.UseSyntaxHighlighting);
  4346. Ini.WriteBool('Options', 'ColorizeCompilerOutput', FOptions.ColorizeCompilerOutput);
  4347. Ini.WriteBool('Options', 'UnderlineErrors', FOptions.UnderlineErrors);
  4348. Ini.WriteBool('Options', 'HighlightWordAtCursorOccurrences', FOptions.HighlightWordAtCursorOccurrences);
  4349. Ini.WriteBool('Options', 'HighlightSelTextOccurrences', FOptions.HighlightSelTextOccurrences);
  4350. Ini.WriteBool('Options', 'EditorCursorPastEOL', FOptions.CursorPastEOL);
  4351. Ini.WriteInteger('Options', 'TabWidth', FOptions.TabWidth);
  4352. Ini.WriteBool('Options', 'UseTabCharacter', FOptions.UseTabCharacter);
  4353. Ini.WriteBool('Options', 'ShowWhiteSpace', FOptions.ShowWhiteSpace);
  4354. Ini.WriteBool('Options', 'UseFolding', FOptions.UseFolding);
  4355. Ini.WriteBool('Options', 'AutoIndent', FOptions.AutoIndent);
  4356. Ini.WriteBool('Options', 'IndentationGuides', FOptions.IndentationGuides);
  4357. Ini.WriteBool('Options', 'GutterLineNumbers', FOptions.GutterLineNumbers);
  4358. Ini.WriteBool('Options', 'ShowPreprocessorOutput', FOptions.ShowPreprocessorOutput);
  4359. Ini.WriteBool('Options', 'OpenIncludedFiles', FOptions.OpenIncludedFiles);
  4360. Ini.WriteInteger('Options', 'KeyMappingType', Ord(FOptions.KeyMappingType));
  4361. Ini.WriteInteger('Options', 'MemoKeyMappingType', Ord(FOptions.MemoKeyMappingType));
  4362. Ini.WriteInteger('Options', 'ThemeType', Ord(FOptions.ThemeType)); { Also see Destroy }
  4363. Ini.WriteString('Options', 'EditorFontName', FMainMemo.Font.Name);
  4364. Ini.WriteInteger('Options', 'EditorFontSize', FMainMemo.Font.Size);
  4365. Ini.WriteInteger('Options', 'EditorFontCharset', FMainMemo.Font.Charset);
  4366. finally
  4367. Ini.Free;
  4368. end;
  4369. finally
  4370. OptionsForm.Free;
  4371. end;
  4372. end;
  4373. { Also see TabIndexToMemoIndex }
  4374. function TMainForm.MemoToTabIndex(const AMemo: TIDEScintEdit): Integer;
  4375. begin
  4376. if AMemo = FMainMemo then
  4377. Result := 0 { First tab displays the main memo }
  4378. else if AMemo = FPreprocessorOutputMemo then begin
  4379. if not FPreprocessorOutputMemo.Used then
  4380. raise Exception.Create('not FPreprocessorOutputMemo.Used');
  4381. Result := MemosTabSet.Tabs.Count-1 { Last tab displays the preprocessor output memo }
  4382. end else begin
  4383. Result := FFileMemos.IndexOf(AMemo as TIDEScintFileEdit); { Other tabs display include files which start second tab }
  4384. { Filter memos explicitly hidden by the user }
  4385. for var MemoIndex := Result-1 downto 0 do
  4386. if FHiddenFiles.IndexOf(FFileMemos[MemoIndex].Filename) <> -1 then
  4387. Dec(Result);
  4388. end;
  4389. end;
  4390. { Also see MemoToTabIndex }
  4391. function TMainForm.TabIndexToMemo(const ATabIndex, AMaxTabIndex: Integer): TIDEScintEdit;
  4392. begin
  4393. if ATabIndex = 0 then
  4394. Result := FMemos[0] { First tab displays the main memo which is FMemos[0] }
  4395. else if FPreprocessorOutputMemo.Used and (ATabIndex = AMaxTabIndex) then
  4396. Result := FMemos[1] { Last tab displays the preprocessor output memo which is FMemos[1] }
  4397. else begin
  4398. { Only count memos not explicitly hidden by the user }
  4399. var TabIndex := 0;
  4400. for var MemoIndex := FirstIncludedFilesMemoIndex to FFileMemos.Count-1 do begin
  4401. if FHiddenFiles.IndexOf(FFileMemos[MemoIndex].Filename) = -1 then begin
  4402. Inc(TabIndex);
  4403. if TabIndex = ATabIndex then begin
  4404. Result := FMemos[MemoIndex + 1]; { Other tabs display include files which start at second tab but at FMemos[2] }
  4405. Exit;
  4406. end;
  4407. end;
  4408. end;
  4409. raise Exception.Create('TabIndexToMemo failed');
  4410. end;
  4411. end;
  4412. procedure TMainForm.MoveCaretAndActivateMemo(AMemo: TIDEScintEdit; const LineNumberOrPosition: Integer;
  4413. const AlwaysResetColumnEvenIfOnRequestedLineAlready: Boolean; const IsPosition: Boolean;
  4414. const PositionVirtualSpace: Integer);
  4415. var
  4416. Pos: Integer;
  4417. begin
  4418. { Reopen tab if needed }
  4419. if AMemo is TIDEScintFileEdit then begin
  4420. var FileName := (AMemo as TIDEScintFileEdit).Filename;
  4421. var HiddenFileIndex := FHiddenFiles.IndexOf(Filename);
  4422. if HiddenFileIndex <> -1 then begin
  4423. ReopenTabOrTabs(HiddenFileIndex, False);
  4424. { The above call to ReopenTabOrTabs will currently lead to a call to UpdateIncludedFilesMemos which
  4425. sets up all the memos. Currently it will keep same memo for the reopened file but in case it no
  4426. longer does at some point: look it up again }
  4427. AMemo := nil;
  4428. for var Memo in FFileMemos do begin
  4429. if Memo.Used and (PathCompare(Memo.Filename, Filename) = 0) then begin
  4430. AMemo := Memo;
  4431. Break;
  4432. end;
  4433. end;
  4434. if AMemo = nil then
  4435. raise Exception.Create('AMemo MIA');
  4436. end;
  4437. end;
  4438. { Move caret }
  4439. if IsPosition then
  4440. Pos := LineNumberOrPosition
  4441. else if AlwaysResetColumnEvenIfOnRequestedLineAlready or (AMemo.CaretLine <> LineNumberOrPosition) then
  4442. Pos := AMemo.GetPositionFromLine(LineNumberOrPosition)
  4443. else
  4444. Pos := AMemo.CaretPosition; { Not actually moving caret - it's already were we want it}
  4445. { If the line is in a contracted section, expand it }
  4446. AMemo.EnsureLineVisible(AMemo.GetLineFromPosition(Pos));
  4447. { If the line isn't in view, scroll so that it's in the center }
  4448. if not AMemo.IsPositionInViewVertically(Pos) then
  4449. AMemo.TopLine := AMemo.GetVisibleLineFromDocLine(AMemo.GetLineFromPosition(Pos)) -
  4450. (AMemo.LinesInWindow div 2);
  4451. AMemo.CaretPosition := Pos;
  4452. if IsPosition then
  4453. AMemo.CaretVirtualSpace := PositionVirtualSpace;
  4454. { Activate memo }
  4455. MemosTabSet.TabIndex := MemoToTabIndex(AMemo); { This causes MemosTabSetClick to show the memo }
  4456. end;
  4457. procedure TMainForm.SetErrorLine(const AMemo: TIDEScintFileEdit; const ALine: Integer);
  4458. var
  4459. OldLine: Integer;
  4460. begin
  4461. if AMemo <> FErrorMemo then begin
  4462. SetErrorLine(FErrorMemo, -1);
  4463. FErrorMemo := AMemo;
  4464. end;
  4465. if FErrorMemo.ErrorLine <> ALine then begin
  4466. OldLine := FErrorMemo.ErrorLine;
  4467. FErrorMemo.ErrorLine := ALine;
  4468. if OldLine >= 0 then
  4469. UpdateLineMarkers(FErrorMemo, OldLine);
  4470. if FErrorMemo.ErrorLine >= 0 then begin
  4471. FErrorMemo.ErrorCaretPosition := FErrorMemo.CaretPosition;
  4472. UpdateLineMarkers(FErrorMemo, FErrorMemo.ErrorLine);
  4473. end;
  4474. end;
  4475. end;
  4476. procedure TMainForm.SetStepLine(const AMemo: TIDEScintFileEdit; ALine: Integer);
  4477. var
  4478. OldLine: Integer;
  4479. begin
  4480. if AMemo <> FStepMemo then begin
  4481. SetStepLine(FStepMemo, -1);
  4482. FStepMemo := AMemo;
  4483. end;
  4484. if FStepMemo.StepLine <> ALine then begin
  4485. OldLine := FStepMemo.StepLine;
  4486. FStepMemo.StepLine := ALine;
  4487. if OldLine >= 0 then
  4488. UpdateLineMarkers(FStepMemo, OldLine);
  4489. if FStepMemo.StepLine >= 0 then
  4490. UpdateLineMarkers(FStepMemo, FStepMemo.StepLine);
  4491. end;
  4492. end;
  4493. procedure TMainForm.HideError;
  4494. begin
  4495. SetErrorLine(FErrorMemo, -1);
  4496. if not FCompiling then
  4497. StatusBar.Panels[spExtraStatus].Text := '';
  4498. end;
  4499. procedure TMainForm.RemoveMemoFromNav(const AMemo: TIDEScintEdit);
  4500. begin
  4501. if FNavStacks.RemoveMemo(AMemo) then
  4502. UpdateNavButtons;
  4503. if FCurrentNavItem.Memo = AMemo then
  4504. FCurrentNavItem.Invalidate;
  4505. end;
  4506. procedure TMainForm.RemoveMemoBadLinesFromNav(const AMemo: TIDEScintEdit);
  4507. begin
  4508. if FNavStacks.RemoveMemoBadLines(AMemo) then
  4509. UpdateNavButtons;
  4510. { We do NOT update FCurrentNav here so it might point to a line that's
  4511. deleted until next UpdateCaretPosPanelAndBackStack by UpdateMemoUI }
  4512. end;
  4513. procedure TMainForm.UpdateNavButtons;
  4514. begin
  4515. ForwardNavButton.Enabled := FNavStacks.Forward.Count > 0;
  4516. BackNavButton.Enabled := (FNavStacks.Back.Count > 0) or
  4517. ForwardNavButton.Enabled; { for the dropdown }
  4518. end;
  4519. procedure TMainForm.BackNavButtonClick(Sender: TObject);
  4520. begin
  4521. { Delphi does not support BTNS_WHOLEDROPDOWN so we can't be like VS which
  4522. can have a disabled back nav button with an enabled dropdown. To avoid
  4523. always showing two dropdowns we keep the back button enabled when we need
  4524. the dropdown. So we need to check for this. }
  4525. if FNavStacks.Back.Count = 0 then begin
  4526. Beep;
  4527. Exit;
  4528. end;
  4529. FNavStacks.Forward.Add(FCurrentNavItem);
  4530. var NewNavItem := FNavStacks.Back.ExtractAt(FNavStacks.Back.Count-1);
  4531. UpdateNavButtons;
  4532. FCurrentNavItem := NewNavItem; { Must be done *before* moving }
  4533. MoveCaretAndActivateMemo(NewNavItem.Memo,
  4534. NewNavItem.Memo.GetPositionFromLineColumn(NewNavItem.Line, NewNavItem.Column), False, True, NewNavItem.VirtualSpace);
  4535. end;
  4536. procedure TMainForm.ForwardNavButtonClick(Sender: TObject);
  4537. begin
  4538. FNavStacks.Back.Add(FCurrentNavItem);
  4539. var NewNavItem := FNavStacks.Forward.ExtractAt(FNavStacks.Forward.Count-1);
  4540. UpdateNavButtons;
  4541. FCurrentNavItem := NewNavItem; { Must be done *before* moving }
  4542. MoveCaretAndActivateMemo(NewNavItem.Memo,
  4543. NewNavItem.Memo.GetPositionFromLineColumn(NewNavItem.Line, NewNavItem.Column), False, True, NewNavItem.VirtualSpace);
  4544. end;
  4545. procedure TMainForm.WMAppCommand(var Message: TMessage);
  4546. begin
  4547. var Command := GET_APPCOMMAND_LPARAM(Message.LParam);
  4548. if Command = APPCOMMAND_BROWSER_BACKWARD then begin
  4549. if BackNavButton.Enabled then
  4550. BackNavButton.Click;
  4551. Message.Result := 1;
  4552. end else if Command = APPCOMMAND_BROWSER_FORWARD then begin
  4553. if ForwardNavButton.Enabled then
  4554. ForwardNavButton.Click;
  4555. Message.Result := 1;
  4556. end;
  4557. end;
  4558. procedure TMainForm.NavItemClick(Sender: TObject);
  4559. begin
  4560. var MenuItem := Sender as TMenuItem;
  4561. var Clicks := Abs(MenuItem.Tag);
  4562. if Clicks > 0 then begin
  4563. var ButtonToClick: TToolButton;
  4564. if MenuItem.Tag > 0 then
  4565. ButtonToClick := ForwardNavButton
  4566. else
  4567. ButtonToClick := BackNavButton;
  4568. while Clicks > 0 do begin
  4569. if not ButtonToClick.Enabled then
  4570. raise Exception.Create('not ButtonToClick.Enabled');
  4571. ButtonToClick.Click;
  4572. Dec(Clicks);
  4573. end;
  4574. end;
  4575. end;
  4576. procedure TMainForm.NavPopupMenuClick(Sender: TObject);
  4577. procedure AddNavItemToMenu(const NavItem: TIDEScintEditNavItem; const Checked: Boolean;
  4578. const ClicksNeeded: Integer; const Menu: TMenuItem);
  4579. begin
  4580. if NavItem.Line >= NavItem.Memo.Lines.Count then
  4581. raise Exception.Create('NavItem.Line >= NavItem.Memo.Lines.Count');
  4582. var LineInfo := NavItem.Memo.Lines[NavItem.Line];
  4583. if LineInfo.Trim = '' then
  4584. LineInfo := Format('Line %d', [NavItem.Line+1]);
  4585. var Caption: String;
  4586. if MemosTabSet.Visible then
  4587. Caption := Format('%s: %s', [MemosTabSet.Tabs[MemoToTabIndex(NavItem.Memo)], LineInfo])
  4588. else
  4589. Caption := LineInfo;
  4590. var MenuItem := TMenuItem.Create(Menu);
  4591. MenuItem.Caption := DoubleAmp(Caption);
  4592. MenuItem.Checked := Checked;
  4593. MenuItem.RadioItem := True;
  4594. MenuItem.Tag := ClicksNeeded;
  4595. MenuItem.OnClick := NavItemClick;
  4596. Menu.Add(MenuItem);
  4597. end;
  4598. begin
  4599. var Menu := Sender as TMenuItem;
  4600. Menu.Clear;
  4601. { Setup dropdown. The result should end up being just like Visual Studio 2022
  4602. which means from top to bottom:
  4603. - Furthest (=oldest) forward item
  4604. - ...
  4605. - Closest (=next) forward item
  4606. - Current position in the active memo, checked
  4607. - Closest (=next) back item
  4608. - ...
  4609. - Furthest (=oldest) back item
  4610. The Tag parameter should be set to the amount of clicks needed to get to
  4611. the item, positive for forward and negative for back }
  4612. for var I := 0 to FNavStacks.Forward.Count-1 do
  4613. AddNavItemToMenu(FNavStacks.Forward[I], False, FNavStacks.Forward.Count-I, Menu);
  4614. AddNavItemToMenu(FCurrentNavItem, True, 0, Menu);
  4615. for var I := FNavStacks.Back.Count-1 downto 0 do
  4616. AddNavItemToMenu(FNavStacks.Back[I], False, -(FNavStacks.Back.Count-I), Menu);
  4617. end;
  4618. procedure TMainForm.UpdateCaretPosPanelAndBackNavStack;
  4619. begin
  4620. { Update panel }
  4621. var Text := Format('%4d:%4d', [FActiveMemo.CaretLine + 1,
  4622. FActiveMemo.CaretColumnExpandedForTabs + 1]);
  4623. if FOptions.ShowCaretPosition then begin
  4624. const CaretPos = FActiveMemo.CaretPosition;
  4625. const Style = FActiveMemo.GetStyleAtPosition(CaretPos);
  4626. Text := Format('%s@%d+%d:%s', [Copy(GetEnumName(TypeInfo(TInnoSetupStylerStyle), Style), 3, MaxInt),
  4627. CaretPos, FActiveMemo.CaretVirtualSpace, Text]);
  4628. end;
  4629. StatusBar.Panels[spCaretPos].Text := Text;
  4630. { Update NavStacks.Back if needed and remember new position }
  4631. var NewNavItem := TIDEScintEditNavItem.Create(FActiveMemo); { This is a record so no need to free }
  4632. if FCurrentNavItem.Valid and FNavStacks.AddNewBackForJump(FCurrentNavItem, NewNavItem) then
  4633. UpdateNavButtons;
  4634. FCurrentNavItem := NewNavItem;
  4635. end;
  4636. procedure TMainForm.UpdateEditModePanel;
  4637. const
  4638. InsertText: array[Boolean] of String = ('Overwrite', 'Insert');
  4639. begin
  4640. if FActiveMemo.ReadOnly then
  4641. StatusBar.Panels[spEditMode].Text := 'Read only'
  4642. else
  4643. StatusBar.Panels[spEditMode].Text := InsertText[FActiveMemo.InsertMode];
  4644. end;
  4645. procedure TMainForm.UpdateFindRegExUI;
  4646. const
  4647. FindRegExText: array[Boolean] of String = ('', '.*');
  4648. begin
  4649. StatusBar.Panels[spFindRegEx].Text := FindRegExText[FOptions.FindRegEx];
  4650. if FOptions.FindRegEx then begin
  4651. FindDialog.Options := FindDialog.Options + [frHideWholeWord];
  4652. ReplaceDialog.Options := ReplaceDialog.Options + [frHideWholeWord];
  4653. end else begin
  4654. FindDialog.Options := FindDialog.Options - [frHideWholeWord];
  4655. ReplaceDialog.Options := ReplaceDialog.Options - [frHideWholeWord];
  4656. end;
  4657. end;
  4658. procedure TMainForm.UpdateMemosTabSetVisibility;
  4659. begin
  4660. MemosTabSet.Visible := FPreprocessorOutputMemo.Used or FFileMemos[FirstIncludedFilesMemoIndex].Used;
  4661. if not MemosTabSet.Visible then
  4662. MemosTabSet.TabIndex := 0; { For next time }
  4663. end;
  4664. procedure TMainForm.UpdateModifiedPanel;
  4665. begin
  4666. if FActiveMemo.Modified then
  4667. StatusBar.Panels[spModified].Text := 'Modified'
  4668. else
  4669. StatusBar.Panels[spModified].Text := '';
  4670. end;
  4671. { Set DontUpdateRelatedVisibilty if you're going to call this function again, avoids flicker }
  4672. procedure TMainForm.UpdatePreprocMemos(const DontUpdateRelatedVisibilty: Boolean);
  4673. procedure UpdatePreprocessorOutputMemo(const NewTabs, NewHints: TStringList;
  4674. const NewCloseButtons: TBoolList);
  4675. begin
  4676. if FOptions.ShowPreprocessorOutput and (FPreprocessorOutput <> '') and
  4677. (FMainMemo.Lines.Text.TrimRight <> FPreprocessorOutput) then begin
  4678. NewTabs.Add('Preprocessor Output');
  4679. NewHints.Add('');
  4680. NewCloseButtons.Add(False);
  4681. FPreprocessorOutputMemo.ReadOnly := False;
  4682. try
  4683. FPreprocessorOutputMemo.Lines.Text := FPreprocessorOutput;
  4684. FPreprocessorOutputMemo.ClearUndo;
  4685. finally
  4686. FPreprocessorOutputMemo.ReadOnly := True;
  4687. end;
  4688. FPreprocessorOutputMemo.Used := True;
  4689. end else begin
  4690. if FPreprocessorOutputMemo.Used then
  4691. RemoveMemoFromNav(FPreprocessorOutputMemo);
  4692. FPreprocessorOutputMemo.Used := False;
  4693. FPreprocessorOutputMemo.Visible := False;
  4694. end;
  4695. end;
  4696. procedure UpdateIncludedFilesMemos(const NewTabs, NewHints: TStringList;
  4697. const NewCloseButtons: TBoolList);
  4698. var
  4699. IncludedFile: TIncludedFile;
  4700. I: Integer;
  4701. begin
  4702. if FOptions.OpenIncludedFiles and (FIncludedFiles.Count > 0) then begin
  4703. var NextMemoIndex := FirstIncludedFilesMemoIndex;
  4704. var NextTabIndex := 1; { First tab displays the main memo }
  4705. for IncludedFile in FIncludedFiles do begin
  4706. IncludedFile.Memo := FFileMemos[NextMemoIndex];
  4707. try
  4708. if not IncludedFile.Memo.Used or
  4709. ((PathCompare(IncludedFile.Memo.Filename, IncludedFile.Filename) <> 0) or
  4710. not IncludedFile.HasLastWriteTime or
  4711. (CompareFileTime(IncludedFile.Memo.FileLastWriteTime, IncludedFile.LastWriteTime) <> 0)) then begin
  4712. IncludedFile.Memo.Filename := IncludedFile.Filename;
  4713. IncludedFile.Memo.CompilerFileIndex := IncludedFile.CompilerFileIndex;
  4714. OpenFile(IncludedFile.Memo, IncludedFile.Filename, False); { Also updates FileLastWriteTime }
  4715. IncludedFile.Memo.Used := True;
  4716. end else begin
  4717. { The memo assigned to the included file already has that file loaded
  4718. and is up-to-date so no call to OpenFile is needed. However, it could be
  4719. that CompilerFileIndex is not set yet. This happens if the initial
  4720. load was from the history loaded by LoadKnownIncludedAndHiddenFiles
  4721. and is followed by the user doing a compile. }
  4722. if IncludedFile.Memo.CompilerFileIndex = UnknownCompilerFileIndex then
  4723. IncludedFile.Memo.CompilerFileIndex := IncludedFile.CompilerFileIndex;
  4724. end;
  4725. if FHiddenFiles.IndexOf(IncludedFile.Filename) = -1 then begin
  4726. NewTabs.Insert(NextTabIndex, GetDisplayFilename(IncludedFile.Filename));
  4727. NewHints.Insert(NextTabIndex, GetFileTitle(IncludedFile.Filename));
  4728. NewCloseButtons.Insert(NextTabIndex, True);
  4729. Inc(NextTabIndex);
  4730. end;
  4731. Inc(NextMemoIndex);
  4732. if NextMemoIndex = FFileMemos.Count then
  4733. Break; { We're out of memos :( }
  4734. except on E: Exception do
  4735. begin
  4736. StatusMessage(smkWarning, 'Failed to open included file: ' + E.Message);
  4737. IncludedFile.Memo := nil;
  4738. end;
  4739. end;
  4740. end;
  4741. { Hide any remaining memos }
  4742. for I := NextMemoIndex to FFileMemos.Count-1 do begin
  4743. FFileMemos[I].BreakPoints.Clear;
  4744. if FFileMemos[I].Used then
  4745. RemoveMemoFromNav(FFileMemos[I]);
  4746. FFileMemos[I].Used := False;
  4747. FFileMemos[I].Visible := False;
  4748. end;
  4749. end else begin
  4750. for I := FirstIncludedFilesMemoIndex to FFileMemos.Count-1 do begin
  4751. FFileMemos[I].BreakPoints.Clear;
  4752. if FFileMemos[I].Used then
  4753. RemoveMemoFromNav(FFileMemos[I]);
  4754. FFileMemos[I].Used := False;
  4755. FFileMemos[I].Visible := False;
  4756. end;
  4757. for IncludedFile in FIncludedFiles do
  4758. IncludedFile.Memo := nil;
  4759. end;
  4760. end;
  4761. var
  4762. NewTabs, NewHints: TStringList;
  4763. NewCloseButtons: TBoolList;
  4764. I, SaveTabIndex: Integer;
  4765. SaveTabName: String;
  4766. begin
  4767. NewTabs := nil;
  4768. NewHints := nil;
  4769. NewCloseButtons := nil;
  4770. try
  4771. NewTabs := TStringList.Create;
  4772. NewTabs.Add(MemosTabSet.Tabs[0]); { 'Main Script' }
  4773. NewHints := TStringList.Create;
  4774. NewHints.Add(GetFileTitle(FMainMemo.Filename));
  4775. NewCloseButtons := TBoolList.Create;
  4776. NewCloseButtons.Add(False);
  4777. UpdatePreprocessorOutputMemo(NewTabs, NewHints, NewCloseButtons);
  4778. UpdateIncludedFilesMemos(NewTabs, NewHints, NewCloseButtons);
  4779. { Set new tabs, try keep same file open }
  4780. SaveTabIndex := MemosTabSet.TabIndex;
  4781. SaveTabName := MemosTabSet.Tabs[MemosTabSet.TabIndex];
  4782. MemosTabSet.Tabs := NewTabs;
  4783. MemosTabSet.Hints := NewHints;
  4784. MemosTabSet.CloseButtons := NewCloseButtons;
  4785. I := MemosTabSet.Tabs.IndexOf(SaveTabName);
  4786. if I <> -1 then
  4787. MemosTabSet.TabIndex := I;
  4788. if MemosTabSet.TabIndex = SaveTabIndex then begin
  4789. { If TabIndex stayed the same then the tabset won't perform a Click but we need this to make
  4790. sure the right memo is visible - so trigger it ourselves }
  4791. MemosTabSetClick(MemosTabSet);
  4792. end;
  4793. finally
  4794. NewCloseButtons.Free;
  4795. NewHints.Free;
  4796. NewTabs.Free;
  4797. end;
  4798. if not DontUpdateRelatedVisibilty then begin
  4799. UpdateMemosTabSetVisibility;
  4800. UpdateBevel1Visibility;
  4801. end;
  4802. end;
  4803. procedure TMainForm.MemoUpdateUI(Sender: TObject; Updated: TScintEditUpdates);
  4804. procedure UpdatePendingSquiggly(const AMemo: TIDEScintEdit);
  4805. var
  4806. Pos: Integer;
  4807. Value: Boolean;
  4808. begin
  4809. { Check for the inPendingSquiggly indicator on either side of the caret }
  4810. Pos := AMemo.CaretPosition;
  4811. Value := False;
  4812. if AMemo.CaretVirtualSpace = 0 then begin
  4813. Value := AMemo.GetIndicatorAtPosition(minPendingSquiggly, Pos);
  4814. if not Value and (Pos > 0) then
  4815. Value := AMemo.GetIndicatorAtPosition(minPendingSquiggly, Pos-1);
  4816. end;
  4817. if FOnPendingSquiggly <> Value then begin
  4818. FOnPendingSquiggly := Value;
  4819. { If caret has left a pending squiggly, force restyle of the line }
  4820. if not Value then begin
  4821. { Stop reporting the caret position to the styler (until the next
  4822. Change event) so the token doesn't re-enter pending-squiggly state
  4823. if the caret comes back and something restyles the line }
  4824. AMemo.ReportCaretPositionToStyler := False;
  4825. AMemo.RestyleLine(AMemo.GetLineFromPosition(FPendingSquigglyCaretPos));
  4826. end;
  4827. end;
  4828. FPendingSquigglyCaretPos := Pos;
  4829. end;
  4830. procedure UpdateBraceHighlighting(const AMemo: TIDEScintEdit);
  4831. const
  4832. OpeningBraces: TSysCharSet = ['(', '[', '{', '<'];
  4833. ClosingBraces: TSysCharSet = [')', ']', '}', '>'];
  4834. function HighlightPos(const AMemo: TIDEScintEdit; const CaretPos: Integer;
  4835. const Before: Boolean; const Braces: TSysCharSet): Boolean;
  4836. begin
  4837. var Pos := CaretPos;
  4838. if Before then begin
  4839. if Pos > 0 then
  4840. Pos := AMemo.GetPositionBefore(Pos)
  4841. else
  4842. Exit(False);
  4843. end;
  4844. var C := AMemo.GetByteAtPosition(Pos);
  4845. Result := C in Braces;
  4846. if Result then begin
  4847. var MatchPos := AMemo.GetPositionOfMatchingBrace(Pos);
  4848. if MatchPos >= 0 then
  4849. AMemo.SetBraceHighlighting(Pos, MatchPos)
  4850. else begin
  4851. { Found an unmatched brace: highlight it as bad unless it's an opening
  4852. brace and the caret is at the end of the line }
  4853. var CaretLineEndPos := AMemo.GetLineEndPosition(AMemo.CaretLine);
  4854. if (C in ClosingBraces) or (CaretPos <> CaretLineEndPos) then
  4855. AMemo.SetBraceBadHighlighting(Pos)
  4856. else
  4857. AMemo.SetBraceHighlighting(-1, -1);
  4858. end;
  4859. end;
  4860. end;
  4861. begin
  4862. var Highlighted := False;
  4863. var Section := FMemosStyler.GetSectionFromLineState(AMemo.Lines.State[AMemo.CaretLine]);
  4864. if (Section <> scNone) and (AMemo.CaretVirtualSpace = 0) then begin
  4865. var Pos := AMemo.CaretPosition;
  4866. Highlighted := Highlighted or HighlightPos(AMemo, Pos, False, OpeningBraces);
  4867. Highlighted := Highlighted or HighlightPos(AMemo, Pos, False, ClosingBraces);
  4868. Highlighted := Highlighted or HighlightPos(AMemo, Pos, True, ClosingBraces);
  4869. Highlighted := Highlighted or HighlightPos(AMemo, Pos, True, OpeningBraces);
  4870. end;
  4871. if not Highlighted then
  4872. AMemo.SetBraceHighlighting(-1, -1);
  4873. end;
  4874. begin
  4875. if Updated * [suContent, suSelection] = [] then
  4876. Exit;
  4877. var Memo := Sender as TIDEScintEdit;
  4878. if (Memo = FErrorMemo) and ((FErrorMemo.ErrorLine < 0) or (FErrorMemo.CaretPosition <> FErrorMemo.ErrorCaretPosition)) then
  4879. HideError;
  4880. if Memo = FActiveMemo then begin
  4881. UpdateCaretPosPanelAndBackNavStack;
  4882. UpdateEditModePanel;
  4883. end;
  4884. UpdatePendingSquiggly(Memo);
  4885. UpdateBraceHighlighting(Memo);
  4886. UpdateOccurrenceIndicators(Memo);
  4887. end;
  4888. procedure TMainForm.MemoModifiedChange(Sender: TObject);
  4889. begin
  4890. if Sender = FActiveMemo then
  4891. UpdateModifiedPanel;
  4892. end;
  4893. procedure TMainForm.MemoCallTipArrowClick(Sender: TObject;
  4894. const Up: Boolean);
  4895. begin
  4896. { Based on SciTE 5.50's SciTEBase::Notify SA::Notification::CallTipClick }
  4897. if Up and (FCallTipState.CurrentCallTip > 0) then begin
  4898. Dec(FCallTipState.CurrentCallTip);
  4899. UpdateCallTipFunctionDefinition;
  4900. end else if not Up and (FCallTipState.CurrentCallTip + 1 < FCallTipState.MaxCallTips) then begin
  4901. Inc(FCallTipState.CurrentCallTip);
  4902. UpdateCallTipFunctionDefinition;
  4903. end;
  4904. end;
  4905. procedure TMainForm.MemoChange(Sender: TObject; const Info: TScintEditChangeInfo);
  4906. procedure MemoLinesInsertedOrDeleted(Memo: TIDEScintFileEdit);
  4907. var
  4908. FirstAffectedLine, Line, LinePos: Integer;
  4909. begin
  4910. Line := Memo.GetLineFromPosition(Info.StartPos);
  4911. LinePos := Memo.GetPositionFromLine(Line);
  4912. FirstAffectedLine := Line;
  4913. { If the deletion/insertion does not start on the first character of Line,
  4914. then we consider the first deleted/inserted line to be the following
  4915. line (Line+1). This way, if you press Del at the end of line 1, the dot
  4916. on line 2 is removed, while line 1's dot stays intact. }
  4917. if Info.StartPos > LinePos then
  4918. Inc(Line);
  4919. if Info.LinesDelta > 0 then
  4920. MemoLinesInserted(Memo, Line, Info.LinesDelta)
  4921. else
  4922. MemoLinesDeleted(Memo, Line, -Info.LinesDelta, FirstAffectedLine);
  4923. end;
  4924. var
  4925. Memo: TIDEScintFileEdit;
  4926. begin
  4927. if not (Sender is TIDEScintFileEdit) then
  4928. Exit;
  4929. Memo := TIDEScintFileEdit(Sender);
  4930. if Memo.OpeningFile then
  4931. Exit;
  4932. FModifiedAnySinceLastCompile := True;
  4933. if FDebugging then
  4934. FModifiedAnySinceLastCompileAndGo := True
  4935. else begin
  4936. { Modified while not debugging or opening a file; free the debug info and clear the dots }
  4937. DestroyDebugInfo;
  4938. end;
  4939. if Info.LinesDelta <> 0 then
  4940. MemoLinesInsertedOrDeleted(Memo);
  4941. if Memo = FErrorMemo then begin
  4942. { When the Delete key is pressed, the caret doesn't move, so reset
  4943. FErrorCaretPosition to ensure that OnUpdateUI calls HideError }
  4944. FErrorMemo.ErrorCaretPosition := -1;
  4945. end;
  4946. { The change should trigger restyling. Allow the styler to see the current
  4947. caret position in case it wants to set a pending squiggly indicator. }
  4948. Memo.ReportCaretPositionToStyler := True;
  4949. end;
  4950. function TMainForm.InitiateAutoCompleteOrCallTipAllowedAtPos(const AMemo: TIDEScintEdit;
  4951. const WordStartLinePos, PositionBeforeWordStartPos: Integer): Boolean;
  4952. begin
  4953. Result := (PositionBeforeWordStartPos < WordStartLinePos) or
  4954. not FMemosStyler.IsCommentOrPascalStringStyle(AMemo.GetStyleAtPosition(PositionBeforeWordStartPos));
  4955. end;
  4956. procedure TMainForm.InitiateAutoComplete(const Key: AnsiChar);
  4957. function OnlyWhiteSpaceBeforeWord(const Memo: TIDEScintEdit; const LinePos, WordStartPos: Integer): Boolean;
  4958. var
  4959. I: Integer;
  4960. C: AnsiChar;
  4961. begin
  4962. { Only allow autocompletion if no non-whitespace characters exist before the current word on the line }
  4963. I := WordStartPos;
  4964. Result := False;
  4965. while I > LinePos do begin
  4966. I := FActiveMemo.GetPositionBefore(I);
  4967. if I < LinePos then
  4968. Exit; { shouldn't get here }
  4969. C := FActiveMemo.GetByteAtPosition(I);
  4970. if C > ' ' then
  4971. Exit;
  4972. end;
  4973. Result := True;
  4974. end;
  4975. var
  4976. CaretPos, Line, LinePos, WordStartPos, WordEndPos, CharsBefore,
  4977. LangNamePos: Integer;
  4978. Section: TInnoSetupStylerSection;
  4979. IsParamSection: Boolean;
  4980. WordList: AnsiString;
  4981. FoundSemicolon, FoundFlagsOrType, FoundDot: Boolean;
  4982. C: AnsiChar;
  4983. begin
  4984. if FActiveMemo.AutoCompleteActive or FActiveMemo.ReadOnly then
  4985. Exit;
  4986. if Key = #0 then begin
  4987. { If a character is typed then Scintilla will handle selections but
  4988. otherwise we should empty them and also make sure the caret is visible
  4989. before we start autocompletion }
  4990. FActiveMemo.SetEmptySelections;
  4991. FActiveMemo.ScrollCaretIntoView;
  4992. end;
  4993. CaretPos := FActiveMemo.CaretPosition;
  4994. Line := FActiveMemo.GetLineFromPosition(CaretPos);
  4995. LinePos := FActiveMemo.GetPositionFromLine(Line);
  4996. WordStartPos := FActiveMemo.GetWordStartPosition(CaretPos, True);
  4997. WordEndPos := FActiveMemo.GetWordEndPosition(CaretPos, True);
  4998. CharsBefore := CaretPos - WordStartPos;
  4999. { Don't auto start autocompletion after a character is typed if there are any
  5000. word characters adjacent to the character }
  5001. if Key <> #0 then begin
  5002. if CharsBefore > 1 then
  5003. Exit;
  5004. if WordEndPos > CaretPos then
  5005. Exit;
  5006. end;
  5007. case FActiveMemo.GetByteAtPosition(WordStartPos) of
  5008. '#':
  5009. begin
  5010. if not OnlyWhiteSpaceBeforeWord(FActiveMemo, LinePos, WordStartPos) then
  5011. Exit;
  5012. WordList := FMemosStyler.ISPPDirectivesWordList;
  5013. FActiveMemo.SetAutoCompleteFillupChars(' ');
  5014. end;
  5015. '{':
  5016. begin
  5017. WordList := FMemosStyler.ConstantsWordList;
  5018. FActiveMemo.SetAutoCompleteFillupChars('\:');
  5019. end;
  5020. '[':
  5021. begin
  5022. if not OnlyWhiteSpaceBeforeWord(FActiveMemo, LinePos, WordStartPos) then
  5023. Exit;
  5024. WordList := FMemosStyler.SectionsWordList;
  5025. FActiveMemo.SetAutoCompleteFillupChars('');
  5026. end;
  5027. else
  5028. begin
  5029. Section := FMemosStyler.GetSectionFromLineState(FActiveMemo.Lines.State[Line]);
  5030. if Section = scCode then begin
  5031. { Space can only initiate autocompletion after non whitespace }
  5032. if (Key = ' ') and OnlyWhiteSpaceBeforeWord(FActiveMemo, LinePos, WordStartPos) then
  5033. Exit;
  5034. var PositionBeforeWordStartPos := FActiveMemo.GetPositionBefore(WordStartPos);
  5035. if Key <> #0 then begin
  5036. FActiveMemo.StyleNeeded(PositionBeforeWordStartPos); { Make sure the typed character has been styled }
  5037. if not InitiateAutoCompleteOrCallTipAllowedAtPos(FActiveMemo, LinePos, PositionBeforeWordStartPos) then
  5038. Exit;
  5039. end;
  5040. WordList := '';
  5041. { Autocomplete event functions if the current word on the line has
  5042. exactly 1 space before it which has the word 'function' or
  5043. 'procedure' before it which has only whitespace before it }
  5044. if (PositionBeforeWordStartPos >= LinePos) and (FActiveMemo.GetByteAtPosition(PositionBeforeWordStartPos) <= ' ') then begin
  5045. var FunctionWordEndPos := PositionBeforeWordStartPos;
  5046. var FunctionWordStartPos := FActiveMemo.GetWordStartPosition(FunctionWordEndPos, True);
  5047. if OnlyWhiteSpaceBeforeWord(FActiveMemo, LinePos, FunctionWordStartPos) then begin
  5048. var FunctionWord := FActiveMemo.GetTextRange(FunctionWordStartPos, FunctionWordEndPos);
  5049. if SameText(FunctionWord, 'procedure') then
  5050. WordList := FMemosStyler.EventFunctionsWordList[True]
  5051. else if SameText(FunctionWord, 'function') then
  5052. WordList := FMemosStyler.EventFunctionsWordList[False];
  5053. if WordList <> '' then
  5054. FActiveMemo.SetAutoCompleteFillupChars('');
  5055. end;
  5056. end;
  5057. { If no event function was found then autocomplete script functions,
  5058. types, etc if the current word has no dot before it }
  5059. if WordList = '' then begin
  5060. var ClassOrRecordMember := (PositionBeforeWordStartPos >= LinePos) and (FActiveMemo.GetByteAtPosition(PositionBeforeWordStartPos) = '.');
  5061. WordList := FMemosStyler.ScriptWordList[ClassOrRecordMember];
  5062. FActiveMemo.SetAutoCompleteFillupChars('');
  5063. end;
  5064. if WordList = '' then
  5065. Exit;
  5066. end else begin
  5067. IsParamSection := FMemosStyler.IsParamSection(Section);
  5068. { Autocomplete if the current word on the line has only whitespace
  5069. before it, or else also: after the last ';' or after 'Flags:' or
  5070. 'Type:' in parameterized sections }
  5071. FoundSemicolon := False;
  5072. FoundFlagsOrType := False;
  5073. FoundDot := False;
  5074. var I := WordStartPos;
  5075. while I > LinePos do begin
  5076. I := FActiveMemo.GetPositionBefore(I);
  5077. if I < LinePos then
  5078. Exit; { shouldn't get here }
  5079. C := FActiveMemo.GetByteAtPosition(I);
  5080. if IsParamSection and (C in [';', ':']) and
  5081. FMemosStyler.IsSymbolStyle(FActiveMemo.GetStyleAtPosition(I)) then begin { Make sure it's an stSymbol ';' or ':' and not one inside a quoted string }
  5082. FoundSemicolon := C = ';';
  5083. if not FoundSemicolon then begin
  5084. var ParameterWordEndPos := I;
  5085. var ParameterWordStartPos := FActiveMemo.GetWordStartPosition(ParameterWordEndPos, True);
  5086. var ParameterWord := FActiveMemo.GetTextRange(ParameterWordStartPos, ParameterWordEndPos);
  5087. FoundFlagsOrType := SameText(ParameterWord, 'Flags') or
  5088. ((Section in [scInstallDelete, scUninstallDelete]) and SameText(ParameterWord, 'Type'));
  5089. end else
  5090. FoundFlagsOrType := False;
  5091. if FoundSemicolon or FoundFlagsOrType then
  5092. Break;
  5093. end;
  5094. if (Section = scLangOptions) and (C = '.') and not FoundDot then begin
  5095. { Verify that a word (language name) precedes the '.', then check for
  5096. any non-whitespace characters before the word }
  5097. LangNamePos := FActiveMemo.GetWordStartPosition(I, True);
  5098. if LangNamePos >= I then
  5099. Exit;
  5100. I := LangNamePos;
  5101. FoundDot := True;
  5102. end else if C > ' ' then begin
  5103. if IsParamSection and not (Section in [scInstallDelete, scUninstallDelete]) and
  5104. (FMemosStyler.FlagsWordList[Section] <> '') then begin
  5105. { Verify word before the current word (or before that when we get here again) is
  5106. a valid flag and if so, continue looking before it instead of stopping }
  5107. var FlagEndPos := FActiveMemo.GetWordEndPosition(I, True);
  5108. var FlagStartPos := FActiveMemo.GetWordStartPosition(I, True);
  5109. var FlagWord := FActiveMemo.GetTextRange(FlagStartPos, FlagEndPos);
  5110. if FMemosStyler.SectionHasFlag(Section, FlagWord) then
  5111. I := FlagStartPos
  5112. else
  5113. Exit;
  5114. end else
  5115. Exit;
  5116. end;
  5117. end;
  5118. { Space can only initiate autocompletion after ';' or 'Flags:' or 'Type:' in parameterized sections }
  5119. if (Key = ' ') and not (FoundSemicolon or FoundFlagsOrType) then
  5120. Exit;
  5121. if FoundFlagsOrType then begin
  5122. WordList := FMemosStyler.FlagsWordList[Section];
  5123. if WordList = '' then
  5124. Exit;
  5125. FActiveMemo.SetAutoCompleteFillupChars(' ');
  5126. end else begin
  5127. WordList := FMemosStyler.KeywordsWordList[Section];
  5128. if WordList = '' then { CustomMessages }
  5129. Exit;
  5130. if IsParamSection then
  5131. FActiveMemo.SetAutoCompleteFillupChars(':')
  5132. else
  5133. FActiveMemo.SetAutoCompleteFillupChars('=');
  5134. end;
  5135. end;
  5136. end;
  5137. end;
  5138. FActiveMemo.ShowAutoComplete(CharsBefore, WordList);
  5139. end;
  5140. procedure TMainForm.UpdateCallTipFunctionDefinition(const Pos: Integer { = -1 });
  5141. begin
  5142. { Based on SciTE 5.50's SciTEBase::FillFunctionDefinition }
  5143. if Pos > 0 then
  5144. FCallTipState.LastPosCallTip := Pos;
  5145. // Should get current api definition
  5146. var FunctionDefinition := FMemosStyler.GetScriptFunctionDefinition(FCallTipState.ClassOrRecordMember, FCallTipState.CurrentCallTipWord, FCallTipState.CurrentCallTip, FCallTipState.MaxCallTips);
  5147. if ((FCallTipState.MaxCallTips = 1) and FunctionDefinition.HasParams) or //if there's a single definition then only show if it has a parameter
  5148. (FCallTipState.MaxCallTips > 1) then begin //if there's multiple then show always just like MemoHintShow, so even the one without parameters if it exists
  5149. FCallTipState.FunctionDefinition := FunctionDefinition.ScriptFuncWithoutHeader;
  5150. if FCallTipState.MaxCallTips > 1 then
  5151. FCallTipState.FunctionDefinition := AnsiString(Format(#1'%d of %d'#2'%s', [FCallTipState.CurrentCallTip+1, FCallTipState.MaxCallTips, FCallTipState.FunctionDefinition]));
  5152. FActiveMemo.ShowCallTip(FCallTipState.LastPosCallTip - Length(FCallTipState.CurrentCallTipWord), FCallTipState.FunctionDefinition);
  5153. ContinueCallTip;
  5154. end;
  5155. end;
  5156. procedure TMainForm.InitiateCallTip(const Key: AnsiChar);
  5157. begin
  5158. var Pos := FActiveMemo.CaretPosition;
  5159. if (FMemosStyler.GetSectionFromLineState(FActiveMemo.Lines.State[FActiveMemo.GetLineFromPosition(Pos)]) <> scCode) or
  5160. ((Key <> #0) and not InitiateAutoCompleteOrCallTipAllowedAtPos(FActiveMemo,
  5161. FActiveMemo.GetPositionFromLine(FActiveMemo.GetLineFromPosition(Pos)),
  5162. FActiveMemo.GetPositionBefore(Pos))) then
  5163. Exit;
  5164. { Based on SciTE 5.50's SciTEBase::StartAutoComplete }
  5165. FCallTipState.CurrentCallTip := 0;
  5166. FCallTipState.CurrentCallTipWord := '';
  5167. var Line := FActiveMemo.CaretLineText;
  5168. var Current := FActiveMemo.CaretPositionInLine;
  5169. var CallTipWordCharacters := FActiveMemo.WordCharsAsSet;
  5170. {$ZEROBASEDSTRINGS ON}
  5171. repeat
  5172. var Braces := 0;
  5173. while ((Current > 0) and ((Braces <> 0) or not (Line[Current-1] = '('))) do begin
  5174. if Line[Current-1] = '(' then
  5175. Dec(Braces)
  5176. else if Line[Current-1] = ')' then
  5177. Inc(Braces);
  5178. Dec(Current);
  5179. Dec(Pos);
  5180. end;
  5181. if Current > 0 then begin
  5182. Dec(Current);
  5183. Dec(Pos);
  5184. end else
  5185. Break;
  5186. while (Current > 0) and (Line[Current-1] <= ' ') do begin
  5187. Dec(Current);
  5188. Dec(Pos);
  5189. end
  5190. until not ((Current > 0) and not CharInSet(Line[Current-1], CallTipWordCharacters));
  5191. {$ZEROBASEDSTRINGS OFF}
  5192. if Current <= 0 then
  5193. Exit;
  5194. FCallTipState.StartCallTipWord := Current - 1;
  5195. {$ZEROBASEDSTRINGS ON}
  5196. while (FCallTipState.StartCallTipWord > 0) and CharInSet(Line[FCallTipState.StartCallTipWord-1], CallTipWordCharacters) do
  5197. Dec(FCallTipState.StartCallTipWord);
  5198. FCallTipState.ClassOrRecordMember := (FCallTipState.StartCallTipWord > 0) and (Line[FCallTipState.StartCallTipWord-1] = '.');
  5199. {$ZEROBASEDSTRINGS OFF}
  5200. SetLength(Line, Current);
  5201. FCallTipState.CurrentCallTipWord := Line.Substring(FCallTipState.StartCallTipWord); { Substring is zero-based }
  5202. FCallTipState.FunctionDefinition := '';
  5203. UpdateCallTipFunctionDefinition(Pos);
  5204. end;
  5205. procedure TMainForm.ContinueCallTip;
  5206. begin
  5207. { Based on SciTE 5.50's SciTEBase::ContinueCallTip }
  5208. var Line := FActiveMemo.CaretLineText;
  5209. var Current := FActiveMemo.CaretPositionInLine;
  5210. var Braces := 0;
  5211. var Commas := 0;
  5212. for var I := FCallTipState.StartCallTipWord to Current-1 do begin
  5213. {$ZEROBASEDSTRINGS ON}
  5214. if CharInSet(Line[I], ['(', '[']) then
  5215. Inc(Braces)
  5216. else if CharInSet(Line[I], [')', ']']) and (Braces > 0) then
  5217. Dec(Braces)
  5218. else if (Braces = 1) and (Line[I] = ',') then
  5219. Inc(Commas);
  5220. {$ZEROBASEDSTRINGS OFF}
  5221. end;
  5222. {$ZEROBASEDSTRINGS ON}
  5223. var StartHighlight := 0;
  5224. var FunctionDefinition := FCallTipState.FunctionDefinition;
  5225. var FunctionDefinitionLength := Length(FunctionDefinition);
  5226. while (StartHighlight < FunctionDefinitionLength) and not (FunctionDefinition[StartHighlight] = '(') do
  5227. Inc(StartHighlight);
  5228. if (StartHighlight < FunctionDefinitionLength) and (FunctionDefinition[StartHighlight] = '(') then
  5229. Inc(StartHighlight);
  5230. while (StartHighlight < FunctionDefinitionLength) and (Commas > 0) do begin
  5231. if FunctionDefinition[StartHighlight] in [',', ';'] then
  5232. Dec(Commas);
  5233. // If it reached the end of the argument list it means that the user typed in more
  5234. // arguments than the ones listed in the calltip
  5235. if FunctionDefinition[StartHighlight] = ')' then
  5236. Commas := 0
  5237. else
  5238. Inc(StartHighlight);
  5239. end;
  5240. if (StartHighlight < FunctionDefinitionLength) and (FunctionDefinition[StartHighlight] in [',', ';']) then
  5241. Inc(StartHighlight);
  5242. var EndHighlight := StartHighlight;
  5243. while (EndHighlight < FunctionDefinitionLength) and not (FunctionDefinition[EndHighlight] in [',', ';']) and not (FunctionDefinition[EndHighlight] = ')') do
  5244. Inc(EndHighlight);
  5245. {$ZEROBASEDSTRINGS OFF}
  5246. FActiveMemo.SetCallTipHighlight(StartHighlight, EndHighlight);
  5247. end;
  5248. procedure TMainForm.MemoCharAdded(Sender: TObject; Ch: AnsiChar);
  5249. function LineIsBlank(const Line: Integer): Boolean;
  5250. begin
  5251. var S := FActiveMemo.Lines.RawLines[Line];
  5252. Result := TScintEdit.RawStringIsBlank(S);
  5253. end;
  5254. var
  5255. NewLine, PreviousLine, NewIndent, PreviousIndent: Integer;
  5256. begin
  5257. if FOptions.AutoIndent and (Ch = FActiveMemo.LineEndingString[Length(FActiveMemo.LineEndingString)]) then begin
  5258. { Add to the new line any (remaining) indentation from the previous line }
  5259. NewLine := FActiveMemo.CaretLine;
  5260. PreviousLine := NewLine-1;
  5261. if PreviousLine >= 0 then begin
  5262. NewIndent := FActiveMemo.GetLineIndentation(NewLine);
  5263. { If no indentation was moved from the previous line to the new line
  5264. (i.e., there are no spaces/tabs directly to the right of the new
  5265. caret position), and the previous line is completely empty (0 length),
  5266. then use the indentation from the last line containing non-space
  5267. characters. }
  5268. if (NewIndent = 0) and (FActiveMemo.Lines.RawLineLengths[PreviousLine] = 0) then begin
  5269. Dec(PreviousLine);
  5270. while (PreviousLine >= 0) and LineIsBlank(PreviousLine) do
  5271. Dec(PreviousLine);
  5272. end;
  5273. if PreviousLine >= 0 then begin
  5274. PreviousIndent := FActiveMemo.GetLineIndentation(PreviousLine);
  5275. FActiveMemo.SetLineIndentation(NewLine, NewIndent + PreviousIndent);
  5276. FActiveMemo.CaretPosition := FActiveMemo.GetPositionFromLineExpandedColumn(NewLine,
  5277. PreviousIndent);
  5278. end;
  5279. end;
  5280. end;
  5281. { Based on SciTE 5.50's SciTEBase::CharAdded but with an altered interaction
  5282. between calltips and autocomplete }
  5283. var DoAutoComplete := False;
  5284. if FActiveMemo.CallTipActive then begin
  5285. if Ch = ')' then begin
  5286. Dec(FCallTipState.BraceCount);
  5287. if FCallTipState.BraceCount < 1 then
  5288. FActiveMemo.CancelCallTip
  5289. else if FOptions.AutoCallTips then
  5290. InitiateCallTip(Ch);
  5291. end else if Ch = '(' then begin
  5292. Inc(FCallTipState.BraceCount);
  5293. if FOptions.AutoCallTips then
  5294. InitiateCallTip(Ch);
  5295. end else
  5296. ContinueCallTip;
  5297. end else if FActiveMemo.AutoCompleteActive then begin
  5298. if Ch = '(' then begin
  5299. Inc(FCallTipState.BraceCount);
  5300. if FOptions.AutoCallTips then begin
  5301. InitiateCallTip(Ch);
  5302. if not FActiveMemo.CallTipActive then begin
  5303. { Normally the calltip activation means any active autocompletion gets
  5304. cancelled by Scintilla but if the current word has no call tip then
  5305. we should make sure ourselves that the added brace still cancels
  5306. the currently active autocompletion }
  5307. DoAutoComplete := True;
  5308. end;
  5309. end;
  5310. end else if Ch = ')' then
  5311. Dec(FCallTipState.BraceCount)
  5312. else
  5313. DoAutoComplete := True;
  5314. end else if Ch = '(' then begin
  5315. FCallTipState.BraceCount := 1;
  5316. if FOptions.AutoCallTips then
  5317. InitiateCallTip(Ch);
  5318. end else
  5319. DoAutoComplete := True;
  5320. if DoAutoComplete then begin
  5321. case Ch of
  5322. 'A'..'Z', 'a'..'z', '_', '#', '{', '[', '<', '0'..'9':
  5323. if not FActiveMemo.AutoCompleteActive and FOptions.AutoAutoComplete and not (Ch in ['0'..'9']) then
  5324. InitiateAutoComplete(Ch);
  5325. else
  5326. var RestartAutoComplete := (Ch in [' ', '.']) and
  5327. (FOptions.AutoAutoComplete or FActiveMemo.AutoCompleteActive);
  5328. FActiveMemo.CancelAutoComplete;
  5329. if RestartAutoComplete then
  5330. InitiateAutoComplete(Ch);
  5331. end;
  5332. end;
  5333. end;
  5334. procedure TMainForm.MemoHintShow(Sender: TObject; var Info: TScintHintInfo);
  5335. function GetCodeVariableDebugEntryFromFileLineCol(FileIndex, Line, Col: Integer; out DebugEntry: PVariableDebugEntry): Boolean;
  5336. var
  5337. I: Integer;
  5338. begin
  5339. { FVariableDebugEntries uses 1-based line and column numbers }
  5340. Inc(Line);
  5341. Inc(Col);
  5342. Result := False;
  5343. for I := 0 to FVariableDebugEntriesCount-1 do begin
  5344. if (FVariableDebugEntries[I].FileIndex = FileIndex) and
  5345. (FVariableDebugEntries[I].LineNumber = Line) and
  5346. (FVariableDebugEntries[I].Col = Col) then begin
  5347. DebugEntry := @FVariableDebugEntries[I];
  5348. Result := True;
  5349. Break;
  5350. end;
  5351. end;
  5352. end;
  5353. function GetCodeColumnFromPosition(const Pos: Integer): Integer;
  5354. var
  5355. LinePos: Integer;
  5356. S: TScintRawString;
  5357. U: String;
  5358. begin
  5359. { [Code] lines get converted from the editor's UTF-8 to UTF-16 Strings when
  5360. passed to the compiler. This can lead to column number discrepancies
  5361. between Scintilla and ROPS. This code simulates the conversion to try to
  5362. find out where ROPS thinks a Pos resides. }
  5363. LinePos := FActiveMemo.GetPositionFromLine(FActiveMemo.GetLineFromPosition(Pos));
  5364. S := FActiveMemo.GetRawTextRange(LinePos, Pos);
  5365. U := FActiveMemo.ConvertRawStringToString(S);
  5366. Result := Length(U);
  5367. end;
  5368. function FindVarOrFuncRange(const Pos: Integer): TScintRange;
  5369. begin
  5370. { Note: The GetPositionAfter is needed so that when the mouse is over a '.'
  5371. between two words, it won't match the word to the left of the '.' }
  5372. FActiveMemo.SetDefaultWordChars;
  5373. Result.StartPos := FActiveMemo.GetWordStartPosition(FActiveMemo.GetPositionAfter(Pos), True);
  5374. Result.EndPos := FActiveMemo.GetWordEndPosition(Pos, True);
  5375. end;
  5376. function FindConstRange(const Pos: Integer): TScintRange;
  5377. var
  5378. BraceLevel, ConstStartPos, Line, LineEndPos, I: Integer;
  5379. C: AnsiChar;
  5380. begin
  5381. Result.StartPos := 0;
  5382. Result.EndPos := 0;
  5383. BraceLevel := 0;
  5384. ConstStartPos := -1;
  5385. Line := FActiveMemo.GetLineFromPosition(Pos);
  5386. LineEndPos := FActiveMemo.GetLineEndPosition(Line);
  5387. I := FActiveMemo.GetPositionFromLine(Line);
  5388. while I < LineEndPos do begin
  5389. if (I > Pos) and (BraceLevel = 0) then
  5390. Break;
  5391. C := FActiveMemo.GetByteAtPosition(I);
  5392. if C = '{' then begin
  5393. if FActiveMemo.GetByteAtPosition(I + 1) = '{' then
  5394. Inc(I)
  5395. else begin
  5396. if BraceLevel = 0 then
  5397. ConstStartPos := I;
  5398. Inc(BraceLevel);
  5399. end;
  5400. end
  5401. else if (C = '}') and (BraceLevel > 0) then begin
  5402. Dec(BraceLevel);
  5403. if (BraceLevel = 0) and (ConstStartPos <> -1) then begin
  5404. if (Pos >= ConstStartPos) and (Pos <= I) then begin
  5405. Result.StartPos := ConstStartPos;
  5406. Result.EndPos := I + 1;
  5407. Exit;
  5408. end;
  5409. ConstStartPos := -1;
  5410. end;
  5411. end;
  5412. I := FActiveMemo.GetPositionAfter(I);
  5413. end;
  5414. end;
  5415. procedure UpdateInfo(var Info: TScintHintInfo; const HintStr: String; const Range: TScintRange; const Memo: TIDEScintEdit);
  5416. begin
  5417. Info.HintStr := HintStr;
  5418. Info.CursorRect.TopLeft := Memo.GetPointFromPosition(Range.StartPos);
  5419. Info.CursorRect.BottomRight := Memo.GetPointFromPosition(Range.EndPos);
  5420. Info.CursorRect.Bottom := Info.CursorRect.Top + Memo.LineHeight;
  5421. Info.HideTimeout := High(Integer); { infinite }
  5422. end;
  5423. begin
  5424. var Pos := FActiveMemo.GetPositionFromPoint(Info.CursorPos, True, True);
  5425. if Pos < 0 then
  5426. Exit;
  5427. var Line := FActiveMemo.GetLineFromPosition(Pos);
  5428. { Check if cursor is over a [Code] variable or function }
  5429. if FMemosStyler.GetSectionFromLineState(FActiveMemo.Lines.State[Line]) = scCode then begin
  5430. var VarOrFuncRange := FindVarOrFuncRange(Pos);
  5431. if VarOrFuncRange.EndPos > VarOrFuncRange.StartPos then begin
  5432. var HintStr := '';
  5433. var DebugEntry: PVariableDebugEntry;
  5434. if (FActiveMemo is TIDEScintFileEdit) and (FDebugClientWnd <> 0) and
  5435. GetCodeVariableDebugEntryFromFileLineCol((FActiveMemo as TIDEScintFileEdit).CompilerFileIndex,
  5436. Line, GetCodeColumnFromPosition(VarOrFuncRange.StartPos), DebugEntry) then begin
  5437. var Output: String;
  5438. case EvaluateVariableEntry(DebugEntry, Output) of
  5439. 1: HintStr := Output;
  5440. 2: HintStr := Output;
  5441. else
  5442. HintStr := 'Unknown error';
  5443. end;
  5444. end else begin
  5445. var ClassMember := False;
  5446. var Name := FActiveMemo.GetTextRange(VarOrFuncRange.StartPos, VarOrFuncRange.EndPos);
  5447. var Index := 0;
  5448. var Count: Integer;
  5449. var FunctionDefinition := FMemosStyler.GetScriptFunctionDefinition(ClassMember, Name, Index, Count);
  5450. if Count = 0 then begin
  5451. ClassMember := not ClassMember;
  5452. FunctionDefinition := FMemosStyler.GetScriptFunctionDefinition(ClassMember, Name, Index, Count);
  5453. end;
  5454. while Index < Count do begin
  5455. if Index <> 0 then
  5456. FunctionDefinition := FMemosStyler.GetScriptFunctionDefinition(ClassMember, Name, Index);
  5457. if HintStr <> '' then
  5458. HintStr := HintStr + #13;
  5459. if FunctionDefinition.WasFunction then
  5460. HintStr := HintStr + 'function '
  5461. else
  5462. HintStr := HintStr + 'procedure ';
  5463. HintStr := HintStr + String(FunctionDefinition.ScriptFuncWithoutHeader);
  5464. Inc(Index);
  5465. end;
  5466. end;
  5467. if HintStr <> '' then begin
  5468. UpdateInfo(Info, HintStr, VarOrFuncRange, FActiveMemo);
  5469. Exit;
  5470. end;
  5471. end;
  5472. end;
  5473. if FDebugClientWnd <> 0 then begin
  5474. { Check if cursor is over a constant }
  5475. var ConstRange := FindConstRange(Pos);
  5476. if ConstRange.EndPos > ConstRange.StartPos then begin
  5477. var HintStr := FActiveMemo.GetTextRange(ConstRange.StartPos, ConstRange.EndPos);
  5478. var Output: String;
  5479. case EvaluateConstant(Info.HintStr, Output) of
  5480. 1: HintStr := HintStr + ' = "' + Output + '"';
  5481. 2: HintStr := HintStr + ' = Exception: ' + Output;
  5482. else
  5483. HintStr := HintStr + ' = Unknown error';
  5484. end;
  5485. UpdateInfo(Info, HintStr, ConstRange, FActiveMemo);
  5486. end;
  5487. end;
  5488. end;
  5489. procedure TMainForm.MainMemoDropFiles(Sender: TObject; X, Y: Integer;
  5490. AFiles: TStrings);
  5491. begin
  5492. if (AFiles.Count > 0) and ConfirmCloseFile(True) then
  5493. OpenFile(FMainMemo, AFiles[0], True);
  5494. end;
  5495. procedure TMainForm.MemoZoom(Sender: TObject);
  5496. begin
  5497. if not FSynchingZoom then begin
  5498. FSynchingZoom := True;
  5499. try
  5500. for var Memo in FMemos do
  5501. if Memo <> Sender then
  5502. Memo.Zoom := (Sender as TScintEdit).Zoom;
  5503. finally
  5504. FSynchingZoom := False;
  5505. end;
  5506. end;
  5507. end;
  5508. procedure TMainForm.StatusBarResize(Sender: TObject);
  5509. begin
  5510. { Without this, on Windows XP with themes, the status bar's size grip gets
  5511. corrupted as the form is resized }
  5512. if StatusBar.HandleAllocated then
  5513. InvalidateRect(StatusBar.Handle, nil, True);
  5514. end;
  5515. procedure TMainForm.WMDebuggerQueryVersion(var Message: TMessage);
  5516. begin
  5517. Message.Result := FCompilerVersion.BinVersion;
  5518. end;
  5519. procedure TMainForm.WMDebuggerHello(var Message: TMessage);
  5520. var
  5521. PID: DWORD;
  5522. WantCodeText: Boolean;
  5523. begin
  5524. FDebugClientWnd := HWND(Message.WParam);
  5525. { Save debug client process handle }
  5526. if FDebugClientProcessHandle <> 0 then begin
  5527. { Shouldn't get here, but just in case, don't leak a handle }
  5528. CloseHandle(FDebugClientProcessHandle);
  5529. FDebugClientProcessHandle := 0;
  5530. end;
  5531. PID := 0;
  5532. if GetWindowThreadProcessId(FDebugClientWnd, @PID) <> 0 then
  5533. FDebugClientProcessHandle := OpenProcess(SYNCHRONIZE or PROCESS_TERMINATE,
  5534. False, PID);
  5535. WantCodeText := Bool(Message.LParam);
  5536. if WantCodeText then
  5537. SendCopyDataMessageStr(FDebugClientWnd, Handle, CD_DebugClient_CompiledCodeTextA, FCompiledCodeText);
  5538. SendCopyDataMessageStr(FDebugClientWnd, Handle, CD_DebugClient_CompiledCodeDebugInfoA, FCompiledCodeDebugInfo);
  5539. UpdateRunMenu;
  5540. end;
  5541. procedure TMainForm.WMDebuggerGoodbye(var Message: TMessage);
  5542. begin
  5543. ReplyMessage(0);
  5544. DebuggingStopped(True);
  5545. end;
  5546. procedure TMainForm.GetMemoAndDebugEntryFromMessage(Kind, Index: Integer; var Memo: TIDEScintFileEdit; var DebugEntry: PDebugEntry);
  5547. function GetMemoFromDebugEntryFileIndex(const FileIndex: Integer): TIDEScintFileEdit;
  5548. var
  5549. Memo: TIDEScintFileEdit;
  5550. begin
  5551. Result := nil;
  5552. if FOptions.OpenIncludedFiles then begin
  5553. for Memo in FFileMemos do begin
  5554. if Memo.Used and (Memo.CompilerFileIndex = FileIndex) then begin
  5555. Result := Memo;
  5556. Exit;
  5557. end;
  5558. end;
  5559. end else if FMainMemo.CompilerFileIndex = FileIndex then
  5560. Result := FMainMemo;
  5561. end;
  5562. var
  5563. I: Integer;
  5564. begin
  5565. for I := 0 to FDebugEntriesCount-1 do begin
  5566. if (FDebugEntries[I].Kind = Kind) and (FDebugEntries[I].Index = Index) then begin
  5567. Memo := GetMemoFromDebugEntryFileIndex(FDebugEntries[I].FileIndex);
  5568. DebugEntry := @FDebugEntries[I];
  5569. Exit;
  5570. end;
  5571. end;
  5572. Memo := nil;
  5573. DebugEntry := nil;
  5574. end;
  5575. procedure TMainForm.BringToForeground;
  5576. { Brings our top window to the foreground. Called when pausing while
  5577. debugging. }
  5578. var
  5579. TopWindow: HWND;
  5580. begin
  5581. TopWindow := GetThreadTopWindow;
  5582. if TopWindow <> 0 then begin
  5583. { First ask the debug client to call SetForegroundWindow() on our window.
  5584. If we don't do this then Windows (98/2000+) will prevent our window from
  5585. becoming activated if the debug client is currently in the foreground. }
  5586. SendMessage(FDebugClientWnd, WM_DebugClient_SetForegroundWindow,
  5587. WPARAM(TopWindow), 0);
  5588. { Now call SetForegroundWindow() ourself. Why? When a remote thread calls
  5589. SetForegroundWindow(), the request is queued; the window doesn't actually
  5590. become active until the next time the window's thread checks the message
  5591. queue. This call causes the window to become active immediately. }
  5592. SetForegroundWindow(TopWindow);
  5593. end;
  5594. end;
  5595. procedure TMainForm.DebuggerStepped(var Message: TMessage; const Intermediate: Boolean);
  5596. var
  5597. Memo: TIDEScintFileEdit;
  5598. DebugEntry: PDebugEntry;
  5599. LineNumber: Integer;
  5600. begin
  5601. GetMemoAndDebugEntryFromMessage(Message.WParam, Message.LParam, Memo, DebugEntry);
  5602. if (Memo = nil) or (DebugEntry = nil) then
  5603. Exit;
  5604. LineNumber := DebugEntry.LineNumber;
  5605. if LineNumber < 0 then { UninstExe has a DebugEntry but not a line number }
  5606. Exit;
  5607. if (LineNumber < Memo.LineStateCount) and
  5608. (Memo.LineState[LineNumber] <> lnEntryProcessed) then begin
  5609. Memo.LineState[LineNumber] := lnEntryProcessed;
  5610. UpdateLineMarkers(Memo, LineNumber);
  5611. end;
  5612. if (FStepMode = smStepOut) and DebugEntry.StepOutMarker then
  5613. FStepMode := smStepInto { Pause on next line }
  5614. else if (FStepMode = smStepInto) or
  5615. ((FStepMode = smStepOver) and not Intermediate) or
  5616. ((FStepMode = smRunToCursor) and
  5617. (FRunToCursorPoint.Kind = Integer(Message.WParam)) and
  5618. (FRunToCursorPoint.Index = Message.LParam)) or
  5619. (Memo.BreakPoints.IndexOf(LineNumber) <> -1) then begin
  5620. MoveCaretAndActivateMemo(Memo, LineNumber, True);
  5621. HideError;
  5622. SetStepLine(Memo, LineNumber);
  5623. BringToForeground;
  5624. { Tell Setup to pause }
  5625. Message.Result := 1;
  5626. FPaused := True;
  5627. FPausedAtCodeLine := DebugEntry.Kind = Ord(deCodeLine);
  5628. UpdateRunMenu;
  5629. UpdateCaption;
  5630. end;
  5631. end;
  5632. procedure TMainForm.WMDebuggerStepped(var Message: TMessage);
  5633. begin
  5634. DebuggerStepped(Message, False);
  5635. end;
  5636. procedure TMainForm.WMDebuggerSteppedIntermediate(var Message: TMessage);
  5637. begin
  5638. DebuggerStepped(Message, True);
  5639. end;
  5640. procedure TMainForm.WMDPIChanged(var Message: TMessage);
  5641. begin
  5642. inherited;
  5643. for var Memo in FMemos do
  5644. Memo.DPIChanged(Message);
  5645. end;
  5646. procedure TMainForm.WMDebuggerException(var Message: TMessage);
  5647. var
  5648. Memo: TIDEScintFileEdit;
  5649. DebugEntry: PDebugEntry;
  5650. LineNumber: Integer;
  5651. S: String;
  5652. begin
  5653. if FOptions.PauseOnDebuggerExceptions then begin
  5654. GetMemoAndDebugEntryFromMessage(Message.WParam, Message.LParam, Memo, DebugEntry);
  5655. if DebugEntry <> nil then
  5656. LineNumber := DebugEntry.LineNumber
  5657. else
  5658. LineNumber := -1;
  5659. if (Memo <> nil) and (LineNumber >= 0) then begin
  5660. MoveCaretAndActivateMemo(Memo, LineNumber, True);
  5661. SetStepLine(Memo, -1);
  5662. SetErrorLine(Memo, LineNumber);
  5663. end;
  5664. BringToForeground;
  5665. { Tell Setup to pause }
  5666. Message.Result := 1;
  5667. FPaused := True;
  5668. FPausedAtCodeLine := (DebugEntry <> nil) and (DebugEntry.Kind = Ord(deCodeLine));
  5669. UpdateRunMenu;
  5670. UpdateCaption;
  5671. ReplyMessage(Message.Result); { so that Setup enters a paused state now }
  5672. if LineNumber >= 0 then begin
  5673. S := Format('Line %d:' + SNewLine + '%s', [LineNumber + 1, AddPeriod(FDebuggerException)]);
  5674. if (Memo <> nil) and (Memo.Filename <> '') then
  5675. S := Memo.Filename + SNewLine2 + S;
  5676. MsgBox(S, 'Runtime Error', mbCriticalError, mb_Ok)
  5677. end else
  5678. MsgBox(AddPeriod(FDebuggerException), 'Runtime Error', mbCriticalError, mb_Ok);
  5679. end;
  5680. end;
  5681. procedure TMainForm.WMDebuggerSetForegroundWindow(var Message: TMessage);
  5682. begin
  5683. SetForegroundWindow(HWND(Message.WParam));
  5684. end;
  5685. procedure TMainForm.WMDebuggerCallStackCount(var Message: TMessage);
  5686. begin
  5687. FCallStackCount := Message.WParam;
  5688. end;
  5689. procedure TMainForm.WMCopyData(var Message: TWMCopyData);
  5690. var
  5691. S: String;
  5692. begin
  5693. case Message.CopyDataStruct.dwData of
  5694. CD_Debugger_ReplyW: begin
  5695. FReplyString := '';
  5696. SetString(FReplyString, PChar(Message.CopyDataStruct.lpData),
  5697. Message.CopyDataStruct.cbData div SizeOf(Char));
  5698. Message.Result := 1;
  5699. end;
  5700. CD_Debugger_ExceptionW: begin
  5701. SetString(FDebuggerException, PChar(Message.CopyDataStruct.lpData),
  5702. Message.CopyDataStruct.cbData div SizeOf(Char));
  5703. Message.Result := 1;
  5704. end;
  5705. CD_Debugger_UninstExeW: begin
  5706. SetString(FUninstExe, PChar(Message.CopyDataStruct.lpData),
  5707. Message.CopyDataStruct.cbData div sizeOf(Char));
  5708. Message.Result := 1;
  5709. end;
  5710. CD_Debugger_LogMessageW: begin
  5711. SetString(S, PChar(Message.CopyDataStruct.lpData),
  5712. Message.CopyDataStruct.cbData div SizeOf(Char));
  5713. DebugLogMessage(S);
  5714. Message.Result := 1;
  5715. end;
  5716. CD_Debugger_TempDirW: begin
  5717. { Paranoia: Store it in a local variable first. That way, if there's
  5718. a problem reading the string FTempDir will be left unmodified.
  5719. Gotta be extra careful when storing a path we'll be deleting. }
  5720. SetString(S, PChar(Message.CopyDataStruct.lpData),
  5721. Message.CopyDataStruct.cbData div SizeOf(Char));
  5722. { Extreme paranoia: If there are any embedded nulls, discard it. }
  5723. if Pos(#0, S) <> 0 then
  5724. S := '';
  5725. FTempDir := S;
  5726. Message.Result := 1;
  5727. end;
  5728. CD_Debugger_CallStackW: begin
  5729. SetString(S, PChar(Message.CopyDataStruct.lpData),
  5730. Message.CopyDataStruct.cbData div SizeOf(Char));
  5731. DebugShowCallStack(S, FCallStackCount);
  5732. end;
  5733. end;
  5734. end;
  5735. function TMainForm.DestroyLineState(const AMemo: TIDEScintFileEdit): Boolean;
  5736. begin
  5737. if Assigned(AMemo.LineState) then begin
  5738. AMemo.LineStateCapacity := 0;
  5739. AMemo.LineStateCount := 0;
  5740. FreeMem(AMemo.LineState);
  5741. AMemo.LineState := nil;
  5742. Result := True;
  5743. end else
  5744. Result := False;
  5745. end;
  5746. procedure TMainForm.DestroyDebugInfo;
  5747. var
  5748. HadDebugInfo: Boolean;
  5749. Memo: TIDEScintFileEdit;
  5750. begin
  5751. HadDebugInfo := False;
  5752. for Memo in FFileMemos do
  5753. if DestroyLineState(Memo) then
  5754. HadDebugInfo := True;
  5755. FDebugEntriesCount := 0;
  5756. FreeMem(FDebugEntries);
  5757. FDebugEntries := nil;
  5758. FVariableDebugEntriesCount := 0;
  5759. FreeMem(FVariableDebugEntries);
  5760. FVariableDebugEntries := nil;
  5761. FCompiledCodeText := '';
  5762. FCompiledCodeDebugInfo := '';
  5763. { Clear all dots and reset breakpoint icons (unless exiting; no point) }
  5764. if HadDebugInfo and not(csDestroying in ComponentState) then
  5765. UpdateAllMemosLineMarkers;
  5766. end;
  5767. var
  5768. PrevCompilerFileIndex: Integer;
  5769. PrevMemo: TIDEScintFileEdit;
  5770. procedure TMainForm.ParseDebugInfo(DebugInfo: Pointer);
  5771. function GetMemoFromCompilerFileIndex(const CompilerFileIndex: Integer): TIDEScintFileEdit;
  5772. var
  5773. Memo: TIDEScintFileEdit;
  5774. begin
  5775. if (PrevCompilerFileIndex <> CompilerFileIndex) then begin
  5776. PrevMemo := nil;
  5777. for Memo in FFileMemos do begin
  5778. if Memo.Used and (Memo.CompilerFileIndex = CompilerFileIndex) then begin
  5779. PrevMemo := Memo;
  5780. Break;
  5781. end;
  5782. end;
  5783. PrevCompilerFileIndex := CompilerFileIndex;
  5784. end;
  5785. Result := PrevMemo;
  5786. end;
  5787. { This creates and fills the DebugEntries and Memo LineState arrays }
  5788. var
  5789. Header: PDebugInfoHeader;
  5790. Memo: TIDEScintFileEdit;
  5791. Size: Cardinal;
  5792. I: Integer;
  5793. begin
  5794. DestroyDebugInfo;
  5795. Header := DebugInfo;
  5796. if (Header.ID <> DebugInfoHeaderID) or
  5797. (Header.Version <> DebugInfoHeaderVersion) then
  5798. raise Exception.Create('Unrecognized debug info format');
  5799. try
  5800. for Memo in FFileMemos do begin
  5801. if Memo.Used then begin
  5802. I := Memo.Lines.Count;
  5803. Memo.LineState := AllocMem(SizeOf(TLineState) * (I + LineStateGrowAmount));
  5804. Memo.LineStateCapacity := I + LineStateGrowAmount;
  5805. Memo.LineStateCount := I;
  5806. end;
  5807. end;
  5808. Inc(Cardinal(DebugInfo), SizeOf(Header^));
  5809. FDebugEntriesCount := Header.DebugEntryCount;
  5810. Size := FDebugEntriesCount * SizeOf(TDebugEntry);
  5811. GetMem(FDebugEntries, Size);
  5812. Move(DebugInfo^, FDebugEntries^, Size);
  5813. for I := 0 to FDebugEntriesCount-1 do
  5814. Dec(FDebugEntries[I].LineNumber);
  5815. Inc(Cardinal(DebugInfo), Size);
  5816. FVariableDebugEntriesCount := Header.VariableDebugEntryCount;
  5817. Size := FVariableDebugEntriesCount * SizeOf(TVariableDebugEntry);
  5818. GetMem(FVariableDebugEntries, Size);
  5819. Move(DebugInfo^, FVariableDebugEntries^, Size);
  5820. Inc(Cardinal(DebugInfo), Size);
  5821. SetString(FCompiledCodeText, PAnsiChar(DebugInfo), Header.CompiledCodeTextLength);
  5822. Inc(Cardinal(DebugInfo), Header.CompiledCodeTextLength);
  5823. SetString(FCompiledCodeDebugInfo, PAnsiChar(DebugInfo), Header.CompiledCodeDebugInfoLength);
  5824. PrevCompilerFileIndex := UnknownCompilerFileIndex;
  5825. for I := 0 to FDebugEntriesCount-1 do begin
  5826. if FDebugEntries[I].LineNumber >= 0 then begin
  5827. Memo := GetMemoFromCompilerFileIndex(FDebugEntries[I].FileIndex);
  5828. if (Memo <> nil) and (FDebugEntries[I].LineNumber < Memo.LineStateCount) then begin
  5829. if Memo.LineState[FDebugEntries[I].LineNumber] = lnUnknown then
  5830. Memo.LineState[FDebugEntries[I].LineNumber] := lnHasEntry;
  5831. end;
  5832. end;
  5833. end;
  5834. UpdateAllMemosLineMarkers;
  5835. except
  5836. DestroyDebugInfo;
  5837. raise;
  5838. end;
  5839. end;
  5840. procedure TMainForm.ResetAllMemosLineState;
  5841. { Changes green dots back to grey dots }
  5842. var
  5843. Memo: TIDEScintFileEdit;
  5844. I: Integer;
  5845. begin
  5846. for Memo in FFileMemos do begin
  5847. if Memo.Used and Assigned(Memo.LineState) then begin
  5848. for I := 0 to Memo.LineStateCount-1 do begin
  5849. if Memo.LineState[I] = lnEntryProcessed then begin
  5850. Memo.LineState[I] := lnHasEntry;
  5851. UpdateLineMarkers(Memo, I);
  5852. end;
  5853. end;
  5854. end;
  5855. end;
  5856. end;
  5857. procedure TMainForm.CheckIfTerminated;
  5858. var
  5859. H: THandle;
  5860. begin
  5861. if FDebugging then begin
  5862. { Check if the process hosting the debug client (e.g. Setup or the
  5863. uninstaller second phase) has terminated. If the debug client hasn't
  5864. connected yet, check the initial process (e.g. SetupLdr or the
  5865. uninstaller first phase) instead. }
  5866. if FDebugClientWnd <> 0 then
  5867. H := FDebugClientProcessHandle
  5868. else
  5869. H := FProcessHandle;
  5870. if WaitForSingleObject(H, 0) <> WAIT_TIMEOUT then
  5871. DebuggingStopped(True);
  5872. end;
  5873. end;
  5874. procedure TMainForm.DebuggingStopped(const WaitForTermination: Boolean);
  5875. function GetExitCodeText: String;
  5876. var
  5877. ExitCode: DWORD;
  5878. begin
  5879. { Note: When debugging an uninstall, this will get the exit code off of
  5880. the first phase process, since that's the exit code users will see when
  5881. running the uninstaller outside the debugger. }
  5882. case WaitForSingleObject(FProcessHandle, 0) of
  5883. WAIT_OBJECT_0:
  5884. begin
  5885. if GetExitCodeProcess(FProcessHandle, ExitCode) then begin
  5886. { If the high bit is set, the process was killed uncleanly (e.g.
  5887. by a debugger). Show the exit code as hex in that case. }
  5888. if ExitCode and $80000000 <> 0 then
  5889. Result := Format(DebugTargetStrings[FDebugTarget] + ' exit code: 0x%.8x', [ExitCode])
  5890. else
  5891. Result := Format(DebugTargetStrings[FDebugTarget] + ' exit code: %u', [ExitCode]);
  5892. end
  5893. else
  5894. Result := 'Unable to get ' + DebugTargetStrings[FDebugTarget] + ' exit code (GetExitCodeProcess failed)';
  5895. end;
  5896. WAIT_TIMEOUT:
  5897. Result := DebugTargetStrings[FDebugTarget] + ' is still running; can''t get exit code';
  5898. else
  5899. Result := 'Unable to get ' + DebugTargetStrings[FDebugTarget] + ' exit code (WaitForSingleObject failed)';
  5900. end;
  5901. end;
  5902. var
  5903. ExitCodeText: String;
  5904. begin
  5905. if WaitForTermination then begin
  5906. { Give the initial process time to fully terminate so we can successfully
  5907. get its exit code }
  5908. WaitForSingleObject(FProcessHandle, 5000);
  5909. end;
  5910. FDebugging := False;
  5911. FDebugClientWnd := 0;
  5912. ExitCodeText := GetExitCodeText;
  5913. if FDebugClientProcessHandle <> 0 then begin
  5914. CloseHandle(FDebugClientProcessHandle);
  5915. FDebugClientProcessHandle := 0;
  5916. end;
  5917. CloseHandle(FProcessHandle);
  5918. FProcessHandle := 0;
  5919. FTempDir := '';
  5920. CheckIfRunningTimer.Enabled := False;
  5921. HideError;
  5922. SetStepLine(FStepMemo, -1);
  5923. UpdateRunMenu;
  5924. UpdateCaption;
  5925. DebugLogMessage('*** ' + ExitCodeText);
  5926. StatusBar.Panels[spExtraStatus].Text := ' ' + ExitCodeText;
  5927. end;
  5928. procedure TMainForm.DetachDebugger;
  5929. begin
  5930. CheckIfTerminated;
  5931. if not FDebugging then Exit;
  5932. SendNotifyMessage(FDebugClientWnd, WM_DebugClient_Detach, 0, 0);
  5933. DebuggingStopped(False);
  5934. end;
  5935. function TMainForm.AskToDetachDebugger: Boolean;
  5936. begin
  5937. if FDebugClientWnd = 0 then begin
  5938. MsgBox('Please stop the running ' + DebugTargetStrings[FDebugTarget] + ' process before performing this command.',
  5939. SCompilerFormCaption, mbError, MB_OK);
  5940. Result := False;
  5941. end else if MsgBox('This command will detach the debugger from the running ' + DebugTargetStrings[FDebugTarget] + ' process. Continue?',
  5942. SCompilerFormCaption, mbError, MB_OKCANCEL) = IDOK then begin
  5943. DetachDebugger;
  5944. Result := True;
  5945. end else
  5946. Result := False;
  5947. end;
  5948. function TMainForm.AnyMemoHasBreakPoint: Boolean;
  5949. begin
  5950. { Also see RDeleteBreakPointsClick }
  5951. for var Memo in FFileMemos do
  5952. if Memo.Used and (Memo.BreakPoints.Count > 0) then
  5953. Exit(True);
  5954. Result := False;
  5955. end;
  5956. procedure TMainForm.RMenuClick(Sender: TObject);
  5957. begin
  5958. RDeleteBreakPoints.Enabled := AnyMemoHasBreakPoint;
  5959. { See UpdateRunMenu for other menu items }
  5960. ApplyMenuBitmaps(RMenu);
  5961. end;
  5962. procedure TMainForm.BreakPointsPopupMenuClick(Sender: TObject);
  5963. begin
  5964. RToggleBreakPoint2.Enabled := FActiveMemo is TIDEScintFileEdit;
  5965. RDeleteBreakPoints2.Enabled := AnyMemoHasBreakPoint;
  5966. { Also see UpdateRunMenu }
  5967. ApplyMenuBitmaps(Sender as TMenuItem);
  5968. end;
  5969. { Should always be called when one of the Enabled states would change because
  5970. other code depends on the states being correct always even if the user never
  5971. clicks the Run menu. This is unlike the other menus. Note: also updates
  5972. BCompile and BStopCompile from the Build menu. }
  5973. procedure TMainForm.UpdateRunMenu;
  5974. begin
  5975. CheckIfTerminated;
  5976. BCompile.Enabled := not FCompiling and not FDebugging;
  5977. CompileButton.Enabled := BCompile.Enabled;
  5978. BStopCompile.Enabled := FCompiling;
  5979. StopCompileButton.Enabled := BStopCompile.Enabled;
  5980. RRun.Enabled := not FCompiling and (not FDebugging or FPaused);
  5981. RunButton.Enabled := RRun.Enabled;
  5982. RPause.Enabled := FDebugging and not FPaused;
  5983. PauseButton.Enabled := RPause.Enabled;
  5984. RRunToCursor.Enabled := RRun.Enabled and (FActiveMemo is TIDEScintFileEdit);
  5985. RStepInto.Enabled := RRun.Enabled;
  5986. RStepOver.Enabled := RRun.Enabled;
  5987. RStepOut.Enabled := FPaused;
  5988. RToggleBreakPoint.Enabled := FActiveMemo is TIDEScintFileEdit;
  5989. RTerminate.Enabled := FDebugging and (FDebugClientWnd <> 0);
  5990. TerminateButton.Enabled := RTerminate.Enabled;
  5991. REvaluate.Enabled := FDebugging and (FDebugClientWnd <> 0);
  5992. { See RMenuClick for other menu items and also see BreakPointsPopupMenuClick }
  5993. end;
  5994. procedure TMainForm.UpdateSaveMenuItemAndButton;
  5995. begin
  5996. FSave.Enabled := FActiveMemo is TIDEScintFileEdit;
  5997. SaveButton.Enabled := FSave.Enabled;
  5998. end;
  5999. procedure TMainForm.UpdateTargetMenu;
  6000. begin
  6001. if FDebugTarget = dtSetup then begin
  6002. RTargetSetup.Checked := True;
  6003. TargetSetupButton.Down := True;
  6004. end else begin
  6005. RTargetUninstall.Checked := True;
  6006. TargetUninstallButton.Down := True;
  6007. end;
  6008. end;
  6009. procedure TMainForm.UpdateKeyMapping;
  6010. type
  6011. TKeyMappedMenu = TPair<TMenuItem, TPair<TShortcut, TToolButton>>;
  6012. function KMM(const MenuItem: TMenuItem; const DelphiKey: Word; const DelphiShift: TShiftState;
  6013. const VisualStudioKey: Word; const VisualStudioShift: TShiftState;
  6014. const ToolButton: TToolButton = nil): TKeyMappedMenu;
  6015. begin
  6016. var AShortCut: TShortCut;
  6017. case FOptions.KeyMappingType of
  6018. kmtDelphi: AShortCut := ShortCut(DelphiKey, DelphiShift);
  6019. kmtVisualStudio: AShortCut := ShortCut(VisualStudioKey, VisualStudioShift);
  6020. else
  6021. raise Exception.Create('Unknown FOptions.KeyMappingType');
  6022. end;
  6023. Result := TKeyMappedMenu.Create(MenuItem, TPair<TShortcut, TToolButton>.Create(AShortcut, ToolButton)); { These are records so no need to free }
  6024. end;
  6025. begin
  6026. var KeyMappedMenus := [
  6027. KMM(EFindRegEx, Ord('R'), [ssCtrl, ssAlt], Ord('R'), [ssAlt]),
  6028. KMM(BCompile, VK_F9, [ssCtrl], Ord('B'), [ssCtrl], CompileButton), { Also FCompileShortCut2 below }
  6029. KMM(RRun, VK_F9, [], VK_F5, [], RunButton),
  6030. KMM(RRunToCursor, VK_F4, [], VK_F10, [ssCtrl]),
  6031. KMM(RStepInto, VK_F7, [], VK_F11, []),
  6032. KMM(RStepOver, VK_F8, [], VK_F10, []),
  6033. KMM(RStepOut, VK_F8, [ssShift], VK_F11, [ssShift]),
  6034. KMM(RToggleBreakPoint, VK_F5, [], VK_F9, []),
  6035. KMM(RDeleteBreakPoints, VK_F5, [ssShift, ssCtrl], VK_F9, [ssShift, ssCtrl]),
  6036. KMM(RTerminate, VK_F2, [ssCtrl], VK_F5, [ssShift], TerminateButton),
  6037. KMM(REvaluate, VK_F7, [ssCtrl], VK_F9, [ssShift])];
  6038. FKeyMappedMenus.Clear;
  6039. for var KeyMappedMenu in KeyMappedMenus do begin
  6040. var ShortCut := KeyMappedMenu.Value.Key;
  6041. var ToolButton := KeyMappedMenu.Value.Value;
  6042. KeyMappedMenu.Key.ShortCut := ShortCut;
  6043. if ToolButton <> nil then begin
  6044. var MenuItem := KeyMappedMenu.Key;
  6045. ToolButton.Hint := Format('%s (%s)', [RemoveAccelChar(MenuItem.Caption), NewShortCutToText(ShortCut)]);
  6046. end;
  6047. FKeyMappedMenus.Add(ShortCut, ToolButton);
  6048. end;
  6049. { Set fake shortcuts on any duplicates of the above in popup menus }
  6050. SetFakeShortCut(RToggleBreakPoint2, RToggleBreakPoint.ShortCut);
  6051. SetFakeShortCut(RDeleteBreakPoints2, RDeleteBreakPoints.ShortCut);
  6052. { Handle two special cases:
  6053. -The Nav buttons have no corresponding menu item and also no ShortCut property
  6054. so they need special handling
  6055. -Visual Studio and Delphi have separate Compile and Build shortcuts and the
  6056. Compile shortcut is displayed by the menu and is set above but we want to
  6057. allow the Build shortcuts as well for our single Build/Compile command }
  6058. FBackNavButtonShortCut := ShortCut(VK_LEFT, [ssAlt]);
  6059. FForwardNavButtonShortCut := ShortCut(VK_RIGHT, [ssAlt]);
  6060. case FOptions.KeyMappingType of
  6061. kmtDelphi:
  6062. begin
  6063. FBackNavButtonShortCut2 := 0;
  6064. FForwardNavButtonShortCut2 := 0;
  6065. FCompileShortCut2 := ShortCut(VK_F9, [ssShift]);
  6066. end;
  6067. kmtVisualStudio:
  6068. begin
  6069. FBackNavButtonShortCut2 := ShortCut(VK_OEM_MINUS, [ssCtrl]);
  6070. FForwardNavButtonShortCut2 := ShortCut(VK_OEM_MINUS, [ssCtrl, ssShift]);
  6071. FCompileShortCut2 := ShortCut(VK_F7, []);
  6072. end;
  6073. else
  6074. raise Exception.Create('Unknown FOptions.KeyMappingType');
  6075. end;
  6076. BackNavButton.Hint := Format('Back (%s)', [NewShortCutToText(FBackNavButtonShortCut)]);
  6077. FKeyMappedMenus.Add(FBackNavButtonShortCut, nil);
  6078. ForwardNavButton.Hint := Format('Forward (%s)', [NewShortCutToText(FForwardNavButtonShortCut)]);
  6079. FKeyMappedMenus.Add(FForwardNavButtonShortCut, nil);
  6080. end;
  6081. procedure TMainForm.UpdateTheme;
  6082. procedure SetListBoxWindowTheme(const ListBox: TListBox);
  6083. begin
  6084. ListBox.Font.Color := FTheme.Colors[tcFore];
  6085. ListBox.Color := FTheme.Colors[tcBack];
  6086. ListBox.Invalidate;
  6087. SetControlWindowTheme(ListBox, FTheme.Dark);
  6088. end;
  6089. begin
  6090. FTheme.Typ := FOptions.ThemeType;
  6091. {$IF CompilerVersion >= 36.0 }
  6092. { For MainForm the active style only impacts message boxes and tooltips: FMemos, ToolbarPanel,
  6093. UpdatePanel, SplitPanel and the 4 ListBoxes all ignore it because their StyleName property is set
  6094. to 'Windows' always, either by the .dfm or by code. Additionally, for scrollbars and StatusBar,
  6095. MainForm's StyleElements is empty. Menus ignore it because shMenus is removed from
  6096. TStyleManager.SystemHooks at startup. }
  6097. if FTheme.Dark then
  6098. TStyleManager.TrySetStyle('Dark')
  6099. else
  6100. TStyleManager.TrySetStyle('Windows');
  6101. { For some reason only MainForm needs this: with StyleName set to an empty string, dialog boxes
  6102. it opens, such as MsgBox, look broken }
  6103. StyleName := TStyleManager.ActiveStyle.Name;
  6104. {$ENDIF}
  6105. if not Application.ShowMainForm then
  6106. Exit;
  6107. SetHelpFileDark(FTheme.Dark);
  6108. InitFormTheme(Self);
  6109. ToolbarPanel.Color := FTheme.Colors[tcToolBack];
  6110. for var Memo in FMemos do begin
  6111. Memo.UpdateThemeColorsAndStyleAttributes;
  6112. SetControlWindowTheme(Memo, FTheme.Dark);
  6113. end;
  6114. SetListBoxWindowTheme(CompilerOutputList);
  6115. SetListBoxWindowTheme(DebugOutputList);
  6116. SetListBoxWindowTheme(DebugCallStackList);
  6117. SetListBoxWindowTheme(FindResultsList);
  6118. if FTheme.Dark then begin
  6119. ThemedToolbarVirtualImageList.ImageCollection := ImagesModule.DarkToolBarImageCollection;
  6120. ThemedMarkersAndACVirtualImageList.ImageCollection := ImagesModule.DarkMarkersAndACImageCollection;
  6121. FBuildImageList := ImagesModule.DarkBuildImageList;
  6122. end else begin
  6123. ThemedToolbarVirtualImageList.ImageCollection := ImagesModule.LightToolBarImageCollection;
  6124. ThemedMarkersAndACVirtualImageList.ImageCollection := ImagesModule.LightMarkersAndACImageCollection;
  6125. FBuildImageList := ImagesModule.LightBuildImageList;
  6126. end;
  6127. UpdateThemeData(True);
  6128. UpdateBevel1Visibility;
  6129. UpdateMarginsAndAutoCompleteIcons;
  6130. SplitPanel.ParentBackground := False;
  6131. SplitPanel.Color := FTheme.Colors[tcSplitterBack];
  6132. FMenuDarkBackgroundBrush.Color := FTheme.Colors[tcToolBack];
  6133. FMenuDarkHotOrSelectedBrush.Color := $2C2C2C; { Same as themed menu drawn by Windows 11, which is close to Colors[tcBack] }
  6134. DrawMenuBar(Handle);
  6135. { SetPreferredAppMode doesn't work without FlushMenuThemes here: it would have
  6136. to be called before the form is created to have an effect without
  6137. FlushMenuThemes. So don't call SetPreferredAppMode if FlushMenuThemes is
  6138. missing. }
  6139. if Assigned(SetPreferredAppMode) and Assigned(FlushMenuThemes) then begin
  6140. FMenuImageList := ThemedToolbarVirtualImageList;
  6141. if FTheme.Dark then
  6142. SetPreferredAppMode(PAM_FORCEDARK)
  6143. else
  6144. SetPreferredAppMode(PAM_FORCELIGHT);
  6145. FlushMenuThemes;
  6146. end else
  6147. FMenuImageList := LightToolbarVirtualImageList;
  6148. end;
  6149. procedure TMainForm.UpdateThemeData(const Open: Boolean);
  6150. procedure CloseThemeDataIfNeeded(var ThemeData: HTHEME);
  6151. begin
  6152. if ThemeData <> 0 then begin
  6153. CloseThemeData(ThemeData);
  6154. ThemeData := 0;
  6155. end;
  6156. end;
  6157. begin
  6158. CloseThemeDataIfNeeded(FProgressThemeData);
  6159. CloseThemeDataIfNeeded(FMenuThemeData);
  6160. CloseThemeDataIfNeeded(FToolbarThemeData);
  6161. CloseThemeDataIfNeeded(FStatusBarThemeData);
  6162. if Open and UseThemes then begin
  6163. FProgressThemeData := OpenThemeData(Handle, 'Progress');
  6164. FMenuThemeData := OpenThemeData(Handle, 'Menu');
  6165. if FTheme.Dark then
  6166. FToolbarThemeData := OpenThemeData(Handle, 'DarkMode::Toolbar');
  6167. if FToolbarThemeData = 0 then
  6168. FToolbarThemeData := OpenThemeData(Handle, 'Toolbar');
  6169. FStatusBarThemeData := OpenThemeData(Handle, 'Status');
  6170. end;
  6171. end;
  6172. procedure TMainForm.UpdateUpdatePanel;
  6173. begin
  6174. UpdatePanel.Visible := FUpdatePanelMessages.Count > 0;
  6175. if UpdatePanel.Visible then begin
  6176. var MessageToShowIndex := FUpdatePanelMessages.Count-1;
  6177. UpdateLinkLabel.Tag := MessageToShowIndex;
  6178. UpdateLinkLabel.Caption := FUpdatePanelMessages[MessageToShowIndex].Msg;
  6179. if not FHighContrastActive then
  6180. UpdatePanel.Color := FUpdatePanelMessages[MessageToShowIndex].Color;
  6181. if FUpdatePanelMessages[MessageToShowIndex].ConfigIdent.StartsWith('Purchase') then
  6182. FDonateImageMenuItem := HPurchase
  6183. else
  6184. FDonateImageMenuItem := HDonate;
  6185. UpdatePanelDonateBitBtn.Hint := RemoveAccelChar(FDonateImageMenuItem.Caption)
  6186. end;
  6187. UpdateBevel1Visibility;
  6188. end;
  6189. procedure TMainForm.UpdateMenuBitmapsIfNeeded;
  6190. procedure AddMenuBitmap(const MenuBitmaps: TMenuBitmaps; const DC: HDC; const BitmapInfo: TBitmapInfo;
  6191. const MenuItem: TMenuItem; const ImageList: TVirtualImageList; const ImageIndex: Integer); overload;
  6192. begin
  6193. var pvBits: Pointer;
  6194. var Bitmap := CreateDIBSection(DC, bitmapInfo, DIB_RGB_COLORS, pvBits, 0, 0);
  6195. var OldBitmap := SelectObject(DC, Bitmap);
  6196. if ImageList_Draw(ImageList.Handle, ImageIndex, DC, 0, 0, ILD_TRANSPARENT) then
  6197. MenuBitmaps.Add(MenuItem, Bitmap)
  6198. else begin
  6199. SelectObject(DC, OldBitmap);
  6200. DeleteObject(Bitmap);
  6201. end;
  6202. end;
  6203. procedure AddMenuBitmap(const MenuBitmaps: TMenuBitmaps; const DC: HDC; const BitmapInfo: TBitmapInfo;
  6204. const MenuItem: TMenuItem; const ImageList: TVirtualImageList; const ImageName: String); overload;
  6205. begin
  6206. AddMenuBitmap(MenuBitmaps, DC, BitmapInfo, MenuItem, ImageList, ImageList.GetIndexByName(ImageName));
  6207. end;
  6208. type
  6209. TButtonedMenu = TPair<TMenuItem, TToolButton>;
  6210. TNamedMenu = TPair<TMenuItem, String>;
  6211. function BM(const MenuItem: TMenuItem; const ToolButton: TToolButton): TButtonedMenu;
  6212. begin
  6213. Result := TButtonedMenu.Create(MenuItem, ToolButton); { This is a record so no need to free }
  6214. end;
  6215. function NM(const MenuItem: TMenuItem; const Name: String): TNamedMenu;
  6216. begin
  6217. Result := TNamedMenu.Create(MenuItem, Name); { This is a record so no need to free }
  6218. end;
  6219. begin
  6220. { This will create bitmaps for the current DPI using ImageList_Draw.
  6221. These draw perfectly even on Windows 7. Other techniques don't work because
  6222. they loose transparency or only look good on Windows 8 and later. Or they do
  6223. work but cause lots more VCL code to be run than just our simple CreateDIB+Draw
  6224. combo.
  6225. ApplyBitmaps will apply them to menu items using SetMenuItemInfo. The menu item
  6226. does not copy the bitmap so they should still be alive after ApplyBitmaps is done.
  6227. Depends on FMenuImageList to pick the best size icons for the current DPI
  6228. from the collection. }
  6229. var ImageList := FMenuImageList;
  6230. var NewSize: TSize;
  6231. NewSize.cx := ImageList.Width;
  6232. NewSize.cy := ImageList.Height;
  6233. if (NewSize.cx <> FMenuBitmapsSize.cx) or (NewSize.cy <> FMenuBitmapsSize.cy) or
  6234. (ImageList.ImageCollection <> FMenuBitmapsSourceImageCollection) then begin
  6235. { Cleanup previous }
  6236. for var Bitmap in FMenuBitmaps.Values do
  6237. DeleteObject(Bitmap);
  6238. FMenuBitmaps.Clear;
  6239. { Create }
  6240. var DC := CreateCompatibleDC(0);
  6241. if DC <> 0 then begin
  6242. try
  6243. var BitmapInfo := CreateBitmapInfo(NewSize.cx, NewSize.cy, 32);
  6244. var ButtonedMenus := [
  6245. BM(FNewMainFile, NewMainFileButton),
  6246. BM(FOpenMainFile, OpenMainFileButton),
  6247. BM(FSave, SaveButton),
  6248. BM(BCompile, CompileButton),
  6249. BM(BStopCompile, StopCompileButton),
  6250. BM(RRun, RunButton),
  6251. BM(RPause, PauseButton),
  6252. BM(RTerminate, TerminateButton),
  6253. BM(HDoc, HelpButton)];
  6254. for var ButtonedMenu in ButtonedMenus do
  6255. AddMenuBitmap(FMenuBitmaps, DC, BitmapInfo, ButtonedMenu.Key, ImageList, ButtonedMenu.Value.ImageIndex);
  6256. var NamedMenus := [
  6257. NM(FClearRecent, 'eraser'),
  6258. NM(FSaveMainFileAs, 'save-as-filled'),
  6259. NM(FSaveAll, 'save-all-filled'),
  6260. NM(FPrint, 'printer'),
  6261. NM(EUndo, 'command-undo-1'),
  6262. NM(ERedo, 'command-redo-1'),
  6263. NM(ECut, 'clipboard-cut'),
  6264. NM(ECopy, 'clipboard-copy'),
  6265. NM(POutputListCopy, 'clipboard-copy'),
  6266. NM(EPaste, 'clipboard-paste'),
  6267. NM(EDelete, 'symbol-cancel'),
  6268. NM(ESelectAll, 'select-all'),
  6269. NM(POutputListSelectAll, 'select-all'),
  6270. NM(EFind, 'find'),
  6271. NM(EFindInFiles, 'folder-open-filled-find'),
  6272. //NM(EFindNext, 'unused\find-arrow-right-2'),
  6273. //NM(EFindPrevious, 'unused\find-arrow-left-2'),
  6274. NM(EReplace, 'replace'),
  6275. NM(EFoldLine, 'symbol-remove'),
  6276. NM(EUnfoldLine, 'symbol-add'),
  6277. NM(VZoomIn, 'zoom-in'),
  6278. NM(VZoomOut, 'zoom-out'),
  6279. NM(VNextTab, 'control-tab-filled-arrow-right-2'),
  6280. NM(VPreviousTab, 'control-tab-filled-arrow-left-2'),
  6281. //NM(VCloseCurrentTab, 'unused\control-tab-filled-cancel-2'),
  6282. NM(VReopenTabs, 'control-tab-filled-redo-1'),
  6283. NM(VReopenTabs2, 'control-tab-filled-redo-1'),
  6284. NM(BOpenOutputFolder, 'folder-open-filled'),
  6285. NM(RParameters, 'control-edit'),
  6286. NM(RRunToCursor, 'debug-start-filled-arrow-right-2'),
  6287. NM(RStepInto, 'debug-step-into'),
  6288. NM(RStepOver, 'debug-step-over'),
  6289. NM(RStepOut, 'debug-step-out'),
  6290. NM(RToggleBreakPoint, 'debug-breakpoint-filled'),
  6291. NM(RToggleBreakPoint2, 'debug-breakpoint-filled'),
  6292. NM(RDeleteBreakPoints, 'debug-breakpoints-filled-eraser'),
  6293. NM(RDeleteBreakPoints2, 'debug-breakpoints-filled-eraser'),
  6294. NM(REvaluate, 'variables'),
  6295. NM(TAddRemovePrograms, 'application'),
  6296. NM(TGenerateGUID, 'tag-script-filled'),
  6297. NM(TFilesDesigner, 'documents-script-filled'),
  6298. NM(TRegistryDesigner, 'control-tree-script-filled'),
  6299. NM(TMsgBoxDesigner, 'comment-text-script-filled'),
  6300. NM(TSignTools, 'padlock-filled'),
  6301. NM(TOptions, 'gear-filled'),
  6302. NM(HPurchase, 'shopping-cart'),
  6303. NM(HRegister, 'key-filled'),
  6304. NM(HDonate, 'heart-filled'),
  6305. NM(HMailingList, 'alert-filled'),
  6306. NM(HWhatsNew, 'announcement'),
  6307. NM(HWebsite, 'home'),
  6308. NM(HAbout, 'button-info')];
  6309. for var NamedMenu in NamedMenus do
  6310. AddMenuBitmap(FMenuBitmaps, DC, BitmapInfo, NamedMenu.Key, ImageList, NamedMenu.Value);
  6311. finally
  6312. DeleteDC(DC);
  6313. end;
  6314. end;
  6315. FMenuBitmapsSize := NewSize;
  6316. FMenuBitmapsSourceImageCollection := FMenuImageList.ImageCollection;
  6317. end;
  6318. end;
  6319. procedure TMainForm.ApplyMenuBitmaps(const ParentMenuItem: TMenuItem);
  6320. begin
  6321. UpdateMenuBitmapsIfNeeded;
  6322. { Setting MainMenu1.ImageList or a menu item's .Bitmap to make a menu item
  6323. show a bitmap is not OK: it causes the entire menu to become owner drawn
  6324. which makes it looks different from native menus and additionally the trick
  6325. SetFakeShortCut uses doesn't work with owner drawn menus.
  6326. Instead UpdateMenuBitmapsIfNeeded has prepared images which can be applied
  6327. to native menu items using SetMenuItemInfo and MIIM_BITMAP - which is what we
  6328. do below.
  6329. A problem with this is that Delphi's TMenu likes to constantly recreate the
  6330. underlying native menu items, for example when updating the caption. Sometimes
  6331. it will even destroy and repopulate an entire menu because of a simple change
  6332. like setting the caption of a single item!
  6333. This means the result of our SetMenuItemInfo call (which Delphi doesn't know
  6334. about) will quickly become lost when Delphi recreates the menu item.
  6335. Fixing this in the OnChange event is not possible, this is event is more
  6336. than useless.
  6337. The solution is shown by TMenu.DispatchPopup: in reaction to WM_INITMENUPOPUP
  6338. it calls our Click events right before the menu is shown, giving us the
  6339. opportunity to call SetMenuItemInfo for the menu's items.
  6340. This works unless Delphi decides to destroy and repopulate the menu after
  6341. calling Click. Most amazingly it can do that indeed: it does this if the DPI
  6342. changed since the last popup or if a automatic hotkey change or line reduction
  6343. happens due to the menu's AutoHotkeys or AutoLineReduction properties. To make
  6344. things even worse: for the Run menu it does this each and every time it is
  6345. opened: this menu currently has a 'Step Out' item which has no shortcut but
  6346. also all its letters are taken by another item already. This confuses the
  6347. AutoHotkeys code, making it destroy and repopulate the entire menu over and
  6348. over because it erroneously thinks a hotkey changed.
  6349. To avoid this MainMenu1.AutoHotkeys was set to maManual since we have always
  6350. managed the hotkeys ourselves anyway and .AutoLineReduction was also set to
  6351. maManual and we now manage that ourselves as well.
  6352. This just leave an issue with the icons not appearing on the first popup after
  6353. a DPI change and this seems like a minor issue only.
  6354. For TPopupMenu: calling ApplyMenuBitmaps(PopupMenu.Items) does work but makes
  6355. the popup only show icons without text. This seems to be a limitiation of menus
  6356. created by CreatePopupMenu instead of CreateMenu. This is why our popups with
  6357. icons are all menu items popped using TMainFormPopupMenu. These menu items
  6358. are hidden in the main menu and temporarily shown on popup. Popping an always
  6359. hidden menu item (or a visible one as a child of a hidden parent) doesnt work. }
  6360. var mmi: TMenuItemInfo;
  6361. mmi.cbSize := SizeOf(mmi);
  6362. mmi.fMask := MIIM_BITMAP;
  6363. for var I := 0 to ParentMenuItem.Count-1 do begin
  6364. var MenuItem := ParentMenuItem.Items[I];
  6365. if MenuItem.Visible then begin
  6366. if FMenuBitmaps.TryGetValue(MenuItem, mmi.hbmpItem) then
  6367. SetMenuItemInfo(ParentMenuItem.Handle, MenuItem.Command, False, mmi);
  6368. if MenuItem.Count > 0 then
  6369. ApplyMenuBitmaps(MenuItem);
  6370. end;
  6371. end;
  6372. end;
  6373. procedure TMainForm.StartProcess;
  6374. var
  6375. RunFilename, RunParameters, WorkingDir: String;
  6376. Info: TShellExecuteInfo;
  6377. SaveFocusWindow: HWND;
  6378. WindowList: Pointer;
  6379. ShellExecuteResult: BOOL;
  6380. ErrorCode: DWORD;
  6381. begin
  6382. if FDebugTarget = dtUninstall then begin
  6383. if FUninstExe = '' then
  6384. raise Exception.Create(SCompilerNeedUninstExe);
  6385. RunFilename := FUninstExe;
  6386. end else begin
  6387. if FCompiledExe = '' then
  6388. raise Exception.Create(SCompilerNeedCompiledExe);
  6389. RunFilename := FCompiledExe;
  6390. end;
  6391. RunParameters := Format('/DEBUGWND=$%x ', [Handle]) + FRunParameters;
  6392. ResetAllMemosLineState;
  6393. DebugOutputList.Clear;
  6394. SendMessage(DebugOutputList.Handle, LB_SETHORIZONTALEXTENT, 0, 0);
  6395. DebugCallStackList.Clear;
  6396. SendMessage(DebugCallStackList.Handle, LB_SETHORIZONTALEXTENT, 0, 0);
  6397. if not (OutputTabSet.TabIndex in [tiDebugOutput, tiDebugCallStack]) then
  6398. OutputTabSet.TabIndex := tiDebugOutput;
  6399. SetStatusPanelVisible(True);
  6400. FillChar(Info, SizeOf(Info), 0);
  6401. Info.cbSize := SizeOf(Info);
  6402. Info.fMask := SEE_MASK_FLAG_NO_UI or SEE_MASK_FLAG_DDEWAIT or
  6403. SEE_MASK_NOCLOSEPROCESS or SEE_MASK_NOZONECHECKS;
  6404. Info.Wnd := Handle;
  6405. if FOptions.RunAsDifferentUser then
  6406. Info.lpVerb := 'runas'
  6407. else
  6408. Info.lpVerb := 'open';
  6409. Info.lpFile := PChar(RunFilename);
  6410. Info.lpParameters := PChar(RunParameters);
  6411. WorkingDir := PathExtractDir(RunFilename);
  6412. Info.lpDirectory := PChar(WorkingDir);
  6413. Info.nShow := SW_SHOWNORMAL;
  6414. { When the RunAsDifferentUser option is enabled, it's this process that
  6415. waits on the UAC dialog, not Setup(Ldr), so we need to disable windows to
  6416. prevent the user from clicking other things before the UAC dialog is
  6417. dismissed (which is definitely a possibility if the "Switch to the secure
  6418. desktop when prompting for elevation" setting is disabled in Group
  6419. Policy). }
  6420. SaveFocusWindow := GetFocus;
  6421. WindowList := DisableTaskWindows(Handle);
  6422. try
  6423. { Also temporarily remove the focus since a disabled window's children can
  6424. still receive keystrokes. This is needed if Windows doesn't switch to
  6425. the secure desktop immediately and instead shows a flashing taskbar
  6426. button that the user must click (which happened on Windows Vista; I'm
  6427. unable to reproduce it on Windows 11). }
  6428. Windows.SetFocus(0);
  6429. ShellExecuteResult := ShellExecuteEx(@Info);
  6430. ErrorCode := GetLastError;
  6431. finally
  6432. EnableTaskWindows(WindowList);
  6433. Windows.SetFocus(SaveFocusWindow);
  6434. end;
  6435. if not ShellExecuteResult then begin
  6436. { Don't display error message if user clicked Cancel at UAC dialog }
  6437. if ErrorCode = ERROR_CANCELLED then
  6438. Abort;
  6439. raise Exception.CreateFmt(SCompilerExecuteSetupError2, [RunFilename,
  6440. ErrorCode, Win32ErrorString(ErrorCode)]);
  6441. end;
  6442. FDebugging := True;
  6443. FPaused := False;
  6444. FProcessHandle := Info.hProcess;
  6445. CheckIfRunningTimer.Enabled := True;
  6446. UpdateRunMenu;
  6447. UpdateCaption;
  6448. DebugLogMessage('*** ' + DebugTargetStrings[FDebugTarget] + ' started');
  6449. end;
  6450. procedure TMainForm.CompileIfNecessary;
  6451. function UnopenedIncludedFileModifiedSinceLastCompile: Boolean;
  6452. var
  6453. IncludedFile: TIncludedFile;
  6454. NewTime: TFileTime;
  6455. begin
  6456. Result := False;
  6457. for IncludedFile in FIncludedFiles do begin
  6458. if (IncludedFile.Memo = nil) and IncludedFile.HasLastWriteTime and
  6459. GetLastWriteTimeOfFile(IncludedFile.Filename, @NewTime) and
  6460. (CompareFileTime(IncludedFile.LastWriteTime, NewTime) <> 0) then begin
  6461. Result := True;
  6462. Exit;
  6463. end;
  6464. end;
  6465. end;
  6466. begin
  6467. CheckIfTerminated;
  6468. { Display warning if the user modified the script while running - does not support unopened included files }
  6469. if FDebugging and FModifiedAnySinceLastCompileAndGo then begin
  6470. if MsgBox('The changes you made will not take effect until you ' +
  6471. 're-compile.' + SNewLine2 + 'Continue running anyway?',
  6472. SCompilerFormCaption, mbError, MB_YESNO) <> IDYES then
  6473. Abort;
  6474. FModifiedAnySinceLastCompileAndGo := False;
  6475. { The process may have terminated while the message box was up; check,
  6476. and if it has, we want to recompile below }
  6477. CheckIfTerminated;
  6478. end;
  6479. if not FDebugging and (FModifiedAnySinceLastCompile or UnopenedIncludedFileModifiedSinceLastCompile) then
  6480. CompileFile('', False);
  6481. end;
  6482. procedure TMainForm.Go(AStepMode: TStepMode);
  6483. begin
  6484. CompileIfNecessary;
  6485. FStepMode := AStepMode;
  6486. HideError;
  6487. SetStepLine(FStepMemo, -1);
  6488. if FDebugging then begin
  6489. if FPaused then begin
  6490. FPaused := False;
  6491. UpdateRunMenu;
  6492. UpdateCaption;
  6493. if DebugCallStackList.Items.Count > 0 then begin
  6494. DebugCallStackList.Clear;
  6495. SendMessage(DebugCallStackList.Handle, LB_SETHORIZONTALEXTENT, 0, 0);
  6496. DebugCallStackList.Update;
  6497. end;
  6498. { Tell it to continue }
  6499. SendNotifyMessage(FDebugClientWnd, WM_DebugClient_Continue,
  6500. Ord(AStepMode = smStepOver), 0);
  6501. end;
  6502. end
  6503. else
  6504. StartProcess;
  6505. end;
  6506. function TMainForm.EvaluateConstant(const S: String;
  6507. out Output: String): Integer;
  6508. begin
  6509. { This is about evaluating constants like 'app' and not [Code] variables }
  6510. FReplyString := '';
  6511. Result := SendCopyDataMessageStr(FDebugClientWnd, Handle,
  6512. CD_DebugClient_EvaluateConstantW, S);
  6513. if Result > 0 then
  6514. Output := FReplyString;
  6515. end;
  6516. function TMainForm.EvaluateVariableEntry(const DebugEntry: PVariableDebugEntry;
  6517. out Output: String): Integer;
  6518. begin
  6519. FReplyString := '';
  6520. Result := SendCopyDataMessage(FDebugClientWnd, Handle, CD_DebugClient_EvaluateVariableEntry,
  6521. DebugEntry, SizeOf(DebugEntry^));
  6522. if Result > 0 then
  6523. Output := FReplyString;
  6524. end;
  6525. procedure TMainForm.RRunClick(Sender: TObject);
  6526. begin
  6527. Go(smRun);
  6528. end;
  6529. procedure TMainForm.RParametersClick(Sender: TObject);
  6530. begin
  6531. ReadMRUParametersList;
  6532. InputQueryCombo('Run Parameters', 'Command line parameters for ' + DebugTargetStrings[dtSetup] +
  6533. ' and ' + DebugTargetStrings[dtUninstall] + ':', FRunParameters, FMRUParametersList);
  6534. if FRunParameters <> '' then
  6535. ModifyMRUParametersList(FRunParameters, True);
  6536. end;
  6537. procedure TMainForm.RPauseClick(Sender: TObject);
  6538. begin
  6539. if FDebugging and not FPaused then begin
  6540. if FStepMode <> smStepInto then begin
  6541. FStepMode := smStepInto;
  6542. UpdateCaption;
  6543. end
  6544. else
  6545. MsgBox('A pause is already pending.', SCompilerFormCaption, mbError,
  6546. MB_OK);
  6547. end;
  6548. end;
  6549. procedure TMainForm.RRunToCursorClick(Sender: TObject);
  6550. function GetDebugEntryFromMemoAndLineNumber(Memo: TIDEScintFileEdit; LineNumber: Integer;
  6551. var DebugEntry: TDebugEntry): Boolean;
  6552. var
  6553. I: Integer;
  6554. begin
  6555. Result := False;
  6556. for I := 0 to FDebugEntriesCount-1 do begin
  6557. if (FDebugEntries[I].FileIndex = Memo.CompilerFileIndex) and
  6558. (FDebugEntries[I].LineNumber = LineNumber) then begin
  6559. DebugEntry := FDebugEntries[I];
  6560. Result := True;
  6561. Break;
  6562. end;
  6563. end;
  6564. end;
  6565. begin
  6566. CompileIfNecessary;
  6567. if not GetDebugEntryFromMemoAndLineNumber((FActiveMemo as TIDEScintFileEdit), FActiveMemo.CaretLine, FRunToCursorPoint) then begin
  6568. MsgBox('No code was generated for the current line.', SCompilerFormCaption,
  6569. mbError, MB_OK);
  6570. Exit;
  6571. end;
  6572. Go(smRunToCursor);
  6573. end;
  6574. procedure TMainForm.RStepIntoClick(Sender: TObject);
  6575. begin
  6576. Go(smStepInto);
  6577. end;
  6578. procedure TMainForm.RStepOutClick(Sender: TObject);
  6579. begin
  6580. if FPausedAtCodeLine then
  6581. Go(smStepOut)
  6582. else
  6583. Go(smStepInto);
  6584. end;
  6585. procedure TMainForm.RStepOverClick(Sender: TObject);
  6586. begin
  6587. Go(smStepOver);
  6588. end;
  6589. procedure TMainForm.RTerminateClick(Sender: TObject);
  6590. var
  6591. S, Dir: String;
  6592. begin
  6593. S := 'This will unconditionally terminate the running ' +
  6594. DebugTargetStrings[FDebugTarget] + ' process. Continue?';
  6595. if FDebugTarget = dtSetup then
  6596. S := S + #13#10#13#10'Note that if ' + DebugTargetStrings[FDebugTarget] + ' ' +
  6597. 'is currently in the installation phase, any changes made to the ' +
  6598. 'system thus far will not be undone, nor will uninstall data be written.';
  6599. if MsgBox(S, 'Terminate', mbConfirmation, MB_YESNO or MB_DEFBUTTON2) <> IDYES then
  6600. Exit;
  6601. CheckIfTerminated;
  6602. if FDebugging then begin
  6603. DebugLogMessage('*** Terminating process');
  6604. Win32Check(TerminateProcess(FDebugClientProcessHandle, 6));
  6605. if (WaitForSingleObject(FDebugClientProcessHandle, 5000) <> WAIT_TIMEOUT) and
  6606. (FTempDir <> '') then begin
  6607. Dir := FTempDir;
  6608. FTempDir := '';
  6609. DebugLogMessage('*** Removing left-over temporary directory: ' + Dir);
  6610. { Sleep for a bit to allow files to be unlocked by Windows,
  6611. otherwise it fails intermittently (with Hyper-Threading, at least) }
  6612. Sleep(50);
  6613. if not DeleteDirTree(Dir) and DirExists(Dir) then
  6614. DebugLogMessage('*** Failed to remove temporary directory');
  6615. end;
  6616. DebuggingStopped(True);
  6617. end;
  6618. end;
  6619. procedure TMainForm.REvaluateClick(Sender: TObject);
  6620. var
  6621. Output: String;
  6622. begin
  6623. if InputQuery('Evaluate', 'Constant to evaluate (e.g., "{app}"):',
  6624. FLastEvaluateConstantText) then begin
  6625. case EvaluateConstant(FLastEvaluateConstantText, Output) of
  6626. 1: MsgBox(Output, 'Evaluate Result', mbInformation, MB_OK);
  6627. 2: MsgBox(Output, 'Evaluate Error', mbError, MB_OK);
  6628. else
  6629. MsgBox('An unknown error occurred.', 'Evaluate Error', mbError, MB_OK);
  6630. end;
  6631. end;
  6632. end;
  6633. procedure TMainForm.CheckIfRunningTimerTimer(Sender: TObject);
  6634. begin
  6635. { In cases of normal Setup termination, we receive a WM_Debugger_Goodbye
  6636. message. But in case we don't get that, use a timer to periodically check
  6637. if the process is no longer running. }
  6638. CheckIfTerminated;
  6639. end;
  6640. procedure TMainForm.POutputListCopyClick(Sender: TObject);
  6641. var
  6642. ListBox: TListBox;
  6643. Text: String;
  6644. I: Integer;
  6645. begin
  6646. if CompilerOutputList.Visible then
  6647. ListBox := CompilerOutputList
  6648. else if DebugOutputList.Visible then
  6649. ListBox := DebugOutputList
  6650. else if DebugCallStackList.Visible then
  6651. ListBox := DebugCallStackList
  6652. else
  6653. ListBox := FindResultsList;
  6654. Text := '';
  6655. if ListBox.SelCount > 0 then begin
  6656. for I := 0 to ListBox.Items.Count-1 do begin
  6657. if ListBox.Selected[I] then begin
  6658. if Text <> '' then
  6659. Text := Text + SNewLine;
  6660. Text := Text + ListBox.Items[I];
  6661. end;
  6662. end;
  6663. end;
  6664. Clipboard.AsText := Text;
  6665. end;
  6666. procedure TMainForm.POutputListSelectAllClick(Sender: TObject);
  6667. var
  6668. ListBox: TListBox;
  6669. I: Integer;
  6670. begin
  6671. if CompilerOutputList.Visible then
  6672. ListBox := CompilerOutputList
  6673. else if DebugOutputList.Visible then
  6674. ListBox := DebugOutputList
  6675. else if DebugCallStackList.Visible then
  6676. ListBox := DebugCallStackList
  6677. else
  6678. ListBox := FindResultsList;
  6679. ListBox.Items.BeginUpdate;
  6680. try
  6681. for I := 0 to ListBox.Items.Count-1 do
  6682. ListBox.Selected[I] := True;
  6683. finally
  6684. ListBox.Items.EndUpdate;
  6685. end;
  6686. end;
  6687. procedure TMainForm.OutputListKeyDown(Sender: TObject; var Key: Word;
  6688. Shift: TShiftState);
  6689. begin
  6690. if Shift = [ssCtrl] then begin
  6691. if Key = Ord('C') then
  6692. POutputListCopyClick(Sender)
  6693. else if Key = Ord('A') then
  6694. POutputListSelectAllClick(Sender);
  6695. end;
  6696. end;
  6697. procedure TMainForm.AppOnIdle(Sender: TObject; var Done: Boolean);
  6698. begin
  6699. { For an explanation of this, see the comment where HandleMessage is called }
  6700. if FCompiling then
  6701. Done := False;
  6702. FBecameIdle := True;
  6703. end;
  6704. procedure TMainForm.EGotoClick(Sender: TObject);
  6705. var
  6706. S: String;
  6707. L: Integer;
  6708. begin
  6709. S := IntToStr(FActiveMemo.CaretLine + 1);
  6710. if InputQuery('Go to Line', 'Line number:', S) then begin
  6711. L := StrToIntDef(S, Low(L));
  6712. if L <> Low(L) then
  6713. FActiveMemo.CaretLine := L - 1;
  6714. end;
  6715. end;
  6716. procedure TMainForm.StatusBarClick(Sender: TObject);
  6717. begin
  6718. if MemosTabSet.Visible and (FHiddenFiles.Count > 0) then begin
  6719. var Point := SmallPointToPoint(TSmallPoint(GetMessagePos()));
  6720. var X := StatusBar.ScreenToClient(Point).X;
  6721. var W := 0;
  6722. for var I := 0 to StatusBar.Panels.Count-1 do begin
  6723. Inc(W, StatusBar.Panels[I].Width);
  6724. if X < W then begin
  6725. if I = spHiddenFilesCount then
  6726. (MemosTabSet.PopupMenu as TMainFormPopupMenu).Popup(Point.X, Point.Y);
  6727. Break;
  6728. end else if I = spHiddenFilesCount then
  6729. Break;
  6730. end;
  6731. end;
  6732. end;
  6733. procedure TMainForm.StatusBarCanvasDrawPanel(Canvas: TCanvas;
  6734. Panel: TStatusPanel; const Rect: TRect);
  6735. const
  6736. TP_DROPDOWNBUTTONGLYPH = 7;
  6737. TS_NORMAL = 1;
  6738. begin
  6739. case Panel.Index of
  6740. spHiddenFilesCount:
  6741. if MemosTabSet.Visible and (FHiddenFiles.Count > 0) then begin
  6742. var RText := Rect;
  6743. if FToolbarThemeData <> 0 then begin
  6744. Dec(RText.Right, RText.Bottom - RText.Top);
  6745. var RGlyph := Rect;
  6746. RGlyph.Left := RText.Right; { RGlyph is now a square }
  6747. DrawThemeBackground(FToolbarThemeData, Canvas.Handle, TP_DROPDOWNBUTTONGLYPH, TS_NORMAL, RGlyph, nil);
  6748. end;
  6749. var Color: TColor := FTheme.Colors[tcFore];
  6750. const LStyle = TStyleManager.ActiveStyle;
  6751. if not LStyle.IsSystemStyle then begin
  6752. const Details = LStyle.GetElementDetails(tsPane);
  6753. LStyle.GetElementColor(Details, ecTextColor, Color);
  6754. end;
  6755. Canvas.Font.Color := Color;
  6756. var S := Format('Tabs closed: %d', [FHiddenFiles.Count]);
  6757. Canvas.TextRect(RText, S, [tfCenter]);
  6758. end;
  6759. spCompileIcon:
  6760. if FCompiling then begin
  6761. var BuildImageList := FBuildImageList;
  6762. ImageList_Draw(BuildImageList.Handle, FBuildAnimationFrame, Canvas.Handle,
  6763. Rect.Left + ((Rect.Right - Rect.Left) - BuildImageList.Width) div 2,
  6764. Rect.Top + ((Rect.Bottom - Rect.Top) - BuildImageList.Height) div 2, ILD_NORMAL);
  6765. end;
  6766. spCompileProgress:
  6767. if FCompiling and (FProgressMax > 0) then begin
  6768. var R := Rect;
  6769. InflateRect(R, -2, -2);
  6770. var LStyle := StyleServices(Self);
  6771. if not LStyle.Enabled or LStyle.IsSystemStyle then
  6772. LStyle := nil;
  6773. if LStyle <> nil then begin
  6774. { See Vcl.ComCtrl's TProgressBarStyleHook.Paint, .PaintFrame, and .PaintBar }
  6775. var Details: TThemedElementDetails;
  6776. Details.Element := teProgress;
  6777. if LStyle.HasTransparentParts(Details) then
  6778. LStyle.DrawParentBackground(Handle, Canvas.Handle, Details, False, @R);
  6779. Details := LStyle.GetElementDetails(tpBar);
  6780. LStyle.DrawElement(Canvas.Handle, Details, R);
  6781. InflateRect(R, -1, -1);
  6782. const W = R.Width;
  6783. const Pos = Round(W * (FProgress / FProgressMax));
  6784. var FillR := R;
  6785. FillR.Right := FillR.Left + Pos;
  6786. Details := LStyle.GetElementDetails(tpChunk);
  6787. LStyle.DrawElement(Canvas.Handle, Details, FillR);
  6788. end else if FProgressThemeData = 0 then begin
  6789. { Border }
  6790. Canvas.Pen.Color := clBtnShadow;
  6791. Canvas.Brush.Style := bsClear;
  6792. Canvas.Rectangle(R);
  6793. InflateRect(R, -1, -1);
  6794. { Filled part }
  6795. var SaveRight := R.Right;
  6796. R.Right := R.Left + MulDiv(FProgress, R.Right - R.Left,
  6797. FProgressMax);
  6798. Canvas.Brush.Color := clHighlight;
  6799. Canvas.FillRect(R);
  6800. { Unfilled part }
  6801. R.Left := R.Right;
  6802. R.Right := SaveRight;
  6803. Canvas.Brush.Color := clBtnFace;
  6804. Canvas.FillRect(R);
  6805. end else begin
  6806. DrawThemeBackground(FProgressThemeData, Canvas.Handle,
  6807. PP_BAR, 0, R, nil);
  6808. { PP_FILL drawing on Windows 11 (and probably 10) is bugged: when
  6809. the width of the green bar is less than ~25 pixels, the bar is
  6810. drawn over the left border. The same thing happens with
  6811. TProgressBar, so I don't think the API is being used incorrectly.
  6812. Work around the bug by passing a clipping rectangle that excludes
  6813. the left edge when running on Windows 10/11 only. (I don't know if
  6814. earlier versions need it, or if later versions will fix it.) }
  6815. var CR := R;
  6816. if (Win32MajorVersion = 10) and (Win32MinorVersion = 0) then
  6817. Inc(CR.Left); { does this need to be DPI-scaled? }
  6818. R.Right := R.Left + MulDiv(FProgress, R.Right - R.Left,
  6819. FProgressMax);
  6820. DrawThemeBackground(FProgressThemeData, Canvas.Handle,
  6821. PP_FILL, PBFS_NORMAL, R, @CR);
  6822. end;
  6823. end;
  6824. end;
  6825. end;
  6826. procedure TMainForm.StatusBarDrawPanel(StatusBar: TStatusBar;
  6827. Panel: TStatusPanel; const Rect: TRect);
  6828. begin
  6829. StatusBarCanvasDrawPanel(StatusBar.Canvas, Panel, Rect);
  6830. end;
  6831. procedure TMainForm.InvalidateStatusPanel(const Index: Integer);
  6832. var
  6833. R: TRect;
  6834. begin
  6835. { For some reason, the VCL doesn't offer a method for this... }
  6836. if SendMessage(StatusBar.Handle, SB_GETRECT, Index, LPARAM(@R)) <> 0 then begin
  6837. InflateRect(R, -1, -1);
  6838. InvalidateRect(StatusBar.Handle, @R, True);
  6839. end;
  6840. end;
  6841. procedure TMainForm.UpdateCompileStatusPanels(const AProgress,
  6842. AProgressMax: Cardinal; const ASecondsRemaining: Integer;
  6843. const ABytesCompressedPerSecond: Cardinal);
  6844. begin
  6845. var CurTick := GetTickCount;
  6846. var LastTick := FLastAnimationTick;
  6847. FLastAnimationTick := CurTick;
  6848. { Icon and text panels - updated every 500ms }
  6849. if CurTick div 500 <> LastTick div 500 then begin
  6850. InvalidateStatusPanel(spCompileIcon);
  6851. FBuildAnimationFrame := (FBuildAnimationFrame + 1) mod 4;
  6852. if ASecondsRemaining >= 0 then
  6853. StatusBar.Panels[spExtraStatus].Text := Format(
  6854. ' Estimated time remaining: %.2d%s%.2d%s%.2d Average KB/sec: %.0n',
  6855. [(ASecondsRemaining div 60) div 60, FormatSettings.TimeSeparator,
  6856. (ASecondsRemaining div 60) mod 60, FormatSettings.TimeSeparator,
  6857. ASecondsRemaining mod 60, ABytesCompressedPerSecond / 1024])
  6858. else
  6859. StatusBar.Panels[spExtraStatus].Text := '';
  6860. end;
  6861. { Progress panel and taskbar progress bar - updated every 100ms }
  6862. if (CurTick div 100 <> LastTick div 100) and
  6863. ((FProgress <> AProgress) or (FProgressMax <> AProgressMax)) then begin
  6864. FProgress := AProgress;
  6865. FProgressMax := AProgressMax;
  6866. InvalidateStatusPanel(spCompileProgress);
  6867. { The taskbar progress updates are slow (on Windows 11). Limiting the
  6868. range to 64 instead of 1024 improved compression KB/sec by about 4%
  6869. (9000 to 9400) when the rate limit above is disabled. }
  6870. var NewValue: Cardinal := 1; { must be at least 1 for progress bar to show }
  6871. if AProgressMax > 0 then begin
  6872. { Not using MulDiv here to avoid rounding up }
  6873. NewValue := (AProgress * 64) div AProgressMax;
  6874. if NewValue = 0 then
  6875. NewValue := 1;
  6876. end;
  6877. { Don't call the function if the value hasn't changed, just in case there's
  6878. a performance penalty. (There doesn't appear to be on Windows 11.) }
  6879. if FTaskbarProgressValue <> NewValue then begin
  6880. FTaskbarProgressValue := NewValue;
  6881. SetAppTaskbarProgressValue(NewValue, 64);
  6882. end;
  6883. end;
  6884. end;
  6885. procedure TMainForm.WMSettingChange(var Message: TMessage);
  6886. begin
  6887. inherited;
  6888. if (FTheme.Typ <> ttClassic) and IsWindows10 and (Message.LParam <> 0) and (StrIComp(PChar(Message.LParam), 'ImmersiveColorSet') = 0) then begin
  6889. FOptions.ThemeType := GetDefaultThemeType;
  6890. UpdateTheme;
  6891. end;
  6892. for var Memo in FMemos do
  6893. Memo.SettingChange(Message);
  6894. end;
  6895. procedure TMainForm.WMThemeChanged(var Message: TMessage);
  6896. begin
  6897. { Don't Run to Cursor into this function, it will interrupt up the theme change }
  6898. UpdateThemeData(True);
  6899. inherited;
  6900. end;
  6901. procedure TMainForm.WMUAHDrawMenu(var Message: TMessage);
  6902. begin
  6903. if FTheme.Dark then begin
  6904. var MenuBarInfo: TMenuBarInfo;
  6905. MenuBarInfo.cbSize := SizeOf(MenuBarInfo);
  6906. GetMenuBarInfo(Handle, Integer(OBJID_MENU), 0, MenuBarInfo);
  6907. var WindowRect: TRect;
  6908. GetWindowRect(Handle, WindowRect);
  6909. var Rect := MenuBarInfo.rcBar;
  6910. OffsetRect(Rect, -WindowRect.Left, -WindowRect.Top);
  6911. var UAHMenu := PUAHMenu(Message.lParam);
  6912. FillRect(UAHMenu.hdc, Rect, FMenuDarkBackgroundBrush.Handle);
  6913. end else
  6914. inherited;
  6915. end;
  6916. procedure TMainForm.WMUAHDrawMenuItem(var Message: TMessage);
  6917. const
  6918. ODS_NOACCEL = $100;
  6919. DTT_TEXTCOLOR = 1;
  6920. MENU_BARITEM = 8;
  6921. MBI_NORMAL = 1;
  6922. var
  6923. Buffer: array of Char;
  6924. begin
  6925. if FTheme.Dark then begin
  6926. var UAHDrawMenuItem := PUAHDrawMenuItem(Message.lParam);
  6927. var MenuItemInfo: TMenuItemInfo;
  6928. MenuItemInfo.cbSize := SizeOf(MenuItemInfo);
  6929. MenuItemInfo.fMask := MIIM_STRING;
  6930. MenuItemInfo.dwTypeData := nil;
  6931. GetMenuItemInfo(UAHDrawMenuItem.um.hmenu, UAHDrawMenuItem.umi.iPosition, True, MenuItemInfo);
  6932. Inc(MenuItemInfo.cch);
  6933. SetLength(Buffer, MenuItemInfo.cch);
  6934. MenuItemInfo.dwTypeData := @Buffer[0];
  6935. GetMenuItemInfo(UAHDrawMenuItem.um.hmenu, UAHDrawMenuItem.umi.iPosition, True, MenuItemInfo);
  6936. var dwFlags: DWORD := DT_CENTER or DT_SINGLELINE or DT_VCENTER;
  6937. if (UAHDrawMenuItem.dis.itemState and ODS_NOACCEL) <> 0 then
  6938. dwFlags := dwFlags or DT_HIDEPREFIX;
  6939. var Inactive := (UAHDrawMenuItem.dis.itemState and ODS_INACTIVE) <> 0;
  6940. var TextColor: TThemeColor;
  6941. if Inactive then
  6942. TextColor := tcMarginFore
  6943. else
  6944. TextColor := tcFore;
  6945. var opts: TDTTOpts;
  6946. opts.dwSize := SizeOf(opts);
  6947. opts.dwFlags := DTT_TEXTCOLOR;
  6948. opts.crText := FTheme.Colors[TextColor];
  6949. var Brush: HBrush;
  6950. { ODS_HOTLIGHT can be set when the menu is inactive so we check Inactive as well. }
  6951. if not Inactive and ((UAHDrawMenuItem.dis.itemState and (ODS_HOTLIGHT or ODS_SELECTED)) <> 0) then
  6952. Brush := FMenuDarkHotOrSelectedBrush.Handle
  6953. else
  6954. Brush := FMenuDarkBackgroundBrush.Handle;
  6955. FillRect(UAHDrawMenuItem.um.hdc, UAHDrawMenuItem.dis.rcItem, Brush);
  6956. DrawThemeTextEx(FMenuThemeData, UAHDrawMenuItem.um.hdc, MENU_BARITEM, MBI_NORMAL, MenuItemInfo.dwTypeData, MenuItemInfo.cch, dwFlags, @UAHDrawMenuItem.dis.rcItem, opts);
  6957. end else
  6958. inherited;
  6959. end;
  6960. { Should be removed if the main menu ever gets removed }
  6961. procedure TMainForm.UAHDrawMenuBottomLine;
  6962. begin
  6963. if not (csDestroying in ComponentState) and (FTheme <> nil) and FTheme.Dark then begin
  6964. var ClientRect: TRect;
  6965. Windows.GetClientRect(Handle, ClientRect);
  6966. MapWindowPoints(Handle, 0, ClientRect, 2);
  6967. var WindowRect: TRect;
  6968. GetWindowRect(Handle, WindowRect);
  6969. var Rect := ClientRect;
  6970. OffsetRect(Rect, -WindowRect.Left, -WindowRect.Top);
  6971. Rect.Bottom := Rect.Top;
  6972. Dec(Rect.Top);
  6973. var DC := GetWindowDC(Handle);
  6974. FillRect(DC, Rect, FMenuDarkBackgroundBrush.Handle);
  6975. ReleaseDC(Handle, DC);
  6976. end;
  6977. end;
  6978. procedure TMainForm.WMNCActivate(var Message: TMessage);
  6979. begin
  6980. inherited;
  6981. UAHDrawMenuBottomLine;
  6982. end;
  6983. procedure TMainForm.WMNCPaint(var Message: TMessage);
  6984. begin
  6985. inherited;
  6986. UAHDrawMenuBottomLine;
  6987. end;
  6988. procedure TMainForm.RTargetClick(Sender: TObject);
  6989. var
  6990. NewTarget: TDebugTarget;
  6991. begin
  6992. if (Sender = RTargetSetup) or (Sender = TargetSetupButton) then
  6993. NewTarget := dtSetup
  6994. else
  6995. NewTarget := dtUninstall;
  6996. if (FDebugTarget <> NewTarget) and (not FDebugging or AskToDetachDebugger) then
  6997. FDebugTarget := NewTarget;
  6998. { Update always even if the user decided not to switch so the states are restored }
  6999. UpdateTargetMenu;
  7000. end;
  7001. procedure TMainForm.AppOnActivate(Sender: TObject);
  7002. const
  7003. ReloadMessages: array[Boolean] of String = (
  7004. 'The %s file has been modified outside of the source editor.' + SNewLine2 +
  7005. 'Do you want to reload the file?',
  7006. 'The %s file has been modified outside of the source editor. Changes have ' +
  7007. 'also been made in the source editor.' + SNewLine2 + 'Do you want to ' +
  7008. 'reload the file and lose the changes made in the source editor?');
  7009. var
  7010. Memo: TIDEScintFileEdit;
  7011. NewTime: TFileTime;
  7012. Changed: Boolean;
  7013. begin
  7014. for Memo in FFileMemos do begin
  7015. if (Memo.Filename = '') or not Memo.Used then
  7016. Continue;
  7017. { See if the file has been modified outside the editor }
  7018. Changed := False;
  7019. if GetLastWriteTimeOfFile(Memo.Filename, @NewTime) then begin
  7020. if CompareFileTime(Memo.FileLastWriteTime, NewTime) <> 0 then begin
  7021. Memo.FileLastWriteTime := NewTime;
  7022. Changed := True;
  7023. end;
  7024. end;
  7025. { If it has been, offer to reload it }
  7026. if Changed then begin
  7027. if IsWindowEnabled(Handle) then begin
  7028. if (not Memo.Modified and FOptions.Autoreload) or
  7029. (MsgBox(Format(ReloadMessages[Memo.Modified], [Memo.Filename]),
  7030. SCompilerFormCaption, mbConfirmation, MB_YESNO) = IDYES) then
  7031. if ConfirmCloseFile(False) then begin
  7032. OpenFile(Memo, Memo.Filename, False, FOptions.UndoAfterReload);
  7033. if Memo = FMainMemo then
  7034. Break; { Reloading the main script will also reload all include files }
  7035. end;
  7036. end
  7037. else begin
  7038. { When a modal dialog is up, don't offer to reload the file. Probably
  7039. not a good idea since the dialog might be manipulating the file. }
  7040. MsgBox('The ' + Memo.Filename + ' file has been modified outside ' +
  7041. 'of the source editor. You might want to reload it.',
  7042. SCompilerFormCaption, mbInformation, MB_OK);
  7043. end;
  7044. end;
  7045. end;
  7046. end;
  7047. procedure TMainForm.CompilerOutputListDrawItem(Control: TWinControl;
  7048. Index: Integer; Rect: TRect; State: TOwnerDrawState);
  7049. const
  7050. ThemeColors: array [TStatusMessageKind] of TThemeColor = (tcGreen, tcFore, tcOrange, tcRed);
  7051. var
  7052. Canvas: TCanvas;
  7053. S: String;
  7054. StatusMessageKind: TStatusMessageKind;
  7055. begin
  7056. Canvas := CompilerOutputList.Canvas;
  7057. S := CompilerOutputList.Items[Index];
  7058. Canvas.FillRect(Rect);
  7059. Inc(Rect.Left, 2);
  7060. if FOptions.ColorizeCompilerOutput and not (odSelected in State) then begin
  7061. StatusMessageKind := TStatusMessageKind(CompilerOutputList.Items.Objects[Index]);
  7062. Canvas.Font.Color := FTheme.Colors[ThemeColors[StatusMessageKind]];
  7063. end;
  7064. Canvas.TextOut(Rect.Left, Rect.Top, S);
  7065. end;
  7066. procedure TMainForm.DebugOutputListDrawItem(Control: TWinControl;
  7067. Index: Integer; Rect: TRect; State: TOwnerDrawState);
  7068. var
  7069. Canvas: TCanvas;
  7070. S: String;
  7071. begin
  7072. Canvas := DebugOutputList.Canvas;
  7073. S := DebugOutputList.Items[Index];
  7074. Canvas.FillRect(Rect);
  7075. Inc(Rect.Left, 2);
  7076. if (S <> '') and (S[1] = #9) then
  7077. Canvas.TextOut(Rect.Left + FDebugLogListTimestampsWidth, Rect.Top, Copy(S, 2, Maxint))
  7078. else begin
  7079. if (Length(S) > 20) and (S[18] = '-') and (S[19] = '-') and (S[20] = ' ') then begin
  7080. { Draw lines that begin with '-- ' (like '-- File entry --') in bold }
  7081. Canvas.TextOut(Rect.Left, Rect.Top, Copy(S, 1, 17));
  7082. Canvas.Font.Style := [fsBold];
  7083. Canvas.TextOut(Rect.Left + FDebugLogListTimestampsWidth, Rect.Top, Copy(S, 18, Maxint));
  7084. end else
  7085. Canvas.TextOut(Rect.Left, Rect.Top, S);
  7086. end;
  7087. end;
  7088. procedure TMainForm.DebugCallStackListDrawItem(Control: TWinControl; Index: Integer; Rect: TRect;
  7089. State: TOwnerDrawState);
  7090. var
  7091. Canvas: TCanvas;
  7092. S: String;
  7093. begin
  7094. Canvas := DebugCallStackList.Canvas;
  7095. S := DebugCallStackList.Items[Index];
  7096. Canvas.FillRect(Rect);
  7097. Inc(Rect.Left, 2);
  7098. Canvas.TextOut(Rect.Left, Rect.Top, S);
  7099. end;
  7100. procedure TMainForm.FindResultsListDblClick(Sender: TObject);
  7101. var
  7102. FindResult: TFindResult;
  7103. Memo: TIDEScintFileEdit;
  7104. I: Integer;
  7105. begin
  7106. I := FindResultsList.ItemIndex;
  7107. if I <> -1 then begin
  7108. FindResult := FindResultsList.Items.Objects[I] as TFindResult;
  7109. if FindResult <> nil then begin
  7110. for Memo in FFileMemos do begin
  7111. if Memo.Used and (PathCompare(Memo.Filename, FindResult.Filename) = 0) then begin
  7112. MoveCaretAndActivateMemo(Memo, FindResult.Line, True);
  7113. Memo.SelectAndEnsureVisible(FindResult.Range);
  7114. ActiveControl := Memo;
  7115. Exit;
  7116. end;
  7117. end;
  7118. MsgBox('File not opened.', SCompilerFormCaption, mbError, MB_OK);
  7119. end;
  7120. end;
  7121. end;
  7122. procedure TMainForm.FindResultsListDrawItem(Control: TWinControl; Index: Integer; Rect: TRect;
  7123. State: TOwnerDrawState);
  7124. var
  7125. Canvas: TCanvas;
  7126. S, S2: String;
  7127. FindResult: TFindResult;
  7128. StartI, EndI: Integer;
  7129. SaveColor: TColor;
  7130. begin
  7131. Canvas := FindResultsList.Canvas;
  7132. S := FindResultsList.Items[Index];
  7133. FindResult := FindResultsList.Items.Objects[Index] as TFindResult;
  7134. Canvas.FillRect(Rect);
  7135. Inc(Rect.Left, 2);
  7136. if FindResult = nil then begin
  7137. Canvas.Font.Style := [fsBold];
  7138. Canvas.TextOut(Rect.Left, Rect.Top, S);
  7139. end else if not (odSelected in State) then begin
  7140. StartI := FindResult.Range.StartPos - FindResult.LineStartPos + 1 + FindResult.PrefixStringLength;
  7141. EndI := FindResult.Range.EndPos - FindResult.LineStartPos + 1 + FindResult.PrefixStringLength;
  7142. if StartI > 1 then begin
  7143. Canvas.TextOut(Rect.Left, Rect.Top, Copy(S, 1, StartI-1));
  7144. Rect.Left := Canvas.PenPos.X;
  7145. end;
  7146. SaveColor := Canvas.Brush.Color;
  7147. if FTheme.Dark then
  7148. Canvas.Brush.Color := FTheme.Colors[tcRed]
  7149. else
  7150. Canvas.Brush.Color := FTheme.Colors[tcSelBack];
  7151. S2 := Copy(S, StartI, EndI-StartI);
  7152. Rect.Right := Rect.Left + Canvas.TextWidth(S2);
  7153. Canvas.TextRect(Rect, Rect.Left, Rect.Top, S2); { TextRect instead of TextOut to avoid a margin around the text }
  7154. if EndI <= Length(S) then begin
  7155. Canvas.Brush.Color := SaveColor;
  7156. S2 := Copy(S, EndI, MaxInt);
  7157. Rect.Left := Rect.Right;
  7158. Rect.Right := Rect.Left + Canvas.TextWidth(S2);
  7159. Canvas.TextRect(Rect, Rect.Left, Rect.Top, S2);
  7160. end;
  7161. end else
  7162. Canvas.TextOut(Rect.Left, Rect.Top, S)
  7163. end;
  7164. procedure TMainForm.OutputTabSetClick(Sender: TObject);
  7165. begin
  7166. case OutputTabSet.TabIndex of
  7167. tiCompilerOutput:
  7168. begin
  7169. CompilerOutputList.BringToFront;
  7170. CompilerOutputList.Visible := True;
  7171. DebugOutputList.Visible := False;
  7172. DebugCallStackList.Visible := False;
  7173. FindResultsList.Visible := False;
  7174. end;
  7175. tiDebugOutput:
  7176. begin
  7177. DebugOutputList.BringToFront;
  7178. DebugOutputList.Visible := True;
  7179. CompilerOutputList.Visible := False;
  7180. DebugCallStackList.Visible := False;
  7181. FindResultsList.Visible := False;
  7182. end;
  7183. tiDebugCallStack:
  7184. begin
  7185. DebugCallStackList.BringToFront;
  7186. DebugCallStackList.Visible := True;
  7187. CompilerOutputList.Visible := False;
  7188. DebugOutputList.Visible := False;
  7189. FindResultsList.Visible := False;
  7190. end;
  7191. tiFindResults:
  7192. begin
  7193. FindResultsList.BringToFront;
  7194. FindResultsList.Visible := True;
  7195. CompilerOutputList.Visible := False;
  7196. DebugOutputList.Visible := False;
  7197. DebugCallStackList.Visible := False;
  7198. end;
  7199. end;
  7200. end;
  7201. procedure TMainForm.ToggleBreakPoint(Line: Integer);
  7202. var
  7203. Memo: TIDEScintFileEdit;
  7204. I: Integer;
  7205. begin
  7206. Memo := FActiveMemo as TIDEScintFileEdit;
  7207. I := Memo.BreakPoints.IndexOf(Line);
  7208. if I = -1 then
  7209. Memo.BreakPoints.Add(Line)
  7210. else
  7211. Memo.BreakPoints.Delete(I);
  7212. UpdateLineMarkers(Memo, Line);
  7213. BuildAndSaveBreakPointLines(Memo);
  7214. end;
  7215. procedure TMainForm.MemoMarginClick(Sender: TObject; MarginNumber: Integer;
  7216. Line: Integer);
  7217. begin
  7218. if (MarginNumber = 1) and RToggleBreakPoint.Enabled then
  7219. ToggleBreakPoint(Line);
  7220. end;
  7221. procedure TMainForm.MemoMarginRightClick(Sender: TObject; MarginNumber: Integer;
  7222. Line: Integer);
  7223. begin
  7224. if MarginNumber = 1 then begin
  7225. var Point := SmallPointToPoint(TSmallPoint(GetMessagePos()));
  7226. var PopupMenu := TMainFormPopupMenu.Create(Self, BreakPointsPopupMenu);
  7227. try
  7228. PopupMenu.Popup(Point.X, Point.Y);
  7229. finally
  7230. PopupMenu.Free;
  7231. end;
  7232. end;
  7233. end;
  7234. procedure TMainForm.RToggleBreakPointClick(Sender: TObject);
  7235. begin
  7236. ToggleBreakPoint(FActiveMemo.CaretLine);
  7237. end;
  7238. procedure TMainForm.RDeleteBreakPointsClick(Sender: TObject);
  7239. begin
  7240. { Also see AnyMemoHasBreakPoint }
  7241. for var Memo in FFileMemos do begin
  7242. if Memo.Used and (Memo.BreakPoints.Count > 0) then begin
  7243. for var I := Memo.BreakPoints.Count-1 downto 0 do begin
  7244. var Line := Memo.BreakPoints[I];
  7245. Memo.BreakPoints.Delete(I);
  7246. UpdateLineMarkers(Memo, Line);
  7247. end;
  7248. BuildAndSaveBreakPointLines(Memo);
  7249. end;
  7250. end;
  7251. end;
  7252. procedure TMainForm.UpdateFindResult(const FindResult: TFindResult; const ItemIndex: Integer;
  7253. const NewLine, NewLineStartPos: Integer);
  7254. begin
  7255. { Also see FindInFilesDialogFind }
  7256. const OldPrefix = Format(' Line %d: ', [FindResult.Line+1]);
  7257. FindResult.Line := NewLine;
  7258. const NewPrefix = Format(' Line %d: ', [FindResult.Line+1]);
  7259. FindResultsList.Items[ItemIndex] := NewPrefix + Copy(FindResultsList.Items[ItemIndex], Length(OldPrefix)+1, MaxInt);
  7260. FindResult.PrefixStringLength := Length(NewPrefix);
  7261. const PosChange = NewLineStartPos - FindResult.LineStartPos;
  7262. FindResult.LineStartPos := NewLineStartPos;
  7263. FindResult.Range.StartPos := FindResult.Range.StartPos + PosChange;
  7264. FindResult.Range.EndPos := FindResult.Range.EndPos + PosChange;
  7265. end;
  7266. procedure TMainForm.MemoLinesInserted(Memo: TIDEScintFileEdit; FirstLine, Count: integer);
  7267. begin
  7268. for var I := 0 to FDebugEntriesCount-1 do
  7269. if (FDebugEntries[I].FileIndex = Memo.CompilerFileIndex) and
  7270. (FDebugEntries[I].LineNumber >= FirstLine) then
  7271. Inc(FDebugEntries[I].LineNumber, Count);
  7272. for var I := FindResultsList.Items.Count-1 downto 0 do begin
  7273. const FindResult = FindResultsList.Items.Objects[I] as TFindResult;
  7274. if FindResult <> nil then begin
  7275. if (PathCompare(FindResult.Filename, Memo.Filename) = 0) and
  7276. (FindResult.Line >= FirstLine) then begin
  7277. const NewLine = FindResult.Line + Count;
  7278. UpdateFindResult(FindResult, I, NewLine, Memo.GetPositionFromLine(NewLine));
  7279. end;
  7280. end;
  7281. end;
  7282. if Assigned(Memo.LineState) and (FirstLine < Memo.LineStateCount) then begin
  7283. { Grow FStateLine if necessary }
  7284. var GrowAmount := (Memo.LineStateCount + Count) - Memo.LineStateCapacity;
  7285. if GrowAmount > 0 then begin
  7286. if GrowAmount < LineStateGrowAmount then
  7287. GrowAmount := LineStateGrowAmount;
  7288. ReallocMem(Memo.LineState, SizeOf(TLineState) * (Memo.LineStateCapacity + GrowAmount));
  7289. Inc(Memo.LineStateCapacity, GrowAmount);
  7290. end;
  7291. { Shift existing line states and clear the new ones }
  7292. for var I := Memo.LineStateCount-1 downto FirstLine do
  7293. Memo.LineState[I + Count] := Memo.LineState[I];
  7294. for var I := FirstLine to FirstLine + Count - 1 do
  7295. Memo.LineState[I] := lnUnknown;
  7296. Inc(Memo.LineStateCount, Count);
  7297. end;
  7298. if Memo.StepLine >= FirstLine then
  7299. Inc(Memo.StepLine, Count);
  7300. if Memo.ErrorLine >= FirstLine then
  7301. Inc(Memo.ErrorLine, Count);
  7302. var BreakPointsChanged := False;
  7303. for var I := 0 to Memo.BreakPoints.Count-1 do begin
  7304. const Line = Memo.BreakPoints[I];
  7305. if Line >= FirstLine then begin
  7306. Memo.BreakPoints[I] := Line + Count;
  7307. BreakPointsChanged := True;
  7308. end;
  7309. end;
  7310. if BreakPointsChanged then
  7311. BuildAndSaveBreakPointLines(Memo);
  7312. FNavStacks.LinesInserted(Memo, FirstLine, Count);
  7313. end;
  7314. procedure TMainForm.MemoLinesDeleted(Memo: TIDEScintFileEdit; FirstLine, Count,
  7315. FirstAffectedLine: Integer);
  7316. begin
  7317. for var I := 0 to FDebugEntriesCount-1 do begin
  7318. const DebugEntry: PDebugEntry = @FDebugEntries[I];
  7319. if (DebugEntry.FileIndex = Memo.CompilerFileIndex) and
  7320. (DebugEntry.LineNumber >= FirstLine) then begin
  7321. if DebugEntry.LineNumber < FirstLine + Count then
  7322. DebugEntry.LineNumber := -1
  7323. else
  7324. Dec(DebugEntry.LineNumber, Count);
  7325. end;
  7326. end;
  7327. for var I := FindResultsList.Items.Count-1 downto 0 do begin
  7328. const FindResult = FindResultsList.Items.Objects[I] as TFindResult;
  7329. if FindResult <> nil then begin
  7330. if (PathCompare(FindResult.Filename, Memo.Filename) = 0) and
  7331. (FindResult.Line >= FirstLine) then begin
  7332. if FindResult.Line < FirstLine + Count then
  7333. FindResultsList.Items.Delete(I)
  7334. else begin
  7335. const NewLine = FindResult.Line - Count;
  7336. UpdateFindResult(FindResult, I, NewLine, Memo.GetPositionFromLine(NewLine));
  7337. end;
  7338. end;
  7339. end;
  7340. end;
  7341. if Assigned(Memo.LineState) then begin
  7342. { Shift existing line states }
  7343. if FirstLine < Memo.LineStateCount - Count then begin
  7344. for var I := FirstLine to Memo.LineStateCount - Count - 1 do
  7345. Memo.LineState[I] := Memo.LineState[I + Count];
  7346. Dec(Memo.LineStateCount, Count);
  7347. end
  7348. else begin
  7349. { There's nothing to shift because the last line(s) were deleted, or
  7350. line(s) past FLineStateCount }
  7351. if Memo.LineStateCount > FirstLine then
  7352. Memo.LineStateCount := FirstLine;
  7353. end;
  7354. end;
  7355. if Memo.StepLine >= FirstLine then begin
  7356. if Memo.StepLine < FirstLine + Count then
  7357. Memo.StepLine := -1
  7358. else
  7359. Dec(Memo.StepLine, Count);
  7360. end;
  7361. if Memo.ErrorLine >= FirstLine then begin
  7362. if Memo.ErrorLine < FirstLine + Count then
  7363. Memo.ErrorLine := -1
  7364. else
  7365. Dec(Memo.ErrorLine, Count);
  7366. end;
  7367. var BreakPointsChanged := False;
  7368. for var I := Memo.BreakPoints.Count-1 downto 0 do begin
  7369. const Line = Memo.BreakPoints[I];
  7370. if Line >= FirstLine then begin
  7371. if Line < FirstLine + Count then begin
  7372. Memo.BreakPoints.Delete(I);
  7373. BreakPointsChanged := True;
  7374. end else begin
  7375. Memo.BreakPoints[I] := Line - Count;
  7376. BreakPointsChanged := True;
  7377. end;
  7378. end;
  7379. end;
  7380. if BreakPointsChanged then
  7381. BuildAndSaveBreakPointLines(Memo);
  7382. if FNavStacks.LinesDeleted(Memo, FirstLine, Count) then
  7383. UpdateNavButtons;
  7384. { We do NOT update FCurrentNavItem here so it might point to a line that's
  7385. deleted until next UpdateCaretPosPanelAndBackStack by UpdateMemoUI }
  7386. { When lines are deleted, Scintilla insists on moving all of the deleted
  7387. lines' markers to the line on which the deletion started
  7388. (FirstAffectedLine). This is bad for us as e.g. it can result in the line
  7389. having two conflicting markers (or two of the same marker). There's no
  7390. way to stop it from doing that, or to easily tell which markers came from
  7391. which lines, so we simply delete and re-create all markers on the line. }
  7392. UpdateLineMarkers(Memo, FirstAffectedLine);
  7393. end;
  7394. procedure TMainForm.UpdateLineMarkers(const AMemo: TIDEScintFileEdit; const Line: Integer);
  7395. var
  7396. NewMarker: Integer;
  7397. begin
  7398. if Line >= AMemo.Lines.Count then
  7399. Exit;
  7400. var StepLine := AMemo.StepLine = Line;
  7401. NewMarker := -1;
  7402. if AMemo.BreakPoints.IndexOf(Line) <> -1 then begin
  7403. if AMemo.LineState = nil then
  7404. NewMarker := mmiBreakpoint
  7405. else if (Line < AMemo.LineStateCount) and (AMemo.LineState[Line] <> lnUnknown) then
  7406. NewMarker := IfThen(StepLine, mmiBreakpointStep, mmiBreakpointGood)
  7407. else
  7408. NewMarker := mmiBreakpointBad;
  7409. end else if StepLine then
  7410. NewMarker := mmiStep
  7411. else begin
  7412. if Line < AMemo.LineStateCount then begin
  7413. case AMemo.LineState[Line] of
  7414. lnHasEntry: NewMarker := mmiHasEntry;
  7415. lnEntryProcessed: NewMarker := mmiEntryProcessed;
  7416. end;
  7417. end;
  7418. end;
  7419. { Delete all markers on the line. To flush out any possible duplicates,
  7420. even the markers we'll be adding next are deleted. }
  7421. if AMemo.GetMarkers(Line) <> [] then
  7422. AMemo.DeleteAllMarkersOnLine(Line);
  7423. if NewMarker <> -1 then
  7424. AMemo.AddMarker(Line, NewMarker);
  7425. if StepLine then
  7426. AMemo.AddMarker(Line, mlmStep)
  7427. else if AMemo.ErrorLine = Line then
  7428. AMemo.AddMarker(Line, mlmError)
  7429. else if NewMarker = mmiBreakpointBad then
  7430. AMemo.AddMarker(Line, mlmBreakpointBad);
  7431. end;
  7432. procedure TMainForm.UpdateLinkLabelLinkClick(Sender: TObject;
  7433. const Link: string; LinkType: TSysLinkType);
  7434. begin
  7435. if LinkType <> sltID then
  7436. Exit;
  7437. if Link = 'fexit' then
  7438. FExit.Click
  7439. else if Link = 'hpurchase' then
  7440. HPurchase.Click
  7441. else if Link = 'hunregister' then
  7442. HUnregister.Click
  7443. else if Link = 'hwhatsnew' then
  7444. HWhatsNew.Click
  7445. else if Link = 'toptions-vscode' then begin
  7446. TOptionsForm.DropDownMemoKeyMappingComboBoxOnNextShow := True;
  7447. TOptions.Click
  7448. end;
  7449. end;
  7450. procedure TMainForm.UpdatePanelCloseBitBtnClick(Sender: TObject);
  7451. begin
  7452. var MessageToHideIndex := UpdateLinkLabel.Tag;
  7453. var Ini := TConfigIniFile.Create;
  7454. try
  7455. Ini.WriteInteger('UpdatePanel', FUpdatePanelMessages[MessageToHideIndex].ConfigIdent, FUpdatePanelMessages[MessageToHideIndex].ConfigValue);
  7456. finally
  7457. Ini.Free;
  7458. end;
  7459. FUpdatePanelMessages.Delete(MessageToHideIndex);
  7460. UpdateUpdatePanel;
  7461. end;
  7462. procedure TMainForm.UpdatePanelDonateBitBtnClick(Sender: TObject);
  7463. begin
  7464. FDonateImageMenuItem.Click;
  7465. end;
  7466. procedure TMainForm.UpdatePanelCloseBitBtnPaint(Sender: TObject; Canvas: TCanvas; var ARect: TRect);
  7467. const
  7468. MENU_SYSTEMCLOSE = 17;
  7469. MSYSC_NORMAL = 1;
  7470. begin
  7471. var R := ARect;
  7472. if FMenuThemeData <> 0 then begin
  7473. var Offset := MulDiv(2, CurrentPPI, 96);
  7474. Inc(R.Left, Offset);
  7475. DrawThemeBackground(FMenuThemeData, Canvas.Handle, MENU_SYSTEMCLOSE, MSYSC_NORMAL, R, nil);
  7476. end else begin
  7477. InflateRect(R, -MulDiv(6, CurrentPPI, 96), -MulDiv(6, CurrentPPI, 96));
  7478. Canvas.Pen.Color := Canvas.Font.Color;
  7479. Canvas.MoveTo(R.Left, R.Top);
  7480. Canvas.LineTo(R.Right, R.Bottom);
  7481. Canvas.MoveTo(R.Left, R.Bottom-1);
  7482. Canvas.LineTo(R.Right, R.Top-1);
  7483. end;
  7484. end;
  7485. procedure TMainForm.UpdateAllMemoLineMarkers(const AMemo: TIDEScintFileEdit);
  7486. begin
  7487. for var Line := 0 to AMemo.Lines.Count-1 do
  7488. UpdateLineMarkers(AMemo, Line);
  7489. end;
  7490. procedure TMainForm.UpdateAllMemosLineMarkers;
  7491. begin
  7492. for var Memo in FFileMemos do
  7493. if Memo.Used then
  7494. UpdateAllMemoLineMarkers(Memo);
  7495. end;
  7496. procedure TMainForm.UpdateBevel1Visibility;
  7497. begin
  7498. { Bevel1 is the line between the toolbar and memos when there's nothing in
  7499. between and the color of the toolbar and memo margins is the same }
  7500. Bevel1.Visible := (ToolBarPanel.Color = FTheme.Colors[tcMarginBack]) and
  7501. not UpdatePanel.Visible and not MemosTabSet.Visible;
  7502. end;
  7503. function TMainForm.ToCurrentPPI(const XY: Integer): Integer;
  7504. begin
  7505. Result := MulDiv(XY, CurrentPPI, 96);
  7506. end;
  7507. function TMainForm.FromCurrentPPI(const XY: Integer): Integer;
  7508. begin
  7509. Result := MulDiv(XY, 96, CurrentPPI);
  7510. end;
  7511. initialization
  7512. Application.OnGetActiveFormHandle := TMainForm.AppOnGetActiveFormHandle;
  7513. InitThemeLibrary;
  7514. InitHtmlHelpLibrary;
  7515. { For ClearType support, try to make the default font Microsoft Sans Serif }
  7516. if DefFontData.Name = 'MS Sans Serif' then
  7517. DefFontData.Name := AnsiString(GetPreferredUIFont);
  7518. CoInitialize(nil);
  7519. finalization
  7520. CoUninitialize();
  7521. end.