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" 
  19     #include "wx/dynarray.h" 
  23     #include "wx/settings.h" 
  24     #include "wx/checklst.h" 
  25     #include "wx/arrstr.h" 
  28 #include "wx/gtk/private.h" 
  29 #include "wx/gtk/treeentry_gtk.h" 
  32     #include "wx/tooltip.h" 
  37 #include <gdk/gdkkeysyms.h> 
  39 //----------------------------------------------------------------------------- 
  41 //----------------------------------------------------------------------------- 
  43 extern bool           g_blockEventsOnDrag
; 
  44 extern bool           g_blockEventsOnScroll
; 
  45 extern wxCursor       g_globalCursor
; 
  49 //----------------------------------------------------------------------------- 
  50 // Macro to tell which row the strings are in (1 if native checklist, 0 if not) 
  51 //----------------------------------------------------------------------------- 
  53 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
  54 #   define WXLISTBOX_DATACOLUMN_ARG(x)  (x->m_hasCheckBoxes ? 1 : 0) 
  56 #   define WXLISTBOX_DATACOLUMN_ARG(x)  (0) 
  57 #endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
  59 #define WXLISTBOX_DATACOLUMN    WXLISTBOX_DATACOLUMN_ARG(this) 
  61 //----------------------------------------------------------------------------- 
  63 //----------------------------------------------------------------------------- 
  67 gtk_listbox_row_activated_callback(GtkTreeView        
*treeview
, 
  69                                    GtkTreeViewColumn  
*col
, 
  72     if (g_isIdle
) wxapp_install_idle_handler(); 
  74     if (g_blockEventsOnDrag
) return; 
  75     if (g_blockEventsOnScroll
) return; 
  77     if (!listbox
->m_hasVMT
) return; 
  80     //1) This is triggered by either a double-click or a space press 
  81     //2) We handle both here because 
  82     //2a) in the case of a space/keypress we can't really know 
  83     //    which item was pressed on because we can't get coords 
  85     //2b) It seems more correct 
  87     int sel 
= gtk_tree_path_get_indices(path
)[0]; 
  89     if(!listbox
->m_spacePressed
) 
  91         //Assume it was double-click 
  92         wxCommandEvent 
event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED
, listbox
->GetId() ); 
  93         event
.SetEventObject( listbox 
); 
  95         if(listbox
->IsSelected(sel
)) 
  97             GtkTreeEntry
* entry 
= listbox
->GtkGetEntry(sel
); 
 102                 event
.SetString(wxConvUTF8
.cMB2WX(gtk_tree_entry_get_label(entry
))); 
 104                 if ( listbox
->HasClientObjectData() ) 
 105                     event
.SetClientObject( 
 106                     (wxClientData
*) gtk_tree_entry_get_userdata(entry
) ); 
 107                 else if ( listbox
->HasClientUntypedData() ) 
 108                     event
.SetClientData( gtk_tree_entry_get_userdata(entry
) ); 
 109                 g_object_unref (entry
); 
 113                 wxLogSysError(wxT("Internal error - could not get entry for double-click")); 
 120         listbox
->GetEventHandler()->ProcessEvent( event 
); 
 124         listbox
->m_spacePressed 
= false; //don't block selection behaviour anymore 
 126         //Space was pressed - toggle the appropriate checkbox and the selection 
 127 #if wxUSE_CHECKLISTBOX //Do it for both native and non-native 
 128         if (listbox
->m_hasCheckBoxes
) 
 130             wxCheckListBox 
*clb 
= (wxCheckListBox 
*)listbox
; 
 132             clb
->Check( sel
, !clb
->IsChecked(sel
) ); 
 134             wxCommandEvent 
new_event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, listbox
->GetId() ); 
 135             new_event
.SetEventObject( listbox 
); 
 136             new_event
.SetInt( sel 
); 
 137             listbox
->GetEventHandler()->ProcessEvent( new_event 
); 
 139 #endif // wxUSE_CHECKLISTBOX 
 141         if(  (((listbox
->GetWindowStyleFlag() & wxLB_MULTIPLE
) != 0) || 
 142               ((listbox
->GetWindowStyleFlag() & wxLB_EXTENDED
) != 0)) ) 
 144             //toggle the selection + send event 
 145             listbox
->GtkSetSelection(sel
, !listbox
->IsSelected( sel 
), false); 
 151 //----------------------------------------------------------------------------- 
 152 // "button_press_event" 
 153 //----------------------------------------------------------------------------- 
 157 gtk_listbox_button_press_callback( GtkWidget 
*widget
, 
 158                                    GdkEventButton 
*gdk_event
, 
 161     if (g_isIdle
) wxapp_install_idle_handler(); 
 163     if (g_blockEventsOnDrag
) return FALSE
; 
 164     if (g_blockEventsOnScroll
) return FALSE
; 
 166     if (!listbox
->m_hasVMT
) return FALSE
; 
 168     //Just to be on the safe side - it should be unset in the activate callback 
 169     //but we don't want any obscure bugs if it doesn't get called somehow... 
 170     listbox
->m_spacePressed 
= false; 
 172 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 173     if ((listbox
->m_hasCheckBoxes
) && (gdk_event
->x 
< 15) && (gdk_event
->type 
!= GDK_2BUTTON_PRESS
)) 
 176         gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
), 
 177                                   (gint
)gdk_event
->x
, (gint
)gdk_event
->y
, 
 178                                   &path
, NULL
, NULL
, NULL
); 
 179         int sel 
= gtk_tree_path_get_indices(path
)[0]; 
 180         gtk_tree_path_free(path
); 
 182         wxCheckListBox 
*clb 
= (wxCheckListBox 
*)listbox
; 
 184         clb
->Check( sel
, !clb
->IsChecked(sel
) ); 
 186         wxCommandEvent 
event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED
, listbox
->GetId() ); 
 187         event
.SetEventObject( listbox 
); 
 189         listbox
->GetEventHandler()->ProcessEvent( event 
); 
 191 #endif // wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 197 //----------------------------------------------------------------------------- 
 199 //----------------------------------------------------------------------------- 
 203 gtk_listbox_key_press_callback( GtkWidget 
*widget
, 
 204                                 GdkEventKey 
*gdk_event
, 
 207     if (g_isIdle
) wxapp_install_idle_handler(); 
 209     if (g_blockEventsOnDrag
) return FALSE
; 
 214     if ((gdk_event
->keyval 
== GDK_Tab
) || (gdk_event
->keyval 
== GDK_ISO_Left_Tab
)) 
 216         wxNavigationKeyEvent new_event
; 
 217         /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */ 
 218         new_event
.SetDirection( (gdk_event
->keyval 
== GDK_Tab
) ); 
 219         /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */ 
 220         new_event
.SetWindowChange( (gdk_event
->state 
& GDK_CONTROL_MASK
) ); 
 221         new_event
.SetCurrentFocus( listbox 
); 
 222         ret 
= listbox
->GetEventHandler()->ProcessEvent( new_event 
); 
 225     if ((gdk_event
->keyval 
== GDK_Return
) && (!ret
)) 
 227         // eat return in all modes (RN:WHY?) 
 231     // Check or uncheck item with SPACE 
 232     if (gdk_event
->keyval 
== ' ') 
 234         //In the keyevent we don't know the index of the item 
 235         //and the activated event gets called anyway... 
 237         //Also, activating with the space causes the treeview to 
 238         //unselect all the items and then select the item in question 
 239         //wx's behaviour is to just toggle the item's selection state 
 240         //and leave the others alone 
 241         listbox
->m_spacePressed 
= true; 
 246         g_signal_stop_emission_by_name (widget
, "key_press_event"); 
 254 //----------------------------------------------------------------------------- 
 255 // "select" and "deselect" 
 256 //----------------------------------------------------------------------------- 
 259 static gboolean 
gtk_listitem_select_cb( GtkTreeSelection
* selection
, 
 262                                         gboolean is_selected
, 
 265     if (g_isIdle
) wxapp_install_idle_handler(); 
 267     if (!listbox
->m_hasVMT
) return TRUE
; 
 268     if (g_blockEventsOnDrag
) return TRUE
; 
 270     if (listbox
->m_spacePressed
) return FALSE
; //see keyevent callback 
 271     if (listbox
->m_blockEvent
) return TRUE
; 
 273     // NB: wxdocs explicitly say that this event only gets sent when 
 274     // something is actually selected, plus the controls example 
 275     // assumes so and passes -1 to the dogetclientdata funcs if not 
 277     // OK, so basically we need to do a bit of a run-around here as 
 278     // 1) is_selected says whether the item(s?) are CURRENTLY selected - 
 279     //    i.e. if is_selected is FALSE then the item is going to be 
 280     //    selected right now! 
 281     // 2) However, since it is not already selected and the user 
 282     //    will expect it to be we need to manually select it and 
 283     //    return FALSE telling GTK we handled the selection 
 284     if (is_selected
) return TRUE
; 
 286     int nIndex 
= gtk_tree_path_get_indices(path
)[0]; 
 287     GtkTreeEntry
* entry 
= listbox
->GtkGetEntry(nIndex
); 
 291         //Now, as mentioned above, we manually select the row that is/was going 
 292         //to be selected anyway by GTK 
 293         listbox
->m_blockEvent 
= true; //if we don't block events we will lock the 
 294                                       //app due to recursion!! 
 296         GtkTreeSelection
* selection 
= 
 297             gtk_tree_view_get_selection(listbox
->m_treeview
); 
 299         gtk_tree_model_get_iter(GTK_TREE_MODEL(listbox
->m_liststore
), &iter
, path
); 
 300         gtk_tree_selection_select_iter(selection
, &iter
); 
 302         listbox
->m_blockEvent 
= false; 
 304         //Finally, send the wx event 
 305         wxCommandEvent 
event(wxEVT_COMMAND_LISTBOX_SELECTED
, listbox
->GetId() ); 
 306         event
.SetEventObject( listbox 
); 
 308         // indicate whether this is a selection or a deselection 
 309         event
.SetExtraLong( 1 ); 
 311         event
.SetInt(nIndex
); 
 312         event
.SetString(wxConvUTF8
.cMB2WX(gtk_tree_entry_get_label(entry
))); 
 314         if ( listbox
->HasClientObjectData() ) 
 315             event
.SetClientObject( 
 316                     (wxClientData
*) gtk_tree_entry_get_userdata(entry
) 
 318         else if ( listbox
->HasClientUntypedData() ) 
 319             event
.SetClientData( gtk_tree_entry_get_userdata(entry
) ); 
 321         listbox
->GetEventHandler()->ProcessEvent( event 
); 
 323         g_object_unref (entry
); 
 324         return FALSE
;  //We handled it/did it manually 
 327     return TRUE
; //allow selection to change 
 331 //----------------------------------------------------------------------------- 
 332 // GtkTreeEntry destruction (to destroy client data) 
 333 //----------------------------------------------------------------------------- 
 336 static void gtk_tree_entry_destroy_cb(GtkTreeEntry
* entry
, 
 339     if(listbox
->HasClientObjectData()) 
 341         gpointer userdata 
= gtk_tree_entry_get_userdata(entry
); 
 343             delete (wxClientData 
*)userdata
; 
 348 //----------------------------------------------------------------------------- 
 349 // Sorting callback (standard CmpNoCase return value) 
 350 //----------------------------------------------------------------------------- 
 353 static gint 
gtk_listbox_sort_callback(GtkTreeModel 
*model
, 
 359     GtkTreeEntry
* entry2
; 
 361     gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
), 
 363                              WXLISTBOX_DATACOLUMN_ARG(listbox
), 
 365     gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
), 
 367                              WXLISTBOX_DATACOLUMN_ARG(listbox
), 
 369     wxCHECK_MSG(entry
, 0, wxT("Could not get entry")); 
 370     wxCHECK_MSG(entry2
, 0, wxT("Could not get entry2")); 
 372     //We compare collate keys here instead of calling g_utf8_collate 
 373     //as it is rather slow (and even the docs reccommend this) 
 374     int ret 
= strcasecmp(gtk_tree_entry_get_collate_key(entry
), 
 375                          gtk_tree_entry_get_collate_key(entry2
)); 
 377     g_object_unref (entry
); 
 378     g_object_unref (entry2
); 
 384 //----------------------------------------------------------------------------- 
 385 // Searching callback (TRUE == not equal, FALSE == equal) 
 386 //----------------------------------------------------------------------------- 
 389 static gboolean 
gtk_listbox_searchequal_callback(GtkTreeModel
* model
, 
 397     gtk_tree_model_get(GTK_TREE_MODEL(listbox
->m_liststore
), 
 399                              WXLISTBOX_DATACOLUMN_ARG(listbox
), 
 401     wxCHECK_MSG(entry
, 0, wxT("Could not get entry")); 
 402     gchar
* keycollatekey 
= g_utf8_collate_key(key
, -1); 
 404     int ret 
= strcasecmp(keycollatekey
, 
 405                          gtk_tree_entry_get_collate_key(entry
)); 
 407     g_free(keycollatekey
); 
 408     g_object_unref (entry
); 
 414 //----------------------------------------------------------------------------- 
 416 //----------------------------------------------------------------------------- 
 418 IMPLEMENT_DYNAMIC_CLASS(wxListBox
, wxControl
) 
 420 // ---------------------------------------------------------------------------- 
 422 // ---------------------------------------------------------------------------- 
 424 void wxListBox::Init() 
 426     m_treeview 
= (GtkTreeView
*) NULL
; 
 427 #if wxUSE_CHECKLISTBOX 
 428     m_hasCheckBoxes 
= false; 
 429 #endif // wxUSE_CHECKLISTBOX 
 430     m_spacePressed 
= false; 
 433 bool wxListBox::Create( wxWindow 
*parent
, wxWindowID id
, 
 434                         const wxPoint 
&pos
, const wxSize 
&size
, 
 435                         const wxArrayString
& choices
, 
 436                         long style
, const wxValidator
& validator
, 
 437                         const wxString 
&name 
) 
 439     wxCArrayString 
chs(choices
); 
 441     return Create( parent
, id
, pos
, size
, chs
.GetCount(), chs
.GetStrings(), 
 442                    style
, validator
, name 
); 
 445 bool wxListBox::Create( wxWindow 
*parent
, wxWindowID id
, 
 446                         const wxPoint 
&pos
, const wxSize 
&size
, 
 447                         int n
, const wxString choices
[], 
 448                         long style
, const wxValidator
& validator
, 
 449                         const wxString 
&name 
) 
 452     m_acceptsFocus 
= true; 
 453     m_blockEvent 
= false; 
 455     if (!PreCreation( parent
, pos
, size 
) || 
 456         !CreateBase( parent
, id
, pos
, size
, style
, validator
, name 
)) 
 458         wxFAIL_MSG( wxT("wxListBox creation failed") ); 
 462     m_widget 
= gtk_scrolled_window_new( (GtkAdjustment
*) NULL
, (GtkAdjustment
*) NULL 
); 
 463     if (style 
& wxLB_ALWAYS_SB
) 
 465       gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget
), 
 466         GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS 
); 
 470       gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget
), 
 471         GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC 
); 
 475     GtkScrolledWindowSetBorder(m_widget
, style
); 
 477     m_treeview 
= GTK_TREE_VIEW( gtk_tree_view_new( ) ); 
 479     //wxListBox doesn't have a header :) 
 480     //NB: If enabled SetFirstItem doesn't work correctly 
 481     gtk_tree_view_set_headers_visible(m_treeview
, FALSE
); 
 483 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
 485         ((wxCheckListBox
*)this)->DoCreateCheckList(); 
 486 #endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
 488     // Create the data column 
 489     gtk_tree_view_insert_column_with_attributes(m_treeview
, -1, "", 
 490                                                 gtk_cell_renderer_text_new(), 
 492                                                 WXLISTBOX_DATACOLUMN
, NULL
); 
 494     // Now create+set the model (GtkListStore) - first argument # of columns 
 495 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
 497         m_liststore 
= gtk_list_store_new(2, G_TYPE_BOOLEAN
, 
 498                                             GTK_TYPE_TREE_ENTRY
); 
 501         m_liststore 
= gtk_list_store_new(1, GTK_TYPE_TREE_ENTRY
); 
 503     gtk_tree_view_set_model(m_treeview
, GTK_TREE_MODEL(m_liststore
)); 
 505     g_object_unref (m_liststore
); //free on treeview destruction 
 507     // Disable the pop-up textctrl that enables searching - note that 
 508     // the docs specify that even if this disabled (which we are doing) 
 509     // the user can still have it through the start-interactive-search 
 510     // key binding...either way we want to provide a searchequal callback 
 511     // NB: If this is enabled a doubleclick event (activate) gets sent 
 512     //     on a successful search 
 513     gtk_tree_view_set_search_column(m_treeview
, WXLISTBOX_DATACOLUMN
); 
 514     gtk_tree_view_set_search_equal_func(m_treeview
, 
 515        (GtkTreeViewSearchEqualFunc
) gtk_listbox_searchequal_callback
, 
 519     gtk_tree_view_set_enable_search(m_treeview
, FALSE
); 
 522     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection( m_treeview 
); 
 523     gtk_tree_selection_set_select_function(selection
, 
 524                      (GtkTreeSelectionFunc
)gtk_listitem_select_cb
, 
 525                                            this, NULL
); //NULL == destroycb 
 527     GtkSelectionMode mode
; 
 528     if (style 
& wxLB_MULTIPLE
) 
 530         mode 
= GTK_SELECTION_MULTIPLE
; 
 532     else if (style 
& wxLB_EXTENDED
) 
 534         mode 
= GTK_SELECTION_EXTENDED
; 
 538         // if style was 0 set single mode 
 539         m_windowStyle 
|= wxLB_SINGLE
; 
 540         mode 
= GTK_SELECTION_SINGLE
; 
 543     gtk_tree_selection_set_mode( selection
, mode 
); 
 545     //Handle sortable stuff 
 546     if(style 
& wxLB_SORT
) 
 548         //Setup sorting in ascending (wx) order 
 549         gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(m_liststore
), 
 550                                              WXLISTBOX_DATACOLUMN
, 
 553         //Set the sort callback 
 554         gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(m_liststore
), 
 555                                         WXLISTBOX_DATACOLUMN
, 
 556                    (GtkTreeIterCompareFunc
) gtk_listbox_sort_callback
, 
 558                                         NULL 
//"destroy notifier" 
 563     gtk_container_add (GTK_CONTAINER (m_widget
), GTK_WIDGET(m_treeview
) ); 
 565     gtk_widget_show( GTK_WIDGET(m_treeview
) ); 
 567     wxListBox::DoInsertItems(wxArrayString(n
, choices
), 0); // insert initial items 
 569     //treeview-specific events 
 570     g_signal_connect(m_treeview
, "row-activated", 
 571                      G_CALLBACK(gtk_listbox_row_activated_callback
), this); 
 574     g_signal_connect (m_treeview
, "button_press_event", 
 575                       G_CALLBACK (gtk_listbox_button_press_callback
), 
 577     g_signal_connect (m_treeview
, "key_press_event", 
 578                       G_CALLBACK (gtk_listbox_key_press_callback
), 
 581     m_parent
->DoAddChild( this ); 
 584     SetBestSize(size
); // need this too because this is a wxControlWithItems 
 589 wxListBox::~wxListBox() 
 596 // ---------------------------------------------------------------------------- 
 598 // ---------------------------------------------------------------------------- 
 600 void wxListBox::GtkInsertItems(const wxArrayString
& items
, 
 601                                void** clientData
, unsigned int pos
) 
 603     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 605     InvalidateBestSize(); 
 607     // Create and set column ids and GValues 
 609     unsigned int nNum 
= items
.GetCount(); 
 610     unsigned int nCurCount 
= wxListBox::GetCount(); 
 611     wxASSERT_MSG(pos 
<= nCurCount
, wxT("Invalid index passed to wxListBox")); 
 613     GtkTreeIter
* pIter 
= NULL
; // append by default 
 615     if (pos 
!= nCurCount
) 
 617         gboolean res 
= gtk_tree_model_iter_nth_child( 
 618                         GTK_TREE_MODEL(m_liststore
), 
 619                         &iter
, NULL
, //NULL = parent = get first 
 623             wxLogSysError(wxT("internal wxListBox error in insertion")); 
 630     for (unsigned int i 
= 0; i 
< nNum
; ++i
) 
 632         wxString label 
= items
[i
]; 
 634 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 637             label
.Prepend(wxCHECKLBOX_STRING
); 
 639 #endif // wxUSE_CHECKLISTBOX 
 642         GtkTreeEntry
* entry 
= gtk_tree_entry_new(); 
 643         gtk_tree_entry_set_label(entry
, wxConvUTF8
.cWX2MB(label
)); 
 644         gtk_tree_entry_set_destroy_func(entry
, 
 645                 (GtkTreeEntryDestroy
)gtk_tree_entry_destroy_cb
, 
 649             gtk_tree_entry_set_userdata(entry
, clientData
[i
]); 
 652         gtk_list_store_insert_before(m_liststore
, &itercur
, pIter
); 
 654 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
 657             gtk_list_store_set(m_liststore
, &itercur
, 
 658                                  0, FALSE
, //FALSE == not toggled 
 663             gtk_list_store_set(m_liststore
, &itercur
, 
 666         g_object_unref (entry
); //liststore always refs :) 
 670 void wxListBox::DoInsertItems(const wxArrayString
& items
, unsigned int pos
) 
 672     wxCHECK_RET( IsValidInsert(pos
), wxT("invalid index in wxListBox::InsertItems") ); 
 674     GtkInsertItems(items
, NULL
, pos
); 
 677 int wxListBox::DoAppend( const wxString
& item 
) 
 679     // Call DoInsertItems 
 680     unsigned int nWhere 
= wxListBox::GetCount(); 
 681     wxArrayString aItems
; 
 683     wxListBox::DoInsertItems(aItems
, nWhere
); 
 687 void wxListBox::DoSetItems( const wxArrayString
& items
, 
 691     GtkInsertItems(items
, clientData
, 0); 
 694 // ---------------------------------------------------------------------------- 
 696 // ---------------------------------------------------------------------------- 
 698 void wxListBox::Clear() 
 700     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 702     InvalidateBestSize(); 
 704     gtk_list_store_clear( m_liststore 
); /* well, THAT was easy :) */ 
 707 void wxListBox::Delete(unsigned int n
) 
 709     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 711     InvalidateBestSize(); 
 714     gboolean res 
= gtk_tree_model_iter_nth_child( 
 715                         GTK_TREE_MODEL(m_liststore
), 
 716                         &iter
, NULL
, //NULL = parent = get first 
 720     wxCHECK_RET( res
, wxT("wrong listbox index") ); 
 722     //this returns false if iter is invalid (i.e. deleting item 
 723     //at end) but since we don't use iter, well... :) 
 724     gtk_list_store_remove(m_liststore
, &iter
); 
 727 // ---------------------------------------------------------------------------- 
 728 // get GtkTreeEntry from position (note: you need to g_unref it if valid) 
 729 // ---------------------------------------------------------------------------- 
 731 struct _GtkTreeEntry
* wxListBox::GtkGetEntry(int n
) const 
 734     gboolean res 
= gtk_tree_model_iter_nth_child( 
 735                         GTK_TREE_MODEL(m_liststore
), 
 736                         &iter
, NULL
, //NULL = parent = get first 
 741         wxLogDebug(wxT("gtk_tree_model_iter_nth_child failed\n") 
 742                    wxT("Passed in value was:[%i]  List size:[%u]"), 
 743                    n
, wxListBox::GetCount() ); 
 748     GtkTreeEntry
* entry 
= NULL
; 
 749     gtk_tree_model_get(GTK_TREE_MODEL(m_liststore
), &iter
, 
 750                        WXLISTBOX_DATACOLUMN
, &entry
, -1); 
 755 // ---------------------------------------------------------------------------- 
 757 // ---------------------------------------------------------------------------- 
 759 void* wxListBox::DoGetItemClientData(unsigned int n
) const 
 761     wxCHECK_MSG( IsValid(n
), NULL
, 
 762                  wxT("Invalid index passed to GetItemClientData") ); 
 764     GtkTreeEntry
* entry 
= GtkGetEntry(n
); 
 765     wxCHECK_MSG(entry
, NULL
, wxT("could not get entry")); 
 767     void* userdata 
= gtk_tree_entry_get_userdata( entry 
); 
 768     g_object_unref (entry
); 
 772 wxClientData
* wxListBox::DoGetItemClientObject(unsigned int n
) const 
 774     return (wxClientData
*) wxListBox::DoGetItemClientData(n
); 
 777 void wxListBox::DoSetItemClientData(unsigned int n
, void* clientData
) 
 779     wxCHECK_RET( IsValid(n
), 
 780                  wxT("Invalid index passed to SetItemClientData") ); 
 782     GtkTreeEntry
* entry 
= GtkGetEntry(n
); 
 783     wxCHECK_RET(entry
, wxT("could not get entry")); 
 785     gtk_tree_entry_set_userdata( entry
, clientData 
); 
 786     g_object_unref (entry
); 
 789 void wxListBox::DoSetItemClientObject(unsigned int n
, wxClientData
* clientData
) 
 791     // wxItemContainer already deletes data for us 
 792     wxListBox::DoSetItemClientData(n
, (void*) clientData
); 
 795 // ---------------------------------------------------------------------------- 
 796 // string list access 
 797 // ---------------------------------------------------------------------------- 
 799 void wxListBox::SetString(unsigned int n
, const wxString 
&string
) 
 801     wxCHECK_RET( IsValid(n
), wxT("invalid index in wxListBox::SetString") ); 
 802     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 804     GtkTreeEntry
* entry 
= GtkGetEntry(n
); 
 805     wxCHECK_RET( entry
, wxT("wrong listbox index") ); 
 807     wxString label 
= string
; 
 809 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 811         label
.Prepend(wxCHECKLBOX_STRING
); 
 812 #endif // wxUSE_CHECKLISTBOX 
 814     // RN: This may look wierd but the problem is that the TreeView 
 815     // doesn't resort or update when changed above and there is no real 
 816     // notification function... 
 817     void* userdata 
= gtk_tree_entry_get_userdata(entry
); 
 818     gtk_tree_entry_set_userdata(entry
, NULL
); //don't delete on destroy 
 819     g_object_unref (entry
); 
 821     bool bWasSelected 
= wxListBox::IsSelected(n
); 
 822     wxListBox::Delete(n
); 
 824     wxArrayString aItems
; 
 826     GtkInsertItems(aItems
, &userdata
, n
); 
 828         wxListBox::GtkSetSelection(n
, true, true); 
 831 wxString 
wxListBox::GetString(unsigned int n
) const 
 833     wxCHECK_MSG( m_treeview 
!= NULL
, wxEmptyString
, wxT("invalid listbox") ); 
 835     GtkTreeEntry
* entry 
= GtkGetEntry(n
); 
 836     wxCHECK_MSG( entry
, wxEmptyString
, wxT("wrong listbox index") ); 
 838     wxString label 
= wxGTK_CONV_BACK( gtk_tree_entry_get_label(entry
) ); 
 840 #if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST 
 841     // checklistboxes have "[±] " prepended to their lables, remove it 
 843     // NB: 4 below is the length of wxCHECKLBOX_STRING from wx/gtk/checklst.h 
 844     if ( m_hasCheckBoxes 
) 
 846 #endif // wxUSE_CHECKLISTBOX 
 848     g_object_unref (entry
); 
 852 unsigned int wxListBox::GetCount() const 
 854     wxCHECK_MSG( m_treeview 
!= NULL
, 0, wxT("invalid listbox") ); 
 856     return (unsigned int)gtk_tree_model_iter_n_children(GTK_TREE_MODEL(m_liststore
), NULL
); 
 859 int wxListBox::FindString( const wxString 
&item
, bool bCase 
) const 
 861     wxCHECK_MSG( m_treeview 
!= NULL
, wxNOT_FOUND
, wxT("invalid listbox") ); 
 863     //Sort of hackish - maybe there is a faster way 
 864     unsigned int nCount 
= wxListBox::GetCount(); 
 866     for(unsigned int i 
= 0; i 
< nCount
; ++i
) 
 868         if( item
.IsSameAs( wxListBox::GetString(i
), bCase 
) ) 
 873     // it's not an error if the string is not found -> no wxCHECK 
 877 // ---------------------------------------------------------------------------- 
 879 // ---------------------------------------------------------------------------- 
 881 int wxListBox::GetSelection() const 
 883     wxCHECK_MSG( m_treeview 
!= NULL
, wxNOT_FOUND
, wxT("invalid listbox")); 
 884     wxCHECK_MSG( HasFlag(wxLB_SINGLE
), wxNOT_FOUND
, 
 885                     wxT("must be single selection listbox")); 
 888     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 890     // only works on single-sel 
 891     if (!gtk_tree_selection_get_selected(selection
, NULL
, &iter
)) 
 895         gtk_tree_model_get_path(GTK_TREE_MODEL(m_liststore
), &iter
); 
 897     int sel 
= gtk_tree_path_get_indices(path
)[0]; 
 899     gtk_tree_path_free(path
); 
 904 int wxListBox::GetSelections( wxArrayInt
& aSelections 
) const 
 906     wxCHECK_MSG( m_treeview 
!= NULL
, wxNOT_FOUND
, wxT("invalid listbox") ); 
 912     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 914     if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(m_liststore
), &iter
)) 
 915     { //gtk_tree_selection_get_selected_rows is GTK 2.2+ so iter instead 
 918             if (gtk_tree_selection_iter_is_selected(selection
, &iter
)) 
 922         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(m_liststore
), &iter
)); 
 925     return aSelections
.GetCount(); 
 928 bool wxListBox::IsSelected( int n 
) const 
 930     wxCHECK_MSG( m_treeview 
!= NULL
, false, wxT("invalid listbox") ); 
 932     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 935     gboolean res 
= gtk_tree_model_iter_nth_child( 
 936                         GTK_TREE_MODEL(m_liststore
), 
 937                         &iter
, NULL
, //NULL = parent = get first 
 940     wxCHECK_MSG( res
, false, wxT("Invalid index") ); 
 942     return gtk_tree_selection_iter_is_selected(selection
, &iter
); 
 945 void wxListBox::DoSetSelection( int n
, bool select 
) 
 947     // passing -1 to SetSelection() is documented to deselect all items 
 948     if ( n 
== wxNOT_FOUND 
) 
 950         // ... and not generate any events in the process 
 954     wxCHECK_RET( IsValid(n
), wxT("invalid index in wxListBox::SetSelection") ); 
 956     // don't generate the selection event 
 957     GtkSetSelection(n
, select
, true); 
 960 void wxListBox::GtkDeselectAll() 
 962     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 964     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 968     gtk_tree_selection_unselect_all(selection
); 
 970     m_blockEvent 
= false; 
 973 void wxListBox::GtkSetSelection(int n
, const bool select
, const bool blockEvent
) 
 975     wxCHECK_RET( m_treeview 
!= NULL
, wxT("invalid listbox") ); 
 977     GtkTreeSelection
* selection 
= gtk_tree_view_get_selection(m_treeview
); 
 980     gboolean res 
= gtk_tree_model_iter_nth_child( 
 981                         GTK_TREE_MODEL(m_liststore
), 
 982                         &iter
, NULL
, //NULL = parent = get first 
 985     wxCHECK_RET( res
, wxT("Invalid index") ); 
 987     m_blockEvent 
= blockEvent
; 
 990         gtk_tree_selection_select_iter(selection
, &iter
); 
 992         gtk_tree_selection_unselect_iter(selection
, &iter
); 
 994     m_blockEvent 
= false; 
 997 void wxListBox::DoSetFirstItem( int n 
) 
 999     wxCHECK_RET( m_treeview
, wxT("invalid listbox") ); 
1000     wxCHECK_RET( IsValid(n
), wxT("invalid index")); 
1002     //RN: I have no idea why this line is needed... 
1003     if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (m_treeview
)) 
1007     gtk_tree_model_iter_nth_child( 
1008                                     GTK_TREE_MODEL(m_liststore
), 
1010                                     NULL
, //NULL = parent = get first 
1014     GtkTreePath
* path 
= gtk_tree_model_get_path( 
1015                             GTK_TREE_MODEL(m_liststore
), &iter
); 
1017     // Scroll to the desired cell (0.0 == topleft alignment) 
1018     gtk_tree_view_scroll_to_cell(m_treeview
, path
, NULL
, 
1021     gtk_tree_path_free(path
); 
1024 // ---------------------------------------------------------------------------- 
1026 // ---------------------------------------------------------------------------- 
1028 int wxListBox::DoListHitTest(const wxPoint
& point
) const 
1030     // gtk_tree_view_get_path_at_pos() also gets items that are not visible and 
1031     // we only want visible items we need to check for it manually here 
1032     if ( !GetClientRect().Inside(point
) ) 
1035     // need to translate from master window since it is in client coords 
1037     gdk_window_get_geometry(gtk_tree_view_get_bin_window(m_treeview
), 
1038                             &binx
, &biny
, NULL
, NULL
, NULL
); 
1041     if ( !gtk_tree_view_get_path_at_pos
 
1047             NULL
,   // [out] column (always 0 here) 
1048             NULL
,   // [out] x-coord relative to the cell (not interested) 
1049             NULL    
// [out] y-coord relative to the cell 
1055     int index 
= gtk_tree_path_get_indices(path
)[0]; 
1056     gtk_tree_path_free(path
); 
1061 // ---------------------------------------------------------------------------- 
1063 // ---------------------------------------------------------------------------- 
1066 void wxListBox::ApplyToolTip( GtkTooltips 
*tips
, const wxChar 
*tip 
) 
1068     // RN: Is this needed anymore? 
1069     gtk_tooltips_set_tip( tips
, GTK_WIDGET( m_treeview 
), wxGTK_CONV(tip
), (gchar
*) NULL 
); 
1071 #endif // wxUSE_TOOLTIPS 
1073 GtkWidget 
*wxListBox::GetConnectWidget() 
1075     // the correct widget for listbox events (such as mouse clicks for example) 
1076     // is m_treeview, not the parent scrolled window 
1077     return GTK_WIDGET(m_treeview
); 
1080 bool wxListBox::IsOwnGtkWindow( GdkWindow 
*window 
) 
1082     return (window 
== gtk_tree_view_get_bin_window(m_treeview
)); 
1085 void wxListBox::DoApplyWidgetStyle(GtkRcStyle 
*style
) 
1087     if (m_hasBgCol 
&& m_backgroundColour
.Ok()) 
1089         GdkWindow 
*window 
= gtk_tree_view_get_bin_window(m_treeview
); 
1092             m_backgroundColour
.CalcPixel( gdk_drawable_get_colormap( window 
) ); 
1093             gdk_window_set_background( window
, m_backgroundColour
.GetColor() ); 
1094             gdk_window_clear( window 
); 
1098     gtk_widget_modify_style( GTK_WIDGET(m_treeview
), style 
); 
1101 void wxListBox::OnInternalIdle() 
1103     //RN: Is this needed anymore? 
1104     wxCursor cursor 
= m_cursor
; 
1105     if (g_globalCursor
.Ok()) cursor 
= g_globalCursor
; 
1107     if (GTK_WIDGET(m_treeview
)->window 
&& cursor
.Ok()) 
1109         /* I now set the cursor the anew in every OnInternalIdle call 
1110            as setting the cursor in a parent window also effects the 
1111            windows above so that checking for the current cursor is 
1113         GdkWindow 
*window 
= gtk_tree_view_get_bin_window(m_treeview
); 
1114         gdk_window_set_cursor( window
, cursor
.GetCursor() ); 
1117     if (wxUpdateUIEvent::CanUpdate(this)) 
1118         UpdateWindowUI(wxUPDATE_UI_FROMIDLE
); 
1121 wxSize 
wxListBox::DoGetBestSize() const 
1123     wxCHECK_MSG(m_treeview
, wxDefaultSize
, wxT("invalid tree view")); 
1125     // Start with a minimum size that's not too small 
1127     GetTextExtent( wxT("X"), &cx
, &cy
); 
1128     int lbWidth 
= 3 * cx
; 
1131     // Get the visible area of the tree view (limit to the 10th item 
1132     // so that it isn't too big) 
1133     unsigned int count 
= GetCount(); 
1138         // Find the widest line 
1139         for(unsigned int i 
= 0; i 
< count
; i
++) { 
1140             wxString 
str(GetString(i
)); 
1141             GetTextExtent(str
, &wLine
, NULL
); 
1142             lbWidth 
= wxMax(lbWidth
, wLine
); 
1147         // And just a bit more for the checkbox if present and then some 
1148         // (these are rough guesses) 
1149 #if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST 
1150         if ( m_hasCheckBoxes 
) 
1153             cy 
= cy 
> 25 ? cy 
: 25; // rough height of checkbox 
1157         // don't make the listbox too tall (limit height to around 10 items) but don't 
1158         // make it too small neither 
1159         lbHeight 
= (cy
+4) * wxMin(wxMax(count
, 3), 10); 
1162     // Add room for the scrollbar 
1163     lbWidth 
+= wxSystemSettings::GetMetric(wxSYS_VSCROLL_X
); 
1165     wxSize 
best(lbWidth
, lbHeight
); 
1166     CacheBestSize(best
); 
1172 wxListBox::GetClassDefaultAttributes(wxWindowVariant 
WXUNUSED(variant
)) 
1174     return GetDefaultAttributesFromGTKWidget(gtk_tree_view_new
, true); 
1177 #endif // wxUSE_LISTBOX