# HG changeset patch
# Node ID bcee309c8a3b
# Parent  1a53026013e4
diff --git a/pidgin/gtkconv.c b/pidgin/gtkconv.c
--- a/pidgin/gtkconv.c
+++ b/pidgin/gtkconv.c
@@ -71,8 +71,6 @@
 #define BUDDYICON_SIZE_MIN    32
 #define BUDDYICON_SIZE_MAX    96
 
-static GtkWidget *invite_dialog = NULL;
-
 /* Prototypes. <-- because Paco-Paco hates this comment. */
 static void got_typing_keypress(PidginConversation *gtkconv, gboolean first);
 static void add_chat_user_common(PurpleChatConversation *chat, PurpleChatUser *cb, const char *old_name);
@@ -321,35 +319,6 @@
 	}
 }
 
-
-static void
-info_cb(GtkWidget *widget, PidginConversation *gtkconv)
-{
-	PurpleConversation *conv = gtkconv->active_conv;
-
-	if (PURPLE_IS_IM_CONVERSATION(conv)) {
-		pidgin_retrieve_user_info(purple_conversation_get_connection(conv),
-					  purple_conversation_get_name(conv));
-	} else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
-		/* Get info of the person currently selected in the GtkTreeView */
-		GtkTreeIter iter;
-		GtkTreeModel *model;
-		GtkTreeSelection *sel;
-		char *name;
-
-		model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtkconv->list));
-		sel   = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkconv->list));
-
-		if (gtk_tree_selection_get_selected(sel, NULL, &iter))
-			gtk_tree_model_get(GTK_TREE_MODEL(model), &iter, CHAT_USERS_NAME_COLUMN, &name, -1);
-		else
-			return;
-
-		chat_do_info(gtkconv, name);
-		g_free(name);
-	}
-}
-
 static void
 block_cb(GtkWidget *widget, PidginConversation *gtkconv)
 {
@@ -374,44 +343,6 @@
 		pidgin_request_add_permit(account, purple_conversation_get_name(conv));
 }
 
-static void
-do_invite(GtkWidget *w, int resp, gpointer data)
-{
-	PidginInviteDialog *dialog = PIDGIN_INVITE_DIALOG(w);
-	PurpleChatConversation *chat = pidgin_invite_dialog_get_conversation(dialog);
-	const gchar *contact, *message;
-
-	if (resp == GTK_RESPONSE_ACCEPT) {
-		contact = pidgin_invite_dialog_get_contact(dialog);
-		if (!g_ascii_strcasecmp(contact, ""))
-			return;
-
-		message = pidgin_invite_dialog_get_message(dialog);
-
-		purple_serv_chat_invite(purple_conversation_get_connection(PURPLE_CONVERSATION(chat)),
-						 purple_chat_conversation_get_id(chat),
-						 message, contact);
-	}
-
-	g_clear_pointer(&invite_dialog, gtk_widget_destroy);
-}
-
-static void
-invite_cb(GtkWidget *widget, PidginConversation *gtkconv)
-{
-	PurpleChatConversation *chat = PURPLE_CHAT_CONVERSATION(gtkconv->active_conv);
-
-	if (invite_dialog == NULL) {
-		invite_dialog = pidgin_invite_dialog_new(chat);
-
-		/* Connect the signals. */
-		g_signal_connect(G_OBJECT(invite_dialog), "response",
-						 G_CALLBACK(do_invite), NULL);
-	}
-
-	gtk_widget_show_all(invite_dialog);
-}
-
 #ifdef USE_VV
 static void
 menu_initiate_media_call_cb(GtkAction *action, gpointer data)
@@ -432,67 +363,6 @@
 #endif
 
 static void
-menu_send_file_cb(GtkAction *action, gpointer data)
-{
-	PidginConversationWindow *win = data;
-	PurpleConversation *conv = pidgin_conversation_window_get_selected(win);
-
-	if (PURPLE_IS_IM_CONVERSATION(conv)) {
-		purple_serv_send_file(purple_conversation_get_connection(conv), purple_conversation_get_name(conv), NULL);
-	}
-
-}
-
-static void
-menu_alias_cb(GtkAction *action, gpointer data)
-{
-	PidginConversationWindow *win = data;
-	PurpleConversation *conv;
-	PurpleAccount *account;
-	const char *name;
-
-	conv    = pidgin_conversation_window_get_selected(win);
-	account = purple_conversation_get_account(conv);
-	name    = purple_conversation_get_name(conv);
-
-	if (PURPLE_IS_IM_CONVERSATION(conv)) {
-		PurpleBuddy *b;
-
-		b = purple_blist_find_buddy(account, name);
-		if (b != NULL)
-			pidgin_dialogs_alias_buddy(b);
-	} else if (PURPLE_IS_CHAT_CONVERSATION(conv)) {
-		PurpleChat *c;
-
-		c = purple_blist_find_chat(account, name);
-		if (c != NULL)
-			pidgin_dialogs_alias_chat(c);
-	}
-}
-
-static void
-menu_get_info_cb(GtkAction *action, gpointer data)
-{
-	PidginConversationWindow *win = data;
-	PurpleConversation *conv;
-
-	conv = pidgin_conversation_window_get_selected(win);
-
-	info_cb(NULL, PIDGIN_CONVERSATION(conv));
-}
-
-static void
-menu_invite_cb(GtkAction *action, gpointer data)
-{
-	PidginConversationWindow *win = data;
-	PurpleConversation *conv;
-
-	conv = pidgin_conversation_window_get_selected(win);
-
-	invite_cb(NULL, PIDGIN_CONVERSATION(conv));
-}
-
-static void
 menu_block_cb(GtkAction *action, gpointer data)
 {
 	PidginConversationWindow *win = data;
@@ -526,14 +396,6 @@
 }
 
 static void
-menu_close_conv_cb(GtkAction *action, gpointer data)
-{
-	PidginConversationWindow *win = data;
-
-	close_conv_cb(NULL, PIDGIN_CONVERSATION(pidgin_conversation_window_get_selected(win)));
-}
-
-static void
 chat_do_im(PidginConversation *gtkconv, const char *who)
 {
 	PurpleConversation *conv = gtkconv->active_conv;
@@ -1151,18 +1013,10 @@
 	{ "AudioVideoCall", NULL, N_("Audio/Video _Call"), NULL, NULL, G_CALLBACK(menu_initiate_media_call_cb) },
 #endif
 
-	{ "SendFile", NULL, N_("Se_nd File..."), NULL, NULL, G_CALLBACK(menu_send_file_cb) },
-	{ "GetInfo", NULL, N_("_Get Info"), "<control>O", NULL, G_CALLBACK(menu_get_info_cb) },
-	{ "Invite", NULL, N_("In_vite..."), NULL, NULL, G_CALLBACK(menu_invite_cb) },
-	{ "MoreMenu", NULL, N_("M_ore"), NULL, NULL, NULL },
-	{ "Alias", NULL, N_("Al_ias..."), NULL, NULL, G_CALLBACK(menu_alias_cb) },
 	{ "Block", NULL, N_("_Block..."), NULL, NULL, G_CALLBACK(menu_block_cb) },
 	{ "Unblock", NULL, N_("_Unblock..."), NULL, NULL, G_CALLBACK(menu_unblock_cb) },
 	{ "Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(menu_add_remove_cb) },
 	{ "Remove", NULL, N_("_Remove..."), NULL, NULL, G_CALLBACK(menu_add_remove_cb) },
-	{ "InsertLink", NULL, N_("Insert Lin_k..."), NULL, NULL, NULL },
-	{ "InsertImage", NULL, N_("Insert Imag_e..."), NULL, NULL, NULL },
-	{ "Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(menu_close_conv_cb) },
 };
 
 /**************************************************************************
@@ -1922,6 +1776,11 @@
 	if(PIDGIN_IS_PIDGIN_CONVERSATION(conv)) {
 		PidginConversation *gtkconv = PIDGIN_CONVERSATION(conv);
 
+		if(gtkconv->attach_timer > 0) {
+			g_source_remove(gtkconv->attach_timer);
+			gtkconv->attach_timer = 0;
+		}
+
 		close_conv_cb(NULL, gtkconv);
 
 		g_free(gtkconv);
@@ -1984,6 +1843,7 @@
 
 	if (gtkconv->attach_timer) {
 		g_source_remove(gtkconv->attach_timer);
+		gtkconv->attach_timer = 0;
 	}
 
 	g_free(gtkconv);
diff --git a/pidgin/pidginconversationwindow.c b/pidgin/pidginconversationwindow.c
--- a/pidgin/pidginconversationwindow.c
+++ b/pidgin/pidginconversationwindow.c
@@ -25,6 +25,9 @@
 #include "pidginconversationwindow.h"
 
 #include "gtkconv.h"
+#include "gtkdialogs.h"
+#include "gtkutils.h"
+#include "pidgininvitedialog.h"
 
 enum {
 	PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT,
@@ -61,6 +64,228 @@
 static GtkWidget *default_window = NULL;
 
 /******************************************************************************
+ * Helpers
+ *****************************************************************************/
+static void
+pidgin_conversation_window_actions_set_enabled(PurpleConversation *conversation,
+                                               GActionMap *map,
+                                               const gchar **actions,
+                                               gboolean enabled)
+{
+	gint i = 0;
+
+	for(i = 0; actions[i] != NULL; i++) {
+		GAction *action = NULL;
+		const gchar *name = actions[i];
+
+		action = g_action_map_lookup_action(map, name);
+		if(action != NULL) {
+			g_simple_action_set_enabled(G_SIMPLE_ACTION(action), enabled);
+		} else {
+			g_critical("Failed to find action named %s", name);
+		}
+	}
+}
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static void
+pidgin_conversation_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),
+			                        message, contact);
+		}
+	}
+
+	gtk_widget_destroy(GTK_WIDGET(invite_dialog));
+}
+
+/******************************************************************************
+ * Actions
+ *****************************************************************************/
+static void
+pidgin_conversation_window_alias(G_GNUC_UNUSED GSimpleAction *simple,
+                                 G_GNUC_UNUSED GVariant *parameter,
+                                 gpointer data)
+{
+	PidginConversationWindow *window = data;
+	PurpleConversation *selected = NULL;
+
+	selected = pidgin_conversation_window_get_selected(window);
+	if(PURPLE_IS_CONVERSATION(selected)) {
+		PurpleAccount *account;
+		const gchar *name;
+
+		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);
+			}
+		}
+	}
+}
+
+static void
+pidgin_conversation_window_close_conversation(G_GNUC_UNUSED GSimpleAction *simple,
+                                              G_GNUC_UNUSED GVariant *parameter,
+                                              gpointer data)
+{
+	PidginConversationWindow *window = data;
+	PurpleConversation *selected = NULL;
+
+	selected = pidgin_conversation_window_get_selected(window);
+	if(PURPLE_IS_CONVERSATION(selected)) {
+		pidgin_conversation_window_remove(window, selected);
+		pidgin_conversation_detach(selected);
+	}
+}
+
+static void
+pidgin_conversation_window_get_info(G_GNUC_UNUSED GSimpleAction *simple,
+                                    G_GNUC_UNUSED GVariant *parameter,
+                                    gpointer data)
+{
+	PidginConversationWindow *window = data;
+	PurpleConversation *selected = NULL;
+
+	selected = pidgin_conversation_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));
+		}
+	}
+}
+
+static void
+pidgin_conversation_window_invite(G_GNUC_UNUSED GSimpleAction *simple,
+                                  G_GNUC_UNUSED GVariant *parameter,
+                                  gpointer data)
+{
+	PidginConversationWindow *window = data;
+	PurpleConversation *selected = NULL;
+
+	selected = pidgin_conversation_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",
+			                  invite_dialog);
+
+			gtk_window_set_transient_for(GTK_WINDOW(invite_dialog),
+			                             GTK_WINDOW(window));
+			gtk_window_set_destroy_with_parent(GTK_WINDOW(invite_dialog), TRUE);
+
+			g_signal_connect(invite_dialog, "response",
+			                 G_CALLBACK(pidgin_conversation_window_invite_cb),
+			                 NULL);
+		}
+
+		gtk_widget_show_all(invite_dialog);
+	}
+}
+
+static void
+pidgin_conversation_window_send_file(G_GNUC_UNUSED GSimpleAction *simple,
+                                     G_GNUC_UNUSED GVariant *parameter,
+                                     gpointer data)
+{
+	PidginConversationWindow *window = data;
+	PurpleConversation *selected = NULL;
+
+	selected = pidgin_conversation_window_get_selected(window);
+	if(PURPLE_IS_CONVERSATION(selected)) {
+		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),
+			                      NULL);
+		}
+	}
+}
+
+static GActionEntry win_entries[] = {
+	{
+		.name = "alias",
+		.activate = pidgin_conversation_window_alias
+	}, {
+		.name = "close",
+		.activate = pidgin_conversation_window_close_conversation
+	}, {
+		.name = "get-info",
+		.activate = pidgin_conversation_window_get_info
+	}, {
+		.name = "invite",
+		.activate = pidgin_conversation_window_invite
+	}, {
+		.name = "send-file",
+		.activate = pidgin_conversation_window_send_file
+	}
+};
+
+/**<private>
+ * pidgin_conversation_window_conversation_actions:
+ *
+ * A list of action names that are only valid if a conversation is selected.
+ */
+static const gchar *pidgin_conversation_window_conversation_actions[] = {
+	"alias",
+	"close",
+	"get-info",
+	NULL
+};
+
+static const gchar *pidgin_conversation_window_im_conversation_actions[] = {
+	"send-file",
+	NULL
+};
+
+static const gchar *pidgin_conversation_window_chat_conversation_actions[] = {
+	"invite",
+	NULL
+};
+
+/******************************************************************************
  * Callbacks
  *****************************************************************************/
 static void
@@ -73,16 +298,49 @@
 	gboolean changed = FALSE;
 
 	if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
+		GObject *obj;
+		gboolean is_conversation = FALSE;
+		gboolean im_selected = FALSE, chat_selected = FALSE;
 		gchar *name = NULL;
 
 		gtk_tree_model_get(model, &iter,
 		                   PIDGIN_CONVERSATION_WINDOW_COLUMN_NAME, &name,
+		                   PIDGIN_CONVERSATION_WINDOW_COLUMN_OBJECT, &obj,
 		                   -1);
 
 		gtk_stack_set_visible_child_name(GTK_STACK(window->stack), name);
 		g_free(name);
 
 		changed = TRUE;
+
+		/* If a conversation is selected, enable the generic conversation
+		 * actions.
+		 */
+		is_conversation = PURPLE_IS_CONVERSATION(obj);
+		pidgin_conversation_window_actions_set_enabled(PURPLE_CONVERSATION(obj),
+		                                               G_ACTION_MAP(window),
+		                                               pidgin_conversation_window_conversation_actions,
+		                                               is_conversation);
+
+		/* If an im is selected enabled the im specific actions otherwise
+		 * disable them.
+		 */
+		im_selected = PURPLE_IS_IM_CONVERSATION(obj);
+		pidgin_conversation_window_actions_set_enabled(PURPLE_CONVERSATION(obj),
+		                                               G_ACTION_MAP(window),
+		                                               pidgin_conversation_window_im_conversation_actions,
+		                                               im_selected);
+
+		/* If a chat is selected enable the chat specific actions otherwise
+		 * disable them.
+		 */
+		chat_selected = PURPLE_IS_CHAT_CONVERSATION(obj);
+		pidgin_conversation_window_actions_set_enabled(PURPLE_CONVERSATION(obj),
+		                                               G_ACTION_MAP(window),
+		                                               pidgin_conversation_window_chat_conversation_actions,
+		                                               chat_selected);
+
+		g_clear_object(&obj);
 	}
 
 	if(!changed) {
@@ -180,6 +438,9 @@
 	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_WIDGET(window));
 	gtk_event_controller_set_propagation_phase(key, GTK_PHASE_CAPTURE);
 	g_signal_connect(G_OBJECT(key), "key-pressed",
@@ -361,6 +622,16 @@
 		                   -1);
 
 		if(PURPLE_CONVERSATION(obj) == conversation) {
+			GtkWidget *child = NULL;
+			const gchar *name = NULL;
+
+			name = purple_conversation_get_name(conversation);
+			child = gtk_stack_get_child_by_name(GTK_STACK(window->stack),
+			                                    name);
+			if(GTK_IS_WIDGET(child)) {
+				gtk_container_remove(GTK_CONTAINER(window->stack), child);
+			}
+
 			gtk_tree_store_remove(window->model, &iter);
 
 			g_clear_object(&obj);
diff --git a/pidgin/resources/gtk/menus.ui b/pidgin/resources/gtk/menus.ui
--- a/pidgin/resources/gtk/menus.ui
+++ b/pidgin/resources/gtk/menus.ui
@@ -75,7 +75,7 @@
         <item>
           <attribute name="label" translatable="yes">New Instant _Message...</attribute>
           <attribute name="action">app.new-message</attribute>
-          <attribute name="accel">&lt;Primary&gt;M</attribute>
+          <attribute name="accel">&lt;Primary&gt;m</attribute>
         </item>
         <item>
           <attribute name="label" translatable="yes">Join a _Chat...</attribute>
@@ -84,19 +84,6 @@
       </section>
 
       <section>
-        <item>
-          <attribute name="label" translatable="yes">_Find...</attribute>
-          <attribute name="action">win.find</attribute>
-          <attribute name="accel">&lt;Primary&gt;F</attribute>
-        </item>
-        <item>
-          <attribute name="label" translatable="yes">Clea_r Scrollback</attribute>
-          <attribute name="action">win.clear-history</attribute>
-          <attribute name="accel">&lt;Primary&gt;L</attribute>
-        </item>
-      </section>
-
-      <section>
         <submenu>
           <attribute name="label" translatable="yes">M_edia</attribute>
 
@@ -124,8 +111,8 @@
         </item>
         <item>
           <attribute name="label" translatable="yes">_Get Info</attribute>
-          <attribute name="action">win.clear-history</attribute>
-          <attribute name="accel">&lt;Primary&gt;O</attribute>
+          <attribute name="action">win.get-info</attribute>
+          <attribute name="accel">&lt;Primary&gt;o</attribute>
         </item>
         <item>
           <attribute name="label" translatable="yes">In_vite...</attribute>
@@ -155,20 +142,9 @@
 
       <section>
         <item>
-          <attribute name="label" translatable="yes">Insert Lin_k...</attribute>
-          <attribute name="action">win.insert-link</attribute>
-        </item>
-        <item>
-          <attribute name="label" translatable="yes">Insert Imag_e...</attribute>
-          <attribute name="action">win.insert-image</attribute>
-        </item>
-      </section>
-
-      <section>
-        <item>
           <attribute name="label" translatable="yes">_Close</attribute>
           <attribute name="action">win.close</attribute>
-          <attribute name="accel">&lt;Primary&gt;W</attribute>
+          <attribute name="accel">&lt;Primary&gt;w</attribute>
         </item>
       </section>
     </submenu>
