improve behaviour of context menu in treeviews

Now rightclicking on an item in the contact treeviews (Conversations,
Contacts, History) will result in the item clicked on being selected.
Before the popup menu would open for the selected item, but the
selection would not change if the click was on a different item.

This also fixes a small memory leak where the a new menu widget would
be created on every right click, but the old menu was never destroyed.
Now the same menu widget is used, it is simply updated once the
selection is changed.

The menus are also now synchronized across all the 3 contact treeviews
and code duplication is reduced by moving the popup code into its
own widget. Menu items which are not relevant to the current selection
are now greyed out instead of the menu simply being different for
different items, this is more in line with standard GTK+ behaviour.

Change-Id: I4e54d618a090e28b565cbef719065c943a826b0e
Tuleap: #930
diff --git a/src/historyview.cpp b/src/historyview.cpp
index 81ee4c6..4b72c50 100644
--- a/src/historyview.cpp
+++ b/src/historyview.cpp
@@ -35,8 +35,7 @@
 #include <QtCore/QDateTime> // for date time formatting
 #include <QtCore/QItemSelectionModel>
 #include "utils/menus.h"
-
-static constexpr const char* COPY_DATA_KEY = "copy_data";
+#include "contactpopupmenu.h"
 
 struct _HistoryView
 {
@@ -52,6 +51,8 @@
 
 struct _HistoryViewPrivate
 {
+    GtkWidget *popup_menu;
+
     CategorizedHistoryModel::SortedProxy *q_sorted_proxy;
     QMetaObject::Connection category_changed;
 };
@@ -129,121 +130,6 @@
 }
 
 static void
-call_contactmethod(G_GNUC_UNUSED GtkWidget *item, ContactMethod *cm)
-{
-    g_return_if_fail(cm);
-    place_new_call(cm);
-}
-
-static void
-copy_contact_info(GtkWidget *item, G_GNUC_UNUSED gpointer user_data)
-{
-    gpointer data = g_object_get_data(G_OBJECT(item), COPY_DATA_KEY);
-    g_return_if_fail(data);
-    gchar* text = (gchar *)data;
-    GtkClipboard* clip = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
-    gtk_clipboard_set_text(clip, text, -1);
-}
-
-/* TODO: can't seem to delete just one item for now, add when supported in backend
- * static void
- * delete_history_item(G_GNUC_UNUSED GtkWidget *item, GtkTreeView *treeview)
- * {
- *     GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
- *     QModelIndex idx = get_index_from_selection(selection);
- *
- *     if (idx.isValid()) {
- *         g_debug("deleting history item");
- *         CategorizedHistoryModel::instance().removeRow(idx.row(), idx.parent());
- *     }
- * }
- */
-
-static gboolean
-history_popup_menu(G_GNUC_UNUSED GtkWidget *widget, GdkEventButton *event, GtkTreeView *treeview)
-{
-    /* build popup menu when right clicking on history item
-     * user should be able to call, copy the "name" or the "number",
-     * delete history item or all of the history,
-     * and eventualy add the number to a contact
-     */
-
-    /* check for right click */
-    if (event->button != BUTTON_RIGHT_CLICK || event->type != GDK_BUTTON_PRESS)
-        return FALSE;
-
-    /* check if the selected item is a call */
-    auto selection = gtk_tree_view_get_selection(treeview);
-    const auto& idx = get_index_from_selection(selection);
-    const auto& var_c = idx.data(static_cast<int>(Call::Role::Object));
-    if (!idx.isValid() || !var_c.isValid())
-        return FALSE;
-    auto call = var_c.value<Call *>();
-    if (call == nullptr)
-        return FALSE;
-
-    GtkWidget *menu = gtk_menu_new();
-
-    /* call */
-    auto item = gtk_menu_item_new_with_mnemonic(_("_Call"));
-    gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-    g_signal_connect(item,
-                     "activate",
-                     G_CALLBACK(call_contactmethod),
-                     call->peerContactMethod());
-
-    /* copy name */
-    QVariant name_var = idx.data(static_cast<int>(Ring::Role::Name));
-    if (name_var.isValid()) {
-        gchar *name = g_strdup_printf("%s", name_var.value<QString>().toUtf8().constData());
-        item = gtk_menu_item_new_with_mnemonic(_("_Copy name"));
-        gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-        g_object_set_data_full(G_OBJECT(item), COPY_DATA_KEY, name, (GDestroyNotify)g_free);
-        g_signal_connect(item, "activate", G_CALLBACK(copy_contact_info), NULL);
-    }
-
-     /* copy number */
-     QVariant number_var = idx.data(static_cast<int>(Ring::Role::Number));
-     if (number_var.isValid()) {
-         gchar *number = g_strdup_printf("%s", number_var.value<QString>().toUtf8().constData());
-         item = gtk_menu_item_new_with_mnemonic(_("_Copy number"));
-         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-         g_object_set_data_full(G_OBJECT(item), COPY_DATA_KEY, number, (GDestroyNotify)g_free);
-         g_signal_connect(item, "activate", G_CALLBACK(copy_contact_info), NULL);
-     }
-
-    /* get the contact method and check if it is already linked to a person,
-     * if not, then offer to either add to a new or existing contact */
-    auto contactmethod = call->peerContactMethod();
-    if (!contact_method_has_contact(contactmethod)) {
-        GtkTreeIter iter;
-        GtkTreeModel *model;
-        gtk_tree_selection_get_selected(selection, &model, &iter);
-        auto path = gtk_tree_model_get_path(model, &iter);
-        auto column = gtk_tree_view_get_column(treeview, 0);
-        GdkRectangle rect;
-        gtk_tree_view_get_cell_area(treeview, path, column, &rect);
-        gtk_tree_view_convert_bin_window_to_widget_coords(treeview, rect.x, rect.y, &rect.x, &rect.y);
-        gtk_tree_path_free(path);
-        auto add_to = menu_item_add_to_contact(contactmethod, GTK_WIDGET(treeview), &rect);
-        gtk_menu_shell_append(GTK_MENU_SHELL(menu), add_to);
-    }
-
-    /* TODO: delete history entry
-     * gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
-     * item = gtk_menu_item_new_with_mnemonic("_Delete entry");
-     * gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
-     * g_signal_connect(item, "activate", G_CALLBACK(delete_history_item), treeview);
-     */
-
-    /* show menu */
-    gtk_widget_show_all(menu);
-    gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);
-
-    return TRUE;
-}
-
-static void
 render_call_photo(G_GNUC_UNUSED GtkTreeViewColumn *tree_column,
                      GtkCellRenderer *cell,
                      GtkTreeModel *tree_model,
@@ -463,7 +349,10 @@
     gtk_tree_view_column_set_expand(column, FALSE);
 
     g_signal_connect(self, "row-activated", G_CALLBACK(activate_history_item), NULL);
-    g_signal_connect(self, "button-press-event", G_CALLBACK(history_popup_menu), self);
+
+    /* init popup menu */
+    priv->popup_menu = contact_popup_menu_new(GTK_TREE_VIEW(self));
+    g_signal_connect_swapped(self, "button-press-event", G_CALLBACK(contact_popup_menu_show), priv->popup_menu);
 
     gtk_widget_show_all(GTK_WIDGET(self));
 }
@@ -474,6 +363,7 @@
     HistoryViewPrivate *priv = HISTORY_VIEW_GET_PRIVATE(object);
 
     QObject::disconnect(priv->category_changed);
+    gtk_widget_destroy(priv->popup_menu);
 
     G_OBJECT_CLASS(history_view_parent_class)->dispose(object);
 }