[Win32SS|UxTheme]
[reactos.git] / reactos / win32ss / user / ntuser / scrollex.c
index e0cd635..64576ec 100644 (file)
@@ -226,6 +226,241 @@ UserScrollDC(
    return Result;
 }
 
+DWORD
+FASTCALL
+IntScrollWindowEx(
+   PWND Window,
+   INT dx,
+   INT dy,
+   const RECT *prcScroll,
+   const RECT *prcClip,
+   HRGN hrgnUpdate,
+   LPRECT prcUpdate,
+   UINT flags)
+{
+   RECTL rcScroll, rcClip, rcCaret;
+   INT Result;
+   PWND CaretWnd;
+   HDC hDC;
+   PREGION RgnUpdate = NULL, RgnTemp, RgnWinupd = NULL;
+   HWND hwndCaret;
+   DWORD dcxflags = 0;
+   int rdw_flags;
+   USER_REFERENCE_ENTRY CaretRef;
+
+   IntGetClientRect(Window, &rcClip);
+
+   if (prcScroll)
+   {
+      RECTL_bIntersectRect(&rcScroll, &rcClip, prcScroll);
+   }
+   else
+      rcScroll = rcClip;
+
+   if (prcClip)
+   {
+      RECTL_bIntersectRect(&rcClip, &rcClip, prcClip);
+   }
+
+   if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
+         (dx == 0 && dy == 0))
+   {
+      return NULLREGION;
+   }
+
+   /* We must use a copy of the region, as we can't hold an exclusive lock
+    * on it while doing callouts to user-mode */
+   RgnUpdate = IntSysCreateRectpRgn(0, 0, 0, 0);
+   if(!RgnUpdate)
+   {
+       EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
+       return ERROR;
+   }
+
+   if (hrgnUpdate)
+   {
+       RgnTemp = REGION_LockRgn(hrgnUpdate);
+       if (!RgnTemp)
+       {
+           EngSetLastError(ERROR_INVALID_HANDLE);
+           return ERROR;
+       }
+       IntGdiCombineRgn(RgnUpdate, RgnTemp, NULL, RGN_COPY);
+       REGION_UnlockRgn(RgnTemp);
+   }
+
+   /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */
+   if (flags & SW_SCROLLWNDDCE)
+   {
+      dcxflags = DCX_USESTYLE;
+
+      if (!(Window->pcls->style & (CS_OWNDC|CS_CLASSDC)))
+         dcxflags |= DCX_CACHE; // AH??? wine~ If not Powned or with Class go Cheap!
+
+      if (flags & SW_SCROLLCHILDREN && Window->style & WS_CLIPCHILDREN)
+         dcxflags |= DCX_CACHE|DCX_NOCLIPCHILDREN;
+   }
+   else
+   {
+       /* So in this case ScrollWindowEx uses Cache DC. */
+       dcxflags = DCX_CACHE|DCX_USESTYLE;
+       if (flags & SW_SCROLLCHILDREN) dcxflags |= DCX_NOCLIPCHILDREN;
+   }
+
+   hDC = UserGetDCEx(Window, 0, dcxflags);
+   if (!hDC)
+   {
+      /* FIXME: SetLastError? */
+      return ERROR;
+   }
+
+   rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ? RDW_INVALIDATE | RDW_ERASE : RDW_INVALIDATE ;
+
+   rcCaret = rcScroll;
+   hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
+
+   Result = UserScrollDC( hDC,
+                          dx,
+                          dy,
+                          &rcScroll,
+                          &rcClip,
+                          NULL,
+                          RgnUpdate,
+                          prcUpdate);
+
+   UserReleaseDC(Window, hDC, FALSE);
+
+   /*
+    * Take into account the fact that some damage may have occurred during
+    * the scroll. Keep a copy in hrgnWinupd to be added to hrngUpdate at the end.
+    */
+
+   RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0);
+   if (!RgnTemp)
+   {
+       EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
+       return ERROR;
+   }
+
+   if (co_IntGetUpdateRgn(Window, RgnTemp, FALSE) != NULLREGION)
+   {
+      PREGION RgnClip = IntSysCreateRectpRgnIndirect(&rcClip);
+      if (RgnClip)
+      {
+          if (hrgnUpdate)
+          {
+             RgnWinupd = IntSysCreateRectpRgn( 0, 0, 0, 0);
+             IntGdiCombineRgn( RgnWinupd, RgnTemp, 0, RGN_COPY);
+          }
+
+          REGION_bOffsetRgn(RgnTemp, dx, dy);
+
+          IntGdiCombineRgn(RgnTemp, RgnTemp, RgnClip, RGN_AND);
+
+          if (hrgnUpdate)
+              IntGdiCombineRgn( RgnWinupd, RgnWinupd, RgnTemp, RGN_OR );
+
+          co_UserRedrawWindow(Window, NULL, RgnTemp, rdw_flags );
+
+          REGION_Delete(RgnClip);
+      }
+   }
+   REGION_Delete(RgnTemp);
+
+   if (flags & SW_SCROLLCHILDREN)
+   {
+      PWND Child;
+      RECTL rcChild;
+      POINT ClientOrigin;
+      USER_REFERENCE_ENTRY WndRef;
+      RECTL rcDummy;
+      LPARAM lParam;
+
+      IntGetClientOrigin(Window, &ClientOrigin);
+
+      for (Child = Window->spwndChild; Child; Child = Child->spwndNext)
+      {
+         rcChild = Child->rcWindow;
+         rcChild.left -= ClientOrigin.x;
+         rcChild.top -= ClientOrigin.y;
+         rcChild.right -= ClientOrigin.x;
+         rcChild.bottom -= ClientOrigin.y;
+
+         if (!prcScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll))
+         {
+            UserRefObjectCo(Child, &WndRef);
+
+            if (Window->spwndParent == UserGetDesktopWindow()) // Window->spwndParent->fnid == FNID_DESKTOP )
+               lParam = MAKELONG(Child->rcClient.left, Child->rcClient.top);
+            else
+               lParam = MAKELONG(rcChild.left + dx, rcChild.top + dy);
+
+            /* wine sends WM_POSCHANGING, WM_POSCHANGED messages */
+            /* windows sometimes a WM_MOVE */                  
+            co_IntSendMessage(UserHMGetHandle(Child), WM_MOVE, 0, lParam);
+            
+            UserDerefObjectCo(Child);
+         }
+      }
+   }
+
+   if (flags & (SW_INVALIDATE | SW_ERASE))
+   {
+      co_UserRedrawWindow( Window,
+                           NULL,
+                           RgnUpdate,
+                           rdw_flags |                                    /*    HACK    */
+                          ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : RDW_NOCHILDREN) );
+   }
+
+   if (hwndCaret && (CaretWnd = UserGetWindowObject(hwndCaret)))
+   {
+      UserRefObjectCo(CaretWnd, &CaretRef);
+
+      co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
+      co_UserShowCaret(CaretWnd);
+
+      UserDerefObjectCo(CaretWnd);
+   }
+
+   if (hrgnUpdate && (Result != ERROR))
+   {
+       /* Give everything back to the caller */
+       RgnTemp = REGION_LockRgn(hrgnUpdate);
+       /* The handle should still be valid */
+       ASSERT(RgnTemp);
+       if (RgnWinupd)
+           IntGdiCombineRgn(RgnTemp, RgnUpdate, RgnWinupd, RGN_OR);
+       else
+           IntGdiCombineRgn(RgnTemp, RgnUpdate, NULL, RGN_COPY);
+       REGION_UnlockRgn(RgnTemp);
+   }
+
+   if (RgnWinupd)
+   {
+       REGION_Delete(RgnWinupd);
+   }
+
+   if (RgnUpdate)
+   {
+      REGION_Delete(RgnUpdate);
+   }
+
+   return Result;
+}
+
+
+BOOL FASTCALL
+IntScrollWindow(PWND pWnd,
+                int dx,
+                int dy,
+                CONST RECT *lpRect,
+                CONST RECT *prcClip)
+{
+   return IntScrollWindowEx( pWnd, dx, dy, lpRect, prcClip, 0, NULL,
+                            (lpRect ? 0 : SW_SCROLLCHILDREN) | (SW_ERASE|SW_INVALIDATE|SW_SCROLLWNDDCE)) != ERROR;
+}
+
 /*
  * NtUserScrollDC
  *