123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326 |
- // c_text.cpp
- // Support for a window that can support regular, bold, italic and symbol
- // fonts each in large & small, plus the ability to select the size of
- // characters to be used.
- //
- //
- // See accompanying file "cwin.tex" for an explanation of the policy
- // for scrolling, text selection and treatment of buffer over-full
- // conditions.
- //
- // Copyright (C)/©/¸ Codemist Ltd, 1995-2001
- /*
- * This code may be used and modified, and redistributed in binary
- * or source form, subject to the "CCL Public License", which should
- * accompany it. This license is a variant on the BSD license, and thus
- * permits use of code derived from this in either open and commercial
- * projects: but it does require that updates to this code be made
- * available back to the originators of the package.
- * Before merging other code in with this or linking this code
- * with other packages or libraries please check that the license terms
- * of the other material are compatible with those of this.
- */
- /* Signature: 14bfdecf 10-Oct-2002 */
- #include "cwin.hpp"
- // There are times when this code wants to indicate to the user's
- // application that it should terminate. If such a request has been
- // raised the macro exception_pending() given here will evaluate to true,
- // but the way that this happens is specific the the CSL/CCL Lisp system
- // and thus is not exactly generic.
- extern "C" {
- extern int C_nil;
- extern FILE *spool_file;
- extern void inject_randomness(int);
- }
- #define exception_pending() ((C_nil&1) != 0)
- #define CSL_IGNORE(a) ((a) = (a))
- static BOOL hasFinished = FALSE;
- CMainWindow::CMainWindow()
- {
- hasFinished = FALSE;
- complete = FALSE;
- char *menuName = "MainMenu";
- HINSTANCE hInst = AfxFindResourceHandle(menuName, RT_MENU);
- HMENU hMenu = ::LoadMenu(hInst, menuName);
- this->CreateEx(WS_EX_ACCEPTFILES,
- mainWindowClass, "",
- WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- NULL, hMenu);
- // Now I will add the dynamic menu items that I need...
- // I demand that the "help" be the rightmost one on the menu bar
- HMENU helpMenu = ::GetSubMenu(hMenu, ::GetMenuItemCount(hMenu)-1);
- int i;
- theApp.dynamicCount = 0;
- // Insert extra help menu items here. I expect that the registry will
- // contain a subkey called HelpItems which is an integer, and then
- // a bunch of strings takked as T000/P000, T001/P001 etc where the Tiii are
- // strings to place on the menu and the Piii are paths to relevant help
- // files that can be opened.
- int helpCount = theApp.GetProfileInt("HelpItems", "HowMany", 0);
- #ifdef MAYBE
- int helpCount = theApp.GetProfileInt("HelpItems", "HowMany", -1);
- if (helpCount < 0)
- { theApp.dynamic[0] = "&Contents"
- theApp.dynamic_files[0] = "???";
- theApp.dynamic[1] = "&Search for Help"
- theApp.dynamic_files[1] = "???";
- helpCount = 2;
- WriteProfileInt("HelpItems", "HowMany", 2);
- WriteProfileString("HelpItems", "T000", theApp.dynamic[0]);
- WriteProfileString("HelpItems", "P000", theApp.dynamic_files[0]);
- WriteProfileString("HelpItems", "T001", theApp.dynamic[1]);
- WriteProfileString("HelpItems", "P001", theApp.dynamic_files[1]);
- }
- else
- #endif
- { if (helpCount >= IDM_LAST_DYNAMIC-IDM_DYNAMIC_ITEMS)
- helpCount = IDM_LAST_DYNAMIC-IDM_DYNAMIC_ITEMS-1;
- for (i=0; i<helpCount; i++)
- { char tag[8];
- sprintf(tag, "T%.3d", i);
- CString key(theApp.GetProfileString("HelpItems", tag, ""));
- sprintf(tag, "P%.3d", i);
- CString path(theApp.GetProfileString("HelpItems", tag, ""));
- if (key.GetLength() != 0 && path.GetLength() != 0)
- { char *m;
- m = (char *)malloc(1+key.GetLength());
- strcpy(m, LPCTSTR(key));
- theApp.dynamic[theApp.dynamicCount] = m;
- m = (char *)malloc(1+path.GetLength());
- strcpy(m, LPCTSTR(path));
- theApp.dynamic_files[theApp.dynamicCount++] = m;
- }
- }
- }
- // Now place them on the menu
- for (i=0; i<theApp.dynamicCount; i++)
- ::InsertMenu(helpMenu, IDM_HELP_ON_HELP, MF_BYCOMMAND,
- IDM_DYNAMIC_ITEMS+i, theApp.dynamic[i]);
- // typeAheadBuffer is a smallish buffer and when the user presses a key the
- // resulting character is put there. No echoing is performed. Use of DEL
- // can discard a previously-typed character and an attempt to over-fill the
- // buffer before other parts of the code have emptied it will cause a BEEP.
- //
- typeAheadP1 = typeAheadP2 = 0; // pointers into typeAheadBuffer
- // inputBuffer holds stuff that the user has accepted (by typing a newline)
- // but that the program has not yet read. inputP shows where the next
- // character should be read from it. The end of the buffer is marked with
- // a newline. If inputP<0 the buffer is empty.
- inputP = -1;
- // textBuffer is a large buffer just containing characters. textFirst
- // points to the first active character and textLast to one after the last
- // active character. Both pointers are kept masked with TEXT_MASK which
- // wraps them around to give a circular buffer.
- textFirst = textLast = 0; // main text buffer, now empty
- caretChar = 0; // char before which caret is shown
- caretLine = 0; caretX = 0;
- icaretChar = icaretLine = icaretX = 0;
- caretVisible = TRUE;
- caretFontWidths = windowFonts.HCourier.across;
- icaretFontWidths = windowFonts.HCourier.across;
- endFontWidths = windowFonts.HCourier.across;
- endX = 0;
- xOffset = 0; // Not horizontally scrolled.
- inputLineStart = -1;
- caretWidth = 2*GetSystemMetrics(SM_CXBORDER);
- clipboardInput = NULL;
- insertMode = TRUE;
- savedP1 = savedP2 = savedFirst = savedLast = 0;
- currentInputLine = -1;
- pageLine = 0;
- pageMode = pagePaused = FALSE;
- selFirstChar = selStartChar = selEndChar = 0;
- selFirstX = selStartX = selEndX = 0;
- selFirstLine = selStartLine = selEndLine = 0;
- trackingSelection = FALSE;
- selRootValid = FALSE;
- hasFocus = FALSE;
- // The characters in textBuffer are organised into lines. The start of each
- // line is recorded in lineBuffer, which is also a circular buffer so the
- // pointers into it are kept reduced using LINE_MASK. lineFirst identified
- // the first line stored. lineLast identifies the last line. Note that
- // the data making up a single line runs from where lineBuffer points either
- // to where a termination character is found in the text (this will be
- // a newline character) or until textLast. The latter case arises only for
- // a final non-terminated line of text. In general there will not be room
- // on the screen to display all the lines of text that are present.
- // lineVisible identifies the first line that can be displayed.
- //
- lineFirst = lineVisible = 0; // line buffer, contains one (empty)
- lineLast = 0; // line
- lineBuffer[0].position = 0;
- lineBuffer[0].up = 000;
- lineBuffer[0].height = 000; // get filled in when fonts are created
- lineBuffer[0].width = 000; // gets filled in when line painted.
- lineBuffer[0].address = textFirst;
- cLeft[0] = cRight[0] = 0;
- strncpy(cMid, programName, 31); cMid[31] = 0;
- GetSystemTime(&titleUpdateTime);
- inject_randomness((int)titleUpdateTime.wMilliseconds);
- //!!cwin_display_date();
- strcpy(cwin_prompt_string, "> "); // A default prompt string.
- windowColour = GetSysColor(COLOR_WINDOW);
- textColour = GetSysColor(COLOR_WINDOWTEXT);
- highlightColour = GetSysColor(COLOR_HIGHLIGHT);
- highlightTextColour = GetSysColor(COLOR_HIGHLIGHTTEXT);
- #ifdef GRAPHICS_WINDOW
- graphicsShown = FALSE;
- graphicsWindow = new CGraphicsWindow;
- graphicsWindow->ShowWindow(SW_HIDE);
- #endif
- helpShown = FALSE;
- helpWindow = new CHelpWindow;
- CClientDC dc(helpWindow);
- dc.SelectObject(&helpWindow->helpFont);
- TEXTMETRIC mm;
- dc.GetTextMetrics(&mm);
- helpWindow->height = mm.tmExternalLeading + mm.tmAscent + mm.tmDescent;
- int widths[4];
- dc.GetCharWidth('X', 'X', widths);
- helpWindow->width = widths[0];
- WINDOWPLACEMENT wp;
- helpWindow->GetWindowPlacement(&wp);
- int screenWidth = GetSystemMetrics(SM_CXSCREEN);
- RECT *wr = &wp.rcNormalPosition;
- int left = wr->left;
- int cwidth = 80*helpWindow->width +
- 3*GetSystemMetrics(SM_CXBORDER) +
- 2*GetSystemMetrics(SM_CXFRAME) + 5;
- // Try to get the whole window onto the screen.
- if (left + cwidth > screenWidth)
- { left = screenWidth - cwidth;
- if (left < 0) left = 0;
- }
- int screenHeight = GetSystemMetrics(SM_CYSCREEN);
- int top = wr->top;
- int cheight = 25*helpWindow->height +
- GetSystemMetrics(SM_CYCAPTION) +
- GetSystemMetrics(SM_CYMENU) +
- 3*GetSystemMetrics(SM_CYBORDER) +
- 2*GetSystemMetrics(SM_CYFRAME) + 5;
- if (top + cheight > screenHeight)
- { top = screenHeight - cheight;
- if (top < 0) top = 0;
- }
- helpWindow->SetWindowPos(NULL,
- top, left,
- cwidth, cheight,
- SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOZORDER);
- helpWindow->ShowWindow(SW_HIDE);
- //!!myTimer = SetTimer(1, 50, NULL); // 20 ticks per second -> message queue
- myTimer = SetTimer(1, 2000, NULL); // tick every other second -> message queue
- cwin_interrupt_pending = 0;
- complete = TRUE;
- }
- CMainWindow::~CMainWindow()
- {
- }
- void CMainWindow::OnDestroy()
- {
- #ifdef GRAPHICS_WINDOW
- graphicsWindow->viewpointWindow.DestroyWindow();
- graphicsWindow->DestroyWindow();
- // delete graphicsWindow;
- #endif
- if (myTimer!=0) KillTimer(myTimer);
- windowFonts.DeleteFonts();
- hasFinished = TRUE;
- }
- BEGIN_MESSAGE_MAP(CMainWindow, CFrameWnd)
- ON_WM_DESTROY()
- ON_WM_CHAR()
- ON_WM_HSCROLL()
- ON_WM_KEYDOWN()
- ON_WM_KILLFOCUS()
- ON_WM_LBUTTONDBLCLK()
- ON_WM_LBUTTONDOWN()
- ON_WM_LBUTTONUP()
- ON_WM_MBUTTONDBLCLK()
- ON_WM_MBUTTONDOWN()
- ON_WM_MBUTTONUP()
- ON_WM_MOUSEMOVE()
- // ON_WM_NCLBUTTONDOWN()
- // ON_WM_NCMBUTTONDOWN()
- // ON_WM_NCRBUTTONDOWN()
- ON_WM_PAINT()
- ON_WM_TIMER()
- ON_WM_RBUTTONDBLCLK()
- ON_WM_RBUTTONDOWN()
- ON_WM_RBUTTONUP()
- ON_WM_SETFOCUS()
- ON_WM_SIZE()
- ON_WM_MOVE()
- ON_WM_VSCROLL()
- // Now things on the menus...
- ON_COMMAND(IDM_READ, OnRead)
- ON_COMMAND(IDM_SAVEAS, OnSaveAs)
- ON_COMMAND(IDM_SAVESEL, OnSaveSel)
- ON_COMMAND(IDM_PRINT, OnPrint)
- ON_COMMAND(IDM_PRINTSEL, OnPrintSel)
- ON_COMMAND(IDM_TOFILE, OnToFile)
- ON_COMMAND(IDM_EXIT, OnExit)
- ON_COMMAND(IDM_CUT, OnCut)
- ON_COMMAND(IDM_COPY, OnCopy)
- ON_COMMAND(IDM_PASTE, OnPaste)
- ON_COMMAND(IDM_REINPUT, OnReInput)
- ON_COMMAND(IDM_SELECTALL, OnSelectAll)
- ON_COMMAND(IDM_CLEAR, OnClear)
- // ON_COMMAND(IDM_UNDO, OnUndo)
- ON_COMMAND(IDM_REDRAW, OnRedraw)
- ON_COMMAND(IDM_HOME, OnHome)
- ON_COMMAND(IDM_END, OnEnd)
- ON_COMMAND(IDM_FONT, OnFont)
- ON_COMMAND(IDM_RESET_FONT, OnResetFont)
- ON_COMMAND(IDM_RESET_WINDOW, OnResetWindow)
- ON_COMMAND(IDM_INTERRUPT, OnInterrupt)
- ON_COMMAND(IDM_BACKTRACE, OnBacktrace)
- ON_COMMAND(IDM_PAGEMODE, OnPageMode)
- #ifdef GRAPHICS_WINDOW
- ON_COMMAND(IDM_GRAPHICS, OnGraphics)
- ON_MESSAGE(WM_USER, OnGraphics1)
- #endif
- #ifndef COMMON
- ON_COMMAND_RANGE(IDM_FIRSTLOAD, IDM_FIRSTLOAD+199, OnLoadLibrary)
- ON_COMMAND_RANGE(IDS_FIRSTSWITCH, IDS_FIRSTSWITCH+199, OnSwitch)
- #endif
- END_MESSAGE_MAP()
- // Message handlers...
- #define caretY (lineBuffer[caretLine].position - \
- lineBuffer[lineVisible].position)
- #define caretDY (lineBuffer[caretLine].height)
- #define caretY1 (caretY+caretDY)
- void CMainWindow::OnPaint()
- {
- RECT update;
- inject_randomness((int)clock());
- if (!GetUpdateRect(&update, TRUE)) return;
- CPaintDC dc(this);
- dc.SetTextAlign(TA_BASELINE);
- dc.SetTextColor(RGB(0,0,0)); currentColour = CH_BLACK;
- dc.SetBkColor(windowColour);
- dc.SelectObject(windowFonts.Courier); currentFont = CH_COURIER;
- currentWidths = windowFonts.HCourier.across;
- int yBase = lineBuffer[lineVisible].position, line;
- HideCaret();
- for (line=lineVisible;; line=(line+1)&LINE_MASK)
- { int y = lineBuffer[line].position - yBase;
- int y1 = y+lineBuffer[line].up;
- int y2 = y+lineBuffer[line].height;
- if (y2 >= update.top)
- lineBuffer[line].width =
- PaintTextLine(&dc, -xOffset, y, y1, y2,
- lineBuffer[line].address,
- clientWidth, &windowFonts, 0);
- if (y > (int)update.bottom+lineBuffer[line].up) break;
- if (line==lineLast) break;
- }
- ShowCaret();
- dc.SelectStockObject(SYSTEM_FONT);
- // Now I will worry about positioning the horizontal scroll thumb.
- int longestLine = 0;
- for (line=lineVisible;; line=(line+1)&LINE_MASK)
- { int y = lineBuffer[line].position - yBase;
- if (y > clientHeight) break;
- int w = lineBuffer[line].width;
- if (w > longestLine) longestLine = w;
- if (line==lineLast) break;
- }
- if (longestLine <= clientWidth)
- { if (xOffset != 0)
- {
- // Mess here - my response is to force the X offset to zero
- // and scroll the window to match - that will generally force a fresh
- // painting operation. This sort of thing can happen when there was a
- // long line in the buffer and the window had been scrolled right to view it,
- // and then by a sequence of vertical scrolling operations that line is
- // no longer on the screen and all the lines that are visible are short enough
- // to be shown in their entirity. It can also happen when somebody
- // increases the size of the window.
- ScrollWindow(xOffset, 0, NULL, NULL);
- xOffset = 0;
- SetScrollPos(SB_HORZ, 0, TRUE);
- }
- }
- else
- { int HScrollPos = (100*xOffset)/(longestLine-clientWidth/2);
- // If (for instance) the font had just cahnged or the windows size had been
- // enlarged you might have too little left visible on the screen - in such
- // cases I will forcibly scroll to correct things.
- if (HScrollPos > 100)
- { ScrollWindow(xOffset-longestLine+clientWidth/2, 0, NULL, NULL);
- xOffset = longestLine-clientWidth/2;
- HScrollPos = 100;
- }
- SetScrollPos(SB_HORZ, HScrollPos, TRUE);
- }
- CPoint caretPos(caretX-xOffset, caretY);
- SetCaretPos(caretPos);
- }
- // a, b and c are pointers into the circular text buffer, with a<=c
- // (logically). Test is a<=b<c. The mess here is because of wrap-
- // around with TEXT_MASK.
- BOOL betweenChar(int a, int b, int c)
- {
- if (a<=c) return (a<=b && b<c);
- else return (a<=b || b<c);
- }
- // Ditto but relative to the line buffer. Observe that the code I have is
- // identical, but I still like to keep the abstractions separate.
- BOOL betweenLine(int a, int b, int c)
- {
- if (a<=c) return (a<=b && b<c);
- else return (a<=b || b<c);
- }
- int CMainWindow::PaintTextLine(CDC *dc, int x,
- int topY, int y, int bottomY, int textChar,
- int width, FontArray *ff, int context)
- {
- // context = 0 painting to screen
- // = 1 printing whole of buffer
- // = 2 just printing selected region
- int nCount = 0;
- int needFont = (currentFont != CH_COURIER);
- int needColour = (currentColour != CH_BLACK);
- // I will buffer up to 80 characters of fixed-pitch output and display them
- // using a single call to TextOut. This is intended to reduce the cost of
- // calling the operating system for output.
- char buff[80];
- int bufp = 0, bufx = 0, bufy = 0;
- for (;;textChar=(textChar+1)&TEXT_MASK)
- {
- // I reset the X coordinate relating to the root, start and end of
- // any selection. This is necessary (for instance) after a font change,
- // and also means I do not have to re-calculate the X offsets when I am
- // inserting characters into the middle of the buffer.
- if (context == 0)
- { if (textChar == selFirstChar) selFirstX = x+xOffset;
- if (textChar == selStartChar) selStartX = x+xOffset;
- if (textChar == selEndChar) selEndX = x+xOffset;
- // Just after I have deleted a character (or two) I can have repositioned
- // the character but I will have lost track of the font active at its new
- // position, and thus of the font metrics, and the X coordinate that the
- // caret will be at. So although when inserting characters into the buffer
- // I can keep track of font & position I reset them here so that a
- // Paint operation following a delection puts things back in a coherent
- // state.
- if (textChar==caretChar)
- { caretX = x+xOffset;
- caretFontWidths = currentWidths;
- }
- if (textChar==icaretChar)
- { icaretX = x+xOffset;
- icaretFontWidths = currentWidths;
- }
- // Ditto for the end of the text buffer.
- if (textChar==textLast)
- { endX = x+xOffset;
- endFontWidths = currentWidths;
- break;
- }
- }
- else if (textChar==textLast) break;
- // I will stop if I either find a newline character or I reach the end
- // of the buffer. In each case I will have sorted out how long the line
- // I was displaying was.
- int c = textBuffer[textChar];
- // I permit ESC characters in the buffer, but I will display them as $
- // following an ancient tradition. Other control characters are not really
- // permitted and could lead to mangled display.
- if (c == 0x1b) c = '$';
- int w = currentWidths[c & 0xff];
- BOOL inSelection = betweenChar(selStartChar, textChar, selEndChar);
- if (context==0 && inSelection &&
- (currentColour==CH_BLACK || currentColour==CH_HIGHLIGHT))
- { needColour = FALSE;
- currentColour = CH_HIGHLIGHT;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SetTextColor(highlightTextColour);
- dc->SetBkColor(highlightColour);
- }
- else if (!inSelection)
- {
- // If I am outside the selected region and context==2 (printing selection)
- // then I map characters onto blanks so thet they will not appear in the
- // output.
- if (context == 2) c = ' ';
- if (currentColour==CH_HIGHLIGHT)
- { needColour = FALSE;
- currentColour = CH_BLACK;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SetTextColor(RGB(0,0,0));
- dc->SetBkColor(windowColour);
- }
- }
- switch (c & 0xff)
- {
- case '\n':
- break;
- case CH_RED:
- needColour = FALSE;
- if (currentColour == c) continue;
- currentColour = c;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SetTextColor(RGB(255,0,0));
- dc->SetBkColor(windowColour);
- continue;
- case CH_BLUE:
- needColour = FALSE;
- if (currentColour == c) continue;
- currentColour = c;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SetTextColor(RGB(0,0,255));
- dc->SetBkColor(windowColour);
- continue;
- case CH_BLACK:
- if (inSelection && context==0)
- { c = CH_HIGHLIGHT;
- needColour = FALSE;
- if (currentColour == c) continue;
- currentColour = c;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SetTextColor(highlightTextColour);
- dc->SetBkColor(highlightColour);
- }
- else
- { needColour = FALSE;
- if (currentColour == c) continue;
- currentColour = c;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SetTextColor(RGB(0,0,0));
- dc->SetBkColor(windowColour);
- }
- continue;
- case CH_GRAY:
- needColour = FALSE;
- if (currentColour == c) continue;
- currentColour = c;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SetTextColor(RGB(128,128,128));
- dc->SetBkColor(windowColour);
- continue;
- case CH_COURIER:
- needFont = FALSE;
- if (currentFont == c) continue;
- currentFont = c;
- currentWidths = ff->HCourier.across;
- dc->SelectObject(ff->Courier);
- continue;
- case CH_ROMAN:
- needFont = FALSE;
- if (currentFont == c) continue;
- currentFont = c;
- currentWidths = ff->HRoman.across;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SelectObject(ff->Roman);
- continue;
- case CH_BOLD:
- needFont = FALSE;
- if (currentFont == c) continue;
- currentFont = c;
- currentWidths = ff->HBold.across;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SelectObject(ff->Bold);
- continue;
- case CH_ITALIC:
- needFont = FALSE;
- if (currentFont == c) continue;
- currentFont = c;
- currentWidths = ff->HItalic.across;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SelectObject(ff->Italic);
- continue;
- case CH_SYMBOL:
- needFont = FALSE;
- if (currentFont == c) continue;
- currentFont = c;
- currentWidths = ff->HSymbol.across;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SelectObject(ff->Symbol);
- continue;
- case CH_Roman:
- needFont = FALSE;
- if (currentFont == c) continue;
- currentFont = c;
- currentWidths = ff->Hroman.across;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SelectObject(ff->roman);
- continue;
- case CH_Bold:
- needFont = FALSE;
- if (currentFont == c) continue;
- currentFont = c;
- currentWidths = ff->Hbold.across;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SelectObject(ff->bold);
- continue;
- case CH_Italic:
- needFont = FALSE;
- if (currentFont == c) continue;
- currentFont = c;
- currentWidths = ff->Hitalic.across;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SelectObject(ff->italic);
- continue;
- case CH_Symbol:
- needFont = FALSE;
- if (currentFont == c) continue;
- currentFont = c;
- currentWidths = ff->Hsymbol.across;
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SelectObject(ff->symbol);
- continue;
- default:
- // The start of every line should be in regular-sized Courier and
- // in black. But rather than calling Windows to force that selection
- // on every line (I hypothesize that selecting a fond and a colour
- // into my device context may be costly) I only do so when I find I have
- // a character to display and then only in cases where what was left over
- // from whatever was last printed was somehow different.
- if (context==0 && needColour && inSelection)
- { if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SetTextColor(highlightTextColour);
- dc->SetBkColor(highlightColour);
- currentColour = CH_BLUE;
- needColour = FALSE;
- }
- else if (needColour)
- { if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SetTextColor(RGB(0,0,0));
- dc->SetBkColor(windowColour);
- currentColour = CH_BLACK;
- needColour = FALSE;
- }
- if (needFont)
- { dc->SelectObject(ff->Courier);
- currentFont = CH_COURIER;
- currentWidths = ff->HCourier.across;
- needFont = FALSE;
- }
- if (c == '\t')
- { c = ' ';
- w = currentWidths[c]*(8-(nCount&7));
- nCount = 7;
- }
- else if (c < 32)
- { w = currentWidths['^'];
- // There is a curiosity here, that for the moment I am going to leave alone.
- // If one has a control character in the buffer it is displayed as something
- // like ^I (with the '^' as a separate character and the 'I' a normal letter).
- // If this is within a selection the 'I' is shown in inverse video as usual
- // but the '^' is not. This could perhaps be seen as a way of making it
- // possible to distinguish between the control character and a regular pair
- // of characters spelt the same.
- if (x+w>=0 && x<width)
- { if (bufp>=sizeof(buff))
- dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- if (bufp == 0) bufx = x, bufy = y;
- buff[bufp++] = '^';
- }
- x += currentWidths['^'];
- c = c | 0x40;
- w = currentWidths[c & 0xff];
- nCount++;
- }
- if (inSelection && context==0)
- { CBrush b(highlightColour);
- CBrush *oldbrush = dc->SelectObject(&b);
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- dc->SelectStockObject(NULL_PEN);
- dc->Rectangle(x, topY, x+w+1, bottomY+1);
- dc->SelectObject(oldbrush);
- }
- if (x+w>=0 && x<width)
- { if (currentFont == CH_COURIER)
- { if (bufp>=sizeof(buff))
- dc->TextOut(bufx, bufy, buff, bufp), bufp = 0;
- if (bufp == 0) bufx = x, bufy = y;
- buff[bufp++] = (char)c;
- }
- else
- { if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp), bufp=0;
- buff[0] = (char)c;
- dc->TextOut(x, y, buff, 1);
- }
- }
- x += w;
- nCount++;
- continue;
- }
- break;
- }
- if (bufp != 0) dc->TextOut(bufx, bufy, buff, bufp);
- // I am NOT happy about the next few lines -- they fill an area in background
- // colour out across to the end of the line. I do this because otherwise
- // despite lots of calls to HideCaret I seem to observe remains of the caret
- // displayed after I have made a line shorter. I do not understand why this
- // happens but hope that what follows will work around the problem.
- if (context==0)
- { CBrush b(windowColour);
- CBrush *oldbrush = dc->SelectObject(&b);
- dc->SelectStockObject(NULL_PEN);
- dc->Rectangle(x, topY,
- width, bottomY+1);
- dc->SelectObject(oldbrush);
- }
- return x + xOffset;
- }
- int cwin_linelength = 80;
- void CMainWindow::OnSize(UINT nType, int cx, int cy)
- {
- WINDOWPLACEMENT wp;
- GetWindowPlacement(&wp);
- RECT *wr = &wp.rcNormalPosition;
- clientWidth = cx;
- clientHeight = cy;
- if (nType != SIZE_MINIMIZED)
- cwin_linelength = (clientWidth-5) / windowFonts.HCourier.across['X'];
- theApp.WriteProfileInt("MainWindow", "ScreenWidth", wr->right-wr->left);
- theApp.WriteProfileInt("MainWindow", "ScreenHeight", wr->bottom-wr->top);
- theApp.WriteProfileInt("MainWindow", "LineLength", cwin_linelength);
- if (caretVisible)
- { OnVScroll(SB_FOR_CARET, 0, NULL);
- OnHScroll(SB_FOR_CARET, 0, NULL);
- }
- OnHScroll(SB_REFRESH_THUMB, 0, NULL);
- OnVScroll(SB_REFRESH_THUMB, 0, NULL);
- }
- void CMainWindow::OnMove(int x, int y)
- {
- WINDOWPLACEMENT wp;
- CSL_IGNORE(x); CSL_IGNORE(y);
- GetWindowPlacement(&wp);
- RECT *wr = &wp.rcNormalPosition;
- theApp.WriteProfileInt("MainWindow", "ScreenLeft", wr->left);
- theApp.WriteProfileInt("MainWindow", "ScreenTop", wr->top);
- }
- void CMainWindow::OnResetWindow()
- {
- int screenWidth = GetSystemMetrics(SM_CXSCREEN);
- WINDOWPLACEMENT wp;
- GetWindowPlacement(&wp);
- RECT *wr = &wp.rcNormalPosition;
- int left = wr->left;
- int cwidth = 80*windowFonts.HCourier.across['X'] +
- 3*GetSystemMetrics(SM_CXBORDER) +
- 2*GetSystemMetrics(SM_CXFRAME) +
- GetSystemMetrics(SM_CXVSCROLL) + 5;
- // Try to get the whole window onto the screen.
- if (left + cwidth > screenWidth)
- { left = screenWidth - cwidth;
- if (left < 0) left = 0;
- }
- SetWindowPos(NULL,
- left, wr->top,
- cwidth, (wr->bottom - wr->top),
- SWP_SHOWWINDOW | SWP_NOZORDER);
- UpdateWindow();
- cwin_linelength = 80;
- theApp.WriteProfileInt("MainWindow", "ScreenLeft", left);
- theApp.WriteProfileInt("MainWindow", "ScreenWidth", cwidth);
- theApp.WriteProfileInt("MainWindow", "ScreenTop", wr->top);
- theApp.WriteProfileInt("MainWindow", "ScreenHeight", wr->bottom-wr->top);
- theApp.WriteProfileInt("MainWindow", "LineLength", cwin_linelength);
- }
- void CMainWindow::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pCntl)
- {
- // I will start by computing the total height of all the stuff I have in
- // my text buffer. At the very worst I only save a couple of thousand lines,
- // so adding up their heights again will not be TOO painful, I hope.
- int VScrollPos, movedUp=0, i, w;
- int aboveTop = -LineY(lineFirst);
- int totalHeight = aboveTop + LineY(lineLast) + LineDY(lineLast);
- CSL_IGNORE(pCntl);
- inject_randomness(totalHeight);
- // If I can make everything visible I will do that regardless of anything
- // the user tries to do.
- if (totalHeight <= clientHeight)
- { lineVisible = lineFirst;
- movedUp = aboveTop;
- VScrollPos = 0;
- }
- else
- { switch (nSBCode)
- {
- case SB_ENDSCROLL:
- return;
- case SB_REFRESH_THUMB:
- // In case the window has just been enlarged I check to see if I can scroll
- // up a bit until one more scroll up would make the last line of my text
- // drop off the (new) bottom of the window.
- while (lineVisible!=lineFirst)
- { int lv = (lineVisible-1)&LINE_MASK;
- int nAbove = lineBuffer[lv].position -
- lineBuffer[lineFirst].position;
- if (totalHeight > nAbove+clientHeight) break;
- lineVisible = lv;
- w = LineDY(lv);
- movedUp += w;
- aboveTop -= w;
- }
- break;
- case SB_THUMBPOSITION:
- case SB_THUMBTRACK:
- i = (nPos*(totalHeight-clientHeight))/100;
- if (i > aboveTop)
- { while (totalHeight > aboveTop+clientHeight &&
- i > aboveTop)
- { w = LineDY(lineVisible);
- aboveTop += w;
- movedUp -= w;
- lineVisible = (lineVisible+1) & LINE_MASK;
- }
- }
- else
- { i += LineDY(lineVisible);
- while (lineVisible != lineFirst &&
- i <= aboveTop)
- { lineVisible = (lineVisible-1) & LINE_MASK;
- w = LineDY(lineVisible);
- aboveTop -= w;
- movedUp += w;
- }
- }
- break;
- case SB_FOR_CARET:
- if (LineY(caretLine)+LineDY(caretLine) > clientHeight)
- { while (totalHeight > aboveTop+clientHeight &&
- LineY(caretLine)+LineDY(caretLine) > clientHeight)
- { w = LineDY(lineVisible);
- aboveTop += w;
- movedUp -= w;
- lineVisible = (lineVisible+1) & LINE_MASK;
- }
- }
- else
- { while (lineVisible != lineFirst &&
- LineY(caretLine) < 0)
- { lineVisible = (lineVisible-1) & LINE_MASK;
- w = LineDY(lineVisible);
- aboveTop -= w;
- movedUp += w;
- }
- }
- break;
- case SB_LINEDOWN:
- // If the bottom line of text is already visible I will not scroll down
- // any further.
- if (totalHeight > aboveTop+clientHeight)
- { lineVisible = (lineVisible+1) & LINE_MASK;
- w = lineBuffer[lineVisible].height;
- aboveTop += w;
- movedUp = -w;
- }
- break;
- case SB_LINEUP:
- if (lineVisible != lineFirst)
- { w = lineBuffer[lineVisible].height;
- aboveTop -= w;
- movedUp = w;
- lineVisible = (lineVisible-1) & LINE_MASK;
- }
- break;
- // When asked to scroll be a "page" I in fact jump by around 0.7 times
- // the number of lines visible on a page.
- case SB_PAGEDOWN:
- while (totalHeight > aboveTop+clientHeight &&
- -10*movedUp < 7*clientHeight)
- { lineVisible = (lineVisible+1) & LINE_MASK;
- w = lineBuffer[lineVisible].height;
- aboveTop += w;
- movedUp -= w;
- }
- break;
- case SB_PAGEUP:
- while (lineVisible != lineFirst &&
- 10*movedUp < 7*clientHeight)
- { w = lineBuffer[lineVisible].height;
- aboveTop -= w;
- movedUp += w;
- lineVisible = (lineVisible-1) & LINE_MASK;
- }
- break;
- }
- if (lineVisible == lineFirst) VScrollPos = 0;
- else if (totalHeight <= aboveTop+clientHeight) VScrollPos = 100;
- else VScrollPos = (100*aboveTop)/(totalHeight-clientHeight);
- }
- // If the user had just dragged the scroll bar I will tend to leave the
- // scroll bar thumb where the user put it. However I will make an exception
- // if the user's drag caused me to go to one or other extreme position, since
- // I think it would be confusing to have the window totally fully scrolled
- // and the thumb not extremal in its bar. But at all intermediate positions
- // it is kindest to let it settle where the user put it!
- if (nSBCode == SB_THUMBPOSITION ||
- nSBCode == SB_THUMBTRACK)
- { if (VScrollPos == 0 || VScrollPos == 100)
- nPos = VScrollPos;
- SetScrollPos(SB_VERT, nPos, TRUE);
- }
- else SetScrollPos(SB_VERT, VScrollPos, TRUE);
- if (movedUp != 0)
- { HideCaret();
- ScrollWindow(0, movedUp, NULL, NULL);
- ShowCaret();
- }
- // If the window is minimized then I will not change the status of apparent
- // visibility of the caret. This is important because if the caret becomes
- // marked as invisible because (for instance) the window is of zero size then
- // it will remain invisible and the window will not be scrolled ever, leaving
- // the text buffer to over-fill and hang things up.
- if (IsIconic()) return;
- else if (caretY >= 0 && caretY1 <= clientHeight &&
- caretX-xOffset >= 0 && caretX-xOffset < clientWidth)
- caretVisible = TRUE;
- else caretVisible = FALSE;
- }
- void CMainWindow::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pCntl)
- {
- // Each time I hit the horizontal scroll-bar I will check to find how
- // long the longest (partially) visible line is.
- int yBase = lineBuffer[lineVisible].position;
- int line, longestLine = 0;
- CSL_IGNORE(pCntl);
- for (line=lineVisible;; line=(line+1)&LINE_MASK)
- { int y = lineBuffer[line].position - yBase;
- if (y > clientHeight) break;
- int w = lineBuffer[line].width;
- if (w > longestLine) longestLine = w;
- if (line==lineLast) break;
- }
- // If everything fits I will just make sure I am scrolled fully left. Well as
- // a matter of caution I do not force things that way if the request was
- // to make the caret visible, since maybe if the screen is not quite up to
- // date it may seem that I could afford to scroll fully left but that would
- // in fact hide the caret.
- if (longestLine < clientWidth && nSBCode != SB_FOR_CARET)
- { if (xOffset != 0)
- { HideCaret();
- ScrollWindow(xOffset, 0, NULL, NULL);
- ShowCaret();
- }
- xOffset = 0;
- SetScrollPos(SB_HORZ, 0, TRUE);
- return;
- }
- int moveLeft = 0, w;
- switch (nSBCode)
- {
- case SB_ENDSCROLL:
- return;
- case SB_REFRESH_THUMB:
- break;
- case SB_THUMBPOSITION:
- case SB_THUMBTRACK:
- w = (nPos*(longestLine-clientWidth/2))/100;
- moveLeft = xOffset - w;
- xOffset = w;
- break;
- case SB_FOR_CARET:
- w = windowFonts.HCourier.across['X'];
- if (caretX-xOffset < 5*w)
- moveLeft = w*((clientWidth/2 - (caretX-xOffset))/w);
- else if (caretX-xOffset >= clientWidth)
- moveLeft = -w*((caretX - xOffset - clientWidth)/w + 4);
- xOffset -= moveLeft;
- break;
- // Small jumps are by the width of the letter X in the biggest fixed pitch
- // font that I have selected. Thus if you select a big font you will get
- // coarser contol over horizontal scrolling.
- case SB_LINERIGHT:
- w = windowFonts.HCourier.across['X'];
- xOffset += w;
- moveLeft = -w;
- break;
- case SB_LINELEFT:
- w = windowFonts.HCourier.across['X'];
- xOffset -= w;
- moveLeft = w;
- break;
- // Big jumps are by roughly (2/3) the width of the main Window
- case SB_PAGERIGHT:
- w = (2*clientWidth)/3;
- xOffset += w;
- moveLeft = -w;
- break;
- case SB_PAGELEFT:
- w = (2*clientWidth)/3;
- xOffset -= w;
- moveLeft = w;
- break;
- }
- if (xOffset < 0)
- { moveLeft += xOffset;
- xOffset = 0;
- }
- // I will permit scrolls to the right to a stage where the longest line
- // is (1/2) across the screen. But no further and especially I will not
- // permit scrolls right that cause the text on the screen to vanish
- // totally. The limit I use here is somewhat of an arbitrary choice. If a
- // "scroll page right" request looked as if it would move beyond this I will
- // clip the scroll.
- w = longestLine - clientWidth/2;
- if (w <= 0)
- { moveLeft += xOffset;
- xOffset = 0;
- w = 1;
- }
- else if (xOffset>w)
- { moveLeft += xOffset - w;
- xOffset = w;
- }
- int HScrollPos = (100*xOffset)/w;
- if (HScrollPos > 100) HScrollPos = 100;
- if ((nSBCode != SB_THUMBPOSITION &&
- nSBCode != SB_THUMBTRACK) ||
- HScrollPos == 0 ||
- HScrollPos == 100) SetScrollPos(SB_HORZ, HScrollPos, TRUE);
- else SetScrollPos(SB_HORZ, nPos, TRUE);
- if (moveLeft != 0)
- { HideCaret();
- ScrollWindow(moveLeft, 0, NULL, NULL);
- ShowCaret();
- }
- }
- void CMainWindow::OnSetFocus(CWnd *pOldWnd)
- {
- CSL_IGNORE(pOldWnd);
- CreateSolidCaret(caretWidth, windowFonts.HCourier.height);
- ShowCaret();
- SetWindowPos(&wndTop, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
- selectScrollSpeed = 0;
- hasFocus = TRUE; /* True just after focus has returned to this window */
- }
- void CMainWindow::OnKillFocus(CWnd *pNewWnd)
- {
- CSL_IGNORE(pNewWnd);
- ::DestroyCaret();
- hasFocus = FALSE;
- }
- // Sometime I find reading documentation quite hard. I had really thought that
- // when one selected an application (eg using ALT+TAB) that would give the
- // input focus to that application's main window. But it appears
- // (experimentally) that this is not always true, and I found myself with
- // a window with hard blue title bar but no input focus. It seems (?) that
- // the application has been "activated", so here I ensure that when that
- // happens it grabs the focus. Elsewhere you will find that whenever I see a
- // mouse button depressed I grab the focus too (just to be on the safe side).
- void CMainWindow::OnActivate(UINT state, CWnd *pWnd, BOOL minim)
- {
- CSL_IGNORE(state);
- CSL_IGNORE(pWnd);
- CSL_IGNORE(minim);
- if (state != WA_INACTIVE) SetFocus();
- }
- // When I put a character into the text buffer I need to worry about purging
- // old characters when the buffer wraps around. That may have an effect on
- // what is visible on the screen, or on a selected region. If I insert a
- // character and I am at the foot of a page I may need to scroll up. All
- // in all the funny cases are quite messy here!
- void CMainWindow::cwin_putchar(int ch)
- {
- if (hasFinished) return;
- BOOL caretAtEnd = (caretChar == textLast);
- if (ch == '\n')
- {
- // If the caret is at the end of the text and if "page mode" is enabled
- // then putting a newline into the text buffer will sometimes lead to
- // a pause....
- BOOL usualTitle = TRUE;
- // The test here says pause if
- // (a) I am in page mode
- // (b) lineVisible==pageLine. Well lineFirst is the line that is now in
- // danger of being about to scroll off the top of the screen, and so
- // pageLine will identify the current line to be "protected" from that
- // fate.
- // (c) caretLine vs clientHeight: This tries to test if scrolling would be
- // called for if the caret moved down a line (hence the 2*LineDY()).
- // In the pause case I just loop polling the window manager until something
- // magic happens. In general I can do the correct magic by making a suitable
- // selection of events do "pageLine = caretLine;"
- //
- // Actually it is nastier than that, because lineVisible may not be up to
- // date - so I have to work out what lineVisible WOULD be in the relevant
- // situation by having code rather line that in OnVScroll/SB_FOR_CARET.
- while (pageMode)
- { int lv = lineVisible, w;
- int aboveTop = -LineY(lineFirst);
- int totalHeight = aboveTop + LineY(lineLast) + LineDY(lineLast);
- if (totalHeight <= clientHeight) lv = lineFirst;
- else while (totalHeight > aboveTop+clientHeight &&
- LineY(caretLine)+LineDY(caretLine) > clientHeight)
- { w = LineDY(lv);
- aboveTop += w;
- lv = (lv+1) & LINE_MASK;
- }
- if (lv==pageLine &&
- (LineY(caretLine)+2*LineDY(caretLine) > clientHeight))
- { if (usualTitle)
- { usualTitle = FALSE;
- pagePaused = TRUE;
- // BeginWaitCursor();
- // For reasons that I do not fully understand a "wait" cursor established here
- // gets turned back into the usual arrow one before I want it to. I suspect
- // maybe that whenever the system re-draws the cursor it does so using the
- // version that is set as default for the application? Anyway the effect is
- // that I get an apparantly inconsistent behaviour, and at present I think
- // that keeping the cursor consistently an arrow is better than letting it
- // flash incoherently between that and an hourglass.
- SetWindowText("(page-mode) Type any key to continue");
- }
- // Here I will force the caret to be visible and poll the window manager
- if (!hasFinished)
- { OnVScroll(SB_FOR_CARET, 0, NULL);
- OnHScroll(SB_FOR_CARET, 0, NULL);
- cwin_poll_window_manager(FALSE);
- }
- if (hasFinished) return;
- continue;
- }
- else break;
- }
- if (!usualTitle)
- { SetWindowText(mainTitle);
- // EndWaitCursor();
- }
- pagePaused = FALSE;
- textBuffer[textLast] = '\n';
- int n = (textLast+1) & TEXT_MASK;
- if (n == textFirst) WrapTextBuffer(TRUE);
- textLast = n;
- // When I see a newline I may need to wrap the line buffer, discarding the
- // oldest saved line.
- int nextPos = lineBuffer[lineLast].position +
- lineBuffer[lineLast].height;
- n = (lineLast+1) & LINE_MASK;
- if (n == lineFirst) WrapTextBuffer(TRUE);
- lineLast = n;
- lineBuffer[lineLast].position = nextPos;
- lineBuffer[lineLast].up = windowFonts.HCourier.up;
- lineBuffer[lineLast].height = windowFonts.HCourier.up +
- windowFonts.HCourier.down;
- lineBuffer[lineLast].width = 0;
- lineBuffer[lineLast].address = textLast;
- endFontWidths = windowFonts.HCourier.across;
- endX = 0;
- if (caretAtEnd)
- { caretChar = icaretChar = textLast;
- caretLine = icaretLine = lineLast;
- caretX = icaretX = 0;
- caretFontWidths = icaretFontWidths = windowFonts.HCourier.across;
- }
- }
- else
- { textBuffer[textLast] = (char)ch;
- int n = (textLast+1) & TEXT_MASK;
- if (n == textFirst) WrapTextBuffer(TRUE);
- // I invalidate all the way to the right of the line just to be on the
- // safe side. This is probably excessive... but equally the painting in of
- // the solid blank to the right of any real text will not be too expensive.
- CRect cr(lineBuffer[lineLast].width-xOffset, LineY(lineLast),
- clientWidth, LineY(lineLast)+LineDY(lineLast));
- InvalidateRect(&cr); // Should I hide the caret here?
- textLast = n;
- endX += endFontWidths[ch];
- lineBuffer[lineLast].width += endFontWidths[ch];
- if (caretAtEnd)
- { caretChar = icaretChar = textLast;
- caretX += caretFontWidths[ch];
- icaretX = caretX;
- }
- }
- }
- // This code inserts characters wherever the caret happens to be, typically
- // in the middle of the text buffer. This returns TRUE if it succeeds. The
- // case when it might fail is if the text buffer is full and the caret is on
- // the first line. Then to make room for the next stuff the first line would
- // need to be discarded, but I can not do that without loss of the caret.
- //
- // UnTypeAhead() is used to push characters into the type-ahead buffer if
- // a newline is inserted into the current line of input (ie between
- // inputLineStart and textLast). It returns FALSE if pushing characters
- // flushed out previously typed stuff.
- BOOL CMainWindow::UnTypeAhead(int ch)
- {
- int w = (typeAheadP1-1)&typeAheadBufferMask;
- typeAheadBuffer[w] = (char)ch;
- typeAheadP1 = w;
- if (typeAheadP1 == typeAheadP2)
- { typeAheadP2 = (typeAheadP2-1)&typeAheadBufferMask;
- return FALSE; // Inserting pushes out something else
- }
- else return TRUE;
- }
- BOOL CMainWindow::InsertAtCaret(char *s, int n)
- {
- // I will do large inserts in 60-character chunks. That is so I can have
- // a limited size circular buffer (64 chars here) for use when copying
- // stuff down the buffer. Huge inserts would benefit from a larger chunk-size
- // here, at the cost of using more memory for the copy buffer.
- while (n > 60)
- { if (!InsertAtCaret(s, 60)) return FALSE;
- s += 60;
- n -= 60;
- }
- if (n == 0) return TRUE;
- int oldCaretLine = caretLine, oldCaretChar = caretChar;
- BOOL startEnd = (inputLineStart == textLast);
- int i, p = textLast;
- // Firstly I will make sure that there is room for the inserted text by
- // stepping on n characters from the current end of the text buffer and
- // wraping to free up the space I walk over. If this were to collide with
- // the caret position I would be in a MESS so in such cases I will not
- // perform the insert & will return a failure flag.
- for (i=0; i<n; i++)
- { p = (p+1)&TEXT_MASK;
- if (p == textFirst)
- { if (caretLine == lineFirst) return FALSE; // fail
- else WrapTextBuffer(FALSE);
- }
- }
- // Also I need to ensure that there is room for the number of new lines
- // that I will insert. It will be rare that this is a problem, I suspect.
- // Also beware here if the caret is positioned on the top line of the
- // buffer.
- int k = 0;
- for (i=0; i<n; i++) if (s[i] == '\n') k++;
- p = lineLast;
- for (i=0; i<k; i++)
- { p = (p+1)&LINE_MASK;
- if (p == lineFirst)
- { if (caretLine == lineFirst) return FALSE; // fail
- else WrapTextBuffer(FALSE);
- }
- }
- // Now I know that there is room for the insertion, I will "just" need to
- // copy characters up through the text buffer.
- char circle[64], circleFlags[64];
- memcpy(circle, s, n);
- memset(circleFlags, 0, n);
- int inP = n, outP = 0;
- // (line,p) is where I read characters from the buffer, while (line1,p1)
- // is where I put them back. Certainly at the start, and quite often all
- // the way through these values will remain in step. They may drift apart if
- // previous deletions within lines lave left gaps in the buffer between the
- // end of one line and the start of the next.
- int line = caretLine;
- p = caretChar;
- int line1 = line, p1 = p;
- while (p != textLast)
- { int c = textBuffer[p];
- circle[inP] = (char)c;
- circleFlags[inP] = 0;
- if (p == caretChar) circleFlags[inP] |= 1;
- // I will leave inputLineStart where it is if it lies just before the
- // caret position at the start of the insert operation.
- if (p == inputLineStart && p != oldCaretChar) circleFlags[inP] |= 2;
- if (p == icaretChar) circleFlags[inP] |= 4;
- if (c == '\n')
- { line = (line+1)&LINE_MASK;
- p = lineBuffer[line].address;
- }
- else p = (p+1)&TEXT_MASK;
- inP = (inP+1)&63;
- int c1 = circle[outP];
- textBuffer[p1] = (char)c1;
- if (circleFlags[outP] & 1) caretChar = p1, caretLine = line1;
- if (circleFlags[outP] & 2) inputLineStart = p1;
- if (circleFlags[outP] & 4) icaretChar = p1, icaretLine = line1;
- p1 = (p1+1)&TEXT_MASK;
- outP = (outP+1)&63;
- // Now if I had just inserted a newline I need to fix up the pointers
- // from the line buffer into the text buffer.
- if (c1 == '\n')
- { if (line == line1) // Here I need to shuffle lines up
- { int a = p1, line2=line;
- lineLast = (lineLast+1)&LINE_MASK;
- for (;;)
- { line2 = (line2+1)&LINE_MASK;
- int w = lineBuffer[line2].address;
- lineBuffer[line2].address = a;
- if (line2==lineLast) break;
- a = w;
- }
- line = (line+1)&LINE_MASK;
- }
- line1 = (line1+1)&LINE_MASK;
- lineBuffer[line1].address = p1;
- }
- }
- while (inP != outP)
- { int c1 = circle[outP];
- textBuffer[p1] = (char)c1;
- if (circleFlags[outP] & 1) caretChar = p1, caretLine = line1;
- if (circleFlags[outP] & 2) inputLineStart = p1;
- if (circleFlags[outP] & 4) icaretChar = p1, icaretLine = line1;
- if (c1 == '\n')
- { line1 = (line1+1)&LINE_MASK;
- lineBuffer[line1].address = p1;
- }
- p1 = (p1+1)&TEXT_MASK;
- outP = (outP+1)&63;
- }
- textLast = p1;
- lineLast = line1;
- // Here it may be that a newline had been inserted within the line that
- // was being prepared for input. If so, stuff after that newline must be
- // pushed back into the type-ahead buffer. When this happens it will always
- // be the case that the text in the buffer after inputLineStart will form
- // a compact block so I can just copy chars backwards
- BOOL success = TRUE;
- if (startEnd) inputLineStart = textLast;
- else if (inputLineStart>=0 && k!=0)
- { int lastNewline = inputLineStart;
- while (lastNewline!=textLast && textBuffer[lastNewline]!='\n')
- lastNewline = (lastNewline+1)&TEXT_MASK;
- while (textLast!=lastNewline)
- { int nn = (textLast-1)&TEXT_MASK;
- if (textLast==caretChar) caretChar = nn;
- if (textLast==icaretChar) icaretChar = nn;
- if (textLast==selStartChar ||
- textLast==selFirstChar ||
- textLast==selEndChar) CancelSelection();
- textLast = nn;
- int c = textBuffer[textLast];
- if (!UnTypeAhead(c)) success = FALSE;
- if (c=='\n') lineLast = (lineLast-1)&LINE_MASK;
- }
- }
- LineSizes();
- CRect cr(0, LineY(oldCaretLine), clientWidth, clientHeight);
- if (k==0) // No newlines inserted
- { cr.left = caretX;
- cr.bottom = LineY(caretLine)+LineDY(caretLine);
- }
- InvalidateRect(&cr);
- UpdateWindow();
- cwin_poll_window_manager(FALSE);
- return success;
- }
- void CMainWindow::cwin_caret_putchar(int ch)
- {
- if (hasFinished) return;
- // If the caret happens to be at the end maybe I ought not to have got here
- // anyway, but I will chain to the code that inserts things at the end of the
- // text.
- if (caretChar == textLast)
- { cwin_putchar(ch);
- return;
- }
- char buff[4];
- buff[0] = (char)ch;
- // I beep if the insertion was impossible.
- if (!InsertAtCaret(buff, 1)) ::MessageBeep(0xffffffff);
- }
- void CMainWindow::cwin_caret_replacechar(int ch)
- {
- if (hasFinished) return;
- // If the caret happens to be at the end maybe I ought not to have got here
- // anyway, but I will chain to the code that inserts things at the end of the
- // text.
- if (caretChar == textLast || ch == '\n' || textBuffer[caretChar] == '\n')
- { cwin_putchar(ch);
- return;
- }
- // Gosh this OUGHT to be easy!
- textBuffer[caretChar] = (char)ch;
- if (caretChar == icaretChar)
- { icaretX += caretFontWidths[ch];
- icaretChar++;
- }
- CRect cr(caretX, LineY(caretLine),
- clientWidth, LineY(caretLine)+LineDY(caretLine));
- InvalidateRect(&cr);
- caretX += caretFontWidths[ch];
- caretChar++;
- UpdateWindow();
- cwin_ensure_screen(FALSE);
- }
- // When I have put enough characters on the screen my text buffer may become
- // full. In which case I will discard the earliest stored line in it. Because
- // the line will always be stored with at least a newline character present
- // this guarantees to free up at least one byte in the buffer. I can also
- // call this routine to discard the top line of the buffer when I need to
- // recycle space in the record of lines (as distinct from characters).
- //
- // If I attempt to cancel part of the buffer by coming here but either the
- // top line of the buffer is visible on the screen or part of the top line
- // is included in a selection then I will pause until the user scrolls the
- // window or does something to the selection that will mean that throwing
- // away information will not discard anything still being looked at.
- //
- // Note that when I say here "Part of the first line is selected" I will
- // mean that a non-empty part of the line is selected. Not having a
- // selection in force is indicated by selStartChar==selEndChar, but the
- // selection pointers can otherwise point anywhere at all in the buffer
- // or indeed outside it.
- void CMainWindow::WrapTextBuffer(BOOL waitForSelection)
- {
- selRootValid = FALSE;
- BOOL usualTitle = TRUE;
- if (waitForSelection)
- { while ((lineVisible == lineFirst && caretChar != textLast &&
- LineY(lineLast)+LineDY(lineLast)>=clientHeight) ||
- (selStartLine == lineFirst &&
- selStartChar != selEndChar))
- { cwin_ensure_screen(FALSE);
- if (usualTitle)
- usualTitle = FALSE,
- SetWindowText(
- "Output pending: cancel selection/scroll down please");
- cwin_poll_window_manager(FALSE);
- }
- }
- if (hasFinished) return;
- if (!usualTitle) SetWindowText(mainTitle);
- if (lineFirst==lineLast) OnClear(); // Drastic!
- else
- { lineFirst = (lineFirst+1)&LINE_MASK;
- textFirst = lineBuffer[lineFirst].address;
- }
- }
- void cwin_putchar(int c)
- {
- theApp.mainWindow->cwin_putchar(c);
- inject_randomness(c);
- inject_randomness((int)clock());
- }
- void CMainWindow::cwin_puts(const char *s)
- {
- if (hasFinished) return;
- int ch;
- // Here I should be careful and do just ONE call to Invalidate for all the
- // changes that I make. But to start with it is MUCH easier to call putchar
- // lots of times, so that is what I will do.
- while ((ch=*s++)!=0) cwin_putchar(ch);
- }
- void cwin_puts(const char *s)
- {
- theApp.mainWindow->cwin_puts(s);
- }
- #ifndef MS_CDECL
- #ifdef _MSC_VER
- #define MS_CDECL __cdecl
- #else
- #define MS_CDECL
- #endif
- #endif
- void MS_CDECL CMainWindow::cwin_printf(const char *s, ...)
- {
- if (hasFinished) return;
- va_list a;
- va_start(a, s);
- cwin_vfprintf(s, a);
- va_end(a);
- }
- void MS_CDECL cwin_printf(const char *s, ...)
- {
- if (hasFinished) return;
- va_list a;
- va_start(a, s);
- theApp.mainWindow->cwin_vfprintf(s, a);
- va_end(a);
- }
- // The versions of printf() that work with the window system use an internal
- // buffer and print characters into that before sending them to the screen.
- // It appears to be hard to check for overflow of this buffer, so the user
- // will have to take care. A call to cwin_printf that generates more than
- // MAX_PRINTF_OUTPUT characters can crash the system. Sorry. Maybe I
- // should use the Watcom-specific _vbprintf() function here...
- #define MAX_PRINTF_OUTPUT 256
- void CMainWindow::cwin_vfprintf(const char *s, va_list a)
- {
- if (hasFinished) return;
- char temp[MAX_PRINTF_OUTPUT];
- #ifdef __WATCOMC__
- _vbprintf(temp, MAX_PRINTF_OUTPUT, s, a);
- // I put a zero byte at the end of the buffer to ensure that the string
- // left there is properly terminated even if _vbprintf() wrote proper
- // characters right up to the end.
- temp[MAX_PRINTF_OUTPUT-1] = 0;
- #else
- vsprintf(temp, s, a);
- #endif
- cwin_puts(temp);
- }
- void cwin_vfprintf(const char *s, va_list a)
- {
- theApp.mainWindow->cwin_vfprintf(s, a);
- }
- void CMainWindow::cwin_ensure_screen(BOOL poll)
- {
- if (hasFinished) return;
- // Here I need to do Invalidate operations, re-position the caret
- // and adjust the position of scroll-bar thumbs.
- if (caretVisible)
- { OnVScroll(SB_FOR_CARET, 0, NULL);
- OnHScroll(SB_FOR_CARET, 0, NULL);
- }
- // OnVScroll(SB_REFRESH_THUMB, 0, NULL);
- HideCaret();
- CPoint caretPos(caretX-xOffset, caretY);
- SetCaretPos(caretPos);
- ShowCaret();
- if (poll) cwin_poll_window_manager(FALSE);
- }
- void cwin_ensure_screen()
- {
- theApp.mainWindow->cwin_ensure_screen(TRUE);
- }
- // This will remove a character from the buffer at a location defined
- // by the caret, which will in general not be at the end of the text.
- void CMainWindow::cwin_caret_unputchar()
- {
- // When I try to remove a character from the screen I know that there is
- // no selection active, since a DELETE when there was a selection deleted it
- // as a block, and so did not reach this bit of code. So perhaps I can
- // make this code work by creating a selected region containing the
- // character (or prompt string) that I to be discarded and then do a
- // DeleteSelection.
- if (hasFinished) return;
- // If it happens that I am at the end of the text I will call the other
- // bit of character deletion code, so that in the bulk of this function I
- // can know that I am in the general case (which tends to be an expensive one)
- if (caretChar==textLast)
- { cwin_unputchar();
- return;
- }
- selRootValid = FALSE;
- // If the caret is right at the start of the buffer I do not have
- // anything to delete. This will, of course, include the case where the
- // buffer is totally empty.
- if (caretChar==textFirst) return;
- // Set up the start of the selection at the position of the caret.
- selFirstChar = selStartChar = selEndChar = caretChar;
- selFirstX = selStartX = selEndX = caretX;
- selFirstLine = selStartLine = selEndLine = caretLine;
- // Now I need to step back a "character", where that could be a newline
- // or a prompt string rather than any single simple character. I also
- // ought to worry here about deletion of items in the text buffer that
- // control formatting. All in all this is pretty dodgy, so I will cope with
- // just the simple cases to start with.
- int n;
- if (lineBuffer[caretLine].address == caretChar) // deleting a newline?
- { return;
- // For now I am going to prevent deletion of or through a prompt or newline
- n = (caretLine-1)&LINE_MASK;
- caretLine=n;
- caretX = lineBuffer[n].width;
- n = lineBuffer[n].address;
- while (textBuffer[n]!='\n') n = (n+1)&TEXT_MASK;
- }
- else
- { n = (caretChar-1) & TEXT_MASK;
- // I produce just an APPROXIMATION to the desired caretX here, but want that
- // so that I can limit the amount of re-painting that happens.
- int w = windowFonts.HCourier.across['X'];
- if ((textBuffer[n]&0xff)==CH_ENDPROMPT) // delete whole of a prompt
- {
- return; // do not delete prompt
- do
- { n = (n-1)&TEXT_MASK;
- caretX -= w;
- } while (n!=textFirst && (textBuffer[n]&0xff)!=CH_PROMPT);
- }
- else caretX -= w;
- }
- caretChar = n;
- if (caretLine == lineLast)
- { icaretChar = caretChar;
- icaretLine = caretLine;
- icaretX = caretX;
- }
- selStartChar = n;
- selStartLine = caretLine;
- selStartX = caretX;
- DeleteSelection();
- }
- // Now a version of unputchar that deletes characters from the end of the
- // text buffer (and the caret may or may not be there).
- void CMainWindow::cwin_unputchar()
- {
- if (hasFinished) return;
- selRootValid = FALSE;
- int n;
- if (textFirst == textLast) return; // utterly empty buffer
- if (lineBuffer[lineLast].address == textLast) // deleting a newline?
- { return;
- n = (lineLast-1)&LINE_MASK;
- endX = lineBuffer[n].width;
- if (caretLine==lineLast) caretLine=n, caretX=endX;
- if (icaretLine==lineLast) icaretLine=n, icaretX=endX;
- lineLast=n;
- n = lineBuffer[n].address;
- while (textBuffer[n]!='\n') n = (n+1)&TEXT_MASK;
- if (textLast == caretChar) caretChar = n;
- if (textLast == icaretChar) icaretChar = n;
- if (textLast == inputLineStart) inputLineStart = n;
- textLast = n;
- }
- else
- { n = (textLast-1) & TEXT_MASK;
- int w = windowFonts.HCourier.across['X'];
- if ((textBuffer[n]&0xff)==CH_ENDPROMPT) // delete whole of a prompt
- { return;
- do
- { n = (n-1)&TEXT_MASK;
- endX -= w;
- } while (n!=textFirst && (textBuffer[n]&0xff)!=CH_PROMPT);
- }
- else endX -= w;
- if (textLast == caretChar) caretChar = n;
- if (textLast == icaretChar) icaretChar = n;
- if (textLast == inputLineStart) inputLineStart = n;
- textLast = n;
- // The amount of invalidation done here runs across to the end of the
- // last line, and that ought to get rid of any residues of the old
- // caret. I use an APPROXIMATION to the new value of endX, but hope that
- // that gets corrected during the re-painting.
- CRect cr(endX-xOffset, LineY(lineLast),
- clientWidth, LineY(lineLast)+LineDY(lineLast));
- InvalidateRect(&cr); // Should I hide the caret here?
- }
- UpdateWindow();
- HideCaret();
- CPoint caretPos(caretX-xOffset, caretY);
- SetCaretPos(caretPos);
- ShowCaret();
- }
- int CMainWindow::cwin_getchar()
- {
- if (hasFinished) return EOF;
- int ch;
- if (inputP >= 0)
- { ch = inputBuffer[inputP++];
- if (ch == '\n') inputP = -1; // This buffer now all used up.
- return ch;
- }
- cwin_putchar(CH_PROMPT);
- cwin_puts(cwin_prompt_string);
- if (spool_file != NULL) fprintf(spool_file, "%s", cwin_prompt_string);
- cwin_putchar(CH_ENDPROMPT);
- if (hasFinished) return EOF;
- inputLineStart = textLast; // just beyond the prompt
- // Next I move characters across from the type-ahead buffer into the main
- // text buffer, echoing them as I go (and handling DELETE chars too). I
- // stop when I find a newline. Perhaps some people would like it if some
- // other characters would terminate an input line, but for the moment I am
- // sticking to just newline.... except that any keyboard or menu activity
- // that raises an "exception" will stop input for me rather abruptly.
- for (;;)
- { cwin_ensure_screen(FALSE);
- while (typeAheadP1 == typeAheadP2 &&
- clipboardInput == NULL)
- {
- // Note that in this case of poll_window_manager I am prepared
- // to delay until something happens.
- cwin_poll_window_manager(TRUE);
- if (hasFinished || exception_pending()) return EOF;
- }
- if (clipboardInput != NULL)
- { ch = *clipboardInputP++;
- // When input is coming from the clipboard to the very end of the input
- // text I will ignore copied prompt strings. Note that this is different
- // from the situation with PASTE inserts into the middle of the text.
- // A curious side-effect here is that if a regular TEXT clipboard file is
- // pasted in and it happens to contain these funny character codes...
- while ((ch&0xff) == CH_PROMPT)
- { while ((ch = *clipboardInputP++) != 0 &&
- (ch&0xff) != CH_ENDPROMPT);
- if (ch == 0) break;
- ch = *clipboardInputP++;
- }
- if (ch == '\r') continue;
- if (ch == 0)
- { free(clipboardInput);
- clipboardInput = NULL;
- continue;
- }
- }
- else
- { ch = typeAheadBuffer[typeAheadP1];
- typeAheadP1 = (typeAheadP1 + 1) & typeAheadBufferMask;
- }
- if (ch == 0x7f) cwin_unputchar();
- else cwin_putchar(ch);
- if (ch == '\n' || hasFinished) break;
- }
- if (hasFinished) return EOF;
- // By the time I exit the above loop there must be at least one character
- // in the line that has just been read, even if it is only the terminating
- // '\n'.
- inputP = 0;
- // When I copy stuff from the screen into the input buffer I discard any
- // prompts. They might be there if the line of input was created in part by
- // pasting material into the middle of a partly-typed line.
- BOOL inPrompt = FALSE;
- for (;;)
- { ch = textBuffer[inputLineStart];
- inputLineStart = (inputLineStart+1)&TEXT_MASK;
- // Here I quietly truncate very very long input lines. For my first attempt
- // I have inputBufferSize set at 2048.
- if ((ch&0xff) == CH_PROMPT) inPrompt = TRUE;
- if (inputP<inputBufferSize && !inPrompt) inputBuffer[inputP++] = (char)ch;
- if ((ch&0xff) == CH_ENDPROMPT) inPrompt = FALSE;
- else if (ch == '\n') break;
- }
- inputBuffer[inputBufferSize-1] = '\n';
- // Now I have the next line of user input in inputBuffer, running from 0
- // up to the first '\n' (and I know there is a '\n' in there somewhere).
- // I want to copy it away into a "saved input lines" buffer, so that they
- // can be retrieved later by a DOSKEY-like protocol. Note that provided
- // I keep the size of the save buffer larger than that of the input buffer
- // the copying operation here will not cause embarassing overflow!
- currentInputLine = -1;
- savedLines[savedP2++] = savedLast;
- if (savedP2 == MAX_SAVED_LINES) savedP2 = 0;
- if (savedP2 == savedP1)
- { savedP1++;
- if (savedP1 == MAX_SAVED_LINES) savedP1 = 0;
- savedFirst = savedLines[savedP1];
- }
- inputP = 0;
- for (;;)
- { ch = inputBuffer[inputP++];
- savedChars[savedLast++] = (char)ch;
- if (savedLast == MAX_SAVED_CHARS) savedLast = 0;
- if (savedLast == savedFirst)
- { savedP1++;
- if (savedP1 == MAX_SAVED_LINES) savedP1 = 0;
- savedFirst = savedLines[savedP1];
- }
- if (ch == '\n') break;
- }
- inputP = 0;
- ch = inputBuffer[inputP++];
- if (ch == '\n') inputP = -1; // This buffer now all used up.
- cwin_ensure_screen(FALSE);
- return ch;
- }
- int cwin_getchar()
- {
- return theApp.mainWindow->cwin_getchar();
- }
- int CMainWindow::cwin_getchar_nowait()
- {
- if (hasFinished) return EOF;
- int ch;
- if (inputP >= 0)
- { ch = inputBuffer[inputP++];
- if (ch == '\n') inputP = -1; // This buffer now all used up.
- return ch;
- }
- cwin_putchar(CH_PROMPT);
- cwin_puts(cwin_prompt_string);
- if (spool_file != NULL) fprintf(spool_file, "%s", cwin_prompt_string);
- cwin_putchar(CH_ENDPROMPT);
- if (hasFinished) return EOF;
- inputLineStart = textLast; // just beyond the prompt
- // Next I move characters across from the type-ahead buffer into the main
- // text buffer, echoing them as I go (and handling DELETE chars too). I
- // stop when I find a newline.
- for (;;)
- { cwin_ensure_screen(FALSE);
- if (typeAheadP1 == typeAheadP2 &&
- clipboardInput == NULL) return EOF;
- if (clipboardInput != NULL)
- { ch = *clipboardInputP++;
- while ((ch&0xff) == CH_PROMPT)
- { while ((ch = *clipboardInputP++) != 0 &&
- (ch&0xff) != CH_ENDPROMPT);
- if (ch == 0) break;
- ch = *clipboardInputP++;
- }
- if (ch == '\r') continue;
- if (ch == 0)
- { free(clipboardInput);
- clipboardInput = NULL;
- continue;
- }
- }
- else
- { ch = typeAheadBuffer[typeAheadP1];
- typeAheadP1 = (typeAheadP1 + 1) & typeAheadBufferMask;
- }
- if (ch == 0x7f) cwin_unputchar();
- else cwin_putchar(ch);
- if (ch == '\n' || hasFinished) break;
- }
- if (hasFinished) return EOF;
- // By the time I exit the above loop there must be at least one character
- // in the line that has just been read, even if it is only the terminating
- // '\n'.
- inputP = 0;
- BOOL inPrompt = FALSE;
- for (;;)
- { ch = textBuffer[inputLineStart];
- inputLineStart = (inputLineStart+1)&TEXT_MASK;
- // Here I quietly truncate very very long input lines. For my first attempt
- // I have inputBufferSize set at 2048.
- if ((ch&0xff) == CH_PROMPT) inPrompt = TRUE;
- if (inputP<inputBufferSize && !inPrompt) inputBuffer[inputP++] = (char)ch;
- if ((ch&0xff) == CH_ENDPROMPT) inPrompt = FALSE;
- else if (ch == '\n') break;
- }
- inputBuffer[inputBufferSize-1] = '\n';
- inputP = 0;
- ch = inputBuffer[inputP++];
- if (ch == '\n') inputP = -1; // This buffer now all used up.
- return ch;
- }
- int cwin_getchar_nowait()
- {
- return theApp.mainWindow->cwin_getchar_nowait();
- }
- void CMainWindow::cwin_discard_input()
- {
- typeAheadP1 = typeAheadP2 = 0;
- if (clipboardInput) free(clipboardInput);
- clipboardInput = 0;
- }
- void cwin_discard_input()
- {
- theApp.mainWindow->cwin_discard_input();
- }
- void CMainWindow::cwin_set_prompt(const char *s)
- {
- strncpy(cwin_prompt_string, s, 31);
- cwin_prompt_string[31] = 0;
- }
- void cwin_set_prompt(const char *s)
- {
- theApp.mainWindow->cwin_set_prompt(s);
- }
- // The READ command on the menu will work by forcing the caret to the
- // end of the input buffer and simulating some typed in text which
- // contains a file-name. I will make a start at pretending to make it
- // configurable here. But there seem to be LOTS of things that need to be
- // changed to match the system that is using the window manager code...
- // I hope that eventually I will provide configuration calls so that the
- // grim detail here gets set from "user" code. The basic idea is that
- // readFileCommand holds a string to be generated, with a marker (%) somewhere
- // where the file-name should go. I need options to control processing
- // of the file-name to get around reader escape conventions etc. I also need
- // to tell the file selection dialog what sorts of files to look for...
- #define VERBATIM_FILENAME 0
- #define DOUBLE_BACKSLASH 1
- #define DOUBLE_DOUBLEQUOTE 2
- #ifdef COMMON
- static char *readFileCommand = "(load \"%\")";
- static int fileOptions = DOUBLE_BACKSLASH;
- static char *defaultExtension = "lsp";
- static char *defaultFile = "*.lsp";
- static char *fileNameFilter =
- "Lisp Files (*.lsp)|*.lsp|All Files (*.*)|*.*||\0";
- #else
- static char *readFileCommand = "in \"%\";";
- static int fileOptions = DOUBLE_DOUBLEQUOTE;
- static char *defaultExtension = "red";
- static char *defaultFile = "*.RED;*.TST";
- static char *fileNameFilter =
- "Reduce (*.red/*.tst)|*.RED;*.TST|Lisp Files (*.lsp)|*.LSP|All Files (*.*)|*.*||\0";
- #endif
- void CMainWindow::OnRead()
- {
- OnEnd();
- pageLine = caretLine;
- CFileDialog FDialog(TRUE, defaultExtension,
- defaultFile, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY,
- fileNameFilter, NULL);
- FDialog.m_ofn.lpstrTitle = "Read File";
- if (FDialog.DoModal() != IDOK)
- { cwin_ensure_screen(FALSE); // needed to re-position caret, it seems.
- return;
- }
- CString p = FDialog.GetPathName();
- int nameLength = p.GetLength();
- char *q = readFileCommand;
- int i = strlen(q);
- while (i-- != 0)
- { int ch = q[i];
- if (ch == '%')
- { int j = nameLength;
- while (j-- != 0)
- { int ch1 = p[j];
- switch (fileOptions)
- {
- case DOUBLE_BACKSLASH:
- if (ch1 == '\\') UnTypeAhead(ch1);
- break;
- case DOUBLE_DOUBLEQUOTE:
- if (ch1 == '"') UnTypeAhead(ch1);
- break;
- default:break;
- }
- UnTypeAhead(ch1);
- }
- }
- else UnTypeAhead(ch);
- }
- cwin_ensure_screen(FALSE);
- }
- #ifndef COMMON
- static char **library_name_p = NULL;
- static int library_name_n = 0;
- static char *library_name[] =
- {
- "algint",
- "applysym",
- "arnum",
- "assist",
- "avector",
- "boolean",
- "cali",
- "camal",
- "changevr",
- "compact",
- "complex",
- "crack",
- "cvit",
- "decompos",
- "defint",
- "desir",
- "dfpart",
- "dummy",
- "elem",
- "excalc",
- "fide",
- "fps",
- "gentran",
- "gnuplot",
- "groebner",
- "ideals",
- "ineq",
- "invbase",
- "laplace",
- "lie",
- "linalg",
- "modsr",
- "ncpoly",
- "normform",
- "numeric",
- "odesolve",
- "orthovec",
- "physop",
- "pmrules",
- "randpoly",
- "reacteqn",
- "residue",
- "rlfi",
- "rsolve",
- "scope",
- "sets",
- "spde",
- "specfn",
- "symmetry",
- "taylor",
- "tps",
- "tri",
- "trigsimp",
- "wu",
- "xcolor",
- "xideal",
- "zeilberg",
- "ztrans"
- };
- void CMainWindow::OnLoadLibrary(UINT a)
- {
- OnEnd();
- pageLine = caretLine;
- char q[100];
- int i;
- if (library_name_p != NULL)
- { if (a-IDM_FIRSTLOAD >= library_name_n) return;
- sprintf(q, "load_package %s;\n",
- library_name_p[a - IDM_FIRSTLOAD]);
- }
- else
- { sprintf(q, "load_package %s;\n", library_name[a - IDM_FIRSTLOAD]);
- }
- i = strlen(q);
- while (i-- != 0)
- { int ch = q[i];
- UnTypeAhead(ch);
- }
- cwin_ensure_screen(FALSE);
- }
- static char **switch_name_p = NULL;
- static int switch_name_n = 0;
- static struct { char *name; int status; } switch_name[] =
- {
- {"algint", 0},
- {"adjprec", 0},
- {"allbranch", 0},
- {"allfac", 1},
- {"arbvars", 0},
- {"asterisk", 1},
- {"backtrace", 0},
- {"balanced_mod", 0},
- {"bfspace", 0},
- {"combineexpt", 0},
- {"combinelogs", 0},
- {"comp", 0},
- {"complex", 0},
- {"compxroots", 0},
- {"cramer", 0},
- {"cref", 0},
- {"defn", 0},
- {"demo", 0},
- {"dfprint", 0},
- {"div", 0},
- {"echo", 0},
- {"errcont", 0},
- {"evallhseqp", 0},
- {"exp", 1},
- {"expandexpt", 1},
- {"expandlogs", 0},
- {"ezgcd", 0},
- {"factor", 0},
- {"fastfor", 0},
- {"force", 0},
- {"fort", 0},
- {"fortupper", 0},
- {"fullprec", 0},
- {"fullprecision", 0},
- {"fullroots", 0},
- {"gc", 0},
- {"gcd", 0},
- {"heugcd", 0},
- {"horner", 0},
- {"ifactor", 0},
- {"int", 0},
- {"intstr", 0},
- {"lcm", 1},
- {"lessspace", 0},
- {"limitedfactors", 0},
- {"list", 0},
- {"listargs", 0},
- {"lower", 1},
- {"mcd", 1},
- {"modular", 0},
- {"msg", 1},
- {"multiplicities", 0},
- {"nat", 1},
- {"nero", 0},
- {"noarg", 1},
- {"noconvert", 0},
- {"nonlnr", 0},
- {"nosplit", 1},
- {"numval", 1},
- {"output", 1},
- {"period", 1},
- {"pgwd", 0},
- {"plap", 0},
- {"precise", 1},
- {"pret", 0},
- {"pri", 1},
- {"pwrds", 1},
- {"quotenewnam", 1},
- {"raise", 0},
- {"rat", 0},
- {"ratarg", 0},
- {"rational", 0},
- {"rationalize", 0},
- {"ratpri", 1},
- {"reduced", 0},
- {"revpri", 0},
- {"rlisp88", 0},
- {"rootmsg", 0},
- {"roundall", 1},
- {"roundbf", 0},
- {"rounded", 0},
- {"savestructr", 0},
- {"solvesingular", 0},
- {"time", 0},
- {"trallfac", 0},
- {"trfac", 0},
- {"trint", 0},
- {"trroot", 0}
- };
- void CMainWindow::OnSwitch(UINT a)
- {
- OnEnd();
- pageLine = caretLine;
- char q[100];
- int i;
- if (switch_name_p != NULL)
- { int n = a - IDS_FIRSTSWITCH;
- if (n >= switch_name_n) return;
- i = switch_name_p[n][0] == 'y';
- switch_name_p[n][0] ^= ('y' ^ 'n');
- // Note well - if the user types "on xxx" or "off xxx" directly rather than
- // by using this mechanism then the checked or otherwise status of the menu
- // items will get out of step. Tough luck. If you use the menu to set or clear
- // a switch the switch will certainly be brought into line with what the
- // menu information indicates.
- GetMenu()->CheckMenuItem(a, MF_BYCOMMAND |
- (i ? MF_UNCHECKED : MF_CHECKED));
- DrawMenuBar();
- sprintf(q, "%s %s;\n",
- i ? "off" : "on",
- 1+switch_name_p[n]);
- }
- else
- { i = switch_name[a - IDS_FIRSTSWITCH].status;
- switch_name[a - IDS_FIRSTSWITCH].status = !i;
- // Note well - if the user types "on xxx" or "off xxx" directly rather than
- // by using this mechanism then the checked or otherwise status of the menu
- // items will get out of step. Tough luck. If you use the menu to set or clear
- // a switch the switch will certainly be brought into line with what the
- // menu information indicates.
- GetMenu()->CheckMenuItem(a, MF_BYCOMMAND |
- (i ? MF_UNCHECKED : MF_CHECKED));
- DrawMenuBar();
- sprintf(q, "%s %s;\n",
- i ? "off" : "on",
- switch_name[a - IDS_FIRSTSWITCH].name);
- }
- i = strlen(q);
- while (i-- != 0)
- { int ch = q[i];
- UnTypeAhead(ch);
- }
- cwin_ensure_screen(FALSE);
- }
- #endif
- void CMainWindow::OnExit()
- {
- pageLine = caretLine;
- DestroyWindow();
- }
- int cwin_interrupt_pending = 0;
- void CMainWindow::OnInterrupt()
- {
- pageLine = caretLine;
- cwin_interrupt_pending = 1;
- cwin_ensure_screen(FALSE);
- }
- void CMainWindow::OnBacktrace()
- {
- pageLine = caretLine;
- cwin_interrupt_pending = 3;
- cwin_ensure_screen(FALSE);
- }
- void CMainWindow::OnPageMode()
- {
- pageLine = caretLine;
- pageMode = !pageMode;
- GetMenu()->CheckMenuItem(IDM_PAGEMODE,
- MF_BYCOMMAND | (pageMode ? MF_CHECKED : MF_UNCHECKED));
- DrawMenuBar();
- cwin_ensure_screen(FALSE);
- }
- #ifdef GRAPHICS_WINDOW
- void CMainWindow::OnGraphics()
- {
- graphicsWindow->ShowWindow(graphicsShown ? SW_HIDE : SW_SHOW);
- if (graphicsShown) graphicsWindow->viewpointWindow.ShowWindow(SW_HIDE);
- graphicsShown = !graphicsShown;
- if (graphicsShown) graphicsWindow->Invalidate();
- GetMenu()->CheckMenuItem(IDM_GRAPHICS,
- MF_BYCOMMAND | (graphicsShown ? MF_CHECKED : MF_UNCHECKED));
- DrawMenuBar();
- cwin_ensure_screen(FALSE);
- }
- void CMainWindow::OnGraphics1(UINT a, LONG b)
- {
- OnGraphics();
- }
- #endif
- void CMainWindow::ReWriteTitleText()
- {
- if (hasFinished) return;
- CWindowDC dc(this);
- // It is not (at present) clear to me how to decide how wide to make
- // the title. What follows is some sort of a guess.
- CRect clientArea;
- GetClientRect(&clientArea);
- int wTitle = clientArea.Width() -
- 3*::GetSystemMetrics(SM_CXVSCROLL);
- int wLeft = dc.GetTextExtent(cLeft, strlen(cLeft)).cx;
- int wMid = dc.GetTextExtent(cMid, strlen(cMid)).cx;
- int wRight = dc.GetTextExtent(cRight, strlen(cRight)).cx;
- // For the calculations about padding that I use here to work the font used
- // in the title bar had better not do any kerning and overhang-effects must
- // not interfere. I find I have all sorts of horrid problems if I try to
- // use regular blank characters for padding, but '\xa0' displays as blank
- // space and is better behaved. It also seems (???) that at least under
- // Windows 3.1 there is no great joy in trying to use caption strings that
- // are longer than 78 characters...
- #define PADDING_CHAR '\xa0'
- char strSp[4]; strSp[0] = PADDING_CHAR;
- int wSp = dc.GetTextExtent(strSp, 1).cx;
- int cw = wTitle / wSp;
- // I first measure things on the supposition that I would allow up to
- // 90 characters in the title. After balancing it a bit I cut that
- // down to the 78 that Windows 3.1 seems to be prepared to tolerate.
- if (cw > 90) wTitle = 90*wSp;
- int pad = (wTitle - wMid)/2;
- char *l = cLeft, *r = cRight;
- for (;;)
- { int padLeft = (pad - wLeft) / wSp;
- int padRight = (pad - wRight) / wSp;
- int excess = strlen(cLeft) + padLeft + strlen(cMid) +
- padRight + strlen(cRight) - 78;
- if (excess > 0)
- { if (excess & 1)
- { if (padLeft > padRight) padLeft--; else padRight--;
- excess--;
- }
- excess /= 2;
- padLeft -= excess;
- padRight -= excess;
- }
- #ifdef DISCARD_LEFT_IF_TITLE_OVERFULL
- if (padLeft <= 0 && padRight <= 0)
- { strcpy(mainTitle, cMid); // Abandon both right & left items
- break;
- }
- #else
- // Here even though there is no room to display the whole text I wanted
- // on the title bar (maybe the window had been iconized?) I will display
- // the left & mid parts with a single blank between.
- if (padLeft <= 0 && padRight <= 0)
- { sprintf(mainTitle, "%s%c%s", cLeft, PADDING_CHAR, cMid);
- break;
- }
- #endif
- else
- {
- #ifdef DISCARD_LEFT_IF_TITLE_OVERFULL
- if (padLeft <= 0 && wLeft != 0)
- { l = ""; // Abandon left item & re-try
- wLeft = 0;
- continue;
- }
- #endif
- if (padRight <= 0 && wRight != 0) // Abandon right item & re-try
- { r = "";
- wRight = 0;
- continue;
- }
- char *p = mainTitle;
- while (*l != 0) *p++ = *l++;
- int i;
- for (i=0; i<padLeft; i++) *p++ = PADDING_CHAR;
- l = cMid;
- while (*l != 0) *p++ = *l++;
- for (i=0; i<padRight; i++) *p++ = PADDING_CHAR;
- while (*r != 0) *p++ = *r++;
- *p = 0;
- break;
- }
- }
- SetWindowText(mainTitle);
- }
- void CMainWindow::cwin_report_left(const char *msg)
- {
- // I will take this opportunity to make the "To-File" menu item track
- // whether there really is a spool file active. This only effects how
- // the menu gets displayed, not what it does...
- if (complete)
- { GetMenu()->ModifyMenu(IDM_TOFILE+MF_STRING, MF_BYCOMMAND, IDM_TOFILE,
- spool_file==NULL ? "&To File ..." :
- "&Terminate log");
- DrawMenuBar();
- }
- if (msg == NULL) msg = "";
- //!!if (msg == NULL)
- //!!{ leftSetByUser = FALSE;
- //!! return; // Date and time of day will appear
- //!!}
- strncpy(cLeft, msg, 31); cLeft[31] = 0;
- ReWriteTitleText();
- leftSetByUser = TRUE;
- }
- void cwin_report_left(const char *s)
- {
- theApp.mainWindow->cwin_report_left(s);
- }
- void CMainWindow::cwin_report_mid(const char *msg)
- {
- if (msg == NULL) msg = programName;
- strncpy(cMid, msg, 31); cLeft[31] = 0;
- ReWriteTitleText();
- }
- void cwin_report_mid(const char *s)
- {
- theApp.mainWindow->cwin_report_mid(s);
- }
- void CMainWindow::cwin_report_right(const char *msg)
- {
- if (msg == NULL) msg = "";
- strncpy(cRight, msg, 31); cRight[31] = 0;
- ReWriteTitleText();
- }
- void cwin_report_right(const char *s)
- {
- theApp.mainWindow->cwin_report_right(s);
- }
- //!! The code (in c_applic.cpp) that updated this info on the screen
- //!! has now been removed, so this is no longer useful.
- //!!
- //!! void CMainWindow::cwin_display_date()
- //!! {
- //!! char dateBuffer[20];
- //!! time_t t0 = time(NULL);
- //!! char *m = ctime(&t0);
- //!! #ifdef OLD_AND_USES_UNIVERSAL_TIME_WHICH_IS_SILLY
- //!! sprintf(dateBuffer, "%02d-%.3s-%02d, %02d:%02d:%02d",
- //!! titleUpdateTime.wDay,
- //!! "xxxJanFebMarAprMayJunJulAugSepOctNovDec"+3*titleUpdateTime.wMonth,
- //!! titleUpdateTime.wYear%100,
- //!! titleUpdateTime.wHour, titleUpdateTime.wMinute,
- //!! titleUpdateTime.wSecond);
- //!! #else
- //!! sprintf(dateBuffer, "%.2s-%.3s-%.2s, %.8s",
- //!! m+8, m+4, m+22, m+11);
- //!! #endif
- //!! cwin_report_left(dateBuffer);
- //!! leftSetByUser = FALSE;
- //!! }
- void cwin_menus(char **packages, char **switches)
- {
- theApp.mainWindow->cwin_menus(packages, switches);
- }
- void CMainWindow::cwin_menus(char **packages, char **switches)
- {
- CMenu *main = GetMenu(); // main menu bar for this window
- #ifndef COMMON
- if (packages != NULL && *packages != NULL)
- {
- library_name_p = packages;
- main->DeleteMenu(4, MF_BYPOSITION);
- int n = IDM_FIRSTLOAD;
- CMenu mload;
- mload.CreatePopupMenu();
- int firstletter = 'a';
- int lastletter = 'a', nextletter;
- int count = 0, nextcount;
- char **p = packages;
- library_name_n = 0;
- while (*p++ != NULL) library_name_n++;
- p = packages;
- while (*p && **p == lastletter) count++, p++;
- char **p1 = p;
- while (*packages)
- { for (;;)
- { nextcount = 0;
- nextletter = lastletter + 1;
- while (*p && **p == nextletter) nextcount++, p++;
- if (count + nextcount > 20) break;
- lastletter = nextletter;
- count += nextcount;
- p1 = p;
- if (lastletter == 'z') break;
- }
- char subname[8];
- if (firstletter == lastletter) sprintf(subname, "%c", firstletter);
- else sprintf(subname, "%c-%c", firstletter, lastletter);
- CMenu sub1;
- sub1.CreatePopupMenu();
- while (packages != p1)
- sub1.AppendMenu(MF_STRING, n++, *packages++);
- mload.AppendMenu(MF_STRING | MF_POPUP, (UINT)sub1.Detach(), subname);
- firstletter = lastletter = nextletter;
- count = nextcount;
- p1 = p;
- }
- main->InsertMenu(4, MF_BYPOSITION | MF_POPUP | MF_STRING,
- (UINT)mload.Detach(), "&Load");
- }
- // Now do roughly the same with switches
- if (switches != NULL && *switches != NULL)
- { switch_name_p = switches;
- main->DeleteMenu(5, MF_BYPOSITION);
- int n = IDS_FIRSTSWITCH;
- CMenu mload;
- mload.CreatePopupMenu();
- int firstletter = 'a';
- int lastletter = 'a', nextletter;
- int count = 0, nextcount;
- char **p = switches;
- switch_name_n = 0;
- while (*p++ != NULL) switch_name_n++;
- p = switches;
- while (*p && (*p)[1] == lastletter) count++, p++;
- char **p1 = p;
- while (*switches)
- { for (;;)
- { nextcount = 0;
- nextletter = lastletter + 1;
- while (*p && (*p)[1] == nextletter) nextcount++, p++;
- if (count + nextcount > 20) break;
- lastletter = nextletter;
- count += nextcount;
- p1 = p;
- if (lastletter == 'z') break;
- }
- char subname[8];
- if (firstletter == lastletter) sprintf(subname, "%c", firstletter);
- else sprintf(subname, "%c-%c", firstletter, lastletter);
- CMenu sub1;
- sub1.CreatePopupMenu();
- while (switches != p1)
- { sub1.AppendMenu(MF_STRING, n, 1+*switches);
- sub1.CheckMenuItem(n, MF_BYCOMMAND |
- (**switches=='y' ? MF_CHECKED : MF_UNCHECKED));
- n++;
- switches++;
- }
- mload.AppendMenu(MF_STRING | MF_POPUP, (UINT)sub1.Detach(), subname);
- firstletter = lastletter = nextletter;
- count = nextcount;
- p1 = p;
- }
- main->InsertMenu(5, MF_BYPOSITION | MF_POPUP | MF_STRING,
- (UINT)mload.Detach(), "&Switch");
- }
- #endif /* COMMON */
- DrawMenuBar();
- }
- // When the user types in a key all that happens (to start with) is that
- // I store it in a buffer. If the buffer gets to be full I beep and
- // ignore any further characters.
- #define Ctrl(x) ((x) & 0x1f)
- void CMainWindow::OnChar(UINT ch, UINT nRepCnt, UINT nFlags)
- {
- CSL_IGNORE(nRepCnt);
- CSL_IGNORE(nFlags);
- // I turn the ENTER key into a newline character (and consequently ^M will
- // also go that way). I also discard 0x7f (for the delete/erase key) and
- // anything in the range 0x80 to 0xa0 (which I reserve for my own private
- // use in the text buffer). I ignore the repetition count.
- // I also have to process some accelerators.
- pageLine = caretLine;
- // The following line is in effect following a request from F J Wright. He
- // pointed out that the character that gets pressed when one is in page mode
- // to un-pause the screen should be allowed to do just that and should not
- // also appear as an input character. So here if I find a character being
- // typed when things are paused I just lose it (as well as clearing the
- // flags that pause things.
- if (pagePaused)
- { pagePaused = FALSE;
- return;
- }
- inject_randomness(ch);
- inject_randomness((int)time(NULL));
- switch (ch)
- {
- case Ctrl('C'):
- ToIcaret();
- caretVisible = TRUE;
- cwin_ensure_screen(FALSE); // may jump screen to caret location
- OnInterrupt();
- return;
- // I make Ctrl+D and Ctrl+Z exit, but note very well that they both
- // exit from this system super-promptly when the key is typed, and
- // they do NOT wait until the program gets around to reading them.
- case Ctrl('D'): // Some Unix users may be used to Ctrl+D as EOF?
- OnExit();
- return;
- case Ctrl('G'):
- ToIcaret();
- caretVisible = TRUE;
- cwin_ensure_screen(FALSE); // may jump screen to caret location
- OnBacktrace();
- return;
- case Ctrl('I'): // TAB (I need to worry about the display of tabs!)
- break;
- case Ctrl('J'): // LINEFEED
- ch = '\n';
- break;
- case Ctrl('L'):
- OnRedraw();
- return;
- case Ctrl('M'): // CARRIAGE RETURN
- ch = '\n';
- case Ctrl('N'):
- OnEnd(); // so that CR accepts the line always
- break;
- case Ctrl('O'):
- OnCopy();
- return;
- case Ctrl('Q'):
- if (pageMode) OnPageMode();
- return;
- case Ctrl('R'):
- OnReInput();
- return;
- case Ctrl('S'):
- if (!pageMode) OnPageMode();
- return;
- case Ctrl('V'):
- OnPaste();
- return;
- case Ctrl('X'):
- OnCut();
- return;
- case Ctrl('Z'): // Some PC users may be used to Ctrl+Z as EOF?
- OnExit();
- return;
- case 0x1b: // ESC goes through to the user
- break;
- default:
- if ((ch & 0x7f) < 0x20) return; // Discard control chars.
- break;
- }
- CancelSelection();
- ToIcaret();
- caretVisible = TRUE;
- cwin_ensure_screen(FALSE);
- // If the caret is NOT at the end of the text then the insert operation I do
- // here goes directly into the text buffer. If the caret IS at the end of the
- // text I push the character into a type-ahead buffer, and beep if there
- // was no room for it there.
- if (caretChar != textLast)
- { if (insertMode) cwin_caret_putchar(ch);
- else cwin_caret_replacechar(ch);
- }
- else
- { int p2 = (typeAheadP2 + 1) & typeAheadBufferMask;
- if (p2 == typeAheadP1)
- { ::MessageBeep(0xffffffff);
- return;
- }
- typeAheadBuffer[typeAheadP2] = (char)ch;
- typeAheadP2 = p2;
- }
- }
- void CMainWindow::ReplaceLastLine(unsigned char *s)
- {
- // The argument s here is ignored, and maybe is not needed????
- typeAheadP1 = typeAheadP2 = 0; // cancel type-ahead
- selRootValid = FALSE;
- OnEnd();
- caretVisible = TRUE;
- // The following loop deletes characters from the last line of the input,
- // until what is left is (a) a totally empty buffer, (b) a buffer where the
- // last line is empty or (c) the final thing in the buffer is a prompt
- // item. While doing the deletion I will not update the screen at all.
- while (textFirst != textLast &&
- lineBuffer[lineLast].address != textLast)
- { int n = (textLast-1) & TEXT_MASK;
- if ((textBuffer[n]&0xff)==CH_ENDPROMPT) break;
- if (textLast == caretChar) caretChar = n;
- if (textLast == icaretChar) icaretChar = n;
- if (textLast == inputLineStart) inputLineStart = n;
- textLast = n;
- }
- int i=savedLines[currentInputLine];
- for (;;)
- { int ch = savedChars[i++];
- if (ch == '\n') break;
- if (i == MAX_SAVED_CHARS) i = 0;
- cwin_putchar(ch);
- }
- CRect cr(0, LineY(lineLast),
- clientWidth, LineY(lineLast)+LineDY(lineLast));
- InvalidateRect(&cr);
- LineSizes();
- UpdateWindow();
- cwin_ensure_screen(FALSE);
- }
- void CMainWindow::OnKeyDown(UINT ch, UINT nRepCnt, UINT nFlags)
- {
- CPoint cp;
- int saveX, saveY;
- CSL_IGNORE(nRepCnt);
- pageLine = caretLine;
- switch (ch)
- {
- // The INS key flips the status with respect to insert mode. I make this
- // TRUE at the start of a run, and any time anybody does a PASTE operation.
- case VK_INSERT:
- insertMode = !insertMode;
- return;
- // I make the DEL key delete forwards by first simulating a right-arrow
- // cursor movement effect and then performing the sort of deletion that
- // I consider ordinary.
- case VK_DELETE:
- ToIcaret();
- caretVisible = TRUE;
- cwin_ensure_screen(FALSE);
- if (selStartChar != selEndChar)
- { DeleteSelection();
- return;
- }
- if (caretChar == textLast) return;
- OnKeyDown(VK_RIGHT, 1, nFlags);
- if (caretChar != textLast)
- { cwin_caret_unputchar();
- return;
- }
- if (typeAheadP1 == typeAheadP2)
- { typeAheadBuffer[typeAheadP2] = 0x7f;
- typeAheadP2 = (typeAheadP2 + 1) & typeAheadBufferMask;
- return;
- }
- typeAheadP2 = (typeAheadP2 - 1) & typeAheadBufferMask;
- return;
- case VK_BACK:
- ToIcaret();
- caretVisible = TRUE;
- cwin_ensure_screen(FALSE); // may jump screen to caret location
- // If there is a selection valid then DELETE will delete it, without any
- // regard for where the caret is (although the caret will generally be
- // at one or other end of the selected region).
- if (selStartChar != selEndChar)
- { DeleteSelection();
- return;
- }
- // If the caret is NOT at the end of the text then DELETE has to work
- // inside the buffer. This will typically make it a fairly expensive
- // operation.
- if (caretChar != textLast)
- { cwin_caret_unputchar();
- return;
- }
- // If there are no characters typed ahead I will put 0x7f into the
- // input buffer. This case can arise in two different circumstances. The
- // first is if a user types DELETE as the first character on a line. Then
- // I will want to discard the junk characters later on. Otherwise it may be
- // that the current line is partly displayed and is subject to interactive
- // editing and then the 0x7f put in the buffer will be found again very
- // soon and used to cause deletion of some prior character. Because of this
- // latter case I will not beep on initial DELETE characters on a line.
- if (typeAheadP1 == typeAheadP2)
- { typeAheadBuffer[typeAheadP2] = 0x7f;
- typeAheadP2 = (typeAheadP2 + 1) & typeAheadBufferMask;
- return;
- }
- // If there are typed-ahead characters I can just discard the most recent. At
- // this stage the character has not been displayed (eg a user typed XXX DEL
- // before the application was ready to look at it) so the removal is really
- // very easy.
- typeAheadP2 = (typeAheadP2 - 1) & typeAheadBufferMask;
- return;
- // The various keys like "PAGE UP" etc do just what the scroll bar can do.
- case VK_PRIOR:
- OnVScroll(SB_PAGEUP, 0, NULL);
- return;
- case VK_NEXT:
- OnVScroll(SB_PAGEDOWN, 0, NULL);
- return;
- case VK_HOME:
- OnHome();
- return;
- case VK_END:
- OnEnd();
- return;
- // The cursor arrow keys are funny here, with behaviour inspired by the
- // DOSKEY history program. Right and left movement moves the caret, but
- // up and down will bring back one of a number of stored input line.
- case VK_UP:
- // If my caret is on the last line I will do something DOSKEY-ish...
- if (caretLine == lineLast)
- { if (currentInputLine == savedP1) return;
- if (currentInputLine == -1) currentInputLine = savedP2;
- if (currentInputLine == 0) currentInputLine = MAX_SAVED_LINES;
- currentInputLine--;
- ReplaceLastLine(&savedChars[savedLines[currentInputLine]]);
- return;
- }
- if (caretLine == lineFirst) return;
- if (caretLine == lineVisible) OnVScroll(SB_LINEUP, 0, NULL);
- cp.x = caretX-xOffset;
- cp.y = caretY - 1;
- FindMouseChar(cp);
- caretVisible = TRUE;
- cwin_ensure_screen(FALSE);
- return;
- case VK_DOWN:
- // If my caret is on the last line I will do something DOSKEY-ish...
- if (caretLine == lineLast)
- { int n = currentInputLine + 1;
- if (n == MAX_SAVED_LINES) n = 0;
- if (currentInputLine == -1 || n == savedP2) return;
- currentInputLine = n;
- ReplaceLastLine(&savedChars[savedLines[currentInputLine]]);
- return;
- }
- cp.x = caretX-xOffset;
- saveY = caretY, saveX = caretX;
- cp.y = caretY + LineDY(caretLine) + 1;
- // The case when I am at the bottom of the screen and hit the "DOWN" key
- // is a bit of a mess. I call FindMouse Char as usual to try to re-position
- // the caret. If that would be outside the window I get a flag set to tell me
- // so. In that case I re-position the caret where it just came from, then
- // scroll the window, and finally repeat my attempt to re-position the
- // caret.
- FindMouseChar(cp);
- if (mouseOutside & 8)
- { cp.x = saveX-xOffset;
- cp.y = saveY + 1;
- FindMouseChar(cp);
- OnVScroll(SB_LINEDOWN, 0, NULL);
- cp.x = caretX-xOffset;
- cp.y = caretY + LineDY(caretLine) + 1;
- FindMouseChar(cp);
- }
- caretVisible = TRUE;
- cwin_ensure_screen(FALSE);
- return;
- case VK_LEFT:
- caretVisible = TRUE;
- if (textFirst == caretChar) return; // at start of buffer?
- if (lineBuffer[caretLine].address == caretChar) // back over a newline?
- { int n = (caretLine-1)&LINE_MASK;
- caretX = lineBuffer[n].width;
- caretLine=n;
- n = lineBuffer[n].address;
- while (textBuffer[n]!='\n') n = (n+1)&TEXT_MASK;
- caretChar = n;
- }
- else
- { int n = (caretChar-1) & TEXT_MASK;
- int w = windowFonts.HCourier.across['X'];
- if ((textBuffer[n]&0xff)==CH_ENDPROMPT) // move over whole of a prompt
- { do
- { n = (n-1)&TEXT_MASK;
- caretX -= w;
- } while (n!=textFirst && (textBuffer[n]&0xff)!=CH_PROMPT);
- caretChar = n;
- if (lineBuffer[caretLine].address == caretChar) // back over a newline?
- { n = (caretLine-1)&LINE_MASK;
- caretX = lineBuffer[n].width;
- caretLine=n;
- n = lineBuffer[n].address;
- while (textBuffer[n]!='\n') n = (n+1)&TEXT_MASK;
- caretChar = n;
- }
- }
- else caretX -= w;
- if (caretLine == lineLast) icaretChar = n, icaretLine = lineLast;
- caretChar = n;
- // The amount of invalidation done here runs across to the end of the
- // last line, and that ought to get rid of any residues of the old
- // caret. I use an APPROXIMATION to the new value of caretX, but hope that
- // that gets corrected during the re-painting.
- CRect cr(caretX-xOffset, LineY(caretLine),
- clientWidth, LineY(caretLine)+LineDY(caretLine));
- InvalidateRect(&cr); // Should I hide the caret here?
- }
- UpdateWindow();
- cwin_ensure_screen(FALSE);
- return;
- case VK_RIGHT:
- caretVisible = TRUE;
- if (textLast == caretChar) return; // at end of buffer?
- if (textBuffer[caretChar] == '\n') // at a newline?
- { caretX = 0;
- int w = windowFonts.HCourier.across['X'];
- caretLine = (caretLine+1)&LINE_MASK;
- caretChar = lineBuffer[caretLine].address;
- if (caretChar!=textLast &&
- (textBuffer[caretChar]&0xff)==CH_PROMPT) // move over whole of a prompt
- { while (caretChar!=textLast &&
- (textBuffer[caretChar]&0xff)!=CH_ENDPROMPT)
- { caretChar = (caretChar+1)&TEXT_MASK;
- caretX += w;
- }
- caretX -= w;
- if (caretChar!=textLast) caretChar = (caretChar+1)&TEXT_MASK;
- }
- CPoint caretPos(caretX-xOffset, caretY);
- SetCaretPos(caretPos);
- if (caretLine == lineLast)
- { icaretLine = caretLine;
- icaretChar = caretChar;
- icaretX = caretX;
- }
- }
- else
- { int n = caretChar;
- int oldCaretX = caretX;
- int w = windowFonts.HCourier.across['X'];
- n = (n+1)&TEXT_MASK;
- caretX += w;
- if (caretLine == lineLast) icaretChar = n, icaretLine = caretLine;
- caretChar = n;
- CRect cr(oldCaretX-xOffset, LineY(caretLine),
- caretX-xOffset+w, LineY(caretLine)+LineDY(caretLine));
- InvalidateRect(&cr); // Should I hide the caret here?
- }
- UpdateWindow();
- cwin_ensure_screen(FALSE);
- return;
- }
- }
- // The next function takes a mouse position and identifies the character in
- // the text buffer that it identifies. It must be kept well in step with
- // the corresponding code in PaintTextLine().
- int CMainWindow::FindMouseChar(CPoint point)
- {
- // I may have captured the mouse, in which case the coordinates returned may
- // be outside my client area. Clip them to it before doing anything else.
- int x = point.x, y = point.y;
- int nCount = 0;
- mouseOutside = 0;
- if (x<0) x=0, mouseOutside |= 1;
- else if (x>clientWidth) x=clientWidth, mouseOutside |= 2;
- if (y<0) y=0, x=0, mouseOutside |= 4;
- else if (y>clientHeight) y=clientHeight, x=clientWidth, mouseOutside |= 8;
- if (mouseOutside == 0) selectScrollSpeed = 0;
- int yOffset = lineBuffer[lineVisible].position;
- int line;
- for (line=lineVisible;;line=(line+1)&LINE_MASK)
- { int y1 = lineBuffer[line].position - yOffset;
- int y2 = y1 + lineBuffer[line].height;
- if (y>=y1 && y<=y2) break;
- if (line==lineLast) break;
- }
- // Now I have identified the line that the mouse cursor is on.
- int textChar = lineBuffer[line].address;
- int activeFont = CH_COURIER;
- unsigned char *activeWidths = windowFonts.HCourier.across;
- int xc = -xOffset;
- int inPrompt = -1, inPromptX;
- for (;;textChar=(textChar+1)&TEXT_MASK)
- { if (textChar==textLast) break;
- int c = textBuffer[textChar];
- int w = activeWidths[c & 0xff];
- switch (c & 0xff)
- {
- case '\n':
- break;
- case CH_PROMPT:
- inPrompt = textChar, inPromptX = xc;
- continue;
- case CH_ENDPROMPT:
- inPrompt = -1;
- continue;
- case CH_RED:
- // case CH_BLUE:
- // case CH_BLACK:
- case CH_GRAY:
- continue;
- case CH_COURIER:
- activeFont = c;
- activeWidths = windowFonts.HCourier.across;
- continue;
- case CH_ROMAN:
- activeFont = c;
- activeWidths = windowFonts.HRoman.across;
- continue;
- case CH_BOLD:
- activeFont = c;
- activeWidths = windowFonts.HBold.across;
- continue;
- case CH_ITALIC:
- activeFont = c;
- activeWidths = windowFonts.HItalic.across;
- continue;
- case CH_SYMBOL:
- activeFont = c;
- activeWidths = windowFonts.HSymbol.across;
- continue;
- case CH_Roman:
- activeFont = c;
- activeWidths = windowFonts.Hroman.across;
- continue;
- case CH_Bold:
- activeFont = c;
- activeWidths = windowFonts.Hbold.across;
- continue;
- case CH_Italic:
- activeFont = c;
- activeWidths = windowFonts.Hitalic.across;
- continue;
- case CH_Symbol:
- activeFont = c;
- activeWidths = windowFonts.Hsymbol.across;
- continue;
- default:
- if (c == '\t')
- { c = ' ';
- w = currentWidths[' ']*(8-(nCount&7));
- nCount = 7;
- }
- else if (c < 32) w = activeWidths['^'] + activeWidths[c & 0xff],
- nCount++;
- if (x <= xc+w/2)
- {
- // I will not allow the user to select a position within a prompt, since
- // if I did it would make it possible to CUT or DELete part of the prompt
- // string, including either the control character that marked its start or
- // its end. The result would be that the status of the text as "prompt" would
- // be lost with possible consequent confusion.
- if (inPrompt>=0) textChar=inPrompt, xc=inPromptX;
- break; // Bingo! Found it.
- }
- xc += w;
- nCount++;
- continue;
- }
- break;
- }
- // If the mouse click moves me to the last line then I place the
- // input caret there. Otherwise if a previous caret had been on the last line
- // I leave the input caret where the previous caret had been.
- if (line == lineLast)
- { icaretChar = textChar;
- icaretX = xc+xOffset;
- icaretFontWidths = activeWidths;
- icaretLine = line;
- }
- else if (caretLine == lineLast)
- { icaretChar = caretChar;
- icaretX = caretX;
- icaretFontWidths = caretFontWidths;
- icaretLine = caretLine;
- }
- caretChar = textChar;
- caretX = xc+xOffset;
- caretFontWidths = activeWidths;
- caretLine = line;
- inject_randomness(caretX);
- inject_randomness(caretLine);
- // In the case that mouse activity re-positions the caret I will move the
- // caret on the screen directly to where it needs to be shown. No benefit
- // would come from delaying until a future re-paint operation.
- HideCaret();
- CPoint caretPos(caretX-xOffset, caretY);
- SetCaretPos(caretPos);
- ShowCaret();
- return caretChar;
- }
- // StartSelection is slightly curious in that it sets the selection
- // to be empty but rooted at a specific point. The effect will be that
- // a subsequent MouseMove or shift-click can extend the selection to
- // be non-trivial. The ugly feature of this is that a selection that has
- // been "started" in this way could suffer if the contents of the text buffer
- // moved. To prevent trouble I will keep a flag that indicates when I have
- // a selection starting-point that I will count as valid. I will set this
- // flag here and clear it anywhere where I move or destroy bits of the
- // buffer.
- void CMainWindow::StartSelection()
- {
- CancelSelection();
- selFirstChar = selStartChar = selEndChar = caretChar;
- selFirstX = selStartX = selEndX = caretX;
- selFirstLine = selStartLine = selEndLine = caretLine;
- selRootValid = TRUE;
- }
- void CMainWindow::InvalidateSelection(int l1, int x1, int l2, int x2)
- {
- int w;
- if (l1 > l2)
- { w=l1, l1=l2, l2=w, w=x1, x1=x2, x2=w;
- if (x2 == 0) l2=(l2-1)&LINE_MASK, x2=clientWidth+xOffset;
- }
- HideCaret();
- if (l1==l2)
- { if (x1>x2) w=x1, x1=x2, x2=w;
- CRect r(x1-xOffset, LineY(l1),
- x2+caretWidth-xOffset, LineY(l1)+LineDY(l1));
- if (x1 != x2) InvalidateRect(&r);
- }
- else
- { CRect r(0, LineY(l1), clientWidth, LineY(l2)+LineDY(l2));
- InvalidateRect(&r);
- }
- ShowCaret();
- }
- void CMainWindow::ExtendSelection()
- {
- int oldStart = selStartChar, oldEnd = selEndChar;
- if (selFirstChar == selStartChar)
- InvalidateSelection(selEndLine, selEndX, caretLine, caretX);
- else InvalidateSelection(selStartLine, selStartX, caretLine, caretX);
- if (betweenChar(textFirst, caretChar, selFirstChar))
- { selStartChar = caretChar; selStartX = caretX; selStartLine = caretLine;
- selEndChar = selFirstChar; selEndX = selFirstX; selEndLine = selFirstLine;
- }
- else
- { selStartChar = selFirstChar; selStartX = selFirstX; selStartLine = selFirstLine;
- selEndChar = caretChar; selEndX = caretX; selEndLine = caretLine;
- }
- if (selStartChar==oldStart && selEndChar==oldEnd) return;
- }
- void CMainWindow::CancelSelection()
- {
- InvalidateSelection(selStartLine, selStartX, selEndLine, selEndX);
- selFirstChar = selStartChar = selEndChar = caretChar;
- selFirstX = selStartX = selEndX = caretX;
- selFirstLine = selStartLine = selEndLine = caretLine;
- }
- void CMainWindow::OnLButtonDblClk(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- CSL_IGNORE(point);
- }
- void CMainWindow::OnLButtonDown(UINT nFlags, CPoint point)
- {
- /*
- * The idea here is that a first click within a window should NOT move
- * the caret or start a new selection, since it was probably (?) the
- * mouse event that just caused the window to become active again.
- */
- CSL_IGNORE(nFlags);
- if (pagePaused || hasFocus) return;
- FindMouseChar(point);
- trackingSelection = TRUE;
- SetCapture();
- selectScrollSpeed = 0;
- if (nFlags & MK_SHIFT && selRootValid) ExtendSelection();
- else StartSelection();
- }
- void CMainWindow::OnLButtonUp(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- if (hasFocus)
- { hasFocus = FALSE;
- return;
- }
- if (pagePaused) pageLine = caretLine;
- else
- { FindMouseChar(point);
- ::ReleaseCapture();
- if (trackingSelection) ExtendSelection();
- trackingSelection = FALSE;
- selectScrollSpeed = 0;
- }
- }
- void CMainWindow::OnMButtonDblClk(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- CSL_IGNORE(point);
- }
- void CMainWindow::OnMButtonDown(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- CSL_IGNORE(point);
- }
- void CMainWindow::OnMButtonUp(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- CSL_IGNORE(point);
- if (hasFocus)
- { hasFocus = FALSE;
- return;
- }
- pageLine = caretLine;
- }
- void CMainWindow::OnRButtonDblClk(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- CSL_IGNORE(point);
- }
- void CMainWindow::OnRButtonDown(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- CSL_IGNORE(point);
- }
- void CMainWindow::OnRButtonUp(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- CSL_IGNORE(point);
- if (hasFocus)
- { hasFocus = FALSE;
- return;
- }
- pageLine = caretLine;
- }
- void CMainWindow::OnMouseMove(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- inject_randomness((int)clock());
- if (trackingSelection && !pagePaused)
- { FindMouseChar(point);
- if (mouseOutside != 0)
- { if (selectScrollSpeed == 0)
- selectScrollSpeed = 7,
- selectScrollCount = 1;
- selectScrollDirection = mouseOutside;
- selectScrollPoint = point;
- OnTimer(1);
- }
- else ExtendSelection();
- }
- }
- void CMainWindow::OnTimer(UINT timerId)
- {
- CSL_IGNORE(timerId);
- if (selectScrollSpeed == 0) return;
- selectScrollCount--;
- if (selectScrollCount > 0) return;
- if (selectScrollDirection == 0) return;
- if (selectScrollDirection & 8) OnVScroll(SB_LINEDOWN, 0, NULL);
- else if (selectScrollDirection & 4) OnVScroll(SB_LINEUP, 0, NULL);
- else if (selectScrollDirection & 1 && xOffset!=0)
- OnHScroll(SB_LINELEFT, 0, NULL);
- else if (selectScrollDirection & 2 &&
- caretX < lineBuffer[caretLine].width)
- OnHScroll(SB_LINERIGHT, 0, NULL);
- selectScrollCount = selectScrollSpeed;
- if (selectScrollSpeed > 1) selectScrollSpeed--;
- FindMouseChar(selectScrollPoint);
- ExtendSelection();
- }
- void CMainWindow::OnNcLButtonDown(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- CSL_IGNORE(point);
- }
- void CMainWindow::OnNcMButtonDown(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- CSL_IGNORE(point);
- }
- void CMainWindow::OnNcRButtonDown(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- CSL_IGNORE(point);
- }
- //
- // Now the things provoked from the menus...
- //
- // SAVEAS will dump everything that is in the text buffer into a
- // log file. The status of prompts and any other funny stuff will not
- // concern me here - I will just dump out bytes as they appear in the
- // text buffer. But at least I will arrange that I do not dump any characters
- // that have a code >= 0x80 into the saved file.
- void CMainWindow::OnSaveAs()
- {
- CFileDialog FDialog(FALSE, "LOG", "savefile.log",
- OFN_HIDEREADONLY,
- "Log Files (*.LOG)|*.LOG|All Files (*.*)|*.*||", NULL);
- FDialog.m_ofn.lpstrTitle = "Save As";
- if (FDialog.DoModal() != IDOK) return;
- FILE *ofile = fopen(FDialog.GetPathName(), "w");
- if (ofile == NULL)
- { DisplayMsg("Could not write to file");
- cwin_ensure_screen(FALSE);
- return;
- }
- for (int i=lineFirst; i!=lineLast; i=(i+1)&LINE_MASK)
- { int p = lineBuffer[i].address, c;
- while ((c = textBuffer[p]) != '\n')
- { if ((c & 0x80) == 0) putc(c, ofile);
- p = (p+1) & TEXT_MASK;
- }
- putc('\n', ofile);
- }
- int p = lineBuffer[lineLast].address;
- while (p != textLast)
- { int c = textBuffer[p];
- if ((c & 0x80) == 0) putc(c, ofile);
- p = (p+1) & TEXT_MASK;
- }
- putc('\n', ofile); // extra newline at end to be tidy.
- fclose(ofile);
- cwin_ensure_screen(FALSE);
- }
- void CMainWindow::OnSaveSel()
- {
- CFileDialog FDialog(FALSE, "LOG", "savesel.log",
- OFN_HIDEREADONLY,
- "Log Files (*.LOG)|*.LOG|All Files (*.*)|*.*||", NULL);
- FDialog.m_ofn.lpstrTitle = "Save Selection";
- if (FDialog.DoModal() != IDOK) return;
- FILE *ofile = fopen(FDialog.GetPathName(), "w");
- if (ofile == NULL)
- { DisplayMsg("Could not write to file");
- cwin_ensure_screen(FALSE);
- return;
- }
- // print selected region...
- int l1 = selStartLine;
- int cp = selStartChar;
- for (;;)
- { while (cp!=selEndChar && textBuffer[cp]!='\n')
- { int c = textBuffer[cp];
- if ((c & 0x80) == 0) putc(c, ofile);
- cp = (cp+1)&TEXT_MASK;
- }
- if (cp==selEndChar) break;
- putc('\n', ofile);
- l1 = (l1+1)&LINE_MASK;
- cp = lineBuffer[l1].address;
- }
- fclose(ofile);
- cwin_ensure_screen(FALSE);
- }
- void CMainWindow::OnToFile()
- {
- // When I have a log file active I will make the menu entry into
- // one that closes the transcript, while if I do not have a file in
- // use the File/&T menu entry will start one off.
- //
- // NOTE that the "spool_file" mentioned here belongs somewhere in CSL,
- // and is not in the "cwin" code in any more direct way.
- if (spool_file != NULL)
- { fprintf(spool_file, "\n+++ End of transcript +++\n");
- fclose(spool_file);
- spool_file = NULL;
- GetMenu()->ModifyMenu(IDM_TOFILE+MF_STRING, MF_BYCOMMAND,
- IDM_TOFILE, "&To File...");
- DrawMenuBar();
- cwin_ensure_screen(FALSE);
- return;
- }
- CFileDialog FDialog(FALSE, "LOG", "logfile.log",
- OFN_HIDEREADONLY,
- "Log Files (*.LOG)|*.LOG|All Files (*.*)|*.*||", NULL);
- FDialog.m_ofn.lpstrTitle = "Transcript File";
- if (FDialog.DoModal() != IDOK) return;
- FILE *ofile = fopen(FDialog.GetPathName(), "w");
- if (ofile == NULL)
- { DisplayMsg("Could not write to file");
- cwin_ensure_screen(FALSE);
- return;
- }
- spool_file = ofile;
- time_t t0 = time(NULL);
- fprintf(spool_file, "+++ Transcript started at %.24s +++\n", ctime(&t0));
- GetMenu()->ModifyMenu(IDM_TOFILE+MF_STRING, MF_BYCOMMAND,
- IDM_TOFILE, "&Terminate log");
- DrawMenuBar();
- cwin_ensure_screen(FALSE);
- }
- static CFont *MakeNewFont(CDC *dc, FontHeights *fh, DWORD charSet,
- const char *fontName, int weight,
- DWORD italic, int height)
- {
- CFont *newFont = new CFont;
- newFont->CreateFont(
- height, 0, // height, width
- 0, 0, // angle of escapement, base line orientation angle
- weight, italic, // weight, italic-flag
- 0, 0, charSet, // underline, strike-out, character-set
- OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
- DEFAULT_QUALITY, DEFAULT_PITCH+FF_DONTCARE,
- fontName);
- dc->SelectObject(newFont);
- TEXTMETRIC mm;
- dc->GetTextMetrics(&mm);
- fh->up = mm.tmExternalLeading + mm.tmAscent;
- fh->down = mm.tmDescent;
- fh->height = mm.tmHeight;
- int widths[256], i;
- dc->GetCharWidth(0, 255, widths);
- for (i=0x80; i<0xa0; i++) widths[i] = 0; // my control characters
- // The width of a character in any sensible sized font will be less than
- // 256 pixels, so I save some space by stashing the information in a byte
- // array rather than a word array. If I really knew I was only ever going to
- // use TrueType fonts maybe I ought to use ABCWidths rather than just
- // widths. At present I think that would count as overkill.
- for (i=0; i<256; i++) fh->across[i] = (unsigned char)widths[i];
- return newFont;
- }
- void CMainWindow::OnPrint()
- {
- CPrintDialog pd(FALSE);
- if (pd.DoModal() != IDOK)
- { cwin_ensure_screen(FALSE);
- return;
- }
- CDC PrintDC;
- if (PrintDC.CreateDC(pd.GetDriverName(), pd.GetDeviceName(),
- pd.GetPortName(), NULL))
- { int printWidth = PrintDC.GetDeviceCaps(HORZRES),
- printHeight = PrintDC.GetDeviceCaps(VERTRES);
- // Also get the size of the print area in mm.
- int sizeX = PrintDC.GetDeviceCaps(HORZRES), // measured in pixels
- sizeY = PrintDC.GetDeviceCaps(VERTRES);
- int mmX = PrintDC.GetDeviceCaps(HORZSIZE), // measured in mm
- mmY = PrintDC.GetDeviceCaps(VERTSIZE);
- // I will try to allow a 10mm margin to right & left and a 15mm at top
- //and bottom of the printed page.
- int offsetX = (10*sizeX)/mmX;
- int offsetY = (15*sizeY)/mmY;
- sizeX -= 2*offsetX;
- sizeY -= 2*offsetY;
- // Create printer fonts, using the same type face as is present on the
- // screen. Scale it so that around 80 columns will fit across the page.
- // I scale for 81 not 80 to leave a margin for rounding etc etc.
- FontHeights fh;
- CFont *f = MakeNewFont(&PrintDC, &fh, ANSI_CHARSET,
- (char *)windowFonts.baseFace,
- windowFonts.boldFlag, 0, 100);
- PrintDC.SelectStockObject(SYSTEM_FONT);
- delete f;
- int X = fh.across['X'];
- int newH = (fh.height * sizeX)/(81 * X);
- printerFonts.ChangeFont(&PrintDC, newH,
- windowFonts.boldFlag, windowFonts.baseFace);
- DOCINFO DocInfo;
- DocInfo.cbSize = sizeof(DOCINFO);
- DocInfo.lpszDocName = "Screen Dump";
- DocInfo.lpszOutput = NULL;
- if (PrintDC.StartDoc(&DocInfo) != -1)
- {
- PrintDC.StartPage();
- PrintDC.SetTextAlign(TA_BASELINE);
- PrintDC.SetTextColor(RGB(0,0,0)); currentColour = CH_BLACK;
- PrintDC.SetBkColor(windowColour);
- PrintDC.SelectObject(printerFonts.Courier);currentFont = CH_COURIER;
- currentWidths = printerFonts.HCourier.across;
- int y = offsetY, line;
- for (line=lineFirst;; line=(line+1)&LINE_MASK)
- { int up, down;
- MeasureLine(lineBuffer[line].address,
- &up, &down, &printerFonts);
- int y1 = y+up;
- int y2 = y+up+down;
- if (y2 > sizeY)
- { PrintDC.EndPage();
- PrintDC.StartPage();
- PrintDC.SetTextAlign(TA_BASELINE);
- PrintDC.SetTextColor(RGB(0,0,0)); currentColour = CH_BLACK;
- PrintDC.SetBkColor(windowColour);
- PrintDC.SelectObject(printerFonts.Courier);currentFont = CH_COURIER;
- currentWidths = printerFonts.HCourier.across;
- y = offsetY;
- y1 = y+up;
- y2 = y+up+down;
- }
- PaintTextLine(&PrintDC, offsetX, y, y1, y2,
- lineBuffer[line].address,
- sizeX, &printerFonts, 1);
- y = y2;
- if (line==lineLast) break;
- }
- PrintDC.SelectStockObject(BLACK_PEN);
- PrintDC.EndPage();
- PrintDC.EndDoc();
- }
- printerFonts.DeleteFonts();
- }
- GlobalFree(pd.m_pd.hDevMode);
- GlobalFree(pd.m_pd.hDevNames);
- cwin_ensure_screen(FALSE);
- }
- void CMainWindow::OnPrintSel()
- {
- if (selStartChar == selEndChar) return; // nothing selected!
- CPrintDialog pd(FALSE);
- if (pd.DoModal() != IDOK)
- { cwin_ensure_screen(FALSE);
- return;
- }
- CDC PrintDC;
- if (PrintDC.CreateDC(pd.GetDriverName(), pd.GetDeviceName(),
- pd.GetPortName(), NULL))
- { int printWidth = PrintDC.GetDeviceCaps(HORZRES),
- printHeight = PrintDC.GetDeviceCaps(VERTRES);
- // Also get the size of the print area in mm.
- int sizeX = PrintDC.GetDeviceCaps(HORZRES), // measured in pixels
- sizeY = PrintDC.GetDeviceCaps(VERTRES);
- int mmX = PrintDC.GetDeviceCaps(HORZSIZE), // measured in mm
- mmY = PrintDC.GetDeviceCaps(VERTSIZE);
- // I will try to allow a 10mm margin to right & left and a 15mm at top
- //and bottom of the printed page.
- int offsetX = (10*sizeX)/mmX;
- int offsetY = (15*sizeY)/mmY;
- sizeX -= 2*offsetX;
- sizeY -= 2*offsetY;
- // Create printer fonts, using the same type face as is present on the
- // screen. Scale it so that around 80 columns will fit across the page.
- // I scale for 81 not 80 to leave a margin for rounding etc etc.
- FontHeights fh;
- CFont *f = MakeNewFont(&PrintDC, &fh, ANSI_CHARSET,
- (char *)windowFonts.baseFace,
- windowFonts.boldFlag, 0, 100);
- PrintDC.SelectStockObject(SYSTEM_FONT);
- delete f;
- int X = fh.across['X'];
- int newH = (fh.height * sizeX)/(81 * X);
- printerFonts.ChangeFont(&PrintDC, newH,
- windowFonts.boldFlag, windowFonts.baseFace);
- DOCINFO DocInfo;
- DocInfo.cbSize = sizeof(DOCINFO);
- DocInfo.lpszDocName = "Selection Dump";
- DocInfo.lpszOutput = NULL;
- if (PrintDC.StartDoc(&DocInfo) != -1)
- {
- PrintDC.StartPage();
- PrintDC.SetTextAlign(TA_BASELINE);
- PrintDC.SetTextColor(RGB(0,0,0)); currentColour = CH_BLACK;
- PrintDC.SetBkColor(windowColour);
- PrintDC.SelectObject(printerFonts.Courier);currentFont = CH_COURIER;
- currentWidths = printerFonts.HCourier.across;
- int y = offsetY, line;
- for (line=selStartLine;; line=(line+1)&LINE_MASK)
- { int up, down;
- MeasureLine(lineBuffer[line].address,
- &up, &down, &printerFonts);
- int y1 = y+up;
- int y2 = y+up+down;
- if (y2 > sizeY)
- { PrintDC.EndPage();
- PrintDC.StartPage();
- PrintDC.SetTextAlign(TA_BASELINE);
- PrintDC.SetTextColor(RGB(0,0,0)); currentColour = CH_BLACK;
- PrintDC.SetBkColor(windowColour);
- PrintDC.SelectObject(printerFonts.Courier);currentFont = CH_COURIER;
- currentWidths = printerFonts.HCourier.across;
- y = offsetY;
- y1 = y+up;
- y2 = y+up+down;
- }
- PaintTextLine(&PrintDC, offsetX, y, y1, y2,
- lineBuffer[line].address,
- sizeX, &printerFonts, 2);
- y = y2;
- if (line==selEndLine) break;
- }
- PrintDC.SelectStockObject(BLACK_PEN);
- PrintDC.EndPage();
- PrintDC.EndDoc();
- }
- printerFonts.DeleteFonts();
- }
- GlobalFree(pd.m_pd.hDevMode);
- GlobalFree(pd.m_pd.hDevNames);
- cwin_ensure_screen(FALSE);
- }
- void CMainWindow::OnCut()
- {
- OnCopy();
- DeleteSelection();
- cwin_ensure_screen(FALSE);
- }
- void CMainWindow::DeleteSelection()
- {
- int p = selStartChar, q = selEndChar;
- if (betweenChar(p, caretChar, q))
- { caretChar = p;
- caretLine = selStartLine;
- // In general after a delete operation caretX will not be valid, but the
- // paint operation puts that right soon enough.
- //-- caretX = selStartX;
- }
- if (betweenChar(p, icaretChar, q))
- { icaretChar = p;
- icaretLine = selStartLine;
- // In general after a delete operation icaretX will not be valid, but the
- // paint operation puts that right soon enough.
- //-- icaretX = selStartX;
- }
- if (selStartLine==selEndLine)
- { while (q!=textLast)
- { int c = textBuffer[q];
- textBuffer[p] = (char)c;
- if (caretChar==q) caretChar = p;
- if (icaretChar==q) icaretChar = p;
- if (inputLineStart==q) inputLineStart=p;
- if (c == '\n') break;
- p = (p+1)&TEXT_MASK;
- q = (q+1)&TEXT_MASK;
- }
- if (textLast==q) textLast = p;
- if (inputLineStart==q) inputLineStart=p;
- if (caretChar==q) caretChar=p;
- if (icaretChar==q) icaretChar=p;
- // The caret will have its position re-calculated during the re-draw, and also
- // the line-length record will be adjusted then.
- InvalidateSelection(selStartLine, selStartX,
- selStartLine, clientWidth+xOffset);
- }
- else
- { int l = selStartLine, l1 = selEndLine;
- lineLast = l;
- for (;;)
- { while (q!=textLast)
- { int c = textBuffer[q];
- textBuffer[p] = (char)c;
- if (caretChar==q) caretChar=q, caretLine = l;
- if (inputLineStart==q) inputLineStart=p;
- if (icaretChar==q) icaretChar=p, icaretLine = l;
- p = (p+1)&TEXT_MASK;
- q = (q+1)&TEXT_MASK;
- if (c=='\n') break;
- }
- if (q==textLast) break;
- l1 = (l1+1)&LINE_MASK;
- q = lineBuffer[l1].address;
- l = (l+1)&LINE_MASK;
- lineBuffer[l].address = p;
- lineLast = l;
- }
- if (inputLineStart==q) inputLineStart=p;
- if (caretChar==q) caretChar=p;
- if (icaretChar==q) caretChar=p;
- textLast = p;
- LineSizes();
- // I invalidate down to the bottom of the screen. In some cases maybe I
- // count do a ScrollWindow to good effect, and the plain invalidation
- // here may cause the screen to flicker more than would be ideal.
- CRect cr(0, LineY(selStartLine), clientWidth, clientHeight);
- InvalidateRect(&cr);
- }
- selFirstChar = selEndChar = selStartChar;
- selFirstX = selEndX = selStartX;
- selFirstLine = selEndLine = selStartLine;
- // The following sequence of operations is intended to scroll the window
- // (if necessary) to make the caret visible, to get scroll bar thumbs
- // up to date (eg after a multi-line delete) etc.
- UpdateWindow();
- OnSize(SIZE_RESTORED, clientWidth, clientHeight);
- HideCaret();
- CPoint caretPos(caretX-xOffset, caretY);
- SetCaretPos(caretPos);
- ShowCaret();
- }
- void CMainWindow::OnCopy()
- {
- // Here I have to allocate some global memory and copy text out of my buffer
- // into it, making sure I insert CR/LF combinations between lines and a
- // zero byte on the end as a terminator. If the text buffer contains funny
- // characters that are used to indicate font changes etc I will just leave
- // them in there for the moment.
- if (!OpenClipboard()) return;
- if (!::EmptyClipboard()) return; // Take control of clipboard
- int l1 = selStartLine;
- int cp = selStartChar, size=1;
- for (;;)
- { while (cp!=selEndChar && textBuffer[cp]!='\n')
- { cp = (cp+1)&TEXT_MASK;
- size++;
- }
- if (cp==selEndChar) break;
- size=size+2;
- l1 = (l1+1)&LINE_MASK;
- cp = lineBuffer[l1].address;
- }
- HGLOBAL h = ::GlobalAlloc(GMEM_MOVEABLE+GMEM_DDESHARE, size);
- HGLOBAL h1 = 0;
- if (clipboardformat != 0)
- h1 = ::GlobalAlloc(GMEM_MOVEABLE+GMEM_DDESHARE, size);
- char *p = NULL, *p1 = NULL;
- if (h != NULL) p = (char *)::GlobalLock(h);
- if (h1 != NULL) p1 = (char *)::GlobalLock(h1);
- if (p != NULL)
- { l1 = selStartLine;
- cp = selStartChar;
- for (;;)
- { while (cp!=selEndChar && textBuffer[cp]!='\n')
- { int c = textBuffer[cp];
- if ((c&0xff) != CH_PROMPT && (c&0xff) != CH_ENDPROMPT) *p++ = c;
- if (p1 != NULL) *p1++ = c;
- cp = (cp+1)&TEXT_MASK;
- size++;
- }
- if (cp==selEndChar) break;
- *p++ = '\r'; *p++ = '\n'; // CR/LF combination at end of line
- if (p1 != NULL)
- { *p1++ = '\r';
- *p1++ = '\n';
- }
- l1 = (l1+1)&LINE_MASK;
- cp = lineBuffer[l1].address;
- }
- *p = 0;
- ::GlobalUnlock(h);
- if (p1 != NULL)
- { *p1 = 0;
- ::GlobalUnlock(h1);
- ::SetClipboardData(clipboardformat, h1);
- }
- ::SetClipboardData(CF_TEXT, h);
-
- }
- ::CloseClipboard();
- cwin_ensure_screen(FALSE);
- }
- void CMainWindow::OnPaste()
- {
- HGLOBAL clipboardInputHandle;
- char *w;
- ToIcaret();
- caretVisible = TRUE;
- cwin_ensure_screen(FALSE); // may jump screen to caret location
- pageLine = caretLine;
- insertMode = TRUE;
- if (!OpenClipboard()) return;
- // If I get a new PASTE operation before an earlier one has quite finished
- // I will just abandon the end of the old one without further fuss.
- if (clipboardInput != NULL) free(clipboardInput);
- // If I had managed to register my own format I will use data in that format
- // in preference to text. Otherwise I will just grab text.
- if (clipboardformat != 0)
- clipboardInputHandle = ::GetClipboardData(clipboardformat);
- else clipboardInputHandle = 0;
- if (clipboardInputHandle == 0)
- clipboardInputHandle = ::GetClipboardData(CF_TEXT);
- clipboardInput = NULL;
- if (clipboardInputHandle != 0)
- { w = (char *)::GlobalLock(clipboardInputHandle);
- if (w != NULL)
- { int n = strlen(w); // amount of data found
- clipboardInput = clipboardInputP = (char *)malloc(n+1);
- if (clipboardInput != NULL) strcpy(clipboardInput, w);
- ::GlobalUnlock(clipboardInputHandle);
- }
- }
- ::CloseClipboard();
- if (caretChar != textLast)
- { char oneLine[128];
- for (;;)
- { int i=0, c;
- while (i<120 && clipboardInput!=NULL && (c=*clipboardInputP++)!=0)
- { if (c == '\r') continue;
- oneLine[i++] = (char)c;
- if (c == '\n') break; // Do pastes one line at a time
- break; // do it one CHARACTER at a time...
- }
- InsertAtCaret(oneLine, i);
- if (c == 0) break;
- }
- if (clipboardInput != NULL) free(clipboardInput);
- clipboardInput = NULL;
- }
- cwin_ensure_screen(FALSE);
- }
- // The REINPUT request just combines a COPY and PASTE. It seems clumsy to
- // have to do both as separate actions.
- void CMainWindow::OnReInput()
- {
- OnCopy();
- OnPaste();
- }
- void CMainWindow::OnSelectAll()
- {
- selFirstChar = selStartChar = textFirst;
- selEndChar = textLast;
- selFirstX = selStartX = 0;
- selEndX = lineBuffer[lineLast].width;
- selFirstLine = selStartLine = lineFirst;
- selEndLine = lineLast;
- HideCaret();
- Invalidate();
- ShowCaret();
- }
- void CMainWindow::OnClear()
- {
- CancelSelection();
- caretChar = icaretChar = textFirst = textLast;
- caretLine = icaretLine = lineFirst = lineLast;
- caretX = icaretX = endX = 0;
- xOffset = 0;
- caretVisible = TRUE;
- caretFontWidths = windowFonts.HCourier.across;
- endFontWidths = windowFonts.HCourier.across;
- if (inputLineStart>=0) inputLineStart = textLast;
- pageLine = caretLine;
- cwin_ensure_screen(FALSE);
- }
- // void CMainWindow::OnUndo()
- // {
- // DisplayMsg("OnUndo Text");
- // }
- void CMainWindow::OnRedraw()
- {
- HideCaret();
- Invalidate();
- ShowCaret();
- }
- // I make END reset the caret to the end of the text buffer, since that
- // is an important state to be in, both with regard to input and to output.
- // HOME just scrolls the window to make the top of the text visible, but does
- // not re-position the caret. That is done because I do not generally expect
- // people to want to insert characters at the very top of the buffer.
- void CMainWindow::OnHome()
- {
- OnVScroll(SB_THUMBPOSITION, 0, NULL);
- OnHScroll(SB_THUMBPOSITION, 0, NULL);
- }
- void CMainWindow::OnEnd()
- {
- icaretLine = caretLine = lineLast;
- icaretChar = caretChar = textLast;
- icaretX = caretX = endX;
- caretFontWidths = endFontWidths;
- selRootValid = TRUE;
- OnVScroll(SB_FOR_CARET, 0, NULL);
- OnHScroll(SB_FOR_CARET, 0, NULL);
- caretVisible = TRUE;
- cwin_ensure_screen(FALSE); // makes sure caret is properly on screen
- }
- void CMainWindow::ToIcaret()
- {
- caretLine = icaretLine;
- caretChar = icaretChar;
- caretX = icaretX;
- caretFontWidths = icaretFontWidths;
- OnVScroll(SB_FOR_CARET, 0, NULL);
- OnHScroll(SB_FOR_CARET, 0, NULL);
- caretVisible = TRUE;
- cwin_ensure_screen(FALSE); // makes sure caret is properly on screen
- }
- //
- // The font arrangements are as follows - at each stage I will have a
- // master fixed-pitch font (typically "Courier New"). Whenever I change
- // that font I locate a collection of slave fonts - Roman, Roman Italic
- // and Symbol each in two sizes (one about the same as the master font and
- // another about (10/12)^2 of that size, for use in sub- and super-scripts).
- //
- // When I change fonts I will only change the ones used for display in
- // my window. The ones used when printing will be left as their original size.
- // I only permit font selection to alter the font (name) for the simple
- // fixed-pitch font that I use. But I still need to reconstruct printer
- // fonts to allow for changes there.
- void CMainWindow::OnResetFont()
- {
- HideCaret();
- CClientDC dc(this);
- // I need to find a suitable size for my default font, so I create one
- // asking for "size=0" and see what windows hands back to me.
- FontHeights fh;
- CFont *f = MakeNewFont(&dc, &fh, ANSI_CHARSET, "Courier New",
- FW_BOLD, 0, 0);
- dc.SelectStockObject(SYSTEM_FONT);
- delete f;
- // If there is a font already set up somebody must discard it...
- windowFonts.DeleteFonts();
- windowFonts.ChangeFont(&dc, fh.height, FW_BOLD, "Courier New");
- theApp.WriteProfileInt("MainWindow", "FontSize", fh.height);
- theApp.WriteProfileString("MainWindow", "FontName", "Courier New");
- theApp.WriteProfileInt("MainWindow", "FontWeight", 0);
- // scroll activity may be needed here just as it is
- // after some changes of window size.
- OnSize(SIZE_RESTORED, clientWidth, clientHeight);
- Invalidate(); // I think a total re-draw after a font change
- // makes sense, and will not worry evan if the new
- // font was identical to the old one, since after all
- // the user prodded the OK not the CANCEL button.
- ShowCaret();
- }
- void CMainWindow::OnFont()
- {
- HideCaret();
- CClientDC dc(this);
- LOGFONT logFont;
- windowFonts.Courier->GetObject(sizeof(logFont), &logFont);
- CFontDialog fontDialog(&logFont,
- CF_SCREENFONTS | CF_FIXEDPITCHONLY |
- CF_TTONLY |
- CF_ANSIONLY | CF_INITTOLOGFONTSTRUCT);
- // I will keep trying to obtain fonts from the user until I find one that
- // can be properly installed. Maybe it is unkind, but if a font as selected
- // from the dialog box fails to be created I just restart the dialog without
- // further explanation.
- for (;;)
- { int w = fontDialog.DoModal();
- if (w == IDCANCEL) break;
- else if (w != IDOK) continue;
- LOGFONT *lf = fontDialog.m_cf.lpLogFont;
- // If there is a font already set up somebody must discard it...
- windowFonts.DeleteFonts();
- // Whatever the user does I do not want either very very small fonts or
- // ones that would be so huge that their metrics could exceed 256. I apply
- // rather arbitrary cut-offs here.
- if (lf->lfHeight < -150) lf->lfHeight = -150;
- if (lf->lfHeight <= -6);
- else if (lf->lfHeight < 6) lf->lfHeight = -6;
- else if (lf->lfHeight > 150) lf->lfHeight = -150;
- windowFonts.ChangeFont(&dc, lf->lfHeight, lf->lfWeight, lf->lfFaceName);
- theApp.WriteProfileInt("MainWindow", "FontSize", lf->lfHeight);
- theApp.WriteProfileString("MainWindow", "FontName", lf->lfFaceName);
- theApp.WriteProfileInt("MainWindow", "FontWeight",
- lf->lfWeight==FW_BOLD ? 0 : 1);
- break;
- }
- // scroll activity may be needed here just as it is
- // after some changes of window size.
- OnSize(SIZE_RESTORED, clientWidth, clientHeight);
- Invalidate(); // I think a total re-draw after a font change
- // makes sense, and will not worry evan if the new
- // font was identical to the old one, since after all
- // the user prodded the OK not the CANCEL button.
- ShowCaret();
- }
- void FontArray::InitFont(CDC *dc, const char *name, int weight, int size)
- {
- // Create and install a suitable initial font - I use "Courier New"
- // and ask for a default size the very first time, and this is
- // achieved by passing down "Courier New" and "0".
- // Because Courier tends to look rather light on
- // the screen I will use a bold weight by default.
- // I really hope that the default size will be such that 80 columns will fit
- // neatly across the screen...
- FontHeights fh;
- weight = weight ? FW_NORMAL : FW_BOLD;
- CFont *f = MakeNewFont(dc, &fh, ANSI_CHARSET, name,
- weight, 0, size);
- dc->SelectStockObject(SYSTEM_FONT);
- delete f;
- ChangeFont(dc, size == 0 ? fh.height : size, weight, name);
- }
- void FontArray::DeleteFonts()
- {
- delete Courier; Courier = NULL;
- delete Roman; Roman = NULL;
- delete Bold; Bold = NULL;
- delete Italic; Italic = NULL;
- delete Symbol; Symbol = NULL;
- delete roman; roman = NULL;
- delete bold; bold = NULL;
- delete italic; italic = NULL;
- delete symbol; symbol = NULL;
- }
- // The next function is a place-holder, I guess.
- void CMainWindow::MeasureLine(int address, int *up, int *down, FontArray *fa)
- {
- CSL_IGNORE(address);
- *up = fa->HCourier.up;
- *down = fa->HCourier.down;
- }
- void FontArray::ChangeFont(CDC *dc, int size, int weight, const char *facename)
- {
- strcpy(baseFace, facename);
- boldFlag = weight;
- Courier = MakeNewFont(dc, &HCourier, ANSI_CHARSET,
- facename, weight, 0, size);
- // I choose the scale factor 1.2^2 between my main and my script font mainly
- // because that is what TeX seems to have adopted - Windows will generally
- // round the size I ask for somewhat.... I also reload the size of my
- // basic fixed-pitch font in case an inconvenient value had been handed in
- // to start with and creating the font led to rounding.
- size = HCourier.height;
- int smallSize = (100*size+72)/144;
- // These fonts have my predefined font-names and weights. They are not in fact
- // really (?) used at present.
- Roman = MakeNewFont(dc, &HRoman, ANSI_CHARSET,
- "Times New Roman", FW_NORMAL, 0, size);
- Bold = MakeNewFont(dc, &HBold, ANSI_CHARSET,
- "Times New Roman", FW_BOLD, 0, size);
- Italic = MakeNewFont(dc, &HItalic, ANSI_CHARSET,
- "Times New Roman", FW_NORMAL, 1, size);
- Symbol = MakeNewFont(dc, &HSymbol, SYMBOL_CHARSET,
- "Symbol", FW_NORMAL, 0, size);
- roman = MakeNewFont(dc, &Hroman, ANSI_CHARSET,
- "Times New Roman", FW_NORMAL, 0, smallSize);
- bold = MakeNewFont(dc, &Hbold, ANSI_CHARSET,
- "Times New Roman", FW_BOLD, 0, smallSize);
- italic = MakeNewFont(dc, &Hitalic, ANSI_CHARSET,
- "Times New Roman", FW_NORMAL, 1, smallSize);
- symbol = MakeNewFont(dc, &Hsymbol, SYMBOL_CHARSET,
- "Symbol", FW_NORMAL, 0, smallSize);
- theApp.mainWindow->LineSizes();
- ::DestroyCaret();
- theApp.mainWindow->CreateSolidCaret(
- 2*GetSystemMetrics(SM_CXBORDER), HCourier.height);
- }
- void CMainWindow::LineSizes()
- {
- int up, down, pos;
- int lineFirst = theApp.mainWindow->lineFirst;
- int lineLast = theApp.mainWindow->lineLast;
- TextLine *lineBuffer = &theApp.mainWindow->lineBuffer[0];
- MeasureLine(lineBuffer[lineFirst].address, &up, &down, &windowFonts);
- lineBuffer[lineFirst].position = 0;
- lineBuffer[lineFirst].up = up;
- lineBuffer[lineFirst].height = up+down;
- lineBuffer[lineFirst].width = 000;
- pos = up + down;
- for (int i=(lineFirst+1)&LINE_MASK;;i=(i+1)&LINE_MASK)
- { MeasureLine(lineBuffer[i].address, &up, &down, &windowFonts);
- lineBuffer[i].position = pos;
- lineBuffer[i].up = up;
- lineBuffer[i].height = up+down;
- lineBuffer[i].width = 000;
- pos += up + down;
- if (i==lineLast) break;
- }
- }
- // Now some support for a really crude and simple text-based help window.
- // The interface I provide here is intended to be such that I can have
- // alternative implementations (eg using Unix) that use a "curses" style
- // screen manager.
- // For Windows I will only support an 80 by 25 window. I guess it
- // would be easy enough to permit other sizes, except that I do not have
- // an easy answer to what should happen if the user re-sizes the window
- // while other things are going on. Hence my conservative caution - at
- // least for now!
- extern "C" {
- int LINES = 25, COLS = 80;
- // initscr() must be called once at the start of a run
- extern void initscr();
- // initkb() and resetkb() delimit regions in the code where keyboard
- // input is treated as requests to the curses window but is accepted
- // with no delay and no echo. Also mouse events can be posted during
- // this time.
- extern void initkb();
- extern void resetkb();
- extern int mouse_button; // set non-zero when user presses a button
- extern int mouse_cx; // 0 <= mouse_cx < COLS
- extern int mouse_cy; // 0 <= mouse_cy < LINES
- // refresh() is called to force the screen to be up to date
- extern void refresh();
- // endwin() hides the curses window, restoring simple text handling
- extern void endwin();
- // Move text insertion point. Origin (0,0) is top left of screen
- extern void move(int y, int x);
- // standout() and standend() delimit inverse video (or whatever) text
- extern void standout();
- extern void standend();
- // erase() clears the whole screen
- extern void erase();
- //
- // addch() and addstr() add text to the screen, advancing the cursor. I
- // view it as illegal to write beyond either right or bottom margin of the
- // screen.
- //
- extern void addch(int ch);
- extern void addstr(char *s);
- //
- // getch() reads a character from the keyboard. It does not wait for
- // a newline, and does not echo anything. Because the name getch() may be
- // in use in some C libraries in a way that could conflict I use some
- // re-naming here. If there has been a mouse-click recently then getch()
- // should return a value (0x100 + bits) where the odd bits may indicate which
- // button was pressed. In that case (mouse_cx,mouse_cy) will be the
- // character-position coordinates at which the hit was taken. Systems
- // that can not support a mouse do not have to worry about this and can always
- // return a value in the range 0..255, or EOF. On some systems getch() will
- // return 0 with no delay if there is no character available (so that
- // the application will busy-wait). On others it is entitled to wait until
- // the user presses a key. But (once again) it should not do line editing or
- // wait for an ENTER.
- //
- extern int my_getch();
- #undef getch
- #define getch() my_getch()
- }
- static BOOL helpShown = FALSE;
- static int helpChar = -1;
- void initscr()
- {
- helpShown = FALSE;
- helpChar = -1;
- }
- void initkb()
- {
- if (!helpShown)
- { erase();
- theApp.mainWindow->helpWindow->ShowWindow(SW_SHOW);
- helpShown = TRUE;
- }
- helpChar = -1;
- }
- void resetkb()
- {
- if (helpShown)
- { theApp.mainWindow->helpWindow->ShowWindow(SW_HIDE);
- helpShown = FALSE;
- }
- }
- void endwin()
- {
- resetkb();
- }
- static int helpY, helpX;
- void move(int y, int x)
- {
- helpY = y;
- helpX = x;
- }
- void erase()
- {
- helpX = helpY = 0;
- for (int y=0; y<LINES; y++)
- for (int x=0; x<COLS; x++)
- theApp.mainWindow->helpWindow->contents[y][x] = ' ';
- theApp.mainWindow->helpWindow->highline =
- theApp.mainWindow->helpWindow->highstart =
- theApp.mainWindow->helpWindow->highend = -1;
- }
- void standout()
- {
- theApp.mainWindow->helpWindow->highline = helpY;
- theApp.mainWindow->helpWindow->highstart =
- theApp.mainWindow->helpWindow->highend = helpX;
- }
- void standend()
- {
- if (theApp.mainWindow->helpWindow->highline != helpY)
- { theApp.mainWindow->helpWindow->highline = helpY;
- theApp.mainWindow->helpWindow->highstart = 0;
- }
- theApp.mainWindow->helpWindow->highend = helpX;
- }
- void addch(int ch)
- {
- if (helpX < COLS && helpY < LINES)
- { theApp.mainWindow->helpWindow->contents[helpY][helpX] = (char)ch;
- helpX++;
- }
- }
- void addstr(char *s)
- {
- while (*s != 0) addch(*s++);
- }
- int mouse_button = 0, mouse_cx = 0, mouse_cy = 0;
- void refresh()
- {
- if (!helpShown) initkb();
- theApp.mainWindow->helpWindow->Invalidate();
- }
- int my_getch()
- {
- while (helpChar < 0) cwin_poll_window_manager(TRUE);
- int ch = helpChar;
- helpChar = -1;
- return ch;
- }
- CHelpWindow::CHelpWindow()
- {
- char *menuName = "HelpMenu";
- HINSTANCE hInst = AfxFindResourceHandle(menuName, RT_MENU);
- HMENU hMenu = ::LoadMenu(hInst, menuName);
- this->CreateEx(0, mainWindowClass, "Help Window",
- WS_OVERLAPPED | WS_CAPTION,
- CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
- NULL, hMenu);
- helpFont.CreateFont(
- 0, 0, // height, width
- 0, 0, // angle of escapement, base line orientation angle
- FW_BOLD, 0, // weight, italic-flag
- 0, 0, ANSI_CHARSET, // underline, strike-out, character-set
- OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
- DEFAULT_QUALITY, DEFAULT_PITCH+FF_DONTCARE,
- "Courier New");
- for (int y=0;y<25;y++)
- for (int x=0;x<80;x++) contents[y][x] = ' ';
- highline = 5;
- highstart = 7;
- highend = 11;
- }
- BEGIN_MESSAGE_MAP(CHelpWindow, CFrameWnd)
- ON_WM_PAINT()
- ON_WM_CHAR()
- ON_WM_KEYDOWN()
- ON_WM_LBUTTONDOWN()
- ON_COMMAND(IDM_REDRAW, OnRedraw)
- // ON_COMMAND(IDM_COPY, OnCopy)
- ON_COMMAND(IDM_CLOSE, OnClose)
- END_MESSAGE_MAP()
- void CHelpWindow::OnPaint()
- {
- CPaintDC dc(this);
- dc.SelectObject(&helpFont);
- for (int i = 0; i<25; i++)
- { if (i == highline)
- { dc.TextOut(0, i*height, contents[i], highstart);
- dc.SetTextColor(theApp.mainWindow->highlightTextColour);
- dc.SetBkColor(theApp.mainWindow->highlightColour);
- dc.TextOut(highstart*width, i*height, &contents[i][highstart],
- highend-highstart);
- dc.SetTextColor(theApp.mainWindow->textColour);
- dc.SetBkColor(theApp.mainWindow->windowColour);
- dc.TextOut(highend*width, i*height, &contents[i][highend],
- 80-highend);
- }
- else dc.TextOut(0, i*height, contents[i], 80);
- }
- dc.SelectStockObject(SYSTEM_FONT);
- }
- void CHelpWindow::OnChar(UINT ch, UINT nRepCnt, UINT nFlags)
- {
- CSL_IGNORE(nRepCnt);
- CSL_IGNORE(nFlags);
- switch (ch)
- {
- //case ('O' & 0x1f):
- // OnCopy();
- // return;
- case ('L' & 0x1f):
- OnRedraw();
- return;
- default:
- helpChar = ch;
- return;
- }
- }
- void CHelpWindow::OnKeyDown(UINT ch, UINT nRepCnt, UINT nFlags)
- {
- CSL_IGNORE(ch);
- CSL_IGNORE(nRepCnt);
- CSL_IGNORE(nFlags);
- }
- void CHelpWindow::OnLButtonDown(UINT nFlags, CPoint point)
- {
- CSL_IGNORE(nFlags);
- CSL_IGNORE(point);
- }
- //void CHelpWindow::OnCopy()
- //{
- // HGLOBAL hpbmi = 0; // @@@@@@@@@
- // if (this->OpenClipboard())
- // { ::EmptyClipboard();
- // ::SetClipboardData(CF_DIB, hpbmi);
- // ::CloseClipboard();
- // }
- //}
- void CHelpWindow::OnRedraw()
- {
- Invalidate();
- }
- void CHelpWindow::OnClose()
- {
- helpChar = 'q';
- }
- // end of c_text.cpp
|