+extern "C" gint wxlistbox_idle_callback( gpointer gdata )
+{
+ wxlistbox_idle_struct* data = (wxlistbox_idle_struct*) gdata;
+ gdk_threads_enter();
+
+ gtk_idle_remove( data->m_tag );
+
+ // check that the items haven't been deleted from the listbox since we had
+ // installed this callback
+ wxListBox *lbox = data->m_listbox;
+ if ( data->m_item < lbox->GetCount() )
+ {
+ lbox->SetFirstItem( data->m_item );
+ }
+
+ delete data;
+
+ gdk_threads_leave();
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// "focus_in_event"
+//-----------------------------------------------------------------------------
+
+static gint gtk_listitem_focus_in_callback( GtkWidget *widget,
+ GdkEvent *WXUNUSED(event),
+ wxWindow *win )
+{
+ if (g_isIdle)
+ wxapp_install_idle_handler();
+
+ g_focusWindowLast =
+ g_focusWindow = win;
+
+ // does the window itself think that it has the focus?
+ if ( !win->m_hasFocus )
+ {
+ // not yet, notify it
+ win->m_hasFocus = TRUE;
+
+ wxChildFocusEvent eventChildFocus(win);
+ (void)win->GetEventHandler()->ProcessEvent(eventChildFocus);
+
+ wxFocusEvent eventFocus(wxEVT_SET_FOCUS, win->GetId());
+ eventFocus.SetEventObject(win);
+
+ (void)win->GetEventHandler()->ProcessEvent(eventFocus);
+ }
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// "focus_out_event"
+//-----------------------------------------------------------------------------
+
+static gint gtk_listitem_focus_out_callback( GtkWidget *widget, GdkEventFocus *gdk_event, wxWindowGTK *win )
+{
+ if (g_isIdle)
+ wxapp_install_idle_handler();
+
+ g_focusWindow = (wxWindowGTK *)NULL;
+
+ // don't send the window a kill focus event if it thinks that it doesn't
+ // have focus already
+ if ( win->m_hasFocus )
+ {
+ win->m_hasFocus = FALSE;
+
+ wxFocusEvent event( wxEVT_KILL_FOCUS, win->GetId() );
+ event.SetEventObject( win );
+
+ // even if we did process the event in wx code, still let GTK itself
+ // process it too as otherwise bad things happen, especially in GTK2
+ // where the text control simply aborts the program if it doesn't get
+ // the matching focus out event
+ (void)win->GetEventHandler()->ProcessEvent( event );
+ }
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// "button_release_event"
+//-----------------------------------------------------------------------------
+
+/* we would normally emit a wxEVT_COMMAND_LISTBOX_DOUBLECLICKED event once
+ a GDK_2BUTTON_PRESS occurs, but this has the particular problem of the
+ listbox keeping the focus until it receives a GDK_BUTTON_RELEASE event.
+ this can lead to race conditions so that we emit the dclick event
+ after the GDK_BUTTON_RELEASE event after the GDK_2BUTTON_PRESS event */
+
+static gint
+gtk_listbox_button_release_callback( GtkWidget * WXUNUSED(widget),
+ GdkEventButton * WXUNUSED(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;
+
+ if (!g_hasDoubleClicked) return FALSE;
+
+ wxCommandEvent event( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, listbox->GetId() );
+ event.SetEventObject( listbox );
+
+ wxArrayInt aSelections;
+ int n, count = listbox->GetSelections(aSelections);
+ if ( count > 0 )
+ {
+ n = aSelections[0];
+ if ( listbox->HasClientObjectData() )
+ event.SetClientObject( listbox->GetClientObject(n) );
+ else if ( listbox->HasClientUntypedData() )
+ event.SetClientData( listbox->GetClientData(n) );
+ event.SetString( listbox->GetString(n) );
+ }
+ else
+ {
+ n = -1;
+ }
+
+ event.m_commandInt = n;
+
+ listbox->GetEventHandler()->ProcessEvent( event );
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// "button_press_event"
+//-----------------------------------------------------------------------------
+
+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;
+
+ int sel = listbox->GtkGetIndex( widget );
+
+#if wxUSE_CHECKLISTBOX
+ if ((listbox->m_hasCheckBoxes) && (gdk_event->x < 15) && (gdk_event->type != GDK_2BUTTON_PRESS))
+ {
+ 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
+
+ /* emit wxEVT_COMMAND_LISTBOX_DOUBLECLICKED later */
+ g_hasDoubleClicked = (gdk_event->type == GDK_2BUTTON_PRESS);
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// "key_press_event"
+//-----------------------------------------------------------------------------
+
+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
+ ret = TRUE;
+ }
+
+#if wxUSE_CHECKLISTBOX
+ if ((gdk_event->keyval == ' ') && (listbox->m_hasCheckBoxes) && (!ret))
+ {
+ int sel = listbox->GtkGetIndex( widget );
+
+ 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 );
+ ret = listbox->GetEventHandler()->ProcessEvent( new_event );
+ }
+#endif // wxUSE_CHECKLISTBOX
+
+ // Check or uncheck item with SPACE
+ if ((gdk_event->keyval == ' ') && (!ret) &&
+ (((listbox->GetWindowStyleFlag() & wxLB_MULTIPLE) != 0) ||
+ ((listbox->GetWindowStyleFlag() & wxLB_EXTENDED) != 0)) )
+ {
+ int sel = listbox->GtkGetIndex( widget );
+
+ if (sel != -1)
+ {
+ ret = TRUE;
+
+ if (listbox->IsSelected( sel ))
+ gtk_list_unselect_item( listbox->m_list, sel );
+ else
+ gtk_list_select_item( listbox->m_list, sel );
+
+ wxCommandEvent new_event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
+ new_event.SetEventObject( listbox );
+ wxArrayInt aSelections;
+ int n, count = listbox->GetSelections(aSelections);
+ if ( count > 0 )
+ {
+ n = aSelections[0];
+ if ( listbox->HasClientObjectData() )
+ new_event.SetClientObject( listbox->GetClientObject(n) );
+ else if ( listbox->HasClientUntypedData() )
+ new_event.SetClientData( listbox->GetClientData(n) );
+ new_event.SetString( listbox->GetString(n) );
+ }
+ else
+ {
+ n = -1;
+ }
+ new_event.m_commandInt = n;
+ listbox->GetEventHandler()->ProcessEvent( new_event );
+ }
+ }
+
+ if (ret)
+ {
+ gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// "select" and "deselect"
+//-----------------------------------------------------------------------------
+
+static void gtk_listitem_select_cb( GtkWidget *widget, wxListBox *listbox, bool is_selection );
+
+static void gtk_listitem_select_callback( GtkWidget *widget, wxListBox *listbox )
+{
+ gtk_listitem_select_cb( widget, listbox, TRUE );
+}
+
+static void gtk_listitem_deselect_callback( GtkWidget *widget, wxListBox *listbox )
+{
+ gtk_listitem_select_cb( widget, listbox, FALSE );
+}
+
+static void gtk_listitem_select_cb( GtkWidget *widget,
+ wxListBox *listbox,
+ bool is_selection )
+{
+ if (g_isIdle) wxapp_install_idle_handler();
+
+ if (!listbox->m_hasVMT) return;
+ if (g_blockEventsOnDrag) return;
+
+ if (listbox->m_blockEvent) return;
+
+ wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() );
+ event.SetEventObject( listbox );
+
+ // indicate whether this is a selection or a deselection
+ event.SetExtraLong( is_selection );
+
+ if ((listbox->GetWindowStyleFlag() & wxLB_SINGLE) != 0)
+ {
+ int sel = listbox->GtkGetIndex( widget );
+
+ if (listbox->m_prevSelection != sel)
+ gtk_list_unselect_item( listbox->m_list, listbox->m_prevSelection );
+
+ listbox->m_prevSelection = sel;
+ }
+
+ wxArrayInt aSelections;
+ int n, count = listbox->GetSelections(aSelections);
+ if ( count > 0 )
+ {
+ n = aSelections[0];
+ if ( listbox->HasClientObjectData() )
+ event.SetClientObject( listbox->GetClientObject(n) );
+ else if ( listbox->HasClientUntypedData() )
+ event.SetClientData( listbox->GetClientData(n) );
+ event.SetString( listbox->GetString(n) );
+ }
+ else
+ {
+ n = -1;
+ }
+
+ event.m_commandInt = n;
+
+// No longer required with new code in wxLB_SINGLE
+// listbox->GetEventHandler()->AddPendingEvent( event );
+ listbox->GetEventHandler()->ProcessEvent( event );
+}
+
+//-----------------------------------------------------------------------------
+// wxListBox