TextControl.cs 66 KB

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