TextView.cs 135 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804
  1. #nullable enable
  2. // TextView.cs: multi-line text editing
  3. using System.Globalization;
  4. using System.Runtime.CompilerServices;
  5. namespace Terminal.Gui.Views;
  6. /// <summary>Fully featured multi-line text editor</summary>
  7. /// <remarks>
  8. /// <list type="table">
  9. /// <listheader>
  10. /// <term>Shortcut</term> <description>Action performed</description>
  11. /// </listheader>
  12. /// <item>
  13. /// <term>Left cursor, Control-b</term> <description>Moves the editing point left.</description>
  14. /// </item>
  15. /// <item>
  16. /// <term>Right cursor, Control-f</term> <description>Moves the editing point right.</description>
  17. /// </item>
  18. /// <item>
  19. /// <term>Alt-b</term> <description>Moves one word back.</description>
  20. /// </item>
  21. /// <item>
  22. /// <term>Alt-f</term> <description>Moves one word forward.</description>
  23. /// </item>
  24. /// <item>
  25. /// <term>Up cursor, Control-p</term> <description>Moves the editing point one line up.</description>
  26. /// </item>
  27. /// <item>
  28. /// <term>Down cursor, Control-n</term> <description>Moves the editing point one line down</description>
  29. /// </item>
  30. /// <item>
  31. /// <term>Home key, Control-a</term> <description>Moves the cursor to the beginning of the line.</description>
  32. /// </item>
  33. /// <item>
  34. /// <term>End key, Control-e</term> <description>Moves the cursor to the end of the line.</description>
  35. /// </item>
  36. /// <item>
  37. /// <term>Control-Home</term> <description>Scrolls to the first line and moves the cursor there.</description>
  38. /// </item>
  39. /// <item>
  40. /// <term>Control-End</term> <description>Scrolls to the last line and moves the cursor there.</description>
  41. /// </item>
  42. /// <item>
  43. /// <term>Delete, Control-d</term> <description>Deletes the character in front of the cursor.</description>
  44. /// </item>
  45. /// <item>
  46. /// <term>Backspace</term> <description>Deletes the character behind the cursor.</description>
  47. /// </item>
  48. /// <item>
  49. /// <term>Control-k</term>
  50. /// <description>
  51. /// Deletes the text until the end of the line and replaces the kill buffer with the deleted text.
  52. /// You can paste this text in a different place by using Control-y.
  53. /// </description>
  54. /// </item>
  55. /// <item>
  56. /// <term>Control-y</term>
  57. /// <description>Pastes the content of the kill ring into the current position.</description>
  58. /// </item>
  59. /// <item>
  60. /// <term>Alt-d</term>
  61. /// <description>
  62. /// Deletes the word above the cursor and adds it to the kill ring. You can paste the contents of
  63. /// the kill ring with Control-y.
  64. /// </description>
  65. /// </item>
  66. /// <item>
  67. /// <term>Control-q</term>
  68. /// <description>
  69. /// Quotes the next input character, to prevent the normal processing of key handling to take
  70. /// place.
  71. /// </description>
  72. /// </item>
  73. /// </list>
  74. /// </remarks>
  75. public class TextView : View, IDesignable
  76. {
  77. private readonly HistoryText _historyText = new ();
  78. private bool _allowsReturn = true;
  79. private bool _allowsTab = true;
  80. private bool _clickWithSelecting;
  81. // The column we are tracking, or -1 if we are not tracking any column
  82. private int _columnTrack = -1;
  83. private bool _continuousFind;
  84. private bool _copyWithoutSelection;
  85. private string? _currentCaller;
  86. private CultureInfo? _currentCulture;
  87. private bool _isButtonShift;
  88. private bool _isButtonReleased;
  89. private bool _isDrawing;
  90. private bool _isReadOnly;
  91. private bool _lastWasKill;
  92. private int _leftColumn;
  93. private TextModel _model = new ();
  94. private bool _multiline = true;
  95. private Dim? _savedHeight;
  96. private int _selectionStartColumn, _selectionStartRow;
  97. private bool _shiftSelecting;
  98. private int _tabWidth = 4;
  99. private int _topRow;
  100. private bool _wordWrap;
  101. private WordWrapManager? _wrapManager;
  102. private bool _wrapNeeded;
  103. /// <summary>
  104. /// Initializes a <see cref="TextView"/> on the specified area, with dimensions controlled with the X, Y, Width
  105. /// and Height properties.
  106. /// </summary>
  107. public TextView ()
  108. {
  109. CanFocus = true;
  110. CursorVisibility = CursorVisibility.Default;
  111. Used = true;
  112. // By default, disable hotkeys (in case someone sets Title)
  113. HotKeySpecifier = new ('\xffff');
  114. _model.LinesLoaded += Model_LinesLoaded!;
  115. _historyText.ChangeText += HistoryText_ChangeText!;
  116. Initialized += TextView_Initialized!;
  117. SuperViewChanged += TextView_SuperViewChanged!;
  118. SubViewsLaidOut += TextView_LayoutComplete;
  119. // Things this view knows how to do
  120. // Note - NewLine is only bound to Enter if Multiline is true
  121. AddCommand (Command.NewLine, ctx => ProcessEnterKey (ctx));
  122. AddCommand (
  123. Command.PageDown,
  124. () =>
  125. {
  126. ProcessPageDown ();
  127. return true;
  128. }
  129. );
  130. AddCommand (
  131. Command.PageDownExtend,
  132. () =>
  133. {
  134. ProcessPageDownExtend ();
  135. return true;
  136. }
  137. );
  138. AddCommand (
  139. Command.PageUp,
  140. () =>
  141. {
  142. ProcessPageUp ();
  143. return true;
  144. }
  145. );
  146. AddCommand (
  147. Command.PageUpExtend,
  148. () =>
  149. {
  150. ProcessPageUpExtend ();
  151. return true;
  152. }
  153. );
  154. AddCommand (Command.Down, () => ProcessMoveDown ());
  155. AddCommand (
  156. Command.DownExtend,
  157. () =>
  158. {
  159. ProcessMoveDownExtend ();
  160. return true;
  161. }
  162. );
  163. AddCommand (Command.Up, () => ProcessMoveUp ());
  164. AddCommand (
  165. Command.UpExtend,
  166. () =>
  167. {
  168. ProcessMoveUpExtend ();
  169. return true;
  170. }
  171. );
  172. AddCommand (Command.Right, () => ProcessMoveRight ());
  173. AddCommand (
  174. Command.RightExtend,
  175. () =>
  176. {
  177. ProcessMoveRightExtend ();
  178. return true;
  179. }
  180. );
  181. AddCommand (Command.Left, () => ProcessMoveLeft ());
  182. AddCommand (
  183. Command.LeftExtend,
  184. () =>
  185. {
  186. ProcessMoveLeftExtend ();
  187. return true;
  188. }
  189. );
  190. AddCommand (
  191. Command.DeleteCharLeft,
  192. () =>
  193. {
  194. ProcessDeleteCharLeft ();
  195. return true;
  196. }
  197. );
  198. AddCommand (
  199. Command.LeftStart,
  200. () =>
  201. {
  202. ProcessMoveLeftStart ();
  203. return true;
  204. }
  205. );
  206. AddCommand (
  207. Command.LeftStartExtend,
  208. () =>
  209. {
  210. ProcessMoveLeftStartExtend ();
  211. return true;
  212. }
  213. );
  214. AddCommand (
  215. Command.DeleteCharRight,
  216. () =>
  217. {
  218. ProcessDeleteCharRight ();
  219. return true;
  220. }
  221. );
  222. AddCommand (
  223. Command.RightEnd,
  224. () =>
  225. {
  226. ProcessMoveEndOfLine ();
  227. return true;
  228. }
  229. );
  230. AddCommand (
  231. Command.RightEndExtend,
  232. () =>
  233. {
  234. ProcessMoveRightEndExtend ();
  235. return true;
  236. }
  237. );
  238. AddCommand (
  239. Command.CutToEndLine,
  240. () =>
  241. {
  242. KillToEndOfLine ();
  243. return true;
  244. }
  245. );
  246. AddCommand (
  247. Command.CutToStartLine,
  248. () =>
  249. {
  250. KillToLeftStart ();
  251. return true;
  252. }
  253. );
  254. AddCommand (
  255. Command.Paste,
  256. () =>
  257. {
  258. ProcessPaste ();
  259. return true;
  260. }
  261. );
  262. AddCommand (
  263. Command.ToggleExtend,
  264. () =>
  265. {
  266. ToggleSelecting ();
  267. return true;
  268. }
  269. );
  270. AddCommand (
  271. Command.Copy,
  272. () =>
  273. {
  274. ProcessCopy ();
  275. return true;
  276. }
  277. );
  278. AddCommand (
  279. Command.Cut,
  280. () =>
  281. {
  282. ProcessCut ();
  283. return true;
  284. }
  285. );
  286. AddCommand (
  287. Command.WordLeft,
  288. () =>
  289. {
  290. ProcessMoveWordBackward ();
  291. return true;
  292. }
  293. );
  294. AddCommand (
  295. Command.WordLeftExtend,
  296. () =>
  297. {
  298. ProcessMoveWordBackwardExtend ();
  299. return true;
  300. }
  301. );
  302. AddCommand (
  303. Command.WordRight,
  304. () =>
  305. {
  306. ProcessMoveWordForward ();
  307. return true;
  308. }
  309. );
  310. AddCommand (
  311. Command.WordRightExtend,
  312. () =>
  313. {
  314. ProcessMoveWordForwardExtend ();
  315. return true;
  316. }
  317. );
  318. AddCommand (
  319. Command.KillWordForwards,
  320. () =>
  321. {
  322. ProcessKillWordForward ();
  323. return true;
  324. }
  325. );
  326. AddCommand (
  327. Command.KillWordBackwards,
  328. () =>
  329. {
  330. ProcessKillWordBackward ();
  331. return true;
  332. }
  333. );
  334. AddCommand (
  335. Command.End,
  336. () =>
  337. {
  338. MoveBottomEnd ();
  339. return true;
  340. }
  341. );
  342. AddCommand (
  343. Command.EndExtend,
  344. () =>
  345. {
  346. MoveBottomEndExtend ();
  347. return true;
  348. }
  349. );
  350. AddCommand (
  351. Command.Start,
  352. () =>
  353. {
  354. MoveTopHome ();
  355. return true;
  356. }
  357. );
  358. AddCommand (
  359. Command.StartExtend,
  360. () =>
  361. {
  362. MoveTopHomeExtend ();
  363. return true;
  364. }
  365. );
  366. AddCommand (
  367. Command.SelectAll,
  368. () =>
  369. {
  370. ProcessSelectAll ();
  371. return true;
  372. }
  373. );
  374. AddCommand (
  375. Command.ToggleOverwrite,
  376. () =>
  377. {
  378. ProcessSetOverwrite ();
  379. return true;
  380. }
  381. );
  382. AddCommand (
  383. Command.EnableOverwrite,
  384. () =>
  385. {
  386. SetOverwrite (true);
  387. return true;
  388. }
  389. );
  390. AddCommand (
  391. Command.DisableOverwrite,
  392. () =>
  393. {
  394. SetOverwrite (false);
  395. return true;
  396. }
  397. );
  398. AddCommand (Command.Tab, () => ProcessTab ());
  399. AddCommand (Command.BackTab, () => ProcessBackTab ());
  400. AddCommand (
  401. Command.Undo,
  402. () =>
  403. {
  404. Undo ();
  405. return true;
  406. }
  407. );
  408. AddCommand (
  409. Command.Redo,
  410. () =>
  411. {
  412. Redo ();
  413. return true;
  414. }
  415. );
  416. AddCommand (
  417. Command.DeleteAll,
  418. () =>
  419. {
  420. DeleteAll ();
  421. return true;
  422. }
  423. );
  424. AddCommand (
  425. Command.Context,
  426. () =>
  427. {
  428. ShowContextMenu (null);
  429. return true;
  430. }
  431. );
  432. AddCommand (
  433. Command.Open,
  434. () =>
  435. {
  436. PromptForColors ();
  437. return true;
  438. });
  439. // Default keybindings for this view
  440. KeyBindings.Remove (Key.Space);
  441. KeyBindings.Remove (Key.Enter);
  442. KeyBindings.Add (Key.Enter, Multiline ? Command.NewLine : Command.Accept);
  443. KeyBindings.Add (Key.PageDown, Command.PageDown);
  444. KeyBindings.Add (Key.V.WithCtrl, Command.PageDown);
  445. KeyBindings.Add (Key.PageDown.WithShift, Command.PageDownExtend);
  446. KeyBindings.Add (Key.PageUp, Command.PageUp);
  447. KeyBindings.Add (Key.PageUp.WithShift, Command.PageUpExtend);
  448. KeyBindings.Add (Key.N.WithCtrl, Command.Down);
  449. KeyBindings.Add (Key.CursorDown, Command.Down);
  450. KeyBindings.Add (Key.CursorDown.WithShift, Command.DownExtend);
  451. KeyBindings.Add (Key.P.WithCtrl, Command.Up);
  452. KeyBindings.Add (Key.CursorUp, Command.Up);
  453. KeyBindings.Add (Key.CursorUp.WithShift, Command.UpExtend);
  454. KeyBindings.Add (Key.F.WithCtrl, Command.Right);
  455. KeyBindings.Add (Key.CursorRight, Command.Right);
  456. KeyBindings.Add (Key.CursorRight.WithShift, Command.RightExtend);
  457. KeyBindings.Add (Key.B.WithCtrl, Command.Left);
  458. KeyBindings.Add (Key.CursorLeft, Command.Left);
  459. KeyBindings.Add (Key.CursorLeft.WithShift, Command.LeftExtend);
  460. KeyBindings.Add (Key.Backspace, Command.DeleteCharLeft);
  461. KeyBindings.Add (Key.Home, Command.LeftStart);
  462. KeyBindings.Add (Key.Home.WithShift, Command.LeftStartExtend);
  463. KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
  464. KeyBindings.Add (Key.D.WithCtrl, Command.DeleteCharRight);
  465. KeyBindings.Add (Key.End, Command.RightEnd);
  466. KeyBindings.Add (Key.E.WithCtrl, Command.RightEnd);
  467. KeyBindings.Add (Key.End.WithShift, Command.RightEndExtend);
  468. KeyBindings.Add (Key.K.WithCtrl, Command.CutToEndLine); // kill-to-end
  469. KeyBindings.Add (Key.Delete.WithCtrl.WithShift, Command.CutToEndLine); // kill-to-end
  470. KeyBindings.Add (Key.Backspace.WithCtrl.WithShift, Command.CutToStartLine); // kill-to-start
  471. KeyBindings.Add (Key.Y.WithCtrl, Command.Paste); // Control-y, yank
  472. KeyBindings.Add (Key.Space.WithCtrl, Command.ToggleExtend);
  473. KeyBindings.Add (Key.C.WithCtrl, Command.Copy);
  474. KeyBindings.Add (Key.W.WithCtrl, Command.Cut); // Move to Unix?
  475. KeyBindings.Add (Key.X.WithCtrl, Command.Cut);
  476. KeyBindings.Add (Key.CursorLeft.WithCtrl, Command.WordLeft);
  477. KeyBindings.Add (Key.CursorLeft.WithCtrl.WithShift, Command.WordLeftExtend);
  478. KeyBindings.Add (Key.CursorRight.WithCtrl, Command.WordRight);
  479. KeyBindings.Add (Key.CursorRight.WithCtrl.WithShift, Command.WordRightExtend);
  480. KeyBindings.Add (Key.Delete.WithCtrl, Command.KillWordForwards); // kill-word-forwards
  481. KeyBindings.Add (Key.Backspace.WithCtrl, Command.KillWordBackwards); // kill-word-backwards
  482. KeyBindings.Add (Key.End.WithCtrl, Command.End);
  483. KeyBindings.Add (Key.End.WithCtrl.WithShift, Command.EndExtend);
  484. KeyBindings.Add (Key.Home.WithCtrl, Command.Start);
  485. KeyBindings.Add (Key.Home.WithCtrl.WithShift, Command.StartExtend);
  486. KeyBindings.Add (Key.A.WithCtrl, Command.SelectAll);
  487. KeyBindings.Add (Key.InsertChar, Command.ToggleOverwrite);
  488. KeyBindings.Add (Key.Tab, Command.Tab);
  489. KeyBindings.Add (Key.Tab.WithShift, Command.BackTab);
  490. KeyBindings.Add (Key.Z.WithCtrl, Command.Undo);
  491. KeyBindings.Add (Key.R.WithCtrl, Command.Redo);
  492. KeyBindings.Add (Key.G.WithCtrl, Command.DeleteAll);
  493. KeyBindings.Add (Key.D.WithCtrl.WithShift, Command.DeleteAll);
  494. KeyBindings.Add (Key.L.WithCtrl, Command.Open);
  495. #if UNIX_KEY_BINDINGS
  496. KeyBindings.Add (Key.C.WithAlt, Command.Copy);
  497. KeyBindings.Add (Key.B.WithAlt, Command.WordLeft);
  498. KeyBindings.Add (Key.W.WithAlt, Command.Cut);
  499. KeyBindings.Add (Key.V.WithAlt, Command.PageUp);
  500. KeyBindings.Add (Key.F.WithAlt, Command.WordRight);
  501. KeyBindings.Add (Key.K.WithAlt, Command.CutToStartLine); // kill-to-start
  502. #endif
  503. _currentCulture = Thread.CurrentThread.CurrentUICulture;
  504. ContextMenu = CreateContextMenu ();
  505. KeyBindings.Add (ContextMenu.Key, Command.Context);
  506. }
  507. // BUGBUG: AllowsReturn is mis-named. It should be EnterKeyAccepts.
  508. /// <summary>
  509. /// Gets or sets whether pressing ENTER in a <see cref="TextView"/> creates a new line of text
  510. /// in the view or invokes the <see cref="View.Accepting"/> event.
  511. /// </summary>
  512. /// <remarks>
  513. /// <para>
  514. /// Setting this property alters <see cref="Multiline"/>.
  515. /// If <see cref="AllowsReturn"/> is set to <see langword="true"/>, then <see cref="Multiline"/> is also set to
  516. /// `true` and
  517. /// vice-versa.
  518. /// </para>
  519. /// <para>
  520. /// If <see cref="AllowsReturn"/> is set to <see langword="false"/>, then <see cref="AllowsTab"/> gets set to
  521. /// <see langword="false"/>.
  522. /// </para>
  523. /// </remarks>
  524. public bool AllowsReturn
  525. {
  526. get => _allowsReturn;
  527. set
  528. {
  529. _allowsReturn = value;
  530. if (_allowsReturn && !_multiline)
  531. {
  532. // BUGBUG: Setting properties should not have side-effects like this. Multiline and AllowsReturn should be independent.
  533. Multiline = true;
  534. }
  535. if (!_allowsReturn && _multiline)
  536. {
  537. Multiline = false;
  538. // BUGBUG: Setting properties should not have side-effects like this. Multiline and AllowsTab should be independent.
  539. AllowsTab = false;
  540. }
  541. SetNeedsDraw ();
  542. }
  543. }
  544. /// <summary>
  545. /// Gets or sets whether the <see cref="TextView"/> inserts a tab character into the text or ignores tab input. If
  546. /// set to `false` and the user presses the tab key (or shift-tab) the focus will move to the next view (or previous
  547. /// with shift-tab). The default is `true`; if the user presses the tab key, a tab character will be inserted into the
  548. /// text.
  549. /// </summary>
  550. public bool AllowsTab
  551. {
  552. get => _allowsTab;
  553. set
  554. {
  555. _allowsTab = value;
  556. if (_allowsTab && _tabWidth == 0)
  557. {
  558. _tabWidth = 4;
  559. }
  560. if (_allowsTab && !_multiline)
  561. {
  562. Multiline = true;
  563. }
  564. if (!_allowsTab && _tabWidth > 0)
  565. {
  566. _tabWidth = 0;
  567. }
  568. SetNeedsDraw ();
  569. }
  570. }
  571. /// <summary>
  572. /// Provides autocomplete context menu based on suggestions at the current cursor position. Configure
  573. /// <see cref="IAutocomplete.SuggestionGenerator"/> to enable this feature
  574. /// </summary>
  575. public IAutocomplete Autocomplete { get; protected set; } = new TextViewAutocomplete ();
  576. /// <summary>Get the Context Menu.</summary>
  577. public PopoverMenu? ContextMenu { get; private set; }
  578. /// <summary>Gets the cursor column.</summary>
  579. /// <value>The cursor column.</value>
  580. public int CurrentColumn { get; private set; }
  581. /// <summary>Gets the current cursor row.</summary>
  582. public int CurrentRow { get; private set; }
  583. /// <summary>Sets or gets the current cursor position.</summary>
  584. public Point CursorPosition
  585. {
  586. get => new (CurrentColumn, CurrentRow);
  587. set
  588. {
  589. List<Cell> line = _model.GetLine (Math.Max (Math.Min (value.Y, _model.Count - 1), 0));
  590. CurrentColumn = value.X < 0 ? 0 :
  591. value.X > line.Count ? line.Count : value.X;
  592. CurrentRow = value.Y < 0 ? 0 :
  593. value.Y > _model.Count - 1 ? Math.Max (_model.Count - 1, 0) : value.Y;
  594. SetNeedsDraw ();
  595. Adjust ();
  596. }
  597. }
  598. /// <summary>
  599. /// Indicates whatever the text has history changes or not. <see langword="true"/> if the text has history changes
  600. /// <see langword="false"/> otherwise.
  601. /// </summary>
  602. public bool HasHistoryChanges => _historyText.HasHistoryChanges;
  603. /// <summary>
  604. /// If <see langword="true"/> and the current <see cref="Cell.Attribute"/> is null will inherit from the
  605. /// previous, otherwise if <see langword="false"/> (default) do nothing. If the text is load with
  606. /// <see cref="Load(List{Cell})"/> this property is automatically sets to <see langword="true"/>.
  607. /// </summary>
  608. public bool InheritsPreviousAttribute { get; set; }
  609. /// <summary>
  610. /// Indicates whatever the text was changed or not. <see langword="true"/> if the text was changed
  611. /// <see langword="false"/> otherwise.
  612. /// </summary>
  613. public bool IsDirty
  614. {
  615. get => _historyText.IsDirty (_model.GetAllLines ());
  616. set => _historyText.Clear (_model.GetAllLines ());
  617. }
  618. /// <summary>Gets or sets the left column.</summary>
  619. public int LeftColumn
  620. {
  621. get => _leftColumn;
  622. set
  623. {
  624. if (value > 0 && _wordWrap)
  625. {
  626. return;
  627. }
  628. _leftColumn = Math.Max (Math.Min (value, Maxlength - 1), 0);
  629. }
  630. }
  631. /// <summary>Gets the number of lines.</summary>
  632. public int Lines => _model.Count;
  633. /// <summary>Gets the maximum visible length line.</summary>
  634. public int Maxlength => _model.GetMaxVisibleLine (_topRow, _topRow + Viewport.Height, TabWidth);
  635. /// <summary>Gets or sets a value indicating whether this <see cref="TextView"/> is a multiline text view.</summary>
  636. public bool Multiline
  637. {
  638. get => _multiline;
  639. set
  640. {
  641. _multiline = value;
  642. if (_multiline && !_allowsTab)
  643. {
  644. AllowsTab = true;
  645. }
  646. if (_multiline && !_allowsReturn)
  647. {
  648. AllowsReturn = true;
  649. }
  650. if (!_multiline)
  651. {
  652. AllowsReturn = false;
  653. AllowsTab = false;
  654. WordWrap = false;
  655. CurrentColumn = 0;
  656. CurrentRow = 0;
  657. _savedHeight = Height;
  658. Height = Dim.Auto (DimAutoStyle.Text, 1);
  659. if (!IsInitialized)
  660. {
  661. _model.LoadString (Text);
  662. }
  663. SetNeedsDraw ();
  664. }
  665. else if (_multiline && _savedHeight is { })
  666. {
  667. Height = _savedHeight;
  668. SetNeedsDraw ();
  669. }
  670. KeyBindings.Remove (Key.Enter);
  671. KeyBindings.Add (Key.Enter, Multiline ? Command.NewLine : Command.Accept);
  672. }
  673. }
  674. /// <summary>Gets or sets whether the <see cref="TextView"/> is in read-only mode or not</summary>
  675. /// <value>Boolean value(Default false)</value>
  676. public bool ReadOnly
  677. {
  678. get => _isReadOnly;
  679. set
  680. {
  681. if (value != _isReadOnly)
  682. {
  683. _isReadOnly = value;
  684. SetNeedsDraw ();
  685. WrapTextModel ();
  686. Adjust ();
  687. }
  688. }
  689. }
  690. /// <summary>Length of the selected text.</summary>
  691. public int SelectedLength => GetSelectedLength ();
  692. /// <summary>
  693. /// Gets the selected text as
  694. /// <see>
  695. /// <cref>List{List{Cell}}</cref>
  696. /// </see>
  697. /// </summary>
  698. public List<List<Cell>> SelectedCellsList
  699. {
  700. get
  701. {
  702. GetRegion (out List<List<Cell>> selectedCellsList);
  703. return selectedCellsList;
  704. }
  705. }
  706. /// <summary>The selected text.</summary>
  707. public string SelectedText
  708. {
  709. get
  710. {
  711. if (!IsSelecting || (_model.Count == 1 && _model.GetLine (0).Count == 0))
  712. {
  713. return string.Empty;
  714. }
  715. return GetSelectedRegion ();
  716. }
  717. }
  718. /// <summary>Get or sets whether the user is currently selecting text.</summary>
  719. public bool IsSelecting { get; set; }
  720. /// <summary>Start column position of the selected text.</summary>
  721. public int SelectionStartColumn
  722. {
  723. get => _selectionStartColumn;
  724. set
  725. {
  726. List<Cell> line = _model.GetLine (_selectionStartRow);
  727. _selectionStartColumn = value < 0 ? 0 :
  728. value > line.Count ? line.Count : value;
  729. IsSelecting = true;
  730. SetNeedsDraw ();
  731. Adjust ();
  732. }
  733. }
  734. /// <summary>Start row position of the selected text.</summary>
  735. public int SelectionStartRow
  736. {
  737. get => _selectionStartRow;
  738. set
  739. {
  740. _selectionStartRow = value < 0 ? 0 :
  741. value > _model.Count - 1 ? Math.Max (_model.Count - 1, 0) : value;
  742. IsSelecting = true;
  743. SetNeedsDraw ();
  744. Adjust ();
  745. }
  746. }
  747. /// <summary>Gets or sets a value indicating the number of whitespace when pressing the TAB key.</summary>
  748. public int TabWidth
  749. {
  750. get => _tabWidth;
  751. set
  752. {
  753. _tabWidth = Math.Max (value, 0);
  754. if (_tabWidth > 0 && !AllowsTab)
  755. {
  756. AllowsTab = true;
  757. }
  758. SetNeedsDraw ();
  759. }
  760. }
  761. /// <summary>Sets or gets the text in the <see cref="TextView"/>.</summary>
  762. /// <remarks>
  763. /// The <see cref="View.TextChanged"/> event is fired whenever this property is set. Note, however, that Text is not
  764. /// set by <see cref="TextView"/> as the user types.
  765. /// </remarks>
  766. public override string Text
  767. {
  768. get
  769. {
  770. if (_wordWrap)
  771. {
  772. return _wrapManager!.Model.ToString ();
  773. }
  774. return _model.ToString ();
  775. }
  776. set
  777. {
  778. ResetPosition ();
  779. _model.LoadString (value);
  780. if (_wordWrap)
  781. {
  782. _wrapManager = new (_model);
  783. _model = _wrapManager.WrapModel (Viewport.Width, out _, out _, out _, out _);
  784. }
  785. OnTextChanged ();
  786. SetNeedsDraw ();
  787. _historyText.Clear (_model.GetAllLines ());
  788. }
  789. }
  790. /// <summary>Gets or sets the top row.</summary>
  791. public int TopRow
  792. {
  793. get => _topRow;
  794. set => _topRow = Math.Max (Math.Min (value, Lines - 1), 0);
  795. }
  796. /// <summary>
  797. /// Tracks whether the text view should be considered "used", that is, that the user has moved in the entry, so
  798. /// new input should be appended at the cursor position, rather than clearing the entry
  799. /// </summary>
  800. public bool Used { get; set; }
  801. /// <summary>Allows word wrap the to fit the available container width.</summary>
  802. public bool WordWrap
  803. {
  804. get => _wordWrap;
  805. set
  806. {
  807. if (value == _wordWrap)
  808. {
  809. return;
  810. }
  811. if (value && !_multiline)
  812. {
  813. return;
  814. }
  815. _wordWrap = value;
  816. ResetPosition ();
  817. if (_wordWrap)
  818. {
  819. _wrapManager = new (_model);
  820. WrapTextModel ();
  821. }
  822. else if (!_wordWrap && _wrapManager is { })
  823. {
  824. _model = _wrapManager.Model;
  825. }
  826. SetNeedsDraw ();
  827. }
  828. }
  829. /// <summary>
  830. /// Gets or sets whether the word forward and word backward navigation should use the same or equivalent rune type.
  831. /// Default is <c>false</c> meaning using equivalent rune type.
  832. /// </summary>
  833. public bool UseSameRuneTypeForWords { get; set; }
  834. /// <summary>
  835. /// Gets or sets whether the word navigation should select only the word itself without spaces around it or with the
  836. /// spaces at right.
  837. /// Default is <c>false</c> meaning that the spaces at right are included in the selection.
  838. /// </summary>
  839. public bool SelectWordOnlyOnDoubleClick { get; set; }
  840. /// <summary>Allows clearing the <see cref="HistoryTextItemEventArgs"/> items updating the original text.</summary>
  841. public void ClearHistoryChanges () { _historyText?.Clear (_model.GetAllLines ()); }
  842. /// <summary>Closes the contents of the stream into the <see cref="TextView"/>.</summary>
  843. /// <returns><c>true</c>, if stream was closed, <c>false</c> otherwise.</returns>
  844. public bool CloseFile ()
  845. {
  846. SetWrapModel ();
  847. bool res = _model.CloseFile ();
  848. ResetPosition ();
  849. SetNeedsDraw ();
  850. UpdateWrapModel ();
  851. return res;
  852. }
  853. /// <summary>Raised when the contents of the <see cref="TextView"/> are changed.</summary>
  854. /// <remarks>
  855. /// Unlike the <see cref="View.TextChanged"/> event, this event is raised whenever the user types or otherwise changes
  856. /// the contents of the <see cref="TextView"/>.
  857. /// </remarks>
  858. public event EventHandler<ContentsChangedEventArgs>? ContentsChanged;
  859. internal void ApplyCellsAttribute (Attribute attribute)
  860. {
  861. if (!ReadOnly && SelectedLength > 0)
  862. {
  863. int startRow = Math.Min (SelectionStartRow, CurrentRow);
  864. int endRow = Math.Max (CurrentRow, SelectionStartRow);
  865. int startCol = SelectionStartRow <= CurrentRow ? SelectionStartColumn : CurrentColumn;
  866. int endCol = CurrentRow >= SelectionStartRow ? CurrentColumn : SelectionStartColumn;
  867. List<List<Cell>> selectedCellsOriginal = [];
  868. List<List<Cell>> selectedCellsChanged = [];
  869. for (int r = startRow; r <= endRow; r++)
  870. {
  871. List<Cell> line = GetLine (r);
  872. selectedCellsOriginal.Add ([.. line]);
  873. for (int c = r == startRow ? startCol : 0;
  874. c < (r == endRow ? endCol : line.Count);
  875. c++)
  876. {
  877. Cell cell = line [c]; // Copy value to a new variable
  878. cell.Attribute = attribute; // Modify the copy
  879. line [c] = cell; // Assign the modified copy back
  880. }
  881. selectedCellsChanged.Add ([.. GetLine (r)]);
  882. }
  883. GetSelectedRegion ();
  884. IsSelecting = false;
  885. _historyText.Add (
  886. [.. selectedCellsOriginal],
  887. new Point (startCol, startRow)
  888. );
  889. _historyText.Add (
  890. [.. selectedCellsChanged],
  891. new Point (startCol, startRow),
  892. TextEditingLineStatus.Attribute
  893. );
  894. }
  895. }
  896. private Attribute? GetSelectedCellAttribute ()
  897. {
  898. List<Cell> line;
  899. if (SelectedLength > 0)
  900. {
  901. line = GetLine (SelectionStartRow);
  902. if (line [Math.Min (SelectionStartColumn, line.Count - 1)].Attribute is { } attributeSel)
  903. {
  904. return new (attributeSel);
  905. }
  906. return GetAttributeForRole (VisualRole.Active);
  907. }
  908. line = GetCurrentLine ();
  909. if (line [Math.Min (CurrentColumn, line.Count - 1)].Attribute is { } attribute)
  910. {
  911. return new (attribute);
  912. }
  913. return GetAttributeForRole (VisualRole.Active);
  914. }
  915. /// <summary>
  916. /// Open a dialog to set the foreground and background colors.
  917. /// </summary>
  918. public void PromptForColors ()
  919. {
  920. if (!ColorPicker.Prompt (
  921. "Colors",
  922. GetSelectedCellAttribute (),
  923. out Attribute newAttribute
  924. ))
  925. {
  926. return;
  927. }
  928. var attribute = new Attribute (
  929. newAttribute.Foreground,
  930. newAttribute.Background,
  931. newAttribute.Style
  932. );
  933. ApplyCellsAttribute (attribute);
  934. }
  935. private string? _copiedText;
  936. private List<List<Cell>> _copiedCellsList = [];
  937. /// <summary>Copy the selected text to the clipboard contents.</summary>
  938. public void Copy ()
  939. {
  940. SetWrapModel ();
  941. if (IsSelecting)
  942. {
  943. _copiedText = GetRegion (out _copiedCellsList);
  944. SetClipboard (_copiedText);
  945. _copyWithoutSelection = false;
  946. }
  947. else
  948. {
  949. List<Cell> currentLine = GetCurrentLine ();
  950. _copiedCellsList.Add (currentLine);
  951. _copiedText = Cell.ToString (currentLine);
  952. SetClipboard (_copiedText);
  953. _copyWithoutSelection = true;
  954. }
  955. UpdateWrapModel ();
  956. DoNeededAction ();
  957. }
  958. /// <summary>Cut the selected text to the clipboard contents.</summary>
  959. public void Cut ()
  960. {
  961. SetWrapModel ();
  962. _copiedText = GetRegion (out _copiedCellsList);
  963. SetClipboard (_copiedText);
  964. if (!_isReadOnly)
  965. {
  966. ClearRegion ();
  967. _historyText.Add (
  968. [new (GetCurrentLine ())],
  969. CursorPosition,
  970. TextEditingLineStatus.Replaced
  971. );
  972. }
  973. UpdateWrapModel ();
  974. IsSelecting = false;
  975. DoNeededAction ();
  976. OnContentsChanged ();
  977. }
  978. /// <summary>Deletes all text.</summary>
  979. public void DeleteAll ()
  980. {
  981. if (Lines == 0)
  982. {
  983. return;
  984. }
  985. _selectionStartColumn = 0;
  986. _selectionStartRow = 0;
  987. MoveBottomEndExtend ();
  988. DeleteCharLeft ();
  989. SetNeedsDraw ();
  990. }
  991. /// <summary>Deletes all the selected or a single character at left from the position of the cursor.</summary>
  992. public void DeleteCharLeft ()
  993. {
  994. if (_isReadOnly)
  995. {
  996. return;
  997. }
  998. SetWrapModel ();
  999. if (IsSelecting)
  1000. {
  1001. _historyText.Add (new () { new (GetCurrentLine ()) }, CursorPosition);
  1002. ClearSelectedRegion ();
  1003. List<Cell> currentLine = GetCurrentLine ();
  1004. _historyText.Add (
  1005. new () { new (currentLine) },
  1006. CursorPosition,
  1007. TextEditingLineStatus.Replaced
  1008. );
  1009. UpdateWrapModel ();
  1010. OnContentsChanged ();
  1011. return;
  1012. }
  1013. if (DeleteTextBackwards ())
  1014. {
  1015. UpdateWrapModel ();
  1016. OnContentsChanged ();
  1017. return;
  1018. }
  1019. UpdateWrapModel ();
  1020. DoNeededAction ();
  1021. OnContentsChanged ();
  1022. }
  1023. /// <summary>Deletes all the selected or a single character at right from the position of the cursor.</summary>
  1024. public void DeleteCharRight ()
  1025. {
  1026. if (_isReadOnly)
  1027. {
  1028. return;
  1029. }
  1030. SetWrapModel ();
  1031. if (IsSelecting)
  1032. {
  1033. _historyText.Add (new () { new (GetCurrentLine ()) }, CursorPosition);
  1034. ClearSelectedRegion ();
  1035. List<Cell> currentLine = GetCurrentLine ();
  1036. _historyText.Add (
  1037. new () { new (currentLine) },
  1038. CursorPosition,
  1039. TextEditingLineStatus.Replaced
  1040. );
  1041. UpdateWrapModel ();
  1042. OnContentsChanged ();
  1043. return;
  1044. }
  1045. if (DeleteTextForwards ())
  1046. {
  1047. UpdateWrapModel ();
  1048. OnContentsChanged ();
  1049. return;
  1050. }
  1051. UpdateWrapModel ();
  1052. DoNeededAction ();
  1053. OnContentsChanged ();
  1054. }
  1055. /// <summary>Invoked when the normal color is drawn.</summary>
  1056. public event EventHandler<CellEventArgs>? DrawNormalColor;
  1057. /// <summary>Invoked when the ready only color is drawn.</summary>
  1058. public event EventHandler<CellEventArgs>? DrawReadOnlyColor;
  1059. /// <summary>Invoked when the selection color is drawn.</summary>
  1060. public event EventHandler<CellEventArgs>? DrawSelectionColor;
  1061. /// <summary>
  1062. /// Invoked when the used color is drawn. The Used Color is used to indicate if the <see cref="Key.InsertChar"/>
  1063. /// was pressed and enabled.
  1064. /// </summary>
  1065. public event EventHandler<CellEventArgs>? DrawUsedColor;
  1066. /// <summary>Find the next text based on the match case with the option to replace it.</summary>
  1067. /// <param name="textToFind">The text to find.</param>
  1068. /// <param name="gaveFullTurn"><c>true</c>If all the text was forward searched.<c>false</c>otherwise.</param>
  1069. /// <param name="matchCase">The match case setting.</param>
  1070. /// <param name="matchWholeWord">The match whole word setting.</param>
  1071. /// <param name="textToReplace">The text to replace.</param>
  1072. /// <param name="replace"><c>true</c>If is replacing.<c>false</c>otherwise.</param>
  1073. /// <returns><c>true</c>If the text was found.<c>false</c>otherwise.</returns>
  1074. public bool FindNextText (
  1075. string textToFind,
  1076. out bool gaveFullTurn,
  1077. bool matchCase = false,
  1078. bool matchWholeWord = false,
  1079. string? textToReplace = null,
  1080. bool replace = false
  1081. )
  1082. {
  1083. if (_model.Count == 0)
  1084. {
  1085. gaveFullTurn = false;
  1086. return false;
  1087. }
  1088. SetWrapModel ();
  1089. ResetContinuousFind ();
  1090. (Point current, bool found) foundPos =
  1091. _model.FindNextText (textToFind, out gaveFullTurn, matchCase, matchWholeWord);
  1092. return SetFoundText (textToFind, foundPos, textToReplace, replace);
  1093. }
  1094. /// <summary>Find the previous text based on the match case with the option to replace it.</summary>
  1095. /// <param name="textToFind">The text to find.</param>
  1096. /// <param name="gaveFullTurn"><c>true</c>If all the text was backward searched.<c>false</c>otherwise.</param>
  1097. /// <param name="matchCase">The match case setting.</param>
  1098. /// <param name="matchWholeWord">The match whole word setting.</param>
  1099. /// <param name="textToReplace">The text to replace.</param>
  1100. /// <param name="replace"><c>true</c>If the text was found.<c>false</c>otherwise.</param>
  1101. /// <returns><c>true</c>If the text was found.<c>false</c>otherwise.</returns>
  1102. public bool FindPreviousText (
  1103. string textToFind,
  1104. out bool gaveFullTurn,
  1105. bool matchCase = false,
  1106. bool matchWholeWord = false,
  1107. string? textToReplace = null,
  1108. bool replace = false
  1109. )
  1110. {
  1111. if (_model.Count == 0)
  1112. {
  1113. gaveFullTurn = false;
  1114. return false;
  1115. }
  1116. SetWrapModel ();
  1117. ResetContinuousFind ();
  1118. (Point current, bool found) foundPos =
  1119. _model.FindPreviousText (textToFind, out gaveFullTurn, matchCase, matchWholeWord);
  1120. return SetFoundText (textToFind, foundPos, textToReplace, replace);
  1121. }
  1122. /// <summary>Reset the flag to stop continuous find.</summary>
  1123. public void FindTextChanged () { _continuousFind = false; }
  1124. /// <summary>Gets all lines of characters.</summary>
  1125. /// <returns></returns>
  1126. public List<List<Cell>> GetAllLines () { return _model.GetAllLines (); }
  1127. /// <summary>
  1128. /// Returns the characters on the current line (where the cursor is positioned). Use <see cref="CurrentColumn"/>
  1129. /// to determine the position of the cursor within that line
  1130. /// </summary>
  1131. /// <returns></returns>
  1132. public List<Cell> GetCurrentLine () { return _model.GetLine (CurrentRow); }
  1133. /// <summary>Returns the characters on the <paramref name="line"/>.</summary>
  1134. /// <param name="line">The intended line.</param>
  1135. /// <returns></returns>
  1136. public List<Cell> GetLine (int line) { return _model.GetLine (line); }
  1137. /// <inheritdoc/>
  1138. protected override bool OnGettingAttributeForRole (in VisualRole role, ref Attribute currentAttribute)
  1139. {
  1140. if (role == VisualRole.Normal)
  1141. {
  1142. currentAttribute = GetAttributeForRole (VisualRole.Editable);
  1143. return true;
  1144. }
  1145. return base.OnGettingAttributeForRole (role, ref currentAttribute);
  1146. }
  1147. /// <summary>
  1148. /// Inserts the given <paramref name="toAdd"/> text at the current cursor position exactly as if the user had just
  1149. /// typed it
  1150. /// </summary>
  1151. /// <param name="toAdd">Text to add</param>
  1152. public void InsertText (string toAdd)
  1153. {
  1154. foreach (char ch in toAdd)
  1155. {
  1156. Key key;
  1157. try
  1158. {
  1159. key = new (ch);
  1160. }
  1161. catch (Exception)
  1162. {
  1163. throw new ArgumentException (
  1164. $"Cannot insert character '{ch}' because it does not map to a Key"
  1165. );
  1166. }
  1167. InsertText (key);
  1168. if (NeedsDraw)
  1169. {
  1170. Adjust ();
  1171. }
  1172. else
  1173. {
  1174. PositionCursor ();
  1175. }
  1176. }
  1177. }
  1178. /// <summary>Loads the contents of the file into the <see cref="TextView"/>.</summary>
  1179. /// <returns><c>true</c>, if file was loaded, <c>false</c> otherwise.</returns>
  1180. /// <param name="path">Path to the file to load.</param>
  1181. public bool Load (string path)
  1182. {
  1183. SetWrapModel ();
  1184. bool res;
  1185. try
  1186. {
  1187. SetWrapModel ();
  1188. res = _model.LoadFile (path);
  1189. _historyText.Clear (_model.GetAllLines ());
  1190. ResetPosition ();
  1191. }
  1192. finally
  1193. {
  1194. UpdateWrapModel ();
  1195. SetNeedsDraw ();
  1196. Adjust ();
  1197. }
  1198. UpdateWrapModel ();
  1199. return res;
  1200. }
  1201. /// <summary>Loads the contents of the stream into the <see cref="TextView"/>.</summary>
  1202. /// <returns><c>true</c>, if stream was loaded, <c>false</c> otherwise.</returns>
  1203. /// <param name="stream">Stream to load the contents from.</param>
  1204. public void Load (Stream stream)
  1205. {
  1206. SetWrapModel ();
  1207. _model.LoadStream (stream);
  1208. _historyText.Clear (_model.GetAllLines ());
  1209. ResetPosition ();
  1210. SetNeedsDraw ();
  1211. UpdateWrapModel ();
  1212. }
  1213. /// <summary>Loads the contents of the <see cref="Cell"/> list into the <see cref="TextView"/>.</summary>
  1214. /// <param name="cells">Rune cells list to load the contents from.</param>
  1215. public void Load (List<Cell> cells)
  1216. {
  1217. SetWrapModel ();
  1218. _model.LoadCells (cells, GetAttributeForRole (VisualRole.Focus));
  1219. _historyText.Clear (_model.GetAllLines ());
  1220. ResetPosition ();
  1221. SetNeedsDraw ();
  1222. UpdateWrapModel ();
  1223. InheritsPreviousAttribute = true;
  1224. }
  1225. /// <summary>Loads the contents of the list of <see cref="Cell"/> list into the <see cref="TextView"/>.</summary>
  1226. /// <param name="cellsList">List of rune cells list to load the contents from.</param>
  1227. public void Load (List<List<Cell>> cellsList)
  1228. {
  1229. SetWrapModel ();
  1230. InheritsPreviousAttribute = true;
  1231. _model.LoadListCells (cellsList, GetAttributeForRole (VisualRole.Focus));
  1232. _historyText.Clear (_model.GetAllLines ());
  1233. ResetPosition ();
  1234. SetNeedsDraw ();
  1235. UpdateWrapModel ();
  1236. }
  1237. /// <inheritdoc/>
  1238. protected override bool OnMouseEvent (MouseEventArgs ev)
  1239. {
  1240. if (ev is { IsSingleDoubleOrTripleClicked: false, IsPressed: false, IsReleased: false, IsWheel: false }
  1241. && !ev.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)
  1242. && !ev.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ButtonShift)
  1243. && !ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked | MouseFlags.ButtonShift)
  1244. && !ev.Flags.HasFlag (ContextMenu!.MouseFlags))
  1245. {
  1246. return false;
  1247. }
  1248. if (!CanFocus)
  1249. {
  1250. return true;
  1251. }
  1252. if (!HasFocus)
  1253. {
  1254. SetFocus ();
  1255. }
  1256. _continuousFind = false;
  1257. // Give autocomplete first opportunity to respond to mouse clicks
  1258. if (SelectedLength == 0 && Autocomplete.OnMouseEvent (ev, true))
  1259. {
  1260. return true;
  1261. }
  1262. if (ev.Flags == MouseFlags.Button1Clicked)
  1263. {
  1264. if (_isButtonReleased)
  1265. {
  1266. _isButtonReleased = false;
  1267. if (SelectedLength == 0)
  1268. {
  1269. StopSelecting ();
  1270. }
  1271. return true;
  1272. }
  1273. if (_shiftSelecting && !_isButtonShift)
  1274. {
  1275. StopSelecting ();
  1276. }
  1277. ProcessMouseClick (ev, out _);
  1278. if (Used)
  1279. {
  1280. PositionCursor ();
  1281. }
  1282. else
  1283. {
  1284. SetNeedsDraw ();
  1285. }
  1286. _lastWasKill = false;
  1287. _columnTrack = CurrentColumn;
  1288. }
  1289. else if (ev.Flags == MouseFlags.WheeledDown)
  1290. {
  1291. _lastWasKill = false;
  1292. _columnTrack = CurrentColumn;
  1293. ScrollTo (_topRow + 1);
  1294. }
  1295. else if (ev.Flags == MouseFlags.WheeledUp)
  1296. {
  1297. _lastWasKill = false;
  1298. _columnTrack = CurrentColumn;
  1299. ScrollTo (_topRow - 1);
  1300. }
  1301. else if (ev.Flags == MouseFlags.WheeledRight)
  1302. {
  1303. _lastWasKill = false;
  1304. _columnTrack = CurrentColumn;
  1305. ScrollTo (_leftColumn + 1, false);
  1306. }
  1307. else if (ev.Flags == MouseFlags.WheeledLeft)
  1308. {
  1309. _lastWasKill = false;
  1310. _columnTrack = CurrentColumn;
  1311. ScrollTo (_leftColumn - 1, false);
  1312. }
  1313. else if (ev.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))
  1314. {
  1315. ProcessMouseClick (ev, out List<Cell> line);
  1316. PositionCursor ();
  1317. if (_model.Count > 0 && _shiftSelecting && IsSelecting)
  1318. {
  1319. if (CurrentRow - _topRow >= Viewport.Height - 1 && _model.Count > _topRow + CurrentRow)
  1320. {
  1321. ScrollTo (_topRow + Viewport.Height);
  1322. }
  1323. else if (_topRow > 0 && CurrentRow <= _topRow)
  1324. {
  1325. ScrollTo (_topRow - Viewport.Height);
  1326. }
  1327. else if (ev.Position.Y >= Viewport.Height)
  1328. {
  1329. ScrollTo (_model.Count);
  1330. }
  1331. else if (ev.Position.Y < 0 && _topRow > 0)
  1332. {
  1333. ScrollTo (0);
  1334. }
  1335. if (CurrentColumn - _leftColumn >= Viewport.Width - 1 && line.Count > _leftColumn + CurrentColumn)
  1336. {
  1337. ScrollTo (_leftColumn + Viewport.Width, false);
  1338. }
  1339. else if (_leftColumn > 0 && CurrentColumn <= _leftColumn)
  1340. {
  1341. ScrollTo (_leftColumn - Viewport.Width, false);
  1342. }
  1343. else if (ev.Position.X >= Viewport.Width)
  1344. {
  1345. ScrollTo (line.Count, false);
  1346. }
  1347. else if (ev.Position.X < 0 && _leftColumn > 0)
  1348. {
  1349. ScrollTo (0, false);
  1350. }
  1351. }
  1352. _lastWasKill = false;
  1353. _columnTrack = CurrentColumn;
  1354. }
  1355. else if (ev.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ButtonShift))
  1356. {
  1357. if (!_shiftSelecting)
  1358. {
  1359. _isButtonShift = true;
  1360. StartSelecting ();
  1361. }
  1362. ProcessMouseClick (ev, out _);
  1363. PositionCursor ();
  1364. _lastWasKill = false;
  1365. _columnTrack = CurrentColumn;
  1366. }
  1367. else if (ev.Flags.HasFlag (MouseFlags.Button1Pressed))
  1368. {
  1369. if (_shiftSelecting)
  1370. {
  1371. _clickWithSelecting = true;
  1372. StopSelecting ();
  1373. }
  1374. ProcessMouseClick (ev, out _);
  1375. PositionCursor ();
  1376. if (!IsSelecting)
  1377. {
  1378. StartSelecting ();
  1379. }
  1380. _lastWasKill = false;
  1381. _columnTrack = CurrentColumn;
  1382. if (Application.Mouse.MouseGrabView is null)
  1383. {
  1384. Application.Mouse.GrabMouse (this);
  1385. }
  1386. }
  1387. else if (ev.Flags.HasFlag (MouseFlags.Button1Released))
  1388. {
  1389. _isButtonReleased = true;
  1390. Application.Mouse.UngrabMouse ();
  1391. }
  1392. else if (ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked))
  1393. {
  1394. if (ev.Flags.HasFlag (MouseFlags.ButtonShift))
  1395. {
  1396. if (!IsSelecting)
  1397. {
  1398. StartSelecting ();
  1399. }
  1400. }
  1401. else if (IsSelecting)
  1402. {
  1403. StopSelecting ();
  1404. }
  1405. ProcessMouseClick (ev, out List<Cell> line);
  1406. if (!IsSelecting)
  1407. {
  1408. StartSelecting ();
  1409. }
  1410. (int startCol, int col, int row)? newPos = _model.ProcessDoubleClickSelection (SelectionStartColumn, CurrentColumn, CurrentRow, UseSameRuneTypeForWords, SelectWordOnlyOnDoubleClick);
  1411. if (newPos.HasValue)
  1412. {
  1413. SelectionStartColumn = newPos.Value.startCol;
  1414. CurrentColumn = newPos.Value.col;
  1415. CurrentRow = newPos.Value.row;
  1416. }
  1417. PositionCursor ();
  1418. _lastWasKill = false;
  1419. _columnTrack = CurrentColumn;
  1420. SetNeedsDraw ();
  1421. }
  1422. else if (ev.Flags.HasFlag (MouseFlags.Button1TripleClicked))
  1423. {
  1424. if (IsSelecting)
  1425. {
  1426. StopSelecting ();
  1427. }
  1428. ProcessMouseClick (ev, out List<Cell> line);
  1429. CurrentColumn = 0;
  1430. if (!IsSelecting)
  1431. {
  1432. StartSelecting ();
  1433. }
  1434. CurrentColumn = line.Count;
  1435. PositionCursor ();
  1436. _lastWasKill = false;
  1437. _columnTrack = CurrentColumn;
  1438. SetNeedsDraw ();
  1439. }
  1440. else if (ev.Flags == ContextMenu!.MouseFlags)
  1441. {
  1442. ShowContextMenu (ev.ScreenPosition);
  1443. }
  1444. OnUnwrappedCursorPosition ();
  1445. return true;
  1446. }
  1447. /// <summary>Will scroll the <see cref="TextView"/> to the last line and position the cursor there.</summary>
  1448. public void MoveEnd ()
  1449. {
  1450. CurrentRow = _model.Count - 1;
  1451. List<Cell> line = GetCurrentLine ();
  1452. CurrentColumn = line.Count;
  1453. TrackColumn ();
  1454. DoNeededAction ();
  1455. }
  1456. /// <summary>Will scroll the <see cref="TextView"/> to the first line and position the cursor there.</summary>
  1457. public void MoveHome ()
  1458. {
  1459. CurrentRow = 0;
  1460. _topRow = 0;
  1461. CurrentColumn = 0;
  1462. _leftColumn = 0;
  1463. TrackColumn ();
  1464. DoNeededAction ();
  1465. }
  1466. /// <summary>
  1467. /// Called when the contents of the TextView change. E.g. when the user types text or deletes text. Raises the
  1468. /// <see cref="ContentsChanged"/> event.
  1469. /// </summary>
  1470. public virtual void OnContentsChanged ()
  1471. {
  1472. ContentsChanged?.Invoke (this, new (CurrentRow, CurrentColumn));
  1473. ProcessInheritsPreviousScheme (CurrentRow, CurrentColumn);
  1474. ProcessAutocomplete ();
  1475. }
  1476. /// <inheritdoc/>
  1477. protected override bool OnDrawingContent ()
  1478. {
  1479. _isDrawing = true;
  1480. SetAttributeForRole (Enabled ? VisualRole.Editable : VisualRole.Disabled);
  1481. (int width, int height) offB = OffSetBackground ();
  1482. int right = Viewport.Width + offB.width;
  1483. int bottom = Viewport.Height + offB.height;
  1484. var row = 0;
  1485. for (int idxRow = _topRow; idxRow < _model.Count; idxRow++)
  1486. {
  1487. List<Cell> line = _model.GetLine (idxRow);
  1488. int lineRuneCount = line.Count;
  1489. var col = 0;
  1490. Move (0, row);
  1491. for (int idxCol = _leftColumn; idxCol < lineRuneCount; idxCol++)
  1492. {
  1493. Rune rune = idxCol >= lineRuneCount ? (Rune)' ' : line [idxCol].Rune;
  1494. int cols = rune.GetColumns ();
  1495. if (idxCol < line.Count && IsSelecting && PointInSelection (idxCol, idxRow))
  1496. {
  1497. OnDrawSelectionColor (line, idxCol, idxRow);
  1498. }
  1499. else if (idxCol == CurrentColumn && idxRow == CurrentRow && !IsSelecting && !Used && HasFocus && idxCol < lineRuneCount)
  1500. {
  1501. OnDrawUsedColor (line, idxCol, idxRow);
  1502. }
  1503. else if (ReadOnly)
  1504. {
  1505. OnDrawReadOnlyColor (line, idxCol, idxRow);
  1506. }
  1507. else
  1508. {
  1509. OnDrawNormalColor (line, idxCol, idxRow);
  1510. }
  1511. if (rune.Value == '\t')
  1512. {
  1513. cols += TabWidth + 1;
  1514. if (col + cols > right)
  1515. {
  1516. cols = right - col;
  1517. }
  1518. for (var i = 0; i < cols; i++)
  1519. {
  1520. if (col + i < right)
  1521. {
  1522. AddRune (col + i, row, (Rune)' ');
  1523. }
  1524. }
  1525. }
  1526. else
  1527. {
  1528. AddRune (col, row, rune);
  1529. // Ensures that cols less than 0 to be 1 because it will be converted to a printable rune
  1530. cols = Math.Max (cols, 1);
  1531. }
  1532. if (!TextModel.SetCol (ref col, Viewport.Right, cols))
  1533. {
  1534. break;
  1535. }
  1536. if (idxCol + 1 < lineRuneCount && col + line [idxCol + 1].Rune.GetColumns () > right)
  1537. {
  1538. break;
  1539. }
  1540. }
  1541. if (col < right)
  1542. {
  1543. SetAttributeForRole (ReadOnly ? VisualRole.ReadOnly : VisualRole.Editable);
  1544. ClearRegion (col, row, right, row + 1);
  1545. }
  1546. row++;
  1547. }
  1548. if (row < bottom)
  1549. {
  1550. SetAttributeForRole (ReadOnly ? VisualRole.ReadOnly : VisualRole.Editable);
  1551. ClearRegion (Viewport.Left, row, right, bottom);
  1552. }
  1553. _isDrawing = false;
  1554. return false;
  1555. }
  1556. /// <inheritdoc/>
  1557. protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? view)
  1558. {
  1559. if (Application.Mouse.MouseGrabView is { } && Application.Mouse.MouseGrabView == this)
  1560. {
  1561. Application.Mouse.UngrabMouse ();
  1562. }
  1563. }
  1564. /// <inheritdoc/>
  1565. protected override bool OnKeyDown (Key key)
  1566. {
  1567. if (!key.IsValid)
  1568. {
  1569. return false;
  1570. }
  1571. // Give autocomplete first opportunity to respond to key presses
  1572. if (SelectedLength == 0 && Autocomplete.Suggestions.Count > 0 && Autocomplete.ProcessKey (key))
  1573. {
  1574. return true;
  1575. }
  1576. return false;
  1577. }
  1578. /// <inheritdoc/>
  1579. protected override bool OnKeyDownNotHandled (Key a)
  1580. {
  1581. if (!CanFocus)
  1582. {
  1583. return true;
  1584. }
  1585. ResetColumnTrack ();
  1586. // Ignore control characters and other special keys
  1587. if (!a.IsKeyCodeAtoZ && (a.KeyCode < KeyCode.Space || a.KeyCode > KeyCode.CharMask))
  1588. {
  1589. return false;
  1590. }
  1591. InsertText (a);
  1592. DoNeededAction ();
  1593. return true;
  1594. }
  1595. /// <inheritdoc/>
  1596. public override bool OnKeyUp (Key key)
  1597. {
  1598. if (key == Key.Space.WithCtrl)
  1599. {
  1600. return true;
  1601. }
  1602. return false;
  1603. }
  1604. /// <summary>Invoke the <see cref="UnwrappedCursorPosition"/> event with the unwrapped <see cref="CursorPosition"/>.</summary>
  1605. public virtual void OnUnwrappedCursorPosition (int? cRow = null, int? cCol = null)
  1606. {
  1607. int? row = cRow ?? CurrentRow;
  1608. int? col = cCol ?? CurrentColumn;
  1609. if (cRow is null && cCol is null && _wordWrap)
  1610. {
  1611. row = _wrapManager!.GetModelLineFromWrappedLines (CurrentRow);
  1612. col = _wrapManager.GetModelColFromWrappedLines (CurrentRow, CurrentColumn);
  1613. }
  1614. UnwrappedCursorPosition?.Invoke (this, new (col.Value, row.Value));
  1615. }
  1616. /// <summary>Paste the clipboard contents into the current selected position.</summary>
  1617. public void Paste ()
  1618. {
  1619. if (_isReadOnly)
  1620. {
  1621. return;
  1622. }
  1623. SetWrapModel ();
  1624. string? contents = Clipboard.Contents;
  1625. if (_copyWithoutSelection && contents.FirstOrDefault (x => x is '\n' or '\r') == 0)
  1626. {
  1627. List<Cell> runeList = contents is null ? [] : Cell.ToCellList (contents);
  1628. List<Cell> currentLine = GetCurrentLine ();
  1629. _historyText.Add ([new (currentLine)], CursorPosition);
  1630. List<List<Cell>> addedLine = [new (currentLine), runeList];
  1631. _historyText.Add (
  1632. [.. addedLine],
  1633. CursorPosition,
  1634. TextEditingLineStatus.Added
  1635. );
  1636. _model.AddLine (CurrentRow, runeList);
  1637. CurrentRow++;
  1638. _historyText.Add (
  1639. [new (GetCurrentLine ())],
  1640. CursorPosition,
  1641. TextEditingLineStatus.Replaced
  1642. );
  1643. SetNeedsDraw ();
  1644. OnContentsChanged ();
  1645. }
  1646. else
  1647. {
  1648. if (IsSelecting)
  1649. {
  1650. ClearRegion ();
  1651. }
  1652. _copyWithoutSelection = false;
  1653. InsertAllText (contents, true);
  1654. if (IsSelecting)
  1655. {
  1656. _historyText.ReplaceLast (
  1657. [new (GetCurrentLine ())],
  1658. CursorPosition,
  1659. TextEditingLineStatus.Original
  1660. );
  1661. }
  1662. SetNeedsDraw ();
  1663. }
  1664. UpdateWrapModel ();
  1665. IsSelecting = false;
  1666. DoNeededAction ();
  1667. }
  1668. /// <summary>Positions the cursor on the current row and column</summary>
  1669. public override Point? PositionCursor ()
  1670. {
  1671. ProcessAutocomplete ();
  1672. if (!CanFocus || !Enabled || Application.Driver is null)
  1673. {
  1674. return null;
  1675. }
  1676. if (Application.Mouse.MouseGrabView == this && IsSelecting)
  1677. {
  1678. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  1679. //var minRow = Math.Min (Math.Max (Math.Min (selectionStartRow, currentRow) - topRow, 0), Viewport.Height);
  1680. //var maxRow = Math.Min (Math.Max (Math.Max (selectionStartRow, currentRow) - topRow, 0), Viewport.Height);
  1681. //SetNeedsDraw (new (0, minRow, Viewport.Width, maxRow));
  1682. SetNeedsDraw ();
  1683. }
  1684. List<Cell> line = _model.GetLine (CurrentRow);
  1685. var col = 0;
  1686. if (line.Count > 0)
  1687. {
  1688. for (int idx = _leftColumn; idx < line.Count; idx++)
  1689. {
  1690. if (idx >= CurrentColumn)
  1691. {
  1692. break;
  1693. }
  1694. int cols = line [idx].Rune.GetColumns ();
  1695. if (line [idx].Rune.Value == '\t')
  1696. {
  1697. cols += TabWidth + 1;
  1698. }
  1699. else
  1700. {
  1701. // Ensures that cols less than 0 to be 1 because it will be converted to a printable rune
  1702. cols = Math.Max (cols, 1);
  1703. }
  1704. if (!TextModel.SetCol (ref col, Viewport.Width, cols))
  1705. {
  1706. col = CurrentColumn;
  1707. break;
  1708. }
  1709. }
  1710. }
  1711. int posX = CurrentColumn - _leftColumn;
  1712. int posY = CurrentRow - _topRow;
  1713. if (posX > -1 && col >= posX && posX < Viewport.Width && _topRow <= CurrentRow && posY < Viewport.Height)
  1714. {
  1715. Move (col, CurrentRow - _topRow);
  1716. return new (col, CurrentRow - _topRow);
  1717. }
  1718. return null; // Hide cursor
  1719. }
  1720. /// <summary>Redoes the latest changes.</summary>
  1721. public void Redo ()
  1722. {
  1723. if (ReadOnly)
  1724. {
  1725. return;
  1726. }
  1727. _historyText.Redo ();
  1728. }
  1729. /// <summary>Replaces all the text based on the match case.</summary>
  1730. /// <param name="textToFind">The text to find.</param>
  1731. /// <param name="matchCase">The match case setting.</param>
  1732. /// <param name="matchWholeWord">The match whole word setting.</param>
  1733. /// <param name="textToReplace">The text to replace.</param>
  1734. /// <returns><c>true</c>If the text was found.<c>false</c>otherwise.</returns>
  1735. public bool ReplaceAllText (
  1736. string textToFind,
  1737. bool matchCase = false,
  1738. bool matchWholeWord = false,
  1739. string? textToReplace = null
  1740. )
  1741. {
  1742. if (_isReadOnly || _model.Count == 0)
  1743. {
  1744. return false;
  1745. }
  1746. SetWrapModel ();
  1747. ResetContinuousFind ();
  1748. (Point current, bool found) foundPos =
  1749. _model.ReplaceAllText (textToFind, matchCase, matchWholeWord, textToReplace);
  1750. return SetFoundText (textToFind, foundPos, textToReplace, false, true);
  1751. }
  1752. /// <summary>
  1753. /// Will scroll the <see cref="TextView"/> to display the specified row at the top if <paramref name="isRow"/> is
  1754. /// true or will scroll the <see cref="TextView"/> to display the specified column at the left if
  1755. /// <paramref name="isRow"/> is false.
  1756. /// </summary>
  1757. /// <param name="idx">
  1758. /// Row that should be displayed at the top or Column that should be displayed at the left, if the value
  1759. /// is negative it will be reset to zero
  1760. /// </param>
  1761. /// <param name="isRow">If true (default) the <paramref name="idx"/> is a row, column otherwise.</param>
  1762. public void ScrollTo (int idx, bool isRow = true)
  1763. {
  1764. if (idx < 0)
  1765. {
  1766. idx = 0;
  1767. }
  1768. if (isRow)
  1769. {
  1770. _topRow = Math.Max (idx > _model.Count - 1 ? _model.Count - 1 : idx, 0);
  1771. }
  1772. else if (!_wordWrap)
  1773. {
  1774. int maxlength =
  1775. _model.GetMaxVisibleLine (_topRow, _topRow + Viewport.Height, TabWidth);
  1776. _leftColumn = Math.Max (!_wordWrap && idx > maxlength - 1 ? maxlength - 1 : idx, 0);
  1777. }
  1778. SetNeedsDraw ();
  1779. }
  1780. /// <summary>Select all text.</summary>
  1781. public void SelectAll ()
  1782. {
  1783. if (_model.Count == 0)
  1784. {
  1785. return;
  1786. }
  1787. StartSelecting ();
  1788. _selectionStartColumn = 0;
  1789. _selectionStartRow = 0;
  1790. CurrentColumn = _model.GetLine (_model.Count - 1).Count;
  1791. CurrentRow = _model.Count - 1;
  1792. SetNeedsDraw ();
  1793. }
  1794. ///// <summary>Raised when the <see cref="Text"/> property of the <see cref="TextView"/> changes.</summary>
  1795. ///// <remarks>
  1796. ///// The <see cref="Text"/> property of <see cref="TextView"/> only changes when it is explicitly set, not as the
  1797. ///// user types. To be notified as the user changes the contents of the TextView see <see cref="IsDirty"/>.
  1798. ///// </remarks>
  1799. //public event EventHandler? TextChanged;
  1800. /// <summary>Undoes the latest changes.</summary>
  1801. public void Undo ()
  1802. {
  1803. if (ReadOnly)
  1804. {
  1805. return;
  1806. }
  1807. _historyText.Undo ();
  1808. }
  1809. /// <summary>Invoked with the unwrapped <see cref="CursorPosition"/>.</summary>
  1810. public event EventHandler<Point>? UnwrappedCursorPosition;
  1811. /// <summary>
  1812. /// Sets the <see cref="View.Driver"/> to an appropriate color for rendering the given <paramref name="idxCol"/>
  1813. /// of the current <paramref name="line"/>. Override to provide custom coloring by calling
  1814. /// <see cref="View.SetAttribute"/> Defaults to <see cref="Scheme.Normal"/>.
  1815. /// </summary>
  1816. /// <param name="line">The line.</param>
  1817. /// <param name="idxCol">The col index.</param>
  1818. /// <param name="idxRow">The row index.</param>
  1819. protected virtual void OnDrawNormalColor (List<Cell> line, int idxCol, int idxRow)
  1820. {
  1821. (int Row, int Col) unwrappedPos = GetUnwrappedPosition (idxRow, idxCol);
  1822. var ev = new CellEventArgs (line, idxCol, unwrappedPos);
  1823. DrawNormalColor?.Invoke (this, ev);
  1824. if (line [idxCol].Attribute is { })
  1825. {
  1826. Attribute? attribute = line [idxCol].Attribute;
  1827. SetAttribute ((Attribute)attribute!);
  1828. }
  1829. else
  1830. {
  1831. SetAttribute (GetAttributeForRole (VisualRole.Normal));
  1832. }
  1833. }
  1834. /// <summary>
  1835. /// Sets the <see cref="View.Driver"/> to an appropriate color for rendering the given <paramref name="idxCol"/>
  1836. /// of the current <paramref name="line"/>. Override to provide custom coloring by calling
  1837. /// <see cref="View.SetAttribute(Attribute)"/> Defaults to <see cref="Scheme.Focus"/>.
  1838. /// </summary>
  1839. /// <param name="line">The line.</param>
  1840. /// <param name="idxCol">The col index.</param>
  1841. /// ///
  1842. /// <param name="idxRow">The row index.</param>
  1843. protected virtual void OnDrawReadOnlyColor (List<Cell> line, int idxCol, int idxRow)
  1844. {
  1845. (int Row, int Col) unwrappedPos = GetUnwrappedPosition (idxRow, idxCol);
  1846. var ev = new CellEventArgs (line, idxCol, unwrappedPos);
  1847. DrawReadOnlyColor?.Invoke (this, ev);
  1848. Attribute? cellAttribute = line [idxCol].Attribute is { } ? line [idxCol].Attribute : GetAttributeForRole (VisualRole.ReadOnly);
  1849. if (cellAttribute!.Value.Foreground == cellAttribute.Value.Background)
  1850. {
  1851. SetAttribute (new (cellAttribute.Value.Foreground, cellAttribute.Value.Background, cellAttribute.Value.Style));
  1852. }
  1853. else
  1854. {
  1855. SetAttributeForRole (VisualRole.ReadOnly);
  1856. }
  1857. }
  1858. /// <summary>
  1859. /// Sets the <see cref="View.Driver"/> to an appropriate color for rendering the given <paramref name="idxCol"/>
  1860. /// of the current <paramref name="line"/>. Override to provide custom coloring by calling
  1861. /// <see cref="View.SetAttribute(Attribute)"/> Defaults to <see cref="Scheme.Focus"/>.
  1862. /// </summary>
  1863. /// <param name="line">The line.</param>
  1864. /// <param name="idxCol">The col index.</param>
  1865. /// ///
  1866. /// <param name="idxRow">The row index.</param>
  1867. protected virtual void OnDrawSelectionColor (List<Cell> line, int idxCol, int idxRow)
  1868. {
  1869. (int Row, int Col) unwrappedPos = GetUnwrappedPosition (idxRow, idxCol);
  1870. var ev = new CellEventArgs (line, idxCol, unwrappedPos);
  1871. DrawSelectionColor?.Invoke (this, ev);
  1872. if (line [idxCol].Attribute is { })
  1873. {
  1874. Attribute? attribute = line [idxCol].Attribute;
  1875. Attribute? active = GetAttributeForRole (VisualRole.Active);
  1876. SetAttribute (new (active!.Value.Foreground, active.Value.Background, attribute!.Value.Style));
  1877. }
  1878. else
  1879. {
  1880. SetAttributeForRole (VisualRole.Active);
  1881. }
  1882. }
  1883. /// <summary>
  1884. /// Sets the <see cref="View.Driver"/> to an appropriate color for rendering the given <paramref name="idxCol"/>
  1885. /// of the current <paramref name="line"/>. Override to provide custom coloring by calling
  1886. /// <see cref="View.SetAttribute(Attribute)"/> Defaults to <see cref="Scheme.HotFocus"/>.
  1887. /// </summary>
  1888. /// <param name="line">The line.</param>
  1889. /// <param name="idxCol">The col index.</param>
  1890. /// ///
  1891. /// <param name="idxRow">The row index.</param>
  1892. protected virtual void OnDrawUsedColor (List<Cell> line, int idxCol, int idxRow)
  1893. {
  1894. (int Row, int Col) unwrappedPos = GetUnwrappedPosition (idxRow, idxCol);
  1895. var ev = new CellEventArgs (line, idxCol, unwrappedPos);
  1896. DrawUsedColor?.Invoke (this, ev);
  1897. if (line [idxCol].Attribute is { })
  1898. {
  1899. Attribute? attribute = line [idxCol].Attribute;
  1900. SetValidUsedColor (attribute!);
  1901. }
  1902. else
  1903. {
  1904. SetValidUsedColor (GetAttributeForRole (VisualRole.Focus));
  1905. }
  1906. }
  1907. private void Adjust ()
  1908. {
  1909. (int width, int height) offB = OffSetBackground ();
  1910. List<Cell> line = GetCurrentLine ();
  1911. bool need = NeedsDraw || _wrapNeeded || !Used;
  1912. (int size, int length) tSize = TextModel.DisplaySize (line, -1, -1, false, TabWidth);
  1913. (int size, int length) dSize = TextModel.DisplaySize (line, _leftColumn, CurrentColumn, true, TabWidth);
  1914. if (!_wordWrap && CurrentColumn < _leftColumn)
  1915. {
  1916. _leftColumn = CurrentColumn;
  1917. need = true;
  1918. }
  1919. else if (!_wordWrap
  1920. && (CurrentColumn - _leftColumn + 1 > Viewport.Width + offB.width || dSize.size + 1 >= Viewport.Width + offB.width))
  1921. {
  1922. _leftColumn = TextModel.CalculateLeftColumn (
  1923. line,
  1924. _leftColumn,
  1925. CurrentColumn,
  1926. Viewport.Width + offB.width,
  1927. TabWidth
  1928. );
  1929. need = true;
  1930. }
  1931. else if ((_wordWrap && _leftColumn > 0) || (dSize.size < Viewport.Width + offB.width && tSize.size < Viewport.Width + offB.width))
  1932. {
  1933. if (_leftColumn > 0)
  1934. {
  1935. _leftColumn = 0;
  1936. need = true;
  1937. }
  1938. }
  1939. if (CurrentRow < _topRow)
  1940. {
  1941. _topRow = CurrentRow;
  1942. need = true;
  1943. }
  1944. else if (CurrentRow - _topRow >= Viewport.Height + offB.height)
  1945. {
  1946. _topRow = Math.Min (Math.Max (CurrentRow - Viewport.Height + 1, 0), CurrentRow);
  1947. need = true;
  1948. }
  1949. else if (_topRow > 0 && CurrentRow < _topRow)
  1950. {
  1951. _topRow = Math.Max (_topRow - 1, 0);
  1952. need = true;
  1953. }
  1954. if (need)
  1955. {
  1956. if (_wrapNeeded)
  1957. {
  1958. WrapTextModel ();
  1959. _wrapNeeded = false;
  1960. }
  1961. SetNeedsDraw ();
  1962. }
  1963. else
  1964. {
  1965. if (IsInitialized)
  1966. {
  1967. PositionCursor ();
  1968. }
  1969. }
  1970. OnUnwrappedCursorPosition ();
  1971. }
  1972. private void AppendClipboard (string text) { Clipboard.Contents += text; }
  1973. private PopoverMenu CreateContextMenu ()
  1974. {
  1975. PopoverMenu menu = new (
  1976. new List<View>
  1977. {
  1978. new MenuItemv2 (this, Command.SelectAll, Strings.ctxSelectAll),
  1979. new MenuItemv2 (this, Command.DeleteAll, Strings.ctxDeleteAll),
  1980. new MenuItemv2 (this, Command.Copy, Strings.ctxCopy),
  1981. new MenuItemv2 (this, Command.Cut, Strings.ctxCut),
  1982. new MenuItemv2 (this, Command.Paste, Strings.ctxPaste),
  1983. new MenuItemv2 (this, Command.Undo, Strings.ctxUndo),
  1984. new MenuItemv2 (this, Command.Redo, Strings.ctxRedo)
  1985. });
  1986. menu.KeyChanged += ContextMenu_KeyChanged;
  1987. return menu;
  1988. }
  1989. private void ClearRegion (int left, int top, int right, int bottom)
  1990. {
  1991. for (int row = top; row < bottom; row++)
  1992. {
  1993. Move (left, row);
  1994. for (int col = left; col < right; col++)
  1995. {
  1996. AddRune (col, row, (Rune)' ');
  1997. }
  1998. }
  1999. }
  2000. //
  2001. // Clears the contents of the selected region
  2002. //
  2003. private void ClearRegion ()
  2004. {
  2005. SetWrapModel ();
  2006. long start, end;
  2007. long currentEncoded = ((long)(uint)CurrentRow << 32) | (uint)CurrentColumn;
  2008. GetEncodedRegionBounds (out start, out end);
  2009. var startRow = (int)(start >> 32);
  2010. var maxrow = (int)(end >> 32);
  2011. var startCol = (int)(start & 0xffffffff);
  2012. var endCol = (int)(end & 0xffffffff);
  2013. List<Cell> line = _model.GetLine (startRow);
  2014. _historyText.Add (new () { new (line) }, new (startCol, startRow));
  2015. List<List<Cell>> removedLines = new ();
  2016. if (startRow == maxrow)
  2017. {
  2018. removedLines.Add (new (line));
  2019. line.RemoveRange (startCol, endCol - startCol);
  2020. CurrentColumn = startCol;
  2021. if (_wordWrap)
  2022. {
  2023. SetNeedsDraw ();
  2024. }
  2025. else
  2026. {
  2027. //QUESTION: Is the below comment still relevant?
  2028. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  2029. //SetNeedsDraw (new (0, startRow - topRow, Viewport.Width, startRow - topRow + 1));
  2030. SetNeedsDraw ();
  2031. }
  2032. _historyText.Add (
  2033. new (removedLines),
  2034. CursorPosition,
  2035. TextEditingLineStatus.Removed
  2036. );
  2037. UpdateWrapModel ();
  2038. return;
  2039. }
  2040. removedLines.Add (new (line));
  2041. line.RemoveRange (startCol, line.Count - startCol);
  2042. List<Cell> line2 = _model.GetLine (maxrow);
  2043. line.AddRange (line2.Skip (endCol));
  2044. for (int row = startRow + 1; row <= maxrow; row++)
  2045. {
  2046. removedLines.Add (new (_model.GetLine (startRow + 1)));
  2047. _model.RemoveLine (startRow + 1);
  2048. }
  2049. if (currentEncoded == end)
  2050. {
  2051. CurrentRow -= maxrow - startRow;
  2052. }
  2053. CurrentColumn = startCol;
  2054. _historyText.Add (
  2055. new (removedLines),
  2056. CursorPosition,
  2057. TextEditingLineStatus.Removed
  2058. );
  2059. UpdateWrapModel ();
  2060. SetNeedsDraw ();
  2061. }
  2062. private void ClearSelectedRegion ()
  2063. {
  2064. SetWrapModel ();
  2065. if (!_isReadOnly)
  2066. {
  2067. ClearRegion ();
  2068. }
  2069. UpdateWrapModel ();
  2070. IsSelecting = false;
  2071. DoNeededAction ();
  2072. }
  2073. private void ContextMenu_KeyChanged (object? sender, KeyChangedEventArgs e) { KeyBindings.Replace (e.OldKey, e.NewKey); }
  2074. private bool DeleteTextBackwards ()
  2075. {
  2076. SetWrapModel ();
  2077. if (CurrentColumn > 0)
  2078. {
  2079. // Delete backwards
  2080. List<Cell> currentLine = GetCurrentLine ();
  2081. _historyText.Add (new () { new (currentLine) }, CursorPosition);
  2082. currentLine.RemoveAt (CurrentColumn - 1);
  2083. if (_wordWrap)
  2084. {
  2085. _wrapNeeded = true;
  2086. }
  2087. CurrentColumn--;
  2088. _historyText.Add (
  2089. new () { new (currentLine) },
  2090. CursorPosition,
  2091. TextEditingLineStatus.Replaced
  2092. );
  2093. if (CurrentColumn < _leftColumn)
  2094. {
  2095. _leftColumn--;
  2096. SetNeedsDraw ();
  2097. }
  2098. else
  2099. {
  2100. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  2101. //SetNeedsDraw (new (0, currentRow - topRow, 1, Viewport.Width));
  2102. SetNeedsDraw ();
  2103. }
  2104. }
  2105. else
  2106. {
  2107. // Merges the current line with the previous one.
  2108. if (CurrentRow == 0)
  2109. {
  2110. return true;
  2111. }
  2112. int prowIdx = CurrentRow - 1;
  2113. List<Cell> prevRow = _model.GetLine (prowIdx);
  2114. _historyText.Add (new () { new (prevRow) }, CursorPosition);
  2115. List<List<Cell>> removedLines = new () { new (prevRow) };
  2116. removedLines.Add (new (GetCurrentLine ()));
  2117. _historyText.Add (
  2118. removedLines,
  2119. new (CurrentColumn, prowIdx),
  2120. TextEditingLineStatus.Removed
  2121. );
  2122. int prevCount = prevRow.Count;
  2123. _model.GetLine (prowIdx).AddRange (GetCurrentLine ());
  2124. _model.RemoveLine (CurrentRow);
  2125. if (_wordWrap)
  2126. {
  2127. _wrapNeeded = true;
  2128. }
  2129. CurrentRow--;
  2130. _historyText.Add (
  2131. new () { GetCurrentLine () },
  2132. new (CurrentColumn, prowIdx),
  2133. TextEditingLineStatus.Replaced
  2134. );
  2135. CurrentColumn = prevCount;
  2136. SetNeedsDraw ();
  2137. }
  2138. UpdateWrapModel ();
  2139. return false;
  2140. }
  2141. private bool DeleteTextForwards ()
  2142. {
  2143. SetWrapModel ();
  2144. List<Cell> currentLine = GetCurrentLine ();
  2145. if (CurrentColumn == currentLine.Count)
  2146. {
  2147. if (CurrentRow + 1 == _model.Count)
  2148. {
  2149. UpdateWrapModel ();
  2150. return true;
  2151. }
  2152. _historyText.Add (new () { new (currentLine) }, CursorPosition);
  2153. List<List<Cell>> removedLines = new () { new (currentLine) };
  2154. List<Cell> nextLine = _model.GetLine (CurrentRow + 1);
  2155. removedLines.Add (new (nextLine));
  2156. _historyText.Add (removedLines, CursorPosition, TextEditingLineStatus.Removed);
  2157. currentLine.AddRange (nextLine);
  2158. _model.RemoveLine (CurrentRow + 1);
  2159. _historyText.Add (
  2160. new () { new (currentLine) },
  2161. CursorPosition,
  2162. TextEditingLineStatus.Replaced
  2163. );
  2164. if (_wordWrap)
  2165. {
  2166. _wrapNeeded = true;
  2167. }
  2168. DoSetNeedsDraw (new (0, CurrentRow - _topRow, Viewport.Width, CurrentRow - _topRow + 1));
  2169. }
  2170. else
  2171. {
  2172. _historyText.Add ([ [.. currentLine]], CursorPosition);
  2173. currentLine.RemoveAt (CurrentColumn);
  2174. _historyText.Add (
  2175. [ [.. currentLine]],
  2176. CursorPosition,
  2177. TextEditingLineStatus.Replaced
  2178. );
  2179. if (_wordWrap)
  2180. {
  2181. _wrapNeeded = true;
  2182. }
  2183. DoSetNeedsDraw (
  2184. new (
  2185. CurrentColumn - _leftColumn,
  2186. CurrentRow - _topRow,
  2187. Viewport.Width,
  2188. Math.Max (CurrentRow - _topRow + 1, 0)
  2189. )
  2190. );
  2191. }
  2192. UpdateWrapModel ();
  2193. return false;
  2194. }
  2195. private void DoNeededAction ()
  2196. {
  2197. if (!NeedsDraw && (IsSelecting || _wrapNeeded || !Used))
  2198. {
  2199. SetNeedsDraw ();
  2200. }
  2201. if (NeedsDraw)
  2202. {
  2203. Adjust ();
  2204. }
  2205. else
  2206. {
  2207. PositionCursor ();
  2208. OnUnwrappedCursorPosition ();
  2209. }
  2210. }
  2211. private void DoSetNeedsDraw (Rectangle rect)
  2212. {
  2213. if (_wrapNeeded)
  2214. {
  2215. SetNeedsDraw ();
  2216. }
  2217. else
  2218. {
  2219. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  2220. //SetNeedsDraw (rect);
  2221. SetNeedsDraw ();
  2222. }
  2223. }
  2224. private IEnumerable<(int col, int row, Cell rune)> ForwardIterator (int col, int row)
  2225. {
  2226. if (col < 0 || row < 0)
  2227. {
  2228. yield break;
  2229. }
  2230. if (row >= _model.Count)
  2231. {
  2232. yield break;
  2233. }
  2234. List<Cell> line = GetCurrentLine ();
  2235. if (col >= line.Count)
  2236. {
  2237. yield break;
  2238. }
  2239. while (row < _model.Count)
  2240. {
  2241. for (int c = col; c < line.Count; c++)
  2242. {
  2243. yield return (c, row, line [c]);
  2244. }
  2245. col = 0;
  2246. row++;
  2247. line = GetCurrentLine ();
  2248. }
  2249. }
  2250. private void GenerateSuggestions ()
  2251. {
  2252. List<Cell> currentLine = GetCurrentLine ();
  2253. int cursorPosition = Math.Min (CurrentColumn, currentLine.Count);
  2254. Autocomplete.Context = new (
  2255. currentLine,
  2256. cursorPosition,
  2257. Autocomplete.Context != null
  2258. ? Autocomplete.Context.Canceled
  2259. : false
  2260. );
  2261. Autocomplete.GenerateSuggestions (
  2262. Autocomplete.Context
  2263. );
  2264. }
  2265. // Returns an encoded region start..end (top 32 bits are the row, low32 the column)
  2266. private void GetEncodedRegionBounds (
  2267. out long start,
  2268. out long end,
  2269. int? startRow = null,
  2270. int? startCol = null,
  2271. int? cRow = null,
  2272. int? cCol = null
  2273. )
  2274. {
  2275. long selection;
  2276. long point;
  2277. if (startRow is null || startCol is null || cRow is null || cCol is null)
  2278. {
  2279. selection = ((long)(uint)_selectionStartRow << 32) | (uint)_selectionStartColumn;
  2280. point = ((long)(uint)CurrentRow << 32) | (uint)CurrentColumn;
  2281. }
  2282. else
  2283. {
  2284. selection = ((long)(uint)startRow << 32) | (uint)startCol;
  2285. point = ((long)(uint)cRow << 32) | (uint)cCol;
  2286. }
  2287. if (selection > point)
  2288. {
  2289. start = point;
  2290. end = selection;
  2291. }
  2292. else
  2293. {
  2294. start = selection;
  2295. end = point;
  2296. }
  2297. }
  2298. //
  2299. // Returns a string with the text in the selected
  2300. // region.
  2301. //
  2302. internal string GetRegion (
  2303. out List<List<Cell>> cellsList,
  2304. int? sRow = null,
  2305. int? sCol = null,
  2306. int? cRow = null,
  2307. int? cCol = null,
  2308. TextModel? model = null
  2309. )
  2310. {
  2311. GetEncodedRegionBounds (out long start, out long end, sRow, sCol, cRow, cCol);
  2312. cellsList = [];
  2313. if (start == end)
  2314. {
  2315. return string.Empty;
  2316. }
  2317. var startRow = (int)(start >> 32);
  2318. var maxRow = (int)(end >> 32);
  2319. var startCol = (int)(start & 0xffffffff);
  2320. var endCol = (int)(end & 0xffffffff);
  2321. List<Cell> line = model is null ? _model.GetLine (startRow) : model.GetLine (startRow);
  2322. List<Cell> cells;
  2323. if (startRow == maxRow)
  2324. {
  2325. cells = line.GetRange (startCol, endCol - startCol);
  2326. cellsList.Add (cells);
  2327. return StringFromRunes (cells);
  2328. }
  2329. cells = line.GetRange (startCol, line.Count - startCol);
  2330. cellsList.Add (cells);
  2331. string res = StringFromRunes (cells);
  2332. for (int row = startRow + 1; row < maxRow; row++)
  2333. {
  2334. cellsList.AddRange ([]);
  2335. cells = model == null ? _model.GetLine (row) : model.GetLine (row);
  2336. cellsList.Add (cells);
  2337. res = res
  2338. + Environment.NewLine
  2339. + StringFromRunes (cells);
  2340. }
  2341. line = model is null ? _model.GetLine (maxRow) : model.GetLine (maxRow);
  2342. cellsList.AddRange ([]);
  2343. cells = line.GetRange (0, endCol);
  2344. cellsList.Add (cells);
  2345. res = res + Environment.NewLine + StringFromRunes (cells);
  2346. return res;
  2347. }
  2348. private int GetSelectedLength () { return SelectedText.Length; }
  2349. private string GetSelectedRegion ()
  2350. {
  2351. int cRow = CurrentRow;
  2352. int cCol = CurrentColumn;
  2353. int startRow = _selectionStartRow;
  2354. int startCol = _selectionStartColumn;
  2355. TextModel model = _model;
  2356. if (_wordWrap)
  2357. {
  2358. cRow = _wrapManager!.GetModelLineFromWrappedLines (CurrentRow);
  2359. cCol = _wrapManager.GetModelColFromWrappedLines (CurrentRow, CurrentColumn);
  2360. startRow = _wrapManager.GetModelLineFromWrappedLines (_selectionStartRow);
  2361. startCol = _wrapManager.GetModelColFromWrappedLines (_selectionStartRow, _selectionStartColumn);
  2362. model = _wrapManager.Model;
  2363. }
  2364. OnUnwrappedCursorPosition (cRow, cCol);
  2365. return GetRegion (out _, startRow, startCol, cRow, cCol, model);
  2366. }
  2367. private (int Row, int Col) GetUnwrappedPosition (int line, int col)
  2368. {
  2369. if (WordWrap)
  2370. {
  2371. return new ValueTuple<int, int> (
  2372. _wrapManager!.GetModelLineFromWrappedLines (line),
  2373. _wrapManager.GetModelColFromWrappedLines (line, col)
  2374. );
  2375. }
  2376. return new ValueTuple<int, int> (line, col);
  2377. }
  2378. private void HistoryText_ChangeText (object sender, HistoryTextItemEventArgs obj)
  2379. {
  2380. SetWrapModel ();
  2381. if (obj is { })
  2382. {
  2383. int startLine = obj.CursorPosition.Y;
  2384. if (obj.RemovedOnAdded is { })
  2385. {
  2386. int offset;
  2387. if (obj.IsUndoing)
  2388. {
  2389. offset = Math.Max (obj.RemovedOnAdded.Lines.Count - obj.Lines.Count, 1);
  2390. }
  2391. else
  2392. {
  2393. offset = obj.RemovedOnAdded.Lines.Count - 1;
  2394. }
  2395. for (var i = 0; i < offset; i++)
  2396. {
  2397. if (Lines > obj.RemovedOnAdded.CursorPosition.Y)
  2398. {
  2399. _model.RemoveLine (obj.RemovedOnAdded.CursorPosition.Y);
  2400. }
  2401. else
  2402. {
  2403. break;
  2404. }
  2405. }
  2406. }
  2407. for (var i = 0; i < obj.Lines.Count; i++)
  2408. {
  2409. if (i == 0 || obj.LineStatus == TextEditingLineStatus.Original || obj.LineStatus == TextEditingLineStatus.Attribute)
  2410. {
  2411. _model.ReplaceLine (startLine, obj.Lines [i]);
  2412. }
  2413. else if (obj is { IsUndoing: true, LineStatus: TextEditingLineStatus.Removed }
  2414. or { IsUndoing: false, LineStatus: TextEditingLineStatus.Added })
  2415. {
  2416. _model.AddLine (startLine, obj.Lines [i]);
  2417. }
  2418. else if (Lines > obj.CursorPosition.Y + 1)
  2419. {
  2420. _model.RemoveLine (obj.CursorPosition.Y + 1);
  2421. }
  2422. startLine++;
  2423. }
  2424. CursorPosition = obj.FinalCursorPosition;
  2425. }
  2426. UpdateWrapModel ();
  2427. Adjust ();
  2428. OnContentsChanged ();
  2429. }
  2430. private void Insert (Cell cell)
  2431. {
  2432. List<Cell> line = GetCurrentLine ();
  2433. if (Used)
  2434. {
  2435. line.Insert (Math.Min (CurrentColumn, line.Count), cell);
  2436. }
  2437. else
  2438. {
  2439. if (CurrentColumn < line.Count)
  2440. {
  2441. line.RemoveAt (CurrentColumn);
  2442. }
  2443. line.Insert (Math.Min (CurrentColumn, line.Count), cell);
  2444. }
  2445. int prow = CurrentRow - _topRow;
  2446. if (!_wrapNeeded)
  2447. {
  2448. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  2449. //SetNeedsDraw (new (0, prow, Math.Max (Viewport.Width, 0), Math.Max (prow + 1, 0)));
  2450. SetNeedsDraw ();
  2451. }
  2452. }
  2453. private void InsertAllText (string text, bool fromClipboard = false)
  2454. {
  2455. if (string.IsNullOrEmpty (text))
  2456. {
  2457. return;
  2458. }
  2459. List<List<Cell>> lines;
  2460. if (fromClipboard && text == _copiedText)
  2461. {
  2462. lines = _copiedCellsList;
  2463. }
  2464. else
  2465. {
  2466. // Get selected attribute
  2467. Attribute? attribute = GetSelectedAttribute (CurrentRow, CurrentColumn);
  2468. lines = Cell.StringToLinesOfCells (text, attribute);
  2469. }
  2470. if (lines.Count == 0)
  2471. {
  2472. return;
  2473. }
  2474. SetWrapModel ();
  2475. List<Cell> line = GetCurrentLine ();
  2476. _historyText.Add ([new (line)], CursorPosition);
  2477. // Optimize single line
  2478. if (lines.Count == 1)
  2479. {
  2480. line.InsertRange (CurrentColumn, lines [0]);
  2481. CurrentColumn += lines [0].Count;
  2482. _historyText.Add (
  2483. [new (line)],
  2484. CursorPosition,
  2485. TextEditingLineStatus.Replaced
  2486. );
  2487. if (!_wordWrap && CurrentColumn - _leftColumn > Viewport.Width)
  2488. {
  2489. _leftColumn = Math.Max (CurrentColumn - Viewport.Width + 1, 0);
  2490. }
  2491. if (_wordWrap)
  2492. {
  2493. SetNeedsDraw ();
  2494. }
  2495. else
  2496. {
  2497. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  2498. //SetNeedsDraw (new (0, currentRow - topRow, Viewport.Width, Math.Max (currentRow - topRow + 1, 0)));
  2499. SetNeedsDraw ();
  2500. }
  2501. UpdateWrapModel ();
  2502. OnContentsChanged ();
  2503. return;
  2504. }
  2505. List<Cell>? rest = null;
  2506. var lastPosition = 0;
  2507. if (_model.Count > 0 && line.Count > 0 && !_copyWithoutSelection)
  2508. {
  2509. // Keep a copy of the rest of the line
  2510. int restCount = line.Count - CurrentColumn;
  2511. rest = line.GetRange (CurrentColumn, restCount);
  2512. line.RemoveRange (CurrentColumn, restCount);
  2513. }
  2514. // First line is inserted at the current location, the rest is appended
  2515. line.InsertRange (CurrentColumn, lines [0]);
  2516. //model.AddLine (currentRow, lines [0]);
  2517. List<List<Cell>> addedLines = [new (line)];
  2518. for (var i = 1; i < lines.Count; i++)
  2519. {
  2520. _model.AddLine (CurrentRow + i, lines [i]);
  2521. addedLines.Add ([.. lines [i]]);
  2522. }
  2523. if (rest is { })
  2524. {
  2525. List<Cell> last = _model.GetLine (CurrentRow + lines.Count - 1);
  2526. lastPosition = last.Count;
  2527. last.InsertRange (last.Count, rest);
  2528. addedLines.Last ().InsertRange (addedLines.Last ().Count, rest);
  2529. }
  2530. _historyText.Add (addedLines, CursorPosition, TextEditingLineStatus.Added);
  2531. // Now adjust column and row positions
  2532. CurrentRow += lines.Count - 1;
  2533. CurrentColumn = rest is { } ? lastPosition : lines [^1].Count;
  2534. Adjust ();
  2535. _historyText.Add (
  2536. [new (line)],
  2537. CursorPosition,
  2538. TextEditingLineStatus.Replaced
  2539. );
  2540. UpdateWrapModel ();
  2541. OnContentsChanged ();
  2542. }
  2543. private bool InsertText (Key a, Attribute? attribute = null)
  2544. {
  2545. //So that special keys like tab can be processed
  2546. if (_isReadOnly)
  2547. {
  2548. return true;
  2549. }
  2550. SetWrapModel ();
  2551. _historyText.Add ([new (GetCurrentLine ())], CursorPosition);
  2552. if (IsSelecting)
  2553. {
  2554. ClearSelectedRegion ();
  2555. }
  2556. if ((uint)a.KeyCode == '\n')
  2557. {
  2558. _model.AddLine (CurrentRow + 1, []);
  2559. CurrentRow++;
  2560. CurrentColumn = 0;
  2561. }
  2562. else if ((uint)a.KeyCode == '\r')
  2563. {
  2564. CurrentColumn = 0;
  2565. }
  2566. else
  2567. {
  2568. if (Used)
  2569. {
  2570. Insert (new () { Rune = a.AsRune, Attribute = attribute });
  2571. CurrentColumn++;
  2572. if (CurrentColumn >= _leftColumn + Viewport.Width)
  2573. {
  2574. _leftColumn++;
  2575. SetNeedsDraw ();
  2576. }
  2577. }
  2578. else
  2579. {
  2580. Insert (new () { Rune = a.AsRune, Attribute = attribute });
  2581. CurrentColumn++;
  2582. }
  2583. }
  2584. _historyText.Add (
  2585. [new (GetCurrentLine ())],
  2586. CursorPosition,
  2587. TextEditingLineStatus.Replaced
  2588. );
  2589. UpdateWrapModel ();
  2590. OnContentsChanged ();
  2591. return true;
  2592. }
  2593. private void KillToEndOfLine ()
  2594. {
  2595. if (_isReadOnly)
  2596. {
  2597. return;
  2598. }
  2599. if (_model.Count == 1 && GetCurrentLine ().Count == 0)
  2600. {
  2601. // Prevents from adding line feeds if there is no more lines.
  2602. return;
  2603. }
  2604. SetWrapModel ();
  2605. List<Cell> currentLine = GetCurrentLine ();
  2606. var setLastWasKill = true;
  2607. if (currentLine.Count > 0 && CurrentColumn == currentLine.Count)
  2608. {
  2609. UpdateWrapModel ();
  2610. DeleteTextForwards ();
  2611. return;
  2612. }
  2613. _historyText.Add (new () { new (currentLine) }, CursorPosition);
  2614. if (currentLine.Count == 0)
  2615. {
  2616. if (CurrentRow < _model.Count - 1)
  2617. {
  2618. List<List<Cell>> removedLines = new () { new (currentLine) };
  2619. _model.RemoveLine (CurrentRow);
  2620. removedLines.Add (new (GetCurrentLine ()));
  2621. _historyText.Add (
  2622. new (removedLines),
  2623. CursorPosition,
  2624. TextEditingLineStatus.Removed
  2625. );
  2626. }
  2627. if (_model.Count > 0 || _lastWasKill)
  2628. {
  2629. string val = Environment.NewLine;
  2630. if (_lastWasKill)
  2631. {
  2632. AppendClipboard (val);
  2633. }
  2634. else
  2635. {
  2636. SetClipboard (val);
  2637. }
  2638. }
  2639. if (_model.Count == 0)
  2640. {
  2641. // Prevents from adding line feeds if there is no more lines.
  2642. setLastWasKill = false;
  2643. }
  2644. }
  2645. else
  2646. {
  2647. int restCount = currentLine.Count - CurrentColumn;
  2648. List<Cell> rest = currentLine.GetRange (CurrentColumn, restCount);
  2649. var val = string.Empty;
  2650. val += StringFromRunes (rest);
  2651. if (_lastWasKill)
  2652. {
  2653. AppendClipboard (val);
  2654. }
  2655. else
  2656. {
  2657. SetClipboard (val);
  2658. }
  2659. currentLine.RemoveRange (CurrentColumn, restCount);
  2660. }
  2661. _historyText.Add (
  2662. [ [.. GetCurrentLine ()]],
  2663. CursorPosition,
  2664. TextEditingLineStatus.Replaced
  2665. );
  2666. UpdateWrapModel ();
  2667. DoSetNeedsDraw (new (0, CurrentRow - _topRow, Viewport.Width, Viewport.Height));
  2668. _lastWasKill = setLastWasKill;
  2669. DoNeededAction ();
  2670. }
  2671. private void KillToLeftStart ()
  2672. {
  2673. if (_isReadOnly)
  2674. {
  2675. return;
  2676. }
  2677. if (_model.Count == 1 && GetCurrentLine ().Count == 0)
  2678. {
  2679. // Prevents from adding line feeds if there is no more lines.
  2680. return;
  2681. }
  2682. SetWrapModel ();
  2683. List<Cell> currentLine = GetCurrentLine ();
  2684. var setLastWasKill = true;
  2685. if (currentLine.Count > 0 && CurrentColumn == 0)
  2686. {
  2687. UpdateWrapModel ();
  2688. DeleteTextBackwards ();
  2689. return;
  2690. }
  2691. _historyText.Add ([ [.. currentLine]], CursorPosition);
  2692. if (currentLine.Count == 0)
  2693. {
  2694. if (CurrentRow > 0)
  2695. {
  2696. _model.RemoveLine (CurrentRow);
  2697. if (_model.Count > 0 || _lastWasKill)
  2698. {
  2699. string val = Environment.NewLine;
  2700. if (_lastWasKill)
  2701. {
  2702. AppendClipboard (val);
  2703. }
  2704. else
  2705. {
  2706. SetClipboard (val);
  2707. }
  2708. }
  2709. if (_model.Count == 0)
  2710. {
  2711. // Prevents from adding line feeds if there is no more lines.
  2712. setLastWasKill = false;
  2713. }
  2714. CurrentRow--;
  2715. currentLine = _model.GetLine (CurrentRow);
  2716. List<List<Cell>> removedLine =
  2717. [
  2718. [..currentLine],
  2719. []
  2720. ];
  2721. _historyText.Add (
  2722. [.. removedLine],
  2723. CursorPosition,
  2724. TextEditingLineStatus.Removed
  2725. );
  2726. CurrentColumn = currentLine.Count;
  2727. }
  2728. }
  2729. else
  2730. {
  2731. int restCount = CurrentColumn;
  2732. List<Cell> rest = currentLine.GetRange (0, restCount);
  2733. var val = string.Empty;
  2734. val += StringFromRunes (rest);
  2735. if (_lastWasKill)
  2736. {
  2737. AppendClipboard (val);
  2738. }
  2739. else
  2740. {
  2741. SetClipboard (val);
  2742. }
  2743. currentLine.RemoveRange (0, restCount);
  2744. CurrentColumn = 0;
  2745. }
  2746. _historyText.Add (
  2747. [ [.. GetCurrentLine ()]],
  2748. CursorPosition,
  2749. TextEditingLineStatus.Replaced
  2750. );
  2751. UpdateWrapModel ();
  2752. DoSetNeedsDraw (new (0, CurrentRow - _topRow, Viewport.Width, Viewport.Height));
  2753. _lastWasKill = setLastWasKill;
  2754. DoNeededAction ();
  2755. }
  2756. private void KillWordBackward ()
  2757. {
  2758. if (_isReadOnly)
  2759. {
  2760. return;
  2761. }
  2762. SetWrapModel ();
  2763. List<Cell> currentLine = GetCurrentLine ();
  2764. _historyText.Add ([ [.. GetCurrentLine ()]], CursorPosition);
  2765. if (CurrentColumn == 0)
  2766. {
  2767. DeleteTextBackwards ();
  2768. _historyText.ReplaceLast (
  2769. [ [.. GetCurrentLine ()]],
  2770. CursorPosition,
  2771. TextEditingLineStatus.Replaced
  2772. );
  2773. UpdateWrapModel ();
  2774. return;
  2775. }
  2776. (int col, int row)? newPos = _model.WordBackward (CurrentColumn, CurrentRow, UseSameRuneTypeForWords);
  2777. if (newPos.HasValue && CurrentRow == newPos.Value.row)
  2778. {
  2779. int restCount = CurrentColumn - newPos.Value.col;
  2780. currentLine.RemoveRange (newPos.Value.col, restCount);
  2781. if (_wordWrap)
  2782. {
  2783. _wrapNeeded = true;
  2784. }
  2785. CurrentColumn = newPos.Value.col;
  2786. }
  2787. else if (newPos.HasValue)
  2788. {
  2789. int restCount;
  2790. if (newPos.Value.row == CurrentRow)
  2791. {
  2792. restCount = currentLine.Count - CurrentColumn;
  2793. currentLine.RemoveRange (CurrentColumn, restCount);
  2794. }
  2795. else
  2796. {
  2797. while (CurrentRow != newPos.Value.row)
  2798. {
  2799. restCount = currentLine.Count;
  2800. currentLine.RemoveRange (0, restCount);
  2801. CurrentRow--;
  2802. currentLine = GetCurrentLine ();
  2803. }
  2804. }
  2805. if (_wordWrap)
  2806. {
  2807. _wrapNeeded = true;
  2808. }
  2809. CurrentColumn = newPos.Value.col;
  2810. CurrentRow = newPos.Value.row;
  2811. }
  2812. _historyText.Add (
  2813. [ [.. GetCurrentLine ()]],
  2814. CursorPosition,
  2815. TextEditingLineStatus.Replaced
  2816. );
  2817. UpdateWrapModel ();
  2818. DoSetNeedsDraw (new (0, CurrentRow - _topRow, Viewport.Width, Viewport.Height));
  2819. DoNeededAction ();
  2820. }
  2821. private void KillWordForward ()
  2822. {
  2823. if (_isReadOnly)
  2824. {
  2825. return;
  2826. }
  2827. SetWrapModel ();
  2828. List<Cell> currentLine = GetCurrentLine ();
  2829. _historyText.Add ([ [.. GetCurrentLine ()]], CursorPosition);
  2830. if (currentLine.Count == 0 || CurrentColumn == currentLine.Count)
  2831. {
  2832. DeleteTextForwards ();
  2833. _historyText.ReplaceLast (
  2834. [ [.. GetCurrentLine ()]],
  2835. CursorPosition,
  2836. TextEditingLineStatus.Replaced
  2837. );
  2838. UpdateWrapModel ();
  2839. return;
  2840. }
  2841. (int col, int row)? newPos = _model.WordForward (CurrentColumn, CurrentRow, UseSameRuneTypeForWords);
  2842. var restCount = 0;
  2843. if (newPos.HasValue && CurrentRow == newPos.Value.row)
  2844. {
  2845. restCount = newPos.Value.col - CurrentColumn;
  2846. currentLine.RemoveRange (CurrentColumn, restCount);
  2847. }
  2848. else if (newPos.HasValue)
  2849. {
  2850. restCount = currentLine.Count - CurrentColumn;
  2851. currentLine.RemoveRange (CurrentColumn, restCount);
  2852. }
  2853. if (_wordWrap)
  2854. {
  2855. _wrapNeeded = true;
  2856. }
  2857. _historyText.Add (
  2858. [ [.. GetCurrentLine ()]],
  2859. CursorPosition,
  2860. TextEditingLineStatus.Replaced
  2861. );
  2862. UpdateWrapModel ();
  2863. DoSetNeedsDraw (new (0, CurrentRow - _topRow, Viewport.Width, Viewport.Height));
  2864. DoNeededAction ();
  2865. }
  2866. private void Model_LinesLoaded (object sender, EventArgs e)
  2867. {
  2868. // This call is not needed. Model_LinesLoaded gets invoked when
  2869. // model.LoadString (value) is called. LoadString is called from one place
  2870. // (Text.set) and historyText.Clear() is called immediately after.
  2871. // If this call happens, HistoryText_ChangeText will get called multiple times
  2872. // when Text is set, which is wrong.
  2873. //historyText.Clear (Text);
  2874. if (!_multiline && !IsInitialized)
  2875. {
  2876. CurrentColumn = Text.GetRuneCount ();
  2877. _leftColumn = CurrentColumn > Viewport.Width + 1 ? CurrentColumn - Viewport.Width + 1 : 0;
  2878. }
  2879. }
  2880. private void MoveBottomEnd ()
  2881. {
  2882. ResetAllTrack ();
  2883. if (_shiftSelecting && IsSelecting)
  2884. {
  2885. StopSelecting ();
  2886. }
  2887. MoveEnd ();
  2888. }
  2889. private void MoveBottomEndExtend ()
  2890. {
  2891. ResetAllTrack ();
  2892. StartSelecting ();
  2893. MoveEnd ();
  2894. }
  2895. private bool MoveDown ()
  2896. {
  2897. if (CurrentRow + 1 < _model.Count)
  2898. {
  2899. if (_columnTrack == -1)
  2900. {
  2901. _columnTrack = CurrentColumn;
  2902. }
  2903. CurrentRow++;
  2904. if (CurrentRow >= _topRow + Viewport.Height)
  2905. {
  2906. _topRow++;
  2907. SetNeedsDraw ();
  2908. }
  2909. TrackColumn ();
  2910. PositionCursor ();
  2911. }
  2912. else if (CurrentRow > Viewport.Height)
  2913. {
  2914. Adjust ();
  2915. }
  2916. else
  2917. {
  2918. return false;
  2919. }
  2920. DoNeededAction ();
  2921. return true;
  2922. }
  2923. private void MoveEndOfLine ()
  2924. {
  2925. List<Cell> currentLine = GetCurrentLine ();
  2926. CurrentColumn = currentLine.Count;
  2927. DoNeededAction ();
  2928. }
  2929. private bool MoveLeft ()
  2930. {
  2931. if (CurrentColumn > 0)
  2932. {
  2933. CurrentColumn--;
  2934. }
  2935. else
  2936. {
  2937. if (CurrentRow > 0)
  2938. {
  2939. CurrentRow--;
  2940. if (CurrentRow < _topRow)
  2941. {
  2942. _topRow--;
  2943. SetNeedsDraw ();
  2944. }
  2945. List<Cell> currentLine = GetCurrentLine ();
  2946. CurrentColumn = Math.Max (currentLine.Count - (ReadOnly ? 1 : 0), 0);
  2947. }
  2948. else
  2949. {
  2950. return false;
  2951. }
  2952. }
  2953. DoNeededAction ();
  2954. return true;
  2955. }
  2956. private void MovePageDown ()
  2957. {
  2958. int nPageDnShift = Viewport.Height - 1;
  2959. if (CurrentRow >= 0 && CurrentRow < _model.Count)
  2960. {
  2961. if (_columnTrack == -1)
  2962. {
  2963. _columnTrack = CurrentColumn;
  2964. }
  2965. CurrentRow = CurrentRow + nPageDnShift > _model.Count
  2966. ? _model.Count > 0 ? _model.Count - 1 : 0
  2967. : CurrentRow + nPageDnShift;
  2968. if (_topRow < CurrentRow - nPageDnShift)
  2969. {
  2970. _topRow = CurrentRow >= _model.Count
  2971. ? CurrentRow - nPageDnShift
  2972. : _topRow + nPageDnShift;
  2973. SetNeedsDraw ();
  2974. }
  2975. TrackColumn ();
  2976. PositionCursor ();
  2977. }
  2978. DoNeededAction ();
  2979. }
  2980. private void MovePageUp ()
  2981. {
  2982. int nPageUpShift = Viewport.Height - 1;
  2983. if (CurrentRow > 0)
  2984. {
  2985. if (_columnTrack == -1)
  2986. {
  2987. _columnTrack = CurrentColumn;
  2988. }
  2989. CurrentRow = CurrentRow - nPageUpShift < 0 ? 0 : CurrentRow - nPageUpShift;
  2990. if (CurrentRow < _topRow)
  2991. {
  2992. _topRow = _topRow - nPageUpShift < 0 ? 0 : _topRow - nPageUpShift;
  2993. SetNeedsDraw ();
  2994. }
  2995. TrackColumn ();
  2996. PositionCursor ();
  2997. }
  2998. DoNeededAction ();
  2999. }
  3000. private bool MoveRight ()
  3001. {
  3002. List<Cell> currentLine = GetCurrentLine ();
  3003. if ((ReadOnly ? CurrentColumn + 1 : CurrentColumn) < currentLine.Count)
  3004. {
  3005. CurrentColumn++;
  3006. }
  3007. else
  3008. {
  3009. if (CurrentRow + 1 < _model.Count)
  3010. {
  3011. CurrentRow++;
  3012. CurrentColumn = 0;
  3013. if (CurrentRow >= _topRow + Viewport.Height)
  3014. {
  3015. _topRow++;
  3016. SetNeedsDraw ();
  3017. }
  3018. }
  3019. else
  3020. {
  3021. return false;
  3022. }
  3023. }
  3024. DoNeededAction ();
  3025. return true;
  3026. }
  3027. private void MoveLeftStart ()
  3028. {
  3029. if (_leftColumn > 0)
  3030. {
  3031. SetNeedsDraw ();
  3032. }
  3033. CurrentColumn = 0;
  3034. _leftColumn = 0;
  3035. DoNeededAction ();
  3036. }
  3037. private void MoveTopHome ()
  3038. {
  3039. ResetAllTrack ();
  3040. if (_shiftSelecting && IsSelecting)
  3041. {
  3042. StopSelecting ();
  3043. }
  3044. MoveHome ();
  3045. }
  3046. private void MoveTopHomeExtend ()
  3047. {
  3048. ResetColumnTrack ();
  3049. StartSelecting ();
  3050. MoveHome ();
  3051. }
  3052. private bool MoveUp ()
  3053. {
  3054. if (CurrentRow > 0)
  3055. {
  3056. if (_columnTrack == -1)
  3057. {
  3058. _columnTrack = CurrentColumn;
  3059. }
  3060. CurrentRow--;
  3061. if (CurrentRow < _topRow)
  3062. {
  3063. _topRow--;
  3064. SetNeedsDraw ();
  3065. }
  3066. TrackColumn ();
  3067. PositionCursor ();
  3068. }
  3069. else
  3070. {
  3071. return false;
  3072. }
  3073. DoNeededAction ();
  3074. return true;
  3075. }
  3076. private void MoveWordBackward ()
  3077. {
  3078. (int col, int row)? newPos = _model.WordBackward (CurrentColumn, CurrentRow, UseSameRuneTypeForWords);
  3079. if (newPos.HasValue)
  3080. {
  3081. CurrentColumn = newPos.Value.col;
  3082. CurrentRow = newPos.Value.row;
  3083. }
  3084. DoNeededAction ();
  3085. }
  3086. private void MoveWordForward ()
  3087. {
  3088. (int col, int row)? newPos = _model.WordForward (CurrentColumn, CurrentRow, UseSameRuneTypeForWords);
  3089. if (newPos.HasValue)
  3090. {
  3091. CurrentColumn = newPos.Value.col;
  3092. CurrentRow = newPos.Value.row;
  3093. }
  3094. DoNeededAction ();
  3095. }
  3096. private (int width, int height) OffSetBackground ()
  3097. {
  3098. var w = 0;
  3099. var h = 0;
  3100. if (SuperView?.Viewport.Right - Viewport.Right < 0)
  3101. {
  3102. w = SuperView!.Viewport.Right - Viewport.Right - 1;
  3103. }
  3104. if (SuperView?.Viewport.Bottom - Viewport.Bottom < 0)
  3105. {
  3106. h = SuperView!.Viewport.Bottom - Viewport.Bottom - 1;
  3107. }
  3108. return (w, h);
  3109. }
  3110. private bool PointInSelection (int col, int row)
  3111. {
  3112. long start, end;
  3113. GetEncodedRegionBounds (out start, out end);
  3114. long q = ((long)(uint)row << 32) | (uint)col;
  3115. return q >= start && q <= end - 1;
  3116. }
  3117. private void ProcessAutocomplete ()
  3118. {
  3119. if (_isDrawing)
  3120. {
  3121. return;
  3122. }
  3123. if (_clickWithSelecting)
  3124. {
  3125. _clickWithSelecting = false;
  3126. return;
  3127. }
  3128. if (SelectedLength > 0)
  3129. {
  3130. return;
  3131. }
  3132. // draw autocomplete
  3133. GenerateSuggestions ();
  3134. var renderAt = new Point (
  3135. Autocomplete.Context.CursorPosition,
  3136. Autocomplete.PopupInsideContainer
  3137. ? CursorPosition.Y + 1 - TopRow
  3138. : 0
  3139. );
  3140. Autocomplete.RenderOverlay (renderAt);
  3141. }
  3142. private bool ProcessBackTab ()
  3143. {
  3144. ResetColumnTrack ();
  3145. if (!AllowsTab || _isReadOnly)
  3146. {
  3147. return false;
  3148. }
  3149. if (CurrentColumn > 0)
  3150. {
  3151. SetWrapModel ();
  3152. List<Cell> currentLine = GetCurrentLine ();
  3153. if (currentLine.Count > 0 && currentLine [CurrentColumn - 1].Rune.Value == '\t')
  3154. {
  3155. _historyText.Add (new () { new (currentLine) }, CursorPosition);
  3156. currentLine.RemoveAt (CurrentColumn - 1);
  3157. CurrentColumn--;
  3158. _historyText.Add (
  3159. new () { new (GetCurrentLine ()) },
  3160. CursorPosition,
  3161. TextEditingLineStatus.Replaced
  3162. );
  3163. }
  3164. SetNeedsDraw ();
  3165. UpdateWrapModel ();
  3166. }
  3167. DoNeededAction ();
  3168. return true;
  3169. }
  3170. private void ProcessCopy ()
  3171. {
  3172. ResetColumnTrack ();
  3173. Copy ();
  3174. }
  3175. private void ProcessCut ()
  3176. {
  3177. ResetColumnTrack ();
  3178. Cut ();
  3179. }
  3180. private void ProcessDeleteCharLeft ()
  3181. {
  3182. ResetColumnTrack ();
  3183. DeleteCharLeft ();
  3184. }
  3185. private void ProcessDeleteCharRight ()
  3186. {
  3187. ResetColumnTrack ();
  3188. DeleteCharRight ();
  3189. }
  3190. private Attribute? GetSelectedAttribute (int row, int col)
  3191. {
  3192. if (!InheritsPreviousAttribute || (Lines == 1 && GetLine (Lines).Count == 0))
  3193. {
  3194. return null;
  3195. }
  3196. List<Cell> line = GetLine (row);
  3197. int foundRow = row;
  3198. while (line.Count == 0)
  3199. {
  3200. if (foundRow == 0 && line.Count == 0)
  3201. {
  3202. return null;
  3203. }
  3204. foundRow--;
  3205. line = GetLine (foundRow);
  3206. }
  3207. int foundCol = foundRow < row ? line.Count - 1 : Math.Min (col, line.Count - 1);
  3208. Cell cell = line [foundCol];
  3209. return cell.Attribute;
  3210. }
  3211. // If InheritsPreviousScheme is enabled this method will check if the rune cell on
  3212. // the row and col location and around has a not null scheme. If it's null will set it with
  3213. // the very most previous valid scheme.
  3214. private void ProcessInheritsPreviousScheme (int row, int col)
  3215. {
  3216. if (!InheritsPreviousAttribute || (Lines == 1 && GetLine (Lines).Count == 0))
  3217. {
  3218. return;
  3219. }
  3220. List<Cell> line = GetLine (row);
  3221. List<Cell> lineToSet = line;
  3222. while (line.Count == 0)
  3223. {
  3224. if (row == 0 && line.Count == 0)
  3225. {
  3226. return;
  3227. }
  3228. row--;
  3229. line = GetLine (row);
  3230. lineToSet = line;
  3231. }
  3232. int colWithColor = Math.Max (Math.Min (col - 2, line.Count - 1), 0);
  3233. Cell cell = line [colWithColor];
  3234. int colWithoutColor = Math.Max (col - 1, 0);
  3235. Cell lineTo = lineToSet [colWithoutColor];
  3236. if (cell.Attribute is { } && colWithColor == 0 && lineTo.Attribute is { })
  3237. {
  3238. for (int r = row - 1; r > -1; r--)
  3239. {
  3240. List<Cell> l = GetLine (r);
  3241. for (int c = l.Count - 1; c > -1; c--)
  3242. {
  3243. Cell cell1 = l [c];
  3244. if (cell1.Attribute is null)
  3245. {
  3246. cell1.Attribute = cell.Attribute;
  3247. l [c] = cell1;
  3248. }
  3249. else
  3250. {
  3251. return;
  3252. }
  3253. }
  3254. }
  3255. return;
  3256. }
  3257. if (cell.Attribute is null)
  3258. {
  3259. for (int r = row; r > -1; r--)
  3260. {
  3261. List<Cell> l = GetLine (r);
  3262. colWithColor = l.FindLastIndex (
  3263. colWithColor > -1 ? colWithColor : l.Count - 1,
  3264. c => c.Attribute != null
  3265. );
  3266. if (colWithColor > -1 && l [colWithColor].Attribute is { })
  3267. {
  3268. cell = l [colWithColor];
  3269. break;
  3270. }
  3271. }
  3272. }
  3273. else
  3274. {
  3275. int cRow = row;
  3276. while (cell.Attribute is null)
  3277. {
  3278. if ((colWithColor == 0 || cell.Attribute is null) && cRow > 0)
  3279. {
  3280. line = GetLine (--cRow);
  3281. colWithColor = line.Count - 1;
  3282. cell = line [colWithColor];
  3283. }
  3284. else if (cRow == 0 && colWithColor < line.Count)
  3285. {
  3286. cell = line [colWithColor + 1];
  3287. }
  3288. }
  3289. }
  3290. if (cell.Attribute is { } && colWithColor > -1 && colWithoutColor < lineToSet.Count && lineTo.Attribute is null)
  3291. {
  3292. while (lineTo.Attribute is null)
  3293. {
  3294. lineTo.Attribute = cell.Attribute;
  3295. lineToSet [colWithoutColor] = lineTo;
  3296. colWithoutColor--;
  3297. if (colWithoutColor == -1 && row > 0)
  3298. {
  3299. lineToSet = GetLine (--row);
  3300. colWithoutColor = lineToSet.Count - 1;
  3301. }
  3302. }
  3303. }
  3304. }
  3305. private void ProcessKillWordBackward ()
  3306. {
  3307. ResetColumnTrack ();
  3308. KillWordBackward ();
  3309. }
  3310. private void ProcessKillWordForward ()
  3311. {
  3312. ResetColumnTrack ();
  3313. StopSelecting ();
  3314. KillWordForward ();
  3315. }
  3316. private void ProcessMouseClick (MouseEventArgs ev, out List<Cell> line)
  3317. {
  3318. List<Cell>? r = null;
  3319. if (_model.Count > 0)
  3320. {
  3321. int maxCursorPositionableLine = Math.Max (_model.Count - 1 - _topRow, 0);
  3322. if (Math.Max (ev.Position.Y, 0) > maxCursorPositionableLine)
  3323. {
  3324. CurrentRow = maxCursorPositionableLine + _topRow;
  3325. }
  3326. else
  3327. {
  3328. CurrentRow = Math.Max (ev.Position.Y + _topRow, 0);
  3329. }
  3330. r = GetCurrentLine ();
  3331. int idx = TextModel.GetColFromX (r, _leftColumn, Math.Max (ev.Position.X, 0), TabWidth);
  3332. if (idx - _leftColumn >= r.Count)
  3333. {
  3334. CurrentColumn = Math.Max (r.Count - _leftColumn - (ReadOnly ? 1 : 0), 0);
  3335. }
  3336. else
  3337. {
  3338. CurrentColumn = idx + _leftColumn;
  3339. }
  3340. }
  3341. line = r!;
  3342. }
  3343. private bool ProcessMoveDown ()
  3344. {
  3345. ResetContinuousFindTrack ();
  3346. if (_shiftSelecting && IsSelecting)
  3347. {
  3348. StopSelecting ();
  3349. }
  3350. return MoveDown ();
  3351. }
  3352. private void ProcessMoveDownExtend ()
  3353. {
  3354. ResetColumnTrack ();
  3355. StartSelecting ();
  3356. MoveDown ();
  3357. }
  3358. private void ProcessMoveEndOfLine ()
  3359. {
  3360. ResetAllTrack ();
  3361. if (_shiftSelecting && IsSelecting)
  3362. {
  3363. StopSelecting ();
  3364. }
  3365. MoveEndOfLine ();
  3366. }
  3367. private void ProcessMoveRightEndExtend ()
  3368. {
  3369. ResetAllTrack ();
  3370. StartSelecting ();
  3371. MoveEndOfLine ();
  3372. }
  3373. private bool ProcessMoveLeft ()
  3374. {
  3375. // if the user presses Left (without any control keys) and they are at the start of the text
  3376. if (CurrentColumn == 0 && CurrentRow == 0)
  3377. {
  3378. if (IsSelecting)
  3379. {
  3380. StopSelecting ();
  3381. return true;
  3382. }
  3383. // do not respond (this lets the key press fall through to navigation system - which usually changes focus backward)
  3384. return false;
  3385. }
  3386. ResetAllTrack ();
  3387. if (_shiftSelecting && IsSelecting)
  3388. {
  3389. StopSelecting ();
  3390. }
  3391. MoveLeft ();
  3392. return true;
  3393. }
  3394. private void ProcessMoveLeftExtend ()
  3395. {
  3396. ResetAllTrack ();
  3397. StartSelecting ();
  3398. MoveLeft ();
  3399. }
  3400. private bool ProcessMoveRight ()
  3401. {
  3402. // if the user presses Right (without any control keys)
  3403. // determine where the last cursor position in the text is
  3404. int lastRow = _model.Count - 1;
  3405. int lastCol = _model.GetLine (lastRow).Count;
  3406. // 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)
  3407. if (CurrentColumn == lastCol && CurrentRow == lastRow)
  3408. {
  3409. // Unless they have text selected
  3410. if (IsSelecting)
  3411. {
  3412. // In which case clear
  3413. StopSelecting ();
  3414. return true;
  3415. }
  3416. return false;
  3417. }
  3418. ResetAllTrack ();
  3419. if (_shiftSelecting && IsSelecting)
  3420. {
  3421. StopSelecting ();
  3422. }
  3423. MoveRight ();
  3424. return true;
  3425. }
  3426. private void ProcessMoveRightExtend ()
  3427. {
  3428. ResetAllTrack ();
  3429. StartSelecting ();
  3430. MoveRight ();
  3431. }
  3432. private void ProcessMoveLeftStart ()
  3433. {
  3434. ResetAllTrack ();
  3435. if (_shiftSelecting && IsSelecting)
  3436. {
  3437. StopSelecting ();
  3438. }
  3439. MoveLeftStart ();
  3440. }
  3441. private void ProcessMoveLeftStartExtend ()
  3442. {
  3443. ResetAllTrack ();
  3444. StartSelecting ();
  3445. MoveLeftStart ();
  3446. }
  3447. private bool ProcessMoveUp ()
  3448. {
  3449. ResetContinuousFindTrack ();
  3450. if (_shiftSelecting && IsSelecting)
  3451. {
  3452. StopSelecting ();
  3453. }
  3454. return MoveUp ();
  3455. }
  3456. private void ProcessMoveUpExtend ()
  3457. {
  3458. ResetColumnTrack ();
  3459. StartSelecting ();
  3460. MoveUp ();
  3461. }
  3462. private void ProcessMoveWordBackward ()
  3463. {
  3464. ResetAllTrack ();
  3465. if (_shiftSelecting && IsSelecting)
  3466. {
  3467. StopSelecting ();
  3468. }
  3469. MoveWordBackward ();
  3470. }
  3471. private void ProcessMoveWordBackwardExtend ()
  3472. {
  3473. ResetAllTrack ();
  3474. StartSelecting ();
  3475. MoveWordBackward ();
  3476. }
  3477. private void ProcessMoveWordForward ()
  3478. {
  3479. ResetAllTrack ();
  3480. if (_shiftSelecting && IsSelecting)
  3481. {
  3482. StopSelecting ();
  3483. }
  3484. MoveWordForward ();
  3485. }
  3486. private void ProcessMoveWordForwardExtend ()
  3487. {
  3488. ResetAllTrack ();
  3489. StartSelecting ();
  3490. MoveWordForward ();
  3491. }
  3492. private void ProcessPageDown ()
  3493. {
  3494. ResetColumnTrack ();
  3495. if (_shiftSelecting && IsSelecting)
  3496. {
  3497. StopSelecting ();
  3498. }
  3499. MovePageDown ();
  3500. }
  3501. private void ProcessPageDownExtend ()
  3502. {
  3503. ResetColumnTrack ();
  3504. StartSelecting ();
  3505. MovePageDown ();
  3506. }
  3507. private void ProcessPageUp ()
  3508. {
  3509. ResetColumnTrack ();
  3510. if (_shiftSelecting && IsSelecting)
  3511. {
  3512. StopSelecting ();
  3513. }
  3514. MovePageUp ();
  3515. }
  3516. private void ProcessPageUpExtend ()
  3517. {
  3518. ResetColumnTrack ();
  3519. StartSelecting ();
  3520. MovePageUp ();
  3521. }
  3522. private void ProcessPaste ()
  3523. {
  3524. ResetColumnTrack ();
  3525. if (_isReadOnly)
  3526. {
  3527. return;
  3528. }
  3529. Paste ();
  3530. }
  3531. private bool ProcessEnterKey (ICommandContext? commandContext)
  3532. {
  3533. ResetColumnTrack ();
  3534. if (_isReadOnly)
  3535. {
  3536. return false;
  3537. }
  3538. if (!AllowsReturn)
  3539. {
  3540. // By Default pressing ENTER should be ignored (OnAccept will return false or null). Only cancel if the
  3541. // event was fired and set Cancel = true.
  3542. return RaiseAccepting (commandContext) is null or false;
  3543. }
  3544. SetWrapModel ();
  3545. List<Cell> currentLine = GetCurrentLine ();
  3546. _historyText.Add (new () { new (currentLine) }, CursorPosition);
  3547. if (IsSelecting)
  3548. {
  3549. ClearSelectedRegion ();
  3550. currentLine = GetCurrentLine ();
  3551. }
  3552. int restCount = currentLine.Count - CurrentColumn;
  3553. List<Cell> rest = currentLine.GetRange (CurrentColumn, restCount);
  3554. currentLine.RemoveRange (CurrentColumn, restCount);
  3555. List<List<Cell>> addedLines = new () { new (currentLine) };
  3556. _model.AddLine (CurrentRow + 1, rest);
  3557. addedLines.Add (new (_model.GetLine (CurrentRow + 1)));
  3558. _historyText.Add (addedLines, CursorPosition, TextEditingLineStatus.Added);
  3559. CurrentRow++;
  3560. var fullNeedsDraw = false;
  3561. if (CurrentRow >= _topRow + Viewport.Height)
  3562. {
  3563. _topRow++;
  3564. fullNeedsDraw = true;
  3565. }
  3566. CurrentColumn = 0;
  3567. _historyText.Add (
  3568. new () { new (GetCurrentLine ()) },
  3569. CursorPosition,
  3570. TextEditingLineStatus.Replaced
  3571. );
  3572. if (!_wordWrap && CurrentColumn < _leftColumn)
  3573. {
  3574. fullNeedsDraw = true;
  3575. _leftColumn = 0;
  3576. }
  3577. if (fullNeedsDraw)
  3578. {
  3579. SetNeedsDraw ();
  3580. }
  3581. else
  3582. {
  3583. // BUGBUG: customized rect aren't supported now because the Redraw isn't using the Intersect method.
  3584. //SetNeedsDraw (new (0, currentRow - topRow, 2, Viewport.Height));
  3585. SetNeedsDraw ();
  3586. }
  3587. UpdateWrapModel ();
  3588. DoNeededAction ();
  3589. OnContentsChanged ();
  3590. return true;
  3591. }
  3592. private void ProcessSelectAll ()
  3593. {
  3594. ResetColumnTrack ();
  3595. SelectAll ();
  3596. }
  3597. private void ProcessSetOverwrite ()
  3598. {
  3599. ResetColumnTrack ();
  3600. SetOverwrite (!Used);
  3601. }
  3602. private bool ProcessTab ()
  3603. {
  3604. ResetColumnTrack ();
  3605. if (!AllowsTab || _isReadOnly)
  3606. {
  3607. return false;
  3608. }
  3609. InsertText (new Key ((KeyCode)'\t'));
  3610. DoNeededAction ();
  3611. return true;
  3612. }
  3613. private void ResetAllTrack ()
  3614. {
  3615. // Handle some state here - whether the last command was a kill
  3616. // operation and the column tracking (up/down)
  3617. _lastWasKill = false;
  3618. _columnTrack = -1;
  3619. _continuousFind = false;
  3620. }
  3621. private void ResetColumnTrack ()
  3622. {
  3623. // Handle some state here - whether the last command was a kill
  3624. // operation and the column tracking (up/down)
  3625. _lastWasKill = false;
  3626. _columnTrack = -1;
  3627. }
  3628. private void ResetContinuousFind ()
  3629. {
  3630. if (!_continuousFind)
  3631. {
  3632. int col = IsSelecting ? _selectionStartColumn : CurrentColumn;
  3633. int row = IsSelecting ? _selectionStartRow : CurrentRow;
  3634. _model.ResetContinuousFind (new (col, row));
  3635. }
  3636. }
  3637. private void ResetContinuousFindTrack ()
  3638. {
  3639. // Handle some state here - whether the last command was a kill
  3640. // operation and the column tracking (up/down)
  3641. _lastWasKill = false;
  3642. _continuousFind = false;
  3643. }
  3644. private void ResetPosition ()
  3645. {
  3646. _topRow = _leftColumn = CurrentRow = CurrentColumn = 0;
  3647. StopSelecting ();
  3648. }
  3649. private void SetClipboard (string text)
  3650. {
  3651. if (text is { })
  3652. {
  3653. Clipboard.Contents = text;
  3654. }
  3655. }
  3656. private bool SetFoundText (
  3657. string text,
  3658. (Point current, bool found) foundPos,
  3659. string? textToReplace = null,
  3660. bool replace = false,
  3661. bool replaceAll = false
  3662. )
  3663. {
  3664. if (foundPos.found)
  3665. {
  3666. StartSelecting ();
  3667. _selectionStartColumn = foundPos.current.X;
  3668. _selectionStartRow = foundPos.current.Y;
  3669. if (!replaceAll)
  3670. {
  3671. CurrentColumn = _selectionStartColumn + text.GetRuneCount ();
  3672. }
  3673. else
  3674. {
  3675. CurrentColumn = _selectionStartColumn + textToReplace!.GetRuneCount ();
  3676. }
  3677. CurrentRow = foundPos.current.Y;
  3678. if (!_isReadOnly && replace)
  3679. {
  3680. Adjust ();
  3681. ClearSelectedRegion ();
  3682. InsertAllText (textToReplace!);
  3683. StartSelecting ();
  3684. _selectionStartColumn = CurrentColumn - textToReplace!.GetRuneCount ();
  3685. }
  3686. else
  3687. {
  3688. UpdateWrapModel ();
  3689. SetNeedsDraw ();
  3690. Adjust ();
  3691. }
  3692. _continuousFind = true;
  3693. return foundPos.found;
  3694. }
  3695. UpdateWrapModel ();
  3696. _continuousFind = false;
  3697. return foundPos.found;
  3698. }
  3699. private void SetOverwrite (bool overwrite)
  3700. {
  3701. Used = overwrite;
  3702. SetNeedsDraw ();
  3703. DoNeededAction ();
  3704. }
  3705. private void SetValidUsedColor (Attribute? attribute)
  3706. {
  3707. // BUGBUG: (v2 truecolor) This code depends on 8-bit color names; disabling for now
  3708. //if ((scheme!.HotNormal.Foreground & scheme.Focus.Background) == scheme.Focus.Foreground) {
  3709. SetAttribute (new (attribute!.Value.Background, attribute!.Value.Foreground, attribute!.Value.Style));
  3710. }
  3711. /// <summary>Restore from original model.</summary>
  3712. private void SetWrapModel ([CallerMemberName] string? caller = null)
  3713. {
  3714. if (_currentCaller is { })
  3715. {
  3716. return;
  3717. }
  3718. if (_wordWrap)
  3719. {
  3720. _currentCaller = caller;
  3721. CurrentColumn = _wrapManager!.GetModelColFromWrappedLines (CurrentRow, CurrentColumn);
  3722. CurrentRow = _wrapManager.GetModelLineFromWrappedLines (CurrentRow);
  3723. _selectionStartColumn =
  3724. _wrapManager.GetModelColFromWrappedLines (_selectionStartRow, _selectionStartColumn);
  3725. _selectionStartRow = _wrapManager.GetModelLineFromWrappedLines (_selectionStartRow);
  3726. _model = _wrapManager.Model;
  3727. }
  3728. }
  3729. private void ShowContextMenu (Point? mousePosition)
  3730. {
  3731. if (!Equals (_currentCulture, Thread.CurrentThread.CurrentUICulture))
  3732. {
  3733. _currentCulture = Thread.CurrentThread.CurrentUICulture;
  3734. }
  3735. if (mousePosition is null)
  3736. {
  3737. mousePosition = ViewportToScreen (new Point (CursorPosition.X, CursorPosition.Y));
  3738. }
  3739. ContextMenu?.MakeVisible (mousePosition);
  3740. }
  3741. private void StartSelecting ()
  3742. {
  3743. if (_shiftSelecting && IsSelecting)
  3744. {
  3745. return;
  3746. }
  3747. _shiftSelecting = true;
  3748. IsSelecting = true;
  3749. _selectionStartColumn = CurrentColumn;
  3750. _selectionStartRow = CurrentRow;
  3751. }
  3752. private void StopSelecting ()
  3753. {
  3754. if (IsSelecting)
  3755. {
  3756. SetNeedsDraw ();
  3757. }
  3758. _shiftSelecting = false;
  3759. IsSelecting = false;
  3760. _isButtonShift = false;
  3761. }
  3762. private string StringFromRunes (List<Cell> cells)
  3763. {
  3764. if (cells is null)
  3765. {
  3766. throw new ArgumentNullException (nameof (cells));
  3767. }
  3768. var size = 0;
  3769. foreach (Cell cell in cells)
  3770. {
  3771. size += cell.Rune.GetEncodingLength ();
  3772. }
  3773. var encoded = new byte [size];
  3774. var offset = 0;
  3775. foreach (Cell cell in cells)
  3776. {
  3777. offset += cell.Rune.Encode (encoded, offset);
  3778. }
  3779. return StringExtensions.ToString (encoded);
  3780. }
  3781. private void TextView_SuperViewChanged (object sender, SuperViewChangedEventArgs e)
  3782. {
  3783. if (e.SuperView is { })
  3784. {
  3785. if (Autocomplete.HostControl is null)
  3786. {
  3787. Autocomplete.HostControl = this;
  3788. }
  3789. }
  3790. else
  3791. {
  3792. Autocomplete.HostControl = null;
  3793. }
  3794. }
  3795. private void TextView_Initialized (object sender, EventArgs e)
  3796. {
  3797. if (Autocomplete.HostControl is null)
  3798. {
  3799. Autocomplete.HostControl = this;
  3800. }
  3801. OnContentsChanged ();
  3802. }
  3803. private void TextView_LayoutComplete (object? sender, LayoutEventArgs e)
  3804. {
  3805. WrapTextModel ();
  3806. Adjust ();
  3807. }
  3808. private void ToggleSelecting ()
  3809. {
  3810. ResetColumnTrack ();
  3811. IsSelecting = !IsSelecting;
  3812. _selectionStartColumn = CurrentColumn;
  3813. _selectionStartRow = CurrentRow;
  3814. }
  3815. // Tries to snap the cursor to the tracking column
  3816. private void TrackColumn ()
  3817. {
  3818. // Now track the column
  3819. List<Cell> line = GetCurrentLine ();
  3820. if (line.Count < _columnTrack)
  3821. {
  3822. CurrentColumn = line.Count;
  3823. }
  3824. else if (_columnTrack != -1)
  3825. {
  3826. CurrentColumn = _columnTrack;
  3827. }
  3828. else if (CurrentColumn > line.Count)
  3829. {
  3830. CurrentColumn = line.Count;
  3831. }
  3832. Adjust ();
  3833. }
  3834. /// <summary>Update the original model.</summary>
  3835. private void UpdateWrapModel ([CallerMemberName] string? caller = null)
  3836. {
  3837. if (_currentCaller is { } && _currentCaller != caller)
  3838. {
  3839. return;
  3840. }
  3841. if (_wordWrap)
  3842. {
  3843. _currentCaller = null;
  3844. _wrapManager!.UpdateModel (
  3845. _model,
  3846. out int nRow,
  3847. out int nCol,
  3848. out int nStartRow,
  3849. out int nStartCol,
  3850. CurrentRow,
  3851. CurrentColumn,
  3852. _selectionStartRow,
  3853. _selectionStartColumn,
  3854. true
  3855. );
  3856. CurrentRow = nRow;
  3857. CurrentColumn = nCol;
  3858. _selectionStartRow = nStartRow;
  3859. _selectionStartColumn = nStartCol;
  3860. _wrapNeeded = true;
  3861. SetNeedsDraw ();
  3862. }
  3863. if (_currentCaller is { })
  3864. {
  3865. throw new InvalidOperationException (
  3866. $"WordWrap settings was changed after the {_currentCaller} call."
  3867. );
  3868. }
  3869. }
  3870. private void WrapTextModel ()
  3871. {
  3872. if (_wordWrap && _wrapManager is { })
  3873. {
  3874. _model = _wrapManager.WrapModel (
  3875. Math.Max (Viewport.Width - (ReadOnly ? 0 : 1), 0), // For the cursor on the last column of a line
  3876. out int nRow,
  3877. out int nCol,
  3878. out int nStartRow,
  3879. out int nStartCol,
  3880. CurrentRow,
  3881. CurrentColumn,
  3882. _selectionStartRow,
  3883. _selectionStartColumn,
  3884. _tabWidth
  3885. );
  3886. CurrentRow = nRow;
  3887. CurrentColumn = nCol;
  3888. _selectionStartRow = nStartRow;
  3889. _selectionStartColumn = nStartCol;
  3890. SetNeedsDraw ();
  3891. }
  3892. }
  3893. /// <inheritdoc />
  3894. public bool EnableForDesign ()
  3895. {
  3896. Text = """
  3897. TextView provides a fully featured multi-line text editor.
  3898. It supports word wrap and history for undo.
  3899. """;
  3900. return true;
  3901. }
  3902. /// <inheritdoc/>
  3903. protected override void Dispose (bool disposing)
  3904. {
  3905. if (disposing && ContextMenu is { })
  3906. {
  3907. ContextMenu.Visible = false;
  3908. ContextMenu.Dispose ();
  3909. ContextMenu = null;
  3910. }
  3911. base.Dispose (disposing);
  3912. }
  3913. }
  3914. /// <summary>
  3915. /// Renders an overlay on another view at a given point that allows selecting from a range of 'autocomplete'
  3916. /// options. An implementation on a TextView.
  3917. /// </summary>
  3918. public class TextViewAutocomplete : PopupAutocomplete
  3919. {
  3920. /// <inheritdoc/>
  3921. protected override void DeleteTextBackwards () { ((TextView)HostControl).DeleteCharLeft (); }
  3922. /// <inheritdoc/>
  3923. protected override void InsertText (string accepted) { ((TextView)HostControl).InsertText (accepted); }
  3924. /// <inheritdoc/>
  3925. protected override void SetCursorPosition (int column)
  3926. {
  3927. ((TextView)HostControl).CursorPosition =
  3928. new (column, ((TextView)HostControl).CurrentRow);
  3929. }
  3930. }