c_text.cpp 153 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326
  1. // c_text.cpp
  2. // Support for a window that can support regular, bold, italic and symbol
  3. // fonts each in large & small, plus the ability to select the size of
  4. // characters to be used.
  5. //
  6. //
  7. // See accompanying file "cwin.tex" for an explanation of the policy
  8. // for scrolling, text selection and treatment of buffer over-full
  9. // conditions.
  10. //
  11. // Copyright (C)/©/¸ Codemist Ltd, 1995-2001
  12. /*
  13. * This code may be used and modified, and redistributed in binary
  14. * or source form, subject to the "CCL Public License", which should
  15. * accompany it. This license is a variant on the BSD license, and thus
  16. * permits use of code derived from this in either open and commercial
  17. * projects: but it does require that updates to this code be made
  18. * available back to the originators of the package.
  19. * Before merging other code in with this or linking this code
  20. * with other packages or libraries please check that the license terms
  21. * of the other material are compatible with those of this.
  22. */
  23. /* Signature: 14bfdecf 10-Oct-2002 */
  24. #include "cwin.hpp"
  25. // There are times when this code wants to indicate to the user's
  26. // application that it should terminate. If such a request has been
  27. // raised the macro exception_pending() given here will evaluate to true,
  28. // but the way that this happens is specific the the CSL/CCL Lisp system
  29. // and thus is not exactly generic.
  30. extern "C" {
  31. extern int C_nil;
  32. extern FILE *spool_file;
  33. extern void inject_randomness(int);
  34. }
  35. #define exception_pending() ((C_nil&1) != 0)
  36. #define CSL_IGNORE(a) ((a) = (a))
  37. static BOOL hasFinished = FALSE;
  38. CMainWindow::CMainWindow()
  39. {
  40. hasFinished = FALSE;
  41. complete = FALSE;
  42. char *menuName = "MainMenu";
  43. HINSTANCE hInst = AfxFindResourceHandle(menuName, RT_MENU);
  44. HMENU hMenu = ::LoadMenu(hInst, menuName);
  45. this->CreateEx(WS_EX_ACCEPTFILES,
  46. mainWindowClass, "",
  47. WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
  48. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  49. NULL, hMenu);
  50. // Now I will add the dynamic menu items that I need...
  51. // I demand that the "help" be the rightmost one on the menu bar
  52. HMENU helpMenu = ::GetSubMenu(hMenu, ::GetMenuItemCount(hMenu)-1);
  53. int i;
  54. theApp.dynamicCount = 0;
  55. // Insert extra help menu items here. I expect that the registry will
  56. // contain a subkey called HelpItems which is an integer, and then
  57. // a bunch of strings takked as T000/P000, T001/P001 etc where the Tiii are
  58. // strings to place on the menu and the Piii are paths to relevant help
  59. // files that can be opened.
  60. int helpCount = theApp.GetProfileInt("HelpItems", "HowMany", 0);
  61. #ifdef MAYBE
  62. int helpCount = theApp.GetProfileInt("HelpItems", "HowMany", -1);
  63. if (helpCount < 0)
  64. { theApp.dynamic[0] = "&Contents"
  65. theApp.dynamic_files[0] = "???";
  66. theApp.dynamic[1] = "&Search for Help"
  67. theApp.dynamic_files[1] = "???";
  68. helpCount = 2;
  69. WriteProfileInt("HelpItems", "HowMany", 2);
  70. WriteProfileString("HelpItems", "T000", theApp.dynamic[0]);
  71. WriteProfileString("HelpItems", "P000", theApp.dynamic_files[0]);
  72. WriteProfileString("HelpItems", "T001", theApp.dynamic[1]);
  73. WriteProfileString("HelpItems", "P001", theApp.dynamic_files[1]);
  74. }
  75. else
  76. #endif
  77. { if (helpCount >= IDM_LAST_DYNAMIC-IDM_DYNAMIC_ITEMS)
  78. helpCount = IDM_LAST_DYNAMIC-IDM_DYNAMIC_ITEMS-1;
  79. for (i=0; i<helpCount; i++)
  80. { char tag[8];
  81. sprintf(tag, "T%.3d", i);
  82. CString key(theApp.GetProfileString("HelpItems", tag, ""));
  83. sprintf(tag, "P%.3d", i);
  84. CString path(theApp.GetProfileString("HelpItems", tag, ""));
  85. if (key.GetLength() != 0 && path.GetLength() != 0)
  86. { char *m;
  87. m = (char *)malloc(1+key.GetLength());
  88. strcpy(m, LPCTSTR(key));
  89. theApp.dynamic[theApp.dynamicCount] = m;
  90. m = (char *)malloc(1+path.GetLength());
  91. strcpy(m, LPCTSTR(path));
  92. theApp.dynamic_files[theApp.dynamicCount++] = m;
  93. }
  94. }
  95. }
  96. // Now place them on the menu
  97. for (i=0; i<theApp.dynamicCount; i++)
  98. ::InsertMenu(helpMenu, IDM_HELP_ON_HELP, MF_BYCOMMAND,
  99. IDM_DYNAMIC_ITEMS+i, theApp.dynamic[i]);
  100. // typeAheadBuffer is a smallish buffer and when the user presses a key the
  101. // resulting character is put there. No echoing is performed. Use of DEL
  102. // can discard a previously-typed character and an attempt to over-fill the
  103. // buffer before other parts of the code have emptied it will cause a BEEP.
  104. //
  105. typeAheadP1 = typeAheadP2 = 0; // pointers into typeAheadBuffer
  106. // inputBuffer holds stuff that the user has accepted (by typing a newline)
  107. // but that the program has not yet read. inputP shows where the next
  108. // character should be read from it. The end of the buffer is marked with
  109. // a newline. If inputP<0 the buffer is empty.
  110. inputP = -1;
  111. // textBuffer is a large buffer just containing characters. textFirst
  112. // points to the first active character and textLast to one after the last
  113. // active character. Both pointers are kept masked with TEXT_MASK which
  114. // wraps them around to give a circular buffer.
  115. textFirst = textLast = 0; // main text buffer, now empty
  116. caretChar = 0; // char before which caret is shown
  117. caretLine = 0; caretX = 0;
  118. icaretChar = icaretLine = icaretX = 0;
  119. caretVisible = TRUE;
  120. caretFontWidths = windowFonts.HCourier.across;
  121. icaretFontWidths = windowFonts.HCourier.across;
  122. endFontWidths = windowFonts.HCourier.across;
  123. endX = 0;
  124. xOffset = 0; // Not horizontally scrolled.
  125. inputLineStart = -1;
  126. caretWidth = 2*GetSystemMetrics(SM_CXBORDER);
  127. clipboardInput = NULL;
  128. insertMode = TRUE;
  129. savedP1 = savedP2 = savedFirst = savedLast = 0;
  130. currentInputLine = -1;
  131. pageLine = 0;
  132. pageMode = pagePaused = FALSE;
  133. selFirstChar = selStartChar = selEndChar = 0;
  134. selFirstX = selStartX = selEndX = 0;
  135. selFirstLine = selStartLine = selEndLine = 0;
  136. trackingSelection = FALSE;
  137. selRootValid = FALSE;
  138. hasFocus = FALSE;
  139. // The characters in textBuffer are organised into lines. The start of each
  140. // line is recorded in lineBuffer, which is also a circular buffer so the
  141. // pointers into it are kept reduced using LINE_MASK. lineFirst identified
  142. // the first line stored. lineLast identifies the last line. Note that
  143. // the data making up a single line runs from where lineBuffer points either
  144. // to where a termination character is found in the text (this will be
  145. // a newline character) or until textLast. The latter case arises only for
  146. // a final non-terminated line of text. In general there will not be room
  147. // on the screen to display all the lines of text that are present.
  148. // lineVisible identifies the first line that can be displayed.
  149. //
  150. lineFirst = lineVisible = 0; // line buffer, contains one (empty)
  151. lineLast = 0; // line
  152. lineBuffer[0].position = 0;
  153. lineBuffer[0].up = 000;
  154. lineBuffer[0].height = 000; // get filled in when fonts are created
  155. lineBuffer[0].width = 000; // gets filled in when line painted.
  156. lineBuffer[0].address = textFirst;
  157. cLeft[0] = cRight[0] = 0;
  158. strncpy(cMid, programName, 31); cMid[31] = 0;
  159. GetSystemTime(&titleUpdateTime);
  160. inject_randomness((int)titleUpdateTime.wMilliseconds);
  161. //!!cwin_display_date();
  162. strcpy(cwin_prompt_string, "> "); // A default prompt string.
  163. windowColour = GetSysColor(COLOR_WINDOW);
  164. textColour = GetSysColor(COLOR_WINDOWTEXT);
  165. highlightColour = GetSysColor(COLOR_HIGHLIGHT);
  166. highlightTextColour = GetSysColor(COLOR_HIGHLIGHTTEXT);
  167. #ifdef GRAPHICS_WINDOW
  168. graphicsShown = FALSE;
  169. graphicsWindow = new CGraphicsWindow;
  170. graphicsWindow->ShowWindow(SW_HIDE);
  171. #endif
  172. helpShown = FALSE;
  173. helpWindow = new CHelpWindow;
  174. CClientDC dc(helpWindow);
  175. dc.SelectObject(&helpWindow->helpFont);
  176. TEXTMETRIC mm;
  177. dc.GetTextMetrics(&mm);
  178. helpWindow->height = mm.tmExternalLeading + mm.tmAscent + mm.tmDescent;
  179. int widths[4];
  180. dc.GetCharWidth('X', 'X', widths);
  181. helpWindow->width = widths[0];
  182. WINDOWPLACEMENT wp;
  183. helpWindow->GetWindowPlacement(&wp);
  184. int screenWidth = GetSystemMetrics(SM_CXSCREEN);
  185. RECT *wr = &wp.rcNormalPosition;
  186. int left = wr->left;
  187. int cwidth = 80*helpWindow->width +
  188. 3*GetSystemMetrics(SM_CXBORDER) +
  189. 2*GetSystemMetrics(SM_CXFRAME) + 5;
  190. // Try to get the whole window onto the screen.
  191. if (left + cwidth > screenWidth)
  192. { left = screenWidth - cwidth;
  193. if (left < 0) left = 0;
  194. }
  195. int screenHeight = GetSystemMetrics(SM_CYSCREEN);
  196. int top = wr->top;
  197. int cheight = 25*helpWindow->height +
  198. GetSystemMetrics(SM_CYCAPTION) +
  199. GetSystemMetrics(SM_CYMENU) +
  200. 3*GetSystemMetrics(SM_CYBORDER) +
  201. 2*GetSystemMetrics(SM_CYFRAME) + 5;
  202. if (top + cheight > screenHeight)
  203. { top = screenHeight - cheight;
  204. if (top < 0) top = 0;
  205. }
  206. helpWindow->SetWindowPos(NULL,
  207. top, left,
  208. cwidth, cheight,
  209. SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOZORDER);
  210. helpWindow->ShowWindow(SW_HIDE);
  211. //!!myTimer = SetTimer(1, 50, NULL); // 20 ticks per second -> message queue
  212. myTimer = SetTimer(1, 2000, NULL); // tick every other second -> message queue
  213. cwin_interrupt_pending = 0;
  214. complete = TRUE;
  215. }
  216. CMainWindow::~CMainWindow()
  217. {
  218. }
  219. void CMainWindow::OnDestroy()
  220. {
  221. #ifdef GRAPHICS_WINDOW
  222. graphicsWindow->viewpointWindow.DestroyWindow();
  223. graphicsWindow->DestroyWindow();
  224. // delete graphicsWindow;
  225. #endif
  226. if (myTimer!=0) KillTimer(myTimer);
  227. windowFonts.DeleteFonts();
  228. hasFinished = TRUE;
  229. }
  230. BEGIN_MESSAGE_MAP(CMainWindow, CFrameWnd)
  231. ON_WM_DESTROY()
  232. ON_WM_CHAR()
  233. ON_WM_HSCROLL()
  234. ON_WM_KEYDOWN()
  235. ON_WM_KILLFOCUS()
  236. ON_WM_LBUTTONDBLCLK()
  237. ON_WM_LBUTTONDOWN()
  238. ON_WM_LBUTTONUP()
  239. ON_WM_MBUTTONDBLCLK()
  240. ON_WM_MBUTTONDOWN()
  241. ON_WM_MBUTTONUP()
  242. ON_WM_MOUSEMOVE()
  243. // ON_WM_NCLBUTTONDOWN()
  244. // ON_WM_NCMBUTTONDOWN()
  245. // ON_WM_NCRBUTTONDOWN()
  246. ON_WM_PAINT()
  247. ON_WM_TIMER()
  248. ON_WM_RBUTTONDBLCLK()
  249. ON_WM_RBUTTONDOWN()
  250. ON_WM_RBUTTONUP()
  251. ON_WM_SETFOCUS()
  252. ON_WM_SIZE()
  253. ON_WM_MOVE()
  254. ON_WM_VSCROLL()
  255. // Now things on the menus...
  256. ON_COMMAND(IDM_READ, OnRead)
  257. ON_COMMAND(IDM_SAVEAS, OnSaveAs)
  258. ON_COMMAND(IDM_SAVESEL, OnSaveSel)
  259. ON_COMMAND(IDM_PRINT, OnPrint)
  260. ON_COMMAND(IDM_PRINTSEL, OnPrintSel)
  261. ON_COMMAND(IDM_TOFILE, OnToFile)
  262. ON_COMMAND(IDM_EXIT, OnExit)
  263. ON_COMMAND(IDM_CUT, OnCut)
  264. ON_COMMAND(IDM_COPY, OnCopy)
  265. ON_COMMAND(IDM_PASTE, OnPaste)
  266. ON_COMMAND(IDM_REINPUT, OnReInput)
  267. ON_COMMAND(IDM_SELECTALL, OnSelectAll)
  268. ON_COMMAND(IDM_CLEAR, OnClear)
  269. // ON_COMMAND(IDM_UNDO, OnUndo)
  270. ON_COMMAND(IDM_REDRAW, OnRedraw)
  271. ON_COMMAND(IDM_HOME, OnHome)
  272. ON_COMMAND(IDM_END, OnEnd)
  273. ON_COMMAND(IDM_FONT, OnFont)
  274. ON_COMMAND(IDM_RESET_FONT, OnResetFont)
  275. ON_COMMAND(IDM_RESET_WINDOW, OnResetWindow)
  276. ON_COMMAND(IDM_INTERRUPT, OnInterrupt)
  277. ON_COMMAND(IDM_BACKTRACE, OnBacktrace)
  278. ON_COMMAND(IDM_PAGEMODE, OnPageMode)
  279. #ifdef GRAPHICS_WINDOW
  280. ON_COMMAND(IDM_GRAPHICS, OnGraphics)
  281. ON_MESSAGE(WM_USER, OnGraphics1)
  282. #endif
  283. #ifndef COMMON
  284. ON_COMMAND_RANGE(IDM_FIRSTLOAD, IDM_FIRSTLOAD+199, OnLoadLibrary)
  285. ON_COMMAND_RANGE(IDS_FIRSTSWITCH, IDS_FIRSTSWITCH+199, OnSwitch)
  286. #endif
  287. END_MESSAGE_MAP()
  288. // Message handlers...
  289. #define caretY (lineBuffer[caretLine].position - \
  290. lineBuffer[lineVisible].position)
  291. #define caretDY (lineBuffer[caretLine].height)
  292. #define caretY1 (caretY+caretDY)
  293. void CMainWindow::OnPaint()
  294. {
  295. RECT update;
  296. inject_randomness((int)clock());
  297. if (!GetUpdateRect(&update, TRUE)) return;
  298. CPaintDC dc(this);
  299. dc.SetTextAlign(TA_BASELINE);
  300. dc.SetTextColor(RGB(0,0,0)); currentColour = CH_BLACK;
  301. dc.SetBkColor(windowColour);
  302. dc.SelectObject(windowFonts.Courier); currentFont = CH_COURIER;
  303. currentWidths = windowFonts.HCourier.across;
  304. int yBase = lineBuffer[lineVisible].position, line;
  305. HideCaret();
  306. for (line=lineVisible;; line=(line+1)&LINE_MASK)
  307. { int y = lineBuffer[line].position - yBase;
  308. int y1 = y+lineBuffer[line].up;
  309. int y2 = y+lineBuffer[line].height;
  310. if (y2 >= update.top)
  311. lineBuffer[line].width =
  312. PaintTextLine(&dc, -xOffset, y, y1, y2,
  313. lineBuffer[line].address,
  314. clientWidth, &windowFonts, 0);
  315. if (y > (int)update.bottom+lineBuffer[line].up) break;
  316. if (line==lineLast) break;
  317. }
  318. ShowCaret();
  319. dc.SelectStockObject(SYSTEM_FONT);
  320. // Now I will worry about positioning the horizontal scroll thumb.
  321. int longestLine = 0;
  322. for (line=lineVisible;; line=(line+1)&LINE_MASK)
  323. { int y = lineBuffer[line].position - yBase;
  324. if (y > clientHeight) break;
  325. int w = lineBuffer[line].width;
  326. if (w > longestLine) longestLine = w;
  327. if (line==lineLast) break;
  328. }
  329. if (longestLine <= clientWidth)
  330. { if (xOffset != 0)
  331. {
  332. // Mess here - my response is to force the X offset to zero
  333. // and scroll the window to match - that will generally force a fresh
  334. // painting operation. This sort of thing can happen when there was a
  335. // long line in the buffer and the window had been scrolled right to view it,
  336. // and then by a sequence of vertical scrolling operations that line is
  337. // no longer on the screen and all the lines that are visible are short enough
  338. // to be shown in their entirity. It can also happen when somebody
  339. // increases the size of the window.
  340. ScrollWindow(xOffset, 0, NULL, NULL);
  341. xOffset = 0;
  342. SetScrollPos(SB_HORZ, 0, TRUE);
  343. }
  344. }
  345. else
  346. { int HScrollPos = (100*xOffset)/(longestLine-clientWidth/2);
  347. // If (for instance) the font had just cahnged or the windows size had been
  348. // enlarged you might have too little left visible on the screen - in such
  349. // cases I will forcibly scroll to correct things.
  350. if (HScrollPos > 100)
  351. { ScrollWindow(xOffset-longestLine+clientWidth/2, 0, NULL, NULL);
  352. xOffset = longestLine-clientWidth/2;
  353. HScrollPos = 100;
  354. }
  355. SetScrollPos(SB_HORZ, HScrollPos, TRUE);
  356. }
  357. CPoint caretPos(caretX-xOffset, caretY);
  358. SetCaretPos(caretPos);
  359. }
  360. // a, b and c are pointers into the circular text buffer, with a<=c
  361. // (logically). Test is a<=b<c. The mess here is because of wrap-
  362. // around with TEXT_MASK.
  363. BOOL betweenChar(int a, int b, int c)
  364. {
  365. if (a<=c) return (a<=b && b<c);
  366. else return (a<=b || b<c);
  367. }
  368. // Ditto but relative to the line buffer. Observe that the code I have is
  369. // identical, but I still like to keep the abstractions separate.
  370. BOOL betweenLine(int a, int b, int c)
  371. {
  372. if (a<=c) return (a<=b && b<c);
  373. else return (a<=b || b<c);
  374. }
  375. int CMainWindow::PaintTextLine(CDC *dc, int x,
  376. int topY, int y, int bottomY, int textChar,
  377. int width, FontArray *ff, int context)
  378. {
  379. // context = 0 painting to screen
  380. // = 1 printing whole of buffer
  381. // = 2 just printing selected region
  382. int nCount = 0;
  383. int needFont = (currentFont != CH_COURIER);
  384. int needColour = (currentColour != CH_BLACK);
  385. // I will buffer up to 80 characters of fixed-pitch output and display them
  386. // using a single call to TextOut. This is intended to reduce the cost of
  387. // calling the operating system for output.
  388. char buff[80];
  389. int bufp = 0, bufx = 0, bufy = 0;
  390. for (;;textChar=(textChar+1)&TEXT_MASK)
  391. {
  392. // I reset the X coordinate relating to the root, start and end of
  393. // any selection. This is necessary (for instance) after a font change,
  394. // and also means I do not have to re-calculate the X offsets when I am
  395. // inserting characters into the middle of the buffer.
  396. if (context == 0)
  397. { if (textChar == selFirstChar) selFirstX = x+xOffset;
  398. if (textChar == selStartChar) selStartX = x+xOffset;
  399. if (textChar == selEndChar) selEndX = x+xOffset;
  400. // Just after I have deleted a character (or two) I can have repositioned
  401. // the character but I will have lost track of the font active at its new
  402. // position, and thus of the font metrics, and the X coordinate that the
  403. // caret will be at. So although when inserting characters into the buffer
  404. // I can keep track of font & position I reset them here so that a
  405. // Paint operation following a delection puts things back in a coherent
  406. // state.
  407. if (textChar==caretChar)
  408. { caretX = x+xOffset;
  409. caretFontWidths = currentWidths;
  410. }
  411. if (textChar==icaretChar)
  412. { icaretX = x+xOffset;
  413. icaretFontWidths = currentWidths;
  414. }
  415. // Ditto for the end of the text buffer.
  416. if (textChar==textLast)
  417. { endX = x+xOffset;
  418. endFontWidths = currentWidths;
  419. break;
  420. }
  421. }
  422. else if (textChar==textLast) break;
  423. // I will stop if I either find a newline character or I reach the end
  424. // of the buffer. In each case I will have sorted out how long the line
  425. // I was displaying was.
  426. int c = textBuffer[textChar];
  427. // I permit ESC characters in the buffer, but I will display them as $
  428. // following an ancient tradition. Other control characters are not really
  429. // permitted and could lead to mangled display.
  430. if (c == 0x1b) c = '$';
  431. int w = currentWidths[c & 0xff];
  432. BOOL inSelection = betweenChar(selStartChar, textChar, selEndChar);
  433. if (context==0 && inSelection &&
  434. (currentColour==CH_BLACK || currentColour==CH_HIGHLIGHT))
  435. { needColour = FALSE;
  436. currentColour = CH_HIGHLIGHT;
  437. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  438. dc->SetTextColor(highlightTextColour);
  439. dc->SetBkColor(highlightColour);
  440. }
  441. else if (!inSelection)
  442. {
  443. // If I am outside the selected region and context==2 (printing selection)
  444. // then I map characters onto blanks so thet they will not appear in the
  445. // output.
  446. if (context == 2) c = ' ';
  447. if (currentColour==CH_HIGHLIGHT)
  448. { needColour = FALSE;
  449. currentColour = CH_BLACK;
  450. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  451. dc->SetTextColor(RGB(0,0,0));
  452. dc->SetBkColor(windowColour);
  453. }
  454. }
  455. switch (c & 0xff)
  456. {
  457. case '\n':
  458. break;
  459. case CH_RED:
  460. needColour = FALSE;
  461. if (currentColour == c) continue;
  462. currentColour = c;
  463. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  464. dc->SetTextColor(RGB(255,0,0));
  465. dc->SetBkColor(windowColour);
  466. continue;
  467. case CH_BLUE:
  468. needColour = FALSE;
  469. if (currentColour == c) continue;
  470. currentColour = c;
  471. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  472. dc->SetTextColor(RGB(0,0,255));
  473. dc->SetBkColor(windowColour);
  474. continue;
  475. case CH_BLACK:
  476. if (inSelection && context==0)
  477. { c = CH_HIGHLIGHT;
  478. needColour = FALSE;
  479. if (currentColour == c) continue;
  480. currentColour = c;
  481. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  482. dc->SetTextColor(highlightTextColour);
  483. dc->SetBkColor(highlightColour);
  484. }
  485. else
  486. { needColour = FALSE;
  487. if (currentColour == c) continue;
  488. currentColour = c;
  489. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  490. dc->SetTextColor(RGB(0,0,0));
  491. dc->SetBkColor(windowColour);
  492. }
  493. continue;
  494. case CH_GRAY:
  495. needColour = FALSE;
  496. if (currentColour == c) continue;
  497. currentColour = c;
  498. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  499. dc->SetTextColor(RGB(128,128,128));
  500. dc->SetBkColor(windowColour);
  501. continue;
  502. case CH_COURIER:
  503. needFont = FALSE;
  504. if (currentFont == c) continue;
  505. currentFont = c;
  506. currentWidths = ff->HCourier.across;
  507. dc->SelectObject(ff->Courier);
  508. continue;
  509. case CH_ROMAN:
  510. needFont = FALSE;
  511. if (currentFont == c) continue;
  512. currentFont = c;
  513. currentWidths = ff->HRoman.across;
  514. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  515. dc->SelectObject(ff->Roman);
  516. continue;
  517. case CH_BOLD:
  518. needFont = FALSE;
  519. if (currentFont == c) continue;
  520. currentFont = c;
  521. currentWidths = ff->HBold.across;
  522. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  523. dc->SelectObject(ff->Bold);
  524. continue;
  525. case CH_ITALIC:
  526. needFont = FALSE;
  527. if (currentFont == c) continue;
  528. currentFont = c;
  529. currentWidths = ff->HItalic.across;
  530. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  531. dc->SelectObject(ff->Italic);
  532. continue;
  533. case CH_SYMBOL:
  534. needFont = FALSE;
  535. if (currentFont == c) continue;
  536. currentFont = c;
  537. currentWidths = ff->HSymbol.across;
  538. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  539. dc->SelectObject(ff->Symbol);
  540. continue;
  541. case CH_Roman:
  542. needFont = FALSE;
  543. if (currentFont == c) continue;
  544. currentFont = c;
  545. currentWidths = ff->Hroman.across;
  546. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  547. dc->SelectObject(ff->roman);
  548. continue;
  549. case CH_Bold:
  550. needFont = FALSE;
  551. if (currentFont == c) continue;
  552. currentFont = c;
  553. currentWidths = ff->Hbold.across;
  554. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  555. dc->SelectObject(ff->bold);
  556. continue;
  557. case CH_Italic:
  558. needFont = FALSE;
  559. if (currentFont == c) continue;
  560. currentFont = c;
  561. currentWidths = ff->Hitalic.across;
  562. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  563. dc->SelectObject(ff->italic);
  564. continue;
  565. case CH_Symbol:
  566. needFont = FALSE;
  567. if (currentFont == c) continue;
  568. currentFont = c;
  569. currentWidths = ff->Hsymbol.across;
  570. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  571. dc->SelectObject(ff->symbol);
  572. continue;
  573. default:
  574. // The start of every line should be in regular-sized Courier and
  575. // in black. But rather than calling Windows to force that selection
  576. // on every line (I hypothesize that selecting a fond and a colour
  577. // into my device context may be costly) I only do so when I find I have
  578. // a character to display and then only in cases where what was left over
  579. // from whatever was last printed was somehow different.
  580. if (context==0 && needColour && inSelection)
  581. { if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  582. dc->SetTextColor(highlightTextColour);
  583. dc->SetBkColor(highlightColour);
  584. currentColour = CH_BLUE;
  585. needColour = FALSE;
  586. }
  587. else if (needColour)
  588. { if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  589. dc->SetTextColor(RGB(0,0,0));
  590. dc->SetBkColor(windowColour);
  591. currentColour = CH_BLACK;
  592. needColour = FALSE;
  593. }
  594. if (needFont)
  595. { dc->SelectObject(ff->Courier);
  596. currentFont = CH_COURIER;
  597. currentWidths = ff->HCourier.across;
  598. needFont = FALSE;
  599. }
  600. if (c == '\t')
  601. { c = ' ';
  602. w = currentWidths[c]*(8-(nCount&7));
  603. nCount = 7;
  604. }
  605. else if (c < 32)
  606. { w = currentWidths['^'];
  607. // There is a curiosity here, that for the moment I am going to leave alone.
  608. // If one has a control character in the buffer it is displayed as something
  609. // like ^I (with the '^' as a separate character and the 'I' a normal letter).
  610. // If this is within a selection the 'I' is shown in inverse video as usual
  611. // but the '^' is not. This could perhaps be seen as a way of making it
  612. // possible to distinguish between the control character and a regular pair
  613. // of characters spelt the same.
  614. if (x+w>=0 && x<width)
  615. { if (bufp>=sizeof(buff))
  616. dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  617. if (bufp == 0) bufx = x, bufy = y;
  618. buff[bufp++] = '^';
  619. }
  620. x += currentWidths['^'];
  621. c = c | 0x40;
  622. w = currentWidths[c & 0xff];
  623. nCount++;
  624. }
  625. if (inSelection && context==0)
  626. { CBrush b(highlightColour);
  627. CBrush *oldbrush = dc->SelectObject(&b);
  628. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  629. dc->SelectStockObject(NULL_PEN);
  630. dc->Rectangle(x, topY, x+w+1, bottomY+1);
  631. dc->SelectObject(oldbrush);
  632. }
  633. if (x+w>=0 && x<width)
  634. { if (currentFont == CH_COURIER)
  635. { if (bufp>=sizeof(buff))
  636. dc->TextOut(bufx, bufy, buff, bufp), bufp = 0;
  637. if (bufp == 0) bufx = x, bufy = y;
  638. buff[bufp++] = (char)c;
  639. }
  640. else
  641. { if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
  642. buff[0] = (char)c;
  643. dc->TextOut(x, y, buff, 1);
  644. }
  645. }
  646. x += w;
  647. nCount++;
  648. continue;
  649. }
  650. break;
  651. }
  652. if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp);
  653. // I am NOT happy about the next few lines -- they fill an area in background
  654. // colour out across to the end of the line. I do this because otherwise
  655. // despite lots of calls to HideCaret I seem to observe remains of the caret
  656. // displayed after I have made a line shorter. I do not understand why this
  657. // happens but hope that what follows will work around the problem.
  658. if (context==0)
  659. { CBrush b(windowColour);
  660. CBrush *oldbrush = dc->SelectObject(&b);
  661. dc->SelectStockObject(NULL_PEN);
  662. dc->Rectangle(x, topY,
  663. width, bottomY+1);
  664. dc->SelectObject(oldbrush);
  665. }
  666. return x + xOffset;
  667. }
  668. int cwin_linelength = 80;
  669. void CMainWindow::OnSize(UINT nType, int cx, int cy)
  670. {
  671. WINDOWPLACEMENT wp;
  672. GetWindowPlacement(&wp);
  673. RECT *wr = &wp.rcNormalPosition;
  674. clientWidth = cx;
  675. clientHeight = cy;
  676. if (nType != SIZE_MINIMIZED)
  677. cwin_linelength = (clientWidth-5) / windowFonts.HCourier.across['X'];
  678. theApp.WriteProfileInt("MainWindow", "ScreenWidth", wr->right-wr->left);
  679. theApp.WriteProfileInt("MainWindow", "ScreenHeight", wr->bottom-wr->top);
  680. theApp.WriteProfileInt("MainWindow", "LineLength", cwin_linelength);
  681. if (caretVisible)
  682. { OnVScroll(SB_FOR_CARET, 0, NULL);
  683. OnHScroll(SB_FOR_CARET, 0, NULL);
  684. }
  685. OnHScroll(SB_REFRESH_THUMB, 0, NULL);
  686. OnVScroll(SB_REFRESH_THUMB, 0, NULL);
  687. }
  688. void CMainWindow::OnMove(int x, int y)
  689. {
  690. WINDOWPLACEMENT wp;
  691. CSL_IGNORE(x); CSL_IGNORE(y);
  692. GetWindowPlacement(&wp);
  693. RECT *wr = &wp.rcNormalPosition;
  694. theApp.WriteProfileInt("MainWindow", "ScreenLeft", wr->left);
  695. theApp.WriteProfileInt("MainWindow", "ScreenTop", wr->top);
  696. }
  697. void CMainWindow::OnResetWindow()
  698. {
  699. int screenWidth = GetSystemMetrics(SM_CXSCREEN);
  700. WINDOWPLACEMENT wp;
  701. GetWindowPlacement(&wp);
  702. RECT *wr = &wp.rcNormalPosition;
  703. int left = wr->left;
  704. int cwidth = 80*windowFonts.HCourier.across['X'] +
  705. 3*GetSystemMetrics(SM_CXBORDER) +
  706. 2*GetSystemMetrics(SM_CXFRAME) +
  707. GetSystemMetrics(SM_CXVSCROLL) + 5;
  708. // Try to get the whole window onto the screen.
  709. if (left + cwidth > screenWidth)
  710. { left = screenWidth - cwidth;
  711. if (left < 0) left = 0;
  712. }
  713. SetWindowPos(NULL,
  714. left, wr->top,
  715. cwidth, (wr->bottom - wr->top),
  716. SWP_SHOWWINDOW | SWP_NOZORDER);
  717. UpdateWindow();
  718. cwin_linelength = 80;
  719. theApp.WriteProfileInt("MainWindow", "ScreenLeft", left);
  720. theApp.WriteProfileInt("MainWindow", "ScreenWidth", cwidth);
  721. theApp.WriteProfileInt("MainWindow", "ScreenTop", wr->top);
  722. theApp.WriteProfileInt("MainWindow", "ScreenHeight", wr->bottom-wr->top);
  723. theApp.WriteProfileInt("MainWindow", "LineLength", cwin_linelength);
  724. }
  725. void CMainWindow::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pCntl)
  726. {
  727. // I will start by computing the total height of all the stuff I have in
  728. // my text buffer. At the very worst I only save a couple of thousand lines,
  729. // so adding up their heights again will not be TOO painful, I hope.
  730. int VScrollPos, movedUp=0, i, w;
  731. int aboveTop = -LineY(lineFirst);
  732. int totalHeight = aboveTop + LineY(lineLast) + LineDY(lineLast);
  733. CSL_IGNORE(pCntl);
  734. inject_randomness(totalHeight);
  735. // If I can make everything visible I will do that regardless of anything
  736. // the user tries to do.
  737. if (totalHeight <= clientHeight)
  738. { lineVisible = lineFirst;
  739. movedUp = aboveTop;
  740. VScrollPos = 0;
  741. }
  742. else
  743. { switch (nSBCode)
  744. {
  745. case SB_ENDSCROLL:
  746. return;
  747. case SB_REFRESH_THUMB:
  748. // In case the window has just been enlarged I check to see if I can scroll
  749. // up a bit until one more scroll up would make the last line of my text
  750. // drop off the (new) bottom of the window.
  751. while (lineVisible!=lineFirst)
  752. { int lv = (lineVisible-1)&LINE_MASK;
  753. int nAbove = lineBuffer[lv].position -
  754. lineBuffer[lineFirst].position;
  755. if (totalHeight > nAbove+clientHeight) break;
  756. lineVisible = lv;
  757. w = LineDY(lv);
  758. movedUp += w;
  759. aboveTop -= w;
  760. }
  761. break;
  762. case SB_THUMBPOSITION:
  763. case SB_THUMBTRACK:
  764. i = (nPos*(totalHeight-clientHeight))/100;
  765. if (i > aboveTop)
  766. { while (totalHeight > aboveTop+clientHeight &&
  767. i > aboveTop)
  768. { w = LineDY(lineVisible);
  769. aboveTop += w;
  770. movedUp -= w;
  771. lineVisible = (lineVisible+1) & LINE_MASK;
  772. }
  773. }
  774. else
  775. { i += LineDY(lineVisible);
  776. while (lineVisible != lineFirst &&
  777. i <= aboveTop)
  778. { lineVisible = (lineVisible-1) & LINE_MASK;
  779. w = LineDY(lineVisible);
  780. aboveTop -= w;
  781. movedUp += w;
  782. }
  783. }
  784. break;
  785. case SB_FOR_CARET:
  786. if (LineY(caretLine)+LineDY(caretLine) > clientHeight)
  787. { while (totalHeight > aboveTop+clientHeight &&
  788. LineY(caretLine)+LineDY(caretLine) > clientHeight)
  789. { w = LineDY(lineVisible);
  790. aboveTop += w;
  791. movedUp -= w;
  792. lineVisible = (lineVisible+1) & LINE_MASK;
  793. }
  794. }
  795. else
  796. { while (lineVisible != lineFirst &&
  797. LineY(caretLine) < 0)
  798. { lineVisible = (lineVisible-1) & LINE_MASK;
  799. w = LineDY(lineVisible);
  800. aboveTop -= w;
  801. movedUp += w;
  802. }
  803. }
  804. break;
  805. case SB_LINEDOWN:
  806. // If the bottom line of text is already visible I will not scroll down
  807. // any further.
  808. if (totalHeight > aboveTop+clientHeight)
  809. { lineVisible = (lineVisible+1) & LINE_MASK;
  810. w = lineBuffer[lineVisible].height;
  811. aboveTop += w;
  812. movedUp = -w;
  813. }
  814. break;
  815. case SB_LINEUP:
  816. if (lineVisible != lineFirst)
  817. { w = lineBuffer[lineVisible].height;
  818. aboveTop -= w;
  819. movedUp = w;
  820. lineVisible = (lineVisible-1) & LINE_MASK;
  821. }
  822. break;
  823. // When asked to scroll be a "page" I in fact jump by around 0.7 times
  824. // the number of lines visible on a page.
  825. case SB_PAGEDOWN:
  826. while (totalHeight > aboveTop+clientHeight &&
  827. -10*movedUp < 7*clientHeight)
  828. { lineVisible = (lineVisible+1) & LINE_MASK;
  829. w = lineBuffer[lineVisible].height;
  830. aboveTop += w;
  831. movedUp -= w;
  832. }
  833. break;
  834. case SB_PAGEUP:
  835. while (lineVisible != lineFirst &&
  836. 10*movedUp < 7*clientHeight)
  837. { w = lineBuffer[lineVisible].height;
  838. aboveTop -= w;
  839. movedUp += w;
  840. lineVisible = (lineVisible-1) & LINE_MASK;
  841. }
  842. break;
  843. }
  844. if (lineVisible == lineFirst) VScrollPos = 0;
  845. else if (totalHeight <= aboveTop+clientHeight) VScrollPos = 100;
  846. else VScrollPos = (100*aboveTop)/(totalHeight-clientHeight);
  847. }
  848. // If the user had just dragged the scroll bar I will tend to leave the
  849. // scroll bar thumb where the user put it. However I will make an exception
  850. // if the user's drag caused me to go to one or other extreme position, since
  851. // I think it would be confusing to have the window totally fully scrolled
  852. // and the thumb not extremal in its bar. But at all intermediate positions
  853. // it is kindest to let it settle where the user put it!
  854. if (nSBCode == SB_THUMBPOSITION ||
  855. nSBCode == SB_THUMBTRACK)
  856. { if (VScrollPos == 0 || VScrollPos == 100)
  857. nPos = VScrollPos;
  858. SetScrollPos(SB_VERT, nPos, TRUE);
  859. }
  860. else SetScrollPos(SB_VERT, VScrollPos, TRUE);
  861. if (movedUp != 0)
  862. { HideCaret();
  863. ScrollWindow(0, movedUp, NULL, NULL);
  864. ShowCaret();
  865. }
  866. // If the window is minimized then I will not change the status of apparent
  867. // visibility of the caret. This is important because if the caret becomes
  868. // marked as invisible because (for instance) the window is of zero size then
  869. // it will remain invisible and the window will not be scrolled ever, leaving
  870. // the text buffer to over-fill and hang things up.
  871. if (IsIconic()) return;
  872. else if (caretY >= 0 && caretY1 <= clientHeight &&
  873. caretX-xOffset >= 0 && caretX-xOffset < clientWidth)
  874. caretVisible = TRUE;
  875. else caretVisible = FALSE;
  876. }
  877. void CMainWindow::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pCntl)
  878. {
  879. // Each time I hit the horizontal scroll-bar I will check to find how
  880. // long the longest (partially) visible line is.
  881. int yBase = lineBuffer[lineVisible].position;
  882. int line, longestLine = 0;
  883. CSL_IGNORE(pCntl);
  884. for (line=lineVisible;; line=(line+1)&LINE_MASK)
  885. { int y = lineBuffer[line].position - yBase;
  886. if (y > clientHeight) break;
  887. int w = lineBuffer[line].width;
  888. if (w > longestLine) longestLine = w;
  889. if (line==lineLast) break;
  890. }
  891. // If everything fits I will just make sure I am scrolled fully left. Well as
  892. // a matter of caution I do not force things that way if the request was
  893. // to make the caret visible, since maybe if the screen is not quite up to
  894. // date it may seem that I could afford to scroll fully left but that would
  895. // in fact hide the caret.
  896. if (longestLine < clientWidth && nSBCode != SB_FOR_CARET)
  897. { if (xOffset != 0)
  898. { HideCaret();
  899. ScrollWindow(xOffset, 0, NULL, NULL);
  900. ShowCaret();
  901. }
  902. xOffset = 0;
  903. SetScrollPos(SB_HORZ, 0, TRUE);
  904. return;
  905. }
  906. int moveLeft = 0, w;
  907. switch (nSBCode)
  908. {
  909. case SB_ENDSCROLL:
  910. return;
  911. case SB_REFRESH_THUMB:
  912. break;
  913. case SB_THUMBPOSITION:
  914. case SB_THUMBTRACK:
  915. w = (nPos*(longestLine-clientWidth/2))/100;
  916. moveLeft = xOffset - w;
  917. xOffset = w;
  918. break;
  919. case SB_FOR_CARET:
  920. w = windowFonts.HCourier.across['X'];
  921. if (caretX-xOffset < 5*w)
  922. moveLeft = w*((clientWidth/2 - (caretX-xOffset))/w);
  923. else if (caretX-xOffset >= clientWidth)
  924. moveLeft = -w*((caretX - xOffset - clientWidth)/w + 4);
  925. xOffset -= moveLeft;
  926. break;
  927. // Small jumps are by the width of the letter X in the biggest fixed pitch
  928. // font that I have selected. Thus if you select a big font you will get
  929. // coarser contol over horizontal scrolling.
  930. case SB_LINERIGHT:
  931. w = windowFonts.HCourier.across['X'];
  932. xOffset += w;
  933. moveLeft = -w;
  934. break;
  935. case SB_LINELEFT:
  936. w = windowFonts.HCourier.across['X'];
  937. xOffset -= w;
  938. moveLeft = w;
  939. break;
  940. // Big jumps are by roughly (2/3) the width of the main Window
  941. case SB_PAGERIGHT:
  942. w = (2*clientWidth)/3;
  943. xOffset += w;
  944. moveLeft = -w;
  945. break;
  946. case SB_PAGELEFT:
  947. w = (2*clientWidth)/3;
  948. xOffset -= w;
  949. moveLeft = w;
  950. break;
  951. }
  952. if (xOffset < 0)
  953. { moveLeft += xOffset;
  954. xOffset = 0;
  955. }
  956. // I will permit scrolls to the right to a stage where the longest line
  957. // is (1/2) across the screen. But no further and especially I will not
  958. // permit scrolls right that cause the text on the screen to vanish
  959. // totally. The limit I use here is somewhat of an arbitrary choice. If a
  960. // "scroll page right" request looked as if it would move beyond this I will
  961. // clip the scroll.
  962. w = longestLine - clientWidth/2;
  963. if (w <= 0)
  964. { moveLeft += xOffset;
  965. xOffset = 0;
  966. w = 1;
  967. }
  968. else if (xOffset>w)
  969. { moveLeft += xOffset - w;
  970. xOffset = w;
  971. }
  972. int HScrollPos = (100*xOffset)/w;
  973. if (HScrollPos > 100) HScrollPos = 100;
  974. if ((nSBCode != SB_THUMBPOSITION &&
  975. nSBCode != SB_THUMBTRACK) ||
  976. HScrollPos == 0 ||
  977. HScrollPos == 100) SetScrollPos(SB_HORZ, HScrollPos, TRUE);
  978. else SetScrollPos(SB_HORZ, nPos, TRUE);
  979. if (moveLeft != 0)
  980. { HideCaret();
  981. ScrollWindow(moveLeft, 0, NULL, NULL);
  982. ShowCaret();
  983. }
  984. }
  985. void CMainWindow::OnSetFocus(CWnd *pOldWnd)
  986. {
  987. CSL_IGNORE(pOldWnd);
  988. CreateSolidCaret(caretWidth, windowFonts.HCourier.height);
  989. ShowCaret();
  990. SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
  991. selectScrollSpeed = 0;
  992. hasFocus = TRUE; /* True just after focus has returned to this window */
  993. }
  994. void CMainWindow::OnKillFocus(CWnd *pNewWnd)
  995. {
  996. CSL_IGNORE(pNewWnd);
  997. ::DestroyCaret();
  998. hasFocus = FALSE;
  999. }
  1000. // Sometime I find reading documentation quite hard. I had really thought that
  1001. // when one selected an application (eg using ALT+TAB) that would give the
  1002. // input focus to that application's main window. But it appears
  1003. // (experimentally) that this is not always true, and I found myself with
  1004. // a window with hard blue title bar but no input focus. It seems (?) that
  1005. // the application has been "activated", so here I ensure that when that
  1006. // happens it grabs the focus. Elsewhere you will find that whenever I see a
  1007. // mouse button depressed I grab the focus too (just to be on the safe side).
  1008. void CMainWindow::OnActivate(UINT state, CWnd *pWnd, BOOL minim)
  1009. {
  1010. CSL_IGNORE(state);
  1011. CSL_IGNORE(pWnd);
  1012. CSL_IGNORE(minim);
  1013. if (state != WA_INACTIVE) SetFocus();
  1014. }
  1015. // When I put a character into the text buffer I need to worry about purging
  1016. // old characters when the buffer wraps around. That may have an effect on
  1017. // what is visible on the screen, or on a selected region. If I insert a
  1018. // character and I am at the foot of a page I may need to scroll up. All
  1019. // in all the funny cases are quite messy here!
  1020. void CMainWindow::cwin_putchar(int ch)
  1021. {
  1022. if (hasFinished) return;
  1023. BOOL caretAtEnd = (caretChar == textLast);
  1024. if (ch == '\n')
  1025. {
  1026. // If the caret is at the end of the text and if "page mode" is enabled
  1027. // then putting a newline into the text buffer will sometimes lead to
  1028. // a pause....
  1029. BOOL usualTitle = TRUE;
  1030. // The test here says pause if
  1031. // (a) I am in page mode
  1032. // (b) lineVisible==pageLine. Well lineFirst is the line that is now in
  1033. // danger of being about to scroll off the top of the screen, and so
  1034. // pageLine will identify the current line to be "protected" from that
  1035. // fate.
  1036. // (c) caretLine vs clientHeight: This tries to test if scrolling would be
  1037. // called for if the caret moved down a line (hence the 2*LineDY()).
  1038. // In the pause case I just loop polling the window manager until something
  1039. // magic happens. In general I can do the correct magic by making a suitable
  1040. // selection of events do "pageLine = caretLine;"
  1041. //
  1042. // Actually it is nastier than that, because lineVisible may not be up to
  1043. // date - so I have to work out what lineVisible WOULD be in the relevant
  1044. // situation by having code rather line that in OnVScroll/SB_FOR_CARET.
  1045. while (pageMode)
  1046. { int lv = lineVisible, w;
  1047. int aboveTop = -LineY(lineFirst);
  1048. int totalHeight = aboveTop + LineY(lineLast) + LineDY(lineLast);
  1049. if (totalHeight <= clientHeight) lv = lineFirst;
  1050. else while (totalHeight > aboveTop+clientHeight &&
  1051. LineY(caretLine)+LineDY(caretLine) > clientHeight)
  1052. { w = LineDY(lv);
  1053. aboveTop += w;
  1054. lv = (lv+1) & LINE_MASK;
  1055. }
  1056. if (lv==pageLine &&
  1057. (LineY(caretLine)+2*LineDY(caretLine) > clientHeight))
  1058. { if (usualTitle)
  1059. { usualTitle = FALSE;
  1060. pagePaused = TRUE;
  1061. // BeginWaitCursor();
  1062. // For reasons that I do not fully understand a "wait" cursor established here
  1063. // gets turned back into the usual arrow one before I want it to. I suspect
  1064. // maybe that whenever the system re-draws the cursor it does so using the
  1065. // version that is set as default for the application? Anyway the effect is
  1066. // that I get an apparantly inconsistent behaviour, and at present I think
  1067. // that keeping the cursor consistently an arrow is better than letting it
  1068. // flash incoherently between that and an hourglass.
  1069. SetWindowText("(page-mode) Type any key to continue");
  1070. }
  1071. // Here I will force the caret to be visible and poll the window manager
  1072. if (!hasFinished)
  1073. { OnVScroll(SB_FOR_CARET, 0, NULL);
  1074. OnHScroll(SB_FOR_CARET, 0, NULL);
  1075. cwin_poll_window_manager(FALSE);
  1076. }
  1077. if (hasFinished) return;
  1078. continue;
  1079. }
  1080. else break;
  1081. }
  1082. if (!usualTitle)
  1083. { SetWindowText(mainTitle);
  1084. // EndWaitCursor();
  1085. }
  1086. pagePaused = FALSE;
  1087. textBuffer[textLast] = '\n';
  1088. int n = (textLast+1) & TEXT_MASK;
  1089. if (n == textFirst) WrapTextBuffer(TRUE);
  1090. textLast = n;
  1091. // When I see a newline I may need to wrap the line buffer, discarding the
  1092. // oldest saved line.
  1093. int nextPos = lineBuffer[lineLast].position +
  1094. lineBuffer[lineLast].height;
  1095. n = (lineLast+1) & LINE_MASK;
  1096. if (n == lineFirst) WrapTextBuffer(TRUE);
  1097. lineLast = n;
  1098. lineBuffer[lineLast].position = nextPos;
  1099. lineBuffer[lineLast].up = windowFonts.HCourier.up;
  1100. lineBuffer[lineLast].height = windowFonts.HCourier.up +
  1101. windowFonts.HCourier.down;
  1102. lineBuffer[lineLast].width = 0;
  1103. lineBuffer[lineLast].address = textLast;
  1104. endFontWidths = windowFonts.HCourier.across;
  1105. endX = 0;
  1106. if (caretAtEnd)
  1107. { caretChar = icaretChar = textLast;
  1108. caretLine = icaretLine = lineLast;
  1109. caretX = icaretX = 0;
  1110. caretFontWidths = icaretFontWidths = windowFonts.HCourier.across;
  1111. }
  1112. }
  1113. else
  1114. { textBuffer[textLast] = (char)ch;
  1115. int n = (textLast+1) & TEXT_MASK;
  1116. if (n == textFirst) WrapTextBuffer(TRUE);
  1117. // I invalidate all the way to the right of the line just to be on the
  1118. // safe side. This is probably excessive... but equally the painting in of
  1119. // the solid blank to the right of any real text will not be too expensive.
  1120. CRect cr(lineBuffer[lineLast].width-xOffset, LineY(lineLast),
  1121. clientWidth, LineY(lineLast)+LineDY(lineLast));
  1122. InvalidateRect(&cr); // Should I hide the caret here?
  1123. textLast = n;
  1124. endX += endFontWidths[ch];
  1125. lineBuffer[lineLast].width += endFontWidths[ch];
  1126. if (caretAtEnd)
  1127. { caretChar = icaretChar = textLast;
  1128. caretX += caretFontWidths[ch];
  1129. icaretX = caretX;
  1130. }
  1131. }
  1132. }
  1133. // This code inserts characters wherever the caret happens to be, typically
  1134. // in the middle of the text buffer. This returns TRUE if it succeeds. The
  1135. // case when it might fail is if the text buffer is full and the caret is on
  1136. // the first line. Then to make room for the next stuff the first line would
  1137. // need to be discarded, but I can not do that without loss of the caret.
  1138. //
  1139. // UnTypeAhead() is used to push characters into the type-ahead buffer if
  1140. // a newline is inserted into the current line of input (ie between
  1141. // inputLineStart and textLast). It returns FALSE if pushing characters
  1142. // flushed out previously typed stuff.
  1143. BOOL CMainWindow::UnTypeAhead(int ch)
  1144. {
  1145. int w = (typeAheadP1-1)&typeAheadBufferMask;
  1146. typeAheadBuffer[w] = (char)ch;
  1147. typeAheadP1 = w;
  1148. if (typeAheadP1 == typeAheadP2)
  1149. { typeAheadP2 = (typeAheadP2-1)&typeAheadBufferMask;
  1150. return FALSE; // Inserting pushes out something else
  1151. }
  1152. else return TRUE;
  1153. }
  1154. BOOL CMainWindow::InsertAtCaret(char *s, int n)
  1155. {
  1156. // I will do large inserts in 60-character chunks. That is so I can have
  1157. // a limited size circular buffer (64 chars here) for use when copying
  1158. // stuff down the buffer. Huge inserts would benefit from a larger chunk-size
  1159. // here, at the cost of using more memory for the copy buffer.
  1160. while (n > 60)
  1161. { if (!InsertAtCaret(s, 60)) return FALSE;
  1162. s += 60;
  1163. n -= 60;
  1164. }
  1165. if (n == 0) return TRUE;
  1166. int oldCaretLine = caretLine, oldCaretChar = caretChar;
  1167. BOOL startEnd = (inputLineStart == textLast);
  1168. int i, p = textLast;
  1169. // Firstly I will make sure that there is room for the inserted text by
  1170. // stepping on n characters from the current end of the text buffer and
  1171. // wraping to free up the space I walk over. If this were to collide with
  1172. // the caret position I would be in a MESS so in such cases I will not
  1173. // perform the insert & will return a failure flag.
  1174. for (i=0; i<n; i++)
  1175. { p = (p+1)&TEXT_MASK;
  1176. if (p == textFirst)
  1177. { if (caretLine == lineFirst) return FALSE; // fail
  1178. else WrapTextBuffer(FALSE);
  1179. }
  1180. }
  1181. // Also I need to ensure that there is room for the number of new lines
  1182. // that I will insert. It will be rare that this is a problem, I suspect.
  1183. // Also beware here if the caret is positioned on the top line of the
  1184. // buffer.
  1185. int k = 0;
  1186. for (i=0; i<n; i++) if (s[i] == '\n') k++;
  1187. p = lineLast;
  1188. for (i=0; i<k; i++)
  1189. { p = (p+1)&LINE_MASK;
  1190. if (p == lineFirst)
  1191. { if (caretLine == lineFirst) return FALSE; // fail
  1192. else WrapTextBuffer(FALSE);
  1193. }
  1194. }
  1195. // Now I know that there is room for the insertion, I will "just" need to
  1196. // copy characters up through the text buffer.
  1197. char circle[64], circleFlags[64];
  1198. memcpy(circle, s, n);
  1199. memset(circleFlags, 0, n);
  1200. int inP = n, outP = 0;
  1201. // (line,p) is where I read characters from the buffer, while (line1,p1)
  1202. // is where I put them back. Certainly at the start, and quite often all
  1203. // the way through these values will remain in step. They may drift apart if
  1204. // previous deletions within lines lave left gaps in the buffer between the
  1205. // end of one line and the start of the next.
  1206. int line = caretLine;
  1207. p = caretChar;
  1208. int line1 = line, p1 = p;
  1209. while (p != textLast)
  1210. { int c = textBuffer[p];
  1211. circle[inP] = (char)c;
  1212. circleFlags[inP] = 0;
  1213. if (p == caretChar) circleFlags[inP] |= 1;
  1214. // I will leave inputLineStart where it is if it lies just before the
  1215. // caret position at the start of the insert operation.
  1216. if (p == inputLineStart && p != oldCaretChar) circleFlags[inP] |= 2;
  1217. if (p == icaretChar) circleFlags[inP] |= 4;
  1218. if (c == '\n')
  1219. { line = (line+1)&LINE_MASK;
  1220. p = lineBuffer[line].address;
  1221. }
  1222. else p = (p+1)&TEXT_MASK;
  1223. inP = (inP+1)&63;
  1224. int c1 = circle[outP];
  1225. textBuffer[p1] = (char)c1;
  1226. if (circleFlags[outP] & 1) caretChar = p1, caretLine = line1;
  1227. if (circleFlags[outP] & 2) inputLineStart = p1;
  1228. if (circleFlags[outP] & 4) icaretChar = p1, icaretLine = line1;
  1229. p1 = (p1+1)&TEXT_MASK;
  1230. outP = (outP+1)&63;
  1231. // Now if I had just inserted a newline I need to fix up the pointers
  1232. // from the line buffer into the text buffer.
  1233. if (c1 == '\n')
  1234. { if (line == line1) // Here I need to shuffle lines up
  1235. { int a = p1, line2=line;
  1236. lineLast = (lineLast+1)&LINE_MASK;
  1237. for (;;)
  1238. { line2 = (line2+1)&LINE_MASK;
  1239. int w = lineBuffer[line2].address;
  1240. lineBuffer[line2].address = a;
  1241. if (line2==lineLast) break;
  1242. a = w;
  1243. }
  1244. line = (line+1)&LINE_MASK;
  1245. }
  1246. line1 = (line1+1)&LINE_MASK;
  1247. lineBuffer[line1].address = p1;
  1248. }
  1249. }
  1250. while (inP != outP)
  1251. { int c1 = circle[outP];
  1252. textBuffer[p1] = (char)c1;
  1253. if (circleFlags[outP] & 1) caretChar = p1, caretLine = line1;
  1254. if (circleFlags[outP] & 2) inputLineStart = p1;
  1255. if (circleFlags[outP] & 4) icaretChar = p1, icaretLine = line1;
  1256. if (c1 == '\n')
  1257. { line1 = (line1+1)&LINE_MASK;
  1258. lineBuffer[line1].address = p1;
  1259. }
  1260. p1 = (p1+1)&TEXT_MASK;
  1261. outP = (outP+1)&63;
  1262. }
  1263. textLast = p1;
  1264. lineLast = line1;
  1265. // Here it may be that a newline had been inserted within the line that
  1266. // was being prepared for input. If so, stuff after that newline must be
  1267. // pushed back into the type-ahead buffer. When this happens it will always
  1268. // be the case that the text in the buffer after inputLineStart will form
  1269. // a compact block so I can just copy chars backwards
  1270. BOOL success = TRUE;
  1271. if (startEnd) inputLineStart = textLast;
  1272. else if (inputLineStart>=0 && k!=0)
  1273. { int lastNewline = inputLineStart;
  1274. while (lastNewline!=textLast && textBuffer[lastNewline]!='\n')
  1275. lastNewline = (lastNewline+1)&TEXT_MASK;
  1276. while (textLast!=lastNewline)
  1277. { int nn = (textLast-1)&TEXT_MASK;
  1278. if (textLast==caretChar) caretChar = nn;
  1279. if (textLast==icaretChar) icaretChar = nn;
  1280. if (textLast==selStartChar ||
  1281. textLast==selFirstChar ||
  1282. textLast==selEndChar) CancelSelection();
  1283. textLast = nn;
  1284. int c = textBuffer[textLast];
  1285. if (!UnTypeAhead(c)) success = FALSE;
  1286. if (c=='\n') lineLast = (lineLast-1)&LINE_MASK;
  1287. }
  1288. }
  1289. LineSizes();
  1290. CRect cr(0, LineY(oldCaretLine), clientWidth, clientHeight);
  1291. if (k==0) // No newlines inserted
  1292. { cr.left = caretX;
  1293. cr.bottom = LineY(caretLine)+LineDY(caretLine);
  1294. }
  1295. InvalidateRect(&cr);
  1296. UpdateWindow();
  1297. cwin_poll_window_manager(FALSE);
  1298. return success;
  1299. }
  1300. void CMainWindow::cwin_caret_putchar(int ch)
  1301. {
  1302. if (hasFinished) return;
  1303. // If the caret happens to be at the end maybe I ought not to have got here
  1304. // anyway, but I will chain to the code that inserts things at the end of the
  1305. // text.
  1306. if (caretChar == textLast)
  1307. { cwin_putchar(ch);
  1308. return;
  1309. }
  1310. char buff[4];
  1311. buff[0] = (char)ch;
  1312. // I beep if the insertion was impossible.
  1313. if (!InsertAtCaret(buff, 1)) ::MessageBeep(0xffffffff);
  1314. }
  1315. void CMainWindow::cwin_caret_replacechar(int ch)
  1316. {
  1317. if (hasFinished) return;
  1318. // If the caret happens to be at the end maybe I ought not to have got here
  1319. // anyway, but I will chain to the code that inserts things at the end of the
  1320. // text.
  1321. if (caretChar == textLast || ch == '\n' || textBuffer[caretChar] == '\n')
  1322. { cwin_putchar(ch);
  1323. return;
  1324. }
  1325. // Gosh this OUGHT to be easy!
  1326. textBuffer[caretChar] = (char)ch;
  1327. if (caretChar == icaretChar)
  1328. { icaretX += caretFontWidths[ch];
  1329. icaretChar++;
  1330. }
  1331. CRect cr(caretX, LineY(caretLine),
  1332. clientWidth, LineY(caretLine)+LineDY(caretLine));
  1333. InvalidateRect(&cr);
  1334. caretX += caretFontWidths[ch];
  1335. caretChar++;
  1336. UpdateWindow();
  1337. cwin_ensure_screen(FALSE);
  1338. }
  1339. // When I have put enough characters on the screen my text buffer may become
  1340. // full. In which case I will discard the earliest stored line in it. Because
  1341. // the line will always be stored with at least a newline character present
  1342. // this guarantees to free up at least one byte in the buffer. I can also
  1343. // call this routine to discard the top line of the buffer when I need to
  1344. // recycle space in the record of lines (as distinct from characters).
  1345. //
  1346. // If I attempt to cancel part of the buffer by coming here but either the
  1347. // top line of the buffer is visible on the screen or part of the top line
  1348. // is included in a selection then I will pause until the user scrolls the
  1349. // window or does something to the selection that will mean that throwing
  1350. // away information will not discard anything still being looked at.
  1351. //
  1352. // Note that when I say here "Part of the first line is selected" I will
  1353. // mean that a non-empty part of the line is selected. Not having a
  1354. // selection in force is indicated by selStartChar==selEndChar, but the
  1355. // selection pointers can otherwise point anywhere at all in the buffer
  1356. // or indeed outside it.
  1357. void CMainWindow::WrapTextBuffer(BOOL waitForSelection)
  1358. {
  1359. selRootValid = FALSE;
  1360. BOOL usualTitle = TRUE;
  1361. if (waitForSelection)
  1362. { while ((lineVisible == lineFirst && caretChar != textLast &&
  1363. LineY(lineLast)+LineDY(lineLast)>=clientHeight) ||
  1364. (selStartLine == lineFirst &&
  1365. selStartChar != selEndChar))
  1366. { cwin_ensure_screen(FALSE);
  1367. if (usualTitle)
  1368. usualTitle = FALSE,
  1369. SetWindowText(
  1370. "Output pending: cancel selection/scroll down please");
  1371. cwin_poll_window_manager(FALSE);
  1372. }
  1373. }
  1374. if (hasFinished) return;
  1375. if (!usualTitle) SetWindowText(mainTitle);
  1376. if (lineFirst==lineLast) OnClear(); // Drastic!
  1377. else
  1378. { lineFirst = (lineFirst+1)&LINE_MASK;
  1379. textFirst = lineBuffer[lineFirst].address;
  1380. }
  1381. }
  1382. void cwin_putchar(int c)
  1383. {
  1384. theApp.mainWindow->cwin_putchar(c);
  1385. inject_randomness(c);
  1386. inject_randomness((int)clock());
  1387. }
  1388. void CMainWindow::cwin_puts(const char *s)
  1389. {
  1390. if (hasFinished) return;
  1391. int ch;
  1392. // Here I should be careful and do just ONE call to Invalidate for all the
  1393. // changes that I make. But to start with it is MUCH easier to call putchar
  1394. // lots of times, so that is what I will do.
  1395. while ((ch=*s++)!=0) cwin_putchar(ch);
  1396. }
  1397. void cwin_puts(const char *s)
  1398. {
  1399. theApp.mainWindow->cwin_puts(s);
  1400. }
  1401. #ifndef MS_CDECL
  1402. #ifdef _MSC_VER
  1403. #define MS_CDECL __cdecl
  1404. #else
  1405. #define MS_CDECL
  1406. #endif
  1407. #endif
  1408. void MS_CDECL CMainWindow::cwin_printf(const char *s, ...)
  1409. {
  1410. if (hasFinished) return;
  1411. va_list a;
  1412. va_start(a, s);
  1413. cwin_vfprintf(s, a);
  1414. va_end(a);
  1415. }
  1416. void MS_CDECL cwin_printf(const char *s, ...)
  1417. {
  1418. if (hasFinished) return;
  1419. va_list a;
  1420. va_start(a, s);
  1421. theApp.mainWindow->cwin_vfprintf(s, a);
  1422. va_end(a);
  1423. }
  1424. // The versions of printf() that work with the window system use an internal
  1425. // buffer and print characters into that before sending them to the screen.
  1426. // It appears to be hard to check for overflow of this buffer, so the user
  1427. // will have to take care. A call to cwin_printf that generates more than
  1428. // MAX_PRINTF_OUTPUT characters can crash the system. Sorry. Maybe I
  1429. // should use the Watcom-specific _vbprintf() function here...
  1430. #define MAX_PRINTF_OUTPUT 256
  1431. void CMainWindow::cwin_vfprintf(const char *s, va_list a)
  1432. {
  1433. if (hasFinished) return;
  1434. char temp[MAX_PRINTF_OUTPUT];
  1435. #ifdef __WATCOMC__
  1436. _vbprintf(temp, MAX_PRINTF_OUTPUT, s, a);
  1437. // I put a zero byte at the end of the buffer to ensure that the string
  1438. // left there is properly terminated even if _vbprintf() wrote proper
  1439. // characters right up to the end.
  1440. temp[MAX_PRINTF_OUTPUT-1] = 0;
  1441. #else
  1442. vsprintf(temp, s, a);
  1443. #endif
  1444. cwin_puts(temp);
  1445. }
  1446. void cwin_vfprintf(const char *s, va_list a)
  1447. {
  1448. theApp.mainWindow->cwin_vfprintf(s, a);
  1449. }
  1450. void CMainWindow::cwin_ensure_screen(BOOL poll)
  1451. {
  1452. if (hasFinished) return;
  1453. // Here I need to do Invalidate operations, re-position the caret
  1454. // and adjust the position of scroll-bar thumbs.
  1455. if (caretVisible)
  1456. { OnVScroll(SB_FOR_CARET, 0, NULL);
  1457. OnHScroll(SB_FOR_CARET, 0, NULL);
  1458. }
  1459. // OnVScroll(SB_REFRESH_THUMB, 0, NULL);
  1460. HideCaret();
  1461. CPoint caretPos(caretX-xOffset, caretY);
  1462. SetCaretPos(caretPos);
  1463. ShowCaret();
  1464. if (poll) cwin_poll_window_manager(FALSE);
  1465. }
  1466. void cwin_ensure_screen()
  1467. {
  1468. theApp.mainWindow->cwin_ensure_screen(TRUE);
  1469. }
  1470. // This will remove a character from the buffer at a location defined
  1471. // by the caret, which will in general not be at the end of the text.
  1472. void CMainWindow::cwin_caret_unputchar()
  1473. {
  1474. // When I try to remove a character from the screen I know that there is
  1475. // no selection active, since a DELETE when there was a selection deleted it
  1476. // as a block, and so did not reach this bit of code. So perhaps I can
  1477. // make this code work by creating a selected region containing the
  1478. // character (or prompt string) that I to be discarded and then do a
  1479. // DeleteSelection.
  1480. if (hasFinished) return;
  1481. // If it happens that I am at the end of the text I will call the other
  1482. // bit of character deletion code, so that in the bulk of this function I
  1483. // can know that I am in the general case (which tends to be an expensive one)
  1484. if (caretChar==textLast)
  1485. { cwin_unputchar();
  1486. return;
  1487. }
  1488. selRootValid = FALSE;
  1489. // If the caret is right at the start of the buffer I do not have
  1490. // anything to delete. This will, of course, include the case where the
  1491. // buffer is totally empty.
  1492. if (caretChar==textFirst) return;
  1493. // Set up the start of the selection at the position of the caret.
  1494. selFirstChar = selStartChar = selEndChar = caretChar;
  1495. selFirstX = selStartX = selEndX = caretX;
  1496. selFirstLine = selStartLine = selEndLine = caretLine;
  1497. // Now I need to step back a "character", where that could be a newline
  1498. // or a prompt string rather than any single simple character. I also
  1499. // ought to worry here about deletion of items in the text buffer that
  1500. // control formatting. All in all this is pretty dodgy, so I will cope with
  1501. // just the simple cases to start with.
  1502. int n;
  1503. if (lineBuffer[caretLine].address == caretChar) // deleting a newline?
  1504. { return;
  1505. // For now I am going to prevent deletion of or through a prompt or newline
  1506. n = (caretLine-1)&LINE_MASK;
  1507. caretLine=n;
  1508. caretX = lineBuffer[n].width;
  1509. n = lineBuffer[n].address;
  1510. while (textBuffer[n]!='\n') n = (n+1)&TEXT_MASK;
  1511. }
  1512. else
  1513. { n = (caretChar-1) & TEXT_MASK;
  1514. // I produce just an APPROXIMATION to the desired caretX here, but want that
  1515. // so that I can limit the amount of re-painting that happens.
  1516. int w = windowFonts.HCourier.across['X'];
  1517. if ((textBuffer[n]&0xff)==CH_ENDPROMPT) // delete whole of a prompt
  1518. {
  1519. return; // do not delete prompt
  1520. do
  1521. { n = (n-1)&TEXT_MASK;
  1522. caretX -= w;
  1523. } while (n!=textFirst && (textBuffer[n]&0xff)!=CH_PROMPT);
  1524. }
  1525. else caretX -= w;
  1526. }
  1527. caretChar = n;
  1528. if (caretLine == lineLast)
  1529. { icaretChar = caretChar;
  1530. icaretLine = caretLine;
  1531. icaretX = caretX;
  1532. }
  1533. selStartChar = n;
  1534. selStartLine = caretLine;
  1535. selStartX = caretX;
  1536. DeleteSelection();
  1537. }
  1538. // Now a version of unputchar that deletes characters from the end of the
  1539. // text buffer (and the caret may or may not be there).
  1540. void CMainWindow::cwin_unputchar()
  1541. {
  1542. if (hasFinished) return;
  1543. selRootValid = FALSE;
  1544. int n;
  1545. if (textFirst == textLast) return; // utterly empty buffer
  1546. if (lineBuffer[lineLast].address == textLast) // deleting a newline?
  1547. { return;
  1548. n = (lineLast-1)&LINE_MASK;
  1549. endX = lineBuffer[n].width;
  1550. if (caretLine==lineLast) caretLine=n, caretX=endX;
  1551. if (icaretLine==lineLast) icaretLine=n, icaretX=endX;
  1552. lineLast=n;
  1553. n = lineBuffer[n].address;
  1554. while (textBuffer[n]!='\n') n = (n+1)&TEXT_MASK;
  1555. if (textLast == caretChar) caretChar = n;
  1556. if (textLast == icaretChar) icaretChar = n;
  1557. if (textLast == inputLineStart) inputLineStart = n;
  1558. textLast = n;
  1559. }
  1560. else
  1561. { n = (textLast-1) & TEXT_MASK;
  1562. int w = windowFonts.HCourier.across['X'];
  1563. if ((textBuffer[n]&0xff)==CH_ENDPROMPT) // delete whole of a prompt
  1564. { return;
  1565. do
  1566. { n = (n-1)&TEXT_MASK;
  1567. endX -= w;
  1568. } while (n!=textFirst && (textBuffer[n]&0xff)!=CH_PROMPT);
  1569. }
  1570. else endX -= w;
  1571. if (textLast == caretChar) caretChar = n;
  1572. if (textLast == icaretChar) icaretChar = n;
  1573. if (textLast == inputLineStart) inputLineStart = n;
  1574. textLast = n;
  1575. // The amount of invalidation done here runs across to the end of the
  1576. // last line, and that ought to get rid of any residues of the old
  1577. // caret. I use an APPROXIMATION to the new value of endX, but hope that
  1578. // that gets corrected during the re-painting.
  1579. CRect cr(endX-xOffset, LineY(lineLast),
  1580. clientWidth, LineY(lineLast)+LineDY(lineLast));
  1581. InvalidateRect(&cr); // Should I hide the caret here?
  1582. }
  1583. UpdateWindow();
  1584. HideCaret();
  1585. CPoint caretPos(caretX-xOffset, caretY);
  1586. SetCaretPos(caretPos);
  1587. ShowCaret();
  1588. }
  1589. int CMainWindow::cwin_getchar()
  1590. {
  1591. if (hasFinished) return EOF;
  1592. int ch;
  1593. if (inputP >= 0)
  1594. { ch = inputBuffer[inputP++];
  1595. if (ch == '\n') inputP = -1; // This buffer now all used up.
  1596. return ch;
  1597. }
  1598. cwin_putchar(CH_PROMPT);
  1599. cwin_puts(cwin_prompt_string);
  1600. if (spool_file != NULL) fprintf(spool_file, "%s", cwin_prompt_string);
  1601. cwin_putchar(CH_ENDPROMPT);
  1602. if (hasFinished) return EOF;
  1603. inputLineStart = textLast; // just beyond the prompt
  1604. // Next I move characters across from the type-ahead buffer into the main
  1605. // text buffer, echoing them as I go (and handling DELETE chars too). I
  1606. // stop when I find a newline. Perhaps some people would like it if some
  1607. // other characters would terminate an input line, but for the moment I am
  1608. // sticking to just newline.... except that any keyboard or menu activity
  1609. // that raises an "exception" will stop input for me rather abruptly.
  1610. for (;;)
  1611. { cwin_ensure_screen(FALSE);
  1612. while (typeAheadP1 == typeAheadP2 &&
  1613. clipboardInput == NULL)
  1614. {
  1615. // Note that in this case of poll_window_manager I am prepared
  1616. // to delay until something happens.
  1617. cwin_poll_window_manager(TRUE);
  1618. if (hasFinished || exception_pending()) return EOF;
  1619. }
  1620. if (clipboardInput != NULL)
  1621. { ch = *clipboardInputP++;
  1622. // When input is coming from the clipboard to the very end of the input
  1623. // text I will ignore copied prompt strings. Note that this is different
  1624. // from the situation with PASTE inserts into the middle of the text.
  1625. // A curious side-effect here is that if a regular TEXT clipboard file is
  1626. // pasted in and it happens to contain these funny character codes...
  1627. while ((ch&0xff) == CH_PROMPT)
  1628. { while ((ch = *clipboardInputP++) != 0 &&
  1629. (ch&0xff) != CH_ENDPROMPT);
  1630. if (ch == 0) break;
  1631. ch = *clipboardInputP++;
  1632. }
  1633. if (ch == '\r') continue;
  1634. if (ch == 0)
  1635. { free(clipboardInput);
  1636. clipboardInput = NULL;
  1637. continue;
  1638. }
  1639. }
  1640. else
  1641. { ch = typeAheadBuffer[typeAheadP1];
  1642. typeAheadP1 = (typeAheadP1 + 1) & typeAheadBufferMask;
  1643. }
  1644. if (ch == 0x7f) cwin_unputchar();
  1645. else cwin_putchar(ch);
  1646. if (ch == '\n' || hasFinished) break;
  1647. }
  1648. if (hasFinished) return EOF;
  1649. // By the time I exit the above loop there must be at least one character
  1650. // in the line that has just been read, even if it is only the terminating
  1651. // '\n'.
  1652. inputP = 0;
  1653. // When I copy stuff from the screen into the input buffer I discard any
  1654. // prompts. They might be there if the line of input was created in part by
  1655. // pasting material into the middle of a partly-typed line.
  1656. BOOL inPrompt = FALSE;
  1657. for (;;)
  1658. { ch = textBuffer[inputLineStart];
  1659. inputLineStart = (inputLineStart+1)&TEXT_MASK;
  1660. // Here I quietly truncate very very long input lines. For my first attempt
  1661. // I have inputBufferSize set at 2048.
  1662. if ((ch&0xff) == CH_PROMPT) inPrompt = TRUE;
  1663. if (inputP<inputBufferSize && !inPrompt) inputBuffer[inputP++] = (char)ch;
  1664. if ((ch&0xff) == CH_ENDPROMPT) inPrompt = FALSE;
  1665. else if (ch == '\n') break;
  1666. }
  1667. inputBuffer[inputBufferSize-1] = '\n';
  1668. // Now I have the next line of user input in inputBuffer, running from 0
  1669. // up to the first '\n' (and I know there is a '\n' in there somewhere).
  1670. // I want to copy it away into a "saved input lines" buffer, so that they
  1671. // can be retrieved later by a DOSKEY-like protocol. Note that provided
  1672. // I keep the size of the save buffer larger than that of the input buffer
  1673. // the copying operation here will not cause embarassing overflow!
  1674. currentInputLine = -1;
  1675. savedLines[savedP2++] = savedLast;
  1676. if (savedP2 == MAX_SAVED_LINES) savedP2 = 0;
  1677. if (savedP2 == savedP1)
  1678. { savedP1++;
  1679. if (savedP1 == MAX_SAVED_LINES) savedP1 = 0;
  1680. savedFirst = savedLines[savedP1];
  1681. }
  1682. inputP = 0;
  1683. for (;;)
  1684. { ch = inputBuffer[inputP++];
  1685. savedChars[savedLast++] = (char)ch;
  1686. if (savedLast == MAX_SAVED_CHARS) savedLast = 0;
  1687. if (savedLast == savedFirst)
  1688. { savedP1++;
  1689. if (savedP1 == MAX_SAVED_LINES) savedP1 = 0;
  1690. savedFirst = savedLines[savedP1];
  1691. }
  1692. if (ch == '\n') break;
  1693. }
  1694. inputP = 0;
  1695. ch = inputBuffer[inputP++];
  1696. if (ch == '\n') inputP = -1; // This buffer now all used up.
  1697. cwin_ensure_screen(FALSE);
  1698. return ch;
  1699. }
  1700. int cwin_getchar()
  1701. {
  1702. return theApp.mainWindow->cwin_getchar();
  1703. }
  1704. int CMainWindow::cwin_getchar_nowait()
  1705. {
  1706. if (hasFinished) return EOF;
  1707. int ch;
  1708. if (inputP >= 0)
  1709. { ch = inputBuffer[inputP++];
  1710. if (ch == '\n') inputP = -1; // This buffer now all used up.
  1711. return ch;
  1712. }
  1713. cwin_putchar(CH_PROMPT);
  1714. cwin_puts(cwin_prompt_string);
  1715. if (spool_file != NULL) fprintf(spool_file, "%s", cwin_prompt_string);
  1716. cwin_putchar(CH_ENDPROMPT);
  1717. if (hasFinished) return EOF;
  1718. inputLineStart = textLast; // just beyond the prompt
  1719. // Next I move characters across from the type-ahead buffer into the main
  1720. // text buffer, echoing them as I go (and handling DELETE chars too). I
  1721. // stop when I find a newline.
  1722. for (;;)
  1723. { cwin_ensure_screen(FALSE);
  1724. if (typeAheadP1 == typeAheadP2 &&
  1725. clipboardInput == NULL) return EOF;
  1726. if (clipboardInput != NULL)
  1727. { ch = *clipboardInputP++;
  1728. while ((ch&0xff) == CH_PROMPT)
  1729. { while ((ch = *clipboardInputP++) != 0 &&
  1730. (ch&0xff) != CH_ENDPROMPT);
  1731. if (ch == 0) break;
  1732. ch = *clipboardInputP++;
  1733. }
  1734. if (ch == '\r') continue;
  1735. if (ch == 0)
  1736. { free(clipboardInput);
  1737. clipboardInput = NULL;
  1738. continue;
  1739. }
  1740. }
  1741. else
  1742. { ch = typeAheadBuffer[typeAheadP1];
  1743. typeAheadP1 = (typeAheadP1 + 1) & typeAheadBufferMask;
  1744. }
  1745. if (ch == 0x7f) cwin_unputchar();
  1746. else cwin_putchar(ch);
  1747. if (ch == '\n' || hasFinished) break;
  1748. }
  1749. if (hasFinished) return EOF;
  1750. // By the time I exit the above loop there must be at least one character
  1751. // in the line that has just been read, even if it is only the terminating
  1752. // '\n'.
  1753. inputP = 0;
  1754. BOOL inPrompt = FALSE;
  1755. for (;;)
  1756. { ch = textBuffer[inputLineStart];
  1757. inputLineStart = (inputLineStart+1)&TEXT_MASK;
  1758. // Here I quietly truncate very very long input lines. For my first attempt
  1759. // I have inputBufferSize set at 2048.
  1760. if ((ch&0xff) == CH_PROMPT) inPrompt = TRUE;
  1761. if (inputP<inputBufferSize && !inPrompt) inputBuffer[inputP++] = (char)ch;
  1762. if ((ch&0xff) == CH_ENDPROMPT) inPrompt = FALSE;
  1763. else if (ch == '\n') break;
  1764. }
  1765. inputBuffer[inputBufferSize-1] = '\n';
  1766. inputP = 0;
  1767. ch = inputBuffer[inputP++];
  1768. if (ch == '\n') inputP = -1; // This buffer now all used up.
  1769. return ch;
  1770. }
  1771. int cwin_getchar_nowait()
  1772. {
  1773. return theApp.mainWindow->cwin_getchar_nowait();
  1774. }
  1775. void CMainWindow::cwin_discard_input()
  1776. {
  1777. typeAheadP1 = typeAheadP2 = 0;
  1778. if (clipboardInput) free(clipboardInput);
  1779. clipboardInput = 0;
  1780. }
  1781. void cwin_discard_input()
  1782. {
  1783. theApp.mainWindow->cwin_discard_input();
  1784. }
  1785. void CMainWindow::cwin_set_prompt(const char *s)
  1786. {
  1787. strncpy(cwin_prompt_string, s, 31);
  1788. cwin_prompt_string[31] = 0;
  1789. }
  1790. void cwin_set_prompt(const char *s)
  1791. {
  1792. theApp.mainWindow->cwin_set_prompt(s);
  1793. }
  1794. // The READ command on the menu will work by forcing the caret to the
  1795. // end of the input buffer and simulating some typed in text which
  1796. // contains a file-name. I will make a start at pretending to make it
  1797. // configurable here. But there seem to be LOTS of things that need to be
  1798. // changed to match the system that is using the window manager code...
  1799. // I hope that eventually I will provide configuration calls so that the
  1800. // grim detail here gets set from "user" code. The basic idea is that
  1801. // readFileCommand holds a string to be generated, with a marker (%) somewhere
  1802. // where the file-name should go. I need options to control processing
  1803. // of the file-name to get around reader escape conventions etc. I also need
  1804. // to tell the file selection dialog what sorts of files to look for...
  1805. #define VERBATIM_FILENAME 0
  1806. #define DOUBLE_BACKSLASH 1
  1807. #define DOUBLE_DOUBLEQUOTE 2
  1808. #ifdef COMMON
  1809. static char *readFileCommand = "(load \"%\")";
  1810. static int fileOptions = DOUBLE_BACKSLASH;
  1811. static char *defaultExtension = "lsp";
  1812. static char *defaultFile = "*.lsp";
  1813. static char *fileNameFilter =
  1814. "Lisp Files (*.lsp)|*.lsp|All Files (*.*)|*.*||\0";
  1815. #else
  1816. static char *readFileCommand = "in \"%\";";
  1817. static int fileOptions = DOUBLE_DOUBLEQUOTE;
  1818. static char *defaultExtension = "red";
  1819. static char *defaultFile = "*.RED;*.TST";
  1820. static char *fileNameFilter =
  1821. "Reduce (*.red/*.tst)|*.RED;*.TST|Lisp Files (*.lsp)|*.LSP|All Files (*.*)|*.*||\0";
  1822. #endif
  1823. void CMainWindow::OnRead()
  1824. {
  1825. OnEnd();
  1826. pageLine = caretLine;
  1827. CFileDialog FDialog(TRUE, defaultExtension,
  1828. defaultFile, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY,
  1829. fileNameFilter, NULL);
  1830. FDialog.m_ofn.lpstrTitle = "Read File";
  1831. if (FDialog.DoModal() != IDOK)
  1832. { cwin_ensure_screen(FALSE); // needed to re-position caret, it seems.
  1833. return;
  1834. }
  1835. CString p = FDialog.GetPathName();
  1836. int nameLength = p.GetLength();
  1837. char *q = readFileCommand;
  1838. int i = strlen(q);
  1839. while (i-- != 0)
  1840. { int ch = q[i];
  1841. if (ch == '%')
  1842. { int j = nameLength;
  1843. while (j-- != 0)
  1844. { int ch1 = p[j];
  1845. switch (fileOptions)
  1846. {
  1847. case DOUBLE_BACKSLASH:
  1848. if (ch1 == '\\') UnTypeAhead(ch1);
  1849. break;
  1850. case DOUBLE_DOUBLEQUOTE:
  1851. if (ch1 == '"') UnTypeAhead(ch1);
  1852. break;
  1853. default:break;
  1854. }
  1855. UnTypeAhead(ch1);
  1856. }
  1857. }
  1858. else UnTypeAhead(ch);
  1859. }
  1860. cwin_ensure_screen(FALSE);
  1861. }
  1862. #ifndef COMMON
  1863. static char **library_name_p = NULL;
  1864. static int library_name_n = 0;
  1865. static char *library_name[] =
  1866. {
  1867. "algint",
  1868. "applysym",
  1869. "arnum",
  1870. "assist",
  1871. "avector",
  1872. "boolean",
  1873. "cali",
  1874. "camal",
  1875. "changevr",
  1876. "compact",
  1877. "complex",
  1878. "crack",
  1879. "cvit",
  1880. "decompos",
  1881. "defint",
  1882. "desir",
  1883. "dfpart",
  1884. "dummy",
  1885. "elem",
  1886. "excalc",
  1887. "fide",
  1888. "fps",
  1889. "gentran",
  1890. "gnuplot",
  1891. "groebner",
  1892. "ideals",
  1893. "ineq",
  1894. "invbase",
  1895. "laplace",
  1896. "lie",
  1897. "linalg",
  1898. "modsr",
  1899. "ncpoly",
  1900. "normform",
  1901. "numeric",
  1902. "odesolve",
  1903. "orthovec",
  1904. "physop",
  1905. "pmrules",
  1906. "randpoly",
  1907. "reacteqn",
  1908. "residue",
  1909. "rlfi",
  1910. "rsolve",
  1911. "scope",
  1912. "sets",
  1913. "spde",
  1914. "specfn",
  1915. "symmetry",
  1916. "taylor",
  1917. "tps",
  1918. "tri",
  1919. "trigsimp",
  1920. "wu",
  1921. "xcolor",
  1922. "xideal",
  1923. "zeilberg",
  1924. "ztrans"
  1925. };
  1926. void CMainWindow::OnLoadLibrary(UINT a)
  1927. {
  1928. OnEnd();
  1929. pageLine = caretLine;
  1930. char q[100];
  1931. int i;
  1932. if (library_name_p != NULL)
  1933. { if (a-IDM_FIRSTLOAD >= library_name_n) return;
  1934. sprintf(q, "load_package %s;\n",
  1935. library_name_p[a - IDM_FIRSTLOAD]);
  1936. }
  1937. else
  1938. { sprintf(q, "load_package %s;\n", library_name[a - IDM_FIRSTLOAD]);
  1939. }
  1940. i = strlen(q);
  1941. while (i-- != 0)
  1942. { int ch = q[i];
  1943. UnTypeAhead(ch);
  1944. }
  1945. cwin_ensure_screen(FALSE);
  1946. }
  1947. static char **switch_name_p = NULL;
  1948. static int switch_name_n = 0;
  1949. static struct { char *name; int status; } switch_name[] =
  1950. {
  1951. {"algint", 0},
  1952. {"adjprec", 0},
  1953. {"allbranch", 0},
  1954. {"allfac", 1},
  1955. {"arbvars", 0},
  1956. {"asterisk", 1},
  1957. {"backtrace", 0},
  1958. {"balanced_mod", 0},
  1959. {"bfspace", 0},
  1960. {"combineexpt", 0},
  1961. {"combinelogs", 0},
  1962. {"comp", 0},
  1963. {"complex", 0},
  1964. {"compxroots", 0},
  1965. {"cramer", 0},
  1966. {"cref", 0},
  1967. {"defn", 0},
  1968. {"demo", 0},
  1969. {"dfprint", 0},
  1970. {"div", 0},
  1971. {"echo", 0},
  1972. {"errcont", 0},
  1973. {"evallhseqp", 0},
  1974. {"exp", 1},
  1975. {"expandexpt", 1},
  1976. {"expandlogs", 0},
  1977. {"ezgcd", 0},
  1978. {"factor", 0},
  1979. {"fastfor", 0},
  1980. {"force", 0},
  1981. {"fort", 0},
  1982. {"fortupper", 0},
  1983. {"fullprec", 0},
  1984. {"fullprecision", 0},
  1985. {"fullroots", 0},
  1986. {"gc", 0},
  1987. {"gcd", 0},
  1988. {"heugcd", 0},
  1989. {"horner", 0},
  1990. {"ifactor", 0},
  1991. {"int", 0},
  1992. {"intstr", 0},
  1993. {"lcm", 1},
  1994. {"lessspace", 0},
  1995. {"limitedfactors", 0},
  1996. {"list", 0},
  1997. {"listargs", 0},
  1998. {"lower", 1},
  1999. {"mcd", 1},
  2000. {"modular", 0},
  2001. {"msg", 1},
  2002. {"multiplicities", 0},
  2003. {"nat", 1},
  2004. {"nero", 0},
  2005. {"noarg", 1},
  2006. {"noconvert", 0},
  2007. {"nonlnr", 0},
  2008. {"nosplit", 1},
  2009. {"numval", 1},
  2010. {"output", 1},
  2011. {"period", 1},
  2012. {"pgwd", 0},
  2013. {"plap", 0},
  2014. {"precise", 1},
  2015. {"pret", 0},
  2016. {"pri", 1},
  2017. {"pwrds", 1},
  2018. {"quotenewnam", 1},
  2019. {"raise", 0},
  2020. {"rat", 0},
  2021. {"ratarg", 0},
  2022. {"rational", 0},
  2023. {"rationalize", 0},
  2024. {"ratpri", 1},
  2025. {"reduced", 0},
  2026. {"revpri", 0},
  2027. {"rlisp88", 0},
  2028. {"rootmsg", 0},
  2029. {"roundall", 1},
  2030. {"roundbf", 0},
  2031. {"rounded", 0},
  2032. {"savestructr", 0},
  2033. {"solvesingular", 0},
  2034. {"time", 0},
  2035. {"trallfac", 0},
  2036. {"trfac", 0},
  2037. {"trint", 0},
  2038. {"trroot", 0}
  2039. };
  2040. void CMainWindow::OnSwitch(UINT a)
  2041. {
  2042. OnEnd();
  2043. pageLine = caretLine;
  2044. char q[100];
  2045. int i;
  2046. if (switch_name_p != NULL)
  2047. { int n = a - IDS_FIRSTSWITCH;
  2048. if (n >= switch_name_n) return;
  2049. i = switch_name_p[n][0] == 'y';
  2050. switch_name_p[n][0] ^= ('y' ^ 'n');
  2051. // Note well - if the user types "on xxx" or "off xxx" directly rather than
  2052. // by using this mechanism then the checked or otherwise status of the menu
  2053. // items will get out of step. Tough luck. If you use the menu to set or clear
  2054. // a switch the switch will certainly be brought into line with what the
  2055. // menu information indicates.
  2056. GetMenu()->CheckMenuItem(a, MF_BYCOMMAND |
  2057. (i ? MF_UNCHECKED : MF_CHECKED));
  2058. DrawMenuBar();
  2059. sprintf(q, "%s %s;\n",
  2060. i ? "off" : "on",
  2061. 1+switch_name_p[n]);
  2062. }
  2063. else
  2064. { i = switch_name[a - IDS_FIRSTSWITCH].status;
  2065. switch_name[a - IDS_FIRSTSWITCH].status = !i;
  2066. // Note well - if the user types "on xxx" or "off xxx" directly rather than
  2067. // by using this mechanism then the checked or otherwise status of the menu
  2068. // items will get out of step. Tough luck. If you use the menu to set or clear
  2069. // a switch the switch will certainly be brought into line with what the
  2070. // menu information indicates.
  2071. GetMenu()->CheckMenuItem(a, MF_BYCOMMAND |
  2072. (i ? MF_UNCHECKED : MF_CHECKED));
  2073. DrawMenuBar();
  2074. sprintf(q, "%s %s;\n",
  2075. i ? "off" : "on",
  2076. switch_name[a - IDS_FIRSTSWITCH].name);
  2077. }
  2078. i = strlen(q);
  2079. while (i-- != 0)
  2080. { int ch = q[i];
  2081. UnTypeAhead(ch);
  2082. }
  2083. cwin_ensure_screen(FALSE);
  2084. }
  2085. #endif
  2086. void CMainWindow::OnExit()
  2087. {
  2088. pageLine = caretLine;
  2089. DestroyWindow();
  2090. }
  2091. int cwin_interrupt_pending = 0;
  2092. void CMainWindow::OnInterrupt()
  2093. {
  2094. pageLine = caretLine;
  2095. cwin_interrupt_pending = 1;
  2096. cwin_ensure_screen(FALSE);
  2097. }
  2098. void CMainWindow::OnBacktrace()
  2099. {
  2100. pageLine = caretLine;
  2101. cwin_interrupt_pending = 3;
  2102. cwin_ensure_screen(FALSE);
  2103. }
  2104. void CMainWindow::OnPageMode()
  2105. {
  2106. pageLine = caretLine;
  2107. pageMode = !pageMode;
  2108. GetMenu()->CheckMenuItem(IDM_PAGEMODE,
  2109. MF_BYCOMMAND | (pageMode ? MF_CHECKED : MF_UNCHECKED));
  2110. DrawMenuBar();
  2111. cwin_ensure_screen(FALSE);
  2112. }
  2113. #ifdef GRAPHICS_WINDOW
  2114. void CMainWindow::OnGraphics()
  2115. {
  2116. graphicsWindow->ShowWindow(graphicsShown ? SW_HIDE : SW_SHOW);
  2117. if (graphicsShown) graphicsWindow->viewpointWindow.ShowWindow(SW_HIDE);
  2118. graphicsShown = !graphicsShown;
  2119. if (graphicsShown) graphicsWindow->Invalidate();
  2120. GetMenu()->CheckMenuItem(IDM_GRAPHICS,
  2121. MF_BYCOMMAND | (graphicsShown ? MF_CHECKED : MF_UNCHECKED));
  2122. DrawMenuBar();
  2123. cwin_ensure_screen(FALSE);
  2124. }
  2125. void CMainWindow::OnGraphics1(UINT a, LONG b)
  2126. {
  2127. OnGraphics();
  2128. }
  2129. #endif
  2130. void CMainWindow::ReWriteTitleText()
  2131. {
  2132. if (hasFinished) return;
  2133. CWindowDC dc(this);
  2134. // It is not (at present) clear to me how to decide how wide to make
  2135. // the title. What follows is some sort of a guess.
  2136. CRect clientArea;
  2137. GetClientRect(&clientArea);
  2138. int wTitle = clientArea.Width() -
  2139. 3*::GetSystemMetrics(SM_CXVSCROLL);
  2140. int wLeft = dc.GetTextExtent(cLeft, strlen(cLeft)).cx;
  2141. int wMid = dc.GetTextExtent(cMid, strlen(cMid)).cx;
  2142. int wRight = dc.GetTextExtent(cRight, strlen(cRight)).cx;
  2143. // For the calculations about padding that I use here to work the font used
  2144. // in the title bar had better not do any kerning and overhang-effects must
  2145. // not interfere. I find I have all sorts of horrid problems if I try to
  2146. // use regular blank characters for padding, but '\xa0' displays as blank
  2147. // space and is better behaved. It also seems (???) that at least under
  2148. // Windows 3.1 there is no great joy in trying to use caption strings that
  2149. // are longer than 78 characters...
  2150. #define PADDING_CHAR '\xa0'
  2151. char strSp[4]; strSp[0] = PADDING_CHAR;
  2152. int wSp = dc.GetTextExtent(strSp, 1).cx;
  2153. int cw = wTitle / wSp;
  2154. // I first measure things on the supposition that I would allow up to
  2155. // 90 characters in the title. After balancing it a bit I cut that
  2156. // down to the 78 that Windows 3.1 seems to be prepared to tolerate.
  2157. if (cw > 90) wTitle = 90*wSp;
  2158. int pad = (wTitle - wMid)/2;
  2159. char *l = cLeft, *r = cRight;
  2160. for (;;)
  2161. { int padLeft = (pad - wLeft) / wSp;
  2162. int padRight = (pad - wRight) / wSp;
  2163. int excess = strlen(cLeft) + padLeft + strlen(cMid) +
  2164. padRight + strlen(cRight) - 78;
  2165. if (excess > 0)
  2166. { if (excess & 1)
  2167. { if (padLeft > padRight) padLeft--; else padRight--;
  2168. excess--;
  2169. }
  2170. excess /= 2;
  2171. padLeft -= excess;
  2172. padRight -= excess;
  2173. }
  2174. #ifdef DISCARD_LEFT_IF_TITLE_OVERFULL
  2175. if (padLeft <= 0 && padRight <= 0)
  2176. { strcpy(mainTitle, cMid); // Abandon both right & left items
  2177. break;
  2178. }
  2179. #else
  2180. // Here even though there is no room to display the whole text I wanted
  2181. // on the title bar (maybe the window had been iconized?) I will display
  2182. // the left & mid parts with a single blank between.
  2183. if (padLeft <= 0 && padRight <= 0)
  2184. { sprintf(mainTitle, "%s%c%s", cLeft, PADDING_CHAR, cMid);
  2185. break;
  2186. }
  2187. #endif
  2188. else
  2189. {
  2190. #ifdef DISCARD_LEFT_IF_TITLE_OVERFULL
  2191. if (padLeft <= 0 && wLeft != 0)
  2192. { l = ""; // Abandon left item & re-try
  2193. wLeft = 0;
  2194. continue;
  2195. }
  2196. #endif
  2197. if (padRight <= 0 && wRight != 0) // Abandon right item & re-try
  2198. { r = "";
  2199. wRight = 0;
  2200. continue;
  2201. }
  2202. char *p = mainTitle;
  2203. while (*l != 0) *p++ = *l++;
  2204. int i;
  2205. for (i=0; i<padLeft; i++) *p++ = PADDING_CHAR;
  2206. l = cMid;
  2207. while (*l != 0) *p++ = *l++;
  2208. for (i=0; i<padRight; i++) *p++ = PADDING_CHAR;
  2209. while (*r != 0) *p++ = *r++;
  2210. *p = 0;
  2211. break;
  2212. }
  2213. }
  2214. SetWindowText(mainTitle);
  2215. }
  2216. void CMainWindow::cwin_report_left(const char *msg)
  2217. {
  2218. // I will take this opportunity to make the "To-File" menu item track
  2219. // whether there really is a spool file active. This only effects how
  2220. // the menu gets displayed, not what it does...
  2221. if (complete)
  2222. { GetMenu()->ModifyMenu(IDM_TOFILE+MF_STRING, MF_BYCOMMAND, IDM_TOFILE,
  2223. spool_file==NULL ? "&To File ..." :
  2224. "&Terminate log");
  2225. DrawMenuBar();
  2226. }
  2227. if (msg == NULL) msg = "";
  2228. //!!if (msg == NULL)
  2229. //!!{ leftSetByUser = FALSE;
  2230. //!! return; // Date and time of day will appear
  2231. //!!}
  2232. strncpy(cLeft, msg, 31); cLeft[31] = 0;
  2233. ReWriteTitleText();
  2234. leftSetByUser = TRUE;
  2235. }
  2236. void cwin_report_left(const char *s)
  2237. {
  2238. theApp.mainWindow->cwin_report_left(s);
  2239. }
  2240. void CMainWindow::cwin_report_mid(const char *msg)
  2241. {
  2242. if (msg == NULL) msg = programName;
  2243. strncpy(cMid, msg, 31); cLeft[31] = 0;
  2244. ReWriteTitleText();
  2245. }
  2246. void cwin_report_mid(const char *s)
  2247. {
  2248. theApp.mainWindow->cwin_report_mid(s);
  2249. }
  2250. void CMainWindow::cwin_report_right(const char *msg)
  2251. {
  2252. if (msg == NULL) msg = "";
  2253. strncpy(cRight, msg, 31); cRight[31] = 0;
  2254. ReWriteTitleText();
  2255. }
  2256. void cwin_report_right(const char *s)
  2257. {
  2258. theApp.mainWindow->cwin_report_right(s);
  2259. }
  2260. //!! The code (in c_applic.cpp) that updated this info on the screen
  2261. //!! has now been removed, so this is no longer useful.
  2262. //!!
  2263. //!! void CMainWindow::cwin_display_date()
  2264. //!! {
  2265. //!! char dateBuffer[20];
  2266. //!! time_t t0 = time(NULL);
  2267. //!! char *m = ctime(&t0);
  2268. //!! #ifdef OLD_AND_USES_UNIVERSAL_TIME_WHICH_IS_SILLY
  2269. //!! sprintf(dateBuffer, "%02d-%.3s-%02d, %02d:%02d:%02d",
  2270. //!! titleUpdateTime.wDay,
  2271. //!! "xxxJanFebMarAprMayJunJulAugSepOctNovDec"+3*titleUpdateTime.wMonth,
  2272. //!! titleUpdateTime.wYear%100,
  2273. //!! titleUpdateTime.wHour, titleUpdateTime.wMinute,
  2274. //!! titleUpdateTime.wSecond);
  2275. //!! #else
  2276. //!! sprintf(dateBuffer, "%.2s-%.3s-%.2s, %.8s",
  2277. //!! m+8, m+4, m+22, m+11);
  2278. //!! #endif
  2279. //!! cwin_report_left(dateBuffer);
  2280. //!! leftSetByUser = FALSE;
  2281. //!! }
  2282. void cwin_menus(char **packages, char **switches)
  2283. {
  2284. theApp.mainWindow->cwin_menus(packages, switches);
  2285. }
  2286. void CMainWindow::cwin_menus(char **packages, char **switches)
  2287. {
  2288. CMenu *main = GetMenu(); // main menu bar for this window
  2289. #ifndef COMMON
  2290. if (packages != NULL && *packages != NULL)
  2291. {
  2292. library_name_p = packages;
  2293. main->DeleteMenu(4, MF_BYPOSITION);
  2294. int n = IDM_FIRSTLOAD;
  2295. CMenu mload;
  2296. mload.CreatePopupMenu();
  2297. int firstletter = 'a';
  2298. int lastletter = 'a', nextletter;
  2299. int count = 0, nextcount;
  2300. char **p = packages;
  2301. library_name_n = 0;
  2302. while (*p++ != NULL) library_name_n++;
  2303. p = packages;
  2304. while (*p && **p == lastletter) count++, p++;
  2305. char **p1 = p;
  2306. while (*packages)
  2307. { for (;;)
  2308. { nextcount = 0;
  2309. nextletter = lastletter + 1;
  2310. while (*p && **p == nextletter) nextcount++, p++;
  2311. if (count + nextcount > 20) break;
  2312. lastletter = nextletter;
  2313. count += nextcount;
  2314. p1 = p;
  2315. if (lastletter == 'z') break;
  2316. }
  2317. char subname[8];
  2318. if (firstletter == lastletter) sprintf(subname, "%c", firstletter);
  2319. else sprintf(subname, "%c-%c", firstletter, lastletter);
  2320. CMenu sub1;
  2321. sub1.CreatePopupMenu();
  2322. while (packages != p1)
  2323. sub1.AppendMenu(MF_STRING, n++, *packages++);
  2324. mload.AppendMenu(MF_STRING | MF_POPUP, (UINT)sub1.Detach(), subname);
  2325. firstletter = lastletter = nextletter;
  2326. count = nextcount;
  2327. p1 = p;
  2328. }
  2329. main->InsertMenu(4, MF_BYPOSITION | MF_POPUP | MF_STRING,
  2330. (UINT)mload.Detach(), "&Load");
  2331. }
  2332. // Now do roughly the same with switches
  2333. if (switches != NULL && *switches != NULL)
  2334. { switch_name_p = switches;
  2335. main->DeleteMenu(5, MF_BYPOSITION);
  2336. int n = IDS_FIRSTSWITCH;
  2337. CMenu mload;
  2338. mload.CreatePopupMenu();
  2339. int firstletter = 'a';
  2340. int lastletter = 'a', nextletter;
  2341. int count = 0, nextcount;
  2342. char **p = switches;
  2343. switch_name_n = 0;
  2344. while (*p++ != NULL) switch_name_n++;
  2345. p = switches;
  2346. while (*p && (*p)[1] == lastletter) count++, p++;
  2347. char **p1 = p;
  2348. while (*switches)
  2349. { for (;;)
  2350. { nextcount = 0;
  2351. nextletter = lastletter + 1;
  2352. while (*p && (*p)[1] == nextletter) nextcount++, p++;
  2353. if (count + nextcount > 20) break;
  2354. lastletter = nextletter;
  2355. count += nextcount;
  2356. p1 = p;
  2357. if (lastletter == 'z') break;
  2358. }
  2359. char subname[8];
  2360. if (firstletter == lastletter) sprintf(subname, "%c", firstletter);
  2361. else sprintf(subname, "%c-%c", firstletter, lastletter);
  2362. CMenu sub1;
  2363. sub1.CreatePopupMenu();
  2364. while (switches != p1)
  2365. { sub1.AppendMenu(MF_STRING, n, 1+*switches);
  2366. sub1.CheckMenuItem(n, MF_BYCOMMAND |
  2367. (**switches=='y' ? MF_CHECKED : MF_UNCHECKED));
  2368. n++;
  2369. switches++;
  2370. }
  2371. mload.AppendMenu(MF_STRING | MF_POPUP, (UINT)sub1.Detach(), subname);
  2372. firstletter = lastletter = nextletter;
  2373. count = nextcount;
  2374. p1 = p;
  2375. }
  2376. main->InsertMenu(5, MF_BYPOSITION | MF_POPUP | MF_STRING,
  2377. (UINT)mload.Detach(), "&Switch");
  2378. }
  2379. #endif /* COMMON */
  2380. DrawMenuBar();
  2381. }
  2382. // When the user types in a key all that happens (to start with) is that
  2383. // I store it in a buffer. If the buffer gets to be full I beep and
  2384. // ignore any further characters.
  2385. #define Ctrl(x) ((x) & 0x1f)
  2386. void CMainWindow::OnChar(UINT ch, UINT nRepCnt, UINT nFlags)
  2387. {
  2388. CSL_IGNORE(nRepCnt);
  2389. CSL_IGNORE(nFlags);
  2390. // I turn the ENTER key into a newline character (and consequently ^M will
  2391. // also go that way). I also discard 0x7f (for the delete/erase key) and
  2392. // anything in the range 0x80 to 0xa0 (which I reserve for my own private
  2393. // use in the text buffer). I ignore the repetition count.
  2394. // I also have to process some accelerators.
  2395. pageLine = caretLine;
  2396. // The following line is in effect following a request from F J Wright. He
  2397. // pointed out that the character that gets pressed when one is in page mode
  2398. // to un-pause the screen should be allowed to do just that and should not
  2399. // also appear as an input character. So here if I find a character being
  2400. // typed when things are paused I just lose it (as well as clearing the
  2401. // flags that pause things.
  2402. if (pagePaused)
  2403. { pagePaused = FALSE;
  2404. return;
  2405. }
  2406. inject_randomness(ch);
  2407. inject_randomness((int)time(NULL));
  2408. switch (ch)
  2409. {
  2410. case Ctrl('C'):
  2411. ToIcaret();
  2412. caretVisible = TRUE;
  2413. cwin_ensure_screen(FALSE); // may jump screen to caret location
  2414. OnInterrupt();
  2415. return;
  2416. // I make Ctrl+D and Ctrl+Z exit, but note very well that they both
  2417. // exit from this system super-promptly when the key is typed, and
  2418. // they do NOT wait until the program gets around to reading them.
  2419. case Ctrl('D'): // Some Unix users may be used to Ctrl+D as EOF?
  2420. OnExit();
  2421. return;
  2422. case Ctrl('G'):
  2423. ToIcaret();
  2424. caretVisible = TRUE;
  2425. cwin_ensure_screen(FALSE); // may jump screen to caret location
  2426. OnBacktrace();
  2427. return;
  2428. case Ctrl('I'): // TAB (I need to worry about the display of tabs!)
  2429. break;
  2430. case Ctrl('J'): // LINEFEED
  2431. ch = '\n';
  2432. break;
  2433. case Ctrl('L'):
  2434. OnRedraw();
  2435. return;
  2436. case Ctrl('M'): // CARRIAGE RETURN
  2437. ch = '\n';
  2438. case Ctrl('N'):
  2439. OnEnd(); // so that CR accepts the line always
  2440. break;
  2441. case Ctrl('O'):
  2442. OnCopy();
  2443. return;
  2444. case Ctrl('Q'):
  2445. if (pageMode) OnPageMode();
  2446. return;
  2447. case Ctrl('R'):
  2448. OnReInput();
  2449. return;
  2450. case Ctrl('S'):
  2451. if (!pageMode) OnPageMode();
  2452. return;
  2453. case Ctrl('V'):
  2454. OnPaste();
  2455. return;
  2456. case Ctrl('X'):
  2457. OnCut();
  2458. return;
  2459. case Ctrl('Z'): // Some PC users may be used to Ctrl+Z as EOF?
  2460. OnExit();
  2461. return;
  2462. case 0x1b: // ESC goes through to the user
  2463. break;
  2464. default:
  2465. if ((ch & 0x7f) < 0x20) return; // Discard control chars.
  2466. break;
  2467. }
  2468. CancelSelection();
  2469. ToIcaret();
  2470. caretVisible = TRUE;
  2471. cwin_ensure_screen(FALSE);
  2472. // If the caret is NOT at the end of the text then the insert operation I do
  2473. // here goes directly into the text buffer. If the caret IS at the end of the
  2474. // text I push the character into a type-ahead buffer, and beep if there
  2475. // was no room for it there.
  2476. if (caretChar != textLast)
  2477. { if (insertMode) cwin_caret_putchar(ch);
  2478. else cwin_caret_replacechar(ch);
  2479. }
  2480. else
  2481. { int p2 = (typeAheadP2 + 1) & typeAheadBufferMask;
  2482. if (p2 == typeAheadP1)
  2483. { ::MessageBeep(0xffffffff);
  2484. return;
  2485. }
  2486. typeAheadBuffer[typeAheadP2] = (char)ch;
  2487. typeAheadP2 = p2;
  2488. }
  2489. }
  2490. void CMainWindow::ReplaceLastLine(unsigned char *s)
  2491. {
  2492. // The argument s here is ignored, and maybe is not needed????
  2493. typeAheadP1 = typeAheadP2 = 0; // cancel type-ahead
  2494. selRootValid = FALSE;
  2495. OnEnd();
  2496. caretVisible = TRUE;
  2497. // The following loop deletes characters from the last line of the input,
  2498. // until what is left is (a) a totally empty buffer, (b) a buffer where the
  2499. // last line is empty or (c) the final thing in the buffer is a prompt
  2500. // item. While doing the deletion I will not update the screen at all.
  2501. while (textFirst != textLast &&
  2502. lineBuffer[lineLast].address != textLast)
  2503. { int n = (textLast-1) & TEXT_MASK;
  2504. if ((textBuffer[n]&0xff)==CH_ENDPROMPT) break;
  2505. if (textLast == caretChar) caretChar = n;
  2506. if (textLast == icaretChar) icaretChar = n;
  2507. if (textLast == inputLineStart) inputLineStart = n;
  2508. textLast = n;
  2509. }
  2510. int i=savedLines[currentInputLine];
  2511. for (;;)
  2512. { int ch = savedChars[i++];
  2513. if (ch == '\n') break;
  2514. if (i == MAX_SAVED_CHARS) i = 0;
  2515. cwin_putchar(ch);
  2516. }
  2517. CRect cr(0, LineY(lineLast),
  2518. clientWidth, LineY(lineLast)+LineDY(lineLast));
  2519. InvalidateRect(&cr);
  2520. LineSizes();
  2521. UpdateWindow();
  2522. cwin_ensure_screen(FALSE);
  2523. }
  2524. void CMainWindow::OnKeyDown(UINT ch, UINT nRepCnt, UINT nFlags)
  2525. {
  2526. CPoint cp;
  2527. int saveX, saveY;
  2528. CSL_IGNORE(nRepCnt);
  2529. pageLine = caretLine;
  2530. switch (ch)
  2531. {
  2532. // The INS key flips the status with respect to insert mode. I make this
  2533. // TRUE at the start of a run, and any time anybody does a PASTE operation.
  2534. case VK_INSERT:
  2535. insertMode = !insertMode;
  2536. return;
  2537. // I make the DEL key delete forwards by first simulating a right-arrow
  2538. // cursor movement effect and then performing the sort of deletion that
  2539. // I consider ordinary.
  2540. case VK_DELETE:
  2541. ToIcaret();
  2542. caretVisible = TRUE;
  2543. cwin_ensure_screen(FALSE);
  2544. if (selStartChar != selEndChar)
  2545. { DeleteSelection();
  2546. return;
  2547. }
  2548. if (caretChar == textLast) return;
  2549. OnKeyDown(VK_RIGHT, 1, nFlags);
  2550. if (caretChar != textLast)
  2551. { cwin_caret_unputchar();
  2552. return;
  2553. }
  2554. if (typeAheadP1 == typeAheadP2)
  2555. { typeAheadBuffer[typeAheadP2] = 0x7f;
  2556. typeAheadP2 = (typeAheadP2 + 1) & typeAheadBufferMask;
  2557. return;
  2558. }
  2559. typeAheadP2 = (typeAheadP2 - 1) & typeAheadBufferMask;
  2560. return;
  2561. case VK_BACK:
  2562. ToIcaret();
  2563. caretVisible = TRUE;
  2564. cwin_ensure_screen(FALSE); // may jump screen to caret location
  2565. // If there is a selection valid then DELETE will delete it, without any
  2566. // regard for where the caret is (although the caret will generally be
  2567. // at one or other end of the selected region).
  2568. if (selStartChar != selEndChar)
  2569. { DeleteSelection();
  2570. return;
  2571. }
  2572. // If the caret is NOT at the end of the text then DELETE has to work
  2573. // inside the buffer. This will typically make it a fairly expensive
  2574. // operation.
  2575. if (caretChar != textLast)
  2576. { cwin_caret_unputchar();
  2577. return;
  2578. }
  2579. // If there are no characters typed ahead I will put 0x7f into the
  2580. // input buffer. This case can arise in two different circumstances. The
  2581. // first is if a user types DELETE as the first character on a line. Then
  2582. // I will want to discard the junk characters later on. Otherwise it may be
  2583. // that the current line is partly displayed and is subject to interactive
  2584. // editing and then the 0x7f put in the buffer will be found again very
  2585. // soon and used to cause deletion of some prior character. Because of this
  2586. // latter case I will not beep on initial DELETE characters on a line.
  2587. if (typeAheadP1 == typeAheadP2)
  2588. { typeAheadBuffer[typeAheadP2] = 0x7f;
  2589. typeAheadP2 = (typeAheadP2 + 1) & typeAheadBufferMask;
  2590. return;
  2591. }
  2592. // If there are typed-ahead characters I can just discard the most recent. At
  2593. // this stage the character has not been displayed (eg a user typed XXX DEL
  2594. // before the application was ready to look at it) so the removal is really
  2595. // very easy.
  2596. typeAheadP2 = (typeAheadP2 - 1) & typeAheadBufferMask;
  2597. return;
  2598. // The various keys like "PAGE UP" etc do just what the scroll bar can do.
  2599. case VK_PRIOR:
  2600. OnVScroll(SB_PAGEUP, 0, NULL);
  2601. return;
  2602. case VK_NEXT:
  2603. OnVScroll(SB_PAGEDOWN, 0, NULL);
  2604. return;
  2605. case VK_HOME:
  2606. OnHome();
  2607. return;
  2608. case VK_END:
  2609. OnEnd();
  2610. return;
  2611. // The cursor arrow keys are funny here, with behaviour inspired by the
  2612. // DOSKEY history program. Right and left movement moves the caret, but
  2613. // up and down will bring back one of a number of stored input line.
  2614. case VK_UP:
  2615. // If my caret is on the last line I will do something DOSKEY-ish...
  2616. if (caretLine == lineLast)
  2617. { if (currentInputLine == savedP1) return;
  2618. if (currentInputLine == -1) currentInputLine = savedP2;
  2619. if (currentInputLine == 0) currentInputLine = MAX_SAVED_LINES;
  2620. currentInputLine--;
  2621. ReplaceLastLine(&savedChars[savedLines[currentInputLine]]);
  2622. return;
  2623. }
  2624. if (caretLine == lineFirst) return;
  2625. if (caretLine == lineVisible) OnVScroll(SB_LINEUP, 0, NULL);
  2626. cp.x = caretX-xOffset;
  2627. cp.y = caretY - 1;
  2628. FindMouseChar(cp);
  2629. caretVisible = TRUE;
  2630. cwin_ensure_screen(FALSE);
  2631. return;
  2632. case VK_DOWN:
  2633. // If my caret is on the last line I will do something DOSKEY-ish...
  2634. if (caretLine == lineLast)
  2635. { int n = currentInputLine + 1;
  2636. if (n == MAX_SAVED_LINES) n = 0;
  2637. if (currentInputLine == -1 || n == savedP2) return;
  2638. currentInputLine = n;
  2639. ReplaceLastLine(&savedChars[savedLines[currentInputLine]]);
  2640. return;
  2641. }
  2642. cp.x = caretX-xOffset;
  2643. saveY = caretY, saveX = caretX;
  2644. cp.y = caretY + LineDY(caretLine) + 1;
  2645. // The case when I am at the bottom of the screen and hit the "DOWN" key
  2646. // is a bit of a mess. I call FindMouse Char as usual to try to re-position
  2647. // the caret. If that would be outside the window I get a flag set to tell me
  2648. // so. In that case I re-position the caret where it just came from, then
  2649. // scroll the window, and finally repeat my attempt to re-position the
  2650. // caret.
  2651. FindMouseChar(cp);
  2652. if (mouseOutside & 8)
  2653. { cp.x = saveX-xOffset;
  2654. cp.y = saveY + 1;
  2655. FindMouseChar(cp);
  2656. OnVScroll(SB_LINEDOWN, 0, NULL);
  2657. cp.x = caretX-xOffset;
  2658. cp.y = caretY + LineDY(caretLine) + 1;
  2659. FindMouseChar(cp);
  2660. }
  2661. caretVisible = TRUE;
  2662. cwin_ensure_screen(FALSE);
  2663. return;
  2664. case VK_LEFT:
  2665. caretVisible = TRUE;
  2666. if (textFirst == caretChar) return; // at start of buffer?
  2667. if (lineBuffer[caretLine].address == caretChar) // back over a newline?
  2668. { int n = (caretLine-1)&LINE_MASK;
  2669. caretX = lineBuffer[n].width;
  2670. caretLine=n;
  2671. n = lineBuffer[n].address;
  2672. while (textBuffer[n]!='\n') n = (n+1)&TEXT_MASK;
  2673. caretChar = n;
  2674. }
  2675. else
  2676. { int n = (caretChar-1) & TEXT_MASK;
  2677. int w = windowFonts.HCourier.across['X'];
  2678. if ((textBuffer[n]&0xff)==CH_ENDPROMPT) // move over whole of a prompt
  2679. { do
  2680. { n = (n-1)&TEXT_MASK;
  2681. caretX -= w;
  2682. } while (n!=textFirst && (textBuffer[n]&0xff)!=CH_PROMPT);
  2683. caretChar = n;
  2684. if (lineBuffer[caretLine].address == caretChar) // back over a newline?
  2685. { n = (caretLine-1)&LINE_MASK;
  2686. caretX = lineBuffer[n].width;
  2687. caretLine=n;
  2688. n = lineBuffer[n].address;
  2689. while (textBuffer[n]!='\n') n = (n+1)&TEXT_MASK;
  2690. caretChar = n;
  2691. }
  2692. }
  2693. else caretX -= w;
  2694. if (caretLine == lineLast) icaretChar = n, icaretLine = lineLast;
  2695. caretChar = n;
  2696. // The amount of invalidation done here runs across to the end of the
  2697. // last line, and that ought to get rid of any residues of the old
  2698. // caret. I use an APPROXIMATION to the new value of caretX, but hope that
  2699. // that gets corrected during the re-painting.
  2700. CRect cr(caretX-xOffset, LineY(caretLine),
  2701. clientWidth, LineY(caretLine)+LineDY(caretLine));
  2702. InvalidateRect(&cr); // Should I hide the caret here?
  2703. }
  2704. UpdateWindow();
  2705. cwin_ensure_screen(FALSE);
  2706. return;
  2707. case VK_RIGHT:
  2708. caretVisible = TRUE;
  2709. if (textLast == caretChar) return; // at end of buffer?
  2710. if (textBuffer[caretChar] == '\n') // at a newline?
  2711. { caretX = 0;
  2712. int w = windowFonts.HCourier.across['X'];
  2713. caretLine = (caretLine+1)&LINE_MASK;
  2714. caretChar = lineBuffer[caretLine].address;
  2715. if (caretChar!=textLast &&
  2716. (textBuffer[caretChar]&0xff)==CH_PROMPT) // move over whole of a prompt
  2717. { while (caretChar!=textLast &&
  2718. (textBuffer[caretChar]&0xff)!=CH_ENDPROMPT)
  2719. { caretChar = (caretChar+1)&TEXT_MASK;
  2720. caretX += w;
  2721. }
  2722. caretX -= w;
  2723. if (caretChar!=textLast) caretChar = (caretChar+1)&TEXT_MASK;
  2724. }
  2725. CPoint caretPos(caretX-xOffset, caretY);
  2726. SetCaretPos(caretPos);
  2727. if (caretLine == lineLast)
  2728. { icaretLine = caretLine;
  2729. icaretChar = caretChar;
  2730. icaretX = caretX;
  2731. }
  2732. }
  2733. else
  2734. { int n = caretChar;
  2735. int oldCaretX = caretX;
  2736. int w = windowFonts.HCourier.across['X'];
  2737. n = (n+1)&TEXT_MASK;
  2738. caretX += w;
  2739. if (caretLine == lineLast) icaretChar = n, icaretLine = caretLine;
  2740. caretChar = n;
  2741. CRect cr(oldCaretX-xOffset, LineY(caretLine),
  2742. caretX-xOffset+w, LineY(caretLine)+LineDY(caretLine));
  2743. InvalidateRect(&cr); // Should I hide the caret here?
  2744. }
  2745. UpdateWindow();
  2746. cwin_ensure_screen(FALSE);
  2747. return;
  2748. }
  2749. }
  2750. // The next function takes a mouse position and identifies the character in
  2751. // the text buffer that it identifies. It must be kept well in step with
  2752. // the corresponding code in PaintTextLine().
  2753. int CMainWindow::FindMouseChar(CPoint point)
  2754. {
  2755. // I may have captured the mouse, in which case the coordinates returned may
  2756. // be outside my client area. Clip them to it before doing anything else.
  2757. int x = point.x, y = point.y;
  2758. int nCount = 0;
  2759. mouseOutside = 0;
  2760. if (x<0) x=0, mouseOutside |= 1;
  2761. else if (x>clientWidth) x=clientWidth, mouseOutside |= 2;
  2762. if (y<0) y=0, x=0, mouseOutside |= 4;
  2763. else if (y>clientHeight) y=clientHeight, x=clientWidth, mouseOutside |= 8;
  2764. if (mouseOutside == 0) selectScrollSpeed = 0;
  2765. int yOffset = lineBuffer[lineVisible].position;
  2766. int line;
  2767. for (line=lineVisible;;line=(line+1)&LINE_MASK)
  2768. { int y1 = lineBuffer[line].position - yOffset;
  2769. int y2 = y1 + lineBuffer[line].height;
  2770. if (y>=y1 && y<=y2) break;
  2771. if (line==lineLast) break;
  2772. }
  2773. // Now I have identified the line that the mouse cursor is on.
  2774. int textChar = lineBuffer[line].address;
  2775. int activeFont = CH_COURIER;
  2776. unsigned char *activeWidths = windowFonts.HCourier.across;
  2777. int xc = -xOffset;
  2778. int inPrompt = -1, inPromptX;
  2779. for (;;textChar=(textChar+1)&TEXT_MASK)
  2780. { if (textChar==textLast) break;
  2781. int c = textBuffer[textChar];
  2782. int w = activeWidths[c & 0xff];
  2783. switch (c & 0xff)
  2784. {
  2785. case '\n':
  2786. break;
  2787. case CH_PROMPT:
  2788. inPrompt = textChar, inPromptX = xc;
  2789. continue;
  2790. case CH_ENDPROMPT:
  2791. inPrompt = -1;
  2792. continue;
  2793. case CH_RED:
  2794. // case CH_BLUE:
  2795. // case CH_BLACK:
  2796. case CH_GRAY:
  2797. continue;
  2798. case CH_COURIER:
  2799. activeFont = c;
  2800. activeWidths = windowFonts.HCourier.across;
  2801. continue;
  2802. case CH_ROMAN:
  2803. activeFont = c;
  2804. activeWidths = windowFonts.HRoman.across;
  2805. continue;
  2806. case CH_BOLD:
  2807. activeFont = c;
  2808. activeWidths = windowFonts.HBold.across;
  2809. continue;
  2810. case CH_ITALIC:
  2811. activeFont = c;
  2812. activeWidths = windowFonts.HItalic.across;
  2813. continue;
  2814. case CH_SYMBOL:
  2815. activeFont = c;
  2816. activeWidths = windowFonts.HSymbol.across;
  2817. continue;
  2818. case CH_Roman:
  2819. activeFont = c;
  2820. activeWidths = windowFonts.Hroman.across;
  2821. continue;
  2822. case CH_Bold:
  2823. activeFont = c;
  2824. activeWidths = windowFonts.Hbold.across;
  2825. continue;
  2826. case CH_Italic:
  2827. activeFont = c;
  2828. activeWidths = windowFonts.Hitalic.across;
  2829. continue;
  2830. case CH_Symbol:
  2831. activeFont = c;
  2832. activeWidths = windowFonts.Hsymbol.across;
  2833. continue;
  2834. default:
  2835. if (c == '\t')
  2836. { c = ' ';
  2837. w = currentWidths[' ']*(8-(nCount&7));
  2838. nCount = 7;
  2839. }
  2840. else if (c < 32) w = activeWidths['^'] + activeWidths[c & 0xff],
  2841. nCount++;
  2842. if (x <= xc+w/2)
  2843. {
  2844. // I will not allow the user to select a position within a prompt, since
  2845. // if I did it would make it possible to CUT or DELete part of the prompt
  2846. // string, including either the control character that marked its start or
  2847. // its end. The result would be that the status of the text as "prompt" would
  2848. // be lost with possible consequent confusion.
  2849. if (inPrompt>=0) textChar=inPrompt, xc=inPromptX;
  2850. break; // Bingo! Found it.
  2851. }
  2852. xc += w;
  2853. nCount++;
  2854. continue;
  2855. }
  2856. break;
  2857. }
  2858. // If the mouse click moves me to the last line then I place the
  2859. // input caret there. Otherwise if a previous caret had been on the last line
  2860. // I leave the input caret where the previous caret had been.
  2861. if (line == lineLast)
  2862. { icaretChar = textChar;
  2863. icaretX = xc+xOffset;
  2864. icaretFontWidths = activeWidths;
  2865. icaretLine = line;
  2866. }
  2867. else if (caretLine == lineLast)
  2868. { icaretChar = caretChar;
  2869. icaretX = caretX;
  2870. icaretFontWidths = caretFontWidths;
  2871. icaretLine = caretLine;
  2872. }
  2873. caretChar = textChar;
  2874. caretX = xc+xOffset;
  2875. caretFontWidths = activeWidths;
  2876. caretLine = line;
  2877. inject_randomness(caretX);
  2878. inject_randomness(caretLine);
  2879. // In the case that mouse activity re-positions the caret I will move the
  2880. // caret on the screen directly to where it needs to be shown. No benefit
  2881. // would come from delaying until a future re-paint operation.
  2882. HideCaret();
  2883. CPoint caretPos(caretX-xOffset, caretY);
  2884. SetCaretPos(caretPos);
  2885. ShowCaret();
  2886. return caretChar;
  2887. }
  2888. // StartSelection is slightly curious in that it sets the selection
  2889. // to be empty but rooted at a specific point. The effect will be that
  2890. // a subsequent MouseMove or shift-click can extend the selection to
  2891. // be non-trivial. The ugly feature of this is that a selection that has
  2892. // been "started" in this way could suffer if the contents of the text buffer
  2893. // moved. To prevent trouble I will keep a flag that indicates when I have
  2894. // a selection starting-point that I will count as valid. I will set this
  2895. // flag here and clear it anywhere where I move or destroy bits of the
  2896. // buffer.
  2897. void CMainWindow::StartSelection()
  2898. {
  2899. CancelSelection();
  2900. selFirstChar = selStartChar = selEndChar = caretChar;
  2901. selFirstX = selStartX = selEndX = caretX;
  2902. selFirstLine = selStartLine = selEndLine = caretLine;
  2903. selRootValid = TRUE;
  2904. }
  2905. void CMainWindow::InvalidateSelection(int l1, int x1, int l2, int x2)
  2906. {
  2907. int w;
  2908. if (l1 > l2)
  2909. { w=l1, l1=l2, l2=w, w=x1, x1=x2, x2=w;
  2910. if (x2 == 0) l2=(l2-1)&LINE_MASK, x2=clientWidth+xOffset;
  2911. }
  2912. HideCaret();
  2913. if (l1==l2)
  2914. { if (x1>x2) w=x1, x1=x2, x2=w;
  2915. CRect r(x1-xOffset, LineY(l1),
  2916. x2+caretWidth-xOffset, LineY(l1)+LineDY(l1));
  2917. if (x1 != x2) InvalidateRect(&r);
  2918. }
  2919. else
  2920. { CRect r(0, LineY(l1), clientWidth, LineY(l2)+LineDY(l2));
  2921. InvalidateRect(&r);
  2922. }
  2923. ShowCaret();
  2924. }
  2925. void CMainWindow::ExtendSelection()
  2926. {
  2927. int oldStart = selStartChar, oldEnd = selEndChar;
  2928. if (selFirstChar == selStartChar)
  2929. InvalidateSelection(selEndLine, selEndX, caretLine, caretX);
  2930. else InvalidateSelection(selStartLine, selStartX, caretLine, caretX);
  2931. if (betweenChar(textFirst, caretChar, selFirstChar))
  2932. { selStartChar = caretChar; selStartX = caretX; selStartLine = caretLine;
  2933. selEndChar = selFirstChar; selEndX = selFirstX; selEndLine = selFirstLine;
  2934. }
  2935. else
  2936. { selStartChar = selFirstChar; selStartX = selFirstX; selStartLine = selFirstLine;
  2937. selEndChar = caretChar; selEndX = caretX; selEndLine = caretLine;
  2938. }
  2939. if (selStartChar==oldStart && selEndChar==oldEnd) return;
  2940. }
  2941. void CMainWindow::CancelSelection()
  2942. {
  2943. InvalidateSelection(selStartLine, selStartX, selEndLine, selEndX);
  2944. selFirstChar = selStartChar = selEndChar = caretChar;
  2945. selFirstX = selStartX = selEndX = caretX;
  2946. selFirstLine = selStartLine = selEndLine = caretLine;
  2947. }
  2948. void CMainWindow::OnLButtonDblClk(UINT nFlags, CPoint point)
  2949. {
  2950. CSL_IGNORE(nFlags);
  2951. CSL_IGNORE(point);
  2952. }
  2953. void CMainWindow::OnLButtonDown(UINT nFlags, CPoint point)
  2954. {
  2955. /*
  2956. * The idea here is that a first click within a window should NOT move
  2957. * the caret or start a new selection, since it was probably (?) the
  2958. * mouse event that just caused the window to become active again.
  2959. */
  2960. CSL_IGNORE(nFlags);
  2961. if (pagePaused || hasFocus) return;
  2962. FindMouseChar(point);
  2963. trackingSelection = TRUE;
  2964. SetCapture();
  2965. selectScrollSpeed = 0;
  2966. if (nFlags & MK_SHIFT && selRootValid) ExtendSelection();
  2967. else StartSelection();
  2968. }
  2969. void CMainWindow::OnLButtonUp(UINT nFlags, CPoint point)
  2970. {
  2971. CSL_IGNORE(nFlags);
  2972. if (hasFocus)
  2973. { hasFocus = FALSE;
  2974. return;
  2975. }
  2976. if (pagePaused) pageLine = caretLine;
  2977. else
  2978. { FindMouseChar(point);
  2979. ::ReleaseCapture();
  2980. if (trackingSelection) ExtendSelection();
  2981. trackingSelection = FALSE;
  2982. selectScrollSpeed = 0;
  2983. }
  2984. }
  2985. void CMainWindow::OnMButtonDblClk(UINT nFlags, CPoint point)
  2986. {
  2987. CSL_IGNORE(nFlags);
  2988. CSL_IGNORE(point);
  2989. }
  2990. void CMainWindow::OnMButtonDown(UINT nFlags, CPoint point)
  2991. {
  2992. CSL_IGNORE(nFlags);
  2993. CSL_IGNORE(point);
  2994. }
  2995. void CMainWindow::OnMButtonUp(UINT nFlags, CPoint point)
  2996. {
  2997. CSL_IGNORE(nFlags);
  2998. CSL_IGNORE(point);
  2999. if (hasFocus)
  3000. { hasFocus = FALSE;
  3001. return;
  3002. }
  3003. pageLine = caretLine;
  3004. }
  3005. void CMainWindow::OnRButtonDblClk(UINT nFlags, CPoint point)
  3006. {
  3007. CSL_IGNORE(nFlags);
  3008. CSL_IGNORE(point);
  3009. }
  3010. void CMainWindow::OnRButtonDown(UINT nFlags, CPoint point)
  3011. {
  3012. CSL_IGNORE(nFlags);
  3013. CSL_IGNORE(point);
  3014. }
  3015. void CMainWindow::OnRButtonUp(UINT nFlags, CPoint point)
  3016. {
  3017. CSL_IGNORE(nFlags);
  3018. CSL_IGNORE(point);
  3019. if (hasFocus)
  3020. { hasFocus = FALSE;
  3021. return;
  3022. }
  3023. pageLine = caretLine;
  3024. }
  3025. void CMainWindow::OnMouseMove(UINT nFlags, CPoint point)
  3026. {
  3027. CSL_IGNORE(nFlags);
  3028. inject_randomness((int)clock());
  3029. if (trackingSelection && !pagePaused)
  3030. { FindMouseChar(point);
  3031. if (mouseOutside != 0)
  3032. { if (selectScrollSpeed == 0)
  3033. selectScrollSpeed = 7,
  3034. selectScrollCount = 1;
  3035. selectScrollDirection = mouseOutside;
  3036. selectScrollPoint = point;
  3037. OnTimer(1);
  3038. }
  3039. else ExtendSelection();
  3040. }
  3041. }
  3042. void CMainWindow::OnTimer(UINT timerId)
  3043. {
  3044. CSL_IGNORE(timerId);
  3045. if (selectScrollSpeed == 0) return;
  3046. selectScrollCount--;
  3047. if (selectScrollCount > 0) return;
  3048. if (selectScrollDirection == 0) return;
  3049. if (selectScrollDirection & 8) OnVScroll(SB_LINEDOWN, 0, NULL);
  3050. else if (selectScrollDirection & 4) OnVScroll(SB_LINEUP, 0, NULL);
  3051. else if (selectScrollDirection & 1 && xOffset!=0)
  3052. OnHScroll(SB_LINELEFT, 0, NULL);
  3053. else if (selectScrollDirection & 2 &&
  3054. caretX < lineBuffer[caretLine].width)
  3055. OnHScroll(SB_LINERIGHT, 0, NULL);
  3056. selectScrollCount = selectScrollSpeed;
  3057. if (selectScrollSpeed > 1) selectScrollSpeed--;
  3058. FindMouseChar(selectScrollPoint);
  3059. ExtendSelection();
  3060. }
  3061. void CMainWindow::OnNcLButtonDown(UINT nFlags, CPoint point)
  3062. {
  3063. CSL_IGNORE(nFlags);
  3064. CSL_IGNORE(point);
  3065. }
  3066. void CMainWindow::OnNcMButtonDown(UINT nFlags, CPoint point)
  3067. {
  3068. CSL_IGNORE(nFlags);
  3069. CSL_IGNORE(point);
  3070. }
  3071. void CMainWindow::OnNcRButtonDown(UINT nFlags, CPoint point)
  3072. {
  3073. CSL_IGNORE(nFlags);
  3074. CSL_IGNORE(point);
  3075. }
  3076. //
  3077. // Now the things provoked from the menus...
  3078. //
  3079. // SAVEAS will dump everything that is in the text buffer into a
  3080. // log file. The status of prompts and any other funny stuff will not
  3081. // concern me here - I will just dump out bytes as they appear in the
  3082. // text buffer. But at least I will arrange that I do not dump any characters
  3083. // that have a code >= 0x80 into the saved file.
  3084. void CMainWindow::OnSaveAs()
  3085. {
  3086. CFileDialog FDialog(FALSE, "LOG", "savefile.log",
  3087. OFN_HIDEREADONLY,
  3088. "Log Files (*.LOG)|*.LOG|All Files (*.*)|*.*||", NULL);
  3089. FDialog.m_ofn.lpstrTitle = "Save As";
  3090. if (FDialog.DoModal() != IDOK) return;
  3091. FILE *ofile = fopen(FDialog.GetPathName(), "w");
  3092. if (ofile == NULL)
  3093. { DisplayMsg("Could not write to file");
  3094. cwin_ensure_screen(FALSE);
  3095. return;
  3096. }
  3097. for (int i=lineFirst; i!=lineLast; i=(i+1)&LINE_MASK)
  3098. { int p = lineBuffer[i].address, c;
  3099. while ((c = textBuffer[p]) != '\n')
  3100. { if ((c & 0x80) == 0) putc(c, ofile);
  3101. p = (p+1) & TEXT_MASK;
  3102. }
  3103. putc('\n', ofile);
  3104. }
  3105. int p = lineBuffer[lineLast].address;
  3106. while (p != textLast)
  3107. { int c = textBuffer[p];
  3108. if ((c & 0x80) == 0) putc(c, ofile);
  3109. p = (p+1) & TEXT_MASK;
  3110. }
  3111. putc('\n', ofile); // extra newline at end to be tidy.
  3112. fclose(ofile);
  3113. cwin_ensure_screen(FALSE);
  3114. }
  3115. void CMainWindow::OnSaveSel()
  3116. {
  3117. CFileDialog FDialog(FALSE, "LOG", "savesel.log",
  3118. OFN_HIDEREADONLY,
  3119. "Log Files (*.LOG)|*.LOG|All Files (*.*)|*.*||", NULL);
  3120. FDialog.m_ofn.lpstrTitle = "Save Selection";
  3121. if (FDialog.DoModal() != IDOK) return;
  3122. FILE *ofile = fopen(FDialog.GetPathName(), "w");
  3123. if (ofile == NULL)
  3124. { DisplayMsg("Could not write to file");
  3125. cwin_ensure_screen(FALSE);
  3126. return;
  3127. }
  3128. // print selected region...
  3129. int l1 = selStartLine;
  3130. int cp = selStartChar;
  3131. for (;;)
  3132. { while (cp!=selEndChar && textBuffer[cp]!='\n')
  3133. { int c = textBuffer[cp];
  3134. if ((c & 0x80) == 0) putc(c, ofile);
  3135. cp = (cp+1)&TEXT_MASK;
  3136. }
  3137. if (cp==selEndChar) break;
  3138. putc('\n', ofile);
  3139. l1 = (l1+1)&LINE_MASK;
  3140. cp = lineBuffer[l1].address;
  3141. }
  3142. fclose(ofile);
  3143. cwin_ensure_screen(FALSE);
  3144. }
  3145. void CMainWindow::OnToFile()
  3146. {
  3147. // When I have a log file active I will make the menu entry into
  3148. // one that closes the transcript, while if I do not have a file in
  3149. // use the File/&T menu entry will start one off.
  3150. //
  3151. // NOTE that the "spool_file" mentioned here belongs somewhere in CSL,
  3152. // and is not in the "cwin" code in any more direct way.
  3153. if (spool_file != NULL)
  3154. { fprintf(spool_file, "\n+++ End of transcript +++\n");
  3155. fclose(spool_file);
  3156. spool_file = NULL;
  3157. GetMenu()->ModifyMenu(IDM_TOFILE+MF_STRING, MF_BYCOMMAND,
  3158. IDM_TOFILE, "&To File...");
  3159. DrawMenuBar();
  3160. cwin_ensure_screen(FALSE);
  3161. return;
  3162. }
  3163. CFileDialog FDialog(FALSE, "LOG", "logfile.log",
  3164. OFN_HIDEREADONLY,
  3165. "Log Files (*.LOG)|*.LOG|All Files (*.*)|*.*||", NULL);
  3166. FDialog.m_ofn.lpstrTitle = "Transcript File";
  3167. if (FDialog.DoModal() != IDOK) return;
  3168. FILE *ofile = fopen(FDialog.GetPathName(), "w");
  3169. if (ofile == NULL)
  3170. { DisplayMsg("Could not write to file");
  3171. cwin_ensure_screen(FALSE);
  3172. return;
  3173. }
  3174. spool_file = ofile;
  3175. time_t t0 = time(NULL);
  3176. fprintf(spool_file, "+++ Transcript started at %.24s +++\n", ctime(&t0));
  3177. GetMenu()->ModifyMenu(IDM_TOFILE+MF_STRING, MF_BYCOMMAND,
  3178. IDM_TOFILE, "&Terminate log");
  3179. DrawMenuBar();
  3180. cwin_ensure_screen(FALSE);
  3181. }
  3182. static CFont *MakeNewFont(CDC *dc, FontHeights *fh, DWORD charSet,
  3183. const char *fontName, int weight,
  3184. DWORD italic, int height)
  3185. {
  3186. CFont *newFont = new CFont;
  3187. newFont->CreateFont(
  3188. height, 0, // height, width
  3189. 0, 0, // angle of escapement, base line orientation angle
  3190. weight, italic, // weight, italic-flag
  3191. 0, 0, charSet, // underline, strike-out, character-set
  3192. OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
  3193. DEFAULT_QUALITY, DEFAULT_PITCH+FF_DONTCARE,
  3194. fontName);
  3195. dc->SelectObject(newFont);
  3196. TEXTMETRIC mm;
  3197. dc->GetTextMetrics(&mm);
  3198. fh->up = mm.tmExternalLeading + mm.tmAscent;
  3199. fh->down = mm.tmDescent;
  3200. fh->height = mm.tmHeight;
  3201. int widths[256], i;
  3202. dc->GetCharWidth(0, 255, widths);
  3203. for (i=0x80; i<0xa0; i++) widths[i] = 0; // my control characters
  3204. // The width of a character in any sensible sized font will be less than
  3205. // 256 pixels, so I save some space by stashing the information in a byte
  3206. // array rather than a word array. If I really knew I was only ever going to
  3207. // use TrueType fonts maybe I ought to use ABCWidths rather than just
  3208. // widths. At present I think that would count as overkill.
  3209. for (i=0; i<256; i++) fh->across[i] = (unsigned char)widths[i];
  3210. return newFont;
  3211. }
  3212. void CMainWindow::OnPrint()
  3213. {
  3214. CPrintDialog pd(FALSE);
  3215. if (pd.DoModal() != IDOK)
  3216. { cwin_ensure_screen(FALSE);
  3217. return;
  3218. }
  3219. CDC PrintDC;
  3220. if (PrintDC.CreateDC(pd.GetDriverName(), pd.GetDeviceName(),
  3221. pd.GetPortName(), NULL))
  3222. { int printWidth = PrintDC.GetDeviceCaps(HORZRES),
  3223. printHeight = PrintDC.GetDeviceCaps(VERTRES);
  3224. // Also get the size of the print area in mm.
  3225. int sizeX = PrintDC.GetDeviceCaps(HORZRES), // measured in pixels
  3226. sizeY = PrintDC.GetDeviceCaps(VERTRES);
  3227. int mmX = PrintDC.GetDeviceCaps(HORZSIZE), // measured in mm
  3228. mmY = PrintDC.GetDeviceCaps(VERTSIZE);
  3229. // I will try to allow a 10mm margin to right & left and a 15mm at top
  3230. //and bottom of the printed page.
  3231. int offsetX = (10*sizeX)/mmX;
  3232. int offsetY = (15*sizeY)/mmY;
  3233. sizeX -= 2*offsetX;
  3234. sizeY -= 2*offsetY;
  3235. // Create printer fonts, using the same type face as is present on the
  3236. // screen. Scale it so that around 80 columns will fit across the page.
  3237. // I scale for 81 not 80 to leave a margin for rounding etc etc.
  3238. FontHeights fh;
  3239. CFont *f = MakeNewFont(&PrintDC, &fh, ANSI_CHARSET,
  3240. (char *)windowFonts.baseFace,
  3241. windowFonts.boldFlag, 0, 100);
  3242. PrintDC.SelectStockObject(SYSTEM_FONT);
  3243. delete f;
  3244. int X = fh.across['X'];
  3245. int newH = (fh.height * sizeX)/(81 * X);
  3246. printerFonts.ChangeFont(&PrintDC, newH,
  3247. windowFonts.boldFlag, windowFonts.baseFace);
  3248. DOCINFO DocInfo;
  3249. DocInfo.cbSize = sizeof(DOCINFO);
  3250. DocInfo.lpszDocName = "Screen Dump";
  3251. DocInfo.lpszOutput = NULL;
  3252. if (PrintDC.StartDoc(&DocInfo) != -1)
  3253. {
  3254. PrintDC.StartPage();
  3255. PrintDC.SetTextAlign(TA_BASELINE);
  3256. PrintDC.SetTextColor(RGB(0,0,0)); currentColour = CH_BLACK;
  3257. PrintDC.SetBkColor(windowColour);
  3258. PrintDC.SelectObject(printerFonts.Courier);currentFont = CH_COURIER;
  3259. currentWidths = printerFonts.HCourier.across;
  3260. int y = offsetY, line;
  3261. for (line=lineFirst;; line=(line+1)&LINE_MASK)
  3262. { int up, down;
  3263. MeasureLine(lineBuffer[line].address,
  3264. &up, &down, &printerFonts);
  3265. int y1 = y+up;
  3266. int y2 = y+up+down;
  3267. if (y2 > sizeY)
  3268. { PrintDC.EndPage();
  3269. PrintDC.StartPage();
  3270. PrintDC.SetTextAlign(TA_BASELINE);
  3271. PrintDC.SetTextColor(RGB(0,0,0)); currentColour = CH_BLACK;
  3272. PrintDC.SetBkColor(windowColour);
  3273. PrintDC.SelectObject(printerFonts.Courier);currentFont = CH_COURIER;
  3274. currentWidths = printerFonts.HCourier.across;
  3275. y = offsetY;
  3276. y1 = y+up;
  3277. y2 = y+up+down;
  3278. }
  3279. PaintTextLine(&PrintDC, offsetX, y, y1, y2,
  3280. lineBuffer[line].address,
  3281. sizeX, &printerFonts, 1);
  3282. y = y2;
  3283. if (line==lineLast) break;
  3284. }
  3285. PrintDC.SelectStockObject(BLACK_PEN);
  3286. PrintDC.EndPage();
  3287. PrintDC.EndDoc();
  3288. }
  3289. printerFonts.DeleteFonts();
  3290. }
  3291. GlobalFree(pd.m_pd.hDevMode);
  3292. GlobalFree(pd.m_pd.hDevNames);
  3293. cwin_ensure_screen(FALSE);
  3294. }
  3295. void CMainWindow::OnPrintSel()
  3296. {
  3297. if (selStartChar == selEndChar) return; // nothing selected!
  3298. CPrintDialog pd(FALSE);
  3299. if (pd.DoModal() != IDOK)
  3300. { cwin_ensure_screen(FALSE);
  3301. return;
  3302. }
  3303. CDC PrintDC;
  3304. if (PrintDC.CreateDC(pd.GetDriverName(), pd.GetDeviceName(),
  3305. pd.GetPortName(), NULL))
  3306. { int printWidth = PrintDC.GetDeviceCaps(HORZRES),
  3307. printHeight = PrintDC.GetDeviceCaps(VERTRES);
  3308. // Also get the size of the print area in mm.
  3309. int sizeX = PrintDC.GetDeviceCaps(HORZRES), // measured in pixels
  3310. sizeY = PrintDC.GetDeviceCaps(VERTRES);
  3311. int mmX = PrintDC.GetDeviceCaps(HORZSIZE), // measured in mm
  3312. mmY = PrintDC.GetDeviceCaps(VERTSIZE);
  3313. // I will try to allow a 10mm margin to right & left and a 15mm at top
  3314. //and bottom of the printed page.
  3315. int offsetX = (10*sizeX)/mmX;
  3316. int offsetY = (15*sizeY)/mmY;
  3317. sizeX -= 2*offsetX;
  3318. sizeY -= 2*offsetY;
  3319. // Create printer fonts, using the same type face as is present on the
  3320. // screen. Scale it so that around 80 columns will fit across the page.
  3321. // I scale for 81 not 80 to leave a margin for rounding etc etc.
  3322. FontHeights fh;
  3323. CFont *f = MakeNewFont(&PrintDC, &fh, ANSI_CHARSET,
  3324. (char *)windowFonts.baseFace,
  3325. windowFonts.boldFlag, 0, 100);
  3326. PrintDC.SelectStockObject(SYSTEM_FONT);
  3327. delete f;
  3328. int X = fh.across['X'];
  3329. int newH = (fh.height * sizeX)/(81 * X);
  3330. printerFonts.ChangeFont(&PrintDC, newH,
  3331. windowFonts.boldFlag, windowFonts.baseFace);
  3332. DOCINFO DocInfo;
  3333. DocInfo.cbSize = sizeof(DOCINFO);
  3334. DocInfo.lpszDocName = "Selection Dump";
  3335. DocInfo.lpszOutput = NULL;
  3336. if (PrintDC.StartDoc(&DocInfo) != -1)
  3337. {
  3338. PrintDC.StartPage();
  3339. PrintDC.SetTextAlign(TA_BASELINE);
  3340. PrintDC.SetTextColor(RGB(0,0,0)); currentColour = CH_BLACK;
  3341. PrintDC.SetBkColor(windowColour);
  3342. PrintDC.SelectObject(printerFonts.Courier);currentFont = CH_COURIER;
  3343. currentWidths = printerFonts.HCourier.across;
  3344. int y = offsetY, line;
  3345. for (line=selStartLine;; line=(line+1)&LINE_MASK)
  3346. { int up, down;
  3347. MeasureLine(lineBuffer[line].address,
  3348. &up, &down, &printerFonts);
  3349. int y1 = y+up;
  3350. int y2 = y+up+down;
  3351. if (y2 > sizeY)
  3352. { PrintDC.EndPage();
  3353. PrintDC.StartPage();
  3354. PrintDC.SetTextAlign(TA_BASELINE);
  3355. PrintDC.SetTextColor(RGB(0,0,0)); currentColour = CH_BLACK;
  3356. PrintDC.SetBkColor(windowColour);
  3357. PrintDC.SelectObject(printerFonts.Courier);currentFont = CH_COURIER;
  3358. currentWidths = printerFonts.HCourier.across;
  3359. y = offsetY;
  3360. y1 = y+up;
  3361. y2 = y+up+down;
  3362. }
  3363. PaintTextLine(&PrintDC, offsetX, y, y1, y2,
  3364. lineBuffer[line].address,
  3365. sizeX, &printerFonts, 2);
  3366. y = y2;
  3367. if (line==selEndLine) break;
  3368. }
  3369. PrintDC.SelectStockObject(BLACK_PEN);
  3370. PrintDC.EndPage();
  3371. PrintDC.EndDoc();
  3372. }
  3373. printerFonts.DeleteFonts();
  3374. }
  3375. GlobalFree(pd.m_pd.hDevMode);
  3376. GlobalFree(pd.m_pd.hDevNames);
  3377. cwin_ensure_screen(FALSE);
  3378. }
  3379. void CMainWindow::OnCut()
  3380. {
  3381. OnCopy();
  3382. DeleteSelection();
  3383. cwin_ensure_screen(FALSE);
  3384. }
  3385. void CMainWindow::DeleteSelection()
  3386. {
  3387. int p = selStartChar, q = selEndChar;
  3388. if (betweenChar(p, caretChar, q))
  3389. { caretChar = p;
  3390. caretLine = selStartLine;
  3391. // In general after a delete operation caretX will not be valid, but the
  3392. // paint operation puts that right soon enough.
  3393. //-- caretX = selStartX;
  3394. }
  3395. if (betweenChar(p, icaretChar, q))
  3396. { icaretChar = p;
  3397. icaretLine = selStartLine;
  3398. // In general after a delete operation icaretX will not be valid, but the
  3399. // paint operation puts that right soon enough.
  3400. //-- icaretX = selStartX;
  3401. }
  3402. if (selStartLine==selEndLine)
  3403. { while (q!=textLast)
  3404. { int c = textBuffer[q];
  3405. textBuffer[p] = (char)c;
  3406. if (caretChar==q) caretChar = p;
  3407. if (icaretChar==q) icaretChar = p;
  3408. if (inputLineStart==q) inputLineStart=p;
  3409. if (c == '\n') break;
  3410. p = (p+1)&TEXT_MASK;
  3411. q = (q+1)&TEXT_MASK;
  3412. }
  3413. if (textLast==q) textLast = p;
  3414. if (inputLineStart==q) inputLineStart=p;
  3415. if (caretChar==q) caretChar=p;
  3416. if (icaretChar==q) icaretChar=p;
  3417. // The caret will have its position re-calculated during the re-draw, and also
  3418. // the line-length record will be adjusted then.
  3419. InvalidateSelection(selStartLine, selStartX,
  3420. selStartLine, clientWidth+xOffset);
  3421. }
  3422. else
  3423. { int l = selStartLine, l1 = selEndLine;
  3424. lineLast = l;
  3425. for (;;)
  3426. { while (q!=textLast)
  3427. { int c = textBuffer[q];
  3428. textBuffer[p] = (char)c;
  3429. if (caretChar==q) caretChar=q, caretLine = l;
  3430. if (inputLineStart==q) inputLineStart=p;
  3431. if (icaretChar==q) icaretChar=p, icaretLine = l;
  3432. p = (p+1)&TEXT_MASK;
  3433. q = (q+1)&TEXT_MASK;
  3434. if (c=='\n') break;
  3435. }
  3436. if (q==textLast) break;
  3437. l1 = (l1+1)&LINE_MASK;
  3438. q = lineBuffer[l1].address;
  3439. l = (l+1)&LINE_MASK;
  3440. lineBuffer[l].address = p;
  3441. lineLast = l;
  3442. }
  3443. if (inputLineStart==q) inputLineStart=p;
  3444. if (caretChar==q) caretChar=p;
  3445. if (icaretChar==q) caretChar=p;
  3446. textLast = p;
  3447. LineSizes();
  3448. // I invalidate down to the bottom of the screen. In some cases maybe I
  3449. // count do a ScrollWindow to good effect, and the plain invalidation
  3450. // here may cause the screen to flicker more than would be ideal.
  3451. CRect cr(0, LineY(selStartLine), clientWidth, clientHeight);
  3452. InvalidateRect(&cr);
  3453. }
  3454. selFirstChar = selEndChar = selStartChar;
  3455. selFirstX = selEndX = selStartX;
  3456. selFirstLine = selEndLine = selStartLine;
  3457. // The following sequence of operations is intended to scroll the window
  3458. // (if necessary) to make the caret visible, to get scroll bar thumbs
  3459. // up to date (eg after a multi-line delete) etc.
  3460. UpdateWindow();
  3461. OnSize(SIZE_RESTORED, clientWidth, clientHeight);
  3462. HideCaret();
  3463. CPoint caretPos(caretX-xOffset, caretY);
  3464. SetCaretPos(caretPos);
  3465. ShowCaret();
  3466. }
  3467. void CMainWindow::OnCopy()
  3468. {
  3469. // Here I have to allocate some global memory and copy text out of my buffer
  3470. // into it, making sure I insert CR/LF combinations between lines and a
  3471. // zero byte on the end as a terminator. If the text buffer contains funny
  3472. // characters that are used to indicate font changes etc I will just leave
  3473. // them in there for the moment.
  3474. if (!OpenClipboard()) return;
  3475. if (!::EmptyClipboard()) return; // Take control of clipboard
  3476. int l1 = selStartLine;
  3477. int cp = selStartChar, size=1;
  3478. for (;;)
  3479. { while (cp!=selEndChar && textBuffer[cp]!='\n')
  3480. { cp = (cp+1)&TEXT_MASK;
  3481. size++;
  3482. }
  3483. if (cp==selEndChar) break;
  3484. size=size+2;
  3485. l1 = (l1+1)&LINE_MASK;
  3486. cp = lineBuffer[l1].address;
  3487. }
  3488. HGLOBAL h = ::GlobalAlloc(GMEM_MOVEABLE+GMEM_DDESHARE, size);
  3489. HGLOBAL h1 = 0;
  3490. if (clipboardformat != 0)
  3491. h1 = ::GlobalAlloc(GMEM_MOVEABLE+GMEM_DDESHARE, size);
  3492. char *p = NULL, *p1 = NULL;
  3493. if (h != NULL) p = (char *)::GlobalLock(h);
  3494. if (h1 != NULL) p1 = (char *)::GlobalLock(h1);
  3495. if (p != NULL)
  3496. { l1 = selStartLine;
  3497. cp = selStartChar;
  3498. for (;;)
  3499. { while (cp!=selEndChar && textBuffer[cp]!='\n')
  3500. { int c = textBuffer[cp];
  3501. if ((c&0xff) != CH_PROMPT && (c&0xff) != CH_ENDPROMPT) *p++ = c;
  3502. if (p1 != NULL) *p1++ = c;
  3503. cp = (cp+1)&TEXT_MASK;
  3504. size++;
  3505. }
  3506. if (cp==selEndChar) break;
  3507. *p++ = '\r'; *p++ = '\n'; // CR/LF combination at end of line
  3508. if (p1 != NULL)
  3509. { *p1++ = '\r';
  3510. *p1++ = '\n';
  3511. }
  3512. l1 = (l1+1)&LINE_MASK;
  3513. cp = lineBuffer[l1].address;
  3514. }
  3515. *p = 0;
  3516. ::GlobalUnlock(h);
  3517. if (p1 != NULL)
  3518. { *p1 = 0;
  3519. ::GlobalUnlock(h1);
  3520. ::SetClipboardData(clipboardformat, h1);
  3521. }
  3522. ::SetClipboardData(CF_TEXT, h);
  3523. }
  3524. ::CloseClipboard();
  3525. cwin_ensure_screen(FALSE);
  3526. }
  3527. void CMainWindow::OnPaste()
  3528. {
  3529. HGLOBAL clipboardInputHandle;
  3530. char *w;
  3531. ToIcaret();
  3532. caretVisible = TRUE;
  3533. cwin_ensure_screen(FALSE); // may jump screen to caret location
  3534. pageLine = caretLine;
  3535. insertMode = TRUE;
  3536. if (!OpenClipboard()) return;
  3537. // If I get a new PASTE operation before an earlier one has quite finished
  3538. // I will just abandon the end of the old one without further fuss.
  3539. if (clipboardInput != NULL) free(clipboardInput);
  3540. // If I had managed to register my own format I will use data in that format
  3541. // in preference to text. Otherwise I will just grab text.
  3542. if (clipboardformat != 0)
  3543. clipboardInputHandle = ::GetClipboardData(clipboardformat);
  3544. else clipboardInputHandle = 0;
  3545. if (clipboardInputHandle == 0)
  3546. clipboardInputHandle = ::GetClipboardData(CF_TEXT);
  3547. clipboardInput = NULL;
  3548. if (clipboardInputHandle != 0)
  3549. { w = (char *)::GlobalLock(clipboardInputHandle);
  3550. if (w != NULL)
  3551. { int n = strlen(w); // amount of data found
  3552. clipboardInput = clipboardInputP = (char *)malloc(n+1);
  3553. if (clipboardInput != NULL) strcpy(clipboardInput, w);
  3554. ::GlobalUnlock(clipboardInputHandle);
  3555. }
  3556. }
  3557. ::CloseClipboard();
  3558. if (caretChar != textLast)
  3559. { char oneLine[128];
  3560. for (;;)
  3561. { int i=0, c;
  3562. while (i<120 && clipboardInput!=NULL && (c=*clipboardInputP++)!=0)
  3563. { if (c == '\r') continue;
  3564. oneLine[i++] = (char)c;
  3565. if (c == '\n') break; // Do pastes one line at a time
  3566. break; // do it one CHARACTER at a time...
  3567. }
  3568. InsertAtCaret(oneLine, i);
  3569. if (c == 0) break;
  3570. }
  3571. if (clipboardInput != NULL) free(clipboardInput);
  3572. clipboardInput = NULL;
  3573. }
  3574. cwin_ensure_screen(FALSE);
  3575. }
  3576. // The REINPUT request just combines a COPY and PASTE. It seems clumsy to
  3577. // have to do both as separate actions.
  3578. void CMainWindow::OnReInput()
  3579. {
  3580. OnCopy();
  3581. OnPaste();
  3582. }
  3583. void CMainWindow::OnSelectAll()
  3584. {
  3585. selFirstChar = selStartChar = textFirst;
  3586. selEndChar = textLast;
  3587. selFirstX = selStartX = 0;
  3588. selEndX = lineBuffer[lineLast].width;
  3589. selFirstLine = selStartLine = lineFirst;
  3590. selEndLine = lineLast;
  3591. HideCaret();
  3592. Invalidate();
  3593. ShowCaret();
  3594. }
  3595. void CMainWindow::OnClear()
  3596. {
  3597. CancelSelection();
  3598. caretChar = icaretChar = textFirst = textLast;
  3599. caretLine = icaretLine = lineFirst = lineLast;
  3600. caretX = icaretX = endX = 0;
  3601. xOffset = 0;
  3602. caretVisible = TRUE;
  3603. caretFontWidths = windowFonts.HCourier.across;
  3604. endFontWidths = windowFonts.HCourier.across;
  3605. if (inputLineStart>=0) inputLineStart = textLast;
  3606. pageLine = caretLine;
  3607. cwin_ensure_screen(FALSE);
  3608. }
  3609. // void CMainWindow::OnUndo()
  3610. // {
  3611. // DisplayMsg("OnUndo Text");
  3612. // }
  3613. void CMainWindow::OnRedraw()
  3614. {
  3615. HideCaret();
  3616. Invalidate();
  3617. ShowCaret();
  3618. }
  3619. // I make END reset the caret to the end of the text buffer, since that
  3620. // is an important state to be in, both with regard to input and to output.
  3621. // HOME just scrolls the window to make the top of the text visible, but does
  3622. // not re-position the caret. That is done because I do not generally expect
  3623. // people to want to insert characters at the very top of the buffer.
  3624. void CMainWindow::OnHome()
  3625. {
  3626. OnVScroll(SB_THUMBPOSITION, 0, NULL);
  3627. OnHScroll(SB_THUMBPOSITION, 0, NULL);
  3628. }
  3629. void CMainWindow::OnEnd()
  3630. {
  3631. icaretLine = caretLine = lineLast;
  3632. icaretChar = caretChar = textLast;
  3633. icaretX = caretX = endX;
  3634. caretFontWidths = endFontWidths;
  3635. selRootValid = TRUE;
  3636. OnVScroll(SB_FOR_CARET, 0, NULL);
  3637. OnHScroll(SB_FOR_CARET, 0, NULL);
  3638. caretVisible = TRUE;
  3639. cwin_ensure_screen(FALSE); // makes sure caret is properly on screen
  3640. }
  3641. void CMainWindow::ToIcaret()
  3642. {
  3643. caretLine = icaretLine;
  3644. caretChar = icaretChar;
  3645. caretX = icaretX;
  3646. caretFontWidths = icaretFontWidths;
  3647. OnVScroll(SB_FOR_CARET, 0, NULL);
  3648. OnHScroll(SB_FOR_CARET, 0, NULL);
  3649. caretVisible = TRUE;
  3650. cwin_ensure_screen(FALSE); // makes sure caret is properly on screen
  3651. }
  3652. //
  3653. // The font arrangements are as follows - at each stage I will have a
  3654. // master fixed-pitch font (typically "Courier New"). Whenever I change
  3655. // that font I locate a collection of slave fonts - Roman, Roman Italic
  3656. // and Symbol each in two sizes (one about the same as the master font and
  3657. // another about (10/12)^2 of that size, for use in sub- and super-scripts).
  3658. //
  3659. // When I change fonts I will only change the ones used for display in
  3660. // my window. The ones used when printing will be left as their original size.
  3661. // I only permit font selection to alter the font (name) for the simple
  3662. // fixed-pitch font that I use. But I still need to reconstruct printer
  3663. // fonts to allow for changes there.
  3664. void CMainWindow::OnResetFont()
  3665. {
  3666. HideCaret();
  3667. CClientDC dc(this);
  3668. // I need to find a suitable size for my default font, so I create one
  3669. // asking for "size=0" and see what windows hands back to me.
  3670. FontHeights fh;
  3671. CFont *f = MakeNewFont(&dc, &fh, ANSI_CHARSET, "Courier New",
  3672. FW_BOLD, 0, 0);
  3673. dc.SelectStockObject(SYSTEM_FONT);
  3674. delete f;
  3675. // If there is a font already set up somebody must discard it...
  3676. windowFonts.DeleteFonts();
  3677. windowFonts.ChangeFont(&dc, fh.height, FW_BOLD, "Courier New");
  3678. theApp.WriteProfileInt("MainWindow", "FontSize", fh.height);
  3679. theApp.WriteProfileString("MainWindow", "FontName", "Courier New");
  3680. theApp.WriteProfileInt("MainWindow", "FontWeight", 0);
  3681. // scroll activity may be needed here just as it is
  3682. // after some changes of window size.
  3683. OnSize(SIZE_RESTORED, clientWidth, clientHeight);
  3684. Invalidate(); // I think a total re-draw after a font change
  3685. // makes sense, and will not worry evan if the new
  3686. // font was identical to the old one, since after all
  3687. // the user prodded the OK not the CANCEL button.
  3688. ShowCaret();
  3689. }
  3690. void CMainWindow::OnFont()
  3691. {
  3692. HideCaret();
  3693. CClientDC dc(this);
  3694. LOGFONT logFont;
  3695. windowFonts.Courier->GetObject(sizeof(logFont), &logFont);
  3696. CFontDialog fontDialog(&logFont,
  3697. CF_SCREENFONTS | CF_FIXEDPITCHONLY |
  3698. CF_TTONLY |
  3699. CF_ANSIONLY | CF_INITTOLOGFONTSTRUCT);
  3700. // I will keep trying to obtain fonts from the user until I find one that
  3701. // can be properly installed. Maybe it is unkind, but if a font as selected
  3702. // from the dialog box fails to be created I just restart the dialog without
  3703. // further explanation.
  3704. for (;;)
  3705. { int w = fontDialog.DoModal();
  3706. if (w == IDCANCEL) break;
  3707. else if (w != IDOK) continue;
  3708. LOGFONT *lf = fontDialog.m_cf.lpLogFont;
  3709. // If there is a font already set up somebody must discard it...
  3710. windowFonts.DeleteFonts();
  3711. // Whatever the user does I do not want either very very small fonts or
  3712. // ones that would be so huge that their metrics could exceed 256. I apply
  3713. // rather arbitrary cut-offs here.
  3714. if (lf->lfHeight < -150) lf->lfHeight = -150;
  3715. if (lf->lfHeight <= -6);
  3716. else if (lf->lfHeight < 6) lf->lfHeight = -6;
  3717. else if (lf->lfHeight > 150) lf->lfHeight = -150;
  3718. windowFonts.ChangeFont(&dc, lf->lfHeight, lf->lfWeight, lf->lfFaceName);
  3719. theApp.WriteProfileInt("MainWindow", "FontSize", lf->lfHeight);
  3720. theApp.WriteProfileString("MainWindow", "FontName", lf->lfFaceName);
  3721. theApp.WriteProfileInt("MainWindow", "FontWeight",
  3722. lf->lfWeight==FW_BOLD ? 0 : 1);
  3723. break;
  3724. }
  3725. // scroll activity may be needed here just as it is
  3726. // after some changes of window size.
  3727. OnSize(SIZE_RESTORED, clientWidth, clientHeight);
  3728. Invalidate(); // I think a total re-draw after a font change
  3729. // makes sense, and will not worry evan if the new
  3730. // font was identical to the old one, since after all
  3731. // the user prodded the OK not the CANCEL button.
  3732. ShowCaret();
  3733. }
  3734. void FontArray::InitFont(CDC *dc, const char *name, int weight, int size)
  3735. {
  3736. // Create and install a suitable initial font - I use "Courier New"
  3737. // and ask for a default size the very first time, and this is
  3738. // achieved by passing down "Courier New" and "0".
  3739. // Because Courier tends to look rather light on
  3740. // the screen I will use a bold weight by default.
  3741. // I really hope that the default size will be such that 80 columns will fit
  3742. // neatly across the screen...
  3743. FontHeights fh;
  3744. weight = weight ? FW_NORMAL : FW_BOLD;
  3745. CFont *f = MakeNewFont(dc, &fh, ANSI_CHARSET, name,
  3746. weight, 0, size);
  3747. dc->SelectStockObject(SYSTEM_FONT);
  3748. delete f;
  3749. ChangeFont(dc, size == 0 ? fh.height : size, weight, name);
  3750. }
  3751. void FontArray::DeleteFonts()
  3752. {
  3753. delete Courier; Courier = NULL;
  3754. delete Roman; Roman = NULL;
  3755. delete Bold; Bold = NULL;
  3756. delete Italic; Italic = NULL;
  3757. delete Symbol; Symbol = NULL;
  3758. delete roman; roman = NULL;
  3759. delete bold; bold = NULL;
  3760. delete italic; italic = NULL;
  3761. delete symbol; symbol = NULL;
  3762. }
  3763. // The next function is a place-holder, I guess.
  3764. void CMainWindow::MeasureLine(int address, int *up, int *down, FontArray *fa)
  3765. {
  3766. CSL_IGNORE(address);
  3767. *up = fa->HCourier.up;
  3768. *down = fa->HCourier.down;
  3769. }
  3770. void FontArray::ChangeFont(CDC *dc, int size, int weight, const char *facename)
  3771. {
  3772. strcpy(baseFace, facename);
  3773. boldFlag = weight;
  3774. Courier = MakeNewFont(dc, &HCourier, ANSI_CHARSET,
  3775. facename, weight, 0, size);
  3776. // I choose the scale factor 1.2^2 between my main and my script font mainly
  3777. // because that is what TeX seems to have adopted - Windows will generally
  3778. // round the size I ask for somewhat.... I also reload the size of my
  3779. // basic fixed-pitch font in case an inconvenient value had been handed in
  3780. // to start with and creating the font led to rounding.
  3781. size = HCourier.height;
  3782. int smallSize = (100*size+72)/144;
  3783. // These fonts have my predefined font-names and weights. They are not in fact
  3784. // really (?) used at present.
  3785. Roman = MakeNewFont(dc, &HRoman, ANSI_CHARSET,
  3786. "Times New Roman", FW_NORMAL, 0, size);
  3787. Bold = MakeNewFont(dc, &HBold, ANSI_CHARSET,
  3788. "Times New Roman", FW_BOLD, 0, size);
  3789. Italic = MakeNewFont(dc, &HItalic, ANSI_CHARSET,
  3790. "Times New Roman", FW_NORMAL, 1, size);
  3791. Symbol = MakeNewFont(dc, &HSymbol, SYMBOL_CHARSET,
  3792. "Symbol", FW_NORMAL, 0, size);
  3793. roman = MakeNewFont(dc, &Hroman, ANSI_CHARSET,
  3794. "Times New Roman", FW_NORMAL, 0, smallSize);
  3795. bold = MakeNewFont(dc, &Hbold, ANSI_CHARSET,
  3796. "Times New Roman", FW_BOLD, 0, smallSize);
  3797. italic = MakeNewFont(dc, &Hitalic, ANSI_CHARSET,
  3798. "Times New Roman", FW_NORMAL, 1, smallSize);
  3799. symbol = MakeNewFont(dc, &Hsymbol, SYMBOL_CHARSET,
  3800. "Symbol", FW_NORMAL, 0, smallSize);
  3801. theApp.mainWindow->LineSizes();
  3802. ::DestroyCaret();
  3803. theApp.mainWindow->CreateSolidCaret(
  3804. 2*GetSystemMetrics(SM_CXBORDER), HCourier.height);
  3805. }
  3806. void CMainWindow::LineSizes()
  3807. {
  3808. int up, down, pos;
  3809. int lineFirst = theApp.mainWindow->lineFirst;
  3810. int lineLast = theApp.mainWindow->lineLast;
  3811. TextLine *lineBuffer = &theApp.mainWindow->lineBuffer[0];
  3812. MeasureLine(lineBuffer[lineFirst].address, &up, &down, &windowFonts);
  3813. lineBuffer[lineFirst].position = 0;
  3814. lineBuffer[lineFirst].up = up;
  3815. lineBuffer[lineFirst].height = up+down;
  3816. lineBuffer[lineFirst].width = 000;
  3817. pos = up + down;
  3818. for (int i=(lineFirst+1)&LINE_MASK;;i=(i+1)&LINE_MASK)
  3819. { MeasureLine(lineBuffer[i].address, &up, &down, &windowFonts);
  3820. lineBuffer[i].position = pos;
  3821. lineBuffer[i].up = up;
  3822. lineBuffer[i].height = up+down;
  3823. lineBuffer[i].width = 000;
  3824. pos += up + down;
  3825. if (i==lineLast) break;
  3826. }
  3827. }
  3828. // Now some support for a really crude and simple text-based help window.
  3829. // The interface I provide here is intended to be such that I can have
  3830. // alternative implementations (eg using Unix) that use a "curses" style
  3831. // screen manager.
  3832. // For Windows I will only support an 80 by 25 window. I guess it
  3833. // would be easy enough to permit other sizes, except that I do not have
  3834. // an easy answer to what should happen if the user re-sizes the window
  3835. // while other things are going on. Hence my conservative caution - at
  3836. // least for now!
  3837. extern "C" {
  3838. int LINES = 25, COLS = 80;
  3839. // initscr() must be called once at the start of a run
  3840. extern void initscr();
  3841. // initkb() and resetkb() delimit regions in the code where keyboard
  3842. // input is treated as requests to the curses window but is accepted
  3843. // with no delay and no echo. Also mouse events can be posted during
  3844. // this time.
  3845. extern void initkb();
  3846. extern void resetkb();
  3847. extern int mouse_button; // set non-zero when user presses a button
  3848. extern int mouse_cx; // 0 <= mouse_cx < COLS
  3849. extern int mouse_cy; // 0 <= mouse_cy < LINES
  3850. // refresh() is called to force the screen to be up to date
  3851. extern void refresh();
  3852. // endwin() hides the curses window, restoring simple text handling
  3853. extern void endwin();
  3854. // Move text insertion point. Origin (0,0) is top left of screen
  3855. extern void move(int y, int x);
  3856. // standout() and standend() delimit inverse video (or whatever) text
  3857. extern void standout();
  3858. extern void standend();
  3859. // erase() clears the whole screen
  3860. extern void erase();
  3861. //
  3862. // addch() and addstr() add text to the screen, advancing the cursor. I
  3863. // view it as illegal to write beyond either right or bottom margin of the
  3864. // screen.
  3865. //
  3866. extern void addch(int ch);
  3867. extern void addstr(char *s);
  3868. //
  3869. // getch() reads a character from the keyboard. It does not wait for
  3870. // a newline, and does not echo anything. Because the name getch() may be
  3871. // in use in some C libraries in a way that could conflict I use some
  3872. // re-naming here. If there has been a mouse-click recently then getch()
  3873. // should return a value (0x100 + bits) where the odd bits may indicate which
  3874. // button was pressed. In that case (mouse_cx,mouse_cy) will be the
  3875. // character-position coordinates at which the hit was taken. Systems
  3876. // that can not support a mouse do not have to worry about this and can always
  3877. // return a value in the range 0..255, or EOF. On some systems getch() will
  3878. // return 0 with no delay if there is no character available (so that
  3879. // the application will busy-wait). On others it is entitled to wait until
  3880. // the user presses a key. But (once again) it should not do line editing or
  3881. // wait for an ENTER.
  3882. //
  3883. extern int my_getch();
  3884. #undef getch
  3885. #define getch() my_getch()
  3886. }
  3887. static BOOL helpShown = FALSE;
  3888. static int helpChar = -1;
  3889. void initscr()
  3890. {
  3891. helpShown = FALSE;
  3892. helpChar = -1;
  3893. }
  3894. void initkb()
  3895. {
  3896. if (!helpShown)
  3897. { erase();
  3898. theApp.mainWindow->helpWindow->ShowWindow(SW_SHOW);
  3899. helpShown = TRUE;
  3900. }
  3901. helpChar = -1;
  3902. }
  3903. void resetkb()
  3904. {
  3905. if (helpShown)
  3906. { theApp.mainWindow->helpWindow->ShowWindow(SW_HIDE);
  3907. helpShown = FALSE;
  3908. }
  3909. }
  3910. void endwin()
  3911. {
  3912. resetkb();
  3913. }
  3914. static int helpY, helpX;
  3915. void move(int y, int x)
  3916. {
  3917. helpY = y;
  3918. helpX = x;
  3919. }
  3920. void erase()
  3921. {
  3922. helpX = helpY = 0;
  3923. for (int y=0; y<LINES; y++)
  3924. for (int x=0; x<COLS; x++)
  3925. theApp.mainWindow->helpWindow->contents[y][x] = ' ';
  3926. theApp.mainWindow->helpWindow->highline =
  3927. theApp.mainWindow->helpWindow->highstart =
  3928. theApp.mainWindow->helpWindow->highend = -1;
  3929. }
  3930. void standout()
  3931. {
  3932. theApp.mainWindow->helpWindow->highline = helpY;
  3933. theApp.mainWindow->helpWindow->highstart =
  3934. theApp.mainWindow->helpWindow->highend = helpX;
  3935. }
  3936. void standend()
  3937. {
  3938. if (theApp.mainWindow->helpWindow->highline != helpY)
  3939. { theApp.mainWindow->helpWindow->highline = helpY;
  3940. theApp.mainWindow->helpWindow->highstart = 0;
  3941. }
  3942. theApp.mainWindow->helpWindow->highend = helpX;
  3943. }
  3944. void addch(int ch)
  3945. {
  3946. if (helpX < COLS && helpY < LINES)
  3947. { theApp.mainWindow->helpWindow->contents[helpY][helpX] = (char)ch;
  3948. helpX++;
  3949. }
  3950. }
  3951. void addstr(char *s)
  3952. {
  3953. while (*s != 0) addch(*s++);
  3954. }
  3955. int mouse_button = 0, mouse_cx = 0, mouse_cy = 0;
  3956. void refresh()
  3957. {
  3958. if (!helpShown) initkb();
  3959. theApp.mainWindow->helpWindow->Invalidate();
  3960. }
  3961. int my_getch()
  3962. {
  3963. while (helpChar < 0) cwin_poll_window_manager(TRUE);
  3964. int ch = helpChar;
  3965. helpChar = -1;
  3966. return ch;
  3967. }
  3968. CHelpWindow::CHelpWindow()
  3969. {
  3970. char *menuName = "HelpMenu";
  3971. HINSTANCE hInst = AfxFindResourceHandle(menuName, RT_MENU);
  3972. HMENU hMenu = ::LoadMenu(hInst, menuName);
  3973. this->CreateEx(0, mainWindowClass, "Help Window",
  3974. WS_OVERLAPPED | WS_CAPTION,
  3975. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  3976. NULL, hMenu);
  3977. helpFont.CreateFont(
  3978. 0, 0, // height, width
  3979. 0, 0, // angle of escapement, base line orientation angle
  3980. FW_BOLD, 0, // weight, italic-flag
  3981. 0, 0, ANSI_CHARSET, // underline, strike-out, character-set
  3982. OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
  3983. DEFAULT_QUALITY, DEFAULT_PITCH+FF_DONTCARE,
  3984. "Courier New");
  3985. for (int y=0;y<25;y++)
  3986. for (int x=0;x<80;x++) contents[y][x] = ' ';
  3987. highline = 5;
  3988. highstart = 7;
  3989. highend = 11;
  3990. }
  3991. BEGIN_MESSAGE_MAP(CHelpWindow, CFrameWnd)
  3992. ON_WM_PAINT()
  3993. ON_WM_CHAR()
  3994. ON_WM_KEYDOWN()
  3995. ON_WM_LBUTTONDOWN()
  3996. ON_COMMAND(IDM_REDRAW, OnRedraw)
  3997. // ON_COMMAND(IDM_COPY, OnCopy)
  3998. ON_COMMAND(IDM_CLOSE, OnClose)
  3999. END_MESSAGE_MAP()
  4000. void CHelpWindow::OnPaint()
  4001. {
  4002. CPaintDC dc(this);
  4003. dc.SelectObject(&helpFont);
  4004. for (int i = 0; i<25; i++)
  4005. { if (i == highline)
  4006. { dc.TextOut(0, i*height, contents[i], highstart);
  4007. dc.SetTextColor(theApp.mainWindow->highlightTextColour);
  4008. dc.SetBkColor(theApp.mainWindow->highlightColour);
  4009. dc.TextOut(highstart*width, i*height, &contents[i][highstart],
  4010. highend-highstart);
  4011. dc.SetTextColor(theApp.mainWindow->textColour);
  4012. dc.SetBkColor(theApp.mainWindow->windowColour);
  4013. dc.TextOut(highend*width, i*height, &contents[i][highend],
  4014. 80-highend);
  4015. }
  4016. else dc.TextOut(0, i*height, contents[i], 80);
  4017. }
  4018. dc.SelectStockObject(SYSTEM_FONT);
  4019. }
  4020. void CHelpWindow::OnChar(UINT ch, UINT nRepCnt, UINT nFlags)
  4021. {
  4022. CSL_IGNORE(nRepCnt);
  4023. CSL_IGNORE(nFlags);
  4024. switch (ch)
  4025. {
  4026. //case ('O' & 0x1f):
  4027. // OnCopy();
  4028. // return;
  4029. case ('L' & 0x1f):
  4030. OnRedraw();
  4031. return;
  4032. default:
  4033. helpChar = ch;
  4034. return;
  4035. }
  4036. }
  4037. void CHelpWindow::OnKeyDown(UINT ch, UINT nRepCnt, UINT nFlags)
  4038. {
  4039. CSL_IGNORE(ch);
  4040. CSL_IGNORE(nRepCnt);
  4041. CSL_IGNORE(nFlags);
  4042. }
  4043. void CHelpWindow::OnLButtonDown(UINT nFlags, CPoint point)
  4044. {
  4045. CSL_IGNORE(nFlags);
  4046. CSL_IGNORE(point);
  4047. }
  4048. //void CHelpWindow::OnCopy()
  4049. //{
  4050. // HGLOBAL hpbmi = 0; // @@@@@@@@@
  4051. // if (this->OpenClipboard())
  4052. // { ::EmptyClipboard();
  4053. // ::SetClipboardData(CF_DIB, hpbmi);
  4054. // ::CloseClipboard();
  4055. // }
  4056. //}
  4057. void CHelpWindow::OnRedraw()
  4058. {
  4059. Invalidate();
  4060. }
  4061. void CHelpWindow::OnClose()
  4062. {
  4063. helpChar = 'q';
  4064. }
  4065. // end of c_text.cpp