private void BuildTransientDocument(PaintEventArgs e, RenderParameter param)
        {
            Rectangle     clip    = e.ClipRectangle;
            RenderProfile profile = GetRenderProfile();

            _transientLines.Clear();

            //Win32.SystemMetrics sm = GEnv.SystemMetrics;
            //param.TargetRect = new Rectangle(sm.ControlBorderWidth+1, sm.ControlBorderHeight,
            //	this.Width - _VScrollBar.Width - sm.ControlBorderWidth + 8, //この8がない値が正当だが、.NETの文字サイズ丸め問題のため行の最終文字が表示されないことがある。これを回避するためにちょっと増やす
            //	this.Height - sm.ControlBorderHeight);
            param.TargetRect = this.ClientRectangle;

            int offset1 = (int)Math.Floor((clip.Top - BORDER) / (profile.Pitch.Height + profile.LineSpacing));

            if (offset1 < 0)
            {
                offset1 = 0;
            }
            param.LineFrom = offset1;
            int offset2 = (int)Math.Floor((clip.Bottom - BORDER) / (profile.Pitch.Height + profile.LineSpacing));

            if (offset2 < 0)
            {
                offset2 = 0;
            }

            param.LineCount = offset2 - offset1 + 1;
            //Debug.WriteLine(String.Format("{0} {1} ", param.LineFrom, param.LineCount));

            int   topline_id = GetTopLine().ID;
            GLine l          = _document.FindLineOrNull(topline_id + param.LineFrom);

            if (l != null)
            {
                for (int i = param.LineFrom; i < param.LineFrom + param.LineCount; i++)
                {
                    _transientLines.Add(l.Clone()); //TODO クローンはきついよなあ だが描画の方が時間かかるので、その間ロックをしないためには仕方ない点もある
                    l = l.NextLine;
                    if (l == null)
                    {
                        break;
                    }
                }
            }

            //以下、_transientLinesにはparam.LineFromから示される値が入っていることに注意

            //選択領域の描画
            if (!_textSelection.IsEmpty)
            {
                TextSelection.TextPoint from = _textSelection.HeadPoint;
                TextSelection.TextPoint to   = _textSelection.TailPoint;
                l = _document.FindLineOrNull(from.Line);
                GLine t = _document.FindLineOrNull(to.Line);
                if (l != null && t != null)   //本当はlがnullではいけないはずだが、それを示唆するバグレポートがあったので念のため
                {
                    t = t.NextLine;
                    int pos = from.Column; //たとえば左端を越えてドラッグしたときの選択範囲は前行末になるので pos==TerminalWidthとなるケースがある。
                    do
                    {
                        int index = l.ID - (topline_id + param.LineFrom);
                        if (pos >= 0 && pos < l.DisplayLength && index >= 0 && index < _transientLines.Count)
                        {
                            GLine r = null;
                            if (l.ID == to.Line)
                            {
                                if (pos != to.Column)
                                {
                                    r = _transientLines[index].CreateInvertedClone(pos, to.Column);
                                }
                            }
                            else
                            {
                                r = _transientLines[index].CreateInvertedClone(pos, l.DisplayLength);
                            }

                            if (r != null)
                            {
                                _transientLines[index] = r;
                            }
                        }
                        pos = 0; //2行目からの選択は行頭から
                        l   = l.NextLine;
                    } while (l != t);
                }
            }

            AdjustCaret(_caret);
            _caret.Enabled = _caret.Enabled && (param.LineFrom <= _caret.Y && _caret.Y < param.LineFrom + param.LineCount);

            //Caret画面外にあるなら処理はしなくてよい。2番目の条件は、Attach-ResizeTerminalの流れの中でこのOnPaintを実行した場合にTerminalHeight>lines.Countになるケースがあるのを防止するため
            if (_caret.Enabled)
            {
                //ヒクヒク問題のため、キャレットを表示しないときでもこの操作は省けない
                if (_caret.Style == CaretType.Box)
                {
                    int y = _caret.Y - param.LineFrom;
                    if (y >= 0 && y < _transientLines.Count)
                    {
                        _transientLines[y].InvertCharacter(_caret.X, _caret.IsActiveTick, _caret.Color);
                    }
                }
            }
        }
        public override UIHandleResult OnMouseMove(MouseEventArgs args)
        {
            if (args.Button != MouseButtons.Left)
            {
                return(UIHandleResult.Pass);
            }
            TextSelection sel = _viewer.TextSelection;

            if (sel.State == SelectionState.Fixed || sel.State == SelectionState.Empty)
            {
                return(UIHandleResult.Pass);
            }
            //クリックだけでもなぜかMouseDownの直後にMouseMoveイベントが来るのでこのようにしてガード。でないと単発クリックでも選択状態になってしまう
            if (sel.StartX == args.X && sel.StartY == args.Y)
            {
                return(UIHandleResult.Capture);
            }

            CharacterDocument document = _viewer.CharacterDocument;

            lock (document) {
                int   topline_id = _viewer.GetTopLine().ID;
                SizeF pitch = _viewer.GetRenderProfile().Pitch;
                int   row, col;
                _viewer.MousePosToTextPos_AllowNegative(args.X, args.Y, out col, out row);
                int viewheight = (int)Math.Floor(_viewer.ClientSize.Height / pitch.Width);
                int target_id  = topline_id + row;

                GLine target_line             = document.FindLineOrEdge(target_id);
                TextSelection.TextPoint point = sel.ConvertSelectionPosition(target_line, col);

                point.Line = RuntimeUtil.AdjustIntRange(point.Line, document.FirstLineNumber, document.LastLineNumber);

                if (_viewer.VScrollBar.Enabled)   //スクロール可能なときは
                {
                    VScrollBar vsc = _viewer.VScrollBar;
                    if (target_id < topline_id) //前方スクロール
                    {
                        vsc.Value = point.Line - document.FirstLineNumber;
                    }
                    else if (point.Line >= topline_id + vsc.LargeChange)   //後方スクロール
                    {
                        int newval = point.Line - document.FirstLineNumber - vsc.LargeChange + 1;
                        if (newval < 0)
                        {
                            newval = 0;
                        }
                        if (newval > vsc.Maximum - vsc.LargeChange)
                        {
                            newval = vsc.Maximum - vsc.LargeChange + 1;
                        }
                        vsc.Value = newval;
                    }
                }
                else   //スクロール不可能なときは見えている範囲で
                {
                    point.Line = RuntimeUtil.AdjustIntRange(point.Line, topline_id, topline_id + viewheight - 1);
                } //ここさぼっている
                //Debug.WriteLine(String.Format("MouseMove {0} {1} {2}", sel.State, sel.PivotType, args.X));
                RangeType rt = sel.PivotType;
                if ((Control.ModifierKeys & Keys.Control) != Keys.None)
                {
                    rt = RangeType.Word;
                }
                else if ((Control.ModifierKeys & Keys.Shift) != Keys.None)
                {
                    rt = RangeType.Line;
                }

                GLine tl = document.FindLine(point.Line);
                sel.ExpandTo(tl, point.Column, rt);
            }
            _viewer.Invalidate(); //TODO 選択状態に変化のあった行のみ更新するようにすればなおよし
            return(UIHandleResult.Capture);
        }