mdi.c 62.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/winuser16.h"
97
#include "wine/unicode.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
98
#include "win.h"
99
#include "controls.h"
100
#include "user_private.h"
101
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
102

103
WINE_DEFAULT_DEBUG_CHANNEL(mdi);
104

105 106 107 108 109
#define MDI_MAXTITLELENGTH      0xa1

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

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

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

117 118
typedef struct
{
119 120 121
    /* 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.
122 123 124 125 126 127 128 129
     * 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.
130
     */
131
    UINT      nActiveChildren;
132
    HWND      hwndChildMaximized;
133
    HWND      hwndActiveChild;
134 135
    HWND      *child; /* array of tracked children */
    HMENU     hFrameMenu;
136 137 138 139 140 141 142 143
    HMENU     hWindowMenu;
    UINT      idFirstChild;
    LPWSTR    frameTitle;
    UINT      nTotalCreated;
    UINT      mdiFlags;
    UINT      sbRecalc;   /* SB_xxx flags for scrollbar fixup */
} MDICLIENTINFO;

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

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

153
static HWND MDI_MoreWindowsDialog(HWND);
154
static LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
155 156
static LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );

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

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

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

183 184 185 186

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


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

220 221 222 223 224 225 226 227 228 229 230 231 232 233
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
234 235 236
/**********************************************************************
 * 			MDI_GetWindow
 *
237
 * returns "activatable" child different from the current or zero
Alexandre Julliard's avatar
Alexandre Julliard committed
238
 */
239
static HWND MDI_GetWindow(MDICLIENTINFO *clientInfo, HWND hWnd, BOOL bNext,
Alexandre Julliard's avatar
Alexandre Julliard committed
240
                            DWORD dwStyleMask )
Alexandre Julliard's avatar
Alexandre Julliard committed
241
{
242 243 244 245
    int i;
    HWND *list;
    HWND last = 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
246
    dwStyleMask |= WS_DISABLED | WS_VISIBLE;
Alexandre Julliard's avatar
Alexandre Julliard committed
247
    if( !hWnd ) hWnd = clientInfo->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
248

249
    if (!(list = WIN_ListChildren( GetParent(hWnd) ))) return 0;
250 251 252 253
    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
254

255
    for ( ; list[i]; i++)
Alexandre Julliard's avatar
Alexandre Julliard committed
256
    {
257 258 259 260 261 262 263 264 265 266 267 268
        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
269
    }
270
 found:
271
    HeapFree( GetProcessHeap(), 0, list );
272
    return last;
Alexandre Julliard's avatar
Alexandre Julliard committed
273 274
}

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

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

    GetClientRect( hwndClient, &rect );
295
    if( rect.bottom - rect.top - delta >= spacing )
Alexandre Julliard's avatar
Alexandre Julliard committed
296 297 298 299 300
	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);
301
    lpPos[0].x = lpPos[0].y = spacing * (total%(nstagger+1));
Alexandre Julliard's avatar
Alexandre Julliard committed
302
}
Alexandre Julliard's avatar
Alexandre Julliard committed
303

Alexandre Julliard's avatar
Alexandre Julliard committed
304
/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
305
 *            MDISetMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
306
 */
307 308
static LRESULT MDISetMenu( HWND hwnd, HMENU hmenuFrame,
                           HMENU hmenuWindow)
Alexandre Julliard's avatar
Alexandre Julliard committed
309
{
Alexandre Julliard's avatar
Alexandre Julliard committed
310
    MDICLIENTINFO *ci;
311
    HWND hwndFrame = GetParent(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
312

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

315 316 317 318 319
    if (hmenuFrame && !IsMenu(hmenuFrame))
    {
	WARN("hmenuFrame is not a menu handle\n");
	return 0L;
    }
320

321 322 323 324 325
    if (hmenuWindow && !IsMenu(hmenuWindow))
    {
	WARN("hmenuWindow is not a menu handle\n");
	return 0L;
    }
326 327

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

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

331 332 333
    if (hmenuFrame)
    {
        if (hmenuFrame == ci->hFrameMenu) return (LRESULT)hmenuFrame;
334

335 336
        if (ci->hwndChildMaximized)
            MDI_RestoreFrameMenu( hwndFrame, ci->hwndChildMaximized );
337
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
338

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

348 349 350
            /* Remove all items from old Window menu */
            ci->nActiveChildren = 0;
            MDI_RefreshMenu(ci);
351

352
            ci->hWindowMenu = hmenuWindow;
Alexandre Julliard's avatar
Alexandre Julliard committed
353

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

362
    if (hmenuFrame)
Alexandre Julliard's avatar
Alexandre Julliard committed
363
    {
364
        SetMenu(hwndFrame, hmenuFrame);
365
        if( hmenuFrame != ci->hFrameMenu )
366
        {
367 368 369
            HMENU oldFrameMenu = ci->hFrameMenu;

            ci->hFrameMenu = hmenuFrame;
370
            if (ci->hwndChildMaximized)
371
                MDI_AugmentFrameMenu( hwndFrame, ci->hwndChildMaximized );
372

373
            return (LRESULT)oldFrameMenu;
374
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
375
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
376 377
    else
    {
378 379 380 381 382 383
        /* 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.
         */
384 385
        if( ci->hwndChildMaximized )
            MDI_AugmentFrameMenu( hwndFrame, ci->hwndChildMaximized );
Alexandre Julliard's avatar
Alexandre Julliard committed
386
    }
387

Alexandre Julliard's avatar
Alexandre Julliard committed
388 389
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
390

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

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

401 402
    if (!ci->hWindowMenu)
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
403

404 405 406 407 408 409
    if (!IsMenu(ci->hWindowMenu))
    {
        WARN("Window menu handle %p is no more valid\n", ci->hWindowMenu);
        return 0;
    }

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

        memset(&mii, 0, sizeof(mii));
        mii.cbSize = sizeof(mii);
        mii.fMask  = MIIM_TYPE;
422 423 424 425 426 427 428 429 430
        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))
431 432 433 434 435 436 437 438 439 440
                {
                    if (mii.wID == ci->idFirstChild)
                    {
                        TRACE("removing %u items including separator\n", count - i);
                        while (RemoveMenu(ci->hWindowMenu, i, MF_BYPOSITION))
                            /* nothing */;

                        break;
                    }
                }
441 442
            }
        }
443 444
    }

445 446
    visible = 0;
    for (i = 0; i < ci->nActiveChildren; i++)
Alexandre Julliard's avatar
Alexandre Julliard committed
447
    {
448
        if (GetWindowLongW(ci->child[i], GWL_STYLE) & WS_VISIBLE)
449
        {
450 451 452 453 454 455 456 457 458 459
            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)
460 461
                /* Visio expects that separator has id 0 */
                AppendMenuW(ci->hWindowMenu, MF_SEPARATOR, 0, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
462

463
            visible++;
Alexandre Julliard's avatar
Alexandre Julliard committed
464

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

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

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

481
    return (LRESULT)ci->hFrameMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
482 483
}

484 485 486

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

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

497 498 499
    GetClientRect( client, &rect );
    AdjustWindowRectEx( &rect, GetWindowLongW( hwnd, GWL_STYLE ),
                        0, GetWindowLongW( hwnd, GWL_EXSTYLE ));
Alexandre Julliard's avatar
Alexandre Julliard committed
500

Alexandre Julliard's avatar
Alexandre Julliard committed
501 502
    lpMinMax->ptMaxSize.x = rect.right -= rect.left;
    lpMinMax->ptMaxSize.y = rect.bottom -= rect.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
503

Alexandre Julliard's avatar
Alexandre Julliard committed
504
    lpMinMax->ptMaxPosition.x = rect.left;
505
    lpMinMax->ptMaxPosition.y = rect.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
506

507
    TRACE("max rect (%d,%d - %d, %d)\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
508
                        rect.left,rect.top,rect.right,rect.bottom);
Alexandre Julliard's avatar
Alexandre Julliard committed
509 510
}

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

521
    hwndPrev = ci->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
522

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

525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
    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 */
542
        SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | (activate ? 0 : SWP_NOACTIVATE) );
543
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
544
}
Alexandre Julliard's avatar
Alexandre Julliard committed
545

546

Alexandre Julliard's avatar
Alexandre Julliard committed
547 548 549
/**********************************************************************
 *                                      MDIDestroyChild
 */
550
static LRESULT MDIDestroyChild( HWND client, MDICLIENTINFO *ci,
551
                                HWND child, BOOL flagDestroy )
Alexandre Julliard's avatar
Alexandre Julliard committed
552
{
553 554
    UINT i;

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

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

578 579 580 581 582 583 584 585 586 587
    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;
588 589 590

            ci->nActiveChildren--;
            break;
591 592
        }
    }
593

594 595
    if (flagDestroy)
    {
596
        SendMessageW(client, WM_MDIREFRESHMENU, 0, 0);
597 598
        MDI_PostUpdate(GetParent(child), ci, SB_BOTH+1);
        DestroyWindow(child);
Alexandre Julliard's avatar
Alexandre Julliard committed
599
    }
600

601
    TRACE("child destroyed - %p\n", child);
Alexandre Julliard's avatar
Alexandre Julliard committed
602
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
603 604
}

Alexandre Julliard's avatar
Alexandre Julliard committed
605

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

618 619
    clientInfo = get_client_info( client );

620 621
    if (clientInfo->hwndActiveChild == child) return 0;

622
    TRACE("%p\n", child);
623

624 625
    frame = GetParent(client);
    isActiveFrameWnd = (GetActiveWindow() == frame);
626
    prevActiveWnd = clientInfo->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
627 628

    /* deactivate prev. active child */
629
    if(prevActiveWnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
630
    {
631 632
        SendMessageW( prevActiveWnd, WM_NCACTIVATE, FALSE, 0L );
        SendMessageW( prevActiveWnd, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child);
Alexandre Julliard's avatar
Alexandre Julliard committed
633
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
634

635
    MDI_SwitchActiveChild( clientInfo, child, FALSE );
636 637
    clientInfo->hwndActiveChild = child;

638 639
    MDI_RefreshMenu(clientInfo);

Alexandre Julliard's avatar
Alexandre Julliard committed
640
    if( isActiveFrameWnd )
641
    {
642
        SendMessageW( child, WM_NCACTIVATE, TRUE, 0L);
643 644 645 646 647 648 649
        /* 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 );
650
    }
651 652

    SendMessageW( child, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child );
653
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
654
}
Alexandre Julliard's avatar
Alexandre Julliard committed
655

Alexandre Julliard's avatar
Alexandre Julliard committed
656 657 658 659 660
/* -------------------- MDI client window functions ------------------- */

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

669
 hobjSrc = SelectObject(hDCSrc, hbClose);
670
 hbCopy = CreateCompatibleBitmap(hDCSrc,GetSystemMetrics(SM_CXSIZE),GetSystemMetrics(SM_CYSIZE));
671
 hobjDest = SelectObject(hDCDest, hbCopy);
Alexandre Julliard's avatar
Alexandre Julliard committed
672

673 674
 BitBlt(hDCDest, 0, 0, GetSystemMetrics(SM_CXSIZE), GetSystemMetrics(SM_CYSIZE),
          hDCSrc, GetSystemMetrics(SM_CXSIZE), 0, SRCCOPY);
675

676 677 678
 SelectObject(hDCSrc, hobjSrc);
 DeleteObject(hbClose);
 DeleteDC(hDCSrc);
Alexandre Julliard's avatar
Alexandre Julliard committed
679

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

682 683
 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
684

685 686 687
 SelectObject(hDCDest, hobjSrc );
 SelectObject(hDCDest, hobjDest);
 DeleteDC(hDCDest);
Alexandre Julliard's avatar
Alexandre Julliard committed
688 689 690 691

 return hbCopy;
}

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

701 702
    if (ci->hwndChildMaximized)
        SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndChildMaximized, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
703

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

706
    if (!(win_array = WIN_ListChildren( client ))) return 0;
707 708 709

    /* remove all the windows we don't want */
    for (i = total = 0; win_array[i]; i++)
Alexandre Julliard's avatar
Alexandre Julliard committed
710
    {
711 712 713 714 715 716 717 718
        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
719
    }
720
    win_array[total] = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
721

722 723 724 725 726 727 728 729 730
    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--)
        {
731 732
            LONG style;
            LONG posOptions = SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER;
733 734

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

739
            if (!(style & WS_SIZEBOX)) posOptions |= SWP_NOSIZE;
740
            SetWindowPos( win_array[i], 0, pos[0].x, pos[0].y, pos[1].x, pos[1].y,
741
                           posOptions);
742 743
        }
    }
744
    HeapFree( GetProcessHeap(), 0, win_array );
745 746

    if (has_icons) ArrangeIconicWindows( client );
Alexandre Julliard's avatar
Alexandre Julliard committed
747 748 749
    return 0;
}

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

759 760
    if (ci->hwndChildMaximized)
        SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndChildMaximized, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
761

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

764
    if (!(win_array = WIN_ListChildren( client ))) return;
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779

    /* 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
780

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

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

790 791 792
        GetClientRect(client,&rect);
        rows    = (int) sqrt((double)total);
        columns = total / rows;
Alexandre Julliard's avatar
Alexandre Julliard committed
793

794 795 796 797 798 799
        if( wParam & MDITILE_HORIZONTAL )  /* version >= 3.1 */
        {
            i = rows;
            rows = columns;  /* exchange r and c */
            columns = i;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
800

801 802 803 804 805
        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
806

807 808 809 810 811 812 813 814 815 816 817 818 819 820
        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++)
            {
821 822 823 824 825
                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);
826 827 828 829 830
                y += ysize;
                pWnd++;
            }
            x += xsize;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
831
    }
832
    HeapFree( GetProcessHeap(), 0, win_array );
833
    if (has_icons) ArrangeIconicWindows( client );
Alexandre Julliard's avatar
Alexandre Julliard committed
834
}
Alexandre Julliard's avatar
Alexandre Julliard committed
835

Alexandre Julliard's avatar
Alexandre Julliard committed
836
/* ----------------------- Frame window ---------------------------- */
Alexandre Julliard's avatar
Alexandre Julliard committed
837

Alexandre Julliard's avatar
Alexandre Julliard committed
838

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

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

851 852
    if( !menu ) return 0;

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

860
    AppendMenuW(menu, MF_HELP | MF_BITMAP,
861 862
                SC_CLOSE, is_close_enabled(hChild, hSysPopup) ?
                (LPCWSTR)HBMMENU_MBAR_CLOSE : (LPCWSTR)HBMMENU_MBAR_CLOSE_D );
863 864 865
    AppendMenuW(menu, MF_HELP | MF_BITMAP,
                SC_RESTORE, (LPCWSTR)HBMMENU_MBAR_RESTORE );
    AppendMenuW(menu, MF_HELP | MF_BITMAP,
866
                SC_MINIMIZE, (LPCWSTR)HBMMENU_MBAR_MINIMIZE ) ;
867

868
    /* The system menu is replaced by the child icon */
869
    hIcon = (HICON)SendMessageW(hChild, WM_GETICON, ICON_SMALL, 0);
870
    if (!hIcon)
871
        hIcon = (HICON)SendMessageW(hChild, WM_GETICON, ICON_BIG, 0);
872
    if (!hIcon)
873
        hIcon = LoadImageW(0, MAKEINTRESOURCEW(IDI_WINLOGO), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899
    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;
      }
    }

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

908 909 910
    EnableMenuItem(hSysPopup, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
    EnableMenuItem(hSysPopup, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
    EnableMenuItem(hSysPopup, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
911
    SetMenuDefaultItem(hSysPopup, SC_CLOSE, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
912

Alexandre Julliard's avatar
Alexandre Julliard committed
913
    /* redraw menu */
914
    DrawMenuBar(frame);
Alexandre Julliard's avatar
Alexandre Julliard committed
915

Alexandre Julliard's avatar
Alexandre Julliard committed
916
    return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
917 918 919
}

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

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

931 932
    if( !menu ) return 0;

933 934 935 936 937 938
    /* 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;

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

947
    GetMenuItemInfoW(menu,
948
		     0,
949 950 951
		     TRUE,
		     &menuInfo);

952
    RemoveMenu(menu,0,MF_BYPOSITION);
953

954 955
    if ( (menuInfo.fType & MFT_BITMAP)           &&
	 (LOWORD(menuInfo.dwTypeData)!=0)        &&
956
	 (LOWORD(menuInfo.dwTypeData)!=HBITMAP_16(hBmpClose)) )
957
    {
958
        DeleteObject(HBITMAP_32(LOWORD(menuInfo.dwTypeData)));
959 960
    }

961
    /* close */
962
    DeleteMenu(menu, SC_CLOSE, MF_BYCOMMAND);
963
    /* restore */
964
    DeleteMenu(menu, SC_RESTORE, MF_BYCOMMAND);
965
    /* minimize */
966
    DeleteMenu(menu, SC_MINIMIZE, MF_BYCOMMAND);
967

968
    DrawMenuBar(frame);
Alexandre Julliard's avatar
Alexandre Julliard committed
969

Alexandre Julliard's avatar
Alexandre Julliard committed
970
    return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
971 972
}

973

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

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

988
    if (!ci) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
989

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

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

    if (ci->frameTitle)
    {
1006
	if (ci->hwndChildMaximized)
Alexandre Julliard's avatar
Alexandre Julliard committed
1007 1008 1009
	{
	    /* combine frame title and child title if possible */

1010 1011
	    static const WCHAR lpBracket[]  = {' ','-',' ','[',0};
	    static const WCHAR lpBracket2[]  = {']',0};
1012
	    int	i_frame_text_length = strlenW(ci->frameTitle);
Alexandre Julliard's avatar
Alexandre Julliard committed
1013

1014
	    lstrcpynW( lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH);
Alexandre Julliard's avatar
Alexandre Julliard committed
1015 1016 1017

	    if( i_frame_text_length + 6 < MDI_MAXTITLELENGTH )
            {
1018
		strcatW( lpBuffer, lpBracket );
1019
                if (GetWindowTextW( ci->hwndActiveChild, lpBuffer + i_frame_text_length + 4,
1020 1021 1022 1023 1024
                                    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
1025 1026 1027
	}
	else
	{
1028
            lstrcpynW(lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH+1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1029 1030 1031 1032 1033
	}
    }
    else
	lpBuffer[0] = '\0';

1034
    DefWindowProcW( frame, WM_SETTEXT, 0, (LPARAM)lpBuffer );
1035 1036 1037 1038

    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
1039
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1040 1041


Alexandre Julliard's avatar
Alexandre Julliard committed
1042
/* ----------------------------- Interface ---------------------------- */
Alexandre Julliard's avatar
Alexandre Julliard committed
1043 1044


Alexandre Julliard's avatar
Alexandre Julliard committed
1045
/**********************************************************************
1046
 *		MDIClientWndProc_common
Alexandre Julliard's avatar
Alexandre Julliard committed
1047
 */
1048 1049
static LRESULT MDIClientWndProc_common( HWND hwnd, UINT message,
                                        WPARAM wParam, LPARAM lParam, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
1050
{
1051
    MDICLIENTINFO *ci;
1052

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

1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
    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 );
    }
1066

Alexandre Julliard's avatar
Alexandre Julliard committed
1067 1068 1069
    switch (message)
    {
      case WM_CREATE:
1070 1071 1072 1073
      {
          /* Since we are using only cs->lpCreateParams, we can safely
           * cast to LPCREATESTRUCTA here */
          LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
1074
          WND *wndPtr = WIN_GetPtr( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
1075 1076 1077 1078

	/* Translation layer doesn't know what's in the cs->lpCreateParams
	 * so we have to keep track of what environment we're in. */

1079
	if( wndPtr->flags & WIN_ISWIN32 )
Alexandre Julliard's avatar
Alexandre Julliard committed
1080
	{
1081
	    LPCLIENTCREATESTRUCT ccs = cs->lpCreateParams;
Alexandre Julliard's avatar
Alexandre Julliard committed
1082 1083 1084
	    ci->hWindowMenu	= ccs->hWindowMenu;
	    ci->idFirstChild	= ccs->idFirstChild;
	}
1085
        else
Alexandre Julliard's avatar
Alexandre Julliard committed
1086
	{
1087
	    LPCLIENTCREATESTRUCT16 ccs = MapSL(PtrToUlong(cs->lpCreateParams));
1088
	    ci->hWindowMenu	= HMENU_32(ccs->hWindowMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
1089 1090
	    ci->idFirstChild	= ccs->idFirstChild;
	}
1091
        WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
1092

1093
        ci->hwndChildMaximized  = 0;
1094
        ci->child = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1095
	ci->nActiveChildren	= 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1096
	ci->nTotalCreated	= 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1097
	ci->frameTitle		= NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1098
	ci->mdiFlags		= 0;
1099
        ci->hFrameMenu = GetMenu(cs->hwndParent);
Alexandre Julliard's avatar
Alexandre Julliard committed
1100

1101
	if (!hBmpClose) hBmpClose = CreateMDIMenuBitmap();
Alexandre Julliard's avatar
Alexandre Julliard committed
1102

1103
        TRACE("Client created: hwnd %p, Window menu %p, idFirst = %04x\n",
1104
              hwnd, ci->hWindowMenu, ci->idFirstChild );
1105 1106
        return 0;
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1107

Alexandre Julliard's avatar
Alexandre Julliard committed
1108
      case WM_DESTROY:
1109
      {
1110 1111
          if( ci->hwndChildMaximized )
              MDI_RestoreFrameMenu(GetParent(hwnd), ci->hwndChildMaximized);
1112 1113 1114 1115

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

1116 1117
          HeapFree( GetProcessHeap(), 0, ci->child );
          HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1118

1119 1120
          return 0;
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1121 1122

      case WM_MDIACTIVATE:
1123
      {
1124 1125
        if( ci->hwndActiveChild != (HWND)wParam )
	    SetWindowPos((HWND)wParam, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE);
1126
        return 0;
1127
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1128 1129

      case WM_MDICASCADE:
1130
        return MDICascade(hwnd, ci);
Alexandre Julliard's avatar
Alexandre Julliard committed
1131 1132

      case WM_MDICREATE:
1133
        if (lParam)
1134
        {
1135 1136
            HWND child;

1137 1138 1139
            if (unicode)
            {
                MDICREATESTRUCTW *csW = (MDICREATESTRUCTW *)lParam;
1140
                child = CreateWindowExW(WS_EX_MDICHILD, csW->szClass,
1141 1142 1143 1144 1145 1146 1147 1148
                                            csW->szTitle, csW->style,
                                            csW->x, csW->y, csW->cx, csW->cy,
                                            hwnd, 0, csW->hOwner,
                                            (LPVOID)csW->lParam);
            }
            else
            {
                MDICREATESTRUCTA *csA = (MDICREATESTRUCTA *)lParam;
1149
                child = CreateWindowExA(WS_EX_MDICHILD, csA->szClass,
1150 1151 1152 1153 1154
                                            csA->szTitle, csA->style,
                                            csA->x, csA->y, csA->cx, csA->cy,
                                            hwnd, 0, csA->hOwner,
                                            (LPVOID)csA->lParam);
            }
1155
            return (LRESULT)child;
1156
        }
1157
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1158 1159

      case WM_MDIDESTROY:
1160
          return MDIDestroyChild( hwnd, ci, WIN_GetFullHandle( (HWND)wParam ), TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1161 1162

      case WM_MDIGETACTIVE:
1163
          if (lParam) *(BOOL *)lParam = IsZoomed(ci->hwndActiveChild);
1164
          return (LRESULT)ci->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
1165 1166

      case WM_MDIICONARRANGE:
Alexandre Julliard's avatar
Alexandre Julliard committed
1167
	ci->mdiFlags |= MDIF_NEEDUPDATE;
1168
        ArrangeIconicWindows( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
1169
	ci->sbRecalc = SB_BOTH+1;
1170 1171 1172
        SendMessageW( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 );
        return 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
1173
      case WM_MDIMAXIMIZE:
1174
	ShowWindow( (HWND)wParam, SW_MAXIMIZE );
1175
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1176

Alexandre Julliard's avatar
Alexandre Julliard committed
1177
      case WM_MDINEXT: /* lParam != 0 means previous window */
1178 1179
      {
        HWND next = MDI_GetWindow( ci, WIN_GetFullHandle( (HWND)wParam ), !lParam, 0 );
1180
        MDI_SwitchActiveChild( ci, next, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1181
	break;
1182
      }
1183

Alexandre Julliard's avatar
Alexandre Julliard committed
1184
      case WM_MDIRESTORE:
1185
        ShowWindow( (HWND)wParam, SW_SHOWNORMAL );
1186
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1187 1188

      case WM_MDISETMENU:
1189
          return MDISetMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1190

Alexandre Julliard's avatar
Alexandre Julliard committed
1191
      case WM_MDIREFRESHMENU:
1192
          return MDI_RefreshMenu( ci );
Alexandre Julliard's avatar
Alexandre Julliard committed
1193

Alexandre Julliard's avatar
Alexandre Julliard committed
1194
      case WM_MDITILE:
Alexandre Julliard's avatar
Alexandre Julliard committed
1195
	ci->mdiFlags |= MDIF_NEEDUPDATE;
1196 1197
        ShowScrollBar( hwnd, SB_BOTH, FALSE );
        MDITile( hwnd, ci, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1198
        ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1199
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1200 1201 1202

      case WM_VSCROLL:
      case WM_HSCROLL:
Alexandre Julliard's avatar
Alexandre Julliard committed
1203
	ci->mdiFlags |= MDIF_NEEDUPDATE;
1204
        ScrollChildren( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1205
	ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1206
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1207 1208

      case WM_SETFOCUS:
1209 1210 1211 1212
          if (ci->hwndActiveChild && !IsIconic( ci->hwndActiveChild ))
              SetFocus( ci->hwndActiveChild );
          return 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
1213
      case WM_NCACTIVATE:
Alexandre Julliard's avatar
Alexandre Julliard committed
1214
        if( ci->hwndActiveChild )
1215
            SendMessageW(ci->hwndActiveChild, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1216
	break;
1217

Alexandre Julliard's avatar
Alexandre Julliard committed
1218
      case WM_PARENTNOTIFY:
1219
        switch (LOWORD(wParam))
Alexandre Julliard's avatar
Alexandre Julliard committed
1220
        {
1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231
        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);

1232 1233 1234
                TRACE("Adding MDI child %p, # of children %d\n",
                      (HWND)lParam, ci->nActiveChildren);

1235 1236 1237 1238 1239 1240
                ci->child[ci->nActiveChildren - 1] = (HWND)lParam;
            }
            break;

        case WM_LBUTTONDOWN:
            {
1241 1242
            HWND child;
            POINT pt;
1243 1244
            pt.x = (short)LOWORD(lParam);
            pt.y = (short)HIWORD(lParam);
1245
            child = ChildWindowFromPoint(hwnd, pt);
Alexandre Julliard's avatar
Alexandre Julliard committed
1246

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

1249
            if( child && child != hwnd && child != ci->hwndActiveChild )
1250
                SetWindowPos(child, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE );
1251 1252
            break;
            }
1253 1254 1255

        case WM_DESTROY:
            return MDIDestroyChild( hwnd, ci, WIN_GetFullHandle( (HWND)lParam ), FALSE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1256
        }
1257
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1258 1259

      case WM_SIZE:
1260
        if( ci->hwndActiveChild && IsZoomed(ci->hwndActiveChild) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1261
	{
1262 1263 1264 1265 1266 1267
	    RECT	rect;

	    rect.left = 0;
	    rect.top = 0;
	    rect.right = LOWORD(lParam);
	    rect.bottom = HIWORD(lParam);
1268 1269 1270
	    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
1271 1272
			 rect.right - rect.left, rect.bottom - rect.top, 1);
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1273
	else
1274
            MDI_PostUpdate(hwnd, ci, SB_BOTH+1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1275

Alexandre Julliard's avatar
Alexandre Julliard committed
1276 1277
	break;

Alexandre Julliard's avatar
Alexandre Julliard committed
1278
      case WM_MDICALCCHILDSCROLL:
Alexandre Julliard's avatar
Alexandre Julliard committed
1279 1280
	if( (ci->mdiFlags & MDIF_NEEDUPDATE) && ci->sbRecalc )
	{
1281
            CalcChildScroll(hwnd, ci->sbRecalc-1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1282 1283 1284
	    ci->sbRecalc = 0;
	    ci->mdiFlags &= ~MDIF_NEEDUPDATE;
	}
1285
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1286
    }
1287 1288
    return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
                     DefWindowProcA( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1289 1290
}

1291 1292 1293 1294 1295
/***********************************************************************
 *		MDIClientWndProcA
 */
static LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
1296 1297
    if (!IsWindow(hwnd)) return 0;
    return MDIClientWndProc_common( hwnd, message, wParam, lParam, FALSE );
1298 1299 1300 1301 1302 1303 1304
}

/***********************************************************************
 *		MDIClientWndProcW
 */
static LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
1305 1306
    if (!IsWindow(hwnd)) return 0;
    return MDIClientWndProc_common( hwnd, message, wParam, lParam, TRUE );
1307
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1308 1309

/***********************************************************************
1310
 *		DefFrameProcA (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1311
 */
1312 1313
LRESULT WINAPI DefFrameProcA( HWND hwnd, HWND hwndMDIClient,
                                UINT message, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1314 1315 1316 1317 1318
{
    if (hwndMDIClient)
    {
	switch (message)
	{
1319 1320
        case WM_SETTEXT:
            {
1321 1322 1323
                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 );
1324
                MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, text );
1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335
                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
1336
    }
1337
    return DefWindowProcA(hwnd, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1338 1339 1340 1341
}


/***********************************************************************
1342
 *		DefFrameProcW (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1343
 */
1344 1345
LRESULT WINAPI DefFrameProcW( HWND hwnd, HWND hwndMDIClient,
                                UINT message, WPARAM wParam, LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1346
{
1347 1348
    MDICLIENTINFO *ci = get_client_info( hwndMDIClient );

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

1351
    if (ci)
Alexandre Julliard's avatar
Alexandre Julliard committed
1352 1353 1354
    {
	switch (message)
	{
1355 1356 1357 1358 1359 1360 1361
        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;
1362
                    if( !ci->hwndChildMaximized ) break;
1363 1364
                    switch( id )
                    {
1365 1366
                    case SC_CLOSE:
                        if (!is_close_enabled(ci->hwndActiveChild, 0)) break;
1367 1368 1369 1370 1371 1372 1373
                    case SC_SIZE:
                    case SC_MOVE:
                    case SC_MINIMIZE:
                    case SC_MAXIMIZE:
                    case SC_NEXTWINDOW:
                    case SC_PREVWINDOW:
                    case SC_RESTORE:
1374
                        return SendMessageW( ci->hwndChildMaximized, WM_SYSCOMMAND,
1375 1376 1377 1378 1379 1380 1381 1382
                                             wParam, lParam);
                    }
                }
                else
                {
                    HWND childHwnd;
                    if (id - ci->idFirstChild == MDI_MOREWINDOWSLIMIT)
                        /* User chose "More Windows..." */
1383
                        childHwnd = MDI_MoreWindowsDialog(hwndMDIClient);
1384 1385
                    else
                        /* User chose one of the windows listed in the "Windows" menu */
1386
                        childHwnd = MDI_GetChildByID(hwndMDIClient, id, ci);
1387 1388 1389 1390 1391 1392

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

1394
        case WM_NCACTIVATE:
1395
	    SendMessageW(hwndMDIClient, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1396 1397
	    break;

1398
        case WM_SETTEXT:
1399
            MDI_UpdateFrameText( hwnd, hwndMDIClient, FALSE, (LPWSTR)lParam );
1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413
	    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;

1414
                if (!IsIconic(hwnd) && ci->hwndActiveChild && !IsZoomed(ci->hwndActiveChild))
1415
                {
1416
                    /* control menu is between the frame system menu and
1417
                     * the first entry of menu bar */
1418
                    WND *wndPtr = WIN_GetPtr(hwnd);
1419 1420 1421 1422

                    if( (wParam == VK_LEFT && GetMenu(hwnd) == next_menu->hmenuIn) ||
                        (wParam == VK_RIGHT && GetSubMenu(wndPtr->hSysMenu, 0) == next_menu->hmenuIn) )
                    {
1423 1424
                        WIN_ReleasePtr(wndPtr);
                        wndPtr = WIN_GetPtr(ci->hwndActiveChild);
1425 1426 1427
                        next_menu->hmenuNext = GetSubMenu(wndPtr->hSysMenu, 0);
                        next_menu->hwndNext = ci->hwndActiveChild;
                    }
1428
                    WIN_ReleasePtr(wndPtr);
1429 1430 1431
                }
                return 0;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1432 1433
	}
    }
1434

1435
    return DefWindowProcW( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1436 1437 1438
}

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

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

1449
    hwnd = WIN_GetFullHandle( hwnd );
1450
    if (!ci) return DefWindowProcA( hwnd, message, wParam, lParam );
1451

Alexandre Julliard's avatar
Alexandre Julliard committed
1452 1453
    switch (message)
    {
1454
    case WM_SETTEXT:
1455
	DefWindowProcA(hwnd, message, wParam, lParam);
1456 1457
	if( ci->hwndChildMaximized == hwnd )
	    MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL );
1458 1459 1460 1461 1462 1463 1464 1465
        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:
1466
    case WM_SHOWWINDOW:
1467 1468 1469 1470
    case WM_SETVISIBLE:
    case WM_SIZE:
    case WM_NEXTMENU:
    case WM_SYSCHAR:
1471
    case WM_DESTROY:
1472
        return DefMDIChildProcW( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1473
    }
1474
    return DefWindowProcA(hwnd, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1475 1476 1477 1478
}


/***********************************************************************
1479
 *		DefMDIChildProcW (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1480
 */
1481 1482
LRESULT WINAPI DefMDIChildProcW( HWND hwnd, UINT message,
                                   WPARAM wParam, LPARAM lParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
1483
{
1484 1485 1486
    HWND client = GetParent(hwnd);
    MDICLIENTINFO *ci = get_client_info( client );

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

1489
    hwnd = WIN_GetFullHandle( hwnd );
1490
    if (!ci) return DefWindowProcW( hwnd, message, wParam, lParam );
1491

Alexandre Julliard's avatar
Alexandre Julliard committed
1492 1493
    switch (message)
    {
1494 1495
    case WM_SETTEXT:
        DefWindowProcW(hwnd, message, wParam, lParam);
1496 1497
        if( ci->hwndChildMaximized == hwnd )
            MDI_UpdateFrameText( GetParent(client), client, TRUE, NULL );
1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510
        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;

1511 1512 1513 1514 1515
    case WM_SETFOCUS:
        if (ci->hwndActiveChild != hwnd)
            MDI_ChildActivate( client, hwnd );
        break;

1516 1517 1518 1519 1520
    case WM_CHILDACTIVATE:
        MDI_ChildActivate( client, hwnd );
        return 0;

    case WM_SYSCOMMAND:
1521
        switch (wParam & 0xfff0)
1522 1523
        {
        case SC_MOVE:
1524
            if( ci->hwndChildMaximized == hwnd )
1525
                return 0;
1526 1527 1528 1529 1530
            break;
        case SC_RESTORE:
        case SC_MINIMIZE:
            break;
        case SC_MAXIMIZE:
1531
            if (ci->hwndChildMaximized == hwnd)
1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542
                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;

1543
    case WM_SHOWWINDOW:
1544
    case WM_SETVISIBLE:
1545
        if (ci->hwndChildMaximized) ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1546 1547 1548 1549
        else MDI_PostUpdate(client, ci, SB_BOTH+1);
        break;

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

1554
        if( ci->hwndChildMaximized == hwnd && wParam != SIZE_MAXIMIZED )
1555
        {
1556
            HWND frame;
1557

1558 1559
            ci->hwndChildMaximized = 0;

1560 1561
            frame = GetParent(client);
            MDI_RestoreFrameMenu( frame, hwnd );
1562
            MDI_UpdateFrameText( frame, client, TRUE, NULL );
1563 1564
        }

1565 1566 1567 1568 1569
        if( wParam == SIZE_MAXIMIZED )
        {
            HWND frame, hMaxChild = ci->hwndChildMaximized;

            if( hMaxChild == hwnd ) break;
1570

1571 1572 1573
            if( hMaxChild)
            {
                SendMessageW( hMaxChild, WM_SETREDRAW, FALSE, 0 );
1574

1575 1576
                MDI_RestoreFrameMenu( GetParent(client), hMaxChild );
                ShowWindow( hMaxChild, SW_SHOWNOACTIVATE );
1577

1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589
                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 );
        }
1590 1591 1592 1593 1594

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

1595 1596
            if (!switchTo) switchTo = hwnd;
            SendMessageW( switchTo, WM_CHILDACTIVATE, 0, 0 );
1597 1598 1599
	}

        MDI_PostUpdate(client, ci, SB_BOTH+1);
1600 1601 1602 1603 1604 1605 1606 1607 1608
        break;

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

            if( wParam == VK_LEFT )  /* switch to frame system menu */
            {
1609
                WND *wndPtr = WIN_GetPtr( parent );
1610
                next_menu->hmenuNext = GetSubMenu( wndPtr->hSysMenu, 0 );
1611
                WIN_ReleasePtr( wndPtr );
1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627
            }
            if( wParam == VK_RIGHT )  /* to frame menu bar */
            {
                next_menu->hmenuNext = GetMenu(parent);
            }
            next_menu->hwndNext = parent;
            return 0;
        }

    case WM_SYSCHAR:
        if (wParam == '-')
        {
            SendMessageW( hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (DWORD)VK_SPACE);
            return 0;
        }
        break;
1628 1629 1630 1631 1632

    case WM_DESTROY:
        /* Remove itself from the Window menu */
        MDI_RefreshMenu(ci);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1633
    }
1634
    return DefWindowProcW(hwnd, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1635 1636
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1637
/**********************************************************************
1638
 *		CreateMDIWindowA (USER32.@) Creates a MDI child
1639 1640 1641 1642
 *
 * RETURNS
 *    Success: Handle to created window
 *    Failure: NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
1643
 */
1644
HWND WINAPI CreateMDIWindowA(
1645 1646 1647
    LPCSTR lpClassName,    /* [in] Pointer to registered child class name */
    LPCSTR lpWindowName,   /* [in] Pointer to window name */
    DWORD dwStyle,         /* [in] Window style */
1648 1649 1650 1651 1652 1653
    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 */
1654
    LPARAM lParam)         /* [in] Application-defined value */
Alexandre Julliard's avatar
Alexandre Julliard committed
1655
{
1656
    TRACE("(%s,%s,%08x,%d,%d,%d,%d,%p,%p,%08lx)\n",
1657 1658
          debugstr_a(lpClassName),debugstr_a(lpWindowName),dwStyle,X,Y,
          nWidth,nHeight,hWndParent,hInstance,lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1659

1660 1661 1662
    return CreateWindowExA(WS_EX_MDICHILD, lpClassName, lpWindowName,
                           dwStyle, X, Y, nWidth, nHeight, hWndParent,
                           0, hInstance, (LPVOID)lParam);
1663 1664
}

1665
/***********************************************************************
1666
 *		CreateMDIWindowW (USER32.@) Creates a MDI child
Alexandre Julliard's avatar
Alexandre Julliard committed
1667 1668 1669 1670
 *
 * RETURNS
 *    Success: Handle to created window
 *    Failure: NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
1671
 */
1672
HWND WINAPI CreateMDIWindowW(
1673 1674 1675
    LPCWSTR lpClassName,    /* [in] Pointer to registered child class name */
    LPCWSTR lpWindowName,   /* [in] Pointer to window name */
    DWORD dwStyle,         /* [in] Window style */
1676 1677 1678 1679 1680 1681
    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 */
1682 1683
    LPARAM lParam)         /* [in] Application-defined value */
{
1684
    TRACE("(%s,%s,%08x,%d,%d,%d,%d,%p,%p,%08lx)\n",
1685 1686
          debugstr_w(lpClassName), debugstr_w(lpWindowName), dwStyle, X, Y,
          nWidth, nHeight, hWndParent, hInstance, lParam);
1687

1688 1689 1690
    return CreateWindowExW(WS_EX_MDICHILD, lpClassName, lpWindowName,
                           dwStyle, X, Y, nWidth, nHeight, hWndParent,
                           0, hInstance, (LPVOID)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1691 1692
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1693
/**********************************************************************
1694
 *		TranslateMDISysAccel (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1695
 */
1696
BOOL WINAPI TranslateMDISysAccel( HWND hwndClient, LPMSG msg )
Alexandre Julliard's avatar
Alexandre Julliard committed
1697
{
1698
    if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN)
Alexandre Julliard's avatar
Alexandre Julliard committed
1699
    {
1700 1701
        MDICLIENTINFO *ci = get_client_info( hwndClient );
        WPARAM wParam = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1702

1703
        if (!ci || !IsWindowEnabled(ci->hwndActiveChild)) return 0;
1704

1705
        /* translate if the Ctrl key is down and Alt not. */
1706

1707 1708 1709 1710 1711 1712 1713 1714 1715 1716
        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:
1717 1718 1719 1720 1721 1722
                if (is_close_enabled(ci->hwndActiveChild, 0))
                {
                    wParam = SC_CLOSE;
                    break;
                }
                /* fall through */
1723 1724 1725
            default:
                return 0;
            }
1726
            TRACE("wParam = %04lx\n", wParam);
1727 1728 1729
            SendMessageW(ci->hwndActiveChild, WM_SYSCOMMAND, wParam, (LPARAM)msg->wParam);
            return 1;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1730 1731
    }
    return 0; /* failure */
Alexandre Julliard's avatar
Alexandre Julliard committed
1732
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1733

1734 1735 1736 1737
/***********************************************************************
 *		CalcChildScroll (USER32.@)
 */
void WINAPI CalcChildScroll( HWND hwnd, INT scroll )
Alexandre Julliard's avatar
Alexandre Julliard committed
1738
{
1739
    SCROLLINFO info;
1740
    RECT childRect, clientRect;
1741
    HWND *list;
Alexandre Julliard's avatar
Alexandre Julliard committed
1742

1743 1744
    GetClientRect( hwnd, &clientRect );
    SetRectEmpty( &childRect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1745

1746
    if ((list = WIN_ListChildren( hwnd )))
Alexandre Julliard's avatar
Alexandre Julliard committed
1747
    {
1748 1749 1750 1751 1752 1753
        int i;
        for (i = 0; list[i]; i++)
        {
            DWORD style = GetWindowLongW( list[i], GWL_STYLE );
            if (style & WS_MAXIMIZE)
            {
1754
                HeapFree( GetProcessHeap(), 0, list );
1755 1756 1757 1758 1759
                ShowScrollBar( hwnd, SB_BOTH, FALSE );
                return;
            }
            if (style & WS_VISIBLE)
            {
1760 1761 1762
                RECT rect;
                GetWindowRect( list[i], &rect );
                UnionRect( &childRect, &rect, &childRect );
1763 1764
            }
        }
1765
        HeapFree( GetProcessHeap(), 0, list );
1766
    }
1767
    MapWindowPoints( 0, hwnd, (POINT *)&childRect, 2 );
1768
    UnionRect( &childRect, &clientRect, &childRect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1769

1770 1771 1772
    /* set common info values */
    info.cbSize = sizeof(info);
    info.fMask = SIF_POS | SIF_RANGE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1773

1774
    /* set the specific */
Alexandre Julliard's avatar
Alexandre Julliard committed
1775
    switch( scroll )
Alexandre Julliard's avatar
Alexandre Julliard committed
1776
    {
1777
	case SB_BOTH:
Alexandre Julliard's avatar
Alexandre Julliard committed
1778
	case SB_HORZ:
1779 1780 1781
			info.nMin = childRect.left;
			info.nMax = childRect.right - clientRect.right;
			info.nPos = clientRect.left - childRect.left;
1782
			SetScrollInfo(hwnd, SB_HORZ, &info, TRUE);
1783 1784
			if (scroll == SB_HORZ) break;
			/* fall through */
Alexandre Julliard's avatar
Alexandre Julliard committed
1785
	case SB_VERT:
1786 1787 1788
			info.nMin = childRect.top;
			info.nMax = childRect.bottom - clientRect.bottom;
			info.nPos = clientRect.top - childRect.top;
1789
			SetScrollInfo(hwnd, SB_VERT, &info, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1790
			break;
1791
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1792
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1793

Alexandre Julliard's avatar
Alexandre Julliard committed
1794

Alexandre Julliard's avatar
Alexandre Julliard committed
1795
/***********************************************************************
1796
 *		ScrollChildren (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1797
 */
1798
void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam,
Alexandre Julliard's avatar
Alexandre Julliard committed
1799
                             LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1800
{
1801 1802
    INT newPos = -1;
    INT curPos, length, minPos, maxPos, shift;
1803
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1804

1805
    GetClientRect( hWnd, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1806

1807
    switch(uMsg)
Alexandre Julliard's avatar
Alexandre Julliard committed
1808
    {
1809
    case WM_HSCROLL:
1810 1811
	GetScrollRange(hWnd,SB_HORZ,&minPos,&maxPos);
	curPos = GetScrollPos(hWnd,SB_HORZ);
1812
	length = (rect.right - rect.left) / 2;
1813
	shift = GetSystemMetrics(SM_CYHSCROLL);
1814 1815
        break;
    case WM_VSCROLL:
1816 1817
	GetScrollRange(hWnd,SB_VERT,&minPos,&maxPos);
	curPos = GetScrollPos(hWnd,SB_VERT);
1818
	length = (rect.bottom - rect.top) / 2;
1819
	shift = GetSystemMetrics(SM_CXVSCROLL);
1820 1821
        break;
    default:
1822 1823
        return;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1824

Alexandre Julliard's avatar
Alexandre Julliard committed
1825 1826
    switch( wParam )
    {
1827
	case SB_LINEUP:
Alexandre Julliard's avatar
Alexandre Julliard committed
1828 1829
		        newPos = curPos - shift;
			break;
1830
	case SB_LINEDOWN:
Alexandre Julliard's avatar
Alexandre Julliard committed
1831 1832
			newPos = curPos + shift;
			break;
1833
	case SB_PAGEUP:
Alexandre Julliard's avatar
Alexandre Julliard committed
1834 1835
			newPos = curPos - length;
			break;
1836
	case SB_PAGEDOWN:
Alexandre Julliard's avatar
Alexandre Julliard committed
1837 1838 1839
			newPos = curPos + length;
			break;

1840
	case SB_THUMBPOSITION:
Alexandre Julliard's avatar
Alexandre Julliard committed
1841 1842 1843
			newPos = LOWORD(lParam);
			break;

1844
	case SB_THUMBTRACK:
Alexandre Julliard's avatar
Alexandre Julliard committed
1845 1846
			return;

1847
	case SB_TOP:
Alexandre Julliard's avatar
Alexandre Julliard committed
1848 1849
			newPos = minPos;
			break;
1850
	case SB_BOTTOM:
Alexandre Julliard's avatar
Alexandre Julliard committed
1851 1852 1853
			newPos = maxPos;
			break;
	case SB_ENDSCROLL:
1854
			CalcChildScroll(hWnd,(uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ);
Alexandre Julliard's avatar
Alexandre Julliard committed
1855
			return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1856
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1857

Alexandre Julliard's avatar
Alexandre Julliard committed
1858 1859
    if( newPos > maxPos )
	newPos = maxPos;
1860
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
1861 1862
	if( newPos < minPos )
	    newPos = minPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
1863

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1866
    if( uMsg == WM_VSCROLL )
1867
	ScrollWindowEx(hWnd ,0 ,curPos - newPos, NULL, NULL, 0, NULL,
Alexandre Julliard's avatar
Alexandre Julliard committed
1868
			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
Alexandre Julliard's avatar
Alexandre Julliard committed
1869
    else
1870
	ScrollWindowEx(hWnd ,curPos - newPos, 0, NULL, NULL, 0, NULL,
Alexandre Julliard's avatar
Alexandre Julliard committed
1871
			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
Alexandre Julliard's avatar
Alexandre Julliard committed
1872 1873
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1874 1875

/******************************************************************************
1876
 *		CascadeWindows (USER32.@) Cascades MDI child windows
Alexandre Julliard's avatar
Alexandre Julliard committed
1877 1878 1879 1880 1881 1882
 *
 * RETURNS
 *    Success: Number of cascaded windows.
 *    Failure: 0
 */
WORD WINAPI
1883
CascadeWindows (HWND hwndParent, UINT wFlags, const RECT *lpRect,
1884
		UINT cKids, const HWND *lpKids)
Alexandre Julliard's avatar
Alexandre Julliard committed
1885
{
1886
    FIXME("(%p,0x%08x,...,%u,...): stub\n", hwndParent, wFlags, cKids);
Alexandre Julliard's avatar
Alexandre Julliard committed
1887 1888 1889 1890
    return 0;
}


1891 1892 1893 1894 1895 1896 1897 1898 1899
/***********************************************************************
 *		CascadeChildWindows (USER32.@)
 */
WORD WINAPI CascadeChildWindows( HWND parent, UINT flags )
{
    return CascadeWindows( parent, flags, NULL, 0, NULL );
}


Alexandre Julliard's avatar
Alexandre Julliard committed
1900
/******************************************************************************
1901
 *		TileWindows (USER32.@) Tiles MDI child windows
Alexandre Julliard's avatar
Alexandre Julliard committed
1902 1903 1904 1905 1906 1907
 *
 * RETURNS
 *    Success: Number of tiled windows.
 *    Failure: 0
 */
WORD WINAPI
1908
TileWindows (HWND hwndParent, UINT wFlags, const RECT *lpRect,
1909
	     UINT cKids, const HWND *lpKids)
Alexandre Julliard's avatar
Alexandre Julliard committed
1910
{
1911
    FIXME("(%p,0x%08x,...,%u,...): stub\n", hwndParent, wFlags, cKids);
Alexandre Julliard's avatar
Alexandre Julliard committed
1912 1913 1914
    return 0;
}

1915 1916 1917 1918 1919 1920 1921 1922 1923 1924

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


1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937
/************************************************************************
 *              "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...
 *
 */

1938
static INT_PTR WINAPI MDI_MoreWindowsDlgProc (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
1939 1940 1941 1942 1943 1944 1945 1946
{
    switch (iMsg)
    {
       case WM_INITDIALOG:
       {
           UINT widest       = 0;
           UINT length;
           UINT i;
1947
           MDICLIENTINFO *ci = get_client_info( (HWND)lParam );
1948 1949
           HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX);

1950 1951
           for (i = 0; i < ci->nActiveChildren; i++)
           {
1952
               WCHAR buffer[MDI_MAXTITLELENGTH];
1953

1954
               if (!InternalGetWindowText( ci->child[i], buffer, sizeof(buffer)/sizeof(WCHAR) ))
1955 1956
                   continue;
               SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)buffer );
1957
               SendMessageW(hListBox, LB_SETITEMDATA, i, (LPARAM)ci->child[i] );
1958
               length = strlenW(buffer);  /* FIXME: should use GetTextExtentPoint */
1959 1960 1961 1962
               if (length > widest)
                   widest = length;
           }
           /* Make sure the horizontal scrollbar scrolls ok */
1963
           SendMessageW(hListBox, LB_SETHORIZONTALEXTENT, widest * 6, 0);
1964 1965

           /* Set the current selection */
1966
           SendMessageW(hListBox, LB_SETCURSEL, MDI_MOREWINDOWSLIMIT, 0);
1967 1968 1969 1970 1971 1972
           return TRUE;
       }

       case WM_COMMAND:
           switch (LOWORD(wParam))
           {
1973 1974 1975
                default:
                    if (HIWORD(wParam) != LBN_DBLCLK) break;
                    /* fall through */
1976 1977 1978 1979 1980 1981
                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);
1982
                    UINT index        = SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
1983 1984
                    LRESULT res = SendMessageW(hListBox, LB_GETITEMDATA, index, 0);
                    EndDialog(hDlg, res);
1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006
                    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.
 */

2007
static HWND MDI_MoreWindowsDialog(HWND hwnd)
2008 2009 2010 2011 2012
{
    LPCVOID template;
    HRSRC hRes;
    HANDLE hDlgTmpl;

2013
    hRes = FindResourceA(user32_module, "MDI_MOREWINDOWS", (LPSTR)RT_DIALOG);
2014 2015 2016 2017

    if (hRes == 0)
        return 0;

2018
    hDlgTmpl = LoadResource(user32_module, hRes );
2019 2020 2021 2022 2023 2024 2025 2026 2027

    if (hDlgTmpl == 0)
        return 0;

    template = LockResource( hDlgTmpl );

    if (template == 0)
        return 0;

2028 2029
    return (HWND) DialogBoxIndirectParamA(user32_module, template, hwnd,
                                          MDI_MoreWindowsDlgProc, (LPARAM) hwnd);
2030
}