]> git.saurik.com Git - wxWidgets.git/blame - src/msw/mdi.cpp
use (new) safer GetTraitsIfExists() in wxMutexGuiEnter/Leave() to avoid crashing...
[wxWidgets.git] / src / msw / mdi.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
a967ef9d
VZ
2// Name: src/msw/mdi.cpp
3// Purpose: MDI classes for wxMSW
2bda0e17
KB
4// Author: Julian Smart
5// Modified by:
6// Created: 04/01/98
7// RCS-ID: $Id$
6c9a19aa 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
a23fd0e1
VZ
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
a23fd0e1 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
efd17a1d 27#if wxUSE_MDI && !defined(__WXUNIVERSAL__)
b442f107 28
59cf2e49
WS
29#include "wx/mdi.h"
30
2bda0e17 31#ifndef WX_PRECOMP
a23fd0e1
VZ
32 #include "wx/frame.h"
33 #include "wx/menu.h"
34 #include "wx/app.h"
35 #include "wx/utils.h"
36 #include "wx/dialog.h"
3304646d 37 #include "wx/statusbr.h"
a23fd0e1 38 #include "wx/settings.h"
0c589ad0
BM
39 #include "wx/intl.h"
40 #include "wx/log.h"
4e3e485b 41 #include "wx/toolbar.h"
2bda0e17
KB
42#endif
43
e27d9a91 44#include "wx/stockitem.h"
2bda0e17
KB
45#include "wx/msw/private.h"
46
2bda0e17
KB
47#include <string.h>
48
a23fd0e1
VZ
49// ---------------------------------------------------------------------------
50// global variables
51// ---------------------------------------------------------------------------
52
e1a6fc11 53extern wxMenu *wxCurrentPopupMenu;
2bda0e17 54
3ca6a5f0 55extern const wxChar *wxMDIFrameClassName; // from app.cpp
2ffa221c 56extern const wxChar *wxMDIChildFrameClassName;
3ca6a5f0 57extern const wxChar *wxMDIChildFrameClassNameNoRedraw;
ac6482e0 58extern void wxRemoveHandleAssociation(wxWindow *win);
a23fd0e1
VZ
59
60// ---------------------------------------------------------------------------
61// constants
62// ---------------------------------------------------------------------------
63
42e69d6b 64static const int IDM_WINDOWTILEHOR = 4001;
a23fd0e1
VZ
65static const int IDM_WINDOWCASCADE = 4002;
66static const int IDM_WINDOWICONS = 4003;
67static const int IDM_WINDOWNEXT = 4004;
42e69d6b 68static const int IDM_WINDOWTILEVERT = 4005;
4f3b37fd 69static const int IDM_WINDOWPREV = 4006;
a23fd0e1
VZ
70
71// This range gives a maximum of 500 MDI children. Should be enough :-)
72static const int wxFIRST_MDI_CHILD = 4100;
73static const int wxLAST_MDI_CHILD = 4600;
2bda0e17 74
42e69d6b
VZ
75// ---------------------------------------------------------------------------
76// private functions
77// ---------------------------------------------------------------------------
78
79// set the MDI menus (by sending the WM_MDISETMENU message) and update the menu
80// of the parent of win (which is supposed to be the MDI client window)
81static void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow);
82
83// insert the window menu (subMenu) into menu just before "Help" submenu or at
84// the very end if not found
85static void InsertWindowMenu(wxWindow *win, WXHMENU menu, HMENU subMenu);
86
df61c009
JS
87// Remove the window menu
88static void RemoveWindowMenu(wxWindow *win, WXHMENU menu);
89
42e69d6b
VZ
90// is this an id of an MDI child?
91inline bool IsMdiCommandId(int id)
92{
93 return (id >= wxFIRST_MDI_CHILD) && (id <= wxLAST_MDI_CHILD);
94}
95
4e152a23 96// unpack the parameters of WM_MDIACTIVATE message
2917e920
BM
97static void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
98 WXWORD *activate, WXHWND *hwndAct, WXHWND *hwndDeact);
42e69d6b 99
4e152a23
VZ
100// return the HMENU of the MDI menu
101static inline HMENU GetMDIWindowMenu(wxMDIParentFrame *frame)
102{
103 wxMenu *menu = frame->GetWindowMenu();
104 return menu ? GetHmenuOf(menu) : 0;
105}
106
a23fd0e1
VZ
107// ===========================================================================
108// implementation
109// ===========================================================================
110
111// ---------------------------------------------------------------------------
112// wxWin macros
113// ---------------------------------------------------------------------------
2bda0e17 114
225fe9d6
VZ
115IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame, wxFrame)
116IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame, wxFrame)
117IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow, wxWindow)
2bda0e17
KB
118
119BEGIN_EVENT_TABLE(wxMDIParentFrame, wxFrame)
a23fd0e1 120 EVT_SIZE(wxMDIParentFrame::OnSize)
6bbe97b7 121 EVT_ICONIZE(wxMDIParentFrame::OnIconized)
a23fd0e1 122 EVT_SYS_COLOUR_CHANGED(wxMDIParentFrame::OnSysColourChanged)
2bda0e17
KB
123END_EVENT_TABLE()
124
f6bcfd97
BP
125BEGIN_EVENT_TABLE(wxMDIChildFrame, wxFrame)
126 EVT_IDLE(wxMDIChildFrame::OnIdle)
127END_EVENT_TABLE()
128
2bda0e17 129BEGIN_EVENT_TABLE(wxMDIClientWindow, wxWindow)
a23fd0e1 130 EVT_SCROLL(wxMDIClientWindow::OnScroll)
2bda0e17
KB
131END_EVENT_TABLE()
132
42e69d6b
VZ
133// ===========================================================================
134// wxMDIParentFrame: the frame which contains the client window which manages
135// the children
136// ===========================================================================
2bda0e17 137
c2dcfdef 138wxMDIParentFrame::wxMDIParentFrame()
2bda0e17
KB
139{
140 m_clientWindow = NULL;
141 m_currentChild = NULL;
df61c009 142 m_windowMenu = (wxMenu*) NULL;
4f8090e0 143 m_parentFrameActive = true;
2bda0e17
KB
144}
145
146bool wxMDIParentFrame::Create(wxWindow *parent,
a23fd0e1
VZ
147 wxWindowID id,
148 const wxString& title,
149 const wxPoint& pos,
150 const wxSize& size,
151 long style,
152 const wxString& name)
2bda0e17 153{
2bda0e17
KB
154 m_clientWindow = NULL;
155 m_currentChild = NULL;
df61c009 156
3d8dea7e
VZ
157 // this style can be used to prevent a window from having the standard MDI
158 // "Window" menu
159 if ( style & wxFRAME_NO_WINDOW_MENU )
160 {
161 m_windowMenu = (wxMenu *)NULL;
162 }
163 else // normal case: we have the window menu, so construct it
df61c009 164 {
df61c009
JS
165 m_windowMenu = new wxMenu;
166
b0a2157c
VZ
167 m_windowMenu->Append(IDM_WINDOWCASCADE, _("&Cascade"));
168 m_windowMenu->Append(IDM_WINDOWTILEHOR, _("Tile &Horizontally"));
169 m_windowMenu->Append(IDM_WINDOWTILEVERT, _("Tile &Vertically"));
df61c009 170 m_windowMenu->AppendSeparator();
b0a2157c
VZ
171 m_windowMenu->Append(IDM_WINDOWICONS, _("&Arrange Icons"));
172 m_windowMenu->Append(IDM_WINDOWNEXT, _("&Next"));
4f3b37fd 173 m_windowMenu->Append(IDM_WINDOWPREV, _("&Previous"));
df61c009
JS
174 }
175
4f8090e0 176 m_parentFrameActive = true;
2bda0e17
KB
177
178 if (!parent)
179 wxTopLevelWindows.Append(this);
180
181 SetName(name);
182 m_windowStyle = style;
183
b225f659
VZ
184 if ( parent )
185 parent->AddChild(this);
2bda0e17 186
598ddd96 187 if ( id != wxID_ANY )
2bda0e17
KB
188 m_windowId = id;
189 else
b225f659 190 m_windowId = NewControlId();
2bda0e17 191
912c192f
VZ
192 WXDWORD exflags;
193 WXDWORD msflags = MSWGetCreateWindowFlags(&exflags);
ea723360
JS
194 msflags &= ~WS_VSCROLL;
195 msflags &= ~WS_HSCROLL;
2bda0e17 196
b225f659 197 if ( !wxWindow::MSWCreate(wxMDIFrameClassName,
e0a050e3 198 title.wx_str(),
b225f659
VZ
199 pos, size,
200 msflags,
201 exflags) )
3ca6a5f0 202 {
4f8090e0 203 return false;
3ca6a5f0 204 }
2bda0e17 205
5bee6b2b
VZ
206 SetOwnBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE));
207
f6bcfd97 208 // unlike (almost?) all other windows, frames are created hidden
4f8090e0 209 m_isShown = false;
f6bcfd97 210
4f8090e0 211 return true;
2bda0e17
KB
212}
213
c2dcfdef 214wxMDIParentFrame::~wxMDIParentFrame()
2bda0e17 215{
d1e44484 216 // see comment in ~wxMDIChildFrame
1904aa72 217#if wxUSE_TOOLBAR
f048e32f 218 m_frameToolBar = NULL;
1904aa72 219#endif
67a99992 220#if wxUSE_STATUSBAR
c0bcc480 221 m_frameStatusBar = NULL;
67a99992 222#endif // wxUSE_STATUSBAR
f048e32f 223
d1e44484
VZ
224 DestroyChildren();
225
df61c009
JS
226 if (m_windowMenu)
227 {
228 delete m_windowMenu;
229 m_windowMenu = (wxMenu*) NULL;
230 }
2bda0e17 231
4e152a23
VZ
232 // the MDI frame menubar is not automatically deleted by Windows unlike for
233 // the normal frames
234 if ( m_hMenu )
235 {
236 ::DestroyMenu((HMENU)m_hMenu);
7c46a16b 237 m_hMenu = (WXHMENU)NULL;
4e152a23
VZ
238 }
239
42e69d6b
VZ
240 if ( m_clientWindow )
241 {
242 if ( m_clientWindow->MSWGetOldWndProc() )
243 m_clientWindow->UnsubclassWin();
2bda0e17 244
42e69d6b
VZ
245 m_clientWindow->SetHWND(0);
246 delete m_clientWindow;
247 }
2bda0e17
KB
248}
249
1e6feb95
VZ
250#if wxUSE_MENUS_NATIVE
251
42e69d6b 252void wxMDIParentFrame::InternalSetMenuBar()
2bda0e17 253{
4f8090e0 254 m_parentFrameActive = true;
2bda0e17 255
4e152a23 256 InsertWindowMenu(GetClientWindow(), m_hMenu, GetMDIWindowMenu(this));
2bda0e17
KB
257}
258
1e6feb95
VZ
259#endif // wxUSE_MENUS_NATIVE
260
df61c009
JS
261void wxMDIParentFrame::SetWindowMenu(wxMenu* menu)
262{
263 if (m_windowMenu)
264 {
265 if (GetMenuBar())
266 {
267 // Remove old window menu
268 RemoveWindowMenu(GetClientWindow(), m_hMenu);
269 }
270
271 delete m_windowMenu;
272 m_windowMenu = (wxMenu*) NULL;
273 }
4e152a23 274
df61c009
JS
275 if (menu)
276 {
277 m_windowMenu = menu;
278 if (GetMenuBar())
b0a2157c
VZ
279 {
280 InsertWindowMenu(GetClientWindow(), m_hMenu,
88c49a0f 281 GetHmenuOf(m_windowMenu));
b0a2157c 282 }
df61c009
JS
283 }
284}
285
6aca4628
CE
286void wxMDIParentFrame::DoMenuUpdates(wxMenu* menu)
287{
288 wxMDIChildFrame *child = GetActiveChild();
289 if ( child )
290 {
291 wxEvtHandler* source = child->GetEventHandler();
292 wxMenuBar* bar = child->GetMenuBar();
293
294 if (menu)
295 {
296 menu->UpdateUI(source);
297 }
298 else
299 {
300 if ( bar != NULL )
301 {
302 int nCount = bar->GetMenuCount();
303 for (int n = 0; n < nCount; n++)
92218ce6 304 bar->GetMenu(n)->UpdateUI(source);
6aca4628
CE
305 }
306 }
307 }
308 else
309 {
310 wxFrameBase::DoMenuUpdates(menu);
311 }
312}
313
10816efb
VZ
314const wxMenuItem *wxMDIParentFrame::FindItemInMenuBar(int menuId) const
315{
316 const wxMenuItem *item = wxFrame::FindItemInMenuBar(menuId);
317 if ( !item && m_currentChild )
318 {
319 item = m_currentChild->FindItemInMenuBar(menuId);
320 }
321
322 return item;
323}
324
6bbe97b7 325void wxMDIParentFrame::UpdateClientSize()
2bda0e17 326{
2bda0e17 327 if ( GetClientWindow() )
42e69d6b
VZ
328 {
329 int width, height;
330 GetClientSize(&width, &height);
2bda0e17 331
42e69d6b
VZ
332 GetClientWindow()->SetSize(0, 0, width, height);
333 }
2bda0e17
KB
334}
335
6bbe97b7
VZ
336void wxMDIParentFrame::OnSize(wxSizeEvent& WXUNUSED(event))
337{
338 UpdateClientSize();
339
340 // do not call event.Skip() here, it somehow messes up MDI client window
341}
342
343void wxMDIParentFrame::OnIconized(wxIconizeEvent& event)
344{
345 event.Skip();
346
6ad7799c 347 if ( !event.IsIconized() )
6bbe97b7 348 UpdateClientSize();
6bbe97b7
VZ
349}
350
2bda0e17 351// Returns the active MDI child window
c2dcfdef 352wxMDIChildFrame *wxMDIParentFrame::GetActiveChild() const
2bda0e17 353{
a23fd0e1
VZ
354 HWND hWnd = (HWND)::SendMessage(GetWinHwnd(GetClientWindow()),
355 WM_MDIGETACTIVE, 0, 0L);
356 if ( hWnd == 0 )
357 return NULL;
358 else
359 return (wxMDIChildFrame *)wxFindWinFromHandle((WXHWND) hWnd);
2bda0e17
KB
360}
361
a23fd0e1
VZ
362// Create the client window class (don't Create the window, just return a new
363// class)
c2dcfdef 364wxMDIClientWindow *wxMDIParentFrame::OnCreateClient()
2bda0e17 365{
a23fd0e1 366 return new wxMDIClientWindow;
2bda0e17
KB
367}
368
369// Responds to colour changes, and passes event on to children.
370void wxMDIParentFrame::OnSysColourChanged(wxSysColourChangedEvent& event)
371{
372 if ( m_clientWindow )
373 {
a756f210 374 m_clientWindow->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE));
2bda0e17
KB
375 m_clientWindow->Refresh();
376 }
2bda0e17 377
42e69d6b 378 event.Skip();
2bda0e17
KB
379}
380
82c9f85c
VZ
381WXHICON wxMDIParentFrame::GetDefaultIcon() const
382{
94826170
VZ
383 // we don't have any standard icons (any more)
384 return (WXHICON)0;
82c9f85c
VZ
385}
386
42e69d6b 387// ---------------------------------------------------------------------------
2bda0e17 388// MDI operations
42e69d6b
VZ
389// ---------------------------------------------------------------------------
390
c2dcfdef 391void wxMDIParentFrame::Cascade()
2bda0e17 392{
a23fd0e1 393 ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDICASCADE, 0, 0);
2bda0e17
KB
394}
395
0d97c090 396void wxMDIParentFrame::Tile(wxOrientation orient)
2bda0e17 397{
0d97c090
VZ
398 wxASSERT_MSG( orient == wxHORIZONTAL || orient == wxVERTICAL,
399 _T("invalid orientation value") );
400
401 ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDITILE,
402 orient == wxHORIZONTAL ? MDITILE_HORIZONTAL
403 : MDITILE_VERTICAL, 0);
2bda0e17
KB
404}
405
c2dcfdef 406void wxMDIParentFrame::ArrangeIcons()
2bda0e17 407{
a23fd0e1 408 ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDIICONARRANGE, 0, 0);
2bda0e17
KB
409}
410
c2dcfdef 411void wxMDIParentFrame::ActivateNext()
2bda0e17 412{
a23fd0e1 413 ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDINEXT, 0, 0);
2bda0e17
KB
414}
415
c2dcfdef 416void wxMDIParentFrame::ActivatePrevious()
2bda0e17 417{
a23fd0e1 418 ::SendMessage(GetWinHwnd(GetClientWindow()), WM_MDINEXT, 0, 1);
2bda0e17
KB
419}
420
42e69d6b 421// ---------------------------------------------------------------------------
a23fd0e1 422// the MDI parent frame window proc
42e69d6b
VZ
423// ---------------------------------------------------------------------------
424
c140b7e7 425WXLRESULT wxMDIParentFrame::MSWWindowProc(WXUINT message,
a23fd0e1
VZ
426 WXWPARAM wParam,
427 WXLPARAM lParam)
2bda0e17 428{
c140b7e7 429 WXLRESULT rc = 0;
4f8090e0 430 bool processed = false;
2bda0e17 431
a23fd0e1
VZ
432 switch ( message )
433 {
42e69d6b
VZ
434 case WM_ACTIVATE:
435 {
436 WXWORD state, minimized;
437 WXHWND hwnd;
438 UnpackActivate(wParam, lParam, &state, &minimized, &hwnd);
439
440 processed = HandleActivate(state, minimized != 0, hwnd);
441 }
442 break;
443
444 case WM_COMMAND:
445 {
446 WXWORD id, cmd;
447 WXHWND hwnd;
448 UnpackCommand(wParam, lParam, &id, &hwnd, &cmd);
449
450 (void)HandleCommand(id, cmd, hwnd);
451
452 // even if the frame didn't process it, there is no need to try it
01ebf752 453 // once again (i.e. call wxFrame::HandleCommand()) - we just did it,
42e69d6b 454 // so pretend we processed the message anyhow
4f8090e0 455 processed = true;
42e69d6b 456 }
b3818fbe
VZ
457
458 // always pass this message DefFrameProc(), otherwise MDI menu
01ebf752 459 // commands (and sys commands - more surprisingly!) won't work
b3818fbe 460 MSWDefWindowProc(message, wParam, lParam);
42e69d6b
VZ
461 break;
462
a23fd0e1
VZ
463 case WM_CREATE:
464 m_clientWindow = OnCreateClient();
465 // Uses own style for client style
466 if ( !m_clientWindow->CreateClient(this, GetWindowStyleFlag()) )
467 {
468 wxLogMessage(_("Failed to create MDI parent frame."));
2bda0e17 469
a23fd0e1
VZ
470 rc = -1;
471 }
2bda0e17 472
4f8090e0 473 processed = true;
a23fd0e1 474 break;
2bda0e17 475
a23fd0e1 476 case WM_ERASEBKGND:
4f8090e0 477 processed = true;
2bda0e17 478
a23fd0e1 479 // we erase background ourselves
4f8090e0 480 rc = true;
a23fd0e1
VZ
481 break;
482
b3818fbe 483 case WM_SIZE:
31f658e4
CE
484 // though we don't (usually) resize the MDI client to exactly fit the
485 // client area we need to pass this one to DefFrameProc to allow the children to show
b3818fbe 486 break;
a23fd0e1 487 }
2bda0e17 488
a23fd0e1
VZ
489 if ( !processed )
490 rc = wxFrame::MSWWindowProc(message, wParam, lParam);
2bda0e17 491
a23fd0e1 492 return rc;
2bda0e17
KB
493}
494
42e69d6b 495bool wxMDIParentFrame::HandleActivate(int state, bool minimized, WXHWND activate)
2bda0e17 496{
4f8090e0 497 bool processed = false;
a23fd0e1 498
42e69d6b 499 if ( wxWindow::HandleActivate(state, minimized, activate) )
a23fd0e1
VZ
500 {
501 // already processed
4f8090e0 502 processed = true;
a23fd0e1 503 }
2bda0e17
KB
504
505 // If this window is an MDI parent, we must also send an OnActivate message
506 // to the current child.
42e69d6b 507 if ( (m_currentChild != NULL) &&
a23fd0e1 508 ((state == WA_ACTIVE) || (state == WA_CLICKACTIVE)) )
c2dcfdef 509 {
4f8090e0 510 wxActivateEvent event(wxEVT_ACTIVATE, true, m_currentChild->GetId());
debe6624 511 event.SetEventObject( m_currentChild );
937013e0 512 if ( m_currentChild->HandleWindowEvent(event) )
4f8090e0 513 processed = true;
2bda0e17 514 }
a23fd0e1
VZ
515
516 return processed;
2bda0e17
KB
517}
518
8a0dce9c 519bool wxMDIParentFrame::HandleCommand(WXWORD id_, WXWORD cmd, WXHWND hwnd)
2bda0e17 520{
8a0dce9c
VZ
521 // sign extend to int from short before comparing with the other int ids
522 int id = (signed short)id_;
523
2bda0e17 524 // In case it's e.g. a toolbar.
42e69d6b 525 if ( hwnd )
e1a6fc11 526 {
42e69d6b
VZ
527 wxWindow *win = wxFindWinFromHandle(hwnd);
528 if ( win )
529 return win->MSWCommand(cmd, id);
e1a6fc11
JS
530 }
531
7dbe942a
JS
532 if (wxCurrentPopupMenu)
533 {
534 wxMenu *popupMenu = wxCurrentPopupMenu;
535 wxCurrentPopupMenu = NULL;
536 if (popupMenu->MSWCommand(cmd, id))
537 return true;
538 }
539
42e69d6b
VZ
540 // is it one of standard MDI commands?
541 WXWPARAM wParam = 0;
4f3b37fd 542 WXLPARAM lParam = 0;
42e69d6b
VZ
543 int msg;
544 switch ( id )
2bda0e17 545 {
42e69d6b
VZ
546 case IDM_WINDOWCASCADE:
547 msg = WM_MDICASCADE;
548 wParam = MDITILE_SKIPDISABLED;
549 break;
550
551 case IDM_WINDOWTILEHOR:
552 wParam |= MDITILE_HORIZONTAL;
553 // fall through
554
555 case IDM_WINDOWTILEVERT:
556 if ( !wParam )
557 wParam = MDITILE_VERTICAL;
558 msg = WM_MDITILE;
559 wParam |= MDITILE_SKIPDISABLED;
560 break;
561
562 case IDM_WINDOWICONS:
563 msg = WM_MDIICONARRANGE;
564 break;
565
566 case IDM_WINDOWNEXT:
567 msg = WM_MDINEXT;
4f3b37fd
JS
568 lParam = 0; // next child
569 break;
570
571 case IDM_WINDOWPREV:
572 msg = WM_MDINEXT;
573 lParam = 1; // previous child
42e69d6b
VZ
574 break;
575
576 default:
577 msg = 0;
2bda0e17 578 }
c2dcfdef 579
42e69d6b 580 if ( msg )
2bda0e17 581 {
4f3b37fd 582 ::SendMessage(GetWinHwnd(GetClientWindow()), msg, wParam, lParam);
42e69d6b 583
4f8090e0 584 return true;
2bda0e17 585 }
42e69d6b
VZ
586
587 // FIXME VZ: what does this test do??
588 if (id >= 0xF000)
2bda0e17 589 {
4f8090e0 590 return false; // Get WndProc to call default proc
2bda0e17 591 }
42e69d6b
VZ
592
593 if ( IsMdiCommandId(id) )
2bda0e17 594 {
222ed1d6 595 wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
42e69d6b 596 while ( node )
2bda0e17 597 {
d162a7ee 598 wxWindow *child = node->GetData();
42e69d6b 599 if ( child->GetHWND() )
2bda0e17 600 {
6756a5f8
VZ
601 int childId = wxGetWindowId(child->GetHWND());
602 if ( childId == (signed short)id )
42e69d6b
VZ
603 {
604 ::SendMessage( GetWinHwnd(GetClientWindow()),
605 WM_MDIACTIVATE,
606 (WPARAM)child->GetHWND(), 0);
4f8090e0 607 return true;
42e69d6b 608 }
2bda0e17 609 }
42e69d6b 610 node = node->GetNext();
2bda0e17 611 }
2bda0e17 612 }
42e69d6b 613 else if ( m_parentFrameActive )
2bda0e17 614 {
42e69d6b
VZ
615 return ProcessCommand(id);
616 }
617 else if ( m_currentChild )
618 {
619 return m_currentChild->HandleCommand(id, cmd, hwnd);
620 }
621 else
622 {
623 // this shouldn't happen because it means that our messages are being
624 // lost (they're not sent to the parent frame nor to the children)
f6bcfd97 625 wxFAIL_MSG(wxT("MDI parent frame is not active, yet there is no active MDI child?"));
2bda0e17 626 }
2bda0e17 627
4f8090e0 628 return false;
2bda0e17
KB
629}
630
c140b7e7 631WXLRESULT wxMDIParentFrame::MSWDefWindowProc(WXUINT message,
42e69d6b
VZ
632 WXWPARAM wParam,
633 WXLPARAM lParam)
2bda0e17 634{
c2dcfdef
VZ
635 WXHWND clientWnd;
636 if ( GetClientWindow() )
637 clientWnd = GetClientWindow()->GetHWND();
638 else
639 clientWnd = 0;
2bda0e17 640
a23fd0e1 641 return DefFrameProc(GetHwnd(), (HWND)clientWnd, message, wParam, lParam);
2bda0e17
KB
642}
643
57a7b7c1
JS
644bool wxMDIParentFrame::MSWTranslateMessage(WXMSG* msg)
645{
a23fd0e1 646 MSG *pMsg = (MSG *)msg;
2bda0e17 647
f6bcfd97 648 // first let the current child get it
a23fd0e1
VZ
649 if ( m_currentChild && m_currentChild->GetHWND() &&
650 m_currentChild->MSWTranslateMessage(msg) )
651 {
4f8090e0 652 return true;
a23fd0e1 653 }
2bda0e17 654
f6bcfd97
BP
655 // then try out accel table (will also check the menu accels)
656 if ( wxFrame::MSWTranslateMessage(msg) )
a23fd0e1 657 {
4f8090e0 658 return true;
a23fd0e1 659 }
2bda0e17 660
f6bcfd97 661 // finally, check for MDI specific built in accel keys
a23fd0e1
VZ
662 if ( pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN )
663 {
664 if ( ::TranslateMDISysAccel(GetWinHwnd(GetClientWindow()), pMsg))
4f8090e0 665 return true;
a23fd0e1 666 }
57a7b7c1 667
4f8090e0 668 return false;
2bda0e17
KB
669}
670
42e69d6b 671// ===========================================================================
a23fd0e1 672// wxMDIChildFrame
42e69d6b 673// ===========================================================================
2bda0e17 674
f6bcfd97 675void wxMDIChildFrame::Init()
2bda0e17 676{
4f8090e0 677 m_needsResize = true;
2596e9fb 678 m_needsInitialShow = true;
2bda0e17
KB
679}
680
681bool wxMDIChildFrame::Create(wxMDIParentFrame *parent,
a23fd0e1
VZ
682 wxWindowID id,
683 const wxString& title,
684 const wxPoint& pos,
685 const wxSize& size,
686 long style,
687 const wxString& name)
2bda0e17 688{
2bda0e17
KB
689 SetName(name);
690
598ddd96 691 if ( id != wxID_ANY )
2bda0e17
KB
692 m_windowId = id;
693 else
cf2810aa 694 m_windowId = NewControlId();
2bda0e17 695
42e69d6b
VZ
696 if ( parent )
697 {
698 parent->AddChild(this);
699 }
2bda0e17 700
2bda0e17
KB
701 int x = pos.x;
702 int y = pos.y;
703 int width = size.x;
704 int height = size.y;
705
706 MDICREATESTRUCT mcs;
c2dcfdef 707
e441e1f4
VZ
708 mcs.szClass = style & wxFULL_REPAINT_ON_RESIZE
709 ? wxMDIChildFrameClassName
710 : wxMDIChildFrameClassNameNoRedraw;
e0a050e3 711 mcs.szTitle = title.wx_str();
2bda0e17 712 mcs.hOwner = wxGetInstance();
598ddd96 713 if (x != wxDefaultCoord)
42e69d6b
VZ
714 mcs.x = x;
715 else
716 mcs.x = CW_USEDEFAULT;
2bda0e17 717
598ddd96 718 if (y != wxDefaultCoord)
42e69d6b
VZ
719 mcs.y = y;
720 else
721 mcs.y = CW_USEDEFAULT;
2bda0e17 722
598ddd96 723 if (width != wxDefaultCoord)
42e69d6b
VZ
724 mcs.cx = width;
725 else
726 mcs.cx = CW_USEDEFAULT;
2bda0e17 727
598ddd96 728 if (height != wxDefaultCoord)
42e69d6b
VZ
729 mcs.cy = height;
730 else
731 mcs.cy = CW_USEDEFAULT;
2bda0e17 732
2596e9fb 733 DWORD msflags = WS_OVERLAPPED | WS_CLIPCHILDREN;
2bda0e17
KB
734 if (style & wxMINIMIZE_BOX)
735 msflags |= WS_MINIMIZEBOX;
736 if (style & wxMAXIMIZE_BOX)
737 msflags |= WS_MAXIMIZEBOX;
1c067fe3 738 if (style & wxRESIZE_BORDER)
2bda0e17
KB
739 msflags |= WS_THICKFRAME;
740 if (style & wxSYSTEM_MENU)
741 msflags |= WS_SYSMENU;
742 if ((style & wxMINIMIZE) || (style & wxICONIZE))
743 msflags |= WS_MINIMIZE;
744 if (style & wxMAXIMIZE)
745 msflags |= WS_MAXIMIZE;
746 if (style & wxCAPTION)
747 msflags |= WS_CAPTION;
748
749 mcs.style = msflags;
750
751 mcs.lParam = 0;
752
b225f659
VZ
753 wxWindowCreationHook hook(this);
754
3ca6a5f0 755 m_hWnd = (WXHWND)::SendMessage(GetWinHwnd(parent->GetClientWindow()),
dca0f651 756 WM_MDICREATE, 0, (LPARAM)&mcs);
2bda0e17 757
05ae668c
VZ
758 if ( !m_hWnd )
759 {
760 wxLogLastError(_T("WM_MDICREATE"));
761 return false;
762 }
763
764 SubclassWin(m_hWnd);
2bda0e17 765
4f8090e0 766 return true;
2bda0e17
KB
767}
768
c2dcfdef 769wxMDIChildFrame::~wxMDIChildFrame()
2bda0e17 770{
b38e5da1
VZ
771 // if we hadn't been created, there is nothing to destroy
772 if ( !m_hWnd )
773 return;
774
d1e44484
VZ
775 // will be destroyed by DestroyChildren() but reset them before calling it
776 // to avoid using dangling pointers if a callback comes in the meanwhile
1904aa72 777#if wxUSE_TOOLBAR
627a3091 778 m_frameToolBar = NULL;
1904aa72 779#endif
67a99992 780#if wxUSE_STATUSBAR
627a3091 781 m_frameStatusBar = NULL;
67a99992 782#endif // wxUSE_STATUSBAR
627a3091 783
d1e44484
VZ
784 DestroyChildren();
785
4e152a23
VZ
786 RemoveWindowMenu(NULL, m_hMenu);
787
c2dcfdef 788 MSWDestroyWindow();
2bda0e17
KB
789}
790
2596e9fb
VS
791bool wxMDIChildFrame::Show(bool show)
792{
793 m_needsInitialShow = false;
6fa30495
KH
794
795 if (!wxFrame::Show(show))
796 return false;
797
798 // KH: Without this call, new MDI children do not become active.
799 // This was added here after the same BringWindowToTop call was
800 // removed from wxTopLevelWindow::Show (November 2005)
801 if ( show )
802 ::BringWindowToTop(GetHwnd());
803
e45a6885
VZ
804 // we need to refresh the MDI frame window menu to include (or exclude if
805 // we've been hidden) this frame
55ae1551 806 wxMDIParentFrame *parent = GetMDIParent();
e45a6885
VZ
807 MDISetMenu(parent->GetClientWindow(), NULL, NULL);
808
6fa30495 809 return true;
2596e9fb
VS
810}
811
2bda0e17 812// Set the client size (i.e. leave the calculation of borders etc.
77ffb593 813// to wxWidgets)
cc2b7472 814void wxMDIChildFrame::DoSetClientSize(int width, int height)
2bda0e17 815{
b3818fbe 816 HWND hWnd = GetHwnd();
2bda0e17
KB
817
818 RECT rect;
2de8030d 819 ::GetClientRect(hWnd, &rect);
2bda0e17
KB
820
821 RECT rect2;
822 GetWindowRect(hWnd, &rect2);
823
824 // Find the difference between the entire window (title bar and all)
825 // and the client area; add this to the new client size to move the
826 // window
827 int actual_width = rect2.right - rect2.left - rect.right + width;
828 int actual_height = rect2.bottom - rect2.top - rect.bottom + height;
829
67a99992 830#if wxUSE_STATUSBAR
a2327a9f 831 if (GetStatusBar() && GetStatusBar()->IsShown())
2bda0e17 832 {
c2dcfdef
VZ
833 int sx, sy;
834 GetStatusBar()->GetSize(&sx, &sy);
2bda0e17
KB
835 actual_height += sy;
836 }
67a99992 837#endif // wxUSE_STATUSBAR
2bda0e17
KB
838
839 POINT point;
840 point.x = rect2.left;
841 point.y = rect2.top;
842
843 // If there's an MDI parent, must subtract the parent's top left corner
844 // since MoveWindow moves relative to the parent
55ae1551 845 wxMDIParentFrame *mdiParent = GetMDIParent();
2bda0e17
KB
846 ::ScreenToClient((HWND) mdiParent->GetClientWindow()->GetHWND(), &point);
847
4f8090e0 848 MoveWindow(hWnd, point.x, point.y, actual_width, actual_height, (BOOL)true);
debe6624 849
5c519b6c
WS
850 wxSize size(width, height);
851 wxSizeEvent event(size, m_windowId);
debe6624 852 event.SetEventObject( this );
937013e0 853 HandleWindowEvent(event);
2bda0e17
KB
854}
855
3e85709e
VZ
856// Unlike other wxTopLevelWindowBase, the mdi child's "GetPosition" is not the
857// same as its GetScreenPosition
858void wxMDIChildFrame::DoGetScreenPosition(int *x, int *y) const
859{
860 HWND hWnd = GetHwnd();
861
862 RECT rect;
863 ::GetWindowRect(hWnd, &rect);
864 if (x)
865 *x = rect.left;
866 if (y)
867 *y = rect.top;
868}
869
870
cc2b7472 871void wxMDIChildFrame::DoGetPosition(int *x, int *y) const
2bda0e17
KB
872{
873 RECT rect;
b3818fbe 874 GetWindowRect(GetHwnd(), &rect);
2bda0e17
KB
875 POINT point;
876 point.x = rect.left;
877 point.y = rect.top;
878
879 // Since we now have the absolute screen coords,
880 // if there's a parent we must subtract its top left corner
55ae1551 881 wxMDIParentFrame *mdiParent = GetMDIParent();
2bda0e17
KB
882 ::ScreenToClient((HWND) mdiParent->GetClientWindow()->GetHWND(), &point);
883
1696dde5
JS
884 if (x)
885 *x = point.x;
886 if (y)
887 *y = point.y;
2bda0e17
KB
888}
889
42e69d6b 890void wxMDIChildFrame::InternalSetMenuBar()
2bda0e17 891{
55ae1551 892 wxMDIParentFrame *parent = GetMDIParent();
2bda0e17 893
4e152a23
VZ
894 InsertWindowMenu(parent->GetClientWindow(),
895 m_hMenu, GetMDIWindowMenu(parent));
2bda0e17 896
4f8090e0 897 parent->m_parentFrameActive = false;
2bda0e17
KB
898}
899
e3307ddd
CE
900void wxMDIChildFrame::DetachMenuBar()
901{
6bbe97b7
VZ
902 RemoveWindowMenu(NULL, m_hMenu);
903 wxFrame::DetachMenuBar();
e3307ddd
CE
904}
905
82c9f85c
VZ
906WXHICON wxMDIChildFrame::GetDefaultIcon() const
907{
94826170
VZ
908 // we don't have any standard icons (any more)
909 return (WXHICON)0;
82c9f85c
VZ
910}
911
42e69d6b 912// ---------------------------------------------------------------------------
2bda0e17 913// MDI operations
42e69d6b
VZ
914// ---------------------------------------------------------------------------
915
9b73db3c 916void wxMDIChildFrame::Maximize(bool maximize)
2bda0e17 917{
55ae1551 918 wxMDIParentFrame *parent = GetMDIParent();
2bda0e17 919 if ( parent && parent->GetClientWindow() )
9b73db3c
VZ
920 {
921 ::SendMessage(GetWinHwnd(parent->GetClientWindow()),
922 maximize ? WM_MDIMAXIMIZE : WM_MDIRESTORE,
923 (WPARAM)GetHwnd(), 0);
924 }
2bda0e17
KB
925}
926
c2dcfdef 927void wxMDIChildFrame::Restore()
2bda0e17 928{
55ae1551 929 wxMDIParentFrame *parent = GetMDIParent();
2bda0e17 930 if ( parent && parent->GetClientWindow() )
9b73db3c
VZ
931 {
932 ::SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIRESTORE,
933 (WPARAM) GetHwnd(), 0);
934 }
2bda0e17
KB
935}
936
c2dcfdef 937void wxMDIChildFrame::Activate()
2bda0e17 938{
55ae1551 939 wxMDIParentFrame *parent = GetMDIParent();
2bda0e17 940 if ( parent && parent->GetClientWindow() )
9b73db3c
VZ
941 {
942 ::SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIACTIVATE,
943 (WPARAM) GetHwnd(), 0);
944 }
2bda0e17
KB
945}
946
42e69d6b
VZ
947// ---------------------------------------------------------------------------
948// MDI window proc and message handlers
949// ---------------------------------------------------------------------------
950
c140b7e7 951WXLRESULT wxMDIChildFrame::MSWWindowProc(WXUINT message,
42e69d6b
VZ
952 WXWPARAM wParam,
953 WXLPARAM lParam)
954{
c140b7e7 955 WXLRESULT rc = 0;
4f8090e0 956 bool processed = false;
42e69d6b
VZ
957
958 switch ( message )
959 {
960 case WM_COMMAND:
961 {
962 WORD id, cmd;
963 WXHWND hwnd;
964 UnpackCommand((WXWPARAM)wParam, (WXLPARAM)lParam,
965 &id, &hwnd, &cmd);
966
967 processed = HandleCommand(id, cmd, (WXHWND)hwnd);
968 }
969 break;
970
42e69d6b 971 case WM_GETMINMAXINFO:
3ebcfb76
VZ
972 processed = HandleGetMinMaxInfo((MINMAXINFO *)lParam);
973 break;
42e69d6b
VZ
974
975 case WM_MDIACTIVATE:
976 {
977 WXWORD act;
978 WXHWND hwndAct, hwndDeact;
979 UnpackMDIActivate(wParam, lParam, &act, &hwndAct, &hwndDeact);
980
981 processed = HandleMDIActivate(act, hwndAct, hwndDeact);
982 }
b3818fbe
VZ
983 // fall through
984
985 case WM_MOVE:
986 // must pass WM_MOVE to DefMDIChildProc() to recalculate MDI client
987 // scrollbars if necessary
988
989 // fall through
990
991 case WM_SIZE:
992 // must pass WM_SIZE to DefMDIChildProc(), otherwise many weird
993 // things happen
994 MSWDefWindowProc(message, wParam, lParam);
42e69d6b
VZ
995 break;
996
b3818fbe
VZ
997 case WM_SYSCOMMAND:
998 // DefMDIChildProc handles SC_{NEXT/PREV}WINDOW here, so pass it
999 // the message (the base class version does not)
1000 return MSWDefWindowProc(message, wParam, lParam);
1001
42e69d6b
VZ
1002 case WM_WINDOWPOSCHANGING:
1003 processed = HandleWindowPosChanging((LPWINDOWPOS)lParam);
1004 break;
1005 }
1006
1007 if ( !processed )
1008 rc = wxFrame::MSWWindowProc(message, wParam, lParam);
1009
1010 return rc;
1011}
1012
8a0dce9c 1013bool wxMDIChildFrame::HandleCommand(WXWORD id_, WXWORD cmd, WXHWND hwnd)
2bda0e17 1014{
8a0dce9c
VZ
1015 // sign extend to int from short before comparing with the other int ids
1016 int id = (signed short)id_;
1017
2bda0e17 1018 // In case it's e.g. a toolbar.
42e69d6b
VZ
1019 if ( hwnd )
1020 {
1021 wxWindow *win = wxFindWinFromHandle(hwnd);
1022 if (win)
1023 return win->MSWCommand(cmd, id);
1024 }
2bda0e17 1025
e1a6fc11
JS
1026 if (wxCurrentPopupMenu)
1027 {
1028 wxMenu *popupMenu = wxCurrentPopupMenu;
1029 wxCurrentPopupMenu = NULL;
1030 if (popupMenu->MSWCommand(cmd, id))
4f8090e0 1031 return true;
e1a6fc11
JS
1032 }
1033
3ca6a5f0 1034 bool processed;
dd60b9ec 1035 if (GetMenuBar() && GetMenuBar()->FindItem(id))
2bda0e17 1036 {
3ca6a5f0 1037 processed = ProcessCommand(id);
2bda0e17
KB
1038 }
1039 else
3ca6a5f0 1040 {
4f8090e0 1041 processed = false;
3ca6a5f0 1042 }
42e69d6b 1043
3ca6a5f0 1044 return processed;
2bda0e17
KB
1045}
1046
42e69d6b
VZ
1047bool wxMDIChildFrame::HandleMDIActivate(long WXUNUSED(activate),
1048 WXHWND hwndAct,
1049 WXHWND hwndDeact)
2bda0e17 1050{
55ae1551 1051 wxMDIParentFrame *parent = GetMDIParent();
2bda0e17 1052
42e69d6b 1053 HMENU menuToSet = 0;
57a7b7c1 1054
42e69d6b 1055 bool activated;
57a7b7c1 1056
42e69d6b
VZ
1057 if ( m_hWnd == hwndAct )
1058 {
4f8090e0 1059 activated = true;
42e69d6b 1060 parent->m_currentChild = this;
2bda0e17 1061
42e69d6b
VZ
1062 HMENU child_menu = (HMENU)GetWinMenu();
1063 if ( child_menu )
1064 {
4f8090e0 1065 parent->m_parentFrameActive = false;
2bda0e17 1066
42e69d6b
VZ
1067 menuToSet = child_menu;
1068 }
1069 }
1070 else if ( m_hWnd == hwndDeact )
2bda0e17 1071 {
42e69d6b 1072 wxASSERT_MSG( parent->m_currentChild == this,
223d09f6 1073 wxT("can't deactivate MDI child which wasn't active!") );
42e69d6b 1074
4f8090e0 1075 activated = false;
42e69d6b 1076 parent->m_currentChild = NULL;
2bda0e17 1077
42e69d6b 1078 HMENU parent_menu = (HMENU)parent->GetWinMenu();
f4075804
VZ
1079
1080 // activate the the parent menu only when there is no other child
1081 // that has been activated
1082 if ( parent_menu && !hwndAct )
42e69d6b 1083 {
4f8090e0 1084 parent->m_parentFrameActive = true;
42e69d6b
VZ
1085
1086 menuToSet = parent_menu;
1087 }
1088 }
1089 else
1090 {
18d2e170 1091 // we have nothing to do with it
4f8090e0 1092 return false;
2bda0e17 1093 }
debe6624 1094
42e69d6b
VZ
1095 if ( menuToSet )
1096 {
4e152a23
VZ
1097 MDISetMenu(parent->GetClientWindow(),
1098 menuToSet, GetMDIWindowMenu(parent));
42e69d6b
VZ
1099 }
1100
1101 wxActivateEvent event(wxEVT_ACTIVATE, activated, m_windowId);
debe6624 1102 event.SetEventObject( this );
2bda0e17 1103
c03b48d7
GT
1104 ResetWindowStyle((void *)NULL);
1105
937013e0 1106 return HandleWindowEvent(event);
42e69d6b
VZ
1107}
1108
1109bool wxMDIChildFrame::HandleWindowPosChanging(void *pos)
1110{
1111 WINDOWPOS *lpPos = (WINDOWPOS *)pos;
a71d815b 1112
42e69d6b 1113 if (!(lpPos->flags & SWP_NOSIZE))
2bda0e17 1114 {
42e69d6b 1115 RECT rectClient;
b3818fbe
VZ
1116 DWORD dwExStyle = ::GetWindowLong(GetHwnd(), GWL_EXSTYLE);
1117 DWORD dwStyle = ::GetWindowLong(GetHwnd(), GWL_STYLE);
42e69d6b
VZ
1118 if (ResetWindowStyle((void *) & rectClient) && (dwStyle & WS_MAXIMIZE))
1119 {
4f8090e0 1120 ::AdjustWindowRectEx(&rectClient, dwStyle, false, dwExStyle);
42e69d6b
VZ
1121 lpPos->x = rectClient.left;
1122 lpPos->y = rectClient.top;
1123 lpPos->cx = rectClient.right - rectClient.left;
1124 lpPos->cy = rectClient.bottom - rectClient.top;
1125 }
42e69d6b 1126 }
42e69d6b 1127
4f8090e0 1128 return false;
42e69d6b
VZ
1129}
1130
3ebcfb76
VZ
1131bool wxMDIChildFrame::HandleGetMinMaxInfo(void *mmInfo)
1132{
1133 MINMAXINFO *info = (MINMAXINFO *)mmInfo;
1134
1135 // let the default window proc calculate the size of MDI children
1136 // frames because it is based on the size of the MDI client window,
1137 // not on the values specified in wxWindow m_max variables
d9f9aa2d 1138 bool processed = MSWDefWindowProc(WM_GETMINMAXINFO, 0, (LPARAM)mmInfo) != 0;
3ebcfb76 1139
e7dda1ff
VS
1140 int minWidth = GetMinWidth(),
1141 minHeight = GetMinHeight();
1142
3ebcfb76 1143 // but allow GetSizeHints() to set the min size
a71d815b 1144 if ( minWidth != wxDefaultCoord )
3ebcfb76 1145 {
e7dda1ff 1146 info->ptMinTrackSize.x = minWidth;
3ebcfb76 1147
4f8090e0 1148 processed = true;
3ebcfb76
VZ
1149 }
1150
a71d815b 1151 if ( minHeight != wxDefaultCoord )
3ebcfb76 1152 {
e7dda1ff 1153 info->ptMinTrackSize.y = minHeight;
3ebcfb76 1154
4f8090e0 1155 processed = true;
3ebcfb76
VZ
1156 }
1157
4c9d78a4 1158 return processed;
3ebcfb76
VZ
1159}
1160
42e69d6b
VZ
1161// ---------------------------------------------------------------------------
1162// MDI specific message translation/preprocessing
1163// ---------------------------------------------------------------------------
2bda0e17 1164
c140b7e7 1165WXLRESULT wxMDIChildFrame::MSWDefWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam)
42e69d6b
VZ
1166{
1167 return DefMDIChildProc(GetHwnd(),
1168 (UINT)message, (WPARAM)wParam, (LPARAM)lParam);
1169}
1170
1171bool wxMDIChildFrame::MSWTranslateMessage(WXMSG* msg)
1172{
1ac76609
VZ
1173 // we must pass the parent frame to ::TranslateAccelerator(), otherwise it
1174 // doesn't do its job correctly for MDI child menus
55ae1551 1175 return MSWDoTranslateMessage(GetMDIParent(), msg);
2bda0e17
KB
1176}
1177
42e69d6b
VZ
1178// ---------------------------------------------------------------------------
1179// misc
1180// ---------------------------------------------------------------------------
1181
c2dcfdef 1182void wxMDIChildFrame::MSWDestroyWindow()
2bda0e17 1183{
55ae1551 1184 wxMDIParentFrame *parent = GetMDIParent();
2bda0e17 1185
42e69d6b
VZ
1186 // Must make sure this handle is invalidated (set to NULL) since all sorts
1187 // of things could happen after the child client is destroyed, but before
1188 // the wxFrame is destroyed.
2bda0e17 1189
42e69d6b 1190 HWND oldHandle = (HWND)GetHWND();
b3818fbe
VZ
1191 SendMessage(GetWinHwnd(parent->GetClientWindow()), WM_MDIDESTROY,
1192 (WPARAM)oldHandle, 0);
18d2e170
JS
1193
1194 if (parent->GetActiveChild() == (wxMDIChildFrame*) NULL)
1195 ResetWindowStyle((void*) NULL);
1196
42e69d6b
VZ
1197 if (m_hMenu)
1198 {
1199 ::DestroyMenu((HMENU) m_hMenu);
1200 m_hMenu = 0;
1201 }
ac6482e0 1202 wxRemoveHandleAssociation(this);
42e69d6b 1203 m_hWnd = 0;
2bda0e17
KB
1204}
1205
42e69d6b
VZ
1206// Change the client window's extended style so we don't get a client edge
1207// style when a child is maximised (a double border looks silly.)
2bda0e17
KB
1208bool wxMDIChildFrame::ResetWindowStyle(void *vrect)
1209{
2bda0e17 1210 RECT *rect = (RECT *)vrect;
55ae1551 1211 wxMDIParentFrame* pFrameWnd = GetMDIParent();
c2dcfdef 1212 wxMDIChildFrame* pChild = pFrameWnd->GetActiveChild();
a71d815b 1213
c2dcfdef
VZ
1214 if (!pChild || (pChild == this))
1215 {
47ca6bfb 1216 HWND hwndClient = GetWinHwnd(pFrameWnd->GetClientWindow());
8082a771
VZ
1217 DWORD dwStyle = ::GetWindowLong(hwndClient, GWL_EXSTYLE);
1218
1219 // we want to test whether there is a maximized child, so just set
1220 // dwThisStyle to 0 if there is no child at all
1221 DWORD dwThisStyle = pChild
1f3943e0 1222 ? ::GetWindowLong(GetWinHwnd(pChild), GWL_STYLE) : 0;
c2dcfdef 1223 DWORD dwNewStyle = dwStyle;
8082a771 1224 if ( dwThisStyle & WS_MAXIMIZE )
c2dcfdef
VZ
1225 dwNewStyle &= ~(WS_EX_CLIENTEDGE);
1226 else
1227 dwNewStyle |= WS_EX_CLIENTEDGE;
1228
1229 if (dwStyle != dwNewStyle)
1230 {
47ca6bfb
VZ
1231 // force update of everything
1232 ::RedrawWindow(hwndClient, NULL, NULL,
1233 RDW_INVALIDATE | RDW_ALLCHILDREN);
1234 ::SetWindowLong(hwndClient, GWL_EXSTYLE, dwNewStyle);
1235 ::SetWindowPos(hwndClient, NULL, 0, 0, 0, 0,
42e69d6b
VZ
1236 SWP_FRAMECHANGED | SWP_NOACTIVATE |
1237 SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER |
1238 SWP_NOCOPYBITS);
c2dcfdef 1239 if (rect)
47ca6bfb 1240 ::GetClientRect(hwndClient, rect);
2bda0e17 1241
4f8090e0 1242 return true;
2bda0e17 1243 }
c2dcfdef 1244 }
a23fd0e1 1245
4f8090e0 1246 return false;
2bda0e17
KB
1247}
1248
42e69d6b
VZ
1249// ===========================================================================
1250// wxMDIClientWindow: the window of predefined (by Windows) class which
1251// contains the child frames
1252// ===========================================================================
2bda0e17 1253
debe6624 1254bool wxMDIClientWindow::CreateClient(wxMDIParentFrame *parent, long style)
2bda0e17 1255{
a756f210 1256 m_backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_APPWORKSPACE);
2bda0e17 1257
42e69d6b
VZ
1258 CLIENTCREATESTRUCT ccs;
1259 m_windowStyle = style;
1260 m_parent = parent;
c2dcfdef 1261
4e152a23 1262 ccs.hWindowMenu = GetMDIWindowMenu(parent);
42e69d6b 1263 ccs.idFirstChild = wxFIRST_MDI_CHILD;
2bda0e17 1264
5c44cd05 1265 DWORD msStyle = MDIS_ALLCHILDSTYLES | WS_VISIBLE | WS_CHILD |
b0766406
JS
1266 WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
1267
42e69d6b
VZ
1268 if ( style & wxHSCROLL )
1269 msStyle |= WS_HSCROLL;
1270 if ( style & wxVSCROLL )
1271 msStyle |= WS_VSCROLL;
2bda0e17 1272
42e69d6b 1273 DWORD exStyle = WS_EX_CLIENTEDGE;
2bda0e17 1274
b225f659 1275 wxWindowCreationHook hook(this);
42e69d6b
VZ
1276 m_hWnd = (WXHWND)::CreateWindowEx
1277 (
1278 exStyle,
223d09f6 1279 wxT("MDICLIENT"),
42e69d6b
VZ
1280 NULL,
1281 msStyle,
1282 0, 0, 0, 0,
1283 GetWinHwnd(parent),
1284 NULL,
1285 wxGetInstance(),
1286 (LPSTR)(LPCLIENTCREATESTRUCT)&ccs);
1287 if ( !m_hWnd )
1288 {
f6bcfd97 1289 wxLogLastError(wxT("CreateWindowEx(MDI client)"));
2bda0e17 1290
4f8090e0 1291 return false;
42e69d6b 1292 }
2bda0e17 1293
42e69d6b 1294 SubclassWin(m_hWnd);
2bda0e17 1295
4f8090e0 1296 return true;
2bda0e17
KB
1297}
1298
1299// Explicitly call default scroll behaviour
1300void wxMDIClientWindow::OnScroll(wxScrollEvent& event)
1301{
1302 // Note: for client windows, the scroll position is not set in
1303 // WM_HSCROLL, WM_VSCROLL, so we can't easily determine what
1304 // scroll position we're at.
1305 // This makes it hard to paint patterns or bitmaps in the background,
1306 // and have the client area scrollable as well.
1307
1308 if ( event.GetOrientation() == wxHORIZONTAL )
1309 m_scrollX = event.GetPosition(); // Always returns zero!
1310 else
1311 m_scrollY = event.GetPosition(); // Always returns zero!
1312
42e69d6b
VZ
1313 event.Skip();
1314}
1315
ec06b234
JS
1316void wxMDIClientWindow::DoSetSize(int x, int y, int width, int height, int sizeFlags)
1317{
1318 // Try to fix a problem whereby if you show an MDI child frame, then reposition the
1319 // client area, you can end up with a non-refreshed portion in the client window
1320 // (see OGL studio sample). So check if the position is changed and if so,
1321 // redraw the MDI child frames.
1322
6bbe97b7 1323 const wxPoint oldPos = GetPosition();
ec06b234 1324
6bbe97b7 1325 wxWindow::DoSetSize(x, y, width, height, sizeFlags | wxSIZE_FORCE);
ec06b234 1326
6bbe97b7 1327 const wxPoint newPos = GetPosition();
ec06b234
JS
1328
1329 if ((newPos.x != oldPos.x) || (newPos.y != oldPos.y))
1330 {
1331 if (GetParent())
1332 {
222ed1d6 1333 wxWindowList::compatibility_iterator node = GetParent()->GetChildren().GetFirst();
ec06b234
JS
1334 while (node)
1335 {
d162a7ee 1336 wxWindow *child = node->GetData();
ec06b234
JS
1337 if (child->IsKindOf(CLASSINFO(wxMDIChildFrame)))
1338 {
d162a7ee
VZ
1339 ::RedrawWindow(GetHwndOf(child),
1340 NULL,
1341 NULL,
1342 RDW_FRAME |
1343 RDW_ALLCHILDREN |
1344 RDW_INVALIDATE);
ec06b234 1345 }
4f8090e0 1346 node = node->GetNext();
ec06b234
JS
1347 }
1348 }
1349 }
1350}
1351
f6bcfd97
BP
1352void wxMDIChildFrame::OnIdle(wxIdleEvent& event)
1353{
2596e9fb
VS
1354 // wxMSW prior to 2.5.3 created MDI child frames as visible, which resulted
1355 // in flicker e.g. when the frame contained controls with non-trivial
1356 // layout. Since 2.5.3, the frame is created hidden as all other top level
1357 // windows. In order to maintain backward compatibility, the frame is shown
1358 // in OnIdle, unless Show(false) was called by the programmer before.
1359 if ( m_needsInitialShow )
1360 {
1361 Show(true);
1362 }
5c519b6c 1363
f6bcfd97
BP
1364 // MDI child frames get their WM_SIZE when they're constructed but at this
1365 // moment they don't have any children yet so all child windows will be
1366 // positioned incorrectly when they are added later - to fix this, we
1367 // generate an artificial size event here
1368 if ( m_needsResize )
1369 {
4f8090e0 1370 m_needsResize = false; // avoid any possibility of recursion
f6bcfd97
BP
1371
1372 SendSizeEvent();
1373 }
1374
1375 event.Skip();
1376}
1377
42e69d6b
VZ
1378// ---------------------------------------------------------------------------
1379// non member functions
1380// ---------------------------------------------------------------------------
1381
1382static void MDISetMenu(wxWindow *win, HMENU hmenuFrame, HMENU hmenuWindow)
1383{
e45a6885
VZ
1384 if ( hmenuFrame || hmenuWindow )
1385 {
1386 if ( !::SendMessage(GetWinHwnd(win),
1387 WM_MDISETMENU,
1388 (WPARAM)hmenuFrame,
1389 (LPARAM)hmenuWindow) )
1390 {
0d7c4404
VZ
1391#ifdef __WXDEBUG__
1392 DWORD err = ::GetLastError();
1393 if ( err )
1394 wxLogApiError(_T("SendMessage(WM_MDISETMENU)"), err);
1395#endif // __WXDEBUG__
e45a6885
VZ
1396 }
1397 }
42e69d6b
VZ
1398
1399 // update menu bar of the parent window
1400 wxWindow *parent = win->GetParent();
223d09f6 1401 wxCHECK_RET( parent, wxT("MDI client without parent frame? weird...") );
42e69d6b 1402
a967ef9d 1403 ::SendMessage(GetWinHwnd(win), WM_MDIREFRESHMENU, 0, 0L);
788722ac 1404
42e69d6b
VZ
1405 ::DrawMenuBar(GetWinHwnd(parent));
1406}
1407
1408static void InsertWindowMenu(wxWindow *win, WXHMENU menu, HMENU subMenu)
1409{
1410 // Try to insert Window menu in front of Help, otherwise append it.
1411 HMENU hmenu = (HMENU)menu;
df61c009
JS
1412
1413 if (subMenu)
1414 {
4e152a23 1415 int N = GetMenuItemCount(hmenu);
4f8090e0 1416 bool success = false;
4e152a23 1417 for ( int i = 0; i < N; i++ )
42e69d6b 1418 {
4e152a23
VZ
1419 wxChar buf[256];
1420 int chars = GetMenuString(hmenu, i, buf, WXSIZEOF(buf), MF_BYPOSITION);
1421 if ( chars == 0 )
1422 {
1423 wxLogLastError(wxT("GetMenuString"));
42e69d6b 1424
4e152a23
VZ
1425 continue;
1426 }
1427
4d931bcc 1428 wxString strBuf(buf);
e27d9a91 1429 if ( wxStripMenuCodes(strBuf) == wxGetStockLabel(wxID_HELP,false) )
4e152a23 1430 {
4f8090e0 1431 success = true;
4e152a23 1432 ::InsertMenu(hmenu, i, MF_BYPOSITION | MF_POPUP | MF_STRING,
dca0f651 1433 (UINT_PTR)subMenu, _("&Window").wx_str());
4e152a23
VZ
1434 break;
1435 }
42e69d6b
VZ
1436 }
1437
4e152a23 1438 if ( !success )
42e69d6b 1439 {
dca0f651
VZ
1440 ::AppendMenu(hmenu, MF_POPUP,
1441 (UINT_PTR)subMenu, _("&Window").wx_str());
42e69d6b
VZ
1442 }
1443 }
1444
42e69d6b
VZ
1445 MDISetMenu(win, hmenu, subMenu);
1446}
1447
df61c009
JS
1448static void RemoveWindowMenu(wxWindow *win, WXHMENU menu)
1449{
4e152a23
VZ
1450 HMENU hMenu = (HMENU)menu;
1451
1452 if ( hMenu )
df61c009 1453 {
4e152a23
VZ
1454 wxChar buf[1024];
1455
1456 int N = ::GetMenuItemCount(hMenu);
1457 for ( int i = 0; i < N; i++ )
df61c009 1458 {
4e152a23
VZ
1459 if ( !::GetMenuString(hMenu, i, buf, WXSIZEOF(buf), MF_BYPOSITION) )
1460 {
2c62b3c3
VZ
1461 // Ignore successful read of menu string with length 0 which
1462 // occurs, for example, for a maximized MDI childs system menu
1463 if ( ::GetLastError() != 0 )
1464 {
1465 wxLogLastError(wxT("GetMenuString"));
1466 }
df61c009 1467
4e152a23
VZ
1468 continue;
1469 }
df61c009 1470
4e152a23
VZ
1471 if ( wxStrcmp(buf, _("&Window")) == 0 )
1472 {
1473 if ( !::RemoveMenu(hMenu, i, MF_BYPOSITION) )
1474 {
1475 wxLogLastError(wxT("RemoveMenu"));
1476 }
1477
1478 break;
1479 }
df61c009
JS
1480 }
1481 }
1482
4e152a23
VZ
1483 if ( win )
1484 {
1485 // we don't change the windows menu, but we update the main one
1486 MDISetMenu(win, hMenu, NULL);
1487 }
df61c009
JS
1488}
1489
2917e920
BM
1490static void UnpackMDIActivate(WXWPARAM wParam, WXLPARAM lParam,
1491 WXWORD *activate, WXHWND *hwndAct, WXHWND *hwndDeact)
42e69d6b 1492{
4f8090e0 1493 *activate = true;
42e69d6b
VZ
1494 *hwndAct = (WXHWND)lParam;
1495 *hwndDeact = (WXHWND)wParam;
2bda0e17 1496}
b9f933ab 1497
efd17a1d 1498#endif // wxUSE_MDI && !defined(__WXUNIVERSAL__)