mdi.c 59.6 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 31 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
 *
 * 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
 *      a WND* associated with it, accesible via the children list of
 *      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
#include "windef.h"
90
#include "winbase.h"
91
#include "wingdi.h"
92
#include "winuser.h"
93
#include "wownt32.h"
94
#include "wine/winuser16.h"
95
#include "wine/unicode.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
96
#include "win.h"
97
#include "controls.h"
98
#include "user_private.h"
99
#include "wine/debug.h"
100
#include "dlgs.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
101

102
WINE_DEFAULT_DEBUG_CHANNEL(mdi);
103

104 105 106 107 108
#define MDI_MAXTITLELENGTH      0xa1

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

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

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

116 117 118 119
typedef struct
{
    UINT      nActiveChildren;
    HWND      hwndActiveChild;
120 121
    HWND      *child; /* array of tracked children */
    HMENU     hFrameMenu;
122 123 124 125 126 127 128 129
    HMENU     hWindowMenu;
    UINT      idFirstChild;
    LPWSTR    frameTitle;
    UINT      nTotalCreated;
    UINT      mdiFlags;
    UINT      sbRecalc;   /* SB_xxx flags for scrollbar fixup */
} MDICLIENTINFO;

130
static HBITMAP hBmpClose   = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
131

Alexandre Julliard's avatar
Alexandre Julliard committed
132
/* ----------------- declarations ----------------- */
133
static void MDI_UpdateFrameText( HWND, HWND, LPCWSTR);
134 135 136
static BOOL MDI_AugmentFrameMenu( HWND, HWND );
static BOOL MDI_RestoreFrameMenu( HWND, HWND );
static LONG MDI_ChildActivate( HWND, HWND );
137
static LRESULT MDI_RefreshMenu(MDICLIENTINFO *);
Alexandre Julliard's avatar
Alexandre Julliard committed
138

139
static HWND MDI_MoreWindowsDialog(HWND);
140
static LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
141 142
static LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );

Alexandre Julliard's avatar
Alexandre Julliard committed
143 144 145
/* -------- Miscellaneous service functions ----------
 *
 *			MDI_GetChildByID
Alexandre Julliard's avatar
Alexandre Julliard committed
146
 */
147
static HWND MDI_GetChildByID(HWND hwnd, UINT id, MDICLIENTINFO *ci)
Alexandre Julliard's avatar
Alexandre Julliard committed
148
{
149 150
    int i;

151
    for (i = 0; ci->nActiveChildren; i++)
152
    {
153
        if (GetWindowLongPtrW( ci->child[i], GWLP_ID ) == id)
154
            return ci->child[i];
155
    }
156
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
157 158
}

159
static void MDI_PostUpdate(HWND hwnd, MDICLIENTINFO* ci, WORD recalc)
Alexandre Julliard's avatar
Alexandre Julliard committed
160
{
Alexandre Julliard's avatar
Alexandre Julliard committed
161 162 163
    if( !(ci->mdiFlags & MDIF_NEEDUPDATE) )
    {
	ci->mdiFlags |= MDIF_NEEDUPDATE;
164
	PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
165 166
    }
    ci->sbRecalc = recalc;
Alexandre Julliard's avatar
Alexandre Julliard committed
167 168
}

169 170 171 172 173 174 175

/*********************************************************************
 * MDIClient class descriptor
 */
const struct builtin_class_descr MDICLIENT_builtin_class =
{
    "MDIClient",            /* name */
176
    0,                      /* style */
177
    MDIClientWndProcA,      /* procA */
178
    MDIClientWndProcW,      /* procW */
179
    sizeof(MDICLIENTINFO),  /* extra */
180
    IDC_ARROW,              /* cursor */
181
    (HBRUSH)(COLOR_APPWORKSPACE+1)    /* brush */
182 183 184
};


185 186 187
static MDICLIENTINFO *get_client_info( HWND client )
{
    MDICLIENTINFO *ret = NULL;
188
    WND *win = WIN_GetPtr( client );
189 190
    if (win)
    {
191 192
        if (win == WND_OTHER_PROCESS)
        {
193
            if (IsWindow(client)) ERR( "client %p belongs to other process\n", client );
194 195
            return NULL;
        }
196
        if (win->cbWndExtra < sizeof(MDICLIENTINFO)) WARN( "%p is not an MDI client\n", client );
197
        else ret = (MDICLIENTINFO *)win->wExtra;
198
        WIN_ReleasePtr( win );
199 200 201 202
    }
    return ret;
}

203 204 205 206 207 208 209 210 211 212 213 214 215 216
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
217 218 219
/**********************************************************************
 * 			MDI_GetWindow
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
220
 * returns "activateable" child different from the current or zero
Alexandre Julliard's avatar
Alexandre Julliard committed
221
 */
222
static HWND MDI_GetWindow(MDICLIENTINFO *clientInfo, HWND hWnd, BOOL bNext,
Alexandre Julliard's avatar
Alexandre Julliard committed
223
                            DWORD dwStyleMask )
Alexandre Julliard's avatar
Alexandre Julliard committed
224
{
225 226 227 228
    int i;
    HWND *list;
    HWND last = 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
229
    dwStyleMask |= WS_DISABLED | WS_VISIBLE;
Alexandre Julliard's avatar
Alexandre Julliard committed
230
    if( !hWnd ) hWnd = clientInfo->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
231

232
    if (!(list = WIN_ListChildren( GetParent(hWnd) ))) return 0;
233 234 235 236
    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
237

238
    for ( ; list[i]; i++)
Alexandre Julliard's avatar
Alexandre Julliard committed
239
    {
240 241 242 243 244 245 246 247 248 249 250 251
        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
252
    }
253
 found:
254
    HeapFree( GetProcessHeap(), 0, list );
255
    return last;
Alexandre Julliard's avatar
Alexandre Julliard committed
256 257
}

Alexandre Julliard's avatar
Alexandre Julliard committed
258 259 260
/**********************************************************************
 *			MDI_CalcDefaultChildPos
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
261
 *  It seems that the default height is about 2/3 of the client rect
Alexandre Julliard's avatar
Alexandre Julliard committed
262
 */
263
void MDI_CalcDefaultChildPos( HWND hwndClient, INT total, LPPOINT lpPos, INT delta, UINT *id )
Alexandre Julliard's avatar
Alexandre Julliard committed
264
{
265
    INT  nstagger;
266
    RECT rect;
267
    INT spacing = GetSystemMetrics(SM_CYCAPTION) + GetSystemMetrics(SM_CYFRAME) - 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
268

269
    if (total < 0) /* we are called from CreateWindow */
270 271 272
    {
        MDICLIENTINFO *ci = get_client_info(hwndClient);
        total = ci ? ci->nTotalCreated : 0;
273
        *id = ci->idFirstChild + ci->nActiveChildren;
274
        TRACE("MDI child id %04x\n", *id);
275 276 277
    }

    GetClientRect( hwndClient, &rect );
278
    if( rect.bottom - rect.top - delta >= spacing )
Alexandre Julliard's avatar
Alexandre Julliard committed
279 280 281 282 283
	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);
284
    lpPos[0].x = lpPos[0].y = spacing * (total%(nstagger+1));
Alexandre Julliard's avatar
Alexandre Julliard committed
285
}
Alexandre Julliard's avatar
Alexandre Julliard committed
286

Alexandre Julliard's avatar
Alexandre Julliard committed
287
/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
288
 *            MDISetMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
289
 */
290 291
static LRESULT MDISetMenu( HWND hwnd, HMENU hmenuFrame,
                           HMENU hmenuWindow)
Alexandre Julliard's avatar
Alexandre Julliard committed
292
{
Alexandre Julliard's avatar
Alexandre Julliard committed
293
    MDICLIENTINFO *ci;
294
    HWND hwndFrame = GetParent(hwnd);
Alexandre Julliard's avatar
Alexandre Julliard committed
295

296
    TRACE("%p %p %p\n", hwnd, hmenuFrame, hmenuWindow);
Alexandre Julliard's avatar
Alexandre Julliard committed
297

298 299 300 301 302
    if (hmenuFrame && !IsMenu(hmenuFrame))
    {
	WARN("hmenuFrame is not a menu handle\n");
	return 0L;
    }
303

304 305 306 307 308
    if (hmenuWindow && !IsMenu(hmenuWindow))
    {
	WARN("hmenuWindow is not a menu handle\n");
	return 0L;
    }
309 310

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

312 313 314
    if (hmenuFrame)
    {
        if (hmenuFrame == ci->hFrameMenu) return (LRESULT)hmenuFrame;
315

316
        if (IsZoomed(ci->hwndActiveChild))
317 318
            MDI_RestoreFrameMenu( hwndFrame, ci->hwndActiveChild );
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
319

320
    if( hmenuWindow && hmenuWindow != ci->hWindowMenu )
Alexandre Julliard's avatar
Alexandre Julliard committed
321
    {
322
        /* delete menu items from ci->hWindowMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
323
         * and add them to hmenuWindow */
324 325
        /* Agent newsreader calls this function with  ci->hWindowMenu == NULL */
        if( ci->hWindowMenu && ci->nActiveChildren )
Alexandre Julliard's avatar
Alexandre Julliard committed
326
        {
327
            UINT nActiveChildren_old = ci->nActiveChildren;
328

329 330 331
            /* Remove all items from old Window menu */
            ci->nActiveChildren = 0;
            MDI_RefreshMenu(ci);
332

333
            ci->hWindowMenu = hmenuWindow;
Alexandre Julliard's avatar
Alexandre Julliard committed
334

335 336 337
            /* Add items to the new Window menu */
            ci->nActiveChildren = nActiveChildren_old;
            MDI_RefreshMenu(ci);
Alexandre Julliard's avatar
Alexandre Julliard committed
338
        }
339
        else
340
            ci->hWindowMenu = hmenuWindow;
341
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
342

343
    if (hmenuFrame)
Alexandre Julliard's avatar
Alexandre Julliard committed
344
    {
345
        SetMenu(hwndFrame, hmenuFrame);
346
        if( hmenuFrame != ci->hFrameMenu )
347
        {
348 349 350
            HMENU oldFrameMenu = ci->hFrameMenu;

            ci->hFrameMenu = hmenuFrame;
351
            if (IsZoomed(ci->hwndActiveChild) && (GetWindowLongW(ci->hwndActiveChild, GWL_STYLE) & WS_VISIBLE))
352 353
                MDI_AugmentFrameMenu( hwndFrame, ci->hwndActiveChild );

354
            return (LRESULT)oldFrameMenu;
355
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
356
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
357 358
    else
    {
359 360 361 362 363 364 365 366
        /* 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.
         */
        if( IsZoomed(ci->hwndActiveChild) )
            MDI_AugmentFrameMenu( hwndFrame, ci->hwndActiveChild );
Alexandre Julliard's avatar
Alexandre Julliard committed
367
    }
368

Alexandre Julliard's avatar
Alexandre Julliard committed
369 370
    return 0;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
371

Alexandre Julliard's avatar
Alexandre Julliard committed
372 373 374
/**********************************************************************
 *            MDIRefreshMenu
 */
375
static LRESULT MDI_RefreshMenu(MDICLIENTINFO *ci)
Alexandre Julliard's avatar
Alexandre Julliard committed
376
{
377
    UINT i, count, visible, id;
378
    WCHAR buf[MDI_MAXTITLELENGTH];
Alexandre Julliard's avatar
Alexandre Julliard committed
379

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

382 383
    if (!ci->hWindowMenu)
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
384

385 386 387 388 389 390
    if (!IsMenu(ci->hWindowMenu))
    {
        WARN("Window menu handle %p is no more valid\n", ci->hWindowMenu);
        return 0;
    }

391 392
    /* Windows finds the last separator in the menu, and if after it
     * there is a menu item with MDI magic ID removes all existing
393 394
     * menu items after it, and then adds visible MDI children.
     */
395
    count = GetMenuItemCount(ci->hWindowMenu);
396 397 398 399 400 401 402
    for (i = 0; i < count; i++)
    {
        MENUITEMINFOW mii;

        memset(&mii, 0, sizeof(mii));
        mii.cbSize = sizeof(mii);
        mii.fMask  = MIIM_TYPE;
403 404 405 406 407 408 409 410 411
        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))
412 413 414 415 416 417 418 419 420 421
                {
                    if (mii.wID == ci->idFirstChild)
                    {
                        TRACE("removing %u items including separator\n", count - i);
                        while (RemoveMenu(ci->hWindowMenu, i, MF_BYPOSITION))
                            /* nothing */;

                        break;
                    }
                }
422 423
            }
        }
424 425
    }

426 427
    visible = 0;
    for (i = 0; i < ci->nActiveChildren; i++)
Alexandre Julliard's avatar
Alexandre Julliard committed
428
    {
429
        if (GetWindowLongW(ci->child[i], GWL_STYLE) & WS_VISIBLE)
430
        {
431 432 433 434 435 436 437 438 439 440
            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)
441 442
                /* Visio expects that separator has id 0 */
                AppendMenuW(ci->hWindowMenu, MF_SEPARATOR, 0, NULL);
Alexandre Julliard's avatar
Alexandre Julliard committed
443

444
            visible++;
Alexandre Julliard's avatar
Alexandre Julliard committed
445

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

448 449 450 451
            buf[0] = '&';
            buf[1] = '0' + visible;
            buf[2] = ' ';
            InternalGetWindowText(ci->child[i], buf + 3, sizeof(buf)/sizeof(WCHAR) - 3);
452
            TRACE("Adding %p, id %u %s\n", ci->child[i], id, debugstr_w(buf));
453
            AppendMenuW(ci->hWindowMenu, MF_STRING, id, buf);
Alexandre Julliard's avatar
Alexandre Julliard committed
454

455 456
            if (ci->child[i] == ci->hwndActiveChild)
                CheckMenuItem(ci->hWindowMenu, id, MF_CHECKED);
457
        }
458 459
        else
            TRACE("MDI child %p is not visible, skipping\n", ci->child[i]);
Alexandre Julliard's avatar
Alexandre Julliard committed
460
    }
461

462
    return (LRESULT)ci->hFrameMenu;
Alexandre Julliard's avatar
Alexandre Julliard committed
463 464
}

465 466 467

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

Alexandre Julliard's avatar
Alexandre Julliard committed
468 469
/**********************************************************************
 *			MDI_ChildGetMinMaxInfo
Alexandre Julliard's avatar
Alexandre Julliard committed
470
 *
471
 * Note: The rule here is that client rect of the maximized MDI child
Alexandre Julliard's avatar
Alexandre Julliard committed
472
 *	 is equal to the client rect of the MDI client window.
Alexandre Julliard's avatar
Alexandre Julliard committed
473
 */
474
static void MDI_ChildGetMinMaxInfo( HWND client, HWND hwnd, MINMAXINFO* lpMinMax )
Alexandre Julliard's avatar
Alexandre Julliard committed
475
{
476
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
477

478 479 480
    GetClientRect( client, &rect );
    AdjustWindowRectEx( &rect, GetWindowLongW( hwnd, GWL_STYLE ),
                        0, GetWindowLongW( hwnd, GWL_EXSTYLE ));
Alexandre Julliard's avatar
Alexandre Julliard committed
481

Alexandre Julliard's avatar
Alexandre Julliard committed
482 483
    lpMinMax->ptMaxSize.x = rect.right -= rect.left;
    lpMinMax->ptMaxSize.y = rect.bottom -= rect.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
484

Alexandre Julliard's avatar
Alexandre Julliard committed
485
    lpMinMax->ptMaxPosition.x = rect.left;
486
    lpMinMax->ptMaxPosition.y = rect.top;
Alexandre Julliard's avatar
Alexandre Julliard committed
487

488
    TRACE("max rect (%d,%d - %d, %d)\n",
Alexandre Julliard's avatar
Alexandre Julliard committed
489
                        rect.left,rect.top,rect.right,rect.bottom);
Alexandre Julliard's avatar
Alexandre Julliard committed
490 491
}

Alexandre Julliard's avatar
Alexandre Julliard committed
492
/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
493
 *			MDI_SwitchActiveChild
494
 *
Alexandre Julliard's avatar
Alexandre Julliard committed
495
 * Note: SetWindowPos sends WM_CHILDACTIVATE to the child window that is
496
 *       being activated
Alexandre Julliard's avatar
Alexandre Julliard committed
497
 */
498
static void MDI_SwitchActiveChild( MDICLIENTINFO *ci, HWND hwndTo, BOOL activate )
Alexandre Julliard's avatar
Alexandre Julliard committed
499
{
500
    HWND hwndPrev;
Alexandre Julliard's avatar
Alexandre Julliard committed
501

502
    hwndPrev = ci->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
503

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

506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
    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 */
523
        SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | (activate ? 0 : SWP_NOACTIVATE) );
524
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
525
}
Alexandre Julliard's avatar
Alexandre Julliard committed
526

527

Alexandre Julliard's avatar
Alexandre Julliard committed
528 529 530
/**********************************************************************
 *                                      MDIDestroyChild
 */
531
static LRESULT MDIDestroyChild( HWND client, MDICLIENTINFO *ci,
532
                                HWND child, BOOL flagDestroy )
Alexandre Julliard's avatar
Alexandre Julliard committed
533
{
534 535
    UINT i;

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

538
    if( child == ci->hwndActiveChild )
Alexandre Julliard's avatar
Alexandre Julliard committed
539
    {
540
        HWND next = MDI_GetWindow(ci, child, TRUE, 0);
541
        if (next)
542
            MDI_SwitchActiveChild(ci, next, TRUE);
543
        else
544 545 546 547 548 549 550 551 552
        {
            ShowWindow(child, SW_HIDE);
            if (IsZoomed(child))
            {
                MDI_RestoreFrameMenu(GetParent(client), child);
                MDI_UpdateFrameText(GetParent(client), client, NULL);
            }
            MDI_ChildActivate(client, 0);
        }
553
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
554

555 556 557 558 559 560 561 562 563 564
    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;
565 566 567

            ci->nActiveChildren--;
            break;
568 569
        }
    }
570

571 572
    SendMessageW(client, WM_MDIREFRESHMENU, 0, 0);

573 574 575 576
    if (flagDestroy)
    {
        MDI_PostUpdate(GetParent(child), ci, SB_BOTH+1);
        DestroyWindow(child);
Alexandre Julliard's avatar
Alexandre Julliard committed
577
    }
578

579
    TRACE("child destroyed - %p\n", child);
Alexandre Julliard's avatar
Alexandre Julliard committed
580
    return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
581 582
}

Alexandre Julliard's avatar
Alexandre Julliard committed
583

Alexandre Julliard's avatar
Alexandre Julliard committed
584
/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
585 586
 *					MDI_ChildActivate
 *
587 588
 * Called in response to WM_CHILDACTIVATE, or when last MDI child
 * is being deactivated.
Alexandre Julliard's avatar
Alexandre Julliard committed
589
 */
590
static LONG MDI_ChildActivate( HWND client, HWND child )
Alexandre Julliard's avatar
Alexandre Julliard committed
591
{
592
    MDICLIENTINFO *clientInfo;
593
    HWND prevActiveWnd, frame;
594 595
    BOOL isActiveFrameWnd;

596 597
    clientInfo = get_client_info( client );

598 599
    if (clientInfo->hwndActiveChild == child) return 0;

600
    TRACE("%p\n", child);
601

602 603
    frame = GetParent(client);
    isActiveFrameWnd = (GetActiveWindow() == frame);
604
    prevActiveWnd = clientInfo->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
605 606

    /* deactivate prev. active child */
607
    if(prevActiveWnd)
Alexandre Julliard's avatar
Alexandre Julliard committed
608
    {
609 610
        SendMessageW( prevActiveWnd, WM_NCACTIVATE, FALSE, 0L );
        SendMessageW( prevActiveWnd, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child);
Alexandre Julliard's avatar
Alexandre Julliard committed
611
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
612

613
    MDI_SwitchActiveChild( clientInfo, child, FALSE );
614 615
    clientInfo->hwndActiveChild = child;

616 617
    MDI_RefreshMenu(clientInfo);

Alexandre Julliard's avatar
Alexandre Julliard committed
618
    if( isActiveFrameWnd )
619
    {
620
        SendMessageW( child, WM_NCACTIVATE, TRUE, 0L);
621 622 623 624 625 626 627
        /* 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 );
628
    }
629 630

    SendMessageW( child, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child );
631
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
632
}
Alexandre Julliard's avatar
Alexandre Julliard committed
633

Alexandre Julliard's avatar
Alexandre Julliard committed
634 635 636 637 638
/* -------------------- MDI client window functions ------------------- */

/**********************************************************************
 *				CreateMDIMenuBitmap
 */
639
static HBITMAP CreateMDIMenuBitmap(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
640
{
641 642
 HDC 		hDCSrc  = CreateCompatibleDC(0);
 HDC		hDCDest	= CreateCompatibleDC(hDCSrc);
643
 HBITMAP	hbClose = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_OLD_CLOSE) );
644 645
 HBITMAP	hbCopy;
 HBITMAP	hobjSrc, hobjDest;
Alexandre Julliard's avatar
Alexandre Julliard committed
646

647
 hobjSrc = SelectObject(hDCSrc, hbClose);
648
 hbCopy = CreateCompatibleBitmap(hDCSrc,GetSystemMetrics(SM_CXSIZE),GetSystemMetrics(SM_CYSIZE));
649
 hobjDest = SelectObject(hDCDest, hbCopy);
Alexandre Julliard's avatar
Alexandre Julliard committed
650

651 652
 BitBlt(hDCDest, 0, 0, GetSystemMetrics(SM_CXSIZE), GetSystemMetrics(SM_CYSIZE),
          hDCSrc, GetSystemMetrics(SM_CXSIZE), 0, SRCCOPY);
653

654 655 656
 SelectObject(hDCSrc, hobjSrc);
 DeleteObject(hbClose);
 DeleteDC(hDCSrc);
Alexandre Julliard's avatar
Alexandre Julliard committed
657

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

660 661
 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
662

663 664 665
 SelectObject(hDCDest, hobjSrc );
 SelectObject(hDCDest, hobjDest);
 DeleteDC(hDCDest);
Alexandre Julliard's avatar
Alexandre Julliard committed
666 667 668 669

 return hbCopy;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
670
/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
671
 *				MDICascade
Alexandre Julliard's avatar
Alexandre Julliard committed
672
 */
673
static LONG MDICascade( HWND client, MDICLIENTINFO *ci )
Alexandre Julliard's avatar
Alexandre Julliard committed
674
{
675 676 677 678
    HWND *win_array;
    BOOL has_icons = FALSE;
    int i, total;

679
    if (IsZoomed(ci->hwndActiveChild))
680
        SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndActiveChild, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
681

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

684
    if (!(win_array = WIN_ListChildren( client ))) return 0;
685 686 687

    /* remove all the windows we don't want */
    for (i = total = 0; win_array[i]; i++)
Alexandre Julliard's avatar
Alexandre Julliard committed
688
    {
689 690 691 692 693 694 695 696
        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
697
    }
698
    win_array[total] = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
699

700 701 702 703 704 705 706 707 708
    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--)
        {
709
            TRACE("move %p to (%d,%d) size [%d,%d]\n",
710 711
                  win_array[i], pos[0].x, pos[0].y, pos[1].x, pos[1].y);

712
            MDI_CalcDefaultChildPos(client, n++, pos, delta, NULL);
713 714 715 716
            SetWindowPos( win_array[i], 0, pos[0].x, pos[0].y, pos[1].x, pos[1].y,
                          SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
        }
    }
717
    HeapFree( GetProcessHeap(), 0, win_array );
718 719

    if (has_icons) ArrangeIconicWindows( client );
Alexandre Julliard's avatar
Alexandre Julliard committed
720 721 722
    return 0;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
723 724 725
/**********************************************************************
 *					MDITile
 */
726
static void MDITile( HWND client, MDICLIENTINFO *ci, WPARAM wParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
727
{
728 729 730
    HWND *win_array;
    int i, total;
    BOOL has_icons = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
731

732
    if (IsZoomed(ci->hwndActiveChild))
733
        SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndActiveChild, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
734

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

737
    if (!(win_array = WIN_ListChildren( client ))) return;
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752

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

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

756
    if (total)
Alexandre Julliard's avatar
Alexandre Julliard committed
757
    {
758 759 760 761
        HWND *pWnd = win_array;
        RECT rect;
        int x, y, xsize, ysize;
        int rows, columns, r, c, i;
Alexandre Julliard's avatar
Alexandre Julliard committed
762

763 764 765
        GetClientRect(client,&rect);
        rows    = (int) sqrt((double)total);
        columns = total / rows;
Alexandre Julliard's avatar
Alexandre Julliard committed
766

767 768 769 770 771 772
        if( wParam & MDITILE_HORIZONTAL )  /* version >= 3.1 */
        {
            i = rows;
            rows = columns;  /* exchange r and c */
            columns = i;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
773

774 775 776 777 778
        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
779

780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
        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++)
            {
                SetWindowPos(*pWnd, 0, x, y, xsize, ysize,
                             SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
                y += ysize;
                pWnd++;
            }
            x += xsize;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
801
    }
802
    HeapFree( GetProcessHeap(), 0, win_array );
803
    if (has_icons) ArrangeIconicWindows( client );
Alexandre Julliard's avatar
Alexandre Julliard committed
804
}
Alexandre Julliard's avatar
Alexandre Julliard committed
805

Alexandre Julliard's avatar
Alexandre Julliard committed
806
/* ----------------------- Frame window ---------------------------- */
Alexandre Julliard's avatar
Alexandre Julliard committed
807

Alexandre Julliard's avatar
Alexandre Julliard committed
808

Alexandre Julliard's avatar
Alexandre Julliard committed
809 810 811
/**********************************************************************
 *					MDI_AugmentFrameMenu
 */
812
static BOOL MDI_AugmentFrameMenu( HWND frame, HWND hChild )
Alexandre Julliard's avatar
Alexandre Julliard committed
813
{
814
    HMENU menu = GetMenu( frame );
815
    HMENU  	hSysPopup = 0;
816 817 818
    HBITMAP hSysMenuBitmap = 0;
    INT nItems;
    UINT iId;
819
    HICON hIcon;
Alexandre Julliard's avatar
Alexandre Julliard committed
820

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

823 824 825 826 827 828
    if( !menu ) return 0;

    /* if the system buttons already exist do not add them again */
    nItems = GetMenuItemCount(menu) - 1;
    iId = GetMenuItemID(menu,nItems) ;
    if (iId == SC_RESTORE || iId == SC_CLOSE)
829
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
830

Alexandre Julliard's avatar
Alexandre Julliard committed
831
    /* create a copy of sysmenu popup and insert it into frame menu bar */
832
    if (!(hSysPopup = GetSystemMenu(hChild, FALSE)))
Alexandre Julliard's avatar
Alexandre Julliard committed
833
	return 0;
834

835 836 837 838 839 840 841
    AppendMenuW(menu, MF_HELP | MF_BITMAP,
                SC_MINIMIZE, (LPCWSTR)HBMMENU_MBAR_MINIMIZE ) ;
    AppendMenuW(menu, MF_HELP | MF_BITMAP,
                SC_RESTORE, (LPCWSTR)HBMMENU_MBAR_RESTORE );
    AppendMenuW(menu, MF_HELP | MF_BITMAP,
                SC_CLOSE, is_close_enabled(hChild, hSysPopup) ?
                (LPCWSTR)HBMMENU_MBAR_CLOSE : (LPCWSTR)HBMMENU_MBAR_CLOSE_D );
842

843
    /* The system menu is replaced by the child icon */
844
    hIcon = (HICON)GetClassLongPtrW(hChild, GCLP_HICONSM);
845
    if (!hIcon)
846
        hIcon = (HICON)GetClassLongPtrW(hChild, GCLP_HICON);
847
    if (!hIcon)
848
        hIcon = LoadImageW(0, MAKEINTRESOURCEW(IDI_WINLOGO), IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR);
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874
    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;
      }
    }

875
    if( !InsertMenuA(menu,0,MF_BYPOSITION | MF_BITMAP | MF_POPUP,
876
                     (UINT_PTR)hSysPopup, (LPSTR)hSysMenuBitmap))
877
    {
878
        TRACE("not inserted\n");
879 880
	DestroyMenu(hSysPopup);
	return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
881
    }
882

883 884 885
    EnableMenuItem(hSysPopup, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
    EnableMenuItem(hSysPopup, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
    EnableMenuItem(hSysPopup, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
886
    SetMenuDefaultItem(hSysPopup, SC_CLOSE, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
887

Alexandre Julliard's avatar
Alexandre Julliard committed
888
    /* redraw menu */
889
    DrawMenuBar(frame);
Alexandre Julliard's avatar
Alexandre Julliard committed
890

Alexandre Julliard's avatar
Alexandre Julliard committed
891
    return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
892 893 894
}

/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
895
 *					MDI_RestoreFrameMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
896
 */
897
static BOOL MDI_RestoreFrameMenu( HWND frame, HWND hChild )
Alexandre Julliard's avatar
Alexandre Julliard committed
898
{
899
    MENUITEMINFOW menuInfo;
900 901 902
    HMENU menu = GetMenu( frame );
    INT nItems = GetMenuItemCount(menu) - 1;
    UINT iId = GetMenuItemID(menu,nItems) ;
Alexandre Julliard's avatar
Alexandre Julliard committed
903

904
    TRACE("frame %p,child %p,nIt=%d,iId=%d\n",frame,hChild,nItems,iId);
Alexandre Julliard's avatar
Alexandre Julliard committed
905

906 907
    if( !menu ) return 0;

908
    /* if there is no system buttons then nothing to do */
909
    if(!(iId == SC_RESTORE || iId == SC_CLOSE) )
910
	return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
911

912 913 914 915
    /*
     * Remove the system menu, If that menu is the icon of the window
     * as it is in win95, we have to delete the bitmap.
     */
916 917
    memset(&menuInfo, 0, sizeof(menuInfo));
    menuInfo.cbSize = sizeof(menuInfo);
918 919
    menuInfo.fMask  = MIIM_DATA | MIIM_TYPE;

920
    GetMenuItemInfoW(menu,
921
		     0,
922 923 924
		     TRUE,
		     &menuInfo);

925
    RemoveMenu(menu,0,MF_BYPOSITION);
926

927 928
    if ( (menuInfo.fType & MFT_BITMAP)           &&
	 (LOWORD(menuInfo.dwTypeData)!=0)        &&
929
	 (LOWORD(menuInfo.dwTypeData)!=HBITMAP_16(hBmpClose)) )
930
    {
931
        DeleteObject(HBITMAP_32(LOWORD(menuInfo.dwTypeData)));
932 933
    }

934
    /* close */
935
    DeleteMenu(menu, SC_CLOSE, MF_BYCOMMAND);
936
    /* restore */
937
    DeleteMenu(menu, SC_RESTORE, MF_BYCOMMAND);
938
    /* minimize */
939
    DeleteMenu(menu, SC_MINIMIZE, MF_BYCOMMAND);
940

941
    DrawMenuBar(frame);
Alexandre Julliard's avatar
Alexandre Julliard committed
942

Alexandre Julliard's avatar
Alexandre Julliard committed
943
    return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
944 945
}

946

Alexandre Julliard's avatar
Alexandre Julliard committed
947
/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
948 949
 *				        MDI_UpdateFrameText
 *
950
 * used when child window is maximized/restored
Alexandre Julliard's avatar
Alexandre Julliard committed
951 952
 *
 * Note: lpTitle can be NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
953
 */
954
static void MDI_UpdateFrameText( HWND frame, HWND hClient, LPCWSTR lpTitle )
Alexandre Julliard's avatar
Alexandre Julliard committed
955
{
956
    WCHAR   lpBuffer[MDI_MAXTITLELENGTH+1];
957
    MDICLIENTINFO *ci = get_client_info( hClient );
Alexandre Julliard's avatar
Alexandre Julliard committed
958

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

961
    if (!ci) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
962

963
    if (!lpTitle && !ci->frameTitle)  /* first time around, get title from the frame window */
964
    {
965 966
        GetWindowTextW( frame, lpBuffer, sizeof(lpBuffer)/sizeof(WCHAR) );
        lpTitle = lpBuffer;
967
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
968

Alexandre Julliard's avatar
Alexandre Julliard committed
969
    /* store new "default" title if lpTitle is not NULL */
970
    if (lpTitle)
Alexandre Julliard's avatar
Alexandre Julliard committed
971
    {
972
	HeapFree( GetProcessHeap(), 0, ci->frameTitle );
973 974
	if ((ci->frameTitle = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpTitle)+1)*sizeof(WCHAR))))
            strcpyW( ci->frameTitle, lpTitle );
Alexandre Julliard's avatar
Alexandre Julliard committed
975 976 977 978
    }

    if (ci->frameTitle)
    {
979
	if (IsZoomed(ci->hwndActiveChild) && IsWindowVisible(ci->hwndActiveChild))
Alexandre Julliard's avatar
Alexandre Julliard committed
980 981 982
	{
	    /* combine frame title and child title if possible */

983 984
	    static const WCHAR lpBracket[]  = {' ','-',' ','[',0};
	    static const WCHAR lpBracket2[]  = {']',0};
985
	    int	i_frame_text_length = strlenW(ci->frameTitle);
Alexandre Julliard's avatar
Alexandre Julliard committed
986

987
	    lstrcpynW( lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH);
Alexandre Julliard's avatar
Alexandre Julliard committed
988 989 990

	    if( i_frame_text_length + 6 < MDI_MAXTITLELENGTH )
            {
991
		strcatW( lpBuffer, lpBracket );
992
                if (GetWindowTextW( ci->hwndActiveChild, lpBuffer + i_frame_text_length + 4,
993 994 995 996 997
                                    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
998 999 1000
	}
	else
	{
1001
            lstrcpynW(lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH+1 );
Alexandre Julliard's avatar
Alexandre Julliard committed
1002 1003 1004 1005 1006
	}
    }
    else
	lpBuffer[0] = '\0';

1007
    DefWindowProcW( frame, WM_SETTEXT, 0, (LPARAM)lpBuffer );
Alexandre Julliard's avatar
Alexandre Julliard committed
1008
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1009 1010


Alexandre Julliard's avatar
Alexandre Julliard committed
1011
/* ----------------------------- Interface ---------------------------- */
Alexandre Julliard's avatar
Alexandre Julliard committed
1012 1013


Alexandre Julliard's avatar
Alexandre Julliard committed
1014
/**********************************************************************
1015
 *		MDIClientWndProc_common
Alexandre Julliard's avatar
Alexandre Julliard committed
1016
 */
1017 1018
static LRESULT MDIClientWndProc_common( HWND hwnd, UINT message,
                                        WPARAM wParam, LPARAM lParam, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
1019
{
1020
    MDICLIENTINFO *ci;
1021

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

1024
    if (!(ci = get_client_info( hwnd ))) return 0;
1025

Alexandre Julliard's avatar
Alexandre Julliard committed
1026 1027 1028
    switch (message)
    {
      case WM_CREATE:
1029 1030 1031 1032
      {
          /* Since we are using only cs->lpCreateParams, we can safely
           * cast to LPCREATESTRUCTA here */
          LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
1033
          WND *wndPtr = WIN_GetPtr( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
1034

1035 1036
          wndPtr->flags |= WIN_ISMDICLIENT;

Alexandre Julliard's avatar
Alexandre Julliard committed
1037 1038 1039
	/* Translation layer doesn't know what's in the cs->lpCreateParams
	 * so we have to keep track of what environment we're in. */

1040
	if( wndPtr->flags & WIN_ISWIN32 )
Alexandre Julliard's avatar
Alexandre Julliard committed
1041
	{
1042
	    LPCLIENTCREATESTRUCT ccs = cs->lpCreateParams;
Alexandre Julliard's avatar
Alexandre Julliard committed
1043 1044 1045
	    ci->hWindowMenu	= ccs->hWindowMenu;
	    ci->idFirstChild	= ccs->idFirstChild;
	}
1046
        else
Alexandre Julliard's avatar
Alexandre Julliard committed
1047
	{
1048
	    LPCLIENTCREATESTRUCT16 ccs = MapSL((SEGPTR)cs->lpCreateParams);
1049
	    ci->hWindowMenu	= HMENU_32(ccs->hWindowMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
1050 1051
	    ci->idFirstChild	= ccs->idFirstChild;
	}
1052
        WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
1053

1054
        ci->child = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1055
	ci->nActiveChildren	= 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1056
	ci->nTotalCreated	= 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1057
	ci->frameTitle		= NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1058
	ci->mdiFlags		= 0;
1059
        ci->hFrameMenu = GetMenu(cs->hwndParent);
Alexandre Julliard's avatar
Alexandre Julliard committed
1060

1061
	if (!hBmpClose) hBmpClose = CreateMDIMenuBitmap();
Alexandre Julliard's avatar
Alexandre Julliard committed
1062

1063
        TRACE("Client created: hwnd %p, Window menu %p, idFirst = %04x\n",
1064
              hwnd, ci->hWindowMenu, ci->idFirstChild );
1065 1066
        return 0;
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1067

Alexandre Julliard's avatar
Alexandre Julliard committed
1068
      case WM_DESTROY:
1069
      {
1070 1071 1072 1073 1074 1075
          if( IsZoomed(ci->hwndActiveChild) )
              MDI_RestoreFrameMenu(GetParent(hwnd), ci->hwndActiveChild);

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

1076 1077
          HeapFree( GetProcessHeap(), 0, ci->child );
          HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1078

1079 1080
          return 0;
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1081 1082

      case WM_MDIACTIVATE:
1083
      {
1084
        MDI_SwitchActiveChild( ci, (HWND)wParam, TRUE );
1085
        return 0;
1086
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1087 1088

      case WM_MDICASCADE:
1089
        return MDICascade(hwnd, ci);
Alexandre Julliard's avatar
Alexandre Julliard committed
1090 1091

      case WM_MDICREATE:
1092
        if (lParam)
1093
        {
1094 1095
            HWND child;

1096 1097 1098
            if (unicode)
            {
                MDICREATESTRUCTW *csW = (MDICREATESTRUCTW *)lParam;
1099
                child = CreateWindowExW(WS_EX_MDICHILD, csW->szClass,
1100 1101 1102 1103 1104 1105 1106 1107
                                            csW->szTitle, csW->style,
                                            csW->x, csW->y, csW->cx, csW->cy,
                                            hwnd, 0, csW->hOwner,
                                            (LPVOID)csW->lParam);
            }
            else
            {
                MDICREATESTRUCTA *csA = (MDICREATESTRUCTA *)lParam;
1108
                child = CreateWindowExA(WS_EX_MDICHILD, csA->szClass,
1109 1110 1111 1112 1113
                                            csA->szTitle, csA->style,
                                            csA->x, csA->y, csA->cx, csA->cy,
                                            hwnd, 0, csA->hOwner,
                                            (LPVOID)csA->lParam);
            }
1114 1115 1116 1117 1118 1119 1120

            if (IsZoomed(ci->hwndActiveChild))
            {
                MDI_AugmentFrameMenu(GetParent(hwnd), child);
                MDI_UpdateFrameText(GetParent(hwnd), hwnd, NULL);
            }
            return (LRESULT)child;
1121
        }
1122
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1123 1124

      case WM_MDIDESTROY:
1125
          return MDIDestroyChild( hwnd, ci, WIN_GetFullHandle( (HWND)wParam ), TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1126 1127

      case WM_MDIGETACTIVE:
1128
          if (lParam) *(BOOL *)lParam = IsZoomed(ci->hwndActiveChild);
1129
          return (LRESULT)ci->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
1130 1131

      case WM_MDIICONARRANGE:
Alexandre Julliard's avatar
Alexandre Julliard committed
1132
	ci->mdiFlags |= MDIF_NEEDUPDATE;
1133
        ArrangeIconicWindows( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
1134
	ci->sbRecalc = SB_BOTH+1;
1135 1136 1137
        SendMessageW( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 );
        return 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
1138
      case WM_MDIMAXIMIZE:
1139
	ShowWindow( (HWND)wParam, SW_MAXIMIZE );
1140
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1141

Alexandre Julliard's avatar
Alexandre Julliard committed
1142
      case WM_MDINEXT: /* lParam != 0 means previous window */
1143 1144
      {
        HWND next = MDI_GetWindow( ci, WIN_GetFullHandle( (HWND)wParam ), !lParam, 0 );
1145
        MDI_SwitchActiveChild( ci, next, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1146
	break;
1147
      }
1148

Alexandre Julliard's avatar
Alexandre Julliard committed
1149
      case WM_MDIRESTORE:
1150 1151
        SendMessageW( (HWND)wParam, WM_SYSCOMMAND, SC_RESTORE, 0);
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1152 1153

      case WM_MDISETMENU:
1154
          return MDISetMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1155

Alexandre Julliard's avatar
Alexandre Julliard committed
1156
      case WM_MDIREFRESHMENU:
1157
          return MDI_RefreshMenu( ci );
Alexandre Julliard's avatar
Alexandre Julliard committed
1158

Alexandre Julliard's avatar
Alexandre Julliard committed
1159
      case WM_MDITILE:
Alexandre Julliard's avatar
Alexandre Julliard committed
1160
	ci->mdiFlags |= MDIF_NEEDUPDATE;
1161 1162
        ShowScrollBar( hwnd, SB_BOTH, FALSE );
        MDITile( hwnd, ci, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1163
        ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1164
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1165 1166 1167

      case WM_VSCROLL:
      case WM_HSCROLL:
Alexandre Julliard's avatar
Alexandre Julliard committed
1168
	ci->mdiFlags |= MDIF_NEEDUPDATE;
1169
        ScrollChildren( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1170
	ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1171
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1172 1173

      case WM_SETFOCUS:
1174 1175 1176 1177
          if (ci->hwndActiveChild && !IsIconic( ci->hwndActiveChild ))
              SetFocus( ci->hwndActiveChild );
          return 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
1178
      case WM_NCACTIVATE:
Alexandre Julliard's avatar
Alexandre Julliard committed
1179
        if( ci->hwndActiveChild )
1180
            SendMessageW(ci->hwndActiveChild, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1181
	break;
1182

Alexandre Julliard's avatar
Alexandre Julliard committed
1183
      case WM_PARENTNOTIFY:
1184
        switch (LOWORD(wParam))
Alexandre Julliard's avatar
Alexandre Julliard committed
1185
        {
1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196
        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);

1197 1198 1199
                TRACE("Adding MDI child %p, # of children %d\n",
                      (HWND)lParam, ci->nActiveChildren);

1200 1201 1202 1203 1204 1205
                ci->child[ci->nActiveChildren - 1] = (HWND)lParam;
            }
            break;

        case WM_LBUTTONDOWN:
            {
1206 1207
            HWND child;
            POINT pt;
1208 1209
            pt.x = (short)LOWORD(lParam);
            pt.y = (short)HIWORD(lParam);
1210
            child = ChildWindowFromPoint(hwnd, pt);
Alexandre Julliard's avatar
Alexandre Julliard committed
1211

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

1214
            if( child && child != hwnd && child != ci->hwndActiveChild )
1215
                SetWindowPos(child, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE );
1216 1217
            break;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1218
        }
1219
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1220 1221

      case WM_SIZE:
1222
        if( IsWindow(ci->hwndActiveChild) && IsZoomed(ci->hwndActiveChild) &&
1223
            (GetWindowLongW(ci->hwndActiveChild, GWL_STYLE) & WS_VISIBLE) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1224
	{
1225 1226 1227 1228 1229 1230
	    RECT	rect;

	    rect.left = 0;
	    rect.top = 0;
	    rect.right = LOWORD(lParam);
	    rect.bottom = HIWORD(lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1231

1232 1233 1234
	    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
1235 1236
			 rect.right - rect.left, rect.bottom - rect.top, 1);
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1237
	else
1238
            MDI_PostUpdate(hwnd, ci, SB_BOTH+1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1239

Alexandre Julliard's avatar
Alexandre Julliard committed
1240 1241
	break;

Alexandre Julliard's avatar
Alexandre Julliard committed
1242
      case WM_MDICALCCHILDSCROLL:
Alexandre Julliard's avatar
Alexandre Julliard committed
1243 1244
	if( (ci->mdiFlags & MDIF_NEEDUPDATE) && ci->sbRecalc )
	{
1245
            CalcChildScroll(hwnd, ci->sbRecalc-1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1246 1247 1248
	    ci->sbRecalc = 0;
	    ci->mdiFlags &= ~MDIF_NEEDUPDATE;
	}
1249
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1250
    }
1251 1252
    return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
                     DefWindowProcA( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1253 1254
}

1255 1256 1257 1258 1259
/***********************************************************************
 *		MDIClientWndProcA
 */
static LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
1260 1261
    if (!IsWindow(hwnd)) return 0;
    return MDIClientWndProc_common( hwnd, message, wParam, lParam, FALSE );
1262 1263 1264 1265 1266 1267 1268
}

/***********************************************************************
 *		MDIClientWndProcW
 */
static LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
1269 1270
    if (!IsWindow(hwnd)) return 0;
    return MDIClientWndProc_common( hwnd, message, wParam, lParam, TRUE );
1271
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1272 1273

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


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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1456 1457
    switch (message)
    {
1458 1459
    case WM_SETTEXT:
        DefWindowProcW(hwnd, message, wParam, lParam);
1460
        if( ci->hwndActiveChild == hwnd && IsZoomed(ci->hwndActiveChild) )
1461
            MDI_UpdateFrameText( GetParent(client), client, NULL );
1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482
        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;

    case WM_CHILDACTIVATE:
        MDI_ChildActivate( client, hwnd );
        return 0;

    case WM_SYSCOMMAND:
        switch( wParam )
        {
        case SC_MOVE:
1483 1484
            if( ci->hwndActiveChild == hwnd && IsZoomed(ci->hwndActiveChild))
                return 0;
1485 1486 1487 1488 1489
            break;
        case SC_RESTORE:
        case SC_MINIMIZE:
            break;
        case SC_MAXIMIZE:
1490
            if (ci->hwndActiveChild == hwnd && IsZoomed(ci->hwndActiveChild))
1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501
                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;

1502
    case WM_SHOWWINDOW:
1503
    case WM_SETVISIBLE:
1504
        if (IsZoomed(ci->hwndActiveChild)) ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1505 1506 1507 1508
        else MDI_PostUpdate(client, ci, SB_BOTH+1);
        break;

    case WM_SIZE:
1509
        if( hwnd == ci->hwndActiveChild )
1510
        {
1511 1512 1513
            if( wParam == SIZE_MAXIMIZED )
            {
                TRACE("maximizing child %p\n", hwnd );
1514

1515 1516 1517 1518
                MDI_AugmentFrameMenu( GetParent(client), hwnd );
            }
            else
                MDI_RestoreFrameMenu( GetParent(client), hwnd );
1519 1520
        }

1521 1522
        MDI_UpdateFrameText( GetParent(client), client, NULL );
        MDI_RefreshMenu(ci);
1523 1524 1525 1526 1527 1528 1529 1530 1531 1532
        MDI_PostUpdate(client, ci, SB_BOTH+1);
        break;

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

            if( wParam == VK_LEFT )  /* switch to frame system menu */
            {
1533
                WND *wndPtr = WIN_GetPtr( parent );
1534
                next_menu->hmenuNext = GetSubMenu( wndPtr->hSysMenu, 0 );
1535
                WIN_ReleasePtr( wndPtr );
1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551
            }
            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;
1552 1553 1554 1555 1556

    case WM_DESTROY:
        /* Remove itself from the Window menu */
        MDI_RefreshMenu(ci);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1557
    }
1558
    return DefWindowProcW(hwnd, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1559 1560
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1561
/**********************************************************************
1562
 *		CreateMDIWindowA (USER32.@) Creates a MDI child
1563 1564 1565 1566
 *
 * RETURNS
 *    Success: Handle to created window
 *    Failure: NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
1567
 */
1568
HWND WINAPI CreateMDIWindowA(
1569 1570 1571
    LPCSTR lpClassName,    /* [in] Pointer to registered child class name */
    LPCSTR lpWindowName,   /* [in] Pointer to window name */
    DWORD dwStyle,         /* [in] Window style */
1572 1573 1574 1575 1576 1577
    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 */
1578
    LPARAM lParam)         /* [in] Application-defined value */
Alexandre Julliard's avatar
Alexandre Julliard committed
1579
{
1580
    TRACE("(%s,%s,%08x,%d,%d,%d,%d,%p,%p,%08lx)\n",
1581 1582
          debugstr_a(lpClassName),debugstr_a(lpWindowName),dwStyle,X,Y,
          nWidth,nHeight,hWndParent,hInstance,lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1583

1584 1585 1586
    return CreateWindowExA(WS_EX_MDICHILD, lpClassName, lpWindowName,
                           dwStyle, X, Y, nWidth, nHeight, hWndParent,
                           0, hInstance, (LPVOID)lParam);
1587 1588
}

1589
/***********************************************************************
1590
 *		CreateMDIWindowW (USER32.@) Creates a MDI child
Alexandre Julliard's avatar
Alexandre Julliard committed
1591 1592 1593 1594
 *
 * RETURNS
 *    Success: Handle to created window
 *    Failure: NULL
Alexandre Julliard's avatar
Alexandre Julliard committed
1595
 */
1596
HWND WINAPI CreateMDIWindowW(
1597 1598 1599
    LPCWSTR lpClassName,    /* [in] Pointer to registered child class name */
    LPCWSTR lpWindowName,   /* [in] Pointer to window name */
    DWORD dwStyle,         /* [in] Window style */
1600 1601 1602 1603 1604 1605
    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 */
1606 1607
    LPARAM lParam)         /* [in] Application-defined value */
{
1608
    TRACE("(%s,%s,%08x,%d,%d,%d,%d,%p,%p,%08lx)\n",
1609 1610
          debugstr_w(lpClassName), debugstr_w(lpWindowName), dwStyle, X, Y,
          nWidth, nHeight, hWndParent, hInstance, lParam);
1611

1612 1613 1614
    return CreateWindowExW(WS_EX_MDICHILD, lpClassName, lpWindowName,
                           dwStyle, X, Y, nWidth, nHeight, hWndParent,
                           0, hInstance, (LPVOID)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1615 1616
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1617
/**********************************************************************
1618
 *		TranslateMDISysAccel (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1619
 */
1620
BOOL WINAPI TranslateMDISysAccel( HWND hwndClient, LPMSG msg )
Alexandre Julliard's avatar
Alexandre Julliard committed
1621
{
1622
    if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN)
Alexandre Julliard's avatar
Alexandre Julliard committed
1623
    {
1624 1625
        MDICLIENTINFO *ci = get_client_info( hwndClient );
        WPARAM wParam = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1626

1627
        if (!ci || !IsWindowEnabled(ci->hwndActiveChild)) return 0;
1628

1629
        /* translate if the Ctrl key is down and Alt not. */
1630

1631 1632 1633 1634 1635 1636 1637 1638 1639 1640
        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:
1641 1642 1643 1644 1645 1646
                if (is_close_enabled(ci->hwndActiveChild, 0))
                {
                    wParam = SC_CLOSE;
                    break;
                }
                /* fall through */
1647 1648 1649 1650 1651 1652 1653
            default:
                return 0;
            }
            TRACE("wParam = %04x\n", wParam);
            SendMessageW(ci->hwndActiveChild, WM_SYSCOMMAND, wParam, (LPARAM)msg->wParam);
            return 1;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
1654 1655
    }
    return 0; /* failure */
Alexandre Julliard's avatar
Alexandre Julliard committed
1656
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1657

1658 1659 1660 1661
/***********************************************************************
 *		CalcChildScroll (USER32.@)
 */
void WINAPI CalcChildScroll( HWND hwnd, INT scroll )
Alexandre Julliard's avatar
Alexandre Julliard committed
1662
{
1663
    SCROLLINFO info;
1664
    RECT childRect, clientRect;
1665
    HWND *list;
Alexandre Julliard's avatar
Alexandre Julliard committed
1666

1667 1668
    GetClientRect( hwnd, &clientRect );
    SetRectEmpty( &childRect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1669

1670
    if ((list = WIN_ListChildren( hwnd )))
Alexandre Julliard's avatar
Alexandre Julliard committed
1671
    {
1672 1673 1674 1675 1676 1677
        int i;
        for (i = 0; list[i]; i++)
        {
            DWORD style = GetWindowLongW( list[i], GWL_STYLE );
            if (style & WS_MAXIMIZE)
            {
1678
                HeapFree( GetProcessHeap(), 0, list );
1679 1680 1681 1682 1683
                ShowScrollBar( hwnd, SB_BOTH, FALSE );
                return;
            }
            if (style & WS_VISIBLE)
            {
1684 1685 1686
                RECT rect;
                GetWindowRect( list[i], &rect );
                UnionRect( &childRect, &rect, &childRect );
1687 1688
            }
        }
1689
        HeapFree( GetProcessHeap(), 0, list );
1690
    }
1691
    MapWindowPoints( 0, hwnd, (POINT *)&childRect, 2 );
1692
    UnionRect( &childRect, &clientRect, &childRect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1693

1694 1695 1696
    /* set common info values */
    info.cbSize = sizeof(info);
    info.fMask = SIF_POS | SIF_RANGE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1697

1698
    /* set the specific */
Alexandre Julliard's avatar
Alexandre Julliard committed
1699
    switch( scroll )
Alexandre Julliard's avatar
Alexandre Julliard committed
1700
    {
1701
	case SB_BOTH:
Alexandre Julliard's avatar
Alexandre Julliard committed
1702
	case SB_HORZ:
1703 1704 1705
			info.nMin = childRect.left;
			info.nMax = childRect.right - clientRect.right;
			info.nPos = clientRect.left - childRect.left;
1706
			SetScrollInfo(hwnd, SB_HORZ, &info, TRUE);
1707 1708
			if (scroll == SB_HORZ) break;
			/* fall through */
Alexandre Julliard's avatar
Alexandre Julliard committed
1709
	case SB_VERT:
1710 1711 1712
			info.nMin = childRect.top;
			info.nMax = childRect.bottom - clientRect.bottom;
			info.nPos = clientRect.top - childRect.top;
1713
			SetScrollInfo(hwnd, SB_VERT, &info, TRUE);
Alexandre Julliard's avatar
Alexandre Julliard committed
1714
			break;
1715
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1716
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1717

Alexandre Julliard's avatar
Alexandre Julliard committed
1718

Alexandre Julliard's avatar
Alexandre Julliard committed
1719
/***********************************************************************
1720
 *		ScrollChildren (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1721
 */
1722
void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam,
Alexandre Julliard's avatar
Alexandre Julliard committed
1723
                             LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1724
{
1725 1726
    INT newPos = -1;
    INT curPos, length, minPos, maxPos, shift;
1727
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1728

1729
    GetClientRect( hWnd, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1730

1731
    switch(uMsg)
Alexandre Julliard's avatar
Alexandre Julliard committed
1732
    {
1733
    case WM_HSCROLL:
1734 1735
	GetScrollRange(hWnd,SB_HORZ,&minPos,&maxPos);
	curPos = GetScrollPos(hWnd,SB_HORZ);
1736
	length = (rect.right - rect.left) / 2;
1737
	shift = GetSystemMetrics(SM_CYHSCROLL);
1738 1739
        break;
    case WM_VSCROLL:
1740 1741
	GetScrollRange(hWnd,SB_VERT,&minPos,&maxPos);
	curPos = GetScrollPos(hWnd,SB_VERT);
1742
	length = (rect.bottom - rect.top) / 2;
1743
	shift = GetSystemMetrics(SM_CXVSCROLL);
1744 1745
        break;
    default:
1746 1747
        return;
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1748

Alexandre Julliard's avatar
Alexandre Julliard committed
1749 1750
    switch( wParam )
    {
1751
	case SB_LINEUP:
Alexandre Julliard's avatar
Alexandre Julliard committed
1752 1753
		        newPos = curPos - shift;
			break;
1754
	case SB_LINEDOWN:
Alexandre Julliard's avatar
Alexandre Julliard committed
1755 1756
			newPos = curPos + shift;
			break;
1757
	case SB_PAGEUP:
Alexandre Julliard's avatar
Alexandre Julliard committed
1758 1759
			newPos = curPos - length;
			break;
1760
	case SB_PAGEDOWN:
Alexandre Julliard's avatar
Alexandre Julliard committed
1761 1762 1763
			newPos = curPos + length;
			break;

1764
	case SB_THUMBPOSITION:
Alexandre Julliard's avatar
Alexandre Julliard committed
1765 1766 1767
			newPos = LOWORD(lParam);
			break;

1768
	case SB_THUMBTRACK:
Alexandre Julliard's avatar
Alexandre Julliard committed
1769 1770
			return;

1771
	case SB_TOP:
Alexandre Julliard's avatar
Alexandre Julliard committed
1772 1773
			newPos = minPos;
			break;
1774
	case SB_BOTTOM:
Alexandre Julliard's avatar
Alexandre Julliard committed
1775 1776 1777
			newPos = maxPos;
			break;
	case SB_ENDSCROLL:
1778
			CalcChildScroll(hWnd,(uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ);
Alexandre Julliard's avatar
Alexandre Julliard committed
1779
			return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1780
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1781

Alexandre Julliard's avatar
Alexandre Julliard committed
1782 1783
    if( newPos > maxPos )
	newPos = maxPos;
1784
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
1785 1786
	if( newPos < minPos )
	    newPos = minPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
1787

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1790
    if( uMsg == WM_VSCROLL )
1791
	ScrollWindowEx(hWnd ,0 ,curPos - newPos, NULL, NULL, 0, NULL,
Alexandre Julliard's avatar
Alexandre Julliard committed
1792
			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
Alexandre Julliard's avatar
Alexandre Julliard committed
1793
    else
1794
	ScrollWindowEx(hWnd ,curPos - newPos, 0, NULL, NULL, 0, NULL,
Alexandre Julliard's avatar
Alexandre Julliard committed
1795
			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
Alexandre Julliard's avatar
Alexandre Julliard committed
1796 1797
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1798 1799

/******************************************************************************
1800
 *		CascadeWindows (USER32.@) Cascades MDI child windows
Alexandre Julliard's avatar
Alexandre Julliard committed
1801 1802 1803 1804 1805 1806
 *
 * RETURNS
 *    Success: Number of cascaded windows.
 *    Failure: 0
 */
WORD WINAPI
1807 1808
CascadeWindows (HWND hwndParent, UINT wFlags, const LPRECT lpRect,
		UINT cKids, const HWND *lpKids)
Alexandre Julliard's avatar
Alexandre Julliard committed
1809
{
1810
    FIXME("(%p,0x%08x,...,%u,...): stub\n", hwndParent, wFlags, cKids);
Alexandre Julliard's avatar
Alexandre Julliard committed
1811 1812 1813 1814 1815
    return 0;
}


/******************************************************************************
1816
 *		TileWindows (USER32.@) Tiles MDI child windows
Alexandre Julliard's avatar
Alexandre Julliard committed
1817 1818 1819 1820 1821 1822
 *
 * RETURNS
 *    Success: Number of tiled windows.
 *    Failure: 0
 */
WORD WINAPI
1823 1824
TileWindows (HWND hwndParent, UINT wFlags, const LPRECT lpRect,
	     UINT cKids, const HWND *lpKids)
Alexandre Julliard's avatar
Alexandre Julliard committed
1825
{
1826
    FIXME("(%p,0x%08x,...,%u,...): stub\n", hwndParent, wFlags, cKids);
Alexandre Julliard's avatar
Alexandre Julliard committed
1827 1828 1829
    return 0;
}

1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842
/************************************************************************
 *              "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...
 *
 */

1843
static INT_PTR WINAPI MDI_MoreWindowsDlgProc (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
1844 1845 1846 1847 1848 1849 1850 1851
{
    switch (iMsg)
    {
       case WM_INITDIALOG:
       {
           UINT widest       = 0;
           UINT length;
           UINT i;
1852
           MDICLIENTINFO *ci = get_client_info( (HWND)lParam );
1853 1854
           HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX);

1855 1856
           for (i = 0; i < ci->nActiveChildren; i++)
           {
1857
               WCHAR buffer[MDI_MAXTITLELENGTH];
1858

1859
               if (!InternalGetWindowText( ci->child[i], buffer, sizeof(buffer)/sizeof(WCHAR) ))
1860 1861
                   continue;
               SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)buffer );
1862
               SendMessageW(hListBox, LB_SETITEMDATA, i, (LPARAM)ci->child[i] );
1863
               length = strlenW(buffer);  /* FIXME: should use GetTextExtentPoint */
1864 1865 1866 1867
               if (length > widest)
                   widest = length;
           }
           /* Make sure the horizontal scrollbar scrolls ok */
1868
           SendMessageW(hListBox, LB_SETHORIZONTALEXTENT, widest * 6, 0);
1869 1870

           /* Set the current selection */
1871
           SendMessageW(hListBox, LB_SETCURSEL, MDI_MOREWINDOWSLIMIT, 0);
1872 1873 1874 1875 1876 1877
           return TRUE;
       }

       case WM_COMMAND:
           switch (LOWORD(wParam))
           {
1878 1879 1880
                default:
                    if (HIWORD(wParam) != LBN_DBLCLK) break;
                    /* fall through */
1881 1882 1883 1884 1885 1886
                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);
1887
                    UINT index        = SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
1888 1889
                    LRESULT res = SendMessageW(hListBox, LB_GETITEMDATA, index, 0);
                    EndDialog(hDlg, res);
1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911
                    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.
 */

1912
static HWND MDI_MoreWindowsDialog(HWND hwnd)
1913 1914 1915 1916 1917
{
    LPCVOID template;
    HRSRC hRes;
    HANDLE hDlgTmpl;

1918
    hRes = FindResourceA(user32_module, "MDI_MOREWINDOWS", (LPSTR)RT_DIALOG);
1919 1920 1921 1922

    if (hRes == 0)
        return 0;

1923
    hDlgTmpl = LoadResource(user32_module, hRes );
1924 1925 1926 1927 1928 1929 1930 1931 1932

    if (hDlgTmpl == 0)
        return 0;

    template = LockResource( hDlgTmpl );

    if (template == 0)
        return 0;

1933
    return (HWND) DialogBoxIndirectParamA(user32_module,
1934
                                          (const DLGTEMPLATE*) template,
1935
                                          hwnd, MDI_MoreWindowsDlgProc, (LPARAM) hwnd);
1936
}