+// "changed"
+//-----------------------------------------------------------------------------
+
+extern "C" {
+static void
+gtk_listitem_changed_callback( GtkTreeSelection* selection, wxListBox *listbox )
+{
+ if (g_blockEventsOnDrag) return;
+
+ if (listbox->m_blockEvent) return;
+
+ wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
+ event.SetEventObject( listbox );
+
+ if (listbox->HasFlag(wxLB_MULTIPLE) || listbox->HasFlag(wxLB_EXTENDED))
+ {
+ wxArrayInt selections;
+ listbox->GetSelections( selections );
+
+ if (selections.GetCount() == 0)
+ {
+ // indicate that this is a deselection
+ event.SetExtraLong( 0 );
+ event.SetInt( -1 );
+
+ listbox->GetEventHandler()->ProcessEvent( event );
+
+ return;
+ }
+ else
+ {
+ // indicate that this is a selection
+ event.SetExtraLong( 1 );
+ event.SetInt( selections[0] );
+
+ listbox->GetEventHandler()->ProcessEvent( event );
+ }
+ }
+ else
+ {
+ int index = listbox->GetSelection();
+ if (index == wxNOT_FOUND)
+ {
+ // indicate that this is a deselection
+ event.SetExtraLong( 0 );
+ event.SetInt( -1 );
+
+ listbox->GetEventHandler()->ProcessEvent( event );
+
+ return;
+ }
+ else
+ {
+ GtkTreeEntry* entry = listbox->GtkGetEntry( index );
+
+ // indicate that this is a selection
+ event.SetExtraLong( 1 );
+
+ event.SetInt( index );
+ event.SetString(wxConvUTF8.cMB2WX(gtk_tree_entry_get_label(entry)));
+
+ if ( listbox->HasClientObjectData() )
+ event.SetClientObject(
+ (wxClientData*) gtk_tree_entry_get_userdata(entry)
+ );
+ else if ( listbox->HasClientUntypedData() )
+ event.SetClientData( gtk_tree_entry_get_userdata(entry) );
+
+ listbox->GetEventHandler()->ProcessEvent( event );
+
+ g_object_unref (entry);
+ }
+ }
+}
+}
+
+//-----------------------------------------------------------------------------
+// GtkTreeEntry destruction (to destroy client data)
+//-----------------------------------------------------------------------------
+
+extern "C" {
+static void gtk_tree_entry_destroy_cb(GtkTreeEntry* entry,
+ wxListBox* listbox)
+{
+ if (listbox->HasClientObjectData())
+ {
+ gpointer userdata = gtk_tree_entry_get_userdata(entry);
+ if (userdata)
+ delete (wxClientData *)userdata;
+ }
+}
+}
+
+//-----------------------------------------------------------------------------
+// Sorting callback (standard CmpNoCase return value)
+//-----------------------------------------------------------------------------
+#include <iostream>
+
+extern "C" {
+static gint gtk_listbox_sort_callback(GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ wxListBox *listbox)
+{
+ GtkTreeEntry* entry;
+ GtkTreeEntry* entry2;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(listbox->m_liststore),
+ a,
+ WXLISTBOX_DATACOLUMN_ARG(listbox),
+ &entry, -1);
+ gtk_tree_model_get(GTK_TREE_MODEL(listbox->m_liststore),
+ b,
+ WXLISTBOX_DATACOLUMN_ARG(listbox),
+ &entry2, -1);
+ wxCHECK_MSG(entry, 0, wxT("Could not get entry"));
+ wxCHECK_MSG(entry2, 0, wxT("Could not get entry2"));
+
+ //We compare collate keys here instead of calling g_utf8_collate
+ //as it is rather slow (and even the docs reccommend this)
+ int ret = strcmp(gtk_tree_entry_get_collate_key(entry),
+ gtk_tree_entry_get_collate_key(entry2));
+
+ g_object_unref (entry);
+ g_object_unref (entry2);
+
+ return ret;
+}
+}
+
+//-----------------------------------------------------------------------------
+// Searching callback (TRUE == not equal, FALSE == equal)
+//-----------------------------------------------------------------------------
+
+extern "C" {
+static gboolean gtk_listbox_searchequal_callback(GtkTreeModel* model,
+ gint column,
+ const gchar* key,
+ GtkTreeIter* iter,
+ wxListBox* listbox)
+{
+ GtkTreeEntry* entry;
+
+ gtk_tree_model_get(GTK_TREE_MODEL(listbox->m_liststore),
+ iter,
+ WXLISTBOX_DATACOLUMN_ARG(listbox),
+ &entry, -1);
+ wxCHECK_MSG(entry, 0, wxT("Could not get entry"));
+ wxGtkString keycollatekey(g_utf8_collate_key(key, -1));
+
+ int ret = strcmp(keycollatekey,
+ gtk_tree_entry_get_collate_key(entry));
+
+ g_object_unref (entry);
+
+ return ret != 0;
+}
+}
+
+//-----------------------------------------------------------------------------
+// wxListBox
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControlWithItems)
+
+// ----------------------------------------------------------------------------
+// construction
+// ----------------------------------------------------------------------------
+
+void wxListBox::Init()
+{
+ m_treeview = (GtkTreeView*) NULL;
+#if wxUSE_CHECKLISTBOX
+ m_hasCheckBoxes = false;
+#endif // wxUSE_CHECKLISTBOX
+}
+
+bool wxListBox::Create( wxWindow *parent, wxWindowID id,
+ const wxPoint &pos, const wxSize &size,
+ const wxArrayString& choices,
+ long style, const wxValidator& validator,
+ const wxString &name )
+{
+ wxCArrayString chs(choices);
+
+ return Create( parent, id, pos, size, chs.GetCount(), chs.GetStrings(),
+ style, validator, name );
+}
+
+bool wxListBox::Create( wxWindow *parent, wxWindowID id,
+ const wxPoint &pos, const wxSize &size,
+ int n, const wxString choices[],
+ long style, const wxValidator& validator,
+ const wxString &name )
+{
+ m_blockEvent = false;
+
+ if (!PreCreation( parent, pos, size ) ||
+ !CreateBase( parent, id, pos, size, style, validator, name ))
+ {
+ wxFAIL_MSG( wxT("wxListBox creation failed") );
+ return false;
+ }
+
+ m_widget = gtk_scrolled_window_new( (GtkAdjustment*) NULL, (GtkAdjustment*) NULL );
+ if (style & wxLB_ALWAYS_SB)
+ {
+ gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
+ }
+ else
+ {
+ gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
+ }
+
+
+ GtkScrolledWindowSetBorder(m_widget, style);
+
+ m_treeview = GTK_TREE_VIEW( gtk_tree_view_new( ) );
+
+ //wxListBox doesn't have a header :)
+ //NB: If enabled SetFirstItem doesn't work correctly
+ gtk_tree_view_set_headers_visible(m_treeview, FALSE);
+
+#if wxUSE_CHECKLISTBOX
+ if(m_hasCheckBoxes)
+ ((wxCheckListBox*)this)->DoCreateCheckList();
+#endif // wxUSE_CHECKLISTBOX
+
+ // Create the data column
+ gtk_tree_view_insert_column_with_attributes(m_treeview, -1, "",
+ gtk_cell_renderer_text_new(),
+ "text",
+ WXLISTBOX_DATACOLUMN, NULL);
+
+ // Now create+set the model (GtkListStore) - first argument # of columns
+#if wxUSE_CHECKLISTBOX
+ if(m_hasCheckBoxes)
+ m_liststore = gtk_list_store_new(2, G_TYPE_BOOLEAN,
+ GTK_TYPE_TREE_ENTRY);
+ else
+#endif
+ m_liststore = gtk_list_store_new(1, GTK_TYPE_TREE_ENTRY);
+
+ gtk_tree_view_set_model(m_treeview, GTK_TREE_MODEL(m_liststore));
+
+ g_object_unref (m_liststore); //free on treeview destruction
+
+ // Disable the pop-up textctrl that enables searching - note that
+ // the docs specify that even if this disabled (which we are doing)
+ // the user can still have it through the start-interactive-search
+ // key binding...either way we want to provide a searchequal callback
+ // NB: If this is enabled a doubleclick event (activate) gets sent
+ // on a successful search
+ gtk_tree_view_set_search_column(m_treeview, WXLISTBOX_DATACOLUMN);
+ gtk_tree_view_set_search_equal_func(m_treeview,
+ (GtkTreeViewSearchEqualFunc) gtk_listbox_searchequal_callback,
+ this,
+ NULL);
+
+ gtk_tree_view_set_enable_search(m_treeview, FALSE);
+
+
+ GtkTreeSelection* selection = gtk_tree_view_get_selection( m_treeview );
+
+ g_signal_connect_after (selection, "changed",
+ G_CALLBACK (gtk_listitem_changed_callback), this);
+
+ GtkSelectionMode mode;
+ if (style & wxLB_MULTIPLE)
+ {
+ mode = GTK_SELECTION_MULTIPLE;
+ }
+ else if (style & wxLB_EXTENDED)
+ {
+ mode = GTK_SELECTION_EXTENDED;
+ }
+ else
+ {
+ // if style was 0 set single mode
+ m_windowStyle |= wxLB_SINGLE;
+ mode = GTK_SELECTION_SINGLE;
+ }
+
+ gtk_tree_selection_set_mode( selection, mode );
+
+ // Handle sortable stuff
+ if(HasFlag(wxLB_SORT))
+ {
+ // Setup sorting in ascending (wx) order
+ gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(m_liststore),
+ WXLISTBOX_DATACOLUMN,
+ GTK_SORT_ASCENDING);
+
+ // Set the sort callback
+ gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(m_liststore),
+ WXLISTBOX_DATACOLUMN,
+ (GtkTreeIterCompareFunc) gtk_listbox_sort_callback,
+ this, //userdata
+ NULL //"destroy notifier"
+ );
+ }
+
+
+ gtk_container_add (GTK_CONTAINER (m_widget), GTK_WIDGET(m_treeview) );
+
+ gtk_widget_show( GTK_WIDGET(m_treeview) );
+ m_focusWidget = GTK_WIDGET(m_treeview);
+
+ Append(n, choices); // insert initial items
+
+ // generate dclick events
+ g_signal_connect_after(m_treeview, "row-activated",
+ G_CALLBACK(gtk_listbox_row_activated_callback), this);
+
+ m_parent->DoAddChild( this );
+
+ PostCreation(size);
+ SetInitialSize(size); // need this too because this is a wxControlWithItems
+
+ return true;
+}
+
+wxListBox::~wxListBox()
+{
+ m_hasVMT = false;
+
+ Clear();
+}
+
+// ----------------------------------------------------------------------------
+// adding items
+// ----------------------------------------------------------------------------
+
+int wxListBox::DoInsertItems(const wxArrayStringsAdapter& items,
+ unsigned int pos,
+ void **clientData,
+ wxClientDataType type)
+{
+ wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox") );
+
+ InvalidateBestSize();
+
+ GtkTreeIter* pIter = NULL; // append by default
+ GtkTreeIter iter;
+ if ( pos != GetCount() )
+ {
+ wxCHECK_MSG( GtkGetIteratorFor(pos, &iter), wxNOT_FOUND,
+ wxT("internal wxListBox error in insertion") );
+
+ pIter = &iter;
+ }
+
+ const unsigned int numItems = items.GetCount();
+ for ( unsigned int i = 0; i < numItems; ++i )
+ {
+ GtkTreeEntry* entry = gtk_tree_entry_new();
+ gtk_tree_entry_set_label(entry, wxGTK_CONV(items[i]));
+ gtk_tree_entry_set_destroy_func(entry,
+ (GtkTreeEntryDestroy)gtk_tree_entry_destroy_cb,
+ this);