TextControl.cs 107 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366
  1. // Permission is hereby granted, free of charge, to any person obtaining
  2. // a copy of this software and associated documentation files (the
  3. // "Software"), to deal in the Software without restriction, including
  4. // without limitation the rights to use, copy, modify, merge, publish,
  5. // distribute, sublicense, and/or sell copies of the Software, and to
  6. // permit persons to whom the Software is furnished to do so, subject to
  7. // the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be
  10. // included in all copies or substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  13. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  14. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  15. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  16. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  17. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  18. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  19. //
  20. // Copyright (c) 2004-2006 Novell, Inc. (http://www.novell.com)
  21. //
  22. // Authors:
  23. // Peter Bartok [email protected]
  24. //
  25. //
  26. // NOT COMPLETE
  27. // There's still plenty of things missing, I've got most of it planned, just hadn't had
  28. // the time to write it all yet.
  29. // Stuff missing (in no particular order):
  30. // - Align text after RecalculateLine
  31. // - Implement tag types for hotlinks, etc.
  32. // - Implement CaretPgUp/PgDown
  33. // NOTE:
  34. // selection_start.pos and selection_end.pos are 0-based
  35. // selection_start.pos = first selected char
  36. // selection_end.pos = first NOT-selected char
  37. //
  38. // FormatText methods are 1-based (as are all tags, LineTag.Start is 1 for
  39. // the first character on a line; the reason is that 0 is the position
  40. // *before* the first character on a line
  41. #undef Debug
  42. using System;
  43. using System.Collections;
  44. using System.Drawing;
  45. using System.Drawing.Text;
  46. using System.Text;
  47. using RTF=System.Windows.Forms.RTF;
  48. namespace System.Windows.Forms {
  49. internal enum LineColor {
  50. Red = 0,
  51. Black = 1
  52. }
  53. internal enum CaretSelection {
  54. Position, // Selection=Caret
  55. Word, // Selection=Word under caret
  56. Line // Selection=Line under caret
  57. }
  58. [Flags]
  59. internal enum FormatSpecified {
  60. None,
  61. BackColor = 2,
  62. Font = 4,
  63. Color = 8,
  64. }
  65. internal enum CaretDirection {
  66. CharForward, // Move a char to the right
  67. CharBack, // Move a char to the left
  68. LineUp, // Move a line up
  69. LineDown, // Move a line down
  70. Home, // Move to the beginning of the line
  71. End, // Move to the end of the line
  72. PgUp, // Move one page up
  73. PgDn, // Move one page down
  74. CtrlPgUp, // Move caret to the first visible char in the viewport
  75. CtrlPgDn, // Move caret to the last visible char in the viewport
  76. CtrlHome, // Move to the beginning of the document
  77. CtrlEnd, // Move to the end of the document
  78. WordBack, // Move to the beginning of the previous word (or beginning of line)
  79. WordForward, // Move to the beginning of the next word (or end of line)
  80. SelectionStart, // Move to the beginning of the current selection
  81. SelectionEnd, // Move to the end of the current selection
  82. CharForwardNoWrap, // Move a char forward, but don't wrap onto the next line
  83. CharBackNoWrap // Move a char backward, but don't wrap onto the previous line
  84. }
  85. internal enum LineEnding {
  86. Wrap, // line wraps to the next line
  87. Limp, // \r
  88. Hard, // \r\n
  89. Soft, // \r\r\n
  90. Rich, // \n
  91. None
  92. }
  93. internal class Document : ICloneable, IEnumerable {
  94. #region Structures
  95. // FIXME - go through code and check for places where
  96. // we do explicit comparisons instead of using the compare overloads
  97. internal struct Marker {
  98. internal Line line;
  99. internal LineTag tag;
  100. internal int pos;
  101. internal int height;
  102. public static bool operator<(Marker lhs, Marker rhs) {
  103. if (lhs.line.line_no < rhs.line.line_no) {
  104. return true;
  105. }
  106. if (lhs.line.line_no == rhs.line.line_no) {
  107. if (lhs.pos < rhs.pos) {
  108. return true;
  109. }
  110. }
  111. return false;
  112. }
  113. public static bool operator>(Marker lhs, Marker rhs) {
  114. if (lhs.line.line_no > rhs.line.line_no) {
  115. return true;
  116. }
  117. if (lhs.line.line_no == rhs.line.line_no) {
  118. if (lhs.pos > rhs.pos) {
  119. return true;
  120. }
  121. }
  122. return false;
  123. }
  124. public static bool operator==(Marker lhs, Marker rhs) {
  125. if ((lhs.line.line_no == rhs.line.line_no) && (lhs.pos == rhs.pos)) {
  126. return true;
  127. }
  128. return false;
  129. }
  130. public static bool operator!=(Marker lhs, Marker rhs) {
  131. if ((lhs.line.line_no != rhs.line.line_no) || (lhs.pos != rhs.pos)) {
  132. return true;
  133. }
  134. return false;
  135. }
  136. public void Combine(Line move_to_line, int move_to_line_length) {
  137. line = move_to_line;
  138. pos += move_to_line_length;
  139. tag = LineTag.FindTag(line, pos);
  140. }
  141. // This is for future use, right now Document.Split does it by hand, with some added shortcut logic
  142. public void Split(Line move_to_line, int split_at) {
  143. line = move_to_line;
  144. pos -= split_at;
  145. tag = LineTag.FindTag(line, pos);
  146. }
  147. public override bool Equals(object obj) {
  148. return this==(Marker)obj;
  149. }
  150. public override int GetHashCode() {
  151. return base.GetHashCode ();
  152. }
  153. public override string ToString() {
  154. return "Marker Line " + line + ", Position " + pos;
  155. }
  156. }
  157. #endregion Structures
  158. #region Local Variables
  159. private Line document;
  160. private int lines;
  161. private Line sentinel;
  162. private int document_id;
  163. private Random random = new Random();
  164. internal string password_char;
  165. private StringBuilder password_cache;
  166. private bool calc_pass;
  167. private int char_count;
  168. // For calculating widths/heights
  169. public static readonly StringFormat string_format = new StringFormat (StringFormat.GenericTypographic);
  170. private int recalc_suspended;
  171. private bool recalc_pending;
  172. private int recalc_start = 1; // This starts at one, since lines are 1 based
  173. private int recalc_end;
  174. private bool recalc_optimize;
  175. private int update_suspended;
  176. private bool update_pending;
  177. private int update_start = 1;
  178. internal bool multiline;
  179. internal HorizontalAlignment alignment;
  180. internal bool wrap;
  181. internal UndoManager undo;
  182. internal Marker caret;
  183. internal Marker selection_start;
  184. internal Marker selection_end;
  185. internal bool selection_visible;
  186. internal Marker selection_anchor;
  187. internal Marker selection_prev;
  188. internal bool selection_end_anchor;
  189. internal int viewport_x;
  190. internal int viewport_y; // The visible area of the document
  191. internal int viewport_width;
  192. internal int viewport_height;
  193. internal int document_x; // Width of the document
  194. internal int document_y; // Height of the document
  195. internal Rectangle invalid;
  196. internal int crlf_size; // 1 or 2, depending on whether we use \r\n or just \n
  197. internal TextBoxBase owner; // Who's owning us?
  198. static internal int caret_width = 1;
  199. static internal int caret_shift = 1;
  200. internal int left_margin = 2; // A left margin for all lines
  201. internal int top_margin = 2;
  202. internal int right_margin = 2;
  203. #endregion // Local Variables
  204. #region Constructors
  205. internal Document (TextBoxBase owner)
  206. {
  207. lines = 0;
  208. this.owner = owner;
  209. multiline = true;
  210. password_char = "";
  211. calc_pass = false;
  212. recalc_pending = false;
  213. // Tree related stuff
  214. sentinel = new Line (this, LineEnding.None);
  215. sentinel.color = LineColor.Black;
  216. document = sentinel;
  217. // We always have a blank line
  218. owner.HandleCreated += new EventHandler(owner_HandleCreated);
  219. owner.VisibleChanged += new EventHandler(owner_VisibleChanged);
  220. Add (1, String.Empty, owner.Font, ThemeEngine.Current.ResPool.GetSolidBrush (owner.ForeColor), LineEnding.None);
  221. undo = new UndoManager (this);
  222. selection_visible = false;
  223. selection_start.line = this.document;
  224. selection_start.pos = 0;
  225. selection_start.tag = selection_start.line.tags;
  226. selection_end.line = this.document;
  227. selection_end.pos = 0;
  228. selection_end.tag = selection_end.line.tags;
  229. selection_anchor.line = this.document;
  230. selection_anchor.pos = 0;
  231. selection_anchor.tag = selection_anchor.line.tags;
  232. caret.line = this.document;
  233. caret.pos = 0;
  234. caret.tag = caret.line.tags;
  235. viewport_x = 0;
  236. viewport_y = 0;
  237. crlf_size = 2;
  238. // Default selection is empty
  239. document_id = random.Next();
  240. string_format.Trimming = StringTrimming.None;
  241. string_format.FormatFlags = StringFormatFlags.DisplayFormatControl;
  242. UpdateMargins ();
  243. }
  244. #endregion
  245. #region Internal Properties
  246. internal Line Root {
  247. get {
  248. return document;
  249. }
  250. set {
  251. document = value;
  252. }
  253. }
  254. internal int Lines {
  255. get {
  256. return lines;
  257. }
  258. }
  259. internal Line CaretLine {
  260. get {
  261. return caret.line;
  262. }
  263. }
  264. internal int CaretPosition {
  265. get {
  266. return caret.pos;
  267. }
  268. }
  269. internal Point Caret {
  270. get {
  271. return new Point((int)caret.tag.line.widths[caret.pos] + caret.line.X, caret.line.Y);
  272. }
  273. }
  274. internal LineTag CaretTag {
  275. get {
  276. return caret.tag;
  277. }
  278. set {
  279. caret.tag = value;
  280. }
  281. }
  282. internal int CRLFSize {
  283. get {
  284. return crlf_size;
  285. }
  286. set {
  287. crlf_size = value;
  288. }
  289. }
  290. internal string PasswordChar {
  291. get {
  292. return password_char;
  293. }
  294. set {
  295. password_char = value;
  296. PasswordCache.Length = 0;
  297. if ((password_char.Length != 0) && (password_char[0] != '\0')) {
  298. calc_pass = true;
  299. } else {
  300. calc_pass = false;
  301. }
  302. }
  303. }
  304. private StringBuilder PasswordCache {
  305. get {
  306. if (password_cache == null)
  307. password_cache = new StringBuilder();
  308. return password_cache;
  309. }
  310. }
  311. internal int ViewPortX {
  312. get {
  313. return viewport_x;
  314. }
  315. set {
  316. viewport_x = value;
  317. }
  318. }
  319. internal int Length {
  320. get {
  321. return char_count + lines - 1; // Add \n for each line but the last
  322. }
  323. }
  324. private int CharCount {
  325. get {
  326. return char_count;
  327. }
  328. set {
  329. char_count = value;
  330. if (LengthChanged != null) {
  331. LengthChanged(this, EventArgs.Empty);
  332. }
  333. }
  334. }
  335. internal int ViewPortY {
  336. get {
  337. return viewport_y;
  338. }
  339. set {
  340. viewport_y = value;
  341. }
  342. }
  343. internal int ViewPortWidth {
  344. get {
  345. return viewport_width;
  346. }
  347. set {
  348. viewport_width = value;
  349. }
  350. }
  351. internal int ViewPortHeight {
  352. get {
  353. return viewport_height;
  354. }
  355. set {
  356. viewport_height = value;
  357. }
  358. }
  359. internal int Width {
  360. get {
  361. return this.document_x;
  362. }
  363. }
  364. internal int Height {
  365. get {
  366. return this.document_y;
  367. }
  368. }
  369. internal bool SelectionVisible {
  370. get {
  371. return selection_visible;
  372. }
  373. }
  374. internal bool Wrap {
  375. get {
  376. return wrap;
  377. }
  378. set {
  379. wrap = value;
  380. }
  381. }
  382. #endregion // Internal Properties
  383. #region Private Methods
  384. internal void UpdateMargins ()
  385. {
  386. switch (owner.actual_border_style) {
  387. case BorderStyle.None:
  388. left_margin = 0;
  389. top_margin = 0;
  390. right_margin = 1;
  391. break;
  392. case BorderStyle.FixedSingle:
  393. left_margin = 2;
  394. top_margin = 2;
  395. right_margin = 3;
  396. break;
  397. case BorderStyle.Fixed3D:
  398. left_margin = 1;
  399. top_margin = 1;
  400. right_margin = 2;
  401. break;
  402. }
  403. }
  404. internal void SuspendRecalc ()
  405. {
  406. recalc_suspended++;
  407. }
  408. internal void ResumeRecalc (bool immediate_update)
  409. {
  410. if (recalc_suspended > 0)
  411. recalc_suspended--;
  412. if (immediate_update && recalc_suspended == 0 && recalc_pending) {
  413. RecalculateDocument (owner.CreateGraphicsInternal(), recalc_start, recalc_end, recalc_optimize);
  414. recalc_pending = false;
  415. }
  416. }
  417. internal void SuspendUpdate ()
  418. {
  419. update_suspended++;
  420. }
  421. internal void ResumeUpdate (bool immediate_update)
  422. {
  423. if (update_suspended > 0)
  424. update_suspended--;
  425. if (immediate_update && update_suspended == 0 && update_pending) {
  426. UpdateView (GetLine (update_start), 0);
  427. update_pending = false;
  428. }
  429. }
  430. // For debugging
  431. internal int DumpTree(Line line, bool with_tags) {
  432. int total;
  433. total = 1;
  434. Console.Write("Line {0} [# {1}], Y: {2}, ending style: {3}, Text: '{4}'",
  435. line.line_no, line.GetHashCode(), line.Y, line.ending,
  436. line.text != null ? line.text.ToString() : "undefined");
  437. if (line.left == sentinel) {
  438. Console.Write(", left = sentinel");
  439. } else if (line.left == null) {
  440. Console.Write(", left = NULL");
  441. }
  442. if (line.right == sentinel) {
  443. Console.Write(", right = sentinel");
  444. } else if (line.right == null) {
  445. Console.Write(", right = NULL");
  446. }
  447. Console.WriteLine("");
  448. if (with_tags) {
  449. LineTag tag;
  450. int count;
  451. int length;
  452. tag = line.tags;
  453. count = 1;
  454. length = 0;
  455. Console.Write(" Tags: ");
  456. while (tag != null) {
  457. Console.Write("{0} <{1}>-<{2}>", count++, tag.start, tag.End
  458. /*line.text.ToString (tag.start - 1, tag.length)*/);
  459. length += tag.Length;
  460. if (tag.line != line) {
  461. Console.Write("BAD line link");
  462. throw new Exception("Bad line link in tree");
  463. }
  464. tag = tag.next;
  465. if (tag != null) {
  466. Console.Write(", ");
  467. }
  468. }
  469. if (length > line.text.Length) {
  470. throw new Exception(String.Format("Length of tags more than length of text on line (expected {0} calculated {1})", line.text.Length, length));
  471. } else if (length < line.text.Length) {
  472. throw new Exception(String.Format("Length of tags less than length of text on line (expected {0} calculated {1})", line.text.Length, length));
  473. }
  474. Console.WriteLine("");
  475. }
  476. if (line.left != null) {
  477. if (line.left != sentinel) {
  478. total += DumpTree(line.left, with_tags);
  479. }
  480. } else {
  481. if (line != sentinel) {
  482. throw new Exception("Left should not be NULL");
  483. }
  484. }
  485. if (line.right != null) {
  486. if (line.right != sentinel) {
  487. total += DumpTree(line.right, with_tags);
  488. }
  489. } else {
  490. if (line != sentinel) {
  491. throw new Exception("Right should not be NULL");
  492. }
  493. }
  494. for (int i = 1; i <= this.lines; i++) {
  495. if (GetLine(i) == null) {
  496. throw new Exception(String.Format("Hole in line order, missing {0}", i));
  497. }
  498. }
  499. if (line == this.Root) {
  500. if (total < this.lines) {
  501. throw new Exception(String.Format("Not enough nodes in tree, found {0}, expected {1}", total, this.lines));
  502. } else if (total > this.lines) {
  503. throw new Exception(String.Format("Too many nodes in tree, found {0}, expected {1}", total, this.lines));
  504. }
  505. }
  506. return total;
  507. }
  508. private void SetSelectionVisible (bool value)
  509. {
  510. selection_visible = value;
  511. // cursor and selection are enemies, we can't have both in the same room at the same time
  512. if (owner.IsHandleCreated && !owner.show_caret_w_selection)
  513. XplatUI.CaretVisible (owner.Handle, !selection_visible);
  514. }
  515. private void DecrementLines(int line_no) {
  516. int current;
  517. current = line_no;
  518. while (current <= lines) {
  519. GetLine(current).line_no--;
  520. current++;
  521. }
  522. return;
  523. }
  524. private void IncrementLines(int line_no) {
  525. int current;
  526. current = this.lines;
  527. while (current >= line_no) {
  528. GetLine(current).line_no++;
  529. current--;
  530. }
  531. return;
  532. }
  533. private void RebalanceAfterAdd(Line line1) {
  534. Line line2;
  535. while ((line1 != document) && (line1.parent.color == LineColor.Red)) {
  536. if (line1.parent == line1.parent.parent.left) {
  537. line2 = line1.parent.parent.right;
  538. if ((line2 != null) && (line2.color == LineColor.Red)) {
  539. line1.parent.color = LineColor.Black;
  540. line2.color = LineColor.Black;
  541. line1.parent.parent.color = LineColor.Red;
  542. line1 = line1.parent.parent;
  543. } else {
  544. if (line1 == line1.parent.right) {
  545. line1 = line1.parent;
  546. RotateLeft(line1);
  547. }
  548. line1.parent.color = LineColor.Black;
  549. line1.parent.parent.color = LineColor.Red;
  550. RotateRight(line1.parent.parent);
  551. }
  552. } else {
  553. line2 = line1.parent.parent.left;
  554. if ((line2 != null) && (line2.color == LineColor.Red)) {
  555. line1.parent.color = LineColor.Black;
  556. line2.color = LineColor.Black;
  557. line1.parent.parent.color = LineColor.Red;
  558. line1 = line1.parent.parent;
  559. } else {
  560. if (line1 == line1.parent.left) {
  561. line1 = line1.parent;
  562. RotateRight(line1);
  563. }
  564. line1.parent.color = LineColor.Black;
  565. line1.parent.parent.color = LineColor.Red;
  566. RotateLeft(line1.parent.parent);
  567. }
  568. }
  569. }
  570. document.color = LineColor.Black;
  571. }
  572. private void RebalanceAfterDelete(Line line1) {
  573. Line line2;
  574. while ((line1 != document) && (line1.color == LineColor.Black)) {
  575. if (line1 == line1.parent.left) {
  576. line2 = line1.parent.right;
  577. if (line2.color == LineColor.Red) {
  578. line2.color = LineColor.Black;
  579. line1.parent.color = LineColor.Red;
  580. RotateLeft(line1.parent);
  581. line2 = line1.parent.right;
  582. }
  583. if ((line2.left.color == LineColor.Black) && (line2.right.color == LineColor.Black)) {
  584. line2.color = LineColor.Red;
  585. line1 = line1.parent;
  586. } else {
  587. if (line2.right.color == LineColor.Black) {
  588. line2.left.color = LineColor.Black;
  589. line2.color = LineColor.Red;
  590. RotateRight(line2);
  591. line2 = line1.parent.right;
  592. }
  593. line2.color = line1.parent.color;
  594. line1.parent.color = LineColor.Black;
  595. line2.right.color = LineColor.Black;
  596. RotateLeft(line1.parent);
  597. line1 = document;
  598. }
  599. } else {
  600. line2 = line1.parent.left;
  601. if (line2.color == LineColor.Red) {
  602. line2.color = LineColor.Black;
  603. line1.parent.color = LineColor.Red;
  604. RotateRight(line1.parent);
  605. line2 = line1.parent.left;
  606. }
  607. if ((line2.right.color == LineColor.Black) && (line2.left.color == LineColor.Black)) {
  608. line2.color = LineColor.Red;
  609. line1 = line1.parent;
  610. } else {
  611. if (line2.left.color == LineColor.Black) {
  612. line2.right.color = LineColor.Black;
  613. line2.color = LineColor.Red;
  614. RotateLeft(line2);
  615. line2 = line1.parent.left;
  616. }
  617. line2.color = line1.parent.color;
  618. line1.parent.color = LineColor.Black;
  619. line2.left.color = LineColor.Black;
  620. RotateRight(line1.parent);
  621. line1 = document;
  622. }
  623. }
  624. }
  625. line1.color = LineColor.Black;
  626. }
  627. private void RotateLeft(Line line1) {
  628. Line line2 = line1.right;
  629. line1.right = line2.left;
  630. if (line2.left != sentinel) {
  631. line2.left.parent = line1;
  632. }
  633. if (line2 != sentinel) {
  634. line2.parent = line1.parent;
  635. }
  636. if (line1.parent != null) {
  637. if (line1 == line1.parent.left) {
  638. line1.parent.left = line2;
  639. } else {
  640. line1.parent.right = line2;
  641. }
  642. } else {
  643. document = line2;
  644. }
  645. line2.left = line1;
  646. if (line1 != sentinel) {
  647. line1.parent = line2;
  648. }
  649. }
  650. private void RotateRight(Line line1) {
  651. Line line2 = line1.left;
  652. line1.left = line2.right;
  653. if (line2.right != sentinel) {
  654. line2.right.parent = line1;
  655. }
  656. if (line2 != sentinel) {
  657. line2.parent = line1.parent;
  658. }
  659. if (line1.parent != null) {
  660. if (line1 == line1.parent.right) {
  661. line1.parent.right = line2;
  662. } else {
  663. line1.parent.left = line2;
  664. }
  665. } else {
  666. document = line2;
  667. }
  668. line2.right = line1;
  669. if (line1 != sentinel) {
  670. line1.parent = line2;
  671. }
  672. }
  673. internal void UpdateView(Line line, int pos) {
  674. if (!owner.IsHandleCreated) {
  675. return;
  676. }
  677. if (update_suspended > 0) {
  678. update_start = Math.Min (update_start, line.line_no);
  679. // update_end = Math.Max (update_end, line.line_no);
  680. // recalc_optimize = true;
  681. update_pending = true;
  682. return;
  683. }
  684. // Optimize invalidation based on Line alignment
  685. if (RecalculateDocument(owner.CreateGraphicsInternal(), line.line_no, line.line_no, true)) {
  686. // Lineheight changed, invalidate the rest of the document
  687. if ((line.Y - viewport_y) >=0 ) {
  688. // We formatted something that's in view, only draw parts of the screen
  689. owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, owner.Height - line.Y - viewport_y));
  690. } else {
  691. // The tag was above the visible area, draw everything
  692. owner.Invalidate();
  693. }
  694. } else {
  695. switch(line.alignment) {
  696. case HorizontalAlignment.Left: {
  697. owner.Invalidate(new Rectangle(line.X + (int)line.widths[pos] - viewport_x - 1, line.Y - viewport_y, viewport_width, line.height + 1));
  698. break;
  699. }
  700. case HorizontalAlignment.Center: {
  701. owner.Invalidate(new Rectangle(line.X, line.Y - viewport_y, viewport_width, line.height + 1));
  702. break;
  703. }
  704. case HorizontalAlignment.Right: {
  705. owner.Invalidate(new Rectangle(line.X, line.Y - viewport_y, (int)line.widths[pos + 1] - viewport_x + line.X, line.height + 1));
  706. break;
  707. }
  708. }
  709. }
  710. }
  711. // Update display from line, down line_count lines; pos is unused, but required for the signature
  712. internal void UpdateView(Line line, int line_count, int pos) {
  713. if (!owner.IsHandleCreated) {
  714. return;
  715. }
  716. if (recalc_suspended > 0) {
  717. recalc_start = Math.Min (recalc_start, line.line_no);
  718. recalc_end = Math.Max (recalc_end, line.line_no + line_count);
  719. recalc_optimize = true;
  720. recalc_pending = true;
  721. return;
  722. }
  723. int start_line_top = line.Y;
  724. int end_line_bottom;
  725. Line end_line;
  726. end_line = GetLine (line.line_no + line_count);
  727. if (end_line == null)
  728. end_line = GetLine (lines);
  729. end_line_bottom = end_line.Y + end_line.height;
  730. if (RecalculateDocument(owner.CreateGraphicsInternal(), line.line_no, line.line_no + line_count, true)) {
  731. // Lineheight changed, invalidate the rest of the document
  732. if ((line.Y - viewport_y) >=0 ) {
  733. // We formatted something that's in view, only draw parts of the screen
  734. owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, owner.Height - line.Y - viewport_y));
  735. } else {
  736. // The tag was above the visible area, draw everything
  737. owner.Invalidate();
  738. }
  739. } else {
  740. int x = 0 - viewport_x;
  741. int w = viewport_width;
  742. int y = Math.Min (start_line_top - viewport_y, line.Y - viewport_y);
  743. int h = Math.Max (end_line_bottom - y, end_line.Y + end_line.height - y);
  744. owner.Invalidate (new Rectangle (x, y, w, h));
  745. }
  746. }
  747. #endregion // Private Methods
  748. #region Internal Methods
  749. // Clear the document and reset state
  750. internal void Empty() {
  751. document = sentinel;
  752. lines = 0;
  753. // We always have a blank line
  754. Add (1, String.Empty, owner.Font, ThemeEngine.Current.ResPool.GetSolidBrush (owner.ForeColor), LineEnding.None);
  755. this.RecalculateDocument(owner.CreateGraphicsInternal());
  756. PositionCaret(0, 0);
  757. SetSelectionVisible (false);
  758. selection_start.line = this.document;
  759. selection_start.pos = 0;
  760. selection_start.tag = selection_start.line.tags;
  761. selection_end.line = this.document;
  762. selection_end.pos = 0;
  763. selection_end.tag = selection_end.line.tags;
  764. char_count = 0;
  765. viewport_x = 0;
  766. viewport_y = 0;
  767. document_x = 0;
  768. document_y = 0;
  769. if (owner.IsHandleCreated)
  770. owner.Invalidate ();
  771. }
  772. internal void PositionCaret(Line line, int pos) {
  773. caret.tag = line.FindTag (pos);
  774. MoveCaretToTextTag ();
  775. caret.line = line;
  776. caret.pos = pos;
  777. if (owner.IsHandleCreated) {
  778. if (owner.Focused) {
  779. if (caret.height != caret.tag.height)
  780. XplatUI.CreateCaret (owner.Handle, caret_width, caret.height);
  781. XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.X - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
  782. }
  783. if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
  784. }
  785. // We set this at the end because we use the heights to determine whether or
  786. // not we need to recreate the caret
  787. caret.height = caret.tag.height;
  788. }
  789. internal void PositionCaret(int x, int y) {
  790. if (!owner.IsHandleCreated) {
  791. return;
  792. }
  793. caret.tag = FindCursor(x, y, out caret.pos);
  794. MoveCaretToTextTag ();
  795. caret.line = caret.tag.line;
  796. caret.height = caret.tag.height;
  797. if (owner.ShowSelection && (!selection_visible || owner.show_caret_w_selection)) {
  798. XplatUI.CreateCaret (owner.Handle, caret_width, caret.height);
  799. XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.X - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
  800. }
  801. if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
  802. }
  803. internal void CaretHasFocus() {
  804. if ((caret.tag != null) && owner.IsHandleCreated) {
  805. XplatUI.CreateCaret(owner.Handle, caret_width, caret.height);
  806. XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.X - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
  807. DisplayCaret ();
  808. }
  809. if (owner.IsHandleCreated && SelectionLength () > 0) {
  810. InvalidateSelectionArea ();
  811. }
  812. }
  813. internal void CaretLostFocus() {
  814. if (!owner.IsHandleCreated) {
  815. return;
  816. }
  817. XplatUI.DestroyCaret(owner.Handle);
  818. }
  819. internal void AlignCaret() {
  820. if (!owner.IsHandleCreated) {
  821. return;
  822. }
  823. caret.tag = LineTag.FindTag (caret.line, caret.pos);
  824. MoveCaretToTextTag ();
  825. caret.height = caret.tag.height;
  826. if (owner.Focused) {
  827. XplatUI.CreateCaret(owner.Handle, caret_width, caret.height);
  828. XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.X - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
  829. DisplayCaret ();
  830. }
  831. if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
  832. }
  833. internal void UpdateCaret() {
  834. if (!owner.IsHandleCreated || caret.tag == null) {
  835. return;
  836. }
  837. MoveCaretToTextTag ();
  838. if (caret.tag.height != caret.height) {
  839. caret.height = caret.tag.height;
  840. if (owner.Focused) {
  841. XplatUI.CreateCaret(owner.Handle, caret_width, caret.height);
  842. }
  843. }
  844. if (owner.Focused) {
  845. XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.X - viewport_x, caret.line.Y + caret.tag.shift - viewport_y + caret_shift);
  846. DisplayCaret ();
  847. }
  848. if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
  849. }
  850. internal void DisplayCaret() {
  851. if (!owner.IsHandleCreated) {
  852. return;
  853. }
  854. if (owner.ShowSelection && (!selection_visible || owner.show_caret_w_selection)) {
  855. XplatUI.CaretVisible(owner.Handle, true);
  856. }
  857. }
  858. internal void HideCaret() {
  859. if (!owner.IsHandleCreated) {
  860. return;
  861. }
  862. if (owner.Focused) {
  863. XplatUI.CaretVisible(owner.Handle, false);
  864. }
  865. }
  866. internal void MoveCaretToTextTag ()
  867. {
  868. if (caret.tag == null || caret.tag.IsTextTag)
  869. return;
  870. if (caret.pos < caret.tag.start) {
  871. caret.tag = caret.tag.previous;
  872. } else {
  873. caret.tag = caret.tag.next;
  874. }
  875. }
  876. internal void MoveCaret(CaretDirection direction) {
  877. // FIXME should we use IsWordSeparator to detect whitespace, instead
  878. // of looking for actual spaces in the Word move cases?
  879. bool nowrap = false;
  880. switch(direction) {
  881. case CaretDirection.CharForwardNoWrap:
  882. nowrap = true;
  883. goto case CaretDirection.CharForward;
  884. case CaretDirection.CharForward: {
  885. caret.pos++;
  886. if (caret.pos > caret.line.TextLengthWithoutEnding ()) {
  887. if (!nowrap) {
  888. // Go into next line
  889. if (caret.line.line_no < this.lines) {
  890. caret.line = GetLine(caret.line.line_no+1);
  891. caret.pos = 0;
  892. caret.tag = caret.line.tags;
  893. } else {
  894. caret.pos--;
  895. }
  896. } else {
  897. // Single line; we stay where we are
  898. caret.pos--;
  899. }
  900. } else {
  901. if ((caret.tag.start - 1 + caret.tag.Length) < caret.pos) {
  902. caret.tag = caret.tag.next;
  903. }
  904. }
  905. UpdateCaret();
  906. return;
  907. }
  908. case CaretDirection.CharBackNoWrap:
  909. nowrap = true;
  910. goto case CaretDirection.CharBack;
  911. case CaretDirection.CharBack: {
  912. if (caret.pos > 0) {
  913. // caret.pos--; // folded into the if below
  914. if (--caret.pos > 0) {
  915. if (caret.tag.start > caret.pos) {
  916. caret.tag = caret.tag.previous;
  917. }
  918. }
  919. } else {
  920. if (caret.line.line_no > 1 && !nowrap) {
  921. caret.line = GetLine(caret.line.line_no - 1);
  922. caret.pos = caret.line.TextLengthWithoutEnding ();
  923. caret.tag = LineTag.FindTag(caret.line, caret.pos);
  924. }
  925. }
  926. UpdateCaret();
  927. return;
  928. }
  929. case CaretDirection.WordForward: {
  930. int len;
  931. len = caret.line.text.Length;
  932. if (caret.pos < len) {
  933. while ((caret.pos < len) && (caret.line.text[caret.pos] != ' ')) {
  934. caret.pos++;
  935. }
  936. if (caret.pos < len) {
  937. // Skip any whitespace
  938. while ((caret.pos < len) && (caret.line.text[caret.pos] == ' ')) {
  939. caret.pos++;
  940. }
  941. }
  942. caret.tag = LineTag.FindTag(caret.line, caret.pos);
  943. } else {
  944. if (caret.line.line_no < this.lines) {
  945. caret.line = GetLine(caret.line.line_no + 1);
  946. caret.pos = 0;
  947. caret.tag = caret.line.tags;
  948. }
  949. }
  950. UpdateCaret();
  951. return;
  952. }
  953. case CaretDirection.WordBack: {
  954. if (caret.pos > 0) {
  955. caret.pos--;
  956. while ((caret.pos > 0) && (caret.line.text[caret.pos] == ' ')) {
  957. caret.pos--;
  958. }
  959. while ((caret.pos > 0) && (caret.line.text[caret.pos] != ' ')) {
  960. caret.pos--;
  961. }
  962. if (caret.line.text.ToString(caret.pos, 1) == " ") {
  963. if (caret.pos != 0) {
  964. caret.pos++;
  965. } else {
  966. caret.line = GetLine(caret.line.line_no - 1);
  967. caret.pos = caret.line.text.Length;
  968. }
  969. }
  970. caret.tag = LineTag.FindTag(caret.line, caret.pos);
  971. } else {
  972. if (caret.line.line_no > 1) {
  973. caret.line = GetLine(caret.line.line_no - 1);
  974. caret.pos = caret.line.text.Length;
  975. caret.tag = LineTag.FindTag(caret.line, caret.pos);
  976. }
  977. }
  978. UpdateCaret();
  979. return;
  980. }
  981. case CaretDirection.LineUp: {
  982. if (caret.line.line_no > 1) {
  983. int pixel;
  984. pixel = (int)caret.line.widths[caret.pos];
  985. PositionCaret(pixel, GetLine(caret.line.line_no - 1).Y);
  986. DisplayCaret ();
  987. }
  988. return;
  989. }
  990. case CaretDirection.LineDown: {
  991. if (caret.line.line_no < lines) {
  992. int pixel;
  993. pixel = (int)caret.line.widths[caret.pos];
  994. PositionCaret(pixel, GetLine(caret.line.line_no + 1).Y);
  995. DisplayCaret ();
  996. }
  997. return;
  998. }
  999. case CaretDirection.Home: {
  1000. if (caret.pos > 0) {
  1001. caret.pos = 0;
  1002. caret.tag = caret.line.tags;
  1003. UpdateCaret();
  1004. }
  1005. return;
  1006. }
  1007. case CaretDirection.End: {
  1008. if (caret.pos < caret.line.TextLengthWithoutEnding ()) {
  1009. caret.pos = caret.line.TextLengthWithoutEnding ();
  1010. caret.tag = LineTag.FindTag(caret.line, caret.pos);
  1011. UpdateCaret();
  1012. }
  1013. return;
  1014. }
  1015. case CaretDirection.PgUp: {
  1016. if (viewport_y == 0 && owner.richtext) {
  1017. owner.vscroll.Value = 0;
  1018. Line line = GetLine (1);
  1019. PositionCaret (line, 0);
  1020. }
  1021. int y_offset = caret.line.Y + caret.line.height - 1 - viewport_y;
  1022. int index;
  1023. LineTag top = FindCursor ((int) caret.line.widths [caret.pos],
  1024. viewport_y - viewport_height, out index);
  1025. owner.vscroll.Value = Math.Min (top.line.Y, owner.vscroll.Maximum - viewport_height);
  1026. PositionCaret ((int) caret.line.widths [caret.pos], y_offset + viewport_y);
  1027. return;
  1028. }
  1029. case CaretDirection.PgDn: {
  1030. if (viewport_y + viewport_height >= document_y && owner.richtext) {
  1031. owner.vscroll.Value = owner.vscroll.Maximum - viewport_height + 1;
  1032. Line line = GetLine (lines);
  1033. PositionCaret (line, line.Text.Length);
  1034. }
  1035. int y_offset = caret.line.Y - viewport_y;
  1036. int index;
  1037. LineTag top = FindCursor ((int) caret.line.widths [caret.pos],
  1038. viewport_y + viewport_height, out index);
  1039. owner.vscroll.Value = Math.Min (top.line.Y, owner.vscroll.Maximum - viewport_height);
  1040. PositionCaret ((int) caret.line.widths [caret.pos], y_offset + viewport_y);
  1041. return;
  1042. }
  1043. case CaretDirection.CtrlPgUp: {
  1044. PositionCaret(0, viewport_y);
  1045. DisplayCaret ();
  1046. return;
  1047. }
  1048. case CaretDirection.CtrlPgDn: {
  1049. Line line;
  1050. LineTag tag;
  1051. int index;
  1052. tag = FindTag(0, viewport_y + viewport_height, out index, false);
  1053. if (tag.line.line_no > 1) {
  1054. line = GetLine(tag.line.line_no - 1);
  1055. } else {
  1056. line = tag.line;
  1057. }
  1058. PositionCaret(line, line.Text.Length);
  1059. DisplayCaret ();
  1060. return;
  1061. }
  1062. case CaretDirection.CtrlHome: {
  1063. caret.line = GetLine(1);
  1064. caret.pos = 0;
  1065. caret.tag = caret.line.tags;
  1066. UpdateCaret();
  1067. return;
  1068. }
  1069. case CaretDirection.CtrlEnd: {
  1070. caret.line = GetLine(lines);
  1071. caret.pos = caret.line.TextLengthWithoutEnding ();
  1072. caret.tag = LineTag.FindTag(caret.line, caret.pos);
  1073. UpdateCaret();
  1074. return;
  1075. }
  1076. case CaretDirection.SelectionStart: {
  1077. caret.line = selection_start.line;
  1078. caret.pos = selection_start.pos;
  1079. caret.tag = selection_start.tag;
  1080. UpdateCaret();
  1081. return;
  1082. }
  1083. case CaretDirection.SelectionEnd: {
  1084. caret.line = selection_end.line;
  1085. caret.pos = selection_end.pos;
  1086. caret.tag = selection_end.tag;
  1087. UpdateCaret();
  1088. return;
  1089. }
  1090. }
  1091. }
  1092. internal void DumpDoc ()
  1093. {
  1094. Console.WriteLine ("<doc lines='{0}'>", lines);
  1095. for (int i = 1; i <= lines ; i++) {
  1096. Line line = GetLine (i);
  1097. Console.WriteLine ("<line no='{0}' ending='{1}'>", line.line_no, line.ending);
  1098. LineTag tag = line.tags;
  1099. while (tag != null) {
  1100. Console.Write ("\t<tag type='{0}' span='{1}->{2}' font='{3}' color='{4}'>",
  1101. tag.GetType (), tag.start, tag.Length, tag.font, tag.color.Color);
  1102. Console.Write (tag.Text ());
  1103. Console.WriteLine ("</tag>");
  1104. tag = tag.next;
  1105. }
  1106. Console.WriteLine ("</line>");
  1107. }
  1108. Console.WriteLine ("</doc>");
  1109. }
  1110. internal void Draw (Graphics g, Rectangle clip)
  1111. {
  1112. Line line; // Current line being drawn
  1113. LineTag tag; // Current tag being drawn
  1114. int start; // First line to draw
  1115. int end; // Last line to draw
  1116. StringBuilder text; // String representing the current line
  1117. int line_no;
  1118. Brush tag_brush;
  1119. Brush current_brush;
  1120. Brush disabled_brush;
  1121. Brush readonly_brush;
  1122. Brush hilight;
  1123. Brush hilight_text;
  1124. // First, figure out from what line to what line we need to draw
  1125. if (multiline) {
  1126. start = GetLineByPixel(clip.Top + viewport_y, false).line_no;
  1127. end = GetLineByPixel(clip.Bottom + viewport_y, false).line_no;
  1128. } else {
  1129. start = GetLineByPixel(clip.Left + viewport_x, false).line_no;
  1130. end = GetLineByPixel(clip.Right + viewport_x, false).line_no;
  1131. }
  1132. ///
  1133. /// We draw the single border ourself
  1134. ///
  1135. if (owner.actual_border_style == BorderStyle.FixedSingle) {
  1136. ControlPaint.DrawBorder (g, owner.ClientRectangle, Color.Black, ButtonBorderStyle.Solid);
  1137. }
  1138. /// Make sure that we aren't drawing one more line then we need to
  1139. line = GetLine (end - 1);
  1140. if (line != null && clip.Bottom == line.Y + line.height + viewport_y)
  1141. end--;
  1142. line_no = start;
  1143. #if Debug
  1144. DateTime n = DateTime.Now;
  1145. Console.WriteLine ("Started drawing: {0}s {1}ms", n.Second, n.Millisecond);
  1146. Console.WriteLine ("CLIP: {0}", clip);
  1147. Console.WriteLine ("S: {0}", GetLine (start).text);
  1148. Console.WriteLine ("E: {0}", GetLine (end).text);
  1149. #endif
  1150. disabled_brush = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorGrayText);
  1151. readonly_brush = ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorControlText);
  1152. hilight = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorHighlight);
  1153. hilight_text = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorHighlightText);
  1154. // Non multiline selection can be handled outside of the loop
  1155. if (!multiline && selection_visible && owner.ShowSelection) {
  1156. g.FillRectangle (hilight,
  1157. selection_start.line.widths [selection_start.pos] +
  1158. selection_start.line.X - viewport_x,
  1159. selection_start.line.Y,
  1160. (selection_end.line.X + selection_end.line.widths [selection_end.pos]) -
  1161. (selection_start.line.X + selection_start.line.widths [selection_start.pos]),
  1162. selection_start.line.height);
  1163. }
  1164. while (line_no <= end) {
  1165. line = GetLine (line_no);
  1166. float line_y = line.Y - viewport_y;
  1167. tag = line.tags;
  1168. if (!calc_pass) {
  1169. text = line.text;
  1170. } else {
  1171. if (PasswordCache.Length < line.text.Length)
  1172. PasswordCache.Append(Char.Parse(password_char), line.text.Length - PasswordCache.Length);
  1173. else if (PasswordCache.Length > line.text.Length)
  1174. PasswordCache.Remove(line.text.Length, PasswordCache.Length - line.text.Length);
  1175. text = PasswordCache;
  1176. }
  1177. int line_selection_start = text.Length + 1;
  1178. int line_selection_end = text.Length + 1;
  1179. if (selection_visible && owner.ShowSelection &&
  1180. (line_no >= selection_start.line.line_no) &&
  1181. (line_no <= selection_end.line.line_no)) {
  1182. if (line_no == selection_start.line.line_no)
  1183. line_selection_start = selection_start.pos + 1;
  1184. else
  1185. line_selection_start = 1;
  1186. if (line_no == selection_end.line.line_no)
  1187. line_selection_end = selection_end.pos + 1;
  1188. else
  1189. line_selection_end = text.Length + 1;
  1190. if (line_selection_end == line_selection_start) {
  1191. // There isn't really selection
  1192. line_selection_start = text.Length + 1;
  1193. line_selection_end = line_selection_start;
  1194. } else if (multiline) {
  1195. // lets draw some selection baby!! (non multiline selection is drawn outside the loop)
  1196. g.FillRectangle (hilight,
  1197. line.widths [line_selection_start - 1] + line.X - viewport_x,
  1198. line_y, line.widths [line_selection_end - 1] - line.widths [line_selection_start - 1],
  1199. line.height);
  1200. }
  1201. }
  1202. current_brush = line.tags.color;
  1203. while (tag != null) {
  1204. // Skip empty tags
  1205. if (tag.Length == 0) {
  1206. tag = tag.next;
  1207. continue;
  1208. }
  1209. if (((tag.X + tag.Width) < (clip.Left - viewport_x)) && (tag.X > (clip.Right - viewport_x))) {
  1210. tag = tag.next;
  1211. continue;
  1212. }
  1213. if (tag.back_color != Color.Empty) {
  1214. g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (tag.back_color), tag.X + line.X - viewport_x,
  1215. line_y + tag.shift, tag.Width, line.height);
  1216. }
  1217. tag_brush = tag.color;
  1218. current_brush = tag_brush;
  1219. if (!owner.Enabled) {
  1220. Color a = ((SolidBrush) tag.color).Color;
  1221. Color b = ThemeEngine.Current.ColorWindowText;
  1222. if ((a.R == b.R) && (a.G == b.G) && (a.B == b.B)) {
  1223. tag_brush = disabled_brush;
  1224. }
  1225. } else if (owner.read_only && !owner.backcolor_set) {
  1226. tag_brush = readonly_brush;
  1227. }
  1228. int tag_pos = tag.start;
  1229. current_brush = tag_brush;
  1230. while (tag_pos < tag.start + tag.Length) {
  1231. int old_tag_pos = tag_pos;
  1232. if (tag_pos >= line_selection_start && tag_pos < line_selection_end) {
  1233. current_brush = hilight_text;
  1234. tag_pos = Math.Min (tag.End, line_selection_end);
  1235. } else if (tag_pos < line_selection_start) {
  1236. current_brush = tag_brush;
  1237. tag_pos = Math.Min (tag.End, line_selection_start);
  1238. } else {
  1239. current_brush = tag_brush;
  1240. tag_pos = tag.End;
  1241. }
  1242. tag.Draw (g, current_brush,
  1243. line.X - viewport_x,
  1244. line_y + tag.shift,
  1245. old_tag_pos - 1, Math.Min (tag.start + tag.Length, tag_pos) - 1,
  1246. text.ToString() );
  1247. }
  1248. tag = tag.next;
  1249. }
  1250. line.DrawEnding (g, line_y);
  1251. line_no++;
  1252. }
  1253. }
  1254. internal int GetLineEnding (string line, int start, out LineEnding ending)
  1255. {
  1256. int res;
  1257. res = line.IndexOf ('\r', start);
  1258. if (res != -1) {
  1259. if (res + 2 < line.Length && line [res + 1] == '\r' && line [res + 2] == '\n') {
  1260. ending = LineEnding.Soft;
  1261. return res;
  1262. }
  1263. if (res + 1 < line.Length && line [res + 1] == '\n') {
  1264. ending = LineEnding.Hard;
  1265. return res;
  1266. }
  1267. ending = LineEnding.Limp;
  1268. return res;
  1269. }
  1270. res = line.IndexOf ('\n', start);
  1271. if (res != -1) {
  1272. ending = LineEnding.Rich;
  1273. return res;
  1274. }
  1275. ending = LineEnding.Wrap;
  1276. return line.Length;
  1277. }
  1278. internal int LineEndingLength (LineEnding ending)
  1279. {
  1280. int res = 0;
  1281. switch (ending) {
  1282. case LineEnding.Limp:
  1283. case LineEnding.Rich:
  1284. res = 1;
  1285. break;
  1286. case LineEnding.Hard:
  1287. res = 2;
  1288. break;
  1289. case LineEnding.Soft:
  1290. res = 3;
  1291. break;
  1292. }
  1293. return res;
  1294. }
  1295. internal string LineEndingToString (LineEnding ending)
  1296. {
  1297. string res = String.Empty;
  1298. switch (ending) {
  1299. case LineEnding.Limp:
  1300. res = "\r";
  1301. break;
  1302. case LineEnding.Hard:
  1303. res = "\r\n";
  1304. break;
  1305. case LineEnding.Soft:
  1306. res = "\r\r\n";
  1307. break;
  1308. case LineEnding.Rich:
  1309. res = "\n";
  1310. break;
  1311. }
  1312. return res;
  1313. }
  1314. // Insert multi-line text at the given position; use formatting at insertion point for inserted text
  1315. internal void Insert(Line line, int pos, bool update_caret, string s) {
  1316. int break_index;
  1317. int base_line;
  1318. int old_line_count;
  1319. int count = 1;
  1320. LineEnding ending;
  1321. LineTag tag = LineTag.FindTag (line, pos);
  1322. SuspendRecalc ();
  1323. base_line = line.line_no;
  1324. old_line_count = lines;
  1325. break_index = GetLineEnding (s, 0, out ending);
  1326. // Bump the text at insertion point a line down if we're inserting more than one line
  1327. if (break_index != s.Length) {
  1328. Split (line, pos);
  1329. line.ending = ending;
  1330. // Remainder of start line is now in base_line + 1
  1331. }
  1332. InsertString (line, pos, s.Substring (0, break_index + LineEndingLength (ending)));
  1333. break_index += LineEndingLength (ending);
  1334. while (break_index < s.Length) {
  1335. int next_break = GetLineEnding (s, break_index, out ending);
  1336. string line_text = s.Substring (break_index, next_break - break_index +
  1337. LineEndingLength (ending));
  1338. Add (base_line + count, line_text, line.alignment, tag.font, tag.color, ending);
  1339. Line last = GetLine (base_line + count);
  1340. last.ending = ending;
  1341. count++;
  1342. break_index = next_break + LineEndingLength (ending);
  1343. }
  1344. ResumeRecalc (true);
  1345. UpdateView(line, lines - old_line_count + 1, pos);
  1346. if (update_caret) {
  1347. // Move caret to the end of the inserted text
  1348. Line l = GetLine (line.line_no + lines - old_line_count);
  1349. PositionCaret(l, l.text.Length);
  1350. DisplayCaret ();
  1351. }
  1352. }
  1353. // Inserts a character at the given position
  1354. internal void InsertString(Line line, int pos, string s) {
  1355. InsertString(line.FindTag(pos), pos, s);
  1356. }
  1357. // Inserts a string at the given position
  1358. internal void InsertString(LineTag tag, int pos, string s) {
  1359. Line line;
  1360. int len;
  1361. len = s.Length;
  1362. CharCount += len;
  1363. line = tag.line;
  1364. line.text.Insert(pos, s);
  1365. tag = tag.next;
  1366. while (tag != null) {
  1367. tag.start += len;
  1368. tag = tag.next;
  1369. }
  1370. line.Grow(len);
  1371. line.recalc = true;
  1372. UpdateView(line, pos);
  1373. }
  1374. // Inserts a string at the caret position
  1375. internal void InsertStringAtCaret(string s, bool move_caret) {
  1376. InsertString (caret.tag, caret.pos, s);
  1377. UpdateView(caret.line, caret.pos);
  1378. if (move_caret) {
  1379. caret.pos += s.Length;
  1380. UpdateCaret();
  1381. }
  1382. }
  1383. // Inserts a character at the given position
  1384. internal void InsertChar(Line line, int pos, char ch) {
  1385. InsertChar(line.FindTag(pos), pos, ch);
  1386. }
  1387. // Inserts a character at the given position
  1388. internal void InsertChar(LineTag tag, int pos, char ch) {
  1389. Line line;
  1390. CharCount++;
  1391. line = tag.line;
  1392. line.text.Insert(pos, ch);
  1393. tag = tag.next;
  1394. while (tag != null) {
  1395. tag.start++;
  1396. tag = tag.next;
  1397. }
  1398. line.Grow(1);
  1399. line.recalc = true;
  1400. undo.RecordTyping (line, pos, ch);
  1401. UpdateView(line, pos);
  1402. }
  1403. // Inserts a character at the current caret position
  1404. internal void InsertCharAtCaret(char ch, bool move_caret) {
  1405. /*
  1406. LineTag tag;
  1407. CharCount++;
  1408. caret.line.text.Insert(caret.pos, ch);
  1409. caret.tag.length++;
  1410. if (caret.tag.next != null) {
  1411. tag = caret.tag.next;
  1412. while (tag != null) {
  1413. tag.start++;
  1414. tag = tag.next;
  1415. }
  1416. }
  1417. caret.line.Grow(1);
  1418. caret.line.recalc = true;
  1419. */
  1420. InsertChar (caret.tag, caret.pos, ch);
  1421. UpdateView(caret.line, caret.pos);
  1422. if (move_caret) {
  1423. caret.pos++;
  1424. UpdateCaret();
  1425. SetSelectionToCaret(true);
  1426. }
  1427. }
  1428. internal void InsertPicture (Line line, int pos, RTF.Picture picture)
  1429. {
  1430. //LineTag next_tag;
  1431. LineTag tag;
  1432. int len;
  1433. len = 1;
  1434. // Just a place holder basically
  1435. line.text.Insert (pos, "I");
  1436. PictureTag picture_tag = new PictureTag (line, pos + 1, picture);
  1437. tag = LineTag.FindTag (line, pos);
  1438. picture_tag.CopyFormattingFrom (tag);
  1439. /*next_tag = */tag.Break (pos + 1);
  1440. picture_tag.previous = tag;
  1441. picture_tag.next = tag.next;
  1442. tag.next = picture_tag;
  1443. //
  1444. // Picture tags need to be surrounded by text tags
  1445. //
  1446. if (picture_tag.next == null) {
  1447. picture_tag.next = new LineTag (line, pos + 1);
  1448. picture_tag.next.CopyFormattingFrom (tag);
  1449. picture_tag.next.previous = picture_tag;
  1450. }
  1451. tag = picture_tag.next;
  1452. while (tag != null) {
  1453. tag.start += len;
  1454. tag = tag.next;
  1455. }
  1456. line.Grow (len);
  1457. line.recalc = true;
  1458. UpdateView (line, pos);
  1459. }
  1460. internal void DeleteMultiline (Line start_line, int pos, int length)
  1461. {
  1462. Marker start = new Marker ();
  1463. Marker end = new Marker ();
  1464. int start_index = LineTagToCharIndex (start_line, pos);
  1465. start.line = start_line;
  1466. start.pos = pos;
  1467. start.tag = LineTag.FindTag (start_line, pos);
  1468. CharIndexToLineTag (start_index + length, out end.line,
  1469. out end.tag, out end.pos);
  1470. SuspendUpdate ();
  1471. if (start.line == end.line) {
  1472. DeleteChars (start.tag, pos, end.pos - pos);
  1473. } else {
  1474. // Delete first and last lines
  1475. DeleteChars (start.tag, start.pos, start.line.text.Length - start.pos);
  1476. DeleteChars (end.line.tags, 0, end.pos);
  1477. int current = start.line.line_no + 1;
  1478. if (current < end.line.line_no) {
  1479. for (int i = end.line.line_no - 1; i >= current; i--) {
  1480. Delete (i);
  1481. }
  1482. }
  1483. // BIG FAT WARNING - selection_end.line might be stale due
  1484. // to the above Delete() call. DONT USE IT before hitting the end of this method!
  1485. // Join start and end
  1486. Combine (start.line.line_no, current);
  1487. }
  1488. ResumeUpdate (true);
  1489. }
  1490. // Deletes n characters at the given position; it will not delete past line limits
  1491. // pos is 0-based
  1492. internal void DeleteChars(LineTag tag, int pos, int count) {
  1493. Line line;
  1494. bool streamline;
  1495. streamline = false;
  1496. line = tag.line;
  1497. CharCount -= count;
  1498. if (pos == line.text.Length) {
  1499. return;
  1500. }
  1501. line.text.Remove(pos, count);
  1502. // Make sure the tag points to the right spot
  1503. while ((tag != null) && (tag.End) < pos) {
  1504. tag = tag.next;
  1505. }
  1506. if (tag == null) {
  1507. goto Cleanup;
  1508. }
  1509. // Check if we're crossing tag boundaries
  1510. if ((pos + count) > (tag.start + tag.Length - 1)) {
  1511. int left;
  1512. // We have to delete cross tag boundaries
  1513. streamline = true;
  1514. left = count;
  1515. left -= tag.start + tag.Length - pos - 1;
  1516. tag = tag.next;
  1517. while ((tag != null) && (left > 0)) {
  1518. tag.start -= count - left;
  1519. if (tag.Length > left) {
  1520. left = 0;
  1521. } else {
  1522. left -= tag.Length;
  1523. tag = tag.next;
  1524. }
  1525. }
  1526. } else {
  1527. // We got off easy, same tag
  1528. if (tag.Length == 0) {
  1529. streamline = true;
  1530. }
  1531. }
  1532. // Delete empty orphaned tags at the end
  1533. LineTag walk = tag;
  1534. while (walk != null && walk.next != null && walk.next.Length == 0) {
  1535. LineTag t = walk;
  1536. walk.next = walk.next.next;
  1537. if (walk.next != null)
  1538. walk.next.previous = t;
  1539. walk = walk.next;
  1540. }
  1541. // Adjust the start point of any tags following
  1542. if (tag != null) {
  1543. tag = tag.next;
  1544. while (tag != null) {
  1545. tag.start -= count;
  1546. tag = tag.next;
  1547. }
  1548. }
  1549. line.recalc = true;
  1550. if (streamline) {
  1551. line.Streamline(lines);
  1552. }
  1553. Cleanup:
  1554. if (pos >= line.TextLengthWithoutEnding ()) {
  1555. LineEnding ending = line.ending;
  1556. GetLineEnding (line.text.ToString (), 0, out ending);
  1557. if (ending != line.ending) {
  1558. line.ending = ending;
  1559. if (!multiline) {
  1560. UpdateView (line, lines, pos);
  1561. owner.Invalidate ();
  1562. return;
  1563. }
  1564. }
  1565. }
  1566. if (!multiline) {
  1567. UpdateView (line, lines, pos);
  1568. owner.Invalidate ();
  1569. } else
  1570. UpdateView(line, pos);
  1571. }
  1572. // Deletes a character at or after the given position (depending on forward); it will not delete past line limits
  1573. internal void DeleteChar(LineTag tag, int pos, bool forward) {
  1574. Line line;
  1575. bool streamline;
  1576. CharCount--;
  1577. streamline = false;
  1578. line = tag.line;
  1579. if ((pos == 0 && forward == false) || (pos == line.text.Length && forward == true)) {
  1580. return;
  1581. }
  1582. if (forward) {
  1583. line.text.Remove(pos, 1);
  1584. while ((tag != null) && (tag.start + tag.Length - 1) <= pos) {
  1585. tag = tag.next;
  1586. }
  1587. if (tag == null) {
  1588. goto Cleanup;
  1589. }
  1590. // tag.length--;
  1591. if (tag.Length == 0) {
  1592. streamline = true;
  1593. }
  1594. } else {
  1595. pos--;
  1596. line.text.Remove(pos, 1);
  1597. if (pos >= (tag.start - 1)) {
  1598. // tag.length--;
  1599. if (tag.Length == 0) {
  1600. streamline = true;
  1601. }
  1602. } else if (tag.previous != null) {
  1603. // tag.previous.length--;
  1604. if (tag.previous.Length == 0) {
  1605. streamline = true;
  1606. }
  1607. }
  1608. }
  1609. // Delete empty orphaned tags at the end
  1610. LineTag walk = tag;
  1611. while (walk != null && walk.next != null && walk.next.Length == 0) {
  1612. LineTag t = walk;
  1613. walk.next = walk.next.next;
  1614. if (walk.next != null)
  1615. walk.next.previous = t;
  1616. walk = walk.next;
  1617. }
  1618. tag = tag.next;
  1619. while (tag != null) {
  1620. tag.start--;
  1621. tag = tag.next;
  1622. }
  1623. line.recalc = true;
  1624. if (streamline) {
  1625. line.Streamline(lines);
  1626. }
  1627. Cleanup:
  1628. if (pos >= line.TextLengthWithoutEnding ()) {
  1629. LineEnding ending = line.ending;
  1630. GetLineEnding (line.text.ToString (), 0, out ending);
  1631. if (ending != line.ending) {
  1632. line.ending = ending;
  1633. if (!multiline) {
  1634. UpdateView (line, lines, pos);
  1635. owner.Invalidate ();
  1636. return;
  1637. }
  1638. }
  1639. }
  1640. if (!multiline) {
  1641. UpdateView (line, lines, pos);
  1642. owner.Invalidate ();
  1643. } else
  1644. UpdateView(line, pos);
  1645. }
  1646. // Combine two lines
  1647. internal void Combine(int FirstLine, int SecondLine) {
  1648. Combine(GetLine(FirstLine), GetLine(SecondLine));
  1649. }
  1650. internal void Combine(Line first, Line second) {
  1651. LineTag last;
  1652. int shift;
  1653. // strip the ending off of the first lines text
  1654. first.text.Length = first.text.Length - LineEndingLength (first.ending);
  1655. // Combine the two tag chains into one
  1656. last = first.tags;
  1657. // Maintain the line ending style
  1658. first.ending = second.ending;
  1659. while (last.next != null) {
  1660. last = last.next;
  1661. }
  1662. // need to get the shift before setting the next tag since that effects length
  1663. shift = last.start + last.Length - 1;
  1664. last.next = second.tags;
  1665. last.next.previous = last;
  1666. // Fix up references within the chain
  1667. last = last.next;
  1668. while (last != null) {
  1669. last.line = first;
  1670. last.start += shift;
  1671. last = last.next;
  1672. }
  1673. // Combine both lines' strings
  1674. first.text.Insert(first.text.Length, second.text.ToString());
  1675. first.Grow(first.text.Length);
  1676. // Remove the reference to our (now combined) tags from the doomed line
  1677. second.tags = null;
  1678. // Renumber lines
  1679. DecrementLines(first.line_no + 2); // first.line_no + 1 will be deleted, so we need to start renumbering one later
  1680. // Mop up
  1681. first.recalc = true;
  1682. first.height = 0; // This forces RecalcDocument/UpdateView to redraw from this line on
  1683. first.Streamline(lines);
  1684. // Update Caret, Selection, etc
  1685. if (caret.line == second) {
  1686. caret.Combine(first, shift);
  1687. }
  1688. if (selection_anchor.line == second) {
  1689. selection_anchor.Combine(first, shift);
  1690. }
  1691. if (selection_start.line == second) {
  1692. selection_start.Combine(first, shift);
  1693. }
  1694. if (selection_end.line == second) {
  1695. selection_end.Combine(first, shift);
  1696. }
  1697. #if Debug
  1698. Line check_first;
  1699. Line check_second;
  1700. check_first = GetLine(first.line_no);
  1701. check_second = GetLine(check_first.line_no + 1);
  1702. Console.WriteLine("Pre-delete: Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
  1703. #endif
  1704. this.Delete(second);
  1705. #if Debug
  1706. check_first = GetLine(first.line_no);
  1707. check_second = GetLine(check_first.line_no + 1);
  1708. Console.WriteLine("Post-delete Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
  1709. #endif
  1710. }
  1711. // Split the line at the position into two
  1712. internal void Split(int LineNo, int pos) {
  1713. Line line;
  1714. LineTag tag;
  1715. line = GetLine(LineNo);
  1716. tag = LineTag.FindTag(line, pos);
  1717. Split(line, tag, pos);
  1718. }
  1719. internal void Split(Line line, int pos) {
  1720. LineTag tag;
  1721. tag = LineTag.FindTag(line, pos);
  1722. Split(line, tag, pos);
  1723. }
  1724. ///<summary>Split line at given tag and position into two lines</summary>
  1725. ///if more space becomes available on previous line</param>
  1726. internal void Split(Line line, LineTag tag, int pos) {
  1727. LineTag new_tag;
  1728. Line new_line;
  1729. bool move_caret;
  1730. bool move_sel_start;
  1731. bool move_sel_end;
  1732. move_caret = false;
  1733. move_sel_start = false;
  1734. move_sel_end = false;
  1735. // Adjust selection and cursors
  1736. if (caret.line == line && caret.pos >= pos) {
  1737. move_caret = true;
  1738. }
  1739. if (selection_start.line == line && selection_start.pos > pos) {
  1740. move_sel_start = true;
  1741. }
  1742. if (selection_end.line == line && selection_end.pos > pos) {
  1743. move_sel_end = true;
  1744. }
  1745. // cover the easy case first
  1746. if (pos == line.text.Length) {
  1747. Add (line.line_no + 1, String.Empty, line.alignment, tag.font, tag.color, line.ending);
  1748. new_line = GetLine (line.line_no + 1);
  1749. if (move_caret) {
  1750. caret.line = new_line;
  1751. caret.tag = new_line.tags;
  1752. caret.pos = 0;
  1753. }
  1754. if (move_sel_start) {
  1755. selection_start.line = new_line;
  1756. selection_start.pos = 0;
  1757. selection_start.tag = new_line.tags;
  1758. }
  1759. if (move_sel_end) {
  1760. selection_end.line = new_line;
  1761. selection_end.pos = 0;
  1762. selection_end.tag = new_line.tags;
  1763. }
  1764. return;
  1765. }
  1766. // We need to move the rest of the text into the new line
  1767. Add (line.line_no + 1, line.text.ToString (pos, line.text.Length - pos), line.alignment, tag.font, tag.color, line.ending);
  1768. // Now transfer our tags from this line to the next
  1769. new_line = GetLine(line.line_no + 1);
  1770. line.recalc = true;
  1771. new_line.recalc = true;
  1772. if ((tag.start - 1) == pos) {
  1773. int shift;
  1774. // We can simply break the chain and move the tag into the next line
  1775. if (tag == line.tags) {
  1776. new_tag = new LineTag(line, 1);
  1777. new_tag.CopyFormattingFrom (tag);
  1778. line.tags = new_tag;
  1779. }
  1780. if (tag.previous != null) {
  1781. tag.previous.next = null;
  1782. }
  1783. new_line.tags = tag;
  1784. tag.previous = null;
  1785. tag.line = new_line;
  1786. // Walk the list and correct the start location of the tags we just bumped into the next line
  1787. shift = tag.start - 1;
  1788. new_tag = tag;
  1789. while (new_tag != null) {
  1790. new_tag.start -= shift;
  1791. new_tag.line = new_line;
  1792. new_tag = new_tag.next;
  1793. }
  1794. } else {
  1795. int shift;
  1796. new_tag = new LineTag (new_line, 1);
  1797. new_tag.next = tag.next;
  1798. new_tag.CopyFormattingFrom (tag);
  1799. new_line.tags = new_tag;
  1800. if (new_tag.next != null) {
  1801. new_tag.next.previous = new_tag;
  1802. }
  1803. tag.next = null;
  1804. shift = pos;
  1805. new_tag = new_tag.next;
  1806. while (new_tag != null) {
  1807. new_tag.start -= shift;
  1808. new_tag.line = new_line;
  1809. new_tag = new_tag.next;
  1810. }
  1811. }
  1812. if (move_caret) {
  1813. caret.line = new_line;
  1814. caret.pos = caret.pos - pos;
  1815. caret.tag = caret.line.FindTag(caret.pos);
  1816. }
  1817. if (move_sel_start) {
  1818. selection_start.line = new_line;
  1819. selection_start.pos = selection_start.pos - pos;
  1820. selection_start.tag = new_line.FindTag(selection_start.pos);
  1821. }
  1822. if (move_sel_end) {
  1823. selection_end.line = new_line;
  1824. selection_end.pos = selection_end.pos - pos;
  1825. selection_end.tag = new_line.FindTag(selection_end.pos);
  1826. }
  1827. CharCount -= line.text.Length - pos;
  1828. line.text.Remove(pos, line.text.Length - pos);
  1829. }
  1830. // Adds a line of text, with given font.
  1831. // Bumps any line at that line number that already exists down
  1832. internal void Add (int LineNo, string Text, Font font, SolidBrush color, LineEnding ending)
  1833. {
  1834. Add (LineNo, Text, alignment, font, color, ending);
  1835. }
  1836. internal void Add (int LineNo, string Text, HorizontalAlignment align, Font font, SolidBrush color, LineEnding ending)
  1837. {
  1838. Line add;
  1839. Line line;
  1840. int line_no;
  1841. CharCount += Text.Length;
  1842. if (LineNo<1 || Text == null) {
  1843. if (LineNo<1) {
  1844. throw new ArgumentNullException("LineNo", "Line numbers must be positive");
  1845. } else {
  1846. throw new ArgumentNullException("Text", "Cannot insert NULL line");
  1847. }
  1848. }
  1849. add = new Line (this, LineNo, Text, align, font, color, ending);
  1850. line = document;
  1851. while (line != sentinel) {
  1852. add.parent = line;
  1853. line_no = line.line_no;
  1854. if (LineNo > line_no) {
  1855. line = line.right;
  1856. } else if (LineNo < line_no) {
  1857. line = line.left;
  1858. } else {
  1859. // Bump existing line numbers; walk all nodes to the right of this one and increment line_no
  1860. IncrementLines(line.line_no);
  1861. line = line.left;
  1862. }
  1863. }
  1864. add.left = sentinel;
  1865. add.right = sentinel;
  1866. if (add.parent != null) {
  1867. if (LineNo > add.parent.line_no) {
  1868. add.parent.right = add;
  1869. } else {
  1870. add.parent.left = add;
  1871. }
  1872. } else {
  1873. // Root node
  1874. document = add;
  1875. }
  1876. RebalanceAfterAdd(add);
  1877. lines++;
  1878. }
  1879. internal virtual void Clear() {
  1880. lines = 0;
  1881. CharCount = 0;
  1882. document = sentinel;
  1883. }
  1884. public virtual object Clone() {
  1885. Document clone;
  1886. clone = new Document(null);
  1887. clone.lines = this.lines;
  1888. clone.document = (Line)document.Clone();
  1889. return clone;
  1890. }
  1891. internal void Delete(int LineNo) {
  1892. Line line;
  1893. if (LineNo>lines) {
  1894. return;
  1895. }
  1896. line = GetLine(LineNo);
  1897. CharCount -= line.text.Length;
  1898. DecrementLines(LineNo + 1);
  1899. Delete(line);
  1900. }
  1901. internal void Delete(Line line1) {
  1902. Line line2;// = new Line();
  1903. Line line3;
  1904. if ((line1.left == sentinel) || (line1.right == sentinel)) {
  1905. line3 = line1;
  1906. } else {
  1907. line3 = line1.right;
  1908. while (line3.left != sentinel) {
  1909. line3 = line3.left;
  1910. }
  1911. }
  1912. if (line3.left != sentinel) {
  1913. line2 = line3.left;
  1914. } else {
  1915. line2 = line3.right;
  1916. }
  1917. line2.parent = line3.parent;
  1918. if (line3.parent != null) {
  1919. if(line3 == line3.parent.left) {
  1920. line3.parent.left = line2;
  1921. } else {
  1922. line3.parent.right = line2;
  1923. }
  1924. } else {
  1925. document = line2;
  1926. }
  1927. if (line3 != line1) {
  1928. LineTag tag;
  1929. if (selection_start.line == line3) {
  1930. selection_start.line = line1;
  1931. }
  1932. if (selection_end.line == line3) {
  1933. selection_end.line = line1;
  1934. }
  1935. if (selection_anchor.line == line3) {
  1936. selection_anchor.line = line1;
  1937. }
  1938. if (caret.line == line3) {
  1939. caret.line = line1;
  1940. }
  1941. line1.alignment = line3.alignment;
  1942. line1.ascent = line3.ascent;
  1943. line1.hanging_indent = line3.hanging_indent;
  1944. line1.height = line3.height;
  1945. line1.indent = line3.indent;
  1946. line1.line_no = line3.line_no;
  1947. line1.recalc = line3.recalc;
  1948. line1.right_indent = line3.right_indent;
  1949. line1.ending = line3.ending;
  1950. line1.space = line3.space;
  1951. line1.tags = line3.tags;
  1952. line1.text = line3.text;
  1953. line1.widths = line3.widths;
  1954. line1.offset = line3.offset;
  1955. tag = line1.tags;
  1956. while (tag != null) {
  1957. tag.line = line1;
  1958. tag = tag.next;
  1959. }
  1960. }
  1961. if (line3.color == LineColor.Black)
  1962. RebalanceAfterDelete(line2);
  1963. this.lines--;
  1964. }
  1965. // Invalidate a section of the document to trigger redraw
  1966. internal void Invalidate(Line start, int start_pos, Line end, int end_pos) {
  1967. Line l1;
  1968. Line l2;
  1969. int p1;
  1970. int p2;
  1971. if ((start == end) && (start_pos == end_pos)) {
  1972. return;
  1973. }
  1974. if (end_pos == -1) {
  1975. end_pos = end.text.Length;
  1976. }
  1977. // figure out what's before what so the logic below is straightforward
  1978. if (start.line_no < end.line_no) {
  1979. l1 = start;
  1980. p1 = start_pos;
  1981. l2 = end;
  1982. p2 = end_pos;
  1983. } else if (start.line_no > end.line_no) {
  1984. l1 = end;
  1985. p1 = end_pos;
  1986. l2 = start;
  1987. p2 = start_pos;
  1988. } else {
  1989. if (start_pos < end_pos) {
  1990. l1 = start;
  1991. p1 = start_pos;
  1992. l2 = end;
  1993. p2 = end_pos;
  1994. } else {
  1995. l1 = end;
  1996. p1 = end_pos;
  1997. l2 = start;
  1998. p2 = start_pos;
  1999. }
  2000. int endpoint = (int) l1.widths [p2];
  2001. if (p2 == l1.text.Length + 1) {
  2002. endpoint = (int) viewport_width;
  2003. }
  2004. #if Debug
  2005. Console.WriteLine("Invaliding backwards from {0}:{1} to {2}:{3} {4}",
  2006. l1.line_no, p1, l2.line_no, p2,
  2007. new Rectangle(
  2008. (int)l1.widths[p1] + l1.X - viewport_x,
  2009. l1.Y - viewport_y,
  2010. (int)l1.widths[p2],
  2011. l1.height
  2012. )
  2013. );
  2014. #endif
  2015. owner.Invalidate(new Rectangle (
  2016. (int)l1.widths[p1] + l1.X - viewport_x,
  2017. l1.Y - viewport_y,
  2018. endpoint - (int)l1.widths[p1] + 1,
  2019. l1.height));
  2020. return;
  2021. }
  2022. #if Debug
  2023. Console.WriteLine("Invaliding from {0}:{1} to {2}:{3} Start => x={4}, y={5}, {6}x{7}", l1.line_no, p1, l2.line_no, p2, (int)l1.widths[p1] + l1.X - viewport_x, l1.Y - viewport_y, viewport_width, l1.height);
  2024. Console.WriteLine ("invalidate start line: {0} position: {1}", l1.text, p1);
  2025. #endif
  2026. // Three invalidates:
  2027. // First line from start
  2028. owner.Invalidate(new Rectangle((int)l1.widths[p1] + l1.X - viewport_x, l1.Y - viewport_y, viewport_width, l1.height));
  2029. // lines inbetween
  2030. if ((l1.line_no + 1) < l2.line_no) {
  2031. int y;
  2032. y = GetLine(l1.line_no + 1).Y;
  2033. owner.Invalidate(new Rectangle(0, y - viewport_y, viewport_width, l2.Y - y));
  2034. #if Debug
  2035. Console.WriteLine("Invaliding from {0}:{1} to {2}:{3} Middle => x={4}, y={5}, {6}x{7}", l1.line_no, p1, l2.line_no, p2, 0, y - viewport_y, viewport_width, l2.Y - y);
  2036. #endif
  2037. }
  2038. // Last line to end
  2039. owner.Invalidate(new Rectangle((int)l2.widths[0] + l2.X - viewport_x, l2.Y - viewport_y, (int)l2.widths[p2] + 1, l2.height));
  2040. #if Debug
  2041. Console.WriteLine("Invaliding from {0}:{1} to {2}:{3} End => x={4}, y={5}, {6}x{7}", l1.line_no, p1, l2.line_no, p2, (int)l2.widths[0] + l2.X - viewport_x, l2.Y - viewport_y, (int)l2.widths[p2] + 1, l2.height);
  2042. #endif
  2043. }
  2044. /// <summary>Select text around caret</summary>
  2045. internal void ExpandSelection(CaretSelection mode, bool to_caret) {
  2046. if (to_caret) {
  2047. // We're expanding the selection to the caret position
  2048. switch(mode) {
  2049. case CaretSelection.Line: {
  2050. // Invalidate the selection delta
  2051. if (caret > selection_prev) {
  2052. Invalidate(selection_prev.line, 0, caret.line, caret.line.text.Length);
  2053. } else {
  2054. Invalidate(selection_prev.line, selection_prev.line.text.Length, caret.line, 0);
  2055. }
  2056. if (caret.line.line_no <= selection_anchor.line.line_no) {
  2057. selection_start.line = caret.line;
  2058. selection_start.tag = caret.line.tags;
  2059. selection_start.pos = 0;
  2060. selection_end.line = selection_anchor.line;
  2061. selection_end.tag = selection_anchor.tag;
  2062. selection_end.pos = selection_anchor.pos;
  2063. selection_end_anchor = true;
  2064. } else {
  2065. selection_start.line = selection_anchor.line;
  2066. selection_start.pos = selection_anchor.height;
  2067. selection_start.tag = selection_anchor.line.FindTag(selection_anchor.height);
  2068. selection_end.line = caret.line;
  2069. selection_end.tag = caret.line.tags;
  2070. selection_end.pos = caret.line.text.Length;
  2071. selection_end_anchor = false;
  2072. }
  2073. selection_prev.line = caret.line;
  2074. selection_prev.tag = caret.tag;
  2075. selection_prev.pos = caret.pos;
  2076. break;
  2077. }
  2078. case CaretSelection.Word: {
  2079. int start_pos;
  2080. int end_pos;
  2081. start_pos = FindWordSeparator(caret.line, caret.pos, false);
  2082. end_pos = FindWordSeparator(caret.line, caret.pos, true);
  2083. // Invalidate the selection delta
  2084. if (caret > selection_prev) {
  2085. Invalidate(selection_prev.line, selection_prev.pos, caret.line, end_pos);
  2086. } else {
  2087. Invalidate(selection_prev.line, selection_prev.pos, caret.line, start_pos);
  2088. }
  2089. if (caret < selection_anchor) {
  2090. selection_start.line = caret.line;
  2091. selection_start.tag = caret.line.FindTag(start_pos);
  2092. selection_start.pos = start_pos;
  2093. selection_end.line = selection_anchor.line;
  2094. selection_end.tag = selection_anchor.tag;
  2095. selection_end.pos = selection_anchor.pos;
  2096. selection_prev.line = caret.line;
  2097. selection_prev.tag = caret.tag;
  2098. selection_prev.pos = start_pos;
  2099. selection_end_anchor = true;
  2100. } else {
  2101. selection_start.line = selection_anchor.line;
  2102. selection_start.pos = selection_anchor.height;
  2103. selection_start.tag = selection_anchor.line.FindTag(selection_anchor.height);
  2104. selection_end.line = caret.line;
  2105. selection_end.tag = caret.line.FindTag(end_pos);
  2106. selection_end.pos = end_pos;
  2107. selection_prev.line = caret.line;
  2108. selection_prev.tag = caret.tag;
  2109. selection_prev.pos = end_pos;
  2110. selection_end_anchor = false;
  2111. }
  2112. break;
  2113. }
  2114. case CaretSelection.Position: {
  2115. SetSelectionToCaret(false);
  2116. return;
  2117. }
  2118. }
  2119. } else {
  2120. // We're setting the selection 'around' the caret position
  2121. switch(mode) {
  2122. case CaretSelection.Line: {
  2123. this.Invalidate(caret.line, 0, caret.line, caret.line.text.Length);
  2124. selection_start.line = caret.line;
  2125. selection_start.tag = caret.line.tags;
  2126. selection_start.pos = 0;
  2127. selection_end.line = caret.line;
  2128. selection_end.pos = caret.line.text.Length;
  2129. selection_end.tag = caret.line.FindTag(selection_end.pos);
  2130. selection_anchor.line = selection_end.line;
  2131. selection_anchor.tag = selection_end.tag;
  2132. selection_anchor.pos = selection_end.pos;
  2133. selection_anchor.height = 0;
  2134. selection_prev.line = caret.line;
  2135. selection_prev.tag = caret.tag;
  2136. selection_prev.pos = caret.pos;
  2137. this.selection_end_anchor = true;
  2138. break;
  2139. }
  2140. case CaretSelection.Word: {
  2141. int start_pos;
  2142. int end_pos;
  2143. start_pos = FindWordSeparator(caret.line, caret.pos, false);
  2144. end_pos = FindWordSeparator(caret.line, caret.pos, true);
  2145. this.Invalidate(selection_start.line, start_pos, caret.line, end_pos);
  2146. selection_start.line = caret.line;
  2147. selection_start.tag = caret.line.FindTag(start_pos);
  2148. selection_start.pos = start_pos;
  2149. selection_end.line = caret.line;
  2150. selection_end.tag = caret.line.FindTag(end_pos);
  2151. selection_end.pos = end_pos;
  2152. selection_anchor.line = selection_end.line;
  2153. selection_anchor.tag = selection_end.tag;
  2154. selection_anchor.pos = selection_end.pos;
  2155. selection_anchor.height = start_pos;
  2156. selection_prev.line = caret.line;
  2157. selection_prev.tag = caret.tag;
  2158. selection_prev.pos = caret.pos;
  2159. this.selection_end_anchor = true;
  2160. break;
  2161. }
  2162. }
  2163. }
  2164. SetSelectionVisible (!(selection_start == selection_end));
  2165. }
  2166. internal void SetSelectionToCaret(bool start) {
  2167. if (start) {
  2168. // Invalidate old selection; selection is being reset to empty
  2169. this.Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  2170. selection_start.line = caret.line;
  2171. selection_start.tag = caret.tag;
  2172. selection_start.pos = caret.pos;
  2173. // start always also selects end
  2174. selection_end.line = caret.line;
  2175. selection_end.tag = caret.tag;
  2176. selection_end.pos = caret.pos;
  2177. selection_anchor.line = caret.line;
  2178. selection_anchor.tag = caret.tag;
  2179. selection_anchor.pos = caret.pos;
  2180. } else {
  2181. // Invalidate from previous end to caret (aka new end)
  2182. if (selection_end_anchor) {
  2183. if (selection_start != caret) {
  2184. this.Invalidate(selection_start.line, selection_start.pos, caret.line, caret.pos);
  2185. }
  2186. } else {
  2187. if (selection_end != caret) {
  2188. this.Invalidate(selection_end.line, selection_end.pos, caret.line, caret.pos);
  2189. }
  2190. }
  2191. if (caret < selection_anchor) {
  2192. selection_start.line = caret.line;
  2193. selection_start.tag = caret.tag;
  2194. selection_start.pos = caret.pos;
  2195. selection_end.line = selection_anchor.line;
  2196. selection_end.tag = selection_anchor.tag;
  2197. selection_end.pos = selection_anchor.pos;
  2198. selection_end_anchor = true;
  2199. } else {
  2200. selection_start.line = selection_anchor.line;
  2201. selection_start.tag = selection_anchor.tag;
  2202. selection_start.pos = selection_anchor.pos;
  2203. selection_end.line = caret.line;
  2204. selection_end.tag = caret.tag;
  2205. selection_end.pos = caret.pos;
  2206. selection_end_anchor = false;
  2207. }
  2208. }
  2209. SetSelectionVisible (!(selection_start == selection_end));
  2210. }
  2211. internal void SetSelection(Line start, int start_pos, Line end, int end_pos) {
  2212. if (selection_visible) {
  2213. Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  2214. }
  2215. if ((end.line_no < start.line_no) || ((end == start) && (end_pos <= start_pos))) {
  2216. selection_start.line = end;
  2217. selection_start.tag = LineTag.FindTag(end, end_pos);
  2218. selection_start.pos = end_pos;
  2219. selection_end.line = start;
  2220. selection_end.tag = LineTag.FindTag(start, start_pos);
  2221. selection_end.pos = start_pos;
  2222. selection_end_anchor = true;
  2223. } else {
  2224. selection_start.line = start;
  2225. selection_start.tag = LineTag.FindTag(start, start_pos);
  2226. selection_start.pos = start_pos;
  2227. selection_end.line = end;
  2228. selection_end.tag = LineTag.FindTag(end, end_pos);
  2229. selection_end.pos = end_pos;
  2230. selection_end_anchor = false;
  2231. }
  2232. selection_anchor.line = start;
  2233. selection_anchor.tag = selection_start.tag;
  2234. selection_anchor.pos = start_pos;
  2235. if (((start == end) && (start_pos == end_pos)) || start == null || end == null) {
  2236. SetSelectionVisible (false);
  2237. } else {
  2238. SetSelectionVisible (true);
  2239. Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  2240. }
  2241. }
  2242. internal void SetSelectionStart(Line start, int start_pos, bool invalidate) {
  2243. // Invalidate from the previous to the new start pos
  2244. if (invalidate)
  2245. Invalidate(selection_start.line, selection_start.pos, start, start_pos);
  2246. selection_start.line = start;
  2247. selection_start.pos = start_pos;
  2248. selection_start.tag = LineTag.FindTag(start, start_pos);
  2249. selection_anchor.line = start;
  2250. selection_anchor.pos = start_pos;
  2251. selection_anchor.tag = selection_start.tag;
  2252. selection_end_anchor = false;
  2253. if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) {
  2254. SetSelectionVisible (true);
  2255. } else {
  2256. SetSelectionVisible (false);
  2257. }
  2258. if (invalidate)
  2259. Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  2260. }
  2261. internal void SetSelectionStart(int character_index, bool invalidate) {
  2262. Line line;
  2263. LineTag tag;
  2264. int pos;
  2265. if (character_index < 0) {
  2266. return;
  2267. }
  2268. CharIndexToLineTag(character_index, out line, out tag, out pos);
  2269. SetSelectionStart(line, pos, invalidate);
  2270. }
  2271. internal void SetSelectionEnd(Line end, int end_pos, bool invalidate) {
  2272. if (end == selection_end.line && end_pos == selection_start.pos) {
  2273. selection_anchor.line = selection_start.line;
  2274. selection_anchor.tag = selection_start.tag;
  2275. selection_anchor.pos = selection_start.pos;
  2276. selection_end.line = selection_start.line;
  2277. selection_end.tag = selection_start.tag;
  2278. selection_end.pos = selection_start.pos;
  2279. selection_end_anchor = false;
  2280. } else if ((end.line_no < selection_anchor.line.line_no) || ((end == selection_anchor.line) && (end_pos <= selection_anchor.pos))) {
  2281. selection_start.line = end;
  2282. selection_start.tag = LineTag.FindTag(end, end_pos);
  2283. selection_start.pos = end_pos;
  2284. selection_end.line = selection_anchor.line;
  2285. selection_end.tag = selection_anchor.tag;
  2286. selection_end.pos = selection_anchor.pos;
  2287. selection_end_anchor = true;
  2288. } else {
  2289. selection_start.line = selection_anchor.line;
  2290. selection_start.tag = selection_anchor.tag;
  2291. selection_start.pos = selection_anchor.pos;
  2292. selection_end.line = end;
  2293. selection_end.tag = LineTag.FindTag(end, end_pos);
  2294. selection_end.pos = end_pos;
  2295. selection_end_anchor = false;
  2296. }
  2297. if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) {
  2298. SetSelectionVisible (true);
  2299. if (invalidate)
  2300. Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  2301. } else {
  2302. SetSelectionVisible (false);
  2303. // ?? Do I need to invalidate here, tests seem to work without it, but I don't think they should :-s
  2304. }
  2305. }
  2306. internal void SetSelectionEnd(int character_index, bool invalidate) {
  2307. Line line;
  2308. LineTag tag;
  2309. int pos;
  2310. if (character_index < 0) {
  2311. return;
  2312. }
  2313. CharIndexToLineTag(character_index, out line, out tag, out pos);
  2314. SetSelectionEnd(line, pos, invalidate);
  2315. }
  2316. internal void SetSelection(Line start, int start_pos) {
  2317. if (selection_visible) {
  2318. Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  2319. }
  2320. selection_start.line = start;
  2321. selection_start.pos = start_pos;
  2322. selection_start.tag = LineTag.FindTag(start, start_pos);
  2323. selection_end.line = start;
  2324. selection_end.tag = selection_start.tag;
  2325. selection_end.pos = start_pos;
  2326. selection_anchor.line = start;
  2327. selection_anchor.tag = selection_start.tag;
  2328. selection_anchor.pos = start_pos;
  2329. selection_end_anchor = false;
  2330. SetSelectionVisible (false);
  2331. }
  2332. internal void InvalidateSelectionArea() {
  2333. Invalidate (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  2334. }
  2335. // Return the current selection, as string
  2336. internal string GetSelection() {
  2337. // We return String.Empty if there is no selection
  2338. if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) {
  2339. return string.Empty;
  2340. }
  2341. if (selection_start.line == selection_end.line) {
  2342. return selection_start.line.text.ToString (selection_start.pos, selection_end.pos - selection_start.pos);
  2343. } else {
  2344. StringBuilder sb;
  2345. int i;
  2346. int start;
  2347. int end;
  2348. sb = new StringBuilder();
  2349. start = selection_start.line.line_no;
  2350. end = selection_end.line.line_no;
  2351. sb.Append(selection_start.line.text.ToString(selection_start.pos, selection_start.line.text.Length - selection_start.pos) + Environment.NewLine);
  2352. if ((start + 1) < end) {
  2353. for (i = start + 1; i < end; i++) {
  2354. sb.Append(GetLine(i).text.ToString() + Environment.NewLine);
  2355. }
  2356. }
  2357. sb.Append(selection_end.line.text.ToString(0, selection_end.pos));
  2358. return sb.ToString();
  2359. }
  2360. }
  2361. internal void ReplaceSelection(string s, bool select_new) {
  2362. int i;
  2363. int selection_pos_on_line = selection_start.pos;
  2364. int selection_start_pos = LineTagToCharIndex (selection_start.line, selection_start.pos);
  2365. SuspendRecalc ();
  2366. // First, delete any selected text
  2367. if ((selection_start.pos != selection_end.pos) || (selection_start.line != selection_end.line)) {
  2368. if (selection_start.line == selection_end.line) {
  2369. undo.RecordDeleteString (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  2370. DeleteChars(selection_start.tag, selection_start.pos, selection_end.pos - selection_start.pos);
  2371. // The tag might have been removed, we need to recalc it
  2372. selection_start.tag = selection_start.line.FindTag(selection_start.pos);
  2373. } else {
  2374. int start;
  2375. int end;
  2376. start = selection_start.line.line_no;
  2377. end = selection_end.line.line_no;
  2378. undo.RecordDeleteString (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  2379. InvalidateSelectionArea ();
  2380. // Delete first line
  2381. DeleteChars(selection_start.tag, selection_start.pos, selection_start.line.text.Length - selection_start.pos);
  2382. selection_start.line.recalc = true;
  2383. // Delete last line
  2384. DeleteChars(selection_end.line.tags, 0, selection_end.pos);
  2385. start++;
  2386. if (start < end) {
  2387. for (i = end - 1; i >= start; i--) {
  2388. Delete(i);
  2389. }
  2390. }
  2391. // BIG FAT WARNING - selection_end.line might be stale due
  2392. // to the above Delete() call. DONT USE IT before hitting the end of this method!
  2393. // Join start and end
  2394. Combine(selection_start.line.line_no, start);
  2395. }
  2396. }
  2397. Insert(selection_start.line, selection_start.pos, false, s);
  2398. undo.RecordInsertString (selection_start.line, selection_start.pos, s);
  2399. ResumeRecalc (false);
  2400. if (!select_new) {
  2401. CharIndexToLineTag(selection_start_pos + s.Length, out selection_start.line,
  2402. out selection_start.tag, out selection_start.pos);
  2403. selection_end.line = selection_start.line;
  2404. selection_end.pos = selection_start.pos;
  2405. selection_end.tag = selection_start.tag;
  2406. selection_anchor.line = selection_start.line;
  2407. selection_anchor.pos = selection_start.pos;
  2408. selection_anchor.tag = selection_start.tag;
  2409. SetSelectionVisible (false);
  2410. } else {
  2411. CharIndexToLineTag(selection_start_pos, out selection_start.line,
  2412. out selection_start.tag, out selection_start.pos);
  2413. CharIndexToLineTag(selection_start_pos + s.Length, out selection_end.line,
  2414. out selection_end.tag, out selection_end.pos);
  2415. selection_anchor.line = selection_start.line;
  2416. selection_anchor.pos = selection_start.pos;
  2417. selection_anchor.tag = selection_start.tag;
  2418. SetSelectionVisible (true);
  2419. }
  2420. PositionCaret (selection_start.line, selection_start.pos);
  2421. UpdateView (selection_start.line, selection_pos_on_line);
  2422. }
  2423. internal void CharIndexToLineTag(int index, out Line line_out, out LineTag tag_out, out int pos) {
  2424. Line line;
  2425. LineTag tag;
  2426. int i;
  2427. int chars;
  2428. int start;
  2429. chars = 0;
  2430. for (i = 1; i <= lines; i++) {
  2431. line = GetLine(i);
  2432. start = chars;
  2433. chars += line.text.Length;
  2434. if (index <= chars) {
  2435. // we found the line
  2436. tag = line.tags;
  2437. while (tag != null) {
  2438. if (index < (start + tag.start + tag.Length - 1)) {
  2439. line_out = line;
  2440. tag_out = LineTag.GetFinalTag (tag);
  2441. pos = index - start;
  2442. return;
  2443. }
  2444. if (tag.next == null) {
  2445. Line next_line;
  2446. next_line = GetLine(line.line_no + 1);
  2447. if (next_line != null) {
  2448. line_out = next_line;
  2449. tag_out = LineTag.GetFinalTag (next_line.tags);
  2450. pos = 0;
  2451. return;
  2452. } else {
  2453. line_out = line;
  2454. tag_out = LineTag.GetFinalTag (tag);
  2455. pos = line_out.text.Length;
  2456. return;
  2457. }
  2458. }
  2459. tag = tag.next;
  2460. }
  2461. }
  2462. }
  2463. line_out = GetLine(lines);
  2464. tag = line_out.tags;
  2465. while (tag.next != null) {
  2466. tag = tag.next;
  2467. }
  2468. tag_out = tag;
  2469. pos = line_out.text.Length;
  2470. }
  2471. internal int LineTagToCharIndex(Line line, int pos) {
  2472. int i;
  2473. int length;
  2474. // Count first and last line
  2475. length = 0;
  2476. // Count the lines in the middle
  2477. for (i = 1; i < line.line_no; i++) {
  2478. length += GetLine(i).text.Length;
  2479. }
  2480. length += pos;
  2481. return length;
  2482. }
  2483. internal int SelectionLength() {
  2484. if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) {
  2485. return 0;
  2486. }
  2487. if (selection_start.line == selection_end.line) {
  2488. return selection_end.pos - selection_start.pos;
  2489. } else {
  2490. int i;
  2491. int start;
  2492. int end;
  2493. int length;
  2494. // Count first and last line
  2495. length = selection_start.line.text.Length - selection_start.pos + selection_end.pos + crlf_size;
  2496. // Count the lines in the middle
  2497. start = selection_start.line.line_no + 1;
  2498. end = selection_end.line.line_no;
  2499. if (start < end) {
  2500. for (i = start; i < end; i++) {
  2501. Line line = GetLine (i);
  2502. length += line.text.Length + LineEndingLength (line.ending);
  2503. }
  2504. }
  2505. return length;
  2506. }
  2507. }
  2508. /// <summary>Give it a Line number and it returns the Line object at with that line number</summary>
  2509. internal Line GetLine(int LineNo) {
  2510. Line line = document;
  2511. while (line != sentinel) {
  2512. if (LineNo == line.line_no) {
  2513. return line;
  2514. } else if (LineNo < line.line_no) {
  2515. line = line.left;
  2516. } else {
  2517. line = line.right;
  2518. }
  2519. }
  2520. return null;
  2521. }
  2522. /// <summary>Retrieve the previous tag; walks line boundaries</summary>
  2523. internal LineTag PreviousTag(LineTag tag) {
  2524. Line l;
  2525. if (tag.previous != null) {
  2526. return tag.previous;
  2527. }
  2528. // Next line
  2529. if (tag.line.line_no == 1) {
  2530. return null;
  2531. }
  2532. l = GetLine(tag.line.line_no - 1);
  2533. if (l != null) {
  2534. LineTag t;
  2535. t = l.tags;
  2536. while (t.next != null) {
  2537. t = t.next;
  2538. }
  2539. return t;
  2540. }
  2541. return null;
  2542. }
  2543. /// <summary>Retrieve the next tag; walks line boundaries</summary>
  2544. internal LineTag NextTag(LineTag tag) {
  2545. Line l;
  2546. if (tag.next != null) {
  2547. return tag.next;
  2548. }
  2549. // Next line
  2550. l = GetLine(tag.line.line_no + 1);
  2551. if (l != null) {
  2552. return l.tags;
  2553. }
  2554. return null;
  2555. }
  2556. internal Line ParagraphStart(Line line) {
  2557. while (line.ending == LineEnding.Wrap) {
  2558. line = GetLine(line.line_no - 1);
  2559. }
  2560. return line;
  2561. }
  2562. internal Line ParagraphEnd(Line line) {
  2563. Line l;
  2564. while (line.ending == LineEnding.Wrap) {
  2565. l = GetLine(line.line_no + 1);
  2566. if ((l == null) || (l.ending != LineEnding.Wrap)) {
  2567. break;
  2568. }
  2569. line = l;
  2570. }
  2571. return line;
  2572. }
  2573. /// <summary>Give it a pixel offset coordinate and it returns the Line covering that are (offset
  2574. /// is either X or Y depending on if we are multiline
  2575. /// </summary>
  2576. internal Line GetLineByPixel (int offset, bool exact)
  2577. {
  2578. Line line = document;
  2579. Line last = null;
  2580. if (multiline) {
  2581. while (line != sentinel) {
  2582. last = line;
  2583. if ((offset >= line.Y) && (offset < (line.Y+line.height))) {
  2584. return line;
  2585. } else if (offset < line.Y) {
  2586. line = line.left;
  2587. } else {
  2588. line = line.right;
  2589. }
  2590. }
  2591. } else {
  2592. while (line != sentinel) {
  2593. last = line;
  2594. if ((offset >= line.X) && (offset < (line.X + line.Width)))
  2595. return line;
  2596. else if (offset < line.X)
  2597. line = line.left;
  2598. else
  2599. line = line.right;
  2600. }
  2601. }
  2602. if (exact) {
  2603. return null;
  2604. }
  2605. return last;
  2606. }
  2607. // Give it x/y pixel coordinates and it returns the Tag at that position; optionally the char position is returned in index
  2608. internal LineTag FindTag(int x, int y, out int index, bool exact) {
  2609. Line line;
  2610. LineTag tag;
  2611. line = GetLineByPixel(y, exact);
  2612. if (line == null) {
  2613. index = 0;
  2614. return null;
  2615. }
  2616. tag = line.tags;
  2617. // Alignment adjustment
  2618. x += line.X;
  2619. while (true) {
  2620. if (x >= tag.X && x < (tag.X+tag.Width)) {
  2621. int end;
  2622. end = tag.start + tag.Length - 1;
  2623. for (int pos = tag.start; pos < end; pos++) {
  2624. if (x < line.widths[pos]) {
  2625. index = pos;
  2626. return LineTag.GetFinalTag (tag);
  2627. }
  2628. }
  2629. index=end;
  2630. return LineTag.GetFinalTag (tag);
  2631. }
  2632. if (tag.next != null) {
  2633. tag = tag.next;
  2634. } else {
  2635. if (exact) {
  2636. index = 0;
  2637. return null;
  2638. }
  2639. index = line.text.Length;
  2640. return LineTag.GetFinalTag (tag);
  2641. }
  2642. }
  2643. }
  2644. // Give it x/y pixel coordinates and it returns the Tag at that position; optionally the char position is returned in index
  2645. internal LineTag FindCursor(int x, int y, out int index) {
  2646. Line line;
  2647. LineTag tag;
  2648. line = GetLineByPixel(multiline ? y : x, false);
  2649. tag = line.tags;
  2650. /// Special case going leftwards of the first tag
  2651. if (x < tag.X) {
  2652. index = 0;
  2653. return LineTag.GetFinalTag (tag);
  2654. }
  2655. while (true) {
  2656. if (x >= tag.X && x < (tag.X+tag.Width)) {
  2657. int end;
  2658. end = tag.TextEnd;
  2659. for (int pos = tag.start - 1; pos < end; pos++) {
  2660. // When clicking on a character, we position the cursor to whatever edge
  2661. // of the character the click was closer
  2662. if (x < (line.X + line.widths[pos] + ((line.widths[pos+1]-line.widths[pos])/2))) {
  2663. index = pos;
  2664. return LineTag.GetFinalTag (tag);
  2665. }
  2666. }
  2667. index=end;
  2668. return LineTag.GetFinalTag (tag);
  2669. }
  2670. if (tag.next != null) {
  2671. tag = tag.next;
  2672. } else {
  2673. index = line.TextLengthWithoutEnding ();
  2674. return LineTag.GetFinalTag (tag);
  2675. }
  2676. }
  2677. }
  2678. /// <summary>Format area of document in specified font and color</summary>
  2679. /// <param name="start_pos">1-based start position on start_line</param>
  2680. /// <param name="end_pos">1-based end position on end_line </param>
  2681. internal void FormatText (Line start_line, int start_pos, Line end_line, int end_pos, Font font,
  2682. SolidBrush color, Color back_color, FormatSpecified specified)
  2683. {
  2684. Line l;
  2685. // First, format the first line
  2686. if (start_line != end_line) {
  2687. // First line
  2688. LineTag.FormatText(start_line, start_pos, start_line.text.Length - start_pos + 1, font, color, back_color, specified);
  2689. // Format last line
  2690. LineTag.FormatText(end_line, 1, end_pos, font, color, back_color, specified);
  2691. // Now all the lines inbetween
  2692. for (int i = start_line.line_no + 1; i < end_line.line_no; i++) {
  2693. l = GetLine(i);
  2694. LineTag.FormatText(l, 1, l.text.Length, font, color, back_color, specified);
  2695. }
  2696. } else {
  2697. // Special case, single line
  2698. LineTag.FormatText(start_line, start_pos, end_pos - start_pos, font, color, back_color, specified);
  2699. }
  2700. }
  2701. internal void RecalculateAlignments ()
  2702. {
  2703. Line line;
  2704. int line_no;
  2705. line_no = 1;
  2706. while (line_no <= lines) {
  2707. line = GetLine(line_no);
  2708. if (line != null) {
  2709. switch (line.alignment) {
  2710. case HorizontalAlignment.Left:
  2711. line.align_shift = 0;
  2712. break;
  2713. case HorizontalAlignment.Center:
  2714. line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
  2715. break;
  2716. case HorizontalAlignment.Right:
  2717. line.align_shift = viewport_width - (int)line.widths[line.text.Length] - right_margin;
  2718. break;
  2719. }
  2720. }
  2721. line_no++;
  2722. }
  2723. return;
  2724. }
  2725. /// <summary>Calculate formatting for the whole document</summary>
  2726. internal bool RecalculateDocument(Graphics g) {
  2727. return RecalculateDocument(g, 1, this.lines, false);
  2728. }
  2729. /// <summary>Calculate formatting starting at a certain line</summary>
  2730. internal bool RecalculateDocument(Graphics g, int start) {
  2731. return RecalculateDocument(g, start, this.lines, false);
  2732. }
  2733. /// <summary>Calculate formatting within two given line numbers</summary>
  2734. internal bool RecalculateDocument(Graphics g, int start, int end) {
  2735. return RecalculateDocument(g, start, end, false);
  2736. }
  2737. /// <summary>With optimize on, returns true if line heights changed</summary>
  2738. internal bool RecalculateDocument(Graphics g, int start, int end, bool optimize) {
  2739. Line line;
  2740. int line_no;
  2741. int offset;
  2742. int new_width;
  2743. bool changed;
  2744. int shift;
  2745. if (recalc_suspended > 0) {
  2746. recalc_pending = true;
  2747. recalc_start = Math.Min (recalc_start, start);
  2748. recalc_end = Math.Max (recalc_end, end);
  2749. recalc_optimize = optimize;
  2750. return false;
  2751. }
  2752. // Fixup the positions, they can go kinda nuts
  2753. start = Math.Max (start, 1);
  2754. end = Math.Min (end, lines);
  2755. offset = GetLine(start).offset;
  2756. line_no = start;
  2757. new_width = 0;
  2758. shift = this.lines;
  2759. if (!optimize) {
  2760. changed = true; // We always return true if we run non-optimized
  2761. } else {
  2762. changed = false;
  2763. }
  2764. while (line_no <= (end + this.lines - shift)) {
  2765. line = GetLine(line_no++);
  2766. line.offset = offset;
  2767. if (!calc_pass) {
  2768. if (!optimize) {
  2769. line.RecalculateLine(g, this);
  2770. } else {
  2771. if (line.recalc && line.RecalculateLine(g, this)) {
  2772. changed = true;
  2773. // If the height changed, all subsequent lines change
  2774. end = this.lines;
  2775. shift = this.lines;
  2776. }
  2777. }
  2778. } else {
  2779. if (!optimize) {
  2780. line.RecalculatePasswordLine(g, this);
  2781. } else {
  2782. if (line.recalc && line.RecalculatePasswordLine(g, this)) {
  2783. changed = true;
  2784. // If the height changed, all subsequent lines change
  2785. end = this.lines;
  2786. shift = this.lines;
  2787. }
  2788. }
  2789. }
  2790. if (line.widths[line.text.Length] > new_width) {
  2791. new_width = (int)line.widths[line.text.Length];
  2792. }
  2793. // Calculate alignment
  2794. if (line.alignment != HorizontalAlignment.Left) {
  2795. if (line.alignment == HorizontalAlignment.Center) {
  2796. line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
  2797. } else {
  2798. line.align_shift = viewport_width - (int)line.widths[line.text.Length] - 1;
  2799. }
  2800. }
  2801. if (multiline)
  2802. offset += line.height;
  2803. else
  2804. offset += (int) line.widths [line.text.Length];
  2805. if (line_no > lines) {
  2806. break;
  2807. }
  2808. }
  2809. if (document_x != new_width) {
  2810. document_x = new_width;
  2811. if (WidthChanged != null) {
  2812. WidthChanged(this, null);
  2813. }
  2814. }
  2815. RecalculateAlignments();
  2816. line = GetLine(lines);
  2817. if (document_y != line.Y + line.height) {
  2818. document_y = line.Y + line.height;
  2819. if (HeightChanged != null) {
  2820. HeightChanged(this, null);
  2821. }
  2822. }
  2823. UpdateCaret();
  2824. return changed;
  2825. }
  2826. internal int Size() {
  2827. return lines;
  2828. }
  2829. private void owner_HandleCreated(object sender, EventArgs e) {
  2830. RecalculateDocument(owner.CreateGraphicsInternal());
  2831. AlignCaret();
  2832. }
  2833. private void owner_VisibleChanged(object sender, EventArgs e) {
  2834. if (owner.Visible) {
  2835. RecalculateDocument(owner.CreateGraphicsInternal());
  2836. }
  2837. }
  2838. internal static bool IsWordSeparator (char ch)
  2839. {
  2840. switch (ch) {
  2841. case ' ':
  2842. case '\t':
  2843. case '(':
  2844. case ')':
  2845. case '\r':
  2846. case '\n':
  2847. return true;
  2848. default:
  2849. return false;
  2850. }
  2851. }
  2852. internal int FindWordSeparator(Line line, int pos, bool forward) {
  2853. int len;
  2854. len = line.text.Length;
  2855. if (forward) {
  2856. for (int i = pos + 1; i < len; i++) {
  2857. if (IsWordSeparator(line.Text[i])) {
  2858. return i + 1;
  2859. }
  2860. }
  2861. return len;
  2862. } else {
  2863. for (int i = pos - 1; i > 0; i--) {
  2864. if (IsWordSeparator(line.Text[i - 1])) {
  2865. return i;
  2866. }
  2867. }
  2868. return 0;
  2869. }
  2870. }
  2871. /* Search document for text */
  2872. internal bool FindChars(char[] chars, Marker start, Marker end, out Marker result) {
  2873. Line line;
  2874. int line_no;
  2875. int pos;
  2876. int line_len;
  2877. // Search for occurence of any char in the chars array
  2878. result = new Marker();
  2879. line = start.line;
  2880. line_no = start.line.line_no;
  2881. pos = start.pos;
  2882. while (line_no <= end.line.line_no) {
  2883. line_len = line.text.Length;
  2884. while (pos < line_len) {
  2885. for (int i = 0; i < chars.Length; i++) {
  2886. if (line.text[pos] == chars[i]) {
  2887. // Special case
  2888. if ((line.line_no == end.line.line_no) && (pos >= end.pos)) {
  2889. return false;
  2890. }
  2891. result.line = line;
  2892. result.pos = pos;
  2893. return true;
  2894. }
  2895. }
  2896. pos++;
  2897. }
  2898. pos = 0;
  2899. line_no++;
  2900. line = GetLine(line_no);
  2901. }
  2902. return false;
  2903. }
  2904. // This version does not build one big string for searching, instead it handles
  2905. // line-boundaries, which is faster and less memory intensive
  2906. // FIXME - Depending on culture stuff we might have to create a big string and use culturespecific
  2907. // search stuff and change it to accept and return positions instead of Markers (which would match
  2908. // RichTextBox behaviour better but would be inconsistent with the rest of TextControl)
  2909. internal bool Find(string search, Marker start, Marker end, out Marker result, RichTextBoxFinds options) {
  2910. Marker last;
  2911. string search_string;
  2912. Line line;
  2913. int line_no;
  2914. int pos;
  2915. int line_len;
  2916. int current;
  2917. bool word;
  2918. bool word_option;
  2919. bool ignore_case;
  2920. bool reverse;
  2921. char c;
  2922. result = new Marker();
  2923. word_option = ((options & RichTextBoxFinds.WholeWord) != 0);
  2924. ignore_case = ((options & RichTextBoxFinds.MatchCase) == 0);
  2925. reverse = ((options & RichTextBoxFinds.Reverse) != 0);
  2926. line = start.line;
  2927. line_no = start.line.line_no;
  2928. pos = start.pos;
  2929. current = 0;
  2930. // Prep our search string, lowercasing it if we do case-independent matching
  2931. if (ignore_case) {
  2932. StringBuilder sb;
  2933. sb = new StringBuilder(search);
  2934. for (int i = 0; i < sb.Length; i++) {
  2935. sb[i] = Char.ToLower(sb[i]);
  2936. }
  2937. search_string = sb.ToString();
  2938. } else {
  2939. search_string = search;
  2940. }
  2941. // We need to check if the character before our start position is a wordbreak
  2942. if (word_option) {
  2943. if (line_no == 1) {
  2944. if ((pos == 0) || (IsWordSeparator(line.text[pos - 1]))) {
  2945. word = true;
  2946. } else {
  2947. word = false;
  2948. }
  2949. } else {
  2950. if (pos > 0) {
  2951. if (IsWordSeparator(line.text[pos - 1])) {
  2952. word = true;
  2953. } else {
  2954. word = false;
  2955. }
  2956. } else {
  2957. // Need to check the end of the previous line
  2958. Line prev_line;
  2959. prev_line = GetLine(line_no - 1);
  2960. if (prev_line.ending == LineEnding.Wrap) {
  2961. if (IsWordSeparator(prev_line.text[prev_line.text.Length - 1])) {
  2962. word = true;
  2963. } else {
  2964. word = false;
  2965. }
  2966. } else {
  2967. word = true;
  2968. }
  2969. }
  2970. }
  2971. } else {
  2972. word = false;
  2973. }
  2974. // To avoid duplication of this loop with reverse logic, we search
  2975. // through the document, remembering the last match and when returning
  2976. // report that last remembered match
  2977. last = new Marker();
  2978. last.height = -1; // Abused - we use it to track change
  2979. while (line_no <= end.line.line_no) {
  2980. if (line_no != end.line.line_no) {
  2981. line_len = line.text.Length;
  2982. } else {
  2983. line_len = end.pos;
  2984. }
  2985. while (pos < line_len) {
  2986. if (word_option && (current == search_string.Length)) {
  2987. if (IsWordSeparator(line.text[pos])) {
  2988. if (!reverse) {
  2989. goto FindFound;
  2990. } else {
  2991. last = result;
  2992. current = 0;
  2993. }
  2994. } else {
  2995. current = 0;
  2996. }
  2997. }
  2998. if (ignore_case) {
  2999. c = Char.ToLower(line.text[pos]);
  3000. } else {
  3001. c = line.text[pos];
  3002. }
  3003. if (c == search_string[current]) {
  3004. if (current == 0) {
  3005. result.line = line;
  3006. result.pos = pos;
  3007. }
  3008. if (!word_option || (word_option && (word || (current > 0)))) {
  3009. current++;
  3010. }
  3011. if (!word_option && (current == search_string.Length)) {
  3012. if (!reverse) {
  3013. goto FindFound;
  3014. } else {
  3015. last = result;
  3016. current = 0;
  3017. }
  3018. }
  3019. } else {
  3020. current = 0;
  3021. }
  3022. pos++;
  3023. if (!word_option) {
  3024. continue;
  3025. }
  3026. if (IsWordSeparator(c)) {
  3027. word = true;
  3028. } else {
  3029. word = false;
  3030. }
  3031. }
  3032. if (word_option) {
  3033. // Mark that we just saw a word boundary
  3034. if (line.ending != LineEnding.Wrap || line.line_no == lines - 1) {
  3035. word = true;
  3036. }
  3037. if (current == search_string.Length) {
  3038. if (word) {
  3039. if (!reverse) {
  3040. goto FindFound;
  3041. } else {
  3042. last = result;
  3043. current = 0;
  3044. }
  3045. } else {
  3046. current = 0;
  3047. }
  3048. }
  3049. }
  3050. pos = 0;
  3051. line_no++;
  3052. line = GetLine(line_no);
  3053. }
  3054. if (reverse) {
  3055. if (last.height != -1) {
  3056. result = last;
  3057. return true;
  3058. }
  3059. }
  3060. return false;
  3061. FindFound:
  3062. if (!reverse) {
  3063. // if ((line.line_no == end.line.line_no) && (pos >= end.pos)) {
  3064. // return false;
  3065. // }
  3066. return true;
  3067. }
  3068. result = last;
  3069. return true;
  3070. }
  3071. /* Marker stuff */
  3072. internal void GetMarker(out Marker mark, bool start) {
  3073. mark = new Marker();
  3074. if (start) {
  3075. mark.line = GetLine(1);
  3076. mark.tag = mark.line.tags;
  3077. mark.pos = 0;
  3078. } else {
  3079. mark.line = GetLine(lines);
  3080. mark.tag = mark.line.tags;
  3081. while (mark.tag.next != null) {
  3082. mark.tag = mark.tag.next;
  3083. }
  3084. mark.pos = mark.line.text.Length;
  3085. }
  3086. }
  3087. #endregion // Internal Methods
  3088. #region Events
  3089. internal event EventHandler CaretMoved;
  3090. internal event EventHandler WidthChanged;
  3091. internal event EventHandler HeightChanged;
  3092. internal event EventHandler LengthChanged;
  3093. #endregion // Events
  3094. #region Administrative
  3095. public IEnumerator GetEnumerator() {
  3096. // FIXME
  3097. return null;
  3098. }
  3099. public override bool Equals(object obj) {
  3100. if (obj == null) {
  3101. return false;
  3102. }
  3103. if (!(obj is Document)) {
  3104. return false;
  3105. }
  3106. if (obj == this) {
  3107. return true;
  3108. }
  3109. if (ToString().Equals(((Document)obj).ToString())) {
  3110. return true;
  3111. }
  3112. return false;
  3113. }
  3114. public override int GetHashCode() {
  3115. return document_id;
  3116. }
  3117. public override string ToString() {
  3118. return "document " + this.document_id;
  3119. }
  3120. #endregion // Administrative
  3121. }
  3122. internal class PictureTag : LineTag {
  3123. internal RTF.Picture picture;
  3124. internal PictureTag (Line line, int start, RTF.Picture picture) : base (line, start)
  3125. {
  3126. this.picture = picture;
  3127. }
  3128. public override bool IsTextTag {
  3129. get { return false; }
  3130. }
  3131. internal override SizeF SizeOfPosition (Graphics dc, int pos)
  3132. {
  3133. return picture.Size;
  3134. }
  3135. internal override int MaxHeight ()
  3136. {
  3137. return (int) (picture.Height + 0.5F);
  3138. }
  3139. internal override void Draw (Graphics dc, Brush brush, float xoff, float y, int start, int end)
  3140. {
  3141. picture.DrawImage (dc, xoff + line.widths [start], y, false);
  3142. }
  3143. internal override void Draw (Graphics dc, Brush brush, float xoff, float y, int start, int end, string text)
  3144. {
  3145. picture.DrawImage (dc, xoff + + line.widths [start], y, false);
  3146. }
  3147. public override string Text ()
  3148. {
  3149. return "I";
  3150. }
  3151. }
  3152. internal class UndoManager {
  3153. internal enum ActionType {
  3154. Typing,
  3155. // This is basically just cut & paste
  3156. InsertString,
  3157. DeleteString,
  3158. UserActionBegin,
  3159. UserActionEnd
  3160. }
  3161. internal class Action {
  3162. internal ActionType type;
  3163. internal int line_no;
  3164. internal int pos;
  3165. internal object data;
  3166. }
  3167. #region Local Variables
  3168. private Document document;
  3169. private Stack undo_actions;
  3170. private Stack redo_actions;
  3171. //private int caret_line;
  3172. //private int caret_pos;
  3173. // When performing an action, we lock the queue, so that the action can't be undone
  3174. private bool locked;
  3175. #endregion // Local Variables
  3176. #region Constructors
  3177. internal UndoManager (Document document)
  3178. {
  3179. this.document = document;
  3180. undo_actions = new Stack (50);
  3181. redo_actions = new Stack (50);
  3182. }
  3183. #endregion // Constructors
  3184. #region Properties
  3185. internal bool CanUndo {
  3186. get { return undo_actions.Count > 0; }
  3187. }
  3188. internal bool CanRedo {
  3189. get { return redo_actions.Count > 0; }
  3190. }
  3191. internal string UndoActionName {
  3192. get {
  3193. foreach (Action action in undo_actions) {
  3194. if (action.type == ActionType.UserActionBegin)
  3195. return (string) action.data;
  3196. if (action.type == ActionType.Typing)
  3197. return Locale.GetText ("Typing");
  3198. }
  3199. return String.Empty;
  3200. }
  3201. }
  3202. internal string RedoActionName {
  3203. get {
  3204. foreach (Action action in redo_actions) {
  3205. if (action.type == ActionType.UserActionBegin)
  3206. return (string) action.data;
  3207. if (action.type == ActionType.Typing)
  3208. return Locale.GetText ("Typing");
  3209. }
  3210. return String.Empty;
  3211. }
  3212. }
  3213. #endregion // Properties
  3214. #region Internal Methods
  3215. internal void Clear ()
  3216. {
  3217. undo_actions.Clear();
  3218. redo_actions.Clear();
  3219. }
  3220. internal void Undo ()
  3221. {
  3222. Action action;
  3223. bool user_action_finished = false;
  3224. if (undo_actions.Count == 0)
  3225. return;
  3226. // Nuke the redo queue
  3227. redo_actions.Clear ();
  3228. locked = true;
  3229. do {
  3230. Line start;
  3231. action = (Action) undo_actions.Pop ();
  3232. // Put onto redo stack
  3233. redo_actions.Push(action);
  3234. // Do the thing
  3235. switch(action.type) {
  3236. case ActionType.UserActionBegin:
  3237. user_action_finished = true;
  3238. break;
  3239. case ActionType.UserActionEnd:
  3240. // noop
  3241. break;
  3242. case ActionType.InsertString:
  3243. start = document.GetLine (action.line_no);
  3244. document.SuspendUpdate ();
  3245. document.DeleteMultiline (start, action.pos, ((string) action.data).Length + 1);
  3246. document.PositionCaret (start, action.pos);
  3247. document.SetSelectionToCaret (true);
  3248. document.ResumeUpdate (true);
  3249. break;
  3250. case ActionType.Typing:
  3251. start = document.GetLine (action.line_no);
  3252. document.SuspendUpdate ();
  3253. document.DeleteMultiline (start, action.pos, ((StringBuilder) action.data).Length);
  3254. document.PositionCaret (start, action.pos);
  3255. document.SetSelectionToCaret (true);
  3256. document.ResumeUpdate (true);
  3257. // This is an open ended operation, so only a single typing operation can be undone at once
  3258. user_action_finished = true;
  3259. break;
  3260. case ActionType.DeleteString:
  3261. start = document.GetLine (action.line_no);
  3262. document.SuspendUpdate ();
  3263. Insert (start, action.pos, (Line) action.data, true);
  3264. document.ResumeUpdate (true);
  3265. break;
  3266. }
  3267. } while (!user_action_finished && undo_actions.Count > 0);
  3268. locked = false;
  3269. }
  3270. internal void Redo ()
  3271. {
  3272. Action action;
  3273. bool user_action_finished = false;
  3274. if (redo_actions.Count == 0)
  3275. return;
  3276. // You can't undo anything after redoing
  3277. undo_actions.Clear ();
  3278. locked = true;
  3279. do {
  3280. Line start;
  3281. int start_index;
  3282. action = (Action) redo_actions.Pop ();
  3283. switch (action.type) {
  3284. case ActionType.UserActionBegin:
  3285. // Noop
  3286. break;
  3287. case ActionType.UserActionEnd:
  3288. user_action_finished = true;
  3289. break;
  3290. case ActionType.InsertString:
  3291. start = document.GetLine (action.line_no);
  3292. document.SuspendUpdate ();
  3293. start_index = document.LineTagToCharIndex (start, action.pos);
  3294. document.InsertString (start, action.pos, (string) action.data);
  3295. document.CharIndexToLineTag (start_index + ((string) action.data).Length,
  3296. out document.caret.line, out document.caret.tag,
  3297. out document.caret.pos);
  3298. document.UpdateCaret ();
  3299. document.SetSelectionToCaret (true);
  3300. document.ResumeUpdate (true);
  3301. break;
  3302. case ActionType.Typing:
  3303. start = document.GetLine (action.line_no);
  3304. document.SuspendUpdate ();
  3305. start_index = document.LineTagToCharIndex (start, action.pos);
  3306. document.InsertString (start, action.pos, ((StringBuilder) action.data).ToString ());
  3307. document.CharIndexToLineTag (start_index + ((StringBuilder) action.data).Length,
  3308. out document.caret.line, out document.caret.tag,
  3309. out document.caret.pos);
  3310. document.UpdateCaret ();
  3311. document.SetSelectionToCaret (true);
  3312. document.ResumeUpdate (true);
  3313. // This is an open ended operation, so only a single typing operation can be undone at once
  3314. user_action_finished = true;
  3315. break;
  3316. case ActionType.DeleteString:
  3317. start = document.GetLine (action.line_no);
  3318. document.SuspendUpdate ();
  3319. document.DeleteMultiline (start, action.pos, ((Line) action.data).text.Length);
  3320. document.PositionCaret (start, action.pos);
  3321. document.SetSelectionToCaret (true);
  3322. document.ResumeUpdate (true);
  3323. break;
  3324. }
  3325. } while (!user_action_finished && redo_actions.Count > 0);
  3326. locked = false;
  3327. }
  3328. #endregion // Internal Methods
  3329. #region Private Methods
  3330. public void BeginUserAction (string name)
  3331. {
  3332. if (locked)
  3333. return;
  3334. Action ua = new Action ();
  3335. ua.type = ActionType.UserActionBegin;
  3336. ua.data = name;
  3337. undo_actions.Push (ua);
  3338. }
  3339. public void EndUserAction ()
  3340. {
  3341. if (locked)
  3342. return;
  3343. Action ua = new Action ();
  3344. ua.type = ActionType.UserActionEnd;
  3345. undo_actions.Push (ua);
  3346. }
  3347. // start_pos, end_pos = 1 based
  3348. public void RecordDeleteString (Line start_line, int start_pos, Line end_line, int end_pos)
  3349. {
  3350. if (locked)
  3351. return;
  3352. Action a = new Action ();
  3353. // We cant simply store the string, because then formatting would be lost
  3354. a.type = ActionType.DeleteString;
  3355. a.line_no = start_line.line_no;
  3356. a.pos = start_pos;
  3357. a.data = Duplicate (start_line, start_pos, end_line, end_pos);
  3358. undo_actions.Push(a);
  3359. }
  3360. public void RecordInsertString (Line line, int pos, string str)
  3361. {
  3362. if (locked || str.Length == 0)
  3363. return;
  3364. Action a = new Action ();
  3365. a.type = ActionType.InsertString;
  3366. a.data = str;
  3367. a.line_no = line.line_no;
  3368. a.pos = pos;
  3369. undo_actions.Push (a);
  3370. }
  3371. public void RecordTyping (Line line, int pos, char ch)
  3372. {
  3373. if (locked)
  3374. return;
  3375. Action a = null;
  3376. if (undo_actions.Count > 0)
  3377. a = (Action) undo_actions.Peek ();
  3378. if (a == null || a.type != ActionType.Typing) {
  3379. a = new Action ();
  3380. a.type = ActionType.Typing;
  3381. a.data = new StringBuilder ();
  3382. a.line_no = line.line_no;
  3383. a.pos = pos;
  3384. undo_actions.Push (a);
  3385. }
  3386. StringBuilder data = (StringBuilder) a.data;
  3387. data.Append (ch);
  3388. }
  3389. // start_pos = 1-based
  3390. // end_pos = 1-based
  3391. public Line Duplicate(Line start_line, int start_pos, Line end_line, int end_pos)
  3392. {
  3393. Line ret;
  3394. Line line;
  3395. Line current;
  3396. LineTag tag;
  3397. LineTag current_tag;
  3398. int start;
  3399. int end;
  3400. int tag_start;
  3401. line = new Line (start_line.document, start_line.ending);
  3402. ret = line;
  3403. for (int i = start_line.line_no; i <= end_line.line_no; i++) {
  3404. current = document.GetLine(i);
  3405. if (start_line.line_no == i) {
  3406. start = start_pos;
  3407. } else {
  3408. start = 0;
  3409. }
  3410. if (end_line.line_no == i) {
  3411. end = end_pos;
  3412. } else {
  3413. end = current.text.Length;
  3414. }
  3415. if (end_pos == 0)
  3416. continue;
  3417. // Text for the tag
  3418. line.text = new StringBuilder (current.text.ToString (start, end - start));
  3419. // Copy tags from start to start+length onto new line
  3420. current_tag = current.FindTag (start);
  3421. while ((current_tag != null) && (current_tag.start <= end)) {
  3422. if ((current_tag.start <= start) && (start < (current_tag.start + current_tag.Length))) {
  3423. // start tag is within this tag
  3424. tag_start = start;
  3425. } else {
  3426. tag_start = current_tag.start;
  3427. }
  3428. tag = new LineTag(line, tag_start - start + 1);
  3429. tag.CopyFormattingFrom (current_tag);
  3430. current_tag = current_tag.next;
  3431. // Add the new tag to the line
  3432. if (line.tags == null) {
  3433. line.tags = tag;
  3434. } else {
  3435. LineTag tail;
  3436. tail = line.tags;
  3437. while (tail.next != null) {
  3438. tail = tail.next;
  3439. }
  3440. tail.next = tag;
  3441. tag.previous = tail;
  3442. }
  3443. }
  3444. if ((i + 1) <= end_line.line_no) {
  3445. line.ending = current.ending;
  3446. // Chain them (we use right/left as next/previous)
  3447. line.right = new Line (start_line.document, start_line.ending);
  3448. line.right.left = line;
  3449. line = line.right;
  3450. }
  3451. }
  3452. return ret;
  3453. }
  3454. // Insert multi-line text at the given position; use formatting at insertion point for inserted text
  3455. internal void Insert(Line line, int pos, Line insert, bool select)
  3456. {
  3457. Line current;
  3458. LineTag tag;
  3459. int offset;
  3460. int lines;
  3461. Line first;
  3462. // Handle special case first
  3463. if (insert.right == null) {
  3464. // Single line insert
  3465. document.Split(line, pos);
  3466. if (insert.tags == null) {
  3467. return; // Blank line
  3468. }
  3469. //Insert our tags at the end
  3470. tag = line.tags;
  3471. while (tag.next != null) {
  3472. tag = tag.next;
  3473. }
  3474. offset = tag.start + tag.Length - 1;
  3475. tag.next = insert.tags;
  3476. line.text.Insert(offset, insert.text.ToString());
  3477. // Adjust start locations
  3478. tag = tag.next;
  3479. while (tag != null) {
  3480. tag.start += offset;
  3481. tag.line = line;
  3482. tag = tag.next;
  3483. }
  3484. // Put it back together
  3485. document.Combine(line.line_no, line.line_no + 1);
  3486. if (select) {
  3487. document.SetSelectionStart (line, pos, false);
  3488. document.SetSelectionEnd (line, pos + insert.text.Length, false);
  3489. }
  3490. document.UpdateView(line, pos);
  3491. return;
  3492. }
  3493. first = line;
  3494. lines = 1;
  3495. current = insert;
  3496. while (current != null) {
  3497. if (current == insert) {
  3498. // Inserting the first line we split the line (and make space)
  3499. document.Split(line.line_no, pos);
  3500. //Insert our tags at the end of the line
  3501. tag = line.tags;
  3502. if (tag != null && tag.Length != 0) {
  3503. while (tag.next != null) {
  3504. tag = tag.next;
  3505. }
  3506. offset = tag.start + tag.Length - 1;
  3507. tag.next = current.tags;
  3508. tag.next.previous = tag;
  3509. tag = tag.next;
  3510. } else {
  3511. offset = 0;
  3512. line.tags = current.tags;
  3513. line.tags.previous = null;
  3514. tag = line.tags;
  3515. }
  3516. line.ending = current.ending;
  3517. } else {
  3518. document.Split(line.line_no, 0);
  3519. offset = 0;
  3520. line.tags = current.tags;
  3521. line.tags.previous = null;
  3522. line.ending = current.ending;
  3523. tag = line.tags;
  3524. }
  3525. // Adjust start locations and line pointers
  3526. while (tag != null) {
  3527. tag.start += offset - 1;
  3528. tag.line = line;
  3529. tag = tag.next;
  3530. }
  3531. line.text.Insert(offset, current.text.ToString());
  3532. line.Grow(line.text.Length);
  3533. line.recalc = true;
  3534. line = document.GetLine(line.line_no + 1);
  3535. // FIXME? Test undo of line-boundaries
  3536. if ((current.right == null) && (current.tags.Length != 0)) {
  3537. document.Combine(line.line_no - 1, line.line_no);
  3538. }
  3539. current = current.right;
  3540. lines++;
  3541. }
  3542. // Recalculate our document
  3543. document.UpdateView(first, lines, pos);
  3544. return;
  3545. }
  3546. #endregion // Private Methods
  3547. }
  3548. }