Rename pidginconversationwindow.[ch] to pidgindisplaywindow.[ch]
Testing Done:
Ran and made sure I could open a conversation.
Bugs closed: PIDGIN-17665
Reviewed at https://reviews.imfreedom.org/r/1792/
--- a/pidgin/gtkconv.c Sun Sep 18 22:36:13 2022 -0500
+++ b/pidgin/gtkconv.c Sun Sep 18 22:49:16 2022 -0500
@@ -46,8 +46,8 @@
#include "pidginavatar.h"
-#include "pidginconversationwindow.h"
+#include "pidgindisplaywindow.h" #include "pidgininfopane.h"
#include "pidgininvitedialog.h"
#include "pidginmessage.h"
--- a/pidgin/meson.build Sun Sep 18 22:36:13 2022 -0500
+++ b/pidgin/meson.build Sun Sep 18 22:49:16 2022 -0500
@@ -33,10 +33,10 @@
- 'pidginconversationwindow.c',
'pidgincontactlistwindow.c',
+ 'pidgindisplaywindow.c', @@ -102,10 +102,10 @@
- 'pidginconversationwindow.h',
'pidgincontactlistwindow.h',
+ 'pidgindisplaywindow.h', --- a/pidgin/pidginapplication.c Sun Sep 18 22:36:13 2022 -0500
+++ b/pidgin/pidginapplication.c Sun Sep 18 22:49:16 2022 -0500
@@ -46,9 +46,9 @@
#include "pidginaccountmanager.h"
#include "pidginaccountsdisabledmenu.h"
#include "pidginaccountsenabledmenu.h"
-#include "pidginconversationwindow.h"
+#include "pidgindisplaywindow.h" #include "pidginmooddialog.h"
#include "pidginpluginsdialog.h"
#include "pidginpluginsmenu.h"
--- a/pidgin/pidginconversationwindow.c Sun Sep 18 22:36:13 2022 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,822 +0,0 @@
- * Pidgin - Internet Messenger
- * Copyright (C) Pidgin Developers <devel@pidgin.im>
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <https://www.gnu.org/licenses/>.
-#include <glib/gi18n-lib.h>
-#include "pidginconversationwindow.h"
-#include "pidgininvitedialog.h"
- PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT,
- PIDGIN_DISPLAY_WINDOW_COLUMN_NAME,
- PIDGIN_DISPLAY_WINDOW_COLUMN_ICON,
- PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP,
- SIG_CONVERSATION_SWITCHED,
-static guint signals[N_SIGNALS] = {0, };
-struct _PidginDisplayWindow {
- GtkApplicationWindow parent;
- GtkTreeSelection *selection;
- GtkWidget *notification_list;
- GtkTreePath *conversation_path;
-G_DEFINE_TYPE(PidginDisplayWindow, pidgin_display_window,
- GTK_TYPE_APPLICATION_WINDOW)
-static GtkWidget *default_window = NULL;
-/******************************************************************************
- *****************************************************************************/
-pidgin_display_window_actions_set_enabled(GActionMap *map,
- for(i = 0; actions[i] != NULL; i++) {
- GAction *action = NULL;
- const gchar *name = actions[i];
- action = g_action_map_lookup_action(map, name);
- g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled);
- g_critical("Failed to find action named %s", name);
-/******************************************************************************
- *****************************************************************************/
-pidgin_display_window_invite_cb(GtkDialog *dialog, gint response_id,
- G_GNUC_UNUSED gpointer data)
- PidginInviteDialog *invite_dialog = PIDGIN_INVITE_DIALOG(dialog);
- PurpleChatConversation *chat = NULL;
- chat = pidgin_invite_dialog_get_conversation(invite_dialog);
- g_object_set_data(G_OBJECT(chat), "pidgin-invite-dialog", NULL);
- if(response_id == GTK_RESPONSE_ACCEPT) {
- const gchar *contact = NULL, *message = NULL;
- contact = pidgin_invite_dialog_get_contact(invite_dialog);
- message = pidgin_invite_dialog_get_message(invite_dialog);
- if(!purple_strequal(contact, "")) {
- PurpleConnection *connection = NULL;
- connection = purple_conversation_get_connection(PURPLE_CONVERSATION(chat));
- purple_serv_chat_invite(connection,
- purple_chat_conversation_get_id(chat),
- gtk_window_destroy(GTK_WINDOW(invite_dialog));
-/******************************************************************************
- *****************************************************************************/
-pidgin_display_window_alias(G_GNUC_UNUSED GSimpleAction *simple,
- G_GNUC_UNUSED GVariant *parameter,
- PidginDisplayWindow *window = data;
- PurpleConversation *selected = NULL;
- selected = pidgin_display_window_get_selected(window);
- if(PURPLE_IS_CONVERSATION(selected)) {
- PurpleAccount *account;
- account = purple_conversation_get_account(selected);
- name = purple_conversation_get_name(selected);
- if(PURPLE_IS_IM_CONVERSATION(selected)) {
- PurpleBuddy *buddy = purple_blist_find_buddy(account, name);
- if(PURPLE_IS_BUDDY(buddy)) {
- pidgin_dialogs_alias_buddy(buddy);
- } else if(PURPLE_IS_CHAT_CONVERSATION(selected)) {
- PurpleChat *chat = purple_blist_find_chat(account, name);
- if(PURPLE_IS_CHAT(chat)) {
- pidgin_dialogs_alias_chat(chat);
-pidgin_display_window_close_conversation(G_GNUC_UNUSED GSimpleAction *simple,
- G_GNUC_UNUSED GVariant *parameter,
- PidginDisplayWindow *window = data;
- PurpleConversation *selected = NULL;
- selected = pidgin_display_window_get_selected(window);
- if(PURPLE_IS_CONVERSATION(selected)) {
- pidgin_display_window_remove(window, selected);
- pidgin_conversation_detach(selected);
-pidgin_display_window_get_info(G_GNUC_UNUSED GSimpleAction *simple,
- G_GNUC_UNUSED GVariant *parameter,
- PidginDisplayWindow *window = data;
- PurpleConversation *selected = NULL;
- selected = pidgin_display_window_get_selected(window);
- if(PURPLE_IS_CONVERSATION(selected)) {
- if(PURPLE_IS_IM_CONVERSATION(selected)) {
- PurpleConnection *connection = NULL;
- connection = purple_conversation_get_connection(selected);
- pidgin_retrieve_user_info(connection,
- purple_conversation_get_name(selected));
-pidgin_display_window_invite(G_GNUC_UNUSED GSimpleAction *simple,
- G_GNUC_UNUSED GVariant *parameter,
- PidginDisplayWindow *window = data;
- PurpleConversation *selected = NULL;
- selected = pidgin_display_window_get_selected(window);
- if(PURPLE_IS_CHAT_CONVERSATION(selected)) {
- GtkWidget *invite_dialog = NULL;
- invite_dialog = g_object_get_data(G_OBJECT(selected),
- "pidgin-invite-dialog");
- if(!GTK_IS_WIDGET(invite_dialog)) {
- invite_dialog = pidgin_invite_dialog_new(PURPLE_CHAT_CONVERSATION(selected));
- g_object_set_data(G_OBJECT(selected), "pidgin-invite-dialog",
- gtk_window_set_transient_for(GTK_WINDOW(invite_dialog),
- gtk_window_set_destroy_with_parent(GTK_WINDOW(invite_dialog), TRUE);
- g_signal_connect(invite_dialog, "response",
- G_CALLBACK(pidgin_display_window_invite_cb),
- gtk_widget_show(invite_dialog);
-pidgin_display_window_send_file(G_GNUC_UNUSED GSimpleAction *simple,
- G_GNUC_UNUSED GVariant *parameter,
- PidginDisplayWindow *window = data;
- PurpleConversation *selected = NULL;
- selected = pidgin_display_window_get_selected(window);
- if(PURPLE_IS_IM_CONVERSATION(selected)) {
- PurpleConnection *connection = NULL;
- connection = purple_conversation_get_connection(selected);
- purple_serv_send_file(connection,
- purple_conversation_get_name(selected),
-static GActionEntry win_entries[] = {
- .activate = pidgin_display_window_alias
- .activate = pidgin_display_window_close_conversation
- .activate = pidgin_display_window_get_info
- .activate = pidgin_display_window_invite
- .activate = pidgin_display_window_send_file
- * pidgin_display_window_conversation_actions:
- * A list of action names that are only valid if a conversation is selected.
-static const gchar *pidgin_display_window_conversation_actions[] = {
-static const gchar *pidgin_display_window_im_conversation_actions[] = {
-static const gchar *pidgin_display_window_chat_conversation_actions[] = {
-/******************************************************************************
- *****************************************************************************/
-pidgin_display_window_selection_changed(GtkTreeSelection *selection,
- PidginDisplayWindow *window = PIDGIN_DISPLAY_WINDOW(data);
- GtkTreeModel *model = NULL;
- gboolean changed = FALSE;
- if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
- gboolean is_conversation = FALSE;
- gboolean im_selected = FALSE, chat_selected = FALSE;
- gtk_tree_model_get(model, &iter,
- PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, &name,
- PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &obj,
- adw_view_stack_set_visible_child_name(ADW_VIEW_STACK(window->stack),
- /* If a conversation is selected, enable the generic conversation
- is_conversation = PURPLE_IS_CONVERSATION(obj);
- pidgin_display_window_actions_set_enabled(G_ACTION_MAP(window),
- pidgin_display_window_conversation_actions,
- /* If an IM is selected, enable the IM-specific actions otherwise
- im_selected = PURPLE_IS_IM_CONVERSATION(obj);
- pidgin_display_window_actions_set_enabled(G_ACTION_MAP(window),
- pidgin_display_window_im_conversation_actions,
- /* If a chat is selected, enable the chat-specific actions otherwise
- chat_selected = PURPLE_IS_CHAT_CONVERSATION(obj);
- pidgin_display_window_actions_set_enabled(G_ACTION_MAP(window),
- pidgin_display_window_chat_conversation_actions,
- adw_view_stack_set_visible_child_name(ADW_VIEW_STACK(window->stack),
-pidgin_display_window_key_pressed_cb(GtkEventControllerKey *controller,
- G_GNUC_UNUSED guint keycode,
- PidginDisplayWindow *window = data;
- /* If CTRL was held down... */
- if (state & GDK_CONTROL_MASK) {
- case GDK_KEY_Page_Down:
- case GDK_KEY_KP_Page_Down:
- pidgin_display_window_select_next(window);
- case GDK_KEY_KP_Page_Up:
- pidgin_display_window_select_previous(window);
- /* If ALT (or whatever) was held down... */
- else if (state & GDK_ALT_MASK) {
- if ('1' <= keyval && keyval <= '9') {
- guint switchto = keyval - '1';
- pidgin_display_window_select_nth(window, switchto);
-/******************************************************************************
- * GObjectImplementation
- *****************************************************************************/
-pidgin_display_window_dispose(GObject *obj) {
- PidginDisplayWindow *window = PIDGIN_DISPLAY_WINDOW(obj);
- if(GTK_IS_TREE_MODEL(window->model)) {
- 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.
- PurpleConversation *conversation = NULL;
- gtk_tree_model_get(model, &iter,
- PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &conversation,
- if(PURPLE_IS_CONVERSATION(conversation)) {
- pidgin_conversation_detach(conversation);
- valid = gtk_tree_store_remove(window->model, &iter);
- g_clear_pointer(&window->conversation_path, gtk_tree_path_free);
- G_OBJECT_CLASS(pidgin_display_window_parent_class)->dispose(obj);
-pidgin_display_window_init(PidginDisplayWindow *window) {
- GtkEventController *key = NULL;
- gtk_widget_init_template(GTK_WIDGET(window));
- gtk_window_set_application(GTK_WINDOW(window),
- GTK_APPLICATION(g_application_get_default()));
- g_action_map_add_action_entries(G_ACTION_MAP(window), win_entries,
- G_N_ELEMENTS(win_entries), window);
- key = gtk_event_controller_key_new();
- gtk_event_controller_set_propagation_phase(key, GTK_PHASE_CAPTURE);
- g_signal_connect(G_OBJECT(key), "key-pressed",
- G_CALLBACK(pidgin_display_window_key_pressed_cb),
- gtk_widget_add_controller(GTK_WIDGET(window), key);
- /* Add our toplevels to the tree store. */
- gtk_tree_store_append(window->model, &iter, NULL);
- gtk_tree_store_set(window->model, &iter,
- PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, window->notification_list,
- PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, "__notifications__",
- PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, _("Notifications"),
- gtk_tree_store_append(window->model, &iter, NULL);
- gtk_tree_store_set(window->model, &iter,
- PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, _("Conversations"),
- PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, "__conversations__",
- gtk_tree_selection_select_iter(window->selection, &iter);
- window->conversation_path = gtk_tree_model_get_path(GTK_TREE_MODEL(window->model),
-pidgin_display_window_class_init(PidginDisplayWindowClass *klass) {
- GObjectClass *obj_class = G_OBJECT_CLASS(klass);
- GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
- obj_class->dispose = pidgin_display_window_dispose;
- * PidginDisplayWindow::conversation-switched:
- * @window: The conversation window.
- * @new_conv: The now active conversation.
- * Emitted when a window switched from one conversation to another.
- signals[SIG_CONVERSATION_SWITCHED] = g_signal_new_class_handler(
- "conversation-switched",
- G_OBJECT_CLASS_TYPE(obj_class),
- PURPLE_TYPE_CONVERSATION
- gtk_widget_class_set_template_from_resource(
- "/im/pidgin/Pidgin3/Conversations/window.ui"
- gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
- gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
- gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
- gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
- gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
- gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow,
- gtk_widget_class_bind_template_callback(widget_class,
- pidgin_display_window_selection_changed);
- gtk_widget_class_bind_template_callback(widget_class,
- pidgin_display_window_key_pressed_cb);
-/******************************************************************************
- *****************************************************************************/
-pidgin_display_window_get_default(void) {
- if(!GTK_IS_WIDGET(default_window)) {
- default_window = pidgin_display_window_new();
- g_object_add_weak_pointer(G_OBJECT(default_window),
- (gpointer)&default_window);
-pidgin_display_window_new(void) {
- PIDGIN_TYPE_DISPLAY_WINDOW,
-pidgin_display_window_add(PidginDisplayWindow *window,
- PurpleConversation *conversation)
- PidginConversation *gtkconv = NULL;
- GtkTreeIter parent, iter;
- GtkTreeModel *model = NULL;
- const gchar *markup = NULL;
- gboolean expand = FALSE;
- g_return_if_fail(PIDGIN_IS_DISPLAY_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");
- if(!gtk_tree_model_iter_has_child(model, &parent)) {
- markup = purple_conversation_get_name(conversation);
- gtkconv = PIDGIN_CONVERSATION(conversation);
- GtkWidget *parent = gtk_widget_get_parent(gtkconv->tab_cont);
- if(GTK_IS_WIDGET(parent)) {
- g_object_ref(gtkconv->tab_cont);
- gtk_widget_unparent(gtkconv->tab_cont);
- adw_view_stack_add_named(ADW_VIEW_STACK(window->stack),
- gtkconv->tab_cont, markup);
- gtk_widget_show(gtkconv->tab_cont);
- if(GTK_IS_WIDGET(parent)) {
- g_object_unref(gtkconv->tab_cont);
- gtk_tree_store_prepend(window->model, &iter, &parent);
- gtk_tree_store_set(window->model, &iter,
- PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, conversation,
- PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, markup,
- PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, markup,
- /* If we just added the first child, expand the parent. */
- 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(GTK_WIDGET(window));
-pidgin_display_window_remove(PidginDisplayWindow *window,
- PurpleConversation *conversation)
- GtkTreeIter parent, iter;
- GtkTreeModel *model = NULL;
- g_return_if_fail(PIDGIN_IS_DISPLAY_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)) {
- /* The path is somehow invalid, so bail... */
- if(!gtk_tree_model_iter_children(model, &iter, &parent)) {
- /* The conversations iter has no children. */
- gtk_tree_model_get(model, &iter,
- PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &obj,
- if(PURPLE_CONVERSATION(obj) == conversation) {
- GtkWidget *child = NULL;
- const gchar *name = NULL;
- name = purple_conversation_get_name(conversation);
- child = adw_view_stack_get_child_by_name(ADW_VIEW_STACK(window->stack),
- if(GTK_IS_WIDGET(child)) {
- gtk_widget_unparent(child);
- gtk_tree_store_remove(window->model, &iter);
- } while(gtk_tree_model_iter_next(model, &iter));
-pidgin_display_window_get_count(PidginDisplayWindow *window) {
- GtkSelectionModel *model = NULL;
- g_return_val_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window), 0);
- model = adw_view_stack_get_pages(ADW_VIEW_STACK(window->stack));
- count = g_list_model_get_n_items(G_LIST_MODEL(model));
-pidgin_display_window_get_selected(PidginDisplayWindow *window) {
- PurpleConversation *conversation = NULL;
- GtkTreeSelection *selection = NULL;
- g_return_val_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window), NULL);
- selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
- if(gtk_tree_selection_get_selected(selection, NULL, &iter)) {
- gtk_tree_model_get(GTK_TREE_MODEL(window->model), &iter,
- PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &conversation,
-pidgin_display_window_select(PidginDisplayWindow *window,
- PurpleConversation *conversation)
- const gchar *name = NULL;
- g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
- g_return_if_fail(PURPLE_IS_CONVERSATION(conversation));
- name = purple_conversation_get_name(conversation);
- adw_view_stack_set_visible_child_name(ADW_VIEW_STACK(window->stack), name);
-pidgin_display_window_select_previous(PidginDisplayWindow *window) {
- GtkTreeModel *model = NULL;
- GtkTreeSelection *selection = NULL;
- g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
- model = GTK_TREE_MODEL(window->model);
- selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
- if(gtk_tree_selection_get_selected(selection, NULL, &iter)) {
- if(gtk_tree_model_iter_previous(model, &iter)) {
- gtk_tree_selection_select_iter(selection, &iter);
- pidgin_display_window_select_last(window);
-pidgin_display_window_select_next(PidginDisplayWindow *window) {
- GtkTreeModel *model = NULL;
- GtkTreeSelection *selection = NULL;
- g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
- model = GTK_TREE_MODEL(window->model);
- selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
- if(gtk_tree_selection_get_selected(selection, NULL, &iter)) {
- if(gtk_tree_model_iter_next(model, &iter)) {
- gtk_tree_selection_select_iter(selection, &iter);
- pidgin_display_window_select_first(window);
-pidgin_display_window_select_first(PidginDisplayWindow *window) {
- GtkTreeModel *model = NULL;
- g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
- model = GTK_TREE_MODEL(window->model);
- if(gtk_tree_model_get_iter_first(model, &iter)) {
- GtkTreeSelection *selection = NULL;
- selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
- gtk_tree_selection_select_iter(selection, &iter);
-pidgin_display_window_select_last(PidginDisplayWindow *window) {
- GtkTreeModel *model = NULL;
- g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
- model = GTK_TREE_MODEL(window->model);
- count = gtk_tree_model_iter_n_children(model, NULL);
- if(gtk_tree_model_iter_nth_child(model, &iter, NULL, count - 1)) {
- GtkTreeSelection *selection = NULL;
- selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
- gtk_tree_selection_select_iter(selection, &iter);
-pidgin_display_window_select_nth(PidginDisplayWindow *window,
- GtkTreeModel *model = NULL;
- g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window));
- model = GTK_TREE_MODEL(window->model);
- if(gtk_tree_model_iter_nth_child(model, &iter, NULL, nth)) {
- GtkTreeSelection *selection = NULL;
- selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view));
- gtk_tree_selection_select_iter(selection, &iter);
-pidgin_display_window_conversation_is_selected(PidginDisplayWindow *window,
- PurpleConversation *conversation)
- const gchar *name = NULL, *visible = NULL;
- g_return_val_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window), FALSE);
- g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE);
- name = purple_conversation_get_name(conversation);
- visible = adw_view_stack_get_visible_child_name(ADW_VIEW_STACK(window->stack));
- return purple_strequal(name, visible);
--- a/pidgin/pidginconversationwindow.h Sun Sep 18 22:36:13 2022 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,204 +0,0 @@
- * Pidgin - Internet Messenger
- * Copyright (C) Pidgin Developers <devel@pidgin.im>
- * Pidgin is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <https://www.gnu.org/licenses/>.
-#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION)
-# error "only <pidgin.h> may be included directly"
-#ifndef PIDGIN_DISPLAY_WINDOW_H
-#define PIDGIN_DISPLAY_WINDOW_H
- * #PidginDisplayWindow is a widget that contains #PidginConversations.
-#define PIDGIN_TYPE_DISPLAY_WINDOW (pidgin_display_window_get_type())
-G_DECLARE_FINAL_TYPE(PidginDisplayWindow, pidgin_display_window,
- PIDGIN, DISPLAY_WINDOW, GtkApplicationWindow)
- * pidgin_display_window_new:
- * Creates a new #PidginDisplayWindow instance.
- * Returns: (transfer full): The new #PidginDisplayWindow instance.
-GtkWidget *pidgin_display_window_new(void);
- * pidgin_display_window_get_default:
- * Gets or creates the default conversation window.
- * Returns: (transfer none): The default #PidginDisplayWindow.
-GtkWidget *pidgin_display_window_get_default(void);
- * pidgin_display_window_add:
- * @window: The #PidginDisplayWindow instance.
- * @conversation: The #PurpleConversation to add to @window.
- * Adds @conversation to @window. If @conversation is already in @window, this
-void pidgin_display_window_add(PidginDisplayWindow *window, PurpleConversation *conversation);
- * pidgin_display_window_remove:
- * @window: The #PidginDisplayWindow instance.
- * @conversation: The #PurpleConversation to remove from @window.
- * Removes @conversation from @window. If @conversation is not in @window, this
-void pidgin_display_window_remove(PidginDisplayWindow *window, PurpleConversation *conversation);
- * pidgin_display_window_get_count:
- * @window: The conversation window instance.
- * Gets the number of conversations that @window contains.
- * Returns: The number of conversations that @window contains.
-guint pidgin_display_window_get_count(PidginDisplayWindow *window);
- * pidgin_display_window_get_selected:
- * @window: The conversation window instance.
- * Gets the currently selected PurpleConversation or %NULL if there is no
- * Returns: (transfer full): The selected PurpleConversation or %NULL.
-PurpleConversation *pidgin_display_window_get_selected(PidginDisplayWindow *window);
- * pidgin_display_window_select:
- * @window: The conversation window instance.
- * @conversation: The conversation to select.
- * Selects @conversation, making it the active conversation.
-void pidgin_display_window_select(PidginDisplayWindow *window, PurpleConversation *conversation);
- * pidgin_display_window_select_previous:
- * @window: The conversation window instance.
- * Switches to the conversation previous to the currently selected
- * If no conversation is selected, the last conversation will be selected.
-void pidgin_display_window_select_previous(PidginDisplayWindow *window);
- * pidgin_display_window_select_next:
- * @window: The conversation window instance.
- * Switches to the conversation next of the currently selected conversation.
- * If no conversation is selected, the first conversation will be selected.
-void pidgin_display_window_select_next(PidginDisplayWindow *window);
- * pidgin_display_window_select_first:
- * @window: The conversation window instance.
- * Selects the first conversation in @window. If there are no conversations in
- * @window this does nothing.
-void pidgin_display_window_select_first(PidginDisplayWindow *window);
- * pidgin_display_window_select_last:
- * @window: The conversation window instance.
- * Selects the last conversation in @window. If there are no conversations in
- * @window this does nothing.
-void pidgin_display_window_select_last(PidginDisplayWindow *window);
- * pidgin_display_window_select_nth:
- * @window: The conversation window instance.
- * @nth: The index of the conversation to switch to.
- * Switches to the @nth conversation. @nth is a 0 based index, so the first
- * conversation is at index 0.
-void pidgin_display_window_select_nth(PidginDisplayWindow *window, guint nth);
- * pidgin_display_window_conversation_is_selected:
- * @window: The conversation window instance.
- * @conversation: The conversation instance.
- * Checks whether @conversation is the active conversation in @window.
- * Returns: %TRUE if @conversation is active.
-gboolean pidgin_display_window_conversation_is_selected(PidginDisplayWindow *window, PurpleConversation *conversation);
-#endif /* PIDGIN_DISPLAY_WINDOW_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgindisplaywindow.c Sun Sep 18 22:49:16 2022 -0500
@@ -0,0 +1,822 @@
+ * Pidgin - Internet Messenger + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. +#include <glib/gi18n-lib.h> +#include "pidgindisplaywindow.h" +#include "pidgininvitedialog.h" + PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, + PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, + PIDGIN_DISPLAY_WINDOW_COLUMN_ICON, + PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, + SIG_CONVERSATION_SWITCHED, +static guint signals[N_SIGNALS] = {0, }; +struct _PidginDisplayWindow { + GtkApplicationWindow parent; + GtkTreeSelection *selection; + GtkWidget *notification_list; + GtkTreePath *conversation_path; +G_DEFINE_TYPE(PidginDisplayWindow, pidgin_display_window, + GTK_TYPE_APPLICATION_WINDOW) +static GtkWidget *default_window = NULL; +/****************************************************************************** + *****************************************************************************/ +pidgin_display_window_actions_set_enabled(GActionMap *map, + for(i = 0; actions[i] != NULL; i++) { + GAction *action = NULL; + const gchar *name = actions[i]; + action = g_action_map_lookup_action(map, name); + g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled); + g_critical("Failed to find action named %s", name); +/****************************************************************************** + *****************************************************************************/ +pidgin_display_window_invite_cb(GtkDialog *dialog, gint response_id, + G_GNUC_UNUSED gpointer data) + PidginInviteDialog *invite_dialog = PIDGIN_INVITE_DIALOG(dialog); + PurpleChatConversation *chat = NULL; + chat = pidgin_invite_dialog_get_conversation(invite_dialog); + g_object_set_data(G_OBJECT(chat), "pidgin-invite-dialog", NULL); + if(response_id == GTK_RESPONSE_ACCEPT) { + const gchar *contact = NULL, *message = NULL; + contact = pidgin_invite_dialog_get_contact(invite_dialog); + message = pidgin_invite_dialog_get_message(invite_dialog); + if(!purple_strequal(contact, "")) { + PurpleConnection *connection = NULL; + connection = purple_conversation_get_connection(PURPLE_CONVERSATION(chat)); + purple_serv_chat_invite(connection, + purple_chat_conversation_get_id(chat), + gtk_window_destroy(GTK_WINDOW(invite_dialog)); +/****************************************************************************** + *****************************************************************************/ +pidgin_display_window_alias(G_GNUC_UNUSED GSimpleAction *simple, + G_GNUC_UNUSED GVariant *parameter, + PidginDisplayWindow *window = data; + PurpleConversation *selected = NULL; + selected = pidgin_display_window_get_selected(window); + if(PURPLE_IS_CONVERSATION(selected)) { + PurpleAccount *account; + account = purple_conversation_get_account(selected); + name = purple_conversation_get_name(selected); + if(PURPLE_IS_IM_CONVERSATION(selected)) { + PurpleBuddy *buddy = purple_blist_find_buddy(account, name); + if(PURPLE_IS_BUDDY(buddy)) { + pidgin_dialogs_alias_buddy(buddy); + } else if(PURPLE_IS_CHAT_CONVERSATION(selected)) { + PurpleChat *chat = purple_blist_find_chat(account, name); + if(PURPLE_IS_CHAT(chat)) { + pidgin_dialogs_alias_chat(chat); +pidgin_display_window_close_conversation(G_GNUC_UNUSED GSimpleAction *simple, + G_GNUC_UNUSED GVariant *parameter, + PidginDisplayWindow *window = data; + PurpleConversation *selected = NULL; + selected = pidgin_display_window_get_selected(window); + if(PURPLE_IS_CONVERSATION(selected)) { + pidgin_display_window_remove(window, selected); + pidgin_conversation_detach(selected); +pidgin_display_window_get_info(G_GNUC_UNUSED GSimpleAction *simple, + G_GNUC_UNUSED GVariant *parameter, + PidginDisplayWindow *window = data; + PurpleConversation *selected = NULL; + selected = pidgin_display_window_get_selected(window); + if(PURPLE_IS_CONVERSATION(selected)) { + if(PURPLE_IS_IM_CONVERSATION(selected)) { + PurpleConnection *connection = NULL; + connection = purple_conversation_get_connection(selected); + pidgin_retrieve_user_info(connection, + purple_conversation_get_name(selected)); +pidgin_display_window_invite(G_GNUC_UNUSED GSimpleAction *simple, + G_GNUC_UNUSED GVariant *parameter, + PidginDisplayWindow *window = data; + PurpleConversation *selected = NULL; + selected = pidgin_display_window_get_selected(window); + if(PURPLE_IS_CHAT_CONVERSATION(selected)) { + GtkWidget *invite_dialog = NULL; + invite_dialog = g_object_get_data(G_OBJECT(selected), + "pidgin-invite-dialog"); + if(!GTK_IS_WIDGET(invite_dialog)) { + invite_dialog = pidgin_invite_dialog_new(PURPLE_CHAT_CONVERSATION(selected)); + g_object_set_data(G_OBJECT(selected), "pidgin-invite-dialog", + gtk_window_set_transient_for(GTK_WINDOW(invite_dialog), + gtk_window_set_destroy_with_parent(GTK_WINDOW(invite_dialog), TRUE); + g_signal_connect(invite_dialog, "response", + G_CALLBACK(pidgin_display_window_invite_cb), + gtk_widget_show(invite_dialog); +pidgin_display_window_send_file(G_GNUC_UNUSED GSimpleAction *simple, + G_GNUC_UNUSED GVariant *parameter, + PidginDisplayWindow *window = data; + PurpleConversation *selected = NULL; + selected = pidgin_display_window_get_selected(window); + if(PURPLE_IS_IM_CONVERSATION(selected)) { + PurpleConnection *connection = NULL; + connection = purple_conversation_get_connection(selected); + purple_serv_send_file(connection, + purple_conversation_get_name(selected), +static GActionEntry win_entries[] = { + .activate = pidgin_display_window_alias + .activate = pidgin_display_window_close_conversation + .activate = pidgin_display_window_get_info + .activate = pidgin_display_window_invite + .activate = pidgin_display_window_send_file + * pidgin_display_window_conversation_actions: + * A list of action names that are only valid if a conversation is selected. +static const gchar *pidgin_display_window_conversation_actions[] = { +static const gchar *pidgin_display_window_im_conversation_actions[] = { +static const gchar *pidgin_display_window_chat_conversation_actions[] = { +/****************************************************************************** + *****************************************************************************/ +pidgin_display_window_selection_changed(GtkTreeSelection *selection, + PidginDisplayWindow *window = PIDGIN_DISPLAY_WINDOW(data); + GtkTreeModel *model = NULL; + gboolean changed = FALSE; + if(gtk_tree_selection_get_selected(selection, &model, &iter)) { + gboolean is_conversation = FALSE; + gboolean im_selected = FALSE, chat_selected = FALSE; + gtk_tree_model_get(model, &iter, + PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, &name, + PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &obj, + adw_view_stack_set_visible_child_name(ADW_VIEW_STACK(window->stack), + /* If a conversation is selected, enable the generic conversation + is_conversation = PURPLE_IS_CONVERSATION(obj); + pidgin_display_window_actions_set_enabled(G_ACTION_MAP(window), + pidgin_display_window_conversation_actions, + /* If an IM is selected, enable the IM-specific actions otherwise + im_selected = PURPLE_IS_IM_CONVERSATION(obj); + pidgin_display_window_actions_set_enabled(G_ACTION_MAP(window), + pidgin_display_window_im_conversation_actions, + /* If a chat is selected, enable the chat-specific actions otherwise + chat_selected = PURPLE_IS_CHAT_CONVERSATION(obj); + pidgin_display_window_actions_set_enabled(G_ACTION_MAP(window), + pidgin_display_window_chat_conversation_actions, + adw_view_stack_set_visible_child_name(ADW_VIEW_STACK(window->stack), +pidgin_display_window_key_pressed_cb(GtkEventControllerKey *controller, + G_GNUC_UNUSED guint keycode, + PidginDisplayWindow *window = data; + /* If CTRL was held down... */ + if (state & GDK_CONTROL_MASK) { + case GDK_KEY_Page_Down: + case GDK_KEY_KP_Page_Down: + pidgin_display_window_select_next(window); + case GDK_KEY_KP_Page_Up: + pidgin_display_window_select_previous(window); + /* If ALT (or whatever) was held down... */ + else if (state & GDK_ALT_MASK) { + if ('1' <= keyval && keyval <= '9') { + guint switchto = keyval - '1'; + pidgin_display_window_select_nth(window, switchto); +/****************************************************************************** + * GObjectImplementation + *****************************************************************************/ +pidgin_display_window_dispose(GObject *obj) { + PidginDisplayWindow *window = PIDGIN_DISPLAY_WINDOW(obj); + if(GTK_IS_TREE_MODEL(window->model)) { + 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. + PurpleConversation *conversation = NULL; + gtk_tree_model_get(model, &iter, + PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &conversation, + if(PURPLE_IS_CONVERSATION(conversation)) { + pidgin_conversation_detach(conversation); + valid = gtk_tree_store_remove(window->model, &iter); + g_clear_pointer(&window->conversation_path, gtk_tree_path_free); + G_OBJECT_CLASS(pidgin_display_window_parent_class)->dispose(obj); +pidgin_display_window_init(PidginDisplayWindow *window) { + GtkEventController *key = NULL; + gtk_widget_init_template(GTK_WIDGET(window)); + gtk_window_set_application(GTK_WINDOW(window), + GTK_APPLICATION(g_application_get_default())); + g_action_map_add_action_entries(G_ACTION_MAP(window), win_entries, + G_N_ELEMENTS(win_entries), window); + key = gtk_event_controller_key_new(); + gtk_event_controller_set_propagation_phase(key, GTK_PHASE_CAPTURE); + g_signal_connect(G_OBJECT(key), "key-pressed", + G_CALLBACK(pidgin_display_window_key_pressed_cb), + gtk_widget_add_controller(GTK_WIDGET(window), key); + /* Add our toplevels to the tree store. */ + gtk_tree_store_append(window->model, &iter, NULL); + gtk_tree_store_set(window->model, &iter, + PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, window->notification_list, + PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, "__notifications__", + PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, _("Notifications"), + gtk_tree_store_append(window->model, &iter, NULL); + gtk_tree_store_set(window->model, &iter, + PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, _("Conversations"), + PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, "__conversations__", + gtk_tree_selection_select_iter(window->selection, &iter); + window->conversation_path = gtk_tree_model_get_path(GTK_TREE_MODEL(window->model), +pidgin_display_window_class_init(PidginDisplayWindowClass *klass) { + GObjectClass *obj_class = G_OBJECT_CLASS(klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + obj_class->dispose = pidgin_display_window_dispose; + * PidginDisplayWindow::conversation-switched: + * @window: The conversation window. + * @new_conv: The now active conversation. + * Emitted when a window switched from one conversation to another. + signals[SIG_CONVERSATION_SWITCHED] = g_signal_new_class_handler( + "conversation-switched", + G_OBJECT_CLASS_TYPE(obj_class), + PURPLE_TYPE_CONVERSATION + gtk_widget_class_set_template_from_resource( + "/im/pidgin/Pidgin3/Display/window.ui" + gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow, + gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow, + gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow, + gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow, + gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow, + gtk_widget_class_bind_template_child(widget_class, PidginDisplayWindow, + gtk_widget_class_bind_template_callback(widget_class, + pidgin_display_window_selection_changed); + gtk_widget_class_bind_template_callback(widget_class, + pidgin_display_window_key_pressed_cb); +/****************************************************************************** + *****************************************************************************/ +pidgin_display_window_get_default(void) { + if(!GTK_IS_WIDGET(default_window)) { + default_window = pidgin_display_window_new(); + g_object_add_weak_pointer(G_OBJECT(default_window), + (gpointer)&default_window); +pidgin_display_window_new(void) { + PIDGIN_TYPE_DISPLAY_WINDOW, +pidgin_display_window_add(PidginDisplayWindow *window, + PurpleConversation *conversation) + PidginConversation *gtkconv = NULL; + GtkTreeIter parent, iter; + GtkTreeModel *model = NULL; + const gchar *markup = NULL; + gboolean expand = FALSE; + g_return_if_fail(PIDGIN_IS_DISPLAY_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"); + if(!gtk_tree_model_iter_has_child(model, &parent)) { + markup = purple_conversation_get_name(conversation); + gtkconv = PIDGIN_CONVERSATION(conversation); + GtkWidget *parent = gtk_widget_get_parent(gtkconv->tab_cont); + if(GTK_IS_WIDGET(parent)) { + g_object_ref(gtkconv->tab_cont); + gtk_widget_unparent(gtkconv->tab_cont); + adw_view_stack_add_named(ADW_VIEW_STACK(window->stack), + gtkconv->tab_cont, markup); + gtk_widget_show(gtkconv->tab_cont); + if(GTK_IS_WIDGET(parent)) { + g_object_unref(gtkconv->tab_cont); + gtk_tree_store_prepend(window->model, &iter, &parent); + gtk_tree_store_set(window->model, &iter, + PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, conversation, + PIDGIN_DISPLAY_WINDOW_COLUMN_NAME, markup, + PIDGIN_DISPLAY_WINDOW_COLUMN_MARKUP, markup, + /* If we just added the first child, expand the parent. */ + 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(GTK_WIDGET(window)); +pidgin_display_window_remove(PidginDisplayWindow *window, + PurpleConversation *conversation) + GtkTreeIter parent, iter; + GtkTreeModel *model = NULL; + g_return_if_fail(PIDGIN_IS_DISPLAY_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)) { + /* The path is somehow invalid, so bail... */ + if(!gtk_tree_model_iter_children(model, &iter, &parent)) { + /* The conversations iter has no children. */ + gtk_tree_model_get(model, &iter, + PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &obj, + if(PURPLE_CONVERSATION(obj) == conversation) { + GtkWidget *child = NULL; + const gchar *name = NULL; + name = purple_conversation_get_name(conversation); + child = adw_view_stack_get_child_by_name(ADW_VIEW_STACK(window->stack), + if(GTK_IS_WIDGET(child)) { + gtk_widget_unparent(child); + gtk_tree_store_remove(window->model, &iter); + } while(gtk_tree_model_iter_next(model, &iter)); +pidgin_display_window_get_count(PidginDisplayWindow *window) { + GtkSelectionModel *model = NULL; + g_return_val_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window), 0); + model = adw_view_stack_get_pages(ADW_VIEW_STACK(window->stack)); + count = g_list_model_get_n_items(G_LIST_MODEL(model)); +pidgin_display_window_get_selected(PidginDisplayWindow *window) { + PurpleConversation *conversation = NULL; + GtkTreeSelection *selection = NULL; + g_return_val_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window), NULL); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view)); + if(gtk_tree_selection_get_selected(selection, NULL, &iter)) { + gtk_tree_model_get(GTK_TREE_MODEL(window->model), &iter, + PIDGIN_DISPLAY_WINDOW_COLUMN_OBJECT, &conversation, +pidgin_display_window_select(PidginDisplayWindow *window, + PurpleConversation *conversation) + const gchar *name = NULL; + g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window)); + g_return_if_fail(PURPLE_IS_CONVERSATION(conversation)); + name = purple_conversation_get_name(conversation); + adw_view_stack_set_visible_child_name(ADW_VIEW_STACK(window->stack), name); +pidgin_display_window_select_previous(PidginDisplayWindow *window) { + GtkTreeModel *model = NULL; + GtkTreeSelection *selection = NULL; + g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window)); + model = GTK_TREE_MODEL(window->model); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view)); + if(gtk_tree_selection_get_selected(selection, NULL, &iter)) { + if(gtk_tree_model_iter_previous(model, &iter)) { + gtk_tree_selection_select_iter(selection, &iter); + pidgin_display_window_select_last(window); +pidgin_display_window_select_next(PidginDisplayWindow *window) { + GtkTreeModel *model = NULL; + GtkTreeSelection *selection = NULL; + g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window)); + model = GTK_TREE_MODEL(window->model); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view)); + if(gtk_tree_selection_get_selected(selection, NULL, &iter)) { + if(gtk_tree_model_iter_next(model, &iter)) { + gtk_tree_selection_select_iter(selection, &iter); + pidgin_display_window_select_first(window); +pidgin_display_window_select_first(PidginDisplayWindow *window) { + GtkTreeModel *model = NULL; + g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window)); + model = GTK_TREE_MODEL(window->model); + if(gtk_tree_model_get_iter_first(model, &iter)) { + GtkTreeSelection *selection = NULL; + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view)); + gtk_tree_selection_select_iter(selection, &iter); +pidgin_display_window_select_last(PidginDisplayWindow *window) { + GtkTreeModel *model = NULL; + g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window)); + model = GTK_TREE_MODEL(window->model); + count = gtk_tree_model_iter_n_children(model, NULL); + if(gtk_tree_model_iter_nth_child(model, &iter, NULL, count - 1)) { + GtkTreeSelection *selection = NULL; + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view)); + gtk_tree_selection_select_iter(selection, &iter); +pidgin_display_window_select_nth(PidginDisplayWindow *window, + GtkTreeModel *model = NULL; + g_return_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window)); + model = GTK_TREE_MODEL(window->model); + if(gtk_tree_model_iter_nth_child(model, &iter, NULL, nth)) { + GtkTreeSelection *selection = NULL; + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(window->view)); + gtk_tree_selection_select_iter(selection, &iter); +pidgin_display_window_conversation_is_selected(PidginDisplayWindow *window, + PurpleConversation *conversation) + const gchar *name = NULL, *visible = NULL; + g_return_val_if_fail(PIDGIN_IS_DISPLAY_WINDOW(window), FALSE); + g_return_val_if_fail(PURPLE_IS_CONVERSATION(conversation), FALSE); + name = purple_conversation_get_name(conversation); + visible = adw_view_stack_get_visible_child_name(ADW_VIEW_STACK(window->stack)); + return purple_strequal(name, visible); --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgindisplaywindow.h Sun Sep 18 22:49:16 2022 -0500
@@ -0,0 +1,204 @@
+ * Pidgin - Internet Messenger + * Copyright (C) Pidgin Developers <devel@pidgin.im> + * Pidgin is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. +#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION) +# error "only <pidgin.h> may be included directly" +#ifndef PIDGIN_DISPLAY_WINDOW_H +#define PIDGIN_DISPLAY_WINDOW_H + * #PidginDisplayWindow is a widget that contains #PidginConversations. +#define PIDGIN_TYPE_DISPLAY_WINDOW (pidgin_display_window_get_type()) +G_DECLARE_FINAL_TYPE(PidginDisplayWindow, pidgin_display_window, + PIDGIN, DISPLAY_WINDOW, GtkApplicationWindow) + * pidgin_display_window_new: + * Creates a new #PidginDisplayWindow instance. + * Returns: (transfer full): The new #PidginDisplayWindow instance. +GtkWidget *pidgin_display_window_new(void); + * pidgin_display_window_get_default: + * Gets or creates the default conversation window. + * Returns: (transfer none): The default #PidginDisplayWindow. +GtkWidget *pidgin_display_window_get_default(void); + * pidgin_display_window_add: + * @window: The #PidginDisplayWindow instance. + * @conversation: The #PurpleConversation to add to @window. + * Adds @conversation to @window. If @conversation is already in @window, this +void pidgin_display_window_add(PidginDisplayWindow *window, PurpleConversation *conversation); + * pidgin_display_window_remove: + * @window: The #PidginDisplayWindow instance. + * @conversation: The #PurpleConversation to remove from @window. + * Removes @conversation from @window. If @conversation is not in @window, this +void pidgin_display_window_remove(PidginDisplayWindow *window, PurpleConversation *conversation); + * pidgin_display_window_get_count: + * @window: The conversation window instance. + * Gets the number of conversations that @window contains. + * Returns: The number of conversations that @window contains. +guint pidgin_display_window_get_count(PidginDisplayWindow *window); + * pidgin_display_window_get_selected: + * @window: The conversation window instance. + * Gets the currently selected PurpleConversation or %NULL if there is no + * Returns: (transfer full): The selected PurpleConversation or %NULL. +PurpleConversation *pidgin_display_window_get_selected(PidginDisplayWindow *window); + * pidgin_display_window_select: + * @window: The conversation window instance. + * @conversation: The conversation to select. + * Selects @conversation, making it the active conversation. +void pidgin_display_window_select(PidginDisplayWindow *window, PurpleConversation *conversation); + * pidgin_display_window_select_previous: + * @window: The conversation window instance. + * Switches to the conversation previous to the currently selected + * If no conversation is selected, the last conversation will be selected. +void pidgin_display_window_select_previous(PidginDisplayWindow *window); + * pidgin_display_window_select_next: + * @window: The conversation window instance. + * Switches to the conversation next of the currently selected conversation. + * If no conversation is selected, the first conversation will be selected. +void pidgin_display_window_select_next(PidginDisplayWindow *window); + * pidgin_display_window_select_first: + * @window: The conversation window instance. + * Selects the first conversation in @window. If there are no conversations in + * @window this does nothing. +void pidgin_display_window_select_first(PidginDisplayWindow *window); + * pidgin_display_window_select_last: + * @window: The conversation window instance. + * Selects the last conversation in @window. If there are no conversations in + * @window this does nothing. +void pidgin_display_window_select_last(PidginDisplayWindow *window); + * pidgin_display_window_select_nth: + * @window: The conversation window instance. + * @nth: The index of the conversation to switch to. + * Switches to the @nth conversation. @nth is a 0 based index, so the first + * conversation is at index 0. +void pidgin_display_window_select_nth(PidginDisplayWindow *window, guint nth); + * pidgin_display_window_conversation_is_selected: + * @window: The conversation window instance. + * @conversation: The conversation instance. + * Checks whether @conversation is the active conversation in @window. + * Returns: %TRUE if @conversation is active. +gboolean pidgin_display_window_conversation_is_selected(PidginDisplayWindow *window, PurpleConversation *conversation); +#endif /* PIDGIN_DISPLAY_WINDOW_H */ --- a/pidgin/resources/Conversations/window.ui Sun Sep 18 22:36:13 2022 -0500
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-Pidgin - Internet Messenger
-Copyright (C) Pidgin Developers <devel@pidgin.im>
-This program is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License
-as published by the Free Software Foundation; either version 2
-of the License, or (at your option) any later version.
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-You should have received a copy of the GNU General Public License
-along with this library; if not, see <https://www.gnu.org/licenses/>.
--->
- <requires lib="gtk" version="4.0"/>
- <requires lib="Adw" version="1.0"/>
- <requires lib="pidgin" version="3.0"/>
- <!-- interface-license-type gplv2 -->
- <!-- interface-name Pidgin -->
- <!-- interface-description Internet Messenger -->
- <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
- <object class="GtkTreeStore" id="model">
- <!-- column-name conversation -->
- <column type="GObject"/>
- <!-- column-name name -->
- <column type="gchararray"/>
- <!-- column-name icon -->
- <column type="GdkPixbuf"/>
- <!-- column-name markup -->
- <column type="gchararray"/>
- <template class="PidginDisplayWindow" parent="GtkApplicationWindow">
- <property name="show-menubar">1</property>
- <property name="default-height">450</property>
- <property name="default-width">950</property>
- <object class="GtkEventControllerKey">
- <property name="propagation-phase">capture</property>
- <signal name="key-pressed" handler="pidgin_display_window_key_pressed_cb"/>
- <object class="GtkBox" id="vbox">
- <property name="orientation">vertical</property>
- <object class="GtkPaned" id="paned">
- <property name="vexpand">1</property>
- <property name="focusable">1</property>
- <property name="position">220</property>
- <object class="GtkBox">
- <property name="margin-top">6</property>
- <property name="margin-start">6</property>
- <property name="margin-end">6</property>
- <property name="margin-bottom">6</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <object class="PidginStatusBox"/>
- <object class="GtkScrolledWindow">
- <property name="vexpand">1</property>
- <property name="focusable">1</property>
- <property name="propagate-natural-width">1</property>
- <property name="child">
- <object class="GtkTreeView" id="view">
- <property name="focusable">1</property>
- <property name="model">model</property>
- <property name="headers-visible">0</property>
- <property name="search-column">3</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="selection">
- <property name="mode">browse</property>
- <signal name="changed" handler="pidgin_display_window_selection_changed" object="PidginDisplayWindow" swapped="no"/>
- <object class="GtkTreeViewColumn" id="markup">
- <object class="GtkCellRendererPixbuf" id="icon"/>
- <attribute name="pixbuf">2</attribute>
- <object class="GtkCellRendererText" id="name"/>
- <attribute name="markup">3</attribute>
- <object class="AdwViewStack" id="stack">
- <object class="AdwViewStackPage">
- <property name="name">__notifications__</property>
- <property name="child">
- <object class="GtkScrolledWindow">
- <object class="PidginNotificationList" id="notification_list">
- <property name="orientation">vertical</property>
- <object class="AdwViewStackPage">
- <property name="name">__conversations__</property>
- <property name="child">
- <object class="AdwStatusPage">
- <property name="icon-name">mail-send-symbolic</property>
- <property name="title" translatable="1">Conversations</property>
- <property name="description" translatable="1">When you send a message to a friend or join a chat it will show up here!</property>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/Display/window.ui Sun Sep 18 22:49:16 2022 -0500
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?> +Pidgin - Internet Messenger +Copyright (C) Pidgin Developers <devel@pidgin.im> +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +You should have received a copy of the GNU General Public License +along with this library; if not, see <https://www.gnu.org/licenses/>. + <requires lib="gtk" version="4.0"/> + <requires lib="Adw" version="1.0"/> + <requires lib="pidgin" version="3.0"/> + <!-- interface-license-type gplv2 --> + <!-- interface-name Pidgin --> + <!-- interface-description Internet Messenger --> + <!-- interface-copyright Pidgin Developers <devel@pidgin.im> --> + <object class="GtkTreeStore" id="model"> + <!-- column-name conversation --> + <column type="GObject"/> + <!-- column-name name --> + <column type="gchararray"/> + <!-- column-name icon --> + <column type="GdkPixbuf"/> + <!-- column-name markup --> + <column type="gchararray"/> + <template class="PidginDisplayWindow" parent="GtkApplicationWindow"> + <property name="show-menubar">1</property> + <property name="default-height">450</property> + <property name="default-width">950</property> + <object class="GtkEventControllerKey"> + <property name="propagation-phase">capture</property> + <signal name="key-pressed" handler="pidgin_display_window_key_pressed_cb"/> + <object class="GtkBox" id="vbox"> + <property name="orientation">vertical</property> + <object class="GtkPaned" id="paned"> + <property name="vexpand">1</property> + <property name="focusable">1</property> + <property name="position">220</property> + <object class="GtkBox"> + <property name="margin-top">6</property> + <property name="margin-start">6</property> + <property name="margin-end">6</property> + <property name="margin-bottom">6</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <object class="PidginStatusBox"/> + <object class="GtkScrolledWindow"> + <property name="vexpand">1</property> + <property name="focusable">1</property> + <property name="propagate-natural-width">1</property> + <property name="child"> + <object class="GtkTreeView" id="view"> + <property name="focusable">1</property> + <property name="model">model</property> + <property name="headers-visible">0</property> + <property name="search-column">3</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="selection"> + <property name="mode">browse</property> + <signal name="changed" handler="pidgin_display_window_selection_changed" object="PidginDisplayWindow" swapped="no"/> + <object class="GtkTreeViewColumn" id="markup"> + <object class="GtkCellRendererPixbuf" id="icon"/> + <attribute name="pixbuf">2</attribute> + <object class="GtkCellRendererText" id="name"/> + <attribute name="markup">3</attribute> + <object class="AdwViewStack" id="stack"> + <object class="AdwViewStackPage"> + <property name="name">__notifications__</property> + <property name="child"> + <object class="GtkScrolledWindow"> + <object class="PidginNotificationList" id="notification_list"> + <property name="orientation">vertical</property> + <object class="AdwViewStackPage"> + <property name="name">__conversations__</property> + <property name="child"> + <object class="AdwStatusPage"> + <property name="icon-name">mail-send-symbolic</property> + <property name="title" translatable="1">Conversations</property> + <property name="description" translatable="1">When you send a message to a friend or join a chat it will show up here!</property> --- a/pidgin/resources/pidgin.gresource.xml Sun Sep 18 22:36:13 2022 -0500
+++ b/pidgin/resources/pidgin.gresource.xml Sun Sep 18 22:49:16 2022 -0500
@@ -15,10 +15,10 @@
<file compressed="true">Conversations/infopane.ui</file>
<file compressed="true">Conversations/invite_dialog.ui</file>
<file compressed="true">Conversations/tab-label.css</file>
- <file compressed="true">Conversations/window.ui</file>
<file compressed="true">Debug/debug.ui</file>
<file compressed="true">Dialogs/addbuddy.ui</file>
<file compressed="true">Dialogs/addchat.ui</file>
+ <file compressed="true">Display/window.ui</file> <file compressed="true">Keypad/keypad.ui</file>
<file compressed="true">Media/window.ui</file>
<file compressed="true">Notifications/addcontact.ui</file>
--- a/po/POTFILES.in Sun Sep 18 22:36:13 2022 -0500
+++ b/po/POTFILES.in Sun Sep 18 22:49:16 2022 -0500
@@ -347,9 +347,9 @@
pidgin/pidgincontactlistwindow.c
-pidgin/pidginconversationwindow.c
+pidgin/pidgindisplaywindow.c pidgin/pidgininvitedialog.c
@@ -398,10 +398,10 @@
pidgin/resources/BuddyList/window.ui
pidgin/resources/Conversations/infopane.ui
pidgin/resources/Conversations/invite_dialog.ui
-pidgin/resources/Conversations/window.ui
pidgin/resources/Debug/debug.ui
pidgin/resources/Dialogs/addbuddy.ui
pidgin/resources/Dialogs/addchat.ui
+pidgin/resources/Display/window.ui pidgin/resources/Keypad/keypad.ui
pidgin/resources/Media/window.ui
pidgin/resources/Plugins/dialog.ui