diff --git a/gtk/gtklistitemmanager.c b/gtk/gtklistitemmanager.c index 1e74c8d8e1..e7c11f483f 100644 --- a/gtk/gtklistitemmanager.c +++ b/gtk/gtklistitemmanager.c @@ -28,7 +28,7 @@ struct _GtkListItemManager GObject parent_instance; GtkWidget *widget; - GListModel *model; + GtkSelectionModel *model; GtkListItemFactory *factory; }; @@ -107,7 +107,7 @@ gtk_list_item_manager_get_factory (GtkListItemManager *self) void gtk_list_item_manager_set_model (GtkListItemManager *self, - GListModel *model) + GtkSelectionModel *model) { g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self)); g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model)); @@ -121,7 +121,7 @@ gtk_list_item_manager_set_model (GtkListItemManager *self, self->model = g_object_ref (model); } -GListModel * +GtkSelectionModel * gtk_list_item_manager_get_model (GtkListItemManager *self) { g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL); @@ -255,14 +255,16 @@ gtk_list_item_manager_acquire_list_item (GtkListItemManager *self, { GtkListItem *result; gpointer item; + gboolean selected; g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL); g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL); result = gtk_list_item_factory_create (self->factory); - item = g_list_model_get_item (self->model, position); - gtk_list_item_factory_bind (self->factory, result, position, item, FALSE); + item = g_list_model_get_item (G_LIST_MODEL (self->model), position); + selected = gtk_selection_model_is_selected (self->model, position); + gtk_list_item_factory_bind (self->factory, result, position, item, selected); g_object_unref (item); gtk_widget_insert_after (GTK_WIDGET (result), self->widget, prev_sibling); @@ -297,7 +299,7 @@ gtk_list_item_manager_try_reacquire_list_item (GtkListItemManager *self, g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL); /* XXX: can we avoid temporarily allocating items on failure? */ - item = g_list_model_get_item (self->model, position); + item = g_list_model_get_item (G_LIST_MODEL (self->model), position); if (g_hash_table_steal_extended (change->items, item, NULL, (gpointer *) &result)) { gtk_list_item_factory_update (self->factory, result, position, FALSE); @@ -334,9 +336,11 @@ gtk_list_item_manager_move_list_item (GtkListItemManager *self, GtkWidget *prev_sibling) { gpointer item; + gboolean selected; - item = g_list_model_get_item (self->model, position); - gtk_list_item_factory_bind (self->factory, GTK_LIST_ITEM (list_item), position, item, FALSE); + item = g_list_model_get_item (G_LIST_MODEL (self->model), position); + selected = gtk_selection_model_is_selected (self->model, position); + gtk_list_item_factory_bind (self->factory, GTK_LIST_ITEM (list_item), position, item, selected); gtk_widget_insert_after (list_item, _gtk_widget_get_parent (list_item), prev_sibling); g_object_unref (item); } @@ -355,10 +359,13 @@ gtk_list_item_manager_update_list_item (GtkListItemManager *self, GtkWidget *item, guint position) { + gboolean selected; + g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self)); g_return_if_fail (GTK_IS_LIST_ITEM (item)); - gtk_list_item_factory_update (self->factory, GTK_LIST_ITEM (item), position, FALSE); + selected = gtk_selection_model_is_selected (self->model, position); + gtk_list_item_factory_update (self->factory, GTK_LIST_ITEM (item), position, selected); } /* diff --git a/gtk/gtklistitemmanagerprivate.h b/gtk/gtklistitemmanagerprivate.h index d34e346a21..47a4434d7d 100644 --- a/gtk/gtklistitemmanagerprivate.h +++ b/gtk/gtklistitemmanagerprivate.h @@ -24,6 +24,7 @@ #include "gtk/gtktypes.h" #include "gtk/gtklistitemfactoryprivate.h" +#include "gtk/gtkselectionmodel.h" G_BEGIN_DECLS @@ -46,8 +47,8 @@ void gtk_list_item_manager_set_factory (GtkListItemMana GtkListItemFactory *factory); GtkListItemFactory * gtk_list_item_manager_get_factory (GtkListItemManager *self); void gtk_list_item_manager_set_model (GtkListItemManager *self, - GListModel *model); -GListModel * gtk_list_item_manager_get_model (GtkListItemManager *self); + GtkSelectionModel *model); +GtkSelectionModel * gtk_list_item_manager_get_model (GtkListItemManager *self); guint gtk_list_item_manager_get_size (GtkListItemManager *self); diff --git a/gtk/gtklistview.c b/gtk/gtklistview.c index 35cf3b6da3..494a2a591b 100644 --- a/gtk/gtklistview.c +++ b/gtk/gtklistview.c @@ -27,6 +27,8 @@ #include "gtklistitemfactoryprivate.h" #include "gtklistitemmanagerprivate.h" #include "gtkscrollable.h" +#include "gtkselectionmodel.h" +#include "gtksingleselection.h" #include "gtkwidgetprivate.h" /* Maximum number of list items created by the listview. @@ -908,7 +910,7 @@ gtk_list_view_model_items_changed_cb (GListModel *model, guint i, offset, anchor_pos; row = gtk_list_view_get_row (self, position, &offset); - for (new_row = gtk_rb_tree_node_get_previous (row); + for (new_row = row ? gtk_rb_tree_node_get_previous (row) : gtk_rb_tree_get_last (self->rows); new_row && new_row->widget == NULL; new_row = gtk_rb_tree_node_get_previous (new_row)) { } @@ -969,6 +971,9 @@ gtk_list_view_model_items_changed_cb (GListModel *model, anchor_pos = gtk_list_item_get_position (GTK_LIST_ITEM (self->anchor)); anchor_pos = position + (anchor_pos - position) * added / removed; + if (anchor_pos >= g_list_model_get_n_items (self->model) && + anchor_pos > 0) + anchor_pos--; } gtk_list_view_set_anchor (self, anchor_pos, self->anchor_align, change, position); } @@ -997,6 +1002,34 @@ gtk_list_view_model_items_changed_cb (GListModel *model, gtk_list_item_manager_end_change (self->item_manager, change); } +static void +gtk_list_view_model_selection_changed_cb (GListModel *model, + guint position, + guint n_items, + GtkListView *self) +{ + ListRow *row; + guint offset; + + row = gtk_list_view_get_row (self, position, &offset); + + if (offset) + { + position += row->n_rows - offset; + n_items -= row->n_rows - offset; + row = gtk_rb_tree_node_get_next (row); + } + + while (n_items > 0) + { + if (row->widget) + gtk_list_item_manager_update_list_item (self->item_manager, row->widget, position); + position += row->n_rows; + n_items -= MIN (n_items, row->n_rows); + row = gtk_rb_tree_node_get_next (row); + } +} + static void gtk_list_view_clear_model (GtkListView *self) { @@ -1005,6 +1038,9 @@ gtk_list_view_clear_model (GtkListView *self) gtk_list_view_remove_rows (self, NULL, 0, g_list_model_get_n_items (self->model)); + g_signal_handlers_disconnect_by_func (self->model, + gtk_list_view_model_selection_changed_cb, + self); g_signal_handlers_disconnect_by_func (self->model, gtk_list_view_model_items_changed_cb, self); @@ -1264,9 +1300,12 @@ gtk_list_view_get_model (GtkListView *self) /** * gtk_list_view_set_model: * @self: a #GtkListView - * @file: (allow-none) (transfer none): the model to use or %NULL for none + * @model: (allow-none) (transfer none): the model to use or %NULL for none * - * Sets the #GListModel to use for + * Sets the #GListModel to use. + * + * If the @model is a #GtkSelectionModel, it is used for managing the selection. + * Otherwise, @self creates a #GtkSingleSelection for the selection. **/ void gtk_list_view_set_model (GtkListView *self, @@ -1280,20 +1319,38 @@ gtk_list_view_set_model (GtkListView *self, gtk_list_view_clear_model (self); - gtk_list_item_manager_set_model (self->item_manager, model); - if (model) { + GtkSelectionModel *selection_model; + self->model = g_object_ref (model); + if (GTK_IS_SELECTION_MODEL (model)) + selection_model = GTK_SELECTION_MODEL (g_object_ref (model)); + else + selection_model = GTK_SELECTION_MODEL (gtk_single_selection_new (model)); + + gtk_list_item_manager_set_model (self->item_manager, selection_model); + g_signal_connect (model, "items-changed", G_CALLBACK (gtk_list_view_model_items_changed_cb), self); + g_signal_connect (selection_model, + "selection-changed", + G_CALLBACK (gtk_list_view_model_selection_changed_cb), + self); + + g_object_unref (selection_model); gtk_list_view_add_rows (self, 0, g_list_model_get_n_items (model)); gtk_list_view_set_anchor (self, 0, 0, NULL, (guint) -1); } + else + { + gtk_list_item_manager_set_model (self->item_manager, NULL); + } + g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]); }