From 3f238e248fc4fc4415b3ce5e420c798d516d24ee Mon Sep 17 00:00:00 2001
From: Hirofumi Katayama <katayama.hirofumi.mz@gmail.com>
Date: Mon, 26 Nov 2018 09:09:52 +0900
Subject: [PATCH] user32: Implement TileWindows

Wine lacks the implementations of TileWindows
function of window arrangement.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46197
Signed-off-by: Hirofumi Katayama <katayama.hirofumi.mz@gmail.com>
---
 dlls/user32/mdi.c | 179 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 175 insertions(+), 4 deletions(-)

diff --git a/dlls/user32/mdi.c b/dlls/user32/mdi.c
index 9679be895ed..c752f967b4d 100644
--- a/dlls/user32/mdi.c
+++ b/dlls/user32/mdi.c
@@ -144,6 +144,10 @@ typedef struct
 
 static HBITMAP hBmpClose   = 0;
 
+static WCHAR shelltray[] = {'S','h','e','l','l','_','T','r','a','y','W','n','d',0};
+static WCHAR progman[] = {'P','r','o','g','m','a','n',0};
+
+
 /* ----------------- declarations ----------------- */
 static void MDI_UpdateFrameText( HWND, HWND, BOOL, LPCWSTR);
 static BOOL MDI_AugmentFrameMenu( HWND, HWND );
@@ -1929,8 +1933,6 @@ WORD WINAPI
 CascadeWindows (HWND hwndParent, UINT wFlags, const RECT *lpRect,
 		UINT cKids, const HWND *lpKids)
 {
-    static WCHAR shelltray[] = {'S','h','e','l','l','_','T','r','a','y','W','n','d',0};
-    static WCHAR progman[] = {'P','r','o','g','m','a','n',0};
     CASCADE_INFO info;
     HWND hwnd, top, prev;
     HMONITOR monitor;
@@ -2084,8 +2086,177 @@ WORD WINAPI
 TileWindows (HWND hwndParent, UINT wFlags, const RECT *lpRect,
 	     UINT cKids, const HWND *lpKids)
 {
-    FIXME("(%p,0x%08x,...,%u,...): stub\n", hwndParent, wFlags, cKids);
-    return 0;
+    HWND hwnd, hwndTop, hwndPrev;
+    CASCADE_INFO info;
+    RECT rcWork, rcWnd;
+    DWORD i, iRow, iColumn, cRows, cColumns, ret = 0;
+    INT x, y, cx, cy, cxNew, cyNew, cxWork, cyWork, cxCell, cyCell, cxMin2, cyMin3;
+    HDWP hDWP;
+    MONITORINFO mi;
+    HMONITOR hMon;
+    POINT pt;
+
+    TRACE("(%p,0x%08x,...,%u,...)\n", hwndParent, wFlags, cKids);
+
+    hwndTop = GetTopWindow(hwndParent);
+
+    ZeroMemory(&info, sizeof(info));
+    info.desktop = GetDesktopWindow();
+    info.tray_wnd = FindWindowW(shelltray, NULL);
+    info.progman = FindWindowW(progman, NULL);
+    info.parent = hwndParent;
+    info.flags = wFlags;
+
+    if (cKids == 0 || lpKids == NULL)
+    {
+        info.top = hwndTop;
+        EnumChildWindows(hwndParent, GetCascadeChildProc, (LPARAM)&info);
+
+        info.top = NULL;
+        GetCascadeChildProc(hwndTop, (LPARAM)&info);
+    }
+    else
+    {
+        info.wnd_count = cKids;
+        info.wnd_array = (HWND *)lpKids;
+    }
+
+    if (info.wnd_count == 0 || info.wnd_array == NULL)
+        return ret;
+
+    if (lpRect)
+    {
+        rcWork = *lpRect;
+    }
+    else if (hwndParent)
+    {
+        GetClientRect(hwndParent, &rcWork);
+    }
+    else
+    {
+        pt.x = pt.y = 0;
+        hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
+        mi.cbSize = sizeof(mi);
+        GetMonitorInfoW(hMon, &mi);
+        rcWork = mi.rcWork;
+    }
+
+    cxWork = rcWork.right - rcWork.left;
+    cyWork = rcWork.bottom - rcWork.top;
+
+    cxMin2 = GetSystemMetrics(SM_CXMIN) * 2;
+    cyMin3 = GetSystemMetrics(SM_CYMIN) * 3;
+
+    /* calculate the numbers and widths of columns and rows */
+    if (info.flags & MDITILE_HORIZONTAL)
+    {
+        cColumns = info.wnd_count;
+        cRows = 1;
+        for (;;)
+        {
+            cxCell = cxWork / cColumns;
+            cyCell = cyWork / cRows;
+            if (cyCell <= cyMin3 || cxCell >= cxMin2)
+                break;
+
+            ++cRows;
+            cColumns = (info.wnd_count + cRows - 1) / cRows;
+        }
+    }
+    else
+    {
+        cRows = info.wnd_count;
+        cColumns = 1;
+        for (;;)
+        {
+            cxCell = cxWork / cColumns;
+            cyCell = cyWork / cRows;
+            if (cxCell <= cxMin2 || cyCell >= cyMin3)
+                break;
+
+            ++cColumns;
+            cRows = (info.wnd_count + cColumns - 1) / cColumns;
+        }
+    }
+
+    hDWP = BeginDeferWindowPos(info.wnd_count);
+    if (hDWP == NULL)
+        goto cleanup;
+
+    x = rcWork.left;
+    y = rcWork.top;
+    hwndPrev = NULL;
+    iRow = iColumn = 0;
+    for (i = info.wnd_count; i > 0;)    /* in reverse order */
+    {
+        --i;
+        hwnd = info.wnd_array[i];
+
+        if (IsZoomed(hwnd))
+            ShowWindow(hwnd, SW_RESTORE | SW_SHOWNA);
+
+        GetWindowRect(hwnd, &rcWnd);
+        cx = rcWnd.right - rcWnd.left;
+        cy = rcWnd.bottom - rcWnd.top;
+
+        /* if we can change the window size */
+        if (GetWindowLongPtrW(hwnd, GWL_STYLE) & WS_THICKFRAME)
+        {
+            cxNew = cxCell;
+            cyNew = cyCell;
+            /* shrink if we can */
+            if (QuerySizeFix(hwnd, &cxNew, &cyNew))
+            {
+                cx = cxNew;
+                cy = cyNew;
+            }
+        }
+
+        hDWP = DeferWindowPos(hDWP, hwnd, HWND_TOP, x, y, cx, cy, SWP_NOACTIVATE);
+        if (hDWP == NULL)
+        {
+            ret = 0;
+            goto cleanup;
+        }
+
+        if (info.flags & MDITILE_HORIZONTAL)
+        {
+            x += cxCell;
+            ++iColumn;
+            if (iColumn >= cColumns)
+            {
+                iColumn = 0;
+                ++iRow;
+                x = rcWork.left;
+                y += cyCell;
+            }
+        }
+        else
+        {
+            y += cyCell;
+            ++iRow;
+            if (iRow >= cRows)
+            {
+                iRow = 0;
+                ++iColumn;
+                x += cxCell;
+                y = rcWork.top;
+            }
+        }
+        hwndPrev = hwnd;
+        ++ret;
+    }
+
+    EndDeferWindowPos(hDWP);
+
+    if (hwndPrev)
+        SetForegroundWindow(hwndPrev);
+
+cleanup:
+    if (cKids == 0 || lpKids == NULL)
+        HeapFree(GetProcessHeap(), 0, info.wnd_array);
+
+    return (WORD)ret;
 }
 
 
-- 
2.24.1

