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