TextView.cs 191 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577
  1. #nullable enable
  2. // TextView.cs: multi-line text editing
  3. using System.Diagnostics;
  4. using System.Globalization;
  5. using System.Runtime.CompilerServices;
  6. using System.Text.Json.Serialization;
  7. using Terminal.Gui.Resources;
  8. namespace Terminal.Gui;
  9. /// <summary>
  10. /// Represents a single row/column within the <see cref="TextView"/>. Includes the glyph and the
  11. /// foreground/background colors.
  12. /// </summary>
  13. [DebuggerDisplay ("{ColorSchemeDebuggerDisplay}")]
  14. public class RuneCell : IEquatable<RuneCell>
  15. {
  16. /// <summary>The <see cref="Terminal.Gui.ColorScheme"/> color sets to draw the glyph with.</summary>
  17. [JsonConverter (typeof (ColorSchemeJsonConverter))]
  18. public ColorScheme? ColorScheme { get; set; }
  19. /// <summary>The glyph to draw.</summary>
  20. [JsonConverter (typeof (RuneJsonConverter))]
  21. public Rune Rune { get; set; }
  22. private string ColorSchemeDebuggerDisplay => ToString ();
  23. /// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
  24. /// <param name="other">An object to compare with this object.</param>
  25. /// <returns>
  26. /// <see langword="true"/> if the current object is equal to the <paramref name="other"/> parameter; otherwise,
  27. /// <see langword="false"/>.
  28. /// </returns>
  29. public bool Equals (RuneCell? other) { return other is { } && Rune.Equals (other.Rune) && ColorScheme == other.ColorScheme; }
  30. /// <summary>Returns a string that represents the current object.</summary>
  31. /// <returns>A string that represents the current object.</returns>
  32. public override string ToString ()
  33. {
  34. string colorSchemeStr = ColorScheme?.ToString () ?? "null";
  35. return $"U+{Rune.Value:X4} '{Rune.ToString ()}'; {colorSchemeStr}";
  36. }
  37. }
  38. internal class TextModel
  39. {
  40. private List<List<RuneCell>> _lines = new ();
  41. private (Point startPointToFind, Point currentPointToFind, bool found) _toFind;
  42. /// <summary>The number of text lines in the model</summary>
  43. public int Count => _lines.Count;
  44. public string? FilePath { get; set; }
  45. /// <summary>Adds a line to the model at the specified position.</summary>
  46. /// <param name="pos">Line number where the line will be inserted.</param>
  47. /// <param name="cells">The line of text and color, as a List of RuneCell.</param>
  48. public void AddLine (int pos, List<RuneCell> cells) { _lines.Insert (pos, cells); }
  49. public bool CloseFile ()
  50. {
  51. if (FilePath is null)
  52. {
  53. throw new ArgumentNullException (nameof (FilePath));
  54. }
  55. FilePath = null;
  56. _lines = new List<List<RuneCell>> ();
  57. return true;
  58. }
  59. public List<List<RuneCell>> GetAllLines () { return _lines; }
  60. /// <summary>Returns the specified line as a List of Rune</summary>
  61. /// <returns>The line.</returns>
  62. /// <param name="line">Line number to retrieve.</param>
  63. public List<RuneCell> GetLine (int line)
  64. {
  65. if (_lines.Count > 0)
  66. {
  67. if (line < Count)
  68. {
  69. return _lines [line];
  70. }
  71. return _lines [Count - 1];
  72. }
  73. _lines.Add (new List<RuneCell> ());
  74. return _lines [0];
  75. }
  76. /// <summary>Returns the maximum line length of the visible lines.</summary>
  77. /// <param name="first">The first line.</param>
  78. /// <param name="last">The last line.</param>
  79. /// <param name="tabWidth">The tab width.</param>
  80. public int GetMaxVisibleLine (int first, int last, int tabWidth)
  81. {
  82. var maxLength = 0;
  83. last = last < _lines.Count ? last : _lines.Count;
  84. for (int i = first; i < last; i++)
  85. {
  86. List<RuneCell> line = GetLine (i);
  87. int tabSum = line.Sum (c => c.Rune.Value == '\t' ? Math.Max (tabWidth - 1, 0) : 0);
  88. int l = line.Count + tabSum;
  89. if (l > maxLength)
  90. {
  91. maxLength = l;
  92. }
  93. }
  94. return maxLength;
  95. }
  96. public event EventHandler? LinesLoaded;
  97. public bool LoadFile (string file)
  98. {
  99. FilePath = file ?? throw new ArgumentNullException (nameof (file));
  100. using (FileStream stream = File.OpenRead (file))
  101. {
  102. LoadStream (stream);
  103. return true;
  104. }
  105. }
  106. public void LoadListRuneCells (List<List<RuneCell>> cellsList, ColorScheme? colorScheme)
  107. {
  108. _lines = cellsList;
  109. SetColorSchemes (colorScheme);
  110. OnLinesLoaded ();
  111. }
  112. public void LoadRuneCells (List<RuneCell> cells, ColorScheme? colorScheme)
  113. {
  114. _lines = ToRuneCells (cells);
  115. SetColorSchemes (colorScheme);
  116. OnLinesLoaded ();
  117. }
  118. public void LoadStream (Stream input)
  119. {
  120. if (input is null)
  121. {
  122. throw new ArgumentNullException (nameof (input));
  123. }
  124. _lines = new List<List<RuneCell>> ();
  125. var buff = new BufferedStream (input);
  126. int v;
  127. List<byte> line = new ();
  128. var wasNewLine = false;
  129. while ((v = buff.ReadByte ()) != -1)
  130. {
  131. if (v == 13)
  132. {
  133. continue;
  134. }
  135. if (v == 10)
  136. {
  137. Append (line);
  138. line.Clear ();
  139. wasNewLine = true;
  140. continue;
  141. }
  142. line.Add ((byte)v);
  143. wasNewLine = false;
  144. }
  145. if (line.Count > 0 || wasNewLine)
  146. {
  147. Append (line);
  148. }
  149. buff.Dispose ();
  150. OnLinesLoaded ();
  151. }
  152. public void LoadString (string content)
  153. {
  154. _lines = StringToLinesOfRuneCells (content);
  155. OnLinesLoaded ();
  156. }
  157. /// <summary>Removes the line at the specified position</summary>
  158. /// <param name="pos">Position.</param>
  159. public void RemoveLine (int pos)
  160. {
  161. if (_lines.Count > 0)
  162. {
  163. if (_lines.Count == 1 && _lines [0].Count == 0)
  164. {
  165. return;
  166. }
  167. _lines.RemoveAt (pos);
  168. }
  169. }
  170. public void ReplaceLine (int pos, List<RuneCell> runes)
  171. {
  172. if (_lines.Count > 0 && pos < _lines.Count)
  173. {
  174. _lines [pos] = new List<RuneCell> (runes);
  175. }
  176. else if (_lines.Count == 0 || (_lines.Count > 0 && pos >= _lines.Count))
  177. {
  178. _lines.Add (runes);
  179. }
  180. }
  181. // Splits a string into a List that contains a List<RuneCell> for each line
  182. public static List<List<RuneCell>> StringToLinesOfRuneCells (string content, ColorScheme? colorScheme = null)
  183. {
  184. List<RuneCell> cells = content.EnumerateRunes ()
  185. .Select (x => new RuneCell { Rune = x, ColorScheme = colorScheme })
  186. .ToList ();
  187. return SplitNewLines (cells);
  188. }
  189. /// <summary>Converts the string into a <see cref="List{RuneCell}"/>.</summary>
  190. /// <param name="str">The string to convert.</param>
  191. /// <param name="colorScheme">The <see cref="ColorScheme"/> to use.</param>
  192. /// <returns></returns>
  193. public static List<RuneCell> ToRuneCellList (string str, ColorScheme? colorScheme = null)
  194. {
  195. List<RuneCell> cells = new ();
  196. foreach (Rune rune in str.EnumerateRunes ())
  197. {
  198. cells.Add (new RuneCell { Rune = rune, ColorScheme = colorScheme });
  199. }
  200. return cells;
  201. }
  202. public override string ToString ()
  203. {
  204. var sb = new StringBuilder ();
  205. for (var i = 0; i < _lines.Count; i++)
  206. {
  207. sb.Append (ToString (_lines [i]));
  208. if (i + 1 < _lines.Count)
  209. {
  210. sb.AppendLine ();
  211. }
  212. }
  213. return sb.ToString ();
  214. }
  215. /// <summary>Converts a <see cref="RuneCell"/> generic collection into a string.</summary>
  216. /// <param name="cells">The enumerable cell to convert.</param>
  217. /// <returns></returns>
  218. public static string ToString (IEnumerable<RuneCell> cells)
  219. {
  220. var str = string.Empty;
  221. foreach (RuneCell cell in cells)
  222. {
  223. str += cell.Rune.ToString ();
  224. }
  225. return str;
  226. }
  227. public (int col, int row)? WordBackward (int fromCol, int fromRow)
  228. {
  229. if (fromRow == 0 && fromCol == 0)
  230. {
  231. return null;
  232. }
  233. int col = Math.Max (fromCol - 1, 0);
  234. int row = fromRow;
  235. try
  236. {
  237. RuneCell? cell = RuneAt (col, row);
  238. Rune rune;
  239. if (cell is { })
  240. {
  241. rune = cell.Rune;
  242. }
  243. else
  244. {
  245. if (col > 0)
  246. {
  247. return (col, row);
  248. }
  249. if (col == 0 && row > 0)
  250. {
  251. row--;
  252. List<RuneCell> line = GetLine (row);
  253. return (line.Count, row);
  254. }
  255. return null;
  256. }
  257. RuneType runeType = GetRuneType (rune);
  258. int lastValidCol = IsSameRuneType (rune, runeType) && (Rune.IsLetterOrDigit (rune) || Rune.IsPunctuation (rune) || Rune.IsSymbol (rune))
  259. ? col
  260. : -1;
  261. void ProcMovePrev (ref int nCol, ref int nRow, Rune nRune)
  262. {
  263. if (Rune.IsWhiteSpace (nRune))
  264. {
  265. while (MovePrev (ref nCol, ref nRow, out nRune))
  266. {
  267. if (Rune.IsLetterOrDigit (nRune) || Rune.IsPunctuation (nRune) || Rune.IsSymbol (nRune))
  268. {
  269. lastValidCol = nCol;
  270. if (runeType == RuneType.IsWhiteSpace || runeType == RuneType.IsUnknow)
  271. {
  272. runeType = GetRuneType (nRune);
  273. }
  274. break;
  275. }
  276. }
  277. if (nRow != fromRow && (Rune.IsLetterOrDigit (nRune) || Rune.IsPunctuation (nRune) || Rune.IsSymbol (nRune)))
  278. {
  279. if (lastValidCol > -1)
  280. {
  281. nCol = lastValidCol;
  282. }
  283. return;
  284. }
  285. while (MovePrev (ref nCol, ref nRow, out nRune))
  286. {
  287. if (!Rune.IsLetterOrDigit (nRune) && !Rune.IsPunctuation (nRune) && !Rune.IsSymbol (nRune))
  288. {
  289. break;
  290. }
  291. if (nRow != fromRow)
  292. {
  293. break;
  294. }
  295. lastValidCol =
  296. (IsSameRuneType (nRune, runeType) && Rune.IsLetterOrDigit (nRune)) || Rune.IsPunctuation (nRune) || Rune.IsSymbol (nRune)
  297. ? nCol
  298. : lastValidCol;
  299. }
  300. if (lastValidCol > -1)
  301. {
  302. nCol = lastValidCol;
  303. nRow = fromRow;
  304. }
  305. }
  306. else
  307. {
  308. if (!MovePrev (ref nCol, ref nRow, out nRune))
  309. {
  310. return;
  311. }
  312. List<RuneCell> line = GetLine (nRow);
  313. if (nCol == 0
  314. && nRow == fromRow
  315. && (Rune.IsLetterOrDigit (line [0].Rune) || Rune.IsPunctuation (line [0].Rune) || Rune.IsSymbol (line [0].Rune)))
  316. {
  317. return;
  318. }
  319. lastValidCol =
  320. (IsSameRuneType (nRune, runeType) && Rune.IsLetterOrDigit (nRune)) || Rune.IsPunctuation (nRune) || Rune.IsSymbol (nRune)
  321. ? nCol
  322. : lastValidCol;
  323. if (lastValidCol > -1 && Rune.IsWhiteSpace (nRune))
  324. {
  325. nCol = lastValidCol;
  326. return;
  327. }
  328. if (fromRow != nRow)
  329. {
  330. nCol = line.Count;
  331. return;
  332. }
  333. ProcMovePrev (ref nCol, ref nRow, nRune);
  334. }
  335. }
  336. ProcMovePrev (ref col, ref row, rune);
  337. if (fromCol != col || fromRow != row)
  338. {
  339. return (col, row);
  340. }
  341. return null;
  342. }
  343. catch (Exception)
  344. {
  345. return null;
  346. }
  347. }
  348. public (int col, int row)? WordForward (int fromCol, int fromRow)
  349. {
  350. if (fromRow == _lines.Count - 1 && fromCol == GetLine (_lines.Count - 1).Count)
  351. {
  352. return null;
  353. }
  354. int col = fromCol;
  355. int row = fromRow;
  356. try
  357. {
  358. Rune rune = RuneAt (col, row).Rune;
  359. RuneType runeType = GetRuneType (rune);
  360. int lastValidCol = IsSameRuneType (rune, runeType) && (Rune.IsLetterOrDigit (rune) || Rune.IsPunctuation (rune) || Rune.IsSymbol (rune))
  361. ? col
  362. : -1;
  363. void ProcMoveNext (ref int nCol, ref int nRow, Rune nRune)
  364. {
  365. if (Rune.IsWhiteSpace (nRune))
  366. {
  367. while (MoveNext (ref nCol, ref nRow, out nRune))
  368. {
  369. if (Rune.IsLetterOrDigit (nRune) || Rune.IsPunctuation (nRune) || Rune.IsSymbol (nRune))
  370. {
  371. lastValidCol = nCol;
  372. return;
  373. }
  374. }
  375. if (nRow != fromRow && (Rune.IsLetterOrDigit (nRune) || Rune.IsPunctuation (nRune) || Rune.IsSymbol (nRune)))
  376. {
  377. if (lastValidCol > -1)
  378. {
  379. nCol = lastValidCol;
  380. }
  381. return;
  382. }
  383. while (MoveNext (ref nCol, ref nRow, out nRune))
  384. {
  385. if (!Rune.IsLetterOrDigit (nRune) && !Rune.IsPunctuation (nRune) && !Rune.IsSymbol (nRune))
  386. {
  387. break;
  388. }
  389. if (nRow != fromRow)
  390. {
  391. break;
  392. }
  393. lastValidCol =
  394. (IsSameRuneType (nRune, runeType) && Rune.IsLetterOrDigit (nRune)) || Rune.IsPunctuation (nRune) || Rune.IsSymbol (nRune)
  395. ? nCol
  396. : lastValidCol;
  397. }
  398. if (lastValidCol > -1)
  399. {
  400. nCol = lastValidCol;
  401. nRow = fromRow;
  402. }
  403. }
  404. else
  405. {
  406. if (!MoveNext (ref nCol, ref nRow, out nRune))
  407. {
  408. return;
  409. }
  410. if (!IsSameRuneType (nRune, runeType) && !Rune.IsWhiteSpace (nRune))
  411. {
  412. return;
  413. }
  414. List<RuneCell> line = GetLine (nRow);
  415. if (nCol == line.Count
  416. && nRow == fromRow
  417. && (Rune.IsLetterOrDigit (line [0].Rune) || Rune.IsPunctuation (line [0].Rune) || Rune.IsSymbol (line [0].Rune)))
  418. {
  419. return;
  420. }
  421. lastValidCol =
  422. (IsSameRuneType (nRune, runeType) && Rune.IsLetterOrDigit (nRune)) || Rune.IsPunctuation (nRune) || Rune.IsSymbol (nRune)
  423. ? nCol
  424. : lastValidCol;
  425. if (fromRow != nRow)
  426. {
  427. nCol = 0;
  428. return;
  429. }
  430. ProcMoveNext (ref nCol, ref nRow, nRune);
  431. }
  432. }
  433. ProcMoveNext (ref col, ref row, rune);
  434. if (fromCol != col || fromRow != row)
  435. {
  436. return (col, row);
  437. }
  438. return null;
  439. }
  440. catch (Exception)
  441. {
  442. return null;
  443. }
  444. }
  445. internal static int CalculateLeftColumn (List<RuneCell> t, int start, int end, int width, int tabWidth = 0)
  446. {
  447. List<Rune> runes = new ();
  448. foreach (RuneCell cell in t)
  449. {
  450. runes.Add (cell.Rune);
  451. }
  452. return CalculateLeftColumn (runes, start, end, width, tabWidth);
  453. }
  454. // Returns the left column in a range of the string.
  455. internal static int CalculateLeftColumn (List<Rune> t, int start, int end, int width, int tabWidth = 0)
  456. {
  457. if (t is null || t.Count == 0)
  458. {
  459. return 0;
  460. }
  461. var size = 0;
  462. int tcount = end > t.Count - 1 ? t.Count - 1 : end;
  463. var col = 0;
  464. for (int i = tcount; i >= 0; i--)
  465. {
  466. Rune rune = t [i];
  467. size += rune.GetColumns ();
  468. if (rune.Value == '\t')
  469. {
  470. size += tabWidth + 1;
  471. }
  472. if (size > width)
  473. {
  474. if (col + width == end)
  475. {
  476. col++;
  477. }
  478. break;
  479. }
  480. if ((end < t.Count && col > 0 && start < end && col == start) || end - col == width - 1)
  481. {
  482. break;
  483. }
  484. col = i;
  485. }
  486. return col;
  487. }
  488. internal static (int size, int length) DisplaySize (
  489. List<RuneCell> t,
  490. int start = -1,
  491. int end = -1,
  492. bool checkNextRune = true,
  493. int tabWidth = 0
  494. )
  495. {
  496. List<Rune> runes = new ();
  497. foreach (RuneCell cell in t)
  498. {
  499. runes.Add (cell.Rune);
  500. }
  501. return DisplaySize (runes, start, end, checkNextRune, tabWidth);
  502. }
  503. // Returns the size and length in a range of the string.
  504. internal static (int size, int length) DisplaySize (
  505. List<Rune> t,
  506. int start = -1,
  507. int end = -1,
  508. bool checkNextRune = true,
  509. int tabWidth = 0
  510. )
  511. {
  512. if (t is null || t.Count == 0)
  513. {
  514. return (0, 0);
  515. }
  516. var size = 0;
  517. var len = 0;
  518. int tcount = end == -1 ? t.Count :
  519. end > t.Count ? t.Count : end;
  520. int i = start == -1 ? 0 : start;
  521. for (; i < tcount; i++)
  522. {
  523. Rune rune = t [i];
  524. size += rune.GetColumns ();
  525. len += rune.GetEncodingLength (Encoding.Unicode);
  526. if (rune.Value == '\t')
  527. {
  528. size += tabWidth + 1;
  529. len += tabWidth - 1;
  530. }
  531. if (checkNextRune && i == tcount - 1 && t.Count > tcount && IsWideRune (t [i + 1], tabWidth, out int s, out int l))
  532. {
  533. size += s;
  534. len += l;
  535. }
  536. }
  537. bool IsWideRune (Rune r, int tWidth, out int s, out int l)
  538. {
  539. s = r.GetColumns ();
  540. l = r.GetEncodingLength ();
  541. if (r.Value == '\t')
  542. {
  543. s += tWidth + 1;
  544. l += tWidth - 1;
  545. }
  546. return s > 1;
  547. }
  548. return (size, len);
  549. }
  550. internal (Point current, bool found) FindNextText (
  551. string text,
  552. out bool gaveFullTurn,
  553. bool matchCase = false,
  554. bool matchWholeWord = false
  555. )
  556. {
  557. if (text is null || _lines.Count == 0)
  558. {
  559. gaveFullTurn = false;
  560. return (Point.Empty, false);
  561. }
  562. if (_toFind.found)
  563. {
  564. _toFind.currentPointToFind.X++;
  565. }
  566. (Point current, bool found) foundPos = GetFoundNextTextPoint (
  567. text,
  568. _lines.Count,
  569. matchCase,
  570. matchWholeWord,
  571. _toFind.currentPointToFind
  572. );
  573. if (!foundPos.found && _toFind.currentPointToFind != _toFind.startPointToFind)
  574. {
  575. foundPos = GetFoundNextTextPoint (
  576. text,
  577. _toFind.startPointToFind.Y + 1,
  578. matchCase,
  579. matchWholeWord,
  580. Point.Empty
  581. );
  582. }
  583. gaveFullTurn = ApplyToFind (foundPos);
  584. return foundPos;
  585. }
  586. internal (Point current, bool found) FindPreviousText (
  587. string text,
  588. out bool gaveFullTurn,
  589. bool matchCase = false,
  590. bool matchWholeWord = false
  591. )
  592. {
  593. if (text is null || _lines.Count == 0)
  594. {
  595. gaveFullTurn = false;
  596. return (Point.Empty, false);
  597. }
  598. if (_toFind.found)
  599. {
  600. _toFind.currentPointToFind.X++;
  601. }
  602. int linesCount = _toFind.currentPointToFind.IsEmpty ? _lines.Count - 1 : _toFind.currentPointToFind.Y;
  603. (Point current, bool found) foundPos = GetFoundPreviousTextPoint (
  604. text,
  605. linesCount,
  606. matchCase,
  607. matchWholeWord,
  608. _toFind.currentPointToFind
  609. );
  610. if (!foundPos.found && _toFind.currentPointToFind != _toFind.startPointToFind)
  611. {
  612. foundPos = GetFoundPreviousTextPoint (
  613. text,
  614. _lines.Count - 1,
  615. matchCase,
  616. matchWholeWord,
  617. new Point (_lines [_lines.Count - 1].Count, _lines.Count)
  618. );
  619. }
  620. gaveFullTurn = ApplyToFind (foundPos);
  621. return foundPos;
  622. }
  623. internal static int GetColFromX (List<RuneCell> t, int start, int x, int tabWidth = 0)
  624. {
  625. List<Rune> runes = new ();
  626. foreach (RuneCell cell in t)
  627. {
  628. runes.Add (cell.Rune);
  629. }
  630. return GetColFromX (runes, start, x, tabWidth);
  631. }
  632. internal static int GetColFromX (List<Rune> t, int start, int x, int tabWidth = 0)
  633. {
  634. if (x < 0)
  635. {
  636. return x;
  637. }
  638. int size = start;
  639. int pX = x + start;
  640. for (int i = start; i < t.Count; i++)
  641. {
  642. Rune r = t [i];
  643. size += r.GetColumns ();
  644. if (r.Value == '\t')
  645. {
  646. size += tabWidth + 1;
  647. }
  648. if (i == pX || size > pX)
  649. {
  650. return i - start;
  651. }
  652. }
  653. return t.Count - start;
  654. }
  655. internal (Point current, bool found) ReplaceAllText (
  656. string text,
  657. bool matchCase = false,
  658. bool matchWholeWord = false,
  659. string? textToReplace = null
  660. )
  661. {
  662. var found = false;
  663. var pos = Point.Empty;
  664. for (var i = 0; i < _lines.Count; i++)
  665. {
  666. List<RuneCell> x = _lines [i];
  667. string txt = GetText (x);
  668. string matchText = !matchCase ? text.ToUpper () : text;
  669. int col = txt.IndexOf (matchText);
  670. while (col > -1)
  671. {
  672. if (matchWholeWord && !MatchWholeWord (txt, matchText, col))
  673. {
  674. if (col + 1 > txt.Length)
  675. {
  676. break;
  677. }
  678. col = txt.IndexOf (matchText, col + 1);
  679. continue;
  680. }
  681. if (col > -1)
  682. {
  683. if (!found)
  684. {
  685. found = true;
  686. }
  687. _lines [i] = ToRuneCellList (ReplaceText (x, textToReplace!, matchText, col));
  688. x = _lines [i];
  689. txt = GetText (x);
  690. pos = new Point (col, i);
  691. col += textToReplace!.Length - matchText.Length;
  692. }
  693. if (col < 0 || col + 1 > txt.Length)
  694. {
  695. break;
  696. }
  697. col = txt.IndexOf (matchText, col + 1);
  698. }
  699. }
  700. string GetText (List<RuneCell> x)
  701. {
  702. string txt = ToString (x);
  703. if (!matchCase)
  704. {
  705. txt = txt.ToUpper ();
  706. }
  707. return txt;
  708. }
  709. return (pos, found);
  710. }
  711. /// <summary>Redefine column and line tracking.</summary>
  712. /// <param name="point">Contains the column and line.</param>
  713. internal void ResetContinuousFind (Point point)
  714. {
  715. _toFind.startPointToFind = _toFind.currentPointToFind = point;
  716. _toFind.found = false;
  717. }
  718. internal static bool SetCol (ref int col, int width, int cols)
  719. {
  720. if (col + cols <= width)
  721. {
  722. col += cols;
  723. return true;
  724. }
  725. return false;
  726. }
  727. // Turns the string into cells, this does not split the
  728. // contents on a newline if it is present.
  729. internal static List<RuneCell> StringToRuneCells (string str, ColorScheme? colorScheme = null)
  730. {
  731. List<RuneCell> cells = new ();
  732. foreach (Rune rune in str.ToRunes ())
  733. {
  734. cells.Add (new RuneCell { Rune = rune, ColorScheme = colorScheme });
  735. }
  736. return cells;
  737. }
  738. internal static List<RuneCell> ToRuneCells (IEnumerable<Rune> runes, ColorScheme? colorScheme = null)
  739. {
  740. List<RuneCell> cells = new ();
  741. foreach (Rune rune in runes)
  742. {
  743. cells.Add (new RuneCell { Rune = rune, ColorScheme = colorScheme });
  744. }
  745. return cells;
  746. }
  747. private void Append (List<byte> line)
  748. {
  749. var str = StringExtensions.ToString (line.ToArray ());
  750. _lines.Add (StringToRuneCells (str));
  751. }
  752. private bool ApplyToFind ((Point current, bool found) foundPos)
  753. {
  754. var gaveFullTurn = false;
  755. if (foundPos.found)
  756. {
  757. _toFind.currentPointToFind = foundPos.current;
  758. if (_toFind.found && _toFind.currentPointToFind == _toFind.startPointToFind)
  759. {
  760. gaveFullTurn = true;
  761. }
  762. if (!_toFind.found)
  763. {
  764. _toFind.startPointToFind = _toFind.currentPointToFind = foundPos.current;
  765. _toFind.found = foundPos.found;
  766. }
  767. }
  768. return gaveFullTurn;
  769. }
  770. private (Point current, bool found) GetFoundNextTextPoint (
  771. string text,
  772. int linesCount,
  773. bool matchCase,
  774. bool matchWholeWord,
  775. Point start
  776. )
  777. {
  778. for (int i = start.Y; i < linesCount; i++)
  779. {
  780. List<RuneCell> x = _lines [i];
  781. string txt = ToString (x);
  782. if (!matchCase)
  783. {
  784. txt = txt.ToUpper ();
  785. }
  786. string matchText = !matchCase ? text.ToUpper () : text;
  787. int col = txt.IndexOf (matchText, Math.Min (start.X, txt.Length));
  788. if (col > -1 && matchWholeWord && !MatchWholeWord (txt, matchText, col))
  789. {
  790. continue;
  791. }
  792. if (col > -1 && ((i == start.Y && col >= start.X) || i > start.Y) && txt.Contains (matchText))
  793. {
  794. return (new Point (col, i), true);
  795. }
  796. if (col == -1 && start.X > 0)
  797. {
  798. start.X = 0;
  799. }
  800. }
  801. return (Point.Empty, false);
  802. }
  803. private (Point current, bool found) GetFoundPreviousTextPoint (
  804. string text,
  805. int linesCount,
  806. bool matchCase,
  807. bool matchWholeWord,
  808. Point start
  809. )
  810. {
  811. for (int i = linesCount; i >= 0; i--)
  812. {
  813. List<RuneCell> x = _lines [i];
  814. string txt = ToString (x);
  815. if (!matchCase)
  816. {
  817. txt = txt.ToUpper ();
  818. }
  819. if (start.Y != i)
  820. {
  821. start.X = Math.Max (x.Count - 1, 0);
  822. }
  823. string matchText = !matchCase ? text.ToUpper () : text;
  824. int col = txt.LastIndexOf (matchText, _toFind.found ? start.X - 1 : start.X);
  825. if (col > -1 && matchWholeWord && !MatchWholeWord (txt, matchText, col))
  826. {
  827. continue;
  828. }
  829. if (col > -1 && ((i <= linesCount && col <= start.X) || i < start.Y) && txt.Contains (matchText))
  830. {
  831. return (new Point (col, i), true);
  832. }
  833. }
  834. return (Point.Empty, false);
  835. }
  836. private RuneType GetRuneType (Rune rune)
  837. {
  838. if (Rune.IsSymbol (rune))
  839. {
  840. return RuneType.IsSymbol;
  841. }
  842. if (Rune.IsWhiteSpace (rune))
  843. {
  844. return RuneType.IsWhiteSpace;
  845. }
  846. if (Rune.IsLetterOrDigit (rune))
  847. {
  848. return RuneType.IsLetterOrDigit;
  849. }
  850. if (Rune.IsPunctuation (rune))
  851. {
  852. return RuneType.IsPunctuation;
  853. }
  854. return RuneType.IsUnknow;
  855. }
  856. private bool IsSameRuneType (Rune newRune, RuneType runeType)
  857. {
  858. RuneType rt = GetRuneType (newRune);
  859. return rt == runeType;
  860. }
  861. private bool MatchWholeWord (string source, string matchText, int index = 0)
  862. {
  863. if (string.IsNullOrEmpty (source) || string.IsNullOrEmpty (matchText))
  864. {
  865. return false;
  866. }
  867. string txt = matchText.Trim ();
  868. int start = index > 0 ? index - 1 : 0;
  869. int end = index + txt.Length;
  870. if ((start == 0 || Rune.IsWhiteSpace ((Rune)source [start])) && (end == source.Length || Rune.IsWhiteSpace ((Rune)source [end])))
  871. {
  872. return true;
  873. }
  874. return false;
  875. }
  876. private bool MoveNext (ref int col, ref int row, out Rune rune)
  877. {
  878. List<RuneCell> line = GetLine (row);
  879. if (col + 1 < line.Count)
  880. {
  881. col++;
  882. rune = line [col].Rune;
  883. if (col + 1 == line.Count && !Rune.IsLetterOrDigit (rune) && !Rune.IsWhiteSpace (line [col - 1].Rune))
  884. {
  885. col++;
  886. }
  887. return true;
  888. }
  889. if (col + 1 == line.Count)
  890. {
  891. col++;
  892. }
  893. while (row + 1 < Count)
  894. {
  895. col = 0;
  896. row++;
  897. line = GetLine (row);
  898. if (line.Count > 0)
  899. {
  900. rune = line [0].Rune;
  901. return true;
  902. }
  903. }
  904. rune = default (Rune);
  905. return false;
  906. }
  907. private bool MovePrev (ref int col, ref int row, out Rune rune)
  908. {
  909. List<RuneCell> line = GetLine (row);
  910. if (col > 0)
  911. {
  912. col--;
  913. rune = line [col].Rune;
  914. return true;
  915. }
  916. if (row == 0)
  917. {
  918. rune = default (Rune);
  919. return false;
  920. }
  921. while (row > 0)
  922. {
  923. row--;
  924. line = GetLine (row);
  925. col = line.Count - 1;
  926. if (col >= 0)
  927. {
  928. rune = line [col].Rune;
  929. return true;
  930. }
  931. }
  932. rune = default (Rune);
  933. return false;
  934. }
  935. private void OnLinesLoaded () { LinesLoaded?.Invoke (this, EventArgs.Empty); }
  936. private string ReplaceText (List<RuneCell> source, string textToReplace, string matchText, int col)
  937. {
  938. string origTxt = ToString (source);
  939. (_, int len) = DisplaySize (source, 0, col, false);
  940. (_, int len2) = DisplaySize (source, col, col + matchText.Length, false);
  941. (_, int len3) = DisplaySize (source, col + matchText.Length, origTxt.GetRuneCount (), false);
  942. return origTxt [..len] + textToReplace + origTxt.Substring (len + len2, len3);
  943. }
  944. private RuneCell RuneAt (int col, int row)
  945. {
  946. List<RuneCell> line = GetLine (row);
  947. if (line.Count > 0)
  948. {
  949. return line [col > line.Count - 1 ? line.Count - 1 : col];
  950. }
  951. return default (RuneCell)!;
  952. }
  953. private void SetColorSchemes (ColorScheme? colorScheme)
  954. {
  955. foreach (List<RuneCell> line in _lines)
  956. {
  957. foreach (RuneCell cell in line)
  958. {
  959. cell.ColorScheme ??= colorScheme;
  960. }
  961. }
  962. }
  963. private static List<List<RuneCell>> SplitNewLines (List<RuneCell> cells)
  964. {
  965. List<List<RuneCell>> lines = new ();
  966. int start = 0, i = 0;
  967. var hasCR = false;
  968. // ASCII code 13 = Carriage Return.
  969. // ASCII code 10 = Line Feed.
  970. for (; i < cells.Count; i++)
  971. {
  972. if (cells [i].Rune.Value == 13)
  973. {
  974. hasCR = true;
  975. continue;
  976. }
  977. if (cells [i].Rune.Value == 10)
  978. {
  979. if (i - start > 0)
  980. {
  981. lines.Add (cells.GetRange (start, hasCR ? i - 1 - start : i - start));
  982. }
  983. else
  984. {
  985. lines.Add (StringToRuneCells (string.Empty));
  986. }
  987. start = i + 1;
  988. hasCR = false;
  989. }
  990. }
  991. if (i - start >= 0)
  992. {
  993. lines.Add (cells.GetRange (start, i - start));
  994. }
  995. return lines;
  996. }
  997. private static List<List<RuneCell>> ToRuneCells (List<RuneCell> cells) { return SplitNewLines (cells); }
  998. private enum RuneType
  999. {
  1000. IsSymbol,
  1001. IsWhiteSpace,
  1002. IsLetterOrDigit,
  1003. IsPunctuation,
  1004. IsUnknow
  1005. }
  1006. }
  1007. internal partial class HistoryText
  1008. {
  1009. public enum LineStatus
  1010. {
  1011. Original,
  1012. Replaced,
  1013. Removed,
  1014. Added
  1015. }
  1016. private readonly List<HistoryTextItem> _historyTextItems = new ();
  1017. private int _idxHistoryText = -1;
  1018. private string? _originalText;
  1019. public bool HasHistoryChanges => _idxHistoryText > -1;
  1020. public bool IsFromHistory { get; private set; }
  1021. public void Add (List<List<RuneCell>> lines, Point curPos, LineStatus lineStatus = LineStatus.Original)
  1022. {
  1023. if (lineStatus == LineStatus.Original && _historyTextItems.Count > 0 && _historyTextItems.Last ().LineStatus == LineStatus.Original)
  1024. {
  1025. return;
  1026. }
  1027. if (lineStatus == LineStatus.Replaced && _historyTextItems.Count > 0 && _historyTextItems.Last ().LineStatus == LineStatus.Replaced)
  1028. {
  1029. return;
  1030. }
  1031. if (_historyTextItems.Count == 0 && lineStatus != LineStatus.Original)
  1032. {
  1033. throw new ArgumentException ("The first item must be the original.");
  1034. }
  1035. if (_idxHistoryText >= 0 && _idxHistoryText + 1 < _historyTextItems.Count)
  1036. {
  1037. _historyTextItems.RemoveRange (
  1038. _idxHistoryText + 1,
  1039. _historyTextItems.Count - _idxHistoryText - 1
  1040. );
  1041. }
  1042. _historyTextItems.Add (new HistoryTextItem (lines, curPos, lineStatus));
  1043. _idxHistoryText++;
  1044. }
  1045. public event EventHandler<HistoryTextItem>? ChangeText;
  1046. public void Clear (string text)
  1047. {
  1048. _historyTextItems.Clear ();
  1049. _idxHistoryText = -1;
  1050. _originalText = text;
  1051. OnChangeText (null);
  1052. }
  1053. public bool IsDirty (string text) { return _originalText != text; }
  1054. public void Redo ()
  1055. {
  1056. if (_historyTextItems?.Count > 0 && _idxHistoryText < _historyTextItems.Count - 1)
  1057. {
  1058. IsFromHistory = true;
  1059. _idxHistoryText++;
  1060. var historyTextItem = new HistoryTextItem (_historyTextItems [_idxHistoryText]) { IsUndoing = false };
  1061. ProcessChanges (ref historyTextItem);
  1062. IsFromHistory = false;
  1063. }
  1064. }
  1065. public void ReplaceLast (List<List<RuneCell>> lines, Point curPos, LineStatus lineStatus)
  1066. {
  1067. HistoryTextItem? found = _historyTextItems.FindLast (x => x.LineStatus == lineStatus);
  1068. if (found is { })
  1069. {
  1070. found.Lines = lines;
  1071. found.CursorPosition = curPos;
  1072. }
  1073. }
  1074. public void Undo ()
  1075. {
  1076. if (_historyTextItems?.Count > 0 && _idxHistoryText > 0)
  1077. {
  1078. IsFromHistory = true;
  1079. _idxHistoryText--;
  1080. var historyTextItem = new HistoryTextItem (_historyTextItems [_idxHistoryText]) { IsUndoing = true };
  1081. ProcessChanges (ref historyTextItem);
  1082. IsFromHistory = false;
  1083. }
  1084. }
  1085. private void OnChangeText (HistoryTextItem? lines) { ChangeText?.Invoke (this, lines!); }
  1086. private void ProcessChanges (ref HistoryTextItem historyTextItem)
  1087. {
  1088. if (historyTextItem.IsUndoing)
  1089. {
  1090. if (_idxHistoryText - 1 > -1
  1091. && (_historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Added
  1092. || _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Removed
  1093. || (historyTextItem.LineStatus == LineStatus.Replaced && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Original)))
  1094. {
  1095. _idxHistoryText--;
  1096. while (_historyTextItems [_idxHistoryText].LineStatus == LineStatus.Added
  1097. && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Removed)
  1098. {
  1099. _idxHistoryText--;
  1100. }
  1101. historyTextItem = new HistoryTextItem (_historyTextItems [_idxHistoryText]);
  1102. historyTextItem.IsUndoing = true;
  1103. historyTextItem.FinalCursorPosition = historyTextItem.CursorPosition;
  1104. }
  1105. if (historyTextItem.LineStatus == LineStatus.Removed && _historyTextItems [_idxHistoryText + 1].LineStatus == LineStatus.Added)
  1106. {
  1107. historyTextItem.RemovedOnAdded =
  1108. new HistoryTextItem (_historyTextItems [_idxHistoryText + 1]);
  1109. }
  1110. if ((historyTextItem.LineStatus == LineStatus.Added && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Original)
  1111. || (historyTextItem.LineStatus == LineStatus.Removed && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Original)
  1112. || (historyTextItem.LineStatus == LineStatus.Added && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Removed))
  1113. {
  1114. if (!historyTextItem.Lines [0]
  1115. .SequenceEqual (_historyTextItems [_idxHistoryText - 1].Lines [0])
  1116. && historyTextItem.CursorPosition == _historyTextItems [_idxHistoryText - 1].CursorPosition)
  1117. {
  1118. historyTextItem.Lines [0] =
  1119. new List<RuneCell> (_historyTextItems [_idxHistoryText - 1].Lines [0]);
  1120. }
  1121. if (historyTextItem.LineStatus == LineStatus.Added && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Removed)
  1122. {
  1123. historyTextItem.FinalCursorPosition =
  1124. _historyTextItems [_idxHistoryText - 2].CursorPosition;
  1125. }
  1126. else
  1127. {
  1128. historyTextItem.FinalCursorPosition =
  1129. _historyTextItems [_idxHistoryText - 1].CursorPosition;
  1130. }
  1131. }
  1132. else
  1133. {
  1134. historyTextItem.FinalCursorPosition = historyTextItem.CursorPosition;
  1135. }
  1136. OnChangeText (historyTextItem);
  1137. while (_historyTextItems [_idxHistoryText].LineStatus == LineStatus.Removed
  1138. || _historyTextItems [_idxHistoryText].LineStatus == LineStatus.Added)
  1139. {
  1140. _idxHistoryText--;
  1141. }
  1142. }
  1143. else if (!historyTextItem.IsUndoing)
  1144. {
  1145. if (_idxHistoryText + 1 < _historyTextItems.Count
  1146. && (historyTextItem.LineStatus == LineStatus.Original
  1147. || _historyTextItems [_idxHistoryText + 1].LineStatus == LineStatus.Added
  1148. || _historyTextItems [_idxHistoryText + 1].LineStatus == LineStatus.Removed))
  1149. {
  1150. _idxHistoryText++;
  1151. historyTextItem = new HistoryTextItem (_historyTextItems [_idxHistoryText]);
  1152. historyTextItem.IsUndoing = false;
  1153. historyTextItem.FinalCursorPosition = historyTextItem.CursorPosition;
  1154. }
  1155. if (historyTextItem.LineStatus == LineStatus.Added && _historyTextItems [_idxHistoryText - 1].LineStatus == LineStatus.Removed)
  1156. {
  1157. historyTextItem.RemovedOnAdded =
  1158. new HistoryTextItem (_historyTextItems [_idxHistoryText - 1]);
  1159. }
  1160. if ((historyTextItem.LineStatus == LineStatus.Removed && _historyTextItems [_idxHistoryText + 1].LineStatus == LineStatus.Replaced)
  1161. || (historyTextItem.LineStatus == LineStatus.Removed && _historyTextItems [_idxHistoryText + 1].LineStatus == LineStatus.Original)
  1162. || (historyTextItem.LineStatus == LineStatus.Added && _historyTextItems [_idxHistoryText + 1].LineStatus == LineStatus.Replaced))
  1163. {
  1164. if (historyTextItem.LineStatus == LineStatus.Removed
  1165. && !historyTextItem.Lines [0]
  1166. .SequenceEqual (_historyTextItems [_idxHistoryText + 1].Lines [0]))
  1167. {
  1168. historyTextItem.Lines [0] =
  1169. new List<RuneCell> (_historyTextItems [_idxHistoryText + 1].Lines [0]);
  1170. }
  1171. historyTextItem.FinalCursorPosition =
  1172. _historyTextItems [_idxHistoryText + 1].CursorPosition;
  1173. }
  1174. else
  1175. {
  1176. historyTextItem.FinalCursorPosition = historyTextItem.CursorPosition;
  1177. }
  1178. OnChangeText (historyTextItem);
  1179. while (_historyTextItems [_idxHistoryText].LineStatus == LineStatus.Removed
  1180. || _historyTextItems [_idxHistoryText].LineStatus == LineStatus.Added)
  1181. {
  1182. _idxHistoryText++;
  1183. }
  1184. }
  1185. }
  1186. }
  1187. internal class WordWrapManager
  1188. {
  1189. private int _frameWidth;
  1190. private bool _isWrapModelRefreshing;
  1191. private List<WrappedLine> _wrappedModelLines = new ();
  1192. public WordWrapManager (TextModel model) { Model = model; }
  1193. public TextModel Model { get; private set; }
  1194. public void AddLine (int row, int col)
  1195. {
  1196. int modelRow = GetModelLineFromWrappedLines (row);
  1197. int modelCol = GetModelColFromWrappedLines (row, col);
  1198. List<RuneCell> line = GetCurrentLine (modelRow);
  1199. int restCount = line.Count - modelCol;
  1200. List<RuneCell> rest = line.GetRange (modelCol, restCount);
  1201. line.RemoveRange (modelCol, restCount);
  1202. Model.AddLine (modelRow + 1, rest);
  1203. _isWrapModelRefreshing = true;
  1204. WrapModel (_frameWidth, out _, out _, out _, out _, modelRow + 1);
  1205. _isWrapModelRefreshing = false;
  1206. }
  1207. public int GetModelColFromWrappedLines (int line, int col)
  1208. {
  1209. if (_wrappedModelLines?.Count == 0)
  1210. {
  1211. return 0;
  1212. }
  1213. int modelLine = GetModelLineFromWrappedLines (line);
  1214. int firstLine = _wrappedModelLines.IndexOf (r => r.ModelLine == modelLine);
  1215. var modelCol = 0;
  1216. for (int i = firstLine; i <= Math.Min (line, _wrappedModelLines!.Count - 1); i++)
  1217. {
  1218. WrappedLine wLine = _wrappedModelLines [i];
  1219. if (i < line)
  1220. {
  1221. modelCol += wLine.ColWidth;
  1222. }
  1223. else
  1224. {
  1225. modelCol += col;
  1226. }
  1227. }
  1228. return modelCol;
  1229. }
  1230. public int GetModelLineFromWrappedLines (int line)
  1231. {
  1232. return _wrappedModelLines.Count > 0
  1233. ? _wrappedModelLines [Math.Min (
  1234. line,
  1235. _wrappedModelLines.Count - 1
  1236. )].ModelLine
  1237. : 0;
  1238. }
  1239. public int GetWrappedLineColWidth (int line, int col, WordWrapManager wrapManager)
  1240. {
  1241. if (_wrappedModelLines?.Count == 0)
  1242. {
  1243. return 0;
  1244. }
  1245. List<WrappedLine> wModelLines = wrapManager._wrappedModelLines;
  1246. int modelLine = GetModelLineFromWrappedLines (line);
  1247. int firstLine = _wrappedModelLines.IndexOf (r => r.ModelLine == modelLine);
  1248. var modelCol = 0;
  1249. var colWidthOffset = 0;
  1250. int i = firstLine;
  1251. while (modelCol < col)
  1252. {
  1253. WrappedLine wLine = _wrappedModelLines! [i];
  1254. WrappedLine wLineToCompare = wModelLines [i];
  1255. if (wLine.ModelLine != modelLine || wLineToCompare.ModelLine != modelLine)
  1256. {
  1257. break;
  1258. }
  1259. modelCol += Math.Max (wLine.ColWidth, wLineToCompare.ColWidth);
  1260. colWidthOffset += wLine.ColWidth - wLineToCompare.ColWidth;
  1261. if (modelCol > col)
  1262. {
  1263. modelCol += col - modelCol;
  1264. }
  1265. i++;
  1266. }
  1267. return modelCol - colWidthOffset;
  1268. }
  1269. public bool Insert (int row, int col, RuneCell cell)
  1270. {
  1271. List<RuneCell> line = GetCurrentLine (GetModelLineFromWrappedLines (row));
  1272. line.Insert (GetModelColFromWrappedLines (row, col), cell);
  1273. if (line.Count > _frameWidth)
  1274. {
  1275. return true;
  1276. }
  1277. return false;
  1278. }
  1279. public bool RemoveAt (int row, int col)
  1280. {
  1281. int modelRow = GetModelLineFromWrappedLines (row);
  1282. List<RuneCell> line = GetCurrentLine (modelRow);
  1283. int modelCol = GetModelColFromWrappedLines (row, col);
  1284. if (modelCol > line.Count)
  1285. {
  1286. Model.RemoveLine (modelRow);
  1287. RemoveAt (row, 0);
  1288. return false;
  1289. }
  1290. if (modelCol < line.Count)
  1291. {
  1292. line.RemoveAt (modelCol);
  1293. }
  1294. if (line.Count > _frameWidth || (row + 1 < _wrappedModelLines.Count && _wrappedModelLines [row + 1].ModelLine == modelRow))
  1295. {
  1296. return true;
  1297. }
  1298. return false;
  1299. }
  1300. public bool RemoveLine (int row, int col, out bool lineRemoved, bool forward = true)
  1301. {
  1302. lineRemoved = false;
  1303. int modelRow = GetModelLineFromWrappedLines (row);
  1304. List<RuneCell> line = GetCurrentLine (modelRow);
  1305. int modelCol = GetModelColFromWrappedLines (row, col);
  1306. if (modelCol == 0 && line.Count == 0)
  1307. {
  1308. Model.RemoveLine (modelRow);
  1309. return false;
  1310. }
  1311. if (modelCol < line.Count)
  1312. {
  1313. if (forward)
  1314. {
  1315. line.RemoveAt (modelCol);
  1316. return true;
  1317. }
  1318. if (modelCol - 1 > -1)
  1319. {
  1320. line.RemoveAt (modelCol - 1);
  1321. return true;
  1322. }
  1323. }
  1324. lineRemoved = true;
  1325. if (forward)
  1326. {
  1327. if (modelRow + 1 == Model.Count)
  1328. {
  1329. return false;
  1330. }
  1331. List<RuneCell> nextLine = Model.GetLine (modelRow + 1);
  1332. line.AddRange (nextLine);
  1333. Model.RemoveLine (modelRow + 1);
  1334. if (line.Count > _frameWidth)
  1335. {
  1336. return true;
  1337. }
  1338. }
  1339. else
  1340. {
  1341. if (modelRow == 0)
  1342. {
  1343. return false;
  1344. }
  1345. List<RuneCell> prevLine = Model.GetLine (modelRow - 1);
  1346. prevLine.AddRange (line);
  1347. Model.RemoveLine (modelRow);
  1348. if (prevLine.Count > _frameWidth)
  1349. {
  1350. return true;
  1351. }
  1352. }
  1353. return false;
  1354. }
  1355. public bool RemoveRange (int row, int index, int count)
  1356. {
  1357. int modelRow = GetModelLineFromWrappedLines (row);
  1358. List<RuneCell> line = GetCurrentLine (modelRow);
  1359. int modelCol = GetModelColFromWrappedLines (row, index);
  1360. try
  1361. {
  1362. line.RemoveRange (modelCol, count);
  1363. }
  1364. catch (Exception)
  1365. {
  1366. return false;
  1367. }
  1368. return true;
  1369. }
  1370. public List<List<RuneCell>> ToListRune (List<string> textList)
  1371. {
  1372. List<List<RuneCell>> runesList = new ();
  1373. foreach (string text in textList)
  1374. {
  1375. runesList.Add (TextModel.ToRuneCellList (text));
  1376. }
  1377. return runesList;
  1378. }
  1379. public void UpdateModel (
  1380. TextModel model,
  1381. out int nRow,
  1382. out int nCol,
  1383. out int nStartRow,
  1384. out int nStartCol,
  1385. int row,
  1386. int col,
  1387. int startRow,
  1388. int startCol,
  1389. bool preserveTrailingSpaces
  1390. )
  1391. {
  1392. _isWrapModelRefreshing = true;
  1393. Model = model;
  1394. WrapModel (
  1395. _frameWidth,
  1396. out nRow,
  1397. out nCol,
  1398. out nStartRow,
  1399. out nStartCol,
  1400. row,
  1401. col,
  1402. startRow,
  1403. startCol,
  1404. 0,
  1405. preserveTrailingSpaces
  1406. );
  1407. _isWrapModelRefreshing = false;
  1408. }
  1409. public TextModel WrapModel (
  1410. int width,
  1411. out int nRow,
  1412. out int nCol,
  1413. out int nStartRow,
  1414. out int nStartCol,
  1415. int row = 0,
  1416. int col = 0,
  1417. int startRow = 0,
  1418. int startCol = 0,
  1419. int tabWidth = 0,
  1420. bool preserveTrailingSpaces = true
  1421. )
  1422. {
  1423. _frameWidth = width;
  1424. int modelRow = _isWrapModelRefreshing ? row : GetModelLineFromWrappedLines (row);
  1425. int modelCol = _isWrapModelRefreshing ? col : GetModelColFromWrappedLines (row, col);
  1426. int modelStartRow = _isWrapModelRefreshing ? startRow : GetModelLineFromWrappedLines (startRow);
  1427. int modelStartCol =
  1428. _isWrapModelRefreshing ? startCol : GetModelColFromWrappedLines (startRow, startCol);
  1429. var wrappedModel = new TextModel ();
  1430. var lines = 0;
  1431. nRow = 0;
  1432. nCol = 0;
  1433. nStartRow = 0;
  1434. nStartCol = 0;
  1435. bool isRowAndColSetted = row == 0 && col == 0;
  1436. bool isStartRowAndColSetted = startRow == 0 && startCol == 0;
  1437. List<WrappedLine> wModelLines = new ();
  1438. for (var i = 0; i < Model.Count; i++)
  1439. {
  1440. List<RuneCell> line = Model.GetLine (i);
  1441. List<List<RuneCell>> wrappedLines = ToListRune (
  1442. TextFormatter.Format (
  1443. TextModel.ToString (line),
  1444. width,
  1445. TextAlignment.Left,
  1446. true,
  1447. preserveTrailingSpaces,
  1448. tabWidth
  1449. )
  1450. );
  1451. var sumColWidth = 0;
  1452. for (var j = 0; j < wrappedLines.Count; j++)
  1453. {
  1454. List<RuneCell> wrapLine = wrappedLines [j];
  1455. if (!isRowAndColSetted && modelRow == i)
  1456. {
  1457. if (nCol + wrapLine.Count <= modelCol)
  1458. {
  1459. nCol += wrapLine.Count;
  1460. nRow = lines;
  1461. if (nCol == modelCol)
  1462. {
  1463. nCol = wrapLine.Count;
  1464. isRowAndColSetted = true;
  1465. }
  1466. else if (j == wrappedLines.Count - 1)
  1467. {
  1468. nCol = wrapLine.Count - j + modelCol - nCol;
  1469. isRowAndColSetted = true;
  1470. }
  1471. }
  1472. else
  1473. {
  1474. int offset = nCol + wrapLine.Count - modelCol;
  1475. nCol = wrapLine.Count - offset;
  1476. nRow = lines;
  1477. isRowAndColSetted = true;
  1478. }
  1479. }
  1480. if (!isStartRowAndColSetted && modelStartRow == i)
  1481. {
  1482. if (nStartCol + wrapLine.Count <= modelStartCol)
  1483. {
  1484. nStartCol += wrapLine.Count;
  1485. nStartRow = lines;
  1486. if (nStartCol == modelStartCol)
  1487. {
  1488. nStartCol = wrapLine.Count;
  1489. isStartRowAndColSetted = true;
  1490. }
  1491. else if (j == wrappedLines.Count - 1)
  1492. {
  1493. nStartCol = wrapLine.Count - j + modelStartCol - nStartCol;
  1494. isStartRowAndColSetted = true;
  1495. }
  1496. }
  1497. else
  1498. {
  1499. int offset = nStartCol + wrapLine.Count - modelStartCol;
  1500. nStartCol = wrapLine.Count - offset;
  1501. nStartRow = lines;
  1502. isStartRowAndColSetted = true;
  1503. }
  1504. }
  1505. for (int k = j; k < wrapLine.Count; k++)
  1506. {
  1507. wrapLine [k].ColorScheme = line [k].ColorScheme;
  1508. }
  1509. wrappedModel.AddLine (lines, wrapLine);
  1510. sumColWidth += wrapLine.Count;
  1511. var wrappedLine = new WrappedLine
  1512. {
  1513. ModelLine = i, Row = lines, RowIndex = j, ColWidth = wrapLine.Count
  1514. };
  1515. wModelLines.Add (wrappedLine);
  1516. lines++;
  1517. }
  1518. }
  1519. _wrappedModelLines = wModelLines;
  1520. return wrappedModel;
  1521. }
  1522. private List<RuneCell> GetCurrentLine (int row) { return Model.GetLine (row); }
  1523. private class WrappedLine
  1524. {
  1525. public int ColWidth;
  1526. public int ModelLine;
  1527. public int Row;
  1528. public int RowIndex;
  1529. }
  1530. }
  1531. /// <summary>Multi-line text editing <see cref="View"/>.</summary>
  1532. /// <remarks>
  1533. /// <para>
  1534. /// <see cref="TextView"/> provides a multi-line text editor. Users interact with it with the standard Windows,
  1535. /// Mac, and Linux (Emacs) commands.
  1536. /// </para>
  1537. /// <list type="table">
  1538. /// <listheader>
  1539. /// <term>Shortcut</term> <description>Action performed</description>
  1540. /// </listheader>
  1541. /// <item>
  1542. /// <term>Left cursor, Control-b</term> <description>Moves the editing point left.</description>
  1543. /// </item>
  1544. /// <item>
  1545. /// <term>Right cursor, Control-f</term> <description>Moves the editing point right.</description>
  1546. /// </item>
  1547. /// <item>
  1548. /// <term>Alt-b</term> <description>Moves one word back.</description>
  1549. /// </item>
  1550. /// <item>
  1551. /// <term>Alt-f</term> <description>Moves one word forward.</description>
  1552. /// </item>
  1553. /// <item>
  1554. /// <term>Up cursor, Control-p</term> <description>Moves the editing point one line up.</description>
  1555. /// </item>
  1556. /// <item>
  1557. /// <term>Down cursor, Control-n</term> <description>Moves the editing point one line down</description>
  1558. /// </item>
  1559. /// <item>
  1560. /// <term>Home key, Control-a</term> <description>Moves the cursor to the beginning of the line.</description>
  1561. /// </item>
  1562. /// <item>
  1563. /// <term>End key, Control-e</term> <description>Moves the cursor to the end of the line.</description>
  1564. /// </item>
  1565. /// <item>
  1566. /// <term>Control-Home</term> <description>Scrolls to the first line and moves the cursor there.</description>
  1567. /// </item>
  1568. /// <item>
  1569. /// <term>Control-End</term> <description>Scrolls to the last line and moves the cursor there.</description>
  1570. /// </item>
  1571. /// <item>
  1572. /// <term>Delete, Control-d</term> <description>Deletes the character in front of the cursor.</description>
  1573. /// </item>
  1574. /// <item>
  1575. /// <term>Backspace</term> <description>Deletes the character behind the cursor.</description>
  1576. /// </item>
  1577. /// <item>
  1578. /// <term>Control-k</term>
  1579. /// <description>
  1580. /// Deletes the text until the end of the line and replaces the kill buffer with the deleted text.
  1581. /// You can paste this text in a different place by using Control-y.
  1582. /// </description>
  1583. /// </item>
  1584. /// <item>
  1585. /// <term>Control-y</term>
  1586. /// <description>Pastes the content of the kill ring into the current position.</description>
  1587. /// </item>
  1588. /// <item>
  1589. /// <term>Alt-d</term>
  1590. /// <description>
  1591. /// Deletes the word above the cursor and adds it to the kill ring. You can paste the contents of
  1592. /// the kill ring with Control-y.
  1593. /// </description>
  1594. /// </item>
  1595. /// <item>
  1596. /// <term>Control-q</term>
  1597. /// <description>
  1598. /// Quotes the next input character, to prevent the normal processing of key handling to take
  1599. /// place.
  1600. /// </description>
  1601. /// </item>
  1602. /// </list>
  1603. /// </remarks>
  1604. public class TextView : View
  1605. {
  1606. private readonly HistoryText _historyText = new ();
  1607. private bool _allowsReturn = true;
  1608. private bool _allowsTab = true;
  1609. private int _bottomOffset, _rightOffset;
  1610. private bool _clickWithSelecting;
  1611. // The column we are tracking, or -1 if we are not tracking any column
  1612. private int _columnTrack = -1;
  1613. private bool _continuousFind;
  1614. private bool _copyWithoutSelection;
  1615. private string? _currentCaller;
  1616. private CultureInfo? _currentCulture;
  1617. private CursorVisibility _desiredCursorVisibility = CursorVisibility.Default;
  1618. private bool _isButtonShift;
  1619. private bool _isDrawing;
  1620. private bool _isReadOnly;
  1621. private bool _lastWasKill;
  1622. private int _leftColumn;
  1623. private TextModel _model = new ();
  1624. private bool _multiline = true;
  1625. private CursorVisibility _savedCursorVisibility;
  1626. private Dim? _savedHeight;
  1627. private int _selectionStartColumn, _selectionStartRow;
  1628. private bool _shiftSelecting;
  1629. private int _tabWidth = 4;
  1630. private int _topRow;
  1631. private bool _wordWrap;
  1632. private WordWrapManager? _wrapManager;
  1633. private bool _wrapNeeded;
  1634. /// <summary>
  1635. /// Initializes a <see cref="TextView"/> on the specified area, with dimensions controlled with the X, Y, Width
  1636. /// and Height properties.
  1637. /// </summary>
  1638. public TextView ()
  1639. {
  1640. CanFocus = true;
  1641. Used = true;
  1642. _model.LinesLoaded += Model_LinesLoaded!;
  1643. _historyText.ChangeText += HistoryText_ChangeText!;
  1644. Initialized += TextView_Initialized!;
  1645. LayoutComplete += TextView_LayoutComplete;
  1646. // Things this view knows how to do
  1647. AddCommand (
  1648. Command.PageDown,
  1649. () =>
  1650. {
  1651. ProcessPageDown ();
  1652. return true;
  1653. }
  1654. );
  1655. AddCommand (
  1656. Command.PageDownExtend,
  1657. () =>
  1658. {
  1659. ProcessPageDownExtend ();
  1660. return true;
  1661. }
  1662. );
  1663. AddCommand (
  1664. Command.PageUp,
  1665. () =>
  1666. {
  1667. ProcessPageUp ();
  1668. return true;
  1669. }
  1670. );
  1671. AddCommand (
  1672. Command.PageUpExtend,
  1673. () =>
  1674. {
  1675. ProcessPageUpExtend ();
  1676. return true;
  1677. }
  1678. );
  1679. AddCommand (
  1680. Command.LineDown,
  1681. () =>
  1682. {
  1683. ProcessMoveDown ();
  1684. return true;
  1685. }
  1686. );
  1687. AddCommand (
  1688. Command.LineDownExtend,
  1689. () =>
  1690. {
  1691. ProcessMoveDownExtend ();
  1692. return true;
  1693. }
  1694. );
  1695. AddCommand (
  1696. Command.LineUp,
  1697. () =>
  1698. {
  1699. ProcessMoveUp ();
  1700. return true;
  1701. }
  1702. );
  1703. AddCommand (
  1704. Command.LineUpExtend,
  1705. () =>
  1706. {
  1707. ProcessMoveUpExtend ();
  1708. return true;
  1709. }
  1710. );
  1711. AddCommand (Command.Right, () => ProcessMoveRight ());
  1712. AddCommand (
  1713. Command.RightExtend,
  1714. () =>
  1715. {
  1716. ProcessMoveRightExtend ();
  1717. return true;
  1718. }
  1719. );
  1720. AddCommand (Command.Left, () => ProcessMoveLeft ());
  1721. AddCommand (
  1722. Command.LeftExtend,
  1723. () =>
  1724. {
  1725. ProcessMoveLeftExtend ();
  1726. return true;
  1727. }
  1728. );
  1729. AddCommand (
  1730. Command.DeleteCharLeft,
  1731. () =>
  1732. {
  1733. ProcessDeleteCharLeft ();
  1734. return true;
  1735. }
  1736. );
  1737. AddCommand (
  1738. Command.StartOfLine,
  1739. () =>
  1740. {
  1741. ProcessMoveStartOfLine ();
  1742. return true;
  1743. }
  1744. );
  1745. AddCommand (
  1746. Command.StartOfLineExtend,
  1747. () =>
  1748. {
  1749. ProcessMoveStartOfLineExtend ();
  1750. return true;
  1751. }
  1752. );
  1753. AddCommand (
  1754. Command.DeleteCharRight,
  1755. () =>
  1756. {
  1757. ProcessDeleteCharRight ();
  1758. return true;
  1759. }
  1760. );
  1761. AddCommand (
  1762. Command.EndOfLine,
  1763. () =>
  1764. {
  1765. ProcessMoveEndOfLine ();
  1766. return true;
  1767. }
  1768. );
  1769. AddCommand (
  1770. Command.EndOfLineExtend,
  1771. () =>
  1772. {
  1773. ProcessMoveEndOfLineExtend ();
  1774. return true;
  1775. }
  1776. );
  1777. AddCommand (
  1778. Command.CutToEndLine,
  1779. () =>
  1780. {
  1781. KillToEndOfLine ();
  1782. return true;
  1783. }
  1784. );
  1785. AddCommand (
  1786. Command.CutToStartLine,
  1787. () =>
  1788. {
  1789. KillToStartOfLine ();
  1790. return true;
  1791. }
  1792. );
  1793. AddCommand (
  1794. Command.Paste,
  1795. () =>
  1796. {
  1797. ProcessPaste ();
  1798. return true;
  1799. }
  1800. );
  1801. AddCommand (
  1802. Command.ToggleExtend,
  1803. () =>
  1804. {
  1805. ToggleSelecting ();
  1806. return true;
  1807. }
  1808. );
  1809. AddCommand (
  1810. Command.Copy,
  1811. () =>
  1812. {
  1813. ProcessCopy ();
  1814. return true;
  1815. }
  1816. );
  1817. AddCommand (
  1818. Command.Cut,
  1819. () =>
  1820. {
  1821. ProcessCut ();
  1822. return true;
  1823. }
  1824. );
  1825. AddCommand (
  1826. Command.WordLeft,
  1827. () =>
  1828. {
  1829. ProcessMoveWordBackward ();
  1830. return true;
  1831. }
  1832. );
  1833. AddCommand (
  1834. Command.WordLeftExtend,
  1835. () =>
  1836. {
  1837. ProcessMoveWordBackwardExtend ();
  1838. return true;
  1839. }
  1840. );
  1841. AddCommand (
  1842. Command.WordRight,
  1843. () =>
  1844. {
  1845. ProcessMoveWordForward ();
  1846. return true;
  1847. }
  1848. );
  1849. AddCommand (
  1850. Command.WordRightExtend,
  1851. () =>
  1852. {
  1853. ProcessMoveWordForwardExtend ();
  1854. return true;
  1855. }
  1856. );
  1857. AddCommand (
  1858. Command.KillWordForwards,
  1859. () =>
  1860. {
  1861. ProcessKillWordForward ();
  1862. return true;
  1863. }
  1864. );
  1865. AddCommand (
  1866. Command.KillWordBackwards,
  1867. () =>
  1868. {
  1869. ProcessKillWordBackward ();
  1870. return true;
  1871. }
  1872. );
  1873. AddCommand (Command.NewLine, () => ProcessReturn ());
  1874. AddCommand (
  1875. Command.BottomEnd,
  1876. () =>
  1877. {
  1878. MoveBottomEnd ();
  1879. return true;
  1880. }
  1881. );
  1882. AddCommand (
  1883. Command.BottomEndExtend,
  1884. () =>
  1885. {
  1886. MoveBottomEndExtend ();
  1887. return true;
  1888. }
  1889. );
  1890. AddCommand (
  1891. Command.TopHome,
  1892. () =>
  1893. {
  1894. MoveTopHome ();
  1895. return true;
  1896. }
  1897. );
  1898. AddCommand (
  1899. Command.TopHomeExtend,
  1900. () =>
  1901. {
  1902. MoveTopHomeExtend ();
  1903. return true;
  1904. }
  1905. );
  1906. AddCommand (
  1907. Command.SelectAll,
  1908. () =>
  1909. {
  1910. ProcessSelectAll ();
  1911. return true;
  1912. }
  1913. );
  1914. AddCommand (
  1915. Command.ToggleOverwrite,
  1916. () =>
  1917. {
  1918. ProcessSetOverwrite ();
  1919. return true;
  1920. }
  1921. );
  1922. AddCommand (
  1923. Command.EnableOverwrite,
  1924. () =>
  1925. {
  1926. SetOverwrite (true);
  1927. return true;
  1928. }
  1929. );
  1930. AddCommand (
  1931. Command.DisableOverwrite,
  1932. () =>
  1933. {
  1934. SetOverwrite (false);
  1935. return true;
  1936. }
  1937. );
  1938. AddCommand (Command.Tab, () => ProcessTab ());
  1939. AddCommand (Command.BackTab, () => ProcessBackTab ());
  1940. AddCommand (Command.NextView, () => ProcessMoveNextView ());
  1941. AddCommand (Command.PreviousView, () => ProcessMovePreviousView ());
  1942. AddCommand (
  1943. Command.Undo,
  1944. () =>
  1945. {
  1946. Undo ();
  1947. return true;
  1948. }
  1949. );
  1950. AddCommand (
  1951. Command.Redo,
  1952. () =>
  1953. {
  1954. Redo ();
  1955. return true;
  1956. }
  1957. );
  1958. AddCommand (
  1959. Command.DeleteAll,
  1960. () =>
  1961. {
  1962. DeleteAll ();
  1963. return true;
  1964. }
  1965. );
  1966. AddCommand (
  1967. Command.ShowContextMenu,
  1968. () =>
  1969. {
  1970. ContextMenu!.Position = new Point (
  1971. CursorPosition.X - _leftColumn + 2,
  1972. CursorPosition.Y - _topRow + 2
  1973. );
  1974. ShowContextMenu ();
  1975. return true;
  1976. }
  1977. );
  1978. // Default keybindings for this view
  1979. KeyBindings.Add (KeyCode.PageDown, Command.PageDown);
  1980. KeyBindings.Add (KeyCode.V | KeyCode.CtrlMask, Command.PageDown);
  1981. KeyBindings.Add (KeyCode.PageDown | KeyCode.ShiftMask, Command.PageDownExtend);
  1982. KeyBindings.Add (KeyCode.PageUp, Command.PageUp);
  1983. KeyBindings.Add ('V' + KeyCode.AltMask, Command.PageUp);
  1984. KeyBindings.Add (KeyCode.PageUp | KeyCode.ShiftMask, Command.PageUpExtend);
  1985. KeyBindings.Add (KeyCode.N | KeyCode.CtrlMask, Command.LineDown);
  1986. KeyBindings.Add (KeyCode.CursorDown, Command.LineDown);
  1987. KeyBindings.Add (KeyCode.CursorDown | KeyCode.ShiftMask, Command.LineDownExtend);
  1988. KeyBindings.Add (KeyCode.P | KeyCode.CtrlMask, Command.LineUp);
  1989. KeyBindings.Add (KeyCode.CursorUp, Command.LineUp);
  1990. KeyBindings.Add (KeyCode.CursorUp | KeyCode.ShiftMask, Command.LineUpExtend);
  1991. KeyBindings.Add (KeyCode.F | KeyCode.CtrlMask, Command.Right);
  1992. KeyBindings.Add (KeyCode.CursorRight, Command.Right);
  1993. KeyBindings.Add (KeyCode.CursorRight | KeyCode.ShiftMask, Command.RightExtend);
  1994. KeyBindings.Add (KeyCode.B | KeyCode.CtrlMask, Command.Left);
  1995. KeyBindings.Add (KeyCode.CursorLeft, Command.Left);
  1996. KeyBindings.Add (KeyCode.CursorLeft | KeyCode.ShiftMask, Command.LeftExtend);
  1997. KeyBindings.Add (KeyCode.Backspace, Command.DeleteCharLeft);
  1998. KeyBindings.Add (KeyCode.Home, Command.StartOfLine);
  1999. KeyBindings.Add (KeyCode.A | KeyCode.CtrlMask, Command.StartOfLine);
  2000. KeyBindings.Add (KeyCode.Home | KeyCode.ShiftMask, Command.StartOfLineExtend);
  2001. KeyBindings.Add (KeyCode.Delete, Command.DeleteCharRight);
  2002. KeyBindings.Add (KeyCode.D | KeyCode.CtrlMask, Command.DeleteCharRight);
  2003. KeyBindings.Add (KeyCode.End, Command.EndOfLine);
  2004. KeyBindings.Add (KeyCode.E | KeyCode.CtrlMask, Command.EndOfLine);
  2005. KeyBindings.Add (KeyCode.End | KeyCode.ShiftMask, Command.EndOfLineExtend);
  2006. KeyBindings.Add (KeyCode.K | KeyCode.CtrlMask, Command.CutToEndLine); // kill-to-end
  2007. KeyBindings.Add (
  2008. KeyCode.Delete | KeyCode.CtrlMask | KeyCode.ShiftMask,
  2009. Command.CutToEndLine
  2010. ); // kill-to-end
  2011. KeyBindings.Add (KeyCode.K | KeyCode.AltMask, Command.CutToStartLine); // kill-to-start
  2012. KeyBindings.Add (
  2013. KeyCode.Backspace | KeyCode.CtrlMask | KeyCode.ShiftMask,
  2014. Command.CutToStartLine
  2015. ); // kill-to-start
  2016. KeyBindings.Add (KeyCode.Y | KeyCode.CtrlMask, Command.Paste); // Control-y, yank
  2017. KeyBindings.Add (KeyCode.Space | KeyCode.CtrlMask, Command.ToggleExtend);
  2018. KeyBindings.Add ('C' + KeyCode.AltMask, Command.Copy);
  2019. KeyBindings.Add (KeyCode.C | KeyCode.CtrlMask, Command.Copy);
  2020. KeyBindings.Add ('W' + KeyCode.AltMask, Command.Cut);
  2021. KeyBindings.Add (KeyCode.W | KeyCode.CtrlMask, Command.Cut);
  2022. KeyBindings.Add (KeyCode.X | KeyCode.CtrlMask, Command.Cut);
  2023. KeyBindings.Add (KeyCode.CursorLeft | KeyCode.CtrlMask, Command.WordLeft);
  2024. KeyBindings.Add ('B' + KeyCode.AltMask, Command.WordLeft);
  2025. KeyBindings.Add (KeyCode.CursorLeft | KeyCode.CtrlMask | KeyCode.ShiftMask, Command.WordLeftExtend);
  2026. KeyBindings.Add (KeyCode.CursorRight | KeyCode.CtrlMask, Command.WordRight);
  2027. KeyBindings.Add ('F' + KeyCode.AltMask, Command.WordRight);
  2028. KeyBindings.Add (KeyCode.CursorRight | KeyCode.CtrlMask | KeyCode.ShiftMask, Command.WordRightExtend);
  2029. KeyBindings.Add (KeyCode.Delete | KeyCode.CtrlMask, Command.KillWordForwards); // kill-word-forwards
  2030. KeyBindings.Add (
  2031. KeyCode.Backspace | KeyCode.CtrlMask,
  2032. Command.KillWordBackwards
  2033. ); // kill-word-backwards
  2034. // BUGBUG: If AllowsReturn is false, Key.Enter should not be bound (so that Toplevel can cause Command.Accept).
  2035. KeyBindings.Add (KeyCode.Enter, Command.NewLine);
  2036. KeyBindings.Add (KeyCode.End | KeyCode.CtrlMask, Command.BottomEnd);
  2037. KeyBindings.Add (KeyCode.End | KeyCode.CtrlMask | KeyCode.ShiftMask, Command.BottomEndExtend);
  2038. KeyBindings.Add (KeyCode.Home | KeyCode.CtrlMask, Command.TopHome);
  2039. KeyBindings.Add (KeyCode.Home | KeyCode.CtrlMask | KeyCode.ShiftMask, Command.TopHomeExtend);
  2040. KeyBindings.Add (KeyCode.T | KeyCode.CtrlMask, Command.SelectAll);
  2041. KeyBindings.Add (KeyCode.Insert, Command.ToggleOverwrite);
  2042. KeyBindings.Add (KeyCode.Tab, Command.Tab);
  2043. KeyBindings.Add (KeyCode.Tab | KeyCode.ShiftMask, Command.BackTab);
  2044. KeyBindings.Add (KeyCode.Tab | KeyCode.CtrlMask, Command.NextView);
  2045. KeyBindings.Add ((KeyCode)Application.AlternateForwardKey, Command.NextView);
  2046. KeyBindings.Add (KeyCode.Tab | KeyCode.CtrlMask | KeyCode.ShiftMask, Command.PreviousView);
  2047. KeyBindings.Add ((KeyCode)Application.AlternateBackwardKey, Command.PreviousView);
  2048. KeyBindings.Add (KeyCode.Z | KeyCode.CtrlMask, Command.Undo);
  2049. KeyBindings.Add (KeyCode.R | KeyCode.CtrlMask, Command.Redo);
  2050. KeyBindings.Add (KeyCode.G | KeyCode.CtrlMask, Command.DeleteAll);
  2051. KeyBindings.Add (KeyCode.D | KeyCode.CtrlMask | KeyCode.ShiftMask, Command.DeleteAll);
  2052. _currentCulture = Thread.CurrentThread.CurrentUICulture;
  2053. ContextMenu = new ContextMenu { MenuItems = BuildContextMenuBarItem () };
  2054. ContextMenu.KeyChanged += ContextMenu_KeyChanged!;
  2055. KeyBindings.Add ((KeyCode)ContextMenu.Key, KeyBindingScope.HotKey, Command.ShowContextMenu);
  2056. }
  2057. /// <summary>
  2058. /// Gets or sets a value indicating whether pressing ENTER in a <see cref="TextView"/> creates a new line of text
  2059. /// in the view or activates the default button for the Toplevel.
  2060. /// </summary>
  2061. public bool AllowsReturn
  2062. {
  2063. get => _allowsReturn;
  2064. set
  2065. {
  2066. _allowsReturn = value;
  2067. if (_allowsReturn && !_multiline)
  2068. {
  2069. Multiline = true;
  2070. }
  2071. if (!_allowsReturn && _multiline)
  2072. {
  2073. Multiline = false;
  2074. AllowsTab = false;
  2075. }
  2076. SetNeedsDisplay ();
  2077. }
  2078. }
  2079. /// <summary>
  2080. /// Gets or sets whether the <see cref="TextView"/> inserts a tab character into the text or ignores tab input. If
  2081. /// set to `false` and the user presses the tab key (or shift-tab) the focus will move to the next view (or previous
  2082. /// with shift-tab). The default is `true`; if the user presses the tab key, a tab character will be inserted into the
  2083. /// text.
  2084. /// </summary>
  2085. public bool AllowsTab
  2086. {
  2087. get => _allowsTab;
  2088. set
  2089. {
  2090. _allowsTab = value;
  2091. if (_allowsTab && _tabWidth == 0)
  2092. {
  2093. _tabWidth = 4;
  2094. }
  2095. if (_allowsTab && !_multiline)
  2096. {
  2097. Multiline = true;
  2098. }
  2099. if (!_allowsTab && _tabWidth > 0)
  2100. {
  2101. _tabWidth = 0;
  2102. }
  2103. SetNeedsDisplay ();
  2104. }
  2105. }
  2106. /// <summary>
  2107. /// Provides autocomplete context menu based on suggestions at the current cursor position. Configure
  2108. /// <see cref="IAutocomplete.SuggestionGenerator"/> to enable this feature
  2109. /// </summary>
  2110. public IAutocomplete Autocomplete { get; protected set; } = new TextViewAutocomplete ();
  2111. /// <summary>
  2112. /// The bottom offset needed to use a horizontal scrollbar or for another reason. This is only needed with the
  2113. /// keyboard navigation.
  2114. /// </summary>
  2115. public int BottomOffset
  2116. {
  2117. get => _bottomOffset;
  2118. set
  2119. {
  2120. if (CurrentRow == Lines - 1 && _bottomOffset > 0 && value == 0)
  2121. {
  2122. _topRow = Math.Max (_topRow - _bottomOffset, 0);
  2123. }
  2124. _bottomOffset = value;
  2125. Adjust ();
  2126. }
  2127. }
  2128. /// <inheritdoc/>
  2129. public override bool CanFocus
  2130. {
  2131. get => base.CanFocus;
  2132. set => base.CanFocus = value;
  2133. }
  2134. /// <summary>Get the <see cref="ContextMenu"/> for this view.</summary>
  2135. public ContextMenu? ContextMenu { get; }
  2136. /// <summary>Gets the cursor column.</summary>
  2137. /// <value>The cursor column.</value>
  2138. public int CurrentColumn { get; private set; }
  2139. /// <summary>Gets the current cursor row.</summary>
  2140. public int CurrentRow { get; private set; }
  2141. /// <summary>Sets or gets the current cursor position.</summary>
  2142. public Point CursorPosition
  2143. {
  2144. get => new (CurrentColumn, CurrentRow);
  2145. set
  2146. {
  2147. List<RuneCell> line = _model.GetLine (Math.Max (Math.Min (value.Y, _model.Count - 1), 0));
  2148. CurrentColumn = value.X < 0 ? 0 :
  2149. value.X > line.Count ? line.Count : value.X;
  2150. CurrentRow = value.Y < 0 ? 0 :
  2151. value.Y > _model.Count - 1 ? Math.Max (_model.Count - 1, 0) : value.Y;
  2152. SetNeedsDisplay ();
  2153. Adjust ();
  2154. }
  2155. }
  2156. /// <summary>Get / Set the wished cursor when the field is focused</summary>
  2157. public CursorVisibility DesiredCursorVisibility
  2158. {
  2159. get => _desiredCursorVisibility;
  2160. set
  2161. {
  2162. if (HasFocus)
  2163. {
  2164. Application.Driver.SetCursorVisibility (value);
  2165. }
  2166. _desiredCursorVisibility = value;
  2167. SetNeedsDisplay ();
  2168. }
  2169. }
  2170. /// <summary>
  2171. /// Indicates whatever the text has history changes or not. <see langword="true"/> if the text has history changes
  2172. /// <see langword="false"/> otherwise.
  2173. /// </summary>
  2174. public bool HasHistoryChanges => _historyText.HasHistoryChanges;
  2175. /// <summary>
  2176. /// If <see langword="true"/> and the current <see cref="RuneCell.ColorScheme"/> is null will inherit from the
  2177. /// previous, otherwise if <see langword="false"/> (default) do nothing. If the text is load with
  2178. /// <see cref="Load(List{RuneCell})"/> this property is automatically sets to <see langword="true"/>.
  2179. /// </summary>
  2180. public bool InheritsPreviousColorScheme { get; set; }
  2181. /// <summary>
  2182. /// Indicates whatever the text was changed or not. <see langword="true"/> if the text was changed
  2183. /// <see langword="false"/> otherwise.
  2184. /// </summary>
  2185. public bool IsDirty
  2186. {
  2187. get => _historyText.IsDirty (Text);
  2188. set => _historyText.Clear (Text);
  2189. }
  2190. /// <summary>Gets or sets the left column.</summary>
  2191. public int LeftColumn
  2192. {
  2193. get => _leftColumn;
  2194. set
  2195. {
  2196. if (value > 0 && _wordWrap)
  2197. {
  2198. return;
  2199. }
  2200. _leftColumn = Math.Max (Math.Min (value, Maxlength - 1), 0);
  2201. }
  2202. }
  2203. /// <summary>Gets the number of lines.</summary>
  2204. public int Lines => _model.Count;
  2205. /// <summary>Gets the maximum visible length line.</summary>
  2206. public int Maxlength => _model.GetMaxVisibleLine (_topRow, _topRow + Frame.Height, TabWidth);
  2207. /// <summary>Gets or sets a value indicating whether this <see cref="TextView"/> is a multiline text view.</summary>
  2208. public bool Multiline
  2209. {
  2210. get => _multiline;
  2211. set
  2212. {
  2213. _multiline = value;
  2214. if (_multiline && !_allowsTab)
  2215. {
  2216. AllowsTab = true;
  2217. }
  2218. if (_multiline && !_allowsReturn)
  2219. {
  2220. AllowsReturn = true;
  2221. }
  2222. if (!_multiline)
  2223. {
  2224. AllowsReturn = false;
  2225. AllowsTab = false;
  2226. WordWrap = false;
  2227. CurrentColumn = 0;
  2228. CurrentRow = 0;
  2229. _savedHeight = Height;
  2230. //var prevLayoutStyle = LayoutStyle;
  2231. //if (LayoutStyle == LayoutStyle.Computed) {
  2232. // LayoutStyle = LayoutStyle.Absolute;
  2233. //}
  2234. Height = 1;
  2235. //LayoutStyle = prevLayoutStyle;
  2236. if (!IsInitialized)
  2237. {
  2238. _model.LoadString (Text);
  2239. }
  2240. SetNeedsDisplay ();
  2241. }
  2242. else if (_multiline && _savedHeight is { })
  2243. {
  2244. //var lyout = LayoutStyle;
  2245. //if (LayoutStyle == LayoutStyle.Computed) {
  2246. // LayoutStyle = LayoutStyle.Absolute;
  2247. //}
  2248. Height = _savedHeight;
  2249. //LayoutStyle = lyout;
  2250. SetNeedsDisplay ();
  2251. }
  2252. }
  2253. }
  2254. /// <summary>Gets or sets whether the <see cref="TextView"/> is in read-only mode or not</summary>
  2255. /// <value>Boolean value(Default false)</value>
  2256. public bool ReadOnly
  2257. {
  2258. get => _isReadOnly;
  2259. set
  2260. {
  2261. if (value != _isReadOnly)
  2262. {
  2263. _isReadOnly = value;
  2264. SetNeedsDisplay ();
  2265. Adjust ();
  2266. }
  2267. }
  2268. }
  2269. /// <summary>
  2270. /// The right offset needed to use a vertical scrollbar or for another reason. This is only needed with the
  2271. /// keyboard navigation.
  2272. /// </summary>
  2273. public int RightOffset
  2274. {
  2275. get => _rightOffset;
  2276. set
  2277. {
  2278. if (!_wordWrap && CurrentColumn == GetCurrentLine ().Count && _rightOffset > 0 && value == 0)
  2279. {
  2280. _leftColumn = Math.Max (_leftColumn - _rightOffset, 0);
  2281. }
  2282. _rightOffset = value;
  2283. Adjust ();
  2284. }
  2285. }
  2286. /// <summary>Length of the selected text.</summary>
  2287. public int SelectedLength => GetSelectedLength ();
  2288. /// <summary>The selected text.</summary>
  2289. public string SelectedText
  2290. {
  2291. get
  2292. {
  2293. if (!Selecting || (_model.Count == 1 && _model.GetLine (0).Count == 0))
  2294. {
  2295. return string.Empty;
  2296. }
  2297. return GetSelectedRegion ();
  2298. }
  2299. }
  2300. /// <summary>Get or sets the selecting.</summary>
  2301. public bool Selecting { get; set; }
  2302. /// <summary>Start column position of the selected text.</summary>
  2303. public int SelectionStartColumn
  2304. {
  2305. get => _selectionStartColumn;
  2306. set
  2307. {
  2308. List<RuneCell> line = _model.GetLine (_selectionStartRow);
  2309. _selectionStartColumn = value < 0 ? 0 :
  2310. value > line.Count ? line.Count : value;
  2311. Selecting = true;
  2312. SetNeedsDisplay ();
  2313. Adjust ();
  2314. }
  2315. }
  2316. /// <summary>Start row position of the selected text.</summary>
  2317. public int SelectionStartRow
  2318. {
  2319. get => _selectionStartRow;
  2320. set
  2321. {
  2322. _selectionStartRow = value < 0 ? 0 :
  2323. value > _model.Count - 1 ? Math.Max (_model.Count - 1, 0) : value;
  2324. Selecting = true;
  2325. SetNeedsDisplay ();
  2326. Adjust ();
  2327. }
  2328. }
  2329. /// <summary>Gets or sets a value indicating the number of whitespace when pressing the TAB key.</summary>
  2330. public int TabWidth
  2331. {
  2332. get => _tabWidth;
  2333. set
  2334. {
  2335. _tabWidth = Math.Max (value, 0);
  2336. if (_tabWidth > 0 && !AllowsTab)
  2337. {
  2338. AllowsTab = true;
  2339. }
  2340. SetNeedsDisplay ();
  2341. }
  2342. }
  2343. /// <summary>Sets or gets the text in the <see cref="TextView"/>.</summary>
  2344. /// <remarks>
  2345. /// The <see cref="TextChanged"/> event is fired whenever this property is set. Note, however, that Text is not
  2346. /// set by <see cref="TextView"/> as the user types.
  2347. /// </remarks>
  2348. public override string Text
  2349. {
  2350. get
  2351. {
  2352. if (_wordWrap)
  2353. {
  2354. return _wrapManager!.Model.ToString ();
  2355. }
  2356. return _model.ToString ();
  2357. }
  2358. set
  2359. {
  2360. ResetPosition ();
  2361. _model.LoadString (value);
  2362. if (_wordWrap)
  2363. {
  2364. _wrapManager = new WordWrapManager (_model);
  2365. _model = _wrapManager.WrapModel (_frameWidth, out _, out _, out _, out _);
  2366. }
  2367. TextChanged?.Invoke (this, EventArgs.Empty);
  2368. SetNeedsDisplay ();
  2369. _historyText.Clear (Text);
  2370. }
  2371. }
  2372. /// <summary>Gets or sets the top row.</summary>
  2373. public int TopRow
  2374. {
  2375. get => _topRow;
  2376. set => _topRow = Math.Max (Math.Min (value, Lines - 1), 0);
  2377. }
  2378. /// <summary>
  2379. /// Tracks whether the text view should be considered "used", that is, that the user has moved in the entry, so
  2380. /// new input should be appended at the cursor position, rather than clearing the entry
  2381. /// </summary>
  2382. public bool Used { get; set; }
  2383. /// <summary>Allows word wrap the to fit the available container width.</summary>
  2384. public bool WordWrap
  2385. {
  2386. get => _wordWrap;
  2387. set
  2388. {
  2389. if (value == _wordWrap)
  2390. {
  2391. return;
  2392. }
  2393. if (value && !_multiline)
  2394. {
  2395. return;
  2396. }
  2397. _wordWrap = value;
  2398. ResetPosition ();
  2399. if (_wordWrap)
  2400. {
  2401. _wrapManager = new WordWrapManager (_model);
  2402. _model = _wrapManager.WrapModel (_frameWidth, out _, out _, out _, out _);
  2403. }
  2404. else if (!_wordWrap && _wrapManager is { })
  2405. {
  2406. _model = _wrapManager.Model;
  2407. }
  2408. SetNeedsDisplay ();
  2409. }
  2410. }
  2411. private int _frameWidth => Math.Max (Frame.Width - (RightOffset != 0 ? 2 : 1), 0);
  2412. /// <summary>Allows clearing the <see cref="HistoryText.HistoryTextItem"/> items updating the original text.</summary>
  2413. public void ClearHistoryChanges () { _historyText?.Clear (Text); }
  2414. /// <summary>Closes the contents of the stream into the <see cref="TextView"/>.</summary>
  2415. /// <returns><c>true</c>, if stream was closed, <c>false</c> otherwise.</returns>
  2416. public bool CloseFile ()
  2417. {
  2418. SetWrapModel ();
  2419. bool res = _model.CloseFile ();
  2420. ResetPosition ();
  2421. SetNeedsDisplay ();
  2422. UpdateWrapModel ();
  2423. return res;
  2424. }
  2425. /// <summary>Raised when the contents of the <see cref="TextView"/> are changed.</summary>
  2426. /// <remarks>
  2427. /// Unlike the <see cref="TextChanged"/> event, this event is raised whenever the user types or otherwise changes
  2428. /// the contents of the <see cref="TextView"/>.
  2429. /// </remarks>
  2430. public event EventHandler<ContentsChangedEventArgs>? ContentsChanged;
  2431. /// <summary>Copy the selected text to the clipboard contents.</summary>
  2432. public void Copy ()
  2433. {
  2434. SetWrapModel ();
  2435. if (Selecting)
  2436. {
  2437. SetClipboard (GetRegion ());
  2438. _copyWithoutSelection = false;
  2439. }
  2440. else
  2441. {
  2442. List<RuneCell> currentLine = GetCurrentLine ();
  2443. SetClipboard (TextModel.ToString (currentLine));
  2444. _copyWithoutSelection = true;
  2445. }
  2446. UpdateWrapModel ();
  2447. DoNeededAction ();
  2448. }
  2449. /// <summary>Cut the selected text to the clipboard contents.</summary>
  2450. public void Cut ()
  2451. {
  2452. SetWrapModel ();
  2453. SetClipboard (GetRegion ());
  2454. if (!_isReadOnly)
  2455. {
  2456. ClearRegion ();
  2457. _historyText.Add (
  2458. new List<List<RuneCell>> { new (GetCurrentLine ()) },
  2459. CursorPosition,
  2460. HistoryText.LineStatus.Replaced
  2461. );
  2462. }
  2463. UpdateWrapModel ();
  2464. Selecting = false;
  2465. DoNeededAction ();
  2466. OnContentsChanged ();
  2467. }
  2468. /// <summary>Deletes all text.</summary>
  2469. public void DeleteAll ()
  2470. {
  2471. if (Lines == 0)
  2472. {
  2473. return;
  2474. }
  2475. _selectionStartColumn = 0;
  2476. _selectionStartRow = 0;
  2477. MoveBottomEndExtend ();
  2478. DeleteCharLeft ();
  2479. SetNeedsDisplay ();
  2480. }
  2481. /// <summary>Deletes all the selected or a single character at left from the position of the cursor.</summary>
  2482. public void DeleteCharLeft ()
  2483. {
  2484. if (_isReadOnly)
  2485. {
  2486. return;
  2487. }
  2488. SetWrapModel ();
  2489. if (Selecting)
  2490. {
  2491. _historyText.Add (new List<List<RuneCell>> { new (GetCurrentLine ()) }, CursorPosition);
  2492. ClearSelectedRegion ();
  2493. List<RuneCell> currentLine = GetCurrentLine ();
  2494. _historyText.Add (
  2495. new List<List<RuneCell>> { new (currentLine) },
  2496. CursorPosition,
  2497. HistoryText.LineStatus.Replaced
  2498. );
  2499. UpdateWrapModel ();
  2500. OnContentsChanged ();
  2501. return;
  2502. }
  2503. if (DeleteTextBackwards ())
  2504. {
  2505. UpdateWrapModel ();
  2506. OnContentsChanged ();
  2507. return;
  2508. }
  2509. UpdateWrapModel ();
  2510. DoNeededAction ();
  2511. OnContentsChanged ();
  2512. }
  2513. /// <summary>Deletes all the selected or a single character at right from the position of the cursor.</summary>
  2514. public void DeleteCharRight ()
  2515. {
  2516. if (_isReadOnly)
  2517. {
  2518. return;
  2519. }
  2520. SetWrapModel ();
  2521. if (Selecting)
  2522. {
  2523. _historyText.Add (new List<List<RuneCell>> { new (GetCurrentLine ()) }, CursorPosition);
  2524. ClearSelectedRegion ();
  2525. List<RuneCell> currentLine = GetCurrentLine ();
  2526. _historyText.Add (
  2527. new List<List<RuneCell>> { new (currentLine) },
  2528. CursorPosition,
  2529. HistoryText.LineStatus.Replaced
  2530. );
  2531. UpdateWrapModel ();
  2532. OnContentsChanged ();
  2533. return;
  2534. }
  2535. if (DeleteTextForwards ())
  2536. {
  2537. UpdateWrapModel ();
  2538. OnContentsChanged ();
  2539. return;
  2540. }
  2541. UpdateWrapModel ();
  2542. DoNeededAction ();
  2543. OnContentsChanged ();
  2544. }
  2545. /// <summary>Invoked when the normal color is drawn.</summary>
  2546. public event EventHandler<RuneCellEventArgs>? DrawNormalColor;
  2547. /// <summary>Invoked when the ready only color is drawn.</summary>
  2548. public event EventHandler<RuneCellEventArgs>? DrawReadOnlyColor;
  2549. /// <summary>Invoked when the selection color is drawn.</summary>
  2550. public event EventHandler<RuneCellEventArgs>? DrawSelectionColor;
  2551. /// <summary>
  2552. /// Invoked when the used color is drawn. The Used Color is used to indicate if the <see cref="Key.InsertChar"/>
  2553. /// was pressed and enabled.
  2554. /// </summary>
  2555. public event EventHandler<RuneCellEventArgs>? DrawUsedColor;
  2556. /// <summary>Find the next text based on the match case with the option to replace it.</summary>
  2557. /// <param name="textToFind">The text to find.</param>
  2558. /// <param name="gaveFullTurn"><c>true</c>If all the text was forward searched.<c>false</c>otherwise.</param>
  2559. /// <param name="matchCase">The match case setting.</param>
  2560. /// <param name="matchWholeWord">The match whole word setting.</param>
  2561. /// <param name="textToReplace">The text to replace.</param>
  2562. /// <param name="replace"><c>true</c>If is replacing.<c>false</c>otherwise.</param>
  2563. /// <returns><c>true</c>If the text was found.<c>false</c>otherwise.</returns>
  2564. public bool FindNextText (
  2565. string textToFind,
  2566. out bool gaveFullTurn,
  2567. bool matchCase = false,
  2568. bool matchWholeWord = false,
  2569. string? textToReplace = null,
  2570. bool replace = false
  2571. )
  2572. {
  2573. if (_model.Count == 0)
  2574. {
  2575. gaveFullTurn = false;
  2576. return false;
  2577. }
  2578. SetWrapModel ();
  2579. ResetContinuousFind ();
  2580. (Point current, bool found) foundPos =
  2581. _model.FindNextText (textToFind, out gaveFullTurn, matchCase, matchWholeWord);
  2582. return SetFoundText (textToFind, foundPos, textToReplace, replace);
  2583. }
  2584. /// <summary>Find the previous text based on the match case with the option to replace it.</summary>
  2585. /// <param name="textToFind">The text to find.</param>
  2586. /// <param name="gaveFullTurn"><c>true</c>If all the text was backward searched.<c>false</c>otherwise.</param>
  2587. /// <param name="matchCase">The match case setting.</param>
  2588. /// <param name="matchWholeWord">The match whole word setting.</param>
  2589. /// <param name="textToReplace">The text to replace.</param>
  2590. /// <param name="replace"><c>true</c>If the text was found.<c>false</c>otherwise.</param>
  2591. /// <returns><c>true</c>If the text was found.<c>false</c>otherwise.</returns>
  2592. public bool FindPreviousText (
  2593. string textToFind,
  2594. out bool gaveFullTurn,
  2595. bool matchCase = false,
  2596. bool matchWholeWord = false,
  2597. string? textToReplace = null,
  2598. bool replace = false
  2599. )
  2600. {
  2601. if (_model.Count == 0)
  2602. {
  2603. gaveFullTurn = false;
  2604. return false;
  2605. }
  2606. SetWrapModel ();
  2607. ResetContinuousFind ();
  2608. (Point current, bool found) foundPos =
  2609. _model.FindPreviousText (textToFind, out gaveFullTurn, matchCase, matchWholeWord);
  2610. return SetFoundText (textToFind, foundPos, textToReplace, replace);
  2611. }
  2612. /// <summary>Reset the flag to stop continuous find.</summary>
  2613. public void FindTextChanged () { _continuousFind = false; }
  2614. /// <summary>Gets all lines of characters.</summary>
  2615. /// <returns></returns>
  2616. public List<List<RuneCell>> GetAllLines () { return _model.GetAllLines (); }
  2617. /// <summary>
  2618. /// Returns the characters on the current line (where the cursor is positioned). Use <see cref="CurrentColumn"/>
  2619. /// to determine the position of the cursor within that line
  2620. /// </summary>
  2621. /// <returns></returns>
  2622. public List<RuneCell> GetCurrentLine () { return _model.GetLine (CurrentRow); }
  2623. /// <summary>Returns the characters on the <paramref name="line"/>.</summary>
  2624. /// <param name="line">The intended line.</param>
  2625. /// <returns></returns>
  2626. public List<RuneCell> GetLine (int line) { return _model.GetLine (line); }
  2627. /// <inheritdoc/>
  2628. public override Attribute GetNormalColor ()
  2629. {
  2630. ColorScheme? cs = ColorScheme;
  2631. if (ColorScheme is null)
  2632. {
  2633. cs = new ColorScheme ();
  2634. }
  2635. return Enabled ? cs.Focus : cs.Disabled;
  2636. }
  2637. /// <summary>
  2638. /// Inserts the given <paramref name="toAdd"/> text at the current cursor position exactly as if the user had just
  2639. /// typed it
  2640. /// </summary>
  2641. /// <param name="toAdd">Text to add</param>
  2642. public void InsertText (string toAdd)
  2643. {
  2644. foreach (char ch in toAdd)
  2645. {
  2646. Key key;
  2647. try
  2648. {
  2649. key = new Key (ch);
  2650. }
  2651. catch (Exception)
  2652. {
  2653. throw new ArgumentException (
  2654. $"Cannot insert character '{ch}' because it does not map to a Key"
  2655. );
  2656. }
  2657. InsertText (key);
  2658. if (NeedsDisplay)
  2659. {
  2660. Adjust ();
  2661. }
  2662. else
  2663. {
  2664. PositionCursor ();
  2665. }
  2666. }
  2667. }
  2668. /// <summary>Loads the contents of the file into the <see cref="TextView"/>.</summary>
  2669. /// <returns><c>true</c>, if file was loaded, <c>false</c> otherwise.</returns>
  2670. /// <param name="path">Path to the file to load.</param>
  2671. public bool Load (string path)
  2672. {
  2673. SetWrapModel ();
  2674. bool res;
  2675. try
  2676. {
  2677. SetWrapModel ();
  2678. res = _model.LoadFile (path);
  2679. _historyText.Clear (Text);
  2680. ResetPosition ();
  2681. }
  2682. finally
  2683. {
  2684. UpdateWrapModel ();
  2685. SetNeedsDisplay ();
  2686. Adjust ();
  2687. }
  2688. UpdateWrapModel ();
  2689. return res;
  2690. }
  2691. /// <summary>Loads the contents of the stream into the <see cref="TextView"/>.</summary>
  2692. /// <returns><c>true</c>, if stream was loaded, <c>false</c> otherwise.</returns>
  2693. /// <param name="stream">Stream to load the contents from.</param>
  2694. public void Load (Stream stream)
  2695. {
  2696. SetWrapModel ();
  2697. _model.LoadStream (stream);
  2698. _historyText.Clear (Text);
  2699. ResetPosition ();
  2700. SetNeedsDisplay ();
  2701. UpdateWrapModel ();
  2702. }
  2703. /// <summary>Loads the contents of the <see cref="RuneCell"/> list into the <see cref="TextView"/>.</summary>
  2704. /// <param name="cells">Rune cells list to load the contents from.</param>
  2705. public void Load (List<RuneCell> cells)
  2706. {
  2707. SetWrapModel ();
  2708. _model.LoadRuneCells (cells, ColorScheme);
  2709. _historyText.Clear (Text);
  2710. ResetPosition ();
  2711. SetNeedsDisplay ();
  2712. UpdateWrapModel ();
  2713. InheritsPreviousColorScheme = true;
  2714. }
  2715. /// <summary>Loads the contents of the list of <see cref="RuneCell"/> list into the <see cref="TextView"/>.</summary>
  2716. /// <param name="cellsList">List of rune cells list to load the contents from.</param>
  2717. public void Load (List<List<RuneCell>> cellsList)
  2718. {
  2719. SetWrapModel ();
  2720. InheritsPreviousColorScheme = true;
  2721. _model.LoadListRuneCells (cellsList, ColorScheme);
  2722. _historyText.Clear (Text);
  2723. ResetPosition ();
  2724. SetNeedsDisplay ();
  2725. UpdateWrapModel ();
  2726. }
  2727. /// <inheritdoc/>
  2728. public override bool MouseEvent (MouseEvent ev)
  2729. {
  2730. if (!ev.Flags.HasFlag (MouseFlags.Button1Clicked)
  2731. && !ev.Flags.HasFlag (MouseFlags.Button1Pressed)
  2732. && !ev.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)
  2733. && !ev.Flags.HasFlag (MouseFlags.Button1Released)
  2734. && !ev.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ButtonShift)
  2735. && !ev.Flags.HasFlag (MouseFlags.WheeledDown)
  2736. && !ev.Flags.HasFlag (MouseFlags.WheeledUp)
  2737. && !ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked)
  2738. && !ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked | MouseFlags.ButtonShift)
  2739. && !ev.Flags.HasFlag (MouseFlags.Button1TripleClicked)
  2740. && !ev.Flags.HasFlag (ContextMenu!.MouseFlags))
  2741. {
  2742. return false;
  2743. }
  2744. if (!CanFocus)
  2745. {
  2746. return true;
  2747. }
  2748. if (!HasFocus)
  2749. {
  2750. SetFocus ();
  2751. }
  2752. _continuousFind = false;
  2753. // Give autocomplete first opportunity to respond to mouse clicks
  2754. if (SelectedLength == 0 && Autocomplete.MouseEvent (ev, true))
  2755. {
  2756. return true;
  2757. }
  2758. if (ev.Flags == MouseFlags.Button1Clicked)
  2759. {
  2760. if (_shiftSelecting && !_isButtonShift)
  2761. {
  2762. StopSelecting ();
  2763. }
  2764. ProcessMouseClick (ev, out _);
  2765. if (Used)
  2766. {
  2767. PositionCursor ();
  2768. }
  2769. else
  2770. {
  2771. SetNeedsDisplay ();
  2772. }
  2773. _lastWasKill = false;
  2774. _columnTrack = CurrentColumn;
  2775. }
  2776. else if (ev.Flags == MouseFlags.WheeledDown)
  2777. {
  2778. _lastWasKill = false;
  2779. _columnTrack = CurrentColumn;
  2780. ScrollTo (_topRow + 1);
  2781. }
  2782. else if (ev.Flags == MouseFlags.WheeledUp)
  2783. {
  2784. _lastWasKill = false;
  2785. _columnTrack = CurrentColumn;
  2786. ScrollTo (_topRow - 1);
  2787. }
  2788. else if (ev.Flags == MouseFlags.WheeledRight)
  2789. {
  2790. _lastWasKill = false;
  2791. _columnTrack = CurrentColumn;
  2792. ScrollTo (_leftColumn + 1, false);
  2793. }
  2794. else if (ev.Flags == MouseFlags.WheeledLeft)
  2795. {
  2796. _lastWasKill = false;
  2797. _columnTrack = CurrentColumn;
  2798. ScrollTo (_leftColumn - 1, false);
  2799. }
  2800. else if (ev.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))
  2801. {
  2802. ProcessMouseClick (ev, out List<RuneCell> line);
  2803. PositionCursor ();
  2804. if (_model.Count > 0 && _shiftSelecting && Selecting)
  2805. {
  2806. if (CurrentRow - _topRow + BottomOffset >= Frame.Height - 1 && _model.Count + BottomOffset > _topRow + CurrentRow)
  2807. {
  2808. ScrollTo (_topRow + Frame.Height);
  2809. }
  2810. else if (_topRow > 0 && CurrentRow <= _topRow)
  2811. {
  2812. ScrollTo (_topRow - Frame.Height);
  2813. }
  2814. else if (ev.Y >= Frame.Height)
  2815. {
  2816. ScrollTo (_model.Count + BottomOffset);
  2817. }
  2818. else if (ev.Y < 0 && _topRow > 0)
  2819. {
  2820. ScrollTo (0);
  2821. }
  2822. if (CurrentColumn - _leftColumn + RightOffset >= Frame.Width - 1 && line.Count + RightOffset > _leftColumn + CurrentColumn)
  2823. {
  2824. ScrollTo (_leftColumn + Frame.Width, false);
  2825. }
  2826. else if (_leftColumn > 0 && CurrentColumn <= _leftColumn)
  2827. {
  2828. ScrollTo (_leftColumn - Frame.Width, false);
  2829. }
  2830. else if (ev.X >= Frame.Width)
  2831. {
  2832. ScrollTo (line.Count + RightOffset, false);
  2833. }
  2834. else if (ev.X < 0 && _leftColumn > 0)
  2835. {
  2836. ScrollTo (0, false);
  2837. }
  2838. }
  2839. _lastWasKill = false;
  2840. _columnTrack = CurrentColumn;
  2841. }
  2842. else if (ev.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ButtonShift))
  2843. {
  2844. if (!_shiftSelecting)
  2845. {
  2846. _isButtonShift = true;
  2847. StartSelecting ();
  2848. }
  2849. ProcessMouseClick (ev, out _);
  2850. PositionCursor ();
  2851. _lastWasKill = false;
  2852. _columnTrack = CurrentColumn;
  2853. }
  2854. else if (ev.Flags.HasFlag (MouseFlags.Button1Pressed))
  2855. {
  2856. if (_shiftSelecting)
  2857. {
  2858. _clickWithSelecting = true;
  2859. StopSelecting ();
  2860. }
  2861. ProcessMouseClick (ev, out _);
  2862. PositionCursor ();
  2863. if (!Selecting)
  2864. {
  2865. StartSelecting ();
  2866. }
  2867. _lastWasKill = false;
  2868. _columnTrack = CurrentColumn;
  2869. if (Application.MouseGrabView is null)
  2870. {
  2871. Application.GrabMouse (this);
  2872. }
  2873. }
  2874. else if (ev.Flags.HasFlag (MouseFlags.Button1Released))
  2875. {
  2876. Application.UngrabMouse ();
  2877. }
  2878. else if (ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked))
  2879. {
  2880. if (ev.Flags.HasFlag (MouseFlags.ButtonShift))
  2881. {
  2882. if (!Selecting)
  2883. {
  2884. StartSelecting ();
  2885. }
  2886. }
  2887. else if (Selecting)
  2888. {
  2889. StopSelecting ();
  2890. }
  2891. ProcessMouseClick (ev, out List<RuneCell> line);
  2892. (int col, int row)? newPos;
  2893. if (CurrentColumn == line.Count
  2894. || (CurrentColumn > 0 && (line [CurrentColumn - 1].Rune.Value != ' ' || line [CurrentColumn].Rune.Value == ' ')))
  2895. {
  2896. newPos = _model.WordBackward (CurrentColumn, CurrentRow);
  2897. if (newPos.HasValue)
  2898. {
  2899. CurrentColumn = CurrentRow == newPos.Value.row ? newPos.Value.col : 0;
  2900. }
  2901. }
  2902. if (!Selecting)
  2903. {
  2904. StartSelecting ();
  2905. }
  2906. newPos = _model.WordForward (CurrentColumn, CurrentRow);
  2907. if (newPos is { } && newPos.HasValue)
  2908. {
  2909. CurrentColumn = CurrentRow == newPos.Value.row ? newPos.Value.col : line.Count;
  2910. }
  2911. PositionCursor ();
  2912. _lastWasKill = false;
  2913. _columnTrack = CurrentColumn;
  2914. }
  2915. else if (ev.Flags.HasFlag (MouseFlags.Button1TripleClicked))
  2916. {
  2917. if (Selecting)
  2918. {
  2919. StopSelecting ();
  2920. }
  2921. ProcessMouseClick (ev, out List<RuneCell> line);
  2922. CurrentColumn = 0;
  2923. if (!Selecting)
  2924. {
  2925. StartSelecting ();
  2926. }
  2927. CurrentColumn = line.Count;
  2928. PositionCursor ();
  2929. _lastWasKill = false;
  2930. _columnTrack = CurrentColumn;
  2931. }
  2932. else if (ev.Flags == ContextMenu!.MouseFlags)
  2933. {
  2934. ContextMenu.Position = new Point (ev.X + 2, ev.Y + 2);
  2935. ShowContextMenu ();
  2936. }
  2937. return true;
  2938. }
  2939. /// <summary>Will scroll the <see cref="TextView"/> to the last line and position the cursor there.</summary>
  2940. public void MoveEnd ()
  2941. {
  2942. CurrentRow = _model.Count - 1;
  2943. List<RuneCell> line = GetCurrentLine ();
  2944. CurrentColumn = line.Count;
  2945. TrackColumn ();
  2946. PositionCursor ();
  2947. }
  2948. /// <summary>Will scroll the <see cref="TextView"/> to the first line and position the cursor there.</summary>
  2949. public void MoveHome ()
  2950. {
  2951. CurrentRow = 0;
  2952. _topRow = 0;
  2953. CurrentColumn = 0;
  2954. _leftColumn = 0;
  2955. TrackColumn ();
  2956. PositionCursor ();
  2957. SetNeedsDisplay ();
  2958. }
  2959. /// <summary>
  2960. /// Called when the contents of the TextView change. E.g. when the user types text or deletes text. Raises the
  2961. /// <see cref="ContentsChanged"/> event.
  2962. /// </summary>
  2963. public virtual void OnContentsChanged ()
  2964. {
  2965. ContentsChanged?.Invoke (this, new ContentsChangedEventArgs (CurrentRow, CurrentColumn));
  2966. ProcessInheritsPreviousColorScheme (CurrentRow, CurrentColumn);
  2967. ProcessAutocomplete ();
  2968. }
  2969. /// <inheritdoc/>
  2970. public override void OnDrawContent (Rect contentArea)
  2971. {
  2972. _isDrawing = true;
  2973. SetNormalColor ();
  2974. (int width, int height) offB = OffSetBackground ();
  2975. int right = Frame.Width + offB.width + RightOffset;
  2976. int bottom = Frame.Height + offB.height + BottomOffset;
  2977. var row = 0;
  2978. for (int idxRow = _topRow; idxRow < _model.Count; idxRow++)
  2979. {
  2980. List<RuneCell> line = _model.GetLine (idxRow);
  2981. int lineRuneCount = line.Count;
  2982. var col = 0;
  2983. Move (0, row);
  2984. for (int idxCol = _leftColumn; idxCol < lineRuneCount; idxCol++)
  2985. {
  2986. Rune rune = idxCol >= lineRuneCount ? (Rune)' ' : line [idxCol].Rune;
  2987. int cols = rune.GetColumns ();
  2988. if (idxCol < line.Count && Selecting && PointInSelection (idxCol, idxRow))
  2989. {
  2990. OnDrawSelectionColor (line, idxCol, idxRow);
  2991. }
  2992. else if (idxCol == CurrentColumn && idxRow == CurrentRow && !Selecting && !Used && HasFocus && idxCol < lineRuneCount)
  2993. {
  2994. OnDrawUsedColor (line, idxCol, idxRow);
  2995. }
  2996. else if (ReadOnly)
  2997. {
  2998. OnDrawReadOnlyColor (line, idxCol, idxRow);
  2999. }
  3000. else
  3001. {
  3002. OnDrawNormalColor (line, idxCol, idxRow);
  3003. }
  3004. if (rune.Value == '\t')
  3005. {
  3006. cols += TabWidth + 1;
  3007. if (col + cols > right)
  3008. {
  3009. cols = right - col;
  3010. }
  3011. for (var i = 0; i < cols; i++)
  3012. {
  3013. if (col + i < right)
  3014. {
  3015. AddRune (col + i, row, (Rune)' ');
  3016. }
  3017. }
  3018. }
  3019. else
  3020. {
  3021. AddRune (col, row, rune);
  3022. }
  3023. if (!TextModel.SetCol (ref col, contentArea.Right, cols))
  3024. {
  3025. break;
  3026. }
  3027. if (idxCol + 1 < lineRuneCount && col + line [idxCol + 1].Rune.GetColumns () > right)
  3028. {
  3029. break;
  3030. }
  3031. }
  3032. if (col < right)
  3033. {
  3034. SetNormalColor ();
  3035. ClearRegion (col, row, right, row + 1);
  3036. }
  3037. row++;
  3038. }
  3039. if (row < bottom)
  3040. {
  3041. SetNormalColor ();
  3042. ClearRegion (contentArea.Left, row, right, bottom);
  3043. }
  3044. PositionCursor ();
  3045. _isDrawing = false;
  3046. }
  3047. /// <inheritdoc/>
  3048. public override bool OnEnter (View view)
  3049. {
  3050. //TODO: Improve it by handling read only mode of the text field
  3051. Application.Driver.SetCursorVisibility (DesiredCursorVisibility);
  3052. return base.OnEnter (view);
  3053. }
  3054. /// <inheritdoc/>
  3055. public override bool? OnInvokingKeyBindings (Key a)
  3056. {
  3057. if (!a.IsValid)
  3058. {
  3059. return false;
  3060. }
  3061. // Give autocomplete first opportunity to respond to key presses
  3062. if (SelectedLength == 0 && Autocomplete.Suggestions.Count > 0 && Autocomplete.ProcessKey (a))
  3063. {
  3064. return true;
  3065. }
  3066. return base.OnInvokingKeyBindings (a);
  3067. }
  3068. /// <inheritdoc/>
  3069. public override bool OnKeyUp (Key a)
  3070. {
  3071. switch (a.KeyCode)
  3072. {
  3073. case KeyCode.Space | KeyCode.CtrlMask:
  3074. return true;
  3075. }
  3076. return base.OnKeyUp (a);
  3077. }
  3078. /// <inheritdoc/>
  3079. public override bool OnLeave (View view)
  3080. {
  3081. if (Application.MouseGrabView is { } && Application.MouseGrabView == this)
  3082. {
  3083. Application.UngrabMouse ();
  3084. }
  3085. return base.OnLeave (view);
  3086. }
  3087. /// <inheritdoc/>
  3088. public override bool OnProcessKeyDown (Key a)
  3089. {
  3090. if (!CanFocus)
  3091. {
  3092. return true;
  3093. }
  3094. ResetColumnTrack ();
  3095. // Ignore control characters and other special keys
  3096. if (!a.IsKeyCodeAtoZ && (a.KeyCode < KeyCode.Space || a.KeyCode > KeyCode.CharMask))
  3097. {
  3098. return false;
  3099. }
  3100. InsertText (a);
  3101. DoNeededAction ();
  3102. return true;
  3103. }
  3104. /// <summary>Invoke the <see cref="UnwrappedCursorPosition"/> event with the unwrapped <see cref="CursorPosition"/>.</summary>
  3105. public virtual void OnUnwrappedCursorPosition (int? cRow = null, int? cCol = null)
  3106. {
  3107. int? row = cRow is null ? CurrentRow : cRow;
  3108. int? col = cCol is null ? CurrentColumn : cCol;
  3109. if (cRow is null && cCol is null && _wordWrap)
  3110. {
  3111. row = _wrapManager!.GetModelLineFromWrappedLines (CurrentRow);
  3112. col = _wrapManager.GetModelColFromWrappedLines (CurrentRow, CurrentColumn);
  3113. }
  3114. UnwrappedCursorPosition?.Invoke (this, new PointEventArgs (new Point ((int)col, (int)row)));
  3115. }
  3116. /// <summary>Paste the clipboard contents into the current selected position.</summary>
  3117. public void Paste ()
  3118. {
  3119. if (_isReadOnly)
  3120. {
  3121. return;
  3122. }
  3123. SetWrapModel ();
  3124. string? contents = Clipboard.Contents;
  3125. if (_copyWithoutSelection && contents.FirstOrDefault (x => x == '\n' || x == '\r') == 0)
  3126. {
  3127. List<RuneCell> runeList = contents is null ? new List<RuneCell> () : TextModel.ToRuneCellList (contents);
  3128. List<RuneCell> currentLine = GetCurrentLine ();
  3129. _historyText.Add (new List<List<RuneCell>> { new (currentLine) }, CursorPosition);
  3130. List<List<RuneCell>> addedLine = new () { new List<RuneCell> (currentLine), runeList };
  3131. _historyText.Add (
  3132. new List<List<RuneCell>> (addedLine),
  3133. CursorPosition,
  3134. HistoryText.LineStatus.Added
  3135. );
  3136. _model.AddLine (CurrentRow, runeList);
  3137. CurrentRow++;
  3138. _historyText.Add (
  3139. new List<List<RuneCell>> { new (GetCurrentLine ()) },
  3140. CursorPosition,
  3141. HistoryText.LineStatus.Replaced
  3142. );
  3143. SetNeedsDisplay ();
  3144. OnContentsChanged ();
  3145. }
  3146. else
  3147. {
  3148. if (Selecting)
  3149. {
  3150. ClearRegion ();
  3151. }
  3152. _copyWithoutSelection = false;
  3153. InsertAllText (contents);
  3154. if (Selecting)
  3155. {
  3156. _historyText.ReplaceLast (
  3157. new List<List<RuneCell>> { new (GetCurrentLine ()) },
  3158. CursorPosition,
  3159. HistoryText.LineStatus.Original
  3160. );
  3161. }
  3162. SetNeedsDisplay ();
  3163. }
  3164. UpdateWrapModel ();
  3165. Selecting = false;
  3166. DoNeededAction ();
  3167. }
  3168. /// <summary>Positions the cursor on the current row and column</summary>
  3169. public override void PositionCursor ()
  3170. {
  3171. ProcessAutocomplete ();
  3172. if (!CanFocus || !Enabled || Application.Driver is null)
  3173. {
  3174. return;
  3175. }
  3176. if (Selecting)
  3177. {
  3178. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  3179. //var minRow = Math.Min (Math.Max (Math.Min (selectionStartRow, currentRow) - topRow, 0), Frame.Height);
  3180. //var maxRow = Math.Min (Math.Max (Math.Max (selectionStartRow, currentRow) - topRow, 0), Frame.Height);
  3181. //SetNeedsDisplay (new Rect (0, minRow, Frame.Width, maxRow));
  3182. SetNeedsDisplay ();
  3183. }
  3184. List<RuneCell> line = _model.GetLine (CurrentRow);
  3185. var col = 0;
  3186. if (line.Count > 0)
  3187. {
  3188. for (int idx = _leftColumn; idx < line.Count; idx++)
  3189. {
  3190. if (idx >= CurrentColumn)
  3191. {
  3192. break;
  3193. }
  3194. int cols = line [idx].Rune.GetColumns ();
  3195. if (line [idx].Rune.Value == '\t')
  3196. {
  3197. cols += TabWidth + 1;
  3198. }
  3199. if (!TextModel.SetCol (ref col, Frame.Width, cols))
  3200. {
  3201. col = CurrentColumn;
  3202. break;
  3203. }
  3204. }
  3205. }
  3206. int posX = CurrentColumn - _leftColumn;
  3207. int posY = CurrentRow - _topRow;
  3208. if (posX > -1 && col >= posX && posX < Frame.Width - RightOffset && _topRow <= CurrentRow && posY < Frame.Height - BottomOffset)
  3209. {
  3210. ResetCursorVisibility ();
  3211. Move (col, CurrentRow - _topRow);
  3212. }
  3213. else
  3214. {
  3215. SaveCursorVisibility ();
  3216. }
  3217. }
  3218. /// <summary>Redoes the latest changes.</summary>
  3219. public void Redo ()
  3220. {
  3221. if (ReadOnly)
  3222. {
  3223. return;
  3224. }
  3225. _historyText.Redo ();
  3226. }
  3227. /// <summary>Replaces all the text based on the match case.</summary>
  3228. /// <param name="textToFind">The text to find.</param>
  3229. /// <param name="matchCase">The match case setting.</param>
  3230. /// <param name="matchWholeWord">The match whole word setting.</param>
  3231. /// <param name="textToReplace">The text to replace.</param>
  3232. /// <returns><c>true</c>If the text was found.<c>false</c>otherwise.</returns>
  3233. public bool ReplaceAllText (
  3234. string textToFind,
  3235. bool matchCase = false,
  3236. bool matchWholeWord = false,
  3237. string? textToReplace = null
  3238. )
  3239. {
  3240. if (_isReadOnly || _model.Count == 0)
  3241. {
  3242. return false;
  3243. }
  3244. SetWrapModel ();
  3245. ResetContinuousFind ();
  3246. (Point current, bool found) foundPos =
  3247. _model.ReplaceAllText (textToFind, matchCase, matchWholeWord, textToReplace);
  3248. return SetFoundText (textToFind, foundPos, textToReplace, false, true);
  3249. }
  3250. /// <summary>
  3251. /// Will scroll the <see cref="TextView"/> to display the specified row at the top if <paramref name="isRow"/> is
  3252. /// true or will scroll the <see cref="TextView"/> to display the specified column at the left if
  3253. /// <paramref name="isRow"/> is false.
  3254. /// </summary>
  3255. /// <param name="idx">
  3256. /// Row that should be displayed at the top or Column that should be displayed at the left, if the value
  3257. /// is negative it will be reset to zero
  3258. /// </param>
  3259. /// <param name="isRow">If true (default) the <paramref name="idx"/> is a row, column otherwise.</param>
  3260. public void ScrollTo (int idx, bool isRow = true)
  3261. {
  3262. if (idx < 0)
  3263. {
  3264. idx = 0;
  3265. }
  3266. if (isRow)
  3267. {
  3268. _topRow = Math.Max (idx > _model.Count - 1 ? _model.Count - 1 : idx, 0);
  3269. }
  3270. else if (!_wordWrap)
  3271. {
  3272. int maxlength =
  3273. _model.GetMaxVisibleLine (_topRow, _topRow + Frame.Height + RightOffset, TabWidth);
  3274. _leftColumn = Math.Max (!_wordWrap && idx > maxlength - 1 ? maxlength - 1 : idx, 0);
  3275. }
  3276. SetNeedsDisplay ();
  3277. }
  3278. /// <summary>Select all text.</summary>
  3279. public void SelectAll ()
  3280. {
  3281. if (_model.Count == 0)
  3282. {
  3283. return;
  3284. }
  3285. StartSelecting ();
  3286. _selectionStartColumn = 0;
  3287. _selectionStartRow = 0;
  3288. CurrentColumn = _model.GetLine (_model.Count - 1).Count;
  3289. CurrentRow = _model.Count - 1;
  3290. SetNeedsDisplay ();
  3291. }
  3292. /// <summary>Raised when the <see cref="Text"/> property of the <see cref="TextView"/> changes.</summary>
  3293. /// <remarks>
  3294. /// The <see cref="Text"/> property of <see cref="TextView"/> only changes when it is explicitly set, not as the
  3295. /// user types. To be notified as the user changes the contents of the TextView see <see cref="IsDirty"/>.
  3296. /// </remarks>
  3297. public event EventHandler? TextChanged;
  3298. /// <summary>Undoes the latest changes.</summary>
  3299. public void Undo ()
  3300. {
  3301. if (ReadOnly)
  3302. {
  3303. return;
  3304. }
  3305. _historyText.Undo ();
  3306. }
  3307. /// <summary>Invoked with the unwrapped <see cref="CursorPosition"/>.</summary>
  3308. public event EventHandler<PointEventArgs>? UnwrappedCursorPosition;
  3309. /// <summary>
  3310. /// Sets the <see cref="View.Driver"/> to an appropriate color for rendering the given <paramref name="idxCol"/>
  3311. /// of the current <paramref name="line"/>. Override to provide custom coloring by calling
  3312. /// <see cref="ConsoleDriver.SetAttribute(Attribute)"/> Defaults to <see cref="ColorScheme.Normal"/>.
  3313. /// </summary>
  3314. /// <param name="line">The line.</param>
  3315. /// <param name="idxCol">The col index.</param>
  3316. /// <param name="idxRow">The row index.</param>
  3317. protected virtual void OnDrawNormalColor (List<RuneCell> line, int idxCol, int idxRow)
  3318. {
  3319. (int Row, int Col) unwrappedPos = GetUnwrappedPosition (idxRow, idxCol);
  3320. var ev = new RuneCellEventArgs (line, idxCol, unwrappedPos);
  3321. DrawNormalColor?.Invoke (this, ev);
  3322. if (line [idxCol].ColorScheme is { })
  3323. {
  3324. ColorScheme? colorScheme = line [idxCol].ColorScheme;
  3325. Driver.SetAttribute (Enabled ? colorScheme!.Focus : colorScheme!.Disabled);
  3326. }
  3327. else
  3328. {
  3329. Driver.SetAttribute (GetNormalColor ());
  3330. }
  3331. }
  3332. /// <summary>
  3333. /// Sets the <see cref="View.Driver"/> to an appropriate color for rendering the given <paramref name="idxCol"/>
  3334. /// of the current <paramref name="line"/>. Override to provide custom coloring by calling
  3335. /// <see cref="ConsoleDriver.SetAttribute(Attribute)"/> Defaults to <see cref="ColorScheme.Focus"/>.
  3336. /// </summary>
  3337. /// <param name="line">The line.</param>
  3338. /// <param name="idxCol">The col index.</param>
  3339. /// ///
  3340. /// <param name="idxRow">The row index.</param>
  3341. protected virtual void OnDrawReadOnlyColor (List<RuneCell> line, int idxCol, int idxRow)
  3342. {
  3343. (int Row, int Col) unwrappedPos = GetUnwrappedPosition (idxRow, idxCol);
  3344. var ev = new RuneCellEventArgs (line, idxCol, unwrappedPos);
  3345. DrawReadOnlyColor?.Invoke (this, ev);
  3346. ColorScheme? colorScheme = line [idxCol].ColorScheme is { } ? line [idxCol].ColorScheme : ColorScheme;
  3347. Attribute attribute;
  3348. if (colorScheme!.Disabled.Foreground == colorScheme.Focus.Background)
  3349. {
  3350. attribute = new Attribute (colorScheme.Focus.Foreground, colorScheme.Focus.Background);
  3351. }
  3352. else
  3353. {
  3354. attribute = new Attribute (colorScheme.Disabled.Foreground, colorScheme.Focus.Background);
  3355. }
  3356. Driver.SetAttribute (attribute);
  3357. }
  3358. /// <summary>
  3359. /// Sets the <see cref="View.Driver"/> to an appropriate color for rendering the given <paramref name="idxCol"/>
  3360. /// of the current <paramref name="line"/>. Override to provide custom coloring by calling
  3361. /// <see cref="ConsoleDriver.SetAttribute(Attribute)"/> Defaults to <see cref="ColorScheme.Focus"/>.
  3362. /// </summary>
  3363. /// <param name="line">The line.</param>
  3364. /// <param name="idxCol">The col index.</param>
  3365. /// ///
  3366. /// <param name="idxRow">The row index.</param>
  3367. protected virtual void OnDrawSelectionColor (List<RuneCell> line, int idxCol, int idxRow)
  3368. {
  3369. (int Row, int Col) unwrappedPos = GetUnwrappedPosition (idxRow, idxCol);
  3370. var ev = new RuneCellEventArgs (line, idxCol, unwrappedPos);
  3371. DrawSelectionColor?.Invoke (this, ev);
  3372. if (line [idxCol].ColorScheme is { })
  3373. {
  3374. ColorScheme? colorScheme = line [idxCol].ColorScheme;
  3375. Driver.SetAttribute (
  3376. new Attribute (colorScheme!.Focus.Background, colorScheme.Focus.Foreground)
  3377. );
  3378. }
  3379. else
  3380. {
  3381. Driver.SetAttribute (
  3382. new Attribute (
  3383. ColorScheme.Focus.Background,
  3384. ColorScheme.Focus.Foreground
  3385. )
  3386. );
  3387. }
  3388. }
  3389. /// <summary>
  3390. /// Sets the <see cref="View.Driver"/> to an appropriate color for rendering the given <paramref name="idxCol"/>
  3391. /// of the current <paramref name="line"/>. Override to provide custom coloring by calling
  3392. /// <see cref="ConsoleDriver.SetAttribute(Attribute)"/> Defaults to <see cref="ColorScheme.HotFocus"/>.
  3393. /// </summary>
  3394. /// <param name="line">The line.</param>
  3395. /// <param name="idxCol">The col index.</param>
  3396. /// ///
  3397. /// <param name="idxRow">The row index.</param>
  3398. protected virtual void OnDrawUsedColor (List<RuneCell> line, int idxCol, int idxRow)
  3399. {
  3400. (int Row, int Col) unwrappedPos = GetUnwrappedPosition (idxRow, idxCol);
  3401. var ev = new RuneCellEventArgs (line, idxCol, unwrappedPos);
  3402. DrawUsedColor?.Invoke (this, ev);
  3403. if (line [idxCol].ColorScheme is { })
  3404. {
  3405. ColorScheme? colorScheme = line [idxCol].ColorScheme;
  3406. SetValidUsedColor (colorScheme!);
  3407. }
  3408. else
  3409. {
  3410. SetValidUsedColor (ColorScheme);
  3411. }
  3412. }
  3413. /// <summary>
  3414. /// Sets the driver to the default color for the control where no text is being rendered. Defaults to
  3415. /// <see cref="ColorScheme.Normal"/>.
  3416. /// </summary>
  3417. protected virtual void SetNormalColor () { Driver.SetAttribute (GetNormalColor ()); }
  3418. private void Adjust ()
  3419. {
  3420. (int width, int height) offB = OffSetBackground ();
  3421. List<RuneCell> line = GetCurrentLine ();
  3422. bool need = NeedsDisplay || _wrapNeeded || !Used;
  3423. (int size, int length) tSize = TextModel.DisplaySize (line, -1, -1, false, TabWidth);
  3424. (int size, int length) dSize = TextModel.DisplaySize (line, _leftColumn, CurrentColumn, true, TabWidth);
  3425. if (!_wordWrap && CurrentColumn < _leftColumn)
  3426. {
  3427. _leftColumn = CurrentColumn;
  3428. need = true;
  3429. }
  3430. else if (!_wordWrap
  3431. && (CurrentColumn - _leftColumn + RightOffset > Frame.Width + offB.width || dSize.size + RightOffset >= Frame.Width + offB.width))
  3432. {
  3433. _leftColumn = TextModel.CalculateLeftColumn (
  3434. line,
  3435. _leftColumn,
  3436. CurrentColumn,
  3437. Frame.Width + offB.width - RightOffset,
  3438. TabWidth
  3439. );
  3440. need = true;
  3441. }
  3442. else if ((_wordWrap && _leftColumn > 0) || (dSize.size + RightOffset < Frame.Width + offB.width && tSize.size + RightOffset < Frame.Width + offB.width))
  3443. {
  3444. if (_leftColumn > 0)
  3445. {
  3446. _leftColumn = 0;
  3447. need = true;
  3448. }
  3449. }
  3450. if (CurrentRow < _topRow)
  3451. {
  3452. _topRow = CurrentRow;
  3453. need = true;
  3454. }
  3455. else if (CurrentRow - _topRow + BottomOffset >= Frame.Height + offB.height)
  3456. {
  3457. _topRow = Math.Min (Math.Max (CurrentRow - Frame.Height + 1 + BottomOffset, 0), CurrentRow);
  3458. need = true;
  3459. }
  3460. else if (_topRow > 0 && CurrentRow < _topRow)
  3461. {
  3462. _topRow = Math.Max (_topRow - 1, 0);
  3463. need = true;
  3464. }
  3465. if (need)
  3466. {
  3467. if (_wrapNeeded)
  3468. {
  3469. WrapTextModel ();
  3470. _wrapNeeded = false;
  3471. }
  3472. SetNeedsDisplay ();
  3473. }
  3474. else
  3475. {
  3476. PositionCursor ();
  3477. }
  3478. OnUnwrappedCursorPosition ();
  3479. }
  3480. private void AppendClipboard (string text) { Clipboard.Contents += text; }
  3481. private MenuBarItem BuildContextMenuBarItem ()
  3482. {
  3483. return new MenuBarItem (
  3484. new MenuItem []
  3485. {
  3486. new (
  3487. Strings.ctxSelectAll,
  3488. "",
  3489. SelectAll,
  3490. null,
  3491. null,
  3492. (KeyCode)KeyBindings.GetKeyFromCommands (Command.SelectAll)
  3493. ),
  3494. new (
  3495. Strings.ctxDeleteAll,
  3496. "",
  3497. DeleteAll,
  3498. null,
  3499. null,
  3500. (KeyCode)KeyBindings.GetKeyFromCommands (Command.DeleteAll)
  3501. ),
  3502. new (
  3503. Strings.ctxCopy,
  3504. "",
  3505. Copy,
  3506. null,
  3507. null,
  3508. (KeyCode)KeyBindings.GetKeyFromCommands (Command.Copy)
  3509. ),
  3510. new (
  3511. Strings.ctxCut,
  3512. "",
  3513. Cut,
  3514. null,
  3515. null,
  3516. (KeyCode)KeyBindings.GetKeyFromCommands (Command.Cut)
  3517. ),
  3518. new (
  3519. Strings.ctxPaste,
  3520. "",
  3521. Paste,
  3522. null,
  3523. null,
  3524. (KeyCode)KeyBindings.GetKeyFromCommands (Command.Paste)
  3525. ),
  3526. new (
  3527. Strings.ctxUndo,
  3528. "",
  3529. Undo,
  3530. null,
  3531. null,
  3532. (KeyCode)KeyBindings.GetKeyFromCommands (Command.Undo)
  3533. ),
  3534. new (
  3535. Strings.ctxRedo,
  3536. "",
  3537. Redo,
  3538. null,
  3539. null,
  3540. (KeyCode)KeyBindings.GetKeyFromCommands (Command.Redo)
  3541. )
  3542. }
  3543. );
  3544. }
  3545. private void ClearRegion (int left, int top, int right, int bottom)
  3546. {
  3547. for (int row = top; row < bottom; row++)
  3548. {
  3549. Move (left, row);
  3550. for (int col = left; col < right; col++)
  3551. {
  3552. AddRune (col, row, (Rune)' ');
  3553. }
  3554. }
  3555. }
  3556. //
  3557. // Clears the contents of the selected region
  3558. //
  3559. private void ClearRegion ()
  3560. {
  3561. SetWrapModel ();
  3562. long start, end;
  3563. long currentEncoded = ((long)(uint)CurrentRow << 32) | (uint)CurrentColumn;
  3564. GetEncodedRegionBounds (out start, out end);
  3565. var startRow = (int)(start >> 32);
  3566. var maxrow = (int)(end >> 32);
  3567. var startCol = (int)(start & 0xffffffff);
  3568. var endCol = (int)(end & 0xffffffff);
  3569. List<RuneCell> line = _model.GetLine (startRow);
  3570. _historyText.Add (new List<List<RuneCell>> { new (line) }, new Point (startCol, startRow));
  3571. List<List<RuneCell>> removedLines = new ();
  3572. if (startRow == maxrow)
  3573. {
  3574. removedLines.Add (new List<RuneCell> (line));
  3575. line.RemoveRange (startCol, endCol - startCol);
  3576. CurrentColumn = startCol;
  3577. if (_wordWrap)
  3578. {
  3579. SetNeedsDisplay ();
  3580. }
  3581. else
  3582. {
  3583. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  3584. //SetNeedsDisplay (new Rect (0, startRow - topRow, Frame.Width, startRow - topRow + 1));
  3585. SetNeedsDisplay ();
  3586. }
  3587. _historyText.Add (
  3588. new List<List<RuneCell>> (removedLines),
  3589. CursorPosition,
  3590. HistoryText.LineStatus.Removed
  3591. );
  3592. UpdateWrapModel ();
  3593. return;
  3594. }
  3595. removedLines.Add (new List<RuneCell> (line));
  3596. line.RemoveRange (startCol, line.Count - startCol);
  3597. List<RuneCell> line2 = _model.GetLine (maxrow);
  3598. line.AddRange (line2.Skip (endCol));
  3599. for (int row = startRow + 1; row <= maxrow; row++)
  3600. {
  3601. removedLines.Add (new List<RuneCell> (_model.GetLine (startRow + 1)));
  3602. _model.RemoveLine (startRow + 1);
  3603. }
  3604. if (currentEncoded == end)
  3605. {
  3606. CurrentRow -= maxrow - startRow;
  3607. }
  3608. CurrentColumn = startCol;
  3609. _historyText.Add (
  3610. new List<List<RuneCell>> (removedLines),
  3611. CursorPosition,
  3612. HistoryText.LineStatus.Removed
  3613. );
  3614. UpdateWrapModel ();
  3615. SetNeedsDisplay ();
  3616. }
  3617. private void ClearSelectedRegion ()
  3618. {
  3619. SetWrapModel ();
  3620. if (!_isReadOnly)
  3621. {
  3622. ClearRegion ();
  3623. }
  3624. UpdateWrapModel ();
  3625. Selecting = false;
  3626. DoNeededAction ();
  3627. }
  3628. private void ContextMenu_KeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.Replace ((KeyCode)e.OldKey, (KeyCode)e.NewKey); }
  3629. private bool DeleteTextBackwards ()
  3630. {
  3631. SetWrapModel ();
  3632. if (CurrentColumn > 0)
  3633. {
  3634. // Delete backwards
  3635. List<RuneCell> currentLine = GetCurrentLine ();
  3636. _historyText.Add (new List<List<RuneCell>> { new (currentLine) }, CursorPosition);
  3637. currentLine.RemoveAt (CurrentColumn - 1);
  3638. if (_wordWrap)
  3639. {
  3640. _wrapNeeded = true;
  3641. }
  3642. CurrentColumn--;
  3643. _historyText.Add (
  3644. new List<List<RuneCell>> { new (currentLine) },
  3645. CursorPosition,
  3646. HistoryText.LineStatus.Replaced
  3647. );
  3648. if (CurrentColumn < _leftColumn)
  3649. {
  3650. _leftColumn--;
  3651. SetNeedsDisplay ();
  3652. }
  3653. else
  3654. {
  3655. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  3656. //SetNeedsDisplay (new Rect (0, currentRow - topRow, 1, Frame.Width));
  3657. SetNeedsDisplay ();
  3658. }
  3659. }
  3660. else
  3661. {
  3662. // Merges the current line with the previous one.
  3663. if (CurrentRow == 0)
  3664. {
  3665. return true;
  3666. }
  3667. int prowIdx = CurrentRow - 1;
  3668. List<RuneCell> prevRow = _model.GetLine (prowIdx);
  3669. _historyText.Add (new List<List<RuneCell>> { new (prevRow) }, CursorPosition);
  3670. List<List<RuneCell>> removedLines = new () { new List<RuneCell> (prevRow) };
  3671. removedLines.Add (new List<RuneCell> (GetCurrentLine ()));
  3672. _historyText.Add (
  3673. removedLines,
  3674. new Point (CurrentColumn, prowIdx),
  3675. HistoryText.LineStatus.Removed
  3676. );
  3677. int prevCount = prevRow.Count;
  3678. _model.GetLine (prowIdx).AddRange (GetCurrentLine ());
  3679. _model.RemoveLine (CurrentRow);
  3680. if (_wordWrap)
  3681. {
  3682. _wrapNeeded = true;
  3683. }
  3684. CurrentRow--;
  3685. _historyText.Add (
  3686. new List<List<RuneCell>> { GetCurrentLine () },
  3687. new Point (CurrentColumn, prowIdx),
  3688. HistoryText.LineStatus.Replaced
  3689. );
  3690. CurrentColumn = prevCount;
  3691. SetNeedsDisplay ();
  3692. }
  3693. UpdateWrapModel ();
  3694. return false;
  3695. }
  3696. private bool DeleteTextForwards ()
  3697. {
  3698. SetWrapModel ();
  3699. List<RuneCell> currentLine = GetCurrentLine ();
  3700. if (CurrentColumn == currentLine.Count)
  3701. {
  3702. if (CurrentRow + 1 == _model.Count)
  3703. {
  3704. UpdateWrapModel ();
  3705. return true;
  3706. }
  3707. _historyText.Add (new List<List<RuneCell>> { new (currentLine) }, CursorPosition);
  3708. List<List<RuneCell>> removedLines = new () { new List<RuneCell> (currentLine) };
  3709. List<RuneCell> nextLine = _model.GetLine (CurrentRow + 1);
  3710. removedLines.Add (new List<RuneCell> (nextLine));
  3711. _historyText.Add (removedLines, CursorPosition, HistoryText.LineStatus.Removed);
  3712. currentLine.AddRange (nextLine);
  3713. _model.RemoveLine (CurrentRow + 1);
  3714. _historyText.Add (
  3715. new List<List<RuneCell>> { new (currentLine) },
  3716. CursorPosition,
  3717. HistoryText.LineStatus.Replaced
  3718. );
  3719. if (_wordWrap)
  3720. {
  3721. _wrapNeeded = true;
  3722. }
  3723. DoSetNeedsDisplay (new Rect (0, CurrentRow - _topRow, Frame.Width, CurrentRow - _topRow + 1));
  3724. }
  3725. else
  3726. {
  3727. _historyText.Add (new List<List<RuneCell>> { new (currentLine) }, CursorPosition);
  3728. currentLine.RemoveAt (CurrentColumn);
  3729. _historyText.Add (
  3730. new List<List<RuneCell>> { new (currentLine) },
  3731. CursorPosition,
  3732. HistoryText.LineStatus.Replaced
  3733. );
  3734. if (_wordWrap)
  3735. {
  3736. _wrapNeeded = true;
  3737. }
  3738. DoSetNeedsDisplay (
  3739. new Rect (
  3740. CurrentColumn - _leftColumn,
  3741. CurrentRow - _topRow,
  3742. Frame.Width,
  3743. CurrentRow - _topRow + 1
  3744. )
  3745. );
  3746. }
  3747. UpdateWrapModel ();
  3748. return false;
  3749. }
  3750. private void DoNeededAction ()
  3751. {
  3752. if (NeedsDisplay)
  3753. {
  3754. Adjust ();
  3755. }
  3756. else
  3757. {
  3758. PositionCursor ();
  3759. }
  3760. }
  3761. private void DoSetNeedsDisplay (Rect rect)
  3762. {
  3763. if (_wrapNeeded)
  3764. {
  3765. SetNeedsDisplay ();
  3766. }
  3767. else
  3768. {
  3769. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  3770. //SetNeedsDisplay (rect);
  3771. SetNeedsDisplay ();
  3772. }
  3773. }
  3774. private IEnumerable<(int col, int row, RuneCell rune)> ForwardIterator (int col, int row)
  3775. {
  3776. if (col < 0 || row < 0)
  3777. {
  3778. yield break;
  3779. }
  3780. if (row >= _model.Count)
  3781. {
  3782. yield break;
  3783. }
  3784. List<RuneCell> line = GetCurrentLine ();
  3785. if (col >= line.Count)
  3786. {
  3787. yield break;
  3788. }
  3789. while (row < _model.Count)
  3790. {
  3791. for (int c = col; c < line.Count; c++)
  3792. {
  3793. yield return (c, row, line [c]);
  3794. }
  3795. col = 0;
  3796. row++;
  3797. line = GetCurrentLine ();
  3798. }
  3799. }
  3800. private void GenerateSuggestions ()
  3801. {
  3802. List<RuneCell> currentLine = GetCurrentLine ();
  3803. int cursorPosition = Math.Min (CurrentColumn, currentLine.Count);
  3804. Autocomplete.Context = new AutocompleteContext (
  3805. currentLine,
  3806. cursorPosition,
  3807. Autocomplete.Context != null
  3808. ? Autocomplete.Context.Canceled
  3809. : false
  3810. );
  3811. Autocomplete.GenerateSuggestions (
  3812. Autocomplete.Context
  3813. );
  3814. }
  3815. // Returns an encoded region start..end (top 32 bits are the row, low32 the column)
  3816. private void GetEncodedRegionBounds (
  3817. out long start,
  3818. out long end,
  3819. int? startRow = null,
  3820. int? startCol = null,
  3821. int? cRow = null,
  3822. int? cCol = null
  3823. )
  3824. {
  3825. long selection;
  3826. long point;
  3827. if (startRow is null || startCol is null || cRow is null || cCol is null)
  3828. {
  3829. selection = ((long)(uint)_selectionStartRow << 32) | (uint)_selectionStartColumn;
  3830. point = ((long)(uint)CurrentRow << 32) | (uint)CurrentColumn;
  3831. }
  3832. else
  3833. {
  3834. selection = ((long)(uint)startRow << 32) | (uint)startCol;
  3835. point = ((long)(uint)cRow << 32) | (uint)cCol;
  3836. }
  3837. if (selection > point)
  3838. {
  3839. start = point;
  3840. end = selection;
  3841. }
  3842. else
  3843. {
  3844. start = selection;
  3845. end = point;
  3846. }
  3847. }
  3848. //
  3849. // Returns a string with the text in the selected
  3850. // region.
  3851. //
  3852. private string GetRegion (
  3853. int? sRow = null,
  3854. int? sCol = null,
  3855. int? cRow = null,
  3856. int? cCol = null,
  3857. TextModel? model = null
  3858. )
  3859. {
  3860. long start, end;
  3861. GetEncodedRegionBounds (out start, out end, sRow, sCol, cRow, cCol);
  3862. if (start == end)
  3863. {
  3864. return string.Empty;
  3865. }
  3866. var startRow = (int)(start >> 32);
  3867. var maxrow = (int)(end >> 32);
  3868. var startCol = (int)(start & 0xffffffff);
  3869. var endCol = (int)(end & 0xffffffff);
  3870. List<RuneCell> line = model is null ? _model.GetLine (startRow) : model.GetLine (startRow);
  3871. if (startRow == maxrow)
  3872. {
  3873. return StringFromRunes (line.GetRange (startCol, endCol - startCol));
  3874. }
  3875. string res = StringFromRunes (line.GetRange (startCol, line.Count - startCol));
  3876. for (int row = startRow + 1; row < maxrow; row++)
  3877. {
  3878. res = res
  3879. + Environment.NewLine
  3880. + StringFromRunes (
  3881. model == null
  3882. ? _model.GetLine (row)
  3883. : model.GetLine (row)
  3884. );
  3885. }
  3886. line = model is null ? _model.GetLine (maxrow) : model.GetLine (maxrow);
  3887. res = res + Environment.NewLine + StringFromRunes (line.GetRange (0, endCol));
  3888. return res;
  3889. }
  3890. private int GetSelectedLength () { return SelectedText.Length; }
  3891. private string GetSelectedRegion ()
  3892. {
  3893. int cRow = CurrentRow;
  3894. int cCol = CurrentColumn;
  3895. int startRow = _selectionStartRow;
  3896. int startCol = _selectionStartColumn;
  3897. TextModel model = _model;
  3898. if (_wordWrap)
  3899. {
  3900. cRow = _wrapManager!.GetModelLineFromWrappedLines (CurrentRow);
  3901. cCol = _wrapManager.GetModelColFromWrappedLines (CurrentRow, CurrentColumn);
  3902. startRow = _wrapManager.GetModelLineFromWrappedLines (_selectionStartRow);
  3903. startCol = _wrapManager.GetModelColFromWrappedLines (_selectionStartRow, _selectionStartColumn);
  3904. model = _wrapManager.Model;
  3905. }
  3906. OnUnwrappedCursorPosition (cRow, cCol);
  3907. return GetRegion (startRow, startCol, cRow, cCol, model);
  3908. }
  3909. private (int Row, int Col) GetUnwrappedPosition (int line, int col)
  3910. {
  3911. if (WordWrap)
  3912. {
  3913. return new ValueTuple<int, int> (
  3914. _wrapManager!.GetModelLineFromWrappedLines (line),
  3915. _wrapManager.GetModelColFromWrappedLines (line, col)
  3916. );
  3917. }
  3918. return new ValueTuple<int, int> (line, col);
  3919. }
  3920. private void HistoryText_ChangeText (object sender, HistoryText.HistoryTextItem obj)
  3921. {
  3922. SetWrapModel ();
  3923. if (obj is { })
  3924. {
  3925. int startLine = obj.CursorPosition.Y;
  3926. if (obj.RemovedOnAdded is { })
  3927. {
  3928. int offset;
  3929. if (obj.IsUndoing)
  3930. {
  3931. offset = Math.Max (obj.RemovedOnAdded.Lines.Count - obj.Lines.Count, 1);
  3932. }
  3933. else
  3934. {
  3935. offset = obj.RemovedOnAdded.Lines.Count - 1;
  3936. }
  3937. for (var i = 0; i < offset; i++)
  3938. {
  3939. if (Lines > obj.RemovedOnAdded.CursorPosition.Y)
  3940. {
  3941. _model.RemoveLine (obj.RemovedOnAdded.CursorPosition.Y);
  3942. }
  3943. else
  3944. {
  3945. break;
  3946. }
  3947. }
  3948. }
  3949. for (var i = 0; i < obj.Lines.Count; i++)
  3950. {
  3951. if (i == 0)
  3952. {
  3953. _model.ReplaceLine (startLine, obj.Lines [i]);
  3954. }
  3955. else if ((obj.IsUndoing && obj.LineStatus == HistoryText.LineStatus.Removed)
  3956. || (!obj.IsUndoing && obj.LineStatus == HistoryText.LineStatus.Added))
  3957. {
  3958. _model.AddLine (startLine, obj.Lines [i]);
  3959. }
  3960. else if (Lines > obj.CursorPosition.Y + 1)
  3961. {
  3962. _model.RemoveLine (obj.CursorPosition.Y + 1);
  3963. }
  3964. startLine++;
  3965. }
  3966. CursorPosition = obj.FinalCursorPosition;
  3967. }
  3968. UpdateWrapModel ();
  3969. Adjust ();
  3970. OnContentsChanged ();
  3971. }
  3972. private void Insert (RuneCell cell)
  3973. {
  3974. List<RuneCell> line = GetCurrentLine ();
  3975. if (Used)
  3976. {
  3977. line.Insert (Math.Min (CurrentColumn, line.Count), cell);
  3978. }
  3979. else
  3980. {
  3981. if (CurrentColumn < line.Count)
  3982. {
  3983. line.RemoveAt (CurrentColumn);
  3984. }
  3985. line.Insert (Math.Min (CurrentColumn, line.Count), cell);
  3986. }
  3987. int prow = CurrentRow - _topRow;
  3988. if (!_wrapNeeded)
  3989. {
  3990. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  3991. //SetNeedsDisplay (new Rect (0, prow, Math.Max (Frame.Width, 0), Math.Max (prow + 1, 0)));
  3992. SetNeedsDisplay ();
  3993. }
  3994. }
  3995. private void InsertAllText (string text)
  3996. {
  3997. if (string.IsNullOrEmpty (text))
  3998. {
  3999. return;
  4000. }
  4001. List<List<RuneCell>> lines = TextModel.StringToLinesOfRuneCells (text);
  4002. if (lines.Count == 0)
  4003. {
  4004. return;
  4005. }
  4006. SetWrapModel ();
  4007. List<RuneCell> line = GetCurrentLine ();
  4008. _historyText.Add (new List<List<RuneCell>> { new (line) }, CursorPosition);
  4009. // Optimize single line
  4010. if (lines.Count == 1)
  4011. {
  4012. line.InsertRange (CurrentColumn, lines [0]);
  4013. CurrentColumn += lines [0].Count;
  4014. _historyText.Add (
  4015. new List<List<RuneCell>> { new (line) },
  4016. CursorPosition,
  4017. HistoryText.LineStatus.Replaced
  4018. );
  4019. if (!_wordWrap && CurrentColumn - _leftColumn > Frame.Width)
  4020. {
  4021. _leftColumn = Math.Max (CurrentColumn - Frame.Width + 1, 0);
  4022. }
  4023. if (_wordWrap)
  4024. {
  4025. SetNeedsDisplay ();
  4026. }
  4027. else
  4028. {
  4029. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  4030. //SetNeedsDisplay (new Rect (0, currentRow - topRow, Frame.Width, Math.Max (currentRow - topRow + 1, 0)));
  4031. SetNeedsDisplay ();
  4032. }
  4033. UpdateWrapModel ();
  4034. OnContentsChanged ();
  4035. return;
  4036. }
  4037. List<RuneCell>? rest = null;
  4038. var lastp = 0;
  4039. if (_model.Count > 0 && line.Count > 0 && !_copyWithoutSelection)
  4040. {
  4041. // Keep a copy of the rest of the line
  4042. int restCount = line.Count - CurrentColumn;
  4043. rest = line.GetRange (CurrentColumn, restCount);
  4044. line.RemoveRange (CurrentColumn, restCount);
  4045. }
  4046. // First line is inserted at the current location, the rest is appended
  4047. line.InsertRange (CurrentColumn, lines [0]);
  4048. //model.AddLine (currentRow, lines [0]);
  4049. List<List<RuneCell>> addedLines = new () { new List<RuneCell> (line) };
  4050. for (var i = 1; i < lines.Count; i++)
  4051. {
  4052. _model.AddLine (CurrentRow + i, lines [i]);
  4053. addedLines.Add (new List<RuneCell> (lines [i]));
  4054. }
  4055. if (rest is { })
  4056. {
  4057. List<RuneCell> last = _model.GetLine (CurrentRow + lines.Count - 1);
  4058. lastp = last.Count;
  4059. last.InsertRange (last.Count, rest);
  4060. addedLines.Last ().InsertRange (addedLines.Last ().Count, rest);
  4061. }
  4062. _historyText.Add (addedLines, CursorPosition, HistoryText.LineStatus.Added);
  4063. // Now adjust column and row positions
  4064. CurrentRow += lines.Count - 1;
  4065. CurrentColumn = rest is { } ? lastp : lines [lines.Count - 1].Count;
  4066. Adjust ();
  4067. _historyText.Add (
  4068. new List<List<RuneCell>> { new (line) },
  4069. CursorPosition,
  4070. HistoryText.LineStatus.Replaced
  4071. );
  4072. UpdateWrapModel ();
  4073. OnContentsChanged ();
  4074. }
  4075. private bool InsertText (Key a, ColorScheme? colorScheme = null)
  4076. {
  4077. //So that special keys like tab can be processed
  4078. if (_isReadOnly)
  4079. {
  4080. return true;
  4081. }
  4082. SetWrapModel ();
  4083. _historyText.Add (new List<List<RuneCell>> { new (GetCurrentLine ()) }, CursorPosition);
  4084. if (Selecting)
  4085. {
  4086. ClearSelectedRegion ();
  4087. }
  4088. if ((uint)a.KeyCode == '\n')
  4089. {
  4090. _model.AddLine (CurrentRow + 1, new List<RuneCell> ());
  4091. CurrentRow++;
  4092. CurrentColumn = 0;
  4093. }
  4094. else if ((uint)a.KeyCode == '\r')
  4095. {
  4096. CurrentColumn = 0;
  4097. }
  4098. else
  4099. {
  4100. if (Used)
  4101. {
  4102. Insert (new RuneCell { Rune = a.AsRune, ColorScheme = colorScheme });
  4103. CurrentColumn++;
  4104. if (CurrentColumn >= _leftColumn + Frame.Width)
  4105. {
  4106. _leftColumn++;
  4107. SetNeedsDisplay ();
  4108. }
  4109. }
  4110. else
  4111. {
  4112. Insert (new RuneCell { Rune = a.AsRune, ColorScheme = colorScheme });
  4113. CurrentColumn++;
  4114. }
  4115. }
  4116. _historyText.Add (
  4117. new List<List<RuneCell>> { new (GetCurrentLine ()) },
  4118. CursorPosition,
  4119. HistoryText.LineStatus.Replaced
  4120. );
  4121. UpdateWrapModel ();
  4122. OnContentsChanged ();
  4123. return true;
  4124. }
  4125. private void KillToEndOfLine ()
  4126. {
  4127. if (_isReadOnly)
  4128. {
  4129. return;
  4130. }
  4131. if (_model.Count == 1 && GetCurrentLine ().Count == 0)
  4132. {
  4133. // Prevents from adding line feeds if there is no more lines.
  4134. return;
  4135. }
  4136. SetWrapModel ();
  4137. List<RuneCell> currentLine = GetCurrentLine ();
  4138. var setLastWasKill = true;
  4139. if (currentLine.Count > 0 && CurrentColumn == currentLine.Count)
  4140. {
  4141. UpdateWrapModel ();
  4142. DeleteTextForwards ();
  4143. return;
  4144. }
  4145. _historyText.Add (new List<List<RuneCell>> { new (currentLine) }, CursorPosition);
  4146. if (currentLine.Count == 0)
  4147. {
  4148. if (CurrentRow < _model.Count - 1)
  4149. {
  4150. List<List<RuneCell>> removedLines = new () { new List<RuneCell> (currentLine) };
  4151. _model.RemoveLine (CurrentRow);
  4152. removedLines.Add (new List<RuneCell> (GetCurrentLine ()));
  4153. _historyText.Add (
  4154. new List<List<RuneCell>> (removedLines),
  4155. CursorPosition,
  4156. HistoryText.LineStatus.Removed
  4157. );
  4158. }
  4159. if (_model.Count > 0 || _lastWasKill)
  4160. {
  4161. string val = Environment.NewLine;
  4162. if (_lastWasKill)
  4163. {
  4164. AppendClipboard (val);
  4165. }
  4166. else
  4167. {
  4168. SetClipboard (val);
  4169. }
  4170. }
  4171. if (_model.Count == 0)
  4172. {
  4173. // Prevents from adding line feeds if there is no more lines.
  4174. setLastWasKill = false;
  4175. }
  4176. }
  4177. else
  4178. {
  4179. int restCount = currentLine.Count - CurrentColumn;
  4180. List<RuneCell> rest = currentLine.GetRange (CurrentColumn, restCount);
  4181. var val = string.Empty;
  4182. val += StringFromRunes (rest);
  4183. if (_lastWasKill)
  4184. {
  4185. AppendClipboard (val);
  4186. }
  4187. else
  4188. {
  4189. SetClipboard (val);
  4190. }
  4191. currentLine.RemoveRange (CurrentColumn, restCount);
  4192. }
  4193. _historyText.Add (
  4194. new List<List<RuneCell>> { new (GetCurrentLine ()) },
  4195. CursorPosition,
  4196. HistoryText.LineStatus.Replaced
  4197. );
  4198. UpdateWrapModel ();
  4199. DoSetNeedsDisplay (new Rect (0, CurrentRow - _topRow, Frame.Width, Frame.Height));
  4200. _lastWasKill = setLastWasKill;
  4201. DoNeededAction ();
  4202. }
  4203. private void KillToStartOfLine ()
  4204. {
  4205. if (_isReadOnly)
  4206. {
  4207. return;
  4208. }
  4209. if (_model.Count == 1 && GetCurrentLine ().Count == 0)
  4210. {
  4211. // Prevents from adding line feeds if there is no more lines.
  4212. return;
  4213. }
  4214. SetWrapModel ();
  4215. List<RuneCell> currentLine = GetCurrentLine ();
  4216. var setLastWasKill = true;
  4217. if (currentLine.Count > 0 && CurrentColumn == 0)
  4218. {
  4219. UpdateWrapModel ();
  4220. DeleteTextBackwards ();
  4221. return;
  4222. }
  4223. _historyText.Add (new List<List<RuneCell>> { new (currentLine) }, CursorPosition);
  4224. if (currentLine.Count == 0)
  4225. {
  4226. if (CurrentRow > 0)
  4227. {
  4228. _model.RemoveLine (CurrentRow);
  4229. if (_model.Count > 0 || _lastWasKill)
  4230. {
  4231. string val = Environment.NewLine;
  4232. if (_lastWasKill)
  4233. {
  4234. AppendClipboard (val);
  4235. }
  4236. else
  4237. {
  4238. SetClipboard (val);
  4239. }
  4240. }
  4241. if (_model.Count == 0)
  4242. {
  4243. // Prevents from adding line feeds if there is no more lines.
  4244. setLastWasKill = false;
  4245. }
  4246. CurrentRow--;
  4247. currentLine = _model.GetLine (CurrentRow);
  4248. List<List<RuneCell>> removedLine = new () { new List<RuneCell> (currentLine) };
  4249. removedLine.Add (new List<RuneCell> ());
  4250. _historyText.Add (
  4251. new List<List<RuneCell>> (removedLine),
  4252. CursorPosition,
  4253. HistoryText.LineStatus.Removed
  4254. );
  4255. CurrentColumn = currentLine.Count;
  4256. }
  4257. }
  4258. else
  4259. {
  4260. int restCount = CurrentColumn;
  4261. List<RuneCell> rest = currentLine.GetRange (0, restCount);
  4262. var val = string.Empty;
  4263. val += StringFromRunes (rest);
  4264. if (_lastWasKill)
  4265. {
  4266. AppendClipboard (val);
  4267. }
  4268. else
  4269. {
  4270. SetClipboard (val);
  4271. }
  4272. currentLine.RemoveRange (0, restCount);
  4273. CurrentColumn = 0;
  4274. }
  4275. _historyText.Add (
  4276. new List<List<RuneCell>> { new (GetCurrentLine ()) },
  4277. CursorPosition,
  4278. HistoryText.LineStatus.Replaced
  4279. );
  4280. UpdateWrapModel ();
  4281. DoSetNeedsDisplay (new Rect (0, CurrentRow - _topRow, Frame.Width, Frame.Height));
  4282. _lastWasKill = setLastWasKill;
  4283. DoNeededAction ();
  4284. }
  4285. private void KillWordBackward ()
  4286. {
  4287. if (_isReadOnly)
  4288. {
  4289. return;
  4290. }
  4291. SetWrapModel ();
  4292. List<RuneCell> currentLine = GetCurrentLine ();
  4293. _historyText.Add (new List<List<RuneCell>> { new (GetCurrentLine ()) }, CursorPosition);
  4294. if (CurrentColumn == 0)
  4295. {
  4296. DeleteTextBackwards ();
  4297. _historyText.ReplaceLast (
  4298. new List<List<RuneCell>> { new (GetCurrentLine ()) },
  4299. CursorPosition,
  4300. HistoryText.LineStatus.Replaced
  4301. );
  4302. UpdateWrapModel ();
  4303. return;
  4304. }
  4305. (int col, int row)? newPos = _model.WordBackward (CurrentColumn, CurrentRow);
  4306. if (newPos.HasValue && CurrentRow == newPos.Value.row)
  4307. {
  4308. int restCount = CurrentColumn - newPos.Value.col;
  4309. currentLine.RemoveRange (newPos.Value.col, restCount);
  4310. if (_wordWrap)
  4311. {
  4312. _wrapNeeded = true;
  4313. }
  4314. CurrentColumn = newPos.Value.col;
  4315. }
  4316. else if (newPos.HasValue)
  4317. {
  4318. int restCount = currentLine.Count - CurrentColumn;
  4319. currentLine.RemoveRange (CurrentColumn, restCount);
  4320. if (_wordWrap)
  4321. {
  4322. _wrapNeeded = true;
  4323. }
  4324. CurrentColumn = newPos.Value.col;
  4325. CurrentRow = newPos.Value.row;
  4326. }
  4327. _historyText.Add (
  4328. new List<List<RuneCell>> { new (GetCurrentLine ()) },
  4329. CursorPosition,
  4330. HistoryText.LineStatus.Replaced
  4331. );
  4332. UpdateWrapModel ();
  4333. DoSetNeedsDisplay (new Rect (0, CurrentRow - _topRow, Frame.Width, Frame.Height));
  4334. DoNeededAction ();
  4335. }
  4336. private void KillWordForward ()
  4337. {
  4338. if (_isReadOnly)
  4339. {
  4340. return;
  4341. }
  4342. SetWrapModel ();
  4343. List<RuneCell> currentLine = GetCurrentLine ();
  4344. _historyText.Add (new List<List<RuneCell>> { new (GetCurrentLine ()) }, CursorPosition);
  4345. if (currentLine.Count == 0 || CurrentColumn == currentLine.Count)
  4346. {
  4347. DeleteTextForwards ();
  4348. _historyText.ReplaceLast (
  4349. new List<List<RuneCell>> { new (GetCurrentLine ()) },
  4350. CursorPosition,
  4351. HistoryText.LineStatus.Replaced
  4352. );
  4353. UpdateWrapModel ();
  4354. return;
  4355. }
  4356. (int col, int row)? newPos = _model.WordForward (CurrentColumn, CurrentRow);
  4357. var restCount = 0;
  4358. if (newPos.HasValue && CurrentRow == newPos.Value.row)
  4359. {
  4360. restCount = newPos.Value.col - CurrentColumn;
  4361. currentLine.RemoveRange (CurrentColumn, restCount);
  4362. }
  4363. else if (newPos.HasValue)
  4364. {
  4365. restCount = currentLine.Count - CurrentColumn;
  4366. currentLine.RemoveRange (CurrentColumn, restCount);
  4367. }
  4368. if (_wordWrap)
  4369. {
  4370. _wrapNeeded = true;
  4371. }
  4372. _historyText.Add (
  4373. new List<List<RuneCell>> { new (GetCurrentLine ()) },
  4374. CursorPosition,
  4375. HistoryText.LineStatus.Replaced
  4376. );
  4377. UpdateWrapModel ();
  4378. DoSetNeedsDisplay (new Rect (0, CurrentRow - _topRow, Frame.Width, Frame.Height));
  4379. DoNeededAction ();
  4380. }
  4381. private void Model_LinesLoaded (object sender, EventArgs e)
  4382. {
  4383. // This call is not needed. Model_LinesLoaded gets invoked when
  4384. // model.LoadString (value) is called. LoadString is called from one place
  4385. // (Text.set) and historyText.Clear() is called immediately after.
  4386. // If this call happens, HistoryText_ChangeText will get called multiple times
  4387. // when Text is set, which is wrong.
  4388. //historyText.Clear (Text);
  4389. if (!_multiline && !IsInitialized)
  4390. {
  4391. CurrentColumn = Text.GetRuneCount ();
  4392. _leftColumn = CurrentColumn > Frame.Width + 1 ? CurrentColumn - Frame.Width + 1 : 0;
  4393. }
  4394. }
  4395. private void MoveBottomEnd ()
  4396. {
  4397. ResetAllTrack ();
  4398. if (_shiftSelecting && Selecting)
  4399. {
  4400. StopSelecting ();
  4401. }
  4402. MoveEnd ();
  4403. }
  4404. private void MoveBottomEndExtend ()
  4405. {
  4406. ResetAllTrack ();
  4407. StartSelecting ();
  4408. MoveEnd ();
  4409. }
  4410. private void MoveDown ()
  4411. {
  4412. if (CurrentRow + 1 < _model.Count)
  4413. {
  4414. if (_columnTrack == -1)
  4415. {
  4416. _columnTrack = CurrentColumn;
  4417. }
  4418. CurrentRow++;
  4419. if (CurrentRow + BottomOffset >= _topRow + Frame.Height)
  4420. {
  4421. _topRow++;
  4422. SetNeedsDisplay ();
  4423. }
  4424. TrackColumn ();
  4425. PositionCursor ();
  4426. }
  4427. else if (CurrentRow > Frame.Height)
  4428. {
  4429. Adjust ();
  4430. }
  4431. DoNeededAction ();
  4432. }
  4433. private void MoveEndOfLine ()
  4434. {
  4435. List<RuneCell> currentLine = GetCurrentLine ();
  4436. CurrentColumn = currentLine.Count;
  4437. Adjust ();
  4438. DoNeededAction ();
  4439. }
  4440. private void MoveLeft ()
  4441. {
  4442. if (CurrentColumn > 0)
  4443. {
  4444. CurrentColumn--;
  4445. }
  4446. else
  4447. {
  4448. if (CurrentRow > 0)
  4449. {
  4450. CurrentRow--;
  4451. if (CurrentRow < _topRow)
  4452. {
  4453. _topRow--;
  4454. SetNeedsDisplay ();
  4455. }
  4456. List<RuneCell> currentLine = GetCurrentLine ();
  4457. CurrentColumn = currentLine.Count;
  4458. }
  4459. }
  4460. Adjust ();
  4461. DoNeededAction ();
  4462. }
  4463. private bool MoveNextView ()
  4464. {
  4465. if (Application.OverlappedTop is { })
  4466. {
  4467. return SuperView?.FocusNext () == true;
  4468. }
  4469. return false;
  4470. }
  4471. private void MovePageDown ()
  4472. {
  4473. int nPageDnShift = Frame.Height - 1;
  4474. if (CurrentRow >= 0 && CurrentRow < _model.Count)
  4475. {
  4476. if (_columnTrack == -1)
  4477. {
  4478. _columnTrack = CurrentColumn;
  4479. }
  4480. CurrentRow = CurrentRow + nPageDnShift > _model.Count
  4481. ? _model.Count > 0 ? _model.Count - 1 : 0
  4482. : CurrentRow + nPageDnShift;
  4483. if (_topRow < CurrentRow - nPageDnShift)
  4484. {
  4485. _topRow = CurrentRow >= _model.Count
  4486. ? CurrentRow - nPageDnShift
  4487. : _topRow + nPageDnShift;
  4488. SetNeedsDisplay ();
  4489. }
  4490. TrackColumn ();
  4491. PositionCursor ();
  4492. }
  4493. DoNeededAction ();
  4494. }
  4495. private void MovePageUp ()
  4496. {
  4497. int nPageUpShift = Frame.Height - 1;
  4498. if (CurrentRow > 0)
  4499. {
  4500. if (_columnTrack == -1)
  4501. {
  4502. _columnTrack = CurrentColumn;
  4503. }
  4504. CurrentRow = CurrentRow - nPageUpShift < 0 ? 0 : CurrentRow - nPageUpShift;
  4505. if (CurrentRow < _topRow)
  4506. {
  4507. _topRow = _topRow - nPageUpShift < 0 ? 0 : _topRow - nPageUpShift;
  4508. SetNeedsDisplay ();
  4509. }
  4510. TrackColumn ();
  4511. PositionCursor ();
  4512. }
  4513. DoNeededAction ();
  4514. }
  4515. private bool MovePreviousView ()
  4516. {
  4517. if (Application.OverlappedTop is { })
  4518. {
  4519. return SuperView?.FocusPrev () == true;
  4520. }
  4521. return false;
  4522. }
  4523. private void MoveRight ()
  4524. {
  4525. List<RuneCell> currentLine = GetCurrentLine ();
  4526. if (CurrentColumn < currentLine.Count)
  4527. {
  4528. CurrentColumn++;
  4529. }
  4530. else
  4531. {
  4532. if (CurrentRow + 1 < _model.Count)
  4533. {
  4534. CurrentRow++;
  4535. CurrentColumn = 0;
  4536. if (CurrentRow >= _topRow + Frame.Height)
  4537. {
  4538. _topRow++;
  4539. SetNeedsDisplay ();
  4540. }
  4541. }
  4542. }
  4543. Adjust ();
  4544. DoNeededAction ();
  4545. }
  4546. private void MoveStartOfLine ()
  4547. {
  4548. if (_leftColumn > 0)
  4549. {
  4550. SetNeedsDisplay ();
  4551. }
  4552. CurrentColumn = 0;
  4553. _leftColumn = 0;
  4554. Adjust ();
  4555. DoNeededAction ();
  4556. }
  4557. private void MoveTopHome ()
  4558. {
  4559. ResetAllTrack ();
  4560. if (_shiftSelecting && Selecting)
  4561. {
  4562. StopSelecting ();
  4563. }
  4564. MoveHome ();
  4565. }
  4566. private void MoveTopHomeExtend ()
  4567. {
  4568. ResetColumnTrack ();
  4569. StartSelecting ();
  4570. MoveHome ();
  4571. }
  4572. private void MoveUp ()
  4573. {
  4574. if (CurrentRow > 0)
  4575. {
  4576. if (_columnTrack == -1)
  4577. {
  4578. _columnTrack = CurrentColumn;
  4579. }
  4580. CurrentRow--;
  4581. if (CurrentRow < _topRow)
  4582. {
  4583. _topRow--;
  4584. SetNeedsDisplay ();
  4585. }
  4586. TrackColumn ();
  4587. PositionCursor ();
  4588. }
  4589. DoNeededAction ();
  4590. }
  4591. private void MoveWordBackward ()
  4592. {
  4593. (int col, int row)? newPos = _model.WordBackward (CurrentColumn, CurrentRow);
  4594. if (newPos.HasValue)
  4595. {
  4596. CurrentColumn = newPos.Value.col;
  4597. CurrentRow = newPos.Value.row;
  4598. }
  4599. Adjust ();
  4600. DoNeededAction ();
  4601. }
  4602. private void MoveWordForward ()
  4603. {
  4604. (int col, int row)? newPos = _model.WordForward (CurrentColumn, CurrentRow);
  4605. if (newPos.HasValue)
  4606. {
  4607. CurrentColumn = newPos.Value.col;
  4608. CurrentRow = newPos.Value.row;
  4609. }
  4610. Adjust ();
  4611. DoNeededAction ();
  4612. }
  4613. private (int width, int height) OffSetBackground ()
  4614. {
  4615. var w = 0;
  4616. var h = 0;
  4617. if (SuperView?.Frame.Right - Frame.Right < 0)
  4618. {
  4619. w = SuperView!.Frame.Right - Frame.Right - 1;
  4620. }
  4621. if (SuperView?.Frame.Bottom - Frame.Bottom < 0)
  4622. {
  4623. h = SuperView!.Frame.Bottom - Frame.Bottom - 1;
  4624. }
  4625. return (w, h);
  4626. }
  4627. private bool PointInSelection (int col, int row)
  4628. {
  4629. long start, end;
  4630. GetEncodedRegionBounds (out start, out end);
  4631. long q = ((long)(uint)row << 32) | (uint)col;
  4632. return q >= start && q <= end - 1;
  4633. }
  4634. private void ProcessAutocomplete ()
  4635. {
  4636. if (_isDrawing)
  4637. {
  4638. return;
  4639. }
  4640. if (_clickWithSelecting)
  4641. {
  4642. _clickWithSelecting = false;
  4643. return;
  4644. }
  4645. if (SelectedLength > 0)
  4646. {
  4647. return;
  4648. }
  4649. // draw autocomplete
  4650. GenerateSuggestions ();
  4651. var renderAt = new Point (
  4652. Autocomplete.Context.CursorPosition,
  4653. Autocomplete.PopupInsideContainer
  4654. ? CursorPosition.Y + 1 - TopRow
  4655. : 0
  4656. );
  4657. Autocomplete.RenderOverlay (renderAt);
  4658. }
  4659. private bool ProcessBackTab ()
  4660. {
  4661. ResetColumnTrack ();
  4662. if (!AllowsTab || _isReadOnly)
  4663. {
  4664. return ProcessMovePreviousView ();
  4665. }
  4666. if (CurrentColumn > 0)
  4667. {
  4668. SetWrapModel ();
  4669. List<RuneCell> currentLine = GetCurrentLine ();
  4670. if (currentLine.Count > 0 && currentLine [CurrentColumn - 1].Rune.Value == '\t')
  4671. {
  4672. _historyText.Add (new List<List<RuneCell>> { new (currentLine) }, CursorPosition);
  4673. currentLine.RemoveAt (CurrentColumn - 1);
  4674. CurrentColumn--;
  4675. _historyText.Add (
  4676. new List<List<RuneCell>> { new (GetCurrentLine ()) },
  4677. CursorPosition,
  4678. HistoryText.LineStatus.Replaced
  4679. );
  4680. }
  4681. SetNeedsDisplay ();
  4682. UpdateWrapModel ();
  4683. }
  4684. DoNeededAction ();
  4685. return true;
  4686. }
  4687. private void ProcessCopy ()
  4688. {
  4689. ResetColumnTrack ();
  4690. Copy ();
  4691. }
  4692. private void ProcessCut ()
  4693. {
  4694. ResetColumnTrack ();
  4695. Cut ();
  4696. }
  4697. private void ProcessDeleteCharLeft ()
  4698. {
  4699. ResetColumnTrack ();
  4700. DeleteCharLeft ();
  4701. }
  4702. private void ProcessDeleteCharRight ()
  4703. {
  4704. ResetColumnTrack ();
  4705. DeleteCharRight ();
  4706. }
  4707. // If InheritsPreviousColorScheme is enabled this method will check if the rune cell on
  4708. // the row and col location and around has a not null color scheme. If it's null will set it with
  4709. // the very most previous valid color scheme.
  4710. private void ProcessInheritsPreviousColorScheme (int row, int col)
  4711. {
  4712. if (!InheritsPreviousColorScheme || (Lines == 1 && GetLine (Lines).Count == 0))
  4713. {
  4714. return;
  4715. }
  4716. List<RuneCell> line = GetLine (row);
  4717. List<RuneCell> lineToSet = line;
  4718. while (line.Count == 0)
  4719. {
  4720. if (row == 0 && line.Count == 0)
  4721. {
  4722. return;
  4723. }
  4724. row--;
  4725. line = GetLine (row);
  4726. lineToSet = line;
  4727. }
  4728. int colWithColor = Math.Max (Math.Min (col - 2, line.Count - 1), 0);
  4729. RuneCell cell = line [colWithColor];
  4730. int colWithoutColor = Math.Max (col - 1, 0);
  4731. if (cell.ColorScheme is { } && colWithColor == 0 && lineToSet [colWithoutColor].ColorScheme is { })
  4732. {
  4733. for (int r = row - 1; r > -1; r--)
  4734. {
  4735. List<RuneCell> l = GetLine (r);
  4736. for (int c = l.Count - 1; c > -1; c--)
  4737. {
  4738. if (l [c].ColorScheme is null)
  4739. {
  4740. l [c].ColorScheme = cell.ColorScheme;
  4741. }
  4742. else
  4743. {
  4744. return;
  4745. }
  4746. }
  4747. }
  4748. return;
  4749. }
  4750. if (cell.ColorScheme is null)
  4751. {
  4752. for (int r = row; r > -1; r--)
  4753. {
  4754. List<RuneCell> l = GetLine (r);
  4755. colWithColor = l.FindLastIndex (
  4756. colWithColor > -1 ? colWithColor : l.Count - 1,
  4757. rc => rc.ColorScheme != null
  4758. );
  4759. if (colWithColor > -1 && l [colWithColor].ColorScheme is { })
  4760. {
  4761. cell = l [colWithColor];
  4762. break;
  4763. }
  4764. }
  4765. }
  4766. else
  4767. {
  4768. int cRow = row;
  4769. while (cell.ColorScheme is null)
  4770. {
  4771. if ((colWithColor == 0 || cell.ColorScheme is null) && cRow > 0)
  4772. {
  4773. line = GetLine (--cRow);
  4774. colWithColor = line.Count - 1;
  4775. cell = line [colWithColor];
  4776. }
  4777. else if (cRow == 0 && colWithColor < line.Count)
  4778. {
  4779. cell = line [colWithColor + 1];
  4780. }
  4781. }
  4782. }
  4783. if (cell.ColorScheme is { } && colWithColor > -1 && colWithoutColor < lineToSet.Count && lineToSet [colWithoutColor].ColorScheme is null)
  4784. {
  4785. while (lineToSet [colWithoutColor].ColorScheme is null)
  4786. {
  4787. lineToSet [colWithoutColor].ColorScheme = cell.ColorScheme;
  4788. colWithoutColor--;
  4789. if (colWithoutColor == -1 && row > 0)
  4790. {
  4791. lineToSet = GetLine (--row);
  4792. colWithoutColor = lineToSet.Count - 1;
  4793. }
  4794. }
  4795. }
  4796. }
  4797. private void ProcessKillWordBackward ()
  4798. {
  4799. ResetColumnTrack ();
  4800. KillWordBackward ();
  4801. }
  4802. private void ProcessKillWordForward ()
  4803. {
  4804. ResetColumnTrack ();
  4805. KillWordForward ();
  4806. }
  4807. private void ProcessMouseClick (MouseEvent ev, out List<RuneCell> line)
  4808. {
  4809. List<RuneCell>? r = null;
  4810. if (_model.Count > 0)
  4811. {
  4812. int maxCursorPositionableLine = Math.Max (_model.Count - 1 - _topRow, 0);
  4813. if (Math.Max (ev.Y, 0) > maxCursorPositionableLine)
  4814. {
  4815. CurrentRow = maxCursorPositionableLine + _topRow;
  4816. }
  4817. else
  4818. {
  4819. CurrentRow = Math.Max (ev.Y + _topRow, 0);
  4820. }
  4821. r = GetCurrentLine ();
  4822. int idx = TextModel.GetColFromX (r, _leftColumn, Math.Max (ev.X, 0), TabWidth);
  4823. if (idx - _leftColumn >= r.Count + RightOffset)
  4824. {
  4825. CurrentColumn = Math.Max (r.Count - _leftColumn + RightOffset, 0);
  4826. }
  4827. else
  4828. {
  4829. CurrentColumn = idx + _leftColumn;
  4830. }
  4831. }
  4832. line = r!;
  4833. }
  4834. private void ProcessMoveDown ()
  4835. {
  4836. ResetContinuousFindTrack ();
  4837. if (_shiftSelecting && Selecting)
  4838. {
  4839. StopSelecting ();
  4840. }
  4841. MoveDown ();
  4842. }
  4843. private void ProcessMoveDownExtend ()
  4844. {
  4845. ResetColumnTrack ();
  4846. StartSelecting ();
  4847. MoveDown ();
  4848. }
  4849. private void ProcessMoveEndOfLine ()
  4850. {
  4851. ResetAllTrack ();
  4852. if (_shiftSelecting && Selecting)
  4853. {
  4854. StopSelecting ();
  4855. }
  4856. MoveEndOfLine ();
  4857. }
  4858. private void ProcessMoveEndOfLineExtend ()
  4859. {
  4860. ResetAllTrack ();
  4861. StartSelecting ();
  4862. MoveEndOfLine ();
  4863. }
  4864. private bool ProcessMoveLeft ()
  4865. {
  4866. // if the user presses Left (without any control keys) and they are at the start of the text
  4867. if (CurrentColumn == 0 && CurrentRow == 0)
  4868. {
  4869. // do not respond (this lets the key press fall through to navigation system - which usually changes focus backward)
  4870. return false;
  4871. }
  4872. ResetAllTrack ();
  4873. if (_shiftSelecting && Selecting)
  4874. {
  4875. StopSelecting ();
  4876. }
  4877. MoveLeft ();
  4878. return true;
  4879. }
  4880. private void ProcessMoveLeftExtend ()
  4881. {
  4882. ResetAllTrack ();
  4883. StartSelecting ();
  4884. MoveLeft ();
  4885. }
  4886. private bool ProcessMoveNextView ()
  4887. {
  4888. ResetColumnTrack ();
  4889. return MoveNextView ();
  4890. }
  4891. private bool ProcessMovePreviousView ()
  4892. {
  4893. ResetColumnTrack ();
  4894. return MovePreviousView ();
  4895. }
  4896. private bool ProcessMoveRight ()
  4897. {
  4898. // if the user presses Right (without any control keys)
  4899. // determine where the last cursor position in the text is
  4900. int lastRow = _model.Count - 1;
  4901. int lastCol = _model.GetLine (lastRow).Count;
  4902. // if they are at the very end of all the text do not respond (this lets the key press fall through to navigation system - which usually changes focus forward)
  4903. if (CurrentColumn == lastCol && CurrentRow == lastRow)
  4904. {
  4905. return false;
  4906. }
  4907. ResetAllTrack ();
  4908. if (_shiftSelecting && Selecting)
  4909. {
  4910. StopSelecting ();
  4911. }
  4912. MoveRight ();
  4913. return true;
  4914. }
  4915. private void ProcessMoveRightExtend ()
  4916. {
  4917. ResetAllTrack ();
  4918. StartSelecting ();
  4919. MoveRight ();
  4920. }
  4921. private void ProcessMoveStartOfLine ()
  4922. {
  4923. ResetAllTrack ();
  4924. if (_shiftSelecting && Selecting)
  4925. {
  4926. StopSelecting ();
  4927. }
  4928. MoveStartOfLine ();
  4929. }
  4930. private void ProcessMoveStartOfLineExtend ()
  4931. {
  4932. ResetAllTrack ();
  4933. StartSelecting ();
  4934. MoveStartOfLine ();
  4935. }
  4936. private void ProcessMoveUp ()
  4937. {
  4938. ResetContinuousFindTrack ();
  4939. if (_shiftSelecting && Selecting)
  4940. {
  4941. StopSelecting ();
  4942. }
  4943. MoveUp ();
  4944. }
  4945. private void ProcessMoveUpExtend ()
  4946. {
  4947. ResetColumnTrack ();
  4948. StartSelecting ();
  4949. MoveUp ();
  4950. }
  4951. private void ProcessMoveWordBackward ()
  4952. {
  4953. ResetAllTrack ();
  4954. if (_shiftSelecting && Selecting)
  4955. {
  4956. StopSelecting ();
  4957. }
  4958. MoveWordBackward ();
  4959. }
  4960. private void ProcessMoveWordBackwardExtend ()
  4961. {
  4962. ResetAllTrack ();
  4963. StartSelecting ();
  4964. MoveWordBackward ();
  4965. }
  4966. private void ProcessMoveWordForward ()
  4967. {
  4968. ResetAllTrack ();
  4969. if (_shiftSelecting && Selecting)
  4970. {
  4971. StopSelecting ();
  4972. }
  4973. MoveWordForward ();
  4974. }
  4975. private void ProcessMoveWordForwardExtend ()
  4976. {
  4977. ResetAllTrack ();
  4978. StartSelecting ();
  4979. MoveWordForward ();
  4980. }
  4981. private void ProcessPageDown ()
  4982. {
  4983. ResetColumnTrack ();
  4984. if (_shiftSelecting && Selecting)
  4985. {
  4986. StopSelecting ();
  4987. }
  4988. MovePageDown ();
  4989. }
  4990. private void ProcessPageDownExtend ()
  4991. {
  4992. ResetColumnTrack ();
  4993. StartSelecting ();
  4994. MovePageDown ();
  4995. }
  4996. private void ProcessPageUp ()
  4997. {
  4998. ResetColumnTrack ();
  4999. if (_shiftSelecting && Selecting)
  5000. {
  5001. StopSelecting ();
  5002. }
  5003. MovePageUp ();
  5004. }
  5005. private void ProcessPageUpExtend ()
  5006. {
  5007. ResetColumnTrack ();
  5008. StartSelecting ();
  5009. MovePageUp ();
  5010. }
  5011. private void ProcessPaste ()
  5012. {
  5013. ResetColumnTrack ();
  5014. if (_isReadOnly)
  5015. {
  5016. return;
  5017. }
  5018. Paste ();
  5019. }
  5020. private bool ProcessReturn ()
  5021. {
  5022. ResetColumnTrack ();
  5023. if (!AllowsReturn || _isReadOnly)
  5024. {
  5025. return false;
  5026. }
  5027. SetWrapModel ();
  5028. List<RuneCell> currentLine = GetCurrentLine ();
  5029. _historyText.Add (new List<List<RuneCell>> { new (currentLine) }, CursorPosition);
  5030. if (Selecting)
  5031. {
  5032. ClearSelectedRegion ();
  5033. currentLine = GetCurrentLine ();
  5034. }
  5035. int restCount = currentLine.Count - CurrentColumn;
  5036. List<RuneCell> rest = currentLine.GetRange (CurrentColumn, restCount);
  5037. currentLine.RemoveRange (CurrentColumn, restCount);
  5038. List<List<RuneCell>> addedLines = new () { new List<RuneCell> (currentLine) };
  5039. _model.AddLine (CurrentRow + 1, rest);
  5040. addedLines.Add (new List<RuneCell> (_model.GetLine (CurrentRow + 1)));
  5041. _historyText.Add (addedLines, CursorPosition, HistoryText.LineStatus.Added);
  5042. CurrentRow++;
  5043. var fullNeedsDisplay = false;
  5044. if (CurrentRow >= _topRow + Frame.Height)
  5045. {
  5046. _topRow++;
  5047. fullNeedsDisplay = true;
  5048. }
  5049. CurrentColumn = 0;
  5050. _historyText.Add (
  5051. new List<List<RuneCell>> { new (GetCurrentLine ()) },
  5052. CursorPosition,
  5053. HistoryText.LineStatus.Replaced
  5054. );
  5055. if (!_wordWrap && CurrentColumn < _leftColumn)
  5056. {
  5057. fullNeedsDisplay = true;
  5058. _leftColumn = 0;
  5059. }
  5060. if (fullNeedsDisplay)
  5061. {
  5062. SetNeedsDisplay ();
  5063. }
  5064. else
  5065. {
  5066. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  5067. //SetNeedsDisplay (new Rect (0, currentRow - topRow, 2, Frame.Height));
  5068. SetNeedsDisplay ();
  5069. }
  5070. UpdateWrapModel ();
  5071. DoNeededAction ();
  5072. OnContentsChanged ();
  5073. return true;
  5074. }
  5075. private void ProcessSelectAll ()
  5076. {
  5077. ResetColumnTrack ();
  5078. SelectAll ();
  5079. }
  5080. private void ProcessSetOverwrite ()
  5081. {
  5082. ResetColumnTrack ();
  5083. SetOverwrite (!Used);
  5084. }
  5085. private bool ProcessTab ()
  5086. {
  5087. ResetColumnTrack ();
  5088. if (!AllowsTab || _isReadOnly)
  5089. {
  5090. return ProcessMoveNextView ();
  5091. }
  5092. InsertText (new Key ((KeyCode)'\t'));
  5093. DoNeededAction ();
  5094. return true;
  5095. }
  5096. private void ResetAllTrack ()
  5097. {
  5098. // Handle some state here - whether the last command was a kill
  5099. // operation and the column tracking (up/down)
  5100. _lastWasKill = false;
  5101. _columnTrack = -1;
  5102. _continuousFind = false;
  5103. }
  5104. private void ResetColumnTrack ()
  5105. {
  5106. // Handle some state here - whether the last command was a kill
  5107. // operation and the column tracking (up/down)
  5108. _lastWasKill = false;
  5109. _columnTrack = -1;
  5110. }
  5111. private void ResetContinuousFind ()
  5112. {
  5113. if (!_continuousFind)
  5114. {
  5115. int col = Selecting ? _selectionStartColumn : CurrentColumn;
  5116. int row = Selecting ? _selectionStartRow : CurrentRow;
  5117. _model.ResetContinuousFind (new Point (col, row));
  5118. }
  5119. }
  5120. private void ResetContinuousFindTrack ()
  5121. {
  5122. // Handle some state here - whether the last command was a kill
  5123. // operation and the column tracking (up/down)
  5124. _lastWasKill = false;
  5125. _continuousFind = false;
  5126. }
  5127. private void ResetCursorVisibility ()
  5128. {
  5129. if (_savedCursorVisibility != 0)
  5130. {
  5131. DesiredCursorVisibility = _savedCursorVisibility;
  5132. _savedCursorVisibility = 0;
  5133. }
  5134. }
  5135. private void ResetPosition ()
  5136. {
  5137. _topRow = _leftColumn = CurrentRow = CurrentColumn = 0;
  5138. StopSelecting ();
  5139. ResetCursorVisibility ();
  5140. }
  5141. private void SaveCursorVisibility ()
  5142. {
  5143. if (_desiredCursorVisibility != CursorVisibility.Invisible)
  5144. {
  5145. if (_savedCursorVisibility == 0)
  5146. {
  5147. _savedCursorVisibility = _desiredCursorVisibility;
  5148. }
  5149. DesiredCursorVisibility = CursorVisibility.Invisible;
  5150. }
  5151. }
  5152. private void SetClipboard (string text)
  5153. {
  5154. if (text is { })
  5155. {
  5156. Clipboard.Contents = text;
  5157. }
  5158. }
  5159. private bool SetFoundText (
  5160. string text,
  5161. (Point current, bool found) foundPos,
  5162. string? textToReplace = null,
  5163. bool replace = false,
  5164. bool replaceAll = false
  5165. )
  5166. {
  5167. if (foundPos.found)
  5168. {
  5169. StartSelecting ();
  5170. _selectionStartColumn = foundPos.current.X;
  5171. _selectionStartRow = foundPos.current.Y;
  5172. if (!replaceAll)
  5173. {
  5174. CurrentColumn = _selectionStartColumn + text.GetRuneCount ();
  5175. }
  5176. else
  5177. {
  5178. CurrentColumn = _selectionStartColumn + textToReplace!.GetRuneCount ();
  5179. }
  5180. CurrentRow = foundPos.current.Y;
  5181. if (!_isReadOnly && replace)
  5182. {
  5183. Adjust ();
  5184. ClearSelectedRegion ();
  5185. InsertAllText (textToReplace!);
  5186. StartSelecting ();
  5187. _selectionStartColumn = CurrentColumn - textToReplace!.GetRuneCount ();
  5188. }
  5189. else
  5190. {
  5191. UpdateWrapModel ();
  5192. SetNeedsDisplay ();
  5193. Adjust ();
  5194. }
  5195. _continuousFind = true;
  5196. return foundPos.found;
  5197. }
  5198. UpdateWrapModel ();
  5199. _continuousFind = false;
  5200. return foundPos.found;
  5201. }
  5202. private void SetOverwrite (bool overwrite)
  5203. {
  5204. Used = overwrite;
  5205. SetNeedsDisplay ();
  5206. DoNeededAction ();
  5207. }
  5208. private static void SetValidUsedColor (ColorScheme colorScheme)
  5209. {
  5210. // BUGBUG: (v2 truecolor) This code depends on 8-bit color names; disabling for now
  5211. //if ((colorScheme!.HotNormal.Foreground & colorScheme.Focus.Background) == colorScheme.Focus.Foreground) {
  5212. Driver.SetAttribute (new Attribute (colorScheme.Focus.Background, colorScheme.Focus.Foreground));
  5213. }
  5214. /// <summary>Restore from original model.</summary>
  5215. private void SetWrapModel ([CallerMemberName] string? caller = null)
  5216. {
  5217. if (_currentCaller is { })
  5218. {
  5219. return;
  5220. }
  5221. if (_wordWrap)
  5222. {
  5223. _currentCaller = caller;
  5224. CurrentColumn = _wrapManager!.GetModelColFromWrappedLines (CurrentRow, CurrentColumn);
  5225. CurrentRow = _wrapManager.GetModelLineFromWrappedLines (CurrentRow);
  5226. _selectionStartColumn =
  5227. _wrapManager.GetModelColFromWrappedLines (_selectionStartRow, _selectionStartColumn);
  5228. _selectionStartRow = _wrapManager.GetModelLineFromWrappedLines (_selectionStartRow);
  5229. _model = _wrapManager.Model;
  5230. }
  5231. }
  5232. private void ShowContextMenu ()
  5233. {
  5234. if (_currentCulture != Thread.CurrentThread.CurrentUICulture)
  5235. {
  5236. _currentCulture = Thread.CurrentThread.CurrentUICulture;
  5237. ContextMenu!.MenuItems = BuildContextMenuBarItem ();
  5238. }
  5239. ContextMenu!.Show ();
  5240. }
  5241. private void StartSelecting ()
  5242. {
  5243. if (_shiftSelecting && Selecting)
  5244. {
  5245. return;
  5246. }
  5247. _shiftSelecting = true;
  5248. Selecting = true;
  5249. _selectionStartColumn = CurrentColumn;
  5250. _selectionStartRow = CurrentRow;
  5251. }
  5252. private void StopSelecting ()
  5253. {
  5254. _shiftSelecting = false;
  5255. Selecting = false;
  5256. _isButtonShift = false;
  5257. }
  5258. private string StringFromRunes (List<RuneCell> cells)
  5259. {
  5260. if (cells is null)
  5261. {
  5262. throw new ArgumentNullException (nameof (cells));
  5263. }
  5264. var size = 0;
  5265. foreach (RuneCell cell in cells)
  5266. {
  5267. size += cell.Rune.GetEncodingLength ();
  5268. }
  5269. var encoded = new byte [size];
  5270. var offset = 0;
  5271. foreach (RuneCell cell in cells)
  5272. {
  5273. offset += cell.Rune.Encode (encoded, offset);
  5274. }
  5275. return StringExtensions.ToString (encoded);
  5276. }
  5277. private void TextView_Initialized (object sender, EventArgs e)
  5278. {
  5279. Autocomplete.HostControl = this;
  5280. if (Application.Top is { })
  5281. {
  5282. Application.Top.AlternateForwardKeyChanged += Top_AlternateForwardKeyChanged!;
  5283. Application.Top.AlternateBackwardKeyChanged += Top_AlternateBackwardKeyChanged!;
  5284. }
  5285. OnContentsChanged ();
  5286. }
  5287. private void TextView_LayoutComplete (object? sender, LayoutEventArgs e)
  5288. {
  5289. WrapTextModel ();
  5290. Adjust ();
  5291. }
  5292. private void ToggleSelecting ()
  5293. {
  5294. ResetColumnTrack ();
  5295. Selecting = !Selecting;
  5296. _selectionStartColumn = CurrentColumn;
  5297. _selectionStartRow = CurrentRow;
  5298. }
  5299. private void Top_AlternateBackwardKeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.Replace ((KeyCode)e.OldKey, (KeyCode)e.NewKey); }
  5300. private void Top_AlternateForwardKeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.Replace ((KeyCode)e.OldKey, (KeyCode)e.NewKey); }
  5301. // Tries to snap the cursor to the tracking column
  5302. private void TrackColumn ()
  5303. {
  5304. // Now track the column
  5305. List<RuneCell> line = GetCurrentLine ();
  5306. if (line.Count < _columnTrack)
  5307. {
  5308. CurrentColumn = line.Count;
  5309. }
  5310. else if (_columnTrack != -1)
  5311. {
  5312. CurrentColumn = _columnTrack;
  5313. }
  5314. else if (CurrentColumn > line.Count)
  5315. {
  5316. CurrentColumn = line.Count;
  5317. }
  5318. Adjust ();
  5319. }
  5320. /// <summary>Update the original model.</summary>
  5321. private void UpdateWrapModel ([CallerMemberName] string? caller = null)
  5322. {
  5323. if (_currentCaller is { } && _currentCaller != caller)
  5324. {
  5325. return;
  5326. }
  5327. if (_wordWrap)
  5328. {
  5329. _currentCaller = null;
  5330. _wrapManager!.UpdateModel (
  5331. _model,
  5332. out int nRow,
  5333. out int nCol,
  5334. out int nStartRow,
  5335. out int nStartCol,
  5336. CurrentRow,
  5337. CurrentColumn,
  5338. _selectionStartRow,
  5339. _selectionStartColumn,
  5340. true
  5341. );
  5342. CurrentRow = nRow;
  5343. CurrentColumn = nCol;
  5344. _selectionStartRow = nStartRow;
  5345. _selectionStartColumn = nStartCol;
  5346. _wrapNeeded = true;
  5347. SetNeedsDisplay ();
  5348. }
  5349. if (_currentCaller is { })
  5350. {
  5351. throw new InvalidOperationException (
  5352. $"WordWrap settings was changed after the {_currentCaller} call."
  5353. );
  5354. }
  5355. }
  5356. private void WrapTextModel ()
  5357. {
  5358. if (_wordWrap && _wrapManager is { })
  5359. {
  5360. _model = _wrapManager.WrapModel (
  5361. _frameWidth,
  5362. out int nRow,
  5363. out int nCol,
  5364. out int nStartRow,
  5365. out int nStartCol,
  5366. CurrentRow,
  5367. CurrentColumn,
  5368. _selectionStartRow,
  5369. _selectionStartColumn,
  5370. _tabWidth
  5371. );
  5372. CurrentRow = nRow;
  5373. CurrentColumn = nCol;
  5374. _selectionStartRow = nStartRow;
  5375. _selectionStartColumn = nStartCol;
  5376. SetNeedsDisplay ();
  5377. }
  5378. }
  5379. }
  5380. /// <summary>
  5381. /// Renders an overlay on another view at a given point that allows selecting from a range of 'autocomplete'
  5382. /// options. An implementation on a TextView.
  5383. /// </summary>
  5384. public class TextViewAutocomplete : PopupAutocomplete
  5385. {
  5386. /// <inheritdoc/>
  5387. protected override void DeleteTextBackwards () { ((TextView)HostControl).DeleteCharLeft (); }
  5388. /// <inheritdoc/>
  5389. protected override void InsertText (string accepted) { ((TextView)HostControl).InsertText (accepted); }
  5390. /// <inheritdoc/>
  5391. protected override void SetCursorPosition (int column)
  5392. {
  5393. ((TextView)HostControl).CursorPosition =
  5394. new Point (column, ((TextView)HostControl).CurrentRow);
  5395. }
  5396. }