/// <summary> /// 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. /// /// </summary> /// <param name="alIn">List of input rectangle</param> /// <param name="ri">Overlapping Rectangle</param> /// <param name="alOut">New sets of reactangle</param> /// <param name="fRiAsInsideRect">Input Rectangle is rectangle covering alIn Rects or everything /// outside of ri must be marked as covered</param> 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); } }
/// <summary> /// 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 : | /// | : : | /// +-----------------+ /// </summary> /// <param name="ro">Outside Rectangle</param> /// <param name="ri">Inside Rectangle</param> /// <param name="alRect">Collection of resulting rectangles</param> /// <param name="fRiAsInsideRect"></param> 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)); } }
/// <summary> /// 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 /// </summary> /// <param name="al">list of ractangle</param> /// <param name="x">X coordinate for a clickable point</param> /// <param name="y">Y coordinate for a clickable point</param> /// <returns>Clickable point found</returns> 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); }
// 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); }
/// <summary> /// 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 /// </summary> /// <param name="ro">Outside Rectangle</param> /// <param name="ri">Inside Rectangle</param> /// <param name="left">Left Margin for the resulting rectangles</param> /// <param name="right">Right Margin for the resulting rectangles</param> /// <param name="alRect">Array of resulting rectangles</param> /// <param name="fRiAsInsideRect">Covered flag</param> 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)); } }
// ------------------------------------------------------ // // 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; }
// 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); }
// 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); }
/// <summary> /// 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. /// </summary> /// <param name="fragment"></param> /// <param name="alIn"></param> /// <param name="alOut"></param> 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); } } }