Fix bug with not selecting wxAuiNotebook page when its child was focused.
[wxWidgets.git] / src / common / framecmn.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/framecmn.cpp
3 // Purpose: common (for all platforms) wxFrame functions
4 // Author: Julian Smart, Vadim Zeitlin
5 // Created: 01/02/97
6 // Copyright: (c) 1998 Robert Roebling and Julian Smart
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // ============================================================================
11 // declarations
12 // ============================================================================
13
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #include "wx/frame.h"
26
27 #ifndef WX_PRECOMP
28 #include "wx/app.h"
29 #include "wx/menu.h"
30 #include "wx/menuitem.h"
31 #include "wx/dcclient.h"
32 #include "wx/toolbar.h"
33 #include "wx/statusbr.h"
34 #endif // WX_PRECOMP
35
36 extern WXDLLEXPORT_DATA(const char) wxFrameNameStr[] = "frame";
37 extern WXDLLEXPORT_DATA(const char) wxStatusLineNameStr[] = "status_line";
38
39 // ----------------------------------------------------------------------------
40 // event table
41 // ----------------------------------------------------------------------------
42
43 #if wxUSE_MENUS
44
45 #if wxUSE_STATUSBAR
46 BEGIN_EVENT_TABLE(wxFrameBase, wxTopLevelWindow)
47 EVT_MENU_OPEN(wxFrameBase::OnMenuOpen)
48 EVT_MENU_CLOSE(wxFrameBase::OnMenuClose)
49
50 EVT_MENU_HIGHLIGHT_ALL(wxFrameBase::OnMenuHighlight)
51 END_EVENT_TABLE()
52 #endif // wxUSE_STATUSBAR
53
54 /* static */
55 bool wxFrameBase::ShouldUpdateMenuFromIdle()
56 {
57 // Usually this is determined at compile time and is determined by whether
58 // the platform supports wxEVT_MENU_OPEN, however in wxGTK we need to also
59 // check if we're using the global menu bar as we don't get EVT_MENU_OPEN
60 // for it and need to fall back to idle time updating even if normally
61 // wxUSE_IDLEMENUUPDATES is set to 0 for wxGTK.
62 #ifdef __WXGTK20__
63 if ( wxApp::GTKIsUsingGlobalMenu() )
64 return true;
65 #endif // !__WXGTK__
66
67 return wxUSE_IDLEMENUUPDATES != 0;
68 }
69
70 #endif // wxUSE_MENUS
71
72 // ============================================================================
73 // implementation
74 // ============================================================================
75
76 // ----------------------------------------------------------------------------
77 // XTI
78 // ----------------------------------------------------------------------------
79
80 wxDEFINE_FLAGS( wxFrameStyle )
81 wxBEGIN_FLAGS( wxFrameStyle )
82 // new style border flags, we put them first to
83 // use them for streaming out
84 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
85 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
86 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
87 wxFLAGS_MEMBER(wxBORDER_RAISED)
88 wxFLAGS_MEMBER(wxBORDER_STATIC)
89 wxFLAGS_MEMBER(wxBORDER_NONE)
90
91 // old style border flags
92 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
93 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
94 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
95 wxFLAGS_MEMBER(wxRAISED_BORDER)
96 wxFLAGS_MEMBER(wxSTATIC_BORDER)
97 wxFLAGS_MEMBER(wxBORDER)
98
99 // standard window styles
100 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
101 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
102 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
103 wxFLAGS_MEMBER(wxWANTS_CHARS)
104 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
105 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
106 wxFLAGS_MEMBER(wxVSCROLL)
107 wxFLAGS_MEMBER(wxHSCROLL)
108
109 // frame styles
110 wxFLAGS_MEMBER(wxSTAY_ON_TOP)
111 wxFLAGS_MEMBER(wxCAPTION)
112 #if WXWIN_COMPATIBILITY_2_6
113 wxFLAGS_MEMBER(wxTHICK_FRAME)
114 #endif // WXWIN_COMPATIBILITY_2_6
115 wxFLAGS_MEMBER(wxSYSTEM_MENU)
116 wxFLAGS_MEMBER(wxRESIZE_BORDER)
117 #if WXWIN_COMPATIBILITY_2_6
118 wxFLAGS_MEMBER(wxRESIZE_BOX)
119 #endif // WXWIN_COMPATIBILITY_2_6
120 wxFLAGS_MEMBER(wxCLOSE_BOX)
121 wxFLAGS_MEMBER(wxMAXIMIZE_BOX)
122 wxFLAGS_MEMBER(wxMINIMIZE_BOX)
123
124 wxFLAGS_MEMBER(wxFRAME_TOOL_WINDOW)
125 wxFLAGS_MEMBER(wxFRAME_FLOAT_ON_PARENT)
126
127 wxFLAGS_MEMBER(wxFRAME_SHAPED)
128 wxEND_FLAGS( wxFrameStyle )
129
130 wxIMPLEMENT_DYNAMIC_CLASS_XTI(wxFrame, wxTopLevelWindow, "wx/frame.h")
131
132 wxBEGIN_PROPERTIES_TABLE(wxFrame)
133 wxEVENT_PROPERTY( Menu, wxEVT_MENU, wxCommandEvent)
134
135 wxPROPERTY( Title,wxString, SetTitle, GetTitle, wxString(), 0 /*flags*/, \
136 wxT("Helpstring"), wxT("group"))
137 wxPROPERTY_FLAGS( WindowStyle, wxFrameStyle, long, SetWindowStyleFlag, \
138 GetWindowStyleFlag, wxEMPTY_PARAMETER_VALUE, 0 /*flags*/, \
139 wxT("Helpstring"), wxT("group")) // style
140 #if wxUSE_MENUS
141 wxPROPERTY( MenuBar, wxMenuBar *, SetMenuBar, GetMenuBar, wxEMPTY_PARAMETER_VALUE, \
142 0 /*flags*/, wxT("Helpstring"), wxT("group"))
143 #endif
144 wxEND_PROPERTIES_TABLE()
145
146 wxEMPTY_HANDLERS_TABLE(wxFrame)
147
148 wxCONSTRUCTOR_6( wxFrame, wxWindow*, Parent, wxWindowID, Id, wxString, Title, \
149 wxPoint, Position, wxSize, Size, long, WindowStyle)
150
151 // ----------------------------------------------------------------------------
152 // construction/destruction
153 // ----------------------------------------------------------------------------
154
155 wxFrameBase::wxFrameBase()
156 {
157 #if wxUSE_MENUS
158 m_frameMenuBar = NULL;
159 #endif // wxUSE_MENUS
160
161 #if wxUSE_TOOLBAR
162 m_frameToolBar = NULL;
163 #endif // wxUSE_TOOLBAR
164
165 #if wxUSE_STATUSBAR
166 m_frameStatusBar = NULL;
167 #endif // wxUSE_STATUSBAR
168
169 m_statusBarPane = 0;
170 }
171
172 wxFrameBase::~wxFrameBase()
173 {
174 // this destructor is required for Darwin
175 }
176
177 wxFrame *wxFrameBase::New(wxWindow *parent,
178 wxWindowID id,
179 const wxString& title,
180 const wxPoint& pos,
181 const wxSize& size,
182 long style,
183 const wxString& name)
184 {
185 return new wxFrame(parent, id, title, pos, size, style, name);
186 }
187
188 void wxFrameBase::DeleteAllBars()
189 {
190 #if wxUSE_MENUS
191 wxDELETE(m_frameMenuBar);
192 #endif // wxUSE_MENUS
193
194 #if wxUSE_STATUSBAR
195 wxDELETE(m_frameStatusBar);
196 #endif // wxUSE_STATUSBAR
197
198 #if wxUSE_TOOLBAR
199 wxDELETE(m_frameToolBar);
200 #endif // wxUSE_TOOLBAR
201 }
202
203 bool wxFrameBase::IsOneOfBars(const wxWindow *win) const
204 {
205 #if wxUSE_MENUS
206 if ( win == GetMenuBar() )
207 return true;
208 #endif // wxUSE_MENUS
209
210 #if wxUSE_STATUSBAR
211 if ( win == GetStatusBar() )
212 return true;
213 #endif // wxUSE_STATUSBAR
214
215 #if wxUSE_TOOLBAR
216 if ( win == GetToolBar() )
217 return true;
218 #endif // wxUSE_TOOLBAR
219
220 wxUnusedVar(win);
221
222 return false;
223 }
224
225 // ----------------------------------------------------------------------------
226 // wxFrame size management: we exclude the areas taken by menu/status/toolbars
227 // from the client area, so the client area is what's really available for the
228 // frame contents
229 // ----------------------------------------------------------------------------
230
231 // get the origin of the client area in the client coordinates
232 wxPoint wxFrameBase::GetClientAreaOrigin() const
233 {
234 wxPoint pt = wxTopLevelWindow::GetClientAreaOrigin();
235
236 #if wxUSE_TOOLBAR && !defined(__WXUNIVERSAL__)
237 wxToolBar *toolbar = GetToolBar();
238 if ( toolbar && toolbar->IsShown() )
239 {
240 int w, h;
241 toolbar->GetSize(&w, &h);
242
243 if ( toolbar->GetWindowStyleFlag() & wxTB_VERTICAL )
244 {
245 pt.x += w;
246 }
247 else
248 {
249 pt.y += h;
250 }
251 }
252 #endif // wxUSE_TOOLBAR
253
254 return pt;
255 }
256
257 // ----------------------------------------------------------------------------
258 // misc
259 // ----------------------------------------------------------------------------
260
261 #if wxUSE_MENUS
262
263 bool wxFrameBase::ProcessCommand(int id)
264 {
265 wxMenuItem* const item = FindItemInMenuBar(id);
266 if ( !item )
267 return false;
268
269 return ProcessCommand(item);
270 }
271
272 bool wxFrameBase::ProcessCommand(wxMenuItem *item)
273 {
274 wxCHECK_MSG( item, false, wxS("Menu item can't be NULL") );
275
276 if (!item->IsEnabled())
277 return true;
278
279 if ((item->GetKind() == wxITEM_RADIO) && item->IsChecked() )
280 return true;
281
282 int checked;
283 if (item->IsCheckable())
284 {
285 item->Toggle();
286
287 // use the new value
288 checked = item->IsChecked();
289 }
290 else // Uncheckable item.
291 {
292 checked = -1;
293 }
294
295 wxMenu* const menu = item->GetMenu();
296 wxCHECK_MSG( menu, false, wxS("Menu item should be attached to a menu") );
297
298 return menu->SendEvent(item->GetId(), checked);
299 }
300
301 #endif // wxUSE_MENUS
302
303 // Do the UI update processing for this window. This is
304 // provided for the application to call if it wants to
305 // force a UI update, particularly for the menus and toolbar.
306 void wxFrameBase::UpdateWindowUI(long flags)
307 {
308 wxWindowBase::UpdateWindowUI(flags);
309
310 #if wxUSE_TOOLBAR
311 if (GetToolBar())
312 GetToolBar()->UpdateWindowUI(flags);
313 #endif
314
315 #if wxUSE_MENUS
316 if (GetMenuBar())
317 {
318 // If coming from an idle event, we only want to update the menus if
319 // we're in the wxUSE_IDLEMENUUPDATES configuration, otherwise they
320 // will be update when the menu is opened later
321 if ( !(flags & wxUPDATE_UI_FROMIDLE) || ShouldUpdateMenuFromIdle() )
322 DoMenuUpdates();
323 }
324 #endif // wxUSE_MENUS
325 }
326
327 // ----------------------------------------------------------------------------
328 // event handlers for status bar updates from menus
329 // ----------------------------------------------------------------------------
330
331 #if wxUSE_MENUS && wxUSE_STATUSBAR
332
333 void wxFrameBase::OnMenuHighlight(wxMenuEvent& event)
334 {
335 #if wxUSE_STATUSBAR
336 (void)ShowMenuHelp(event.GetMenuId());
337 #endif // wxUSE_STATUSBAR
338 }
339
340 void wxFrameBase::OnMenuOpen(wxMenuEvent& event)
341 {
342 if ( !ShouldUpdateMenuFromIdle() )
343 {
344 // as we didn't update the menus from idle time, do it now
345 DoMenuUpdates(event.GetMenu());
346 }
347 }
348
349 void wxFrameBase::OnMenuClose(wxMenuEvent& WXUNUSED(event))
350 {
351 DoGiveHelp(wxEmptyString, false);
352 }
353
354 #endif // wxUSE_MENUS && wxUSE_STATUSBAR
355
356 // Implement internal behaviour (menu updating on some platforms)
357 void wxFrameBase::OnInternalIdle()
358 {
359 wxTopLevelWindow::OnInternalIdle();
360
361 #if wxUSE_MENUS
362 if ( ShouldUpdateMenuFromIdle() && wxUpdateUIEvent::CanUpdate(this) )
363 DoMenuUpdates();
364 #endif
365 }
366
367 // ----------------------------------------------------------------------------
368 // status bar stuff
369 // ----------------------------------------------------------------------------
370
371 #if wxUSE_STATUSBAR
372
373 wxStatusBar* wxFrameBase::CreateStatusBar(int number,
374 long style,
375 wxWindowID id,
376 const wxString& name)
377 {
378 // the main status bar can only be created once (or else it should be
379 // deleted before calling CreateStatusBar() again)
380 wxCHECK_MSG( !m_frameStatusBar, NULL,
381 wxT("recreating status bar in wxFrame") );
382
383 SetStatusBar(OnCreateStatusBar(number, style, id, name));
384
385 return m_frameStatusBar;
386 }
387
388 wxStatusBar *wxFrameBase::OnCreateStatusBar(int number,
389 long style,
390 wxWindowID id,
391 const wxString& name)
392 {
393 wxStatusBar *statusBar = new wxStatusBar(this, id, style, name);
394
395 statusBar->SetFieldsCount(number);
396
397 return statusBar;
398 }
399
400 void wxFrameBase::SetStatusText(const wxString& text, int number)
401 {
402 wxCHECK_RET( m_frameStatusBar != NULL, wxT("no statusbar to set text for") );
403
404 m_frameStatusBar->SetStatusText(text, number);
405 }
406
407 void wxFrameBase::SetStatusWidths(int n, const int widths_field[] )
408 {
409 wxCHECK_RET( m_frameStatusBar != NULL, wxT("no statusbar to set widths for") );
410
411 m_frameStatusBar->SetStatusWidths(n, widths_field);
412
413 PositionStatusBar();
414 }
415
416 void wxFrameBase::PushStatusText(const wxString& text, int number)
417 {
418 wxCHECK_RET( m_frameStatusBar != NULL, wxT("no statusbar to set text for") );
419
420 m_frameStatusBar->PushStatusText(text, number);
421 }
422
423 void wxFrameBase::PopStatusText(int number)
424 {
425 wxCHECK_RET( m_frameStatusBar != NULL, wxT("no statusbar to set text for") );
426
427 m_frameStatusBar->PopStatusText(number);
428 }
429
430 bool wxFrameBase::ShowMenuHelp(int menuId)
431 {
432 #if wxUSE_MENUS
433 // if no help string found, we will clear the status bar text
434 //
435 // NB: wxID_NONE is used for (sub)menus themselves by wxMSW
436 wxString helpString;
437 if ( menuId != wxID_SEPARATOR && menuId != wxID_NONE )
438 {
439 const wxMenuItem * const item = FindItemInMenuBar(menuId);
440 if ( item && !item->IsSeparator() )
441 helpString = item->GetHelp();
442
443 // notice that it's ok if we don't find the item because it might
444 // belong to the popup menu, so don't assert here
445 }
446
447 DoGiveHelp(helpString, true);
448
449 return !helpString.empty();
450 #else // !wxUSE_MENUS
451 return false;
452 #endif // wxUSE_MENUS/!wxUSE_MENUS
453 }
454
455 void wxFrameBase::SetStatusBar(wxStatusBar *statBar)
456 {
457 bool hadBar = m_frameStatusBar != NULL;
458 m_frameStatusBar = statBar;
459
460 if ( (m_frameStatusBar != NULL) != hadBar )
461 {
462 PositionStatusBar();
463
464 DoLayout();
465 }
466 }
467
468 #endif // wxUSE_STATUSBAR
469
470 #if wxUSE_MENUS || wxUSE_TOOLBAR
471 void wxFrameBase::DoGiveHelp(const wxString& help, bool show)
472 {
473 #if wxUSE_STATUSBAR
474 if ( m_statusBarPane < 0 )
475 {
476 // status bar messages disabled
477 return;
478 }
479
480 wxStatusBar *statbar = GetStatusBar();
481 if ( !statbar )
482 return;
483
484 wxString text;
485 if ( show )
486 {
487 // remember the old status bar text if this is the first time we're
488 // called since the menu has been opened as we're going to overwrite it
489 // in our DoGiveHelp() and we want to restore it when the menu is
490 // closed
491 //
492 // note that it would be logical to do this in OnMenuOpen() but under
493 // MSW we get an EVT_MENU_HIGHLIGHT before EVT_MENU_OPEN, strangely
494 // enough, and so this doesn't work and instead we use the ugly trick
495 // with using special m_oldStatusText value as "menu opened" (but it is
496 // arguably better than adding yet another member variable to wxFrame
497 // on all platforms)
498 if ( m_oldStatusText.empty() )
499 {
500 m_oldStatusText = statbar->GetStatusText(m_statusBarPane);
501 if ( m_oldStatusText.empty() )
502 {
503 // use special value to prevent us from doing this the next time
504 m_oldStatusText += wxT('\0');
505 }
506 }
507
508 m_lastHelpShown =
509 text = help;
510 }
511 else // hide help, restore the original text
512 {
513 // clear the last shown help string but remember its value
514 wxString lastHelpShown;
515 lastHelpShown.swap(m_lastHelpShown);
516
517 // also clear the old status text but remember it too to restore it
518 // below
519 text.swap(m_oldStatusText);
520
521 if ( statbar->GetStatusText(m_statusBarPane) != lastHelpShown )
522 {
523 // if the text was changed with an explicit SetStatusText() call
524 // from the user code in the meanwhile, do not overwrite it with
525 // the old status bar contents -- this is almost certainly not what
526 // the user expects and would be very hard to avoid from user code
527 return;
528 }
529 }
530
531 statbar->SetStatusText(text, m_statusBarPane);
532 #else
533 wxUnusedVar(help);
534 wxUnusedVar(show);
535 #endif // wxUSE_STATUSBAR
536 }
537 #endif // wxUSE_MENUS || wxUSE_TOOLBAR
538
539
540 // ----------------------------------------------------------------------------
541 // toolbar stuff
542 // ----------------------------------------------------------------------------
543
544 #if wxUSE_TOOLBAR
545
546 wxToolBar* wxFrameBase::CreateToolBar(long style,
547 wxWindowID id,
548 const wxString& name)
549 {
550 // the main toolbar can't be recreated (unless it was explicitly deleted
551 // before)
552 wxCHECK_MSG( !m_frameToolBar, NULL,
553 wxT("recreating toolbar in wxFrame") );
554
555 if ( style == -1 )
556 {
557 // use default style
558 //
559 // NB: we don't specify the default value in the method declaration
560 // because
561 // a) this allows us to have different defaults for different
562 // platforms (even if we don't have them right now)
563 // b) we don't need to include wx/toolbar.h in the header then
564 style = wxTB_DEFAULT_STYLE;
565 }
566
567 SetToolBar(OnCreateToolBar(style, id, name));
568
569 return m_frameToolBar;
570 }
571
572 wxToolBar* wxFrameBase::OnCreateToolBar(long style,
573 wxWindowID id,
574 const wxString& name)
575 {
576 #if defined(__WXWINCE__) && defined(__POCKETPC__)
577 return new wxToolMenuBar(this, id,
578 wxDefaultPosition, wxDefaultSize,
579 style, name);
580 #else
581 return new wxToolBar(this, id,
582 wxDefaultPosition, wxDefaultSize,
583 style, name);
584 #endif
585 }
586
587 void wxFrameBase::SetToolBar(wxToolBar *toolbar)
588 {
589 if ( (toolbar != NULL) != (m_frameToolBar != NULL) )
590 {
591 // the toolbar visibility must have changed so we need to both position
592 // the toolbar itself (if it appeared) and to relayout the frame
593 // contents in any case
594
595 if ( toolbar )
596 {
597 // we need to assign it to m_frameToolBar for PositionToolBar() to
598 // do anything
599 m_frameToolBar = toolbar;
600 PositionToolBar();
601 }
602 //else: tricky: do not reset m_frameToolBar yet as otherwise DoLayout()
603 // wouldn't recognize the (still existing) toolbar as one of our
604 // bars and wouldn't layout the single child of the frame correctly
605
606
607 // and this is even more tricky: we want DoLayout() to recognize the
608 // old toolbar for the purpose of not counting it among our non-bar
609 // children but we don't want to reserve any more space for it so we
610 // temporarily hide it
611 if ( m_frameToolBar )
612 m_frameToolBar->Hide();
613
614 DoLayout();
615
616 if ( m_frameToolBar )
617 m_frameToolBar->Show();
618 }
619
620 // this might have been already done above but it's simpler to just always
621 // do it unconditionally instead of testing for whether we already did it
622 m_frameToolBar = toolbar;
623 }
624
625 #endif // wxUSE_TOOLBAR
626
627 // ----------------------------------------------------------------------------
628 // menus
629 // ----------------------------------------------------------------------------
630
631 #if wxUSE_MENUS
632
633 // update all menus
634 void wxFrameBase::DoMenuUpdates(wxMenu* menu)
635 {
636 if (menu)
637 {
638 wxEvtHandler* source = GetEventHandler();
639 menu->UpdateUI(source);
640 }
641 else
642 {
643 wxMenuBar* bar = GetMenuBar();
644 if (bar != NULL)
645 bar->UpdateMenus();
646 }
647 }
648
649 void wxFrameBase::DetachMenuBar()
650 {
651 if ( m_frameMenuBar )
652 {
653 m_frameMenuBar->Detach();
654 m_frameMenuBar = NULL;
655 }
656 }
657
658 void wxFrameBase::AttachMenuBar(wxMenuBar *menubar)
659 {
660 if ( menubar )
661 {
662 menubar->Attach((wxFrame *)this);
663 m_frameMenuBar = menubar;
664 }
665 }
666
667 void wxFrameBase::SetMenuBar(wxMenuBar *menubar)
668 {
669 if ( menubar == GetMenuBar() )
670 {
671 // nothing to do
672 return;
673 }
674
675 DetachMenuBar();
676
677 this->AttachMenuBar(menubar);
678 }
679
680 wxMenuItem *wxFrameBase::FindItemInMenuBar(int menuId) const
681 {
682 const wxMenuBar * const menuBar = GetMenuBar();
683
684 return menuBar ? menuBar->FindItem(menuId) : NULL;
685 }
686
687 #endif // wxUSE_MENUS