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
);
83 RemoveWindowMenu(GetMenuBar());
87 bool wxGenericMDIParentFrame::Create(wxWindow
*parent
,
89 const wxString
& title
,
95 // this style can be used to prevent a window from having the standard MDI
97 if ( !(style
& wxFRAME_NO_WINDOW_MENU
) )
100 m_windowMenu
= new wxMenu
;
102 m_windowMenu
->Append(wxWINDOWCLOSE
, _("Cl&ose"));
103 m_windowMenu
->Append(wxWINDOWCLOSEALL
, _("Close All"));
104 m_windowMenu
->AppendSeparator();
105 m_windowMenu
->Append(wxWINDOWNEXT
, _("&Next"));
106 m_windowMenu
->Append(wxWINDOWPREV
, _("&Previous"));
107 #endif // wxUSE_MENUS
110 // the scrolling styles don't make sense neither for us nor for our client
111 // window (to which they're supposed to apply)
112 style
&= ~(wxHSCROLL
| wxVSCROLL
);
114 if ( !wxFrame::Create( parent
, id
, title
, pos
, size
, style
, name
) )
117 wxGenericMDIClientWindow
* const client
= OnCreateGenericClient();
118 if ( !client
->CreateGenericClient(this) )
121 m_clientWindow
= client
;
126 wxGenericMDIClientWindow
*wxGenericMDIParentFrame::OnCreateGenericClient()
128 return new wxGenericMDIClientWindow
;
131 bool wxGenericMDIParentFrame::CloseAll()
133 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
135 return true; // none of the windows left
137 wxBookCtrlBase
* const book
= client
->GetBookCtrl();
138 while ( book
->GetPageCount() )
140 wxGenericMDIChildFrame
* const child
= client
->GetChild(0);
141 if ( !child
->Close() )
143 // it refused to close, don't close the remaining ones neither
152 void wxGenericMDIParentFrame::SetWindowMenu(wxMenu
* pMenu
)
154 // Replace the window menu from the currently loaded menu bar.
155 wxMenuBar
*pMenuBar
= GetMenuBar();
159 RemoveWindowMenu(pMenuBar
);
161 wxDELETE(m_windowMenu
);
166 m_windowMenu
= pMenu
;
168 AddWindowMenu(pMenuBar
);
172 void wxGenericMDIParentFrame::SetMenuBar(wxMenuBar
*pMenuBar
)
174 // Remove the Window menu from the old menu bar
175 RemoveWindowMenu(GetMenuBar());
176 // Add the Window menu to the new menu bar.
177 AddWindowMenu(pMenuBar
);
179 wxFrame::SetMenuBar(pMenuBar
);
181 #endif // wxUSE_MENUS
183 void wxGenericMDIParentFrame::WXSetChildMenuBar(wxGenericMDIChildFrame
*pChild
)
188 // No Child, set Our menu bar back.
189 SetMenuBar(m_pMyMenuBar
);
191 // Make sure we know our menu bar is in use
196 if (pChild
->GetMenuBar() == NULL
)
199 // Do we need to save the current bar?
200 if (m_pMyMenuBar
== NULL
)
201 m_pMyMenuBar
= GetMenuBar();
203 SetMenuBar(pChild
->GetMenuBar());
205 #endif // wxUSE_MENUS
208 wxGenericMDIClientWindow
*
209 wxGenericMDIParentFrame::GetGenericClientWindow() const
211 return static_cast<wxGenericMDIClientWindow
*>(m_clientWindow
);
214 wxBookCtrlBase
*wxGenericMDIParentFrame::GetBookCtrl() const
216 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
217 return client
? client
->GetBookCtrl() : NULL
;
220 void wxGenericMDIParentFrame::AdvanceActive(bool forward
)
222 wxBookCtrlBase
* const book
= GetBookCtrl();
224 book
->AdvanceSelection(forward
);
227 void wxGenericMDIParentFrame::WXUpdateChildTitle(wxGenericMDIChildFrame
*child
)
229 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
231 const int pos
= client
->FindChild(child
);
232 if ( pos
== wxNOT_FOUND
)
235 client
->GetBookCtrl()->SetPageText(pos
, child
->GetTitle());
238 void wxGenericMDIParentFrame::WXActivateChild(wxGenericMDIChildFrame
*child
)
240 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
242 const int pos
= client
->FindChild(child
);
243 if ( pos
== wxNOT_FOUND
)
246 client
->GetBookCtrl()->SetSelection(pos
);
249 void wxGenericMDIParentFrame::WXRemoveChild(wxGenericMDIChildFrame
*child
)
251 const bool removingActive
= WXIsActiveChild(child
);
252 if ( removingActive
)
254 SetActiveChild(NULL
);
255 WXSetChildMenuBar(NULL
);
258 wxGenericMDIClientWindow
* const client
= GetGenericClientWindow();
259 wxCHECK_RET( client
, "should have client window" );
261 wxBookCtrlBase
* const book
= client
->GetBookCtrl();
263 // Remove page if still there
264 int pos
= client
->FindChild(child
);
265 if ( pos
!= wxNOT_FOUND
)
267 if ( book
->RemovePage(pos
) )
271 if ( removingActive
)
273 // Set the new selection to a remaining page
274 const size_t count
= book
->GetPageCount();
275 if ( count
> (size_t)pos
)
277 book
->SetSelection(pos
);
282 book
->SetSelection(count
- 1);
288 wxGenericMDIParentFrame::WXIsActiveChild(wxGenericMDIChildFrame
*child
) const
290 return static_cast<wxMDIChildFrameBase
*>(GetActiveChild()) == child
;
294 void wxGenericMDIParentFrame::RemoveWindowMenu(wxMenuBar
*pMenuBar
)
296 if (pMenuBar
&& m_windowMenu
)
298 // Remove old window menu
299 int pos
= pMenuBar
->FindMenu(_("&Window"));
300 if (pos
!= wxNOT_FOUND
)
302 wxASSERT(m_windowMenu
== pMenuBar
->GetMenu(pos
)); // DBG:: We're going to delete the wrong menu!!!
303 pMenuBar
->Remove(pos
);
308 void wxGenericMDIParentFrame::AddWindowMenu(wxMenuBar
*pMenuBar
)
310 if (pMenuBar
&& m_windowMenu
)
312 int pos
= pMenuBar
->FindMenu(wxGetStockLabel(wxID_HELP
,false));
313 if (pos
== wxNOT_FOUND
)
315 pMenuBar
->Append(m_windowMenu
, _("&Window"));
319 pMenuBar
->Insert(pos
, m_windowMenu
, _("&Window"));
324 void wxGenericMDIParentFrame::OnWindowMenu(wxCommandEvent
&event
)
326 switch ( event
.GetId() )
329 if ( m_currentChild
)
330 m_currentChild
->Close();
333 case wxWINDOWCLOSEALL
:
349 #endif // wxUSE_MENUS
351 void wxGenericMDIParentFrame::OnClose(wxCloseEvent
& event
)
359 bool wxGenericMDIParentFrame::ProcessEvent(wxEvent
& event
)
361 if ( m_currentChild
)
363 // the menu events should be given to the child as we show its menu bar
365 const wxEventType eventType
= event
.GetEventType();
366 if ( eventType
== wxEVT_COMMAND_MENU_SELECTED
||
367 eventType
== wxEVT_UPDATE_UI
)
369 // set the flag indicating that this event was forwarded to the
370 // child from the parent and so shouldn't be propagated upwards if
371 // not processed to avoid infinite loop
372 m_childHandler
= m_currentChild
;
373 wxON_BLOCK_EXIT_NULL(m_childHandler
);
375 if ( m_currentChild
->ProcessWindowEvent(event
) )
380 return wxMDIParentFrameBase::ProcessEvent(event
);
383 // ----------------------------------------------------------------------------
384 // wxGenericMDIChildFrame
385 // ----------------------------------------------------------------------------
387 IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIChildFrame
, wxFrame
)
389 BEGIN_EVENT_TABLE(wxGenericMDIChildFrame
, wxFrame
)
390 EVT_MENU_HIGHLIGHT_ALL(wxGenericMDIChildFrame::OnMenuHighlight
)
392 EVT_CLOSE(wxGenericMDIChildFrame::OnClose
)
395 void wxGenericMDIChildFrame::Init()
399 #endif // wxUSE_MENUS
401 #if !wxUSE_GENERIC_MDI_AS_NATIVE
402 m_mdiParentGeneric
= NULL
;
406 wxGenericMDIChildFrame::~wxGenericMDIChildFrame()
408 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
410 // it could happen that we don't have a valid parent if we hadn't been ever
411 // really created -- but in this case there is nothing else to do neither
413 parent
->WXRemoveChild(this);
417 #endif // wxUSE_MENUS
420 bool wxGenericMDIChildFrame::Create(wxGenericMDIParentFrame
*parent
,
422 const wxString
& title
,
423 const wxPoint
& WXUNUSED(pos
),
425 long WXUNUSED(style
),
426 const wxString
& name
)
428 // unfortunately we can't use the base class m_mdiParent field unless
429 // wxGenericMDIParentFrame is wxMDIParentFrame
430 #if wxUSE_GENERIC_MDI_AS_NATIVE
431 m_mdiParent
= parent
;
432 #else // generic != native
433 // leave m_mdiParent NULL, we don't have it
434 m_mdiParentGeneric
= parent
;
437 wxBookCtrlBase
* const book
= parent
->GetBookCtrl();
439 wxASSERT_MSG( book
, "Missing MDI client window." );
441 // note that we ignore the styles, none of the usual TLW styles apply to
442 // this (child) window
443 if ( !wxWindow::Create(book
, id
, wxDefaultPosition
, size
, 0, name
) )
447 book
->AddPage(this, title
, true);
453 void wxGenericMDIChildFrame::SetMenuBar( wxMenuBar
*menu_bar
)
455 wxMenuBar
*pOldMenuBar
= m_pMenuBar
;
456 m_pMenuBar
= menu_bar
;
460 wxGenericMDIParentFrame
*parent
= GetGenericMDIParent();
464 m_pMenuBar
->SetParent(parent
);
466 if ( parent
->WXIsActiveChild(this) )
468 // Replace current menu bars
470 parent
->WXSetChildMenuBar(NULL
);
471 parent
->WXSetChildMenuBar(this);
477 wxMenuBar
*wxGenericMDIChildFrame::GetMenuBar() const
481 #endif // wxUSE_MENUS
483 void wxGenericMDIChildFrame::SetTitle(const wxString
& title
)
487 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
489 parent
->WXUpdateChildTitle(this);
490 //else: it's ok, we might be not created yet
493 void wxGenericMDIChildFrame::Activate()
495 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
497 wxCHECK_RET( parent
, "can't activate MDI child without parent" );
498 parent
->WXActivateChild(this);
501 void wxGenericMDIChildFrame::OnMenuHighlight(wxMenuEvent
& event
)
503 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
506 // we don't have any help text for this item,
507 // but may be the MDI frame does?
508 parent
->OnMenuHighlight(event
);
512 void wxGenericMDIChildFrame::OnClose(wxCloseEvent
& WXUNUSED(event
))
514 // we're not a TLW so don't delay the destruction of this window
518 bool wxGenericMDIChildFrame::TryAfter(wxEvent
& event
)
520 // we shouldn't propagate the event to the parent if we received it from it
521 // in the first place
522 wxGenericMDIParentFrame
* const parent
= GetGenericMDIParent();
523 if ( parent
&& parent
->WXIsInsideChildHandler(this) )
526 return wxTDIChildFrame::TryAfter(event
);
529 // ----------------------------------------------------------------------------
530 // wxGenericMDIClientWindow
531 // ----------------------------------------------------------------------------
533 IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIClientWindow
, wxWindow
)
536 wxGenericMDIClientWindow::CreateGenericClient(wxWindow
*parent
)
538 if ( !wxWindow::Create(parent
, wxID_ANY
) )
541 m_notebook
= new wxNotebook(this, wxID_ANY
);
544 wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED
,
545 wxNotebookEventHandler(
546 wxGenericMDIClientWindow::OnPageChanged
),
551 // now that we have a notebook to resize, hook up OnSize() too
552 Connect(wxEVT_SIZE
, wxSizeEventHandler(wxGenericMDIClientWindow::OnSize
));
557 wxBookCtrlBase
*wxGenericMDIClientWindow::GetBookCtrl() const
562 wxGenericMDIChildFrame
*wxGenericMDIClientWindow::GetChild(size_t pos
) const
564 return static_cast<wxGenericMDIChildFrame
*>(GetBookCtrl()->GetPage(pos
));
567 int wxGenericMDIClientWindow::FindChild(wxGenericMDIChildFrame
*child
) const
569 wxBookCtrlBase
* const book
= GetBookCtrl();
570 const size_t count
= book
->GetPageCount();
571 for ( size_t pos
= 0; pos
< count
; pos
++ )
573 if ( book
->GetPage(pos
) == child
)
580 void wxGenericMDIClientWindow::PageChanged(int oldSelection
, int newSelection
)
582 // Don't do anything if nothing changed
583 if (oldSelection
== newSelection
)
586 // Again check if we really need to do this...
587 if (newSelection
!= -1)
589 wxGenericMDIChildFrame
* const child
= GetChild(newSelection
);
591 if ( child
->GetGenericMDIParent()->WXIsActiveChild(child
) )
595 // Notify old active child that it has been deactivated
596 if (oldSelection
!= -1)
598 wxGenericMDIChildFrame
* const oldChild
= GetChild(oldSelection
);
601 wxActivateEvent
event(wxEVT_ACTIVATE
, false, oldChild
->GetId());
602 event
.SetEventObject( oldChild
);
603 oldChild
->GetEventHandler()->ProcessEvent(event
);
607 // Notify new active child that it has been activated
608 if (newSelection
!= -1)
610 wxGenericMDIChildFrame
* const activeChild
= GetChild(newSelection
);
613 wxActivateEvent
event(wxEVT_ACTIVATE
, true, activeChild
->GetId());
614 event
.SetEventObject( activeChild
);
615 activeChild
->GetEventHandler()->ProcessEvent(event
);
617 wxGenericMDIParentFrame
* const
618 parent
= activeChild
->GetGenericMDIParent();
622 // this is a dirty hack as activeChild is not really a
623 // wxMDIChildFrame at all but we still want to store it in the
624 // base class m_currentChild field and this will work as long
625 // as we only use as wxMDIChildFrameBase pointer (which it is)
626 parent
->SetActiveChild(
627 reinterpret_cast<wxMDIChildFrame
*>(activeChild
));
628 parent
->WXSetChildMenuBar(activeChild
);
634 void wxGenericMDIClientWindow::OnPageChanged(wxBookCtrlEvent
& event
)
636 PageChanged(event
.GetOldSelection(), event
.GetSelection());
641 void wxGenericMDIClientWindow::OnSize(wxSizeEvent
& WXUNUSED(event
))
643 m_notebook
->SetSize(GetClientSize());