X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/a2e4a628eeebe7af896a0981759d643b99f8bb78..a9e2e6e52b9c2371413597a92ccc4f8352963c32:/src/gtk/listbox.cpp diff --git a/src/gtk/listbox.cpp b/src/gtk/listbox.cpp index 53d9569aa1..842f157f5c 100644 --- a/src/gtk/listbox.cpp +++ b/src/gtk/listbox.cpp @@ -32,7 +32,6 @@ #include "wx/tooltip.h" #endif -#include #include #include @@ -63,13 +62,11 @@ extern bool g_blockEventsOnScroll; extern "C" { static void -gtk_listbox_row_activated_callback(GtkTreeView *treeview, +gtk_listbox_row_activated_callback(GtkTreeView * WXUNUSED(treeview), GtkTreePath *path, - GtkTreeViewColumn *col, + GtkTreeViewColumn * WXUNUSED(col), wxListBox *listbox) { - if (g_isIdle) wxapp_install_idle_handler(); - if (g_blockEventsOnDrag) return; if (g_blockEventsOnScroll) return; @@ -107,7 +104,7 @@ gtk_listbox_row_activated_callback(GtkTreeView *treeview, event.SetInt(-1); } - listbox->GetEventHandler()->ProcessEvent( event ); + listbox->HandleWindowEvent( event ); } } @@ -117,54 +114,86 @@ gtk_listbox_row_activated_callback(GtkTreeView *treeview, extern "C" { static void -gtk_listitem_changed_callback( GtkTreeSelection* selection, wxListBox *listbox ) +gtk_listitem_changed_callback(GtkTreeSelection * WXUNUSED(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)) + if (listbox->HasFlag(wxLB_MULTIPLE | wxLB_EXTENDED)) + { + listbox->CalcAndSendEvent(); + } + else // single selection { - wxArrayInt selections; - listbox->GetSelections( selections ); + wxCommandEvent event(wxEVT_COMMAND_LISTBOX_SELECTED, listbox->GetId() ); + event.SetEventObject( listbox ); - if (selections.GetCount() == 0) + int index = listbox->GetSelection(); + if (index == wxNOT_FOUND) { // indicate that this is a deselection event.SetExtraLong( 0 ); event.SetInt( -1 ); - listbox->GetEventHandler()->ProcessEvent( event ); + listbox->HandleWindowEvent( event ); return; } else { + GtkTreeEntry* entry = listbox->GtkGetEntry( index ); + // indicate that this is a selection event.SetExtraLong( 1 ); - event.SetInt( selections[0] ); - listbox->GetEventHandler()->ProcessEvent( event ); + 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->HandleWindowEvent( event ); + + g_object_unref (entry); } } - 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 ); +//----------------------------------------------------------------------------- +// "key_press_event" +//----------------------------------------------------------------------------- - return; - } +extern "C" { +static gboolean +gtk_listbox_key_press_callback( GtkWidget *WXUNUSED(widget), + GdkEventKey *gdk_event, + wxListBox *listbox ) +{ + if ((gdk_event->keyval == GDK_Return) || + (gdk_event->keyval == GDK_ISO_Enter) || + (gdk_event->keyval == GDK_KP_Enter)) + { + int index = -1; + if (!listbox->HasMultipleSelection()) + index = listbox->GetSelection(); else { + wxArrayInt sels; + if (listbox->GetSelections( sels ) < 1) + return FALSE; + index = sels[0]; + } + + if (index != wxNOT_FOUND) + { + wxCommandEvent event(wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, listbox->GetId() ); + event.SetEventObject( listbox ); + GtkTreeEntry* entry = listbox->GtkGetEntry( index ); // indicate that this is a selection @@ -180,11 +209,30 @@ gtk_listitem_changed_callback( GtkTreeSelection* selection, wxListBox *listbox ) else if ( listbox->HasClientUntypedData() ) event.SetClientData( gtk_tree_entry_get_userdata(entry) ); - listbox->GetEventHandler()->ProcessEvent( event ); + /* bool ret = */ listbox->HandleWindowEvent( event ); g_object_unref (entry); + +// wxMac and wxMSW always invoke default action +// if (!ret) + { + // DClick not handled -> invoke default action + wxWindow *tlw = wxGetTopLevelParent( listbox ); + if (tlw) + { + GtkWindow *gtk_window = GTK_WINDOW( tlw->GetHandle() ); + if (gtk_window) + gtk_window_activate_default( gtk_window ); + } + } + + // Always intercept, otherwise we'd get another dclick + // event from row_activated + return TRUE; } } + + return FALSE; } } @@ -210,7 +258,7 @@ static void gtk_tree_entry_destroy_cb(GtkTreeEntry* entry, //----------------------------------------------------------------------------- extern "C" { -static gint gtk_listbox_sort_callback(GtkTreeModel *model, +static gint gtk_listbox_sort_callback(GtkTreeModel * WXUNUSED(model), GtkTreeIter *a, GtkTreeIter *b, wxListBox *listbox) @@ -231,8 +279,8 @@ static gint gtk_listbox_sort_callback(GtkTreeModel *model, //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)); + 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); @@ -246,8 +294,8 @@ static gint gtk_listbox_sort_callback(GtkTreeModel *model, //----------------------------------------------------------------------------- extern "C" { -static gboolean gtk_listbox_searchequal_callback(GtkTreeModel* model, - gint column, +static gboolean gtk_listbox_searchequal_callback(GtkTreeModel * WXUNUSED(model), + gint WXUNUSED(column), const gchar* key, GtkTreeIter* iter, wxListBox* listbox) @@ -261,8 +309,8 @@ static gboolean gtk_listbox_searchequal_callback(GtkTreeModel* model, wxCHECK_MSG(entry, 0, wxT("Could not get entry")); wxGtkString keycollatekey(g_utf8_collate_key(key, -1)); - int ret = strcasecmp(keycollatekey, - gtk_tree_entry_get_collate_key(entry)); + int ret = strcmp(keycollatekey, + gtk_tree_entry_get_collate_key(entry)); g_object_unref (entry); @@ -274,7 +322,7 @@ static gboolean gtk_listbox_searchequal_callback(GtkTreeModel* model, // wxListBox //----------------------------------------------------------------------------- -IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl) +IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControlWithItems) // ---------------------------------------------------------------------------- // construction @@ -282,7 +330,7 @@ IMPLEMENT_DYNAMIC_CLASS(wxListBox, wxControl) void wxListBox::Init() { - m_treeview = (GtkTreeView*) NULL; + m_treeview = NULL; #if wxUSE_CHECKLISTBOX m_hasCheckBoxes = false; #endif // wxUSE_CHECKLISTBOX @@ -306,9 +354,6 @@ bool wxListBox::Create( wxWindow *parent, wxWindowID id, long style, const wxValidator& validator, const wxString &name ) { - m_needParent = true; - m_blockEvent = false; - if (!PreCreation( parent, pos, size ) || !CreateBase( parent, id, pos, size, style, validator, name )) { @@ -316,7 +361,8 @@ bool wxListBox::Create( wxWindow *parent, wxWindowID id, return false; } - m_widget = gtk_scrolled_window_new( (GtkAdjustment*) NULL, (GtkAdjustment*) NULL ); + m_widget = gtk_scrolled_window_new( NULL, NULL ); + g_object_ref(m_widget); if (style & wxLB_ALWAYS_SB) { gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW(m_widget), @@ -329,7 +375,7 @@ bool wxListBox::Create( wxWindow *parent, wxWindowID id, } - GtkScrolledWindowSetBorder(m_widget, style); + GTKScrolledWindowSetBorder(m_widget, style); m_treeview = GTK_TREE_VIEW( gtk_tree_view_new( ) ); @@ -375,32 +421,23 @@ bool wxListBox::Create( wxWindow *parent, wxWindowID id, 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) + // GTK_SELECTION_EXTENDED is a deprecated synonym for GTK_SELECTION_MULTIPLE + if ( style & (wxLB_MULTIPLE | wxLB_EXTENDED) ) { mode = GTK_SELECTION_MULTIPLE; } - else if (style & wxLB_EXTENDED) + else // no multi-selection flags specified { - mode = GTK_SELECTION_EXTENDED; - } - else - { - // if style was 0 set single mode m_windowStyle |= wxLB_SINGLE; mode = GTK_SELECTION_SINGLE; } + GtkTreeSelection* selection = gtk_tree_view_get_selection( m_treeview ); gtk_tree_selection_set_mode( selection, mode ); // Handle sortable stuff - if(style & wxLB_SORT) + if(HasFlag(wxLB_SORT)) { // Setup sorting in ascending (wx) order gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(m_liststore), @@ -422,17 +459,24 @@ bool wxListBox::Create( wxWindow *parent, wxWindowID id, gtk_widget_show( GTK_WIDGET(m_treeview) ); m_focusWidget = GTK_WIDGET(m_treeview); - wxListBox::DoInsertItems(wxArrayString(n, choices), 0); // insert initial items + 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); + // for intercepting dclick generation by + g_signal_connect (m_treeview, "key_press_event", + G_CALLBACK (gtk_listbox_key_press_callback), + this); m_parent->DoAddChild( this ); PostCreation(size); SetInitialSize(size); // need this too because this is a wxControlWithItems + g_signal_connect_after (selection, "changed", + G_CALLBACK (gtk_listitem_changed_callback), this); + return true; } @@ -443,116 +487,114 @@ wxListBox::~wxListBox() Clear(); } +void wxListBox::GtkDisableEvents() +{ + GtkTreeSelection* selection = gtk_tree_view_get_selection( m_treeview ); + + g_signal_handlers_block_by_func(selection, + (gpointer) gtk_listitem_changed_callback, this); +} + +void wxListBox::GtkEnableEvents() +{ + GtkTreeSelection* selection = gtk_tree_view_get_selection( m_treeview ); + + g_signal_handlers_unblock_by_func(selection, + (gpointer) gtk_listitem_changed_callback, this); + + UpdateOldSelections(); +} + + +void wxListBox::Update() +{ + wxWindow::Update(); + + if (m_treeview) + gdk_window_process_updates(GTK_WIDGET(m_treeview)->window, TRUE); +} + // ---------------------------------------------------------------------------- // adding items // ---------------------------------------------------------------------------- -void wxListBox::GtkInsertItems(const wxArrayString& items, - void** clientData, unsigned int pos) +int wxListBox::DoInsertItems(const wxArrayStringsAdapter& items, + unsigned int pos, + void **clientData, + wxClientDataType type) { - wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") ); + wxCHECK_MSG( m_treeview != NULL, wxNOT_FOUND, wxT("invalid listbox") ); InvalidateBestSize(); - // Create and set column ids and GValues - - unsigned int nNum = items.GetCount(); - unsigned int nCurCount = wxListBox::GetCount(); - wxASSERT_MSG(pos <= nCurCount, wxT("Invalid index passed to wxListBox")); - GtkTreeIter* pIter = NULL; // append by default GtkTreeIter iter; - if (pos != nCurCount) + if ( pos != GetCount() ) { - wxCHECK_RET( GtkGetIteratorFor(pos, &iter), + wxCHECK_MSG( GtkGetIteratorFor(pos, &iter), wxNOT_FOUND, wxT("internal wxListBox error in insertion") ); pIter = &iter; } - for (unsigned int i = 0; i < nNum; ++i) + const unsigned int numItems = items.GetCount(); + for ( unsigned int i = 0; i < numItems; ++i ) { - wxString label = items[i]; - GtkTreeEntry* entry = gtk_tree_entry_new(); - gtk_tree_entry_set_label(entry, wxGTK_CONV(label)); + gtk_tree_entry_set_label(entry, wxGTK_CONV(items[i])); gtk_tree_entry_set_destroy_func(entry, (GtkTreeEntryDestroy)gtk_tree_entry_destroy_cb, this); - if (clientData) - gtk_tree_entry_set_userdata(entry, clientData[i]); - GtkTreeIter itercur; gtk_list_store_insert_before(m_liststore, &itercur, pIter); GtkSetItem(itercur, entry); g_object_unref (entry); - } -} - -void wxListBox::DoInsertItems(const wxArrayString& items, unsigned int pos) -{ - wxCHECK_RET( IsValidInsert(pos), wxT("invalid index in wxListBox::InsertItems") ); - GtkInsertItems(items, NULL, pos); -} - -int wxListBox::DoAppend( const wxString& item ) -{ - wxCHECK_MSG( m_treeview != NULL, -1, wxT("invalid listbox") ); - - InvalidateBestSize(); - - GtkTreeEntry* entry = gtk_tree_entry_new(); - gtk_tree_entry_set_label( entry, wxGTK_CONV(item) ); - gtk_tree_entry_set_destroy_func(entry, - (GtkTreeEntryDestroy)gtk_tree_entry_destroy_cb, - this); - - GtkTreeIter itercur; - gtk_list_store_insert_before( m_liststore, &itercur, NULL ); - - GtkSetItem(itercur, entry); + if (clientData) + AssignNewItemClientData(GtkGetIndexFor(itercur), clientData, i, type); + } - g_object_unref (entry); + UpdateOldSelections(); - return GtkGetIndexFor(itercur); -} - -void wxListBox::DoSetItems( const wxArrayString& items, - void **clientData) -{ - Clear(); - GtkInsertItems(items, clientData, 0); + return pos + numItems - 1; } // ---------------------------------------------------------------------------- // deleting items // ---------------------------------------------------------------------------- -void wxListBox::Clear() +void wxListBox::DoClear() { wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") ); + GtkDisableEvents(); // just in case + InvalidateBestSize(); gtk_list_store_clear( m_liststore ); /* well, THAT was easy :) */ + + GtkEnableEvents(); } -void wxListBox::Delete(unsigned int n) +void wxListBox::DoDeleteOneItem(unsigned int n) { wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") ); InvalidateBestSize(); + GtkDisableEvents(); // just in case + GtkTreeIter iter; wxCHECK_RET( GtkGetIteratorFor(n, &iter), wxT("wrong listbox index") ); // this returns false if iter is invalid (e.g. deleting item at end) but // since we don't use iter, we ignore the return value gtk_list_store_remove(m_liststore, &iter); + + GtkEnableEvents(); } // ---------------------------------------------------------------------------- @@ -636,11 +678,6 @@ void* wxListBox::DoGetItemClientData(unsigned int n) const return userdata; } -wxClientData* wxListBox::DoGetItemClientObject(unsigned int n) const -{ - return (wxClientData*) wxListBox::DoGetItemClientData(n); -} - void wxListBox::DoSetItemClientData(unsigned int n, void* clientData) { wxCHECK_RET( IsValid(n), @@ -653,12 +690,6 @@ void wxListBox::DoSetItemClientData(unsigned int n, void* clientData) g_object_unref (entry); } -void wxListBox::DoSetItemClientObject(unsigned int n, wxClientData* clientData) -{ - // wxItemContainer already deletes data for us - wxListBox::DoSetItemClientData(n, (void*) clientData); -} - // ---------------------------------------------------------------------------- // string list access // ---------------------------------------------------------------------------- @@ -747,7 +778,7 @@ int wxListBox::GetSelections( wxArrayInt& aSelections ) const aSelections.Empty(); - int i = 0; + int i = 0; GtkTreeIter iter; GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview); @@ -779,53 +810,42 @@ bool wxListBox::IsSelected( int n ) const void wxListBox::DoSetSelection( int n, bool select ) { + wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") ); + + GtkDisableEvents(); + + GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview); + // passing -1 to SetSelection() is documented to deselect all items if ( n == wxNOT_FOUND ) { - // ... and not generate any events in the process - GtkDeselectAll(); + gtk_tree_selection_unselect_all(selection); + GtkEnableEvents(); return; } wxCHECK_RET( IsValid(n), wxT("invalid index in wxListBox::SetSelection") ); - // don't generate the selection event - GtkSetSelection(n, select, true); -} - -void wxListBox::GtkDeselectAll() -{ - wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") ); - - GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview); - - m_blockEvent = true; - - gtk_tree_selection_unselect_all(selection); - - m_blockEvent = false; -} - -void wxListBox::GtkSetSelection(int n, const bool select, const bool blockEvent) -{ - wxCHECK_RET( m_treeview != NULL, wxT("invalid listbox") ); - - GtkTreeSelection* selection = gtk_tree_view_get_selection(m_treeview); - + GtkTreeIter iter; wxCHECK_RET( GtkGetIteratorFor(n, &iter), wxT("Invalid index") ); - m_blockEvent = blockEvent; - if (select) gtk_tree_selection_select_iter(selection, &iter); else gtk_tree_selection_unselect_iter(selection, &iter); - m_blockEvent = false; + GtkTreePath* path = gtk_tree_model_get_path( + GTK_TREE_MODEL(m_liststore), &iter); + + gtk_tree_view_scroll_to_cell(m_treeview, path, NULL, FALSE, 0.0f, 0.0f); + + gtk_tree_path_free(path); + + GtkEnableEvents(); } -void wxListBox::DoSetFirstItem( int n ) +void wxListBox::DoScrollToCell(int n, float alignY, float alignX) { wxCHECK_RET( m_treeview, wxT("invalid listbox") ); wxCHECK_RET( IsValid(n), wxT("invalid index")); @@ -843,11 +863,21 @@ void wxListBox::DoSetFirstItem( int n ) // Scroll to the desired cell (0.0 == topleft alignment) gtk_tree_view_scroll_to_cell(m_treeview, path, NULL, - TRUE, 0.0f, 0.0f); + TRUE, alignY, alignX); gtk_tree_path_free(path); } +void wxListBox::DoSetFirstItem(int n) +{ + DoScrollToCell(n, 0, 0); +} + +void wxListBox::EnsureVisible(int n) +{ + DoScrollToCell(n, 0.5, 0); +} + // ---------------------------------------------------------------------------- // hittest // ---------------------------------------------------------------------------- @@ -890,10 +920,10 @@ int wxListBox::DoListHitTest(const wxPoint& point) const // ---------------------------------------------------------------------------- #if wxUSE_TOOLTIPS -void wxListBox::ApplyToolTip( GtkTooltips *tips, const wxChar *tip ) +void wxListBox::GTKApplyToolTip( GtkTooltips *tips, const gchar *tip ) { // RN: Is this needed anymore? - gtk_tooltips_set_tip( tips, GTK_WIDGET( m_treeview ), wxGTK_CONV(tip), (gchar*) NULL ); + gtk_tooltips_set_tip( tips, GTK_WIDGET( m_treeview ), tip, NULL ); } #endif // wxUSE_TOOLTIPS @@ -932,43 +962,40 @@ wxSize wxListBox::DoGetBestSize() const // Start with a minimum size that's not too small int cx, cy; GetTextExtent( wxT("X"), &cx, &cy); - int lbWidth = 3 * cx; + int lbWidth = 0; int lbHeight = 10; - // Get the visible area of the tree view (limit to the 10th item - // so that it isn't too big) - unsigned int count = GetCount(); - if (count) + // Find the widest string. + const unsigned int count = GetCount(); + if ( count ) { int wLine; - - // Find the widest line - for(unsigned int i = 0; i < count; i++) { - wxString str(GetString(i)); - GetTextExtent(str, &wLine, NULL); - lbWidth = wxMax(lbWidth, wLine); + for ( unsigned int i = 0; i < count; i++ ) + { + GetTextExtent(GetString(i), &wLine, NULL); + if ( wLine > lbWidth ) + lbWidth = wLine; } + } - lbWidth += 3 * cx; + lbWidth += 3 * cx; - // And just a bit more for the checkbox if present and then some - // (these are rough guesses) + // And just a bit more for the checkbox if present and then some + // (these are rough guesses) #if wxUSE_CHECKLISTBOX - if ( m_hasCheckBoxes ) - { - lbWidth += 35; - cy = cy > 25 ? cy : 25; // rough height of checkbox - } -#endif - - // don't make the listbox too tall (limit height to around 10 items) but don't - // make it too small neither - lbHeight = (cy+4) * wxMin(wxMax(count, 3), 10); + if ( m_hasCheckBoxes ) + { + lbWidth += 35; + cy = cy > 25 ? cy : 25; // rough height of checkbox } +#endif // Add room for the scrollbar lbWidth += wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); + // Don't make the listbox too tall but don't make it too small neither + lbHeight = (cy+4) * wxMin(wxMax(count, 3), 10); + wxSize best(lbWidth, lbHeight); CacheBestSize(best); return best;