TextControl.cs 68 KB

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