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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  21     #pragma implementation "containr.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  34     #include "wx/window.h" 
  37 #include "wx/containr.h" 
  40     #include "wx/scrolbar.h" 
  44     #include "wx/radiobut.h" 
  47 // ============================================================================ 
  49 // ============================================================================ 
  51 wxControlContainer::wxControlContainer(wxWindow 
*winParent
) 
  53     m_winParent 
= winParent
; 
  61 bool wxControlContainer::AcceptsFocus() const 
  63     // if we're not shown or disabled, we can't accept focus 
  64     if ( m_winParent
->IsShown() && m_winParent
->IsEnabled() ) 
  66         // otherwise we can accept focus either if we have no children at all 
  67         // (in this case we're probably not used as a container) or only when 
  68         // at least one child will accept focus 
  69         wxWindowList::compatibility_iterator node 
= m_winParent
->GetChildren().GetFirst(); 
  74         // wxMac has eventually the two scrollbars as children, they don't count 
  75         // as real children in the algorithm mentioned above 
  76         bool hasRealChildren 
= false ; 
  81             wxWindow 
*child 
= node
->GetData(); 
  83             if ( child
->AcceptsFocus() ) 
  89             wxScrollBar 
*sb 
= wxDynamicCast( child 
, wxScrollBar 
) ; 
  90             if ( sb 
== NULL 
|| !m_winParent
->MacIsWindowScrollbar( sb 
) ) 
  91                 hasRealChildren 
= true ; 
  93             node 
= node
->GetNext(); 
  97         if ( !hasRealChildren 
) 
 105 void wxControlContainer::SetLastFocus(wxWindow 
*win
) 
 107     // the panel itself should never get the focus at all but if it does happen 
 108     // temporarily (as it seems to do under wxGTK), at the very least don't 
 109     // forget our previous m_winLastFocused 
 110     if ( win 
!= m_winParent 
) 
 112         // if we're setting the focus 
 115             // find the last _immediate_ child which got focus 
 116             wxWindow 
*winParent 
= win
; 
 117             while ( winParent 
!= m_winParent 
) 
 120                 winParent 
= win
->GetParent(); 
 122                 // Yes, this can happen, though in a totally pathological case. 
 123                 // like when detaching a menubar from a frame with a child 
 124                 // which has pushed itself as an event handler for the menubar. 
 127                 wxASSERT_MSG( winParent
, 
 128                               _T("Setting last focus for a window that is not our child?") ); 
 132         m_winLastFocused 
= win
; 
 136             wxLogTrace(_T("focus"), _T("Set last focus to %s(%s)"), 
 137                        win
->GetClassInfo()->GetClassName(), 
 138                        win
->GetLabel().c_str()); 
 142             wxLogTrace(_T("focus"), _T("No more last focus")); 
 146     // propagate the last focus upwards so that our parent can set focus back 
 147     // to us if it loses it now and regains later 
 148     wxWindow 
*parent 
= m_winParent
->GetParent(); 
 151         wxChildFocusEvent 
eventFocus(m_winParent
); 
 152         parent
->GetEventHandler()->ProcessEvent(eventFocus
); 
 156 // ---------------------------------------------------------------------------- 
 157 // Keyboard handling - this is the place where the TAB traversal logic is 
 158 // implemented. As this code is common to all ports, this ensures consistent 
 159 // behaviour even if we don't specify how exactly the wxNavigationKeyEvent are 
 160 // generated and this is done in platform specific code which also ensures that 
 161 // we can follow the given platform standards. 
 162 // ---------------------------------------------------------------------------- 
 164 void wxControlContainer::HandleOnNavigationKey( wxNavigationKeyEvent
& event 
) 
 166     wxWindow 
*parent 
= m_winParent
->GetParent(); 
 168     // the event is propagated downwards if the event emitter was our parent 
 169     bool goingDown 
= event
.GetEventObject() == parent
; 
 171     const wxWindowList
& children 
= m_winParent
->GetChildren(); 
 173     // there is not much to do if we don't have children and we're not 
 174     // interested in "notebook page change" events here 
 175     if ( !children
.GetCount() || event
.IsWindowChange() ) 
 177         // let the parent process it unless it already comes from our parent 
 178         // of we don't have any 
 180              !parent 
|| !parent
->GetEventHandler()->ProcessEvent(event
) ) 
 188     // where are we going? 
 189     bool forward 
= event
.GetDirection(); 
 191     // the node of the children list from which we should start looking for the 
 192     // next acceptable child 
 193     wxWindowList::compatibility_iterator node
, start_node
; 
 195     // we should start from the first/last control and not from the one which 
 196     // had focus the last time if we're propagating the event downwards because 
 197     // for our parent we look like a single control 
 200         // just to be sure it's not used (normally this is not necessary, but 
 201         // doesn't hurt neither) 
 202         m_winLastFocused 
= (wxWindow 
*)NULL
; 
 204         // start from first or last depending on where we're going 
 205         node 
= forward 
? children
.GetFirst() : children
.GetLast(); 
 207         // we want to cycle over all nodes 
 208         start_node 
= wxWindowList::compatibility_iterator(); 
 212         // try to find the child which has the focus currently 
 214         // the event emitter might have done this for us 
 215         wxWindow 
*winFocus 
= event
.GetCurrentFocus(); 
 217         // but if not, we might know where the focus was ourselves 
 219             winFocus 
= m_winLastFocused
; 
 221         // if still no luck, do it the hard way 
 223             winFocus 
= wxWindow::FindFocus(); 
 227             // ok, we found the focus - now is it our child? 
 228             start_node 
= children
.Find( winFocus 
); 
 232             start_node 
= wxWindowList::compatibility_iterator(); 
 235         if ( !start_node 
&& m_winLastFocused 
) 
 237             // window which has focus isn't our child, fall back to the one 
 238             // which had the focus the last time 
 239             start_node 
= children
.Find( m_winLastFocused 
); 
 242         // if we still didn't find anything, we should start with the first one 
 245             start_node 
= children
.GetFirst(); 
 248         // and the first child which we can try setting focus to is the next or 
 250         node 
= forward 
? start_node
->GetNext() : start_node
->GetPrevious(); 
 253     // we want to cycle over all elements passing by NULL 
 254     while ( node 
!= start_node 
) 
 256         // Have we come to the last or first item on the panel? 
 261                 // Check if our (may be grand) parent is another panel: if this 
 262                 // is the case, they will know what to do with this navigation 
 263                 // key and so give them the chance to process it instead of 
 264                 // looping inside this panel (normally, the focus will go to 
 265                 // the next/previous item after this panel in the parent 
 267                 wxWindow 
*focussed_child_of_parent 
= m_winParent
; 
 270                     // we don't want to tab into a different dialog or frame 
 271                     if ( focussed_child_of_parent
->IsTopLevel() ) 
 274                     event
.SetCurrentFocus( focussed_child_of_parent 
); 
 275                     if ( parent
->GetEventHandler()->ProcessEvent( event 
) ) 
 278                     focussed_child_of_parent 
= parent
; 
 280                     parent 
= parent
->GetParent(); 
 283             //else: as the focus came from our parent, we definitely don't want 
 284             //      to send it back to it! 
 286             // no, we are not inside another panel so process this ourself 
 287             node 
= forward 
? children
.GetFirst() : children
.GetLast(); 
 292         wxWindow 
*child 
= node
->GetData(); 
 295         bool is_not_msw_rb 
= !m_winLastFocused 
|| 
 296                                 !wxIsKindOf(m_winLastFocused
,wxRadioButton
); 
 298         static const bool is_not_msw_rb 
= true; 
 301         if ( child
->AcceptsFocusFromKeyboard() && is_not_msw_rb 
) 
 303             // if we're setting the focus to a child panel we should prevent it 
 304             // from giving it to the child which had the focus the last time 
 305             // and instead give it to the first/last child depending from which 
 306             // direction we're coming 
 307             event
.SetEventObject(m_winParent
); 
 310             // we need to hop to the next activated 
 311             // radio button, not just the next radio 
 313             if (wxIsKindOf(child
,wxRadioButton
)) 
 315                 wxRadioButton 
*rb 
= (wxRadioButton
*) child
; 
 320                         wxWindowList::compatibility_iterator node 
= children
.Find( child 
); 
 322                             node 
= node
->GetNext(); 
 324                             node 
= node
->GetPrevious(); 
 326                             return; // this would probably an error 
 327                         child 
= node
->GetData(); 
 328                         if (!wxIsKindOf(child
,wxRadioButton
)) 
 330                         rb 
= (wxRadioButton
*) child
; 
 338             // disable propagation for this call as otherwise the event might 
 339             // bounce back to us. 
 340             wxPropagationDisabler 
disableProp(event
); 
 341             if ( !child
->GetEventHandler()->ProcessEvent(event
) ) 
 343                 // set it first in case SetFocusFromKbd() results in focus 
 345                 m_winLastFocused 
= child
; 
 347                 // everything is simple: just give focus to it 
 348                 child
->SetFocusFromKbd(); 
 350             //else: the child manages its focus itself 
 357         node 
= forward 
? node
->GetNext() : node
->GetPrevious(); 
 360     // we cycled through all of our children and none of them wanted to accept 
 365 void wxControlContainer::HandleOnWindowDestroy(wxWindowBase 
*child
) 
 367     if ( child 
== m_winLastFocused 
) 
 368         m_winLastFocused 
= NULL
; 
 370     if ( child 
== m_winDefault 
) 
 373     if ( child 
== m_winTmpDefault 
) 
 374         m_winTmpDefault 
= NULL
; 
 377 // ---------------------------------------------------------------------------- 
 379 // ---------------------------------------------------------------------------- 
 381 bool wxControlContainer::DoSetFocus() 
 383     wxLogTrace(_T("focus"), _T("SetFocus on wxPanel 0x%08lx."), 
 384                (unsigned long)m_winParent
->GetHandle()); 
 389     // when the panel gets the focus we move the focus to either the last 
 390     // window that had the focus or the first one that can get it unless the 
 391     // focus had been already set to some other child 
 393     wxWindow 
*win 
= wxWindow::FindFocus(); 
 396         if ( win 
== m_winParent 
) 
 398             // our child already has focus, don't take it away from it 
 402         if ( win
->IsTopLevel() ) 
 404             // don't look beyond the first top level parent - useless and 
 409         win 
= win
->GetParent(); 
 412     // protect against infinite recursion: 
 415     bool ret 
= SetFocusToChild(); 
 417     m_inSetFocus 
= false; 
 422 void wxControlContainer::HandleOnFocus(wxFocusEvent
& event
) 
 424     wxLogTrace(_T("focus"), _T("OnFocus on wxPanel 0x%08lx, name: %s"), 
 425                (unsigned long)m_winParent
->GetHandle(), 
 426                m_winParent
->GetName().c_str() ); 
 433 bool wxControlContainer::SetFocusToChild() 
 435     return wxSetFocusToChild(m_winParent
, &m_winLastFocused
); 
 438 // ---------------------------------------------------------------------------- 
 439 // SetFocusToChild(): this function is used by wxPanel but also by wxFrame in 
 440 // wxMSW, this is why it is outside of wxControlContainer class 
 441 // ---------------------------------------------------------------------------- 
 443 bool wxSetFocusToChild(wxWindow 
*win
, wxWindow 
**childLastFocused
) 
 445     wxCHECK_MSG( win
, false, _T("wxSetFocusToChild(): invalid window") ); 
 446     wxCHECK_MSG( childLastFocused
, false, 
 447                  _T("wxSetFocusToChild(): NULL child poonter") ); 
 449     if ( *childLastFocused 
) 
 451         // It might happen that the window got reparented 
 452         if ( (*childLastFocused
)->GetParent() == win 
) 
 454             wxLogTrace(_T("focus"), 
 455                        _T("SetFocusToChild() => last child (0x%08lx)."), 
 456                        (unsigned long)(*childLastFocused
)->GetHandle()); 
 458             // not SetFocusFromKbd(): we're restoring focus back to the old 
 459             // window and not setting it as the result of a kbd action 
 460             (*childLastFocused
)->SetFocus(); 
 465             // it doesn't count as such any more 
 466             *childLastFocused 
= (wxWindow 
*)NULL
; 
 470     // set the focus to the first child who wants it 
 471     wxWindowList::compatibility_iterator node 
= win
->GetChildren().GetFirst(); 
 474         wxWindow 
*child 
= node
->GetData(); 
 476         if ( child
->AcceptsFocusFromKeyboard() && !child
->IsTopLevel() ) 
 478             wxLogTrace(_T("focus"), 
 479                        _T("SetFocusToChild() => first child (0x%08lx)."), 
 480                        (unsigned long)child
->GetHandle()); 
 482             *childLastFocused 
= child
; 
 483             child
->SetFocusFromKbd(); 
 487         node 
= node
->GetNext();