mdi.c 61.4 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 285
    {
        MDICLIENTINFO *ci = get_client_info(hwndClient);
        total = ci ? ci->nTotalCreated : 0;
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 848
    if( !menu ) return 0;

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);
Alexandre Julliard's avatar
Alexandre Julliard committed
853
	return 0;
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, 0, 0, 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 901
	DestroyMenu(hSysPopup);
	return 0;
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

Alexandre Julliard's avatar
Alexandre Julliard committed
912
    return 1;
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 928
    if( !menu ) return 0;

929 930 931 932 933 934
    /* 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) )
        return 0;

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

Alexandre Julliard's avatar
Alexandre Julliard committed
966
    return 1;
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
      {
        HWND next = MDI_GetWindow( ci, WIN_GetFullHandle( (HWND)wParam ), !lParam, 0 );
1160
        MDI_SwitchActiveChild( ci, next, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1161
	break;
1162
      }
1163

Alexandre Julliard's avatar
Alexandre Julliard committed
1164
      case WM_MDIRESTORE:
1165
        ShowWindow( (HWND)wParam, SW_SHOWNORMAL );
1166
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1167 1168

      case WM_MDISETMENU:
1169
          return MDISetMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1170

Alexandre Julliard's avatar
Alexandre Julliard committed
1171
      case WM_MDIREFRESHMENU:
1172
          return MDI_RefreshMenu( ci );
Alexandre Julliard's avatar
Alexandre Julliard committed
1173

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

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

      case WM_SETFOCUS:
1189 1190 1191 1192
          if (ci->hwndActiveChild && !IsIconic( ci->hwndActiveChild ))
              SetFocus( ci->hwndActiveChild );
          return 0;

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1198
      case WM_PARENTNOTIFY:
1199
        switch (LOWORD(wParam))
Alexandre Julliard's avatar
Alexandre Julliard committed
1200
        {
1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
        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);

1212 1213 1214
                TRACE("Adding MDI child %p, # of children %d\n",
                      (HWND)lParam, ci->nActiveChildren);

1215 1216 1217 1218 1219 1220
                ci->child[ci->nActiveChildren - 1] = (HWND)lParam;
            }
            break;

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

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

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

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

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

	    rect.left = 0;
	    rect.top = 0;
	    rect.right = LOWORD(lParam);
	    rect.bottom = HIWORD(lParam);
1248 1249 1250
	    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
1251 1252
			 rect.right - rect.left, rect.bottom - rect.top, 1);
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1253
	else
1254
            MDI_PostUpdate(hwnd, ci, SB_BOTH+1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1255

Alexandre Julliard's avatar
Alexandre Julliard committed
1256 1257
	break;

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1271
/***********************************************************************
1272
 *		DefFrameProcA (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1273
 */
1274 1275
LRESULT WINAPI DefFrameProcA( HWND hwnd, HWND hwndMDIClient,
                                UINT message, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1276 1277 1278 1279 1280
{
    if (hwndMDIClient)
    {
	switch (message)
	{
1281 1282
        case WM_SETTEXT:
            {
1283 1284 1285
                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 );
1286
                MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, text );
1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297
                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
1298
    }
1299
    return DefWindowProcA(hwnd, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1300 1301 1302 1303
}


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

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

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

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

1356
        case WM_NCACTIVATE:
1357
	    SendMessageW(hwndMDIClient, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1358 1359
	    break;

1360
        case WM_SETTEXT:
1361
            MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, (LPWSTR)lParam );
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375
	    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;

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

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

1397
    return DefWindowProcW( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1398 1399 1400
}

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

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

1411
    hwnd = WIN_GetFullHandle( hwnd );
1412
    if (!ci) return DefWindowProcA( hwnd, message, wParam, lParam );
1413

Alexandre Julliard's avatar
Alexandre Julliard committed
1414 1415
    switch (message)
    {
1416
    case WM_SETTEXT:
1417
	DefWindowProcA(hwnd, message, wParam, lParam);
1418 1419
	if( ci->hwndChildMaximized == hwnd )
	    MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL );
1420 1421 1422 1423 1424 1425 1426 1427
        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:
1428
    case WM_SHOWWINDOW:
1429 1430 1431 1432
    case WM_SETVISIBLE:
    case WM_SIZE:
    case WM_NEXTMENU:
    case WM_SYSCHAR:
1433
    case WM_DESTROY:
1434
        return DefMDIChildProcW( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1435
    }
1436
    return DefWindowProcA(hwnd, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1437 1438 1439 1440
}


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

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

1451
    hwnd = WIN_GetFullHandle( hwnd );
1452
    if (!ci) return DefWindowProcW( hwnd, message, wParam, lParam );
1453

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

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

    case WM_MENUCHAR:
        return 0x00010000; /* MDI children don't have menu bars */

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

1473 1474 1475 1476 1477
    case WM_SETFOCUS:
        if (ci->hwndActiveChild != hwnd)
            MDI_ChildActivate( client, hwnd );
        break;

1478 1479 1480 1481 1482
    case WM_CHILDACTIVATE:
        MDI_ChildActivate( client, hwnd );
        return 0;

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

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

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

1516
        if( ci->hwndChildMaximized == hwnd && wParam != SIZE_MAXIMIZED )
1517
        {
1518
            HWND frame;
1519

1520 1521
            ci->hwndChildMaximized = 0;

1522 1523
            frame = GetParent(client);
            MDI_RestoreFrameMenu( frame, hwnd );
1524
            MDI_UpdateFrameText( frame, client, TRUE, NULL );
1525 1526
        }

1527 1528 1529 1530 1531
        if( wParam == SIZE_MAXIMIZED )
        {
            HWND frame, hMaxChild = ci->hwndChildMaximized;

            if( hMaxChild == hwnd ) break;
1532

1533 1534 1535
            if( hMaxChild)
            {
                SendMessageW( hMaxChild, WM_SETREDRAW, FALSE, 0 );
1536

1537 1538
                MDI_RestoreFrameMenu( GetParent(client), hMaxChild );
                ShowWindow( hMaxChild, SW_SHOWNOACTIVATE );
1539

1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551
                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 );
        }
1552 1553 1554 1555 1556

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

1557 1558
            if (!switchTo) switchTo = hwnd;
            SendMessageW( switchTo, WM_CHILDACTIVATE, 0, 0 );
1559 1560 1561
	}

        MDI_PostUpdate(client, ci, SB_BOTH+1);
1562 1563 1564 1565 1566 1567 1568 1569 1570
        break;

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

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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1599
/**********************************************************************
1600
 *		CreateMDIWindowA (USER32.@) Creates a MDI child
1601 1602 1603 1604
 *
 * RETURNS
 *    Success: Handle to created window
 *    Failure: NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
1605
 */
1606
HWND WINAPI CreateMDIWindowA(
1607 1608 1609
    LPCSTR lpClassName,    /* [in] Pointer to registered child class name */
    LPCSTR lpWindowName,   /* [in] Pointer to window name */
    DWORD dwStyle,         /* [in] Window style */
1610 1611 1612 1613 1614 1615
    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 */
1616
    LPARAM lParam)         /* [in] Application-defined value */
Alexandre Julliard's avatar
Alexandre Julliard committed
1617
{
1618
    TRACE("(%s,%s,%08x,%d,%d,%d,%d,%p,%p,%08lx)\n",
1619 1620
          debugstr_a(lpClassName),debugstr_a(lpWindowName),dwStyle,X,Y,
          nWidth,nHeight,hWndParent,hInstance,lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1621

1622 1623 1624
    return CreateWindowExA(WS_EX_MDICHILD, lpClassName, lpWindowName,
                           dwStyle, X, Y, nWidth, nHeight, hWndParent,
                           0, hInstance, (LPVOID)lParam);
1625 1626
}

1627
/***********************************************************************
1628
 *		CreateMDIWindowW (USER32.@) Creates a MDI child
Alexandre Julliard's avatar
Alexandre Julliard committed
1629 1630 1631 1632
 *
 * RETURNS
 *    Success: Handle to created window
 *    Failure: NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
1633
 */
1634
HWND WINAPI CreateMDIWindowW(
1635 1636 1637
    LPCWSTR lpClassName,    /* [in] Pointer to registered child class name */
    LPCWSTR lpWindowName,   /* [in] Pointer to window name */
    DWORD dwStyle,         /* [in] Window style */
1638 1639 1640 1641 1642 1643
    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 */
1644 1645
    LPARAM lParam)         /* [in] Application-defined value */
{
1646
    TRACE("(%s,%s,%08x,%d,%d,%d,%d,%p,%p,%08lx)\n",
1647 1648
          debugstr_w(lpClassName), debugstr_w(lpWindowName), dwStyle, X, Y,
          nWidth, nHeight, hWndParent, hInstance, lParam);
1649

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

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

1665
        if (!ci || !IsWindowEnabled(ci->hwndActiveChild)) return 0;
1666

1667
        /* translate if the Ctrl key is down and Alt not. */
1668

1669 1670 1671 1672 1673 1674 1675 1676 1677 1678
        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:
1679 1680 1681 1682 1683 1684
                if (is_close_enabled(ci->hwndActiveChild, 0))
                {
                    wParam = SC_CLOSE;
                    break;
                }
                /* fall through */
1685 1686 1687
            default:
                return 0;
            }
1688
            TRACE("wParam = %04lx\n", wParam);
1689
            SendMessageW(ci->hwndActiveChild, WM_SYSCOMMAND, wParam, msg->wParam);
1690 1691
            return 1;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1692 1693
    }
    return 0; /* failure */
Alexandre Julliard's avatar
Alexandre Julliard committed
1694
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1695

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

1705 1706
    GetClientRect( hwnd, &clientRect );
    SetRectEmpty( &childRect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1707

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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1756

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

1767
    GetClientRect( hWnd, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1768

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

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

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

1806
	case SB_THUMBTRACK:
Alexandre Julliard's avatar
Alexandre Julliard committed
1807 1808
			return;

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

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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1836 1837

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


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


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

1877 1878 1879 1880 1881 1882 1883 1884 1885 1886

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


1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899
/************************************************************************
 *              "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...
 *
 */

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

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

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

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

       case WM_COMMAND:
           switch (LOWORD(wParam))
           {
1935 1936 1937
                default:
                    if (HIWORD(wParam) != LBN_DBLCLK) break;
                    /* fall through */
1938 1939 1940 1941 1942 1943
                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);
1944
                    UINT index        = SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
1945 1946
                    LRESULT res = SendMessageW(hListBox, LB_GETITEMDATA, index, 0);
                    EndDialog(hDlg, res);
1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968
                    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.
 */

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

1975
    hRes = FindResourceA(user32_module, "MDI_MOREWINDOWS", (LPSTR)RT_DIALOG);
1976 1977 1978 1979

    if (hRes == 0)
        return 0;

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

    if (hDlgTmpl == 0)
        return 0;

    template = LockResource( hDlgTmpl );

    if (template == 0)
        return 0;

1990 1991
    return (HWND) DialogBoxIndirectParamA(user32_module, template, hwnd,
                                          MDI_MoreWindowsDlgProc, (LPARAM) hwnd);
1992
}