+ if ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab))
+ {
+ wxNavigationKeyEvent new_event;
+ /* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
+ new_event.SetDirection( (gdk_event->keyval == GDK_Tab) );
+ /* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
+ new_event.SetWindowChange( (gdk_event->state & GDK_CONTROL_MASK) );
+ new_event.SetCurrentFocus( listbox );
+ ret = listbox->GetEventHandler()->ProcessEvent( new_event );
+ }
+
+ if ((gdk_event->keyval == GDK_Return) && (!ret))
+ {
+ // eat return in all modes (RN:WHY?)
+ ret = true;
+ }
+
+ // Check or uncheck item with SPACE
+ if (gdk_event->keyval == ' ')
+ {
+ //In the keyevent we don't know the index of the item
+ //and the activated event gets called anyway...
+ //
+ //Also, activating with the space causes the treeview to
+ //unselect all the items and then select the item in question
+ //wx's behaviour is to just toggle the item's selection state
+ //and leave the others alone
+ listbox->m_spacePressed = true;
+ }
+
+ if (ret)
+ {
+ g_signal_stop_emission_by_name (widget, "key_press_event");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+}
+
+//-----------------------------------------------------------------------------
+// "select" and "deselect"
+//-----------------------------------------------------------------------------
+
+extern "C" {
+static gboolean gtk_listitem_select_cb( GtkTreeSelection* selection,
+ GtkTreeModel* model,
+ GtkTreePath* path,
+ gboolean is_selected,
+ wxListBox *listbox )
+{
+ if (g_isIdle) wxapp_install_idle_handler();
+
+ if (!listbox->m_hasVMT) return TRUE;
+ if (g_blockEventsOnDrag) return TRUE;
+
+ if (listbox->m_spacePressed) return FALSE; //see keyevent callback
+ if (listbox->m_blockEvent) return TRUE;
+
+ // NB: wxdocs explicitly say that this event only gets sent when
+ // something is actually selected, plus the controls example
+ // assumes so and passes -1 to the dogetclientdata funcs if not
+
+ // OK, so basically we need to do a bit of a run-around here as
+ // 1) is_selected says whether the item(s?) are CURRENTLY selected -
+ // i.e. if is_selected is FALSE then the item is going to be
+ // selected right now!
+ // 2) However, since it is not already selected and the user
+ // will expect it to be we need to manually select it and
+ // return FALSE telling GTK we handled the selection
+ if (is_selected) return TRUE;
+
+ int nIndex = gtk_tree_path_get_indices(path)[0];
+ GtkTreeEntry* entry = listbox->GtkGetEntry(nIndex);
+
+ if(entry)
+ {
+ //Now, as mentioned above, we manually select the row that is/was going
+ //to be selected anyway by GTK
+ listbox->m_blockEvent = true; //if we don't block events we will lock the
+ //app due to recursion!!
+
+ GtkTreeSelection* selection =
+ gtk_tree_view_get_selection(listbox->m_treeview);
+ GtkTreeIter iter;
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(listbox->m_liststore), &iter, path);
+ gtk_tree_selection_select_iter(selection, &iter);
+
+ listbox->m_blockEvent = false;
+
+ //Finally, send the wx event
+ wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
+ event.SetEventObject( listbox );
+
+ // indicate whether this is a selection or a deselection
+ event.SetExtraLong( 1 );
+
+ event.SetInt(nIndex);
+ 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(G_OBJECT(entry));
+ return FALSE; //We handled it/did it manually
+ }
+
+ return TRUE; //allow selection to change
+}
+}
+
+//-----------------------------------------------------------------------------
+// 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)
+//-----------------------------------------------------------------------------
+
+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 = strcasecmp(gtk_tree_entry_get_collate_key(entry),
+ gtk_tree_entry_get_collate_key(entry2));
+
+ g_object_unref(G_OBJECT(entry));
+ g_object_unref(G_OBJECT(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"));
+ gchar* keycollatekey = g_utf8_collate_key(key, -1);
+
+ int ret = strcasecmp(keycollatekey,
+ gtk_tree_entry_get_collate_key(entry));
+
+ g_free(keycollatekey);
+ g_object_unref(G_OBJECT(entry));
+
+ return ret != 0;
+}