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
7 // Copyright: (c) 2002 Hans Van Leemputten
8 // (c) 2008 Vadim Zeitlin
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
36 #include "wx/generic/mdig.h"
37 #include "wx/notebook.h"
38 #include "wx/scopeguard.h"
40 #include "wx/stockitem.h"
50 //-----------------------------------------------------------------------------
51 // wxGenericMDIParentFrame
52 //-----------------------------------------------------------------------------
54 IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIParentFrame
, wxFrame
)
56 BEGIN_EVENT_TABLE(wxGenericMDIParentFrame
, wxFrame
)
57 EVT_CLOSE(wxGenericMDIParentFrame::OnClose
)
59 EVT_MENU(wxID_ANY
, wxGenericMDIParentFrame::OnWindowMenu
)
63 void wxGenericMDIParentFrame::Init()
70 wxGenericMDIParentFrame::~wxGenericMDIParentFrame()
72 // Make sure the client window is destructed before the menu bars are!
73 wxDELETE(m_clientWindow
);
76 wxDELETE(m_pMyMenuBar
);
78 RemoveWindowMenu(GetMenuBar());
82 bool wxGenericMDIParentFrame::Create(wxWindow
*parent
,
84 const wxString
& title
,
90 // this style can be used to prevent a window from having the standard MDI
92 if ( !(style
& wxFRAME_NO_WINDOW_MENU
) )
95 m_windowMenu
= new wxMenu
;
97 m_windowMenu
->Append(wxWINDOWCLOSE
, _("Cl&ose"));
98 m_windowMenu
->Append(wxWINDOWCLOSEALL
, _("Close All"));
99 m_windowMenu
->AppendSeparator();
100 m_windowMenu
->Append(wxWINDOWNEXT
, _("&Next"));
101 m_windowMenu
->Append(wxWINDOWPREV
, _("&Previous"));
102 #endif // wxUSE_MENUS
105 // the scrolling styles don't make sense neither for us nor for our client
106 // window (to which they're supposed to apply)
107 style
&= ~(wxHSCROLL
| wxVSCROLL
);
109 if ( !wxFrame::Create( parent
, id
, title
, pos
, size
, style
, name
) )
112 wxGenericMDIClientWindow
* const client
= OnCreateGenericClient();
113 if ( !client
->CreateGenericClient(this) )
116 m_clientWindow
= client
;
121 wxGenericMDIClientWindow
*wxGenericMDIParentFrame::OnCreateGenericClient()
123 return new wxGenericMDIClientWindow
;
126 bool wxGenericMDIParentFrame::CloseAll()
128 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
130 return true; // none of the windows left
132 wxBookCtrlBase
* const book
= client
->GetBookCtrl();
133 while ( book
->GetPageCount() )
135 wxGenericMDIChildFrame
* const child
= client
->GetChild(0);
136 if ( !child
->Close() )
138 // it refused to close, don't close the remaining ones neither
147 void wxGenericMDIParentFrame::SetWindowMenu(wxMenu
* pMenu
)
149 // Replace the window menu from the currently loaded menu bar.
150 wxMenuBar
*pMenuBar
= GetMenuBar();
154 RemoveWindowMenu(pMenuBar
);
156 wxDELETE(m_windowMenu
);
161 m_windowMenu
= pMenu
;
163 AddWindowMenu(pMenuBar
);
167 void wxGenericMDIParentFrame::SetMenuBar(wxMenuBar
*pMenuBar
)
169 // Remove the Window menu from the old menu bar
170 RemoveWindowMenu(GetMenuBar());
171 // Add the Window menu to the new menu bar.
172 AddWindowMenu(pMenuBar
);
174 wxFrame::SetMenuBar(pMenuBar
);
176 #endif // wxUSE_MENUS
178 void wxGenericMDIParentFrame::WXSetChildMenuBar(wxGenericMDIChildFrame
*pChild
)
183 // No Child, set Our menu bar back.
184 SetMenuBar(m_pMyMenuBar
);
186 // Make sure we know our menu bar is in use
191 if (pChild
->GetMenuBar() == NULL
)
194 // Do we need to save the current bar?
195 if (m_pMyMenuBar
== NULL
)
196 m_pMyMenuBar
= GetMenuBar();
198 SetMenuBar(pChild
->GetMenuBar());
200 #endif // wxUSE_MENUS
203 wxGenericMDIClientWindow
*
204 wxGenericMDIParentFrame::GetGenericClientWindow() const
206 return static_cast<wxGenericMDIClientWindow
*>(m_clientWindow
);
209 wxBookCtrlBase
*wxGenericMDIParentFrame::GetBookCtrl() const
211 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
212 return client
? client
->GetBookCtrl() : NULL
;
215 void wxGenericMDIParentFrame::AdvanceActive(bool forward
)
217 wxBookCtrlBase
* const book
= GetBookCtrl();
219 book
->AdvanceSelection(forward
);
222 void wxGenericMDIParentFrame::WXUpdateChildTitle(wxGenericMDIChildFrame
*child
)
224 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
226 const int pos
= client
->FindChild(child
);
227 if ( pos
== wxNOT_FOUND
)
230 client
->GetBookCtrl()->SetPageText(pos
, child
->GetTitle());
233 void wxGenericMDIParentFrame::WXActivateChild(wxGenericMDIChildFrame
*child
)
235 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
237 const int pos
= client
->FindChild(child
);
238 if ( pos
== wxNOT_FOUND
)
241 client
->GetBookCtrl()->SetSelection(pos
);
244 void wxGenericMDIParentFrame::WXRemoveChild(wxGenericMDIChildFrame
*child
)
246 const bool removingActive
= WXIsActiveChild(child
);
247 if ( removingActive
)
249 SetActiveChild(NULL
);
250 WXSetChildMenuBar(NULL
);
253 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
254 wxCHECK_RET( client
, "should have client window" );
256 wxBookCtrlBase
* const book
= client
->GetBookCtrl();
258 // Remove page if still there
259 int pos
= client
->FindChild(child
);
260 if ( pos
!= wxNOT_FOUND
)
262 if ( book
->RemovePage(pos
) )
266 if ( removingActive
)
268 // Set the new selection to a remaining page
269 const size_t count
= book
->GetPageCount();
270 if ( count
> (size_t)pos
)
272 book
->SetSelection(pos
);
277 book
->SetSelection(count
- 1);
283 wxGenericMDIParentFrame::WXIsActiveChild(wxGenericMDIChildFrame
*child
) const
285 return static_cast<wxMDIChildFrameBase
*>(GetActiveChild()) == child
;
289 void wxGenericMDIParentFrame::RemoveWindowMenu(wxMenuBar
*pMenuBar
)
291 if (pMenuBar
&& m_windowMenu
)
293 // Remove old window menu
294 int pos
= pMenuBar
->FindMenu(_("&Window"));
295 if (pos
!= wxNOT_FOUND
)
297 wxASSERT(m_windowMenu
== pMenuBar
->GetMenu(pos
)); // DBG:: We're going to delete the wrong menu!!!
298 pMenuBar
->Remove(pos
);
303 void wxGenericMDIParentFrame::AddWindowMenu(wxMenuBar
*pMenuBar
)
305 if (pMenuBar
&& m_windowMenu
)
307 int pos
= pMenuBar
->FindMenu(wxGetStockLabel(wxID_HELP
,false));
308 if (pos
== wxNOT_FOUND
)
310 pMenuBar
->Append(m_windowMenu
, _("&Window"));
314 pMenuBar
->Insert(pos
, m_windowMenu
, _("&Window"));
319 void wxGenericMDIParentFrame::OnWindowMenu(wxCommandEvent
&event
)
321 switch ( event
.GetId() )
324 if ( m_currentChild
)
325 m_currentChild
->Close();
328 case wxWINDOWCLOSEALL
:
344 #endif // wxUSE_MENUS
346 void wxGenericMDIParentFrame::OnClose(wxCloseEvent
& event
)
354 bool wxGenericMDIParentFrame::ProcessEvent(wxEvent
& event
)
356 if ( m_currentChild
)
358 // the menu events should be given to the child as we show its menu bar
360 const wxEventType eventType
= event
.GetEventType();
361 if ( eventType
== wxEVT_MENU
||
362 eventType
== wxEVT_UPDATE_UI
)
364 // set the flag indicating that this event was forwarded to the
365 // child from the parent and so shouldn't be propagated upwards if
366 // not processed to avoid infinite loop
367 m_childHandler
= m_currentChild
;
368 wxON_BLOCK_EXIT_NULL(m_childHandler
);
370 if ( m_currentChild
->ProcessWindowEvent(event
) )
375 return wxMDIParentFrameBase::ProcessEvent(event
);
378 // ----------------------------------------------------------------------------
379 // wxGenericMDIChildFrame
380 // ----------------------------------------------------------------------------
382 IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIChildFrame
, wxFrame
)
384 BEGIN_EVENT_TABLE(wxGenericMDIChildFrame
, wxFrame
)
385 EVT_MENU_HIGHLIGHT_ALL(wxGenericMDIChildFrame::OnMenuHighlight
)
387 EVT_CLOSE(wxGenericMDIChildFrame::OnClose
)
390 void wxGenericMDIChildFrame::Init()
394 #endif // wxUSE_MENUS
396 #if !wxUSE_GENERIC_MDI_AS_NATIVE
397 m_mdiParentGeneric
= NULL
;
401 wxGenericMDIChildFrame::~wxGenericMDIChildFrame()
403 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
405 // it could happen that we don't have a valid parent if we hadn't been ever
406 // really created -- but in this case there is nothing else to do neither
408 parent
->WXRemoveChild(this);
412 #endif // wxUSE_MENUS
415 bool wxGenericMDIChildFrame::Create(wxGenericMDIParentFrame
*parent
,
417 const wxString
& title
,
418 const wxPoint
& WXUNUSED(pos
),
420 long WXUNUSED(style
),
421 const wxString
& name
)
423 // unfortunately we can't use the base class m_mdiParent field unless
424 // wxGenericMDIParentFrame is wxMDIParentFrame
425 #if wxUSE_GENERIC_MDI_AS_NATIVE
426 m_mdiParent
= parent
;
427 #else // generic != native
428 // leave m_mdiParent NULL, we don't have it
429 m_mdiParentGeneric
= parent
;
432 wxBookCtrlBase
* const book
= parent
->GetBookCtrl();
434 wxASSERT_MSG( book
, "Missing MDI client window." );
436 // note that we ignore the styles, none of the usual TLW styles apply to
437 // this (child) window
438 if ( !wxWindow::Create(book
, id
, wxDefaultPosition
, size
, 0, name
) )
442 book
->AddPage(this, title
, true);
448 void wxGenericMDIChildFrame::SetMenuBar( wxMenuBar
*menu_bar
)
450 wxMenuBar
*pOldMenuBar
= m_pMenuBar
;
451 m_pMenuBar
= menu_bar
;
455 wxGenericMDIParentFrame
*parent
= GetGenericMDIParent();
459 m_pMenuBar
->SetParent(parent
);
461 if ( parent
->WXIsActiveChild(this) )
463 // Replace current menu bars
465 parent
->WXSetChildMenuBar(NULL
);
466 parent
->WXSetChildMenuBar(this);
472 wxMenuBar
*wxGenericMDIChildFrame::GetMenuBar() const
476 #endif // wxUSE_MENUS
478 void wxGenericMDIChildFrame::SetTitle(const wxString
& title
)
482 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
484 parent
->WXUpdateChildTitle(this);
485 //else: it's ok, we might be not created yet
488 void wxGenericMDIChildFrame::Activate()
490 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
492 wxCHECK_RET( parent
, "can't activate MDI child without parent" );
493 parent
->WXActivateChild(this);
496 void wxGenericMDIChildFrame::OnMenuHighlight(wxMenuEvent
& event
)
498 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
501 // we don't have any help text for this item,
502 // but may be the MDI frame does?
503 parent
->OnMenuHighlight(event
);
507 void wxGenericMDIChildFrame::OnClose(wxCloseEvent
& WXUNUSED(event
))
509 // we're not a TLW so don't delay the destruction of this window
513 bool wxGenericMDIChildFrame::TryAfter(wxEvent
& event
)
515 // we shouldn't propagate the event to the parent if we received it from it
516 // in the first place
517 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
518 if ( parent
&& parent
->WXIsInsideChildHandler(this) )
521 return wxTDIChildFrame::TryAfter(event
);
524 // ----------------------------------------------------------------------------
525 // wxGenericMDIClientWindow
526 // ----------------------------------------------------------------------------
528 IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIClientWindow
, wxWindow
)
531 wxGenericMDIClientWindow::CreateGenericClient(wxWindow
*parent
)
533 if ( !wxWindow::Create(parent
, wxID_ANY
) )
536 m_notebook
= new wxNotebook(this, wxID_ANY
);
539 wxEVT_NOTEBOOK_PAGE_CHANGED
,
540 wxNotebookEventHandler(
541 wxGenericMDIClientWindow::OnPageChanged
),
546 // now that we have a notebook to resize, hook up OnSize() too
547 Connect(wxEVT_SIZE
, wxSizeEventHandler(wxGenericMDIClientWindow::OnSize
));
552 wxBookCtrlBase
*wxGenericMDIClientWindow::GetBookCtrl() const
557 wxGenericMDIChildFrame
*wxGenericMDIClientWindow::GetChild(size_t pos
) const
559 return static_cast<wxGenericMDIChildFrame
*>(GetBookCtrl()->GetPage(pos
));
562 int wxGenericMDIClientWindow::FindChild(wxGenericMDIChildFrame
*child
) const
564 wxBookCtrlBase
* const book
= GetBookCtrl();
565 const size_t count
= book
->GetPageCount();
566 for ( size_t pos
= 0; pos
< count
; pos
++ )
568 if ( book
->GetPage(pos
) == child
)
575 void wxGenericMDIClientWindow::PageChanged(int oldSelection
, int newSelection
)
577 // Don't do anything if nothing changed
578 if (oldSelection
== newSelection
)
581 // Again check if we really need to do this...
582 if (newSelection
!= -1)
584 wxGenericMDIChildFrame
* const child
= GetChild(newSelection
);
586 if ( child
->GetGenericMDIParent()->WXIsActiveChild(child
) )
590 // Notify old active child that it has been deactivated
591 if (oldSelection
!= -1)
593 wxGenericMDIChildFrame
* const oldChild
= GetChild(oldSelection
);
596 wxActivateEvent
event(wxEVT_ACTIVATE
, false, oldChild
->GetId());
597 event
.SetEventObject( oldChild
);
598 oldChild
->GetEventHandler()->ProcessEvent(event
);
602 // Notify new active child that it has been activated
603 if (newSelection
!= -1)
605 wxGenericMDIChildFrame
* const activeChild
= GetChild(newSelection
);
608 wxActivateEvent
event(wxEVT_ACTIVATE
, true, activeChild
->GetId());
609 event
.SetEventObject( activeChild
);
610 activeChild
->GetEventHandler()->ProcessEvent(event
);
612 wxGenericMDIParentFrame
* const
613 parent
= activeChild
->GetGenericMDIParent();
617 // this is a dirty hack as activeChild is not really a
618 // wxMDIChildFrame at all but we still want to store it in the
619 // base class m_currentChild field and this will work as long
620 // as we only use as wxMDIChildFrameBase pointer (which it is)
621 parent
->SetActiveChild(
622 reinterpret_cast<wxMDIChildFrame
*>(activeChild
));
623 parent
->WXSetChildMenuBar(activeChild
);
629 void wxGenericMDIClientWindow::OnPageChanged(wxBookCtrlEvent
& event
)
631 PageChanged(event
.GetOldSelection(), event
.GetSelection());
636 void wxGenericMDIClientWindow::OnSize(wxSizeEvent
& WXUNUSED(event
))
638 m_notebook
->SetSize(GetClientSize());