1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/gtk/listbox.cpp 
   4 // Author:      Robert Roebling 
   6 // Copyright:   (c) 1998 Robert Roebling 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  10 // For compilers that support precompilation, includes "wx.h". 
  11 #include "wx/wxprec.h" 
  17 #include "wx/listbox.h" 
  18 #include "wx/dynarray.h" 
  19 #include "wx/arrstr.h" 
  22 #include "wx/checklst.h" 
  23 #include "wx/settings.h" 
  24 #include "wx/gtk/private.h" 
  27 #include "wx/tooltip.h" 
  32 #include <gdk/gdkkeysyms.h> 
  34 //----------------------------------------------------------------------------- 
  36 //----------------------------------------------------------------------------- 
  38 extern void wxapp_install_idle_handler(); 
  41 //----------------------------------------------------------------------------- 
  43 //----------------------------------------------------------------------------- 
  45 extern bool           g_blockEventsOnDrag
; 
  46 extern bool           g_blockEventsOnScroll
; 
  47 extern wxCursor       g_globalCursor
; 
  48 extern wxWindowGTK   
*g_delayedFocus
; 
  49 extern wxWindowGTK   
*g_focusWindow
; 
  50 extern wxWindowGTK   
*g_focusWindowLast
; 
  52 static bool       g_hasDoubleClicked 
= FALSE
; 
  54 //----------------------------------------------------------------------------- 
  55 // idle callback for SetFirstItem 
  56 //----------------------------------------------------------------------------- 
  58 struct wxlistbox_idle_struct
 
  66 static gint 
wxlistbox_idle_callback( gpointer gdata 
) 
  68     wxlistbox_idle_struct
* data 
= (wxlistbox_idle_struct
*) gdata
; 
  71     gtk_idle_remove( data
->m_tag 
); 
  73     // check that the items haven't been deleted from the listbox since we had 
  74     // installed this callback 
  75     wxListBox 
*lbox 
= data
->m_listbox
; 
  76     if ( data
->m_item 
< lbox
->GetCount() ) 
  78         lbox
->SetFirstItem( data
->m_item 
); 
  89 //----------------------------------------------------------------------------- 
  91 //----------------------------------------------------------------------------- 
  94 static gint 
gtk_listitem_focus_in_callback( GtkWidget 
*widget
, 
  95                                           GdkEvent 
*WXUNUSED(event
), 
  99         wxapp_install_idle_handler(); 
 104     // does the window itself think that it has the focus? 
 105     if ( !win
->m_hasFocus 
) 
 107         // not yet, notify it 
 108         win
->m_hasFocus 
= TRUE
; 
 110         wxChildFocusEvent 
eventChildFocus(win
); 
 111         (void)win
->GetEventHandler()->ProcessEvent(eventChildFocus
); 
 113         wxFocusEvent 
eventFocus(wxEVT_SET_FOCUS
, win
->GetId()); 
 114         eventFocus
.SetEventObject(win
); 
 116         (void)win
->GetEventHandler()->ProcessEvent(eventFocus
); 
 123 //----------------------------------------------------------------------------- 
 125 //----------------------------------------------------------------------------- 
 128 static gint 
gtk_listitem_focus_out_callback( GtkWidget 
*widget
, GdkEventFocus 
*gdk_event
, wxWindowGTK 
*win 
) 
 131         wxapp_install_idle_handler(); 
 133     g_focusWindow 
= (wxWindowGTK 
*)NULL
; 
 135     // don't send the window a kill focus event if it thinks that it doesn't 
 136     // have focus already 
 137     if ( win
->m_hasFocus 
) 
 139         win
->m_hasFocus 
= FALSE
; 
 141         wxFocusEvent 
event( wxEVT_KILL_FOCUS
, win
->GetId() ); 
 142         event
.SetEventObject( win 
); 
 144         // even if we did process the event in wx code, still let GTK itself 
 145         // process it too as otherwise bad things happen, especially in GTK2 
 146         // where the text control simply aborts the program if it doesn't get 
 147         // the matching focus out event 
 148         (void)win
->GetEventHandler()->ProcessEvent( event 
); 
 155 //----------------------------------------------------------------------------- 
 156 // "button_release_event" 
 157 //----------------------------------------------------------------------------- 
 159 /* we would normally emit a wxEVT_COMMAND_LISTBOX_DOUBLECLICKED event once 
 160    a GDK_2BUTTON_PRESS occurs, but this has the particular problem of the 
 161    listbox keeping the focus until it receives a GDK_BUTTON_RELEASE event. 
 162    this can lead to race conditions so that we emit the dclick event 
 163    after the GDK_BUTTON_RELEASE event after the GDK_2BUTTON_PRESS event */ 
 167 gtk_listbox_button_release_callback( GtkWidget 
* WXUNUSED(widget
), 
 168                                      GdkEventButton 
* WXUNUSED(gdk_event
), 
 171     if (g_isIdle
) wxapp_install_idle_handler(); 
 173     if (g_blockEventsOnDrag
) return FALSE
; 
 174     if (g_blockEventsOnScroll
) return FALSE
; 
 176     if (!listbox
->m_hasVMT
) return FALSE
; 
 178     if (!g_hasDoubleClicked
) return FALSE
; 
 180     wxCommandEvent 
event( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
, listbox
->GetId() ); 
 181     event
.SetEventObject( listbox 
); 
 183     wxArrayInt aSelections
; 
 184     int n
, count 
= listbox
->GetSelections(aSelections
); 
 188         if ( listbox
->HasClientObjectData() ) 
 189             event
.SetClientObject( listbox
->GetClientObject(n
) ); 
 190         else if ( listbox
->HasClientUntypedData() ) 
 191             event
.SetClientData( listbox
->GetClientData(n
) ); 
 192         event
.SetString( listbox
->GetString(n
) ); 
 201     listbox
->GetEventHandler()->ProcessEvent( event 
); 
 207 //----------------------------------------------------------------------------- 
 208 // "button_press_event" 
 209 //----------------------------------------------------------------------------- 
 213 gtk_listbox_button_press_callback( GtkWidget 
*widget
, 
 214                                    GdkEventButton 
*gdk_event
, 
 217     if (g_isIdle
) wxapp_install_idle_handler(); 
 219     if (g_blockEventsOnDrag
) return FALSE
; 
 220     if (g_blockEventsOnScroll
) return FALSE
; 
 222     if (!listbox
->m_hasVMT
) return FALSE
; 
 224     int sel 
= listbox
->GtkGetIndex( widget 
); 
 226 #if wxUSE_CHECKLISTBOX 
 227     if ((listbox
->m_hasCheckBoxes
) && (gdk_event
->x 
< 15) && (gdk_event
->type 
!= GDK_2BUTTON_PRESS
)) 
 229         wxCheckListBox 
*clb 
= (wxCheckListBox 
*)listbox
; 
 231         clb
->Check( sel
, !clb
->IsChecked(sel
) ); 
 233         wxCommandEvent 
event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, listbox
->GetId() ); 
 234         event
.SetEventObject( listbox 
); 
 236         listbox
->GetEventHandler()->ProcessEvent( event 
); 
 238 #endif // wxUSE_CHECKLISTBOX 
 240     if ((gdk_event
->state 
== 0) && 
 241          (((listbox
->GetWindowStyleFlag() & wxLB_MULTIPLE
) != 0) || 
 242           ((listbox
->GetWindowStyleFlag() & wxLB_EXTENDED
) != 0)) ) 
 244             listbox
->m_blockEvent 
= TRUE
; 
 247             for (i 
= 0; i 
< (int)listbox
->GetCount(); i
++) 
 249                     gtk_list_unselect_item( GTK_LIST(listbox
->m_list
), i 
); 
 251             listbox
->m_blockEvent 
= FALSE
; 
 256     /* emit wxEVT_COMMAND_LISTBOX_DOUBLECLICKED later */ 
 257     g_hasDoubleClicked 
= (gdk_event
->type 
== GDK_2BUTTON_PRESS
); 
 263 //----------------------------------------------------------------------------- 
 265 //----------------------------------------------------------------------------- 
 269 gtk_listbox_key_press_callback( GtkWidget 
*widget
, GdkEventKey 
*gdk_event
, wxListBox 
*listbox 
) 
 272         wxapp_install_idle_handler(); 
 274     if (g_blockEventsOnDrag
) 
 279     if ((gdk_event
->keyval 
== GDK_Tab
) || (gdk_event
->keyval 
== GDK_ISO_Left_Tab
)) 
 281         wxNavigationKeyEvent new_event
; 
 282         /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */ 
 283         new_event
.SetDirection( (gdk_event
->keyval 
== GDK_Tab
) ); 
 284         /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */ 
 285         new_event
.SetWindowChange( (gdk_event
->state 
& GDK_CONTROL_MASK
) ); 
 286         new_event
.SetCurrentFocus( listbox 
); 
 287         ret 
= listbox
->GetEventHandler()->ProcessEvent( new_event 
); 
 290     if ((gdk_event
->keyval 
== GDK_Return
) && (!ret
)) 
 292         // eat return in all modes 
 296 #if wxUSE_CHECKLISTBOX 
 297     if ((gdk_event
->keyval 
== ' ') && (listbox
->m_hasCheckBoxes
) && (!ret
)) 
 299         int sel 
= listbox
->GtkGetIndex( widget 
); 
 301         wxCheckListBox 
*clb 
= (wxCheckListBox 
*)listbox
; 
 303         clb
->Check( sel
, !clb
->IsChecked(sel
) ); 
 305         wxCommandEvent 
new_event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, listbox
->GetId() ); 
 306         new_event
.SetEventObject( listbox 
); 
 307         new_event
.SetInt( sel 
); 
 308         ret 
= listbox
->GetEventHandler()->ProcessEvent( new_event 
); 
 310 #endif // wxUSE_CHECKLISTBOX 
 312     // Check or uncheck item with SPACE 
 313     if ((gdk_event
->keyval 
== ' ') && (!ret
) && 
 314          (((listbox
->GetWindowStyleFlag() & wxLB_MULTIPLE
) != 0) || 
 315           ((listbox
->GetWindowStyleFlag() & wxLB_EXTENDED
) != 0)) ) 
 317         int sel 
= listbox
->GtkGetIndex( widget 
); 
 323             if (listbox
->IsSelected( sel 
)) 
 324                 gtk_list_unselect_item( listbox
->m_list
, sel 
); 
 326                 gtk_list_select_item( listbox
->m_list
, sel 
); 
 328             wxCommandEvent 
new_event(wxEVT_COMMAND_LISTBOX_SELECTED
, listbox
->GetId() ); 
 329             new_event
.SetEventObject( listbox 
); 
 330             wxArrayInt aSelections
; 
 331             int n
, count 
= listbox
->GetSelections(aSelections
); 
 335                 if ( listbox
->HasClientObjectData() ) 
 336                     new_event
.SetClientObject( listbox
->GetClientObject(n
) ); 
 337                 else if ( listbox
->HasClientUntypedData() ) 
 338                     new_event
.SetClientData( listbox
->GetClientData(n
) ); 
 339                 new_event
.SetString( listbox
->GetString(n
) ); 
 346             listbox
->GetEventHandler()->ProcessEvent( new_event 
); 
 352         gtk_signal_emit_stop_by_name( GTK_OBJECT(widget
), "key_press_event" ); 
 360 //----------------------------------------------------------------------------- 
 361 // "select" and "deselect" 
 362 //----------------------------------------------------------------------------- 
 364 static void gtk_listitem_select_cb( GtkWidget 
*widget
, 
 368     if (g_isIdle
) wxapp_install_idle_handler(); 
 370     if (!listbox
->m_hasVMT
) return; 
 371     if (g_blockEventsOnDrag
) return; 
 373     if (listbox
->m_blockEvent
) return; 
 375     wxCommandEvent 
event(wxEVT_COMMAND_LISTBOX_SELECTED
, listbox
->GetId() ); 
 376     event
.SetEventObject( listbox 
); 
 378     // indicate whether this is a selection or a deselection 
 379     event
.SetExtraLong( is_selection 
); 
 381     if ((listbox
->GetWindowStyleFlag() & wxLB_SINGLE
) != 0) 
 383         int sel 
= listbox
->GtkGetIndex( widget 
); 
 385         if (listbox
->m_prevSelection 
!= sel
) 
 386             gtk_list_unselect_item( listbox
->m_list
, listbox
->m_prevSelection 
); 
 388         listbox
->m_prevSelection 
= sel
; 
 391     wxArrayInt aSelections
; 
 392     int n
, count 
= listbox
->GetSelections(aSelections
); 
 396         if ( listbox
->HasClientObjectData() ) 
 397             event
.SetClientObject( listbox
->GetClientObject(n
) ); 
 398         else if ( listbox
->HasClientUntypedData() ) 
 399             event
.SetClientData( listbox
->GetClientData(n
) ); 
 400         event
.SetString( listbox
->GetString(n
) ); 
 409 //    No longer required with new code in wxLB_SINGLE 
 410 //    listbox->GetEventHandler()->AddPendingEvent( event ); 
 411     listbox
->GetEventHandler()->ProcessEvent( event 
); 
 415 static void gtk_listitem_select_callback( GtkWidget 
*widget
, wxListBox 
*listbox 
) 
 417     gtk_listitem_select_cb( widget
, listbox
, TRUE 
); 
 422 static void gtk_listitem_deselect_callback( GtkWidget 
*widget
, wxListBox 
*listbox 
) 
 424     gtk_listitem_select_cb( widget
, listbox
, FALSE 
); 
 428 //----------------------------------------------------------------------------- 
 430 //----------------------------------------------------------------------------- 
 434 gtk_listbox_realized_callback( GtkWidget 
*m_widget
, wxListBox 
*win 
) 
 437         wxapp_install_idle_handler(); 
 439     GList 
*child 
= win
->m_list
->children
; 
 440     for (child 
= win
->m_list
->children
; child 
!= NULL
; child 
= child
->next
) 
 441         gtk_widget_show( GTK_WIDGET(child
->data
) ); 
 447 //----------------------------------------------------------------------------- 
 449 //----------------------------------------------------------------------------- 
 451 IMPLEMENT_DYNAMIC_CLASS(wxListBox
,wxControl
) 
 453 // ---------------------------------------------------------------------------- 
 455 // ---------------------------------------------------------------------------- 
 457 wxListBox::wxListBox() 
 459     m_list 
= (GtkList 
*) NULL
; 
 460 #if wxUSE_CHECKLISTBOX 
 461     m_hasCheckBoxes 
= FALSE
; 
 462 #endif // wxUSE_CHECKLISTBOX 
 465 bool wxListBox::Create( wxWindow 
*parent
, wxWindowID id
, 
 466                         const wxPoint 
&pos
, const wxSize 
&size
, 
 467                         const wxArrayString
& choices
, 
 468                         long style
, const wxValidator
& validator
, 
 469                         const wxString 
&name 
) 
 471     wxCArrayString 
chs(choices
); 
 473     return Create( parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 474                    style
, validator
, name 
); 
 477 bool wxListBox::Create( wxWindow 
*parent
, wxWindowID id
, 
 478                         const wxPoint 
&pos
, const wxSize 
&size
, 
 479                         int n
, const wxString choices
[], 
 480                         long style
, const wxValidator
& validator
, 
 481                         const wxString 
&name 
) 
 484     m_acceptsFocus 
= TRUE
; 
 485     m_prevSelection 
= 0;  // or -1 ?? 
 486     m_blockEvent 
= FALSE
; 
 488     if (!PreCreation( parent
, pos
, size 
) || 
 489         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
 491         wxFAIL_MSG( wxT("wxListBox creation failed") ); 
 495     m_widget 
= gtk_scrolled_window_new( (GtkAdjustment
*) NULL
, (GtkAdjustment
*) NULL 
); 
 496     if (style 
& wxLB_ALWAYS_SB
) 
 498       gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget
), 
 499         GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS 
); 
 503       gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget
), 
 504         GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC 
); 
 507     m_list 
= GTK_LIST( gtk_list_new() ); 
 509     GtkSelectionMode mode
; 
 510     if (style 
& wxLB_MULTIPLE
) 
 512         mode 
= GTK_SELECTION_MULTIPLE
; 
 514     else if (style 
& wxLB_EXTENDED
) 
 516         mode 
= GTK_SELECTION_EXTENDED
; 
 520         // if style was 0 set single mode 
 521         m_windowStyle 
|= wxLB_SINGLE
; 
 522         mode 
= GTK_SELECTION_SINGLE
; 
 525     gtk_list_set_selection_mode( GTK_LIST(m_list
), mode 
); 
 527     gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(m_widget
), GTK_WIDGET(m_list
) ); 
 529     /* make list scroll when moving the focus down using cursor keys */ 
 530     gtk_container_set_focus_vadjustment( 
 531         GTK_CONTAINER(m_list
), 
 532         gtk_scrolled_window_get_vadjustment( 
 533             GTK_SCROLLED_WINDOW(m_widget
))); 
 535     gtk_widget_show( GTK_WIDGET(m_list
) ); 
 537     gtk_signal_connect( GTK_OBJECT(m_list
), "realize", 
 538                         GTK_SIGNAL_FUNC(gtk_listbox_realized_callback
), (gpointer
) this ); 
 540     if ( style 
& wxLB_SORT 
) 
 542         // this will change DoAppend() behaviour 
 543         m_strings 
= new wxSortedArrayString
; 
 547         m_strings 
= (wxSortedArrayString 
*)NULL
; 
 550     for (int i 
= 0; i 
< n
; i
++) 
 553         DoAppend(choices
[i
]); 
 556     m_parent
->DoAddChild( this ); 
 559     SetBestSize(size
); // need this too because this is a wxControlWithItems 
 564 wxListBox::~wxListBox() 
 574 // ---------------------------------------------------------------------------- 
 576 // ---------------------------------------------------------------------------- 
 578 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
) 
 580     wxCHECK_RET( m_list 
!= NULL
, wxT("invalid listbox") ); 
 582     // VZ: notice that InsertItems knows nothing about sorting, so calling it 
 583     //     from outside (and not from our own Append) is likely to break 
 586     // code elsewhere supposes we have as many items in m_clientList as items 
 588     wxASSERT_MSG( m_clientList
.GetCount() == (size_t)GetCount(), 
 589                   wxT("bug in client data management") ); 
 591     InvalidateBestSize(); 
 593     GList 
*children 
= m_list
->children
; 
 594     int length 
= g_list_length(children
); 
 596     wxCHECK_RET( pos 
<= length
, wxT("invalid index in wxListBox::InsertItems") ); 
 598     size_t nItems 
= items
.GetCount(); 
 603         for (size_t n 
= 0; n 
< nItems
; n
++) 
 605             index 
= m_strings
->Add( items
[n
] ); 
 607             if (index 
!= GetCount()) 
 609                 GtkAddItem( items
[n
], index 
); 
 610                 wxList::compatibility_iterator node 
= m_clientList
.Item( index 
); 
 611                 m_clientList
.Insert( node
, (wxObject
*) NULL 
); 
 615                 GtkAddItem( items
[n
] ); 
 616                 m_clientList
.Append( (wxObject
*) NULL 
); 
 624             for ( size_t n 
= 0; n 
< nItems
; n
++ ) 
 626                 GtkAddItem( items
[n
] ); 
 628                 m_clientList
.Append((wxObject 
*)NULL
); 
 633             wxList::compatibility_iterator node 
= m_clientList
.Item( pos 
); 
 634             for ( size_t n 
= 0; n 
< nItems
; n
++ ) 
 636                 GtkAddItem( items
[n
], pos
+n 
); 
 638                 m_clientList
.Insert( node
, (wxObject 
*)NULL 
); 
 643     wxASSERT_MSG( m_clientList
.GetCount() == (size_t)GetCount(), 
 644                       wxT("bug in client data management") ); 
 647 int wxListBox::DoAppend( const wxString
& item 
) 
 649     InvalidateBestSize(); 
 653         // need to determine the index 
 654         int index 
= m_strings
->Add( item 
); 
 656         // only if not at the end anyway 
 657         if (index 
!= GetCount()) 
 659            GtkAddItem( item
, index 
); 
 661            wxList::compatibility_iterator node 
= m_clientList
.Item( index 
); 
 662            m_clientList
.Insert( node
, (wxObject 
*)NULL 
); 
 670     m_clientList
.Append((wxObject 
*)NULL
); 
 672     return GetCount() - 1; 
 675 void wxListBox::GtkAddItem( const wxString 
&item
, int pos 
) 
 677     wxCHECK_RET( m_list 
!= NULL
, wxT("invalid listbox") ); 
 679     GtkWidget 
*list_item
; 
 681     wxString 
label(item
); 
 682 #if wxUSE_CHECKLISTBOX 
 685         label
.Prepend(wxCHECKLBOX_STRING
); 
 687 #endif // wxUSE_CHECKLISTBOX 
 689     list_item 
= gtk_list_item_new_with_label( wxGTK_CONV( label 
) ); 
 691     GList 
*gitem_list 
= g_list_alloc (); 
 692     gitem_list
->data 
= list_item
; 
 695         gtk_list_append_items( GTK_LIST (m_list
), gitem_list 
); 
 697         gtk_list_insert_items( GTK_LIST (m_list
), gitem_list
, pos 
); 
 699     gtk_signal_connect_after( GTK_OBJECT(list_item
), "select", 
 700       GTK_SIGNAL_FUNC(gtk_listitem_select_callback
), (gpointer
)this ); 
 702     if (HasFlag(wxLB_MULTIPLE
) || HasFlag(wxLB_EXTENDED
)) 
 703         gtk_signal_connect_after( GTK_OBJECT(list_item
), "deselect", 
 704           GTK_SIGNAL_FUNC(gtk_listitem_deselect_callback
), (gpointer
)this ); 
 706     gtk_signal_connect( GTK_OBJECT(list_item
), 
 707                         "button_press_event", 
 708                         (GtkSignalFunc
)gtk_listbox_button_press_callback
, 
 711     gtk_signal_connect_after( GTK_OBJECT(list_item
), 
 712                         "button_release_event", 
 713                         (GtkSignalFunc
)gtk_listbox_button_release_callback
, 
 716     gtk_signal_connect( GTK_OBJECT(list_item
), 
 718                            (GtkSignalFunc
)gtk_listbox_key_press_callback
, 
 722     gtk_signal_connect( GTK_OBJECT(list_item
), "focus_in_event", 
 723             GTK_SIGNAL_FUNC(gtk_listitem_focus_in_callback
), (gpointer
)this ); 
 725     gtk_signal_connect( GTK_OBJECT(list_item
), "focus_out_event", 
 726             GTK_SIGNAL_FUNC(gtk_listitem_focus_out_callback
), (gpointer
)this ); 
 728     ConnectWidget( list_item 
); 
 730     if (GTK_WIDGET_REALIZED(m_widget
)) 
 732         gtk_widget_show( list_item 
); 
 734         gtk_widget_realize( list_item 
); 
 735         gtk_widget_realize( GTK_BIN(list_item
)->child 
); 
 738         if (m_tooltip
) m_tooltip
->Apply( this ); 
 742     // Apply current widget style to the new list_item 
 743     GtkRcStyle 
*style 
= CreateWidgetStyle(); 
 746         gtk_widget_modify_style( GTK_WIDGET( list_item 
), style 
); 
 747         GtkBin 
*bin 
= GTK_BIN( list_item 
); 
 748         gtk_widget_modify_style( GTK_WIDGET( bin
->child 
), style 
); 
 749         gtk_rc_style_unref( style 
); 
 753 void wxListBox::DoSetItems( const wxArrayString
& items
, 
 758     DoInsertItems(items
, 0); 
 762         size_t count 
= items
.GetCount(); 
 763         for ( size_t n 
= 0; n 
< count
; n
++ ) 
 765             SetClientData(n
, clientData
[n
]); 
 770 // ---------------------------------------------------------------------------- 
 772 // ---------------------------------------------------------------------------- 
 774 void wxListBox::Clear() 
 776     wxCHECK_RET( m_list 
!= NULL
, wxT("invalid listbox") ); 
 778     gtk_list_clear_items( m_list
, 0, GetCount() ); 
 780     if ( GTK_LIST(m_list
)->last_focus_child 
!= NULL  
) 
 782         // This should be NULL, I think. 
 783         GTK_LIST(m_list
)->last_focus_child 
= NULL
; 
 786     if ( HasClientObjectData() ) 
 788         // destroy the data (due to Robert's idea of using wxList<wxObject> 
 789         // and not wxList<wxClientData> we can't just say 
 790         // m_clientList.DeleteContents(TRUE) - this would crash! 
 791         wxList::compatibility_iterator node 
= m_clientList
.GetFirst(); 
 794             delete (wxClientData 
*)node
->GetData(); 
 795             node 
= node
->GetNext(); 
 798     m_clientList
.Clear(); 
 804 void wxListBox::Delete( int n 
) 
 806     wxCHECK_RET( m_list 
!= NULL
, wxT("invalid listbox") ); 
 808     GList 
*child 
= g_list_nth( m_list
->children
, n 
); 
 810     wxCHECK_RET( child
, wxT("wrong listbox index") ); 
 812     GList 
*list 
= g_list_append( (GList
*) NULL
, child
->data 
); 
 813     gtk_list_remove_items( m_list
, list 
); 
 816     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 819         if ( m_clientDataItemsType 
== wxClientData_Object 
) 
 821             wxClientData 
*cd 
= (wxClientData
*)node
->GetData(); 
 825         m_clientList
.Erase( node 
); 
 829         m_strings
->RemoveAt(n
); 
 832 // ---------------------------------------------------------------------------- 
 834 // ---------------------------------------------------------------------------- 
 836 void wxListBox::DoSetItemClientData( int n
, void* clientData 
) 
 838     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid listbox control") ); 
 840     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 841     wxCHECK_RET( node
, wxT("invalid index in wxListBox::DoSetItemClientData") ); 
 843     node
->SetData( (wxObject
*) clientData 
); 
 846 void* wxListBox::DoGetItemClientData( int n 
) const 
 848     wxCHECK_MSG( m_widget 
!= NULL
, NULL
, wxT("invalid listbox control") ); 
 850     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 851     wxCHECK_MSG( node
, NULL
, wxT("invalid index in wxListBox::DoGetItemClientData") ); 
 853     return node
->GetData(); 
 856 void wxListBox::DoSetItemClientObject( int n
, wxClientData
* clientData 
) 
 858     wxCHECK_RET( m_widget 
!= NULL
, wxT("invalid listbox control") ); 
 860     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 861     wxCHECK_RET( node
, wxT("invalid index in wxListBox::DoSetItemClientObject") ); 
 863     // wxItemContainer already deletes data for us 
 865     node
->SetData( (wxObject
*) clientData 
); 
 868 wxClientData
* wxListBox::DoGetItemClientObject( int n 
) const 
 870     wxCHECK_MSG( m_widget 
!= NULL
, (wxClientData
*) NULL
, wxT("invalid listbox control") ); 
 872     wxList::compatibility_iterator node 
= m_clientList
.Item( n 
); 
 873     wxCHECK_MSG( node
, (wxClientData 
*)NULL
, 
 874                  wxT("invalid index in wxListBox::DoGetItemClientObject") ); 
 876     return (wxClientData
*) node
->GetData(); 
 879 // ---------------------------------------------------------------------------- 
 880 // string list access 
 881 // ---------------------------------------------------------------------------- 
 883 wxString 
wxListBox::GetRealLabel(GList 
*item
) const 
 885     GtkBin 
*bin 
= GTK_BIN( item
->data 
); 
 886     GtkLabel 
*label 
= GTK_LABEL( bin
->child 
); 
 891     str 
= wxGTK_CONV_BACK( gtk_label_get_text( label 
) ); 
 893     str 
= wxString( label
->label 
); 
 896 #if wxUSE_CHECKLISTBOX 
 897     // checklistboxes have "[±] " prepended to their lables, remove it 
 899     // NB: 4 below is the length of wxCHECKLBOX_STRING from wx/gtk/checklst.h 
 900     if ( m_hasCheckBoxes 
) 
 902 #endif // wxUSE_CHECKLISTBOX 
 907 void wxListBox::SetString( int n
, const wxString 
&string 
) 
 909     wxCHECK_RET( m_list 
!= NULL
, wxT("invalid listbox") ); 
 911     GList 
*child 
= g_list_nth( m_list
->children
, n 
); 
 914         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
 915         GtkLabel 
*label 
= GTK_LABEL( bin
->child 
); 
 918 #if wxUSE_CHECKLISTBOX 
 920             str 
+= wxCHECKLBOX_STRING
; 
 921 #endif // wxUSE_CHECKLISTBOX 
 924         gtk_label_set( label
, wxGTK_CONV( str 
) ); 
 928         wxFAIL_MSG(wxT("wrong listbox index")); 
 932 wxString 
wxListBox::GetString( int n 
) const 
 934     wxCHECK_MSG( m_list 
!= NULL
, wxEmptyString
, wxT("invalid listbox") ); 
 936     GList 
*child 
= g_list_nth( m_list
->children
, n 
); 
 939         return GetRealLabel(child
); 
 942     wxFAIL_MSG(wxT("wrong listbox index")); 
 944     return wxEmptyString
; 
 947 int wxListBox::GetCount() const 
 949     wxCHECK_MSG( m_list 
!= NULL
, -1, wxT("invalid listbox") ); 
 951     GList 
*children 
= m_list
->children
; 
 952     return g_list_length(children
); 
 955 int wxListBox::FindString( const wxString 
&item
, bool bCase 
) const 
 957     wxCHECK_MSG( m_list 
!= NULL
, wxNOT_FOUND
, wxT("invalid listbox") ); 
 959     GList 
*child 
= m_list
->children
; 
 963         if ( item
.IsSameAs( GetRealLabel(child
), bCase 
) ) 
 970     // it's not an error if the string is not found -> no wxCHECK 
 975 // ---------------------------------------------------------------------------- 
 977 // ---------------------------------------------------------------------------- 
 979 int wxListBox::GetSelection() const 
 981     wxCHECK_MSG( m_list 
!= NULL
, -1, wxT("invalid listbox") ); 
 983     GList 
*child 
= m_list
->children
; 
 987         if (GTK_WIDGET(child
->data
)->state 
== GTK_STATE_SELECTED
) return count
; 
 994 int wxListBox::GetSelections( wxArrayInt
& aSelections 
) const 
 996     wxCHECK_MSG( m_list 
!= NULL
, -1, wxT("invalid listbox") ); 
 998     // get the number of selected items first 
 999     GList 
*child 
= m_list
->children
; 
1001     for (child 
= m_list
->children
; child 
!= NULL
; child 
= child
->next
) 
1003         if (GTK_WIDGET(child
->data
)->state 
== GTK_STATE_SELECTED
) 
1007     aSelections
.Empty(); 
1011         // now fill the list 
1012         aSelections
.Alloc(count
); // optimization attempt 
1014         for (child 
= m_list
->children
; child 
!= NULL
; child 
= child
->next
, i
++) 
1016             if (GTK_WIDGET(child
->data
)->state 
== GTK_STATE_SELECTED
) 
1024 bool wxListBox::IsSelected( int n 
) const 
1026     wxCHECK_MSG( m_list 
!= NULL
, FALSE
, wxT("invalid listbox") ); 
1028     GList 
*target 
= g_list_nth( m_list
->children
, n 
); 
1030     wxCHECK_MSG( target
, FALSE
, wxT("invalid listbox index") ); 
1032     return (GTK_WIDGET(target
->data
)->state 
== GTK_STATE_SELECTED
) ; 
1035 void wxListBox::DoSetSelection( int n
, bool select 
) 
1037     wxCHECK_RET( m_list 
!= NULL
, wxT("invalid listbox") ); 
1039     m_blockEvent 
= TRUE
; 
1043         if ((m_windowStyle 
& wxLB_SINGLE
) != 0) 
1044             gtk_list_unselect_item( m_list
, m_prevSelection 
); 
1045         gtk_list_select_item( m_list
, n 
); 
1046         m_prevSelection 
= n
; 
1049         gtk_list_unselect_item( m_list
, n 
); 
1051     m_blockEvent 
= FALSE
; 
1054 void wxListBox::DoSetFirstItem( int n 
) 
1056     wxCHECK_RET( m_list
, wxT("invalid listbox") ); 
1058     if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_list
)) 
1061     // terribly efficient 
1062     const gchar 
*vadjustment_key 
= "gtk-vadjustment"; 
1063     guint vadjustment_key_id 
= g_quark_from_static_string (vadjustment_key
); 
1065     GtkAdjustment 
*adjustment 
= 
1066        (GtkAdjustment
*) gtk_object_get_data_by_id (GTK_OBJECT (m_list
), vadjustment_key_id
); 
1067     wxCHECK_RET( adjustment
, wxT("invalid listbox code") ); 
1069     GList 
*target 
= g_list_nth( m_list
->children
, n 
); 
1070     wxCHECK_RET( target
, wxT("invalid listbox index") ); 
1072     GtkWidget 
*item 
= GTK_WIDGET(target
->data
); 
1073     wxCHECK_RET( item
, wxT("invalid listbox code") ); 
1075     if (item
->allocation
.y 
== -1) 
1077         wxlistbox_idle_struct
* data 
= new wxlistbox_idle_struct
; 
1078         data
->m_listbox 
= this; 
1080         data
->m_tag 
= gtk_idle_add_priority( 800, wxlistbox_idle_callback
, (gpointer
) data 
); 
1085     float y 
= item
->allocation
.y
; 
1086     if (y 
> adjustment
->upper 
- adjustment
->page_size
) 
1087         y 
= adjustment
->upper 
- adjustment
->page_size
; 
1088     gtk_adjustment_set_value( adjustment
, y 
); 
1091 // ---------------------------------------------------------------------------- 
1093 // ---------------------------------------------------------------------------- 
1095 int wxListBox::GtkGetIndex( GtkWidget 
*item 
) const 
1099         GList 
*child 
= m_list
->children
; 
1103             if (GTK_WIDGET(child
->data
) == item
) return count
; 
1105             child 
= child
->next
; 
1112 void wxListBox::ApplyToolTip( GtkTooltips 
*tips
, const wxChar 
*tip 
) 
1114     GList 
*child 
= m_list
->children
; 
1117         gtk_tooltips_set_tip( tips
, GTK_WIDGET( child
->data 
), wxConvCurrent
->cWX2MB(tip
), (gchar
*) NULL 
); 
1118         child 
= child
->next
; 
1121 #endif // wxUSE_TOOLTIPS 
1123 GtkWidget 
*wxListBox::GetConnectWidget() 
1125     // return GTK_WIDGET(m_list); 
1129 bool wxListBox::IsOwnGtkWindow( GdkWindow 
*window 
) 
1134     if (m_widget
->window 
== window
) return TRUE
; 
1136     if (GTK_WIDGET(m_list
)->window 
== window
) return TRUE
; 
1138     GList 
*child 
= m_list
->children
; 
1141         GtkWidget 
*bin 
= GTK_WIDGET( child
->data 
); 
1142         if (bin
->window 
== window
) return TRUE
; 
1143         child 
= child
->next
; 
1150 void wxListBox::DoApplyWidgetStyle(GtkRcStyle 
*style
) 
1152     if (m_hasBgCol 
&& m_backgroundColour
.Ok()) 
1154         GdkWindow 
*window 
= GTK_WIDGET(m_list
)->window
; 
1157             m_backgroundColour
.CalcPixel( gdk_window_get_colormap( window 
) ); 
1158             gdk_window_set_background( window
, m_backgroundColour
.GetColor() ); 
1159             gdk_window_clear( window 
); 
1163     GList 
*child 
= m_list
->children
; 
1166         gtk_widget_modify_style( GTK_WIDGET(child
->data
), style 
); 
1168         GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
1169         GtkWidget 
*label 
= GTK_WIDGET( bin
->child 
); 
1170         gtk_widget_modify_style( label
, style 
); 
1172         child 
= child
->next
; 
1176 void wxListBox::OnInternalIdle() 
1178     wxCursor cursor 
= m_cursor
; 
1179     if (g_globalCursor
.Ok()) cursor 
= g_globalCursor
; 
1181     if (GTK_WIDGET(m_list
)->window 
&& cursor
.Ok()) 
1183         /* I now set the cursor the anew in every OnInternalIdle call 
1184            as setting the cursor in a parent window also effects the 
1185            windows above so that checking for the current cursor is 
1188         gdk_window_set_cursor( GTK_WIDGET(m_list
)->window
, cursor
.GetCursor() ); 
1190         GList 
*child 
= m_list
->children
; 
1193             GtkBin 
*bin 
= GTK_BIN( child
->data 
); 
1194             GtkWidget 
*label 
= GTK_WIDGET( bin
->child 
); 
1199                 gdk_window_set_cursor( label
->window
, cursor
.GetCursor() ); 
1201             child 
= child
->next
; 
1205     if (g_delayedFocus 
== this) 
1207         if (GTK_WIDGET_REALIZED(m_widget
)) 
1209             gtk_widget_grab_focus( m_widget 
); 
1210             g_delayedFocus 
= NULL
; 
1214     if (wxUpdateUIEvent::CanUpdate(this)) 
1215         UpdateWindowUI(wxUPDATE_UI_FROMIDLE
); 
1218 wxSize 
wxListBox::DoGetBestSize() const 
1220     int lbWidth 
= 100;  // some defaults 
1224     // Find the widest line 
1225     for(int i 
= 0; i 
< GetCount(); i
++) { 
1226         wxString 
str(GetString(i
)); 
1227         GetTextExtent(str
, &wLine
, NULL
); 
1228         lbWidth 
= wxMax(lbWidth
, wLine
); 
1231     // Add room for the scrollbar 
1232     lbWidth 
+= wxSystemSettings::GetMetric(wxSYS_VSCROLL_X
); 
1234     // And just a bit more 
1236     GetTextExtent( wxT("X"), &cx
, &cy
); 
1239     // don't make the listbox too tall (limit height to around 10 items) but don't 
1240     // make it too small neither 
1241     lbHeight 
= (cy
+4) * wxMin(wxMax(GetCount(), 3), 10); 
1243     wxSize 
best(lbWidth
, lbHeight
); 
1244     CacheBestSize(best
); 
1248 void wxListBox::FixUpMouseEvent(GtkWidget 
*widget
, wxCoord
& x
, wxCoord
& y
) 
1250     // the mouse event coords are relative to the listbox items, we need to 
1251     // translate them to the normal client coords 
1252     x 
+= widget
->allocation
.x
; 
1253     y 
+= widget
->allocation
.y
; 
1259 wxListBox::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
1261     return GetDefaultAttributesFromGTKWidget(gtk_list_new
, true); 
1264 #endif // wxUSE_LISTBOX