1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/gtk/listbox.cpp 
   4 // Author:      Robert Roebling 
   5 // Modified By: Ryan Norton (GtkTreeView implementation) 
   7 // Copyright:   (c) 1998 Robert Roebling 
   8 // Licence:     wxWindows licence 
   9 ///////////////////////////////////////////////////////////////////////////// 
  11 // For compilers that support precompilation, includes "wx.h". 
  12 #include "wx/wxprec.h" 
  16 #include "wx/listbox.h" 
  17 #include "wx/dynarray.h" 
  18 #include "wx/arrstr.h" 
  21 #include "wx/checklst.h" 
  22 #include "wx/settings.h" 
  24 #include "wx/gtk/private.h" 
  25 #include "wx/gtk/treeentry_gtk.h" 
  28 #include "wx/tooltip.h" 
  33 #include <gdk/gdkkeysyms.h> 
  35 //----------------------------------------------------------------------------- 
  37 //----------------------------------------------------------------------------- 
  39 extern bool           g_blockEventsOnDrag
; 
  40 extern bool           g_blockEventsOnScroll
; 
  41 extern wxCursor       g_globalCursor
; 
  45 //----------------------------------------------------------------------------- 
  46 // Macro to tell which row the strings are in (1 if native checklist, 0 if not) 
  47 //----------------------------------------------------------------------------- 
  49 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
  50 #   define WXLISTBOX_DATACOLUMN_ARG(x)  (x->m_hasCheckBoxes ? 1 : 0) 
  52 #   define WXLISTBOX_DATACOLUMN_ARG(x)  (0) 
  53 #endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
  55 #define WXLISTBOX_DATACOLUMN    WXLISTBOX_DATACOLUMN_ARG(this) 
  57 //----------------------------------------------------------------------------- 
  59 //----------------------------------------------------------------------------- 
  63 gtk_listbox_row_activated_callback(GtkTreeView        
*treeview
, 
  65                                    GtkTreeViewColumn  
*col
, 
  68     if (g_isIdle
) wxapp_install_idle_handler(); 
  70     if (g_blockEventsOnDrag
) return; 
  71     if (g_blockEventsOnScroll
) return; 
  73     if (!listbox
->m_hasVMT
) return; 
  76     //1) This is triggered by either a double-click or a space press 
  77     //2) We handle both here because 
  78     //2a) in the case of a space/keypress we can't really know 
  79     //    which item was pressed on because we can't get coords 
  81     //2b) It seems more correct 
  83     int sel 
= gtk_tree_path_get_indices(path
)[0]; 
  85     if(!listbox
->m_spacePressed
) 
  87         //Assume it was double-click 
  88         wxCommandEvent 
event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
, listbox
->GetId() ); 
  89         event
.SetEventObject( listbox 
); 
  91         if(listbox
->IsSelected(sel
)) 
  93             GtkTreeEntry
* entry 
= listbox
->GtkGetEntry(sel
); 
  98                 event
.SetString(wxConvUTF8
.cMB2WX(gtk_tree_entry_get_label(entry
))); 
 100                 if ( listbox
->HasClientObjectData() ) 
 101                     event
.SetClientObject( 
 102                     (wxClientData
*) gtk_tree_entry_get_userdata(entry
) ); 
 103                 else if ( listbox
->HasClientUntypedData() ) 
 104                     event
.SetClientData( gtk_tree_entry_get_userdata(entry
) ); 
 105                 g_object_unref(G_OBJECT(entry
)); 
 109                 wxLogSysError(wxT("Internal error - could not get entry for double-click")); 
 116         listbox
->GetEventHandler()->ProcessEvent( event 
); 
 120         listbox
->m_spacePressed 
= false; //don't block selection behaviour anymore 
 122         //Space was pressed - toggle the appropriate checkbox and the selection 
 123 #if wxUSE_CHECKLISTBOX //Do it for both native and non-native 
 124         if (listbox
->m_hasCheckBoxes
) 
 126             wxCheckListBox 
*clb 
= (wxCheckListBox 
*)listbox
; 
 128             clb
->Check( sel
, !clb
->IsChecked(sel
) ); 
 130             wxCommandEvent 
new_event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, listbox
->GetId() ); 
 131             new_event
.SetEventObject( listbox 
); 
 132             new_event
.SetInt( sel 
); 
 133             listbox
->GetEventHandler()->ProcessEvent( new_event 
); 
 135 #endif // wxUSE_CHECKLISTBOX 
 137         if(  (((listbox
->GetWindowStyleFlag() & wxLB_MULTIPLE
) != 0) || 
 138               ((listbox
->GetWindowStyleFlag() & wxLB_EXTENDED
) != 0)) ) 
 140             //toggle the selection + send event 
 141             listbox
->GtkSetSelection(sel
, !listbox
->IsSelected( sel 
), false); 
 147 //----------------------------------------------------------------------------- 
 148 // "button_press_event" 
 149 //----------------------------------------------------------------------------- 
 153 gtk_listbox_button_press_callback( GtkWidget 
*widget
, 
 154                                    GdkEventButton 
*gdk_event
, 
 157     if (g_isIdle
) wxapp_install_idle_handler(); 
 159     if (g_blockEventsOnDrag
) return FALSE
; 
 160     if (g_blockEventsOnScroll
) return FALSE
; 
 162     if (!listbox
->m_hasVMT
) return FALSE
; 
 164     //Just to be on the safe side - it should be unset in the activate callback 
 165     //but we don't want any obscure bugs if it doesn't get called somehow... 
 166     listbox
->m_spacePressed 
= false; 
 168 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 169     if ((listbox
->m_hasCheckBoxes
) && (gdk_event
->x 
< 15) && (gdk_event
->type 
!= GDK_2BUTTON_PRESS
)) 
 172         gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
), 
 173                                   (gint
)gdk_event
->x
, (gint
)gdk_event
->y
, 
 174                                   &path
, NULL
, NULL
, NULL
); 
 175         int sel 
= gtk_tree_path_get_indices(path
)[0]; 
 176         gtk_tree_path_free(path
); 
 178         wxCheckListBox 
*clb 
= (wxCheckListBox 
*)listbox
; 
 180         clb
->Check( sel
, !clb
->IsChecked(sel
) ); 
 182         wxCommandEvent 
event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, listbox
->GetId() ); 
 183         event
.SetEventObject( listbox 
); 
 185         listbox
->GetEventHandler()->ProcessEvent( event 
); 
 187 #endif // wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 193 //----------------------------------------------------------------------------- 
 195 //----------------------------------------------------------------------------- 
 199 gtk_listbox_key_press_callback( GtkWidget 
*widget
, 
 200                                 GdkEventKey 
*gdk_event
, 
 203     if (g_isIdle
) wxapp_install_idle_handler(); 
 205     if (g_blockEventsOnDrag
) return FALSE
; 
 210     if ((gdk_event
->keyval 
== GDK_Tab
) || (gdk_event
->keyval 
== GDK_ISO_Left_Tab
)) 
 212         wxNavigationKeyEvent new_event
; 
 213         /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */ 
 214         new_event
.SetDirection( (gdk_event
->keyval 
== GDK_Tab
) ); 
 215         /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */ 
 216         new_event
.SetWindowChange( (gdk_event
->state 
& GDK_CONTROL_MASK
) ); 
 217         new_event
.SetCurrentFocus( listbox 
); 
 218         ret 
= listbox
->GetEventHandler()->ProcessEvent( new_event 
); 
 221     if ((gdk_event
->keyval 
== GDK_Return
) && (!ret
)) 
 223         // eat return in all modes (RN:WHY?) 
 227     // Check or uncheck item with SPACE 
 228     if (gdk_event
->keyval 
== ' ') 
 230         //In the keyevent we don't know the index of the item 
 231         //and the activated event gets called anyway... 
 233         //Also, activating with the space causes the treeview to 
 234         //unselect all the items and then select the item in question 
 235         //wx's behaviour is to just toggle the item's selection state 
 236         //and leave the others alone 
 237         listbox
->m_spacePressed 
= true; 
 242         g_signal_stop_emission_by_name (widget
, "key_press_event"); 
 250 //----------------------------------------------------------------------------- 
 251 // "select" and "deselect" 
 252 //----------------------------------------------------------------------------- 
 255 static gboolean 
gtk_listitem_select_cb( GtkTreeSelection
* selection
, 
 258                                         gboolean is_selected
, 
 261     if (g_isIdle
) wxapp_install_idle_handler(); 
 263     if (!listbox
->m_hasVMT
) return TRUE
; 
 264     if (g_blockEventsOnDrag
) return TRUE
; 
 266     if (listbox
->m_spacePressed
) return FALSE
; //see keyevent callback 
 267     if (listbox
->m_blockEvent
) return TRUE
; 
 269     // NB: wxdocs explicitly say that this event only gets sent when 
 270     // something is actually selected, plus the controls example 
 271     // assumes so and passes -1 to the dogetclientdata funcs if not 
 273     // OK, so basically we need to do a bit of a run-around here as 
 274     // 1) is_selected says whether the item(s?) are CURRENTLY selected - 
 275     //    i.e. if is_selected is FALSE then the item is going to be 
 276     //    selected right now! 
 277     // 2) However, since it is not already selected and the user 
 278     //    will expect it to be we need to manually select it and 
 279     //    return FALSE telling GTK we handled the selection 
 280     if (is_selected
) return TRUE
; 
 282     int nIndex 
= gtk_tree_path_get_indices(path
)[0]; 
 283     GtkTreeEntry
* entry 
= listbox
->GtkGetEntry(nIndex
); 
 287         //Now, as mentioned above, we manually select the row that is/was going 
 288         //to be selected anyway by GTK 
 289         listbox
->m_blockEvent 
= true; //if we don't block events we will lock the 
 290                                       //app due to recursion!! 
 292         GtkTreeSelection
* selection 
= 
 293             gtk_tree_view_get_selection(listbox
->m_treeview
); 
 295         gtk_tree_model_get_iter(GTK_TREE_MODEL(listbox
->m_liststore
), &iter
, path
); 
 296         gtk_tree_selection_select_iter(selection
, &iter
); 
 298         listbox
->m_blockEvent 
= false; 
 300         //Finally, send the wx event 
 301         wxCommandEvent 
event(wxEVT_COMMAND_LISTBOX_SELECTED
, listbox
->GetId() ); 
 302         event
.SetEventObject( listbox 
); 
 304         // indicate whether this is a selection or a deselection 
 305         event
.SetExtraLong( 1 ); 
 307         event
.SetInt(nIndex
); 
 308         event
.SetString(wxConvUTF8
.cMB2WX(gtk_tree_entry_get_label(entry
))); 
 310         if ( listbox
->HasClientObjectData() ) 
 311             event
.SetClientObject( 
 312                     (wxClientData
*) gtk_tree_entry_get_userdata(entry
) 
 314         else if ( listbox
->HasClientUntypedData() ) 
 315             event
.SetClientData( gtk_tree_entry_get_userdata(entry
) ); 
 317         listbox
->GetEventHandler()->ProcessEvent( event 
); 
 319         g_object_unref(G_OBJECT(entry
)); 
 320         return FALSE
;  //We handled it/did it manually 
 323     return TRUE
; //allow selection to change 
 327 //----------------------------------------------------------------------------- 
 328 // GtkTreeEntry destruction (to destroy client data) 
 329 //----------------------------------------------------------------------------- 
 332 static void gtk_tree_entry_destroy_cb(GtkTreeEntry
* entry
, 
 335     if(listbox
->HasClientObjectData()) 
 337         gpointer userdata 
= gtk_tree_entry_get_userdata(entry
); 
 339             delete (wxClientData 
*)userdata
; 
 344 //----------------------------------------------------------------------------- 
 345 // Sorting callback (standard CmpNoCase return value) 
 346 //----------------------------------------------------------------------------- 
 349 static gint 
gtk_listbox_sort_callback(GtkTreeModel 
*model
, 
 355     GtkTreeEntry
* entry2
; 
 357     gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
), 
 359                              WXLISTBOX_DATACOLUMN_ARG(listbox
), 
 361     gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
), 
 363                              WXLISTBOX_DATACOLUMN_ARG(listbox
), 
 365     wxCHECK_MSG(entry
, 0, wxT("Could not get entry")); 
 366     wxCHECK_MSG(entry2
, 0, wxT("Could not get entry2")); 
 368     //We compare collate keys here instead of calling g_utf8_collate 
 369     //as it is rather slow (and even the docs reccommend this) 
 370     int ret 
= strcasecmp(gtk_tree_entry_get_collate_key(entry
), 
 371                          gtk_tree_entry_get_collate_key(entry2
)); 
 373     g_object_unref(G_OBJECT(entry
)); 
 374     g_object_unref(G_OBJECT(entry2
)); 
 380 //----------------------------------------------------------------------------- 
 381 // Searching callback (TRUE == not equal, FALSE == equal) 
 382 //----------------------------------------------------------------------------- 
 385 static gboolean 
gtk_listbox_searchequal_callback(GtkTreeModel
* model
, 
 393     gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
), 
 395                              WXLISTBOX_DATACOLUMN_ARG(listbox
), 
 397     wxCHECK_MSG(entry
, 0, wxT("Could not get entry")); 
 398     gchar
* keycollatekey 
= g_utf8_collate_key(key
, -1); 
 400     int ret 
= strcasecmp(keycollatekey
, 
 401                          gtk_tree_entry_get_collate_key(entry
)); 
 403     g_free(keycollatekey
); 
 404     g_object_unref(G_OBJECT(entry
)); 
 410 //----------------------------------------------------------------------------- 
 412 //----------------------------------------------------------------------------- 
 414 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
) 
 416 // ---------------------------------------------------------------------------- 
 418 // ---------------------------------------------------------------------------- 
 420 void wxListBox::Init() 
 422     m_treeview 
= (GtkTreeView
*) NULL
; 
 423 #if wxUSE_CHECKLISTBOX 
 424     m_hasCheckBoxes 
= false; 
 425 #endif // wxUSE_CHECKLISTBOX 
 426     m_spacePressed 
= false; 
 429 bool wxListBox::Create( wxWindow 
*parent
, wxWindowID id
, 
 430                         const wxPoint 
&pos
, const wxSize 
&size
, 
 431                         const wxArrayString
& choices
, 
 432                         long style
, const wxValidator
& validator
, 
 433                         const wxString 
&name 
) 
 435     wxCArrayString 
chs(choices
); 
 437     return Create( parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 438                    style
, validator
, name 
); 
 441 bool wxListBox::Create( wxWindow 
*parent
, wxWindowID id
, 
 442                         const wxPoint 
&pos
, const wxSize 
&size
, 
 443                         int n
, const wxString choices
[], 
 444                         long style
, const wxValidator
& validator
, 
 445                         const wxString 
&name 
) 
 448     m_acceptsFocus 
= true; 
 449     m_blockEvent 
= false; 
 451     if (!PreCreation( parent
, pos
, size 
) || 
 452         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
 454         wxFAIL_MSG( wxT("wxListBox creation failed") ); 
 458     m_widget 
= gtk_scrolled_window_new( (GtkAdjustment
*) NULL
, (GtkAdjustment
*) NULL 
); 
 459     if (style 
& wxLB_ALWAYS_SB
) 
 461       gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget
), 
 462         GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS 
); 
 466       gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget
), 
 467         GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC 
); 
 470     m_treeview 
= GTK_TREE_VIEW( gtk_tree_view_new( ) ); 
 472     //wxListBox doesn't have a header :) 
 473     //NB: If enabled SetFirstItem doesn't work correctly 
 474     gtk_tree_view_set_headers_visible(m_treeview
, FALSE
); 
 476 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
 478         ((wxCheckListBox
*)this)->DoCreateCheckList(); 
 479 #endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
 481     // Create the data column 
 482     gtk_tree_view_insert_column_with_attributes(m_treeview
, -1, "", 
 483                                                 gtk_cell_renderer_text_new(), 
 485                                                 WXLISTBOX_DATACOLUMN
, NULL
); 
 487     // Now create+set the model (GtkListStore) - first argument # of columns 
 488 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
 490         m_liststore 
= gtk_list_store_new(2, G_TYPE_BOOLEAN
, 
 491                                             GTK_TYPE_TREE_ENTRY
); 
 494         m_liststore 
= gtk_list_store_new(1, GTK_TYPE_TREE_ENTRY
); 
 496     gtk_tree_view_set_model(m_treeview
, GTK_TREE_MODEL(m_liststore
)); 
 498     g_object_unref(G_OBJECT(m_liststore
)); //free on treeview destruction 
 500     // Disable the pop-up textctrl that enables searching - note that 
 501     // the docs specify that even if this disabled (which we are doing) 
 502     // the user can still have it through the start-interactive-search 
 503     // key binding...either way we want to provide a searchequal callback 
 504     // NB: If this is enabled a doubleclick event (activate) gets sent 
 505     //     on a successful search 
 506     gtk_tree_view_set_search_column(m_treeview
, WXLISTBOX_DATACOLUMN
); 
 507     gtk_tree_view_set_search_equal_func(m_treeview
, 
 508        (GtkTreeViewSearchEqualFunc
) gtk_listbox_searchequal_callback
, 
 512     gtk_tree_view_set_enable_search(m_treeview
, FALSE
); 
 515     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection( m_treeview 
); 
 516     gtk_tree_selection_set_select_function(selection
, 
 517                      (GtkTreeSelectionFunc
)gtk_listitem_select_cb
, 
 518                                            this, NULL
); //NULL == destroycb 
 520     GtkSelectionMode mode
; 
 521     if (style 
& wxLB_MULTIPLE
) 
 523         mode 
= GTK_SELECTION_MULTIPLE
; 
 525     else if (style 
& wxLB_EXTENDED
) 
 527         mode 
= GTK_SELECTION_EXTENDED
; 
 531         // if style was 0 set single mode 
 532         m_windowStyle 
|= wxLB_SINGLE
; 
 533         mode 
= GTK_SELECTION_SINGLE
; 
 536     gtk_tree_selection_set_mode( selection
, mode 
); 
 538     //Handle sortable stuff 
 539     if(style 
& wxLB_SORT
) 
 541         //Setup sorting in ascending (wx) order 
 542         gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(m_liststore
), 
 543                                              WXLISTBOX_DATACOLUMN
, 
 546         //Set the sort callback 
 547         gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(m_liststore
), 
 548                                         WXLISTBOX_DATACOLUMN
, 
 549                    (GtkTreeIterCompareFunc
) gtk_listbox_sort_callback
, 
 551                                         NULL 
//"destroy notifier" 
 556     gtk_container_add (GTK_CONTAINER (m_widget
), GTK_WIDGET(m_treeview
) ); 
 558     gtk_widget_show( GTK_WIDGET(m_treeview
) ); 
 560     wxListBox::DoInsertItems(wxArrayString(n
, choices
), 0); // insert initial items 
 562     //treeview-specific events 
 563     g_signal_connect(m_treeview
, "row-activated", 
 564                      G_CALLBACK(gtk_listbox_row_activated_callback
), this); 
 567     g_signal_connect (m_treeview
, "button_press_event", 
 568                       G_CALLBACK (gtk_listbox_button_press_callback
), 
 570     g_signal_connect (m_treeview
, "key_press_event", 
 571                       G_CALLBACK (gtk_listbox_key_press_callback
), 
 574     m_parent
->DoAddChild( this ); 
 577     SetBestSize(size
); // need this too because this is a wxControlWithItems 
 582 wxListBox::~wxListBox() 
 589 // ---------------------------------------------------------------------------- 
 591 // ---------------------------------------------------------------------------- 
 593 void wxListBox::GtkInsertItems(const wxArrayString
& items
, 
 594                                void** clientData
, unsigned int pos
) 
 596     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 598     InvalidateBestSize(); 
 600     // Create and set column ids and GValues 
 602     unsigned int nNum 
= items
.GetCount(); 
 603     unsigned int nCurCount 
= wxListBox::GetCount(); 
 604     wxASSERT_MSG(pos 
<= nCurCount
, wxT("Invalid index passed to wxListBox")); 
 606     GtkTreeIter
* pIter 
= NULL
; // append by default 
 608     if (pos 
!= nCurCount
) 
 610         gboolean res 
= gtk_tree_model_iter_nth_child( 
 611                         GTK_TREE_MODEL(m_liststore
), 
 612                         &iter
, NULL
, //NULL = parent = get first 
 616             wxLogSysError(wxT("internal wxListBox error in insertion")); 
 623     for (unsigned int i 
= 0; i 
< nNum
; ++i
) 
 625         wxString label 
= items
[i
]; 
 627 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 630             label
.Prepend(wxCHECKLBOX_STRING
); 
 632 #endif // wxUSE_CHECKLISTBOX 
 635         GtkTreeEntry
* entry 
= gtk_tree_entry_new(); 
 636         gtk_tree_entry_set_label(entry
, wxConvUTF8
.cWX2MB(label
)); 
 637         gtk_tree_entry_set_destroy_func(entry
, 
 638                 (GtkTreeEntryDestroy
)gtk_tree_entry_destroy_cb
, 
 642             gtk_tree_entry_set_userdata(entry
, clientData
[i
]); 
 645         gtk_list_store_insert_before(m_liststore
, &itercur
, pIter
); 
 647 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
 650             gtk_list_store_set(m_liststore
, &itercur
, 
 651                                  0, FALSE
, //FALSE == not toggled 
 656             gtk_list_store_set(m_liststore
, &itercur
, 
 659         g_object_unref(G_OBJECT(entry
)); //liststore always refs :) 
 663 void wxListBox::DoInsertItems(const wxArrayString
& items
, unsigned int pos
) 
 665     wxCHECK_RET( IsValidInsert(pos
), wxT("invalid index in wxListBox::InsertItems") ); 
 667     GtkInsertItems(items
, NULL
, pos
); 
 670 int wxListBox::DoAppend( const wxString
& item 
) 
 672     // Call DoInsertItems 
 673     unsigned int nWhere 
= wxListBox::GetCount(); 
 674     wxArrayString aItems
; 
 676     wxListBox::DoInsertItems(aItems
, nWhere
); 
 680 void wxListBox::DoSetItems( const wxArrayString
& items
, 
 684     GtkInsertItems(items
, clientData
, 0); 
 687 // ---------------------------------------------------------------------------- 
 689 // ---------------------------------------------------------------------------- 
 691 void wxListBox::Clear() 
 693     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 695     InvalidateBestSize(); 
 697     gtk_list_store_clear( m_liststore 
); /* well, THAT was easy :) */ 
 700 void wxListBox::Delete(unsigned int n
) 
 702     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 704     InvalidateBestSize(); 
 707     gboolean res 
= gtk_tree_model_iter_nth_child( 
 708                         GTK_TREE_MODEL(m_liststore
), 
 709                         &iter
, NULL
, //NULL = parent = get first 
 713     wxCHECK_RET( res
, wxT("wrong listbox index") ); 
 715     //this returns false if iter is invalid (i.e. deleting item 
 716     //at end) but since we don't use iter, well... :) 
 717     gtk_list_store_remove(m_liststore
, &iter
); 
 720 // ---------------------------------------------------------------------------- 
 721 // get GtkTreeEntry from position (note: you need to g_unref it if valid) 
 722 // ---------------------------------------------------------------------------- 
 724 struct _GtkTreeEntry
* wxListBox::GtkGetEntry(int n
) const 
 727     gboolean res 
= gtk_tree_model_iter_nth_child( 
 728                         GTK_TREE_MODEL(m_liststore
), 
 729                         &iter
, NULL
, //NULL = parent = get first 
 734         wxLogDebug(wxT("gtk_tree_model_iter_nth_child failed\n") 
 735                    wxT("Passed in value was:[%i]  List size:[%u]"), 
 736                    n
, wxListBox::GetCount() ); 
 741     GtkTreeEntry
* entry 
= NULL
; 
 742     gtk_tree_model_get(GTK_TREE_MODEL(m_liststore
), &iter
, 
 743                        WXLISTBOX_DATACOLUMN
, &entry
, -1); 
 748 // ---------------------------------------------------------------------------- 
 750 // ---------------------------------------------------------------------------- 
 752 void* wxListBox::DoGetItemClientData(unsigned int n
) const 
 754     wxCHECK_MSG( IsValid(n
), NULL
, 
 755                  wxT("Invalid index passed to GetItemClientData") ); 
 757     GtkTreeEntry
* entry 
= GtkGetEntry(n
); 
 758     wxCHECK_MSG(entry
, NULL
, wxT("could not get entry")); 
 760     void* userdata 
= gtk_tree_entry_get_userdata( entry 
); 
 761     g_object_unref(G_OBJECT(entry
)); 
 765 wxClientData
* wxListBox::DoGetItemClientObject(unsigned int n
) const 
 767     return (wxClientData
*) wxListBox::DoGetItemClientData(n
); 
 770 void wxListBox::DoSetItemClientData(unsigned int n
, void* clientData
) 
 772     wxCHECK_RET( IsValid(n
), 
 773                  wxT("Invalid index passed to SetItemClientData") ); 
 775     GtkTreeEntry
* entry 
= GtkGetEntry(n
); 
 776     wxCHECK_RET(entry
, wxT("could not get entry")); 
 778     gtk_tree_entry_set_userdata( entry
, clientData 
); 
 779     g_object_unref(G_OBJECT(entry
)); 
 782 void wxListBox::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
) 
 784     // wxItemContainer already deletes data for us 
 785     wxListBox::DoSetItemClientData(n
, (void*) clientData
); 
 788 // ---------------------------------------------------------------------------- 
 789 // string list access 
 790 // ---------------------------------------------------------------------------- 
 792 void wxListBox::SetString(unsigned int n
, const wxString 
&string
) 
 794     wxCHECK_RET( IsValid(n
), wxT("invalid index in wxListBox::SetString") ); 
 795     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 797     GtkTreeEntry
* entry 
= GtkGetEntry(n
); 
 798     wxCHECK_RET( entry
, wxT("wrong listbox index") ); 
 800     wxString label 
= string
; 
 802 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 804         label
.Prepend(wxCHECKLBOX_STRING
); 
 805 #endif // wxUSE_CHECKLISTBOX 
 807     // RN: This may look wierd but the problem is that the TreeView 
 808     // doesn't resort or update when changed above and there is no real 
 809     // notification function... 
 810     void* userdata 
= gtk_tree_entry_get_userdata(entry
); 
 811     gtk_tree_entry_set_userdata(entry
, NULL
); //don't delete on destroy 
 812     g_object_unref(G_OBJECT(entry
)); 
 814     bool bWasSelected 
= wxListBox::IsSelected(n
); 
 815     wxListBox::Delete(n
); 
 817     wxArrayString aItems
; 
 819     GtkInsertItems(aItems
, &userdata
, n
); 
 821         wxListBox::GtkSetSelection(n
, true, true); 
 824 wxString 
wxListBox::GetString(unsigned int n
) const 
 826     wxCHECK_MSG( m_treeview 
!= NULL
, wxEmptyString
, wxT("invalid listbox") ); 
 828     GtkTreeEntry
* entry 
= GtkGetEntry(n
); 
 829     wxCHECK_MSG( entry
, wxEmptyString
, wxT("wrong listbox index") ); 
 831     wxString label 
= wxGTK_CONV_BACK( gtk_tree_entry_get_label(entry
) ); 
 833 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 834     // checklistboxes have "[±] " prepended to their lables, remove it 
 836     // NB: 4 below is the length of wxCHECKLBOX_STRING from wx/gtk/checklst.h 
 837     if ( m_hasCheckBoxes 
) 
 839 #endif // wxUSE_CHECKLISTBOX 
 841     g_object_unref(G_OBJECT(entry
)); 
 845 unsigned int wxListBox::GetCount() const 
 847     wxCHECK_MSG( m_treeview 
!= NULL
, 0, wxT("invalid listbox") ); 
 849     return (unsigned int)gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_liststore
), NULL
); 
 852 int wxListBox::FindString( const wxString 
&item
, bool bCase 
) const 
 854     wxCHECK_MSG( m_treeview 
!= NULL
, wxNOT_FOUND
, wxT("invalid listbox") ); 
 856     //Sort of hackish - maybe there is a faster way 
 857     unsigned int nCount 
= wxListBox::GetCount(); 
 859     for(unsigned int i 
= 0; i 
< nCount
; ++i
) 
 861         if( item
.IsSameAs( wxListBox::GetString(i
), bCase 
) ) 
 866     // it's not an error if the string is not found -> no wxCHECK 
 870 // ---------------------------------------------------------------------------- 
 872 // ---------------------------------------------------------------------------- 
 874 int wxListBox::GetSelection() const 
 876     wxCHECK_MSG( m_treeview 
!= NULL
, -1, wxT("invalid listbox")); 
 877     wxCHECK_MSG( HasFlag(wxLB_SINGLE
), -1, 
 878                     wxT("must be single selection listbox")); 
 881     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 883     // only works on single-sel 
 884     if (!gtk_tree_selection_get_selected(selection
, NULL
, &iter
)) 
 888         gtk_tree_model_get_path(GTK_TREE_MODEL(m_liststore
), &iter
); 
 890     int sel 
= gtk_tree_path_get_indices(path
)[0]; 
 892     gtk_tree_path_free(path
); 
 897 int wxListBox::GetSelections( wxArrayInt
& aSelections 
) const 
 899     wxCHECK_MSG( m_treeview 
!= NULL
, -1, wxT("invalid listbox") ); 
 905     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 907     if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(m_liststore
), &iter
)) 
 908     { //gtk_tree_selection_get_selected_rows is GTK 2.2+ so iter instead 
 911             if (gtk_tree_selection_iter_is_selected(selection
, &iter
)) 
 915         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(m_liststore
), &iter
)); 
 918     return aSelections
.GetCount(); 
 921 bool wxListBox::IsSelected( int n 
) const 
 923     wxCHECK_MSG( m_treeview 
!= NULL
, false, wxT("invalid listbox") ); 
 925     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 928     gboolean res 
= gtk_tree_model_iter_nth_child( 
 929                         GTK_TREE_MODEL(m_liststore
), 
 930                         &iter
, NULL
, //NULL = parent = get first 
 933     wxCHECK_MSG( res
, false, wxT("Invalid index") ); 
 935     return gtk_tree_selection_iter_is_selected(selection
, &iter
); 
 938 void wxListBox::DoSetSelection( int n
, bool select 
) 
 940     return GtkSetSelection(n
, select
, true); //docs say no events here 
 943 void wxListBox::GtkSetSelection(int n
, const bool select
, const bool blockEvent
) 
 945     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 947     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 950     gboolean res 
= gtk_tree_model_iter_nth_child( 
 951                         GTK_TREE_MODEL(m_liststore
), 
 952                         &iter
, NULL
, //NULL = parent = get first 
 955     wxCHECK_RET( res
, wxT("Invalid index") ); 
 957     m_blockEvent 
= blockEvent
; 
 960         gtk_tree_selection_select_iter(selection
, &iter
); 
 962         gtk_tree_selection_unselect_iter(selection
, &iter
); 
 964     m_blockEvent 
= false; 
 967 void wxListBox::DoSetFirstItem( int n 
) 
 969     wxCHECK_RET( m_treeview
, wxT("invalid listbox") ); 
 970     wxCHECK_RET( IsValid(n
), wxT("invalid index")); 
 972     //RN: I have no idea why this line is needed... 
 973     if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_treeview
)) 
 977     gtk_tree_model_iter_nth_child( 
 978                                     GTK_TREE_MODEL(m_liststore
), 
 980                                     NULL
, //NULL = parent = get first 
 984     GtkTreePath
* path 
= gtk_tree_model_get_path( 
 985                             GTK_TREE_MODEL(m_liststore
), &iter
); 
 987     // Scroll to the desired cell (0.0 == topleft alignment) 
 988     gtk_tree_view_scroll_to_cell(m_treeview
, path
, NULL
, 
 991     gtk_tree_path_free(path
); 
 994 // ---------------------------------------------------------------------------- 
 996 // ---------------------------------------------------------------------------- 
 998 int wxListBox::DoListHitTest(const wxPoint
& point
) const 
1000     // need to translate from master window since it is in client coords 
1002     gdk_window_get_geometry(gtk_tree_view_get_bin_window(m_treeview
), 
1003                             &binx
, &biny
, NULL
, NULL
, NULL
); 
1006     if ( !gtk_tree_view_get_path_at_pos
 
1012             NULL
,   // [out] column (always 0 here) 
1013             NULL
,   // [out] x-coord relative to the cell (not interested) 
1014             NULL    
// [out] y-coord relative to the cell 
1020     int index 
= gtk_tree_path_get_indices(path
)[0]; 
1021     gtk_tree_path_free(path
); 
1026 // ---------------------------------------------------------------------------- 
1028 // ---------------------------------------------------------------------------- 
1031 void wxListBox::ApplyToolTip( GtkTooltips 
*tips
, const wxChar 
*tip 
) 
1033     // RN: Is this needed anymore? 
1034     gtk_tooltips_set_tip( tips
, GTK_WIDGET( m_treeview 
), wxGTK_CONV(tip
), (gchar
*) NULL 
); 
1036 #endif // wxUSE_TOOLTIPS 
1038 GtkWidget 
*wxListBox::GetConnectWidget() 
1040     // the correct widget for listbox events (such as mouse clicks for example) 
1041     // is m_treeview, not the parent scrolled window 
1042     return GTK_WIDGET(m_treeview
); 
1045 bool wxListBox::IsOwnGtkWindow( GdkWindow 
*window 
) 
1047     return (window 
== gtk_tree_view_get_bin_window(m_treeview
)); 
1050 void wxListBox::DoApplyWidgetStyle(GtkRcStyle 
*style
) 
1052     if (m_hasBgCol 
&& m_backgroundColour
.Ok()) 
1054         GdkWindow 
*window 
= gtk_tree_view_get_bin_window(m_treeview
); 
1057             m_backgroundColour
.CalcPixel( gdk_window_get_colormap( window 
) ); 
1058             gdk_window_set_background( window
, m_backgroundColour
.GetColor() ); 
1059             gdk_window_clear( window 
); 
1063     gtk_widget_modify_style( GTK_WIDGET(m_treeview
), style 
); 
1066 void wxListBox::OnInternalIdle() 
1068     //RN: Is this needed anymore? 
1069     wxCursor cursor 
= m_cursor
; 
1070     if (g_globalCursor
.Ok()) cursor 
= g_globalCursor
; 
1072     if (GTK_WIDGET(m_treeview
)->window 
&& cursor
.Ok()) 
1074         /* I now set the cursor the anew in every OnInternalIdle call 
1075            as setting the cursor in a parent window also effects the 
1076            windows above so that checking for the current cursor is 
1078         GdkWindow 
*window 
= gtk_tree_view_get_bin_window(m_treeview
); 
1079         gdk_window_set_cursor( window
, cursor
.GetCursor() ); 
1082     if (wxUpdateUIEvent::CanUpdate(this)) 
1083         UpdateWindowUI(wxUPDATE_UI_FROMIDLE
); 
1086 wxSize 
wxListBox::DoGetBestSize() const 
1088     wxCHECK_MSG(m_treeview
, wxDefaultSize
, wxT("invalid tree view")); 
1090     // Start with a minimum size that's not too small 
1092     GetTextExtent( wxT("X"), &cx
, &cy
); 
1093     int lbWidth 
= 3 * cx
; 
1096     // Get the visible area of the tree view (limit to the 10th item 
1097     // so that it isn't too big) 
1098     unsigned int count 
= GetCount(); 
1103         // Find the widest line 
1104         for(unsigned int i 
= 0; i 
< count
; i
++) { 
1105             wxString 
str(GetString(i
)); 
1106             GetTextExtent(str
, &wLine
, NULL
); 
1107             lbWidth 
= wxMax(lbWidth
, wLine
); 
1112         // And just a bit more for the checkbox if present and then some 
1113         // (these are rough guesses) 
1114 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
1115         if ( m_hasCheckBoxes 
) 
1118             cy 
= cy 
> 25 ? cy 
: 25; // rough height of checkbox 
1122         // don't make the listbox too tall (limit height to around 10 items) but don't 
1123         // make it too small neither 
1124         lbHeight 
= (cy
+4) * wxMin(wxMax(count
, 3), 10); 
1127     // Add room for the scrollbar 
1128     lbWidth 
+= wxSystemSettings::GetMetric(wxSYS_VSCROLL_X
); 
1130     wxSize 
best(lbWidth
, lbHeight
); 
1131     CacheBestSize(best
); 
1137 wxListBox::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
1139     return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new
, true); 
1142 #endif // wxUSE_LISTBOX