]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/generic/mdig.cpp
Let wxWindow::Fit[Inside]() work even for windows without children.
[wxWidgets.git] / src / generic / mdig.cpp
... / ...
CommitLineData
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
6// Created: 29/07/2002
7// RCS-ID: $Id$
8// Copyright: (c) 2002 Hans Van Leemputten
9// (c) 2008 Vadim Zeitlin
10// Licence: wxWindows licence
11/////////////////////////////////////////////////////////////////////////////
12
13// ===========================================================================
14// declarations
15// ===========================================================================
16
17// ---------------------------------------------------------------------------
18// headers
19// ---------------------------------------------------------------------------
20
21// For compilers that support precompilation, includes "wx.h".
22#include "wx/wxprec.h"
23
24#ifdef __BORLANDC__
25 #pragma hdrstop
26#endif
27
28#if wxUSE_MDI
29
30#ifndef WX_PRECOMP
31 #include "wx/menu.h"
32 #include "wx/intl.h"
33 #include "wx/log.h"
34#endif //WX_PRECOMP
35
36#include "wx/mdi.h"
37#include "wx/generic/mdig.h"
38#include "wx/notebook.h"
39#include "wx/scopeguard.h"
40
41#include "wx/stockitem.h"
42
43enum MDI_MENU_ID
44{
45 wxWINDOWCLOSE = 4001,
46 wxWINDOWCLOSEALL,
47 wxWINDOWNEXT,
48 wxWINDOWPREV
49};
50
51//-----------------------------------------------------------------------------
52// wxGenericMDIParentFrame
53//-----------------------------------------------------------------------------
54
55IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIParentFrame, wxFrame)
56
57BEGIN_EVENT_TABLE(wxGenericMDIParentFrame, wxFrame)
58 EVT_CLOSE(wxGenericMDIParentFrame::OnClose)
59#if wxUSE_MENUS
60 EVT_MENU(wxID_ANY, wxGenericMDIParentFrame::OnWindowMenu)
61#endif
62END_EVENT_TABLE()
63
64void wxGenericMDIParentFrame::Init()
65{
66#if wxUSE_MENUS
67 m_pMyMenuBar = NULL;
68#endif // wxUSE_MENUS
69}
70
71wxGenericMDIParentFrame::~wxGenericMDIParentFrame()
72{
73 // Make sure the client window is destructed before the menu bars are!
74 wxDELETE(m_clientWindow);
75
76#if wxUSE_MENUS
77 wxDELETE(m_pMyMenuBar);
78
79 RemoveWindowMenu(GetMenuBar());
80#endif // wxUSE_MENUS
81}
82
83bool wxGenericMDIParentFrame::Create(wxWindow *parent,
84 wxWindowID id,
85 const wxString& title,
86 const wxPoint& pos,
87 const wxSize& size,
88 long style,
89 const wxString& name)
90{
91 // this style can be used to prevent a window from having the standard MDI
92 // "Window" menu
93 if ( !(style & wxFRAME_NO_WINDOW_MENU) )
94 {
95#if wxUSE_MENUS
96 m_windowMenu = new wxMenu;
97
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
104 }
105
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);
109
110 if ( !wxFrame::Create( parent, id, title, pos, size, style, name ) )
111 return false;
112
113 wxGenericMDIClientWindow * const client = OnCreateGenericClient();
114 if ( !client->CreateGenericClient(this) )
115 return false;
116
117 m_clientWindow = client;
118
119 return true;
120}
121
122wxGenericMDIClientWindow *wxGenericMDIParentFrame::OnCreateGenericClient()
123{
124 return new wxGenericMDIClientWindow;
125}
126
127bool wxGenericMDIParentFrame::CloseAll()
128{
129 wxGenericMDIClientWindow * const client = GetGenericClientWindow();
130 if ( !client )
131 return true; // none of the windows left
132
133 wxBookCtrlBase * const book = client->GetBookCtrl();
134 while ( book->GetPageCount() )
135 {
136 wxGenericMDIChildFrame * const child = client->GetChild(0);
137 if ( !child->Close() )
138 {
139 // it refused to close, don't close the remaining ones neither
140 return false;
141 }
142 }
143
144 return true;
145}
146
147#if wxUSE_MENUS
148void wxGenericMDIParentFrame::SetWindowMenu(wxMenu* pMenu)
149{
150 // Replace the window menu from the currently loaded menu bar.
151 wxMenuBar *pMenuBar = GetMenuBar();
152
153 if (m_windowMenu)
154 {
155 RemoveWindowMenu(pMenuBar);
156
157 wxDELETE(m_windowMenu);
158 }
159
160 if (pMenu)
161 {
162 m_windowMenu = pMenu;
163
164 AddWindowMenu(pMenuBar);
165 }
166}
167
168void wxGenericMDIParentFrame::SetMenuBar(wxMenuBar *pMenuBar)
169{
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);
174
175 wxFrame::SetMenuBar(pMenuBar);
176}
177#endif // wxUSE_MENUS
178
179void wxGenericMDIParentFrame::WXSetChildMenuBar(wxGenericMDIChildFrame *pChild)
180{
181#if wxUSE_MENUS
182 if (pChild == NULL)
183 {
184 // No Child, set Our menu bar back.
185 SetMenuBar(m_pMyMenuBar);
186
187 // Make sure we know our menu bar is in use
188 m_pMyMenuBar = NULL;
189 }
190 else
191 {
192 if (pChild->GetMenuBar() == NULL)
193 return;
194
195 // Do we need to save the current bar?
196 if (m_pMyMenuBar == NULL)
197 m_pMyMenuBar = GetMenuBar();
198
199 SetMenuBar(pChild->GetMenuBar());
200 }
201#endif // wxUSE_MENUS
202}
203
204wxGenericMDIClientWindow *
205wxGenericMDIParentFrame::GetGenericClientWindow() const
206{
207 return static_cast<wxGenericMDIClientWindow *>(m_clientWindow);
208}
209
210wxBookCtrlBase *wxGenericMDIParentFrame::GetBookCtrl() const
211{
212 wxGenericMDIClientWindow * const client = GetGenericClientWindow();
213 return client ? client->GetBookCtrl() : NULL;
214}
215
216void wxGenericMDIParentFrame::AdvanceActive(bool forward)
217{
218 wxBookCtrlBase * const book = GetBookCtrl();
219 if ( book )
220 book->AdvanceSelection(forward);
221}
222
223void wxGenericMDIParentFrame::WXUpdateChildTitle(wxGenericMDIChildFrame *child)
224{
225 wxGenericMDIClientWindow * const client = GetGenericClientWindow();
226
227 const int pos = client->FindChild(child);
228 if ( pos == wxNOT_FOUND )
229 return;
230
231 client->GetBookCtrl()->SetPageText(pos, child->GetTitle());
232}
233
234void wxGenericMDIParentFrame::WXActivateChild(wxGenericMDIChildFrame *child)
235{
236 wxGenericMDIClientWindow * const client = GetGenericClientWindow();
237
238 const int pos = client->FindChild(child);
239 if ( pos == wxNOT_FOUND )
240 return;
241
242 client->GetBookCtrl()->SetSelection(pos);
243}
244
245void wxGenericMDIParentFrame::WXRemoveChild(wxGenericMDIChildFrame *child)
246{
247 const bool removingActive = WXIsActiveChild(child);
248 if ( removingActive )
249 {
250 SetActiveChild(NULL);
251 WXSetChildMenuBar(NULL);
252 }
253
254 wxGenericMDIClientWindow * const client = GetGenericClientWindow();
255 wxCHECK_RET( client, "should have client window" );
256
257 wxBookCtrlBase * const book = client->GetBookCtrl();
258
259 // Remove page if still there
260 int pos = client->FindChild(child);
261 if ( pos != wxNOT_FOUND )
262 {
263 if ( book->RemovePage(pos) )
264 book->Refresh();
265 }
266
267 if ( removingActive )
268 {
269 // Set the new selection to a remaining page
270 const size_t count = book->GetPageCount();
271 if ( count > (size_t)pos )
272 {
273 book->SetSelection(pos);
274 }
275 else
276 {
277 if ( count > 0 )
278 book->SetSelection(count - 1);
279 }
280 }
281}
282
283bool
284wxGenericMDIParentFrame::WXIsActiveChild(wxGenericMDIChildFrame *child) const
285{
286 return static_cast<wxMDIChildFrameBase *>(GetActiveChild()) == child;
287}
288
289#if wxUSE_MENUS
290void wxGenericMDIParentFrame::RemoveWindowMenu(wxMenuBar *pMenuBar)
291{
292 if (pMenuBar && m_windowMenu)
293 {
294 // Remove old window menu
295 int pos = pMenuBar->FindMenu(_("&Window"));
296 if (pos != wxNOT_FOUND)
297 {
298 wxASSERT(m_windowMenu == pMenuBar->GetMenu(pos)); // DBG:: We're going to delete the wrong menu!!!
299 pMenuBar->Remove(pos);
300 }
301 }
302}
303
304void wxGenericMDIParentFrame::AddWindowMenu(wxMenuBar *pMenuBar)
305{
306 if (pMenuBar && m_windowMenu)
307 {
308 int pos = pMenuBar->FindMenu(wxGetStockLabel(wxID_HELP,false));
309 if (pos == wxNOT_FOUND)
310 {
311 pMenuBar->Append(m_windowMenu, _("&Window"));
312 }
313 else
314 {
315 pMenuBar->Insert(pos, m_windowMenu, _("&Window"));
316 }
317 }
318}
319
320void wxGenericMDIParentFrame::OnWindowMenu(wxCommandEvent &event)
321{
322 switch ( event.GetId() )
323 {
324 case wxWINDOWCLOSE:
325 if ( m_currentChild )
326 m_currentChild->Close();
327 break;
328
329 case wxWINDOWCLOSEALL:
330 CloseAll();
331 break;
332
333 case wxWINDOWNEXT:
334 ActivateNext();
335 break;
336
337 case wxWINDOWPREV:
338 ActivatePrevious();
339 break;
340
341 default:
342 event.Skip();
343 }
344}
345#endif // wxUSE_MENUS
346
347void wxGenericMDIParentFrame::OnClose(wxCloseEvent& event)
348{
349 if ( !CloseAll() )
350 event.Veto();
351 else
352 event.Skip();
353}
354
355bool wxGenericMDIParentFrame::ProcessEvent(wxEvent& event)
356{
357 if ( m_currentChild )
358 {
359 // the menu events should be given to the child as we show its menu bar
360 // as our own
361 const wxEventType eventType = event.GetEventType();
362 if ( eventType == wxEVT_COMMAND_MENU_SELECTED ||
363 eventType == wxEVT_UPDATE_UI )
364 {
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);
370
371 if ( m_currentChild->ProcessWindowEvent(event) )
372 return true;
373 }
374 }
375
376 return wxMDIParentFrameBase::ProcessEvent(event);
377}
378
379// ----------------------------------------------------------------------------
380// wxGenericMDIChildFrame
381// ----------------------------------------------------------------------------
382
383IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIChildFrame, wxFrame)
384
385BEGIN_EVENT_TABLE(wxGenericMDIChildFrame, wxFrame)
386 EVT_MENU_HIGHLIGHT_ALL(wxGenericMDIChildFrame::OnMenuHighlight)
387
388 EVT_CLOSE(wxGenericMDIChildFrame::OnClose)
389END_EVENT_TABLE()
390
391void wxGenericMDIChildFrame::Init()
392{
393#if wxUSE_MENUS
394 m_pMenuBar = NULL;
395#endif // wxUSE_MENUS
396
397#if !wxUSE_GENERIC_MDI_AS_NATIVE
398 m_mdiParentGeneric = NULL;
399#endif
400}
401
402wxGenericMDIChildFrame::~wxGenericMDIChildFrame()
403{
404 wxGenericMDIParentFrame * const parent = GetGenericMDIParent();
405
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
408 if ( parent )
409 parent->WXRemoveChild(this);
410
411#if wxUSE_MENUS
412 delete m_pMenuBar;
413#endif // wxUSE_MENUS
414}
415
416bool wxGenericMDIChildFrame::Create(wxGenericMDIParentFrame *parent,
417 wxWindowID id,
418 const wxString& title,
419 const wxPoint& WXUNUSED(pos),
420 const wxSize& size,
421 long WXUNUSED(style),
422 const wxString& name)
423{
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;
431#endif
432
433 wxBookCtrlBase * const book = parent->GetBookCtrl();
434
435 wxASSERT_MSG( book, "Missing MDI client window." );
436
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) )
440 return false;
441
442 m_title = title;
443 book->AddPage(this, title, true);
444
445 return true;
446}
447
448#if wxUSE_MENUS
449void wxGenericMDIChildFrame::SetMenuBar( wxMenuBar *menu_bar )
450{
451 wxMenuBar *pOldMenuBar = m_pMenuBar;
452 m_pMenuBar = menu_bar;
453
454 if (m_pMenuBar)
455 {
456 wxGenericMDIParentFrame *parent = GetGenericMDIParent();
457
458 if ( parent )
459 {
460 m_pMenuBar->SetParent(parent);
461
462 if ( parent->WXIsActiveChild(this) )
463 {
464 // Replace current menu bars
465 if (pOldMenuBar)
466 parent->WXSetChildMenuBar(NULL);
467 parent->WXSetChildMenuBar(this);
468 }
469 }
470 }
471}
472
473wxMenuBar *wxGenericMDIChildFrame::GetMenuBar() const
474{
475 return m_pMenuBar;
476}
477#endif // wxUSE_MENUS
478
479void wxGenericMDIChildFrame::SetTitle(const wxString& title)
480{
481 m_title = title;
482
483 wxGenericMDIParentFrame * const parent = GetGenericMDIParent();
484 if ( parent )
485 parent->WXUpdateChildTitle(this);
486 //else: it's ok, we might be not created yet
487}
488
489void wxGenericMDIChildFrame::Activate()
490{
491 wxGenericMDIParentFrame * const parent = GetGenericMDIParent();
492
493 wxCHECK_RET( parent, "can't activate MDI child without parent" );
494 parent->WXActivateChild(this);
495}
496
497void wxGenericMDIChildFrame::OnMenuHighlight(wxMenuEvent& event)
498{
499 wxGenericMDIParentFrame * const parent = GetGenericMDIParent();
500 if ( parent)
501 {
502 // we don't have any help text for this item,
503 // but may be the MDI frame does?
504 parent->OnMenuHighlight(event);
505 }
506}
507
508void wxGenericMDIChildFrame::OnClose(wxCloseEvent& WXUNUSED(event))
509{
510 // we're not a TLW so don't delay the destruction of this window
511 delete this;
512}
513
514bool wxGenericMDIChildFrame::TryAfter(wxEvent& event)
515{
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) )
520 return false;
521
522 return wxTDIChildFrame::TryAfter(event);
523}
524
525// ----------------------------------------------------------------------------
526// wxGenericMDIClientWindow
527// ----------------------------------------------------------------------------
528
529IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIClientWindow, wxWindow)
530
531bool
532wxGenericMDIClientWindow::CreateGenericClient(wxWindow *parent)
533{
534 if ( !wxWindow::Create(parent, wxID_ANY) )
535 return false;
536
537 m_notebook = new wxNotebook(this, wxID_ANY);
538 m_notebook->Connect
539 (
540 wxEVT_COMMAND_NOTEBOOK_PAGE_CHANGED,
541 wxNotebookEventHandler(
542 wxGenericMDIClientWindow::OnPageChanged),
543 NULL,
544 this
545 );
546
547 // now that we have a notebook to resize, hook up OnSize() too
548 Connect(wxEVT_SIZE, wxSizeEventHandler(wxGenericMDIClientWindow::OnSize));
549
550 return true;
551}
552
553wxBookCtrlBase *wxGenericMDIClientWindow::GetBookCtrl() const
554{
555 return m_notebook;
556}
557
558wxGenericMDIChildFrame *wxGenericMDIClientWindow::GetChild(size_t pos) const
559{
560 return static_cast<wxGenericMDIChildFrame *>(GetBookCtrl()->GetPage(pos));
561}
562
563int wxGenericMDIClientWindow::FindChild(wxGenericMDIChildFrame *child) const
564{
565 wxBookCtrlBase * const book = GetBookCtrl();
566 const size_t count = book->GetPageCount();
567 for ( size_t pos = 0; pos < count; pos++ )
568 {
569 if ( book->GetPage(pos) == child )
570 return pos;
571 }
572
573 return wxNOT_FOUND;
574}
575
576void wxGenericMDIClientWindow::PageChanged(int oldSelection, int newSelection)
577{
578 // Don't do anything if nothing changed
579 if (oldSelection == newSelection)
580 return;
581
582 // Again check if we really need to do this...
583 if (newSelection != -1)
584 {
585 wxGenericMDIChildFrame * const child = GetChild(newSelection);
586
587 if ( child->GetGenericMDIParent()->WXIsActiveChild(child) )
588 return;
589 }
590
591 // Notify old active child that it has been deactivated
592 if (oldSelection != -1)
593 {
594 wxGenericMDIChildFrame * const oldChild = GetChild(oldSelection);
595 if (oldChild)
596 {
597 wxActivateEvent event(wxEVT_ACTIVATE, false, oldChild->GetId());
598 event.SetEventObject( oldChild );
599 oldChild->GetEventHandler()->ProcessEvent(event);
600 }
601 }
602
603 // Notify new active child that it has been activated
604 if (newSelection != -1)
605 {
606 wxGenericMDIChildFrame * const activeChild = GetChild(newSelection);
607 if ( activeChild )
608 {
609 wxActivateEvent event(wxEVT_ACTIVATE, true, activeChild->GetId());
610 event.SetEventObject( activeChild );
611 activeChild->GetEventHandler()->ProcessEvent(event);
612
613 wxGenericMDIParentFrame * const
614 parent = activeChild->GetGenericMDIParent();
615
616 if ( parent )
617 {
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);
625 }
626 }
627 }
628}
629
630void wxGenericMDIClientWindow::OnPageChanged(wxBookCtrlEvent& event)
631{
632 PageChanged(event.GetOldSelection(), event.GetSelection());
633
634 event.Skip();
635}
636
637void wxGenericMDIClientWindow::OnSize(wxSizeEvent& WXUNUSED(event))
638{
639 m_notebook->SetSize(GetClientSize());
640}
641
642#endif // wxUSE_MDI
643