diff -r aaa984be3a68 -r ef50e0dc74b0 pidgin/pidginconversationwindow.c --- a/pidgin/pidginconversationwindow.c Mon Jan 24 20:16:44 2022 -0600 +++ b/pidgin/pidginconversationwindow.c Mon Jan 24 20:29:28 2022 -0600 @@ -20,6 +20,8 @@ * along with this program; if not, see . */ +#include + #include "pidginconversationwindow.h" #include "gtkconv.h" @@ -45,6 +47,8 @@ GtkTreeStore *model; GtkWidget *stack; + + GtkTreePath *conversation_path; }; G_DEFINE_TYPE(PidginConversationWindow, pidgin_conversation_window, @@ -83,27 +87,6 @@ } static gboolean -pidgin_conversation_window_foreach_destroy(GtkTreeModel *model, - GtkTreePath *path, - GtkTreeIter *iter, - gpointer data) -{ - PurpleConversation *conversation = NULL; - - gtk_tree_model_get(model, iter, - PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT, &conversation, - -1); - - if(conversation != NULL) { - pidgin_conversation_detach(conversation); - - gtk_list_store_remove(GTK_LIST_STORE(model), iter); - } - - return FALSE; -} - -static gboolean pidgin_conversation_window_key_pressed_cb(GtkEventControllerKey *controller, guint keyval, G_GNUC_UNUSED guint keycode, @@ -152,8 +135,32 @@ PidginConversationWindow *window = PIDGIN_CONVERSATION_WINDOW(obj); if(GTK_IS_TREE_MODEL(window->model)) { - gtk_tree_model_foreach(GTK_TREE_MODEL(window->model), - pidgin_conversation_window_foreach_destroy, window); + GtkTreeModel *model = GTK_TREE_MODEL(window->model); + GtkTreeIter parent, iter; + + gtk_tree_model_get_iter(model, &parent, window->conversation_path); + if(gtk_tree_model_iter_children(model, &iter, &parent)) { + gboolean valid = FALSE; + + /* gtk_tree_store_remove moves the iter to the next item at the + * same level, so we abuse that to do our iteration. + */ + do { + PurpleConversation *conversation = NULL; + + gtk_tree_model_get(model, &iter, + PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT, &conversation, + -1); + + if(PURPLE_IS_CONVERSATION(conversation)) { + pidgin_conversation_detach(conversation); + + valid = gtk_tree_store_remove(window->model, &iter); + } + } while(valid); + } + + g_clear_pointer(&window->conversation_path, gtk_tree_path_free); } G_OBJECT_CLASS(pidgin_conversation_window_parent_class)->dispose(obj); @@ -162,6 +169,7 @@ static void pidgin_conversation_window_init(PidginConversationWindow *window) { GtkEventController *key = NULL; + GtkTreeIter iter; gtk_widget_init_template(GTK_WIDGET(window)); @@ -175,6 +183,14 @@ window); g_object_set_data_full(G_OBJECT(window), "key-press-controller", key, g_object_unref); + + /* Add our toplevels to the tree store. */ + gtk_tree_store_append(window->model, &iter, NULL); + gtk_tree_store_set(window->model, &iter, + PIDGIN_CONVERSATION_WINDOW_COLUMN_MARKUP, _("Conversations"), + -1); + window->conversation_path = gtk_tree_model_get_path(GTK_TREE_MODEL(window->model), + &iter); } static void @@ -241,12 +257,26 @@ PurpleConversation *conversation) { PidginConversation *gtkconv = NULL; - GtkTreeIter iter; + GtkTreeIter parent, iter; + GtkTreeModel *model = NULL; const gchar *markup = NULL; + gboolean expand = FALSE; g_return_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window)); g_return_if_fail(PURPLE_IS_CONVERSATION(conversation)); + model = GTK_TREE_MODEL(window->model); + if(!gtk_tree_model_get_iter(model, &parent, window->conversation_path)) { + /* If we can't find the conversation_path we have to bail. */ + g_warning("couldn't get an iterator to conversation_path"); + + return; + } + + if(!gtk_tree_model_iter_has_child(model, &parent)) { + expand = TRUE; + } + markup = purple_conversation_get_name(conversation); gtkconv = PIDGIN_CONVERSATION(conversation); @@ -266,12 +296,19 @@ } } - gtk_tree_store_prepend(window->model, &iter, NULL); + gtk_tree_store_prepend(window->model, &iter, &parent); gtk_tree_store_set(window->model, &iter, PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT, conversation, PIDGIN_CONVERSATION_WINDOW_COLUMN_MARKUP, markup, -1); + /* If we just added the first child, expand the parent. */ + if(expand) { + gtk_tree_view_expand_row(GTK_TREE_VIEW(window->view), + window->conversation_path, FALSE); + } + + if(!gtk_widget_is_visible(GTK_WIDGET(window))) { gtk_widget_show_all(GTK_WIDGET(window)); } @@ -281,19 +318,27 @@ pidgin_conversation_window_remove(PidginConversationWindow *window, PurpleConversation *conversation) { - GtkTreeIter iter; + GtkTreeIter parent, iter; + GtkTreeModel *model = NULL; GObject *obj = NULL; g_return_if_fail(PIDGIN_IS_CONVERSATION_WINDOW(window)); g_return_if_fail(PURPLE_IS_CONVERSATION(conversation)); - if(!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(window->model), &iter)) { - /* The tree is empty. */ + model = GTK_TREE_MODEL(window->model); + + if(!gtk_tree_model_get_iter(model, &parent, window->conversation_path)) { + /* The path is somehow invalid, so bail... */ + return; + } + + if(!gtk_tree_model_iter_children(model, &iter, &parent)) { + /* The conversations iter has no children. */ return; } do { - gtk_tree_model_get(GTK_TREE_MODEL(window->model), &iter, + gtk_tree_model_get(model, &iter, PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT, &obj, -1); @@ -306,7 +351,7 @@ } g_clear_object(&obj); - } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(window->model), &iter)); + } while(gtk_tree_model_iter_next(model, &iter)); } guint