using System.Collections.Generic; using System; using System.Text; using System.Drawing; namespace LB_VisionControls { /// /// Line of text /// public class Line : IList { protected List chars; public string FoldingStartMarker { get; set; } public string FoldingEndMarker { get; set; } /// /// Text of line was changed /// public bool IsChanged { get; set; } /// /// Time of last visit of caret in this line /// /// This property can be used for forward/backward navigating public DateTime LastVisit { get; set; } /// /// Background brush. /// public Brush BackgroundBrush { get; set; } /// /// Unique ID /// public int UniqueId { get; private set; } /// /// Count of needed start spaces for AutoIndent /// public int AutoIndentSpacesNeededCount { get; internal set; } internal Line(int uid) { this.UniqueId = uid; chars = new List(); } /// /// Clears style of chars, delete folding markers /// public void ClearStyle(StyleIndex styleIndex) { FoldingStartMarker = null; FoldingEndMarker = null; for (int i = 0; i < Count; i++) { Char c = this[i]; c.style &= ~styleIndex; this[i] = c; } } /// /// Text of the line /// public virtual string Text { get { StringBuilder sb = new StringBuilder(Count); foreach (Char c in this) sb.Append(c.c); return sb.ToString(); } } /// /// Clears folding markers /// public void ClearFoldingMarkers() { FoldingStartMarker = null; FoldingEndMarker = null; } /// /// Count of start spaces /// public int StartSpacesCount { get { int spacesCount = 0; for (int i = 0; i < Count; i++) if (this[i].c == ' ') spacesCount++; else break; return spacesCount; } } public int IndexOf(Char item) { return chars.IndexOf(item); } public void Insert(int index, Char item) { chars.Insert(index, item); } public void RemoveAt(int index) { chars.RemoveAt(index); } public Char this[int index] { get { return chars[index]; } set { chars[index] = value; } } public void Add(Char item) { chars.Add(item); } public void Clear() { chars.Clear(); } public bool Contains(Char item) { return chars.Contains(item); } public void CopyTo(Char[] array, int arrayIndex) { chars.CopyTo(array, arrayIndex); } /// /// Chars count /// public int Count { get { return chars.Count; } } public bool IsReadOnly { get { return false; } } public bool Remove(Char item) { return chars.Remove(item); } public IEnumerator GetEnumerator() { return chars.GetEnumerator(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return chars.GetEnumerator() as System.Collections.IEnumerator; } public virtual void RemoveRange(int index, int count) { if (index >= Count) return; chars.RemoveRange(index, Math.Min(Count - index, count)); } public virtual void TrimExcess() { chars.TrimExcess(); } public virtual void AddRange(IEnumerable collection) { chars.AddRange(collection); } } public struct LineInfo { List cutOffPositions; //Y coordinate of line on screen internal int startY;// = -1; /// /// Visible state /// public VisibleState VisibleState; public LineInfo(int startY) { cutOffPositions = null; VisibleState = VisibleState.Visible; this.startY = startY; } /// /// Positions for wordwrap cutoffs /// public List CutOffPositions { get { if (cutOffPositions == null) cutOffPositions = new List(); return cutOffPositions; } } /// /// Count of wordwrap string count for this line /// public int WordWrapStringsCount { get { switch (VisibleState) { case VisibleState.Visible: if (cutOffPositions == null) return 1; else return cutOffPositions.Count + 1; case VisibleState.Hidden: return 0; case VisibleState.StartOfHiddenBlock: return 1; } return 0; } } internal int GetWordWrapStringStartPosition(int iWordWrapLine) { return iWordWrapLine == 0 ? 0 : CutOffPositions[iWordWrapLine - 1]; } internal int GetWordWrapStringFinishPosition(int iWordWrapLine, Line line) { if (WordWrapStringsCount <= 0) return 0; return iWordWrapLine == WordWrapStringsCount - 1 ? line.Count - 1 : CutOffPositions[iWordWrapLine] - 1; } /// /// Gets index of wordwrap string for given char position /// public int GetWordWrapStringIndex(int iChar) { if (cutOffPositions == null || cutOffPositions.Count == 0) return 0; for (int i = 0; i < cutOffPositions.Count; i++) if (cutOffPositions[i] >/*>=*/ iChar) return i; return cutOffPositions.Count; } /// /// Calculates wordwrap cutoffs /// internal void CalcCutOffs(int maxCharsPerLine, bool allowIME, bool charWrap, Line line) { int segmentLength = 0; int cutOff = 0; CutOffPositions.Clear(); for (int i = 0; i < line.Count; i++) { char c = line[i].c; if (charWrap) { //char wrapping cutOff = Math.Min(i + 1, line.Count - 1); } else { //word wrapping if (allowIME && isCJKLetter(c))//in CJK languages cutoff can be in any letter { cutOff = i; } else if (!char.IsLetterOrDigit(c) && c != '_') cutOff = Math.Min(i + 1, line.Count - 1); } segmentLength++; if (segmentLength == maxCharsPerLine) { if (cutOff == 0 || (cutOffPositions.Count > 0 && cutOff == cutOffPositions[cutOffPositions.Count - 1])) cutOff = i + 1; CutOffPositions.Add(cutOff); segmentLength = 1 + i - cutOff; } } } private bool isCJKLetter(char c) { int code = Convert.ToInt32(c); return (code >= 0x3300 && code <= 0x33FF) || (code >= 0xFE30 && code <= 0xFE4F) || (code >= 0xF900 && code <= 0xFAFF) || (code >= 0x2E80 && code <= 0x2EFF) || (code >= 0x31C0 && code <= 0x31EF) || (code >= 0x4E00 && code <= 0x9FFF) || (code >= 0x3400 && code <= 0x4DBF) || (code >= 0x3200 && code <= 0x32FF) || (code >= 0x2460 && code <= 0x24FF) || (code >= 0x3040 && code <= 0x309F) || (code >= 0x2F00 && code <= 0x2FDF) || (code >= 0x31A0 && code <= 0x31BF) || (code >= 0x4DC0 && code <= 0x4DFF) || (code >= 0x3100 && code <= 0x312F) || (code >= 0x30A0 && code <= 0x30FF) || (code >= 0x31F0 && code <= 0x31FF) || (code >= 0x2FF0 && code <= 0x2FFF) || (code >= 0x1100 && code <= 0x11FF) || (code >= 0xA960 && code <= 0xA97F) || (code >= 0xD7B0 && code <= 0xD7FF) || (code >= 0x3130 && code <= 0x318F) || (code >= 0xAC00 && code <= 0xD7AF); } } public enum VisibleState : byte { Visible, StartOfHiddenBlock, Hidden } public enum IndentMarker { None, Increased, Decreased } }