mdi.c 59.2 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 (%ld,%ld - %ld, %ld)\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
        SetFocus( client );
622
    }
623 624

    SendMessageW( child, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child );
625
    return TRUE;
Alexandre Julliard's avatar
Alexandre Julliard committed
626
}
Alexandre Julliard's avatar
Alexandre Julliard committed
627

Alexandre Julliard's avatar
Alexandre Julliard committed
628 629 630 631 632
/* -------------------- MDI client window functions ------------------- */

/**********************************************************************
 *				CreateMDIMenuBitmap
 */
633
static HBITMAP CreateMDIMenuBitmap(void)
Alexandre Julliard's avatar
Alexandre Julliard committed
634
{
635 636
 HDC 		hDCSrc  = CreateCompatibleDC(0);
 HDC		hDCDest	= CreateCompatibleDC(hDCSrc);
637
 HBITMAP	hbClose = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_OLD_CLOSE) );
638 639
 HBITMAP	hbCopy;
 HBITMAP	hobjSrc, hobjDest;
Alexandre Julliard's avatar
Alexandre Julliard committed
640

641
 hobjSrc = SelectObject(hDCSrc, hbClose);
642
 hbCopy = CreateCompatibleBitmap(hDCSrc,GetSystemMetrics(SM_CXSIZE),GetSystemMetrics(SM_CYSIZE));
643
 hobjDest = SelectObject(hDCDest, hbCopy);
Alexandre Julliard's avatar
Alexandre Julliard committed
644

645 646
 BitBlt(hDCDest, 0, 0, GetSystemMetrics(SM_CXSIZE), GetSystemMetrics(SM_CYSIZE),
          hDCSrc, GetSystemMetrics(SM_CXSIZE), 0, SRCCOPY);
647

648 649 650
 SelectObject(hDCSrc, hobjSrc);
 DeleteObject(hbClose);
 DeleteDC(hDCSrc);
Alexandre Julliard's avatar
Alexandre Julliard committed
651

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

654 655
 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
656

657 658 659
 SelectObject(hDCDest, hobjSrc );
 SelectObject(hDCDest, hobjDest);
 DeleteDC(hDCDest);
Alexandre Julliard's avatar
Alexandre Julliard committed
660 661 662 663

 return hbCopy;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
664
/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
665
 *				MDICascade
Alexandre Julliard's avatar
Alexandre Julliard committed
666
 */
667
static LONG MDICascade( HWND client, MDICLIENTINFO *ci )
Alexandre Julliard's avatar
Alexandre Julliard committed
668
{
669 670 671 672
    HWND *win_array;
    BOOL has_icons = FALSE;
    int i, total;

673
    if (IsZoomed(ci->hwndActiveChild))
674
        SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndActiveChild, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
675

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

678
    if (!(win_array = WIN_ListChildren( client ))) return 0;
679 680 681

    /* remove all the windows we don't want */
    for (i = total = 0; win_array[i]; i++)
Alexandre Julliard's avatar
Alexandre Julliard committed
682
    {
683 684 685 686 687 688 689 690
        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
691
    }
692
    win_array[total] = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
693

694 695 696 697 698 699 700 701 702
    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--)
        {
703
            TRACE("move %p to (%ld,%ld) size [%ld,%ld]\n",
704 705
                  win_array[i], pos[0].x, pos[0].y, pos[1].x, pos[1].y);

706
            MDI_CalcDefaultChildPos(client, n++, pos, delta, NULL);
707 708 709 710
            SetWindowPos( win_array[i], 0, pos[0].x, pos[0].y, pos[1].x, pos[1].y,
                          SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
        }
    }
711
    HeapFree( GetProcessHeap(), 0, win_array );
712 713

    if (has_icons) ArrangeIconicWindows( client );
Alexandre Julliard's avatar
Alexandre Julliard committed
714 715 716
    return 0;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
717 718 719
/**********************************************************************
 *					MDITile
 */
720
static void MDITile( HWND client, MDICLIENTINFO *ci, WPARAM wParam )
Alexandre Julliard's avatar
Alexandre Julliard committed
721
{
722 723 724
    HWND *win_array;
    int i, total;
    BOOL has_icons = FALSE;
Alexandre Julliard's avatar
Alexandre Julliard committed
725

726
    if (IsZoomed(ci->hwndActiveChild))
727
        SendMessageW(client, WM_MDIRESTORE, (WPARAM)ci->hwndActiveChild, 0);
Alexandre Julliard's avatar
Alexandre Julliard committed
728

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

731
    if (!(win_array = WIN_ListChildren( client ))) return;
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746

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

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

750
    if (total)
Alexandre Julliard's avatar
Alexandre Julliard committed
751
    {
752 753 754 755
        HWND *pWnd = win_array;
        RECT rect;
        int x, y, xsize, ysize;
        int rows, columns, r, c, i;
Alexandre Julliard's avatar
Alexandre Julliard committed
756

757 758 759
        GetClientRect(client,&rect);
        rows    = (int) sqrt((double)total);
        columns = total / rows;
Alexandre Julliard's avatar
Alexandre Julliard committed
760

761 762 763 764 765 766
        if( wParam & MDITILE_HORIZONTAL )  /* version >= 3.1 */
        {
            i = rows;
            rows = columns;  /* exchange r and c */
            columns = i;
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
767

768 769 770 771 772
        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
773

774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
        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
795
    }
796
    HeapFree( GetProcessHeap(), 0, win_array );
797
    if (has_icons) ArrangeIconicWindows( client );
Alexandre Julliard's avatar
Alexandre Julliard committed
798
}
Alexandre Julliard's avatar
Alexandre Julliard committed
799

Alexandre Julliard's avatar
Alexandre Julliard committed
800
/* ----------------------- Frame window ---------------------------- */
Alexandre Julliard's avatar
Alexandre Julliard committed
801

Alexandre Julliard's avatar
Alexandre Julliard committed
802

Alexandre Julliard's avatar
Alexandre Julliard committed
803 804 805
/**********************************************************************
 *					MDI_AugmentFrameMenu
 */
806
static BOOL MDI_AugmentFrameMenu( HWND frame, HWND hChild )
Alexandre Julliard's avatar
Alexandre Julliard committed
807
{
808
    HMENU menu = GetMenu( frame );
809
    HMENU  	hSysPopup = 0;
810 811 812
    HBITMAP hSysMenuBitmap = 0;
    INT nItems;
    UINT iId;
813
    HICON hIcon;
Alexandre Julliard's avatar
Alexandre Julliard committed
814

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

817 818 819 820 821 822
    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)
823
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
824

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

829 830 831 832 833 834 835
    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 );
836

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

869
    if( !InsertMenuA(menu,0,MF_BYPOSITION | MF_BITMAP | MF_POPUP,
870
                     (UINT_PTR)hSysPopup, (LPSTR)hSysMenuBitmap))
871
    {
872
        TRACE("not inserted\n");
873 874
	DestroyMenu(hSysPopup);
	return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
875
    }
876

877 878 879
    EnableMenuItem(hSysPopup, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
    EnableMenuItem(hSysPopup, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
    EnableMenuItem(hSysPopup, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
880
    SetMenuDefaultItem(hSysPopup, SC_CLOSE, FALSE);
Alexandre Julliard's avatar
Alexandre Julliard committed
881

Alexandre Julliard's avatar
Alexandre Julliard committed
882
    /* redraw menu */
883
    DrawMenuBar(frame);
Alexandre Julliard's avatar
Alexandre Julliard committed
884

Alexandre Julliard's avatar
Alexandre Julliard committed
885
    return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
886 887 888
}

/**********************************************************************
Alexandre Julliard's avatar
Alexandre Julliard committed
889
 *					MDI_RestoreFrameMenu
Alexandre Julliard's avatar
Alexandre Julliard committed
890
 */
891
static BOOL MDI_RestoreFrameMenu( HWND frame, HWND hChild )
Alexandre Julliard's avatar
Alexandre Julliard committed
892
{
893
    MENUITEMINFOW menuInfo;
894 895 896
    HMENU menu = GetMenu( frame );
    INT nItems = GetMenuItemCount(menu) - 1;
    UINT iId = GetMenuItemID(menu,nItems) ;
Alexandre Julliard's avatar
Alexandre Julliard committed
897

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

900 901
    if( !menu ) return 0;

902
    /* if there is no system buttons then nothing to do */
903
    if(!(iId == SC_RESTORE || iId == SC_CLOSE) )
904
	return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
905

906 907 908 909
    /*
     * Remove the system menu, If that menu is the icon of the window
     * as it is in win95, we have to delete the bitmap.
     */
910 911
    memset(&menuInfo, 0, sizeof(menuInfo));
    menuInfo.cbSize = sizeof(menuInfo);
912 913
    menuInfo.fMask  = MIIM_DATA | MIIM_TYPE;

914
    GetMenuItemInfoW(menu,
915
		     0,
916 917 918
		     TRUE,
		     &menuInfo);

919
    RemoveMenu(menu,0,MF_BYPOSITION);
920

921 922
    if ( (menuInfo.fType & MFT_BITMAP)           &&
	 (LOWORD(menuInfo.dwTypeData)!=0)        &&
923
	 (LOWORD(menuInfo.dwTypeData)!=HBITMAP_16(hBmpClose)) )
924
    {
925
        DeleteObject(HBITMAP_32(LOWORD(menuInfo.dwTypeData)));
926 927
    }

928
    /* close */
929
    DeleteMenu(menu, SC_CLOSE, MF_BYCOMMAND);
930
    /* restore */
931
    DeleteMenu(menu, SC_RESTORE, MF_BYCOMMAND);
932
    /* minimize */
933
    DeleteMenu(menu, SC_MINIMIZE, MF_BYCOMMAND);
934

935
    DrawMenuBar(frame);
Alexandre Julliard's avatar
Alexandre Julliard committed
936

Alexandre Julliard's avatar
Alexandre Julliard committed
937
    return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
938 939
}

940

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

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

955
    if (!ci) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
956

957
    if (!lpTitle && !ci->frameTitle)  /* first time around, get title from the frame window */
958
    {
959 960
        GetWindowTextW( frame, lpBuffer, sizeof(lpBuffer)/sizeof(WCHAR) );
        lpTitle = lpBuffer;
961
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
962

Alexandre Julliard's avatar
Alexandre Julliard committed
963
    /* store new "default" title if lpTitle is not NULL */
964
    if (lpTitle)
Alexandre Julliard's avatar
Alexandre Julliard committed
965
    {
966
	HeapFree( GetProcessHeap(), 0, ci->frameTitle );
967 968
	if ((ci->frameTitle = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpTitle)+1)*sizeof(WCHAR))))
            strcpyW( ci->frameTitle, lpTitle );
Alexandre Julliard's avatar
Alexandre Julliard committed
969 970 971 972
    }

    if (ci->frameTitle)
    {
973
	if (IsZoomed(ci->hwndActiveChild) && IsWindowVisible(ci->hwndActiveChild))
Alexandre Julliard's avatar
Alexandre Julliard committed
974 975 976
	{
	    /* combine frame title and child title if possible */

977 978
	    static const WCHAR lpBracket[]  = {' ','-',' ','[',0};
	    static const WCHAR lpBracket2[]  = {']',0};
979
	    int	i_frame_text_length = strlenW(ci->frameTitle);
Alexandre Julliard's avatar
Alexandre Julliard committed
980

981
	    lstrcpynW( lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH);
Alexandre Julliard's avatar
Alexandre Julliard committed
982 983 984

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

1001
    DefWindowProcW( frame, WM_SETTEXT, 0, (LPARAM)lpBuffer );
Alexandre Julliard's avatar
Alexandre Julliard committed
1002
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1003 1004


Alexandre Julliard's avatar
Alexandre Julliard committed
1005
/* ----------------------------- Interface ---------------------------- */
Alexandre Julliard's avatar
Alexandre Julliard committed
1006 1007


Alexandre Julliard's avatar
Alexandre Julliard committed
1008
/**********************************************************************
1009
 *		MDIClientWndProc_common
Alexandre Julliard's avatar
Alexandre Julliard committed
1010
 */
1011 1012
static LRESULT MDIClientWndProc_common( HWND hwnd, UINT message,
                                        WPARAM wParam, LPARAM lParam, BOOL unicode )
Alexandre Julliard's avatar
Alexandre Julliard committed
1013
{
1014
    MDICLIENTINFO *ci;
1015

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

1018
    if (!(ci = get_client_info( hwnd ))) return 0;
1019

Alexandre Julliard's avatar
Alexandre Julliard committed
1020 1021 1022
    switch (message)
    {
      case WM_CREATE:
1023 1024 1025 1026
      {
          /* Since we are using only cs->lpCreateParams, we can safely
           * cast to LPCREATESTRUCTA here */
          LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
1027
          WND *wndPtr = WIN_GetPtr( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
1028

1029 1030
          wndPtr->flags |= WIN_ISMDICLIENT;

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

1034
	if( wndPtr->flags & WIN_ISWIN32 )
Alexandre Julliard's avatar
Alexandre Julliard committed
1035
	{
1036
	    LPCLIENTCREATESTRUCT ccs = cs->lpCreateParams;
Alexandre Julliard's avatar
Alexandre Julliard committed
1037 1038 1039
	    ci->hWindowMenu	= ccs->hWindowMenu;
	    ci->idFirstChild	= ccs->idFirstChild;
	}
1040
        else
Alexandre Julliard's avatar
Alexandre Julliard committed
1041
	{
1042
	    LPCLIENTCREATESTRUCT16 ccs = MapSL((SEGPTR)cs->lpCreateParams);
1043
	    ci->hWindowMenu	= HMENU_32(ccs->hWindowMenu);
Alexandre Julliard's avatar
Alexandre Julliard committed
1044 1045
	    ci->idFirstChild	= ccs->idFirstChild;
	}
1046
        WIN_ReleasePtr( wndPtr );
Alexandre Julliard's avatar
Alexandre Julliard committed
1047

1048
        ci->child = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1049
	ci->nActiveChildren	= 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1050
	ci->nTotalCreated	= 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1051
	ci->frameTitle		= NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
1052
	ci->mdiFlags		= 0;
1053
        ci->hFrameMenu = GetMenu(cs->hwndParent);
Alexandre Julliard's avatar
Alexandre Julliard committed
1054

1055
	if (!hBmpClose) hBmpClose = CreateMDIMenuBitmap();
Alexandre Julliard's avatar
Alexandre Julliard committed
1056

1057
        TRACE("Client created: hwnd %p, Window menu %p, idFirst = %04x\n",
1058
              hwnd, ci->hWindowMenu, ci->idFirstChild );
1059 1060
        return 0;
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1061

Alexandre Julliard's avatar
Alexandre Julliard committed
1062
      case WM_DESTROY:
1063
      {
1064 1065 1066 1067 1068 1069
          if( IsZoomed(ci->hwndActiveChild) )
              MDI_RestoreFrameMenu(GetParent(hwnd), ci->hwndActiveChild);

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

1070 1071
          HeapFree( GetProcessHeap(), 0, ci->child );
          HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1072

1073 1074
          return 0;
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1075 1076

      case WM_MDIACTIVATE:
1077
      {
1078
        MDI_SwitchActiveChild( ci, (HWND)wParam, TRUE );
1079
        return 0;
1080
      }
Alexandre Julliard's avatar
Alexandre Julliard committed
1081 1082

      case WM_MDICASCADE:
1083
        return MDICascade(hwnd, ci);
Alexandre Julliard's avatar
Alexandre Julliard committed
1084 1085

      case WM_MDICREATE:
1086
        if (lParam)
1087
        {
1088 1089
            HWND child;

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

            if (IsZoomed(ci->hwndActiveChild))
            {
                MDI_AugmentFrameMenu(GetParent(hwnd), child);
                MDI_UpdateFrameText(GetParent(hwnd), hwnd, NULL);
            }
            return (LRESULT)child;
1115
        }
1116
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1117 1118

      case WM_MDIDESTROY:
1119
          return MDIDestroyChild( hwnd, ci, WIN_GetFullHandle( (HWND)wParam ), TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1120 1121

      case WM_MDIGETACTIVE:
1122
          if (lParam) *(BOOL *)lParam = IsZoomed(ci->hwndActiveChild);
1123
          return (LRESULT)ci->hwndActiveChild;
Alexandre Julliard's avatar
Alexandre Julliard committed
1124 1125

      case WM_MDIICONARRANGE:
Alexandre Julliard's avatar
Alexandre Julliard committed
1126
	ci->mdiFlags |= MDIF_NEEDUPDATE;
1127
        ArrangeIconicWindows( hwnd );
Alexandre Julliard's avatar
Alexandre Julliard committed
1128
	ci->sbRecalc = SB_BOTH+1;
1129 1130 1131
        SendMessageW( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 );
        return 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
1132
      case WM_MDIMAXIMIZE:
1133
	ShowWindow( (HWND)wParam, SW_MAXIMIZE );
1134
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1135

Alexandre Julliard's avatar
Alexandre Julliard committed
1136
      case WM_MDINEXT: /* lParam != 0 means previous window */
1137 1138
      {
        HWND next = MDI_GetWindow( ci, WIN_GetFullHandle( (HWND)wParam ), !lParam, 0 );
1139
        MDI_SwitchActiveChild( ci, next, TRUE );
Alexandre Julliard's avatar
Alexandre Julliard committed
1140
	break;
1141
      }
1142

Alexandre Julliard's avatar
Alexandre Julliard committed
1143
      case WM_MDIRESTORE:
1144 1145
        SendMessageW( (HWND)wParam, WM_SYSCOMMAND, SC_RESTORE, 0);
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1146 1147

      case WM_MDISETMENU:
1148
          return MDISetMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1149

Alexandre Julliard's avatar
Alexandre Julliard committed
1150
      case WM_MDIREFRESHMENU:
1151
          return MDI_RefreshMenu( ci );
Alexandre Julliard's avatar
Alexandre Julliard committed
1152

Alexandre Julliard's avatar
Alexandre Julliard committed
1153
      case WM_MDITILE:
Alexandre Julliard's avatar
Alexandre Julliard committed
1154
	ci->mdiFlags |= MDIF_NEEDUPDATE;
1155 1156
        ShowScrollBar( hwnd, SB_BOTH, FALSE );
        MDITile( hwnd, ci, wParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1157
        ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1158
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1159 1160 1161

      case WM_VSCROLL:
      case WM_HSCROLL:
Alexandre Julliard's avatar
Alexandre Julliard committed
1162
	ci->mdiFlags |= MDIF_NEEDUPDATE;
1163
        ScrollChildren( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1164
	ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1165
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1166 1167

      case WM_SETFOCUS:
1168 1169 1170 1171
          if (ci->hwndActiveChild && !IsIconic( ci->hwndActiveChild ))
              SetFocus( ci->hwndActiveChild );
          return 0;

Alexandre Julliard's avatar
Alexandre Julliard committed
1172
      case WM_NCACTIVATE:
Alexandre Julliard's avatar
Alexandre Julliard committed
1173
        if( ci->hwndActiveChild )
1174
            SendMessageW(ci->hwndActiveChild, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1175
	break;
1176

Alexandre Julliard's avatar
Alexandre Julliard committed
1177
      case WM_PARENTNOTIFY:
1178
        switch (LOWORD(wParam))
Alexandre Julliard's avatar
Alexandre Julliard committed
1179
        {
1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190
        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);

1191 1192 1193
                TRACE("Adding MDI child %p, # of children %d\n",
                      (HWND)lParam, ci->nActiveChildren);

1194 1195 1196 1197 1198 1199
                ci->child[ci->nActiveChildren - 1] = (HWND)lParam;
            }
            break;

        case WM_LBUTTONDOWN:
            {
1200 1201
            HWND child;
            POINT pt;
1202 1203
            pt.x = (short)LOWORD(lParam);
            pt.y = (short)HIWORD(lParam);
1204
            child = ChildWindowFromPoint(hwnd, pt);
Alexandre Julliard's avatar
Alexandre Julliard committed
1205

1206
	    TRACE("notification from %p (%li,%li)\n",child,pt.x,pt.y);
Alexandre Julliard's avatar
Alexandre Julliard committed
1207

1208
            if( child && child != hwnd && child != ci->hwndActiveChild )
1209
                SetWindowPos(child, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE );
1210 1211
            break;
            }
Alexandre Julliard's avatar
Alexandre Julliard committed
1212
        }
1213
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1214 1215

      case WM_SIZE:
1216
        if( IsWindow(ci->hwndActiveChild) && IsZoomed(ci->hwndActiveChild) &&
1217
            (GetWindowLongW(ci->hwndActiveChild, GWL_STYLE) & WS_VISIBLE) )
Alexandre Julliard's avatar
Alexandre Julliard committed
1218
	{
1219 1220 1221 1222 1223 1224
	    RECT	rect;

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

1226 1227 1228
	    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
1229 1230
			 rect.right - rect.left, rect.bottom - rect.top, 1);
	}
Alexandre Julliard's avatar
Alexandre Julliard committed
1231
	else
1232
            MDI_PostUpdate(hwnd, ci, SB_BOTH+1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1233

Alexandre Julliard's avatar
Alexandre Julliard committed
1234 1235
	break;

Alexandre Julliard's avatar
Alexandre Julliard committed
1236
      case WM_MDICALCCHILDSCROLL:
Alexandre Julliard's avatar
Alexandre Julliard committed
1237 1238
	if( (ci->mdiFlags & MDIF_NEEDUPDATE) && ci->sbRecalc )
	{
1239
            CalcChildScroll(hwnd, ci->sbRecalc-1);
Alexandre Julliard's avatar
Alexandre Julliard committed
1240 1241 1242
	    ci->sbRecalc = 0;
	    ci->mdiFlags &= ~MDIF_NEEDUPDATE;
	}
1243
        return 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1244
    }
1245 1246
    return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
                     DefWindowProcA( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1247 1248
}

1249 1250 1251 1252 1253
/***********************************************************************
 *		MDIClientWndProcA
 */
static LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
1254 1255
    if (!IsWindow(hwnd)) return 0;
    return MDIClientWndProc_common( hwnd, message, wParam, lParam, FALSE );
1256 1257 1258 1259 1260 1261 1262
}

/***********************************************************************
 *		MDIClientWndProcW
 */
static LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
1263 1264
    if (!IsWindow(hwnd)) return 0;
    return MDIClientWndProc_common( hwnd, message, wParam, lParam, TRUE );
1265
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1266 1267

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


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

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

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

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

1352
        case WM_NCACTIVATE:
1353
	    SendMessageW(hwndMDIClient, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1354 1355
	    break;

1356
        case WM_SETTEXT:
1357
            MDI_UpdateFrameText( hwnd, hwndMDIClient, (LPWSTR)lParam );
1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371
	    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;

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

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

1393
    return DefWindowProcW( hwnd, message, wParam, lParam );
Alexandre Julliard's avatar
Alexandre Julliard committed
1394 1395 1396
}

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

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

1407
    hwnd = WIN_GetFullHandle( hwnd );
1408
    if (!ci) return DefWindowProcA( hwnd, message, wParam, lParam );
1409

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


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

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

1447
    hwnd = WIN_GetFullHandle( hwnd );
1448
    if (!ci) return DefWindowProcW( hwnd, message, wParam, lParam );
1449

Alexandre Julliard's avatar
Alexandre Julliard committed
1450 1451
    switch (message)
    {
1452 1453
    case WM_SETTEXT:
        DefWindowProcW(hwnd, message, wParam, lParam);
1454
        if( ci->hwndActiveChild == hwnd && IsZoomed(ci->hwndActiveChild) )
1455
            MDI_UpdateFrameText( GetParent(client), client, NULL );
1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476
        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:
1477 1478
            if( ci->hwndActiveChild == hwnd && IsZoomed(ci->hwndActiveChild))
                return 0;
1479 1480 1481 1482 1483
            break;
        case SC_RESTORE:
        case SC_MINIMIZE:
            break;
        case SC_MAXIMIZE:
1484
            if (ci->hwndActiveChild == hwnd && IsZoomed(ci->hwndActiveChild))
1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495
                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;

1496
    case WM_SHOWWINDOW:
1497
    case WM_SETVISIBLE:
1498
        if (IsZoomed(ci->hwndActiveChild)) ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1499 1500 1501 1502
        else MDI_PostUpdate(client, ci, SB_BOTH+1);
        break;

    case WM_SIZE:
1503
        if( hwnd == ci->hwndActiveChild )
1504
        {
1505 1506 1507
            if( wParam == SIZE_MAXIMIZED )
            {
                TRACE("maximizing child %p\n", hwnd );
1508

1509 1510 1511 1512
                MDI_AugmentFrameMenu( GetParent(client), hwnd );
            }
            else
                MDI_RestoreFrameMenu( GetParent(client), hwnd );
1513 1514
        }

1515 1516
        MDI_UpdateFrameText( GetParent(client), client, NULL );
        MDI_RefreshMenu(ci);
1517 1518 1519 1520 1521 1522 1523 1524 1525 1526
        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 */
            {
1527
                WND *wndPtr = WIN_GetPtr( parent );
1528
                next_menu->hmenuNext = GetSubMenu( wndPtr->hSysMenu, 0 );
1529
                WIN_ReleasePtr( wndPtr );
1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545
            }
            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;
1546 1547 1548 1549 1550

    case WM_DESTROY:
        /* Remove itself from the Window menu */
        MDI_RefreshMenu(ci);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
1551
    }
1552
    return DefWindowProcW(hwnd, message, wParam, lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1553 1554
}

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

1578 1579 1580
    return CreateWindowExA(WS_EX_MDICHILD, lpClassName, lpWindowName,
                           dwStyle, X, Y, nWidth, nHeight, hWndParent,
                           0, hInstance, (LPVOID)lParam);
1581 1582
}

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

1606 1607 1608
    return CreateWindowExW(WS_EX_MDICHILD, lpClassName, lpWindowName,
                           dwStyle, X, Y, nWidth, nHeight, hWndParent,
                           0, hInstance, (LPVOID)lParam);
Alexandre Julliard's avatar
Alexandre Julliard committed
1609 1610
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1611
/**********************************************************************
1612
 *		TranslateMDISysAccel (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1613
 */
1614
BOOL WINAPI TranslateMDISysAccel( HWND hwndClient, LPMSG msg )
Alexandre Julliard's avatar
Alexandre Julliard committed
1615
{
1616
    if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN)
Alexandre Julliard's avatar
Alexandre Julliard committed
1617
    {
1618 1619
        MDICLIENTINFO *ci = get_client_info( hwndClient );
        WPARAM wParam = 0;
Alexandre Julliard's avatar
Alexandre Julliard committed
1620

1621
        if (!ci || !IsWindowEnabled(ci->hwndActiveChild)) return 0;
1622

1623
        /* translate if the Ctrl key is down and Alt not. */
1624

1625 1626 1627 1628 1629 1630 1631 1632 1633 1634
        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:
1635 1636 1637 1638 1639 1640
                if (is_close_enabled(ci->hwndActiveChild, 0))
                {
                    wParam = SC_CLOSE;
                    break;
                }
                /* fall through */
1641 1642 1643 1644 1645 1646 1647
            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
1648 1649
    }
    return 0; /* failure */
Alexandre Julliard's avatar
Alexandre Julliard committed
1650
}
Alexandre Julliard's avatar
Alexandre Julliard committed
1651

1652 1653 1654 1655
/***********************************************************************
 *		CalcChildScroll (USER32.@)
 */
void WINAPI CalcChildScroll( HWND hwnd, INT scroll )
Alexandre Julliard's avatar
Alexandre Julliard committed
1656
{
1657
    SCROLLINFO info;
1658
    RECT childRect, clientRect;
1659
    HWND *list;
Alexandre Julliard's avatar
Alexandre Julliard committed
1660

1661 1662
    GetClientRect( hwnd, &clientRect );
    SetRectEmpty( &childRect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1663

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

1688 1689 1690
    /* set common info values */
    info.cbSize = sizeof(info);
    info.fMask = SIF_POS | SIF_RANGE;
Alexandre Julliard's avatar
Alexandre Julliard committed
1691

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1712

Alexandre Julliard's avatar
Alexandre Julliard committed
1713
/***********************************************************************
1714
 *		ScrollChildren (USER32.@)
Alexandre Julliard's avatar
Alexandre Julliard committed
1715
 */
1716
void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam,
Alexandre Julliard's avatar
Alexandre Julliard committed
1717
                             LPARAM lParam)
Alexandre Julliard's avatar
Alexandre Julliard committed
1718
{
1719 1720
    INT newPos = -1;
    INT curPos, length, minPos, maxPos, shift;
1721
    RECT rect;
Alexandre Julliard's avatar
Alexandre Julliard committed
1722

1723
    GetClientRect( hWnd, &rect );
Alexandre Julliard's avatar
Alexandre Julliard committed
1724

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1743 1744
    switch( wParam )
    {
1745
	case SB_LINEUP:
Alexandre Julliard's avatar
Alexandre Julliard committed
1746 1747
		        newPos = curPos - shift;
			break;
1748
	case SB_LINEDOWN:
Alexandre Julliard's avatar
Alexandre Julliard committed
1749 1750
			newPos = curPos + shift;
			break;
1751
	case SB_PAGEUP:
Alexandre Julliard's avatar
Alexandre Julliard committed
1752 1753
			newPos = curPos - length;
			break;
1754
	case SB_PAGEDOWN:
Alexandre Julliard's avatar
Alexandre Julliard committed
1755 1756 1757
			newPos = curPos + length;
			break;

1758
	case SB_THUMBPOSITION:
Alexandre Julliard's avatar
Alexandre Julliard committed
1759 1760 1761
			newPos = LOWORD(lParam);
			break;

1762
	case SB_THUMBTRACK:
Alexandre Julliard's avatar
Alexandre Julliard committed
1763 1764
			return;

1765
	case SB_TOP:
Alexandre Julliard's avatar
Alexandre Julliard committed
1766 1767
			newPos = minPos;
			break;
1768
	case SB_BOTTOM:
Alexandre Julliard's avatar
Alexandre Julliard committed
1769 1770 1771
			newPos = maxPos;
			break;
	case SB_ENDSCROLL:
1772
			CalcChildScroll(hWnd,(uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ);
Alexandre Julliard's avatar
Alexandre Julliard committed
1773
			return;
Alexandre Julliard's avatar
Alexandre Julliard committed
1774
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
1775

Alexandre Julliard's avatar
Alexandre Julliard committed
1776 1777
    if( newPos > maxPos )
	newPos = maxPos;
1778
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
1779 1780
	if( newPos < minPos )
	    newPos = minPos;
Alexandre Julliard's avatar
Alexandre Julliard committed
1781

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

Alexandre Julliard's avatar
Alexandre Julliard committed
1784
    if( uMsg == WM_VSCROLL )
1785
	ScrollWindowEx(hWnd ,0 ,curPos - newPos, NULL, NULL, 0, NULL,
Alexandre Julliard's avatar
Alexandre Julliard committed
1786
			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
Alexandre Julliard's avatar
Alexandre Julliard committed
1787
    else
1788
	ScrollWindowEx(hWnd ,curPos - newPos, 0, NULL, NULL, 0, NULL,
Alexandre Julliard's avatar
Alexandre Julliard committed
1789
			SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
Alexandre Julliard's avatar
Alexandre Julliard committed
1790 1791
}

Alexandre Julliard's avatar
Alexandre Julliard committed
1792 1793

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


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

1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836
/************************************************************************
 *              "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...
 *
 */

1837
static INT_PTR WINAPI MDI_MoreWindowsDlgProc (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
1838 1839 1840 1841 1842 1843 1844 1845
{
    switch (iMsg)
    {
       case WM_INITDIALOG:
       {
           UINT widest       = 0;
           UINT length;
           UINT i;
1846
           MDICLIENTINFO *ci = get_client_info( (HWND)lParam );
1847 1848
           HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX);

1849 1850
           for (i = 0; i < ci->nActiveChildren; i++)
           {
1851
               WCHAR buffer[MDI_MAXTITLELENGTH];
1852

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

           /* Set the current selection */
1865
           SendMessageW(hListBox, LB_SETCURSEL, MDI_MOREWINDOWSLIMIT, 0);
1866 1867 1868 1869 1870 1871
           return TRUE;
       }

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

1906
static HWND MDI_MoreWindowsDialog(HWND hwnd)
1907 1908 1909 1910 1911
{
    LPCVOID template;
    HRSRC hRes;
    HANDLE hDlgTmpl;

1912
    hRes = FindResourceA(user32_module, "MDI_MOREWINDOWS", (LPSTR)RT_DIALOG);
1913 1914 1915 1916

    if (hRes == 0)
        return 0;

1917
    hDlgTmpl = LoadResource(user32_module, hRes );
1918 1919 1920 1921 1922 1923 1924 1925 1926

    if (hDlgTmpl == 0)
        return 0;

    template = LockResource( hDlgTmpl );

    if (template == 0)
        return 0;

1927
    return (HWND) DialogBoxIndirectParamA(user32_module,
1928
                                          (const DLGTEMPLATE*) template,
1929
                                          hwnd, MDI_MoreWindowsDlgProc, (LPARAM) hwnd);
1930
}