ClickablePoint.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / UIAutomation / Win32Providers / MS / Internal / AutomationProxies / ClickablePoint.cs / 1305600 / ClickablePoint.cs

                             
using System;
using System.Windows.Automation;
using System.Windows.Automation.Provider;
using System.Runtime.InteropServices; 
using System.Collections;
using System.ComponentModel; 
using MS.Win32; 

// PRESHARP: In order to avoid generating warnings about unkown message numbers and unknown pragmas. 
#pragma warning disable 1634, 1691

namespace MS.Internal.AutomationProxies
{ 
    static internal class ClickablePoint
    { 
        ///  
        /// Static Constructor. Retrieve and keeps the hwnd for "Program"
        /// The Windows Rectangle for "Program"  is the union for the real 
        /// Estate for all the monitors.
        /// 
        static ClickablePoint()
        { 
            _hwndProgman = Misc.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "Progman", null);
            if (_hwndProgman == IntPtr.Zero) 
            { 
                _hwndProgman = _hwndDesktop;
            } 
        }

        //------------------------------------------------------
        // 
        //  Internal Methods
        // 
        //----------------------------------------------------- 

        #region Internal Methods 

        /// 
        /// Return a clickable point in a Rectangle for a given window
        /// 
        /// The algorithm uses the Windows Z order.
        /// To be visible, a rectangle must be enclosed in the hwnd of its parents 
        /// and must but at least partially on top of the all of siblings as predecessors. 
        /// In the windows ordered scheme, the first sibling comes on top followed by the
        /// next, etc. For a given hwnd, it is sufficent then to look for all the 
        /// predecessor siblings.
        ///
        /// The scheme below is using recursion. This is make slightly harder to read
        /// But makes it a bit more efficent. 
        /// 
        /// Window Handle 
        /// Input list of Rectangles to check GetPoint against 
        /// Output list of Rectangles after the exclusion test
        /// Clickable Point 
        /// True if there is a clickable in ro
        static internal bool GetPoint(IntPtr hwnd, ArrayList alIn, ArrayList alOut, ref NativeMethods.Win32Point pt)
        {
            IntPtr hwndStart = hwnd; 
            IntPtr hwndCurrent = hwnd;
 
            // Do the window on top exclusion 
            // Only one level deep is necessary as grand children are clipped to their parent (our children)
            for (hwnd = Misc.GetWindow(hwnd, NativeMethods.GW_CHILD); hwnd != IntPtr.Zero; hwnd = Misc.GetWindow(hwnd, NativeMethods.GW_HWNDNEXT)) 
            {
                // For siblings, the element bounding rectangle must not be covered by the
                // bounding rect of its siblings
                if (!ClickableInRect(hwnd, ref pt, true, alIn, alOut)) 
                {
                    return false; 
                } 
            }
 
            // Check for Parent and Sibling
            hwnd = hwndStart;
            while (true)
            { 
                hwnd = Misc.GetWindow(hwnd, NativeMethods.GW_HWNDPREV);
                if (hwnd == IntPtr.Zero) 
                { 
                    // Very top of the Windows hierarchy we're done
                    if (hwndCurrent == _hwndDesktop) 
                    {
                        break;
                    }
 
                    // The desktop is the parent we should stop here
                    if (Misc.IsBitSet(Misc.GetWindowStyle(hwndCurrent), NativeMethods.WS_POPUP)) 
                    { 
                        hwnd = _hwndDesktop;
                    } 
                    else
                    {
                        // We finished with all the hwnd siblings so get to the parent
                        hwnd = Misc.GetParent(hwndCurrent); 
                    }
 
                    if (hwnd == IntPtr.Zero) 
                    {
                        // final clipping against the desktop 
                        hwnd = _hwndDesktop;
                    }

                    // For parent, the element bounding rectangle must be within it's parent bounding rect 
                    // The desktop contains the bounding rectangle only for the main monintor, the
                    // The Progman window contains the area for the union of all the monitors 
                    // Substitute the Desktop with the Progman hwnd for clipping calculation 
                    IntPtr hwndClip = hwnd == _hwndDesktop ? _hwndProgman : hwnd;
 
                    if (!ClickableInRect(hwndClip, ref pt, false, alIn, alOut))
                    {
                        return false;
                    } 

                    // Current Parent 
                    hwndCurrent = hwnd; 
                    continue;
                } 

                // For siblings, the element bounding rectangle must not be covered by the
                // bounding rect of its siblings
                if (!ClickableInRect(hwnd, ref pt, true, alIn, alOut)) 
                {
                    return false; 
                } 
            }
 
            return true;
        }

        ///  
        /// Go through the list of all element chidren and exclude them from the list of
        /// visible/clickable rectangles. 
        /// The element children may be listed in any order. A check on all of them must 
        /// be performed. There is no easy way out.
        ///  
        /// 
        /// 
        /// 
        internal static void ExcludeChildren(ProxyFragment fragment, ArrayList alIn, ArrayList alOut) 
        {
            // First go through all the children to exclude whatever is on top 
            for (ProxySimple simple = fragment.GetFirstChild(); simple != null; simple = fragment.GetNextSibling(simple)) 
            {
                // The exclusion for hwnd children is done by the GetPoint routine 
                if (simple is ProxyHwnd)
                {
                    continue;
                } 

                // Copy the output bits 
                alIn.Clear(); 
                alIn.AddRange(alOut);
 
                NativeMethods.Win32Rect rc = new NativeMethods.Win32Rect(simple.BoundingRectangle);
                CPRect rcp = new CPRect(ref rc, false);

                ClickablePoint.SplitRect(alIn, ref rcp, alOut, true); 

                // recurse on the children 
                if (simple is ProxyFragment) 
                {
                    ExcludeChildren((ProxyFragment)simple, alIn, alOut); 
                }
            }
        }
 
        #endregion
 
        // ----------------------------------------------------- 
        //
        // Internal Fields 
        //
        // -----------------------------------------------------

        #region Internal Fields 

        ///  
        /// Rectangle is inclusive exclusive 
        /// 
        internal struct CPRect 
        {
            internal bool _fNotCovered;

            internal int _left; 

            internal int _top; 
 
            internal int _right;
 
            internal int _bottom;

            internal CPRect(int left, int top, int right, int bottom, bool fRiAsInsideRect)
            { 
                _left = left;
                _top = top; 
                _right = right; 
                _bottom = bottom;
                _fNotCovered = fRiAsInsideRect; 
            }

            // ref to make it a pointer
            internal CPRect(ref NativeMethods.Win32Rect rc, bool fRiAsInsideRect) 
            {
                _left = rc.left; 
                _top = rc.top; 
                _right = rc.right;
                _bottom = rc.bottom; 
                _fNotCovered = fRiAsInsideRect;
            }

            // return true if the 2 rectangle intersects 
            internal bool Intersect(ref CPRect ri)
            { 
                return !(_top >= ri._bottom || ri._top >= _bottom || _left >= ri._right || ri._left >= _right); 
            }
 
            // return true if ri completely covers this
            internal bool Overlap(ref CPRect ri)
            {
                return (ri._left <= _left && ri._right >= _right && ri._top <= _top && ri._bottom >= _bottom); 
            }
        } 
 
        #endregion
 
        // ------------------------------------------------------
        //
        // Private Methods
        // 
        // -----------------------------------------------------
 
        #region Private Methods 

        private static bool ClickableInRect(IntPtr hwnd, ref NativeMethods.Win32Point pt, bool fRiAsInsideRect, ArrayList alIn, ArrayList alOut) 
        {
            if (!SafeNativeMethods.IsWindowVisible(hwnd))
            {
                return fRiAsInsideRect; 
            }
 
            // Get the window rect. If this window has a width and it is effectivly invisible 
            NativeMethods.Win32Rect rc = new NativeMethods.Win32Rect();
 
            if (!Misc.GetWindowRect(hwnd, ref rc))
            {
                return fRiAsInsideRect;
            } 

            if ((rc.right - rc.left) <= 0 || (rc.bottom - rc.top) <= 0) 
            { 
                return fRiAsInsideRect;
            } 

            // Try for transparency...
            if (fRiAsInsideRect)
            { 
                int x = (rc.right + rc.left) / 2;
                int y = (rc.top + rc.bottom) / 2; 
 
                try
                { 
                    int lr = Misc.ProxySendMessageInt(hwnd, NativeMethods.WM_NCHITTEST, IntPtr.Zero, NativeMethods.Util.MAKELPARAM(x, y));

                    if (lr == NativeMethods.HTTRANSPARENT)
                    { 
                        return true;
                    } 
                } 
// PRESHARP: Warning - Catch statements should not have empty bodies
#pragma warning disable 6502 
                catch (TimeoutException)
                {
                    // Ignore this timeout error.  Avalon HwndWrappers have a problem with this WM_NCHITTEST call sometimes.
                } 
#pragma warning restore 6502
            } 
 
            // Copy the output bits
            alIn.Clear(); 
            alIn.AddRange(alOut);

            CPRect rcp = new CPRect(ref rc, false);
 
            ClickablePoint.SplitRect(alIn, ref rcp, alOut, fRiAsInsideRect);
            if (!GetClickablePoint(alOut, out pt.x, out pt.y)) 
            { 
                return false;
            } 

            return true;
        }
 
        /// 
        /// Split a rectangle into a maximum of 3 rectangble. 
        ///   ro is the outside rectangle and ri is the inside rectangle 
        ///   ro might is split vertically in a a maximim of 3 rectangles sharing the same
        ///   right and left margin 
        /// 
        /// Outside Rectangle
        /// Inside Rectangle
        /// Left Margin for the resulting rectangles 
        /// Right Margin for the resulting rectangles
        /// Array of resulting rectangles 
        /// Covered flag 
        static private void SplitVertical(ref CPRect ro, ref CPRect ri, int left, int right, ArrayList alRect, bool fRiAsInsideRect)
        { 
            // bottom clip
            if (ri._bottom > ro._bottom)
            {
                ri._bottom = ro._bottom; 
            }
 
            int top = ro._top; 
            int bottom = ri._top;
 
            if (bottom > top)
            {
                alRect.Add(new CPRect(left, top, right, bottom, ro._fNotCovered));
                top = bottom; 
            }
 
            bottom = ri._bottom; 
            if (bottom > top)
            { 
                alRect.Add(new CPRect(left, top, right, bottom, ro._fNotCovered & fRiAsInsideRect));
                top = bottom;
            }
 
            bottom = ro._bottom;
            if (bottom > top) 
            { 
                alRect.Add(new CPRect(left, top, right, bottom, ro._fNotCovered));
            } 
        }

        /// 
        /// Slip a rectangle into a maximum of 5 pieces. 
        /// ro is the out rectangle and ri is the exclusion rectangle.
        /// The number of resulting rectangles varies based on the position of ri relative to ro 
        /// Each resulting reactangles are flaged  as covered or not. 
        /// The ro covered flag is or'ed to allow for recursive calls
        /// 
        ///      +-----------------+
        ///      |     :  2 :      |
        ///      |     :    :      |
        ///      |     ######      | 
        ///      |     ###3##      |
        ///      | 1   ######  5   | 
        ///      |     ######      | 
        ///      |     :    :      |
        ///      |     :  4 :      | 
        ///      |     :    :      |
        ///      +-----------------+
        /// 
        /// Outside Rectangle 
        /// Inside Rectangle
        /// Collection of resulting rectangles 
        ///  
        static private void SplitRect(ref CPRect ro, CPRect ri, ArrayList alRect, bool fRiAsInsideRect)
        { 
            // If ri is fully outside easy way out.
            if (!ro._fNotCovered || !ro.Intersect(ref ri))
            {
                ro._fNotCovered &= fRiAsInsideRect; 
                alRect.Add(ro);
                return; 
            } 

            if (ro.Overlap(ref ri)) 
            {
                ro._fNotCovered &= !fRiAsInsideRect;
                alRect.Add(ro);
                return; 
            }
 
            // right clip 
            if (ri._right > ro._right)
            { 
                ri._right = ro._right;
            }

            // bottom clip 
            if (ri._bottom > ro._bottom)
            { 
                ri._bottom = ro._bottom; 
            }
 
            int left = ro._left;
            int right = ri._left;

            if (right > left) 
            {
                alRect.Add(new CPRect(left, ro._top, right, ro._bottom, ro._fNotCovered & fRiAsInsideRect)); 
                left = right; 
            }
 
            right = ri._right;
            if (right > left)
            {
                SplitVertical(ref ro, ref ri, left, right, alRect, !fRiAsInsideRect); 
                left = right;
            } 
 
            right = ro._right;
            if (right > left) 
            {
                alRect.Add(new CPRect(left, ro._top, right, ro._bottom, ro._fNotCovered & fRiAsInsideRect));
            }
        } 

        ///  
        /// Takes as input a set of rectangles to perform a rectangular decomposition 
        /// based on the ri. It creates a new set of rectangles each of them being
        /// marked as covered or not. 
        ///
        /// 
        /// List of input rectangle
        /// Overlapping Rectangle 
        /// New sets of reactangle
        /// Input Rectangle is rectangle covering alIn Rects or everything 
        ///                               outside of ri must be marked as covered 
        static private void SplitRect(ArrayList alIn, ref CPRect ri, ArrayList alOut, bool fRiAsInsideRect)
        { 
            alOut.Clear();
            for (int i = 0, c = alIn.Count; i < c; i++)
            {
                CPRect ro = (CPRect)alIn[i]; 

                SplitRect(ref ro, ri, alOut, fRiAsInsideRect); 
            } 
        }
 
        /// 
        /// Find a clickable point in a list of rectangle.
        /// Goes through the list of rectangle, stops on the first rectangle that is not covered
        /// and returns the mid point 
        /// 
        /// list of ractangle 
        /// X coordinate for a clickable point 
        /// Y coordinate for a clickable point
        /// Clickable point found 
        static private bool GetClickablePoint(ArrayList al, out int x, out int y)
        {
            for (int i = 0, c = al.Count; i < c; i++)
            { 
                CPRect r = (CPRect)al[i];
 
                if (r._fNotCovered == true && (r._right - r._left) * (r._bottom - r._top) > 0) 
                {
                    // Skip if the rectangle is empty 
                    if (r._right > r._left && r._bottom > r._top)
                    {
                        // mid point rounded to the left
                        x = ((r._right - 1) + r._left) / 2; 
                        y = ((r._bottom - 1) + r._top) / 2;
                        return true; 
                    } 
                }
            } 

            x = y = 0;
            return false;
        } 

        #endregion 
 
        #region Private fields
 
        // Top level Desktop window
        private static IntPtr _hwndDesktop = UnsafeNativeMethods.GetDesktopWindow();

        /// The WindowsRect for "Program" is the union for the real 
        /// estate for all the monitors. Instead of doing clipping against the root of the hwnd
        /// tree that is the desktop. The last clipping should be done against the Progman hwnd. 
        private static IntPtr _hwndProgman; 

        #endregion Private fields 

    }
}

// 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