TextControl.cs 73 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952
  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-2005 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 to support wrapping (ie have a 'newline' tag), for images, etc.
  32. // - Wrap and recalculate lines
  33. // - Implement CaretPgUp/PgDown
  34. // - Finish selection calculations (invalidate only changed, more ways to select)
  35. // - Implement C&P
  36. #undef Debug
  37. using System;
  38. using System.Collections;
  39. using System.Drawing;
  40. using System.Drawing.Text;
  41. using System.Text;
  42. namespace System.Windows.Forms {
  43. internal enum LineColor {
  44. Red = 0,
  45. Black = 1
  46. }
  47. internal enum CaretDirection {
  48. CharForward, // Move a char to the right
  49. CharBack, // Move a char to the left
  50. LineUp, // Move a line up
  51. LineDown, // Move a line down
  52. Home, // Move to the beginning of the line
  53. End, // Move to the end of the line
  54. PgUp, // Move one page up
  55. PgDn, // Move one page down
  56. CtrlHome, // Move to the beginning of the document
  57. CtrlEnd, // Move to the end of the document
  58. WordBack, // Move to the beginning of the previous word (or beginning of line)
  59. WordForward // Move to the beginning of the next word (or end of line)
  60. }
  61. // Being cloneable should allow for nice line and document copies...
  62. internal class Line : ICloneable, IComparable {
  63. #region Local Variables
  64. // Stuff that matters for our line
  65. internal StringBuilder text; // Characters for the line
  66. internal float[] widths; // Width of each character; always one larger than text.Length
  67. internal int space; // Number of elements in text and widths
  68. internal int line_no; // Line number
  69. internal LineTag tags; // Tags describing the text
  70. internal int Y; // Baseline
  71. internal int height; // Height of the line (height of tallest tag)
  72. internal int ascent; // Ascent of the line (ascent of the tallest tag)
  73. internal HorizontalAlignment alignment; // Alignment of the line
  74. internal int align_shift; // Pixel shift caused by the alignment
  75. internal bool soft_break; // Tag is 'broken soft' and continuation from previous line
  76. // Stuff that's important for the tree
  77. internal Line parent; // Our parent line
  78. internal Line left; // Line with smaller line number
  79. internal Line right; // Line with higher line number
  80. internal LineColor color; // We're doing a black/red tree. this is the node color
  81. internal int DEFAULT_TEXT_LEN; //
  82. internal static StringFormat string_format; // For calculating widths/heights
  83. internal bool recalc; // Line changed
  84. #endregion // Local Variables
  85. #region Constructors
  86. internal Line() {
  87. color = LineColor.Red;
  88. left = null;
  89. right = null;
  90. parent = null;
  91. text = null;
  92. recalc = true;
  93. soft_break = false;
  94. alignment = HorizontalAlignment.Left;
  95. if (string_format == null) {
  96. string_format = new StringFormat(StringFormat.GenericTypographic);
  97. string_format.Trimming = StringTrimming.None;
  98. string_format.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
  99. }
  100. }
  101. internal Line(int LineNo, string Text, Font font, Brush color) : this() {
  102. space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
  103. text = new StringBuilder(Text, space);
  104. line_no = LineNo;
  105. widths = new float[space + 1];
  106. tags = new LineTag(this, 1, text.Length);
  107. tags.font = font;
  108. tags.color = color;
  109. }
  110. internal Line(int LineNo, string Text, HorizontalAlignment align, Font font, Brush color) : this() {
  111. space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
  112. text = new StringBuilder(Text, space);
  113. line_no = LineNo;
  114. alignment = align;
  115. widths = new float[space + 1];
  116. tags = new LineTag(this, 1, text.Length);
  117. tags.font = font;
  118. tags.color = color;
  119. }
  120. internal Line(int LineNo, string Text, LineTag tag) : this() {
  121. space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;
  122. text = new StringBuilder(Text, space);
  123. line_no = LineNo;
  124. widths = new float[space + 1];
  125. tags = tag;
  126. }
  127. #endregion // Constructors
  128. #region Internal Properties
  129. internal int Height {
  130. get {
  131. return height;
  132. }
  133. set {
  134. height = value;
  135. }
  136. }
  137. internal int LineNo {
  138. get {
  139. return line_no;
  140. }
  141. set {
  142. line_no = value;
  143. }
  144. }
  145. internal string Text {
  146. get {
  147. return text.ToString();
  148. }
  149. set {
  150. text = new StringBuilder(value, value.Length > DEFAULT_TEXT_LEN ? value.Length : DEFAULT_TEXT_LEN);
  151. }
  152. }
  153. internal HorizontalAlignment Alignment {
  154. get {
  155. return alignment;
  156. }
  157. set {
  158. if (alignment != value) {
  159. alignment = value;
  160. recalc = true;
  161. }
  162. }
  163. }
  164. #if no
  165. internal StringBuilder Text {
  166. get {
  167. return text;
  168. }
  169. set {
  170. text = value;
  171. }
  172. }
  173. #endif
  174. #endregion // Internal Properties
  175. #region Internal Methods
  176. // Make sure we always have enoughs space in text and widths
  177. internal void Grow(int minimum) {
  178. int length;
  179. float[] new_widths;
  180. length = text.Length;
  181. if ((length + minimum) > space) {
  182. // We need to grow; double the size
  183. if ((length + minimum) > (space * 2)) {
  184. new_widths = new float[length + minimum * 2 + 1];
  185. space = length + minimum * 2;
  186. } else {
  187. new_widths = new float[space * 2 + 1];
  188. space *= 2;
  189. }
  190. widths.CopyTo(new_widths, 0);
  191. widths = new_widths;
  192. }
  193. }
  194. internal void Streamline() {
  195. LineTag current;
  196. LineTag next;
  197. current = this.tags;
  198. next = current.next;
  199. // Catch what the loop below wont; eliminate 0 length
  200. // tags, but only if there are other tags after us
  201. while ((current.length == 0) && (next != null)) {
  202. tags = next;
  203. current = next;
  204. next = current.next;
  205. }
  206. if (next == null) {
  207. return;
  208. }
  209. while (next != null) {
  210. // Take out 0 length tags
  211. if (next.length == 0) {
  212. current.next = next.next;
  213. if (current.next != null) {
  214. current.next.previous = current;
  215. }
  216. next = current.next;
  217. continue;
  218. }
  219. if (current.Combine(next)) {
  220. next = current.next;
  221. continue;
  222. }
  223. current = current.next;
  224. next = current.next;
  225. }
  226. }
  227. /// <summary> Find the tag on a line based on the character position</summary>
  228. internal LineTag FindTag(int pos) {
  229. LineTag tag;
  230. if (pos == 0) {
  231. return tags;
  232. }
  233. tag = this.tags;
  234. if (pos > text.Length) {
  235. pos = text.Length;
  236. }
  237. while (tag != null) {
  238. if ((tag.start <= pos) && (pos < (tag.start + tag.length))) {
  239. return tag;
  240. }
  241. tag = tag.next;
  242. }
  243. return null;
  244. }
  245. /// <summary>
  246. /// Go through all tags on a line and recalculate all size-related values;
  247. /// returns true if lineheight changed
  248. /// </summary>
  249. internal bool RecalculateLine(Graphics g, Document doc) {
  250. LineTag tag;
  251. int pos;
  252. int len;
  253. SizeF size;
  254. float w;
  255. int prev_height;
  256. bool retval;
  257. bool wrapped;
  258. Line line;
  259. int wrap_pos;
  260. float wrap_width;
  261. pos = 0;
  262. len = this.text.Length;
  263. tag = this.tags;
  264. prev_height = this.height; // For drawing optimization calculations
  265. this.height = 0; // Reset line height
  266. this.ascent = 0; // Reset the ascent for the line
  267. tag.shift = 0;
  268. tag.width = 0;
  269. widths[0] = 0;
  270. this.recalc = false;
  271. retval = false;
  272. wrapped = false;
  273. wrap_pos = 0;
  274. wrap_width = 0;
  275. while (pos < len) {
  276. size = g.MeasureString(this.text.ToString(pos, 1), tag.font, 10000, string_format);
  277. w = size.Width;
  278. if (Char.IsWhiteSpace(text[pos])) {
  279. wrap_pos = pos + 1;
  280. wrap_width = tag.width + w;
  281. }
  282. if (doc.wrap) {
  283. if ((widths[pos] + w) + 27 > doc.viewport_width) {
  284. pos = wrap_pos;
  285. tag.width = wrap_width;
  286. doc.Split(this, tag, pos, true);
  287. len = this.text.Length;
  288. retval = true;
  289. wrapped = true;
  290. }
  291. }
  292. // Contract all soft lines that follow back into our line
  293. if (!wrapped) {
  294. tag.width += w;
  295. pos++;
  296. widths[pos] = widths[pos-1] + w;
  297. if (pos == len) {
  298. line = doc.GetLine(this.line_no + 1);
  299. if ((line != null) && (line.soft_break)) {
  300. // Pull the previous line back into this one
  301. doc.Combine(this.line_no, this.line_no + 1);
  302. len = this.text.Length;
  303. retval = true;
  304. }
  305. }
  306. }
  307. if (pos == (tag.start-1 + tag.length)) {
  308. // We just found the end of our current tag
  309. tag.height = (int)tag.font.Height;
  310. // Check if we're the tallest on the line (so far)
  311. if (tag.height > this.height) {
  312. this.height = tag.height; // Yep; make sure the line knows
  313. }
  314. if (tag.ascent == 0) {
  315. int descent;
  316. XplatUI.GetFontMetrics(g, tag.font, out tag.ascent, out descent);
  317. }
  318. if (tag.ascent > this.ascent) {
  319. LineTag t;
  320. // We have a tag that has a taller ascent than the line;
  321. t = tags;
  322. while (t != tag) {
  323. t.shift = tag.ascent - t.ascent;
  324. t = t.next;
  325. }
  326. // Save on our line
  327. this.ascent = tag.ascent;
  328. } else {
  329. tag.shift = this.ascent - tag.ascent;
  330. }
  331. // Update our horizontal starting pixel position
  332. if (tag.previous == null) {
  333. tag.X = 0;
  334. } else {
  335. tag.X = tag.previous.X + (int)tag.previous.width;
  336. }
  337. tag = tag.next;
  338. if (tag != null) {
  339. tag.width = 0;
  340. tag.shift = 0;
  341. wrap_pos = pos;
  342. wrap_width = tag.width;
  343. }
  344. }
  345. }
  346. if (this.height == 0) {
  347. this.height = tags.font.Height;
  348. tag.height = this.height;
  349. }
  350. if (prev_height != this.height) {
  351. retval = true;
  352. }
  353. return retval;
  354. }
  355. #endregion // Internal Methods
  356. #region Administrative
  357. public int CompareTo(object obj) {
  358. if (obj == null) {
  359. return 1;
  360. }
  361. if (! (obj is Line)) {
  362. throw new ArgumentException("Object is not of type Line", "obj");
  363. }
  364. if (line_no < ((Line)obj).line_no) {
  365. return -1;
  366. } else if (line_no > ((Line)obj).line_no) {
  367. return 1;
  368. } else {
  369. return 0;
  370. }
  371. }
  372. public object Clone() {
  373. Line clone;
  374. clone = new Line();
  375. clone.text = text;
  376. if (left != null) {
  377. clone.left = (Line)left.Clone();
  378. }
  379. if (left != null) {
  380. clone.left = (Line)left.Clone();
  381. }
  382. return clone;
  383. }
  384. internal object CloneLine() {
  385. Line clone;
  386. clone = new Line();
  387. clone.text = text;
  388. return clone;
  389. }
  390. public override bool Equals(object obj) {
  391. if (obj == null) {
  392. return false;
  393. }
  394. if (!(obj is Line)) {
  395. return false;
  396. }
  397. if (obj == this) {
  398. return true;
  399. }
  400. if (line_no == ((Line)obj).line_no) {
  401. return true;
  402. }
  403. return false;
  404. }
  405. public override int GetHashCode() {
  406. return base.GetHashCode ();
  407. }
  408. public override string ToString() {
  409. return "Line " + line_no;
  410. }
  411. #endregion // Administrative
  412. }
  413. internal class Document : ICloneable, IEnumerable {
  414. #region Structures
  415. internal struct Marker {
  416. internal Line line;
  417. internal LineTag tag;
  418. internal int pos;
  419. internal int height;
  420. }
  421. #endregion Structures
  422. #region Local Variables
  423. private Line document;
  424. private int lines;
  425. private Line sentinel;
  426. private Line last_found;
  427. private int document_id;
  428. private Random random = new Random();
  429. internal bool multiline;
  430. internal bool wrap;
  431. internal Marker caret;
  432. internal Marker selection_start;
  433. internal Marker selection_end;
  434. internal bool selection_visible;
  435. internal int viewport_x;
  436. internal int viewport_y; // The visible area of the document
  437. internal int viewport_width;
  438. internal int viewport_height;
  439. internal int document_x; // Width of the document
  440. internal int document_y; // Height of the document
  441. internal Control owner; // Who's owning us?
  442. #endregion // Local Variables
  443. #region Constructors
  444. internal Document(Control owner) {
  445. lines = 0;
  446. this.owner = owner;
  447. multiline = true;
  448. // Tree related stuff
  449. sentinel = new Line();
  450. sentinel.color = LineColor.Black;
  451. document = sentinel;
  452. last_found = sentinel;
  453. // We always have a blank line
  454. Add(1, "", owner.Font, new SolidBrush(owner.ForeColor));
  455. this.RecalculateDocument(owner.CreateGraphics());
  456. PositionCaret(0, 0);
  457. lines=1;
  458. selection_visible = false;
  459. selection_start.line = this.document;
  460. selection_start.pos = 0;
  461. selection_end.line = this.document;
  462. selection_end.pos = 0;
  463. viewport_x = -2;
  464. viewport_y = -2;
  465. // Default selection is empty
  466. document_id = random.Next();
  467. }
  468. #endregion
  469. #region Internal Properties
  470. internal Line Root {
  471. get {
  472. return document;
  473. }
  474. set {
  475. document = value;
  476. }
  477. }
  478. internal int Lines {
  479. get {
  480. return lines;
  481. }
  482. }
  483. internal Line CaretLine {
  484. get {
  485. return caret.line;
  486. }
  487. }
  488. internal int CaretPosition {
  489. get {
  490. return caret.pos;
  491. }
  492. }
  493. internal Point Caret {
  494. get {
  495. return new Point((int)caret.tag.line.widths[caret.pos] + caret.line.align_shift, caret.line.Y);
  496. }
  497. }
  498. internal LineTag CaretTag {
  499. get {
  500. return caret.tag;
  501. }
  502. }
  503. internal int ViewPortX {
  504. get {
  505. return viewport_x;
  506. }
  507. set {
  508. viewport_x = value;
  509. }
  510. }
  511. internal int ViewPortY {
  512. get {
  513. return viewport_y;
  514. }
  515. set {
  516. viewport_y = value;
  517. }
  518. }
  519. internal int ViewPortWidth {
  520. get {
  521. return viewport_width;
  522. }
  523. set {
  524. viewport_width = value;
  525. }
  526. }
  527. internal int ViewPortHeight {
  528. get {
  529. return viewport_height;
  530. }
  531. set {
  532. viewport_height = value;
  533. }
  534. }
  535. internal int Width {
  536. get {
  537. return this.document_x;
  538. }
  539. }
  540. internal int Height {
  541. get {
  542. return this.document_y;
  543. }
  544. }
  545. internal bool Wrap {
  546. get {
  547. return wrap;
  548. }
  549. set {
  550. wrap = value;
  551. // FIXME - force recalc/redisplay
  552. }
  553. }
  554. #endregion // Internal Properties
  555. #region Private Methods
  556. // For debugging
  557. internal void DumpTree(Line line, bool with_tags) {
  558. Console.Write("Line {0}, Y: {1} Text {2}", line.line_no, line.Y, line.text != null ? line.text.ToString() : "undefined");
  559. if (line.left == sentinel) {
  560. Console.Write(", left = sentinel");
  561. } else if (line.left == null) {
  562. Console.Write(", left = NULL");
  563. }
  564. if (line.right == sentinel) {
  565. Console.Write(", right = sentinel");
  566. } else if (line.right == null) {
  567. Console.Write(", right = NULL");
  568. }
  569. Console.WriteLine("");
  570. if (with_tags) {
  571. LineTag tag;
  572. int count;
  573. tag = line.tags;
  574. count = 1;
  575. Console.Write(" Tags: ");
  576. while (tag != null) {
  577. Console.Write("{0} <{1}>-<{2}> ", count++, tag.start, tag.length);
  578. if (tag.line != line) {
  579. Console.Write("BAD line link");
  580. throw new Exception("Bad line link in tree");
  581. }
  582. tag = tag.next;
  583. if (tag != null) {
  584. Console.Write(", ");
  585. }
  586. }
  587. Console.WriteLine("");
  588. }
  589. if (line.left != null) {
  590. if (line.left != sentinel) {
  591. DumpTree(line.left, with_tags);
  592. }
  593. } else {
  594. if (line != sentinel) {
  595. throw new Exception("Left should not be NULL");
  596. }
  597. }
  598. if (line.right != null) {
  599. if (line.right != sentinel) {
  600. DumpTree(line.right, with_tags);
  601. }
  602. } else {
  603. if (line != sentinel) {
  604. throw new Exception("Right should not be NULL");
  605. }
  606. }
  607. }
  608. private void DecrementLines(int line_no) {
  609. int current;
  610. current = line_no;
  611. while (current <= lines) {
  612. GetLine(current).line_no--;
  613. current++;
  614. }
  615. return;
  616. }
  617. private void IncrementLines(int line_no) {
  618. int current;
  619. current = this.lines;
  620. while (current >= line_no) {
  621. GetLine(current).line_no++;
  622. current--;
  623. }
  624. return;
  625. }
  626. private void RebalanceAfterAdd(Line line1) {
  627. Line line2;
  628. while ((line1 != document) && (line1.parent.color == LineColor.Red)) {
  629. if (line1.parent == line1.parent.parent.left) {
  630. line2 = line1.parent.parent.right;
  631. if ((line2 != null) && (line2.color == LineColor.Red)) {
  632. line1.parent.color = LineColor.Black;
  633. line2.color = LineColor.Black;
  634. line1.parent.parent.color = LineColor.Red;
  635. line1 = line1.parent.parent;
  636. } else {
  637. if (line1 == line1.parent.right) {
  638. line1 = line1.parent;
  639. RotateLeft(line1);
  640. }
  641. line1.parent.color = LineColor.Black;
  642. line1.parent.parent.color = LineColor.Red;
  643. RotateRight(line1.parent.parent);
  644. }
  645. } else {
  646. line2 = line1.parent.parent.left;
  647. if ((line2 != null) && (line2.color == LineColor.Red)) {
  648. line1.parent.color = LineColor.Black;
  649. line2.color = LineColor.Black;
  650. line1.parent.parent.color = LineColor.Red;
  651. line1 = line1.parent.parent;
  652. } else {
  653. if (line1 == line1.parent.left) {
  654. line1 = line1.parent;
  655. RotateRight(line1);
  656. }
  657. line1.parent.color = LineColor.Black;
  658. line1.parent.parent.color = LineColor.Red;
  659. RotateLeft(line1.parent.parent);
  660. }
  661. }
  662. }
  663. document.color = LineColor.Black;
  664. }
  665. private void RebalanceAfterDelete(Line line1) {
  666. Line line2;
  667. while ((line1 != document) && (line1.color == LineColor.Black)) {
  668. if (line1 == line1.parent.left) {
  669. line2 = line1.parent.right;
  670. if (line2.color == LineColor.Red) {
  671. line2.color = LineColor.Black;
  672. line1.parent.color = LineColor.Red;
  673. RotateLeft(line1.parent);
  674. line2 = line1.parent.right;
  675. }
  676. if ((line2.left.color == LineColor.Black) && (line2.right.color == LineColor.Black)) {
  677. line2.color = LineColor.Red;
  678. line1 = line1.parent;
  679. } else {
  680. if (line2.right.color == LineColor.Black) {
  681. line2.left.color = LineColor.Black;
  682. line2.color = LineColor.Red;
  683. RotateRight(line2);
  684. line2 = line1.parent.right;
  685. }
  686. line2.color = line1.parent.color;
  687. line1.parent.color = LineColor.Black;
  688. line2.right.color = LineColor.Black;
  689. RotateLeft(line1.parent);
  690. line1 = document;
  691. }
  692. } else {
  693. line2 = line1.parent.left;
  694. if (line2.color == LineColor.Red) {
  695. line2.color = LineColor.Black;
  696. line1.parent.color = LineColor.Red;
  697. RotateRight(line1.parent);
  698. line2 = line1.parent.left;
  699. }
  700. if ((line2.right.color == LineColor.Black) && (line2.left.color == LineColor.Black)) {
  701. line2.color = LineColor.Red;
  702. line1 = line1.parent;
  703. } else {
  704. if (line2.left.color == LineColor.Black) {
  705. line2.right.color = LineColor.Black;
  706. line2.color = LineColor.Red;
  707. RotateLeft(line2);
  708. line2 = line1.parent.left;
  709. }
  710. line2.color = line1.parent.color;
  711. line1.parent.color = LineColor.Black;
  712. line2.left.color = LineColor.Black;
  713. RotateRight(line1.parent);
  714. line1 = document;
  715. }
  716. }
  717. }
  718. line1.color = LineColor.Black;
  719. }
  720. private void RotateLeft(Line line1) {
  721. Line line2 = line1.right;
  722. line1.right = line2.left;
  723. if (line2.left != sentinel) {
  724. line2.left.parent = line1;
  725. }
  726. if (line2 != sentinel) {
  727. line2.parent = line1.parent;
  728. }
  729. if (line1.parent != null) {
  730. if (line1 == line1.parent.left) {
  731. line1.parent.left = line2;
  732. } else {
  733. line1.parent.right = line2;
  734. }
  735. } else {
  736. document = line2;
  737. }
  738. line2.left = line1;
  739. if (line1 != sentinel) {
  740. line1.parent = line2;
  741. }
  742. }
  743. private void RotateRight(Line line1) {
  744. Line line2 = line1.left;
  745. line1.left = line2.right;
  746. if (line2.right != sentinel) {
  747. line2.right.parent = line1;
  748. }
  749. if (line2 != sentinel) {
  750. line2.parent = line1.parent;
  751. }
  752. if (line1.parent != null) {
  753. if (line1 == line1.parent.right) {
  754. line1.parent.right = line2;
  755. } else {
  756. line1.parent.left = line2;
  757. }
  758. } else {
  759. document = line2;
  760. }
  761. line2.right = line1;
  762. if (line1 != sentinel) {
  763. line1.parent = line2;
  764. }
  765. }
  766. internal void UpdateView(Line line, int pos) {
  767. if (RecalculateDocument(owner.CreateGraphics(), line.line_no, line.line_no, true)) {
  768. // Lineheight changed, invalidate the rest of the document
  769. if ((line.Y - viewport_y) >=0 ) {
  770. // We formatted something that's in view, only draw parts of the screen
  771. owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, owner.Height - line.Y - viewport_y));
  772. } else {
  773. // The tag was above the visible area, draw everything
  774. owner.Invalidate();
  775. }
  776. } else {
  777. owner.Invalidate(new Rectangle((int)line.widths[pos] - viewport_x - 1, line.Y - viewport_y, viewport_width, line.height));
  778. }
  779. }
  780. // Update display from line, down line_count lines; pos is unused, but required for the signature
  781. internal void UpdateView(Line line, int line_count, int pos) {
  782. if (RecalculateDocument(owner.CreateGraphics(), line.line_no, line.line_no + line_count - 1, true)) {
  783. // Lineheight changed, invalidate the rest of the document
  784. if ((line.Y - viewport_y) >=0 ) {
  785. // We formatted something that's in view, only draw parts of the screen
  786. owner.Invalidate(new Rectangle(0, line.Y - viewport_y, viewport_width, owner.Height - line.Y - viewport_y));
  787. } else {
  788. // The tag was above the visible area, draw everything
  789. owner.Invalidate();
  790. }
  791. } else {
  792. Line end_line;
  793. end_line = GetLine(line.line_no + line_count -1);
  794. if (end_line == null) {
  795. end_line = line;
  796. }
  797. owner.Invalidate(new Rectangle(0 - viewport_x, line.Y - viewport_y, (int)line.widths[line.text.Length], end_line.Y + end_line.height));
  798. }
  799. }
  800. #endregion // Private Methods
  801. #region Internal Methods
  802. // Clear the document and reset state
  803. internal void Empty() {
  804. document = sentinel;
  805. last_found = sentinel;
  806. lines = 0;
  807. // We always have a blank line
  808. Add(1, "", owner.Font, new SolidBrush(owner.ForeColor));
  809. this.RecalculateDocument(owner.CreateGraphics());
  810. PositionCaret(0, 0);
  811. selection_visible = false;
  812. selection_start.line = this.document;
  813. selection_start.pos = 0;
  814. selection_end.line = this.document;
  815. selection_end.pos = 0;
  816. viewport_x = 0;
  817. viewport_y = 0;
  818. document_x = 0;
  819. document_y = 0;
  820. }
  821. internal void PositionCaret(Line line, int pos) {
  822. caret.tag = line.FindTag(pos);
  823. caret.line = line;
  824. caret.pos = pos;
  825. caret.height = caret.tag.height;
  826. XplatUI.DestroyCaret(owner.Handle);
  827. XplatUI.CreateCaret(owner.Handle, 2, caret.height);
  828. XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.align_shift - viewport_x, caret.line.Y + caret.tag.shift - viewport_y);
  829. if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
  830. }
  831. internal void PositionCaret(int x, int y) {
  832. caret.tag = FindCursor(x, y, out caret.pos);
  833. caret.line = caret.tag.line;
  834. caret.height = caret.tag.height;
  835. XplatUI.DestroyCaret(owner.Handle);
  836. XplatUI.CreateCaret(owner.Handle, 2, caret.height);
  837. XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.align_shift - viewport_x, caret.line.Y + caret.tag.shift - viewport_y);
  838. if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
  839. }
  840. internal void CaretHasFocus() {
  841. if (caret.tag != null) {
  842. XplatUI.CreateCaret(owner.Handle, 2, caret.height);
  843. XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.align_shift - viewport_x, caret.line.Y + caret.tag.shift - viewport_y);
  844. XplatUI.CaretVisible(owner.Handle, true);
  845. }
  846. }
  847. internal void CaretLostFocus() {
  848. XplatUI.DestroyCaret(owner.Handle);
  849. }
  850. internal void AlignCaret() {
  851. caret.tag = LineTag.FindTag(caret.line, caret.pos);
  852. caret.height = caret.tag.height;
  853. XplatUI.CreateCaret(owner.Handle, 2, caret.height);
  854. XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.align_shift - viewport_x, caret.line.Y + caret.tag.shift - viewport_y);
  855. XplatUI.CaretVisible(owner.Handle, true);
  856. if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
  857. }
  858. internal void UpdateCaret() {
  859. if (caret.tag.height != caret.height) {
  860. caret.height = caret.tag.height;
  861. XplatUI.CreateCaret(owner.Handle, 2, caret.height);
  862. }
  863. XplatUI.SetCaretPos(owner.Handle, (int)caret.tag.line.widths[caret.pos] + caret.line.align_shift - viewport_x, caret.line.Y + caret.tag.shift - viewport_y);
  864. XplatUI.CaretVisible(owner.Handle, true);
  865. if (CaretMoved != null) CaretMoved(this, EventArgs.Empty);
  866. }
  867. internal void DisplayCaret() {
  868. XplatUI.CaretVisible(owner.Handle, true);
  869. }
  870. internal void HideCaret() {
  871. XplatUI.CaretVisible(owner.Handle, false);
  872. }
  873. internal void MoveCaret(CaretDirection direction) {
  874. switch(direction) {
  875. case CaretDirection.CharForward: {
  876. caret.pos++;
  877. if (caret.pos > caret.line.text.Length) {
  878. if (multiline) {
  879. // Go into next line
  880. if (caret.line.line_no < this.lines) {
  881. caret.line = GetLine(caret.line.line_no+1);
  882. caret.pos = 0;
  883. caret.tag = caret.line.tags;
  884. } else {
  885. caret.pos--;
  886. }
  887. } else {
  888. // Single line; we stay where we are
  889. caret.pos--;
  890. }
  891. } else {
  892. if ((caret.tag.start - 1 + caret.tag.length) < caret.pos) {
  893. caret.tag = caret.tag.next;
  894. }
  895. }
  896. UpdateCaret();
  897. return;
  898. }
  899. case CaretDirection.CharBack: {
  900. if (caret.pos > 0) {
  901. // caret.pos--; // folded into the if below
  902. if (--caret.pos > 0) {
  903. if (caret.tag.start > caret.pos) {
  904. caret.tag = caret.tag.previous;
  905. }
  906. }
  907. } else {
  908. if (caret.line.line_no > 1) {
  909. caret.line = GetLine(caret.line.line_no - 1);
  910. caret.pos = caret.line.text.Length;
  911. caret.tag = LineTag.FindTag(caret.line, caret.pos);
  912. }
  913. }
  914. UpdateCaret();
  915. return;
  916. }
  917. case CaretDirection.WordForward: {
  918. int len;
  919. len = caret.line.text.Length;
  920. if (caret.pos < len) {
  921. while ((caret.pos < len) && (caret.line.text.ToString(caret.pos, 1) != " ")) {
  922. caret.pos++;
  923. }
  924. if (caret.pos < len) {
  925. // Skip any whitespace
  926. while ((caret.pos < len) && (caret.line.text.ToString(caret.pos, 1) == " ")) {
  927. caret.pos++;
  928. }
  929. }
  930. } else {
  931. if (caret.line.line_no < this.lines) {
  932. caret.line = GetLine(caret.line.line_no+1);
  933. caret.pos = 0;
  934. caret.tag = caret.line.tags;
  935. }
  936. }
  937. UpdateCaret();
  938. return;
  939. }
  940. case CaretDirection.WordBack: {
  941. if (caret.pos > 0) {
  942. caret.pos--;
  943. while ((caret.pos > 0) && (caret.line.text.ToString(caret.pos, 1) == " ")) {
  944. caret.pos--;
  945. }
  946. while ((caret.pos > 0) && (caret.line.text.ToString(caret.pos, 1) != " ")) {
  947. caret.pos--;
  948. }
  949. if (caret.line.text.ToString(caret.pos, 1) == " ") {
  950. if (caret.pos != 0) {
  951. caret.pos++;
  952. } else {
  953. caret.line = GetLine(caret.line.line_no - 1);
  954. caret.pos = caret.line.text.Length;
  955. caret.tag = LineTag.FindTag(caret.line, caret.pos);
  956. }
  957. }
  958. } else {
  959. if (caret.line.line_no > 1) {
  960. caret.line = GetLine(caret.line.line_no - 1);
  961. caret.pos = caret.line.text.Length;
  962. caret.tag = LineTag.FindTag(caret.line, caret.pos);
  963. }
  964. }
  965. UpdateCaret();
  966. return;
  967. }
  968. case CaretDirection.LineUp: {
  969. if (caret.line.line_no > 1) {
  970. int pixel;
  971. pixel = (int)caret.line.widths[caret.pos];
  972. PositionCaret(pixel, GetLine(caret.line.line_no - 1).Y);
  973. XplatUI.CaretVisible(owner.Handle, true);
  974. }
  975. return;
  976. }
  977. case CaretDirection.LineDown: {
  978. if (caret.line.line_no < lines) {
  979. int pixel;
  980. pixel = (int)caret.line.widths[caret.pos];
  981. PositionCaret(pixel, GetLine(caret.line.line_no + 1).Y);
  982. XplatUI.CaretVisible(owner.Handle, true);
  983. }
  984. return;
  985. }
  986. case CaretDirection.Home: {
  987. if (caret.pos > 0) {
  988. caret.pos = 0;
  989. caret.tag = caret.line.tags;
  990. UpdateCaret();
  991. }
  992. return;
  993. }
  994. case CaretDirection.End: {
  995. if (caret.pos < caret.line.text.Length) {
  996. caret.pos = caret.line.text.Length;
  997. caret.tag = LineTag.FindTag(caret.line, caret.pos);
  998. UpdateCaret();
  999. }
  1000. return;
  1001. }
  1002. case CaretDirection.PgUp: {
  1003. return;
  1004. }
  1005. case CaretDirection.PgDn: {
  1006. return;
  1007. }
  1008. case CaretDirection.CtrlHome: {
  1009. caret.line = GetLine(1);
  1010. caret.pos = 0;
  1011. caret.tag = caret.line.tags;
  1012. UpdateCaret();
  1013. return;
  1014. }
  1015. case CaretDirection.CtrlEnd: {
  1016. caret.line = GetLine(lines);
  1017. caret.pos = 0;
  1018. caret.tag = caret.line.tags;
  1019. UpdateCaret();
  1020. return;
  1021. }
  1022. }
  1023. }
  1024. // Draw the document
  1025. internal void Draw(Graphics g, Rectangle clip) {
  1026. Line line; // Current line being drawn
  1027. LineTag tag; // Current tag being drawn
  1028. int start; // First line to draw
  1029. int end; // Last line to draw
  1030. string s; // String representing the current line
  1031. int line_no; //
  1032. Brush hilight;
  1033. Brush hilight_text;
  1034. // First, figure out from what line to what line we need to draw
  1035. start = GetLineByPixel(clip.Top + viewport_y, false).line_no;
  1036. end = GetLineByPixel(clip.Bottom + viewport_y, false).line_no;
  1037. Console.WriteLine("Starting drawing at line {0}, ending at line {1} (clip-bottom:{2})", start, end, clip.Bottom);
  1038. // Now draw our elements; try to only draw those that are visible
  1039. line_no = start;
  1040. #if Debug
  1041. DateTime n = DateTime.Now;
  1042. Console.WriteLine("Started drawing: {0}s {1}ms", n.Second, n.Millisecond);
  1043. #endif
  1044. hilight = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorHilight);
  1045. hilight_text = ThemeEngine.Current.ResPool.GetSolidBrush(ThemeEngine.Current.ColorHilightText);
  1046. while (line_no <= end) {
  1047. line = GetLine(line_no);
  1048. tag = line.tags;
  1049. s = line.text.ToString();
  1050. while (tag != null) {
  1051. if (((tag.X + tag.width) > (clip.Left - viewport_x)) || (tag.X < (clip.Right - viewport_x))) {
  1052. // Check for selection
  1053. if ((!selection_visible) || (!owner.has_focus) || (line_no < selection_start.line.line_no) || (line_no > selection_end.line.line_no)) {
  1054. // regular drawing
  1055. g.DrawString(s.Substring(tag.start-1, tag.length), tag.font, tag.color, tag.X + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
  1056. } else {
  1057. // we might have to draw our selection
  1058. if ((line_no != selection_start.line.line_no) && (line_no != selection_end.line.line_no)) {
  1059. g.FillRectangle(hilight, tag.X + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, line.widths[tag.start + tag.length - 1], tag.height);
  1060. g.DrawString(s.Substring(tag.start-1, tag.length), tag.font, hilight_text, tag.X + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
  1061. } else {
  1062. bool highlight;
  1063. bool partial;
  1064. highlight = false;
  1065. partial = false;
  1066. // Check the partial drawings first
  1067. if ((selection_start.tag == tag) && (selection_end.tag == tag)) {
  1068. partial = true;
  1069. // First, the regular part
  1070. g.DrawString(s.Substring(tag.start - 1, selection_start.pos - tag.start + 1), tag.font, tag.color, tag.X + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
  1071. // Now the highlight
  1072. g.FillRectangle(hilight, line.widths[selection_start.pos] + line.align_shift, line.Y + tag.shift - viewport_y, line.widths[selection_end.pos] - line.widths[selection_start.pos], tag.height);
  1073. g.DrawString(s.Substring(selection_start.pos, selection_end.pos - selection_start.pos), tag.font, hilight_text, line.widths[selection_start.pos] + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
  1074. // And back to the regular
  1075. g.DrawString(s.Substring(selection_end.pos, tag.length - selection_end.pos), tag.font, tag.color, line.widths[selection_end.pos] + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
  1076. } else if (selection_start.tag == tag) {
  1077. partial = true;
  1078. // The highlighted part
  1079. g.FillRectangle(hilight, line.widths[selection_start.pos] + line.align_shift, line.Y + tag.shift - viewport_y, line.widths[tag.start + tag.length - 1] - line.widths[selection_start.pos], tag.height);
  1080. g.DrawString(s.Substring(selection_start.pos, tag.length - selection_start.pos), tag.font, hilight_text, line.widths[selection_start.pos] + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
  1081. // The regular part
  1082. g.DrawString(s.Substring(tag.start - 1, selection_start.pos - tag.start + 1), tag.font, tag.color, tag.X + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
  1083. } else if (selection_end.tag == tag) {
  1084. partial = true;
  1085. // The highlighted part
  1086. g.FillRectangle(hilight, tag.X + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, line.widths[selection_end.pos], tag.height);
  1087. g.DrawString(s.Substring(tag.start - 1, selection_end.pos - tag.start + 1), tag.font, hilight_text, tag.X + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
  1088. // The regular part
  1089. g.DrawString(s.Substring(selection_end.pos, tag.length - selection_end.pos), tag.font, tag.color, line.widths[selection_end.pos] + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
  1090. } else {
  1091. // no partially selected tags here, simple checks...
  1092. if (selection_start.line == line) {
  1093. if ((tag.start + tag.length - 1) > selection_start.pos) {
  1094. highlight = true;
  1095. }
  1096. }
  1097. if (selection_end.line == line) {
  1098. if ((tag.start + tag.length - 1) < selection_start.pos) {
  1099. highlight = true;
  1100. }
  1101. }
  1102. }
  1103. if (!partial) {
  1104. if (highlight) {
  1105. g.FillRectangle(hilight, tag.X + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, line.widths[tag.start + tag.length - 1], tag.height);
  1106. g.DrawString(s.Substring(tag.start-1, tag.length), tag.font, hilight_text, tag.X + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
  1107. } else {
  1108. g.DrawString(s.Substring(tag.start-1, tag.length), tag.font, tag.color, tag.X + line.align_shift - viewport_x, line.Y + tag.shift - viewport_y, StringFormat.GenericTypographic);
  1109. }
  1110. }
  1111. }
  1112. }
  1113. }
  1114. tag = tag.next;
  1115. }
  1116. line_no++;
  1117. }
  1118. #if Debug
  1119. n = DateTime.Now;
  1120. Console.WriteLine("Finished drawing: {0}s {1}ms", n.Second, n.Millisecond);
  1121. #endif
  1122. }
  1123. // Inserts a character at the given position
  1124. internal void InsertString(Line line, int pos, string s) {
  1125. InsertString(line.FindTag(pos), pos, s);
  1126. }
  1127. // Inserts a string at the given position
  1128. internal void InsertString(LineTag tag, int pos, string s) {
  1129. Line line;
  1130. int len;
  1131. len = s.Length;
  1132. line = tag.line;
  1133. line.text.Insert(pos, s);
  1134. tag.length += len;
  1135. tag = tag.next;
  1136. while (tag != null) {
  1137. tag.start += len;
  1138. tag = tag.next;
  1139. }
  1140. line.Grow(len);
  1141. line.recalc = true;
  1142. UpdateView(line, pos);
  1143. }
  1144. // Inserts a string at the caret position
  1145. internal void InsertStringAtCaret(string s, bool move_caret) {
  1146. LineTag tag;
  1147. int len;
  1148. len = s.Length;
  1149. caret.line.text.Insert(caret.pos, s);
  1150. caret.tag.length += len;
  1151. if (caret.tag.next != null) {
  1152. tag = caret.tag.next;
  1153. while (tag != null) {
  1154. tag.start += len;
  1155. tag = tag.next;
  1156. }
  1157. }
  1158. caret.line.Grow(len);
  1159. caret.line.recalc = true;
  1160. UpdateView(caret.line, caret.pos);
  1161. if (move_caret) {
  1162. caret.pos += len;
  1163. UpdateCaret();
  1164. }
  1165. }
  1166. // Inserts a character at the given position
  1167. internal void InsertChar(Line line, int pos, char ch) {
  1168. InsertChar(line.FindTag(pos), pos, ch);
  1169. }
  1170. // Inserts a character at the given position
  1171. internal void InsertChar(LineTag tag, int pos, char ch) {
  1172. Line line;
  1173. line = tag.line;
  1174. line.text.Insert(pos, ch);
  1175. tag.length++;
  1176. tag = tag.next;
  1177. while (tag != null) {
  1178. tag.start++;
  1179. tag = tag.next;
  1180. }
  1181. line.Grow(1);
  1182. line.recalc = true;
  1183. UpdateView(line, pos);
  1184. }
  1185. // Inserts a character at the current caret position
  1186. internal void InsertCharAtCaret(char ch, bool move_caret) {
  1187. LineTag tag;
  1188. caret.line.text.Insert(caret.pos, ch);
  1189. caret.tag.length++;
  1190. if (caret.tag.next != null) {
  1191. tag = caret.tag.next;
  1192. while (tag != null) {
  1193. tag.start++;
  1194. tag = tag.next;
  1195. }
  1196. }
  1197. caret.line.Grow(1);
  1198. caret.line.recalc = true;
  1199. UpdateView(caret.line, caret.pos);
  1200. if (move_caret) {
  1201. caret.pos++;
  1202. UpdateCaret();
  1203. }
  1204. }
  1205. // Inserts n characters at the given position; it will not delete past line limits
  1206. internal void DeleteChars(LineTag tag, int pos, int count) {
  1207. Line line;
  1208. bool streamline;
  1209. streamline = false;
  1210. line = tag.line;
  1211. if (pos == line.text.Length) {
  1212. return;
  1213. }
  1214. line.text.Remove(pos, count);
  1215. // Check if we're crossing tag boundaries
  1216. if ((pos + count) > (tag.start + tag.length)) {
  1217. int left;
  1218. // We have to delete cross tag boundaries
  1219. streamline = true;
  1220. left = count;
  1221. left -= pos - tag.start;
  1222. tag.length -= pos - tag.start;
  1223. tag = tag.next;
  1224. while ((tag != null) && (left > 0)) {
  1225. if (tag.length > left) {
  1226. tag.length -= left;
  1227. left = 0;
  1228. } else {
  1229. left -= tag.length;
  1230. tag.length = 0;
  1231. tag = tag.next;
  1232. }
  1233. }
  1234. } else {
  1235. // We got off easy, same tag
  1236. tag.length -= count;
  1237. }
  1238. tag = tag.next;
  1239. while (tag != null) {
  1240. tag.start -= count;
  1241. tag = tag.next;
  1242. }
  1243. line.recalc = true;
  1244. if (streamline) {
  1245. line.Streamline();
  1246. }
  1247. UpdateView(line, pos);
  1248. }
  1249. // Deletes a character at or after the given position (depending on forward); it will not delete past line limits
  1250. internal void DeleteChar(LineTag tag, int pos, bool forward) {
  1251. Line line;
  1252. bool streamline;
  1253. streamline = false;
  1254. line = tag.line;
  1255. if ((pos == 0 && forward == false) || (pos == line.text.Length && forward == true)) {
  1256. return;
  1257. }
  1258. if (forward) {
  1259. line.text.Remove(pos, 1);
  1260. tag.length--;
  1261. if (tag.length == 0) {
  1262. streamline = true;
  1263. }
  1264. } else {
  1265. pos--;
  1266. line.text.Remove(pos, 1);
  1267. if (pos >= (tag.start - 1)) {
  1268. tag.length--;
  1269. if (tag.length == 0) {
  1270. streamline = true;
  1271. }
  1272. } else if (tag.previous != null) {
  1273. tag.previous.length--;
  1274. if (tag.previous.length == 0) {
  1275. streamline = true;
  1276. }
  1277. }
  1278. }
  1279. tag = tag.next;
  1280. while (tag != null) {
  1281. tag.start--;
  1282. tag = tag.next;
  1283. }
  1284. line.recalc = true;
  1285. if (streamline) {
  1286. line.Streamline();
  1287. }
  1288. UpdateView(line, pos);
  1289. }
  1290. // Combine two lines
  1291. internal void Combine(int FirstLine, int SecondLine) {
  1292. Combine(GetLine(FirstLine), GetLine(SecondLine));
  1293. }
  1294. internal void Combine(Line first, Line second) {
  1295. LineTag last;
  1296. int shift;
  1297. // Combine the two tag chains into one
  1298. last = first.tags;
  1299. while (last.next != null) {
  1300. last = last.next;
  1301. }
  1302. last.next = second.tags;
  1303. last.next.previous = last;
  1304. shift = last.start + last.length - 1;
  1305. // Fix up references within the chain
  1306. last = last.next;
  1307. while (last != null) {
  1308. last.line = first;
  1309. last.start += shift;
  1310. last = last.next;
  1311. }
  1312. // Combine both lines' strings
  1313. first.text.Insert(first.text.Length, second.text.ToString());
  1314. first.Grow(first.text.Length);
  1315. // Remove the reference to our (now combined) tags from the doomed line
  1316. second.tags = null;
  1317. // Renumber lines
  1318. DecrementLines(first.line_no + 2); // first.line_no + 1 will be deleted, so we need to start renumbering one later
  1319. // Mop up
  1320. first.recalc = true;
  1321. first.height = 0; // This forces RecalcDocument/UpdateView to redraw from this line on
  1322. first.Streamline();
  1323. #if Debug
  1324. Line check_first;
  1325. Line check_second;
  1326. check_first = GetLine(first.line_no);
  1327. check_second = GetLine(check_first.line_no + 1);
  1328. Console.WriteLine("Pre-delete: Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
  1329. #endif
  1330. this.Delete(second);
  1331. #if Debug
  1332. check_first = GetLine(first.line_no);
  1333. check_second = GetLine(check_first.line_no + 1);
  1334. Console.WriteLine("Post-delete Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y);
  1335. #endif
  1336. }
  1337. // Split the line at the position into two
  1338. internal void Split(int LineNo, int pos) {
  1339. Line line;
  1340. LineTag tag;
  1341. line = GetLine(LineNo);
  1342. tag = LineTag.FindTag(line, pos);
  1343. Split(line, tag, pos, false);
  1344. }
  1345. internal void Split(Line line, int pos) {
  1346. LineTag tag;
  1347. tag = LineTag.FindTag(line, pos);
  1348. Split(line, tag, pos, false);
  1349. }
  1350. internal void Split(Line line, LineTag tag, int pos, bool soft) {
  1351. LineTag new_tag;
  1352. Line new_line;
  1353. bool move_caret;
  1354. move_caret = false;
  1355. // Adjust selection and cursors
  1356. if (soft && (caret.line == line) && (caret.pos >= pos)) {
  1357. move_caret = true;
  1358. }
  1359. // cover the easy case first
  1360. if (pos == line.text.Length) {
  1361. Add(line.line_no + 1, "", line.alignment, tag.font, tag.color);
  1362. if (soft) {
  1363. if (move_caret) {
  1364. caret.line = GetLine(line.line_no + 1);
  1365. caret.line.soft_break = true;
  1366. caret.tag = selection_start.line.tags;
  1367. caret.pos = 0;
  1368. } else {
  1369. GetLine(line.line_no + 1).soft_break = true;
  1370. }
  1371. }
  1372. return;
  1373. }
  1374. // We need to move the rest of the text into the new line
  1375. Add(line.line_no + 1, line.text.ToString(pos, line.text.Length - pos), line.alignment, tag.font, tag.color);
  1376. // Now transfer our tags from this line to the next
  1377. new_line = GetLine(line.line_no + 1);
  1378. line.recalc = true;
  1379. if ((tag.start - 1) == pos) {
  1380. int shift;
  1381. // We can simply break the chain and move the tag into the next line
  1382. if (tag == line.tags) {
  1383. new_tag = new LineTag(line, 1, 0);
  1384. new_tag.font = tag.font;
  1385. new_tag.color = tag.color;
  1386. line.tags = new_tag;
  1387. }
  1388. if (tag.previous != null) {
  1389. tag.previous.next = null;
  1390. }
  1391. new_line.tags = tag;
  1392. tag.previous = null;
  1393. tag.line = new_line;
  1394. // Walk the list and correct the start location of the tags we just bumped into the next line
  1395. shift = tag.start - 1;
  1396. new_tag = tag;
  1397. while (new_tag != null) {
  1398. new_tag.start -= shift;
  1399. new_tag.line = new_line;
  1400. new_tag = new_tag.next;
  1401. }
  1402. } else {
  1403. int shift;
  1404. new_tag = new LineTag(new_line, 1, tag.start - 1 + tag.length - pos);
  1405. new_tag.next = tag.next;
  1406. new_tag.font = tag.font;
  1407. new_tag.color = tag.color;
  1408. new_line.tags = new_tag;
  1409. if (new_tag.next != null) {
  1410. new_tag.next.previous = new_tag;
  1411. }
  1412. tag.next = null;
  1413. tag.length = pos - tag.start + 1;
  1414. shift = pos;
  1415. new_tag = new_tag.next;
  1416. while (new_tag != null) {
  1417. new_tag.start -= shift;
  1418. new_tag.line = new_line;
  1419. new_tag = new_tag.next;
  1420. }
  1421. }
  1422. if (soft) {
  1423. if (move_caret) {
  1424. caret.line = new_line;
  1425. caret.pos = caret.pos - pos;
  1426. caret.tag = caret.line.FindTag(caret.pos);
  1427. }
  1428. new_line.soft_break = true;
  1429. }
  1430. line.text.Remove(pos, line.text.Length - pos);
  1431. }
  1432. // Adds a line of text, with given font.
  1433. // Bumps any line at that line number that already exists down
  1434. internal void Add(int LineNo, string Text, Font font, Brush color) {
  1435. Add(LineNo, Text, HorizontalAlignment.Left, font, color);
  1436. }
  1437. internal void Add(int LineNo, string Text, HorizontalAlignment align, Font font, Brush color) {
  1438. Line add;
  1439. Line line;
  1440. int line_no;
  1441. if (LineNo<1 || Text == null) {
  1442. if (LineNo<1) {
  1443. throw new ArgumentNullException("LineNo", "Line numbers must be positive");
  1444. } else {
  1445. throw new ArgumentNullException("Text", "Cannot insert NULL line");
  1446. }
  1447. }
  1448. add = new Line(LineNo, Text, align, font, color);
  1449. line = document;
  1450. while (line != sentinel) {
  1451. add.parent = line;
  1452. line_no = line.line_no;
  1453. if (LineNo > line_no) {
  1454. line = line.right;
  1455. } else if (LineNo < line_no) {
  1456. line = line.left;
  1457. } else {
  1458. // Bump existing line numbers; walk all nodes to the right of this one and increment line_no
  1459. IncrementLines(line.line_no);
  1460. line = line.left;
  1461. }
  1462. }
  1463. add.left = sentinel;
  1464. add.right = sentinel;
  1465. if (add.parent != null) {
  1466. if (LineNo > add.parent.line_no) {
  1467. add.parent.right = add;
  1468. } else {
  1469. add.parent.left = add;
  1470. }
  1471. } else {
  1472. // Root node
  1473. document = add;
  1474. }
  1475. RebalanceAfterAdd(add);
  1476. lines++;
  1477. }
  1478. internal virtual void Clear() {
  1479. lines = 0;
  1480. document = sentinel;
  1481. }
  1482. public virtual object Clone() {
  1483. Document clone;
  1484. clone = new Document(null);
  1485. clone.lines = this.lines;
  1486. clone.document = (Line)document.Clone();
  1487. return clone;
  1488. }
  1489. internal void Delete(int LineNo) {
  1490. if (LineNo>lines) {
  1491. return;
  1492. }
  1493. Delete(GetLine(LineNo));
  1494. }
  1495. internal void Delete(Line line1) {
  1496. Line line2;// = new Line();
  1497. Line line3;
  1498. if ((line1.left == sentinel) || (line1.right == sentinel)) {
  1499. line3 = line1;
  1500. } else {
  1501. line3 = line1.right;
  1502. while (line3.left != sentinel) {
  1503. line3 = line3.left;
  1504. }
  1505. }
  1506. if (line3.left != sentinel) {
  1507. line2 = line3.left;
  1508. } else {
  1509. line2 = line3.right;
  1510. }
  1511. line2.parent = line3.parent;
  1512. if (line3.parent != null) {
  1513. if(line3 == line3.parent.left) {
  1514. line3.parent.left = line2;
  1515. } else {
  1516. line3.parent.right = line2;
  1517. }
  1518. } else {
  1519. document = line2;
  1520. }
  1521. if (line3 != line1) {
  1522. LineTag tag;
  1523. line1.ascent = line3.ascent;
  1524. line1.height = line3.height;
  1525. line1.line_no = line3.line_no;
  1526. line1.recalc = line3.recalc;
  1527. line1.space = line3.space;
  1528. line1.tags = line3.tags;
  1529. line1.text = line3.text;
  1530. line1.widths = line3.widths;
  1531. line1.Y = line3.Y;
  1532. line1.soft_break = line3.soft_break;
  1533. tag = line1.tags;
  1534. while (tag != null) {
  1535. tag.line = line1;
  1536. tag = tag.next;
  1537. }
  1538. }
  1539. if (line3.color == LineColor.Black)
  1540. RebalanceAfterDelete(line2);
  1541. this.lines--;
  1542. last_found = sentinel;
  1543. }
  1544. // Set our selection markers
  1545. internal void Invalidate(Line start, int start_pos, Line end, int end_pos) {
  1546. Line l1;
  1547. Line l2;
  1548. int p1;
  1549. int p2;
  1550. // figure out what's before what so the logic below is straightforward
  1551. if (start.line_no < end.line_no) {
  1552. l1 = start;
  1553. p1 = start_pos;
  1554. l2 = end;
  1555. p2 = end_pos;
  1556. } else if (start.line_no > end.line_no) {
  1557. l1 = end;
  1558. p1 = end_pos;
  1559. l2 = start;
  1560. p2 = start_pos;
  1561. } else {
  1562. if (start_pos < end_pos) {
  1563. l1 = start;
  1564. p1 = start_pos;
  1565. l2 = end;
  1566. p2 = end_pos;
  1567. } else {
  1568. l1 = end;
  1569. p1 = end_pos;
  1570. l2 = start;
  1571. p2 = start_pos;
  1572. }
  1573. owner.Invalidate(new Rectangle((int)l1.widths[p1] - viewport_x, l1.Y - viewport_y, (int)l2.widths[p2] - (int)l1.widths[p1], l1.height));
  1574. return;
  1575. }
  1576. // Three invalidates:
  1577. // First line from start
  1578. owner.Invalidate(new Rectangle((int)l1.widths[p1] - viewport_x, l1.Y - viewport_y, viewport_width, l1.height));
  1579. // lines inbetween
  1580. if ((l1.line_no + 1) < l2.line_no) {
  1581. int y;
  1582. y = GetLine(l1.line_no + 1).Y;
  1583. owner.Invalidate(new Rectangle(0 - viewport_x, y - viewport_y, viewport_width, GetLine(l2.line_no - 1).Y - y - viewport_y));
  1584. }
  1585. // Last line to end
  1586. owner.Invalidate(new Rectangle((int)l1.widths[p1] - viewport_x, l1.Y - viewport_y, viewport_width, l1.height));
  1587. }
  1588. // It's nothing short of pathetic to always invalidate the whole control
  1589. // I will find time to finish the optimization and make it invalidate deltas only
  1590. internal void SetSelectionToCaret(bool start) {
  1591. if (start) {
  1592. selection_start.line = caret.line;
  1593. selection_start.tag = caret.tag;
  1594. selection_start.pos = caret.pos;
  1595. // start always also selects end
  1596. selection_end.line = caret.line;
  1597. selection_end.tag = caret.tag;
  1598. selection_end.pos = caret.pos;
  1599. } else {
  1600. if (caret.line.line_no <= selection_end.line.line_no) {
  1601. if ((caret.line != selection_end.line) || (caret.pos < selection_end.pos)) {
  1602. selection_start.line = caret.line;
  1603. selection_start.tag = caret.tag;
  1604. selection_start.pos = caret.pos;
  1605. } else {
  1606. selection_end.line = caret.line;
  1607. selection_end.tag = caret.tag;
  1608. selection_end.pos = caret.pos;
  1609. }
  1610. } else {
  1611. selection_end.line = caret.line;
  1612. selection_end.tag = caret.tag;
  1613. selection_end.pos = caret.pos;
  1614. }
  1615. }
  1616. if ((selection_start.line == selection_end.line) && (selection_start.pos == selection_end.pos)) {
  1617. selection_visible = false;
  1618. } else {
  1619. selection_visible = true;
  1620. }
  1621. owner.Invalidate();
  1622. }
  1623. #if buggy
  1624. internal void SetSelection(Line start, int start_pos, Line end, int end_pos) {
  1625. // Ok, this is ugly, bad and slow, but I don't have time right now to optimize it.
  1626. if (selection_visible) {
  1627. // Try to only invalidate what's changed so we don't redraw the whole thing
  1628. if ((start != selection_start.line) || (start_pos != selection_start.pos) && ((end == selection_end.line) && (end_pos == selection_end.pos))) {
  1629. Invalidate(start, start_pos, selection_start.line, selection_start.pos);
  1630. } else if ((end != selection_end.line) || (end_pos != selection_end.pos) && ((start == selection_start.line) && (start_pos == selection_start.pos))) {
  1631. Invalidate(end, end_pos, selection_end.line, selection_end.pos);
  1632. } else {
  1633. // both start and end changed
  1634. Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  1635. }
  1636. }
  1637. selection_start.line = start;
  1638. selection_start.pos = start_pos;
  1639. if (start != null) {
  1640. selection_start.tag = LineTag.FindTag(start, start_pos);
  1641. }
  1642. selection_end.line = end;
  1643. selection_end.pos = end_pos;
  1644. if (end != null) {
  1645. selection_end.tag = LineTag.FindTag(end, end_pos);
  1646. }
  1647. if (((start == end) && (start_pos == end_pos)) || start == null || end == null) {
  1648. selection_visible = false;
  1649. } else {
  1650. selection_visible = true;
  1651. }
  1652. }
  1653. #endif
  1654. // Make sure that start is always before end
  1655. private void FixupSelection() {
  1656. if (selection_start.line.line_no > selection_end.line.line_no) {
  1657. Line line;
  1658. int pos;
  1659. LineTag tag;
  1660. line = selection_start.line;
  1661. tag = selection_start.tag;
  1662. pos = selection_start.pos;
  1663. selection_start.line = selection_end.line;
  1664. selection_start.tag = selection_end.tag;
  1665. selection_start.pos = selection_end.pos;
  1666. selection_end.line = line;
  1667. selection_end.tag = tag;
  1668. selection_end.pos = pos;
  1669. return;
  1670. }
  1671. if ((selection_start.line == selection_end.line) && (selection_start.pos > selection_end.pos)) {
  1672. int pos;
  1673. pos = selection_start.pos;
  1674. selection_start.pos = selection_end.pos;
  1675. selection_end.pos = pos;
  1676. Console.WriteLine("flipped: sel start: {0} end: {1}", selection_start.pos, selection_end.pos);
  1677. return;
  1678. }
  1679. return;
  1680. }
  1681. internal void SetSelectionStart(Line start, int start_pos) {
  1682. selection_start.line = start;
  1683. selection_start.pos = start_pos;
  1684. selection_start.tag = LineTag.FindTag(start, start_pos);
  1685. FixupSelection();
  1686. if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) {
  1687. selection_visible = true;
  1688. }
  1689. if (selection_visible) {
  1690. Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  1691. }
  1692. }
  1693. internal void SetSelectionEnd(Line end, int end_pos) {
  1694. selection_end.line = end;
  1695. selection_end.pos = end_pos;
  1696. selection_end.tag = LineTag.FindTag(end, end_pos);
  1697. FixupSelection();
  1698. if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) {
  1699. selection_visible = true;
  1700. }
  1701. if (selection_visible) {
  1702. Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  1703. }
  1704. }
  1705. internal void SetSelection(Line start, int start_pos) {
  1706. if (selection_visible) {
  1707. Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos);
  1708. }
  1709. selection_start.line = start;
  1710. selection_start.pos = start_pos;
  1711. selection_start.tag = LineTag.FindTag(start, start_pos);
  1712. selection_end.line = start;
  1713. selection_end.tag = selection_start.tag;
  1714. selection_end.pos = start_pos;
  1715. selection_visible = false;
  1716. }
  1717. internal void InvalidateSelectionArea() {
  1718. // implement me
  1719. }
  1720. // Return the current selection, as string
  1721. internal string GetSelection() {
  1722. // We return String.Empty if there is no selection
  1723. if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) {
  1724. return string.Empty;
  1725. }
  1726. if (!multiline || (selection_start.line == selection_end.line)) {
  1727. return selection_start.line.text.ToString(selection_start.pos, selection_end.pos - selection_start.pos);
  1728. } else {
  1729. StringBuilder sb;
  1730. int i;
  1731. int start;
  1732. int end;
  1733. sb = new StringBuilder();
  1734. start = selection_start.line.line_no;
  1735. end = selection_end.line.line_no;
  1736. sb.Append(selection_start.line.text.ToString(selection_start.pos, selection_start.line.text.Length - selection_start.pos) + Environment.NewLine);
  1737. if ((start + 1) < end) {
  1738. for (i = start + 1; i < end; i++) {
  1739. sb.Append(GetLine(i).text.ToString() + Environment.NewLine);
  1740. }
  1741. }
  1742. sb.Append(selection_end.line.text.ToString(0, selection_end.pos));
  1743. return sb.ToString();
  1744. }
  1745. }
  1746. internal void ReplaceSelection(string s) {
  1747. // The easiest is to break the lines where the selection tags are and delete those lines
  1748. if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) {
  1749. // Nothing to delete, simply insert
  1750. InsertString(selection_start.tag, selection_start.pos, s);
  1751. }
  1752. if (!multiline || (selection_start.line == selection_end.line)) {
  1753. DeleteChars(selection_start.tag, selection_start.pos, selection_end.pos - selection_start.pos);
  1754. // The tag might have been removed, we need to recalc it
  1755. selection_start.tag = selection_start.line.FindTag(selection_start.pos);
  1756. InsertString(selection_start.tag, selection_start.pos, s);
  1757. } else {
  1758. int i;
  1759. int start;
  1760. int end;
  1761. int base_line;
  1762. string[] ins;
  1763. int insert_lines;
  1764. start = selection_start.line.line_no;
  1765. end = selection_end.line.line_no;
  1766. // Delete first line
  1767. DeleteChars(selection_start.tag, selection_start.pos, selection_end.pos - selection_start.pos);
  1768. start++;
  1769. if (start < end) {
  1770. for (i = end - 1; i >= start; i--) {
  1771. Delete(i);
  1772. }
  1773. }
  1774. // Delete last line
  1775. DeleteChars(selection_end.line.tags, 0, selection_end.pos);
  1776. ins = s.Split(new char[] {'\n'});
  1777. for (int j = 0; j < ins.Length; j++) {
  1778. if (ins[j].EndsWith("\r")) {
  1779. ins[j] = ins[j].Substring(0, ins[j].Length - 1);
  1780. }
  1781. }
  1782. insert_lines = ins.Length;
  1783. // Bump the text at insertion point a line down if we're inserting more than one line
  1784. if (insert_lines > 1) {
  1785. Split(selection_start.line, selection_start.pos);
  1786. // Reminder of start line is now in startline+1
  1787. // if the last line does not end with a \n we will insert the last line in front of the just moved text
  1788. if (s.EndsWith("\n")) {
  1789. insert_lines--; // We don't want to insert the last line as part of the loop anymore
  1790. InsertString(GetLine(selection_start.line.line_no + 1), 0, ins[insert_lines - 1]);
  1791. }
  1792. }
  1793. // Insert the first line
  1794. InsertString(selection_start.line, selection_start.pos, ins[0]);
  1795. if (insert_lines > 1) {
  1796. base_line = selection_start.line.line_no + 1;
  1797. for (i = 1; i < insert_lines; i++) {
  1798. Add(base_line + i, ins[i], selection_start.line.alignment, selection_start.tag.font, selection_start.tag.color);
  1799. }
  1800. }
  1801. }
  1802. selection_end.line = selection_start.line;
  1803. selection_end.pos = selection_start.pos;
  1804. selection_end.tag = selection_start.tag;
  1805. selection_visible = false;
  1806. PositionCaret(selection_start.line, selection_start.pos);
  1807. InvalidateSelectionArea();
  1808. }
  1809. internal void CharIndexToLineTag(int index, out Line line_out, out LineTag tag_out, out int pos) {
  1810. Line line;
  1811. LineTag tag;
  1812. int i;
  1813. int chars;
  1814. int start;
  1815. chars = 0;
  1816. for (i = 1; i < lines; i++) {
  1817. line = GetLine(i);
  1818. start = chars;
  1819. chars += line.text.Length + 2; // We count the trailing \n as a char
  1820. if (index <= chars) {
  1821. // we found the line
  1822. tag = line.tags;
  1823. while (tag != null) {
  1824. if (index < (start + tag.start + tag.length)) {
  1825. line_out = line;
  1826. tag_out = tag;
  1827. pos = index - start;
  1828. return;
  1829. }
  1830. if (tag.next == null) {
  1831. Line next_line;
  1832. next_line = GetLine(line.line_no + 1);
  1833. if (next_line != null) {
  1834. line_out = next_line;
  1835. tag_out = next_line.tags;
  1836. pos = 0;
  1837. return;
  1838. } else {
  1839. line_out = line;
  1840. tag_out = tag;
  1841. pos = line_out.text.Length;
  1842. return;
  1843. }
  1844. }
  1845. tag = tag.next;
  1846. }
  1847. }
  1848. }
  1849. line_out = GetLine(lines);
  1850. tag = line_out.tags;
  1851. while (tag.next != null) {
  1852. tag = tag.next;
  1853. }
  1854. tag_out = tag;
  1855. pos = line_out.text.Length;
  1856. }
  1857. internal int LineTagToCharIndex(Line line, int pos) {
  1858. int i;
  1859. int length;
  1860. // Count first and last line
  1861. length = 0;
  1862. // Count the lines in the middle
  1863. for (i = 1; i < line.line_no; i++) {
  1864. length += GetLine(i).text.Length + 2; // Add one for the \n at the end of the line
  1865. }
  1866. length += pos;
  1867. return length;
  1868. }
  1869. internal int SelectionLength() {
  1870. if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) {
  1871. return 0;
  1872. }
  1873. if (!multiline || (selection_start.line == selection_end.line)) {
  1874. return selection_end.pos - selection_start.pos;
  1875. } else {
  1876. int i;
  1877. int start;
  1878. int end;
  1879. int length;
  1880. // Count first and last line
  1881. length = selection_start.line.text.Length - selection_start.pos + selection_end.pos;
  1882. // Count the lines in the middle
  1883. start = selection_start.line.line_no + 1;
  1884. end = selection_end.line.line_no;
  1885. if (start < end) {
  1886. for (i = start; i < end; i++) {
  1887. length += GetLine(i).text.Length;
  1888. }
  1889. }
  1890. return length;
  1891. }
  1892. }
  1893. /// <summary>Give it a Line number and it returns the Line object at with that line number</summary>
  1894. internal Line GetLine(int LineNo) {
  1895. Line line = document;
  1896. while (line != sentinel) {
  1897. if (LineNo == line.line_no) {
  1898. return line;
  1899. } else if (LineNo < line.line_no) {
  1900. line = line.left;
  1901. } else {
  1902. line = line.right;
  1903. }
  1904. }
  1905. return null;
  1906. }
  1907. /// <summary>Retrieve the previous tag; walks line boundaries</summary>
  1908. internal LineTag PreviousTag(LineTag tag) {
  1909. Line l;
  1910. if (tag.previous != null) {
  1911. return tag.previous;
  1912. }
  1913. // Next line
  1914. if (tag.line.line_no == 1) {
  1915. return null;
  1916. }
  1917. l = GetLine(tag.line.line_no - 1);
  1918. if (l != null) {
  1919. LineTag t;
  1920. t = l.tags;
  1921. while (t.next != null) {
  1922. t = t.next;
  1923. }
  1924. return t;
  1925. }
  1926. return null;
  1927. }
  1928. /// <summary>Retrieve the next tag; walks line boundaries</summary>
  1929. internal LineTag NextTag(LineTag tag) {
  1930. Line l;
  1931. if (tag.next != null) {
  1932. return tag.next;
  1933. }
  1934. // Next line
  1935. l = GetLine(tag.line.line_no + 1);
  1936. if (l != null) {
  1937. return l.tags;
  1938. }
  1939. return null;
  1940. }
  1941. /// <summary>Give it a Y pixel coordinate and it returns the Line covering that Y coordinate</summary>
  1942. internal Line GetLineByPixel(int y, bool exact) {
  1943. Line line = document;
  1944. Line last = null;
  1945. while (line != sentinel) {
  1946. last = line;
  1947. if ((y >= line.Y) && (y < (line.Y+line.height))) {
  1948. return line;
  1949. } else if (y < line.Y) {
  1950. line = line.left;
  1951. } else {
  1952. line = line.right;
  1953. }
  1954. }
  1955. if (exact) {
  1956. return null;
  1957. }
  1958. return last;
  1959. }
  1960. // Give it x/y pixel coordinates and it returns the Tag at that position; optionally the char position is returned in index
  1961. internal LineTag FindTag(int x, int y, out int index, bool exact) {
  1962. Line line;
  1963. LineTag tag;
  1964. line = GetLineByPixel(y, exact);
  1965. if (line == null) {
  1966. index = 0;
  1967. return null;
  1968. }
  1969. tag = line.tags;
  1970. // Alignment adjustment
  1971. x += line.align_shift;
  1972. while (true) {
  1973. if (x >= tag.X && x < (tag.X+tag.width)) {
  1974. int end;
  1975. end = tag.start + tag.length - 1;
  1976. for (int pos = tag.start; pos < end; pos++) {
  1977. if (x < line.widths[pos]) {
  1978. index = pos;
  1979. return tag;
  1980. }
  1981. }
  1982. index=end;
  1983. return tag;
  1984. }
  1985. if (tag.next != null) {
  1986. tag = tag.next;
  1987. } else {
  1988. if (exact) {
  1989. index = 0;
  1990. return null;
  1991. }
  1992. index = line.text.Length;
  1993. return tag;
  1994. }
  1995. }
  1996. }
  1997. // Give it x/y pixel coordinates and it returns the Tag at that position; optionally the char position is returned in index
  1998. internal LineTag FindCursor(int x, int y, out int index) {
  1999. Line line;
  2000. LineTag tag;
  2001. line = GetLineByPixel(y, false);
  2002. tag = line.tags;
  2003. // Adjust for alignment
  2004. x += line.align_shift;
  2005. while (true) {
  2006. if (x >= tag.X && x < (tag.X+tag.width)) {
  2007. int end;
  2008. end = tag.start + tag.length - 1;
  2009. for (int pos = tag.start-1; pos < end; pos++) {
  2010. // When clicking on a character, we position the cursor to whatever edge
  2011. // of the character the click was closer
  2012. if (x < (line.widths[pos] + ((line.widths[pos+1]-line.widths[pos])/2))) {
  2013. index = pos;
  2014. return tag;
  2015. }
  2016. }
  2017. index=end;
  2018. return tag;
  2019. }
  2020. if (tag.next != null) {
  2021. tag = tag.next;
  2022. } else {
  2023. index = line.text.Length;
  2024. return tag;
  2025. }
  2026. }
  2027. }
  2028. internal void FormatText(Line start_line, int start_pos, Line end_line, int end_pos, Font font, Brush color) {
  2029. Line l;
  2030. // First, format the first line
  2031. LineTag.FormatText(start_line, start_pos, start_line.text.Length - start_pos, font, color);
  2032. // Format last line
  2033. if (end_line != start_line) {
  2034. LineTag.FormatText(end_line, 0, end_pos, font, color);
  2035. }
  2036. // Now all the lines inbetween
  2037. for (int i = start_line.line_no + 1; i < end_line.line_no - 1; i++) {
  2038. l = GetLine(i);
  2039. LineTag.FormatText(l, 0, l.text.Length, font, color);
  2040. }
  2041. }
  2042. internal void RecalculateAlignments() {
  2043. Line line;
  2044. int line_no;
  2045. line_no = 1;
  2046. while (line_no <= lines) {
  2047. line = GetLine(line_no);
  2048. if (line.alignment != HorizontalAlignment.Left) {
  2049. if (line.alignment == HorizontalAlignment.Center) {
  2050. line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
  2051. } else {
  2052. line.align_shift = viewport_width - (int)line.widths[line.text.Length];
  2053. }
  2054. }
  2055. line_no++;
  2056. }
  2057. return;
  2058. }
  2059. /// <summary>Calculate formatting for the whole document</summary>
  2060. internal bool RecalculateDocument(Graphics g) {
  2061. return RecalculateDocument(g, 1, this.lines, false);
  2062. }
  2063. /// <summary>Calculate formatting starting at a certain line</summary>
  2064. internal bool RecalculateDocument(Graphics g, int start) {
  2065. return RecalculateDocument(g, start, this.lines, false);
  2066. }
  2067. /// <summary>Calculate formatting within two given line numbers</summary>
  2068. internal bool RecalculateDocument(Graphics g, int start, int end) {
  2069. return RecalculateDocument(g, start, end, false);
  2070. }
  2071. /// <summary>With optimize on, returns true if line heights changed</summary>
  2072. internal bool RecalculateDocument(Graphics g, int start, int end, bool optimize) {
  2073. Line line;
  2074. int line_no;
  2075. int Y;
  2076. int new_width;
  2077. Y = GetLine(start).Y;
  2078. line_no = start;
  2079. new_width = 0;
  2080. if (optimize) {
  2081. bool changed;
  2082. bool alignment_recalc;
  2083. changed = false;
  2084. alignment_recalc = true;
  2085. while (line_no <= end) {
  2086. line = GetLine(line_no++);
  2087. line.Y = Y;
  2088. if (line.recalc) {
  2089. if (line.RecalculateLine(g, this)) {
  2090. changed = true;
  2091. // If the height changed, all subsequent lines change
  2092. end = this.lines;
  2093. }
  2094. if (line.widths[line.text.Length] > new_width) {
  2095. new_width = (int)line.widths[line.text.Length];
  2096. }
  2097. // Calculate alignment
  2098. if (line.alignment != HorizontalAlignment.Left) {
  2099. if (line.alignment == HorizontalAlignment.Center) {
  2100. line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
  2101. } else {
  2102. line.align_shift = viewport_width - (int)line.widths[line.text.Length];
  2103. }
  2104. }
  2105. }
  2106. Y += line.height;
  2107. if (line_no > lines) {
  2108. break;
  2109. }
  2110. }
  2111. if (document_x < new_width) {
  2112. document_x = new_width;
  2113. alignment_recalc = true;
  2114. if (WidthChanged != null) {
  2115. WidthChanged(this, null);
  2116. }
  2117. }
  2118. if (alignment_recalc) {
  2119. RecalculateAlignments();
  2120. }
  2121. line = GetLine(lines);
  2122. if (document_y != line.Y + line.height) {
  2123. document_y = line.Y + line.height;
  2124. if (HeightChanged != null) {
  2125. HeightChanged(this, null);
  2126. }
  2127. }
  2128. return changed;
  2129. } else {
  2130. int shift;
  2131. shift = 0;
  2132. while (line_no <= (end + shift)) {
  2133. line = GetLine(line_no++);
  2134. line.Y = Y;
  2135. shift = this.lines;
  2136. line.RecalculateLine(g, this);
  2137. if (this.lines > shift) {
  2138. shift = this.lines - shift;
  2139. } else {
  2140. shift = 0;
  2141. }
  2142. if (line.widths[line.text.Length] > new_width) {
  2143. new_width = (int)line.widths[line.text.Length];
  2144. }
  2145. // Calculate alignment
  2146. if (line.alignment != HorizontalAlignment.Left) {
  2147. if (line.alignment == HorizontalAlignment.Center) {
  2148. line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2;
  2149. } else {
  2150. line.align_shift = viewport_width - (int)line.widths[line.text.Length];
  2151. }
  2152. }
  2153. Y += line.height;
  2154. if (line_no > lines) {
  2155. break;
  2156. }
  2157. }
  2158. if (document_x != new_width) {
  2159. document_x = new_width;
  2160. if (WidthChanged != null) {
  2161. WidthChanged(this, null);
  2162. }
  2163. }
  2164. RecalculateAlignments();
  2165. line = GetLine(lines);
  2166. if (document_y != line.Y + line.height) {
  2167. document_y = line.Y + line.height;
  2168. if (HeightChanged != null) {
  2169. HeightChanged(this, null);
  2170. }
  2171. }
  2172. return true;
  2173. }
  2174. }
  2175. internal bool SetCursor(int x, int y) {
  2176. return true;
  2177. }
  2178. internal int Size() {
  2179. return lines;
  2180. }
  2181. #endregion // Internal Methods
  2182. #region Events
  2183. internal event EventHandler CaretMoved;
  2184. internal event EventHandler WidthChanged;
  2185. internal event EventHandler HeightChanged;
  2186. #endregion // Events
  2187. #region Administrative
  2188. public IEnumerator GetEnumerator() {
  2189. // FIXME
  2190. return null;
  2191. }
  2192. public override bool Equals(object obj) {
  2193. if (obj == null) {
  2194. return false;
  2195. }
  2196. if (!(obj is Document)) {
  2197. return false;
  2198. }
  2199. if (obj == this) {
  2200. return true;
  2201. }
  2202. if (ToString().Equals(((Document)obj).ToString())) {
  2203. return true;
  2204. }
  2205. return false;
  2206. }
  2207. public override int GetHashCode() {
  2208. return document_id;
  2209. }
  2210. public override string ToString() {
  2211. return "document " + this.document_id;
  2212. }
  2213. #endregion // Administrative
  2214. }
  2215. internal class LineTag {
  2216. #region Local Variables;
  2217. // Payload; formatting
  2218. internal Font font; // System.Drawing.Font object for this tag
  2219. internal Brush color; // System.Drawing.Brush object
  2220. // Payload; text
  2221. internal int start; // start, in chars; index into Line.text
  2222. internal int length; // length, in chars
  2223. internal bool r_to_l; // Which way is the font
  2224. // Drawing support
  2225. internal int height; // Height in pixels of the text this tag describes
  2226. internal int X; // X location of the text this tag describes
  2227. internal float width; // Width in pixels of the text this tag describes
  2228. internal int ascent; // Ascent of the font for this tag
  2229. internal int shift; // Shift down for this tag, to stay on baseline
  2230. // Administrative
  2231. internal Line line; // The line we're on
  2232. internal LineTag next; // Next tag on the same line
  2233. internal LineTag previous; // Previous tag on the same line
  2234. #endregion;
  2235. #region Constructors
  2236. internal LineTag(Line line, int start, int length) {
  2237. this.line = line;
  2238. this.start = start;
  2239. this.length = length;
  2240. this.X = 0;
  2241. this.width = 0;
  2242. }
  2243. #endregion // Constructors
  2244. #region Internal Methods
  2245. /// <summary>Applies 'font' to characters starting at 'start' for 'length' chars;
  2246. /// Removes any previous tags overlapping the same area;
  2247. /// returns true if lineheight has changed</summary>
  2248. internal static bool FormatText(Line line, int start, int length, Font font, Brush color) {
  2249. LineTag tag;
  2250. LineTag start_tag;
  2251. int end;
  2252. bool retval = false; // Assume line-height doesn't change
  2253. // Too simple?
  2254. if (font.Height != line.height) {
  2255. retval = true;
  2256. }
  2257. line.recalc = true; // This forces recalculation of the line in RecalculateDocument
  2258. // A little sanity, not sure if it's needed, might be able to remove for speed
  2259. if (length > line.text.Length) {
  2260. length = line.text.Length;
  2261. }
  2262. tag = line.tags;
  2263. end = start + length;
  2264. // Common special case
  2265. if ((start == 1) && (length == tag.length)) {
  2266. tag.ascent = 0;
  2267. tag.font = font;
  2268. tag.color = color;
  2269. return retval;
  2270. }
  2271. start_tag = FindTag(line, start);
  2272. tag = new LineTag(line, start, length);
  2273. tag.font = font;
  2274. tag.color = color;
  2275. if (start == 1) {
  2276. line.tags = tag;
  2277. }
  2278. if (start_tag.start == start) {
  2279. tag.next = start_tag;
  2280. tag.previous = start_tag.previous;
  2281. if (start_tag.previous != null) {
  2282. start_tag.previous.next = tag;
  2283. }
  2284. start_tag.previous = tag;
  2285. } else {
  2286. // Insert ourselves 'in the middle'
  2287. if ((start_tag.next != null) && (start_tag.next.start < end)) {
  2288. tag.next = start_tag.next;
  2289. } else {
  2290. tag.next = new LineTag(line, start_tag.start, start_tag.length);
  2291. tag.next.font = start_tag.font;
  2292. tag.next.color = start_tag.color;
  2293. if (start_tag.next != null) {
  2294. tag.next.next = start_tag.next;
  2295. tag.next.next.previous = tag.next;
  2296. }
  2297. }
  2298. tag.next.previous = tag;
  2299. start_tag.length = start - start_tag.start;
  2300. tag.previous = start_tag;
  2301. start_tag.next = tag;
  2302. #if nope
  2303. if (tag.next.start > (tag.start + tag.length)) {
  2304. tag.next.length += tag.next.start - (tag.start + tag.length);
  2305. tag.next.start = tag.start + tag.length;
  2306. }
  2307. #endif
  2308. }
  2309. // Elimination loop
  2310. tag = tag.next;
  2311. while ((tag != null) && (tag.start < end)) {
  2312. if ((tag.start + tag.length) <= end) {
  2313. // remove the tag
  2314. tag.previous.next = tag.next;
  2315. if (tag.next != null) {
  2316. tag.next.previous = tag.previous;
  2317. }
  2318. tag = tag.previous;
  2319. } else {
  2320. // Adjust the length of the tag
  2321. tag.length = (tag.start + tag.length) - end;
  2322. tag.start = end;
  2323. }
  2324. tag = tag.next;
  2325. }
  2326. return retval;
  2327. }
  2328. /// <summary>Finds the tag that describes the character at position 'pos' on 'line'</summary>
  2329. internal static LineTag FindTag(Line line, int pos) {
  2330. LineTag tag = line.tags;
  2331. // Beginning of line is a bit special
  2332. if (pos == 0) {
  2333. return tag;
  2334. }
  2335. while (tag != null) {
  2336. if ((tag.start <= pos) && (pos < (tag.start+tag.length))) {
  2337. return tag;
  2338. }
  2339. tag = tag.next;
  2340. }
  2341. return null;
  2342. }
  2343. /// <summary>Combines 'this' tag with 'other' tag</summary>
  2344. internal bool Combine(LineTag other) {
  2345. if (!this.Equals(other)) {
  2346. return false;
  2347. }
  2348. this.width += other.width;
  2349. this.length += other.length;
  2350. this.next = other.next;
  2351. if (this.next != null) {
  2352. this.next.previous = this;
  2353. }
  2354. return true;
  2355. }
  2356. /// <summary>Remove 'this' tag ; to be called when formatting is to be removed</summary>
  2357. internal bool Remove() {
  2358. if ((this.start == 1) && (this.next == null)) {
  2359. // We cannot remove the only tag
  2360. return false;
  2361. }
  2362. if (this.start != 1) {
  2363. this.previous.length += this.length;
  2364. this.previous.width = -1;
  2365. this.previous.next = this.next;
  2366. this.next.previous = this.previous;
  2367. } else {
  2368. this.next.start = 1;
  2369. this.next.length += this.length;
  2370. this.next.width = -1;
  2371. this.line.tags = this.next;
  2372. this.next.previous = null;
  2373. }
  2374. return true;
  2375. }
  2376. /// <summary>Checks if 'this' tag describes the same formatting options as 'obj'</summary>
  2377. public override bool Equals(object obj) {
  2378. LineTag other;
  2379. if (obj == null) {
  2380. return false;
  2381. }
  2382. if (!(obj is LineTag)) {
  2383. return false;
  2384. }
  2385. if (obj == this) {
  2386. return true;
  2387. }
  2388. other = (LineTag)obj;
  2389. if (this.font.Equals(other.font) && this.color.Equals(other.color)) { // FIXME add checking for things like link or type later
  2390. return true;
  2391. }
  2392. return false;
  2393. }
  2394. public override int GetHashCode() {
  2395. return base.GetHashCode ();
  2396. }
  2397. public override string ToString() {
  2398. return "Tag starts at index " + this.start + "length " + this.length + " text: " + this.line.Text.Substring(this.start-1, this.length) + "Font " + this.font.ToString();
  2399. }
  2400. #endregion // Internal Methods
  2401. }
  2402. }