conversationpopupmenu: add popup menu for conversations

This class will replace contactpopupmenu to use the new lrc.

Change-Id: I4fefd530484288f03a0fd0df7d1ccf7df6f71ea9
diff --git a/src/accountcontainer.h b/src/accountcontainer.h
new file mode 100644
index 0000000..6cfbace
--- /dev/null
+++ b/src/accountcontainer.h
@@ -0,0 +1,37 @@
+/****************************************************************************
+ *   Copyright (C) 2017 Savoir-faire Linux                                  *
+ *   Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com>             *
+ *   Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>           *
+ *                                                                          *
+ *   This library is free software; you can redistribute it and/or          *
+ *   modify it under the terms of the GNU Lesser General Public             *
+ *   License as published by the Free Software Foundation; either           *
+ *   version 2.1 of the License, or (at your option) any later version.     *
+ *                                                                          *
+ *   This library 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      *
+ *   Lesser 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 <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+#pragma once
+
+// Lrc
+#include <api/account.h>
+
+/**
+ * This class contains a const reference linked to an account.
+ * NOTE: it avoids weird initialization with Gtk
+ * @param  accInfo the account linked
+ */
+class AccountContainer {
+
+public:
+    explicit AccountContainer (const lrc::api::account::Info& accInfo)
+    : info(accInfo)
+    {}
+
+    const lrc::api::account::Info& info;
+};
diff --git a/src/conversationpopupmenu.cpp b/src/conversationpopupmenu.cpp
new file mode 100644
index 0000000..24a262b
--- /dev/null
+++ b/src/conversationpopupmenu.cpp
@@ -0,0 +1,234 @@
+/****************************************************************************
+ *   Copyright (C) 2017 Savoir-faire Linux                                  *
+ *   Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com>             *
+ *   Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>           *
+ *                                                                          *
+ *   This library is free software; you can redistribute it and/or          *
+ *   modify it under the terms of the GNU Lesser General Public             *
+ *   License as published by the Free Software Foundation; either           *
+ *   version 2.1 of the License, or (at your option) any later version.     *
+ *                                                                          *
+ *   This library 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      *
+ *   Lesser 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 <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include "conversationpopupmenu.h"
+
+// GTK+ related
+#include <glib/gi18n.h>
+
+// Lrc
+#include <api/conversationmodel.h>
+#include <api/contactmodel.h>
+#include <api/contact.h>
+
+struct _ConversationPopupMenu
+{
+    GtkMenu parent;
+};
+
+struct _ConversationPopupMenuClass
+{
+    GtkMenuClass parent_class;
+};
+
+typedef struct _ConversationPopupMenuPrivate ConversationPopupMenuPrivate;
+
+struct _ConversationPopupMenuPrivate
+{
+    GtkTreeView *treeview;
+
+    AccountContainer* accountContainer_;
+    int row_;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(ConversationPopupMenu, conversation_popup_menu, GTK_TYPE_MENU);
+
+#define CONVERSATION_POPUP_MENU_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), CONVERSATION_POPUP_MENU_TYPE, ConversationPopupMenuPrivate))
+
+static void
+remove_history_conversation(GtkWidget *menu, ConversationPopupMenuPrivate* priv)
+{
+    try
+    {
+        auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_);
+        priv->accountContainer_->info.conversationModel->clearHistory(conversation.uid);
+    }
+    catch (...)
+    {
+        g_warning("Can't get conversation at row %i", priv->row_);
+    }
+}
+
+static void
+remove_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
+{
+    try
+    {
+        auto conversationUid = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_).uid;
+        priv->accountContainer_->info.conversationModel->removeConversation(conversationUid);
+    }
+    catch (...)
+    {
+        g_warning("Can't get conversation at row %i", priv->row_);
+    }
+}
+
+static void
+block_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
+{
+    try
+    {
+        auto conversationUid = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_).uid;
+        priv->accountContainer_->info.conversationModel->removeConversation(conversationUid, true);
+    }
+    catch (...)
+    {
+        g_warning("Can't get conversation at row %i", priv->row_);
+    }
+}
+
+static void
+add_conversation(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
+{
+    try
+    {
+        auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_);
+        priv->accountContainer_->info.conversationModel->makePermanent(conversation.uid);
+    }
+    catch (...)
+    {
+        g_warning("Can't get conversation at row %i", priv->row_);
+    }
+}
+
+static void
+place_call(G_GNUC_UNUSED GtkWidget *menu, ConversationPopupMenuPrivate* priv)
+{
+    try
+    {
+        auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(priv->row_);
+        priv->accountContainer_->info.conversationModel->placeCall(conversation.uid);
+    }
+    catch (...)
+    {
+        g_warning("Can't get conversation at row %i", priv->row_);
+    }
+}
+
+/**
+ * Update the menu when the selected conversation in the treeview changes.
+ */
+static void
+update(GtkTreeSelection *selection, ConversationPopupMenu *self)
+{
+    ConversationPopupMenuPrivate *priv = CONVERSATION_POPUP_MENU_GET_PRIVATE(self);
+    /* clear the current menu */
+    gtk_container_forall(GTK_CONTAINER(self), (GtkCallback)gtk_widget_destroy, nullptr);
+
+    // Retrieve conversation
+    GtkTreeIter iter;
+    GtkTreeModel *model;
+    if (!gtk_tree_selection_get_selected(selection, &model, &iter)) return;
+    auto path = gtk_tree_model_get_path(model, &iter);
+    auto idx = gtk_tree_path_get_indices(path);
+    auto conversation = priv->accountContainer_->info.conversationModel->filteredConversation(idx[0]);
+    priv->row_ = idx[0];
+    auto contactInfo = priv->accountContainer_->info.contactModel->getContact(conversation.participants.front());
+    if (contactInfo.profileInfo.uri.empty()) return;
+
+    // we always build a menu, however in some cases some or all of the conversations will be deactivated
+    // we prefer this to having an empty menu because GTK+ behaves weird in the empty menu case
+    auto place_call_conversation = gtk_menu_item_new_with_mnemonic(_("_Place call"));
+    gtk_menu_shell_append(GTK_MENU_SHELL(self), place_call_conversation);
+    g_signal_connect(place_call_conversation, "activate", G_CALLBACK(place_call), priv);
+    if (contactInfo.profileInfo.type == lrc::api::profile::Type::TEMPORARY ||
+        contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING) {
+        // If we can add this conversation
+        auto add_conversation_conversation = gtk_menu_item_new_with_mnemonic(_("_Add conversation"));
+        gtk_menu_shell_append(GTK_MENU_SHELL(self), add_conversation_conversation);
+        g_signal_connect(add_conversation_conversation, "activate", G_CALLBACK(add_conversation), priv);
+        if (contactInfo.profileInfo.type == lrc::api::profile::Type::PENDING) {
+            auto rm_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Discard invitation"));
+            gtk_menu_shell_append(GTK_MENU_SHELL(self), rm_conversation_item);
+            g_signal_connect(rm_conversation_item, "activate", G_CALLBACK(remove_conversation), priv);
+            auto block_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Block invitations"));
+            gtk_menu_shell_append(GTK_MENU_SHELL(self), block_conversation_item);
+            g_signal_connect(block_conversation_item, "activate", G_CALLBACK(block_conversation), priv);
+        }
+    } else {
+        auto rm_history_conversation = gtk_menu_item_new_with_mnemonic(_("_Clear history"));
+        gtk_menu_shell_append(GTK_MENU_SHELL(self), rm_history_conversation);
+        g_signal_connect(rm_history_conversation, "activate", G_CALLBACK(remove_history_conversation), priv);
+        auto rm_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Remove conversation"));
+        gtk_menu_shell_append(GTK_MENU_SHELL(self), rm_conversation_item);
+        g_signal_connect(rm_conversation_item, "activate", G_CALLBACK(remove_conversation), priv);
+        auto block_conversation_item = gtk_menu_item_new_with_mnemonic(_("_Block contact"));
+        gtk_menu_shell_append(GTK_MENU_SHELL(self), block_conversation_item);
+        g_signal_connect(block_conversation_item, "activate", G_CALLBACK(block_conversation), priv);
+    }
+
+    /* show all conversations */
+    gtk_widget_show_all(GTK_WIDGET(self));
+
+}
+
+static void
+conversation_popup_menu_dispose(GObject *object)
+{
+    G_OBJECT_CLASS(conversation_popup_menu_parent_class)->dispose(object);
+}
+
+static void
+conversation_popup_menu_finalize(GObject *object)
+{
+    G_OBJECT_CLASS(conversation_popup_menu_parent_class)->finalize(object);
+}
+
+static void
+conversation_popup_menu_class_init(ConversationPopupMenuClass *klass)
+{
+    G_OBJECT_CLASS(klass)->finalize = conversation_popup_menu_finalize;
+    G_OBJECT_CLASS(klass)->dispose = conversation_popup_menu_dispose;
+}
+
+
+static void
+conversation_popup_menu_init(G_GNUC_UNUSED ConversationPopupMenu *self)
+{
+    // nothing to do
+}
+
+GtkWidget *
+conversation_popup_menu_new (GtkTreeView *treeview, AccountContainer* accountContainer)
+{
+    gpointer self = g_object_new(CONVERSATION_POPUP_MENU_TYPE, NULL);
+    ConversationPopupMenuPrivate *priv = CONVERSATION_POPUP_MENU_GET_PRIVATE(self);
+    priv->accountContainer_ = accountContainer;
+
+    priv->treeview = treeview;
+    GtkTreeSelection *selection = gtk_tree_view_get_selection(priv->treeview);
+
+    // build the menu for the first time
+    update(selection, CONVERSATION_POPUP_MENU(self));
+
+    return (GtkWidget *)self;
+}
+
+gboolean
+conversation_popup_menu_show(ConversationPopupMenu *self, GdkEventButton *event)
+{
+    if (!self) return GDK_EVENT_PROPAGATE;
+    if (event->type == GDK_BUTTON_PRESS
+        && event->button == GDK_BUTTON_SECONDARY) {
+        // Show popup menu. Will be updated later.
+        gtk_menu_popup(GTK_MENU(self), NULL, NULL, NULL, NULL, event->button, event->time);
+    }
+
+    return GDK_EVENT_PROPAGATE; // so that the conversation selection changes
+}
diff --git a/src/conversationpopupmenu.h b/src/conversationpopupmenu.h
new file mode 100644
index 0000000..174f1d5
--- /dev/null
+++ b/src/conversationpopupmenu.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+ *   Copyright (C) 2017 Savoir-faire Linux                                  *
+ *   Author: Nicolas Jäger <nicolas.jager@savoirfairelinux.com>             *
+ *   Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>           *
+ *                                                                          *
+ *   This library is free software; you can redistribute it and/or          *
+ *   modify it under the terms of the GNU Lesser General Public             *
+ *   License as published by the Free Software Foundation; either           *
+ *   version 2.1 of the License, or (at your option) any later version.     *
+ *                                                                          *
+ *   This library 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      *
+ *   Lesser 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 <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+#pragma once
+
+// GTK+ related
+#include <gtk/gtk.h>
+
+// LRC
+#include "accountcontainer.h"
+
+G_BEGIN_DECLS
+
+#define CONVERSATION_POPUP_MENU_TYPE            (conversation_popup_menu_get_type ())
+#define CONVERSATION_POPUP_MENU(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CONVERSATION_POPUP_MENU_TYPE, ConversationPopupMenu))
+#define CONVERSATION_POPUP_MENU_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), CONVERSATION_POPUP_MENU_TYPE, ConversationPopupMenuClass))
+#define IS_CONVERSATION_POPUP_MENU(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), CONVERSATION_POPUP_MENU_TYPE))
+#define IS_CONVERSATION_POPUP_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), CONVERSATION_POPUP_MENU_TYPE))
+
+typedef struct _ConversationPopupMenu      ConversationPopupMenu;
+typedef struct _ConversationPopupMenuClass ConversationPopupMenuClass;
+
+GType      conversation_popup_menu_get_type (void) G_GNUC_CONST;
+GtkWidget *conversation_popup_menu_new      (GtkTreeView *treeview, AccountContainer* accountContainer);
+gboolean   conversation_popup_menu_show     (ConversationPopupMenu *self, GdkEventButton *event);
+
+G_END_DECLS