1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/mdig.cpp
3 // Purpose: Generic MDI (Multiple Document Interface) classes
4 // Author: Hans Van Leemputten
8 // Copyright: (c) Hans Van Leemputten
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
29 #include "wx/generic/mdig.h"
38 #include "wx/stockitem.h"
48 //-----------------------------------------------------------------------------
49 // wxGenericMDIParentFrame
50 //-----------------------------------------------------------------------------
52 IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIParentFrame
, wxFrame
)
54 BEGIN_EVENT_TABLE(wxGenericMDIParentFrame
, wxFrame
)
56 EVT_MENU (wxID_ANY
, wxGenericMDIParentFrame::DoHandleMenu
)
60 wxGenericMDIParentFrame::wxGenericMDIParentFrame()
65 wxGenericMDIParentFrame::wxGenericMDIParentFrame(wxWindow
*parent
,
67 const wxString
& title
,
75 (void)Create(parent
, id
, title
, pos
, size
, style
, name
);
78 wxGenericMDIParentFrame::~wxGenericMDIParentFrame()
80 // Make sure the client window is destructed before the menu bars are!
81 wxDELETE(m_pClientWindow
);
87 m_pMyMenuBar
= (wxMenuBar
*) NULL
;
90 RemoveWindowMenu(GetMenuBar());
95 m_pWindowMenu
= (wxMenu
*) NULL
;
100 bool wxGenericMDIParentFrame::Create(wxWindow
*parent
,
102 const wxString
& title
,
106 const wxString
& name
)
108 // this style can be used to prevent a window from having the standard MDI
110 if ( !(style
& wxFRAME_NO_WINDOW_MENU
) )
113 m_pWindowMenu
= new wxMenu
;
115 m_pWindowMenu
->Append(wxWINDOWCLOSE
, _("Cl&ose"));
116 m_pWindowMenu
->Append(wxWINDOWCLOSEALL
, _("Close All"));
117 m_pWindowMenu
->AppendSeparator();
118 m_pWindowMenu
->Append(wxWINDOWNEXT
, _("&Next"));
119 m_pWindowMenu
->Append(wxWINDOWPREV
, _("&Previous"));
120 #endif // wxUSE_MENUS
123 if ( !wxFrame::Create( parent
, id
, title
, pos
, size
, style
, name
) )
126 m_pClientWindow
= OnCreateClient();
128 return m_pClientWindow
!= NULL
;
132 void wxGenericMDIParentFrame::SetWindowMenu(wxMenu
* pMenu
)
134 // Replace the window menu from the currently loaded menu bar.
135 wxMenuBar
*pMenuBar
= GetMenuBar();
139 RemoveWindowMenu(pMenuBar
);
141 wxDELETE(m_pWindowMenu
);
146 m_pWindowMenu
= pMenu
;
148 AddWindowMenu(pMenuBar
);
152 void wxGenericMDIParentFrame::SetMenuBar(wxMenuBar
*pMenuBar
)
154 // Remove the Window menu from the old menu bar
155 RemoveWindowMenu(GetMenuBar());
156 // Add the Window menu to the new menu bar.
157 AddWindowMenu(pMenuBar
);
159 wxFrame::SetMenuBar(pMenuBar
);
161 #endif // wxUSE_MENUS
163 void wxGenericMDIParentFrame::SetChildMenuBar(wxGenericMDIChildFrame
*pChild
)
166 if (pChild
== (wxGenericMDIChildFrame
*) NULL
)
168 // No Child, set Our menu bar back.
169 SetMenuBar(m_pMyMenuBar
);
171 // Make sure we know our menu bar is in use
172 m_pMyMenuBar
= (wxMenuBar
*) NULL
;
176 if (pChild
->GetMenuBar() == (wxMenuBar
*) NULL
)
179 // Do we need to save the current bar?
180 if (m_pMyMenuBar
== NULL
)
181 m_pMyMenuBar
= GetMenuBar();
183 SetMenuBar(pChild
->GetMenuBar());
185 #endif // wxUSE_MENUS
188 bool wxGenericMDIParentFrame::ProcessEvent(wxEvent
& event
)
191 * Redirect events to active child first.
194 // Stops the same event being processed repeatedly
195 static wxEventType inEvent
= wxEVT_NULL
;
196 if (inEvent
== event
.GetEventType())
199 inEvent
= event
.GetEventType();
201 // Let the active child (if any) process the event first.
203 if (m_pActiveChild
&& event
.IsKindOf(CLASSINFO(wxCommandEvent
))
205 /* This is sure to not give problems... */
206 && (event
.GetEventType() == wxEVT_COMMAND_MENU_SELECTED
||
207 event
.GetEventType() == wxEVT_UPDATE_UI
)
209 /* This was tested on wxMSW and worked... */
210 && event
.GetEventObject() != m_pClientWindow
211 && !(event
.GetEventType() == wxEVT_ACTIVATE
||
212 event
.GetEventType() == wxEVT_SET_FOCUS
||
213 event
.GetEventType() == wxEVT_KILL_FOCUS
||
214 event
.GetEventType() == wxEVT_CHILD_FOCUS
||
215 event
.GetEventType() == wxEVT_COMMAND_SET_FOCUS
||
216 event
.GetEventType() == wxEVT_COMMAND_KILL_FOCUS
)
220 res
= m_pActiveChild
->GetEventHandler()->ProcessEvent(event
);
223 // If the event was not handled this frame will handle it!
226 res
= GetEventHandler()->wxEvtHandler::ProcessEvent(event
);
229 inEvent
= wxEVT_NULL
;
234 wxGenericMDIChildFrame
*wxGenericMDIParentFrame::GetActiveChild() const
236 return m_pActiveChild
;
239 void wxGenericMDIParentFrame::SetActiveChild(wxGenericMDIChildFrame
* pChildFrame
)
241 m_pActiveChild
= pChildFrame
;
244 wxGenericMDIClientWindow
*wxGenericMDIParentFrame::GetClientWindow() const
246 return m_pClientWindow
;
249 wxGenericMDIClientWindow
*wxGenericMDIParentFrame::OnCreateClient()
251 #if wxUSE_GENERIC_MDI_AS_NATIVE
252 return new wxMDIClientWindow( this );
254 return new wxGenericMDIClientWindow( this );
258 void wxGenericMDIParentFrame::ActivateNext()
260 if (m_pClientWindow
&& m_pClientWindow
->GetSelection() != -1)
262 size_t active
= m_pClientWindow
->GetSelection() + 1;
263 if (active
>= m_pClientWindow
->GetPageCount())
266 m_pClientWindow
->SetSelection(active
);
270 void wxGenericMDIParentFrame::ActivatePrevious()
272 if (m_pClientWindow
&& m_pClientWindow
->GetSelection() != -1)
274 int active
= m_pClientWindow
->GetSelection() - 1;
276 active
= m_pClientWindow
->GetPageCount() - 1;
278 m_pClientWindow
->SetSelection(active
);
282 void wxGenericMDIParentFrame::Init()
284 m_pClientWindow
= (wxGenericMDIClientWindow
*) NULL
;
285 m_pActiveChild
= (wxGenericMDIChildFrame
*) NULL
;
287 m_pWindowMenu
= (wxMenu
*) NULL
;
288 m_pMyMenuBar
= (wxMenuBar
*) NULL
;
289 #endif // wxUSE_MENUS
293 void wxGenericMDIParentFrame::RemoveWindowMenu(wxMenuBar
*pMenuBar
)
295 if (pMenuBar
&& m_pWindowMenu
)
297 // Remove old window menu
298 int pos
= pMenuBar
->FindMenu(_("&Window"));
299 if (pos
!= wxNOT_FOUND
)
301 wxASSERT(m_pWindowMenu
== pMenuBar
->GetMenu(pos
)); // DBG:: We're going to delete the wrong menu!!!
302 pMenuBar
->Remove(pos
);
307 void wxGenericMDIParentFrame::AddWindowMenu(wxMenuBar
*pMenuBar
)
309 if (pMenuBar
&& m_pWindowMenu
)
311 int pos
= pMenuBar
->FindMenu(wxGetStockLabel(wxID_HELP
,false));
312 if (pos
== wxNOT_FOUND
)
314 pMenuBar
->Append(m_pWindowMenu
, _("&Window"));
318 pMenuBar
->Insert(pos
, m_pWindowMenu
, _("&Window"));
323 void wxGenericMDIParentFrame::DoHandleMenu(wxCommandEvent
&event
)
325 switch (event
.GetId())
330 m_pActiveChild
->Close();
333 case wxWINDOWCLOSEALL
:
335 #if 0 // code is only needed if next #if is set to 0!
336 wxGenericMDIChildFrame
*pFirstActiveChild
= m_pActiveChild
;
338 while (m_pActiveChild
)
340 if (!m_pActiveChild
->Close())
342 return; // We failed...
346 #if 1 // What's best? Delayed deleting or immediate deleting?
347 delete m_pActiveChild
;
348 m_pActiveChild
= NULL
;
352 if (pFirstActiveChild
== m_pActiveChild
)
353 return; // We've called Close on all items, no need to continue.
369 #endif // wxUSE_MENUS
371 void wxGenericMDIParentFrame::DoGetClientSize(int *width
, int *height
) const
373 wxFrame::DoGetClientSize( width
, height
);
377 //-----------------------------------------------------------------------------
378 // wxGenericMDIChildFrame
379 //-----------------------------------------------------------------------------
381 IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIChildFrame
, wxPanel
)
383 BEGIN_EVENT_TABLE(wxGenericMDIChildFrame
, wxPanel
)
384 EVT_MENU_HIGHLIGHT_ALL(wxGenericMDIChildFrame::OnMenuHighlight
)
385 EVT_ACTIVATE(wxGenericMDIChildFrame::OnActivate
)
387 EVT_CLOSE(wxGenericMDIChildFrame::OnCloseWindow
)
388 EVT_SIZE(wxGenericMDIChildFrame::OnSize
)
391 wxGenericMDIChildFrame::wxGenericMDIChildFrame()
396 wxGenericMDIChildFrame::wxGenericMDIChildFrame( wxGenericMDIParentFrame
*parent
,
397 wxWindowID id
, const wxString
& title
,
398 const wxPoint
& WXUNUSED(pos
), const wxSize
& size
,
399 long style
, const wxString
& name
)
403 Create( parent
, id
, title
, wxDefaultPosition
, size
, style
, name
);
406 wxGenericMDIChildFrame::~wxGenericMDIChildFrame()
408 wxGenericMDIParentFrame
*pParentFrame
= GetMDIParentFrame();
410 if (pParentFrame
!= NULL
)
412 bool bActive
= false;
413 if (pParentFrame
->GetActiveChild() == this)
415 pParentFrame
->SetActiveChild((wxGenericMDIChildFrame
*) NULL
);
416 pParentFrame
->SetChildMenuBar((wxGenericMDIChildFrame
*) NULL
);
420 wxGenericMDIClientWindow
*pClientWindow
= pParentFrame
->GetClientWindow();
422 // Remove page if still there
424 for (pos
= 0; pos
< pClientWindow
->GetPageCount(); pos
++)
426 if (pClientWindow
->GetPage(pos
) == this)
428 if (pClientWindow
->RemovePage(pos
))
429 pClientWindow
->Refresh();
436 // Set the new selection to the a remaining page
437 if (pClientWindow
->GetPageCount() > pos
)
439 pClientWindow
->SetSelection(pos
);
443 if ((int)pClientWindow
->GetPageCount() - 1 >= 0)
444 pClientWindow
->SetSelection(pClientWindow
->GetPageCount() - 1);
450 wxDELETE(m_pMenuBar
);
451 #endif // wxUSE_MENUS
454 bool wxGenericMDIChildFrame::Create( wxGenericMDIParentFrame
*parent
,
455 wxWindowID id
, const wxString
& title
,
456 const wxPoint
& WXUNUSED(pos
), const wxSize
& size
,
457 long style
, const wxString
& name
)
459 wxGenericMDIClientWindow
* pClientWindow
= parent
->GetClientWindow();
461 wxASSERT_MSG((pClientWindow
!= (wxWindow
*) NULL
), wxT("Missing MDI client window.") );
463 wxPanel::Create(pClientWindow
, id
, wxDefaultPosition
, size
, style
, name
);
465 SetMDIParentFrame(parent
);
467 // This is the currently active child
468 parent
->SetActiveChild(this);
472 pClientWindow
->AddPage(this, title
, true);
473 ApplyMDIChildFrameRect(); // Ok confirme the size change!
474 pClientWindow
->Refresh();
480 void wxGenericMDIChildFrame::SetMenuBar( wxMenuBar
*menu_bar
)
482 wxMenuBar
*pOldMenuBar
= m_pMenuBar
;
483 m_pMenuBar
= menu_bar
;
487 wxGenericMDIParentFrame
*pParentFrame
= GetMDIParentFrame();
489 if (pParentFrame
!= NULL
)
491 m_pMenuBar
->SetParent(pParentFrame
);
493 if (pParentFrame
->GetActiveChild() == this)
495 // Replace current menu bars
497 pParentFrame
->SetChildMenuBar((wxGenericMDIChildFrame
*) NULL
);
498 pParentFrame
->SetChildMenuBar((wxGenericMDIChildFrame
*) this);
504 wxMenuBar
*wxGenericMDIChildFrame::GetMenuBar() const
508 #endif // wxUSE_MENUS
510 void wxGenericMDIChildFrame::SetTitle(const wxString
& title
)
514 wxGenericMDIParentFrame
*pParentFrame
= GetMDIParentFrame();
516 if (pParentFrame
!= NULL
)
518 wxGenericMDIClientWindow
* pClientWindow
= pParentFrame
->GetClientWindow();
520 if (pClientWindow
!= NULL
)
523 for (pos
= 0; pos
< pClientWindow
->GetPageCount(); pos
++)
525 if (pClientWindow
->GetPage(pos
) == this)
527 pClientWindow
->SetPageText(pos
, m_Title
);
535 wxString
wxGenericMDIChildFrame::GetTitle() const
540 void wxGenericMDIChildFrame::Activate()
542 wxGenericMDIParentFrame
*pParentFrame
= GetMDIParentFrame();
544 if (pParentFrame
!= NULL
)
546 wxGenericMDIClientWindow
* pClientWindow
= pParentFrame
->GetClientWindow();
548 if (pClientWindow
!= NULL
)
551 for (pos
= 0; pos
< pClientWindow
->GetPageCount(); pos
++)
553 if (pClientWindow
->GetPage(pos
) == this)
555 pClientWindow
->SetSelection(pos
);
563 void wxGenericMDIChildFrame::OnMenuHighlight(wxMenuEvent
& event
)
566 if ( m_pMDIParentFrame
)
568 // we don't have any help text for this item,
569 // but may be the MDI frame does?
570 m_pMDIParentFrame
->OnMenuHighlight(event
);
574 #endif // wxUSE_STATUSBAR
577 void wxGenericMDIChildFrame::OnActivate(wxActivateEvent
& WXUNUSED(event
))
582 /*** Copied from top level..! ***/
583 // default resizing behaviour - if only ONE subwindow, resize to fill the
585 void wxGenericMDIChildFrame::OnSize(wxSizeEvent
& WXUNUSED(event
))
587 // if we're using constraints or sizers - do use them
588 if ( GetAutoLayout() )
594 // do we have _exactly_ one child?
595 wxWindow
*child
= (wxWindow
*)NULL
;
596 for ( wxWindowList::compatibility_iterator node
= GetChildren().GetFirst();
598 node
= node
->GetNext() )
600 wxWindow
*win
= node
->GetData();
602 // exclude top level and managed windows (status bar isn't
603 // currently in the children list except under wxMac anyhow, but
604 // it makes no harm to test for it)
605 if ( !win
->IsTopLevel() /*&& !IsOneOfBars(win)*/ )
609 return; // it's our second subwindow - nothing to do
616 // do we have any children at all?
619 // exactly one child - set it's size to fill the whole frame
620 int clientW
, clientH
;
621 DoGetClientSize(&clientW
, &clientH
);
623 // for whatever reasons, wxGTK wants to have a small offset - it
624 // probably looks better with it?
626 static const int ofs
= 1;
628 static const int ofs
= 0;
631 child
->SetSize(ofs
, ofs
, clientW
- 2*ofs
, clientH
- 2*ofs
);
636 /*** Copied from top level..! ***/
637 // The default implementation for the close window event.
638 void wxGenericMDIChildFrame::OnCloseWindow(wxCloseEvent
& WXUNUSED(event
))
643 void wxGenericMDIChildFrame::SetMDIParentFrame(wxGenericMDIParentFrame
* parentFrame
)
645 m_pMDIParentFrame
= parentFrame
;
648 wxGenericMDIParentFrame
* wxGenericMDIChildFrame::GetMDIParentFrame() const
650 return m_pMDIParentFrame
;
653 void wxGenericMDIChildFrame::Init()
655 m_pMDIParentFrame
= (wxGenericMDIParentFrame
*) NULL
;
657 m_pMenuBar
= (wxMenuBar
*) NULL
;
658 #endif // wxUSE_MENUS
661 void wxGenericMDIChildFrame::DoMoveWindow(int x
, int y
, int width
, int height
)
663 m_MDIRect
= wxRect(x
, y
, width
, height
);
666 void wxGenericMDIChildFrame::ApplyMDIChildFrameRect()
668 wxPanel::DoMoveWindow(m_MDIRect
.x
, m_MDIRect
.y
, m_MDIRect
.width
, m_MDIRect
.height
);
671 //-----------------------------------------------------------------------------
672 // wxGenericMDIClientWindow
673 //-----------------------------------------------------------------------------
675 #define wxID_NOTEBOOK_CLIENT_AREA wxID_HIGHEST + 100
677 IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIClientWindow
, wxNotebook
)
679 BEGIN_EVENT_TABLE(wxGenericMDIClientWindow
, wxNotebook
)
680 EVT_NOTEBOOK_PAGE_CHANGED(wxID_NOTEBOOK_CLIENT_AREA
, wxGenericMDIClientWindow::OnPageChanged
)
681 EVT_SIZE(wxGenericMDIClientWindow::OnSize
)
685 wxGenericMDIClientWindow::wxGenericMDIClientWindow()
689 wxGenericMDIClientWindow::wxGenericMDIClientWindow( wxGenericMDIParentFrame
*parent
, long style
)
691 CreateClient( parent
, style
);
694 wxGenericMDIClientWindow::~wxGenericMDIClientWindow()
699 bool wxGenericMDIClientWindow::CreateClient( wxGenericMDIParentFrame
*parent
, long style
)
701 SetWindowStyleFlag(style
);
703 bool success
= wxNotebook::Create(parent
, wxID_NOTEBOOK_CLIENT_AREA
, wxPoint(0,0), wxSize(100, 100), 0);
707 wxFont font(10, wxSWISS, wxNORMAL, wxNORMAL);
708 wxFont selFont(10, wxSWISS, wxNORMAL, wxBOLD);
709 GetTabView()->SetTabFont(font);
710 GetTabView()->SetSelectedTabFont(selFont);
711 GetTabView()->SetTabSize(120, 18);
712 GetTabView()->SetTabSelectionHeight(20);
720 int wxGenericMDIClientWindow::SetSelection(size_t nPage
)
722 int oldSelection
= wxNotebook::SetSelection(nPage
);
724 #if !defined(__WXMSW__) // No need to do this for wxMSW as wxNotebook::SetSelection()
725 // will already cause this to be done!
726 // Handle the page change.
727 PageChanged(oldSelection
, nPage
);
733 void wxGenericMDIClientWindow::PageChanged(int OldSelection
, int newSelection
)
735 // Don't do to much work, only when something realy should change!
736 if (OldSelection
== newSelection
)
738 // Again check if we realy need to do this...
739 if (newSelection
!= -1)
741 wxGenericMDIChildFrame
* child
= (wxGenericMDIChildFrame
*)GetPage(newSelection
);
743 if (child
->GetMDIParentFrame()->GetActiveChild() == child
)
747 // Notify old active child that it has been deactivated
748 if (OldSelection
!= -1)
750 wxGenericMDIChildFrame
* oldChild
= (wxGenericMDIChildFrame
*)GetPage(OldSelection
);
753 wxActivateEvent
event(wxEVT_ACTIVATE
, false, oldChild
->GetId());
754 event
.SetEventObject( oldChild
);
755 oldChild
->GetEventHandler()->ProcessEvent(event
);
759 // Notify new active child that it has been activated
760 if (newSelection
!= -1)
762 wxGenericMDIChildFrame
* activeChild
= (wxGenericMDIChildFrame
*)GetPage(newSelection
);
765 wxActivateEvent
event(wxEVT_ACTIVATE
, true, activeChild
->GetId());
766 event
.SetEventObject( activeChild
);
767 activeChild
->GetEventHandler()->ProcessEvent(event
);
769 if (activeChild
->GetMDIParentFrame())
771 activeChild
->GetMDIParentFrame()->SetActiveChild(activeChild
);
772 activeChild
->GetMDIParentFrame()->SetChildMenuBar(activeChild
);
778 void wxGenericMDIClientWindow::OnPageChanged(wxBookCtrlEvent
& event
)
780 PageChanged(event
.GetOldSelection(), event
.GetSelection());
785 void wxGenericMDIClientWindow::OnSize(wxSizeEvent
& event
)
787 wxNotebook::OnSize(event
);
790 for (pos
= 0; pos
< GetPageCount(); pos
++)
792 ((wxGenericMDIChildFrame
*)GetPage(pos
))->ApplyMDIChildFrameRect();
798 * Define normal wxMDI classes based on wxGenericMDI
801 #if wxUSE_GENERIC_MDI_AS_NATIVE
803 wxMDIChildFrame
* wxMDIParentFrame::GetActiveChild() const
805 wxGenericMDIChildFrame
*pGFrame
= wxGenericMDIParentFrame::GetActiveChild();
806 wxMDIChildFrame
*pFrame
= wxDynamicCast(pGFrame
, wxMDIChildFrame
);
808 wxASSERT_MSG(!(pFrame
== NULL
&& pGFrame
!= NULL
), wxT("Active frame is class not derived from wxMDIChildFrame!"));
813 IMPLEMENT_DYNAMIC_CLASS(wxMDIParentFrame
, wxGenericMDIParentFrame
)
814 IMPLEMENT_DYNAMIC_CLASS(wxMDIChildFrame
, wxGenericMDIChildFrame
)
815 IMPLEMENT_DYNAMIC_CLASS(wxMDIClientWindow
, wxGenericMDIClientWindow
)
817 #endif // wxUSE_GENERIC_MDI_AS_NATIVE