TextView.cs 139 KB

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