Copy w32api from trunk
[reactos.git] / reactos / lib / user32 / windows / mdi.c
1 /* MDI.C
2 *
3 * Copyright 1994, Bob Amstadt
4 * 1995,1996 Alex Korobka
5 *
6 * This file contains routines to support MDI (Multiple Document
7 * Interface) features .
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * Notes: Fairly complete implementation.
24 * Also, Excel and WinWord do _not_ use MDI so if you're trying
25 * to fix them look elsewhere.
26 *
27 * Notes on how the "More Windows..." is implemented:
28 *
29 * When we have more than 9 opened windows, a "More Windows..."
30 * option appears in the "Windows" menu. Each child window has
31 * a WND* associated with it, accesible via the children list of
32 * the parent window. This WND* has a wIDmenu member, which reflects
33 * the position of the child in the window list. For example, with
34 * 9 child windows, we could have the following pattern:
35 *
36 *
37 *
38 * Name of the child window pWndChild->wIDmenu
39 * Doc1 5000
40 * Doc2 5001
41 * Doc3 5002
42 * Doc4 5003
43 * Doc5 5004
44 * Doc6 5005
45 * Doc7 5006
46 * Doc8 5007
47 * Doc9 5008
48 *
49 *
50 * The "Windows" menu, as the "More windows..." dialog, are constructed
51 * in this order. If we add a child, we would have the following list:
52 *
53 *
54 * Name of the child window pWndChild->wIDmenu
55 * Doc1 5000
56 * Doc2 5001
57 * Doc3 5002
58 * Doc4 5003
59 * Doc5 5004
60 * Doc6 5005
61 * Doc7 5006
62 * Doc8 5007
63 * Doc9 5008
64 * Doc10 5009
65 *
66 * But only 5000 to 5008 would be displayed in the "Windows" menu. We want
67 * the last created child to be in the menu, so we swap the last child with
68 * the 9th... Doc9 will be accessible via the "More Windows..." option.
69 *
70 * Doc1 5000
71 * Doc2 5001
72 * Doc3 5002
73 * Doc4 5003
74 * Doc5 5004
75 * Doc6 5005
76 * Doc7 5006
77 * Doc8 5007
78 * Doc9 5009
79 * Doc10 5008
80 *
81 */
82
83 #include <stdlib.h>
84 #include <stdarg.h>
85 #include <stdio.h>
86 #include <string.h>
87 #include <math.h>
88
89 #include "windef.h"
90 #include "winbase.h"
91 #include "wingdi.h"
92 #include "winuser.h"
93 #include "wine/unicode.h"
94 #include "user32/regcontrol.h"
95 #include <winnls.h>
96 #include "wine/debug.h"
97 #include "dlgs.h"
98
99 WINE_DEFAULT_DEBUG_CHANNEL(mdi);
100
101 #define MDI_MAXLISTLENGTH 0x40
102 #define MDI_MAXTITLELENGTH 0xa1
103
104 #define MDI_NOFRAMEREPAINT 0
105 #define MDI_REPAINTFRAMENOW 1
106 #define MDI_REPAINTFRAME 2
107
108 #define WM_MDICALCCHILDSCROLL 0x10ac /* this is exactly what Windows uses */
109
110 /* "More Windows..." definitions */
111 #define MDI_MOREWINDOWSLIMIT 9 /* after this number of windows, a "More Windows..."
112 option will appear under the Windows menu */
113 #define MDI_IDC_LISTBOX 100
114 #define IDS_MDI_MOREWINDOWS 13
115
116 #define MDIF_NEEDUPDATE 0x0001
117
118 typedef struct
119 {
120 UINT nActiveChildren;
121 HWND hwndChildMaximized;
122 HWND hwndActiveChild;
123 HMENU hWindowMenu;
124 UINT idFirstChild;
125 LPWSTR frameTitle;
126 UINT nTotalCreated;
127 UINT mdiFlags;
128 UINT sbRecalc; /* SB_xxx flags for scrollbar fixup */
129 } MDICLIENTINFO;
130
131 static HBITMAP hBmpClose = 0;
132
133 /* ----------------- declarations ----------------- */
134 static void MDI_UpdateFrameText( HWND, HWND, BOOL, LPCWSTR);
135 static BOOL MDI_AugmentFrameMenu( HWND, HWND );
136 static BOOL MDI_RestoreFrameMenu( HWND, HWND );
137 static LONG MDI_ChildActivate( HWND, HWND );
138
139 static HWND MDI_MoreWindowsDialog(HWND);
140 static void MDI_SwapMenuItems(HWND, UINT, UINT);
141 static LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
142 static LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
143
144 #ifdef __REACTOS__
145 void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
146 void WINAPI CalcChildScroll(HWND hwnd, INT scroll);
147
148 BOOL CALLBACK MDI_GetChildByID_EnumProc (HWND hwnd, LPARAM lParam )
149 {
150 DWORD *control = (DWORD *)lParam;
151 if(*control == GetWindowLongW( hwnd, GWL_ID ))
152 {
153 *control = (DWORD)hwnd;
154 return FALSE;
155 }
156 return TRUE;
157 }
158 #endif
159
160 /* -------- Miscellaneous service functions ----------
161 *
162 * MDI_GetChildByID
163 */
164 static HWND MDI_GetChildByID(HWND hwnd, UINT id)
165 {
166 #ifdef __REACTOS__
167 DWORD Control = id;
168 if (hwnd && !EnumChildWindows(hwnd, (ENUMWINDOWSPROC)&MDI_GetChildByID_EnumProc, (LPARAM)&Control))
169 {
170 return (HWND)Control;
171 }
172 return 0;
173 #else
174 HWND ret;
175 HWND *win_array;
176 int i;
177
178 if (!(win_array = WIN_ListChildren( hwnd ))) return 0;
179 for (i = 0; win_array[i]; i++)
180 {
181 if (GetWindowLongA( win_array[i], GWL_ID ) == id) break;
182 }
183 ret = win_array[i];
184 HeapFree( GetProcessHeap(), 0, win_array );
185 return ret;
186 #endif
187 }
188
189 static void MDI_PostUpdate(HWND hwnd, MDICLIENTINFO* ci, WORD recalc)
190 {
191 if( !(ci->mdiFlags & MDIF_NEEDUPDATE) )
192 {
193 ci->mdiFlags |= MDIF_NEEDUPDATE;
194 PostMessageA( hwnd, WM_MDICALCCHILDSCROLL, 0, 0);
195 }
196 ci->sbRecalc = recalc;
197 }
198
199
200 /*********************************************************************
201 * MDIClient class descriptor
202 */
203 const struct builtin_class_descr MDICLIENT_builtin_class =
204 {
205 L"MDIClient", /* name */
206 0, /* style */
207 MDIClientWndProcW, /* procW */
208 MDIClientWndProcA, /* procA */
209 sizeof(MDICLIENTINFO *), /* extra */
210 IDC_ARROW, /* cursor */
211 (HBRUSH)(COLOR_APPWORKSPACE+1) /* brush */
212 };
213
214
215 static MDICLIENTINFO *get_client_info( HWND client )
216 {
217 #ifdef __REACTOS__
218 return (MDICLIENTINFO *)GetWindowLongPtr(client, 0);
219 #else
220 MDICLIENTINFO *ret = NULL;
221 WND *win = WIN_GetPtr( client );
222 if (win)
223 {
224 if (win == WND_OTHER_PROCESS)
225 {
226 ERR( "client %p belongs to other process\n", client );
227 return NULL;
228 }
229 if (win->cbWndExtra < sizeof(MDICLIENTINFO)) WARN( "%p is not an MDI client\n", client );
230 else ret = (MDICLIENTINFO *)win->wExtra;
231 WIN_ReleasePtr( win );
232 }
233 return ret;
234 #endif
235 }
236
237 /**********************************************************************
238 * MDI_MenuModifyItem
239 */
240 static void MDI_MenuModifyItem( HWND client, HWND hWndChild )
241 {
242 MDICLIENTINFO *clientInfo = get_client_info( client );
243 WCHAR buffer[128];
244 UINT n, id;
245
246 if (!clientInfo || !clientInfo->hWindowMenu) return;
247
248 id = GetWindowLongA( hWndChild, GWL_ID );
249 if (id >= clientInfo->idFirstChild + MDI_MOREWINDOWSLIMIT) return;
250 buffer[0] = '&';
251 buffer[1] = '1' + id - clientInfo->idFirstChild;
252 buffer[2] = ' ';
253 GetWindowTextW( hWndChild, buffer + 3, sizeof(buffer)/sizeof(WCHAR) - 3 );
254
255 n = GetMenuState(clientInfo->hWindowMenu, id, MF_BYCOMMAND);
256 ModifyMenuW(clientInfo->hWindowMenu, id, MF_BYCOMMAND | MF_STRING, id, buffer );
257 CheckMenuItem(clientInfo->hWindowMenu, id, n & MF_CHECKED);
258 }
259
260 /**********************************************************************
261 * MDI_MenuDeleteItem
262 */
263 static BOOL MDI_MenuDeleteItem( HWND client, HWND hWndChild )
264 {
265 WCHAR buffer[128];
266 static const WCHAR format[] = {'&','%','d',' ',0};
267 MDICLIENTINFO *clientInfo = get_client_info( client );
268 UINT index = 0,id,n;
269
270 if( !clientInfo->nActiveChildren || !clientInfo->hWindowMenu )
271 return FALSE;
272
273 id = GetWindowLongA( hWndChild, GWL_ID );
274 DeleteMenu(clientInfo->hWindowMenu,id,MF_BYCOMMAND);
275
276 /* walk the rest of MDI children to prevent gaps in the id
277 * sequence and in the menu child list */
278
279 for( index = id+1; index <= clientInfo->nActiveChildren +
280 clientInfo->idFirstChild; index++ )
281 {
282 HWND hwnd = MDI_GetChildByID(client,index);
283 if (!hwnd)
284 {
285 TRACE("no window for id=%i\n",index);
286 continue;
287 }
288
289 /* set correct id */
290 SetWindowLongW( hwnd, GWL_ID, GetWindowLongW( hwnd, GWL_ID ) - 1 );
291
292 n = wsprintfW(buffer, format ,index - clientInfo->idFirstChild);
293 GetWindowTextW( hwnd, buffer + n, sizeof(buffer)/sizeof(WCHAR) - n );
294
295 /* change menu if the current child is to be shown in the
296 * "Windows" menu
297 */
298 if (index <= clientInfo->idFirstChild + MDI_MOREWINDOWSLIMIT)
299 ModifyMenuW(clientInfo->hWindowMenu ,index ,MF_BYCOMMAND | MF_STRING,
300 index - 1 , buffer );
301 }
302
303 /* We must restore the "More Windows..." option if there are enough children
304 */
305 if (clientInfo->nActiveChildren - 1 > MDI_MOREWINDOWSLIMIT)
306 {
307 WCHAR szTmp[50];
308 LoadStringW(GetModuleHandleA("USER32"), IDS_MDI_MOREWINDOWS, szTmp, sizeof(szTmp)/sizeof(szTmp[0]));
309 AppendMenuW(clientInfo->hWindowMenu, MF_STRING, clientInfo->idFirstChild + MDI_MOREWINDOWSLIMIT, szTmp);
310 }
311 return TRUE;
312 }
313
314 /**********************************************************************
315 * MDI_GetWindow
316 *
317 * returns "activateable" child different from the current or zero
318 */
319 static HWND MDI_GetWindow(MDICLIENTINFO *clientInfo, HWND hWnd, BOOL bNext,
320 DWORD dwStyleMask )
321 {
322 #ifdef __REACTOS__
323 /* FIXME */
324 return 0;
325 #else
326 int i;
327 HWND *list;
328 HWND last = 0;
329
330 dwStyleMask |= WS_DISABLED | WS_VISIBLE;
331 if( !hWnd ) hWnd = clientInfo->hwndActiveChild;
332
333 if (!(list = WIN_ListChildren( GetParent(hWnd) ))) return 0;
334 i = 0;
335 /* start from next after hWnd */
336 while (list[i] && list[i] != hWnd) i++;
337 if (list[i]) i++;
338
339 for ( ; list[i]; i++)
340 {
341 if (GetWindow( list[i], GW_OWNER )) continue;
342 if ((GetWindowLongW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue;
343 last = list[i];
344 if (bNext) goto found;
345 }
346 /* now restart from the beginning */
347 for (i = 0; list[i] && list[i] != hWnd; i++)
348 {
349 if (GetWindow( list[i], GW_OWNER )) continue;
350 if ((GetWindowLongW( list[i], GWL_STYLE ) & dwStyleMask) != WS_VISIBLE) continue;
351 last = list[i];
352 if (bNext) goto found;
353 }
354 found:
355 HeapFree( GetProcessHeap(), 0, list );
356 return last;
357 #endif
358 }
359
360 /**********************************************************************
361 * MDI_CalcDefaultChildPos
362 *
363 * It seems that the default height is about 2/3 of the client rect
364 */
365 static void MDI_CalcDefaultChildPos( HWND hwnd, WORD n, LPPOINT lpPos, INT delta)
366 {
367 INT nstagger;
368 RECT rect;
369 INT spacing = GetSystemMetrics(SM_CYCAPTION) +
370 GetSystemMetrics(SM_CYFRAME) - 1;
371
372 GetClientRect( hwnd, &rect );
373 if( rect.bottom - rect.top - delta >= spacing )
374 rect.bottom -= delta;
375
376 nstagger = (rect.bottom - rect.top)/(3 * spacing);
377 lpPos[1].x = (rect.right - rect.left - nstagger * spacing);
378 lpPos[1].y = (rect.bottom - rect.top - nstagger * spacing);
379 lpPos[0].x = lpPos[0].y = spacing * (n%(nstagger+1));
380 }
381
382 /**********************************************************************
383 * MDISetMenu
384 */
385 static LRESULT MDISetMenu( HWND hwnd, HMENU hmenuFrame,
386 HMENU hmenuWindow)
387 {
388 MDICLIENTINFO *ci;
389 HWND hwndFrame = GetParent(hwnd);
390 HMENU oldFrameMenu = GetMenu(hwndFrame);
391
392 TRACE("%p %p %p\n", hwnd, hmenuFrame, hmenuWindow);
393
394 if (hmenuFrame && !IsMenu(hmenuFrame))
395 {
396 WARN("hmenuFrame is not a menu handle\n");
397 return 0L;
398 }
399
400 if (hmenuWindow && !IsMenu(hmenuWindow))
401 {
402 WARN("hmenuWindow is not a menu handle\n");
403 return 0L;
404 }
405
406 if (!(ci = get_client_info( hwnd ))) return 0;
407
408 if( ci->hwndChildMaximized && hmenuFrame && hmenuFrame!=oldFrameMenu )
409 MDI_RestoreFrameMenu( GetParent(hwnd), ci->hwndChildMaximized );
410
411 if( hmenuWindow && hmenuWindow != ci->hWindowMenu )
412 {
413 /* delete menu items from ci->hWindowMenu
414 * and add them to hmenuWindow */
415 /* Agent newsreader calls this function with ci->hWindowMenu == NULL */
416 if( ci->hWindowMenu && ci->nActiveChildren )
417 {
418 INT j;
419 LPWSTR buffer = NULL;
420 MENUITEMINFOW mii;
421 INT nbWindowsMenuItems; /* num of documents shown + "More Windows..." if present */
422 INT i = GetMenuItemCount(ci->hWindowMenu) - 1;
423 INT pos = GetMenuItemCount(hmenuWindow) + 1;
424
425 AppendMenuA( hmenuWindow, MF_SEPARATOR, 0, NULL);
426
427 if (ci->nActiveChildren <= MDI_MOREWINDOWSLIMIT)
428 nbWindowsMenuItems = ci->nActiveChildren;
429 else
430 nbWindowsMenuItems = MDI_MOREWINDOWSLIMIT + 1;
431
432 j = i - nbWindowsMenuItems + 1;
433
434 for( ; i >= j ; i-- )
435 {
436 memset(&mii, 0, sizeof(mii));
437 mii.cbSize = sizeof(mii);
438 mii.fMask = MIIM_CHECKMARKS | MIIM_DATA | MIIM_ID | MIIM_STATE
439 | MIIM_SUBMENU | MIIM_TYPE | MIIM_BITMAP;
440
441 GetMenuItemInfoW(ci->hWindowMenu, i, TRUE, &mii);
442 if(mii.cch) { /* Menu is MFT_STRING */
443 mii.cch++; /* add room for '\0' */
444 buffer = HeapAlloc(GetProcessHeap(), 0,
445 mii.cch * sizeof(WCHAR));
446 mii.dwTypeData = buffer;
447 GetMenuItemInfoW(ci->hWindowMenu, i, TRUE, &mii);
448 }
449 DeleteMenu(ci->hWindowMenu, i, MF_BYPOSITION);
450 InsertMenuItemW(hmenuWindow, pos, TRUE, &mii);
451 if(buffer) {
452 HeapFree(GetProcessHeap(), 0, buffer);
453 buffer = NULL;
454 }
455 }
456 /* remove separator */
457 DeleteMenu(ci->hWindowMenu, i, MF_BYPOSITION);
458 }
459 ci->hWindowMenu = hmenuWindow;
460 }
461
462 if (hmenuFrame)
463 {
464 SetMenu(hwndFrame, hmenuFrame);
465 if( hmenuFrame!=oldFrameMenu )
466 {
467 if( ci->hwndChildMaximized )
468 MDI_AugmentFrameMenu( GetParent(hwnd), ci->hwndChildMaximized );
469 return (LRESULT)oldFrameMenu;
470 }
471 }
472 else
473 {
474 HMENU menu = GetMenu( GetParent(hwnd) );
475 INT nItems = GetMenuItemCount(menu) - 1;
476 UINT iId = GetMenuItemID(menu,nItems) ;
477
478 if( !(iId == SC_RESTORE || iId == SC_CLOSE) )
479 {
480 /* SetMenu() may already have been called, meaning that this window
481 * already has its menu. But they may have done a SetMenu() on
482 * an MDI window, and called MDISetMenu() after the fact, meaning
483 * that the "if" to this "else" wouldn't catch the need to
484 * augment the frame menu.
485 */
486 if( ci->hwndChildMaximized )
487 MDI_AugmentFrameMenu( GetParent(hwnd), ci->hwndChildMaximized );
488 }
489 }
490 return 0;
491 }
492
493 /**********************************************************************
494 * MDIRefreshMenu
495 */
496 static LRESULT MDIRefreshMenu( HWND hwnd, HMENU hmenuFrame,
497 HMENU hmenuWindow)
498 {
499 HWND hwndFrame = GetParent(hwnd);
500 HMENU oldFrameMenu = GetMenu(hwndFrame);
501
502 TRACE("%p %p %p\n", hwnd, hmenuFrame, hmenuWindow);
503
504 FIXME("partially function stub\n");
505
506 return (LRESULT)oldFrameMenu;
507 }
508
509
510 /* ------------------ MDI child window functions ---------------------- */
511
512
513 /**********************************************************************
514 * MDICreateChild
515 */
516 static HWND MDICreateChild( HWND parent, MDICLIENTINFO *ci,
517 LPMDICREATESTRUCTA cs, BOOL unicode )
518 {
519 POINT pos[2];
520 DWORD style = cs->style | (WS_CHILD | WS_CLIPSIBLINGS);
521 HWND hwnd, hwndMax = 0;
522 UINT wIDmenu = ci->idFirstChild + ci->nActiveChildren;
523 #ifndef __REACTOS__
524 WND *wndParent;
525 #endif
526 static const WCHAR lpstrDef[] = {'j','u','n','k','!',0};
527
528 TRACE("origin %i,%i - dim %i,%i, style %08lx\n",
529 cs->x, cs->y, cs->cx, cs->cy, cs->style);
530 /* calculate placement */
531 MDI_CalcDefaultChildPos(parent, ci->nTotalCreated++, pos, 0);
532
533 #ifndef __REACTOS__
534 if (cs->cx == CW_USEDEFAULT || cs->cx == CW_USEDEFAULT16 || !cs->cx) cs->cx = pos[1].x;
535 if (cs->cy == CW_USEDEFAULT || cs->cy == CW_USEDEFAULT16 || !cs->cy) cs->cy = pos[1].y;
536
537 if (cs->x == CW_USEDEFAULT || cs->x == CW_USEDEFAULT16)
538 #else
539 if (cs->cx == CW_USEDEFAULT || !cs->cx) cs->cx = pos[1].x;
540 if (cs->cy == CW_USEDEFAULT || !cs->cy) cs->cy = pos[1].y;
541
542 if (cs->x == CW_USEDEFAULT)
543 #endif
544 {
545 cs->x = pos[0].x;
546 cs->y = pos[0].y;
547 }
548
549 /* restore current maximized child */
550 if( (style & WS_VISIBLE) && ci->hwndChildMaximized )
551 {
552 TRACE("Restoring current maximized child %p\n", ci->hwndChildMaximized);
553 if( style & WS_MAXIMIZE )
554 SendMessageW(parent, WM_SETREDRAW, FALSE, 0L);
555 hwndMax = ci->hwndChildMaximized;
556 ShowWindow( hwndMax, SW_SHOWNOACTIVATE );
557 if( style & WS_MAXIMIZE )
558 SendMessageW(parent, WM_SETREDRAW, TRUE, 0L);
559 }
560
561 if (ci->nActiveChildren <= MDI_MOREWINDOWSLIMIT)
562 /* this menu is needed to set a check mark in MDI_ChildActivate */
563 if (ci->hWindowMenu != 0)
564 AppendMenuW(ci->hWindowMenu, MF_STRING, wIDmenu, lpstrDef);
565
566 ci->nActiveChildren++;
567
568 /* fix window style */
569 #ifndef __REACTOS__
570 wndParent = WIN_FindWndPtr( parent );
571 if( !(wndParent->dwStyle & MDIS_ALLCHILDSTYLES) )
572 #else
573 if( !(GetWindowLong(parent, GWL_STYLE) & MDIS_ALLCHILDSTYLES) )
574 #endif
575 {
576 TRACE("MDIS_ALLCHILDSTYLES is missing, fixing window style\n");
577 style &= (WS_CHILD | WS_CLIPSIBLINGS | WS_MINIMIZE | WS_MAXIMIZE |
578 WS_CLIPCHILDREN | WS_DISABLED | WS_VSCROLL | WS_HSCROLL );
579 style |= (WS_VISIBLE | WS_OVERLAPPEDWINDOW);
580 }
581
582 #ifndef __REACTOS__
583 if( wndParent->flags & WIN_ISWIN32 )
584 #endif
585 {
586 #ifndef __REACTOS__
587 WIN_ReleaseWndPtr( wndParent );
588 #endif
589 if(unicode)
590 {
591 MDICREATESTRUCTW *csW = (MDICREATESTRUCTW *)cs;
592 hwnd = CreateWindowW( csW->szClass, csW->szTitle, style,
593 csW->x, csW->y, csW->cx, csW->cy, parent,
594 (HMENU)wIDmenu, csW->hOwner, csW );
595 }
596 else
597 hwnd = CreateWindowA( cs->szClass, cs->szTitle, style,
598 cs->x, cs->y, cs->cx, cs->cy, parent,
599 (HMENU)wIDmenu, cs->hOwner, cs );
600 }
601 #ifndef __REACTOS__
602 else
603 {
604 MDICREATESTRUCT16 cs16;
605 SEGPTR title, cls, seg_cs16;
606
607 WIN_ReleaseWndPtr( wndParent );
608 STRUCT32_MDICREATESTRUCT32Ato16( cs, &cs16 );
609 cs16.szTitle = title = MapLS( cs->szTitle );
610 cs16.szClass = cls = MapLS( cs->szClass );
611 seg_cs16 = MapLS( &cs16 );
612 hwnd = WIN_Handle32( CreateWindow16( cs->szClass, cs->szTitle, style,
613 cs16.x, cs16.y, cs16.cx, cs16.cy,
614 HWND_16(parent), (HMENU16)wIDmenu,
615 cs16.hOwner, (LPVOID)seg_cs16 ));
616 UnMapLS( seg_cs16 );
617 UnMapLS( title );
618 UnMapLS( cls );
619 }
620 #endif
621
622 /* MDI windows are WS_CHILD so they won't be activated by CreateWindow */
623
624 if (hwnd)
625 {
626 /* All MDI child windows have the WS_EX_MDICHILD style */
627 SetWindowLongW( hwnd, GWL_EXSTYLE, GetWindowLongW( hwnd, GWL_EXSTYLE ) | WS_EX_MDICHILD );
628
629 /* If we have more than 9 windows, we must insert the new one at the
630 * 9th position in order to see it in the "Windows" menu
631 */
632 if (ci->nActiveChildren > MDI_MOREWINDOWSLIMIT)
633 MDI_SwapMenuItems( parent, GetWindowLongW( hwnd, GWL_ID ),
634 ci->idFirstChild + MDI_MOREWINDOWSLIMIT - 1);
635
636 MDI_MenuModifyItem(parent, hwnd);
637
638 /* Have we hit the "More Windows..." limit? If so, we must
639 * add a "More Windows..." option
640 */
641 if (ci->nActiveChildren == MDI_MOREWINDOWSLIMIT + 1)
642 {
643 WCHAR szTmp[50];
644 LoadStringW(GetModuleHandleA("USER32"), IDS_MDI_MOREWINDOWS, szTmp, sizeof(szTmp)/sizeof(szTmp[0]));
645
646 ModifyMenuW(ci->hWindowMenu,
647 ci->idFirstChild + MDI_MOREWINDOWSLIMIT,
648 MF_BYCOMMAND | MF_STRING,
649 ci->idFirstChild + MDI_MOREWINDOWSLIMIT,
650 szTmp);
651 }
652
653 if( IsIconic(hwnd) && ci->hwndActiveChild )
654 {
655 TRACE("Minimizing created MDI child %p\n", hwnd);
656 ShowWindow( hwnd, SW_SHOWMINNOACTIVE );
657 }
658 else
659 {
660 /* WS_VISIBLE is clear if a) the MDI client has
661 * MDIS_ALLCHILDSTYLES style and 2) the flag is cleared in the
662 * MDICreateStruct. If so the created window is not shown nor
663 * activated.
664 */
665 if (IsWindowVisible(hwnd)) ShowWindow(hwnd, SW_SHOW);
666 }
667 TRACE("created child - %p\n",hwnd);
668 }
669 else
670 {
671 ci->nActiveChildren--;
672 DeleteMenu(ci->hWindowMenu,wIDmenu,MF_BYCOMMAND);
673 if( IsWindow(hwndMax) )
674 ShowWindow(hwndMax, SW_SHOWMAXIMIZED);
675 }
676
677 return hwnd;
678 }
679
680 /**********************************************************************
681 * MDI_ChildGetMinMaxInfo
682 *
683 * Note: The rule here is that client rect of the maximized MDI child
684 * is equal to the client rect of the MDI client window.
685 */
686 static void MDI_ChildGetMinMaxInfo( HWND client, HWND hwnd, MINMAXINFO* lpMinMax )
687 {
688 RECT rect;
689
690 GetClientRect( client, &rect );
691 AdjustWindowRectEx( &rect, GetWindowLongW( hwnd, GWL_STYLE ),
692 0, GetWindowLongW( hwnd, GWL_EXSTYLE ));
693
694 lpMinMax->ptMaxSize.x = rect.right -= rect.left;
695 lpMinMax->ptMaxSize.y = rect.bottom -= rect.top;
696
697 lpMinMax->ptMaxPosition.x = rect.left;
698 lpMinMax->ptMaxPosition.y = rect.top;
699
700 TRACE("max rect (%ld,%ld - %ld, %ld)\n",
701 rect.left,rect.top,rect.right,rect.bottom);
702 }
703
704 /**********************************************************************
705 * MDI_SwitchActiveChild
706 *
707 * Note: SetWindowPos sends WM_CHILDACTIVATE to the child window that is
708 * being activated
709 */
710 static void MDI_SwitchActiveChild( HWND clientHwnd, HWND childHwnd,
711 BOOL bNextWindow )
712 {
713 HWND hwndTo = 0;
714 HWND hwndPrev = 0;
715 MDICLIENTINFO *ci = get_client_info( clientHwnd );
716
717 hwndTo = MDI_GetWindow(ci, childHwnd, bNextWindow, 0);
718
719 TRACE("from %p, to %p\n",childHwnd,hwndTo);
720
721 if ( !hwndTo ) return; /* no window to switch to */
722
723 hwndPrev = ci->hwndActiveChild;
724
725 if ( hwndTo != hwndPrev )
726 {
727 SetWindowPos( hwndTo, HWND_TOP, 0, 0, 0, 0,
728 SWP_NOMOVE | SWP_NOSIZE );
729
730 if( bNextWindow && hwndPrev )
731 SetWindowPos( hwndPrev, HWND_BOTTOM, 0, 0, 0, 0,
732 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE );
733 }
734 }
735
736
737 /**********************************************************************
738 * MDIDestroyChild
739 */
740 static LRESULT MDIDestroyChild( HWND parent, MDICLIENTINFO *ci,
741 HWND child, BOOL flagDestroy )
742 {
743 if( child == ci->hwndActiveChild )
744 {
745 MDI_SwitchActiveChild(parent, child, TRUE);
746
747 if( child == ci->hwndActiveChild )
748 {
749 ShowWindow( child, SW_HIDE);
750 if( child == ci->hwndChildMaximized )
751 {
752 HWND frame = GetParent(parent);
753 MDI_RestoreFrameMenu( frame, child );
754 ci->hwndChildMaximized = 0;
755 MDI_UpdateFrameText( frame, parent, TRUE, NULL);
756 }
757
758 MDI_ChildActivate(parent, 0);
759 }
760 }
761
762 MDI_MenuDeleteItem(parent, child);
763
764 ci->nActiveChildren--;
765
766 TRACE("child destroyed - %p\n",child);
767
768 if (flagDestroy)
769 {
770 MDI_PostUpdate(GetParent(child), ci, SB_BOTH+1);
771 DestroyWindow(child);
772 }
773 return 0;
774 }
775
776
777 /**********************************************************************
778 * MDI_ChildActivate
779 *
780 * Note: hWndChild is NULL when last child is being destroyed
781 */
782 static LONG MDI_ChildActivate( HWND client, HWND child )
783 {
784 MDICLIENTINFO *clientInfo = get_client_info( client );
785 HWND prevActiveWnd = clientInfo->hwndActiveChild;
786 BOOL isActiveFrameWnd;
787
788 if (child && (!IsWindowEnabled( child ))) return 0;
789
790 /* Don't activate if it is already active. Might happen
791 since ShowWindow DOES activate MDI children */
792 if (clientInfo->hwndActiveChild == child) return 0;
793
794 TRACE("%p\n", child);
795
796 isActiveFrameWnd = (GetActiveWindow() == GetParent(client));
797
798 /* deactivate prev. active child */
799 if(prevActiveWnd)
800 {
801 SetWindowLongA( prevActiveWnd, GWL_STYLE,
802 GetWindowLongA( prevActiveWnd, GWL_STYLE ) | WS_SYSMENU );
803 SendMessageA( prevActiveWnd, WM_NCACTIVATE, FALSE, 0L );
804 SendMessageA( prevActiveWnd, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child);
805 /* uncheck menu item */
806 if( clientInfo->hWindowMenu )
807 {
808 UINT prevID = GetWindowLongA( prevActiveWnd, GWL_ID );
809
810 if (prevID - clientInfo->idFirstChild < MDI_MOREWINDOWSLIMIT)
811 CheckMenuItem( clientInfo->hWindowMenu, prevID, 0);
812 else
813 CheckMenuItem( clientInfo->hWindowMenu,
814 clientInfo->idFirstChild + MDI_MOREWINDOWSLIMIT - 1, 0);
815 }
816 }
817
818 /* set appearance */
819 if (clientInfo->hwndChildMaximized && clientInfo->hwndChildMaximized != child)
820 {
821 INT cmd = SW_SHOWNORMAL;
822
823 if( child )
824 {
825 UINT state = GetMenuState(GetSystemMenu(child, FALSE), SC_MAXIMIZE, MF_BYCOMMAND);
826 if (state != 0xFFFFFFFF && (state & (MF_DISABLED | MF_GRAYED)))
827 SendMessageW(clientInfo->hwndChildMaximized, WM_SYSCOMMAND, SC_RESTORE, 0);
828 else
829 cmd = SW_SHOWMAXIMIZED;
830
831 clientInfo->hwndActiveChild = child;
832 }
833
834 ShowWindow( clientInfo->hwndActiveChild, cmd );
835 }
836
837 clientInfo->hwndActiveChild = child;
838
839 /* check if we have any children left */
840 if( !child )
841 {
842 if( isActiveFrameWnd )
843 SetFocus( client );
844 return 0;
845 }
846
847 /* check menu item */
848 if( clientInfo->hWindowMenu )
849 {
850 UINT id = GetWindowLongA( child, GWL_ID );
851 /* The window to be activated must be displayed in the "Windows" menu */
852 if (id >= clientInfo->idFirstChild + MDI_MOREWINDOWSLIMIT)
853 {
854 MDI_SwapMenuItems( GetParent(child),
855 id, clientInfo->idFirstChild + MDI_MOREWINDOWSLIMIT - 1);
856 id = clientInfo->idFirstChild + MDI_MOREWINDOWSLIMIT - 1;
857 MDI_MenuModifyItem( GetParent(child), child );
858 }
859
860 CheckMenuItem(clientInfo->hWindowMenu, id, MF_CHECKED);
861 }
862 /* bring active child to the top */
863 SetWindowPos( child, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
864
865 if( isActiveFrameWnd )
866 {
867 SendMessageA( child, WM_NCACTIVATE, TRUE, 0L);
868 if( GetFocus() == client )
869 SendMessageA( client, WM_SETFOCUS, (WPARAM)client, 0L );
870 else
871 SetFocus( client );
872 }
873 SendMessageA( child, WM_MDIACTIVATE, (WPARAM)prevActiveWnd, (LPARAM)child );
874 return TRUE;
875 }
876
877 /* -------------------- MDI client window functions ------------------- */
878
879 /**********************************************************************
880 * CreateMDIMenuBitmap
881 */
882 static HBITMAP CreateMDIMenuBitmap(void)
883 {
884 HDC hDCSrc = CreateCompatibleDC(0);
885 HDC hDCDest = CreateCompatibleDC(hDCSrc);
886 HBITMAP hbClose = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE) );
887 HBITMAP hbCopy;
888 HBITMAP hobjSrc, hobjDest;
889
890 hobjSrc = SelectObject(hDCSrc, hbClose);
891 hbCopy = CreateCompatibleBitmap(hDCSrc,GetSystemMetrics(SM_CXSIZE),GetSystemMetrics(SM_CYSIZE));
892 hobjDest = SelectObject(hDCDest, hbCopy);
893
894 BitBlt(hDCDest, 0, 0, GetSystemMetrics(SM_CXSIZE), GetSystemMetrics(SM_CYSIZE),
895 hDCSrc, GetSystemMetrics(SM_CXSIZE), 0, SRCCOPY);
896
897 SelectObject(hDCSrc, hobjSrc);
898 DeleteObject(hbClose);
899 DeleteDC(hDCSrc);
900
901 hobjSrc = SelectObject( hDCDest, GetStockObject(BLACK_PEN) );
902
903 MoveToEx( hDCDest, GetSystemMetrics(SM_CXSIZE) - 1, 0, NULL );
904 LineTo( hDCDest, GetSystemMetrics(SM_CXSIZE) - 1, GetSystemMetrics(SM_CYSIZE) - 1);
905
906 SelectObject(hDCDest, hobjSrc );
907 SelectObject(hDCDest, hobjDest);
908 DeleteDC(hDCDest);
909
910 return hbCopy;
911 }
912
913 /**********************************************************************
914 * MDICascade
915 */
916 static LONG MDICascade( HWND client, MDICLIENTINFO *ci )
917 {
918 #ifdef __REACTOS__
919 /* FIXME */
920 return 0;
921 #else
922 HWND *win_array;
923 BOOL has_icons = FALSE;
924 int i, total;
925
926 if (ci->hwndChildMaximized)
927 SendMessageA( client, WM_MDIRESTORE,
928 (WPARAM)ci->hwndChildMaximized, 0);
929
930 if (ci->nActiveChildren == 0) return 0;
931
932 if (!(win_array = WIN_ListChildren( client ))) return 0;
933
934 /* remove all the windows we don't want */
935 for (i = total = 0; win_array[i]; i++)
936 {
937 if (!IsWindowVisible( win_array[i] )) continue;
938 if (GetWindow( win_array[i], GW_OWNER )) continue; /* skip owned windows */
939 if (IsIconic( win_array[i] ))
940 {
941 has_icons = TRUE;
942 continue;
943 }
944 win_array[total++] = win_array[i];
945 }
946 win_array[total] = 0;
947
948 if (total)
949 {
950 INT delta = 0, n = 0, i;
951 POINT pos[2];
952 if (has_icons) delta = GetSystemMetrics(SM_CYICONSPACING) + GetSystemMetrics(SM_CYICON);
953
954 /* walk the list (backwards) and move windows */
955 for (i = total - 1; i >= 0; i--)
956 {
957 TRACE("move %p to (%ld,%ld) size [%ld,%ld]\n",
958 win_array[i], pos[0].x, pos[0].y, pos[1].x, pos[1].y);
959
960 MDI_CalcDefaultChildPos(client, n++, pos, delta);
961 SetWindowPos( win_array[i], 0, pos[0].x, pos[0].y, pos[1].x, pos[1].y,
962 SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
963 }
964 }
965 HeapFree( GetProcessHeap(), 0, win_array );
966
967 if (has_icons) ArrangeIconicWindows( client );
968 return 0;
969 #endif
970 }
971
972 /**********************************************************************
973 * MDITile
974 */
975 static void MDITile( HWND client, MDICLIENTINFO *ci, WPARAM wParam )
976 {
977 #ifdef __REACTOS__
978 /* FIXME */
979 return;
980 #else
981 HWND *win_array;
982 int i, total;
983 BOOL has_icons = FALSE;
984
985 if (ci->hwndChildMaximized)
986 SendMessageA( client, WM_MDIRESTORE, (WPARAM)ci->hwndChildMaximized, 0);
987
988 if (ci->nActiveChildren == 0) return;
989
990 if (!(win_array = WIN_ListChildren( client ))) return;
991
992 /* remove all the windows we don't want */
993 for (i = total = 0; win_array[i]; i++)
994 {
995 if (!IsWindowVisible( win_array[i] )) continue;
996 if (GetWindow( win_array[i], GW_OWNER )) continue; /* skip owned windows (icon titles) */
997 if (IsIconic( win_array[i] ))
998 {
999 has_icons = TRUE;
1000 continue;
1001 }
1002 if ((wParam & MDITILE_SKIPDISABLED) && !IsWindowEnabled( win_array[i] )) continue;
1003 win_array[total++] = win_array[i];
1004 }
1005 win_array[total] = 0;
1006
1007 TRACE("%u windows to tile\n", total);
1008
1009 if (total)
1010 {
1011 HWND *pWnd = win_array;
1012 RECT rect;
1013 int x, y, xsize, ysize;
1014 int rows, columns, r, c, i;
1015
1016 GetClientRect(client,&rect);
1017 rows = (int) sqrt((double)total);
1018 columns = total / rows;
1019
1020 if( wParam & MDITILE_HORIZONTAL ) /* version >= 3.1 */
1021 {
1022 i = rows;
1023 rows = columns; /* exchange r and c */
1024 columns = i;
1025 }
1026
1027 if (has_icons)
1028 {
1029 y = rect.bottom - 2 * GetSystemMetrics(SM_CYICONSPACING) - GetSystemMetrics(SM_CYICON);
1030 rect.bottom = ( y - GetSystemMetrics(SM_CYICON) < rect.top )? rect.bottom: y;
1031 }
1032
1033 ysize = rect.bottom / rows;
1034 xsize = rect.right / columns;
1035
1036 for (x = i = 0, c = 1; c <= columns && *pWnd; c++)
1037 {
1038 if (c == columns)
1039 {
1040 rows = total - i;
1041 ysize = rect.bottom / rows;
1042 }
1043
1044 y = 0;
1045 for (r = 1; r <= rows && *pWnd; r++, i++)
1046 {
1047 SetWindowPos(*pWnd, 0, x, y, xsize, ysize,
1048 SWP_DRAWFRAME | SWP_NOACTIVATE | SWP_NOZORDER);
1049 y += ysize;
1050 pWnd++;
1051 }
1052 x += xsize;
1053 }
1054 }
1055 HeapFree( GetProcessHeap(), 0, win_array );
1056 if (has_icons) ArrangeIconicWindows( client );
1057 #endif
1058 }
1059
1060 /* ----------------------- Frame window ---------------------------- */
1061
1062
1063 /**********************************************************************
1064 * MDI_AugmentFrameMenu
1065 */
1066 static BOOL MDI_AugmentFrameMenu( HWND frame, HWND hChild )
1067 {
1068 HMENU menu = GetMenu( frame );
1069 HMENU hSysPopup = 0;
1070 HBITMAP hSysMenuBitmap = 0;
1071 INT nItems;
1072 UINT iId;
1073 HICON hIcon;
1074
1075 TRACE("frame %p,child %p\n",frame,hChild);
1076
1077 if( !menu ) return 0;
1078
1079 /* if the system buttons already exist do not add them again */
1080 nItems = GetMenuItemCount(menu) - 1;
1081 iId = GetMenuItemID(menu,nItems) ;
1082 if (iId == SC_RESTORE || iId == SC_CLOSE)
1083 return 0;
1084
1085 /* create a copy of sysmenu popup and insert it into frame menu bar */
1086 if (!(hSysPopup = GetSystemMenu(hChild, FALSE)))
1087 return 0;
1088
1089 AppendMenuA(menu,MF_HELP | MF_BITMAP,
1090 SC_MINIMIZE, (LPSTR)(DWORD)HBMMENU_MBAR_MINIMIZE ) ;
1091 AppendMenuA(menu,MF_HELP | MF_BITMAP,
1092 SC_RESTORE, (LPSTR)(DWORD)HBMMENU_MBAR_RESTORE );
1093
1094 AppendMenuA(menu,MF_HELP | MF_BITMAP,
1095 SC_CLOSE, (LPSTR)(DWORD)HBMMENU_MBAR_CLOSE );
1096
1097 /* The system menu is replaced by the child icon */
1098 hIcon = (HICON)GetClassLongW(hChild, GCL_HICONSM);
1099 if (!hIcon)
1100 hIcon = (HICON)GetClassLongW(hChild, GCL_HICON);
1101 if (!hIcon)
1102 hIcon = LoadIconW(NULL, IDI_APPLICATION);
1103 if (hIcon)
1104 {
1105 HDC hMemDC;
1106 HBITMAP hBitmap, hOldBitmap;
1107 HBRUSH hBrush;
1108 HDC hdc = GetDC(hChild);
1109
1110 if (hdc)
1111 {
1112 int cx, cy;
1113 cx = GetSystemMetrics(SM_CXSMICON);
1114 cy = GetSystemMetrics(SM_CYSMICON);
1115 hMemDC = CreateCompatibleDC(hdc);
1116 hBitmap = CreateCompatibleBitmap(hdc, cx, cy);
1117 hOldBitmap = SelectObject(hMemDC, hBitmap);
1118 SetMapMode(hMemDC, MM_TEXT);
1119 hBrush = CreateSolidBrush(GetSysColor(COLOR_MENU));
1120 DrawIconEx(hMemDC, 0, 0, hIcon, cx, cy, 0, hBrush, DI_NORMAL);
1121 SelectObject (hMemDC, hOldBitmap);
1122 DeleteObject(hBrush);
1123 DeleteDC(hMemDC);
1124 ReleaseDC(hChild, hdc);
1125 hSysMenuBitmap = hBitmap;
1126 }
1127 }
1128
1129 if( !InsertMenuA(menu,0,MF_BYPOSITION | MF_BITMAP | MF_POPUP,
1130 (UINT_PTR)hSysPopup, (LPSTR)hSysMenuBitmap))
1131 {
1132 TRACE("not inserted\n");
1133 DestroyMenu(hSysPopup);
1134 return 0;
1135 }
1136
1137 EnableMenuItem(hSysPopup, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
1138 EnableMenuItem(hSysPopup, SC_MOVE, MF_BYCOMMAND | MF_GRAYED);
1139 EnableMenuItem(hSysPopup, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
1140 SetMenuDefaultItem(hSysPopup, SC_CLOSE, FALSE);
1141
1142 /* redraw menu */
1143 DrawMenuBar(frame);
1144
1145 return 1;
1146 }
1147
1148 /**********************************************************************
1149 * MDI_RestoreFrameMenu
1150 */
1151 static BOOL MDI_RestoreFrameMenu( HWND frame, HWND hChild )
1152 {
1153 MENUITEMINFOW menuInfo;
1154 HMENU menu = GetMenu( frame );
1155 INT nItems = GetMenuItemCount(menu) - 1;
1156 UINT iId = GetMenuItemID(menu,nItems) ;
1157
1158 TRACE("frame %p,child %p,nIt=%d,iId=%d\n",frame,hChild,nItems,iId);
1159
1160 if(!(iId == SC_RESTORE || iId == SC_CLOSE) )
1161 return 0;
1162
1163 /*
1164 * Remove the system menu, If that menu is the icon of the window
1165 * as it is in win95, we have to delete the bitmap.
1166 */
1167 memset(&menuInfo, 0, sizeof(menuInfo));
1168 menuInfo.cbSize = sizeof(menuInfo);
1169 menuInfo.fMask = MIIM_DATA | MIIM_TYPE | MIIM_BITMAP;
1170
1171 GetMenuItemInfoW(menu,
1172 0,
1173 TRUE,
1174 &menuInfo);
1175
1176 RemoveMenu(menu,0,MF_BYPOSITION);
1177
1178 if ( menuInfo.hbmpItem != 0 )
1179 DeleteObject(menuInfo.hbmpItem);
1180
1181 /* close */
1182 DeleteMenu(menu,GetMenuItemCount(menu) - 1,MF_BYPOSITION);
1183 /* restore */
1184 DeleteMenu(menu,GetMenuItemCount(menu) - 1,MF_BYPOSITION);
1185 /* minimize */
1186 DeleteMenu(menu,GetMenuItemCount(menu) - 1,MF_BYPOSITION);
1187
1188 DrawMenuBar(frame);
1189
1190 return 1;
1191 }
1192
1193
1194 /**********************************************************************
1195 * MDI_UpdateFrameText
1196 *
1197 * used when child window is maximized/restored
1198 *
1199 * Note: lpTitle can be NULL
1200 */
1201 static void MDI_UpdateFrameText( HWND frame, HWND hClient,
1202 BOOL repaint, LPCWSTR lpTitle )
1203 {
1204 WCHAR lpBuffer[MDI_MAXTITLELENGTH+1];
1205 MDICLIENTINFO *ci = get_client_info( hClient );
1206
1207 TRACE("repaint %i, frameText %s\n", repaint, debugstr_w(lpTitle));
1208
1209 if (!ci) return;
1210
1211 if (!lpTitle && !ci->frameTitle) /* first time around, get title from the frame window */
1212 {
1213 GetWindowTextW( frame, lpBuffer, sizeof(lpBuffer)/sizeof(WCHAR) );
1214 lpTitle = lpBuffer;
1215 }
1216
1217 /* store new "default" title if lpTitle is not NULL */
1218 if (lpTitle)
1219 {
1220 if (ci->frameTitle) HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1221 if ((ci->frameTitle = HeapAlloc( GetProcessHeap(), 0, (strlenW(lpTitle)+1)*sizeof(WCHAR))))
1222 strcpyW( ci->frameTitle, lpTitle );
1223 }
1224
1225 if (ci->frameTitle)
1226 {
1227 if (ci->hwndChildMaximized)
1228 {
1229 /* combine frame title and child title if possible */
1230
1231 static const WCHAR lpBracket[] = {' ','-',' ','[',0};
1232 static const WCHAR lpBracket2[] = {']',0};
1233 int i_frame_text_length = strlenW(ci->frameTitle);
1234
1235 lstrcpynW( lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH);
1236
1237 if( i_frame_text_length + 6 < MDI_MAXTITLELENGTH )
1238 {
1239 strcatW( lpBuffer, lpBracket );
1240 if (GetWindowTextW( ci->hwndChildMaximized, lpBuffer + i_frame_text_length + 4,
1241 MDI_MAXTITLELENGTH - i_frame_text_length - 5 ))
1242 strcatW( lpBuffer, lpBracket2 );
1243 else
1244 lpBuffer[i_frame_text_length] = 0; /* remove bracket */
1245 }
1246 }
1247 else
1248 {
1249 lstrcpynW(lpBuffer, ci->frameTitle, MDI_MAXTITLELENGTH+1 );
1250 }
1251 }
1252 else
1253 lpBuffer[0] = '\0';
1254
1255 DefWindowProcW( frame, WM_SETTEXT, 0, (LPARAM)lpBuffer );
1256 if( repaint == MDI_REPAINTFRAME)
1257 SetWindowPos( frame, 0,0,0,0,0, SWP_FRAMECHANGED |
1258 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER );
1259 }
1260
1261
1262 /* ----------------------------- Interface ---------------------------- */
1263
1264
1265 /**********************************************************************
1266 * MDIClientWndProc_common
1267 */
1268 static LRESULT MDIClientWndProc_common( HWND hwnd, UINT message,
1269 WPARAM wParam, LPARAM lParam, BOOL unicode )
1270 {
1271 MDICLIENTINFO *ci = NULL;
1272
1273 if (WM_NCCREATE != message
1274 && NULL == (ci = get_client_info(hwnd)))
1275 {
1276 return 0;
1277 }
1278
1279 switch (message)
1280 {
1281 #ifdef __REACTOS__
1282 case WM_NCCREATE:
1283 if (!(ci = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ci))))
1284 return FALSE;
1285 SetWindowLongPtrW( hwnd, 0, (LONG_PTR)ci );
1286 return TRUE;
1287 #endif
1288
1289 case WM_CREATE:
1290 {
1291 RECT rect;
1292 /* Since we are using only cs->lpCreateParams, we can safely
1293 * cast to LPCREATESTRUCTA here */
1294 LPCREATESTRUCTA cs = (LPCREATESTRUCTA)lParam;
1295 #ifndef __REACTOS__
1296 WND *wndPtr = WIN_GetPtr( hwnd );
1297 #endif
1298
1299 /* Translation layer doesn't know what's in the cs->lpCreateParams
1300 * so we have to keep track of what environment we're in. */
1301
1302 #ifndef __REACTOS__
1303 if( wndPtr->flags & WIN_ISWIN32 )
1304 #endif
1305 {
1306 #define ccs ((LPCLIENTCREATESTRUCT)cs->lpCreateParams)
1307 ci->hWindowMenu = ccs->hWindowMenu;
1308 ci->idFirstChild = ccs->idFirstChild;
1309 #undef ccs
1310 }
1311 #ifndef __REACTOS__
1312 else
1313 {
1314 LPCLIENTCREATESTRUCT16 ccs = MapSL((SEGPTR)cs->lpCreateParams);
1315 ci->hWindowMenu = HMENU_32(ccs->hWindowMenu);
1316 ci->idFirstChild = ccs->idFirstChild;
1317 }
1318 WIN_ReleasePtr( wndPtr );
1319 #endif
1320
1321 ci->hwndChildMaximized = 0;
1322 ci->nActiveChildren = 0;
1323 ci->nTotalCreated = 0;
1324 ci->frameTitle = NULL;
1325 ci->mdiFlags = 0;
1326 SetWindowLongW( hwnd, GWL_STYLE, GetWindowLongW(hwnd,GWL_STYLE) | WS_CLIPCHILDREN );
1327
1328 if (!hBmpClose) hBmpClose = CreateMDIMenuBitmap();
1329
1330 if (ci->hWindowMenu != 0)
1331 AppendMenuW( ci->hWindowMenu, MF_SEPARATOR, 0, NULL );
1332
1333 GetClientRect( GetParent(hwnd), &rect);
1334 MoveWindow( hwnd, 0, 0, rect.right, rect.bottom, FALSE );
1335
1336 MDI_UpdateFrameText( GetParent(hwnd), hwnd, MDI_NOFRAMEREPAINT, NULL);
1337
1338 TRACE("Client created - hwnd = %p, idFirst = %u\n", hwnd, ci->idFirstChild );
1339 return 0;
1340 }
1341
1342 case WM_DESTROY:
1343 {
1344 INT nItems;
1345 if( ci->hwndChildMaximized )
1346 MDI_RestoreFrameMenu( GetParent(hwnd), ci->hwndChildMaximized);
1347 if((ci->hWindowMenu != 0) &&
1348 (nItems = GetMenuItemCount(ci->hWindowMenu)) > 0)
1349 {
1350 ci->idFirstChild = nItems - 1;
1351 ci->nActiveChildren++; /* to delete a separator */
1352 while( ci->nActiveChildren-- )
1353 DeleteMenu(ci->hWindowMenu,MF_BYPOSITION,ci->idFirstChild--);
1354 }
1355 if (ci->frameTitle) HeapFree( GetProcessHeap(), 0, ci->frameTitle );
1356 #ifdef __REACTOS__
1357 HeapFree( GetProcessHeap(), 0, ci );
1358 SetWindowLongPtrW( hwnd, 0, 0 );
1359 #endif
1360 return 0;
1361 }
1362
1363 case WM_MDIACTIVATE:
1364 if( ci->hwndActiveChild != (HWND)wParam )
1365 SetWindowPos((HWND)wParam, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE);
1366 return 0;
1367
1368 case WM_MDICASCADE:
1369 return MDICascade(hwnd, ci);
1370
1371 case WM_MDICREATE:
1372 if (lParam)
1373 return (LRESULT)MDICreateChild( hwnd, ci, (MDICREATESTRUCTA *)lParam, unicode );
1374 return 0;
1375
1376 case WM_MDIDESTROY:
1377 #ifndef __REACTOS__
1378 return MDIDestroyChild( hwnd, ci, WIN_GetFullHandle( (HWND)wParam ), TRUE );
1379 #else
1380 return MDIDestroyChild( hwnd, ci, (HWND)wParam, TRUE );
1381 #endif
1382
1383 case WM_MDIGETACTIVE:
1384 if (lParam) *(BOOL *)lParam = (ci->hwndChildMaximized != 0);
1385 return (LRESULT)ci->hwndActiveChild;
1386
1387 case WM_MDIICONARRANGE:
1388 ci->mdiFlags |= MDIF_NEEDUPDATE;
1389 ArrangeIconicWindows( hwnd );
1390 ci->sbRecalc = SB_BOTH+1;
1391 SendMessageW( hwnd, WM_MDICALCCHILDSCROLL, 0, 0 );
1392 return 0;
1393
1394 case WM_MDIMAXIMIZE:
1395 ShowWindow( (HWND)wParam, SW_MAXIMIZE );
1396 return 0;
1397
1398 case WM_MDINEXT: /* lParam != 0 means previous window */
1399 #ifndef __REACTOS__
1400 MDI_SwitchActiveChild( hwnd, WIN_GetFullHandle( (HWND)wParam ), !lParam );
1401 #else
1402 MDI_SwitchActiveChild( hwnd, (HWND)wParam, !lParam );
1403 #endif
1404 break;
1405
1406 case WM_MDIRESTORE:
1407 SendMessageW( (HWND)wParam, WM_SYSCOMMAND, SC_RESTORE, 0);
1408 return 0;
1409
1410 case WM_MDISETMENU:
1411 return MDISetMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1412
1413 case WM_MDIREFRESHMENU:
1414 return MDIRefreshMenu( hwnd, (HMENU)wParam, (HMENU)lParam );
1415
1416 case WM_MDITILE:
1417 ci->mdiFlags |= MDIF_NEEDUPDATE;
1418 ShowScrollBar( hwnd, SB_BOTH, FALSE );
1419 MDITile( hwnd, ci, wParam );
1420 ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1421 return 0;
1422
1423 case WM_VSCROLL:
1424 case WM_HSCROLL:
1425 ci->mdiFlags |= MDIF_NEEDUPDATE;
1426 ScrollChildren( hwnd, message, wParam, lParam );
1427 ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1428 return 0;
1429
1430 case WM_SETFOCUS:
1431 if (ci->hwndActiveChild && !IsIconic( ci->hwndActiveChild ))
1432 SetFocus( ci->hwndActiveChild );
1433 return 0;
1434
1435 case WM_NCACTIVATE:
1436 if( ci->hwndActiveChild )
1437 SendMessageW(ci->hwndActiveChild, message, wParam, lParam);
1438 break;
1439
1440 case WM_PARENTNOTIFY:
1441 if (LOWORD(wParam) == WM_LBUTTONDOWN)
1442 {
1443 HWND child;
1444 POINT pt;
1445 pt.x = (short)LOWORD(lParam);
1446 pt.y = (short)HIWORD(lParam);
1447 child = ChildWindowFromPoint(hwnd, pt);
1448
1449 TRACE("notification from %p (%li,%li)\n",child,pt.x,pt.y);
1450
1451 if( child && child != hwnd && child != ci->hwndActiveChild )
1452 SetWindowPos(child, 0,0,0,0,0, SWP_NOSIZE | SWP_NOMOVE );
1453 }
1454 return 0;
1455
1456 case WM_SIZE:
1457 if( IsWindow(ci->hwndChildMaximized) )
1458 {
1459 RECT rect;
1460
1461 rect.left = 0;
1462 rect.top = 0;
1463 rect.right = LOWORD(lParam);
1464 rect.bottom = HIWORD(lParam);
1465
1466 AdjustWindowRectEx(&rect, GetWindowLongA(ci->hwndChildMaximized,GWL_STYLE),
1467 0, GetWindowLongA(ci->hwndChildMaximized,GWL_EXSTYLE) );
1468 MoveWindow(ci->hwndChildMaximized, rect.left, rect.top,
1469 rect.right - rect.left, rect.bottom - rect.top, 1);
1470 }
1471 else
1472 MDI_PostUpdate(hwnd, ci, SB_BOTH+1);
1473
1474 break;
1475
1476 case WM_MDICALCCHILDSCROLL:
1477 if( (ci->mdiFlags & MDIF_NEEDUPDATE) && ci->sbRecalc )
1478 {
1479 CalcChildScroll(hwnd, ci->sbRecalc-1);
1480 ci->sbRecalc = 0;
1481 ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1482 }
1483 return 0;
1484 }
1485 return unicode ? DefWindowProcW( hwnd, message, wParam, lParam ) :
1486 DefWindowProcA( hwnd, message, wParam, lParam );
1487 }
1488
1489 /***********************************************************************
1490 * MDIClientWndProcA
1491 */
1492 static LRESULT WINAPI MDIClientWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1493 {
1494 if (!IsWindow(hwnd)) return 0;
1495 return MDIClientWndProc_common( hwnd, message, wParam, lParam, FALSE );
1496 }
1497
1498 /***********************************************************************
1499 * MDIClientWndProcW
1500 */
1501 static LRESULT WINAPI MDIClientWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
1502 {
1503 if (!IsWindow(hwnd)) return 0;
1504 return MDIClientWndProc_common( hwnd, message, wParam, lParam, TRUE );
1505 }
1506
1507 #ifndef __REACTOS__
1508 /***********************************************************************
1509 * DefFrameProc (USER.445)
1510 */
1511 LRESULT WINAPI DefFrameProc16( HWND16 hwnd, HWND16 hwndMDIClient,
1512 UINT16 message, WPARAM16 wParam, LPARAM lParam )
1513 {
1514 switch (message)
1515 {
1516 case WM_SETTEXT:
1517 lParam = (LPARAM)MapSL(lParam);
1518 /* fall through */
1519 case WM_COMMAND:
1520 case WM_NCACTIVATE:
1521 case WM_SETFOCUS:
1522 case WM_SIZE:
1523 return DefFrameProcA( WIN_Handle32(hwnd), WIN_Handle32(hwndMDIClient),
1524 message, wParam, lParam );
1525
1526 case WM_NEXTMENU:
1527 {
1528 MDINEXTMENU next_menu;
1529 DefFrameProcW( WIN_Handle32(hwnd), WIN_Handle32(hwndMDIClient),
1530 message, wParam, (LPARAM)&next_menu );
1531 return MAKELONG( HMENU_16(next_menu.hmenuNext), HWND_16(next_menu.hwndNext) );
1532 }
1533 default:
1534 return DefWindowProc16(hwnd, message, wParam, lParam);
1535 }
1536 }
1537 #endif
1538
1539 /***********************************************************************
1540 * DefFrameProcA (USER32.@)
1541 */
1542 LRESULT WINAPI DefFrameProcA( HWND hwnd, HWND hwndMDIClient,
1543 UINT message, WPARAM wParam, LPARAM lParam)
1544 {
1545 if (hwndMDIClient)
1546 {
1547 switch (message)
1548 {
1549 case WM_SETTEXT:
1550 {
1551 DWORD len = MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, NULL, 0 );
1552 LPWSTR text = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1553 MultiByteToWideChar( CP_ACP, 0, (LPSTR)lParam, -1, text, len );
1554 MDI_UpdateFrameText(hwnd, hwndMDIClient, MDI_REPAINTFRAME, text );
1555 HeapFree( GetProcessHeap(), 0, text );
1556 }
1557 return 1; /* success. FIXME: check text length */
1558
1559 case WM_COMMAND:
1560 case WM_NCACTIVATE:
1561 case WM_NEXTMENU:
1562 case WM_SETFOCUS:
1563 case WM_SIZE:
1564 return DefFrameProcW( hwnd, hwndMDIClient, message, wParam, lParam );
1565 }
1566 }
1567 return DefWindowProcA(hwnd, message, wParam, lParam);
1568 }
1569
1570
1571 /***********************************************************************
1572 * DefFrameProcW (USER32.@)
1573 */
1574 LRESULT WINAPI DefFrameProcW( HWND hwnd, HWND hwndMDIClient,
1575 UINT message, WPARAM wParam, LPARAM lParam)
1576 {
1577 MDICLIENTINFO *ci = get_client_info( hwndMDIClient );
1578
1579 if (ci)
1580 {
1581 switch (message)
1582 {
1583 case WM_COMMAND:
1584 {
1585 WORD id = LOWORD(wParam);
1586 /* check for possible syscommands for maximized MDI child */
1587 if (id < ci->idFirstChild || id >= ci->idFirstChild + ci->nActiveChildren)
1588 {
1589 if( (id - 0xf000) & 0xf00f ) break;
1590 if( !ci->hwndChildMaximized ) break;
1591 switch( id )
1592 {
1593 case SC_SIZE:
1594 case SC_MOVE:
1595 case SC_MINIMIZE:
1596 case SC_MAXIMIZE:
1597 case SC_NEXTWINDOW:
1598 case SC_PREVWINDOW:
1599 case SC_CLOSE:
1600 case SC_RESTORE:
1601 return SendMessageW( ci->hwndChildMaximized, WM_SYSCOMMAND,
1602 wParam, lParam);
1603 }
1604 }
1605 else
1606 {
1607 HWND childHwnd;
1608 if (id - ci->idFirstChild == MDI_MOREWINDOWSLIMIT)
1609 /* User chose "More Windows..." */
1610 childHwnd = MDI_MoreWindowsDialog(hwndMDIClient);
1611 else
1612 /* User chose one of the windows listed in the "Windows" menu */
1613 childHwnd = MDI_GetChildByID(hwndMDIClient,id);
1614
1615 if( childHwnd )
1616 SendMessageW( hwndMDIClient, WM_MDIACTIVATE, (WPARAM)childHwnd, 0 );
1617 }
1618 }
1619 break;
1620
1621 case WM_NCACTIVATE:
1622 SendMessageW(hwndMDIClient, message, wParam, lParam);
1623 break;
1624
1625 case WM_SETTEXT:
1626 MDI_UpdateFrameText(hwnd, hwndMDIClient, MDI_REPAINTFRAME, (LPWSTR)lParam );
1627 return 1; /* success. FIXME: check text length */
1628
1629 case WM_SETFOCUS:
1630 SetFocus(hwndMDIClient);
1631 break;
1632
1633 case WM_SIZE:
1634 MoveWindow(hwndMDIClient, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
1635 break;
1636
1637 case WM_NEXTMENU:
1638 {
1639 /* FIXME */
1640 #ifndef __REACTOS__
1641 MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam;
1642
1643 if (!IsIconic(hwnd) && ci->hwndActiveChild && !ci->hwndChildMaximized)
1644 {
1645 /* control menu is between the frame system menu and
1646 * the first entry of menu bar */
1647 WND *wndPtr = WIN_GetPtr(hwnd);
1648
1649 if( (wParam == VK_LEFT && GetMenu(hwnd) == next_menu->hmenuIn) ||
1650 (wParam == VK_RIGHT && GetSubMenu(wndPtr->hSysMenu, 0) == next_menu->hmenuIn) )
1651 {
1652 WIN_ReleasePtr(wndPtr);
1653 wndPtr = WIN_GetPtr(ci->hwndActiveChild);
1654 next_menu->hmenuNext = GetSubMenu(wndPtr->hSysMenu, 0);
1655 next_menu->hwndNext = ci->hwndActiveChild;
1656 }
1657 WIN_ReleasePtr(wndPtr);
1658 }
1659 #endif
1660 return 0;
1661 }
1662 }
1663 }
1664
1665 return DefWindowProcW( hwnd, message, wParam, lParam );
1666 }
1667
1668
1669 #ifndef __REACTOS__
1670 /***********************************************************************
1671 * DefMDIChildProc (USER.447)
1672 */
1673 LRESULT WINAPI DefMDIChildProc16( HWND16 hwnd, UINT16 message,
1674 WPARAM16 wParam, LPARAM lParam )
1675 {
1676 switch (message)
1677 {
1678 case WM_SETTEXT:
1679 return DefMDIChildProcA( WIN_Handle32(hwnd), message, wParam, (LPARAM)MapSL(lParam) );
1680 case WM_MENUCHAR:
1681 case WM_CLOSE:
1682 case WM_SETFOCUS:
1683 case WM_CHILDACTIVATE:
1684 case WM_SYSCOMMAND:
1685 case WM_SETVISIBLE:
1686 case WM_SIZE:
1687 case WM_SYSCHAR:
1688 return DefMDIChildProcW( WIN_Handle32(hwnd), message, wParam, lParam );
1689 case WM_GETMINMAXINFO:
1690 {
1691 MINMAXINFO16 *mmi16 = (MINMAXINFO16 *)MapSL(lParam);
1692 MINMAXINFO mmi;
1693 STRUCT32_MINMAXINFO16to32( mmi16, &mmi );
1694 DefMDIChildProcW( WIN_Handle32(hwnd), message, wParam, (LPARAM)&mmi );
1695 STRUCT32_MINMAXINFO32to16( &mmi, mmi16 );
1696 return 0;
1697 }
1698 case WM_NEXTMENU:
1699 {
1700 MDINEXTMENU next_menu;
1701 DefMDIChildProcW( WIN_Handle32(hwnd), message, wParam, (LPARAM)&next_menu );
1702 return MAKELONG( HMENU_16(next_menu.hmenuNext), HWND_16(next_menu.hwndNext) );
1703 }
1704 default:
1705 return DefWindowProc16(hwnd, message, wParam, lParam);
1706 }
1707 }
1708 #endif
1709
1710
1711 /***********************************************************************
1712 * DefMDIChildProcA (USER32.@)
1713 */
1714 LRESULT WINAPI DefMDIChildProcA( HWND hwnd, UINT message,
1715 WPARAM wParam, LPARAM lParam )
1716 {
1717 HWND client = GetParent(hwnd);
1718 MDICLIENTINFO *ci = get_client_info( client );
1719
1720 #ifndef __REACTOS__
1721 hwnd = WIN_GetFullHandle( hwnd );
1722 #endif
1723 if (!ci) return DefWindowProcA( hwnd, message, wParam, lParam );
1724
1725 switch (message)
1726 {
1727 case WM_SETTEXT:
1728 DefWindowProcA(hwnd, message, wParam, lParam);
1729 MDI_MenuModifyItem( client, hwnd );
1730 if( ci->hwndChildMaximized == hwnd )
1731 MDI_UpdateFrameText( GetParent(client), client, MDI_REPAINTFRAME, NULL );
1732 return 1; /* success. FIXME: check text length */
1733
1734 case WM_GETMINMAXINFO:
1735 case WM_MENUCHAR:
1736 case WM_CLOSE:
1737 case WM_SETFOCUS:
1738 case WM_CHILDACTIVATE:
1739 case WM_SYSCOMMAND:
1740 /* FIXME */
1741 #ifndef __REACTOS__
1742 case WM_SETVISIBLE:
1743 #endif
1744 case WM_SIZE:
1745 case WM_NEXTMENU:
1746 case WM_SYSCHAR:
1747 return DefMDIChildProcW( hwnd, message, wParam, lParam );
1748 }
1749 return DefWindowProcA(hwnd, message, wParam, lParam);
1750 }
1751
1752
1753 /***********************************************************************
1754 * DefMDIChildProcW (USER32.@)
1755 */
1756 LRESULT WINAPI DefMDIChildProcW( HWND hwnd, UINT message,
1757 WPARAM wParam, LPARAM lParam )
1758 {
1759 HWND client = GetParent(hwnd);
1760 MDICLIENTINFO *ci = get_client_info( client );
1761
1762 #ifndef __REACTOS__
1763 hwnd = WIN_GetFullHandle( hwnd );
1764 #endif
1765 if (!ci) return DefWindowProcW( hwnd, message, wParam, lParam );
1766
1767 switch (message)
1768 {
1769 case WM_SETTEXT:
1770 DefWindowProcW(hwnd, message, wParam, lParam);
1771 MDI_MenuModifyItem( client, hwnd );
1772 if( ci->hwndChildMaximized == hwnd )
1773 MDI_UpdateFrameText( GetParent(client), client, MDI_REPAINTFRAME, NULL );
1774 return 1; /* success. FIXME: check text length */
1775
1776 case WM_GETMINMAXINFO:
1777 MDI_ChildGetMinMaxInfo( client, hwnd, (MINMAXINFO *)lParam );
1778 return 0;
1779
1780 case WM_MENUCHAR:
1781 return 0x00010000; /* MDI children don't have menu bars */
1782
1783 case WM_CLOSE:
1784 SendMessageW( client, WM_MDIDESTROY, (WPARAM)hwnd, 0 );
1785 return 0;
1786
1787 case WM_SETFOCUS:
1788 if (ci->hwndActiveChild != hwnd) MDI_ChildActivate( client, hwnd );
1789 break;
1790
1791 case WM_CHILDACTIVATE:
1792 MDI_ChildActivate( client, hwnd );
1793 return 0;
1794
1795 case WM_SYSCOMMAND:
1796 switch( wParam )
1797 {
1798 case SC_MOVE:
1799 if( ci->hwndChildMaximized == hwnd) return 0;
1800 break;
1801 case SC_RESTORE:
1802 case SC_MINIMIZE:
1803 SetWindowLongW( hwnd, GWL_STYLE,
1804 GetWindowLongW( hwnd, GWL_STYLE ) | WS_SYSMENU );
1805 break;
1806 case SC_MAXIMIZE:
1807 if (ci->hwndChildMaximized == hwnd)
1808 return SendMessageW( GetParent(client), message, wParam, lParam);
1809 SetWindowLongW( hwnd, GWL_STYLE,
1810 GetWindowLongW( hwnd, GWL_STYLE ) & ~WS_SYSMENU );
1811 break;
1812 case SC_NEXTWINDOW:
1813 SendMessageW( client, WM_MDINEXT, 0, 0);
1814 return 0;
1815 case SC_PREVWINDOW:
1816 SendMessageW( client, WM_MDINEXT, 0, 1);
1817 return 0;
1818 }
1819 break;
1820
1821 /* FIXME */
1822 #ifndef __REACTOS__
1823 case WM_SETVISIBLE:
1824 if( ci->hwndChildMaximized) ci->mdiFlags &= ~MDIF_NEEDUPDATE;
1825 else MDI_PostUpdate(client, ci, SB_BOTH+1);
1826 break;
1827 #endif
1828
1829 case WM_SIZE:
1830 if( ci->hwndActiveChild == hwnd && wParam != SIZE_MAXIMIZED )
1831 {
1832 ci->hwndChildMaximized = 0;
1833 MDI_RestoreFrameMenu( GetParent(client), hwnd );
1834 MDI_UpdateFrameText( GetParent(client), client, MDI_REPAINTFRAME, NULL );
1835 }
1836
1837 if( wParam == SIZE_MAXIMIZED )
1838 {
1839 HWND hMaxChild = ci->hwndChildMaximized;
1840
1841 if( hMaxChild == hwnd ) break;
1842 if( hMaxChild)
1843 {
1844 SendMessageW( hMaxChild, WM_SETREDRAW, FALSE, 0 );
1845 MDI_RestoreFrameMenu( GetParent(client), hMaxChild );
1846 ShowWindow( hMaxChild, SW_SHOWNOACTIVATE );
1847 SendMessageW( hMaxChild, WM_SETREDRAW, TRUE, 0 );
1848 }
1849 TRACE("maximizing child %p\n", hwnd );
1850
1851 /* keep track of the maximized window. */
1852 ci->hwndChildMaximized = hwnd; /* !!! */
1853
1854 /* The maximized window should also be the active window */
1855 MDI_ChildActivate( client, hwnd );
1856 MDI_AugmentFrameMenu( GetParent(client), hwnd );
1857 MDI_UpdateFrameText( GetParent(client), client, MDI_REPAINTFRAME, NULL );
1858 }
1859
1860 if( wParam == SIZE_MINIMIZED )
1861 {
1862 HWND switchTo = MDI_GetWindow(ci, hwnd, TRUE, WS_MINIMIZE);
1863
1864 if (switchTo) SendMessageW( switchTo, WM_CHILDACTIVATE, 0, 0);
1865 }
1866 MDI_PostUpdate(client, ci, SB_BOTH+1);
1867 break;
1868
1869 case WM_NEXTMENU:
1870 {
1871 /* FIXME */
1872 #ifndef __REACTOS__
1873 MDINEXTMENU *next_menu = (MDINEXTMENU *)lParam;
1874 HWND parent = GetParent(client);
1875
1876 if( wParam == VK_LEFT ) /* switch to frame system menu */
1877 {
1878 WND *wndPtr = WIN_GetPtr( parent );
1879 next_menu->hmenuNext = GetSubMenu( wndPtr->hSysMenu, 0 );
1880 WIN_ReleasePtr( wndPtr );
1881 }
1882 if( wParam == VK_RIGHT ) /* to frame menu bar */
1883 {
1884 next_menu->hmenuNext = GetMenu(parent);
1885 }
1886 next_menu->hwndNext = parent;
1887 #endif
1888 return 0;
1889 }
1890
1891 case WM_SYSCHAR:
1892 if (wParam == '-')
1893 {
1894 SendMessageW( hwnd, WM_SYSCOMMAND, (WPARAM)SC_KEYMENU, (DWORD)VK_SPACE);
1895 return 0;
1896 }
1897 break;
1898 }
1899 return DefWindowProcW(hwnd, message, wParam, lParam);
1900 }
1901
1902 /**********************************************************************
1903 * CreateMDIWindowA (USER32.@) Creates a MDI child
1904 *
1905 * RETURNS
1906 * Success: Handle to created window
1907 * Failure: NULL
1908 */
1909 HWND WINAPI CreateMDIWindowA(
1910 LPCSTR lpClassName, /* [in] Pointer to registered child class name */
1911 LPCSTR lpWindowName, /* [in] Pointer to window name */
1912 DWORD dwStyle, /* [in] Window style */
1913 INT X, /* [in] Horizontal position of window */
1914 INT Y, /* [in] Vertical position of window */
1915 INT nWidth, /* [in] Width of window */
1916 INT nHeight, /* [in] Height of window */
1917 HWND hWndParent, /* [in] Handle to parent window */
1918 HINSTANCE hInstance, /* [in] Handle to application instance */
1919 LPARAM lParam) /* [in] Application-defined value */
1920 {
1921 MDICLIENTINFO *pCi = get_client_info( hWndParent );
1922 MDICREATESTRUCTA cs;
1923
1924 TRACE("(%s,%s,%ld,%d,%d,%d,%d,%p,%p,%ld)\n",
1925 debugstr_a(lpClassName),debugstr_a(lpWindowName),dwStyle,X,Y,
1926 nWidth,nHeight,hWndParent,hInstance,lParam);
1927
1928 if (!pCi)
1929 {
1930 ERR("bad hwnd for MDI-client: %p\n", hWndParent);
1931 return 0;
1932 }
1933 cs.szClass=lpClassName;
1934 cs.szTitle=lpWindowName;
1935 cs.hOwner=hInstance;
1936 cs.x=X;
1937 cs.y=Y;
1938 cs.cx=nWidth;
1939 cs.cy=nHeight;
1940 cs.style=dwStyle;
1941 cs.lParam=lParam;
1942
1943 return MDICreateChild(hWndParent, pCi, &cs, FALSE);
1944 }
1945
1946 /***********************************************************************
1947 * CreateMDIWindowW (USER32.@) Creates a MDI child
1948 *
1949 * RETURNS
1950 * Success: Handle to created window
1951 * Failure: NULL
1952 */
1953 HWND WINAPI CreateMDIWindowW(
1954 LPCWSTR lpClassName, /* [in] Pointer to registered child class name */
1955 LPCWSTR lpWindowName, /* [in] Pointer to window name */
1956 DWORD dwStyle, /* [in] Window style */
1957 INT X, /* [in] Horizontal position of window */
1958 INT Y, /* [in] Vertical position of window */
1959 INT nWidth, /* [in] Width of window */
1960 INT nHeight, /* [in] Height of window */
1961 HWND hWndParent, /* [in] Handle to parent window */
1962 HINSTANCE hInstance, /* [in] Handle to application instance */
1963 LPARAM lParam) /* [in] Application-defined value */
1964 {
1965 MDICLIENTINFO *pCi = get_client_info( hWndParent );
1966 MDICREATESTRUCTW cs;
1967
1968 TRACE("(%s,%s,%ld,%d,%d,%d,%d,%p,%p,%ld)\n",
1969 debugstr_w(lpClassName), debugstr_w(lpWindowName), dwStyle, X, Y,
1970 nWidth, nHeight, hWndParent, hInstance, lParam);
1971
1972 if (!pCi)
1973 {
1974 ERR("bad hwnd for MDI-client: %p\n", hWndParent);
1975 return 0;
1976 }
1977 cs.szClass = lpClassName;
1978 cs.szTitle = lpWindowName;
1979 cs.hOwner = hInstance;
1980 cs.x = X;
1981 cs.y = Y;
1982 cs.cx = nWidth;
1983 cs.cy = nHeight;
1984 cs.style = dwStyle;
1985 cs.lParam = lParam;
1986
1987 return MDICreateChild(hWndParent, pCi, (MDICREATESTRUCTA *)&cs, TRUE);
1988 }
1989
1990 /**********************************************************************
1991 * TranslateMDISysAccel (USER32.@)
1992 */
1993 BOOL WINAPI TranslateMDISysAccel( HWND hwndClient, LPMSG msg )
1994 {
1995 if (msg->message == WM_KEYDOWN || msg->message == WM_SYSKEYDOWN)
1996 {
1997 MDICLIENTINFO *ci = get_client_info( hwndClient );
1998 WPARAM wParam = 0;
1999
2000 if (!ci || !IsWindowEnabled(ci->hwndActiveChild)) return 0;
2001
2002 /* translate if the Ctrl key is down and Alt not. */
2003
2004 if( (GetKeyState(VK_CONTROL) & 0x8000) && !(GetKeyState(VK_MENU) & 0x8000))
2005 {
2006 switch( msg->wParam )
2007 {
2008 case VK_F6:
2009 case VK_TAB:
2010 wParam = ( GetKeyState(VK_SHIFT) & 0x8000 ) ? SC_NEXTWINDOW : SC_PREVWINDOW;
2011 break;
2012 case VK_F4:
2013 case VK_RBUTTON:
2014 wParam = SC_CLOSE;
2015 break;
2016 default:
2017 return 0;
2018 }
2019 TRACE("wParam = %04x\n", wParam);
2020 SendMessageW(ci->hwndActiveChild, WM_SYSCOMMAND, wParam, (LPARAM)msg->wParam);
2021 return 1;
2022 }
2023 }
2024 return 0; /* failure */
2025 }
2026
2027 /***********************************************************************
2028 * CalcChildScroll (USER32.@)
2029 */
2030 void WINAPI CalcChildScroll( HWND hwnd, INT scroll )
2031 {
2032 SCROLLINFO info;
2033 RECT childRect, clientRect;
2034 #ifndef __REACTOS__
2035 HWND *list;
2036 #else
2037 WINDOWINFO WindowInfo;
2038 HWND hWndCurrent;
2039 #endif
2040 /* The rectangle returned by GetClientRect always has 0,0 as top left
2041 * because it is in client coordinates. The rectangles returned by
2042 * GetWindowRect are in screen coordinates to make this complicated.
2043 *
2044 * Apparently (in ReactOS at least) the rcClient returned by GetWindowInfo
2045 * is in screen coordinates too.
2046 */
2047
2048 GetClientRect( hwnd, &clientRect );
2049 SetRectEmpty( &childRect );
2050
2051 #ifndef __REACTOS__
2052 if ((list = WIN_ListChildren( hwnd )))
2053 {
2054 int i;
2055 for (i = 0; list[i]; i++)
2056 {
2057 DWORD style = GetWindowLongW( list[i], GWL_STYLE );
2058 if (style & WS_MAXIMIZE)
2059 {
2060 HeapFree( GetProcessHeap(), 0, list );
2061 ShowScrollBar( hwnd, SB_BOTH, FALSE );
2062 return;
2063 }
2064 if (style & WS_VISIBLE)
2065 {
2066 WND *pWnd = WIN_FindWndPtr( list[i] );
2067 UnionRect( &childRect, &pWnd->rectWindow, &childRect );
2068 WIN_ReleaseWndPtr( pWnd );
2069 }
2070 }
2071 HeapFree( GetProcessHeap(), 0, list );
2072 }
2073 #else
2074 WindowInfo.cbSize = sizeof(WindowInfo);
2075 if (!GetWindowInfo(hwnd, &WindowInfo))
2076 {
2077 ERR("Can't get window info\n");
2078 return;
2079 }
2080
2081 hWndCurrent = GetWindow(hwnd, GW_CHILD);
2082 while (hWndCurrent != NULL)
2083 {
2084 DWORD style = GetWindowLongW( hWndCurrent, GWL_STYLE );
2085 if (style & WS_MAXIMIZE)
2086 {
2087 ShowScrollBar( hwnd, SB_BOTH, FALSE );
2088 return;
2089 }
2090 if (style & WS_VISIBLE)
2091 {
2092 RECT WindowRect;
2093
2094 GetWindowRect( hWndCurrent, &WindowRect );
2095 OffsetRect(&WindowRect,
2096 -WindowInfo.rcClient.left,
2097 -WindowInfo.rcClient.top);
2098 UnionRect( &childRect, &WindowRect, &childRect );
2099 }
2100 hWndCurrent = GetWindow(hWndCurrent, GW_HWNDNEXT);
2101 }
2102 #endif
2103 UnionRect( &childRect, &clientRect, &childRect );
2104
2105 /* set common info values */
2106 info.cbSize = sizeof(info);
2107 info.fMask = SIF_POS | SIF_RANGE | SIF_PAGE;
2108
2109 /* set the specific scrollbars*/
2110 /* Note how we set nPos to 0 because we scroll the clients instead of
2111 * the window, and we set nPage to 1 bigger than the clientRect because
2112 * otherwise the scrollbar never disables. This causes a somewhat ugly
2113 * effect though while scrolling.
2114 */
2115 switch( scroll )
2116 {
2117 case SB_BOTH:
2118 case SB_HORZ:
2119 info.nMin = childRect.left;
2120 info.nMax = childRect.right;
2121 info.nPos = 0;
2122 info.nPage = 1 + clientRect.right - clientRect.left;
2123 SetScrollInfo(hwnd, SB_HORZ, &info, TRUE);
2124 if (scroll == SB_HORZ) break;
2125 /* fall through */
2126 case SB_VERT:
2127 info.nMin = childRect.top;
2128 info.nMax = childRect.bottom;
2129 info.nPos = 0;
2130 info.nPage = 1 + clientRect.bottom - clientRect.top;
2131 SetScrollInfo(hwnd, SB_VERT, &info, TRUE);
2132 break;
2133 }
2134 }
2135
2136
2137 /***********************************************************************
2138 * ScrollChildren (USER32.@)
2139 */
2140 void WINAPI ScrollChildren(HWND hWnd, UINT uMsg, WPARAM wParam,
2141 LPARAM lParam)
2142 {
2143 INT newPos = -1;
2144 INT curPos, length, minPos, maxPos, shift;
2145 RECT rect;
2146
2147 GetClientRect( hWnd, &rect );
2148
2149 switch(uMsg)
2150 {
2151 case WM_HSCROLL:
2152 GetScrollRange(hWnd,SB_HORZ,&minPos,&maxPos);
2153 curPos = GetScrollPos(hWnd,SB_HORZ);
2154 length = (rect.right - rect.left) / 2;
2155 shift = GetSystemMetrics(SM_CYHSCROLL);
2156 break;
2157 case WM_VSCROLL:
2158 GetScrollRange(hWnd,SB_VERT,&minPos,&maxPos);
2159 curPos = GetScrollPos(hWnd,SB_VERT);
2160 length = (rect.bottom - rect.top) / 2;
2161 shift = GetSystemMetrics(SM_CXVSCROLL);
2162 break;
2163 default:
2164 return;
2165 }
2166
2167 switch( wParam )
2168 {
2169 case SB_LINEUP:
2170 newPos = curPos - shift;
2171 break;
2172 case SB_LINEDOWN:
2173 newPos = curPos + shift;
2174 break;
2175 case SB_PAGEUP:
2176 newPos = curPos - length;
2177 break;
2178 case SB_PAGEDOWN:
2179 newPos = curPos + length;
2180 break;
2181
2182 case SB_THUMBPOSITION:
2183 newPos = LOWORD(lParam);
2184 break;
2185
2186 case SB_THUMBTRACK:
2187 return;
2188
2189 case SB_TOP:
2190 newPos = minPos;
2191 break;
2192 case SB_BOTTOM:
2193 newPos = maxPos;
2194 break;
2195 case SB_ENDSCROLL:
2196 CalcChildScroll(hWnd,(uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ);
2197 return;
2198 }
2199
2200 if( newPos > maxPos )
2201 newPos = maxPos;
2202 else
2203 if( newPos < minPos )
2204 newPos = minPos;
2205
2206 SetScrollPos(hWnd, (uMsg == WM_VSCROLL)?SB_VERT:SB_HORZ , newPos, TRUE);
2207
2208 if( uMsg == WM_VSCROLL )
2209 ScrollWindowEx(hWnd ,0 ,curPos - newPos, NULL, NULL, 0, NULL,
2210 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
2211 else
2212 ScrollWindowEx(hWnd ,curPos - newPos, 0, NULL, NULL, 0, NULL,
2213 SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN );
2214 }
2215
2216
2217 /******************************************************************************
2218 * CascadeWindows (USER32.@) Cascades MDI child windows
2219 *
2220 * RETURNS
2221 * Success: Number of cascaded windows.
2222 * Failure: 0
2223 */
2224 WORD WINAPI
2225 CascadeWindows (HWND hwndParent, UINT wFlags, LPCRECT lpRect,
2226 UINT cKids, const HWND *lpKids)
2227 {
2228 FIXME("(%p,0x%08x,...,%u,...): stub\n", hwndParent, wFlags, cKids);
2229 return 0;
2230 }
2231
2232
2233 /******************************************************************************
2234 * TileWindows (USER32.@) Tiles MDI child windows
2235 *
2236 * RETURNS
2237 * Success: Number of tiled windows.
2238 * Failure: 0
2239 */
2240 WORD WINAPI
2241 TileWindows (HWND hwndParent, UINT wFlags, LPCRECT lpRect,
2242 UINT cKids, const HWND *lpKids)
2243 {
2244 FIXME("(%p,0x%08x,...,%u,...): stub\n", hwndParent, wFlags, cKids);
2245 return 0;
2246 }
2247
2248 /************************************************************************
2249 * "More Windows..." functionality
2250 */
2251
2252 /* MDI_MoreWindowsDlgProc
2253 *
2254 * This function will process the messages sent to the "More Windows..."
2255 * dialog.
2256 * Return values: 0 = cancel pressed
2257 * HWND = ok pressed or double-click in the list...
2258 *
2259 */
2260
2261 static INT_PTR WINAPI MDI_MoreWindowsDlgProc (HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
2262 {
2263 switch (iMsg)
2264 {
2265 case WM_INITDIALOG:
2266 {
2267 #ifdef __REACTOS__
2268 /* FIXME */
2269 return FALSE;
2270 #else
2271 UINT widest = 0;
2272 UINT length;
2273 UINT i;
2274 MDICLIENTINFO *ci = get_client_info( (HWND)lParam );
2275 HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
2276 HWND *list, *sorted_list;
2277
2278 if (!(list = WIN_ListChildren( (HWND)lParam ))) return TRUE;
2279 if (!(sorted_list = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
2280 sizeof(HWND) * ci->nActiveChildren )))
2281 {
2282 HeapFree( GetProcessHeap(), 0, list );
2283 return FALSE;
2284 }
2285
2286 /* Fill the list, sorted by id... */
2287 for (i = 0; list[i]; i++)
2288 {
2289 UINT id = GetWindowLongW( list[i], GWL_ID ) - ci->idFirstChild;
2290 if (id < ci->nActiveChildren) sorted_list[id] = list[i];
2291 }
2292 HeapFree( GetProcessHeap(), 0, list );
2293
2294 for (i = 0; i < ci->nActiveChildren; i++)
2295 {
2296 WCHAR buffer[128];
2297
2298 if (!GetWindowTextW( sorted_list[i], buffer, sizeof(buffer)/sizeof(WCHAR) ))
2299 continue;
2300 SendMessageW(hListBox, LB_ADDSTRING, 0, (LPARAM)buffer );
2301 SendMessageW(hListBox, LB_SETITEMDATA, i, (LPARAM)sorted_list[i] );
2302 length = strlenW(buffer); /* FIXME: should use GetTextExtentPoint */
2303 if (length > widest)
2304 widest = length;
2305 }
2306 /* Make sure the horizontal scrollbar scrolls ok */
2307 SendMessageW(hListBox, LB_SETHORIZONTALEXTENT, widest * 6, 0);
2308
2309 /* Set the current selection */
2310 SendMessageW(hListBox, LB_SETCURSEL, MDI_MOREWINDOWSLIMIT, 0);
2311 return TRUE;
2312 #endif
2313 }
2314
2315 case WM_COMMAND:
2316 switch (LOWORD(wParam))
2317 {
2318 default:
2319 if (HIWORD(wParam) != LBN_DBLCLK) break;
2320 /* fall through */
2321 case IDOK:
2322 {
2323 /* windows are sorted by menu ID, so we must return the
2324 * window associated to the given id
2325 */
2326 HWND hListBox = GetDlgItem(hDlg, MDI_IDC_LISTBOX);
2327 UINT index = SendMessageW(hListBox, LB_GETCURSEL, 0, 0);
2328 LRESULT res = SendMessageW(hListBox, LB_GETITEMDATA, index, 0);
2329 EndDialog(hDlg, res);
2330 return TRUE;
2331 }
2332 case IDCANCEL:
2333 EndDialog(hDlg, 0);
2334 return TRUE;
2335 }
2336 break;
2337 }
2338 return FALSE;
2339 }
2340
2341 /*
2342 *
2343 * MDI_MoreWindowsDialog
2344 *
2345 * Prompts the user with a listbox containing the opened
2346 * documents. The user can then choose a windows and click
2347 * on OK to set the current window to the one selected, or
2348 * CANCEL to cancel. The function returns a handle to the
2349 * selected window.
2350 */
2351
2352 static HWND MDI_MoreWindowsDialog(HWND hwnd)
2353 {
2354 LPCVOID template;
2355 HRSRC hRes;
2356 HANDLE hDlgTmpl;
2357
2358 hRes = FindResourceA(GetModuleHandleA("USER32"), "MDI_MOREWINDOWS", (LPSTR)RT_DIALOG);
2359
2360 if (hRes == 0)
2361 return 0;
2362
2363 hDlgTmpl = LoadResource(GetModuleHandleA("USER32"), hRes );
2364
2365 if (hDlgTmpl == 0)
2366 return 0;
2367
2368 template = LockResource( hDlgTmpl );
2369
2370 if (template == 0)
2371 return 0;
2372
2373 return (HWND) DialogBoxIndirectParamA(GetModuleHandleA("USER32"),
2374 (LPDLGTEMPLATE) template,
2375 hwnd, MDI_MoreWindowsDlgProc, (LPARAM) hwnd);
2376 }
2377
2378 /*
2379 *
2380 * MDI_SwapMenuItems
2381 *
2382 * Will swap the menu IDs for the given 2 positions.
2383 * pos1 and pos2 are menu IDs
2384 *
2385 *
2386 */
2387
2388 static void MDI_SwapMenuItems(HWND parent, UINT pos1, UINT pos2)
2389 {
2390 /* FIXME */
2391 #ifndef __REACTOS__
2392 HWND *list;
2393 int i;
2394
2395 if (!(list = WIN_ListChildren( parent ))) return;
2396 for (i = 0; list[i]; i++)
2397 {
2398 UINT id = GetWindowLongW( list[i], GWL_ID );
2399 if (id == pos1) SetWindowLongW( list[i], GWL_ID, pos2 );
2400 else if (id == pos2) SetWindowLongW( list[i], GWL_ID, pos1 );
2401 }
2402 HeapFree( GetProcessHeap(), 0, list );
2403 #endif
2404 }