public GLine Export() { GWord w = new GWord(_decorations[0] == null? TextDecoration.ClonedDefault() : _decorations[0], 0, GLine.CalcCharGroup(_text[0])); GLine line = new GLine(_text, w); line.EOLType = _eolType; int m = _text.Length; for (int offset = 1; offset < m; offset++) { char ch = _text[offset]; if (ch == '\0') { break; } else if (ch == GLine.WIDECHAR_PAD) { continue; } TextDecoration dec = _decorations[offset]; if (_decorations[offset - 1] != dec || w.CharGroup != GLine.CalcCharGroup(ch)) { if (dec == null) { dec = TextDecoration.ClonedDefault(); //!!本当はここがnullになっているのはありえないはず。後で調査すること } GWord ww = new GWord(dec, offset, GLine.CalcCharGroup(ch)); w.Next = ww; w = ww; } } return(line); }
private void DrawWord(IntPtr hdc, int x, int y, GWord word) { unsafe { int len; if (word.CharGroup == CharGroup.SingleByte) { fixed(char *p = &_text[0]) { len = WordNextOffset(word) - word.Offset; Win32.TextOut(hdc, x, y, p + word.Offset, len); //Win32.ExtTextOut(hdc, x, y, 4, null, p+word.Offset, len, null); } } else { string t = WordText(word); fixed(char *p = t) { len = t.Length; Win32.TextOut(hdc, x, y, p, len); //Win32.ExtTextOut(hdc, x, y, 4, null, p, len, null); } } } }
public GLine(int length) { Debug.Assert(length > 0); _text = new char[length]; _firstWord = new GWord(TextDecoration.ClonedDefault(), 0, CharGroup.SingleByte); _id = -1; }
private void DrawStringByOneChar2(IntPtr hdc, GWord word, int display_length, IntPtr bkbrush, float fx, int y, RenderProfile prof) { float pitch = prof.Pitch.Width; int nextoffset = WordNextOffset(word); if (bkbrush != IntPtr.Zero) //これがないと日本語文字ピッチが小さいとき選択時のすきまができる場合がある { Win32.RECT rect = new Win32.RECT(); rect.left = (int)fx; rect.top = y; rect.right = (int)(fx + pitch * display_length); rect.bottom = y + (int)prof.Pitch.Height; Win32.FillRect(hdc, ref rect, bkbrush); } for (int i = word.Offset; i < nextoffset; i++) { char ch = _text[i]; if (ch == '\0') { break; } if (ch == GLine.WIDECHAR_PAD) { continue; } unsafe { Win32.TextOut(hdc, (int)fx, y, &ch, 1); } fx += pitch * CalcDisplayLength(ch); } }
private string WordText(GWord word) { int nextoffset = WordNextOffset(word); if (nextoffset == 0) { return(""); } else { Debug.Assert(nextoffset - word.Offset >= 0); if (word.CharGroup == CharGroup.SingleByte) { return(new string(_text, word.Offset, nextoffset - word.Offset)); } else { char[] buf = new char[256]; int o = word.Offset, i = 0; while (o < nextoffset) { char ch = _text[o]; if (ch != GLine.WIDECHAR_PAD) { buf[i++] = ch; } o++; } return(new string(buf, 0, i)); } } }
internal int WordNextOffset(GWord word) { //ここは呼ばれることがとても多いのでキャッシュを設ける int cache = word.nextOffsetCache; if (cache < 0) { if (word.Next == null) { int i = _text.Length - 1; while (i >= 0 && _text[i] == '\0') { i--; } word.nextOffsetCache = i + 1; return(i + 1); } else { word.nextOffsetCache = word.Next.Offset; return(word.Next.Offset); } } else { return(cache); } }
public GLine(int length) { Debug.Assert(length>0); _text = new char[length]; _firstWord = new GWord(TextDecoration.ClonedDefault(), 0, CharGroup.SingleByte); _id = -1; }
/// <summary> /// 引数と同じ内容で初期化する。lineの内容は破壊されない。 /// 引数がnullのときは引数なしのコンストラクタと同じ結果になる。 /// </summary> public void Load(GLine line, int cc) { if (line == null) //これがnullになっているとしか思えないクラッシュレポートがあった。本来はないはずなんだが... { Clear(80); return; } Clear(line.Length); GWord w = line.FirstWord; Debug.Assert(line.Text.Length == _text.Length); Array.Copy(line.Text, 0, _text, 0, _text.Length); int n = 0; while (w != null) { int nextoffset = line.WordNextOffset(w); while (n < nextoffset) { _decorations[n++] = w.Decoration; } w = w.Next; } _eolType = line.EOLType; ExpandBuffer(cc + 1); this.CaretColumn = cc; //' 'で埋めることもあるのでプロパティセットを使う }
public void Clear() { for (int i = 0; i < _text.Length; i++) { _text[i] = '\0'; } _firstWord = new GWord(TextDecoration.ClonedDefault(), 0, CharGroup.SingleByte); }
internal void Clear(TextDecoration dec) { for (int i = 0; i < _text.Length; i++) { _text[i] = ' '; } _firstWord = new GWord(dec, 0, CharGroup.SingleByte); }
/// <summary> /// 文字列、デコレーション、オフセットを指定するコンストラクタ。TypeはNormalになる。 /// </summary> internal GWord(TextDecoration d, int o, CharGroup chargroup) { Debug.Assert(d != null); _offset = o; _decoration = d; _next = null; _charGroup = chargroup; nextOffsetCache = -1; displayLengthCache = -1; }
public GWord DeepClone() { GWord w = new GWord(_decoration, _offset, _charGroup); if (_next != null) { w._next = _next.DeepClone(); } return(w); }
internal void Append(GWord w) { if (_firstWord == null) { _firstWord = w; } else { this.LastWord.Next = w; } }
/* * //!!.NETの描画バグ * Graphics#DrawStringに渡す文字列は、スペースのみで構成されていると無視されてしまうようだ。 * これだとアンダーラインだけを引きたいときなどに困る。調べたところ、末尾にタブをつけるとこの仕組みをだますことができることが判明 * .NETの次のバージョンでは直っていることを期待 */ private string WordTextForFuckingDotNet(GWord word) { int nextoffset = WordNextOffset(word); if (nextoffset == 0) { return(""); } else { bool last_is_space = false; Debug.Assert(nextoffset - word.Offset >= 0); if (word.CharGroup == CharGroup.SingleByte) { last_is_space = _text[nextoffset - 1] == ' '; if (last_is_space) { return(new string(_text, word.Offset, nextoffset - word.Offset) + '\t'); } else { return(new string(_text, word.Offset, nextoffset - word.Offset)); } } else { char[] buf = new char[256]; int o = word.Offset, i = 0; while (o < nextoffset) { char ch = _text[o]; if (ch != GLine.WIDECHAR_PAD) { last_is_space = ch == ' '; buf[i++] = ch; } o++; } if (last_is_space) { buf[i++] = (char)'\t'; } return(new string(buf, 0, i)); } } }
private int WordDisplayLength(GWord word) { //ここは呼ばれることがとても多いのでキャッシュを設ける int cache = word.displayLengthCache; if (cache < 0) { int nextoffset = WordNextOffset(word); int l = nextoffset - word.Offset; word.displayLengthCache = l; return(l); } else { return(cache); } }
//index�̈ʒu�̕\���]�����V����GLine��Ԃ� //inverse��false���ƁAGWord�̕����͂��邪Decoration�̔��]�͂��Ȃ��B�q�N�q�N���̑Ώ��Ƃ��Ď����B internal GLine InverseCaret(int index, bool inverse, bool underline) { ExpandBuffer(index+1); if(_text[index]==WIDECHAR_PAD) index--; GLine ret = new GLine(_text, null); ret.ID = _id; ret.EOLType = _eolType; GWord w = _firstWord; int nextoffset = 0; while(w!=null) { nextoffset = WordNextOffset(w); if(w.Offset<=index && index<nextoffset) { //!!tail���珇�ɘA���������������͂悢 if(w.Offset<index) { GWord head = new GWord(w.Decoration, w.Offset, w.CharGroup); ret.Append(head); } TextDecoration dec = (TextDecoration)w.Decoration.Clone(); if(inverse) { //�F���L�����b�g�̃T�|�[�g dec.ToCaretStyle(); } if(underline) dec.Underline = true; GWord mid = new GWord(dec, index, w.CharGroup); ret.Append(mid); if(index+CalcDisplayLength(_text[index]) < nextoffset) { GWord tail = new GWord(w.Decoration, index+CalcDisplayLength(_text[index]), w.CharGroup); ret.Append(tail); } } else ret.Append(w.StandAloneClone()); w = w.Next; } //!!���́A�L�����b�g�ʒu�ɃX�y�[�X������̂�Inverse�Ƃ͈Ⴄ�����ł��邩�番�����邱�� if(nextoffset<=index) { while(nextoffset<=index) { Debug.Assert(nextoffset < ret.Text.Length); ret.Text[nextoffset++] = ' '; } TextDecoration dec = TextDecoration.ClonedDefault(); if(inverse) { dec.ToCaretStyle(); } if(underline) dec.Underline = true; ret.Append(new GWord(dec, index, CharGroup.SingleByte)); } return ret; }
private int WordDisplayLength(GWord word) { //�����͌Ă�邱�Ƃ��ƂĂ�����̂ŃL���b�V����݂��� int cache = word.displayLengthCache; if(cache < 0) { int nextoffset = WordNextOffset(word); int l = nextoffset - word.Offset; word.displayLengthCache = l; return l; } else return cache; }
internal GLine InverseRange(int from, int to) { ExpandBuffer(Math.Max(from+1,to)); //���������T�C�Y�����Ƃ��Ȃǂɂ��̏�����������Ȃ����Ƃ����� Debug.Assert(from>=0 && from<_text.Length); if(from<_text.Length && _text[from]==WIDECHAR_PAD) from--; if(to>0 && to-1<_text.Length && _text[to-1]==WIDECHAR_PAD) to--; GLine ret = new GLine(_text, null); ret.ID = _id; ret.EOLType = _eolType; //�����̔z���Z�b�g TextDecoration[] dec = new TextDecoration[_text.Length]; GWord w = _firstWord; while(w!=null) { Debug.Assert(w.Decoration!=null); dec[w.Offset] = w.Decoration; w = w.Next; } //���]�J�n�_ TextDecoration original = null; TextDecoration inverse = null; for(int i=from; i>=0; i--) { if(dec[i]!=null) { original = dec[i]; break; } } Debug.Assert(original!=null); inverse = (TextDecoration)original.Clone(); inverse.Inverse(); dec[from] = inverse; //�͈͂ɓn���Ĕ��]��� for(int i=from+1; i<to; i++) { if(i<dec.Length && dec[i]!=null) { original = dec[i]; inverse = (TextDecoration)original.Clone(); inverse.Inverse(); dec[i] = inverse; } } if(to<dec.Length && dec[to]==null) dec[to] = original; //����ɏ]����GWord���� w = null; for(int i=dec.Length-1; i>=0; i--) { char ch = _text[i]; if(dec[i]!=null && ch!='\0') { int j = i; if(ch==WIDECHAR_PAD) j++; GWord ww = new GWord(dec[i], j, CalcCharGroup(ch)); ww.Next = w; w = ww; } } ret.Append(w); return ret; }
public GWord DeepClone() { GWord w = new GWord(_decoration, _offset, _charGroup); if(_next!=null) w._next = _next.DeepClone(); return w; }
internal void Render(IntPtr hdc, RenderParameter param, RenderProfile prof, int y) { if (_text[0] == '\0') { return; //何も描かなくてよい } float fx = (float)param.TargetRect.Left; RectangleF rect = new RectangleF(); rect.Y = param.TargetRect.Top + y; rect.Height = prof.Pitch.Height; GWord word = _firstWord; while (word != null) { rect.X = fx /*- prof.CharGap*/; //Nativeな描画では不要? rect.Width = param.TargetRect.Right - rect.X; int ix = (int)rect.X; int iy = (int)rect.Y; TextDecoration dec = word.Decoration; //Brush brush = prof.CalcTextBrush(dec); uint forecolorref = DrawUtil.ToCOLORREF(prof.CalcTextColor(dec)); Color bkcolor = prof.CalcBackColor(dec); uint bkcolorref = DrawUtil.ToCOLORREF(bkcolor); IntPtr hfont = prof.CalcHFONT_NoUnderline(dec, word.CharGroup); Win32.SelectObject(hdc, hfont); Win32.SetTextColor(hdc, forecolorref); Win32.SetBkColor(hdc, bkcolorref); Win32.SetBkMode(hdc, bkcolor == prof.BackColor? 1 : 2); //基本背景色と一緒ならTRANSPARENT, 異なればOPAQUE IntPtr bkbrush = bkcolor == prof.BackColor? IntPtr.Zero : Win32.CreateSolidBrush(bkcolorref); int display_length = WordDisplayLength(word); if (word.Decoration == null) //装飾なし //g.DrawString(WordText(word), font, brush, rect); { DrawWord(hdc, ix, iy, word); } else { //if(dec.Bold || (!prof.UsingIdenticalFont && word.CharGroup==CharGroup.TwoBytes)) if (dec.Bold || word.CharGroup == CharGroup.TwoBytes) //同じフォント指定でも日本語が半角の2倍でない場合あり。パフォーマンス問題はクリアされつつあるので確実に1文字ずつ描画 { DrawStringByOneChar2(hdc, word, display_length, bkbrush, rect.X, iy, prof); } else { DrawWord(hdc, ix, iy, word); //いまやアホな描画エンジンの問題からは解放された! } } //Debug.WriteLine("PW="+p.Pitch.Width+",TL="+(pb.Text.Length*p.Pitch.Width)+", real="+g.MeasureString(pb.Text, p.Font).Width); if (dec.Underline) { DrawUnderline(hdc, forecolorref, ix, iy + (int)prof.Pitch.Height - 1, (int)(prof.Pitch.Width * display_length)); } fx += prof.Pitch.Width * display_length; word = word.Next; if (bkbrush != IntPtr.Zero) { Win32.DeleteObject(bkbrush); } } }
internal int WordNextOffset(GWord word) { //�����͌Ă�邱�Ƃ��ƂĂ�����̂ŃL���b�V����݂��� int cache = word.nextOffsetCache; if(cache < 0) { if(word.Next==null) { int i = _text.Length-1; while(i>=0 && _text[i]=='\0') i--; word.nextOffsetCache = i+1; return i+1; } else { word.nextOffsetCache = word.Next.Offset; return word.Next.Offset; } } else return cache; }
private void DrawWord(IntPtr hdc, int x, int y, GWord word) { unsafe { int len; if(word.CharGroup==CharGroup.SingleByte) { fixed(char* p = &_text[0]) { len = WordNextOffset(word) - word.Offset; Win32.TextOut(hdc, x, y, p+word.Offset, len); //Win32.ExtTextOut(hdc, x, y, 4, null, p+word.Offset, len, null); } } else { string t = WordText(word); fixed(char* p = t) { len = t.Length; Win32.TextOut(hdc, x, y, p, len); //Win32.ExtTextOut(hdc, x, y, 4, null, p, len, null); } } } }
private void DrawStringByOneChar2(IntPtr hdc, GWord word, int display_length, IntPtr bkbrush, float fx, int y, RenderProfile prof) { float pitch = prof.Pitch.Width; int nextoffset = WordNextOffset(word); if(bkbrush!=IntPtr.Zero) { //���ꂪ�Ȃ��Ɠ��{�ꕶ���s�b�`���������Ƃ��I����̂����܂��ł���ꍇ������ Win32.RECT rect = new Win32.RECT(); rect.left = (int)fx; rect.top = y; rect.right = (int)(fx + pitch*display_length); rect.bottom = y + (int)prof.Pitch.Height; Win32.FillRect(hdc, ref rect, bkbrush); } for(int i=word.Offset; i<nextoffset; i++) { char ch = _text[i]; if(ch=='\0') break; if(ch==GLine.WIDECHAR_PAD) continue; unsafe { Win32.TextOut(hdc, (int)fx, y, &ch, 1); } fx += pitch * CalcDisplayLength(ch); } }
internal void Clear(TextDecoration dec) { for(int i=0; i<_text.Length; i++) _text[i] = ' '; _firstWord = new GWord(dec, 0, CharGroup.SingleByte); }
/* * //!!.NET�̕`��o�O * Graphics#DrawString�ɓn��������́A�X�y�[�X�݂̂ō\������Ă���Ɩ�������Ă��܂��悤���B * ���ꂾ�ƃA���_�[���C����������������Ƃ��Ȃǂɍ���B���ׂ��Ƃ���A�����Ƀ^�u�����Ƃ��̎d�g�݂���܂����Ƃ��ł��邱�Ƃ����� * .NET�̎��̃o�[�W�����ł͒����Ă��邱�Ƃ���� */ private string WordTextForFuckingDotNet(GWord word) { int nextoffset = WordNextOffset(word); if(nextoffset==0) return ""; else { bool last_is_space = false; Debug.Assert(nextoffset-word.Offset >= 0); if(word.CharGroup==CharGroup.SingleByte) { last_is_space = _text[nextoffset-1]==' '; if(last_is_space) return new string(_text, word.Offset, nextoffset-word.Offset)+'\t'; else return new string(_text, word.Offset, nextoffset-word.Offset); } else { char[] buf = new char[256]; int o = word.Offset, i=0; while(o < nextoffset) { char ch = _text[o]; if(ch!=GLine.WIDECHAR_PAD) { last_is_space = ch=='�@'; buf[i++] = ch; } o++; } if(last_is_space) buf[i++] = (char)'\t'; return new string(buf, 0, i); } } }
public GLine(char[] data, GWord firstWord) { _text = (char[])data.Clone(); _firstWord = firstWord; _id = -1; }
private string WordText(GWord word) { int nextoffset = WordNextOffset(word); if(nextoffset==0) return ""; else { Debug.Assert(nextoffset-word.Offset >= 0); if(word.CharGroup==CharGroup.SingleByte) return new string(_text, word.Offset, nextoffset-word.Offset); else { char[] buf = new char[256]; int o = word.Offset, i=0; while(o < nextoffset) { char ch = _text[o]; if(ch!=GLine.WIDECHAR_PAD) buf[i++] = ch; o++; } return new string(buf, 0, i); } } }
internal void Append(GWord w) { if(_firstWord==null) _firstWord = w; else this.LastWord.Next = w; }
//indexの位置の表示を反転した新しいGLineを返す //inverseがfalseだと、GWordの分割はするがDecorationの反転はしない。ヒクヒク問題の対処として実装。 internal GLine InverseCaret(int index, bool inverse, bool underline) { ExpandBuffer(index + 1); if (_text[index] == WIDECHAR_PAD) { index--; } GLine ret = new GLine(_text, null); ret.ID = _id; ret.EOLType = _eolType; GWord w = _firstWord; int nextoffset = 0; while (w != null) { nextoffset = WordNextOffset(w); if (w.Offset <= index && index < nextoffset) { //!!tailから順に連結した方が効率はよい if (w.Offset < index) { GWord head = new GWord(w.Decoration, w.Offset, w.CharGroup); ret.Append(head); } TextDecoration dec = (TextDecoration)w.Decoration.Clone(); if (inverse) { //色つきキャレットのサポート dec.ToCaretStyle(); } if (underline) { dec.Underline = true; } GWord mid = new GWord(dec, index, w.CharGroup); ret.Append(mid); if (index + CalcDisplayLength(_text[index]) < nextoffset) { GWord tail = new GWord(w.Decoration, index + CalcDisplayLength(_text[index]), w.CharGroup); ret.Append(tail); } } else { ret.Append(w.StandAloneClone()); } w = w.Next; } //!!この、キャレット位置にスペースを入れるのはInverseとは違う処理であるから分離すること if (nextoffset <= index) { while (nextoffset <= index) { Debug.Assert(nextoffset < ret.Text.Length); ret.Text[nextoffset++] = ' '; } TextDecoration dec = TextDecoration.ClonedDefault(); if (inverse) { dec.ToCaretStyle(); } if (underline) { dec.Underline = true; } ret.Append(new GWord(dec, index, CharGroup.SingleByte)); } return(ret); }
public GLine Export() { GWord w = new GWord(_decorations[0]==null? TextDecoration.ClonedDefault() : _decorations[0], 0, GLine.CalcCharGroup(_text[0])); GLine line = new GLine(_text, w); line.EOLType = _eolType; int m = _text.Length; for(int offset=1; offset<m; offset++) { char ch = _text[offset]; if(ch=='\0') break; else if(ch==GLine.WIDECHAR_PAD) continue; TextDecoration dec = _decorations[offset]; if(_decorations[offset-1]!=dec || w.CharGroup!=GLine.CalcCharGroup(ch)) { if(dec==null) dec = TextDecoration.ClonedDefault(); //!!�{���͂�����null�ɂȂ��Ă���̂͂��肦�Ȃ��͂��B��Œ������邱�� GWord ww = new GWord(dec, offset, GLine.CalcCharGroup(ch)); w.Next = ww; w = ww; } } return line; }
/// <summary> /// ������A�f�R���[�V�����A�I�t�Z�b�g��w�肷��R���X�g���N�^�BType��Normal�ɂȂ�B /// </summary> internal GWord(TextDecoration d, int o, CharGroup chargroup) { Debug.Assert(d!=null); _offset = o; _decoration = d; _next = null; _charGroup = chargroup; nextOffsetCache = -1; displayLengthCache = -1; }
internal GLine InverseRange(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 > 0 && to - 1 < _text.Length && _text[to - 1] == WIDECHAR_PAD) { to--; } GLine ret = new GLine(_text, null); ret.ID = _id; ret.EOLType = _eolType; //装飾の配列をセット TextDecoration[] dec = new TextDecoration[_text.Length]; GWord w = _firstWord; while (w != null) { Debug.Assert(w.Decoration != null); dec[w.Offset] = w.Decoration; w = w.Next; } //反転開始点 TextDecoration original = null; TextDecoration inverse = null; for (int i = from; i >= 0; i--) { if (dec[i] != null) { original = dec[i]; break; } } Debug.Assert(original != null); inverse = (TextDecoration)original.Clone(); inverse.Inverse(); dec[from] = inverse; //範囲に渡って反転作業 for (int i = from + 1; i < to; i++) { if (i < dec.Length && dec[i] != null) { original = dec[i]; inverse = (TextDecoration)original.Clone(); inverse.Inverse(); dec[i] = inverse; } } if (to < dec.Length && dec[to] == null) { dec[to] = original; } //これに従ってGWordを作る w = null; for (int i = dec.Length - 1; i >= 0; i--) { char ch = _text[i]; if (dec[i] != null && ch != '\0') { int j = i; if (ch == WIDECHAR_PAD) { j++; } GWord ww = new GWord(dec[i], j, CalcCharGroup(ch)); ww.Next = w; w = ww; } } ret.Append(w); return(ret); }
public void Clear() { for(int i=0; i<_text.Length; i++) _text[i] = '\0'; _firstWord = new GWord(TextDecoration.ClonedDefault(), 0, CharGroup.SingleByte); }