context: // *
  // * Copyright (C) 2008 Roger Alsing : http://www.RogerAlsing.com
  // *
  // * This library is free software; you can redistribute it and/or modify it
  // * under the terms of the GNU Lesser General Public License 2.1 or later, as
  // * published by the Free Software Foundation. See the included license.txt
  // * or http://www.gnu.org/copyleft/lesser.html for details.
  // *
  // *
  
  using System;
  using Alsing.SourceCode;
  
  namespace Alsing.Windows.Forms.SyntaxBox
  {
      /// <summary>
      /// Selection class used by the SyntaxBoxControl
      /// </summary>
      public class Selection
      {
          /// <summary>
          /// Event fired when the selection has changed.
          /// </summary>
          public event EventHandler Change = null;
  
          private void PositionChange(object s, EventArgs e)
          {
              OnChange();
          }
  
          private void OnChange()
          {
              if (Change != null)
                  Change(this, null);
          }
  
          #region Instance constructors
  
          /// <summary>
          /// Selection Constructor.
          /// </summary>
          /// <param name="control">Control that will use this selection</param>
          public Selection(EditViewControl control)
          {
              Control = control;
              Bounds = new TextRange();
          }
  
          #endregion Instance constructors
  
          #region Public instance properties
  
          /// <summary>
          /// Gets the text of the active selection
          /// </summary>
          public String Text
          {
              get
              {
                  if (!IsValid)
                  {
                      return "";
                  }
                  else
                  {
                      return Control.Document.GetRange(LogicalBounds);
                  }
              }
              set
              {
                  if (Text == value)
                      return;
  
                  //selection text bug fix 
                  //
                  //selection gets too short if \n is used instead of newline
                  string tmp = value.Replace(Environment.NewLine, "\n");
                  tmp = tmp.Replace("\n", Environment.NewLine);
                  value = tmp;
                  //---
  
  
                  TextPoint oCaretPos = Control.Caret.Position;
                  int nCaretX = oCaretPos.X;
                  int nCaretY = oCaretPos.Y;
                  Control.Document.StartUndoCapture();
                  DeleteSelection();
                  Control.Document.InsertText(value, oCaretPos.X, oCaretPos.Y);
                  SelLength = value.Length;
                  if (nCaretX != oCaretPos.X || nCaretY != oCaretPos.Y)
  
                  {
                      Control.Caret.Position = new TextPoint(Bounds.LastColumn,
                                                             Bounds.LastRow);
                  }
  
                  Control.Document.EndUndoCapture();
                  Control.Document.InvokeChange();
              }
          }
  
          /// <summary>
          /// Returns the normalized positions of the selection.
          /// Swapping start and end values if the selection is reversed.
          /// </summary>
          public TextRange LogicalBounds
          {
              get
              {
                  var r = new TextRange();
                  if (Bounds.FirstRow < Bounds.LastRow)
                  {
                      return Bounds;
                  }
                  else if (Bounds.FirstRow == Bounds.LastRow &&
                           Bounds.FirstColumn < Bounds.LastColumn)
                  {
                      return Bounds;
                  }
                  else
                  {
                      r.FirstColumn = Bounds.LastColumn;
                      r.FirstRow = Bounds.LastRow;
                      r.LastColumn = Bounds.FirstColumn;
                      r.LastRow = Bounds.FirstRow;
                      return r;
                  }
              }
          }
  
          /// <summary>
          /// Returns true if the selection contains One or more chars
          /// </summary>
          public bool IsValid
          {
              get
              {
                  return (LogicalBounds.FirstColumn != LogicalBounds.LastColumn
                          || LogicalBounds.FirstRow != LogicalBounds.LastRow);
              }
          }
  
          /// <summary>
          /// gets or sets the length of the selection in chars
          /// </summary>
          public int SelLength
          {
              get
              {
                  var p1 = new TextPoint(Bounds.FirstColumn,
                                         Bounds.FirstRow);
                  var p2 = new TextPoint(Bounds.LastColumn,
                                         Bounds.LastRow);
                  int i1 = Control.Document.PointToIntPos(p1);
                  int i2 = Control.Document.PointToIntPos(p2);
                  return i2 - i1;
              }
              set { SelEnd = SelStart + value; }
          }
  
          /// <summary>
          /// Gets or Sets the Selection end as an index in the document text.
          /// </summary>
          public int SelEnd
          {
              get
              {
                  var p = new TextPoint(Bounds.LastColumn, Bounds.LastRow)
                      ;
                  return Control.Document.PointToIntPos(p);
              }
              set
              {
                  TextPoint p = Control.Document.IntPosToPoint(value);
                  Bounds.LastColumn = p.X;
                  Bounds.LastRow = p.Y;
              }
          }
  
  
          /// <summary>
          /// Gets or Sets the Selection start as an index in the document text.
          /// </summary>
          public int SelStart
          {
              get
              {
                  var p = new TextPoint(Bounds.FirstColumn,
                                        Bounds.FirstRow);
                  return Control.Document.PointToIntPos(p);
              }
              set
              {
                  TextPoint p = Control.Document.IntPosToPoint(value);
                  Bounds.FirstColumn = p.X;
                  Bounds.FirstRow = p.Y;
              }
          }
  
          /// <summary>
          /// Gets or Sets the logical Selection start as an index in the document text.
          /// </summary>
          public int LogicalSelStart
          {
              get
              {
                  var p = new TextPoint(LogicalBounds.FirstColumn,
                                        LogicalBounds.FirstRow);
                  return Control.Document.PointToIntPos(p);
              }
              set
              {
                  TextPoint p = Control.Document.IntPosToPoint(value);
                  Bounds.FirstColumn = p.X;
                  Bounds.FirstRow = p.Y;
              }
          }
  
          #endregion Public instance properties
  
          #region Public instance methods
  
          /// <summary>
          /// Indent the active selection one step.
          /// </summary>
          public void Indent()
          {
              if (!IsValid)
                  return;
  
              Row xtr = null;
              var ActionGroup = new UndoBlockCollection();
  
gt:             for (int i = LogicalBounds.FirstRow;