//GLineManipulatorなどのためのコンストラクタ internal GLine(char[] data, int dataLength, GWord firstWord) { _text = data; _displayLength = dataLength; _firstWord = firstWord; _id = -1; }
/// 引数と同じ内容で初期化する。lineの内容は破壊されない。 /// 引数がnullのときは引数なしのコンストラクタと同じ結果になる。 /// <summary> /// <ja> /// 引数と同じ内容で初期化します。 /// </ja> /// <en> /// Initialize same as argument. /// </en> /// </summary> /// <param name="cc"> /// <ja> /// 設定するキャレット位置 /// </ja> /// <en> /// The caret position to set. /// </en> /// </param> /// <param name="line"> /// <ja>コピー元となるGLineオブジェクト</ja> /// <en>GLine object that becomes copy origin</en> /// </param> /// <remarks> /// <ja> /// <paramref name="line"/>がnullのときには、引数なしのコンストラクタと同じ結果になります。 /// </ja> /// <en> /// The same results with the constructor who doesn't have the argument when <paramref name="line"/> is null. /// </en> /// </remarks> public void Load(GLine line, int cc) { if (line == null) //これがnullになっているとしか思えないクラッシュレポートがあった。本来はないはずなんだが... { Clear(80); return; } Clear(line.Length); GWord w = line.FirstWord; _text = line.DuplicateBuffer(_text); int n = 0; while (w != null) { int nextoffset = line.GetNextOffset(w); while (n < nextoffset) { _attrs[n++] = new CharAttr(w.Decoration, w.CharGroup); } w = w.Next; } _eolType = line.EOLType; ExpandBuffer(cc + 1); this.CaretColumn = cc; //' 'で埋めることもあるのでプロパティセットを使う }
/// 文字列、デコレーション、オフセットを指定するコンストラクタ。 public GWord(TextDecoration d, int o, CharGroup chargroup) { Debug.Assert(d != null); _offset = o; _decoration = d; _next = null; _charGroup = chargroup; }
public GLine(int length) { Debug.Assert(length > 0); _text = new char[length]; _displayLength = 0; _firstWord = new GWord(TextDecoration.Default, 0, CharGroup.LatinHankaku); _id = -1; }
internal GWord DeepClone() { GWord w = StandAloneClone(); if (_next != null) { w._next = _next.DeepClone(); } return(w); }
/// <summary> /// <ja> /// データをエクスポートします。 /// </ja> /// <en> /// Export the data. /// </en> /// </summary> /// <returns><ja>エクスポートされたGLineオブジェクト</ja><en>Exported GLine object</en></returns> public GLine Export() { GWord firstWord; GWord lastWord; CharAttr firstAttr = _attrs[0]; if (firstAttr.Decoration == null) { firstAttr.Decoration = TextDecoration.Default; } firstWord = lastWord = new GWord(firstAttr.Decoration, 0, firstAttr.CharGroup); int limit = _text.Length; int offset; if (_text[0] == '\0') { offset = 0; } else { CharAttr prevAttr = firstAttr; for (offset = 1; offset < limit; offset++) { char ch = _text[offset]; if (ch == '\0') { break; } else if (ch == GLine.WIDECHAR_PAD) { continue; } CharAttr attr = _attrs[offset]; if (attr.Decoration != prevAttr.Decoration || attr.CharGroup != prevAttr.CharGroup) { if (attr.Decoration == null) { attr.Decoration = TextDecoration.Default; } GWord w = new GWord(attr.Decoration, offset, attr.CharGroup); lastWord.Next = w; lastWord = w; prevAttr = attr; } } } GLine line = new GLine((char[])_text.Clone(), offset, firstWord); line.EOLType = _eolType; return(line); }
internal int GetNextOffset(GWord word) { if (word.Next == null) { return(_displayLength); } else { return(word.Next.Offset); } }
private void Append(GWord w) { if (_firstWord == null) { _firstWord = w; } else { this.LastWord.Next = w; } }
public static GLine CreateSimpleGLine(string text, TextDecoration dec) { char[] buff = new char[text.Length * 2]; int offset = 0; int start = 0; CharGroup prevType = CharGroup.LatinHankaku; GWord firstWord = null; GWord lastWord = null; for (int i = 0; i < text.Length; i++) { char originalChar = text[i]; char privateChar = Unicode.ToPrivate(originalChar); CharGroup nextType = Unicode.GetCharGroup(privateChar); int size = CharGroupUtil.GetColumnsPerCharacter(nextType); if (nextType != prevType) { if (offset > start) { GWord newWord = new GWord(dec, start, prevType); if (lastWord == null) { firstWord = lastWord = newWord; } else { lastWord.Next = newWord; lastWord = newWord; } } prevType = nextType; start = offset; } buff[offset++] = originalChar; if (size == 2) { buff[offset++] = WIDECHAR_PAD; } } GWord w = new GWord(dec, start, prevType); if (lastWord == null) { firstWord = w; } else { lastWord.Next = w; } return(new GLine(buff, offset, firstWord)); }
public void Clear(TextDecoration dec) { TextDecoration fillDec = (dec != null) ? dec.RetainBackColor() : TextDecoration.Default; char fill = fillDec.IsDefault ? '\0' : ' '; // 色指定付きのことがあるのでスペース for (int i = 0; i < _text.Length; i++) { _text[i] = fill; } _displayLength = fillDec.IsDefault ? 0 : _text.Length; _firstWord = new GWord(fillDec, 0, CharGroup.LatinHankaku); }
public static GLine CreateSimpleGLine(string text, TextDecoration dec) { char[] buff = new char[text.Length * 2]; int offset = 0; int start = 0; CharGroup prevType = CharGroup.LatinHankaku; GWord firstWord = null; GWord lastWord = null; for (int i = 0; i < text.Length; i++) { char originalChar = text[i]; char privateChar = Unicode.ToPrivate(originalChar); CharGroup nextType = Unicode.GetCharGroup(privateChar); int size = CharGroupUtil.GetColumnsPerCharacter(nextType); if (nextType != prevType) { if (offset > start) { GWord newWord = new GWord(dec, start, prevType); if (lastWord == null) { firstWord = lastWord = newWord; } else { lastWord.Next = newWord; lastWord = newWord; } } prevType = nextType; start = offset; } buff[offset++] = originalChar; if (size == 2) buff[offset++] = WIDECHAR_PAD; } GWord w = new GWord(dec, start, prevType); if (lastWord == null) { firstWord = w; } else { lastWord.Next = w; } return new GLine(buff, offset, firstWord); }
/// <summary> /// Invert text attribute at the specified position. /// </summary> /// <remarks> /// <para>If doInvert was false, only splitting of GWords will be performed. /// It is required to avoid problem when the text which conatins blinking cursor is drawn by DrawWord().</para> /// <para>DrawWord() draws contiguous characters at once, /// and the character pitch depends how the character in the font was designed.</para> /// <para>By split GWord even if inversion is not required, /// the position of a character of the blinking cursor will be constant.</para> /// </remarks> /// <param name="index">Column index to invert.</param> /// <param name="doInvert">Whether inversion is really applied.</param> /// <param name="color">Background color of the inverted character.</param> internal void InvertCharacter(int index, bool doInvert, Color color) { //��Ƀf�[�^�̂���Ƃ������̈ʒu��w�肳�ꂽ��o�b�t�@��L���Ă��� if (index >= _displayLength) { int prevLength = _displayLength; ExpandBuffer(index + 1); for (int i = prevLength; i < index + 1; i++) _text[i] = ' '; _displayLength = index + 1; this.LastWord.Next = new GWord(TextDecoration.Default, prevLength, CharGroup.LatinHankaku); } if (_text[index] == WIDECHAR_PAD) index--; GWord prev = null; GWord word = _firstWord; int nextoffset = 0; while (word != null) { nextoffset = GetNextOffset(word); if (word.Offset <= index && index < nextoffset) { GWord next = word.Next; //�L�����b�g�̔��] TextDecoration inv_dec = word.Decoration; if (doInvert) inv_dec = inv_dec.GetInvertedCopyForCaret(color); //GWord�͍ő�R��(head:index�̑O�Amiddle:index�Atail:index�̎�)�ɕ�������� GWord head = word.Offset < index ? new GWord(word.Decoration, word.Offset, word.CharGroup) : null; GWord mid = new GWord(inv_dec, index, word.CharGroup); int nextIndex = index + CharGroupUtil.GetColumnsPerCharacter(word.CharGroup); GWord tail = nextIndex < nextoffset ? new GWord(word.Decoration, nextIndex, word.CharGroup) : null; //�A�� head,tail��null�̂��Ƃ����̂ł�₱���� List<GWord> list = new List<GWord>(3); if (head != null) { list.Add(head); head.Next = mid; } list.Add(mid); mid.Next = tail == null ? next : tail; if (tail != null) list.Add(tail); //�O��Ƃ̘A�� if (prev == null) _firstWord = list[0]; else prev.Next = list[0]; list[list.Count - 1].Next = next; break; } prev = word; word = word.Next; } }
/// <summary> /// Invert text attribute at the specified position. /// </summary> /// <remarks> /// <para>If doInvert was false, only splitting of GWords will be performed. /// It is required to avoid problem when the text which conatins blinking cursor is drawn by DrawWord().</para> /// <para>DrawWord() draws contiguous characters at once, /// and the character pitch depends how the character in the font was designed.</para> /// <para>By split GWord even if inversion is not required, /// the position of a character of the blinking cursor will be constant.</para> /// </remarks> /// <param name="index">Column index to invert.</param> /// <param name="doInvert">Whether inversion is really applied.</param> /// <param name="color">Background color of the inverted character.</param> internal void InvertCharacter(int index, bool doInvert, Color color) { //先にデータのあるところより先の位置を指定されたらバッファを広げておく if (index >= _displayLength) { int prevLength = _displayLength; ExpandBuffer(index + 1); for (int i = prevLength; i < index + 1; i++) _text[i] = ' '; _displayLength = index + 1; this.LastWord.Next = new GWord(TextDecoration.Default, prevLength, CharGroup.LatinHankaku); } if (_text[index] == WIDECHAR_PAD) index--; GWord prev = null; GWord word = _firstWord; int nextoffset = 0; while (word != null) { nextoffset = GetNextOffset(word); if (word.Offset <= index && index < nextoffset) { GWord next = word.Next; //キャレットの反転 TextDecoration inv_dec = word.Decoration; if (doInvert) inv_dec = inv_dec.GetInvertedCopyForCaret(color); //GWordは最大3つ(head:indexの前、middle:index、tail:indexの次)に分割される GWord head = word.Offset < index ? new GWord(word.Decoration, word.Offset, word.CharGroup) : null; GWord mid = new GWord(inv_dec, index, word.CharGroup); int nextIndex = index + CharGroupUtil.GetColumnsPerCharacter(word.CharGroup); GWord tail = nextIndex < nextoffset ? new GWord(word.Decoration, nextIndex, word.CharGroup) : null; //連結 head,tailはnullのこともあるのでややこしい List<GWord> list = new List<GWord>(3); if (head != null) { list.Add(head); head.Next = mid; } list.Add(mid); mid.Next = tail == null ? next : tail; if (tail != null) list.Add(tail); //前後との連結 if (prev == null) _firstWord = list[0]; else prev.Next = list[0]; list[list.Count - 1].Next = next; break; } prev = word; word = word.Next; } }
/// <summary> /// Clone this instance that text attributes in the specified range are inverted. /// </summary> /// <param name="from">start column index of the range. (inclusive)</param> /// <param name="to">end column index of the range. (exclusive)</param> /// <returns>new instance</returns> internal GLine CreateInvertedClone(int from, int to) { ExpandBuffer(Math.Max(from + 1, to)); //激しくリサイズしたときなどにこの条件が満たせないことがある Debug.Assert(from >= 0 && from < _text.Length); if (from < _text.Length && _text[from] == WIDECHAR_PAD) from--; if (to < _text.Length && _text[to] == WIDECHAR_PAD) to++; const int PHASE_LEFT = 0; const int PHASE_MIDDLE = 1; const int PHASE_RIGHT = 2; int phase = PHASE_LEFT; int inverseIndex = from; GWord first = null; GWord last = null; for (GWord word = _firstWord; word != null; word = word.Next) { TextDecoration originalDecoration = word.Decoration; if (originalDecoration == null) originalDecoration = TextDecoration.Default; int wordStart = word.Offset; int wordEnd = GetNextOffset(word); do { GWord newWord; if (phase == PHASE_RIGHT || inverseIndex < wordStart || wordEnd <= inverseIndex) { TextDecoration newDec = (phase == PHASE_MIDDLE) ? originalDecoration.GetInvertedCopy() : originalDecoration; newWord = new GWord(newDec, wordStart, word.CharGroup); wordStart = wordEnd; } else { TextDecoration leftDec = (phase == PHASE_LEFT) ? originalDecoration : originalDecoration.GetInvertedCopy(); if (wordStart < inverseIndex) newWord = new GWord(leftDec, wordStart, word.CharGroup); else newWord = null; wordStart = inverseIndex; // update phase if (phase == PHASE_LEFT) { phase = PHASE_MIDDLE; inverseIndex = to; } else if (phase == PHASE_MIDDLE) { phase = PHASE_RIGHT; } } // append new GWord to the list. if (newWord != null) { if (last == null) first = newWord; else last.Next = newWord; last = newWord; } } while (wordStart < wordEnd); } GLine ret = new GLine((char[])_text.Clone(), _displayLength, first); ret.ID = _id; ret.EOLType = _eolType; return ret; }
private void Append(GWord w) { if (_firstWord == null) _firstWord = w; else this.LastWord.Next = w; }
internal int GetNextOffset(GWord word) { if (word.Next == null) return _displayLength; else return word.Next.Offset; }
public void Clear(TextDecoration dec) { TextDecoration fillDec = (dec != null) ? dec.RetainBackColor() : TextDecoration.Default; char fill = fillDec.IsDefault ? '\0' : ' '; // 色指定付きのことがあるのでスペース for (int i = 0; i < _text.Length; i++) _text[i] = fill; _displayLength = fillDec.IsDefault ? 0 : _text.Length; _firstWord = new GWord(fillDec, 0, CharGroup.LatinHankaku); }
/// <summary> /// <ja> /// データをエクスポートします。 /// </ja> /// <en> /// Export the data. /// </en> /// </summary> /// <returns><ja>エクスポートされたGLineオブジェクト</ja><en>Exported GLine object</en></returns> public GLine Export() { GWord firstWord; GWord lastWord; CharAttr firstAttr = _attrs[0]; if (firstAttr.Decoration == null) firstAttr.Decoration = TextDecoration.Default; firstWord = lastWord = new GWord(firstAttr.Decoration, 0, firstAttr.CharGroup); int limit = _text.Length; int offset; if (_text[0] == '\0') { offset = 0; } else { CharAttr prevAttr = firstAttr; for (offset = 1; offset < limit; offset++) { char ch = _text[offset]; if (ch == '\0') break; else if (ch == GLine.WIDECHAR_PAD) continue; CharAttr attr = _attrs[offset]; if (attr.Decoration != prevAttr.Decoration || attr.CharGroup != prevAttr.CharGroup) { if (attr.Decoration == null) attr.Decoration = TextDecoration.Default; GWord w = new GWord(attr.Decoration, offset, attr.CharGroup); lastWord.Next = w; lastWord = w; prevAttr = attr; } } } GLine line = new GLine((char[])_text.Clone(), offset, firstWord); line.EOLType = _eolType; return line; }
internal void Render(IntPtr hdc, RenderProfile prof, Color baseBackColor, int x, int y) { if (_text.Length == 0 || _text[0] == '\0') { return; //何も描かなくてよい。これはよくあるケース } float fx0 = (float)x; float fx1 = fx0; int y1 = y; int y2 = y1 + (int)prof.Pitch.Height; float pitch = prof.Pitch.Width; int defaultBackColorArgb = baseBackColor.ToArgb(); Win32.SetBkMode(hdc, Win32.TRANSPARENT); GWord word = _firstWord; while (word != null) { TextDecoration dec = word.Decoration; IntPtr hFont = prof.CalcHFONT_NoUnderline(dec, word.CharGroup); Win32.SelectObject(hdc, hFont); uint foreColorRef = DrawUtil.ToCOLORREF(prof.CalcTextColor(dec)); Win32.SetTextColor(hdc, foreColorRef); Color bkColor = prof.CalcBackColor(dec); bool isOpaque = (bkColor.ToArgb() != defaultBackColorArgb); if (isOpaque) { uint bkColorRef = DrawUtil.ToCOLORREF(bkColor); Win32.SetBkColor(hdc, bkColorRef); } int nextOffset = GetNextOffset(word); float fx2 = fx0 + pitch * nextOffset; if (prof.CalcBold(dec) || CharGroupUtil.IsCJK(word.CharGroup)) { // It is not always true that width of a character in the CJK font is twice of a character in the ASCII font. // Characters are drawn one by one to adjust pitch. int step = CharGroupUtil.GetColumnsPerCharacter(word.CharGroup); float charPitch = pitch * step; int offset = word.Offset; float fx = fx1; if (isOpaque) { // If background fill is required, we call ExtTextOut() with ETO_OPAQUE to draw the first character. if (offset < nextOffset) { Win32.RECT rect = new Win32.RECT((int)fx1, y1, (int)fx2, y2); char ch = _text[offset]; Debug.Assert(ch != GLine.WIDECHAR_PAD); unsafe { Win32.ExtTextOut(hdc, rect.left, rect.top, Win32.ETO_OPAQUE, &rect, &ch, 1, null); } } offset += step; fx += charPitch; } for (; offset < nextOffset; offset += step) { char ch = _text[offset]; Debug.Assert(ch != GLine.WIDECHAR_PAD); unsafe { Win32.ExtTextOut(hdc, (int)fx, y1, 0, null, &ch, 1, null); } fx += charPitch; } } else { int offset = word.Offset; int displayLength = nextOffset - offset; if (isOpaque) { Win32.RECT rect = new Win32.RECT((int)fx1, y1, (int)fx2, y2); unsafe { fixed(char *p = &_text[offset]) { Win32.ExtTextOut(hdc, rect.left, rect.top, Win32.ETO_OPAQUE, &rect, p, displayLength, null); } } } else { unsafe { fixed(char *p = &_text[offset]) { Win32.ExtTextOut(hdc, (int)fx1, y1, 0, null, p, displayLength, null); } } } } if (dec.Underline) { DrawUnderline(hdc, foreColorRef, (int)fx1, y2 - 1, (int)fx2 - (int)fx1); } fx1 = fx2; word = word.Next; } }
/// <summary> /// Invert text attribute at the specified position. /// </summary> /// <remarks> /// <para>If doInvert was false, only splitting of GWords will be performed. /// It is required to avoid problem when the text which conatins blinking cursor is drawn by DrawWord().</para> /// <para>DrawWord() draws contiguous characters at once, /// and the character pitch depends how the character in the font was designed.</para> /// <para>By split GWord even if inversion is not required, /// the position of a character of the blinking cursor will be constant.</para> /// </remarks> /// <param name="index">Column index to invert.</param> /// <param name="doInvert">Whether inversion is really applied.</param> /// <param name="color">Background color of the inverted character.</param> internal void InvertCharacter(int index, bool doInvert, Color color) { //先にデータのあるところより先の位置を指定されたらバッファを広げておく if (index >= _displayLength) { int prevLength = _displayLength; ExpandBuffer(index + 1); for (int i = prevLength; i < index + 1; i++) { _text[i] = ' '; } _displayLength = index + 1; this.LastWord.Next = new GWord(TextDecoration.Default, prevLength, CharGroup.LatinHankaku); } if (_text[index] == WIDECHAR_PAD) { index--; } GWord prev = null; GWord word = _firstWord; int nextoffset = 0; while (word != null) { nextoffset = GetNextOffset(word); if (word.Offset <= index && index < nextoffset) { GWord next = word.Next; //キャレットの反転 TextDecoration inv_dec = word.Decoration; if (doInvert) { inv_dec = inv_dec.GetInvertedCopyForCaret(color); } //GWordは最大3つ(head:indexの前、middle:index、tail:indexの次)に分割される GWord head = word.Offset < index ? new GWord(word.Decoration, word.Offset, word.CharGroup) : null; GWord mid = new GWord(inv_dec, index, word.CharGroup); int nextIndex = index + CharGroupUtil.GetColumnsPerCharacter(word.CharGroup); GWord tail = nextIndex < nextoffset ? new GWord(word.Decoration, nextIndex, word.CharGroup) : null; //連結 head,tailはnullのこともあるのでややこしい List <GWord> list = new List <GWord>(3); if (head != null) { list.Add(head); head.Next = mid; } list.Add(mid); mid.Next = tail == null ? next : tail; if (tail != null) { list.Add(tail); } //前後との連結 if (prev == null) { _firstWord = list[0]; } else { prev.Next = list[0]; } list[list.Count - 1].Next = next; break; } prev = word; word = word.Next; } }
/// <summary> /// Clone this instance that text attributes in the specified range are inverted. /// </summary> /// <param name="from">start column index of the range. (inclusive)</param> /// <param name="to">end column index of the range. (exclusive)</param> /// <returns>new instance</returns> internal GLine CreateInvertedClone(int from, int to) { ExpandBuffer(Math.Max(from + 1, to)); //激しくリサイズしたときなどにこの条件が満たせないことがある Debug.Assert(from >= 0 && from < _text.Length); if (from < _text.Length && _text[from] == WIDECHAR_PAD) { from--; } if (to < _text.Length && _text[to] == WIDECHAR_PAD) { to++; } const int PHASE_LEFT = 0; const int PHASE_MIDDLE = 1; const int PHASE_RIGHT = 2; int phase = PHASE_LEFT; int inverseIndex = from; GWord first = null; GWord last = null; for (GWord word = _firstWord; word != null; word = word.Next) { TextDecoration originalDecoration = word.Decoration; if (originalDecoration == null) { originalDecoration = TextDecoration.Default; } int wordStart = word.Offset; int wordEnd = GetNextOffset(word); do { GWord newWord; if (phase == PHASE_RIGHT || inverseIndex < wordStart || wordEnd <= inverseIndex) { TextDecoration newDec = (phase == PHASE_MIDDLE) ? originalDecoration.GetInvertedCopy() : originalDecoration; newWord = new GWord(newDec, wordStart, word.CharGroup); wordStart = wordEnd; } else { TextDecoration leftDec = (phase == PHASE_LEFT) ? originalDecoration : originalDecoration.GetInvertedCopy(); if (wordStart < inverseIndex) { newWord = new GWord(leftDec, wordStart, word.CharGroup); } else { newWord = null; } wordStart = inverseIndex; // update phase if (phase == PHASE_LEFT) { phase = PHASE_MIDDLE; inverseIndex = to; } else if (phase == PHASE_MIDDLE) { phase = PHASE_RIGHT; } } // append new GWord to the list. if (newWord != null) { if (last == null) { first = newWord; } else { last.Next = newWord; } last = newWord; } } while (wordStart < wordEnd); } GLine ret = new GLine((char[])_text.Clone(), _displayLength, first); ret.ID = _id; ret.EOLType = _eolType; return(ret); }