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