mdi.c 61.7 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3
/* MDI.C
 *
 * Copyright 1994, Bob Amstadt
Alexandre Julliard's avatar
Alexandre Julliard committed
4
 *           1995,1996 Alex Korobka
Alexandre Julliard's avatar
Alexandre Julliard committed
5
 *
6 7
 * This file contains routines to support MDI (Multiple Document
 * Interface) features .
Alexandre Julliard's avatar
Alexandre Julliard committed
8
 *
9 10 11 12 13 14 15 16 17 18 19 20
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
21
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22
 *
23
 * Notes: Fairly complete implementation.
Alexandre Julliard's avatar
Alexandre Julliard committed
24
 *        Also, Excel and WinWord do _not_ use MDI so if you're trying
25
 *	  to fix them look elsewhere.
26 27 28 29 30
 *
 * Notes on how the "More Windows..." is implemented:
 *
 *      When we have more than 9 opened windows, a "More Windows..."
 *      option appears in the "Windows" menu. Each child window has
31
 *      a WND* associated with it, accessible via the children list of
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
 *      the parent window. This WND* has a wIDmenu member, which reflects
 *      the position of the child in the window list. For example, with
 *      9 child windows, we could have the following pattern:
 *
 *
 *
 *                Name of the child window    pWndChild->wIDmenu
 *                     Doc1                       5000
 *                     Doc2                       5001
 *                     Doc3                       5002
 *                     Doc4                       5003
 *                     Doc5                       5004
 *                     Doc6                       5005
 *                     Doc7                       5006
 *                     Doc8                       5007
 *                     Doc9                       5008
 *
 *
 *       The "Windows" menu, as the "More windows..." dialog, are constructed
 *       in this order. If we add a child, we would have the following list:
 *
 *
 *               Name of the child window    pWndChild->wIDmenu
 *                     Doc1                       5000
 *                     Doc2                       5001
 *                     Doc3                       5002
 *                     Doc4                       5003
 *                     Doc5                       5004
 *                     Doc6                       5005
 *                     Doc7                       5006
 *                     Doc8                       5007
 *                     Doc9                       5008
 *                     Doc10                      5009
 *
 *       But only 5000 to 5008 would be displayed in the "Windows" menu. We want
 *       the last created child to be in the menu, so we swap the last child with
 *       the 9th... Doc9 will be accessible via the "More Windows..." option.
 *
 *                     Doc1                       5000
 *                     Doc2                       5001
 *                     Doc3                       5002
 *                     Doc4                       5003
 *                     Doc5                       5004
 *                     Doc6                       5005
 *                     Doc7                       5006
 *                     Doc8                       5007
 *                     Doc9                       5009
 *                     Doc10                      5008
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
81
 */
Alexandre Julliard's avatar
Alexandre Julliard committed
82

Alexandre Julliard's avatar
Alexandre Julliard committed
83
#include <stdlib.h>
84
#include <stdarg.h>
85
#include <stdio.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
86
#include <string.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
87
#include <math.h>
88

89 90
#define OEMRESOURCE

91
#include "windef.h"
92
#include "winbase.h"
93
#include "wingdi.h"
94
#include "winuser.h"
95
#include "wownt32.h"
96
#include "wine/unicode.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
97
#include "win.h"
98
#include "controls.h"
99
#include "user_private.h"
100
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
101

102
WINE_DEFAULT_DEBUG_CHANNEL(mdi);
103

104 105 106 107 108
#define MDI_MAXTITLELENGTH      0xa1

#define WM_MDICALCCHILDSCROLL   0x10ac /* this is exactly what Windows uses */

/* "More Windows..." definitions */
109
#define MDI_MOREWINDOWSLIMIT    9       /* after this number of windows, a "More Windows..."
110 111
                                           option will appear under the Windows menu */
#define MDI_IDC_LISTBOX         100
112
#define IDS_MDI_MOREWINDOWS     13
113

Alexandre Julliard's avatar
Alexandre Julliard committed
114 115
#define MDIF_NEEDUPDATE		0x0001

116 117
typedef struct
{
118 119 120
    /* At some points, particularly when switching MDI children, active and
     * maximized MDI children may be not the same window, so we need to track
     * them separately.
121 122 123 124 125 126 127 128
     * The only place where we switch to/from maximized state is DefMDIChildProc
     * WM_SIZE/SIZE_MAXIMIZED handler. We get that notification only after the
     * ShowWindow(SW_SHOWMAXIMIZED) request, therefore window is guaranteed to
     * be visible at the time we get the notification, and it's safe to assume
     * that hwndChildMaximized is always visible.
     * If the app plays games with WS_VISIBLE, WS_MAXIMIZE or any other window
     * states it must keep coherency with USER32 on its own. This is true for
     * Windows as well.
129
     */
130
    UINT      nActiveChildren;
131
    HWND      hwndChildMaximized;
132
    HWND      hwndActiveChild;
133 134
    HWND      *child; /* array of tracked children */
    HMENU     hFrameMenu;
135 136 137 138 139 140 141 142
    HMENU     hWindowMenu;
    UINT      idFirstChild;
    LPWSTR    frameTitle;
    UINT      nTotalCreated;
    UINT      mdiFlags;
    UINT      sbRecalc;   /* SB_xxx flags for scrollbar fixup */
} MDICLIENTINFO;

143
static HBITMAP hBmpClose   = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
144

Alexandre Julliard's avatar
Alexandre Julliard committed
145
/* ----------------- declarations ----------------- */
146
static void MDI_UpdateFrameText( HWND, HWND, BOOL, LPCWSTR);
147 148 149
static BOOL MDI_AugmentFrameMenu( HWND, HWND );
static BOOL MDI_RestoreFrameMenu( HWND, HWND );
static LONG MDI_ChildActivate( HWND, HWND );
150
static LRESULT MDI_RefreshMenu(MDICLIENTINFO *);
Alexandre Julliard's avatar
Alexandre Julliard committed
151

152
static HWND MDI_MoreWindowsDialog(HWND);
153

Alexandre Julliard's avatar
Alexandre Julliard committed
154 155 156
/* -------- Miscellaneous service functions ----------
 *
 *			MDI_GetChildByID
Alexandre Julliard's avatar
Alexandre Julliard committed
157
 */
158
static HWND MDI_GetChildByID(HWND hwnd, UINT id, MDICLIENTINFO *ci)
Alexandre Julliard's avatar
Alexandre Julliard committed
159
{
160 161
    int i;

162
    for (i = 0; ci->nActiveChildren; i++)
163
    {
164
        if (GetWindowLongPtrW( ci->child[i], GWLP_ID ) == id)
165
            return ci->child[i];
166
    }
167
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
168 169
}

170
static void MDI_PostUpdate(HWND hwnd, MDICLIENTINFO* ci, WORD recalc)
Alexandre Julliard's avatar
Alexandre Julliard committed
171
{
Alexandre Julliard's avatar
Alexandre Julliard committed
172 173 174
    if( !(ci->mdiFlags & MDIF_NEEDUPDATE) )
    {
	ci->mdiFlags |= MDIF_NEEDUPDATE;
175
	PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
176 177
    }
    ci->sbRecalc = recalc;
Alexandre Julliard's avatar
Alexandre Julliard committed
178 179
}

180 181 182 183

/*********************************************************************
 * MDIClient class descriptor
 */
184
static const WCHAR mdiclientW[] = {'M','D','I','C','l','i','e','n','t',0};
185 186
const struct builtin_class_descr MDICLIENT_builtin_class =
{
187
    mdiclientW,             /* name */
188
    0,                      /* style */
189
    WINPROC_MDICLIENT,      /* proc */
190
    sizeof(MDICLIENTINFO),  /* extra */
191
    IDC_ARROW,              /* cursor */
192
    (HBRUSH)(COLOR_APPWORKSPACE+1)    /* brush */
193 194 195
};


196 197 198
static MDICLIENTINFO *get_client_info( HWND client )
{
    MDICLIENTINFO *ret = NULL;
199
    WND *win = WIN_GetPtr( client );
200 201
    if (win)
    {
202
        if (win == WND_OTHER_PROCESS || win == WND_DESKTOP)
203
        {
204
            if (IsWindow(client)) WARN( "client %p belongs to other process\n", client );
205 206
            return NULL;
        }
207 208 209 210
        if (win->flags & WIN_ISMDICLIENT)
            ret = (MDICLIENTINFO *)win->wExtra;
        else
            WARN( "%p is not an MDI client\n", client );
211
        WIN_ReleasePtr( win );
212 213 214 215
    }
    return ret;
}

216 217 218 219 220 221 222 223 224 225 226 227 228 229
static BOOL is_close_enabled(HWND hwnd, HMENU hSysMenu)
{
    if (GetClassLongW(hwnd, GCL_STYLE) & CS_NOCLOSE) return FALSE;

    if (!hSysMenu) hSysMenu = GetSystemMenu(hwnd, FALSE);
    if (hSysMenu)
    {
        UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
        if (state == 0xFFFFFFFF || (state & (MF_DISABLED | MF_GRAYED)))
            return FALSE;
    }
    return TRUE;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
230 231 232
/**********************************************************************
 * 			MDI_GetWindow
 *
233
 * returns "activatable" child different from the current or zero
Alexandre Julliard's avatar
Alexandre Julliard committed
234
 */
235
static HWND MDI_GetWindow(MDICLIENTINFO *clientInfo, HWND hWnd, BOOL bNext,
Alexandre Julliard's avatar
Alexandre Julliard committed
236
                            DWORD dwStyleMask )
Alexandre Julliard's avatar
Alexandre Julliard committed
237
{
238 239 240 241
    int i;
    HWND *list;
    HWND last = 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
242
    dwStyleMask |= WS_DISABLED | WS_VISIBLE;
Alexandre Julliard's avatar
Alexandre Julliard committed
243
    if( !hWnd ) hWnd = clientInfo->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
244

245
    if (!(list = WIN_ListChildren( GetParent(hWnd) ))) return 0;
246 247 248 249
    i = 0;
    /* start from next after hWnd */
    while (list[i] && list[i] != hWnd) i++;
    if (list[i]) i++;
Alexandre Julliard's avatar
Alexandre Julliard committed
250

251
    for ( ; list[i]; i++)
Alexandre Julliard's avatar
Alexandre Julliard committed
252
    {
253 254 255 256 257 258 259 260 261 262 263 264
        if (GetWindow( list[i], GW_OWNER )) continue;
        if ((GetWindowLongW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue;
        last = list[i];
        if (bNext) goto found;
    }
    /* now restart from the beginning */
    for (i = 0; list[i] && list[i] != hWnd; i++)
    {
        if (GetWindow( list[i], GW_OWNER )) continue;
        if ((GetWindowLongW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue;
        last = list[i];
        if (bNext) goto found;
Alexandre Julliard's avatar
Alexandre Julliard committed
265
    }
266
 found:
267
    HeapFree( GetProcessHeap(), 0, list );
268
    return last;
Alexandre Julliard's avatar
Alexandre Julliard committed
269 270
}

Alexandre Julliard's avatar
Alexandre Julliard committed
271 272 273
/**********************************************************************
 *			MDI_CalcDefaultChildPos
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
274
 *  It seems that the default height is about 2/3 of the client rect
Alexandre Julliard's avatar
Alexandre Julliard committed
275
 */
276
void MDI_CalcDefaultChildPos( HWND hwndClient, INT total, LPPOINT lpPos, INT delta, UINT *id )
Alexandre Julliard's avatar
Alexandre Julliard committed
277
{
278
    INT  nstagger;
279
    RECT rect;
280
    INT spacing = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME) - 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
281

282
    if (total < 0) /* we are called from CreateWindow */
283 284
    {
        MDICLIENTINFO *ci = get_client_info(hwndClient);
285
        total = ci->nTotalCreated;
286
        *id = ci->idFirstChild + ci->nActiveChildren;
287
        TRACE("MDI child id %04x\n", *id);
288 289 290
    }

    GetClientRect( hwndClient, &rect );
291
    if( rect.bottom - rect.top - delta >= spacing )
Alexandre Julliard's avatar
Alexandre Julliard committed
292 293 294 295 296
	rect.bottom -= delta;

    nstagger = (rect.bottom - rect.top)/(3 * spacing);
    lpPos[1].x = (rect.right - rect.left - nstagger * spacing);
    lpPos[1].y = (rect.bottom - rect.top - nstagger * spacing);
297
    lpPos[0].x = lpPos[0].y = spacing * (total%(nstagger+1));
Alexandre Julliard's avatar
Alexandre Julliard committed
298
}
Alexandre Julliard's avatar
Alexandre Julliard committed
299

Alexandre Julliard's avatar
Alexandre Julliard committed
300
/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
301
 *            MDISetMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
302
 */
303 304
static LRESULT MDISetMenu( HWND hwnd, HMENU hmenuFrame,
                           HMENU hmenuWindow)
Alexandre Julliard's avatar
Alexandre Julliard committed
305
{
Alexandre Julliard's avatar
Alexandre Julliard committed
306
    MDICLIENTINFO *ci;
307
    HWND hwndFrame = GetParent(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
308

309
    TRACE("%p, frame menu %p, window menu %p\n", hwnd, hmenuFrame, hmenuWindow);
Alexandre Julliard's avatar
Alexandre Julliard committed
310

311 312 313 314 315
    if (hmenuFrame && !IsMenu(hmenuFrame))
    {
	WARN("hmenuFrame is not a menu handle\n");
	return 0L;
    }
316

317 318 319 320 321
    if (hmenuWindow && !IsMenu(hmenuWindow))
    {
	WARN("hmenuWindow is not a menu handle\n");
	return 0L;
    }
322 323

    if (!(ci = get_client_info( hwnd ))) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
324

325 326
    TRACE("old frame menu %p, old window menu %p\n", ci->hFrameMenu, ci->hWindowMenu);

327 328 329
    if (hmenuFrame)
    {
        if (hmenuFrame == ci->hFrameMenu) return (LRESULT)hmenuFrame;
330

331 332
        if (ci->hwndChildMaximized)
            MDI_RestoreFrameMenu( hwndFrame, ci->hwndChildMaximized );
333
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
334

335
    if( hmenuWindow && hmenuWindow != ci->hWindowMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
336
    {
337
        /* delete menu items from ci->hWindowMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
338
         * and add them to hmenuWindow */
339 340
        /* Agent newsreader calls this function with  ci->hWindowMenu == NULL */
        if( ci->hWindowMenu && ci->nActiveChildren )
Alexandre Julliard's avatar
Alexandre Julliard committed
341
        {
342
            UINT nActiveChildren_old = ci->nActiveChildren;
343

344 345 346
            /* Remove all items from old Window menu */
            ci->nActiveChildren = 0;
            MDI_RefreshMenu(ci);
347

348
            ci->hWindowMenu = hmenuWindow;
Alexandre Julliard's avatar
Alexandre Julliard committed
349

350 351 352
            /* Add items to the new Window menu */
            ci->nActiveChildren = nActiveChildren_old;
            MDI_RefreshMenu(ci);
Alexandre Julliard's avatar
Alexandre Julliard committed
353
        }
354
        else
355
            ci->hWindowMenu = hmenuWindow;
356
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
357

358
    if (hmenuFrame)
Alexandre Julliard's avatar
Alexandre Julliard committed
359
    {
360
        SetMenu(hwndFrame, hmenuFrame);
361
        if( hmenuFrame != ci->hFrameMenu )
362
        {
363 364 365
            HMENU oldFrameMenu = ci->hFrameMenu;

            ci->hFrameMenu = hmenuFrame;
366
            if (ci->hwndChildMaximized)
367
                MDI_AugmentFrameMenu( hwndFrame, ci->hwndChildMaximized );
368

369
            return (LRESULT)oldFrameMenu;
370
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
371
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
372 373
    else
    {
374 375 376 377 378 379
        /* SetMenu() may already have been called, meaning that this window
         * already has its menu. But they may have done a SetMenu() on
         * an MDI window, and called MDISetMenu() after the fact, meaning
         * that the "if" to this "else" wouldn't catch the need to
         * augment the frame menu.
         */
380 381
        if( ci->hwndChildMaximized )
            MDI_AugmentFrameMenu( hwndFrame, ci->hwndChildMaximized );
Alexandre Julliard's avatar
Alexandre Julliard committed
382
    }
383

Alexandre Julliard's avatar
Alexandre Julliard committed
384 385
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
386

Alexandre Julliard's avatar
Alexandre Julliard committed
387 388 389
/**********************************************************************
 *            MDIRefreshMenu
 */
390
static LRESULT MDI_RefreshMenu(MDICLIENTINFO *ci)
Alexandre Julliard's avatar
Alexandre Julliard committed
391
{
392
    UINT i, count, visible, id;
393
    WCHAR buf[MDI_MAXTITLELENGTH];
Alexandre Julliard's avatar
Alexandre Julliard committed
394

395
    TRACE("children %u, window menu %p\n", ci->nActiveChildren, ci->hWindowMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
396

397 398
    if (!ci->hWindowMenu)
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
399

400 401 402 403 404 405
    if (!IsMenu(ci->hWindowMenu))
    {
        WARN("Window menu handle %p is no more valid\n", ci->hWindowMenu);
        return 0;
    }

406 407
    /* Windows finds the last separator in the menu, and if after it
     * there is a menu item with MDI magic ID removes all existing
408 409
     * menu items after it, and then adds visible MDI children.
     */
410
    count = GetMenuItemCount(ci->hWindowMenu);
411 412 413 414 415 416 417
    for (i = 0; i < count; i++)
    {
        MENUITEMINFOW mii;

        memset(&mii, 0, sizeof(mii));
        mii.cbSize = sizeof(mii);
        mii.fMask  = MIIM_TYPE;
418 419 420 421 422 423 424 425 426
        if (GetMenuItemInfoW(ci->hWindowMenu, i, TRUE, &mii))
        {
            if (mii.fType & MF_SEPARATOR)
            {
                /* Windows checks only ID of the menu item */
                memset(&mii, 0, sizeof(mii));
                mii.cbSize = sizeof(mii);
                mii.fMask  = MIIM_ID;
                if (GetMenuItemInfoW(ci->hWindowMenu, i + 1, TRUE, &mii))
427 428 429 430 431 432 433 434 435 436
                {
                    if (mii.wID == ci->idFirstChild)
                    {
                        TRACE("removing %u items including separator\n", count - i);
                        while (RemoveMenu(ci->hWindowMenu, i, MF_BYPOSITION))
                            /* nothing */;

                        break;
                    }
                }
437 438
            }
        }
439 440
    }

441 442
    visible = 0;
    for (i = 0; i < ci->nActiveChildren; i++)
Alexandre Julliard's avatar
Alexandre Julliard committed
443
    {
444
        if (GetWindowLongW(ci->child[i], GWL_STYLE) & WS_VISIBLE)
445
        {
446 447 448 449 450 451 452 453 454 455
            id = ci->idFirstChild + visible;

            if (visible == MDI_MOREWINDOWSLIMIT)
            {
                LoadStringW(user32_module, IDS_MDI_MOREWINDOWS, buf, sizeof(buf)/sizeof(WCHAR));
                AppendMenuW(ci->hWindowMenu, MF_STRING, id, buf);
                break;
            }

            if (!visible)
456 457
                /* Visio expects that separator has id 0 */
                AppendMenuW(ci->hWindowMenu, MF_SEPARATOR, 0, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
458

459
            visible++;
Alexandre Julliard's avatar
Alexandre Julliard committed
460

461
            SetWindowLongPtrW(ci->child[i], GWLP_ID, id);
Alexandre Julliard's avatar
Alexandre Julliard committed
462

463 464 465 466
            buf[0] = '&';
            buf[1] = '0' + visible;
            buf[2] = ' ';
            InternalGetWindowText(ci->child[i], buf + 3, sizeof(buf)/sizeof(WCHAR) - 3);
467
            TRACE("Adding %p, id %u %s\n", ci->child[i], id, debugstr_w(buf));
468
            AppendMenuW(ci->hWindowMenu, MF_STRING, id, buf);
Alexandre Julliard's avatar
Alexandre Julliard committed
469

470 471
            if (ci->child[i] == ci->hwndActiveChild)
                CheckMenuItem(ci->hWindowMenu, id, MF_CHECKED);
472
        }
473 474
        else
            TRACE("MDI child %p is not visible, skipping\n", ci->child[i]);
Alexandre Julliard's avatar
Alexandre Julliard committed
475
    }
476

477
    return (LRESULT)ci->hFrameMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
478 479
}

480 481 482

/* ------------------ MDI child window functions ---------------------- */

Alexandre Julliard's avatar
Alexandre Julliard committed
483 484
/**********************************************************************
 *			MDI_ChildGetMinMaxInfo
Alexandre Julliard's avatar
Alexandre Julliard committed
485
 *
486
 * Note: The rule here is that client rect of the maximized MDI child
Alexandre Julliard's avatar
Alexandre Julliard committed
487
 *	 is equal to the client rect of the MDI client window.
Alexandre Julliard's avatar
Alexandre Julliard committed
488
 */
489
static void MDI_ChildGetMinMaxInfo( HWND client, HWND hwnd, MINMAXINFO* lpMinMax )
Alexandre Julliard's avatar
Alexandre Julliard committed
490
{
491
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
492

493 494 495
    GetClientRect( client, &rect );
    AdjustWindowRectEx( &rect, GetWindowLongW( hwnd, GWL_STYLE ),
                        0, GetWindowLongW( hwnd, GWL_EXSTYLE ));
Alexandre Julliard's avatar
Alexandre Julliard committed
496

Alexandre Julliard's avatar
Alexandre Julliard committed
497 498
    lpMinMax->ptMaxSize.x = rect.right -= rect.left;
    lpMinMax->ptMaxSize.y = rect.bottom -= rect.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
499

Alexandre Julliard's avatar
Alexandre Julliard committed
500
    lpMinMax->ptMaxPosition.x = rect.left;
501
    lpMinMax->ptMaxPosition.y = rect.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
502

503
    TRACE("max rect (%d,%d - %d, %d)\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
504
                        rect.left,rect.top,rect.right,rect.bottom);
Alexandre Julliard's avatar
Alexandre Julliard committed
505 506
}

Alexandre Julliard's avatar
Alexandre Julliard committed
507
/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
508
 *			MDI_SwitchActiveChild
509
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
510
 * Note: SetWindowPos sends WM_CHILDACTIVATE to the child window that is
511
 *       being activated
Alexandre Julliard's avatar
Alexandre Julliard committed
512
 */
513
static void MDI_SwitchActiveChild( MDICLIENTINFO *ci, HWND hwndTo, BOOL activate )
Alexandre Julliard's avatar
Alexandre Julliard committed
514
{
515
    HWND hwndPrev;
Alexandre Julliard's avatar
Alexandre Julliard committed
516

517
    hwndPrev = ci->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
518

519
    TRACE("from %p, to %p\n", hwndPrev, hwndTo);
Alexandre Julliard's avatar
Alexandre Julliard committed
520

521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
    if ( hwndTo != hwndPrev )
    {
        BOOL was_zoomed = IsZoomed(hwndPrev);

        if (was_zoomed)
        {
            /* restore old MDI child */
            SendMessageW( hwndPrev, WM_SETREDRAW, FALSE, 0 );
            ShowWindow( hwndPrev, SW_RESTORE );
            SendMessageW( hwndPrev, WM_SETREDRAW, TRUE, 0 );

            /* activate new MDI child */
            SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
            /* maximize new MDI child */
            ShowWindow( hwndTo, SW_MAXIMIZE );
        }
        /* activate new MDI child */
538
        SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | (activate ? 0 : SWP_NOACTIVATE) );
539
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
540
}
Alexandre Julliard's avatar
Alexandre Julliard committed
541

542

Alexandre Julliard's avatar
Alexandre Julliard committed
543 544 545
/**********************************************************************
 *                                      MDIDestroyChild
 */
546
static LRESULT MDIDestroyChild( HWND client, MDICLIENTINFO *ci,
547
                                HWND child, BOOL flagDestroy )
Alexandre Julliard's avatar
Alexandre Julliard committed
548
{
549 550
    UINT i;

551 552
    TRACE("# of managed children %u\n", ci->nActiveChildren);

553
    if( child == ci->hwndActiveChild )
Alexandre Julliard's avatar
Alexandre Julliard committed
554
    {
555
        HWND next = MDI_GetWindow(ci, child, TRUE, 0);
556 557
        /* flagDestroy == 0 means we were called from WM_PARENTNOTIFY handler */
        if (flagDestroy && next)
558
            MDI_SwitchActiveChild(ci, next, TRUE);
559
        else
560 561
        {
            ShowWindow(child, SW_HIDE);
562
            if (child == ci->hwndChildMaximized)
563
            {
564 565 566 567
                HWND frame = GetParent(client);
                MDI_RestoreFrameMenu(frame, child);
                ci->hwndChildMaximized = 0;
                MDI_UpdateFrameText(frame, client, TRUE, NULL);
568
            }
569 570
            if (flagDestroy)
                MDI_ChildActivate(client, 0);
571
        }
572
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
573

574 575 576 577 578 579 580 581 582 583
    for (i = 0; i < ci->nActiveChildren; i++)
    {
        if (ci->child[i] == child)
        {
            HWND *new_child = HeapAlloc(GetProcessHeap(), 0, (ci->nActiveChildren - 1) * sizeof(HWND));
            memcpy(new_child, ci->child, i * sizeof(HWND));
            if (i + 1 < ci->nActiveChildren)
                memcpy(new_child + i, ci->child + i + 1, (ci->nActiveChildren - i - 1) * sizeof(HWND));
            HeapFree(GetProcessHeap(), 0, ci->child);
            ci->child = new_child;
584 585 586

            ci->nActiveChildren--;
            break;
587 588
        }
    }
589

590 591
    if (flagDestroy)
    {
592
        SendMessageW(client, WM_MDIREFRESHMENU, 0, 0);
593 594
        MDI_PostUpdate(GetParent(child), ci, SB_BOTH+1);
        DestroyWindow(child);
Alexandre Julliard's avatar
Alexandre Julliard committed
595
    }
596

597
    TRACE("child destroyed - %p\n", child);
Alexandre Julliard's avatar
Alexandre Julliard committed
598
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
599 600
}

Alexandre Julliard's avatar
Alexandre Julliard committed
601

Alexandre Julliard's avatar
Alexandre Julliard committed
602
/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
603 604
 *					MDI_ChildActivate
 *
605 606
 * Called in response to WM_CHILDACTIVATE, or when last MDI child
 * is being deactivated.
Alexandre Julliard's avatar
Alexandre Julliard committed
607
 */
608
static LONG MDI_ChildActivate( HWND client, HWND child )
Alexandre Julliard's avatar
Alexandre Julliard committed
609
{
610
    MDICLIENTINFO *clientInfo;
611
    HWND prevActiveWnd, frame;
612 613
    BOOL isActiveFrameWnd;

614 615
    clientInfo = get_client_info( client );

616 617
    if (clientInfo->hwndActiveChild == child) return 0;

618
    TRACE("%p\n", child);
619

620 621
    frame = GetParent(client);
    isActiveFrameWnd = (GetActiveWindow() == frame);
622
    prevActiveWnd = clientInfo->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
623 624

    /* deactivate prev. active child */
625
    if(prevActiveWnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
626
    {
627 628
        SendMessageW( prevActiveWnd, WM_NCACTIVATE, FALSE, 0L );
        SendMessageW( prevActiveWnd, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child);
Alexandre Julliard's avatar
Alexandre Julliard committed
629
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
630

631
    MDI_SwitchActiveChild( clientInfo, child, FALSE );
632 633
    clientInfo->hwndActiveChild = child;

634 635
    MDI_RefreshMenu(clientInfo);

Alexandre Julliard's avatar
Alexandre Julliard committed
636
    if( isActiveFrameWnd )
637
    {
638
        SendMessageW( child, WM_NCACTIVATE, TRUE, 0L);
639 640 641 642 643 644 645
        /* Let the client window manage focus for children, but if the focus
         * is already on the client (for instance this is the 1st child) then
         * SetFocus won't work. It appears that Windows sends WM_SETFOCUS
         * manually in this case.
         */
        if (SetFocus(client) == client)
            SendMessageW( client, WM_SETFOCUS, (WPARAM)client, 0 );
646
    }
647 648

    SendMessageW( child, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child );
649
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
650
}
Alexandre Julliard's avatar
Alexandre Julliard committed
651

Alexandre Julliard's avatar
Alexandre Julliard committed
652 653 654 655 656
/* -------------------- MDI client window functions ------------------- */

/**********************************************************************
 *				CreateMDIMenuBitmap
 */
657
static HBITMAP CreateMDIMenuBitmap(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
658
{
659 660
 HDC 		hDCSrc  = CreateCompatibleDC(0);
 HDC		hDCDest	= CreateCompatibleDC(hDCSrc);
661
 HBITMAP	hbClose = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_OLD_CLOSE) );
662 663
 HBITMAP	hbCopy;
 HBITMAP	hobjSrc, hobjDest;
Alexandre Julliard's avatar
Alexandre Julliard committed
664

665
 hobjSrc = SelectObject(hDCSrc, hbClose);
666
 hbCopy = CreateCompatibleBitmap(hDCSrc,GetSystemMetrics(SM_CXSIZE),GetSystemMetrics(SM_CYSIZE));
667
 hobjDest = SelectObject(hDCDest, hbCopy);
Alexandre Julliard's avatar
Alexandre Julliard committed
668

669 670
 BitBlt(hDCDest, 0, 0, GetSystemMetrics(SM_CXSIZE), GetSystemMetrics(SM_CYSIZE),
          hDCSrc, GetSystemMetrics(SM_CXSIZE), 0, SRCCOPY);
671

672 673 674
 SelectObject(hDCSrc, hobjSrc);
 DeleteObject(hbClose);
 DeleteDC(hDCSrc);
Alexandre Julliard's avatar
Alexandre Julliard committed
675

676
 hobjSrc = SelectObject( hDCDest, GetStockObject(BLACK_PEN) );
Alexandre Julliard's avatar
Alexandre Julliard committed
677

678 679
 MoveToEx( hDCDest, GetSystemMetrics(SM_CXSIZE) - 1, 0, NULL );
 LineTo( hDCDest, GetSystemMetrics(SM_CXSIZE) - 1, GetSystemMetrics(SM_CYSIZE) - 1);
Alexandre Julliard's avatar
Alexandre Julliard committed
680

681 682 683
 SelectObject(hDCDest, hobjSrc );
 SelectObject(hDCDest, hobjDest);
 DeleteDC(hDCDest);
Alexandre Julliard's avatar
Alexandre Julliard committed
684 685 686 687

 return hbCopy;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
688
/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
689
 *				MDICascade
Alexandre Julliard's avatar
Alexandre Julliard committed
690
 */
691
static LONG MDICascade( HWND client, MDICLIENTINFO *ci )
Alexandre Julliard's avatar
Alexandre Julliard committed
692
{
693 694 695 696
    HWND *win_array;
    BOOL has_icons = FALSE;
    int i, total;

697 698
    if (ci->hwndChildMaximized)
        SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndChildMaximized, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
699

Alexandre Julliard's avatar
Alexandre Julliard committed
700
    if (ci->nActiveChildren == 0) return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
701

702
    if (!(win_array = WIN_ListChildren( client ))) return 0;
703 704 705

    /* remove all the windows we don't want */
    for (i = total = 0; win_array[i]; i++)
Alexandre Julliard's avatar
Alexandre Julliard committed
706
    {
707 708 709 710 711 712 713 714
        if (!IsWindowVisible( win_array[i] )) continue;
        if (GetWindow( win_array[i], GW_OWNER )) continue; /* skip owned windows */
        if (IsIconic( win_array[i] ))
        {
            has_icons = TRUE;
            continue;
        }
        win_array[total++] = win_array[i];
Alexandre Julliard's avatar
Alexandre Julliard committed
715
    }
716
    win_array[total] = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
717

718 719 720 721 722 723 724 725 726
    if (total)
    {
        INT delta = 0, n = 0, i;
        POINT pos[2];
        if (has_icons) delta = GetSystemMetrics(SM_CYICONSPACING) + GetSystemMetrics(SM_CYICON);

        /* walk the list (backwards) and move windows */
        for (i = total - 1; i >= 0; i--)
        {
727 728
            LONG style;
            LONG posOptions = SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER;
729 730

            MDI_CalcDefaultChildPos(client, n++, pos, delta, NULL);
731
            TRACE("move %p to (%d,%d) size [%d,%d]\n",
732
                  win_array[i], pos[0].x, pos[0].y, pos[1].x, pos[1].y);
733
            style = GetWindowLongW(win_array[i], GWL_STYLE);
734

735
            if (!(style & WS_SIZEBOX)) posOptions |= SWP_NOSIZE;
736
            SetWindowPos( win_array[i], 0, pos[0].x, pos[0].y, pos[1].x, pos[1].y,
737
                           posOptions);
738 739
        }
    }
740
    HeapFree( GetProcessHeap(), 0, win_array );
741 742

    if (has_icons) ArrangeIconicWindows( client );
Alexandre Julliard's avatar
Alexandre Julliard committed
743 744 745
    return 0;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
746 747 748
/**********************************************************************
 *					MDITile
 */
749
static void MDITile( HWND client, MDICLIENTINFO *ci, WPARAM wParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
750
{
751 752 753
    HWND *win_array;
    int i, total;
    BOOL has_icons = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
754

755 756
    if (ci->hwndChildMaximized)
        SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndChildMaximized, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
757

Alexandre Julliard's avatar
Alexandre Julliard committed
758
    if (ci->nActiveChildren == 0) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
759

760
    if (!(win_array = WIN_ListChildren( client ))) return;
761 762 763 764 765 766 767 768 769 770 771 772 773 774 775

    /* remove all the windows we don't want */
    for (i = total = 0; win_array[i]; i++)
    {
        if (!IsWindowVisible( win_array[i] )) continue;
        if (GetWindow( win_array[i], GW_OWNER )) continue; /* skip owned windows (icon titles) */
        if (IsIconic( win_array[i] ))
        {
            has_icons = TRUE;
            continue;
        }
        if ((wParam & MDITILE_SKIPDISABLED) && !IsWindowEnabled( win_array[i] )) continue;
        win_array[total++] = win_array[i];
    }
    win_array[total] = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
776

777
    TRACE("%u windows to tile\n", total);
Alexandre Julliard's avatar
Alexandre Julliard committed
778

779
    if (total)
Alexandre Julliard's avatar
Alexandre Julliard committed
780
    {
781 782 783 784
        HWND *pWnd = win_array;
        RECT rect;
        int x, y, xsize, ysize;
        int rows, columns, r, c, i;
Alexandre Julliard's avatar
Alexandre Julliard committed
785

786 787 788
        GetClientRect(client,&rect);
        rows    = (int) sqrt((double)total);
        columns = total / rows;
Alexandre Julliard's avatar
Alexandre Julliard committed
789

790 791 792 793 794 795
        if( wParam & MDITILE_HORIZONTAL )  /* version >= 3.1 */
        {
            i = rows;
            rows = columns;  /* exchange r and c */
            columns = i;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
796

797 798 799 800 801
        if (has_icons)
        {
            y = rect.bottom - 2 * GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON);
            rect.bottom = ( y - GetSystemMetrics(SM_CYICON) < rect.top )? rect.bottom: y;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
802

803 804 805 806 807 808 809 810 811 812 813 814 815 816
        ysize   = rect.bottom / rows;
        xsize   = rect.right  / columns;

        for (x = i = 0, c = 1; c <= columns && *pWnd; c++)
        {
            if (c == columns)
            {
                rows  = total - i;
                ysize = rect.bottom / rows;
            }

            y = 0;
            for (r = 1; r <= rows && *pWnd; r++, i++)
            {
817 818 819 820 821
                LONG posOptions = SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER;
                LONG style = GetWindowLongW(win_array[i], GWL_STYLE);
                if (!(style & WS_SIZEBOX)) posOptions |= SWP_NOSIZE;

                SetWindowPos(*pWnd, 0, x, y, xsize, ysize, posOptions);
822 823 824 825 826
                y += ysize;
                pWnd++;
            }
            x += xsize;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
827
    }
828
    HeapFree( GetProcessHeap(), 0, win_array );
829
    if (has_icons) ArrangeIconicWindows( client );
Alexandre Julliard's avatar
Alexandre Julliard committed
830
}
Alexandre Julliard's avatar
Alexandre Julliard committed
831

Alexandre Julliard's avatar
Alexandre Julliard committed
832
/* ----------------------- Frame window ---------------------------- */
Alexandre Julliard's avatar
Alexandre Julliard committed
833

Alexandre Julliard's avatar
Alexandre Julliard committed
834

Alexandre Julliard's avatar
Alexandre Julliard committed
835 836 837
/**********************************************************************
 *					MDI_AugmentFrameMenu
 */
838
static BOOL MDI_AugmentFrameMenu( HWND frame, HWND hChild )
Alexandre Julliard's avatar
Alexandre Julliard committed
839
{
840
    HMENU menu = GetMenu( frame );
841
    HMENU  	hSysPopup = 0;
842
    HBITMAP hSysMenuBitmap = 0;
843
    HICON hIcon;
Alexandre Julliard's avatar
Alexandre Julliard committed
844

845
    TRACE("frame %p,child %p\n",frame,hChild);
Alexandre Julliard's avatar
Alexandre Julliard committed
846

847
    if (!menu) return FALSE;
848

Alexandre Julliard's avatar
Alexandre Julliard committed
849
    /* create a copy of sysmenu popup and insert it into frame menu bar */
850
    if (!(hSysPopup = GetSystemMenu(hChild, FALSE)))
851 852
    {
        TRACE("child %p doesn't have a system menu\n", hChild);
853
        return FALSE;
854
    }
855

856
    AppendMenuW(menu, MF_HELP | MF_BITMAP,
857 858
                SC_CLOSE, is_close_enabled(hChild, hSysPopup) ?
                (LPCWSTR)HBMMENU_MBAR_CLOSE : (LPCWSTR)HBMMENU_MBAR_CLOSE_D );
859 860 861
    AppendMenuW(menu, MF_HELP | MF_BITMAP,
                SC_RESTORE, (LPCWSTR)HBMMENU_MBAR_RESTORE );
    AppendMenuW(menu, MF_HELP | MF_BITMAP,
862
                SC_MINIMIZE, (LPCWSTR)HBMMENU_MBAR_MINIMIZE ) ;
863

864
    /* The system menu is replaced by the child icon */
865
    hIcon = (HICON)SendMessageW(hChild, WM_GETICON, ICON_SMALL, 0);
866
    if (!hIcon)
867
        hIcon = (HICON)SendMessageW(hChild, WM_GETICON, ICON_BIG, 0);
868
    if (!hIcon)
869
        hIcon = LoadImageW(0, MAKEINTRESOURCEW(IDI_WINLOGO), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895
    if (hIcon)
    {
      HDC hMemDC;
      HBITMAP hBitmap, hOldBitmap;
      HBRUSH hBrush;
      HDC hdc = GetDC(hChild);

      if (hdc)
      {
        int cx, cy;
        cx = GetSystemMetrics(SM_CXSMICON);
        cy = GetSystemMetrics(SM_CYSMICON);
        hMemDC = CreateCompatibleDC(hdc);
        hBitmap = CreateCompatibleBitmap(hdc, cx, cy);
        hOldBitmap = SelectObject(hMemDC, hBitmap);
        SetMapMode(hMemDC, MM_TEXT);
        hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
        DrawIconEx(hMemDC, 0, 0, hIcon, cx, cy, 0, hBrush, DI_NORMAL);
        SelectObject (hMemDC, hOldBitmap);
        DeleteObject(hBrush);
        DeleteDC(hMemDC);
        ReleaseDC(hChild, hdc);
        hSysMenuBitmap = hBitmap;
      }
    }

896
    if( !InsertMenuA(menu,0,MF_BYPOSITION | MF_BITMAP | MF_POPUP,
897
                     (UINT_PTR)hSysPopup, (LPSTR)hSysMenuBitmap))
898
    {
899
        TRACE("not inserted\n");
900
	DestroyMenu(hSysPopup);
901
        return FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
902
    }
903

904 905 906
    EnableMenuItem(hSysPopup, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
    EnableMenuItem(hSysPopup, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
    EnableMenuItem(hSysPopup, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
907
    SetMenuDefaultItem(hSysPopup, SC_CLOSE, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
908

Alexandre Julliard's avatar
Alexandre Julliard committed
909
    /* redraw menu */
910
    DrawMenuBar(frame);
Alexandre Julliard's avatar
Alexandre Julliard committed
911

912
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
913 914 915
}

/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
916
 *					MDI_RestoreFrameMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
917
 */
918
static BOOL MDI_RestoreFrameMenu( HWND frame, HWND hChild )
Alexandre Julliard's avatar
Alexandre Julliard committed
919
{
920
    MENUITEMINFOW menuInfo;
921
    HMENU menu = GetMenu( frame );
922 923
    INT nItems;
    UINT iId;
Alexandre Julliard's avatar
Alexandre Julliard committed
924

925
    TRACE("frame %p, child %p\n", frame, hChild);
Alexandre Julliard's avatar
Alexandre Julliard committed
926

927
    if (!menu) return FALSE;
928

929 930 931 932
    /* if there is no system buttons then nothing to do */
    nItems = GetMenuItemCount(menu) - 1;
    iId = GetMenuItemID(menu, nItems);
    if ( !(iId == SC_RESTORE || iId == SC_CLOSE) )
933
        return FALSE;
934

935 936 937 938
    /*
     * Remove the system menu, If that menu is the icon of the window
     * as it is in win95, we have to delete the bitmap.
     */
939 940
    memset(&menuInfo, 0, sizeof(menuInfo));
    menuInfo.cbSize = sizeof(menuInfo);
941 942
    menuInfo.fMask  = MIIM_DATA | MIIM_TYPE;

943
    GetMenuItemInfoW(menu,
944
		     0,
945 946 947
		     TRUE,
		     &menuInfo);

948
    RemoveMenu(menu,0,MF_BYPOSITION);
949

950 951
    if ( (menuInfo.fType & MFT_BITMAP)           &&
	 (LOWORD(menuInfo.dwTypeData)!=0)        &&
952
	 (LOWORD(menuInfo.dwTypeData)!=HBITMAP_16(hBmpClose)) )
953
    {
954
        DeleteObject(HBITMAP_32(LOWORD(menuInfo.dwTypeData)));
955 956
    }

957
    /* close */
958
    DeleteMenu(menu, SC_CLOSE, MF_BYCOMMAND);
959
    /* restore */
960
    DeleteMenu(menu, SC_RESTORE, MF_BYCOMMAND);
961
    /* minimize */
962
    DeleteMenu(menu, SC_MINIMIZE, MF_BYCOMMAND);
963

964
    DrawMenuBar(frame);
Alexandre Julliard's avatar
Alexandre Julliard committed
965

966
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
967 968
}

969

Alexandre Julliard's avatar
Alexandre Julliard committed
970
/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
971 972
 *				        MDI_UpdateFrameText
 *
973
 * used when child window is maximized/restored
Alexandre Julliard's avatar
Alexandre Julliard committed
974 975
 *
 * Note: lpTitle can be NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
976
 */
977
static void MDI_UpdateFrameText( HWND frame, HWND hClient, BOOL repaint, LPCWSTR lpTitle )
Alexandre Julliard's avatar
Alexandre Julliard committed
978
{
979
    WCHAR   lpBuffer[MDI_MAXTITLELENGTH+1];
980
    MDICLIENTINFO *ci = get_client_info( hClient );
Alexandre Julliard's avatar
Alexandre Julliard committed
981

982
    TRACE("frameText %s\n", debugstr_w(lpTitle));
Alexandre Julliard's avatar
Alexandre Julliard committed
983

984
    if (!ci) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
985

986
    if (!lpTitle && !ci->frameTitle)  /* first time around, get title from the frame window */
987
    {
988 989
        GetWindowTextW( frame, lpBuffer, sizeof(lpBuffer)/sizeof(WCHAR) );
        lpTitle = lpBuffer;
990
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
991

Alexandre Julliard's avatar
Alexandre Julliard committed
992
    /* store new "default" title if lpTitle is not NULL */
993
    if (lpTitle)
Alexandre Julliard's avatar
Alexandre Julliard committed
994
    {
995
	HeapFree( GetProcessHeap(), 0, ci->frameTitle );
996 997
	if ((ci->frameTitle = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpTitle)+1)*sizeof(WCHAR))))
            strcpyW( ci->frameTitle, lpTitle );
Alexandre Julliard's avatar
Alexandre Julliard committed
998 999 1000 1001
    }

    if (ci->frameTitle)
    {
1002
	if (ci->hwndChildMaximized)
Alexandre Julliard's avatar
Alexandre Julliard committed
1003 1004 1005
	{
	    /* combine frame title and child title if possible */

1006 1007
	    static const WCHAR lpBracket[]  = {' ','-',' ','[',0};
	    static const WCHAR lpBracket2[]  = {']',0};
1008
	    int	i_frame_text_length = strlenW(ci->frameTitle);
Alexandre Julliard's avatar
Alexandre Julliard committed
1009

1010
	    lstrcpynW( lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH);
Alexandre Julliard's avatar
Alexandre Julliard committed
1011 1012 1013

	    if( i_frame_text_length + 6 < MDI_MAXTITLELENGTH )
            {
1014
		strcatW( lpBuffer, lpBracket );
1015
                if (GetWindowTextW( ci->hwndActiveChild, lpBuffer + i_frame_text_length + 4,
1016 1017 1018 1019 1020
                                    MDI_MAXTITLELENGTH - i_frame_text_length - 5 ))
                    strcatW( lpBuffer, lpBracket2 );
                else
                    lpBuffer[i_frame_text_length] = 0;  /* remove bracket */
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1021 1022 1023
	}
	else
	{
1024
            lstrcpynW(lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH+1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1025 1026 1027 1028 1029
	}
    }
    else
	lpBuffer[0] = '\0';

1030
    DefWindowProcW( frame, WM_SETTEXT, 0, (LPARAM)lpBuffer );
1031 1032 1033 1034

    if (repaint)
        SetWindowPos( frame, 0,0,0,0,0, SWP_FRAMECHANGED |
                      SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER );
Alexandre Julliard's avatar
Alexandre Julliard committed
1035
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1036 1037


Alexandre Julliard's avatar
Alexandre Julliard committed
1038
/* ----------------------------- Interface ---------------------------- */
Alexandre Julliard's avatar
Alexandre Julliard committed
1039 1040


Alexandre Julliard's avatar
Alexandre Julliard committed
1041
/**********************************************************************
1042
 *		MDIClientWndProc_common
Alexandre Julliard's avatar
Alexandre Julliard committed
1043
 */
1044
LRESULT MDIClientWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
1045
{
1046
    MDICLIENTINFO *ci;
1047

1048
    TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1049

1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060
    if (!(ci = get_client_info( hwnd )))
    {
        if (message == WM_NCCREATE)
        {
            WND *wndPtr = WIN_GetPtr( hwnd );
            wndPtr->flags |= WIN_ISMDICLIENT;
            WIN_ReleasePtr( wndPtr );
        }
        return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
                         DefWindowProcA( hwnd, message, wParam, lParam );
    }
1061

Alexandre Julliard's avatar
Alexandre Julliard committed
1062 1063 1064
    switch (message)
    {
      case WM_CREATE:
1065 1066 1067
      {
          /* Since we are using only cs->lpCreateParams, we can safely
           * cast to LPCREATESTRUCTA here */
1068 1069
        LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
        LPCLIENTCREATESTRUCT ccs = cs->lpCreateParams;
Alexandre Julliard's avatar
Alexandre Julliard committed
1070

1071 1072
        ci->hWindowMenu		= ccs->hWindowMenu;
        ci->idFirstChild	= ccs->idFirstChild;
1073
        ci->hwndChildMaximized  = 0;
1074
        ci->child = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1075
	ci->nActiveChildren	= 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1076
	ci->nTotalCreated	= 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1077
	ci->frameTitle		= NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1078
	ci->mdiFlags		= 0;
1079
        ci->hFrameMenu = GetMenu(cs->hwndParent);
Alexandre Julliard's avatar
Alexandre Julliard committed
1080

1081
	if (!hBmpClose) hBmpClose = CreateMDIMenuBitmap();
Alexandre Julliard's avatar
Alexandre Julliard committed
1082

1083
        TRACE("Client created: hwnd %p, Window menu %p, idFirst = %04x\n",
1084
              hwnd, ci->hWindowMenu, ci->idFirstChild );
1085 1086
        return 0;
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1087

Alexandre Julliard's avatar
Alexandre Julliard committed
1088
      case WM_DESTROY:
1089
      {
1090 1091
          if( ci->hwndChildMaximized )
              MDI_RestoreFrameMenu(GetParent(hwnd), ci->hwndChildMaximized);
1092 1093 1094 1095

          ci->nActiveChildren = 0;
          MDI_RefreshMenu(ci);

1096 1097
          HeapFree( GetProcessHeap(), 0, ci->child );
          HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1098

1099 1100
          return 0;
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1101 1102

      case WM_MDIACTIVATE:
1103
      {
1104 1105
        if( ci->hwndActiveChild != (HWND)wParam )
	    SetWindowPos((HWND)wParam, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE);
1106
        return 0;
1107
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1108 1109

      case WM_MDICASCADE:
1110
        return MDICascade(hwnd, ci);
Alexandre Julliard's avatar
Alexandre Julliard committed
1111 1112

      case WM_MDICREATE:
1113
        if (lParam)
1114
        {
1115 1116
            HWND child;

1117 1118 1119
            if (unicode)
            {
                MDICREATESTRUCTW *csW = (MDICREATESTRUCTW *)lParam;
1120
                child = CreateWindowExW(WS_EX_MDICHILD, csW->szClass,
1121 1122 1123 1124 1125 1126 1127 1128
                                            csW->szTitle, csW->style,
                                            csW->x, csW->y, csW->cx, csW->cy,
                                            hwnd, 0, csW->hOwner,
                                            (LPVOID)csW->lParam);
            }
            else
            {
                MDICREATESTRUCTA *csA = (MDICREATESTRUCTA *)lParam;
1129
                child = CreateWindowExA(WS_EX_MDICHILD, csA->szClass,
1130 1131 1132 1133 1134
                                            csA->szTitle, csA->style,
                                            csA->x, csA->y, csA->cx, csA->cy,
                                            hwnd, 0, csA->hOwner,
                                            (LPVOID)csA->lParam);
            }
1135
            return (LRESULT)child;
1136
        }
1137
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1138 1139

      case WM_MDIDESTROY:
1140
          return MDIDestroyChild( hwnd, ci, WIN_GetFullHandle( (HWND)wParam ), TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1141 1142

      case WM_MDIGETACTIVE:
1143
          if (lParam) *(BOOL *)lParam = IsZoomed(ci->hwndActiveChild);
1144
          return (LRESULT)ci->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
1145 1146

      case WM_MDIICONARRANGE:
Alexandre Julliard's avatar
Alexandre Julliard committed
1147
	ci->mdiFlags |= MDIF_NEEDUPDATE;
1148
        ArrangeIconicWindows( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
1149
	ci->sbRecalc = SB_BOTH+1;
1150 1151 1152
        SendMessageW( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 );
        return 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
1153
      case WM_MDIMAXIMIZE:
1154
	ShowWindow( (HWND)wParam, SW_MAXIMIZE );
1155
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1156

Alexandre Julliard's avatar
Alexandre Julliard committed
1157
      case WM_MDINEXT: /* lParam != 0 means previous window */
1158
      {
1159 1160
        HWND hwnd = wParam ? WIN_GetFullHandle((HWND)wParam) : ci->hwndActiveChild;
        HWND next = MDI_GetWindow( ci, hwnd, !lParam, 0 );
1161
        MDI_SwitchActiveChild( ci, next, TRUE );
1162 1163
        if(!lParam)
            SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1164
	break;
1165
      }
1166

Alexandre Julliard's avatar
Alexandre Julliard committed
1167
      case WM_MDIRESTORE:
1168
        ShowWindow( (HWND)wParam, SW_SHOWNORMAL );
1169
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1170 1171

      case WM_MDISETMENU:
1172
          return MDISetMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1173

Alexandre Julliard's avatar
Alexandre Julliard committed
1174
      case WM_MDIREFRESHMENU:
1175
          return MDI_RefreshMenu( ci );
Alexandre Julliard's avatar
Alexandre Julliard committed
1176

Alexandre Julliard's avatar
Alexandre Julliard committed
1177
      case WM_MDITILE:
Alexandre Julliard's avatar
Alexandre Julliard committed
1178
	ci->mdiFlags |= MDIF_NEEDUPDATE;
1179 1180
        ShowScrollBar( hwnd, SB_BOTH, FALSE );
        MDITile( hwnd, ci, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1181
        ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1182
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1183 1184 1185

      case WM_VSCROLL:
      case WM_HSCROLL:
Alexandre Julliard's avatar
Alexandre Julliard committed
1186
	ci->mdiFlags |= MDIF_NEEDUPDATE;
1187
        ScrollChildren( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1188
	ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1189
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1190 1191

      case WM_SETFOCUS:
1192 1193 1194 1195
          if (ci->hwndActiveChild && !IsIconic( ci->hwndActiveChild ))
              SetFocus( ci->hwndActiveChild );
          return 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
1196
      case WM_NCACTIVATE:
Alexandre Julliard's avatar
Alexandre Julliard committed
1197
        if( ci->hwndActiveChild )
1198
            SendMessageW(ci->hwndActiveChild, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1199
	break;
1200

Alexandre Julliard's avatar
Alexandre Julliard committed
1201
      case WM_PARENTNOTIFY:
1202
        switch (LOWORD(wParam))
Alexandre Julliard's avatar
Alexandre Julliard committed
1203
        {
1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
        case WM_CREATE:
            if (GetWindowLongW((HWND)lParam, GWL_EXSTYLE) & WS_EX_MDICHILD)
            {
                ci->nTotalCreated++;
                ci->nActiveChildren++;

                if (!ci->child)
                    ci->child = HeapAlloc(GetProcessHeap(), 0, sizeof(HWND));
                else
                    ci->child = HeapReAlloc(GetProcessHeap(), 0, ci->child, sizeof(HWND) * ci->nActiveChildren);

1215 1216 1217
                TRACE("Adding MDI child %p, # of children %d\n",
                      (HWND)lParam, ci->nActiveChildren);

1218 1219 1220 1221 1222 1223
                ci->child[ci->nActiveChildren - 1] = (HWND)lParam;
            }
            break;

        case WM_LBUTTONDOWN:
            {
1224 1225
            HWND child;
            POINT pt;
1226 1227
            pt.x = (short)LOWORD(lParam);
            pt.y = (short)HIWORD(lParam);
1228
            child = ChildWindowFromPoint(hwnd, pt);
Alexandre Julliard's avatar
Alexandre Julliard committed
1229

1230
            TRACE("notification from %p (%i,%i)\n",child,pt.x,pt.y);
Alexandre Julliard's avatar
Alexandre Julliard committed
1231

1232
            if( child && child != hwnd && child != ci->hwndActiveChild )
1233
                SetWindowPos(child, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE );
1234 1235
            break;
            }
1236 1237 1238

        case WM_DESTROY:
            return MDIDestroyChild( hwnd, ci, WIN_GetFullHandle( (HWND)lParam ), FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1239
        }
1240
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1241 1242

      case WM_SIZE:
1243
        if( ci->hwndActiveChild && IsZoomed(ci->hwndActiveChild) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1244
	{
1245 1246 1247 1248 1249 1250
	    RECT	rect;

	    rect.left = 0;
	    rect.top = 0;
	    rect.right = LOWORD(lParam);
	    rect.bottom = HIWORD(lParam);
1251 1252 1253
	    AdjustWindowRectEx(&rect, GetWindowLongA(ci->hwndActiveChild, GWL_STYLE),
                               0, GetWindowLongA(ci->hwndActiveChild, GWL_EXSTYLE) );
	    MoveWindow(ci->hwndActiveChild, rect.left, rect.top,
Alexandre Julliard's avatar
Alexandre Julliard committed
1254 1255
			 rect.right - rect.left, rect.bottom - rect.top, 1);
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1256
	else
1257
            MDI_PostUpdate(hwnd, ci, SB_BOTH+1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1258

Alexandre Julliard's avatar
Alexandre Julliard committed
1259 1260
	break;

Alexandre Julliard's avatar
Alexandre Julliard committed
1261
      case WM_MDICALCCHILDSCROLL:
Alexandre Julliard's avatar
Alexandre Julliard committed
1262 1263
	if( (ci->mdiFlags & MDIF_NEEDUPDATE) && ci->sbRecalc )
	{
1264
            CalcChildScroll(hwnd, ci->sbRecalc-1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1265 1266 1267
	    ci->sbRecalc = 0;
	    ci->mdiFlags &= ~MDIF_NEEDUPDATE;
	}
1268
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1269
    }
1270 1271
    return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
                     DefWindowProcA( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1272 1273
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1274
/***********************************************************************
1275
 *		DefFrameProcA (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1276
 */
1277 1278
LRESULT WINAPI DefFrameProcA( HWND hwnd, HWND hwndMDIClient,
                                UINT message, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1279 1280 1281 1282 1283
{
    if (hwndMDIClient)
    {
	switch (message)
	{
1284 1285
        case WM_SETTEXT:
            {
1286 1287 1288
                DWORD len = MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, NULL, 0 );
                LPWSTR text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
                MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, text, len );
1289
                MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, text );
1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300
                HeapFree( GetProcessHeap(), 0, text );
            }
            return 1; /* success. FIXME: check text length */

        case WM_COMMAND:
        case WM_NCACTIVATE:
        case WM_NEXTMENU:
        case WM_SETFOCUS:
        case WM_SIZE:
            return DefFrameProcW( hwnd, hwndMDIClient, message, wParam, lParam );
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1301
    }
1302
    return DefWindowProcA(hwnd, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1303 1304 1305 1306
}


/***********************************************************************
1307
 *		DefFrameProcW (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1308
 */
1309 1310
LRESULT WINAPI DefFrameProcW( HWND hwnd, HWND hwndMDIClient,
                                UINT message, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1311
{
1312 1313
    MDICLIENTINFO *ci = get_client_info( hwndMDIClient );

1314
    TRACE("%p %p %04x (%s) %08lx %08lx\n", hwnd, hwndMDIClient, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1315

1316
    if (ci)
Alexandre Julliard's avatar
Alexandre Julliard committed
1317 1318 1319
    {
	switch (message)
	{
1320 1321 1322 1323 1324 1325 1326
        case WM_COMMAND:
            {
                WORD id = LOWORD(wParam);
                /* check for possible syscommands for maximized MDI child */
                if (id <  ci->idFirstChild || id >= ci->idFirstChild + ci->nActiveChildren)
                {
                    if( (id - 0xf000) & 0xf00f ) break;
1327
                    if( !ci->hwndChildMaximized ) break;
1328 1329
                    switch( id )
                    {
1330 1331
                    case SC_CLOSE:
                        if (!is_close_enabled(ci->hwndActiveChild, 0)) break;
1332 1333 1334 1335 1336 1337 1338
                    case SC_SIZE:
                    case SC_MOVE:
                    case SC_MINIMIZE:
                    case SC_MAXIMIZE:
                    case SC_NEXTWINDOW:
                    case SC_PREVWINDOW:
                    case SC_RESTORE:
1339
                        return SendMessageW( ci->hwndChildMaximized, WM_SYSCOMMAND,
1340 1341 1342 1343 1344 1345 1346 1347
                                             wParam, lParam);
                    }
                }
                else
                {
                    HWND childHwnd;
                    if (id - ci->idFirstChild == MDI_MOREWINDOWSLIMIT)
                        /* User chose "More Windows..." */
1348
                        childHwnd = MDI_MoreWindowsDialog(hwndMDIClient);
1349 1350
                    else
                        /* User chose one of the windows listed in the "Windows" menu */
1351
                        childHwnd = MDI_GetChildByID(hwndMDIClient, id, ci);
1352 1353 1354 1355 1356 1357

                    if( childHwnd )
                        SendMessageW( hwndMDIClient, WM_MDIACTIVATE, (WPARAM)childHwnd, 0 );
                }
            }
            break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1358

1359
        case WM_NCACTIVATE:
1360
	    SendMessageW(hwndMDIClient, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1361 1362
	    break;

1363
        case WM_SETTEXT:
1364
            MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, (LPWSTR)lParam );
1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378
	    return 1; /* success. FIXME: check text length */

        case WM_SETFOCUS:
	    SetFocus(hwndMDIClient);
	    break;

        case WM_SIZE:
            MoveWindow(hwndMDIClient, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
            break;

        case WM_NEXTMENU:
            {
                MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam;

1379
                if (!IsIconic(hwnd) && ci->hwndActiveChild && !IsZoomed(ci->hwndActiveChild))
1380
                {
1381
                    /* control menu is between the frame system menu and
1382
                     * the first entry of menu bar */
1383
                    WND *wndPtr = WIN_GetPtr(hwnd);
1384 1385 1386 1387

                    if( (wParam == VK_LEFT && GetMenu(hwnd) == next_menu->hmenuIn) ||
                        (wParam == VK_RIGHT && GetSubMenu(wndPtr->hSysMenu, 0) == next_menu->hmenuIn) )
                    {
1388 1389
                        WIN_ReleasePtr(wndPtr);
                        wndPtr = WIN_GetPtr(ci->hwndActiveChild);
1390 1391 1392
                        next_menu->hmenuNext = GetSubMenu(wndPtr->hSysMenu, 0);
                        next_menu->hwndNext = ci->hwndActiveChild;
                    }
1393
                    WIN_ReleasePtr(wndPtr);
1394 1395 1396
                }
                return 0;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1397 1398
	}
    }
1399

1400
    return DefWindowProcW( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1401 1402 1403
}

/***********************************************************************
1404
 *		DefMDIChildProcA (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1405
 */
1406 1407
LRESULT WINAPI DefMDIChildProcA( HWND hwnd, UINT message,
                                   WPARAM wParam, LPARAM lParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
1408
{
1409 1410 1411
    HWND client = GetParent(hwnd);
    MDICLIENTINFO *ci = get_client_info( client );

1412
    TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1413

1414
    hwnd = WIN_GetFullHandle( hwnd );
1415
    if (!ci) return DefWindowProcA( hwnd, message, wParam, lParam );
1416

Alexandre Julliard's avatar
Alexandre Julliard committed
1417 1418
    switch (message)
    {
1419
    case WM_SETTEXT:
1420
	DefWindowProcA(hwnd, message, wParam, lParam);
1421 1422
	if( ci->hwndChildMaximized == hwnd )
	    MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL );
1423 1424 1425 1426 1427 1428 1429 1430
        return 1; /* success. FIXME: check text length */

    case WM_GETMINMAXINFO:
    case WM_MENUCHAR:
    case WM_CLOSE:
    case WM_SETFOCUS:
    case WM_CHILDACTIVATE:
    case WM_SYSCOMMAND:
1431
    case WM_SHOWWINDOW:
1432 1433 1434 1435
    case WM_SETVISIBLE:
    case WM_SIZE:
    case WM_NEXTMENU:
    case WM_SYSCHAR:
1436
    case WM_DESTROY:
1437
        return DefMDIChildProcW( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1438
    }
1439
    return DefWindowProcA(hwnd, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1440 1441 1442 1443
}


/***********************************************************************
1444
 *		DefMDIChildProcW (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1445
 */
1446 1447
LRESULT WINAPI DefMDIChildProcW( HWND hwnd, UINT message,
                                   WPARAM wParam, LPARAM lParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
1448
{
1449 1450 1451
    HWND client = GetParent(hwnd);
    MDICLIENTINFO *ci = get_client_info( client );

1452
    TRACE("%p %04x (%s) %08lx %08lx\n", hwnd, message, SPY_GetMsgName(message, hwnd), wParam, lParam);
1453

1454
    hwnd = WIN_GetFullHandle( hwnd );
1455
    if (!ci) return DefWindowProcW( hwnd, message, wParam, lParam );
1456

Alexandre Julliard's avatar
Alexandre Julliard committed
1457 1458
    switch (message)
    {
1459 1460
    case WM_SETTEXT:
        DefWindowProcW(hwnd, message, wParam, lParam);
1461 1462
        if( ci->hwndChildMaximized == hwnd )
            MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL );
1463 1464 1465 1466 1467 1468 1469
        return 1; /* success. FIXME: check text length */

    case WM_GETMINMAXINFO:
        MDI_ChildGetMinMaxInfo( client, hwnd, (MINMAXINFO *)lParam );
        return 0;

    case WM_MENUCHAR:
1470
        return MAKELRESULT( 0, MNC_CLOSE ); /* MDI children don't have menu bars */
1471 1472 1473 1474 1475

    case WM_CLOSE:
        SendMessageW( client, WM_MDIDESTROY, (WPARAM)hwnd, 0 );
        return 0;

1476 1477 1478 1479 1480
    case WM_SETFOCUS:
        if (ci->hwndActiveChild != hwnd)
            MDI_ChildActivate( client, hwnd );
        break;

1481 1482 1483 1484 1485
    case WM_CHILDACTIVATE:
        MDI_ChildActivate( client, hwnd );
        return 0;

    case WM_SYSCOMMAND:
1486
        switch (wParam & 0xfff0)
1487 1488
        {
        case SC_MOVE:
1489
            if( ci->hwndChildMaximized == hwnd )
1490
                return 0;
1491 1492 1493 1494 1495
            break;
        case SC_RESTORE:
        case SC_MINIMIZE:
            break;
        case SC_MAXIMIZE:
1496
            if (ci->hwndChildMaximized == hwnd)
1497 1498 1499
                return SendMessageW( GetParent(client), message, wParam, lParam);
            break;
        case SC_NEXTWINDOW:
1500
            SendMessageW( client, WM_MDINEXT, (WPARAM)ci->hwndActiveChild, 0);
1501 1502
            return 0;
        case SC_PREVWINDOW:
1503
            SendMessageW( client, WM_MDINEXT, (WPARAM)ci->hwndActiveChild, 1);
1504 1505 1506 1507
            return 0;
        }
        break;

1508
    case WM_SHOWWINDOW:
1509
    case WM_SETVISIBLE:
1510
        if (ci->hwndChildMaximized) ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1511 1512 1513 1514
        else MDI_PostUpdate(client, ci, SB_BOTH+1);
        break;

    case WM_SIZE:
1515
        /* This is the only place where we switch to/from maximized state */
1516
        /* do not change */
1517 1518
        TRACE("current active %p, maximized %p\n", ci->hwndActiveChild, ci->hwndChildMaximized);

1519
        if( ci->hwndChildMaximized == hwnd && wParam != SIZE_MAXIMIZED )
1520
        {
1521
            HWND frame;
1522

1523 1524
            ci->hwndChildMaximized = 0;

1525 1526
            frame = GetParent(client);
            MDI_RestoreFrameMenu( frame, hwnd );
1527
            MDI_UpdateFrameText( frame, client, TRUE, NULL );
1528 1529
        }

1530 1531 1532 1533 1534
        if( wParam == SIZE_MAXIMIZED )
        {
            HWND frame, hMaxChild = ci->hwndChildMaximized;

            if( hMaxChild == hwnd ) break;
1535

1536 1537 1538
            if( hMaxChild)
            {
                SendMessageW( hMaxChild, WM_SETREDRAW, FALSE, 0 );
1539

1540 1541
                MDI_RestoreFrameMenu( GetParent(client), hMaxChild );
                ShowWindow( hMaxChild, SW_SHOWNOACTIVATE );
1542

1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554
                SendMessageW( hMaxChild, WM_SETREDRAW, TRUE, 0 );
            }

            TRACE("maximizing child %p\n", hwnd );

            /* keep track of the maximized window. */
            ci->hwndChildMaximized = hwnd; /* !!! */

            frame = GetParent(client);
            MDI_AugmentFrameMenu( frame, hwnd );
            MDI_UpdateFrameText( frame, client, TRUE, NULL );
        }
1555 1556 1557 1558 1559

        if( wParam == SIZE_MINIMIZED )
        {
            HWND switchTo = MDI_GetWindow( ci, hwnd, TRUE, WS_MINIMIZE );

1560 1561
            if (!switchTo) switchTo = hwnd;
            SendMessageW( switchTo, WM_CHILDACTIVATE, 0, 0 );
1562 1563 1564
	}

        MDI_PostUpdate(client, ci, SB_BOTH+1);
1565 1566 1567 1568 1569 1570 1571 1572 1573
        break;

    case WM_NEXTMENU:
        {
            MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam;
            HWND parent = GetParent(client);

            if( wParam == VK_LEFT )  /* switch to frame system menu */
            {
1574
                WND *wndPtr = WIN_GetPtr( parent );
1575
                next_menu->hmenuNext = GetSubMenu( wndPtr->hSysMenu, 0 );
1576
                WIN_ReleasePtr( wndPtr );
1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588
            }
            if( wParam == VK_RIGHT )  /* to frame menu bar */
            {
                next_menu->hmenuNext = GetMenu(parent);
            }
            next_menu->hwndNext = parent;
            return 0;
        }

    case WM_SYSCHAR:
        if (wParam == '-')
        {
1589
            SendMessageW( hwnd, WM_SYSCOMMAND, SC_KEYMENU, VK_SPACE);
1590 1591 1592
            return 0;
        }
        break;
1593 1594 1595 1596 1597

    case WM_DESTROY:
        /* Remove itself from the Window menu */
        MDI_RefreshMenu(ci);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1598
    }
1599
    return DefWindowProcW(hwnd, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1600 1601
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1602
/**********************************************************************
1603
 *		CreateMDIWindowA (USER32.@) Creates a MDI child
1604 1605 1606 1607
 *
 * RETURNS
 *    Success: Handle to created window
 *    Failure: NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
1608
 */
1609
HWND WINAPI CreateMDIWindowA(
1610 1611 1612
    LPCSTR lpClassName,    /* [in] Pointer to registered child class name */
    LPCSTR lpWindowName,   /* [in] Pointer to window name */
    DWORD dwStyle,         /* [in] Window style */
1613 1614 1615 1616 1617 1618
    INT X,               /* [in] Horizontal position of window */
    INT Y,               /* [in] Vertical position of window */
    INT nWidth,          /* [in] Width of window */
    INT nHeight,         /* [in] Height of window */
    HWND hWndParent,     /* [in] Handle to parent window */
    HINSTANCE hInstance, /* [in] Handle to application instance */
1619
    LPARAM lParam)         /* [in] Application-defined value */
Alexandre Julliard's avatar
Alexandre Julliard committed
1620
{
1621
    TRACE("(%s,%s,%08x,%d,%d,%d,%d,%p,%p,%08lx)\n",
1622 1623
          debugstr_a(lpClassName),debugstr_a(lpWindowName),dwStyle,X,Y,
          nWidth,nHeight,hWndParent,hInstance,lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1624

1625 1626 1627
    return CreateWindowExA(WS_EX_MDICHILD, lpClassName, lpWindowName,
                           dwStyle, X, Y, nWidth, nHeight, hWndParent,
                           0, hInstance, (LPVOID)lParam);
1628 1629
}

1630
/***********************************************************************
1631
 *		CreateMDIWindowW (USER32.@) Creates a MDI child
Alexandre Julliard's avatar
Alexandre Julliard committed
1632 1633 1634 1635
 *
 * RETURNS
 *    Success: Handle to created window
 *    Failure: NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
1636
 */
1637
HWND WINAPI CreateMDIWindowW(
1638 1639 1640
    LPCWSTR lpClassName,    /* [in] Pointer to registered child class name */
    LPCWSTR lpWindowName,   /* [in] Pointer to window name */
    DWORD dwStyle,         /* [in] Window style */
1641 1642 1643 1644 1645 1646
    INT X,               /* [in] Horizontal position of window */
    INT Y,               /* [in] Vertical position of window */
    INT nWidth,          /* [in] Width of window */
    INT nHeight,         /* [in] Height of window */
    HWND hWndParent,     /* [in] Handle to parent window */
    HINSTANCE hInstance, /* [in] Handle to application instance */
1647 1648
    LPARAM lParam)         /* [in] Application-defined value */
{
1649
    TRACE("(%s,%s,%08x,%d,%d,%d,%d,%p,%p,%08lx)\n",
1650 1651
          debugstr_w(lpClassName), debugstr_w(lpWindowName), dwStyle, X, Y,
          nWidth, nHeight, hWndParent, hInstance, lParam);
1652

1653 1654 1655
    return CreateWindowExW(WS_EX_MDICHILD, lpClassName, lpWindowName,
                           dwStyle, X, Y, nWidth, nHeight, hWndParent,
                           0, hInstance, (LPVOID)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1656 1657
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1658
/**********************************************************************
1659
 *		TranslateMDISysAccel (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1660
 */
1661
BOOL WINAPI TranslateMDISysAccel( HWND hwndClient, LPMSG msg )
Alexandre Julliard's avatar
Alexandre Julliard committed
1662
{
1663
    if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN)
Alexandre Julliard's avatar
Alexandre Julliard committed
1664
    {
1665 1666
        MDICLIENTINFO *ci = get_client_info( hwndClient );
        WPARAM wParam = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1667

1668
        if (!ci || !IsWindowEnabled(ci->hwndActiveChild)) return FALSE;
1669

1670
        /* translate if the Ctrl key is down and Alt not. */
1671

1672 1673 1674 1675 1676 1677 1678 1679 1680 1681
        if( (GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000))
        {
            switch( msg->wParam )
            {
            case VK_F6:
            case VK_TAB:
                wParam = ( GetKeyState(VK_SHIFT) & 0x8000 ) ? SC_NEXTWINDOW : SC_PREVWINDOW;
                break;
            case VK_F4:
            case VK_RBUTTON:
1682 1683 1684 1685 1686 1687
                if (is_close_enabled(ci->hwndActiveChild, 0))
                {
                    wParam = SC_CLOSE;
                    break;
                }
                /* fall through */
1688
            default:
1689
                return FALSE;
1690
            }
1691
            TRACE("wParam = %04lx\n", wParam);
1692
            SendMessageW(ci->hwndActiveChild, WM_SYSCOMMAND, wParam, msg->wParam);
1693
            return TRUE;
1694
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1695
    }
1696
    return FALSE; /* failure */
Alexandre Julliard's avatar
Alexandre Julliard committed
1697
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1698

1699 1700 1701 1702
/***********************************************************************
 *		CalcChildScroll (USER32.@)
 */
void WINAPI CalcChildScroll( HWND hwnd, INT scroll )
Alexandre Julliard's avatar
Alexandre Julliard committed
1703
{
1704
    SCROLLINFO info;
1705
    RECT childRect, clientRect;
1706
    HWND *list;
Alexandre Julliard's avatar
Alexandre Julliard committed
1707

1708 1709
    GetClientRect( hwnd, &clientRect );
    SetRectEmpty( &childRect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1710

1711
    if ((list = WIN_ListChildren( hwnd )))
Alexandre Julliard's avatar
Alexandre Julliard committed
1712
    {
1713 1714 1715 1716 1717 1718
        int i;
        for (i = 0; list[i]; i++)
        {
            DWORD style = GetWindowLongW( list[i], GWL_STYLE );
            if (style & WS_MAXIMIZE)
            {
1719
                HeapFree( GetProcessHeap(), 0, list );
1720 1721 1722 1723 1724
                ShowScrollBar( hwnd, SB_BOTH, FALSE );
                return;
            }
            if (style & WS_VISIBLE)
            {
1725
                RECT rect;
1726
                WIN_GetRectangles( list[i], COORDS_PARENT, &rect, NULL );
1727
                UnionRect( &childRect, &rect, &childRect );
1728 1729
            }
        }
1730
        HeapFree( GetProcessHeap(), 0, list );
1731
    }
1732
    UnionRect( &childRect, &clientRect, &childRect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1733

1734 1735 1736
    /* set common info values */
    info.cbSize = sizeof(info);
    info.fMask = SIF_POS | SIF_RANGE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1737

1738
    /* set the specific */
Alexandre Julliard's avatar
Alexandre Julliard committed
1739
    switch( scroll )
Alexandre Julliard's avatar
Alexandre Julliard committed
1740
    {
1741
	case SB_BOTH:
Alexandre Julliard's avatar
Alexandre Julliard committed
1742
	case SB_HORZ:
1743 1744 1745
			info.nMin = childRect.left;
			info.nMax = childRect.right - clientRect.right;
			info.nPos = clientRect.left - childRect.left;
1746
			SetScrollInfo(hwnd, SB_HORZ, &info, TRUE);
1747 1748
			if (scroll == SB_HORZ) break;
			/* fall through */
Alexandre Julliard's avatar
Alexandre Julliard committed
1749
	case SB_VERT:
1750 1751 1752
			info.nMin = childRect.top;
			info.nMax = childRect.bottom - clientRect.bottom;
			info.nPos = clientRect.top - childRect.top;
1753
			SetScrollInfo(hwnd, SB_VERT, &info, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1754
			break;
1755
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1756
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1757

Alexandre Julliard's avatar
Alexandre Julliard committed
1758

Alexandre Julliard's avatar
Alexandre Julliard committed
1759
/***********************************************************************
1760
 *		ScrollChildren (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1761
 */
1762
void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam,
Alexandre Julliard's avatar
Alexandre Julliard committed
1763
                             LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1764
{
1765 1766
    INT newPos = -1;
    INT curPos, length, minPos, maxPos, shift;
1767
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1768

1769
    GetClientRect( hWnd, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1770

1771
    switch(uMsg)
Alexandre Julliard's avatar
Alexandre Julliard committed
1772
    {
1773
    case WM_HSCROLL:
1774 1775
	GetScrollRange(hWnd,SB_HORZ,&minPos,&maxPos);
	curPos = GetScrollPos(hWnd,SB_HORZ);
1776
	length = (rect.right - rect.left) / 2;
1777
	shift = GetSystemMetrics(SM_CYHSCROLL);
1778 1779
        break;
    case WM_VSCROLL:
1780 1781
	GetScrollRange(hWnd,SB_VERT,&minPos,&maxPos);
	curPos = GetScrollPos(hWnd,SB_VERT);
1782
	length = (rect.bottom - rect.top) / 2;
1783
	shift = GetSystemMetrics(SM_CXVSCROLL);
1784 1785
        break;
    default:
1786 1787
        return;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1788

Alexandre Julliard's avatar
Alexandre Julliard committed
1789 1790
    switch( wParam )
    {
1791
	case SB_LINEUP:
Alexandre Julliard's avatar
Alexandre Julliard committed
1792 1793
		        newPos = curPos - shift;
			break;
1794
	case SB_LINEDOWN:
Alexandre Julliard's avatar
Alexandre Julliard committed
1795 1796
			newPos = curPos + shift;
			break;
1797
	case SB_PAGEUP:
Alexandre Julliard's avatar
Alexandre Julliard committed
1798 1799
			newPos = curPos - length;
			break;
1800
	case SB_PAGEDOWN:
Alexandre Julliard's avatar
Alexandre Julliard committed
1801 1802 1803
			newPos = curPos + length;
			break;

1804
	case SB_THUMBPOSITION:
Alexandre Julliard's avatar
Alexandre Julliard committed
1805 1806 1807
			newPos = LOWORD(lParam);
			break;

1808
	case SB_THUMBTRACK:
Alexandre Julliard's avatar
Alexandre Julliard committed
1809 1810
			return;

1811
	case SB_TOP:
Alexandre Julliard's avatar
Alexandre Julliard committed
1812 1813
			newPos = minPos;
			break;
1814
	case SB_BOTTOM:
Alexandre Julliard's avatar
Alexandre Julliard committed
1815 1816 1817
			newPos = maxPos;
			break;
	case SB_ENDSCROLL:
1818
			CalcChildScroll(hWnd,(uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ);
Alexandre Julliard's avatar
Alexandre Julliard committed
1819
			return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1820
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1821

Alexandre Julliard's avatar
Alexandre Julliard committed
1822 1823
    if( newPos > maxPos )
	newPos = maxPos;
1824
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
1825 1826
	if( newPos < minPos )
	    newPos = minPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
1827

1828
    SetScrollPos(hWnd, (uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ , newPos, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1829

Alexandre Julliard's avatar
Alexandre Julliard committed
1830
    if( uMsg == WM_VSCROLL )
1831
	ScrollWindowEx(hWnd ,0 ,curPos - newPos, NULL, NULL, 0, NULL,
Alexandre Julliard's avatar
Alexandre Julliard committed
1832
			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
Alexandre Julliard's avatar
Alexandre Julliard committed
1833
    else
1834
	ScrollWindowEx(hWnd ,curPos - newPos, 0, NULL, NULL, 0, NULL,
Alexandre Julliard's avatar
Alexandre Julliard committed
1835
			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
Alexandre Julliard's avatar
Alexandre Julliard committed
1836 1837
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1838 1839

/******************************************************************************
1840
 *		CascadeWindows (USER32.@) Cascades MDI child windows
Alexandre Julliard's avatar
Alexandre Julliard committed
1841 1842 1843 1844 1845 1846
 *
 * RETURNS
 *    Success: Number of cascaded windows.
 *    Failure: 0
 */
WORD WINAPI
1847
CascadeWindows (HWND hwndParent, UINT wFlags, const RECT *lpRect,
1848
		UINT cKids, const HWND *lpKids)
Alexandre Julliard's avatar
Alexandre Julliard committed
1849
{
1850
    FIXME("(%p,0x%08x,...,%u,...): stub\n", hwndParent, wFlags, cKids);
Alexandre Julliard's avatar
Alexandre Julliard committed
1851 1852 1853 1854
    return 0;
}


1855 1856 1857 1858 1859 1860 1861 1862 1863
/***********************************************************************
 *		CascadeChildWindows (USER32.@)
 */
WORD WINAPI CascadeChildWindows( HWND parent, UINT flags )
{
    return CascadeWindows( parent, flags, NULL, 0, NULL );
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1864
/******************************************************************************
1865
 *		TileWindows (USER32.@) Tiles MDI child windows
Alexandre Julliard's avatar
Alexandre Julliard committed
1866 1867 1868 1869 1870 1871
 *
 * RETURNS
 *    Success: Number of tiled windows.
 *    Failure: 0
 */
WORD WINAPI
1872
TileWindows (HWND hwndParent, UINT wFlags, const RECT *lpRect,
1873
	     UINT cKids, const HWND *lpKids)
Alexandre Julliard's avatar
Alexandre Julliard committed
1874
{
1875
    FIXME("(%p,0x%08x,...,%u,...): stub\n", hwndParent, wFlags, cKids);
Alexandre Julliard's avatar
Alexandre Julliard committed
1876 1877 1878
    return 0;
}

1879 1880 1881 1882 1883 1884 1885 1886 1887 1888

/***********************************************************************
 *		TileChildWindows (USER32.@)
 */
WORD WINAPI TileChildWindows( HWND parent, UINT flags )
{
    return TileWindows( parent, flags, NULL, 0, NULL );
}


1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901
/************************************************************************
 *              "More Windows..." functionality
 */

/*              MDI_MoreWindowsDlgProc
 *
 *    This function will process the messages sent to the "More Windows..."
 *    dialog.
 *    Return values:  0    = cancel pressed
 *                    HWND = ok pressed or double-click in the list...
 *
 */

1902
static INT_PTR WINAPI MDI_MoreWindowsDlgProc (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
1903 1904 1905 1906 1907 1908 1909 1910
{
    switch (iMsg)
    {
       case WM_INITDIALOG:
       {
           UINT widest       = 0;
           UINT length;
           UINT i;
1911
           MDICLIENTINFO *ci = get_client_info( (HWND)lParam );
1912 1913
           HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX);

1914 1915
           for (i = 0; i < ci->nActiveChildren; i++)
           {
1916
               WCHAR buffer[MDI_MAXTITLELENGTH];
1917

1918
               if (!InternalGetWindowText( ci->child[i], buffer, sizeof(buffer)/sizeof(WCHAR) ))
1919 1920
                   continue;
               SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)buffer );
1921
               SendMessageW(hListBox, LB_SETITEMDATA, i, (LPARAM)ci->child[i] );
1922
               length = strlenW(buffer);  /* FIXME: should use GetTextExtentPoint */
1923 1924 1925 1926
               if (length > widest)
                   widest = length;
           }
           /* Make sure the horizontal scrollbar scrolls ok */
1927
           SendMessageW(hListBox, LB_SETHORIZONTALEXTENT, widest * 6, 0);
1928 1929

           /* Set the current selection */
1930
           SendMessageW(hListBox, LB_SETCURSEL, MDI_MOREWINDOWSLIMIT, 0);
1931 1932 1933 1934 1935 1936
           return TRUE;
       }

       case WM_COMMAND:
           switch (LOWORD(wParam))
           {
1937 1938 1939
                default:
                    if (HIWORD(wParam) != LBN_DBLCLK) break;
                    /* fall through */
1940 1941 1942 1943 1944 1945
                case IDOK:
                {
                    /*  windows are sorted by menu ID, so we must return the
                     *  window associated to the given id
                     */
                    HWND hListBox     = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
1946
                    UINT index        = SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
1947 1948
                    LRESULT res = SendMessageW(hListBox, LB_GETITEMDATA, index, 0);
                    EndDialog(hDlg, res);
1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970
                    return TRUE;
                }
                case IDCANCEL:
                    EndDialog(hDlg, 0);
                    return TRUE;
           }
           break;
    }
    return FALSE;
}

/*
 *
 *                      MDI_MoreWindowsDialog
 *
 *     Prompts the user with a listbox containing the opened
 *     documents. The user can then choose a windows and click
 *     on OK to set the current window to the one selected, or
 *     CANCEL to cancel. The function returns a handle to the
 *     selected window.
 */

1971
static HWND MDI_MoreWindowsDialog(HWND hwnd)
1972 1973 1974 1975 1976
{
    LPCVOID template;
    HRSRC hRes;
    HANDLE hDlgTmpl;

1977
    hRes = FindResourceA(user32_module, "MDI_MOREWINDOWS", (LPSTR)RT_DIALOG);
1978 1979 1980 1981

    if (hRes == 0)
        return 0;

1982
    hDlgTmpl = LoadResource(user32_module, hRes );
1983 1984 1985 1986 1987 1988 1989 1990 1991

    if (hDlgTmpl == 0)
        return 0;

    template = LockResource( hDlgTmpl );

    if (template == 0)
        return 0;

1992 1993
    return (HWND) DialogBoxIndirectParamA(user32_module, template, hwnd,
                                          MDI_MoreWindowsDlgProc, (LPARAM) hwnd);
1994
}