public void MoveCaret(TextCoordinate newCoordinate, bool extendSelection)
        {
            bool wasSelection = this.HasSelection;

            this.Caret = newCoordinate;

            if (!extendSelection)
            {
                this.SelectionAnchor = newCoordinate;
            }

            EnsureMaxHorizontal((this.Caret.Index + 1) * this.charWidth);
            EnsureCaretVisible();

            var handler = this.CaretMoved;

            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }

            if (extendSelection || wasSelection)
            {
                handler = this.SelectionChanged;

                if (handler != null)
                {
                    handler(this, EventArgs.Empty);
                }
            }
        }
 public void Select(TextCoordinate anchor, TextCoordinate end)
 {
     this.SelectionAnchor = anchor;
     this.MoveCaret(end, true);
 }
 public TextLocation MapToLocation(TextCoordinate coord, out int virtualSpaces)
 {
     Debug.Assert(coord.Line == this.line);
     return MapToLocation(coord.Index, out virtualSpaces);
 }
 public TextLocation MapToLocation(TextCoordinate coord, out int virtualSpaces)
 {
     return MapToLocation(coord.Line, coord.Index, out virtualSpaces);
 }
        public Geometry BuildGeometryForRange(TextCoordinate end1, TextCoordinate end2)
        {
            if (end1 == end2)
            {
                // No selection. Empty geometry.
                return Geometry.Empty;
            }

            Geometry resultGeometry;

            if (end1.Line == end2.Line)
            {
                int left = Math.Min(end1.Index, end2.Index);
                int right = Math.Max(end1.Index, end2.Index);

                var rect = new Rect(left * this.charWidth, end1.Line * this.lineHeight, (right - left) * this.charWidth, this.lineHeight);

                // Must inflate the rect just a bit -- otherwise, rounding issues cause incomplete union combinations (below -- even though this
                // particular rect won't ever be combined, we want it to have the same size when single-line).
                rect.Inflate(.5, .5);
                resultGeometry = new RectangleGeometry(rect);
            }
            else
            {
                var top = (end1.Line < end2.Line) ? end1 : end2;
                var bottom = (end1.Line < end2.Line) ? end2 : end1;

                var topRect = new Rect(top.Index * charWidth, top.Line * this.lineHeight, this.maxHorizontal + this.canvas.ActualWidth + 2, this.lineHeight);
                var midRect = new Rect(0, (top.Line + 1) * this.lineHeight, this.maxHorizontal + this.canvas.ActualWidth + 2, (bottom.Line - top.Line - 1) * this.lineHeight);
                var bottomRect = new Rect(0, bottom.Line * this.lineHeight, bottom.Index * this.charWidth, this.lineHeight);

                // Inflate to ensure overlap for correct union combination
                topRect.Inflate(.5, .5);
                midRect.Inflate(.5, .5);
                bottomRect.Inflate(.5, .5);
                var topGeometry = new RectangleGeometry(topRect);
                var bottomGeometry = new RectangleGeometry(bottomRect);

                if (top.Line == bottom.Line - 1)
                {
                    // Just top and bottom -- no lines between
                    resultGeometry = new CombinedGeometry(GeometryCombineMode.Union, topGeometry, bottomGeometry);
                }
                else
                {
                    // Two combinations for 3 geometries
                    var midGeometry = new RectangleGeometry(midRect);
                    var firstCombinedGeometry = new CombinedGeometry(GeometryCombineMode.Union, topGeometry, midGeometry);

                    resultGeometry = new CombinedGeometry(GeometryCombineMode.Union, firstCombinedGeometry, bottomGeometry);
                }
            }

            resultGeometry.Transform = this.highlightTransform;
            return resultGeometry;
        }
        void MoveCaretToMouse(MouseEventArgs e, bool extendSelection)
        {
            var pos = e.GetPosition(this.canvas);

            var index = (int)(Math.Max(0, pos.X + this.HorizontalOffset + (this.charWidth / 3)) / this.charWidth);
            var line = Math.Max(0, Math.Min(this.Buffer.TextData.Lines.Count - 1, this.TopVisibleLine + (int)(pos.Y / this.lineHeight)));

            var coord = new TextCoordinate { Line = line, Index = index };
            TextLineVisual lineVisual = GetLineVisual(coord.Line);
            int virtualSpaces;

            // Must map to location and back to coordinate to "land" on tabs correctly.
            var loc = lineVisual.MapToLocation(coord, out virtualSpaces);

            if (this.doubleClickSelect)
            {
                if (loc.Index == lineVisual.Line.Length)
                {
                    if (this.SelectionAnchor > coord)
                    {
                        virtualSpaces = 0;
                    }
                }
                else
                {
                    var wordRange = GetWordExtent(loc.Line, loc.Index);
                    var startCoord = lineVisual.MapToCoordinate(wordRange.Start);
                    var endCoord = lineVisual.MapToCoordinate(wordRange.End);

                    if (!extendSelection)
                    {
                        // This is the initial double-click. Select the word.
                        this.SelectionAnchor = startCoord;
                        MoveCaret(endCoord, true);
                    }
                    else if (this.SelectionAnchor > endCoord)
                    {
                        MoveCaret(startCoord, true);
                    }
                    else
                    {
                        MoveCaret(endCoord, true);
                    }
                    return;
                }
            }

            coord = lineVisual.MapToCoordinate(loc);
            coord.Index += virtualSpaces;

            MoveCaret(coord, extendSelection);
        }
 public TextCoordinateSpan(TextCoordinate start, TextCoordinate end)
     : this()
 {
     this.Start = start;
     this.End = end;
 }