RubberbandSelector.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / System / Windows / Documents / RubberbandSelector.cs / 1305600 / RubberbandSelector.cs

                            //---------------------------------------------------------------------------- 
// 
//      Copyright (C) 2003 by Microsoft Corporation.  All rights reserved.
// 
// 
// Description:
// 
// History: 
//      09/29/2004 - Eric Leese (eleese) - Created.
// 
//---------------------------------------------------------------------------
namespace System.Windows.Documents
{
    using MS.Internal;                          // For Invariant.Assert 
    using MS.Internal.Documents;
    using System.Windows;                       // DependencyID etc. 
    using System.Windows.Controls;              // Canvas 
    using System.Collections;
    using System.Windows.Media; 
    using System.Windows.Media.Imaging;
    using System.Windows.Media.TextFormatting;  // CharacterHit
    using System.Windows.Shapes;                // Glyphs
    using System.Windows.Markup; 
    using System.Windows.Input;
    using System.Threading; 
    using System; 
    using System.IO;
    using System.Collections.Generic; 
    using System.Security ;
    using System.Security.Permissions ;
    using System.Diagnostics;
 

    //===================================================================== 
    ///  
    /// Class has a function similar to that of TextEditor.  It can be attached
    /// to a PageGrid by PageViewer to enable rubber band selection.  It should not 
    /// be attached at the same time TextEditor is attached.
    /// 
    internal sealed class RubberbandSelector
    { 
        #region Internal Methods
        ///  
        /// Clears current selection 
        /// 
        internal void ClearSelection() 
        {
            if (HasSelection)
            {
                FixedPage p = _page; 
                _page = null;
                UpdateHighlightVisual(p); 
            } 
            _selectionRect = Rect.Empty;
        } 

        /// 
        /// Attaches selector to scope to start rubberband selection mode
        ///  
        /// the scope, typically a DocumentGrid
        /// 
        /// Critical - creates a command binding. 
        /// TAS - registering our own internal commands is considered safe.
        /// 
        [SecurityCritical , SecurityTreatAsSafe ]
        internal void AttachRubberbandSelector(FrameworkElement scope)
        {
            if (scope == null) 
            {
                throw new ArgumentNullException("scope"); 
            } 

            ClearSelection(); 
            scope.MouseLeftButtonDown += new MouseButtonEventHandler(OnLeftMouseDown);
            scope.MouseLeftButtonUp += new MouseButtonEventHandler(OnLeftMouseUp);
            scope.MouseMove += new MouseEventHandler(OnMouseMove);
            scope.QueryCursor += new QueryCursorEventHandler(OnQueryCursor); 
            scope.Cursor = null; // Cursors.Cross;
 
            //If the passed-in scope is DocumentGrid, we want to 
            //attach our commands to its DocumentViewerOwner, since
            //DocumentGrid is not focusable by default. 
            if (scope is DocumentGrid)
            {
                _uiScope = ((DocumentGrid)scope).DocumentViewerOwner;
                Invariant.Assert(_uiScope != null, "DocumentGrid's DocumentViewerOwner cannot be null."); 
            }
            else 
            { 
                _uiScope = scope;
            } 

            //Attach the RubberBandSelector's Copy command to the UIScope.
            CommandBinding binding = new CommandBinding(ApplicationCommands.Copy);
            binding.Executed += new ExecutedRoutedEventHandler(OnCopy); 
            binding.CanExecute += new CanExecuteRoutedEventHandler(QueryCopy);
            _uiScope.CommandBindings.Add(binding); 
 
            _scope = scope;
        } 

        /// 
        /// Removes rubberband selector from its scope -- gets out of rubberband selection mode
        ///  
        internal void DetachRubberbandSelector()
        { 
            ClearSelection(); 

            if (_scope != null) 
            {
                _scope.MouseLeftButtonDown -= new MouseButtonEventHandler(OnLeftMouseDown);
                _scope.MouseLeftButtonUp -= new MouseButtonEventHandler(OnLeftMouseUp);
                _scope.MouseMove -= new MouseEventHandler(OnMouseMove); 
                _scope.QueryCursor -= new QueryCursorEventHandler(OnQueryCursor);
                _scope = null; 
            } 

            if (_uiScope != null) 
            {
                CommandBindingCollection commandBindings = _uiScope.CommandBindings;
                foreach (CommandBinding binding in commandBindings)
                { 
                    if (binding.Command == ApplicationCommands.Copy)
                    { 
                        binding.Executed -= new ExecutedRoutedEventHandler(OnCopy); 
                        binding.CanExecute -= new CanExecuteRoutedEventHandler(QueryCopy);
                    } 
                }
                _uiScope = null;
            }
        } 
        #endregion Internal Methods
 
        #region Private Methods 
        // extends current selection to point
        private void ExtendSelection(Point pt) 
        {
            // clip to page
            Size pageSize = _panel.ComputePageSize(_page);
 
            if (pt.X < 0)
            { 
                pt.X = 0; 
            }
            else if (pt.X > pageSize.Width) 
            {
                pt.X = pageSize.Width;
            }
 
            if (pt.Y < 0)
            { 
                pt.Y = 0; 
            }
            else if (pt.Y > pageSize.Height) 
            {
                pt.Y = pageSize.Height;
            }
 
            //create rectangle extending from selection origin to current point
            _selectionRect = new Rect(_origin, pt); 
 
            UpdateHighlightVisual(_page);
        } 

        //redraws highlights on page
        private void UpdateHighlightVisual(FixedPage page)
        { 
            if (page != null)
            { 
                HighlightVisual hv = HighlightVisual.GetHighlightVisual(page); 
                if (hv != null)
                { 
                    hv.UpdateRubberbandSelection(this);
                }
            }
        } 

        ///  
        ///    This code checks for all permissions 
        /// that it would otherwise assert to enable rubber band copy.  This is to make sure untrusted applications
        /// do not trigger a copy. 
        /// 
        private bool HasFullTrustPermissions()
        {
            try 
            {
                (new SecurityPermission(SecurityPermissionFlag.SerializationFormatter | SecurityPermissionFlag.UnmanagedCode)).Demand(); 
                CodeAccessPermission mediaAccessPermission = SecurityHelper.CreateMediaAccessPermission(null); 
                mediaAccessPermission.Demand();
                return true; 
            }
            catch (SecurityException)
            {
                return false; 
            }
        } 
 
        /// 
        ///    Critical: This calls into GetBitmap, which is security critical.  It also puts a bitmap on the 
        ///              clipboard, which is not typically allowed in partially trusted code.It also asserts
        ///              to add content to the clipboard
        ///    TreatAsSafe: We guarantee this code can only be triggered by the user deliberately copying the
        ///              selection.  Because we generate the bitmap from what the user sees, this is no more 
        ///              dangerous than a screen capture.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        private void OnCopy(object sender, ExecutedRoutedEventArgs e)
        { 
            if (HasSelection && _selectionRect.Width > 0 && _selectionRect.Height > 0)
            {

                //Copy to clipboard 
                IDataObject dataObject;
                string textString = GetText(); 
                System.Drawing.Bitmap bmp = null; 

                bool supportImageCopy = false; 

                if (_scope is DocumentGrid && ((DocumentGrid)_scope).DocumentViewerOwner is DocumentApplicationDocumentViewer)
                {
                    // This is XPSViewer, make sure it is user initiated 
                    if (!e.UserInitiated && !HasFullTrustPermissions())
                    { 
                        return; 
                    }
                    supportImageCopy = true; 
                }
                else
                {
                    //Outside of XPSViewer, support image copy in full trust only 
                    supportImageCopy = HasFullTrustPermissions();
                } 
 
                if (supportImageCopy)
                { 
                    bmp = GetBitmap();
                }

                (new UIPermission(UIPermissionClipboard.AllClipboard)).Assert();//BlessedAssert 
                try
                { 
                    dataObject = new DataObject(); 
                    // Order of data is irrelevant, the pasting application will determine format
                    dataObject.SetData(DataFormats.Text, textString, true); 
                    dataObject.SetData(DataFormats.UnicodeText, textString, true);
                    if (bmp != null)
                    {
                        dataObject.SetData(DataFormats.Bitmap, bmp, true); 
                    }
 
                } 
                finally
                { 
                    UIPermission.RevertAssert();
                }

 
                PermissionSet ps = new PermissionSet(PermissionState.None);
                ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter)); 
                ps.AddPermission(new UIPermission(UIPermissionClipboard.AllClipboard)); 
                ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode));
 
                if (supportImageCopy)
                {
                    CodeAccessPermission mediaAccessPermission = SecurityHelper.CreateMediaAccessPermission(null);
                    ps.AddPermission(mediaAccessPermission); 
                }
 
                ps.Assert(); // BlessedAssert 

                try 
                {

                    Clipboard.SetDataObject(dataObject, true);
                } 
                catch (System.Runtime.InteropServices.ExternalException)
                { 
                    // Clipboard is failed to set the data object. 
                    return;
                } 
                finally
                {
                    SecurityPermission.RevertAssert();
                } 

            } 
        } 

        //returns bitmap snapshot of selected area 
        //this code takes a BitmapImage and converts it to a Bitmap so it can be put on the clipboard
        /// 
        ///    Critical: This calls into copy pixels which is link demand protected. It initially had a demand and this
        ///              code did not work in PT 
        /// 
        [SecurityCritical] 
        System.Drawing.Bitmap GetBitmap() 
        {
            BitmapSource contentImage = GetImage(); 
            int imageWidth = (int)contentImage.Width;
            int imageHeight = (int)contentImage.Height;

            System.Drawing.Bitmap bitmapFinal = new System.Drawing.Bitmap( 
                                        imageWidth,
                                        imageHeight, 
                                        System.Drawing.Imaging.PixelFormat.Format32bppRgb); 

            System.Drawing.Imaging.BitmapData bmData = 
                                bitmapFinal.LockBits(
                                    new System.Drawing.Rectangle(0, 0, imageWidth, imageHeight),
                                    System.Drawing.Imaging.ImageLockMode.WriteOnly,
                                    System.Drawing.Imaging.PixelFormat.Format32bppRgb); 

            FormatConvertedBitmap formatConverter = new FormatConvertedBitmap(); 
            formatConverter.BeginInit(); 
            formatConverter.Source = contentImage;
            formatConverter.DestinationFormat = PixelFormats.Bgr32; 
            formatConverter.EndInit();

            CodeAccessPermission mediaAccessPermission = SecurityHelper.CreateMediaAccessPermission(null);
 
            if (mediaAccessPermission != null)
            { 
                mediaAccessPermission.Assert(); //BlessedAssert 
            }
            try 
            {
                formatConverter.CopyPixels(
                            new Int32Rect(0, 0, imageWidth, imageHeight),
                            bmData.Scan0, 
                            bmData.Stride * (bmData.Height - 1) + (bmData.Width * 4),
                            bmData.Stride); 
            } 
            finally
            { 
                if (mediaAccessPermission != null)
                {
                    CodeAccessPermission.RevertAssert();
                } 
            }
 
            bitmapFinal.UnlockBits(bmData); 

            return bitmapFinal; 
        }

        //gets snapshot image
        private BitmapSource GetImage() 
        {
            //get copy of page 
            Visual v = GetVisual(-_selectionRect.Left, -_selectionRect.Top); 

            //create image of appropriate size 
            double dpi = 96; // default screen dpi, in fact no other dpi seems to work if you want something at 100% scale
            double scale = dpi / 96.0;
            RenderTargetBitmap data = new RenderTargetBitmap((int)(scale * _selectionRect.Width), (int)(scale * _selectionRect.Height),dpi,dpi, PixelFormats.Pbgra32);
 
            data.Render(v);
            return data; 
        } 

        private Visual GetVisual(double offsetX, double offsetY) 
        {
            ContainerVisual root = new ContainerVisual();
            DrawingVisual visual = new DrawingVisual();
 
            root.Children.Add(visual);
            visual.Offset  = new Vector(offsetX, offsetY); 
 
            DrawingContext dc = visual.RenderOpen();
            dc.DrawDrawing(_page.GetDrawing()); 
            dc.Close();

            UIElementCollection vc = _page.Children;
            foreach (UIElement child in vc) 
            {
                CloneVisualTree(visual, child); 
            } 

            return root; 
        }

        private void CloneVisualTree(ContainerVisual parent, Visual old)
        { 
            DrawingVisual visual = new DrawingVisual();
            parent.Children.Add(visual); 
 
            visual.Clip = VisualTreeHelper.GetClip(old);
            visual.Offset = VisualTreeHelper.GetOffset(old); 
            visual.Transform = VisualTreeHelper.GetTransform(old);
            visual.Opacity = VisualTreeHelper.GetOpacity(old);
            visual.OpacityMask = VisualTreeHelper.GetOpacityMask(old);
 
#pragma warning disable 0618
            visual.BitmapEffectInput = VisualTreeHelper.GetBitmapEffectInput(old); 
            visual.BitmapEffect = VisualTreeHelper.GetBitmapEffect(old); 
#pragma warning restore 0618
 
            // snapping guidelines??

            DrawingContext dc = visual.RenderOpen();
            dc.DrawDrawing(old.GetDrawing()); 
            dc.Close();
 
            int count = VisualTreeHelper.GetChildrenCount(old); 
            for(int i = 0; i < count; i++)
            { 
                Visual child = old.InternalGetVisualChild(i);
                CloneVisualTree(visual, child);
            }
 
        }
        //gets text within selected area 
        private string GetText() 
        {
            double top = _selectionRect.Top; 
            double bottom = _selectionRect.Bottom;
            double left = _selectionRect.Left;
            double right = _selectionRect.Right;
 
            double lastBaseline = 0;
            double baseline = 0; 
            double lastHeight = 0; 
            double height = 0;
 
            int nChildren = _page.Children.Count;
            ArrayList ranges = new ArrayList(); //text ranges in area

            FixedNode[] nodesInLine = _panel.FixedContainer.FixedTextBuilder.GetFirstLine(_pageIndex); 

            while (nodesInLine != null && nodesInLine.Length > 0) 
            { 
                TextPositionPair textRange = null; //current text range
 
                foreach (FixedNode node in nodesInLine)
                {
                    Glyphs g = _page.GetGlyphsElement(node);
                    if (g != null) 
                    {
                        int begin, end; //first and last index in range 
                        bool includeEnd; //is the end of this glyphs included in selection? 
                        if (IntersectGlyphs(g, top, left, bottom, right, out begin, out end, out includeEnd, out baseline, out height))
                        { 
                            if (textRange == null || begin > 0)
                            {
                                //begin new text range
                                textRange = new TextPositionPair(); 
                                textRange.first = _GetTextPosition(node, begin);
                                ranges.Add(textRange); 
                            } 

                            textRange.second = _GetTextPosition(node, end); 

                            if (!includeEnd)
                            {
                                // so future textRanges aren't concatenated with this one 
                                textRange = null;
                            } 
                        } 
                        else
                        { 
                            //this Glyphs completely outside selected region
                            textRange = null;
                        }
                        lastBaseline = baseline; 
                        lastHeight = height;
                    } 
                } 
                int count = 1;
                nodesInLine = _panel.FixedContainer.FixedTextBuilder.GetNextLine(nodesInLine[0], true, ref count); 
            }

            string text = "";
            foreach (TextPositionPair range in ranges) 
            {
                Debug.Assert(range.first != null && range.second != null); 
                text = text + TextRangeBase.GetTextInternal(range.first, range.second) + "\r\n"; //CRLF 
            }
 
            return text;
        }

        private ITextPointer _GetTextPosition(FixedNode node, int charIndex) 
        {
            FixedPosition fixedPosition = new FixedPosition(node, charIndex); 
 
            // Create a FlowPosition to represent this fixed position
            FlowPosition flowHit = _panel.FixedContainer.FixedTextBuilder.CreateFlowPosition(fixedPosition); 
            if (flowHit != null)
            {
                // Create a TextPointer from the flow position
                return new FixedTextPointer(false, LogicalDirection.Forward, flowHit); 
            }
            return null; 
        } 

 
        //determines whether and where a rectangle intersects a Glyphs
        private bool IntersectGlyphs(Glyphs g, double top, double left, double bottom, double right, out int begin, out int end, out bool includeEnd, out double baseline, out double height)
        {
            begin = 0; 
            end = 0;
            includeEnd = false; 
 
            GlyphRun run = g.ToGlyphRun();
            Rect boundingRect = run.ComputeAlignmentBox(); 
            boundingRect.Offset(run.BaselineOrigin.X, run.BaselineOrigin.Y);

            //useful for same line detection
            baseline = run.BaselineOrigin.Y; 
            height = boundingRect.Height;
 
            double centerLine = boundingRect.Y + .5 * boundingRect.Height; 
            GeneralTransform t = g.TransformToAncestor(_page);
 
            Point pt1;
            t.TryTransform(new Point(boundingRect.Left, centerLine), out pt1);
            Point pt2;
            t.TryTransform(new Point(boundingRect.Right, centerLine), out pt2); 

            double dStart, dEnd; 
 
            bool cross = false;
            if (pt1.X < left) 
            {
                if (pt2.X < left)
                {
                    return false; 
                }
                cross = true; 
            } 
            else if (pt1.X > right)
            { 
                if (pt2.X > right)
                {
                    return false;
                } 
                cross = true;
            } 
            else if (pt2.X < left || pt2.X > right) 
            {
                cross = true; 
            }

            if (cross)
            { 
                double d1 = (left - pt1.X) / (pt2.X - pt1.X);
                double d2 = (right - pt1.X) / (pt2.X - pt1.X); 
                if (d2 > d1) 
                {
                    dStart = d1; 
                    dEnd = d2;
                }
                else
                { 
                    dStart = d2;
                    dEnd = d1; 
                } 
            }
            else 
            {
                dStart = 0;
                dEnd = 1;
            } 

            cross = false; 
            if (pt1.Y < top) 
            {
                if (pt2.Y < top) 
                {
                    return false;
                }
                cross = true; 
            }
            else if (pt1.Y > bottom) 
            { 
                if (pt2.Y > bottom)
                { 
                    return false;
                }
                cross = true;
            } 
            else if (pt2.Y < top || pt2.Y > bottom)
            { 
                cross = true; 
            }
 
            if (cross)
            {
                double d1 = (top - pt1.Y) / (pt2.Y - pt1.Y);
                double d2 = (bottom - pt1.Y) / (pt2.Y - pt1.Y); 
                if (d2 > d1)
                { 
                    if (d1 > dStart) 
                    {
                        dStart = d1; 
                    }
                    if (d2 < dEnd)
                    {
                        dEnd = d2; 
                    }
                } 
                else 
                {
                    if (d2 > dStart) 
                    {
                    dStart = d2;
                    }
                    if (d1 < dEnd) 
                    {
                        dEnd = d1; 
                    } 
                }
            } 

            dStart = boundingRect.Left + boundingRect.Width * dStart;
            dEnd = boundingRect.Left + boundingRect.Width * dEnd;
 
            bool leftToRight = ((run.BidiLevel & 1) == 0);
 
            begin = GlyphRunHitTest(run, dStart, leftToRight); 
            end = GlyphRunHitTest(run, dEnd, leftToRight);
 
            if (begin > end)
            {
                int temp = begin;
                begin = end; 
                end = temp;
            } 
 
            Debug.Assert(end >= begin);
 
            int characterCount = (run.Characters == null) ? 0 : run.Characters.Count;
            includeEnd = (end == characterCount);

            return true; 
        }
 
        //Returns the character offset in a GlyphRun given an X position 
        private int GlyphRunHitTest(GlyphRun run, double xoffset, bool LTR)
        { 
            bool isInside;
            double distance = LTR ? xoffset - run.BaselineOrigin.X : run.BaselineOrigin.X - xoffset;
            CharacterHit hit = run.GetCaretCharacterHitFromDistance(distance, out isInside);
            return hit.FirstCharacterIndex + hit.TrailingLength; 
        }
 
        //queryenabled handler for copy command 
        private void QueryCopy(object sender, CanExecuteRoutedEventArgs e)
        { 
            if (HasSelection)
            {
                e.CanExecute = true;
            } 
        }
 
        private void OnLeftMouseDown(object sender, MouseButtonEventArgs e) 
        {
            e.Handled = true; 

            FixedDocumentPage dp = GetFixedPanelDocumentPage(e.GetPosition(_scope));
            if (dp != null)
            { 
                //give focus to the UI Scope so that actions like
                //Copy will work after making a selection. 
                _uiScope.Focus(); 

                //turn on mouse capture 
                _scope.CaptureMouse();

                ClearSelection();
 
                //mark start position
                _panel = dp.Owner; 
                _page = dp.FixedPage; 
                _isSelecting = true;
                _origin = e.GetPosition(_page); 
                _pageIndex = dp.PageIndex;
            }
        }
 
        private void OnLeftMouseUp(object sender, MouseButtonEventArgs e)
        { 
            e.Handled = true; 
            _scope.ReleaseMouseCapture();
 
            if (_isSelecting)
            {
                _isSelecting = false;
                if (_page != null) 
                {
                    ExtendSelection(e.GetPosition(_page)); 
                } 
            }
        } 

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            e.Handled = true; 

            if (e.LeftButton == MouseButtonState.Released) 
            { 
                _isSelecting = false;
            } 
            else if (_isSelecting)
            {
                if (_page != null)
                { 
                    ExtendSelection(e.GetPosition(_page));
                } 
            } 
        }
 
        private void OnQueryCursor(object sender, QueryCursorEventArgs e)
        {
            if (_isSelecting || GetFixedPanelDocumentPage(e.GetPosition(_scope)) != null)
            { 
                e.Cursor = Cursors.Cross;
            } 
            else 
            {
                e.Cursor = Cursors.Arrow; 
            }

            e.Handled = true;
        } 

        private FixedDocumentPage GetFixedPanelDocumentPage(Point pt) 
        { 
            DocumentGrid mpScope = _scope as DocumentGrid;
            if (mpScope != null) 
            {
                DocumentPage dp = mpScope.GetDocumentPageFromPoint(pt);
                FixedDocumentPage fdp = dp as FixedDocumentPage;
                if (fdp == null) 
                {
                    FixedDocumentSequenceDocumentPage fdsdp = dp as FixedDocumentSequenceDocumentPage; 
                    if (fdsdp != null) 
                    {
                        fdp = fdsdp.ChildDocumentPage as FixedDocumentPage; 
                    }
                }
                return fdp;
            } 
            return null;
        } 
        #endregion Private Methods 

        #region Internal Properties 
        internal FixedPage Page
        {
            get { return _page; }
        } 

        internal Rect SelectionRect 
        { 
            get { return _selectionRect; }
        } 

        internal bool HasSelection
        {
            get { return _page != null && _panel != null && !_selectionRect.IsEmpty; } 
        }
        #endregion Internal Properties 
 
        #region Private Fields
        private FixedDocument _panel;     // FixedDocument on which we are selecting 
        private FixedPage _page;       // page on which we are selecting, or null
        private Rect _selectionRect;   // rectangle in page coordinates, or empty
        private bool _isSelecting;     // true if mouse is down and we are currently drawing the box
        private Point _origin;         // point where we started dragging 
        private UIElement _scope;      // element to which we are attached
        private FrameworkElement _uiScope;      // parent of _scope, if _scope is a DocumentGrid. 
        private int _pageIndex;        // index of _page 
        #endregion Private Fields
 
        // a lightweight TextRange like class used in GetText.  We needed a class
        // here because we needed this to be a reference type.
        private class TextPositionPair
        { 
            public ITextPointer first;
            public ITextPointer second; 
        } 
    }
} 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
// 
//      Copyright (C) 2003 by Microsoft Corporation.  All rights reserved.
// 
// 
// Description:
// 
// History: 
//      09/29/2004 - Eric Leese (eleese) - Created.
// 
//---------------------------------------------------------------------------
namespace System.Windows.Documents
{
    using MS.Internal;                          // For Invariant.Assert 
    using MS.Internal.Documents;
    using System.Windows;                       // DependencyID etc. 
    using System.Windows.Controls;              // Canvas 
    using System.Collections;
    using System.Windows.Media; 
    using System.Windows.Media.Imaging;
    using System.Windows.Media.TextFormatting;  // CharacterHit
    using System.Windows.Shapes;                // Glyphs
    using System.Windows.Markup; 
    using System.Windows.Input;
    using System.Threading; 
    using System; 
    using System.IO;
    using System.Collections.Generic; 
    using System.Security ;
    using System.Security.Permissions ;
    using System.Diagnostics;
 

    //===================================================================== 
    ///  
    /// Class has a function similar to that of TextEditor.  It can be attached
    /// to a PageGrid by PageViewer to enable rubber band selection.  It should not 
    /// be attached at the same time TextEditor is attached.
    /// 
    internal sealed class RubberbandSelector
    { 
        #region Internal Methods
        ///  
        /// Clears current selection 
        /// 
        internal void ClearSelection() 
        {
            if (HasSelection)
            {
                FixedPage p = _page; 
                _page = null;
                UpdateHighlightVisual(p); 
            } 
            _selectionRect = Rect.Empty;
        } 

        /// 
        /// Attaches selector to scope to start rubberband selection mode
        ///  
        /// the scope, typically a DocumentGrid
        /// 
        /// Critical - creates a command binding. 
        /// TAS - registering our own internal commands is considered safe.
        /// 
        [SecurityCritical , SecurityTreatAsSafe ]
        internal void AttachRubberbandSelector(FrameworkElement scope)
        {
            if (scope == null) 
            {
                throw new ArgumentNullException("scope"); 
            } 

            ClearSelection(); 
            scope.MouseLeftButtonDown += new MouseButtonEventHandler(OnLeftMouseDown);
            scope.MouseLeftButtonUp += new MouseButtonEventHandler(OnLeftMouseUp);
            scope.MouseMove += new MouseEventHandler(OnMouseMove);
            scope.QueryCursor += new QueryCursorEventHandler(OnQueryCursor); 
            scope.Cursor = null; // Cursors.Cross;
 
            //If the passed-in scope is DocumentGrid, we want to 
            //attach our commands to its DocumentViewerOwner, since
            //DocumentGrid is not focusable by default. 
            if (scope is DocumentGrid)
            {
                _uiScope = ((DocumentGrid)scope).DocumentViewerOwner;
                Invariant.Assert(_uiScope != null, "DocumentGrid's DocumentViewerOwner cannot be null."); 
            }
            else 
            { 
                _uiScope = scope;
            } 

            //Attach the RubberBandSelector's Copy command to the UIScope.
            CommandBinding binding = new CommandBinding(ApplicationCommands.Copy);
            binding.Executed += new ExecutedRoutedEventHandler(OnCopy); 
            binding.CanExecute += new CanExecuteRoutedEventHandler(QueryCopy);
            _uiScope.CommandBindings.Add(binding); 
 
            _scope = scope;
        } 

        /// 
        /// Removes rubberband selector from its scope -- gets out of rubberband selection mode
        ///  
        internal void DetachRubberbandSelector()
        { 
            ClearSelection(); 

            if (_scope != null) 
            {
                _scope.MouseLeftButtonDown -= new MouseButtonEventHandler(OnLeftMouseDown);
                _scope.MouseLeftButtonUp -= new MouseButtonEventHandler(OnLeftMouseUp);
                _scope.MouseMove -= new MouseEventHandler(OnMouseMove); 
                _scope.QueryCursor -= new QueryCursorEventHandler(OnQueryCursor);
                _scope = null; 
            } 

            if (_uiScope != null) 
            {
                CommandBindingCollection commandBindings = _uiScope.CommandBindings;
                foreach (CommandBinding binding in commandBindings)
                { 
                    if (binding.Command == ApplicationCommands.Copy)
                    { 
                        binding.Executed -= new ExecutedRoutedEventHandler(OnCopy); 
                        binding.CanExecute -= new CanExecuteRoutedEventHandler(QueryCopy);
                    } 
                }
                _uiScope = null;
            }
        } 
        #endregion Internal Methods
 
        #region Private Methods 
        // extends current selection to point
        private void ExtendSelection(Point pt) 
        {
            // clip to page
            Size pageSize = _panel.ComputePageSize(_page);
 
            if (pt.X < 0)
            { 
                pt.X = 0; 
            }
            else if (pt.X > pageSize.Width) 
            {
                pt.X = pageSize.Width;
            }
 
            if (pt.Y < 0)
            { 
                pt.Y = 0; 
            }
            else if (pt.Y > pageSize.Height) 
            {
                pt.Y = pageSize.Height;
            }
 
            //create rectangle extending from selection origin to current point
            _selectionRect = new Rect(_origin, pt); 
 
            UpdateHighlightVisual(_page);
        } 

        //redraws highlights on page
        private void UpdateHighlightVisual(FixedPage page)
        { 
            if (page != null)
            { 
                HighlightVisual hv = HighlightVisual.GetHighlightVisual(page); 
                if (hv != null)
                { 
                    hv.UpdateRubberbandSelection(this);
                }
            }
        } 

        ///  
        ///    This code checks for all permissions 
        /// that it would otherwise assert to enable rubber band copy.  This is to make sure untrusted applications
        /// do not trigger a copy. 
        /// 
        private bool HasFullTrustPermissions()
        {
            try 
            {
                (new SecurityPermission(SecurityPermissionFlag.SerializationFormatter | SecurityPermissionFlag.UnmanagedCode)).Demand(); 
                CodeAccessPermission mediaAccessPermission = SecurityHelper.CreateMediaAccessPermission(null); 
                mediaAccessPermission.Demand();
                return true; 
            }
            catch (SecurityException)
            {
                return false; 
            }
        } 
 
        /// 
        ///    Critical: This calls into GetBitmap, which is security critical.  It also puts a bitmap on the 
        ///              clipboard, which is not typically allowed in partially trusted code.It also asserts
        ///              to add content to the clipboard
        ///    TreatAsSafe: We guarantee this code can only be triggered by the user deliberately copying the
        ///              selection.  Because we generate the bitmap from what the user sees, this is no more 
        ///              dangerous than a screen capture.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        private void OnCopy(object sender, ExecutedRoutedEventArgs e)
        { 
            if (HasSelection && _selectionRect.Width > 0 && _selectionRect.Height > 0)
            {

                //Copy to clipboard 
                IDataObject dataObject;
                string textString = GetText(); 
                System.Drawing.Bitmap bmp = null; 

                bool supportImageCopy = false; 

                if (_scope is DocumentGrid && ((DocumentGrid)_scope).DocumentViewerOwner is DocumentApplicationDocumentViewer)
                {
                    // This is XPSViewer, make sure it is user initiated 
                    if (!e.UserInitiated && !HasFullTrustPermissions())
                    { 
                        return; 
                    }
                    supportImageCopy = true; 
                }
                else
                {
                    //Outside of XPSViewer, support image copy in full trust only 
                    supportImageCopy = HasFullTrustPermissions();
                } 
 
                if (supportImageCopy)
                { 
                    bmp = GetBitmap();
                }

                (new UIPermission(UIPermissionClipboard.AllClipboard)).Assert();//BlessedAssert 
                try
                { 
                    dataObject = new DataObject(); 
                    // Order of data is irrelevant, the pasting application will determine format
                    dataObject.SetData(DataFormats.Text, textString, true); 
                    dataObject.SetData(DataFormats.UnicodeText, textString, true);
                    if (bmp != null)
                    {
                        dataObject.SetData(DataFormats.Bitmap, bmp, true); 
                    }
 
                } 
                finally
                { 
                    UIPermission.RevertAssert();
                }

 
                PermissionSet ps = new PermissionSet(PermissionState.None);
                ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter)); 
                ps.AddPermission(new UIPermission(UIPermissionClipboard.AllClipboard)); 
                ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode));
 
                if (supportImageCopy)
                {
                    CodeAccessPermission mediaAccessPermission = SecurityHelper.CreateMediaAccessPermission(null);
                    ps.AddPermission(mediaAccessPermission); 
                }
 
                ps.Assert(); // BlessedAssert 

                try 
                {

                    Clipboard.SetDataObject(dataObject, true);
                } 
                catch (System.Runtime.InteropServices.ExternalException)
                { 
                    // Clipboard is failed to set the data object. 
                    return;
                } 
                finally
                {
                    SecurityPermission.RevertAssert();
                } 

            } 
        } 

        //returns bitmap snapshot of selected area 
        //this code takes a BitmapImage and converts it to a Bitmap so it can be put on the clipboard
        /// 
        ///    Critical: This calls into copy pixels which is link demand protected. It initially had a demand and this
        ///              code did not work in PT 
        /// 
        [SecurityCritical] 
        System.Drawing.Bitmap GetBitmap() 
        {
            BitmapSource contentImage = GetImage(); 
            int imageWidth = (int)contentImage.Width;
            int imageHeight = (int)contentImage.Height;

            System.Drawing.Bitmap bitmapFinal = new System.Drawing.Bitmap( 
                                        imageWidth,
                                        imageHeight, 
                                        System.Drawing.Imaging.PixelFormat.Format32bppRgb); 

            System.Drawing.Imaging.BitmapData bmData = 
                                bitmapFinal.LockBits(
                                    new System.Drawing.Rectangle(0, 0, imageWidth, imageHeight),
                                    System.Drawing.Imaging.ImageLockMode.WriteOnly,
                                    System.Drawing.Imaging.PixelFormat.Format32bppRgb); 

            FormatConvertedBitmap formatConverter = new FormatConvertedBitmap(); 
            formatConverter.BeginInit(); 
            formatConverter.Source = contentImage;
            formatConverter.DestinationFormat = PixelFormats.Bgr32; 
            formatConverter.EndInit();

            CodeAccessPermission mediaAccessPermission = SecurityHelper.CreateMediaAccessPermission(null);
 
            if (mediaAccessPermission != null)
            { 
                mediaAccessPermission.Assert(); //BlessedAssert 
            }
            try 
            {
                formatConverter.CopyPixels(
                            new Int32Rect(0, 0, imageWidth, imageHeight),
                            bmData.Scan0, 
                            bmData.Stride * (bmData.Height - 1) + (bmData.Width * 4),
                            bmData.Stride); 
            } 
            finally
            { 
                if (mediaAccessPermission != null)
                {
                    CodeAccessPermission.RevertAssert();
                } 
            }
 
            bitmapFinal.UnlockBits(bmData); 

            return bitmapFinal; 
        }

        //gets snapshot image
        private BitmapSource GetImage() 
        {
            //get copy of page 
            Visual v = GetVisual(-_selectionRect.Left, -_selectionRect.Top); 

            //create image of appropriate size 
            double dpi = 96; // default screen dpi, in fact no other dpi seems to work if you want something at 100% scale
            double scale = dpi / 96.0;
            RenderTargetBitmap data = new RenderTargetBitmap((int)(scale * _selectionRect.Width), (int)(scale * _selectionRect.Height),dpi,dpi, PixelFormats.Pbgra32);
 
            data.Render(v);
            return data; 
        } 

        private Visual GetVisual(double offsetX, double offsetY) 
        {
            ContainerVisual root = new ContainerVisual();
            DrawingVisual visual = new DrawingVisual();
 
            root.Children.Add(visual);
            visual.Offset  = new Vector(offsetX, offsetY); 
 
            DrawingContext dc = visual.RenderOpen();
            dc.DrawDrawing(_page.GetDrawing()); 
            dc.Close();

            UIElementCollection vc = _page.Children;
            foreach (UIElement child in vc) 
            {
                CloneVisualTree(visual, child); 
            } 

            return root; 
        }

        private void CloneVisualTree(ContainerVisual parent, Visual old)
        { 
            DrawingVisual visual = new DrawingVisual();
            parent.Children.Add(visual); 
 
            visual.Clip = VisualTreeHelper.GetClip(old);
            visual.Offset = VisualTreeHelper.GetOffset(old); 
            visual.Transform = VisualTreeHelper.GetTransform(old);
            visual.Opacity = VisualTreeHelper.GetOpacity(old);
            visual.OpacityMask = VisualTreeHelper.GetOpacityMask(old);
 
#pragma warning disable 0618
            visual.BitmapEffectInput = VisualTreeHelper.GetBitmapEffectInput(old); 
            visual.BitmapEffect = VisualTreeHelper.GetBitmapEffect(old); 
#pragma warning restore 0618
 
            // snapping guidelines??

            DrawingContext dc = visual.RenderOpen();
            dc.DrawDrawing(old.GetDrawing()); 
            dc.Close();
 
            int count = VisualTreeHelper.GetChildrenCount(old); 
            for(int i = 0; i < count; i++)
            { 
                Visual child = old.InternalGetVisualChild(i);
                CloneVisualTree(visual, child);
            }
 
        }
        //gets text within selected area 
        private string GetText() 
        {
            double top = _selectionRect.Top; 
            double bottom = _selectionRect.Bottom;
            double left = _selectionRect.Left;
            double right = _selectionRect.Right;
 
            double lastBaseline = 0;
            double baseline = 0; 
            double lastHeight = 0; 
            double height = 0;
 
            int nChildren = _page.Children.Count;
            ArrayList ranges = new ArrayList(); //text ranges in area

            FixedNode[] nodesInLine = _panel.FixedContainer.FixedTextBuilder.GetFirstLine(_pageIndex); 

            while (nodesInLine != null && nodesInLine.Length > 0) 
            { 
                TextPositionPair textRange = null; //current text range
 
                foreach (FixedNode node in nodesInLine)
                {
                    Glyphs g = _page.GetGlyphsElement(node);
                    if (g != null) 
                    {
                        int begin, end; //first and last index in range 
                        bool includeEnd; //is the end of this glyphs included in selection? 
                        if (IntersectGlyphs(g, top, left, bottom, right, out begin, out end, out includeEnd, out baseline, out height))
                        { 
                            if (textRange == null || begin > 0)
                            {
                                //begin new text range
                                textRange = new TextPositionPair(); 
                                textRange.first = _GetTextPosition(node, begin);
                                ranges.Add(textRange); 
                            } 

                            textRange.second = _GetTextPosition(node, end); 

                            if (!includeEnd)
                            {
                                // so future textRanges aren't concatenated with this one 
                                textRange = null;
                            } 
                        } 
                        else
                        { 
                            //this Glyphs completely outside selected region
                            textRange = null;
                        }
                        lastBaseline = baseline; 
                        lastHeight = height;
                    } 
                } 
                int count = 1;
                nodesInLine = _panel.FixedContainer.FixedTextBuilder.GetNextLine(nodesInLine[0], true, ref count); 
            }

            string text = "";
            foreach (TextPositionPair range in ranges) 
            {
                Debug.Assert(range.first != null && range.second != null); 
                text = text + TextRangeBase.GetTextInternal(range.first, range.second) + "\r\n"; //CRLF 
            }
 
            return text;
        }

        private ITextPointer _GetTextPosition(FixedNode node, int charIndex) 
        {
            FixedPosition fixedPosition = new FixedPosition(node, charIndex); 
 
            // Create a FlowPosition to represent this fixed position
            FlowPosition flowHit = _panel.FixedContainer.FixedTextBuilder.CreateFlowPosition(fixedPosition); 
            if (flowHit != null)
            {
                // Create a TextPointer from the flow position
                return new FixedTextPointer(false, LogicalDirection.Forward, flowHit); 
            }
            return null; 
        } 

 
        //determines whether and where a rectangle intersects a Glyphs
        private bool IntersectGlyphs(Glyphs g, double top, double left, double bottom, double right, out int begin, out int end, out bool includeEnd, out double baseline, out double height)
        {
            begin = 0; 
            end = 0;
            includeEnd = false; 
 
            GlyphRun run = g.ToGlyphRun();
            Rect boundingRect = run.ComputeAlignmentBox(); 
            boundingRect.Offset(run.BaselineOrigin.X, run.BaselineOrigin.Y);

            //useful for same line detection
            baseline = run.BaselineOrigin.Y; 
            height = boundingRect.Height;
 
            double centerLine = boundingRect.Y + .5 * boundingRect.Height; 
            GeneralTransform t = g.TransformToAncestor(_page);
 
            Point pt1;
            t.TryTransform(new Point(boundingRect.Left, centerLine), out pt1);
            Point pt2;
            t.TryTransform(new Point(boundingRect.Right, centerLine), out pt2); 

            double dStart, dEnd; 
 
            bool cross = false;
            if (pt1.X < left) 
            {
                if (pt2.X < left)
                {
                    return false; 
                }
                cross = true; 
            } 
            else if (pt1.X > right)
            { 
                if (pt2.X > right)
                {
                    return false;
                } 
                cross = true;
            } 
            else if (pt2.X < left || pt2.X > right) 
            {
                cross = true; 
            }

            if (cross)
            { 
                double d1 = (left - pt1.X) / (pt2.X - pt1.X);
                double d2 = (right - pt1.X) / (pt2.X - pt1.X); 
                if (d2 > d1) 
                {
                    dStart = d1; 
                    dEnd = d2;
                }
                else
                { 
                    dStart = d2;
                    dEnd = d1; 
                } 
            }
            else 
            {
                dStart = 0;
                dEnd = 1;
            } 

            cross = false; 
            if (pt1.Y < top) 
            {
                if (pt2.Y < top) 
                {
                    return false;
                }
                cross = true; 
            }
            else if (pt1.Y > bottom) 
            { 
                if (pt2.Y > bottom)
                { 
                    return false;
                }
                cross = true;
            } 
            else if (pt2.Y < top || pt2.Y > bottom)
            { 
                cross = true; 
            }
 
            if (cross)
            {
                double d1 = (top - pt1.Y) / (pt2.Y - pt1.Y);
                double d2 = (bottom - pt1.Y) / (pt2.Y - pt1.Y); 
                if (d2 > d1)
                { 
                    if (d1 > dStart) 
                    {
                        dStart = d1; 
                    }
                    if (d2 < dEnd)
                    {
                        dEnd = d2; 
                    }
                } 
                else 
                {
                    if (d2 > dStart) 
                    {
                    dStart = d2;
                    }
                    if (d1 < dEnd) 
                    {
                        dEnd = d1; 
                    } 
                }
            } 

            dStart = boundingRect.Left + boundingRect.Width * dStart;
            dEnd = boundingRect.Left + boundingRect.Width * dEnd;
 
            bool leftToRight = ((run.BidiLevel & 1) == 0);
 
            begin = GlyphRunHitTest(run, dStart, leftToRight); 
            end = GlyphRunHitTest(run, dEnd, leftToRight);
 
            if (begin > end)
            {
                int temp = begin;
                begin = end; 
                end = temp;
            } 
 
            Debug.Assert(end >= begin);
 
            int characterCount = (run.Characters == null) ? 0 : run.Characters.Count;
            includeEnd = (end == characterCount);

            return true; 
        }
 
        //Returns the character offset in a GlyphRun given an X position 
        private int GlyphRunHitTest(GlyphRun run, double xoffset, bool LTR)
        { 
            bool isInside;
            double distance = LTR ? xoffset - run.BaselineOrigin.X : run.BaselineOrigin.X - xoffset;
            CharacterHit hit = run.GetCaretCharacterHitFromDistance(distance, out isInside);
            return hit.FirstCharacterIndex + hit.TrailingLength; 
        }
 
        //queryenabled handler for copy command 
        private void QueryCopy(object sender, CanExecuteRoutedEventArgs e)
        { 
            if (HasSelection)
            {
                e.CanExecute = true;
            } 
        }
 
        private void OnLeftMouseDown(object sender, MouseButtonEventArgs e) 
        {
            e.Handled = true; 

            FixedDocumentPage dp = GetFixedPanelDocumentPage(e.GetPosition(_scope));
            if (dp != null)
            { 
                //give focus to the UI Scope so that actions like
                //Copy will work after making a selection. 
                _uiScope.Focus(); 

                //turn on mouse capture 
                _scope.CaptureMouse();

                ClearSelection();
 
                //mark start position
                _panel = dp.Owner; 
                _page = dp.FixedPage; 
                _isSelecting = true;
                _origin = e.GetPosition(_page); 
                _pageIndex = dp.PageIndex;
            }
        }
 
        private void OnLeftMouseUp(object sender, MouseButtonEventArgs e)
        { 
            e.Handled = true; 
            _scope.ReleaseMouseCapture();
 
            if (_isSelecting)
            {
                _isSelecting = false;
                if (_page != null) 
                {
                    ExtendSelection(e.GetPosition(_page)); 
                } 
            }
        } 

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            e.Handled = true; 

            if (e.LeftButton == MouseButtonState.Released) 
            { 
                _isSelecting = false;
            } 
            else if (_isSelecting)
            {
                if (_page != null)
                { 
                    ExtendSelection(e.GetPosition(_page));
                } 
            } 
        }
 
        private void OnQueryCursor(object sender, QueryCursorEventArgs e)
        {
            if (_isSelecting || GetFixedPanelDocumentPage(e.GetPosition(_scope)) != null)
            { 
                e.Cursor = Cursors.Cross;
            } 
            else 
            {
                e.Cursor = Cursors.Arrow; 
            }

            e.Handled = true;
        } 

        private FixedDocumentPage GetFixedPanelDocumentPage(Point pt) 
        { 
            DocumentGrid mpScope = _scope as DocumentGrid;
            if (mpScope != null) 
            {
                DocumentPage dp = mpScope.GetDocumentPageFromPoint(pt);
                FixedDocumentPage fdp = dp as FixedDocumentPage;
                if (fdp == null) 
                {
                    FixedDocumentSequenceDocumentPage fdsdp = dp as FixedDocumentSequenceDocumentPage; 
                    if (fdsdp != null) 
                    {
                        fdp = fdsdp.ChildDocumentPage as FixedDocumentPage; 
                    }
                }
                return fdp;
            } 
            return null;
        } 
        #endregion Private Methods 

        #region Internal Properties 
        internal FixedPage Page
        {
            get { return _page; }
        } 

        internal Rect SelectionRect 
        { 
            get { return _selectionRect; }
        } 

        internal bool HasSelection
        {
            get { return _page != null && _panel != null && !_selectionRect.IsEmpty; } 
        }
        #endregion Internal Properties 
 
        #region Private Fields
        private FixedDocument _panel;     // FixedDocument on which we are selecting 
        private FixedPage _page;       // page on which we are selecting, or null
        private Rect _selectionRect;   // rectangle in page coordinates, or empty
        private bool _isSelecting;     // true if mouse is down and we are currently drawing the box
        private Point _origin;         // point where we started dragging 
        private UIElement _scope;      // element to which we are attached
        private FrameworkElement _uiScope;      // parent of _scope, if _scope is a DocumentGrid. 
        private int _pageIndex;        // index of _page 
        #endregion Private Fields
 
        // a lightweight TextRange like class used in GetText.  We needed a class
        // here because we needed this to be a reference type.
        private class TextPositionPair
        { 
            public ITextPointer first;
            public ITextPointer second; 
        } 
    }
} 


// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK