]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/generic/mdig.cpp
Make wxOwnerDrawnComboBox::DoGetBestSize() twice as fast.
[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// 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
42enum MDI_MENU_ID
43{
44 wxWINDOWCLOSE = 4001,
45 wxWINDOWCLOSEALL,
46 wxWINDOWNEXT,
47 wxWINDOWPREV
48};
49
50//-----------------------------------------------------------------------------
51// wxGenericMDIParentFrame
52//-----------------------------------------------------------------------------
53
54IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIParentFrame, wxFrame)
55
56BEGIN_EVENT_TABLE(wxGenericMDIParentFrame, wxFrame)
57 EVT_CLOSE(wxGenericMDIParentFrame::OnClose)
58#if wxUSE_MENUS
59 EVT_MENU(wxID_ANY, wxGenericMDIParentFrame::OnWindowMenu)
60#endif
61END_EVENT_TABLE()
62
63void wxGenericMDIParentFrame::Init()
64{
65#if wxUSE_MENUS
66 m_pMyMenuBar = NULL;
67#endif // wxUSE_MENUS
68}
69
70wxGenericMDIParentFrame::~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
82bool 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
121wxGenericMDIClientWindow *wxGenericMDIParentFrame::OnCreateGenericClient()
122{
123 return new wxGenericMDIClientWindow;
124}
125
126bool 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
147void 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
167void 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
178void 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
203wxGenericMDIClientWindow *
204wxGenericMDIParentFrame::GetGenericClientWindow() const
205{
206 return static_cast<wxGenericMDIClientWindow *>(m_clientWindow);
207}
208
209wxBookCtrlBase *wxGenericMDIParentFrame::GetBookCtrl() const
210{
211 wxGenericMDIClientWindow * const client = GetGenericClientWindow();
212 return client ? client->GetBookCtrl() : NULL;
213}
214
215void wxGenericMDIParentFrame::AdvanceActive(bool forward)
216{
217 wxBookCtrlBase * const book = GetBookCtrl();
218 if ( book )
219 book->AdvanceSelection(forward);
220}
221
222void 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
233void 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
244void 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
282bool
283wxGenericMDIParentFrame::WXIsActiveChild(wxGenericMDIChildFrame *child) const
284{
285 return static_cast<wxMDIChildFrameBase *>(GetActiveChild()) == child;
286}
287
288#if wxUSE_MENUS
289void 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
303void 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
319void 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
346void wxGenericMDIParentFrame::OnClose(wxCloseEvent& event)
347{
348 if ( !CloseAll() )
349 event.Veto();
350 else
351 event.Skip();
352}
353
354bool 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
382IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIChildFrame, wxFrame)
383
384BEGIN_EVENT_TABLE(wxGenericMDIChildFrame, wxFrame)
385 EVT_MENU_HIGHLIGHT_ALL(wxGenericMDIChildFrame::OnMenuHighlight)
386
387 EVT_CLOSE(wxGenericMDIChildFrame::OnClose)
388END_EVENT_TABLE()
389
390void 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
401wxGenericMDIChildFrame::~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
415bool 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
448void 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
472wxMenuBar *wxGenericMDIChildFrame::GetMenuBar() const
473{
474 return m_pMenuBar;
475}
476#endif // wxUSE_MENUS
477
478void 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
488void 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
496void 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
507void 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
513bool 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
528IMPLEMENT_DYNAMIC_CLASS(wxGenericMDIClientWindow, wxWindow)
529
530bool
531wxGenericMDIClientWindow::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
552wxBookCtrlBase *wxGenericMDIClientWindow::GetBookCtrl() const
553{
554 return m_notebook;
555}
556
557wxGenericMDIChildFrame *wxGenericMDIClientWindow::GetChild(size_t pos) const
558{
559 return static_cast<wxGenericMDIChildFrame *>(GetBookCtrl()->GetPage(pos));
560}
561
562int 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
575void 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
629void wxGenericMDIClientWindow::OnPageChanged(wxBookCtrlEvent& event)
630{
631 PageChanged(event.GetOldSelection(), event.GetSelection());
632
633 event.Skip();
634}
635
636void wxGenericMDIClientWindow::OnSize(wxSizeEvent& WXUNUSED(event))
637{
638 m_notebook->SetSize(GetClientSize());
639}
640
641#endif // wxUSE_MDI
642