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