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" 
  18 #include "wx/listbox.h" 
  19 #include "wx/dynarray.h" 
  20 #include "wx/arrstr.h" 
  23 #include "wx/checklst.h" 
  24 #include "wx/settings.h" 
  26 #include "wx/gtk/private.h" 
  27 #include "wx/gtk/treeentry_gtk.h" 
  30 #include "wx/tooltip.h" 
  35 #include <gdk/gdkkeysyms.h> 
  37 //----------------------------------------------------------------------------- 
  39 //----------------------------------------------------------------------------- 
  41 extern bool           g_blockEventsOnDrag
; 
  42 extern bool           g_blockEventsOnScroll
; 
  43 extern wxCursor       g_globalCursor
; 
  46 //----------------------------------------------------------------------------- 
  48 //----------------------------------------------------------------------------- 
  50 extern void wxapp_install_idle_handler(); 
  53 //----------------------------------------------------------------------------- 
  54 // Macro to tell which row the strings are in (1 if native checklist, 0 if not) 
  55 //----------------------------------------------------------------------------- 
  57 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
  58 #   define WXLISTBOX_DATACOLUMN_ARG(x)  (x->m_hasCheckBoxes ? 1 : 0) 
  60 #   define WXLISTBOX_DATACOLUMN_ARG(x)  (0) 
  61 #endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
  63 #define WXLISTBOX_DATACOLUMN    WXLISTBOX_DATACOLUMN_ARG(this) 
  65 //----------------------------------------------------------------------------- 
  67 //----------------------------------------------------------------------------- 
  71 gtk_listbox_row_activated_callback(GtkTreeView        
*treeview
, 
  73                                    GtkTreeViewColumn  
*col
, 
  76     if (g_isIdle
) wxapp_install_idle_handler(); 
  78     if (g_blockEventsOnDrag
) return; 
  79     if (g_blockEventsOnScroll
) return; 
  81     if (!listbox
->m_hasVMT
) return; 
  84     //1) This is triggered by either a double-click or a space press 
  85     //2) We handle both here because 
  86     //2a) in the case of a space/keypress we can't really know 
  87     //    which item was pressed on because we can't get coords 
  89     //2b) It seems more correct 
  91     int sel 
= gtk_tree_path_get_indices(path
)[0]; 
  93     if(!listbox
->m_spacePressed
) 
  95         //Assume it was double-click 
  96         wxCommandEvent 
event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
, listbox
->GetId() ); 
  97         event
.SetEventObject( listbox 
); 
  99         if(listbox
->IsSelected(sel
)) 
 101             GtkTreeEntry
* entry 
= listbox
->GtkGetEntry(sel
); 
 106                 event
.SetString(wxConvUTF8
.cMB2WX(gtk_tree_entry_get_label(entry
))); 
 108                 if ( listbox
->HasClientObjectData() ) 
 109                     event
.SetClientObject(  
 110                     (wxClientData
*) gtk_tree_entry_get_userdata(entry
) ); 
 111                 else if ( listbox
->HasClientUntypedData() ) 
 112                     event
.SetClientData( gtk_tree_entry_get_userdata(entry
) ); 
 113                 g_object_unref(G_OBJECT(entry
)); 
 117                 wxLogSysError(wxT("Internal error - could not get entry for double-click")); 
 124         listbox
->GetEventHandler()->ProcessEvent( event 
); 
 128         listbox
->m_spacePressed 
= false; //don't block selection behaviour anymore 
 130         //Space was pressed - toggle the appropriate checkbox and the selection 
 131 #if wxUSE_CHECKLISTBOX //Do it for both native and non-native 
 132         if (listbox
->m_hasCheckBoxes
) 
 134             wxCheckListBox 
*clb 
= (wxCheckListBox 
*)listbox
; 
 136             clb
->Check( sel
, !clb
->IsChecked(sel
) ); 
 138             wxCommandEvent 
new_event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, listbox
->GetId() ); 
 139             new_event
.SetEventObject( listbox 
); 
 140             new_event
.SetInt( sel 
); 
 141             listbox
->GetEventHandler()->ProcessEvent( new_event 
); 
 143 #endif // wxUSE_CHECKLISTBOX 
 145         if(  (((listbox
->GetWindowStyleFlag() & wxLB_MULTIPLE
) != 0) || 
 146               ((listbox
->GetWindowStyleFlag() & wxLB_EXTENDED
) != 0)) ) 
 148             //toggle the selection + send event 
 149             if(listbox
->IsSelected( sel 
)) 
 150                 listbox
->GtkSetSelection(sel
, FALSE
, FALSE
); 
 152                 listbox
->GtkSetSelection(sel
, TRUE
, FALSE
); 
 158 //----------------------------------------------------------------------------- 
 159 // "button_press_event" 
 160 //----------------------------------------------------------------------------- 
 164 gtk_listbox_button_press_callback( GtkWidget 
*widget
, 
 165                                    GdkEventButton 
*gdk_event
, 
 168     if (g_isIdle
) wxapp_install_idle_handler(); 
 170     if (g_blockEventsOnDrag
) return FALSE
; 
 171     if (g_blockEventsOnScroll
) return FALSE
; 
 173     if (!listbox
->m_hasVMT
) return FALSE
; 
 175     //Just to be on the safe side - it should be unset in the activate callback 
 176     //but we don't want any obscure bugs if it doesn't get called somehow... 
 177     listbox
->m_spacePressed 
= false; 
 179 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 180     if ((listbox
->m_hasCheckBoxes
) && (gdk_event
->x 
< 15) && (gdk_event
->type 
!= GDK_2BUTTON_PRESS
)) 
 183         gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
),  
 184                                   (gint
)gdk_event
->x
, (gint
)gdk_event
->y
, 
 185                                   &path
, NULL
, NULL
, NULL
); 
 186         int sel 
= gtk_tree_path_get_indices(path
)[0]; 
 187         gtk_tree_path_free(path
); 
 189         wxCheckListBox 
*clb 
= (wxCheckListBox 
*)listbox
; 
 191         clb
->Check( sel
, !clb
->IsChecked(sel
) ); 
 193         wxCommandEvent 
event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, listbox
->GetId() ); 
 194         event
.SetEventObject( listbox 
); 
 196         listbox
->GetEventHandler()->ProcessEvent( event 
); 
 198 #endif // wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 204 //----------------------------------------------------------------------------- 
 206 //----------------------------------------------------------------------------- 
 210 gtk_listbox_key_press_callback( GtkWidget 
*widget
,  
 211                                 GdkEventKey 
*gdk_event
,  
 214     if (g_isIdle
) wxapp_install_idle_handler(); 
 216     if (g_blockEventsOnDrag
) return FALSE
; 
 221     if ((gdk_event
->keyval 
== GDK_Tab
) || (gdk_event
->keyval 
== GDK_ISO_Left_Tab
)) 
 223         wxNavigationKeyEvent new_event
; 
 224         /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */ 
 225         new_event
.SetDirection( (gdk_event
->keyval 
== GDK_Tab
) ); 
 226         /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */ 
 227         new_event
.SetWindowChange( (gdk_event
->state 
& GDK_CONTROL_MASK
) ); 
 228         new_event
.SetCurrentFocus( listbox 
); 
 229         ret 
= listbox
->GetEventHandler()->ProcessEvent( new_event 
); 
 232     if ((gdk_event
->keyval 
== GDK_Return
) && (!ret
)) 
 234         // eat return in all modes (RN:WHY?) 
 238     // Check or uncheck item with SPACE 
 239     if (gdk_event
->keyval 
== ' ') 
 241         //In the keyevent we don't know the index of the item 
 242         //and the activated event gets called anyway... 
 244         //Also, activating with the space causes the treeview to  
 245         //unselect all the items and then select the item in question 
 246         //wx's behaviour is to just toggle the item's selection state 
 247         //and leave the others alone 
 248         listbox
->m_spacePressed 
= true; 
 253         g_signal_stop_emission_by_name (widget
, "key_press_event"); 
 261 //----------------------------------------------------------------------------- 
 262 // "select" and "deselect" 
 263 //----------------------------------------------------------------------------- 
 266 static gboolean 
gtk_listitem_select_cb( GtkTreeSelection
* selection
, 
 269                                         gboolean is_selected
, 
 272     if (g_isIdle
) wxapp_install_idle_handler(); 
 274     if (!listbox
->m_hasVMT
) return TRUE
; 
 275     if (g_blockEventsOnDrag
) return TRUE
; 
 277     if (listbox
->m_spacePressed
) return FALSE
; //see keyevent callback 
 278     if (listbox
->m_blockEvent
) return TRUE
; 
 280     // NB: wxdocs explicitly say that this event only gets sent when 
 281     // something is actually selected, plus the controls example 
 282     // assumes so and passes -1 to the dogetclientdata funcs if not 
 284     // OK, so basically we need to do a bit of a run-around here as 
 285     // 1) is_selected says whether the item(s?) are CURRENTLY selected -     
 286     //    i.e. if is_selected is FALSE then the item is going to be  
 287     //    selected right now! 
 288     // 2) However, since it is not already selected and the user  
 289     //    will expect it to be we need to manually select it and 
 290     //    return FALSE telling GTK we handled the selection 
 291     if (is_selected
) return TRUE
; 
 293     int nIndex 
= gtk_tree_path_get_indices(path
)[0]; 
 294     GtkTreeEntry
* entry 
= listbox
->GtkGetEntry(nIndex
); 
 298         //Now, as mentioned above, we manually select the row that is/was going 
 299         //to be selected anyway by GTK 
 300         listbox
->m_blockEvent 
= TRUE
; //if we don't block events we will lock the 
 301                                       //app due to recursion!! 
 303         GtkTreeSelection
* selection 
=  
 304             gtk_tree_view_get_selection(listbox
->m_treeview
); 
 306         gtk_tree_model_get_iter(GTK_TREE_MODEL(listbox
->m_liststore
), &iter
, path
); 
 307         gtk_tree_selection_select_iter(selection
, &iter
); 
 309         listbox
->m_blockEvent 
= FALSE
; 
 311         //Finally, send the wx event 
 312     wxCommandEvent 
event(wxEVT_COMMAND_LISTBOX_SELECTED
, listbox
->GetId() ); 
 313     event
.SetEventObject( listbox 
); 
 315     // indicate whether this is a selection or a deselection 
 316         event
.SetExtraLong( 1 ); 
 318         event
.SetInt(nIndex
); 
 319         event
.SetString(wxConvUTF8
.cMB2WX(gtk_tree_entry_get_label(entry
))); 
 321         if ( listbox
->HasClientObjectData() ) 
 322             event
.SetClientObject(  
 323                     (wxClientData
*) gtk_tree_entry_get_userdata(entry
)  
 325         else if ( listbox
->HasClientUntypedData() ) 
 326             event
.SetClientData( gtk_tree_entry_get_userdata(entry
) ); 
 328     listbox
->GetEventHandler()->ProcessEvent( event 
); 
 330         g_object_unref(G_OBJECT(entry
)); 
 331         return FALSE
;  //We handled it/did it manually 
 334     return TRUE
; //allow selection to change 
 338 //----------------------------------------------------------------------------- 
 339 // GtkTreeEntry destruction (to destroy client data) 
 340 //----------------------------------------------------------------------------- 
 343 static void gtk_tree_entry_destroy_cb(GtkTreeEntry
* entry
,  
 346     if(listbox
->HasClientObjectData()) 
 348         gpointer userdata 
= gtk_tree_entry_get_userdata(entry
); 
 350             delete (wxClientData 
*)userdata
; 
 355 //----------------------------------------------------------------------------- 
 356 // Sorting callback (standard CmpNoCase return value) 
 357 //----------------------------------------------------------------------------- 
 360 static gint 
gtk_listbox_sort_callback(GtkTreeModel 
*model
, 
 366     GtkTreeEntry
* entry2
; 
 368     gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
), 
 370                              WXLISTBOX_DATACOLUMN_ARG(listbox
),  
 372     gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
), 
 374                              WXLISTBOX_DATACOLUMN_ARG(listbox
),  
 376     wxCHECK_MSG(entry
, 0, wxT("Could not get entry")); 
 377     wxCHECK_MSG(entry2
, 0, wxT("Could not get entry2")); 
 379     //We compare collate keys here instead of calling g_utf8_collate 
 380     //as it is rather slow (and even the docs reccommend this) 
 381     int ret 
= strcasecmp(gtk_tree_entry_get_collate_key(entry
), 
 382                          gtk_tree_entry_get_collate_key(entry2
)); 
 384     g_object_unref(G_OBJECT(entry
)); 
 385     g_object_unref(G_OBJECT(entry2
)); 
 391 //----------------------------------------------------------------------------- 
 392 // Searching callback (TRUE == not equal, FALSE == equal) 
 393 //----------------------------------------------------------------------------- 
 396 static gboolean 
gtk_listbox_searchequal_callback(GtkTreeModel
* model
, 
 404     gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
), 
 406                              WXLISTBOX_DATACOLUMN_ARG(listbox
),  
 408     wxCHECK_MSG(entry
, 0, wxT("Could not get entry")); 
 409     gchar
* keycollatekey 
= g_utf8_collate_key(key
, -1); 
 411     int ret 
= strcasecmp(keycollatekey
, 
 412                          gtk_tree_entry_get_collate_key(entry
)); 
 414     g_free(keycollatekey
); 
 415     g_object_unref(G_OBJECT(entry
)); 
 421 //----------------------------------------------------------------------------- 
 423 //----------------------------------------------------------------------------- 
 425 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
) 
 427 // ---------------------------------------------------------------------------- 
 429 // ---------------------------------------------------------------------------- 
 431 wxListBox::wxListBox() 
 433     m_treeview 
= (GtkTreeView
*) NULL
; 
 434 #if wxUSE_CHECKLISTBOX 
 435     m_hasCheckBoxes 
= FALSE
; 
 436 #endif // wxUSE_CHECKLISTBOX 
 437     m_spacePressed 
= false; 
 440 bool wxListBox::Create( wxWindow 
*parent
, wxWindowID id
, 
 441                         const wxPoint 
&pos
, const wxSize 
&size
, 
 442                         const wxArrayString
& choices
, 
 443                         long style
, const wxValidator
& validator
, 
 444                         const wxString 
&name 
) 
 446     wxCArrayString 
chs(choices
); 
 448     return Create( parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 449                    style
, validator
, name 
); 
 452 bool wxListBox::Create( wxWindow 
*parent
, wxWindowID id
, 
 453                         const wxPoint 
&pos
, const wxSize 
&size
, 
 454                         int n
, const wxString choices
[], 
 455                         long style
, const wxValidator
& validator
, 
 456                         const wxString 
&name 
) 
 459     m_acceptsFocus 
= TRUE
; 
 460     m_blockEvent 
= FALSE
; 
 462     if (!PreCreation( parent
, pos
, size 
) || 
 463         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
 465         wxFAIL_MSG( wxT("wxListBox creation failed") ); 
 469     m_widget 
= gtk_scrolled_window_new( (GtkAdjustment
*) NULL
, (GtkAdjustment
*) NULL 
); 
 470     if (style 
& wxLB_ALWAYS_SB
) 
 472       gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget
), 
 473         GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS 
); 
 477       gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget
), 
 478         GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC 
); 
 481     m_treeview 
= GTK_TREE_VIEW( gtk_tree_view_new( ) ); 
 483     //wxListBox doesn't have a header :) 
 484     //NB: If enabled SetFirstItem doesn't work correctly 
 485     gtk_tree_view_set_headers_visible(m_treeview
, FALSE
); 
 487 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
 489         ((wxCheckListBox
*)this)->DoCreateCheckList(); 
 490 #endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
 492     // Create the data column 
 493     gtk_tree_view_insert_column_with_attributes(m_treeview
, -1, "",  
 494                                                 gtk_cell_renderer_text_new(), 
 496                                                 WXLISTBOX_DATACOLUMN
, NULL
); 
 498     // Now create+set the model (GtkListStore) - first argument # of columns 
 500 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
 502         m_liststore 
= gtk_list_store_new(2, G_TYPE_BOOLEAN
, 
 503                                             GTK_TYPE_TREE_ENTRY
); 
 506         m_liststore 
= gtk_list_store_new(1, GTK_TYPE_TREE_ENTRY
); 
 508     gtk_tree_view_set_model(m_treeview
, GTK_TREE_MODEL(m_liststore
)); 
 510     g_object_unref(G_OBJECT(m_liststore
)); //free on treeview destruction 
 512     // Disable the pop-up textctrl that enables searching - note that 
 513     // the docs specify that even if this disabled (which we are doing) 
 514     // the user can still have it through the start-interactive-search 
 515     // key binding...either way we want to provide a searchequal callback 
 516     // NB: If this is enabled a doubleclick event (activate) gets sent  
 517     //     on a successful search 
 518     gtk_tree_view_set_search_column(m_treeview
, WXLISTBOX_DATACOLUMN
); 
 519     gtk_tree_view_set_search_equal_func(m_treeview
,  
 520        (GtkTreeViewSearchEqualFunc
) gtk_listbox_searchequal_callback
, 
 524     gtk_tree_view_set_enable_search(m_treeview
, FALSE
); 
 527     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection( m_treeview 
); 
 528     gtk_tree_selection_set_select_function(selection
,  
 529                      (GtkTreeSelectionFunc
)gtk_listitem_select_cb
, 
 530                                            this, NULL
); //NULL == destroycb          
 532     GtkSelectionMode mode
; 
 533     if (style 
& wxLB_MULTIPLE
) 
 535         mode 
= GTK_SELECTION_MULTIPLE
; 
 537     else if (style 
& wxLB_EXTENDED
) 
 539         mode 
= GTK_SELECTION_EXTENDED
; 
 543         // if style was 0 set single mode 
 544         m_windowStyle 
|= wxLB_SINGLE
; 
 545         mode 
= GTK_SELECTION_SINGLE
; 
 548     gtk_tree_selection_set_mode( selection
, mode 
); 
 550     //Handle sortable stuff  
 551     if(style 
& wxLB_SORT
) 
 553         //Setup sorting in ascending (wx) order 
 554         gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(m_liststore
),  
 555                                              WXLISTBOX_DATACOLUMN
,  
 558         //Set the sort callback 
 559         gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(m_liststore
), 
 560                                         WXLISTBOX_DATACOLUMN
, 
 561                    (GtkTreeIterCompareFunc
) gtk_listbox_sort_callback
, 
 563                                         NULL 
//"destroy notifier" 
 568     gtk_container_add (GTK_CONTAINER (m_widget
), GTK_WIDGET(m_treeview
) ); 
 570     gtk_widget_show( GTK_WIDGET(m_treeview
) ); 
 572     wxListBox::DoInsertItems(wxArrayString(n
, choices
), 0); // insert initial items 
 574     //treeview-specific events 
 575     g_signal_connect(m_treeview
, "row-activated", 
 576                      G_CALLBACK(gtk_listbox_row_activated_callback
), this); 
 579     g_signal_connect (m_treeview
, "button_press_event", 
 580                       G_CALLBACK (gtk_listbox_button_press_callback
), 
 582     g_signal_connect (m_treeview
, "key_press_event", 
 583                       G_CALLBACK (gtk_listbox_key_press_callback
), 
 586     m_parent
->DoAddChild( this ); 
 589     SetBestSize(size
); // need this too because this is a wxControlWithItems 
 594 wxListBox::~wxListBox() 
 601 // ---------------------------------------------------------------------------- 
 603 // ---------------------------------------------------------------------------- 
 605 void wxListBox::GtkInsertItems(const wxArrayString
& items
,  
 606                         void** clientData
, int pos
) 
 608     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 610     InvalidateBestSize(); 
 612     // Create and set column ids and GValues 
 614     size_t nNum 
= items
.GetCount(); 
 615     int nCurCount 
= wxListBox::GetCount(); 
 616     wxASSERT_MSG(pos 
<= nCurCount
, wxT("Invalid index passed to wxListBox")); 
 618     GtkTreeIter
* pIter 
= NULL
; // append by default 
 620     if (pos 
!= nCurCount
) 
 622         gboolean res 
= gtk_tree_model_iter_nth_child( 
 623                         GTK_TREE_MODEL(m_liststore
), 
 624                         &iter
, NULL
, //NULL = parent = get first 
 628             wxLogSysError(wxT("internal wxListBox error in insertion")); 
 635     for (size_t i 
= 0; i 
< nNum
; ++i
) 
 637         wxString label 
= items
[i
]; 
 639 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 642         label
.Prepend(wxCHECKLBOX_STRING
); 
 644 #endif // wxUSE_CHECKLISTBOX 
 647         GtkTreeEntry
* entry 
= gtk_tree_entry_new(); 
 648         gtk_tree_entry_set_label(entry
, wxConvUTF8
.cWX2MB(label
)); 
 649         gtk_tree_entry_set_destroy_func(entry
,  
 650                 (GtkTreeEntryDestroy
)gtk_tree_entry_destroy_cb
, 
 654             gtk_tree_entry_set_userdata(entry
, clientData
[i
]); 
 657         gtk_list_store_insert_before(m_liststore
, &itercur
, pIter
); 
 659 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
 662             gtk_list_store_set(m_liststore
, &itercur
,  
 663                                  0, FALSE
, //FALSE == not toggled 
 668             gtk_list_store_set(m_liststore
, &itercur
,  
 671         g_object_unref(G_OBJECT(entry
)); //liststore always refs :) 
 675 void wxListBox::DoInsertItems(const wxArrayString
& items
, int pos
) 
 677     GtkInsertItems(items
, NULL
, pos
); 
 680 int wxListBox::DoAppend( const wxString
& item 
) 
 682     InvalidateBestSize(); 
 684     //Just call DoInsertItems for now 
 685     //RN: Originally I had gtk_list_store_append etc. 
 686     //    here as an optimization but now the insert 
 687     //    has been streamlined and its quite a bit of code duplication 
 688     int nWhere 
= wxListBox::GetCount(); 
 689     wxArrayString aItems
; 
 691     wxListBox::DoInsertItems(aItems
, nWhere
); 
 695 void wxListBox::DoSetItems( const wxArrayString
& items
, 
 699     GtkInsertItems(items
, clientData
, 0); 
 702 // ---------------------------------------------------------------------------- 
 704 // ---------------------------------------------------------------------------- 
 706 void wxListBox::Clear() 
 708     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 710     gtk_list_store_clear( m_liststore 
); /* well, THAT was easy :) */ 
 713 void wxListBox::Delete( int n 
) 
 715     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 718     gboolean res 
= gtk_tree_model_iter_nth_child( 
 719                         GTK_TREE_MODEL(m_liststore
), 
 720                         &iter
, NULL
, //NULL = parent = get first 
 724     wxCHECK_RET( res
, wxT("wrong listbox index") ); 
 726     //this returns false if iter is invalid (i.e. deleting item 
 727     //at end) but since we don't use iter, well... :) 
 728     gtk_list_store_remove(m_liststore
, &iter
); 
 731 // ---------------------------------------------------------------------------- 
 732 // get GtkTreeEntry from position (note: you need to g_unref it if valid) 
 733 // ---------------------------------------------------------------------------- 
 735 struct _GtkTreeEntry
* wxListBox::GtkGetEntry(int n
) const 
 738     gboolean res 
= gtk_tree_model_iter_nth_child( 
 739                         GTK_TREE_MODEL(m_liststore
), 
 740                         &iter
, NULL
, //NULL = parent = get first 
 745         wxLogDebug(wxT("gtk_tree_model_iter_nth_child failed\n") 
 746                    wxT("Passed in value was:[%i]  List size:[%i]"), 
 747                    n
, wxListBox::GetCount() ); 
 752     GtkTreeEntry
* entry 
= NULL
; 
 753     gtk_tree_model_get(GTK_TREE_MODEL(m_liststore
), &iter
, 
 754                        WXLISTBOX_DATACOLUMN
, &entry
, -1); 
 759 // ---------------------------------------------------------------------------- 
 761 // ---------------------------------------------------------------------------- 
 763 void* wxListBox::DoGetItemClientData( int n 
) const 
 765     wxCHECK_MSG( n 
>= 0 && n 
< wxListBox::GetCount(), NULL
,  
 766                  wxT("Invalid index passed to GetItemClientData") ); 
 768     GtkTreeEntry
* entry 
= GtkGetEntry(n
); 
 769     wxCHECK_MSG(entry
, NULL
, wxT("could not get entry")); 
 771     void* userdata 
= gtk_tree_entry_get_userdata( entry 
); 
 772     g_object_unref(G_OBJECT(entry
)); 
 776 wxClientData
* wxListBox::DoGetItemClientObject( int n 
) const 
 778     return (wxClientData
*) wxListBox::DoGetItemClientData(n
); 
 781 void wxListBox::DoSetItemClientData( int n
, void* clientData 
) 
 783     wxCHECK_RET( n 
>= 0 && n 
< wxListBox::GetCount(),  
 784                  wxT("Invalid index passed to SetItemClientData") ); 
 786     GtkTreeEntry
* entry 
= GtkGetEntry(n
); 
 787     wxCHECK_RET(entry
, wxT("could not get entry")); 
 789     gtk_tree_entry_set_userdata( entry
, clientData 
); 
 790     g_object_unref(G_OBJECT(entry
)); 
 793 void wxListBox::DoSetItemClientObject( int n
, wxClientData
* clientData 
) 
 795     // wxItemContainer already deletes data for us 
 796     wxListBox::DoSetItemClientData(n
, (void*) clientData
); 
 799 // ---------------------------------------------------------------------------- 
 800 // string list access 
 801 // ---------------------------------------------------------------------------- 
 803 void wxListBox::SetString( int n
, const wxString 
&string 
) 
 805     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 807     GtkTreeEntry
* entry 
= GtkGetEntry(n
); 
 808     wxCHECK_RET( entry
, wxT("wrong listbox index") ); 
 810     wxString label 
= string
; 
 812 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 814             label
.Prepend(wxCHECKLBOX_STRING
); 
 815 #endif // wxUSE_CHECKLISTBOX 
 817     // RN: This may look wierd but the problem is that the TreeView  
 818     // doesn't resort or update when changed above and there is no real 
 819     // notification function... 
 820     void* userdata 
= gtk_tree_entry_get_userdata(entry
); 
 821     gtk_tree_entry_set_userdata(entry
, NULL
); //don't delete on destroy 
 822     g_object_unref(G_OBJECT(entry
)); 
 824     bool bWasSelected 
= wxListBox::IsSelected(n
); 
 825     wxListBox::Delete(n
); 
 827     wxArrayString aItems
; 
 829     GtkInsertItems(aItems
, &userdata
, n
); 
 831         wxListBox::GtkSetSelection(n
, TRUE
, TRUE
); 
 834 wxString 
wxListBox::GetString( int n 
) const 
 836     wxCHECK_MSG( m_treeview 
!= NULL
, wxEmptyString
, wxT("invalid listbox") ); 
 838     GtkTreeEntry
* entry 
= GtkGetEntry(n
); 
 839     wxCHECK_MSG( entry
, wxEmptyString
, wxT("wrong listbox index") ); 
 841     wxString label 
= wxGTK_CONV_BACK( gtk_tree_entry_get_label(entry
) ); 
 843 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 844     // checklistboxes have "[±] " prepended to their lables, remove it 
 846     // NB: 4 below is the length of wxCHECKLBOX_STRING from wx/gtk/checklst.h 
 847     if ( m_hasCheckBoxes 
) 
 849 #endif // wxUSE_CHECKLISTBOX 
 851     g_object_unref(G_OBJECT(entry
)); 
 855 int wxListBox::GetCount() const 
 857     wxCHECK_MSG( m_treeview 
!= NULL
, -1, wxT("invalid listbox") ); 
 859     return gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_liststore
), NULL
); 
 862 int wxListBox::FindString( const wxString 
&item
, bool bCase 
) const 
 864     wxCHECK_MSG( m_treeview 
!= NULL
, wxNOT_FOUND
, wxT("invalid listbox") ); 
 866     //Sort of hackish - maybe there is a faster way 
 867     int nCount 
= wxListBox::GetCount(); 
 869     for(int i 
= 0; i 
< nCount
; ++i
) 
 871         if( item
.IsSameAs( wxListBox::GetString(i
), bCase 
) ) 
 876     // it's not an error if the string is not found -> no wxCHECK 
 880 // ---------------------------------------------------------------------------- 
 882 // ---------------------------------------------------------------------------- 
 884 int wxListBox::GetSelection() const 
 886     wxCHECK_MSG( m_treeview 
!= NULL
, -1, wxT("invalid listbox")); 
 887     wxCHECK_MSG( HasFlag(wxLB_SINGLE
), -1,  
 888                     wxT("must be single selection listbox")); 
 891     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 893     // only works on single-sel     
 894     if (!gtk_tree_selection_get_selected(selection
, NULL
, &iter
)) 
 898         gtk_tree_model_get_path(GTK_TREE_MODEL(m_liststore
), &iter
); 
 900     int sel 
= gtk_tree_path_get_indices(path
)[0]; 
 902     gtk_tree_path_free(path
); 
 907 int wxListBox::GetSelections( wxArrayInt
& aSelections 
) const 
 909     wxCHECK_MSG( m_treeview 
!= NULL
, -1, wxT("invalid listbox") ); 
 915     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 917     if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(m_liststore
), &iter
)) 
 918     { //gtk_tree_selection_get_selected_rows is GTK 2.2+ so iter instead 
 921             if (gtk_tree_selection_iter_is_selected(selection
, &iter
)) 
 925         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(m_liststore
), &iter
)); 
 928     return aSelections
.GetCount(); 
 931 bool wxListBox::IsSelected( int n 
) const 
 933     wxCHECK_MSG( m_treeview 
!= NULL
, FALSE
, wxT("invalid listbox") ); 
 935     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 938     gboolean res 
= gtk_tree_model_iter_nth_child( 
 939                         GTK_TREE_MODEL(m_liststore
), 
 940                         &iter
, NULL
, //NULL = parent = get first 
 943     wxCHECK_MSG( res
, FALSE
, wxT("Invalid index") ); 
 945     return gtk_tree_selection_iter_is_selected(selection
, &iter
); 
 948 void wxListBox::DoSetSelection( int n
, bool select 
) 
 950     return GtkSetSelection(n
, select
, TRUE
); //docs say no events here 
 953 void wxListBox::GtkSetSelection(int n
, const bool select
, const bool blockEvent
) 
 955     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 957     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 960     gboolean res 
= gtk_tree_model_iter_nth_child( 
 961                         GTK_TREE_MODEL(m_liststore
), 
 962                         &iter
, NULL
, //NULL = parent = get first 
 965     wxCHECK_RET( res
, wxT("Invalid index") ); 
 967     m_blockEvent 
= blockEvent
; 
 970         gtk_tree_selection_select_iter(selection
, &iter
); 
 972         gtk_tree_selection_unselect_iter(selection
, &iter
); 
 974     m_blockEvent 
= FALSE
; 
 977 void wxListBox::DoSetFirstItem( int n 
) 
 979     wxCHECK_RET( m_treeview
, wxT("invalid listbox") ); 
 980     wxCHECK_RET( n 
>= 0 && n 
< wxListBox::GetCount(), wxT("invalid index")); 
 982     //RN: I have no idea why this line is needed... 
 983     if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_treeview
)) 
 986     // terribly efficient (RN:???) 
 987     // RN: Note that evidently the vadjustment property "vadjustment" from 
 988     // GtkTreeView is different from the "gtk-vadjustment"... 
 989     // (i.e. gtk_tree_view_get_vadjustment) 
 990     const gchar 
*vadjustment_key 
= "gtk-vadjustment"; 
 991     guint vadjustment_key_id 
= g_quark_from_static_string (vadjustment_key
); 
 993     GtkAdjustment 
*adjustment 
= 
 994        (GtkAdjustment
*) g_object_get_qdata(G_OBJECT (m_treeview
), vadjustment_key_id
); 
 995     wxCHECK_RET( adjustment
, wxT("invalid listbox code") ); 
 997     // Get the greater of the item heights from each column 
 998     gint cellheight 
= 0, cellheightcur
; 
 999     GList
* columnlist 
= gtk_tree_view_get_columns(m_treeview
); 
1000     GList
* curlist 
= columnlist
; 
1004         gtk_tree_view_column_cell_get_size( 
1005             GTK_TREE_VIEW_COLUMN(curlist
->data
), 
1006             NULL
, NULL
, NULL
, NULL
, 
1009         cellheight 
= cellheightcur 
> cellheight 
?  
1010                       cellheightcur 
: cellheight
; 
1012         curlist 
= curlist
->next
; 
1015     g_list_free(columnlist
); 
1017     float y 
= (float) (cellheight 
* n
); 
1018     if (y 
> adjustment
->upper 
- adjustment
->page_size
) 
1019         y 
= adjustment
->upper 
- adjustment
->page_size
; 
1020     gtk_adjustment_set_value( adjustment
, y 
); 
1024 // ---------------------------------------------------------------------------- 
1026 // ---------------------------------------------------------------------------- 
1029 void wxListBox::ApplyToolTip( GtkTooltips 
*tips
, const wxChar 
*tip 
) 
1031     // RN: Is this needed anymore? 
1032     gtk_tooltips_set_tip( tips
, GTK_WIDGET( m_treeview 
), wxGTK_CONV(tip
), (gchar
*) NULL 
); 
1034 #endif // wxUSE_TOOLTIPS 
1036 GtkWidget 
*wxListBox::GetConnectWidget() 
1038     // return GTK_WIDGET(m_treeview); 
1042 bool wxListBox::IsOwnGtkWindow( GdkWindow 
*window 
) 
1044     return (window 
== gtk_tree_view_get_bin_window(m_treeview
)); 
1047 void wxListBox::DoApplyWidgetStyle(GtkRcStyle 
*style
) 
1049     if (m_hasBgCol 
&& m_backgroundColour
.Ok()) 
1051         GdkWindow 
*window 
= gtk_tree_view_get_bin_window(m_treeview
); 
1054             m_backgroundColour
.CalcPixel( gdk_window_get_colormap( window 
) ); 
1055             gdk_window_set_background( window
, m_backgroundColour
.GetColor() ); 
1056             gdk_window_clear( window 
); 
1060     gtk_widget_modify_style( GTK_WIDGET(m_treeview
), style 
); 
1063 void wxListBox::OnInternalIdle() 
1065     //RN: Is this needed anymore? 
1066     wxCursor cursor 
= m_cursor
; 
1067     if (g_globalCursor
.Ok()) cursor 
= g_globalCursor
; 
1069     if (GTK_WIDGET(m_treeview
)->window 
&& cursor
.Ok()) 
1071         /* I now set the cursor the anew in every OnInternalIdle call 
1072            as setting the cursor in a parent window also effects the 
1073            windows above so that checking for the current cursor is 
1075         GdkWindow 
*window 
= gtk_tree_view_get_bin_window(m_treeview
); 
1076         gdk_window_set_cursor( window
, cursor
.GetCursor() ); 
1079     if (wxUpdateUIEvent::CanUpdate(this)) 
1080         UpdateWindowUI(wxUPDATE_UI_FROMIDLE
); 
1083 wxSize 
wxListBox::DoGetBestSize() const 
1085     wxCHECK_MSG(m_treeview
, wxDefaultSize
, wxT("invalid tree view")); 
1090     // Get the visible area of the tree view 
1092     gtk_tree_view_get_visible_rect(m_treeview
, &rect
); 
1093     lbWidth 
= rect
.width
; 
1094     lbHeight 
= rect
.height
; 
1096     // Add room for the scrollbar 
1097     lbWidth 
+= wxSystemSettings::GetMetric(wxSYS_VSCROLL_X
); 
1099     // And just a bit more 
1101     GetTextExtent( wxT("X"), &cx
, &cy
); 
1104     // don't make the listbox too tall (limit height to around 10 items) but don't 
1105     // make it too small neither 
1106     lbHeight 
= (cy
+4) * wxMin(wxMax(GetCount(), 3), 10); 
1108     wxSize 
best(lbWidth
, lbHeight
); 
1109     CacheBestSize(best
); 
1115 wxListBox::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
1117     return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new
, true); 
1120 #endif // wxUSE_LISTBOX