+// data
+//-----------------------------------------------------------------------------
+
+extern bool g_blockEventsOnDrag;
+extern bool g_blockEventsOnScroll;
+extern wxCursor g_globalCursor;
+
+
+
+//-----------------------------------------------------------------------------
+// Macro to tell which row the strings are in (1 if native checklist, 0 if not)
+//-----------------------------------------------------------------------------
+
+#if wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
+# define WXLISTBOX_DATACOLUMN_ARG(x) (x->m_hasCheckBoxes ? 1 : 0)
+#else
+# define WXLISTBOX_DATACOLUMN_ARG(x) (0)
+#endif // wxUSE_CHECKLISTBOX && wxUSE_NATIVEGTKCHECKLIST
+
+#define WXLISTBOX_DATACOLUMN WXLISTBOX_DATACOLUMN_ARG(this)
+
+//-----------------------------------------------------------------------------
+// "row-activated"
+//-----------------------------------------------------------------------------
+
+extern "C" {
+static void
+gtk_listbox_row_activated_callback(GtkTreeView *treeview,
+ GtkTreePath *path,
+ GtkTreeViewColumn *col,
+ wxListBox *listbox)
+{
+ if (g_isIdle) wxapp_install_idle_handler();
+
+ if (g_blockEventsOnDrag) return;
+ if (g_blockEventsOnScroll) return;
+
+ if (!listbox->m_hasVMT) return;
+
+ //Notes:
+ //1) This is triggered by either a double-click or a space press
+ //2) We handle both here because
+ //2a) in the case of a space/keypress we can't really know
+ // which item was pressed on because we can't get coords
+ // from a keyevent
+ //2b) It seems more correct
+
+ int sel = gtk_tree_path_get_indices(path)[0];
+
+ if(!listbox->m_spacePressed)
+ {
+ //Assume it was double-click
+ wxCommandEvent event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, listbox->GetId() );
+ event.SetEventObject( listbox );
+
+ if(listbox->IsSelected(sel))
+ {
+ GtkTreeEntry* entry = listbox->GtkGetEntry(sel);
+
+ if(entry)
+ {
+ event.SetInt(sel);
+ 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) );
+ g_object_unref(G_OBJECT(entry));
+ }
+ else
+ {
+ wxLogSysError(wxT("Internal error - could not get entry for double-click"));
+ event.SetInt(-1);
+ }
+ }
+ else
+ event.SetInt(-1);
+
+ listbox->GetEventHandler()->ProcessEvent( event );
+ }
+ else
+ {
+ listbox->m_spacePressed = false; //don't block selection behaviour anymore
+
+ //Space was pressed - toggle the appropriate checkbox and the selection
+#if wxUSE_CHECKLISTBOX //Do it for both native and non-native
+ if (listbox->m_hasCheckBoxes)
+ {
+ wxCheckListBox *clb = (wxCheckListBox *)listbox;
+
+ clb->Check( sel, !clb->IsChecked(sel) );
+
+ wxCommandEvent new_event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, listbox->GetId() );
+ new_event.SetEventObject( listbox );
+ new_event.SetInt( sel );
+ listbox->GetEventHandler()->ProcessEvent( new_event );
+ }
+#endif // wxUSE_CHECKLISTBOX
+
+ if( (((listbox->GetWindowStyleFlag() & wxLB_MULTIPLE) != 0) ||
+ ((listbox->GetWindowStyleFlag() & wxLB_EXTENDED) != 0)) )
+ {
+ //toggle the selection + send event
+ listbox->GtkSetSelection(sel, !listbox->IsSelected( sel ), false);
+ }
+ }
+}
+}
+
+//-----------------------------------------------------------------------------
+// "button_press_event"
+//-----------------------------------------------------------------------------
+
+extern "C" {
+static gint
+gtk_listbox_button_press_callback( GtkWidget *widget,
+ GdkEventButton *gdk_event,
+ wxListBox *listbox )
+{
+ if (g_isIdle) wxapp_install_idle_handler();
+
+ if (g_blockEventsOnDrag) return FALSE;
+ if (g_blockEventsOnScroll) return FALSE;
+
+ if (!listbox->m_hasVMT) return FALSE;
+
+ //Just to be on the safe side - it should be unset in the activate callback
+ //but we don't want any obscure bugs if it doesn't get called somehow...
+ listbox->m_spacePressed = false;
+
+#if wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
+ if ((listbox->m_hasCheckBoxes) && (gdk_event->x < 15) && (gdk_event->type != GDK_2BUTTON_PRESS))
+ {
+ GtkTreePath* path;
+ gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget),
+ (gint)gdk_event->x, (gint)gdk_event->y,
+ &path, NULL, NULL, NULL);
+ int sel = gtk_tree_path_get_indices(path)[0];
+ gtk_tree_path_free(path);
+
+ wxCheckListBox *clb = (wxCheckListBox *)listbox;
+
+ clb->Check( sel, !clb->IsChecked(sel) );
+
+ wxCommandEvent event( wxEVT_COMMAND_CHECKLISTBOX_TOGGLED, listbox->GetId() );
+ event.SetEventObject( listbox );
+ event.SetInt( sel );
+ listbox->GetEventHandler()->ProcessEvent( event );
+ }
+#endif // wxUSE_CHECKLISTBOX && !wxUSE_NATIVEGTKCHECKLIST
+
+ return FALSE;
+}
+}
+
+//-----------------------------------------------------------------------------
+// "key_press_event"
+//-----------------------------------------------------------------------------
+
+extern "C" {
+static gint
+gtk_listbox_key_press_callback( GtkWidget *widget,
+ GdkEventKey *gdk_event,
+ wxListBox *listbox )
+{
+ if (g_isIdle) wxapp_install_idle_handler();
+
+ if (g_blockEventsOnDrag) return FALSE;
+
+
+ bool ret = false;
+
+ 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)