1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/common/containr.cpp 
   3 // Purpose:     implementation of wxControlContainer 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 2001 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // License:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  28     #include "wx/containr.h" 
  34     #include "wx/window.h" 
  35     #include "wx/scrolbar.h" 
  36     #include "wx/radiobut.h" 
  39 // trace mask for focus messages 
  40 #define TRACE_FOCUS _T("focus") 
  42 // ============================================================================ 
  44 // ============================================================================ 
  46 // ---------------------------------------------------------------------------- 
  47 // wxControlContainerBase 
  48 // ---------------------------------------------------------------------------- 
  50 void wxControlContainerBase::SetCanFocus(bool acceptsFocus
) 
  52     if ( acceptsFocus 
== m_acceptsFocus 
) 
  55     m_acceptsFocus 
= acceptsFocus
; 
  57     m_winParent
->SetCanFocus(m_acceptsFocus
); 
  60 // if the window has a focusable child, it shouldn't be focusable itself (think 
  61 // of wxPanel used for grouping different controls) but if it doesn't have any 
  62 // (focusable) children, then it should be possible to give it focus (think of 
  63 // wxGrid or generic wxListCtrl) 
  64 bool wxControlContainerBase::ShouldAcceptFocus() const 
  66     // we can accept focus either if we have no children at all (in this case 
  67     // we're probably not used as a container) or only when at least one child 
  69     wxWindowList::compatibility_iterator node 
= m_winParent
->GetChildren().GetFirst(); 
  75         wxWindow 
*child 
= node
->GetData(); 
  76         node 
= node
->GetNext(); 
  78         if ( !m_winParent
->IsClientAreaChild(child
) ) 
  81         if ( child
->CanAcceptFocus() ) 
  88 #ifndef wxHAS_NATIVE_TAB_TRAVERSAL 
  90 // ---------------------------------------------------------------------------- 
  91 // generic wxControlContainer 
  92 // ---------------------------------------------------------------------------- 
  94 wxControlContainer::wxControlContainer() 
  96     m_winLastFocused 
= NULL
; 
 100 void wxControlContainer::SetLastFocus(wxWindow 
*win
) 
 102     // the panel itself should never get the focus at all but if it does happen 
 103     // temporarily (as it seems to do under wxGTK), at the very least don't 
 104     // forget our previous m_winLastFocused 
 105     if ( win 
!= m_winParent 
) 
 107         // if we're setting the focus 
 110             // find the last _immediate_ child which got focus 
 111             wxWindow 
*winParent 
= win
; 
 112             while ( winParent 
!= m_winParent 
) 
 115                 winParent 
= win
->GetParent(); 
 117                 // Yes, this can happen, though in a totally pathological case. 
 118                 // like when detaching a menubar from a frame with a child 
 119                 // which has pushed itself as an event handler for the menubar. 
 122                 wxASSERT_MSG( winParent
, 
 123                               _T("Setting last focus for a window that is not our child?") ); 
 127         m_winLastFocused 
= win
; 
 131             wxLogTrace(TRACE_FOCUS
, _T("Set last focus to %s(%s)"), 
 132                        win
->GetClassInfo()->GetClassName(), 
 133                        win
->GetLabel().c_str()); 
 137             wxLogTrace(TRACE_FOCUS
, _T("No more last focus")); 
 141     // propagate the last focus upwards so that our parent can set focus back 
 142     // to us if it loses it now and regains later; do *not* do this if we are 
 143     // a toplevel window (e.g. wxDialog) that has another frame as its parent 
 144     if ( !m_winParent
->IsTopLevel() ) 
 146         wxWindow 
*parent 
= m_winParent
->GetParent(); 
 149             wxChildFocusEvent 
eventFocus(m_winParent
); 
 150             parent
->GetEventHandler()->ProcessEvent(eventFocus
); 
 155 // -------------------------------------------------------------------- 
 156 // The following four functions are used to find other radio buttons 
 157 // within the same group. Used by wxSetFocusToChild on wxMSW 
 158 // -------------------------------------------------------------------- 
 162 wxRadioButton
* wxGetPreviousButtonInGroup(wxRadioButton 
*btn
) 
 164     if ( btn
->HasFlag(wxRB_GROUP
) || btn
->HasFlag(wxRB_SINGLE
) ) 
 167     const wxWindowList
& siblings 
= btn
->GetParent()->GetChildren(); 
 168     wxWindowList::compatibility_iterator nodeThis 
= siblings
.Find(btn
); 
 169     wxCHECK_MSG( nodeThis
, NULL
, _T("radio button not a child of its parent?") ); 
 171     // Iterate over all previous siblings until we find the next radio button 
 172     wxWindowList::compatibility_iterator nodeBefore 
= nodeThis
->GetPrevious(); 
 173     wxRadioButton 
*prevBtn 
= 0; 
 176         prevBtn 
= wxDynamicCast(nodeBefore
->GetData(), wxRadioButton
); 
 180         nodeBefore 
= nodeBefore
->GetPrevious(); 
 183     if (!prevBtn 
|| prevBtn
->HasFlag(wxRB_SINGLE
)) 
 185         // no more buttons in group 
 192 wxRadioButton
* wxGetNextButtonInGroup(wxRadioButton 
*btn
) 
 194     if (btn
->HasFlag(wxRB_SINGLE
)) 
 197     const wxWindowList
& siblings 
= btn
->GetParent()->GetChildren(); 
 198     wxWindowList::compatibility_iterator nodeThis 
= siblings
.Find(btn
); 
 199     wxCHECK_MSG( nodeThis
, NULL
, _T("radio button not a child of its parent?") ); 
 201     // Iterate over all previous siblings until we find the next radio button 
 202     wxWindowList::compatibility_iterator nodeNext 
= nodeThis
->GetNext(); 
 203     wxRadioButton 
*nextBtn 
= 0; 
 206         nextBtn 
= wxDynamicCast(nodeNext
->GetData(), wxRadioButton
); 
 210         nodeNext 
= nodeNext
->GetNext(); 
 213     if ( !nextBtn 
|| nextBtn
->HasFlag(wxRB_GROUP
) || nextBtn
->HasFlag(wxRB_SINGLE
) ) 
 215         // no more buttons or the first button of the next group 
 222 wxRadioButton
* wxGetFirstButtonInGroup(wxRadioButton 
*btn
) 
 226         wxRadioButton
* prevBtn 
= wxGetPreviousButtonInGroup(btn
); 
 234 wxRadioButton
* wxGetLastButtonInGroup(wxRadioButton 
*btn
) 
 238         wxRadioButton
* nextBtn 
= wxGetNextButtonInGroup(btn
); 
 246 wxRadioButton
* wxGetSelectedButtonInGroup(wxRadioButton 
*btn
) 
 248     // Find currently selected button 
 252     if (btn
->HasFlag(wxRB_SINGLE
)) 
 255     wxRadioButton 
*selBtn
; 
 257     // First check all previous buttons 
 258     for (selBtn 
= wxGetPreviousButtonInGroup(btn
); selBtn
; selBtn 
= wxGetPreviousButtonInGroup(selBtn
)) 
 259         if (selBtn
->GetValue()) 
 262     // Now all following buttons 
 263     for (selBtn 
= wxGetNextButtonInGroup(btn
); selBtn
; selBtn 
= wxGetNextButtonInGroup(selBtn
)) 
 264         if (selBtn
->GetValue()) 
 272 // ---------------------------------------------------------------------------- 
 273 // Keyboard handling - this is the place where the TAB traversal logic is 
 274 // implemented. As this code is common to all ports, this ensures consistent 
 275 // behaviour even if we don't specify how exactly the wxNavigationKeyEvent are 
 276 // generated and this is done in platform specific code which also ensures that 
 277 // we can follow the given platform standards. 
 278 // ---------------------------------------------------------------------------- 
 280 void wxControlContainer::HandleOnNavigationKey( wxNavigationKeyEvent
& event 
) 
 282     wxWindow 
*parent 
= m_winParent
->GetParent(); 
 284     // the event is propagated downwards if the event emitter was our parent 
 285     bool goingDown 
= event
.GetEventObject() == parent
; 
 287     const wxWindowList
& children 
= m_winParent
->GetChildren(); 
 289     // if we have exactly one notebook-like child window (actually it could be 
 290     // any window that returns true from its HasMultiplePages()), then 
 291     // [Shift-]Ctrl-Tab and Ctrl-PageUp/Down keys should iterate over its pages 
 292     // even if the focus is outside of the control because this is how the 
 293     // standard MSW properties dialogs behave and we do it under other platforms 
 294     // as well because it seems like a good idea -- but we can always put this 
 295     // block inside "#ifdef __WXMSW__" if it's not suitable there 
 296     if ( event
.IsWindowChange() && !goingDown 
) 
 298         // check if we have a unique notebook-like child 
 299         wxWindow 
*bookctrl 
= NULL
; 
 300         for ( wxWindowList::const_iterator i 
= children
.begin(), 
 301                                          end 
= children
.end(); 
 305             wxWindow 
* const window 
= *i
; 
 306             if ( window
->HasMultiplePages() ) 
 310                     // this is the second book-like control already so don't do 
 311                     // anything as we don't know which one should have its page 
 323             // make sure that we don't bubble up the event again from the book 
 324             // control resulting in infinite recursion 
 325             wxNavigationKeyEvent 
eventCopy(event
); 
 326             eventCopy
.SetEventObject(m_winParent
); 
 327             if ( bookctrl
->GetEventHandler()->ProcessEvent(eventCopy
) ) 
 332     // there is not much to do if we don't have children and we're not 
 333     // interested in "notebook page change" events here 
 334     if ( !children
.GetCount() || event
.IsWindowChange() ) 
 336         // let the parent process it unless it already comes from our parent 
 337         // of we don't have any 
 339              !parent 
|| !parent
->GetEventHandler()->ProcessEvent(event
) ) 
 347     // where are we going? 
 348     const bool forward 
= event
.GetDirection(); 
 350     // the node of the children list from which we should start looking for the 
 351     // next acceptable child 
 352     wxWindowList::compatibility_iterator node
, start_node
; 
 354     // we should start from the first/last control and not from the one which 
 355     // had focus the last time if we're propagating the event downwards because 
 356     // for our parent we look like a single control 
 359         // just to be sure it's not used (normally this is not necessary, but 
 360         // doesn't hurt neither) 
 361         m_winLastFocused 
= (wxWindow 
*)NULL
; 
 363         // start from first or last depending on where we're going 
 364         node 
= forward 
? children
.GetFirst() : children
.GetLast(); 
 368         // try to find the child which has the focus currently 
 370         // the event emitter might have done this for us 
 371         wxWindow 
*winFocus 
= event
.GetCurrentFocus(); 
 373         // but if not, we might know where the focus was ourselves 
 375             winFocus 
= m_winLastFocused
; 
 377         // if still no luck, do it the hard way 
 379             winFocus 
= wxWindow::FindFocus(); 
 384             // If we are in a radio button group, start from the first item in the 
 386             if ( event
.IsFromTab() && wxIsKindOf(winFocus
, wxRadioButton 
) ) 
 387                 winFocus 
= wxGetFirstButtonInGroup((wxRadioButton
*)winFocus
); 
 389             // ok, we found the focus - now is it our child? 
 390             start_node 
= children
.Find( winFocus 
); 
 393         if ( !start_node 
&& m_winLastFocused 
) 
 395             // window which has focus isn't our child, fall back to the one 
 396             // which had the focus the last time 
 397             start_node 
= children
.Find( m_winLastFocused 
); 
 400         // if we still didn't find anything, we should start with the first one 
 403             start_node 
= children
.GetFirst(); 
 406         // and the first child which we can try setting focus to is the next or 
 408         node 
= forward 
? start_node
->GetNext() : start_node
->GetPrevious(); 
 411     // we want to cycle over all elements passing by NULL 
 414         // don't go into infinite loop 
 415         if ( start_node 
&& node 
&& node 
== start_node 
) 
 418         // Have we come to the last or first item on the panel? 
 423                 // exit now as otherwise we'd loop forever 
 429                 // Check if our (maybe grand) parent is another panel: if this 
 430                 // is the case, they will know what to do with this navigation 
 431                 // key and so give them the chance to process it instead of 
 432                 // looping inside this panel (normally, the focus will go to 
 433                 // the next/previous item after this panel in the parent 
 435                 wxWindow 
*focussed_child_of_parent 
= m_winParent
; 
 438                     // we don't want to tab into a different dialog or frame 
 439                     if ( focussed_child_of_parent
->IsTopLevel() ) 
 442                     event
.SetCurrentFocus( focussed_child_of_parent 
); 
 443                     if ( parent
->GetEventHandler()->ProcessEvent( event 
) ) 
 446                     focussed_child_of_parent 
= parent
; 
 448                     parent 
= parent
->GetParent(); 
 451             //else: as the focus came from our parent, we definitely don't want 
 452             //      to send it back to it! 
 454             // no, we are not inside another panel so process this ourself 
 455             node 
= forward 
? children
.GetFirst() : children
.GetLast(); 
 460         wxWindow 
*child 
= node
->GetData(); 
 463         if ( event
.IsFromTab() ) 
 465             if ( wxIsKindOf(child
, wxRadioButton
) ) 
 467                 // only radio buttons with either wxRB_GROUP or wxRB_SINGLE 
 469                 if ( child
->HasFlag(wxRB_GROUP
) ) 
 471                     // need to tab into the active button within a group 
 472                     wxRadioButton 
*rb 
= wxGetSelectedButtonInGroup((wxRadioButton
*)child
); 
 476                 else if ( !child
->HasFlag(wxRB_SINGLE
) ) 
 478                     node 
= forward 
? node
->GetNext() : node
->GetPrevious(); 
 483         else if ( m_winLastFocused 
&& 
 484                   wxIsKindOf(m_winLastFocused
, wxRadioButton
) && 
 485                   !m_winLastFocused
->HasFlag(wxRB_SINGLE
) ) 
 487             // cursor keys don't navigate out of a radio button group so 
 488             // find the correct radio button to focus 
 491                 child 
= wxGetNextButtonInGroup((wxRadioButton
*)m_winLastFocused
); 
 494                     // no next button in group, set it to the first button 
 495                     child 
= wxGetFirstButtonInGroup((wxRadioButton
*)m_winLastFocused
); 
 500                 child 
= wxGetPreviousButtonInGroup((wxRadioButton
*)m_winLastFocused
); 
 503                     // no previous button in group, set it to the last button 
 504                     child 
= wxGetLastButtonInGroup((wxRadioButton
*)m_winLastFocused
); 
 508             if ( child 
== m_winLastFocused 
) 
 510                 // must be a group consisting of only one button therefore 
 511                 // no need to send a navigation event 
 518         if ( child
->CanAcceptFocusFromKeyboard() ) 
 520             // if we're setting the focus to a child panel we should prevent it 
 521             // from giving it to the child which had the focus the last time 
 522             // and instead give it to the first/last child depending from which 
 523             // direction we're coming 
 524             event
.SetEventObject(m_winParent
); 
 526             // disable propagation for this call as otherwise the event might 
 527             // bounce back to us. 
 528             wxPropagationDisabler 
disableProp(event
); 
 529             if ( !child
->GetEventHandler()->ProcessEvent(event
) ) 
 531                 // set it first in case SetFocusFromKbd() results in focus 
 533                 m_winLastFocused 
= child
; 
 535                 // everything is simple: just give focus to it 
 536                 child
->SetFocusFromKbd(); 
 538             //else: the child manages its focus itself 
 545         node 
= forward 
? node
->GetNext() : node
->GetPrevious(); 
 548     // we cycled through all of our children and none of them wanted to accept 
 553 void wxControlContainer::HandleOnWindowDestroy(wxWindowBase 
*child
) 
 555     if ( child 
== m_winLastFocused 
) 
 556         m_winLastFocused 
= NULL
; 
 559 // ---------------------------------------------------------------------------- 
 561 // ---------------------------------------------------------------------------- 
 563 bool wxControlContainer::DoSetFocus() 
 565     wxLogTrace(TRACE_FOCUS
, _T("SetFocus on wxPanel 0x%p."), 
 566                m_winParent
->GetHandle()); 
 571     // when the panel gets the focus we move the focus to either the last 
 572     // window that had the focus or the first one that can get it unless the 
 573     // focus had been already set to some other child 
 575     wxWindow 
*win 
= wxWindow::FindFocus(); 
 578         if ( win 
== m_winParent 
) 
 580             // our child already has focus, don't take it away from it 
 584         if ( win
->IsTopLevel() ) 
 586             // don't look beyond the first top level parent - useless and 
 591         win 
= win
->GetParent(); 
 594     // protect against infinite recursion: 
 597     bool ret 
= SetFocusToChild(); 
 599     m_inSetFocus 
= false; 
 604 void wxControlContainer::HandleOnFocus(wxFocusEvent
& event
) 
 606     wxLogTrace(TRACE_FOCUS
, _T("OnFocus on wxPanel 0x%p, name: %s"), 
 607                m_winParent
->GetHandle(), 
 608                m_winParent
->GetName().c_str() ); 
 615 bool wxControlContainer::SetFocusToChild() 
 617     return wxSetFocusToChild(m_winParent
, &m_winLastFocused
); 
 620 // ---------------------------------------------------------------------------- 
 621 // SetFocusToChild(): this function is used by wxPanel but also by wxFrame in 
 622 // wxMSW, this is why it is outside of wxControlContainer class 
 623 // ---------------------------------------------------------------------------- 
 625 bool wxSetFocusToChild(wxWindow 
*win
, wxWindow 
**childLastFocused
) 
 627     wxCHECK_MSG( win
, false, _T("wxSetFocusToChild(): invalid window") ); 
 628     wxCHECK_MSG( childLastFocused
, false, 
 629                  _T("wxSetFocusToChild(): NULL child poonter") ); 
 631     if ( *childLastFocused 
) 
 633         // It might happen that the window got reparented 
 634         if ( (*childLastFocused
)->GetParent() == win 
) 
 636             wxLogTrace(TRACE_FOCUS
, 
 637                        _T("SetFocusToChild() => last child (0x%p)."), 
 638                        (*childLastFocused
)->GetHandle()); 
 640             // not SetFocusFromKbd(): we're restoring focus back to the old 
 641             // window and not setting it as the result of a kbd action 
 642             (*childLastFocused
)->SetFocus(); 
 647             // it doesn't count as such any more 
 648             *childLastFocused 
= (wxWindow 
*)NULL
; 
 652     // set the focus to the first child who wants it 
 653     wxWindowList::compatibility_iterator node 
= win
->GetChildren().GetFirst(); 
 656         wxWindow 
*child 
= node
->GetData(); 
 657         node 
= node
->GetNext(); 
 659         // skip special windows: 
 660         if ( !win
->IsClientAreaChild(child
) ) 
 663         if ( child
->CanAcceptFocusFromKeyboard() && !child
->IsTopLevel() ) 
 666             // If a radiobutton is the first focusable child, search for the 
 667             // selected radiobutton in the same group 
 668             wxRadioButton
* btn 
= wxDynamicCast(child
, wxRadioButton
); 
 671                 wxRadioButton
* selected 
= wxGetSelectedButtonInGroup(btn
); 
 677             wxLogTrace(TRACE_FOCUS
, 
 678                        _T("SetFocusToChild() => first child (0x%p)."), 
 681             *childLastFocused 
= child
; 
 682             child
->SetFocusFromKbd(); 
 690 #endif // !wxHAS_NATIVE_TAB_TRAVERSAL