1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/mdig.cpp
3 // Purpose: Generic MDI (Multiple Document Interface) classes
4 // Author: Hans Van Leemputten
5 // Modified by: 2008-10-31 Vadim Zeitlin: derive from the base classes
8 // Copyright: (c) 2002 Hans Van Leemputten
9 // (c) 2008 Vadim Zeitlin
10 // Licence: wxWindows licence
11 /////////////////////////////////////////////////////////////////////////////
13 // ===========================================================================
15 // ===========================================================================
17 // ---------------------------------------------------------------------------
19 // ---------------------------------------------------------------------------
21 // For compilers that support precompilation, includes "wx.h".
22 #include "wx/wxprec.h"
37 #include "wx/generic/mdig.h"
38 #include "wx/notebook.h"
39 #include "wx/scopeguard.h"
41 #include "wx/stockitem.h"
51 //-----------------------------------------------------------------------------
52 // wxGenericMDIParentFrame
53 //-----------------------------------------------------------------------------
55 IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIParentFrame
, wxFrame
)
57 BEGIN_EVENT_TABLE(wxGenericMDIParentFrame
, wxFrame
)
58 EVT_CLOSE(wxGenericMDIParentFrame::OnClose
)
60 EVT_MENU(wxID_ANY
, wxGenericMDIParentFrame::OnWindowMenu
)
64 void wxGenericMDIParentFrame::Init()
71 wxGenericMDIParentFrame::~wxGenericMDIParentFrame()
73 // Make sure the client window is destructed before the menu bars are!
74 wxDELETE(m_clientWindow
);
77 wxDELETE(m_pMyMenuBar
);
79 RemoveWindowMenu(GetMenuBar());
83 bool wxGenericMDIParentFrame::Create(wxWindow
*parent
,
85 const wxString
& title
,
91 // this style can be used to prevent a window from having the standard MDI
93 if ( !(style
& wxFRAME_NO_WINDOW_MENU
) )
96 m_windowMenu
= new wxMenu
;
98 m_windowMenu
->Append(wxWINDOWCLOSE
, _("Cl&ose"));
99 m_windowMenu
->Append(wxWINDOWCLOSEALL
, _("Close All"));
100 m_windowMenu
->AppendSeparator();
101 m_windowMenu
->Append(wxWINDOWNEXT
, _("&Next"));
102 m_windowMenu
->Append(wxWINDOWPREV
, _("&Previous"));
103 #endif // wxUSE_MENUS
106 // the scrolling styles don't make sense neither for us nor for our client
107 // window (to which they're supposed to apply)
108 style
&= ~(wxHSCROLL
| wxVSCROLL
);
110 if ( !wxFrame::Create( parent
, id
, title
, pos
, size
, style
, name
) )
113 wxGenericMDIClientWindow
* const client
= OnCreateGenericClient();
114 if ( !client
->CreateGenericClient(this) )
117 m_clientWindow
= client
;
122 wxGenericMDIClientWindow
*wxGenericMDIParentFrame::OnCreateGenericClient()
124 return new wxGenericMDIClientWindow
;
127 bool wxGenericMDIParentFrame::CloseAll()
129 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
131 return true; // none of the windows left
133 wxBookCtrlBase
* const book
= client
->GetBookCtrl();
134 while ( book
->GetPageCount() )
136 wxGenericMDIChildFrame
* const child
= client
->GetChild(0);
137 if ( !child
->Close() )
139 // it refused to close, don't close the remaining ones neither
148 void wxGenericMDIParentFrame::SetWindowMenu(wxMenu
* pMenu
)
150 // Replace the window menu from the currently loaded menu bar.
151 wxMenuBar
*pMenuBar
= GetMenuBar();
155 RemoveWindowMenu(pMenuBar
);
157 wxDELETE(m_windowMenu
);
162 m_windowMenu
= pMenu
;
164 AddWindowMenu(pMenuBar
);
168 void wxGenericMDIParentFrame::SetMenuBar(wxMenuBar
*pMenuBar
)
170 // Remove the Window menu from the old menu bar
171 RemoveWindowMenu(GetMenuBar());
172 // Add the Window menu to the new menu bar.
173 AddWindowMenu(pMenuBar
);
175 wxFrame::SetMenuBar(pMenuBar
);
177 #endif // wxUSE_MENUS
179 void wxGenericMDIParentFrame::WXSetChildMenuBar(wxGenericMDIChildFrame
*pChild
)
184 // No Child, set Our menu bar back.
185 SetMenuBar(m_pMyMenuBar
);
187 // Make sure we know our menu bar is in use
192 if (pChild
->GetMenuBar() == NULL
)
195 // Do we need to save the current bar?
196 if (m_pMyMenuBar
== NULL
)
197 m_pMyMenuBar
= GetMenuBar();
199 SetMenuBar(pChild
->GetMenuBar());
201 #endif // wxUSE_MENUS
204 wxGenericMDIClientWindow
*
205 wxGenericMDIParentFrame::GetGenericClientWindow() const
207 return static_cast<wxGenericMDIClientWindow
*>(m_clientWindow
);
210 wxBookCtrlBase
*wxGenericMDIParentFrame::GetBookCtrl() const
212 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
213 return client
? client
->GetBookCtrl() : NULL
;
216 void wxGenericMDIParentFrame::AdvanceActive(bool forward
)
218 wxBookCtrlBase
* const book
= GetBookCtrl();
220 book
->AdvanceSelection(forward
);
223 void wxGenericMDIParentFrame::WXUpdateChildTitle(wxGenericMDIChildFrame
*child
)
225 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
227 const int pos
= client
->FindChild(child
);
228 if ( pos
== wxNOT_FOUND
)
231 client
->GetBookCtrl()->SetPageText(pos
, child
->GetTitle());
234 void wxGenericMDIParentFrame::WXActivateChild(wxGenericMDIChildFrame
*child
)
236 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
238 const int pos
= client
->FindChild(child
);
239 if ( pos
== wxNOT_FOUND
)
242 client
->GetBookCtrl()->SetSelection(pos
);
245 void wxGenericMDIParentFrame::WXRemoveChild(wxGenericMDIChildFrame
*child
)
247 const bool removingActive
= WXIsActiveChild(child
);
248 if ( removingActive
)
250 SetActiveChild(NULL
);
251 WXSetChildMenuBar(NULL
);
254 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
255 wxCHECK_RET( client
, "should have client window" );
257 wxBookCtrlBase
* const book
= client
->GetBookCtrl();
259 // Remove page if still there
260 int pos
= client
->FindChild(child
);
261 if ( pos
!= wxNOT_FOUND
)
263 if ( book
->RemovePage(pos
) )
267 if ( removingActive
)
269 // Set the new selection to a remaining page
270 const size_t count
= book
->GetPageCount();
271 if ( count
> (size_t)pos
)
273 book
->SetSelection(pos
);
278 book
->SetSelection(count
- 1);
284 wxGenericMDIParentFrame::WXIsActiveChild(wxGenericMDIChildFrame
*child
) const
286 return static_cast<wxMDIChildFrameBase
*>(GetActiveChild()) == child
;
290 void wxGenericMDIParentFrame::RemoveWindowMenu(wxMenuBar
*pMenuBar
)
292 if (pMenuBar
&& m_windowMenu
)
294 // Remove old window menu
295 int pos
= pMenuBar
->FindMenu(_("&Window"));
296 if (pos
!= wxNOT_FOUND
)
298 wxASSERT(m_windowMenu
== pMenuBar
->GetMenu(pos
)); // DBG:: We're going to delete the wrong menu!!!
299 pMenuBar
->Remove(pos
);
304 void wxGenericMDIParentFrame::AddWindowMenu(wxMenuBar
*pMenuBar
)
306 if (pMenuBar
&& m_windowMenu
)
308 int pos
= pMenuBar
->FindMenu(wxGetStockLabel(wxID_HELP
,false));
309 if (pos
== wxNOT_FOUND
)
311 pMenuBar
->Append(m_windowMenu
, _("&Window"));
315 pMenuBar
->Insert(pos
, m_windowMenu
, _("&Window"));
320 void wxGenericMDIParentFrame::OnWindowMenu(wxCommandEvent
&event
)
322 switch ( event
.GetId() )
325 if ( m_currentChild
)
326 m_currentChild
->Close();
329 case wxWINDOWCLOSEALL
:
345 #endif // wxUSE_MENUS
347 void wxGenericMDIParentFrame::OnClose(wxCloseEvent
& event
)
355 bool wxGenericMDIParentFrame::ProcessEvent(wxEvent
& event
)
357 if ( m_currentChild
)
359 // the menu events should be given to the child as we show its menu bar
361 const wxEventType eventType
= event
.GetEventType();
362 if ( eventType
== wxEVT_MENU
||
363 eventType
== wxEVT_UPDATE_UI
)
365 // set the flag indicating that this event was forwarded to the
366 // child from the parent and so shouldn't be propagated upwards if
367 // not processed to avoid infinite loop
368 m_childHandler
= m_currentChild
;
369 wxON_BLOCK_EXIT_NULL(m_childHandler
);
371 if ( m_currentChild
->ProcessWindowEvent(event
) )
376 return wxMDIParentFrameBase::ProcessEvent(event
);
379 // ----------------------------------------------------------------------------
380 // wxGenericMDIChildFrame
381 // ----------------------------------------------------------------------------
383 IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIChildFrame
, wxFrame
)
385 BEGIN_EVENT_TABLE(wxGenericMDIChildFrame
, wxFrame
)
386 EVT_MENU_HIGHLIGHT_ALL(wxGenericMDIChildFrame::OnMenuHighlight
)
388 EVT_CLOSE(wxGenericMDIChildFrame::OnClose
)
391 void wxGenericMDIChildFrame::Init()
395 #endif // wxUSE_MENUS
397 #if !wxUSE_GENERIC_MDI_AS_NATIVE
398 m_mdiParentGeneric
= NULL
;
402 wxGenericMDIChildFrame::~wxGenericMDIChildFrame()
404 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
406 // it could happen that we don't have a valid parent if we hadn't been ever
407 // really created -- but in this case there is nothing else to do neither
409 parent
->WXRemoveChild(this);
413 #endif // wxUSE_MENUS
416 bool wxGenericMDIChildFrame::Create(wxGenericMDIParentFrame
*parent
,
418 const wxString
& title
,
419 const wxPoint
& WXUNUSED(pos
),
421 long WXUNUSED(style
),
422 const wxString
& name
)
424 // unfortunately we can't use the base class m_mdiParent field unless
425 // wxGenericMDIParentFrame is wxMDIParentFrame
426 #if wxUSE_GENERIC_MDI_AS_NATIVE
427 m_mdiParent
= parent
;
428 #else // generic != native
429 // leave m_mdiParent NULL, we don't have it
430 m_mdiParentGeneric
= parent
;
433 wxBookCtrlBase
* const book
= parent
->GetBookCtrl();
435 wxASSERT_MSG( book
, "Missing MDI client window." );
437 // note that we ignore the styles, none of the usual TLW styles apply to
438 // this (child) window
439 if ( !wxWindow::Create(book
, id
, wxDefaultPosition
, size
, 0, name
) )
443 book
->AddPage(this, title
, true);
449 void wxGenericMDIChildFrame::SetMenuBar( wxMenuBar
*menu_bar
)
451 wxMenuBar
*pOldMenuBar
= m_pMenuBar
;
452 m_pMenuBar
= menu_bar
;
456 wxGenericMDIParentFrame
*parent
= GetGenericMDIParent();
460 m_pMenuBar
->SetParent(parent
);
462 if ( parent
->WXIsActiveChild(this) )
464 // Replace current menu bars
466 parent
->WXSetChildMenuBar(NULL
);
467 parent
->WXSetChildMenuBar(this);
473 wxMenuBar
*wxGenericMDIChildFrame::GetMenuBar() const
477 #endif // wxUSE_MENUS
479 void wxGenericMDIChildFrame::SetTitle(const wxString
& title
)
483 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
485 parent
->WXUpdateChildTitle(this);
486 //else: it's ok, we might be not created yet
489 void wxGenericMDIChildFrame::Activate()
491 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
493 wxCHECK_RET( parent
, "can't activate MDI child without parent" );
494 parent
->WXActivateChild(this);
497 void wxGenericMDIChildFrame::OnMenuHighlight(wxMenuEvent
& event
)
499 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
502 // we don't have any help text for this item,
503 // but may be the MDI frame does?
504 parent
->OnMenuHighlight(event
);
508 void wxGenericMDIChildFrame::OnClose(wxCloseEvent
& WXUNUSED(event
))
510 // we're not a TLW so don't delay the destruction of this window
514 bool wxGenericMDIChildFrame::TryAfter(wxEvent
& event
)
516 // we shouldn't propagate the event to the parent if we received it from it
517 // in the first place
518 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
519 if ( parent
&& parent
->WXIsInsideChildHandler(this) )
522 return wxTDIChildFrame::TryAfter(event
);
525 // ----------------------------------------------------------------------------
526 // wxGenericMDIClientWindow
527 // ----------------------------------------------------------------------------
529 IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIClientWindow
, wxWindow
)
532 wxGenericMDIClientWindow::CreateGenericClient(wxWindow
*parent
)
534 if ( !wxWindow::Create(parent
, wxID_ANY
) )
537 m_notebook
= new wxNotebook(this, wxID_ANY
);
540 wxEVT_NOTEBOOK_PAGE_CHANGED
,
541 wxNotebookEventHandler(
542 wxGenericMDIClientWindow::OnPageChanged
),
547 // now that we have a notebook to resize, hook up OnSize() too
548 Connect(wxEVT_SIZE
, wxSizeEventHandler(wxGenericMDIClientWindow::OnSize
));
553 wxBookCtrlBase
*wxGenericMDIClientWindow::GetBookCtrl() const
558 wxGenericMDIChildFrame
*wxGenericMDIClientWindow::GetChild(size_t pos
) const
560 return static_cast<wxGenericMDIChildFrame
*>(GetBookCtrl()->GetPage(pos
));
563 int wxGenericMDIClientWindow::FindChild(wxGenericMDIChildFrame
*child
) const
565 wxBookCtrlBase
* const book
= GetBookCtrl();
566 const size_t count
= book
->GetPageCount();
567 for ( size_t pos
= 0; pos
< count
; pos
++ )
569 if ( book
->GetPage(pos
) == child
)
576 void wxGenericMDIClientWindow::PageChanged(int oldSelection
, int newSelection
)
578 // Don't do anything if nothing changed
579 if (oldSelection
== newSelection
)
582 // Again check if we really need to do this...
583 if (newSelection
!= -1)
585 wxGenericMDIChildFrame
* const child
= GetChild(newSelection
);
587 if ( child
->GetGenericMDIParent()->WXIsActiveChild(child
) )
591 // Notify old active child that it has been deactivated
592 if (oldSelection
!= -1)
594 wxGenericMDIChildFrame
* const oldChild
= GetChild(oldSelection
);
597 wxActivateEvent
event(wxEVT_ACTIVATE
, false, oldChild
->GetId());
598 event
.SetEventObject( oldChild
);
599 oldChild
->GetEventHandler()->ProcessEvent(event
);
603 // Notify new active child that it has been activated
604 if (newSelection
!= -1)
606 wxGenericMDIChildFrame
* const activeChild
= GetChild(newSelection
);
609 wxActivateEvent
event(wxEVT_ACTIVATE
, true, activeChild
->GetId());
610 event
.SetEventObject( activeChild
);
611 activeChild
->GetEventHandler()->ProcessEvent(event
);
613 wxGenericMDIParentFrame
* const
614 parent
= activeChild
->GetGenericMDIParent();
618 // this is a dirty hack as activeChild is not really a
619 // wxMDIChildFrame at all but we still want to store it in the
620 // base class m_currentChild field and this will work as long
621 // as we only use as wxMDIChildFrameBase pointer (which it is)
622 parent
->SetActiveChild(
623 reinterpret_cast<wxMDIChildFrame
*>(activeChild
));
624 parent
->WXSetChildMenuBar(activeChild
);
630 void wxGenericMDIClientWindow::OnPageChanged(wxBookCtrlEvent
& event
)
632 PageChanged(event
.GetOldSelection(), event
.GetSelection());
637 void wxGenericMDIClientWindow::OnSize(wxSizeEvent
& WXUNUSED(event
))
639 m_notebook
->SetSize(GetClientSize());